# Verify minimum required version
cmake_minimum_required(VERSION 3.6.2)
-if(CMAKE_VERSION VERSION_EQUAL 3.0 OR CMAKE_VERSION VERSION_GREATER 3.0)
- cmake_policy(SET CMP0042 NEW)
-endif()
+cmake_policy(SET CMP0042 NEW) # MACOSX_RPATH is enabled by default.
# Set the project name
project(diagnostics)
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME diagnostics)
add_component(diagnostics)
-if (WIN32)
+if(CLR_CMAKE_HOST_WIN32)
message(STATUS "VS_PLATFORM_TOOLSET is ${CMAKE_VS_PLATFORM_TOOLSET}")
message(STATUS "VS_PLATFORM_NAME is ${CMAKE_VS_PLATFORM_NAME}")
-endif (WIN32)
+endif(CLR_CMAKE_HOST_WIN32)
set(ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
-if (NOT WIN32)
+if(CLR_CMAKE_HOST_UNIX)
# Drop the static scope for SOS, so it's available outside the
# compilation unit for external linkage; see extern declaration
# in SOS sources.
file(READ "${VERSION_FILE_PATH}" VERSION_FILE_CONTENTS)
string(REPLACE "static char" "char" VERSION_LINE_WITHOUT_STATIC "${VERSION_FILE_CONTENTS}")
file(WRITE "${VERSION_FILE_PATH}" "${VERSION_LINE_WITHOUT_STATIC}")
-endif (NOT WIN32)
+endif(CLR_CMAKE_HOST_UNIX)
# Where _version.h for Windows is generated
-if (WIN32)
+if(CLR_CMAKE_HOST_WIN32)
include_directories(${CLR_ARTIFACTS_OBJ_DIR})
-endif (WIN32)
+endif(CLR_CMAKE_HOST_WIN32)
set(CORECLR_SET_RPATH ON)
if(CORECLR_SET_RPATH)
OPTION(CLR_CMAKE_ENABLE_CODE_COVERAGE "Enable code coverage" OFF)
OPTION(CLR_CMAKE_WARNINGS_ARE_ERRORS "Warnings are errors" ON)
+#----------------------------------------------------
+# Cross target Component build specific configuration
+#----------------------------------------------------
+if(CLR_CROSS_COMPONENTS_BUILD)
+ add_definitions(-DCROSS_COMPILE)
+
+ if(CLR_CMAKE_HOST_ARCH_AMD64 AND (CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_I386))
+ set(FEATURE_CROSSBITNESS 1)
+ endif(CLR_CMAKE_HOST_ARCH_AMD64 AND (CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_I386))
+endif(CLR_CROSS_COMPONENTS_BUILD)
+
#------------------------------------
# Definitions (for platform)
#-----------------------------------
clr_unknown_arch()
endif (CLR_CMAKE_TARGET_ARCH_AMD64)
-if(WIN32)
+if(CLR_CMAKE_HOST_WIN32)
add_definitions(-DWIN32)
add_definitions(-D_WIN32)
add_definitions(-DWINVER=0x0602)
add_definitions(-D_WIN32_WINNT=0x0602)
add_definitions(-DWIN32_LEAN_AND_MEAN=1)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
-endif(WIN32)
+endif(CLR_CMAKE_HOST_WIN32)
+
+add_definitions(-DUNICODE)
+add_definitions(-D_UNICODE)
#--------------------------------------
# FEATURE Defines
add_definitions(-DFEATURE_PAL_ANSI)
endif(CLR_CMAKE_HOST_UNIX)
-if(WIN32)
+if(CLR_CMAKE_HOST_WIN32)
add_definitions(-DFEATURE_COMINTEROP)
-endif(WIN32)
+endif(CLR_CMAKE_HOST_WIN32)
if(NOT CMAKE_SYSTEM_NAME STREQUAL NetBSD)
add_definitions(-DFEATURE_HIJACK)
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dbgutil", "src\SOS\dbgutil\dbgutil.vcxproj", "{A9A7C879-C320-3327-BB84-16E1322E17AE}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debugshim", "src\SOS\debugshim\debugshim.vcxproj", "{6A94C5FE-8706-3505-834E-DA16242F3864}"
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gcdump", "src\SOS\gcdump\gcdump.vcxproj", "{20EBC3C4-917C-402D-B778-9A6E3742BF5A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SOS.InstallHelper", "src\SOS\SOS.InstallHelper\SOS.InstallHelper.csproj", "{1F012743-941B-4915-8C55-02097894CF3F}"
ProjectSection(SolutionItems) = preProject
src\inc\arrayholder.h = src\inc\arrayholder.h
src\inc\bitvector.h = src\inc\bitvector.h
+ src\inc\check.h = src\inc\check.h
+ src\inc\check.inl = src\inc\check.inl
src\inc\clrdata.idl = src\inc\clrdata.idl
+ src\inc\clrhost.h = src\inc\clrhost.h
src\inc\clrinternal.idl = src\inc\clrinternal.idl
+ src\inc\clrnt.h = src\inc\clrnt.h
src\inc\clrprivappxhosting.idl = src\inc\clrprivappxhosting.idl
src\inc\clrprivbinding.idl = src\inc\clrprivbinding.idl
src\inc\clrprivhosting.idl = src\inc\clrprivhosting.idl
src\inc\clrprivruntimebinders.idl = src\inc\clrprivruntimebinders.idl
+ src\inc\clrtypes.h = src\inc\clrtypes.h
src\inc\CMakeLists.txt = src\inc\CMakeLists.txt
+ src\inc\contract.h = src\inc\contract.h
+ src\inc\contract.inl = src\inc\contract.inl
src\inc\cor.h = src\inc\cor.h
src\inc\cordebug.idl = src\inc\cordebug.idl
src\inc\coreclrhost.h = src\inc\coreclrhost.h
src\inc\corhdr.h = src\inc\corhdr.h
src\inc\corhlpr.cpp = src\inc\corhlpr.cpp
src\inc\corhlpr.h = src\inc\corhlpr.h
+ src\inc\corhlprpriv.h = src\inc\corhlprpriv.h
src\inc\corjitflags.h = src\inc\corjitflags.h
src\inc\corprof.idl = src\inc\corprof.idl
src\inc\corpub.idl = src\inc\corpub.idl
src\inc\corsym.idl = src\inc\corsym.idl
src\inc\cortypeinfo.h = src\inc\cortypeinfo.h
src\inc\crosscomp.h = src\inc\crosscomp.h
+ src\inc\crsttypes.h = src\inc\crsttypes.h
+ src\inc\crtwrap.h = src\inc\crtwrap.h
src\inc\daccess.h = src\inc\daccess.h
src\inc\dacprivate.h = src\inc\dacprivate.h
+ src\inc\dbgenginemetrics.h = src\inc\dbgenginemetrics.h
src\inc\dbgportable.h = src\inc\dbgportable.h
src\inc\dbgtargetcontext.h = src\inc\dbgtargetcontext.h
src\inc\dbgutil.h = src\inc\dbgutil.h
src\inc\debugmacros.h = src\inc\debugmacros.h
src\inc\debugmacrosext.h = src\inc\debugmacrosext.h
+ src\inc\debugreturn.h = src\inc\debugreturn.h
+ src\inc\entrypoints.h = src\inc\entrypoints.h
+ src\inc\ex.h = src\inc\ex.h
+ src\inc\fstring.h = src\inc\fstring.h
src\inc\fusion.idl = src\inc\fusion.idl
src\inc\gcdecoder.cpp = src\inc\gcdecoder.cpp
src\inc\gcdesc.h = src\inc\gcdesc.h
src\inc\gcinfodecoder.h = src\inc\gcinfodecoder.h
src\inc\gcinfodumper.h = src\inc\gcinfodumper.h
src\inc\gcinfotypes.h = src\inc\gcinfotypes.h
+ src\inc\gcmsg.inl = src\inc\gcmsg.inl
src\inc\genericstackprobe.h = src\inc\genericstackprobe.h
+ src\inc\getproductversionnumber.h = src\inc\getproductversionnumber.h
src\inc\hillclimbing.h = src\inc\hillclimbing.h
src\inc\holder.h = src\inc\holder.h
src\inc\iallocator.h = src\inc\iallocator.h
+ src\inc\iterator.h = src\inc\iterator.h
src\inc\livedatatarget.h = src\inc\livedatatarget.h
src\inc\log.h = src\inc\log.h
src\inc\loglf.h = src\inc\loglf.h
src\inc\longfilepathwrappers.h = src\inc\longfilepathwrappers.h
+ src\inc\mdcommon.h = src\inc\mdcommon.h
+ src\inc\memoryrange.h = src\inc\memoryrange.h
+ src\inc\metadata.h = src\inc\metadata.h
src\inc\metahost.idl = src\inc\metahost.idl
src\inc\MSCOREE.IDL = src\inc\MSCOREE.IDL
src\inc\mscorsvc.idl = src\inc\mscorsvc.idl
src\inc\msodw.h = src\inc\msodw.h
src\inc\new.hpp = src\inc\new.hpp
+ src\inc\nibblemapmacros.h = src\inc\nibblemapmacros.h
+ src\inc\nsutilpriv.h = src\inc\nsutilpriv.h
src\inc\opcode.def = src\inc\opcode.def
src\inc\openum.h = src\inc\openum.h
+ src\inc\ostype.h = src\inc\ostype.h
src\inc\palclr.h = src\inc\palclr.h
src\inc\palclr_win.h = src\inc\palclr_win.h
+ src\inc\pedecoder.h = src\inc\pedecoder.h
+ src\inc\pedecoder.inl = src\inc\pedecoder.inl
src\inc\predeftlsslot.h = src\inc\predeftlsslot.h
src\inc\random.h = src\inc\random.h
+ src\inc\readytorun.h = src\inc\readytorun.h
+ src\inc\readytoruninstructionset.h = src\inc\readytoruninstructionset.h
src\inc\regdisp.h = src\inc\regdisp.h
+ src\inc\registrywrapper.h = src\inc\registrywrapper.h
+ src\inc\releaseholder.h = src\inc\releaseholder.h
+ src\inc\resource.h = src\inc\resource.h
src\inc\runtimeinfo.h = src\inc\runtimeinfo.h
src\inc\safemath.h = src\inc\safemath.h
+ src\inc\safewrap.h = src\inc\safewrap.h
+ src\inc\sbuffer.h = src\inc\sbuffer.h
+ src\inc\sbuffer.inl = src\inc\sbuffer.inl
+ src\inc\securityutil.h = src\inc\securityutil.h
+ src\inc\securitywrapper.h = src\inc\securitywrapper.h
+ src\inc\sigparser.h = src\inc\sigparser.h
src\inc\sospriv.idl = src\inc\sospriv.idl
+ src\inc\sstring.h = src\inc\sstring.h
+ src\inc\sstring.inl = src\inc\sstring.inl
src\inc\stacktrace.h = src\inc\stacktrace.h
src\inc\static_assert.h = src\inc\static_assert.h
src\inc\staticcontract.h = src\inc\staticcontract.h
+ src\inc\stdmacros.h = src\inc\stdmacros.h
src\inc\stresslog.h = src\inc\stresslog.h
src\inc\switches.h = src\inc\switches.h
src\inc\tls.h = src\inc\tls.h
+ src\inc\unreachable.h = src\inc\unreachable.h
+ src\inc\utilcode.h = src\inc\utilcode.h
src\inc\volatile.h = src\inc\volatile.h
src\inc\warningcontrol.h = src\inc\warningcontrol.h
+ src\inc\win64unwind.h = src\inc\win64unwind.h
+ src\inc\winwrap.h = src\inc\winwrap.h
src\inc\xclrdata.idl = src\inc\xclrdata.idl
src\inc\xcordebug.idl = src\inc\xcordebug.idl
+ src\inc\yieldprocessornormalized.h = src\inc\yieldprocessornormalized.h
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "clr_std", "clr_std", "{33239640-6F4B-4DA4-A780-2F5B26D57EAD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-dsrouter", "src\Tools\dotnet-dsrouter\dotnet-dsrouter.csproj", "{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dbgshim", "src\dbgshim\dbgshim.vcxproj", "{BD779298-8631-3F5D-AA59-82897E5454A7}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utilcode", "src\utilcode\utilcode.vcxproj", "{8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|Any CPU = Checked|Any CPU
{A9A7C879-C320-3327-BB84-16E1322E17AE}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{A9A7C879-C320-3327-BB84-16E1322E17AE}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{A9A7C879-C320-3327-BB84-16E1322E17AE}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Checked|Any CPU.ActiveCfg = Checked|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Checked|ARM.ActiveCfg = Checked|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Checked|ARM64.ActiveCfg = Checked|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Checked|x64.ActiveCfg = Checked|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Checked|x64.Build.0 = Checked|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Checked|x86.ActiveCfg = Checked|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Debug|Any CPU.ActiveCfg = Debug|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Debug|ARM.ActiveCfg = Debug|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Debug|ARM64.ActiveCfg = Debug|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Debug|x64.ActiveCfg = Debug|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Debug|x64.Build.0 = Debug|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Debug|x86.ActiveCfg = Debug|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Release|Any CPU.ActiveCfg = Release|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Release|ARM.ActiveCfg = Release|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Release|ARM64.ActiveCfg = Release|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Release|x64.ActiveCfg = Release|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Release|x64.Build.0 = Release|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.Release|x86.ActiveCfg = Release|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.RelWithDebInfo|ARM.ActiveCfg = RelWithDebInfo|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
- {6A94C5FE-8706-3505-834E-DA16242F3864}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{20EBC3C4-917C-402D-B778-9A6E3742BF5A}.Checked|Any CPU.ActiveCfg = Checked|x64
{20EBC3C4-917C-402D-B778-9A6E3742BF5A}.Checked|ARM.ActiveCfg = Checked|x64
{20EBC3C4-917C-402D-B778-9A6E3742BF5A}.Checked|ARM64.ActiveCfg = Checked|x64
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Checked|Any CPU.ActiveCfg = Checked|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Checked|ARM.ActiveCfg = Checked|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Checked|ARM64.ActiveCfg = Checked|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Checked|x64.ActiveCfg = Checked|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Checked|x64.Build.0 = Checked|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Checked|x86.ActiveCfg = Checked|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Debug|ARM.ActiveCfg = Debug|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Debug|ARM64.ActiveCfg = Debug|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Debug|x64.ActiveCfg = Debug|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Debug|x64.Build.0 = Debug|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Debug|x86.ActiveCfg = Debug|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Release|Any CPU.ActiveCfg = Release|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Release|ARM.ActiveCfg = Release|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Release|ARM64.ActiveCfg = Release|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Release|x64.ActiveCfg = Release|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Release|x64.Build.0 = Release|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.Release|x86.ActiveCfg = Release|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.RelWithDebInfo|ARM.ActiveCfg = RelWithDebInfo|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
+ {BD779298-8631-3F5D-AA59-82897E5454A7}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Checked|Any CPU.ActiveCfg = Checked|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Checked|ARM.ActiveCfg = Checked|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Checked|ARM64.ActiveCfg = Checked|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Checked|x64.ActiveCfg = Checked|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Checked|x64.Build.0 = Checked|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Checked|x86.ActiveCfg = Checked|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Debug|ARM.ActiveCfg = Debug|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Debug|ARM64.ActiveCfg = Debug|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Debug|x64.ActiveCfg = Debug|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Debug|x64.Build.0 = Debug|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Debug|x86.ActiveCfg = Debug|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Release|Any CPU.ActiveCfg = Release|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Release|ARM.ActiveCfg = Release|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Release|ARM64.ActiveCfg = Release|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Release|x64.ActiveCfg = Release|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Release|x64.Build.0 = Release|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.Release|x86.ActiveCfg = Release|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.RelWithDebInfo|ARM.ActiveCfg = RelWithDebInfo|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
{43D41DE9-7CCC-4DCB-A68A-B9099E538125} = {B62728C8-1267-4043-B46F-5537BBAEC692}
{41F59D85-FC36-3015-861B-F177863252BC} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
{A9A7C879-C320-3327-BB84-16E1322E17AE} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
- {6A94C5FE-8706-3505-834E-DA16242F3864} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
{20EBC3C4-917C-402D-B778-9A6E3742BF5A} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
{1F012743-941B-4915-8C55-02097894CF3F} = {41638A4C-0DAF-47ED-A774-ECBBAC0315D7}
{41351955-16D5-48D7-AF4C-AF25F5FB2E78} = {B62728C8-1267-4043-B46F-5537BBAEC692}
{064BC7DD-D44C-400E-9215-7546E092AB98} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
{61F73DD0-F346-4D7A-AB12-63415B4EEEE1} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
{2BD55143-B102-4FEC-84AD-DC3B330FA9AC} = {B62728C8-1267-4043-B46F-5537BBAEC692}
+ {BD779298-8631-3F5D-AA59-82897E5454A7} = {19FAB78C-3351-4911-8F0C-8C6056401740}
+ {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D} = {19FAB78C-3351-4911-8F0C-8C6056401740}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46465737-C938-44FC-BE1A-4CE139EBB5E0}
powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0Build.ps1""" %_commonArgs% -bundletools %*"
if NOT '%ERRORLEVEL%' == '0' goto ExitWithCode
+echo Creating dbgshim packages
+powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0common\Build.ps1""" %_commonArgs% -pack -noBl /bl:'%_logDir%PackDbgShim.binlog' -projects %~dp0..\src\dbgshim\pkg\Microsoft.Diagnostics.DbgShim.proj %*"
+if NOT '%ERRORLEVEL%' == '0' goto ExitWithCode
+
echo Signing and publishing manifest
powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0common\Build.ps1""" %_commonArgs% -sign -publish -noBl /bl:'%_logDir%SignPublish.binlog' %*"
if NOT '%ERRORLEVEL%' == '0' goto ExitWithCode
echo ""
echo "Common Options:"
echo ""
- echo "BuildArch can be: -arm, -armel, -arm64, -loongarch64, -s390x, x64, x86, -wasm"
+ echo "BuildArch can be: -arm, -armv6, -armel, -arm64, -loongarch64, -s390x, x64, x86, -wasm"
echo "BuildType can be: -debug, -checked, -release"
echo "-os: target OS (defaults to running OS)"
echo "-bindir: output directory (defaults to $__ProjectRoot/artifacts)"
__BuildArch=arm
;;
+ armv6|-armv6)
+ __BuildArch=armv6
+ ;;
+
arm64|-arm64)
__BuildArch=arm64
;;
elseif (CLR_CMAKE_HOST_ARCH_ARM)
set(ARCH_HOST_NAME arm)
add_definitions(-DHOST_ARM)
+elseif (CLR_CMAKE_HOST_ARCH_ARMV6)
+ set(ARCH_HOST_NAME armv6)
+ add_definitions(-DHOST_ARMV6)
elseif (CLR_CMAKE_HOST_ARCH_ARM64)
set(ARCH_HOST_NAME arm64)
add_definitions(-DHOST_ARM64 -DHOST_64BIT)
message("Detected Linux x86_64")
elseif(CLR_CMAKE_HOST_UNIX_ARM)
message("Detected Linux ARM")
+ elseif(CLR_CMAKE_HOST_UNIX_ARMV6)
+ message("Detected Linux ARMv6")
elseif(CLR_CMAKE_HOST_UNIX_ARM64)
message("Detected Linux ARM64")
elseif(CLR_CMAKE_HOST_UNIX_LOONGARCH64)
set(ARCH_TARGET_NAME arm)
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_ARCH>>>:TARGET_ARM>)
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_ARCH>>>:TARGET_32BIT>)
+elseif (CLR_CMAKE_TARGET_ARCH_ARMV6)
+ set(ARCH_SOURCES_DIR arm)
+ set(ARCH_TARGET_NAME armv6)
+ add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_ARCH>>>:TARGET_ARM>)
+ add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_ARCH>>>:TARGET_ARMV6>)
+ add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_ARCH>>>:TARGET_32BIT>)
elseif (CLR_CMAKE_TARGET_ARCH_I386)
set(ARCH_TARGET_NAME x86)
set(ARCH_SOURCES_DIR i386)
endif(ARM_SOFTFP)
endif(CLR_CMAKE_HOST_UNIX_ARM)
+if(CLR_CMAKE_HOST_UNIX_ARMV6)
+ add_compile_options(-mfpu=vfp)
+ add_definitions(-DCLR_ARM_FPU_CAPABILITY=0x0)
+ add_compile_options(-march=armv6zk)
+ add_compile_options(-mcpu=arm1176jzf-s)
+ add_compile_options(-mfloat-abi=hard)
+endif(CLR_CMAKE_HOST_UNIX_ARMV6)
+
if(CLR_CMAKE_HOST_UNIX_X86)
add_compile_options(-msse2)
endif()
set(CLR_CMAKE_HOST_UNIX_ARMV7L 1)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL arm OR CMAKE_SYSTEM_PROCESSOR STREQUAL armv7-a)
set(CLR_CMAKE_HOST_UNIX_ARM 1)
+ elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL armv6 OR CMAKE_SYSTEM_PROCESSOR STREQUAL armv6l)
+ set(CLR_CMAKE_HOST_UNIX_ARMV6 1)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL arm64)
set(CLR_CMAKE_HOST_UNIX_ARM64 1)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL loongarch64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL loongarch64)
if(CLR_CMAKE_HOST_UNIX_ARMV7L)
set(CLR_CMAKE_HOST_ARCH_ARMV7L 1)
endif()
+elseif(CLR_CMAKE_HOST_UNIX_ARMV6)
+ set(CLR_CMAKE_HOST_ARCH_ARMV6 1)
+ set(CLR_CMAKE_HOST_ARCH "armv6")
+
+ if(CLR_CMAKE_HOST_UNIX_ARMV6L)
+ set(CLR_CMAKE_HOST_ARCH_ARMV6L 1)
+ endif()
elseif(CLR_CMAKE_HOST_UNIX_ARM64)
set(CLR_CMAKE_HOST_ARCH_ARM64 1)
set(CLR_CMAKE_HOST_ARCH "arm64")
set(CLR_CMAKE_TARGET_ARCH_LOONGARCH64 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL arm)
set(CLR_CMAKE_TARGET_ARCH_ARM 1)
+ elseif(CLR_CMAKE_TARGET_ARCH STREQUAL armv6)
+ set(CLR_CMAKE_TARGET_ARCH_ARMV6 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL armel)
set(CLR_CMAKE_TARGET_ARCH_ARM 1)
set(CLR_CMAKE_TARGET_ARCH_ARMV7L 1)
set(CLR_CMAKE_TARGET_UNIX_ARM 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL arm)
set(CLR_CMAKE_TARGET_UNIX_ARM 1)
+ elseif(CLR_CMAKE_TARGET_ARCH STREQUAL armv6)
+ set(CLR_CMAKE_TARGET_UNIX_ARMV6 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL arm64)
set(CLR_CMAKE_TARGET_UNIX_ARM64 1)
elseif(CLR_CMAKE_TARGET_ARCH STREQUAL loongarch64)
if(CLR_CMAKE_TARGET_ANDROID)
set(TOOLSET_PREFIX ${ANDROID_TOOLCHAIN_PREFIX})
- elseif(CMAKE_CROSSCOMPILING AND NOT DEFINED CLR_CROSS_COMPONENTS_BUILD AND (CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l OR
- CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL arm OR CMAKE_SYSTEM_PROCESSOR STREQUAL s390x))
+ elseif(CMAKE_CROSSCOMPILING AND NOT DEFINED CLR_CROSS_COMPONENTS_BUILD AND
+ CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv7l|armv6l|aarch64|arm|s390x)$")
set(TOOLSET_PREFIX "${TOOLCHAIN}-")
else()
set(TOOLSET_PREFIX "")
elseif(CLR_CROSS_COMPONENTS_BUILD)
message(FATAL_ERROR "Only AMD64, I386 host are supported for linux cross-architecture component. Found: ${CMAKE_SYSTEM_PROCESSOR}")
else()
- message(FATAL_ERROR "Only AMD64, ARM64, LOONGARCH64 and ARM are supported. Found: ${CMAKE_SYSTEM_PROCESSOR}")
+ message(FATAL_ERROR "Only AMD64, ARMV6, ARM64, LOONGARCH64 and ARM are supported. Found: ${CMAKE_SYSTEM_PROCESSOR}")
endif()
endfunction()
+# C to MASM include file translator
+# This is replacement for the deprecated h2inc tool that used to be part of VS.
+function(h2inc filename output)
+ file(STRINGS ${filename} lines)
+ get_filename_component(path "${filename}" DIRECTORY)
+ file(RELATIVE_PATH relative_filename "${CLR_REPO_ROOT_DIR}" "${filename}")
+
+ file(APPEND "${output}" "// File start: ${relative_filename}\n")
+
+ # Use of NEWLINE_CONSUME is needed for lines with trailing backslash
+ file(STRINGS ${filename} contents NEWLINE_CONSUME)
+ string(REGEX REPLACE "\\\\\n" "\\\\\\\\ \n" contents "${contents}")
+ string(REGEX REPLACE "\n" ";" lines "${contents}")
+
+ foreach(line IN LISTS lines)
+ string(REGEX REPLACE "\\\\\\\\ " "\\\\" line "${line}")
+
+ if(line MATCHES "^ *# pragma")
+ # Ignore pragmas
+ continue()
+ endif()
+
+ if(line MATCHES "^ *# *include *\"(.*)\"")
+ # Expand includes.
+ h2inc("${path}/${CMAKE_MATCH_1}" "${output}")
+ continue()
+ endif()
+
+ if(line MATCHES "^ *#define +([0-9A-Za-z_()]+) *(.*)")
+ # Augment #defines with their MASM equivalent
+ set(name "${CMAKE_MATCH_1}")
+ set(value "${CMAKE_MATCH_2}")
+
+ # Note that we do not handle multiline constants
+
+ # Strip comments from value
+ string(REGEX REPLACE "//.*" "" value "${value}")
+ string(REGEX REPLACE "/\\*.*\\*/" "" value "${value}")
+
+ # Strip whitespaces from value
+ string(REPLACE " +$" "" value "${value}")
+
+ # ignore #defines with arguments
+ if(NOT "${name}" MATCHES "\\(")
+ set(HEX_NUMBER_PATTERN "0x([0-9A-Fa-f]+)")
+ set(DECIMAL_NUMBER_PATTERN "(-?[0-9]+)")
+
+ if("${value}" MATCHES "${HEX_NUMBER_PATTERN}")
+ string(REGEX REPLACE "${HEX_NUMBER_PATTERN}" "0\\1h" value "${value}") # Convert hex constants
+ file(APPEND "${output}" "${name} EQU ${value}\n")
+ elseif("${value}" MATCHES "${DECIMAL_NUMBER_PATTERN}" AND (NOT "${value}" MATCHES "[G-Zg-z]+" OR "${value}" MATCHES "\\("))
+ string(REGEX REPLACE "${DECIMAL_NUMBER_PATTERN}" "\\1t" value "${value}") # Convert dec constants
+ file(APPEND "${output}" "${name} EQU ${value}\n")
+ else()
+ file(APPEND "${output}" "${name} TEXTEQU <${value}>\n")
+ endif()
+ endif()
+ endif()
+
+ file(APPEND "${output}" "${line}\n")
+ endforeach()
+
+ file(APPEND "${output}" "// File end: ${relative_filename}\n")
+endfunction()
+
# Build a list of compiler definitions by putting -D in front of each define.
function(get_compile_definitions DefinitionName)
# Get the current list of definitions
find_library(UNWIND_ARCH NAMES unwind-arm)
endif()
+ if(CLR_CMAKE_HOST_ARCH_ARMV6)
+ find_library(UNWIND_ARCH NAMES unwind-arm)
+ endif()
+
if(CLR_CMAKE_HOST_ARCH_ARM64)
find_library(UNWIND_ARCH NAMES unwind-aarch64)
endif()
fi
;;
+ armv6l)
+ arch=armv6
+ ;;
+
i[3-6]86)
echo "Unsupported CPU $CPUName detected, build might not succeed!"
arch=x86
else()
message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only arm64 or x64 is supported for OSX cross build!")
endif()
-elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|arm64|loongarch64|s390x|x86)$" OR FREEBSD OR ILLUMOS)
+elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|s390x|x86)$" OR FREEBSD OR ILLUMOS)
set_cache_value(FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL_EXITCODE 1)
set_cache_value(GETPWUID_R_SETS_ERRNO_EXITCODE 0)
set_cache_value(HAS_POSIX_SEMAPHORES_EXITCODE 0)
set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 0)
endif()
else()
- message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only armel, arm, arm64, loongarch64, s390x and x86 are supported!")
+ message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only armel, arm, armv6, arm64, loongarch64, s390x and x86 are supported!")
endif()
-if(TARGET_ARCH_NAME STREQUAL "x86" OR TARGET_ARCH_NAME STREQUAL "s390x" OR TARGET_ARCH_NAME STREQUAL "loongarch64")
+if(TARGET_ARCH_NAME STREQUAL "x86" OR TARGET_ARCH_NAME STREQUAL "s390x" OR TARGET_ARCH_NAME STREQUAL "armv6" OR TARGET_ARCH_NAME STREQUAL "loongarch64")
set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 0)
endif()
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>true</IsPackable>
+ <IsShipping>false</IsShipping>
<IsShippingPackage>false</IsShippingPackage>
<PreReleaseVersionLabel></PreReleaseVersionLabel>
<VersionPrefix></VersionPrefix>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>true</IsPackable>
+ <IsShipping>false</IsShipping>
<IsShippingPackage>false</IsShippingPackage>
<PreReleaseVersionLabel></PreReleaseVersionLabel>
<VersionPrefix></VersionPrefix>
+if (CLR_CMAKE_HOST_UNIX)
+ include_directories("${ROOT_DIR}/src/pal/inc")
+ include_directories("${ROOT_DIR}/src/pal/inc/rt")
+ include_directories("${ROOT_DIR}/src/pal/src/safecrt")
+endif (CLR_CMAKE_HOST_UNIX)
+
include_directories(${ROOT_DIR}/src)
include_directories(${ROOT_DIR}/src/pal/prebuilt/inc)
include_directories(inc)
if (CLR_CMAKE_HOST_UNIX)
- add_subdirectory(palrt)
- add_subdirectory(pal)
+ add_subdirectory(pal)
endif(CLR_CMAKE_HOST_UNIX)
add_subdirectory(inc)
add_subdirectory(SOS)
+
+if (CLR_CMAKE_HOST_UNIX)
+ # This prevents inclusion of standard C compiler headers
+ add_compile_options(-nostdinc)
+ include_directories(${ROOT_DIR}/src/pal/inc/rt/cpp)
+
+ add_subdirectory(palrt)
+endif(CLR_CMAKE_HOST_UNIX)
+
+add_subdirectory(utilcode)
+add_subdirectory(dbgshim)
<Import Project="Sdk.props" Sdk="Microsoft.DotNet.Arcade.Sdk" />
<PropertyGroup>
- <IsShipping>true</IsShipping>
- <IsShippingAssembly>false</IsShippingAssembly>
+ <IsShipping>false</IsShipping>
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
<NoPackageAnalysis>true</NoPackageAnalysis>
<DebugSymbols>true</DebugSymbols>
<PackageReleaseNotes>$(Description)</PackageReleaseNotes>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
- <IsShipping>false</IsShipping>
- <IsShippingAssembly>true</IsShippingAssembly>
+ <IsShipping>true</IsShipping>
+ <IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
<ItemGroup>
<PackageReleaseNotes>$(Description)</PackageReleaseNotes>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
+ <IsShipping>true</IsShipping>
<IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
<NoWarn>;1591;1701</NoWarn>
<Description>Diagnostics extension commands</Description>
<IsPackable>true</IsPackable>
- <IsShipping>false</IsShipping>
<PackageTags>Diagnostic</PackageTags>
<PackageReleaseNotes>$(Description)</PackageReleaseNotes>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
- <IsShippingAssembly>true</IsShippingAssembly>
+ <IsShipping>true</IsShipping>
+ <IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
<ItemGroup>
<IncludeSymbols>true</IncludeSymbols>
<!-- Do not ship this package until ready to be consumed. -->
<IsShipping>false</IsShipping>
+ <IsShippingAssembly>true</IsShippingAssembly>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- Version information -->
<VersionPrefix>5.0.0</VersionPrefix>
<IncludeSymbols>true</IncludeSymbols>
<!-- Do not ship this package until ready to be consumed. -->
<IsShipping>false</IsShipping>
+ <IsShippingAssembly>true</IsShippingAssembly>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- Version information -->
<VersionPrefix>5.0.0</VersionPrefix>
<TargetFramework>netstandard2.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>;1591;1701</NoWarn>
- <Description>Diagnostic utility functions and helpers</Description>
+ <Description>Diagnostic command line REPL support</Description>
<IsPackable>true</IsPackable>
<PackageTags>Diagnostic</PackageTags>
<PackageReleaseNotes>$(Description)</PackageReleaseNotes>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
+ <IsShipping>true</IsShipping>
<IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
if(CLR_CMAKE_HOST_UNIX)
- include_directories(${ROOT_DIR}/src/pal/inc)
- include_directories(${ROOT_DIR}/src/pal/inc/rt)
-
- add_subdirectory(lldbplugin)
+ add_subdirectory(lldbplugin)
endif(CLR_CMAKE_HOST_UNIX)
# lldbplugin doesn't build with these options
-if(WIN32)
+if(CLR_CMAKE_HOST_WIN32)
message(STATUS "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
message(STATUS "VSInstallDir: $ENV{VSInstallDir}")
if(NOT CLR_CMAKE_TARGET_ARCH_ARM64)
add_subdirectory(SOS.UnitTests/Debuggees/DesktopClrHost)
endif()
-endif(WIN32)
+endif(CLR_CMAKE_HOST_WIN32)
add_definitions(-D_SECURE_SCL=0)
add_subdirectory(dbgutil)
add_subdirectory(SOS.Extensions)
add_subdirectory(Strike)
-
-if(CLR_CMAKE_HOST_UNIX)
- add_compile_options(-fPIC)
-
- # Include the dummy c++ include files
- include_directories(${ROOT_DIR}/src/pal/inc/rt/cpp)
-
- # This prevents inclusion of standard C compiler headers
- add_compile_options(-nostdinc)
-endif(CLR_CMAKE_HOST_UNIX)
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>;1591;1701</NoWarn>
<Description>.NET Diagnostic Extensions support</Description>
+ <IsShipping>true</IsShipping>
<IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
<NoWarn>;1591;1701</NoWarn>
<Description>SOS Hosting support</Description>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <IsShipping>true</IsShipping>
+ <IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
<ItemGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>SOS.InstallHelper</AssemblyName>
<NoWarn>;1591;1701</NoWarn>
+ <IsPackable>false</IsPackable>
<Description>Diagnostic SOS Install Helper</Description>
<PackageReleaseNotes>$(Description)</PackageReleaseNotes>
<PackageTags>SOS</PackageTags>
+ <IsShipping>true</IsShipping>
+ <IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
</Project>
<SOSPackagePathPrefix>tools</SOSPackagePathPrefix>
<GalleryManifestName>$(ArtifactsPackagesDir)\GalleryManifest.xml</GalleryManifestName>
<BeforePack>GenerateGalleryZip;GenerateSymbolsZip</BeforePack>
+ <IsShipping>true</IsShipping>
<IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
<PackageReleaseNotes>$(Description)</PackageReleaseNotes>
<IsPackable>true</IsPackable>
<IncludeBuildOutput>false</IncludeBuildOutput>
- <IsShippingPackage>false</IsShippingPackage>
<IncludeSymbols>true</IncludeSymbols>
<SOSPackagePathPrefix>tools</SOSPackagePathPrefix>
+ <IsShipping>true</IsShipping>
+ <IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
<ItemGroup>
<PropertyGroup>
<DebugType Condition="'$(TargetFramework)' == 'net462'">full</DebugType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <IsShippingAssembly>false</IsShippingAssembly>
<Optimize>false</Optimize>
<BuildTargetFrameworks>netcoreapp3.1;net5.0;net6.0</BuildTargetFrameworks>
</PropertyGroup>
include_directories(BEFORE xplat)
- add_compile_options(-fPIC)
-
set(SOS_SOURCES
disasm.cpp
eeheap.cpp
\**********************************************************************/
DECLARE_API(EEVersion)
{
- INIT_API();
+ INIT_API_NO_RET_ON_FAILURE();
static const int fileVersionBufferSize = 1024;
ArrayHolder<char> fileVersionBuffer = new char[fileVersionBufferSize];
}
}
- if (!InitializeHeapData())
- ExtOut("GC Heap not initialized, so GC mode is not determined yet.\n");
- else if (IsServerBuild())
- ExtOut("Server mode with %d gc heaps\n", GetGcHeapCount());
- else
- ExtOut("Workstation mode\n");
+ // Only print if DAC was loaded/initialized
+ if (g_sos != nullptr)
+ {
+ if (!InitializeHeapData())
+ ExtOut("GC Heap not initialized, so GC mode is not determined yet.\n");
+ else if (IsServerBuild())
+ ExtOut("Server mode with %d gc heaps\n", GetGcHeapCount());
+ else
+ ExtOut("Workstation mode\n");
- if (!GetGcStructuresValid()) {
- ExtOut("In plan phase of garbage collection\n");
+ if (!GetGcStructuresValid())
+ {
+ ExtOut("In plan phase of garbage collection\n");
+ }
}
// Print SOS version
typedef BOOL (WINAPI *FntGetProcessTimes)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
static FntGetProcessTimes pFntGetProcessTimes = (FntGetProcessTimes)-1;
if (pFntGetProcessTimes == (FntGetProcessTimes)-1) {
- HINSTANCE hstat = LoadLibrary ("Kernel32.dll");
+ HINSTANCE hstat = LoadLibraryA("kernel32.dll");
if (hstat != 0)
{
pFntGetProcessTimes = (FntGetProcessTimes)GetProcAddress (hstat, "GetProcessTimes");
static FntNtQueryInformationProcess pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)-1;
if (pFntNtQueryInformationProcess == (FntNtQueryInformationProcess)-1) {
- HINSTANCE hstat = LoadLibrary ("ntdll.dll");
+ HINSTANCE hstat = LoadLibraryA("ntdll.dll");
if (hstat != 0)
{
pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)GetProcAddress (hstat, "NtQueryInformationProcess");
HMODULE DllHandle = *InOutDllHandle;
if (DllHandle == NULL)
{
- DllHandle = LoadLibraryEx(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+ DllHandle = LoadLibraryExA(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
if (DllHandle != NULL)
*InOutDllHandle = DllHandle;
}
// Check if a file exist
BOOL FileExist (const char *filename)
{
- WIN32_FIND_DATA FindFileData;
- HANDLE handle = FindFirstFile (filename, &FindFileData);
+ WIN32_FIND_DATAA FindFileData;
+ HANDLE handle = FindFirstFileA(filename, &FindFileData);
if (handle != INVALID_HANDLE_VALUE) {
FindClose (handle);
return TRUE;
{
VS_FIXEDFILEINFO *pTmpFileInfo = NULL;
UINT uLen = 0;
- if (VerQueryValue(pVersionInfo, "\\", (LPVOID *) &pTmpFileInfo, &uLen))
+ if (VerQueryValueA(pVersionInfo, "\\", (LPVOID *) &pTmpFileInfo, &uLen))
{
if (pFileInfo->dwFileVersionMS == (DWORD)-1) {
return FALSE;
if(CLR_CMAKE_HOST_WIN32)
include_directories(${ROOT_DIR}/src/inc/llvm)
#use static crt
- add_definitions(-MT)
+ set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
endif(CLR_CMAKE_HOST_WIN32)
add_definitions(-DPAL_STDCPP_COMPAT)
)
endif(NOT DEFINED CLR_CMAKE_HOST_OSX)
-
add_library(dbgutil STATIC ${DBGUTIL_SOURCES})
+++ /dev/null
-if(WIN32)
- #use static crt
- add_definitions(-MT)
- add_definitions(-DHOST_IS_WINDOWS_OS)
-endif(WIN32)
-
-if(CLR_CMAKE_HOST_UNIX)
- add_compile_options(-fPIC)
-endif(CLR_CMAKE_HOST_UNIX)
-
-set(DEBUGSHIM_SOURCES
- debugshim.cpp
-)
-
-add_library(debugshim STATIC ${DEBUGSHIM_SOURCES})
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-//*****************************************************************************
-// debugshim.cpp
-//
-
-//
-//*****************************************************************************
-
-#include "debugshim.h"
-#include "dbgutil.h"
-#include <crtdbg.h>
-#include <clrinternal.h> //has the CLR_ID_V4_DESKTOP guid in it
-#include "palclr.h"
-
-#ifndef IMAGE_FILE_MACHINE_ARMNT
-#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
-#endif
-
-#ifndef IMAGE_FILE_MACHINE_ARM64
-#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian
-#endif
-
-#ifndef IMAGE_FILE_MACHINE_MIPS64
-#define IMAGE_FILE_MACHINE_MIPS64 0xDD64 // MIPS64 Little-Endian
-#endif
-
-// making the defines very clear, these represent the host architecture - aka
-// the arch on which this code is running
-#if defined(_X86_)
-#define _HOST_X86_
-#elif defined(_AMD64_)
-#define _HOST_AMD64_
-#elif defined(_ARM_)
-#define _HOST_ARM_
-#elif defined(_ARM64_)
-#define _HOST_ARM64_
-#elif defined(_MIPS64_)
-#define _HOST_MIPS64_
-#endif
-
-//*****************************************************************************
-// CLRDebuggingImpl implementation (ICLRDebugging)
-//*****************************************************************************
-
-typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImpl2FnPtr)(ULONG64 clrInstanceId,
- IUnknown * pDataTarget,
- LPCWSTR pDacModulePath,
- CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion,
- REFIID riid,
- IUnknown ** ppInstance,
- CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags);
-
-typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImplFnPtr)(ULONG64 clrInstanceId,
- IUnknown * pDataTarget,
- HMODULE hDacDll,
- CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion,
- REFIID riid,
- IUnknown ** ppInstance,
- CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags);
-
-typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcess2FnPtr)(ULONG64 clrInstanceId,
- IUnknown * pDataTarget,
- HMODULE hDacDll,
- REFIID riid,
- IUnknown ** ppInstance,
- CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags);
-
-typedef HMODULE (STDAPICALLTYPE *LoadLibraryWFnPtr)(LPCWSTR lpLibFileName);
-
-// Implementation of ICLRDebugging::OpenVirtualProcess
-//
-// Arguments:
-// moduleBaseAddress - the address of the module which might be a CLR
-// pDataTarget - the data target for inspecting the process
-// pLibraryProvider - a callback for locating DBI and DAC
-// pMaxDebuggerSupportedVersion - the max version of the CLR that this debugger will support debugging
-// riidProcess - the IID of the interface that should be passed back in ppProcess
-// ppProcess - output for the ICorDebugProcess# if this module is a CLR
-// pVersion - the CLR version if this module is a CLR
-// pFlags - output, see the CLR_DEBUGGING_PROCESS_FLAGS for more details. Right now this has only one possible
-// value which indicates this runtime had an unhandled exception
-STDMETHODIMP CLRDebuggingImpl::OpenVirtualProcess(
- ULONG64 moduleBaseAddress,
- IUnknown * pDataTarget,
- ICLRDebuggingLibraryProvider * pLibraryProvider,
- CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion,
- REFIID riidProcess,
- IUnknown ** ppProcess,
- CLR_DEBUGGING_VERSION * pVersion,
- CLR_DEBUGGING_PROCESS_FLAGS * pFlags)
-{
- //PRECONDITION(CheckPointer(pDataTarget));
-
- HRESULT hr = S_OK;
- ICorDebugDataTarget * pDt = NULL;
- HMODULE hDbi = NULL;
- HMODULE hDac = NULL;
- LPWSTR pDacModulePath = NULL;
- LPWSTR pDbiModulePath = NULL;
- DWORD dbiTimestamp;
- DWORD dbiSizeOfImage;
- WCHAR dbiName[MAX_PATH_FNAME] = { 0 };
- DWORD dacTimestamp;
- DWORD dacSizeOfImage;
- WCHAR dacName[MAX_PATH_FNAME] = { 0 };
- CLR_DEBUGGING_VERSION version;
- BOOL versionSupportedByCaller = FALSE;
-
- // argument checking
- if ((ppProcess != NULL || pFlags != NULL) && pLibraryProvider == NULL)
- {
- hr = E_POINTER; // the library provider must be specified if either
- // ppProcess or pFlags is non-NULL
- }
- else if ((ppProcess != NULL || pFlags != NULL) && pMaxDebuggerSupportedVersion == NULL)
- {
- hr = E_POINTER; // the max supported version must be specified if either
- // ppProcess or pFlags is non-NULL
- }
- else if (pVersion != NULL && pVersion->wStructVersion != 0)
- {
- hr = CORDBG_E_UNSUPPORTED_VERSION_STRUCT;
- }
- else if (FAILED(pDataTarget->QueryInterface(__uuidof(ICorDebugDataTarget), (void**)&pDt)))
- {
- hr = CORDBG_E_MISSING_DATA_TARGET_INTERFACE;
- }
-
- if (SUCCEEDED(hr))
- {
- // get CLR version
- // The expectation is that new versions of the CLR will continue to use the same GUID
- // (unless there's a reason to hide them from older shims), but debuggers will tell us the
- // CLR version they're designed for and mscordbi.dll can decide whether or not to accept it.
- version.wStructVersion = 0;
- hr = GetCLRInfo(pDt,
- moduleBaseAddress,
- &version,
- &dbiTimestamp,
- &dbiSizeOfImage,
- dbiName,
- MAX_PATH_FNAME,
- &dacTimestamp,
- &dacSizeOfImage,
- dacName,
- MAX_PATH_FNAME);
- }
-
- // If we need to fetch either the process info or the flags info then we need to find
- // mscordbi and DAC and do the version specific OVP work
- if (SUCCEEDED(hr) && (ppProcess != NULL || pFlags != NULL))
- {
- ICLRDebuggingLibraryProvider2* pLibraryProvider2;
- if (SUCCEEDED(pLibraryProvider->QueryInterface(__uuidof(ICLRDebuggingLibraryProvider2), (void**)&pLibraryProvider2)))
- {
- if (FAILED(pLibraryProvider2->ProvideLibrary2(dbiName, dbiTimestamp, dbiSizeOfImage, &pDbiModulePath)) ||
- pDbiModulePath == NULL)
- {
- hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
- }
-
- if (SUCCEEDED(hr))
- {
- hDbi = LoadLibraryW(pDbiModulePath);
- if (hDbi == NULL)
- {
- hr = HRESULT_FROM_WIN32(GetLastError());
- }
- }
-
- if (SUCCEEDED(hr))
- {
- // Adjust the timestamp and size of image if this DAC is a known buggy version and needs to be retargeted
- RetargetDacIfNeeded(&dacTimestamp, &dacSizeOfImage);
-
- // Ask library provider for dac
- if (FAILED(pLibraryProvider2->ProvideLibrary2(dacName, dacTimestamp, dacSizeOfImage, &pDacModulePath)) ||
- pDacModulePath == NULL)
- {
- hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
- }
-
- if (SUCCEEDED(hr))
- {
- hDac = LoadLibraryW(pDacModulePath);
- if (hDac == NULL)
- {
- hr = HRESULT_FROM_WIN32(GetLastError());
- }
- }
- }
-
- pLibraryProvider2->Release();
- }
- else {
- // Ask library provider for dbi
- if (FAILED(pLibraryProvider->ProvideLibrary(dbiName, dbiTimestamp, dbiSizeOfImage, &hDbi)) ||
- hDbi == NULL)
- {
- hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
- }
-
- if (SUCCEEDED(hr))
- {
- // Adjust the timestamp and size of image if this DAC is a known buggy version and needs to be retargeted
- RetargetDacIfNeeded(&dacTimestamp, &dacSizeOfImage);
-
- // ask library provider for dac
- if (FAILED(pLibraryProvider->ProvideLibrary(dacName, dacTimestamp, dacSizeOfImage, &hDac)) ||
- hDac == NULL)
- {
- hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
- }
- }
- }
-
- *ppProcess = NULL;
-
- if (SUCCEEDED(hr) && pDacModulePath != NULL)
- {
- // Get access to the latest OVP implementation and call it
- OpenVirtualProcessImpl2FnPtr ovpFn = (OpenVirtualProcessImpl2FnPtr)GetProcAddress(hDbi, "OpenVirtualProcessImpl2");
- if (ovpFn != NULL)
- {
- hr = ovpFn(moduleBaseAddress, pDataTarget, pDacModulePath, pMaxDebuggerSupportedVersion, riidProcess, ppProcess, pFlags);
- if (FAILED(hr))
- {
- _ASSERTE(ppProcess == NULL || *ppProcess == NULL);
- _ASSERTE(pFlags == NULL || *pFlags == 0);
- }
- }
-#ifdef FEATURE_PAL
- else
- {
- // On Linux/MacOS the DAC module handle needs to be re-created using the DAC PAL instance
- // before being passed to DBI's OpenVirtualProcess* implementation. The DBI and DAC share
- // the same PAL where dbgshim has it's own.
- LoadLibraryWFnPtr loadLibraryWFn = (LoadLibraryWFnPtr)GetProcAddress(hDac, "LoadLibraryW");
- if (loadLibraryWFn != NULL)
- {
- hDac = loadLibraryWFn(pDacModulePath);
- if (hDac == NULL)
- {
- hr = E_HANDLE;
- }
- }
- else
- {
- hr = E_HANDLE;
- }
- }
-#endif // FEATURE_PAL
- }
-
- // If no errors so far and "OpenVirtualProcessImpl2" doesn't exist
- if (SUCCEEDED(hr) && *ppProcess == NULL)
- {
- // Get access to OVP and call it
- OpenVirtualProcessImplFnPtr ovpFn = (OpenVirtualProcessImplFnPtr)GetProcAddress(hDbi, "OpenVirtualProcessImpl");
- if (ovpFn == NULL)
- {
- // Fallback to CLR v4 Beta1 path, but skip some of the checking we'd normally do (maxSupportedVersion, etc.)
- OpenVirtualProcess2FnPtr ovp2Fn = (OpenVirtualProcess2FnPtr)GetProcAddress(hDbi, "OpenVirtualProcess2");
- if (ovp2Fn == NULL)
- {
- hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
- }
- else
- {
- hr = ovp2Fn(moduleBaseAddress, pDataTarget, hDac, riidProcess, ppProcess, pFlags);
- }
- }
- else
- {
- // Have a CLR v4 Beta2+ DBI, call it and let it do the version check
- hr = ovpFn(moduleBaseAddress, pDataTarget, hDac, pMaxDebuggerSupportedVersion, riidProcess, ppProcess, pFlags);
- if (FAILED(hr))
- {
- _ASSERTE(ppProcess == NULL || *ppProcess == NULL);
- _ASSERTE(pFlags == NULL || *pFlags == 0);
- }
- }
- }
- }
-
- //version is still valid in some failure cases
- if (pVersion != NULL &&
- (SUCCEEDED(hr) ||
- (hr == CORDBG_E_UNSUPPORTED_DEBUGGING_MODEL) ||
- (hr == CORDBG_E_UNSUPPORTED_FORWARD_COMPAT)))
- {
- memcpy(pVersion, &version, sizeof(CLR_DEBUGGING_VERSION));
- }
-
- if (pDacModulePath != NULL)
- {
-#ifdef FEATURE_PAL
- free(pDacModulePath);
-#else
- CoTaskMemFree(pDacModulePath);
-#endif
- }
-
- if (pDbiModulePath != NULL)
- {
-#ifdef FEATURE_PAL
- free(pDbiModulePath);
-#else
- CoTaskMemFree(pDbiModulePath);
-#endif
- }
-
- // free the data target we QI'ed earlier
- if (pDt != NULL)
- {
- pDt->Release();
- }
-
- return hr;
-}
-
-// Checks to see if this DAC is one of a known set of old DAC builds which contains an issue.
-// If so we retarget to a newer compatible version which has the bug fixed. This is done
-// by changing the PE information used to lookup the DAC.
-//
-// Arguments
-// pdwTimeStamp - on input, the timestamp of DAC as embedded in the CLR image
-// on output, a potentially new timestamp for an updated DAC to use
-// instead
-// pdwSizeOfImage - on input, the sizeOfImage of DAC as embedded in the CLR image
-// on output, a potentially new sizeOfImage for an updated DAC to use
-// instead
-VOID CLRDebuggingImpl::RetargetDacIfNeeded(DWORD* pdwTimeStamp,
- DWORD* pdwSizeOfImage)
-{
-
- // This code is auto generated by the CreateRetargetTable tool
- // on 3/4/2011 6:35 PM
- // and then copy-pasted here.
- //
- //
- //
- // Retarget the GDR1 amd64 build
- if( (*pdwTimeStamp == 0x4d536868) && (*pdwSizeOfImage == 0x17b000))
- {
- *pdwTimeStamp = 0x4d71a160;
- *pdwSizeOfImage = 0x17b000;
- }
- // Retarget the GDR1 x86 build
- else if( (*pdwTimeStamp == 0x4d5368f2) && (*pdwSizeOfImage == 0x120000))
- {
- *pdwTimeStamp = 0x4d71a14f;
- *pdwSizeOfImage = 0x120000;
- }
- // Retarget the RTM amd64 build
- else if( (*pdwTimeStamp == 0x4ba21fa7) && (*pdwSizeOfImage == 0x17b000))
- {
- *pdwTimeStamp = 0x4d71a13c;
- *pdwSizeOfImage = 0x17b000;
- }
- // Retarget the RTM x86 build
- else if( (*pdwTimeStamp == 0x4ba1da25) && (*pdwSizeOfImage == 0x120000))
- {
- *pdwTimeStamp = 0x4d71a128;
- *pdwSizeOfImage = 0x120000;
- }
- // This code is auto generated by the CreateRetargetTable tool
- // on 8/17/2011 1:28 AM
- // and then copy-pasted here.
- //
- //
- //
- // Retarget the GDR2 amd64 build
- else if( (*pdwTimeStamp == 0x4da428c7) && (*pdwSizeOfImage == 0x17b000))
- {
- *pdwTimeStamp = 0x4e4b7bc2;
- *pdwSizeOfImage = 0x17b000;
- }
- // Retarget the GDR2 x86 build
- else if( (*pdwTimeStamp == 0x4da3fe52) && (*pdwSizeOfImage == 0x120000))
- {
- *pdwTimeStamp = 0x4e4b7bb1;
- *pdwSizeOfImage = 0x120000;
- }
- // End auto-generated code
-}
-
-#define PE_FIXEDFILEINFO_SIGNATURE 0xFEEF04BD
-
-// The format of the special debugging resource we embed in CLRs starting in
-// v4
-struct CLR_DEBUG_RESOURCE
-{
- DWORD dwVersion;
- GUID signature;
- DWORD dwDacTimeStamp;
- DWORD dwDacSizeOfImage;
- DWORD dwDbiTimeStamp;
- DWORD dwDbiSizeOfImage;
-};
-
-// Checks to see if a module is a CLR and if so, fetches the debug data
-// from the embedded resource
-//
-// Arguments
-// pDataTarget - dataTarget for the process we are inspecting
-// moduleBaseAddress - base address of a module we should inspect
-// pVersion - output, the version of the CLR detected if this is a CLR
-// pdwDbiTimeStamp - the timestamp of DBI as embedded in the CLR image
-// pdwDbiSizeOfImage - the SizeOfImage of DBI as embedded in the CLR image
-// pDbiName - output, the filename of DBI (as calculated by this function but that might change)
-// dwDbiNameCharCount - input, the number of WCHARs in the buffer pointed to by pDbiName
-// pdwDacTimeStampe - the timestamp of DAC as embedded in the CLR image
-// pdwDacSizeOfImage - the SizeOfImage of DAC as embedded in the CLR image
-// pDacName - output, the filename of DAC (as calculated by this function but that might change)
-// dwDacNameCharCount - input, the number of WCHARs in the buffer pointed to by pDacName
-HRESULT CLRDebuggingImpl::GetCLRInfo(ICorDebugDataTarget* pDataTarget,
- ULONG64 moduleBaseAddress,
- CLR_DEBUGGING_VERSION* pVersion,
- DWORD* pdwDbiTimeStamp,
- DWORD* pdwDbiSizeOfImage,
- __out_z __inout_ecount(dwDbiNameCharCount) WCHAR* pDbiName,
- DWORD dwDbiNameCharCount,
- DWORD* pdwDacTimeStamp,
- DWORD* pdwDacSizeOfImage,
- __out_z __inout_ecount(dwDacNameCharCount) WCHAR* pDacName,
- DWORD dwDacNameCharCount)
-{
-#ifndef FEATURE_PAL
- if (m_isWindowsTarget)
- {
- WORD imageFileMachine = 0;
- DWORD resourceSectionRVA = 0;
- HRESULT hr = GetMachineAndResourceSectionRVA(pDataTarget, moduleBaseAddress, &imageFileMachine, &resourceSectionRVA);
-
- // We want the version resource which has type = RT_VERSION = 16, name = 1, language = 0x409
- DWORD versionResourceRVA = 0;
- DWORD versionResourceSize = 0;
- if(SUCCEEDED(hr))
- {
- hr = GetResourceRvaFromResourceSectionRva(pDataTarget, moduleBaseAddress, resourceSectionRVA, 16, 1, 0x409,
- &versionResourceRVA, &versionResourceSize);
- }
-
- // At last we get our version info
- VS_FIXEDFILEINFO fixedFileInfo = {0};
- if(SUCCEEDED(hr))
- {
- // The version resource has 3 words, then the unicode string "VS_VERSION_INFO"
- // (16 WCHARS including the null terminator)
- // then padding to a 32-bit boundary, then the VS_FIXEDFILEINFO struct
- DWORD fixedFileInfoRVA = ((versionResourceRVA + 3*2 + 16*2 + 3)/4)*4;
- hr = ReadFromDataTarget(pDataTarget, moduleBaseAddress + fixedFileInfoRVA, (BYTE*)&fixedFileInfo, sizeof(fixedFileInfo));
- }
-
- //Verify the signature on the version resource
- if(SUCCEEDED(hr) && fixedFileInfo.dwSignature != PE_FIXEDFILEINFO_SIGNATURE)
- {
- hr = CORDBG_E_NOT_CLR;
- }
-
- // Record the version information
- if(SUCCEEDED(hr))
- {
- pVersion->wMajor = (WORD) (fixedFileInfo.dwProductVersionMS >> 16);
- pVersion->wMinor = (WORD) (fixedFileInfo.dwProductVersionMS & 0xFFFF);
- pVersion->wBuild = (WORD) (fixedFileInfo.dwProductVersionLS >> 16);
- pVersion->wRevision = (WORD) (fixedFileInfo.dwProductVersionLS & 0xFFFF);
- }
-
- // Now grab the special clr debug info resource
- // We may need to scan a few different names searching though...
- // 1) CLRDEBUGINFO<host_os><host_arch> where host_os = 'WINDOWS' or 'CORESYS' and host_arch = 'X86' or 'ARM' or 'AMD64'
- // 2) For back-compat if the host os is windows and the host architecture matches the target then CLRDEBUGINFO is used with no suffix.
- DWORD debugResourceRVA = 0;
- DWORD debugResourceSize = 0;
- BOOL useCrossPlatformNaming = FALSE;
- if(SUCCEEDED(hr))
- {
- // the initial state is that we haven't found a proper resource
- HRESULT hrGetResource = E_FAIL;
-
- // First check for the resource which has type = RC_DATA = 10, name = "CLRDEBUGINFO<host_os><host_arch>", language = 0
- #if defined (HOST_IS_WINDOWS_OS) && defined(_HOST_X86_)
- const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSX86");
- #endif
-
- #if !defined (HOST_IS_WINDOWS_OS) && defined(_HOST_X86_)
- const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSX86");
- #endif
-
- #if defined (HOST_IS_WINDOWS_OS) && defined(_HOST_AMD64_)
- const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSAMD64");
- #endif
-
- #if !defined (HOST_IS_WINDOWS_OS) && defined(_HOST_AMD64_)
- const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSAMD64");
- #endif
-
- #if defined (HOST_IS_WINDOWS_OS) && defined(_HOST_ARM64_)
- const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSARM64");
- #endif
-
- #if !defined (HOST_IS_WINDOWS_OS) && defined(_HOST_ARM64_)
- const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSARM64");
- #endif
-
- #if !defined (HOST_IS_WINDOWS_OS) && defined(_HOST_MIPS64_)
- const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSMIPS64");
- #endif
-
- #if defined (HOST_IS_WINDOWS_OS) && defined(_HOST_ARM_)
- const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSARM");
- #endif
-
- #if !defined (HOST_IS_WINDOWS_OS) && defined(_HOST_ARM_)
- const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSARM");
- #endif
-
- hrGetResource = GetResourceRvaFromResourceSectionRvaByName(pDataTarget, moduleBaseAddress, resourceSectionRVA, 10, resourceName, 0,
- &debugResourceRVA, &debugResourceSize);
- useCrossPlatformNaming = SUCCEEDED(hrGetResource);
-
-
- #if defined(HOST_IS_WINDOWS_OS) && (defined(_HOST_X86_) || defined(_HOST_AMD64_) || defined(_HOST_ARM_))
- #if defined(_HOST_X86_)
- #define _HOST_MACHINE_TYPE IMAGE_FILE_MACHINE_I386
- #elif defined(_HOST_AMD64_)
- #define _HOST_MACHINE_TYPE IMAGE_FILE_MACHINE_AMD64
- #elif defined(_HOST_ARM_)
- #define _HOST_MACHINE_TYPE IMAGE_FILE_MACHINE_ARMNT
- #endif
-
- // if this is windows, and if host_arch matches target arch then we can fallback to searching for CLRDEBUGINFO on failure
- if(FAILED(hrGetResource) && (imageFileMachine == _HOST_MACHINE_TYPE))
- {
- hrGetResource = GetResourceRvaFromResourceSectionRvaByName(pDataTarget, moduleBaseAddress, resourceSectionRVA, 10, W("CLRDEBUGINFO"), 0,
- &debugResourceRVA, &debugResourceSize);
- }
-
- #undef _HOST_MACHINE_TYPE
- #endif
- // if the search failed, we don't recognize the CLR
- if(FAILED(hrGetResource))
- hr = CORDBG_E_NOT_CLR;
- }
-
- CLR_DEBUG_RESOURCE debugResource;
- if(SUCCEEDED(hr) && debugResourceSize != sizeof(debugResource))
- {
- hr = CORDBG_E_NOT_CLR;
- }
-
- // Get the special debug resource from the image and return the results
- if(SUCCEEDED(hr))
- {
- hr = ReadFromDataTarget(pDataTarget, moduleBaseAddress + debugResourceRVA, (BYTE*)&debugResource, sizeof(debugResource));
- }
- if(SUCCEEDED(hr) && (debugResource.dwVersion != 0))
- {
- hr = CORDBG_E_NOT_CLR;
- }
-
- // The signature needs to match m_skuId exactly, except for m_skuId=CLR_ID_ONECORE_CLR which is
- // also compatible with the older CLR_ID_PHONE_CLR signature.
- if(SUCCEEDED(hr) &&
- (debugResource.signature != m_skuId) &&
- !( (debugResource.signature == CLR_ID_PHONE_CLR) && (m_skuId == CLR_ID_ONECORE_CLR) ))
- {
- hr = CORDBG_E_NOT_CLR;
- }
-
- if(SUCCEEDED(hr) &&
- (debugResource.signature != CLR_ID_ONECORE_CLR) &&
- useCrossPlatformNaming)
- {
- FormatLongDacModuleName(pDacName, dwDacNameCharCount, imageFileMachine, &fixedFileInfo);
- swprintf_s(pDbiName, dwDbiNameCharCount, W("%s_%s.dll"), MAIN_DBI_MODULE_NAME_W, W("x86"));
- }
- else
- {
- if(m_skuId == CLR_ID_V4_DESKTOP)
- swprintf_s(pDacName, dwDacNameCharCount, W("%s.dll"), CLR_DAC_MODULE_NAME_W);
- else
- swprintf_s(pDacName, dwDacNameCharCount, W("%s.dll"), CORECLR_DAC_MODULE_NAME_W);
- swprintf_s(pDbiName, dwDbiNameCharCount, W("%s.dll"), MAIN_DBI_MODULE_NAME_W);
- }
-
- if(SUCCEEDED(hr))
- {
- *pdwDbiTimeStamp = debugResource.dwDbiTimeStamp;
- *pdwDbiSizeOfImage = debugResource.dwDbiSizeOfImage;
- *pdwDacTimeStamp = debugResource.dwDacTimeStamp;
- *pdwDacSizeOfImage = debugResource.dwDacSizeOfImage;
- }
-
- // any failure should be interpreted as this module not being a CLR
- if(FAILED(hr))
- {
- return CORDBG_E_NOT_CLR;
- }
- else
- {
- return S_OK;
- }
- }
- else
-#endif // FEATURE_PAL
- {
- swprintf_s(pDacName, dwDacNameCharCount, W("%s"), MAKEDLLNAME_W(CORECLR_DAC_MODULE_NAME_W));
- swprintf_s(pDbiName, dwDbiNameCharCount, W("%s"), MAKEDLLNAME_W(MAIN_DBI_MODULE_NAME_W));
-
- pVersion->wMajor = 0;
- pVersion->wMinor = 0;
- pVersion->wBuild = 0;
- pVersion->wRevision = 0;
-
- *pdwDbiTimeStamp = 0;
- *pdwDbiSizeOfImage = 0;
- *pdwDacTimeStamp = 0;
- *pdwDacSizeOfImage = 0;
-
- return S_OK;
- }
-}
-
-// Formats the long name for DAC
-HRESULT CLRDebuggingImpl::FormatLongDacModuleName(__out_z __inout_ecount(cchBuffer) WCHAR * pBuffer,
- DWORD cchBuffer,
- DWORD targetImageFileMachine,
- VS_FIXEDFILEINFO * pVersion)
-{
-
-#ifndef HOST_IS_WINDOWS_OS
- _ASSERTE(!"NYI");
- return E_NOTIMPL;
-#endif
-
-#if defined(_HOST_X86_)
- const WCHAR* pHostArch = W("x86");
-#elif defined(_HOST_AMD64_)
- const WCHAR* pHostArch = W("amd64");
-#elif defined(_HOST_ARM_)
- const WCHAR* pHostArch = W("arm");
-#elif defined(_HOST_ARM64_)
- const WCHAR* pHostArch = W("arm64");
-#elif defined(_HOST_MIPS64_)
- const WCHAR* pHostArch = W("mips64");
-#else
- _ASSERTE(!"Unknown host arch");
- return E_NOTIMPL;
-#endif
-
- const WCHAR* pDacBaseName = NULL;
- if(m_skuId == CLR_ID_V4_DESKTOP)
- pDacBaseName = CLR_DAC_MODULE_NAME_W;
- else if(m_skuId == CLR_ID_CORECLR || m_skuId == CLR_ID_PHONE_CLR || m_skuId == CLR_ID_ONECORE_CLR)
- pDacBaseName = CORECLR_DAC_MODULE_NAME_W;
- else
- {
- _ASSERTE(!"Unknown SKU id");
- return E_UNEXPECTED;
- }
-
- const WCHAR* pTargetArch = NULL;
- if(targetImageFileMachine == IMAGE_FILE_MACHINE_I386)
- {
- pTargetArch = W("x86");
- }
- else if(targetImageFileMachine == IMAGE_FILE_MACHINE_AMD64)
- {
- pTargetArch = W("amd64");
- }
- else if(targetImageFileMachine == IMAGE_FILE_MACHINE_ARMNT)
- {
- pTargetArch = W("arm");
- }
- else if(targetImageFileMachine == IMAGE_FILE_MACHINE_ARM64)
- {
- pTargetArch = W("arm64");
- }
- else if(targetImageFileMachine == IMAGE_FILE_MACHINE_MIPS64)
- {
- pTargetArch = W("mips64");
- }
- else
- {
- _ASSERTE(!"Unknown target image file machine type");
- return E_INVALIDARG;
- }
-
- const WCHAR* pBuildFlavor = W("");
- if(pVersion->dwFileFlags & VS_FF_DEBUG)
- {
- if(pVersion->dwFileFlags & VS_FF_SPECIALBUILD)
- pBuildFlavor = W(".dbg");
- else
- pBuildFlavor = W(".chk");
- }
-
- // WARNING: if you change the formatting make sure you recalculate the maximum
- // possible size string and verify callers pass a big enough buffer. This doesn't
- // have to be a tight estimate, just make sure its >= the biggest possible DAC name
- // and it can be calculated statically
- DWORD minCchBuffer =
- (DWORD) wcslen(CLR_DAC_MODULE_NAME_W) + (DWORD) wcslen(CORECLR_DAC_MODULE_NAME_W) + // max name
- 10 + // max host arch
- 10 + // max target arch
- 40 + // max version
- 10 + // max build flavor
- (DWORD) wcslen(W("name_host_target_version.flavor.dll")) + // max intermediate formatting chars
- 1; // null terminator
-
- // validate the output buffer is larger than our estimate above
- _ASSERTE(cchBuffer >= minCchBuffer);
- if(!(cchBuffer >= minCchBuffer)) return E_INVALIDARG;
-
- swprintf_s(pBuffer, cchBuffer, W("%s_%s_%s_%u.%u.%u.%02u%s.dll"),
- pDacBaseName,
- pHostArch,
- pTargetArch,
- pVersion->dwProductVersionMS >> 16,
- pVersion->dwProductVersionMS & 0xFFFF,
- pVersion->dwProductVersionLS >> 16,
- pVersion->dwProductVersionLS & 0xFFFF,
- pBuildFlavor);
- return S_OK;
-}
-
-// An implementation of ICLRDebugging::CanUnloadNow
-//
-// Arguments:
-// hModule - a handle to a module provided earlier by ProvideLibrary
-//
-// Returns:
-// S_OK if the library is no longer in use and can be unloaded, S_FALSE otherwise
-//
-STDMETHODIMP CLRDebuggingImpl::CanUnloadNow(HMODULE hModule)
-{
- // In V4 at least we don't support any unloading.
- HRESULT hr = S_FALSE;
-
- return hr;
-}
-
-
-
-STDMETHODIMP CLRDebuggingImpl::QueryInterface(REFIID riid, void **ppvObject)
-{
- HRESULT hr = S_OK;
-
- if (riid == __uuidof(IUnknown))
- {
- IUnknown *pItf = static_cast<IUnknown *>(this);
- pItf->AddRef();
- *ppvObject = pItf;
- }
- else if (riid == __uuidof(ICLRDebugging))
- {
- ICLRDebugging *pItf = static_cast<ICLRDebugging *>(this);
- pItf->AddRef();
- *ppvObject = pItf;
- }
- else
- hr = E_NOINTERFACE;
-
- return hr;
-}
-
-// Standard AddRef implementation
-ULONG CLRDebuggingImpl::AddRef()
-{
- return InterlockedIncrement(&m_cRef);
-}
-
-// Standard Release implementation.
-ULONG CLRDebuggingImpl::Release()
-{
- _ASSERTE(m_cRef > 0);
-
- ULONG cRef = InterlockedDecrement(&m_cRef);
-
- if (cRef == 0)
- delete this; // Relies on virtual dtor to work properly.
-
- return cRef;
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-//*****************************************************************************
-// debugshim.h
-//
-
-//
-//*****************************************************************************
-
-#ifndef _DEBUG_SHIM_
-#define _DEBUG_SHIM_
-
-#include "cor.h"
-#include "cordebug.h"
-#include <wchar.h>
-#include <metahost.h>
-
-#define CORECLR_DAC_MODULE_NAME_W W("mscordaccore")
-#define CLR_DAC_MODULE_NAME_W W("mscordacwks")
-#define MAIN_DBI_MODULE_NAME_W W("mscordbi")
-
-// forward declaration
-struct ICorDebugDataTarget;
-
-// ICLRDebugging implementation.
-class CLRDebuggingImpl : public ICLRDebugging
-{
-
-public:
- CLRDebuggingImpl(GUID skuId, bool isWindowsTarget) : m_cRef(0), m_skuId(skuId), m_isWindowsTarget(isWindowsTarget)
- {
- }
-
- virtual ~CLRDebuggingImpl() {}
-
-public:
- // ICLRDebugging methods:
- STDMETHOD(OpenVirtualProcess(
- ULONG64 moduleBaseAddress,
- IUnknown * pDataTarget,
- ICLRDebuggingLibraryProvider * pLibraryProvider,
- CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion,
- REFIID riidProcess,
- IUnknown ** ppProcess,
- CLR_DEBUGGING_VERSION * pVersion,
- CLR_DEBUGGING_PROCESS_FLAGS * pFlags));
-
- STDMETHOD(CanUnloadNow(HMODULE hModule));
-
- //IUnknown methods:
- STDMETHOD(QueryInterface(
- REFIID riid,
- void **ppvObject));
-
- // Standard AddRef implementation
- STDMETHOD_(ULONG, AddRef());
-
- // Standard Release implementation.
- STDMETHOD_(ULONG, Release());
-
-
-
-private:
- VOID RetargetDacIfNeeded(DWORD* pdwTimeStamp,
- DWORD* pdwSizeOfImage);
-
- HRESULT GetCLRInfo(ICorDebugDataTarget * pDataTarget,
- ULONG64 moduleBaseAddress,
- CLR_DEBUGGING_VERSION * pVersion,
- DWORD * pdwDbiTimeStamp,
- DWORD * pdwDbiSizeOfImage,
- __out_z __inout_ecount(dwDbiNameCharCount) WCHAR * pDbiName,
- DWORD dwDbiNameCharCount,
- DWORD * pdwDacTimeStamp,
- DWORD * pdwDacSizeOfImage,
- __out_z __inout_ecount(dwDacNameCharCount) WCHAR * pDacName,
- DWORD dwDacNameCharCount);
-
- HRESULT FormatLongDacModuleName(__out_z __inout_ecount(cchBuffer) WCHAR * pBuffer,
- DWORD cchBuffer,
- DWORD targetImageFileMachine,
- VS_FIXEDFILEINFO * pVersion);
-
- volatile LONG m_cRef;
- GUID m_skuId;
- bool m_isWindowsTarget;
-
-}; // class CLRDebuggingImpl
-
-#endif
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|x64">
- <Configuration>Debug</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Checked|x64">
- <Configuration>Checked</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|x64">
- <Configuration>Release</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="RelWithDebInfo|x64">
- <Configuration>RelWithDebInfo</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{6A94C5FE-8706-3505-834E-DA16242F3864}</ProjectGuid>
- <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
- <Keyword>Win32Proj</Keyword>
- <Platform>x64</Platform>
- <ProjectName>debugshim</ProjectName>
- <VCProjectUpgraderObjectName>NoUpgrade</VCProjectUpgraderObjectName>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
- <ConfigurationType>StaticLibrary</ConfigurationType>
- <CharacterSet>MultiByte</CharacterSet>
- <PlatformToolset>v142</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'" Label="Configuration">
- <ConfigurationType>StaticLibrary</ConfigurationType>
- <CharacterSet>MultiByte</CharacterSet>
- <PlatformToolset>v141</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
- <ConfigurationType>StaticLibrary</ConfigurationType>
- <CharacterSet>MultiByte</CharacterSet>
- <PlatformToolset>v141</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'" Label="Configuration">
- <ConfigurationType>StaticLibrary</ConfigurationType>
- <CharacterSet>MultiByte</CharacterSet>
- <PlatformToolset>v141</PlatformToolset>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
- </ImportGroup>
- <ImportGroup 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>10.0.20506.1</_ProjectFileVersion>
- <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ArtifactsObjDir)Windows_NT.x64.Debug\src\SOS\debugshim\Debug\</OutDir>
- <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">debugshim.dir\Debug\</IntDir>
- <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">debugshim</TargetName>
- <TargetExt Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.lib</TargetExt>
- <OutDir Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">$(ArtifactsObjDir)Windows_NT.x64.Debug\src\SOS\debugshim\Checked\</OutDir>
- <IntDir Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">debugshim.dir\Checked\</IntDir>
- <TargetName Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">debugshim</TargetName>
- <TargetExt Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">.lib</TargetExt>
- <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ArtifactsObjDir)Windows_NT.x64.Debug\src\SOS\debugshim\Release\</OutDir>
- <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">debugshim.dir\Release\</IntDir>
- <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">debugshim</TargetName>
- <TargetExt Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.lib</TargetExt>
- <OutDir Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">$(ArtifactsObjDir)Windows_NT.x64.Debug\src\SOS\debugshim\RelWithDebInfo\</OutDir>
- <IntDir Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">debugshim.dir\RelWithDebInfo\</IntDir>
- <TargetName Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">debugshim</TargetName>
- <TargetExt Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">.lib</TargetExt>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <ClCompile>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <AdditionalOptions>%(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8 /homeparams</AdditionalOptions>
- <AssemblerListingLocation>Debug/</AssemblerListingLocation>
- <BufferSecurityCheck>true</BufferSecurityCheck>
- <CompileAs>CompileAsCpp</CompileAs>
- <ControlFlowGuard>Guard</ControlFlowGuard>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4960;4961;4603;4627;4838;4456;4457;4458;4459;4091</DisableSpecificWarnings>
- <ExceptionHandling>Async</ExceptionHandling>
- <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
- <ForcedIncludeFiles>WarningControl.h</ForcedIncludeFiles>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <MinimalRebuild>false</MinimalRebuild>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <OmitDefaultLibName>true</OmitDefaultLibName>
- <OmitFramePointers>false</OmitFramePointers>
- <Optimization>Disabled</Optimization>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <RuntimeTypeInfo>false</RuntimeTypeInfo>
- <StringPooling>true</StringPooling>
- <StructMemberAlignment>8Bytes</StructMemberAlignment>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <TreatSpecificWarningsAsErrors>4640</TreatSpecificWarningsAsErrors>
- <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
- <TreatWarningAsError>true</TreatWarningAsError>
- <UndefinePreprocessorDefinitions>_MT</UndefinePreprocessorDefinitions>
- <UseFullPaths>true</UseFullPaths>
- <WarningLevel>Level3</WarningLevel>
- <PreprocessorDefinitions>DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <ObjectFileName>$(IntDir)</ObjectFileName>
- </ClCompile>
- <ResourceCompile>
- <PreprocessorDefinitions>WIN32;_DEBUG;DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- </ResourceCompile>
- <MASM>
- <PreprocessorDefinitions>DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalOptions>%(AdditionalOptions) /ZH:SHA_256</AdditionalOptions>
- <IncludePaths>..\..\pal\prebuilt\inc;..\..\inc;%(IncludePaths)</IncludePaths>
- </MASM>
- <Midl>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
- <HeaderFileName>%(Filename).h</HeaderFileName>
- <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
- <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
- <ProxyFileName>%(Filename)_p.c</ProxyFileName>
- </Midl>
- <Lib>
- <AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
- </Lib>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">
- <ClCompile>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <AdditionalOptions>%(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8</AdditionalOptions>
- <AssemblerListingLocation>Checked/</AssemblerListingLocation>
- <BufferSecurityCheck>true</BufferSecurityCheck>
- <CompileAs>CompileAsCpp</CompileAs>
- <ControlFlowGuard>Guard</ControlFlowGuard>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4960;4961;4603;4627;4838;4456;4457;4458;4459;4091</DisableSpecificWarnings>
- <ExceptionHandling>Async</ExceptionHandling>
- <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
- <ForcedIncludeFiles>WarningControl.h</ForcedIncludeFiles>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <MinimalRebuild>false</MinimalRebuild>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <OmitDefaultLibName>true</OmitDefaultLibName>
- <OmitFramePointers>false</OmitFramePointers>
- <Optimization>MinSpace</Optimization>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <RuntimeTypeInfo>false</RuntimeTypeInfo>
- <StringPooling>true</StringPooling>
- <StructMemberAlignment>8Bytes</StructMemberAlignment>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <TreatSpecificWarningsAsErrors>4640</TreatSpecificWarningsAsErrors>
- <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
- <TreatWarningAsError>true</TreatWarningAsError>
- <UndefinePreprocessorDefinitions>_MT</UndefinePreprocessorDefinitions>
- <UseFullPaths>true</UseFullPaths>
- <WarningLevel>Level3</WarningLevel>
- <PreprocessorDefinitions>DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR="Checked";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <ObjectFileName>$(IntDir)</ObjectFileName>
- </ClCompile>
- <ResourceCompile>
- <PreprocessorDefinitions>WIN32;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR=\"Checked\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- </ResourceCompile>
- <MASM>
- <PreprocessorDefinitions>DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR="Checked";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalOptions>%(AdditionalOptions) /ZH:SHA_256</AdditionalOptions>
- <IncludePaths>..\..\pal\prebuilt\inc;..\..\inc;%(IncludePaths)</IncludePaths>
- </MASM>
- <Midl>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
- <HeaderFileName>%(Filename).h</HeaderFileName>
- <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
- <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
- <ProxyFileName>%(Filename)_p.c</ProxyFileName>
- </Midl>
- <Lib>
- <AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
- </Lib>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <ClCompile>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <AdditionalOptions>%(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8</AdditionalOptions>
- <AssemblerListingLocation>Release/</AssemblerListingLocation>
- <BufferSecurityCheck>true</BufferSecurityCheck>
- <CompileAs>CompileAsCpp</CompileAs>
- <ControlFlowGuard>Guard</ControlFlowGuard>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4960;4961;4603;4627;4838;4456;4457;4458;4459;4091</DisableSpecificWarnings>
- <ExceptionHandling>Async</ExceptionHandling>
- <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
- <ForcedIncludeFiles>WarningControl.h</ForcedIncludeFiles>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <MinimalRebuild>false</MinimalRebuild>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <OmitDefaultLibName>true</OmitDefaultLibName>
- <OmitFramePointers>false</OmitFramePointers>
- <Optimization>MinSpace</Optimization>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <RuntimeTypeInfo>false</RuntimeTypeInfo>
- <StringPooling>true</StringPooling>
- <StructMemberAlignment>8Bytes</StructMemberAlignment>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <TreatSpecificWarningsAsErrors>4640</TreatSpecificWarningsAsErrors>
- <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
- <TreatWarningAsError>true</TreatWarningAsError>
- <UndefinePreprocessorDefinitions>_MT</UndefinePreprocessorDefinitions>
- <UseFullPaths>true</UseFullPaths>
- <WarningLevel>Level3</WarningLevel>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <PreprocessorDefinitions>NDEBUG;URTBLDENV_FRIENDLY=Retail;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR="Release";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <ObjectFileName>$(IntDir)</ObjectFileName>
- </ClCompile>
- <ResourceCompile>
- <PreprocessorDefinitions>WIN32;NDEBUG;URTBLDENV_FRIENDLY=Retail;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR=\"Release\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- </ResourceCompile>
- <MASM>
- <PreprocessorDefinitions>NDEBUG;URTBLDENV_FRIENDLY=Retail;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR="Release";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalOptions>%(AdditionalOptions) /ZH:SHA_256</AdditionalOptions>
- <IncludePaths>..\..\pal\prebuilt\inc;..\..\inc;%(IncludePaths)</IncludePaths>
- </MASM>
- <Midl>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
- <HeaderFileName>%(Filename).h</HeaderFileName>
- <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
- <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
- <ProxyFileName>%(Filename)_p.c</ProxyFileName>
- </Midl>
- <Lib>
- <AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
- <LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
- </Lib>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">
- <ClCompile>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <AdditionalOptions>%(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8</AdditionalOptions>
- <AssemblerListingLocation>RelWithDebInfo/</AssemblerListingLocation>
- <BufferSecurityCheck>true</BufferSecurityCheck>
- <CompileAs>CompileAsCpp</CompileAs>
- <ControlFlowGuard>Guard</ControlFlowGuard>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4960;4961;4603;4627;4838;4456;4457;4458;4459;4091</DisableSpecificWarnings>
- <ExceptionHandling>Async</ExceptionHandling>
- <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
- <ForcedIncludeFiles>WarningControl.h</ForcedIncludeFiles>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <MinimalRebuild>false</MinimalRebuild>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <OmitDefaultLibName>true</OmitDefaultLibName>
- <OmitFramePointers>false</OmitFramePointers>
- <Optimization>MinSpace</Optimization>
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <RuntimeTypeInfo>false</RuntimeTypeInfo>
- <StringPooling>true</StringPooling>
- <StructMemberAlignment>8Bytes</StructMemberAlignment>
- <SuppressStartupBanner>true</SuppressStartupBanner>
- <TreatSpecificWarningsAsErrors>4640</TreatSpecificWarningsAsErrors>
- <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
- <TreatWarningAsError>true</TreatWarningAsError>
- <UndefinePreprocessorDefinitions>_MT</UndefinePreprocessorDefinitions>
- <UseFullPaths>true</UseFullPaths>
- <WarningLevel>Level3</WarningLevel>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <PreprocessorDefinitions>NDEBUG;URTBLDENV_FRIENDLY=Retail;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR="RelWithDebInfo";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <ObjectFileName>$(IntDir)</ObjectFileName>
- </ClCompile>
- <ResourceCompile>
- <PreprocessorDefinitions>WIN32;NDEBUG;URTBLDENV_FRIENDLY=Retail;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR=\"RelWithDebInfo\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- </ResourceCompile>
- <MASM>
- <PreprocessorDefinitions>NDEBUG;URTBLDENV_FRIENDLY=Retail;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;WIN32;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_SECURE_SCL=0;HOST_IS_WINDOWS_OS;CMAKE_INTDIR="RelWithDebInfo";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalOptions>%(AdditionalOptions) /ZH:SHA_256</AdditionalOptions>
- <IncludePaths>..\..\pal\prebuilt\inc;..\..\inc;%(IncludePaths)</IncludePaths>
- </MASM>
- <Midl>
- <AdditionalIncludeDirectories>..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
- <HeaderFileName>%(Filename).h</HeaderFileName>
- <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
- <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
- <ProxyFileName>%(Filename)_p.c</ProxyFileName>
- </Midl>
- <Lib>
- <AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
- <LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
- </Lib>
- </ItemDefinitionGroup>
- <ItemGroup>
- <ClCompile Include="debugshim.cpp" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="debugshim.h" />
- </ItemGroup>
- <ItemGroup>
- <Text Include="CMakeLists.txt" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
- </ImportGroup>
-</Project>
\ No newline at end of file
if(CLR_CMAKE_HOST_UNIX)
add_definitions(-DHOST_UNIX)
- add_compile_options(-fPIC)
else(CLR_CMAKE_HOST_UNIX)
add_definitions(-DHOST_WINDOWS)
endif(CLR_CMAKE_HOST_UNIX)
static bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
{
ArrayHolder<char> hostPath = new char[MAX_LONGPATH+1];
- if (::GetModuleFileName(NULL, hostPath, MAX_LONGPATH) == 0)
+ if (::GetModuleFileNameA(NULL, hostPath, MAX_LONGPATH) == 0)
{
return false;
}
* to the standard code-manager spec.
*/
-#ifndef TARGET_UNIX
-#include "utilcode.h" // For _ASSERTE()
-#endif //!TARGET_UNIX
#include "gcdump.h"
/*****************************************************************************/
#ifdef TARGET_X86
/*****************************************************************************/
-#ifndef TARGET_UNIX
-#include "utilcode.h" // For _ASSERTE()
-#endif //!TARGET_UNIX
#include "gcdump.h"
--- /dev/null
+add_definitions(-DFEATURE_NO_HOST)
+add_definitions(-DSELF_NO_HOST)
+add_definitions(-D_BLD_CLR)
+
+set(DBGSHIM_SOURCES
+ dbgshim.cpp
+ debugshim.cpp
+)
+
+if(CLR_CMAKE_HOST_WIN32)
+ # Use static crt
+ set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
+ add_definitions(-DFX_VER_INTERNALNAME_STR=dbgshim.dll)
+endif(CLR_CMAKE_HOST_WIN32)
+
+if(CLR_CMAKE_HOST_WIN32)
+ list(APPEND DBGSHIM_SOURCES
+ dbgshim.rc
+ )
+ preprocess_file(${CMAKE_CURRENT_SOURCE_DIR}/dbgshim.ntdef ${CMAKE_CURRENT_BINARY_DIR}/dbgshim.def)
+ list(APPEND DBGSHIM_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dbgshim.def)
+else(CLR_CMAKE_HOST_WIN32)
+ set(DEF_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/dbgshim_unixexports.src)
+ set(EXPORTS_FILE ${CMAKE_CURRENT_BINARY_DIR}/dbgshim.exports)
+ generate_exports_file(${DEF_SOURCES} ${EXPORTS_FILE})
+
+ if(CLR_CMAKE_HOST_LINUX OR CLR_CMAKE_HOST_FREEBSD OR CLR_CMAKE_HOST_NETBSD OR CLR_CMAKE_HOST_SUNOS)
+ # This option is necessary to ensure that the overloaded delete operator defined inside
+ # of the utilcode will be used instead of the standard library delete operator.
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Xlinker -Bsymbolic")
+ endif(CLR_CMAKE_HOST_LINUX OR CLR_CMAKE_HOST_FREEBSD OR CLR_CMAKE_HOST_NETBSD OR CLR_CMAKE_HOST_SUNOS)
+
+ set_exports_linker_option(${EXPORTS_FILE})
+
+endif(CLR_CMAKE_HOST_WIN32)
+
+add_library_clr(dbgshim SHARED ${DBGSHIM_SOURCES})
+
+if(CLR_CMAKE_HOST_UNIX)
+ add_custom_target(dbgshim_exports DEPENDS ${EXPORTS_FILE})
+ add_dependencies(dbgshim dbgshim_exports)
+
+ set_property(TARGET dbgshim APPEND_STRING PROPERTY LINK_FLAGS ${EXPORTS_LINKER_OPTION})
+ set_property(TARGET dbgshim APPEND_STRING PROPERTY LINK_DEPENDS ${EXPORTS_FILE})
+endif(CLR_CMAKE_HOST_UNIX)
+
+set(DBGSHIM_LIBRARIES
+ corguids
+ dbgutil
+ utilcodestaticnohost
+)
+
+if(CLR_CMAKE_HOST_WIN32)
+ list(APPEND DBGSHIM_LIBRARIES
+ kernel32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_VCRT_LIB}
+ uuid.lib
+ user32.lib
+ advapi32.lib
+ ole32.lib
+ oleaut32.lib
+ WtsApi32.lib
+ version.lib
+ psapi.lib
+ )
+else()
+ list(APPEND DBGSHIM_LIBRARIES
+ coreclrpal
+ palrt
+ )
+endif(CLR_CMAKE_HOST_WIN32)
+
+target_link_libraries(dbgshim ${DBGSHIM_LIBRARIES})
+
+# add the install targets
+install_clr(TARGETS dbgshim DESTINATIONS . )
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// DbgShim.cpp
+//
+// This contains the APIs for creating a telesto managed-debugging session. These APIs serve to locate an
+// mscordbi.dll for a given telesto dll and then instantiate the ICorDebug object.
+//
+//*****************************************************************************
+
+#include <winwrap.h>
+#include <utilcode.h>
+#include <log.h>
+#include <tlhelp32.h>
+#include <cor.h>
+#include <sstring.h>
+#ifndef TARGET_UNIX
+#include <securityutil.h>
+#endif
+
+#include <ex.h>
+#include <cordebug.h> // for Version nunmbers
+#include <pedecoder.h>
+#include <getproductversionnumber.h>
+#include <dbgenginemetrics.h>
+#include <arrayholder.h>
+
+#ifndef TARGET_UNIX
+#define PSAPI_VERSION 2
+#include <psapi.h>
+#endif
+
+#include "dbgshim.h"
+
+/*
+
+// Here's a High-level overview of the API usage
+
+From the debugger:
+A debugger calls GetStartupNotificationEvent(pid of debuggee) to get an event, which is signalled when
+that process loads a Telesto. The debugger thus waits on that event, and when it's signalled, it can call
+EnumerateCLRs / CloseCLREnumeration to get an array of Telestos in the target process (including the one
+that was just loaded). It can then call CreateVersionStringFromModule, CreateDebuggingInterfaceFromVersion
+to attach to any or all Telestos of interest.
+
+From the debuggee:
+When a new Telesto spins up, it checks for the startup event (created via GetStartupNotificationEvent), and
+if it exists, it will:
+- signal it
+- wait on the "Continue" event, thus giving a debugger a chance to attach to the telesto
+
+Notes:
+- There is no CreateProcess (Launch) case. All Launching is really an "Early-attach case".
+
+*/
+
+#ifdef HOST_UNIX
+#define INITIALIZE_SHIM { if (PAL_InitializeDLL() != 0) return E_FAIL; }
+#else
+#define INITIALIZE_SHIM
+#endif
+
+// Contract for public APIs. These must be NOTHROW.
+#define PUBLIC_CONTRACT \
+ INITIALIZE_SHIM \
+ CONTRACTL \
+ { \
+ NOTHROW; \
+ } \
+ CONTRACTL_END;
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// CreateProcessForLaunch - a stripped down version of the Windows CreateProcess
+// that can be supported cross-platform.
+//
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CreateProcessForLaunch(
+ _In_ LPWSTR lpCommandLine,
+ _In_ BOOL bSuspendProcess,
+ _In_ LPVOID lpEnvironment,
+ _In_ LPCWSTR lpCurrentDirectory,
+ _Out_ PDWORD pProcessId,
+ _Out_ HANDLE *pResumeHandle)
+{
+ PUBLIC_CONTRACT;
+
+ PROCESS_INFORMATION processInfo;
+ STARTUPINFOW startupInfo;
+ DWORD dwCreationFlags = 0;
+
+ ZeroMemory(&processInfo, sizeof(processInfo));
+ ZeroMemory(&startupInfo, sizeof(startupInfo));
+
+ startupInfo.cb = sizeof(startupInfo);
+
+ if (bSuspendProcess)
+ {
+ dwCreationFlags = CREATE_SUSPENDED;
+ }
+
+ BOOL result = CreateProcessW(
+ NULL,
+ lpCommandLine,
+ NULL,
+ NULL,
+ FALSE,
+ dwCreationFlags,
+ lpEnvironment,
+ lpCurrentDirectory,
+ &startupInfo,
+ &processInfo);
+
+ if (!result) {
+ *pProcessId = 0;
+ *pResumeHandle = NULL;
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ if (processInfo.hProcess != NULL)
+ {
+ CloseHandle(processInfo.hProcess);
+ }
+
+ *pProcessId = processInfo.dwProcessId;
+ *pResumeHandle = processInfo.hThread;
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// ResumeProcess - to be used with the CreateProcessForLaunch resume handle
+//
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+ResumeProcess(
+ _In_ HANDLE hResumeHandle)
+{
+ PUBLIC_CONTRACT;
+ if (ResumeThread(hResumeHandle) == (DWORD)-1)
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// CloseResumeHandle - to be used with the CreateProcessForLaunch resume handle
+//
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CloseResumeHandle(
+ _In_ HANDLE hResumeHandle)
+{
+ PUBLIC_CONTRACT;
+ if (!CloseHandle(hResumeHandle))
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+ return S_OK;
+}
+
+#ifdef TARGET_UNIX
+
+static
+void
+RuntimeStartupHandler(
+ char *pszModulePath,
+ HMODULE hModule,
+ PVOID parameter);
+
+#else // TARGET_UNIX
+
+static
+DWORD
+StartupHelperThread(
+ LPVOID p);
+
+static
+HRESULT
+GetContinueStartupEvent(
+ DWORD debuggeePID,
+ LPCWSTR szTelestoFullPath,
+ _Out_ HANDLE *phContinueStartupEvent);
+
+#endif // TARGET_UNIX
+
+// Functions that we'll look for in the loaded Mscordbi module.
+typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObject)(
+ int iDebuggerVersion,
+ DWORD pid,
+ HMODULE hmodTargetCLR,
+ IUnknown **ppCordb);
+
+typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObjectEx)(
+ int iDebuggerVersion,
+ DWORD pid,
+ LPCWSTR lpApplicationGroupId,
+ HMODULE hmodTargetCLR,
+ IUnknown **ppCordb);
+
+HRESULT CreateCoreDbgWithSandboxSupport(
+ HMODULE hCLRModule, HMODULE hDBIModule, DWORD processId, LPCWSTR lpApplicationGroupId, int iDebuggerVersion, IUnknown **ppCordb)
+{
+ FPCoreCLRCreateCordbObjectEx fpCreate =
+ (FPCoreCLRCreateCordbObjectEx)GetProcAddress(hDBIModule, "CoreCLRCreateCordbObjectEx");
+ if (fpCreate == NULL)
+ {
+ return CORDBG_E_INCOMPATIBLE_PROTOCOL;
+ }
+
+ return fpCreate(iDebuggerVersion, processId, lpApplicationGroupId, hCLRModule, ppCordb);
+}
+
+HRESULT CreateCoreDbgWithoutSandboxSupport(
+ HMODULE hCLRModule, HMODULE hDBIModule, DWORD processId, int iDebuggerVersion, IUnknown **ppCordb)
+{
+ FPCoreCLRCreateCordbObject fpCreate =
+ (FPCoreCLRCreateCordbObject)GetProcAddress(hDBIModule, "CoreCLRCreateCordbObject");
+ if (fpCreate == NULL)
+ {
+ return CORDBG_E_INCOMPATIBLE_PROTOCOL;
+ }
+
+ return fpCreate(iDebuggerVersion, processId, hCLRModule, ppCordb);
+}
+
+HRESULT CreateCoreDbg(
+ HMODULE hCLRModule, HMODULE hDBIModule, DWORD processId, LPCWSTR lpApplicationGroupId, int iDebuggerVersion, IUnknown **ppCordb)
+{
+ HRESULT hr = S_OK;
+
+ if (lpApplicationGroupId != NULL)
+ {
+ hr = CreateCoreDbgWithSandboxSupport(hCLRModule, hDBIModule, processId, lpApplicationGroupId, iDebuggerVersion, ppCordb);
+ }
+ else
+ {
+ hr = CreateCoreDbgWithoutSandboxSupport(hCLRModule, hDBIModule, processId, iDebuggerVersion, ppCordb);
+ }
+
+ return hr;
+}
+
+//
+// Helper class for RegisterForRuntimeStartup
+//
+class RuntimeStartupHelper
+{
+ LONG m_ref;
+ DWORD m_processId;
+ PSTARTUP_CALLBACK m_callback;
+ PVOID m_parameter;
+#ifdef TARGET_UNIX
+ PVOID m_unregisterToken;
+ LPWSTR m_applicationGroupId;
+#else
+ bool m_canceled;
+ HANDLE m_startupEvent;
+ DWORD m_threadId;
+ HANDLE m_threadHandle;
+#endif // TARGET_UNIX
+
+public:
+ RuntimeStartupHelper(DWORD dwProcessId, PSTARTUP_CALLBACK pfnCallback, PVOID parameter) :
+ m_ref(1),
+ m_processId(dwProcessId),
+ m_callback(pfnCallback),
+ m_parameter(parameter),
+#ifdef TARGET_UNIX
+ m_unregisterToken(NULL),
+ m_applicationGroupId(NULL)
+#else
+ m_canceled(false),
+ m_startupEvent(NULL),
+ m_threadId(0),
+ m_threadHandle(NULL)
+#endif // TARGET_UNIX
+ {
+ }
+
+ ~RuntimeStartupHelper()
+ {
+#ifdef TARGET_UNIX
+ if (m_applicationGroupId != NULL)
+ {
+ delete m_applicationGroupId;
+ }
+#else // TARGET_UNIX
+ if (m_startupEvent != NULL)
+ {
+ CloseHandle(m_startupEvent);
+ }
+ if (m_threadHandle != NULL)
+ {
+ CloseHandle(m_threadHandle);
+ }
+#endif // TARGET_UNIX
+ }
+
+ LONG AddRef()
+ {
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+ }
+
+ LONG Release()
+ {
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+ }
+
+#ifdef TARGET_UNIX
+
+ HRESULT Register(LPCWSTR lpApplicationGroupId)
+ {
+ if (lpApplicationGroupId != NULL)
+ {
+ int size = wcslen(lpApplicationGroupId) + 1;
+ m_applicationGroupId = new (nothrow) WCHAR[size];
+ if (m_applicationGroupId == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ wcscpy_s(m_applicationGroupId, size, lpApplicationGroupId);
+ }
+
+ DWORD pe = PAL_RegisterForRuntimeStartup(m_processId, m_applicationGroupId, RuntimeStartupHandler, this, &m_unregisterToken);
+ if (pe != NO_ERROR)
+ {
+ return HRESULT_FROM_WIN32(pe);
+ }
+ return S_OK;
+ }
+
+ void Unregister()
+ {
+ PAL_UnregisterForRuntimeStartup(m_unregisterToken);
+ }
+
+ void InvokeStartupCallback(char *pszModulePath, HMODULE hModule)
+ {
+ IUnknown *pCordb = NULL;
+ HMODULE hMod = NULL;
+ HRESULT hr = S_OK;
+
+ // If either of these are NULL, there was an error from the PAL
+ // callback. GetLastError returns the error code from the PAL.
+ if (pszModulePath == NULL || hModule == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ goto exit;
+ }
+
+ PAL_CPP_TRY
+ {
+ char dbiPath[MAX_LONGPATH];
+
+ char *pszLast = strrchr(pszModulePath, DIRECTORY_SEPARATOR_CHAR_A);
+ if (pszLast == NULL)
+ {
+ _ASSERT(!"InvokeStartupCallback: can find separator in coreclr path\n");
+ hr = E_INVALIDARG;
+ goto exit;
+ }
+
+ strncpy_s(dbiPath, ARRAY_SIZE(dbiPath), pszModulePath, pszLast - pszModulePath);
+ strcat_s(dbiPath, ARRAY_SIZE(dbiPath), DIRECTORY_SEPARATOR_STR_A MAKEDLLNAME_A("mscordbi"));
+
+ hMod = LoadLibraryA(dbiPath);
+ if (hMod == NULL)
+ {
+ hr = CORDBG_E_DEBUG_COMPONENT_MISSING;
+ goto exit;
+ }
+
+ HRESULT hr = CreateCoreDbg(hModule, hMod, m_processId, m_applicationGroupId, CorDebugVersion_2_0, &pCordb);
+ _ASSERTE((pCordb == NULL) == FAILED(hr));
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+
+ m_callback(pCordb, m_parameter, S_OK);
+ }
+ PAL_CPP_CATCH_ALL
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+ PAL_CPP_ENDTRY
+
+ exit:
+ if (FAILED(hr))
+ {
+ _ASSERTE(pCordb == NULL);
+
+ if (hMod != NULL)
+ {
+ FreeLibrary(hMod);
+ }
+
+ // Invoke the callback on error
+ m_callback(NULL, m_parameter, hr);
+ }
+ }
+
+#else // TARGET_UNIX
+
+ HRESULT Register(LPCWSTR lpApplicationGroupId)
+ {
+ HRESULT hr = GetStartupNotificationEvent(m_processId, &m_startupEvent);
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+
+ // Add a reference for the thread handler
+ AddRef();
+
+ m_threadHandle = CreateThread(
+ NULL,
+ 0,
+ ::StartupHelperThread,
+ this,
+ 0,
+ &m_threadId);
+
+ if (m_threadHandle == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ Release();
+ goto exit;
+ }
+
+ exit:
+ return hr;
+ }
+
+ bool AreAllHandlesValid(HANDLE *handleArray, DWORD arrayLength)
+ {
+ for (int i = 0; i < (int)arrayLength; i++)
+ {
+ HANDLE h = handleArray[i];
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ HRESULT InternalEnumerateCLRs(HANDLE** ppHandleArray, _In_reads_(*pdwArrayLength) LPWSTR** ppStringArray, DWORD* pdwArrayLength)
+ {
+ int numTries = 0;
+ HRESULT hr;
+
+ while (numTries < 25)
+ {
+ hr = EnumerateCLRs(m_processId, ppHandleArray, ppStringArray, pdwArrayLength);
+
+ // EnumerateCLRs uses the OS API CreateToolhelp32Snapshot which can return ERROR_BAD_LENGTH or
+ // ERROR_PARTIAL_COPY. If we get either of those, we try wait 1/10th of a second try again (that
+ // is the recommendation of the OS API owners).
+ if ((hr != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && (hr != HRESULT_FROM_WIN32(ERROR_BAD_LENGTH)))
+ {
+ // Just return any other error or if no handles were found (which means the coreclr module wasn't found yet).
+ if (FAILED(hr) || *ppHandleArray == NULL || *pdwArrayLength <= 0)
+ {
+ return hr;
+ }
+ // If EnumerateCLRs succeeded but any of the handles are INVALID_HANDLE_VALUE, then sleep and retry
+ // also. This fixes a race condition where dbgshim catches the coreclr module just being loaded but
+ // before g_hContinueStartupEvent has been initialized.
+ if (AreAllHandlesValid(*ppHandleArray, *pdwArrayLength))
+ {
+ return hr;
+ }
+ // Clean up memory allocated in EnumerateCLRs since this path it succeeded
+ CloseCLREnumeration(*ppHandleArray, *ppStringArray, *pdwArrayLength);
+
+ *ppHandleArray = NULL;
+ *ppStringArray = NULL;
+ *pdwArrayLength = 0;
+ }
+
+ // Sleep and retry enumerating the runtimes
+ Sleep(100);
+ numTries++;
+
+ if (m_canceled)
+ {
+ break;
+ }
+ }
+
+ // Indicate a timeout
+ hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
+
+ return hr;
+ }
+
+ void WakeRuntimes(HANDLE *handleArray, DWORD arrayLength)
+ {
+ if (handleArray != NULL)
+ {
+ for (int i = 0; i < (int)arrayLength; i++)
+ {
+ HANDLE h = handleArray[i];
+ if (h != NULL && h != INVALID_HANDLE_VALUE)
+ {
+ SetEvent(h);
+ }
+ }
+ }
+ }
+
+ void Unregister()
+ {
+ m_canceled = true;
+
+ HANDLE *handleArray = NULL;
+ LPWSTR *stringArray = NULL;
+ DWORD arrayLength = 0;
+
+ // Wake up runtime(s)
+ HRESULT hr = EnumerateCLRs(m_processId, &handleArray, &stringArray, &arrayLength);
+ if (SUCCEEDED(hr))
+ {
+ WakeRuntimes(handleArray, arrayLength);
+ CloseCLREnumeration(handleArray, stringArray, arrayLength);
+ }
+
+ // Wake up worker thread
+ SetEvent(m_startupEvent);
+
+ // Don't need to wake up and wait for the worker thread if called on it
+ if (m_threadId != GetCurrentThreadId())
+ {
+ // Wait for work thread to exit
+ WaitForSingleObject(m_threadHandle, INFINITE);
+ }
+ }
+
+ HRESULT InvokeStartupCallback(bool *pCoreClrExists)
+ {
+ HANDLE *handleArray = NULL;
+ LPWSTR *stringArray = NULL;
+ DWORD arrayLength = 0;
+ HRESULT hr = S_OK;
+
+ PAL_CPP_TRY
+ {
+ IUnknown *pCordb = NULL;
+ WCHAR verStr[MAX_LONGPATH];
+ DWORD verLen;
+
+ *pCoreClrExists = FALSE;
+
+ hr = InternalEnumerateCLRs(&handleArray, &stringArray, &arrayLength);
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+
+ for (int i = 0; i < (int)arrayLength; i++)
+ {
+ *pCoreClrExists = TRUE;
+
+ hr = CreateVersionStringFromModule(m_processId, stringArray[i], verStr, ARRAY_SIZE(verStr), &verLen);
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+
+ hr = CreateDebuggingInterfaceFromVersion(verStr, &pCordb);
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+
+ m_callback(pCordb, m_parameter, S_OK);
+
+ // Currently only the first coreclr module in a process is supported
+ break;
+ }
+ }
+ PAL_CPP_CATCH_ALL
+ {
+ hr = E_FAIL;
+ goto exit;
+ }
+ PAL_CPP_ENDTRY
+
+ exit:
+ if (*pCoreClrExists)
+ {
+ // Wake up all the runtimes
+ WakeRuntimes(handleArray, arrayLength);
+ }
+ CloseCLREnumeration(handleArray, stringArray, arrayLength);
+
+ return hr;
+ }
+
+ void StartupHelperThread()
+ {
+ bool coreclrExists = false;
+
+ HRESULT hr = InvokeStartupCallback(&coreclrExists);
+ // The retry logic in InternalEnumerateCLRs failed if ERROR_TIMEOUT was returned.
+ if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_TIMEOUT)))
+ {
+ if (!coreclrExists && !m_canceled)
+ {
+ // Wait until the coreclr runtime (debuggee) starts up
+ if (WaitForSingleObject(m_startupEvent, INFINITE) == WAIT_OBJECT_0)
+ {
+ if (!m_canceled)
+ {
+ hr = InvokeStartupCallback(&coreclrExists);
+ if (SUCCEEDED(hr))
+ {
+ // We should always find a coreclr module so fail if we don't
+ if (!coreclrExists)
+ {
+ hr = E_FAIL;
+ }
+ }
+ }
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+ }
+
+ if (FAILED(hr) && !m_canceled)
+ {
+ m_callback(NULL, m_parameter, hr);
+ }
+ }
+
+#endif // TARGET_UNIX
+};
+
+#ifdef TARGET_UNIX
+
+static
+void
+RuntimeStartupHandler(char *pszModulePath, HMODULE hModule, PVOID parameter)
+{
+ RuntimeStartupHelper *helper = (RuntimeStartupHelper *)parameter;
+ helper->InvokeStartupCallback(pszModulePath, hModule);
+}
+
+#else // TARGET_UNIX
+
+static
+DWORD
+StartupHelperThread(LPVOID p)
+{
+ RuntimeStartupHelper *helper = (RuntimeStartupHelper *)p;
+ helper->StartupHelperThread();
+ helper->Release();
+ return 0;
+}
+
+#endif // TARGET_UNIX
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// RegisterForRuntimeStartup -- Refer to RegisterForRuntimeStartupEx.
+// This method calls RegisterForRuntimeStartupEx with null application group ID value
+//
+// dwProcessId -- process id of the target process
+// pfnCallback -- invoked when coreclr runtime starts
+// parameter -- data to pass to callback
+// ppUnregisterToken -- pointer to put the UnregisterForRuntimeStartup token.
+//
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+RegisterForRuntimeStartup(
+ _In_ DWORD dwProcessId,
+ _In_ PSTARTUP_CALLBACK pfnCallback,
+ _In_ PVOID parameter,
+ _Out_ PVOID *ppUnregisterToken)
+{
+ return RegisterForRuntimeStartupEx(dwProcessId, NULL, pfnCallback, parameter, ppUnregisterToken);
+}
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// RegisterForRuntimeStartupEx -- executes the callback when the coreclr runtime
+// starts in the specified process. The callback is passed the proper ICorDebug
+// instance for the version of the runtime or an error if something fails. This
+// API works for launch and attach (and even the attach scenario if the runtime
+// hasn't been loaded yet) equally on both xplat and Windows. The callback is
+// always called on a separate thread. This API returns immediately.
+//
+// The callback is invoked when the coreclr runtime module is loaded during early
+// initialization. The runtime is blocked during initialization until the callback
+// returns.
+//
+// If the runtime is already loaded in the process (as in the normal attach case),
+// the callback is executed and the runtime is not blocked.
+//
+// The callback is always invoked on a separate thread and this API returns immediately.
+//
+// Only the first coreclr module instance found in the target process is currently
+// supported.
+//
+// dwProcessId -- process id of the target process
+// lpApplicationGroupId - A string representing the application group ID of a sandboxed
+// process running in Mac. Pass NULL if the process is not
+// running in a sandbox and other platforms.
+// pfnCallback -- invoked when coreclr runtime starts
+// parameter -- data to pass to callback
+// ppUnregisterToken -- pointer to put the UnregisterForRuntimeStartup token.
+//
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+RegisterForRuntimeStartupEx(
+ _In_ DWORD dwProcessId,
+ _In_ LPCWSTR lpApplicationGroupId,
+ _In_ PSTARTUP_CALLBACK pfnCallback,
+ _In_ PVOID parameter,
+ _Out_ PVOID *ppUnregisterToken)
+{
+ PUBLIC_CONTRACT;
+
+ if (pfnCallback == NULL || ppUnregisterToken == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr = S_OK;
+
+ RuntimeStartupHelper *helper = new (nothrow) RuntimeStartupHelper(dwProcessId, pfnCallback, parameter);
+ if (helper == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ hr = helper->Register(lpApplicationGroupId);
+ if (FAILED(hr))
+ {
+ helper->Release();
+ helper = NULL;
+ }
+ }
+
+ *ppUnregisterToken = helper;
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// UnregisterForRuntimeStartup -- stops/cancels runtime startup notification. Needs
+// to be called during the debugger's shutdown to cleanup the internal data.
+//
+// This API can be called in the startup callback. Otherwise, it will block until
+// the callback thread finishes and no more callbacks will be initiated after this
+// API returns.
+//
+// pUnregisterToken -- unregister token from RegisterForRuntimeStartup or NULL.
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+UnregisterForRuntimeStartup(
+ _In_ PVOID pUnregisterToken)
+{
+ PUBLIC_CONTRACT;
+
+ if (pUnregisterToken != NULL)
+ {
+ RuntimeStartupHelper *helper = (RuntimeStartupHelper *)pUnregisterToken;
+ helper->Unregister();
+ helper->Release();
+ }
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// GetStartupNotificationEvent -- creates a global, named event that is PID-
+// qualified (i.e. process global) that is used to notify the debugger of
+// any CLR instance startup in the process.
+//
+// debuggeePID -- process ID of the target process
+// phStartupEvent -- out param for the returned event handle
+//
+//-----------------------------------------------------------------------------
+#define StartupNotifyEventNamePrefix W("TelestoStartupEvent_")
+#define SessionIdPrefix W("Session\\")
+
+// NULL terminator is included in sizeof(StartupNotifyEventNamePrefix)
+const int cchEventNameBufferSize = (sizeof(StartupNotifyEventNamePrefix) + sizeof(SessionIdPrefix)) / sizeof(WCHAR)
+ + 8 // + hex process id DWORD
+ + 10 // + decimal session id DWORD
+ + 1; // '\' after session id
+
+DLLEXPORT
+HRESULT
+GetStartupNotificationEvent(
+ _In_ DWORD debuggeePID,
+ _Out_ HANDLE* phStartupEvent)
+{
+ PUBLIC_CONTRACT;
+
+ if (phStartupEvent == NULL)
+ return E_INVALIDARG;
+
+#ifndef TARGET_UNIX
+ HRESULT hr;
+ DWORD currentSessionId = 0, debuggeeSessionId = 0;
+ if (!ProcessIdToSessionId(GetCurrentProcessId(), ¤tSessionId))
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ if (!ProcessIdToSessionId(debuggeePID, &debuggeeSessionId))
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ // Here we could just add "Global\" to the event name and this would solve cross-session debugging scenario, but that would require event name change
+ // in CoreCLR, and break backward compatibility. Instead if we see that debugee is in a different session, we explicitly create startup event
+ // in that session (by adding "Session\#\"). We could do it even for our own session, but that's vaguely documented behavior and we'd
+ // like to use it as little as possible.
+ WCHAR szEventName[cchEventNameBufferSize];
+ if (currentSessionId == debuggeeSessionId)
+ {
+ swprintf_s(szEventName, cchEventNameBufferSize, StartupNotifyEventNamePrefix W("%08x"), debuggeePID);
+ }
+ else
+ {
+ swprintf_s(szEventName, cchEventNameBufferSize, SessionIdPrefix W("%u\\") StartupNotifyEventNamePrefix W("%08x"), debuggeeSessionId, debuggeePID);
+ }
+
+ // Determine an appropriate ACL and SECURITY_ATTRIBUTES to apply to this event. We use the same logic
+ // here as the debugger uses for other events (like the setup-sync-event). Specifically, this does
+ // the work to ensure a debuggee running as another user, or with a low integrity level can signal
+ // this event.
+ PACL pACL = NULL;
+ SECURITY_ATTRIBUTES * pSA = NULL;
+ IfFailRet(SecurityUtil::GetACLOfPid(debuggeePID, &pACL));
+ SecurityUtil secUtil(pACL);
+
+ HandleHolder hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, debuggeePID);
+ if (hProcess == NULL)
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ IfFailRet(secUtil.Init(hProcess));
+ IfFailRet(secUtil.GetSA(&pSA));
+
+ HANDLE startupEvent = WszCreateEvent(pSA,
+ FALSE, // false -> auto-reset
+ FALSE, // false -> initially non-signaled
+ szEventName);
+ DWORD dwStatus = GetLastError();
+ if (NULL == startupEvent)
+ {
+ // if the event already exists, try to open it, otherwise we fail.
+
+ if (ERROR_ALREADY_EXISTS != dwStatus)
+ return E_FAIL;
+
+ startupEvent = WszOpenEvent(SYNCHRONIZE, FALSE, szEventName);
+
+ if (NULL == startupEvent)
+ return E_FAIL;
+ }
+
+ *phStartupEvent = startupEvent;
+ return S_OK;
+#else
+ *phStartupEvent = NULL;
+ return E_NOTIMPL;
+#endif // TARGET_UNIX
+}
+// Refer to clr\src\mscoree\mscorwks_ntdef.src.
+const WORD kOrdinalForMetrics = 2;
+
+//-----------------------------------------------------------------------------
+// The CLR_ENGINE_METRICS is a static struct in coreclr.dll. It's exported by coreclr.dll at ordinal 2 in
+// the export address table. This function returns the CLR_ENGINE_METRICS and the RVA to the continue
+// startup event for a coreclr.dll specified by its full path.
+//
+// Arguments:
+// szTelestoFullPath - (in) full path of telesto
+// pEngineMetricsOut - (out) filled in based on metrics from target telesto.
+// pdwRVAContinueStartupEvent - (out; optional) return the RVA to the continue startup event
+//
+// Returns:
+// Throwss on error.
+//
+// Notes:
+// When VS pops up the attach dialog box, it is actually enumerating all the processes on the machine
+// (if the appropiate checkbox is checked) and checking each process to see if a DLL named "coreclr.dll"
+// is loaded. If there is one, we will go down this code path, but there is no guarantee that the
+// coreclr.dll is ours. A malicious user can be running a process with a bogus coreclr.dll loaded.
+// That's why we need to be extra careful reading coreclr.dll in this function.
+//-----------------------------------------------------------------------------
+static
+void
+GetTargetCLRMetrics(
+ LPCWSTR szTelestoFullPath,
+ CLR_ENGINE_METRICS *pEngineMetricsOut,
+ DWORD *pdwRVAContinueStartupEvent = NULL)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ CONSISTENCY_CHECK(szTelestoFullPath != NULL);
+ CONSISTENCY_CHECK(pEngineMetricsOut != NULL);
+
+#ifndef TARGET_UNIX
+ HRESULT hr = S_OK;
+
+ HandleHolder hCoreClrFile = WszCreateFile(szTelestoFullPath,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL, // default security descriptor
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (hCoreClrFile == INVALID_HANDLE_VALUE)
+ {
+ ThrowLastError();
+ }
+
+ DWORD cbFileHigh = 0;
+ DWORD cbFileLow = GetFileSize(hCoreClrFile, &cbFileHigh);
+ if (cbFileLow == INVALID_FILE_SIZE)
+ {
+ ThrowLastError();
+ }
+
+ // A maximum size of 100 MB should be more than enough for coreclr.dll.
+ if ((cbFileHigh != 0) || (cbFileLow > 0x6400000) || (cbFileLow == 0))
+ {
+ ThrowHR(E_FAIL);
+ }
+
+ HandleHolder hCoreClrMap = WszCreateFileMapping(hCoreClrFile, NULL, PAGE_READONLY, cbFileHigh, cbFileLow, NULL);
+ if (hCoreClrMap == NULL)
+ {
+ ThrowLastError();
+ }
+
+ MapViewHolder hCoreClrMapView = MapViewOfFile(hCoreClrMap, FILE_MAP_READ, 0, 0, 0);
+ if (hCoreClrMapView == NULL)
+ {
+ ThrowLastError();
+ }
+
+ // At this point we have read the file into the process, but be careful because it is flat, i.e. not mapped.
+ // We need to translate RVAs into file offsets, but fortunately PEDecoder can do all of that for us.
+ PEDecoder pedecoder(hCoreClrMapView, (COUNT_T)cbFileLow);
+
+ // Check the NT headers.
+ if (!pedecoder.CheckNTFormat())
+ {
+ ThrowHR(E_FAIL);
+ }
+
+ // At this point we can safely read anything in the NT headers.
+
+ if (!pedecoder.HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_EXPORT) ||
+ !pedecoder.CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_EXPORT))
+ {
+ ThrowHR(E_FAIL);
+ }
+ IMAGE_DATA_DIRECTORY * pExportDirectoryEntry = pedecoder.GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_EXPORT);
+
+ // At this point we can safely read the IMAGE_DATA_DIRECTORY of the export directory.
+
+ if (!pedecoder.CheckDirectory(pExportDirectoryEntry))
+ {
+ ThrowHR(E_FAIL);
+ }
+ IMAGE_EXPORT_DIRECTORY * pExportDir =
+ reinterpret_cast<IMAGE_EXPORT_DIRECTORY *>(pedecoder.GetDirectoryData(pExportDirectoryEntry));
+
+ // At this point we have checked that everything in the export directory is readable.
+
+ // Check to make sure the ordinal we have fits in the table in the export directory.
+ // The "base" here is like the starting index of the arrays in the export directory.
+ if ((pExportDir->Base > kOrdinalForMetrics) ||
+ (pExportDir->NumberOfFunctions < (kOrdinalForMetrics - pExportDir->Base)))
+ {
+ ThrowHR(E_FAIL);
+ }
+ DWORD dwRealIndex = kOrdinalForMetrics - pExportDir->Base;
+
+ // Check that we can read the RVA at the element (specified by the ordinal) in the export address table.
+ // Then read the RVA to the CLR_ENGINE_METRICS.
+ if (!pedecoder.CheckRva(pExportDir->AddressOfFunctions, (dwRealIndex + 1) * sizeof(DWORD)))
+ {
+ ThrowHR(E_FAIL);
+ }
+ DWORD rvaMetrics = *reinterpret_cast<DWORD *>(
+ pedecoder.GetRvaData(pExportDir->AddressOfFunctions + dwRealIndex * sizeof(DWORD)));
+
+ // Make sure we can safely read the CLR_ENGINE_METRICS at the RVA we have retrieved.
+ if (!pedecoder.CheckRva(rvaMetrics, sizeof(*pEngineMetricsOut)))
+ {
+ ThrowHR(E_FAIL);
+ }
+
+ // Finally, copy the CLR_ENGINE_METRICS into the output buffer.
+ CLR_ENGINE_METRICS * pMetricsInFile = reinterpret_cast<CLR_ENGINE_METRICS *>(pedecoder.GetRvaData(rvaMetrics));
+ *pEngineMetricsOut = *pMetricsInFile;
+
+ // At this point, we have retrieved the CLR_ENGINE_METRICS from the target process and
+ // stored it in output buffer.
+ if (pEngineMetricsOut->cbSize != sizeof(*pEngineMetricsOut))
+ {
+ ThrowHR(E_INVALIDARG);
+ }
+
+ if (pdwRVAContinueStartupEvent != NULL)
+ {
+ // Note that the pointer stored in the CLR_ENGINE_METRICS is assuming that the DLL is loaded at its
+ // preferred base address. We need to translate that to an RVA.
+ if (((SIZE_T)pEngineMetricsOut->phContinueStartupEvent < (SIZE_T)pedecoder.GetPreferredBase()) ||
+ ((SIZE_T)pEngineMetricsOut->phContinueStartupEvent >
+ ((SIZE_T)pedecoder.GetPreferredBase() + pedecoder.GetVirtualSize())))
+ {
+ ThrowHR(E_FAIL);
+ }
+
+ DWORD rvaContinueStartupEvent =
+ (DWORD)((SIZE_T)pEngineMetricsOut->phContinueStartupEvent - (SIZE_T)pedecoder.GetPreferredBase());
+
+ // We can't use CheckRva() here because for unmapped files it actually checks the RVA against the file
+ // size as well. We have already checked the RVA above. Now just check that the entire HANDLE
+ // falls in the loaded image.
+ if ((rvaContinueStartupEvent + sizeof(HANDLE)) > pedecoder.GetVirtualSize())
+ {
+ ThrowHR(E_FAIL);
+ }
+
+ *pdwRVAContinueStartupEvent = rvaContinueStartupEvent;
+ }
+
+ // Holder will call FreeLibrary()
+#else
+ //TODO: So far on POSIX systems we only support one version of debugging interface
+ // in future we might want to detect it the same way we do it on Windows.
+ pEngineMetricsOut->cbSize = sizeof(*pEngineMetricsOut);
+ pEngineMetricsOut->dwDbiVersion = CorDebugLatestVersion;
+ pEngineMetricsOut->phContinueStartupEvent = NULL;
+
+ if (pdwRVAContinueStartupEvent != NULL)
+ {
+ *pdwRVAContinueStartupEvent = NULL;
+ }
+#endif // TARGET_UNIX
+}
+
+// Returns true iff the module represents CoreClr.
+static
+bool
+IsCoreClr(
+ const WCHAR* pModulePath)
+{
+ _ASSERTE(pModulePath != NULL);
+
+ //strip off everything up to and including the last slash in the path to get name
+ const WCHAR* pModuleName = pModulePath;
+ while(wcschr(pModuleName, DIRECTORY_SEPARATOR_CHAR_W) != NULL)
+ {
+ pModuleName = wcschr(pModuleName, DIRECTORY_SEPARATOR_CHAR_W);
+ pModuleName++; // pass the slash
+ }
+
+ // MAIN_CLR_MODULE_NAME_W gets changed for desktop builds, so we directly code against the CoreClr name.
+ return _wcsicmp(pModuleName, MAKEDLLNAME_W(W("coreclr"))) == 0;
+}
+
+// Returns true iff the module sent is named CoreClr.dll and has the metrics expected in it's PE header.
+static
+bool
+IsCoreClrWithGoodHeader(
+ HANDLE hProcess,
+ HMODULE hModule)
+{
+ HRESULT hr = S_OK;
+
+ WCHAR modulePath[MAX_LONGPATH];
+ modulePath[0] = W('\0');
+ if(0 == GetModuleFileNameEx(hProcess, hModule, modulePath, MAX_LONGPATH))
+ {
+ return false;
+ }
+ else
+ {
+ modulePath[MAX_LONGPATH-1] = 0; // on older OS'es this doesn't get null terminated automatically on truncation
+ }
+
+ if (IsCoreClr(modulePath))
+ {
+ // We don't care about the particular error returned, only that
+ // what we tried wasn't a 'real' coreclr.dll.
+ EX_TRY
+ {
+ CLR_ENGINE_METRICS metricsStruct;
+ GetTargetCLRMetrics(modulePath, &metricsStruct); // throws
+
+ // If we got this far, then we think it's a good one.
+ }
+ EX_CATCH_HRESULT(hr);
+ return (hr == S_OK);
+ }
+
+ return false;
+}
+
+static
+HRESULT
+EnumProcessModulesInternal(
+ HANDLE hProcess,
+ DWORD *pCountModules,
+ HMODULE** ppModules)
+{
+ *pCountModules = 0;
+ *ppModules = nullptr;
+
+ // Start with 1024 modules
+ DWORD cbNeeded = sizeof(HMODULE) * 1024;
+
+ ArrayHolder<HMODULE> modules = new (nothrow) HMODULE[cbNeeded / sizeof(HMODULE)];
+ if (modules == nullptr)
+ {
+ return HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
+ }
+
+ if(!EnumProcessModules(hProcess, modules, cbNeeded, &cbNeeded))
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ // If 1024 isn't enough, try the modules array size returned (cbNeeded)
+ if (cbNeeded > (sizeof(HMODULE) * 1024))
+ {
+ modules = new (nothrow) HMODULE[cbNeeded / sizeof(HMODULE)];
+ if (modules == nullptr)
+ {
+ return HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
+ }
+
+ DWORD cbNeeded2;
+ if(!EnumProcessModules(hProcess, modules, cbNeeded, &cbNeeded2))
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ // The only way cbNeeded2 could change on the second call is if number of
+ // modules loaded by the process changed in the small window between the
+ // above EnumProcessModules calls. If this actually happens, then give
+ // up on trying to get the whole module list and risk missing the coreclr
+ // module.
+ cbNeeded = min(cbNeeded, cbNeeded2);
+ }
+
+ *pCountModules = cbNeeded / sizeof(HMODULE);
+ *ppModules = modules.Detach();
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// EnumerateCLRs -- returns an array of full paths to each coreclr.dll in the
+// target process. Also returns a corresponding array of continue events
+// that *MUST* be signaled by the caller in order to allow the CLRs in the
+// target process to proceed.
+//
+// debuggeePID -- process ID of the target process
+// ppHandleArrayOut -- out parameter in which an array of handles is returned.
+// the length of this array is returned by the pdwArrayLengthOut out param
+// ppStringArrayOut -- out parameter in which an array of full paths to each
+// coreclr.dll in the process is returned. The length of this array is the
+// same as the handle array and is returned by the pdwArrayLengthOut param
+// pdwArrayLengthOut -- out param in which the length of the two returned arrays
+// are returned.
+//
+// Notes:
+// Callers use code:CloseCLREnumeration to free the returned arrays.
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+EnumerateCLRs(
+ DWORD debuggeePID,
+ _Out_ HANDLE** ppHandleArrayOut,
+ _Out_ LPWSTR** ppStringArrayOut,
+ _Out_ DWORD* pdwArrayLengthOut)
+{
+ PUBLIC_CONTRACT;
+
+ // All out params must be non-NULL.
+ if ((ppHandleArrayOut == NULL) || (ppStringArrayOut == NULL) || (pdwArrayLengthOut == NULL))
+ return E_INVALIDARG;
+
+ HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, debuggeePID);
+ if (NULL == hProcess)
+ return E_FAIL;
+
+ // The modules in the array returned don't need to be closed
+ DWORD countModules;
+ ArrayHolder<HMODULE> modules = nullptr;
+ HRESULT hr = EnumProcessModulesInternal(hProcess, &countModules, &modules);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ //
+ // count the number of coreclr.dll entries
+ //
+ DWORD count = 0;
+ for(DWORD i = 0; i < countModules; i++)
+ {
+ if (IsCoreClrWithGoodHeader(hProcess, modules[i]))
+ {
+ count++;
+ }
+ }
+
+ // If we didn't find anything, no point in continuing.
+ if (count == 0)
+ {
+ *ppHandleArrayOut = NULL;
+ *ppStringArrayOut = NULL;
+ *pdwArrayLengthOut = 0;
+
+ return S_OK;
+ }
+
+ size_t cbEventArrayData = sizeof(HANDLE) * count; // event array data
+ size_t cbStringArrayData = sizeof(LPWSTR) * count; // string array data
+ size_t cbStringData = sizeof(WCHAR) * count * MAX_LONGPATH; // strings data
+ size_t cbBuffer = cbEventArrayData + cbStringArrayData + cbStringData;
+
+ BYTE* pOutBuffer = new (nothrow) BYTE[cbBuffer];
+ if (NULL == pOutBuffer)
+ return E_OUTOFMEMORY;
+
+ ZeroMemory(pOutBuffer, cbBuffer);
+
+ HANDLE* pEventArray = (HANDLE*) &pOutBuffer[0];
+ LPWSTR* pStringArray = (LPWSTR*) &pOutBuffer[cbEventArrayData];
+ WCHAR* pStringData = (WCHAR*) &pOutBuffer[cbEventArrayData + cbStringArrayData];
+ DWORD idx = 0;
+
+ // There's no guarantee that another coreclr hasn't loaded already anyhow,
+ // so if we get the corner case that the second time through we enumerate
+ // more coreclrs, just ignore the extras.
+ // This mismatch could happen when
+ // a) take module shapshot
+ // b) underlying file is opened for exclusive access/deleted/moved/ACL'd etc so we can't open it
+ // c) count is determined
+ // d) file is closed/copied/moved/ACL'd etc so we can find/open it again
+ // e) this loop runs
+ // Thus the loop checks idx < count
+
+ for(DWORD i = 0; i < countModules && idx < count; i++)
+ {
+ if (IsCoreClrWithGoodHeader(hProcess, modules[i]))
+ {
+ // fill in path
+ pStringArray[idx] = &pStringData[idx * MAX_LONGPATH];
+ GetModuleFileNameEx(hProcess, modules[i], pStringArray[idx], MAX_LONGPATH);
+
+#ifndef TARGET_UNIX
+ // fill in event handle -- if GetContinueStartupEvent fails, it will still return
+ // INVALID_HANDLE_VALUE in hContinueStartupEvent, which is what we want. we don't
+ // want to bail out of the enumeration altogether if we can't get an event from
+ // one telesto.
+
+ HANDLE hContinueStartupEvent = INVALID_HANDLE_VALUE;
+ HRESULT hr = GetContinueStartupEvent(debuggeePID, pStringArray[idx], &hContinueStartupEvent);
+ pEventArray[idx] = hContinueStartupEvent;
+#else
+ pEventArray[idx] = NULL;
+#endif // TARGET_UNIX
+
+ idx++;
+ }
+ }
+
+ // Patch things up so CloseCLREnumeration() can still have it's
+ // pointer arithmatic checks succeed, and the user doesn't see a 'dead' entry.
+ // Specifically, it's expected that pEventArray and pStringArray point to the
+ // same contiguous chunk of memory so that pStringArray == pEventArray[*pdwArrayLengthOut].
+ // This is expected to be a very rare case.
+ if (idx < count)
+ {
+ // Move the string pointers back.
+ LPWSTR* pSATemp = (LPWSTR*)&pOutBuffer[sizeof(HANDLE)*idx];
+ for (DWORD i = 0; i < idx; i++)
+ {
+ pSATemp[i] = pStringArray[i];
+ }
+
+ // Fix up string array pointer.
+ pStringArray = (LPWSTR*)&pOutBuffer[sizeof(HANDLE)*idx];
+
+ // Strings themselves don't need moved.
+ }
+
+ *ppHandleArrayOut = pEventArray;
+ *ppStringArrayOut = pStringArray;
+ *pdwArrayLengthOut = idx;
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// CloseCLREnumeration -- used to free resources allocated by EnumerateCLRs
+//
+// pHandleArray -- handle array originally returned by EnumerateCLRs
+// pStringArray -- string array originally returned by EnumerateCLRs
+// dwArrayLength -- array length originally returned by EnumerateCLRs
+//
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CloseCLREnumeration(
+ _In_ HANDLE* pHandleArray,
+ _In_ LPWSTR* pStringArray,
+ _In_ DWORD dwArrayLength)
+{
+ PUBLIC_CONTRACT;
+
+ // It's possible that EnumerateCLRs found nothing to enumerate, in which case
+ // pointers and count are zeroed. If a debugger calls this function in that
+ // case, let's not try to delete [] on NULL.
+ if (pHandleArray == NULL)
+ return S_OK;
+
+ if ((pHandleArray + dwArrayLength) != (HANDLE*)pStringArray)
+ return E_INVALIDARG;
+
+#ifndef TARGET_UNIX
+ for (DWORD i = 0; i < dwArrayLength; i++)
+ {
+ HANDLE hTemp = pHandleArray[i];
+ if ( (NULL != hTemp)
+ && (INVALID_HANDLE_VALUE != hTemp))
+ {
+ CloseHandle(hTemp);
+ }
+ }
+#endif // TARGET_UNIX
+
+ delete[] pHandleArray;
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Get the base address of a module from the remote process.
+//
+// Returns:
+// - On success, base address (in remote process) of mscoree,
+// - NULL if the module is not loaded.
+// - else Throws. *ppBaseAddress = NULL
+//-----------------------------------------------------------------------------
+static
+BYTE*
+GetRemoteModuleBaseAddress(
+ DWORD dwPID,
+ LPCWSTR szFullModulePath)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
+ if (NULL == hProcess)
+ {
+ ThrowHR(E_FAIL);
+ }
+
+ // The modules in the array returned don't need to be closed
+ DWORD countModules;
+ ArrayHolder<HMODULE> modules = nullptr;
+ HRESULT hr = EnumProcessModulesInternal(hProcess, &countModules, &modules);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+
+ for(DWORD i = 0; i < countModules; i++)
+ {
+ WCHAR modulePath[MAX_LONGPATH];
+ if(0 == GetModuleFileNameEx(hProcess, modules[i], modulePath, MAX_LONGPATH))
+ {
+ continue;
+ }
+ else
+ {
+ modulePath[MAX_LONGPATH-1] = 0; // on older OS'es this doesn't get null terminated automatically
+ if (_wcsicmp(modulePath, szFullModulePath) == 0)
+ {
+ return (BYTE*) modules[i];
+ }
+ }
+ }
+
+ // Successfully enumerated modules but couldn't find the requested one.
+ return NULL;
+}
+
+// DBI version: max 8 hex chars
+// SEMICOLON: 1
+// PID: max 8 hex chars
+// SEMICOLON: 1
+// HMODULE: max 16 hex chars (64-bit)
+// SEMICOLON: 1
+// PROTOCOL STRING: (variable length)
+const int c_iMaxVersionStringLen = 8 + 1 + 8 + 1 + 16; // 64-bit hmodule
+const int c_iMinVersionStringLen = 8 + 1 + 8 + 1 + 8; // 32-bit hmodule
+const int c_idxFirstSemi = 8;
+const int c_idxSecondSemi = 17;
+const WCHAR *c_versionStrFormat = W("%08x;%08x;%p");
+
+//-----------------------------------------------------------------------------
+// Public API.
+// Given a path to a coreclr.dll, get the Version string.
+//
+// Arguments:
+// pidDebuggee - OS process ID of debuggee.
+// szModuleName - a full or relative path to a valid coreclr.dll in the debuggee.
+// pBuffer - the buffer to fill the version string into
+// if pdwLength != NULL, we set *pdwLength to the length of the version string on
+// output (including the null terminator).
+// cchBuffer - length of pBuffer on input in characters
+//
+// Returns:
+// S_OK - on success.
+// E_INVALIDARG -
+// HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) if the buffer is too small.
+// COR_E_FILENOTFOUND - module is not found in a given debugee process
+//
+// Notes:
+// The null-terminated version string including null, is
+// copied to pVersion on output. Thus *pdwLength == wcslen(pBuffer)+1.
+// The version string is an opaque string that can only be passed back to other
+// DbgShim APIs.
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CreateVersionStringFromModule(
+ _In_ DWORD pidDebuggee,
+ _In_ LPCWSTR szModuleName,
+ _Out_writes_to_opt_(cchBuffer, *pdwLength) LPWSTR pBuffer,
+ _In_ DWORD cchBuffer,
+ _Out_ DWORD* pdwLength)
+{
+ PUBLIC_CONTRACT;
+
+ if (szModuleName == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ // it is ok for both to be null (to query the required buffer size) or both to be non-null.
+ if ((pBuffer == NULL) != (cchBuffer == 0))
+ {
+ return E_INVALIDARG;
+ }
+
+ SIZE_T nLengthWithNull = c_iMaxVersionStringLen + 1;
+ _ASSERTE(nLengthWithNull > 0);
+
+ if (pdwLength != NULL)
+ {
+ *pdwLength = (DWORD) nLengthWithNull;
+ }
+
+ if (nLengthWithNull > cchBuffer)
+ {
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+ else if (pBuffer != NULL)
+ {
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ CorDebugInterfaceVersion dbiVersion = CorDebugInvalidVersion;
+ BYTE* hmodTargetCLR = NULL;
+ CLR_ENGINE_METRICS metricsStruct;
+
+ GetTargetCLRMetrics(szModuleName, &metricsStruct); // throws
+ dbiVersion = (CorDebugInterfaceVersion) metricsStruct.dwDbiVersion;
+
+ hmodTargetCLR = GetRemoteModuleBaseAddress(pidDebuggee, szModuleName); // throws
+ if (hmodTargetCLR == NULL)
+ {
+ hr = COR_E_FILENOTFOUND;
+ }
+ else
+ {
+ swprintf_s(pBuffer, cchBuffer, c_versionStrFormat, dbiVersion, pidDebuggee, hmodTargetCLR);
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+ }
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Parse a version string into useful data.
+//
+// Arguments:
+// szDebuggeeVersion - (in) null terminated version string
+// piDebuggerVersion - (out) interface number that the debugger expects to use.
+// pdwPidDebuggee - (out) OS process ID of debuggee
+// phmodTargetCLR - (out) module handle of CoreClr within the debuggee.
+//
+// Returns:
+// S_OK on success. Else failures.
+//
+// Notes:
+// The version string is coming from the target CoreClr and in the case of a corrupted target, could be
+// an arbitrary string. It should be treated as untrusted public input.
+//-----------------------------------------------------------------------------
+static
+HRESULT
+ParseVersionString(
+ LPCWSTR szDebuggeeVersion,
+ CorDebugInterfaceVersion *piDebuggerVersion,
+ DWORD *pdwPidDebuggee,
+ HMODULE *phmodTargetCLR)
+{
+ if ((piDebuggerVersion == NULL) ||
+ (pdwPidDebuggee == NULL) ||
+ (phmodTargetCLR == NULL) ||
+ (wcslen(szDebuggeeVersion) < c_iMinVersionStringLen) ||
+ (W(';') != szDebuggeeVersion[c_idxFirstSemi]) ||
+ (W(';') != szDebuggeeVersion[c_idxSecondSemi]))
+ {
+ return E_INVALIDARG;
+ }
+
+ int numFieldsAssigned = swscanf_s(szDebuggeeVersion, c_versionStrFormat, piDebuggerVersion, pdwPidDebuggee, phmodTargetCLR);
+ if (numFieldsAssigned != 3)
+ {
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Appends "\mscordbi.dll" to the path. This converts a directory name into the full path to mscordbi.dll.
+//
+// Arguments:
+// szFullDbiPath - (in/out): on input, the directory containing dbi. On output, the full path to dbi.dll.
+//-----------------------------------------------------------------------------
+static
+void
+AppendDbiDllName(SString & szFullDbiPath)
+{
+ const WCHAR * pDbiDllName = DIRECTORY_SEPARATOR_STR_W MAKEDLLNAME_W(W("mscordbi"));
+ szFullDbiPath.Append(pDbiDllName);
+}
+
+//-----------------------------------------------------------------------------
+// Return a path to the dbi next to the runtime, if present.
+//
+// Arguments:
+// pidDebuggee - OS process ID of debuggee
+// hmodTargetCLR - handle to CoreClr within debuggee process
+// szFullDbiPath - (out) the full path of Mscordbi.dll next to the debuggee's CoreClr.dll.
+//
+// Notes:
+// This just calculates a filename and does not determine if the file actually exists.
+//-----------------------------------------------------------------------------
+static
+void
+GetDbiFilenameNextToRuntime(
+ DWORD pidDebuggee,
+ HMODULE hmodTargetCLR,
+ SString & szFullDbiPath,
+ SString & szFullCoreClrPath)
+{
+ szFullDbiPath.Clear();
+
+ //
+ // Step 1: (pid, hmodule) --> full path
+ //
+ HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pidDebuggee);
+ WCHAR modulePath[MAX_LONGPATH];
+ if(0 == GetModuleFileNameEx(hProcess, hmodTargetCLR, modulePath, MAX_LONGPATH))
+ {
+ ThrowHR(E_FAIL);
+ }
+
+ //
+ // Step 2: 'Coreclr.dll' --> 'mscordbi.dll'
+ //
+ WCHAR * pCoreClrPath = modulePath;
+ WCHAR * pLast = wcsrchr(pCoreClrPath, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pLast == NULL)
+ {
+ ThrowHR(E_FAIL);
+ }
+
+ // c:\abc\coreclr.dll
+ // 01234567890
+ // c:\abc\mscordbi.dll
+
+ // Copy everything up to but not including the last '\', thus excluding '\coreclr.dll'
+ // Then append '\mscordbi.dll' to get a full path to dbi.
+ COUNT_T len = (COUNT_T) (pLast - pCoreClrPath); // length not including final '\'
+ szFullDbiPath.Set(pCoreClrPath, len);
+
+ AppendDbiDllName(szFullDbiPath);
+
+ szFullCoreClrPath.Set(pCoreClrPath, (COUNT_T)wcslen(pCoreClrPath));
+}
+
+
+//---------------------------------------------------------------------------------------
+//
+// The current policy is that the DBI DLL must live right next to the coreclr DLL. We check the product
+// version number of both of them to make sure they match.
+//
+// Arguments:
+// szFullDbiPath - full path to mscordbi.dll
+// szFullCoreClrPath - full path to coreclr.dll
+//
+// Return Value:
+// true if the versions match
+//
+static
+bool
+CheckDbiAndRuntimeVersion(
+ SString & szFullDbiPath,
+ SString & szFullCoreClrPath)
+{
+#ifndef TARGET_UNIX
+ DWORD dwDbiVersionMS = 0;
+ DWORD dwDbiVersionLS = 0;
+ DWORD dwCoreClrVersionMS = 0;
+ DWORD dwCoreClrVersionLS = 0;
+
+ // The version numbers follow the convention used by VS_FIXEDFILEINFO.
+ GetProductVersionNumber(szFullDbiPath, &dwDbiVersionMS, &dwDbiVersionLS);
+ GetProductVersionNumber(szFullCoreClrPath, &dwCoreClrVersionMS, &dwCoreClrVersionLS);
+
+ if ((dwDbiVersionMS == dwCoreClrVersionMS) &&
+ (dwDbiVersionLS == dwCoreClrVersionLS))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+#else
+ return true;
+#endif // TARGET_UNIX
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+// Given a version string, create the matching mscordbi.dll for it.
+// Create a managed debugging interface for the specified version.
+//
+// Parameters:
+// iDebuggerVersion - the version of interface the debugger (eg, Cordbg) expects.
+// szDebuggeeVersion - the version of the debuggee. This will map to a version of mscordbi.dll
+// ppCordb - the outparameter used to return the debugging interface object.
+//
+// Return:
+// S_OK on success. *ppCordb will be non-null.
+// CORDBG_E_INCOMPATIBLE_PROTOCOL - if the proper DBI is not available. This can be a very common error if
+// the right debug pack is not installed.
+// else Error. (*ppCordb will be null)
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CreateDebuggingInterfaceFromVersionEx(
+ _In_ int iDebuggerVersion,
+ _In_ LPCWSTR szDebuggeeVersion,
+ _Out_ IUnknown ** ppCordb)
+{
+ return CreateDebuggingInterfaceFromVersion2(iDebuggerVersion, szDebuggeeVersion, NULL, ppCordb);
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+// Given a version string, create the matching mscordbi.dll for it.
+// Create a managed debugging interface for the specified version.
+//
+// Parameters:
+// iDebuggerVersion - the version of interface the debugger (eg, Cordbg) expects.
+// szDebuggeeVersion - the version of the debuggee. This will map to a version of mscordbi.dll
+// lpApplicationGroupId - A string representing the application group ID of a sandboxed
+// process running in Mac. Pass NULL if the process is not
+// running in a sandbox and other platforms.
+// ppCordb - the outparameter used to return the debugging interface object.
+//
+// Return:
+// S_OK on success. *ppCordb will be non-null.
+// CORDBG_E_INCOMPATIBLE_PROTOCOL - if the proper DBI is not available. This can be a very common error if
+// the right debug pack is not installed.
+// else Error. (*ppCordb will be null)
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CreateDebuggingInterfaceFromVersion2(
+ _In_ int iDebuggerVersion,
+ _In_ LPCWSTR szDebuggeeVersion,
+ _In_ LPCWSTR szApplicationGroupId,
+ _Out_ IUnknown ** ppCordb)
+{
+ PUBLIC_CONTRACT;
+
+ HRESULT hrIgnore = S_OK; // ignored HResult
+ HRESULT hr = S_OK;
+ HMODULE hMod = NULL;
+ IUnknown * pCordb = NULL;
+
+ LOG((LF_CORDB, LL_EVERYTHING, "Calling CreateDebuggerInterfaceFromVersion, ver=%S\n", szDebuggeeVersion));
+
+ if ((szDebuggeeVersion == NULL) || (ppCordb == NULL))
+ {
+ hr = E_INVALIDARG;
+ goto Exit;
+ }
+
+ //
+ // Step 1: Parse version information into internal data structures
+ //
+
+ CorDebugInterfaceVersion iTargetVersion; // the CorDebugInterfaceVersion (CorDebugVersion_2_0)
+ DWORD pidDebuggee; // OS process ID of the debuggee
+ HMODULE hmodTargetCLR; // module of Telesto in target (the clrInstanceId)
+
+ hr = ParseVersionString(szDebuggeeVersion, &iTargetVersion, &pidDebuggee, &hmodTargetCLR);
+ if (FAILED(hr))
+ goto Exit;
+
+ //
+ // Step 2: Find the proper dbi module (mscordbi) and load it.
+ //
+
+ // Check for dbi next to target CLR.
+ // This will be very common for internal developer setups, but not common in end-user setups.
+ EX_TRY
+ {
+ SString szFullDbiPath;
+ SString szFullCoreClrPath;
+
+ GetDbiFilenameNextToRuntime(pidDebuggee, hmodTargetCLR, szFullDbiPath, szFullCoreClrPath);
+
+ if (!CheckDbiAndRuntimeVersion(szFullDbiPath, szFullCoreClrPath))
+ {
+ hr = CORDBG_E_INCOMPATIBLE_PROTOCOL;
+ goto Exit;
+ }
+
+ // We calculated where dbi would be, but haven't yet verified if it's there.
+ // Try to load it. We're using this to check for file existence.
+
+ // Issue:951525: coreclr mscordbi load fails on downlevel OS since LoadLibraryEx can't find
+ // dependent forwarder DLLs. Force LoadLibrary to look for dependencies in szFullDbiPath plus the default
+ // search paths.
+#ifndef TARGET_UNIX
+ hMod = WszLoadLibraryEx(szFullDbiPath, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+#else
+ hMod = LoadLibraryExW(szFullDbiPath, NULL, 0);
+#endif
+ }
+ EX_CATCH_HRESULT(hrIgnore); // failure leaves hMod null
+
+ // Couldn't find Dbi, likely because the right debug pack is not installed. Failure.
+ if (NULL == hMod)
+ {
+ // Check for the following two HRESULTs and return them specifically. These are returned by
+ // CreateToolhelp32Snapshot() and could be transient errors. The debugger may choose to retry.
+ if ((hrIgnore == HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) || (hrIgnore == HRESULT_FROM_WIN32(ERROR_BAD_LENGTH)))
+ {
+ hr = hrIgnore;
+ }
+ else
+ {
+ hr = CORDBG_E_DEBUG_COMPONENT_MISSING;
+ }
+ goto Exit;
+ }
+
+ //
+ // Step 3: Now that module is loaded, instantiate an ICorDebug.
+ //
+ hr = CreateCoreDbg(hmodTargetCLR, hMod, pidDebuggee, szApplicationGroupId, iDebuggerVersion, &pCordb);
+ _ASSERTE((pCordb == NULL) == FAILED(hr));
+
+Exit:
+ if (FAILED(hr))
+ {
+ if (pCordb != NULL)
+ {
+ pCordb->Release();
+ pCordb = NULL;
+ }
+
+ if (hMod != NULL)
+ {
+ _ASSERTE(pCordb == NULL);
+ FreeLibrary(hMod);
+ }
+ }
+
+ // Set our outparam.
+ *ppCordb = pCordb;
+
+ // On success case, mscordbi.dll is leaked.
+ // - We never give the caller back the module handle, so our caller can't do FreeLibrary().
+ // - ICorDebug can't unload itself.
+
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// Public API.
+// Superceded by CreateDebuggingInterfaceFromVersionEx in SLv4.
+// Given a version string, create the matching mscordbi.dll for it.
+// Create a managed debugging interface for the specified version.
+//
+// Parameters:
+// szDebuggeeVersion - the version of the debuggee. This will map to a version of mscordbi.dll
+// ppCordb - the outparameter used to return the debugging interface object.
+//
+// Return:
+// S_OK on success. *ppCordb will be non-null.
+// CORDBG_E_INCOMPATIBLE_PROTOCOL - if the proper DBI is not available. This can be a very common error if
+// the right debug pack is not installed.
+// else Error. (*ppCordb will be null)
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CreateDebuggingInterfaceFromVersion(
+ _In_ LPCWSTR szDebuggeeVersion,
+ _Out_ IUnknown ** ppCordb
+)
+{
+ PUBLIC_CONTRACT;
+
+ return CreateDebuggingInterfaceFromVersionEx(CorDebugVersion_2_0, szDebuggeeVersion, ppCordb);
+}
+
+#ifndef TARGET_UNIX
+
+//------------------------------------------------------------------------------
+// Manually retrieves the "continue startup" event from the correct CLR instance
+// in the target process.
+//
+// Arguments:
+// debuggeePID - (in) OS Process ID of debuggee
+// szTelestoFullPath - (in) full path to telesto within the process.
+// phContinueStartupEvent - (out)
+//
+// Returns:
+// S_OK on success.
+//------------------------------------------------------------------------------
+HRESULT
+GetContinueStartupEvent(
+ DWORD debuggeePID,
+ LPCWSTR szTelestoFullPath,
+ _Out_ HANDLE* phContinueStartupEvent)
+{
+ if ((phContinueStartupEvent == NULL) || (szTelestoFullPath == NULL))
+ return E_INVALIDARG;
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ *phContinueStartupEvent = INVALID_HANDLE_VALUE;
+
+ DWORD dwCoreClrContinueEventOffset = 0;
+ CLR_ENGINE_METRICS metricsStruct;
+
+ GetTargetCLRMetrics(szTelestoFullPath, &metricsStruct, &dwCoreClrContinueEventOffset); // throws
+
+
+ BYTE* pbBaseAddress = GetRemoteModuleBaseAddress(debuggeePID, szTelestoFullPath); // throws
+
+
+ HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, debuggeePID);
+ if (NULL == hProcess)
+ ThrowHR(E_FAIL);
+
+ HANDLE continueEvent = NULL;
+
+ SIZE_T nBytesRead;
+ if (!ReadProcessMemory(hProcess, pbBaseAddress + dwCoreClrContinueEventOffset, &continueEvent,
+ sizeof(continueEvent), &nBytesRead))
+ {
+ ThrowHR(E_FAIL);
+ }
+
+ if (NULL != continueEvent && INVALID_HANDLE_VALUE != continueEvent)
+ {
+ if (!DuplicateHandle(hProcess, continueEvent, GetCurrentProcess(), &continueEvent,
+ EVENT_MODIFY_STATE, FALSE, 0))
+ {
+ ThrowHR(E_FAIL);
+ }
+ }
+
+ *phContinueStartupEvent = continueEvent;
+ }
+ EX_CATCH_HRESULT(hr)
+ return hr;
+}
+
+#endif // !TARGET_UNIX
+
+#if defined(FEATURE_CORESYSTEM)
+#include "debugshim.h"
+#endif
+
+//-----------------------------------------------------------------------------
+// Public API.
+//
+// Parameters:
+// clsid
+// riid
+// ppInterface
+//
+// Return:
+// S_OK on success.
+//-----------------------------------------------------------------------------
+DLLEXPORT
+HRESULT
+CLRCreateInstance(
+ REFCLSID clsid,
+ REFIID riid,
+ LPVOID *ppInterface)
+{
+ PUBLIC_CONTRACT;
+
+#if defined(FEATURE_CORESYSTEM)
+
+ if (ppInterface == NULL)
+ return E_POINTER;
+
+ if (clsid != CLSID_CLRDebugging || riid != IID_ICLRDebugging)
+ return E_NOINTERFACE;
+
+#if defined(FEATURE_CORESYSTEM)
+ GUID skuId = CLR_ID_ONECORE_CLR;
+#else
+ GUID skuId = CLR_ID_CORECLR;
+#endif
+
+ CLRDebuggingImpl *pDebuggingImpl = new (nothrow) CLRDebuggingImpl(skuId);
+ if (NULL == pDebuggingImpl)
+ return E_OUTOFMEMORY;
+
+ return pDebuggingImpl->QueryInterface(riid, ppInterface);
+#else
+ return E_NOTIMPL;
+#endif
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// DbgShim.h
+//
+//*****************************************************************************
+
+#include <windows.h>
+
+typedef VOID (*PSTARTUP_CALLBACK)(IUnknown *pCordb, PVOID parameter, HRESULT hr);
+
+EXTERN_C HRESULT
+CreateProcessForLaunch(
+ _In_ LPWSTR lpCommandLine,
+ _In_ BOOL bSuspendProcess,
+ _In_ LPVOID lpEnvironment,
+ _In_ LPCWSTR lpCurrentDirectory,
+ _Out_ PDWORD pProcessId,
+ _Out_ HANDLE *pResumeHandle);
+
+EXTERN_C HRESULT
+ResumeProcess(
+ _In_ HANDLE hResumeHandle);
+
+EXTERN_C HRESULT
+CloseResumeHandle(
+ _In_ HANDLE hResumeHandle);
+
+EXTERN_C HRESULT
+RegisterForRuntimeStartup(
+ _In_ DWORD dwProcessId,
+ _In_ PSTARTUP_CALLBACK pfnCallback,
+ _In_ PVOID parameter,
+ _Out_ PVOID *ppUnregisterToken);
+
+EXTERN_C HRESULT
+RegisterForRuntimeStartupEx(
+ _In_ DWORD dwProcessId,
+ _In_ LPCWSTR szApplicationGroupId,
+ _In_ PSTARTUP_CALLBACK pfnCallback,
+ _In_ PVOID parameter,
+ _Out_ PVOID *ppUnregisterToken);
+
+EXTERN_C HRESULT
+UnregisterForRuntimeStartup(
+ _In_ PVOID pUnregisterToken);
+
+EXTERN_C HRESULT
+GetStartupNotificationEvent(
+ _In_ DWORD debuggeePID,
+ _Out_ HANDLE* phStartupEvent);
+
+EXTERN_C HRESULT
+EnumerateCLRs(DWORD debuggeePID,
+ _Out_ HANDLE** ppHandleArrayOut,
+ _Out_ LPWSTR** ppStringArrayOut,
+ _Out_ DWORD* pdwArrayLengthOut);
+
+EXTERN_C HRESULT
+CloseCLREnumeration(
+ _In_ HANDLE* pHandleArray,
+ _In_ LPWSTR* pStringArray,
+ _In_ DWORD dwArrayLength);
+
+EXTERN_C HRESULT
+CreateVersionStringFromModule(
+ _In_ DWORD pidDebuggee,
+ _In_ LPCWSTR szModuleName,
+ _Out_writes_to_opt_(cchBuffer, *pdwLength) LPWSTR pBuffer,
+ _In_ DWORD cchBuffer,
+ _Out_ DWORD* pdwLength);
+
+EXTERN_C HRESULT
+CreateDebuggingInterfaceFromVersionEx(
+ _In_ int iDebuggerVersion,
+ _In_ LPCWSTR szDebuggeeVersion,
+ _Out_ IUnknown ** ppCordb);
+
+EXTERN_C
+DLLEXPORT
+HRESULT
+CreateDebuggingInterfaceFromVersion2(
+ _In_ int iDebuggerVersion,
+ _In_ LPCWSTR szDebuggeeVersion,
+ _In_ LPCWSTR szApplicationGroupId,
+ _Out_ IUnknown ** ppCordb);
+
+EXTERN_C HRESULT
+CreateDebuggingInterfaceFromVersion(
+ _In_ LPCWSTR szDebuggeeVersion,
+ _Out_ IUnknown ** ppCordb);
--- /dev/null
+; Licensed to the .NET Foundation under one or more agreements.
+; The .NET Foundation licenses this file to you under the MIT license.
+
+EXPORTS
+ CreateProcessForLaunch
+ ResumeProcess
+ CloseResumeHandle
+ RegisterForRuntimeStartup
+ RegisterForRuntimeStartupEx
+ UnregisterForRuntimeStartup
+ GetStartupNotificationEvent
+ EnumerateCLRs
+ CloseCLREnumeration
+ CreateVersionStringFromModule
+ CreateDebuggingInterfaceFromVersion
+ CreateDebuggingInterfaceFromVersionEx
+ CreateDebuggingInterfaceFromVersion2
+ CLRCreateInstance
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#define FX_VER_FILEDESCRIPTION_STR "Microsoft .NET Runtime Multi-CLR Debugging Helper\0"
+
+#include <fxver.h>
+#include <fxver.rc>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ </PropertyGroup>
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Checked|x64">
+ <Configuration>Checked</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="RelWithDebInfo|x64">
+ <Configuration>RelWithDebInfo</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{BD779298-8631-3F5D-AA59-82897E5454A7}</ProjectGuid>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ <Keyword>Win32Proj</Keyword>
+ <Platform>x64</Platform>
+ <ProjectName>dbgshim</ProjectName>
+ <VCProjectUpgraderObjectName>NoUpgrade</VCProjectUpgraderObjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
+ </ImportGroup>
+ <ImportGroup 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>10.0.20506.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ArtifactsObjDir)Windows_NT.x64.Debug\src\dbgshim\Debug\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">dbgshim.dir\Debug\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">dbgshim</TargetName>
+ <TargetExt Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.dll</TargetExt>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">$(ArtifactsObjDir)Windows_NT.x64.Debug\src\dbgshim\Checked\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">dbgshim.dir\Checked\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">dbgshim</TargetName>
+ <TargetExt Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">.dll</TargetExt>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">false</GenerateManifest>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ArtifactsObjDir)Windows_NT.x64.Debug\src\dbgshim\Release\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">dbgshim.dir\Release\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">dbgshim</TargetName>
+ <TargetExt Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.dll</TargetExt>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">$(ArtifactsObjDir)Windows_NT.x64.Debug\src\dbgshim\RelWithDebInfo\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">dbgshim.dir\RelWithDebInfo\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">dbgshim</TargetName>
+ <TargetExt Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">.dll</TargetExt>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">false</GenerateManifest>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 /homeparams</AdditionalOptions>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091</DisableSpecificWarnings>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <OmitFramePointers>false</OmitFramePointers>
+ <Optimization>Disabled</Optimization>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <RemoveUnreferencedCodeData>true</RemoveUnreferencedCodeData>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <StructMemberAlignment>8Bytes</StructMemberAlignment>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TreatSpecificWarningsAsErrors>4007;4013;4102;4551;4700;4640;4806</TreatSpecificWarningsAsErrors>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <UseFullPaths>true</UseFullPaths>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Debug;BUILDENV_DEBUG=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR="Debug";dbgshim_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;DEBUG;_DBG;URTBLDENV_FRIENDLY=Debug;BUILDENV_DEBUG=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR=\"Debug\";dbgshim_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <MASM>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Debug;BUILDENV_DEBUG=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR="Debug";dbgshim_EXPORTS;;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <IncludePaths>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths)</IncludePaths>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont</AdditionalOptions>
+ </MASM>
+ <Midl>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+ <HeaderFileName>%(Filename).h</HeaderFileName>
+ <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+ <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+ <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>..\inc\Debug\corguids.lib;..\SOS\dbgutil\Debug\dbgutil.lib;..\utilcode\Debug\utilcodestaticnohost.lib;kernel32.lib;libcmtd.lib;libvcruntimed.lib;uuid.lib;user32.lib;advapi32.lib;ole32.lib;oleaut32.lib;WtsApi32.lib;version.lib;psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /machine:x64 /guard:cf /PDBCOMPRESS /IGNORE:4197,4013,4254,4070,4221 /SUBSYSTEM:WINDOWS,6.01 /guard:ehcont /CETCOMPAT</AdditionalOptions>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+ <ImportLibrary>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Debug/dbgshim.lib</ImportLibrary>
+ <LargeAddressAware>true</LargeAddressAware>
+ <ModuleDefinitionFile>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/dbgshim.def</ModuleDefinitionFile>
+ <ProgramDataBaseFile>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Debug/dbgshim.pdb</ProgramDataBaseFile>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ <ProjectReference>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ </ProjectReference>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8</AdditionalOptions>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091</DisableSpecificWarnings>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <OmitFramePointers>false</OmitFramePointers>
+ <Optimization>MaxSpeed</Optimization>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <RemoveUnreferencedCodeData>true</RemoveUnreferencedCodeData>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <StructMemberAlignment>8Bytes</StructMemberAlignment>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TreatSpecificWarningsAsErrors>4007;4013;4102;4551;4700;4640;4806</TreatSpecificWarningsAsErrors>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <UseFullPaths>true</UseFullPaths>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR="Checked";dbgshim_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR=\"Checked\";dbgshim_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <MASM>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR="Checked";dbgshim_EXPORTS;;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <IncludePaths>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths)</IncludePaths>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont</AdditionalOptions>
+ </MASM>
+ <Midl>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+ <HeaderFileName>%(Filename).h</HeaderFileName>
+ <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+ <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+ <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>..\inc\Checked\corguids.lib;..\SOS\dbgutil\Checked\dbgutil.lib;..\utilcode\Checked\utilcodestaticnohost.lib;kernel32.lib;libcmtd.lib;libvcruntimed.lib;uuid.lib;user32.lib;advapi32.lib;ole32.lib;oleaut32.lib;WtsApi32.lib;version.lib;psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /machine:x64 /guard:cf /PDBCOMPRESS /IGNORE:4197,4013,4254,4070,4221 /SUBSYSTEM:WINDOWS,6.01 /guard:ehcont /CETCOMPAT</AdditionalOptions>
+ <EnableCOMDATFolding>false</EnableCOMDATFolding>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+ <ImportLibrary>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Checked/dbgshim.lib</ImportLibrary>
+ <LargeAddressAware>true</LargeAddressAware>
+ <ModuleDefinitionFile>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/dbgshim.def</ModuleDefinitionFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <ProgramDataBaseFile>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Checked/dbgshim.pdb</ProgramDataBaseFile>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ <ProjectReference>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ </ProjectReference>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8</AdditionalOptions>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091</DisableSpecificWarnings>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <OmitFramePointers>false</OmitFramePointers>
+ <Optimization>Full</Optimization>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <RemoveUnreferencedCodeData>true</RemoveUnreferencedCodeData>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <StructMemberAlignment>8Bytes</StructMemberAlignment>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TreatSpecificWarningsAsErrors>4007;4013;4102;4551;4700;4640;4806</TreatSpecificWarningsAsErrors>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <UseFullPaths>true</UseFullPaths>
+ <WarningLevel>Level3</WarningLevel>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR="Release";dbgshim_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR=\"Release\";dbgshim_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <MASM>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR="Release";dbgshim_EXPORTS;;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <IncludePaths>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths)</IncludePaths>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont</AdditionalOptions>
+ </MASM>
+ <Midl>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+ <HeaderFileName>%(Filename).h</HeaderFileName>
+ <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+ <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+ <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>..\inc\Release\corguids.lib;..\SOS\dbgutil\Release\dbgutil.lib;..\utilcode\Release\utilcodestaticnohost.lib;kernel32.lib;libcmt.lib;libvcruntime.lib;uuid.lib;user32.lib;advapi32.lib;ole32.lib;oleaut32.lib;WtsApi32.lib;version.lib;psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /machine:x64 /guard:cf /PDBCOMPRESS /IGNORE:4197,4013,4254,4070,4221 /SUBSYSTEM:WINDOWS,6.01 /guard:ehcont /CETCOMPAT /DEFAULTLIB:ucrt.lib</AdditionalOptions>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <IgnoreSpecificDefaultLibraries>libucrt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+ <ImportLibrary>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Release/dbgshim.lib</ImportLibrary>
+ <LargeAddressAware>true</LargeAddressAware>
+ <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
+ <ModuleDefinitionFile>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/dbgshim.def</ModuleDefinitionFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <ProgramDataBaseFile>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Release/dbgshim.pdb</ProgramDataBaseFile>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ <ProjectReference>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ </ProjectReference>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8</AdditionalOptions>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091</DisableSpecificWarnings>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <OmitFramePointers>false</OmitFramePointers>
+ <Optimization>MaxSpeed</Optimization>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <RemoveUnreferencedCodeData>true</RemoveUnreferencedCodeData>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <StructMemberAlignment>8Bytes</StructMemberAlignment>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TreatSpecificWarningsAsErrors>4007;4013;4102;4551;4700;4640;4806</TreatSpecificWarningsAsErrors>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <UseFullPaths>true</UseFullPaths>
+ <WarningLevel>Level3</WarningLevel>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR="RelWithDebInfo";dbgshim_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR=\"RelWithDebInfo\";dbgshim_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <MASM>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;FEATURE_NO_HOST;SELF_NO_HOST;_BLD_CLR;FX_VER_INTERNALNAME_STR=dbgshim.dll;CMAKE_INTDIR="RelWithDebInfo";dbgshim_EXPORTS;;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <IncludePaths>$(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths)</IncludePaths>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont</AdditionalOptions>
+ </MASM>
+ <Midl>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+ <HeaderFileName>%(Filename).h</HeaderFileName>
+ <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+ <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+ <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>..\inc\RelWithDebInfo\corguids.lib;..\SOS\dbgutil\RelWithDebInfo\dbgutil.lib;..\utilcode\RelWithDebInfo\utilcodestaticnohost.lib;kernel32.lib;libcmt.lib;libvcruntime.lib;uuid.lib;user32.lib;advapi32.lib;ole32.lib;oleaut32.lib;WtsApi32.lib;version.lib;psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /machine:x64 /guard:cf /PDBCOMPRESS /IGNORE:4197,4013,4254,4070,4221 /SUBSYSTEM:WINDOWS,6.01 /guard:ehcont /CETCOMPAT</AdditionalOptions>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+ <ImportLibrary>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/RelWithDebInfo/dbgshim.lib</ImportLibrary>
+ <LargeAddressAware>true</LargeAddressAware>
+ <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
+ <ModuleDefinitionFile>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/dbgshim.def</ModuleDefinitionFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <ProgramDataBaseFile>$(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/RelWithDebInfo/dbgshim.pdb</ProgramDataBaseFile>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ <ProjectReference>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ </ProjectReference>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="dbgshim.cpp" />
+ <ClCompile Include="debugshim.cpp" />
+ <None Include="dbgshim.ntdef" />
+ <None Include="dbgshim_unixexports.src" />
+ <None Include="pkg\Directory.Build.props" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.linux-arm.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.linux-arm64.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.linux-musl-arm64.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.linux-musl-x64.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.linux-x64.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.osx-arm64.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.osx-x64.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.props" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.win-arm.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.win-arm64.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.win-x64.proj" />
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.win-x86.proj" />
+ <ResourceCompile Include="dbgshim.rc" />
+ <None Include="CMakeLists.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="debugshim.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="dbgshim.cpp" />
+ <ClCompile Include="debugshim.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="CMakeLists.txt" />
+ <None Include="pkg\Directory.Build.props">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.linux-arm.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.linux-arm64.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.linux-musl-arm64.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.linux-musl-x64.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.linux-x64.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.osx-arm64.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.osx-x64.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.props">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.win-arm.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.win-arm64.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.win-x64.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="pkg\Microsoft.Diagnostics.DbgShim.win-x86.proj">
+ <Filter>pkg</Filter>
+ </None>
+ <None Include="dbgshim.ntdef" />
+ <None Include="dbgshim_unixexports.src" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="dbgshim.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="pkg">
+ <UniqueIdentifier>{ac4ed0ef-d259-4eed-8152-b4c1db4df94c}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="debugshim.h" />
+ </ItemGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+; Licensed to the .NET Foundation under one or more agreements.
+; The .NET Foundation licenses this file to you under the MIT license.
+
+CreateProcessForLaunch
+ResumeProcess
+CloseResumeHandle
+RegisterForRuntimeStartup
+RegisterForRuntimeStartupEx
+UnregisterForRuntimeStartup
+GetStartupNotificationEvent
+EnumerateCLRs
+CloseCLREnumeration
+CreateVersionStringFromModule
+CreateDebuggingInterfaceFromVersion
+CreateDebuggingInterfaceFromVersionEx
+CreateDebuggingInterfaceFromVersion2
+CLRCreateInstance
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// debugshim.cpp
+//
+
+//
+//*****************************************************************************
+
+#include "debugshim.h"
+#include "dbgutil.h"
+#include <crtdbg.h>
+#include <clrinternal.h> //has the CLR_ID_V4_DESKTOP guid in it
+#include "palclr.h"
+
+#ifndef IMAGE_FILE_MACHINE_ARMNT
+#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
+#endif
+
+#ifndef IMAGE_FILE_MACHINE_ARM64
+#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian
+#endif
+
+//*****************************************************************************
+// CLRDebuggingImpl implementation (ICLRDebugging)
+//*****************************************************************************
+
+typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImpl2FnPtr)(ULONG64 clrInstanceId,
+ IUnknown * pDataTarget,
+ LPCWSTR pDacModulePath,
+ CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion,
+ REFIID riid,
+ IUnknown ** ppInstance,
+ CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags);
+
+typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImplFnPtr)(ULONG64 clrInstanceId,
+ IUnknown * pDataTarget,
+ HMODULE hDacDll,
+ CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion,
+ REFIID riid,
+ IUnknown ** ppInstance,
+ CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags);
+
+typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcess2FnPtr)(ULONG64 clrInstanceId,
+ IUnknown * pDataTarget,
+ HMODULE hDacDll,
+ REFIID riid,
+ IUnknown ** ppInstance,
+ CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags);
+
+typedef HMODULE (STDAPICALLTYPE *LoadLibraryWFnPtr)(LPCWSTR lpLibFileName);
+
+static bool IsTargetWindows(ICorDebugDataTarget* pDataTarget)
+{
+ CorDebugPlatform targetPlatform;
+
+ HRESULT result = pDataTarget->GetPlatform(&targetPlatform);
+
+ if(FAILED(result))
+ {
+ _ASSERTE(!"Unexpected error");
+ return false;
+ }
+
+ switch (targetPlatform)
+ {
+ case CORDB_PLATFORM_WINDOWS_X86:
+ case CORDB_PLATFORM_WINDOWS_AMD64:
+ case CORDB_PLATFORM_WINDOWS_IA64:
+ case CORDB_PLATFORM_WINDOWS_ARM:
+ case CORDB_PLATFORM_WINDOWS_ARM64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Implementation of ICLRDebugging::OpenVirtualProcess
+//
+// Arguments:
+// moduleBaseAddress - the address of the module which might be a CLR
+// pDataTarget - the data target for inspecting the process
+// pLibraryProvider - a callback for locating DBI and DAC
+// pMaxDebuggerSupportedVersion - the max version of the CLR that this debugger will support debugging
+// riidProcess - the IID of the interface that should be passed back in ppProcess
+// ppProcess - output for the ICorDebugProcess# if this module is a CLR
+// pVersion - the CLR version if this module is a CLR
+// pFlags - output, see the CLR_DEBUGGING_PROCESS_FLAGS for more details. Right now this has only one possible
+// value which indicates this runtime had an unhandled exception
+STDMETHODIMP CLRDebuggingImpl::OpenVirtualProcess(
+ ULONG64 moduleBaseAddress,
+ IUnknown * pDataTarget,
+ ICLRDebuggingLibraryProvider * pLibraryProvider,
+ CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion,
+ REFIID riidProcess,
+ IUnknown ** ppProcess,
+ CLR_DEBUGGING_VERSION * pVersion,
+ CLR_DEBUGGING_PROCESS_FLAGS * pFlags)
+{
+ //PRECONDITION(CheckPointer(pDataTarget));
+
+ HRESULT hr = S_OK;
+ ICorDebugDataTarget * pDt = NULL;
+ HMODULE hDbi = NULL;
+ HMODULE hDac = NULL;
+ LPWSTR pDacModulePath = NULL;
+ LPWSTR pDbiModulePath = NULL;
+ DWORD dbiTimestamp;
+ DWORD dbiSizeOfImage;
+ WCHAR dbiName[MAX_PATH_FNAME] = { 0 };
+ DWORD dacTimestamp;
+ DWORD dacSizeOfImage;
+ WCHAR dacName[MAX_PATH_FNAME] = { 0 };
+ CLR_DEBUGGING_VERSION version;
+ BOOL versionSupportedByCaller = FALSE;
+
+ // argument checking
+ if ((ppProcess != NULL || pFlags != NULL) && pLibraryProvider == NULL)
+ {
+ hr = E_POINTER; // the library provider must be specified if either
+ // ppProcess or pFlags is non-NULL
+ }
+ else if ((ppProcess != NULL || pFlags != NULL) && pMaxDebuggerSupportedVersion == NULL)
+ {
+ hr = E_POINTER; // the max supported version must be specified if either
+ // ppProcess or pFlags is non-NULL
+ }
+ else if (pVersion != NULL && pVersion->wStructVersion != 0)
+ {
+ hr = CORDBG_E_UNSUPPORTED_VERSION_STRUCT;
+ }
+ else if (FAILED(pDataTarget->QueryInterface(__uuidof(ICorDebugDataTarget), (void**)&pDt)))
+ {
+ hr = CORDBG_E_MISSING_DATA_TARGET_INTERFACE;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // get CLR version
+ // The expectation is that new versions of the CLR will continue to use the same GUID
+ // (unless there's a reason to hide them from older shims), but debuggers will tell us the
+ // CLR version they're designed for and mscordbi.dll can decide whether or not to accept it.
+ version.wStructVersion = 0;
+ hr = GetCLRInfo(pDt,
+ moduleBaseAddress,
+ &version,
+ &dbiTimestamp,
+ &dbiSizeOfImage,
+ dbiName,
+ MAX_PATH_FNAME,
+ &dacTimestamp,
+ &dacSizeOfImage,
+ dacName,
+ MAX_PATH_FNAME);
+ }
+
+ // If we need to fetch either the process info or the flags info then we need to find
+ // mscordbi and DAC and do the version specific OVP work
+ if (SUCCEEDED(hr) && (ppProcess != NULL || pFlags != NULL))
+ {
+ ICLRDebuggingLibraryProvider2* pLibraryProvider2;
+ if (SUCCEEDED(pLibraryProvider->QueryInterface(__uuidof(ICLRDebuggingLibraryProvider2), (void**)&pLibraryProvider2)))
+ {
+ if (FAILED(pLibraryProvider2->ProvideLibrary2(dbiName, dbiTimestamp, dbiSizeOfImage, &pDbiModulePath)) ||
+ pDbiModulePath == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hDbi = LoadLibraryW(pDbiModulePath);
+ if (hDbi == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Adjust the timestamp and size of image if this DAC is a known buggy version and needs to be retargeted
+ RetargetDacIfNeeded(&dacTimestamp, &dacSizeOfImage);
+
+ // Ask library provider for dac
+ if (FAILED(pLibraryProvider2->ProvideLibrary2(dacName, dacTimestamp, dacSizeOfImage, &pDacModulePath)) ||
+ pDacModulePath == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hDac = LoadLibraryW(pDacModulePath);
+ if (hDac == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+ }
+
+ pLibraryProvider2->Release();
+ }
+ else {
+ // Ask library provider for dbi
+ if (FAILED(pLibraryProvider->ProvideLibrary(dbiName, dbiTimestamp, dbiSizeOfImage, &hDbi)) ||
+ hDbi == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Adjust the timestamp and size of image if this DAC is a known buggy version and needs to be retargeted
+ RetargetDacIfNeeded(&dacTimestamp, &dacSizeOfImage);
+
+ // ask library provider for dac
+ if (FAILED(pLibraryProvider->ProvideLibrary(dacName, dacTimestamp, dacSizeOfImage, &hDac)) ||
+ hDac == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ }
+ }
+ }
+
+ *ppProcess = NULL;
+
+ if (SUCCEEDED(hr) && pDacModulePath != NULL)
+ {
+ // Get access to the latest OVP implementation and call it
+ OpenVirtualProcessImpl2FnPtr ovpFn = (OpenVirtualProcessImpl2FnPtr)GetProcAddress(hDbi, "OpenVirtualProcessImpl2");
+ if (ovpFn != NULL)
+ {
+ hr = ovpFn(moduleBaseAddress, pDataTarget, pDacModulePath, pMaxDebuggerSupportedVersion, riidProcess, ppProcess, pFlags);
+ if (FAILED(hr))
+ {
+ _ASSERTE(ppProcess == NULL || *ppProcess == NULL);
+ _ASSERTE(pFlags == NULL || *pFlags == 0);
+ }
+ }
+#ifdef HOST_UNIX
+ else
+ {
+ // On Linux/MacOS the DAC module handle needs to be re-created using the DAC PAL instance
+ // before being passed to DBI's OpenVirtualProcess* implementation. The DBI and DAC share
+ // the same PAL where dbgshim has it's own.
+ LoadLibraryWFnPtr loadLibraryWFn = (LoadLibraryWFnPtr)GetProcAddress(hDac, "LoadLibraryW");
+ if (loadLibraryWFn != NULL)
+ {
+ hDac = loadLibraryWFn(pDacModulePath);
+ if (hDac == NULL)
+ {
+ hr = E_HANDLE;
+ }
+ }
+ else
+ {
+ hr = E_HANDLE;
+ }
+ }
+#endif // HOST_UNIX
+ }
+
+ // If no errors so far and "OpenVirtualProcessImpl2" doesn't exist
+ if (SUCCEEDED(hr) && *ppProcess == NULL)
+ {
+ // Get access to OVP and call it
+ OpenVirtualProcessImplFnPtr ovpFn = (OpenVirtualProcessImplFnPtr)GetProcAddress(hDbi, "OpenVirtualProcessImpl");
+ if (ovpFn == NULL)
+ {
+ // Fallback to CLR v4 Beta1 path, but skip some of the checking we'd normally do (maxSupportedVersion, etc.)
+ OpenVirtualProcess2FnPtr ovp2Fn = (OpenVirtualProcess2FnPtr)GetProcAddress(hDbi, "OpenVirtualProcess2");
+ if (ovp2Fn == NULL)
+ {
+ hr = CORDBG_E_LIBRARY_PROVIDER_ERROR;
+ }
+ else
+ {
+ hr = ovp2Fn(moduleBaseAddress, pDataTarget, hDac, riidProcess, ppProcess, pFlags);
+ }
+ }
+ else
+ {
+ // Have a CLR v4 Beta2+ DBI, call it and let it do the version check
+ hr = ovpFn(moduleBaseAddress, pDataTarget, hDac, pMaxDebuggerSupportedVersion, riidProcess, ppProcess, pFlags);
+ if (FAILED(hr))
+ {
+ _ASSERTE(ppProcess == NULL || *ppProcess == NULL);
+ _ASSERTE(pFlags == NULL || *pFlags == 0);
+ }
+ }
+ }
+ }
+
+ //version is still valid in some failure cases
+ if (pVersion != NULL &&
+ (SUCCEEDED(hr) ||
+ (hr == CORDBG_E_UNSUPPORTED_DEBUGGING_MODEL) ||
+ (hr == CORDBG_E_UNSUPPORTED_FORWARD_COMPAT)))
+ {
+ memcpy(pVersion, &version, sizeof(CLR_DEBUGGING_VERSION));
+ }
+
+ if (pDacModulePath != NULL)
+ {
+#ifdef HOST_UNIX
+ free(pDacModulePath);
+#else
+ CoTaskMemFree(pDacModulePath);
+#endif
+ }
+
+ if (pDbiModulePath != NULL)
+ {
+#ifdef HOST_UNIX
+ free(pDbiModulePath);
+#else
+ CoTaskMemFree(pDbiModulePath);
+#endif
+ }
+
+ // free the data target we QI'ed earlier
+ if (pDt != NULL)
+ {
+ pDt->Release();
+ }
+
+ return hr;
+}
+
+// Checks to see if this DAC is one of a known set of old DAC builds which contains an issue.
+// If so we retarget to a newer compatible version which has the bug fixed. This is done
+// by changing the PE information used to lookup the DAC.
+//
+// Arguments
+// pdwTimeStamp - on input, the timestamp of DAC as embedded in the CLR image
+// on output, a potentially new timestamp for an updated DAC to use
+// instead
+// pdwSizeOfImage - on input, the sizeOfImage of DAC as embedded in the CLR image
+// on output, a potentially new sizeOfImage for an updated DAC to use
+// instead
+VOID CLRDebuggingImpl::RetargetDacIfNeeded(DWORD* pdwTimeStamp,
+ DWORD* pdwSizeOfImage)
+{
+
+ // This code is auto generated by the CreateRetargetTable tool
+ // on 3/4/2011 6:35 PM
+ // and then copy-pasted here.
+ //
+ //
+ //
+ // Retarget the GDR1 amd64 build
+ if( (*pdwTimeStamp == 0x4d536868) && (*pdwSizeOfImage == 0x17b000))
+ {
+ *pdwTimeStamp = 0x4d71a160;
+ *pdwSizeOfImage = 0x17b000;
+ }
+ // Retarget the GDR1 x86 build
+ else if( (*pdwTimeStamp == 0x4d5368f2) && (*pdwSizeOfImage == 0x120000))
+ {
+ *pdwTimeStamp = 0x4d71a14f;
+ *pdwSizeOfImage = 0x120000;
+ }
+ // Retarget the RTM amd64 build
+ else if( (*pdwTimeStamp == 0x4ba21fa7) && (*pdwSizeOfImage == 0x17b000))
+ {
+ *pdwTimeStamp = 0x4d71a13c;
+ *pdwSizeOfImage = 0x17b000;
+ }
+ // Retarget the RTM x86 build
+ else if( (*pdwTimeStamp == 0x4ba1da25) && (*pdwSizeOfImage == 0x120000))
+ {
+ *pdwTimeStamp = 0x4d71a128;
+ *pdwSizeOfImage = 0x120000;
+ }
+ // This code is auto generated by the CreateRetargetTable tool
+ // on 8/17/2011 1:28 AM
+ // and then copy-pasted here.
+ //
+ //
+ //
+ // Retarget the GDR2 amd64 build
+ else if( (*pdwTimeStamp == 0x4da428c7) && (*pdwSizeOfImage == 0x17b000))
+ {
+ *pdwTimeStamp = 0x4e4b7bc2;
+ *pdwSizeOfImage = 0x17b000;
+ }
+ // Retarget the GDR2 x86 build
+ else if( (*pdwTimeStamp == 0x4da3fe52) && (*pdwSizeOfImage == 0x120000))
+ {
+ *pdwTimeStamp = 0x4e4b7bb1;
+ *pdwSizeOfImage = 0x120000;
+ }
+ // End auto-generated code
+}
+
+#define PE_FIXEDFILEINFO_SIGNATURE 0xFEEF04BD
+
+// The format of the special debugging resource we embed in CLRs starting in
+// v4
+struct CLR_DEBUG_RESOURCE
+{
+ DWORD dwVersion;
+ GUID signature;
+ DWORD dwDacTimeStamp;
+ DWORD dwDacSizeOfImage;
+ DWORD dwDbiTimeStamp;
+ DWORD dwDbiSizeOfImage;
+};
+
+// Checks to see if a module is a CLR and if so, fetches the debug data
+// from the embedded resource
+//
+// Arguments
+// pDataTarget - dataTarget for the process we are inspecting
+// moduleBaseAddress - base address of a module we should inspect
+// pVersion - output, the version of the CLR detected if this is a CLR
+// pdwDbiTimeStamp - the timestamp of DBI as embedded in the CLR image
+// pdwDbiSizeOfImage - the SizeOfImage of DBI as embedded in the CLR image
+// pDbiName - output, the filename of DBI (as calculated by this function but that might change)
+// dwDbiNameCharCount - input, the number of WCHARs in the buffer pointed to by pDbiName
+// pdwDacTimeStampe - the timestamp of DAC as embedded in the CLR image
+// pdwDacSizeOfImage - the SizeOfImage of DAC as embedded in the CLR image
+// pDacName - output, the filename of DAC (as calculated by this function but that might change)
+// dwDacNameCharCount - input, the number of WCHARs in the buffer pointed to by pDacName
+HRESULT CLRDebuggingImpl::GetCLRInfo(ICorDebugDataTarget* pDataTarget,
+ ULONG64 moduleBaseAddress,
+ CLR_DEBUGGING_VERSION* pVersion,
+ DWORD* pdwDbiTimeStamp,
+ DWORD* pdwDbiSizeOfImage,
+ _Inout_updates_z_(dwDbiNameCharCount) WCHAR* pDbiName,
+ DWORD dwDbiNameCharCount,
+ DWORD* pdwDacTimeStamp,
+ DWORD* pdwDacSizeOfImage,
+ _Inout_updates_z_(dwDacNameCharCount) WCHAR* pDacName,
+ DWORD dwDacNameCharCount)
+{
+#ifdef HOST_WINDOWS
+ if(IsTargetWindows(pDataTarget))
+ {
+ WORD imageFileMachine = 0;
+ DWORD resourceSectionRVA = 0;
+ HRESULT hr = GetMachineAndResourceSectionRVA(pDataTarget, moduleBaseAddress, &imageFileMachine, &resourceSectionRVA);
+
+ // We want the version resource which has type = RT_VERSION = 16, name = 1, language = 0x409
+ DWORD versionResourceRVA = 0;
+ DWORD versionResourceSize = 0;
+ if(SUCCEEDED(hr))
+ {
+ hr = GetResourceRvaFromResourceSectionRva(pDataTarget, moduleBaseAddress, resourceSectionRVA, 16, 1, 0x409,
+ &versionResourceRVA, &versionResourceSize);
+ }
+
+ // At last we get our version info
+ VS_FIXEDFILEINFO fixedFileInfo = {0};
+ if(SUCCEEDED(hr))
+ {
+ // The version resource has 3 words, then the unicode string "VS_VERSION_INFO"
+ // (16 WCHARS including the null terminator)
+ // then padding to a 32-bit boundary, then the VS_FIXEDFILEINFO struct
+ DWORD fixedFileInfoRVA = ((versionResourceRVA + 3*2 + 16*2 + 3)/4)*4;
+ hr = ReadFromDataTarget(pDataTarget, moduleBaseAddress + fixedFileInfoRVA, (BYTE*)&fixedFileInfo, sizeof(fixedFileInfo));
+ }
+
+ //Verify the signature on the version resource
+ if(SUCCEEDED(hr) && fixedFileInfo.dwSignature != PE_FIXEDFILEINFO_SIGNATURE)
+ {
+ hr = CORDBG_E_NOT_CLR;
+ }
+
+ // Record the version information
+ if(SUCCEEDED(hr))
+ {
+ pVersion->wMajor = (WORD) (fixedFileInfo.dwProductVersionMS >> 16);
+ pVersion->wMinor = (WORD) (fixedFileInfo.dwProductVersionMS & 0xFFFF);
+ pVersion->wBuild = (WORD) (fixedFileInfo.dwProductVersionLS >> 16);
+ pVersion->wRevision = (WORD) (fixedFileInfo.dwProductVersionLS & 0xFFFF);
+ }
+
+ // Now grab the special clr debug info resource
+ // We may need to scan a few different names searching though...
+ // 1) CLRDEBUGINFO<host_os><host_arch> where host_os = 'WINDOWS' or 'CORESYS' and host_arch = 'X86' or 'ARM' or 'AMD64'
+ // 2) For back-compat if the host os is windows and the host architecture matches the target then CLRDEBUGINFO is used with no suffix.
+ DWORD debugResourceRVA = 0;
+ DWORD debugResourceSize = 0;
+ BOOL useCrossPlatformNaming = FALSE;
+ if(SUCCEEDED(hr))
+ {
+ // the initial state is that we haven't found a proper resource
+ HRESULT hrGetResource = E_FAIL;
+
+ // First check for the resource which has type = RC_DATA = 10, name = "CLRDEBUGINFO<host_os><host_arch>", language = 0
+ #if defined (HOST_WINDOWS) && defined(HOST_X86)
+ const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSX86");
+ #endif
+
+ #if !defined (HOST_WINDOWS) && defined(HOST_X86)
+ const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSX86");
+ #endif
+
+ #if defined (HOST_WINDOWS) && defined(HOST_AMD64)
+ const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSAMD64");
+ #endif
+
+ #if !defined (HOST_WINDOWS) && defined(HOST_AMD64)
+ const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSAMD64");
+ #endif
+
+ #if defined (HOST_WINDOWS) && defined(HOST_ARM64)
+ const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSARM64");
+ #endif
+
+ #if !defined (HOST_WINDOWS) && defined(HOST_ARM64)
+ const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSARM64");
+ #endif
+
+ #if defined (HOST_WINDOWS) && defined(HOST_ARM)
+ const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSARM");
+ #endif
+
+ #if !defined (HOST_WINDOWS) && defined(HOST_ARM)
+ const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSARM");
+ #endif
+
+ hrGetResource = GetResourceRvaFromResourceSectionRvaByName(pDataTarget, moduleBaseAddress, resourceSectionRVA, 10, resourceName, 0,
+ &debugResourceRVA, &debugResourceSize);
+ useCrossPlatformNaming = SUCCEEDED(hrGetResource);
+
+
+ #if defined(HOST_WINDOWS) && (defined(HOST_X86) || defined(HOST_AMD64) || defined(HOST_ARM))
+ #if defined(HOST_X86)
+ #define _HOST_MACHINE_TYPE IMAGE_FILE_MACHINE_I386
+ #elif defined(HOST_AMD64)
+ #define _HOST_MACHINE_TYPE IMAGE_FILE_MACHINE_AMD64
+ #elif defined(HOST_ARM)
+ #define _HOST_MACHINE_TYPE IMAGE_FILE_MACHINE_ARMNT
+ #endif
+
+ // if this is windows, and if host_arch matches target arch then we can fallback to searching for CLRDEBUGINFO on failure
+ if(FAILED(hrGetResource) && (imageFileMachine == _HOST_MACHINE_TYPE))
+ {
+ hrGetResource = GetResourceRvaFromResourceSectionRvaByName(pDataTarget, moduleBaseAddress, resourceSectionRVA, 10, W("CLRDEBUGINFO"), 0,
+ &debugResourceRVA, &debugResourceSize);
+ }
+
+ #undef _HOST_MACHINE_TYPE
+ #endif
+ // if the search failed, we don't recognize the CLR
+ if(FAILED(hrGetResource))
+ hr = CORDBG_E_NOT_CLR;
+ }
+
+ CLR_DEBUG_RESOURCE debugResource;
+ if(SUCCEEDED(hr) && debugResourceSize != sizeof(debugResource))
+ {
+ hr = CORDBG_E_NOT_CLR;
+ }
+
+ // Get the special debug resource from the image and return the results
+ if(SUCCEEDED(hr))
+ {
+ hr = ReadFromDataTarget(pDataTarget, moduleBaseAddress + debugResourceRVA, (BYTE*)&debugResource, sizeof(debugResource));
+ }
+ if(SUCCEEDED(hr) && (debugResource.dwVersion != 0))
+ {
+ hr = CORDBG_E_NOT_CLR;
+ }
+
+ // The signature needs to match m_skuId exactly, except for m_skuId=CLR_ID_ONECORE_CLR which is
+ // also compatible with the older CLR_ID_PHONE_CLR signature.
+ if(SUCCEEDED(hr) &&
+ (debugResource.signature != m_skuId) &&
+ !( (debugResource.signature == CLR_ID_PHONE_CLR) && (m_skuId == CLR_ID_ONECORE_CLR) ))
+ {
+ hr = CORDBG_E_NOT_CLR;
+ }
+
+ if(SUCCEEDED(hr) &&
+ (debugResource.signature != CLR_ID_ONECORE_CLR) &&
+ useCrossPlatformNaming)
+ {
+ FormatLongDacModuleName(pDacName, dwDacNameCharCount, imageFileMachine, &fixedFileInfo);
+ swprintf_s(pDbiName, dwDbiNameCharCount, W("%s_%s.dll"), MAIN_DBI_MODULE_NAME_W, W("x86"));
+ }
+ else
+ {
+ if(m_skuId == CLR_ID_V4_DESKTOP)
+ swprintf_s(pDacName, dwDacNameCharCount, W("%s.dll"), CLR_DAC_MODULE_NAME_W);
+ else
+ swprintf_s(pDacName, dwDacNameCharCount, W("%s.dll"), CORECLR_DAC_MODULE_NAME_W);
+ swprintf_s(pDbiName, dwDbiNameCharCount, W("%s.dll"), MAIN_DBI_MODULE_NAME_W);
+ }
+
+ if(SUCCEEDED(hr))
+ {
+ *pdwDbiTimeStamp = debugResource.dwDbiTimeStamp;
+ *pdwDbiSizeOfImage = debugResource.dwDbiSizeOfImage;
+ *pdwDacTimeStamp = debugResource.dwDacTimeStamp;
+ *pdwDacSizeOfImage = debugResource.dwDacSizeOfImage;
+ }
+
+ // any failure should be interpreted as this module not being a CLR
+ if(FAILED(hr))
+ {
+ return CORDBG_E_NOT_CLR;
+ }
+ else
+ {
+ return S_OK;
+ }
+ }
+ else
+#endif // !HOST_WINDOWS
+ {
+ swprintf_s(pDacName, dwDacNameCharCount, W("%s"), MAKEDLLNAME_W(CORECLR_DAC_MODULE_NAME_W));
+ swprintf_s(pDbiName, dwDbiNameCharCount, W("%s"), MAKEDLLNAME_W(MAIN_DBI_MODULE_NAME_W));
+
+ pVersion->wMajor = 0;
+ pVersion->wMinor = 0;
+ pVersion->wBuild = 0;
+ pVersion->wRevision = 0;
+
+ *pdwDbiTimeStamp = 0;
+ *pdwDbiSizeOfImage = 0;
+ *pdwDacTimeStamp = 0;
+ *pdwDacSizeOfImage = 0;
+
+ return S_OK;
+ }
+}
+
+// Formats the long name for DAC
+HRESULT CLRDebuggingImpl::FormatLongDacModuleName(_Inout_updates_z_(cchBuffer) WCHAR * pBuffer,
+ DWORD cchBuffer,
+ DWORD targetImageFileMachine,
+ VS_FIXEDFILEINFO * pVersion)
+{
+
+#ifndef HOST_WINDOWS
+ _ASSERTE(!"NYI");
+ return E_NOTIMPL;
+#endif
+
+#if defined(HOST_X86)
+ const WCHAR* pHostArch = W("x86");
+#elif defined(HOST_AMD64)
+ const WCHAR* pHostArch = W("amd64");
+#elif defined(HOST_ARM)
+ const WCHAR* pHostArch = W("arm");
+#elif defined(HOST_ARM64)
+ const WCHAR* pHostArch = W("arm64");
+#else
+ _ASSERTE(!"Unknown host arch");
+ return E_NOTIMPL;
+#endif
+
+ const WCHAR* pDacBaseName = NULL;
+ if(m_skuId == CLR_ID_V4_DESKTOP)
+ pDacBaseName = CLR_DAC_MODULE_NAME_W;
+ else if(m_skuId == CLR_ID_CORECLR || m_skuId == CLR_ID_PHONE_CLR || m_skuId == CLR_ID_ONECORE_CLR)
+ pDacBaseName = CORECLR_DAC_MODULE_NAME_W;
+ else
+ {
+ _ASSERTE(!"Unknown SKU id");
+ return E_UNEXPECTED;
+ }
+
+ const WCHAR* pTargetArch = NULL;
+ if(targetImageFileMachine == IMAGE_FILE_MACHINE_I386)
+ {
+ pTargetArch = W("x86");
+ }
+ else if(targetImageFileMachine == IMAGE_FILE_MACHINE_AMD64)
+ {
+ pTargetArch = W("amd64");
+ }
+ else if(targetImageFileMachine == IMAGE_FILE_MACHINE_ARMNT)
+ {
+ pTargetArch = W("arm");
+ }
+ else if(targetImageFileMachine == IMAGE_FILE_MACHINE_ARM64)
+ {
+ pTargetArch = W("arm64");
+ }
+ else
+ {
+ _ASSERTE(!"Unknown target image file machine type");
+ return E_INVALIDARG;
+ }
+
+ const WCHAR* pBuildFlavor = W("");
+ if(pVersion->dwFileFlags & VS_FF_DEBUG)
+ {
+ if(pVersion->dwFileFlags & VS_FF_SPECIALBUILD)
+ pBuildFlavor = W(".dbg");
+ else
+ pBuildFlavor = W(".chk");
+ }
+
+ // WARNING: if you change the formatting make sure you recalculate the maximum
+ // possible size string and verify callers pass a big enough buffer. This doesn't
+ // have to be a tight estimate, just make sure its >= the biggest possible DAC name
+ // and it can be calculated statically
+ DWORD minCchBuffer =
+ (DWORD) wcslen(CLR_DAC_MODULE_NAME_W) + (DWORD) wcslen(CORECLR_DAC_MODULE_NAME_W) + // max name
+ 10 + // max host arch
+ 10 + // max target arch
+ 40 + // max version
+ 10 + // max build flavor
+ (DWORD) wcslen(W("name_host_target_version.flavor.dll")) + // max intermediate formatting chars
+ 1; // null terminator
+
+ // validate the output buffer is larger than our estimate above
+ _ASSERTE(cchBuffer >= minCchBuffer);
+ if(!(cchBuffer >= minCchBuffer)) return E_INVALIDARG;
+
+ swprintf_s(pBuffer, cchBuffer, W("%s_%s_%s_%u.%u.%u.%02u%s.dll"),
+ pDacBaseName,
+ pHostArch,
+ pTargetArch,
+ pVersion->dwProductVersionMS >> 16,
+ pVersion->dwProductVersionMS & 0xFFFF,
+ pVersion->dwProductVersionLS >> 16,
+ pVersion->dwProductVersionLS & 0xFFFF,
+ pBuildFlavor);
+ return S_OK;
+}
+
+// An implementation of ICLRDebugging::CanUnloadNow
+//
+// Arguments:
+// hModule - a handle to a module provided earlier by ProvideLibrary
+//
+// Returns:
+// S_OK if the library is no longer in use and can be unloaded, S_FALSE otherwise
+//
+STDMETHODIMP CLRDebuggingImpl::CanUnloadNow(HMODULE hModule)
+{
+ // In V4 at least we don't support any unloading.
+ HRESULT hr = S_FALSE;
+
+ return hr;
+}
+
+
+
+STDMETHODIMP CLRDebuggingImpl::QueryInterface(REFIID riid, void **ppvObject)
+{
+ HRESULT hr = S_OK;
+
+ if (riid == __uuidof(IUnknown))
+ {
+ IUnknown *pItf = static_cast<IUnknown *>(this);
+ pItf->AddRef();
+ *ppvObject = pItf;
+ }
+ else if (riid == __uuidof(ICLRDebugging))
+ {
+ ICLRDebugging *pItf = static_cast<ICLRDebugging *>(this);
+ pItf->AddRef();
+ *ppvObject = pItf;
+ }
+ else
+ hr = E_NOINTERFACE;
+
+ return hr;
+}
+
+// Standard AddRef implementation
+ULONG CLRDebuggingImpl::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+// Standard Release implementation.
+ULONG CLRDebuggingImpl::Release()
+{
+ _ASSERTE(m_cRef > 0);
+
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+
+ if (cRef == 0)
+ delete this; // Relies on virtual dtor to work properly.
+
+ return cRef;
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// debugshim.h
+//
+
+//
+//*****************************************************************************
+
+#ifndef _DEBUG_SHIM_
+#define _DEBUG_SHIM_
+
+#include "cor.h"
+#include "cordebug.h"
+#include <wchar.h>
+#include <metahost.h>
+
+#define CORECLR_DAC_MODULE_NAME_W W("mscordaccore")
+#define CLR_DAC_MODULE_NAME_W W("mscordacwks")
+#define MAIN_DBI_MODULE_NAME_W W("mscordbi")
+
+// forward declaration
+struct ICorDebugDataTarget;
+
+// ICLRDebugging implementation.
+class CLRDebuggingImpl : public ICLRDebugging
+{
+
+public:
+ CLRDebuggingImpl(GUID skuId) : m_cRef(0), m_skuId(skuId)
+ {
+ }
+
+ virtual ~CLRDebuggingImpl() {}
+
+public:
+ // ICLRDebugging methods:
+ STDMETHOD(OpenVirtualProcess(
+ ULONG64 moduleBaseAddress,
+ IUnknown * pDataTarget,
+ ICLRDebuggingLibraryProvider * pLibraryProvider,
+ CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion,
+ REFIID riidProcess,
+ IUnknown ** ppProcess,
+ CLR_DEBUGGING_VERSION * pVersion,
+ CLR_DEBUGGING_PROCESS_FLAGS * pFlags));
+
+ STDMETHOD(CanUnloadNow(HMODULE hModule));
+
+ //IUnknown methods:
+ STDMETHOD(QueryInterface(
+ REFIID riid,
+ void **ppvObject));
+
+ // Standard AddRef implementation
+ STDMETHOD_(ULONG, AddRef());
+
+ // Standard Release implementation.
+ STDMETHOD_(ULONG, Release());
+
+
+
+private:
+ VOID RetargetDacIfNeeded(DWORD* pdwTimeStamp,
+ DWORD* pdwSizeOfImage);
+
+ HRESULT GetCLRInfo(ICorDebugDataTarget * pDataTarget,
+ ULONG64 moduleBaseAddress,
+ CLR_DEBUGGING_VERSION * pVersion,
+ DWORD * pdwDbiTimeStamp,
+ DWORD * pdwDbiSizeOfImage,
+ _Inout_updates_z_(dwDbiNameCharCount) WCHAR * pDbiName,
+ DWORD dwDbiNameCharCount,
+ DWORD * pdwDacTimeStamp,
+ DWORD * pdwDacSizeOfImage,
+ _Inout_updates_z_(dwDacNameCharCount) WCHAR * pDacName,
+ DWORD dwDacNameCharCount);
+
+ HRESULT FormatLongDacModuleName(_Inout_updates_z_(cchBuffer) WCHAR * pBuffer,
+ DWORD cchBuffer,
+ DWORD targetImageFileMachine,
+ VS_FIXEDFILEINFO * pVersion);
+
+ volatile LONG m_cRef;
+ GUID m_skuId;
+
+}; // class CLRDebuggingImpl
+
+#endif
--- /dev/null
+<Project>
+ <Import Project="$(MSBuildThisFileDirectory)..\..\Directory.Build.props"/>
+ <PropertyGroup>
+ <IsShipping>true</IsShipping>
+ <NoPackageAnalysis>true</NoPackageAnalysis>
+ <PackageDescription>Internal implementation package not meant for direct consumption. Please do not reference directly.</PackageDescription>
+ </PropertyGroup>
+
+ <Choose>
+ <When Condition="$(OutputRid.StartsWith('win'))">
+ <PropertyGroup>
+ <OsFolderName>Windows_NT</OsFolderName>
+ <ExeSuffix>.exe</ExeSuffix>
+ <LibSuffix>.dll</LibSuffix>
+ <StaticLibSuffix>.lib</StaticLibSuffix>
+ <SymbolsSuffix>.pdb</SymbolsSuffix>
+ </PropertyGroup>
+ </When>
+ <When Condition="$(OutputRid.StartsWith('osx'))">
+ <PropertyGroup>
+ <OsFolderName>OSX</OsFolderName>
+ <LibPrefix>lib</LibPrefix>
+ <LibSuffix>.dylib</LibSuffix>
+ <StaticLibSuffix>.a</StaticLibSuffix>
+ <SymbolsSuffix>.dwarf</SymbolsSuffix>
+ </PropertyGroup>
+ </When>
+ <Otherwise>
+ <PropertyGroup>
+ <OsFolderName>Linux</OsFolderName>
+ <OsFolderName Condition="$(OutputRid.StartsWith('linux-musl'))">Linux-musl</OsFolderName>
+ <LibPrefix>lib</LibPrefix>
+ <LibSuffix>.so</LibSuffix>
+ <StaticLibSuffix>.a</StaticLibSuffix>
+ <SymbolsSuffix>.dbg</SymbolsSuffix>
+ </PropertyGroup>
+ </Otherwise>
+ </Choose>
+
+ <PropertyGroup>
+ <NativeBinDir>$(ArtifactsBinDir)\$(OsFolderName).$(PackageArch).$(Configuration)\</NativeBinDir>
+ </PropertyGroup>
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>linux-arm</OutputRid>
+ <PackageArch>arm</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>linux-arm64</OutputRid>
+ <PackageArch>arm64</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>linux-musl-x64</OutputRid>
+ <PackageArch>x64</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>linux-musl-x64</OutputRid>
+ <PackageArch>x64</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>linux-x64</OutputRid>
+ <PackageArch>x64</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>osx-arm64</OutputRid>
+ <PackageArch>arm64</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>osx-x64</OutputRid>
+ <PackageArch>x64</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
--- /dev/null
+<Project Sdk="Microsoft.Build.NoTargets">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <IsPackable>true</IsPackable>
+ <!-- Reference the outputs for the dependency nodes calculation. -->
+ <NoTargetsDoNotReferenceOutputAssemblies>false</NoTargetsDoNotReferenceOutputAssemblies>
+ <!-- This is a meta package and doesn't contain any libs. -->
+ <NoWarn>$(NoWarn);NU5128</NoWarn>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <!--
+ Listing the runtime specific packages to populate the dependencies section.
+ Not building these references to avoid unintentional Build/Pack invocations.
+ -->
+ <ProjectReference Include="$(MSBuildThisFileDirectory)*.proj" Exclude="$(MSBuildProjectFile)" BuildReference="false" />
+ </ItemGroup>
+
+ <Target Name="PackRidSpecificPackages"
+ AfterTargets="Pack">
+ <MSBuild Targets="Pack"
+ Projects="@(ProjectReference)"
+ Properties="$(TraversalGlobalProperties)" />
+ </Target>
+
+</Project>
\ No newline at end of file
--- /dev/null
+<Project Sdk="Microsoft.Build.NoTargets">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
+ <!-- IncludeBuildOutput needs to be set to true to make NuGet include the passed in debug symbol files. -->
+ <IncludeBuildOutput>false</IncludeBuildOutput>
+ <IncludeSymbols>true</IncludeSymbols>
+ <DebugSymbols>false</DebugSymbols>
+ <DebugType>none</DebugType>
+ <IsPackable>true</IsPackable>
+ <AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder>.pdb;.dbg;.dwarf</AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder>
+ <!-- When KeepNativeSymbols is set, debug symbols are kept in the .so files. Separate symbol files do not exist that need to be packed. -->
+ <!-- <TargetsForTfmSpecificDebugSymbolsInPackage Condition="'$(KeepNativeSymbols)' != 'true'">$(TargetsForTfmSpecificDebugSymbolsInPackage);AddRuntimeSpecificNativeSymbolToPackage</TargetsForTfmSpecificDebugSymbolsInPackage> -->
+ <UseRuntimePackageDisclaimer>true</UseRuntimePackageDisclaimer>
+ <!-- This is a native package and doesn't contain any ref/lib assets. -->
+ <NoWarn>$(NoWarn);NU5128</NoWarn>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <None Include="$(NativeBinDir)$(LibPrefix)dbgshim$(LibSuffix)"
+ PackagePath="runtimes/$(OutputRid)/native"
+ Pack="true" />
+ </ItemGroup>
+
+ <Target Name="AddRuntimeSpecificNativeSymbolToPackage">
+ <ItemGroup>
+ <TfmSpecificDebugSymbolsFile Include="$(NativeBinDir)$(LibPrefix)dbgshim$(LibSuffix)$(SymbolsSuffix)"
+ TargetPath="/runtimes/$(OutputRid)/native"
+ TargetFramework="$(TargetFramework)" />
+ </ItemGroup>
+ </Target>
+
+ <Target Name="CreateManifestResourceNames" />
+ </Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>win-arm</OutputRid>
+ <PackageArch>arm</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>win-arm64</OutputRid>
+ <PackageArch>arm64</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>win-x64</OutputRid>
+ <PackageArch>x64</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <OutputRid>win-x86</OutputRid>
+ <PackageArch>x86</PackageArch>
+ </PropertyGroup>
+ <Import Project="Microsoft.Diagnostics.DbgShim.props" />
+</Project>
-if(CLR_CMAKE_HOST_UNIX)
- include_directories(${ROOT_DIR}/src/pal/inc)
- include_directories(${ROOT_DIR}/src/pal/inc/rt)
-endif(CLR_CMAKE_HOST_UNIX)
set( CORGUIDS_IDL_SOURCES
cordebug.idl
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// Check.h
+//
+
+//
+// Assertion checking infrastructure
+// ---------------------------------------------------------------------------
+
+
+#ifndef CHECK_H_
+#define CHECK_H_
+
+#include "static_assert.h"
+#include "daccess.h"
+#include "unreachable.h"
+
+#ifdef _DEBUG
+
+#ifdef _MSC_VER
+// Make sure we can recurse deep enough for FORCEINLINE
+#pragma inline_recursion(on)
+#pragma inline_depth(16)
+#pragma warning(disable:4714)
+#endif // _MSC_VER
+
+#if !defined(DISABLE_CONTRACTS)
+#define CHECK_INVARIANTS 1
+#define VALIDATE_OBJECTS 1
+#endif
+
+#endif // _DEBUG
+
+#if defined(_DEBUG) && !defined(DACCESS_COMPILE)
+#define _DEBUG_IMPL 1
+#endif
+
+#ifdef _DEBUG
+#define DEBUG_ARG(x) , x
+#else
+#define DEBUG_ARG(x)
+#endif
+
+#define CHECK_STRESS 1
+
+//--------------------------------------------------------------------------------
+// A CHECK is an object which encapsulates a potential assertion
+// failure. It not only contains the result of the check, but if the check fails,
+// also records information about the condition and call site.
+//
+// CHECK also serves as a holder to prevent recursive CHECKS. These can be
+// particularly common when putting preconditions inside predicates, especially
+// routines called by an invariant.
+//
+// Note that using CHECK is perfectly efficient in a free build - the CHECK becomes
+// a simple string constant pointer (typically either NULL or (LPCSTR)1, although some
+// check failures may include messages)
+//
+// NOTE: you should NEVER use the CHECK class API directly - use the macros below.
+//--------------------------------------------------------------------------------
+
+class SString;
+
+class CHECK
+{
+protected:
+ // On retail, this is a pointer to a string literal, null or (LPCSTR)1.
+ // On debug, this is a pointer to dynamically allocated memory - that
+ // lets us have formatted strings in debug builds.
+ LPCSTR m_message;
+
+#ifdef _DEBUG
+ LPCSTR m_condition;
+ LPCSTR m_file;
+ INT m_line;
+ LONG *m_pCount;
+
+ // Keep leakage counters.
+ static size_t s_cLeakedBytes;
+ static size_t s_cNumFailures;
+
+ static thread_local LONG t_count;
+#endif
+
+ static BOOL s_neverEnforceAsserts;
+
+public: // !!! NOTE: Called from macros only!!!
+
+ // If we are not in a check, return TRUE and PushCheck; otherwise return FALSE
+ BOOL EnterAssert();
+
+ // Pops check count
+ void LeaveAssert();
+
+ // Just return if we are in a check
+ BOOL IsInAssert();
+
+ // Should we skip enforcing asserts
+ static BOOL EnforceAssert();
+
+ static BOOL EnforceAssert_StaticCheckOnly();
+
+ static void ResetAssert();
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4702) // Disable bogus unreachable code warning
+#endif // _MSC_VER
+ CHECK() : m_message(NULL)
+#ifdef _DEBUG
+ , m_condition (NULL)
+ , m_file(NULL)
+ , m_line(NULL)
+ , m_pCount(NULL)
+#endif
+ {}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+ // Fail records the result of a condition check. Can take either a
+ // boolean value or another check result
+ BOOL Fail(BOOL condition);
+ BOOL Fail(const CHECK &check);
+
+ // Setup records context info after a failure.
+ void Setup(LPCSTR message DEBUG_ARG(LPCSTR condition) DEBUG_ARG(LPCSTR file) DEBUG_ARG(INT line));
+ static LPCSTR FormatMessage(LPCSTR messageFormat, ...);
+
+ // Trigger triggers the actual check failure. The trigger may provide a reason
+ // to include in the failure message.
+ void Trigger(LPCSTR reason);
+
+ // Finally, convert to a BOOL to allow just testing the result of a Check function
+ operator BOOL();
+
+ BOOL operator!();
+
+ CHECK &operator()() { return *this; }
+
+ static inline const CHECK OK() {
+ return CHECK();
+ }
+
+ static void SetAssertEnforcement(BOOL value);
+
+ private:
+#ifdef _DEBUG
+ static LPCSTR AllocateDynamicMessage(const SString &s);
+#endif
+};
+
+
+//--------------------------------------------------------------------------------
+// These CHECK macros are the correct way to propagate an assertion. These
+// routines are designed for use inside "Check" routines. Such routines may
+// be Invariants, Validate routines, or any other assertional predicates.
+//
+// A Check routine should return a value of type CHECK.
+//
+// It should consist of multiple CHECK or CHECK_MSG statements (along with appropritate
+// control flow) and should end with CHECK_OK() if all other checks pass.
+//
+// It may contain a CONTRACT_CHECK contract, but this is only appropriate if the
+// check is used for non-assertional purposes (otherwise the contract will never execute).
+// Note that CONTRACT_CHECK contracts do not support postconditions.
+//
+// CHECK: Check the given condition, return a CHECK failure if FALSE
+// CHECK_MSG: Same, but include a message paramter if the check fails
+// CHECK_OK: Return a successful check value;
+//--------------------------------------------------------------------------------
+
+#ifdef _DEBUG
+#define DEBUG_ONLY_MESSAGE(msg) msg
+#else
+// On retail, we don't want to add a bunch of string literals to the image,
+// so we just use the same one everywhere.
+#define DEBUG_ONLY_MESSAGE(msg) ((LPCSTR)1)
+#endif
+
+#define CHECK_MSG_EX(_condition, _message, _RESULT) \
+do \
+{ \
+ CHECK _check; \
+ if (_check.Fail(_condition)) \
+ { \
+ ENTER_DEBUG_ONLY_CODE; \
+ _check.Setup(DEBUG_ONLY_MESSAGE(_message) \
+ DEBUG_ARG(#_condition) \
+ DEBUG_ARG(__FILE__) \
+ DEBUG_ARG(__LINE__)); \
+ _RESULT(_check); \
+ LEAVE_DEBUG_ONLY_CODE; \
+ } \
+} while (0)
+
+#define RETURN_RESULT(r) return r
+
+#define CHECK_MSG(_condition, _message) \
+ CHECK_MSG_EX(_condition, _message, RETURN_RESULT)
+
+#define CHECK(_condition) \
+ CHECK_MSG(_condition, "")
+
+#define CHECK_MSGF(_condition, _args) \
+ CHECK_MSG(_condition, CHECK::FormatMessage _args)
+
+#define CHECK_FAIL(_message) \
+ CHECK_MSG(FALSE, _message); UNREACHABLE()
+
+#define CHECK_FAILF(_args) \
+ CHECK_MSGF(FALSE, _args); UNREACHABLE()
+
+#define CHECK_OK \
+ return CHECK::OK()
+
+//--------------------------------------------------------------------------------
+// ASSERT_CHECK is the proper way to trigger a check result. If the CHECK
+// has failed, the diagnostic assertion routines will fire with appropriate
+// context information.
+//
+// Note that the condition may either be a raw boolean expression or a CHECK result
+// returned from a Check routine.
+//
+// Recursion note: ASSERT_CHECKs are only performed if there is no current check in
+// progress.
+//--------------------------------------------------------------------------------
+
+#ifndef ENTER_DEBUG_ONLY_CODE
+#define ENTER_DEBUG_ONLY_CODE
+#endif
+
+#ifndef LEAVE_DEBUG_ONLY_CODE
+#define LEAVE_DEBUG_ONLY_CODE
+#endif
+
+#define ASSERT_CHECK(_condition, _message, _reason) \
+do \
+{ \
+ CHECK _check; \
+ if (_check.EnterAssert()) \
+ { \
+ ENTER_DEBUG_ONLY_CODE; \
+ if (_check.Fail(_condition)) \
+ { \
+ _check.Setup(_message \
+ DEBUG_ARG(#_condition) \
+ DEBUG_ARG(__FILE__) \
+ DEBUG_ARG(__LINE__)); \
+ _check.Trigger(_reason); \
+ } \
+ LEAVE_DEBUG_ONLY_CODE; \
+ _check.LeaveAssert(); \
+ } \
+} while (0)
+
+// ex: ASSERT_CHECKF(1+2==4, "my reason", ("Woah %d", 1+3));
+// note that the double parenthesis, the 'args' param below will include one pair of parens.
+#define ASSERT_CHECKF(_condition, _reason, _args) \
+ ASSERT_CHECK(_condition, CHECK::FormatMessage _args, _reason)
+
+//--------------------------------------------------------------------------------
+// INVARIANTS are descriptions of conditions which are always true at well defined
+// points of execution. Invariants may be checked by the caller or callee at any
+// time as paranoia requires.
+//
+// There are really two flavors of invariant. The "public invariant" describes
+// to the caller invariant behavior about the abstraction which is visible from
+// the public API (and of course it should be expressible in that public API).
+//
+// The "internal invariant" (or representation invariant), on the other hand, is
+// a description of the private implementation of the abstraction, which may examine
+// internal state of the abstraction or use private entry points.
+//
+// Classes with invariants should introduce methods called
+// void Invariant();
+// and
+// void InternalInvariant();
+// to allow invariant checks.
+//--------------------------------------------------------------------------------
+
+#if CHECK_INVARIANTS
+
+template <typename TYPENAME>
+CHECK CheckInvariant(TYPENAME &obj)
+{
+#if defined(_MSC_VER) || defined(__llvm__)
+ __if_exists(TYPENAME::Invariant)
+ {
+ CHECK(obj.Invariant());
+ }
+ __if_exists(TYPENAME::InternalInvariant)
+ {
+ CHECK(obj.InternalInvariant());
+ }
+#endif
+
+ CHECK_OK;
+}
+
+#define CHECK_INVARIANT(o) \
+ ASSERT_CHECK(CheckInvariant(o), NULL, "Invariant failure")
+
+#else
+
+#define CHECK_INVARIANT(o) do { } while (0)
+
+#endif
+
+//--------------------------------------------------------------------------------
+// VALIDATE is a check to be made on an object type which identifies a pointer as
+// a valid instance of the object, by calling CheckPointer on it. Normally a null
+// pointer is treated as an error; VALIDATE_NULL (or CheckPointer(o, NULL_OK))
+// may be used when a null pointer is acceptible.
+//
+// In addition to the null/non-null check, a type may provide a specific Check method
+// for more sophisticated identification. In general, the Check method
+// should answer the question
+// "Is this a valid instance of its declared compile-time type?". For instance, if
+// runtype type identification were supported for the type, it should be invoked here.
+//
+// Note that CheckPointer will also check the invariant(s) if appropriate, so the
+// invariants should NOT be explicitly invoked from the Check method.
+//--------------------------------------------------------------------------------
+
+enum IsNullOK
+{
+ NULL_NOT_OK = 0,
+ NULL_OK = 1
+};
+
+#if CHECK_INVARIANTS
+template <typename TYPENAME>
+CHECK CheckPointer(TYPENAME *o, IsNullOK ok = NULL_NOT_OK)
+{
+ if (o == NULL)
+ {
+ CHECK_MSG(ok, "Illegal null pointer");
+ }
+ else
+ {
+#if defined(_MSC_VER) || defined(__llvm__)
+ __if_exists(TYPENAME::Check)
+ {
+ CHECK(o->Check());
+ }
+#endif
+ }
+
+ CHECK_OK;
+}
+
+template <typename TYPENAME>
+CHECK CheckValue(TYPENAME &val)
+{
+#if defined(_MSC_VER) || defined(__llvm__)
+ __if_exists(TYPENAME::Check)
+ {
+ CHECK(val.Check());
+ }
+#endif
+
+ CHECK(CheckInvariant(val));
+
+ CHECK_OK;
+}
+#else // CHECK_INVARIANTS
+
+#ifdef _DEBUG_IMPL
+// Don't defined these functions to be nops for the non-debug
+// build as it may hide important checks
+template <typename TYPENAME>
+CHECK CheckPointer(TYPENAME *o, IsNullOK ok = NULL_NOT_OK)
+{
+ if (o == NULL)
+ {
+ CHECK_MSG(ok, "Illegal null pointer");
+ }
+
+ CHECK_OK;
+}
+
+template <typename TYPENAME>
+CHECK CheckValue(TYPENAME &val)
+{
+ CHECK_OK;
+}
+#endif
+
+#endif // CHECK_INVARIANTS
+
+#if VALIDATE_OBJECTS
+
+#define VALIDATE(o) \
+ ASSERT_CHECK(CheckPointer(o), "Validation failure")
+#define VALIDATE_NULL(o) \
+ ASSERT_CHECK(CheckPointer(o, NULL_OK), "Validation failure")
+
+#else
+
+#define VALIDATE(o) do { } while (0)
+#define VALIDATE_NULL(o) do { } while (0)
+
+#endif
+
+//--------------------------------------------------------------------------------
+// CONSISTENCY_CHECKS are ad-hoc assertions about the expected state of the program
+// at a given time. A failure in one of these indicates a bug in the code.
+//
+// Note that the condition may either be a raw boolean expression or a CHECK result
+// returned from a Check routine.
+//--------------------------------------------------------------------------------
+
+#define CONSISTENCY_CHECK(_condition) \
+ CONSISTENCY_CHECK_MSG(_condition, "")
+
+#ifdef _DEBUG_IMPL
+
+#define CONSISTENCY_CHECK_MSG(_condition, _message) \
+ ASSERT_CHECK(_condition, _message, "Consistency check failed")
+
+#define CONSISTENCY_CHECK_MSGF(_condition, args) \
+ ASSERT_CHECKF(_condition, "Consistency check failed", args)
+
+#else
+
+#define CONSISTENCY_CHECK_MSG(_condition, _message) do { } while (0)
+#define CONSISTENCY_CHECK_MSGF(_condition, args) do { } while (0)
+
+#endif
+
+//--------------------------------------------------------------------------------
+// SIMPLIFYING_ASSUMPTIONS are workarounds which are placed in the code to allow progress
+// to be made in the case of difficult corner cases. These should NOT be left in the
+// code; they are really just markers of things which need to be fixed.
+//
+// Note that the condition may either be a raw boolean expression or a CHECK result
+// returned from a Check routine.
+//--------------------------------------------------------------------------------
+
+// Ex usage:
+// SIMPLIFYING_ASSUMPTION(SomeExpression());
+#define SIMPLIFYING_ASSUMPTION(_condition) \
+ SIMPLIFYING_ASSUMPTION_MSG(_condition, "")
+
+
+// Helper for HRs. Will provide formatted message showing the failure code.
+#define SIMPLIFYING_ASSUMPTION_SUCCEEDED(__hr) \
+ { \
+ HRESULT __hr2 = (__hr); \
+ (void)__hr2; \
+ SIMPLIFYING_ASSUMPTION_MSGF(SUCCEEDED(__hr2), ("HRESULT failed.\n Expected success.\n Actual=0x%x\n", __hr2)); \
+ }
+
+#ifdef _DEBUG_IMPL
+
+// Ex usage:
+// SIMPLIFYING_ASSUMPTION_MSG(SUCCEEDED(hr), "It failed!");
+#define SIMPLIFYING_ASSUMPTION_MSG(_condition, _message) \
+ ASSERT_CHECK(_condition, _message, "Unhandled special case detected")
+
+// use a formatted string. Ex usage:
+// SIMPLIFYING_ASSUMPTION_MSGF(SUCCEEDED(hr), ("Woah it failed! 0x%08x", hr));
+#define SIMPLIFYING_ASSUMPTION_MSGF(_condition, args) \
+ ASSERT_CHECKF(_condition, "Unhandled special case detected", args)
+
+#else // !_DEBUG_IMPL
+
+#define SIMPLIFYING_ASSUMPTION_MSG(_condition, _message) do { } while (0)
+#define SIMPLIFYING_ASSUMPTION_MSGF(_condition, args) do { } while (0)
+
+#endif // !_DEBUG_IMPL
+
+//--------------------------------------------------------------------------------
+// COMPILER_ASSUME_MSG is a statement that tells the compiler to assume the
+// condition is true. In a checked build these turn into asserts;
+// in a free build they are passed through to the compiler to use in optimization.
+//--------------------------------------------------------------------------------
+
+#if defined(_PREFAST_) || defined(_PREFIX_) || defined(__clang_analyzer__)
+#define COMPILER_ASSUME_MSG(_condition, _message) if (!(_condition)) __UNREACHABLE();
+#define COMPILER_ASSUME_MSGF(_condition, args) if (!(_condition)) __UNREACHABLE();
+#else
+
+#if defined(DACCESS_COMPILE)
+#define COMPILER_ASSUME_MSG(_condition, _message) do { } while (0)
+#define COMPILER_ASSUME_MSGF(_condition, args) do { } while (0)
+#else
+
+#if defined(_DEBUG)
+#define COMPILER_ASSUME_MSG(_condition, _message) \
+ ASSERT_CHECK(_condition, _message, "Compiler optimization assumption invalid")
+#define COMPILER_ASSUME_MSGF(_condition, args) \
+ ASSERT_CHECKF(_condition, "Compiler optimization assumption invalid", args)
+#else
+#define COMPILER_ASSUME_MSG(_condition, _message) __assume(_condition)
+#define COMPILER_ASSUME_MSGF(_condition, args) __assume(_condition)
+#endif // _DEBUG
+
+#endif // DACCESS_COMPILE
+
+#endif // _PREFAST_ || _PREFIX_
+
+
+#define COMPILER_ASSUME(_condition) \
+ COMPILER_ASSUME_MSG(_condition, "")
+
+
+//--------------------------------------------------------------------------------
+// PREFIX_ASSUME_MSG and PREFAST_ASSUME_MSG are just another name
+// for COMPILER_ASSUME_MSG
+// In a checked build these turn into asserts; in a free build
+// they are passed through to the compiler to use in optimization;
+// via an __assume(_condition) optimization hint.
+//--------------------------------------------------------------------------------
+
+#define PREFIX_ASSUME_MSG(_condition, _message) \
+ COMPILER_ASSUME_MSG(_condition, _message)
+
+#define PREFIX_ASSUME_MSGF(_condition, args) \
+ COMPILER_ASSUME_MSGF(_condition, args)
+
+#define PREFIX_ASSUME(_condition) \
+ COMPILER_ASSUME_MSG(_condition, "")
+
+#define PREFAST_ASSUME_MSG(_condition, _message) \
+ COMPILER_ASSUME_MSG(_condition, _message)
+
+#define PREFAST_ASSUME_MSGF(_condition, args) \
+ COMPILER_ASSUME_MSGF(_condition, args)
+
+#define PREFAST_ASSUME(_condition) \
+ COMPILER_ASSUME_MSG(_condition, "")
+
+//--------------------------------------------------------------------------------
+// UNREACHABLE points are locations in the code which should not be able to be
+// reached under any circumstances (e.g. a default in a switch which is supposed to
+// cover all cases.). This macro tells the compiler this, and also embeds a check
+// to make sure it is always true.
+//--------------------------------------------------------------------------------
+
+#define UNREACHABLE() \
+ UNREACHABLE_MSG("")
+
+#ifdef __llvm__
+
+// LLVM complains if a function does not return what it says.
+#define UNREACHABLE_RET() do { UNREACHABLE(); return 0; } while (0)
+#define UNREACHABLE_MSG_RET(_message) UNREACHABLE_MSG(_message); return 0;
+
+#else // __llvm__
+
+#define UNREACHABLE_RET() UNREACHABLE()
+#define UNREACHABLE_MSG_RET(_message) UNREACHABLE_MSG(_message)
+
+#endif // __llvm__ else
+
+#ifdef _DEBUG_IMPL
+
+// Note that the "do { } while (0)" syntax trick here doesn't work, as the compiler
+// gives an error that the while(0) is unreachable code
+#define UNREACHABLE_MSG(_message) \
+{ \
+ CHECK _check; \
+ _check.Setup(_message, "<unreachable>", __FILE__, __LINE__); \
+ _check.Trigger("Reached the \"unreachable\""); \
+} __UNREACHABLE()
+
+#else
+
+#define UNREACHABLE_MSG(_message) __UNREACHABLE()
+
+#endif
+
+
+//--------------------------------------------------------------------------------
+// STRESS_CHECK represents a check which is included in a free build
+// @todo: behavior on trigger
+//
+// Note that the condition may either be a raw boolean expression or a CHECK result
+// returned from a Check routine.
+//
+// Since Retail builds don't allow formatted checks, there's no STRESS_CHECK_MSGF.
+//--------------------------------------------------------------------------------
+
+#if CHECK_STRESS
+
+#define STRESS_CHECK(_condition, _message) \
+ ASSERT_CHECK(_condition, _message, "Stress Assertion Failure")
+
+#else
+
+#define STRESS_CHECK(_condition, _message) do { } while (0)
+
+#endif
+
+//--------------------------------------------------------------------------------
+// CONTRACT_CHECK is used to put contracts on Check function. Note that it does
+// not support postconditions.
+//--------------------------------------------------------------------------------
+
+#define CONTRACT_CHECK CONTRACTL
+#define CONTRACT_CHECK_END CONTRACTL_END
+
+//--------------------------------------------------------------------------------
+// CCHECK is used for Check functions which may fail due to out of memory
+// or other transient failures. These failures should be ignored when doing
+// assertions, but they cannot be ignored when the Check function is used in
+// normal code.
+// @todo: really crufty to have 2 sets of CHECK macros
+//--------------------------------------------------------------------------------
+
+#ifdef _DEBUG
+
+#define CCHECK_START \
+ { \
+ BOOL ___exception = FALSE; \
+ BOOL ___transient = FALSE; \
+ CHECK ___result = CHECK::OK(); \
+ EX_TRY {
+
+#define CCHECK_END \
+ } EX_CATCH { \
+ if (___result.IsInAssert()) \
+ { \
+ ___exception = TRUE; \
+ ___transient = GET_EXCEPTION()->IsTransient(); \
+ } \
+ else \
+ EX_RETHROW; \
+ } EX_END_CATCH(RethrowTerminalExceptions); \
+ \
+ if (___exception) \
+ { \
+ if (___transient) \
+ CHECK_OK; \
+ else \
+ CHECK_FAIL("Nontransient exception occurred during check"); \
+ } \
+ CHECK(___result); \
+ }
+
+#define CRETURN_RESULT(r) ___result = r
+
+#define CCHECK_MSG(_condition, _message) \
+ CHECK_MSG_EX(_condition, _message, CRETURN_RESULT)
+
+#define CCHECK(_condition) \
+ CCHECK_MSG(_condition, "")
+
+#define CCHECK_MSGF(_condition, _args) \
+ CCHECK_MSG(_condition, CHECK::FormatMessage _args)
+
+#define CCHECK_FAIL(_message) \
+ CCHECK_MSG(FALSE, _message); UNREACHABLE()
+
+#define CCHECK_FAILF(_args) \
+ CCHECK_MSGF(FALSE, _args); UNREACHABLE()
+
+#else // _DEBUG
+
+#define CCHECK_START
+#define CCHECK_END
+
+#define CCHECK CHECK
+#define CCHECK_MSG CHECK_MSG
+#define CCHECK_MSGF CHECK_MSGF
+#define CCHECK_FAIL CHECK_FAIL
+#define CCHECK_FAILF CHECK_FAILF
+
+#endif
+
+
+
+//--------------------------------------------------------------------------------
+// Common base level checks
+//--------------------------------------------------------------------------------
+
+CHECK CheckAlignment(UINT alignment);
+
+CHECK CheckAligned(UINT value, UINT alignment);
+#if defined(_MSC_VER)
+CHECK CheckAligned(ULONG value, UINT alignment);
+#endif
+CHECK CheckAligned(UINT64 value, UINT alignment);
+CHECK CheckAligned(const void *address, UINT alignment);
+
+CHECK CheckOverflow(UINT value1, UINT value2);
+#if defined(_MSC_VER)
+CHECK CheckOverflow(ULONG value1, ULONG value2);
+#endif
+CHECK CheckOverflow(UINT64 value1, UINT64 value2);
+CHECK CheckOverflow(PTR_CVOID address, UINT offset);
+#if defined(_MSC_VER)
+CHECK CheckOverflow(const void *address, ULONG offset);
+#endif
+CHECK CheckOverflow(const void *address, UINT64 offset);
+
+CHECK CheckUnderflow(UINT value1, UINT value2);
+#if defined(_MSC_VER)
+CHECK CheckUnderflow(ULONG value1, ULONG value2);
+#endif
+CHECK CheckUnderflow(UINT64 value1, UINT64 value2);
+CHECK CheckUnderflow(const void *address, UINT offset);
+#if defined(_MSC_VER)
+CHECK CheckUnderflow(const void *address, ULONG offset);
+#endif
+CHECK CheckUnderflow(const void *address, UINT64 offset);
+CHECK CheckUnderflow(const void *address, void *address2);
+
+CHECK CheckZeroedMemory(const void *memory, SIZE_T size);
+
+// These include overflow checks
+CHECK CheckBounds(const void *rangeBase, UINT32 rangeSize, UINT32 offset);
+CHECK CheckBounds(const void *rangeBase, UINT32 rangeSize, UINT32 offset, UINT32 size);
+
+void WINAPI ReleaseCheckTls(LPVOID pTlsData);
+
+// ================================================================================
+// Inline definitions
+// ================================================================================
+
+#include "check.inl"
+
+#endif // CHECK_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef CHECK_INL_
+#define CHECK_INL_
+
+#include "check.h"
+#include "clrhost.h"
+#include "debugmacros.h"
+#include "clrtypes.h"
+
+FORCEINLINE BOOL CHECK::EnterAssert()
+{
+ if (s_neverEnforceAsserts)
+ return FALSE;
+
+#ifdef _DEBUG_IMPL
+ m_pCount = &t_count;
+
+ if (!*m_pCount)
+ {
+ *m_pCount = 1;
+ return TRUE;
+ }
+ else
+ return FALSE;
+#else
+ // Don't bother doing recursive checks on a free build, since checks should
+ // be extremely isolated
+ return TRUE;
+#endif
+}
+
+FORCEINLINE void CHECK::LeaveAssert()
+{
+#ifdef _DEBUG_IMPL
+ *m_pCount = 0;
+#endif
+}
+
+FORCEINLINE BOOL CHECK::IsInAssert()
+{
+#ifdef _DEBUG_IMPL
+ if (!m_pCount)
+ m_pCount = &t_count;
+
+ return *m_pCount;
+#else
+ return FALSE;
+#endif
+}
+
+FORCEINLINE BOOL CHECK::EnforceAssert()
+{
+ if (s_neverEnforceAsserts)
+ return FALSE;
+ else
+ {
+ CHECK chk;
+ return !chk.IsInAssert();
+ }
+}
+
+FORCEINLINE void CHECK::ResetAssert()
+{
+ CHECK chk;
+ if (chk.IsInAssert())
+ chk.LeaveAssert();
+}
+
+inline void CHECK::SetAssertEnforcement(BOOL value)
+{
+ s_neverEnforceAsserts = !value;
+}
+
+// Fail records the result of a condition check. Can take either a
+// boolean value or another check result
+FORCEINLINE BOOL CHECK::Fail(BOOL condition)
+{
+#ifdef _DEBUG
+ if (!condition)
+ {
+ m_condition = NULL;
+ m_file = NULL;
+ m_line = 0;
+ }
+#endif
+ return !condition;
+}
+
+FORCEINLINE BOOL CHECK::Fail(const CHECK &check)
+{
+ m_message = check.m_message;
+#ifdef _DEBUG
+ if (m_message != NULL)
+ {
+ m_condition = check.m_condition;
+ m_file = check.m_file;
+ m_line = check.m_line;
+ }
+#endif
+ return m_message != NULL;
+}
+
+#ifndef _DEBUG
+FORCEINLINE void CHECK::Setup(LPCSTR message)
+{
+ m_message = message;
+}
+
+FORCEINLINE LPCSTR CHECK::FormatMessage(LPCSTR messageFormat, ...)
+{
+ return messageFormat;
+}
+#endif
+
+FORCEINLINE CHECK::operator BOOL ()
+{
+ return m_message == NULL;
+}
+
+FORCEINLINE BOOL CHECK::operator!()
+{
+ return m_message != NULL;
+}
+
+inline CHECK CheckAlignment(UINT alignment)
+{
+ STATIC_CONTRACT_WRAPPER;
+ CHECK((alignment & (alignment-1)) == 0);
+ CHECK_OK;
+}
+
+inline CHECK CheckAligned(UINT value, UINT alignment)
+{
+ STATIC_CONTRACT_WRAPPER;
+ CHECK(AlignmentTrim(value, alignment) == 0);
+ CHECK_OK;
+}
+
+#ifndef HOST_UNIX
+// For Unix this and the previous function get the same types.
+// So, exclude this one.
+inline CHECK CheckAligned(ULONG value, UINT alignment)
+{
+ STATIC_CONTRACT_WRAPPER;
+ CHECK(AlignmentTrim(value, alignment) == 0);
+ CHECK_OK;
+}
+#endif // HOST_UNIX
+
+inline CHECK CheckAligned(UINT64 value, UINT alignment)
+{
+ STATIC_CONTRACT_WRAPPER;
+ CHECK(AlignmentTrim(value, alignment) == 0);
+ CHECK_OK;
+}
+
+inline CHECK CheckAligned(const void *address, UINT alignment)
+{
+ STATIC_CONTRACT_WRAPPER;
+ CHECK(AlignmentTrim((SIZE_T)address, alignment) == 0);
+ CHECK_OK;
+}
+
+inline CHECK CheckOverflow(UINT value1, UINT value2)
+{
+ CHECK(value1 + value2 >= value1);
+ CHECK_OK;
+}
+
+#if defined(_MSC_VER)
+inline CHECK CheckOverflow(ULONG value1, ULONG value2)
+{
+ CHECK(value1 + value2 >= value1);
+ CHECK_OK;
+}
+#endif
+
+inline CHECK CheckOverflow(UINT64 value1, UINT64 value2)
+{
+ CHECK(value1 + value2 >= value1);
+ CHECK_OK;
+}
+
+inline CHECK CheckOverflow(PTR_CVOID address, UINT offset)
+{
+ TADDR targetAddr = dac_cast<TADDR>(address);
+#if POINTER_BITS == 32
+ CHECK((UINT) (SIZE_T)(targetAddr) + offset >= (UINT) (SIZE_T) (targetAddr));
+#else
+ CHECK((UINT64) targetAddr + offset >= (UINT64) targetAddr);
+#endif
+
+ CHECK_OK;
+}
+
+#if defined(_MSC_VER)
+inline CHECK CheckOverflow(const void *address, ULONG offset)
+{
+#if POINTER_BITS == 32
+ CHECK((ULONG) (SIZE_T) address + offset >= (ULONG) (SIZE_T) address);
+#else
+ CHECK((UINT64) address + offset >= (UINT64) address);
+#endif
+
+ CHECK_OK;
+}
+#endif
+
+inline CHECK CheckOverflow(const void *address, UINT64 offset)
+{
+#if POINTER_BITS == 32
+ CHECK(offset >> 32 == 0);
+ CHECK((UINT) (SIZE_T) address + (UINT) offset >= (UINT) (SIZE_T) address);
+#else
+ CHECK((UINT64) address + offset >= (UINT64) address);
+#endif
+
+ CHECK_OK;
+}
+
+
+inline CHECK CheckUnderflow(UINT value1, UINT value2)
+{
+ CHECK(value1 - value2 <= value1);
+
+ CHECK_OK;
+}
+
+#ifndef HOST_UNIX
+// For Unix this and the previous function get the same types.
+// So, exclude this one.
+inline CHECK CheckUnderflow(ULONG value1, ULONG value2)
+{
+ CHECK(value1 - value2 <= value1);
+
+ CHECK_OK;
+}
+#endif // HOST_UNIX
+
+inline CHECK CheckUnderflow(UINT64 value1, UINT64 value2)
+{
+ CHECK(value1 - value2 <= value1);
+
+ CHECK_OK;
+}
+
+inline CHECK CheckUnderflow(const void *address, UINT offset)
+{
+#if POINTER_BITS == 32
+ CHECK((UINT) (SIZE_T) address - offset <= (UINT) (SIZE_T) address);
+#else
+ CHECK((UINT64) address - offset <= (UINT64) address);
+#endif
+
+ CHECK_OK;
+}
+
+#if defined(_MSC_VER)
+inline CHECK CheckUnderflow(const void *address, ULONG offset)
+{
+#if POINTER_BITS == 32
+ CHECK((ULONG) (SIZE_T) address - offset <= (ULONG) (SIZE_T) address);
+#else
+ CHECK((UINT64) address - offset <= (UINT64) address);
+#endif
+
+ CHECK_OK;
+}
+#endif
+
+inline CHECK CheckUnderflow(const void *address, UINT64 offset)
+{
+#if POINTER_BITS == 32
+ CHECK(offset >> 32 == 0);
+ CHECK((UINT) (SIZE_T) address - (UINT) offset <= (UINT) (SIZE_T) address);
+#else
+ CHECK((UINT64) address - offset <= (UINT64) address);
+#endif
+
+ CHECK_OK;
+}
+
+inline CHECK CheckUnderflow(const void *address, void *address2)
+{
+#if POINTER_BITS == 32
+ CHECK((UINT) (SIZE_T) address - (UINT) (SIZE_T) address2 <= (UINT) (SIZE_T) address);
+#else
+ CHECK((UINT64) address - (UINT64) address2 <= (UINT64) address);
+#endif
+
+ CHECK_OK;
+}
+
+inline CHECK CheckZeroedMemory(const void *memory, SIZE_T size)
+{
+ CHECK(CheckOverflow(memory, size));
+
+ BYTE *p = (BYTE *) memory;
+ BYTE *pEnd = p + size;
+
+ while (p < pEnd)
+ CHECK(*p++ == 0);
+
+ CHECK_OK;
+}
+
+inline CHECK CheckBounds(const void *rangeBase, UINT32 rangeSize, UINT32 offset)
+{
+ CHECK(CheckOverflow(dac_cast<PTR_CVOID>(rangeBase), rangeSize));
+ CHECK(offset <= rangeSize);
+ CHECK_OK;
+}
+
+inline CHECK CheckBounds(const void *rangeBase, UINT32 rangeSize, UINT32 offset, UINT32 size)
+{
+ CHECK(CheckOverflow(dac_cast<PTR_CVOID>(rangeBase), rangeSize));
+ CHECK(CheckOverflow(offset, size));
+ CHECK(offset + size <= rangeSize);
+ CHECK_OK;
+}
+
+#endif // CHECK_INL_
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+
+
+#ifndef __CLRHOST_H__
+#define __CLRHOST_H__
+
+#include "windows.h" // worth to include before mscoree.h so we are guaranteed to pick few definitions
+#ifdef CreateSemaphore
+#undef CreateSemaphore
+#endif
+#include "mscoree.h"
+#include "clrinternal.h"
+#include "switches.h"
+#include "holder.h"
+#include "new.hpp"
+#include "staticcontract.h"
+#include "predeftlsslot.h"
+#include "safemath.h"
+#include "debugreturn.h"
+#include "yieldprocessornormalized.h"
+
+#if !defined(_DEBUG_IMPL) && defined(_DEBUG) && !defined(DACCESS_COMPILE)
+#define _DEBUG_IMPL 1
+#endif
+
+#define BEGIN_PRESERVE_LAST_ERROR \
+ { \
+ DWORD __dwLastError = ::GetLastError(); \
+ DEBUG_ASSURE_NO_RETURN_BEGIN(PRESERVE_LAST_ERROR); \
+ {
+
+#define END_PRESERVE_LAST_ERROR \
+ } \
+ DEBUG_ASSURE_NO_RETURN_END(PRESERVE_LAST_ERROR); \
+ ::SetLastError(__dwLastError); \
+ }
+
+//
+// TRASH_LASTERROR macro sets bogus last error in debug builds to help find places that fail to save it
+//
+#ifdef _DEBUG
+
+#define LAST_ERROR_TRASH_VALUE 42424 /* = 0xa5b8 */
+
+#define TRASH_LASTERROR \
+ SetLastError(LAST_ERROR_TRASH_VALUE)
+
+#else // _DEBUG
+
+#define TRASH_LASTERROR
+
+#endif // _DEBUG
+
+
+LPVOID ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
+BOOL ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
+SIZE_T ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength);
+BOOL ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
+
+#ifdef HOST_WINDOWS
+HANDLE ClrGetProcessExecutableHeap();
+#endif
+
+#ifdef FAILPOINTS_ENABLED
+extern int RFS_HashStack();
+#endif
+
+// Critical section support for CLR DLLs other than the the EE.
+// Include the header defining each Crst type and its corresponding level (relative rank). This is
+// auto-generated from a tool that takes a high-level description of each Crst type and its dependencies.
+#include "crsttypes.h"
+
+// critical section api
+CRITSEC_COOKIE ClrCreateCriticalSection(CrstType type, CrstFlags flags);
+void ClrDeleteCriticalSection(CRITSEC_COOKIE cookie);
+void ClrEnterCriticalSection(CRITSEC_COOKIE cookie);
+void ClrLeaveCriticalSection(CRITSEC_COOKIE cookie);
+
+// Rather than use the above APIs directly, it is recommended that holder classes
+// be used. This guarantees that the locks will be vacated when the scope is popped,
+// either on exception or on return.
+
+typedef Holder<CRITSEC_COOKIE, ClrEnterCriticalSection, ClrLeaveCriticalSection, NULL> CRITSEC_Holder;
+
+// Use this holder to manage CRITSEC_COOKIE allocation to ensure it will be released if anything goes wrong
+FORCEINLINE void VoidClrDeleteCriticalSection(CRITSEC_COOKIE cs) { if (cs != NULL) ClrDeleteCriticalSection(cs); }
+typedef Wrapper<CRITSEC_COOKIE, DoNothing<CRITSEC_COOKIE>, VoidClrDeleteCriticalSection, NULL> CRITSEC_AllocationHolder;
+
+DWORD GetClrModulePathName(SString& buffer);
+
+extern thread_local int t_CantAllocCount;
+
+inline void IncCantAllocCount()
+{
+ t_CantAllocCount++;
+}
+
+inline void DecCantAllocCount()
+{
+ t_CantAllocCount--;
+}
+
+class CantAllocHolder
+{
+public:
+ CantAllocHolder ()
+ {
+ IncCantAllocCount ();
+ }
+ ~CantAllocHolder()
+ {
+ DecCantAllocCount ();
+ }
+};
+
+// At places where want to allocate stress log, we need to first check if we are allowed to do so.
+inline bool IsInCantAllocRegion ()
+{
+ return t_CantAllocCount != 0;
+}
+inline BOOL IsInCantAllocStressLogRegion()
+{
+ return t_CantAllocCount != 0;
+}
+
+#endif
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#ifndef CLRNT_H_
+#define CLRNT_H_
+
+#include "staticcontract.h"
+
+//
+// This file is the result of some changes to the SDK header files.
+// In particular, nt.h and some of its dependencies are no longer
+// available except as "nonship" files. As a result, this file
+// was created as a simple cut and past of structures and functions
+// from NT that are either not yet documented or have been overlooked
+// as being part of the platform SDK.
+//
+
+//
+// ALL PLATFORMS
+//
+
+#define STATUS_INVALID_PARAMETER_3 ((NTSTATUS)0xC00000F1L)
+#define STATUS_INVALID_PARAMETER_4 ((NTSTATUS)0xC00000F2L)
+#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
+#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+
+#ifndef STATUS_UNWIND
+#define STATUS_UNWIND ((NTSTATUS)0x80000027L)
+#endif
+
+#ifndef DBG_PRINTEXCEPTION_C
+#define DBG_PRINTEXCEPTION_C ((DWORD)0x40010006L)
+#endif
+
+#ifndef STATUS_UNWIND_CONSOLIDATE
+#define STATUS_UNWIND_CONSOLIDATE ((NTSTATUS)0x80000029L)
+#endif
+
+#ifndef STATUS_LONGJUMP
+#define STATUS_LONGJUMP ((NTSTATUS)0x80000026L)
+#endif
+
+#ifndef LOCALE_NAME_MAX_LENGTH
+#define LOCALE_NAME_MAX_LENGTH 85
+#endif // !LOCALE_NAME_MAX_LENGTH
+
+#ifndef SUBLANG_CUSTOM_DEFAULT
+#define SUBLANG_CUSTOM_DEFAULT 0x03 // default custom language/locale
+#define SUBLANG_CUSTOM_UNSPECIFIED 0x04 // custom language/locale
+#define LOCALE_CUSTOM_DEFAULT \
+ (MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT), SORT_DEFAULT))
+#define LOCALE_CUSTOM_UNSPECIFIED \
+ (MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED), SORT_DEFAULT))
+#endif // !SUBLANG_CUSTOM_DEFAULT
+
+#ifndef __out_xcount_opt
+#define __out_xcount_opt(var) __out
+#endif
+
+#ifndef __encoded_pointer
+#define __encoded_pointer
+#endif
+
+#ifndef __range
+#define __range(min, man)
+#endif
+
+#ifndef __field_bcount
+#define __field_bcount(size)
+#endif
+
+#ifndef __field_ecount_opt
+#define __field_ecount_opt(nFields)
+#endif
+
+#ifndef __field_ecount
+#define __field_ecount(EHCount)
+#endif
+
+#undef _Ret_bytecap_
+#define _Ret_bytecap_(_Size)
+
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+#endif
+
+#define ARGUMENT_PRESENT(ArgumentPointer) (\
+ (CHAR *)(ArgumentPointer) != (CHAR *)(NULL) )
+
+#define EXCEPTION_CHAIN_END ((PEXCEPTION_REGISTRATION_RECORD)-1)
+
+typedef signed char SCHAR;
+typedef SCHAR *PSCHAR;
+typedef LONG NTSTATUS;
+
+#ifndef HOST_UNIX
+
+#define TLS_MINIMUM_AVAILABLE 64 // winnt
+#define TLS_EXPANSION_SLOTS 1024
+
+typedef enum _THREADINFOCLASS {
+ ThreadBasicInformation,
+ ThreadTimes,
+ ThreadPriority,
+ ThreadBasePriority,
+ ThreadAffinityMask,
+ ThreadImpersonationToken,
+ ThreadDescriptorTableEntry,
+ ThreadEnableAlignmentFaultFixup,
+ ThreadEventPair_Reusable,
+ ThreadQuerySetWin32StartAddress,
+ ThreadZeroTlsCell,
+ ThreadPerformanceCount,
+ ThreadAmILastThread,
+ ThreadIdealProcessor,
+ ThreadPriorityBoost,
+ ThreadSetTlsArrayAddress,
+ ThreadIsIoPending,
+ ThreadHideFromDebugger,
+ ThreadBreakOnTermination,
+ MaxThreadInfoClass
+ } THREADINFOCLASS;
+
+typedef enum _SYSTEM_INFORMATION_CLASS {
+ SystemBasicInformation,
+ SystemProcessorInformation, // obsolete...delete
+ SystemPerformanceInformation,
+ SystemTimeOfDayInformation,
+ SystemPathInformation,
+ SystemProcessInformation,
+ SystemCallCountInformation,
+ SystemDeviceInformation,
+ SystemProcessorPerformanceInformation,
+ SystemFlagsInformation,
+ SystemCallTimeInformation,
+ SystemModuleInformation,
+ SystemLocksInformation,
+ SystemStackTraceInformation,
+ SystemPagedPoolInformation,
+ SystemNonPagedPoolInformation,
+ SystemHandleInformation,
+ SystemObjectInformation,
+ SystemPageFileInformation,
+ SystemVdmInstemulInformation,
+ SystemVdmBopInformation,
+ SystemFileCacheInformation,
+ SystemPoolTagInformation,
+ SystemInterruptInformation,
+ SystemDpcBehaviorInformation,
+ SystemFullMemoryInformation,
+ SystemLoadGdiDriverInformation,
+ SystemUnloadGdiDriverInformation,
+ SystemTimeAdjustmentInformation,
+ SystemSummaryMemoryInformation,
+ SystemMirrorMemoryInformation,
+ SystemPerformanceTraceInformation,
+ SystemObsolete0,
+ SystemExceptionInformation,
+ SystemCrashDumpStateInformation,
+ SystemKernelDebuggerInformation,
+ SystemContextSwitchInformation,
+ SystemRegistryQuotaInformation,
+ SystemExtendServiceTableInformation,
+ SystemPrioritySeperation,
+ SystemVerifierAddDriverInformation,
+ SystemVerifierRemoveDriverInformation,
+ SystemProcessorIdleInformation,
+ SystemLegacyDriverInformation,
+ SystemCurrentTimeZoneInformation,
+ SystemLookasideInformation,
+ SystemTimeSlipNotification,
+ SystemSessionCreate,
+ SystemSessionDetach,
+ SystemSessionInformation,
+ SystemRangeStartInformation,
+ SystemVerifierInformation,
+ SystemVerifierThunkExtend,
+ SystemSessionProcessInformation,
+ SystemLoadGdiDriverInSystemSpace,
+ SystemNumaProcessorMap,
+ SystemPrefetcherInformation,
+ SystemExtendedProcessInformation,
+ SystemRecommendedSharedDataAlignment,
+ SystemComPlusPackage,
+ SystemNumaAvailableMemory,
+ SystemProcessorPowerInformation,
+ SystemEmulationBasicInformation,
+ SystemEmulationProcessorInformation,
+ SystemExtendedHandleInformation,
+ SystemLostDelayedWriteInformation
+} SYSTEM_INFORMATION_CLASS;
+
+typedef enum _EVENT_INFORMATION_CLASS {
+ EventBasicInformation
+ } EVENT_INFORMATION_CLASS;
+
+typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
+ LARGE_INTEGER IdleTime;
+ LARGE_INTEGER KernelTime;
+ LARGE_INTEGER UserTime;
+ LARGE_INTEGER DpcTime; // DEVL only
+ LARGE_INTEGER InterruptTime; // DEVL only
+ ULONG InterruptCount;
+} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
+
+typedef enum _EVENT_TYPE {
+ NotificationEvent,
+ SynchronizationEvent
+ } EVENT_TYPE;
+
+typedef struct _EVENT_BASIC_INFORMATION {
+ EVENT_TYPE EventType;
+ LONG EventState;
+} EVENT_BASIC_INFORMATION, *PEVENT_BASIC_INFORMATION;
+
+#define RTL_MEG (1024UL * 1024UL)
+#define RTLP_IMAGE_MAX_DOS_HEADER ( 256UL * RTL_MEG)
+
+typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION {
+ BOOLEAN KernelDebuggerEnabled;
+ BOOLEAN KernelDebuggerNotPresent;
+} SYSTEM_KERNEL_DEBUGGER_INFORMATION, *PSYSTEM_KERNEL_DEBUGGER_INFORMATION;
+
+typedef struct _STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+#ifdef MIDL_PASS
+ [size_is(MaximumLength), length_is(Length) ]
+#endif // MIDL_PASS
+ PCHAR Buffer;
+} STRING;
+typedef STRING *PSTRING;
+
+typedef STRING ANSI_STRING;
+typedef PSTRING PANSI_STRING;
+
+typedef STRING OEM_STRING;
+typedef PSTRING POEM_STRING;
+typedef CONST STRING* PCOEM_STRING;
+
+typedef struct _UNICODE_STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+#ifdef MIDL_PASS
+ [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
+#else // MIDL_PASS
+ PWSTR Buffer;
+#endif // MIDL_PASS
+} UNICODE_STRING;
+typedef UNICODE_STRING *PUNICODE_STRING;
+typedef const UNICODE_STRING *PCUNICODE_STRING;
+#define UNICODE_NULL ((WCHAR)0) // winnt
+
+typedef struct _STRING32 {
+ USHORT Length;
+ USHORT MaximumLength;
+ ULONG Buffer;
+} STRING32;
+typedef STRING32 *PSTRING32;
+
+typedef STRING32 UNICODE_STRING32;
+typedef UNICODE_STRING32 *PUNICODE_STRING32;
+
+typedef STRING32 ANSI_STRING32;
+typedef ANSI_STRING32 *PANSI_STRING32;
+
+
+typedef struct _STRING64 {
+ USHORT Length;
+ USHORT MaximumLength;
+ ULONGLONG Buffer;
+} STRING64;
+typedef STRING64 *PSTRING64;
+
+typedef STRING64 UNICODE_STRING64;
+typedef UNICODE_STRING64 *PUNICODE_STRING64;
+
+typedef STRING64 ANSI_STRING64;
+typedef ANSI_STRING64 *PANSI_STRING64;
+
+#define GDI_HANDLE_BUFFER_SIZE32 34
+#define GDI_HANDLE_BUFFER_SIZE64 60
+
+#if !defined(TARGET_AMD64)
+#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32
+#else
+#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE64
+#endif
+
+typedef ULONG GDI_HANDLE_BUFFER32[GDI_HANDLE_BUFFER_SIZE32];
+typedef ULONG GDI_HANDLE_BUFFER64[GDI_HANDLE_BUFFER_SIZE64];
+typedef ULONG GDI_HANDLE_BUFFER [GDI_HANDLE_BUFFER_SIZE ];
+
+
+typedef struct _PEB_LDR_DATA {
+ ULONG Length;
+ BOOLEAN Initialized;
+ HANDLE SsHandle;
+ LIST_ENTRY InLoadOrderModuleList;
+ LIST_ENTRY InMemoryOrderModuleList;
+ LIST_ENTRY InInitializationOrderModuleList;
+ PVOID EntryInProgress;
+} PEB_LDR_DATA, *PPEB_LDR_DATA;
+
+typedef struct _PEB_FREE_BLOCK {
+ struct _PEB_FREE_BLOCK *Next;
+ ULONG Size;
+} PEB_FREE_BLOCK, *PPEB_FREE_BLOCK;
+
+typedef PVOID* PPVOID;
+
+typedef
+VOID
+(*PPS_POST_PROCESS_INIT_ROUTINE) (
+ VOID
+ );
+
+typedef struct _LDR_DATA_TABLE_ENTRY {
+ LIST_ENTRY InLoadOrderLinks;
+ LIST_ENTRY InMemoryOrderLinks;
+ LIST_ENTRY InInitializationOrderLinks;
+ PVOID DllBase;
+ PVOID EntryPoint;
+ ULONG SizeOfImage;
+ UNICODE_STRING FullDllName;
+ UNICODE_STRING BaseDllName;
+ ULONG Flags;
+ USHORT LoadCount;
+ USHORT TlsIndex;
+ union _foo {
+ LIST_ENTRY HashLinks;
+ struct _bar {
+ PVOID SectionPointer;
+ ULONG CheckSum;
+ };
+ };
+ union _foo2 {
+ struct _bar2 {
+ ULONG TimeDateStamp;
+ };
+ struct _bar3 {
+ PVOID LoadedImports;
+ };
+ };
+ PVOID EntryPointActivationContext;
+} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
+
+#define TYPE3(arg) arg
+
+typedef struct _PEB {
+ BOOLEAN InheritedAddressSpace; // These four fields cannot change unless the
+ BOOLEAN ReadImageFileExecOptions; //
+ BOOLEAN BeingDebugged; //
+ BOOLEAN SpareBool; //
+ HANDLE Mutant; // INITIAL_PEB structure is also updated.
+
+ PVOID ImageBaseAddress;
+ PPEB_LDR_DATA Ldr;
+ TYPE3(struct _RTL_USER_PROCESS_PARAMETERS*) ProcessParameters;
+ PVOID SubSystemData;
+ PVOID ProcessHeap;
+ TYPE3(struct _RTL_CRITICAL_SECTION*) FastPebLock;
+ PVOID FastPebLockRoutine;
+ PVOID FastPebUnlockRoutine;
+ ULONG EnvironmentUpdateCount;
+ PVOID KernelCallbackTable;
+ ULONG SystemReserved[1];
+
+ struct _foo {
+ ULONG ExecuteOptions : 2;
+ ULONG SpareBits : 30;
+ };
+
+
+ PPEB_FREE_BLOCK FreeList;
+ ULONG TlsExpansionCounter;
+ PVOID TlsBitmap;
+ ULONG TlsBitmapBits[2]; // TLS_MINIMUM_AVAILABLE bits
+ PVOID ReadOnlySharedMemoryBase;
+ PVOID ReadOnlySharedMemoryHeap;
+ PPVOID ReadOnlyStaticServerData;
+ PVOID AnsiCodePageData;
+ PVOID OemCodePageData;
+ PVOID UnicodeCaseTableData;
+
+ //
+ // Useful information for LdrpInitialize
+ ULONG NumberOfProcessors;
+ ULONG NtGlobalFlag;
+
+ //
+ // Passed up from MmCreatePeb from Session Manager registry key
+ //
+
+ LARGE_INTEGER CriticalSectionTimeout;
+ SIZE_T HeapSegmentReserve;
+ SIZE_T HeapSegmentCommit;
+ SIZE_T HeapDeCommitTotalFreeThreshold;
+ SIZE_T HeapDeCommitFreeBlockThreshold;
+
+ //
+ // Where heap manager keeps track of all heaps created for a process
+ // Fields initialized by MmCreatePeb. ProcessHeaps is initialized
+ // to point to the first free byte after the PEB and MaximumNumberOfHeaps
+ // is computed from the page size used to hold the PEB, less the fixed
+ // size of this data structure.
+ //
+
+ ULONG NumberOfHeaps;
+ ULONG MaximumNumberOfHeaps;
+ PPVOID ProcessHeaps;
+
+ //
+ //
+ PVOID GdiSharedHandleTable;
+ PVOID ProcessStarterHelper;
+ ULONG GdiDCAttributeList;
+ PVOID LoaderLock;
+
+ //
+ // Following fields filled in by MmCreatePeb from system values and/or
+ // image header.
+ //
+
+ ULONG OSMajorVersion;
+ ULONG OSMinorVersion;
+ USHORT OSBuildNumber;
+ USHORT OSCSDVersion;
+ ULONG OSPlatformId;
+ ULONG ImageSubsystem;
+ ULONG ImageSubsystemMajorVersion;
+ ULONG ImageSubsystemMinorVersion;
+ ULONG_PTR ImageProcessAffinityMask;
+ GDI_HANDLE_BUFFER GdiHandleBuffer;
+ PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
+
+ PVOID TlsExpansionBitmap;
+ ULONG TlsExpansionBitmapBits[32]; // TLS_EXPANSION_SLOTS bits
+
+ //
+ // Id of the Hydra session in which this process is running
+ //
+ ULONG SessionId;
+
+ //
+ // Filled in by LdrpInstallAppcompatBackend
+ //
+ ULARGE_INTEGER AppCompatFlags;
+
+ //
+ // ntuser appcompat flags
+ //
+ ULARGE_INTEGER AppCompatFlagsUser;
+
+ //
+ // Filled in by LdrpInstallAppcompatBackend
+ //
+ PVOID pShimData;
+
+ //
+ // Filled in by LdrQueryImageFileExecutionOptions
+ //
+ PVOID AppCompatInfo;
+
+ //
+ // Used by GetVersionExW as the szCSDVersion string
+ //
+ UNICODE_STRING CSDVersion;
+
+ //
+ // Fusion stuff
+ //
+ PVOID ActivationContextData;
+ PVOID ProcessAssemblyStorageMap;
+ PVOID SystemDefaultActivationContextData;
+ PVOID SystemAssemblyStorageMap;
+
+ //
+ // Enforced minimum initial commit stack
+ //
+ SIZE_T MinimumStackCommit;
+
+} PEB, *PPEB;
+
+#define ACTIVATION_CONTEXT_STACK_FLAG_QUERIES_DISABLED (0x00000001)
+
+typedef struct _ACTIVATION_CONTEXT_STACK {
+ ULONG Flags;
+ ULONG NextCookieSequenceNumber;
+ PVOID ActiveFrame;
+ LIST_ENTRY FrameListCache;
+
+#if NT_SXS_PERF_COUNTERS_ENABLED
+ struct _ACTIVATION_CONTEXT_STACK_PERF_COUNTERS {
+ ULONGLONG Activations;
+ ULONGLONG ActivationCycles;
+ ULONGLONG Deactivations;
+ ULONGLONG DeactivationCycles;
+ } Counters;
+#endif // NT_SXS_PERF_COUNTERS_ENABLED
+} ACTIVATION_CONTEXT_STACK, *PACTIVATION_CONTEXT_STACK;
+
+typedef const ACTIVATION_CONTEXT_STACK *PCACTIVATION_CONTEXT_STACK;
+
+#define TEB_ACTIVE_FRAME_CONTEXT_FLAG_EXTENDED (0x00000001)
+
+typedef struct _TEB_ACTIVE_FRAME_CONTEXT {
+ ULONG Flags;
+ PCSTR FrameName;
+} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT;
+
+typedef const struct _TEB_ACTIVE_FRAME_CONTEXT *PCTEB_ACTIVE_FRAME_CONTEXT;
+
+typedef struct _TEB_ACTIVE_FRAME_CONTEXT_EX {
+ TEB_ACTIVE_FRAME_CONTEXT BasicContext;
+ PCSTR SourceLocation; // e.g. "Z:\foo\bar\baz.c"
+} TEB_ACTIVE_FRAME_CONTEXT_EX, *PTEB_ACTIVE_FRAME_CONTEXT_EX;
+
+typedef const struct _TEB_ACTIVE_FRAME_CONTEXT_EX *PCTEB_ACTIVE_FRAME_CONTEXT_EX;
+
+#define TEB_ACTIVE_FRAME_FLAG_EXTENDED (0x00000001)
+
+typedef struct _TEB_ACTIVE_FRAME {
+ ULONG Flags;
+ TYPE3(struct _TEB_ACTIVE_FRAME*) Previous;
+ PCTEB_ACTIVE_FRAME_CONTEXT Context;
+} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME;
+
+typedef const struct _TEB_ACTIVE_FRAME *PCTEB_ACTIVE_FRAME;
+
+typedef struct _TEB_ACTIVE_FRAME_EX {
+ TEB_ACTIVE_FRAME BasicFrame;
+ PVOID ExtensionIdentifier; // use address of your DLL Main or something unique to your mapping in the address space
+} TEB_ACTIVE_FRAME_EX, *PTEB_ACTIVE_FRAME_EX;
+
+typedef const struct _TEB_ACTIVE_FRAME_EX *PCTEB_ACTIVE_FRAME_EX;
+
+typedef struct _CLIENT_ID {
+ HANDLE UniqueProcess;
+ HANDLE UniqueThread;
+} CLIENT_ID;
+typedef CLIENT_ID *PCLIENT_ID;
+
+#define GDI_BATCH_BUFFER_SIZE 310
+
+typedef struct _GDI_TEB_BATCH {
+ ULONG Offset;
+ ULONG_PTR HDC;
+ ULONG Buffer[GDI_BATCH_BUFFER_SIZE];
+} GDI_TEB_BATCH,*PGDI_TEB_BATCH;
+
+typedef struct _Wx86ThreadState {
+ PULONG CallBx86Eip;
+ PVOID DeallocationCpu;
+ BOOLEAN UseKnownWx86Dll;
+ char OleStubInvoked;
+} WX86THREAD, *PWX86THREAD;
+
+#define STATIC_UNICODE_BUFFER_LENGTH 261
+#define WIN32_CLIENT_INFO_LENGTH 62
+
+typedef struct _PEB* PPEB;
+
+typedef struct _TEB {
+ NT_TIB NtTib;
+ PVOID EnvironmentPointer;
+ CLIENT_ID ClientId;
+ PVOID ActiveRpcHandle;
+ PVOID ThreadLocalStoragePointer;
+#if defined(PEBTEB_BITS)
+ PVOID ProcessEnvironmentBlock;
+#else
+ PPEB ProcessEnvironmentBlock;
+#endif
+ ULONG LastErrorValue;
+ ULONG CountOfOwnedCriticalSections;
+ PVOID CsrClientThread;
+ PVOID Win32ThreadInfo; // PtiCurrent
+ ULONG User32Reserved[26]; // user32.dll items
+ ULONG UserReserved[5]; // Winsrv SwitchStack
+ PVOID WOW32Reserved; // used by WOW
+ LCID CurrentLocale;
+ ULONG FpSoftwareStatusRegister; // offset known by outsiders!
+ PVOID SystemReserved1[54]; // Used by FP emulator
+ NTSTATUS ExceptionCode; // for RaiseUserException
+ ACTIVATION_CONTEXT_STACK ActivationContextStack; // Fusion activation stack
+ // sizeof(PVOID) is a way to express processor-dependence, more generally than #ifdef HOST_64BIT
+ UCHAR SpareBytes1[48 - sizeof(PVOID) - sizeof(ACTIVATION_CONTEXT_STACK)];
+ GDI_TEB_BATCH GdiTebBatch; // Gdi batching
+ CLIENT_ID RealClientId;
+ HANDLE GdiCachedProcessHandle;
+ ULONG GdiClientPID;
+ ULONG GdiClientTID;
+ PVOID GdiThreadLocalInfo;
+ ULONG_PTR Win32ClientInfo[WIN32_CLIENT_INFO_LENGTH]; // User32 Client Info
+ PVOID glDispatchTable[233]; // OpenGL
+ ULONG_PTR glReserved1[29]; // OpenGL
+ PVOID glReserved2; // OpenGL
+ PVOID glSectionInfo; // OpenGL
+ PVOID glSection; // OpenGL
+ PVOID glTable; // OpenGL
+ PVOID glCurrentRC; // OpenGL
+ PVOID glContext; // OpenGL
+ ULONG LastStatusValue;
+ UNICODE_STRING StaticUnicodeString;
+ WCHAR StaticUnicodeBuffer[STATIC_UNICODE_BUFFER_LENGTH];
+ PVOID DeallocationStack;
+ PVOID TlsSlots[TLS_MINIMUM_AVAILABLE];
+ LIST_ENTRY TlsLinks;
+ PVOID Vdm;
+ PVOID ReservedForNtRpc;
+ PVOID DbgSsReserved[2];
+ ULONG HardErrorsAreDisabled;
+ PVOID Instrumentation[16];
+ PVOID WinSockData; // WinSock
+ ULONG GdiBatchCount;
+ BOOLEAN InDbgPrint;
+ BOOLEAN FreeStackOnTermination;
+ BOOLEAN HasFiberData;
+ BOOLEAN IdealProcessor;
+ ULONG Spare3;
+ PVOID ReservedForPerf;
+ PVOID ReservedForOle;
+ ULONG WaitingOnLoaderLock;
+ WX86THREAD Wx86Thread;
+ PPVOID TlsExpansionSlots;
+ LCID ImpersonationLocale; // Current locale of impersonated user
+ ULONG IsImpersonating; // Thread impersonation status
+ PVOID NlsCache; // NLS thread cache
+ PVOID pShimData; // Per thread data used in the shim
+ ULONG HeapVirtualAffinity;
+ HANDLE CurrentTransactionHandle;// reserved for TxF transaction context
+ PTEB_ACTIVE_FRAME ActiveFrame;
+} TEB;
+typedef TEB *PTEB;
+
+typedef struct _CURDIR {
+ UNICODE_STRING DosPath;
+ HANDLE Handle;
+} CURDIR, *PCURDIR;
+
+#define RTL_USER_PROC_CURDIR_CLOSE 0x00000002
+#define RTL_USER_PROC_CURDIR_INHERIT 0x00000003
+
+typedef struct _RTL_DRIVE_LETTER_CURDIR {
+ USHORT Flags;
+ USHORT Length;
+ ULONG TimeStamp;
+ STRING DosPath;
+} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
+
+
+#define RTL_MAX_DRIVE_LETTERS 32
+#define RTL_DRIVE_LETTER_VALID (USHORT)0x0001
+
+typedef struct _RTL_USER_PROCESS_PARAMETERS {
+ ULONG MaximumLength;
+ ULONG Length;
+
+ ULONG Flags;
+ ULONG DebugFlags;
+
+ HANDLE ConsoleHandle;
+ ULONG ConsoleFlags;
+ HANDLE StandardInput;
+ HANDLE StandardOutput;
+ HANDLE StandardError;
+
+ CURDIR CurrentDirectory; // ProcessParameters
+ UNICODE_STRING DllPath; // ProcessParameters
+ UNICODE_STRING ImagePathName; // ProcessParameters
+ UNICODE_STRING CommandLine; // ProcessParameters
+ PVOID Environment; // NtAllocateVirtualMemory
+
+ ULONG StartingX;
+ ULONG StartingY;
+ ULONG CountX;
+ ULONG CountY;
+ ULONG CountCharsX;
+ ULONG CountCharsY;
+ ULONG FillAttribute;
+
+ ULONG WindowFlags;
+ ULONG ShowWindowFlags;
+ UNICODE_STRING WindowTitle; // ProcessParameters
+ UNICODE_STRING DesktopInfo; // ProcessParameters
+ UNICODE_STRING ShellInfo; // ProcessParameters
+ UNICODE_STRING RuntimeData; // ProcessParameters
+ RTL_DRIVE_LETTER_CURDIR CurrentDirectores[ RTL_MAX_DRIVE_LETTERS ];
+} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
+
+
+typedef enum _PROCESSINFOCLASS {
+ ProcessBasicInformation,
+ ProcessQuotaLimits,
+ ProcessIoCounters,
+ ProcessVmCounters,
+ ProcessTimes,
+ ProcessBasePriority,
+ ProcessRaisePriority,
+ ProcessDebugPort,
+ ProcessExceptionPort,
+ ProcessAccessToken,
+ ProcessLdtInformation,
+ ProcessLdtSize,
+ ProcessDefaultHardErrorMode,
+ ProcessIoPortHandlers, // Note: this is kernel mode only
+ ProcessPooledUsageAndLimits,
+ ProcessWorkingSetWatch,
+ ProcessUserModeIOPL,
+ ProcessEnableAlignmentFaultFixup,
+ ProcessPriorityClass,
+ ProcessWx86Information,
+ ProcessHandleCount,
+ ProcessAffinityMask,
+ ProcessPriorityBoost,
+ ProcessDeviceMap,
+ ProcessSessionInformation,
+ ProcessForegroundInformation,
+ ProcessWow64Information,
+ ProcessImageFileName,
+ ProcessLUIDDeviceMapsEnabled,
+ ProcessBreakOnTermination,
+ ProcessDebugObjectHandle,
+ ProcessDebugFlags,
+ ProcessHandleTracing,
+ MaxProcessInfoClass // MaxProcessInfoClass should always be the last enum
+ } PROCESSINFOCLASS;
+
+
+typedef struct _VM_COUNTERS {
+ SIZE_T PeakVirtualSize;
+ SIZE_T VirtualSize;
+ ULONG PageFaultCount;
+ SIZE_T PeakWorkingSetSize;
+ SIZE_T WorkingSetSize;
+ SIZE_T QuotaPeakPagedPoolUsage;
+ SIZE_T QuotaPagedPoolUsage;
+ SIZE_T QuotaPeakNonPagedPoolUsage;
+ SIZE_T QuotaNonPagedPoolUsage;
+ SIZE_T PagefileUsage;
+ SIZE_T PeakPagefileUsage;
+} VM_COUNTERS;
+typedef VM_COUNTERS *PVM_COUNTERS;
+
+#undef TYPE3
+
+#endif // !defined(HOST_UNIX)
+
+#if !defined(TARGET_X86)
+
+typedef enum _FUNCTION_TABLE_TYPE {
+ RF_SORTED,
+ RF_UNSORTED,
+ RF_CALLBACK
+} FUNCTION_TABLE_TYPE;
+
+typedef struct _DYNAMIC_FUNCTION_TABLE {
+ LIST_ENTRY Links;
+ PT_RUNTIME_FUNCTION FunctionTable;
+ LARGE_INTEGER TimeStamp;
+
+#ifdef TARGET_ARM
+ ULONG MinimumAddress;
+ ULONG MaximumAddress;
+ ULONG BaseAddress;
+#else
+ ULONG64 MinimumAddress;
+ ULONG64 MaximumAddress;
+ ULONG64 BaseAddress;
+#endif
+
+ PGET_RUNTIME_FUNCTION_CALLBACK Callback;
+ PVOID Context;
+ PWSTR OutOfProcessCallbackDll;
+ FUNCTION_TABLE_TYPE Type;
+ ULONG EntryCount;
+} DYNAMIC_FUNCTION_TABLE, *PDYNAMIC_FUNCTION_TABLE;
+
+#endif // !TARGET_X86
+
+//
+// AMD64
+//
+#ifdef TARGET_AMD64
+
+#define RUNTIME_FUNCTION__BeginAddress(prf) (prf)->BeginAddress
+#define RUNTIME_FUNCTION__SetBeginAddress(prf,address) ((prf)->BeginAddress = (address))
+
+#define RUNTIME_FUNCTION__EndAddress(prf, ImageBase) (prf)->EndAddress
+
+#define RUNTIME_FUNCTION__GetUnwindInfoAddress(prf) (prf)->UnwindData
+#define RUNTIME_FUNCTION__SetUnwindInfoAddress(prf,address) do { (prf)->UnwindData = (address); } while (0)
+#define OFFSETOF__RUNTIME_FUNCTION__UnwindInfoAddress offsetof(T_RUNTIME_FUNCTION, UnwindData)
+
+#include "win64unwind.h"
+
+typedef
+PEXCEPTION_ROUTINE
+(RtlVirtualUnwindFn) (
+ IN ULONG HandlerType,
+ IN ULONG64 ImageBase,
+ IN ULONG64 ControlPc,
+ IN PT_RUNTIME_FUNCTION FunctionEntry,
+ IN OUT PCONTEXT ContextRecord,
+ OUT PVOID *HandlerData,
+ OUT PULONG64 EstablisherFrame,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
+ );
+
+#ifndef HOST_UNIX
+extern RtlVirtualUnwindFn* RtlVirtualUnwind_Unsafe;
+#else // !HOST_UNIX
+PEXCEPTION_ROUTINE
+RtlVirtualUnwind_Unsafe(
+ IN ULONG HandlerType,
+ IN ULONG64 ImageBase,
+ IN ULONG64 ControlPc,
+ IN PT_RUNTIME_FUNCTION FunctionEntry,
+ IN OUT PCONTEXT ContextRecord,
+ OUT PVOID *HandlerData,
+ OUT PULONG64 EstablisherFrame,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
+ );
+#endif // !HOST_UNIX
+
+#endif // TARGET_AMD64
+
+//
+// X86
+//
+
+#ifdef TARGET_X86
+#ifndef TARGET_UNIX
+//
+// x86 ABI does not define RUNTIME_FUNCTION. Define our own to allow unification between x86 and other platforms.
+//
+#ifdef HOST_X86
+typedef struct _RUNTIME_FUNCTION {
+ DWORD BeginAddress;
+ DWORD UnwindData;
+} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
+
+typedef struct _DISPATCHER_CONTEXT {
+ _EXCEPTION_REGISTRATION_RECORD* RegistrationPointer;
+} DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT;
+#endif // HOST_X86
+#endif // !TARGET_UNIX
+
+#define RUNTIME_FUNCTION__BeginAddress(prf) (prf)->BeginAddress
+#define RUNTIME_FUNCTION__SetBeginAddress(prf,addr) ((prf)->BeginAddress = (addr))
+
+#ifdef FEATURE_EH_FUNCLETS
+#include "win64unwind.h"
+#include "daccess.h"
+
+FORCEINLINE
+DWORD
+RtlpGetFunctionEndAddress (
+ _In_ PT_RUNTIME_FUNCTION FunctionEntry,
+ _In_ TADDR ImageBase
+ )
+{
+ PTR_UNWIND_INFO pUnwindInfo = (PTR_UNWIND_INFO)(ImageBase + FunctionEntry->UnwindData);
+
+ return FunctionEntry->BeginAddress + pUnwindInfo->FunctionLength;
+}
+
+#define RUNTIME_FUNCTION__EndAddress(prf, ImageBase) RtlpGetFunctionEndAddress(prf, ImageBase)
+
+#define RUNTIME_FUNCTION__GetUnwindInfoAddress(prf) (prf)->UnwindData
+#define RUNTIME_FUNCTION__SetUnwindInfoAddress(prf, addr) do { (prf)->UnwindData = (addr); } while(0)
+
+#ifdef HOST_X86
+EXTERN_C
+NTSYSAPI
+PEXCEPTION_ROUTINE
+NTAPI
+RtlVirtualUnwind (
+ _In_ DWORD HandlerType,
+ _In_ DWORD ImageBase,
+ _In_ DWORD ControlPc,
+ _In_ PRUNTIME_FUNCTION FunctionEntry,
+ __inout PT_CONTEXT ContextRecord,
+ _Out_ PVOID *HandlerData,
+ _Out_ PDWORD EstablisherFrame,
+ __inout_opt PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers
+ );
+#endif // HOST_X86
+#endif // FEATURE_EH_FUNCLETS
+
+#endif // TARGET_X86
+
+#ifdef TARGET_ARM
+#include "daccess.h"
+
+//
+// Define unwind information flags.
+//
+
+#define UNW_FLAG_NHANDLER 0x0 /* any handler */
+#define UNW_FLAG_EHANDLER 0x1 /* filter handler */
+#define UNW_FLAG_UHANDLER 0x2 /* unwind handler */
+
+// This function returns the length of a function using the new unwind info on arm.
+// Taken from minkernel\ntos\rtl\arm\ntrtlarm.h.
+FORCEINLINE
+ULONG
+RtlpGetFunctionEndAddress (
+ _In_ PT_RUNTIME_FUNCTION FunctionEntry,
+ _In_ TADDR ImageBase
+ )
+{
+ ULONG FunctionLength;
+
+ FunctionLength = FunctionEntry->UnwindData;
+ if ((FunctionLength & 3) != 0) {
+ FunctionLength = (FunctionLength >> 2) & 0x7ff;
+ } else {
+ FunctionLength = *(PTR_ULONG)(ImageBase + FunctionLength) & 0x3ffff;
+ }
+
+ return FunctionEntry->BeginAddress + 2 * FunctionLength;
+}
+
+#define RUNTIME_FUNCTION__BeginAddress(FunctionEntry) ThumbCodeToDataPointer<DWORD,DWORD>((FunctionEntry)->BeginAddress)
+#define RUNTIME_FUNCTION__SetBeginAddress(FunctionEntry,address) ((FunctionEntry)->BeginAddress = DataPointerToThumbCode<DWORD,DWORD>(address))
+
+#define RUNTIME_FUNCTION__EndAddress(FunctionEntry, ImageBase) ThumbCodeToDataPointer<DWORD,DWORD>(RtlpGetFunctionEndAddress(FunctionEntry, ImageBase))
+
+#define RUNTIME_FUNCTION__SetUnwindInfoAddress(prf,address) do { (prf)->UnwindData = (address); } while (0)
+
+typedef struct _UNWIND_INFO {
+ // dummy
+} UNWIND_INFO, *PUNWIND_INFO;
+
+#if defined(HOST_UNIX) || defined(HOST_X86)
+
+EXTERN_C
+NTSYSAPI
+PEXCEPTION_ROUTINE
+NTAPI
+RtlVirtualUnwind (
+ _In_ DWORD HandlerType,
+ _In_ DWORD ImageBase,
+ _In_ DWORD ControlPc,
+ _In_ PT_RUNTIME_FUNCTION FunctionEntry,
+ __inout PT_CONTEXT ContextRecord,
+ _Out_ PVOID *HandlerData,
+ _Out_ PDWORD EstablisherFrame,
+ __inout_opt PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers
+ );
+#endif // HOST_UNIX || HOST_X86
+
+#define UNW_FLAG_NHANDLER 0x0
+
+#endif // TARGET_ARM
+
+#ifdef TARGET_ARM64
+#include "daccess.h"
+
+#define UNW_FLAG_NHANDLER 0x0 /* any handler */
+#define UNW_FLAG_EHANDLER 0x1 /* filter handler */
+#define UNW_FLAG_UHANDLER 0x2 /* unwind handler */
+
+// This function returns the RVA of the end of the function (exclusive, so one byte after the actual end)
+// using the unwind info on ARM64. (see ExternalAPIs\Win9CoreSystem\inc\winnt.h)
+FORCEINLINE
+ULONG64
+RtlpGetFunctionEndAddress (
+ _In_ PT_RUNTIME_FUNCTION FunctionEntry,
+ _In_ ULONG64 ImageBase
+ )
+{
+ ULONG64 FunctionLength;
+
+ FunctionLength = FunctionEntry->UnwindData;
+ if ((FunctionLength & 3) != 0) {
+ FunctionLength = (FunctionLength >> 2) & 0x7ff;
+ } else {
+ FunctionLength = *(PTR_ULONG64)(ImageBase + FunctionLength) & 0x3ffff;
+ }
+
+ return FunctionEntry->BeginAddress + 4 * FunctionLength;
+}
+
+#define RUNTIME_FUNCTION__BeginAddress(FunctionEntry) ((FunctionEntry)->BeginAddress)
+#define RUNTIME_FUNCTION__SetBeginAddress(FunctionEntry,address) ((FunctionEntry)->BeginAddress = (address))
+
+#define RUNTIME_FUNCTION__EndAddress(FunctionEntry, ImageBase) (RtlpGetFunctionEndAddress(FunctionEntry, (ULONG64)(ImageBase)))
+
+#define RUNTIME_FUNCTION__SetUnwindInfoAddress(prf,address) do { (prf)->UnwindData = (address); } while (0)
+
+typedef struct _UNWIND_INFO {
+ // dummy
+} UNWIND_INFO, *PUNWIND_INFO;
+
+EXTERN_C
+NTSYSAPI
+PEXCEPTION_ROUTINE
+NTAPI
+RtlVirtualUnwind(
+ IN ULONG HandlerType,
+ IN ULONG64 ImageBase,
+ IN ULONG64 ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN OUT PCONTEXT ContextRecord,
+ OUT PVOID *HandlerData,
+ OUT PULONG64 EstablisherFrame,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
+ );
+
+#ifndef IMAGE_FILE_MACHINE_ARM64
+#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian
+#endif
+
+#ifndef IMAGE_REL_ARM64_BRANCH26
+#define IMAGE_REL_ARM64_BRANCH26 0x0003 // 26 bit offset << 2 & sign ext. for B & BL
+#endif
+
+#ifndef IMAGE_REL_ARM64_PAGEBASE_REL21
+#define IMAGE_REL_ARM64_PAGEBASE_REL21 0x0004 // ADRP 21 bit PC-relative page address
+#endif
+
+#ifndef IMAGE_REL_ARM64_PAGEOFFSET_12A
+#define IMAGE_REL_ARM64_PAGEOFFSET_12A 0x0006 // ADD 12 bit page offset
+#endif
+
+#endif
+
+#endif // CLRNT_H_
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-import "unknwn.idl";
-
-// Forward declarations
-interface ICLRPrivAppXDomain;
-interface ICLRPrivAppXRuntime;
-
-/**************************************************************************************
- ** ICLRPrivAppXRuntime
- **************************************************************************************/
-[
- uuid(6D2DF5A4-FA3A-4481-8BA0-0422FD21720F),
- version(1.0),
- local
-]
-interface ICLRPrivAppXRuntime : IUnknown
-{
- /**********************************************************************************
- ** Use to create and initialize the AppX domain. Should only be called once; all
- ** subsequent calls will fail. Thread safe.
- **
- ** wzFriendlyName - the domain friendly name.
- **********************************************************************************/
- HRESULT InitializeAppXDomain(
- [in] LPCWSTR wzFriendlyName);
-
- /**********************************************************************************
- ** Use to retrieve the AppX domain. InitializeAppXDomain must have been
- ** successfully called previously. May be called multiple times. Thread safe.
- **
- ** riidDomain - the IID of the interface to be returned in ppvDomain.
- ** ppIAppXDomain - receives the ICLRPrivAppXDomain interface.
- **********************************************************************************/
- HRESULT GetAppXDomain(
- [in] REFIID riidDomain,
- [out] LPVOID * ppvDomain);
-}
-
-/**************************************************************************************
- ** ICLRPrivAppXDomain
- **************************************************************************************/
-[
- uuid(6633398E-823D-4361-B30E-824043BD4686),
- version(1.0),
- local
-]
-interface ICLRPrivAppXDomain : IUnknown
-{
- /**********************************************************************************
- ** Use to create a delegate to a static method.
- **
- ** wzAssemblyName - the name of the assembly that contains the target type.
- ** wzTypeName - the name of the type that contains the target method.
- ** wzMethodName - the static method for which to create a delegate.
- ** ppvDelegate - receives the native-callable function pointer corresponding to
- ** the specified static method.
- **********************************************************************************/
- HRESULT CreateDelegate(
- [in] LPCWSTR wzAssemblyName,
- [in] LPCWSTR wzTypeName,
- [in] LPCWSTR wzMethodName,
- [out] LPVOID * ppvDelegate);
-};
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-import "unknwn.idl";
-import "objidl.idl";
-
-// Forward declarations
-interface ICLRPrivBinder;
-interface ICLRPrivAssembly;
-
-/**************************************************************************************
- ** This IDL file defines the assembly binding host interfaces. Some things to keep
- ** in mind:
- ** - Equality is determined by pointer equality: two interface instances
- ** should be considered equal if and only if their pointer values are equal.
- ** - All operations are idempotent: when a method is called more than once with
- ** the same input values, it is required to return identical results. The only
- ** possible exceptions center around transient errors such as E_OUTOFMEMORY.
- **************************************************************************************/
-
-/**************************************************************************************
- ** ICLRPrivBinder - Use to bind to an assembly by name or by metadata reference.
- **************************************************************************************/
-[
- uuid(2601F621-E462-404C-B299-3E1DE72F8542),
- version(1.0),
- local
-]
-interface ICLRPrivBinder : IUnknown
-{
- /**********************************************************************************
- ** BindAssemblyByName -- Binds an assembly by name.
- ** NOTE: This method is required to be idempotent. See general comment above.
- **
- ** pAssemblyFullName - name of the assembly for which a bind is being requested.
- ** ppAssembly - upon success, receives the bound assembly.
- **********************************************************************************/
- HRESULT BindAssemblyByName(
- [in] struct AssemblyNameData* pAssemblyNameData,
- [out, retval] ICLRPrivAssembly ** ppAssembly);
-
- /**********************************************************************************
- ** GetBinderID
- ** pBinderId, pointer to binder id. The binder id has the following properties
- ** It is a pointer that does not change over the lifetime of a binder object
- ** It points at an object in memory that will remain allocated for the lifetime of the binder.
- ** This value should be the same for a set of binder objects that represent the same binder behavior.
- **********************************************************************************/
- HRESULT GetBinderID(
- [out, retval] UINT_PTR *pBinderId);
-
- /**********************************************************************************
- ** GetLoaderAllocator
- ** Get LoaderAllocator for binders that contain it. For other binders, return
- ** E_FAIL
- **
- ** pLoaderAllocator - when successful, constains the returned LoaderAllocator
- **********************************************************************************/
- HRESULT GetLoaderAllocator(
- [out, retval] LPVOID * pLoaderAllocator);
-};
-
-/**************************************************************************************
- ** ASSEMBLY_IMAGE_TYPES - The set of assembly image formats.
- **************************************************************************************/
-enum ASSEMBLY_IMAGE_TYPES
-{
- // IL image format
- ASSEMBLY_IMAGE_TYPE_IL = 0x0001,
- // Native image (NGEN) format
- ASSEMBLY_IMAGE_TYPE_NATIVE = 0x0002,
- // Binder's preferred image type
- ASSEMBLY_IMAGE_TYPE_DEFAULT = 0x0003,
- // Only supported on CoreCLR
- ASSEMBLY_IMAGE_TYPE_ASSEMBLY = 0x0004,
-};
-
-/**************************************************************************************
- ** ICLRPrivAssembly - Represents an assembly bind result. Extends ICLRPrivBinder, which
- ** implicitly tracks the parent binder, and enables simple assembly-relative binds.
- **************************************************************************************/
-[
- uuid(2601F621-E462-404C-B299-3E1DE72F8543),
- version(1.0),
- local
-]
-interface ICLRPrivAssembly : ICLRPrivBinder
-{
- /**********************************************************************************
- ** GetAvailableImageTypes - Use to retrieve the set of images available for an
- ** assembly.
- ** NOTE: This method is required to be idempotent. See general comment above.
- **
- ** pdwImageTypes - set to values from ASSEMBLY_IMAGE_TYPES to indicate
- ** which image formats are available for the assembly.
- **********************************************************************************/
- HRESULT GetAvailableImageTypes(
- [out, retval] LPDWORD pdwImageTypes);
-};
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-import "unknwn.idl";
-import "CLRPrivBinding.idl";
-
-//=====================================================================================================================
-// Forward declarations
-interface ICLRPrivRuntime;
-
-//=====================================================================================================================
-// CLRPrivHosting coclass
-//=====================================================================================================================
-[
- uuid("EDA73987-E6C0-42BF-A6B7-073F7B24D8C7")
-]
-library CLRPrivHosting
-{
- //=================================================================================================================
- // CLRPrivRuntime
- //=================================================================================================================
- [
- uuid(BC1B53A8-DCBC-43B2-BB17-1E4061447AE8)
- ]
- coclass CLRPrivRuntime
- {
- [default] interface ICLRPrivRuntime;
- };
-};
-
-//=====================================================================================================================
-// ICLRPrivRuntime
-//=====================================================================================================================
-[
- uuid(BC1B53A8-DCBC-43B2-BB17-1E4061447AE9),
- version(1.0),
- local
-]
-interface ICLRPrivRuntime : IUnknown
-{
- /**********************************************************************************
- ** Used for accessing additional hosting functionality.
- **
- ** rclsid - the CoClass from which to request the interface.
- ** riid - the IID of the interface being requested.
- ** ppUnk - receives the interface pointer value.
- **********************************************************************************/
- HRESULT GetInterface(
- [in] REFCLSID rclsid,
- [in] REFIID riid,
- [out, iid_is(riid), retval] LPVOID *ppUnk);
-
- /**********************************************************************************
- ** Creates a domain using the provided binder for the root default bind context.
- **
- ** pwzFriendlyName - the name to associate with the domain.
- ** pBinder - the binder to use for root-level binds in the default context.
- ** pdwAppDomainId - receives the ID of the created domain.
- **********************************************************************************/
- HRESULT CreateAppDomain(
- [in, string] LPCWSTR pwzFriendlyName,
- [in] ICLRPrivBinder * pBinder,
- [out, retval] LPDWORD pdwAppDomainId);
-
- /**********************************************************************************
- ** Creates a native-callable function pointer to the specified method.
- **
- ** appDomainID - the domain in which to create the delegate.
- ** wszAssemblyName - the name of the assembly in which the method is defined.
- ** wszClassName - the name of the class (including namespace) in which the method
- ** is defined.
- ** wszMethodName - the name of the method for which to create a delegate.
- ** ppvDelegate - receives the delegate pointer value.
- **********************************************************************************/
- HRESULT CreateDelegate(
- [in] DWORD appDomainID,
- [in, string] LPCWSTR wszAssemblyName,
- [in, string] LPCWSTR wszClassName,
- [in, string] LPCWSTR wszMethodName,
- [out, retval] LPVOID * ppvDelegate);
-
- /**********************************************************************************
- ** Creates an AppX appdomain and executes entrypoint method of an executable there
- **
- **********************************************************************************/
- HRESULT ExecuteMain(
- [in] ICLRPrivBinder * pBinder,
- [out, retval] int * pRetVal
- );
-};
-
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-import "CLRPrivBinding.idl";
-
-//=====================================================================================================================
-// CLRPrivRuntime
-//=====================================================================================================================
-[
- uuid("EA6A2170-8F6A-4007-87A9-02429F615958")
-]
-library CLRPrivRuntimeBinders
-{
- //=================================================================================================================
- // CLRPrivAppXBinder
- //=================================================================================================================
- [
- uuid(E990F732-2D0A-48AC-87FC-EF12B618981A),
- helpstring("Runtime-provided package-based assembly binder for AppX."),
- ]
- coclass CLRPrivAppXBinder
- {
- [default] interface ICLRPrivBinder;
- };
-
- //=================================================================================================================
- // CLRPrivFusionBinder
- //=================================================================================================================
- [
- uuid(E990F732-2D0A-48AC-87FC-EF12B618981C),
- helpstring("Runtime-provided binder for wrapping ICLRPrivBinder-compatible subset of fusion functionality."),
- ]
- coclass CLRPrivFusionBinder
- {
- [default] interface ICLRPrivBinder;
- };
-};
-
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ================================================================================
+// Standard primitive types for CLR code
+//
+// This header serves as a platform layer containing all of the primitive types
+// which we use across CLR implementation code.
+// ================================================================================
+
+
+#ifndef CLRTYPES_H_
+#define CLRTYPES_H_
+
+#if defined(_MSC_VER) && !defined(SOURCE_FORMATTING) && defined(FEATURE_CORESYSTEM)
+ // Prefer intsafe.h when available, which defines many of the MAX/MIN
+ // values below (which is why they are in #ifndef blocks).
+ #include <intsafe.h>
+#endif
+
+#include "crtwrap.h"
+#include "winwrap.h"
+#include "staticcontract.h"
+#include "static_assert.h"
+
+#if HOST_64BIT
+ #define POINTER_BITS (64)
+#else
+ #define POINTER_BITS (32)
+#endif
+
+// ================================================================================
+// Integral types - use these for all integral types
+// These types are in ALL_CAPS. Each type has a _MIN and _MAX defined for it.
+// ================================================================================
+
+// --------------------------------------------------------------------------------
+// Use these types for fixed size integers:
+// INT8 UINT8 INT16 UINT16 INT32 UINT32 INT64 UINT64
+// --------------------------------------------------------------------------------
+
+#ifndef INT8_MAX
+ typedef signed char INT8;
+ typedef unsigned char UINT8;
+ typedef short INT16;
+ typedef unsigned short UINT16;
+ typedef int INT32;
+ typedef unsigned int UINT32;
+ typedef __int64 INT64;
+ typedef unsigned __int64 UINT64;
+
+ #ifdef _MSC_VER
+ /* These macros must exactly match those in the Windows SDK's intsafe.h */
+ #define INT8_MIN (-127i8 - 1)
+ #define INT16_MIN (-32767i16 - 1)
+ #define INT32_MIN (-2147483647i32 - 1)
+ #define INT64_MIN (-9223372036854775807i64 - 1)
+
+ #define INT8_MAX 127i8
+ #define INT16_MAX 32767i16
+ #define INT32_MAX 2147483647i32
+ #define INT64_MAX 9223372036854775807i64
+
+ #define UINT8_MAX 0xffui8
+ #define UINT16_MAX 0xffffui16
+ #define UINT32_MAX 0xffffffffui32
+ #define UINT64_MAX 0xffffffffffffffffui64
+ #else
+ #define INT8_MIN ((INT8)0x80)
+ #define INT16_MIN ((INT16)0x8000)
+ #define INT32_MIN ((INT32)0x80000000)
+ #define INT64_MIN ((INT64) I64(0x8000000000000000))
+
+ #define INT8_MAX ((INT8)0x7f)
+ #define INT16_MAX ((INT16)0x7fff)
+ #define INT32_MAX ((INT32)0x7fffffff)
+ #define INT64_MAX ((INT64) I64(0x7fffffffffffffff))
+
+ #define UINT8_MAX ((UINT8)0xffU)
+ #define UINT16_MAX ((UINT16)0xffffU)
+ #define UINT32_MAX ((UINT32)0xffffffffU)
+ #define UINT64_MAX ((UINT64) UI64(0xffffffffffffffff))
+ #endif
+#endif // !INT8_MAX
+
+// UINTX_MINs aren't defined in standard header files,
+// so definition must be separately predicated.
+#ifndef UINT8_MIN
+ #ifdef _MSC_VER
+ #define UINT8_MIN 0ui8
+ #define UINT16_MIN 0ui16
+ #define UINT32_MIN 0ui32
+ #define UINT64_MIN 0ui64
+ #else
+ #define UINT8_MIN ((UINT8)0U)
+ #define UINT16_MIN ((UINT16)0U)
+ #define UINT32_MIN ((UINT32)0U)
+ #define UINT64_MIN ((UINT64) UI64(0))
+ #endif
+#endif
+
+
+// --------------------------------------------------------------------------------
+// Use these types for pointer-sized integral types
+// SIZE_T SSIZE_T
+//
+// These types are the ONLY types which can be safely cast back and forth from a
+// pointer.
+// --------------------------------------------------------------------------------
+
+#ifndef SIZE_T_MAX
+ #if NEED_POINTER_SIZED_TYPEDEFS
+ typedef size_t SIZE_T;
+ typedef ptrdiff_t SSIZE_T;
+ #endif
+
+ #if POINTER_BITS == 64
+ #define SIZE_T_MAX UINT64_MAX
+ #define SIZE_T_MIN UINT64_MIN
+
+ #define SSIZE_T_MAX INT64_MAX
+ #define SSIZE_T_MIN INT64_MIN
+ #else
+ #define SIZE_T_MAX UINT32_MAX
+ #define SIZE_T_MIN UINT32_MIN
+
+ #define SSIZE_T_MAX INT32_MAX
+ #define SSIZE_T_MIN INT32_MIN
+ #endif
+#endif
+
+// --------------------------------------------------------------------------------
+// Non-pointer sized types
+// COUNT_T SCOUNT_T
+//
+// Use these types for "large" counts or indexes which will not exceed 32 bits. They
+// may also be used for pointer differences, if you can guarantee that the pointers
+// are pointing to the same region of memory. (It can NOT be used for arbitrary
+// pointer subtraction.)
+// --------------------------------------------------------------------------------
+
+#ifndef COUNT_T_MAX
+ typedef UINT32 COUNT_T;
+ typedef INT32 SCOUNT_T;
+
+ #define COUNT_T_MAX UINT32_MAX
+ #define COUNT_T_MIN UINT32_MIN
+
+ #define SCOUNT_T_MAX INT32_MAX
+ #define SCOUNT_T_MIN INT32_MIN
+#endif
+
+// --------------------------------------------------------------------------------
+// Integral types with additional semantic content
+// BOOL BYTE
+// --------------------------------------------------------------------------------
+
+#ifndef BYTE_MAX
+ #if NEED_BOOL_TYPEDEF
+ typedef bool BOOL;
+ #endif
+
+ #define BOOL_MAX 1
+ #define BOOL_MIN 0
+
+ #define TRUE 1
+ #define FALSE 0
+
+ typedef UINT8 BYTE;
+
+ #define BYTE_MAX UINT8_MAX
+ #define BYTE_MIN UINT8_MIN
+#endif
+
+// --------------------------------------------------------------------------------
+// Character types
+// CHAR SCHAR UCHAR WCHAR
+// --------------------------------------------------------------------------------
+
+typedef char CHAR;
+typedef signed char SCHAR;
+typedef unsigned char UCHAR;
+
+typedef CHAR ASCII;
+typedef CHAR ANSI;
+typedef CHAR UTF8;
+
+// Standard C defines:
+
+// CHAR_MAX
+// CHAR_MIN
+// SCHAR_MAX
+// SCHAR_MIN
+// UCHAR_MAX
+// UCHAR_MIN
+// WCHAR_MAX
+// WCHAR_MIN
+
+#ifndef ASCII_MAX
+ #define ASCII_MIN ((ASCII)0)
+ #define ASCII_MAX ((ASCII)127)
+
+ #define ANSI_MIN ((ANSI)0)
+ #define ANSI_MAX ((ANSI)255)
+
+ #define UTF8_MIN ((UTF8)0)
+ #define UTF8_MAX ((UTF8)255)
+#endif
+
+// ================================================================================
+// Non-integral types
+// These types are in ALL_CAPS.
+// ================================================================================
+
+// --------------------------------------------------------------------------------
+// Floating point types
+// FLOAT DOUBLE
+// --------------------------------------------------------------------------------
+
+// ================================================================================
+// Runtime type definitions - these are guaranteed to be identical with the
+// corresponding managed type
+// ================================================================================
+
+typedef WCHAR CLR_CHAR;
+typedef INT8 CLR_I1;
+typedef UINT8 CLR_U1;
+typedef INT16 CLR_I2;
+typedef UINT16 CLR_U2;
+typedef INT32 CLR_I4;
+typedef UINT32 CLR_U4;
+typedef INT64 CLR_I8;
+typedef UINT64 CLR_U8;
+typedef FLOAT CLR_R4;
+typedef DOUBLE CLR_R8;
+typedef SSIZE_T CLR_I;
+typedef SIZE_T CLR_U;
+
+#define CLR_CHAR_MAX WCHAR_MAX
+#define CLR_CHAR_MIN WCHAR_MIN
+
+#define CLR_I1_MAX INT8_MAX
+#define CLR_I1_MIN INT8_MIN
+
+#define CLR_U1_MAX UINT8_MAX
+#define CLR_U1_MIN UINT8_MIN
+
+#define CLR_I2_MAX INT16_MAX
+#define CLR_I2_MIN INT16_MIN
+
+#define CLR_U2_MAX UINT16_MAX
+#define CLR_U2_MIN UINT16_MIN
+
+#define CLR_I4_MAX INT32_MAX
+#define CLR_I4_MIN INT32_MIN
+
+#define CLR_U4_MAX UINT32_MAX
+#define CLR_U4_MIN UINT32_MIN
+
+#define CLR_I8_MAX INT64_MAX
+#define CLR_I8_MIN INT64_MIN
+
+#define CLR_U8_MAX UINT64_MAX
+#define CLR_U8_MIN UINT64_MIN
+
+#define CLR_I_MAX SSIZE_T_MAX
+#define CLR_I_MIN SSIZE_T_MIN
+
+#define CLR_U_MAX SIZE_T_MAX
+#define CLR_U_MIN SIZE_T_MIN
+
+ typedef bool CLR_BOOL;
+
+static_assert_no_msg(sizeof(CLR_BOOL) == 1);
+
+#define CLR_BOOL_MAX BOOL_MAX
+#define CLR_BOOL_MIN BOOL_MIN
+
+#define CLR_NAN_32 0xFFC00000
+#define CLR_NAN_64 I64(0xFFF8000000000000)
+
+// ================================================================================
+// Simple utility functions
+// ================================================================================
+
+// Note that these routines are in terms of UINT, ULONG, and ULONG64, since those are
+// the unsigned integral types the compiler overloads based on.
+
+// --------------------------------------------------------------------------------
+// Min/Max
+// --------------------------------------------------------------------------------
+
+template <typename T>
+T Min(T v1, T v2)
+{
+ STATIC_CONTRACT_LEAF;
+ return v1 < v2 ? v1 : v2;
+}
+
+template <typename T>
+T Max(T v1, T v2)
+{
+ STATIC_CONTRACT_LEAF;
+ return v1 > v2 ? v1 : v2;
+}
+
+// --------------------------------------------------------------------------------
+// Alignment bit twiddling macros - "alignment" must be power of 2
+//
+// AlignUp - align value to given increment, rounding up
+// AlignmentPad - amount adjusted by AlignUp
+// AlignUp(value, x) == value + AlignmentPad(value, x)
+//
+// AlignDown - align value to given increment, rounding down
+// AlignmentTrim - amount adjusted by AlignDown
+// AlignDown(value, x) == value - AlignmentTrim(value, x)
+// --------------------------------------------------------------------------------
+
+inline UINT AlignUp(UINT value, UINT alignment)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ return (value+alignment-1)&~(alignment-1);
+}
+
+#if defined(_MSC_VER)
+inline ULONG AlignUp(ULONG value, UINT alignment)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ return (value+alignment-1)&~(alignment-1);
+}
+#endif
+
+inline UINT64 AlignUp(UINT64 value, UINT alignment)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ return (value+alignment-1)&~(UINT64)(alignment-1);
+}
+
+inline UINT AlignDown(UINT value, UINT alignment)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ return (value&~(alignment-1));
+}
+
+#if defined(_MSC_VER)
+inline ULONG AlignDown(ULONG value, UINT alignment)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ return (value&~(ULONG)(alignment-1));
+}
+#endif
+
+inline UINT64 AlignDown(UINT64 value, UINT alignment)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ return (value&~(UINT64)(alignment-1));
+}
+
+inline UINT AlignmentPad(UINT value, UINT alignment)
+{
+ STATIC_CONTRACT_WRAPPER;
+ return AlignUp(value, alignment) - value;
+}
+
+#if defined(_MSC_VER)
+inline UINT AlignmentPad(ULONG value, UINT alignment)
+{
+ STATIC_CONTRACT_WRAPPER;
+ return AlignUp(value, alignment) - value;
+}
+#endif
+
+inline UINT AlignmentPad(UINT64 value, UINT alignment)
+{
+ STATIC_CONTRACT_WRAPPER;
+ return (UINT) (AlignUp(value, alignment) - value);
+}
+
+inline UINT AlignmentTrim(UINT value, UINT alignment)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ return value&(alignment-1);
+}
+
+#ifndef HOST_UNIX
+// For Unix this and the previous function get the same types.
+// So, exclude this one.
+inline UINT AlignmentTrim(ULONG value, UINT alignment)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ return value&(alignment-1);
+}
+#endif // HOST_UNIX
+
+inline UINT AlignmentTrim(UINT64 value, UINT alignment)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ return ((UINT)value)&(alignment-1);
+}
+
+#endif // CLRTYPES_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// Contract.h
+//
+
+// ! I am the owner for issues in the contract *infrastructure*, not for every
+// ! CONTRACT_VIOLATION dialog that comes up. If you interrupt my work for a routine
+// ! CONTRACT_VIOLATION, you will become the new owner of this file.
+//--------------------------------------------------------------------------------
+// CONTRACTS - User Reference
+//
+// A CONTRACT is a container for a set of checked declarations about a
+// function. Besides giving developers a "laundry list" of checks to
+// make checking more complete, contracts compile these checks
+// as hidden annotations into the checked executable that our static scanner
+// uses to detect violations automatically.
+//
+// Contracts can be dynamic or static. Dynamic contracts perform runtime checks
+// as well as being visible to the static scanner. Static contracts generate no
+// runtime code but are still visible to the scanner. Dynamic contracts are
+// preferred unless perf or other considerations preclude them.
+//
+// The following annotations can appear in contracts:
+//
+//
+// THROWS an exception might be thrown out of the function
+// -or- NOTHROW an exception will NOT be thrown out of the function
+//
+//
+//
+// INJECT_FAULT(statement) function might require its caller to handle an OOM
+// -or- FAULT_FORBID function will NOT require its caller to handle an OOM
+//
+//
+//
+// GC_TRIGGERS the function can trigger a GC
+// -or- GC_NOTRIGGER the function will never trigger a GC provided its
+// called in coop mode.
+//
+//
+// MODE_COOPERATIVE the function requires Cooperative GC mode on entry
+// -or- MODE_PREEMPTIVE the function requires Preemptive GC mode on entry
+// -or- MODE_ANY the function can be entered in either mode
+//
+// LOADS_TYPE(level) the function promises not to load any types beyond "level"
+//
+// CAN_TAKE_LOCK the function has a code path that takes a lock
+// _or_ (CAN_TAKE_LOCK and CANNOT_RETAKE_LOCK)
+// the function has a code path that takes a lock, but never tries to reenter
+// locks held at the time this function was called.
+// -or- CANNOT_TAKE_LOCK the function will never allow a lock to be taken
+// -or- the default is WRAPPER(CAN_TAKE_LOCK). i.e., if any callees take locks,
+// then it's ok for this function to as well. If LIMITED_METHOD_CONTRACT is specified,
+// however, then CANNOT_TAKE_LOCK is assumed.
+//
+//
+// SUPPORTS_DAC The function has been written to be callable from out-of-process using DAC.
+// In builds where DACCESS_COMPILE is defined, such functions can only call
+// other such functions (and a few primitives like new). Functions that support
+// DAC must be carefully written to conform to the rules in daccess.h.
+//
+// SUPPORTS_DAC_HOST_ONLY The function and its call graph has been written to be callable from out of process
+// using DAC, but it differs from SUPPORTS_DAC in that these functions won't perform
+// any marshalling. Because it does no marshalling, SUPPORTS_DAC_HOST_ONLY functions
+// and their call graph won't be checked by DacCop. This should only be used by utility
+// functions which will never marshal anything.
+//
+// PRECONDITION(X) - generic CHECK or BOOL expression which should be true
+// on function entry
+//
+// POSTCONDITION(X) - generic CHECK or BOOL expression which should be true
+// on function entry. Note that variable RETVAL will be
+// available for use in the expression.
+//
+//
+// INSTANCE_CHECK - equivalent of:
+// PRECONDITION(CheckPointer(this));
+// POSTCONDITION(CheckInvariant(this));
+// INSTANCE_CHECK_NULL - equivalent of:
+// PRECONDITION(CheckPointer(this, NULL_OK));
+// POSTCONDITION(CheckInvariant(this, NULL_OK));
+// CONSTRUCTOR_CHECK - equivalent of:
+// POSTCONDITION(CheckPointer(this));
+// DESTRUCTOR_CHECK - equivalent of:
+// PRECONDITION(CheckPointer(this));
+//
+//
+//
+//
+// Contracts come in the following flavors:
+//
+// Dynamic:
+// CONTRACTL the standard version used for all dynamic contracts
+// except those including postconditions.
+//
+// CONTRACT(rettype) an uglier version of CONTRACTL that's unfortunately
+// needed to support postconditions. You must specify
+// the correct return type and it cannot be "void."
+// (Use CONTRACT_VOID instead) You must use the
+// RETURN macro rather than the "return" keyword.
+//
+// CONTRACT_VOID you can't supply "void" to a CONTRACT - use this
+// instead.
+//
+// Static:
+// LIMITED_METHOD_CONTRACT
+// A static contract equivalent to NOTHROW/GC_NOTRIGGER/FORBID_FAULT/MODE_ANY.
+// Use only for trivial functions that call only functions with LIMITED_METHOD_CONTRACTs
+// (as long as there is no cycle that may introduce infinite recursion).
+//
+// STATIC_CONTRACT_THROWS
+// STATIC_CONTRACT_NOTHROW
+// STATIC_CONTRACT_GC_TRIGGERS
+// STATIC_CONTRACT_GCNOTRIGGER
+// STATIC_CONTRACT_FAULT
+// STATIC_CONTRACT_FORBID_FAULT
+// use to implement statically checkable contracts
+// when runtime contracts cannot be used.
+//
+//
+// WRAPPER(annotation)
+//
+// When a function does not explicitly caused a condition, use the WRAPPER macro around
+// the declaration. This implies that the function is dependent on the functions it calls
+// for its behaviour, and guarantees nothing.
+//
+//
+// CONTRACT_VIOLATION(violationmask):
+//
+// A bandaid used to suppress contract assertions. A contract violation
+// is always a bug and you're expected to remove it before shipping.
+// If a violation cannot be fixed immediately, however, it's better
+// to use this on the offending callsite than to disable a contract entirely.
+//
+// The violationmask can be one or more of the following OR'd together.
+//
+// ThrowsViolation
+// GCViolation
+// ModeViolation
+// FaultViolation
+// FaultNotFatal
+// HostViolation
+// LoadsTypeViolation
+// TakesLockViolation
+//
+// The associated assertion will be suppressed until you leave the scope
+// containing the CONTRACT_VIOLATION. Note, however, that any called
+// function that redeclares the associated annotation reinstates
+// the assert for the scope of *its* call. This prevents a CONTRACT_VIOLATION
+// placed at the root of a calltree from decimating our entire protection.
+//
+//
+// PERMANENT_CONTRACT_VIOLATION(violationmask, permanentContractViolationReason):
+//
+// Like a CONTRACT_VIOLATION but also indicates that the violation was a deliberate decision
+// and we don't plan on removing the violation in the next release. The reason
+// for the violation should be given as the second parameter to the macro. Reasons
+// are currently for documentation purposes only and do not have an effect on the binary.
+// Valid values are listed below in the definition of PermanentContractViolationReason.
+//
+//
+// CONDITIONAL_CONTRACT_VIOLATION(violationmask, condition):
+//
+// Similar to CONTRACT_VIOLATION, but only suppresses the contract if the
+// condition evaluates to non-zero. The need for this macro should be very
+// rare, but it can be useful if a contract should be suppressed based on a
+// condition known only at run-time. For example, if a particular test causes
+// call sequences never expected by real scenarios, you may want to suppress
+// resulting violations, but only when that test is run.
+//
+// WRAPPER_NO_CONTRACT
+//
+// A do-nothing contract used by functions that trivially wrap another.
+//
+//
+// "LEGACY" stuff - these features have been mostly superceded by better solutions
+// so their use should be discouraged.
+//
+//
+// DISABLED(annotation)
+//
+// Indicates that a condition is supposed to be checked but is being suppressed
+// due to some temporary bug. The more surgical CONTRACT_VIOLATION is
+// preferred over DISABLED.
+//
+// UNCHECKED(annotation)
+//
+// Indicates that a condition is supposed to be checked but is being suppressed
+// due for perf reasons. Use STATIC_CONTRACT over this.
+//
+//
+// Default values:
+// If you don't specify certain annotaions, you get defaults.
+// - THROWS/NOTHROW defaults to THROWS
+// - GCTRIGGERS/GCNOTRIGGER defaults to GCTRIGGERS within the VM directory
+// and to no check otherwise
+// - INJECT/FORBID_FAULT defaults to no check
+// - MODE defaults to MODE_ANY
+//
+// The problem is that defaults don't work well with static contracts.
+// The scanner will always treat a missing annotation as DISABLED.
+// New code should not rely on defaults. Explicitly state your invariants.
+//
+//
+//--------------------------------------------------------------------------------
+
+
+
+
+#ifndef CONTRACT_H_
+#define CONTRACT_H_
+
+#ifdef _MSC_VER
+#pragma warning(disable:4189) //local variable is initialized but not referenced
+#endif
+
+
+// We only enable contracts in _DEBUG builds
+#if defined(_DEBUG) && !defined(DISABLE_CONTRACTS) && !defined(JIT_BUILD)
+#define ENABLE_CONTRACTS_DATA
+#endif
+
+// Also, we won't enable contracts if this is a DAC build.
+#if defined(ENABLE_CONTRACTS_DATA) && !defined(DACCESS_COMPILE) && !defined(CROSS_COMPILE)
+#define ENABLE_CONTRACTS
+#endif
+
+// Finally, only define the implementaiton parts of contracts if this isn't a DAC build.
+#if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS)
+#define ENABLE_CONTRACTS_IMPL
+#endif
+
+#include "specstrings.h"
+#include "clrtypes.h"
+#include "malloc.h"
+#include "check.h"
+#include "debugreturn.h"
+#include "staticcontract.h"
+
+#ifdef ENABLE_CONTRACTS_DATA
+
+#include "eh.h"
+
+// We chain these onto a stack to give us a stack trace of contract assertions (useful
+// when the bug report doesn't contain valid symbols)
+
+struct ContractStackRecord
+{
+ ContractStackRecord *m_pNext;
+ const char *m_szFunction;
+ const char *m_szFile;
+ int m_lineNum;
+ UINT m_testmask; // Bitmask of Contract::TestEnum bitsf
+ const char *m_construct; // The syntactic construct that pushed this thing
+};
+
+class CrstBase;
+
+// The next few enums / structs are used to keep track of all kinds of locks
+// currently taken by the current thread (crsts, spinlocks, CLR critical sections).
+// Across the VM, there are still multiple counts of locks. The lock counts in these
+// contract structs are used to verify consistency of lock take/release in EE code, and
+// for contracts. Both user and EE locks are tracked here, but it's EE code consistency
+// we're verifying. The Thread object keeps its own counts as well, primarily of user
+// locks for implementing thread abort & escalation policy. We tried to have the Thread
+// counts also be used for consistency checking, but that doesn't work. Thread counters
+// have the following behavior that hurts our internal consistency checks:
+// - They only count user locks.
+// - Counters are reset & restored as we leave and return to AppDomains
+
+// An array of these is stored in DbgStateLockData::m_rgTakenLockInfos
+// to remember which locks we've taken. If you hit an assert that
+// indicates we're exiting locks in the wrong order, or that locks were
+// taken when we expected none to be taken, then you can use
+// DbgStateLockData::m_rgTakenLockInfos to see the locks we know about.
+struct TakenLockInfo
+{
+ // Generally, this will be a pointer to the lock, but really it's just
+ // a value that identifies which lock is taken. Ya see, sometimes we don't
+ // have a lock pointer handy (e.g., if the lock is based on a GC object,
+ // which has no persistent object pointer we can use). Look at the source
+ // indicated by m_szFile / m_lineNum to see what was specified as m_pvLock.
+ //
+ // A common case is that the lock is just a Crst, so to aid debugging, we
+ // also include a statically typed version of this pointer (m_pCrstBase) just
+ // for Crsts. Again, you'll want look at m_szFile / m_lineNum to see how to
+ // interpret this union.
+ union
+ {
+ void * m_pvLock;
+ CrstBase * m_pCrstBase;
+ };
+
+ // File & line of the *LOCK_TAKEN* macro that added this lock to our list
+ const char * m_szFile;
+ int m_lineNum;
+};
+
+enum DbgStateLockType
+{
+ // EE locks (used to sync EE structures). These do not include
+ // CRST_HOST_BREAKABLE Crsts, and are thus not held while managed
+ // code runs
+ kDbgStateLockType_EE,
+
+ // CRST_HOST_BREAKABLE Crsts. These can be held while arbitrary
+ // managed code runs.
+ kDbgStateLockType_HostBreakableCrst,
+
+ // User locks (e.g., Monitor.Enter, ReaderWriterLock class)
+ kDbgStateLockType_User,
+
+ // add more lock types here
+
+ kDbgStateLockType_Count
+};
+
+// This keeps track of how many locks, and which locks, are currently owned
+// by the current thread. There is one instance of this structure per
+// thread (no EE Thread object required). This is in contrast to the
+// ClrDebugState structure, which is instantiated once per function
+// on the stack. Reason is that ClrDebugState resets its state on exit
+// of function (Contract destructor reinstates previous ClrDebugState), whereas
+// we want DbgStateLockData to persist across function enters & exits.
+struct DbgStateLockData
+{
+ // When a lock is taken, we keep track of its pointer and file/line# when it
+ // was added in a static-size array DbgStateLockData::m_rgTakenLockInfos. This is
+ // the size of that array, and therefore indicates the maximum number of locks we
+ // expect one thread to hold at the same time. If we should exceed this limit,
+ // we'll lose this data for the latter locks that exceed this limit
+ // (though still maintaining an accurate *count* of locks).
+ static const int kMaxAllowedSimultaneousLocks = 20;
+
+ // Count of locks taken, separately by type
+ UINT m_rgcLocksTaken[kDbgStateLockType_Count];
+
+ // List of the specific locks that have been taken (all DbgStateLockTypes
+ // intermingled), in the order they were taken. If we exceed the elements
+ // in the array, we just won't track the latter locks in here (though they are
+ // included in the counts above)
+ TakenLockInfo m_rgTakenLockInfos[kMaxAllowedSimultaneousLocks];
+
+ void SetStartingValues();
+ void LockTaken(DbgStateLockType dbgStateLockType,
+ UINT cEntrances,
+ void * pvLock,
+ _In_z_ const char * szFunction,
+ _In_z_ const char * szFile,
+ int lineNum);
+ void LockReleased(DbgStateLockType dbgStateLockType, UINT cExits, void * pvLock);
+ UINT GetLockCount(DbgStateLockType dbgStateLockType);
+ UINT GetCombinedLockCount();
+};
+
+// This struct contains all lock contract information. It is created and destroyed along with
+// ClrDebugState. m_pLockData points to a DbgStateLockData object that is allocated per thread
+// and persists across function enters and exists.
+struct DbgStateLockState
+{
+private:
+ // Count of locks taken at the time the function with CANNOT_RETAKE_LOCK contract
+ // was called
+ UINT m_cLocksEnteringCannotRetakeLock;
+
+ DbgStateLockData * m_pLockData; // How many and which locks are currently taken on this thread
+
+public:
+ void SetStartingValues();
+ void OnEnterCannotRetakeLockFunction();
+ BOOL IsLockRetaken(void * pvLock);
+ BOOL IsSafeToRelease(UINT cReleases);
+ void SetDbgStateLockData(DbgStateLockData * pDbgStateLockData);
+ DbgStateLockData * GetDbgStateLockData();
+};
+
+
+#define CONTRACT_BITMASK_OK_TO_THROW 0x1 << 0
+#define CONTRACT_BITMASK_FAULT_FORBID 0x1 << 1
+#define CONTRACT_BITMASK_HOSTCALLS 0x1 << 2
+#define CONTRACT_BITMASK_SOTOLERANT 0x1 << 3
+#define CONTRACT_BITMASK_DEBUGONLY 0x1 << 4
+#define CONTRACT_BITMASK_SONOTMAINLINE 0x1 << 5
+#define CONTRACT_BITMASK_OK_TO_LOCK 0x1 << 6
+#define CONTRACT_BITMASK_OK_TO_RETAKE_LOCK 0x1 << 7
+
+
+#define CONTRACT_BITMASK_IS_SET(whichbit) ((m_flags & (whichbit)) != 0)
+#define CONTRACT_BITMASK_SET(whichbit) (m_flags |= (whichbit))
+#define CONTRACT_BITMASK_RESET(whichbit) (m_flags &= ~(whichbit))
+#define CONTRACT_BITMASK_UPDATE(whichbit, value) ((value)?CONTRACT_BITMASK_SET(whichbit):CONTRACT_BITMASK_RESET(whichbit))
+
+struct ClrDebugState
+{
+private:
+ UINT_PTR m_flags;
+ UINT_PTR m_violationmask; // Current CONTRACT_VIOLATIONS in effect
+ ContractStackRecord *m_pContractStackTrace;
+ UINT m_GCNoTriggerCount;
+ UINT m_GCForbidCount;
+ UINT m_maxLoadTypeLevel; // taken from enum ClassLoadLevel
+ BOOL m_allowGetThread; // TRUE if GetThread() is ok in this scope
+ DbgStateLockState m_LockState;
+
+public:
+ // Use an explicit Init rather than ctor as we don't want automatic
+ // construction of the ClrDebugState embedded inside the contract.
+ void SetStartingValues()
+ {
+ m_violationmask = 0; // No violations allowed
+
+ // Default is we're in a THROWS scope. This is not ideal, but there are
+ // just too many places that I'd have to go clean up right now
+ // (hundreds) in order to make this FALSE by default.
+ // Faults not forbidden (an unfortunate default but
+ // we'd never get this debug infrastructure bootstrapped otherwise.)
+ // We start out in SO-tolerant mode and must probe before entering SO-intolerant
+ // any global state updates.
+ // Initial mode is non-debug until we say otherwise
+ // Everthing defaults to mainline
+ // By default, GetThread() is perfectly fine to call
+ // By default, it's ok to take a lock (or call someone who does)
+ m_flags = CONTRACT_BITMASK_OK_TO_THROW|
+ CONTRACT_BITMASK_HOSTCALLS|
+ CONTRACT_BITMASK_SOTOLERANT|
+ CONTRACT_BITMASK_OK_TO_LOCK|
+ CONTRACT_BITMASK_OK_TO_RETAKE_LOCK;
+
+ m_pContractStackTrace = NULL; // At top of stack, no contracts in force
+ m_GCNoTriggerCount = 0;
+ m_GCForbidCount = 0;
+
+ m_maxLoadTypeLevel = ((UINT)(-1)); // ideally CLASS_LOAD_LEVEL_FINAL but we don't have access to that #define, so
+ // the max integer value will do as a substitute.
+
+ m_allowGetThread = TRUE; // By default, GetThread() is perfectly fine to call
+
+ m_LockState.SetStartingValues();
+ }
+
+ void CheckOkayToThrow(_In_z_ const char *szFunction, _In_z_ const char *szFile, int lineNum); // Asserts if its not okay to throw.
+ BOOL CheckOkayToThrowNoAssert(); // Returns if OK to throw
+
+ //--//
+
+ UINT_PTR* ViolationMaskPtr()
+ {
+ return &m_violationmask;
+ }
+
+ UINT_PTR ViolationMask()
+ {
+ return m_violationmask;
+ }
+
+ void ViolationMaskSet( UINT_PTR value )
+ {
+ m_violationmask |= value;
+ }
+
+ void ViolationMaskReset( UINT_PTR value )
+ {
+ m_violationmask &= ~value;
+ }
+
+ //--//
+
+ BOOL IsOkToThrow()
+ {
+ return CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_OK_TO_THROW);
+ }
+
+ void SetOkToThrow()
+ {
+ CONTRACT_BITMASK_SET(CONTRACT_BITMASK_OK_TO_THROW);
+ }
+
+ BOOL SetOkToThrow( BOOL value )
+ {
+ BOOL prevState = CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_OK_TO_THROW);
+ CONTRACT_BITMASK_UPDATE(CONTRACT_BITMASK_OK_TO_THROW, value);
+ return prevState;
+ }
+
+ void ResetOkToThrow()
+ {
+ CONTRACT_BITMASK_RESET(CONTRACT_BITMASK_OK_TO_THROW);
+ }
+ //--//
+
+ BOOL IsFaultForbid()
+ {
+ return CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_FAULT_FORBID);
+ }
+
+
+ void SetFaultForbid()
+ {
+ CONTRACT_BITMASK_SET(CONTRACT_BITMASK_FAULT_FORBID);
+ }
+
+ BOOL SetFaultForbid(BOOL value)
+ {
+ BOOL prevState = CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_FAULT_FORBID);
+ CONTRACT_BITMASK_UPDATE(CONTRACT_BITMASK_FAULT_FORBID, value);
+ return prevState;
+ }
+
+ void ResetFaultForbid()
+ {
+ CONTRACT_BITMASK_RESET(CONTRACT_BITMASK_FAULT_FORBID);
+ }
+
+ //--//
+ BOOL IsHostCaller()
+ {
+ return CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_HOSTCALLS);
+ }
+
+ void SetHostCaller()
+ {
+ CONTRACT_BITMASK_SET(CONTRACT_BITMASK_HOSTCALLS);
+ }
+
+
+ BOOL SetHostCaller(BOOL value)
+ {
+ BOOL prevState = CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_HOSTCALLS);
+ CONTRACT_BITMASK_UPDATE(CONTRACT_BITMASK_HOSTCALLS,value);
+ return prevState;
+ }
+
+ void ResetHostCaller()
+ {
+ CONTRACT_BITMASK_RESET(CONTRACT_BITMASK_HOSTCALLS);
+ }
+
+ //--//
+ BOOL IsDebugOnly()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ return CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_DEBUGONLY);
+ }
+
+ void SetDebugOnly()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ CONTRACT_BITMASK_SET(CONTRACT_BITMASK_DEBUGONLY);
+ }
+
+ BOOL SetDebugOnly(BOOL value)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ BOOL prevState = CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_DEBUGONLY);
+ CONTRACT_BITMASK_UPDATE(CONTRACT_BITMASK_DEBUGONLY,value);
+ return prevState;
+ }
+
+ void ResetDebugOnly()
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ CONTRACT_BITMASK_RESET(CONTRACT_BITMASK_DEBUGONLY);
+ }
+
+ //--//
+ BOOL IsOkToLock()
+ {
+ return CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_OK_TO_LOCK);
+ }
+
+ void SetOkToLock()
+ {
+ CONTRACT_BITMASK_SET(CONTRACT_BITMASK_OK_TO_LOCK);
+ }
+
+ BOOL SetOkToLock( BOOL value )
+ {
+ BOOL prevState = CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_OK_TO_LOCK);
+ CONTRACT_BITMASK_UPDATE(CONTRACT_BITMASK_OK_TO_LOCK, value);
+ return prevState;
+ }
+
+ void ResetOkToLock()
+ {
+ CONTRACT_BITMASK_RESET(CONTRACT_BITMASK_OK_TO_LOCK);
+ }
+
+ //--//
+ BOOL IsOkToRetakeLock()
+ {
+ return CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_OK_TO_RETAKE_LOCK);
+ }
+
+ void ResetOkToRetakeLock()
+ {
+ CONTRACT_BITMASK_RESET(CONTRACT_BITMASK_OK_TO_RETAKE_LOCK);
+ }
+
+
+ //--//
+ void LinkContractStackTrace( ContractStackRecord* pContractStackTrace )
+ {
+ pContractStackTrace->m_pNext = m_pContractStackTrace;
+
+ m_pContractStackTrace = pContractStackTrace;
+ }
+
+ ContractStackRecord* GetContractStackTrace()
+ {
+ return m_pContractStackTrace;
+ }
+
+ void SetContractStackTrace(ContractStackRecord* pContractStackTrace )
+ {
+ m_pContractStackTrace = pContractStackTrace;
+ }
+
+ //--//
+
+ UINT GetGCNoTriggerCount()
+ {
+ return m_GCNoTriggerCount;
+ }
+
+ void DecrementGCNoTriggerCount()
+ {
+ m_GCNoTriggerCount--;
+ }
+
+ void IncrementGCNoTriggerCount()
+ {
+ m_GCNoTriggerCount++;
+ }
+
+
+ UINT GetGCForbidCount()
+ {
+ return m_GCForbidCount;
+ }
+
+ void DecrementGCForbidCount()
+ {
+ m_GCForbidCount--;
+ }
+
+ void IncrementGCForbidCount()
+ {
+ m_GCForbidCount++;
+ }
+
+ UINT GetMaxLoadTypeLevel()
+ {
+ return m_maxLoadTypeLevel;
+ }
+
+ void SetMaxLoadTypeLevel(UINT newLevel)
+ {
+ m_maxLoadTypeLevel = newLevel;
+ }
+
+ //--//
+
+ void SetDbgStateLockData(DbgStateLockData * pDbgStateLockData)
+ {
+ m_LockState.SetDbgStateLockData(pDbgStateLockData);
+ }
+
+ DbgStateLockData * GetDbgStateLockData()
+ {
+ return m_LockState.GetDbgStateLockData();
+ }
+
+ void OnEnterCannotRetakeLockFunction()
+ {
+ m_LockState.OnEnterCannotRetakeLockFunction();
+ }
+
+ void CheckOkayToLock(_In_z_ const char *szFunction, _In_z_ const char *szFile, int lineNum); // Asserts if its not okay to lock
+ BOOL CheckOkayToLockNoAssert(); // Returns if OK to lock
+ void LockTaken(DbgStateLockType dbgStateLockType,
+ UINT cEntrances,
+ void * pvLock,
+ _In_z_ const char * szFunction,
+ _In_z_ const char * szFile,
+ int lineNum);
+ void LockReleased(DbgStateLockType dbgStateLockType, UINT cExits, void * pvLock);
+ UINT GetLockCount(DbgStateLockType dbgStateLockType);
+ UINT GetCombinedLockCount();
+};
+
+#endif // ENABLE_CONTRACTS
+
+#ifdef ENABLE_CONTRACTS_IMPL
+// Create ClrDebugState.
+// This routine is not allowed to return NULL. If it can't allocate the memory needed,
+// it should return a pointer to a global static ClrDebugState that indicates
+// that debug assertions should be skipped.
+ClrDebugState *CLRInitDebugState();
+ClrDebugState *GetClrDebugState(BOOL fAlloc = TRUE);
+
+extern thread_local ClrDebugState* t_pClrDebugState;
+
+// This function returns a ClrDebugState if one has been created, but will not create one itself.
+inline ClrDebugState *CheckClrDebugState()
+{
+ STATIC_CONTRACT_LIMITED_METHOD;
+ return t_pClrDebugState;
+}
+
+void CONTRACT_ASSERT(const char *szElaboration,
+ UINT whichTest,
+ UINT whichTestMask,
+ const char *szFunction,
+ const char *szFile,
+ int lineNum
+ );
+
+#endif
+
+// This needs to be defined up here b/c it is used by ASSERT_CHECK which is used by the contract impl
+#ifdef _DEBUG
+#ifdef ENTER_DEBUG_ONLY_CODE
+#undef ENTER_DEBUG_ONLY_CODE
+#endif
+#ifdef LEAVE_DEBUG_ONLY_CODE
+#undef LEAVE_DEBUG_ONLY_CODE
+#endif
+
+#ifdef ENABLE_CONTRACTS_IMPL
+// This can only appear in a debug function so don't define it non-debug
+class DebugOnlyCodeHolder
+{
+public:
+ // We use GetClrDebugState on entry, but CheckClrDebugState on Leave
+ // That way we make sure to create one if we need to set state, but
+ // we don't recreated one on exit if its been deleted.
+ DEBUG_NOINLINE void Enter()
+ {
+ SCAN_SCOPE_BEGIN;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ m_pClrDebugState = GetClrDebugState();
+ if (m_pClrDebugState)
+ {
+ m_oldDebugOnlyValue = m_pClrDebugState->IsDebugOnly();
+ m_pClrDebugState->SetDebugOnly();
+ }
+ }
+
+ DEBUG_NOINLINE void Leave()
+ {
+ SCAN_SCOPE_END;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ m_pClrDebugState = CheckClrDebugState();
+ if (m_pClrDebugState)
+ {
+ m_pClrDebugState->SetDebugOnly( m_oldDebugOnlyValue );
+ }
+ }
+
+private:
+BOOL m_oldDebugOnlyValue;
+ClrDebugState *m_pClrDebugState;
+};
+
+#define ENTER_DEBUG_ONLY_CODE \
+ DebugOnlyCodeHolder __debugOnlyCodeHolder; \
+ __debugOnlyCodeHolder.Enter();
+
+#define LEAVE_DEBUG_ONLY_CODE \
+ __debugOnlyCodeHolder.Leave();
+
+
+class AutoCleanupDebugOnlyCodeHolder : public DebugOnlyCodeHolder
+{
+public:
+ DEBUG_NOINLINE AutoCleanupDebugOnlyCodeHolder()
+ {
+ SCAN_SCOPE_BEGIN;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ Enter();
+ };
+
+ DEBUG_NOINLINE ~AutoCleanupDebugOnlyCodeHolder()
+ {
+ SCAN_SCOPE_END;
+
+ Leave();
+ };
+};
+
+#define DEBUG_ONLY_FUNCTION \
+ STATIC_CONTRACT_DEBUG_ONLY; \
+ AutoCleanupDebugOnlyCodeHolder __debugOnlyCodeHolder;
+
+#define DEBUG_ONLY_REGION() \
+ AutoCleanupDebugOnlyCodeHolder __debugOnlyCodeHolder;
+
+
+#define BEGIN_DEBUG_ONLY_CODE \
+ { \
+ AutoCleanupDebugOnlyCodeHolder __debugOnlyCodeHolder;
+
+#define END_DEBUG_ONLY_CODE \
+ }
+
+#else // ENABLE_CONTRACTS_IMPL
+#define DEBUG_ONLY_FUNCTION STATIC_CONTRACT_DEBUG_ONLY
+#define DEBUG_ONLY_REGION()
+#define BEGIN_DEBUG_ONLY_CODE
+#define END_DEBUG_ONLY_CODE
+#define ENTER_DEBUG_ONLY_CODE
+#define LEAVE_DEBUG_ONLY_CODE
+#endif
+
+#else // _DEBUG
+#define DEBUG_ONLY_REGION()
+#endif
+
+
+#ifdef ENABLE_CONTRACTS_IMPL
+
+// These helpers encapsulate our access to our FLS debug state. To improve
+// contract perf, we'll soon move these to a private alloced block
+// so we can reuse the pointer instead of constantly refetching it.
+// Thus, these helpers are just to bridge this transition.
+inline LPVOID GetViolationMask()
+{
+ ClrDebugState *pState = CheckClrDebugState();
+ if (pState)
+ {
+ return (LPVOID)pState->ViolationMask();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// This is the default binding of the MAYBETEMPLATE identifier,
+// used in the RETURN macro
+template <int DUMMY>
+class ___maybetemplate
+{
+ public:
+ FORCEINLINE void *operator new (size_t size)
+ {
+ return NULL;
+ }
+};
+
+// This is an abstract base class for contracts. The main reason we have this is so that the dtor for many derived class can
+// be performant. If this class was not abstract and had a dtor, then the dtor for the derived class adds EH overhead (even if the derived
+// class did not anything special in its dtor)
+class BaseContract
+{
+ // Really private, but used by macros
+ public:
+
+ // We use a single integer to specify all the settings for intrinsic tests
+ // such as THROWS and GC_TRIGGERS. The compiler should be able to fold all
+ // these clauses into a single constant.
+ //
+ // The value "0" is significant as this is what the entire mask will be initialized to
+ // in the absence of any clauses. Hence, whichever value is assigned "0" will be the
+ // default setting for the test.
+ //
+ // Also, there must be a "disabled" setting for each category in order to support
+ // the DISABLED macro.
+ enum TestEnum
+ {
+ THROWS_Mask = 0x00000003,
+ THROWS_Yes = 0x00000000, // the default
+ THROWS_No = 0x00000001,
+ THROWS_Disabled = 0x00000002,
+
+ GC_Mask = 0x0000000C,
+ GC_Triggers = 0x00000000, // the default
+ GC_NoTrigger = 0x00000004,
+ GC_Disabled = 0x00000008,
+
+ FAULT_Mask = 0x00000030,
+ FAULT_Disabled = 0x00000000, // the default
+ FAULT_Inject = 0x00000010,
+ FAULT_Forbid = 0x00000020,
+
+ MODE_Mask = 0x000000C0,
+ MODE_Disabled = 0x00000000, // the default
+ MODE_Preempt = 0x00000040,
+ MODE_Coop = 0x00000080,
+
+ DEBUG_ONLY_Yes = 0x00000400, // code runs under debug only
+
+ SO_MAINLINE_No = 0x00000800, // code is not part of our mainline SO scenario
+
+ // Any place where we can't safely call into the host should have a HOST_NoCalls contract
+ HOST_Mask = 0x00003000,
+ HOST_Calls = 0x00002000,
+ HOST_NoCalls = 0x00001000,
+ HOST_Disabled = 0x00000000, // the default
+
+ // These enforce the CAN_TAKE_LOCK / CANNOT_TAKE_LOCK contracts
+ CAN_TAKE_LOCK_Mask = 0x00060000,
+ CAN_TAKE_LOCK_Yes = 0x00020000,
+ CAN_TAKE_LOCK_No = 0x00040000,
+ CAN_TAKE_LOCK_Disabled = 0x00000000, // the default
+
+ // These enforce the CANNOT_RETAKE_LOCK contract
+ CAN_RETAKE_LOCK_No = 0x00080000,
+ CAN_RETAKE_LOCK_No_Disabled = 0x00000000, // the default
+
+ PRECONDITION_Used = 0x00010000, // a PRECONDITION appeared inside the contract
+
+ // IMPORTANT!!! LOADS_TYPE_Mask and LOADS_TYPE_Shift must be kept in sync.
+ LOADS_TYPE_Mask = 0x00f00000, // the max loadstype level + 1 ("+1" because 0 is reserved for the default which is "disabled")
+ LOADS_TYPE_Shift = 20, // # of bits to right-shift to get loadstype bits to rightmost position.
+ LOADS_TYPE_Disabled = 0x00000000, // the default
+
+ ALL_Disabled = THROWS_Disabled|GC_Disabled|FAULT_Disabled|MODE_Disabled|LOADS_TYPE_Disabled|
+ HOST_Disabled|CAN_TAKE_LOCK_Disabled|CAN_RETAKE_LOCK_No_Disabled
+
+ };
+
+ enum Operation
+ {
+ Setup = 0x01,
+ Preconditions = 0x02,
+ Postconditions = 0x04,
+ };
+
+
+ NOTHROW_DECL BaseContract() : m_testmask(0), m_pClrDebugState(NULL)
+ {
+ }
+ NOTHROW_DECL void Restore()
+ {
+ // m_pClrDebugState is setup in BaseContract::DoChecks. If an SO happens after the
+ // BaseContract object is constructed but before DoChecks is invoked, m_pClrDebugState
+ // will remain NULL (which is what it is set to in the BaseContract ctor).
+ //
+ // Thus, we should check for it being NULL before dereferencing it.
+ if (m_pClrDebugState)
+ {
+ // Backout all changes to debug state.
+ *m_pClrDebugState = m_IncomingClrDebugState;
+ }
+ }
+
+ void DoChecks(UINT testmask, _In_z_ const char *szFunction, _In_z_ const char *szFile, int lineNum);
+ void Disable()
+ {
+ }
+ BOOL CheckFaultInjection();
+
+ protected:
+ UINT m_testmask;
+ // Override this function in any derived class to indicate that you have defined a destructor for that class
+ // and that dtor calls Restore()
+ virtual void DestructorDefinedThatCallsRestore() = 0;
+
+
+ protected:
+ ClrDebugState *m_pClrDebugState;
+ ClrDebugState m_IncomingClrDebugState;
+
+ ContractStackRecord m_contractStackRecord;
+
+ public:
+ // --------------------------------------------------------------------------------
+ // These classes and declarations are used to implement our fake return keyword.
+ // --------------------------------------------------------------------------------
+
+ // ___box is used to protect the "detected" return value from being combined with other parts
+ // of the return expression after we have processed it. This can happen if the return
+ // expression is a non-parenthesized expression with an operator of lower precedence than
+ // ">".
+ //
+ // If you have such a case (and see this class listed in an error message),
+ // parenthesize your return value expression.
+ template <typename T>
+ class Box__USE_PARENS_WITH_THIS_EXPRESSION
+ {
+ const T &value;
+
+ public:
+
+ FORCEINLINE Box__USE_PARENS_WITH_THIS_EXPRESSION(const T &value)
+ : value(value)
+ {
+ }
+
+ FORCEINLINE const T& Unbox()
+ {
+ return value;
+ }
+ };
+
+ // PseudoTemplate is a class which can be instantated with a template-like syntax, resulting
+ // in an expression which simply boxes a following value in a Box
+
+ template <typename T>
+ class PseudoTemplate
+ {
+ public:
+ FORCEINLINE void *operator new (size_t size)
+ {
+ return NULL;
+ }
+
+ FORCEINLINE Box__USE_PARENS_WITH_THIS_EXPRESSION<T> operator>(const T &value)
+ {
+ return Box__USE_PARENS_WITH_THIS_EXPRESSION<T>(value);
+ }
+
+ FORCEINLINE PseudoTemplate operator<(int dummy)
+ {
+ return PseudoTemplate();
+ }
+ };
+
+ // Returner is used to assign the return value to the RETVAL local. Note the use of
+ // operator , because of its low precedence.
+
+ template <typename RETURNTYPE>
+ class Returner
+ {
+ RETURNTYPE &m_value;
+ BOOL m_got;
+ public:
+
+ FORCEINLINE Returner(RETURNTYPE &value)
+ : m_value(value),
+ m_got(FALSE)
+ {
+ }
+
+ template <typename T>
+ FORCEINLINE RETURNTYPE operator,(Box__USE_PARENS_WITH_THIS_EXPRESSION<T> value)
+ {
+ m_value = value.Unbox();
+ m_got = TRUE;
+ return m_value;
+ }
+
+ FORCEINLINE void operator,(___maybetemplate<0> &dummy)
+ {
+ m_got = TRUE;
+ }
+
+ FORCEINLINE BOOL GotReturn()
+ {
+ return m_got;
+ }
+ };
+
+ // This type ensures that postconditions were run via RETURN or RETURN_VOID
+ class RanPostconditions
+ {
+ public:
+ bool ran;
+ int count;
+ const char *function;
+
+ FORCEINLINE RanPostconditions(const char *function)
+ : ran(false),
+ count(0),
+ function(function)
+ {
+ }
+
+ FORCEINLINE int operator++()
+ {
+ return ++count;
+ }
+
+ FORCEINLINE ~RanPostconditions()
+ {
+ // Note: __uncaught_exception() is not a perfect check. It will return TRUE during any exception
+ // processing. So, if there is a contract called from an exception filter (like our
+ // COMPlusFrameHandler) then it will return TRUE and the saftey check below will not be performed.
+ if (!__uncaught_exception())
+ ASSERT_CHECK(count == 0 || ran, function, "Didn't run postconditions - be sure to use RETURN at the end of the function");
+ }
+
+ };
+
+ // Set contract enforcement level
+ static void SetUnconditionalContractEnforcement(BOOL enforceUnconditionally);
+
+ // Check contract enforcement
+ static BOOL EnforceContract();
+
+ private:
+ static BOOL s_alwaysEnforceContracts;
+};
+
+class Contract: public BaseContract
+{
+ // Have to override this function in any derived class to indicate that a valid destructor is defined for this class
+ virtual void DestructorDefinedThatCallsRestore(){}
+
+ public:
+ NOTHROW_DECL ~Contract()
+ {
+ Restore();
+ }
+};
+
+#endif // ENABLE_CONTRACTS_IMPL
+
+
+#ifdef _DEBUG
+
+// Valid parameters for CONTRACT_VIOLATION macro
+enum ContractViolationBits
+{
+ ThrowsViolation = 0x00000001, // suppress THROW tags in this scope
+ GCViolation = 0x00000002, // suppress GCTRIGGER tags in this scope
+ ModeViolation = 0x00000004, // suppress MODE_PREEMP and MODE_COOP tags in this scope
+ FaultViolation = 0x00000008, // suppress INJECT_FAULT assertions in this scope
+ FaultNotFatal = 0x00000010, // suppress INJECT_FAULT but not fault injection by harness
+ LoadsTypeViolation = 0x00000040, // suppress LOADS_TYPE tags in this scope
+ TakesLockViolation = 0x00000080, // suppress CAN_TAKE_LOCK tags in this scope
+ HostViolation = 0x00000100, // suppress HOST_CALLS tags in this scope
+
+ //These are not violation bits. We steal some bits out of the violation mask to serve as
+ // general flag bits.
+ CanFreeMe = 0x00010000, // If this bit is ON, the ClrDebugState was allocated by
+ // a version of utilcode that registers an Fls Callback to free
+ // the state. If this bit is OFF, the ClrDebugState was allocated
+ // by an old version of utilcode that doesn't. (And you can't
+ // assume that the old utilcode used the same allocator as the new utilcode.)
+ // (Most likely, this is because you are using an older shim with
+ // a newer mscorwks.dll)
+ //
+ // The Fls callback must only attempt to free debugstates that
+ // have this bit on.
+
+ BadDebugState = 0x00020000, // If we OOM creating the ClrDebugState, we return a pointer to
+ // a static ClrDebugState that has this bit turned on. (We don't
+ // want to slow down contracts with null tests everywhere.)
+ // Other than this specific bit, all other fields of the DebugState
+ // must be considered trash. You can stomp on them and you can bit-test them
+ // but you can't throw up any asserts based on them and you certainly
+ // can't deref any pointers stored in the bad DebugState.
+
+ AllViolation = 0xFFFFFFFF,
+};
+
+#endif
+
+#ifdef ENABLE_CONTRACTS_IMPL
+
+// Global variables allow PRECONDITION and POSTCONDITION to be used outside contracts
+static const BaseContract::Operation ___op = (Contract::Operation) (Contract::Preconditions
+ |Contract::Postconditions);
+enum {
+ ___disabled = 0
+};
+
+static UINT ___testmask;
+
+// End of global variables
+
+static int ___ran;
+
+class __SafeToUsePostCondition {
+public:
+ static int safe_to_use_postcondition() {return 0;};
+};
+
+class __YouCannotUseAPostConditionHere {
+private:
+ static int safe_to_use_postcondition() {return 0;};
+};
+
+typedef __SafeToUsePostCondition __PostConditionOK;
+
+// Uncomment the following line to disable runtime contracts completely - PRE/POST conditions will still be present
+//#define __FORCE_NORUNTIME_CONTRACTS__ 1
+
+#ifndef __FORCE_NORUNTIME_CONTRACTS__
+
+#define CONTRACT_SETUP(_contracttype, _returntype, _returnexp) \
+ _returntype RETVAL; \
+ _contracttype ___contract; \
+ Contract::Returner<_returntype> ___returner(RETVAL); \
+ Contract::RanPostconditions ___ran(__FUNCTION__); \
+ Contract::Operation ___op = Contract::Setup; \
+ BOOL ___contract_enabled = FALSE; \
+ DEBUG_ASSURE_NO_RETURN_BEGIN(CONTRACT) \
+ ___contract_enabled = Contract::EnforceContract(); \
+ enum {___disabled = 0}; \
+ if (!___contract_enabled) \
+ ___contract.Disable(); \
+ else \
+ { \
+ enum { ___CheckMustBeInside_CONTRACT = 1 }; \
+ if (0) \
+ { \
+ /* If you see an "unreferenced label" warning with this name, */\
+ /* Be sure that you have a RETURN at the end of your */ \
+ /* CONTRACT_VOID function */ \
+ ___run_postconditions_DID_YOU_FORGET_A_RETURN: \
+ if (___contract_enabled) \
+ { \
+ ___op = Contract::Postconditions; \
+ ___ran.ran = true; \
+ } \
+ else \
+ { \
+ DEBUG_OK_TO_RETURN_BEGIN(CONTRACT) \
+ ___run_return: \
+ return _returnexp; \
+ DEBUG_OK_TO_RETURN_END(CONTRACT) \
+ } \
+ } \
+ if (0) \
+ { \
+ ___run_preconditions: \
+ ___op = Contract::Preconditions; \
+ } \
+ UINT ___testmask = 0; \
+
+#define CONTRACTL_SETUP(_contracttype) \
+ _contracttype ___contract; \
+ BOOL ___contract_enabled = Contract::EnforceContract(); \
+ enum {___disabled = 0}; \
+ if (!___contract_enabled) \
+ ___contract.Disable(); \
+ else \
+ { \
+ typedef __YouCannotUseAPostConditionHere __PostConditionOK; \
+ enum { ___CheckMustBeInside_CONTRACT = 1 }; \
+ Contract::Operation ___op = Contract::Setup; \
+ enum {___disabled = 0}; \
+ if (0) \
+ { \
+ ___run_preconditions: \
+ ___op = Contract::Preconditions; \
+ } \
+ if (0) \
+ { \
+ /* define for CONTRACT_END even though we can't get here */ \
+ ___run_return: \
+ UNREACHABLE(); \
+ } \
+ UINT ___testmask = 0; \
+
+#else // #ifndef __FORCE_NORUNTIME_CONTRACTS__
+
+#define CONTRACT_SETUP(_contracttype, _returntype, _returnexp) \
+ _returntype RETVAL; \
+ Contract::Returner<_returntype> ___returner(RETVAL); \
+ Contract::RanPostconditions ___ran(__FUNCTION__); \
+ Contract::Operation ___op = Contract::Setup; \
+ DEBUG_ASSURE_NO_RETURN_BEGIN(CONTRACT) \
+ BOOL ___contract_enabled = Contract::EnforceContract(); \
+ enum {___disabled = 0}; \
+ { \
+ enum { ___CheckMustBeInside_CONTRACT = 1 }; \
+ if (0) \
+ { \
+ /* If you see an "unreferenced label" warning with this name, */\
+ /* Be sure that you have a RETURN at the end of your */ \
+ /* CONTRACT_VOID function */ \
+ ___run_postconditions_DID_YOU_FORGET_A_RETURN: \
+ if (___contract_enabled) \
+ { \
+ ___op = Contract::Postconditions; \
+ ___ran.ran = true; \
+ } \
+ else \
+ { \
+ DEBUG_OK_TO_RETURN_BEGIN(CONTRACT) \
+ ___run_return: \
+ return _returnexp; \
+ DEBUG_OK_TO_RETURN_END(CONTRACT) \
+ } \
+ } \
+ if (0) \
+ { \
+ ___run_preconditions: \
+ ___op = Contract::Preconditions; \
+ } \
+ UINT ___testmask = 0; \
+
+
+
+
+#define CONTRACTL_SETUP(_contracttype) \
+ BOOL ___contract_enabled = Contract::EnforceContract(); \
+ enum {___disabled = 0}; \
+ { \
+ typedef __YouCannotUseAPostConditionHere __PostConditionOK; \
+ enum { ___CheckMustBeInside_CONTRACT = 1 }; \
+ Contract::Operation ___op = Contract::Setup; \
+ enum {___disabled = 0}; \
+ if (0) \
+ { \
+ ___run_preconditions: \
+ ___op = Contract::Preconditions; \
+ } \
+ if (0) \
+ { \
+ /* define for CONTRACT_END even though we can't get here */ \
+ ___run_return: \
+ UNREACHABLE(); \
+ } \
+ UINT ___testmask = 0; \
+
+#endif // __FORCE_NORUNTIME_CONTRACTS__
+
+
+#define CUSTOM_CONTRACT(_contracttype, _returntype) \
+ typedef Contract::PseudoTemplate<_returntype> ___maybetemplate; \
+ CONTRACT_SETUP(_contracttype, _returntype, RETVAL)
+
+#define CUSTOM_CONTRACT_VOID(_contracttype) \
+ CONTRACT_SETUP(_contracttype, int, ;)
+
+#define CUSTOM_CONTRACTL(_contracttype) \
+ CONTRACTL_SETUP(_contracttype)
+
+// Although this thing only needs to run in the Setup phase, we'll let it
+// run unconditionally. This way, the compiler will see a sequence like this:
+//
+// THROWS; GC_TRIGGERS; FORBID_FAULT ==>
+//
+// ___testmask |= constant
+// ___testmask |= constant
+// ___testmask |= constant
+//
+// and be able to fold all these into a single constant at runtime.
+//
+#define REQUEST_TEST(thetest, todisable) (___testmask |= (___CheckMustBeInside_CONTRACT, (___disabled ? (todisable) : (thetest))))
+
+
+#define INJECT_FAULT(_statement) \
+ do \
+ { \
+ STATIC_CONTRACT_FAULT; \
+ REQUEST_TEST(Contract::FAULT_Inject, Contract::FAULT_Disabled); \
+ if (0) \
+ { \
+ _statement; \
+ } \
+ } \
+ while(0) \
+
+
+#define FORBID_FAULT do { STATIC_CONTRACT_FORBID_FAULT; REQUEST_TEST(Contract::FAULT_Forbid, Contract::FAULT_Disabled); } while(0)
+
+#define THROWS do { STATIC_CONTRACT_THROWS; REQUEST_TEST(Contract::THROWS_Yes, Contract::THROWS_Disabled); } while(0)
+
+#define NOTHROW do { STATIC_CONTRACT_NOTHROW; REQUEST_TEST(Contract::THROWS_No, Contract::THROWS_Disabled); } while(0) \
+
+#define ENTRY_POINT STATIC_CONTRACT_ENTRY_POINT
+
+#define LOADS_TYPE(maxlevel) do { REQUEST_TEST( ((maxlevel) + 1) << Contract::LOADS_TYPE_Shift, Contract::LOADS_TYPE_Disabled ); } while(0)
+
+#define CAN_TAKE_LOCK do { STATIC_CONTRACT_CAN_TAKE_LOCK; REQUEST_TEST(Contract::CAN_TAKE_LOCK_Yes, Contract::CAN_TAKE_LOCK_Disabled); } while(0)
+
+#define CANNOT_TAKE_LOCK do { STATIC_CONTRACT_CANNOT_TAKE_LOCK; REQUEST_TEST(Contract::CAN_TAKE_LOCK_No, Contract::CAN_TAKE_LOCK_Disabled); } while(0)
+
+#define CANNOT_RETAKE_LOCK do { REQUEST_TEST(Contract::CAN_RETAKE_LOCK_No, Contract::CAN_RETAKE_LOCK_No_Disabled); } while(0)
+
+#define DEBUG_ONLY do { STATIC_CONTRACT_DEBUG_ONLY; REQUEST_TEST(Contract::DEBUG_ONLY_Yes, 0); } while (0)
+
+#ifndef __DISABLE_PREPOST_CONDITIONS__
+#define PRECONDITION_MSG(_expression, _message) \
+ do \
+ { \
+ enum { ___CheckMustBeInside_CONTRACT = 1 }; \
+ REQUEST_TEST(Contract::PRECONDITION_Used, 0); \
+ if ((___op&Contract::Preconditions) && !___disabled) \
+ ASSERT_CHECK(_expression, _message, "Precondition failure"); \
+ } \
+ while(0)
+
+
+#define PRECONDITION(_expression) \
+ PRECONDITION_MSG(_expression, NULL)
+
+#define POSTCONDITION_MSG(_expression, _message) \
+ ++___ran; \
+ if ((!(0 && __PostConditionOK::safe_to_use_postcondition())) && \
+ (___op&Contract::Postconditions) && \
+ !___disabled) \
+ { \
+ ASSERT_CHECK(_expression, _message, "Postcondition failure"); \
+ }
+
+#define POSTCONDITION(_expression) \
+ POSTCONDITION_MSG(_expression, NULL)
+
+#define INSTANCE_CHECK \
+ ___CheckMustBeInside_CONTRACT; \
+ if ((___op&Contract::Preconditions) && !___disabled) \
+ ASSERT_CHECK(CheckPointer(this), NULL, "Instance precheck failure"); \
+ ++___ran; \
+ if ((___op&Contract::Postconditions) && !___disabled) \
+ ASSERT_CHECK(CheckPointer(this), NULL, "Instance postcheck failure");
+
+#define INSTANCE_CHECK_NULL \
+ ___CheckMustBeInside_CONTRACT; \
+ if ((___op&Contract::Preconditions) && !___disabled) \
+ ASSERT_CHECK(CheckPointer(this, NULL_OK), NULL, "Instance precheck failure"); \
+ ++___ran; \
+ if ((___op&Contract::Postconditions) && !___disabled) \
+ ASSERT_CHECK(CheckPointer(this, NULL_OK), NULL, "Instance postcheck failure");
+
+#define CONSTRUCTOR_CHECK \
+ ___CheckMustBeInside_CONTRACT; \
+ ++___ran; \
+ if ((___op&Contract::Postconditions) && !___disabled) \
+ ASSERT_CHECK(CheckPointer(this), NULL, "Instance postcheck failure");
+
+#define DESTRUCTOR_CHECK \
+ ___CheckMustBeInside_CONTRACT; \
+ NOTHROW; \
+ if ((___op&Contract::Preconditions) && !___disabled) \
+ ASSERT_CHECK(CheckPointer(this), NULL, "Instance precheck failure");
+#else // __DISABLE_PREPOST_CONDITIONS__
+
+
+#define PRECONDITION_MSG(_expression, _message) do { } while(0)
+#define PRECONDITION(_expression) do { } while(0)
+#define POSTCONDITION_MSG(_expression, _message) do { } while(0)
+#define POSTCONDITION(_expression) do { } while(0)
+#define INSTANCE_CHECK
+#define INSTANCE_CHECK_NULL
+#define CONSTRUCTOR_CHECK
+#define DESTRUCTOR_CHECK
+
+#endif // __DISABLE_PREPOST_CONDITIONS__
+
+#define UNCHECKED(thecheck) \
+ do { \
+ ANNOTATION_UNCHECKED(thecheck); \
+ enum {___disabled = 1 }; \
+ thecheck; \
+ } while(0)
+
+#define DISABLED(thecheck) UNCHECKED(thecheck)
+
+#define WRAPPER(thecheck) UNCHECKED(thecheck)
+
+// This keyword is redundant but it's handy for reducing the nuisance editing you
+// have to when repeatedly enabling and disabling contract items while debugging.
+// You shouldn't check in code that explicitly uses ENABLED.
+#define ENABLED(_check) _check
+
+
+#ifndef __FORCE_NORUNTIME_CONTRACTS__
+#define CONTRACTL_END \
+ if (___op & Contract::Setup) \
+ { \
+ ___contract.DoChecks(___testmask, __FUNCTION__, __FILE__, __LINE__); \
+ if (___testmask & Contract::PRECONDITION_Used) \
+ { \
+ goto ___run_preconditions; \
+ } \
+ } \
+ else if (___op & Contract::Postconditions) \
+ { \
+ goto ___run_return; \
+ } \
+ ___CheckMustBeInside_CONTRACT; \
+ }
+
+#else
+
+#define CONTRACTL_END \
+ if (___op & Contract::Setup) \
+ { \
+ if (___testmask & Contract::PRECONDITION_Used) \
+ { \
+ goto ___run_preconditions; \
+ } \
+ } \
+ else if (___op & Contract::Postconditions) \
+ { \
+ goto ___run_return; \
+ } \
+ ___CheckMustBeInside_CONTRACT; \
+ } \
+
+#endif // __FORCE_NORUNTIME_CONTRACTS__
+
+#define CONTRACT_END CONTRACTL_END \
+ DEBUG_ASSURE_NO_RETURN_END(CONTRACT) \
+
+
+// The final expression in the RETURN macro deserves special explanation (or something.)
+// The expression is constructed so as to be syntactically ambiguous, depending on whether
+// __maybetemplate is a template or not. If it is a template, the expression is syntactically
+// correct as-is. If it is not, the angle brackets are interpreted as
+// less than & greater than, and the expression is incomplete. This is the point - we can
+// choose whether we need an expression or not based on the context in which the macro is used.
+// This allows the same RETURN macro to be used both in value-returning and void-returning
+// contracts.
+//
+// The "__returner ," portion of the expression is used instead of "RETVAL =", since ","
+// has lower precedence than "=". (Ain't overloaded operators fun.)
+//
+// Also note that the < and > operators on the non-template version of __maybetemplate
+// are overridden to "box" the return value in a special type and pass it
+// through to the __returner's "," operator. This is so we can detect a case where an
+// operator with lower precedence than ">" is in the return expression - in such a case we
+// will get a type error message, which instructs that parens be placed around the return
+// value expression.
+
+#define RETURN_BODY \
+ if (___returner.GotReturn()) \
+ goto ___run_postconditions_DID_YOU_FORGET_A_RETURN; \
+ else \
+ ___returner, * new ___maybetemplate < 0 >
+
+
+// We have two versions of the RETURN macro. CONTRACT_RETURN is for use inside the CONTRACT
+// scope where it is OK to return this way, even though the CONTRACT macro itself does not
+// allow a return. RETURN is for use inside the function body where it might not be OK
+// to return and we need to ensure that we don't allow a return where one should not happen
+//
+#define RETURN \
+ while (DEBUG_ASSURE_SAFE_TO_RETURN, TRUE) \
+ RETURN_BODY \
+
+#define RETURN_VOID \
+ RETURN
+
+#define CONTRACT_RETURN \
+ while (___CheckMustBeInside_CONTRACT, TRUE) \
+ RETURN_BODY \
+
+#define CONTRACT_RETURN_VOID \
+ CONTRACT_RETURN \
+
+#if 0
+#define CUSTOM_LIMITED_METHOD_CONTRACT(_contracttype) \
+ { \
+ _contracttype ___contract; \
+ STATIC_CONTRACT_LEAF; \
+ ___contract.DoChecks(Contract::THROWS_No|Contract::GC_NoTrigger|Contract::MODE_Disabled|Contract::FAULT_Disabled); \
+ /* Should add some assertion mechanism to ensure no other contracts are called */ \
+ }
+#else
+#define CUSTOM_LIMITED_METHOD_CONTRACT(_contracttype) \
+ { \
+ STATIC_CONTRACT_LEAF; \
+ }
+#endif
+
+#define CUSTOM_WRAPPER_NO_CONTRACT(_contracttype) \
+ { \
+ /* Should add some assertion mechanism to ensure one other contract is called */ \
+ STATIC_CONTRACT_WRAPPER; \
+ }
+
+#define CONTRACT_THROWS() \
+ { \
+ ::GetClrDebugState()->CheckOkayToThrow(__FUNCTION__, __FILE__, __LINE__); \
+ }
+
+#define CONTRACT_THROWSEX(__func, __file, __line) \
+ { \
+ ::GetClrDebugState()->CheckOkayToThrow(__func, __file, __line); \
+ }
+
+#else // ENABLE_CONTRACTS_IMPL
+#define CUSTOM_CONTRACT(_contracttype, _returntype) if (0) { struct YouCannotUseThisHere { int x; }; // This temporary typedef allows retail use of
+#define CUSTOM_CONTRACT_VOID(_contracttype) if (0) { struct YouCannotUseThisHere { int x; }; // FORBIDGC_LOADER_USE_ENABLED
+#define CUSTOM_CONTRACTL(_contracttype) if (0) { struct YouCannotUseThisHere { int x; }; // inside contracts and asserts but nowhere else.
+
+#define INJECT_FAULT(_statement)
+#define FORBID_FAULT
+#define THROWS
+#define NOTHROW
+#define CAN_TAKE_LOCK
+#define CANNOT_TAKE_LOCK
+#define CANNOT_RETAKE_LOCK
+#define LOADS_TYPE(maxlevel)
+#define ENTRY_POINT
+
+#ifdef _DEBUG
+// This can only appear in a debug function so don't define it non-debug
+#define DEBUG_ONLY STATIC_CONTRACT_DEBUG_ONLY
+#else
+#define DEBUG_ONLY
+#endif
+
+#define PRECONDITION_MSG(_expression, _message) do { } while(0)
+#define PRECONDITION(_expression) do { } while(0)
+#define POSTCONDITION_MSG(_expression, _message) do { } while(0)
+#define POSTCONDITION(_expression) do { } while(0)
+#define INSTANCE_CHECK
+#define INSTANCE_CHECK_NULL
+#define CONSTRUCTOR_CHECK
+#define DESTRUCTOR_CHECK
+#define UNCHECKED(thecheck)
+#define DISABLED(thecheck)
+#define WRAPPER(thecheck)
+#define ENABLED(_check)
+#define CONTRACT_END }
+#define CONTRACTL_END }
+
+#define CUSTOM_LIMITED_METHOD_CONTRACT(_contracttype) \
+ { \
+ /* Should add some assertion mechanism to ensure one other contract is called */ \
+ STATIC_CONTRACT_LEAF; \
+ }
+#define CUSTOM_WRAPPER_NO_CONTRACT(_contracttype) \
+ { \
+ /* Should add some assertion mechanism to ensure one other contract is called */ \
+ STATIC_CONTRACT_WRAPPER; \
+ }
+
+
+#define RETURN return
+#define RETURN_VOID RETURN
+
+#define CONTRACT_THROWS()
+#define CONTRACT_THROWSEX(__func, __file, __line)
+
+#endif // ENABLE_CONTRACTS_IMPL
+
+
+#define CONTRACT(_returntype) CUSTOM_CONTRACT(Contract, _returntype)
+#define CONTRACT_VOID CUSTOM_CONTRACT_VOID(Contract)
+#define CONTRACTL CUSTOM_CONTRACTL(Contract)
+
+// See description near the top of the file
+#define LIMITED_METHOD_CONTRACT CUSTOM_LIMITED_METHOD_CONTRACT(Contract)
+
+#define WRAPPER_NO_CONTRACT CUSTOM_WRAPPER_NO_CONTRACT(Contract)
+
+// GC_NOTRIGGER allowed but not currently enforced at runtime
+#define GC_NOTRIGGER STATIC_CONTRACT_GC_NOTRIGGER
+#define GC_TRIGGERS static_assert(false, "TriggersGC not supported in utilcode contracts")
+
+#ifdef ENABLE_CONTRACTS_IMPL
+template <UINT_PTR VIOLATION_MASK>
+class ContractViolationHolder
+{
+public:
+ ContractViolationHolder()
+ {
+ m_pviolationmask = NULL;
+ m_oldviolationmask = 0;
+ }
+
+ DEBUG_NOINLINE void Enter();
+
+ DEBUG_NOINLINE void Leave()
+ {
+ SCAN_SCOPE_END;
+ LeaveInternal();
+ };
+
+protected:
+ // We require that violationMask is passed as a parameter here to hopefully defeat the
+ // compiler's desire to fold all the Enter and Ctor implementations together.
+ FORCEINLINE void EnterInternal(UINT_PTR violationMask)
+ {
+ _ASSERTE(0 == (violationMask & ~(ThrowsViolation | GCViolation | ModeViolation | FaultViolation |
+ FaultNotFatal | HostViolation |
+ TakesLockViolation | LoadsTypeViolation)) ||
+ violationMask == AllViolation);
+
+ m_pviolationmask = GetClrDebugState()->ViolationMaskPtr();
+ m_oldviolationmask = *m_pviolationmask;
+ *m_pviolationmask = (m_oldviolationmask | violationMask);
+ };
+
+ FORCEINLINE void LeaveInternal()
+ {
+ // This can be used in places where our debug state has been destroyed, so check for it first.
+ if (CheckClrDebugState())
+ {
+ _ASSERTE(m_pviolationmask != NULL);
+ *m_pviolationmask = m_oldviolationmask;
+ }
+ };
+
+ UINT_PTR *m_pviolationmask;
+ UINT_PTR m_oldviolationmask;
+};
+
+template <UINT_PTR VIOLATION_MASK>
+class AutoCleanupContractViolationHolder : ContractViolationHolder<VIOLATION_MASK>
+{
+public:
+ DEBUG_NOINLINE AutoCleanupContractViolationHolder(BOOL fEnterViolation = TRUE);
+
+ DEBUG_NOINLINE ~AutoCleanupContractViolationHolder()
+ {
+ SCAN_SCOPE_END;
+ this->LeaveInternal();
+ };
+};
+
+#endif // ENABLE_CONTRACTS_IMPL
+
+#ifdef ENABLE_CONTRACTS_IMPL
+#define BEGIN_CONTRACT_VIOLATION(violationmask) \
+ { \
+ ContractViolationHolder<violationmask> __violationHolder_onlyOneAllowedPerScope; \
+ __violationHolder_onlyOneAllowedPerScope.Enter(); \
+ DEBUG_ASSURE_NO_RETURN_BEGIN(CONTRACT) \
+
+// Use this to jump out prematurely from a violation. Used for EH
+// when the function might not return
+#define RESET_CONTRACT_VIOLATION() \
+ __violationHolder_onlyOneAllowedPerScope.Leave(); \
+
+#define END_CONTRACT_VIOLATION \
+ DEBUG_ASSURE_NO_RETURN_END(CONTRACT) \
+ __violationHolder_onlyOneAllowedPerScope.Leave(); \
+ } \
+
+// See description near the top of the file
+#define CONTRACT_VIOLATION(violationMask) \
+ AutoCleanupContractViolationHolder<violationMask> __violationHolder_onlyOneAllowedPerScope;
+
+
+// Reasons for having the violation. Use one of these values as an additional parameter to
+// E.g. PERMANENT_CONTRACT_VIOLATION(ThrowsViolation, ReasonContractInfrastructure)
+// New values and explanations can be added when needed.
+enum PermanentContractViolationReason
+{
+ ReasonContractInfrastructure, // This violation is there for contract test or infrastructure purposes.
+ ReasonDebugOnly, // Code path doesn't occur on retail builds
+ ReasonNonShippingCode, // Code runs in undocumented non-shipping feature
+ ReasonIBC, // Code runs in IBC scenarios only and the violation is safe.
+ ReasonNGEN, // Code runs in NGEN scenarios only and the violation is safe.
+ ReasonProfilerCallout, // Profiler implementers are guaranteed not to throw.
+ ReasonUnsupportedForSQLF1Profiling, // This code path violates HOST_NOCALLS, but that's ok b/c SQL will never
+ // invoke it, and thus SQL/F1 profiling (the primary reason to enforce
+ // HOST_NOCALLS) is not in danger.
+ ReasonRuntimeReentrancy, // e.g. SafeQueryInterface
+ ReasonShutdownOnly, // Code path only runs as part of Shutdown and the violation is safe.
+ ReasonSOTolerance, // We would like to redesign SO contracts anyways
+ ReasonStartupOnly, // Code path only runs as part of Startup and the violation is safe.
+ ReasonWorkaroundForScanBug, // Violation is needed because of a bug in SCAN
+ ReasonProfilerAsyncCannotRetakeLock, // Profiler may call this from redirected thread, causing a CANNOT_TAKE_LOCK
+ // violation, but the scope is still protected with CANNOT_RETAKE_LOCK
+ ReasonILStubWillNotThrow, // Specially-crafted reverse COM IL stubs will not throw
+};
+
+// See the discussion near the top of the file on the use of PERMANENT_CONTRACT_VIOLATION
+// The reasonEnum is currently only used for documentation and searchability. Here
+// we have the compiler check for a typo.
+#define PERMANENT_CONTRACT_VIOLATION(violationMask, reasonEnum) \
+ if (0) \
+ PermanentContractViolationReason reason = reasonEnum; \
+ CONTRACT_VIOLATION(violationMask)
+
+#define CONDITIONAL_CONTRACT_VIOLATION(violationMask, condition) \
+ AutoCleanupContractViolationHolder<violationMask> __violationHolder_onlyOneAllowedPerScope((condition));
+
+#else
+#define BEGIN_CONTRACT_VIOLATION(violationmask)
+#define RESET_CONTRACT_VIOLATION()
+#define END_CONTRACT_VIOLATION
+#define CONTRACT_VIOLATION(violationmask)
+#define CONDITIONAL_CONTRACT_VIOLATION(violationMask, condition)
+#define PERMANENT_CONTRACT_VIOLATION(violationMask, reasonEnum)
+#endif
+
+
+
+#ifdef ENABLE_CONTRACTS_IMPL
+// Holder for setting up a faultforbid region
+class FaultForbidHolder
+{
+ public:
+ DEBUG_NOINLINE FaultForbidHolder(BOOL fConditional, BOOL fAlloc, const char *szFunction, const char *szFile, int lineNum)
+ {
+ SCAN_SCOPE_BEGIN;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ m_fConditional = fConditional;
+ if (m_fConditional)
+ {
+ m_pClrDebugState = GetClrDebugState(fAlloc);
+
+ //
+ // If we fail to get a debug state, then we must not be allocating and
+ // we simply no-op this holder.
+ //
+ if (m_pClrDebugState == NULL)
+ {
+ _ASSERTE(!fAlloc);
+ m_fConditional = FALSE;
+ return;
+ }
+
+ m_oldClrDebugState = *m_pClrDebugState;
+
+ m_pClrDebugState->ViolationMaskReset( FaultViolation|FaultNotFatal );
+ m_pClrDebugState->SetFaultForbid();
+
+ m_ContractStackRecord.m_szFunction = szFunction;
+ m_ContractStackRecord.m_szFile = szFile;
+ m_ContractStackRecord.m_lineNum = lineNum;
+ m_ContractStackRecord.m_testmask = (Contract::ALL_Disabled & ~((UINT)(Contract::FAULT_Mask))) | Contract::FAULT_Forbid;
+ m_ContractStackRecord.m_construct = "FAULT_FORBID";
+ m_pClrDebugState->LinkContractStackTrace( &m_ContractStackRecord );
+ }
+ }
+
+ DEBUG_NOINLINE ~FaultForbidHolder()
+ {
+ SCAN_SCOPE_END;
+
+ if (m_fConditional)
+ {
+ *m_pClrDebugState = m_oldClrDebugState;
+ }
+ }
+
+ private:
+ ClrDebugState *m_pClrDebugState;
+ ClrDebugState m_oldClrDebugState;
+ BOOL m_fConditional;
+ ContractStackRecord m_ContractStackRecord;
+
+};
+#endif // ENABLE_CONTRACTS_IMPL
+
+
+#ifdef ENABLE_CONTRACTS_IMPL
+
+#define FAULT_FORBID() FaultForbidHolder _ffh(TRUE, TRUE, __FUNCTION__, __FILE__, __LINE__);
+#define FAULT_FORBID_NO_ALLOC() FaultForbidHolder _ffh(TRUE, FALSE, __FUNCTION__, __FILE__, __LINE__);
+#define MAYBE_FAULT_FORBID(cond) FaultForbidHolder _ffh(cond, TRUE, __FUNCTION__, __FILE__, __LINE__);
+#define MAYBE_FAULT_FORBID_NO_ALLOC(cond) FaultForbidHolder _ffh(cond, FALSE, __FUNCTION__, __FILE__, __LINE__);
+
+#else // ENABLE_CONTRACTS_IMPL
+
+#define FAULT_FORBID() ;
+#define FAULT_FORBID_NO_ALLOC() ;
+#define MAYBE_FAULT_FORBID(cond) ;
+#define MAYBE_FAULT_FORBID_NO_ALLOC(cond) ;
+
+#endif // ENABLE_CONTRACTS_IMPL
+
+
+#ifdef ENABLE_CONTRACTS_IMPL
+
+inline BOOL AreFaultsForbiddenHelper()
+{
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_NOTHROW;
+
+ ClrDebugState *pClrDebugState = CheckClrDebugState();
+ if (!pClrDebugState)
+ {
+ // By default, faults are not forbidden. Not the most desirable default
+ // but we'd never get this debug infrastructure bootstrapped otherwise.
+ return FALSE;
+ }
+ else
+ {
+ return pClrDebugState->IsFaultForbid() && (!(pClrDebugState->ViolationMask() & (FaultViolation|FaultNotFatal|BadDebugState)));
+ }
+}
+
+#define ARE_FAULTS_FORBIDDEN() AreFaultsForbiddenHelper()
+#else
+
+// If you got an error about ARE_FAULTS_FORBIDDEN being undefined, it's because you tried
+// to use this predicate in a free build outside of a CONTRACT or ASSERT.
+//
+#define ARE_FAULTS_FORBIDDEN() (sizeof(YouCannotUseThisHere) != 0)
+#endif
+
+
+// This allows a fault-forbid region to invoke a non-mandatory allocation, such as for the
+// purpose of growing a lookaside cache (if the allocation fails, the code can abandon the
+// cache growing operation without negative effect.)
+//
+// Although it's implemented using CONTRACT_VIOLATION(), it's not a bug to have this in the code.
+//
+// It *is* a bug to use this to hide a situation where an OOM is genuinely fatal but not handled.
+#define FAULT_NOT_FATAL() CONTRACT_VIOLATION(FaultNotFatal)
+
+
+
+#ifdef ENABLE_CONTRACTS_IMPL
+
+//------------------------------------------------------------------------------------
+// Underlying class support for TRIGGERS_TYPE_LOAD and OVERRIDE_TYPE_LOAD_LEVEL_LIMIT.
+// Don't reference this class directly. Use the macros.
+//------------------------------------------------------------------------------------
+class LoadsTypeHolder
+{
+ public:
+ LoadsTypeHolder(BOOL fConditional,
+ UINT newLevel,
+ BOOL fEnforceLevelChangeDirection,
+ const char *szFunction,
+ const char *szFile,
+ int lineNum
+ );
+
+ ~LoadsTypeHolder();
+
+ private:
+ ClrDebugState *m_pClrDebugState;
+ ClrDebugState m_oldClrDebugState;
+ BOOL m_fConditional;
+ ContractStackRecord m_contractStackRecord;
+
+};
+
+#endif // ENABLE_CONTRACTS_IMPL
+
+
+//------------------------------------------------------------------------------------
+// TRIGGERS_TYPE_LOAD(newLevel)
+// Works just LOADS_TYPE in contracts but lets you protect individual scopes
+//
+// OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(newLevel)
+// Sets a new limit just like TRIGGERS_TYPE_LOAD but does not restrict you
+// to decreasing the limit. Only the loader should use this and only when it
+// can prove structurally that no recursion will occur as a result.
+//------------------------------------------------------------------------------------
+#ifdef ENABLE_CONTRACTS_IMPL
+
+#define TRIGGERS_TYPE_LOAD(newLevel) LoadsTypeHolder _lth(TRUE, newLevel, TRUE, __FUNCTION__, __FILE__, __LINE__);
+#define MAYBE_TRIGGERS_TYPE_LOAD(newLevel, fEnable) LoadsTypeHolder _lth(fEnable, newLevel, TRUE, __FUNCTION__, __FILE__, __LINE__);
+#define OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(newLevel) LoadsTypeHolder _lth(TRUE, newLevel, FALSE, __FUNCTION__, __FILE__, __LINE__);
+#define MAYBE_OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(newLevel, fEnable) LoadsTypeHolder _lth(fEnable, newLevel, FALSE, __FUNCTION__, __FILE__, __LINE__);
+
+#else // ENABLE_CONTRACTS_IMPL
+
+#define TRIGGERS_TYPE_LOAD(newLevel)
+#define MAYBE_TRIGGERS_TYPE_LOAD(newLevel, fEnable)
+#define OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(newLevel)
+#define MAYBE_OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(newLevel, fEnable)
+
+#endif // ENABLE_CONTRACTS_IMPL
+
+
+
+#ifdef ENABLE_CONTRACTS_IMPL
+
+// This sets up a marker that says its okay to throw on this thread. This is not a public macro, and should only be
+// used from within the implementation of various try/catch macros.
+class ClrTryMarkerHolder
+{
+public:
+ DEBUG_NOINLINE ClrTryMarkerHolder()
+ {
+ SCAN_SCOPE_BEGIN;
+ STATIC_CONTRACT_THROWS;
+
+ m_pClrDebugState = GetClrDebugState();
+ m_oldOkayToThrowValue = m_pClrDebugState->IsOkToThrow();
+ m_pClrDebugState->SetOkToThrow();
+ }
+
+ DEBUG_NOINLINE ~ClrTryMarkerHolder()
+ {
+ SCAN_SCOPE_END;
+
+ m_pClrDebugState->SetOkToThrow( m_oldOkayToThrowValue );
+ }
+
+private:
+ BOOL m_oldOkayToThrowValue;
+ ClrDebugState *m_pClrDebugState;
+};
+
+#define CLR_TRY_MARKER() ClrTryMarkerHolder ___tryMarkerHolder;
+
+#else // ENABLE_CONTRACTS_IMPL
+
+#define CLR_TRY_MARKER()
+
+#endif
+
+#ifdef ENABLE_CONTRACTS_IMPL
+// Note: This routine will create a ClrDebugState if called for the first time.
+// It cannot return NULL (see comment for InitClrDebugState).
+inline ClrDebugState *GetClrDebugState(BOOL fAlloc)
+{
+ STATIC_CONTRACT_LIMITED_METHOD;
+
+ ClrDebugState *pState = CheckClrDebugState();
+
+ if (pState)
+ {
+ return pState;
+ }
+
+ if (fAlloc)
+ {
+ return CLRInitDebugState();
+ }
+
+ return NULL;
+}
+#endif // ENABLE_CONTRACTS_IMPL
+
+#ifdef ENABLE_CONTRACTS_IMPL
+
+class HostNoCallHolder
+{
+ public:
+ DEBUG_NOINLINE HostNoCallHolder()
+ {
+ SCAN_SCOPE_BEGIN;
+ STATIC_CONTRACT_HOST_NOCALLS;
+
+ m_clrDebugState = GetClrDebugState();
+ m_previousState = m_clrDebugState->SetHostCaller(FALSE);
+ }
+
+ DEBUG_NOINLINE ~HostNoCallHolder()
+ {
+ SCAN_SCOPE_END;
+
+ m_clrDebugState->SetHostCaller(m_previousState);
+ }
+
+ private:
+ BOOL m_previousState;
+ ClrDebugState* m_clrDebugState;
+
+};
+
+#define BEGIN_HOST_NOCALL_CODE \
+ { \
+ HostNoCallHolder __hostNoCallHolder; \
+ CantAllocHolder __cantAlloc;
+
+#define END_HOST_NOCALL_CODE \
+ }
+
+#else // ENABLE_CONTRACTS_IMPL
+#define BEGIN_HOST_NOCALL_CODE \
+ { \
+ CantAllocHolder __cantAlloc; \
+
+#define END_HOST_NOCALL_CODE \
+ }
+#endif
+
+
+#if defined(ENABLE_CONTRACTS_IMPL)
+
+// Macros to indicate we're taking or releasing locks
+
+// Most general macros, not used directly
+#define LOCK_TAKEN_MULTIPLE(dbgStateLockType, cEntrances, pvLock) \
+ ::GetClrDebugState()->LockTaken((dbgStateLockType), (cEntrances), (void*) (pvLock), __FUNCTION__, __FILE__, __LINE__)
+#define LOCK_RELEASED_MULTIPLE(dbgStateLockType, cExits, pvLock) \
+ ::GetClrDebugState()->LockReleased((dbgStateLockType), (cExits), (void*) (pvLock))
+
+// Use these only if you need to force multiple entrances or exits in a single
+// line (e.g., to restore the lock to a previous state). CRWLock in vm\rwlock.cpp does this
+#define EE_LOCK_TAKEN_MULTIPLE(cEntrances, pvLock) \
+ LOCK_TAKEN_MULTIPLE(kDbgStateLockType_EE, cEntrances, pvLock)
+#define EE_LOCK_RELEASED_MULTIPLE(cExits, pvLock) \
+ LOCK_RELEASED_MULTIPLE(kDbgStateLockType_EE, cExits, pvLock)
+#define HOST_BREAKABLE_CRST_TAKEN_MULTIPLE(cEntrances, pvLock) \
+ LOCK_TAKEN_MULTIPLE(kDbgStateLockType_HostBreakableCrst, cEntrances, pvLock)
+#define HOST_BREAKABLE_CRST_RELEASED_MULTIPLE(cExits, pvLock) \
+ LOCK_RELEASED_MULTIPLE(kDbgStateLockType_HostBreakableCrst, cExits, pvLock)
+#define USER_LOCK_TAKEN_MULTIPLE(cEntrances, pvLock) \
+ LOCK_TAKEN_MULTIPLE(kDbgStateLockType_User, cEntrances, pvLock)
+#define USER_LOCK_RELEASED_MULTIPLE(cExits, pvLock) \
+ LOCK_RELEASED_MULTIPLE(kDbgStateLockType_User, cExits, pvLock)
+
+// These are most typically used
+#define EE_LOCK_TAKEN(pvLock) \
+ LOCK_TAKEN_MULTIPLE(kDbgStateLockType_EE, 1, pvLock)
+#define EE_LOCK_RELEASED(pvLock) \
+ LOCK_RELEASED_MULTIPLE(kDbgStateLockType_EE, 1, pvLock)
+#define HOST_BREAKABLE_CRST_TAKEN(pvLock) \
+ LOCK_TAKEN_MULTIPLE(kDbgStateLockType_HostBreakableCrst, 1, pvLock)
+#define HOST_BREAKABLE_CRST_RELEASED(pvLock) \
+ LOCK_RELEASED_MULTIPLE(kDbgStateLockType_HostBreakableCrst, 1, pvLock)
+#define USER_LOCK_TAKEN(pvLock) \
+ LOCK_TAKEN_MULTIPLE(kDbgStateLockType_User, 1, pvLock)
+#define USER_LOCK_RELEASED(pvLock) \
+ LOCK_RELEASED_MULTIPLE(kDbgStateLockType_User, 1, pvLock)
+
+#else // defined(ENABLE_CONTRACTS_IMPL)
+
+#define LOCK_TAKEN_MULTIPLE(dbgStateLockType, cEntrances, pvLock)
+#define LOCK_RELEASED_MULTIPLE(dbgStateLockType, cExits, pvLock)
+#define EE_LOCK_TAKEN_MULTIPLE(cEntrances, pvLock)
+#define EE_LOCK_RELEASED_MULTIPLE(cExits, pvLock)
+#define HOST_BREAKABLE_CRST_TAKEN_MULTIPLE(cEntrances, pvLock)
+#define HOST_BREAKABLE_CRST_RELEASED_MULTIPLE(cExits, pvLock)
+#define USER_LOCK_TAKEN_MULTIPLE(cEntrances, pvLock)
+#define USER_LOCK_RELEASED_MULTIPLE(cExits, pvLock)
+#define EE_LOCK_TAKEN(pvLock)
+#define EE_LOCK_RELEASED(pvLock)
+#define HOST_BREAKABLE_CRST_TAKEN(pvLock)
+#define HOST_BREAKABLE_CRST_RELEASED(pvLock)
+#define USER_LOCK_TAKEN(pvLock)
+#define USER_LOCK_RELEASED(pvLock)
+
+#endif // defined(ENABLE_CONTRACTS_IMPL)
+
+#if defined(ENABLE_CONTRACTS_IMPL)
+
+// Abbreviation for an assert that is only considered if there is a valid
+// ClrDebugState available. Useful if you want to assert based on the value
+// of GetDbgStateLockCount(), where a return of 0 (the default if there is no
+// valid ClrDebugState available) would cause your assert to fire. The variable
+// __pClrDebugState is set to the current ClrDebugState, and may be used within
+// your assert expression
+#define ASSERT_UNLESS_NO_DEBUG_STATE(e) \
+ { \
+ ClrDebugState * __pClrDebugState = GetClrDebugState(); \
+ _ASSERTE(((__pClrDebugState->ViolationMask() & BadDebugState) != 0) || (e)); \
+ }
+
+#else // defined(ENABLE_CONTRACTS_IMPL)
+
+#define ASSERT_UNLESS_NO_DEBUG_STATE(e)
+
+#endif // defined(ENABLE_CONTRACTS_IMPL)
+
+
+//-----------------------------------------------------------------------------
+// Debug support to ensure that nobody calls New on the helper thread.
+// This is for interop debugging.
+// They should be using the InteropSafe heap.
+// Having this in the meantime allows us to
+// assert that the helper thread never calls new, and maintain a finite list of
+// exceptions (bugs).
+// Eventually, all those bugs should be fixed this holder can be completely removed.
+//
+// It is also the case that we disallow allocations when any thread is OS suspended
+// This happens for a short time when we are suspending the EE. We supress both
+// of these.
+//
+// @todo- ideally this would be rolled into the ContractViolation.
+// also, we'd have contract bit for whether APIs can be called on the helper thread.
+// @todo - if we really wanted to be strict, we should make this per-thread.
+//-----------------------------------------------------------------------------
+#ifdef ENABLE_CONTRACTS_IMPL
+extern Volatile<LONG> g_DbgSuppressAllocationAsserts;
+#define SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE CounterHolder _AllowNewOnHelperHolder(&g_DbgSuppressAllocationAsserts);
+#else
+// Nothing in retail since this holder just disabled an assert.
+#define SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Support for contracts in DAC builds
+//
+// At the moment, most of the contract system is disabled in DAC builds.
+// We do however want some simple static contracts in order to support static
+// analysis tools that run on mscordacwks.dll like DacCop.
+// Note that we want these static contracts in both DEBUG and retail builds.
+// We also already get simple static contracts like WRAPPER and LEAF.
+//
+//-----------------------------------------------------------------------------
+#if defined(DACCESS_COMPILE)
+
+// SUPPORTS_DAC is an annotation that says the function is designed to be used in DAC builds.
+// This enables full DacCop analysis on the function, including verifying that all functions that are
+// called also support DAC.
+#define SUPPORTS_DAC do { STATIC_CONTRACT_SUPPORTS_DAC; } while(0)
+
+// Normally a function can be annotated just with WRAPPER_NO_CONTRACT, which (in addition to the normal
+// contract meaning) indicates to DacCop that the function should be considered to support DAC when
+// it is called from a supports-dac function. This is to avoid having to add a DAC-specific contract
+// to all the trivial one-line wrapper functions we have.
+// However, we occasionally want these semantics even for functions which are not appropriate to label
+// as WRAPPER_NO_CONTRACT. For example, a template function may support DAC for certain template arguments,
+// but not others (due to the functions it calls). We want to ensure that when such a function is called
+// in a DAC code path, analysis is enabled on that particular instantiation including checking all of the
+// call targets specific to this template instantiation. But we don't want to require that the call targets
+// for ALL instantiations support dac, since we may not even be using them in DAC code paths. Ideally we'd
+// remove any such code from the DAC build, but this will take time.
+#define SUPPORTS_DAC_WRAPPER do { STATIC_CONTRACT_WRAPPER; } while(0)
+
+// SUPPORTS_DAC_HOST_ONLY indicates that a function is allowed to be called in DAC builds, but rather
+// than being a normal DAC function which operates on marshalled data, it is a host-only utility function
+// that knows nothing about DAC and operates solely on the host. For example, DbgAssertDialog is a utility
+// function for popping assert dialogs - there is nothing DAC-specific about this. Ideally such utility
+// functions would be confined to their own library which had no access to DAC functionality, and which
+// is not analyzed by DacCop. At the moment splitting utilcode into two variations like this is too
+// painful, but we hope to do it in the future (primarily to support functions which can be used in either
+// DAC or host-only mode).
+// WARNING: This contract disables DacCop analysis on the function and any functions it calls, so it
+// should be used very carefully.
+#define SUPPORTS_DAC_HOST_ONLY do { STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; } while(0)
+
+#else
+#define SUPPORTS_DAC
+#define SUPPORTS_DAC_HOST_ONLY
+#define SUPPORTS_DAC_WRAPPER
+#endif // DACCESS_COMPILE
+
+// LIMITED_METHOD_DAC_CONTRACT is a shortcut for LIMITED_METHOD_CONTRACT and SUPPORTS_DAC. Usefull for one-line inline functions.
+#define LIMITED_METHOD_DAC_CONTRACT LIMITED_METHOD_CONTRACT; SUPPORTS_DAC
+
+//
+// The default contract is the recommended contract for ordinary code.
+// The ordinary code can throw or trigger GC any time, does not operate
+// on raw object refs, etc.
+//
+
+#define STANDARD_VM_CHECK \
+ THROWS;
+
+#define STANDARD_VM_CONTRACT \
+ CONTRACTL \
+ { \
+ STANDARD_VM_CHECK; \
+ } \
+ CONTRACTL_END; \
+
+#define STATIC_STANDARD_VM_CONTRACT \
+ STATIC_CONTRACT_THROWS; \
+ STATIC_CONTRACT_GC_TRIGGERS; \
+ STATIC_CONTRACT_MODE_PREEMPTIVE;
+
+#define AFTER_CONTRACTS
+#include "volatile.h"
+
+#endif // CONTRACT_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// Contract.inl
+//
+
+// ! I am the owner for issues in the contract *infrastructure*, not for every
+// ! CONTRACT_VIOLATION dialog that comes up. If you interrupt my work for a routine
+// ! CONTRACT_VIOLATION, you will become the new owner of this file.
+// ---------------------------------------------------------------------------
+
+#ifndef CONTRACT_INL_
+#define CONTRACT_INL_
+
+#include "contract.h"
+#include <string.h>
+
+#ifdef ENABLE_CONTRACTS_IMPL
+
+inline void BaseContract::DoChecks(UINT testmask, _In_z_ const char *szFunction, _In_z_ const char *szFile, int lineNum)
+{
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ // Cache the pointer to our ClrDebugState if it's not already cached.
+ // Derived types could set up this ptr before calling BaseContract::DoChecks if they have access to the Thread ptr
+ if (m_pClrDebugState == NULL)
+ {
+ m_pClrDebugState = GetClrDebugState();
+ }
+
+ // Save the incoming contents for restoration in the destructor
+ m_IncomingClrDebugState = *m_pClrDebugState;
+
+ m_testmask = testmask; // Save the testmask for destructor
+
+ // Setup the new stack record.
+ m_contractStackRecord.m_szFunction = szFunction;
+ m_contractStackRecord.m_szFile = szFile;
+ m_contractStackRecord.m_lineNum = lineNum;
+ m_contractStackRecord.m_testmask = testmask;
+ m_contractStackRecord.m_construct = "CONTRACT";
+
+ // Link the new ContractStackRecord into the chain for this thread.
+ m_pClrDebugState->LinkContractStackTrace( &m_contractStackRecord );
+
+ if (testmask & DEBUG_ONLY_Yes)
+ {
+ m_pClrDebugState->SetDebugOnly();
+ }
+
+ switch (testmask & FAULT_Mask)
+ {
+ case FAULT_Forbid:
+ m_pClrDebugState->ViolationMaskReset( FaultViolation|FaultNotFatal );
+ m_pClrDebugState->SetFaultForbid();
+ break;
+
+ case FAULT_Inject:
+ if (m_pClrDebugState->IsFaultForbid() &&
+ !(m_pClrDebugState->ViolationMask() & (FaultViolation|FaultNotFatal|BadDebugState)))
+ {
+ CONTRACT_ASSERT("INJECT_FAULT called in a FAULTFORBID region.",
+ BaseContract::FAULT_Forbid,
+ BaseContract::FAULT_Mask,
+ m_contractStackRecord.m_szFunction,
+ m_contractStackRecord.m_szFile,
+ m_contractStackRecord.m_lineNum);
+ }
+ break;
+
+ case FAULT_Disabled:
+ // Nothing
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ switch (testmask & THROWS_Mask)
+ {
+ case THROWS_Yes:
+ m_pClrDebugState->CheckOkayToThrow(m_contractStackRecord.m_szFunction,
+ m_contractStackRecord.m_szFile,
+ m_contractStackRecord.m_lineNum);
+ break;
+
+ case THROWS_No:
+ m_pClrDebugState->ViolationMaskReset( ThrowsViolation );
+ m_pClrDebugState->ResetOkToThrow();
+ break;
+
+ case THROWS_Disabled:
+ // Nothing
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ // LOADS_TYPE check
+ switch (testmask & LOADS_TYPE_Mask)
+ {
+ case LOADS_TYPE_Disabled:
+ // Nothing
+ break;
+
+ default:
+ {
+ UINT newTypeLoadLevel = ((testmask & LOADS_TYPE_Mask) >> LOADS_TYPE_Shift) - 1;
+ if (newTypeLoadLevel > m_pClrDebugState->GetMaxLoadTypeLevel())
+ {
+ if (!((LoadsTypeViolation|BadDebugState) & m_pClrDebugState->ViolationMask()))
+ {
+ CONTRACT_ASSERT("A function tried to load a type past the current level limit.",
+ (m_pClrDebugState->GetMaxLoadTypeLevel() + 1) << LOADS_TYPE_Shift,
+ Contract::LOADS_TYPE_Mask,
+ m_contractStackRecord.m_szFunction,
+ m_contractStackRecord.m_szFile,
+ m_contractStackRecord.m_lineNum
+ );
+ }
+ }
+ m_pClrDebugState->SetMaxLoadTypeLevel(newTypeLoadLevel);
+ m_pClrDebugState->ViolationMaskReset(LoadsTypeViolation);
+
+ }
+ break;
+ }
+
+ if (testmask & CAN_RETAKE_LOCK_No)
+ {
+ m_pClrDebugState->OnEnterCannotRetakeLockFunction();
+ m_pClrDebugState->ResetOkToRetakeLock();
+ }
+
+ switch (testmask & CAN_TAKE_LOCK_Mask)
+ {
+ case CAN_TAKE_LOCK_Yes:
+ m_pClrDebugState->CheckOkayToLock(m_contractStackRecord.m_szFunction,
+ m_contractStackRecord.m_szFile,
+ m_contractStackRecord.m_lineNum);
+ break;
+
+ case CAN_TAKE_LOCK_No:
+ m_pClrDebugState->ViolationMaskReset(TakesLockViolation);
+ m_pClrDebugState->ResetOkToLock();
+ break;
+
+ case CAN_TAKE_LOCK_Disabled:
+ // Nothing
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+}
+
+FORCEINLINE BOOL BaseContract::CheckFaultInjection()
+{
+ // ??? use m_tag to see if we should trigger an injection
+ return FALSE;
+}
+
+inline BOOL ClrDebugState::CheckOkayToThrowNoAssert()
+{
+ if (!IsOkToThrow() && !(m_violationmask & (ThrowsViolation|BadDebugState)))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+inline void ClrDebugState::CheckOkayToThrow(_In_z_ const char *szFunction, _In_z_ const char *szFile, int lineNum)
+{
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (!CheckOkayToThrowNoAssert())
+ {
+ CONTRACT_ASSERT("THROWS called in a NOTHROW region.",
+ BaseContract::THROWS_No,
+ BaseContract::THROWS_Mask,
+ szFunction,
+ szFile,
+ lineNum);
+ }
+}
+
+inline BOOL ClrDebugState::CheckOkayToLockNoAssert()
+{
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (!IsOkToLock() && !(m_violationmask & (TakesLockViolation|BadDebugState)))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+inline void ClrDebugState::CheckOkayToLock(_In_z_ const char *szFunction, _In_z_ const char *szFile, int lineNum)
+{
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (!CheckOkayToLockNoAssert())
+ {
+
+ CONTRACT_ASSERT("CAN_TAKE_LOCK called in a CANNOT_TAKE_LOCK region.",
+ BaseContract::CAN_TAKE_LOCK_No,
+ BaseContract::CAN_TAKE_LOCK_Mask,
+ szFunction,
+ szFile,
+ lineNum);
+
+ }
+}
+
+
+inline void ClrDebugState::LockTaken(DbgStateLockType dbgStateLockType,
+ UINT cTakes,
+ void * pvLock,
+ _In_z_ const char * szFunction,
+ _In_z_ const char * szFile,
+ int lineNum)
+{
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if ((m_violationmask & BadDebugState) != 0)
+ {
+ return;
+ }
+
+ // Assert if we're taking a lock in a CANNOT_TAKE_LOCK scope. Even if this asserts, we'll
+ // continue to the following lines to track the lock
+ CheckOkayToLock(szFunction, szFile, lineNum);
+
+ _ASSERTE(GetDbgStateLockData() != NULL);
+
+ if (!IsOkToRetakeLock())
+ {
+ if (m_LockState.IsLockRetaken(pvLock))
+ {
+ CONTRACT_ASSERT("You cannot take a lock which is already being held in a CANNOT_RETAKE_LOCK scope.",
+ BaseContract::CAN_RETAKE_LOCK_No,
+ BaseContract::CAN_RETAKE_LOCK_No,
+ szFunction,
+ szFile,
+ lineNum);
+ }
+ }
+
+ GetDbgStateLockData()->LockTaken(dbgStateLockType, cTakes, pvLock, szFunction, szFile, lineNum);
+}
+
+inline void ClrDebugState::LockReleased(DbgStateLockType dbgStateLockType, UINT cReleases, void * pvLock)
+{
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if ((m_violationmask & BadDebugState) != 0)
+ {
+ return;
+ }
+
+ _ASSERTE(GetDbgStateLockData() != NULL);
+
+ if (!IsOkToRetakeLock())
+ {
+ // It is very suspicious to release any locks being hold at the time this function was
+ // called in a CANNOT_RETAKE_LOCK scope
+ _ASSERTE(m_LockState.IsSafeToRelease(cReleases));
+ }
+
+ GetDbgStateLockData()->LockReleased(dbgStateLockType, cReleases, pvLock);
+}
+
+inline UINT ClrDebugState::GetLockCount(DbgStateLockType dbgStateLockType)
+{
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if ((m_violationmask & BadDebugState) != 0)
+ {
+ return 0;
+ }
+
+ _ASSERTE(GetDbgStateLockData() != NULL);
+ return GetDbgStateLockData()->GetLockCount(dbgStateLockType);
+}
+
+inline UINT ClrDebugState::GetCombinedLockCount()
+{
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if ((m_violationmask & BadDebugState) != 0)
+ {
+ return 0;
+ }
+
+ _ASSERTE(GetDbgStateLockData() != NULL);
+ return GetDbgStateLockData()->GetCombinedLockCount();
+}
+
+inline void DbgStateLockData::LockTaken(DbgStateLockType dbgStateLockType,
+ UINT cTakes, // # times we're taking this lock (usually 1)
+ void * pvLock,
+ _In_z_ const char * szFunction,
+ _In_z_ const char * szFile,
+ int lineNum)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ // Technically the lock's already been taken before we're called, but it's
+ // handy to have this contract here at the leaf end of the call chain, as it
+ // ensures SCAN will enforce that no use of the LOCK_TAKEN macros occurs
+ // in a CANNOT_TAKE_LOCK scope (as LOCK_TAKEN macros just call this function).
+ STATIC_CONTRACT_CAN_TAKE_LOCK;
+
+ // Valid enum?
+ _ASSERTE(UINT(dbgStateLockType) < kDbgStateLockType_Count);
+
+ UINT cCombinedLocks = GetCombinedLockCount();
+
+ // Are we exceeding the threshold for what we can store in m_rgTakenLockInfos?
+ // If so, assert a warning, but we'll deal with it.
+ if ((cCombinedLocks <= ARRAY_SIZE(m_rgTakenLockInfos)) &&
+ (cCombinedLocks + cTakes > ARRAY_SIZE(m_rgTakenLockInfos)))
+ {
+ // Actually, for now we are NOT asserting until I can dedicate more time
+ // to this. Some class loader code paths legally hold many simultaneous
+ // locks (>10). Need to do further analysis on reasonable value to set
+ // for kMaxAllowedSimultaneousLocks. Since lock order checking is turned
+ // off for the moment anyway, exceeding kMaxAllowedSimultaneousLocks
+ // has no consequences for now anyway.
+ }
+
+ m_rgcLocksTaken[dbgStateLockType] += cTakes;
+
+ // Remember as many of these new entrances in m_rgTakenLockInfos as we can
+ for (UINT i = cCombinedLocks;
+ i < min (ARRAY_SIZE(m_rgTakenLockInfos), cCombinedLocks + cTakes);
+ i++)
+ {
+ m_rgTakenLockInfos[i].m_pvLock = pvLock;
+ m_rgTakenLockInfos[i].m_szFile = szFile;
+ m_rgTakenLockInfos[i].m_lineNum = lineNum;
+ }
+}
+
+inline void DbgStateLockData::LockReleased(DbgStateLockType dbgStateLockType, UINT cReleases, void * pvLock)
+{
+ // Valid enum?
+ _ASSERTE(UINT(dbgStateLockType) < kDbgStateLockType_Count);
+
+ if (cReleases > m_rgcLocksTaken[dbgStateLockType])
+ {
+ _ASSERTE(!"Releasing lock(s) that were never taken");
+ cReleases = m_rgcLocksTaken[dbgStateLockType];
+ }
+
+ UINT cCombinedLocks = GetCombinedLockCount();
+
+ // If lock count is within range of our m_rgTakenLockInfos buffer size, then
+ // make sure we're releasing locks in reverse order of how we took them
+ for (UINT i = cCombinedLocks - cReleases;
+ i < min (ARRAY_SIZE(m_rgTakenLockInfos), cCombinedLocks);
+ i++)
+ {
+ if (m_rgTakenLockInfos[i].m_pvLock != pvLock)
+ {
+ // Ok, I lied. We're not really checking that we're releasing locks in reverse
+ // order, because sometimes we legally release them out of order. (The loader
+ // does this intentionally in a few places.) We should consider whether those
+ // places can be changed, or whether we can add some kind of macro to declare
+ // that we're releasing out of order, and that it's ok & intentional. At that
+ // point, we can place a nice ASSERTE right here. Until then, do nothing.
+ }
+
+ // We may be clearing out the wrong entry in m_rgTakenLockInfos here, if the locks
+ // were released out of order. However, it will eventually correct itself once all
+ // the out-of-order locks have been released. And our count
+ // (i.e., m_rgcLocksTaken[dbgStateLockType]) will always be accurate
+ memset(&(m_rgTakenLockInfos[i]),
+ 0,
+ sizeof(m_rgTakenLockInfos[i]));
+ }
+
+ m_rgcLocksTaken[dbgStateLockType] -= cReleases;
+}
+
+inline void DbgStateLockData::SetStartingValues()
+{
+ memset(this, 0, sizeof(*this));
+}
+
+inline UINT DbgStateLockData::GetLockCount(DbgStateLockType dbgStateLockType)
+{
+ _ASSERTE(UINT(dbgStateLockType) < kDbgStateLockType_Count);
+ return m_rgcLocksTaken[dbgStateLockType];
+}
+
+inline UINT DbgStateLockData::GetCombinedLockCount()
+{
+ // If this fires, the set of lock types must have changed. You'll need to
+ // fix the sum below to include all lock types
+ _ASSERTE(kDbgStateLockType_Count == 3);
+
+ return m_rgcLocksTaken[0] + m_rgcLocksTaken[1] + m_rgcLocksTaken[2];
+}
+
+inline void DbgStateLockState::SetStartingValues()
+{
+ m_cLocksEnteringCannotRetakeLock = 0;
+ m_pLockData = NULL; // Will get filled in by CLRInitDebugState()
+}
+
+// We set a marker to record the number of locks that have been taken when
+// CANNOT_RETAKE_LOCK contract is constructed.
+inline void DbgStateLockState::OnEnterCannotRetakeLockFunction()
+{
+ m_cLocksEnteringCannotRetakeLock = m_pLockData->GetCombinedLockCount();
+}
+
+inline BOOL DbgStateLockState::IsLockRetaken(void * pvLock)
+{
+ // m_cLocksEnteringCannotRetakeLock must be in valid range
+ _ASSERTE(m_cLocksEnteringCannotRetakeLock <= m_pLockData->GetCombinedLockCount());
+
+ // m_cLocksEnteringCannotRetakeLock records the number of locks that were taken
+ // when CANNOT_RETAKE_LOCK contract was constructed.
+ for (UINT i = 0;
+ i < min(ARRAY_SIZE(m_pLockData->m_rgTakenLockInfos), m_cLocksEnteringCannotRetakeLock);
+ ++i)
+ {
+ if (m_pLockData->m_rgTakenLockInfos[i].m_pvLock == pvLock)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+inline BOOL DbgStateLockState::IsSafeToRelease(UINT cReleases)
+{
+ return m_cLocksEnteringCannotRetakeLock <= (m_pLockData->GetCombinedLockCount() - cReleases);
+}
+
+inline void DbgStateLockState::SetDbgStateLockData(DbgStateLockData * pDbgStateLockData)
+{
+ m_pLockData = pDbgStateLockData;
+}
+
+inline DbgStateLockData * DbgStateLockState::GetDbgStateLockData()
+{
+ return m_pLockData;
+}
+
+inline
+void CONTRACT_ASSERT(const char *szElaboration,
+ UINT whichTest,
+ UINT whichTestMask,
+ const char *szFunction,
+ const char *szFile,
+ int lineNum)
+{
+ if (CheckClrDebugState() && ( CheckClrDebugState()->ViolationMask() & BadDebugState))
+ {
+ _ASSERTE(!"Someone tried to assert a contract violation although the contracts were disabled in this thread due to"
+ " an OOM or a shim/mscorwks mismatch. You can probably safely ignore this assert - however, whoever"
+ " called CONTRACT_ASSERT was supposed to checked if the current violationmask had the BadDebugState set."
+ " Look up the stack, see who called CONTRACT_ASSERT and file a bug against the owner.");
+ return;
+ }
+
+ // prevent recursion - we use the same mechanism as CHECK, so this will
+ // also prevent mutual recursion involving ASSERT_CHECKs
+ CHECK _check;
+ if (_check.EnterAssert())
+ {
+ char Buf[512*20 + 2048 + 1024];
+
+ sprintf_s(Buf,ARRAY_SIZE(Buf), "CONTRACT VIOLATION by %s at \"%s\" @ %d\n\n%s\n", szFunction, szFile, lineNum, szElaboration);
+
+ int count = 20;
+ ContractStackRecord *pRec = CheckClrDebugState() ? CheckClrDebugState()->GetContractStackTrace() : NULL;
+ BOOL foundconflict = FALSE;
+ BOOL exceptionBuildingStack = FALSE;
+
+ PAL_TRY_NAKED
+ {
+ while (pRec != NULL)
+ {
+ char tmpbuf[512];
+ BOOL fshowconflict = FALSE;
+
+ if (!foundconflict)
+ {
+ if (whichTest == (pRec->m_testmask & whichTestMask))
+ {
+ foundconflict = TRUE;
+ fshowconflict = TRUE;
+ }
+ }
+
+ if (count != 0 || fshowconflict)
+ {
+ if (count != 0)
+ {
+ count--;
+ }
+ else
+ {
+ // Show that some lines have been skipped
+ strcat_s(Buf, ARRAY_SIZE(Buf), "\n ...");
+
+ }
+
+ sprintf_s(tmpbuf,ARRAY_SIZE(tmpbuf),
+ "\n%s %s in %s at \"%s\" @ %d",
+ fshowconflict ? "VIOLATED-->" : " ",
+ pRec->m_construct,
+ pRec->m_szFunction,
+ pRec->m_szFile,
+ pRec->m_lineNum
+ );
+
+ strcat_s(Buf, ARRAY_SIZE(Buf), tmpbuf);
+ }
+
+ pRec = pRec->m_pNext;
+ }
+ }
+ PAL_EXCEPT_NAKED(EXCEPTION_EXECUTE_HANDLER)
+ {
+ // We're done trying to walk the stack of contracts. We faulted trying to form the contract stack trace,
+ // and that usually means that its corrupted. A common cause of this is having CONTRACTs in functions that
+ // never return, but instead do a non-local goto.
+ count = 0;
+ exceptionBuildingStack = TRUE;
+ }
+ PAL_ENDTRY_NAKED;
+
+ if (count == 0)
+ {
+ strcat_s(Buf,ARRAY_SIZE(Buf), "\n ...");
+ }
+
+ if (exceptionBuildingStack)
+ {
+ strcat_s(Buf,ARRAY_SIZE(Buf),
+ "\n"
+ "\nError forming contract stack. Any contract stack displayed above is correct,"
+ "\nbut it's most probably truncated. This is probably due to a CONTRACT in a"
+ "\nfunction that does a non-local goto. There are two bugs here:"
+ "\n"
+ "\n 1) the CONTRACT violation, and"
+ "\n 2) the CONTRACT in the function with the non-local goto."
+ "\n"
+ "\nPlease fix both bugs!"
+ "\n"
+ );
+ }
+
+ strcat_s(Buf,ARRAY_SIZE(Buf), "\n\n");
+
+ if (!foundconflict && count != 0)
+ {
+ if (whichTest == BaseContract::THROWS_No)
+ {
+ strcat_s(Buf,ARRAY_SIZE(Buf), "You can't throw here because there is no handler on the stack.\n");
+ }
+ else
+ {
+ strcat_s(Buf,ARRAY_SIZE(Buf), "We can't find the violated contract. Look for an old-style non-holder-based contract.\n");
+ }
+ }
+
+ DbgAssertDialog((char *)szFile, lineNum, Buf);
+ _check.LeaveAssert();
+ }
+}
+
+
+FORCEINLINE BOOL BaseContract::EnforceContract()
+{
+ if (s_alwaysEnforceContracts)
+ return TRUE;
+ else
+ return CHECK::EnforceAssert();
+}
+
+inline void BaseContract::SetUnconditionalContractEnforcement(BOOL value)
+{
+ s_alwaysEnforceContracts = value;
+}
+
+inline UINT GetDbgStateCombinedLockCount()
+{
+ return GetClrDebugState()->GetCombinedLockCount();
+}
+inline UINT GetDbgStateLockCount(DbgStateLockType dbgStateLockType)
+{
+ return GetClrDebugState()->GetLockCount(dbgStateLockType);
+}
+
+#define ASSERT_NO_USER_LOCKS_HELD() \
+ _ASSERTE(GetDbgStateLockCount(kDbgStateLockType_User) == 0)
+#define ASSERT_NO_HOST_BREAKABLE_CRSTS_HELD() \
+ _ASSERTE(GetDbgStateLockCount(kDbgStateLockType_HostBreakableCrst) == 0)
+#define ASSERT_NO_EE_LOCKS_HELD() \
+ _ASSERTE(GetDbgStateLockCount(kDbgStateLockType_EE) == 0)
+
+#else // ENABLE_CONTRACTS_IMPL
+
+#define ASSERT_NO_USER_LOCKS_HELD()
+#define ASSERT_NO_HOST_BREAKABLE_CRSTS_HELD()
+#define ASSERT_NO_EE_LOCKS_HELD()
+
+#endif // ENABLE_CONTRACTS_IMPL
+
+#endif // CONTRACT_INL_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*****************************************************************************
+ ** **
+ ** Corhlprpriv.h - **
+ ** **
+ *****************************************************************************/
+
+#ifndef __CORHLPRPRIV_H__
+#define __CORHLPRPRIV_H__
+
+#include "corhlpr.h"
+#include "fstring.h"
+
+#if defined(_MSC_VER) && defined(HOST_X86)
+#pragma optimize("y", on) // If routines don't get inlined, don't pay the EBP frame penalty
+#endif
+
+//*****************************************************************************
+//
+//***** Utility helpers
+//
+//*****************************************************************************
+
+#ifndef SOS_INCLUDE
+
+//*****************************************************************************
+//
+// **** CQuickBytes
+// This helper class is useful for cases where 90% of the time you allocate 512
+// or less bytes for a data structure. This class contains a 512 byte buffer.
+// Alloc() will return a pointer to this buffer if your allocation is small
+// enough, otherwise it asks the heap for a larger buffer which is freed for
+// you. No mutex locking is required for the small allocation case, making the
+// code run faster, less heap fragmentation, etc... Each instance will allocate
+// 520 bytes, so use accordinly.
+//
+//*****************************************************************************
+namespace NSQuickBytesHelper
+{
+ template <BOOL bThrow>
+ struct _AllocBytes;
+
+ template <>
+ struct _AllocBytes<TRUE>
+ {
+ static BYTE *Invoke(SIZE_T iItems)
+ {
+ return NEW_THROWS(iItems);
+ }
+ };
+
+ template <>
+ struct _AllocBytes<FALSE>
+ {
+ static BYTE *Invoke(SIZE_T iItems)
+ {
+ return NEW_NOTHROW(iItems);
+ }
+ };
+};
+
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr);
+
+template <SIZE_T SIZE, SIZE_T INCREMENT>
+class CQuickMemoryBase
+{
+protected:
+ template <typename ELEM_T>
+ static ELEM_T Min(ELEM_T a, ELEM_T b)
+ { return a < b ? a : b; }
+
+ template <typename ELEM_T>
+ static ELEM_T Max(ELEM_T a, ELEM_T b)
+ { return a < b ? b : a; }
+
+ // bGrow - indicates that this is a resize and that the original data
+ // needs to be copied over.
+ // bThrow - indicates whether or not memory allocations will throw.
+ template <BOOL bGrow, BOOL bThrow>
+ void *_Alloc(SIZE_T iItems)
+ {
+#if defined(_BLD_CLR) && defined(_DEBUG)
+ { // Exercise heap for OOM-fault injection purposes
+ BYTE * pb = NSQuickBytesHelper::_AllocBytes<bThrow>::Invoke(iItems);
+ _ASSERTE(!bThrow || pb != NULL); // _AllocBytes would have thrown if bThrow == TRUE
+ if (pb == NULL) return NULL; // bThrow == FALSE and we failed to allocate memory
+ delete [] pb; // Success, delete allocated memory.
+ }
+#endif
+ if (iItems <= cbTotal)
+ { // Fits within existing memory allocation
+ iSize = iItems;
+ }
+ else if (iItems <= SIZE)
+ { // Will fit in internal buffer.
+ if (pbBuff == NULL)
+ { // Any previous allocation is in the internal buffer and the new
+ // allocation fits in the internal buffer, so just update the size.
+ iSize = iItems;
+ cbTotal = SIZE;
+ }
+ else
+ { // There was a previous allocation, sitting in pbBuff
+ if (bGrow)
+ { // If growing, need to copy any existing data over.
+ memcpy(&rgData[0], pbBuff, Min(cbTotal, SIZE));
+ }
+
+ delete [] pbBuff;
+ pbBuff = NULL;
+ iSize = iItems;
+ cbTotal = SIZE;
+ }
+ }
+ else
+ { // Need to allocate a new buffer
+ SIZE_T cbTotalNew = iItems + (bGrow ? INCREMENT : 0);
+ BYTE * pbBuffNew = NSQuickBytesHelper::_AllocBytes<bThrow>::Invoke(cbTotalNew);
+
+ if (!bThrow && pbBuffNew == NULL)
+ { // Allocation failed. Zero out structure.
+ if (pbBuff != NULL)
+ { // Delete old buffer
+ delete [] pbBuff;
+ }
+ pbBuff = NULL;
+ iSize = 0;
+ cbTotal = 0;
+ return NULL;
+ }
+
+ if (bGrow && cbTotal > 0)
+ { // If growing, need to copy any existing data over.
+ memcpy(pbBuffNew, (BYTE *)Ptr(), Min(cbTotal, cbTotalNew));
+ }
+
+ if (pbBuff != NULL)
+ { // Delete old pre-existing buffer
+ delete [] pbBuff;
+ pbBuff = NULL;
+ }
+
+ pbBuff = pbBuffNew;
+ cbTotal = cbTotalNew;
+ iSize = iItems;
+ }
+
+ return Ptr();
+ }
+
+public:
+ void Init()
+ {
+ pbBuff = 0;
+ iSize = 0;
+ cbTotal = SIZE;
+ }
+
+ void Destroy()
+ {
+ if (pbBuff)
+ {
+ delete [] pbBuff;
+ pbBuff = 0;
+ }
+ }
+
+ void *AllocThrows(SIZE_T iItems)
+ {
+ return _Alloc<FALSE /*bGrow*/, TRUE /*bThrow*/>(iItems);
+ }
+
+ void *AllocNoThrow(SIZE_T iItems)
+ {
+ return _Alloc<FALSE /*bGrow*/, FALSE /*bThrow*/>(iItems);
+ }
+
+ void ReSizeThrows(SIZE_T iItems)
+ {
+ _Alloc<TRUE /*bGrow*/, TRUE /*bThrow*/>(iItems);
+ }
+
+#ifdef __GNUC__
+ // This makes sure that we will not get an undefined symbol
+ // when building a release version of libcoreclr using LLVM/GCC.
+ __attribute__((used))
+#endif // __GNUC__
+ HRESULT ReSizeNoThrow(SIZE_T iItems);
+
+ void Shrink(SIZE_T iItems)
+ {
+ _ASSERTE(iItems <= cbTotal);
+ iSize = iItems;
+ }
+
+ operator PVOID()
+ {
+ return ((pbBuff) ? pbBuff : (PVOID)&rgData[0]);
+ }
+
+ void *Ptr()
+ {
+ return ((pbBuff) ? pbBuff : (PVOID)&rgData[0]);
+ }
+
+ const void *Ptr() const
+ {
+ return ((pbBuff) ? pbBuff : (PVOID)&rgData[0]);
+ }
+
+ SIZE_T Size() const
+ {
+ return (iSize);
+ }
+
+ SIZE_T MaxSize() const
+ {
+ return (cbTotal);
+ }
+
+ void Maximize()
+ {
+ iSize = cbTotal;
+ }
+
+
+ // Convert UTF8 string to UNICODE string, optimized for speed
+ HRESULT ConvertUtf8_UnicodeNoThrow(const char * utf8str)
+ {
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = FString::Utf8_Unicode_Length(utf8str, & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ LPWSTR buffer = (LPWSTR) AllocNoThrow((length + 1) * sizeof(WCHAR));
+
+ if (buffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ hr = FString::Utf8_Unicode(utf8str, allAscii, buffer, length);
+ }
+ }
+
+ return hr;
+ }
+
+ // Convert UTF8 string to UNICODE string, optimized for speed
+ void ConvertUtf8_Unicode(const char * utf8str)
+ {
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = FString::Utf8_Unicode_Length(utf8str, & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ LPWSTR buffer = (LPWSTR) AllocThrows((length + 1) * sizeof(WCHAR));
+
+ hr = FString::Utf8_Unicode(utf8str, allAscii, buffer, length);
+ }
+
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ }
+
+ // Convert UNICODE string to UTF8 string, optimized for speed
+ void ConvertUnicode_Utf8(const WCHAR * pString)
+ {
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = FString::Unicode_Utf8_Length(pString, & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ LPSTR buffer = (LPSTR) AllocThrows((length + 1) * sizeof(char));
+
+ hr = FString::Unicode_Utf8(pString, allAscii, buffer, length);
+ }
+
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ }
+
+ // Copy single byte string and hold it
+ const char * SetStringNoThrow(const char * pStr, SIZE_T len)
+ {
+ LPSTR buffer = (LPSTR) AllocNoThrow(len + 1);
+
+ if (buffer != NULL)
+ {
+ memcpy(buffer, pStr, len);
+ buffer[len] = 0;
+ }
+
+ return buffer;
+ }
+
+#ifdef DACCESS_COMPILE
+ void
+ EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+ {
+ // Assume that 'this' is enumerated, either explicitly
+ // or because this class is embedded in another.
+ DacEnumMemoryRegion(dac_cast<TADDR>(pbBuff), iSize);
+ }
+#endif // DACCESS_COMPILE
+
+ BYTE *pbBuff;
+ SIZE_T iSize; // number of bytes used
+ SIZE_T cbTotal; // total bytes allocated in the buffer
+ // use UINT64 to enforce the alignment of the memory
+ UINT64 rgData[(SIZE+sizeof(UINT64)-1)/sizeof(UINT64)];
+};
+
+// These should be multiples of 8 so that data can be naturally aligned.
+#define CQUICKBYTES_BASE_SIZE 512
+#define CQUICKBYTES_INCREMENTAL_SIZE 128
+
+class CQuickBytesBase : public CQuickMemoryBase<CQUICKBYTES_BASE_SIZE, CQUICKBYTES_INCREMENTAL_SIZE>
+{
+};
+
+
+class CQuickBytes : public CQuickBytesBase
+{
+public:
+ CQuickBytes()
+ {
+ Init();
+ }
+
+ ~CQuickBytes()
+ {
+ Destroy();
+ }
+};
+
+/* to be used as static variable - no constructor/destructor, assumes zero
+ initialized memory */
+class CQuickBytesStatic : public CQuickBytesBase
+{
+};
+
+template <SIZE_T CQUICKBYTES_BASE_SPECIFY_SIZE>
+class CQuickBytesSpecifySizeBase : public CQuickMemoryBase<CQUICKBYTES_BASE_SPECIFY_SIZE, CQUICKBYTES_INCREMENTAL_SIZE>
+{
+};
+
+template <SIZE_T CQUICKBYTES_BASE_SPECIFY_SIZE>
+class CQuickBytesSpecifySize : public CQuickBytesSpecifySizeBase<CQUICKBYTES_BASE_SPECIFY_SIZE>
+{
+public:
+ CQuickBytesSpecifySize()
+ {
+ this->Init();
+ }
+
+ ~CQuickBytesSpecifySize()
+ {
+ this->Destroy();
+ }
+};
+
+/* to be used as static variable - no constructor/destructor, assumes zero
+ initialized memory */
+template <SIZE_T CQUICKBYTES_BASE_SPECIFY_SIZE>
+class CQuickBytesSpecifySizeStatic : public CQuickBytesSpecifySizeBase<CQUICKBYTES_BASE_SPECIFY_SIZE>
+{
+};
+
+template <class T> class CQuickArrayBase : public CQuickBytesBase
+{
+public:
+ T* AllocThrows(SIZE_T iItems)
+ {
+ CheckOverflowThrows(iItems);
+ return (T*)CQuickBytesBase::AllocThrows(iItems * sizeof(T));
+ }
+
+ void ReSizeThrows(SIZE_T iItems)
+ {
+ CheckOverflowThrows(iItems);
+ CQuickBytesBase::ReSizeThrows(iItems * sizeof(T));
+ }
+
+ T* AllocNoThrow(SIZE_T iItems)
+ {
+ if (!CheckOverflowNoThrow(iItems))
+ {
+ return NULL;
+ }
+ return (T*)CQuickBytesBase::AllocNoThrow(iItems * sizeof(T));
+ }
+
+ HRESULT ReSizeNoThrow(SIZE_T iItems)
+ {
+ if (!CheckOverflowNoThrow(iItems))
+ {
+ return E_OUTOFMEMORY;
+ }
+ return CQuickBytesBase::ReSizeNoThrow(iItems * sizeof(T));
+ }
+
+ void Shrink(SIZE_T iItems)
+ {
+ CQuickBytesBase::Shrink(iItems * sizeof(T));
+ }
+
+ T* Ptr()
+ {
+ return (T*) CQuickBytesBase::Ptr();
+ }
+
+ const T* Ptr() const
+ {
+ return (T*) CQuickBytesBase::Ptr();
+ }
+
+ SIZE_T Size() const
+ {
+ return CQuickBytesBase::Size() / sizeof(T);
+ }
+
+ SIZE_T MaxSize() const
+ {
+ return CQuickBytesBase::cbTotal / sizeof(T);
+ }
+
+ T& operator[] (SIZE_T ix)
+ {
+ _ASSERTE(ix < Size());
+ return *(Ptr() + ix);
+ }
+
+ const T& operator[] (SIZE_T ix) const
+ {
+ _ASSERTE(ix < Size());
+ return *(Ptr() + ix);
+ }
+
+private:
+ inline
+ BOOL CheckOverflowNoThrow(SIZE_T iItems)
+ {
+ SIZE_T totalSize = iItems * sizeof(T);
+
+ if (totalSize / sizeof(T) != iItems)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ inline
+ void CheckOverflowThrows(SIZE_T iItems)
+ {
+ if (!CheckOverflowNoThrow(iItems))
+ {
+ THROW_OUT_OF_MEMORY();
+ }
+ }
+};
+
+template <class T> class CQuickArray : public CQuickArrayBase<T>
+{
+public:
+ CQuickArray<T>()
+ {
+ this->Init();
+ }
+
+ ~CQuickArray<T>()
+ {
+ this->Destroy();
+ }
+};
+
+// This is actually more of a stack with array access. Essentially, you can
+// only add elements through Push and remove them through Pop, but you can
+// access and modify any random element with the index operator. You cannot
+// access elements that have not been added.
+
+template <class T>
+class CQuickArrayList : protected CQuickArray<T>
+{
+private:
+ SIZE_T m_curSize;
+
+public:
+ // Make these specific functions public.
+ using CQuickArray<T>::AllocThrows;
+ using CQuickArray<T>::ReSizeThrows;
+ using CQuickArray<T>::AllocNoThrow;
+ using CQuickArray<T>::ReSizeNoThrow;
+ using CQuickArray<T>::MaxSize;
+ using CQuickArray<T>::Ptr;
+
+ CQuickArrayList()
+ : m_curSize(0)
+ {
+ this->Init();
+ }
+
+ ~CQuickArrayList()
+ {
+ this->Destroy();
+ }
+
+ // Can only access values that have been pushed.
+ T& operator[] (SIZE_T ix)
+ {
+ _ASSERTE(ix < m_curSize);
+ return CQuickArray<T>::operator[](ix);
+ }
+
+ // Can only access values that have been pushed.
+ const T& operator[] (SIZE_T ix) const
+ {
+ _ASSERTE(ix < m_curSize);
+ return CQuickArray<T>::operator[](ix);
+ }
+
+ // THROWS: Resizes if necessary.
+ void Push(const T & value)
+ {
+ // Resize if necessary - thows.
+ if (m_curSize + 1 >= CQuickArray<T>::Size())
+ ReSizeThrows((m_curSize + 1) * 2);
+
+ // Append element to end of array.
+ _ASSERTE(m_curSize + 1 < CQuickArray<T>::Size());
+ SIZE_T ix = m_curSize++;
+ (*this)[ix] = value;
+ }
+
+ // NOTHROW: Resizes if necessary.
+ BOOL PushNoThrow(const T & value)
+ {
+ // Resize if necessary - nothow.
+ if (m_curSize + 1 >= CQuickArray<T>::Size()) {
+ if (ReSizeNoThrow((m_curSize + 1) * 2) != NOERROR)
+ return FALSE;
+ }
+
+ // Append element to end of array.
+ _ASSERTE(m_curSize + 1 < CQuickArray<T>::Size());
+ SIZE_T ix = m_curSize++;
+ (*this)[ix] = value;
+ return TRUE;
+ }
+
+ T Pop()
+ {
+ _ASSERTE(m_curSize > 0);
+ T retval = (*this)[m_curSize - 1];
+ INDEBUG(ZeroMemory(&(this->Ptr()[m_curSize - 1]), sizeof(T));)
+ --m_curSize;
+ return retval;
+ }
+
+ SIZE_T Size() const
+ {
+ return m_curSize;
+ }
+
+ void Shrink()
+ {
+ CQuickArray<T>::Shrink(m_curSize);
+ }
+};
+
+
+/* to be used as static variable - no constructor/destructor, assumes zero
+ initialized memory */
+template <class T> class CQuickArrayStatic : public CQuickArrayBase<T>
+{
+};
+
+typedef CQuickArrayBase<WCHAR> CQuickWSTRBase;
+typedef CQuickArray<WCHAR> CQuickWSTR;
+typedef CQuickArrayStatic<WCHAR> CQuickWSTRStatic;
+
+typedef CQuickArrayBase<CHAR> CQuickSTRBase;
+typedef CQuickArray<CHAR> CQuickSTR;
+typedef CQuickArrayStatic<CHAR> CQuickSTRStatic;
+
+class RidBitmap
+{
+public:
+ HRESULT InsertToken(mdToken token)
+ {
+ HRESULT hr = S_OK;
+ mdToken rid = RidFromToken(token);
+ SIZE_T index = rid / 8;
+ BYTE bit = (1 << (rid % 8));
+
+ if (index >= buffer.Size())
+ {
+ SIZE_T oldSize = buffer.Size();
+ SIZE_T newSize = index+1+oldSize/8;
+ IfFailRet(buffer.ReSizeNoThrow(newSize));
+ memset(&buffer[oldSize], 0, newSize-oldSize);
+ }
+
+ buffer[index] |= bit;
+ return hr;
+ }
+
+ bool IsTokenInBitmap(mdToken token)
+ {
+ mdToken rid = RidFromToken(token);
+ SIZE_T index = rid / 8;
+ BYTE bit = (1 << (rid % 8));
+
+ return ((index < buffer.Size()) && (buffer[index] & bit));
+ }
+
+ void Reset()
+ {
+ if (buffer.Size())
+ {
+ memset(&buffer[0], 0, buffer.Size());
+ }
+ }
+
+private:
+ CQuickArray<BYTE> buffer;
+};
+
+//*****************************************************************************
+//
+//***** Signature helpers
+//
+//*****************************************************************************
+
+HRESULT _CountBytesOfOneArg(
+ PCCOR_SIGNATURE pbSig,
+ ULONG *pcbTotal);
+
+HRESULT _GetFixedSigOfVarArg( // S_OK or error.
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob of CLR signature
+ ULONG cbSigBlob, // [IN] size of signature
+ CQuickBytes *pqbSig, // [OUT] output buffer for fixed part of VarArg Signature
+ ULONG *pcbSigBlob); // [OUT] number of bytes written to the above output buffer
+
+#endif //!SOS_INCLUDE
+
+#if defined(_MSC_VER) && defined(TARGET_X86)
+#pragma optimize("", on) // restore command line default optimizations
+#endif
+
+
+//---------------------------------------------------------------------------------------
+//
+// Reads compressed integer from buffer pData, fills the result to *pnDataOut. Advances buffer pointer.
+// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
+//
+inline
+__checkReturn
+HRESULT
+CorSigUncompressData_EndPtr(
+ PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
+ PCCOR_SIGNATURE pDataEnd, // End of buffer
+ DWORD * pnDataOut) // [OUT] Compressed integer read from the buffer
+{
+ _ASSERTE(pData <= pDataEnd);
+ HRESULT hr = S_OK;
+
+ INT_PTR cbDataSize = pDataEnd - pData;
+ if (cbDataSize > 4)
+ { // Compressed integer cannot be bigger than 4 bytes
+ cbDataSize = 4;
+ }
+ DWORD dwDataSize = (DWORD)cbDataSize;
+
+ ULONG cbDataOutLength;
+ IfFailRet(CorSigUncompressData(
+ pData,
+ dwDataSize,
+ pnDataOut,
+ &cbDataOutLength));
+ pData += cbDataOutLength;
+
+ return hr;
+} // CorSigUncompressData_EndPtr
+
+//---------------------------------------------------------------------------------------
+//
+// Reads CorElementType (1 byte) from buffer pData, fills the result to *pTypeOut. Advances buffer pointer.
+// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
+//
+inline
+__checkReturn
+HRESULT
+CorSigUncompressElementType_EndPtr(
+ PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
+ PCCOR_SIGNATURE pDataEnd, // End of buffer
+ CorElementType * pTypeOut) // [OUT] ELEMENT_TYPE_* value read from the buffer
+{
+ _ASSERTE(pData <= pDataEnd);
+ // We don't expect pData > pDataEnd, but the runtime check doesn't cost much and it is more secure in
+ // case caller has a bug
+ if (pData >= pDataEnd)
+ { // No data
+ return META_E_BAD_SIGNATURE;
+ }
+ // Read 'type' as 1 byte
+ *pTypeOut = (CorElementType)*pData;
+ pData++;
+
+ return S_OK;
+} // CorSigUncompressElementType_EndPtr
+
+//---------------------------------------------------------------------------------------
+//
+// Reads pointer (4/8 bytes) from buffer pData, fills the result to *ppvPointerOut. Advances buffer pointer.
+// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
+//
+inline
+__checkReturn
+HRESULT
+CorSigUncompressPointer_EndPtr(
+ PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
+ PCCOR_SIGNATURE pDataEnd, // End of buffer
+ void ** ppvPointerOut) // [OUT] Pointer value read from the buffer
+{
+ _ASSERTE(pData <= pDataEnd);
+ // We could just skip this check as pointers should be only in trusted (and therefore correct)
+ // signatures and we check for that on the caller side, but it won't hurt to have this check and it will
+ // make it easier to catch invalid signatures in trusted code (e.g. IL stubs, NGEN images, etc.)
+ if (pData + sizeof(void *) > pDataEnd)
+ { // Not enough data in the buffer
+ _ASSERTE(!"This signature is invalid. Note that caller should check that it is not comming from untrusted source!");
+ return META_E_BAD_SIGNATURE;
+ }
+ *ppvPointerOut = *(void * UNALIGNED *)pData;
+ pData += sizeof(void *);
+
+ return S_OK;
+} // CorSigUncompressPointer_EndPtr
+
+//---------------------------------------------------------------------------------------
+//
+// Reads compressed TypeDef/TypeRef/TypeSpec token, fills the result to *pnDataOut. Advances buffer pointer.
+// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
+//
+inline
+__checkReturn
+HRESULT
+CorSigUncompressToken_EndPtr(
+ PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
+ PCCOR_SIGNATURE pDataEnd, // End of buffer
+ mdToken * ptkTokenOut) // [OUT] Token read from the buffer
+{
+ _ASSERTE(pData <= pDataEnd);
+ HRESULT hr = S_OK;
+
+ INT_PTR cbDataSize = pDataEnd - pData;
+ if (cbDataSize > 4)
+ { // Compressed token cannot be bigger than 4 bytes
+ cbDataSize = 4;
+ }
+ DWORD dwDataSize = (DWORD)cbDataSize;
+
+ uint32_t cbTokenOutLength;
+ IfFailRet(CorSigUncompressToken(
+ pData,
+ dwDataSize,
+ ptkTokenOut,
+ &cbTokenOutLength));
+ pData += cbTokenOutLength;
+
+ return hr;
+} // CorSigUncompressToken_EndPtr
+
+#endif // __CORHLPRPRIV_H__
--- /dev/null
+//
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+#ifndef __CRST_TYPES_INCLUDED
+#define __CRST_TYPES_INCLUDED
+
+// **** THIS IS AN AUTOMATICALLY GENERATED HEADER FILE -- DO NOT EDIT!!! ****
+
+// This file describes the range of Crst types available and their mapping to a numeric level (used by the
+// runtime in debug mode to validate we're deadlock free). To modify these settings edit the
+// file:CrstTypes.def file and run the clr\artifacts\CrstTypeTool utility to generate a new version of this file.
+
+// Each Crst type is declared as a value in the following CrstType enum.
+enum CrstType
+{
+ CrstAppDomainCache = 0,
+ CrstAppDomainHandleTable = 1,
+ CrstArgBasedStubCache = 2,
+ CrstAssemblyList = 3,
+ CrstAssemblyLoader = 4,
+ CrstAvailableClass = 5,
+ CrstAvailableParamTypes = 6,
+ CrstBaseDomain = 7,
+ CrstCCompRC = 8,
+ CrstClassFactInfoHash = 9,
+ CrstClassInit = 10,
+ CrstClrNotification = 11,
+ CrstCodeFragmentHeap = 12,
+ CrstCodeVersioning = 13,
+ CrstCOMCallWrapper = 14,
+ CrstCOMWrapperCache = 15,
+ CrstDataTest1 = 16,
+ CrstDataTest2 = 17,
+ CrstDbgTransport = 18,
+ CrstDeadlockDetection = 19,
+ CrstDebuggerController = 20,
+ CrstDebuggerFavorLock = 21,
+ CrstDebuggerHeapExecMemLock = 22,
+ CrstDebuggerHeapLock = 23,
+ CrstDebuggerJitInfo = 24,
+ CrstDebuggerMutex = 25,
+ CrstDelegateToFPtrHash = 26,
+ CrstDomainLocalBlock = 27,
+ CrstDynamicIL = 28,
+ CrstDynamicMT = 29,
+ CrstEtwTypeLogHash = 30,
+ CrstEventPipe = 31,
+ CrstEventStore = 32,
+ CrstException = 33,
+ CrstExecutableAllocatorLock = 34,
+ CrstExecuteManRangeLock = 35,
+ CrstExternalObjectContextCache = 36,
+ CrstFCall = 37,
+ CrstFuncPtrStubs = 38,
+ CrstFusionAppCtx = 39,
+ CrstGCCover = 40,
+ CrstGlobalStrLiteralMap = 41,
+ CrstHandleTable = 42,
+ CrstIbcProfile = 43,
+ CrstIJWFixupData = 44,
+ CrstIJWHash = 45,
+ CrstILStubGen = 46,
+ CrstInlineTrackingMap = 47,
+ CrstInstMethodHashTable = 48,
+ CrstInterop = 49,
+ CrstInteropData = 50,
+ CrstIsJMCMethod = 51,
+ CrstISymUnmanagedReader = 52,
+ CrstJit = 53,
+ CrstJitGenericHandleCache = 54,
+ CrstJitInlineTrackingMap = 55,
+ CrstJitPatchpoint = 56,
+ CrstJitPerf = 57,
+ CrstJumpStubCache = 58,
+ CrstLeafLock = 59,
+ CrstListLock = 60,
+ CrstLoaderAllocator = 61,
+ CrstLoaderAllocatorReferences = 62,
+ CrstLoaderHeap = 63,
+ CrstManagedObjectWrapperMap = 64,
+ CrstMethodDescBackpatchInfoTracker = 65,
+ CrstModule = 66,
+ CrstModuleFixup = 67,
+ CrstModuleLookupTable = 68,
+ CrstMulticoreJitHash = 69,
+ CrstMulticoreJitManager = 70,
+ CrstNativeImageEagerFixups = 71,
+ CrstNativeImageLoad = 72,
+ CrstNls = 73,
+ CrstNotifyGdb = 74,
+ CrstObjectList = 75,
+ CrstPEImage = 76,
+ CrstPendingTypeLoadEntry = 77,
+ CrstPgoData = 78,
+ CrstPinnedByrefValidation = 79,
+ CrstProfilerGCRefDataFreeList = 80,
+ CrstProfilingAPIStatus = 81,
+ CrstRCWCache = 82,
+ CrstRCWCleanupList = 83,
+ CrstReadyToRunEntryPointToMethodDescMap = 84,
+ CrstReflection = 85,
+ CrstReJITGlobalRequest = 86,
+ CrstRetThunkCache = 87,
+ CrstSavedExceptionInfo = 88,
+ CrstSaveModuleProfileData = 89,
+ CrstSecurityStackwalkCache = 90,
+ CrstSigConvert = 91,
+ CrstSingleUseLock = 92,
+ CrstSpecialStatics = 93,
+ CrstStackSampler = 94,
+ CrstStressLog = 95,
+ CrstStubCache = 96,
+ CrstStubDispatchCache = 97,
+ CrstStubUnwindInfoHeapSegments = 98,
+ CrstSyncBlockCache = 99,
+ CrstSyncHashLock = 100,
+ CrstSystemBaseDomain = 101,
+ CrstSystemDomain = 102,
+ CrstSystemDomainDelayedUnloadList = 103,
+ CrstThreadIdDispenser = 104,
+ CrstThreadpoolTimerQueue = 105,
+ CrstThreadpoolWaitThreads = 106,
+ CrstThreadpoolWorker = 107,
+ CrstThreadStore = 108,
+ CrstTieredCompilation = 109,
+ CrstTypeEquivalenceMap = 110,
+ CrstTypeIDMap = 111,
+ CrstUMEntryThunkCache = 112,
+ CrstUMEntryThunkFreeListLock = 113,
+ CrstUniqueStack = 114,
+ CrstUnresolvedClassLock = 115,
+ CrstUnwindInfoTableLock = 116,
+ CrstVSDIndirectionCellLock = 117,
+ CrstWrapperTemplate = 118,
+ kNumberOfCrstTypes = 119
+};
+
+#endif // __CRST_TYPES_INCLUDED
+
+// Define some debug data in one module only -- vm\crst.cpp.
+#if defined(__IN_CRST_CPP) && defined(_DEBUG)
+
+// An array mapping CrstType to level.
+int g_rgCrstLevelMap[] =
+{
+ 10, // CrstAppDomainCache
+ 14, // CrstAppDomainHandleTable
+ 3, // CrstArgBasedStubCache
+ 0, // CrstAssemblyList
+ 12, // CrstAssemblyLoader
+ 4, // CrstAvailableClass
+ 5, // CrstAvailableParamTypes
+ 7, // CrstBaseDomain
+ -1, // CrstCCompRC
+ 13, // CrstClassFactInfoHash
+ 11, // CrstClassInit
+ -1, // CrstClrNotification
+ 6, // CrstCodeFragmentHeap
+ 9, // CrstCodeVersioning
+ 0, // CrstCOMCallWrapper
+ 5, // CrstCOMWrapperCache
+ 3, // CrstDataTest1
+ 0, // CrstDataTest2
+ 0, // CrstDbgTransport
+ 0, // CrstDeadlockDetection
+ -1, // CrstDebuggerController
+ 3, // CrstDebuggerFavorLock
+ 0, // CrstDebuggerHeapExecMemLock
+ 0, // CrstDebuggerHeapLock
+ 4, // CrstDebuggerJitInfo
+ 10, // CrstDebuggerMutex
+ 0, // CrstDelegateToFPtrHash
+ 16, // CrstDomainLocalBlock
+ 0, // CrstDynamicIL
+ 3, // CrstDynamicMT
+ 0, // CrstEtwTypeLogHash
+ 18, // CrstEventPipe
+ 0, // CrstEventStore
+ 0, // CrstException
+ 0, // CrstExecutableAllocatorLock
+ 0, // CrstExecuteManRangeLock
+ 0, // CrstExternalObjectContextCache
+ 4, // CrstFCall
+ 7, // CrstFuncPtrStubs
+ 10, // CrstFusionAppCtx
+ 10, // CrstGCCover
+ 13, // CrstGlobalStrLiteralMap
+ 1, // CrstHandleTable
+ 0, // CrstIbcProfile
+ 8, // CrstIJWFixupData
+ 0, // CrstIJWHash
+ 7, // CrstILStubGen
+ 3, // CrstInlineTrackingMap
+ 17, // CrstInstMethodHashTable
+ 20, // CrstInterop
+ 5, // CrstInteropData
+ 0, // CrstIsJMCMethod
+ 7, // CrstISymUnmanagedReader
+ 11, // CrstJit
+ 0, // CrstJitGenericHandleCache
+ 16, // CrstJitInlineTrackingMap
+ 4, // CrstJitPatchpoint
+ -1, // CrstJitPerf
+ 6, // CrstJumpStubCache
+ 0, // CrstLeafLock
+ -1, // CrstListLock
+ 15, // CrstLoaderAllocator
+ 16, // CrstLoaderAllocatorReferences
+ 3, // CrstLoaderHeap
+ 3, // CrstManagedObjectWrapperMap
+ 14, // CrstMethodDescBackpatchInfoTracker
+ 5, // CrstModule
+ 15, // CrstModuleFixup
+ 4, // CrstModuleLookupTable
+ 0, // CrstMulticoreJitHash
+ 13, // CrstMulticoreJitManager
+ 0, // CrstNativeImageEagerFixups
+ 0, // CrstNativeImageLoad
+ 0, // CrstNls
+ 0, // CrstNotifyGdb
+ 2, // CrstObjectList
+ 5, // CrstPEImage
+ 19, // CrstPendingTypeLoadEntry
+ 4, // CrstPgoData
+ 0, // CrstPinnedByrefValidation
+ 0, // CrstProfilerGCRefDataFreeList
+ 13, // CrstProfilingAPIStatus
+ 4, // CrstRCWCache
+ 0, // CrstRCWCleanupList
+ 10, // CrstReadyToRunEntryPointToMethodDescMap
+ 8, // CrstReflection
+ 17, // CrstReJITGlobalRequest
+ 4, // CrstRetThunkCache
+ 3, // CrstSavedExceptionInfo
+ 0, // CrstSaveModuleProfileData
+ 0, // CrstSecurityStackwalkCache
+ 4, // CrstSigConvert
+ 5, // CrstSingleUseLock
+ 0, // CrstSpecialStatics
+ 0, // CrstStackSampler
+ -1, // CrstStressLog
+ 5, // CrstStubCache
+ 0, // CrstStubDispatchCache
+ 4, // CrstStubUnwindInfoHeapSegments
+ 3, // CrstSyncBlockCache
+ 0, // CrstSyncHashLock
+ 5, // CrstSystemBaseDomain
+ 13, // CrstSystemDomain
+ 0, // CrstSystemDomainDelayedUnloadList
+ 0, // CrstThreadIdDispenser
+ 7, // CrstThreadpoolTimerQueue
+ 7, // CrstThreadpoolWaitThreads
+ 13, // CrstThreadpoolWorker
+ 12, // CrstThreadStore
+ 8, // CrstTieredCompilation
+ 4, // CrstTypeEquivalenceMap
+ 10, // CrstTypeIDMap
+ 4, // CrstUMEntryThunkCache
+ 3, // CrstUMEntryThunkFreeListLock
+ 4, // CrstUniqueStack
+ 7, // CrstUnresolvedClassLock
+ 3, // CrstUnwindInfoTableLock
+ 4, // CrstVSDIndirectionCellLock
+ 3, // CrstWrapperTemplate
+};
+
+// An array mapping CrstType to a stringized name.
+LPCSTR g_rgCrstNameMap[] =
+{
+ "CrstAppDomainCache",
+ "CrstAppDomainHandleTable",
+ "CrstArgBasedStubCache",
+ "CrstAssemblyList",
+ "CrstAssemblyLoader",
+ "CrstAvailableClass",
+ "CrstAvailableParamTypes",
+ "CrstBaseDomain",
+ "CrstCCompRC",
+ "CrstClassFactInfoHash",
+ "CrstClassInit",
+ "CrstClrNotification",
+ "CrstCodeFragmentHeap",
+ "CrstCodeVersioning",
+ "CrstCOMCallWrapper",
+ "CrstCOMWrapperCache",
+ "CrstDataTest1",
+ "CrstDataTest2",
+ "CrstDbgTransport",
+ "CrstDeadlockDetection",
+ "CrstDebuggerController",
+ "CrstDebuggerFavorLock",
+ "CrstDebuggerHeapExecMemLock",
+ "CrstDebuggerHeapLock",
+ "CrstDebuggerJitInfo",
+ "CrstDebuggerMutex",
+ "CrstDelegateToFPtrHash",
+ "CrstDomainLocalBlock",
+ "CrstDynamicIL",
+ "CrstDynamicMT",
+ "CrstEtwTypeLogHash",
+ "CrstEventPipe",
+ "CrstEventStore",
+ "CrstException",
+ "CrstExecutableAllocatorLock",
+ "CrstExecuteManRangeLock",
+ "CrstExternalObjectContextCache",
+ "CrstFCall",
+ "CrstFuncPtrStubs",
+ "CrstFusionAppCtx",
+ "CrstGCCover",
+ "CrstGlobalStrLiteralMap",
+ "CrstHandleTable",
+ "CrstIbcProfile",
+ "CrstIJWFixupData",
+ "CrstIJWHash",
+ "CrstILStubGen",
+ "CrstInlineTrackingMap",
+ "CrstInstMethodHashTable",
+ "CrstInterop",
+ "CrstInteropData",
+ "CrstIsJMCMethod",
+ "CrstISymUnmanagedReader",
+ "CrstJit",
+ "CrstJitGenericHandleCache",
+ "CrstJitInlineTrackingMap",
+ "CrstJitPatchpoint",
+ "CrstJitPerf",
+ "CrstJumpStubCache",
+ "CrstLeafLock",
+ "CrstListLock",
+ "CrstLoaderAllocator",
+ "CrstLoaderAllocatorReferences",
+ "CrstLoaderHeap",
+ "CrstManagedObjectWrapperMap",
+ "CrstMethodDescBackpatchInfoTracker",
+ "CrstModule",
+ "CrstModuleFixup",
+ "CrstModuleLookupTable",
+ "CrstMulticoreJitHash",
+ "CrstMulticoreJitManager",
+ "CrstNativeImageEagerFixups",
+ "CrstNativeImageLoad",
+ "CrstNls",
+ "CrstNotifyGdb",
+ "CrstObjectList",
+ "CrstPEImage",
+ "CrstPendingTypeLoadEntry",
+ "CrstPgoData",
+ "CrstPinnedByrefValidation",
+ "CrstProfilerGCRefDataFreeList",
+ "CrstProfilingAPIStatus",
+ "CrstRCWCache",
+ "CrstRCWCleanupList",
+ "CrstReadyToRunEntryPointToMethodDescMap",
+ "CrstReflection",
+ "CrstReJITGlobalRequest",
+ "CrstRetThunkCache",
+ "CrstSavedExceptionInfo",
+ "CrstSaveModuleProfileData",
+ "CrstSecurityStackwalkCache",
+ "CrstSigConvert",
+ "CrstSingleUseLock",
+ "CrstSpecialStatics",
+ "CrstStackSampler",
+ "CrstStressLog",
+ "CrstStubCache",
+ "CrstStubDispatchCache",
+ "CrstStubUnwindInfoHeapSegments",
+ "CrstSyncBlockCache",
+ "CrstSyncHashLock",
+ "CrstSystemBaseDomain",
+ "CrstSystemDomain",
+ "CrstSystemDomainDelayedUnloadList",
+ "CrstThreadIdDispenser",
+ "CrstThreadpoolTimerQueue",
+ "CrstThreadpoolWaitThreads",
+ "CrstThreadpoolWorker",
+ "CrstThreadStore",
+ "CrstTieredCompilation",
+ "CrstTypeEquivalenceMap",
+ "CrstTypeIDMap",
+ "CrstUMEntryThunkCache",
+ "CrstUMEntryThunkFreeListLock",
+ "CrstUniqueStack",
+ "CrstUnresolvedClassLock",
+ "CrstUnwindInfoTableLock",
+ "CrstVSDIndirectionCellLock",
+ "CrstWrapperTemplate",
+};
+
+// Define a special level constant for unordered locks.
+#define CRSTUNORDERED (-1)
+
+// Define inline helpers to map Crst types to names and levels.
+inline static int GetCrstLevel(CrstType crstType)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(crstType >= 0 && crstType < kNumberOfCrstTypes);
+ return g_rgCrstLevelMap[crstType];
+}
+inline static LPCSTR GetCrstName(CrstType crstType)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(crstType >= 0 && crstType < kNumberOfCrstTypes);
+ return g_rgCrstNameMap[crstType];
+}
+
+#endif // defined(__IN_CRST_CPP) && defined(_DEBUG)
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// CrtWrap.h
+//
+// Wrapper code for the C runtime library.
+//
+//*****************************************************************************
+
+#ifndef __CrtWrap_h__
+#define __CrtWrap_h__
+
+#include <stdint.h>
+#include <windows.h>
+#include <objbase.h>
+#include <tchar.h>
+#include "debugmacros.h"
+#include <stdlib.h>
+#include <malloc.h>
+#include <wchar.h>
+#include <stdio.h>
+
+#ifdef HOST_WINDOWS
+// CoreCLR.dll uses linker .def files to control the exported symbols.
+// Define DLLEXPORT macro as empty on Windows.
+#define DLLEXPORT
+#endif
+
+#endif // __CrtWrap_h__
#ifdef PAL_STDCPP_COMPAT
#include <type_traits>
+#else
+#include "clr_std/type_traits"
+#include "crosscomp.h"
#endif
//
// target pointer. For cross-plat, this may be different than SIZE_T
// which reflects the host pointer size.
typedef SIZE_T TSIZE_T;
+#define VPTR_CLASS_METHODS(name)
+// Used for base classes that can be instantiated directly.
+// The fake vfn is still used to force a vtable even when
+// all the normal vfns are ifdef'ed out.
+#define VPTR_BASE_CONCRETE_VTABLE_CLASS(name) \
+public: name(TADDR addr, TADDR vtAddr) {} \
+ VPTR_CLASS_METHODS(name)
//
// This version of the macros turns into normal pointers
#define S8PTRMAX(type, maxChars) type*
#define S16PTR(type) type*
#define S16PTRMAX(type, maxChars) type*
+#define _SPTR_DECL(acc_type, store_type, var) \
+ static store_type var
+#define _SPTR_IMPL(acc_type, store_type, cls, var) \
+ store_type cls::var
+
+#define GVAL_DECL(type, var) \
+ extern type var
+#define GVAL_IMPL(type, var) \
+ type var
+#define GVAL_IMPL_INIT(type, var, init) \
+ type var = init
//----------------------------------------------------------------------------
// dac_cast
// Perhaps we should more precisely restrict it's usage, but we get the precise
// restrictions in DAC builds, so it wouldn't buy us much.
return (Tgt)(src);
+#define SPTR_DECL(type, var) _SPTR_DECL(type*, PTR_##type, var)
+#define SPTR_IMPL(type, cls, var) _SPTR_IMPL(type*, PTR_##type, cls, var)
}
//----------------------------------------------------------------------------
typedef DPTR(IMAGE_NT_HEADERS32) PTR_IMAGE_NT_HEADERS32;
typedef DPTR(IMAGE_NT_HEADERS64) PTR_IMAGE_NT_HEADERS64;
typedef DPTR(IMAGE_SECTION_HEADER) PTR_IMAGE_SECTION_HEADER;
+typedef DPTR(IMAGE_EXPORT_DIRECTORY) PTR_IMAGE_EXPORT_DIRECTORY;
typedef DPTR(IMAGE_TLS_DIRECTORY) PTR_IMAGE_TLS_DIRECTORY;
//----------------------------------------------------------------------------
typedef DPTR(PCODE) PTR_PCODE;
typedef DPTR(PTR_PCODE) PTR_PTR_PCODE;
+// TARGET_CONSISTENCY_CHECK represents a condition that should not fail unless the DAC target is corrupt.
+// This is in contrast to ASSERTs in DAC infrastructure code which shouldn't fail regardless of the memory
+// read from the target. At the moment we treat these the same, but in the future we will want a mechanism
+// for disabling just the target consistency checks (eg. for tests that intentionally use corrupted targets).
+// @dbgtodo : Separating asserts and target consistency checks is tracked by DevDiv Bugs 31674
+#define TARGET_CONSISTENCY_CHECK(expr,msg) _ASSERTE_MSG(expr,msg)
+
+// For cross compilation, controlling type layout is important
+// We add a simple macro here which defines DAC_ALIGNAS to the C++11 alignas operator
+// This helps force the alignment of the next member
+// For most cross compilation cases the layout of types simply works
+// There are a few cases (where this macro is helpful) which are not consistent across platforms:
+// - Base class whose size is padded to its align size. On Linux the gcc/clang
+// layouts will reuse this padding in the derived class for the first member
+// - Class with an vtable pointer and an alignment greater than the pointer size.
+// The Windows compilers will align the first member to the alignment size of the
+// class. Linux will align the first member to its natural alignment
+#define DAC_ALIGNAS(a) alignas(a)
#endif // #ifndef __daccess_h__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+// DbgEngineMetrics.h
+//
+// This file contains the defintion of CLR_ENGINE_METRICS. This struct is used for Silverlight debugging.
+//
+// ======================================================================================
+
+
+
+#ifndef __DbgEngineMetrics_h__
+#define __DbgEngineMetrics_h__
+
+//---------------------------------------------------------------------------------------
+//
+// This struct contains information necessary for Silverlight debugging. coreclr.dll has a static struct
+// of this type. It is read by dbgshim.dll to help synchronize the debugger and coreclr.dll in launch
+// and early attach scenarios.
+//
+
+typedef struct tagCLR_ENGINE_METRICS
+{
+ DWORD cbSize; // the size of the struct; also identifies the format of the struct
+ DWORD dwDbiVersion; // the version of the debugging interface expected by this CoreCLR
+ LPVOID phContinueStartupEvent; // pointer to the continue startup event handle
+} CLR_ENGINE_METRICS;
+
+#endif // __DbgEngineMetrics_h__
#ifndef __DebugMacros_h__
#define __DebugMacros_h__
-#include "stacktrace.h"
#include "debugmacrosext.h"
#include "palclr.h"
extern VOID ANALYZER_NORETURN DbgAssertDialog(const char *szFile, int iLine, const char *szExpr);
-#define TRACE_BUFF_SIZE (cchMaxAssertStackLevelStringLen * cfrMaxAssertStackLevels + cchMaxAssertExprLen + 1)
-extern char g_szExprWithStack[TRACE_BUFF_SIZE];
-
extern int _DbgBreakCount;
#define PRE_ASSERTE /* if you need to change modes before doing asserts override */
#undef _ASSERT
#define _ASSERT _ASSERTE
-
-#if defined(_DEBUG) && defined(HOST_WINDOWS)
-
-// This function returns the EXE time stamp (effectively a random number)
-// Under retail it always returns 0. This is meant to be used in the
-// RandomOnExe macro
-unsigned DbgGetEXETimeStamp();
-
-// returns true 'fractionOn' amount of the time using the EXE timestamp
-// as the random number seed. For example DbgRandomOnExe(.1) returns true 1/10
-// of the time. We use the line number so that different uses of DbgRandomOnExe
-// will not be coorelated with each other (9973 is prime). Returns false on a retail build
-#define DbgRandomOnHashAndExe(hash, fractionOn) \
- (((DbgGetEXETimeStamp() * __LINE__ * ((hash) ? (hash) : 1)) % 9973) < \
- unsigned((fractionOn) * 9973))
-#define DbgRandomOnExe(fractionOn) DbgRandomOnHashAndExe(0, fractionOn)
-#define DbgRandomOnStringAndExe(string, fractionOn) DbgRandomOnHashAndExe(HashStringA(string), fractionOn)
-
-#else
-
-#define DbgGetEXETimeStamp() 0
-#define DbgRandomOnHashAndExe(hash, fractionOn) 0
-#define DbgRandomOnExe(fractionOn) 0
-#define DbgRandomOnStringAndExe(fractionOn) 0
-
-#endif // _DEBUG && !FEATUREPAL
-
#endif
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#ifndef _DEBUGRETURN_H_
+#define _DEBUGRETURN_H_
+
+// Note that with OACR Prefast is run over checked (_DEBUG is defined) sources
+// so we have to first check the _PREFAST_ define followed by the _DEBUG define
+//
+#ifdef _PREFAST_
+
+// Use prefast to detect gotos out of no-return blocks. The gotos out of no-return blocks
+// should be reported as memory leaks by prefast. The (nothrow) is because PREfix sees the
+// throw from the new statement, and doesn't like these macros used in a destructor (and
+// the NULL returned by failure works just fine in delete[])
+
+#define DEBUG_ASSURE_NO_RETURN_BEGIN(arg) { char* __noReturnInThisBlock_##arg = ::new (nothrow) char[1];
+#define DEBUG_ASSURE_NO_RETURN_END(arg) ::delete[] __noReturnInThisBlock_##arg; }
+
+#define DEBUG_OK_TO_RETURN_BEGIN(arg) { ::delete[] __noReturnInThisBlock_##arg;
+#define DEBUG_OK_TO_RETURN_END(arg) __noReturnInThisBlock_##arg = ::new (nothrow) char[1]; }
+
+#define DEBUG_ASSURE_SAFE_TO_RETURN TRUE
+#define return return
+
+#else // !_PREFAST_
+
+// This is disabled in build 190024315 (a pre-release build after VS 2015 Update 3) and
+// earlier because those builds only support C++11 constexpr, which doesn't allow the
+// use of 'if' statements within the body of a constexpr function. Later builds support
+// C++14 constexpr.
+#if defined(_DEBUG) && !defined(JIT_BUILD) && (!defined(_MSC_FULL_VER) || _MSC_FULL_VER > 190024315)
+
+// Code to generate a compile-time error if return statements appear where they
+// shouldn't.
+//
+// Here's the way it works...
+//
+// We create two classes with a safe_to_return() method. The method is static,
+// returns void, and does nothing. One class has the method as public, the other
+// as private. We introduce a global scope typedef for __ReturnOK that refers to
+// the class with the public method. So, by default, the expression
+//
+// __ReturnOK::safe_to_return()
+//
+// quietly compiles and does nothing. When we enter a block in which we want to
+// inhibit returns, we introduce a new typedef that defines __ReturnOK as the
+// class with the private method. Inside this scope,
+//
+// __ReturnOK::safe_to_return()
+//
+// generates a compile-time error.
+//
+// To cause the method to be called, we have to #define the return keyword.
+// The simplest working version would be
+//
+// #define return if (0) __ReturnOK::safe_to_return(); else return
+//
+// but we've used
+//
+// #define return for (;1;__ReturnOK::safe_to_return()) return
+//
+// because it happens to generate somewhat faster code in a checked build. (They
+// both introduce no overhead in a fastchecked build.)
+//
+class __SafeToReturn {
+public:
+ static int safe_to_return() {return 0;};
+ static int used() {return 0;};
+};
+
+class __YouCannotUseAReturnStatementHere {
+private:
+ // If you got here, and you're wondering what you did wrong -- you're using
+ // a return statement where it's not allowed. Likely, it's inside one of:
+ // GCPROTECT_BEGIN ... GCPROTECT_END
+ // HELPER_METHOD_FRAME_BEGIN ... HELPER_METHOD_FRAME_END
+ //
+ static int safe_to_return() {return 0;};
+public:
+ // Some compilers warn if all member functions in a class are private
+ // or if a typedef is unused. Rather than disable the warning, we'll work
+ // around it here.
+ static int used() {return 0;};
+};
+
+typedef __SafeToReturn __ReturnOK;
+
+// Use this to ensure that it is safe to return from a given scope
+#define DEBUG_ASSURE_SAFE_TO_RETURN __ReturnOK::safe_to_return()
+
+// Unfortunately, the only way to make this work is to #define all return statements --
+// even the ones at global scope. This actually generates better code that appears.
+// The call is dead, and does not appear in the generated code, even in a checked
+// build. (And, in fastchecked, there is no penalty at all.)
+//
+#ifdef _MSC_VER
+#define return if (0 && __ReturnOK::safe_to_return()) { } else return
+#else // _MSC_VER
+#define return for (;1;__ReturnOK::safe_to_return()) return
+#endif // _MSC_VER
+
+#define DEBUG_ASSURE_NO_RETURN_BEGIN(arg) { typedef __YouCannotUseAReturnStatementHere __ReturnOK; if (0 && __ReturnOK::used()) { } else {
+#define DEBUG_ASSURE_NO_RETURN_END(arg) } }
+
+#define DEBUG_OK_TO_RETURN_BEGIN(arg) { typedef __SafeToReturn __ReturnOK; if (0 && __ReturnOK::used()) { } else {
+#define DEBUG_OK_TO_RETURN_END(arg) } }
+
+#else // defined(_DEBUG) && !defined(JIT_BUILD) && (!defined(_MSC_FULL_VER) || _MSC_FULL_VER > 190024315)
+
+#define DEBUG_ASSURE_SAFE_TO_RETURN TRUE
+
+#define DEBUG_ASSURE_NO_RETURN_BEGIN(arg) {
+#define DEBUG_ASSURE_NO_RETURN_END(arg) }
+
+#define DEBUG_OK_TO_RETURN_BEGIN(arg) {
+#define DEBUG_OK_TO_RETURN_END(arg) }
+
+#endif // defined(_DEBUG) && !defined(JIT_BUILD) && (!defined(_MSC_FULL_VER) || _MSC_FULL_VER > 190024315)
+
+#endif // !_PREFAST_
+
+#endif // _DEBUGRETURN_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//-----------------------------------------------------------------------------
+// Entrypoint markers
+// Used to identify all external entrypoints into the CLR (via COM, exports, etc)
+// and perform various tasks on all of them
+//-----------------------------------------------------------------------------
+
+
+#ifndef __ENTRYPOINTS_h__
+#define __ENTRYPOINTS_h__
+
+#define BEGIN_ENTRYPOINT_THROWS
+#define END_ENTRYPOINT_THROWS
+#define BEGIN_ENTRYPOINT_THROWS_WITH_THREAD(____thread)
+#define END_ENTRYPOINT_THROWS_WITH_THREAD
+#define BEGIN_ENTRYPOINT_NOTHROW_WITH_THREAD(___thread)
+#define END_ENTRYPOINT_NOTHROW_WITH_THREAD
+#define BEGIN_ENTRYPOINT_NOTHROW
+#define END_ENTRYPOINT_NOTHROW
+#define BEGIN_ENTRYPOINT_VOIDRET
+#define END_ENTRYPOINT_VOIDRET
+#define BEGIN_CLEANUP_ENTRYPOINT
+#define END_CLEANUP_ENTRYPOINT
+
+#endif // __ENTRYPOINTS_h__
+
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#if !defined(_EX_H_)
+#define _EX_H_
+
+#define EX_TRY_HOLDER
+
+#include "sstring.h"
+#include "crtwrap.h"
+#include "winwrap.h"
+#include "corerror.h"
+#include "stresslog.h"
+#include "staticcontract.h"
+#include "entrypoints.h"
+
+#if !defined(_DEBUG_IMPL) && defined(_DEBUG) && !defined(DACCESS_COMPILE)
+#define _DEBUG_IMPL 1
+#endif
+
+
+//===========================================================================================
+// 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);
+
+
+// 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.
+
+#define INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE 5
+DWORD MarkAsThrownByUs(/*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE]);
+DWORD MarkAsThrownByUs(/*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE], ULONG_PTR arg0);
+// (the existing system can support more overloads up to 4 fixed arguments but we don't need them at this time.)
+
+
+// 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);
+
+
+//-----------------------------------------------------------------------------------
+// The following group wraps the basic abstracts specifically for EXCEPTION_COMPLUS.
+//-----------------------------------------------------------------------------------
+BOOL IsComPlusException(const EXCEPTION_RECORD *pcER);
+
+
+//===========================================================================================
+//===========================================================================================
+
+
+//-------------------------------------------------------------------------------------------
+// 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 hresult, SString &result, BOOL bNoGeekStuff = FALSE);
+
+
+//-------------------------------------------------------------------------------------------
+// Similar to GetHRMsg but phrased for top-level exception message.
+//-------------------------------------------------------------------------------------------
+void GenerateTopLevelHRExceptionMessage(HRESULT hresult, SString &result);
+
+
+// ---------------------------------------------------------------------------
+// We save current ExceptionPointers using VectoredExceptionHandler. The save data is only valid
+// duing exception handling. GetCurrentExceptionPointers returns the saved data.
+// ---------------------------------------------------------------------------
+void GetCurrentExceptionPointers(PEXCEPTION_POINTERS pExceptionInfo DEBUG_ARG(bool checkExceptionRecordLocation));
+
+// ---------------------------------------------------------------------------
+// We save current ExceptionPointers using VectoredExceptionHandler. The save data is only valid
+// duing exception handling. GetCurrentExceptionCode returns the current exception code.
+// ---------------------------------------------------------------------------
+DWORD GetCurrentExceptionCode();
+
+// ---------------------------------------------------------------------------
+// Standard exception hierarchy & infrastructure for library code & EE
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// Exception class. Abstract root exception of our hierarchy.
+// ---------------------------------------------------------------------------
+
+class Exception;
+class SEHException;
+
+
+// Exception hierarchy:
+/* GetInstanceType
+Exception
+ |
+ |-> HRException Y
+ | |
+ | |-> HRMsgException
+ | |-> COMException
+ |
+ |-> SEHException Y
+ |
+ |-> DelegatingException Y
+ |
+ |-> OutOfMemoryException Y
+ |
+ |-> CLRException Y
+ |
+ |-> EEException Y
+ | |
+ | |-> EEMessageException
+ | |
+ | |-> EEResourceException
+ | |
+ | |-> EECOMException
+ | |
+ | |-> EEFieldException
+ | |
+ | |-> EEMethodException
+ | |
+ | |-> EEArgumentException
+ | |
+ | |-> EETypeLoadException
+ | |
+ | |-> EEFileLoadException
+ |
+ |-> ObjrefException Y
+ |
+ |-> CLRLastThrownObjectException Y
+*/
+
+class Exception
+{
+ friend bool DebugIsEECxxExceptionPointer(void* pv);
+
+ private:
+ static const int c_type = 0x524f4f54; // 'ROOT'
+ static Exception * g_OOMException;
+ static Exception * g_SOException;
+
+ protected:
+ Exception *m_innerException;
+
+ public:
+ Exception() {LIMITED_METHOD_DAC_CONTRACT; m_innerException = NULL;}
+ virtual ~Exception() {LIMITED_METHOD_DAC_CONTRACT; if (m_innerException != NULL) Exception::Delete(m_innerException); }
+ virtual BOOL IsDomainBound() {return m_innerException!=NULL && m_innerException->IsDomainBound();} ;
+ virtual HRESULT GetHR() = 0;
+ virtual void GetMessage(SString &s);
+ virtual IErrorInfo *GetErrorInfo() { LIMITED_METHOD_CONTRACT; return NULL; }
+ virtual HRESULT SetErrorInfo() { LIMITED_METHOD_CONTRACT; return S_OK; }
+ void SetInnerException(Exception * pInnerException) { LIMITED_METHOD_CONTRACT; m_innerException = pInnerException; }
+
+ // Dynamic type query for catchers
+ static int GetType() { LIMITED_METHOD_CONTRACT; return c_type; }
+ // !!! If GetInstanceType is implemented, IsSameInstanceType should be implemented
+ virtual int GetInstanceType() = 0;
+ virtual BOOL IsType(int type) {LIMITED_METHOD_CONTRACT; return type == c_type; }
+
+ // This is used in CLRException::GetThrowable to detect if we are in a recursive situation.
+ virtual BOOL IsSameInstanceType(Exception *pException) = 0;
+
+ // Will create a new instance of the Exception. Note that this will
+ // be free of app domain or thread affinity. Not every type of exception
+ // can be cloned with full fidelity.
+ virtual Exception *Clone();
+
+ // DomainBoundClone is a specialized form of cloning which is guaranteed
+ // to provide full fidelity. However, the result is bound to the current
+ // app domain and should not be leaked.
+ Exception *DomainBoundClone();
+
+ class HandlerState
+ {
+ enum CaughtFlags
+ {
+ Caught = 1,
+ CaughtSO = 2,
+ CaughtCxx = 4,
+ };
+
+ DWORD m_dwFlags;
+ public:
+ Exception* m_pExceptionPtr;
+
+ HandlerState();
+
+ void CleanupTry();
+ void SetupCatch(INDEBUG_COMMA(_In_z_ const char * szFile) int lineNum);
+ void SucceedCatch();
+
+ BOOL DidCatch() { return (m_dwFlags & Caught); }
+ void SetCaught() { m_dwFlags |= Caught; }
+
+ BOOL DidCatchCxx() { return (m_dwFlags & CaughtCxx); }
+ void SetCaughtCxx() { m_dwFlags |= CaughtCxx; }
+ };
+
+ // Is this exception type considered "uncatchable"?
+ BOOL IsTerminal();
+
+ // Is this exception type considered "transient" (would a retry possibly succeed)?
+ BOOL IsTransient();
+ static BOOL IsTransient(HRESULT hr);
+
+ // Get an HRESULT's source representation, if known
+ static LPCSTR GetHRSymbolicName(HRESULT hr);
+
+ static Exception* GetOOMException();
+
+ // Preallocated exceptions: If there is a preallocated instance of some
+ // subclass of Exception, override this function and return a correct
+ // value. The default implementation returns constant FALSE
+ virtual BOOL IsPreallocatedException();
+ BOOL IsPreallocatedOOMException();
+
+ static void Delete(Exception* pvMemory);
+
+protected:
+
+ // This virtual method must be implemented by any non abstract Exception
+ // derived class. It must allocate a NEW exception of the identical type and
+ // copy all the relevant fields from the current exception to the new one.
+ // It is NOT responsible however for copying the inner exception. This
+ // will be handled by the base Exception class.
+ virtual Exception *CloneHelper();
+
+ // This virtual method must be implemented by Exception subclasses whose
+ // DomainBoundClone behavior is different than their normal clone behavior.
+ // It must allocate a NEW exception of the identical type and
+ // copy all the relevant fields from the current exception to the new one.
+ // It is NOT responsible however for copying the inner exception. This
+ // will be handled by the base Exception class.
+ virtual Exception *DomainBoundCloneHelper() { return CloneHelper(); }
+};
+
+#if 1
+
+inline void Exception__Delete(Exception* pvMemory)
+{
+ Exception::Delete(pvMemory);
+}
+
+using ExceptionHolder = SpecializedWrapper<Exception, Exception__Delete>;
+#else
+
+//------------------------------------------------------------------------------
+// class ExceptionHolder
+//
+// This is a very lightweight holder class for use inside the EX_TRY family
+// of macros. It is based on the standard Holder classes, but has been
+// highly specialized for this one function, so that extra code can be
+// removed, and the resulting code can be simple enough for all of the
+// non-exceptional-case code to be inlined.
+class ExceptionHolder
+{
+private:
+ Exception *m_value;
+ BOOL m_acquired;
+
+public:
+ FORCEINLINE ExceptionHolder(Exception *pException = NULL, BOOL take = TRUE)
+ : m_value(pException)
+ {
+ m_acquired = pException && take;
+ }
+
+ FORCEINLINE ~ExceptionHolder()
+ {
+ if (m_acquired)
+ {
+ Exception::Delete(m_value);
+ }
+ }
+
+ Exception* operator->() { return m_value; }
+
+ void operator=(Exception *p)
+ {
+ Release();
+ m_value = p;
+ Acquire();
+ }
+
+ BOOL IsNull() { return m_value == NULL; }
+
+ operator Exception*() { return m_value; }
+
+ Exception* GetValue() { return m_value; }
+
+ void SuppressRelease() { m_acquired = FALSE; }
+
+private:
+ void Acquire()
+ {
+ _ASSERTE(!m_acquired);
+
+ if (!IsNull())
+ {
+ m_acquired = TRUE;
+ }
+ }
+ void Release()
+ {
+ if (m_acquired)
+ {
+ _ASSERTE(!IsNull());
+ Exception::Delete(m_value);
+ m_acquired = FALSE;
+ }
+ }
+
+};
+
+#endif
+
+// ---------------------------------------------------------------------------
+// HRException class. Implements exception API for exceptions generated from HRESULTs
+// ---------------------------------------------------------------------------
+
+class HRException : public Exception
+{
+ friend bool DebugIsEECxxExceptionPointer(void* pv);
+
+ protected:
+ HRESULT m_hr;
+
+ public:
+ HRException();
+ HRException(HRESULT hr);
+
+ static const int c_type = 0x48522020; // 'HR '
+
+ // Dynamic type query for catchers
+ static int GetType() {LIMITED_METHOD_DAC_CONTRACT; return c_type; }
+ virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; }
+ virtual BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || Exception::IsType(type); }
+ // Virtual overrides
+ HRESULT GetHR();
+
+ BOOL IsSameInstanceType(Exception *pException)
+ {
+ WRAPPER_NO_CONTRACT;
+ return pException->GetInstanceType() == GetType() && pException->GetHR() == m_hr;
+ }
+
+ protected:
+ virtual Exception *CloneHelper()
+ {
+ WRAPPER_NO_CONTRACT;
+ return new HRException(m_hr);
+ }
+};
+
+// ---------------------------------------------------------------------------
+// HRMessageException class. Implements exception API for exceptions
+// generated from HRESULTs, and includes in info message.
+// ---------------------------------------------------------------------------
+
+class HRMsgException : public HRException
+{
+ friend bool DebugIsEECxxExceptionPointer(void* pv);
+
+ protected:
+ SString m_msg;
+
+ public:
+ HRMsgException();
+ HRMsgException(HRESULT hr, SString const &msg);
+
+ // Virtual overrides
+ void GetMessage(SString &s);
+
+ protected:
+ virtual Exception *CloneHelper()
+ {
+ WRAPPER_NO_CONTRACT;
+ return new HRMsgException(m_hr, m_msg);
+ }
+};
+
+// ---------------------------------------------------------------------------
+// COMException class. Implements exception API for standard COM-based error info
+// ---------------------------------------------------------------------------
+
+class COMException : public HRException
+{
+ friend bool DebugIsEECxxExceptionPointer(void* pv);
+
+ private:
+ IErrorInfo *m_pErrorInfo;
+
+ public:
+ COMException();
+ COMException(HRESULT hr) ;
+ COMException(HRESULT hr, IErrorInfo *pErrorInfo);
+ ~COMException();
+
+ // Virtual overrides
+ IErrorInfo *GetErrorInfo();
+ void GetMessage(SString &result);
+
+ protected:
+ virtual Exception *CloneHelper()
+ {
+ WRAPPER_NO_CONTRACT;
+ return new COMException(m_hr, m_pErrorInfo);
+ }
+};
+
+// ---------------------------------------------------------------------------
+// SEHException class. Implements exception API for SEH exception info
+// ---------------------------------------------------------------------------
+
+class SEHException : public Exception
+{
+ friend bool DebugIsEECxxExceptionPointer(void* pv);
+
+ public:
+ EXCEPTION_RECORD m_exception;
+
+ SEHException();
+ SEHException(EXCEPTION_RECORD *pRecord, T_CONTEXT *pContext = NULL);
+
+ static const int c_type = 0x53454820; // 'SEH '
+
+ // Dynamic type query for catchers
+ static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; }
+ virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; }
+ virtual BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || Exception::IsType(type); }
+
+ BOOL IsSameInstanceType(Exception *pException)
+ {
+ WRAPPER_NO_CONTRACT;
+ return pException->GetInstanceType() == GetType() && pException->GetHR() == GetHR();
+ }
+
+ // Virtual overrides
+ HRESULT GetHR();
+ IErrorInfo *GetErrorInfo();
+ void GetMessage(SString &result);
+
+ protected:
+ virtual Exception *CloneHelper()
+ {
+ WRAPPER_NO_CONTRACT;
+ return new SEHException(&m_exception);
+ }
+};
+
+// ---------------------------------------------------------------------------
+// DelegatingException class. Implements exception API for "foreign" exceptions.
+// ---------------------------------------------------------------------------
+
+class DelegatingException : public Exception
+{
+ Exception *m_delegatedException;
+ Exception* GetDelegate();
+
+ enum {DELEGATE_NOT_YET_SET = -1};
+ bool IsDelegateSet() {LIMITED_METHOD_DAC_CONTRACT; return m_delegatedException != (Exception*)DELEGATE_NOT_YET_SET; }
+ bool IsDelegateValid() {LIMITED_METHOD_DAC_CONTRACT; return IsDelegateSet() && m_delegatedException != NULL; }
+
+ public:
+
+ DelegatingException();
+ ~DelegatingException();
+
+ static const int c_type = 0x44454C20; // 'DEL '
+
+ // Dynamic type query for catchers
+ static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; }
+ virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; }
+ virtual BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || Exception::IsType(type); }
+
+ BOOL IsSameInstanceType(Exception *pException)
+ {
+ WRAPPER_NO_CONTRACT;
+ return pException->GetInstanceType() == GetType() && pException->GetHR() == GetHR();
+ }
+
+ // Virtual overrides
+ virtual BOOL IsDomainBound() {return Exception::IsDomainBound() ||(m_delegatedException!=NULL && m_delegatedException->IsDomainBound());} ;
+ HRESULT GetHR();
+ IErrorInfo *GetErrorInfo();
+ void GetMessage(SString &result);
+ virtual Exception *Clone();
+
+ protected:
+ virtual Exception *CloneHelper()
+ {
+ WRAPPER_NO_CONTRACT;
+ return new DelegatingException();
+ }
+};
+
+//------------------------------------------------------------------------------
+// class OutOfMemoryException
+//
+// While there could be any number of instances of this class, there is one
+// special instance, the pre-allocated OOM exception. Storage for that
+// instance is allocated in the image, so we can always obtain it, even
+// in low memory situations.
+// Note that, in fact, there is only one instance.
+//------------------------------------------------------------------------------
+class OutOfMemoryException : public Exception
+{
+ private:
+ static const int c_type = 0x4F4F4D20; // 'OOM '
+ BOOL bIsPreallocated;
+
+ public:
+ OutOfMemoryException() : bIsPreallocated(FALSE) {}
+ OutOfMemoryException(BOOL b) : bIsPreallocated(b) {}
+
+ // Dynamic type query for catchers
+ static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; }
+ virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; }
+ BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || Exception::IsType(type); }
+
+ BOOL IsSameInstanceType(Exception *pException)
+ {
+ WRAPPER_NO_CONTRACT;
+ return pException->GetInstanceType() == GetType();
+ }
+
+ HRESULT GetHR() {LIMITED_METHOD_DAC_CONTRACT; return E_OUTOFMEMORY; }
+ void GetMessage(SString &result) { WRAPPER_NO_CONTRACT; result.SetASCII("Out Of Memory"); }
+
+ virtual Exception *Clone();
+
+ virtual BOOL IsPreallocatedException() { return bIsPreallocated; }
+};
+
+template <typename STATETYPE>
+class CAutoTryCleanup
+{
+public:
+ DEBUG_NOINLINE CAutoTryCleanup(STATETYPE& refState) :
+ m_refState(refState)
+ {
+ SCAN_SCOPE_BEGIN;
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+#ifdef ENABLE_CONTRACTS_IMPL
+ // This is similar to ClrTryMarkerHolder. We're marking that its okay to throw on this thread now because
+ // we're within a try block. We fold this into here strictly for performance reasons... we have one
+ // stack-allocated object do the work.
+ m_pClrDebugState = GetClrDebugState();
+ m_oldOkayToThrowValue = m_pClrDebugState->IsOkToThrow();
+ m_pClrDebugState->SetOkToThrow();
+#endif
+ }
+
+ DEBUG_NOINLINE ~CAutoTryCleanup()
+ {
+ SCAN_SCOPE_END;
+ WRAPPER_NO_CONTRACT;
+
+ m_refState.CleanupTry();
+
+#ifdef ENABLE_CONTRACTS_IMPL
+ // Restore the original OkayToThrow value since we're leaving the try block.
+
+ m_pClrDebugState->SetOkToThrow( m_oldOkayToThrowValue );
+#endif // ENABLE_CONTRACTS_IMPL
+ }
+
+protected:
+ STATETYPE& m_refState;
+
+#ifdef ENABLE_CONTRACTS_DATA
+private:
+ BOOL m_oldOkayToThrowValue;
+ ClrDebugState *m_pClrDebugState;
+#endif
+};
+
+// ---------------------------------------------------------------------------
+// Throw/Catch macros
+//
+// Usage:
+//
+// EX_TRY
+// {
+// EX_THROW(HRException, (E_FAIL));
+// }
+// EX_CATCH
+// {
+// Exception *e = GET_EXCEPTION();
+// EX_RETHROW;
+// }
+// EX_END_CATCH(RethrowTerminalExceptions, RethrowTransientExceptions or SwallowAllExceptions)
+//
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// #NO_HOST_CPP_EH_ONLY
+//
+// The EX_CATCH* macros defined below can work one of two ways:
+// 1. They catch all exceptions, both C++ and SEH exceptions.
+// 2. They catch only C++ exceptions.
+//
+// Which way they are defined depends on what sort of handling of SEH
+// exceptions, like AV's, you wish to have in your DLL. In general we
+// do not typically want to catch and swallow AV's.
+//
+// By default, the macros catch all exceptions. This is how they work when
+// compiled into the primary runtime DLL (clr.dll). This is reasonable for
+// the CLR becuase it needs to also catch managed exceptions, which are SEH
+// exceptions, and because that DLL also includes a vectored exception
+// handler that will take down the process on any AV within clr.dll.
+//
+// But for uses of these macros outside of the CLR DLL there are other
+// possibilities. If a DLL only uses facilities in Utilcode that throw the
+// C++ exceptions defined above, and never needs to catch a managed exception,
+// then that DLL should setup the macros to only catch C++ exceptions. That
+// way, AV's are not accidentally swallowed and hidden.
+//
+// On the other hand, if a DLL needs to catch managed exceptions, then it has
+// no choice but to also catch all SEH exceptions, including AV's. In that case
+// the DLL should also include a vectored handler, like CLR.dll, to take the
+// process down on an AV.
+//
+// The behavior difference is controled by NO_HOST_CPP_EH_ONLY. When defined,
+// the EX_CATCH* macros only catch C++ exceptions. When not defined, they catch
+// C++ and SEH exceptions.
+//
+// Note: use of NO_HOST_CPP_EH_ONLY is only valid outside the primary CLR DLLs.
+// Thus it is an error to attempt to define it without also defining SELF_NO_HOST.
+// ---------------------------------------------------------------------------
+
+#if defined(NO_HOST_CPP_EH_ONLY) && !defined(SELF_NO_HOST)
+#error It is incorrect to attempt to have C++-only EH macros when hosted. This is only valid for components outside the runtime DLLs.
+#endif
+
+//-----------------------------------------------------------------------
+// EX_END_CATCH has a mandatory argument which is one of "RethrowTerminalExceptions",
+// "RethrowTransientExceptions", or "SwallowAllExceptions".
+//
+// If an exception is considered "terminal" (e->IsTerminal()), it should normally
+// be allowed to proceed. Hence, most of the time, you should use RethrowTerminalExceptions.
+//
+// In some cases you will want transient exceptions (terminal plus things like
+// resource exhaustion) to proceed as well. Use RethrowTransientExceptions for this cas.
+//
+// If you have a good reason to use SwallowAllExceptions, (e.g. a hard COM interop boundary)
+// use one of the higher level macros for this if available, or consider developing one.
+// Otherwise, clearly document why you're swallowing terminal exceptions. Raw uses of
+// SwallowAllExceptions will cause the cleanup police to come knocking on your door
+// at some point.
+//
+// A lot of existing TRY's swallow terminals right now simply because there is
+// backout code following the END_CATCH that has to be executed. The solution is
+// to replace that backout code with holder objects.
+
+//-----------------------------------------------------------------------
+
+#define RethrowTransientExceptions \
+ if (GET_EXCEPTION()->IsTransient()) \
+ { \
+ EX_RETHROW; \
+ } \
+
+#define SwallowAllExceptions ;
+
+// When applied to EX_END_CATCH, this policy will always rethrow Terminal exceptions if they are
+// encountered.
+#define RethrowTerminalExceptions \
+ if (GET_EXCEPTION()->IsTerminal()) \
+ { \
+ STATIC_CONTRACT_THROWS_TERMINAL; \
+ EX_RETHROW; \
+ } \
+
+// Special define to be used in EEStartup that will also check for VM initialization before
+// commencing on a path that may use the managed thread object.
+#define RethrowTerminalExceptionsWithInitCheck \
+ if ((g_fEEStarted == TRUE) && (GetThreadNULLOk() != NULL)) \
+ { \
+ RethrowTerminalExceptions \
+ }
+
+#ifdef _DEBUG
+
+void ExThrowTrap(const char *fcn, const char *file, int line, const char *szType, HRESULT hr, const char *args);
+
+#define EX_THROW_DEBUG_TRAP(fcn, file, line, szType, hr, args) ExThrowTrap(fcn, file, line, szType, hr, args)
+
+#else
+
+#define EX_THROW_DEBUG_TRAP(fcn, file, line, szType, hr, args)
+
+#endif
+
+#define EX_THROW(_type, _args) \
+ { \
+ FAULT_NOT_FATAL(); \
+ \
+ _type * ___pExForExThrow = new _type _args ; \
+ /* don't embed file names in retail to save space and avoid IP */ \
+ /* a findstr /n will allow you to locate it in a pinch */ \
+ STRESS_LOG3(LF_EH, LL_INFO100, "EX_THROW Type = 0x%x HR = 0x%x, " \
+ INDEBUG(__FILE__) " line %d\n", _type::GetType(), \
+ ___pExForExThrow->GetHR(), __LINE__); \
+ EX_THROW_DEBUG_TRAP(__FUNCTION__, __FILE__, __LINE__, #_type, ___pExForExThrow->GetHR(), #_args); \
+ PAL_CPP_THROW(_type *, ___pExForExThrow); \
+ }
+
+//--------------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------------
+Exception *ExThrowWithInnerHelper(Exception *inner);
+
+// This macro will set the m_innerException into the newly created exception
+// The passed in _type has to be derived from CLRException. You cannot put OOM
+// as the inner exception. If we are throwing in OOM case, allocate more memory (this macro will clone)
+// does not make any sense.
+//
+#define EX_THROW_WITH_INNER(_type, _args, _inner) \
+ { \
+ FAULT_NOT_FATAL(); \
+ \
+ Exception *_inner2 = ExThrowWithInnerHelper(_inner); \
+ _type *___pExForExThrow = new _type _args ; \
+ ___pExForExThrow->SetInnerException(_inner2); \
+ STRESS_LOG3(LF_EH, LL_INFO100, "EX_THROW_WITH_INNER Type = 0x%x HR = 0x%x, " \
+ INDEBUG(__FILE__) " line %d\n", _type::GetType(), \
+ ___pExForExThrow->GetHR(), __LINE__); \
+ EX_THROW_DEBUG_TRAP(__FUNCTION__, __FILE__, __LINE__, #_type, ___pExForExThrow->GetHR(), #_args); \
+ PAL_CPP_THROW(_type *, ___pExForExThrow); \
+ }
+
+//#define IsCLRException(ex) ((ex !=NULL) && ex->IsType(CLRException::GetType())
+
+#define EX_TRY_IMPL EX_TRY_CUSTOM(Exception::HandlerState, , DelegatingException /* was SEHException*/)
+
+#define EX_TRY_CPP_ONLY EX_TRY_CUSTOM_CPP_ONLY(Exception::HandlerState, , DelegatingException /* was SEHException*/)
+
+#ifndef INCONTRACT
+#ifdef ENABLE_CONTRACTS
+#define INCONTRACT(x) x
+#else
+#define INCONTRACT(x)
+#endif
+#endif
+
+#define EX_TRY_CUSTOM(STATETYPE, STATEARG, DEFAULT_EXCEPTION_TYPE) \
+ { \
+ STATETYPE __state STATEARG; \
+ typedef DEFAULT_EXCEPTION_TYPE __defaultException_t; \
+ SCAN_EHMARKER(); \
+ PAL_CPP_TRY \
+ { \
+ SCAN_EHMARKER_TRY(); \
+ SCAN_EHMARKER(); \
+ PAL_CPP_TRY \
+ { \
+ SCAN_EHMARKER_TRY(); \
+ CAutoTryCleanup<STATETYPE> __autoCleanupTry(__state); \
+ /* prevent annotations from being dropped by optimizations in debug */ \
+ INDEBUG(static bool __alwayszero;) \
+ INDEBUG(VolatileLoad(&__alwayszero);) \
+ { \
+ /* Disallow returns to make exception handling work. */ \
+ /* Some work is done after the catch, see EX_ENDTRY. */ \
+ DEBUG_ASSURE_NO_RETURN_BEGIN(EX_TRY) \
+ EX_TRY_HOLDER \
+
+
+#define EX_CATCH_IMPL_EX(DerivedExceptionClass) \
+ DEBUG_ASSURE_NO_RETURN_END(EX_TRY) \
+ } \
+ SCAN_EHMARKER_END_TRY(); \
+ } \
+ PAL_CPP_CATCH_DERIVED (DerivedExceptionClass, __pExceptionRaw) \
+ { \
+ SCAN_EHMARKER_CATCH(); \
+ __state.SetCaughtCxx(); \
+ __state.m_pExceptionPtr = __pExceptionRaw; \
+ SCAN_EHMARKER_END_CATCH(); \
+ SCAN_IGNORE_THROW_MARKER; \
+ PAL_CPP_RETHROW; \
+ } \
+ PAL_CPP_ENDTRY \
+ SCAN_EHMARKER_END_TRY(); \
+ } \
+ PAL_CPP_CATCH_ALL \
+ { \
+ SCAN_EHMARKER_CATCH(); \
+ __defaultException_t __defaultException; \
+ CHECK::ResetAssert(); \
+ ExceptionHolder __pException(__state.m_pExceptionPtr); \
+ /* work around unreachable code warning */ \
+ if (true) { \
+ DEBUG_ASSURE_NO_RETURN_BEGIN(EX_CATCH) \
+ /* don't embed file names in retail to save space and avoid IP */ \
+ /* a findstr /n will allow you to locate it in a pinch */ \
+ __state.SetupCatch(INDEBUG_COMMA(__FILE__) __LINE__); \
+
+#define EX_CATCH_IMPL EX_CATCH_IMPL_EX(Exception)
+
+#define EX_TRY_CUSTOM_CPP_ONLY(STATETYPE, STATEARG, DEFAULT_EXCEPTION_TYPE) \
+ { \
+ STATETYPE __state STATEARG; \
+ typedef DEFAULT_EXCEPTION_TYPE __defaultException_t; \
+ SCAN_EHMARKER(); \
+ PAL_CPP_TRY \
+ { \
+ SCAN_EHMARKER_TRY(); \
+ CAutoTryCleanup<STATETYPE> __autoCleanupTry(__state); \
+ /* prevent annotations from being dropped by optimizations in debug */ \
+ INDEBUG(static bool __alwayszero;) \
+ INDEBUG(VolatileLoad(&__alwayszero);) \
+ { \
+ /* Disallow returns to make exception handling work. */ \
+ /* Some work is done after the catch, see EX_ENDTRY. */ \
+ DEBUG_ASSURE_NO_RETURN_BEGIN(EX_TRY) \
+
+#define EX_CATCH_IMPL_CPP_ONLY \
+ DEBUG_ASSURE_NO_RETURN_END(EX_TRY) \
+ } \
+ SCAN_EHMARKER_END_TRY(); \
+ } \
+ PAL_CPP_CATCH_DERIVED (Exception, __pExceptionRaw) \
+ { \
+ SCAN_EHMARKER_CATCH(); \
+ __state.SetCaughtCxx(); \
+ __state.m_pExceptionPtr = __pExceptionRaw; \
+ SCAN_EHMARKER_END_CATCH(); \
+ SCAN_IGNORE_THROW_MARKER; \
+ __defaultException_t __defaultException; \
+ CHECK::ResetAssert(); \
+ ExceptionHolder __pException(__state.m_pExceptionPtr); \
+ /* work around unreachable code warning */ \
+ if (true) { \
+ DEBUG_ASSURE_NO_RETURN_BEGIN(EX_CATCH) \
+ /* don't embed file names in retail to save space and avoid IP */ \
+ /* a findstr /n will allow you to locate it in a pinch */ \
+ __state.SetupCatch(INDEBUG_COMMA(__FILE__) __LINE__); \
+
+
+// Here we finally define the EX_CATCH* macros that will be used throughout the system.
+// These can catch C++ and SEH exceptions, or just C++ exceptions.
+// See code:NO_HOST_CPP_EH_ONLY for more details.
+//
+// Note: we make it illegal to use forms that are redundant with the basic EX_CATCH
+// version. I.e., in the C++ & SEH version, EX_CATCH_CPP_AND_SEH is the same as EX_CATCH.
+// Likewise, in the C++ only version, EX_CATCH_CPP_ONLY is redundant with EX_CATCH.
+
+#ifndef NO_HOST_CPP_EH_ONLY
+#define EX_TRY EX_TRY_IMPL
+#define EX_CATCH EX_CATCH_IMPL
+#define EX_CATCH_EX EX_CATCH_IMPL_EX
+#define EX_CATCH_CPP_ONLY EX_CATCH_IMPL_CPP_ONLY
+#define EX_CATCH_CPP_AND_SEH Dont_Use_EX_CATCH_CPP_AND_SEH
+#else
+#define EX_TRY EX_TRY_CPP_ONLY
+#define EX_CATCH EX_CATCH_IMPL_CPP_ONLY
+#define EX_CATCH_CPP_ONLY Dont_Use_EX_CATCH_CPP_ONLY
+#define EX_CATCH_CPP_AND_SEH EX_CATCH_IMPL
+
+// Note: at this time we don't have a use case for EX_CATCH_EX, and we do not have
+// the C++-only version of the implementation available. Thus we disallow its use at this time.
+// If a real use case arises then we should go ahead and enable this.
+#define EX_CATCH_EX Dont_Use_EX_CATCH_EX
+#endif
+
+#define EX_END_CATCH_UNREACHABLE \
+ DEBUG_ASSURE_NO_RETURN_END(EX_CATCH) \
+ } \
+ SCAN_EHMARKER_END_CATCH(); \
+ UNREACHABLE(); \
+ } \
+ PAL_CPP_ENDTRY \
+ } \
+
+
+// "terminalexceptionpolicy" must be one of "RethrowTerminalExceptions",
+// "RethrowTransientExceptions", or "SwallowAllExceptions"
+
+#define EX_END_CATCH(terminalexceptionpolicy) \
+ terminalexceptionpolicy; \
+ __state.SucceedCatch(); \
+ DEBUG_ASSURE_NO_RETURN_END(EX_CATCH) \
+ } \
+ SCAN_EHMARKER_END_CATCH(); \
+ } \
+ EX_ENDTRY \
+ } \
+
+
+#define EX_END_CATCH_FOR_HOOK \
+ __state.SucceedCatch(); \
+ DEBUG_ASSURE_NO_RETURN_END(EX_CATCH) \
+ ANNOTATION_HANDLER_END; \
+ } \
+ SCAN_EHMARKER_END_CATCH(); \
+ } \
+ EX_ENDTRY
+
+#define EX_ENDTRY \
+ PAL_CPP_ENDTRY
+
+#define EX_RETHROW \
+ { \
+ __pException.SuppressRelease(); \
+ PAL_CPP_RETHROW; \
+ } \
+
+ // Define a copy of GET_EXCEPTION() that will not be redefined by clrex.h
+#define GET_EXCEPTION() (__pException == NULL ? &__defaultException : __pException.GetValue())
+#define EXTRACT_EXCEPTION() (__pException.Extract())
+
+
+//==============================================================================
+// High-level macros for common uses of EX_TRY. Try using these rather
+// than the raw EX_TRY constructs.
+//==============================================================================
+
+//===================================================================================
+// Macro for converting exceptions into HR internally. Unlike EX_CATCH_HRESULT,
+// it does not set up IErrorInfo on the current thread.
+//
+// Usage:
+//
+// HRESULT hr = S_OK;
+// EX_TRY
+// <do managed stuff>
+// EX_CATCH_HRESULT_NO_ERRORINFO(hr);
+// return hr;
+//
+// Comments:
+// Since IErrorInfo is not set up, this does not require COM interop to be started.
+//===================================================================================
+
+#define EX_CATCH_HRESULT_NO_ERRORINFO(_hr) \
+ EX_CATCH \
+ { \
+ (_hr) = GET_EXCEPTION()->GetHR(); \
+ _ASSERTE(FAILED(_hr)); \
+ } \
+ EX_END_CATCH(SwallowAllExceptions)
+
+
+//===================================================================================
+// Macro for catching managed exception object.
+//
+// Usage:
+//
+// OBJECTREF pThrowable = NULL;
+// EX_TRY
+// <do managed stuff>
+// EX_CATCH_THROWABLE(&pThrowable);
+//
+//===================================================================================
+
+#define EX_CATCH_THROWABLE(ppThrowable) \
+ EX_CATCH \
+ { \
+ *ppThrowable = GET_THROWABLE(); \
+ } \
+ EX_END_CATCH(SwallowAllExceptions)
+
+
+#ifdef FEATURE_COMINTEROP
+
+//===================================================================================
+// Macro for defining external entrypoints such as COM interop boundaries.
+// The boundary will catch all exceptions (including terminals) and convert
+// them into HR/IErrorInfo pairs as appropriate.
+//
+// Usage:
+//
+// HRESULT hr = S_OK;
+// EX_TRY
+// <do managed stuff>
+// EX_CATCH_HRESULT(hr);
+// return hr;
+//
+// Comments:
+// Note that IErrorInfo will automatically be set up on the thread if appropriate.
+//===================================================================================
+
+#define EX_CATCH_HRESULT(_hr) \
+ EX_CATCH \
+ { \
+ (_hr) = GET_EXCEPTION()->GetHR(); \
+ _ASSERTE(FAILED(_hr)); \
+ IErrorInfo *pErr = GET_EXCEPTION()->GetErrorInfo(); \
+ if (pErr != NULL) \
+ { \
+ SetErrorInfo(0, pErr); \
+ pErr->Release(); \
+ } \
+ } \
+ EX_END_CATCH(SwallowAllExceptions)
+
+//===================================================================================
+// Macro to make conditional catching more succinct.
+//
+// Usage:
+//
+// EX_TRY
+// ...
+// EX_CATCH_HRESULT_IF(IsHRESULTForExceptionKind(GET_EXCEPTION()->GetHR(), kFileNotFoundException));
+//===================================================================================
+
+#define EX_CATCH_HRESULT_IF(HR, ...) \
+ EX_CATCH \
+ { \
+ (HR) = GET_EXCEPTION()->GetHR(); \
+ \
+ /* Rethrow if condition is false. */ \
+ if (!(__VA_ARGS__)) \
+ EX_RETHROW; \
+ \
+ _ASSERTE(FAILED(HR)); \
+ IErrorInfo *pErr = GET_EXCEPTION()->GetErrorInfo(); \
+ if (pErr != NULL) \
+ { \
+ SetErrorInfo(0, pErr); \
+ pErr->Release(); \
+ } \
+ } \
+ EX_END_CATCH(SwallowAllExceptions)
+
+#else // FEATURE_COMINTEROP
+
+#define EX_CATCH_HRESULT(_hr) EX_CATCH_HRESULT_NO_ERRORINFO(_hr)
+
+#endif // FEATURE_COMINTEROP
+
+//===================================================================================
+// Macro for containing normal exceptions but letting terminal exceptions continue to propagate.
+//
+// Usage:
+//
+// EX_TRY
+// {
+// ...your stuff...
+// }
+// EX_SWALLOW_NONTERMINAL
+//
+// Remember, terminal exceptions (such as ThreadAbort) will still throw out of this
+// block. So don't use this as a substitute for exception-safe cleanup!
+//===================================================================================
+
+#define EX_SWALLOW_NONTERMINAL \
+ EX_CATCH \
+ { \
+ } \
+ EX_END_CATCH(RethrowTerminalExceptions) \
+
+
+//===================================================================================
+// Macro for containing normal exceptions but letting transient exceptions continue to propagate.
+//
+// Usage:
+//
+// EX_TRY
+// {
+// ...your stuff...
+// }
+// EX_SWALLOW_NONTRANSIENT
+//
+// Terminal exceptions (such as ThreadAbort and OutOfMemory) will still throw out of this
+// block. So don't use this as a substitute for exception-safe cleanup!
+//===================================================================================
+
+#define EX_SWALLOW_NONTRANSIENT \
+ EX_CATCH \
+ { \
+ } \
+ EX_END_CATCH(RethrowTransientExceptions) \
+
+
+//===================================================================================
+// Macro for observing or wrapping exceptions in flight.
+//
+// Usage:
+//
+// EX_TRY
+// {
+// ... your stuff ...
+// }
+// EX_HOOK
+// {
+// ... your stuff ...
+// }
+// EX_END_HOOK
+// ... control will never get here ...
+//
+//
+// EX_HOOK is like EX_CATCH except that you can't prevent the
+// exception from being rethrown. You can throw a new exception inside the hook
+// (for example, if you want to wrap the exception in flight with your own).
+// But if control reaches the end of the hook, the original exception gets rethrown.
+//
+// Avoid using EX_HOOK for conditional backout if a destructor-based holder
+// will suffice. Because these macros are implemented on top of SEH, using them will
+// prevent the use of holders anywhere else inside the same function. That is, instead
+// of saying this:
+//
+// EX_TRY // DON'T DO THIS
+// {
+// thing = new Thing();
+// blah
+// }
+// EX_HOOK
+// {
+// delete thing; // if it failed, we don't want to keep the Thing.
+// }
+// EX_END_HOOK
+//
+// do this:
+//
+// Holder<Thing> thing = new Thing(); //DO THIS INSTEAD
+// blah
+// // If we got here, we succeeded. So tell holder we want to keep the thing.
+// thing.SuppressRelease();
+//
+// We won't rethrow the exception if it is a Stack Overflow exception. Instead, we'll throw a new
+// exception. This will allow the stack to unwind point, and so we won't be jeopardizing a
+// second stack overflow.
+//===================================================================================
+#define EX_HOOK \
+ EX_CATCH \
+ { \
+
+#define EX_END_HOOK \
+ } \
+ ANNOTATION_HANDLER_END; \
+ EX_RETHROW; \
+ EX_END_CATCH_FOR_HOOK; \
+ }
+
+// ---------------------------------------------------------------------------
+// Inline implementations. Pay no attention to that man behind the curtain.
+// ---------------------------------------------------------------------------
+
+inline Exception::HandlerState::HandlerState()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ m_dwFlags = 0;
+ m_pExceptionPtr = NULL;
+
+#if defined(STACK_GUARDS_DEBUG) && defined(ENABLE_CONTRACTS_IMPL)
+ // If we have a debug state, use its setting for SO tolerance. The default
+ // is SO-tolerant if we have no debug state. Can't probe w/o debug state and
+ // can't enter SO-interolant mode w/o probing.
+ GetClrDebugState();
+#endif
+}
+
+inline void Exception::HandlerState::CleanupTry()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+}
+
+inline void Exception::HandlerState::SetupCatch(INDEBUG_COMMA(_In_z_ const char * szFile) int lineNum)
+{
+ WRAPPER_NO_CONTRACT;
+
+ /* don't embed file names in retail to save space and avoid IP */
+ /* a findstr /n will allow you to locate it in a pinch */
+#ifdef _DEBUG
+ STRESS_LOG2(LF_EH, LL_INFO100, "EX_CATCH %s line %d\n", szFile, lineNum);
+#else
+ STRESS_LOG1(LF_EH, LL_INFO100, "EX_CATCH line %d\n", lineNum);
+#endif
+
+ SetCaught();
+}
+
+inline void Exception::HandlerState::SucceedCatch()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+}
+
+inline HRException::HRException()
+ : m_hr(E_UNEXPECTED)
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+}
+
+inline HRException::HRException(HRESULT hr)
+ : m_hr(hr)
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ // Catchers assume only failing hresults
+ _ASSERTE(FAILED(hr));
+}
+
+inline HRMsgException::HRMsgException()
+ : HRException()
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+inline HRMsgException::HRMsgException(HRESULT hr, SString const &s)
+ : HRException(hr), m_msg(s)
+{
+ WRAPPER_NO_CONTRACT;
+}
+
+inline COMException::COMException()
+ : HRException(),
+ m_pErrorInfo(NULL)
+{
+ WRAPPER_NO_CONTRACT;
+}
+
+inline COMException::COMException(HRESULT hr)
+ : HRException(hr),
+ m_pErrorInfo(NULL)
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+inline COMException::COMException(HRESULT hr, IErrorInfo *pErrorInfo)
+ : HRException(hr),
+ m_pErrorInfo(pErrorInfo)
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+inline SEHException::SEHException()
+{
+ LIMITED_METHOD_CONTRACT;
+ memset(&m_exception, 0, sizeof(EXCEPTION_RECORD));
+}
+
+inline SEHException::SEHException(EXCEPTION_RECORD *pointers, T_CONTEXT *pContext)
+{
+ LIMITED_METHOD_CONTRACT;
+ memcpy(&m_exception, pointers, sizeof(EXCEPTION_RECORD));
+}
+
+// The exception throwing helpers are intentionally not inlined
+// Exception throwing is a rare slow codepath that should be optimized for code size
+
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr);
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr, SString const &msg);
+void DECLSPEC_NORETURN ThrowWin32(DWORD err);
+void DECLSPEC_NORETURN ThrowLastError();
+void DECLSPEC_NORETURN ThrowOutOfMemory();
+void DECLSPEC_NORETURN ThrowStackOverflow();
+
+#undef IfFailThrow
+inline HRESULT IfFailThrow(HRESULT hr)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+
+ return hr;
+}
+
+inline HRESULT IfFailThrow(HRESULT hr, SString &msg)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (FAILED(hr))
+ {
+ ThrowHR(hr, msg);
+ }
+
+ return hr;
+}
+
+inline HRESULT IfTransientFailThrow(HRESULT hr)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (FAILED(hr) && Exception::IsTransient(hr))
+ {
+ ThrowHR(hr);
+ }
+
+ return hr;
+}
+
+// Set if fatal error (like stack overflow or out of memory) occurred in this process.
+GVAL_DECL(HRESULT, g_hrFatalError);
+
+#endif // _EX_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// FString.h (Fast String)
+//
+
+// ---------------------------------------------------------------------------
+
+// ------------------------------------------------------------------------------------------
+// FString is fast string handling namespace
+
+
+// 1) Simple
+// 2) No C++ exception
+// 3) Optimized for speed
+
+
+#ifndef _FSTRING_H_
+#define _FSTRING_H_
+
+namespace FString
+{
+ // Note: All "length" parameters do not count the space for the null terminator.
+ // Caller of Unicode_Utf8 and Utf8_Unicode must pass in a buffer of size at least length + 1.
+
+ // Scan for ASCII only string, calculate result UTF8 string length
+ HRESULT Unicode_Utf8_Length(_In_z_ LPCWSTR pString, _Out_ bool * pAllAscii, _Out_ DWORD * pLength);
+
+ // Convert UNICODE string to UTF8 string. Direct/fast conversion if ASCII
+ HRESULT Unicode_Utf8(_In_z_ LPCWSTR pString, bool allAscii, _Out_writes_bytes_(length) LPSTR pBuffer, DWORD length);
+
+ // Scan for ASCII string, calculate result UNICODE string length
+ HRESULT Utf8_Unicode_Length(_In_z_ LPCSTR pString, _Out_ bool * pAllAscii, _Out_ DWORD * pLength);
+
+ // Convert UTF8 string to UNICODE. Direct/fast conversion if ASCII
+ HRESULT Utf8_Unicode(_In_z_ LPCSTR pString, bool allAscii, _Out_writes_bytes_(length) LPWSTR pBuffer, DWORD length);
+
+ HRESULT ConvertUnicode_Utf8(_In_z_ LPCWSTR pString, _Outptr_result_z_ LPSTR * pBuffer);
+
+ HRESULT ConvertUtf8_Unicode(_In_z_ LPCSTR pString, _Outptr_result_z_ LPWSTR * pBuffer);
+
+} // namespace FString
+
+#endif // _FSTRING_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+// GetProductVersionNumber.h
+//
+// Helper function to retrieve the file version number of a file.
+//
+// ======================================================================================
+
+
+
+#ifndef __GetProductVersionNumber_h__
+#define __GetProductVersionNumber_h__
+
+#include "contract.h"
+#include "sstring.h"
+#include "holder.h"
+#include "ex.h"
+
+//---------------------------------------------------------------------------------------
+//
+// Given the full path to an image, return the product version number.
+//
+// Arguments:
+// szFullPath - full path to the image
+// pdwVersionMS - out parameter; return the most significant 4 bytes of the version number according to
+// the VS_FIXEDFILEINFO convention
+// pdwVersionLS - out parameter; return the least significant 4 bytes of the version number according to
+// the VS_FIXEDFILEINFO convention
+//
+// Notes:
+// Throws on error
+
+void inline GetProductVersionNumber(SString &szFullPath, DWORD * pdwVersionMS, DWORD * pdwVersionLS)
+{
+ WRAPPER_NO_CONTRACT;
+#ifndef TARGET_UNIX
+
+ DWORD dwDummy = 0;
+ DWORD dwFileInfoSize = 0;
+
+ // Get the size of all of the file version information.
+ dwFileInfoSize = GetFileVersionInfoSize(szFullPath, &dwDummy);
+ if (dwFileInfoSize == 0)
+ {
+ ThrowLastError();
+ }
+
+ // Create the buffer to store the file information.
+ NewHolder<BYTE> pbFileInfo(new BYTE[dwFileInfoSize]);
+
+ // Actually retrieve the file version information.
+ if (!GetFileVersionInfo(szFullPath, NULL, dwFileInfoSize, pbFileInfo))
+ {
+ ThrowLastError();
+ }
+
+ // Now retrieve only the relevant version information, which will be returned in a VS_FIXEDFILEINFO.
+ UINT uVersionInfoSize = 0;
+ VS_FIXEDFILEINFO * pVersionInfo = NULL;
+
+ if (!VerQueryValue(pbFileInfo, W("\\"), reinterpret_cast<LPVOID *>(&pVersionInfo), &uVersionInfoSize))
+ {
+ ThrowLastError();
+ }
+ _ASSERTE(uVersionInfoSize == sizeof(VS_FIXEDFILEINFO));
+
+ *pdwVersionMS = pVersionInfo->dwProductVersionMS;
+ *pdwVersionLS = pVersionInfo->dwProductVersionLS;
+#else
+ *pdwVersionMS = 0;
+ *pdwVersionLS = 0;
+#endif // TARGET_UNIX
+}
+
+#endif // __GetProductVersionNumber_h__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#ifndef __HOLDER_H_
+#define __HOLDER_H_
+
+#include <wincrypt.h>
+#include "cor.h"
+#include "staticcontract.h"
+#include "volatile.h"
+#include "palclr.h"
+
+#ifdef PAL_STDCPP_COMPAT
+#include <utility>
+#include <type_traits>
+#else
+#include "clr_std/utility"
+#include "clr_std/type_traits"
+#endif
+
+#if defined(FEATURE_COMINTEROP) && !defined(STRIKE)
+#include <Activation.h>
+#include <Inspectable.h>
+#endif
+
+// Note: you can't use CONTRACT's in this file. You can't use dynamic contracts because the impl of dynamic
+// contracts depends on holders. You can't use static contracts because they include the function name as a string,
+// and the template names in this file are just too long, so you get a compiler error.
+//
+// All the functions in this file are pretty basic, so the lack of CONTRACT's isn't a big loss.
+
+#ifdef _MSC_VER
+// Make sure we can recurse deep enough for FORCEINLINE
+#pragma inline_recursion(on)
+#pragma inline_depth(16)
+#pragma warning(disable:4714)
+#endif // _MSC_VER
+
+//------------------------------------------------------------------------------------------------
+// Declare but do not define methods that would normally be automatically generated by the
+// compiler. This will produce a link-time error if they are used. This is involved in object
+// initialization and operator eliding; see
+// http://groups.google.com/group/comp.lang.c++/msg/3cd673ab749bed83?dmode=source
+// for the intricate details.
+
+#ifdef __GNUC__
+// GCC checks accessibility of the copy ctor before performing elision optimization, so
+// it must be declared as public. VC does not, so we can declare it private to give an
+// earlier error message.
+#define HIDE_GENERATED_METHODS(_NAME) \
+ public: \
+ _NAME(_NAME const &); \
+ private: \
+ _NAME & operator=(_NAME const &);
+#else
+#define HIDE_GENERATED_METHODS(_NAME) \
+ private: \
+ _NAME(_NAME const &); \
+ _NAME & operator=(_NAME const &);
+#endif
+
+#ifdef _DEBUG
+
+//------------------------------------------------------------------------------------------------
+// This is used to make Visual Studio autoexp.dat work sensibly with holders again.
+// The problem is that certain codebases (particulary Fusion) implement key data structures
+// using a class but refer to it using a holder to an interface type. This combination prevents
+// autoexp rules from working as desired.
+//
+// Example: Take this useful autoexp rule for CAssemblyName.
+//
+// CAssemblyName=<_rProp._rProp[3].asStr>
+//
+// To get the same rule to fire when your assemblyname is wrapped in a ReleaseHolder,
+// add these companion rules.
+//
+// HolderBase<CAssemblyName *>=<m_value->_rProp._rProp[3].asStr>
+// HolderBase<IAssemblyName *>=<m_pAutoExpVisibleValue->_asCAssemblyName->_rProp._rProp[3].asStr>
+//
+//------------------------------------------------------------------------------------------------
+struct AutoExpVisibleValue
+{
+ private:
+ union
+ {
+ const void *_pPreventEmptyUnion;
+ };
+};
+#endif //_DEBUG
+
+//-----------------------------------------------------------------------------
+// Holder is the base class of all holder objects. Any backout object should derive from it.
+// (Eventually some additional bookeeping and exception handling code will be placed in this
+// base class.)
+//
+// There are several ways to use this class:
+// 1. Derive from HolderBase, and instantiate a Holder or Wrapper around your base. This is necessary
+// if you need to add more state to your holder.
+// 2. Instantiate the Holder template with your type and functions.
+// 3. Instantiate the Wrapper template with your type and functions. The Wrapper adds some additional
+// operator overloads to provide "smart pointer" like behavior
+// 4. Use a prebaked Holder. This is ALWAYS the preferable strategy. It is expected that
+// the general design patter is that Holders will be provided as part of a typical data abstraction.
+// (See Crst for an example of this.)
+//-----------------------------------------------------------------------------
+
+
+
+//-----------------------------------------------------------------------------
+// HolderBase defines the base holder functionality. You can subtype and plug in
+// a different base if you need to add more members for access during
+// acquire & release
+//-----------------------------------------------------------------------------
+template <typename TYPE>
+class HolderBase
+{
+ friend class ClrDataAccess;
+
+ protected:
+ TYPE m_value;
+
+ HolderBase(TYPE value)
+ : m_value(value)
+ {
+#ifdef _DEBUG
+ m_pAutoExpVisibleValue = (const AutoExpVisibleValue *)(&m_value);
+#endif //_DEBUG
+ }
+
+ void DoAcquire()
+ {
+ // Insert any global or thread bookeeping here
+ }
+
+ void DoRelease()
+ {
+ // Insert any global or thread bookeeping here
+ }
+
+#ifdef _DEBUG
+ private:
+ // NOT DEAD CODE: This field is not referenced in the source code but it is not a dead field. See comments above "class AutoExpVisibleValue" for why this field exists.
+ // Note: What we really want is for m_value and m_pAutoExpVisibleValue to be members of a union. But the compiler won't let you build that
+ // since it can't prove that "TYPE" doesn't have a non-trivial constructor.
+ const AutoExpVisibleValue *m_pAutoExpVisibleValue; // This field is to be referenced from individual autoexp.dat files, NOT from the CLR source code.
+#endif //_DEBUG
+
+}; // class HolderBase<>
+
+#ifndef _PREFAST_ // Work around an ICE error in EspX.dll
+
+template <typename TYPE>
+BOOL CompareDefault(TYPE value, TYPE defaultValue)
+{
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ return value == defaultValue;
+}
+
+#else
+
+template <typename TYPE>
+BOOL CompareDefault(TYPE value, TYPE defaultValue)
+{
+ return FALSE;
+}
+
+#endif
+
+
+template <typename TYPE>
+BOOL NoNull(TYPE value, TYPE defaultValue)
+{
+ return FALSE;
+}
+
+// Used e.g. for retail version of code:SyncAccessHolder
+template <typename TYPE>
+class NoOpBaseHolder
+{
+ public:
+ FORCEINLINE NoOpBaseHolder()
+ {
+ }
+ FORCEINLINE NoOpBaseHolder(TYPE value, BOOL take = TRUE)
+ {
+ }
+ FORCEINLINE ~NoOpBaseHolder()
+ {
+ }
+ FORCEINLINE void Assign(TYPE value, BOOL fTake = TRUE)
+ {
+ }
+ FORCEINLINE void Acquire()
+ {
+ }
+ FORCEINLINE void Release()
+ {
+ }
+ FORCEINLINE void Clear()
+ {
+ }
+ FORCEINLINE void SuppressRelease()
+ {
+ }
+ FORCEINLINE TYPE Extract()
+ {
+ }
+ FORCEINLINE TYPE GetValue()
+ {
+ }
+ FORCEINLINE BOOL IsNull() const
+ {
+ return FALSE;
+ }
+
+ private:
+ NoOpBaseHolder& operator=(NoOpBaseHolder const &);
+ NoOpBaseHolder(NoOpBaseHolder const &);
+}; // NoOpBaseHolder<>
+
+
+template
+ <
+ typename TYPE,
+ typename BASE,
+ UINT_PTR DEFAULTVALUE = 0,
+ BOOL IS_NULL(TYPE, TYPE) = CompareDefault<TYPE>
+ >
+class BaseHolder : protected BASE
+{
+ friend class ClrDataAccess;
+ protected:
+ BOOL m_acquired; // Have we acquired the resource?
+
+ static_assert(!std::is_pointer<TYPE>::value || DEFAULTVALUE == 0 || DEFAULTVALUE == UINT_PTR(-1 /*INVALID_HANDLE_VALUE*/),
+ "DEFAULTVALUE must be NULL for pointer holders and wrappers.");
+
+ public:
+ FORCEINLINE BaseHolder()
+ : BASE(TYPE(DEFAULTVALUE)),
+ m_acquired(FALSE)
+ {
+ }
+ FORCEINLINE BaseHolder(TYPE value)
+ : BASE(value),
+ m_acquired(FALSE)
+ {
+ if (!IsNull())
+ Acquire();
+ }
+ FORCEINLINE BaseHolder(TYPE value, BOOL takeOwnership)
+ : BASE(value),
+ m_acquired(FALSE)
+ {
+ if (takeOwnership)
+ Acquire();
+ }
+ FORCEINLINE ~BaseHolder()
+ {
+ Release();
+ }
+ // Sets the value to 'value'. Doesn't call Acquire if value is DEFAULTVALUE.
+ FORCEINLINE void Assign(TYPE value)
+ {
+ Assign(value, !IS_NULL(value, TYPE(DEFAULTVALUE)));
+ }
+ // Sets the value to 'value'. Doesn't call Acquire if fTake is FALSE.
+ FORCEINLINE void Assign(TYPE value, BOOL takeOwnership)
+ {
+ Release();
+ this->m_value = value;
+ if (takeOwnership)
+ Acquire();
+ }
+ FORCEINLINE void Acquire()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ _ASSERTE(!m_acquired);
+
+ if (!IsNull())
+ {
+ this->DoAcquire();
+ m_acquired = TRUE;
+ }
+ }
+ FORCEINLINE void Release()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ if (m_acquired)
+ {
+ _ASSERTE(!IsNull());
+ this->DoRelease();
+ m_acquired = FALSE;
+ }
+ }
+
+ FORCEINLINE void Clear()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ Assign(TYPE(DEFAULTVALUE), FALSE);
+ }
+ FORCEINLINE void SuppressRelease()
+ {
+ STATIC_CONTRACT_LEAF;
+ m_acquired = FALSE;
+ }
+ FORCEINLINE TYPE Extract()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ SuppressRelease();
+ return GetValue();
+ }
+ FORCEINLINE TYPE GetValue()
+ {
+ STATIC_CONTRACT_LEAF;
+ return this->m_value;
+ }
+ FORCEINLINE BOOL IsNull() const
+ {
+ STATIC_CONTRACT_WRAPPER;
+ return IS_NULL(this->m_value, TYPE(DEFAULTVALUE));
+ }
+
+ HIDE_GENERATED_METHODS(BaseHolder)
+}; // BaseHolder<>
+
+template <void (*ACQUIRE)(), void (*RELEASEF)()>
+class StateHolder
+{
+ private:
+ BOOL m_acquired; // Have we acquired the state?
+
+ public:
+ FORCEINLINE StateHolder(BOOL take = TRUE)
+ : m_acquired(FALSE)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ if (take)
+ Acquire();
+ }
+ FORCEINLINE ~StateHolder()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ Release();
+ }
+ FORCEINLINE void Acquire()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ // Insert any global or thread bookeeping here
+
+ _ASSERTE(!m_acquired);
+
+ ACQUIRE();
+ m_acquired = TRUE;
+ }
+ FORCEINLINE void Release()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ // Insert any global or thread bookeeping here
+
+ if (m_acquired)
+ {
+ RELEASEF();
+ m_acquired = FALSE;
+ }
+ }
+ FORCEINLINE void Clear()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ if (m_acquired)
+ {
+ RELEASEF();
+ }
+
+ m_acquired = FALSE;
+ }
+ FORCEINLINE void SuppressRelease()
+ {
+ STATIC_CONTRACT_LEAF;
+ m_acquired = FALSE;
+ }
+
+ HIDE_GENERATED_METHODS(StateHolder)
+}; // class StateHolder<>
+
+// Holder for the case where the acquire function can fail.
+template <typename VALUE, BOOL (*ACQUIRE)(VALUE value), void (*RELEASEF)(VALUE value)>
+class ConditionalStateHolder
+{
+ private:
+ VALUE m_value;
+ BOOL m_acquired; // Have we acquired the state?
+
+ public:
+ FORCEINLINE ConditionalStateHolder(VALUE value, BOOL take = TRUE)
+ : m_value(value), m_acquired(FALSE)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ if (take)
+ Acquire();
+ }
+ FORCEINLINE ~ConditionalStateHolder()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ Release();
+ }
+ FORCEINLINE BOOL Acquire()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ // Insert any global or thread bookeeping here
+
+ _ASSERTE(!m_acquired);
+
+ m_acquired = ACQUIRE(m_value);
+
+ return m_acquired;
+ }
+ FORCEINLINE void Release()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ // Insert any global or thread bookeeping here
+
+ if (m_acquired)
+ {
+ RELEASEF(m_value);
+ m_acquired = FALSE;
+ }
+ }
+ FORCEINLINE void Clear()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ if (m_acquired)
+ {
+ RELEASEF(m_value);
+ }
+
+ m_acquired = FALSE;
+ }
+ FORCEINLINE void SuppressRelease()
+ {
+ STATIC_CONTRACT_LEAF;
+ m_acquired = FALSE;
+ }
+ FORCEINLINE BOOL Acquired()
+ {
+ STATIC_CONTRACT_LEAF;
+
+ return m_acquired;
+ }
+
+ HIDE_GENERATED_METHODS(ConditionalStateHolder)
+}; // class ConditionalStateHolder<>
+
+
+// Making the copy constructor private produces a warning about "can't generate copy
+// constructor" on all holders (duh, that's the point.)
+#ifdef _MSC_VER
+#pragma warning(disable:4511)
+#endif // _MSC_VER
+
+//-----------------------------------------------------------------------------
+// BaseWrapper is just Base like a Holder, but it "transparently" proxies the type it contains,
+// using operator overloads. Use this when you want a holder to expose the functionality of
+// the value it contains.
+//-----------------------------------------------------------------------------
+template <typename TYPE, typename BASE,
+ UINT_PTR DEFAULTVALUE = 0, BOOL IS_NULL(TYPE, TYPE) = CompareDefault<TYPE>>
+class BaseWrapper : public BaseHolder<TYPE, BASE, DEFAULTVALUE, IS_NULL>
+{
+ typedef BaseHolder<TYPE, BASE, DEFAULTVALUE, IS_NULL> BaseT;
+
+
+#ifdef __GNUC__
+//#pragma GCC visibility push(hidden)
+#endif // __GNUC__
+ // This temporary object takes care of the case where we are initializing
+ // a holder's contents by passing it as an out parameter. The object is
+ // guaranteed to live longer than the call it is passed to, hence we should
+ // properly acquire the object on return
+ friend class AddressInitHolder;
+ class AddressInitHolder
+ {
+ protected:
+ BaseWrapper<TYPE,BASE,DEFAULTVALUE,IS_NULL> &m_holder;
+
+ public:
+ FORCEINLINE AddressInitHolder(BaseWrapper<TYPE,BASE,DEFAULTVALUE,IS_NULL> &holder)
+ : m_holder(holder)
+ {
+ //
+ // We must clear the value, to avoid the following scenario:
+ //
+ // ReleaseHolder<MyType> pMyType;
+ // hr = MyFunction(&pMyType, ...);
+ // <do something with pMyType>
+ // hr = MyFunction(&pMyType, ...); <- calls Release before call and Acquire on return.
+ //
+ // If the second call to MyFunction were to fail and return without updating the
+ // out parameter, then ~AddressInitHolder will all Acquire, which is a no-op on
+ // the underlying pointer but sets m_acquired to TRUE. Thus, when pMyType goes
+ // out of scope, ReleaseHolder will cause a double-release of pMyType.
+ //
+ // By calling Clear, then the call to Acquire in the dtor will not set m_acquired
+ // to true since IsNull(m_holder.m_value) will return true.
+ //
+ m_holder.Clear();
+ }
+ FORCEINLINE ~AddressInitHolder()
+ {
+ m_holder.Acquire();
+ }
+
+ // It's not optimal to have to declare these casting operators. But if we don't,
+ // people cannot cast the result of &holder to these values. (The C++ compiler won't
+ // automatically use the TYPE * cast as an intermediate value.) So we put them here,
+ // rather than forcing callers to double cast in these common cases.
+
+ FORCEINLINE operator IUnknown **()
+ {
+ IUnknown *unknown;
+ // Typesafe check. This will fail at compile time if
+ // m_holder.m_value can't be converted to an IUnknown *.
+ unknown = static_cast<IUnknown*>(m_holder.m_value);
+ // do the cast with an unsafe cast
+ return (IUnknown **)(&m_holder.m_value);
+ }
+
+#if defined(FEATURE_COMINTEROP) && !defined(STRIKE)
+ FORCEINLINE operator IInspectable **()
+ {
+ IInspectable *inspectable;
+ // Typesafe check. This will fail at compile time if
+ // m_holder.m_value can't be converted to an IInspectable *.
+ inspectable = static_cast<IInspectable *>(m_holder.m_value);
+ return (IInspectable **)(&m_holder.m_value);
+
+ }
+#endif // FEATURE_COMINTEROP
+
+ FORCEINLINE operator void **()
+ {
+ return (void **)(&m_holder.m_value);
+ }
+ FORCEINLINE operator void *()
+ {
+ return (void *)(&m_holder.m_value);
+ }
+ };
+
+ // Separate out method with TYPE * cast operator, since it may clash with IUnknown ** or
+ // void ** cast operator.
+ friend class TypedAddressInitHolder;
+ class TypedAddressInitHolder : public AddressInitHolder
+ {
+ public:
+ FORCEINLINE TypedAddressInitHolder(BaseWrapper & holder)
+ : AddressInitHolder(holder)
+ {
+ }
+
+ FORCEINLINE operator TYPE *()
+ {
+ return static_cast<TYPE *>(&this->m_holder.m_value);
+ }
+ };
+#ifdef __GNUC__
+//#pragma GCC visibility pop
+#endif // __GNUC__
+
+ public:
+ FORCEINLINE BaseWrapper()
+ : BaseT(TYPE(DEFAULTVALUE), FALSE)
+ {
+ }
+ FORCEINLINE BaseWrapper(TYPE value)
+ : BaseT(value)
+ {
+ }
+ FORCEINLINE BaseWrapper(TYPE value, BOOL take)
+ : BaseT(value, take)
+ {
+ }
+ FORCEINLINE BaseWrapper& operator=(TYPE value)
+ {
+ BaseT::Assign(value);
+ return *this;
+ }
+ FORCEINLINE operator TYPE() const
+ {
+ return this->m_value;
+ }
+ FORCEINLINE TypedAddressInitHolder operator&()
+ {
+ return TypedAddressInitHolder(*this);
+ }
+ template <typename T>
+ FORCEINLINE bool operator==(T const & value) const
+ {
+ return !!(this->m_value == TYPE(value));
+ }
+ template <typename T>
+ FORCEINLINE bool operator!=(T const & value) const
+ {
+ return !!(this->m_value != TYPE(value));
+ }
+
+ // This handles the NULL value that is an int and the
+ // compiler doesn't want to convert int to a pointer.
+ FORCEINLINE bool operator==(int value) const
+ {
+ return !!(this->m_value == TYPE((void*)(SIZE_T)value));
+ }
+ FORCEINLINE bool operator!=(int value) const
+ {
+ return !!(this->m_value != TYPE((void*)(SIZE_T)value));
+ }
+
+ FORCEINLINE const TYPE &operator->() const
+ {
+ return this->m_value;
+ }
+ FORCEINLINE int operator!() const
+ {
+ return this->IsNull();
+ }
+
+ private:
+ HIDE_GENERATED_METHODS(BaseWrapper)
+}; // class BaseWrapper<>
+
+//-----------------------------------------------------------------------------
+// Generic templates to use to wrap up acquire/release functionality for Holder
+//-----------------------------------------------------------------------------
+
+template <typename TYPE>
+FORCEINLINE void DoNothing(TYPE value)
+{
+ // @TODO: Due to prefast template problems, implementations of the DoNothing macro have been changed
+ // Search by prefast, and remove them when prefast is ready
+}
+
+FORCEINLINE void DoNothing()
+{
+}
+
+// Prefast stuff.We should have DoNothing<type*> in the holder declaration, but currently
+// prefast doesnt support, it, so im stuffing all these here so if we need to change the template you can change
+// everything here. When prefast works, remove the following functions
+struct ConnectionCookie;
+FORCEINLINE void ConnectionCookieDoNothing(ConnectionCookie* p)
+{
+}
+
+class ComCallWrapper;
+FORCEINLINE void CCWHolderDoNothing(ComCallWrapper* p)
+{
+}
+
+
+FORCEINLINE void DispParamHolderDoNothing(VARIANT* p)
+{
+}
+
+FORCEINLINE void VariantPtrDoNothing(VARIANT* p)
+{
+}
+
+FORCEINLINE void VariantDoNothing(VARIANT)
+{
+}
+
+FORCEINLINE void ZeroDoNothing(VOID* p)
+{
+}
+
+class CtxEntry;
+FORCEINLINE void CtxEntryDoNothing(CtxEntry* p)
+{
+}
+
+struct RCW;
+FORCEINLINE void NewRCWHolderDoNothing(RCW*)
+{
+}
+
+// Prefast stuff.We should have DoNothing<SafeArray*> in the holder declaration
+FORCEINLINE void SafeArrayDoNothing(SAFEARRAY* p)
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Holder/Wrapper are the simplest way to define holders - they synthesizes a base class out of
+// function pointers
+//-----------------------------------------------------------------------------
+
+template <typename TYPE, void (*ACQUIREF)(TYPE), void (*RELEASEF)(TYPE)>
+class FunctionBase : protected HolderBase<TYPE>
+{
+ friend class ClrDataAccess;
+ protected:
+
+ FORCEINLINE FunctionBase(TYPE value)
+ : HolderBase<TYPE>(value)
+ {
+ }
+
+ FORCEINLINE void DoAcquire()
+ {
+ ACQUIREF(this->m_value);
+ }
+
+ void DoRelease()
+ {
+ RELEASEF(this->m_value);
+ }
+}; // class Function<>
+
+template
+ <
+ typename TYPE,
+ void (*ACQUIREF)(TYPE),
+ void (*RELEASEF)(TYPE),
+ UINT_PTR DEFAULTVALUE = 0,
+ BOOL IS_NULL(TYPE, TYPE) = CompareDefault<TYPE>,
+ // For legacy compat (see EEJitManager::WriterLockHolder), where default ctor
+ // causes ACQUIREF(DEFAULTVALUE), but ACQUIREF ignores the argument and
+ // operates on static or global value instead.
+ bool DEFAULT_CTOR_ACQUIRE = true
+ >
+class Holder : public BaseHolder<TYPE, FunctionBase<TYPE, ACQUIREF, RELEASEF>,
+ DEFAULTVALUE, IS_NULL>
+{
+ typedef BaseHolder<TYPE, FunctionBase<TYPE, ACQUIREF, RELEASEF>,
+ DEFAULTVALUE, IS_NULL> BaseT;
+
+ public:
+ FORCEINLINE Holder()
+ : BaseT(TYPE(DEFAULTVALUE), DEFAULT_CTOR_ACQUIRE)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ }
+
+ FORCEINLINE Holder(TYPE value)
+ : BaseT(value)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ }
+
+ FORCEINLINE Holder(TYPE value, BOOL takeOwnership)
+ : BaseT(value, takeOwnership)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ }
+
+ FORCEINLINE Holder& operator=(TYPE p)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ BaseT::operator=(p);
+ return *this;
+ }
+
+ HIDE_GENERATED_METHODS(Holder)
+};
+
+//---------------------------------------------------------------------------------------
+//
+template
+ <
+ typename TYPE,
+ void (*ACQUIREF)(TYPE),
+ void (*RELEASEF)(TYPE),
+ UINT_PTR DEFAULTVALUE = 0,
+ BOOL IS_NULL(TYPE, TYPE) = CompareDefault<TYPE>,
+ // For legacy compat (see EEJitManager::WriterLockHolder), where default ctor
+ // causes ACQUIREF(DEFAULTVALUE), but ACQUIREF ignores the argument and
+ // operates on static or global value instead.
+ bool DEFAULT_CTOR_ACQUIRE = true
+ >
+class Wrapper : public BaseWrapper<TYPE, FunctionBase<TYPE, ACQUIREF, RELEASEF>,
+ DEFAULTVALUE, IS_NULL>
+{
+ typedef BaseWrapper<TYPE, FunctionBase<TYPE, ACQUIREF, RELEASEF>,
+ DEFAULTVALUE, IS_NULL> BaseT;
+
+ public:
+ FORCEINLINE Wrapper()
+ : BaseT(TYPE(DEFAULTVALUE), DEFAULT_CTOR_ACQUIRE)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ }
+
+ FORCEINLINE Wrapper(TYPE value)
+ : BaseT(value)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ }
+
+ FORCEINLINE Wrapper(TYPE value, BOOL takeOwnership)
+ : BaseT(value, takeOwnership)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ }
+
+ FORCEINLINE Wrapper& operator=(TYPE const & value)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ BaseT::operator=(value);
+ return *this;
+ }
+
+ HIDE_GENERATED_METHODS(Wrapper)
+}; // Wrapper<>
+
+//---------------------------------------------------------------------------------------
+// - Cannot use the standard INDEBUG macro: holder.h is used in places where INDEBUG is defined in the nonstandard way
+#if defined(_DEBUG)
+#define INDEBUG_AND_WINDOWS_FOR_HOLDERS(x) x
+#else
+#define INDEBUG_AND_WINDOWS_FOR_HOLDERS(x)
+#endif
+
+template <typename _TYPE, void (*_RELEASEF)(_TYPE*)>
+class SpecializedWrapper : public Wrapper<_TYPE*, DoNothing<_TYPE*>, _RELEASEF, NULL>
+{
+ using BaseT = Wrapper<_TYPE*, DoNothing<_TYPE*>, _RELEASEF, NULL>;
+public:
+ FORCEINLINE SpecializedWrapper() : BaseT(NULL, FALSE)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ INDEBUG_AND_WINDOWS_FOR_HOLDERS(m_pvalue = &this->m_value;)
+ }
+ FORCEINLINE SpecializedWrapper(_TYPE* value) : BaseT(value)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ INDEBUG_AND_WINDOWS_FOR_HOLDERS(m_pvalue = &this->m_value;)
+ }
+ FORCEINLINE SpecializedWrapper(_TYPE* value, BOOL takeOwnership) : BaseT(value, takeOwnership)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ INDEBUG_AND_WINDOWS_FOR_HOLDERS(m_pvalue = &this->m_value;)
+ }
+ FORCEINLINE ~SpecializedWrapper()
+ {
+ }
+
+ SpecializedWrapper(SpecializedWrapper const &) = delete;
+ SpecializedWrapper & operator=(SpecializedWrapper const &) = delete;
+
+ FORCEINLINE SpecializedWrapper& operator=(_TYPE * value)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ BaseT::operator=(value);
+ return *this;
+ }
+
+ FORCEINLINE SpecializedWrapper(SpecializedWrapper && other)
+ : BaseT(NULL, FALSE)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ INDEBUG_AND_WINDOWS_FOR_HOLDERS(m_pvalue = &this->m_value;)
+ *this = std::move(other);
+ }
+
+ FORCEINLINE SpecializedWrapper& operator=(SpecializedWrapper && other)
+ {
+ BaseT::m_value = std::move(other.BaseT::m_value);
+ BaseT::m_acquired = std::move(other.BaseT::m_acquired);
+ other.BaseT::m_value = nullptr;
+ other.BaseT::m_acquired = FALSE;
+ return *this;
+ }
+
+ /* Since operator& is overloaded we need a way to get a type safe this pointer. */
+ FORCEINLINE SpecializedWrapper* GetAddr()
+ {
+ STATIC_CONTRACT_LEAF;
+ return this;
+ }
+ private:
+ /* m_ppValue: Do not use from source code: Only for convenient use from debugger */
+ /* watch windows - saves five mouseclicks when inspecting holders. */
+ INDEBUG_AND_WINDOWS_FOR_HOLDERS(_TYPE ** m_pvalue;)
+};
+
+//-----------------------------------------------------------------------------
+// NOTE: THIS IS UNSAFE TO USE IN THE VM for interop COM objects!!
+// WE DO NOT CORRECTLY CHANGE TO PREEMPTIVE MODE BEFORE CALLING RELEASE!!
+// USE SafeComHolder
+//
+// ReleaseHolder : COM Interface holder for use outside the VM (or on well known instances
+// which do not need preemptive Relesae)
+//
+// Usage example:
+//
+// {
+// ReleaseHolder<IFoo> foo;
+// hr = FunctionToGetRefOfFoo(&foo);
+// // Note ComHolder doesn't call AddRef - it assumes you already have a ref (if non-0).
+// } // foo->Release() on out of scope (WITHOUT RESPECT FOR GC MODE!!)
+//
+//-----------------------------------------------------------------------------
+
+template <typename TYPE>
+FORCEINLINE void DoTheRelease(TYPE *value)
+{
+ if (value)
+ {
+ value->Release();
+ }
+}
+
+template<typename _TYPE>
+using DoNothingHolder = SpecializedWrapper<_TYPE, DoNothing<_TYPE*>>;
+
+template<typename _TYPE>
+using ReleaseHolder = SpecializedWrapper<_TYPE, DoTheRelease<_TYPE>>;
+
+template<typename _TYPE>
+using NonVMComHolder = SpecializedWrapper<_TYPE, DoTheRelease<_TYPE>>;
+
+
+//-----------------------------------------------------------------------------
+// StubHolder : holder for stubs
+//
+// Usage example:
+//
+// {
+// StubHolder<Stub> foo;
+// foo = new Stub();
+// foo->AddRef();
+// // Note StubHolder doesn't call AddRef for you.
+// } // foo->DecRef() on out of scope
+//
+//-----------------------------------------------------------------------------
+template<typename _TYPE>
+class ExecutableWriterHolder;
+
+template <typename TYPE>
+FORCEINLINE void StubRelease(TYPE* value)
+{
+ if (value)
+ {
+ ExecutableWriterHolder<TYPE> stubWriterHolder(value, sizeof(TYPE));
+ stubWriterHolder.GetRW()->DecRef();
+ }
+}
+
+template<typename _TYPE>
+using StubHolder = SpecializedWrapper<_TYPE, StubRelease<_TYPE>>;
+
+//-----------------------------------------------------------------------------
+// CoTaskMemHolder : CoTaskMemAlloc allocated memory holder
+//
+// {
+// CoTaskMemHolder<Foo> foo = (Foo*) CoTaskMemAlloc(sizeof(Foo));
+// } // delete foo on out of scope
+//-----------------------------------------------------------------------------
+
+template <typename TYPE>
+FORCEINLINE void DeleteCoTaskMem(TYPE *value)
+{
+ if (value)
+ CoTaskMemFree(value);
+}
+
+template<typename _TYPE>
+using CoTaskMemHolder = SpecializedWrapper<_TYPE, DeleteCoTaskMem<_TYPE>>;
+
+//-----------------------------------------------------------------------------
+// NewHolder : New'ed memory holder
+//
+// {
+// NewHolder<Foo> foo = new Foo ();
+// } // delete foo on out of scope
+//-----------------------------------------------------------------------------
+
+template <typename TYPE>
+FORCEINLINE void Delete(TYPE *value)
+{
+ STATIC_CONTRACT_LEAF;
+
+ static_assert(!std::is_same<typename std::remove_cv<TYPE>::type, WCHAR>::value,
+ "Must use NewArrayHolder (not NewHolder) for strings.");
+ static_assert(!std::is_same<typename std::remove_cv<TYPE>::type, CHAR>::value,
+ "Must use NewArrayHolder (not NewHolder) for strings.");
+
+ delete value;
+}
+
+template<typename _TYPE>
+using NewHolder = SpecializedWrapper<_TYPE, Delete<_TYPE>>;
+
+ //-----------------------------------------------------------------------------
+// NewExecutableHolder : New'ed memory holder for executable memory.
+//
+// {
+// NewExecutableHolder<Foo> foo = (Foo*) new (executable) Byte[num];
+// } // delete foo on out of scope
+//-----------------------------------------------------------------------------
+// IJW
+template<class T> void DeleteExecutable(T *p);
+
+template<typename _TYPE>
+using NewExecutableHolder = SpecializedWrapper<_TYPE, DeleteExecutable<_TYPE>>;
+
+//-----------------------------------------------------------------------------
+// NewArrayHolder : New []'ed pointer holder
+// {
+// NewArrayHolder<Foo> foo = new Foo [30];
+// } // delete [] foo on out of scope
+//-----------------------------------------------------------------------------
+
+template <typename TYPE>
+FORCEINLINE void DeleteArray(TYPE *value)
+{
+ STATIC_CONTRACT_WRAPPER;
+ delete [] value;
+ value = NULL;
+}
+
+template<typename _TYPE>
+using NewArrayHolder = SpecializedWrapper<_TYPE, DeleteArray<_TYPE>>;
+typedef NewArrayHolder<CHAR> AStringHolder;
+typedef NewArrayHolder<WCHAR> WStringHolder;
+
+//-----------------------------------------------------------------------------
+// A special array holder that expects its contents are interface pointers,
+// and will call Release() on them.
+//
+// NOTE: You may ONLY use this if you've determined that it is SAFE to call
+// Release() on the contained interface pointers (e.g., as opposed to SafeRelease)
+//
+template <typename INTERFACE>
+class NewInterfaceArrayHolder : public NewArrayHolder<INTERFACE *>
+{
+public:
+ NewInterfaceArrayHolder() :
+ NewArrayHolder<INTERFACE *>(),
+ m_cElements(0)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ }
+
+ NewInterfaceArrayHolder& operator=(INTERFACE ** value)
+ {
+ STATIC_CONTRACT_WRAPPER;
+ NewArrayHolder<INTERFACE *>::operator=(value);
+ return *this;
+ }
+
+ void SetElementCount(ULONG32 cElements)
+ {
+ STATIC_CONTRACT_LEAF;
+ m_cElements = cElements;
+ }
+
+ ~NewInterfaceArrayHolder()
+ {
+ STATIC_CONTRACT_LEAF;
+ for (ULONG32 i=0; i < m_cElements; i++)
+ {
+ if (this->m_value[i] != NULL)
+ this->m_value[i]->Release();
+ }
+ }
+
+protected:
+ ULONG32 m_cElements;
+};
+
+
+//-----------------------------------------------------------------------------
+// ResetPointerHolder : pointer which needs to be set to NULL
+// {
+// ResetPointerHolder<Foo> holder = &pFoo;
+// } // "*pFoo=NULL" on out of scope
+//-----------------------------------------------------------------------------
+#ifdef __GNUC__
+// With -fvisibility-inlines-hidden, the Invoke methods below
+// get hidden, which causes warnings when visible classes expose them.
+#define VISIBLE __attribute__ ((visibility("default")))
+#else
+#define VISIBLE
+#endif // __GNUC__
+
+namespace detail
+{
+ template <typename T>
+ struct ZeroMem
+ {
+ static VISIBLE void Invoke(T * pVal)
+ {
+ ZeroMemory(pVal, sizeof(T));
+ }
+ };
+
+ template <typename T>
+ struct ZeroMem<T*>
+ {
+ static VISIBLE void Invoke(T ** pVal)
+ {
+ *pVal = NULL;
+ }
+ };
+
+}
+#undef VISIBLE
+
+template<typename _TYPE>
+using ResetPointerHolder = SpecializedWrapper<_TYPE, detail::ZeroMem<_TYPE>::Invoke>;
+template<typename _TYPE>
+using FieldNuller = SpecializedWrapper<_TYPE, detail::ZeroMem<_TYPE>::Invoke>;
+
+//-----------------------------------------------------------------------------
+// Wrap win32 functions using HANDLE
+//-----------------------------------------------------------------------------
+
+FORCEINLINE void VoidCloseHandle(HANDLE h) { if (h != NULL) CloseHandle(h); }
+// (UINT_PTR) -1 is INVALID_HANDLE_VALUE
+FORCEINLINE void VoidCloseFileHandle(HANDLE h) { if (h != ((HANDLE)((LONG_PTR) -1))) CloseHandle(h); }
+FORCEINLINE void VoidFindClose(HANDLE h) { FindClose(h); }
+FORCEINLINE void VoidUnmapViewOfFile(void *ptr) { UnmapViewOfFile(ptr); }
+
+template <typename TYPE>
+FORCEINLINE void TypeUnmapViewOfFile(TYPE *ptr) { UnmapViewOfFile(ptr); }
+
+// (UINT_PTR) -1 is INVALID_HANDLE_VALUE
+//@TODO: Dangerous default value. Some Win32 functions return INVALID_HANDLE_VALUE, some return NULL (such as CreatEvent).
+typedef Wrapper<HANDLE, DoNothing<HANDLE>, VoidCloseHandle, (UINT_PTR) -1> HandleHolder;
+typedef Wrapper<HANDLE, DoNothing<HANDLE>, VoidCloseFileHandle, (UINT_PTR) -1> FileHandleHolder;
+typedef Wrapper<HANDLE, DoNothing<HANDLE>, VoidFindClose, (UINT_PTR) -1> FindHandleHolder;
+
+typedef Wrapper<void *, DoNothing, VoidUnmapViewOfFile> MapViewHolder;
+
+#ifdef WszDeleteFile
+// Deletes a file with the specified path. Do not use if you care about failures
+// deleting the file, as failures are ignored by VoidDeleteFile.
+FORCEINLINE void VoidDeleteFile(LPCWSTR wszFilePath) { WszDeleteFile(wszFilePath); }
+typedef Wrapper<LPCWSTR, DoNothing<LPCWSTR>, VoidDeleteFile, NULL> DeleteFileHolder;
+#endif // WszDeleteFile
+
+
+//-----------------------------------------------------------------------------
+// Misc holders
+//-----------------------------------------------------------------------------
+
+// A holder for HMODULE.
+FORCEINLINE void HolderFreeLibrary(HMODULE h) { FreeLibrary(h); }
+
+typedef Wrapper<HMODULE, DoNothing<HMODULE>, HolderFreeLibrary, NULL> HModuleHolder;
+
+template <typename T> FORCEINLINE
+void DoLocalFree(T* pMem)
+{
+#ifdef HOST_WINDOWS
+ (LocalFree)((void*)pMem);
+#else
+ (free)((void*)pMem);
+#endif
+}
+
+template<typename _TYPE>
+using LocalAllocHolder = SpecializedWrapper<_TYPE, DoLocalFree<_TYPE>>;
+
+inline void BoolSet( _Out_ bool * val ) { *val = true; }
+inline void BoolUnset( _Out_ bool * val ) { *val = false; }
+
+typedef Wrapper< bool *, BoolSet, BoolUnset > BoolFlagStateHolder;
+
+//
+// We need the following methods to have volatile arguments, so that they can accept
+// raw pointers in addition to the results of the & operator on Volatile<T>.
+//
+
+FORCEINLINE void CounterIncrease(RAW_KEYWORD(volatile) LONG* p) {InterlockedIncrement(p);};
+FORCEINLINE void CounterDecrease(RAW_KEYWORD(volatile) LONG* p) {InterlockedDecrement(p);};
+
+typedef Wrapper<RAW_KEYWORD(volatile) LONG*, CounterIncrease, CounterDecrease, (UINT_PTR)0, CompareDefault<RAW_KEYWORD(volatile) LONG*>> CounterHolder;
+
+
+#ifdef HOST_WINDOWS
+FORCEINLINE void RegKeyRelease(HKEY k) {RegCloseKey(k);};
+typedef Wrapper<HKEY,DoNothing,RegKeyRelease> RegKeyHolder;
+#endif // HOST_WINDOWS
+
+class ErrorModeHolder
+{
+ UINT m_oldMode;
+public:
+ ErrorModeHolder(UINT newMode){m_oldMode=SetErrorMode(newMode);};
+ ~ErrorModeHolder(){SetErrorMode(m_oldMode);};
+ UINT OldMode() {return m_oldMode;};
+};
+
+#ifdef HOST_WINDOWS
+//-----------------------------------------------------------------------------
+// HKEYHolder : HKEY holder, Calls RegCloseKey on scope exit.
+//
+// {
+// HKEYHolder hFoo = NULL;
+// WszRegOpenKeyEx(HKEY_CLASSES_ROOT, L"Interface",0, KEY_READ, hFoo);
+//
+// } // close key on out of scope via RegCloseKey.
+//-----------------------------------------------------------------------------
+
+class HKEYHolder
+{
+public:
+ HKEYHolder()
+ {
+ STATIC_CONTRACT_LEAF;
+ m_value = 0;
+ }
+
+ ~HKEYHolder()
+ {
+ STATIC_CONTRACT_WRAPPER;
+ if (m_value != NULL)
+ ::RegCloseKey(m_value);
+ }
+
+ FORCEINLINE void operator=(HKEY p)
+ {
+ STATIC_CONTRACT_LEAF;
+ if (p != 0)
+ m_value = p;
+ }
+
+ FORCEINLINE operator HKEY()
+ {
+ STATIC_CONTRACT_LEAF;
+ return m_value;
+ }
+
+ FORCEINLINE operator HKEY*()
+ {
+ STATIC_CONTRACT_LEAF;
+ return &m_value;
+ }
+
+ FORCEINLINE HKEY* operator&()
+ {
+ STATIC_CONTRACT_LEAF;
+ return &m_value;
+ }
+
+private:
+ HKEY m_value;
+};
+#endif // HOST_WINDOWS
+
+//----------------------------------------------------------------------------
+//
+// External data access does not want certain holder implementations
+// to be active as locks should not be taken and so on. Provide
+// a no-op in that case.
+//
+//----------------------------------------------------------------------------
+
+#ifndef DACCESS_COMPILE
+
+#define DacHolder Holder
+
+#else
+
+template <typename TYPE, void (*ACQUIRE)(TYPE), void (*RELEASEF)(TYPE), UINT_PTR DEFAULTVALUE = 0, BOOL IS_NULL(TYPE, TYPE) = CompareDefault<TYPE>, BOOL VALIDATE_BACKOUT_STACK = TRUE>
+class DacHolder
+{
+ protected:
+ TYPE m_value;
+
+ private:
+ BOOL m_acquired; // Have we acquired the resource?
+
+ public:
+ FORCEINLINE DacHolder()
+ : m_value(TYPE(DEFAULTVALUE)),
+ m_acquired(FALSE)
+ {
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ }
+
+ // construct a new instance of DacHolder
+ // Arguments:
+ // input: value - the resource held
+ // take - indicates whether the lock should be taken--the default is true. See Notes:
+ // Note: In DAC builds, the Acquire function does not actually take the lock, instead
+ // it determines whether the lock is held (by the LS). If it is, the locked data
+ // is assumed to be inconsistent and the Acquire function will throw.
+ FORCEINLINE DacHolder(TYPE value, BOOL take = TRUE)
+ : m_value(value),
+ m_acquired(FALSE)
+ {
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ if (take)
+ Acquire();
+
+ }
+ FORCEINLINE ~DacHolder()
+ {
+ STATIC_CONTRACT_SUPPORTS_DAC;
+ }
+ // Sets the value to 'value'. Doesn't call Acquire/Release if fTake is FALSE.
+ FORCEINLINE void Assign(TYPE value, BOOL fTake = TRUE)
+ {
+ m_value = value;
+ }
+ FORCEINLINE void Acquire()
+ {
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ if (!IsNull())
+ {
+ m_acquired = TRUE;
+ // because ACQUIRE is a template argument, if the line m_acquired = TRUE is placed after the call
+ // where it logically belongs, the compiler flags it as "unreachable code."
+ ACQUIRE(this->m_value);
+ }
+ }
+ FORCEINLINE void Release()
+ {
+ // Insert any global or thread bookeeping here
+
+ if (m_acquired)
+ {
+ m_acquired = FALSE;
+ }
+ }
+ FORCEINLINE void Clear()
+ {
+ m_value = TYPE(DEFAULTVALUE);
+ m_acquired = FALSE;
+ }
+ FORCEINLINE void SuppressRelease()
+ {
+ m_acquired = FALSE;
+ }
+ FORCEINLINE TYPE GetValue()
+ {
+ return m_value;
+ }
+ FORCEINLINE BOOL IsNull() const
+ {
+ return IS_NULL(m_value, TYPE(DEFAULTVALUE));
+ }
+
+ private:
+ FORCEINLINE DacHolder& operator=(const Holder<TYPE, ACQUIRE, RELEASEF> &holder)
+ {
+ }
+
+ FORCEINLINE DacHolder(const Holder<TYPE, ACQUIRE, RELEASEF> &holder)
+ {
+ }
+};
+
+#endif // #ifndef DACCESS_COMPILE
+
+// Holder-specific clr::SafeAddRef and clr::SafeRelease helper functions.
+namespace clr
+{
+ // Copied from utilcode.h. We can't include the header directly because there
+ // is circular reference.
+ // Forward declare the overload which is used by 'SafeAddRef' below.
+ template <typename ItfT>
+ static inline
+ typename std::enable_if< std::is_pointer<ItfT>::value, ItfT >::type
+ SafeAddRef(ItfT pItf);
+
+ template < typename ItfT > __checkReturn
+ ItfT *
+ SafeAddRef(ReleaseHolder<ItfT> & pItf)
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ //@TODO: Would be good to add runtime validation that the return value is used.
+ return SafeAddRef(pItf.GetValue());
+ }
+
+ namespace detail
+ {
+ template <typename T>
+ char IsHolderHelper(HolderBase<T>*);
+ int IsHolderHelper(...);
+
+ template <typename T>
+ struct IsHolder : public std::conditional<
+ sizeof(IsHolderHelper(reinterpret_cast<T*>(NULL))) == sizeof(char),
+ std::true_type,
+ std::false_type>::type
+ {};
+ }
+
+ template < typename T >
+ typename std::enable_if<detail::IsHolder<T>::value, ULONG>::type
+ SafeRelease(T& arg)
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ return arg.Release();
+ }
+}
+
+#endif // __HOLDER_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// Iterator.h
+// ---------------------------------------------------------------------------
+
+// ================================================================================
+// Iterator pattern:
+//
+// This pattern is similar to the STL iterator pattern. It basically consists of
+// wrapping an "iteration variable" in an object, and providing pointer-like operators
+// on the iterator. Example usage:
+//
+// for (Iterator start = foo->Begin(), end = foo->End(); start != end; start++)
+// {
+// // use foo, start
+// }
+//
+// There are 3 levels of iterator functionality
+// 1. Enumerator (STL forward) - only go forward one at a time
+// Enumerators have the following operations:
+// operator * : access current element as ref
+// operator -> : access current element as ptr
+// operator++ : go to the next element
+// operator==, operator!= : compare to another enumerator
+//
+//
+// 2. Scanner (STL bidirectional) - backward or forward one at a time
+// Scanners have all the functionality of enumerators, plus:
+// operator-- : go backward one element
+//
+// 3. Indexer (STL random access) - skip around arbitrarily
+// Indexers have all the functionality of scanners, plus:
+// operator[] : access the element at index from iterator as ref
+// operator+= : advance iterator by index
+// operator+ : return new iterator at index from iterator
+// operator-= : rewind iterator by index
+// operator- : return new iterator at index back from iterator
+// operator <, operator <=, operator >, operator>= :
+// range comparison on two indexers
+//
+// The object being iterated should define the following methods:
+// Begin() return an iterator starting at the first element
+// End() return an iterator just past the last element
+//
+// Iterator types are normally defined as member types named "Iterator", no matter
+// what their functionality level is.
+// ================================================================================
+
+
+#ifndef ITERATOR_H_
+#define ITERATOR_H_
+
+#include "contract.h"
+
+namespace HIDDEN {
+// These prototypes are not for direct use - they are only here to illustrate the
+// iterator pattern
+
+template <typename CONTAINER, typename ELEMENT>
+class ScannerPrototype
+{
+ public:
+
+ typedef ScannerPrototype<CONTAINER, ELEMENT> Iterator;
+
+ ELEMENT &operator*();
+ ELEMENT *operator->();
+
+ Iterator &operator++();
+ Iterator operator++(int);
+ Iterator &operator--();
+ Iterator operator--(int);
+
+ bool operator==(const Iterator &i);
+ bool operator!=(const Iterator &i);
+};
+
+template <typename CONTAINER, typename ELEMENT>
+class EnumeratorPrototype
+{
+ public:
+
+ typedef EnumeratorPrototype<CONTAINER, ELEMENT> Iterator;
+
+ ELEMENT &operator*();
+ ELEMENT *operator->();
+
+ Iterator &operator++();
+ Iterator operator++(int);
+
+ bool operator==(const Iterator &i);
+ bool operator!=(const Iterator &i);
+};
+
+template <typename CONTAINER, typename ELEMENT>
+class IndexerPrototype
+{
+ public:
+ typedef IndexerPrototype<CONTAINER, ELEMENT> Iterator;
+
+ ELEMENT &operator*();
+ ELEMENT *operator->();
+ ELEMENT &operator[](int index);
+
+ Iterator &operator++();
+ Iterator operator++(int);
+ Iterator &operator--();
+ Iterator operator--(int);
+
+ Iterator &operator+=(SCOUNT_T index);
+ Iterator &operator-=(SCOUNT_T index);
+
+ Iterator operator+(SCOUNT_T index);
+ Iterator operator-(SCOUNT_T index);
+
+ SCOUNT_T operator-(Iterator &i);
+
+ bool operator==(const Iterator &i);
+ bool operator!=(const Iterator &i);
+ bool operator<(const Iterator &i);
+ bool operator<=(const Iterator &i);
+ bool operator>(const Iterator &i);
+ bool operator>=(const Iterator &i);
+};
+
+template <typename ELEMENT>
+class EnumerablePrototype
+{
+ typedef EnumeratorPrototype<EnumerablePrototype, ELEMENT> Iterator;
+
+ Iterator Begin();
+ Iterator End();
+};
+
+template <typename ELEMENT>
+class ScannablePrototype
+{
+ typedef ScannerPrototype<ScannablePrototype, ELEMENT> Iterator;
+
+ Iterator Begin();
+ Iterator End();
+};
+
+template <typename ELEMENT>
+class IndexablePrototype
+{
+ typedef IndexerPrototype<IndexablePrototype, ELEMENT> Iterator;
+
+ Iterator Begin();
+ Iterator End();
+};
+
+};
+
+// --------------------------------------------------------------------------------
+// EnumeratorBase, ScannerBase, and IndexerBase are abstract classes
+// describing basic operations for iterator functionality at the different levels.
+//
+// You
+// 1. Use the classes as a pattern (don't derive from them), and plug in your own
+// class into the Enumerator/Scanner/Indexer templates below.
+// 2. Subclass the AbstractEnumerator/AbstractScanner/AbstractIndexer classes
+// --------------------------------------------------------------------------------
+
+namespace HIDDEN
+{
+// These prototypes are not for direct use - they are only here to illustrate the
+// pattern of the BASE class for the Iterator templates
+
+template <typename ELEMENT, typename CONTAINER>
+class EnumeratorBasePrototype
+{
+ protected:
+ EnumeratorBasePrototype(CONTAINER *container, BOOL begin);
+ ELEMENT &Get() const;
+ void Next();
+ BOOL Equal(const EnumeratorBasePrototype &i) const;
+ CHECK DoCheck() const;
+};
+
+template <typename ELEMENT, typename CONTAINER>
+class ScannerBasePrototype
+{
+ protected:
+ ScannerBasePrototype(CONTAINER *container, BOOL begin);
+ ELEMENT &Get() const;
+ void Next();
+ void Previous();
+ BOOL Equal(const ScannerBasePrototype &i) const;
+ CHECK DoCheck() const;
+};
+
+template <typename ELEMENT, typename CONTAINER>
+class IndexerBasePrototype
+{
+ protected:
+ IndexerBasePrototype(CONTAINER *container, SCOUNT_T delta);
+ ELEMENT &GetAt(SCOUNT_T delta) const;
+ void Skip(SCOUNT_T delta);
+ SCOUNT_T Subtract(const IndexerBasePrototype &i) const;
+ CHECK DoCheck(SCOUNT_T delta) const;
+};
+
+};
+
+
+template <typename CONTAINER>
+class CheckedIteratorBase
+{
+ protected:
+#if defined(_DEBUG)
+ const CONTAINER *m_container;
+ int m_revision;
+#endif
+
+ CHECK CheckRevision() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__llvm__))
+ __if_exists(CONTAINER::m_revision)
+ {
+ CHECK_MSG(m_revision == m_container->m_revision,
+ "Use of Iterator after container has been modified");
+ }
+#endif
+ CHECK_OK;
+ }
+
+ CheckedIteratorBase()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+#if defined(_DEBUG)
+ m_container = NULL;
+#endif
+ }
+
+ CheckedIteratorBase(const CONTAINER *container)
+ {
+ LIMITED_METHOD_CONTRACT;
+#if defined(_DEBUG)
+ m_container = container;
+#if defined(_MSC_VER) || defined(__llvm__)
+ __if_exists(CONTAINER::m_revision)
+ {
+ m_revision = m_container->m_revision;
+ }
+#endif
+#endif
+ }
+
+ void Resync(const CONTAINER *container)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__llvm__))
+ __if_exists(CONTAINER::m_revision)
+ {
+ m_revision = m_container->m_revision;
+ }
+#endif
+ }
+
+#if defined(_DEBUG)
+ const CONTAINER *GetContainerDebug() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_container;
+ }
+#endif
+
+ public:
+ CHECK Check() const
+ {
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ CHECK(CheckRevision());
+ CHECK_OK;
+ }
+
+ CHECK CheckContainer(const CONTAINER *container) const
+ {
+ WRAPPER_NO_CONTRACT;
+#if defined(_DEBUG)
+ CHECK(container == m_container);
+#endif
+ CHECK_OK;
+ }
+
+};
+
+
+// --------------------------------------------------------------------------------
+// Enumerator, Scanner, and Indexer provide a template to produce an iterator
+// on an existing class with a single iteration variable.
+//
+// The template takes 3 type parameters:
+// CONTAINER - type of object being interated on
+// ELEMENT - type of iteration
+// BASE - base type of the iteration. This type must follow the pattern described
+// by the above Prototypes
+// --------------------------------------------------------------------------------
+
+template <typename ELEMENT, typename SUBTYPE>
+class Enumerator
+{
+ private:
+ const SUBTYPE *This() const
+ {
+ return (const SUBTYPE *) this;
+ }
+
+ SUBTYPE *This()
+ {
+ return (SUBTYPE *)this;
+ }
+
+ public:
+
+ Enumerator()
+ {
+ }
+
+ CHECK CheckIndex() const
+ {
+#if defined(_DEBUG)
+ CHECK(This()->DoCheck());
+#endif
+ CHECK_OK;
+ }
+
+ ELEMENT &operator*() const
+ {
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(This()->CheckIndex());
+
+ return This()->Get();
+ }
+ ELEMENT *operator->() const
+ {
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(This()->CheckIndex());
+
+ return &(This()->Get());
+ }
+ SUBTYPE &operator++()
+ {
+ PRECONDITION(CheckPointer(This()));
+
+ This()->Next();
+ return *This();
+ }
+ SUBTYPE operator++(int)
+ {
+ PRECONDITION(CheckPointer(This()));
+
+ SUBTYPE i = *This();
+ This()->Next();
+ return i;
+ }
+ bool operator==(const SUBTYPE &i) const
+ {
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+
+ return This()->Equal(i);
+ }
+ bool operator!=(const SUBTYPE &i) const
+ {
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+
+ return !This()->Equal(i);
+ }
+};
+
+template <typename ELEMENT, typename SUBTYPE>
+class Scanner
+{
+ private:
+ const SUBTYPE *This() const
+ {
+ return (const SUBTYPE *)this;
+ }
+
+ SUBTYPE *This()
+ {
+ return (SUBTYPE *)this;
+ }
+
+ public:
+
+ Scanner()
+ {
+ }
+
+ CHECK CheckIndex() const
+ {
+#if defined(_DEBUG)
+ CHECK(This()->DoCheck());
+#endif
+ CHECK_OK;
+ }
+
+ ELEMENT &operator*() const
+ {
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(This()->CheckIndex());
+
+ return This()->Get();
+ }
+ ELEMENT *operator->() const
+ {
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(This()->CheckIndex());
+
+ return &This()->Get();
+ }
+ SUBTYPE &operator++()
+ {
+ PRECONDITION(CheckPointer(This()));
+
+ This()->Next();
+ return *This();
+ }
+ SUBTYPE operator++(int)
+ {
+ PRECONDITION(CheckPointer(This()));
+
+ SUBTYPE i = *This();
+ This()->Next();
+ return i;
+ }
+ SUBTYPE &operator--()
+ {
+ PRECONDITION(CheckPointer(This()));
+
+ This()->Previous();
+ return *This();
+ }
+ SUBTYPE operator--(int)
+ {
+ PRECONDITION(CheckPointer(this));
+
+ SUBTYPE i = *This();
+ This()->Previous();
+ return i;
+ }
+ bool operator==(const SUBTYPE &i) const
+ {
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+
+ return This()->Equal(i);
+ }
+ bool operator!=(const SUBTYPE &i) const
+ {
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+
+ return !This()->Equal(i);
+ }
+};
+
+template <typename ELEMENT, typename SUBTYPE>
+class Indexer
+{
+ private:
+ const SUBTYPE *This() const
+ {
+ return (const SUBTYPE *)this;
+ }
+
+ SUBTYPE *This()
+ {
+ return (SUBTYPE *)this;
+ }
+
+ public:
+
+ Indexer()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ }
+
+ CHECK CheckIndex() const
+ {
+#if defined(_DEBUG)
+ CHECK(This()->DoCheck(0));
+#endif
+ CHECK_OK;
+ }
+
+ CHECK CheckIndex(int index) const
+ {
+#if defined(_DEBUG)
+ CHECK(This()->DoCheck(index));
+#endif
+ CHECK_OK;
+ }
+
+ ELEMENT &operator*() const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(This()->CheckIndex(0));
+
+ return *(ELEMENT*)&This()->GetAt(0);
+ }
+ ELEMENT *operator->() const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(This()->CheckIndex(0));
+
+ return &This()->GetAt(0);
+ }
+ ELEMENT &operator[](int index) const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(This()->CheckIndex(index));
+ return *(ELEMENT*)&This()->GetAt(index);
+ }
+ SUBTYPE &operator++()
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ This()->Skip(1);
+ return *This();
+ }
+ SUBTYPE operator++(int)
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ SUBTYPE i = *This();
+ This()->Skip(1);
+ return i;
+ }
+ SUBTYPE &operator--()
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ This()->Skip(-1);
+ return *This();
+ }
+ SUBTYPE operator--(int)
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ SUBTYPE i = *This();
+ This()->Skip(-1);
+ return i;
+ }
+ SUBTYPE &operator+=(SCOUNT_T index)
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ This()->Skip(index);
+ return *This();
+ }
+ SUBTYPE operator+(SCOUNT_T index) const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ SUBTYPE i = *This();
+ i.Skip(index);
+ return i;
+ }
+ SUBTYPE &operator-=(SCOUNT_T index)
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ This()->Skip(-index);
+ return *This();
+ }
+ SUBTYPE operator-(SCOUNT_T index) const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ SUBTYPE i = *This();
+ i.Skip(-index);
+ return i;
+ }
+ SCOUNT_T operator-(const SUBTYPE &i) const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+
+ return This()->Subtract(i);
+ }
+ bool operator==(const SUBTYPE &i) const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+
+ return This()->Subtract(i) == 0;
+ }
+ bool operator!=(const SUBTYPE &i) const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+
+ return This()->Subtract(i) != 0;
+ }
+ bool operator<(const SUBTYPE &i) const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+ return This()->Subtract(i) < 0;
+ }
+ bool operator<=(const SUBTYPE &i) const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+ return This()->Subtract(i) <= 0;
+ }
+ bool operator>(const SUBTYPE &i) const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+ return This()->Subtract(i) > 0;
+ }
+ bool operator>=(const SUBTYPE &i) const
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(This()));
+ PRECONDITION(i.Check());
+ return This()->Subtract(i) >= 0;
+ }
+};
+
+#endif // ITERATOR_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// MDCommon.h
+//
+// Common header file for both MD and COMPLIB subdirectories
+//
+//*****************************************************************************
+
+#ifndef __MDCommon_h__
+#define __MDCommon_h__
+
+// File types for the database.
+enum FILETYPE
+{
+ FILETYPE_UNKNOWN, // Unknown or undefined type.
+ FILETYPE_CLB, // Native .clb file format.
+ FILETYPE_CLX, // An obsolete file format.
+ FILETYPE_NTPE, // Windows PE executable.
+ FILETYPE_NTOBJ, // .obj file format (with .clb embedded).
+ FILETYPE_TLB // Typelib format.
+};
+
+enum MAPPINGTYPE
+{
+ MTYPE_NOMAPPING, // No mapped file
+ MTYPE_FLAT, // Mapped as a flat file
+ MTYPE_IMAGE // Mapped with the SEC_IMAGE flag
+};
+
+
+#define SCHEMA_STREAM_A "#Schema"
+#define STRING_POOL_STREAM_A "#Strings"
+#define BLOB_POOL_STREAM_A "#Blob"
+#define US_BLOB_POOL_STREAM_A "#US"
+#define GUID_POOL_STREAM_A "#GUID"
+#define COMPRESSED_MODEL_STREAM_A "#~"
+#define ENC_MODEL_STREAM_A "#-"
+#define MINIMAL_MD_STREAM_A "#JTD"
+#define HOT_MODEL_STREAM_A "#!"
+#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB
+#define PDB_STREAM_A "#Pdb"
+#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB
+
+#define SCHEMA_STREAM W("#Schema")
+#define STRING_POOL_STREAM W("#Strings")
+#define BLOB_POOL_STREAM W("#Blob")
+#define US_BLOB_POOL_STREAM W("#US")
+#define GUID_POOL_STREAM W("#GUID")
+#define COMPRESSED_MODEL_STREAM W("#~")
+#define ENC_MODEL_STREAM W("#-")
+#define MINIMAL_MD_STREAM W("#JTD")
+#define HOT_MODEL_STREAM W("#!")
+#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB
+#define PDB_STREAM W("#Pdb")
+#endif // FEATURE_METADATA_EMIT_PORTABLE_PDB
+
+#endif // __MDCommon_h__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// MemoryRange.h
+//
+// defines the code:MemoryRange class.
+//*****************************************************************************
+
+#ifndef _memory_range_h
+#define _memory_range_h
+
+#include "daccess.h"
+
+// MemoryRange is a descriptor of a memory range. This groups (pointer + size).
+//
+// Some key qualities:
+// - simple!
+// - Not mutable
+// - blitabble descriptor which can be useful for out-of-process tools like the debugger.
+// - no ownership semantics.
+// - no manipulation, growing semantics.
+// - no memory marshalling, allocation, copying. etc.
+// - can be efficiently passed / copied / returned by value
+//
+// This class has general value as an abstraction to group pointer and size together. It also has significant
+// value to the debugger. An expected design pattern is that other mutable complex data structures (eg,
+// code:SBuffer, code:CGrowableStream) will provide an accessor to expose their underlying storage as a
+// MemoryRange to debugger. This mirrors the Debugger's code:TargetBuffer data structure, but as a
+// general-purpose VM utility versus a debugger right-side data structure.
+
+//
+class MemoryRange
+{
+public:
+ // Constructor to create a memory range around a (start address, size) pair.
+ MemoryRange() :
+ m_pStartAddress(NULL),
+ m_cbBytes(0)
+ {
+ SUPPORTS_DAC;
+ }
+
+ MemoryRange(PTR_VOID pStartAddress, SIZE_T cbBytes) :
+ m_pStartAddress(pStartAddress),
+ m_cbBytes(cbBytes)
+ {
+ SUPPORTS_DAC;
+ }
+
+ // Note: use compiler-default copy ctor and assignment operator
+
+
+
+ // Check whether a pointer is in the memory range represented by this instance.
+ BOOL IsInRange(PTR_VOID pAddress) const
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (dac_cast<TADDR>(pAddress) - dac_cast<TADDR>(m_pStartAddress)) < m_cbBytes;
+ }
+
+ // Check whether a pointer is in the memory range represented by this instance.
+ BOOL IsInRange(TADDR pAddress) const
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (pAddress - dac_cast<TADDR>(m_pStartAddress)) < m_cbBytes;
+ }
+
+ // Get the starting address.
+ PTR_VOID StartAddress() const
+ {
+ SUPPORTS_DAC;
+ return m_pStartAddress;
+ }
+
+ // Get the size of the range in bytes
+ SIZE_T Size() const
+ {
+ SUPPORTS_DAC;
+ return m_cbBytes;
+ }
+
+private:
+ // The start of the memory range.
+ PTR_VOID const m_pStartAddress;
+
+ // The size of the memory range in bytes.
+ // This is s SIZE_T so that it can describe any memory range in the process (for example, larger than 4gb on 64-bit machines)
+ const SIZE_T m_cbBytes;
+
+};
+
+typedef ArrayDPTR(MemoryRange) ARRAY_PTR_MemoryRange;
+
+#endif // _memory_range_h
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//****************************************************************************
+// File: metadata.h
+//
+
+//
+// Notes:
+// Common includes for EE & metadata internal. This file contains
+// definition of CorMetaDataScope
+//****************************************************************************
+
+#ifndef _METADATA_H_
+#define _METADATA_H_
+
+#include "ex.h"
+
+class CorProfileData;
+class IMetaModelCommon;
+class MDInternalRW;
+class UTSemReadWrite;
+
+inline int IsGlobalMethodParentTk(mdTypeDef td)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (td == mdTypeDefNil || td == mdTokenNil);
+}
+
+typedef enum CorInternalStates
+{
+ tdNoTypes = 0x00000000,
+ tdAllAssemblies = 0x00000001,
+ tdAllTypes = 0xffffffff,
+} CorInternalStates;
+
+//
+// MetaData custom value names.
+//
+enum CorIfaceAttr
+{
+ ifDual = 0, // Interface derives from IDispatch.
+ ifVtable = 1, // Interface derives from IUnknown.
+ ifDispatch = 2, // Interface is a dispinterface.
+ ifInspectable = 3, // Interface derives from IInspectable.
+ ifLast = 4, // The last member of the enum.
+};
+
+inline BOOL IsDispatchBasedItf(CorIfaceAttr ifaceAttr)
+{
+ return (ifaceAttr == ifDual || ifaceAttr == ifDispatch);
+}
+
+enum CorClassIfaceAttr
+{
+ clsIfNone = 0, // No class interface is generated.
+ clsIfAutoDisp = 1, // A dispatch only class interface is generated.
+ clsIfAutoDual = 2, // A dual class interface is generated.
+ clsIfLast = 3, // The last member of the enum.
+};
+
+//
+// The default values for the COM interface and class interface types.
+//
+#define DEFAULT_COM_INTERFACE_TYPE ifDual
+#define DEFAULT_CLASS_INTERFACE_TYPE clsIfAutoDisp
+
+#define HANDLE_UNCOMPRESSED(func) (E_FAIL)
+#define HANDLE_UNCOMPRESSED_BOOL(func) (false)
+
+class TOKENLIST : public CDynArray<mdToken>
+{
+};
+
+
+typedef enum tagEnumType
+{
+ MDSimpleEnum = 0x0, // simple enumerator that doesn't allocate memory
+
+ // You could get this kind of enum if you perform a non-simple query (such as EnumMethodWithName).
+ //
+ MDDynamicArrayEnum = 0x2, // dynamic array that holds tokens
+} EnumType;
+
+//*****************************************
+// Enumerator used by MetaDataInternal
+//*****************************************
+struct HENUMInternal
+{
+ DWORD m_tkKind; // kind of tables that the enum is holding the result
+ uint32_t m_ulCount; // count of total entries holding by the enumerator
+
+ EnumType m_EnumType;
+
+ struct {
+ uint32_t m_ulStart;
+ uint32_t m_ulEnd;
+ uint32_t m_ulCur;
+ } u;
+
+ // m_cursor will go away when we no longer support running EE with uncompressed
+ // format. WHEN WE REMOVE THIS, REMOVE ITS VESTIAGES FROM ZeroEnum as well
+ //
+ union {
+ void* m_alignpad; // The first item is m_cursor[] is a pointer
+ char m_cursor[32]; // cursor holding query result for read/write mode
+ };
+
+ // TOKENLIST daTKList; // dynamic arrays of token list
+ HENUMInternal() : m_EnumType(MDSimpleEnum) { LIMITED_METHOD_DAC_CONTRACT; }
+
+ // in-place initialization
+ static void InitDynamicArrayEnum(
+ HENUMInternal *pEnum); // HENUMInternal to be initialized
+
+ static void InitSimpleEnum(
+ DWORD tkKind, // kind of token that we are iterating
+ ULONG ridStart, // starting rid
+ ULONG ridEnd, // end rid
+ HENUMInternal *pEnum); // HENUMInternal to be initialized
+
+ // Specialized helper which should be better than always calling memset
+ inline
+ static void ZeroEnum(
+ HENUMInternal *pEnum)
+ {
+ // we use this to avoid the memset that will happen otherwise.
+ // this should be inlined in its caller. we are seeing a large
+ // number of calls to memset from MDInternalRO::EnumPermissionSetsInit
+ // on x64 which we can eliminate with this code.
+ pEnum->m_tkKind = 0;
+ pEnum->m_ulCount = 0;
+ pEnum->m_EnumType = MDSimpleEnum;
+ pEnum->u.m_ulStart = 0;
+ pEnum->u.m_ulEnd = 0;
+ pEnum->u.m_ulCur = 0;
+
+ // TODO: remove this when we remove m_cursor from the HENUMInternal structure
+ _ASSERTE(IS_ALIGNED(pEnum->m_cursor, sizeof(DWORD)));
+ _ASSERTE((sizeof(HENUMInternal) - offsetof(HENUMInternal, m_cursor)) == (8 * sizeof(DWORD)));
+
+ DWORD* pBuffer = (DWORD*)pEnum->m_cursor;
+ pBuffer[0] = 0;
+ pBuffer[1] = 0;
+ pBuffer[2] = 0;
+ pBuffer[3] = 0;
+ pBuffer[4] = 0;
+ pBuffer[5] = 0;
+ pBuffer[6] = 0;
+ pBuffer[7] = 0;
+ }
+
+ // This will only clear the content of enum and will not free the memory of enum
+ static void ClearEnum(
+ HENUMInternal *pmdEnum);
+
+ // create a HENUMInternal. This will allocate the memory
+ __checkReturn
+ static HRESULT CreateSimpleEnum(
+ DWORD tkKind, // kind of token that we are iterating
+ ULONG ridStart, // starting rid
+ ULONG ridEnd, // end rid
+ HENUMInternal **ppEnum); // return the created HENUMInternal
+
+ __checkReturn
+ static HRESULT CreateDynamicArrayEnum(
+ DWORD tkKind, // kind of token that we are iterating
+ HENUMInternal **ppEnum); // return the created HENUMInternal
+
+ // Destory Enum. This will free the memory
+ static void DestroyEnum(
+ HENUMInternal *pmdEnum);
+
+ static void DestroyEnumIfEmpty(
+ HENUMInternal **ppEnum); // reset the enumerator pointer to NULL if empty
+
+ __checkReturn
+ static HRESULT EnumWithCount(
+ HENUMInternal *pEnum, // enumerator
+ ULONG cMax, // max tokens that caller wants
+ mdToken rTokens[], // output buffer to fill the tokens
+ ULONG *pcTokens); // number of tokens fill to the buffer upon return
+
+ __checkReturn
+ static HRESULT EnumWithCount(
+ HENUMInternal *pEnum, // enumerator
+ ULONG cMax, // max tokens that caller wants
+ mdToken rTokens1[], // first output buffer to fill the tokens
+ mdToken rTokens2[], // second output buffer to fill the tokens
+ ULONG *pcTokens); // number of tokens fill to the buffer upon return
+
+ __checkReturn
+ static HRESULT AddElementToEnum(
+ HENUMInternal *pEnum, // return the created HENUMInternal
+ mdToken tk); // token to fill
+
+ //*****************************************
+ // Get next value contained in the enumerator
+ //*****************************************
+ static bool EnumNext(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ mdToken *ptk); // [OUT] token to scope the search
+
+ __checkReturn
+ static HRESULT GetCount(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ ULONG *pCount); // ]OUT] the index of the desired item
+
+ __checkReturn
+ static HRESULT GetElement(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ ULONG ix, // ]IN] the index of the desired item
+ mdToken *ptk); // [OUT] token to fill
+
+};
+
+
+
+//*****************************************
+// Default Value for field, param or property. Returned by GetDefaultValue
+//*****************************************
+typedef struct _MDDefaultValue
+{
+#if BIGENDIAN
+ _MDDefaultValue(void)
+ {
+ m_bType = ELEMENT_TYPE_END;
+ }
+ ~_MDDefaultValue(void)
+ {
+ if (m_bType == ELEMENT_TYPE_STRING)
+ {
+ delete[] m_wzValue;
+ }
+ }
+#endif
+
+ // type of default value
+ BYTE m_bType; // CorElementType for the default value
+
+ // the default value
+ union
+ {
+ BOOL m_bValue; // ELEMENT_TYPE_BOOLEAN
+ CHAR m_cValue; // ELEMENT_TYPE_I1
+ BYTE m_byteValue; // ELEMENT_TYPE_UI1
+ SHORT m_sValue; // ELEMENT_TYPE_I2
+ USHORT m_usValue; // ELEMENT_TYPE_UI2
+ LONG m_lValue; // ELEMENT_TYPE_I4
+ ULONG m_ulValue; // ELEMENT_TYPE_UI4
+ LONGLONG m_llValue; // ELEMENT_TYPE_I8
+ ULONGLONG m_ullValue; // ELEMENT_TYPE_UI8
+ FLOAT m_fltValue; // ELEMENT_TYPE_R4
+ DOUBLE m_dblValue; // ELEMENT_TYPE_R8
+ LPCWSTR m_wzValue; // ELEMENT_TYPE_STRING
+ IUnknown *m_unkValue; // ELEMENT_TYPE_CLASS
+ };
+ ULONG m_cbSize; // default value size (for blob)
+
+} MDDefaultValue;
+
+
+
+//*****************************************
+// structure use to in GetAllEventAssociates and GetAllPropertyAssociates
+//*****************************************
+typedef struct
+{
+ mdMethodDef m_memberdef;
+ DWORD m_dwSemantics;
+} ASSOCIATE_RECORD;
+
+
+//
+// structure use to retrieve class layout informaiton
+//
+typedef struct
+{
+ RID m_ridFieldCur; // indexing to the field table
+ RID m_ridFieldEnd; // end index to field table
+} MD_CLASS_LAYOUT;
+
+
+// Structure for describing the Assembly MetaData.
+typedef struct
+{
+ USHORT usMajorVersion; // Major Version.
+ USHORT usMinorVersion; // Minor Version.
+ USHORT usBuildNumber; // Build Number.
+ USHORT usRevisionNumber; // Revision Number.
+ LPCSTR szLocale; // Locale.
+} AssemblyMetaDataInternal;
+
+
+
+// Callback definition for comparing signatures.
+// (*PSIGCOMPARE) (BYTE ScopeSignature[], DWORD ScopeSignatureLength,
+// BYTE ExternalSignature[], DWORD ExternalSignatureLength,
+// void* SignatureData);
+typedef BOOL (*PSIGCOMPARE)(PCCOR_SIGNATURE, DWORD, PCCOR_SIGNATURE, DWORD, void*);
+
+
+// {1B119F60-C507-4024-BB39-F8223FB3E1FD}
+EXTERN_GUID(IID_IMDInternalImport, 0x1b119f60, 0xc507, 0x4024, 0xbb, 0x39, 0xf8, 0x22, 0x3f, 0xb3, 0xe1, 0xfd);
+
+#undef INTERFACE
+#define INTERFACE IMDInternalImport
+DECLARE_INTERFACE_(IMDInternalImport, IUnknown)
+{
+ //*****************************************************************************
+ // return the count of entries of a given kind in a scope
+ // For example, pass in mdtMethodDef will tell you how many MethodDef
+ // contained in a scope
+ //*****************************************************************************
+ STDMETHOD_(ULONG, GetCountWithTokenKind)(// return hresult
+ DWORD tkKind) PURE; // [IN] pass in the kind of token.
+
+ //*****************************************************************************
+ // enumerator for typedef
+ //*****************************************************************************
+ __checkReturn
+ STDMETHOD(EnumTypeDefInit)( // return hresult
+ HENUMInternal *phEnum) PURE; // [OUT] buffer to fill for enumerator data
+
+ //*****************************************************************************
+ // enumerator for MethodImpl
+ //*****************************************************************************
+ __checkReturn
+ STDMETHOD(EnumMethodImplInit)( // return hresult
+ mdTypeDef td, // [IN] TypeDef over which to scope the enumeration.
+ HENUMInternal *phEnumBody, // [OUT] buffer to fill for enumerator data for MethodBody tokens.
+ HENUMInternal *phEnumDecl) PURE; // [OUT] buffer to fill for enumerator data for MethodDecl tokens.
+
+ ULONG EnumMethodImplGetCount(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) // [IN] MethodDecl enumerator.
+ {
+ return phEnumBody->m_ulCount;
+ }
+
+ STDMETHOD_(void, EnumMethodImplReset)(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) PURE; // [IN] MethodDecl enumerator.
+
+ __checkReturn
+ STDMETHOD(EnumMethodImplNext)( // return hresult (S_OK = TRUE, S_FALSE = FALSE or error code)
+ HENUMInternal *phEnumBody, // [IN] input enum for MethodBody
+ HENUMInternal *phEnumDecl, // [IN] input enum for MethodDecl
+ mdToken *ptkBody, // [OUT] return token for MethodBody
+ mdToken *ptkDecl) PURE; // [OUT] return token for MethodDecl
+
+ STDMETHOD_(void, EnumMethodImplClose)(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) PURE; // [IN] MethodDecl enumerator.
+
+ //*****************************************
+ // Enumerator helpers for memberdef, memberref, interfaceimp,
+ // event, property, exception, param
+ //*****************************************
+
+ __checkReturn
+ STDMETHOD(EnumGlobalFunctionsInit)( // return hresult
+ HENUMInternal *phEnum) PURE; // [OUT] buffer to fill for enumerator data
+
+ __checkReturn
+ STDMETHOD(EnumGlobalFieldsInit)( // return hresult
+ HENUMInternal *phEnum) PURE; // [OUT] buffer to fill for enumerator data
+
+ __checkReturn
+ STDMETHOD(EnumInit)( // return S_FALSE if record not found
+ DWORD tkKind, // [IN] which table to work on
+ mdToken tkParent, // [IN] token to scope the search
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ __checkReturn
+ STDMETHOD(EnumAllInit)( // return S_FALSE if record not found
+ DWORD tkKind, // [IN] which table to work on
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ bool EnumNext(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ mdToken *ptk) // [OUT] token to scope the search
+ {
+ _ASSERTE(phEnum && ptk);
+ if (phEnum->u.m_ulCur >= phEnum->u.m_ulEnd)
+ return false;
+
+ if ( phEnum->m_EnumType == MDSimpleEnum )
+ {
+ *ptk = phEnum->u.m_ulCur | phEnum->m_tkKind;
+ phEnum->u.m_ulCur++;
+ }
+ else
+ {
+ TOKENLIST *pdalist = (TOKENLIST *)&(phEnum->m_cursor);
+
+ _ASSERTE( phEnum->m_EnumType == MDDynamicArrayEnum );
+ *ptk = *( pdalist->Get(phEnum->u.m_ulCur++) );
+ }
+ return true;
+ }
+
+ ULONG EnumGetCount(
+ HENUMInternal *phEnum) // [IN] the enumerator to retrieve information
+ {
+ _ASSERTE(phEnum);
+ return phEnum->m_ulCount;
+ }
+
+ void EnumReset(
+ HENUMInternal *phEnum) // [IN] the enumerator to be reset
+ {
+ _ASSERTE(phEnum);
+ _ASSERTE( phEnum->m_EnumType == MDSimpleEnum || phEnum->m_EnumType == MDDynamicArrayEnum);
+
+ phEnum->u.m_ulCur = phEnum->u.m_ulStart;
+ } // MDInternalRW::EnumReset
+
+ void EnumClose(
+ HENUMInternal *phEnum) // [IN] the enumerator to be closed
+ {
+ _ASSERTE( phEnum->m_EnumType == MDSimpleEnum ||
+ phEnum->m_EnumType == MDDynamicArrayEnum);
+ if (phEnum->m_EnumType == MDDynamicArrayEnum)
+ HENUMInternal::ClearEnum(phEnum);
+ }
+
+ //*****************************************
+ // Enumerator helpers for CustomAttribute
+ //*****************************************
+ __checkReturn
+ STDMETHOD(EnumCustomAttributeByNameInit)(// return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ LPCSTR szName, // [IN] CustomAttribute's name to scope the search
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ //*****************************************
+ // Nagivator helper to navigate back to the parent token given a token.
+ // For example, given a memberdef token, it will return the containing typedef.
+ //
+ // the mapping is as following:
+ // ---given child type---------parent type
+ // mdMethodDef mdTypeDef
+ // mdFieldDef mdTypeDef
+ // mdInterfaceImpl mdTypeDef
+ // mdParam mdMethodDef
+ // mdProperty mdTypeDef
+ // mdEvent mdTypeDef
+ //
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetParentToken)(
+ mdToken tkChild, // [IN] given child token
+ mdToken *ptkParent) PURE; // [OUT] returning parent
+
+ //*****************************************
+ // Custom value helpers
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetCustomAttributeProps)( // S_OK or error.
+ mdCustomAttribute at, // [IN] The attribute.
+ mdToken *ptkType) PURE; // [OUT] Put attribute type here.
+
+ __checkReturn
+ STDMETHOD(GetCustomAttributeAsBlob)(
+ mdCustomAttribute cv, // [IN] given custom value token
+ void const **ppBlob, // [OUT] return the pointer to internal blob
+ ULONG *pcbSize) PURE; // [OUT] return the size of the blob
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD (GetScopeProps)(
+ LPCSTR *pszName, // [OUT] scope name
+ GUID *pmvid) PURE; // [OUT] version id
+
+ // finding a particular method
+ __checkReturn
+ STDMETHOD(FindMethodDef)(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmd) PURE; // [OUT] matching memberdef
+
+ // return a iSeq's param given a MethodDef
+ __checkReturn
+ STDMETHOD(FindParamOfMethod)( // S_OK or error.
+ mdMethodDef md, // [IN] The owning method of the param.
+ ULONG iSeq, // [IN] The sequence # of the param.
+ mdParamDef *pparamdef) PURE; // [OUT] Put ParamDef token here.
+
+ //*****************************************
+ //
+ // GetName* functions
+ //
+ //*****************************************
+
+ // return the name and namespace of typedef
+ __checkReturn
+ STDMETHOD(GetNameOfTypeDef)(
+ mdTypeDef classdef, // given classdef
+ LPCSTR *pszname, // return class name(unqualified)
+ LPCSTR *psznamespace) PURE; // return the name space name
+
+ __checkReturn
+ STDMETHOD(GetIsDualOfTypeDef)(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pDual) PURE; // [OUT] return dual flag here.
+
+ __checkReturn
+ STDMETHOD(GetIfaceTypeOfTypeDef)(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pIface) PURE; // [OUT] 0=dual, 1=vtable, 2=dispinterface
+
+ // get the name of either methoddef
+ __checkReturn
+ STDMETHOD(GetNameOfMethodDef)( // return the name of the memberdef in UTF8
+ mdMethodDef md, // given memberdef
+ LPCSTR *pszName) PURE;
+
+ __checkReturn
+ STDMETHOD(GetNameAndSigOfMethodDef)(
+ mdMethodDef methoddef, // [IN] given memberdef
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of CLR signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszName) PURE;
+
+ // return the name of a FieldDef
+ __checkReturn
+ STDMETHOD(GetNameOfFieldDef)(
+ mdFieldDef fd, // given memberdef
+ LPCSTR *pszName) PURE;
+
+ // return the name of typeref
+ __checkReturn
+ STDMETHOD(GetNameOfTypeRef)(
+ mdTypeRef classref, // [IN] given typeref
+ LPCSTR *psznamespace, // [OUT] return typeref name
+ LPCSTR *pszname) PURE; // [OUT] return typeref namespace
+
+ // return the resolutionscope of typeref
+ __checkReturn
+ STDMETHOD(GetResolutionScopeOfTypeRef)(
+ mdTypeRef classref, // given classref
+ mdToken *ptkResolutionScope) PURE;
+
+ // Find the type token given the name.
+ __checkReturn
+ STDMETHOD(FindTypeRefByName)(
+ LPCSTR szNamespace, // [IN] Namespace for the TypeRef.
+ LPCSTR szName, // [IN] Name of the TypeRef.
+ mdToken tkResolutionScope, // [IN] Resolution Scope fo the TypeRef.
+ mdTypeRef *ptk) PURE; // [OUT] TypeRef token returned.
+
+ // return the TypeDef properties
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetTypeDefProps)(
+ mdTypeDef classdef, // given classdef
+ DWORD *pdwAttr, // return flags on class, tdPublic, tdAbstract
+ mdToken *ptkExtends) PURE; // [OUT] Put base class TypeDef/TypeRef here
+
+ // return the item's guid
+ __checkReturn
+ STDMETHOD(GetItemGuid)(
+ mdToken tkObj, // [IN] given item.
+ CLSID *pGuid) PURE; // [out[ put guid here.
+
+ // Get enclosing class of the NestedClass.
+ __checkReturn
+ STDMETHOD(GetNestedClassProps)( // S_OK or error
+ mdTypeDef tkNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptkEnclosingClass) PURE; // [OUT] EnclosingClass token.
+
+ // Get count of Nested classes given the enclosing class.
+ __checkReturn
+ STDMETHOD(GetCountNestedClasses)( // return count of Nested classes.
+ mdTypeDef tkEnclosingClass, // Enclosing class.
+ ULONG *pcNestedClassesCount) PURE;
+
+ // Return array of Nested classes given the enclosing class.
+ __checkReturn
+ STDMETHOD(GetNestedClasses)( // Return actual count.
+ mdTypeDef tkEnclosingClass, // [IN] Enclosing class.
+ mdTypeDef *rNestedClasses, // [OUT] Array of nested class tokens.
+ ULONG ulNestedClasses, // [IN] Size of array.
+ ULONG *pcNestedClasses) PURE;
+
+ // return the ModuleRef properties
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetModuleRefProps)(
+ mdModuleRef mur, // [IN] moduleref token
+ LPCSTR *pszName) PURE; // [OUT] buffer to fill with the moduleref name
+
+ //*****************************************
+ //
+ // GetSig* functions
+ //
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetSigOfMethodDef)(
+ mdMethodDef tkMethodDef, // [IN] given MethodDef
+ ULONG * pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE * ppSig) PURE;
+
+ __checkReturn
+ STDMETHOD(GetSigOfFieldDef)(
+ mdFieldDef tkFieldDef, // [IN] given FieldDef
+ ULONG * pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE * ppSig) PURE;
+
+ __checkReturn
+ STDMETHOD(GetSigFromToken)(
+ mdToken tk, // FieldDef, MethodDef, Signature or TypeSpec token
+ ULONG * pcbSig,
+ PCCOR_SIGNATURE * ppSig) PURE;
+
+
+
+ //*****************************************
+ // get method property
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetMethodDefProps)(
+ mdMethodDef md, // The method for which to get props.
+ DWORD *pdwFlags) PURE;
+
+ //*****************************************
+ // return method implementation informaiton, like RVA and implflags
+ //*****************************************
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetMethodImplProps)(
+ mdToken tk, // [IN] MethodDef
+ ULONG *pulCodeRVA, // [OUT] CodeRVA
+ DWORD *pdwImplFlags) PURE; // [OUT] Impl. Flags
+
+ //*****************************************
+ // return method implementation informaiton, like RVA and implflags
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetFieldRVA)(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulCodeRVA) PURE; // [OUT] CodeRVA
+
+ //*****************************************
+ // get field property
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetFieldDefProps)(
+ mdFieldDef fd, // [IN] given fielddef
+ DWORD *pdwFlags) PURE; // [OUT] return fdPublic, fdPrive, etc flags
+
+ //*****************************************************************************
+ // return default value of a token(could be paramdef, fielddef, or property
+ //*****************************************************************************
+ __checkReturn
+ STDMETHOD(GetDefaultValue)(
+ mdToken tk, // [IN] given FieldDef, ParamDef, or Property
+ MDDefaultValue *pDefaultValue) PURE;// [OUT] default value to fill
+
+
+ //*****************************************
+ // get dispid of a MethodDef or a FieldDef
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetDispIdOfMemberDef)( // return hresult
+ mdToken tk, // [IN] given methoddef or fielddef
+ ULONG *pDispid) PURE; // [OUT] Put the dispid here.
+
+ //*****************************************
+ // return TypeRef/TypeDef given an InterfaceImpl token
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetTypeOfInterfaceImpl)( // return the TypeRef/typedef token for the interfaceimpl
+ mdInterfaceImpl iiImpl, // given a interfaceimpl
+ mdToken *ptkType) PURE;
+
+ //*****************************************
+ // look up function for TypeDef
+ //*****************************************
+ __checkReturn
+ STDMETHOD(FindTypeDef)(
+ LPCSTR szNamespace, // [IN] Namespace for the TypeDef.
+ LPCSTR szName, // [IN] Name of the TypeDef.
+ mdToken tkEnclosingClass, // [IN] TypeRef/TypeDef Token for the enclosing class.
+ mdTypeDef *ptypedef) PURE; // [IN] return typedef
+
+ //*****************************************
+ // return name and sig of a memberref
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetNameAndSigOfMemberRef)( // return name here
+ mdMemberRef memberref, // given memberref
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of CLR signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszName) PURE;
+
+ //*****************************************************************************
+ // Given memberref, return the parent. It can be TypeRef, ModuleRef, MethodDef
+ //*****************************************************************************
+ __checkReturn
+ STDMETHOD(GetParentOfMemberRef)(
+ mdMemberRef memberref, // given memberref
+ mdToken *ptkParent) PURE; // return the parent token
+
+ __checkReturn
+ STDMETHOD(GetParamDefProps)(
+ mdParamDef paramdef, // given a paramdef
+ USHORT *pusSequence, // [OUT] slot number for this parameter
+ DWORD *pdwAttr, // [OUT] flags
+ LPCSTR *pszName) PURE; // [OUT] return the name of the parameter
+
+ __checkReturn
+ STDMETHOD(GetPropertyInfoForMethodDef)( // Result.
+ mdMethodDef md, // [IN] memberdef
+ mdProperty *ppd, // [OUT] put property token here
+ LPCSTR *pName, // [OUT] put pointer to name here
+ ULONG *pSemantic) PURE; // [OUT] put semantic here
+
+ //*****************************************
+ // class layout/sequence information
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetClassPackSize)( // return error if class doesn't have packsize
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwPackSize) PURE; // [OUT] 1, 2, 4, 8, or 16
+
+ __checkReturn
+ STDMETHOD(GetClassTotalSize)( // return error if class doesn't have total size info
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwClassSize) PURE; // [OUT] return the total size of the class
+
+ __checkReturn
+ STDMETHOD(GetClassLayoutInit)(
+ mdTypeDef td, // [IN] give typedef
+ MD_CLASS_LAYOUT *pLayout) PURE; // [OUT] set up the status of query here
+
+ __checkReturn
+ STDMETHOD(GetClassLayoutNext)(
+ MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here
+ mdFieldDef *pfd, // [OUT] return the fielddef
+ ULONG *pulOffset) PURE; // [OUT] return the offset/ulSequence associate with it
+
+ //*****************************************
+ // marshal information of a field
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetFieldMarshal)( // return error if no native type associate with the token
+ mdFieldDef fd, // [IN] given fielddef
+ PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature
+ ULONG *pcbNativeType) PURE; // [OUT] the count of bytes of *ppvNativeType
+
+
+ //*****************************************
+ // property APIs
+ //*****************************************
+ // find a property by name
+ __checkReturn
+ STDMETHOD(FindProperty)(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szPropName, // [IN] property name
+ mdProperty *pProp) PURE; // [OUT] return property token
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetPropertyProps)(
+ mdProperty prop, // [IN] property token
+ LPCSTR *szProperty, // [OUT] property name
+ DWORD *pdwPropFlags, // [OUT] property flags.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob
+ ULONG *pcbSig) PURE; // [OUT] count of bytes in *ppvSig
+
+ //**********************************
+ // Event APIs
+ //**********************************
+ __checkReturn
+ STDMETHOD(FindEvent)(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szEventName, // [IN] event name
+ mdEvent *pEvent) PURE; // [OUT] return event token
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetEventProps)(
+ mdEvent ev, // [IN] event token
+ LPCSTR *pszEvent, // [OUT] Event name
+ DWORD *pdwEventFlags, // [OUT] Event flags.
+ mdToken *ptkEventType) PURE; // [OUT] EventType class
+
+
+ //**********************************
+ // find a particular associate of a property or an event
+ //**********************************
+ __checkReturn
+ STDMETHOD(FindAssociate)(
+ mdToken evprop, // [IN] given a property or event token
+ DWORD associate, // [IN] given a associate semantics(setter, getter, testdefault, reset, AddOn, RemoveOn, Fire)
+ mdMethodDef *pmd) PURE; // [OUT] return method def token
+
+ // Note, void function in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(EnumAssociateInit)(
+ mdToken evprop, // [IN] given a property or an event token
+ HENUMInternal *phEnum) PURE; // [OUT] cursor to hold the query result
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetAllAssociates)(
+ HENUMInternal *phEnum, // [IN] query result form GetPropertyAssociateCounts
+ ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output
+ ULONG cAssociateRec) PURE; // [IN] size of the buffer
+
+
+ //**********************************
+ // Get info about a PermissionSet.
+ //**********************************
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetPermissionSetProps)(
+ mdPermission pm, // [IN] the permission token.
+ DWORD *pdwAction, // [OUT] CorDeclSecurity.
+ void const **ppvPermission, // [OUT] permission blob.
+ ULONG *pcbPermission) PURE; // [OUT] count of bytes of pvPermission.
+
+ //****************************************
+ // Get the String given the String token.
+ // Returns a pointer to the string, or NULL in case of error.
+ //****************************************
+ __checkReturn
+ STDMETHOD(GetUserString)(
+ mdString stk, // [IN] the string token.
+ ULONG *pchString, // [OUT] count of characters in the string.
+ BOOL *pbIs80Plus, // [OUT] specifies where there are extended characters >= 0x80.
+ LPCWSTR *pwszUserString) PURE;
+
+ //*****************************************************************************
+ // p-invoke APIs.
+ //*****************************************************************************
+ __checkReturn
+ STDMETHOD(GetPinvokeMap)(
+ mdToken tk, // [IN] FieldDef, MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ LPCSTR *pszImportName, // [OUT] Import name.
+ mdModuleRef *pmrImportDLL) PURE; // [OUT] ModuleRef token for the target DLL.
+
+ //*****************************************************************************
+ // helpers to convert a text signature to a com format
+ //*****************************************************************************
+ __checkReturn
+ STDMETHOD(ConvertTextSigToComSig)( // Return hresult.
+ BOOL fCreateTrIfNotFound, // [IN] create typeref if not found
+ LPCSTR pSignature, // [IN] class file format signature
+ CQuickBytes *pqbNewSig, // [OUT] place holder for CLR signature
+ ULONG *pcbCount) PURE; // [OUT] the result size of signature
+
+ //*****************************************************************************
+ // Assembly MetaData APIs.
+ //*****************************************************************************
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetAssemblyProps)(
+ mdAssembly mda, // [IN] The Assembly for which to get the properties.
+ const void **ppbPublicKey, // [OUT] Pointer to the public key.
+ ULONG *pcbPublicKey, // [OUT] Count of bytes in the public key.
+ ULONG *pulHashAlgId, // [OUT] Hash Algorithm.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData.
+ DWORD *pdwAssemblyFlags) PURE;// [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetAssemblyRefProps)(
+ mdAssemblyRef mdar, // [IN] The AssemblyRef for which to get the properties.
+ const void **ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token.
+ ULONG *pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData.
+ const void **ppbHashValue, // [OUT] Hash blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob.
+ DWORD *pdwAssemblyRefFlags) PURE; // [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetFileProps)(
+ mdFile mdf, // [IN] The File for which to get the properties.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ const void **ppbHashValue, // [OUT] Pointer to the Hash Value Blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the Hash Value Blob.
+ DWORD *pdwFileFlags) PURE; // [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetExportedTypeProps)(
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ LPCSTR *pszNamespace, // [OUT] Namespace.
+ LPCSTR *pszName, // [OUT] Name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file.
+ DWORD *pdwExportedTypeFlags) PURE; // [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ __checkReturn
+ STDMETHOD(GetManifestResourceProps)(
+ mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file.
+ DWORD *pdwResourceFlags) PURE;// [OUT] Flags.
+
+ __checkReturn
+ STDMETHOD(FindExportedTypeByName)( // S_OK or error
+ LPCSTR szNamespace, // [IN] Namespace of the ExportedType.
+ LPCSTR szName, // [IN] Name of the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] ExportedType for the enclosing class.
+ mdExportedType *pmct) PURE; // [OUT] Put ExportedType token here.
+
+ __checkReturn
+ STDMETHOD(FindManifestResourceByName)( // S_OK or error
+ LPCSTR szName, // [IN] Name of the ManifestResource.
+ mdManifestResource *pmmr) PURE; // [OUT] Put ManifestResource token here.
+
+ __checkReturn
+ STDMETHOD(GetAssemblyFromScope)( // S_OK or error
+ mdAssembly *ptkAssembly) PURE; // [OUT] Put token here.
+
+ __checkReturn
+ STDMETHOD(GetCustomAttributeByName)( // S_OK or error
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) PURE; // [OUT] Put size of data here.
+
+ // Note: The return type of this method was void in v1
+ __checkReturn
+ STDMETHOD(GetTypeSpecFromToken)( // S_OK or error.
+ mdTypeSpec typespec, // [IN] Signature token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token.
+ ULONG *pcbSig) PURE; // [OUT] return size of signature.
+
+ __checkReturn
+ STDMETHOD(SetUserContextData)( // S_OK or E_NOTIMPL
+ IUnknown *pIUnk) PURE; // The user context.
+
+ __checkReturn
+ STDMETHOD_(BOOL, IsValidToken)( // True or False.
+ mdToken tk) PURE; // [IN] Given token.
+
+ __checkReturn
+ STDMETHOD(TranslateSigWithScope)(
+ IMDInternalImport *pAssemImport, // [IN] import assembly scope.
+ const void *pbHashValue, // [IN] hash value for the import assembly.
+ ULONG cbHashValue, // [IN] count of bytes in the hash value.
+ PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope
+ ULONG cbSigBlob, // [IN] count of bytes of signature
+ IMetaDataAssemblyEmit *pAssemEmit, // [IN] assembly emit scope.
+ IMetaDataEmit *emit, // [IN] emit interface
+ CQuickBytes *pqkSigEmit, // [OUT] buffer to hold translated signature
+ ULONG *pcbSig) PURE; // [OUT] count of bytes in the translated signature
+
+ STDMETHOD_(IMetaModelCommon*, GetMetaModelCommon)( // Return MetaModelCommon interface.
+ ) PURE;
+
+ STDMETHOD_(IUnknown *, GetCachedPublicInterface)(BOOL fWithLock) PURE; // return the cached public interface
+ __checkReturn
+ STDMETHOD(SetCachedPublicInterface)(IUnknown *pUnk) PURE; // no return value
+ STDMETHOD_(UTSemReadWrite*, GetReaderWriterLock)() PURE; // return the reader writer lock
+ __checkReturn
+ STDMETHOD(SetReaderWriterLock)(UTSemReadWrite * pSem) PURE;
+
+ STDMETHOD_(mdModule, GetModuleFromScope)() PURE; // [OUT] Put mdModule token here.
+
+
+ //-----------------------------------------------------------------
+ // Additional custom methods
+
+ // finding a particular method
+ __checkReturn
+ STDMETHOD(FindMethodDefUsingCompare)(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ PSIGCOMPARE pSignatureCompare, // [IN] Routine to compare signatures
+ void* pSignatureArgs, // [IN] Additional info to supply the compare function
+ mdMethodDef *pmd) PURE; // [OUT] matching memberdef
+
+ // Additional v2 methods.
+
+ //*****************************************
+ // return a field offset for a given field
+ //*****************************************
+ __checkReturn
+ STDMETHOD(GetFieldOffset)(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulOffset) PURE; // [OUT] FieldOffset
+
+ __checkReturn
+ STDMETHOD(GetMethodSpecProps)(
+ mdMethodSpec ms, // [IN] The method instantiation
+ mdToken *tkParent, // [OUT] MethodDef or MemberRef
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob) PURE; // [OUT] actual size of signature blob
+
+ __checkReturn
+ STDMETHOD(GetTableInfoWithIndex)(
+ ULONG index, // [IN] pass in the table index
+ void **pTable, // [OUT] pointer to table at index
+ void **pTableSize) PURE; // [OUT] size of table at index
+
+ __checkReturn
+ STDMETHOD(ApplyEditAndContinue)(
+ void *pDeltaMD, // [IN] the delta metadata
+ ULONG cbDeltaMD, // [IN] length of pData
+ IMDInternalImport **ppv) PURE; // [OUT] the resulting metadata interface
+
+ //**********************************
+ // Generics APIs
+ //**********************************
+ __checkReturn
+ STDMETHOD(GetGenericParamProps)( // S_OK or error.
+ mdGenericParam rd, // [IN] The type parameter
+ ULONG* pulSequence, // [OUT] Parameter sequence number
+ DWORD* pdwAttr, // [OUT] Type parameter flags (for future use)
+ mdToken *ptOwner, // [OUT] The owner (TypeDef or MethodDef)
+ DWORD *reserved, // [OUT] The kind (TypeDef/Ref/Spec, for future use)
+ LPCSTR *szName) PURE; // [OUT] The name
+
+ __checkReturn
+ STDMETHOD(GetGenericParamConstraintProps)( // S_OK or error.
+ mdGenericParamConstraint rd, // [IN] The constraint token
+ mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained
+ mdToken *ptkConstraintType) PURE; // [OUT] TypeDef/Ref/Spec constraint
+
+ //*****************************************************************************
+ // This function gets the "built for" version of a metadata scope.
+ // NOTE: if the scope has never been saved, it will not have a built-for
+ // version, and an empty string will be returned.
+ //*****************************************************************************
+ __checkReturn
+ STDMETHOD(GetVersionString)( // S_OK or error.
+ LPCSTR *pVer) PURE; // [OUT] Put version string here.
+
+
+ __checkReturn
+ STDMETHOD(GetTypeDefRefTokenInTypeSpec)(// return S_FALSE if enclosing type does not have a token
+ mdTypeSpec tkTypeSpec, // [IN] TypeSpec token to look at
+ mdToken *tkEnclosedToken) PURE; // [OUT] The enclosed type token
+
+#define MD_STREAM_VER_1X 0x10000
+#define MD_STREAM_VER_2_B1 0x10001
+#define MD_STREAM_VER_2 0x20000
+ STDMETHOD_(DWORD, GetMetadataStreamVersion)() PURE; //returns DWORD with major version of
+ // MD stream in senior word and minor version--in junior word
+
+ __checkReturn
+ STDMETHOD(GetNameOfCustomAttribute)(// S_OK or error
+ mdCustomAttribute mdAttribute, // [IN] The Custom Attribute
+ LPCUTF8 *pszNamespace, // [OUT] Namespace of Custom Attribute.
+ LPCUTF8 *pszName) PURE; // [OUT] Name of Custom Attribute.
+
+ STDMETHOD(SetOptimizeAccessForSpeed)(// S_OK or error
+ BOOL fOptSpeed) PURE;
+
+ STDMETHOD(SetVerifiedByTrustedSource)(// S_OK or error
+ BOOL fVerified) PURE;
+
+ STDMETHOD(GetRvaOffsetData)(
+ DWORD *pFirstMethodRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in MethodDef table.
+ DWORD *pMethodDefRecordSize, // [OUT] Size of each record in MethodDef table.
+ DWORD *pMethodDefCount, // [OUT] Number of records in MethodDef table.
+ DWORD *pFirstFieldRvaOffset, // [OUT] Offset (from start of metadata) to the first RVA field in FieldRVA table.
+ DWORD *pFieldRvaRecordSize, // [OUT] Size of each record in FieldRVA table.
+ DWORD *pFieldRvaCount // [OUT] Number of records in FieldRVA table.
+ ) PURE;
+
+ //----------------------------------------------------------------------------------------
+ // !!! READ THIS !!!
+ //
+ // New methods have to be added at the end. The order and signatures of the existing methods
+ // have to be preserved. We need to maintain a backward compatibility for this interface to
+ // allow ildasm to work on SingleCLR.
+ //
+ //----------------------------------------------------------------------------------------
+
+}; // IMDInternalImport
+
+
+// {E03D7730-D7E3-11d2-8C0D-00C04FF7431A}
+EXTERN_GUID(IID_IMDInternalImportENC, 0xe03d7730, 0xd7e3, 0x11d2, 0x8c, 0xd, 0x0, 0xc0, 0x4f, 0xf7, 0x43, 0x1a);
+
+#undef INTERFACE
+#define INTERFACE IMDInternalImportENC
+DECLARE_INTERFACE_(IMDInternalImportENC, IMDInternalImport)
+{
+private:
+ using IMDInternalImport::ApplyEditAndContinue;
+public:
+ // ENC only methods here.
+ STDMETHOD(ApplyEditAndContinue)( // S_OK or error.
+ MDInternalRW *pDelta) PURE; // Interface to MD with the ENC delta.
+
+ STDMETHOD(EnumDeltaTokensInit)( // return hresult
+ HENUMInternal *phEnum) PURE; // [OUT] buffer to fill for enumerator data
+
+}; // IMDInternalImportENC
+
+// {F102C526-38CB-49ed-9B5F-498816AE36E0}
+EXTERN_GUID(IID_IMDInternalEmit, 0xf102c526, 0x38cb, 0x49ed, 0x9b, 0x5f, 0x49, 0x88, 0x16, 0xae, 0x36, 0xe0);
+
+#undef INTERFACE
+#define INTERFACE IMDInternalEmit
+DECLARE_INTERFACE_(IMDInternalEmit, IUnknown)
+{
+ STDMETHOD(ChangeMvid)( // S_OK or error.
+ REFGUID newMvid) PURE; // GUID to use as the MVID
+
+ STDMETHOD(SetMDUpdateMode)(
+ ULONG updateMode, ULONG *pPreviousUpdateMode) PURE;
+
+}; // IMDInternalEmit
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+
+struct IMDCustomDataSource;
+class CMiniMdSchema;
+struct CMiniTableDef;
+namespace MetaData
+{
+ class DataBlob;
+}
+
+// {CC0C8F7A-A00B-493D-80B6-CE0C92491670}
+EXTERN_GUID(IID_IMDCustomDataSource, 0xcc0c8f7a, 0xa00b, 0x493d, 0x80, 0xb6, 0xce, 0xc, 0x92, 0x49, 0x16, 0x70);
+
+#undef INTERFACE
+#define INTERFACE IMDCustomDataSource
+DECLARE_INTERFACE_(IMDCustomDataSource, IUnknown)
+{
+ STDMETHOD(GetSchema)(CMiniMdSchema* pSchema) PURE;
+ STDMETHOD(GetTableDef)(ULONG32 tableIndex, CMiniTableDef* pTableDef) PURE;
+ STDMETHOD(GetBlobHeap)(MetaData::DataBlob* pBlobHeapData) PURE;
+ STDMETHOD(GetGuidHeap)(MetaData::DataBlob* pGuidHeapData) PURE;
+ STDMETHOD(GetStringHeap)(MetaData::DataBlob* pStringHeapData) PURE;
+ STDMETHOD(GetUserStringHeap)(MetaData::DataBlob* pUserStringHeapData) PURE;
+ STDMETHOD(GetTableRecords)(ULONG32 tableIndex, MetaData::DataBlob* pTableRecordData) PURE;
+ STDMETHOD(GetTableSortable)(ULONG32 tableIndex, BOOL* pSortable) PURE;
+ STDMETHOD(GetStorageSignature)(MetaData::DataBlob* pStorageSignature) PURE;
+
+}; // IMDCustomDataSource
+
+// {503F79FB-7AAE-4364-BDA6-8E235D173AEC}
+EXTERN_GUID(IID_IMetaDataDispenserCustom,
+ 0x503f79fb, 0x7aae, 0x4364, 0xbd, 0xa6, 0x8e, 0x23, 0x5d, 0x17, 0x3a, 0xec);
+
+#undef INTERFACE
+#define INTERFACE IMetaDataDispenserCustom
+DECLARE_INTERFACE_(IMetaDataDispenserCustom, IUnknown)
+{
+ STDMETHOD(OpenScopeOnCustomDataSource)( // Return code.
+ IMDCustomDataSource *pCustomSource, // [in] The scope to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) PURE; // [out] Return interface on success.
+
+}; // IMetaDataDispenserCustom
+
+#endif // FEATURE_METADATA_CUSTOM_DATA_SOURCE
+
+#ifdef FEATURE_METADATA_DEBUGGEE_DATA_SOURCE
+struct ICorDebugDataTarget;
+HRESULT CreateRemoteMDInternalRWSource(TADDR mdInternalRWRemoteAddress, ICorDebugDataTarget* pDataTarget, DWORD defines, DWORD dataStructureVersion, IMDCustomDataSource** ppDataSource);
+#endif
+
+enum MetaDataReorderingOptions {
+ NoReordering=0x0,
+ ReArrangeStringPool=0x1
+};
+
+#ifdef __HOLDER_H_
+
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr);
+
+// This wrapper class ensures that the HENUMInternal is EnumClose'd no matter how the scope is exited.
+class HENUMTypeDefInternalHolder
+{
+public:
+ FORCEINLINE HENUMTypeDefInternalHolder(IMDInternalImport *pInternalImport)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ m_pInternalImport = pInternalImport;
+ m_fAcquired = FALSE;
+ }
+
+ FORCEINLINE VOID EnumTypeDefInit()
+ {
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ _ASSERTE(!m_fAcquired);
+ HRESULT hr = m_pInternalImport->EnumTypeDefInit(&m_hEnum);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ m_fAcquired = TRUE;
+
+ }
+
+ FORCEINLINE HRESULT EnumTypeDefInitNoThrow()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(!m_fAcquired);
+ HRESULT hr = m_pInternalImport->EnumTypeDefInit(&m_hEnum);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ m_fAcquired = TRUE;
+
+ return hr;
+ }
+
+ FORCEINLINE ~HENUMTypeDefInternalHolder()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ if (m_fAcquired)
+ {
+ m_pInternalImport->EnumClose(&m_hEnum);
+ }
+ }
+
+ FORCEINLINE HENUMInternal* operator& ()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(m_fAcquired);
+ return &m_hEnum;
+ }
+
+private:
+ FORCEINLINE HENUMTypeDefInternalHolder(const HENUMTypeDefInternalHolder &)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(!"Don't try to assign this class.");
+ }
+
+private:
+ IMDInternalImport *m_pInternalImport;
+ HENUMInternal m_hEnum;
+ BOOL m_fAcquired;
+};
+
+
+// This wrapper class ensures that the HENUMInternal is EnumClose'd no matter how the scope is exited.
+class HENUMInternalHolder
+{
+public:
+ FORCEINLINE HENUMInternalHolder(IMDInternalImport *pInternalImport)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ m_pInternalImport = pInternalImport;
+ m_fAcquired = FALSE;
+ }
+
+ FORCEINLINE VOID EnumGlobalFunctionsInit()
+ {
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ _ASSERTE(!m_fAcquired);
+ HRESULT hr = m_pInternalImport->EnumGlobalFunctionsInit(&m_hEnum);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ m_fAcquired = TRUE;
+
+ }
+
+
+ FORCEINLINE VOID EnumGlobalFieldsInit()
+ {
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ _ASSERTE(!m_fAcquired);
+ HRESULT hr = m_pInternalImport->EnumGlobalFieldsInit(&m_hEnum);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ m_fAcquired = TRUE;
+
+ }
+
+ FORCEINLINE VOID EnumTypeDefInit()
+ {
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ _ASSERTE(!m_fAcquired);
+ HRESULT hr = m_pInternalImport->EnumTypeDefInit(&m_hEnum);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ m_fAcquired = TRUE;
+ }
+
+ FORCEINLINE VOID EnumAssociateInit(mdToken tkParent) // [IN] token to scope the search
+ {
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ _ASSERTE(!m_fAcquired);
+ IfFailThrow(m_pInternalImport->EnumAssociateInit(tkParent, &m_hEnum));
+ m_fAcquired = TRUE;
+ }
+
+ FORCEINLINE VOID EnumInit(DWORD tkKind, // [IN] which table to work on
+ mdToken tkParent // [IN] token to scope the search
+ )
+ {
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ HRESULT hr = EnumInitNoThrow(tkKind, tkParent);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ }
+
+ __checkReturn
+ FORCEINLINE HRESULT EnumInitNoThrow(DWORD tkKind, // [IN] which table to work on
+ mdToken tkParent // [IN] token to scope the search
+ )
+ {
+ CONTRACTL {
+ NOTHROW;
+ } CONTRACTL_END;
+
+ _ASSERTE(!m_fAcquired);
+ HRESULT hr = m_pInternalImport->EnumInit(tkKind, tkParent, &m_hEnum);
+ if (SUCCEEDED(hr))
+ {
+ m_fAcquired = TRUE;
+ }
+ return hr;
+ }
+
+ FORCEINLINE VOID EnumAllInit(DWORD tkKind // [IN] which table to work on
+ )
+ {
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ _ASSERTE(!m_fAcquired);
+ HRESULT hr = m_pInternalImport->EnumAllInit(tkKind, &m_hEnum);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ m_fAcquired = TRUE;
+
+ }
+
+ FORCEINLINE ULONG EnumGetCount()
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CONSISTENCY_CHECK(m_fAcquired);
+ } CONTRACTL_END;
+
+ return m_pInternalImport->EnumGetCount(&m_hEnum);
+ }
+
+ FORCEINLINE bool EnumNext(mdToken * pTok)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CONSISTENCY_CHECK(m_fAcquired);
+ } CONTRACTL_END;
+
+ return m_pInternalImport->EnumNext(&m_hEnum, pTok);
+ }
+
+ FORCEINLINE void EnumReset()
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CONSISTENCY_CHECK(m_fAcquired);
+ } CONTRACTL_END;
+
+ return m_pInternalImport->EnumReset(&m_hEnum);
+ }
+
+ FORCEINLINE ~HENUMInternalHolder()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ if (m_fAcquired)
+ {
+ // Ignore the error
+ (void)m_pInternalImport->EnumClose(&m_hEnum);
+ }
+ }
+
+ FORCEINLINE HENUMInternal* operator& ()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(m_fAcquired);
+ return &m_hEnum;
+ }
+
+private:
+ FORCEINLINE HENUMInternalHolder(const HENUMInternalHolder &)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(!"Don't try to assign this class.");
+ }
+
+
+protected:
+ IMDInternalImport *m_pInternalImport;
+ HENUMInternal m_hEnum;
+ BOOL m_fAcquired;
+};
+
+class HENUMInternalMethodImplHolder : protected HENUMInternalHolder
+{
+ public:
+ FORCEINLINE HENUMInternalMethodImplHolder(IMDInternalImport *pInternalImport)
+ : HENUMInternalHolder(pInternalImport)
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+
+ FORCEINLINE ~HENUMInternalMethodImplHolder()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ if (m_fAcquired)
+ {
+ // Ignore the error
+ (void)m_pInternalImport->EnumClose(&m_hEnum2);
+ }
+ }
+
+ FORCEINLINE void EnumMethodImplInit(mdToken tkParent // [IN] token to scope the search
+ )
+ {
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ HRESULT hr = EnumMethodImplInitNoThrow(tkParent);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ }
+
+ __checkReturn
+ FORCEINLINE HRESULT EnumMethodImplInitNoThrow(mdToken tkParent // [IN] token to scope the search
+ )
+ {
+ CONTRACTL {
+ NOTHROW;
+ } CONTRACTL_END;
+
+ _ASSERTE(!m_fAcquired);
+ HRESULT hr = m_pInternalImport->EnumMethodImplInit(tkParent, &m_hEnum, &m_hEnum2);
+ if (SUCCEEDED(hr))
+ {
+ m_fAcquired = TRUE;
+ }
+ return hr;
+ }
+
+ __checkReturn
+ FORCEINLINE HRESULT EnumMethodImplNext(
+ mdToken *ptkImpl,
+ mdToken *ptkDecl)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CONSISTENCY_CHECK(m_fAcquired);
+ } CONTRACTL_END;
+
+ return m_pInternalImport->EnumMethodImplNext(&m_hEnum, &m_hEnum2, ptkImpl, ptkDecl);
+ }
+
+ FORCEINLINE ULONG EnumMethodImplGetCount()
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CONSISTENCY_CHECK(m_fAcquired);
+ } CONTRACTL_END;
+
+ return m_pInternalImport->EnumMethodImplGetCount(&m_hEnum, &m_hEnum2);
+ }
+
+ protected:
+ HENUMInternal m_hEnum2;
+};
+
+#endif //__HOLDER_H_
+
+#endif // _METADATA_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef NIBBLEMAPMACROS_H_
+#define NIBBLEMAPMACROS_H_
+
+///////////////////////////////////////////////////////////////////////
+//// some mmgr stuff for JIT, especially for jit code blocks
+///////////////////////////////////////////////////////////////////////
+//
+// In order to quickly find the start of a jit code block
+// we keep track of all those positions via a map.
+// Each entry in this map represents 32 byte (a bucket) of the code heap.
+// We make the assumption that no two code-blocks can start in
+// the same 32byte bucket;
+// Additionally we assume that every code header is DWORD aligned.
+// Because we cannot guarantee that jitblocks always start at
+// multiples of 32 bytes we cannot use a simple bitmap; instead we
+// use a nibble (4 bit) per bucket and encode the offset of the header
+// inside the bucket (in DWORDS). In order to make initialization
+// easier we add one to the real offset, a nibble-value of zero
+// means that there is no header start in the resp. bucket.
+// In order to speed up "backwards scanning" we start numbering
+// nibbles inside a DWORD from the highest bits (28..31). Because
+// of that we can scan backwards inside the DWORD with right shifts.
+
+#if defined(HOST_64BIT)
+// TODO: bump up the windows CODE_ALIGN to 16 and iron out any nibble map bugs that exist.
+// TODO: there is something wrong with USE_INDIRECT_CODEHEADER with CODE_ALIGN=16
+# define CODE_ALIGN 4
+# define LOG2_CODE_ALIGN 2
+#else
+# define CODE_ALIGN sizeof(DWORD) // 4 byte boundry
+# define LOG2_CODE_ALIGN 2
+#endif
+#define NIBBLE_MASK 0xf
+#define NIBBLE_SIZE 4 // 4 bits
+#define LOG2_NIBBLE_SIZE 2
+#define NIBBLES_PER_DWORD ((8*sizeof(DWORD)) >> LOG2_NIBBLE_SIZE) // 8 (4-bit) nibbles per dword
+#define NIBBLES_PER_DWORD_MASK (NIBBLES_PER_DWORD - 1) // 7
+#define LOG2_NIBBLES_PER_DWORD 3
+#define BYTES_PER_BUCKET (NIBBLES_PER_DWORD * CODE_ALIGN) // 32 bytes per bucket
+#define LOG2_BYTES_PER_BUCKET (LOG2_CODE_ALIGN + LOG2_NIBBLES_PER_DWORD) // 5 bits per bucket
+#define MASK_BYTES_PER_BUCKET (BYTES_PER_BUCKET - 1) // 31
+#define HIGHEST_NIBBLE_BIT (32 - NIBBLE_SIZE) // 28 (i.e 32 - 4)
+#define HIGHEST_NIBBLE_MASK (NIBBLE_MASK << HIGHEST_NIBBLE_BIT) // 0xf0000000
+
+#define ADDR2POS(x) ((x) >> LOG2_BYTES_PER_BUCKET)
+#define ADDR2OFFS(x) (DWORD) ((((x) & MASK_BYTES_PER_BUCKET) >> LOG2_CODE_ALIGN) + 1)
+#define POSOFF2ADDR(pos, of) (size_t) (((pos) << LOG2_BYTES_PER_BUCKET) + (((of) - 1) << LOG2_CODE_ALIGN))
+#define HEAP2MAPSIZE(x) (((x) / (BYTES_PER_BUCKET * NIBBLES_PER_DWORD)) * CODE_ALIGN)
+#define POS2SHIFTCOUNT(x) (DWORD) (HIGHEST_NIBBLE_BIT - (((x) & NIBBLES_PER_DWORD_MASK) << LOG2_NIBBLE_SIZE))
+#define POS2MASK(x) (DWORD) ~(HIGHEST_NIBBLE_MASK >> (((x) & NIBBLES_PER_DWORD_MASK) << LOG2_NIBBLE_SIZE))
+
+#endif // NIBBLEMAPMACROS_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// NSUtilPriv.h
+//
+// Helpers for converting namespace separators.
+//
+//*****************************************************************************
+
+#ifndef __NSUTILPRIV_H__
+#define __NSUTILPRIV_H__
+
+template <class T> class CQuickArray;
+class SString;
+
+struct ns
+{
+
+//*****************************************************************************
+// 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.
+//*****************************************************************************
+static
+int GetFullLength( // Number of chars in full name.
+ const WCHAR *szNameSpace, // Namspace for value.
+ const WCHAR *szName); // Name of value.
+
+static
+int GetFullLength( // Number of chars in full name.
+ LPCUTF8 szNameSpace, // Namspace for value.
+ LPCUTF8 szName); // Name of value.
+
+//*****************************************************************************
+// Scan the given string to see if the name contains any invalid characters
+// that are not allowed.
+//*****************************************************************************
+static
+int IsValidName( // true if valid, false invalid.
+ const WCHAR *szName); // Name to parse.
+
+static
+int IsValidName( // true if valid, false invalid.
+ LPCUTF8 szName); // Name to parse.
+
+
+//*****************************************************************************
+// 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.
+//*****************************************************************************
+static
+WCHAR *FindSep( // Pointer to separator or null.
+ const WCHAR *szPath); // The path to look in.
+
+static
+LPUTF8 FindSep( // Pointer to separator or null.
+ LPCUTF8 szPath); // The path to look in.
+
+
+//*****************************************************************************
+// 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".
+//*****************************************************************************
+static
+WCHAR *SplitInline( // Pointer to name portion.
+ __inout __inout_z WCHAR *szPath); // The path to split.
+
+static
+LPUTF8 SplitInline( // Pointer to name portion.
+ __inout __inout_z LPUTF8 szPath); // The path to split.
+
+static
+void SplitInline(
+ __inout __inout_z LPWSTR szPath, // Path to split.
+ LPCWSTR &szNameSpace, // Return pointer to namespace.
+ LPCWSTR &szName); // Return pointer to name.
+
+static
+void SplitInline(
+ __inout __inout_z LPUTF8 szPath, // Path to split.
+ LPCUTF8 &szNameSpace, // Return pointer to namespace.
+ LPCUTF8 &szName); // Return pointer to name.
+
+
+//*****************************************************************************
+// Split the last parsable element from the end of the string as the name,
+// the first part as the namespace.
+//*****************************************************************************
+static
+int SplitPath( // true ok, false trunction.
+ const WCHAR *szPath, // Path to split.
+ _Out_writes_opt_ (cchNameSpace) WCHAR *szNameSpace, // Output for namespace value.
+ int cchNameSpace, // Max chars for output.
+ _Out_writes_opt_ (cchName) WCHAR *szName, // Output for name.
+ int cchName); // Max chars for output.
+
+static
+int SplitPath( // true ok, false trunction.
+ LPCUTF8 szPath, // Path to split.
+ _Out_writes_opt_ (cchNameSpace) LPUTF8 szNameSpace, // Output for namespace value.
+ int cchNameSpace, // Max chars for output.
+ _Out_writes_opt_ (cchName) LPUTF8 szName, // Output for name.
+ int cchName); // Max chars for output.
+
+
+//*****************************************************************************
+// Take two values and put them together in a fully qualified path using the
+// correct separator.
+//*****************************************************************************
+static
+int MakePath( // true ok, false truncation.
+ _Out_writes_(cchChars) WCHAR *szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ const WCHAR *szNameSpace, // Namespace.
+ const WCHAR *szName); // Name.
+
+static
+int MakePath( // true ok, false truncation.
+ _Out_writes_(cchChars) LPUTF8 szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ LPCUTF8 szNameSpace, // Namespace.
+ LPCUTF8 szName); // Name.
+
+static
+int MakePath( // true ok, false truncation.
+ _Out_writes_(cchChars) WCHAR *szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ LPCUTF8 szNameSpace, // Namespace.
+ LPCUTF8 szName); // Name.
+
+static
+int 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
+int 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
+int 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
+void 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.
+
+//*****************************************************************************
+// Concatinate type names to assembly names
+//*****************************************************************************
+static
+bool MakeAssemblyQualifiedName( // true if ok, false if out of memory
+ CQuickBytes &qb, // location to put result
+ const WCHAR *szTypeName, // Type name
+ const WCHAR *szAssemblyName); // Assembly Name
+
+static
+bool MakeAssemblyQualifiedName( // true ok, false truncation
+ _Out_writes_ (dwBuffer) WCHAR* pBuffer, // Buffer to receive 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
+int 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
+int MakeNestedTypeName( // true ok, false truncation.
+ _Out_writes_ (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
+void 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
+}; // struct ns
+
+#ifndef NAMESPACE_SEPARATOR_CHAR
+#define NAMESPACE_SEPARATOR_CHAR '.'
+#define NAMESPACE_SEPARATOR_WCHAR W('.')
+#define NAMESPACE_SEPARATOR_STR "."
+#define NAMESPACE_SEPARATOR_WSTR W(".")
+#define NAMESPACE_SEPARATOR_LEN 1
+#define ASSEMBLY_SEPARATOR_CHAR ','
+#define ASSEMBLY_SEPARATOR_WCHAR W(',')
+#define ASSEMBLY_SEPARATOR_STR ", "
+#define ASSEMBLY_SEPARATOR_WSTR W(", ")
+#define ASSEMBLY_SEPARATOR_LEN 2
+#define BACKSLASH_CHAR '\\'
+#define BACKSLASH_WCHAR W('\\')
+#define NESTED_SEPARATOR_CHAR '+'
+#define NESTED_SEPARATOR_WCHAR W('+')
+#define NESTED_SEPARATOR_STR "+"
+#define NESTED_SEPARATOR_WSTR W("+")
+#endif
+
+#define EMPTY_STR ""
+#define EMPTY_WSTR W("")
+
+#define MAKE_FULL_PATH_ON_STACK_UTF8(toptr, pnamespace, pname) \
+{ \
+ int __i##toptr = ns::GetFullLength(pnamespace, pname); \
+ toptr = (char *) alloca(__i##toptr); \
+ ns::MakePath(toptr, __i##toptr, pnamespace, pname); \
+}
+
+#define MAKE_FULL_PATH_ON_STACK_UNICODE(toptr, pnamespace, pname) \
+{ \
+ int __i##toptr = ns::GetFullLength(pnamespace, pname); \
+ toptr = (WCHAR *) alloca(__i##toptr * sizeof(WCHAR)); \
+ ns::MakePath(toptr, __i##toptr, pnamespace, pname); \
+}
+
+#define MAKE_FULLY_QUALIFIED_NAME(pszFullyQualifiedName, pszNameSpace, pszName) MAKE_FULL_PATH_ON_STACK_UTF8(pszFullyQualifiedName, pszNameSpace, pszName)
+
+#define MAKE_FULLY_QUALIFIED_MEMBER_NAME(ptr, pszNameSpace, pszClassName, pszMemberName, pszSig) \
+{ \
+ int __i##ptr = ns::GetFullLength(pszNameSpace, pszClassName); \
+ __i##ptr += (pszMemberName ? (int) strlen(pszMemberName) : 0); \
+ __i##ptr += (NAMESPACE_SEPARATOR_LEN * 2); \
+ __i##ptr += (pszSig ? (int) strlen(pszSig) : 0); \
+ ptr = (LPUTF8) alloca(__i##ptr); \
+ ns::MakePath(ptr, __i##ptr, pszNameSpace, pszClassName); \
+ if (pszMemberName) { \
+ strcat_s(ptr, __i##ptr, NAMESPACE_SEPARATOR_STR); \
+ strcat_s(ptr, __i##ptr, pszMemberName); \
+ } \
+ if (pszSig) { \
+ if (! pszMemberName) \
+ strcat_s(ptr, __i##ptr, NAMESPACE_SEPARATOR_STR); \
+ strcat_s(ptr, __i##ptr, pszSig); \
+ } \
+}
+
+#ifdef _PREFAST_
+// need to eliminate the expansion of MAKE_FULLY_QUALIFIED_MEMBER_NAME in prefast
+// builds to prevent it complaining about the potential for NULLs to strlen and strcat
+#undef MAKE_FULLY_QUALIFIED_MEMBER_NAME
+// need to set ptr=NULL so we don't get a build error because ptr isn't inited in a couple cases
+#define MAKE_FULLY_QUALIFIED_MEMBER_NAME(ptr, pszNameSpace, pszClassName, pszMemberName, pszSig) ptr=NULL;
+#endif
+
+#endif
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#include "staticcontract.h"
+
+#ifndef WRAPPER_NO_CONTRACT
+#define WRAPPER_NO_CONTRACT ANNOTATION_WRAPPER
+#endif
+
+#ifndef LIMITED_METHOD_CONTRACT
+#define LIMITED_METHOD_CONTRACT ANNOTATION_FN_LEAF
+#endif
+
+//*****************************************************************************
+// Enum to track which version of the OS we are running
+// Note that NT5 (Win2k) is the minimum supported platform. Any code using
+// utilcode (which includes the CLR's execution engine) will fail to start
+// on a pre-Win2k platform. This is enforced by InitRunningOnVersionStatus.
+//
+// Note: The value is used for data mining from links clicked by user in shim dialog - see code:FWLinkTemplateFromTextID
+// Please do not modify existing values, adding new ones is fine.
+//*****************************************************************************
+typedef enum {
+ RUNNING_ON_STATUS_UNINITED = 0,
+ RUNNING_ON_WIN7 = 1,
+ RUNNING_ON_WIN8 = 2
+} RunningOnStatusEnum;
+
+extern RunningOnStatusEnum gRunningOnStatus;
+
+void InitRunningOnVersionStatus();
+
+#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM)
+typedef enum
+{
+ WINRT_STATUS_UNINITED = 0,
+ WINRT_STATUS_UNSUPPORTED,
+ WINRT_STATUS_SUPPORTED
+}
+WinRTStatusEnum;
+
+extern WinRTStatusEnum gWinRTStatus;
+
+void InitWinRTStatus();
+#endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM
+
+//*****************************************************************************
+// Returns true if you are running on Windows 8 or newer.
+//*****************************************************************************
+inline BOOL RunningOnWin8()
+{
+ WRAPPER_NO_CONTRACT;
+#if (!defined(HOST_X86) && !defined(HOST_AMD64))
+ return TRUE;
+#else
+ if (gRunningOnStatus == RUNNING_ON_STATUS_UNINITED)
+ {
+ InitRunningOnVersionStatus();
+ }
+
+ return (gRunningOnStatus >= RUNNING_ON_WIN8) ? TRUE : FALSE;
+#endif
+}
+
+#ifdef FEATURE_COMINTEROP
+
+#ifdef FEATURE_CORESYSTEM
+
+inline BOOL WinRTSupported()
+{
+ return RunningOnWin8();
+}
+#else
+inline BOOL WinRTSupported()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+
+ if (gWinRTStatus == WINRT_STATUS_UNINITED)
+ {
+ InitWinRTStatus();
+ }
+
+ return gWinRTStatus == WINRT_STATUS_SUPPORTED;
+}
+#endif // FEATURE_CORESYSTEM
+
+#endif // FEATURE_COMINTEROP
+
+#ifdef HOST_64BIT
+inline BOOL RunningInWow64()
+{
+ return FALSE;
+}
+#else
+BOOL RunningInWow64();
+#endif
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// --------------------------------------------------------------------------------
+// PEDecoder.h
+//
+
+// --------------------------------------------------------------------------------
+
+// --------------------------------------------------------------------------------
+// PEDecoder - Utility class for reading and verifying PE files.
+//
+// Note that the Check step is optional if you are willing to trust the
+// integrity of the image.
+// (Or at any rate can be factored into an initial verification step.)
+//
+// Functions which access the memory of the PE file take a "flat" flag - this
+// indicates whether the PE images data has been loaded flat the way it resides in the file,
+// or if the sections have been mapped into memory at the proper base addresses.
+//
+// Finally, some functions take an optional "size" argument, which can be used for
+// range verification. This is an optional parameter, but if you omit it be sure
+// you verify the size in some other way.
+// --------------------------------------------------------------------------------
+
+
+#ifndef PEDECODER_H_
+#define PEDECODER_H_
+
+// --------------------------------------------------------------------------------
+// Required headers
+// --------------------------------------------------------------------------------
+
+#include "windows.h"
+#include "clrtypes.h"
+#include "check.h"
+#include "contract.h"
+#include "cor.h"
+#include "corhdr.h"
+
+#include "readytorun.h"
+typedef DPTR(struct READYTORUN_CORE_HEADER) PTR_READYTORUN_CORE_HEADER;
+typedef DPTR(struct READYTORUN_HEADER) PTR_READYTORUN_HEADER;
+typedef DPTR(struct READYTORUN_SECTION) PTR_READYTORUN_SECTION;
+
+typedef DPTR(IMAGE_COR20_HEADER) PTR_IMAGE_COR20_HEADER;
+
+// --------------------------------------------------------------------------------
+// Forward declared types
+// --------------------------------------------------------------------------------
+
+class Module;
+
+// --------------------------------------------------------------------------------
+// RVA definition
+// --------------------------------------------------------------------------------
+
+// Needs to be DWORD to avoid conflict with <imagehlp.h>
+typedef DWORD RVA;
+
+#ifdef _MSC_VER
+// Wrapper to suppress ambigous overload problems with MSVC.
+inline CHECK CheckOverflow(RVA value1, COUNT_T value2)
+{
+ WRAPPER_NO_CONTRACT;
+ CHECK(CheckOverflow((UINT32) value1, (UINT32) value2));
+ CHECK_OK;
+}
+#endif // _MSC_VER
+
+// --------------------------------------------------------------------------------
+// IMAGE_FILE_MACHINE_NATIVE
+// --------------------------------------------------------------------------------
+
+#if defined(TARGET_X86)
+#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_I386
+#elif defined(TARGET_AMD64)
+#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_AMD64
+#elif defined(TARGET_ARM)
+#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_ARMNT
+#elif defined(TARGET_ARM64)
+#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_ARM64
+#elif defined(TARGET_S390X)
+#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_UNKNOWN
+#else
+#error "port me"
+#endif
+
+// Machine code for native images
+#if defined(__APPLE__)
+#define IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE 0x4644
+#elif defined(__FreeBSD__)
+#define IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE 0xADC4
+#elif defined(__linux__)
+#define IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE 0x7B79
+#elif defined(__NetBSD__)
+#define IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE 0x1993
+#elif defined(__sun)
+#define IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE 0x1992
+#else
+#define IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE 0
+#endif
+
+#define IMAGE_FILE_MACHINE_NATIVE_NI (IMAGE_FILE_MACHINE_NATIVE ^ IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE)
+
+// --------------------------------------------------------------------------------
+// Types
+// --------------------------------------------------------------------------------
+
+typedef DPTR(class PEDecoder) PTR_PEDecoder;
+
+typedef bool (*PEDecoder_ResourceTypesCallbackFunction)(LPCWSTR lpType, void* context);
+typedef bool (*PEDecoder_ResourceNamesCallbackFunction)(LPCWSTR lpName, LPCWSTR lpType, void* context);
+typedef bool (*PEDecoder_ResourceCallbackFunction)(LPCWSTR lpName, LPCWSTR lpType, DWORD langid, BYTE* data, COUNT_T cbData, void* context);
+
+class PEDecoder
+{
+ public:
+
+ // ------------------------------------------------------------
+ // Public API
+ // ------------------------------------------------------------
+
+ // Access functions are divided into 3 categories:
+ // Has - check if the element is present
+ // Check - Do consistency checks on the element (requires Has).
+ // This step is optional if you are willing to trust the integrity of the
+ // image. (It is asserted in a checked build.)
+ // Get - Access the element (requires Has and Check)
+
+ PEDecoder();
+ PEDecoder(void *flatBase, COUNT_T size); // flatBase is the raw disk layout data (using MapViewOfFile)
+ PEDecoder(PTR_VOID mappedBase, bool relocated = FALSE); // mappedBase is the mapped/expanded file (using LoadLibrary)
+
+ void Init(void *flatBase, COUNT_T size);
+ HRESULT Init(void *mappedBase, bool relocated = FALSE);
+ void Reset(); //make sure you don't have a thread race
+
+ PTR_VOID GetBase() const; // Currently loaded base, as opposed to GetPreferredBase()
+ BOOL IsMapped() const;
+ BOOL IsRelocated() const;
+ BOOL IsFlat() const;
+ BOOL HasContents() const;
+ COUNT_T GetSize() const; // size of file on disk, as opposed to GetVirtualSize()
+
+ // High level image checks:
+
+ CHECK CheckFormat() const; // Check whatever is present
+ CHECK CheckNTFormat() const; // Check a PE file image
+ CHECK CheckCORFormat() const; // Check a COR image (IL or native)
+ CHECK CheckILFormat() const; // Check a managed image
+ CHECK CheckILOnlyFormat() const; // Check an IL only image
+
+ // NT header access
+
+ BOOL HasNTHeaders() const;
+ CHECK CheckNTHeaders() const;
+
+ IMAGE_NT_HEADERS32 *GetNTHeaders32() const;
+ IMAGE_NT_HEADERS64 *GetNTHeaders64() const;
+ BOOL Has32BitNTHeaders() const;
+
+ const void *GetHeaders(COUNT_T *pSize = NULL) const;
+
+ BOOL IsDll() const;
+ BOOL HasBaseRelocations() const;
+ const void *GetPreferredBase() const; // OptionalHeaders.ImageBase
+ COUNT_T GetVirtualSize() const; // OptionalHeaders.SizeOfImage - size of mapped/expanded image in memory
+ WORD GetSubsystem() const;
+ WORD GetDllCharacteristics() const;
+ DWORD GetTimeDateStamp() const;
+ DWORD GetCheckSum() const;
+ WORD GetMachine() const;
+ WORD GetCharacteristics() const;
+ DWORD GetFileAlignment() const;
+ DWORD GetSectionAlignment() const;
+ SIZE_T GetSizeOfStackReserve() const;
+ SIZE_T GetSizeOfStackCommit() const;
+ SIZE_T GetSizeOfHeapReserve() const;
+ SIZE_T GetSizeOfHeapCommit() const;
+ UINT32 GetLoaderFlags() const;
+ UINT32 GetWin32VersionValue() const;
+ COUNT_T GetNumberOfRvaAndSizes() const;
+ COUNT_T GetNumberOfSections() const;
+ PTR_IMAGE_SECTION_HEADER FindFirstSection() const;
+ IMAGE_SECTION_HEADER *FindSection(LPCSTR sectionName) const;
+
+ DWORD GetImageIdentity() const;
+
+ BOOL HasWriteableSections() const;
+
+ // Directory entry access
+
+ BOOL HasDirectoryEntry(int entry) const;
+ CHECK CheckDirectoryEntry(int entry, int forbiddenFlags = 0, IsNullOK ok = NULL_NOT_OK) const;
+ IMAGE_DATA_DIRECTORY *GetDirectoryEntry(int entry) const;
+ TADDR GetDirectoryEntryData(int entry, COUNT_T *pSize = NULL) const;
+
+ // IMAGE_DATA_DIRECTORY access
+
+ CHECK CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFlags = 0, IsNullOK ok = NULL_NOT_OK) const;
+ TADDR GetDirectoryData(IMAGE_DATA_DIRECTORY *pDir) const;
+ TADDR GetDirectoryData(IMAGE_DATA_DIRECTORY *pDir, COUNT_T *pSize) const;
+
+ // Basic RVA access
+
+ CHECK CheckRva(RVA rva, IsNullOK ok = NULL_NOT_OK) const;
+ CHECK CheckRva(RVA rva, COUNT_T size, int forbiddenFlags=0, IsNullOK ok = NULL_NOT_OK) const;
+ TADDR GetRvaData(RVA rva, IsNullOK ok = NULL_NOT_OK) const;
+ // Called with ok=NULL_OK only for mapped fields (RVA statics)
+
+ CHECK CheckData(const void *data, IsNullOK ok = NULL_NOT_OK) const;
+ CHECK CheckData(const void *data, COUNT_T size, IsNullOK ok = NULL_NOT_OK) const;
+ RVA GetDataRva(const TADDR data) const;
+ BOOL PointerInPE(PTR_CVOID data) const;
+
+ // Flat mapping utilities - using PointerToRawData instead of (Relative)VirtualAddress
+ CHECK CheckOffset(COUNT_T fileOffset, IsNullOK ok = NULL_NOT_OK) const;
+ CHECK CheckOffset(COUNT_T fileOffset, COUNT_T size, IsNullOK ok = NULL_NOT_OK) const;
+ TADDR GetOffsetData(COUNT_T fileOffset, IsNullOK ok = NULL_NOT_OK) const;
+ // Called with ok=NULL_OK only for mapped fields (RVA statics)
+
+ // Mapping between RVA and file offsets
+ COUNT_T RvaToOffset(RVA rva) const;
+ RVA OffsetToRva(COUNT_T fileOffset) const;
+
+ // Base intra-image pointer access
+ // (These are for pointers retrieved out of the PE image)
+
+ CHECK CheckInternalAddress(SIZE_T address, IsNullOK ok = NULL_NOT_OK) const;
+ CHECK CheckInternalAddress(SIZE_T address, COUNT_T size, IsNullOK ok = NULL_NOT_OK) const;
+ TADDR GetInternalAddressData(SIZE_T address) const;
+
+ // CLR loader IL Image verification - these checks apply to IL_ONLY images.
+
+ BOOL IsILOnly() const;
+ CHECK CheckILOnly() const;
+
+ // Strong name & hashing support
+
+ BOOL HasStrongNameSignature() const;
+ CHECK CheckStrongNameSignature() const;
+ PTR_CVOID GetStrongNameSignature(COUNT_T *pSize = NULL) const;
+
+ // CorHeader flag support
+
+ // IsStrongNameSigned indicates whether the signature has been filled in.
+ // (otherwise if it has a signature it is delay signed.)
+ BOOL IsStrongNameSigned() const; // TRUE if the COMIMAGE_FLAGS_STRONGNAMESIGNED flag is set
+
+ // TLS
+
+ BOOL HasTls() const;
+ CHECK CheckTls() const;
+ PTR_VOID GetTlsRange(COUNT_T *pSize = NULL) const;
+ UINT32 GetTlsIndex() const;
+
+ // Win32 resources
+ void *GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSize = NULL) const;
+ bool EnumerateWin32ResourceTypes(PEDecoder_ResourceTypesCallbackFunction callback, void* context) const;
+ bool EnumerateWin32ResourceNames(LPCWSTR lpType, PEDecoder_ResourceNamesCallbackFunction callback, void* context) const;
+ bool EnumerateWin32Resources(LPCWSTR lpName, LPCWSTR lpType, PEDecoder_ResourceCallbackFunction callback, void* context) const;
+ public:
+
+ // COR header fields
+
+ BOOL HasCorHeader() const;
+ CHECK CheckCorHeader() const;
+ IMAGE_COR20_HEADER *GetCorHeader() const;
+
+ PTR_CVOID GetMetadata(COUNT_T *pSize = NULL) const;
+
+ const void *GetResources(COUNT_T *pSize = NULL) const;
+ CHECK CheckResource(COUNT_T offset) const;
+ const void *GetResource(COUNT_T offset, COUNT_T *pSize = NULL) const;
+
+ BOOL HasManagedEntryPoint() const;
+ ULONG GetEntryPointToken() const;
+ IMAGE_COR_VTABLEFIXUP *GetVTableFixups(COUNT_T *pCount = NULL) const;
+
+ BOOL IsNativeMachineFormat() const;
+ BOOL IsI386() const;
+
+ void GetPEKindAndMachine(DWORD * pdwPEKind, DWORD *pdwMachine); // Returns CorPEKind flags
+ BOOL IsPlatformNeutral(); // Returns TRUE for IL-only platform neutral images
+
+ //
+ // Verifies that the IL is within the bounds of the image.
+ //
+ CHECK CheckILMethod(RVA rva);
+
+ //
+ // Compute size of IL blob. Assumes that the IL is within the bounds of the image - make sure
+ // to call CheckILMethod before calling this method.
+ //
+ static SIZE_T ComputeILMethodSize(TADDR pIL);
+
+ // Debug directory access, returns NULL if no such entry
+ PTR_IMAGE_DEBUG_DIRECTORY GetDebugDirectoryEntry(UINT index) const;
+
+ PTR_CVOID GetNativeManifestMetadata(COUNT_T* pSize = NULL) const;
+
+ BOOL IsComponentAssembly() const;
+ BOOL HasReadyToRunHeader() const;
+ READYTORUN_HEADER *GetReadyToRunHeader() const;
+
+ void GetEXEStackSizes(SIZE_T *PE_SizeOfStackReserve, SIZE_T *PE_SizeOfStackCommit) const;
+
+ // Native DLLMain Entrypoint
+ BOOL HasNativeEntryPoint() const;
+ void *GetNativeEntryPoint() const;
+
+ // Look up a named symbol in the export directory
+ PTR_VOID GetExport(LPCSTR exportName) const;
+
+#ifdef DACCESS_COMPILE
+ void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, bool enumThis);
+#endif
+
+ protected:
+
+ // ------------------------------------------------------------
+ // Protected API for subclass use
+ // ------------------------------------------------------------
+
+ // Checking utilites
+ static CHECK CheckBounds(RVA rangeBase, COUNT_T rangeSize, RVA rva);
+ static CHECK CheckBounds(RVA rangeBase, COUNT_T rangeSize, RVA rva, COUNT_T size);
+
+ static CHECK CheckBounds(const void *rangeBase, COUNT_T rangeSize, const void *pointer);
+ static CHECK CheckBounds(PTR_CVOID rangeBase, COUNT_T rangeSize, PTR_CVOID pointer, COUNT_T size);
+
+ protected:
+
+ // Flat mapping utilities - using PointerToRawData instead of (Relative)VirtualAddress
+ IMAGE_SECTION_HEADER *RvaToSection(RVA rva) const;
+ IMAGE_SECTION_HEADER *OffsetToSection(COUNT_T fileOffset) const;
+
+ void SetRelocated();
+ IMAGE_NT_HEADERS* FindNTHeaders() const;
+
+ private:
+
+ // ------------------------------------------------------------
+ // Internal functions
+ // ------------------------------------------------------------
+
+ enum METADATA_SECTION_TYPE
+ {
+ METADATA_SECTION_FULL,
+ };
+
+ IMAGE_DATA_DIRECTORY *GetMetaDataHelper(METADATA_SECTION_TYPE type) const;
+
+ static PTR_IMAGE_SECTION_HEADER FindFirstSection(IMAGE_NT_HEADERS * pNTHeaders);
+
+ IMAGE_COR20_HEADER *FindCorHeader() const;
+ READYTORUN_HEADER *FindReadyToRunHeader() const;
+
+ // Flat mapping utilities
+ RVA InternalAddressToRva(SIZE_T address) const;
+
+ // NT header subchecks
+ CHECK CheckSection(COUNT_T previousAddressEnd, COUNT_T addressStart, COUNT_T addressSize,
+ COUNT_T previousOffsetEnd, COUNT_T offsetStart, COUNT_T offsetSize) const;
+
+ // Pure managed subchecks
+ CHECK CheckILOnlyImportDlls() const;
+ CHECK CheckILOnlyImportByNameTable(RVA rva) const;
+ CHECK CheckILOnlyBaseRelocations() const;
+ CHECK CheckILOnlyEntryPoint() const;
+
+ // ------------------------------------------------------------
+ // Instance members
+ // ------------------------------------------------------------
+
+ enum
+ {
+ FLAG_MAPPED = 0x01, // the file is mapped/hydrated (vs. the raw disk layout)
+ FLAG_CONTENTS = 0x02, // the file has contents
+ FLAG_RELOCATED = 0x04, // relocs have been applied
+ FLAG_NT_CHECKED = 0x10,
+ FLAG_COR_CHECKED = 0x20,
+ FLAG_IL_ONLY_CHECKED = 0x40,
+ FLAG_NATIVE_CHECKED = 0x80,
+
+ FLAG_HAS_NO_READYTORUN_HEADER = 0x100,
+ };
+
+ TADDR m_base;
+ COUNT_T m_size; // size of file on disk, as opposed to OptionalHeaders.SizeOfImage
+ ULONG m_flags;
+
+ PTR_IMAGE_NT_HEADERS m_pNTHeaders;
+ PTR_IMAGE_COR20_HEADER m_pCorHeader;
+ PTR_READYTORUN_HEADER m_pReadyToRunHeader;
+};
+
+//
+// MethodSectionIterator class is used to iterate hot (or) cold method section in an ngen image.
+// It can also iterate nibble maps generated by the JIT in a regular HeapList.
+//
+class MethodSectionIterator
+{
+ private:
+ PTR_DWORD m_codeTableStart;
+ PTR_DWORD m_codeTable;
+ PTR_DWORD m_codeTableEnd;
+
+ BYTE *m_code;
+
+ DWORD m_dword;
+ DWORD m_index;
+
+ BYTE *m_current;
+
+ public:
+
+ //If code is a target pointer, then GetMethodCode and FindMethodCode return
+ //target pointers. codeTable may be a pointer of either type, since it is
+ //converted internally into a host pointer.
+ MethodSectionIterator(const void *code, SIZE_T codeSize,
+ const void *codeTable, SIZE_T codeTableSize);
+ BOOL Next();
+ BYTE *GetMethodCode() { return m_current; } // Get the start of method code of the current method in the iterator
+};
+
+#include "pedecoder.inl"
+
+#endif // PEDECODER_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// --------------------------------------------------------------------------------
+// PEDecoder.inl
+//
+
+// --------------------------------------------------------------------------------
+
+#ifndef _PEDECODER_INL_
+#define _PEDECODER_INL_
+
+#include "pedecoder.h"
+#include "ex.h"
+
+#ifndef DACCESS_COMPILE
+
+inline PEDecoder::PEDecoder()
+ : m_base(0),
+ m_size(0),
+ m_flags(0),
+ m_pNTHeaders(nullptr),
+ m_pCorHeader(nullptr),
+ m_pReadyToRunHeader(nullptr)
+{
+ CONTRACTL
+ {
+ CONSTRUCTOR_CHECK;
+ NOTHROW;
+ CANNOT_TAKE_LOCK;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+}
+#else
+inline PEDecoder::PEDecoder()
+{
+ LIMITED_METHOD_CONTRACT;
+}
+#endif // #ifndef DACCESS_COMPILE
+
+inline PTR_VOID PEDecoder::GetBase() const
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ return PTR_VOID(m_base);
+}
+
+inline BOOL PEDecoder::IsMapped() const
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ return (m_flags & FLAG_MAPPED) != 0;
+}
+
+inline BOOL PEDecoder::IsRelocated() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (m_flags & FLAG_RELOCATED) != 0;
+}
+
+inline void PEDecoder::SetRelocated()
+{
+ m_flags |= FLAG_RELOCATED;
+}
+
+inline BOOL PEDecoder::IsFlat() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return HasContents() && !IsMapped();
+}
+
+inline BOOL PEDecoder::HasContents() const
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ return (m_flags & FLAG_CONTENTS) != 0;
+}
+
+inline COUNT_T PEDecoder::GetSize() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return m_size;
+}
+
+inline PEDecoder::PEDecoder(PTR_VOID mappedBase, bool fixedUp /*= FALSE*/)
+ : m_base(dac_cast<TADDR>(mappedBase)),
+ m_size(0),
+ m_flags(FLAG_MAPPED | FLAG_CONTENTS | FLAG_NT_CHECKED | (fixedUp ? FLAG_RELOCATED : 0)),
+ m_pNTHeaders(nullptr),
+ m_pCorHeader(nullptr),
+ m_pReadyToRunHeader(nullptr)
+{
+ CONTRACTL
+ {
+ CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(mappedBase));
+ PRECONDITION(PEDecoder(mappedBase,fixedUp).CheckNTHeaders());
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ // Temporarily set the size to 2 pages, so we can get the headers.
+ m_size = GetOsPageSize()*2;
+
+ m_pNTHeaders = PTR_IMAGE_NT_HEADERS(FindNTHeaders());
+ if (!m_pNTHeaders)
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+
+ m_size = VAL32(m_pNTHeaders->OptionalHeader.SizeOfImage);
+}
+
+#ifndef DACCESS_COMPILE
+
+//REM
+//what's the right way to do this?
+//we want to use some against TADDR, but also want to do
+//some against what's just in the current process.
+//m_base is a TADDR in DAC all the time, though.
+//Have to implement separate fn to do the lookup??
+inline PEDecoder::PEDecoder(void *flatBase, COUNT_T size)
+ : m_base((TADDR)flatBase),
+ m_size(size),
+ m_flags(FLAG_CONTENTS),
+ m_pNTHeaders(NULL),
+ m_pCorHeader(NULL),
+ m_pReadyToRunHeader(NULL)
+{
+ CONTRACTL
+ {
+ CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(flatBase));
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+}
+
+inline void PEDecoder::Init(void *flatBase, COUNT_T size)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION((size == 0) || CheckPointer(flatBase));
+ PRECONDITION(!HasContents());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ m_base = (TADDR)flatBase;
+ m_size = size;
+ m_flags = FLAG_CONTENTS;
+}
+
+
+
+inline HRESULT PEDecoder::Init(void *mappedBase, bool fixedUp /*= FALSE*/)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(mappedBase));
+ PRECONDITION(!HasContents());
+ }
+ CONTRACTL_END;
+
+ m_base = (TADDR)mappedBase;
+ m_flags = FLAG_MAPPED | FLAG_CONTENTS;
+ if (fixedUp)
+ m_flags |= FLAG_RELOCATED;
+
+ // Temporarily set the size to 2 pages, so we can get the headers.
+ m_size = GetOsPageSize()*2;
+
+ m_pNTHeaders = FindNTHeaders();
+ if (!m_pNTHeaders)
+ return COR_E_BADIMAGEFORMAT;
+
+ m_size = VAL32(m_pNTHeaders->OptionalHeader.SizeOfImage);
+ return S_OK;
+}
+
+inline void PEDecoder::Reset()
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+ m_base=NULL;
+ m_flags=NULL;
+ m_size=NULL;
+ m_pNTHeaders=NULL;
+ m_pCorHeader=NULL;
+ m_pReadyToRunHeader=NULL;
+}
+#endif // #ifndef DACCESS_COMPILE
+
+
+inline IMAGE_NT_HEADERS32 *PEDecoder::GetNTHeaders32() const
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ return dac_cast<PTR_IMAGE_NT_HEADERS32>(FindNTHeaders());
+}
+
+inline IMAGE_NT_HEADERS64 *PEDecoder::GetNTHeaders64() const
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ return dac_cast<PTR_IMAGE_NT_HEADERS64>(FindNTHeaders());
+}
+
+inline BOOL PEDecoder::Has32BitNTHeaders() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(HasNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ return (FindNTHeaders()->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC));
+}
+
+inline const void *PEDecoder::GetHeaders(COUNT_T *pSize) const
+{
+ CONTRACT(const void *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ //even though some data in OptionalHeader is different for 32 and 64, this field is the same
+ if (pSize != NULL)
+ *pSize = VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders);
+
+ RETURN (const void *) m_base;
+}
+
+inline BOOL PEDecoder::IsDll() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ return ((FindNTHeaders()->FileHeader.Characteristics & VAL16(IMAGE_FILE_DLL)) != 0);
+}
+
+inline BOOL PEDecoder::HasBaseRelocations() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ return ((FindNTHeaders()->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED)) == 0);
+}
+
+inline const void *PEDecoder::GetPreferredBase() const
+{
+ CONTRACT(const void *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ if (Has32BitNTHeaders())
+ RETURN (const void *) (SIZE_T) VAL32(GetNTHeaders32()->OptionalHeader.ImageBase);
+ else
+ RETURN (const void *) (SIZE_T) VAL64(GetNTHeaders64()->OptionalHeader.ImageBase);
+}
+
+inline COUNT_T PEDecoder::GetVirtualSize() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ //even though some data in OptionalHeader is different for 32 and 64, this field is the same
+ return VAL32(FindNTHeaders()->OptionalHeader.SizeOfImage);
+}
+
+inline WORD PEDecoder::GetSubsystem() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ //even though some data in OptionalHeader is different for 32 and 64, this field is the same
+ return VAL16(FindNTHeaders()->OptionalHeader.Subsystem);
+}
+
+inline WORD PEDecoder::GetDllCharacteristics() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ //even though some data in OptionalHeader is different for 32 and 64, this field is the same
+ return VAL16(FindNTHeaders()->OptionalHeader.DllCharacteristics);
+}
+
+inline DWORD PEDecoder::GetTimeDateStamp() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ return VAL32(FindNTHeaders()->FileHeader.TimeDateStamp);
+}
+
+inline DWORD PEDecoder::GetCheckSum() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ //even though some data in OptionalHeader is different for 32 and 64, this field is the same
+ return VAL32(FindNTHeaders()->OptionalHeader.CheckSum);
+}
+
+inline DWORD PEDecoder::GetFileAlignment() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ //even though some data in OptionalHeader is different for 32 and 64, this field is the same
+ return VAL32(FindNTHeaders()->OptionalHeader.FileAlignment);
+}
+
+inline DWORD PEDecoder::GetSectionAlignment() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ //even though some data in OptionalHeader is different for 32 and 64, this field is the same
+ return VAL32(FindNTHeaders()->OptionalHeader.SectionAlignment);
+}
+
+inline WORD PEDecoder::GetMachine() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ return VAL16(FindNTHeaders()->FileHeader.Machine);
+}
+
+inline WORD PEDecoder::GetCharacteristics() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ return VAL16(FindNTHeaders()->FileHeader.Characteristics);
+}
+
+inline SIZE_T PEDecoder::GetSizeOfStackReserve() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (Has32BitNTHeaders())
+ return (SIZE_T) VAL32(GetNTHeaders32()->OptionalHeader.SizeOfStackReserve);
+ else
+ return (SIZE_T) VAL64(GetNTHeaders64()->OptionalHeader.SizeOfStackReserve);
+}
+
+
+inline SIZE_T PEDecoder::GetSizeOfStackCommit() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (Has32BitNTHeaders())
+ return (SIZE_T) VAL32(GetNTHeaders32()->OptionalHeader.SizeOfStackCommit);
+ else
+ return (SIZE_T) VAL64(GetNTHeaders64()->OptionalHeader.SizeOfStackCommit);
+}
+
+
+inline SIZE_T PEDecoder::GetSizeOfHeapReserve() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (Has32BitNTHeaders())
+ return (SIZE_T) VAL32(GetNTHeaders32()->OptionalHeader.SizeOfHeapReserve);
+ else
+ return (SIZE_T) VAL64(GetNTHeaders64()->OptionalHeader.SizeOfHeapReserve);
+}
+
+
+inline SIZE_T PEDecoder::GetSizeOfHeapCommit() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (Has32BitNTHeaders())
+ return (SIZE_T) VAL32(GetNTHeaders32()->OptionalHeader.SizeOfHeapCommit);
+ else
+ return (SIZE_T) VAL64(GetNTHeaders64()->OptionalHeader.SizeOfHeapCommit);
+}
+
+inline UINT32 PEDecoder::GetLoaderFlags() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (Has32BitNTHeaders())
+ return VAL32(GetNTHeaders32()->OptionalHeader.LoaderFlags);
+ else
+ return VAL32(GetNTHeaders64()->OptionalHeader.LoaderFlags);
+}
+
+inline UINT32 PEDecoder::GetWin32VersionValue() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (Has32BitNTHeaders())
+ return VAL32(GetNTHeaders32()->OptionalHeader.Win32VersionValue);
+ else
+ return VAL32(GetNTHeaders64()->OptionalHeader.Win32VersionValue);
+}
+
+inline COUNT_T PEDecoder::GetNumberOfRvaAndSizes() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (Has32BitNTHeaders())
+ return VAL32(GetNTHeaders32()->OptionalHeader.NumberOfRvaAndSizes);
+ else
+ return VAL32(GetNTHeaders64()->OptionalHeader.NumberOfRvaAndSizes);
+}
+
+inline BOOL PEDecoder::HasDirectoryEntry(int entry) const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ if (Has32BitNTHeaders())
+ return (GetNTHeaders32()->OptionalHeader.DataDirectory[entry].VirtualAddress != 0);
+ else
+ return (GetNTHeaders64()->OptionalHeader.DataDirectory[entry].VirtualAddress != 0);
+}
+
+inline IMAGE_DATA_DIRECTORY *PEDecoder::GetDirectoryEntry(int entry) const
+{
+ CONTRACT(IMAGE_DATA_DIRECTORY *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ if (Has32BitNTHeaders())
+ RETURN dac_cast<PTR_IMAGE_DATA_DIRECTORY>(
+ dac_cast<TADDR>(GetNTHeaders32()) +
+ offsetof(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory) +
+ entry * sizeof(IMAGE_DATA_DIRECTORY));
+ else
+ RETURN dac_cast<PTR_IMAGE_DATA_DIRECTORY>(
+ dac_cast<TADDR>(GetNTHeaders64()) +
+ offsetof(IMAGE_NT_HEADERS64, OptionalHeader.DataDirectory) +
+ entry * sizeof(IMAGE_DATA_DIRECTORY));
+}
+
+inline TADDR PEDecoder::GetDirectoryEntryData(int entry, COUNT_T *pSize) const
+{
+ CONTRACT(TADDR)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckDirectoryEntry(entry, 0, NULL_OK));
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer((void *)RETVAL, NULL_OK));
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(entry);
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN GetDirectoryData(pDir);
+}
+
+inline TADDR PEDecoder::GetDirectoryData(IMAGE_DATA_DIRECTORY *pDir) const
+{
+ CONTRACT(TADDR)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckDirectory(pDir, 0, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ POSTCONDITION(CheckPointer((void *)RETVAL, NULL_OK));
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACT_END;
+
+ RETURN GetRvaData(VAL32(pDir->VirtualAddress));
+}
+
+inline TADDR PEDecoder::GetDirectoryData(IMAGE_DATA_DIRECTORY *pDir, COUNT_T *pSize) const
+{
+ CONTRACT(TADDR)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckDirectory(pDir, 0, NULL_OK));
+ PRECONDITION(CheckPointer(pSize));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ POSTCONDITION(CheckPointer((void *)RETVAL, NULL_OK));
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACT_END;
+
+ *pSize = VAL32(pDir->Size);
+
+ RETURN GetRvaData(VAL32(pDir->VirtualAddress));
+}
+
+inline TADDR PEDecoder::GetInternalAddressData(SIZE_T address) const
+{
+ CONTRACT(TADDR)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckInternalAddress(address, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer((void *)RETVAL));
+ }
+ CONTRACT_END;
+
+ RETURN GetRvaData(InternalAddressToRva(address));
+}
+
+inline BOOL PEDecoder::HasCorHeader() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ SUPPORTS_DAC;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ return HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_COMHEADER);
+}
+
+inline BOOL PEDecoder::IsILOnly() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(HasCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ // Pretend that ready-to-run images are IL-only
+ return((GetCorHeader()->Flags & VAL32(COMIMAGE_FLAGS_ILONLY)) != 0) || HasReadyToRunHeader();
+}
+
+inline COUNT_T PEDecoder::RvaToOffset(RVA rva) const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckRva(rva,NULL_OK));
+ NOTHROW;
+ CANNOT_TAKE_LOCK;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+ if(rva > 0)
+ {
+ IMAGE_SECTION_HEADER *section = RvaToSection(rva);
+ if (section == NULL)
+ return rva;
+
+ return rva - VAL32(section->VirtualAddress) + VAL32(section->PointerToRawData);
+ }
+ else return 0;
+}
+
+inline RVA PEDecoder::OffsetToRva(COUNT_T fileOffset) const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckOffset(fileOffset,NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+ if(fileOffset > 0)
+ {
+ IMAGE_SECTION_HEADER *section = OffsetToSection(fileOffset);
+ PREFIX_ASSUME (section!=NULL); //TODO: actually it is possible that it si null we need to rethink how we handle this cases and do better there
+
+ return fileOffset - VAL32(section->PointerToRawData) + VAL32(section->VirtualAddress);
+ }
+ else return 0;
+}
+
+
+inline BOOL PEDecoder::IsStrongNameSigned() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(HasCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ return ((GetCorHeader()->Flags & VAL32(COMIMAGE_FLAGS_STRONGNAMESIGNED)) != 0);
+}
+
+
+inline BOOL PEDecoder::HasStrongNameSignature() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(HasCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ return (GetCorHeader()->StrongNameSignature.VirtualAddress != 0);
+}
+
+inline CHECK PEDecoder::CheckStrongNameSignature() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(HasCorHeader());
+ PRECONDITION(HasStrongNameSignature());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ return CheckDirectory(&GetCorHeader()->StrongNameSignature, IMAGE_SCN_MEM_WRITE, NULL_OK);
+}
+
+inline PTR_CVOID PEDecoder::GetStrongNameSignature(COUNT_T *pSize) const
+{
+ CONTRACT(PTR_CVOID)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(HasCorHeader());
+ PRECONDITION(HasStrongNameSignature());
+ PRECONDITION(CheckStrongNameSignature());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->StrongNameSignature;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN dac_cast<PTR_CVOID>(GetDirectoryData(pDir));
+}
+
+inline BOOL PEDecoder::HasTls() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ return HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_TLS);
+}
+
+inline CHECK PEDecoder::CheckTls() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_TLS, 0, NULL_OK));
+
+ IMAGE_TLS_DIRECTORY *pTlsHeader = (IMAGE_TLS_DIRECTORY *) GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_TLS);
+
+ CHECK(CheckUnderflow(VALPTR(pTlsHeader->EndAddressOfRawData), VALPTR(pTlsHeader->StartAddressOfRawData)));
+ CHECK(VALPTR(pTlsHeader->EndAddressOfRawData) - VALPTR(pTlsHeader->StartAddressOfRawData) <= COUNT_T_MAX);
+
+ CHECK(CheckInternalAddress(VALPTR(pTlsHeader->StartAddressOfRawData),
+ (COUNT_T) (VALPTR(pTlsHeader->EndAddressOfRawData) - VALPTR(pTlsHeader->StartAddressOfRawData))));
+
+ CHECK_OK;
+}
+
+inline PTR_VOID PEDecoder::GetTlsRange(COUNT_T * pSize) const
+{
+ CONTRACT(void *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(HasTls());
+ PRECONDITION(CheckTls());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ IMAGE_TLS_DIRECTORY *pTlsHeader =
+ PTR_IMAGE_TLS_DIRECTORY(GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_TLS));
+
+ if (pSize != 0)
+ *pSize = (COUNT_T) (VALPTR(pTlsHeader->EndAddressOfRawData) - VALPTR(pTlsHeader->StartAddressOfRawData));
+ PREFIX_ASSUME (pTlsHeader!=NULL);
+ RETURN PTR_VOID(GetInternalAddressData(pTlsHeader->StartAddressOfRawData));
+}
+
+inline UINT32 PEDecoder::GetTlsIndex() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(HasTls());
+ PRECONDITION(CheckTls());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ IMAGE_TLS_DIRECTORY *pTlsHeader = (IMAGE_TLS_DIRECTORY *) GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_TLS);
+
+ return (UINT32)*PTR_UINT32(GetInternalAddressData((SIZE_T)VALPTR(pTlsHeader->AddressOfIndex)));
+}
+
+inline IMAGE_COR20_HEADER *PEDecoder::GetCorHeader() const
+{
+ CONTRACT(IMAGE_COR20_HEADER *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(HasCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ if (m_pCorHeader == NULL)
+ const_cast<PEDecoder *>(this)->m_pCorHeader =
+ dac_cast<PTR_IMAGE_COR20_HEADER>(FindCorHeader());
+
+ RETURN m_pCorHeader;
+}
+
+inline BOOL PEDecoder::IsNativeMachineFormat() const
+{
+ if (!HasContents() || !HasNTHeaders() )
+ return FALSE;
+ _ASSERTE(m_pNTHeaders);
+ WORD expectedFormat = HasCorHeader() && HasReadyToRunHeader() ?
+ IMAGE_FILE_MACHINE_NATIVE_NI :
+ IMAGE_FILE_MACHINE_NATIVE;
+ //do not call GetNTHeaders as we do not want to bother with PE32->PE32+ conversion
+ return m_pNTHeaders->FileHeader.Machine==expectedFormat;
+}
+
+inline BOOL PEDecoder::IsI386() const
+{
+ if (!HasContents() || !HasNTHeaders() )
+ return FALSE;
+ _ASSERTE(m_pNTHeaders);
+ //do not call GetNTHeaders as we do not want to bother with PE32->PE32+ conversion
+ return m_pNTHeaders->FileHeader.Machine==IMAGE_FILE_MACHINE_I386;
+}
+
+// static
+inline PTR_IMAGE_SECTION_HEADER PEDecoder::FindFirstSection(IMAGE_NT_HEADERS * pNTHeaders)
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ return dac_cast<PTR_IMAGE_SECTION_HEADER>(
+ dac_cast<TADDR>(pNTHeaders) +
+ FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
+ VAL16(pNTHeaders->FileHeader.SizeOfOptionalHeader));
+}
+
+inline COUNT_T PEDecoder::GetNumberOfSections() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ return VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+}
+
+
+inline DWORD PEDecoder::GetImageIdentity() const
+{
+ WRAPPER_NO_CONTRACT;
+ return GetTimeDateStamp() ^ GetCheckSum() ^ DWORD( GetVirtualSize() );
+}
+
+
+inline PTR_IMAGE_SECTION_HEADER PEDecoder::FindFirstSection() const
+{
+ CONTRACT(IMAGE_SECTION_HEADER *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ RETURN FindFirstSection(FindNTHeaders());
+}
+
+inline IMAGE_NT_HEADERS *PEDecoder::FindNTHeaders() const
+{
+ CONTRACT(IMAGE_NT_HEADERS *)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ RETURN PTR_IMAGE_NT_HEADERS(m_base + VAL32(PTR_IMAGE_DOS_HEADER(m_base)->e_lfanew));
+}
+
+inline IMAGE_COR20_HEADER *PEDecoder::FindCorHeader() const
+{
+ CONTRACT(IMAGE_COR20_HEADER *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(HasCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ const IMAGE_COR20_HEADER * pCor=PTR_IMAGE_COR20_HEADER(GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_COMHEADER));
+ RETURN ((IMAGE_COR20_HEADER*)pCor);
+}
+
+inline CHECK PEDecoder::CheckBounds(RVA rangeBase, COUNT_T rangeSize, RVA rva)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ CHECK(CheckOverflow(rangeBase, rangeSize));
+ CHECK(rva >= rangeBase);
+ CHECK(rva <= rangeBase + rangeSize);
+ CHECK_OK;
+}
+
+inline CHECK PEDecoder::CheckBounds(RVA rangeBase, COUNT_T rangeSize, RVA rva, COUNT_T size)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ CHECK(CheckOverflow(rangeBase, rangeSize));
+ CHECK(CheckOverflow(rva, size));
+ CHECK(rva >= rangeBase);
+ CHECK(rva + size <= rangeBase + rangeSize);
+ CHECK_OK;
+}
+
+inline CHECK PEDecoder::CheckBounds(const void *rangeBase, COUNT_T rangeSize, const void *pointer)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ CHECK(CheckOverflow(dac_cast<PTR_CVOID>(rangeBase), rangeSize));
+ CHECK(dac_cast<TADDR>(pointer) >= dac_cast<TADDR>(rangeBase));
+ CHECK(dac_cast<TADDR>(pointer) <= dac_cast<TADDR>(rangeBase) + rangeSize);
+ CHECK_OK;
+}
+
+inline CHECK PEDecoder::CheckBounds(PTR_CVOID rangeBase, COUNT_T rangeSize, PTR_CVOID pointer, COUNT_T size)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ CHECK(CheckOverflow(rangeBase, rangeSize));
+ CHECK(CheckOverflow(pointer, size));
+ CHECK(dac_cast<TADDR>(pointer) >= dac_cast<TADDR>(rangeBase));
+ CHECK(dac_cast<TADDR>(pointer) + size <= dac_cast<TADDR>(rangeBase) + rangeSize);
+ CHECK_OK;
+}
+
+inline void PEDecoder::GetPEKindAndMachine(DWORD * pdwPEKind, DWORD *pdwMachine)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ DWORD dwKind=0,dwMachine=0;
+ if(HasContents() && HasNTHeaders())
+ {
+ dwMachine = GetMachine();
+
+ BOOL fIsPE32Plus = !Has32BitNTHeaders();
+
+ if (fIsPE32Plus)
+ dwKind |= (DWORD)pe32Plus;
+
+ if (HasCorHeader())
+ {
+ IMAGE_COR20_HEADER * pCorHdr = GetCorHeader();
+ if(pCorHdr != NULL)
+ {
+ DWORD dwCorFlags = pCorHdr->Flags;
+
+ if (dwCorFlags & VAL32(COMIMAGE_FLAGS_ILONLY))
+ {
+ dwKind |= (DWORD)peILonly;
+#ifdef HOST_64BIT
+ // compensate for shim promotion of PE32/ILONLY headers to PE32+ on WIN64
+ if (fIsPE32Plus && (GetMachine() == IMAGE_FILE_MACHINE_I386))
+ dwKind &= ~((DWORD)pe32Plus);
+#endif
+ }
+
+ if (COR_IS_32BIT_REQUIRED(dwCorFlags))
+ dwKind |= (DWORD)pe32BitRequired;
+ else if (COR_IS_32BIT_PREFERRED(dwCorFlags))
+ dwKind |= (DWORD)pe32BitPreferred;
+
+ // compensate for MC++ peculiarity
+ if(dwKind == 0)
+ dwKind = (DWORD)pe32BitRequired;
+ }
+ else
+ {
+ dwKind |= (DWORD)pe32Unmanaged;
+ }
+
+ if (HasReadyToRunHeader())
+ {
+ if (dwMachine == IMAGE_FILE_MACHINE_NATIVE_NI)
+ {
+ // Supply the original machine type to the assembly binder
+ dwMachine = IMAGE_FILE_MACHINE_NATIVE;
+ }
+
+ if ((GetReadyToRunHeader()->CoreHeader.Flags & READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE) != 0)
+ {
+ // Supply the original PEKind/Machine to the assembly binder to make the full assembly name look like the original
+ dwKind = peILonly;
+ dwMachine = IMAGE_FILE_MACHINE_I386;
+ }
+ }
+ }
+ else
+ {
+ dwKind |= (DWORD)pe32Unmanaged;
+ }
+ }
+
+ *pdwPEKind = dwKind;
+ *pdwMachine = dwMachine;
+}
+
+inline BOOL PEDecoder::IsPlatformNeutral()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ DWORD dwKind, dwMachine;
+ GetPEKindAndMachine(&dwKind, &dwMachine);
+ return ((dwKind & (peILonly | pe32Plus | pe32BitRequired)) == peILonly) && (dwMachine == IMAGE_FILE_MACHINE_I386);
+}
+
+inline BOOL PEDecoder::IsComponentAssembly() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ return HasReadyToRunHeader() && (m_pReadyToRunHeader->CoreHeader.Flags & READYTORUN_FLAG_COMPONENT) != 0;
+}
+
+inline BOOL PEDecoder::HasReadyToRunHeader() const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ if (m_flags & FLAG_HAS_NO_READYTORUN_HEADER)
+ return FALSE;
+
+ if (m_pReadyToRunHeader != NULL)
+ return TRUE;
+
+ return FindReadyToRunHeader() != NULL;
+}
+
+inline READYTORUN_HEADER * PEDecoder::GetReadyToRunHeader() const
+{
+ CONTRACT(READYTORUN_HEADER *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(HasCorHeader());
+ PRECONDITION(HasReadyToRunHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ SUPPORTS_DAC;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACT_END;
+
+ if (m_pReadyToRunHeader != NULL)
+ RETURN m_pReadyToRunHeader;
+
+ RETURN FindReadyToRunHeader();
+}
+
+#endif // _PEDECODER_INL_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// readytorun.h
+//
+
+//
+// Contains definitions for the Ready to Run file format
+//
+
+#ifndef __READYTORUN_H__
+#define __READYTORUN_H__
+
+#define READYTORUN_SIGNATURE 0x00525452 // 'RTR'
+
+// Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
+#define READYTORUN_MAJOR_VERSION 0x0006
+#define READYTORUN_MINOR_VERSION 0x0000
+
+#define MINIMUM_READYTORUN_MAJOR_VERSION 0x006
+
+// R2R Version 2.1 adds the InliningInfo section
+// R2R Version 2.2 adds the ProfileDataInfo section
+// R2R Version 3.0 changes calling conventions to correctly handle explicit structures to spec.
+// R2R 3.0 is not backward compatible with 2.x.
+// R2R Version 6.0 changes managed layout for sequential types with any unmanaged non-blittable fields.
+// R2R 6.0 is not backward compatible with 5.x or earlier.
+
+struct READYTORUN_CORE_HEADER
+{
+ DWORD Flags; // READYTORUN_FLAG_XXX
+
+ DWORD NumberOfSections;
+
+ // Array of sections follows. The array entries are sorted by Type
+ // READYTORUN_SECTION Sections[];
+};
+
+struct READYTORUN_HEADER
+{
+ DWORD Signature; // READYTORUN_SIGNATURE
+ USHORT MajorVersion; // READYTORUN_VERSION_XXX
+ USHORT MinorVersion;
+
+ READYTORUN_CORE_HEADER CoreHeader;
+};
+
+struct READYTORUN_COMPONENT_ASSEMBLIES_ENTRY
+{
+ IMAGE_DATA_DIRECTORY CorHeader;
+ IMAGE_DATA_DIRECTORY ReadyToRunCoreHeader;
+};
+
+enum ReadyToRunFlag
+{
+ READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE = 0x00000001, // Set if the original IL assembly was platform-neutral
+ READYTORUN_FLAG_SKIP_TYPE_VALIDATION = 0x00000002, // Set of methods with native code was determined using profile data
+ READYTORUN_FLAG_PARTIAL = 0x00000004,
+ READYTORUN_FLAG_NONSHARED_PINVOKE_STUBS = 0x00000008, // PInvoke stubs compiled into image are non-shareable (no secret parameter)
+ READYTORUN_FLAG_EMBEDDED_MSIL = 0x00000010, // MSIL is embedded in the composite R2R executable
+ READYTORUN_FLAG_COMPONENT = 0x00000020, // This is the header describing a component assembly of composite R2R
+};
+
+enum class ReadyToRunSectionType : uint32_t
+{
+ CompilerIdentifier = 100,
+ ImportSections = 101,
+ RuntimeFunctions = 102,
+ MethodDefEntryPoints = 103,
+ ExceptionInfo = 104,
+ DebugInfo = 105,
+ DelayLoadMethodCallThunks = 106,
+ // 107 used by an older format of AvailableTypes
+ AvailableTypes = 108,
+ InstanceMethodEntryPoints = 109,
+ InliningInfo = 110, // Added in V2.1, deprecated in 4.1
+ ProfileDataInfo = 111, // Added in V2.2
+ ManifestMetadata = 112, // Added in V2.3
+ AttributePresence = 113, // Added in V3.1
+ InliningInfo2 = 114, // Added in V4.1
+ ComponentAssemblies = 115, // Added in V4.1
+ OwnerCompositeExecutable = 116, // Added in V4.1
+ PgoInstrumentationData = 117, // Added in V5.2
+ ManifestAssemblyMvids = 118, // Added in V5.3
+
+ // If you add a new section consider whether it is a breaking or non-breaking change.
+ // Usually it is non-breaking, but if it is preferable to have older runtimes fail
+ // to load the image vs. ignoring the new section it could be marked breaking.
+ // Increment the READYTORUN_MINOR_VERSION (non-breaking) or READYTORUN_MAJOR_VERSION
+ // (breaking) as appropriate.
+};
+
+struct READYTORUN_SECTION
+{
+ ReadyToRunSectionType Type; // READYTORUN_SECTION_XXX
+ IMAGE_DATA_DIRECTORY Section;
+};
+
+//
+// READYTORUN_IMPORT_SECTION describes image range with references to code or runtime data structures
+//
+// There is number of different types of these ranges: eagerly initialized at image load vs. lazily initialized at method entry
+// vs. lazily initialized on first use; handles vs. code pointers, etc.
+//
+struct READYTORUN_IMPORT_SECTION
+{
+ IMAGE_DATA_DIRECTORY Section; // Section containing values to be fixed up
+ USHORT Flags; // One or more of ReadyToRunImportSectionFlags
+ BYTE Type; // One of ReadyToRunImportSectionType
+ BYTE EntrySize;
+ DWORD Signatures; // RVA of optional signature descriptors
+ DWORD AuxiliaryData; // RVA of optional auxiliary data (typically GC info)
+};
+
+enum ReadyToRunImportSectionType
+{
+ READYTORUN_IMPORT_SECTION_TYPE_UNKNOWN = 0,
+};
+
+enum ReadyToRunImportSectionFlags
+{
+ READYTORUN_IMPORT_SECTION_FLAGS_EAGER = 0x0001,
+};
+
+//
+// Constants for method and field encoding
+//
+
+enum ReadyToRunMethodSigFlags
+{
+ READYTORUN_METHOD_SIG_UnboxingStub = 0x01,
+ READYTORUN_METHOD_SIG_InstantiatingStub = 0x02,
+ READYTORUN_METHOD_SIG_MethodInstantiation = 0x04,
+ READYTORUN_METHOD_SIG_SlotInsteadOfToken = 0x08,
+ READYTORUN_METHOD_SIG_MemberRefToken = 0x10,
+ READYTORUN_METHOD_SIG_Constrained = 0x20,
+ READYTORUN_METHOD_SIG_OwnerType = 0x40,
+ READYTORUN_METHOD_SIG_UpdateContext = 0x80,
+};
+
+enum ReadyToRunFieldSigFlags
+{
+ READYTORUN_FIELD_SIG_IndexInsteadOfToken = 0x08,
+ READYTORUN_FIELD_SIG_MemberRefToken = 0x10,
+ READYTORUN_FIELD_SIG_OwnerType = 0x40,
+};
+
+enum ReadyToRunTypeLayoutFlags
+{
+ READYTORUN_LAYOUT_HFA = 0x01,
+ READYTORUN_LAYOUT_Alignment = 0x02,
+ READYTORUN_LAYOUT_Alignment_Native = 0x04,
+ READYTORUN_LAYOUT_GCLayout = 0x08,
+ READYTORUN_LAYOUT_GCLayout_Empty = 0x10,
+};
+
+enum ReadyToRunVirtualFunctionOverrideFlags
+{
+ READYTORUN_VIRTUAL_OVERRIDE_None = 0x00,
+ READYTORUN_VIRTUAL_OVERRIDE_VirtualFunctionOverriden = 0x01,
+};
+
+//
+// Constants for fixup signature encoding
+//
+
+enum ReadyToRunFixupKind
+{
+ READYTORUN_FIXUP_ThisObjDictionaryLookup = 0x07,
+ READYTORUN_FIXUP_TypeDictionaryLookup = 0x08,
+ READYTORUN_FIXUP_MethodDictionaryLookup = 0x09,
+
+ READYTORUN_FIXUP_TypeHandle = 0x10,
+ READYTORUN_FIXUP_MethodHandle = 0x11,
+ READYTORUN_FIXUP_FieldHandle = 0x12,
+
+ READYTORUN_FIXUP_MethodEntry = 0x13, /* For calling a method entry point */
+ READYTORUN_FIXUP_MethodEntry_DefToken = 0x14, /* Smaller version of MethodEntry - method is def token */
+ READYTORUN_FIXUP_MethodEntry_RefToken = 0x15, /* Smaller version of MethodEntry - method is ref token */
+
+ READYTORUN_FIXUP_VirtualEntry = 0x16, /* For invoking a virtual method */
+ READYTORUN_FIXUP_VirtualEntry_DefToken = 0x17, /* Smaller version of VirtualEntry - method is def token */
+ READYTORUN_FIXUP_VirtualEntry_RefToken = 0x18, /* Smaller version of VirtualEntry - method is ref token */
+ READYTORUN_FIXUP_VirtualEntry_Slot = 0x19, /* Smaller version of VirtualEntry - type & slot */
+
+ READYTORUN_FIXUP_Helper = 0x1A, /* Helper */
+ READYTORUN_FIXUP_StringHandle = 0x1B, /* String handle */
+
+ READYTORUN_FIXUP_NewObject = 0x1C, /* Dynamically created new helper */
+ READYTORUN_FIXUP_NewArray = 0x1D,
+
+ READYTORUN_FIXUP_IsInstanceOf = 0x1E, /* Dynamically created casting helper */
+ READYTORUN_FIXUP_ChkCast = 0x1F,
+
+ READYTORUN_FIXUP_FieldAddress = 0x20, /* For accessing a cross-module static fields */
+ READYTORUN_FIXUP_CctorTrigger = 0x21, /* Static constructor trigger */
+
+ READYTORUN_FIXUP_StaticBaseNonGC = 0x22, /* Dynamically created static base helpers */
+ READYTORUN_FIXUP_StaticBaseGC = 0x23,
+ READYTORUN_FIXUP_ThreadStaticBaseNonGC = 0x24,
+ READYTORUN_FIXUP_ThreadStaticBaseGC = 0x25,
+
+ READYTORUN_FIXUP_FieldBaseOffset = 0x26, /* Field base offset */
+ READYTORUN_FIXUP_FieldOffset = 0x27, /* Field offset */
+
+ READYTORUN_FIXUP_TypeDictionary = 0x28,
+ READYTORUN_FIXUP_MethodDictionary = 0x29,
+
+ READYTORUN_FIXUP_Check_TypeLayout = 0x2A, /* size, alignment, HFA, reference map */
+ READYTORUN_FIXUP_Check_FieldOffset = 0x2B,
+
+ READYTORUN_FIXUP_DelegateCtor = 0x2C, /* optimized delegate ctor */
+ READYTORUN_FIXUP_DeclaringTypeHandle = 0x2D,
+
+ READYTORUN_FIXUP_IndirectPInvokeTarget = 0x2E, /* Target (indirect) of an inlined pinvoke */
+ READYTORUN_FIXUP_PInvokeTarget = 0x2F, /* Target of an inlined pinvoke */
+
+ READYTORUN_FIXUP_Check_InstructionSetSupport= 0x30, /* Define the set of instruction sets that must be supported/unsupported to use the fixup */
+
+ READYTORUN_FIXUP_Verify_FieldOffset = 0x31, /* Generate a runtime check to ensure that the field offset matches between compile and runtime. Unlike Check_FieldOffset, this will generate a runtime failure instead of silently dropping the method */
+ READYTORUN_FIXUP_Verify_TypeLayout = 0x32, /* Generate a runtime check to ensure that the type layout (size, alignment, HFA, reference map) matches between compile and runtime. Unlike Check_TypeLayout, this will generate a runtime failure instead of silently dropping the method */
+
+ READYTORUN_FIXUP_Check_VirtualFunctionOverride = 0x33, /* Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, code will not be used */
+ READYTORUN_FIXUP_Verify_VirtualFunctionOverride = 0x34, /* Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, generate runtime failure. */
+};
+
+//
+// Intrinsics and helpers
+//
+
+enum ReadyToRunHelper
+{
+ READYTORUN_HELPER_Invalid = 0x00,
+
+ // Not a real helper - handle to current module passed to delay load helpers.
+ READYTORUN_HELPER_Module = 0x01,
+ READYTORUN_HELPER_GSCookie = 0x02,
+ READYTORUN_HELPER_IndirectTrapThreads = 0x03,
+
+ //
+ // Delay load helpers
+ //
+
+ // All delay load helpers use custom calling convention:
+ // - scratch register - address of indirection cell. 0 = address is inferred from callsite.
+ // - stack - section index, module handle
+ READYTORUN_HELPER_DelayLoad_MethodCall = 0x08,
+
+ READYTORUN_HELPER_DelayLoad_Helper = 0x10,
+ READYTORUN_HELPER_DelayLoad_Helper_Obj = 0x11,
+ READYTORUN_HELPER_DelayLoad_Helper_ObjObj = 0x12,
+
+ // JIT helpers
+
+ // Exception handling helpers
+ READYTORUN_HELPER_Throw = 0x20,
+ READYTORUN_HELPER_Rethrow = 0x21,
+ READYTORUN_HELPER_Overflow = 0x22,
+ READYTORUN_HELPER_RngChkFail = 0x23,
+ READYTORUN_HELPER_FailFast = 0x24,
+ READYTORUN_HELPER_ThrowNullRef = 0x25,
+ READYTORUN_HELPER_ThrowDivZero = 0x26,
+
+ // Write barriers
+ READYTORUN_HELPER_WriteBarrier = 0x30,
+ READYTORUN_HELPER_CheckedWriteBarrier = 0x31,
+ READYTORUN_HELPER_ByRefWriteBarrier = 0x32,
+
+ // Array helpers
+ READYTORUN_HELPER_Stelem_Ref = 0x38,
+ READYTORUN_HELPER_Ldelema_Ref = 0x39,
+
+ READYTORUN_HELPER_MemSet = 0x40,
+ READYTORUN_HELPER_MemCpy = 0x41,
+
+ // PInvoke helpers
+ READYTORUN_HELPER_PInvokeBegin = 0x42,
+ READYTORUN_HELPER_PInvokeEnd = 0x43,
+ READYTORUN_HELPER_GCPoll = 0x44,
+ READYTORUN_HELPER_ReversePInvokeEnter = 0x45,
+ READYTORUN_HELPER_ReversePInvokeExit = 0x46,
+
+ // Get string handle lazily
+ READYTORUN_HELPER_GetString = 0x50,
+
+ // Used by /Tuning for Profile optimizations
+ READYTORUN_HELPER_LogMethodEnter = 0x51,
+
+ // Reflection helpers
+ READYTORUN_HELPER_GetRuntimeTypeHandle = 0x54,
+ READYTORUN_HELPER_GetRuntimeMethodHandle = 0x55,
+ READYTORUN_HELPER_GetRuntimeFieldHandle = 0x56,
+
+ READYTORUN_HELPER_Box = 0x58,
+ READYTORUN_HELPER_Box_Nullable = 0x59,
+ READYTORUN_HELPER_Unbox = 0x5A,
+ READYTORUN_HELPER_Unbox_Nullable = 0x5B,
+ READYTORUN_HELPER_NewMultiDimArr = 0x5C,
+
+ // Helpers used with generic handle lookup cases
+ READYTORUN_HELPER_NewObject = 0x60,
+ READYTORUN_HELPER_NewArray = 0x61,
+ READYTORUN_HELPER_CheckCastAny = 0x62,
+ READYTORUN_HELPER_CheckInstanceAny = 0x63,
+ READYTORUN_HELPER_GenericGcStaticBase = 0x64,
+ READYTORUN_HELPER_GenericNonGcStaticBase = 0x65,
+ READYTORUN_HELPER_GenericGcTlsBase = 0x66,
+ READYTORUN_HELPER_GenericNonGcTlsBase = 0x67,
+ READYTORUN_HELPER_VirtualFuncPtr = 0x68,
+
+ // Long mul/div/shift ops
+ READYTORUN_HELPER_LMul = 0xC0,
+ READYTORUN_HELPER_LMulOfv = 0xC1,
+ READYTORUN_HELPER_ULMulOvf = 0xC2,
+ READYTORUN_HELPER_LDiv = 0xC3,
+ READYTORUN_HELPER_LMod = 0xC4,
+ READYTORUN_HELPER_ULDiv = 0xC5,
+ READYTORUN_HELPER_ULMod = 0xC6,
+ READYTORUN_HELPER_LLsh = 0xC7,
+ READYTORUN_HELPER_LRsh = 0xC8,
+ READYTORUN_HELPER_LRsz = 0xC9,
+ READYTORUN_HELPER_Lng2Dbl = 0xCA,
+ READYTORUN_HELPER_ULng2Dbl = 0xCB,
+
+ // 32-bit division helpers
+ READYTORUN_HELPER_Div = 0xCC,
+ READYTORUN_HELPER_Mod = 0xCD,
+ READYTORUN_HELPER_UDiv = 0xCE,
+ READYTORUN_HELPER_UMod = 0xCF,
+
+ // Floating point conversions
+ READYTORUN_HELPER_Dbl2Int = 0xD0,
+ READYTORUN_HELPER_Dbl2IntOvf = 0xD1,
+ READYTORUN_HELPER_Dbl2Lng = 0xD2,
+ READYTORUN_HELPER_Dbl2LngOvf = 0xD3,
+ READYTORUN_HELPER_Dbl2UInt = 0xD4,
+ READYTORUN_HELPER_Dbl2UIntOvf = 0xD5,
+ READYTORUN_HELPER_Dbl2ULng = 0xD6,
+ READYTORUN_HELPER_Dbl2ULngOvf = 0xD7,
+
+ // Floating point ops
+ READYTORUN_HELPER_DblRem = 0xE0,
+ READYTORUN_HELPER_FltRem = 0xE1,
+ READYTORUN_HELPER_DblRound = 0xE2,
+ READYTORUN_HELPER_FltRound = 0xE3,
+
+#ifdef FEATURE_EH_FUNCLETS
+ // Personality rountines
+ READYTORUN_HELPER_PersonalityRoutine = 0xF0,
+ READYTORUN_HELPER_PersonalityRoutineFilterFunclet = 0xF1,
+#endif
+
+ // Synchronized methods
+ READYTORUN_HELPER_MonitorEnter = 0xF8,
+ READYTORUN_HELPER_MonitorExit = 0xF9,
+
+ //
+ // Deprecated/legacy
+ //
+
+ // JIT32 x86-specific write barriers
+ READYTORUN_HELPER_WriteBarrier_EAX = 0x100,
+ READYTORUN_HELPER_WriteBarrier_EBX = 0x101,
+ READYTORUN_HELPER_WriteBarrier_ECX = 0x102,
+ READYTORUN_HELPER_WriteBarrier_ESI = 0x103,
+ READYTORUN_HELPER_WriteBarrier_EDI = 0x104,
+ READYTORUN_HELPER_WriteBarrier_EBP = 0x105,
+ READYTORUN_HELPER_CheckedWriteBarrier_EAX = 0x106,
+ READYTORUN_HELPER_CheckedWriteBarrier_EBX = 0x107,
+ READYTORUN_HELPER_CheckedWriteBarrier_ECX = 0x108,
+ READYTORUN_HELPER_CheckedWriteBarrier_ESI = 0x109,
+ READYTORUN_HELPER_CheckedWriteBarrier_EDI = 0x10A,
+ READYTORUN_HELPER_CheckedWriteBarrier_EBP = 0x10B,
+
+ // JIT32 x86-specific exception handling
+ READYTORUN_HELPER_EndCatch = 0x110,
+
+ // Stack probing helper
+ READYTORUN_HELPER_StackProbe = 0x111,
+
+ READYTORUN_HELPER_GetCurrentManagedThreadId = 0x112,
+};
+
+#include "readytoruninstructionset.h"
+
+//
+// Exception info
+//
+
+struct READYTORUN_EXCEPTION_LOOKUP_TABLE_ENTRY
+{
+ DWORD MethodStart;
+ DWORD ExceptionInfo;
+};
+
+struct READYTORUN_EXCEPTION_CLAUSE
+{
+ CorExceptionFlag Flags;
+ DWORD TryStartPC;
+ DWORD TryEndPC;
+ DWORD HandlerStartPC;
+ DWORD HandlerEndPC;
+ union {
+ mdToken ClassToken;
+ DWORD FilterOffset;
+ };
+};
+
+enum ReadyToRunRuntimeConstants : DWORD
+{
+ READYTORUN_PInvokeTransitionFrameSizeInPointerUnits = 11,
+#ifdef TARGET_X86
+ READYTORUN_ReversePInvokeTransitionFrameSizeInPointerUnits = 5,
+#else
+ READYTORUN_ReversePInvokeTransitionFrameSizeInPointerUnits = 2,
+#endif
+};
+
+enum ReadyToRunHFAElemType : DWORD
+{
+ READYTORUN_HFA_ELEMTYPE_None = 0,
+ READYTORUN_HFA_ELEMTYPE_Float32 = 1,
+ READYTORUN_HFA_ELEMTYPE_Float64 = 2,
+ READYTORUN_HFA_ELEMTYPE_Vector64 = 3,
+ READYTORUN_HFA_ELEMTYPE_Vector128 = 4,
+};
+
+#endif // __READYTORUN_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// DO NOT EDIT THIS FILE! IT IS AUTOGENERATED
+// FROM /src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt
+// using /src/coreclr/tools/Common/JitInterface/ThunkGenerator/gen.bat
+
+#ifndef READYTORUNINSTRUCTIONSET_H
+#define READYTORUNINSTRUCTIONSET_H
+enum ReadyToRunInstructionSet
+{
+ READYTORUN_INSTRUCTION_Sse=1,
+ READYTORUN_INSTRUCTION_Sse2=2,
+ READYTORUN_INSTRUCTION_Sse3=3,
+ READYTORUN_INSTRUCTION_Ssse3=4,
+ READYTORUN_INSTRUCTION_Sse41=5,
+ READYTORUN_INSTRUCTION_Sse42=6,
+ READYTORUN_INSTRUCTION_Avx=7,
+ READYTORUN_INSTRUCTION_Avx2=8,
+ READYTORUN_INSTRUCTION_Aes=9,
+ READYTORUN_INSTRUCTION_Bmi1=10,
+ READYTORUN_INSTRUCTION_Bmi2=11,
+ READYTORUN_INSTRUCTION_Fma=12,
+ READYTORUN_INSTRUCTION_Lzcnt=13,
+ READYTORUN_INSTRUCTION_Pclmulqdq=14,
+ READYTORUN_INSTRUCTION_Popcnt=15,
+ READYTORUN_INSTRUCTION_ArmBase=16,
+ READYTORUN_INSTRUCTION_AdvSimd=17,
+ READYTORUN_INSTRUCTION_Crc32=18,
+ READYTORUN_INSTRUCTION_Sha1=19,
+ READYTORUN_INSTRUCTION_Sha256=20,
+ READYTORUN_INSTRUCTION_Atomics=21,
+ READYTORUN_INSTRUCTION_X86Base=22,
+ READYTORUN_INSTRUCTION_Dp=23,
+ READYTORUN_INSTRUCTION_Rdm=24,
+ READYTORUN_INSTRUCTION_AvxVnni=25,
+
+};
+
+#endif // READYTORUNINSTRUCTIONSET_H
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// File: registrywrapper.h
+//
+// Wrapper around Win32 Registry Functions allowing redirection of .NET
+// Framework root registry location
+//
+//*****************************************************************************
+#ifndef __REGISTRYWRAPPER_H
+#define __REGISTRYWRAPPER_H
+
+
+#define ClrRegCreateKeyEx RegCreateKeyExW
+#define ClrRegOpenKeyEx RegOpenKeyExW
+#define IsNgenOffline() false
+
+
+#endif // __REGISTRYWRAPPER_H
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//{{NO_DEPENDENCIES}}
+// Used by mscorrc.rc
+//
+
+
+// For (failing) hresults of facility FACILITY_URT, we store
+// unparameterized description strings in the range
+// 0x6000..0x9000.
+#define MSG_FOR_URT_HR(hr) (0x6000 + (HRESULT_CODE(hr)))
+#define MAX_URT_HRESULT_CODE 0x3000
+
+#define HR_FOR_URT_MSG(code) (((code) >=0x6000 && (code) <= 0x6000+MAX_URT_HRESULT_CODE) ? \
+ MAKE_HRESULT(SEVERITY_ERROR, FACILITY_URT, (code) - 0x6000) : \
+ (code))
+
+#ifndef HRESULT_CODE
+#define HRESULT_CODE(hr) ((hr) & 0xFFFF)
+#endif // HRESULT_CODE
+
+
+//-----------------------------------------------------------------------------
+// Resource strings for MDA descriptions.
+//-----------------------------------------------------------------------------
+
+#define MDARC_DEBUGGER_FIBER_MODE_NOT_SUPPORTED 0x1934
+
+#define IDS_RTL 0x01F5
+
+#define IDS_DS_ACTIVESESSIONS 0x1701
+#define IDS_DS_DATASOURCENAME 0x1702
+#define IDS_DS_DATASOURCEREADONLY 0x1703
+#define IDS_DS_DBMSNAME 0x1704
+#define IDS_DS_DBMSVER 0x1705
+#define IDS_DS_IDENTIFIERCASE 0x1706
+#define IDS_DS_DSOTHREADMODEL 0x1707
+
+#define IDS_EE_NDIRECT_UNSUPPORTED_SIG 0x1708
+#define IDS_EE_NDIRECT_BADNATL 0x170a
+#define IDS_EE_NDIRECT_LOADLIB_WIN 0x170b
+#define IDS_EE_NDIRECT_GETPROCADDRESS_WIN 0x170c
+#define IDS_EE_COM_UNSUPPORTED_SIG 0x170d
+#define IDS_EE_NOSYNCHRONIZED 0x170f
+#define IDS_EE_NDIRECT_BADNATL_THISCALL 0x1710
+#define IDS_EE_MULTIPLE_CALLCONV_UNSUPPORTED 0x1711
+
+#define IDS_EE_LOAD_BAD_MAIN_SIG 0x1712
+#define IDS_EE_COM_UNSUPPORTED_TYPE 0x1713
+
+#define IDS_EE_NOTNDIRECT 0x1719
+#define IDS_EE_RETHROW_NOT_ALLOWED 0x171d
+#define IDS_EE_INVALID_OLE_VARIANT 0x171e
+
+#define IDS_EE_FILE_NOT_FOUND 0x80070002
+#define IDS_EE_PATH_TOO_LONG 0x8007006F
+#define IDS_EE_PROC_NOT_FOUND 0x8007007F
+#define IDS_EE_ALREADY_EXISTS 0x800700B7
+#define IDS_EE_BAD_USER_PROFILE 0x800704E5
+#define IDS_INET_E_CANNOT_CONNECT 0x1799 // 0x800C0004
+#define IDS_INET_E_RESOURCE_NOT_FOUND 0x1a60 // 0x800C0005
+#define IDS_INET_E_CONNECTION_TIMEOUT 0x1a1e // 0x800C000B
+#define IDS_INET_E_SECURITY_PROBLEM 0x800C000E
+
+#define IDS_EE_TO_MANY_ARGUMENTS_IN_MAIN 0x1721
+#define IDS_EE_FAILED_TO_FIND_MAIN 0x1722
+#define IDS_EE_ILLEGAL_TOKEN_FOR_MAIN 0x1723
+#define IDS_EE_MAIN_METHOD_MUST_BE_STATIC 0x1724
+#define IDS_EE_MAIN_METHOD_HAS_INVALID_RTN 0x1725
+#define IDS_EE_VTABLECALLSNOTSUPPORTED 0x1726
+
+#define IDS_EE_BADMARSHALFIELD_STRING 0x1727
+#define IDS_EE_BADMARSHALFIELD_NOCUSTOMMARSH 0x1728
+#define IDS_EE_BADMARSHALFIELD_FIXEDARRAY_ZEROSIZE 0x172a
+#define IDS_EE_BADMARSHALFIELD_LAYOUTCLASS 0x172b
+#define IDS_EE_BADMARSHALFIELD_ARRAY 0x172c
+
+#define IDS_EE_BADMARSHALPARAM_NO_LPTSTR 0x172d
+
+#define IDS_EE_SAFEARRAYTYPEMISMATCH 0x1738
+#define IDS_EE_SAFEARRAYRANKMISMATCH 0x1739
+#define IDS_EE_BADMARSHAL_GENERIC 0x173a
+#define IDS_EE_BADMARSHAL_CHAR 0x173b
+#define IDS_EE_BADMARSHAL_BOOLEAN 0x173c
+#define IDS_EE_BADMARSHAL_I1 0x173d
+#define IDS_EE_BADMARSHAL_I2 0x173e
+#define IDS_EE_BADMARSHAL_I4 0x173f
+#define IDS_EE_BADMARSHAL_I8 0x1740
+#define IDS_EE_BADMARSHAL_I 0x1741
+#define IDS_EE_BADMARSHAL_R4 0x1742
+#define IDS_EE_BADMARSHAL_R8 0x1743
+#define IDS_EE_BADMARSHAL_PTR 0x1745
+#define IDS_EE_BADMARSHAL_NOLAYOUT 0x1746
+#define IDS_EE_BADMARSHALPARAM_STRING 0x1747
+#define IDS_EE_BADMARSHALPARAM_STRINGBUILDER 0x1748
+#define IDS_EE_BADMARSHAL_DELEGATE 0x1749
+#define IDS_EE_BADMARSHAL_FNPTR 0x174a
+#define IDS_EE_BADMARSHAL_INTERFACE 0x174b
+#define IDS_EE_BADMARSHAL_CLASS 0x174c
+#define IDS_EE_BADMARSHAL_VALUETYPE 0x174d
+#define IDS_EE_BADMARSHAL_OBJECT 0x174e
+#define IDS_EE_BADMARSHALFIELD_OBJECT 0x174f
+#define IDS_EE_BADMARSHALPARAM_DECIMAL 0x1750
+#define IDS_EE_BADMARSHAL_GUID 0x1751
+#define IDS_EE_BADMARSHAL_DATETIME 0x1753
+#define IDS_EE_BADMARSHAL_ARRAY 0x1754
+#define IDS_EE_BADMARSHAL_BADMANAGED 0x1756
+#define IDS_EE_SRC_OBJ_NOT_COMOBJECT 0x1757
+#define IDS_EE_CANNOT_COERCE_COMOBJECT 0x1759
+#define IDS_EE_BADMARSHAL_AUTOLAYOUT 0x175a
+#define IDS_EE_BADMARSHAL_RESTRICTION 0x175d
+#define IDS_EE_BADMARSHAL_ASANYRESTRICTION 0x175f
+#define IDS_EE_BADMARSHAL_VBBYVALSTRRESTRICTION 0x1760
+#define IDS_EE_BADMARSHAL_AWORESTRICTION 0x1761
+#define IDS_EE_BADMARSHAL_ARGITERATORRESTRICTION 0x1765
+#define IDS_EE_BADMARSHAL_HANDLEREFRESTRICTION 0x1766
+
+#define IDS_EE_ADUNLOAD_NOT_ALLOWED 0x1767
+
+#define IDS_CANNOT_MARSHAL 0x1770
+#define IDS_CANNOT_MARSHAL_RECURSIVE_DEF 0x1771
+#define IDS_EE_HASH_VAL_FAILED 0x1772
+
+
+#define IDS_CLASSLOAD_GENERAL 0x80131522
+#define IDS_CLASSLOAD_BADFORMAT 0x1774
+#define IDS_CLASSLOAD_BYREFARRAY 0x1775
+#define IDS_CLASSLOAD_BYREFLIKEARRAY 0x1776
+#define IDS_CLASSLOAD_MISSINGMETHOD 0x1777
+#define IDS_CLASSLOAD_STATICVIRTUAL 0x1778
+#define IDS_CLASSLOAD_REDUCEACCESS 0x1779
+#define IDS_CLASSLOAD_BADPINVOKE 0x177a
+#define IDS_CLASSLOAD_VALUECLASSTOOLARGE 0x177b
+#define IDS_CLASSLOAD_NOTIMPLEMENTED 0x177c
+#define IDS_CLASSLOAD_PARENTNULL 0x177d
+#define IDS_CLASSLOAD_PARENTINTERFACE 0x177e
+#define IDS_CLASSLOAD_INTERFACEOBJECT 0x177f
+#define IDS_CLASSLOAD_INTERFACENULL 0x1780
+#define IDS_CLASSLOAD_NOTINTERFACE 0x1781
+#define IDS_CLASSLOAD_VALUEINSTANCEFIELD 0x1782
+#define IDS_CLASSLOAD_EXPLICIT_GENERIC 0x1783
+#define IDS_CLASSLOAD_RANK_TOOLARGE 0x1785
+#define IDS_CLASSLOAD_BAD_UNMANAGED_RVA 0x1787
+#define IDS_CLASSLOAD_ENCLOSING 0x1789
+#define IDS_CLASSLOAD_EXPLICIT_LAYOUT 0x178a
+#define IDS_CLASSLOAD_SEALEDPARENT 0x178b
+#define IDS_CLASSLOAD_NOMETHOD_NAME 0x178c
+#define IDS_CLASSLOAD_BADSPECIALMETHOD 0x178e
+#define IDS_CLASSLOAD_MI_DECLARATIONNOTFOUND 0x178f
+#define IDS_CLASSLOAD_MI_MULTIPLEOVERRIDES 0x1790
+#define IDS_CLASSLOAD_MI_ACCESS_FAILURE 0x1791
+#define IDS_CLASSLOAD_MI_BADSIGNATURE 0x1793
+#define IDS_CLASSLOAD_MI_NOTIMPLEMENTED 0x1794
+#define IDS_CLASSLOAD_MI_MUSTBEVIRTUAL 0x1796
+#define IDS_CLASSLOAD_MISSINGMETHODRVA 0x1797
+#define IDS_CLASSLOAD_FIELDTOOLARGE 0x1798
+#define IDS_CLASSLOAD_CANTEXTEND 0x179a
+#define IDS_CLASSLOAD_ZEROSIZE 0x179b
+#define IDS_CLASSLOAD_TYPESPEC 0x179c
+#define IDS_CLASSLOAD_BAD_FIELD 0x179d
+#define IDS_CLASSLOAD_MI_ILLEGAL_BODY 0x179e
+#define IDS_CLASSLOAD_MI_ILLEGAL_TOKEN_BODY 0x17a0
+#define IDS_CLASSLOAD_MI_ILLEGAL_TOKEN_DECL 0x17a1
+#define IDS_CLASSLOAD_MI_SEALED_DECL 0x17a2
+#define IDS_CLASSLOAD_MI_FINAL_DECL 0x17a3
+#define IDS_CLASSLOAD_MI_NONVIRTUAL_DECL 0x17a4
+#define IDS_CLASSLOAD_MI_BODY_DECL_MISMATCH 0x17a5
+#define IDS_CLASSLOAD_MI_MISSING_SIG_BODY 0x17a6
+#define IDS_CLASSLOAD_MI_MISSING_SIG_DECL 0x17a7
+#define IDS_CLASSLOAD_MI_BADRETURNTYPE 0x17a8
+#define IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL 0x17a9
+
+#define IDS_CLASSLOAD_TOOMANYGENERICARGS 0x17ab
+#define IDS_ERROR 0x17b0
+#define IDS_DEBUG_SERVICE_CAPTION 0x17b4
+#define IDS_DEBUG_USERBREAKPOINT 0x17b6
+#define IDS_DEBUG_UNHANDLEDEXCEPTION 0x17b7
+#define IDS_DEBUG_UNHANDLEDEXCEPTION_IPC 0x17b8
+#define IDS_PERFORMANCEMON_FUNCNOTFOUND 0x17bb
+#define IDS_PERFORMANCEMON_FUNCNOTFOUND_TITLE 0x17bc
+#define IDS_PERFORMANCEMON_PSAPINOTFOUND 0x17bd
+#define IDS_PERFORMANCEMON_PSAPINOTFOUND_TITLE 0x17be
+
+#define IDS_DEBUG_UNHANDLED_EXCEPTION_MSG 0x17c0
+#define IDS_DEBUG_USER_BREAKPOINT_MSG 0x17c1
+
+#define IDS_INVALID_REDIM 0x17c3
+#define IDS_INVALID_PINVOKE_CALLCONV 0x17c4
+#define IDS_CLASSLOAD_NSTRUCT_EXPLICIT_OFFSET 0x17c7
+#define IDS_EE_BADPINVOKEFIELD_NOTMARSHALABLE 0x17c9
+#define IDS_WRONGSIZEARRAY_IN_NSTRUCT 0x17ca
+
+#define IDS_EE_INVALIDLCIDPARAM 0x17cd
+#define IDS_EE_BADMARSHAL_NESTEDARRAY 0x17ce
+#define IDS_EE_INVALIDCOMSOURCEITF 0x17d1
+#define IDS_EE_CANNOT_COERCE_BYREF_VARIANT 0x17d2
+#define IDS_EE_WRAPPER_MUST_HAVE_DEF_CONS 0x17d3
+#define IDS_EE_INVALID_STD_DISPID_NAME 0x17d4
+#define IDS_EE_NO_IDISPATCH_ON_TARGET 0x17d5
+#define IDS_EE_NON_STD_NAME_WITH_STD_DISPID 0x17d6
+#define IDS_EE_INVOKE_NEW_ENUM_INVALID_RETURN 0x17d7
+#define IDS_EE_COM_OBJECT_RELEASE_RACE 0x17d8
+#define IDS_EE_COM_OBJECT_NO_LONGER_HAS_WRAPPER 0x17d9
+#define IDS_EE_CALLBACK_NOT_CALLED_FROM_CCTOR 0x17da
+#define IDS_EE_CALLBACK_ALREADY_REGISTERED 0x17de
+#define IDS_EE_NDIRECT_BADNATL_CALLCONV 0x17df
+#define IDS_EE_CANNOTCAST 0x17e0
+#define IDS_EE_NOTISOMORPHIC 0x17e1
+
+#define IDS_EE_NOCUSTOMMARSHALER 0x17e7
+#define IDS_EE_SIZECONTROLOUTOFRANGE 0x17e8
+#define IDS_EE_SIZECONTROLBADTYPE 0x17e9
+#define IDS_EE_SAFEARRAYSZARRAYMISMATCH 0x17eb
+#define IDS_EE_INVALID_VT_FOR_CUSTOM_MARHALER 0x17ec
+#define IDS_EE_BAD_COMEXTENDS_CLASS 0x17ed
+
+#define IDS_EE_ERRORTITLE 0x17f0
+#define IDS_EE_ERRORMESSAGETEMPLATE 0x17f1
+
+#define IDS_EE_LOCAL_COGETCLASSOBJECT_FAILED 0x17f5
+
+#define IDS_EE_MISSING_FIELD 0x17f7
+#define IDS_EE_MISSING_METHOD 0x17f8
+
+#define IDS_EE_INTERFACE_NOT_DISPATCH_BASED 0x17f9
+
+#define IDS_EE_UNHANDLED_EXCEPTION 0x17fc
+#define IDS_EE_EXCEPTION_TOSTRING_FAILED 0x17fd
+
+#define IDS_CLASSLOAD_EQUIVALENTSTRUCTMETHODS 0x17fe
+#define IDS_CLASSLOAD_EQUIVALENTSTRUCTFIELDS 0x17ff
+
+#define IDS_EE_NO_IDISPATCH 0x1a02
+
+
+#define IDS_EE_SIGTOOCOMPLEX 0x1a03
+#define IDS_EE_STRUCTTOOCOMPLEX 0x1a04
+#define IDS_EE_STRUCTARRAYTOOLARGE 0x1a05
+#define IDS_EE_BADMARSHALFIELD_NOSTRINGBUILDER 0x1a06
+#define IDS_EE_NAME_UNKNOWN 0x1a07
+#define IDS_EE_NO_BACKING_CLASS_FACTORY 0x1a0b
+#define IDS_EE_NAME_UNKNOWN_UNQ 0x1a0c
+#define IDS_EE_STRING_TOOLONG 0x1a0d
+#define IDS_EE_VARARG_NOT_SUPPORTED 0x1a0f
+
+#define IDS_EE_INVALID_CA 0x1a10
+
+#define IDS_EE_THREAD_CANNOT_GET 0x1a15
+#define IDS_EE_THREAD_BAD_STATE 0x1a1b
+#define IDS_EE_THREAD_ABORT_WHILE_SUSPEND 0x1a1c
+
+#define IDS_EE_NOVARIANTRETURN 0x1a1d
+
+#define IDS_EE_METHOD_NOT_FOUND_ON_EV_PROV 0x1a24
+#define IDS_EE_BAD_COMEVENTITF_CLASS 0x1a25
+
+#define IDS_EE_COREXEMAIN2_FAILED_TITLE 0x1a2b
+#define IDS_EE_COREXEMAIN2_FAILED_TEXT 0x1a2c
+
+#define IDS_EE_ICUSTOMMARSHALERNOTIMPL 0x1a2e
+#define IDS_EE_GETINSTANCENOTIMPL 0x1a2f
+
+#define IDS_EE_BADMARSHAL_CUSTOMMARSHALER 0x1a30
+
+#define IDS_CLASSLOAD_COMIMPCANNOTHAVELAYOUT 0x1a31
+#define IDS_EE_INVALIDCOMDEFITF 0x1a32
+#define IDS_EE_COMDEFITFNOTSUPPORTED 0x1a33
+
+#define IDS_EE_CLASS_TO_VARIANT_TLB_NOT_REG 0x1a35
+#define IDS_EE_CANNOT_MAP_TO_MANAGED_VC 0x1a36
+
+#define IDS_EE_MARSHAL_UNMAPPABLE_CHAR 0x1a37
+
+#define IDS_EE_BADMARSHAL_SAFEHANDLENATIVETOCOM 0x1a3a
+#define IDS_EE_BADMARSHAL_ABSTRACTOUTSAFEHANDLE 0x1a3b
+#define IDS_EE_BADMARSHAL_RETURNSHCOMTONATIVE 0x1a3c
+#define IDS_EE_BADMARSHAL_SAFEHANDLE 0x1a3d
+
+#define IDS_EE_SAFEHANDLECLOSED 0x1a3f
+#define IDS_EE_SAFEHANDLECANNOTSETHANDLE 0x1a40
+
+#define IDS_EE_BADMARSHAL_ABSTRACTRETSAFEHANDLE 0x1a44
+#define IDS_EE_SH_IN_VARIANT_NOT_SUPPORTED 0x1a47
+
+#define IDS_EE_BADMARSHAL_SYSARRAY 0x1a48
+#define IDS_EE_VAR_WRAP_IN_VAR_NOT_SUPPORTED 0x1a49
+#define IDS_EE_RECORD_NON_SUPPORTED_FIELDS 0x1a4a
+
+#define IDS_CLASSLOAD_TYPEWRONGNUMGENERICARGS 0x1a4b
+#define IDS_CLASSLOAD_NSTRUCT_NEGATIVE_OFFSET 0x1a4d
+
+#define IDS_CLASSLOAD_INVALIDINSTANTIATION 0x1a59
+
+#define IDS_EE_CLASSLOAD_INVALIDINSTANTIATION 0x1a59
+#define IDS_EE_BADMARSHALFIELD_ZEROLENGTHFIXEDSTRING 0x1a5a
+
+#define IDS_EE_BADMARSHAL_CRITICALHANDLENATIVETOCOM 0x1a62
+#define IDS_EE_BADMARSHAL_ABSTRACTOUTCRITICALHANDLE 0x1a63
+#define IDS_EE_BADMARSHAL_RETURNCHCOMTONATIVE 0x1a64
+#define IDS_EE_BADMARSHAL_CRITICALHANDLE 0x1a65
+
+#define IDS_EE_BADMARSHAL_ABSTRACTRETCRITICALHANDLE 0x1a6a
+#define IDS_EE_CH_IN_VARIANT_NOT_SUPPORTED 0x1a6b
+
+#define IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_IMPLICIT_OVERRIDE 0x1a6f
+#define IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_IMPLICIT_IMPLEMENTATION 0x1a70
+#define IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_LOCAL_METHOD_IMPL 0x1a71
+#define IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_PARENT_METHOD_IMPL 0x1a72
+#define IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_INTERFACE_METHOD_IMPL 0x1a73
+
+#define IDS_EE_NDIRECT_BADNATL_VARARGS_CALLCONV 0x1a75
+
+#define IDS_CLASSLOAD_VARIANCE_IN_METHOD_ARG 0x1a79
+#define IDS_CLASSLOAD_VARIANCE_IN_METHOD_RESULT 0x1a7a
+#define IDS_CLASSLOAD_VARIANCE_IN_BASE 0x1a7b
+#define IDS_CLASSLOAD_VARIANCE_IN_INTERFACE 0x1a7c
+#define IDS_CLASSLOAD_VARIANCE_IN_CONSTRAINT 0x1a7d
+#define IDS_CLASSLOAD_VARIANCE_CLASS 0x1a7e
+#define IDS_CLASSLOAD_BADVARIANCE 0x1a7f
+
+#define IDS_CLASSLOAD_OVERLAPPING_INTERFACES 0x1a80
+#define IDS_CLASSLOAD_32BITCLRLOADING64BITASSEMBLY 0x1a81
+#define IDS_EE_ASSEMBLY_GETTYPE_CANNONT_HAVE_ASSEMBLY_SPEC 0x1a84
+
+#define IDS_EE_CANNOT_HAVE_ASSEMBLY_SPEC 0x1a86
+#define IDS_EE_NEEDS_ASSEMBLY_SPEC 0x1a87
+
+#define IDS_EE_FILELOAD_ERROR_GENERIC 0x1a88
+
+#define IDS_EE_BADMARSHAL_UNSUPPORTED_SIG 0x1a89
+#define IDS_EE_BADMARSHAL_STRINGARRAY 0x1a8a
+#define IDS_EE_BADMARSHAL_OBJECTARRAY 0x1a8b
+#define IDS_EE_BADMARSHAL_DATETIMEARRAY 0x1a8c
+#define IDS_EE_BADMARSHAL_DECIMALARRAY 0x1a8d
+#define IDS_EE_BADMARSHAL_SAFEHANDLEARRAY 0x1a8f
+#define IDS_EE_BADMARSHAL_CRITICALHANDLEARRAY 0x1a90
+#define IDS_EE_BADMARSHALFIELD_ERROR_MSG 0x1a92
+#define IDS_EE_BADMARSHAL_ERROR_MSG 0x1a93
+#define IDS_EE_COM_INVISIBLE_PARENT 0x1a97
+
+#define IDS_EE_REMOTE_COGETCLASSOBJECT_FAILED 0x1a98
+#define IDS_EE_CREATEINSTANCE_FAILED 0x1a99
+#define IDS_EE_CREATEINSTANCE_LIC_FAILED 0x1a9a
+
+#define IDS_EE_RCW_INVALIDCAST_ITF 0x1a9b
+#define IDS_EE_RCW_INVALIDCAST_EVENTITF 0x1a9c
+#define IDS_EE_RCW_INVALIDCAST_IENUMERABLE 0x1a9d
+#define IDS_EE_RCW_INVALIDCAST_MNGSTDITF 0x1a9e
+#define IDS_EE_RCW_INVALIDCAST_COMOBJ_TO_MD 0x1a9f
+#define IDS_EE_RCW_INVALIDCAST_TO_NON_COMOBJTYPE 0x1aa0
+#define IDS_EE_RCW_INVALIDCAST_MD_TO_MD 0x1aa1
+
+#define IDS_EE_GENERIC 0x1aa2
+#define IDS_EE_BADMARSHAL_GENERICS_RESTRICTION 0x1aa3
+
+#define IDS_EE_THREAD_ABORT 0x1aa4
+#define IDS_EE_THREAD_INTERRUPTED 0x1aa5
+#define IDS_EE_OUT_OF_MEMORY 0x1aa6
+
+#define IDS_EE_ATTEMPT_TO_CREATE_GENERIC_CCW 0x1aa9
+#define IDS_EE_ATTEMPT_TO_CREATE_NON_ABSTRACT_CCW 0x1aaa
+#define IDS_EE_COMIMPORT_METHOD_NO_INTERFACE 0x1aab
+#define IDS_EE_OUT_OF_MEMORY_WITHIN_RANGE 0x1aac
+#define IDS_EE_ARRAY_DIMENSIONS_EXCEEDED 0x1aad
+#define IDS_EE_OUT_OF_SYNCBLOCKS 0x1aae
+
+#define IDS_CLASSLOAD_MI_CANNOT_OVERRIDE 0x1ab3
+#define IDS_CLASSLOAD_COLLECTIBLEFIXEDVTATTR 0x1ab6
+#define IDS_CLASSLOAD_EQUIVALENTBADTYPE 0x1ab7
+#define IDS_EE_CODEEXECUTION_CONTAINSGENERICVAR 0x1abb
+#define IDS_CLASSLOAD_WRONGCPU 0x1abc
+#define IDS_EE_CREATEINSTANCEFROMAPP_FAILED 0x1abd
+
+#define IDS_IBC_MISSING_EXTERNAL_TYPE 0x1ac5
+#define IDS_IBC_MISSING_EXTERNAL_METHOD 0x1ac6
+#define IDS_EE_HWINTRINSIC_NGEN_DISALLOWED 0x1ac7
+#define IDS_CLASSLOAD_MI_FINAL_IMPL 0x1ac8
+#define IDS_CLASSLOAD_AMBIGUOUS_OVERRIDE 0x1ac9
+#define IDS_CLASSLOAD_UNSUPPORTED_DISPATCH 0x1aca
+#define IDS_CLASSLOAD_METHOD_NOT_IMPLEMENTED 0x1acb
+
+#define BFA_INVALID_TOKEN_TYPE 0x2001
+#define BFA_INVALID_TOKEN 0x2003
+#define BFA_UNABLE_TO_GET_NESTED_PROPS 0x2005
+#define BFA_METHOD_TOKEN_OUT_OF_RANGE 0x2006
+#define BFA_METHOD_NAME_TOO_LONG 0x2007
+#define BFA_METHOD_IN_A_ENUM 0x2009
+#define BFA_METHOD_WITH_NONZERO_RVA 0x200a
+#define BFA_ABSTRACT_METHOD_WITH_RVA 0x200b
+#define BFA_RUNTIME_METHOD_WITH_RVA 0x200c
+#define BFA_INTERNAL_METHOD_WITH_RVA 0x200d
+#define BFA_AB_METHOD_IN_AB_CLASS 0x200e
+#define BFA_NONVIRT_AB_METHOD 0x200f
+#define BFA_NONAB_NONCCTOR_METHOD_ON_INT 0x2010
+#define BFA_VIRTUAL_PINVOKE_METHOD 0x2011
+#define BFA_VIRTUAL_STATIC_METHOD 0x2012
+#define BFA_VIRTUAL_INSTANCE_CTOR 0x2013
+#define BFA_VIRTUAL_NONAB_INT_METHOD 0x2014
+#define BFA_NONVIRT_INST_INT_METHOD 0x2015
+#define BFA_SYNC_METHOD_IN_VT 0x2016
+#define BFA_NONSTATIC_GLOBAL_METHOD 0x2017
+#define BFA_GLOBAL_INST_CTOR 0x2018
+#define BFA_BAD_PLACE_FOR_GENERIC_METHOD 0x2019
+#define BFA_GENERIC_METHOD_RUNTIME_IMPL 0x201a
+#define BFA_BAD_RUNTIME_IMPL 0x201b
+#define BFA_BAD_FLAGS_ON_DELEGATE 0x201c
+#define BFA_UNKNOWN_DELEGATE_METHOD 0x201d
+#define BFA_GENERIC_METHODS_INST 0x201e
+#define BFA_BAD_FIELD_TOKEN 0x201f
+#define BFA_INVALID_FIELD_ACC_FLAGS 0x2020
+#define BFA_FIELD_LITERAL_AND_INIT 0x2021
+#define BFA_NONSTATIC_GLOBAL_FIELD 0x2022
+#define BFA_INSTANCE_FIELD_IN_INT 0x2023
+#define BFA_INSTANCE_FIELD_IN_ENUM 0x2024
+#define BFA_NONVIRT_NO_SEARCH 0x2025
+#define BFA_MANAGED_NATIVE_NYI 0x2027
+#define BFA_BAD_IMPL_FLAGS 0x2028
+#define BFA_BAD_UNMANAGED_ENTRY_POINT 0x2029
+#define BFA_GENCODE_NOT_BE_VARARG 0x202b
+#define BFA_CANNOT_INHERIT_FROM_DELEGATE 0x202c
+#define BFA_DELEGATE_CLASS_NOTSEALED 0x202d
+#define BFA_ENCLOSING_TYPE_NOT_FOUND 0x202e
+#define BFA_ILLEGAL_DELEGATE_METHOD 0x202f
+#define BFA_MISSING_DELEGATE_METHOD 0x2030
+#define BFA_MULT_TYPE_SAME_NAME 0x2031
+#define BFA_INVALID_METHOD_TOKEN 0x2032
+#define BFA_ECALLS_MUST_BE_IN_SYS_MOD 0x2034
+#define BFA_CANT_GET_CLASSLAYOUT 0x2035
+#define BFA_CALLCONV_NOT_LOCAL_SIG 0x2036
+#define BFA_BAD_CLASS_TOKEN 0x2037
+#define BFA_BAD_IL_RANGE 0x2038
+#define BFA_METHODDEF_WO_TYPEDEF_PARENT 0x2039
+#define BFA_METHODDEF_PARENT_NO_MEMBERS 0x203a
+#define BFA_INVALID_TOKEN_IN_MANIFESTRES 0x203c
+#define BFA_EMPTY_ASSEMDEF_NAME 0x203d
+#define BFA_BAD_IL 0x203e
+#define BFA_CLASSLOAD_VALUETYPEMISMATCH 0x203f
+#define BFA_METHODDECL_NOT_A_METHODDEF 0x2040
+#define BFA_DUPLICATE_DELEGATE_METHOD 0x2041
+#define BFA_ECALLS_MUST_HAVE_ZERO_RVA 0x2042
+#define BFA_METADATA_CORRUPT 0x2043
+#define BFA_BAD_SIGNATURE 0x2044
+#define BFA_TYPEREG_NAME_TOO_LONG 0x2045
+#define BFA_BAD_TYPEREF_TOKEN 0x2046
+#define BFA_BAD_CLASS_INT_CA 0x2047
+#define BFA_BAD_CLASS_INT_CA_FORMAT 0x2048
+#define BFA_BAD_COMPLUS_SIG 0x2049
+#define BFA_BAD_ELEM_IN_SIZEOF 0x204b
+#define BFA_IJW_IN_COLLECTIBLE_ALC 0x204c
+
+#define IDS_CLASSLOAD_INTERFACE_NO_ACCESS 0x204f
+
+#define BFA_BAD_CA_HEADER 0x2050
+#define BFA_BAD_STRING_TOKEN 0x2052
+#define BFA_BAD_STRING_TOKEN_RANGE 0x2053
+#define BFA_FIXUP_WRONG_PLATFORM 0x2054
+#define BFA_UNEXPECTED_GENERIC_TOKENTYPE 0x2055
+#define BFA_MDARRAY_BADRANK 0x2056
+#define BFA_SDARRAY_BADRANK 0x2057
+#define BFA_BAD_PACKING_SIZE 0x2058
+#define BFA_UNEXPECTED_ARRAY_TYPE 0x2059
+#define BFA_BAD_VISIBILITY 0x205a
+#define BFA_FAMILY_ON_GLOBAL 0x205b
+#define BFA_NOFIND_EXPORTED_TYPE 0x205c
+#define BFA_NOT_AN_ARRAY 0x205d
+#define BFA_EXPECTED_METHODDEF_OR_MEMBERREF 0x205e
+
+#define IDS_CLASSLOAD_BAD_METHOD_COUNT 0x2062
+#define IDS_CLASSLOAD_BAD_FIELD_COUNT 0x2063
+#define IDS_CLASSLOAD_MUST_BE_BYVAL 0x2064
+#define IDS_CLASSLOAD_BAD_VARIANCE_SIG 0x2065
+#define IDS_CLASSLOAD_VARIANCE_IN_DELEGATE 0x2066
+
+#define BFA_UNEXPECTED_FIELD_SIGNATURE 0x2068
+#define BFA_UNEXPECTED_TOKEN_AFTER_CLASSVALTYPE 0x2069
+#define BFA_FNPTR_CANNOT_BE_A_FIELD 0x206a
+#define BFA_FNPTR_CANNOT_BE_GENERIC 0x206b
+#define BFA_UNEXPECTED_TOKEN_AFTER_GENINST 0x206c
+#define BFA_TYPEDBYREFCANNOTHAVEBYREF 0x206e
+
+#define IDS_CLASSLOAD_MI_BAD_SIG 0x2070
+
+#define IDS_EE_TOOMANYFIELDS 0x2072
+
+#define IDS_EE_NDIRECT_GETPROCADDRESS_NONAME 0x2073
+#define IDS_EE_CLASS_CONSTRAINTS_VIOLATION 0x2076
+#define IDS_EE_METHOD_CONSTRAINTS_VIOLATION 0x2077
+#define IDS_CLASSLOAD_TOO_MANY_METHODS 0x2078
+#define IDS_CLASSLOAD_ENUM_EXTRA_GENERIC_TYPE_PARAM 0x2079
+
+#define IDS_CLASSLOAD_GENERICTYPE_RECURSIVE 0x207D
+#define IDS_EE_JIT_COMPILER_ERROR 0x207F
+
+#define IDS_ER_APPLICATION 0x2082
+#define IDS_ER_UNKNOWN 0x2083
+#define IDS_ER_FRAMEWORK_VERSION 0x2084
+#define IDS_ER_UNHANDLEDEXCEPTION 0x2085
+#define IDS_ER_UNHANDLEDEXCEPTIONMSG 0x2086
+#define IDS_ER_MANAGEDFAILFAST 0x2087
+#define IDS_ER_MANAGEDFAILFASTMSG 0x2088
+#define IDS_ER_UNMANAGEDFAILFAST 0x2089
+#define IDS_ER_STACK_OVERFLOW 0x208a
+#define IDS_ER_STACK 0x208b
+#define IDS_ER_WORDAT 0x208c
+#define IDS_ER_UNMANAGEDFAILFASTMSG 0x208d
+#define IDS_ER_UNHANDLEDEXCEPTIONINFO 0x208e
+#define IDS_ER_MESSAGE_TRUNCATE 0x208f
+
+#define IDS_EE_OBJECT_TO_VARIANT_NOT_SUPPORTED 0x2090
+#define IDS_EE_OBJECT_TO_ITF_NOT_SUPPORTED 0x2091
+
+#define IDS_EE_BADMARSHALFIELD_DECIMAL 0x2099
+
+#define IDS_EE_CANNOTCASTSAME 0x209a
+#define IDS_EE_CANNOTCAST_HELPER_BYTE 0x209b
+#define IDS_EE_CANNOTCAST_HELPER_PATH 0x209c
+
+// For ForwardInteropStubAttribute
+#ifdef FEATURE_COMINTEROP
+#define IDS_EE_INTEROP_STUB_CA_MUST_BE_WITHIN_SAME_ASSEMBLY 0x2107
+#define IDS_EE_INTEROP_STUB_CA_STUB_CLASS_MUST_NOT_BE_GENERIC 0x2108
+#define IDS_EE_INTEROP_STUB_CA_STUB_CLASS_MUST_NOT_BE_INTERFACE 0x2109
+#define IDS_EE_INTEROP_STUB_CA_STUB_METHOD_MISSING 0x2110
+#define IDS_EE_INTEROP_STUB_CA_NO_ACCESS_TO_STUB_METHOD 0x2111
+#endif
+
+#define IDS_EE_INTEROP_CODE_SIZE_COMMENT 0x2112
+
+#define BFA_REFERENCE_ASSEMBLY 0x2113
+
+#define IDS_E_FIELDACCESS 0x2114
+#define IDS_E_METHODACCESS 0x2115
+#define IDS_E_TYPEACCESS 0x2116
+
+// Profiler error messages for event log
+#define IDS_E_PROF_NO_CLSID 0x2500
+#define IDS_E_PROF_INTERNAL_INIT 0x2501
+#define IDS_E_PROF_BAD_CLSID 0x2502
+#define IDS_E_PROF_NO_CALLBACK_IFACE 0x2503
+#define IDS_E_PROF_CCI_FAILED 0x2504
+#define IDS_E_PROF_INIT_CALLBACK_FAILED 0x2505
+#define IDS_PROF_SUPPLEMENTARY_INFO 0x2506
+#define IDS_PROF_LOAD_COMPLETE 0x2507
+#define IDS_E_PROF_BAD_PATH 0x2508
+#define IDS_E_PROF_NOTIFICATION_DISABLED 0x2509
+#define IDS_PROF_ALREADY_LOADED 0x250A
+#define IDS_E_PROF_NOTIFICATION_LIMIT_EXCEEDED 0x250B
+#define IDS_E_PROF_NOT_ATTACHABLE 0x250E
+#define IDS_E_PROF_UNHANDLED_EXCEPTION_ON_LOAD 0x250F
+#define IDS_PROF_ATTACH_REQUEST_RECEIVED 0x2512
+#define IDS_PROF_DETACH_INITIATED 0x2513
+#define IDS_PROF_DETACH_COMPLETE 0x2514
+#define IDS_PROF_DETACH_THREAD_ERROR 0x2515
+#define IDS_PROF_CANCEL_ACTIVATION 0x2516
+#define IDS_PROF_V2PROFILER_DISABLED 0x2517
+#define IDS_PROF_V2PROFILER_ENABLED 0x2518
+#define IDS_PROF_PROFILER_DISABLED 0x251A
+
+#define IDS_ER_CODECONTRACT_FAILED 0x251B
+#define IDS_ER_CODECONTRACT_DETAILMSG 0x251C
+
+#define IDS_E_PROF_TIMEOUT_WAITING_FOR_CONCURRENT_GC 0x251D
+
+#define IDS_EE_CANNOTCAST_NOMARSHAL 0x2629
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
+#define IDS_EE_NATIVE_COM_WEAKREF_BAD_TYPE 0x262e
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
+
+#define IDS_HOST_ASSEMBLY_RESOLVER_ASSEMBLY_ALREADY_LOADED_IN_CONTEXT 0x2636
+#define IDS_HOST_ASSEMBLY_RESOLVER_DYNAMICALLY_EMITTED_ASSEMBLIES_UNSUPPORTED 0x2637
+#define IDS_HOST_ASSEMBLY_RESOLVER_INCOMPATIBLE_BINDING_CONTEXT 0x2638
+
+#define IDS_NATIVE_IMAGE_CANNOT_BE_LOADED_MULTIPLE_TIMES 0x263a
+
+#define IDS_CLASSLOAD_BYREFLIKE_STATICFIELD 0x263b
+#define IDS_CLASSLOAD_BYREFLIKE_INSTANCEFIELD 0x263c
+#define IDS_EE_NDIRECT_LOADLIB_LINUX 0x263e
+#define IDS_EE_NDIRECT_LOADLIB_MAC 0x263f
+#define IDS_EE_NDIRECT_GETPROCADDRESS_UNIX 0x2640
+#define IDS_EE_ERROR_COM 0x2641
+
+#define IDS_EE_CANNOT_SET_INITONLY_STATIC_FIELD 0x2643
+
+#define IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL 0x2644
+#define IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO 0x2645
+#define IDS_EE_BADMARSHAL_STRING_OUT 0x2646
+#define IDS_EE_BADMARSHAL_COPYCTORRESTRICTION 0x2647
+#define IDS_EE_BADMARSHAL_DELEGATE_TLB_INTERFACE 0x2649
+#define IDS_EE_THREAD_APARTMENT_NOT_SUPPORTED 0x264A
+#define IDS_EE_NO_IINSPECTABLE 0x264B
+#define IDS_EE_BADMARSHAL_MARSHAL_DISABLED 0x264C
+#define IDS_EE_NDIRECT_DISABLEDMARSHAL_SETLASTERROR 0x264D
+#define IDS_EE_NDIRECT_DISABLEDMARSHAL_LCID 0x264E
+#define IDS_EE_NDIRECT_DISABLEDMARSHAL_PRESERVESIG 0x264F
+#define IDS_EE_NDIRECT_DISABLEDMARSHAL_VARARGS 0x2650
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// SafeWrap.h
+//
+
+//
+// This file contains wrapper functions for Win32 API's that take SStrings
+// and use CLR-safe holders.
+//*****************************************************************************
+
+
+/*
+ Guidelines for SafeWrapper APIs:
+Most of these are 'common-sense', plus a few arbitrary decisions thrown in for
+consistency's sake.
+
+- THROWING: Throw on oom, but return all other failure codes.
+ The rationale here is that SString operations already throw, so to make these APIs
+ non-throwing would require an extra EX_TRY/EX_CATCH. Most callees will want to throw
+ on OOM anyways. So making these APIs non-throwing would mean an extra try/catch in
+ the caller + an extra check at the callee. We can eliminate that overhead and just make
+ it throwing.
+
+ Return non-oom failure codes because callees actually freqeuntly expect an API to fail.
+ For example, the callee will have special handling for file-not-found.
+
+ For convenience, you could add a no-throwing wrapper version of the API:
+ ClrGetEnvironmentVariable <-- default throws on oom.
+ ClrGetEnvironmentVariableNoThrow <-- never throws.
+
+- NAMING: Prefix the name with 'Clr', just like we do for win32 APIs going through hosting.
+
+- DON'T FORGET CONTRACTS: Most of these APIs will likely be Throws/GC_Notrigger.
+ Also use PRECONDITIONs + POSTCONDITIONS when possible.
+
+- SIGNATURES: Keep the method signture as close the the original win32 API as possible.
+ - Preserve the return type + value. (except allow it to throw on oom). If the return value
+ should be a holder, then use that as an out-parameter at the end of the argument list.
+ We don't want to return holders because that will cause the dtors to be called.
+ - For input strings use 'const SString &' instead of 'LPCWSTR'.
+ - Change ('out' string, length) pairs to 'SString &' (this is the convention the rest of the CLR uses for SStrings)
+ - Use Holders where appropriate.
+ - Preserve other parameters.
+
+- USE SSTRINGS TO AVOID BUFFER OVERRUN ISSUES: Repeated here for emphasis. Use SStrings when
+ applicable to make it very easy to verify the code does not have buffer overruns.
+ This will also simplify callsites from having to figure out the length of the output string.
+
+- USE YOUR BEST JUDGEMENT: The primary goal of these API wrappers is to embrace 'security-safe' practices.
+ Certainly take any additional steps to that goal. For example, it may make sense to eliminate
+ corner case inputs for a given API or to break a single confusing API up into several discrete and
+ move obvious APIs.
+
+*/
+#ifndef _safewrap_h_
+#define _safewrap_h_
+
+#include "holder.h"
+
+class SString;
+bool ClrGetEnvironmentVariable(LPCSTR szEnvVarName, SString & value);
+bool ClrGetEnvironmentVariableNoThrow(LPCSTR szEnvVarName, SString & value);
+void ClrGetModuleFileName(HMODULE hModule, SString & value);
+
+void ClrGetCurrentDirectory(SString & value);
+
+
+/* --------------------------------------------------------------------------- *
+ * Simple wrapper around WszFindFirstFile/WszFindNextFile
+ * --------------------------------------------------------------------------- */
+class ClrDirectoryEnumerator
+{
+ WIN32_FIND_DATAW data;
+ FindHandleHolder dirHandle;
+ BOOL fFindNext; // Skip FindNextFile first time around
+
+public:
+ ClrDirectoryEnumerator(LPCWSTR pBaseDirectory, LPCWSTR pMask = W("*"));
+ bool Next();
+
+ LPCWSTR GetFileName()
+ {
+ return data.cFileName;
+ }
+
+ DWORD GetFileAttributes()
+ {
+ return data.dwFileAttributes;
+ }
+
+ void Close()
+ {
+ dirHandle.Clear();
+ }
+};
+
+// Read a REG_SZ (null-terminated string) value from the registry. Throws.
+void ClrRegReadString(HKEY hKey, const SString & szValueName, SString & value);
+
+/* --------------------------------------------------------------------------- *
+ * Simple wrapper around RegisterEventSource/ReportEvent/DeregisterEventSource
+ * --------------------------------------------------------------------------- */
+// 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,
+ WORD wNumStrings,
+ LPCWSTR *lpStrings,
+ DWORD dwDataSize = 0,
+ LPVOID lpRawData = NULL);
+
+DWORD ClrReportEvent(
+ LPCWSTR pEventSource,
+ WORD wType,
+ WORD wCategory,
+ DWORD dwEventID,
+ PSID lpUserSid,
+ LPCWSTR pMessage);
+
+//*****************************************************************************
+// This provides a wrapper around GetFileSize() that forces it to fail
+// if the file is >4g and pdwHigh is NULL. Other than that, it acts like
+// the genuine GetFileSize().
+//
+//
+//*****************************************************************************
+DWORD inline SafeGetFileSize(HANDLE hFile, DWORD *pdwHigh)
+{
+ if (pdwHigh != NULL)
+ {
+ return ::GetFileSize(hFile, pdwHigh);
+ }
+ else
+ {
+ DWORD hi;
+ DWORD lo = ::GetFileSize(hFile, &hi);
+ if (lo == 0xffffffff && GetLastError() != NO_ERROR)
+ {
+ return lo;
+ }
+ // api succeeded. is the file too large?
+ if (hi != 0)
+ {
+ // there isn't really a good error to set here...
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return 0xffffffff;
+ }
+
+ if (lo == 0xffffffff)
+ {
+ // note that a success return of (hi=0,lo=0xffffffff) will be
+ // treated as an error by the caller. Again, that's part of the
+ // price of being a slacker and not handling the high dword.
+ // We'll set a lasterror for them to pick up.
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ return lo;
+ }
+
+}
+
+#endif // _safewrap_h_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// --------------------------------------------------------------------------------
+// SBuffer.h (Safe Buffer)
+//
+
+// --------------------------------------------------------------------------------
+
+// --------------------------------------------------------------------------------
+// SBuffer is a relatively safe way to manipulate a dynamically
+// allocated data buffer. An SBuffer is conceptually a simple array
+// of bytes. It maintains both a conceptual size and an actual allocated size.
+//
+// SBuffer provides safe access to the data buffer by providing rich high
+// level functionality (like insertion, deleteion, copying, comparison, and
+// iteration) without exposing direct pointers to its buffers.
+//
+// For interoperability, SBuffers can expose their buffers - either as readonly
+// by BYTE * or void * cases, or as writable by the OpenRawBuffer/CloseRawBuffer
+// entry points. Use of these should be limited wherever possible though; as there
+// is always a possibilility of buffer overrun.
+//
+// To mimimize heap allocations, the InlineSBuffer template will preallocate a fixed
+// size buffer inline with the SBuffer object itself. It will use this buffer unless
+// it needs a bigger one, in which case it transparently moves on to using the heap.
+// The StackSBuffer class instatiates the InlineSBuffer with a standard heuristic
+// stack preallocation size.
+//
+// SBuffer is "subclassable" to add content typeing to the buffer. See SArray and
+// SString for examples.
+// --------------------------------------------------------------------------------
+
+
+#ifndef _SBUFFER_H_
+#define _SBUFFER_H_
+
+#include "clrtypes.h"
+#include "iterator.h"
+#include "check.h"
+#include "daccess.h"
+#include "memoryrange.h"
+
+// ================================================================================
+// Macros for computing padding
+// ================================================================================
+
+#define ALIGNMENT(size) \
+ (( ((size)^((size)-1)) >> 1) +1)
+
+#define ALIGN(size, align) \
+ (((size)+((align)-1)) & ~((align)-1))
+
+#define PAD(size, align) \
+ (ALIGN((size), (align)) - (size))
+
+// ================================================================================
+// SBuffer : base class for safe buffers
+// ================================================================================
+
+typedef DPTR(class SBuffer) PTR_SBuffer;
+
+class SBuffer
+{
+ public:
+ //--------------------------------------------------------------------
+ // Flags and constants
+ //--------------------------------------------------------------------
+
+ enum ImmutableFlag
+ {
+ Immutable
+ };
+
+ enum PreallocFlag
+ {
+ Prealloc
+ };
+
+ //--------------------------------------------------------------------
+ // Types
+ //--------------------------------------------------------------------
+
+ public:
+ class CIterator;
+ friend class CIterator;
+
+ class Iterator;
+ friend class Iterator;
+
+ //--------------------------------------------------------------------
+ // Initializers and constructors
+ //--------------------------------------------------------------------
+
+ public:
+ // Constructors
+ SBuffer();
+ SBuffer(COUNT_T size);
+ SBuffer(const BYTE *buffer, COUNT_T size);
+ explicit SBuffer(const SBuffer &buffer);
+
+ // Immutable constructor should ONLY be used if buffer will
+ // NEVER BE FREED OR MODIFIED. PERIOD. .
+ SBuffer(ImmutableFlag immutable, const BYTE *buffer, COUNT_T size);
+
+ // Prealloc should be allocated inline with SBuffer - it must have the same
+ // lifetime as SBuffer's memory.
+ SBuffer(PreallocFlag prealloc, void *buffer, COUNT_T size);
+
+ ~SBuffer();
+
+ void Clear();
+
+ void Set(const SBuffer &buffer);
+ void Set(const BYTE *buffer, COUNT_T size);
+ void SetImmutable(const BYTE *buffer, COUNT_T size);
+
+ //--------------------------------------------------------------------
+ // Buffer size routines. A buffer has an externally visible size, but
+ // it also has an internal allocation size which may be larger.
+ //--------------------------------------------------------------------
+
+ // Get and set size of buffer. Note that the actual size of the
+ // internally allocated memory block may be bigger.
+ COUNT_T GetSize() const;
+ void SetSize(COUNT_T count);
+
+ // Grow size of buffer to maximum amount without reallocating.
+ void MaximizeSize();
+
+ //--------------------------------------------------------------------
+ // Buffer allocation routines
+ //--------------------------------------------------------------------
+
+ // Return the current available allocation space of the buffer.
+ COUNT_T GetAllocation() const;
+
+ // Preallocate some memory you expect to use. This can prevent
+ // multiple reallocations. Note this does not change the visible
+ // size of the buffer.
+ void Preallocate(COUNT_T allocation) const;
+
+ // Shrink memory usage of buffer to minimal amount. Note that
+ // this does not change the visible size of the buffer.
+ void Trim() const;
+
+ //--------------------------------------------------------------------
+ // Content manipulation routines
+ //--------------------------------------------------------------------
+
+ void Zero();
+ void Fill(BYTE value);
+ void Fill(const Iterator &to, BYTE value, COUNT_T size);
+
+ // Internal copy. "Copy" leaves from range as is; "Move"
+ // leaves from range in uninitialized state.
+ // (This distinction is more important when using from a
+ // typed wrapper than in the base SBuffer class.)
+ //
+ // NOTE: Copy vs Move is NOT based on whether ranges overlap
+ // or not. Ranges may overlap in either case.
+ //
+ // Note that both Iterators must be on THIS buffer.
+ void Copy(const Iterator &to, const CIterator &from, COUNT_T size);
+ void Move(const Iterator &to, const CIterator &from, COUNT_T size);
+
+ // External copy.
+ void Copy(const Iterator &i, const SBuffer &source);
+ void Copy(const Iterator &i, const void *source, COUNT_T size);
+ void Copy(void *dest, const CIterator &i, COUNT_T size);
+
+ // Insert bytes at the given iterator location.
+ void Insert(const Iterator &i, const SBuffer &source);
+ void Insert(const Iterator &i, COUNT_T size);
+
+ // Delete bytes at the given iterator location
+ void Delete(const Iterator &i, COUNT_T size);
+
+ // Replace bytes at the given iterator location
+ void Replace(const Iterator &i, COUNT_T deleteSize, const SBuffer &insert);
+ void Replace(const Iterator &i, COUNT_T deleteSize, COUNT_T insertSize);
+
+ // Compare entire buffer; return -1, 0, 1
+ int Compare(const SBuffer &compare) const;
+ int Compare(const BYTE *match, COUNT_T size) const;
+
+ // Compare entire buffer; return TRUE or FALSE
+ BOOL Equals(const SBuffer &compare) const;
+ BOOL Equals(const BYTE *match, COUNT_T size) const;
+
+ // Match portion of this buffer to given bytes; return TRUE or FALSE
+ BOOL Match(const CIterator &i, const SBuffer &match) const;
+ BOOL Match(const CIterator &i, const BYTE *match, COUNT_T size) const;
+
+ //--------------------------------------------------------------------
+ // Iterators
+ //
+ // Note that any iterator returned is not
+ // valid after any operation which may resize the buffer, unless
+ // the operation was performed on that particular iterator.
+ //--------------------------------------------------------------------
+
+ CIterator Begin() const;
+ CIterator End() const;
+
+ Iterator Begin();
+ Iterator End();
+
+ BYTE & operator[] (int index);
+ const BYTE & operator[] (int index) const;
+
+ //--------------------------------------------------------------------
+ // Raw buffer access
+ //
+ // Accessing a raw buffer via pointer is inherently more dangerous than
+ // other uses of this API, and should be avoided if at all possible.
+ // It is primarily provided for compatibility with existing APIs.
+ //
+ // Note that any buffer pointer returned is not
+ // valid after any operation which may resize the buffer.
+ //--------------------------------------------------------------------
+
+ // Casting operators return the existing buffer as
+ // a raw const pointer. Note that the pointer is valid only
+ // until the buffer is modified via an API.
+ operator const void *() const;
+ operator const BYTE *() const;
+
+ // To write directly to the SString's underlying buffer:
+ // 1) Call OpenRawBuffer() and pass it the count of bytes
+ // you need.
+ // 2) That returns a pointer to the raw buffer which you can write to.
+ // 3) When you are done writing to the pointer, call CloseBuffer()
+ // and pass it the count of bytes you actually wrote.
+ // The pointer from step 1 is now invalid.
+
+ // example usage:
+ // void GetInfo(SBuffer &buf)
+ // {
+ // BYTE *p = buf.OpenRawBuffer(3);
+ // OSGetSomeInfo(p, 3);
+ // buf.CloseRawBuffer();
+ // }
+
+ // You should open the buffer, write the data, and immediately close it.
+ // No sbuffer operations are valid while the buffer is opened.
+ //
+ // In a debug build, Open/Close will do lots of little checks to make sure
+ // you don't buffer overflow while it's opened. In a retail build, this
+ // is a very streamlined action.
+
+ // Open the raw buffer for writing count bytes
+ BYTE *OpenRawBuffer(COUNT_T maxCount);
+
+ // Call after OpenRawBuffer().
+
+ // Provide the count of bytes actually used. This will make sure the
+ // SBuffer's size is correct.
+ void CloseRawBuffer(COUNT_T actualCount);
+
+ // Close the buffer. Assumes that we completely filled the buffer
+ // that OpenRawBuffer() gave back.
+ void CloseRawBuffer();
+
+ //--------------------------------------------------------------------
+ // Check routines. These are typically used internally, but may be
+ // called externally if desired.
+ //--------------------------------------------------------------------
+
+ CHECK CheckBufferClosed() const;
+ static CHECK CheckSize(COUNT_T size);
+ static CHECK CheckAllocation(COUNT_T allocation);
+ CHECK CheckIteratorRange(const CIterator &i) const;
+ CHECK CheckIteratorRange(const CIterator &i, COUNT_T size) const;
+
+ CHECK Check() const;
+ CHECK Invariant() const;
+ CHECK InternalInvariant() const;
+
+ protected:
+
+ //--------------------------------------------------------------------
+ // Internal helper routines
+ //--------------------------------------------------------------------
+
+ // Preserve = preserve contents while reallocating
+ typedef enum
+ {
+ DONT_PRESERVE = 0,
+ PRESERVE = 1,
+ } Preserve;
+
+ void Resize(COUNT_T size, Preserve preserve = PRESERVE);
+ void ResizePadded(COUNT_T size, Preserve preserve = PRESERVE);
+ void TweakSize(COUNT_T size);
+ void ReallocateBuffer(COUNT_T allocation, Preserve preserve);
+ void EnsureMutable() const;
+
+ //--------------------------------------------------------------------
+ // We define some extra flags and fields for subclasses (these are specifically
+ // designed for SString, but use otherwise if desired.)
+ //--------------------------------------------------------------------
+
+ BOOL IsFlag1() const;
+ void SetFlag1();
+ void ClearFlag1();
+
+ BOOL IsFlag2() const;
+ void SetFlag2();
+ void ClearFlag2();
+
+ BOOL IsFlag3() const;
+ void SetFlag3();
+ void ClearFlag3();
+
+ INT GetRepresentationField() const;
+ void SetRepresentationField(int value);
+
+ protected:
+
+ //--------------------------------------------------------------------
+ // Flag access
+ //--------------------------------------------------------------------
+
+ BOOL IsAllocated() const;
+ void SetAllocated();
+ void ClearAllocated();
+
+ BOOL IsImmutable() const;
+ void SetImmutable();
+ void ClearImmutable();
+
+#if _DEBUG
+ BOOL IsOpened() const;
+ void SetOpened();
+ void ClearOpened();
+#endif
+
+ //--------------------------------------------------------------------
+ // Buffer management routines
+ //--------------------------------------------------------------------
+
+ // Allocate and free a memory buffer
+ BYTE *NewBuffer(COUNT_T allocation);
+ void DeleteBuffer(BYTE *buffer, COUNT_T allocation);
+
+ // Use existing buffer
+ BYTE *UseBuffer(BYTE *buffer, COUNT_T *allocation);
+
+ CHECK CheckBuffer(const BYTE* buffer, COUNT_T allocation) const;
+
+ // Manipulates contents of the buffer via the plugins below, but
+ // adds some debugging checks. Should always call through here rather
+ // than directly calling the extensibility points.
+ void DebugMoveBuffer(_Out_writes_bytes_(size) BYTE *to, BYTE *from, COUNT_T size);
+ void DebugCopyConstructBuffer(_Out_writes_bytes_(size) BYTE *to, const BYTE *from, COUNT_T size);
+ void DebugConstructBuffer(BYTE *buffer, COUNT_T size);
+ void DebugDestructBuffer(BYTE *buffer, COUNT_T size);
+
+ void DebugStompUnusedBuffer(BYTE *buffer, COUNT_T size);
+#ifdef _DEBUG
+ static BOOL EnsureGarbageCharOnly(const BYTE *buffer, COUNT_T size);
+#endif
+ CHECK CheckUnusedBuffer(const BYTE *buffer, COUNT_T size) const;
+
+#ifdef DACCESS_COMPILE
+public:
+
+ // Expose the raw Target address of the buffer to DAC.
+ // This does not do any marshalling. This can be useful if the caller wants to allocate the buffer on
+ // its own heap so that it can survive Flush calls.
+ MemoryRange DacGetRawBuffer() const
+ {
+ SUPPORTS_DAC;
+ PTR_VOID p = dac_cast<PTR_VOID>((TADDR) m_buffer);
+ return MemoryRange(p, GetSize());
+ }
+
+protected:
+
+ // Return a host copy of the buffer, allocated on the DAC heap (and thus invalidated at the next call to Flush).
+ void* DacGetRawContent(void) const
+ {
+ SUPPORTS_DAC;
+
+ // SBuffers are used in DAC in two ways - buffers in the host, and marshalled buffers from the target.
+ // This is a problem - we can't reason about the address space of the buffer statically, and instead rely on
+ // the dynamic usage (i.e. the methods are basically bifurcated into those you can use on host instances,
+ // and those you can use on marshalled copies).
+ // Ideally we'll have two versions of the SBuffer code - one that's marshalled (normal DACization) and one
+ // that isn't (host-only utility). This is the "dual-mode DAC problem".
+ // But this only affects a couple classes, and so for now we'll ignore the problem - causing a bunch of DacCop
+ // violations.
+ DACCOP_IGNORE(CastBetweenAddressSpaces, "SBuffer has the dual-mode DAC problem");
+ DACCOP_IGNORE(FieldAccess, "SBuffer has the dual-mode DAC problem");
+ TADDR bufAddr = (TADDR)m_buffer;
+
+ return DacInstantiateTypeByAddress(bufAddr, m_size, true);
+ }
+
+ void EnumMemoryRegions(CLRDataEnumMemoryFlags flags) const
+ {
+ SUPPORTS_DAC;
+
+ if (flags != CLRDATA_ENUM_MEM_TRIAGE)
+ {
+ DacEnumMemoryRegion((TADDR)m_buffer, m_size);
+ }
+ }
+#endif
+
+ //----------------------------------------------------------------------------
+ // Iterator base class
+ //----------------------------------------------------------------------------
+
+ friend class CheckedIteratorBase<SBuffer>;
+
+ class EMPTY_BASES_DECL Index : public CheckedIteratorBase<SBuffer>
+ {
+ friend class SBuffer;
+
+ friend class CIterator;
+ friend class Indexer<const BYTE, CIterator>;
+
+ friend class Iterator;
+ friend class Indexer<BYTE, Iterator>;
+
+ protected:
+ BYTE* m_ptr;
+
+ Index();
+ Index(SBuffer *container, SCOUNT_T index);
+ BYTE &GetAt(SCOUNT_T delta) const;
+ void Skip(SCOUNT_T delta);
+ SCOUNT_T Subtract(const Index &i) const;
+
+ CHECK DoCheck(SCOUNT_T delta) const;
+
+ void Resync(const SBuffer *container, BYTE *value) const;
+ };
+
+ public:
+
+ class EMPTY_BASES_DECL CIterator : public Index, public Indexer<const BYTE, CIterator>
+ {
+ friend class SBuffer;
+
+ public:
+ CIterator()
+ {
+ }
+
+ CIterator(const SBuffer *buffer, int index)
+ : Index(const_cast<SBuffer*>(buffer), index)
+ {
+ }
+ };
+
+ class EMPTY_BASES_DECL Iterator : public Index, public Indexer<BYTE, Iterator>
+ {
+ friend class SBuffer;
+
+ public:
+ operator const CIterator &() const
+ {
+ return *(const CIterator *)this;
+ }
+
+ operator CIterator &()
+ {
+ return *(CIterator *)this;
+ }
+
+ Iterator()
+ {
+ }
+
+ Iterator(SBuffer *buffer, int index)
+ : Index(buffer, index)
+ {
+ }
+
+ };
+
+
+ //----------------------------------------------------------------------------
+ // Member and data declarations
+ //----------------------------------------------------------------------------
+
+ private:
+ enum
+ {
+ REPRESENTATION_MASK = 0x07,
+ ALLOCATED = 0x08,
+ IMMUTABLE = 0x10,
+ OPENED = 0x20,
+ FLAG1 = 0x40,
+ FLAG2 = 0x80,
+ FLAG3 = 0x100,
+ };
+
+ COUNT_T m_size; // externally visible size
+ COUNT_T m_allocation; // actual allocated size
+ UINT32 m_flags; // @todo: steal flags from sizes
+
+ protected:
+ union {
+ BYTE *m_buffer;
+ WCHAR *m_asStr; // For debugging, view as a unicode string
+ };
+
+#if _DEBUG
+ protected:
+ // We will update the "revision" of the buffer every time it is potentially reallocation,
+ // so we can tell when iterators are no longer valid.
+ int m_revision;
+#endif
+};
+
+// ================================================================================
+// InlineSBuffer : Tlempate for an SBuffer with preallocated buffer space
+// ================================================================================
+
+#define BUFFER_ALIGNMENT 4
+
+template <COUNT_T size>
+class EMPTY_BASES_DECL InlineSBuffer : public SBuffer
+{
+ private:
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4200) // zero sized array
+#pragma warning(disable:4324) // don't complain if DECLSPEC_ALIGN actually pads
+ DECLSPEC_ALIGN(BUFFER_ALIGNMENT) BYTE m_prealloc[size];
+#pragma warning(pop)
+#else
+ // use UINT64 to get maximum alignment of the memory
+ UINT64 m_prealloc[ALIGN(size,sizeof(UINT64))/sizeof(UINT64)];
+#endif // _MSC_VER
+
+ public:
+ InlineSBuffer()
+ : SBuffer(Prealloc, (BYTE*)m_prealloc, size)
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+};
+
+
+// a 1K sized buffer filled with $ that we'll use in debug builds for verification
+#define GARBAGE_FILL_DWORD 0x24242424 // $$$$
+#define GARBAGE_FILL_BUFFER_ITEMS 16
+#define GARBAGE_FILL_BUFFER_SIZE GARBAGE_FILL_BUFFER_ITEMS*sizeof(DWORD)
+// ================================================================================
+// StackSBuffer : SBuffer with relatively large preallocated buffer for stack use
+// ================================================================================
+
+#define STACK_ALLOC 256
+
+typedef InlineSBuffer<STACK_ALLOC> StackSBuffer;
+
+// ================================================================================
+// Inline definitions
+// ================================================================================
+
+/// a wrapper for templates and such, that use "==".
+/// more expensive than a typical "==", though
+inline BOOL operator == (const SBuffer& b1,const SBuffer& b2)
+{
+ return b1.Equals(b2);
+};
+
+#include <sbuffer.inl>
+
+#endif // _SBUFFER_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+
+#ifndef _SBUFFER_INL_
+#define _SBUFFER_INL_
+
+#include "sbuffer.h"
+
+#if defined(_MSC_VER)
+#pragma inline_depth (20)
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4702) // Disable bogus unreachable code warning
+#endif // _MSC_VER
+
+inline SBuffer::SBuffer(PreallocFlag flag, void *buffer, COUNT_T size)
+ : m_size(0),
+ m_allocation(NULL),
+ m_flags(0),
+ m_buffer(NULL)
+{
+ CONTRACT_VOID
+ {
+ CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckSize(size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ m_buffer = UseBuffer((BYTE *) buffer, &size);
+ m_allocation = size;
+
+#ifdef _DEBUG
+ m_revision = 0;
+#endif
+
+ RETURN;
+}
+
+inline SBuffer::SBuffer()
+ : m_size(0),
+ m_allocation(0),
+ m_flags(0),
+ m_buffer(NULL)
+{
+ CONTRACT_VOID
+ {
+ CONSTRUCTOR_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+#ifdef _DEBUG
+ m_revision = 0;
+#endif
+
+ RETURN;
+}
+
+inline SBuffer::SBuffer(COUNT_T size)
+ : m_size(0),
+ m_allocation(0),
+ m_flags(0),
+ m_buffer(NULL)
+{
+ CONTRACT_VOID
+ {;
+ CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckSize(size));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Resize(size);
+
+#ifdef _DEBUG
+ m_revision = 0;
+#endif
+
+ RETURN;
+}
+
+inline SBuffer::SBuffer(const SBuffer &buffer)
+ : m_size(0),
+ m_allocation(0),
+ m_flags(0),
+ m_buffer(NULL)
+{
+ CONTRACT_VOID
+ {
+ CONSTRUCTOR_CHECK;
+ PRECONDITION(buffer.Check());
+ POSTCONDITION(Equals(buffer));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Set(buffer);
+
+#ifdef _DEBUG
+ m_revision = 0;
+#endif
+
+ RETURN;
+}
+
+inline SBuffer::SBuffer(const BYTE *buffer, COUNT_T size)
+ : m_size(0),
+ m_allocation(0),
+ m_flags(0),
+ m_buffer(NULL)
+{
+ CONTRACT_VOID
+ {
+ CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckSize(size));
+ POSTCONDITION(Equals(buffer, size));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Set(buffer, size);
+
+#ifdef _DEBUG
+ m_revision = 0;
+#endif
+
+ RETURN;
+}
+
+
+inline SBuffer::SBuffer(ImmutableFlag immutable, const BYTE *buffer, COUNT_T size)
+ : m_size(size),
+ m_allocation(size),
+ m_flags(IMMUTABLE),
+ m_buffer(const_cast<BYTE*>(buffer))
+{
+ CONTRACT_VOID
+ {
+ CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckSize(size));
+ POSTCONDITION(Equals(buffer, size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+#ifdef _DEBUG
+ m_revision = 0;
+#endif
+
+ RETURN;
+}
+
+inline SBuffer::~SBuffer()
+{
+ CONTRACT_VOID
+ {
+ NOTHROW;
+ DESTRUCTOR_CHECK;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (IsAllocated())
+ {
+ DeleteBuffer(m_buffer, m_allocation);
+ }
+
+#ifdef _DEBUG
+ m_revision = 0;
+#endif
+
+ RETURN;
+}
+
+inline void SBuffer::Set(const SBuffer &buffer)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(buffer.Check());
+ POSTCONDITION(Equals(buffer));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (buffer.IsImmutable()
+ && (IsImmutable() || m_allocation < buffer.GetSize()))
+ {
+ // Share immutable block rather than reallocate and copy
+ // (Note that we prefer to copy to our buffer if we
+ // don't have to reallocate it.)
+
+ if (IsAllocated())
+ DeleteBuffer(m_buffer, m_allocation);
+
+ m_size = buffer.m_size;
+ m_allocation = buffer.m_allocation;
+ m_buffer = buffer.m_buffer;
+ m_flags = buffer.m_flags;
+
+#if _DEBUG
+ // Increment our revision to invalidate iterators
+ m_revision++;
+#endif
+
+ }
+ else
+ {
+ Resize(buffer.m_size, DONT_PRESERVE);
+ EnsureMutable();
+
+ // PreFix seems to think it can choose m_allocation==0 and buffer.m_size > 0 here.
+ // From the code for Resize and EnsureMutable, this is clearly impossible.
+ PREFIX_ASSUME( (this->m_buffer != NULL) || (buffer.m_size == 0) );
+
+ MoveMemory(m_buffer, buffer.m_buffer, buffer.m_size);
+ }
+
+ RETURN;
+}
+
+inline void SBuffer::Set(const BYTE *buffer, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(buffer, size == 0 ? NULL_OK : NULL_NOT_OK));
+ PRECONDITION(CheckSize(size));
+ POSTCONDITION(Equals(buffer, size));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Resize(size);
+ EnsureMutable();
+
+ // PreFix seems to think it can choose m_allocation==0 and size > 0 here.
+ // From the code for Resize, this is clearly impossible.
+ PREFIX_ASSUME( (this->m_buffer != NULL) || (size == 0) );
+
+ MoveMemory(m_buffer, buffer, size);
+
+ RETURN;
+}
+
+inline void SBuffer::SetImmutable(const BYTE *buffer, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(buffer, size == 0 ? NULL_OK : NULL_NOT_OK));
+ PRECONDITION(CheckSize(size));
+ POSTCONDITION(Equals(buffer, size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ }
+ CONTRACT_END;
+
+ SBuffer temp(Immutable, buffer, size);
+
+ {
+ // This can't really throw
+ CONTRACT_VIOLATION(ThrowsViolation);
+ Set(temp);
+ }
+
+ RETURN;
+}
+
+inline COUNT_T SBuffer::GetSize() const
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ return m_size;
+}
+
+inline void SBuffer::SetSize(COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckSize(size));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Resize(size);
+
+ RETURN;
+}
+
+inline void SBuffer::MaximizeSize()
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (!IsImmutable())
+ Resize(m_allocation);
+
+ RETURN;
+}
+
+inline COUNT_T SBuffer::GetAllocation() const
+{
+ CONTRACT(COUNT_T)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ RETURN m_allocation;
+}
+
+inline void SBuffer::Preallocate(COUNT_T allocation) const
+{
+ CONTRACT_VOID
+ {
+ if (allocation) THROWS; else NOTHROW;
+ INSTANCE_CHECK;
+ PRECONDITION(CheckAllocation(allocation));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (allocation > m_allocation)
+ const_cast<SBuffer *>(this)->ReallocateBuffer(allocation, PRESERVE);
+
+ RETURN;
+}
+
+inline void SBuffer::Trim() const
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (!IsImmutable())
+ const_cast<SBuffer *>(this)->ReallocateBuffer(m_size, PRESERVE);
+
+ RETURN;
+}
+
+inline void SBuffer::Zero()
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ ZeroMemory(m_buffer, m_size);
+
+ RETURN;
+}
+
+inline void SBuffer::Fill(BYTE value)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ memset(m_buffer, value, m_size);
+
+ RETURN;
+}
+
+inline void SBuffer::Fill(const Iterator &i, BYTE value, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i, size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ memset(i.m_ptr, value, size);
+
+ RETURN;
+}
+
+inline void SBuffer::Copy(const Iterator &to, const CIterator &from, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(to, size));
+ PRECONDITION(CheckIteratorRange(from, size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ DebugDestructBuffer(to.m_ptr, size);
+
+ DebugCopyConstructBuffer(to.m_ptr, from.m_ptr, size);
+
+ RETURN;
+}
+
+inline void SBuffer::Move(const Iterator &to, const CIterator &from, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(to, size));
+ PRECONDITION(CheckIteratorRange(from, size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ DebugDestructBuffer(to.m_ptr, size);
+
+ DebugMoveBuffer(to.m_ptr, from.m_ptr, size);
+
+ DebugConstructBuffer(from.m_ptr, size);
+
+ RETURN;
+}
+
+inline void SBuffer::Copy(const Iterator &i, const SBuffer &source)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i, source.GetSize()));
+ PRECONDITION(source.Check());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ DebugDestructBuffer(i.m_ptr, source.m_size);
+
+ DebugCopyConstructBuffer(i.m_ptr, source.m_buffer, source.m_size);
+
+ RETURN;
+}
+
+inline void SBuffer::Copy(const Iterator &i, const void *source, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckSize(size));
+ PRECONDITION(CheckIteratorRange(i, size));
+ PRECONDITION(CheckPointer(source, size == 0 ? NULL_OK : NULL_NOT_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ DebugDestructBuffer(i.m_ptr, size);
+
+ DebugCopyConstructBuffer(i.m_ptr, (const BYTE *) source, size);
+
+ RETURN;
+}
+
+inline void SBuffer::Copy(void *dest, const CIterator &i, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckSize(size));
+ PRECONDITION(CheckIteratorRange(i, size));
+ PRECONDITION(CheckPointer(dest, size == 0 ? NULL_OK : NULL_NOT_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ memcpy(dest, i.m_ptr, size);
+
+ RETURN;
+}
+
+inline void SBuffer::Insert(const Iterator &i, const SBuffer &source)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ PRECONDITION(CheckIteratorRange(i,0));
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Replace(i, 0, source.GetSize());
+ Copy(i, source, source.GetSize());
+
+ RETURN;
+}
+
+inline void SBuffer::Insert(const Iterator &i, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ PRECONDITION(CheckIteratorRange(i,0));
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Replace(i, 0, size);
+
+ RETURN;
+}
+
+inline void SBuffer::Clear()
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Delete(Begin(), GetSize());
+
+ RETURN;
+}
+
+inline void SBuffer::Delete(const Iterator &i, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i, size));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Replace(i, size, 0);
+
+ RETURN;
+}
+
+inline void SBuffer::Replace(const Iterator &i, COUNT_T deleteSize, const SBuffer &insert)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i, deleteSize));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Replace(i, deleteSize, insert.GetSize());
+ Copy(i, insert, insert.GetSize());
+
+ RETURN;
+}
+
+inline int SBuffer::Compare(const SBuffer &compare) const
+{
+ CONTRACT(int)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(compare.Check());
+ POSTCONDITION(RETVAL == -1 || RETVAL == 0 || RETVAL == 1);
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ RETURN Compare(compare.m_buffer, compare.m_size);
+}
+
+inline int SBuffer::Compare(const BYTE *compare, COUNT_T size) const
+{
+ CONTRACT(int)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(compare));
+ PRECONDITION(CheckSize(size));
+ POSTCONDITION(RETVAL == -1 || RETVAL == 0 || RETVAL == 1);
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ COUNT_T smaller;
+ int equals;
+ int result;
+
+ if (m_size < size)
+ {
+ smaller = m_size;
+ equals = -1;
+ }
+ else if (m_size > size)
+ {
+ smaller = size;
+ equals = 1;
+ }
+ else
+ {
+ smaller = size;
+ equals = 0;
+ }
+
+ result = memcmp(m_buffer, compare, size);
+
+ if (result == 0)
+ RETURN equals;
+ else
+ RETURN result;
+}
+
+inline BOOL SBuffer::Equals(const SBuffer &compare) const
+{
+ CONTRACT(int)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(compare.Check());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ RETURN Equals(compare.m_buffer, compare.m_size);
+}
+
+inline BOOL SBuffer::Equals(const BYTE *compare, COUNT_T size) const
+{
+ CONTRACT(int)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(compare));
+ PRECONDITION(CheckSize(size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (m_size != size)
+ RETURN FALSE;
+ else
+ RETURN (memcmp(m_buffer, compare, size) == 0);
+}
+
+inline BOOL SBuffer::Match(const CIterator &i, const SBuffer &match) const
+{
+ CONTRACT(int)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(match.Check());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ RETURN Match(i, match.m_buffer, match.m_size);
+}
+
+inline BOOL SBuffer::Match(const CIterator &i, const BYTE *match, COUNT_T size) const
+{
+ CONTRACT(int)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(CheckPointer(match));
+ PRECONDITION(CheckSize(size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ COUNT_T remaining = (COUNT_T) (m_buffer + m_size - i.m_ptr);
+
+ if (remaining < size)
+ RETURN FALSE;
+
+ RETURN (memcmp(i.m_ptr, match, size) == 0);
+}
+
+//----------------------------------------------------------------------------
+// EnsureMutable
+// Ensures that the buffer is mutable
+//----------------------------------------------------------------------------
+inline void SBuffer::EnsureMutable() const
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckBufferClosed());
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (IsImmutable())
+ const_cast<SBuffer *>(this)->ReallocateBuffer(m_allocation, PRESERVE);
+
+ RETURN;
+}
+
+//----------------------------------------------------------------------------
+// Resize
+// Change the visible size of the buffer; realloc if necessary
+//----------------------------------------------------------------------------
+FORCEINLINE void SBuffer::Resize(COUNT_T size, Preserve preserve)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckSize(size));
+ POSTCONDITION(GetSize() == size);
+ POSTCONDITION(m_allocation >= GetSize());
+ POSTCONDITION(CheckInvariant(*this));
+ if (size > 0) THROWS; else NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+#ifdef _DEBUG
+ // Change our revision
+ m_revision++;
+#endif
+
+ SCOUNT_T delta = size - m_size;
+
+ if (delta < 0)
+ DebugDestructBuffer(m_buffer + size, -delta);
+
+ // Only actually allocate if we are growing
+ if (size > m_allocation)
+ ReallocateBuffer(size, preserve);
+
+ if (delta > 0)
+ DebugConstructBuffer(m_buffer + m_size, delta);
+
+ m_size = size;
+
+ RETURN;
+}
+
+//----------------------------------------------------------------------------
+// ResizePadded
+// Change the visible size of the buffer; realloc if necessary
+// add extra space to minimize further growth
+//----------------------------------------------------------------------------
+inline void SBuffer::ResizePadded(COUNT_T size, Preserve preserve)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckSize(size));
+ POSTCONDITION(GetSize() == size);
+ POSTCONDITION(m_allocation >= GetSize());
+ POSTCONDITION(CheckInvariant(*this));
+ if (size > 0) THROWS; else NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+#ifdef _DEBUG
+ // Change our revision
+ m_revision++;
+#endif
+
+ SCOUNT_T delta = size - m_size;
+
+ if (delta < 0)
+ DebugDestructBuffer(m_buffer + size, -delta);
+
+ // Only actually allocate if we are growing
+ if (size > m_allocation)
+ {
+ COUNT_T padded = (size*3)/2;
+
+ ReallocateBuffer(padded, preserve);
+ }
+
+ if (delta > 0)
+ DebugConstructBuffer(m_buffer + m_size, delta);
+
+ m_size = size;
+
+ RETURN;
+}
+
+//----------------------------------------------------------------------------
+// TweakSize
+// An optimized form of Resize, which can only adjust the size within the
+// currently allocated range, and never reallocates
+//----------------------------------------------------------------------------
+inline void SBuffer::TweakSize(COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckSize(size));
+ PRECONDITION(size <= GetAllocation());
+ POSTCONDITION(GetSize() == size);
+ POSTCONDITION(CheckInvariant(*this));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+#ifdef _DEBUG
+ // Change our revision
+ m_revision++;
+#endif
+
+ SCOUNT_T delta = size - m_size;
+
+ if (delta < 0)
+ DebugDestructBuffer(m_buffer + size, -delta);
+ else
+ DebugConstructBuffer(m_buffer + m_size, delta);
+
+ m_size = size;
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// SBuffer allocates all memory via NewBuffer & DeleteBuffer members.
+// If SBUFFER_CANARY_CHECKS is defined, NewBuffer will place Canaries at the start
+// and end of the buffer to detect overflows.
+//-----------------------------------------------------------------------------
+
+#ifdef _DEBUG
+#define SBUFFER_CANARY_CHECKS 1
+#endif
+
+#ifdef SBUFFER_CANARY_CHECKS
+
+// The value we place at the start/end of the buffer,
+static const UINT64 SBUFFER_CANARY_VALUE = UI64(0xD00BED00BED00BAA);
+
+// Expose the quantity of padding needed when providing a prealloced
+// buffer. This is an unrolled version of the actualAllocation calculated
+// below for use as a constant value for InlineSString<X> to use. It is
+// padded with one additional sizeof(SBUFFER_CANARY_VALUE) to account for
+// possible alignment problems issues (pre- and post-padding).
+#define SBUFFER_PADDED_SIZE(desiredUsefulSize) \
+ ((((SIZE_T)(desiredUsefulSize) + sizeof(SBUFFER_CANARY_VALUE) - 1) & \
+ ~(sizeof(SBUFFER_CANARY_VALUE)-1)) + 3 * sizeof(SBUFFER_CANARY_VALUE))
+
+#else // SBUFFER_CANARY_CHECKS
+
+#define SBUFFER_PADDED_SIZE(desiredUsefulSize) (desiredUsefulSize)
+
+#endif // SBUFFER_CANARY_CHECKS else
+
+// Must match expected guaranteed alignment of new []
+#ifdef ALIGN_ACCESS
+static const int SBUFFER_ALIGNMENT = ALIGN_ACCESS;
+#else
+// This is only 4 bytes on win98 and below
+static const int SBUFFER_ALIGNMENT = 4;
+#endif
+
+//----------------------------------------------------------------------------
+// Allocate memory, use canaries.
+//----------------------------------------------------------------------------
+inline BYTE *SBuffer::NewBuffer(COUNT_T allocation)
+{
+ CONTRACT(BYTE*)
+ {
+ PRECONDITION(CheckSize(allocation));
+ PRECONDITION(allocation > 0);
+ POSTCONDITION(CheckPointer(RETVAL));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+#ifdef SBUFFER_CANARY_CHECKS
+
+ COUNT_T alignPadding = AlignmentPad(allocation, sizeof(SBUFFER_CANARY_VALUE));
+ COUNT_T actualAllocation= sizeof(SBUFFER_CANARY_VALUE) + allocation + alignPadding + sizeof(SBUFFER_CANARY_VALUE);
+ BYTE *raw = new BYTE [actualAllocation];
+
+ *(UINT64*) raw = SBUFFER_CANARY_VALUE;
+ *(UINT64*) (raw + sizeof(SBUFFER_CANARY_VALUE) + allocation + alignPadding) = SBUFFER_CANARY_VALUE;
+
+ BYTE *buffer = raw + sizeof(SBUFFER_CANARY_VALUE);
+
+#else
+
+ BYTE *buffer = new BYTE [allocation];
+
+#endif
+
+ DebugStompUnusedBuffer(buffer, allocation);
+
+ CONSISTENCY_CHECK(CheckBuffer(buffer, allocation));
+
+ RETURN buffer;
+}
+
+//----------------------------------------------------------------------------
+// Use existing memory, use canaries.
+//----------------------------------------------------------------------------
+inline BYTE *SBuffer::UseBuffer(BYTE *buffer, COUNT_T *allocation)
+{
+ CONTRACT(BYTE*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC_HOST_ONLY;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckSize(*allocation));
+// POSTCONDITION(CheckPointer(RETVAL));
+ POSTCONDITION(CheckSize(*allocation));
+ }
+ CONTRACT_END;
+
+#ifdef SBUFFER_CANARY_CHECKS
+
+ COUNT_T prepad = AlignmentPad((SIZE_T) buffer, sizeof(SBUFFER_CANARY_VALUE));
+ COUNT_T postpad = AlignmentTrim((SIZE_T) buffer+*allocation, sizeof(SBUFFER_CANARY_VALUE));
+
+ SCOUNT_T usableAllocation = *allocation - prepad - sizeof(SBUFFER_CANARY_VALUE) - sizeof(SBUFFER_CANARY_VALUE) - postpad;
+ if (usableAllocation <= 0)
+ {
+ buffer = NULL;
+ *allocation = 0;
+ }
+ else
+ {
+ BYTE *result = buffer + prepad + sizeof(SBUFFER_CANARY_VALUE);
+
+ *(UINT64*) (buffer + prepad) = SBUFFER_CANARY_VALUE;
+ *(UINT64*) (buffer + prepad + sizeof(SBUFFER_CANARY_VALUE) + usableAllocation) = SBUFFER_CANARY_VALUE;
+
+ buffer = result;
+ *allocation = usableAllocation;
+ }
+
+#endif
+
+ DebugStompUnusedBuffer(buffer, *allocation);
+
+ CONSISTENCY_CHECK(CheckBuffer(buffer, *allocation));
+
+ RETURN buffer;
+}
+
+//----------------------------------------------------------------------------
+// Free memory allocated by NewHelper
+//----------------------------------------------------------------------------
+inline void SBuffer::DeleteBuffer(BYTE *buffer, COUNT_T allocation)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CheckSize(allocation));
+ POSTCONDITION(CheckPointer(buffer));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ CONSISTENCY_CHECK(CheckBuffer(buffer, allocation));
+
+#ifdef SBUFFER_CANARY_CHECKS
+
+ delete [] (buffer - sizeof(SBUFFER_CANARY_VALUE));
+
+#else
+
+ delete [] buffer;
+
+#endif
+
+ RETURN;
+}
+
+//----------------------------------------------------------------------------
+// Check the buffer at the given address. The memory must have been a pointer
+// returned by NewHelper.
+//----------------------------------------------------------------------------
+inline CHECK SBuffer::CheckBuffer(const BYTE *buffer, COUNT_T allocation) const
+{
+ CONTRACT_CHECK
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ PRECONDITION(CheckPointer(buffer));
+ }
+ CONTRACT_CHECK_END;
+
+ if (allocation > 0)
+ {
+#ifdef SBUFFER_CANARY_CHECKS
+ const BYTE *raw = buffer - sizeof(SBUFFER_CANARY_VALUE);
+
+ COUNT_T alignPadding = ((allocation + (sizeof(SBUFFER_CANARY_VALUE) - 1)) & ~((sizeof(SBUFFER_CANARY_VALUE) - 1))) - allocation;
+
+ CHECK_MSG(*(UINT64*) raw == SBUFFER_CANARY_VALUE, "SBuffer underflow");
+ CHECK_MSG(*(UINT64*) (raw + sizeof(SBUFFER_CANARY_VALUE) + allocation + alignPadding) == SBUFFER_CANARY_VALUE, "SBuffer overflow");
+
+#endif
+
+ CHECK_MSG((((SIZE_T)buffer) & (SBUFFER_ALIGNMENT-1)) == 0, "SBuffer not properly aligned");
+ }
+
+ CHECK_OK;
+}
+
+
+inline BYTE *SBuffer::OpenRawBuffer(COUNT_T size)
+{
+ CONTRACT(BYTE*)
+ {
+#if _DEBUG
+ PRECONDITION_MSG(!IsOpened(), "Can't nest calls to OpenBuffer()");
+#endif
+ PRECONDITION(CheckSize(size));
+ POSTCONDITION(GetSize() == size);
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ Resize(size);
+ EnsureMutable();
+
+#if _DEBUG
+ SetOpened();
+#endif
+
+ RETURN m_buffer;
+}
+
+//----------------------------------------------------------------------------
+// Close an open buffer. Assumes that we wrote exactly number of characters
+// we requested in OpenBuffer.
+//----------------------------------------------------------------------------
+inline void SBuffer::CloseRawBuffer()
+{
+ CONTRACT_VOID
+ {
+#if _DEBUG
+ PRECONDITION(IsOpened());
+#endif
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ CloseRawBuffer(m_size);
+
+ RETURN;
+}
+
+//----------------------------------------------------------------------------
+// CloseBuffer() tells the SBuffer that we're done using the unsafe buffer.
+// finalSize is the count of bytes actually used (so we can set m_count).
+// This is important if we request a buffer larger than what we actually
+// used.
+//----------------------------------------------------------------------------
+inline void SBuffer::CloseRawBuffer(COUNT_T finalSize)
+{
+ CONTRACT_VOID
+ {
+#if _DEBUG
+ PRECONDITION_MSG(IsOpened(), "Can only CloseRawBuffer() after a call to OpenRawBuffer()");
+#endif
+ PRECONDITION(CheckSize(finalSize));
+ PRECONDITION_MSG(finalSize <= GetSize(), "Can't use more characters than requested via OpenRawBuffer()");
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+#if _DEBUG
+ ClearOpened();
+#endif
+
+ TweakSize(finalSize);
+
+ CONSISTENCY_CHECK(CheckBuffer(m_buffer, m_allocation));
+
+ RETURN;
+}
+
+inline SBuffer::operator const void *() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (void *) m_buffer;
+}
+
+inline SBuffer::operator const BYTE *() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return m_buffer;
+}
+
+inline BYTE &SBuffer::operator[](int index)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_buffer[index];
+}
+
+inline const BYTE &SBuffer::operator[](int index) const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_buffer[index];
+}
+
+inline SBuffer::Iterator SBuffer::Begin()
+{
+ CONTRACT(SBuffer::Iterator)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ // This is a bit unfortunate to have to do here, but it's our
+ // last opportunity before possibly doing a *i= with the iterator
+ EnsureMutable();
+
+ RETURN Iterator(this, 0);
+}
+
+inline SBuffer::Iterator SBuffer::End()
+{
+ CONTRACT(SBuffer::Iterator)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // This is a bit unfortunate to have to do here, but it's our
+ // last opportunity before possibly doing a *i= with the iterator
+ EnsureMutable();
+
+ RETURN Iterator(this, m_size);
+}
+
+inline SBuffer::CIterator SBuffer::Begin() const
+{
+ CONTRACT(SBuffer::CIterator)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ RETURN SBuffer::CIterator(this, 0);
+}
+
+inline SBuffer::CIterator SBuffer::End() const
+{
+ CONTRACT(SBuffer::CIterator)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ RETURN CIterator(const_cast<SBuffer*>(this), m_size);
+}
+
+inline BOOL SBuffer::IsAllocated() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (m_flags & ALLOCATED) != 0;
+}
+
+inline void SBuffer::SetAllocated()
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ m_flags |= ALLOCATED;
+}
+
+inline void SBuffer::ClearAllocated()
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ m_flags &= ~ALLOCATED;
+}
+
+inline BOOL SBuffer::IsImmutable() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (m_flags & IMMUTABLE) != 0;
+}
+
+inline void SBuffer::SetImmutable()
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ m_flags |= IMMUTABLE;
+}
+
+inline void SBuffer::ClearImmutable()
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ m_flags &= ~IMMUTABLE;
+}
+
+inline BOOL SBuffer::IsFlag1() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (m_flags & FLAG1) != 0;
+}
+
+inline void SBuffer::SetFlag1()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ m_flags |= FLAG1;
+}
+
+inline void SBuffer::ClearFlag1()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_flags &= ~FLAG1;
+}
+
+inline BOOL SBuffer::IsFlag2() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (m_flags & FLAG2) != 0;
+}
+
+inline void SBuffer::SetFlag2()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_flags |= FLAG2;
+}
+
+inline void SBuffer::ClearFlag2()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_flags &= ~FLAG2;
+}
+
+inline BOOL SBuffer::IsFlag3() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (m_flags & FLAG3) != 0;
+}
+
+inline void SBuffer::SetFlag3()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_flags |= FLAG3;
+}
+
+inline void SBuffer::ClearFlag3()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ m_flags &= ~FLAG3;
+}
+
+inline int SBuffer::GetRepresentationField() const
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ return (m_flags & REPRESENTATION_MASK);
+}
+
+inline void SBuffer::SetRepresentationField(int value)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION((value & ~REPRESENTATION_MASK) == 0);
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ m_flags &= ~REPRESENTATION_MASK;
+ m_flags |= value;
+
+ RETURN;
+}
+
+#if _DEBUG
+inline BOOL SBuffer::IsOpened() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (m_flags & OPENED) != 0;
+}
+
+inline void SBuffer::SetOpened()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_flags |= OPENED;
+}
+
+inline void SBuffer::ClearOpened()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_flags &= ~OPENED;
+}
+#endif
+
+inline void SBuffer::DebugMoveBuffer(_Out_writes_bytes_(size) BYTE *to, BYTE *from, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(to, size == 0 ? NULL_OK : NULL_NOT_OK));
+ PRECONDITION(CheckPointer(from, size == 0 ? NULL_OK : NULL_NOT_OK));
+ PRECONDITION(CheckSize(size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (size == 0) // special case
+ RETURN;
+
+ // Handle overlapping ranges
+ if (to > from && to < from + size)
+ CONSISTENCY_CHECK(CheckUnusedBuffer(from + size, (COUNT_T) (to - from)));
+ else if (to < from && to + size > from)
+ CONSISTENCY_CHECK(CheckUnusedBuffer(to, (COUNT_T) (from - to)));
+ else
+ CONSISTENCY_CHECK(CheckUnusedBuffer(to, size));
+
+ memmove(to, from, size);
+
+ // Handle overlapping ranges
+ if (to > from && to < from + size)
+ DebugStompUnusedBuffer(from, (COUNT_T) (to - from));
+ else if (to < from && to + size > from)
+ DebugStompUnusedBuffer(to + size, (COUNT_T) (from - to));
+ else
+ DebugStompUnusedBuffer(from, size);
+
+ RETURN;
+}
+
+inline void SBuffer::DebugCopyConstructBuffer(_Out_writes_bytes_(size) BYTE *to, const BYTE *from, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(to, size == 0 ? NULL_OK : NULL_NOT_OK));
+ PRECONDITION(CheckPointer(from, size == 0 ? NULL_OK : NULL_NOT_OK));
+ PRECONDITION(CheckSize(size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (size != 0) {
+ CONSISTENCY_CHECK(CheckUnusedBuffer(to, size));
+ memmove(to, from, size);
+ }
+
+ RETURN;
+}
+
+inline void SBuffer::DebugConstructBuffer(BYTE *buffer, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(buffer, size == 0 ? NULL_OK : NULL_NOT_OK));
+ PRECONDITION(CheckSize(size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ DEBUG_ONLY;
+ }
+ CONTRACT_END;
+
+ if (size != 0) {
+ CONSISTENCY_CHECK(CheckUnusedBuffer(buffer, size));
+ }
+
+ RETURN;
+}
+
+inline void SBuffer::DebugDestructBuffer(BYTE *buffer, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(buffer, size == 0 ? NULL_OK : NULL_NOT_OK));
+ PRECONDITION(CheckSize(size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ DEBUG_ONLY;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (size != 0)
+ {
+ DebugStompUnusedBuffer(buffer, size);
+ }
+
+ RETURN;
+}
+
+static const BYTE GARBAGE_FILL_CHARACTER = '$';
+
+extern const DWORD g_garbageFillBuffer[];
+
+inline void SBuffer::DebugStompUnusedBuffer(BYTE *buffer, COUNT_T size)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(buffer, size == 0 ? NULL_OK : NULL_NOT_OK));
+ PRECONDITION(CheckSize(size));
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ DEBUG_ONLY;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+#if _DEBUG
+ if (!IsImmutable()
+ || buffer < m_buffer || buffer > m_buffer + m_allocation) // Allocating a new buffer
+ {
+ // Whack the memory
+ if (size > GARBAGE_FILL_BUFFER_SIZE) size = GARBAGE_FILL_BUFFER_SIZE;
+ memset(buffer, GARBAGE_FILL_CHARACTER, size);
+ }
+#endif
+
+ RETURN;
+}
+
+#if _DEBUG
+inline BOOL SBuffer::EnsureGarbageCharOnly(const BYTE *buffer, COUNT_T size)
+{
+ LIMITED_METHOD_CONTRACT;
+ BOOL bRet = TRUE;
+ if (size > GARBAGE_FILL_BUFFER_SIZE)
+ {
+ size = GARBAGE_FILL_BUFFER_SIZE;
+ }
+ if (bRet && size > 0)
+ {
+ bRet &= (memcmp(buffer, g_garbageFillBuffer, size) == 0);
+ }
+ return bRet;
+}
+#endif
+
+inline CHECK SBuffer::CheckUnusedBuffer(const BYTE *buffer, COUNT_T size) const
+{
+ WRAPPER_NO_CONTRACT;
+ // This check is too expensive.
+#if 0 // _DEBUG
+ if (!IsImmutable()
+ || buffer < m_buffer || buffer > m_buffer + m_allocation) // Allocating a new buffer
+ {
+ if (!SBuffer::EnsureGarbageCharOnly(buffer, size))
+ {
+ CHECK_FAIL("Overwrite of unused buffer region found");
+ }
+ }
+#endif
+ CHECK_OK;
+}
+
+inline CHECK SBuffer::Check() const
+{
+ WRAPPER_NO_CONTRACT;
+ CHECK(CheckBufferClosed());
+ CHECK_OK;
+}
+
+inline CHECK SBuffer::Invariant() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ CHECK_OK;
+}
+
+inline CHECK SBuffer::InternalInvariant() const
+{
+ WRAPPER_NO_CONTRACT;
+ CHECK(m_size <= m_allocation);
+
+ CHECK(CheckUnusedBuffer(m_buffer + m_size, m_allocation - m_size));
+
+ if (IsAllocated())
+ CHECK(CheckBuffer(m_buffer, m_allocation));
+
+ CHECK_OK;
+}
+
+inline CHECK SBuffer::CheckBufferClosed() const
+{
+ WRAPPER_NO_CONTRACT;
+#if _DEBUG
+ CHECK_MSG(!IsOpened(), "Cannot use buffer API while raw open is in progress");
+#endif
+ CHECK_OK;
+}
+
+inline CHECK SBuffer::CheckSize(COUNT_T size)
+{
+ LIMITED_METHOD_CONTRACT;
+ // !todo: add any range checking here
+ CHECK_OK;
+}
+
+inline CHECK SBuffer::CheckAllocation(COUNT_T size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // !todo: add any range checking here
+ CHECK_OK;
+}
+
+inline CHECK SBuffer::CheckIteratorRange(const CIterator &i) const
+{
+ WRAPPER_NO_CONTRACT;
+ CHECK(i.Check());
+ CHECK(i.CheckContainer(this));
+ CHECK(i >= Begin());
+ CHECK(i < End());
+ CHECK_OK;
+}
+
+inline CHECK SBuffer::CheckIteratorRange(const CIterator &i, COUNT_T size) const
+{
+ WRAPPER_NO_CONTRACT;
+ CHECK(i.Check());
+ CHECK(i.CheckContainer(this));
+ CHECK(i >= Begin());
+ CHECK(i + size <= End());
+ CHECK_OK;
+}
+
+inline SBuffer::Index::Index()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ m_ptr = NULL;
+}
+
+inline SBuffer::Index::Index(SBuffer *container, SCOUNT_T index)
+ : CheckedIteratorBase<SBuffer>(container)
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ m_ptr = container->m_buffer + index;
+}
+
+inline BYTE &SBuffer::Index::GetAt(SCOUNT_T delta) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return m_ptr[delta];
+}
+
+inline void SBuffer::Index::Skip(SCOUNT_T delta)
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ m_ptr += delta;
+}
+
+inline SCOUNT_T SBuffer::Index::Subtract(const Index &i) const
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ return (SCOUNT_T) (m_ptr - i.m_ptr);
+}
+
+inline CHECK SBuffer::Index::DoCheck(SCOUNT_T delta) const
+{
+ WRAPPER_NO_CONTRACT;
+#if _DEBUG
+ CHECK(m_ptr + delta >= GetContainerDebug()->m_buffer);
+ CHECK(m_ptr + delta < GetContainerDebug()->m_buffer + GetContainerDebug()->m_size);
+#endif
+ CHECK_OK;
+}
+
+inline void SBuffer::Index::Resync(const SBuffer *buffer, BYTE *value) const
+{
+ CONTRACT_VOID
+ {
+ // INSTANCE_CHECK - Iterator is out of sync with its object now by definition
+ POSTCONDITION(CheckPointer(this));
+ PRECONDITION(CheckPointer(buffer));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ const_cast<Index*>(this)->CheckedIteratorBase<SBuffer>::Resync(const_cast<SBuffer*>(buffer));
+ const_cast<Index*>(this)->m_ptr = value;
+
+ RETURN;
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+#endif // _SBUFFER_INL_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#ifndef SECURITYUTIL_H
+#define SECURITYUTIL_H
+
+#include "winnt.h"
+
+// Security utility class. This is currently used by the debugger right-side and dbgshim to figure out the
+// SECURITY_ATTRIBUTES to use on various IPC objects (named events, etc.).
+// This is pretty debugger specific, and so perhaps doesn't actually belong in utilcode (that's just the most
+// convenient way to share it between mscordbi and dbgshim.dll). This is also a pretty big mess. All of
+// this ACL craziness is already gone in Arrowhead, so it's not a high priority to clean this up.
+class SecurityUtil
+{
+public:
+
+ //
+ // This will generate ACL containing the current process and
+ // an allowed ACE on the target process of the given pid.
+ //
+ // Host should free returned *ppACL by calling FreeACL
+ //
+ static HRESULT GetACLOfPid(DWORD pid, PACL *ppACL);
+
+ static void FreeACL(PACL pACL);
+
+ static HRESULT GetMandatoryLabelFromProcess(HANDLE hProcess, LPBYTE * ppbLabel);
+ static DWORD * GetIntegrityLevelFromMandatorySID(PSID psidIntegrityLevelLabel);
+
+ // instance functions. SecurityUtil is used to minimized memory allocation when converting
+ // pACL to SECURITY_ATTRIBUTES
+ // The needed memory to hold SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR are embedded
+ // in the SecurityUtil instance.
+ //
+ SecurityUtil(PACL pACL);
+ ~SecurityUtil();
+ HRESULT Init();
+ HRESULT Init(HANDLE pid);
+ HRESULT GetSA(SECURITY_ATTRIBUTES **PPSA);
+private:
+ HRESULT SetSecurityDescriptorMandatoryLabel(PSID psidIntegrityLevelLabel);
+ SECURITY_ATTRIBUTES m_SA;
+ SECURITY_DESCRIPTOR m_SD;
+ PACL m_pACL;
+ // Saved by SetSecurityDescriptorMandatoryLabel so that the memory can be deleted properly
+ PACL m_pSacl;
+ bool m_fInitialized;
+};
+
+#endif // !SECURITYUTIL_H
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// File: SecurityWrapper.h
+//
+// Wrapper around Win32 Security functions
+//
+//*****************************************************************************
+
+
+#ifndef _SECURITY_WRAPPER_H
+#define _SECURITY_WRAPPER_H
+
+#ifdef TARGET_UNIX
+#error This file should not be included on non-Windows platforms.
+#endif
+
+//-----------------------------------------------------------------------------
+// Wrapper around a PSID.
+// This class does not own the memory.
+//-----------------------------------------------------------------------------
+class Sid
+{
+public:
+ // Initial the Sid wrapper around an existing SID.
+ Sid(PSID pSid);
+ static bool Equals(const Sid & a, const Sid & b) { return Equals(a.m_pSid, b.m_pSid); }
+ static bool Equals(const Sid & a, PSID b) { return Equals(a.m_pSid, b); }
+ static bool Equals(PSID a, const Sid & b) { return Equals(a, b.m_pSid); }
+ static bool Equals(PSID a, PSID b);
+
+ PSID RawSid() { return m_pSid; }
+protected:
+ // Pointer to Sid buffer. We don't owner the data.
+ PSID m_pSid;
+};
+
+//-----------------------------------------------------------------------------
+// Wrapper around a PSID with buffer.
+//-----------------------------------------------------------------------------
+class SidBuffer
+{
+public:
+ SidBuffer();
+ ~SidBuffer();
+
+ // Get the underlying sid
+ Sid GetSid();
+
+ // Do we not have a sid? This will be true if init fails.
+ bool IsNull() { return m_pBuffer == NULL; }
+
+ // Go to definitions to see detailed comments
+ HRESULT InitFromProcessNoThrow(DWORD pid);
+ void InitFromProcess(DWORD pid); // throws
+ HRESULT InitFromProcessUserNoThrow(DWORD pid);
+ void InitFromProcessUser(DWORD pid); // throws
+ HRESULT InitFromProcessAppContainerSidNoThrow(DWORD pid);
+
+protected:
+ BYTE * m_pBuffer;
+};
+
+//-----------------------------------------------------------------------------
+// Access Control List.
+//-----------------------------------------------------------------------------
+class Dacl
+{
+public:
+ Dacl(PACL pAcl);
+
+ SIZE_T GetAceCount();
+ ACE_HEADER * GetAce(SIZE_T dwAceIndex);
+protected:
+ PACL m_acl;
+};
+
+//-----------------------------------------------------------------------------
+// Represent a win32 SECURITY_DESCRIPTOR object.
+// (Note there's a "SecurityDescriptor" class in the VM for managed goo,
+// so we prefix this with "Win32" to avoid a naming collision.)
+//-----------------------------------------------------------------------------
+class Win32SecurityDescriptor
+{
+public:
+ Win32SecurityDescriptor();
+ ~Win32SecurityDescriptor();
+
+ HRESULT InitFromHandleNoThrow(HANDLE h);
+ void InitFromHandle(HANDLE h); // throws
+
+ // Gets the owner SID from this SecurityDescriptor.
+ HRESULT GetOwnerNoThrow( PSID* ppSid );
+ Sid GetOwner(); // throws
+ Dacl GetDacl(); // throws
+
+protected:
+ PSECURITY_DESCRIPTOR m_pDesc;
+};
+
+
+#endif // _SECURITY_WRAPPER_H
#ifndef _H_SIGPARSER
#define _H_SIGPARSER
+#ifndef LIMITED_METHOD_DAC_CONTRACT
#define LIMITED_METHOD_DAC_CONTRACT ((void)0)
+#endif
+#ifndef LIMITED_METHOD_CONTRACT
#define LIMITED_METHOD_CONTRACT ((void)0)
+#endif
+#ifndef WRAPPER_NO_CONTRACT
#define WRAPPER_NO_CONTRACT ((void)0)
+#endif
+#ifndef SUPPORTS_DAC
#define SUPPORTS_DAC ((void)0)
-
+#endif
#ifndef _ASSERT
#define _ASSERT _ASSERTE
#endif
-#include "utilcode.h"
#include "corhdr.h"
#include <minipal/utils.h>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// SString.h (Safe String)
+//
+
+// ---------------------------------------------------------------------------
+
+// ------------------------------------------------------------------------------------------
+// SString is the "standard" string representation for the EE. Its has two purposes.
+// (1) it provides an easy-to-use, relatively efficient, string class for APIs to standardize
+// on.
+// (2) it completely encapsulates all "unsafe" string operations - that is, string operations
+// which yield possible buffer overrun bugs. Typesafe use of this API should help guarantee
+// safety.
+//
+// A SString is conceptually unicode, although the internal conversion might be delayed as long as possible
+// Basically it's up to the implementation whether conversion takes place immediately or is delayed, and if
+// delayed, what operations trigger the conversion.
+//
+// Note that anywhere you express a "position" in a string, it is in terms of the Unicode representation of the
+// string.
+//
+// If you need a direct non-unicode representation, you will have to provide a fresh SString which can
+// receive a conversion operation if necessary.
+//
+// The alternate encodings available are:
+// 1. ASCII - string consisting entirely of ASCII (7 bit) characters. This is the only 1 byte encoding
+// guaranteed to be fixed width. Such a string is also a valid instance of all the other 1 byte string
+// representations, and we take advantage of this fact.
+// 2. UTF-8 - standard multibyte unicode encoding.
+// 3. ANSI - Potentially multibyte encoding using the ANSI page determined by GetACP().
+//
+// @todo: Note that we could also provide support for several other cases (but currently do not.)
+// - Page specified by GetOEMCP() (OEM page)
+// - Arbitrary page support
+//
+// @todo: argument & overflow/underflow checking needs to be added
+// ------------------------------------------------------------------------------------------
+
+
+#ifndef _SSTRING_H_
+#define _SSTRING_H_
+
+#include "utilcode.h"
+#include "sbuffer.h"
+#include "debugmacros.h"
+
+// ==========================================================================================
+// Documentational typedefs: use these to indicate specific representations of 8 bit strings:
+// ==========================================================================================
+
+// Note that LPCSTR means ASCII (7-bit) only!
+
+typedef CHAR ASCII;
+typedef ASCII *LPASCII;
+typedef const ASCII *LPCASCII;
+
+typedef CHAR ANSI;
+typedef ANSI *LPANSI;
+typedef const ANSI *LPCANSI;
+
+typedef CHAR UTF8;
+typedef UTF8 *LPUTF8;
+typedef const UTF8 *LPCUTF8;
+
+// ==========================================================================================
+// SString is the base class for safe strings.
+// ==========================================================================================
+
+
+typedef DPTR(class SString) PTR_SString;
+class EMPTY_BASES_DECL SString : private SBuffer
+{
+ friend struct _DacGlobals;
+
+private:
+ enum Representation
+ {
+ // Note: bits are meaningful: xVS V == Variable? S == Single byte width?
+ REPRESENTATION_EMPTY = 0x00, // 000
+ REPRESENTATION_UNICODE = 0x04, // 100
+ REPRESENTATION_ASCII = 0x01, // 001
+ REPRESENTATION_UTF8 = 0x03, // 011
+ REPRESENTATION_ANSI = 0x07, // 111
+
+ REPRESENTATION_VARIABLE_MASK = 0x02,
+ REPRESENTATION_SINGLE_MASK = 0x01,
+ REPRESENTATION_MASK = 0x07,
+ };
+
+ // Minimum guess for Printf buffer size
+ const static COUNT_T MINIMUM_GUESS = 20;
+
+
+#ifdef _DEBUG
+ // Used to have a public ctor of this form - made it too easy to lose
+ // utf8 info by accident. Now you have to specify the representation type
+ // explicitly - this privator ctor prevents reinsertion of this ctor.
+ explicit SString(const ASCII *)
+ {
+ _ASSERTE(!"Don't call this.");
+ }
+#endif
+
+ protected:
+ class Index;
+ class UIndex;
+
+ friend class Index;
+ friend class UIndex;
+
+ public:
+
+ // UIterator is character-level assignable.
+ class UIterator;
+
+ // CIterators/Iterator'string must be modified by SString APIs.
+ class CIterator;
+ class Iterator;
+
+ // Tokens for constructor overloads
+ enum tagUTF8Literal { Utf8Literal };
+ enum tagLiteral { Literal };
+ enum tagUTF8 { Utf8 };
+ enum tagANSI { Ansi };
+ enum tagASCII {Ascii };
+
+ static void Startup();
+ static CHECK CheckStartup();
+
+ static const SString &Empty();
+
+ SString();
+
+ explicit SString(const SString &s);
+
+ SString(const SString &s1, const SString &s2);
+ SString(const SString &s1, const SString &s2, const SString &s3);
+ SString(const SString &s1, const SString &s2, const SString &s3, const SString &s4);
+ SString(const SString &s, const CIterator &i, COUNT_T length);
+ SString(const SString &s, const CIterator &start, const CIterator &end);
+ SString(const WCHAR *string);
+ SString(const WCHAR *string, COUNT_T count);
+ SString(enum tagASCII dummyTag, const ASCII *string);
+ SString(enum tagASCII dummyTag, const ASCII *string, COUNT_T count);
+ SString(enum tagUTF8 dummytag, const UTF8 *string);
+ SString(enum tagUTF8 dummytag, const UTF8 *string, COUNT_T count);
+ SString(enum tagANSI dummytag, const ANSI *string);
+ SString(enum tagANSI dummytag, const ANSI *string, COUNT_T count);
+ SString(WCHAR character);
+
+ // NOTE: Literals MUST be read-only never-freed strings.
+ SString(enum tagLiteral dummytag, const CHAR *literal);
+ SString(enum tagUTF8Literal dummytag, const UTF8 *literal);
+ SString(enum tagLiteral dummytag, const WCHAR *literal);
+ SString(enum tagLiteral dummytag, const WCHAR *literal, COUNT_T count);
+
+ // Set this string to the concatenation of s1,s2,s3,s4
+ void Set(const SString &s);
+ void Set(const SString &s1, const SString &s2);
+ void Set(const SString &s1, const SString &s2, const SString &s3);
+ void Set(const SString &s1, const SString &s2, const SString &s3, const SString &s4);
+
+ // Set this string to the substring of s, starting at i, of length characters.
+ void Set(const SString &s, const CIterator &i, COUNT_T length);
+
+ // Set this string to the substring of s, starting at start and ending at end (exclusive)
+ void Set(const SString &s, const CIterator &start, const CIterator &end);
+
+ // Set this string to a copy of the given string
+ void Set(const WCHAR *string);
+ void SetASCII(const ASCII *string);
+ void SetUTF8(const UTF8 *string);
+ void SetANSI(const ANSI *string);
+
+ // Set this string to a copy of the first count chars of the given string
+ void Set(const WCHAR *string, COUNT_T count);
+
+ // Set this string to a prellocated copy of a given string.
+ // The caller is the owner of the bufffer and has to coordinate its lifetime.
+ void SetPreallocated(const WCHAR *string, COUNT_T count);
+
+ void SetASCII(const ASCII *string, COUNT_T count);
+
+ void SetUTF8(const UTF8 *string, COUNT_T count);
+ void SetANSI(const ANSI *string, COUNT_T count);
+
+ // Set this string to the unicode character
+ void Set(WCHAR character);
+
+ // Set this string to the UTF8 character
+ void SetUTF8(CHAR character);
+
+ // This this string to the given literal. We share the mem and don't make a copy.
+ void SetLiteral(const CHAR *literal);
+ void SetLiteral(const WCHAR *literal);
+
+ // ------------------------------------------------------------------
+ // Public operations
+ // ------------------------------------------------------------------
+
+ // Normalizes the string representation to unicode. This can be used to
+ // make basic read-only operations non-failing.
+ void Normalize() const;
+
+ // Return the number of characters in the string (excluding the terminating NULL).
+ COUNT_T GetCount() const;
+ BOOL IsEmpty() const;
+
+ // Return whether a single byte string has all characters which fit in the ASCII set.
+ // (Note that this will return FALSE if the string has been converted to unicode for any
+ // reason.)
+ BOOL IsASCII() const;
+
+ // !!!!!!!!!!!!!! WARNING about case insensitive operations !!!!!!!!!!!!!!!
+ //
+ // THIS IS NOT SUPPORTED FULLY ON WIN9x
+ // SString case-insensitive comparison is based off LCMapString,
+ // which does not work on characters outside the current OS code page.
+ //
+ // Case insensitive code in SString is primarily targeted at
+ // supporting path comparisons, which is supported correctly on 9x,
+ // since file system names are limited to the OS code page.
+ //
+ // !!!!!!!!!!!!!! WARNING about case insensitive operations !!!!!!!!!!!!!!!
+
+ // Compute a content-based hash value
+ ULONG Hash() const;
+ ULONG HashCaseInsensitive() const;
+
+ // Do a string comparison. Return 0 if the strings
+ // have the same value, -1 if this is "less than" s, or 1 if
+ // this is "greater than" s.
+ int Compare(const SString &s) const;
+ int CompareCaseInsensitive(const SString &s) const; // invariant locale
+
+ // Do a case sensitive string comparison. Return TRUE if the strings
+ // have the same value FALSE if not.
+ BOOL Equals(const SString &s) const;
+ BOOL EqualsCaseInsensitive(const SString &s) const; // invariant locale
+
+ // Match s to a portion of the string starting at the position.
+ // Return TRUE if the strings have the same value
+ // (regardless of representation), FALSE if not.
+ BOOL Match(const CIterator &i, const SString &s) const;
+ BOOL MatchCaseInsensitive(const CIterator &i, const SString &s) const; // invariant locale
+
+ BOOL Match(const CIterator &i, WCHAR c) const;
+ BOOL MatchCaseInsensitive(const CIterator &i, WCHAR c) const; // invariant locale
+
+ // Like match, but advances the iterator past the match
+ // if successful
+ BOOL Skip(CIterator &i, const SString &s) const;
+ BOOL Skip(CIterator &i, WCHAR c) const;
+
+ // Start searching for a match of the given string, starting at
+ // the given iterator point.
+ // If a match exists, move the iterator to point to the nearest
+ // occurrence of s in the string and return TRUE.
+ // If no match exists, return FALSE and leave the iterator unchanged.
+ BOOL Find(CIterator &i, const SString &s) const;
+ BOOL Find(CIterator &i, const WCHAR *s) const;
+ BOOL FindASCII(CIterator &i, const ASCII *s) const;
+ BOOL FindUTF8(CIterator &i, const UTF8 *s) const;
+ BOOL Find(CIterator &i, WCHAR c) const;
+
+ BOOL FindBack(CIterator &i, const SString &s) const;
+ BOOL FindBack(CIterator &i, const WCHAR *s) const;
+ BOOL FindBackASCII(CIterator &i, const ASCII *s) const;
+ BOOL FindBackUTF8(CIterator &i, const UTF8 *s) const;
+ BOOL FindBack(CIterator &i, WCHAR c) const;
+
+ // Returns TRUE if this string begins with the contents of s
+ BOOL BeginsWith(const SString &s) const;
+ BOOL BeginsWithCaseInsensitive(const SString &s) const; // invariant locale
+
+ // Returns TRUE if this string ends with the contents of s
+ BOOL EndsWith(const SString &s) const;
+ BOOL EndsWithCaseInsensitive(const SString &s) const; // invariant locale
+
+ // Sets this string to an empty string "".
+ void Clear();
+
+ // Truncate the string to the iterator position
+ void Truncate(const Iterator &i);
+
+ // Append s to the end of this string.
+ void Append(const SString &s);
+ void Append(const WCHAR *s);
+ void AppendASCII(const CHAR *s);
+ void AppendUTF8(const CHAR *s);
+
+ // Append char c to the end of this string.
+ void Append(const WCHAR c);
+ void AppendUTF8(const CHAR c);
+
+ // Insert s into this string at the 'position'th character.
+ void Insert(const Iterator &i, const SString &s);
+ void Insert(const Iterator &i, const WCHAR *s);
+ void InsertASCII(const Iterator &i, const CHAR *s);
+ void InsertUTF8(const Iterator &i, const CHAR *s);
+
+ // Delete substring position + length
+ void Delete(const Iterator &i, COUNT_T length);
+
+ // Replace character at i with c
+ void Replace(const Iterator &i, WCHAR c);
+
+ // Replace substring at (i,i+length) with s
+ void Replace(const Iterator &i, COUNT_T length, const SString &s);
+
+ // Make sure that string buffer has room to grow
+ void Preallocate(COUNT_T characters) const;
+
+ // Shrink buffer size as much as possible (reallocate if necessary.)
+ void Trim() const;
+
+ // ------------------------------------------------------------------
+ // Iterators:
+ // ------------------------------------------------------------------
+
+ // SString splits iterators into two categories.
+ //
+ // CIterator and Iterator are cheap to create, but allow only read-only
+ // access to the string.
+ //
+ // UIterator forces a unicode conversion, but allows
+ // assignment to individual string characters. They are also a bit more
+ // efficient once created.
+
+ // ------------------------------------------------------------------
+ // UIterator:
+ // ------------------------------------------------------------------
+
+ protected:
+
+ class EMPTY_BASES_DECL UIndex : public SBuffer::Index
+ {
+ friend class SString;
+ friend class Indexer<WCHAR, UIterator>;
+
+ protected:
+
+ UIndex();
+ UIndex(SString *string, SCOUNT_T index);
+ WCHAR &GetAt(SCOUNT_T delta) const;
+ void Skip(SCOUNT_T delta);
+ SCOUNT_T Subtract(const UIndex &i) const;
+ CHECK DoCheck(SCOUNT_T delta) const;
+
+ WCHAR *GetUnicode() const;
+ };
+
+ public:
+
+ class EMPTY_BASES_DECL UIterator : public UIndex, public Indexer<WCHAR, UIterator>
+ {
+ friend class SString;
+
+ public:
+ UIterator()
+ {
+ }
+
+ UIterator(SString *string, int index)
+ : UIndex(string, index)
+ {
+ }
+ };
+
+ UIterator BeginUnicode();
+ UIterator EndUnicode();
+
+ // For CIterator & Iterator, we try our best to iterate the string without
+ // modifying it. (Currently, we do require an ASCII or Unicode string
+ // for simple WCHAR retrival, but you could imagine being more flexible
+ // going forward - perhaps even supporting iterating multibyte encodings
+ // directly.)
+ //
+ // Because of the runtime-changable nature of the string, CIterators
+ // require an extra member to record the character size. They also
+ // are unable to properly implement GetAt as required by the template
+ // (since there may not be a direct WCHAR pointer), so they provide
+ // further customization in a subclass.
+ //
+ // Normally the user expects to cast Iterators to CIterators transparently, so
+ // we provide a constructor on CIterator to support this.
+
+ protected:
+
+ class EMPTY_BASES_DECL Index : public SBuffer::Index
+ {
+ friend class SString;
+
+ friend class Indexer<const WCHAR, CIterator>;
+ friend class Indexer<WCHAR, Iterator>;
+
+ protected:
+ int m_characterSizeShift;
+
+ Index();
+ Index(SString *string, SCOUNT_T index);
+ BYTE &GetAt(SCOUNT_T delta) const;
+ void Skip(SCOUNT_T delta);
+ SCOUNT_T Subtract(const Index &i) const;
+ CHECK DoCheck(SCOUNT_T delta) const;
+
+ void Resync(const SString *string, BYTE *ptr) const;
+
+ const WCHAR *GetUnicode() const;
+ const CHAR *GetASCII() const;
+
+ public:
+ // Note these should supercede the Indexer versions
+ // since this class comes first in the inheritence list
+ WCHAR operator*() const;
+ void operator->() const;
+ WCHAR operator[](int index) const;
+ };
+
+ public:
+
+ class EMPTY_BASES_DECL CIterator : public Index, public Indexer<const WCHAR, CIterator>
+ {
+ friend class SString;
+
+ public:
+ const Iterator &ConstCast() const
+ {
+ return *(const Iterator *)this;
+ }
+
+ Iterator &ConstCast()
+ {
+ return *(Iterator *)this;
+ }
+
+ operator const SBuffer::CIterator &() const
+ {
+ return *(const SBuffer::CIterator *)this;
+ }
+
+ operator SBuffer::CIterator &()
+ {
+ return *(SBuffer::CIterator *)this;
+ }
+
+ CIterator()
+ {
+ }
+
+ CIterator(const SString *string, int index)
+ : Index(const_cast<SString *>(string), index)
+ {
+ }
+
+ // explicitly resolve these for gcc
+ WCHAR operator*() const { return Index::operator*(); }
+ void operator->() const { Index::operator->(); }
+ WCHAR operator[](int index) const { return Index::operator[](index); }
+ };
+
+ class EMPTY_BASES_DECL Iterator : public Index, public Indexer<WCHAR, Iterator>
+ {
+ friend class SString;
+
+ public:
+ operator const CIterator &() const
+ {
+ return *(const CIterator *)this;
+ }
+
+ operator CIterator &()
+ {
+ return *(CIterator *)this;
+ }
+
+ operator const SBuffer::Iterator &() const
+ {
+ return *(const SBuffer::Iterator *)this;
+ }
+
+ operator SBuffer::Iterator &()
+ {
+ return *(SBuffer::Iterator *)this;
+ }
+
+ Iterator()
+ {
+ }
+
+ Iterator(SString *string, int index)
+ : Index(string, index)
+ {
+ SUPPORTS_DAC;
+ }
+
+ // explicitly resolve these for gcc
+ WCHAR operator*() const { return Index::operator*(); }
+ void operator->() const { Index::operator->(); }
+ WCHAR operator[](int index) const { return Index::operator[](index); }
+ };
+
+ CIterator Begin() const;
+ CIterator End() const;
+
+ Iterator Begin();
+ Iterator End();
+
+ // ------------------------------------------------------------------
+ // Conversion:
+ // ------------------------------------------------------------------
+
+ // Get a const pointer to the string in the current representation.
+ // This pointer can not be cached because it will become invalid if
+ // the SString changes representation or reallocates its buffer.
+
+ // You can always get a unicode string. This will force a conversion
+ // if necessary.
+ const WCHAR *GetUnicode() const;
+ const WCHAR *GetUnicode(const CIterator &i) const;
+
+ void LowerCase();
+ void UpperCase();
+
+ // Helper function to convert string in-place to lower-case (no allocation overhead for SString instance)
+ static void LowerCase(__inout_z LPWSTR wszString);
+
+ // These routines will use the given scratch string if necessary
+ // to perform a conversion to the desired representation
+
+ // Use a local declaration of InlineScratchBuffer or StackScratchBuffer for parameters of
+ // AbstractScratchBuffer.
+ class AbstractScratchBuffer;
+
+ // These routines will use the given scratch buffer if necessary
+ // to perform a conversion to the desired representation. Note that
+ // the lifetime of the pointer return is limited by BOTH the
+ // scratch string and the source (this) string.
+ //
+ // Typical usage:
+ //
+ // SString *s = ...;
+ // {
+ // StackScratchBuffer buffer;
+ // const UTF8 *utf8 = s->GetUTF8(buffer);
+ // CallFoo(utf8);
+ // }
+ // // No more pointers to returned buffer allowed.
+
+ const UTF8 *GetUTF8(AbstractScratchBuffer &scratch) const;
+ const UTF8 *GetUTF8(AbstractScratchBuffer &scratch, COUNT_T *pcbUtf8) const;
+ const ANSI *GetANSI(AbstractScratchBuffer &scratch) const;
+
+ // Used when the representation is known, throws if the representation doesn't match
+ const UTF8 *GetUTF8NoConvert() const;
+
+ // Converts/copies into the given output string
+ void ConvertToUnicode(SString &dest) const;
+ void ConvertToANSI(SString &dest) const;
+ COUNT_T ConvertToUTF8(SString &dest) const;
+
+ //-------------------------------------------------------------------
+ // Accessing the string contents directly
+ //-------------------------------------------------------------------
+
+ // To write directly to the SString's underlying buffer:
+ // 1) Call OpenXXXBuffer() and pass it the count of characters
+ // you need. (Not including the null-terminator).
+ // 2) That returns a pointer to the raw buffer which you can write to.
+ // 3) When you are done writing to the pointer, call CloseBuffer()
+ // and pass it the count of characters you actually wrote (not including
+ // the null). The pointer from step 1 is now invalid.
+
+ // example usage:
+ // void GetName(SString & str) {
+ // char * p = str.OpenANSIBuffer(3);
+ // strcpy(p, "Cat");
+ // str.CloseBuffer();
+ // }
+
+ // Regarding the null-terminator:
+ // 1) Note that we wrote 4 characters (3 + a null). That's ok. OpenBuffer
+ // allocates 1 extra byte for the null.
+ // 2) If we only wrote 3 characters and no null, that's ok too. CloseBuffer()
+ // will add a null-terminator.
+
+ // You should open the buffer, write the data, and immediately close it.
+ // No sstring operations are valid while the buffer is opened.
+ //
+ // In a debug build, Open/Close will do lots of little checks to make sure
+ // you don't buffer overflow while it's opened. In a retail build, this
+ // is a very streamlined action.
+
+
+ // Open the raw buffer for writing countChars characters (not including the null).
+ WCHAR *OpenUnicodeBuffer(COUNT_T maxCharCount);
+ UTF8 *OpenUTF8Buffer(COUNT_T maxSingleCharCount);
+ ANSI *OpenANSIBuffer(COUNT_T maxSingleCharCount);
+
+ //Returns the unicode string, the caller is reponsible for lifetime of the string
+ WCHAR *GetCopyOfUnicodeString();
+
+ // Get the max size that can be passed to OpenUnicodeBuffer without causing allocations.
+ COUNT_T GetUnicodeAllocation();
+
+ // Call after OpenXXXBuffer().
+
+ // Provide the count of characters actually used (not including the
+ // null terminator). This will make sure the SString's size is correct
+ // and that we have a null-terminator.
+ void CloseBuffer(COUNT_T finalCount);
+
+ // Close the buffer. Assumes that we completely filled the buffer
+ // that OpenBuffer() gave back. If we didn't write all the characters,
+ // call CloseBuffer(int) instead.
+ void CloseBuffer();
+
+#ifdef DACCESS_COMPILE
+ // DAC access to string functions.
+ // Note that other accessors above are not DAC-safe and will return TARGET pointers into
+ // the string instead of copying the string over to the host.
+ // @dbgtodo dac support: Prevent usage of such DAC-unsafe SString APIs in DAC code
+
+ // Instantiate a copy of the raw buffer in the host and return a pointer to it
+ void * DacGetRawContent() const;
+
+ // Instantiate a copy of the raw buffer in the host. Requires that the underlying
+ // representation is already unicode.
+ const WCHAR * DacGetRawUnicode() const;
+
+ // Copy the string from the target into the provided buffer, converting to unicode if necessary
+ bool DacGetUnicode(COUNT_T bufChars,
+ _Inout_updates_z_(bufChars) WCHAR * buffer,
+ COUNT_T * needChars) const;
+
+ void EnumMemoryRegions(CLRDataEnumMemoryFlags flags) const
+ {
+ SUPPORTS_DAC;
+ SBuffer::EnumMemoryRegions(flags);
+ }
+#endif
+
+ //---------------------------------------------------------------------
+ // Utilities
+ //---------------------------------------------------------------------
+
+ // WARNING: The MBCS version of printf function are factory for globalization
+ // issues when used to format Unicode strings (%S). The Unicode versions are
+ // preferred in this case.
+ void Printf(const CHAR *format, ...);
+ void VPrintf(const CHAR *format, va_list args);
+
+ void Printf(const WCHAR *format, ...);
+ void PPrintf(const WCHAR *format, ...);
+ void VPrintf(const WCHAR *format, va_list args);
+
+ void PVPrintf(const WCHAR *format, va_list args);
+
+ void AppendPrintf(const CHAR *format, ...);
+ void AppendVPrintf(const CHAR *format, va_list args);
+
+ void AppendPrintf(const WCHAR *format, ...);
+ void AppendVPrintf(const WCHAR *format, va_list args);
+
+ BOOL LoadResource(CCompRC::ResourceCategory eCategory, int resourceID);
+ HRESULT LoadResourceAndReturnHR(CCompRC::ResourceCategory eCategory, int resourceID);
+ HRESULT LoadResourceAndReturnHR(CCompRC* pResourceDLL, CCompRC::ResourceCategory eCategory, int resourceID);
+ BOOL FormatMessage(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId,
+ const SString &arg1 = Empty(), const SString &arg2 = Empty(),
+ const SString &arg3 = Empty(), const SString &arg4 = Empty(),
+ const SString &arg5 = Empty(), const SString &arg6 = Empty(),
+ const SString &arg7 = Empty(), const SString &arg8 = Empty(),
+ const SString &arg9 = Empty(), const SString &arg10 = Empty());
+
+#if 1
+ // @todo - get rid of this and move it outside of SString
+ void MakeFullNamespacePath(const SString &nameSpace, const SString &name);
+#endif
+
+ //--------------------------------------------------------------------
+ // Operators
+ //--------------------------------------------------------------------
+
+ operator const WCHAR * () const { WRAPPER_NO_CONTRACT; return GetUnicode(); }
+
+ WCHAR operator[](int index) { WRAPPER_NO_CONTRACT; return Begin()[index]; }
+ WCHAR operator[](int index) const { WRAPPER_NO_CONTRACT; return Begin()[index]; }
+
+ SString &operator= (const SString &s) { WRAPPER_NO_CONTRACT; Set(s); return *this; }
+ SString &operator+= (const SString &s) { WRAPPER_NO_CONTRACT; Append(s); return *this; }
+
+ // -------------------------------------------------------------------
+ // Check functions
+ // -------------------------------------------------------------------
+
+ CHECK CheckIteratorRange(const CIterator &i) const;
+ CHECK CheckIteratorRange(const CIterator &i, COUNT_T length) const;
+ CHECK CheckEmpty() const;
+
+ static CHECK CheckCount(COUNT_T count);
+ static CHECK CheckRepresentation(int representation);
+
+#if CHECK_INVARIANTS
+ static CHECK CheckASCIIString(const ASCII *string);
+ static CHECK CheckASCIIString(const ASCII *string, COUNT_T count);
+
+ CHECK Check() const;
+ CHECK Invariant() const;
+ CHECK InternalInvariant() const;
+#endif // CHECK_INVARIANTS
+
+ // Helpers for CRT function equivalance.
+ static int __cdecl _stricmp(const CHAR *buffer1, const CHAR *buffer2);
+ static int __cdecl _strnicmp(const CHAR *buffer1, const CHAR *buffer2, COUNT_T count);
+
+ static int __cdecl _wcsicmp(const WCHAR *buffer1, const WCHAR *buffer2);
+ static int __cdecl _wcsnicmp(const WCHAR *buffer1, const WCHAR *buffer2, COUNT_T count);
+
+ // C++ convenience overloads
+ static int _tstricmp(const CHAR *buffer1, const CHAR *buffer2);
+ static int _tstricmp(const WCHAR *buffer1, const WCHAR *buffer2);
+
+ static int _tstrnicmp(const CHAR *buffer1, const CHAR *buffer2, COUNT_T count);
+ static int _tstrnicmp(const WCHAR *buffer1, const WCHAR *buffer2, COUNT_T count);
+
+ // -------------------------------------------------------------------
+ // Internal routines
+ // -------------------------------------------------------------------
+
+
+ protected:
+ // Use this via InlineSString<X>
+ SString(void *buffer, COUNT_T size);
+
+ private:
+ static int CaseCompareHelperA(const CHAR *buffer1, const CHAR *buffer2, COUNT_T count, BOOL stopOnNull, BOOL stopOnCount);
+ static int CaseCompareHelper(const WCHAR *buffer1, const WCHAR *buffer2, COUNT_T count, BOOL stopOnNull, BOOL stopOnCount);
+
+ // Internal helpers:
+
+ static const BYTE s_EmptyBuffer[2];
+
+ static UINT s_ACP;
+
+ SPTR_DECL(SString,s_Empty);
+
+ COUNT_T GetRawCount() const;
+
+ // Get buffer as appropriate string rep
+ ASCII *GetRawASCII() const;
+ UTF8 *GetRawUTF8() const;
+ ANSI *GetRawANSI() const;
+ WCHAR *GetRawUnicode() const;
+
+ void InitEmpty();
+
+ Representation GetRepresentation() const;
+ void SetRepresentation(Representation representation);
+ BOOL IsRepresentation(Representation representation) const;
+ BOOL IsFixedSize() const;
+ BOOL IsIteratable() const;
+ BOOL IsSingleByte() const;
+
+ int GetCharacterSizeShift() const;
+
+ COUNT_T SizeToCount(COUNT_T size) const;
+ COUNT_T CountToSize(COUNT_T count) const;
+
+ COUNT_T GetBufferSizeInCharIncludeNullChar() const;
+
+ BOOL IsLiteral() const;
+ BOOL IsAllocated() const;
+ BOOL IsBufferOpen() const;
+ BOOL IsASCIIScanned() const;
+ void SetASCIIScanned() const;
+ void SetNormalized() const;
+ BOOL IsNormalized() const;
+ void ClearNormalized() const;
+
+ void EnsureWritable() const;
+ void ConvertToFixed() const;
+ void ConvertToIteratable() const;
+
+ void ConvertASCIIToUnicode(SString &dest) const;
+ void ConvertToUnicode() const;
+ void ConvertToUnicode(const CIterator &i) const;
+
+ const SString &GetCompatibleString(const SString &s, SString &scratch) const;
+ const SString &GetCompatibleString(const SString &s, SString &scratch, const CIterator &i) const;
+ BOOL ScanASCII() const;
+ void NullTerminate();
+
+ void Resize(COUNT_T count, Representation representation,
+ Preserve preserve = DONT_PRESERVE);
+
+ void OpenBuffer(Representation representation, COUNT_T countChars);
+};
+
+// ===========================================================================
+// InlineSString is used for stack allocation of strings, or when the string contents
+// are expected or known to be small. Note that it still supports expandability via
+// heap allocation if necessary.
+// ===========================================================================
+
+template <COUNT_T MEMSIZE>
+class EMPTY_BASES_DECL InlineSString : public SString
+{
+private:
+ DAC_ALIGNAS(SString)
+ BYTE m_inline[SBUFFER_PADDED_SIZE(MEMSIZE)];
+
+public:
+ FORCEINLINE InlineSString()
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+
+ FORCEINLINE InlineSString(const SString &s)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(s);
+ }
+
+ FORCEINLINE InlineSString(const SString &s1, const SString &s2)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(s1, s2);
+ }
+
+ FORCEINLINE InlineSString(const SString &s1, const SString &s2, const SString &s3)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(s1, s2, s3);
+ }
+
+ FORCEINLINE InlineSString(const SString &s1, const SString &s2, const SString &s3, const SString &s4)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(s1, s2, s3, s4);
+ }
+
+ FORCEINLINE InlineSString(const SString &s, const CIterator &start, const CIterator &end)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(s, start, end);
+ }
+
+ FORCEINLINE InlineSString(const SString &s, const CIterator &i, COUNT_T length)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(s, i, length);
+ }
+
+ FORCEINLINE InlineSString(const WCHAR *string)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(string);
+ }
+
+ FORCEINLINE InlineSString(const WCHAR *string, COUNT_T count)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(string, count);
+ }
+
+ FORCEINLINE InlineSString(enum tagASCII, const CHAR *string)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ SetASCII(string);
+ }
+
+ FORCEINLINE InlineSString(enum tagASCII, const CHAR *string, COUNT_T count)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ SetASCII(string, count);
+ }
+
+ FORCEINLINE InlineSString(tagUTF8 dummytag, const UTF8 *string)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ SetUTF8(string);
+ }
+
+ FORCEINLINE InlineSString(tagUTF8 dummytag, const UTF8 *string, COUNT_T count)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ SetUTF8(string, count);
+ }
+
+ FORCEINLINE InlineSString(enum tagANSI dummytag, const ANSI *string)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ SetANSI(string);
+ }
+
+ FORCEINLINE InlineSString(enum tagANSI dummytag, const ANSI *string, COUNT_T count)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ SetANSI(string, count);
+ }
+
+ FORCEINLINE InlineSString(WCHAR character)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(character);
+ }
+
+ FORCEINLINE InlineSString(tagUTF8 dummytag, const UTF8 character)
+ : SString(m_inline, SBUFFER_PADDED_SIZE(MEMSIZE))
+ {
+ WRAPPER_NO_CONTRACT;
+ SetUTF8(character);
+ }
+
+ FORCEINLINE InlineSString<MEMSIZE> &operator= (const SString &s)
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(s);
+ return *this;
+ }
+
+ FORCEINLINE InlineSString<MEMSIZE> &operator= (const InlineSString<MEMSIZE> &s)
+ {
+ WRAPPER_NO_CONTRACT;
+ Set(s);
+ return *this;
+ }
+};
+
+// ================================================================================
+// StackSString is a lot like CQuickBytes. Use it to create an SString object
+// using some stack space as a preallocated buffer.
+// ================================================================================
+
+typedef InlineSString<512> StackSString;
+
+// This is a smaller version for when it is known that the string that's going to
+// be needed is small and it's preferable not to take up the stack space.
+typedef InlineSString<32> SmallStackSString;
+
+// To be used specifically for path strings.
+#ifdef _DEBUG
+// This is a smaller version for debug builds to exercise the buffer allocation path
+typedef InlineSString<32> PathString;
+typedef InlineSString<2 * 32> LongPathString;
+#else
+// Set it to the current MAX_PATH
+typedef InlineSString<260> PathString;
+typedef InlineSString<2 * 260> LongPathString;
+#endif
+
+// ================================================================================
+// Quick macro to create an SString around a literal string.
+// usage:
+// s = SL("My literal String");
+// ================================================================================
+
+#define SL(_literal) SString(SString::Literal, _literal)
+
+// ================================================================================
+// ScratchBuffer classes are used by the GetXXX() routines to allocate scratch space in.
+// ================================================================================
+
+class EMPTY_BASES_DECL SString::AbstractScratchBuffer : private SString
+{
+ protected:
+ // Do not use this class directly - use
+ // ScratchBuffer or StackScratchBuffer.
+ AbstractScratchBuffer(void *buffer, COUNT_T size);
+};
+
+template <COUNT_T MEMSIZE>
+class EMPTY_BASES_DECL ScratchBuffer : public SString::AbstractScratchBuffer
+{
+ private:
+ DAC_ALIGNAS(::SString::AbstractScratchBuffer)
+ BYTE m_inline[MEMSIZE];
+
+ public:
+ ScratchBuffer()
+ : AbstractScratchBuffer((void *)m_inline, MEMSIZE)
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+};
+
+typedef ScratchBuffer<256> StackScratchBuffer;
+
+// ================================================================================
+// Special contract definition - THROWS_UNLESS_NORMALIZED
+// this is used for operations which might fail for generalized strings but
+// not if the string has already been converted to unicode. Rather than just
+// setting this on all conversions to unicode, we only set it when explicitly
+// asked. This should expose more potential problems.
+// ================================================================================
+
+#define THROWS_UNLESS_NORMALIZED \
+ if (IsNormalized()) NOTHROW; else THROWS
+
+#define THROWS_UNLESS_BOTH_NORMALIZED(s) \
+ if (IsNormalized() && s.IsNormalized()) NOTHROW; else THROWS
+
+#define FAULTS_UNLESS_NORMALIZED(stmt) \
+ if (IsNormalized()) FORBID_FAULT; else INJECT_FAULT(stmt)
+
+#define FAULTS_UNLESS_BOTH_NORMALIZED(s, stmt) \
+ if (IsNormalized() && s.IsNormalized()) FORBID_FAULT; else INJECT_FAULT(stmt)
+
+// ================================================================================
+// Inline definitions
+// ================================================================================
+
+#include <sstring.inl>
+
+#endif // _SSTRING_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+
+#ifndef _SSTRING_INL_
+#define _SSTRING_INL_
+
+#include "sstring.h"
+
+#if defined(_MSC_VER)
+#pragma inline_depth (20)
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4702) // Disable bogus unreachable code warning
+#endif // _MSC_VER
+
+//#define SSTRING_EXTRA_CHECKS
+#ifdef SSTRING_EXTRA_CHECKS
+#define SS_CONTRACT CONTRACT
+#define SS_CONTRACT_VOID CONTRACT_VOID
+#define SS_CONTRACT_END CONTRACT_END
+#define SS_RETURN RETURN
+#define SS_CONSTRUCTOR_CHECK CONSTRUCTOR_CHECK
+#define SS_PRECONDITION PRECONDITION
+#define SS_POSTCONDITION POSTCONDITION
+
+#else //SSTRING_EXTRA_CHECKS
+
+#define SS_CONTRACT(x) CONTRACTL
+#define SS_CONTRACT_VOID CONTRACTL
+#define SS_CONTRACT_END CONTRACTL_END
+#define SS_RETURN return
+#define SS_CONSTRUCTOR_CHECK
+#define SS_PRECONDITION(x)
+#define SS_POSTCONDITION(x)
+//Do I need this instance check at all?
+
+#endif
+
+
+// ---------------------------------------------------------------------------
+// Inline implementations. Pay no attention to that man behind the curtain.
+// ---------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+// Default constructor. Sets the string to the empty string.
+//----------------------------------------------------------------------------
+inline SString::SString()
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+#ifdef SSTRING_EXTRA_CHECKS
+ CONTRACT_VOID
+ {
+ CONSTRUCTOR_CHECK;
+ POSTCONDITION(IsEmpty());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ RETURN;
+#else
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+#endif
+}
+
+inline SString::SString(void *buffer, COUNT_T size)
+ : SBuffer(Prealloc, buffer, size)
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckSize(size));
+ SS_POSTCONDITION(IsEmpty());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ if (size < sizeof(WCHAR))
+ {
+ // Ignore the useless buffer
+ SetImmutable(s_EmptyBuffer, sizeof(s_EmptyBuffer));
+ }
+ else
+ {
+ SBuffer::TweakSize(sizeof(WCHAR));
+ GetRawUnicode()[0] = 0;
+ }
+
+ SS_RETURN;
+}
+
+inline SString::SString(const SString &s)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(s.Check());
+ SS_POSTCONDITION(Equals(s));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Set(s);
+
+ SS_RETURN;
+}
+
+inline SString::SString(const SString &s1, const SString &s2)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(s1.Check());
+ PRECONDITION(s2.Check());
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Set(s1, s2);
+
+ SS_RETURN;
+}
+
+inline SString::SString(const SString &s1, const SString &s2, const SString &s3)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(s1.Check());
+ PRECONDITION(s2.Check());
+ PRECONDITION(s3.Check());
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Set(s1, s2, s3);
+
+ SS_RETURN;
+}
+
+inline SString::SString(const SString &s1, const SString &s2, const SString &s3, const SString &s4)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(s1.Check());
+ PRECONDITION(s2.Check());
+ PRECONDITION(s3.Check());
+ PRECONDITION(s4.Check());
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ Set(s1, s2, s3, s4);
+
+ SS_RETURN;
+}
+
+inline SString::SString(const SString &s, const CIterator &i, COUNT_T count)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(s.Check());
+ PRECONDITION(i.Check());
+ PRECONDITION(CheckCount(count));
+ SS_POSTCONDITION(s.Match(i, *this));
+ SS_POSTCONDITION(GetRawCount() == count);
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Set(s, i, count);
+
+ SS_RETURN;
+}
+
+inline SString::SString(const SString &s, const CIterator &start, const CIterator &end)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(s.Check());
+ PRECONDITION(start.Check());
+ PRECONDITION(s.CheckIteratorRange(start));
+ PRECONDITION(end.Check());
+ PRECONDITION(s.CheckIteratorRange(end));
+ PRECONDITION(start <= end);
+ SS_POSTCONDITION(s.Match(start, *this));
+ SS_POSTCONDITION(GetRawCount() == (COUNT_T) (end - start));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Set(s, start, end);
+
+ SS_RETURN;
+}
+
+inline SString::SString(const WCHAR *string)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Set(string);
+
+ SS_RETURN;
+}
+
+inline SString::SString(const WCHAR *string, COUNT_T count)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Set(string, count);
+
+ SS_RETURN;
+}
+
+inline SString::SString(enum tagASCII, const ASCII *string)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckASCIIString(string));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SetASCII(string);
+
+ SS_RETURN;
+}
+
+inline SString::SString(enum tagASCII, const ASCII *string, COUNT_T count)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckASCIIString(string, count));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SetASCII(string, count);
+
+ SS_RETURN;
+}
+
+inline SString::SString(tagUTF8 dummytag, const UTF8 *string)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ // !!! Check for illegal UTF8 encoding?
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ SetUTF8(string);
+
+ SS_RETURN;
+}
+
+inline SString::SString(tagUTF8 dummytag, const UTF8 *string, COUNT_T count)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ // !!! Check for illegal UTF8 encoding?
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SetUTF8(string, count);
+
+ SS_RETURN;
+}
+
+inline SString::SString(tagANSI dummytag, const ANSI *string)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SetANSI(string);
+
+ SS_RETURN;
+}
+
+inline SString::SString(tagANSI dummytag, const ANSI *string, COUNT_T count)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SetANSI(string, count);
+
+ SS_RETURN;
+}
+
+inline SString::SString(WCHAR character)
+ : SBuffer(Immutable, s_EmptyBuffer, sizeof(s_EmptyBuffer))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Set(character);
+
+ SS_RETURN;
+}
+
+inline SString::SString(tagLiteral dummytag, const ASCII *literal)
+ : SBuffer(Immutable, (const BYTE *) literal, (COUNT_T) (strlen(literal)+1)*sizeof(CHAR))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(literal));
+ PRECONDITION(CheckASCIIString(literal));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ SetRepresentation(REPRESENTATION_ASCII);
+
+ SS_RETURN;
+}
+
+inline SString::SString(tagUTF8Literal dummytag, const UTF8 *literal)
+ : SBuffer(Immutable, (const BYTE *) literal, (COUNT_T) (strlen(literal)+1)*sizeof(CHAR))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(literal));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SetRepresentation(REPRESENTATION_UTF8);
+
+ SS_RETURN;
+}
+
+inline SString::SString(tagLiteral dummytag, const WCHAR *literal)
+ : SBuffer(Immutable, (const BYTE *) literal, (COUNT_T) (wcslen(literal)+1)*sizeof(WCHAR))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(literal));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SetRepresentation(REPRESENTATION_UNICODE);
+ SetNormalized();
+
+ SS_RETURN;
+}
+
+inline SString::SString(tagLiteral dummytag, const WCHAR *literal, COUNT_T count)
+ : SBuffer(Immutable, (const BYTE *) literal, (count + 1) * sizeof(WCHAR))
+{
+ SS_CONTRACT_VOID
+ {
+ SS_CONSTRUCTOR_CHECK;
+ PRECONDITION(CheckPointer(literal));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SetRepresentation(REPRESENTATION_UNICODE);
+ SetNormalized();
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to s
+// s - source string
+//-----------------------------------------------------------------------------
+inline void SString::Set(const SString &s)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ SS_POSTCONDITION(Equals(s));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ SBuffer::Set(s);
+ SetRepresentation(s.GetRepresentation());
+ ClearNormalized();
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to concatenation of s1 and s2
+//-----------------------------------------------------------------------------
+inline void SString::Set(const SString &s1, const SString &s2)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s1.Check());
+ PRECONDITION(s2.Check());
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Preallocate(s1.GetCount() + s2.GetCount());
+
+ Set(s1);
+ Append(s2);
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to concatenation of s1, s2, and s3
+//-----------------------------------------------------------------------------
+inline void SString::Set(const SString &s1, const SString &s2, const SString &s3)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s1.Check());
+ PRECONDITION(s2.Check());
+ PRECONDITION(s3.Check());
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Preallocate(s1.GetCount() + s2.GetCount() + s3.GetCount());
+
+ Set(s1);
+ Append(s2);
+ Append(s3);
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to concatenation of s1, s2, s3, and s4
+//-----------------------------------------------------------------------------
+inline void SString::Set(const SString &s1, const SString &s2, const SString &s3, const SString &s4)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s1.Check());
+ PRECONDITION(s2.Check());
+ PRECONDITION(s3.Check());
+ PRECONDITION(s4.Check());
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Preallocate(s1.GetCount() + s2.GetCount() + s3.GetCount() + s4.GetCount());
+
+ Set(s1);
+ Append(s2);
+ Append(s3);
+ Append(s4);
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to the substring from s.
+// s - the source string
+// start - the character to start at
+// length - number of characters to copy from s.
+//-----------------------------------------------------------------------------
+inline void SString::Set(const SString &s, const CIterator &i, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ PRECONDITION(i.Check());
+ PRECONDITION(CheckCount(count));
+ SS_POSTCONDITION(s.Match(i, *this));
+ SS_POSTCONDITION(GetRawCount() == count);
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ // @todo: detect case where we can reuse literal?
+ Resize(count, s.GetRepresentation());
+ SBuffer::Copy(SBuffer::Begin(), i.m_ptr, count<<i.m_characterSizeShift);
+ NullTerminate();
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to the substring from s.
+// s - the source string
+// start - the position to start
+// end - the position to end (exclusive)
+//-----------------------------------------------------------------------------
+inline void SString::Set(const SString &s, const CIterator &start, const CIterator &end)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ PRECONDITION(start.Check());
+ PRECONDITION(s.CheckIteratorRange(start));
+ PRECONDITION(end.Check());
+ PRECONDITION(s.CheckIteratorRange(end));
+ PRECONDITION(end >= start);
+ SS_POSTCONDITION(s.Match(start, *this));
+ SS_POSTCONDITION(GetRawCount() == (COUNT_T) (end - start));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ Set(s, start, end - start);
+
+ SS_RETURN;
+}
+
+// Return a global empty string
+inline const SString &SString::Empty()
+{
+#ifdef SSTRING_EXTRA_CHECKS
+ CONTRACTL
+ {
+ // POSTCONDITION(RETVAL.IsEmpty());
+ PRECONDITION(CheckStartup());
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+#else
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+#endif
+
+ _ASSERTE(s_Empty != NULL); // Did you call SString::Startup()?
+ return *s_Empty;
+}
+
+// Get a const pointer to the internal buffer as a unicode string.
+inline const WCHAR *SString::GetUnicode() const
+{
+ SS_CONTRACT(const WCHAR *)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckPointer(RETVAL));
+ if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUnicode();
+
+ SS_RETURN GetRawUnicode();
+}
+
+// Normalize the string to unicode. This will make many operations nonfailing.
+inline void SString::Normalize() const
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ SS_POSTCONDITION(IsNormalized());
+ THROWS_UNLESS_NORMALIZED;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUnicode();
+ SetNormalized();
+
+ SS_RETURN;
+}
+
+// Get a const pointer to the internal buffer as a unicode string.
+inline const WCHAR *SString::GetUnicode(const CIterator &i) const
+{
+ SS_CONTRACT(const WCHAR *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ THROWS_UNLESS_NORMALIZED;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ PRECONDITION(CheckPointer(this));
+
+ ConvertToUnicode(i);
+
+ SS_RETURN i.GetUnicode();
+}
+
+// Append s to the end of this string.
+inline void SString::Append(const SString &s)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(s.Check());
+ THROWS;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ Insert(End(), s);
+
+ SS_RETURN;
+}
+
+inline void SString::Append(const WCHAR *string)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckPointer(string));
+ THROWS;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ // Wrap the string in temporary SString without copying it
+ SString s(SString::Literal, string);
+ s.ClearImmutable();
+ Append(s);
+
+ SS_RETURN;
+}
+
+inline void SString::AppendASCII(const CHAR *string)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckPointer(string));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(SString::Ascii, string);
+ Append(s);
+
+ SS_RETURN;
+}
+
+inline void SString::AppendUTF8(const CHAR *string)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckPointer(string));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(SString::Utf8, string);
+ Append(s);
+
+ SS_RETURN;
+}
+
+inline void SString::Append(const WCHAR c)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ InlineSString<2 * sizeof(c)> s(c);
+ Append(s);
+
+ SS_RETURN;
+}
+
+inline void SString::AppendUTF8(const CHAR c)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ THROWS;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ InlineSString<2 * sizeof(c)> s(SString::Utf8, c);
+ Append(s);
+
+ SS_RETURN;
+}
+
+// Turn this on to test that these if you are testing common scenarios dealing with
+// ASCII strings that do not touch the cases where this family of function differs
+// in behavior for expected reasons.
+//#define VERIFY_CRT_EQUIVALNCE 1
+
+// Helpers for CRT function equivalance.
+/* static */
+inline int __cdecl SString::_stricmp(const CHAR *buffer1, const CHAR *buffer2) {
+ WRAPPER_NO_CONTRACT;
+ int returnValue = CaseCompareHelperA(buffer1, buffer2, 0, TRUE, FALSE);
+#ifdef VERIFY_CRT_EQUIVALNCE
+ _ASSERTE((returnValue == 0) == (::_stricmp(buffer1, buffer2) == 0));
+#endif
+ return returnValue;
+
+}
+
+/* static */
+inline int __cdecl SString::_strnicmp(const CHAR *buffer1, const CHAR *buffer2, COUNT_T count) {
+ WRAPPER_NO_CONTRACT;
+ int returnValue = CaseCompareHelperA(buffer1, buffer2, count, TRUE, TRUE);
+#ifdef VERIFY_CRT_EQUIVALNCE
+ _ASSERTE((returnValue == 0) == (::_strnicmp(buffer1, buffer2, count) == 0));
+#endif
+ return returnValue;
+}
+
+/* static */
+inline int __cdecl SString::_wcsicmp(const WCHAR *buffer1, const WCHAR *buffer2) {
+ WRAPPER_NO_CONTRACT;
+ int returnValue = CaseCompareHelper(buffer1, buffer2, 0, TRUE, FALSE);
+#ifdef VERIFY_CRT_EQUIVALNCE
+ _ASSERTE((returnValue == 0) == (::_wcsicmp(buffer1, buffer2) == 0));
+#endif
+ return returnValue;
+
+}
+
+/* static */
+inline int __cdecl SString::_wcsnicmp(const WCHAR *buffer1, const WCHAR *buffer2, COUNT_T count) {
+ WRAPPER_NO_CONTRACT;
+ int returnValue = CaseCompareHelper(buffer1, buffer2, count, TRUE, TRUE);
+#ifdef VERIFY_CRT_EQUIVALNCE
+ _ASSERTE((returnValue == 0) == (::_wcsnicmp(buffer1, buffer2, count) == 0));
+#endif
+ return returnValue;
+}
+
+inline int SString::_tstricmp(const CHAR *buffer1, const CHAR *buffer2)
+{
+ return _stricmp(buffer1, buffer2);
+}
+
+inline int SString::_tstricmp(const WCHAR *buffer1, const WCHAR *buffer2)
+{
+ return _wcsicmp(buffer1, buffer2);
+}
+
+inline int SString::_tstrnicmp(const CHAR *buffer1, const CHAR *buffer2, COUNT_T count)
+{
+ return _strnicmp(buffer1, buffer2, count);
+}
+
+inline int SString::_tstrnicmp(const WCHAR *buffer1, const WCHAR *buffer2, COUNT_T count)
+{
+ return _wcsnicmp(buffer1, buffer2, count);
+}
+
+inline BOOL SString::Match(const CIterator &i, WCHAR c) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ NOTHROW;
+ }
+ SS_CONTRACT_END;
+
+ // End() will not throw here
+ CONTRACT_VIOLATION(ThrowsViolation);
+ SS_RETURN (i < End() && i[0] == c);
+}
+
+inline BOOL SString::Skip(CIterator &i, const SString &s) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ }
+ SS_CONTRACT_END;
+
+ if (Match(i, s))
+ {
+ i += s.GetRawCount();
+ SS_RETURN TRUE;
+ }
+ else
+ SS_RETURN FALSE;
+}
+
+inline BOOL SString::Skip(CIterator &i, WCHAR c) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ NOTHROW;
+ }
+ SS_CONTRACT_END;
+
+ if (Match(i, c))
+ {
+ i++;
+ SS_RETURN TRUE;
+ }
+ else
+ SS_RETURN FALSE;
+}
+
+// Find string within this string. Return TRUE and update iterator if found
+inline BOOL SString::Find(CIterator &i, const WCHAR *string) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(CheckPointer(string));
+ SS_POSTCONDITION(RETVAL == Match(i, SString(string)));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(string);
+ SS_RETURN Find(i, s);
+}
+
+inline BOOL SString::FindASCII(CIterator &i, const CHAR *string) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(CheckPointer(string));
+ SS_POSTCONDITION(RETVAL == Match(i, SString(SString::Ascii, string)));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(SString::Ascii, string);
+ SS_RETURN Find(i, s);
+}
+
+inline BOOL SString::FindUTF8(CIterator &i, const CHAR *string) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(CheckPointer(string));
+ SS_POSTCONDITION(RETVAL == Match(i, SString(SString::Ascii, string)));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(SString::Utf8, string);
+ SS_RETURN Find(i, s);
+}
+
+inline BOOL SString::FindBack(CIterator &i, const WCHAR *string) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(CheckPointer(string));
+ SS_POSTCONDITION(RETVAL == Match(i, SString(string)));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(string);
+ SS_RETURN FindBack(i, s);
+}
+
+inline BOOL SString::FindBackASCII(CIterator &i, const CHAR *string) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(CheckPointer(string));
+ SS_POSTCONDITION(RETVAL == Match(i, SString(SString::Ascii, string)));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(SString::Ascii, string);
+ SS_RETURN FindBack(i, s);
+}
+
+inline BOOL SString::FindBackUTF8(CIterator &i, const CHAR *string) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(CheckPointer(string));
+ SS_POSTCONDITION(RETVAL == Match(i, SString(SString::Ascii, string)));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(SString::Utf8, string);
+ SS_RETURN FindBack(i, s);
+}
+
+// Insert string at iterator position
+inline void SString::Insert(const Iterator &i, const SString &s)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(s.Check());
+ THROWS;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ Replace(i, 0, s);
+
+ SS_RETURN;
+}
+
+inline void SString::Insert(const Iterator &i, const WCHAR *string)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(CheckPointer(string));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(string);
+ Replace(i, 0, s);
+
+ SS_RETURN;
+}
+
+inline void SString::InsertASCII(const Iterator &i, const CHAR *string)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(CheckPointer(string));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(SString::Ascii, string);
+ Replace(i, 0, s);
+
+ SS_RETURN;
+}
+
+inline void SString::InsertUTF8(const Iterator &i, const CHAR *string)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(CheckPointer(string));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ StackSString s(SString::Utf8, string);
+ Replace(i, 0, s);
+
+ SS_RETURN;
+}
+
+// Delete string at iterator position
+inline void SString::Delete(const Iterator &i, COUNT_T length)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i, length));
+ THROWS;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ Replace(i, length, Empty());
+
+ SS_RETURN;
+}
+
+// Preallocate some space for the string buffer
+inline void SString::Preallocate(COUNT_T characters) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Assume unicode since we may get converted
+ SBuffer::Preallocate(characters * sizeof(WCHAR));
+}
+
+// Trim unused space from the buffer
+inline void SString::Trim() const
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (GetRawCount() == 0)
+ {
+ // Share the global empty string buffer.
+ const_cast<SString *>(this)->SBuffer::SetImmutable(s_EmptyBuffer, sizeof(s_EmptyBuffer));
+ }
+ else
+ {
+ SBuffer::Trim();
+ }
+}
+
+// RETURN true if the string is empty.
+inline BOOL SString::IsEmpty() const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ NOTHROW;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ SS_RETURN (GetRawCount() == 0);
+}
+
+// RETURN true if the string rep is ASCII.
+inline BOOL SString::IsASCII() const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ NOTHROW;
+ }
+ SS_CONTRACT_END;
+
+ SS_RETURN IsRepresentation(REPRESENTATION_ASCII);
+}
+
+// Get the number of characters in the string (excluding the terminating NULL)
+inline COUNT_T SString::GetCount() const
+{
+ SS_CONTRACT(COUNT_T)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckCount(RETVAL));
+ THROWS_UNLESS_NORMALIZED;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToFixed();
+
+ SS_RETURN SizeToCount(GetSize());
+}
+
+// Private helpers:
+// Return the current size of the string (even if it is multibyte)
+inline COUNT_T SString::GetRawCount() const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return SizeToCount(GetSize());
+}
+
+// Private helpers:
+// get string contents as a particular character set:
+
+inline ASCII *SString::GetRawASCII() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (ASCII *) m_buffer;
+}
+
+inline UTF8 *SString::GetRawUTF8() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (UTF8 *) m_buffer;
+}
+
+inline ANSI *SString::GetRawANSI() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (ANSI *) m_buffer;
+}
+
+inline WCHAR *SString::GetRawUnicode() const
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ return (WCHAR *)m_buffer;
+}
+
+// Private helper:
+// get the representation (ansi, unicode, utf8)
+inline SString::Representation SString::GetRepresentation() const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (Representation) SBuffer::GetRepresentationField();
+}
+
+// Private helper.
+// Set the representation.
+inline void SString::SetRepresentation(SString::Representation representation)
+{
+#ifdef SSTRING_EXTRA_CHECKS
+ CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckRepresentation(representation));
+ POSTCONDITION(GetRepresentation() == representation);
+ }
+ CONTRACT_END;
+#else //SSTRING_EXTRA_CHECKS
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+#endif //SSTRING_EXTRA_CHECKS
+
+ SBuffer::SetRepresentationField((int) representation);
+
+ SS_RETURN;
+}
+
+// Private helper:
+// Get the amount to shift the byte size to get a character count
+inline int SString::GetCharacterSizeShift() const
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Note that the flag is backwards; we want the default
+ // value to match the default representation (empty)
+ return (GetRepresentation()&REPRESENTATION_SINGLE_MASK) == 0;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// We know the buffer should be m_count characters. Place a null terminator
+// in the buffer to make our internal string null-terminated at that length.
+//----------------------------------------------------------------------------
+FORCEINLINE void SString::NullTerminate()
+{
+ SUPPORTS_DAC_HOST_ONLY;
+#ifdef SSTRING_EXTRA_CHECKS
+ CONTRACT_VOID
+ {
+ POSTCONDITION(CheckPointer(this));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+#else //SSTRING_EXTRA_CHECKS
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+#endif //SSTRING_EXTRA_CHECKS
+
+ BYTE *end = m_buffer + GetSize();
+
+ if (GetRepresentation()&REPRESENTATION_SINGLE_MASK)
+ {
+ ((CHAR *)end)[-1] = 0;
+ }
+ else
+ {
+ ((WCHAR *)end)[-1] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//----------------------------------------------------------------------------
+// private helper
+// Return true if the string is a literal.
+// A literal string has immutable memory.
+//----------------------------------------------------------------------------
+inline BOOL SString::IsLiteral() const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return SBuffer::IsImmutable() && (m_buffer != s_EmptyBuffer);
+}
+
+//----------------------------------------------------------------------------
+// private helper:
+// RETURN true if the string allocated (and should delete) its buffer.
+// IsAllocated() will RETURN false for Literal strings and
+// stack-based strings (the buffer is on the stack)
+//----------------------------------------------------------------------------
+inline BOOL SString::IsAllocated() const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return SBuffer::IsAllocated();
+}
+
+//----------------------------------------------------------------------------
+// Return true after we call OpenBuffer(), but before we close it.
+// All SString operations are illegal while the buffer is open.
+//----------------------------------------------------------------------------
+#if _DEBUG
+inline BOOL SString::IsBufferOpen() const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return SBuffer::IsOpened();
+}
+#endif
+
+//----------------------------------------------------------------------------
+// Return true if we've scanned the string to see if it is in the ASCII subset.
+//----------------------------------------------------------------------------
+inline BOOL SString::IsASCIIScanned() const
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ return SBuffer::IsFlag1();
+}
+
+//----------------------------------------------------------------------------
+// Set that we've scanned the string to see if it is in the ASCII subset.
+//----------------------------------------------------------------------------
+inline void SString::SetASCIIScanned() const
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ const_cast<SString *>(this)->SBuffer::SetFlag1();
+}
+
+//----------------------------------------------------------------------------
+// Return true if we've normalized the string to unicode
+//----------------------------------------------------------------------------
+inline BOOL SString::IsNormalized() const
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ return SBuffer::IsFlag3();
+}
+
+//----------------------------------------------------------------------------
+// Set that we've normalized the string to unicode
+//----------------------------------------------------------------------------
+inline void SString::SetNormalized() const
+{
+ WRAPPER_NO_CONTRACT;
+
+ const_cast<SString *>(this)->SBuffer::SetFlag3();
+}
+
+//----------------------------------------------------------------------------
+// Clear normalization
+//----------------------------------------------------------------------------
+inline void SString::ClearNormalized() const
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ const_cast<SString *>(this)->SBuffer::ClearFlag3();
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Check to see if the string representation has single byte size
+//----------------------------------------------------------------------------
+inline BOOL SString::IsSingleByte() const
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ return ((GetRepresentation()&REPRESENTATION_SINGLE_MASK) != 0);
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Check to see if the string representation has fixed size characters
+//----------------------------------------------------------------------------
+inline BOOL SString::IsFixedSize() const
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ if (GetRepresentation()&REPRESENTATION_VARIABLE_MASK)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Check to see if the string representation is appropriate for iteration
+//----------------------------------------------------------------------------
+inline BOOL SString::IsIteratable() const
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ // Note that in many cases ANSI may be fixed width. However we
+ // currently still do not allow iterating on them, because we would have to
+ // do character-by-character conversion on a character dereference (which must
+ // go to unicode) . We may want to adjust this going forward to
+ // depending on perf in the non-ASCII but fixed width ANSI case.
+
+ return ((GetRepresentation()&REPRESENTATION_VARIABLE_MASK) == 0);
+}
+
+//----------------------------------------------------------------------------
+// Private helper
+// Return the size of the given string in bytes
+// in the given representation.
+// count does not include the null-terminator, but the RETURN value does.
+//----------------------------------------------------------------------------
+inline COUNT_T SString::CountToSize(COUNT_T count) const
+{
+ SS_CONTRACT(COUNT_T)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckCount(count));
+ SS_POSTCONDITION(SizeToCount(RETVAL) == count);
+ NOTHROW;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ SS_RETURN (count+1) << GetCharacterSizeShift();
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Return the maxmimum count of characters that could fit in a buffer of
+// 'size' bytes in the given representation.
+// 'size' includes the null terminator, but the RETURN value does not.
+//----------------------------------------------------------------------------
+inline COUNT_T SString::SizeToCount(COUNT_T size) const
+{
+ SS_CONTRACT(COUNT_T)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckSize(size));
+ SS_POSTCONDITION(CountToSize(RETVAL) == size);
+ NOTHROW;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ SS_RETURN (size >> GetCharacterSizeShift()) - 1;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Return the maxmimum count of characters that could fit in the current
+// buffer including NULL terminator.
+//----------------------------------------------------------------------------
+inline COUNT_T SString::GetBufferSizeInCharIncludeNullChar() const
+{
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ return (GetSize() >> GetCharacterSizeShift());
+}
+
+
+
+//----------------------------------------------------------------------------
+// Assert helper
+// Asser that the iterator is within the given string.
+//----------------------------------------------------------------------------
+inline CHECK SString::CheckIteratorRange(const CIterator &i) const
+{
+ CANNOT_HAVE_CONTRACT;
+ CHECK(i >= Begin());
+ CHECK(i <= End()); // Note that it's OK to look at the terminating null
+ CHECK_OK;
+}
+
+//----------------------------------------------------------------------------
+// Assert helper
+// Asser that the iterator is within the given string.
+//----------------------------------------------------------------------------
+inline CHECK SString::CheckIteratorRange(const CIterator &i, COUNT_T length) const
+{
+ CANNOT_HAVE_CONTRACT;
+ CHECK(i >= Begin());
+ CHECK(i + length <= End()); // Note that it's OK to look at the terminating null
+ CHECK_OK;
+}
+
+//----------------------------------------------------------------------------
+// Assert that the string is empty
+//----------------------------------------------------------------------------
+inline CHECK SString::CheckEmpty() const
+{
+ CANNOT_HAVE_CONTRACT;
+ CHECK(IsEmpty());
+ CHECK_OK;
+}
+
+//----------------------------------------------------------------------------
+// Check the range of a count
+//----------------------------------------------------------------------------
+inline CHECK SString::CheckCount(COUNT_T count)
+{
+ CANNOT_HAVE_CONTRACT;
+ CHECK(CheckSize(count*sizeof(WCHAR)));
+ CHECK_OK;
+}
+
+//----------------------------------------------------------------------------
+// Check the representation field
+//----------------------------------------------------------------------------
+inline CHECK SString::CheckRepresentation(int representation)
+{
+ CANNOT_HAVE_CONTRACT;
+ CHECK(representation == REPRESENTATION_EMPTY
+ || representation == REPRESENTATION_UNICODE
+ || representation == REPRESENTATION_ASCII
+ || representation == REPRESENTATION_UTF8
+ || representation == REPRESENTATION_ANSI);
+ CHECK((representation & REPRESENTATION_MASK) == representation);
+
+ CHECK_OK;
+}
+
+#if CHECK_INVARIANTS
+//----------------------------------------------------------------------------
+// Assert helper. Check that the string only uses the ASCII subset of
+// codes.
+//----------------------------------------------------------------------------
+inline CHECK SString::CheckASCIIString(const CHAR *string)
+{
+ CANNOT_HAVE_CONTRACT;
+ if (string != NULL)
+ CHECK(CheckASCIIString(string, (int) strlen(string)));
+ CHECK_OK;
+}
+
+inline CHECK SString::CheckASCIIString(const CHAR *string, COUNT_T count)
+{
+ CANNOT_HAVE_CONTRACT;
+#if _DEBUG
+ const CHAR *sEnd = string + count;
+ while (string < sEnd)
+ {
+ CHECK_MSG((*string & 0x80) == 0x00, "Found non-ASCII character in string.");
+ string++;
+ }
+#endif
+ CHECK_OK;
+}
+
+//----------------------------------------------------------------------------
+// Check routine and invariants.
+//----------------------------------------------------------------------------
+
+inline CHECK SString::Check() const
+{
+ CANNOT_HAVE_CONTRACT;
+ CHECK(SBuffer::Check());
+ CHECK_OK;
+}
+
+inline CHECK SString::Invariant() const
+{
+ CANNOT_HAVE_CONTRACT;
+ CHECK(SBuffer::Invariant());
+ CHECK_OK;
+}
+
+inline CHECK SString::InternalInvariant() const
+{
+ CANNOT_HAVE_CONTRACT;
+ CHECK(SBuffer::InternalInvariant());
+ CHECK(SBuffer::GetSize() >= 2);
+ if (IsNormalized())
+ CHECK(IsRepresentation(REPRESENTATION_UNICODE));
+ CHECK_OK;
+}
+#endif // CHECK_INVARIANTS
+
+//----------------------------------------------------------------------------
+// Return a writeable buffer that can store 'countChars'+1 unicode characters.
+// Call CloseBuffer when done.
+//----------------------------------------------------------------------------
+inline WCHAR *SString::OpenUnicodeBuffer(COUNT_T countChars)
+{
+ SS_CONTRACT(WCHAR*)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckCount(countChars));
+#if _DEBUG
+ SS_POSTCONDITION(IsBufferOpen());
+#endif
+ SS_POSTCONDITION(GetRawCount() == countChars);
+ SS_POSTCONDITION(GetRepresentation() == REPRESENTATION_UNICODE || countChars == 0);
+ SS_POSTCONDITION(CheckPointer(RETVAL));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ OpenBuffer(REPRESENTATION_UNICODE, countChars);
+ SS_RETURN GetRawUnicode();
+}
+
+//----------------------------------------------------------------------------
+// Return a copy of the underlying buffer, the caller is responsible for managing
+// the returned memory
+//----------------------------------------------------------------------------
+inline WCHAR *SString::GetCopyOfUnicodeString()
+{
+ SS_CONTRACT(WCHAR*)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckPointer(buffer));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+ NewArrayHolder<WCHAR> buffer = NULL;
+
+ buffer = new WCHAR[GetCount() +1];
+ wcscpy_s(buffer, GetCount() + 1, GetUnicode());
+
+ SS_RETURN buffer.Extract();
+}
+
+//----------------------------------------------------------------------------
+// Return a writeable buffer that can store 'countChars'+1 ansi characters.
+// Call CloseBuffer when done.
+//----------------------------------------------------------------------------
+inline ANSI *SString::OpenANSIBuffer(COUNT_T countChars)
+{
+ SS_CONTRACT(ANSI*)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckCount(countChars));
+#if _DEBUG
+ SS_POSTCONDITION(IsBufferOpen());
+#endif
+ SS_POSTCONDITION(GetRawCount() == countChars);
+ SS_POSTCONDITION(GetRepresentation() == REPRESENTATION_ANSI || countChars == 0);
+ SS_POSTCONDITION(CheckPointer(RETVAL));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ OpenBuffer(REPRESENTATION_ANSI, countChars);
+ SS_RETURN GetRawANSI();
+}
+
+//----------------------------------------------------------------------------
+// Return a writeable buffer that can store 'countChars'+1 ansi characters.
+// Call CloseBuffer when done.
+//----------------------------------------------------------------------------
+inline UTF8 *SString::OpenUTF8Buffer(COUNT_T countBytes)
+{
+ SS_CONTRACT(UTF8*)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckCount(countBytes));
+#if _DEBUG
+ SS_POSTCONDITION(IsBufferOpen());
+#endif
+ SS_POSTCONDITION(GetRawCount() == countBytes);
+ SS_POSTCONDITION(GetRepresentation() == REPRESENTATION_UTF8 || countBytes == 0);
+ SS_POSTCONDITION(CheckPointer(RETVAL));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ OpenBuffer(REPRESENTATION_UTF8, countBytes);
+ SS_RETURN GetRawUTF8();
+}
+
+//----------------------------------------------------------------------------
+// Private helper to open a raw buffer.
+// Called by public functions to open the buffer in the specific
+// representation.
+// While the buffer is opened, all other operations are illegal. Call
+// CloseBuffer() when done.
+//----------------------------------------------------------------------------
+inline void SString::OpenBuffer(SString::Representation representation, COUNT_T countChars)
+{
+#ifdef SSTRING_EXTRA_CHECKS
+ CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION_MSG(!IsBufferOpen(), "Can't nest calls to OpenBuffer()");
+ PRECONDITION(CheckRepresentation(representation));
+ PRECONDITION(CheckSize(countChars));
+#if _DEBUG
+ POSTCONDITION(IsBufferOpen());
+#endif
+ POSTCONDITION(GetRawCount() == countChars);
+ POSTCONDITION(GetRepresentation() == representation || countChars == 0);
+ THROWS;
+ }
+ CONTRACT_END;
+#else
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_THROWS;
+#endif
+
+ Resize(countChars, representation);
+
+ SBuffer::OpenRawBuffer(CountToSize(countChars));
+
+ SS_RETURN;
+}
+
+//----------------------------------------------------------------------------
+// Get the max size that can be passed to OpenUnicodeBuffer without causing
+// allocations.
+//----------------------------------------------------------------------------
+inline COUNT_T SString::GetUnicodeAllocation()
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ COUNT_T allocation = GetAllocation();
+ return ( (allocation > sizeof(WCHAR))
+ ? (allocation - sizeof(WCHAR)) / sizeof(WCHAR) : 0 );
+}
+
+//----------------------------------------------------------------------------
+// Close an open buffer. Assumes that we wrote exactly number of characters
+// we requested in OpenBuffer.
+//----------------------------------------------------------------------------
+inline void SString::CloseBuffer()
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+#if _DEBUG
+ PRECONDITION_MSG(IsBufferOpen(), "Can only CloseBuffer() after a call to OpenBuffer()");
+#endif
+ SS_POSTCONDITION(CheckPointer(this));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ SBuffer::CloseRawBuffer();
+ NullTerminate();
+
+ SS_RETURN;
+}
+
+//----------------------------------------------------------------------------
+// CloseBuffer() tells the SString that we're done using the unsafe buffer.
+// countChars is the count of characters actually used (so we can set m_count).
+// This is important if we request a buffer larger than what we actually
+// used.
+//----------------------------------------------------------------------------
+inline void SString::CloseBuffer(COUNT_T finalCount)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+#if _DEBUG
+ PRECONDITION_MSG(IsBufferOpen(), "Can only CloseBuffer() after a call to OpenBuffer()");
+#endif
+ PRECONDITION(CheckSize(finalCount));
+ SS_POSTCONDITION(CheckPointer(this));
+ SS_POSTCONDITION(GetRawCount() == finalCount);
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ SBuffer::CloseRawBuffer(CountToSize(finalCount));
+ NullTerminate();
+
+ SS_RETURN;
+}
+
+//----------------------------------------------------------------------------
+// EnsureWritable
+// Ensures that the buffer is writable
+//----------------------------------------------------------------------------
+inline void SString::EnsureWritable() const
+{
+#ifdef SSTRING_EXTRA_CHECKS
+ CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ POSTCONDITION(!IsLiteral());
+ THROWS;
+ }
+ CONTRACT_END;
+#else //SSTRING_EXTRA_CHECKS
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_THROWS;
+#endif //SSTRING_EXTRA_CHECKS
+
+ if (IsLiteral())
+ const_cast<SString *>(this)->Resize(GetRawCount(), GetRepresentation(), PRESERVE);
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Convert the internal representation to be a fixed size
+//-----------------------------------------------------------------------------
+inline void SString::ConvertToFixed() const
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ SS_PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(IsFixedSize());
+ THROWS_UNLESS_NORMALIZED;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ // If we're already fixed size, great.
+ if (IsFixedSize())
+ SS_RETURN;
+
+ // See if we can coerce it to ASCII.
+ if (ScanASCII())
+ SS_RETURN;
+
+ // Convert to unicode then.
+ ConvertToUnicode();
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Convert the internal representation to be an iteratable one (current
+// requirements here are that it be trivially convertable to unicode chars.)
+//-----------------------------------------------------------------------------
+inline void SString::ConvertToIteratable() const
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ SS_PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(IsIteratable());
+ THROWS_UNLESS_NORMALIZED;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ // If we're already iteratable, great.
+ if (IsIteratable())
+ SS_RETURN;
+
+ // See if we can coerce it to ASCII.
+ if (ScanASCII())
+ SS_RETURN;
+
+ // Convert to unicode then.
+ ConvertToUnicode();
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Create iterators on the string.
+//-----------------------------------------------------------------------------
+
+inline SString::UIterator SString::BeginUnicode()
+{
+ SS_CONTRACT(SString::UIterator)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckValue(RETVAL));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUnicode();
+ EnsureWritable();
+
+ SS_RETURN UIterator(this, 0);
+}
+
+inline SString::UIterator SString::EndUnicode()
+{
+ SS_CONTRACT(SString::UIterator)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckValue(RETVAL));
+ THROWS;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUnicode();
+ EnsureWritable();
+
+ SS_RETURN UIterator(this, GetCount());
+}
+
+//-----------------------------------------------------------------------------
+// Create CIterators on the string.
+//-----------------------------------------------------------------------------
+
+FORCEINLINE SString::CIterator SString::Begin() const
+{
+ SS_CONTRACT(SString::CIterator)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckValue(RETVAL));
+ THROWS_UNLESS_NORMALIZED;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToIteratable();
+
+ SS_RETURN CIterator(this, 0);
+}
+
+FORCEINLINE SString::CIterator SString::End() const
+{
+ SS_CONTRACT(SString::CIterator)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckValue(RETVAL));
+ THROWS_UNLESS_NORMALIZED;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToIteratable();
+
+ SS_RETURN CIterator(this, GetCount());
+}
+
+//-----------------------------------------------------------------------------
+// Create Iterators on the string.
+//-----------------------------------------------------------------------------
+
+FORCEINLINE SString::Iterator SString::Begin()
+{
+ SS_CONTRACT(SString::Iterator)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckValue(RETVAL));
+ THROWS; // EnsureMutable always throws
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToIteratable();
+ EnsureMutable();
+
+ SS_RETURN Iterator(this, 0);
+}
+
+FORCEINLINE SString::Iterator SString::End()
+{
+ SS_CONTRACT(SString::Iterator)
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckValue(RETVAL));
+ THROWS; // EnsureMutable always Throws
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToIteratable();
+ EnsureMutable();
+
+ SS_RETURN Iterator(this, GetCount());
+}
+
+//-----------------------------------------------------------------------------
+// CIterator support routines
+//-----------------------------------------------------------------------------
+
+inline SString::Index::Index()
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+inline SString::Index::Index(SString *string, SCOUNT_T index)
+ : SBuffer::Index(string, index<<string->GetCharacterSizeShift())
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(string));
+ PRECONDITION(string->IsIteratable());
+ PRECONDITION(DoCheck(0));
+ SS_POSTCONDITION(CheckPointer(this));
+ // POSTCONDITION(Subtract(string->Begin()) == index); contract violation - fix later
+ NOTHROW;
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ m_characterSizeShift = string->GetCharacterSizeShift();
+
+ SS_RETURN;
+}
+
+inline BYTE &SString::Index::GetAt(SCOUNT_T delta) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return m_ptr[delta<<m_characterSizeShift];
+}
+
+inline void SString::Index::Skip(SCOUNT_T delta)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ m_ptr += (delta<<m_characterSizeShift);
+}
+
+inline SCOUNT_T SString::Index::Subtract(const Index &i) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (SCOUNT_T) ((m_ptr - i.m_ptr)>>m_characterSizeShift);
+}
+
+inline CHECK SString::Index::DoCheck(SCOUNT_T delta) const
+{
+ CANNOT_HAVE_CONTRACT;
+#if _DEBUG
+ const SString *string = (const SString *) GetContainerDebug();
+
+ CHECK(m_ptr + (delta<<m_characterSizeShift) >= string->m_buffer);
+ CHECK(m_ptr + (delta<<m_characterSizeShift) < string->m_buffer + string->GetSize());
+#endif
+ CHECK_OK;
+}
+
+inline void SString::Index::Resync(const SString *string, BYTE *ptr) const
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ SBuffer::Index::Resync(string, ptr);
+
+ const_cast<SString::Index*>(this)->m_characterSizeShift = string->GetCharacterSizeShift();
+}
+
+
+inline const WCHAR *SString::Index::GetUnicode() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (const WCHAR *) m_ptr;
+}
+
+inline const CHAR *SString::Index::GetASCII() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (const CHAR *) m_ptr;
+}
+
+inline WCHAR SString::Index::operator*() const
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ if (m_characterSizeShift == 0)
+ return *(CHAR*)&GetAt(0);
+ else
+ return *(WCHAR*)&GetAt(0);
+}
+
+inline void SString::Index::operator->() const
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+inline WCHAR SString::Index::operator[](int index) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_characterSizeShift == 0)
+ return *(CHAR*)&GetAt(index);
+ else
+ return *(WCHAR*)&GetAt(index);
+}
+
+//-----------------------------------------------------------------------------
+// Iterator support routines
+//-----------------------------------------------------------------------------
+
+inline SString::UIndex::UIndex()
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+inline SString::UIndex::UIndex(SString *string, SCOUNT_T index)
+ : SBuffer::Index(string, index*sizeof(WCHAR))
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(string));
+ PRECONDITION(string->IsRepresentation(REPRESENTATION_UNICODE));
+ PRECONDITION(DoCheck(0));
+ SS_POSTCONDITION(CheckPointer(this));
+ NOTHROW;
+ CANNOT_TAKE_LOCK;
+ }
+ SS_CONTRACT_END;
+
+ SS_RETURN;
+}
+
+inline WCHAR &SString::UIndex::GetAt(SCOUNT_T delta) const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return ((WCHAR*)m_ptr)[delta];
+}
+
+inline void SString::UIndex::Skip(SCOUNT_T delta)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_ptr += delta * sizeof(WCHAR);
+}
+
+inline SCOUNT_T SString::UIndex::Subtract(const UIndex &i) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (SCOUNT_T) (GetUnicode() - i.GetUnicode());
+}
+
+inline CHECK SString::UIndex::DoCheck(SCOUNT_T delta) const
+{
+ CANNOT_HAVE_CONTRACT;
+#if _DEBUG
+ const SString *string = (const SString *) GetContainerDebug();
+
+ CHECK(GetUnicode() + delta >= string->GetRawUnicode());
+ CHECK(GetUnicode() + delta <= string->GetRawUnicode() + string->GetCount());
+#endif
+
+ CHECK_OK;
+}
+
+inline WCHAR *SString::UIndex::GetUnicode() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (WCHAR*) m_ptr;
+}
+
+//-----------------------------------------------------------------------------
+// Opaque scratch buffer class routines
+//-----------------------------------------------------------------------------
+inline SString::AbstractScratchBuffer::AbstractScratchBuffer(void *buffer, COUNT_T size)
+ : SString(buffer, size)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckCount(size));
+ NOTHROW;
+ }
+ SS_CONTRACT_END;
+
+ SS_RETURN;
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+#endif // _SSTRING_INL_
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-//-----------------------------------------------------------------------------
-
-//-----------------------------------------------------------------------------
-
-#ifndef __STACK_TRACE_H__
-#define __STACK_TRACE_H__
-
-HINSTANCE LoadImageHlp();
-HINSTANCE LoadDbgHelp();
-
-#include <specstrings.h>
-
-//
-//--- Constants ---------------------------------------------------------------
-//
-
-#define cchMaxAssertModuleLen 60
-#define cchMaxAssertSymbolLen 257
-#define cfrMaxAssertStackLevels 20
-#define cchMaxAssertExprLen 257
-
-#ifdef HOST_64BIT
-
-#define cchMaxAssertStackLevelStringLen \
- ((3 * 8) + cchMaxAssertModuleLen + cchMaxAssertSymbolLen + 13)
- // 3 addresses of at most 8 char, module, symbol, and the extra chars:
- // 0x<address>: <module>! <symbol> + 0x<offset>\n
- //FMT_ADDR_BARE is defined as "%08x`%08x" on Win64, and as
- //"%08x" on 32 bit platforms. Hence the difference in the definitions.
-
-#else
-
-#define cchMaxAssertStackLevelStringLen \
- ((2 * 8) + cchMaxAssertModuleLen + cchMaxAssertSymbolLen + 12)
- // 2 addresses of at most 8 char, module, symbol, and the extra chars:
- // 0x<address>: <module>! <symbol> + 0x<offset>\n
-
-#endif
-
-//
-//--- Prototypes --------------------------------------------------------------
-//
-
-/****************************************************************************
-* MagicDeinit *
-*-------------*
-* Description:
-* Cleans up for the symbol loading code. Should be called before
-* exiting in order to free the dynamically loaded imagehlp.dll
-******************************************************************** robch */
-void MagicDeinit(void);
-
-/****************************************************************************
-* GetStringFromStackLevels *
-*--------------------------*
-* Description:
-* Retrieves a string from the stack frame. If more than one frame, they
-* are separated by newlines. Each fram appears in this format:
-*
-* 0x<address>: <module>! <symbol> + 0x<offset>
-******************************************************************** robch */
-void GetStringFromStackLevels(UINT ifrStart, UINT cfrTotal, _Out_writes_(cchMaxAssertStackLevelStringLen * cfrTotal) CHAR *pszString, struct _CONTEXT * pContext = NULL);
-
-/****************************************************************************
-* GetStringFromAddr *
-*-------------------*
-* Description:
-* Builds a string from an address in the format:
-*
-* 0x<address>: <module>! <symbol> + 0x<offset>
-******************************************************************** robch */
-void GetStringFromAddr(DWORD_PTR dwAddr, _Out_writes_(cchMaxAssertStackLevelStringLen) LPSTR szString);
-
-#if defined(HOST_X86) && !defined(TARGET_UNIX)
-/****************************************************************************
-* 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" void __stdcall ClrCaptureContext(_Out_ PCONTEXT ctx);
-#else // HOST_X86 && !TARGET_UNIX
-#define ClrCaptureContext RtlCaptureContext
-#endif // HOST_X86 && !TARGET_UNIX
-
-
-#endif
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+
+//
+// common.h - precompiled headers include for the COM+ Execution Engine
+//
+
+//
+// Make sure _ASSERTE is defined before including this header file
+// Other than that, please keep this header self-contained so that it can be included in
+// all dlls
+//
+
+
+#ifndef _stdmacros_h_
+#define _stdmacros_h_
+
+#include "specstrings.h"
+#include "contract.h"
+
+#ifndef _ASSERTE
+#error Please define _ASSERTE before including StdMacros.h
+#endif
+
+#ifdef _DEBUG
+#define DEBUG_ARG(x) , x
+#define DEBUG_ARG1(x) x
+#else
+#define DEBUG_ARG(x)
+#define DEBUG_ARG1(x)
+#endif
+
+#ifdef DACCESS_COMPILE
+#define DAC_ARG(x) , x
+#else
+#define DAC_ARG(x)
+#endif
+
+
+/********************************************/
+/* Portability macros */
+/********************************************/
+
+#ifdef TARGET_AMD64
+#define AMD64_FIRST_ARG(x) x ,
+#define AMD64_ARG(x) , x
+#define AMD64_ONLY(x) x
+#define NOT_AMD64(x)
+#define NOT_AMD64_ARG(x)
+#else
+#define AMD64_FIRST_ARG(x)
+#define AMD64_ARG(x)
+#define AMD64_ONLY(x)
+#define NOT_AMD64(x) x
+#define NOT_AMD64_ARG(x) , x
+#endif
+
+#ifdef TARGET_X86
+#define X86_FIRST_ARG(x) x ,
+#define X86_ARG(x) , x
+#define X86_ONLY(x) x
+#define NOT_X86(x)
+#define NOT_X86_ARG(x)
+#else
+#define X86_FIRST_ARG(x)
+#define X86_ARG(x)
+#define X86_ONLY(x)
+#define NOT_X86(x) x
+#define NOT_X86_ARG(x) , x
+#endif
+
+#ifdef HOST_64BIT
+#define BIT64_ARG(x) , x
+#define BIT64_ONLY(x) x
+#define NOT_BIT64(x)
+#define NOT_BIT64_ARG(x)
+#else
+#define BIT64_ARG(x)
+#define BIT64_ONLY(x)
+#define NOT_BIT64(x) x
+#define NOT_BIT64_ARG(x) , x
+#endif // HOST_64BIT
+
+#ifdef TARGET_ARM
+#define ARM_FIRST_ARG(x) x ,
+#define ARM_ARG(x) , x
+#define ARM_ONLY(x) x
+#define NOT_ARM(x)
+#define NOT_ARM_ARG(x)
+#else
+#define ARM_FIRST_ARG(x)
+#define ARM_ARG(x)
+#define ARM_ONLY(x)
+#define NOT_ARM(x) x
+#define NOT_ARM_ARG(x) , x
+#endif
+
+#ifdef TARGET_ARM64
+#define ARM64_FIRST_ARG(x) x ,
+#define ARM64_ARG(x) , x
+#define ARM64_ONLY(x) x
+#define NOT_ARM64(x)
+#define NOT_ARM64_ARG(x)
+#else
+#define ARM64_FIRST_ARG(x)
+#define ARM64_ARG(x)
+#define ARM64_ONLY(x)
+#define NOT_ARM64(x) x
+#define NOT_ARM64_ARG(x) , x
+#endif
+
+#ifdef TARGET_64BIT
+#define LOG2_PTRSIZE 3
+#else
+#define LOG2_PTRSIZE 2
+#endif
+
+#ifdef HOST_64BIT
+ #define INVALID_POINTER_CC 0xcccccccccccccccc
+ #define INVALID_POINTER_CD 0xcdcdcdcdcdcdcdcd
+ #define FMT_ADDR " %08x`%08x "
+ #define LFMT_ADDR W(" %08x`%08x ")
+ #define DBG_ADDR(ptr) (DWORD)(((UINT_PTR) (ptr)) >> 32), (DWORD)(((UINT_PTR) (ptr)) & 0xffffffff)
+#else // HOST_64BIT
+ #define INVALID_POINTER_CC 0xcccccccc
+ #define INVALID_POINTER_CD 0xcdcdcdcd
+ #define FMT_ADDR " %08x "
+ #define LFMT_ADDR W(" %08x ")
+ #define DBG_ADDR(ptr) (DWORD)((UINT_PTR)(ptr))
+#endif // HOST_64BIT
+
+#ifdef TARGET_ARM
+ #define ALIGN_ACCESS ((1<<LOG2_PTRSIZE)-1)
+#endif
+
+
+#ifndef ALLOC_ALIGN_CONSTANT
+#define ALLOC_ALIGN_CONSTANT (sizeof(void*)-1)
+#endif
+
+
+inline void *GetTopMemoryAddress(void)
+{
+ WRAPPER_NO_CONTRACT;
+
+ static void *result; // = NULL;
+ if( NULL == result )
+ {
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo( &sysInfo );
+ result = sysInfo.lpMaximumApplicationAddress;
+ }
+ return result;
+}
+inline void *GetBotMemoryAddress(void)
+{
+ WRAPPER_NO_CONTRACT;
+
+ static void *result; // = NULL;
+ if( NULL == result )
+ {
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo( &sysInfo );
+ result = sysInfo.lpMinimumApplicationAddress;
+ }
+ return result;
+}
+
+#define TOP_MEMORY (GetTopMemoryAddress())
+#define BOT_MEMORY (GetBotMemoryAddress())
+
+
+//
+// This macro returns val rounded up as necessary to be a multiple of alignment; alignment must be a power of 2
+//
+inline size_t ALIGN_UP( size_t val, size_t alignment )
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ // alignment must be a power of 2 for this implementation to work (need modulo otherwise)
+ _ASSERTE( 0 == (alignment & (alignment - 1)) );
+ size_t result = (val + (alignment - 1)) & ~(alignment - 1);
+ _ASSERTE( result >= val ); // check for overflow
+ return result;
+}
+
+template <typename T> inline T ALIGN_UP(T val, size_t alignment)
+{
+ WRAPPER_NO_CONTRACT;
+ return (T)ALIGN_UP((size_t)val, alignment);
+}
+
+inline size_t ALIGN_DOWN( size_t val, size_t alignment )
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // alignment must be a power of 2 for this implementation to work (need modulo otherwise)
+ _ASSERTE( 0 == (alignment & (alignment - 1)) );
+ size_t result = val & ~(alignment - 1);
+ return result;
+}
+inline void* ALIGN_DOWN( void* val, size_t alignment )
+{
+ WRAPPER_NO_CONTRACT;
+ return (void*) ALIGN_DOWN( (size_t)val, alignment );
+}
+inline uint8_t* ALIGN_DOWN( uint8_t* val, size_t alignment )
+{
+ WRAPPER_NO_CONTRACT;
+ return (uint8_t*) ALIGN_DOWN( (size_t)val, alignment );
+}
+
+inline BOOL IS_ALIGNED( size_t val, size_t alignment )
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ // alignment must be a power of 2 for this implementation to work (need modulo otherwise)
+ _ASSERTE( 0 == (alignment & (alignment - 1)) );
+ return 0 == (val & (alignment - 1));
+}
+inline BOOL IS_ALIGNED( const void* val, size_t alignment )
+{
+ WRAPPER_NO_CONTRACT;
+ return IS_ALIGNED( (size_t) val, alignment );
+}
+
+// Rounds a ULONG up to the nearest power of two number.
+inline ULONG RoundUpToPower2(ULONG x)
+{
+ if (x == 0) return 1;
+
+ x = x - 1;
+ x = x | (x >> 1);
+ x = x | (x >> 2);
+ x = x | (x >> 4);
+ x = x | (x >> 8);
+ x = x | (x >> 16);
+ return x + 1;
+}
+
+#ifdef ALIGN_ACCESS
+
+// NOTE: pSrc is evaluated three times!!!
+#define MAYBE_UNALIGNED_READ(pSrc, bits) (IS_ALIGNED((size_t)(pSrc), sizeof(UINT##bits)) ? \
+ (*(UINT##bits*) (pSrc)) : \
+ (GET_UNALIGNED_##bits(pSrc)) )
+
+#define MAYBE_UNALIGNED_WRITE(pDst, bits, expr) do { if (IS_ALIGNED((size_t)(pDst), sizeof(UINT##bits))) \
+ *(UINT##bits*)(pDst) = (UINT##bits)(expr); else \
+ SET_UNALIGNED_##bits(pDst, (UINT##bits)(expr)); } while (0)
+
+// these are necessary for MAYBE_UNALIGNED_XXX to work with UINT_PTR
+#define GET_UNALIGNED__PTR(x) GET_UNALIGNED_PTR(x)
+#define SET_UNALIGNED__PTR(p,x) SET_UNALIGNED_PTR(p,x)
+
+#else // ALIGN_ACCESS
+#define MAYBE_UNALIGNED_READ(pSrc, bits) (*(UINT##bits*)(pSrc))
+#define MAYBE_UNALIGNED_WRITE(pDst, bits, expr) do { *(UINT##bits*)(pDst) = (UINT##bits)(expr); } while(0)
+#endif // ALIGN_ACCESS
+
+//
+// define some useful macros for logging object
+//
+
+#define FMT_OBJECT "object" FMT_ADDR
+#define FMT_HANDLE "handle" FMT_ADDR
+#define FMT_CLASS "%s"
+#define FMT_REG "r%d "
+#define FMT_STK "sp%s0x%02x "
+#define FMT_PIPTR "%s%s pointer "
+
+
+#define DBG_GET_CLASS_NAME(pMT) \
+ (((pMT) == NULL) ? NULL : (pMT)->GetClass()->GetDebugClassName())
+
+#define DBG_CLASS_NAME_MT(pMT) \
+ (DBG_GET_CLASS_NAME(pMT) == NULL) ? "<null-class>" : DBG_GET_CLASS_NAME(pMT)
+
+#define DBG_GET_MT_FROM_OBJ(obj) \
+ (MethodTable*)((size_t)((Object*) (obj))->GetGCSafeMethodTable())
+
+#define DBG_CLASS_NAME_OBJ(obj) \
+ ((obj) == NULL) ? "null" : DBG_CLASS_NAME_MT(DBG_GET_MT_FROM_OBJ(obj))
+
+#define DBG_CLASS_NAME_IPTR2(obj,iptr) \
+ ((iptr) != 0) ? "" : DBG_CLASS_NAME_MT(DBG_GET_MT_FROM_OBJ(obj))
+
+#define DBG_CLASS_NAME_IPTR(obj,iptr) \
+ ((obj) == NULL) ? "null" : DBG_CLASS_NAME_IPTR2(obj,iptr)
+
+#define DBG_STK(off) \
+ (off >= 0) ? "+" : "-", \
+ (off >= 0) ? off : -off
+
+#define DBG_PIN_NAME(pin) \
+ (pin) ? "pinned " : ""
+
+#define DBG_IPTR_NAME(iptr) \
+ (iptr) ? "interior" : "base"
+
+#define LOG_HANDLE_OBJECT_CLASS(str1, hnd, str2, obj) \
+ str1 FMT_HANDLE str2 FMT_OBJECT FMT_CLASS "\n", \
+ DBG_ADDR(hnd), DBG_ADDR(obj), DBG_CLASS_NAME_OBJ(obj)
+
+#define LOG_OBJECT_CLASS(obj) \
+ FMT_OBJECT FMT_CLASS "\n", \
+ DBG_ADDR(obj), DBG_CLASS_NAME_OBJ(obj)
+
+#define LOG_PIPTR_OBJECT_CLASS(obj, pin, iptr) \
+ FMT_PIPTR FMT_ADDR FMT_CLASS "\n", \
+ DBG_PIN_NAME(pin), DBG_IPTR_NAME(iptr), \
+ DBG_ADDR(obj), DBG_CLASS_NAME_IPTR(obj,iptr)
+
+#define LOG_HANDLE_OBJECT(str1, hnd, str2, obj) \
+ str1 FMT_HANDLE str2 FMT_OBJECT "\n", \
+ DBG_ADDR(hnd), DBG_ADDR(obj)
+
+#define LOG_PIPTR_OBJECT(obj, pin, iptr) \
+ FMT_PIPTR FMT_ADDR "\n", \
+ DBG_PIN_NAME(pin), DBG_IPTR_NAME(iptr), \
+ DBG_ADDR(obj)
+
+#define UNIQUE_LABEL_DEF(a,x) a##x
+#define UNIQUE_LABEL_DEF_X(a,x) UNIQUE_LABEL_DEF(a,x)
+#ifdef _MSC_VER
+#define UNIQUE_LABEL(a) UNIQUE_LABEL_DEF_X(_unique_label_##a##_, __COUNTER__)
+#else
+#define UNIQUE_LABEL(a) UNIQUE_LABEL_DEF_X(_unique_label_##a##_, __LINE__)
+#endif
+
+// This is temporary. LKG should provide these macros and we should then
+// remove STRUNCATE and _TRUNCATE from here.
+
+/* error codes */
+#if !defined(STRUNCATE)
+#define STRUNCATE 80
+#endif
+
+/* _TRUNCATE */
+#if !defined(_TRUNCATE)
+#define _TRUNCATE ((size_t)-1)
+#endif
+
+#endif //_stdmacros_h_
#include "log.h"
#if defined(STRESS_LOG) && !defined(FEATURE_NO_STRESSLOG)
-#include "releaseholder.h"
-#include "volatile.h"
#include "staticcontract.h"
#include "mscoree.h"
#include "clrinternal.h"
+#include "volatile.h"
+#ifndef STRESS_LOG_ANALYZER
+#include "holder.h"
#ifdef STRESS_LOG_READONLY
#include <stddef.h> // offsetof
#else //STRESS_LOG_READONLY
#ifndef _ASSERTE
#define _ASSERTE(expr)
#endif
+#else
+#include <stddef.h> // offsetof
+#endif // STRESS_LOG_ANALYZER
/* The STRESS_LOG* macros work like printf. In fact the use printf in their implementation
so all printf format specifications work. In addition the Stress log dumper knows
static StressLog theLog; // We only have one log, and this is it
};
+#ifndef STRESS_LOG_ANALYZER
+typedef Holder<CRITSEC_COOKIE, StressLog::Enter, StressLog::Leave, NULL, CompareDefault<CRITSEC_COOKIE>> StressLogLockHolder;
+#endif //!STRESS_LOG_ANALYZER
+
+#if defined(DACCESS_COMPILE)
+inline BOOL StressLog::LogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ // StressLog isn't dacized, and besides we don't want to log to it in DAC builds.
+ return FALSE;
+}
+#endif
+
/*************************************************************************************/
/* private classes */
// define this to test data safety for the DAC. See code:DataTest::TestDataSafety.
#define TEST_DATA_CONSISTENCY
-#if !defined(STRESS_LOG) && !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
-#define STRESS_LOG
-#endif
-
#if defined(_DEBUG) && !defined(DACCESS_COMPILE)
#define USE_CHECKED_OBJECTREFS
#endif
#define FEATURE_SHARE_GENERIC_CODE
-#if defined(_DEBUG) && !defined(DACCESS_COMPILE)
- #define LOGGING
-#endif
-
-#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
-// Failpoint support
-#if defined(_DEBUG) && !defined(DACCESS_COMPILE) && !defined(TARGET_UNIX)
-#define FAILPOINTS_ENABLED
-#endif
-#endif //!defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
-
-#if 0
- // Enable to track details of EESuspension
- #define TIME_SUSPEND
-#endif // 0
-
#ifndef DACCESS_COMPILE
// Enabled to track GC statistics
#define GC_STATS
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-
//*****************************************************************************
-// UtilCode.h (dummy/empty file)
+// UtilCode.h
//
// Utility functions implemented in UtilCode.lib.
//
#ifndef __UtilCode_h__
#define __UtilCode_h__
+#include "crtwrap.h"
+#include "winwrap.h"
+#include <wchar.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <ole2.h>
+#include <oleauto.h>
+#include <limits.h>
+#include "clrtypes.h"
+#include "safewrap.h"
+#include "volatile.h"
+#include <daccess.h>
+#include "clrhost.h"
+#include "debugmacros.h"
+#include "corhlprpriv.h"
+#include "winnls.h"
+#include "check.h"
+#include "safemath.h"
+#include "new.hpp"
+
+#ifdef PAL_STDCPP_COMPAT
+#include <type_traits>
+#else
+#include "clr_std/type_traits"
+#endif
+
+#include "contract.h"
+#include "entrypoints.h"
+
#include<minipal/utils.h>
+#include "clrnt.h"
+
+#include "random.h"
+
+#define WINDOWS_KERNEL32_DLLNAME_A "kernel32"
+#define WINDOWS_KERNEL32_DLLNAME_W W("kernel32")
+
+#define CoreLibName_W W("System.Private.CoreLib")
+#define CoreLibName_IL_W W("System.Private.CoreLib.dll")
+#define CoreLibName_NI_W W("System.Private.CoreLib.ni.dll")
+#define CoreLibName_TLB_W W("System.Private.CoreLib.tlb")
+#define CoreLibName_A "System.Private.CoreLib"
+#define CoreLibName_IL_A "System.Private.CoreLib.dll"
+#define CoreLibName_NI_A "System.Private.CoreLib.ni.dll"
+#define CoreLibName_TLB_A "System.Private.CoreLib.tlb"
+#define CoreLibNameLen 22
+#define CoreLibSatelliteName_A "System.Private.CoreLib.resources"
+#define CoreLibSatelliteNameLen 32
+#define LegacyCoreLibName_A "mscorlib"
+
+class StringArrayList;
+
+#if !defined(_DEBUG_IMPL) && defined(_DEBUG) && !defined(DACCESS_COMPILE)
+#define _DEBUG_IMPL 1
+#endif
+
+#ifdef TARGET_ARM
+
+// Under ARM we generate code only with Thumb encoding. In order to ensure we execute such code in the correct
+// mode we must ensure the low-order bit is set in any code address we'll call as a sub-routine. In C++ this
+// is handled automatically for us by the compiler. When generating and working with code generated
+// dynamically we have to be careful to set or mask-out this bit as appropriate.
+#ifndef THUMB_CODE
+#define THUMB_CODE 1
+#endif
+
+// Given a WORD extract the bitfield [lowbit, highbit] (i.e. BitExtract(0xffff, 15, 0) == 0xffff).
+inline WORD BitExtract(WORD wValue, DWORD highbit, DWORD lowbit)
+{
+ _ASSERTE((highbit < 16) && (lowbit < 16) && (highbit >= lowbit));
+ return (wValue >> lowbit) & ((1 << ((highbit - lowbit) + 1)) - 1);
+}
+
+// Determine whether an ARM Thumb mode instruction is 32-bit or 16-bit based on the first WORD of the
+// instruction.
+inline bool Is32BitInstruction(WORD opcode)
+{
+ return BitExtract(opcode, 15, 11) >= 0x1d;
+}
+
+template <typename ResultType, typename SourceType>
+inline ResultType DataPointerToThumbCode(SourceType pCode)
+{
+ return (ResultType)(((UINT_PTR)pCode) | THUMB_CODE);
+}
+
+template <typename ResultType, typename SourceType>
+inline ResultType ThumbCodeToDataPointer(SourceType pCode)
+{
+ return (ResultType)(((UINT_PTR)pCode) & ~THUMB_CODE);
+}
+
+#endif // TARGET_ARM
+
+// Convert from a PCODE to the corresponding PINSTR. On many architectures this will be the identity function;
+// on ARM, this will mask off the THUMB bit.
+inline TADDR PCODEToPINSTR(PCODE pc)
+{
+#ifdef TARGET_ARM
+ return ThumbCodeToDataPointer<TADDR,PCODE>(pc);
+#else
+ return dac_cast<PCODE>(pc);
+#endif
+}
+
+// Convert from a PINSTR to the corresponding PCODE. On many architectures this will be the identity function;
+// on ARM, this will raise the THUMB bit.
+inline PCODE PINSTRToPCODE(TADDR addr)
+{
+#ifdef TARGET_ARM
+ return DataPointerToThumbCode<PCODE,TADDR>(addr);
+#else
+ return dac_cast<PCODE>(addr);
+#endif
+}
+
+typedef LPCSTR LPCUTF8;
+typedef LPSTR LPUTF8;
+
+#include "nsutilpriv.h"
+
+#include "stdmacros.h"
+
+//********** Macros. **********************************************************
+#ifndef FORCEINLINE
+ #if _MSC_VER < 1200
+ #define FORCEINLINE inline
+ #else
+ #define FORCEINLINE __forceinline
+ #endif
+#endif
+
+#ifndef DEBUG_NOINLINE
+#if defined(_DEBUG)
+#define DEBUG_NOINLINE NOINLINE
+#else
+#define DEBUG_NOINLINE
+#endif
+#endif
+
+#include <stddef.h> // for offsetof
+#include <minipal/utils.h>
+
+#define IS_DIGIT(ch) (((ch) >= W('0')) && ((ch) <= W('9')))
+#define DIGIT_TO_INT(ch) ((ch) - W('0'))
+#define INT_TO_DIGIT(i) ((WCHAR)(W('0') + (i)))
+
+#define IS_HEXDIGIT(ch) ((((ch) >= W('a')) && ((ch) <= W('f'))) || \
+ (((ch) >= W('A')) && ((ch) <= W('F'))))
+#define HEXDIGIT_TO_INT(ch) ((towlower(ch) - W('a')) + 10)
+#define INT_TO_HEXDIGIT(i) ((WCHAR)(W('a') + ((i) - 10)))
+
+
+// Helper will 4 byte align a value, rounding up.
+#define ALIGN4BYTE(val) (((val) + 3) & ~0x3)
+
+#ifdef _DEBUG
+#define DEBUGARG(x) , x
+#else
+#define DEBUGARG(x)
+#endif
+
+#ifndef sizeofmember
+// Returns the size of a class or struct member.
+#define sizeofmember(c,m) (sizeof(((c*)0)->m))
+#endif
+
+//=--------------------------------------------------------------------------=
+// Prefast helpers.
+//
+
+#include "safemath.h"
+
+
+//=--------------------------------------------------------------------------=
+// string helpers.
+
+//
+// given and ANSI String, copy it into a wide buffer.
+// be careful about scoping when using this macro!
+//
+// how to use the below two macros:
+//
+// ...
+// LPSTR pszA;
+// pszA = MyGetAnsiStringRoutine();
+// MAKE_WIDEPTR_FROMANSI(pwsz, pszA);
+// MyUseWideStringRoutine(pwsz);
+// ...
+//
+// similarily for MAKE_ANSIPTR_FROMWIDE. note that the first param does not
+// have to be declared, and no clean up must be done.
+//
+
+// We'll define an upper limit that allows multiplication by 4 (the max
+// bytes/char in UTF-8) but still remains positive, and allows some room for pad.
+// Under normal circumstances, we should never get anywhere near this limit.
+#define MAKE_MAX_LENGTH 0x1fffff00
+
+#ifndef MAKE_TOOLONGACTION
+#define MAKE_TOOLONGACTION ThrowHR(COR_E_OVERFLOW)
+#endif
+
+#ifndef MAKE_TRANSLATIONFAILED
+#define MAKE_TRANSLATIONFAILED ThrowWin32(ERROR_NO_UNICODE_TRANSLATION)
+#endif
+
+// This version throws on conversion errors (ie, no best fit character
+// mapping to characters that look similar, and no use of the default char
+// ('?') when printing out unrepresentable characters. Use this method for
+// most development in the EE, especially anything like metadata or class
+// names. See the BESTFIT version if you're printing out info to the console.
+#define MAKE_MULTIBYTE_FROMWIDE(ptrname, widestr, codepage) \
+ int __l##ptrname = (int)wcslen(widestr); \
+ if (__l##ptrname > MAKE_MAX_LENGTH) \
+ MAKE_TOOLONGACTION; \
+ __l##ptrname = (int)((__l##ptrname + 1) * 2 * sizeof(char)); \
+ CQuickBytes __CQuickBytes##ptrname; \
+ __CQuickBytes##ptrname.AllocThrows(__l##ptrname); \
+ BOOL __b##ptrname; \
+ DWORD __cBytes##ptrname = WszWideCharToMultiByte(codepage, WC_NO_BEST_FIT_CHARS, widestr, -1, (LPSTR)__CQuickBytes##ptrname.Ptr(), __l##ptrname, NULL, &__b##ptrname); \
+ if (__b##ptrname || (__cBytes##ptrname == 0 && (widestr[0] != W('\0')))) { \
+ MAKE_TRANSLATIONFAILED; \
+ } \
+ LPSTR ptrname = (LPSTR)__CQuickBytes##ptrname.Ptr()
+
+// This version does best fit character mapping and also allows the use
+// of the default char ('?') for any Unicode character that isn't
+// representable. This is reasonable for writing to the console, but
+// shouldn't be used for most string conversions.
+#define MAKE_MULTIBYTE_FROMWIDE_BESTFIT(ptrname, widestr, codepage) \
+ int __l##ptrname = (int)wcslen(widestr); \
+ if (__l##ptrname > MAKE_MAX_LENGTH) \
+ MAKE_TOOLONGACTION; \
+ __l##ptrname = (int)((__l##ptrname + 1) * 2 * sizeof(char)); \
+ CQuickBytes __CQuickBytes##ptrname; \
+ __CQuickBytes##ptrname.AllocThrows(__l##ptrname); \
+ DWORD __cBytes##ptrname = WszWideCharToMultiByte(codepage, 0, widestr, -1, (LPSTR)__CQuickBytes##ptrname.Ptr(), __l##ptrname, NULL, NULL); \
+ if (__cBytes##ptrname == 0 && __l##ptrname != 0) { \
+ MAKE_TRANSLATIONFAILED; \
+ } \
+ LPSTR ptrname = (LPSTR)__CQuickBytes##ptrname.Ptr()
+
+// Use for anything critical other than output to console, where weird
+// character mappings are unacceptable.
+#define MAKE_ANSIPTR_FROMWIDE(ptrname, widestr) MAKE_MULTIBYTE_FROMWIDE(ptrname, widestr, CP_ACP)
+
+// Use for output to the console.
+#define MAKE_ANSIPTR_FROMWIDE_BESTFIT(ptrname, widestr) MAKE_MULTIBYTE_FROMWIDE_BESTFIT(ptrname, widestr, CP_ACP)
+
+#define MAKE_WIDEPTR_FROMANSI(ptrname, ansistr) \
+ CQuickBytes __qb##ptrname; \
+ int __l##ptrname; \
+ __l##ptrname = WszMultiByteToWideChar(CP_ACP, 0, ansistr, -1, 0, 0); \
+ if (__l##ptrname > MAKE_MAX_LENGTH) \
+ MAKE_TOOLONGACTION; \
+ LPWSTR ptrname = (LPWSTR) __qb##ptrname.AllocThrows((__l##ptrname+1)*sizeof(WCHAR)); \
+ if (WszMultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, ansistr, -1, ptrname, __l##ptrname) == 0) { \
+ MAKE_TRANSLATIONFAILED; \
+ }
+
+#define MAKE_WIDEPTR_FROMANSI_NOTHROW(ptrname, ansistr) \
+ CQuickBytes __qb##ptrname; \
+ LPWSTR ptrname = 0; \
+ int __l##ptrname; \
+ __l##ptrname = WszMultiByteToWideChar(CP_ACP, 0, ansistr, -1, 0, 0); \
+ if (__l##ptrname <= MAKE_MAX_LENGTH) { \
+ ptrname = (LPWSTR) __qb##ptrname.AllocNoThrow((__l##ptrname+1)*sizeof(WCHAR)); \
+ if (ptrname) { \
+ if (WszMultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, ansistr, -1, ptrname, __l##ptrname) != 0) { \
+ ptrname[__l##ptrname] = 0; \
+ } else { \
+ ptrname = 0; \
+ } \
+ } \
+ }
+
+#define MAKE_UTF8PTR_FROMWIDE(ptrname, widestr) CQuickBytes _##ptrname; _##ptrname.ConvertUnicode_Utf8(widestr); LPSTR ptrname = (LPSTR) _##ptrname.Ptr();
+
+#define MAKE_UTF8PTR_FROMWIDE_NOTHROW(ptrname, widestr) \
+ CQuickBytes __qb##ptrname; \
+ int __l##ptrname = (int)wcslen(widestr); \
+ LPUTF8 ptrname = 0; \
+ if (__l##ptrname <= MAKE_MAX_LENGTH) { \
+ __l##ptrname = (int)((__l##ptrname + 1) * 2 * sizeof(char)); \
+ ptrname = (LPUTF8) __qb##ptrname.AllocNoThrow(__l##ptrname); \
+ } \
+ if (ptrname) { \
+ INT32 __lresult##ptrname=WszWideCharToMultiByte(CP_UTF8, 0, widestr, -1, ptrname, __l##ptrname-1, NULL, NULL); \
+ DWORD __dwCaptureLastError##ptrname = ::GetLastError(); \
+ if ((__lresult##ptrname==0) && (((LPCWSTR)widestr)[0] != W('\0'))) { \
+ if (__dwCaptureLastError##ptrname==ERROR_INSUFFICIENT_BUFFER) { \
+ INT32 __lsize##ptrname=WszWideCharToMultiByte(CP_UTF8, 0, widestr, -1, NULL, 0, NULL, NULL); \
+ ptrname = (LPSTR) __qb##ptrname .AllocNoThrow(__lsize##ptrname); \
+ if (ptrname) { \
+ if (WszWideCharToMultiByte(CP_UTF8, 0, widestr, -1, ptrname, __lsize##ptrname, NULL, NULL) != 0) { \
+ ptrname[__l##ptrname] = 0; \
+ } else { \
+ ptrname = 0; \
+ } \
+ } \
+ } \
+ else { \
+ ptrname = 0; \
+ } \
+ } \
+ } \
+
+#define MAKE_WIDEPTR_FROMUTF8N(ptrname, utf8str, n8chrs) \
+ CQuickBytes __qb##ptrname; \
+ int __l##ptrname; \
+ __l##ptrname = WszMultiByteToWideChar(CP_UTF8, 0, utf8str, n8chrs, 0, 0); \
+ if (__l##ptrname > MAKE_MAX_LENGTH) \
+ MAKE_TOOLONGACTION; \
+ LPWSTR ptrname = (LPWSTR) __qb##ptrname .AllocThrows((__l##ptrname+1)*sizeof(WCHAR)); \
+ if (0==WszMultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8str, n8chrs, ptrname, __l##ptrname)) { \
+ MAKE_TRANSLATIONFAILED; \
+ } \
+ ptrname[__l##ptrname] = 0;
+
+
+#define MAKE_WIDEPTR_FROMUTF8(ptrname, utf8str) CQuickBytes _##ptrname; _##ptrname.ConvertUtf8_Unicode(utf8str); LPCWSTR ptrname = (LPCWSTR) _##ptrname.Ptr();
+
+
+#define MAKE_WIDEPTR_FROMUTF8N_NOTHROW(ptrname, utf8str, n8chrs) \
+ CQuickBytes __qb##ptrname; \
+ int __l##ptrname; \
+ LPWSTR ptrname = 0; \
+ __l##ptrname = WszMultiByteToWideChar(CP_UTF8, 0, utf8str, n8chrs, 0, 0); \
+ if (__l##ptrname <= MAKE_MAX_LENGTH) { \
+ ptrname = (LPWSTR) __qb##ptrname.AllocNoThrow((__l##ptrname+1)*sizeof(WCHAR)); \
+ if (ptrname) { \
+ if (WszMultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8str, n8chrs, ptrname, __l##ptrname) != 0) { \
+ ptrname[__l##ptrname] = 0; \
+ } else { \
+ ptrname = 0; \
+ } \
+ } \
+ }
+
+#define MAKE_WIDEPTR_FROMUTF8_NOTHROW(ptrname, utf8str) MAKE_WIDEPTR_FROMUTF8N_NOTHROW(ptrname, utf8str, -1)
+
+// This method takes the number of characters
+#define MAKE_MULTIBYTE_FROMWIDEN(ptrname, widestr, _nCharacters, _pCnt, codepage) \
+ CQuickBytes __qb##ptrname; \
+ int __l##ptrname; \
+ __l##ptrname = WszWideCharToMultiByte(codepage, WC_NO_BEST_FIT_CHARS, widestr, _nCharacters, NULL, 0, NULL, NULL); \
+ if (__l##ptrname > MAKE_MAX_LENGTH) \
+ MAKE_TOOLONGACTION; \
+ ptrname = (LPUTF8) __qb##ptrname .AllocThrows(__l##ptrname+1); \
+ BOOL __b##ptrname; \
+ DWORD _pCnt = WszWideCharToMultiByte(codepage, WC_NO_BEST_FIT_CHARS, widestr, _nCharacters, ptrname, __l##ptrname, NULL, &__b##ptrname); \
+ if (__b##ptrname || (_pCnt == 0 && _nCharacters > 0)) { \
+ MAKE_TRANSLATIONFAILED; \
+ } \
+ ptrname[__l##ptrname] = 0;
+
+#define MAKE_MULTIBYTE_FROMWIDEN_BESTFIT(ptrname, widestr, _nCharacters, _pCnt, codepage) \
+ CQuickBytes __qb##ptrname; \
+ int __l##ptrname; \
+ __l##ptrname = WszWideCharToMultiByte(codepage, 0, widestr, _nCharacters, NULL, 0, NULL, NULL); \
+ if (__l##ptrname > MAKE_MAX_LENGTH) \
+ MAKE_TOOLONGACTION; \
+ ptrname = (LPUTF8) __qb##ptrname .AllocThrows(__l##ptrname+1); \
+ DWORD _pCnt = WszWideCharToMultiByte(codepage, 0, widestr, _nCharacters, ptrname, __l##ptrname, NULL, NULL); \
+ if (_pCnt == 0 && _nCharacters > 0) { \
+ MAKE_TRANSLATIONFAILED; \
+ } \
+ ptrname[__l##ptrname] = 0;
+
+#define MAKE_ANSIPTR_FROMWIDEN(ptrname, widestr, _nCharacters, _pCnt) \
+ MAKE_MULTIBYTE_FROMWIDEN(ptrname, widestr, _nCharacters, _pCnt, CP_ACP)
+
+
+inline
+LPWSTR DuplicateString(
+ LPCWSTR wszString,
+ size_t cchString)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ LPWSTR wszDup = NULL;
+ if (wszString != NULL)
+ {
+ wszDup = new (nothrow) WCHAR[cchString + 1];
+ if (wszDup != NULL)
+ {
+ wcscpy_s(wszDup, cchString + 1, wszString);
+ }
+ }
+ return wszDup;
+}
+
+inline
+LPWSTR DuplicateString(
+ LPCWSTR wszString)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ if (wszString != NULL)
+ {
+ return DuplicateString(wszString, wcslen(wszString));
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void DECLSPEC_NORETURN ThrowOutOfMemory();
+
+inline
+LPWSTR DuplicateStringThrowing(
+ LPCWSTR wszString,
+ size_t cchString)
+{
+ STATIC_CONTRACT_THROWS;
+
+ if (wszString == NULL)
+ return NULL;
+
+ LPWSTR wszDup = DuplicateString(wszString, cchString);
+ if (wszDup == NULL)
+ ThrowOutOfMemory();
+
+ return wszDup;
+}
+
+inline
+LPWSTR DuplicateStringThrowing(
+ LPCWSTR wszString)
+{
+ STATIC_CONTRACT_THROWS;
+
+ if (wszString == NULL)
+ return NULL;
+
+ LPWSTR wszDup = DuplicateString(wszString);
+ if (wszDup == NULL)
+ ThrowOutOfMemory();
+
+ return wszDup;
+}
+
+
+//*****************************************************************************
+// Placement new is used to new and object at an exact location. The pointer
+// is simply returned to the caller without actually using the heap. The
+// advantage here is that you cause the ctor() code for the object to be run.
+// This is ideal for heaps of C++ objects that need to get init'd multiple times.
+// Example:
+// void *pMem = GetMemFromSomePlace();
+// Foo *p = new (pMem) Foo;
+// DoSomething(p);
+// p->~Foo();
+//*****************************************************************************
+#ifndef __PLACEMENT_NEW_INLINE
+#define __PLACEMENT_NEW_INLINE
+inline void *__cdecl operator new(size_t, void *_P)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (_P);
+}
+#endif // __PLACEMENT_NEW_INLINE
+
+
+/********************************************************************************/
+/* portability helpers */
+
+#ifdef TARGET_64BIT
+#define IN_TARGET_64BIT(x) x
+#define IN_TARGET_32BIT(x)
+#else
+#define IN_TARGET_64BIT(x)
+#define IN_TARGET_32BIT(x) x
+#endif
+
+void * __cdecl
+operator new(size_t n);
+
+_Ret_bytecap_(n) void * __cdecl
+operator new[](size_t n);
+
+void __cdecl
+operator delete(void *p) NOEXCEPT;
+
+void __cdecl
+operator delete[](void *p) NOEXCEPT;
+
+#ifdef _DEBUG_IMPL
+HRESULT _OutOfMemory(LPCSTR szFile, int iLine);
+#define OutOfMemory() _OutOfMemory(__FILE__, __LINE__)
+#else
+inline HRESULT OutOfMemory()
+{
+ LIMITED_METHOD_CONTRACT;
+ return (E_OUTOFMEMORY);
+}
+#endif
+
+//*****************************************************************************
+// Handle accessing localizable resource strings
+//*****************************************************************************
+typedef LPCWSTR LocaleID;
+typedef WCHAR LocaleIDValue[LOCALE_NAME_MAX_LENGTH];
+
+// Notes about the culture callbacks:
+// - The language we're operating in can change at *runtime*!
+// - A process may operate in *multiple* languages.
+// (ex: Each thread may have it's own language)
+// - If we don't care what language we're in (or have no way of knowing),
+// then return a 0-length name and UICULTUREID_DONTCARE for the culture ID.
+// - GetCultureName() and the GetCultureId() must be in sync (refer to the
+// same language).
+// - We have two functions separate functions for better performance.
+// - The name is used to resolve a directory for MsCorRC.dll.
+// - The id is used as a key to map to a dll hinstance.
+
+// Callback to obtain both the culture name and the culture's parent culture name
+typedef HRESULT (*FPGETTHREADUICULTURENAMES)(__inout StringArrayList* pCultureNames);
+const LPCWSTR UICULTUREID_DONTCARE = NULL;
+
+typedef int (*FPGETTHREADUICULTUREID)(LocaleIDValue*);
+
+HMODULE CLRLoadLibrary(LPCWSTR lpLibFileName);
+
+HMODULE CLRLoadLibraryEx(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
+
+BOOL CLRFreeLibrary(HMODULE hModule);
+
+// Load a string using the resources for the current module.
+STDAPI UtilLoadStringRC(UINT iResouceID, _Out_writes_ (iMax) LPWSTR szBuffer, int iMax, int bQuiet=FALSE);
+
+// Specify callbacks so that UtilLoadStringRC can find out which language we're in.
+// If no callbacks specified (or both parameters are NULL), we default to the
+// resource dll in the root (which is probably english).
+void SetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID fpGetThreadUICultureId
+);
+
+void GetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID* fpGetThreadUICultureId
+);
+
+//*****************************************************************************
+// Use this class by privately deriving from noncopyable to disallow copying of
+// your class.
+//*****************************************************************************
+class noncopyable
+{
+protected:
+ noncopyable()
+ {}
+ ~noncopyable()
+ {}
+
+private:
+ noncopyable(const noncopyable&);
+ const noncopyable& operator=(const noncopyable&);
+};
+
+//*****************************************************************************
+// Must associate each handle to an instance of a resource dll with the int
+// that it represents
+//*****************************************************************************
+typedef HINSTANCE HRESOURCEDLL;
+
+
+class CCulturedHInstance
+{
+ LocaleIDValue m_LangId;
+ HRESOURCEDLL m_hInst;
+ BOOL m_fMissing;
+
+public:
+ CCulturedHInstance()
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_hInst = NULL;
+ m_fMissing = FALSE;
+ }
+
+ BOOL HasID(LocaleID id)
+ {
+ _ASSERTE(m_hInst != NULL || m_fMissing);
+ if (id == UICULTUREID_DONTCARE)
+ return FALSE;
+
+ return wcscmp(id, m_LangId) == 0;
+ }
+
+ HRESOURCEDLL GetLibraryHandle()
+ {
+ return m_hInst;
+ }
+
+ BOOL IsSet()
+ {
+ return m_hInst != NULL;
+ }
+
+ BOOL IsMissing()
+ {
+ return m_fMissing;
+ }
+
+ void SetMissing(LocaleID id)
+ {
+ _ASSERTE(m_hInst == NULL);
+ SetId(id);
+ m_fMissing = TRUE;
+ }
+
+ void Set(LocaleID id, HRESOURCEDLL hInst)
+ {
+ _ASSERTE(m_hInst == NULL);
+ _ASSERTE(m_fMissing == FALSE);
+ SetId(id);
+ m_hInst = hInst;
+ }
+ private:
+ void SetId(LocaleID id)
+ {
+ if (id != UICULTUREID_DONTCARE)
+ {
+ wcsncpy_s(m_LangId, ARRAY_SIZE(m_LangId), id, ARRAY_SIZE(m_LangId));
+ m_LangId[STRING_LENGTH(m_LangId)] = W('\0');
+ }
+ else
+ {
+ m_LangId[0] = W('\0');
+ }
+ }
+ };
+
+#ifndef DACCESS_COMPILE
+void AddThreadPreferredUILanguages(StringArrayList* pArray);
+#endif
+//*****************************************************************************
+// CCompRC manages string Resource access for COM+. This includes loading
+// the MsCorRC.dll for resources as well allowing each thread to use a
+// a different localized version.
+//*****************************************************************************
+class CCompRC
+{
+public:
+
+ enum ResourceCategory
+ {
+ // must be present
+ Required,
+
+ // present in Desktop CLR and Core CLR + debug pack, an error
+ // If missing, get a generic error message instead
+ Error,
+
+ // present in Desktop CLR and Core CLR + debug pack, normal operation (e.g tracing)
+ // if missing, get a generic "resource not found" message instead
+ Debugging,
+
+ // present in Desktop CLR, optional for CoreCLR
+ DesktopCLR,
+
+ // might not be present, non essential
+ Optional
+ };
+
+ CCompRC()
+ {
+ // This constructor will be fired up on startup. Make sure it doesn't
+ // do anything besides zero-out out values.
+ m_fpGetThreadUICultureId = NULL;
+ m_fpGetThreadUICultureNames = NULL;
+
+ m_pHash = NULL;
+ m_nHashSize = 0;
+ m_csMap = NULL;
+ m_pResourceFile = NULL;
+ }// CCompRC
+
+ HRESULT Init(LPCWSTR pResourceFile);
+ void Destroy();
+
+ HRESULT LoadString(ResourceCategory eCategory, UINT iResourceID, _Out_writes_ (iMax) LPWSTR szBuffer, int iMax , int *pcwchUsed=NULL);
+ HRESULT LoadString(ResourceCategory eCategory, LocaleID langId, UINT iResourceID, _Out_writes_ (iMax) LPWSTR szBuffer, int iMax, int *pcwchUsed);
+
+ void SetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID fpGetThreadUICultureId
+ );
+
+ void GetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID* fpGetThreadUICultureId
+ );
+
+ // Get the default resource location (mscorrc.dll)
+ static CCompRC* GetDefaultResourceDll();
+
+ static void GetDefaultCallbacks(
+ FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID* fpGetThreadUICultureId)
+ {
+ WRAPPER_NO_CONTRACT;
+ m_DefaultResourceDll.GetResourceCultureCallbacks(
+ fpGetThreadUICultureNames,
+ fpGetThreadUICultureId);
+ }
+
+ static void SetDefaultCallbacks(
+ FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID fpGetThreadUICultureId)
+ {
+ WRAPPER_NO_CONTRACT;
+ // Either both are NULL or neither are NULL
+ _ASSERTE((fpGetThreadUICultureNames != NULL) ==
+ (fpGetThreadUICultureId != NULL));
+
+ m_DefaultResourceDll.SetResourceCultureCallbacks(
+ fpGetThreadUICultureNames,
+ fpGetThreadUICultureId);
+ }
+
+private:
+// String resouces packaged as PE files only exist on Windows
+#ifdef HOST_WINDOWS
+ HRESULT GetLibrary(LocaleID langId, HRESOURCEDLL* phInst);
+#ifndef DACCESS_COMPILE
+ HRESULT LoadLibraryHelper(HRESOURCEDLL *pHInst,
+ SString& rcPath);
+ HRESULT LoadLibraryThrows(HRESOURCEDLL * pHInst);
+ HRESULT LoadLibrary(HRESOURCEDLL * pHInst);
+ HRESULT LoadResourceFile(HRESOURCEDLL * pHInst, LPCWSTR lpFileName);
+#endif // DACCESS_COMPILE
+#endif // HOST_WINDOWS
+
+ // We do not have global constructors any more
+ static LONG m_dwDefaultInitialized;
+ static CCompRC m_DefaultResourceDll;
+ static LPCWSTR m_pDefaultResource;
+
+ // We must map between a thread's int and a dll instance.
+ // Since we only expect 1 language almost all of the time, we'll special case
+ // that and then use a variable size map for everything else.
+ CCulturedHInstance m_Primary;
+ CCulturedHInstance * m_pHash;
+ int m_nHashSize;
+
+ CRITSEC_COOKIE m_csMap;
+
+ LPCWSTR m_pResourceFile;
+
+ // Main accessors for hash
+ HRESOURCEDLL LookupNode(LocaleID langId, BOOL &fMissing);
+ HRESULT AddMapNode(LocaleID langId, HRESOURCEDLL hInst, BOOL fMissing = FALSE);
+
+ FPGETTHREADUICULTUREID m_fpGetThreadUICultureId;
+ FPGETTHREADUICULTURENAMES m_fpGetThreadUICultureNames;
+};
+
+HRESULT UtilLoadResourceString(CCompRC::ResourceCategory eCategory, UINT iResouceID, _Out_writes_ (iMax) LPWSTR szBuffer, int iMax);
+
+
+int UtilMessageBox(
+ HWND hWnd, // Handle to Owner Window
+ UINT uText, // Resource Identifier for Text message
+ UINT uCaption, // Resource Identifier for Caption
+ 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
+
+int UtilMessageBoxNonLocalized(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR lpText, // Resource Identifier for Text message
+ LPCWSTR lpTitle, // Resource Identifier for Caption
+ 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
+
+int UtilMessageBoxVA(
+ HWND hWnd, // Handle to Owner Window
+ UINT uText, // Resource Identifier for Text message
+ UINT uCaption, // Resource Identifier for Caption
+ 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
+
+int UtilMessageBoxNonLocalizedVA(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR lpText, // Text message
+ LPCWSTR lpCaption, // Caption
+ 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
+
+int UtilMessageBoxNonLocalizedVA(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR lpText, // Text message
+ LPCWSTR lpCaption, // Caption
+ LPCWSTR lpDetails, // Details that may be shown in a collapsed extended area of the dialog (Vista or higher).
+ 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
+
+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
+ ...);
+
+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
+ ...);
+
+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
+
+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
+
+
+// The HRESULT_FROM_WIN32 macro evaluates its arguments three times.
+// <TODO>TODO: All HRESULT_FROM_WIN32(GetLastError()) should be replaced by calls to
+// this helper function avoid code bloat</TODO>
+inline HRESULT HRESULT_FROM_GetLastError()
+{
+ WRAPPER_NO_CONTRACT;
+ DWORD dw = GetLastError();
+ // Make sure we return a failure
+ if (dw == ERROR_SUCCESS)
+ {
+ _ASSERTE(!"We were expecting to get an error code, but a success code is being returned. Check this code path for Everett!");
+ return E_FAIL;
+ }
+ else
+ return HRESULT_FROM_WIN32(dw);
+}
+
+inline HRESULT HRESULT_FROM_GetLastErrorNA()
+{
+ WRAPPER_NO_CONTRACT;
+ DWORD dw = GetLastError();
+ // Make sure we return a failure
+ if (dw == ERROR_SUCCESS)
+ return E_FAIL;
+ else
+ return HRESULT_FROM_WIN32(dw);
+}
+
+inline HRESULT BadError(HRESULT hr)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(!"Serious Error");
+ return (hr);
+}
+
+#define TESTANDRETURN(test, hrVal) \
+{ \
+ int ___test = (int)(test); \
+ if (! ___test) \
+ return hrVal; \
+}
+
+#define TESTANDRETURNPOINTER(pointer) \
+ TESTANDRETURN((pointer)!=NULL, E_POINTER)
+
+#define TESTANDRETURNMEMORY(pointer) \
+ TESTANDRETURN((pointer)!=NULL, E_OUTOFMEMORY)
+
+#define TESTANDRETURNHR(hr) \
+ TESTANDRETURN(SUCCEEDED(hr), hr)
+
+#define TESTANDRETURNARG(argtest) \
+ TESTANDRETURN(argtest, E_INVALIDARG)
+
+// Quick validity check for HANDLEs that are returned by Win32 APIs that
+// use INVALID_HANDLE_VALUE instead of NULL to indicate an error
+inline BOOL IsValidHandle(HANDLE h)
+{
+ LIMITED_METHOD_CONTRACT;
+ return ((h != NULL) && (h != INVALID_HANDLE_VALUE));
+}
+
+// Count the bits in a value in order iBits time.
+inline int CountBits(int iNum)
+{
+ LIMITED_METHOD_CONTRACT;
+ int iBits;
+ for (iBits=0; iNum; iBits++)
+ iNum = iNum & (iNum - 1);
+ return (iBits);
+}
+
+//*****************************************************************************
+//
+// Paths functions. Use these instead of the CRT.
+//
+//*****************************************************************************
+// secure version! Specify the size of the each buffer in count of elements
+void SplitPath(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);
+
+//*******************************************************************************
+// 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);
+
+
+void MakePath(_Out_ CQuickWSTR &path,
+ _In_ LPCWSTR drive,
+ _In_ LPCWSTR dir,
+ _In_ LPCWSTR fname,
+ _In_ LPCWSTR ext);
+
+WCHAR * FullPath(_Out_writes_ (maxlen) WCHAR *UserBuf, const WCHAR *path, size_t maxlen);
+
+//*****************************************************************************
+//
+// SString version of the path functions.
+//
+//*****************************************************************************
+void SplitPath(_In_ SString const &path,
+ __inout_opt SString *drive,
+ __inout_opt SString *dir,
+ __inout_opt SString *fname,
+ __inout_opt SString *ext);
+
+#include "ostype.h"
+
+#define CLRGetTickCount64() GetTickCount64()
+
+//
+// Allocate free memory within the range [pMinAddr..pMaxAddr] using
+// ClrVirtualQuery to find free memory and ClrVirtualAlloc to allocate it.
+//
+BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr,
+ const BYTE *pMaxAddr,
+ SIZE_T dwSize,
+ DWORD flAllocationType,
+ DWORD flProtect);
+
+//
+// Allocate free memory with specific alignment
+//
+LPVOID ClrVirtualAllocAligned(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect, SIZE_T alignment);
+
+class NumaNodeInfo
+{
+private:
+ static BOOL m_enableGCNumaAware;
+ static uint16_t m_nNodes;
+ static BOOL InitNumaNodeInfoAPI();
+
+public:
+ static BOOL CanEnableGCNumaAware();
+ static void InitNumaNodeInfo();
+
+#if !defined(FEATURE_REDHAWK)
+public: // functions
+
+ static LPVOID VirtualAllocExNuma(HANDLE hProc, LPVOID lpAddr, SIZE_T size,
+ DWORD allocType, DWORD prot, DWORD node);
+#ifdef HOST_WINDOWS
+ static BOOL GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, PUSHORT node_no);
+ static bool GetNumaInfo(PUSHORT total_nodes, DWORD* max_procs_per_node);
+#else // HOST_WINDOWS
+ static BOOL GetNumaProcessorNodeEx(USHORT proc_no, PUSHORT node_no);
+#endif // HOST_WINDOWS
+#endif
+};
+
+#ifdef HOST_WINDOWS
+
+struct CPU_Group_Info
+{
+ WORD nr_active; // at most 64
+ WORD reserved[1];
+ WORD begin;
+ WORD end;
+ DWORD_PTR active_mask;
+ DWORD groupWeight;
+ DWORD activeThreadWeight;
+};
+
+class CPUGroupInfo
+{
+private:
+ static LONG m_initialization;
+ static WORD m_nGroups;
+ static WORD m_nProcessors;
+ static BOOL m_enableGCCPUGroups;
+ static BOOL m_threadUseAllCpuGroups;
+ static BOOL m_threadAssignCpuGroups;
+ static WORD m_initialGroup;
+ static CPU_Group_Info *m_CPUGroupInfoArray;
+
+ static BOOL InitCPUGroupInfoArray();
+ static BOOL InitCPUGroupInfoRange();
+ static void InitCPUGroupInfo();
+ static BOOL IsInitialized();
+
+public:
+ static void EnsureInitialized();
+ static BOOL CanEnableGCCPUGroups();
+ static BOOL CanEnableThreadUseAllCpuGroups();
+ static BOOL CanAssignCpuGroupsToThreads();
+ static WORD GetNumActiveProcessors();
+ static void GetGroupForProcessor(WORD processor_number,
+ WORD *group_number, WORD *group_processor_number);
+ static DWORD CalculateCurrentProcessorNumber();
+ static bool GetCPUGroupInfo(PUSHORT total_groups, DWORD* max_procs_per_group);
+ //static void PopulateCPUUsageArray(void * infoBuffer, ULONG infoSize);
+
+#if !defined(FEATURE_REDHAWK)
+public:
+ static BOOL GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP relationship,
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *slpiex, PDWORD count);
+ static BOOL SetThreadGroupAffinity(HANDLE h,
+ const GROUP_AFFINITY *groupAffinity, GROUP_AFFINITY *previousGroupAffinity);
+ static BOOL GetThreadGroupAffinity(HANDLE h, GROUP_AFFINITY *groupAffinity);
+ static BOOL GetSystemTimes(FILETIME *idleTime, FILETIME *kernelTime, FILETIME *userTime);
+ static void ChooseCPUGroupAffinity(GROUP_AFFINITY *gf);
+ static void ClearCPUGroupAffinity(GROUP_AFFINITY *gf);
+ static BOOL GetCPUGroupRange(WORD group_number, WORD* group_begin, WORD* group_size);
+#endif
+};
+
+DWORD_PTR GetCurrentProcessCpuMask();
+
+#endif // HOST_WINDOWS
+
+int GetTotalProcessorCount();
+
+//******************************************************************************
+// Returns the number of processors that a process has been configured to run on
+//******************************************************************************
+int GetCurrentProcessCpuCount();
+
+uint32_t GetOsPageSize();
+
+
+//*****************************************************************************
+// Return != 0 if the bit at the specified index in the array is on and 0 if
+// it is off.
+//*****************************************************************************
+inline int GetBit(PTR_BYTE pcBits,int iBit)
+{
+ LIMITED_METHOD_CONTRACT;
+ return (pcBits[iBit>>3] & (1 << (iBit & 0x7)));
+}
+
+#ifdef DACCESS_COMPILE
+inline int GetBit(BYTE const * pcBits,int iBit)
+{
+ WRAPPER_NO_CONTRACT;
+ return GetBit(dac_cast<PTR_BYTE>(pcBits), iBit);
+}
+#endif
+
+//*****************************************************************************
+// Set the state of the bit at the specified index based on the value of bOn.
+//*****************************************************************************
+inline void SetBit(PTR_BYTE pcBits,int iBit,int bOn)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (bOn)
+ pcBits[iBit>>3] |= (1 << (iBit & 0x7));
+ else
+ pcBits[iBit>>3] &= ~(1 << (iBit & 0x7));
+}
+
+#ifdef DACCESS_COMPILE
+inline void SetBit(BYTE * pcBits,int iBit,int bOn)
+{
+ WRAPPER_NO_CONTRACT;
+ SetBit(dac_cast<PTR_BYTE>(pcBits), iBit, bOn);
+}
+#endif
+
+template<typename T>
+class SimpleListNode
+{
+public:
+ SimpleListNode<T>(const T& _t)
+ {
+ data = _t;
+ next = 0;
+ }
+
+ T data;
+ SimpleListNode<T>* next;
+};
+
+template<typename T>
+class SimpleList
+{
+public:
+ typedef SimpleListNode<T> NodeType;
+
+ SimpleList<T>()
+ {
+ head = NULL;
+ }
+
+ void LinkHead(NodeType* pNode)
+ {
+ pNode->next = head;
+ head = pNode;
+ }
+
+ NodeType* UnlinkHead()
+ {
+ NodeType* ret = head;
+
+ if (head)
+ {
+ head = head->next;
+ }
+ return ret;
+ }
+
+ NodeType* Head()
+ {
+ return head;
+ }
+
+protected:
+
+ NodeType* head;
+};
+
+
+template < typename T, typename U >
+struct Pair
+{
+public:
+ typedef Pair< T, U > this_type;
+ typedef T first_type;
+ typedef U second_type;
+
+ Pair()
+ {}
+
+ Pair( T const & t, U const & u )
+ : m_first( t )
+ , m_second( u )
+ { SUPPORTS_DAC; }
+
+ Pair( this_type const & obj )
+ : m_first( obj.m_first )
+ , m_second( obj.m_second )
+ {}
+
+ this_type & operator=( this_type const & obj )
+ {
+ m_first = obj.m_first;
+ m_second = obj.m_second;
+ return *this;
+ }
+
+ T & First()
+ {
+ return m_first;
+ }
+
+ T const & First() const
+ {
+ return m_first;
+ }
+
+ U & Second()
+ {
+ return m_second;
+ }
+
+ U const & Second() const
+ {
+ return m_second;
+ }
+
+ bool operator==(const Pair& rhs) const
+ {
+ return ((this->First() == rhs.First()) &&
+ (this->Second() == rhs.Second()));
+ }
+
+ bool operator!=(const Pair& rhs) const
+ {
+ return !(*this == rhs);
+ }
+
+private:
+ first_type m_first;
+ second_type m_second;
+};
+
+
+template < typename T, typename U >
+Pair< T, U > MakePair( T const & t, U const & u )
+{
+ SUPPORTS_DAC;
+ return Pair< T, U >( t, u );
+}
+
+
+//*****************************************************************************
+// This class implements a dynamic array of structures for which the order of
+// the elements is unimportant. This means that any item placed in the list
+// may be swapped to any other location in the list at any time. If the order
+// of the items you place in the array is important, then use the CStructArray
+// class.
+//*****************************************************************************
+
+template <class T,
+ int iGrowInc,
+ class ALLOCATOR>
+class CUnorderedArrayWithAllocator
+{
+ int m_iCount; // # of elements used in the list.
+ int m_iSize; // # of elements allocated in the list.
+public:
+#ifndef DACCESS_COMPILE
+ T *m_pTable; // Pointer to the list of elements.
+#else
+ TADDR m_pTable; // Pointer to the list of elements.
+#endif
+
+public:
+
+#ifndef DACCESS_COMPILE
+
+ CUnorderedArrayWithAllocator() :
+ m_iCount(0),
+ m_iSize(0),
+ m_pTable(NULL)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+ ~CUnorderedArrayWithAllocator()
+ {
+ LIMITED_METHOD_CONTRACT;
+ // Free the chunk of memory.
+ if (m_pTable != NULL)
+ ALLOCATOR::Free(this, m_pTable);
+ }
+
+ void Clear()
+ {
+ WRAPPER_NO_CONTRACT;
+ m_iCount = 0;
+ if (m_iSize > iGrowInc)
+ {
+ T* tmp = ALLOCATOR::AllocNoThrow(this, iGrowInc);
+ if (tmp) {
+ ALLOCATOR::Free(this, m_pTable);
+ m_pTable = tmp;
+ m_iSize = iGrowInc;
+ }
+ }
+ }
+
+ void Clear(int iFirst, int iCount)
+ {
+ WRAPPER_NO_CONTRACT;
+ int iSize;
+
+ if (iFirst + iCount < m_iCount)
+ memmove(&m_pTable[iFirst], &m_pTable[iFirst + iCount], sizeof(T) * (m_iCount - (iFirst + iCount)));
+
+ m_iCount -= iCount;
+
+ iSize = ((m_iCount / iGrowInc) * iGrowInc) + ((m_iCount % iGrowInc != 0) ? iGrowInc : 0);
+ if (m_iSize > iGrowInc && iSize < m_iSize)
+ {
+ T *tmp = ALLOCATOR::AllocNoThrow(this, iSize);
+ if (tmp) {
+ memcpy (tmp, m_pTable, iSize * sizeof(T));
+ delete [] m_pTable;
+ m_pTable = tmp;
+ m_iSize = iSize;
+ }
+ }
+ _ASSERTE(m_iCount <= m_iSize);
+ }
+
+ T *Table()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_pTable);
+ }
+
+ T *Append()
+ {
+ CONTRACTL {
+ NOTHROW;
+ } CONTRACTL_END;
+
+ // The array should grow, if we can't fit one more element into the array.
+ if (m_iSize <= m_iCount && GrowNoThrow() == NULL)
+ return (NULL);
+ return (&m_pTable[m_iCount++]);
+ }
+
+ T *AppendThrowing()
+ {
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ // The array should grow, if we can't fit one more element into the array.
+ if (m_iSize <= m_iCount)
+ Grow();
+ return (&m_pTable[m_iCount++]);
+ }
+
+ void Delete(const T &Entry)
+ {
+ LIMITED_METHOD_CONTRACT;
+ --m_iCount;
+ for (int i=0; i <= m_iCount; ++i)
+ if (m_pTable[i] == Entry)
+ {
+ m_pTable[i] = m_pTable[m_iCount];
+ return;
+ }
+
+ // Just in case we didn't find it.
+ ++m_iCount;
+ }
+
+ void DeleteByIndex(int i)
+ {
+ LIMITED_METHOD_CONTRACT;
+ --m_iCount;
+ m_pTable[i] = m_pTable[m_iCount];
+ }
+
+ void Swap(int i,int j)
+ {
+ LIMITED_METHOD_CONTRACT;
+ T tmp;
+
+ if (i == j)
+ return;
+ tmp = m_pTable[i];
+ m_pTable[i] = m_pTable[j];
+ m_pTable[j] = tmp;
+ }
+
+#else
+
+ TADDR Table()
+ {
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+ return (m_pTable);
+ }
+
+ void EnumMemoryRegions(void)
+ {
+ SUPPORTS_DAC;
+ DacEnumMemoryRegion(m_pTable, m_iCount * sizeof(T));
+ }
+
+#endif // #ifndef DACCESS_COMPILE
+
+ USHORT Count()
+ {
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+ _ASSERTE(FitsIn<USHORT>(m_iCount));
+ return static_cast<USHORT>(m_iCount);
+ }
+
+private:
+ T *Grow();
+ T *GrowNoThrow();
+};
+
+
+#ifndef DACCESS_COMPILE
+
+//*****************************************************************************
+// Increase the size of the array.
+//*****************************************************************************
+template <class T,
+ int iGrowInc,
+ class ALLOCATOR>
+T *CUnorderedArrayWithAllocator<T,iGrowInc,ALLOCATOR>::GrowNoThrow() // NULL if can't grow.
+{
+ WRAPPER_NO_CONTRACT;
+ T *pTemp;
+
+ // try to allocate memory for reallocation.
+ if ((pTemp = ALLOCATOR::AllocNoThrow(this, m_iSize+iGrowInc)) == NULL)
+ return (NULL);
+ memcpy (pTemp, m_pTable, m_iSize*sizeof(T));
+ ALLOCATOR::Free(this, m_pTable);
+ m_pTable = pTemp;
+ m_iSize += iGrowInc;
+ _ASSERTE(m_iSize > 0);
+ return (pTemp);
+}
+
+template <class T,
+ int iGrowInc,
+ class ALLOCATOR>
+T *CUnorderedArrayWithAllocator<T,iGrowInc,ALLOCATOR>::Grow() // exception if can't grow.
+{
+ WRAPPER_NO_CONTRACT;
+ T *pTemp;
+
+ // try to allocate memory for reallocation.
+ pTemp = ALLOCATOR::AllocThrowing(this, m_iSize+iGrowInc);
+ memcpy (pTemp, m_pTable, m_iSize*sizeof(T));
+ ALLOCATOR::Free(this, m_pTable);
+ m_pTable = pTemp;
+ m_iSize += iGrowInc;
+ _ASSERTE(m_iSize > 0);
+ return (pTemp);
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+
+template <class T>
+class CUnorderedArray__Allocator
+{
+public:
+
+ static T *AllocThrowing (void*, int nElements)
+ {
+ return new T[nElements];
+ }
+
+ static T *AllocNoThrow (void*, int nElements)
+ {
+ return new (nothrow) T[nElements];
+ }
+
+ static void Free (void*, T *pTable)
+ {
+ delete [] pTable;
+ }
+};
+
+
+template <class T,int iGrowInc>
+class CUnorderedArray : public CUnorderedArrayWithAllocator<T, iGrowInc, CUnorderedArray__Allocator<T> >
+{
+public:
+
+ CUnorderedArray ()
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+};
+
+
+//Used by the debugger. Included here in hopes somebody else might, too
+typedef CUnorderedArray<SIZE_T, 17> SIZE_T_UNORDERED_ARRAY;
+
+
+//*****************************************************************************
+// This class implements a dynamic array of structures for which the insert
+// order is important. Inserts will slide all elements after the location
+// down, deletes slide all values over the deleted item. If the order of the
+// items in the array is unimportant to you, then CUnorderedArray may provide
+// the same feature set at lower cost.
+//*****************************************************************************
+class CStructArray
+{
+ BYTE *m_pList; // Pointer to the list of elements.
+ int m_iCount; // # of elements used in the list.
+ int m_iSize; // # of elements allocated in the list.
+ int m_iGrowInc; // Growth increment.
+ short m_iElemSize; // Size of an array element.
+ bool m_bFree; // true if data is automatically maintained.
+
+public:
+ CStructArray(short iElemSize, short iGrowInc = 1) :
+ m_pList(NULL),
+ m_iCount(0),
+ m_iSize(0),
+ m_iGrowInc(iGrowInc),
+ m_iElemSize(iElemSize),
+ m_bFree(true)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+ ~CStructArray()
+ {
+ WRAPPER_NO_CONTRACT;
+ Clear();
+ }
+
+ void *Insert(int iIndex);
+ void *InsertThrowing(int iIndex);
+ void *Append();
+ void *AppendThrowing();
+ int AllocateBlock(int iCount);
+ void AllocateBlockThrowing(int iCount);
+ void Delete(int iIndex);
+ void *Ptr()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_pList);
+ }
+ void *Get(int iIndex)
+ {
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(iIndex < m_iCount);
+ return (BYTE*) Ptr() + (iIndex * (size_t)m_iElemSize);
+ }
+ size_t Size()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_iCount * (size_t)m_iElemSize);
+ }
+ int Count()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_iCount);
+ }
+ void Clear();
+ void ClearCount()
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_iCount = 0;
+ }
+
+ void InitOnMem(short iElemSize, void *pList, int iCount, int iSize, int iGrowInc=1)
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_iElemSize = iElemSize;
+ m_iGrowInc = (short) iGrowInc;
+ m_pList = (BYTE*)pList;
+ m_iCount = iCount;
+ m_iSize = iSize;
+ m_bFree = false;
+ }
+
+private:
+ void Grow(int iCount);
+};
+
+
+//*****************************************************************************
+// This template simplifies access to a CStructArray by removing void * and
+// adding some operator overloads.
+//*****************************************************************************
+template <class T>
+class CDynArray : public CStructArray
+{
+public:
+ CDynArray(short iGrowInc=16) :
+ CStructArray(sizeof(T), iGrowInc)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ T *Insert(int iIndex)
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T *)CStructArray::Insert((int)iIndex));
+ }
+
+ T *InsertThrowing(int iIndex)
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T *)CStructArray::InsertThrowing((int)iIndex));
+ }
+
+ T *Append()
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T *)CStructArray::Append());
+ }
+
+ T *AppendThrowing()
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T *)CStructArray::AppendThrowing());
+ }
+
+ T *Ptr()
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T *)CStructArray::Ptr());
+ }
+
+ T *Get(int iIndex)
+ {
+ WRAPPER_NO_CONTRACT;
+ return (Ptr() + iIndex);
+ }
+ T &operator[](int iIndex)
+ {
+ WRAPPER_NO_CONTRACT;
+ return (*(Ptr() + iIndex));
+ }
+ int ItemIndex(T *p)
+ {
+ WRAPPER_NO_CONTRACT;
+ return (((int)(LONG_PTR)p - (int)(LONG_PTR)Ptr()) / sizeof(T));
+ }
+ void Move(int iFrom, int iTo)
+ {
+ WRAPPER_NO_CONTRACT;
+ T tmp;
+
+ _ASSERTE(iFrom >= 0 && iFrom < Count() &&
+ iTo >= 0 && iTo < Count());
+
+ tmp = *(Ptr() + iFrom);
+ if (iTo > iFrom)
+ memmove(Ptr() + iFrom, Ptr() + iFrom + 1, (iTo - iFrom) * sizeof(T));
+ else
+ memmove(Ptr() + iTo + 1, Ptr() + iTo, (iFrom - iTo) * sizeof(T));
+ *(Ptr() + iTo) = tmp;
+ }
+};
+
+// Some common arrays.
+typedef CDynArray<int> INTARRAY;
+typedef CDynArray<short> SHORTARRAY;
+typedef CDynArray<int> LONGARRAY;
+typedef CDynArray<USHORT> USHORTARRAY;
+typedef CDynArray<ULONG> ULONGARRAY;
+typedef CDynArray<BYTE> BYTEARRAY;
+typedef CDynArray<mdToken> TOKENARRAY;
+
+template <class T> class CStackArray : public CStructArray
+{
+public:
+ CStackArray(short iGrowInc=4) :
+ CStructArray(sizeof(T), iGrowInc),
+ m_curPos(0)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ void Push(T p)
+ {
+ WRAPPER_NO_CONTRACT;
+ // We should only inc m_curPos after we grow the array.
+ T *pT = (T *)CStructArray::InsertThrowing(m_curPos);
+ m_curPos ++;
+ *pT = p;
+ }
+
+ T * Pop()
+ {
+ WRAPPER_NO_CONTRACT;
+ T * retPtr;
+
+ _ASSERTE(m_curPos > 0);
+
+ retPtr = (T *)CStructArray::Get(m_curPos-1);
+ CStructArray::Delete(m_curPos--);
+
+ return (retPtr);
+ }
+
+ int Count()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return(m_curPos);
+ }
+
+private:
+ int m_curPos;
+};
+
+
+//*****************************************************************************
+// This template manages a list of free entries by their 0 based offset. By
+// making it a template, you can use whatever size free chain will match your
+// maximum count of items. -1 is reserved.
+//*****************************************************************************
+template <class T> class TFreeList
+{
+public:
+ void Init(
+ T *rgList,
+ int iCount)
+ {
+ LIMITED_METHOD_CONTRACT;
+ // Save off values.
+ m_rgList = rgList;
+ m_iCount = iCount;
+ m_iNext = 0;
+
+ // Init free list.
+ int i;
+ for (i=0; i<iCount - 1; i++)
+ m_rgList[i] = i + 1;
+ m_rgList[i] = (T) -1;
+ }
+
+ T GetFreeEntry() // Index of free item, or -1.
+ {
+ LIMITED_METHOD_CONTRACT;
+ T iNext;
+
+ if (m_iNext == (T) -1)
+ return (-1);
+
+ iNext = m_iNext;
+ m_iNext = m_rgList[m_iNext];
+ return (iNext);
+ }
+
+ void DelFreeEntry(T iEntry)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(iEntry < m_iCount);
+ m_rgList[iEntry] = m_iNext;
+ m_iNext = iEntry;
+ }
+
+ // This function can only be used when it is guaranteed that the free
+ // array is contigous, for example, right after creation to quickly
+ // get a range of items from the heap.
+ void ReserveRange(int iCount)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(iCount < m_iCount);
+ _ASSERTE(m_iNext == 0);
+ m_iNext = iCount;
+ }
+
+private:
+ T *m_rgList; // List of free info.
+ int m_iCount; // How many entries to manage.
+ T m_iNext; // Next item to get.
+};
+
+
+//*****************************************************************************
+//*****************************************************************************
+template <class T> class CQuickSort
+{
+protected:
+ T *m_pBase; // Base of array to sort.
+private:
+ SSIZE_T m_iCount; // How many items in array.
+ SSIZE_T m_iElemSize; // Size of one element.
+public:
+ CQuickSort(
+ T *pBase, // Address of first element.
+ SSIZE_T iCount) : // How many there are.
+ m_pBase(pBase),
+ m_iCount(iCount),
+ m_iElemSize(sizeof(T))
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ }
+
+//*****************************************************************************
+// Call to sort the array.
+//*****************************************************************************
+ inline void Sort()
+ {
+ WRAPPER_NO_CONTRACT;
+ SortRange(0, m_iCount - 1);
+ }
+
+protected:
+//*****************************************************************************
+// Override this function to do the comparison.
+//*****************************************************************************
+ virtual FORCEINLINE int Compare( // -1, 0, or 1
+ T *psFirst, // First item to compare.
+ T *psSecond) // Second item to compare.
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (memcmp(psFirst, psSecond, sizeof(T)));
+// return (::Compare(*psFirst, *psSecond));
+ }
+
+ virtual FORCEINLINE void Swap(
+ SSIZE_T iFirst,
+ SSIZE_T iSecond)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (iFirst == iSecond) return;
+ T sTemp( m_pBase[iFirst] );
+ m_pBase[iFirst] = m_pBase[iSecond];
+ m_pBase[iSecond] = sTemp;
+ }
+
+private:
+ inline void SortRange(
+ SSIZE_T iLeft,
+ SSIZE_T iRight)
+ {
+ WRAPPER_NO_CONTRACT;
+ SSIZE_T iLast;
+ SSIZE_T i; // loop variable.
+
+ for (;;)
+ {
+ // if less than two elements you're done.
+ if (iLeft >= iRight)
+ return;
+
+ // ASSERT that we now have valid indicies. This is statically provable
+ // since this private function is only called with valid indicies,
+ // and iLeft and iRight only converge towards eachother. However,
+ // PreFast can't detect this because it doesn't know about our callers.
+ COMPILER_ASSUME(iLeft >= 0 && iLeft < m_iCount);
+ COMPILER_ASSUME(iRight >= 0 && iRight < m_iCount);
+
+ // The mid-element is the pivot, move it to the left.
+ Swap(iLeft, (iLeft + iRight) / 2);
+ iLast = iLeft;
+
+ // move everything that is smaller than the pivot to the left.
+ for (i = iLeft + 1; i <= iRight; i++)
+ {
+ if (Compare(&m_pBase[i], &m_pBase[iLeft]) < 0)
+ {
+ Swap(i, ++iLast);
+ }
+ }
+
+ // Put the pivot to the point where it is in between smaller and larger elements.
+ Swap(iLeft, iLast);
+
+ // Sort each partition.
+ SSIZE_T iLeftLast = iLast - 1;
+ SSIZE_T iRightFirst = iLast + 1;
+ if (iLeftLast - iLeft < iRight - iRightFirst)
+ { // Left partition is smaller, sort it recursively
+ SortRange(iLeft, iLeftLast);
+ // Tail call to sort the right (bigger) partition
+ iLeft = iRightFirst;
+ //iRight = iRight;
+ continue;
+ }
+ else
+ { // Right partition is smaller, sort it recursively
+ SortRange(iRightFirst, iRight);
+ // Tail call to sort the left (bigger) partition
+ //iLeft = iLeft;
+ iRight = iLeftLast;
+ continue;
+ }
+ }
+ }
+};
+
+//*****************************************************************************
+// Faster and simpler version of the binary search below.
+//*****************************************************************************
+template <class T>
+const T * BinarySearch(const T * pBase, int iCount, const T & find)
+{
+ WRAPPER_NO_CONTRACT;
+
+ int iFirst = 0;
+ int iLast = iCount - 1;
+
+ // It is faster to use linear search once we get down to a small number of elements.
+ while (iLast - iFirst > 10)
+ {
+ int iMid = (iLast + iFirst) / 2;
+
+ if (find < pBase[iMid])
+ iLast = iMid - 1;
+ else
+ iFirst = iMid;
+ }
+
+ for (int i = iFirst; i <= iLast; i++)
+ {
+ if (find == pBase[i])
+ return &pBase[i];
+
+ if (find < pBase[i])
+ break;
+ }
+
+ return NULL;
+}
+
+//*****************************************************************************
+// This template encapsulates a binary search algorithm on the given type
+// of data.
+//*****************************************************************************
+template <class T> class CBinarySearch
+{
+private:
+ const T *m_pBase; // Base of array to sort.
+ int m_iCount; // How many items in array.
+
+public:
+ CBinarySearch(
+ const T *pBase, // Address of first element.
+ int iCount) : // Value to find.
+ m_pBase(pBase),
+ m_iCount(iCount)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+//*****************************************************************************
+// Searches for the item passed to ctor.
+//*****************************************************************************
+ const T *Find( // Pointer to found item in array.
+ const T *psFind, // The key to find.
+ int *piInsert = NULL) // Index to insert at.
+ {
+ WRAPPER_NO_CONTRACT;
+ int iMid, iFirst, iLast; // Loop control.
+ int iCmp; // Comparison.
+
+ iFirst = 0;
+ iLast = m_iCount - 1;
+ while (iFirst <= iLast)
+ {
+ iMid = (iLast + iFirst) / 2;
+ iCmp = Compare(psFind, &m_pBase[iMid]);
+ if (iCmp == 0)
+ {
+ if (piInsert != NULL)
+ *piInsert = iMid;
+ return (&m_pBase[iMid]);
+ }
+ else if (iCmp < 0)
+ iLast = iMid - 1;
+ else
+ iFirst = iMid + 1;
+ }
+ if (piInsert != NULL)
+ *piInsert = iFirst;
+ return (NULL);
+ }
+
+//*****************************************************************************
+// Override this function to do the comparison if a comparison operator is
+// not valid for your data type (such as a struct).
+//*****************************************************************************
+ virtual int Compare( // -1, 0, or 1
+ const T *psFirst, // Key you are looking for.
+ const T *psSecond) // Item to compare to.
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (memcmp(psFirst, psSecond, sizeof(T)));
+// return (::Compare(*psFirst, *psSecond));
+ }
+};
+
+//*****************************************************************************
+// The information that the hash table implementation stores at the beginning
+// of every record that can be but in the hash table.
+//*****************************************************************************
+typedef DPTR(struct HASHENTRY) PTR_HASHENTRY;
+struct HASHENTRY
+{
+ ULONG iPrev; // Previous bucket in the chain.
+ ULONG iNext; // Next bucket in the chain.
+};
+
+typedef DPTR(struct FREEHASHENTRY) PTR_FREEHASHENTRY;
+struct FREEHASHENTRY : HASHENTRY
+{
+ ULONG iFree;
+};
+
+//*****************************************************************************
+// Used by the FindFirst/FindNextEntry functions. These api's allow you to
+// do a sequential scan of all entries.
+//*****************************************************************************
+struct HASHFIND
+{
+ ULONG iBucket; // The next bucket to look in.
+ ULONG iNext;
+};
+
+
+//*****************************************************************************
+// IMPORTANT: This data structure is deprecated, please do not add any new uses.
+// The hashtable implementation that should be used instead is code:SHash.
+// If code:SHash does not work for you, talk to mailto:clrdeag.
+//*****************************************************************************
+// This is a class that implements a chain and bucket hash table.
+//
+// The data is actually supplied as an array of structures by the user of this class.
+// This allows the buckets to use small indices to point to the chain, instead of pointers.
+//
+// Each entry in the array contains a HASHENTRY structure immediately
+// followed by the key used to hash the structure.
+//
+// The HASHENTRY part of every structure is used to implement the chain of
+// entries in a single bucket.
+//
+// This implementation does not support rehashing the buckets if the table grows
+// to big.
+// @TODO: Fix this by adding an abstract function Hash() which must be implemented
+// by all clients.
+//
+//*****************************************************************************
+class CHashTable
+{
+ friend class DebuggerRCThread; //RCthread actually needs access to
+ //fields of derrived class DebuggerPatchTable
+
+protected:
+ TADDR m_pcEntries; // Pointer to the array of structs.
+ ULONG m_iEntrySize; // Size of the structs.
+
+ ULONG m_iBuckets; // # of chains we are hashing into.
+ PTR_ULONG m_piBuckets; // Ptr to the array of bucket chains.
+
+ INDEBUG(unsigned m_maxSearch;) // For evaluating perf characteristics
+
+ HASHENTRY *EntryPtr(ULONG iEntry)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (PTR_HASHENTRY(m_pcEntries + (iEntry * (size_t)m_iEntrySize)));
+ }
+
+ ULONG ItemIndex(HASHENTRY *p)
+ {
+ SUPPORTS_DAC;
+ LIMITED_METHOD_CONTRACT;
+ return (ULONG)((dac_cast<TADDR>(p) - m_pcEntries) / m_iEntrySize);
+ }
+
+
+public:
+
+ CHashTable(
+ ULONG iBuckets) : // # of chains we are hashing into.
+ m_pcEntries((TADDR)NULL),
+ m_iBuckets(iBuckets)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ m_piBuckets = NULL;
+
+ INDEBUG(m_maxSearch = 0;)
+ }
+
+ CHashTable() : // # of chains we are hashing into.
+ m_pcEntries((TADDR)NULL),
+ m_iBuckets(5)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ m_piBuckets = NULL;
+
+ INDEBUG(m_maxSearch = 0;)
+ }
+
+#ifndef DACCESS_COMPILE
+
+ ~CHashTable()
+ {
+ LIMITED_METHOD_CONTRACT;
+ if (m_piBuckets != NULL)
+ {
+ delete [] m_piBuckets;
+ m_piBuckets = NULL;
+ }
+ }
+
+//*****************************************************************************
+// 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 NewInit( // Return status.
+ BYTE *pcEntries, // Array of structs we are managing.
+ ULONG iEntrySize); // Size of the entries.
+
+//*****************************************************************************
+// This can be called to change the pointer to the table that the hash table
+// is managing. You might call this if (for example) you realloc the size
+// of the table and its pointer is different.
+//*****************************************************************************
+ void SetTable(
+ BYTE *pcEntries) // Array of structs we are managing.
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_pcEntries = (TADDR)pcEntries;
+ }
+
+//*****************************************************************************
+// Clear the hash table as if there were nothing in it.
+//*****************************************************************************
+ void Clear()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(m_piBuckets != NULL);
+ memset(m_piBuckets, 0xff, m_iBuckets * sizeof(ULONG));
+ }
+
+//*****************************************************************************
+// Add the struct at the specified index in m_pcEntries to the hash chains.
+//*****************************************************************************
+ BYTE *Add( // New entry.
+ ULONG iHash, // Hash value of entry to add.
+ ULONG iIndex); // Index of struct in m_pcEntries.
+
+//*****************************************************************************
+// Delete the struct at the specified index in m_pcEntries from the hash chains.
+//*****************************************************************************
+ void Delete(
+ ULONG iHash, // Hash value of entry to delete.
+ ULONG iIndex); // Index of struct in m_pcEntries.
+
+ void Delete(
+ ULONG iHash, // Hash value of entry to delete.
+ HASHENTRY *psEntry); // The struct to delete.
+
+//*****************************************************************************
+// The item at the specified index has been moved, update the previous and
+// next item.
+//*****************************************************************************
+ void Move(
+ ULONG iHash, // Hash value for the item.
+ ULONG iNew); // New location.
+
+#endif // #ifndef DACCESS_COMPILE
+
+//*****************************************************************************
+// Return a boolean indicating whether or not this hash table has been inited.
+//*****************************************************************************
+ int IsInited()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_piBuckets != NULL);
+ }
+
+//*****************************************************************************
+// Search the hash table for an entry with the specified key value.
+//*****************************************************************************
+ BYTE *Find( // Index of struct in m_pcEntries.
+ ULONG iHash, // Hash value of the item.
+ SIZE_T key); // The key to match.
+
+//*****************************************************************************
+// Search the hash table for the next entry with the specified key value.
+//*****************************************************************************
+ ULONG FindNext( // Index of struct in m_pcEntries.
+ SIZE_T key, // The key to match.
+ ULONG iIndex); // Index of previous match.
+
+//*****************************************************************************
+// Returns the first entry in the first hash bucket and inits the search
+// struct. Use the FindNextEntry function to continue walking the list. The
+// return order is not gauranteed.
+//*****************************************************************************
+ BYTE *FindFirstEntry( // First entry found, or 0.
+ HASHFIND *psSrch) // Search object.
+ {
+ WRAPPER_NO_CONTRACT;
+ if (m_piBuckets == 0)
+ return (0);
+ psSrch->iBucket = 1;
+ psSrch->iNext = m_piBuckets[0];
+ return (FindNextEntry(psSrch));
+ }
+
+//*****************************************************************************
+// Returns the next entry in the list.
+//*****************************************************************************
+ BYTE *FindNextEntry( // The next entry, or0 for end of list.
+ HASHFIND *psSrch); // Search object.
+
+#ifdef DACCESS_COMPILE
+ void EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
+ ULONG numEntries);
+#endif
+
+protected:
+ virtual BOOL Cmp(SIZE_T key1, const HASHENTRY * pc2) = 0;
+};
+
+
+class CNewData
+{
+public:
+ static BYTE *Alloc(int iSize, int iMaxSize)
+ {
+ WRAPPER_NO_CONTRACT;
+ return (new BYTE[iSize]);
+ }
+ static void Free(BYTE *pPtr, int iSize)
+ {
+ LIMITED_METHOD_CONTRACT;
+ delete [] pPtr;
+ }
+ static BYTE *Grow(BYTE *&pPtr, int iCurSize)
+ {
+ WRAPPER_NO_CONTRACT;
+ BYTE *p;
+ S_SIZE_T newSize = S_SIZE_T(iCurSize) + S_SIZE_T(GrowSize(iCurSize));
+ //check for overflow
+ if(newSize.IsOverflow())
+ p = NULL;
+ else
+ p = new (nothrow) BYTE[newSize.Value()];
+ if (p == 0) return (0);
+ memcpy (p, pPtr, iCurSize);
+ delete [] pPtr;
+ pPtr = p;
+ return pPtr;
+ }
+ static void Clean(BYTE * pData, int iSize)
+ {
+ }
+ static int RoundSize(int iSize)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (iSize);
+ }
+ static int GrowSize(int iCurSize)
+ {
+ LIMITED_METHOD_CONTRACT;
+ int newSize = (3 * iCurSize) / 2;
+ return (newSize < 256) ? 256 : newSize;
+ }
+};
+
+class CNewDataNoThrow
+{
+public:
+ static BYTE *Alloc(int iSize, int iMaxSize)
+ {
+ WRAPPER_NO_CONTRACT;
+ return (new (nothrow) BYTE[iSize]);
+ }
+ static void Free(BYTE *pPtr, int iSize)
+ {
+ LIMITED_METHOD_CONTRACT;
+ delete [] pPtr;
+ }
+ static BYTE *Grow(BYTE *&pPtr, int iCurSize)
+ {
+ WRAPPER_NO_CONTRACT;
+ BYTE *p;
+ S_SIZE_T newSize = S_SIZE_T(iCurSize) + S_SIZE_T(GrowSize(iCurSize));
+ //check for overflow
+ if(newSize.IsOverflow())
+ p = NULL;
+ else
+ p = new (nothrow) BYTE[newSize.Value()];
+ if (p == 0) return (0);
+ memcpy (p, pPtr, iCurSize);
+ delete [] pPtr;
+ pPtr = p;
+ return pPtr;
+ }
+ static void Clean(BYTE * pData, int iSize)
+ {
+ }
+ static int RoundSize(int iSize)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (iSize);
+ }
+ static int GrowSize(int iCurSize)
+ {
+ LIMITED_METHOD_CONTRACT;
+ int newSize = (3 * iCurSize) / 2;
+ return (newSize < 256) ? 256 : newSize;
+ }
+};
+
+
+//*****************************************************************************
+// IMPORTANT: This data structure is deprecated, please do not add any new uses.
+// The hashtable implementation that should be used instead is code:SHash.
+// If code:SHash does not work for you, talk to mailto:clrdeag.
+//*****************************************************************************
+// CHashTable expects the data to be in a single array - this is provided by
+// CHashTableAndData.
+// The array is allocated using the MemMgr type. CNewData and
+// CNewDataNoThrow can be used for this.
+//*****************************************************************************
+template <class MemMgr>
+class CHashTableAndData : public CHashTable
+{
+public:
+ DAC_ALIGNAS(CHashTable)
+ ULONG m_iFree; // Index into m_pcEntries[] of next available slot
+ ULONG m_iEntries; // size of m_pcEntries[]
+
+public:
+
+ CHashTableAndData() :
+ CHashTable()
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ CHashTableAndData(
+ ULONG iBuckets) : // # of chains we are hashing into.
+ CHashTable(iBuckets)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+#ifndef DACCESS_COMPILE
+
+ ~CHashTableAndData()
+ {
+ WRAPPER_NO_CONTRACT;
+ if (m_pcEntries != NULL)
+ MemMgr::Free((BYTE*)m_pcEntries, MemMgr::RoundSize(m_iEntries * m_iEntrySize));
+ }
+
+//*****************************************************************************
+// 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 NewInit( // Return status.
+ ULONG iEntries, // # of entries.
+ ULONG iEntrySize, // Size of the entries.
+ int iMaxSize); // Max size of data.
+
+//*****************************************************************************
+// Clear the hash table as if there were nothing in it.
+//*****************************************************************************
+ void Clear()
+ {
+ WRAPPER_NO_CONTRACT;
+ m_iFree = 0;
+ InitFreeChain(0, m_iEntries);
+ CHashTable::Clear();
+ }
+
+//*****************************************************************************
+// Grabs a slot for the new entry to be added.
+// The caller should fill in the non-HASHENTRY part of the returned slot
+//*****************************************************************************
+ BYTE *Add(
+ ULONG iHash) // Hash value of entry to add.
+ {
+ WRAPPER_NO_CONTRACT;
+ FREEHASHENTRY *psEntry;
+
+ // Make the table bigger if necessary.
+ if (m_iFree == UINT32_MAX && !Grow())
+ return (NULL);
+
+ // Add the first entry from the free list to the hash chain.
+ psEntry = (FREEHASHENTRY *) CHashTable::Add(iHash, m_iFree);
+ m_iFree = psEntry->iFree;
+
+ // If we're recycling memory, give our memory-allocator a chance to re-init it.
+
+ // Each entry is prefixed with a header - we don't want to trash that.
+ SIZE_T cbHeader = sizeof(FREEHASHENTRY);
+ MemMgr::Clean((BYTE*) psEntry + cbHeader, (int) (m_iEntrySize - cbHeader));
+
+ return ((BYTE *) psEntry);
+ }
+
+//*****************************************************************************
+// Delete the struct at the specified index in m_pcEntries from the hash chains.
+//*****************************************************************************
+ void Delete(
+ ULONG iHash, // Hash value of entry to delete.
+ ULONG iIndex) // Index of struct in m_pcEntries.
+ {
+ WRAPPER_NO_CONTRACT;
+ CHashTable::Delete(iHash, iIndex);
+ ((FREEHASHENTRY *) EntryPtr(iIndex))->iFree = m_iFree;
+ m_iFree = iIndex;
+ }
+
+ void Delete(
+ ULONG iHash, // Hash value of entry to delete.
+ HASHENTRY *psEntry) // The struct to delete.
+ {
+ WRAPPER_NO_CONTRACT;
+ CHashTable::Delete(iHash, psEntry);
+ ((FREEHASHENTRY *) psEntry)->iFree = m_iFree;
+ m_iFree = ItemIndex(psEntry);
+ }
+
+#endif // #ifndef DACCESS_COMPILE
+
+ // This is a sad legacy workaround. The debugger's patch table (implemented as this
+ // class) is shared across process. We publish the runtime offsets of
+ // some key fields. Since those fields are private, we have to provide
+ // accessors here. So if you're not using these functions, don't start.
+ // We can hopefully remove them.
+ // Note that we can't just make RCThread a friend of this class (we tried
+ // originally) because the inheritence chain has a private modifier,
+ // so DebuggerPatchTable::m_pcEntries is illegal.
+ static SIZE_T helper_GetOffsetOfEntries()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return offsetof(CHashTableAndData, m_pcEntries);
+ }
+
+ static SIZE_T helper_GetOffsetOfCount()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return offsetof(CHashTableAndData, m_iEntries);
+ }
+
+#ifdef DACCESS_COMPILE
+ void EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+ {
+ SUPPORTS_DAC;
+ CHashTable::EnumMemoryRegions(flags, m_iEntries);
+ }
+#endif
+
+private:
+ void InitFreeChain(ULONG iStart,ULONG iEnd);
+ int Grow();
+};
+
+#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.
+//*****************************************************************************
+template<class MemMgr>
+HRESULT CHashTableAndData<MemMgr>::NewInit(// Return status.
+ ULONG iEntries, // # of entries.
+ ULONG iEntrySize, // Size of the entries.
+ int iMaxSize) // Max size of data.
+{
+ WRAPPER_NO_CONTRACT;
+ BYTE *pcEntries;
+ HRESULT hr;
+
+
+ // note that this function can throw because it depends on the <M>::Alloc
+
+ // Allocate the memory for the entries.
+ if ((pcEntries = MemMgr::Alloc(MemMgr::RoundSize(iEntries * iEntrySize),
+ MemMgr::RoundSize(iMaxSize))) == 0)
+ return (E_OUTOFMEMORY);
+ m_iEntries = iEntries;
+
+ // Init the base table.
+ if (FAILED(hr = CHashTable::NewInit(pcEntries, iEntrySize)))
+ MemMgr::Free(pcEntries, MemMgr::RoundSize(iEntries * iEntrySize));
+ else
+ {
+ // Init the free chain.
+ m_iFree = 0;
+ InitFreeChain(0, iEntries);
+ }
+ return (hr);
+}
+
+//*****************************************************************************
+// Initialize a range of records such that they are linked together to be put
+// on the free chain.
+//*****************************************************************************
+template<class MemMgr>
+void CHashTableAndData<MemMgr>::InitFreeChain(
+ ULONG iStart, // Index to start initializing.
+ ULONG iEnd) // Index to stop initializing
+{
+ LIMITED_METHOD_CONTRACT;
+ BYTE* pcPtr;
+ _ASSERTE(iEnd > iStart);
+
+ pcPtr = (BYTE*)m_pcEntries + iStart * (size_t)m_iEntrySize;
+ for (++iStart; iStart < iEnd; ++iStart)
+ {
+ ((FREEHASHENTRY *) pcPtr)->iFree = iStart;
+ pcPtr += m_iEntrySize;
+ }
+ ((FREEHASHENTRY *) pcPtr)->iFree = UINT32_MAX;
+}
+
+//*****************************************************************************
+// Attempt to increase the amount of space available for the record heap.
+//*****************************************************************************
+template<class MemMgr>
+int CHashTableAndData<MemMgr>::Grow() // 1 if successful, 0 if not.
+{
+ WRAPPER_NO_CONTRACT;
+ int iCurSize; // Current size in bytes.
+ int iEntries; // New # of entries.
+
+ _ASSERTE(m_pcEntries != NULL);
+ _ASSERTE(m_iFree == UINT32_MAX);
+
+ // Compute the current size and new # of entries.
+ S_UINT32 iTotEntrySize = S_UINT32(m_iEntries) * S_UINT32(m_iEntrySize);
+ if( iTotEntrySize.IsOverflow() )
+ {
+ _ASSERTE( !"CHashTableAndData overflow!" );
+ return (0);
+ }
+ iCurSize = MemMgr::RoundSize( iTotEntrySize.Value() );
+ iEntries = (iCurSize + MemMgr::GrowSize(iCurSize)) / m_iEntrySize;
+
+ if ( (iEntries < 0) || ((ULONG)iEntries <= m_iEntries) )
+ {
+ _ASSERTE( !"CHashTableAndData overflow!" );
+ return (0);
+ }
+
+ // Try to expand the array.
+ if (MemMgr::Grow(*(BYTE**)&m_pcEntries, iCurSize) == 0)
+ return (0);
+
+ // Init the newly allocated space.
+ InitFreeChain(m_iEntries, iEntries);
+ m_iFree = m_iEntries;
+ m_iEntries = iEntries;
+ return (1);
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+//*****************************************************************************
+//*****************************************************************************
+
+inline COUNT_T HashCOUNT_T(COUNT_T currentHash, COUNT_T data)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return ((currentHash << 5) + currentHash) ^ data;
+}
+
+inline COUNT_T HashPtr(COUNT_T currentHash, PTR_VOID ptr)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ return HashCOUNT_T(currentHash, COUNT_T(SIZE_T(dac_cast<TADDR>(ptr))));
+}
+
+inline DWORD HashThreeToOne(DWORD a, DWORD b, DWORD c)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ /*
+ lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+
+ These are functions for producing 32-bit hashes for hash table lookup.
+ hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+ are externally useful functions. Routines to test the hash are included
+ if SELF_TEST is defined. You can use this free for any purpose. It's in
+ the public domain. It has no warranty.
+ */
+
+ #define rot32(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+ c ^= b; c -= rot32(b,14);
+ a ^= c; a -= rot32(c,11);
+ b ^= a; b -= rot32(a,25);
+ c ^= b; c -= rot32(b,16);
+ a ^= c; a -= rot32(c,4);
+ b ^= a; b -= rot32(a,14);
+ c ^= b; c -= rot32(b,24);
+
+ return c;
+}
+
+inline ULONG HashBytes(BYTE const *pbData, size_t iSize)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG hash = 5381;
+
+ BYTE const *pbDataEnd = pbData + iSize;
+
+ for (/**/ ; pbData < pbDataEnd; pbData++)
+ {
+ hash = ((hash << 5) + hash) ^ *pbData;
+ }
+ return hash;
+}
+
+// Helper function for hashing a string char by char.
+inline ULONG HashStringA(LPCSTR szStr)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG hash = 5381;
+ int c;
+
+ while ((c = *szStr) != 0)
+ {
+ hash = ((hash << 5) + hash) ^ c;
+ ++szStr;
+ }
+ return hash;
+}
+
+inline ULONG HashString(LPCWSTR szStr)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG hash = 5381;
+ int c;
+
+ while ((c = *szStr) != 0)
+ {
+ hash = ((hash << 5) + hash) ^ c;
+ ++szStr;
+ }
+ return hash;
+}
+
+inline ULONG HashStringN(LPCWSTR szStr, SIZE_T cchStr)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG hash = 5381;
+
+ // hash the string two characters at a time
+ ULONG *ptr = (ULONG *)szStr;
+
+ // we assume that szStr is null-terminated
+ _ASSERTE(cchStr <= wcslen(szStr));
+ SIZE_T cDwordCount = (cchStr + 1) / 2;
+
+ for (SIZE_T i = 0; i < cDwordCount; i++)
+ {
+ hash = ((hash << 5) + hash) ^ ptr[i];
+ }
+
+ return hash;
+}
+
+// Case-insensitive string hash function.
+inline ULONG HashiStringA(LPCSTR szStr)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG hash = 5381;
+ while (*szStr != 0)
+ {
+ hash = ((hash << 5) + hash) ^ toupper(*szStr);
+ szStr++;
+ }
+ return hash;
+}
+
+// Case-insensitive string hash function.
+inline ULONG HashiString(LPCWSTR szStr)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG hash = 5381;
+ while (*szStr != 0)
+ {
+ hash = ((hash << 5) + hash) ^ towupper(*szStr);
+ szStr++;
+ }
+ return hash;
+}
+
+// Case-insensitive string hash function.
+inline ULONG HashiStringN(LPCWSTR szStr, DWORD count)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG hash = 5381;
+ while (*szStr != 0 && count--)
+ {
+ hash = ((hash << 5) + hash) ^ towupper(*szStr);
+ szStr++;
+ }
+ return hash;
+}
+
+// Case-insensitive string hash function when all of the
+// characters in the string are known to be below 0x80.
+// Knowing this is much more efficient than calling
+// towupper above.
+inline ULONG HashiStringKnownLower80(LPCWSTR szStr) {
+ LIMITED_METHOD_CONTRACT;
+ ULONG hash = 5381;
+ int c;
+ int mask = ~0x20;
+ while ((c = *szStr)!=0) {
+ //If we have a lowercase character, ANDing off 0x20
+ //(mask) will make it an uppercase character.
+ if (c>='a' && c<='z') {
+ c&=mask;
+ }
+ hash = ((hash << 5) + hash) ^ c;
+ ++szStr;
+ }
+ return hash;
+}
+
+inline ULONG HashiStringNKnownLower80(LPCWSTR szStr, DWORD count) {
+ LIMITED_METHOD_CONTRACT;
+ ULONG hash = 5381;
+ int c;
+ int mask = ~0x20;
+ while ((c = *szStr) !=0 && count--) {
+ //If we have a lowercase character, ANDing off 0x20
+ //(mask) will make it an uppercase character.
+ if (c>='a' && c<='z') {
+ c&=mask;
+ }
+ hash = ((hash << 5) + hash) ^ c;
+ ++szStr;
+ }
+ return hash;
+}
+
+//*****************************************************************************
+// IMPORTANT: This data structure is deprecated, please do not add any new uses.
+// The hashtable implementation that should be used instead is code:SHash.
+// If code:SHash does not work for you, talk to mailto:clrdeag.
+//*****************************************************************************
+// This class implements a closed hashing table. Values are hashed to a bucket,
+// and if that bucket is full already, then the value is placed in the next
+// free bucket starting after the desired target (with wrap around). If the
+// table becomes 75% full, it is grown and rehashed to reduce lookups. This
+// class is best used in a reltively small lookup table where hashing is
+// not going to cause many collisions. By not having the collision chain
+// logic, a lot of memory is saved.
+//
+// The user of the template is required to supply several methods which decide
+// how each element can be marked as free, deleted, or used. It would have
+// been possible to write this with more internal logic, but that would require
+// either (a) more overhead to add status on top of elements, or (b) hard
+// coded types like one for strings, one for ints, etc... This gives you the
+// flexibility of adding logic to your type.
+//*****************************************************************************
+class CClosedHashBase
+{
+ BYTE *EntryPtr(int iEntry)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_rgData + (iEntry * (size_t)m_iEntrySize));
+ }
+
+ BYTE *EntryPtr(int iEntry, BYTE *rgData)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (rgData + (iEntry * (size_t)m_iEntrySize));
+ }
+
+public:
+ enum ELEMENTSTATUS
+ {
+ FREE, // Item is not in use right now.
+ DELETED, // Item is deleted.
+ USED // Item is in use.
+ };
+
+ CClosedHashBase(
+ int iBuckets, // How many buckets should we start with.
+ int iEntrySize, // Size of an entry.
+ bool bPerfect) : // true if bucket size will hash with no collisions.
+ m_bPerfect(bPerfect),
+ m_iBuckets(iBuckets),
+ m_iEntrySize(iEntrySize),
+ m_iCount(0),
+ m_iCollisions(0),
+ m_rgData(0)
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_iSize = iBuckets + 7;
+ }
+
+ virtual ~CClosedHashBase()
+ {
+ WRAPPER_NO_CONTRACT;
+ Clear();
+ }
+
+ virtual void Clear()
+ {
+ LIMITED_METHOD_CONTRACT;
+ delete [] m_rgData;
+ m_iCount = 0;
+ m_iCollisions = 0;
+ m_rgData = 0;
+ }
+
+//*****************************************************************************
+// Accessors for getting at the underlying data. Be careful to use Count()
+// only when you want the number of buckets actually used.
+//*****************************************************************************
+
+ int Count()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_iCount);
+ }
+
+ int Collisions()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_iCollisions);
+ }
+
+ int Buckets()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_iBuckets);
+ }
+
+ void SetBuckets(int iBuckets, bool bPerfect=false)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(m_rgData == 0);
+ m_iBuckets = iBuckets;
+ m_iSize = m_iBuckets + 7;
+ m_bPerfect = bPerfect;
+ }
+
+ BYTE *Data()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_rgData);
+ }
+
+//*****************************************************************************
+// Add a new item to hash table given the key value. If this new entry
+// exceeds maximum size, then the table will grow and be re-hashed, which
+// may cause a memory error.
+//*****************************************************************************
+ BYTE *Add( // New item to fill out on success.
+ void *pData) // The value to hash on.
+ {
+ WRAPPER_NO_CONTRACT;
+ // 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);
+ }
+
+ return (DoAdd(pData, m_rgData, m_iBuckets, m_iSize, m_iCollisions, m_iCount));
+ }
+
+//*****************************************************************************
+// 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 Delete(
+ void *pData); // Key value to delete.
+
+
+//*****************************************************************************
+// Callback function passed to DeleteLoop.
+//*****************************************************************************
+ typedef BOOL (* DELETELOOPFUNC)( // Delete current item?
+ BYTE *pEntry, // Bucket entry to evaluate
+ void *pCustomizer); // User-defined value
+
+//*****************************************************************************
+// 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 DeleteLoop(
+ DELETELOOPFUNC pDeleteLoopFunc, // Decides whether to delete item
+ void *pCustomizer); // Extra value passed to deletefunc.
+
+
+//*****************************************************************************
+// Lookup a key value and return a pointer to the element if found.
+//*****************************************************************************
+ BYTE *Find( // The item if found, 0 if not.
+ void *pData); // The key to lookup.
+
+//*****************************************************************************
+// Look for an item in the table. If it isn't found, then create a new one and
+// return that.
+//*****************************************************************************
+ BYTE *FindOrAdd( // The item if found, 0 if not.
+ void *pData, // The key to lookup.
+ bool &bNew); // true if created.
+
+//*****************************************************************************
+// The following functions are used to traverse each used entry. This code
+// will skip over deleted and free entries freeing the caller up from such
+// logic.
+//*****************************************************************************
+ BYTE *GetFirst() // The first entry, 0 if none.
+ {
+ WRAPPER_NO_CONTRACT;
+ int i; // Loop control.
+
+ // If we've never allocated the table there can't be any to get.
+ if (m_rgData == 0)
+ return (0);
+
+ // Find the first one.
+ for (i=0; i<m_iSize; i++)
+ {
+ if (Status(EntryPtr(i)) != FREE && Status(EntryPtr(i)) != DELETED)
+ return (EntryPtr(i));
+ }
+ return (0);
+ }
+
+ BYTE *GetNext(BYTE *Prev) // The next entry, 0 if done.
+ {
+ WRAPPER_NO_CONTRACT;
+ int i; // Loop control.
+
+ for (i = (int)(((size_t) Prev - (size_t) &m_rgData[0]) / m_iEntrySize) + 1; i<m_iSize; i++)
+ {
+ if (Status(EntryPtr(i)) != FREE && Status(EntryPtr(i)) != DELETED)
+ return (EntryPtr(i));
+ }
+ return (0);
+ }
+
+private:
+//*****************************************************************************
+// Hash is called with a pointer to an element in the table. You must override
+// this method and provide a hash algorithm for your element type.
+//*****************************************************************************
+ virtual unsigned int Hash( // The key value.
+ void const *pData)=0; // Raw data to hash.
+
+//*****************************************************************************
+// Compare is used in the typical memcmp way, 0 is eqaulity, -1/1 indicate
+// direction of miscompare. In this system everything is always equal or not.
+//*****************************************************************************
+ virtual unsigned int Compare( // 0, -1, or 1.
+ void const *pData, // Raw key data on lookup.
+ BYTE *pElement)=0; // The element to compare data against.
+
+//*****************************************************************************
+// Return true if the element is free to be used.
+//*****************************************************************************
+ virtual ELEMENTSTATUS Status( // The status of the entry.
+ BYTE *pElement)=0; // The element to check.
+
+//*****************************************************************************
+// Sets the status of the given element.
+//*****************************************************************************
+ virtual void SetStatus(
+ BYTE *pElement, // The element to set status for.
+ ELEMENTSTATUS eStatus)=0; // New status.
+
+//*****************************************************************************
+// Returns the internal key value for an element.
+//*****************************************************************************
+ virtual void *GetKey( // The data to hash on.
+ BYTE *pElement)=0; // The element to return data ptr for.
+
+//*****************************************************************************
+// This helper actually does the add for you.
+//*****************************************************************************
+ BYTE *DoAdd(void *pData, BYTE *rgData, int &iBuckets, int iSize,
+ int &iCollisions, int &iCount);
+
+//*****************************************************************************
+// 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 ReHash(); // true if successful.
+
+//*****************************************************************************
+// Walk each item in the table and mark it free.
+//*****************************************************************************
+ void InitFree(BYTE *ptr, int iSize)
+ {
+ WRAPPER_NO_CONTRACT;
+ int i;
+ for (i=0; i<iSize; i++, ptr += m_iEntrySize)
+ SetStatus(ptr, FREE);
+ }
+
+private:
+ bool m_bPerfect; // true if the table size guarantees
+ // no collisions.
+ int m_iBuckets; // How many buckets do we have.
+ int m_iEntrySize; // Size of an entry.
+ int m_iSize; // How many elements can we have.
+ int m_iCount; // How many items cannot be used (NON free, i.e. USED+DELETED).
+ int m_iCollisions; // How many have we had.
+ BYTE *m_rgData; // Data element list.
+};
+
+//*****************************************************************************
+// IMPORTANT: This data structure is deprecated, please do not add any new uses.
+// The hashtable implementation that should be used instead is code:SHash.
+// If code:SHash does not work for you, talk to mailto:clrdeag.
+//*****************************************************************************
+template <class T> class CClosedHash : public CClosedHashBase
+{
+public:
+ CClosedHash(
+ int iBuckets, // How many buckets should we start with.
+ bool bPerfect=false) : // true if bucket size will hash with no collisions.
+ CClosedHashBase(iBuckets, sizeof(T), bPerfect)
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+
+ T &operator[](int iIndex)
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T &) *(Data() + (iIndex * sizeof(T))));
+ }
+
+
+//*****************************************************************************
+// Add a new item to hash table given the key value. If this new entry
+// exceeds maximum size, then the table will grow and be re-hashed, which
+// may cause a memory error.
+//*****************************************************************************
+ T *Add( // New item to fill out on success.
+ void *pData) // The value to hash on.
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T *) CClosedHashBase::Add(pData));
+ }
+
+//*****************************************************************************
+// Lookup a key value and return a pointer to the element if found.
+//*****************************************************************************
+ T *Find( // The item if found, 0 if not.
+ void *pData) // The key to lookup.
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T *) CClosedHashBase::Find(pData));
+ }
+
+//*****************************************************************************
+// Look for an item in the table. If it isn't found, then create a new one and
+// return that.
+//*****************************************************************************
+ T *FindOrAdd( // The item if found, 0 if not.
+ void *pData, // The key to lookup.
+ bool &bNew) // true if created.
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T *) CClosedHashBase::FindOrAdd(pData, bNew));
+ }
+
+
+//*****************************************************************************
+// The following functions are used to traverse each used entry. This code
+// will skip over deleted and free entries freeing the caller up from such
+// logic.
+//*****************************************************************************
+ T *GetFirst() // The first entry, 0 if none.
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T *) CClosedHashBase::GetFirst());
+ }
+
+ T *GetNext(T *Prev) // The next entry, 0 if done.
+ {
+ WRAPPER_NO_CONTRACT;
+ return ((T *) CClosedHashBase::GetNext((BYTE *) Prev));
+ }
+};
+
+
+//*****************************************************************************
+// IMPORTANT: This data structure is deprecated, please do not add any new uses.
+// The hashtable implementation that should be used instead is code:SHash.
+// If code:SHash does not work for you, talk to mailto:clrdeag.
+//*****************************************************************************
+// Closed hash with typed parameters. The derived class is the second
+// parameter to the template. The derived class must implement:
+// unsigned long Hash(const T *pData);
+// unsigned long Compare(const T *p1, T *p2);
+// ELEMENTSTATUS Status(T *pEntry);
+// void SetStatus(T *pEntry, ELEMENTSTATUS s);
+// void* GetKey(T *pEntry);
+//*****************************************************************************
+template<class T, class H>class CClosedHashEx : public CClosedHash<T>
+{
+public:
+ CClosedHashEx(
+ int iBuckets, // How many buckets should we start with.
+ bool bPerfect=false) : // true if bucket size will hash with no collisions.
+ CClosedHash<T> (iBuckets, bPerfect)
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+
+ unsigned int Hash(const void *pData)
+ {
+ WRAPPER_NO_CONTRACT;
+ return static_cast<H*>(this)->Hash((const T*)pData);
+ }
+
+ unsigned int Compare(const void *p1, BYTE *p2)
+ {
+ WRAPPER_NO_CONTRACT;
+ return static_cast<H*>(this)->Compare((const T*)p1, (T*)p2);
+ }
+
+ typename CClosedHash<T>::ELEMENTSTATUS Status(BYTE *p)
+ {
+ WRAPPER_NO_CONTRACT;
+ return static_cast<H*>(this)->Status((T*)p);
+ }
+
+ void SetStatus(BYTE *p, typename CClosedHash<T>::ELEMENTSTATUS s)
+ {
+ WRAPPER_NO_CONTRACT;
+ static_cast<H*>(this)->SetStatus((T*)p, s);
+ }
+
+ void* GetKey(BYTE *p)
+ {
+ WRAPPER_NO_CONTRACT;
+ return static_cast<H*>(this)->GetKey((T*)p);
+ }
+};
+
+
+//*****************************************************************************
+// IMPORTANT: This data structure is deprecated, please do not add any new uses.
+// The hashtable implementation that should be used instead is code:SHash.
+// If code:SHash does not work for you, talk to mailto:clrdeag.
+//*****************************************************************************
+// This template is another form of a closed hash table. It handles collisions
+// through a linked chain. To use it, derive your hashed item from HASHLINK
+// and implement the virtual functions required. 1.5 * ibuckets will be
+// allocated, with the extra .5 used for collisions. If you add to the point
+// where no free nodes are available, the entire table is grown to make room.
+// The advantage to this system is that collisions are always directly known,
+// there either is one or there isn't.
+//*****************************************************************************
+struct HASHLINK
+{
+ ULONG iNext; // Offset for next entry.
+};
+
+template <class T> class CChainedHash
+{
+ friend class VerifyLayoutsMD;
+public:
+ CChainedHash(int iBuckets=32) :
+ m_rgData(0),
+ m_iBuckets(iBuckets),
+ m_iCount(0),
+ m_iMaxChain(0),
+ m_iFree(0)
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_iSize = iBuckets + (iBuckets / 2);
+ }
+
+ ~CChainedHash()
+ {
+ LIMITED_METHOD_CONTRACT;
+ if (m_rgData)
+ delete [] m_rgData;
+ }
+
+ void SetBuckets(int iBuckets)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(m_rgData == 0);
+ // if iBuckets==0, then we'll allocate a zero size array and AV on dereference.
+ _ASSERTE(iBuckets > 0);
+ m_iBuckets = iBuckets;
+ m_iSize = iBuckets + (iBuckets / 2);
+ }
+
+ T *Add(void const *pData)
+ {
+ WRAPPER_NO_CONTRACT;
+ ULONG iHash;
+ int iBucket;
+ T *pItem;
+
+ // Build the list if required.
+ if (m_rgData == 0 || m_iFree == 0xffffffff)
+ {
+ if (!ReHash())
+ return (0);
+ }
+
+ // Hash the item and pick a bucket.
+ iHash = Hash(pData);
+ iBucket = iHash % m_iBuckets;
+
+ // Use the bucket if it is free.
+ if (InUse(&m_rgData[iBucket]) == false)
+ {
+ pItem = &m_rgData[iBucket];
+ pItem->iNext = 0xffffffff;
+ }
+ // Else take one off of the free list for use.
+ else
+ {
+ ULONG iEntry;
+
+ // Pull an item from the free list.
+ iEntry = m_iFree;
+ pItem = &m_rgData[m_iFree];
+ m_iFree = pItem->iNext;
+
+ // Link the new node in after the bucket.
+ pItem->iNext = m_rgData[iBucket].iNext;
+ m_rgData[iBucket].iNext = iEntry;
+ }
+ ++m_iCount;
+ return (pItem);
+ }
+
+ T *Find(void const *pData, bool bAddIfNew=false)
+ {
+ WRAPPER_NO_CONTRACT;
+ ULONG iHash;
+ int iBucket;
+ T *pItem;
+
+ // Check states for lookup.
+ if (m_rgData == 0)
+ {
+ // If we won't be adding, then we are through.
+ if (bAddIfNew == false)
+ return (0);
+
+ // Otherwise, create the table.
+ if (!ReHash())
+ return (0);
+ }
+
+ // Hash the item and pick a bucket.
+ iHash = Hash(pData);
+ iBucket = iHash % m_iBuckets;
+
+ // If it isn't in use, then there it wasn't found.
+ if (!InUse(&m_rgData[iBucket]))
+ {
+ if (bAddIfNew == false)
+ pItem = 0;
+ else
+ {
+ pItem = &m_rgData[iBucket];
+ pItem->iNext = 0xffffffff;
+ ++m_iCount;
+ }
+ }
+ // Scan the list for the one we want.
+ else
+ {
+ ULONG iChain = 0;
+ for (pItem=(T *) &m_rgData[iBucket]; pItem; pItem=GetNext(pItem))
+ {
+ if (Cmp(pData, pItem) == 0)
+ break;
+ ++iChain;
+ }
+
+ if (!pItem && bAddIfNew)
+ {
+ ULONG iEntry;
+
+ // Record maximum chain length.
+ if (iChain > m_iMaxChain)
+ m_iMaxChain = iChain;
+
+ // Now need more room.
+ if (m_iFree == 0xffffffff)
+ {
+ if (!ReHash())
+ return (0);
+ }
+
+ // Pull an item from the free list.
+ iEntry = m_iFree;
+ pItem = &m_rgData[m_iFree];
+ m_iFree = pItem->iNext;
+
+ // Link the new node in after the bucket.
+ pItem->iNext = m_rgData[iBucket].iNext;
+ m_rgData[iBucket].iNext = iEntry;
+ ++m_iCount;
+ }
+ }
+ return (pItem);
+ }
+
+ int Count()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_iCount);
+ }
+
+ int Buckets()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_iBuckets);
+ }
+
+ ULONG MaxChainLength()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_iMaxChain);
+ }
+
+ virtual void Clear()
+ {
+ LIMITED_METHOD_CONTRACT;
+ // Free up the memory.
+ if (m_rgData)
+ {
+ delete [] m_rgData;
+ m_rgData = 0;
+ }
+
+ m_rgData = 0;
+ m_iFree = 0;
+ m_iCount = 0;
+ m_iMaxChain = 0;
+ }
+
+ virtual bool InUse(T *pItem)=0;
+ virtual void SetFree(T *pItem)=0;
+ virtual ULONG Hash(void const *pData)=0;
+ virtual int Cmp(void const *pData, void *pItem)=0;
+private:
+ inline T *GetNext(T *pItem)
+ {
+ LIMITED_METHOD_CONTRACT;
+ if (pItem->iNext != 0xffffffff)
+ return ((T *) &m_rgData[pItem->iNext]);
+ return (0);
+ }
+
+ bool ReHash()
+ {
+ WRAPPER_NO_CONTRACT;
+ T *rgTemp;
+ int iNewSize;
+
+ // If this is a first time allocation, then just malloc it.
+ if (!m_rgData)
+ {
+ if ((m_rgData = new (nothrow) T[m_iSize]) == 0)
+ return (false);
+
+ int i;
+ for (i=0; i<m_iSize; i++)
+ SetFree(&m_rgData[i]);
+
+ m_iFree = m_iBuckets;
+ for (i=m_iBuckets; i<m_iSize; i++)
+ ((T *) &m_rgData[i])->iNext = i + 1;
+ ((T *) &m_rgData[m_iSize - 1])->iNext = 0xffffffff;
+ return (true);
+ }
+
+ // Otherwise we need more room on the free chain, so allocate some.
+ iNewSize = m_iSize + (m_iSize / 2);
+
+ // Allocate/realloc memory.
+ if ((rgTemp = new (nothrow) T[iNewSize]) == 0)
+ return (false);
+
+ memcpy (rgTemp,m_rgData,m_iSize*sizeof(T));
+ delete [] m_rgData;
+
+ // Init new entries, save the new free chain, and reset internals.
+ m_iFree = m_iSize;
+ for (int i=m_iFree; i<iNewSize; i++)
+ {
+ SetFree(&rgTemp[i]);
+ ((T *) &rgTemp[i])->iNext = i + 1;
+ }
+ ((T *) &rgTemp[iNewSize - 1])->iNext = 0xffffffff;
+
+ m_rgData = rgTemp;
+ m_iSize = iNewSize;
+ return (true);
+ }
+
+private:
+ T *m_rgData; // Data to store items in.
+ int m_iBuckets; // How many buckets we want.
+ int m_iSize; // How many are allocated.
+ int m_iCount; // How many are we using.
+ ULONG m_iMaxChain; // Max chain length.
+ ULONG m_iFree; // Free chain.
+};
+
+
+//*****************************************************************************
+//
+//********** String helper functions.
+//
+//*****************************************************************************
+
+//*****************************************************************************
+// Checks if string length exceeds the specified limit
+//*****************************************************************************
+inline BOOL IsStrLongerThan(_In_ _In_z_ char* pstr, unsigned N)
+{
+ LIMITED_METHOD_CONTRACT;
+ unsigned i = 0;
+ if(pstr)
+ {
+ for(i=0; (i < N)&&(pstr[i]); i++);
+ }
+ return (i >= N);
+}
+
+
+//*****************************************************************************
+// Class to parse a list of simple assembly names and then find a match
+//*****************************************************************************
+
+class AssemblyNamesList
+{
+ struct AssemblyName
+ {
+ LPUTF8 m_assemblyName;
+ AssemblyName *m_next; // Next name
+ };
+
+ AssemblyName *m_pNames; // List of names
+
+public:
+
+ bool IsInList(LPCUTF8 assemblyName);
+
+ bool IsEmpty()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_pNames == 0;
+ }
+
+ AssemblyNamesList(_In_ LPWSTR list);
+ ~AssemblyNamesList();
+};
+
+//*****************************************************************************
+// Class to parse a list of method names and then find a match
+//*****************************************************************************
+
+struct CORINFO_SIG_INFO;
+
+class MethodNamesListBase
+{
+ struct MethodName
+ {
+ LPUTF8 methodName; // NULL means wildcard
+ LPUTF8 className; // NULL means wildcard
+ int numArgs; // number of args for the method, -1 is wildcard
+ MethodName *next; // Next name
+ };
+
+ MethodName *pNames; // List of names
+
+ bool IsInList(LPCUTF8 methodName, LPCUTF8 className, int numArgs);
+
+public:
+ void Init()
+ {
+ LIMITED_METHOD_CONTRACT;
+ pNames = 0;
+ }
+
+ void Init(_In_ _In_z_ LPWSTR list)
+ {
+ WRAPPER_NO_CONTRACT;
+ pNames = 0;
+ Insert(list);
+ }
+
+ void Destroy();
+
+ void Insert(_In_ _In_z_ LPWSTR list);
+
+ bool IsInList(LPCUTF8 methodName, LPCUTF8 className, PCCOR_SIGNATURE sig = NULL);
+ bool IsInList(LPCUTF8 methodName, LPCUTF8 className, CORINFO_SIG_INFO* pSigInfo);
+ bool IsEmpty()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return pNames == 0;
+ }
+};
+
+class MethodNamesList : public MethodNamesListBase
+{
+public:
+ MethodNamesList()
+ {
+ WRAPPER_NO_CONTRACT;
+ Init();
+ }
+
+ MethodNamesList(_In_ LPWSTR list)
+ {
+ WRAPPER_NO_CONTRACT;
+ Init(list);
+ }
+
+ ~MethodNamesList()
+ {
+ WRAPPER_NO_CONTRACT;
+ Destroy();
+ }
+};
+
+//*****************************************************************************
+// Convert a pointer to a string into a GUID.
+//*****************************************************************************
+HRESULT LPCSTRToGuid( // Return status.
+ LPCSTR szGuid, // String to convert.
+ GUID *psGuid); // Buffer for converted GUID.
+
+//*****************************************************************************
+// Convert a GUID into a pointer to a string
+//*****************************************************************************
+int GuidToLPWSTR( // Return status.
+ GUID Guid, // [IN] The GUID to convert.
+ _Out_writes_ (cchGuid) LPWSTR szGuid, // [OUT] String into which the GUID is stored
+ DWORD cchGuid); // [IN] Size in wide chars of szGuid
+
+//*****************************************************************************
+// Parse a Wide char string into a GUID
+//*****************************************************************************
+BOOL LPWSTRToGuid(
+ GUID * Guid, // [OUT] The GUID to fill in
+ _In_reads_(cchGuid) LPCWSTR szGuid, // [IN] String to parse
+ DWORD cchGuid); // [IN] Count in wchars in string
+
+typedef VPTR(class RangeList) PTR_RangeList;
+
+class RangeList
+{
+ public:
+ VPTR_BASE_CONCRETE_VTABLE_CLASS(RangeList)
+
+#ifndef DACCESS_COMPILE
+ RangeList();
+ ~RangeList();
+#else
+ RangeList()
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+#endif
+
+ // Wrappers to make the virtual calls DAC-safe.
+ BOOL AddRange(const BYTE *start, const BYTE *end, void *id)
+ {
+ return this->AddRangeWorker(start, end, id);
+ }
+
+ void RemoveRanges(void *id, const BYTE *start = NULL, const BYTE *end = NULL)
+ {
+ return this->RemoveRangesWorker(id, start, end);
+ }
+
+ BOOL IsInRange(TADDR address, TADDR *pID = NULL)
+ {
+ SUPPORTS_DAC;
+
+ return this->IsInRangeWorker(address, pID);
+ }
+
+#ifndef DACCESS_COMPILE
+
+ // You can overload these two for synchronization (as LockedRangeList does)
+ virtual BOOL AddRangeWorker(const BYTE *start, const BYTE *end, void *id);
+ // If both "start" and "end" are NULL, then this method deletes all ranges with
+ // the given id (i.e. the original behaviour). Otherwise, it ignores the given
+ // id and deletes all ranges falling in the region [start, end).
+ virtual void RemoveRangesWorker(void *id, const BYTE *start = NULL, const BYTE *end = NULL);
+#else
+ virtual BOOL AddRangeWorker(const BYTE *start, const BYTE *end, void *id)
+ {
+ return TRUE;
+ }
+ virtual void RemoveRangesWorker(void *id, const BYTE *start = NULL, const BYTE *end = NULL) { }
+#endif // !DACCESS_COMPILE
+
+ virtual BOOL IsInRangeWorker(TADDR address, TADDR *pID = NULL);
+
+#ifdef DACCESS_COMPILE
+ void EnumMemoryRegions(enum CLRDataEnumMemoryFlags flags);
+#endif
+
+ enum
+ {
+ RANGE_COUNT = 10
+ };
+
+
+ private:
+ struct Range
+ {
+ TADDR start;
+ TADDR end;
+ TADDR id;
+ };
+
+ struct RangeListBlock
+ {
+ Range ranges[RANGE_COUNT];
+ DPTR(RangeListBlock) next;
+
+#ifdef DACCESS_COMPILE
+ void EnumMemoryRegions(enum CLRDataEnumMemoryFlags flags);
+#endif
+
+ };
+
+ void InitBlock(RangeListBlock *block);
+
+ RangeListBlock m_starterBlock;
+ DPTR(RangeListBlock) m_firstEmptyBlock;
+ TADDR m_firstEmptyRange;
+};
+
+
+//
+// A private function to do the equavilent 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.
+//
+
+HRESULT FakeCoCreateInstanceEx(REFCLSID rclsid,
+ LPCWSTR wszDllPath,
+ REFIID riid,
+ void ** ppv,
+ HMODULE * phmodDll);
+
+// Provided for backward compatibility and for code that doesn't need the HMODULE of the
+// DLL that was loaded to create the COM object. See comment at implementation of
+// code:FakeCoCreateInstanceEx for more details.
+inline HRESULT FakeCoCreateInstance(REFCLSID rclsid,
+ REFIID riid,
+ void ** ppv)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ return FakeCoCreateInstanceEx(rclsid, NULL, riid, ppv, NULL);
+};
+
+//*****************************************************************************
+// Gets the directory based on the location of the module. This routine
+// is called at COR setup time. Set is called during EEStartup and by the
+// MetaData dispenser.
+//*****************************************************************************
+HRESULT GetInternalSystemDirectory(_Out_writes_to_opt_(*pdwLength,*pdwLength) LPWSTR buffer, __inout DWORD* pdwLength);
+LPCWSTR GetInternalSystemDirectory(_Out_opt_ DWORD * pdwLength = NULL);
+
+//*****************************************************************************
+// This function validates the given Method/Field/Standalone signature. (util.cpp)
+//*****************************************************************************
+struct IMDInternalImport;
+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
+
+//*****************************************************************************
+// 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.
+//*****************************************************************************
+HRESULT GetImageRuntimeVersionString(PVOID pMetaData, LPCSTR* pString);
+
+//*****************************************************************************
+// The registry keys and values that contain the information regarding
+// the default registered unmanaged debugger.
+//*****************************************************************************
+
+#define kDebugApplicationsPoliciesKey W("SOFTWARE\\Policies\\Microsoft\\Windows\\Windows Error Reporting\\DebugApplications")
+#define kDebugApplicationsKey W("SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\DebugApplications")
+
+#define kUnmanagedDebuggerKey W("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug")
+#define kUnmanagedDebuggerValue W("Debugger")
+#define kUnmanagedDebuggerAutoValue W("Auto")
+#define kUnmanagedDebuggerAutoExclusionListKey W("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\\AutoExclusionList")
+
+BOOL GetRegistryLongValue(HKEY hKeyParent, // Parent key.
+ LPCWSTR szKey, // Key name to look at.
+ LPCWSTR szName, // Name of value to get.
+ long *pValue, // Put value here, if found.
+ BOOL fReadNonVirtualizedKey); // Whether to read 64-bit hive on WOW64
+
+HRESULT GetCurrentModuleFileName(SString& pBuffer);
+
+//*****************************************************************************
+// Retrieve information regarding what registered default debugger
+//*****************************************************************************
+void GetDebuggerSettingInfo(SString &debuggerKeyValue, BOOL *pfAuto);
+HRESULT GetDebuggerSettingInfoWorker(_Out_writes_to_opt_(*pcchDebuggerString, *pcchDebuggerString) LPWSTR wszDebuggerString, DWORD * pcchDebuggerString, BOOL * pfAuto);
+
+void TrimWhiteSpace(__inout_ecount(*pcch) LPCWSTR *pwsz, __inout LPDWORD pcch);
+
+
+//*****************************************************************************
+// 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 = 0); // Initial characters in the array to leave (default 0).
+
+//*****************************************************************************
+// Extract the movl 64-bit unsigned immediate from an IA64 bundle
+// (Format X2)
+//*****************************************************************************
+UINT64 GetIA64Imm64(UINT64 * pBundle);
+UINT64 GetIA64Imm64(UINT64 qword0, UINT64 qword1);
+
+//*****************************************************************************
+// Deposit the movl 64-bit unsigned immediate into an IA64 bundle
+// (Format X2)
+//*****************************************************************************
+void PutIA64Imm64(UINT64 * pBundle, UINT64 imm64);
+
+//*****************************************************************************
+// 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);
+INT32 GetIA64Rel25(UINT64 qword0, UINT64 qword1, UINT32 slot);
+
+//*****************************************************************************
+// 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);
+
+//*****************************************************************************
+// Extract the IP-Relative signed 64-bit immediate from an IA64 bundle
+// (Formats X3 or X4)
+//*****************************************************************************
+INT64 GetIA64Rel64(UINT64 * pBundle);
+INT64 GetIA64Rel64(UINT64 qword0, UINT64 qword1);
+
+//*****************************************************************************
+// Deposit the IP-Relative signed 64-bit immediate into a IA64 bundle
+// (Formats X3 or X4)
+//*****************************************************************************
+void PutIA64Rel64(UINT64 * pBundle, INT64 imm64);
+
+//*****************************************************************************
+// Extract the 32-bit immediate from movw/movt Thumb2 sequence
+//*****************************************************************************
+UINT32 GetThumb2Mov32(UINT16 * p);
+
+//*****************************************************************************
+// Deposit the 32-bit immediate into movw/movt Thumb2 sequence
+//*****************************************************************************
+void PutThumb2Mov32(UINT16 * p, UINT32 imm32);
+
+//*****************************************************************************
+// Extract the 24-bit rel offset from bl instruction
+//*****************************************************************************
+INT32 GetThumb2BlRel24(UINT16 * p);
+
+//*****************************************************************************
+// Extract the 24-bit rel offset from bl instruction
+//*****************************************************************************
+void PutThumb2BlRel24(UINT16 * p, INT32 imm24);
+
+//*****************************************************************************
+// Extract the PC-Relative offset from a b or bl instruction
+//*****************************************************************************
+INT32 GetArm64Rel28(UINT32 * pCode);
+
+//*****************************************************************************
+// Extract the PC-Relative page address from an adrp instruction
+//*****************************************************************************
+INT32 GetArm64Rel21(UINT32 * pCode);
+
+//*****************************************************************************
+// Extract the page offset from an add instruction
+//*****************************************************************************
+INT32 GetArm64Rel12(UINT32 * pCode);
+
+//*****************************************************************************
+// Deposit the PC-Relative offset 'imm28' into a b or bl instruction
+//*****************************************************************************
+void PutArm64Rel28(UINT32 * pCode, INT32 imm28);
+
+//*****************************************************************************
+// Deposit the PC-Relative page address 'imm21' into an adrp instruction
+//*****************************************************************************
+void PutArm64Rel21(UINT32 * pCode, INT32 imm21);
+
+//*****************************************************************************
+// Deposit the page offset 'imm12' into an add instruction
+//*****************************************************************************
+void PutArm64Rel12(UINT32 * pCode, INT32 imm12);
+
+//*****************************************************************************
+// Returns whether the offset fits into bl instruction
+//*****************************************************************************
+inline bool FitsInThumb2BlRel24(INT32 imm24)
+{
+ return ((imm24 << 7) >> 7) == imm24;
+}
+
+//*****************************************************************************
+// Returns whether the offset fits into an Arm64 b or bl instruction
+//*****************************************************************************
+inline bool FitsInRel28(INT32 val32)
+{
+ return (val32 >= -0x08000000) && (val32 < 0x08000000);
+}
+
+//*****************************************************************************
+// Returns whether the offset fits into an Arm64 adrp instruction
+//*****************************************************************************
+inline bool FitsInRel21(INT32 val32)
+{
+ return (val32 >= 0) && (val32 <= 0x001FFFFF);
+}
+
+//*****************************************************************************
+// Returns whether the offset fits into an Arm64 add instruction
+//*****************************************************************************
+inline bool FitsInRel12(INT32 val32)
+{
+ return (val32 >= 0) && (val32 <= 0x00000FFF);
+}
+
+//*****************************************************************************
+// Returns whether the offset fits into an Arm64 b or bl instruction
+//*****************************************************************************
+inline bool FitsInRel28(INT64 val64)
+{
+ return (val64 >= -0x08000000LL) && (val64 < 0x08000000LL);
+}
+
+#if !defined(DACCESS_COMPILE)
+
+extern thread_local size_t t_ThreadType;
+
+// check if current thread is a GC thread (concurrent or server)
+inline BOOL IsGCSpecialThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ return !!(t_ThreadType & ThreadType_GC);
+}
+
+// check if current thread is a Gate thread
+inline BOOL IsGateSpecialThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_Gate);
+}
+
+// check if current thread is a Timer thread
+inline BOOL IsTimerSpecialThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_Timer);
+}
+
+// check if current thread is a debugger helper thread
+inline BOOL IsDbgHelperSpecialThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_DbgHelper);
+}
+
+// check if current thread is a debugger helper thread
+inline BOOL IsETWRundownSpecialThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_ETWRundownThread);
+}
+
+// check if current thread is a generic instantiation lookup compare thread
+inline BOOL IsGenericInstantiationLookupCompareThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_GenericInstantiationCompare);
+}
+
+// check if current thread is a thread which is performing shutdown
+inline BOOL IsShutdownSpecialThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_Shutdown);
+}
+
+inline BOOL IsThreadPoolIOCompletionSpecialThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_Threadpool_IOCompletion);
+}
+
+inline BOOL IsThreadPoolWorkerSpecialThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_Threadpool_Worker);
+}
+
+inline BOOL IsWaitSpecialThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_Wait);
+}
+
+// check if current thread is a thread which is performing shutdown
+inline BOOL IsSuspendEEThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_DynamicSuspendEE);
+}
+
+inline BOOL IsFinalizerThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_Finalizer);
+}
+
+inline BOOL IsShutdownHelperThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_ShutdownHelper);
+}
+
+inline BOOL IsProfilerAttachThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ return !!(t_ThreadType & ThreadType_ProfAPI_Attach);
+}
+
+// set special type for current thread
+void ClrFlsSetThreadType(TlsThreadTypeFlag flag);
+void ClrFlsClearThreadType(TlsThreadTypeFlag flag);
+
+#endif //!DACCESS_COMPILE
+
+HRESULT SetThreadName(HANDLE hThread, PCWSTR lpThreadDescription);
+
+inline BOOL IsGCThread ()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+#if !defined(DACCESS_COMPILE)
+ return IsGCSpecialThread () || IsSuspendEEThread ();
+#else
+ return FALSE;
+#endif
+}
+
+class ClrFlsThreadTypeSwitch
+{
+public:
+ ClrFlsThreadTypeSwitch (TlsThreadTypeFlag flag)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+#ifndef DACCESS_COMPILE
+ m_flag = flag;
+ m_fPreviouslySet = (t_ThreadType & flag);
+
+ // In debug builds, remember the full group of flags that were set at the time
+ // the constructor was called. This will be used in ASSERTs in the destructor
+ INDEBUG(m_nPreviousFlagGroup = t_ThreadType);
+
+ if (!m_fPreviouslySet)
+ {
+ ClrFlsSetThreadType(flag);
+ }
+#endif // DACCESS_COMPILE
+ }
+
+ ~ClrFlsThreadTypeSwitch ()
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+#ifndef DACCESS_COMPILE
+ // This holder should only be used to set (and thus restore) ONE thread type flag
+ // at a time. If more than that one flag was modified since this holder was
+ // instantiated, then this holder still restores only the flag it knows about. To
+ // prevent confusion, assert if some other flag was modified, so the user doesn't
+ // expect the holder to restore the entire original set of flags.
+ //
+ // The expression below says that the only difference between the previous flag
+ // group and the current flag group should be m_flag (or no difference at all, if
+ // m_flag's state didn't actually change).
+ _ASSERTE(((m_nPreviousFlagGroup ^ t_ThreadType) | (size_t) m_flag) == (size_t) m_flag);
+
+ if (m_fPreviouslySet)
+ {
+ ClrFlsSetThreadType(m_flag);
+ }
+ else
+ {
+ ClrFlsClearThreadType(m_flag);
+ }
+#endif // DACCESS_COMPILE
+ }
+
+private:
+ TlsThreadTypeFlag m_flag;
+ BOOL m_fPreviouslySet;
+ INDEBUG(size_t m_nPreviousFlagGroup);
+};
+
+//*********************************************************************************
+
+#include "contract.inl"
+
+namespace util
+{
+ // compare adapters
+ //
+
+ template < typename T >
+ struct less
+ {
+ bool operator()( T const & first, T const & second ) const
+ {
+ return first < second;
+ }
+ };
+
+ template < typename T >
+ struct greater
+ {
+ bool operator()( T const & first, T const & second ) const
+ {
+ return first > second;
+ }
+ };
+
+
+ // sort adapters
+ //
+
+ template< typename Iter, typename Pred >
+ void sort( Iter begin, Iter end, Pred pred );
+
+ template< typename T, typename Pred >
+ void sort( T * begin, T * end, Pred pred )
+ {
+ struct sort_helper : CQuickSort< T >
+ {
+ sort_helper( T * begin, T * end, Pred pred )
+ : CQuickSort< T >( begin, end - begin )
+ , m_pred( pred )
+ {}
+
+ virtual int Compare( T * first, T * second )
+ {
+ return m_pred( *first, *second ) ? -1
+ : ( m_pred( *second, *first ) ? 1 : 0 );
+ }
+
+ Pred m_pred;
+ };
+
+ sort_helper sort_obj( begin, end, pred );
+ sort_obj.Sort();
+ }
+
+
+ template < typename Iter >
+ void sort( Iter begin, Iter end );
+
+ template < typename T >
+ void sort( T * begin, T * end )
+ {
+ util::sort( begin, end, util::less< T >() );
+ }
+
+
+ // binary search adapters
+ //
+
+ template < typename Iter, typename T, typename Pred >
+ Iter lower_bound( Iter begin, Iter end, T const & val, Pred pred );
+
+ template < typename T, typename Pred >
+ T * lower_bound( T * begin, T * end, T const & val, Pred pred )
+ {
+ for (; begin != end; )
+ {
+ T * mid = begin + ( end - begin ) / 2;
+ if ( pred( *mid, val ) )
+ begin = ++mid;
+ else
+ end = mid;
+ }
+
+ return begin;
+ }
+
+
+ template < typename Iter, typename T >
+ Iter lower_bound( Iter begin, Iter end, T const & val );
+
+ template < typename T >
+ T * lower_bound( T * begin, T * end, T const & val )
+ {
+ return util::lower_bound( begin, end, val, util::less< T >() );
+ }
+}
+
+
+/* ------------------------------------------------------------------------ *
+ * Overloaded operators for the executable heap
+ * ------------------------------------------------------------------------ */
+
+#ifdef HOST_WINDOWS
+
+struct CExecutable { int x; };
+extern const CExecutable executable;
+
+void * __cdecl operator new(size_t n, const CExecutable&);
+void * __cdecl operator new[](size_t n, const CExecutable&);
+void * __cdecl operator new(size_t n, const CExecutable&, const NoThrow&);
+void * __cdecl operator new[](size_t n, const CExecutable&, const NoThrow&);
+
+
+//
+// Executable heap delete to match the executable heap new above.
+//
+template<class T> void DeleteExecutable(T *p)
+{
+ if (p != NULL)
+ {
+ p->T::~T();
+
+ HeapFree(ClrGetProcessExecutableHeap(), 0, p);
+ }
+}
+
+#endif // HOST_WINDOWS
+
+BOOL NoGuiOnAssert();
+#ifdef _DEBUG
+VOID TerminateOnAssert();
+#endif // _DEBUG
+
+
+BOOL ThreadWillCreateGuardPage(SIZE_T sizeReservedStack, SIZE_T sizeCommitedStack);
+
+FORCEINLINE void HolderSysFreeString(BSTR str) { CONTRACT_VIOLATION(ThrowsViolation); SysFreeString(str); }
+
+typedef Wrapper<BSTR, DoNothing, HolderSysFreeString> BSTRHolder;
+
+BOOL IsIPInModule(PTR_VOID pModuleBaseAddress, PCODE ip);
+
+namespace UtilCode
+{
+ // These are type-safe versions of Interlocked[Compare]Exchange
+ // They avoid invoking struct cast operations via reinterpreting
+ // the struct's address as a LONG* or LONGLONG* and dereferencing it.
+ //
+ // If we had a global ::operator & (unary), we would love to use that
+ // to ensure we were not also accidentally getting a structs's provided
+ // operator &. TODO: probe with a static_assert?
+
+ template <typename T, int SIZE = sizeof(T)>
+ struct InterlockedCompareExchangeHelper;
+
+ template <typename T>
+ struct InterlockedCompareExchangeHelper<T, sizeof(LONG)>
+ {
+ static inline T InterlockedExchange(
+ T volatile * target,
+ T value)
+ {
+ static_assert_no_msg(sizeof(T) == sizeof(LONG));
+ LONG res = ::InterlockedExchange(
+ reinterpret_cast<LONG volatile *>(target),
+ *reinterpret_cast<LONG *>(/*::operator*/&(value)));
+ return *reinterpret_cast<T*>(&res);
+ }
+
+ static inline T InterlockedCompareExchange(
+ T volatile * destination,
+ T exchange,
+ T comparand)
+ {
+ static_assert_no_msg(sizeof(T) == sizeof(LONG));
+ LONG res = ::InterlockedCompareExchange(
+ reinterpret_cast<LONG volatile *>(destination),
+ *reinterpret_cast<LONG*>(/*::operator*/&(exchange)),
+ *reinterpret_cast<LONG*>(/*::operator*/&(comparand)));
+ return *reinterpret_cast<T*>(&res);
+ }
+ };
+
+ template <typename T>
+ struct InterlockedCompareExchangeHelper<T, sizeof(LONGLONG)>
+ {
+ static inline T InterlockedExchange(
+ T volatile * target,
+ T value)
+ {
+ static_assert_no_msg(sizeof(T) == sizeof(LONGLONG));
+ LONGLONG res = ::InterlockedExchange64(
+ reinterpret_cast<LONGLONG volatile *>(target),
+ *reinterpret_cast<LONGLONG *>(/*::operator*/&(value)));
+ return *reinterpret_cast<T*>(&res);
+ }
+
+ static inline T InterlockedCompareExchange(
+ T volatile * destination,
+ T exchange,
+ T comparand)
+ {
+ static_assert_no_msg(sizeof(T) == sizeof(LONGLONG));
+ LONGLONG res = ::InterlockedCompareExchange64(
+ reinterpret_cast<LONGLONG volatile *>(destination),
+ *reinterpret_cast<LONGLONG*>(/*::operator*/&(exchange)),
+ *reinterpret_cast<LONGLONG*>(/*::operator*/&(comparand)));
+ return *reinterpret_cast<T*>(&res);
+ }
+ };
+}
+
+template <typename T>
+inline T InterlockedExchangeT(
+ T volatile * target,
+ T value)
+{
+ return ::UtilCode::InterlockedCompareExchangeHelper<T>::InterlockedExchange(
+ target, value);
+}
+
+template <typename T>
+inline T InterlockedCompareExchangeT(
+ T volatile * destination,
+ T exchange,
+ T comparand)
+{
+ return ::UtilCode::InterlockedCompareExchangeHelper<T>::InterlockedCompareExchange(
+ destination, exchange, comparand);
+}
+
+// Pointer variants for Interlocked[Compare]ExchangePointer
+// If the underlying type is a const type, we have to remove its constness
+// since Interlocked[Compare]ExchangePointer doesn't take const void * arguments.
+template <typename T>
+inline T* InterlockedExchangeT(
+ T* volatile * target,
+ T* value)
+{
+ //STATIC_ASSERT(value == 0);
+ typedef typename std::remove_const<T>::type * non_const_ptr_t;
+ return reinterpret_cast<T*>(InterlockedExchangePointer(
+ reinterpret_cast<PVOID volatile *>(const_cast<non_const_ptr_t volatile *>(target)),
+ reinterpret_cast<PVOID>(const_cast<non_const_ptr_t>(value))));
+}
+
+template <typename T>
+inline T* InterlockedCompareExchangeT(
+ T* volatile * destination,
+ T* exchange,
+ T* comparand)
+{
+ //STATIC_ASSERT(exchange == 0);
+ typedef typename std::remove_const<T>::type * non_const_ptr_t;
+ return reinterpret_cast<T*>(InterlockedCompareExchangePointer(
+ reinterpret_cast<PVOID volatile *>(const_cast<non_const_ptr_t volatile *>(destination)),
+ reinterpret_cast<PVOID>(const_cast<non_const_ptr_t>(exchange)),
+ reinterpret_cast<PVOID>(const_cast<non_const_ptr_t>(comparand))));
+}
+
+// NULL pointer variants of the above to avoid having to cast NULL
+// to the appropriate pointer type.
+template <typename T>
+inline T* InterlockedExchangeT(
+ T* volatile * target,
+ std::nullptr_t value) // When nullptr is provided as argument.
+{
+ //STATIC_ASSERT(value == 0);
+ return InterlockedExchangeT(target, static_cast<T*>(value));
+}
+
+template <typename T>
+inline T* InterlockedCompareExchangeT(
+ T* volatile * destination,
+ std::nullptr_t exchange, // When nullptr is provided as argument.
+ T* comparand)
+{
+ //STATIC_ASSERT(exchange == 0);
+ return InterlockedCompareExchangeT(destination, static_cast<T*>(exchange), comparand);
+}
+
+template <typename T>
+inline T* InterlockedCompareExchangeT(
+ T* volatile * destination,
+ T* exchange,
+ std::nullptr_t comparand) // When nullptr is provided as argument.
+{
+ //STATIC_ASSERT(comparand == 0);
+ return InterlockedCompareExchangeT(destination, exchange, static_cast<T*>(comparand));
+}
+
+// NULL pointer variants of the above to avoid having to cast NULL
+// to the appropriate pointer type.
+template <typename T>
+inline T* InterlockedExchangeT(
+ T* volatile * target,
+ int value) // When NULL is provided as argument.
+{
+ //STATIC_ASSERT(value == 0);
+ return InterlockedExchangeT(target, nullptr);
+}
+
+template <typename T>
+inline T* InterlockedCompareExchangeT(
+ T* volatile * destination,
+ int exchange, // When NULL is provided as argument.
+ T* comparand)
+{
+ //STATIC_ASSERT(exchange == 0);
+ return InterlockedCompareExchangeT(destination, nullptr, comparand);
+}
+
+template <typename T>
+inline T* InterlockedCompareExchangeT(
+ T* volatile * destination,
+ T* exchange,
+ int comparand) // When NULL is provided as argument.
+{
+ //STATIC_ASSERT(comparand == 0);
+ return InterlockedCompareExchangeT(destination, exchange, nullptr);
+}
+
+#undef InterlockedExchangePointer
+#define InterlockedExchangePointer Use_InterlockedExchangeT
+#undef InterlockedCompareExchangePointer
+#define InterlockedCompareExchangePointer Use_InterlockedCompareExchangeT
+
+// Returns the directory for clr module. So, if path was for "C:\Dir1\Dir2\Filename.DLL",
+// then this would return "C:\Dir1\Dir2\" (note the trailing backslash).
+HRESULT GetClrModuleDirectory(SString& wszPath);
+HRESULT CopySystemDirectory(const SString& pPathString, SString& pbuffer);
+
+HMODULE LoadLocalizedResourceDLLForSDK(_In_z_ LPCWSTR wzResourceDllName, _In_opt_z_ LPCWSTR modulePath=NULL, bool trySelf=true);
+// This is a slight variation that can be used for anything else
+typedef void* (__cdecl *LocalizedFileHandler)(LPCWSTR);
+void* FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler lfh, _In_opt_z_ LPCWSTR modulePath=NULL);
+
+
+namespace Clr { namespace Util
+{
+
+#ifdef HOST_WINDOWS
+namespace Com
+{
+ HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, SString & ssInprocServer32Name);
+}
+#endif // HOST_WINDOWS
+
+}}
+
+inline DWORD GetLoadWithAlteredSearchPathFlag()
+{
+ LIMITED_METHOD_CONTRACT;
+ #ifdef LOAD_WITH_ALTERED_SEARCH_PATH
+ return LOAD_WITH_ALTERED_SEARCH_PATH;
+ #else
+ return 0;
+ #endif
+}
+
+// clr::SafeAddRef and clr::SafeRelease helpers.
+namespace clr
+{
+ //=================================================================================================================
+ template <typename ItfT>
+ static inline
+ typename std::enable_if< std::is_pointer<ItfT>::value, ItfT >::type
+ SafeAddRef(ItfT pItf)
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ if (pItf != nullptr)
+ {
+ pItf->AddRef();
+ }
+ return pItf;
+ }
+
+ //=================================================================================================================
+ template <typename ItfT>
+ typename std::enable_if< std::is_pointer<ItfT>::value && std::is_reference<ItfT>::value, ULONG >::type
+ SafeRelease(ItfT pItf)
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ ULONG res = 0;
+ if (pItf != nullptr)
+ {
+ res = pItf->Release();
+ pItf = nullptr;
+ }
+ return res;
+ }
+
+ //=================================================================================================================
+ template <typename ItfT>
+ typename std::enable_if< std::is_pointer<ItfT>::value && !std::is_reference<ItfT>::value, ULONG >::type
+ SafeRelease(ItfT pItf)
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ ULONG res = 0;
+ if (pItf != nullptr)
+ {
+ res = pItf->Release();
+ }
+ return res;
+ }
+}
+
+// clr::SafeDelete
+namespace clr
+{
+ //=================================================================================================================
+ template <typename PtrT>
+ static inline
+ typename std::enable_if< std::is_pointer<PtrT>::value, PtrT >::type
+ SafeDelete(PtrT & ptr)
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ if (ptr != nullptr)
+ {
+ delete ptr;
+ ptr = nullptr;
+ }
+ }
+}
+
+// ======================================================================================
+// Spinning support (used by VM and by MetaData via file:..\Utilcode\UTSem.cpp)
+
+struct SpinConstants
+{
+ DWORD dwInitialDuration;
+ DWORD dwMaximumDuration;
+ DWORD dwBackoffFactor;
+ DWORD dwRepetitions;
+ DWORD dwMonitorSpinCount;
+};
+
+extern SpinConstants g_SpinConstants;
+
#endif // __UtilCode_h__
#endif
#if defined(__GNUC__)
-#if defined(HOST_ARM) || defined(HOST_ARM64)
+#if defined(HOST_ARMV6)
+// DMB ISH not valid on ARMv6
+#define VOLATILE_MEMORY_BARRIER() asm volatile ("mcr p15, 0, r0, c7, c10, 5" : : : "memory")
+#elif defined(HOST_ARM) || defined(HOST_ARM64)
// This is functionally equivalent to the MemoryBarrier() macro used on ARM on Windows.
#define VOLATILE_MEMORY_BARRIER() asm volatile ("dmb ish" : : : "memory")
#else
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef _WIN64UNWIND_H_
+#define _WIN64UNWIND_H_
+
+//
+// Define AMD64 exception handling structures and function prototypes.
+//
+// Define unwind operation codes.
+//
+
+typedef enum _UNWIND_OP_CODES {
+ UWOP_PUSH_NONVOL = 0,
+ UWOP_ALLOC_LARGE,
+ UWOP_ALLOC_SMALL,
+ UWOP_SET_FPREG,
+ UWOP_SAVE_NONVOL,
+ UWOP_SAVE_NONVOL_FAR,
+ UWOP_EPILOG,
+ UWOP_SPARE_CODE,
+ UWOP_SAVE_XMM128,
+ UWOP_SAVE_XMM128_FAR,
+ UWOP_PUSH_MACHFRAME,
+
+#ifdef TARGET_UNIX
+ // UWOP_SET_FPREG_LARGE is a CLR Unix-only extension to the Windows AMD64 unwind codes.
+ // It is not part of the standard Windows AMD64 unwind codes specification.
+ // UWOP_SET_FPREG allows for a maximum of a 240 byte offset between RSP and the
+ // frame pointer, when the frame pointer is established. UWOP_SET_FPREG_LARGE
+ // has a 32-bit range scaled by 16. When UWOP_SET_FPREG_LARGE is used,
+ // UNWIND_INFO.FrameRegister must be set to the frame pointer register, and
+ // UNWIND_INFO.FrameOffset must be set to 15 (its maximum value). UWOP_SET_FPREG_LARGE
+ // is followed by two UNWIND_CODEs that are combined to form a 32-bit offset (the same
+ // as UWOP_SAVE_NONVOL_FAR). This offset is then scaled by 16. The result must be less
+ // than 2^32 (that is, the top 4 bits of the unscaled 32-bit number must be zero). This
+ // result is used as the frame pointer register offset from RSP at the time the frame pointer
+ // is established. Either UWOP_SET_FPREG or UWOP_SET_FPREG_LARGE can be used, but not both.
+
+ UWOP_SET_FPREG_LARGE,
+#endif // TARGET_UNIX
+} UNWIND_OP_CODES, *PUNWIND_OP_CODES;
+
+static const UCHAR UnwindOpExtraSlotTable[] = {
+ 0, // UWOP_PUSH_NONVOL
+ 1, // UWOP_ALLOC_LARGE (or 3, special cased in lookup code)
+ 0, // UWOP_ALLOC_SMALL
+ 0, // UWOP_SET_FPREG
+ 1, // UWOP_SAVE_NONVOL
+ 2, // UWOP_SAVE_NONVOL_FAR
+ 1, // UWOP_EPILOG
+ 2, // UWOP_SPARE_CODE // previously 64-bit UWOP_SAVE_XMM_FAR
+ 1, // UWOP_SAVE_XMM128
+ 2, // UWOP_SAVE_XMM128_FAR
+ 0, // UWOP_PUSH_MACHFRAME
+
+#ifdef TARGET_UNIX
+ 2, // UWOP_SET_FPREG_LARGE
+#endif // TARGET_UNIX
+};
+
+//
+// Define unwind code structure.
+//
+
+typedef union _UNWIND_CODE {
+ struct {
+ UCHAR CodeOffset;
+ UCHAR UnwindOp : 4;
+ UCHAR OpInfo : 4;
+ };
+
+ struct {
+ UCHAR OffsetLow;
+ UCHAR UnwindOp : 4;
+ UCHAR OffsetHigh : 4;
+ } EpilogueCode;
+
+ USHORT FrameOffset;
+} UNWIND_CODE, *PUNWIND_CODE;
+
+//
+// Define unwind information flags.
+//
+
+#define UNW_FLAG_NHANDLER 0x0
+#define UNW_FLAG_EHANDLER 0x1
+#define UNW_FLAG_UHANDLER 0x2
+#define UNW_FLAG_CHAININFO 0x4
+
+#ifdef TARGET_X86
+
+typedef struct _UNWIND_INFO {
+ ULONG FunctionLength;
+} UNWIND_INFO, *PUNWIND_INFO;
+
+#else // TARGET_X86
+
+typedef struct _UNWIND_INFO {
+ UCHAR Version : 3;
+ UCHAR Flags : 5;
+ UCHAR SizeOfProlog;
+ UCHAR CountOfUnwindCodes;
+ UCHAR FrameRegister : 4;
+ UCHAR FrameOffset : 4;
+ UNWIND_CODE UnwindCode[1];
+
+//
+// The unwind codes are followed by an optional DWORD aligned field that
+// contains the exception handler address or the address of chained unwind
+// information. If an exception handler address is specified, then it is
+// followed by the language specified exception handler data.
+//
+// union {
+// ULONG ExceptionHandler;
+// ULONG FunctionEntry;
+// };
+//
+// ULONG ExceptionData[];
+//
+
+} UNWIND_INFO, *PUNWIND_INFO;
+
+#endif // TARGET_X86
+#endif // _WIN64UNWIND_H_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// WinWrap.h
+//
+// This file contains wrapper functions for Win32 API's that take strings.
+//
+// The Common Language Runtime 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.
+//
+//*****************************************************************************
+
+#ifndef __WIN_WRAP_H__
+#define __WIN_WRAP_H__
+
+//********** Macros. **********************************************************
+#if !defined(WIN32_LEAN_AND_MEAN)
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+//
+// WinCE uniformly uses cdecl calling convention on x86. __stdcall is defined as __cdecl in SDK.
+// STDCALL macro is meant to be used where we have hard dependency on __stdcall calling convention
+// - the unification with __cdecl does not apply to STDCALL.
+//
+#define STDCALL _stdcall
+
+//********** Includes. ********************************************************
+
+#include <crtwrap.h>
+#include <windows.h>
+#include <wincrypt.h>
+#include <specstrings.h>
+
+#include "registrywrapper.h"
+#include "longfilepathwrappers.h"
+
+#if defined(_PREFAST_) || defined(SOURCE_FORMATTING)
+//
+// For PREFAST we don't want the C_ASSERT to be expanded since it always
+// involves the comparison of two constants which causes PREfast warning 326
+//
+#undef C_ASSERT
+#define C_ASSERT(expr)
+#endif
+
+#include "palclr.h"
+
+#if !defined(__TODO_PORT_TO_WRAPPERS__)
+//*****************************************************************************
+// Undefine all of the windows wrappers so you can't use them.
+//*****************************************************************************
+
+// winbase.h
+#undef GetBinaryType
+#undef GetShortPathName
+#undef GetEnvironmentStrings
+#undef FreeEnvironmentStrings
+#undef FormatMessage
+#undef lstrcmp
+#undef lstrcmpi
+#undef lstrcpyn
+#undef lstrlen
+#undef CreateMutex
+#undef OpenMutex
+#undef CreateEvent
+#undef OpenEvent
+#undef CreateSemaphore
+#undef OpenSemaphore
+#undef CreateWaitableTimer
+#undef CreateFileMapping
+#undef OpenFileMapping
+#undef LoadLibrary
+#undef LoadLibraryEx
+#undef GetModuleFileName
+#undef GetModuleHandle
+#undef GetModuleHandleEx
+#undef CreateProcess
+#undef GetCommandLine
+#undef GetEnvironmentVariable
+#undef SetEnvironmentVariable
+#undef ExpandEnvironmentStrings
+#undef OutputDebugString
+#undef FindResource
+#undef FindResourceEx
+#undef BeginUpdateResource
+#undef UpdateResource
+#undef EndUpdateResource
+#undef GetPrivateProfileInt
+#undef GetSystemDirectory
+#undef GetTempPath
+#undef GetTempFileName
+#undef GetCurrentDirectory
+#undef GetFullPathName
+#undef CreateFile
+#undef GetFileAttributes
+#undef GetFileAttributesEx
+#undef DeleteFile
+#undef FindFirstFileEx
+#undef FindFirstFile
+#undef FindNextFile
+#undef CopyFile
+#undef CopyFileEx
+#undef MoveFile
+#undef MoveFileEx
+#undef CreateHardLink
+#undef CreateNamedPipe
+#undef WaitNamedPipe
+#undef LookupPrivilegeValue
+#undef GetVersionEx
+
+// winuser.h
+#undef MAKEINTRESOURCE
+#undef GetUserObjectInformation
+#undef GetMessage
+
+#undef SendMessage
+#undef CharLower
+#undef MessageBox
+#undef GetClassName
+#undef LoadString
+#undef GetCalendarInfo
+#undef GetDateFormat
+#undef GetTimeFormat
+#undef LCMapString
+
+#endif // !defined(__TODO_PORT_TO_WRAPPERS__)
+
+//
+// NT supports the wide entry points. So we redefine the wrappers right back
+// to the *W entry points as macros. This way no client code needs a wrapper on NT.
+//
+
+// winbase.h
+#define WszFormatMessage FormatMessageW
+#define Wszlstrcmp lstrcmpW
+#define Wszlstrcmpi lstrcmpiW
+#define WszCreateMutex CreateMutexW
+#define WszOpenMutex OpenMutexW
+#define WszCreateEvent CreateEventW
+#define WszOpenEvent OpenEventW
+#define WszCreateWaitableTimer CreateWaitableTimerW
+#define WszCreateFileMapping CreateFileMappingW
+#define WszOpenFileMapping OpenFileMappingW
+#define WszGetModuleHandle GetModuleHandleW
+#define WszGetModuleHandleEx GetModuleHandleExW
+#define WszGetCommandLine GetCommandLineW
+#define WszSetEnvironmentVariable SetEnvironmentVariableW
+#define WszExpandEnvironmentStrings ExpandEnvironmentStringsW
+#define WszOutputDebugString OutputDebugStringW
+#define WszFindResource FindResourceW
+#define WszFindResourceEx FindResourceExW
+#define WszBeginUpdateResource BeginUpdateResourceW
+#define WszUpdateResource UpdateResourceW
+#define WszEndUpdateResource EndUpdateResourceW
+#define WszGetPrivateProfileInt GetPrivateProfileIntW
+#define WszGetSystemDirectory GetSystemDirectoryW
+#define WszCreateNamedPipe CreateNamedPipeW
+#define WszWaitNamedPipe WaitNamedPipeW
+#define WszLookupPrivilegeValue LookupPrivilegeValueW
+
+// winuser.h
+#define WszMAKEINTRESOURCE MAKEINTRESOURCEW
+#define WszGetUserObjectInformation GetUserObjectInformationW
+#define WszGetMessage GetMessageW
+#define WszSendMessage SendMessageW
+#define WszCharLower CharLowerW
+#define WszMessageBox LateboundMessageBoxW
+#define WszGetClassName GetClassNameW
+#define WszLoadString LoadStringW
+#define WszRegOpenKeyEx ClrRegOpenKeyEx
+#define WszRegOpenKey(hKey, wszSubKey, phkRes) ClrRegOpenKeyEx(hKey, wszSubKey, 0, KEY_ALL_ACCESS, phkRes)
+#define WszRegQueryValue RegQueryValueW
+#define WszRegQueryValueEx RegQueryValueExW
+#define WszRegQueryValueExTrue RegQueryValueExW
+#define WszRegQueryStringValueEx RegQueryValueExW
+
+#define WszRegQueryInfoKey RegQueryInfoKeyW
+#define WszRegEnumValue RegEnumValueW
+#define WszRegEnumKeyEx RegEnumKeyExW
+#define WszGetCalendarInfo GetCalendarInfoW
+#define WszGetDateFormat GetDateFormatW
+#define WszGetTimeFormat GetTimeFormatW
+#define WszLCMapString LCMapStringW
+#define WszMultiByteToWideChar MultiByteToWideChar
+#define WszWideCharToMultiByte WideCharToMultiByte
+#define WszCreateSemaphore(_secattr, _count, _maxcount, _name) CreateSemaphoreExW((_secattr), (_count), (_maxcount), (_name), 0, MAXIMUM_ALLOWED | SYNCHRONIZE | SEMAPHORE_MODIFY_STATE)
+
+#ifdef FEATURE_CORESYSTEM
+
+// CoreSystem has GetFileVersionInfo{Size}Ex but not GetFileVersionInfoSize{Size}
+#undef GetFileVersionInfo
+#define GetFileVersionInfo(_filename, _handle, _len, _data) GetFileVersionInfoEx(0, (_filename), (_handle), (_len), (_data))
+#undef GetFileVersionInfoSize
+#define GetFileVersionInfoSize(_filename, _handle) GetFileVersionInfoSizeEx(0, (_filename), (_handle))
+
+#endif // FEATURE_CORESYSTEM
+
+#ifndef _T
+#define _T(str) W(str)
+#endif
+
+//File and Directory Functions which need special handling for LongFile Names
+//Note only the functions which are currently used are defined
+#define WszLoadLibrary LoadLibraryExWrapper
+#define WszLoadLibraryEx LoadLibraryExWrapper
+#define WszCreateFile CreateFileWrapper
+#define WszGetFileAttributes GetFileAttributesWrapper
+#define WszGetFileAttributesEx GetFileAttributesExWrapper
+#define WszDeleteFile DeleteFileWrapper
+#define WszFindFirstFileEx FindFirstFileExWrapper
+#define WszFindNextFile FindNextFileW
+#define WszMoveFileEx MoveFileExWrapper
+
+//Can not use extended syntax
+#define WszGetFullPathName GetFullPathNameW
+
+//Long Files will not work on these till redstone
+#define WszGetCurrentDirectory GetCurrentDirectoryWrapper
+#define WszGetTempFileName GetTempFileNameWrapper
+#define WszGetTempPath GetTempPathWrapper
+
+//APIS which have a buffer as an out parameter
+#define WszGetEnvironmentVariable GetEnvironmentVariableWrapper
+#define WszSearchPath SearchPathWrapper
+#define WszGetModuleFileName GetModuleFileNameWrapper
+
+//NOTE: IF the following API's are enabled ensure that they can work with LongFile Names
+//See the usage and implementation of above API's
+//
+//#define WszGetBinaryType GetBinaryTypeWrapper //Coresys does not seem to have this API
+
+#if HOST_UNIX
+#define WszFindFirstFile FindFirstFileW
+#else
+#define WszFindFirstFile(_lpFileName_, _lpFindData_) FindFirstFileExWrapper(_lpFileName_, FindExInfoStandard, _lpFindData_, FindExSearchNameMatch, NULL, 0)
+#endif // HOST_UNIX
+//*****************************************************************************
+// Prototypes for API's.
+//*****************************************************************************
+
+extern DWORD g_dwMaxDBCSCharByteSize;
+
+void EnsureCharSetInfoInitialized();
+
+inline DWORD GetMaxDBCSCharByteSize()
+{
+ // contract.h not visible here
+ __annotation(W("WRAPPER ") W("GetMaxDBCSCharByteSize"));
+#ifndef HOST_UNIX
+ EnsureCharSetInfoInitialized();
+
+ _ASSERTE(g_dwMaxDBCSCharByteSize != 0);
+ return (g_dwMaxDBCSCharByteSize);
+#else // HOST_UNIX
+ return 3;
+#endif // HOST_UNIX
+}
+
+#ifndef HOST_UNIX
+BOOL RunningInteractive();
+#else // !HOST_UNIX
+#define RunningInteractive() FALSE
+#endif // !HOST_UNIX
+
+#ifndef Wsz_mbstowcs
+#define Wsz_mbstowcs(szOut, szIn, iSize) WszMultiByteToWideChar(CP_ACP, 0, szIn, -1, szOut, iSize)
+#endif
+
+#ifndef Wsz_wcstombs
+#define Wsz_wcstombs(szOut, szIn, iSize) WszWideCharToMultiByte(CP_ACP, 0, szIn, -1, szOut, iSize, 0, 0)
+#endif
+
+// For all platforms:
+
+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
+ );
+
+#if defined(HOST_X86) && defined(_MSC_VER)
+
+//
+// Windows SDK does not use intrinsics on x86. Redefine the interlocked operations to use intrinsics.
+//
+
+#include "intrin.h"
+
+#define InterlockedIncrement _InterlockedIncrement
+#define InterlockedDecrement _InterlockedDecrement
+#define InterlockedExchange _InterlockedExchange
+#define InterlockedCompareExchange _InterlockedCompareExchange
+#define InterlockedExchangeAdd _InterlockedExchangeAdd
+#define InterlockedCompareExchange64 _InterlockedCompareExchange64
+#define InterlockedAnd _InterlockedAnd
+#define InterlockedOr _InterlockedOr
+
+//
+// There is no _InterlockedCompareExchangePointer intrinsic in VC++ for x86.
+// winbase.h #defines InterlockedCompareExchangePointer as __InlineInterlockedCompareExchangePointer,
+// which calls the Win32 InterlockedCompareExchange, not the intrinsic _InterlockedCompareExchange.
+// We want the intrinsic, so we #undef the Windows version of this API, and define our own.
+//
+#ifdef InterlockedCompareExchangePointer
+#undef InterlockedCompareExchangePointer
+#endif
+
+FORCEINLINE
+PVOID
+InterlockedCompareExchangePointer (
+ __inout PVOID volatile *Destination,
+ _In_opt_ PVOID ExChange,
+ _In_opt_ PVOID Comperand
+ )
+{
+ return((PVOID)(LONG_PTR)_InterlockedCompareExchange((LONG volatile *)Destination, (LONG)(LONG_PTR)ExChange, (LONG)(LONG_PTR)Comperand));
+}
+
+#endif // HOST_X86 && _MSC_VER
+
+#if defined(HOST_X86) & !defined(InterlockedIncrement64)
+
+// Interlockedxxx64 that do not have intrinsics are only supported on Windows Server 2003
+// or higher for X86 so define our own portable implementation
+
+#undef InterlockedIncrement64
+#define InterlockedIncrement64 __InterlockedIncrement64
+#undef InterlockedDecrement64
+#define InterlockedDecrement64 __InterlockedDecrement64
+#undef InterlockedExchange64
+#define InterlockedExchange64 __InterlockedExchange64
+#undef InterlockedExchangeAdd64
+#define InterlockedExchangeAdd64 __InterlockedExchangeAdd64
+
+__forceinline LONGLONG __InterlockedIncrement64(LONGLONG volatile *Addend)
+{
+ LONGLONG Old;
+
+ do {
+ Old = *Addend;
+ } while (InterlockedCompareExchange64(Addend,
+ Old + 1,
+ Old) != Old);
+
+ return Old + 1;
+}
+
+__forceinline LONGLONG __InterlockedDecrement64(LONGLONG volatile *Addend)
+{
+ LONGLONG Old;
+
+ do {
+ Old = *Addend;
+ } while (InterlockedCompareExchange64(Addend,
+ Old - 1,
+ Old) != Old);
+
+ return Old - 1;
+}
+
+__forceinline LONGLONG __InterlockedExchange64(LONGLONG volatile * Target, LONGLONG Value)
+{
+ LONGLONG Old;
+
+ do {
+ Old = *Target;
+ } while (InterlockedCompareExchange64(Target,
+ Value,
+ Old) != Old);
+
+ return Old;
+}
+
+__forceinline LONGLONG __InterlockedExchangeAdd64(LONGLONG volatile * Addend, LONGLONG Value)
+{
+ LONGLONG Old;
+
+ do {
+ Old = *Addend;
+ } while (InterlockedCompareExchange64(Addend,
+ Old + Value,
+ Old) != Old);
+
+ return Old;
+}
+
+#endif // HOST_X86
+
+// Output printf-style formatted text to the debugger if it's present or stdout otherwise.
+inline void DbgWPrintf(const LPCWSTR wszFormat, ...)
+{
+ WCHAR wszBuffer[4096];
+
+ va_list args;
+ va_start(args, wszFormat);
+
+ _vsnwprintf_s(wszBuffer, sizeof(wszBuffer) / sizeof(WCHAR), _TRUNCATE, wszFormat, args);
+
+ va_end(args);
+
+ if (IsDebuggerPresent())
+ {
+ OutputDebugStringW(wszBuffer);
+ }
+ else
+ {
+ fwprintf(stdout, W("%s"), wszBuffer);
+ fflush(stdout);
+ }
+}
+
+typedef int (*MessageBoxWFnPtr)(HWND hWnd,
+ LPCWSTR lpText,
+ LPCWSTR lpCaption,
+ UINT uType);
+
+inline int LateboundMessageBoxW(HWND hWnd,
+ LPCWSTR lpText,
+ LPCWSTR lpCaption,
+ UINT uType)
+{
+#ifndef HOST_UNIX
+ // User32 should exist on all systems where displaying a message box makes sense.
+ HMODULE hGuiExtModule = WszLoadLibrary(W("user32"));
+ if (hGuiExtModule)
+ {
+ int result = IDCANCEL;
+ MessageBoxWFnPtr fnptr = (MessageBoxWFnPtr)GetProcAddress(hGuiExtModule, "MessageBoxW");
+ if (fnptr)
+ result = fnptr(hWnd, lpText, lpCaption, uType);
+
+ FreeLibrary(hGuiExtModule);
+ return result;
+ }
+#endif // !HOST_UNIX
+
+ // No luck. Output the caption and text to the debugger if present or stdout otherwise.
+ if (lpText == NULL)
+ lpText = W("<null>");
+ if (lpCaption == NULL)
+ lpCaption = W("<null>");
+ DbgWPrintf(W("**** MessageBox invoked, title '%s' ****\n"), lpCaption);
+ DbgWPrintf(W(" %s\n"), lpText);
+ DbgWPrintf(W("********\n"));
+ DbgWPrintf(W("\n"));
+
+ // Indicate to the caller that message box was not actually displayed
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return 0;
+}
+
+inline int LateboundMessageBoxA(HWND hWnd,
+ LPCSTR lpText,
+ LPCSTR lpCaption,
+ UINT uType)
+{
+ if (lpText == NULL)
+ lpText = "<null>";
+ if (lpCaption == NULL)
+ lpCaption = "<null>";
+
+ SIZE_T cchText = strlen(lpText) + 1;
+ LPWSTR wszText = (LPWSTR)_alloca(cchText * sizeof(WCHAR));
+ swprintf_s(wszText, cchText, W("%S"), lpText);
+
+ SIZE_T cchCaption = strlen(lpCaption) + 1;
+ LPWSTR wszCaption = (LPWSTR)_alloca(cchCaption * sizeof(WCHAR));
+ swprintf_s(wszCaption, cchCaption, W("%S"), lpCaption);
+
+ return LateboundMessageBoxW(hWnd, wszText, wszCaption, uType);
+}
+
+#if defined(FEATURE_CORESYSTEM)
+
+#define MessageBoxW LateboundMessageBoxW
+#define MessageBoxA LateboundMessageBoxA
+
+#endif // FEATURE_CORESYSTEM
+
+#endif // __WIN_WRAP_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+// Undefine YieldProcessor to encourage using the normalized versions below instead. System_YieldProcessor() can be used where
+// the intention is to use the system-default implementation of YieldProcessor().
+#define HAS_SYSTEM_YIELDPROCESSOR
+FORCEINLINE void System_YieldProcessor() { YieldProcessor(); }
+#ifdef YieldProcessor
+#undef YieldProcessor
+#endif
+#define YieldProcessor Dont_Use_YieldProcessor
+
+#define DISABLE_COPY(T) \
+ T(const T &) = delete; \
+ T &operator =(const T &) = delete
+
+#define DISABLE_CONSTRUCT_COPY(T) \
+ T() = delete; \
+ DISABLE_COPY(T)
+
+class YieldProcessorNormalization
+{
+public:
+ static const unsigned int TargetNsPerNormalizedYield = 37;
+ static const unsigned int TargetMaxNsPerSpinIteration = 272;
+
+ // These are maximums for the computed values for normalization based their calculation
+ static const unsigned int MaxYieldsPerNormalizedYield = TargetNsPerNormalizedYield * 10;
+ static const unsigned int MaxOptimalMaxNormalizedYieldsPerSpinIteration =
+ TargetMaxNsPerSpinIteration * 3 / (TargetNsPerNormalizedYield * 2) + 1;
+
+private:
+ static bool s_isMeasurementScheduled;
+
+ static unsigned int s_yieldsPerNormalizedYield;
+ static unsigned int s_optimalMaxNormalizedYieldsPerSpinIteration;
+
+public:
+ static bool IsMeasurementScheduled()
+ {
+ return s_isMeasurementScheduled;
+ }
+
+ static void PerformMeasurement();
+
+private:
+ static void ScheduleMeasurementIfNecessary();
+
+public:
+ static unsigned int GetOptimalMaxNormalizedYieldsPerSpinIteration()
+ {
+ return s_optimalMaxNormalizedYieldsPerSpinIteration;
+ }
+
+ static void FireMeasurementEvents();
+
+private:
+ static double AtomicLoad(double *valueRef);
+ static void AtomicStore(double *valueRef, double value);
+
+ DISABLE_CONSTRUCT_COPY(YieldProcessorNormalization);
+
+ friend class YieldProcessorNormalizationInfo;
+ friend void YieldProcessorNormalizedForPreSkylakeCount(unsigned int);
+};
+
+class YieldProcessorNormalizationInfo
+{
+private:
+ unsigned int yieldsPerNormalizedYield;
+ unsigned int optimalMaxNormalizedYieldsPerSpinIteration;
+ unsigned int optimalMaxYieldsPerSpinIteration;
+
+public:
+ YieldProcessorNormalizationInfo()
+ : yieldsPerNormalizedYield(YieldProcessorNormalization::s_yieldsPerNormalizedYield),
+ optimalMaxNormalizedYieldsPerSpinIteration(YieldProcessorNormalization::s_optimalMaxNormalizedYieldsPerSpinIteration),
+ optimalMaxYieldsPerSpinIteration(yieldsPerNormalizedYield * optimalMaxNormalizedYieldsPerSpinIteration)
+ {
+ YieldProcessorNormalization::ScheduleMeasurementIfNecessary();
+ }
+
+ DISABLE_COPY(YieldProcessorNormalizationInfo);
+
+ friend void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &);
+ friend void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &, unsigned int);
+ friend void YieldProcessorNormalizedForPreSkylakeCount(const YieldProcessorNormalizationInfo &, unsigned int);
+ friend void YieldProcessorWithBackOffNormalized(const YieldProcessorNormalizationInfo &, unsigned int);
+};
+
+// See YieldProcessorNormalized() for preliminary info. Typical usage:
+// if (!condition)
+// {
+// YieldProcessorNormalizationInfo normalizationInfo;
+// do
+// {
+// YieldProcessorNormalized(normalizationInfo);
+// } while (!condition);
+// }
+FORCEINLINE void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &normalizationInfo)
+{
+ unsigned int n = normalizationInfo.yieldsPerNormalizedYield;
+ _ASSERTE(n != 0);
+ do
+ {
+ System_YieldProcessor();
+ } while (--n != 0);
+}
+
+// Delays execution of the current thread for a short duration. Unlike YieldProcessor(), an effort is made to normalize the
+// delay across processors. The actual delay may be meaningful in several ways, including but not limited to the following:
+// - The delay should be long enough that a tiny spin-wait like the following has a decent likelihood of observing a new value
+// for the condition (when changed by a different thread) on each iteration, otherwise it may unnecessary increase CPU usage
+// and decrease scalability of the operation.
+// while(!condition)
+// {
+// YieldProcessorNormalized();
+// }
+// - The delay should be short enough that a tiny spin-wait like above would not miss multiple cross-thread changes to the
+// condition, otherwise it may unnecessarily increase latency of the operation
+// - In reasonably short spin-waits, the actual delay may not matter much. In unreasonably long spin-waits that progress in
+// yield count per iteration for each failed check of the condition, the progression can significantly magnify the second
+// issue above on later iterations.
+// - This function and variants are intended to provide a decent balance between the above issues, as ideal solutions to each
+// issue have trade-offs between them. If latency of the operation is far more important in the scenario, consider using
+// System_YieldProcessor() instead, which would issue a delay that is typically <= the delay issued by this method.
+FORCEINLINE void YieldProcessorNormalized()
+{
+ YieldProcessorNormalized(YieldProcessorNormalizationInfo());
+}
+
+// See YieldProcessorNormalized(count) for preliminary info. Typical usage:
+// if (!moreExpensiveCondition)
+// {
+// YieldProcessorNormalizationInfo normalizationInfo;
+// do
+// {
+// YieldProcessorNormalized(normalizationInfo, 2);
+// } while (!moreExpensiveCondition);
+// }
+FORCEINLINE void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &normalizationInfo, unsigned int count)
+{
+ _ASSERTE(count != 0);
+
+ if (sizeof(SIZE_T) <= sizeof(unsigned int))
+ {
+ // On platforms with a small SIZE_T, prevent overflow on the multiply below
+ const unsigned int MaxCount = UINT_MAX / YieldProcessorNormalization::MaxYieldsPerNormalizedYield;
+ if (count > MaxCount)
+ {
+ count = MaxCount;
+ }
+ }
+
+ SIZE_T n = (SIZE_T)count * normalizationInfo.yieldsPerNormalizedYield;
+ _ASSERTE(n != 0);
+ do
+ {
+ System_YieldProcessor();
+ } while (--n != 0);
+}
+
+// See YieldProcessorNormalized() for preliminary info. This function repeats the delay 'count' times. This overload is
+// preferred over the single-count overload when multiple yields are desired per spin-wait iteration. Typical usage:
+// while(!moreExpensiveCondition)
+// {
+// YieldProcessorNormalized(2);
+// }
+FORCEINLINE void YieldProcessorNormalized(unsigned int count)
+{
+ YieldProcessorNormalized(YieldProcessorNormalizationInfo(), count);
+}
+
+// Please DO NOT use this function in new code! See YieldProcessorNormalizedForPreSkylakeCount(preSkylakeCount) for preliminary
+// info. Typical usage:
+// if (!condition)
+// {
+// YieldProcessorNormalizationInfo normalizationInfo;
+// do
+// {
+// YieldProcessorNormalizedForPreSkylakeCount(normalizationInfo, 100);
+// } while (!condition);
+// }
+FORCEINLINE void YieldProcessorNormalizedForPreSkylakeCount(
+ const YieldProcessorNormalizationInfo &normalizationInfo,
+ unsigned int preSkylakeCount)
+{
+ _ASSERTE(preSkylakeCount != 0);
+
+ if (sizeof(SIZE_T) <= sizeof(unsigned int))
+ {
+ // On platforms with a small SIZE_T, prevent overflow on the multiply below
+ const unsigned int MaxCount = UINT_MAX / YieldProcessorNormalization::MaxYieldsPerNormalizedYield;
+ if (preSkylakeCount > MaxCount)
+ {
+ preSkylakeCount = MaxCount;
+ }
+ }
+
+ const unsigned int PreSkylakeCountToSkylakeCountDivisor = 8;
+ SIZE_T n = (SIZE_T)preSkylakeCount * normalizationInfo.yieldsPerNormalizedYield / PreSkylakeCountToSkylakeCountDivisor;
+ if (n == 0)
+ {
+ n = 1;
+ }
+ do
+ {
+ System_YieldProcessor();
+ } while (--n != 0);
+}
+
+// Please DO NOT use this function in new code! This function is to be used for old spin-wait loops that have not been retuned
+// for recent processors, and especially where the yield count may be unreasonably high. The function scales the yield count in
+// an attempt to normalize the total delay across processors, to approximately the total delay that would be issued on a
+// pre-Skylake processor. New code should be tuned with YieldProcessorNormalized() or variants instead. Typical usage:
+// while(!condition)
+// {
+// YieldProcessorNormalizedForPreSkylakeCount(100);
+// }
+FORCEINLINE void YieldProcessorNormalizedForPreSkylakeCount(unsigned int preSkylakeCount)
+{
+ // This function does not forward to the one above because it is used by some code under utilcode, where
+ // YieldProcessorNormalizationInfo cannot be used since normalization does not happen in some of its consumers. So this
+ // version uses the fields in YieldProcessorNormalization directly.
+
+ _ASSERTE(preSkylakeCount != 0);
+
+ if (sizeof(SIZE_T) <= sizeof(unsigned int))
+ {
+ // On platforms with a small SIZE_T, prevent overflow on the multiply below
+ const unsigned int MaxCount = UINT_MAX / YieldProcessorNormalization::MaxYieldsPerNormalizedYield;
+ if (preSkylakeCount > MaxCount)
+ {
+ preSkylakeCount = MaxCount;
+ }
+ }
+
+ const unsigned int PreSkylakeCountToSkylakeCountDivisor = 8;
+ SIZE_T n =
+ (SIZE_T)preSkylakeCount *
+ YieldProcessorNormalization::s_yieldsPerNormalizedYield /
+ PreSkylakeCountToSkylakeCountDivisor;
+ if (n == 0)
+ {
+ n = 1;
+ }
+ do
+ {
+ System_YieldProcessor();
+ } while (--n != 0);
+}
+
+// See YieldProcessorNormalized() for preliminary info. This function is to be used when there is a decent possibility that the
+// condition would not be satisfied within a short duration. The current implementation increases the delay per spin-wait
+// iteration exponentially up to a limit. Typical usage:
+// if (!conditionThatMayNotBeSatisfiedSoon)
+// {
+// YieldProcessorNormalizationInfo normalizationInfo;
+// do
+// {
+// YieldProcessorWithBackOffNormalized(normalizationInfo); // maybe Sleep(0) occasionally
+// } while (!conditionThatMayNotBeSatisfiedSoon);
+// }
+FORCEINLINE void YieldProcessorWithBackOffNormalized(
+ const YieldProcessorNormalizationInfo &normalizationInfo,
+ unsigned int spinIteration)
+{
+ // This shift value should be adjusted based on the asserted conditions below
+ const UINT8 MaxShift = 3;
+ static_assert_no_msg(
+ ((unsigned int)1 << MaxShift) <= YieldProcessorNormalization::MaxOptimalMaxNormalizedYieldsPerSpinIteration);
+ static_assert_no_msg(
+ ((unsigned int)1 << (MaxShift + 1)) > YieldProcessorNormalization::MaxOptimalMaxNormalizedYieldsPerSpinIteration);
+
+ unsigned int n;
+ if (spinIteration <= MaxShift &&
+ ((unsigned int)1 << spinIteration) < normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration)
+ {
+ n = ((unsigned int)1 << spinIteration) * normalizationInfo.yieldsPerNormalizedYield;
+ }
+ else
+ {
+ n = normalizationInfo.optimalMaxYieldsPerSpinIteration;
+ }
+ _ASSERTE(n != 0);
+ do
+ {
+ System_YieldProcessor();
+ } while (--n != 0);
+}
+
+#undef DISABLE_CONSTRUCT_COPY
+#undef DISABLE_COPY
add_compile_options(-fexceptions)
add_definitions(-DUSE_STL)
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
add_subdirectory(src)
#define PAL_INITIALIZE_SYNC_THREAD 0x01
#define PAL_INITIALIZE_EXEC_ALLOCATOR 0x02
#define PAL_INITIALIZE_STD_HANDLES 0x04
-#define PAL_INITIALIZE_REGISTER_SIGTERM_HANDLER 0x08
-#define PAL_INITIALIZE_DEBUGGER_EXCEPTIONS 0x10
-#define PAL_INITIALIZE_ENSURE_STACK_SIZE 0x20
// PAL_Initialize() flags
-#define PAL_INITIALIZE (PAL_INITIALIZE_SYNC_THREAD | PAL_INITIALIZE_STD_HANDLES)
+#define PAL_INITIALIZE (PAL_INITIALIZE_SYNC_THREAD | \
+ PAL_INITIALIZE_STD_HANDLES)
-// PAL_InitializeDLL() flags - don't start any of the helper threads
-#define PAL_INITIALIZE_DLL PAL_INITIALIZE_NONE
-
-// PAL_InitializeCoreCLR() flags
-#define PAL_INITIALIZE_CORECLR (PAL_INITIALIZE | PAL_INITIALIZE_EXEC_ALLOCATOR | PAL_INITIALIZE_REGISTER_SIGTERM_HANDLER | PAL_INITIALIZE_DEBUGGER_EXCEPTIONS | PAL_INITIALIZE_ENSURE_STACK_SIZE)
+// PAL_InitializeDLL() flags - don't start any of the helper threads or register any exceptions
+#define PAL_INITIALIZE_DLL PAL_INITIALIZE_NONE
typedef DWORD (PALAPI_NOEXPORT *PTHREAD_START_ROUTINE)(LPVOID lpThreadParameter);
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
/******************* PAL-Specific Entrypoints *****************************/
+PALIMPORT
+void
+PALAPI
+PAL_InitializeWithFlags(
+ DWORD flags);
+
PALIMPORT
int
PALAPI
PAL_InitializeDLL();
+typedef VOID (*PPAL_STARTUP_CALLBACK)(
+ char *modulePath,
+ HMODULE hModule,
+ PVOID parameter);
+
+PALIMPORT
+DWORD
+PALAPI
+PAL_RegisterForRuntimeStartup(
+ IN DWORD dwProcessId,
+ IN LPCWSTR lpApplicationGroupId,
+ IN PPAL_STARTUP_CALLBACK pfnCallback,
+ IN PVOID parameter,
+ OUT PVOID *ppUnregisterToken);
+
+PALIMPORT
+DWORD
+PALAPI
+PAL_UnregisterForRuntimeStartup(
+ IN PVOID pUnregisterToken);
+
+static const unsigned int MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH = MAX_PATH;
+PALIMPORT
+VOID
+PALAPI
+PAL_GetTransportName(
+ const unsigned int MAX_TRANSPORT_NAME_LENGTH,
+ OUT char *name,
+ IN const char *prefix,
+ IN DWORD id,
+ IN const char *applicationGroupId,
+ IN const char *suffix);
+PALIMPORT
+VOID
+PALAPI
+PAL_GetTransportPipeName(
+ OUT char *name,
+ IN DWORD id,
+ IN const char *applicationGroupId,
+ IN const char *suffix);
+
PALIMPORT
HINSTANCE
PALAPI
GetCurrentSessionId();
+PALIMPORT
+HANDLE
+PALAPI
+GetCurrentProcess();
PALIMPORT
DWORD
DWORD dwProcessId;
DWORD dwThreadId_PAL_Undefined;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
+PALIMPORT
+BOOL
+PALAPI
+CreateProcessW(
+ IN LPCWSTR lpApplicationName,
+ IN LPWSTR lpCommandLine,
+ IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ IN BOOL bInheritHandles,
+ IN DWORD dwCreationFlags,
+ IN LPVOID lpEnvironment,
+ IN LPCWSTR lpCurrentDirectory,
+ IN LPSTARTUPINFOW lpStartupInfo,
+ OUT LPPROCESS_INFORMATION lpProcessInformation);
+
+#define CreateProcess CreateProcessW
+PALIMPORT
+BOOL
+PALAPI
+TerminateProcess(
+ IN HANDLE hProcess,
+ IN UINT uExitCode);
+
+#define MAXIMUM_WAIT_OBJECTS 64
+#define WAIT_OBJECT_0 0
+#define WAIT_ABANDONED 0x00000080
+#define WAIT_ABANDONED_0 0x00000080
+#define WAIT_TIMEOUT 258
+#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
+
+#define INFINITE 0xFFFFFFFF // Infinite timeout
+
+PALIMPORT
+DWORD
+PALAPI
+WaitForSingleObject(
+ IN HANDLE hHandle,
+ IN DWORD dwMilliseconds);
+
+#define DEBUG_PROCESS 0x00000001
+#define DEBUG_ONLY_THIS_PROCESS 0x00000002
+#define CREATE_SUSPENDED 0x00000004
+#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000
+PALIMPORT
+DWORD
+PALAPI
+ResumeThread(
+ IN HANDLE hThread);
+
#ifdef HOST_X86
#define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \
0xFFF)
+PALIMPORT
+HANDLE
+PALAPI
+OpenProcess(
+ IN DWORD dwDesiredAccess, /* PROCESS_DUP_HANDLE or PROCESS_ALL_ACCESS */
+ IN BOOL bInheritHandle,
+ IN DWORD dwProcessId
+ );
+
PALIMPORT
BOOL
PALAPI
#define wcspbrk PAL_wcspbrk
#define wcscmp PAL_wcscmp
#define wcsncpy PAL_wcsncpy
-#define wcstok PAL_wcstok
#define wcscspn PAL_wcscspn
#define iswprint PAL_iswprint
#define realloc PAL_realloc
PALIMPORT DLLEXPORT const WCHAR * __cdecl PAL_wcsrchr(const WCHAR *, WCHAR);
PALIMPORT WCHAR _WConst_return * __cdecl PAL_wcspbrk(const WCHAR *, const WCHAR *);
PALIMPORT DLLEXPORT WCHAR _WConst_return * __cdecl PAL_wcsstr(const WCHAR *, const WCHAR *);
-PALIMPORT WCHAR * __cdecl PAL_wcstok(WCHAR *, const WCHAR *);
PALIMPORT DLLEXPORT size_t __cdecl PAL_wcscspn(const WCHAR *, const WCHAR *);
PALIMPORT int __cdecl PAL_swprintf(WCHAR *, const WCHAR *, ...);
PALIMPORT int __cdecl PAL_vswprintf(WCHAR *, const WCHAR *, va_list);
#include "unixasmmacrosamd64.inc"
#elif defined(HOST_ARM)
#include "unixasmmacrosarm.inc"
+#elif defined(HOST_ARMV6)
+#include "unixasmmacrosarm.inc"
#elif defined(HOST_ARM64)
#include "unixasmmacrosarm64.inc"
#elif defined(HOST_S390X)
.endm
.macro EMIT_BREAKPOINT
+#ifdef __armv6__
+ .inst 0xe7f001f0
+#else
.inst.w 0xde01
+#endif
.endm
.macro PROLOG_PUSH RegList
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#define BuildNumberMajor 30319
-#define BuildNumberMinor 0
-#define BuildNumberMajor_A "30319"
-#define BuildNumberMinor_A "00"
-#define BuildNumbers_A "30319.00"
-#define BuildNumbers_T TEXT("30319.00")
-
-#define NDPBuildNumberMajor 30319
-#define NDPBuildNumberMinor 0
-#define NDPBuildNumbers_A "30319.00"
-#define NDPBuildNumbers_T TEXT("30319.00")
-
-#define NDPFileVersionMinor 5
-#define NDPFileVersionBuild 30319
-#define NDPFileVersionRevision 0
-
-#define NDPFileVersionMinor_A "5"
-#define NDPFileVersionBuild_A "30319"
-#define NDPFileVersionRevision_A "00"
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-
-
-/* this ALWAYS GENERATED file contains the definitions for the interfaces */
-
-
- /* File created by MIDL compiler version 8.00.0603 */
-/* @@MIDL_FILE_HEADING( ) */
-
-#pragma warning( disable: 4049 ) /* more than 64k source lines */
-
-
-/* verify that the <rpcndr.h> version is high enough to compile this file*/
-#ifndef __REQUIRED_RPCNDR_H_VERSION__
-#define __REQUIRED_RPCNDR_H_VERSION__ 475
-#endif
-
-#include "rpc.h"
-#include "rpcndr.h"
-
-#ifndef __RPCNDR_H_VERSION__
-#error this stub requires an updated version of <rpcndr.h>
-#endif /* __RPCNDR_H_VERSION__ */
-
-#ifndef COM_NO_WINDOWS_H
-#include "windows.h"
-#include "ole2.h"
-#endif /*COM_NO_WINDOWS_H*/
-
-#ifndef __clrprivbinding_h__
-#define __clrprivbinding_h__
-
-#if defined(_MSC_VER) && (_MSC_VER >= 1020)
-#pragma once
-#endif
-
-/* Forward Declarations */
-
-#ifndef __ICLRPrivBinder_FWD_DEFINED__
-#define __ICLRPrivBinder_FWD_DEFINED__
-typedef interface ICLRPrivBinder ICLRPrivBinder;
-
-#endif /* __ICLRPrivBinder_FWD_DEFINED__ */
-
-
-#ifndef __ICLRPrivAssembly_FWD_DEFINED__
-#define __ICLRPrivAssembly_FWD_DEFINED__
-typedef interface ICLRPrivAssembly ICLRPrivAssembly;
-
-#endif /* __ICLRPrivAssembly_FWD_DEFINED__ */
-
-
-/* header files for imported files */
-#include "unknwn.h"
-#include "objidl.h"
-
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-
-/* interface __MIDL_itf_clrprivbinding_0000_0000 */
-/* [local] */
-
-
-
-
-
-extern RPC_IF_HANDLE __MIDL_itf_clrprivbinding_0000_0000_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_clrprivbinding_0000_0000_v0_0_s_ifspec;
-
-#ifndef __ICLRPrivBinder_INTERFACE_DEFINED__
-#define __ICLRPrivBinder_INTERFACE_DEFINED__
-
-/* interface ICLRPrivBinder */
-/* [object][local][version][uuid] */
-
-
-EXTERN_C const IID IID_ICLRPrivBinder;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("2601F621-E462-404C-B299-3E1DE72F8542")
- ICLRPrivBinder : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE BindAssemblyByName(
- /* [in] */ struct AssemblyNameData *pAssemblyNameData,
- /* [retval][out] */ ICLRPrivAssembly **ppAssembly) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetBinderID(
- /* [retval][out] */ UINT_PTR *pBinderId) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetLoaderAllocator(
- /* [retval][out] */ LPVOID *pLoaderAllocator) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICLRPrivBinderVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICLRPrivBinder * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICLRPrivBinder * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICLRPrivBinder * This);
-
- HRESULT ( STDMETHODCALLTYPE *BindAssemblyByName )(
- ICLRPrivBinder * This,
- /* [in] */ struct AssemblyNameData *pAssemblyNameData,
- /* [retval][out] */ ICLRPrivAssembly **ppAssembly);
-
- HRESULT ( STDMETHODCALLTYPE *GetBinderID )(
- ICLRPrivBinder * This,
- /* [retval][out] */ UINT_PTR *pBinderId);
-
- HRESULT ( STDMETHODCALLTYPE *GetLoaderAllocator )(
- ICLRPrivBinder * This,
- /* [retval][out] */ LPVOID *pLoaderAllocator);
-
- END_INTERFACE
- } ICLRPrivBinderVtbl;
-
- interface ICLRPrivBinder
- {
- CONST_VTBL struct ICLRPrivBinderVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICLRPrivBinder_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICLRPrivBinder_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICLRPrivBinder_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICLRPrivBinder_BindAssemblyByName(This,pAssemblyNameData,ppAssembly) \
- ( (This)->lpVtbl -> BindAssemblyByName(This,pAssemblyNameData,ppAssembly) )
-
-#define ICLRPrivBinder_GetBinderID(This,pBinderId) \
- ( (This)->lpVtbl -> GetBinderID(This,pBinderId) )
-
-#define ICLRPrivBinder_GetLoaderAllocator(This,pLoaderAllocator) \
- ( (This)->lpVtbl -> GetLoaderAllocator(This,pLoaderAllocator) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICLRPrivBinder_INTERFACE_DEFINED__ */
-
-
-/* interface __MIDL_itf_clrprivbinding_0000_0001 */
-/* [local] */
-
-
-enum ASSEMBLY_IMAGE_TYPES
- {
- ASSEMBLY_IMAGE_TYPE_IL = 0x1,
- ASSEMBLY_IMAGE_TYPE_NATIVE = 0x2,
- ASSEMBLY_IMAGE_TYPE_DEFAULT = 0x3,
- ASSEMBLY_IMAGE_TYPE_ASSEMBLY = 0x4
- } ;
-
-
-extern RPC_IF_HANDLE __MIDL_itf_clrprivbinding_0000_0001_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_clrprivbinding_0000_0001_v0_0_s_ifspec;
-
-#ifndef __ICLRPrivAssembly_INTERFACE_DEFINED__
-#define __ICLRPrivAssembly_INTERFACE_DEFINED__
-
-/* interface ICLRPrivAssembly */
-/* [object][local][version][uuid] */
-
-
-EXTERN_C const IID IID_ICLRPrivAssembly;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("2601F621-E462-404C-B299-3E1DE72F8543")
- ICLRPrivAssembly : public ICLRPrivBinder
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetAvailableImageTypes(
- /* [retval][out] */ LPDWORD pdwImageTypes) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICLRPrivAssemblyVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICLRPrivAssembly * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICLRPrivAssembly * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICLRPrivAssembly * This);
-
- HRESULT ( STDMETHODCALLTYPE *BindAssemblyByName )(
- ICLRPrivAssembly * This,
- /* [in] */ struct AssemblyNameData *pAssemblyNameData,
- /* [retval][out] */ ICLRPrivAssembly **ppAssembly);
-
- HRESULT ( STDMETHODCALLTYPE *GetBinderID )(
- ICLRPrivAssembly * This,
- /* [retval][out] */ UINT_PTR *pBinderId);
-
- HRESULT ( STDMETHODCALLTYPE *GetLoaderAllocator )(
- ICLRPrivAssembly * This,
- /* [retval][out] */ LPVOID *pLoaderAllocator);
-
- HRESULT ( STDMETHODCALLTYPE *GetAvailableImageTypes )(
- ICLRPrivAssembly * This,
- /* [retval][out] */ LPDWORD pdwImageTypes);
-
- END_INTERFACE
- } ICLRPrivAssemblyVtbl;
-
- interface ICLRPrivAssembly
- {
- CONST_VTBL struct ICLRPrivAssemblyVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICLRPrivAssembly_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICLRPrivAssembly_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICLRPrivAssembly_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICLRPrivAssembly_BindAssemblyByName(This,pAssemblyNameData,ppAssembly) \
- ( (This)->lpVtbl -> BindAssemblyByName(This,pAssemblyNameData,ppAssembly) )
-
-#define ICLRPrivAssembly_GetBinderID(This,pBinderId) \
- ( (This)->lpVtbl -> GetBinderID(This,pBinderId) )
-
-#define ICLRPrivAssembly_GetLoaderAllocator(This,pLoaderAllocator) \
- ( (This)->lpVtbl -> GetLoaderAllocator(This,pLoaderAllocator) )
-
-
-#define ICLRPrivAssembly_GetAvailableImageTypes(This,pdwImageTypes) \
- ( (This)->lpVtbl -> GetAvailableImageTypes(This,pdwImageTypes) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICLRPrivAssembly_INTERFACE_DEFINED__ */
-
-
-/* Additional Prototypes for ALL interfaces */
-
-/* end of Additional Prototypes */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
-
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-
-
-/* this ALWAYS GENERATED file contains the definitions for the interfaces */
-
-
- /* File created by MIDL compiler version 8.00.0603 */
-/* @@MIDL_FILE_HEADING( ) */
-
-#pragma warning( disable: 4049 ) /* more than 64k source lines */
-
-
-/* verify that the <rpcndr.h> version is high enough to compile this file*/
-#ifndef __REQUIRED_RPCNDR_H_VERSION__
-#define __REQUIRED_RPCNDR_H_VERSION__ 475
-#endif
-
-#include "rpc.h"
-#include "rpcndr.h"
-
-#ifndef __RPCNDR_H_VERSION__
-#error this stub requires an updated version of <rpcndr.h>
-#endif // __RPCNDR_H_VERSION__
-
-#ifndef COM_NO_WINDOWS_H
-#include "windows.h"
-#include "ole2.h"
-#endif /*COM_NO_WINDOWS_H*/
-
-#ifndef __mscorsvc_h__
-#define __mscorsvc_h__
-
-#if defined(_MSC_VER) && (_MSC_VER >= 1020)
-#pragma once
-#endif
-
-/* Forward Declarations */
-
-#ifndef __ICorSvcDependencies_FWD_DEFINED__
-#define __ICorSvcDependencies_FWD_DEFINED__
-typedef interface ICorSvcDependencies ICorSvcDependencies;
-
-#endif /* __ICorSvcDependencies_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcWorker_FWD_DEFINED__
-#define __ICorSvcWorker_FWD_DEFINED__
-typedef interface ICorSvcWorker ICorSvcWorker;
-
-#endif /* __ICorSvcWorker_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcWorker2_FWD_DEFINED__
-#define __ICorSvcWorker2_FWD_DEFINED__
-typedef interface ICorSvcWorker2 ICorSvcWorker2;
-
-#endif /* __ICorSvcWorker2_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcWorker3_FWD_DEFINED__
-#define __ICorSvcWorker3_FWD_DEFINED__
-typedef interface ICorSvcWorker3 ICorSvcWorker3;
-
-#endif /* __ICorSvcWorker3_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcSetPrivateAttributes_FWD_DEFINED__
-#define __ICorSvcSetPrivateAttributes_FWD_DEFINED__
-typedef interface ICorSvcSetPrivateAttributes ICorSvcSetPrivateAttributes;
-
-#endif /* __ICorSvcSetPrivateAttributes_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcRepository_FWD_DEFINED__
-#define __ICorSvcRepository_FWD_DEFINED__
-typedef interface ICorSvcRepository ICorSvcRepository;
-
-#endif /* __ICorSvcRepository_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcAppX_FWD_DEFINED__
-#define __ICorSvcAppX_FWD_DEFINED__
-typedef interface ICorSvcAppX ICorSvcAppX;
-
-#endif /* __ICorSvcAppX_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcLogger_FWD_DEFINED__
-#define __ICorSvcLogger_FWD_DEFINED__
-typedef interface ICorSvcLogger ICorSvcLogger;
-
-#endif /* __ICorSvcLogger_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcPooledWorker_FWD_DEFINED__
-#define __ICorSvcPooledWorker_FWD_DEFINED__
-typedef interface ICorSvcPooledWorker ICorSvcPooledWorker;
-
-#endif /* __ICorSvcPooledWorker_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcBindToWorker_FWD_DEFINED__
-#define __ICorSvcBindToWorker_FWD_DEFINED__
-typedef interface ICorSvcBindToWorker ICorSvcBindToWorker;
-
-#endif /* __ICorSvcBindToWorker_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvc_FWD_DEFINED__
-#define __ICorSvc_FWD_DEFINED__
-typedef interface ICorSvc ICorSvc;
-
-#endif /* __ICorSvc_FWD_DEFINED__ */
-
-
-#ifndef __ICompileProgressNotification_FWD_DEFINED__
-#define __ICompileProgressNotification_FWD_DEFINED__
-typedef interface ICompileProgressNotification ICompileProgressNotification;
-
-#endif /* __ICompileProgressNotification_FWD_DEFINED__ */
-
-
-#ifndef __ICompileProgressNotification2_FWD_DEFINED__
-#define __ICompileProgressNotification2_FWD_DEFINED__
-typedef interface ICompileProgressNotification2 ICompileProgressNotification2;
-
-#endif /* __ICompileProgressNotification2_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcInstaller_FWD_DEFINED__
-#define __ICorSvcInstaller_FWD_DEFINED__
-typedef interface ICorSvcInstaller ICorSvcInstaller;
-
-#endif /* __ICorSvcInstaller_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcAdvancedInstaller_FWD_DEFINED__
-#define __ICorSvcAdvancedInstaller_FWD_DEFINED__
-typedef interface ICorSvcAdvancedInstaller ICorSvcAdvancedInstaller;
-
-#endif /* __ICorSvcAdvancedInstaller_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcOptimizer_FWD_DEFINED__
-#define __ICorSvcOptimizer_FWD_DEFINED__
-typedef interface ICorSvcOptimizer ICorSvcOptimizer;
-
-#endif /* __ICorSvcOptimizer_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcOptimizer2_FWD_DEFINED__
-#define __ICorSvcOptimizer2_FWD_DEFINED__
-typedef interface ICorSvcOptimizer2 ICorSvcOptimizer2;
-
-#endif /* __ICorSvcOptimizer2_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcOptimizer3_FWD_DEFINED__
-#define __ICorSvcOptimizer3_FWD_DEFINED__
-typedef interface ICorSvcOptimizer3 ICorSvcOptimizer3;
-
-#endif /* __ICorSvcOptimizer3_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcManager_FWD_DEFINED__
-#define __ICorSvcManager_FWD_DEFINED__
-typedef interface ICorSvcManager ICorSvcManager;
-
-#endif /* __ICorSvcManager_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcManager2_FWD_DEFINED__
-#define __ICorSvcManager2_FWD_DEFINED__
-typedef interface ICorSvcManager2 ICorSvcManager2;
-
-#endif /* __ICorSvcManager2_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcSetLegacyServiceBehavior_FWD_DEFINED__
-#define __ICorSvcSetLegacyServiceBehavior_FWD_DEFINED__
-typedef interface ICorSvcSetLegacyServiceBehavior ICorSvcSetLegacyServiceBehavior;
-
-#endif /* __ICorSvcSetLegacyServiceBehavior_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcSetTaskBootTriggerState_FWD_DEFINED__
-#define __ICorSvcSetTaskBootTriggerState_FWD_DEFINED__
-typedef interface ICorSvcSetTaskBootTriggerState ICorSvcSetTaskBootTriggerState;
-
-#endif /* __ICorSvcSetTaskBootTriggerState_FWD_DEFINED__ */
-
-
-#ifndef __ICorSvcSetTaskDelayStartTriggerState_FWD_DEFINED__
-#define __ICorSvcSetTaskDelayStartTriggerState_FWD_DEFINED__
-typedef interface ICorSvcSetTaskDelayStartTriggerState ICorSvcSetTaskDelayStartTriggerState;
-
-#endif /* __ICorSvcSetTaskDelayStartTriggerState_FWD_DEFINED__ */
-
-
-/* header files for imported files */
-#include "unknwn.h"
-
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-
-/* interface __MIDL_itf_mscorsvc_0000_0000 */
-/* [local] */
-
-#if 0
-#endif
-EXTERN_GUID(CLSID_CorSvcWorker, 0x8ed1a844, 0x32a7, 0x4a67, 0xba, 0x62, 0xf8, 0xd5, 0xaf, 0xdf, 0xf4, 0x60);
-EXTERN_GUID(CLSID_CorSvcBindToWorker, 0x9f74fb09, 0x4221, 0x40b4, 0xae, 0x21, 0xae, 0xb6, 0xdf, 0xf2, 0x99, 0x4e);
-STDAPI CorGetSvc(IUnknown **pIUnknown);
-
-
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0000_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0000_v0_0_s_ifspec;
-
-
-#ifndef __mscorsvc_LIBRARY_DEFINED__
-#define __mscorsvc_LIBRARY_DEFINED__
-
-/* library mscorsvc */
-/* [helpstring][version][uuid] */
-
-typedef /* [public][public][public][public][public][public][public][public] */
-enum __MIDL___MIDL_itf_mscorsvc_0001_0004_0001
- {
- ScenarioDefault = 0,
- ScenarioAll = 0x1,
- ScenarioDebug = 0x2,
- ScenarioProfile = 0x8,
- ScenarioTuningDataCollection = 0x10,
- ScenarioLegacy = 0x20,
- ScenarioNgenLastRetry = 0x10000,
- ScenarioAutoNGen = 0x100000,
- ScenarioRepositoryOnly = 0x200000
- } OptimizationScenario;
-
-typedef /* [public] */
-enum __MIDL___MIDL_itf_mscorsvc_0001_0004_0002
- {
- ScenarioEmitFixups = 0x10000,
- ScenarioProfileInfo = 0x20000
- } PrivateOptimizationScenario;
-
-typedef struct _SvcWorkerPriority
- {
- DWORD dwPriorityClass;
- } SvcWorkerPriority;
-
-typedef /* [public] */
-enum __MIDL___MIDL_itf_mscorsvc_0001_0007_0001
- {
- DbgTypePdb = 0x1
- } NGenPrivateAttributesFlags;
-
-typedef struct _NGenPrivateAttributes
- {
- DWORD Flags;
- DWORD ZapStats;
- BSTR DbgDir;
- } NGenPrivateAttributes;
-
-typedef /* [public][public] */
-enum __MIDL___MIDL_itf_mscorsvc_0001_0008_0001
- {
- RepositoryDefault = 0,
- MoveFromRepository = 0x1,
- CopyToRepository = 0x2,
- IgnoreRepository = 0x4
- } RepositoryFlags;
-
-typedef
-enum CorSvcLogLevel
- {
- LogLevel_Error = 0,
- LogLevel_Warning = ( LogLevel_Error + 1 ) ,
- LogLevel_Success = ( LogLevel_Warning + 1 ) ,
- LogLevel_Info = ( LogLevel_Success + 1 )
- } CorSvcLogLevel;
-
-
-EXTERN_C const IID LIBID_mscorsvc;
-
-#ifndef __ICorSvcDependencies_INTERFACE_DEFINED__
-#define __ICorSvcDependencies_INTERFACE_DEFINED__
-
-/* interface ICorSvcDependencies */
-/* [unique][uuid][oleautomation][object] */
-
-
-EXTERN_C const IID IID_ICorSvcDependencies;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("ddb34005-9ba3-4025-9554-f00a2df5dbf5")
- ICorSvcDependencies : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetAssemblyDependencies(
- /* [in] */ BSTR pAssemblyName,
- /* [out] */ SAFEARRAY * *pDependencies,
- /* [out] */ DWORD *assemblyNGenSetting,
- /* [out] */ BSTR *pNativeImageIdentity,
- /* [out] */ BSTR *pAssemblyDisplayName,
- /* [out] */ SAFEARRAY * *pDependencyLoadSetting,
- /* [out] */ SAFEARRAY * *pDependencyNGenSetting) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcDependenciesVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcDependencies * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcDependencies * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcDependencies * This);
-
- HRESULT ( STDMETHODCALLTYPE *GetAssemblyDependencies )(
- ICorSvcDependencies * This,
- /* [in] */ BSTR pAssemblyName,
- /* [out] */ SAFEARRAY * *pDependencies,
- /* [out] */ DWORD *assemblyNGenSetting,
- /* [out] */ BSTR *pNativeImageIdentity,
- /* [out] */ BSTR *pAssemblyDisplayName,
- /* [out] */ SAFEARRAY * *pDependencyLoadSetting,
- /* [out] */ SAFEARRAY * *pDependencyNGenSetting);
-
- END_INTERFACE
- } ICorSvcDependenciesVtbl;
-
- interface ICorSvcDependencies
- {
- CONST_VTBL struct ICorSvcDependenciesVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcDependencies_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcDependencies_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcDependencies_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcDependencies_GetAssemblyDependencies(This,pAssemblyName,pDependencies,assemblyNGenSetting,pNativeImageIdentity,pAssemblyDisplayName,pDependencyLoadSetting,pDependencyNGenSetting) \
- ( (This)->lpVtbl -> GetAssemblyDependencies(This,pAssemblyName,pDependencies,assemblyNGenSetting,pNativeImageIdentity,pAssemblyDisplayName,pDependencyLoadSetting,pDependencyNGenSetting) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcDependencies_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcWorker_INTERFACE_DEFINED__
-#define __ICorSvcWorker_INTERFACE_DEFINED__
-
-/* interface ICorSvcWorker */
-/* [unique][uuid][oleautomation][object] */
-
-
-EXTERN_C const IID IID_ICorSvcWorker;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("d1047bc2-67c0-400c-a94c-e64446a67fbe")
- ICorSvcWorker : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE SetPriority(
- /* [in] */ SvcWorkerPriority priority) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE OptimizeAssembly(
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pApplicationName,
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ SAFEARRAY * loadAlwaysList,
- /* [in] */ SAFEARRAY * loadSometimesList,
- /* [in] */ SAFEARRAY * loadNeverList,
- /* [out] */ BSTR *pNativeImageIdentity) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE DeleteNativeImage(
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pNativeImage) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE DisplayNativeImages(
- /* [in] */ BSTR pAssemblyName) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetCorSvcDependencies(
- /* [in] */ BSTR pApplicationName,
- /* [in] */ OptimizationScenario scenario,
- /* [out] */ ICorSvcDependencies **pCorSvcDependencies) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Stop( void) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcWorkerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcWorker * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcWorker * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcWorker * This);
-
- HRESULT ( STDMETHODCALLTYPE *SetPriority )(
- ICorSvcWorker * This,
- /* [in] */ SvcWorkerPriority priority);
-
- HRESULT ( STDMETHODCALLTYPE *OptimizeAssembly )(
- ICorSvcWorker * This,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pApplicationName,
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ SAFEARRAY * loadAlwaysList,
- /* [in] */ SAFEARRAY * loadSometimesList,
- /* [in] */ SAFEARRAY * loadNeverList,
- /* [out] */ BSTR *pNativeImageIdentity);
-
- HRESULT ( STDMETHODCALLTYPE *DeleteNativeImage )(
- ICorSvcWorker * This,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pNativeImage);
-
- HRESULT ( STDMETHODCALLTYPE *DisplayNativeImages )(
- ICorSvcWorker * This,
- /* [in] */ BSTR pAssemblyName);
-
- HRESULT ( STDMETHODCALLTYPE *GetCorSvcDependencies )(
- ICorSvcWorker * This,
- /* [in] */ BSTR pApplicationName,
- /* [in] */ OptimizationScenario scenario,
- /* [out] */ ICorSvcDependencies **pCorSvcDependencies);
-
- HRESULT ( STDMETHODCALLTYPE *Stop )(
- ICorSvcWorker * This);
-
- END_INTERFACE
- } ICorSvcWorkerVtbl;
-
- interface ICorSvcWorker
- {
- CONST_VTBL struct ICorSvcWorkerVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcWorker_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcWorker_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcWorker_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcWorker_SetPriority(This,priority) \
- ( (This)->lpVtbl -> SetPriority(This,priority) )
-
-#define ICorSvcWorker_OptimizeAssembly(This,pAssemblyName,pApplicationName,scenario,loadAlwaysList,loadSometimesList,loadNeverList,pNativeImageIdentity) \
- ( (This)->lpVtbl -> OptimizeAssembly(This,pAssemblyName,pApplicationName,scenario,loadAlwaysList,loadSometimesList,loadNeverList,pNativeImageIdentity) )
-
-#define ICorSvcWorker_DeleteNativeImage(This,pAssemblyName,pNativeImage) \
- ( (This)->lpVtbl -> DeleteNativeImage(This,pAssemblyName,pNativeImage) )
-
-#define ICorSvcWorker_DisplayNativeImages(This,pAssemblyName) \
- ( (This)->lpVtbl -> DisplayNativeImages(This,pAssemblyName) )
-
-#define ICorSvcWorker_GetCorSvcDependencies(This,pApplicationName,scenario,pCorSvcDependencies) \
- ( (This)->lpVtbl -> GetCorSvcDependencies(This,pApplicationName,scenario,pCorSvcDependencies) )
-
-#define ICorSvcWorker_Stop(This) \
- ( (This)->lpVtbl -> Stop(This) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcWorker_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcWorker2_INTERFACE_DEFINED__
-#define __ICorSvcWorker2_INTERFACE_DEFINED__
-
-/* interface ICorSvcWorker2 */
-/* [unique][uuid][oleautomation][object] */
-
-
-EXTERN_C const IID IID_ICorSvcWorker2;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("f3358a7d-0061-4776-880e-a2f21b9ef93e")
- ICorSvcWorker2 : public ICorSvcWorker
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE CreatePdb(
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pAppBaseOrConfig,
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ BSTR pNativeImagePath,
- /* [in] */ BSTR pPdbPath) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcWorker2Vtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcWorker2 * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcWorker2 * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcWorker2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *SetPriority )(
- ICorSvcWorker2 * This,
- /* [in] */ SvcWorkerPriority priority);
-
- HRESULT ( STDMETHODCALLTYPE *OptimizeAssembly )(
- ICorSvcWorker2 * This,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pApplicationName,
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ SAFEARRAY * loadAlwaysList,
- /* [in] */ SAFEARRAY * loadSometimesList,
- /* [in] */ SAFEARRAY * loadNeverList,
- /* [out] */ BSTR *pNativeImageIdentity);
-
- HRESULT ( STDMETHODCALLTYPE *DeleteNativeImage )(
- ICorSvcWorker2 * This,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pNativeImage);
-
- HRESULT ( STDMETHODCALLTYPE *DisplayNativeImages )(
- ICorSvcWorker2 * This,
- /* [in] */ BSTR pAssemblyName);
-
- HRESULT ( STDMETHODCALLTYPE *GetCorSvcDependencies )(
- ICorSvcWorker2 * This,
- /* [in] */ BSTR pApplicationName,
- /* [in] */ OptimizationScenario scenario,
- /* [out] */ ICorSvcDependencies **pCorSvcDependencies);
-
- HRESULT ( STDMETHODCALLTYPE *Stop )(
- ICorSvcWorker2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *CreatePdb )(
- ICorSvcWorker2 * This,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pAppBaseOrConfig,
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ BSTR pNativeImagePath,
- /* [in] */ BSTR pPdbPath);
-
- END_INTERFACE
- } ICorSvcWorker2Vtbl;
-
- interface ICorSvcWorker2
- {
- CONST_VTBL struct ICorSvcWorker2Vtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcWorker2_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcWorker2_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcWorker2_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcWorker2_SetPriority(This,priority) \
- ( (This)->lpVtbl -> SetPriority(This,priority) )
-
-#define ICorSvcWorker2_OptimizeAssembly(This,pAssemblyName,pApplicationName,scenario,loadAlwaysList,loadSometimesList,loadNeverList,pNativeImageIdentity) \
- ( (This)->lpVtbl -> OptimizeAssembly(This,pAssemblyName,pApplicationName,scenario,loadAlwaysList,loadSometimesList,loadNeverList,pNativeImageIdentity) )
-
-#define ICorSvcWorker2_DeleteNativeImage(This,pAssemblyName,pNativeImage) \
- ( (This)->lpVtbl -> DeleteNativeImage(This,pAssemblyName,pNativeImage) )
-
-#define ICorSvcWorker2_DisplayNativeImages(This,pAssemblyName) \
- ( (This)->lpVtbl -> DisplayNativeImages(This,pAssemblyName) )
-
-#define ICorSvcWorker2_GetCorSvcDependencies(This,pApplicationName,scenario,pCorSvcDependencies) \
- ( (This)->lpVtbl -> GetCorSvcDependencies(This,pApplicationName,scenario,pCorSvcDependencies) )
-
-#define ICorSvcWorker2_Stop(This) \
- ( (This)->lpVtbl -> Stop(This) )
-
-
-#define ICorSvcWorker2_CreatePdb(This,pAssemblyName,pAppBaseOrConfig,scenario,pNativeImagePath,pPdbPath) \
- ( (This)->lpVtbl -> CreatePdb(This,pAssemblyName,pAppBaseOrConfig,scenario,pNativeImagePath,pPdbPath) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcWorker2_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcWorker3_INTERFACE_DEFINED__
-#define __ICorSvcWorker3_INTERFACE_DEFINED__
-
-/* interface ICorSvcWorker3 */
-/* [unique][uuid][oleautomation][object] */
-
-
-EXTERN_C const IID IID_ICorSvcWorker3;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("DC516615-47BE-477e-8B55-C5ABE0D76B8F")
- ICorSvcWorker3 : public ICorSvcWorker2
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE CreatePdb2(
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pAppBaseOrConfig,
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ BSTR pNativeImagePath,
- /* [in] */ BSTR pPdbPath,
- /* [in] */ BOOL pdbLines,
- /* [in] */ BSTR managedPdbSearchPath) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcWorker3Vtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcWorker3 * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcWorker3 * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcWorker3 * This);
-
- HRESULT ( STDMETHODCALLTYPE *SetPriority )(
- ICorSvcWorker3 * This,
- /* [in] */ SvcWorkerPriority priority);
-
- HRESULT ( STDMETHODCALLTYPE *OptimizeAssembly )(
- ICorSvcWorker3 * This,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pApplicationName,
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ SAFEARRAY * loadAlwaysList,
- /* [in] */ SAFEARRAY * loadSometimesList,
- /* [in] */ SAFEARRAY * loadNeverList,
- /* [out] */ BSTR *pNativeImageIdentity);
-
- HRESULT ( STDMETHODCALLTYPE *DeleteNativeImage )(
- ICorSvcWorker3 * This,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pNativeImage);
-
- HRESULT ( STDMETHODCALLTYPE *DisplayNativeImages )(
- ICorSvcWorker3 * This,
- /* [in] */ BSTR pAssemblyName);
-
- HRESULT ( STDMETHODCALLTYPE *GetCorSvcDependencies )(
- ICorSvcWorker3 * This,
- /* [in] */ BSTR pApplicationName,
- /* [in] */ OptimizationScenario scenario,
- /* [out] */ ICorSvcDependencies **pCorSvcDependencies);
-
- HRESULT ( STDMETHODCALLTYPE *Stop )(
- ICorSvcWorker3 * This);
-
- HRESULT ( STDMETHODCALLTYPE *CreatePdb )(
- ICorSvcWorker3 * This,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pAppBaseOrConfig,
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ BSTR pNativeImagePath,
- /* [in] */ BSTR pPdbPath);
-
- HRESULT ( STDMETHODCALLTYPE *CreatePdb2 )(
- ICorSvcWorker3 * This,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BSTR pAppBaseOrConfig,
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ BSTR pNativeImagePath,
- /* [in] */ BSTR pPdbPath,
- /* [in] */ BOOL pdbLines,
- /* [in] */ BSTR managedPdbSearchPath);
-
- END_INTERFACE
- } ICorSvcWorker3Vtbl;
-
- interface ICorSvcWorker3
- {
- CONST_VTBL struct ICorSvcWorker3Vtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcWorker3_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcWorker3_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcWorker3_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcWorker3_SetPriority(This,priority) \
- ( (This)->lpVtbl -> SetPriority(This,priority) )
-
-#define ICorSvcWorker3_OptimizeAssembly(This,pAssemblyName,pApplicationName,scenario,loadAlwaysList,loadSometimesList,loadNeverList,pNativeImageIdentity) \
- ( (This)->lpVtbl -> OptimizeAssembly(This,pAssemblyName,pApplicationName,scenario,loadAlwaysList,loadSometimesList,loadNeverList,pNativeImageIdentity) )
-
-#define ICorSvcWorker3_DeleteNativeImage(This,pAssemblyName,pNativeImage) \
- ( (This)->lpVtbl -> DeleteNativeImage(This,pAssemblyName,pNativeImage) )
-
-#define ICorSvcWorker3_DisplayNativeImages(This,pAssemblyName) \
- ( (This)->lpVtbl -> DisplayNativeImages(This,pAssemblyName) )
-
-#define ICorSvcWorker3_GetCorSvcDependencies(This,pApplicationName,scenario,pCorSvcDependencies) \
- ( (This)->lpVtbl -> GetCorSvcDependencies(This,pApplicationName,scenario,pCorSvcDependencies) )
-
-#define ICorSvcWorker3_Stop(This) \
- ( (This)->lpVtbl -> Stop(This) )
-
-
-#define ICorSvcWorker3_CreatePdb(This,pAssemblyName,pAppBaseOrConfig,scenario,pNativeImagePath,pPdbPath) \
- ( (This)->lpVtbl -> CreatePdb(This,pAssemblyName,pAppBaseOrConfig,scenario,pNativeImagePath,pPdbPath) )
-
-
-#define ICorSvcWorker3_CreatePdb2(This,pAssemblyName,pAppBaseOrConfig,scenario,pNativeImagePath,pPdbPath,pdbLines,managedPdbSearchPath) \
- ( (This)->lpVtbl -> CreatePdb2(This,pAssemblyName,pAppBaseOrConfig,scenario,pNativeImagePath,pPdbPath,pdbLines,managedPdbSearchPath) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcWorker3_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcSetPrivateAttributes_INTERFACE_DEFINED__
-#define __ICorSvcSetPrivateAttributes_INTERFACE_DEFINED__
-
-/* interface ICorSvcSetPrivateAttributes */
-/* [unique][uuid][oleautomation][object] */
-
-
-EXTERN_C const IID IID_ICorSvcSetPrivateAttributes;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("b18e0b40-c089-4350-8328-066c668bccc2")
- ICorSvcSetPrivateAttributes : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE SetNGenPrivateAttributes(
- /* [in] */ NGenPrivateAttributes ngenPrivateAttributes) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcSetPrivateAttributesVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcSetPrivateAttributes * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcSetPrivateAttributes * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcSetPrivateAttributes * This);
-
- HRESULT ( STDMETHODCALLTYPE *SetNGenPrivateAttributes )(
- ICorSvcSetPrivateAttributes * This,
- /* [in] */ NGenPrivateAttributes ngenPrivateAttributes);
-
- END_INTERFACE
- } ICorSvcSetPrivateAttributesVtbl;
-
- interface ICorSvcSetPrivateAttributes
- {
- CONST_VTBL struct ICorSvcSetPrivateAttributesVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcSetPrivateAttributes_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcSetPrivateAttributes_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcSetPrivateAttributes_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcSetPrivateAttributes_SetNGenPrivateAttributes(This,ngenPrivateAttributes) \
- ( (This)->lpVtbl -> SetNGenPrivateAttributes(This,ngenPrivateAttributes) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcSetPrivateAttributes_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcRepository_INTERFACE_DEFINED__
-#define __ICorSvcRepository_INTERFACE_DEFINED__
-
-/* interface ICorSvcRepository */
-/* [unique][uuid][oleautomation][object] */
-
-
-EXTERN_C const IID IID_ICorSvcRepository;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("d5346658-b5fd-4353-9647-07ad4783d5a0")
- ICorSvcRepository : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE SetRepository(
- /* [in] */ BSTR pRepositoryDir,
- /* [in] */ RepositoryFlags repositoryFlags) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcRepositoryVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcRepository * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcRepository * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcRepository * This);
-
- HRESULT ( STDMETHODCALLTYPE *SetRepository )(
- ICorSvcRepository * This,
- /* [in] */ BSTR pRepositoryDir,
- /* [in] */ RepositoryFlags repositoryFlags);
-
- END_INTERFACE
- } ICorSvcRepositoryVtbl;
-
- interface ICorSvcRepository
- {
- CONST_VTBL struct ICorSvcRepositoryVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcRepository_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcRepository_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcRepository_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcRepository_SetRepository(This,pRepositoryDir,repositoryFlags) \
- ( (This)->lpVtbl -> SetRepository(This,pRepositoryDir,repositoryFlags) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcRepository_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcAppX_INTERFACE_DEFINED__
-#define __ICorSvcAppX_INTERFACE_DEFINED__
-
-/* interface ICorSvcAppX */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcAppX;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("5c814791-559e-4f7f-83ce-184a4ccbae24")
- ICorSvcAppX : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE SetPackage(
- /* [in] */ BSTR pPackageFullName) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE SetLocalAppDataDirectory(
- /* [in] */ BSTR pLocalAppDataDirectory) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcAppXVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcAppX * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcAppX * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcAppX * This);
-
- HRESULT ( STDMETHODCALLTYPE *SetPackage )(
- ICorSvcAppX * This,
- /* [in] */ BSTR pPackageFullName);
-
- HRESULT ( STDMETHODCALLTYPE *SetLocalAppDataDirectory )(
- ICorSvcAppX * This,
- /* [in] */ BSTR pLocalAppDataDirectory);
-
- END_INTERFACE
- } ICorSvcAppXVtbl;
-
- interface ICorSvcAppX
- {
- CONST_VTBL struct ICorSvcAppXVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcAppX_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcAppX_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcAppX_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcAppX_SetPackage(This,pPackageFullName) \
- ( (This)->lpVtbl -> SetPackage(This,pPackageFullName) )
-
-#define ICorSvcAppX_SetLocalAppDataDirectory(This,pLocalAppDataDirectory) \
- ( (This)->lpVtbl -> SetLocalAppDataDirectory(This,pLocalAppDataDirectory) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcAppX_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcLogger_INTERFACE_DEFINED__
-#define __ICorSvcLogger_INTERFACE_DEFINED__
-
-/* interface ICorSvcLogger */
-/* [unique][uuid][oleautomation][object] */
-
-
-EXTERN_C const IID IID_ICorSvcLogger;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("d189ff1a-e266-4f13-9637-4b9522279ffc")
- ICorSvcLogger : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE Log(
- /* [in] */ CorSvcLogLevel logLevel,
- /* [in] */ BSTR message) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcLoggerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcLogger * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcLogger * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcLogger * This);
-
- HRESULT ( STDMETHODCALLTYPE *Log )(
- ICorSvcLogger * This,
- /* [in] */ CorSvcLogLevel logLevel,
- /* [in] */ BSTR message);
-
- END_INTERFACE
- } ICorSvcLoggerVtbl;
-
- interface ICorSvcLogger
- {
- CONST_VTBL struct ICorSvcLoggerVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcLogger_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcLogger_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcLogger_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcLogger_Log(This,logLevel,message) \
- ( (This)->lpVtbl -> Log(This,logLevel,message) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcLogger_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcPooledWorker_INTERFACE_DEFINED__
-#define __ICorSvcPooledWorker_INTERFACE_DEFINED__
-
-/* interface ICorSvcPooledWorker */
-/* [unique][uuid][oleautomation][object] */
-
-
-EXTERN_C const IID IID_ICorSvcPooledWorker;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("0631e7e2-6046-4fde-8b6d-a09b64fda6f3")
- ICorSvcPooledWorker : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE CanReuseProcess(
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ ICorSvcLogger *pCorSvcLogger,
- /* [out] */ BOOL *pCanContinue) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcPooledWorkerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcPooledWorker * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcPooledWorker * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcPooledWorker * This);
-
- HRESULT ( STDMETHODCALLTYPE *CanReuseProcess )(
- ICorSvcPooledWorker * This,
- /* [in] */ OptimizationScenario scenario,
- /* [in] */ ICorSvcLogger *pCorSvcLogger,
- /* [out] */ BOOL *pCanContinue);
-
- END_INTERFACE
- } ICorSvcPooledWorkerVtbl;
-
- interface ICorSvcPooledWorker
- {
- CONST_VTBL struct ICorSvcPooledWorkerVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcPooledWorker_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcPooledWorker_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcPooledWorker_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcPooledWorker_CanReuseProcess(This,scenario,pCorSvcLogger,pCanContinue) \
- ( (This)->lpVtbl -> CanReuseProcess(This,scenario,pCorSvcLogger,pCanContinue) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcPooledWorker_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcBindToWorker_INTERFACE_DEFINED__
-#define __ICorSvcBindToWorker_INTERFACE_DEFINED__
-
-/* interface ICorSvcBindToWorker */
-/* [unique][uuid][oleautomation][object] */
-
-
-EXTERN_C const IID IID_ICorSvcBindToWorker;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("5c6fb596-4828-4ed5-b9dd-293dad736fb5")
- ICorSvcBindToWorker : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE BindToRuntimeWorker(
- /* [in] */ BSTR pRuntimeVersion,
- /* [in] */ DWORD ParentProcessID,
- /* [in] */ BSTR pInterruptEventName,
- /* [in] */ ICorSvcLogger *pCorSvcLogger,
- /* [out] */ ICorSvcWorker **pCorSvcWorker) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcBindToWorkerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcBindToWorker * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcBindToWorker * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcBindToWorker * This);
-
- HRESULT ( STDMETHODCALLTYPE *BindToRuntimeWorker )(
- ICorSvcBindToWorker * This,
- /* [in] */ BSTR pRuntimeVersion,
- /* [in] */ DWORD ParentProcessID,
- /* [in] */ BSTR pInterruptEventName,
- /* [in] */ ICorSvcLogger *pCorSvcLogger,
- /* [out] */ ICorSvcWorker **pCorSvcWorker);
-
- END_INTERFACE
- } ICorSvcBindToWorkerVtbl;
-
- interface ICorSvcBindToWorker
- {
- CONST_VTBL struct ICorSvcBindToWorkerVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcBindToWorker_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcBindToWorker_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcBindToWorker_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcBindToWorker_BindToRuntimeWorker(This,pRuntimeVersion,ParentProcessID,pInterruptEventName,pCorSvcLogger,pCorSvcWorker) \
- ( (This)->lpVtbl -> BindToRuntimeWorker(This,pRuntimeVersion,ParentProcessID,pInterruptEventName,pCorSvcLogger,pCorSvcWorker) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcBindToWorker_INTERFACE_DEFINED__ */
-
-#endif /* __mscorsvc_LIBRARY_DEFINED__ */
-
-/* interface __MIDL_itf_mscorsvc_0000_0001 */
-/* [local] */
-
-typedef /* [public][public] */
-enum __MIDL___MIDL_itf_mscorsvc_0000_0001_0001
- {
- Service_NoAction = -1,
- Service_Start = 0,
- Service_Stop = 0x1,
- Service_Pause = 0x2,
- Service_Continue = 0x3,
- Service_Interrogate = 0x4,
- Service_StartPaused = 0x5
- } ControlServiceAction;
-
-typedef struct _COR_SERVICE_STATUS
- {
- WCHAR sServiceName[ 64 ];
- DWORD dwServiceType;
- DWORD dwCurrentState;
- DWORD dwControlsAccepted;
- DWORD dwWin32ExitCode;
- DWORD dwServiceSpecificExitCode;
- DWORD dwCheckPoint;
- DWORD dwWaitHint;
- } COR_SERVICE_STATUS;
-
-typedef struct _COR_SERVICE_STATUS *LPCOR_SERVICE_STATUS;
-
-typedef struct _ServiceOptions
- {
- BOOL RunAsWindowsService;
- BOOL RunAsPrivateRuntime;
- BOOL StartPaused;
- } ServiceOptions;
-
-
-
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0001_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0001_v0_0_s_ifspec;
-
-#ifndef __ICorSvc_INTERFACE_DEFINED__
-#define __ICorSvc_INTERFACE_DEFINED__
-
-/* interface ICorSvc */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvc;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("3eef5ff0-3680-4f20-8a8f-9051aca66b22")
- ICorSvc : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE GetServiceManagerInterface(
- /* [in] */ IUnknown **pIUnknown) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE InstallService( void) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE UninstallService( void) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE ControlService(
- /* [in] */ ControlServiceAction Action,
- /* [out] */ LPCOR_SERVICE_STATUS lpServiceStatus) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE RunService(
- /* [in] */ ServiceOptions options) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvc * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvc * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvc * This);
-
- HRESULT ( STDMETHODCALLTYPE *GetServiceManagerInterface )(
- ICorSvc * This,
- /* [in] */ IUnknown **pIUnknown);
-
- HRESULT ( STDMETHODCALLTYPE *InstallService )(
- ICorSvc * This);
-
- HRESULT ( STDMETHODCALLTYPE *UninstallService )(
- ICorSvc * This);
-
- HRESULT ( STDMETHODCALLTYPE *ControlService )(
- ICorSvc * This,
- /* [in] */ ControlServiceAction Action,
- /* [out] */ LPCOR_SERVICE_STATUS lpServiceStatus);
-
- HRESULT ( STDMETHODCALLTYPE *RunService )(
- ICorSvc * This,
- /* [in] */ ServiceOptions options);
-
- END_INTERFACE
- } ICorSvcVtbl;
-
- interface ICorSvc
- {
- CONST_VTBL struct ICorSvcVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvc_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvc_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvc_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvc_GetServiceManagerInterface(This,pIUnknown) \
- ( (This)->lpVtbl -> GetServiceManagerInterface(This,pIUnknown) )
-
-#define ICorSvc_InstallService(This) \
- ( (This)->lpVtbl -> InstallService(This) )
-
-#define ICorSvc_UninstallService(This) \
- ( (This)->lpVtbl -> UninstallService(This) )
-
-#define ICorSvc_ControlService(This,Action,lpServiceStatus) \
- ( (This)->lpVtbl -> ControlService(This,Action,lpServiceStatus) )
-
-#define ICorSvc_RunService(This,options) \
- ( (This)->lpVtbl -> RunService(This,options) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvc_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICompileProgressNotification_INTERFACE_DEFINED__
-#define __ICompileProgressNotification_INTERFACE_DEFINED__
-
-/* interface ICompileProgressNotification */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICompileProgressNotification;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("01c10030-6c81-4671-bd51-14b184c673b2")
- ICompileProgressNotification : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE CompileStarted(
- /* [in] */ DWORD cAssembliesToCompile,
- /* [in] */ DWORD cTimeEstimate) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE ProgressNotification(
- /* [in] */ DWORD cAssembly,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BOOL isStartNotification,
- /* [in] */ HRESULT hrResult,
- /* [in] */ BSTR errorExplanation,
- /* [in] */ DWORD cTimeRemainingEstimate) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICompileProgressNotificationVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICompileProgressNotification * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICompileProgressNotification * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICompileProgressNotification * This);
-
- HRESULT ( STDMETHODCALLTYPE *CompileStarted )(
- ICompileProgressNotification * This,
- /* [in] */ DWORD cAssembliesToCompile,
- /* [in] */ DWORD cTimeEstimate);
-
- HRESULT ( STDMETHODCALLTYPE *ProgressNotification )(
- ICompileProgressNotification * This,
- /* [in] */ DWORD cAssembly,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BOOL isStartNotification,
- /* [in] */ HRESULT hrResult,
- /* [in] */ BSTR errorExplanation,
- /* [in] */ DWORD cTimeRemainingEstimate);
-
- END_INTERFACE
- } ICompileProgressNotificationVtbl;
-
- interface ICompileProgressNotification
- {
- CONST_VTBL struct ICompileProgressNotificationVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICompileProgressNotification_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICompileProgressNotification_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICompileProgressNotification_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICompileProgressNotification_CompileStarted(This,cAssembliesToCompile,cTimeEstimate) \
- ( (This)->lpVtbl -> CompileStarted(This,cAssembliesToCompile,cTimeEstimate) )
-
-#define ICompileProgressNotification_ProgressNotification(This,cAssembly,pAssemblyName,isStartNotification,hrResult,errorExplanation,cTimeRemainingEstimate) \
- ( (This)->lpVtbl -> ProgressNotification(This,cAssembly,pAssemblyName,isStartNotification,hrResult,errorExplanation,cTimeRemainingEstimate) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICompileProgressNotification_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICompileProgressNotification2_INTERFACE_DEFINED__
-#define __ICompileProgressNotification2_INTERFACE_DEFINED__
-
-/* interface ICompileProgressNotification2 */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICompileProgressNotification2;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("98E5BDE2-E9A0-4ADE-9CB2-6CD06FDB1A85")
- ICompileProgressNotification2 : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE CompileStarted(
- /* [in] */ DWORD cAssembliesToCompile,
- /* [in] */ DWORD cTimeEstimate,
- /* [in] */ DWORD threadID) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE ProgressNotification(
- /* [in] */ DWORD cAssembly,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BOOL isStartNotification,
- /* [in] */ HRESULT hrResult,
- /* [in] */ BSTR errorExplanation,
- /* [in] */ DWORD cTimeRemainingEstimate,
- /* [in] */ DWORD threadID) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICompileProgressNotification2Vtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICompileProgressNotification2 * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICompileProgressNotification2 * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICompileProgressNotification2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *CompileStarted )(
- ICompileProgressNotification2 * This,
- /* [in] */ DWORD cAssembliesToCompile,
- /* [in] */ DWORD cTimeEstimate,
- /* [in] */ DWORD threadID);
-
- HRESULT ( STDMETHODCALLTYPE *ProgressNotification )(
- ICompileProgressNotification2 * This,
- /* [in] */ DWORD cAssembly,
- /* [in] */ BSTR pAssemblyName,
- /* [in] */ BOOL isStartNotification,
- /* [in] */ HRESULT hrResult,
- /* [in] */ BSTR errorExplanation,
- /* [in] */ DWORD cTimeRemainingEstimate,
- /* [in] */ DWORD threadID);
-
- END_INTERFACE
- } ICompileProgressNotification2Vtbl;
-
- interface ICompileProgressNotification2
- {
- CONST_VTBL struct ICompileProgressNotification2Vtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICompileProgressNotification2_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICompileProgressNotification2_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICompileProgressNotification2_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICompileProgressNotification2_CompileStarted(This,cAssembliesToCompile,cTimeEstimate,threadID) \
- ( (This)->lpVtbl -> CompileStarted(This,cAssembliesToCompile,cTimeEstimate,threadID) )
-
-#define ICompileProgressNotification2_ProgressNotification(This,cAssembly,pAssemblyName,isStartNotification,hrResult,errorExplanation,cTimeRemainingEstimate,threadID) \
- ( (This)->lpVtbl -> ProgressNotification(This,cAssembly,pAssemblyName,isStartNotification,hrResult,errorExplanation,cTimeRemainingEstimate,threadID) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICompileProgressNotification2_INTERFACE_DEFINED__ */
-
-
-/* interface __MIDL_itf_mscorsvc_0000_0004 */
-/* [local] */
-
-typedef /* [public][public] */
-enum __MIDL___MIDL_itf_mscorsvc_0000_0004_0001
- {
- DefaultOptimizeFlags = 0,
- TolerateCompilationFailures = 0x1,
- OptimizeNGenQueueOnly = 0x2
- } OptimizeFlags;
-
-
-
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0004_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0004_v0_0_s_ifspec;
-
-#ifndef __ICorSvcInstaller_INTERFACE_DEFINED__
-#define __ICorSvcInstaller_INTERFACE_DEFINED__
-
-/* interface ICorSvcInstaller */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcInstaller;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("0523feee-eb0e-4857-b2aa-db787521d077")
- ICorSvcInstaller : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE Install(
- /* [in] */ BSTR path) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Uninstall(
- /* [in] */ BSTR path) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Optimize(
- /* [in] */ ICompileProgressNotification *pCompileProgressNotification,
- /* [in] */ OptimizeFlags optimizeFlags) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE SetLogger(
- /* [in] */ ICorSvcLogger *pCorSvcLogger) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcInstallerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcInstaller * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcInstaller * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcInstaller * This);
-
- HRESULT ( STDMETHODCALLTYPE *Install )(
- ICorSvcInstaller * This,
- /* [in] */ BSTR path);
-
- HRESULT ( STDMETHODCALLTYPE *Uninstall )(
- ICorSvcInstaller * This,
- /* [in] */ BSTR path);
-
- HRESULT ( STDMETHODCALLTYPE *Optimize )(
- ICorSvcInstaller * This,
- /* [in] */ ICompileProgressNotification *pCompileProgressNotification,
- /* [in] */ OptimizeFlags optimizeFlags);
-
- HRESULT ( STDMETHODCALLTYPE *SetLogger )(
- ICorSvcInstaller * This,
- /* [in] */ ICorSvcLogger *pCorSvcLogger);
-
- END_INTERFACE
- } ICorSvcInstallerVtbl;
-
- interface ICorSvcInstaller
- {
- CONST_VTBL struct ICorSvcInstallerVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcInstaller_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcInstaller_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcInstaller_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcInstaller_Install(This,path) \
- ( (This)->lpVtbl -> Install(This,path) )
-
-#define ICorSvcInstaller_Uninstall(This,path) \
- ( (This)->lpVtbl -> Uninstall(This,path) )
-
-#define ICorSvcInstaller_Optimize(This,pCompileProgressNotification,optimizeFlags) \
- ( (This)->lpVtbl -> Optimize(This,pCompileProgressNotification,optimizeFlags) )
-
-#define ICorSvcInstaller_SetLogger(This,pCorSvcLogger) \
- ( (This)->lpVtbl -> SetLogger(This,pCorSvcLogger) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcInstaller_INTERFACE_DEFINED__ */
-
-
-/* interface __MIDL_itf_mscorsvc_0000_0005 */
-/* [local] */
-
-typedef /* [public][public][public][public][public] */
-enum __MIDL___MIDL_itf_mscorsvc_0000_0005_0001
- {
- DefaultFlags = 0,
- AllowPartialNames = 0x1,
- KeepPriority = 0x2,
- NoRoot = 0x4
- } GeneralFlags;
-
-typedef /* [public][public][public][public][public] */
-enum __MIDL___MIDL_itf_mscorsvc_0000_0005_0002
- {
- Priority_None = -1,
- Priority_0 = 0,
- Priority_1 = 0x1,
- Priority_2 = 0x2,
- Priority_3 = 0x3,
- Priority_Default = Priority_3,
- Priority_Lowest = Priority_3,
- Priority_LowestAggressive = Priority_2,
- Priority_Highest = Priority_0,
- Priority_Highest_Root = Priority_1,
- Priority_Lowest_Root = Priority_3
- } PriorityLevel;
-
-
-
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0005_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0005_v0_0_s_ifspec;
-
-#ifndef __ICorSvcAdvancedInstaller_INTERFACE_DEFINED__
-#define __ICorSvcAdvancedInstaller_INTERFACE_DEFINED__
-
-/* interface ICorSvcAdvancedInstaller */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcAdvancedInstaller;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("0871fb80-3ea0-47cc-9b51-d92e2aee75db")
- ICorSvcAdvancedInstaller : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE Install(
- /* [in] */ BSTR path,
- /* [in] */ OptimizationScenario optScenario,
- /* [in] */ BSTR config,
- /* [in] */ GeneralFlags generalFlags,
- /* [in] */ PriorityLevel priorityLevel) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Uninstall(
- /* [in] */ BSTR path,
- /* [in] */ OptimizationScenario optScenario,
- /* [in] */ BSTR config,
- /* [in] */ GeneralFlags generalFlags) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcAdvancedInstallerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcAdvancedInstaller * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcAdvancedInstaller * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcAdvancedInstaller * This);
-
- HRESULT ( STDMETHODCALLTYPE *Install )(
- ICorSvcAdvancedInstaller * This,
- /* [in] */ BSTR path,
- /* [in] */ OptimizationScenario optScenario,
- /* [in] */ BSTR config,
- /* [in] */ GeneralFlags generalFlags,
- /* [in] */ PriorityLevel priorityLevel);
-
- HRESULT ( STDMETHODCALLTYPE *Uninstall )(
- ICorSvcAdvancedInstaller * This,
- /* [in] */ BSTR path,
- /* [in] */ OptimizationScenario optScenario,
- /* [in] */ BSTR config,
- /* [in] */ GeneralFlags generalFlags);
-
- END_INTERFACE
- } ICorSvcAdvancedInstallerVtbl;
-
- interface ICorSvcAdvancedInstaller
- {
- CONST_VTBL struct ICorSvcAdvancedInstallerVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcAdvancedInstaller_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcAdvancedInstaller_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcAdvancedInstaller_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcAdvancedInstaller_Install(This,path,optScenario,config,generalFlags,priorityLevel) \
- ( (This)->lpVtbl -> Install(This,path,optScenario,config,generalFlags,priorityLevel) )
-
-#define ICorSvcAdvancedInstaller_Uninstall(This,path,optScenario,config,generalFlags) \
- ( (This)->lpVtbl -> Uninstall(This,path,optScenario,config,generalFlags) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcAdvancedInstaller_INTERFACE_DEFINED__ */
-
-
-/* interface __MIDL_itf_mscorsvc_0000_0006 */
-/* [local] */
-
-typedef /* [public][public][public] */
-enum __MIDL___MIDL_itf_mscorsvc_0000_0006_0001
- {
- UpdateDefault = 0,
- Force = 0x1,
- PostReboot = 0x2
- } UpdateFlags;
-
-
-
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0006_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0006_v0_0_s_ifspec;
-
-#ifndef __ICorSvcOptimizer_INTERFACE_DEFINED__
-#define __ICorSvcOptimizer_INTERFACE_DEFINED__
-
-/* interface ICorSvcOptimizer */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcOptimizer;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("94af0ec4-c10d-45d4-a625-d68d1b02a396")
- ICorSvcOptimizer : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE Update(
- /* [in] */ BSTR path,
- /* [in] */ UpdateFlags updateFlags,
- /* [in] */ GeneralFlags generalFlags) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Display(
- /* [in] */ BSTR path,
- /* [in] */ GeneralFlags generalFlags) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE ScheduleWork(
- /* [in] */ PriorityLevel priorityLevel) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcOptimizerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcOptimizer * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcOptimizer * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcOptimizer * This);
-
- HRESULT ( STDMETHODCALLTYPE *Update )(
- ICorSvcOptimizer * This,
- /* [in] */ BSTR path,
- /* [in] */ UpdateFlags updateFlags,
- /* [in] */ GeneralFlags generalFlags);
-
- HRESULT ( STDMETHODCALLTYPE *Display )(
- ICorSvcOptimizer * This,
- /* [in] */ BSTR path,
- /* [in] */ GeneralFlags generalFlags);
-
- HRESULT ( STDMETHODCALLTYPE *ScheduleWork )(
- ICorSvcOptimizer * This,
- /* [in] */ PriorityLevel priorityLevel);
-
- END_INTERFACE
- } ICorSvcOptimizerVtbl;
-
- interface ICorSvcOptimizer
- {
- CONST_VTBL struct ICorSvcOptimizerVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcOptimizer_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcOptimizer_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcOptimizer_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcOptimizer_Update(This,path,updateFlags,generalFlags) \
- ( (This)->lpVtbl -> Update(This,path,updateFlags,generalFlags) )
-
-#define ICorSvcOptimizer_Display(This,path,generalFlags) \
- ( (This)->lpVtbl -> Display(This,path,generalFlags) )
-
-#define ICorSvcOptimizer_ScheduleWork(This,priorityLevel) \
- ( (This)->lpVtbl -> ScheduleWork(This,priorityLevel) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcOptimizer_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcOptimizer2_INTERFACE_DEFINED__
-#define __ICorSvcOptimizer2_INTERFACE_DEFINED__
-
-/* interface ICorSvcOptimizer2 */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcOptimizer2;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("ee3b09c2-0110-4b6e-a73f-a3d6562f98ab")
- ICorSvcOptimizer2 : public ICorSvcOptimizer
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE CreatePdb(
- /* [in] */ BSTR nativeImagePath,
- /* [in] */ BSTR pdbPath) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcOptimizer2Vtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcOptimizer2 * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcOptimizer2 * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcOptimizer2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *Update )(
- ICorSvcOptimizer2 * This,
- /* [in] */ BSTR path,
- /* [in] */ UpdateFlags updateFlags,
- /* [in] */ GeneralFlags generalFlags);
-
- HRESULT ( STDMETHODCALLTYPE *Display )(
- ICorSvcOptimizer2 * This,
- /* [in] */ BSTR path,
- /* [in] */ GeneralFlags generalFlags);
-
- HRESULT ( STDMETHODCALLTYPE *ScheduleWork )(
- ICorSvcOptimizer2 * This,
- /* [in] */ PriorityLevel priorityLevel);
-
- HRESULT ( STDMETHODCALLTYPE *CreatePdb )(
- ICorSvcOptimizer2 * This,
- /* [in] */ BSTR nativeImagePath,
- /* [in] */ BSTR pdbPath);
-
- END_INTERFACE
- } ICorSvcOptimizer2Vtbl;
-
- interface ICorSvcOptimizer2
- {
- CONST_VTBL struct ICorSvcOptimizer2Vtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcOptimizer2_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcOptimizer2_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcOptimizer2_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcOptimizer2_Update(This,path,updateFlags,generalFlags) \
- ( (This)->lpVtbl -> Update(This,path,updateFlags,generalFlags) )
-
-#define ICorSvcOptimizer2_Display(This,path,generalFlags) \
- ( (This)->lpVtbl -> Display(This,path,generalFlags) )
-
-#define ICorSvcOptimizer2_ScheduleWork(This,priorityLevel) \
- ( (This)->lpVtbl -> ScheduleWork(This,priorityLevel) )
-
-
-#define ICorSvcOptimizer2_CreatePdb(This,nativeImagePath,pdbPath) \
- ( (This)->lpVtbl -> CreatePdb(This,nativeImagePath,pdbPath) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcOptimizer2_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcOptimizer3_INTERFACE_DEFINED__
-#define __ICorSvcOptimizer3_INTERFACE_DEFINED__
-
-/* interface ICorSvcOptimizer3 */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcOptimizer3;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("6EED164F-61EE-4a07-ABE8-670F92B4B7A9")
- ICorSvcOptimizer3 : public ICorSvcOptimizer2
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE CreatePdb2(
- /* [in] */ BSTR nativeImagePath,
- /* [in] */ BSTR pdbPath,
- /* [in] */ BOOL pdbLines,
- /* [in] */ BSTR managedPdbSearchPath) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcOptimizer3Vtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcOptimizer3 * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcOptimizer3 * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcOptimizer3 * This);
-
- HRESULT ( STDMETHODCALLTYPE *Update )(
- ICorSvcOptimizer3 * This,
- /* [in] */ BSTR path,
- /* [in] */ UpdateFlags updateFlags,
- /* [in] */ GeneralFlags generalFlags);
-
- HRESULT ( STDMETHODCALLTYPE *Display )(
- ICorSvcOptimizer3 * This,
- /* [in] */ BSTR path,
- /* [in] */ GeneralFlags generalFlags);
-
- HRESULT ( STDMETHODCALLTYPE *ScheduleWork )(
- ICorSvcOptimizer3 * This,
- /* [in] */ PriorityLevel priorityLevel);
-
- HRESULT ( STDMETHODCALLTYPE *CreatePdb )(
- ICorSvcOptimizer3 * This,
- /* [in] */ BSTR nativeImagePath,
- /* [in] */ BSTR pdbPath);
-
- HRESULT ( STDMETHODCALLTYPE *CreatePdb2 )(
- ICorSvcOptimizer3 * This,
- /* [in] */ BSTR nativeImagePath,
- /* [in] */ BSTR pdbPath,
- /* [in] */ BOOL pdbLines,
- /* [in] */ BSTR managedPdbSearchPath);
-
- END_INTERFACE
- } ICorSvcOptimizer3Vtbl;
-
- interface ICorSvcOptimizer3
- {
- CONST_VTBL struct ICorSvcOptimizer3Vtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcOptimizer3_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcOptimizer3_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcOptimizer3_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcOptimizer3_Update(This,path,updateFlags,generalFlags) \
- ( (This)->lpVtbl -> Update(This,path,updateFlags,generalFlags) )
-
-#define ICorSvcOptimizer3_Display(This,path,generalFlags) \
- ( (This)->lpVtbl -> Display(This,path,generalFlags) )
-
-#define ICorSvcOptimizer3_ScheduleWork(This,priorityLevel) \
- ( (This)->lpVtbl -> ScheduleWork(This,priorityLevel) )
-
-
-#define ICorSvcOptimizer3_CreatePdb(This,nativeImagePath,pdbPath) \
- ( (This)->lpVtbl -> CreatePdb(This,nativeImagePath,pdbPath) )
-
-
-#define ICorSvcOptimizer3_CreatePdb2(This,nativeImagePath,pdbPath,pdbLines,managedPdbSearchPath) \
- ( (This)->lpVtbl -> CreatePdb2(This,nativeImagePath,pdbPath,pdbLines,managedPdbSearchPath) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcOptimizer3_INTERFACE_DEFINED__ */
-
-
-/* interface __MIDL_itf_mscorsvc_0000_0009 */
-/* [local] */
-
-typedef /* [public][public] */
-enum __MIDL___MIDL_itf_mscorsvc_0000_0009_0001
- {
- NewWorkAvailable = 0,
- ClientWorkStart = 0x1,
- ClientWorkDone = 0x2,
- UpdatePostReboot = 0x3,
- NewWorkAvailableWithDelay = 0x4
- } ServiceNotification;
-
-
-
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0009_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_mscorsvc_0000_0009_v0_0_s_ifspec;
-
-#ifndef __ICorSvcManager_INTERFACE_DEFINED__
-#define __ICorSvcManager_INTERFACE_DEFINED__
-
-/* interface ICorSvcManager */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcManager;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("8f416a48-d663-4a7e-9732-fbca3fc46ea8")
- ICorSvcManager : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE ScheduleWorkForSinglePriorityLevel(
- /* [in] */ PriorityLevel priorityLevel,
- /* [in] */ BSTR pInterruptEventName,
- /* [out] */ BOOL *pWorkScheduled) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Optimize(
- /* [in] */ DWORD dwWorkerPriorityClass,
- /* [in] */ ICompileProgressNotification *pCompileProgressNotification,
- /* [in] */ BSTR pInterruptEventName) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE NotifyService(
- ServiceNotification notification) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE IsWorkAvailable(
- /* [in] */ PriorityLevel priorityLevel,
- /* [out] */ BOOL *pWorkAvailable) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Update(
- /* [in] */ UpdateFlags updateFlags,
- /* [in] */ BSTR pInterruptEventName) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE SetSvcLogger(
- /* [in] */ ICorSvcLogger *pCorSvcLogger) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcManagerVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcManager * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcManager * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcManager * This);
-
- HRESULT ( STDMETHODCALLTYPE *ScheduleWorkForSinglePriorityLevel )(
- ICorSvcManager * This,
- /* [in] */ PriorityLevel priorityLevel,
- /* [in] */ BSTR pInterruptEventName,
- /* [out] */ BOOL *pWorkScheduled);
-
- HRESULT ( STDMETHODCALLTYPE *Optimize )(
- ICorSvcManager * This,
- /* [in] */ DWORD dwWorkerPriorityClass,
- /* [in] */ ICompileProgressNotification *pCompileProgressNotification,
- /* [in] */ BSTR pInterruptEventName);
-
- HRESULT ( STDMETHODCALLTYPE *NotifyService )(
- ICorSvcManager * This,
- ServiceNotification notification);
-
- HRESULT ( STDMETHODCALLTYPE *IsWorkAvailable )(
- ICorSvcManager * This,
- /* [in] */ PriorityLevel priorityLevel,
- /* [out] */ BOOL *pWorkAvailable);
-
- HRESULT ( STDMETHODCALLTYPE *Update )(
- ICorSvcManager * This,
- /* [in] */ UpdateFlags updateFlags,
- /* [in] */ BSTR pInterruptEventName);
-
- HRESULT ( STDMETHODCALLTYPE *SetSvcLogger )(
- ICorSvcManager * This,
- /* [in] */ ICorSvcLogger *pCorSvcLogger);
-
- END_INTERFACE
- } ICorSvcManagerVtbl;
-
- interface ICorSvcManager
- {
- CONST_VTBL struct ICorSvcManagerVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcManager_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcManager_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcManager_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcManager_ScheduleWorkForSinglePriorityLevel(This,priorityLevel,pInterruptEventName,pWorkScheduled) \
- ( (This)->lpVtbl -> ScheduleWorkForSinglePriorityLevel(This,priorityLevel,pInterruptEventName,pWorkScheduled) )
-
-#define ICorSvcManager_Optimize(This,dwWorkerPriorityClass,pCompileProgressNotification,pInterruptEventName) \
- ( (This)->lpVtbl -> Optimize(This,dwWorkerPriorityClass,pCompileProgressNotification,pInterruptEventName) )
-
-#define ICorSvcManager_NotifyService(This,notification) \
- ( (This)->lpVtbl -> NotifyService(This,notification) )
-
-#define ICorSvcManager_IsWorkAvailable(This,priorityLevel,pWorkAvailable) \
- ( (This)->lpVtbl -> IsWorkAvailable(This,priorityLevel,pWorkAvailable) )
-
-#define ICorSvcManager_Update(This,updateFlags,pInterruptEventName) \
- ( (This)->lpVtbl -> Update(This,updateFlags,pInterruptEventName) )
-
-#define ICorSvcManager_SetSvcLogger(This,pCorSvcLogger) \
- ( (This)->lpVtbl -> SetSvcLogger(This,pCorSvcLogger) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcManager_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcManager2_INTERFACE_DEFINED__
-#define __ICorSvcManager2_INTERFACE_DEFINED__
-
-/* interface ICorSvcManager2 */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcManager2;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("29626056-8031-441b-affa-7a82480058b3")
- ICorSvcManager2 : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE SetRuntimeVersion(
- BSTR version) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE SetPackageMoniker(
- BSTR moniker) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE SetLocalAppData(
- BSTR directory) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcManager2Vtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcManager2 * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcManager2 * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcManager2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *SetRuntimeVersion )(
- ICorSvcManager2 * This,
- BSTR version);
-
- HRESULT ( STDMETHODCALLTYPE *SetPackageMoniker )(
- ICorSvcManager2 * This,
- BSTR moniker);
-
- HRESULT ( STDMETHODCALLTYPE *SetLocalAppData )(
- ICorSvcManager2 * This,
- BSTR directory);
-
- END_INTERFACE
- } ICorSvcManager2Vtbl;
-
- interface ICorSvcManager2
- {
- CONST_VTBL struct ICorSvcManager2Vtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcManager2_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcManager2_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcManager2_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcManager2_SetRuntimeVersion(This,version) \
- ( (This)->lpVtbl -> SetRuntimeVersion(This,version) )
-
-#define ICorSvcManager2_SetPackageMoniker(This,moniker) \
- ( (This)->lpVtbl -> SetPackageMoniker(This,moniker) )
-
-#define ICorSvcManager2_SetLocalAppData(This,directory) \
- ( (This)->lpVtbl -> SetLocalAppData(This,directory) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcManager2_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcSetLegacyServiceBehavior_INTERFACE_DEFINED__
-#define __ICorSvcSetLegacyServiceBehavior_INTERFACE_DEFINED__
-
-/* interface ICorSvcSetLegacyServiceBehavior */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcSetLegacyServiceBehavior;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("35e5d609-ec3d-4fc2-9ba2-5f99e42ff42f")
- ICorSvcSetLegacyServiceBehavior : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE SetLegacyServiceBehavior( void) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcSetLegacyServiceBehaviorVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcSetLegacyServiceBehavior * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcSetLegacyServiceBehavior * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcSetLegacyServiceBehavior * This);
-
- HRESULT ( STDMETHODCALLTYPE *SetLegacyServiceBehavior )(
- ICorSvcSetLegacyServiceBehavior * This);
-
- END_INTERFACE
- } ICorSvcSetLegacyServiceBehaviorVtbl;
-
- interface ICorSvcSetLegacyServiceBehavior
- {
- CONST_VTBL struct ICorSvcSetLegacyServiceBehaviorVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcSetLegacyServiceBehavior_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcSetLegacyServiceBehavior_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcSetLegacyServiceBehavior_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcSetLegacyServiceBehavior_SetLegacyServiceBehavior(This) \
- ( (This)->lpVtbl -> SetLegacyServiceBehavior(This) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcSetLegacyServiceBehavior_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcSetTaskBootTriggerState_INTERFACE_DEFINED__
-#define __ICorSvcSetTaskBootTriggerState_INTERFACE_DEFINED__
-
-/* interface ICorSvcSetTaskBootTriggerState */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcSetTaskBootTriggerState;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("115466A4-7005-4CA3-971F-01F0A2C8EF09")
- ICorSvcSetTaskBootTriggerState : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE SetTaskBootTriggerState(
- BOOL bEnabled) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcSetTaskBootTriggerStateVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcSetTaskBootTriggerState * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcSetTaskBootTriggerState * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcSetTaskBootTriggerState * This);
-
- HRESULT ( STDMETHODCALLTYPE *SetTaskBootTriggerState )(
- ICorSvcSetTaskBootTriggerState * This,
- BOOL bEnabled);
-
- END_INTERFACE
- } ICorSvcSetTaskBootTriggerStateVtbl;
-
- interface ICorSvcSetTaskBootTriggerState
- {
- CONST_VTBL struct ICorSvcSetTaskBootTriggerStateVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcSetTaskBootTriggerState_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcSetTaskBootTriggerState_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcSetTaskBootTriggerState_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcSetTaskBootTriggerState_SetTaskBootTriggerState(This,bEnabled) \
- ( (This)->lpVtbl -> SetTaskBootTriggerState(This,bEnabled) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcSetTaskBootTriggerState_INTERFACE_DEFINED__ */
-
-
-#ifndef __ICorSvcSetTaskDelayStartTriggerState_INTERFACE_DEFINED__
-#define __ICorSvcSetTaskDelayStartTriggerState_INTERFACE_DEFINED__
-
-/* interface ICorSvcSetTaskDelayStartTriggerState */
-/* [unique][uuid][object] */
-
-
-EXTERN_C const IID IID_ICorSvcSetTaskDelayStartTriggerState;
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
- MIDL_INTERFACE("261DD1E3-F07E-4B8D-B54E-F26889413626")
- ICorSvcSetTaskDelayStartTriggerState : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE SetTaskDelayStartTriggerState(
- BOOL bEnabled) = 0;
-
- };
-
-
-#else /* C style interface */
-
- typedef struct ICorSvcSetTaskDelayStartTriggerStateVtbl
- {
- BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- ICorSvcSetTaskDelayStartTriggerState * This,
- /* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- _COM_Outptr_ void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- ICorSvcSetTaskDelayStartTriggerState * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
- ICorSvcSetTaskDelayStartTriggerState * This);
-
- HRESULT ( STDMETHODCALLTYPE *SetTaskDelayStartTriggerState )(
- ICorSvcSetTaskDelayStartTriggerState * This,
- BOOL bEnabled);
-
- END_INTERFACE
- } ICorSvcSetTaskDelayStartTriggerStateVtbl;
-
- interface ICorSvcSetTaskDelayStartTriggerState
- {
- CONST_VTBL struct ICorSvcSetTaskDelayStartTriggerStateVtbl *lpVtbl;
- };
-
-
-
-#ifdef COBJMACROS
-
-
-#define ICorSvcSetTaskDelayStartTriggerState_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
-
-#define ICorSvcSetTaskDelayStartTriggerState_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
-
-#define ICorSvcSetTaskDelayStartTriggerState_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
-
-
-#define ICorSvcSetTaskDelayStartTriggerState_SetTaskDelayStartTriggerState(This,bEnabled) \
- ( (This)->lpVtbl -> SetTaskDelayStartTriggerState(This,bEnabled) )
-
-#endif /* COBJMACROS */
-
-
-#endif /* C style interface */
-
-
-
-
-#endif /* __ICorSvcSetTaskDelayStartTriggerState_INTERFACE_DEFINED__ */
-
-
-/* Additional Prototypes for ALL interfaces */
-
-unsigned long __RPC_USER BSTR_UserSize( unsigned long *, unsigned long , BSTR * );
-unsigned char * __RPC_USER BSTR_UserMarshal( unsigned long *, unsigned char *, BSTR * );
-unsigned char * __RPC_USER BSTR_UserUnmarshal(unsigned long *, unsigned char *, BSTR * );
-void __RPC_USER BSTR_UserFree( unsigned long *, BSTR * );
-
-/* end of Additional Prototypes */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
-
set(PAL_ARCH_SOURCES_DIR amd64)
elseif(CLR_CMAKE_HOST_ARCH_ARM)
set(PAL_ARCH_SOURCES_DIR arm)
+elseif(CLR_CMAKE_HOST_ARCH_ARMV6)
+ set(PAL_ARCH_SOURCES_DIR arm)
+ add_definitions(-D__armv6__)
elseif(CLR_CMAKE_HOST_ARCH_ARM64)
set(PAL_ARCH_SOURCES_DIR arm64)
elseif(CLR_CMAKE_HOST_ARCH_I386)
cruntime/string.cpp
cruntime/stringtls.cpp
cruntime/wchar.cpp
- cruntime/wchartls.cpp
debug/debug.cpp
file/directory.cpp
file/file.cpp
init/pal.cpp
init/sxs.cpp
loader/module.cpp
- loader/modulename.cpp
locale/unicode.cpp
locale/unicodedata.cpp
locale/utf8.cpp
map/common.cpp
map/map.cpp
map/virtual.cpp
- memory/local.cpp
misc/dbgmsg.cpp
misc/environ.cpp
misc/error.cpp
safecrt/xtow_s.cpp
shmemory/shmemory.cpp
sync/cs.cpp
+ synchobj/mutex.cpp
+ synchmgr/synchcontrollers.cpp
+ synchmgr/synchmanager.cpp
+ synchmgr/wait.cpp
+ thread/process.cpp
thread/thread.cpp
+ thread/threadsusp.cpp
)
add_library(coreclrpal
free(newBuff);
return -1;
}
- ret = InternalFwrite(newBuff, 1, count, stream, &iError);
+ ret = InternalFwrite(newBuff, 1, nsize, stream, &iError);
if (iError != 0)
{
ERROR("InternalFwrite did not write the whole buffer. Error is %d\n", iError);
/*++
-
-
Module Name:
wchar.c
Implementation of wide char string functions.
-
-
--*/
-
#include "pal/palinternal.h"
#include "pal/cruntime.h"
#include "pal/dbgmsg.h"
#include "pal/thread.hpp"
#include "pal/threadsusp.hpp"
-
#if HAVE_CONFIG_H
#include "config.h"
#endif
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-/*++
-
-
-
-Module Name:
-
- wchartls.c
-
-Abstract:
-
- Implementation of wide char string functions that depend on per-thread data
-
-
-
---*/
-
-#include "pal/palinternal.h"
-#include "pal/thread.hpp"
-#include "pal/dbgmsg.h"
-
-using namespace CorUnix;
-
-
-SET_DEFAULT_DEBUG_CHANNEL(CRT);
-
-/*++
-Function:
- PAL_wcstok
-
-Finds the next token in a wide character string.
-
-Return value:
-
-A pointer to the next token found in strToken. Returns NULL when no more
-tokens are found. Each call modifies strToken by substituting a NULL
-character for each delimiter that is encountered.
-
-Parameters:
-strToken String containing token(s)
-strDelimit Set of delimiter characters
-
---*/
-WCHAR *
-__cdecl
-PAL_wcstok(WCHAR *strToken, const WCHAR *strDelimit)
-{
- CPalThread *pThread = NULL;
- WCHAR *retval = NULL;
- WCHAR *delim_ptr;
- WCHAR *next_context; /* string to save in TLS for future calls */
-
- PERF_ENTRY(wcstok);
- ENTRY("PAL_wcstok (strToken=%p (%S), strDelimit=%p (%S))\n",
- strToken?strToken:W16_NULLSTRING,
- strToken?strToken:W16_NULLSTRING,
- strDelimit?strDelimit:W16_NULLSTRING,
- strDelimit?strDelimit:W16_NULLSTRING);
-
- /* Get the per-thread buffer from the thread structure. */
- pThread = InternalGetCurrentThread();
-
- if(NULL == strDelimit)
- {
- ERROR("delimiter string is NULL\n");
- goto done;
- }
-
- /* get token string from TLS if none is provided */
- if(NULL == strToken)
- {
- TRACE("wcstok() called with NULL string, using previous string\n");
- strToken = pThread->crtInfo.wcstokContext;
- if(NULL == strToken)
- {
- ERROR("wcstok called with NULL string without a previous call\n");
- goto done;
- }
- }
-
- /* first, skip all leading delimiters */
- while ((*strToken != '\0') && (PAL_wcschr(strDelimit,*strToken)))
- {
- strToken++;
- }
-
- /* if there were only delimiters, there's no string */
- if('\0' == strToken[0])
- {
- TRACE("end of string already reached, returning NULL\n");
- goto done;
- }
-
- /* we're now at the beginning of the token; look for the first delimiter */
- delim_ptr = PAL_wcspbrk(strToken,strDelimit);
- if(NULL == delim_ptr)
- {
- TRACE("no delimiters found, this is the last token\n");
- /* place the next context at the end of the string, so that subsequent
- calls will return NULL */
- next_context = strToken+PAL_wcslen(strToken);
- retval = strToken;
- }
- else
- {
- /* null-terminate current token */
- *delim_ptr=0;
-
- /* place the next context right after the delimiter */
- next_context = delim_ptr+1;
- retval = strToken;
-
- TRACE("found delimiter; next token will be %p\n",next_context);
- }
-
- pThread->crtInfo.wcstokContext = next_context;
-
-done:
- LOGEXIT("PAL_wcstok() returns %p (%S)\n", retval?retval:W16_NULLSTRING, retval?retval:W16_NULLSTRING);
- PERF_EXIT(wcstok);
- return(retval);
-}
-
enum PalObjectTypeId
{
- otiAutoResetEvent = 0,
- otiManualResetEvent,
- otiMutex,
- otiNamedMutex,
- otiSemaphore,
- otiFile,
+ otiFile = 0,
otiFileMapping,
- otiSocket,
otiProcess,
otiThread,
- otiIOCompletionPort,
ObjectTypeIdCount // This entry must come last in the enumeration
};
};
extern IPalObjectManager *g_pObjectManager;
+
+ enum ThreadWakeupReason
+ {
+ WaitSucceeded,
+ MutexAbondoned,
+ WaitTimeout,
+ WaitFailed
+ };
+
+ class IPalSynchronizationManager
+ {
+ public:
+
+ //
+ // A thread calls BlockThread to put itself to sleep after it has
+ // registered itself with the objects it is to wait on. A thread
+ // need not have registered with any objects, as would occur in
+ // the implementation of Sleep[Ex].
+ //
+ // Needless to say a thread must not be holding any PAL locks
+ // directly or implicitly (e.g., by holding a reference to a
+ // synchronization controller) when it calls this method.
+ //
+
+ virtual
+ PAL_ERROR
+ BlockThread(
+ CPalThread *pCurrentThread,
+ DWORD dwTimeout,
+ bool fAlertable,
+ bool fIsSleep,
+ ThreadWakeupReason *peWakeupReason, // OUT
+ DWORD *pdwSignaledObject // OUT
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ AbandonObjectsOwnedByThread(
+ CPalThread *pCallingThread,
+ CPalThread *pTargetThread
+ ) = 0;
+
+ //
+ // This routine is primarily meant for use by WaitForMultipleObjects[Ex].
+ // The caller must individually release each of the returned controller
+ // interfaces.
+ //
+
+ virtual
+ PAL_ERROR
+ GetSynchWaitControllersForObjects(
+ CPalThread *pThread,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ ISynchWaitController *rgControllers[]
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ GetSynchStateControllersForObjects(
+ CPalThread *pThread,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ ISynchStateController *rgControllers[]
+ ) = 0;
+
+ //
+ // These following routines are meant for use only by IPalObject
+ // implementations. The first two routines are used to
+ // allocate and free an object's synchronization state; the third
+ // is called during object promotion.
+ //
+
+ virtual
+ PAL_ERROR
+ AllocateObjectSynchData(
+ CObjectType *pObjectType,
+ ObjectDomain eObjectDomain,
+ VOID **ppvSynchData // OUT
+ ) = 0;
+
+ virtual
+ void
+ FreeObjectSynchData(
+ CObjectType *pObjectType,
+ ObjectDomain eObjectDomain,
+ VOID *pvSynchData
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ PromoteObjectSynchData(
+ CPalThread *pThread,
+ VOID *pvLocalSynchData,
+ VOID **ppvSharedSynchData // OUT
+ ) = 0;
+
+ //
+ // The next two routines provide access to the process-wide
+ // synchronization lock
+ //
+
+ virtual
+ void
+ AcquireProcessLock(
+ CPalThread *pThread
+ ) = 0;
+
+ virtual
+ void
+ ReleaseProcessLock(
+ CPalThread *pThread
+ ) = 0;
+
+ //
+ // The final routines are used by IPalObject::GetSynchStateController
+ // and IPalObject::GetSynchWaitController
+ //
+
+ virtual
+ PAL_ERROR
+ CreateSynchStateController(
+ CPalThread *pThread, // IN, OPTIONAL
+ CObjectType *pObjectType,
+ VOID *pvSynchData,
+ ObjectDomain eObjectDomain,
+ ISynchStateController **ppStateController // OUT
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ CreateSynchWaitController(
+ CPalThread *pThread, // IN, OPTIONAL
+ CObjectType *pObjectType,
+ VOID *pvSynchData,
+ ObjectDomain eObjectDomain,
+ ISynchWaitController **ppWaitController // OUT
+ ) = 0;
+ };
+
+ extern IPalSynchronizationManager *g_pSynchronizationManager;
+
}
#endif // _CORUNIX_H
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-/*++
-
-
-
-Module Name:
-
- include/pal/modulename.h
-
-Abstract:
- Header file for functions to get the name of a module
-
-Revision History:
-
-
-
---*/
-
-#ifndef _PAL_MODULENAME_H_
-#define _PAL_MODULENAME_H_
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif // __cplusplus
-
-const char *PAL_dladdr(LPVOID ProcAddress);
-
-#ifdef __cplusplus
-}
-#endif // __cplusplus
-
-#endif /*_PAL_MODULENAME_H_*/
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+
+
+Module Name:
+
+ mutex.hpp
+
+Abstract:
+
+ Mutex object structure definition.
+
+
+
+--*/
+
+#ifndef _PAL_MUTEX_H_
+#define _PAL_MUTEX_H_
+
+#include "corunix.hpp"
+#include "sharedmemory.h"
+
+#include <pthread.h>
+
+#define SYNCSPINLOCK_F_ASYMMETRIC 1
+
+#define SPINLOCKInit(lock) (*(lock) = 0)
+#define SPINLOCKDestroy SPINLOCKInit
+
+void SPINLOCKAcquire (LONG * lock, unsigned int flags);
+void SPINLOCKRelease (LONG * lock);
+DWORD SPINLOCKTryAcquire (LONG * lock);
+
+#endif //_PAL_MUTEX_H_
calls to CreateThread from succeeding once shutdown has started
[defined in process.c]
*/
+extern Volatile<LONG> terminator;
// The process and session ID of this process, so we can avoid excessive calls to getpid() and getsid().
extern DWORD gPID;
extern DWORD gSID;
+
+/*++
+Function:
+ PROCGetProcessIDFromHandle
+
+Abstract
+ Return the process ID from a process handle
+--*/
+DWORD PROCGetProcessIDFromHandle(HANDLE hProcess);
+
+/*++
+Function:
+ PROCCleanupInitialProcess
+
+Abstract
+ Cleanup all the structures for the initial process.
+
+Parameter
+ VOID
+
+Return
+ VOID
+
+--*/
+VOID PROCCleanupInitialProcess(VOID);
+
+
+/*++
+Function:
+ PROCProcessLock
+
+Abstract
+ Enter the critical section associated to the current process
+--*/
+VOID PROCProcessLock(VOID);
+
+
+/*++
+Function:
+ PROCProcessUnlock
+
+Abstract
+ Leave the critical section associated to the current process
+--*/
+VOID PROCProcessUnlock(VOID);
+
/*++
Function:
PROCAbort()
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/procobj.hpp
+
+Abstract:
+ Header file for process structures
+
+
+
+--*/
+
+#ifndef _PAL_PROCOBJ_HPP_
+#define _PAL_PROCOBJ_HPP_
+
+#include "corunix.hpp"
+
+namespace CorUnix
+{
+ extern CObjectType otProcess;
+
+ typedef enum
+ {
+ PS_IDLE,
+ PS_STARTING,
+ PS_RUNNING,
+ PS_DONE
+ } PROCESS_STATE;
+
+ //
+ // Struct for process module list (EnumProcessModules)
+ //
+ struct ProcessModules
+ {
+ ProcessModules *Next;
+ PVOID BaseAddress;
+ CHAR Name[0];
+ };
+
+ //
+ // Ideally dwProcessId would be part of the process object's immutable
+ // data. Doing so, though, creates complications in CreateProcess. The
+ // contents of the immutable data for a new object must be set before
+ // that object is registered with the object manager (as the object
+ // manager may make a copy of the immutable data). The PID for a new
+ // process, though, is not known until after creation. Registering the
+ // process object after process creation creates an undesirable error path
+ // -- if we are not able to register the process object (say, because of
+ // a low resource condition) we would be forced to return an error to
+ // the caller of CreateProcess, even though the new process was actually
+ // created...
+ //
+ // Note: we could work around this by effectively always going down
+ // the create suspended path. That is, the new process would not exec until
+ // the parent process released it. It's unclear how much benefit this would
+ // provide us.
+ //
+
+ class CProcProcessLocalData
+ {
+ public:
+ CProcProcessLocalData()
+ :
+ dwProcessId(0),
+ ps(PS_IDLE),
+ dwExitCode(0),
+ lAttachCount(0),
+ pProcessModules(NULL),
+ cProcessModules(0)
+ {
+ };
+
+ ~CProcProcessLocalData();
+
+ DWORD dwProcessId;
+ PROCESS_STATE ps;
+ DWORD dwExitCode;
+ LONG lAttachCount;
+ ProcessModules *pProcessModules;
+ DWORD cProcessModules;
+ };
+
+ PAL_ERROR
+ InternalCreateProcess(
+ CPalThread *pThread,
+ LPCWSTR lpApplicationName,
+ LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation
+ );
+
+ PAL_ERROR
+ InitializeProcessData(
+ void
+ );
+
+ PAL_ERROR
+ InitializeProcessCommandLine(
+ LPWSTR lpwstrCmdLine,
+ LPWSTR lpwstrFullPath
+ );
+
+ PAL_ERROR
+ CreateInitialProcessAndThreadObjects(
+ CPalThread *pThread
+ );
+
+ extern IPalObject *g_pobjProcess;
+}
+
+#endif // _PAL_PROCOBJ_HPP_
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/synchcache.hpp
+
+Abstract:
+ Simple look-aside cache for unused objects with default
+ constructor or no constructor
+
+
+
+--*/
+
+#ifndef _SYNCH_CACHE_H_
+#define _SYNCH_CACHE_H_
+
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+
+namespace CorUnix
+{
+ template <typename T> class CSynchCache
+ {
+ typedef union _USynchCacheStackNode
+ {
+ union _USynchCacheStackNode * next;
+ BYTE objraw[sizeof(T)];
+ } USynchCacheStackNode;
+
+ static const int MaxDepth = 256;
+
+ Volatile<USynchCacheStackNode*> m_pHead;
+ CRITICAL_SECTION m_cs;
+ Volatile<int> m_iDepth;
+ int m_iMaxDepth;
+#ifdef _DEBUG
+ int m_iMaxTrackedDepth;
+#endif
+
+ void Lock(CPalThread * pthrCurrent)
+ { InternalEnterCriticalSection(pthrCurrent, &m_cs); }
+ void Unlock(CPalThread * pthrCurrent)
+ { InternalLeaveCriticalSection(pthrCurrent, &m_cs); }
+
+ public:
+ CSynchCache(int iMaxDepth = MaxDepth) :
+ m_pHead(NULL),
+ m_iDepth(0),
+ m_iMaxDepth(iMaxDepth)
+#ifdef _DEBUG
+ ,m_iMaxTrackedDepth(0)
+#endif
+ {
+ InternalInitializeCriticalSection(&m_cs);
+ if (m_iMaxDepth < 0)
+ {
+ m_iMaxDepth = 0;
+ }
+ }
+
+ ~CSynchCache()
+ {
+ Flush(NULL, true);
+ InternalDeleteCriticalSection(&m_cs);
+ }
+
+#ifdef _DEBUG
+ int GetMaxTrackedDepth() { return m_iMaxTrackedDepth; }
+#endif
+
+ T * Get(CPalThread * pthrCurrent)
+ {
+ T * pObj = NULL;
+
+ Get(pthrCurrent, 1, &pObj);
+ return pObj;
+ }
+
+ int Get(CPalThread * pthrCurrent, int n, T ** ppObjs)
+ {
+ void * pvObjRaw;
+ USynchCacheStackNode * pNode;
+ int i = 0,j;
+
+ Lock(pthrCurrent);
+ pNode = m_pHead;
+ while (pNode && i < n)
+ {
+ ppObjs[i] = (T *)pNode;
+ pNode = pNode->next;
+ i++;
+ }
+ m_pHead = pNode;
+ m_iDepth -= i;
+
+#ifdef _DEBUG
+ if (NULL == m_pHead && m_iDepth != 0)
+ {
+ // Can't use ASSERT here, since this is header
+ // is included by other headers with inline methods
+ // which causes template instatiation in the header
+ // where the DEBUG CHANNEL is not defined and cannot
+ // be defined
+ fprintf(stderr,"SYNCCACHE: Invalid cache depth value");
+ DebugBreak();
+ }
+#endif // _DEBUG
+
+ Unlock(pthrCurrent);
+
+ for (j=i;j<n;j++)
+ {
+ pvObjRaw = (void *) InternalNew<USynchCacheStackNode>();
+ if (NULL == pvObjRaw)
+ break;
+#ifdef _DEBUG
+ memset(pvObjRaw, 0, sizeof(USynchCacheStackNode));
+#endif
+ ppObjs[j] = reinterpret_cast<T*>(pvObjRaw);
+ }
+
+ for (i=0;i<j;i++)
+ {
+ new ((void *)ppObjs[i]) T;
+ }
+
+ return j;
+ }
+
+ void Add(CPalThread * pthrCurrent, T * pobj)
+ {
+ USynchCacheStackNode * pNode = reinterpret_cast<USynchCacheStackNode *>(pobj);
+
+ if (NULL == pobj)
+ {
+ return;
+ }
+
+ pobj->~T();
+
+ Lock(pthrCurrent);
+ if (m_iDepth < m_iMaxDepth)
+ {
+#ifdef _DEBUG
+ if (m_iDepth > m_iMaxTrackedDepth)
+ {
+ m_iMaxTrackedDepth = m_iDepth;
+ }
+#endif
+ pNode->next = m_pHead;
+ m_pHead = pNode;
+ m_iDepth++;
+ }
+ else
+ {
+ InternalDelete((char *)pNode);
+ }
+ Unlock(pthrCurrent);
+ }
+
+ void Flush(CPalThread * pthrCurrent, bool fDontLock = false)
+ {
+ USynchCacheStackNode * pNode, * pTemp;
+
+ if (!fDontLock)
+ {
+ Lock(pthrCurrent);
+ }
+ pNode = m_pHead;
+ m_pHead = NULL;
+ m_iDepth = 0;
+ if (!fDontLock)
+ {
+ Unlock(pthrCurrent);
+ }
+
+ while (pNode)
+ {
+ pTemp = pNode;
+ pNode = pNode->next;
+ InternalDelete((char *)pTemp);
+ }
+ }
+ };
+
+ template <typename T> class CSHRSynchCache
+ {
+ union _USHRSynchCacheStackNode; // fwd declaration
+ typedef struct _SHRCachePTRs
+ {
+ union _USHRSynchCacheStackNode * pNext;
+ SharedID shrid;
+ } SHRCachePTRs;
+ typedef union _USHRSynchCacheStackNode
+ {
+ SHRCachePTRs pointers;
+ BYTE objraw[sizeof(T)];
+ } USHRSynchCacheStackNode;
+
+ static const int MaxDepth = 256;
+ static const int PreAllocFactor = 10; // Everytime a Get finds no available
+ // cached raw intances, it preallocates
+ // MaxDepth/PreAllocFactor new raw
+ // instances and store them into the
+ // cache before continuing
+
+ Volatile<USHRSynchCacheStackNode*> m_pHead;
+ CRITICAL_SECTION m_cs;
+ Volatile<int> m_iDepth;
+ int m_iMaxDepth;
+#ifdef _DEBUG
+ int m_iMaxTrackedDepth;
+#endif
+
+ void Lock(CPalThread * pthrCurrent)
+ { InternalEnterCriticalSection(pthrCurrent, &m_cs); }
+ void Unlock(CPalThread * pthrCurrent)
+ { InternalLeaveCriticalSection(pthrCurrent, &m_cs); }
+
+ public:
+ CSHRSynchCache(int iMaxDepth = MaxDepth) :
+ m_pHead(NULL),
+ m_iDepth(0),
+ m_iMaxDepth(iMaxDepth)
+#ifdef _DEBUG
+ ,m_iMaxTrackedDepth(0)
+#endif
+ {
+ InternalInitializeCriticalSection(&m_cs);
+ if (m_iMaxDepth < 0)
+ {
+ m_iMaxDepth = 0;
+ }
+ }
+
+ ~CSHRSynchCache()
+ {
+ Flush(NULL, true);
+ InternalDeleteCriticalSection(&m_cs);
+ }
+
+#ifdef _DEBUG
+ int GetMaxTrackedDepth() { return m_iMaxTrackedDepth; }
+#endif
+
+ SharedID Get(CPalThread * pthrCurrent)
+ {
+ SharedID shridObj = NULL;
+
+ Get(pthrCurrent, 1, &shridObj);
+ return shridObj;
+ }
+
+ int Get(CPalThread * pthrCurrent, int n, SharedID * shridpObjs)
+ {
+ SharedID shridObj;
+ void * pvObjRaw = NULL;
+ USHRSynchCacheStackNode * pNode;
+ int i = 0, j, k;
+
+ Lock(pthrCurrent);
+ pNode = m_pHead;
+ while (pNode && i < n)
+ {
+ shridpObjs[i] = pNode->pointers.shrid;
+ pvObjRaw = (void *)pNode;
+ pNode = pNode->pointers.pNext;
+ i++;
+ }
+ m_pHead = pNode;
+ m_iDepth -= i;
+
+#ifdef _DEBUG
+ if (NULL == m_pHead && m_iDepth != 0)
+ {
+ // Can't use ASSERT here, since this is header
+ // (see comment above)
+ fprintf(stderr,"SYNCCACHE: Invalid cache depth value");
+ DebugBreak();
+ }
+#endif // _DEBUG
+
+ if (0 == m_iDepth)
+ {
+ for (k=0; k<m_iMaxDepth/PreAllocFactor-n+i; k++)
+ {
+ shridObj = malloc(sizeof(USHRSynchCacheStackNode));
+ if (NULL == shridObj)
+ {
+ Flush(pthrCurrent, true);
+ break;
+ }
+ pNode = SharedIDToTypePointer(USHRSynchCacheStackNode, shridObj);
+#ifdef _DEBUG
+ memset(reinterpret_cast<void*>(pNode), 0, sizeof(USHRSynchCacheStackNode));
+#endif
+ pNode->pointers.shrid = shridObj;
+ pNode->pointers.pNext = m_pHead;
+ m_pHead = pNode;
+ m_iDepth++;
+ }
+ }
+
+ Unlock(pthrCurrent);
+
+ for (j=i;j<n;j++)
+ {
+ shridObj = malloc(sizeof(USHRSynchCacheStackNode));
+ if (NULL == shridObj)
+ break;
+#ifdef _DEBUG
+ pvObjRaw = SharedIDToPointer(shridObj);
+ memset(pvObjRaw, 0, sizeof(USHRSynchCacheStackNode));
+#endif
+ shridpObjs[j] = shridObj;
+ }
+
+ for (i=0;i<j;i++)
+ {
+ pvObjRaw = SharedIDToPointer(shridpObjs[i]);
+ new (pvObjRaw) T;
+ }
+
+ return j;
+ }
+
+ void Add(CPalThread * pthrCurrent, SharedID shridObj)
+ {
+ if (NULL == shridObj)
+ {
+ return;
+ }
+
+ USHRSynchCacheStackNode * pNode = SharedIDToTypePointer(USHRSynchCacheStackNode, shridObj);
+ T * pObj = reinterpret_cast<T *>(pNode);
+
+ pObj->~T();
+
+ pNode->pointers.shrid = shridObj;
+
+ Lock(pthrCurrent);
+ if (m_iDepth < m_iMaxDepth)
+ {
+ m_iDepth++;
+#ifdef _DEBUG
+ if (m_iDepth > m_iMaxTrackedDepth)
+ {
+ m_iMaxTrackedDepth = m_iDepth;
+ }
+#endif
+ pNode->pointers.pNext = m_pHead;
+ m_pHead = pNode;
+ }
+ else
+ {
+ free(shridObj);
+ }
+ Unlock(pthrCurrent);
+ }
+
+ void Flush(CPalThread * pthrCurrent, bool fDontLock = false)
+ {
+ USHRSynchCacheStackNode * pNode, * pTemp;
+ SharedID shridTemp;
+
+ if (!fDontLock)
+ {
+ Lock(pthrCurrent);
+ }
+ pNode = m_pHead;
+ m_pHead = NULL;
+ m_iDepth = 0;
+ if (!fDontLock)
+ {
+ Unlock(pthrCurrent);
+ }
+
+ while (pNode)
+ {
+ pTemp = pNode;
+ pNode = pNode->pointers.pNext;
+ shridTemp = pTemp->pointers.shrid;
+ free(shridTemp);
+ }
+ }
+ };
+}
+
+#endif // _SYNCH_CACHE_H_
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/synchobjects.hpp
+
+Abstract:
+ Header file for synchronization manager and controllers
+
+
+
+--*/
+
+#ifndef _SINCHOBJECTS_HPP_
+#define _SINCHOBJECTS_HPP_
+
+#include "corunix.hpp"
+#include "threadinfo.hpp"
+#include "mutex.hpp"
+#include "shm.hpp"
+#include "list.h"
+
+#include <pthread.h>
+
+#define SharedID SHMPTR
+#define SharedIDToPointer(shID) SHMPTR_TO_TYPED_PTR(PVOID, shID)
+#define SharedIDToTypePointer(TYPE,shID) SHMPTR_TO_TYPED_PTR(TYPE, shID)
+
+namespace CorUnix
+{
+ DWORD InternalWaitForMultipleObjectsEx(
+ CPalThread * pthrCurrent,
+ DWORD nCount,
+ CONST HANDLE *lpHandles,
+ BOOL bWaitAll,
+ DWORD dwMilliseconds,
+ BOOL bAlertable,
+ BOOL bPrioritize = FALSE);
+
+ enum THREAD_STATE
+ {
+ TS_IDLE,
+ TS_STARTING,
+ TS_RUNNING,
+ TS_FAILED,
+ TS_DONE,
+ };
+
+ // forward declarations
+ struct _ThreadWaitInfo;
+ struct _WaitingThreadsListNode;
+ class CSynchData;
+
+ typedef struct _WaitingThreadsListNode * PWaitingThreadsListNode;
+ typedef struct _OwnedObjectsListNode * POwnedObjectsListNode;
+ typedef struct _ThreadApcInfoNode * PThreadApcInfoNode;
+
+ typedef struct _ThreadWaitInfo
+ {
+ WaitType wtWaitType;
+ WaitDomain wdWaitDomain;
+ LONG lObjCount;
+ LONG lSharedObjCount;
+ CPalThread * pthrOwner;
+ PWaitingThreadsListNode rgpWTLNodes[MAXIMUM_WAIT_OBJECTS];
+
+ _ThreadWaitInfo() : wtWaitType(SingleObject), wdWaitDomain(LocalWait),
+ lObjCount(0), lSharedObjCount(0),
+ pthrOwner(NULL) {}
+ } ThreadWaitInfo;
+
+ typedef struct _ThreadNativeWaitData
+ {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int iPred;
+ DWORD dwObjectIndex;
+ ThreadWakeupReason twrWakeupReason;
+ bool fInitialized;
+
+ _ThreadNativeWaitData() :
+ iPred(0),
+ dwObjectIndex(0),
+ twrWakeupReason(WaitSucceeded),
+ fInitialized(false)
+ {
+ }
+
+ ~_ThreadNativeWaitData();
+ } ThreadNativeWaitData;
+
+ class CThreadSynchronizationInfo : public CThreadInfoInitializer
+ {
+ friend class CPalSynchronizationManager;
+ friend class CSynchWaitController;
+
+ THREAD_STATE m_tsThreadState;
+ SharedID m_shridWaitAwakened;
+ Volatile<LONG> m_lLocalSynchLockCount;
+ Volatile<LONG> m_lSharedSynchLockCount;
+ LIST_ENTRY m_leOwnedObjsList;
+
+ ThreadNativeWaitData m_tnwdNativeData;
+ ThreadWaitInfo m_twiWaitInfo;
+
+#ifdef SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ static const int PendingSignalingsArraySize = 10;
+ LONG m_lPendingSignalingCount;
+ CPalThread * m_rgpthrPendingSignalings[PendingSignalingsArraySize];
+ LIST_ENTRY m_lePendingSignalingsOverflowList;
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+
+ public:
+
+ CThreadSynchronizationInfo();
+ virtual ~CThreadSynchronizationInfo();
+
+ //
+ // CThreadInfoInitializer methods
+ //
+ virtual PAL_ERROR InitializePreCreate(void);
+
+ virtual PAL_ERROR InitializePostCreate(
+ CPalThread *pthrCurrent,
+ SIZE_T threadId,
+ DWORD dwLwpId
+ );
+
+ THREAD_STATE GetThreadState(void)
+ {
+ return m_tsThreadState;
+ };
+
+ void SetThreadState(THREAD_STATE tsThreadState)
+ {
+ m_tsThreadState = tsThreadState;
+ };
+
+ ThreadNativeWaitData * GetNativeData()
+ {
+ return &m_tnwdNativeData;
+ }
+
+#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ PAL_ERROR RunDeferredThreadConditionSignalings();
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+
+ // NOTE: the following methods provide non-synchronized access to
+ // the list of owned objects for this thread. Any thread
+ // accessing this list MUST own the appropriate
+ // synchronization lock(s).
+ void AddObjectToOwnedList(POwnedObjectsListNode pooln);
+ void RemoveObjectFromOwnedList(POwnedObjectsListNode pooln);
+ POwnedObjectsListNode RemoveFirstObjectFromOwnedList(void);
+
+ // The following methods provide access to the native wait lock for
+ // those implementations that need a lock to protect the support for
+ // native thread blocking (e.g.: pthread conditions)
+ void AcquireNativeWaitLock(void);
+ void ReleaseNativeWaitLock(void);
+ bool TryAcquireNativeWaitLock(void);
+ };
+
+ class CPalSynchMgrController
+ {
+ public:
+ static IPalSynchronizationManager * CreatePalSynchronizationManager();
+
+ static PAL_ERROR StartWorker(CPalThread * pthrCurrent);
+
+ static PAL_ERROR PrepareForShutdown(void);
+
+ static PAL_ERROR Shutdown(CPalThread *pthrCurrent, bool fFullCleanup);
+ };
+}
+
+#endif // _SINCHOBJECTS_HPP_
+
#include "threadsusp.hpp"
#include "threadinfo.hpp"
+#include "synchobjects.hpp"
#include <errno.h>
namespace CorUnix
SignalHandlerThread
};
+ PAL_ERROR
+ InternalCreateThread(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ DWORD dwStackSize,
+ LPTHREAD_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter,
+ DWORD dwCreationFlags,
+ PalThreadType eThreadType,
+ SIZE_T* pThreadId,
+ HANDLE *phThread
+ );
+
+ PAL_ERROR
+ InternalGetThreadDataFromHandle(
+ CPalThread *pThread,
+ HANDLE hThread,
+ CPalThread **ppTargetThread,
+ IPalObject **ppobjThread
+ );
+
+ VOID
+ InternalEndCurrentThread(
+ CPalThread *pThread
+ );
+
+ PAL_ERROR
+ InternalCreateDummyThread(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ CPalThread **ppDummyThread,
+ HANDLE *phThread
+ );
+
+
PAL_ERROR
CreateThreadData(
CPalThread **ppThread
);
+ PAL_ERROR
+ CreateThreadObject(
+ CPalThread *pThread,
+ CPalThread *pNewThread,
+ HANDLE *phThread
+ );
+
/* In the windows CRT there is a constant defined for the max width
of a _ecvt conversion. That constant is 348. 348 for the value, plus
the exponent value, decimal, and sign if required. */
class CPalThread
{
+ friend
+ PAL_ERROR
+ InternalCreateThread(
+ CPalThread *,
+ LPSECURITY_ATTRIBUTES,
+ DWORD,
+ LPTHREAD_START_ROUTINE,
+ LPVOID,
+ DWORD,
+ PalThreadType,
+ SIZE_T*,
+ HANDLE*
+ );
+
+ friend
+ PAL_ERROR
+ InternalCreateDummyThread(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ CPalThread **ppDummyThread,
+ HANDLE *phThread
+ );
+
friend
PAL_ERROR
CreateThreadData(
CPalThread **ppThread
);
+ friend
+ PAL_ERROR
+ CreateThreadObject(
+ CPalThread *pThread,
+ CPalThread *pNewThread,
+ HANDLE *phThread
+ );
+
private:
CPalThread *m_pNext;
+ DWORD m_dwExitCode;
+ BOOL m_fExitCodeSet;
CRITICAL_SECTION m_csLock;
bool m_fLockInitialized;
bool m_fIsDummy;
LONG m_lRefCount;
+ //
+ // The IPalObject for this thread. The thread will release its reference
+ // to this object when it exits.
+ //
+
+ IPalObject *m_pThreadObject;
+
//
// Thread ID info
//
DWORD m_dwLwpId;
pthread_t m_pthreadSelf;
+ //
+ // Start info
+ //
+
+ LPTHREAD_START_ROUTINE m_lpStartAddress;
+ LPVOID m_lpStartParameter;
+ BOOL m_bCreateSuspended;
+ PalThreadType m_eThreadType;
+
+ //
+ // pthread mutex / condition variable for gating thread startup.
+ // InternalCreateThread waits on the condition variable to determine
+ // when the new thread has reached passed all failure points in
+ // the entry routine
+ //
+
+ pthread_mutex_t m_startMutex;
+ pthread_cond_t m_startCond;
+ bool m_fStartItemsInitialized;
+ bool m_fStartStatus;
+ bool m_fStartStatusSet;
+
+ // Base address of the stack of this thread
+ void* m_stackBase;
+ // Limit address of the stack of this thread
+ void* m_stackLimit;
+ // Signal handler's alternate stack to help with stack overflow
+ void* m_alternateStack;
+
+ //
+ // The thread entry routine (called from InternalCreateThread)
+ //
+
+ static void* ThreadEntry(void * pvParam);
+
+ //
+ // Data for PAL side-by-side support
+ //
+
public:
//
// Embedded information for areas owned by other subsystems
//
+ CThreadSynchronizationInfo synchronizationInfo;
+ CThreadSuspensionInfo suspensionInfo;
CThreadCRTInfo crtInfo;
CPalThread()
:
m_pNext(NULL),
+ m_dwExitCode(STILL_ACTIVE),
+ m_fExitCodeSet(FALSE),
m_fLockInitialized(FALSE),
+ m_fIsDummy(FALSE),
m_lRefCount(1),
+ m_pThreadObject(NULL),
m_threadId(0),
m_dwLwpId(0),
- m_pthreadSelf(0)
+ m_pthreadSelf(0),
+ m_lpStartAddress(NULL),
+ m_lpStartParameter(NULL),
+ m_bCreateSuspended(FALSE),
+ m_eThreadType(UserCreatedThread),
+ m_fStartItemsInitialized(FALSE),
+ m_fStartStatus(FALSE),
+ m_fStartStatusSet(FALSE),
+ m_stackBase(NULL),
+ m_stackLimit(NULL)
{
};
void
);
+ //
+ // SetStartStatus is called by THREADEntry or InternalSuspendNewThread
+ // to inform InternalCreateThread of the results of the thread's
+ // initialization. InternalCreateThread calls WaitForStartStatus to
+ // obtain this information (and will not return to its caller until
+ // the info is available).
+ //
+
+ void
+ SetStartStatus(
+ bool fStartSucceeded
+ );
+
+ bool
+ WaitForStartStatus(
+ void
+ );
+
void
Lock(
CPalThread *pThread
InternalLeaveCriticalSection(pThread, &m_csLock);
};
+ //
+ // The following three methods provide access to the
+ // native lock used to protect thread native wait data.
+ //
+
+ void
+ AcquireNativeWaitLock(
+ void
+ )
+ {
+ synchronizationInfo.AcquireNativeWaitLock();
+ }
+
+ void
+ ReleaseNativeWaitLock(
+ void
+ )
+ {
+ synchronizationInfo.ReleaseNativeWaitLock();
+ }
+
+ bool
+ TryAcquireNativeWaitLock(
+ void
+ )
+ {
+ return synchronizationInfo.TryAcquireNativeWaitLock();
+ }
+
static void
SetLastError(
DWORD dwLastError
return errno;
};
+ void
+ SetExitCode(
+ DWORD dwExitCode
+ )
+ {
+ m_dwExitCode = dwExitCode;
+ m_fExitCodeSet = TRUE;
+ };
+
+ BOOL
+ GetExitCode(
+ DWORD *pdwExitCode
+ )
+ {
+ *pdwExitCode = m_dwExitCode;
+ return m_fExitCodeSet;
+ };
+
SIZE_T
GetThreadId(
void
return m_pthreadSelf;
};
+ LPTHREAD_START_ROUTINE
+ GetStartAddress(
+ void
+ )
+ {
+ return m_lpStartAddress;
+ };
+
+ LPVOID
+ GetStartParameter(
+ void
+ )
+ {
+ return m_lpStartParameter;
+ };
+
+ BOOL
+ GetCreateSuspended(
+ void
+ )
+ {
+ return m_bCreateSuspended;
+ };
+
+ PalThreadType
+ GetThreadType(
+ void
+ )
+ {
+ return m_eThreadType;
+ };
+
+ IPalObject *
+ GetThreadObject(
+ void
+ )
+ {
+ return m_pThreadObject;
+ }
+ BOOL
+ IsDummy(
+ void
+ )
+ {
+ return m_fIsDummy;
+ };
+
CPalThread*
GetNext(
void
pThread = CreateCurrentThreadData();
return pThread;
}
+
+/***
+
+ $$TODO: These are needed only to support cross-process thread duplication
+
+ class CThreadImmutableData
+ {
+ public:
+ DWORD dwProcessId;
+ };
+
+ class CThreadSharedData
+ {
+ public:
+ DWORD dwThreadId;
+ DWORD dwExitCode;
+ };
+***/
+
+ //
+ // The process local information for a thread is just a pointer
+ // to the underlying CPalThread object.
+ //
+
+ class CThreadProcessLocalData
+ {
+ public:
+ CPalThread *pThread;
+ };
+
+ extern CObjectType otThread;
}
BOOL
// Need this ifdef since this header is included by .c files so they can use the diagnostic function.
#ifdef __cplusplus
+// Note: do not include malloc.hpp from this header. The template InternalDelete
+// needs to know the layout of class CPalThread, which includes a member of type
+// CThreadSuspensionInfo, which is defined later in this header, and it is not
+// yet known at this point.
+// If any future change should bring this issue back, the circular dependency can
+// be further broken by making the InternalDelete's CPalThread argument a
+// templatized argument, so that type checking on it takes place only at
+// instantiation time.
#include "pal/threadinfo.hpp"
#include "pal/thread.hpp"
#include "pal/printfcpp.hpp"
+#include "pal/mutex.hpp"
#include "pal/init.h"
#include <signal.h>
-#include "pal/sharedmemory.h"
+#include <semaphore.h>
+#include <sched.h>
+// We have a variety of options for synchronizing thread suspensions and resumptions between the requestor and
+// target threads. Analyze the various capabilities given to us by configure and define one of three macros
+// here for simplicity:
+// USE_POSIX_SEMAPHORES
+// USE_SYSV_SEMAPHORES
+// USE_PTHREAD_CONDVARS
+#if HAS_POSIX_SEMAPHORES
+
+// Favor posix semaphores.
+#define USE_POSIX_SEMAPHORES 1
+
+#if HAVE_SYS_SEMAPHORE_H
+#include <sys/semaphore.h>
+#elif HAVE_SEMAPHORE_H
+#include <semaphore.h>
+#endif // HAVE_SYS_SEMAPHORE_H
+
+#elif HAS_PTHREAD_MUTEXES && HAVE_MACH_EXCEPTIONS
+
+// Can only use the pthread solution if we're not using signals since pthread mutexes are not signal safe.
+#define USE_PTHREAD_CONDVARS 1
+
+#include <pthread.h>
+
+#elif HAS_SYSV_SEMAPHORES
+
+// SYSV semaphores are our last choice since they're shared across processes so it's possible to leak them
+// on abnormal process termination.
+#define USE_SYSV_SEMAPHORES 1
+
+#include <sys/sem.h>
+#include <sys/types.h>
+
+#else
+#error "Don't know how to synchronize thread suspends and resumes on this platform"
+#endif // HAS_POSIX_SEMAPHORES
+
+#include <stdarg.h>
+
+namespace CorUnix
+{
+#ifdef _DEBUG
+#define MAX_TRACKED_CRITSECS 8
+#endif
+
+ PAL_ERROR
+ InternalResumeThread(
+ CPalThread *pthrResumer,
+ HANDLE hTarget,
+ DWORD *pdwSuspendCount
+ );
+
+ class CThreadSuspensionInfo : public CThreadInfoInitializer
+ {
+ private:
+ BOOL m_fPending; // TRUE if a suspension is pending on a thread (because the thread is in an unsafe region)
+ BOOL m_fSelfsusp; // TRUE if thread is self suspending and while thread is self suspended
+ BOOL m_fSuspendedForShutdown; // TRUE once the thread is suspended during PAL cleanup
+ int m_nBlockingPipe; // blocking pipe used for a process that was created suspended
+#ifdef _DEBUG
+ Volatile<LONG> m_lNumThreadsSuspendedByThisThread; // number of threads that this thread has suspended; used for suspension diagnostics
+#endif
+#if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ int m_nSpinlock; // thread's suspension spinlock, which is used to synchronize suspension and resumption attempts
+#else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ pthread_mutex_t m_ptmSuspmutex; // thread's suspension mutex, which is used to synchronize suspension and resumption attempts
+ BOOL m_fSuspmutexInitialized;
+#endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+#if USE_POSIX_SEMAPHORES
+ sem_t m_semSusp; // suspension semaphore
+ sem_t m_semResume; // resumption semaphore
+ BOOL m_fSemaphoresInitialized;
+#elif USE_SYSV_SEMAPHORES
+ // necessary id's and sembuf structures for SysV semaphores
+ int m_nSemsuspid; // id for the suspend semaphore
+ int m_nSemrespid; // id for the resume semaphore
+ struct sembuf m_sbSemwait; // struct representing a wait operation
+ struct sembuf m_sbSempost; // struct representing a post operation
+#elif USE_PTHREAD_CONDVARS
+ pthread_cond_t m_condSusp; // suspension condition variable
+ pthread_mutex_t m_mutexSusp; // mutex associated with the condition above
+ BOOL m_fSuspended; // set to true once the suspend has been acknowledged
+
+ pthread_cond_t m_condResume; // resumption condition variable
+ pthread_mutex_t m_mutexResume; // mutex associated with the condition above
+ BOOL m_fResumed; // set to true once the resume has been acknowledged
+
+ BOOL m_fSemaphoresInitialized;
+#endif // USE_POSIX_SEMAPHORES
+
+ /* Most of the variables above are either accessed by a thread
+ holding the appropriate suspension mutex(es) or are only
+ accessed by their own threads (and thus don't require
+ synchronization).
+
+ m_fPending, m_fSuspendedForShutdown,
+ m_fSuspendSignalSent, and m_fResumeSignalSent
+ may be set by a different thread than the owner and thus
+ require synchronization.
+
+ m_fSelfsusp is set to TRUE only by its own thread but may be later
+ accessed by other threads.
+
+ m_lNumThreadsSuspendedByThisThread is accessed by its owning
+ thread and therefore does not require synchronization. */
+ VOID
+ AcquireSuspensionLocks(
+ CPalThread *pthrSuspender,
+ CPalThread *pthrTarget
+ );
+
+ VOID
+ ReleaseSuspensionLocks(
+ CPalThread *pthrSuspender,
+ CPalThread *pthrTarget
+ );
+
+#if USE_POSIX_SEMAPHORES
+ sem_t*
+ GetSuspendSemaphore(
+ void
+ )
+ {
+ return &m_semSusp;
+ };
+
+ sem_t*
+ GetResumeSemaphore(
+ void
+ )
+ {
+ return &m_semResume;
+ };
+#elif USE_SYSV_SEMAPHORES
+ int
+ GetSuspendSemaphoreId(
+ void
+ )
+ {
+ return m_nSemsuspid;
+ };
+
+ sembuf*
+ GetSemaphorePostBuffer(
+ void
+ )
+ {
+ return &m_sbSempost;
+ };
+#endif // USE_POSIX_SEMAPHORES
+
+#if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ LONG*
+ GetSuspensionSpinlock(
+ void
+ )
+ {
+ return &m_nSpinlock;
+ }
+#else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ pthread_mutex_t*
+ GetSuspensionMutex(
+ void
+ )
+ {
+ return &m_ptmSuspmutex;
+ }
+#endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+
+ void
+ SetSelfSusp(
+ BOOL fSelfsusp
+ )
+ {
+ m_fSelfsusp = fSelfsusp;
+ };
+
+ BOOL
+ GetSelfSusp(
+ void
+ )
+ {
+ return m_fSelfsusp;
+ };
+
+ static
+ BOOL
+ TryAcquireSuspensionLock(
+ CPalThread* pthrTarget
+ );
+
+ int GetBlockingPipe(
+ void
+ )
+ {
+ return m_nBlockingPipe;
+ };
+
+ public:
+ virtual PAL_ERROR InitializePreCreate();
+
+ CThreadSuspensionInfo()
+ : m_fPending(FALSE)
+ , m_fSelfsusp(FALSE)
+ , m_fSuspendedForShutdown(FALSE)
+ , m_nBlockingPipe(-1)
+#ifdef _DEBUG
+ , m_lNumThreadsSuspendedByThisThread(0)
+#endif // _DEBUG
+#if !DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ , m_fSuspmutexInitialized(FALSE)
+#endif
+#if USE_POSIX_SEMAPHORES || USE_PTHREAD_CONDVARS
+ , m_fSemaphoresInitialized(FALSE)
+#endif
+ {
+ InitializeSuspensionLock();
+ };
+
+ virtual ~CThreadSuspensionInfo();
+ void
+ AcquireSuspensionLock(
+ CPalThread *pthrCurrent
+ );
+
+ void
+ ReleaseSuspensionLock(
+ CPalThread *pthrCurrent
+ );
+
+ PAL_ERROR
+ InternalSuspendNewThreadFromData(
+ CPalThread *pThread
+ );
+
+ PAL_ERROR
+ InternalResumeThreadFromData(
+ CPalThread *pthrResumer,
+ CPalThread *pthrTarget,
+ DWORD *pdwSuspendCount
+ );
+
+ VOID InitializeSuspensionLock();
+
+ void SetBlockingPipe(
+ int nBlockingPipe
+ )
+ {
+ m_nBlockingPipe = nBlockingPipe;
+ };
+ };
+} //end CorUnix
+
+extern const BYTE WAKEUPCODE; // use for pipe reads during self suspend.
#endif // __cplusplus
+#ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
+extern LONG g_ssSuspensionLock;
+#endif
+
#endif // _PAL_THREADSUSP_HPP
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <minipal/utils.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Alignment helpers (copied for PAL use from stdmacros.h)
SET_DEFAULT_DEBUG_CHANNEL(PAL); // some headers have code with asserts, so do this first
#include "pal/thread.hpp"
+#include "pal/synchobjects.hpp"
+#include "pal/procobj.hpp"
#include "pal/cs.hpp"
#include "pal/file.hpp"
#include "pal/map.hpp"
#include "../objmgr/shmobjectmanager.hpp"
#include "pal/palinternal.h"
#include "pal/sharedmemory.h"
+#include "pal/shmemory.h"
#include "pal/process.h"
+#include "../thread/procprivate.hpp"
#include "pal/module.h"
#include "pal/virtual.h"
#include "pal/misc.h"
extern "C" BOOL CRTInitStdStreams( void );
Volatile<INT> init_count = 0;
+Volatile<BOOL> shutdown_intent = 0;
static BOOL g_fThreadDataAvailable = FALSE;
static pthread_mutex_t init_critsec_mutex = PTHREAD_MUTEX_INITIALIZER;
static int Initialize(int argc, const char *const argv[], DWORD flags);
static BOOL INIT_IncreaseDescriptorLimit(void);
-// Process and session ID of this process.
-DWORD gPID = (DWORD) -1;
-DWORD gSID = (DWORD) -1;
-
-//
-// Key used for associating CPalThread's with the underlying pthread
-// (through pthread_setspecific)
-//
-pthread_key_t CorUnix::thObjKey;
-
#if defined(__APPLE__)
static bool RunningNatively()
{
}
#endif // __APPLE__
+
/*++
Function:
- PAL_InitializeDLL
+ PAL_InitializeWithFlags
Abstract:
- Initializes the non-runtime DLLs/modules like the DAC and SOS.
+ This function is the first function of the PAL to be called.
+ Internal structure initialization is done here. It could be called
+ several time by the same process, a reference count is kept.
Return:
0 if successful
--*/
int
PALAPI
-PAL_InitializeDLL()
+PAL_InitializeWithFlags(
+ int argc,
+ const char *const argv[],
+ DWORD flags)
{
- return Initialize(0, NULL, PAL_INITIALIZE_DLL);
+ return Initialize(argc, argv, flags);
}
-#ifdef ENSURE_PRIMARY_STACK_SIZE
/*++
Function:
- EnsureStackSize
+ PAL_InitializeDLL
Abstract:
- This fixes a problem on MUSL where the initial stack size reported by the
- pthread_attr_getstack is about 128kB, but this limit is not fixed and
- the stack can grow dynamically. The problem is that it makes the
- functions ReflectionInvocation::[Try]EnsureSufficientExecutionStack
- to fail for real life scenarios like e.g. compilation of corefx.
- Since there is no real fixed limit for the stack, the code below
- ensures moving the stack limit to a value that makes reasonable
- real life scenarios work.
+ Initializes the non-runtime DLLs/modules like the DAC and SOS.
+
+Return:
+ 0 if successful
+ -1 if it failed
--*/
-__attribute__((noinline,NOOPT_ATTRIBUTE))
-void
-EnsureStackSize(SIZE_T stackSize)
+int
+PALAPI
+PAL_InitializeDLL()
{
- volatile uint8_t *s = (uint8_t *)_alloca(stackSize);
- *s = 0;
+ return Initialize(0, NULL, PAL_INITIALIZE_DLL);
}
-#endif // ENSURE_PRIMARY_STACK_SIZE
/*++
Function:
void
InitializeDefaultStackSize()
{
- char* defaultStackSizeStr = getenv("COMPlus_DefaultStackSize");
- if (defaultStackSizeStr != NULL)
- {
- errno = 0;
- // Like all numeric values specific by the COMPlus_xxx variables, it is a
- // hexadecimal string without any prefix.
- long int size = strtol(defaultStackSizeStr, NULL, 16);
-
- if (errno == 0)
- {
- g_defaultStackSize = std::max(size, (long int)PTHREAD_STACK_MIN);
- }
- }
-
#ifdef ENSURE_PRIMARY_STACK_SIZE
if (g_defaultStackSize == 0)
{
InitializeDefaultStackSize();
-#ifdef ENSURE_PRIMARY_STACK_SIZE
- if (flags & PAL_INITIALIZE_ENSURE_STACK_SIZE)
- {
- EnsureStackSize(g_defaultStackSize);
- }
-#endif // ENSURE_PRIMARY_STACK_SIZE
-
-
// Initialize the environment.
if (FALSE == EnvironInitialize())
{
// we use large numbers of threads or have many open files.
}
+ /* initialize the shared memory infrastructure */
+ if (!SHMInitialize())
+ {
+ ERROR("Shared memory initialization failed!\n");
+ palError = ERROR_PALINIT_SHM;
+ goto CLEANUP0;
+ }
+
//
+ // Initialize global process data
+ //
+
+ palError = InitializeProcessData();
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to initialize process data\n");
+ goto CLEANUP1;
+ }
+
// Allocate the initial thread data
//
if (NO_ERROR != palError)
{
ERROR("Unable to create initial thread data\n");
- goto CLEANUP2;
+ goto CLEANUP1a;
}
+ PROCAddThread(pThread, pThread);
+
//
// It's now safe to access our thread data
//
}
g_pObjectManager = pshmom;
+
+ //
+ // Initialize the synchronization manager
+ //
+ g_pSynchronizationManager =
+ CPalSynchMgrController::CreatePalSynchronizationManager();
+
+ if (NULL == g_pSynchronizationManager)
+ {
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ ERROR("Failure creating synchronization manager\n");
+ goto CLEANUP1c;
+ }
}
else
{
if (init_count == 0)
{
+ //
+ // Create the initial process and thread objects
+ //
+ palError = CreateInitialProcessAndThreadObjects(pThread);
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to create initial process and thread objects\n");
+ goto CLEANUP2;
+ }
+
palError = ERROR_GEN_FAILURE;
/* Initialize the File mapping critical section. */
goto CLEANUP10;
}
+ if (flags & PAL_INITIALIZE_SYNC_THREAD)
+ {
+ //
+ // Tell the synchronization manager to start its worker thread
+ //
+ palError = CPalSynchMgrController::StartWorker(pThread);
+ if (NO_ERROR != palError)
+ {
+ ERROR("Synch manager failed to start worker thread\n");
+ goto CLEANUP13;
+ }
+ }
+
if (flags & PAL_INITIALIZE_STD_HANDLES)
{
/* create file objects for standard handles */
CLEANUP15:
FILECleanupStdHandles();
CLEANUP14:
+CLEANUP13:
VIRTUALCleanup();
CLEANUP10:
MAPCleanup();
CLEANUP6:
+ PROCCleanupInitialProcess();
CLEANUP2:
+ // Cleanup synchronization manager
+CLEANUP1c:
+ // Cleanup object manager
CLEANUP1b:
+ // Cleanup initial thread data
+CLEANUP1a:
+ // Cleanup global process data
+CLEANUP1:
SHMCleanup();
CLEANUP0:
CLEANUP0a:
return g_fThreadDataAvailable;
}
+/*++
+Function:
+ PALCommonCleanup
+
+ Utility function to prepare for shutdown.
+
+--*/
+void
+PALCommonCleanup()
+{
+ static bool cleanupDone = false;
+
+ // Declare the beginning of shutdown
+ PALSetShutdownIntent();
+
+ if (!cleanupDone)
+ {
+ cleanupDone = true;
+
+ //
+ // Let the synchronization manager know we're about to shutdown
+ //
+ CPalSynchMgrController::PrepareForShutdown();
+ }
+}
+
+BOOL PALIsShuttingDown()
+{
+ /* TODO: This function may be used to provide a reader/writer-like
+ mechanism (or a ref counting one) to prevent PAL APIs that need to access
+ PAL runtime data, from working when PAL is shutting down. Each of those API
+ should acquire a read access while executing. The shutting down code would
+ acquire a write lock, i.e. suspending any new incoming reader, and waiting
+ for the current readers to be done. That would allow us to get rid of the
+ dangerous suspend-all-other-threads at shutdown time */
+ return shutdown_intent;
+}
+
+void PALSetShutdownIntent()
+{
+ /* TODO: See comment in PALIsShuttingDown */
+ shutdown_intent = TRUE;
+}
+
/*++
Function:
PALInitLock
#endif // !DONT_SET_RLIMIT_NOFILE
return TRUE;
}
-
-/*++
-Function:
- PROCAbort()
-
- Aborts the process after calling the shutdown cleanup handler. This function
- should be called instead of calling abort() directly.
-
- Does not return
---*/
-PAL_NORETURN
-VOID
-PROCAbort()
-{
- // Abort the process after waiting for the core dump to complete
- abort();
-}
-
-/*++
-Function:
- GetCurrentProcessId
-
-See MSDN doc.
---*/
-DWORD
-PALAPI
-GetCurrentProcessId(
- VOID)
-{
- PERF_ENTRY(GetCurrentProcessId);
- ENTRY("GetCurrentProcessId()\n" );
-
- LOGEXIT("GetCurrentProcessId returns DWORD %#x\n", gPID);
- PERF_EXIT(GetCurrentProcessId);
- return gPID;
-}
-
-
-/*++
-Function:
- GetCurrentSessionId
-
-See MSDN doc.
---*/
-DWORD
-PALAPI
-GetCurrentSessionId(
- VOID)
-{
- PERF_ENTRY(GetCurrentSessionId);
- ENTRY("GetCurrentSessionId()\n" );
-
- LOGEXIT("GetCurrentSessionId returns DWORD %#x\n", gSID);
- PERF_EXIT(GetCurrentSessionId);
- return gSID;
-}
-
#include "pal/dbgmsg.h"
#include "pal/thread.hpp"
-#include "pal/init.h"
+#include "../thread/procprivate.hpp"
#include "pal/module.h"
#include "pal/process.h"
goto exit;
}
+ HANDLE hThread;
+ palError = CreateThreadObject(pThread, pThread, &hThread);
+ if (NO_ERROR != palError)
+ {
+ pthread_setspecific(thObjKey, NULL);
+ pThread->ReleaseThreadReference();
+ goto exit;
+ }
+
+ // Like CreateInitialProcessAndThreadObjects, we do not need this
+ // thread handle, since we're not returning it to anyone who will
+ // possibly release it.
+ (void)g_pObjectManager->RevokeHandle(pThread, hThread);
+
+ PROCAddThread(pThread, pThread);
+
exit:
*ppThread = pThread;
return palError;
#include "pal/file.h"
#include "pal/utils.h"
#include "pal/init.h"
-#include "pal/modulename.h"
#include "pal/environ.h"
#include "pal/virtual.h"
#include "pal/map.hpp"
/* if we don't know the module's full name yet, this is our chance to obtain it */
if (!module->lib_name && module->dl_handle)
{
- const char* libName = PAL_dladdr((LPVOID)ProcAddress);
- if (libName)
+ Dl_info dl_info;
+ if (dladdr((LPVOID)ProcAddress, &dl_info) != 0)
{
+ const char* libName = dl_info.dli_fname;
module->lib_name = UTIL_MBToWC_Alloc(libName, -1);
if (nullptr == module->lib_name)
{
module, libName);
}
}
+ else
+ {
+ TRACE("GetProcAddress: dladdr() call failed!\n");
+ }
}
}
else
MODSTRUCT *module = nullptr;
BOOL InLoadOrder = TRUE; /* true if in load order, false for reverse */
CPalThread *pThread;
-
+
+ pThread = InternalGetCurrentThread();
+ if (UserCreatedThread != pThread->GetThreadType())
+ {
+ return;
+ }
+
/* Validate dwReason */
switch(dwReason)
{
- case DLL_PROCESS_ATTACH:
+ case DLL_PROCESS_ATTACH:
ASSERT("got called with DLL_PROCESS_ATTACH parameter! Why?\n");
break;
case DLL_PROCESS_DETACH:
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-/*++
-
-
-
-Module Name:
-
- modulename.cpp
-
-Abstract:
-
- Implementation of internal functions to get module names
-
-
-
---*/
-
-#include "pal/thread.hpp"
-#include "pal/malloc.hpp"
-#include "pal/palinternal.h"
-#include "pal/dbgmsg.h"
-#include "pal/modulename.h"
-
-#include <dlfcn.h>
-
-using namespace CorUnix;
-
-SET_DEFAULT_DEBUG_CHANNEL(LOADER);
-
-/*++
- PAL_dladdr
-
- Internal wrapper for dladder used only to get module name
-
-Parameters:
- LPVOID ProcAddress: a pointer to a function in a shared library
-
-Return value:
- Pointer to string with the fullpath to the shared library containing
- ProcAddress.
-
- NULL if error occurred.
-
-Notes:
- The string returned by this function is owned by the OS.
- If you need to keep it, strdup() it, because it is unknown how long
- this ptr will point at the string you want (over the lifetime of
- the system running) It is only safe to use it immediately after calling
- this function.
---*/
-const char *PAL_dladdr(LPVOID ProcAddress)
-{
- Dl_info dl_info;
- if (!dladdr(ProcAddress, &dl_info))
- {
- WARN("dladdr() call failed!\n");
- /* If we get an error, return NULL */
- return (NULL);
- }
- else
- {
- /* Return the module name */
- return dl_info.dli_fname;
- }
-}
-
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-/*++
-
-
-
-Module Name:
-
- local.c
-
-Abstract:
-
- Implementation of local memory management functions.
-
-Revision History:
-
-
-
---*/
-
-#include "pal/palinternal.h"
-#include "pal/dbgmsg.h"
-
-
-SET_DEFAULT_DEBUG_CHANNEL(MEM);
-
-
-/*++
-Function:
- LocalAlloc
-
-See MSDN doc.
---*/
-HLOCAL
-PALAPI
-LocalAlloc(
- IN UINT uFlags,
- IN SIZE_T uBytes)
-{
- LPVOID lpRetVal = NULL;
- PERF_ENTRY(LocalAlloc);
- ENTRY("LocalAlloc (uFlags=%#x, uBytes=%u)\n", uFlags, uBytes);
-
- if ((uFlags & ~LMEM_ZEROINIT) != 0)
- {
- ASSERT("Invalid parameter AllocFlags=0x%x\n", uFlags);
- SetLastError(ERROR_INVALID_PARAMETER);
- goto done;
- }
-
- lpRetVal = PAL_malloc(uBytes);
-
- if (lpRetVal == NULL)
- {
- ERROR("Not enough memory\n");
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto done;
- }
-
- if ((uFlags & LMEM_ZEROINIT) != 0)
- {
- memset(lpRetVal, 0, uBytes);
- }
-
-done:
- LOGEXIT( "LocalAlloc returning %p.\n", lpRetVal );
- PERF_EXIT(LocalAlloc);
- return (HLOCAL) lpRetVal;
-}
-
-/*++
-Function:
- LocalFree
-
-See MSDN doc.
---*/
-HLOCAL
-PALAPI
-LocalFree(
- IN HLOCAL hMem)
-{
- BOOL bRetVal = FALSE;
- PERF_ENTRY(LocalFree);
- ENTRY("LocalFree (hmem=%p)\n", hMem);
-
- free(hMem);
- bRetVal = TRUE;
-
- LOGEXIT( "LocalFree returning %p.\n", bRetVal == TRUE ? (HLOCAL)NULL : hMem );
- PERF_EXIT(LocalFree);
- return bRetVal == TRUE ? (HLOCAL)NULL : hMem;
-}
allocChars = MAX_ERROR_STRING_LENGTH + 1;
}
- LPWSTR lpRetVal = (LPWSTR)LocalAlloc(LMEM_FIXED, allocChars * sizeof(WCHAR));
+ LPWSTR lpRetVal = (LPWSTR)PAL_malloc(allocChars * sizeof(WCHAR));
if (lpRetVal)
{
UINT NumOfBytes = 0; \
nSize *= 2; \
NumOfBytes = nSize * sizeof( WCHAR ); \
- lpTemp = static_cast<WCHAR *>( LocalAlloc( LMEM_FIXED, NumOfBytes ) ); \
+ lpTemp = static_cast<WCHAR *>( PAL_malloc( NumOfBytes ) ); \
TRACE( "Growing the buffer.\n" );\
\
if ( !lpTemp ) \
\
*lpWorkingString = '\0';\
PAL_wcscpy( lpTemp, lpReturnString );\
- LocalFree( lpReturnString ); \
+ free( lpReturnString ); \
lpWorkingString = lpReturnString = lpTemp; \
lpWorkingString += nCount; \
} \
/* Parameter processing. */
if ( dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER )
{
- TRACE( "Allocated %d TCHARs. Don't forget to call LocalFree to "
+ TRACE( "Allocated %d TCHARs. Don't forget to call free to "
"free the memory when done.\n", nSize );
bIsLocalAlloced = TRUE;
}
}
lpWorkingString = static_cast<WCHAR *>(
- LocalAlloc( LMEM_FIXED, nSize * sizeof( WCHAR ) ) );
+ PAL_malloc( nSize * sizeof( WCHAR ) ) );
if ( !lpWorkingString )
{
ERROR( "Unable to allocate memory for the working string.\n" );
{
TRACE( "Copying the string into the buffer.\n" );
PAL_wcsncpy( lpBuffer, lpReturnString, nCount + 1 );
- LocalFree( lpReturnString );
+ free( lpReturnString );
}
}
else /* Error, something occurred. */
{
if ( lpReturnString )
{
- LocalFree( lpReturnString );
+ free( lpReturnString );
}
}
LOGEXIT( "FormatMessageW returns %d.\n", nCount );
ASSERT("Attempt to obtain a synch data for a non-waitable object\n");
return ERROR_INVALID_HANDLE;
}
+
+/*++
+Function:
+ CSharedMemoryWaitableObject::Initialize
+
+ Performs possibly-failing initialization for a newly-constructed
+ object
+
+Parameters:
+ pthr -- thread data for calling thread
+ poa -- the object attributes (e.g., name) for the object
+--*/
+
+PAL_ERROR
+CSharedMemoryWaitableObject::Initialize(
+ CPalThread *pthr,
+ CObjectAttributes *poa
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != poa);
+
+ ENTRY("CSharedMemoryWaitableObject::Initialize"
+ "(this = %p, pthr = %p, poa = %p)\n",
+ this,
+ pthr,
+ poa
+ );
+
+ palError = CSharedMemoryObject::Initialize(pthr, poa);
+ if (NO_ERROR != palError)
+ {
+ goto InitializeExit;
+ }
+
+ //
+ // Sanity check the passed in object type
+ //
+
+ _ASSERTE(CObjectType::WaitableObject == m_pot->GetSynchronizationSupport());
+
+ palError = g_pSynchronizationManager->AllocateObjectSynchData(
+ m_pot,
+ m_ObjectDomain,
+ &m_pvSynchData
+ );
+
+ if (NO_ERROR == palError && SharedObject == m_ObjectDomain)
+ {
+ SHMObjData *pshmod = SHMPTR_TO_TYPED_PTR(SHMObjData, m_shmod);
+ _ASSERTE(NULL != pshmod);
+
+ pshmod->pvSynchData = m_pvSynchData;
+ }
+
+InitializeExit:
+
+ LOGEXIT("CSharedMemoryWaitableObject::Initialize returns %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject
+
+ Destructor; should only be called from ReleaseReference
+--*/
+
+CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject()
+{
+ ENTRY("CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject"
+ "(this = %p)\n",
+ this
+ );
+
+ if (!m_fSharedDataDereferenced)
+ {
+ ASSERT("DereferenceSharedData not called before object destructor -- delete called directly?\n");
+ DereferenceSharedData();
+ }
+
+ if (NULL != m_pvSynchData && m_fDeleteSharedData)
+ {
+ g_pSynchronizationManager->FreeObjectSynchData(
+ m_pot,
+ m_ObjectDomain,
+ m_pvSynchData
+ );
+ }
+
+ LOGEXIT("CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject\n");
+}
+
+/*++
+Function:
+ CSharedMemoryWaitableObject::GetSynchStateController
+
+ Obtain a synchronization state controller for this object.
+
+Parameters:
+ pthr -- thread data for calling thread
+ ppStateController -- on success, receives a pointer to the state controller
+ instance
+--*/
+
+PAL_ERROR
+CSharedMemoryWaitableObject::GetSynchStateController(
+ CPalThread *pthr, // IN, OPTIONAL
+ ISynchStateController **ppStateController // OUT
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != ppStateController);
+
+ ENTRY("CSharedMemoryWaitableObject::GetSynchStateController"
+ "(this = %p, pthr = %p, ppStateController = %p",
+ this,
+ pthr,
+ ppStateController
+ );
+
+ //
+ // We need to grab the local synch lock before creating the controller
+ // (otherwise we could get promoted after passing in our parameters)
+ //
+
+ g_pSynchronizationManager->AcquireProcessLock(pthr);
+
+ palError = g_pSynchronizationManager->CreateSynchStateController(
+ pthr,
+ m_pot,
+ m_pvSynchData,
+ m_ObjectDomain,
+ ppStateController
+ );
+
+ g_pSynchronizationManager->ReleaseProcessLock(pthr);
+
+ LOGEXIT("CSharedMemoryWaitableObject::GetSynchStateController returns %d\n",
+ palError
+ );
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryWaitableObject::GetSynchWaitController
+
+ Obtain a synchronization wait controller for this object.
+
+Parameters:
+ pthr -- thread data for calling thread
+ ppWaitController -- on success, receives a pointer to the wait controller
+ instance
+--*/
+
+PAL_ERROR
+CSharedMemoryWaitableObject::GetSynchWaitController(
+ CPalThread *pthr, // IN, OPTIONAL
+ ISynchWaitController **ppWaitController // OUT
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != ppWaitController);
+
+ ENTRY("CSharedMemoryWaitableObject::GetSynchWaitController"
+ "(this = %p, pthr = %p, ppWaitController = %p",
+ this,
+ pthr,
+ ppWaitController
+ );
+
+ //
+ // We need to grab the local synch lock before creating the controller
+ // (otherwise we could get promoted after passing in our parameters)
+ //
+
+ g_pSynchronizationManager->AcquireProcessLock(pthr);
+
+ palError = g_pSynchronizationManager->CreateSynchWaitController(
+ pthr,
+ m_pot,
+ m_pvSynchData,
+ m_ObjectDomain,
+ ppWaitController
+ );
+
+ g_pSynchronizationManager->ReleaseProcessLock(pthr);
+
+ LOGEXIT("CSharedMemoryWaitableObject::GetSynchWaitController returns %d\n",
+ palError
+ );
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryWaitableObject::GetObjectSynchData
+
+ Obtain the synchronization data for this object. This method should only
+ be called by the synchronization manager
+
+Parameters:
+ ppvSynchData -- on success, receives a pointer to the object's synch data
+--*/
+
+PAL_ERROR
+CSharedMemoryWaitableObject::GetObjectSynchData(
+ VOID **ppvSynchData // OUT
+ )
+{
+ _ASSERTE(NULL != ppvSynchData);
+
+ ENTRY("CSharedMemoryWaitableObject::GetObjectSynchData"
+ "(this = %p, ppvSynchData = %p)\n",
+ this,
+ ppvSynchData
+ );
+
+ *ppvSynchData = m_pvSynchData;
+
+ LOGEXIT("CSharedMemoryWaitableObject::GetObjectSynchData returns %d\n",
+ NO_ERROR
+ );
+
+ return NO_ERROR;
+}
+
);
};
+
+ class CSharedMemoryWaitableObject : public CSharedMemoryObject
+ {
+ template <class T> friend void InternalDelete(T *p);
+
+ protected:
+
+ VOID *m_pvSynchData;
+
+ virtual ~CSharedMemoryWaitableObject();
+
+ public:
+
+ CSharedMemoryWaitableObject(
+ CObjectType *pot,
+ CRITICAL_SECTION *pcsObjListLock
+ )
+ :
+ CSharedMemoryObject(pot, pcsObjListLock),
+ m_pvSynchData(NULL)
+ {
+ };
+
+ //
+ // Constructor used to import a shared object into this process. The
+ // shared memory lock must be held when calling this contstructor
+ //
+
+ CSharedMemoryWaitableObject(
+ CObjectType *pot,
+ CRITICAL_SECTION *pcsObjListLock,
+ SHMPTR shmSharedObjectData,
+ SHMObjData *psmod,
+ bool fAddRefSharedData
+ )
+ :
+ CSharedMemoryObject(pot, pcsObjListLock, shmSharedObjectData, psmod, fAddRefSharedData),
+ m_pvSynchData(psmod->pvSynchData)
+ {
+ };
+
+ virtual
+ PAL_ERROR
+ Initialize(
+ CPalThread *pthr,
+ CObjectAttributes *poa
+ );
+
+ //
+ // IPalObject routines
+ //
+
+ virtual
+ PAL_ERROR
+ GetSynchStateController(
+ CPalThread *pthr,
+ ISynchStateController **ppStateController
+ );
+
+ virtual
+ PAL_ERROR
+ GetSynchWaitController(
+ CPalThread *pthr,
+ ISynchWaitController **ppWaitController
+ );
+
+ virtual
+ PAL_ERROR
+ GetObjectSynchData(
+ VOID **ppvSynchData
+ );
+ };
+
}
#endif // _PAL_SHMOBJECT_HPP
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
/*++
#include "shmobject.hpp"
#include "pal/cs.hpp"
#include "pal/thread.hpp"
+#include "pal/procobj.hpp"
#include "pal/dbgmsg.h"
SET_DEFAULT_DEBUG_CHANNEL(PAL);
if (CObjectType::WaitableObject == pot->GetSynchronizationSupport())
{
- _ASSERTE(FALSE);
+ pshmobj = InternalNew<CSharedMemoryWaitableObject>(pot, &m_csListLock);
}
else
{
)
{
PAL_ERROR palError = NO_ERROR;
- CSharedMemoryObject *pshmobj = NULL;
+ CSharedMemoryObject *pshmobj;
PLIST_ENTRY pleObjectList;
_ASSERTE(NULL != pthr);
if (CObjectType::WaitableObject == pot->GetSynchronizationSupport())
{
- _ASSERTE(FALSE);
+ pshmobj = InternalNew<CSharedMemoryWaitableObject>(pot,
+ &m_csListLock,
+ shmSharedObjectData,
+ psmod,
+ fAddRefSharedData);
}
else
{
return palError;
}
-static PalObjectTypeId RemotableObjectTypes[] =
- {otiManualResetEvent, otiAutoResetEvent, otiMutex, otiProcess};
-
-static CAllowedObjectTypes aotRemotable(
- RemotableObjectTypes,
- sizeof(RemotableObjectTypes) / sizeof(RemotableObjectTypes[0])
- );
-
/*++
Function:
CheckObjectTypeAndRights
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+
+
+Module Name:
+
+ synchcontrollers.cpp
+
+Abstract:
+ Implementation of Synchronization Controllers and related objects
+
+
+
+--*/
+
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(SYNC); // some headers have code with asserts, so do this first
+
+#include "synchmanager.hpp"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sched.h>
+#include <errno.h>
+#include <limits.h>
+
+namespace CorUnix
+{
+#ifdef SYNCH_STATISTICS
+ LONG g_rglStatWaitCount[ObjectTypeIdCount] = { 0 };
+ LONG g_rglStatContentionCount[ObjectTypeIdCount] = { 0 };
+#endif // SYNCH_STATISTICS
+ ////////////////////////////
+ // //
+ // CSynchControllerBase //
+ // //
+ ////////////////////////////
+
+ /*++
+ Method:
+ CSynchControllerBase::Init
+
+ Initializes a generic controller
+ --*/
+ PAL_ERROR CSynchControllerBase::Init(
+ CPalThread * pthrCurrent,
+ ControllerType ctCtrlrType,
+ ObjectDomain odObjectDomain,
+ CObjectType *potObjectType,
+ CSynchData * psdSynchData,
+ WaitDomain wdWaitDomain)
+ {
+ VALIDATEOBJECT(psdSynchData);
+
+ _ASSERTE(InternalGetCurrentThread() == pthrCurrent);
+
+ // Initialize internal controller data
+ m_pthrOwner = pthrCurrent;
+ m_ctCtrlrType = ctCtrlrType;
+ m_odObjectDomain = odObjectDomain;
+ m_potObjectType = potObjectType;
+ m_psdSynchData = psdSynchData;
+ m_wdWaitDomain = wdWaitDomain;
+
+ // Add reference to target synch data
+ m_psdSynchData->AddRef();
+
+ // Acquire lock implied by the controller
+ CPalSynchronizationManager::AcquireLocalSynchLock(m_pthrOwner);
+ if (LocalWait != m_wdWaitDomain)
+ {
+ CPalSynchronizationManager::AcquireSharedSynchLock(m_pthrOwner);
+ }
+
+ return NO_ERROR;
+ }
+
+ /*++
+ Method:
+ CSynchControllerBase::Release
+
+ Releases a generic controller a return it to the appropriate cache
+ --*/
+ void CSynchControllerBase::Release()
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+#ifdef _DEBUG
+ ThreadWaitInfo * ptwiWaitInfo =
+ CPalSynchronizationManager::GetThreadWaitInfo(m_pthrOwner);
+#endif // _DEBUG
+
+ CPalSynchronizationManager * pSynchManager =
+ CPalSynchronizationManager::GetInstance();
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERTE(ptwiWaitInfo->pthrOwner == m_pthrOwner);
+
+ // Release reference to target synch data
+ m_psdSynchData->Release(m_pthrOwner);
+
+ // Release lock implied by the controller
+ if (LocalWait != m_wdWaitDomain)
+ {
+ CPalSynchronizationManager::ReleaseSharedSynchLock(m_pthrOwner);
+ }
+ CPalSynchronizationManager::ReleaseLocalSynchLock(m_pthrOwner);
+
+ // Return controller to the appropriate cache
+ if (WaitController == m_ctCtrlrType)
+ {
+ // The cast here must be static_cast and not reinterpet_cast.
+ // In fact in general static_cast<CSynchWaitController*>(this) is
+ // equal to this-sizeof(void*), given that CSynchWaitController
+ // has a virtual table, while CSynchControllerBase doesn't.
+ pSynchManager->CacheAddWaitCtrlr(m_pthrOwner,
+ static_cast<CSynchWaitController*>(this));
+ }
+ else
+ {
+ // The cast here must be static_cast and not reinterpet_cast
+ pSynchManager->CacheAddStateCtrlr(m_pthrOwner,
+ static_cast<CSynchStateController*>(this));
+ }
+ }
+
+ ////////////////////////////
+ // //
+ // CSynchWaitController //
+ // //
+ ////////////////////////////
+
+ /*++
+ Method:
+ CSynchWaitController::CanThreadWaitWithoutBlocking
+
+ Returns whether or not the thread owning this controller can
+ wait on the target object without blocking (i.e. the objet is
+ signaled)
+ --*/
+ PAL_ERROR CSynchWaitController::CanThreadWaitWithoutBlocking(
+ bool * pfCanWaitWithoutBlocking,
+ bool * pfAbandoned)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ bool fRetVal = false;
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERTE(NULL != pfCanWaitWithoutBlocking);
+ _ASSERTE(NULL != pfAbandoned);
+
+ fRetVal = m_psdSynchData->CanWaiterWaitWithoutBlocking(m_pthrOwner, pfAbandoned);
+
+ if(!fRetVal && otiProcess == m_psdSynchData->GetObjectTypeId())
+ {
+ // Note: if the target object is a process, here we need to check
+ // whether or not it has already exited. In fact, since currently
+ // we do not monitor a process status as long as there is no
+ // thread waiting on it, in general if the process already exited
+ // the process object is likely not to be signaled yet, therefore
+ // the above CanWaiterWaitWithoutBlocking call probably returned
+ // false, and, without the check below, that would cause the
+ // current thread to eventually go to sleep for a short time
+ // (until the worker thread notifies that the waited process has
+ // indeed exited), while it would not be necessary.
+ // As side effect that would cause a WaitForSingleObject with zero
+ // timeout to always return WAIT_TIMEOUT, even though the target
+ // process already exited. WaitForSingleObject with zero timeout
+ // is a common way to probe whether or not a process has already
+ // exited, and it is supposed to return WAIT_OBJECT_0 if the
+ // process exited, and WAIT_TIMEOUT if it is still active.
+ // In order to support this feature we need to check at this time
+ // whether or not the process has already exited.
+
+ CProcProcessLocalData * pProcLocalData = GetProcessLocalData();
+ DWORD dwExitCode = 0;
+ bool fIsActualExitCode = false;
+
+ _ASSERT_MSG(NULL != pProcLocalData,
+ "Process synch data pointer is missing\n");
+
+ if (NULL != pProcLocalData &&
+ CPalSynchronizationManager::HasProcessExited(pProcLocalData->dwProcessId,
+ &dwExitCode,
+ &fIsActualExitCode))
+ {
+ TRACE("Process pid=%u exited with %s exitcode=%u\n",
+ pProcLocalData->dwProcessId,
+ fIsActualExitCode ? "actual" : "guessed",
+ dwExitCode);
+
+ // Store the exit code in the process local data
+ if (fIsActualExitCode)
+ {
+ pProcLocalData->dwExitCode = dwExitCode;
+ }
+
+ // Set process status to PS_DONE
+ pProcLocalData->ps = PS_DONE;
+
+ // Set signal count
+ m_psdSynchData->SetSignalCount(1);
+
+ // Releasing all local waiters
+ // (see comments in DoMonitorProcesses)
+ m_psdSynchData->ReleaseAllLocalWaiters(m_pthrOwner);
+
+ fRetVal = true;
+ }
+ }
+
+ *pfCanWaitWithoutBlocking = fRetVal;
+ return NO_ERROR;
+ }
+
+ /*++
+ Method:
+ CSynchWaitController::ReleaseWaitingThreadWithoutBlocking
+
+ Performs all the steps needed to be done by the controller's owner
+ thread in order to wait on the target object without blocking
+ (e.g. modifying the object signal count accordingly with its
+ thread release semantics)
+ This method should be called only after having received positive
+ response from CanThreadWaitWithoutBlocking called on the same
+ controller.
+ --*/
+ PAL_ERROR CSynchWaitController::ReleaseWaitingThreadWithoutBlocking()
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ PAL_ERROR palErr = NO_ERROR;
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+
+ palErr = m_psdSynchData->ReleaseWaiterWithoutBlocking(m_pthrOwner, m_pthrOwner);
+
+#ifdef SYNCH_STATISTICS
+ if (NO_ERROR == palErr)
+ {
+ m_psdSynchData->IncrementStatWaitCount();
+ }
+#endif
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CSynchWaitController::RegisterWaitingThread
+
+ Registers the controller's owner thread for waiting on the target
+ object
+ --*/
+ PAL_ERROR CSynchWaitController::RegisterWaitingThread(
+ WaitType wtWaitType,
+ DWORD dwIndex,
+ bool fAlertable,
+ bool fPrioritize)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ PAL_ERROR palErr = NO_ERROR;
+ WaitingThreadsListNode * pwtlnNewNode = NULL;
+ SharedID shridNewNode = NULL;
+ ThreadWaitInfo * ptwiWaitInfo;
+ DWORD * pdwWaitState;
+ bool fSharedObject = (SharedObject == m_odObjectDomain);
+ bool fEarlyDeath = false;
+ bool fSynchDataRefd = false;
+ CPalSynchronizationManager * pSynchManager =
+ CPalSynchronizationManager::GetInstance();
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+
+ ptwiWaitInfo = CPalSynchronizationManager::GetThreadWaitInfo(
+ m_pthrOwner);
+
+ _ASSERTE(ptwiWaitInfo->pthrOwner == m_pthrOwner);
+
+ pdwWaitState = SharedIDToTypePointer(DWORD,
+ m_pthrOwner->synchronizationInfo.m_shridWaitAwakened);
+
+ if (fSharedObject)
+ {
+ shridNewNode = pSynchManager->CacheGetSharedWTListNode(m_pthrOwner);
+ pwtlnNewNode = SharedIDToTypePointer(WaitingThreadsListNode, shridNewNode);
+ }
+ else
+ {
+ pwtlnNewNode = pSynchManager->CacheGetLocalWTListNode(m_pthrOwner);
+ }
+
+ if (!pwtlnNewNode)
+ {
+ if (fSharedObject && (NULL != shridNewNode))
+ {
+ ASSERT("Bad Shared Memory ptr %p\n", shridNewNode);
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+ else
+ {
+ ERROR("Out of memory\n");
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ goto RWT_exit;
+ }
+
+ if (ptwiWaitInfo->lObjCount >= MAXIMUM_WAIT_OBJECTS)
+ {
+ ASSERT("Too many objects");
+ palErr = ERROR_INTERNAL_ERROR;
+ goto RWT_exit;
+ }
+
+ if (0 == ptwiWaitInfo->lObjCount)
+ {
+ ptwiWaitInfo->wtWaitType = wtWaitType;
+ ptwiWaitInfo->wdWaitDomain = m_wdWaitDomain;
+ }
+ else
+ {
+ _ASSERT_MSG(wtWaitType == ptwiWaitInfo->wtWaitType,
+ "Conflicting wait types in wait registration\n");
+
+ if (m_wdWaitDomain != ptwiWaitInfo->wdWaitDomain)
+ {
+ ptwiWaitInfo->wdWaitDomain = MixedWait;
+ }
+ }
+
+ pwtlnNewNode->shridSHRThis = NULL;
+ pwtlnNewNode->ptwiWaitInfo = ptwiWaitInfo;
+ pwtlnNewNode->dwObjIndex = dwIndex;
+ pwtlnNewNode->dwProcessId = gPID;
+ pwtlnNewNode->dwThreadId = m_pthrOwner->GetThreadId();
+ pwtlnNewNode->dwFlags = (MultipleObjectsWaitAll == wtWaitType) ?
+ WTLN_FLAG_WAIT_ALL : 0;
+ pwtlnNewNode->shridWaitingState = m_pthrOwner->synchronizationInfo.m_shridWaitAwakened;
+ if (fSharedObject)
+ {
+ pwtlnNewNode->dwFlags |= WTLN_FLAG_OWNER_OBJECT_IS_SHARED;
+ pwtlnNewNode->shridSHRThis = shridNewNode;
+ pwtlnNewNode->ptrOwnerObjSynchData.shrid = m_psdSynchData->GetSharedThis();
+ }
+ else
+ {
+ pwtlnNewNode->ptrOwnerObjSynchData.ptr = m_psdSynchData;
+ }
+
+ // AddRef the synch data (will be released in UnregisterWait)
+ m_psdSynchData->AddRef();
+ fSynchDataRefd = true;
+
+ ptwiWaitInfo->rgpWTLNodes[ptwiWaitInfo->lObjCount] = pwtlnNewNode;
+
+ if(otiProcess == m_psdSynchData->GetObjectTypeId())
+ {
+ CProcProcessLocalData * pProcLocalData = GetProcessLocalData();
+
+ if (NULL == pProcLocalData)
+ {
+ // Process local data pointer not set in the controller.
+ // This pointer is set in CSynchWaitController only when the
+ // wait controller for the object is created by calling
+ // GetSynchWaitControllersForObjects
+ ASSERT("Process synch data pointer is missing\n");
+ palErr = ERROR_INTERNAL_ERROR;
+ goto RWT_exit;
+ }
+
+ palErr = pSynchManager->RegisterProcessForMonitoring(m_pthrOwner,
+ m_psdSynchData,
+ m_pProcessObject,
+ pProcLocalData);
+ if (NO_ERROR != palErr)
+ {
+ goto RWT_exit;
+ }
+ }
+
+ if (0 == ptwiWaitInfo->lObjCount)
+ {
+ DWORD dwWaitState;
+
+ // Setting the thread in wait state
+ dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE: TWS_WAITING);
+
+ TRACE("Switching my wait state [%p] from TWS_ACTIVE to %u \n",
+ pdwWaitState, dwWaitState);
+
+ dwWaitState = InterlockedCompareExchange(
+ (LONG *)pdwWaitState, (LONG)dwWaitState, TWS_ACTIVE);
+ if ((DWORD)TWS_ACTIVE != dwWaitState)
+ {
+ if ((DWORD)TWS_EARLYDEATH == dwWaitState)
+ {
+ // Process is terminating, this thread will soon be
+ // suspended (by SuspendOtherThreads).
+ WARN("Thread is about to get suspended by "
+ "TerminateProcess\n");
+
+ fEarlyDeath = true;
+ palErr = WAIT_FAILED;
+ }
+ else
+ {
+ ASSERT("Unexpected thread wait state %d\n", dwWaitState);
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+ goto RWT_exit;
+ }
+ }
+
+ // Add new node to queue
+ if (fSharedObject)
+ {
+ m_psdSynchData->SharedWaiterEnqueue(shridNewNode, fPrioritize);
+ ptwiWaitInfo->lSharedObjCount += 1;
+ }
+ else
+ {
+ m_psdSynchData->WaiterEnqueue(pwtlnNewNode, fPrioritize);
+ }
+
+ // Succeeded: update object count
+ ptwiWaitInfo->lObjCount++;
+
+ RWT_exit:
+ if (palErr != NO_ERROR)
+ {
+ // Unregister any partial wait registration
+ pSynchManager->UnRegisterWait(m_pthrOwner, ptwiWaitInfo, fSharedObject);
+
+ if (fSynchDataRefd)
+ {
+ m_psdSynchData->Release(m_pthrOwner);
+ }
+ if ((fSharedObject) && (NULL != shridNewNode))
+ {
+ pSynchManager->CacheAddSharedWTListNode(m_pthrOwner, shridNewNode);
+ }
+ else if (NULL != pwtlnNewNode)
+ {
+ pSynchManager->CacheAddLocalWTListNode(m_pthrOwner, pwtlnNewNode);
+ }
+
+ if (fEarlyDeath)
+ {
+ // Early death detected, i.e. the process is about to exit.
+ // We need to completely release the synch lock(s) before
+ // going to sleep
+ LONG lLocalSynchLockCount;
+ LONG lSharedSynchLockCount;
+
+ lSharedSynchLockCount = CPalSynchronizationManager::ResetSharedSynchLock(m_pthrOwner);
+ lLocalSynchLockCount = CPalSynchronizationManager::ResetLocalSynchLock(m_pthrOwner);
+
+ _ASSERTE(0 < lLocalSynchLockCount);
+
+ // Sleep for ever
+ CPalSynchronizationManager::ThreadPrepareForShutdown();
+ }
+ }
+#ifdef SYNCH_STATISTICS
+ else
+ {
+ m_psdSynchData->IncrementStatWaitCount();
+ m_psdSynchData->IncrementStatContentionCount();
+ }
+#endif
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CSynchWaitController::ReleaseController
+
+ Releases the current controller
+ --*/
+ void CSynchWaitController::ReleaseController()
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+
+ Release();
+ }
+
+ /*++
+ Method:
+ CSynchWaitController::GetProcessLocalData
+
+ Accessor Get method for process local data of the target object
+ --*/
+ CProcProcessLocalData * CSynchWaitController::GetProcessLocalData()
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERT_MSG(NULL != m_pProcLocalData,
+ "Pointer to process local data not yet initialized\n");
+
+ return m_pProcLocalData;
+ }
+
+ /*++
+ Method:
+ CSynchWaitController::SetProcessData
+
+ Accessor Set method for process local data of the target object
+ --*/
+ void CSynchWaitController::SetProcessData(IPalObject* pProcessObject, CProcProcessLocalData * pProcLocalData)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERT_MSG(m_pProcessObject == nullptr, "SetProcessData should not be called more than once");
+ _ASSERT_MSG(pProcessObject != nullptr && pProcessObject->GetObjectType()->GetId() == otiProcess, "Invalid process object passed to SetProcessData");
+
+ m_pProcessObject = pProcessObject;
+ m_pProcLocalData = pProcLocalData;
+ }
+
+ /////////////////////////////
+ // //
+ // CSynchStateController //
+ // //
+ /////////////////////////////
+
+ /*++
+ Method:
+ CSynchStateController::GetSignalCount
+
+ Returns the current signal count of the target object
+ --*/
+ PAL_ERROR CSynchStateController::GetSignalCount(LONG *plSignalCount)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ PAL_ERROR palErr = NO_ERROR;
+ LONG lCount = m_psdSynchData->GetSignalCount();
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERTE(NULL != plSignalCount);
+ _ASSERT_MSG(0 <= lCount,
+ "Internal error: negative signal count [signal count=%d]",
+ lCount);
+
+ *plSignalCount = lCount;
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CSynchStateController::SetSignalCount
+
+ Sets the signal count of the target object, possibly triggering
+ waiting threads awakening.
+ --*/
+ PAL_ERROR CSynchStateController::SetSignalCount(LONG lNewCount)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERTE(lNewCount >= 0);
+
+ m_psdSynchData->Signal(m_pthrOwner, lNewCount, false);
+
+ return NO_ERROR;
+ }
+
+ /*++
+ Method:
+ CSynchStateController::IncrementSignalCount
+
+ Increments the signal count of the target object, possibly triggering
+ waiting threads awakening.
+ --*/
+ PAL_ERROR CSynchStateController::IncrementSignalCount(
+ LONG lAmountToIncrement)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERTE(lAmountToIncrement > 0);
+
+ LONG lOldCount = m_psdSynchData->GetSignalCount();
+ LONG lNewCount = lOldCount + lAmountToIncrement;
+
+ _ASSERT_MSG(lNewCount > lOldCount,
+ "Signal count increment %d would make current signal count %d to "
+ "wrap around\n", lAmountToIncrement, lOldCount);
+
+ m_psdSynchData->Signal(m_pthrOwner, lNewCount, false);
+
+ return NO_ERROR;
+ }
+
+ /*++
+ Method:
+ CSynchStateController::DecrementSignalCount
+
+ Decrements the signal count of the target object.
+ --*/
+ PAL_ERROR CSynchStateController::DecrementSignalCount(
+ LONG lAmountToDecrement)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERTE(lAmountToDecrement > 0);
+
+ PAL_ERROR palErr = NO_ERROR;
+ LONG lCount = m_psdSynchData->GetSignalCount();
+ _ASSERTE(lAmountToDecrement <= lCount);
+
+ m_psdSynchData->SetSignalCount(lCount - lAmountToDecrement);
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CSynchStateController::SetOwner
+
+ Sets the owner of the target object and initializes the ownership
+ count to 1 (for objects with tracked ownership).
+ --*/
+ PAL_ERROR CSynchStateController::SetOwner(CPalThread * pNewOwningThread)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ PAL_ERROR palErr = NO_ERROR;
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERTE(NULL != pNewOwningThread);
+ _ASSERT_MSG(CObjectType::OwnershipTracked ==
+ m_potObjectType->GetOwnershipSemantics(),
+ "SetOwner called on an object without OwnershipTracked "
+ "semantics\n");
+
+ if (0 != m_psdSynchData->GetOwnershipCount())
+ {
+ ASSERT("Ownership count should be zero at this time\n");
+ palErr = ERROR_INTERNAL_ERROR;
+ goto SO_exit;
+ }
+
+ palErr = m_psdSynchData->AssignOwnershipToThread(m_pthrOwner,
+ pNewOwningThread);
+
+ _ASSERT_MSG(0 == m_psdSynchData->GetOwnershipCount() ||
+ 0 == m_psdSynchData->GetSignalCount(),
+ "Conflicting values for SignalCount [%d] and "
+ "OwnershipCount [%d]\n",
+ m_psdSynchData->GetOwnershipCount(),
+ m_psdSynchData->GetSignalCount());
+
+ SO_exit:
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CSynchStateController::DecrementOwnershipCount
+
+ Decrements the ownership count of the target object possibly triggering
+ waiting threads awakening (for objects with tracked ownership).
+ --*/
+ PAL_ERROR CSynchStateController::DecrementOwnershipCount()
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ PAL_ERROR palErr = NO_ERROR;
+ LONG lOwnershipCount = m_psdSynchData->GetOwnershipCount();
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERT_MSG(CObjectType::OwnershipTracked ==
+ m_potObjectType->GetOwnershipSemantics(),
+ "Trying to decrement ownership count on an object with "
+ "ownership semantics other than OwnershipTracked\n");
+ _ASSERT_MSG(0 <= lOwnershipCount,
+ "Operation would make ownership count negative - object "
+ "should be owned at this time [ownership count=%d]\n",
+ lOwnershipCount);
+
+ if ( (1 > lOwnershipCount) ||
+ (m_psdSynchData->GetOwnerProcessID() != gPID) ||
+ (m_psdSynchData->GetOwnerThread() != m_pthrOwner) )
+ {
+ palErr = ERROR_NOT_OWNER;
+ goto DOC_exit;
+ }
+
+ lOwnershipCount--;
+ m_psdSynchData->SetOwnershipCount(lOwnershipCount);
+
+ if (0 == lOwnershipCount)
+ {
+ CPalSynchronizationManager * pSynchManager =
+ CPalSynchronizationManager::GetInstance();
+ OwnedObjectsListNode * pooln =
+ m_psdSynchData->GetOwnershipListNode();
+
+ _ASSERT_MSG(NULL != pooln,
+ "Null ownership node pointer in SynchData with ownership "
+ "semantics\n");
+ _ASSERT_MSG(m_psdSynchData == pooln->pPalObjSynchData,
+ "Corrupted ownership node\n");
+
+ // Object has been released
+ // Remove it from list of owned objs for current thread
+ m_pthrOwner->synchronizationInfo.RemoveObjectFromOwnedList(pooln);
+
+ // Release SynchData reference count implied by the ownership
+ // list node
+ m_psdSynchData->Release(m_pthrOwner);
+
+ // Return node to the cache
+ pSynchManager->CacheAddOwnedObjsListNode(m_pthrOwner, pooln);
+
+ // Reset ownership
+ m_psdSynchData->ResetOwnership();
+
+ // Signal it and trigger waiter thread awakening
+ m_psdSynchData->Signal(m_pthrOwner, 1, false);
+ }
+
+ DOC_exit:
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CSynchStateController::ReleaseController
+
+ Releases the controller.
+ --*/
+ void CSynchStateController::ReleaseController(void)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+
+ Release();
+ }
+
+ //////////////////
+ // //
+ // CSynchData //
+ // //
+ //////////////////
+
+ /*++
+ Method:
+ CSynchData::Release
+
+ Decremnt the reference count of the target synchdata and retrurns
+ it to the appropriate cache if the reference count reaches zero.
+ --*/
+ LONG CSynchData::Release(CPalThread * pthrCurrent)
+ {
+ VALIDATEOBJECT(this);
+
+ LONG lCount = InterlockedDecrement(&m_lRefCount);
+
+ _ASSERT_MSG(0 <= lCount,
+ "CSynchData %p with negative reference count [%d]\n",
+ this, lCount);
+
+ if (0 == lCount)
+ {
+ CPalSynchronizationManager * pSynchManager =
+ CPalSynchronizationManager::GetInstance();
+ bool fSharedObject = (SharedObject == m_odObjectDomain);
+
+ _ASSERT_MSG((fSharedObject && (NULL == m_ptrWTLHead.shrid)) ||
+ (!fSharedObject && (NULL == m_ptrWTLHead.ptr)),
+ "Final Release on CSynchData with threads still in "
+ "the waiting list\n");
+
+ TRACE("Disposing %s waitable object with SynchData @ "
+ "{shrid=%p, p=%p}\n",
+ (SharedObject == m_odObjectDomain) ? "shared" : "local",
+ (PVOID)m_shridThis, this);
+
+
+#ifdef SYNCH_STATISTICS
+ LONG lStatWaitCount = GetStatWaitCount();
+ LONG lStatContentionCount = GetStatContentionCount();
+ LONG lCount, lNewCount;
+
+ TRACE("Statistical data for SynchData of otiType=%u @ %p: WaitCount=%d "
+ "ContentionCount=%d\n", m_otiObjectTypeId, this, lStatWaitCount,
+ lStatContentionCount);
+
+ do {
+ lCount = g_rglStatWaitCount[m_otiObjectTypeId];
+ lNewCount = lCount + lStatWaitCount;
+ lNewCount = InterlockedCompareExchange(&(g_rglStatWaitCount[m_otiObjectTypeId]),
+ lNewCount, lCount);
+ } while (lCount != lNewCount);
+
+ lStatWaitCount = lNewCount;
+
+ do {
+ lCount = g_rglStatContentionCount[m_otiObjectTypeId];
+ lNewCount = lCount + lStatContentionCount;
+ lNewCount = InterlockedCompareExchange(&(g_rglStatContentionCount[m_otiObjectTypeId]),
+ lNewCount, lCount);
+ } while (lCount != lNewCount);
+
+ lStatContentionCount = lNewCount;
+
+ TRACE("Total current statistical data for otiType=%u objects: WaitCount=%d "
+ "ContentionCount=%d\n", m_otiObjectTypeId, lStatWaitCount,
+ lStatContentionCount);
+#endif // SYNCH_STATISTICS
+
+ if (fSharedObject)
+ {
+ pSynchManager->CacheAddSharedSynchData(pthrCurrent, m_shridThis);
+ }
+ else
+ {
+ pSynchManager->CacheAddLocalSynchData(pthrCurrent, this);
+ }
+ }
+
+ return lCount;
+ }
+
+ /*++
+ Method:
+ CSynchData::ReleaseWaiterWithoutBlocking
+
+ Performs all the steps needed to be done by the target thread in order
+ to wait without blocking on the object associated with the current
+ SynchData (e.g. modifying the object signal count accordingly with its
+ thread release semantics)
+
+ Note: this method must be called while holding the appropriate
+ synchronization locks (the local process synch lock if the target
+ object is local, both local and shared one if the object is shared).
+ --*/
+ PAL_ERROR CSynchData::ReleaseWaiterWithoutBlocking(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget)
+ {
+ VALIDATEOBJECT(this);
+
+ PAL_ERROR palErr = NO_ERROR;
+ CObjectType * potObjectType = GetObjectType();
+#ifdef _DEBUG
+ CObjectType::SignalingSemantics ssSignalingSemantics =
+ potObjectType->GetSignalingSemantics();
+#endif // _DEBUG
+ CObjectType::OwnershipSemantics osOwnershipSemantics =
+ potObjectType->GetOwnershipSemantics();
+ CObjectType::ThreadReleaseSemantics trsThreadReleaseSemantics =
+ potObjectType->GetThreadReleaseSemantics();
+ bool fReenteringObjWithOwnership = false;
+
+ _ASSERT_MSG(CObjectType::SignalingNotApplicable != ssSignalingSemantics,
+ "Signaling not applicable");
+ _ASSERT_MSG(CObjectType::ThreadReleaseNotApplicable !=
+ trsThreadReleaseSemantics,
+ "Thread releasing not applicable");
+ _ASSERT_MSG(CObjectType::SingleTransitionObject != ssSignalingSemantics ||
+ (CObjectType::ThreadReleaseHasNoSideEffects ==
+ trsThreadReleaseSemantics &&
+ CObjectType::NoOwner == osOwnershipSemantics),
+ "Conflicting object synchronization attributes "
+ "[SignalingSemantics=%u OwnershipSemantics=%u "
+ "ThreadReleaseSemantics=%u]\n", ssSignalingSemantics,
+ osOwnershipSemantics, trsThreadReleaseSemantics);
+
+ if (CObjectType::OwnershipTracked == osOwnershipSemantics &&
+ 0 < GetOwnershipCount())
+ {
+ // We are rentering an object with ownership: we need to skip
+ // the object unsignaling
+ fReenteringObjWithOwnership = true;
+ }
+
+ if (!fReenteringObjWithOwnership &&
+ CObjectType::ThreadReleaseAltersSignalCount == trsThreadReleaseSemantics)
+ {
+ _ASSERT_MSG(0 < GetSignalCount(),
+ "Internal error: operation would make signal count "
+ "negative - object should be signaled at this time "
+ "[signal count=%d]", GetSignalCount());
+ _ASSERT_MSG(CObjectType::OwnershipTracked != osOwnershipSemantics ||
+ 1 == GetSignalCount(),
+ "Ownable objects cannot have signal count greater "
+ "than zero [current SignalCount=%d]\n",
+ GetSignalCount());
+
+ // Unsignal the object
+ DecrementSignalCount();
+ }
+
+ if (CObjectType::OwnershipTracked == osOwnershipSemantics)
+ {
+ _ASSERT_MSG(0 == GetOwnershipCount() || 0 == GetSignalCount(),
+ "OwnershipCount and SignalCount with conflicting "
+ "values\n");
+
+ // Take ownership or increment ownership count.
+ // We do this after the object unsignaling to minimize possibilities
+ // of having both SignalCount and OwnershipCount greater than zero
+ // (see comment in AssignOwnershipToThread)
+ palErr = AssignOwnershipToThread(pthrCurrent, pthrTarget);
+
+ if (NO_ERROR != palErr)
+ {
+ ERROR("AssignOwnershipToThread failed with error %u; "
+ "ownership data on object with SynchData {shrid=%p p=%p} "
+ "may be corrupted\n", palErr, (void *)m_shridThis, this);
+ }
+ }
+
+#ifdef SYNCH_STATISTICS
+ if (NO_ERROR == palErr)
+ {
+ IncrementStatWaitCount();
+ }
+#endif
+ return palErr;
+
+ }
+
+ /*++
+ Method:
+ CSynchData::CanWaiterWaitWithoutBlocking
+
+ Returns whether or not the waiter thread can wait on the target object
+ without blocking (i.e. the objet is signaled)
+
+ Note: this method must be called while holding the appropriate
+ synchronization locks (the local process synch lock if the target
+ object is local, both local and shared one if the object is shared).
+ --*/
+ bool CSynchData::CanWaiterWaitWithoutBlocking(
+ CPalThread * pWaiterThread,
+ bool * pfAbandoned)
+ {
+ VALIDATEOBJECT(this);
+
+ bool fRetVal = (0 < GetSignalCount());
+ bool fAbandoned = false;
+ bool fOwnershipTracked = (CObjectType::OwnershipTracked ==
+ GetObjectType()->GetOwnershipSemantics());
+ if (fRetVal)
+ {
+ // Object signaled: thread can wait without blocking
+ if (fOwnershipTracked)
+ {
+ fAbandoned = IsAbandoned();
+ }
+
+ goto CWWWB_exit;
+ }
+
+ // Object not signaled: thread can wait without blocking only if the
+ // object is an ownable one, and it is owned by the current thread
+ if (fOwnershipTracked)
+ {
+ _ASSERT_MSG(0 < GetSignalCount() || 0 < GetOwnershipCount(),
+ "Objects with ownership must be either signaled or "
+ "owned by a thread\n");
+
+ if ((GetOwnerProcessID() == gPID) &&
+ (GetOwnerThread() == pWaiterThread) )
+ {
+ fRetVal = true;
+ goto CWWWB_exit;
+ }
+ }
+
+ CWWWB_exit:
+ *pfAbandoned = fAbandoned;
+ return fRetVal;
+ }
+
+ /*++
+ Method:
+ CSynchData::Signal
+
+ Sets the signal count of the object owning the target SynchData,
+ possibly triggering awakening of waiting threads.
+
+ Note: this method must be called while holding the appropriate
+ synchronization locks (the local process synch lock if the target
+ object is local, both local and shared one if the object is shared).
+ --*/
+ void CSynchData::Signal(
+ CPalThread * pthrCurrent,
+ LONG lSignalCount,
+ bool fWorkerThread)
+ {
+ VALIDATEOBJECT(this);
+
+ bool fThreadReleased = false;
+ bool fDelegatedSignaling = false;
+ bool fReleaseAltersSignalCount =
+ (CObjectType::ThreadReleaseAltersSignalCount ==
+ GetObjectType()->GetThreadReleaseSemantics());
+
+ _ASSERTE(0 <= lSignalCount);
+
+ // Preset the signal count to the new value, so that it can be used
+ // by ReleaseFirstWaiter when delegating signaling to another process
+ m_lSignalCount = lSignalCount;
+
+ while (m_lSignalCount > 0)
+ {
+ fThreadReleased = ReleaseFirstWaiter(pthrCurrent,
+ &fDelegatedSignaling,
+ fWorkerThread);
+ if (!fThreadReleased)
+ {
+ // No more threads to release: break out of the loop
+ // keeping the current signal count
+ break;
+ }
+ if (fReleaseAltersSignalCount)
+ {
+ // Adjust signal count
+ m_lSignalCount--;
+ }
+ if (fDelegatedSignaling)
+ {
+ // Object signaling has been delegated
+ m_lSignalCount = 0;
+ }
+ }
+
+ _ASSERT_MSG(CObjectType::OwnershipTracked !=
+ GetObjectType()->GetOwnershipSemantics() ||
+ 0 == GetOwnershipCount() || 0 == GetSignalCount(),
+ "Conflicting values for SignalCount [%d] and "
+ "OwnershipCount [%d]\n",
+ GetOwnershipCount(), GetSignalCount());
+
+ return;
+ }
+
+ /*++
+ Method:
+ CSynchData::ReleaseFirstWaiter
+
+ Releases the first thread from the front of the list of waiting threads
+ whose wait is fully satisfied, possibly triggering remote awakening (if
+ the target thread lives in a different process) or object signaling
+ delegation (if the target thread lives in a different processing and it
+ is blocked on a wait-all).
+
+ Note: this method must be called while holding the appropriate
+ synchronization locks (the local process synch lock if the target
+ object is local, both local and shared one if the object is shared).
+ --*/
+ bool CSynchData::ReleaseFirstWaiter(
+ CPalThread * pthrCurrent,
+ bool * pfDelegated,
+ bool fWorkerThread)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ bool fSharedSynchLock = false;
+ bool fSharedObject = (SharedObject == GetObjectDomain());
+ bool fThreadAwakened = false;
+ bool fDelegatedSignaling = false;
+ DWORD * pdwWaitState;
+ DWORD dwObjIdx;
+ SharedID shridItem = NULL, shridNextItem = NULL;
+ WaitingThreadsListNode * pwtlnItem, * pwtlnNextItem;
+ DWORD dwPid = gPID;
+ CPalSynchronizationManager * pSynchManager =
+ CPalSynchronizationManager::GetInstance();
+
+ VALIDATEOBJECT(this);
+
+ *pfDelegated = false;
+
+ if (fSharedObject)
+ {
+ shridItem = GetWTLHeadShmPtr();
+ pwtlnItem = SharedIDToTypePointer(WaitingThreadsListNode, shridItem);
+ }
+ else
+ {
+ pwtlnItem = GetWTLHeadPtr();
+ }
+
+ while (pwtlnItem)
+ {
+ VALIDATEOBJECT(pwtlnItem);
+
+ WaitCompletionState wcsWaitCompletionState;
+ bool fWaitAll = (0 != (WTLN_FLAG_WAIT_ALL & pwtlnItem->dwFlags));
+ pdwWaitState = SharedIDToTypePointer(DWORD,
+ pwtlnItem->shridWaitingState);
+
+ if (fSharedObject)
+ {
+ shridNextItem = pwtlnItem->ptrNext.shrid;
+ pwtlnNextItem = SharedIDToTypePointer(WaitingThreadsListNode,
+ shridNextItem);
+ }
+ else
+ {
+ pwtlnNextItem = pwtlnItem->ptrNext.ptr;
+ }
+
+ if (fWaitAll)
+ {
+ // Wait All: we need to find out whether the wait is satisfied,
+ // or it is not, or if that cannot be determined from within
+ // this process (WaitMayBeSatisfied); in this case we need to
+ // delegate the object signaling to the process hosting the
+ // thread that owns the current target WaitingThreadsListNode
+
+ // If the target object is local (fSharedObject == false)
+ // we're probably not holding the shared lock.
+ // If the wait is not a LocalWait, it involves at least one
+ // shared object. If that is the case, at this time we need
+ // to grab the shared lock. In fact IsRestOfWaitAllSatisfied
+ // and UnsignalRestOfLocalAwakeningWaitAll must be called
+ // atomically to prevent that another thread living
+ // in a different process could race with us stealing the
+ // signaling from one of the objects involved in the wait-all.
+ //
+ // Note: pwtlnItem->ptwiWaitInfo is valid only if the target
+ // wait originates in the current process. Anyway in the
+ // following 'if' we don't need to check that since we are
+ // already making sure that the object is local (!fSharedObject).
+ // If a wait involves at least one object local to this process,
+ // it can only be a wait performed by a thread in the current
+ // process, therefore pwtlnItem->ptwiWaitInfo is valid.
+
+ _ASSERTE(fSharedObject || pwtlnItem->dwProcessId == gPID);
+
+ if (!fSharedSynchLock && !fSharedObject &&
+ LocalWait != pwtlnItem->ptwiWaitInfo->wdWaitDomain)
+ {
+ CPalSynchronizationManager::AcquireSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = true;
+ }
+
+ // First check if the current target node is already marked for
+ // wait all check in progress, and in case skip it by setting
+ // wcsWaitCompletionState to WaitIsNotSatisfied
+ bool fMarkedForDelegatedObjectSingalingInProgress =
+ (0 != (WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS & pwtlnItem->dwFlags));
+
+ wcsWaitCompletionState =
+ fMarkedForDelegatedObjectSingalingInProgress ? WaitIsNotSatisfied :
+ IsRestOfWaitAllSatisfied(pwtlnItem);
+ }
+ else
+ {
+ // Normal Wait: the wait is satisfied by definition
+ wcsWaitCompletionState = WaitIsSatisfied;
+ }
+
+ if (WaitIsSatisfied == wcsWaitCompletionState)
+ {
+ //
+ // Target wait is satisfied
+ //
+ TRACE("Trying to switch wait state [%p] from WAIT/ALERTABLE "
+ "to ACTIVE for thread=%u\n",
+ pdwWaitState, pwtlnItem->dwThreadId);
+
+ if (CPalSynchronizationManager::InterlockedAwaken(pdwWaitState, FALSE))
+ {
+ TRACE("Succeeded switching wait state [%p] from WAIT/ALERTABLE "
+ "to TWS_ACTIVE for trhead=%u\n",
+ pdwWaitState, pwtlnItem->dwThreadId);
+
+ dwObjIdx = pwtlnItem->dwObjIndex;
+
+ if (dwPid == pwtlnItem->dwProcessId)
+ {
+ ///////////////////////////
+ //
+ // Local Thread Awakening
+ //
+ ///////////////////////////
+ ThreadWaitInfo * ptwiWaitInfo = pwtlnItem->ptwiWaitInfo;
+ bool fAbandoned = false;
+
+ if (CObjectType::OwnershipTracked ==
+ GetObjectType()->GetOwnershipSemantics())
+ {
+ // Get the abandoned status before resetting it by
+ // assigning ownership to target thread
+ fAbandoned = IsAbandoned();
+
+ // Assign ownership to target thread
+ // Note: This will cause both ownership count and
+ // signal count to be greater than zero at the
+ // same time; the signal count will be anyway
+ // decremented immediately by the caller
+ // CsynchData::Signal
+ palErr = AssignOwnershipToThread(pthrCurrent,
+ ptwiWaitInfo->pthrOwner);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Synch Worker: AssignOwnershipToThread "
+ "failed with error %u; ownership data on "
+ "object with SynchData %p may be "
+ "corrupted\n", palErr, this);
+ }
+ }
+
+ if (fWaitAll)
+ {
+ // Wait all satisfied: unsignal other objects
+ // involved in the wait
+ CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll(
+ pthrCurrent,
+ ptwiWaitInfo->pthrOwner,
+ pwtlnItem,
+ this);
+ }
+
+ TRACE("Unregistering wait for thread %u and waking it up "
+ "[pdwWaitState=%p]\n", pwtlnItem->dwThreadId,
+ pdwWaitState);
+
+ // Unregister the wait
+ pSynchManager->UnRegisterWait(pthrCurrent,
+ ptwiWaitInfo,
+ fSharedObject || fSharedSynchLock);
+
+ // After UnRegisterWait pwtlnItem is invalid
+ pwtlnItem = NULL;
+
+ palErr = CPalSynchronizationManager::WakeUpLocalThread(
+ pthrCurrent,
+ ptwiWaitInfo->pthrOwner,
+ fAbandoned ? MutexAbondoned : WaitSucceeded,
+ dwObjIdx);
+
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Failed to wakeup local thread %#x: "
+ "object signaling may be "
+ "lost\n", ptwiWaitInfo->pthrOwner->GetThreadId());
+ }
+ }
+ else
+ {
+ ///////////////////////////
+ //
+ // Remote Thread Awakening
+ //
+ ///////////////////////////
+
+ // Note: if we are here, this cannot be a wait-all
+ _ASSERT_MSG(!fWaitAll,
+ "Control should never reach this point if "
+ "target wait is a wait-all\n");
+
+ // Wake up remote thread
+ palErr = CPalSynchronizationManager::WakeUpRemoteThread(shridItem);
+
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Failed to dispatch remote awakening cmd to "
+ "worker thread in process pid=%d to wake up"
+ "thread tid=%#x; object signaling may be "
+ "lost\n", pwtlnItem->dwProcessId,
+ pwtlnItem->dwThreadId);
+ }
+ }
+
+ // A thread has been awakened
+ fThreadAwakened = true;
+
+ // break out of the while loop
+ break;
+ }
+ }
+ else if (WaitMayBeSatisfied == wcsWaitCompletionState)
+ {
+ //////////////////////////////////////////
+ //
+ // Wait All with remote thread awakening
+ //
+ //////////////////////////////////////////
+
+ //
+ // We need to transfer the object signaling to the process
+ // hosting the target waiter thread
+ //
+
+ _ASSERT_MSG(fWaitAll,
+ "IsRestOfWaitAllSatisfied() apparently "
+ "returned -1 on a normal (non wait all) "
+ "wait\n");
+ _ASSERT_MSG(fSharedObject,
+ "About to delegate object signaling to a remote "
+ "process, but the signaled object is actually "
+ "local\n");
+
+ // Delegate object signaling to target process
+ palErr = CPalSynchronizationManager::DelegateSignalingToRemoteProcess(
+ pthrCurrent,
+ pwtlnItem->dwProcessId,
+ pwtlnItem->ptrOwnerObjSynchData.shrid);
+
+ TRACE("Delegating object signaling for SynchData shrid=%p\n",
+ (VOID *)pwtlnItem->ptrOwnerObjSynchData.shrid);
+
+ if (NO_ERROR == palErr)
+ {
+ // A remote thread will be awakened
+ // This will also cause the object to be unsignaled by the
+ // code calling ReleaseFirstWaiter before releasing the
+ // synch locks, so no other WaitForMultipleObjects
+ // involving the target object may race stealing this
+ // particuklar object signaling
+ fThreadAwakened = true;
+
+ fDelegatedSignaling = true;
+
+ // break out of the while loop
+ break;
+ }
+ else
+ {
+ ERROR("Failed to delegate object signaling to remote "
+ "process %d. Looking for another waiter.\n",
+ pwtlnItem->dwProcessId);
+
+ // Go on: a different target waiter will be selected
+ }
+ }
+
+ if (fWorkerThread && fWaitAll && (dwPid == pwtlnItem->dwProcessId))
+ {
+ // Mark the target wait for object signaling
+ CPalSynchronizationManager::MarkWaitForDelegatedObjectSignalingInProgress(
+ pthrCurrent,
+ pwtlnItem);
+ }
+
+ // Go to the next item
+ shridItem = shridNextItem;
+ pwtlnItem = pwtlnNextItem;
+ }
+
+ if (fDelegatedSignaling)
+ {
+ *pfDelegated = true;
+ }
+ else if (fWorkerThread)
+ {
+ // Reset 'delegated object signaling in progress' flags
+ CPalSynchronizationManager::UnmarkTWListForDelegatedObjectSignalingInProgress(
+ this);
+ }
+
+ if (fSharedSynchLock)
+ {
+ CPalSynchronizationManager::ReleaseSharedSynchLock(pthrCurrent);
+ }
+ return fThreadAwakened;
+ }
+
+ /*++
+ Method:
+ CSynchData::Signal
+
+ Releases all the threads waiting on this object and living in the current
+ process.
+
+ Note: this method must be called while holding the appropriate
+ synchronization locks (the local process synch lock if the target
+ object is local, both local and shared one if the object is shared).
+ --*/
+ LONG CSynchData::ReleaseAllLocalWaiters(
+ CPalThread * pthrCurrent)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ LONG lAwakenedCount = 0;
+ bool fSharedSynchLock = false;
+ bool fSharedObject = (SharedObject == GetObjectDomain());
+ DWORD * pdwWaitState;
+ DWORD dwObjIdx;
+ SharedID shridItem = NULL, shridNextItem = NULL;
+ WaitingThreadsListNode * pwtlnItem, * pwtlnNextItem;
+ DWORD dwPid = gPID;
+ CPalSynchronizationManager * pSynchManager =
+ CPalSynchronizationManager::GetInstance();
+
+ VALIDATEOBJECT(this);
+
+ if (fSharedObject)
+ {
+ shridItem = GetWTLHeadShmPtr();
+ pwtlnItem = SharedIDToTypePointer(WaitingThreadsListNode, shridItem);
+ }
+ else
+ {
+ pwtlnItem = GetWTLHeadPtr();
+ }
+
+ while (pwtlnItem)
+ {
+ VALIDATEOBJECT(pwtlnItem);
+
+ bool fWaitAll = (0 != (WTLN_FLAG_WAIT_ALL & pwtlnItem->dwFlags));
+ pdwWaitState = SharedIDToTypePointer(DWORD,
+ pwtlnItem->shridWaitingState);
+
+ if (fSharedObject)
+ {
+ shridNextItem = pwtlnItem->ptrNext.shrid;
+ pwtlnNextItem = SharedIDToTypePointer(WaitingThreadsListNode,
+ shridNextItem);
+ }
+ else
+ {
+ pwtlnNextItem = pwtlnItem->ptrNext.ptr;
+ }
+
+ // See note in similar spot in ReleaseFirstWaiter
+
+ _ASSERTE(fSharedObject || pwtlnItem->dwProcessId == gPID);
+
+ if (!fSharedSynchLock && !fSharedObject &&
+ LocalWait != pwtlnItem->ptwiWaitInfo->wdWaitDomain)
+ {
+ CPalSynchronizationManager::AcquireSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = true;
+ }
+
+ if( dwPid == pwtlnItem->dwProcessId &&
+ (!fWaitAll || WaitIsSatisfied == IsRestOfWaitAllSatisfied(pwtlnItem)) )
+ {
+ //
+ // Target wait is satisfied
+ //
+ TRACE("Trying to switch wait state [%p] from WAIT/ALERTABLE "
+ "to ACTIVE for thread=%u\n",
+ pdwWaitState, pwtlnItem->dwThreadId);
+
+ if (CPalSynchronizationManager::InterlockedAwaken(pdwWaitState, FALSE))
+ {
+ TRACE("Succeeded switching wait state [%p] from WAIT/ALERTABLE "
+ "to TWS_ACTIVE for trhead=%u\n",
+ pdwWaitState, pwtlnItem->dwThreadId);
+
+ dwObjIdx = pwtlnItem->dwObjIndex;
+
+ ThreadWaitInfo * ptwiWaitInfo = pwtlnItem->ptwiWaitInfo;
+ bool fAbandoned = false;
+
+ if (CObjectType::OwnershipTracked ==
+ GetObjectType()->GetOwnershipSemantics())
+ {
+ // Get the abandoned status before resetting it by
+ // assigning ownership to target thread
+ fAbandoned = IsAbandoned();
+
+ // Assign ownership to target thread
+ palErr = AssignOwnershipToThread(pthrCurrent,
+ ptwiWaitInfo->pthrOwner);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Synch Worker: AssignOwnershipToThread "
+ "failed with error %u; ownership data on "
+ "object with SynchData %p may be "
+ "corrupted\n", palErr, this);
+ }
+ }
+
+ if (fWaitAll)
+ {
+ // Wait all satisfied: unsignal other objects
+ // involved in the wait
+ CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll(
+ pthrCurrent,
+ ptwiWaitInfo->pthrOwner,
+ pwtlnItem,
+ this);
+ }
+
+ TRACE("Unregistering wait for thread %u and waking it up "
+ "[pdwWaitState=%p]\n", pwtlnItem->dwThreadId,
+ pdwWaitState);
+
+ // Unregister the wait
+ pSynchManager->UnRegisterWait(pthrCurrent,
+ ptwiWaitInfo,
+ fSharedObject || fSharedSynchLock);
+
+ // After UnRegisterWait pwtlnItem is invalid
+ pwtlnItem = NULL;
+
+ palErr = CPalSynchronizationManager::WakeUpLocalThread(
+ pthrCurrent,
+ ptwiWaitInfo->pthrOwner,
+ fAbandoned ? MutexAbondoned : WaitSucceeded,
+ dwObjIdx);
+
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Failed to wakeup local thread %#x: "
+ "object signaling may be "
+ "lost\n", ptwiWaitInfo->pthrOwner->GetThreadId());
+ }
+ else
+ {
+ // A thread has been awakened
+ lAwakenedCount++;
+ }
+ }
+ }
+
+ // Go to the next item
+ shridItem = shridNextItem;
+ pwtlnItem = pwtlnNextItem;
+ }
+
+ if (fSharedSynchLock)
+ {
+ CPalSynchronizationManager::ReleaseSharedSynchLock(pthrCurrent);
+ }
+ return lAwakenedCount;
+ }
+
+ /*++
+ Method:
+ CSynchData::IsRestOfWaitAllSatisfied
+
+ Returns whether or not the current wait-all operation is fully satisfied,
+ assuming the current target object as signaled (i.e. whether or not all the
+ involved object, except the current one, are signaled).
+ It returns:
+ - WaitIsNotSatisfied if the wait-all is not fully satisfied.
+ - WaitIsSatisfied if the wait-all is fully satisfied.
+ - WaitMayBeSatisfied if the target thread lives in a different process and
+ therefore the wait may involve objects local to the remote process, and
+ as result is generally not possible to say whther or not the wait-all is
+ fully satisfied from the current process.
+
+ Note: this method must be called while holding the synchronization locks
+ appropriate to all the objects involved in the wait-all. If any
+ of the objects is shared, the caller must own both local and
+ shared synch locks; if no shared object is involved in the wait,
+ only the local synch lock is needed.
+ --*/
+ WaitCompletionState CSynchData::IsRestOfWaitAllSatisfied(
+ WaitingThreadsListNode * pwtlnNode)
+ {
+ int iSignaledOrOwnedObjCount = 0;
+ int iTgtCount = 0;
+ int i;
+ WaitCompletionState wcsWaitCompletionState = WaitIsNotSatisfied;
+ CSynchData * psdSynchDataItem = NULL;
+ ThreadWaitInfo * ptwiWaitInfo = NULL;
+
+ VALIDATEOBJECT(this);
+ VALIDATEOBJECT(pwtlnNode);
+
+ _ASSERT_MSG(0 != (WTLN_FLAG_WAIT_ALL & pwtlnNode->dwFlags),
+ "IsRestOfWaitAllSatisfied() called on a normal "
+ "(non wait all) wait");
+ _ASSERT_MSG((SharedObject == GetObjectDomain()) ==
+ (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNode->dwFlags)),
+ "WTLN_FLAG_OWNER_OBJECT_IS_SHARED in WaitingThreadsListNode "
+ "not consistent with target object's domain\n");
+
+ if(gPID != pwtlnNode->dwProcessId)
+ {
+ ////////////////////////////
+ //
+ // Remote Thread Awakening
+ //
+ ////////////////////////////
+
+ // Cannot determine whether or not the wait all is satisfied from
+ // this process
+ wcsWaitCompletionState = WaitMayBeSatisfied;
+ goto IROWAS_exit;
+ }
+
+ ///////////////////////////
+ //
+ // Local Thread Awakening
+ //
+ ///////////////////////////
+
+ ptwiWaitInfo = pwtlnNode->ptwiWaitInfo;
+
+ iTgtCount = ptwiWaitInfo->lObjCount;
+ for (i=0; i < iTgtCount; i++)
+ {
+ WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i];
+ bool fRetVal;
+ bool fIsAbandoned;
+
+ VALIDATEOBJECT(pwtlnItem);
+
+ if (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnItem->dwFlags))
+ {
+ psdSynchDataItem = SharedIDToTypePointer(CSynchData,
+ pwtlnItem->ptrOwnerObjSynchData.shrid);
+ }
+ else
+ {
+ psdSynchDataItem = pwtlnItem->ptrOwnerObjSynchData.ptr;
+ }
+
+ VALIDATEOBJECT(psdSynchDataItem);
+
+ if (pwtlnItem == pwtlnNode)
+ {
+ _ASSERT_MSG (this == psdSynchDataItem,
+ "pwtlnNode and pwtlnItem match, but this "
+ "and psdSynchDataItem don't\n");
+
+ // The target object (the one related to pwtlnNode) is counted as
+ // signaled/owned without checking it (also if it is not, as
+ // it normally happens when this method is called)
+ iSignaledOrOwnedObjCount++;
+ continue;
+ }
+
+ fRetVal = psdSynchDataItem->CanWaiterWaitWithoutBlocking(
+ ptwiWaitInfo->pthrOwner,
+ &fIsAbandoned);
+
+ if (fRetVal)
+ {
+ iSignaledOrOwnedObjCount++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (iSignaledOrOwnedObjCount < iTgtCount)
+ {
+ wcsWaitCompletionState = WaitIsNotSatisfied;
+ }
+ else
+ {
+ wcsWaitCompletionState = WaitIsSatisfied;
+ }
+
+ IROWAS_exit:
+ TRACE("IsRestOfWaitAllSatisfied() returning %u \n", wcsWaitCompletionState);
+
+ return wcsWaitCompletionState;
+ }
+
+
+ /*++
+ Method:
+ CSynchData::SetOwner
+
+ Blindly sets the thread whose CPalThread is passed as argument, as the
+ owner of the current object.
+ WARNING: this method discards any previous ownership data and does not
+ update the list of the object owned by the owner thread.
+
+ Note: this method must be called while holding the appropriate
+ synchronization locks (the local process synch lock if the target
+ object is local, both local and shared one if the object is shared).
+ --*/
+ void CSynchData::SetOwner(CPalThread * pOwnerThread)
+ {
+ VALIDATEOBJECT(this);
+
+ m_dwOwnerPid = gPID;
+ m_dwOwnerTid = pOwnerThread->GetThreadId();
+ m_pOwnerThread = pOwnerThread;
+ }
+
+ /*++
+ Method:
+ CSynchData::ResetOwnership
+
+ Resets current object's ownership data
+
+ Note: this method must be called while holding the appropriate
+ synchronization locks (the local process synch lock if the target
+ object is local, both local and shared one if the object is shared).
+ --*/
+ void CSynchData::ResetOwnership()
+ {
+ VALIDATEOBJECT(this);
+
+ m_lOwnershipCount = 0;
+ m_dwOwnerPid = 0;
+ m_dwOwnerTid = 0;
+ m_pOwnerThread = NULL;
+ m_poolnOwnedObjectListNode = NULL;
+ }
+
+ /*++
+ Method:
+ CSynchData::AssignOwnershipToThread
+
+ Assigns thw ownership of the current object to the target thread, performing
+ all the operations neede to mantain the correct status of ownership data,
+ also handling recursive object ownership acquisition
+
+ Note: this method must be called while holding the appropriate
+ synchronization locks (the local process synch lock if the target
+ object is local, both local and shared one if the object is shared).
+ --*/
+ PAL_ERROR CSynchData::AssignOwnershipToThread(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget)
+ {
+ // Note: when this method is called by ReleaseFirstWaiter there is
+ // a small time window in which both SignalCount and
+ // OwnershipCount can be greater than zero (which normally
+ // is illegal). Anyway that is fine since ReleaseFirstWaiter
+ // will restore the value right after, and such situation
+ // takes place while holding synchroniztion locks, so no
+ // other thread/process can access the object.
+
+ PAL_ERROR palErr = NO_ERROR;
+
+ _ASSERT_MSG(CObjectType::OwnershipTracked ==
+ GetObjectType()->GetOwnershipSemantics(),
+ "AssignOwnershipToThread called on a non-ownable "
+ "CSynchData [this=%p OwnershipSemantics=%u]\n", this,
+ GetObjectType()->GetOwnershipSemantics());
+
+
+ if (0 < m_lOwnershipCount)
+ {
+ //
+ // Object already owned, incrementing ownership count
+ //
+ _ASSERT_MSG(0 == GetSignalCount(),
+ "Conflicting OwnershipCount and SignalCount values\n");
+
+ _ASSERT_MSG(pthrTarget == m_pOwnerThread && gPID == m_dwOwnerPid,
+ "Attempting to assign ownership of CSynchData %p to "
+ "thread {pid=%#x tid=%#x} while it is currently owned "
+ "by thread {pid=%#x tid=%#x}\n", this,
+ gPID, pthrTarget->GetThreadId(),
+ m_dwOwnerPid, m_pOwnerThread->GetThreadId());
+
+ m_lOwnershipCount++;
+
+ TRACE("Incrementing ownership count for object with "
+ "SynchData %p owned by thread %#x [new count=%d]\n",
+ this, pthrTarget->GetThreadId(), m_lOwnershipCount);
+ }
+ else
+ {
+ //
+ // Acquiring currently not owned object
+ //
+ CPalSynchronizationManager * pSynchManager =
+ CPalSynchronizationManager::GetInstance();
+ OwnedObjectsListNode * pooln;
+
+ pooln = pSynchManager->CacheGetOwnedObjsListNode(pthrCurrent);
+ if (NULL == pooln)
+ {
+ ERROR("Out of memory while acquiring mutex ownership");
+ // In this case we bail out. It will result in no
+ // thread being awakend, which may cause deadlock,
+ // but it is anyway better than corrupting the
+ // ownership list
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ goto AOTT_exit;
+ }
+
+ TRACE("Assigning ownable object with SynchData %p to "
+ "thread %#x\n",
+ this, pthrTarget->GetThreadId());
+
+ // Set ownership data
+ SetOwner(pthrTarget);
+ SetOwnershipListNode(pooln);
+ SetOwnershipCount(1);
+ SetAbandoned(false);
+
+ // Add object to list of owned objs for current thread
+ pooln->pPalObjSynchData = this;
+ AddRef();
+ pthrTarget->synchronizationInfo.AddObjectToOwnedList(pooln);
+ }
+
+ AOTT_exit:
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CSynchData::WaiterEnqueue
+
+ Adds the WaitingThreadsListNode passed as argument at the end of the
+ list of WaitingThreadsListNode for the current object, representing
+ the threads waiting on the current object. The target SynchData is
+ assumed to be local to the current process
+
+ Note: this method must be called while holding the local process
+ synchronization lock.
+ --*/
+ void CSynchData::WaiterEnqueue(WaitingThreadsListNode * pwtlnNewNode, bool fPrioritize)
+ {
+ VALIDATEOBJECT(this);
+ VALIDATEOBJECT(pwtlnNewNode);
+
+ _ASSERT_MSG(ProcessLocalObject == GetObjectDomain(),
+ "Trying to enqueue a WaitingThreadsListNode as local "
+ "on a shared object\n");
+ _ASSERT_MSG(0 == (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNewNode->dwFlags),
+ "Trying to add a WaitingThreadsListNode marked as shared "
+ "as it was a local one\n");
+
+ if (!fPrioritize)
+ {
+ // Enqueue normally to the end of the queue
+ WaitingThreadsListNode * pwtlnCurrLast = m_ptrWTLTail.ptr;
+
+ pwtlnNewNode->ptrNext.ptr = NULL;
+ if (NULL == pwtlnCurrLast)
+ {
+ _ASSERT_MSG(NULL == m_ptrWTLHead.ptr,
+ "Corrupted waiting list on local CSynchData @ %p\n",
+ this);
+
+ pwtlnNewNode->ptrPrev.ptr = NULL;
+ m_ptrWTLHead.ptr = pwtlnNewNode;
+ m_ptrWTLTail.ptr = pwtlnNewNode;
+ }
+ else
+ {
+ VALIDATEOBJECT(pwtlnCurrLast);
+
+ pwtlnNewNode->ptrPrev.ptr = pwtlnCurrLast;
+ pwtlnCurrLast->ptrNext.ptr = pwtlnNewNode;
+ m_ptrWTLTail.ptr = pwtlnNewNode;
+ }
+ }
+ else
+ {
+ // The wait is prioritized, enqueue to the beginning of the queue
+ WaitingThreadsListNode * pwtlnCurrFirst = m_ptrWTLHead.ptr;
+
+ pwtlnNewNode->ptrPrev.ptr = NULL;
+ if (NULL == pwtlnCurrFirst)
+ {
+ _ASSERT_MSG(NULL == m_ptrWTLTail.ptr,
+ "Corrupted waiting list on local CSynchData @ %p\n",
+ this);
+
+ pwtlnNewNode->ptrNext.ptr = NULL;
+ m_ptrWTLHead.ptr = pwtlnNewNode;
+ m_ptrWTLTail.ptr = pwtlnNewNode;
+ }
+ else
+ {
+ VALIDATEOBJECT(pwtlnCurrFirst);
+
+ pwtlnNewNode->ptrNext.ptr = pwtlnCurrFirst;
+ pwtlnCurrFirst->ptrPrev.ptr = pwtlnNewNode;
+ m_ptrWTLHead.ptr = pwtlnNewNode;
+ }
+ }
+
+ m_ulcWaitingThreads += 1;
+
+ return;
+ }
+
+ /*++
+ Method:
+ CSynchData::SharedWaiterEnqueue
+
+ Adds the WaitingThreadsListNode passed as argument at the end of the
+ list of WaitingThreadsListNode for the current object, representing
+ the threads waiting on the current object. The target SynchData is
+ assumed to be shared among processes
+
+ Note: this method must be called while holding both local and shared
+ synchronization locks.
+ --*/
+ void CSynchData::SharedWaiterEnqueue(SharedID shridNewNode, bool fPrioritize)
+ {
+ VALIDATEOBJECT(this);
+
+ _ASSERT_MSG(SharedObject == GetObjectDomain(),
+ "Trying to enqueue a WaitingThreadsListNode as shared "
+ "on a local object\n");
+
+ if (!fPrioritize)
+ {
+ // Enqueue normally to the end of the queue
+ SharedID shridCurrLast;
+ WaitingThreadsListNode * pwtlnCurrLast, * pwtlnNewNode;
+
+ shridCurrLast = m_ptrWTLTail.shrid;
+ pwtlnCurrLast = SharedIDToTypePointer(WaitingThreadsListNode, shridCurrLast);
+ pwtlnNewNode = SharedIDToTypePointer(WaitingThreadsListNode, shridNewNode);
+
+ _ASSERT_MSG(1 == (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNewNode->dwFlags),
+ "Trying to add a WaitingThreadsListNode marked as local "
+ "as it was a shared one\n");
+
+ VALIDATEOBJECT(pwtlnNewNode);
+
+ pwtlnNewNode->ptrNext.shrid = NULL;
+ if (NULL == pwtlnCurrLast)
+ {
+ _ASSERT_MSG(NULL == m_ptrWTLHead.shrid,
+ "Corrupted waiting list on shared CSynchData at "
+ "{shrid=%p, p=%p}\n", m_shridThis, this);
+
+ pwtlnNewNode->ptrPrev.shrid = NULL;
+ m_ptrWTLHead.shrid = shridNewNode;
+ m_ptrWTLTail.shrid = shridNewNode;
+ }
+ else
+ {
+ VALIDATEOBJECT(pwtlnCurrLast);
+
+ pwtlnNewNode->ptrPrev.shrid = shridCurrLast;
+ pwtlnCurrLast->ptrNext.shrid = shridNewNode;
+ m_ptrWTLTail.shrid = shridNewNode;
+ }
+ }
+ else
+ {
+ // The wait is prioritized, enqueue to the beginning of the queue
+ SharedID shridCurrFirst;
+ WaitingThreadsListNode * pwtlnCurrFirst, * pwtlnNewNode;
+
+ shridCurrFirst = m_ptrWTLHead.shrid;
+ pwtlnCurrFirst = SharedIDToTypePointer(WaitingThreadsListNode, shridCurrFirst);
+ pwtlnNewNode = SharedIDToTypePointer(WaitingThreadsListNode, shridNewNode);
+
+ _ASSERT_MSG(1 == (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNewNode->dwFlags),
+ "Trying to add a WaitingThreadsListNode marked as local "
+ "as it was a shared one\n");
+
+ VALIDATEOBJECT(pwtlnNewNode);
+
+ pwtlnNewNode->ptrPrev.shrid = NULL;
+ if (NULL == pwtlnCurrFirst)
+ {
+ _ASSERT_MSG(NULL == m_ptrWTLTail.shrid,
+ "Corrupted waiting list on shared CSynchData at "
+ "{shrid=%p, p=%p}\n", m_shridThis, this);
+
+ pwtlnNewNode->ptrNext.shrid = NULL;
+ m_ptrWTLHead.shrid = shridNewNode;
+ m_ptrWTLTail.shrid = shridNewNode;
+ }
+ else
+ {
+ VALIDATEOBJECT(pwtlnCurrFirst);
+
+ pwtlnNewNode->ptrNext.shrid = shridCurrFirst;
+ pwtlnCurrFirst->ptrPrev.shrid = shridNewNode;
+ m_ptrWTLHead.shrid = shridNewNode;
+ }
+ }
+
+ m_ulcWaitingThreads += 1;
+
+ return;
+ }
+
+#ifdef SYNCH_OBJECT_VALIDATION
+ CSynchData::~CSynchData()
+ {
+ ValidateObject(true);
+ InvalidateObject();
+ }
+ /*++
+ Method:
+ CSynchData::ValidateObject
+
+ Makes sure that the signature at the beginning and at the end of the
+ current object are those of a currently alive object (i.e. the object
+ has been constructed and does not appear to have been overwritten)
+ --*/
+ void CSynchData::ValidateObject(bool fDestructor)
+ {
+ TRACE("Verifying in-use CSynchData @ %p\n", this);
+ _ASSERT_MSG(HeadSignature == m_dwDebugHeadSignature,
+ "CSynchData header signature corruption [p=%p]", this);
+ _ASSERT_MSG(TailSignature == m_dwDebugTailSignature,
+ "CSynchData trailer signature corruption [p=%p]", this);
+ _ASSERT_MSG((fDestructor && 0 == m_lRefCount) ||
+ (!fDestructor && 0 < m_lRefCount),
+ "CSynchData %p with NULL reference count\n", this);
+ }
+ /*++
+ Method:
+ CSynchData::ValidateEmptyObject
+
+ Makes sure that the signature at the beginning and at the end of the
+ current object are not those of a currently alive object (i.e. the
+ object has not yet been constructed or it has alread been destructed)
+ --*/
+ void CSynchData::ValidateEmptyObject()
+ {
+ TRACE("Verifying empty CSynchData @ %p\n", this);
+ _ASSERT_MSG(HeadSignature != m_dwDebugHeadSignature,
+ "CSynchData header previously signed [p=%p]", this);
+ _ASSERT_MSG(TailSignature != m_dwDebugTailSignature,
+ "CSynchData trailer previously signed [p=%p]", this);
+ }
+ /*++
+ Method:
+ CSynchData::InvalidateObject
+
+ Turns signatures from alive object to destructed object
+ --*/
+ void CSynchData::InvalidateObject()
+ {
+ TRACE("Invalidating CSynchData @ %p\n", this);
+ m_dwDebugHeadSignature = EmptySignature;
+ m_dwDebugTailSignature = EmptySignature;
+ }
+#endif // SYNCH_OBJECT_VALIDATION
+}
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+
+
+Module Name:
+
+ synchmanager.cpp
+
+Abstract:
+ Implementation of Synchronization Manager and related objects
+
+
+
+--*/
+
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(SYNC); // some headers have code with asserts, so do this first
+
+#include "synchmanager.hpp"
+#include "pal/file.hpp"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sched.h>
+#include <signal.h>
+#include <errno.h>
+#if HAVE_POLL
+#include <poll.h>
+#else
+#include "pal/fakepoll.h"
+#endif // HAVE_POLL
+
+#include <algorithm>
+
+const int CorUnix::CThreadSynchronizationInfo::PendingSignalingsArraySize;
+
+namespace CorUnix
+{
+ /////////////////////////////////
+ // //
+ // WaitingThreadsListNode //
+ // //
+ /////////////////////////////////
+#ifdef SYNCH_OBJECT_VALIDATION
+ _WaitingThreadsListNode::_WaitingThreadsListNode()
+ {
+ ValidateEmptyObject();
+ dwDebugHeadSignature = HeadSignature;
+ dwDebugTailSignature = TailSignature;
+ }
+ _WaitingThreadsListNode::~_WaitingThreadsListNode()
+ {
+ ValidateObject();
+ InvalidateObject();
+ }
+ void _WaitingThreadsListNode::ValidateObject()
+ {
+ TRACE("Verifying WaitingThreadsListNode @ %p\n", this);
+ _ASSERT_MSG(HeadSignature == dwDebugHeadSignature,
+ "WaitingThreadsListNode header signature corruption [p=%p]",
+ this);
+ _ASSERT_MSG(TailSignature == dwDebugTailSignature,
+ "WaitingThreadsListNode trailer signature corruption [p=%p]",
+ this);
+ }
+ void _WaitingThreadsListNode::ValidateEmptyObject()
+ {
+ _ASSERT_MSG(HeadSignature != dwDebugHeadSignature,
+ "WaitingThreadsListNode header previously signed [p=%p]",
+ this);
+ _ASSERT_MSG(TailSignature != dwDebugTailSignature,
+ "WaitingThreadsListNode trailer previously signed [p=%p]",
+ this);
+ }
+ void _WaitingThreadsListNode::InvalidateObject()
+ {
+ TRACE("Invalidating WaitingThreadsListNode @ %p\n", this);
+ dwDebugHeadSignature = EmptySignature;
+ dwDebugTailSignature = EmptySignature;
+ }
+#endif // SYNCH_OBJECT_VALIDATION
+
+ //////////////////////////////
+ // //
+ // CPalSynchMgrController //
+ // //
+ //////////////////////////////
+
+ /*++
+ Method:
+ CPalSynchMgrController::CreatePalSynchronizationManager
+
+ Creates the Synchronization Manager. It must be called once per process.
+ --*/
+ IPalSynchronizationManager * CPalSynchMgrController::CreatePalSynchronizationManager()
+ {
+ return CPalSynchronizationManager::CreatePalSynchronizationManager();
+ };
+
+ /*++
+ Method:
+ CPalSynchMgrController::StartWorker
+
+ Starts the Synchronization Manager's Worker Thread
+ --*/
+ PAL_ERROR CPalSynchMgrController::StartWorker(
+ CPalThread * pthrCurrent)
+ {
+ return CPalSynchronizationManager::StartWorker(pthrCurrent);
+ }
+
+ /*++
+ Method:
+ CPalSynchMgrController::PrepareForShutdown
+
+ This method performs the part of Synchronization Manager's shutdown that
+ needs to be carried out when core PAL subsystems are still active
+ --*/
+ PAL_ERROR CPalSynchMgrController::PrepareForShutdown()
+ {
+ return CPalSynchronizationManager::PrepareForShutdown();
+ }
+
+ //////////////////////////////////
+ // //
+ // CPalSynchronizationManager //
+ // //
+ //////////////////////////////////
+
+ IPalSynchronizationManager * g_pSynchronizationManager = NULL;
+
+ CPalSynchronizationManager * CPalSynchronizationManager::s_pObjSynchMgr = NULL;
+ Volatile<LONG> CPalSynchronizationManager::s_lInitStatus = SynchMgrStatusIdle;
+ CRITICAL_SECTION CPalSynchronizationManager::s_csSynchProcessLock;
+ CRITICAL_SECTION CPalSynchronizationManager::s_csMonitoredProcessesLock;
+
+ CPalSynchronizationManager::CPalSynchronizationManager()
+ : m_dwWorkerThreadTid(0),
+ m_pipoThread(NULL),
+ m_pthrWorker(NULL),
+ m_iProcessPipeRead(-1),
+ m_iProcessPipeWrite(-1),
+ m_pmplnMonitoredProcesses(NULL),
+ m_lMonitoredProcessesCount(0),
+ m_pmplnExitedNodes(NULL),
+ m_cacheWaitCtrlrs(CtrlrsCacheMaxSize),
+ m_cacheStateCtrlrs(CtrlrsCacheMaxSize),
+ m_cacheSynchData(SynchDataCacheMaxSize),
+ m_cacheSHRSynchData(SynchDataCacheMaxSize),
+ m_cacheWTListNodes(WTListNodeCacheMaxSize),
+ m_cacheSHRWTListNodes(WTListNodeCacheMaxSize),
+ m_cacheOwnedObjectsListNodes(OwnedObjectsListCacheMaxSize)
+ {
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ m_iKQueue = -1;
+ // Initialize data to 0 and flags to EV_EOF
+ EV_SET(&m_keProcessPipeEvent, 0, 0, EV_EOF, 0, 0, 0);
+#endif // HAVE_KQUEUE
+ }
+
+ CPalSynchronizationManager::~CPalSynchronizationManager()
+ {
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::BlockThread
+
+ Called by a thread to go to sleep for a wait or a sleep
+
+ NOTE: This method must must be called without holding any
+ synchronization lock (as well as other locks)
+ --*/
+ PAL_ERROR CPalSynchronizationManager::BlockThread(
+ CPalThread *pthrCurrent,
+ DWORD dwTimeout,
+ bool fAlertable,
+ bool fIsSleep,
+ ThreadWakeupReason *ptwrWakeupReason,
+ DWORD * pdwSignaledObject)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ ThreadWakeupReason twrWakeupReason = WaitFailed;
+ DWORD * pdwWaitState;
+ DWORD dwWaitState = 0;
+ DWORD dwSigObjIdx = 0;
+ bool fEarlyDeath = false;
+
+ pdwWaitState = SharedIDToTypePointer(DWORD,
+ pthrCurrent->synchronizationInfo.m_shridWaitAwakened);
+
+ _ASSERT_MSG(NULL != pdwWaitState,
+ "Got NULL pdwWaitState from m_shridWaitAwakened=%p\n",
+ (VOID *)pthrCurrent->synchronizationInfo.m_shridWaitAwakened);
+
+ if (fIsSleep || fAlertable)
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ goto BT_exit;
+ }
+
+ {
+ TRACE("Current thread is about to block for waiting\n");
+
+ palErr = ThreadNativeWait(
+ &pthrCurrent->synchronizationInfo.m_tnwdNativeData,
+ dwTimeout,
+ &twrWakeupReason,
+ &dwSigObjIdx);
+
+ if (NO_ERROR != palErr)
+ {
+ ERROR("ThreadNativeWait() failed [palErr=%d]\n", palErr);
+ twrWakeupReason = WaitFailed;
+ goto BT_exit;
+ }
+
+ TRACE("ThreadNativeWait returned {WakeupReason=%u "
+ "dwSigObjIdx=%u}\n", twrWakeupReason, dwSigObjIdx);
+ }
+
+ if (WaitTimeout == twrWakeupReason)
+ {
+ // timeout reached. set wait state back to 'active'
+ dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE : TWS_WAITING);
+
+ TRACE("Current thread awakened for timeout: switching wait "
+ "state [%p] from %u to TWS_ACTIVE [current *pdwWaitState=%u]\n",
+ pdwWaitState, dwWaitState, *pdwWaitState);
+
+ DWORD dwOldWaitState = InterlockedCompareExchange(
+ (LONG *)pdwWaitState,
+ TWS_ACTIVE, (LONG)dwWaitState);
+
+ switch (dwOldWaitState)
+ {
+ case TWS_ACTIVE:
+ // We were already ACTIVE; someone decided to wake up this
+ // thread sometime between the moment the native wait
+ // timed out and here. Since the signaling side succeeded
+ // its InterlockedCompareExchange, it will signal the
+ // condition/predicate pair (we just raced overtaking it);
+ // therefore we need to clear the condition/predicate
+ // by waiting on it one more time.
+ // That will also cause this method to report a signal
+ // rather than a timeout.
+ // In the remote signaling scenario, this second wait
+ // also makes sure that the shared id passed over the
+ // process pipe is valid for the entire duration of time
+ // in which the worker thread deals with it
+ TRACE("Current thread already ACTIVE: a signaling raced "
+ "with the timeout: re-waiting natively to clear the "
+ "predicate\n");
+
+ palErr = ThreadNativeWait(
+ &pthrCurrent->synchronizationInfo.m_tnwdNativeData,
+ SecondNativeWaitTimeout,
+ &twrWakeupReason,
+ &dwSigObjIdx);
+
+ if (NO_ERROR != palErr)
+ {
+ ERROR("ThreadNativeWait() failed [palErr=%d]\n",
+ palErr);
+ twrWakeupReason = WaitFailed;
+ }
+
+ if (WaitTimeout == twrWakeupReason)
+ {
+ ERROR("Second native wait timed out\n");
+ }
+
+ break;
+ case TWS_EARLYDEATH:
+ // Thread is about to be suspended by TerminateProcess.
+ // Anyway, if the wait timed out, we still want to
+ // (try to) unregister the wait (especially if it
+ // involves shared objects)
+ WARN("Thread is about to be suspended by TerminateProcess\n");
+ fEarlyDeath = true;
+ palErr = WAIT_FAILED;
+ break;
+ case TWS_WAITING:
+ case TWS_ALERTABLE:
+ default:
+ _ASSERT_MSG(dwOldWaitState == dwWaitState,
+ "Unexpected wait status: actual=%u, expected=%u\n",
+ dwOldWaitState, dwWaitState);
+ break;
+ }
+ }
+
+ switch (twrWakeupReason)
+ {
+ case WaitTimeout:
+ {
+ // Awakened for timeout: we need to unregister the wait
+ ThreadWaitInfo * ptwiWaitInfo;
+
+ TRACE("Current thread awakened for timeout: unregistering the wait\n");
+
+ // Local lock
+ AcquireLocalSynchLock(pthrCurrent);
+
+ ptwiWaitInfo = GetThreadWaitInfo(pthrCurrent);
+
+ // Unregister the wait
+ // Note: UnRegisterWait will take care of grabbing the shared synch lock, if needed.
+ UnRegisterWait(pthrCurrent, ptwiWaitInfo, false);
+
+ // Unlock
+ ReleaseLocalSynchLock(pthrCurrent);
+
+ break;
+ }
+ case WaitSucceeded:
+ case MutexAbondoned:
+ *pdwSignaledObject = dwSigObjIdx;
+ break;
+ default:
+ // 'Alerted' and 'WaitFailed' go through this case
+ break;
+ }
+
+ // Set the returned wakeup reason
+ *ptwrWakeupReason = twrWakeupReason;
+
+ TRACE("Current thread is now active [WakeupReason=%u SigObjIdx=%u]\n",
+ twrWakeupReason, dwSigObjIdx);
+
+ _ASSERT_MSG(TWS_ACTIVE == VolatileLoad(pdwWaitState) ||
+ TWS_EARLYDEATH == VolatileLoad(pdwWaitState),
+ "Unexpected thread wait state %u\n", VolatileLoad(pdwWaitState));
+
+ BT_exit:
+ if (fEarlyDeath)
+ {
+ ThreadPrepareForShutdown();
+ }
+
+ return palErr;
+ }
+
+ PAL_ERROR CPalSynchronizationManager::ThreadNativeWait(
+ ThreadNativeWaitData * ptnwdNativeWaitData,
+ DWORD dwTimeout,
+ ThreadWakeupReason * ptwrWakeupReason,
+ DWORD * pdwSignaledObject)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ int iRet, iWaitRet = 0;
+ struct timespec tsAbsTmo;
+
+ TRACE("ThreadNativeWait(ptnwdNativeWaitData=%p, dwTimeout=%u, ...)\n",
+ ptnwdNativeWaitData, dwTimeout);
+
+ if (dwTimeout != INFINITE)
+ {
+ // Calculate absolute timeout
+ palErr = GetAbsoluteTimeout(dwTimeout, &tsAbsTmo, /*fPreferMonotonicClock*/ TRUE);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Failed to convert timeout to absolute timeout\n");
+ goto TNW_exit;
+ }
+ }
+
+ // Lock the mutex
+ iRet = pthread_mutex_lock(&ptnwdNativeWaitData->mutex);
+ if (0 != iRet)
+ {
+ ERROR("Internal Error: cannot lock mutex\n");
+ palErr = ERROR_INTERNAL_ERROR;
+ *ptwrWakeupReason = WaitFailed;
+ goto TNW_exit;
+ }
+
+ while (FALSE == ptnwdNativeWaitData->iPred)
+ {
+ if (INFINITE == dwTimeout)
+ {
+ iWaitRet = pthread_cond_wait(&ptnwdNativeWaitData->cond,
+ &ptnwdNativeWaitData->mutex);
+ }
+ else
+ {
+ iWaitRet = pthread_cond_timedwait(&ptnwdNativeWaitData->cond,
+ &ptnwdNativeWaitData->mutex,
+ &tsAbsTmo);
+ }
+
+ if (ETIMEDOUT == iWaitRet)
+ {
+ _ASSERT_MSG(INFINITE != dwTimeout,
+ "Got ETIMEDOUT despite timeout being INFINITE\n");
+ break;
+ }
+ else if (0 != iWaitRet)
+ {
+ ERROR("pthread_cond_%swait returned %d [errno=%d (%s)]\n",
+ (INFINITE == dwTimeout) ? "" : "timed",
+ iWaitRet, errno, strerror(errno));
+ palErr = ERROR_INTERNAL_ERROR;
+ break;
+ }
+ }
+
+ // Reset the predicate
+ if (0 == iWaitRet)
+ {
+ // We don't want to reset the predicate if pthread_cond_timedwait
+ // timed out racing with a pthread_cond_signal. When
+ // pthread_cond_timedwait times out, it needs to grab the mutex
+ // before returning. At timeout time, it may happen that the
+ // signaling thread just grabbed the mutex, but it hasn't called
+ // pthread_cond_signal yet. In this scenario pthread_cond_timedwait
+ // will have to wait for the signaling side to release the mutex.
+ // As a result it will return with error timeout, but the predicate
+ // will be set. Since pthread_cond_timedwait timed out, the
+ // predicate value is intended for the next signal. In case of a
+ // object signaling racing with a wait timeout this predicate value
+ // will be picked up by the 'second native wait' (see comments in
+ // BlockThread).
+
+ ptnwdNativeWaitData->iPred = FALSE;
+ }
+
+ // Unlock the mutex
+ iRet = pthread_mutex_unlock(&ptnwdNativeWaitData->mutex);
+ if (0 != iRet)
+ {
+ ERROR("Cannot unlock mutex [err=%d]\n", iRet);
+ palErr = ERROR_INTERNAL_ERROR;
+ goto TNW_exit;
+ }
+
+ _ASSERT_MSG(ETIMEDOUT != iRet || INFINITE != dwTimeout, "Got timeout return code with INFINITE timeout\n");
+
+ if (0 == iWaitRet)
+ {
+ *ptwrWakeupReason = ptnwdNativeWaitData->twrWakeupReason;
+ *pdwSignaledObject = ptnwdNativeWaitData->dwObjectIndex;
+ }
+ else if (ETIMEDOUT == iWaitRet)
+ {
+ *ptwrWakeupReason = WaitTimeout;
+ }
+
+ TNW_exit:
+ TRACE("ThreadNativeWait: returning %u [WakeupReason=%u]\n", palErr, *ptwrWakeupReason);
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::AbandonObjectsOwnedByThread
+
+ This method is called by a thread at thread-exit time to abandon
+ any currently owned waitable object (mutexes). If pthrTarget is
+ different from pthrCurrent, AbandonObjectsOwnedByThread assumes
+ to be called whether by TerminateThread or at shutdown time. See
+ comments below for more details
+ --*/
+ PAL_ERROR CPalSynchronizationManager::AbandonObjectsOwnedByThread(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ OwnedObjectsListNode * poolnItem;
+ bool fSharedSynchLock = false;
+ CThreadSynchronizationInfo * pSynchInfo = &pthrTarget->synchronizationInfo;
+ CPalSynchronizationManager * pSynchManager = GetInstance();
+
+ // Local lock
+ AcquireLocalSynchLock(pthrCurrent);
+
+ // Abandon owned objects
+ while (NULL != (poolnItem = pSynchInfo->RemoveFirstObjectFromOwnedList()))
+ {
+ CSynchData * psdSynchData = poolnItem->pPalObjSynchData;
+
+ _ASSERT_MSG(NULL != psdSynchData,
+ "NULL psdSynchData pointer in ownership list node\n");
+
+ VALIDATEOBJECT(psdSynchData);
+
+ TRACE("Abandoning object with SynchData at %p\n", psdSynchData);
+
+ if (!fSharedSynchLock &&
+ (SharedObject == psdSynchData->GetObjectDomain()))
+ {
+ AcquireSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = true;
+ }
+
+ // Reset ownership data
+ psdSynchData->ResetOwnership();
+
+ // Set abandoned status; in case there is a thread to be released:
+ // - if the thread is local, ReleaseFirstWaiter will reset the
+ // abandoned status
+ // - if the thread is remote, the remote worker thread will use
+ // the value and reset it
+ psdSynchData->SetAbandoned(true);
+
+ // Signal the object and trigger thread awakening
+ psdSynchData->Signal(pthrCurrent, 1, false);
+
+ // Release reference to to SynchData
+ psdSynchData->Release(pthrCurrent);
+
+ // Return node to the cache
+ pSynchManager->m_cacheOwnedObjectsListNodes.Add(pthrCurrent, poolnItem);
+ }
+
+ if (pthrTarget != pthrCurrent)
+ {
+ // If the target thead is not the current one, we are being called
+ // at shutdown time, right before the target thread is suspended,
+ // or anyway the target thread is being terminated.
+ // In this case we switch its wait state to TWS_EARLYDEATH so that,
+ // if the thread is currently waiting/sleeping and it wakes up
+ // before shutdown code manage to suspend it, it will be rerouted
+ // to ThreadPrepareForShutdown (that will be done without holding
+ // any internal lock, in a way to accomodate shutdown time thread
+ // suspension).
+ // At this time we also unregister the wait, so no dummy nodes are
+ // left around on waiting objects.
+ // The TWS_EARLYDEATH wait-state will also prevent the thread from
+ // successfully registering for a possible new wait in the same
+ // time window.
+ LONG lTWState;
+ DWORD * pdwWaitState;
+
+ pdwWaitState = SharedIDToTypePointer(DWORD, pthrTarget->synchronizationInfo.m_shridWaitAwakened);
+ lTWState = InterlockedExchange((LONG *)pdwWaitState, TWS_EARLYDEATH);
+
+ if (( ((LONG)TWS_WAITING == lTWState) || ((LONG)TWS_ALERTABLE == lTWState) ) &&
+ (0 < pSynchInfo->m_twiWaitInfo.lObjCount))
+ {
+ // Unregister the wait
+ // Note: UnRegisterWait will take care of grabbing the shared synch lock, if needed.
+ UnRegisterWait(pthrCurrent, &pSynchInfo->m_twiWaitInfo, fSharedSynchLock);
+ }
+ }
+
+ // Unlock
+ if (fSharedSynchLock)
+ {
+ ReleaseSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = false;
+ }
+
+ ReleaseLocalSynchLock(pthrCurrent);
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::GetSynchWaitControllersForObjects
+
+ Returns an array of wait controllers, one for each of the objects
+ in rgObjects
+ --*/
+ PAL_ERROR CPalSynchronizationManager::GetSynchWaitControllersForObjects(
+ CPalThread *pthrCurrent,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ ISynchWaitController * rgControllers[])
+ {
+ return GetSynchControllersForObjects(pthrCurrent,
+ rgObjects,
+ dwObjectCount,
+ (void **)rgControllers,
+ CSynchControllerBase::WaitController);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::GetSynchStateControllersForObjects
+
+ Returns an array of state controllers, one for each of the objects
+ in rgObjects
+ --*/
+ PAL_ERROR CPalSynchronizationManager::GetSynchStateControllersForObjects(
+ CPalThread *pthrCurrent,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ ISynchStateController *rgControllers[])
+ {
+ return GetSynchControllersForObjects(pthrCurrent,
+ rgObjects,
+ dwObjectCount,
+ (void **)rgControllers,
+ CSynchControllerBase::StateController);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::GetSynchControllersForObjects
+
+ Internal common implementation for GetSynchWaitControllersForObjects and
+ GetSynchStateControllersForObjects
+ --*/
+ PAL_ERROR CPalSynchronizationManager::GetSynchControllersForObjects(
+ CPalThread *pthrCurrent,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ void ** ppvControllers,
+ CSynchControllerBase::ControllerType ctCtrlrType)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ unsigned int uIdx, uCount = 0, uSharedObjectCount = 0;
+ WaitDomain wdWaitDomain = LocalWait;
+ CObjectType * potObjectType = NULL;
+ unsigned int uErrCleanupIdxFirstNotInitializedCtrlr = 0;
+ unsigned int uErrCleanupIdxLastCtrlr = 0;
+ bool fLocalSynchLock = false;
+
+ union
+ {
+ CSynchWaitController * pWaitCtrlrs[MAXIMUM_WAIT_OBJECTS];
+ CSynchStateController * pStateCtrlrs[MAXIMUM_WAIT_OBJECTS];
+ } Ctrlrs;
+
+ if ((dwObjectCount <= 0) || (dwObjectCount > MAXIMUM_WAIT_OBJECTS))
+ {
+ palErr = ERROR_INVALID_PARAMETER;
+ goto GSCFO_exit;
+ }
+
+ if (CSynchControllerBase::WaitController == ctCtrlrType)
+ {
+ uCount = (unsigned int)m_cacheWaitCtrlrs.Get(pthrCurrent,
+ dwObjectCount,
+ Ctrlrs.pWaitCtrlrs);
+ }
+ else
+ {
+ uCount = (unsigned int)m_cacheStateCtrlrs.Get(pthrCurrent,
+ dwObjectCount,
+ Ctrlrs.pStateCtrlrs);
+ }
+
+ if (uCount < dwObjectCount)
+ {
+ // We got less controllers (uCount) than we asked for (dwObjectCount),
+ // probably because of low memory.
+ // None of these controllers is initialized, so they must be all
+ // returned directly to the cache
+ uErrCleanupIdxLastCtrlr = uCount;
+
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ goto GSCFO_error_cleanup;
+ }
+
+ //
+ // We need to acquire the local synch lock before evaluating object domains
+ //
+ AcquireLocalSynchLock(pthrCurrent);
+ fLocalSynchLock = true;
+
+ for (uIdx=0; uIdx<dwObjectCount; uIdx++)
+ {
+ if (SharedObject == rgObjects[uIdx]->GetObjectDomain())
+ {
+ ++uSharedObjectCount;
+ }
+
+ if (uSharedObjectCount > 0 && uSharedObjectCount <= uIdx)
+ {
+ wdWaitDomain = MixedWait;
+ break;
+ }
+ }
+
+ if (dwObjectCount == uSharedObjectCount)
+ {
+ wdWaitDomain = SharedWait;
+ }
+
+ for (uIdx=0;uIdx<dwObjectCount;uIdx++)
+ {
+ void * pvSData;
+ CSynchData * psdSynchData;
+ ObjectDomain odObjectDomain = rgObjects[uIdx]->GetObjectDomain();
+
+ palErr = rgObjects[uIdx]->GetObjectSynchData((void **)&pvSData);
+ if (NO_ERROR != palErr)
+ {
+ break;
+ }
+
+ psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(
+ CSynchData, reinterpret_cast<SharedID>(pvSData)) :
+ static_cast<CSynchData *>(pvSData);
+
+ VALIDATEOBJECT(psdSynchData);
+
+ potObjectType = rgObjects[uIdx]->GetObjectType();
+
+ if (CSynchControllerBase::WaitController == ctCtrlrType)
+ {
+ Ctrlrs.pWaitCtrlrs[uIdx]->Init(pthrCurrent,
+ ctCtrlrType,
+ odObjectDomain,
+ potObjectType,
+ psdSynchData,
+ wdWaitDomain);
+ }
+ else
+ {
+ Ctrlrs.pStateCtrlrs[uIdx]->Init(pthrCurrent,
+ ctCtrlrType,
+ odObjectDomain,
+ potObjectType,
+ psdSynchData,
+ wdWaitDomain);
+ }
+
+ if (CSynchControllerBase::WaitController == ctCtrlrType &&
+ otiProcess == potObjectType->GetId())
+ {
+ CProcProcessLocalData * pProcLocData;
+ IDataLock * pDataLock;
+
+ palErr = rgObjects[uIdx]->GetProcessLocalData(
+ pthrCurrent,
+ ReadLock,
+ &pDataLock,
+ (void **)&pProcLocData);
+
+ if (NO_ERROR != palErr)
+ {
+ // In case of failure here, bail out of the loop, but
+ // keep track (by incrementing the counter 'uIdx') of the
+ // fact that this controller has already being initialized
+ // and therefore need to be Release'd rather than just
+ // returned to the cache
+ uIdx++;
+ break;
+ }
+
+ Ctrlrs.pWaitCtrlrs[uIdx]->SetProcessData(rgObjects[uIdx], pProcLocData);
+ pDataLock->ReleaseLock(pthrCurrent, false);
+ }
+ }
+ if (NO_ERROR != palErr)
+ {
+ // An error occurred while initializing the (uIdx+1)-th controller,
+ // i.e. the one at index uIdx; therefore the first uIdx controllers
+ // must be Release'd, while the remaining uCount-uIdx must be returned
+ // directly to the cache.
+ uErrCleanupIdxFirstNotInitializedCtrlr = uIdx;
+ uErrCleanupIdxLastCtrlr = dwObjectCount;
+
+ goto GSCFO_error_cleanup;
+ }
+
+ // Succeeded
+ if (CSynchControllerBase::WaitController == ctCtrlrType)
+ {
+ for (uIdx=0;uIdx<dwObjectCount;uIdx++)
+ {
+ // The multiple cast is NEEDED, though currently it does not
+ // change the value ot the pointer. Anyway, if in the future
+ // a virtual method should be added to the base class
+ // CSynchControllerBase, both derived classes would have two
+ // virtual tables, therefore a static cast from, for instance,
+ // a CSynchWaitController* to a ISynchWaitController* would
+ // return the given pointer incremented by the size of a
+ // generic pointer on the specific platform
+ ppvControllers[uIdx] = reinterpret_cast<void *>(
+ static_cast<ISynchWaitController *>(Ctrlrs.pWaitCtrlrs[uIdx]));
+ }
+ }
+ else
+ {
+ for (uIdx=0;uIdx<dwObjectCount;uIdx++)
+ {
+ // See comment above
+ ppvControllers[uIdx] = reinterpret_cast<void *>(
+ static_cast<ISynchStateController *>(Ctrlrs.pStateCtrlrs[uIdx]));
+ }
+ }
+
+ // Succeeded: skip error cleanup
+ goto GSCFO_exit;
+
+ GSCFO_error_cleanup:
+ if (CSynchControllerBase::WaitController == ctCtrlrType)
+ {
+ // Release already initialized wait controllers
+ for (uIdx=0; uIdx<uErrCleanupIdxFirstNotInitializedCtrlr; uIdx++)
+ {
+ Ctrlrs.pWaitCtrlrs[uIdx]->Release();
+ }
+
+ // Return to the cache not yet initialized wait controllers
+ for (uIdx=uErrCleanupIdxFirstNotInitializedCtrlr; uIdx<uErrCleanupIdxLastCtrlr; uIdx++)
+ {
+ m_cacheWaitCtrlrs.Add(pthrCurrent, Ctrlrs.pWaitCtrlrs[uIdx]);
+ }
+ }
+ else
+ {
+ // Release already initialized state controllers
+ for (uIdx=0; uIdx<uErrCleanupIdxFirstNotInitializedCtrlr; uIdx++)
+ {
+ Ctrlrs.pStateCtrlrs[uIdx]->Release();
+ }
+
+ // Return to the cache not yet initialized state controllers
+ for (uIdx=uErrCleanupIdxFirstNotInitializedCtrlr; uIdx<uErrCleanupIdxLastCtrlr; uIdx++)
+ {
+ m_cacheStateCtrlrs.Add(pthrCurrent, Ctrlrs.pStateCtrlrs[uIdx]);
+ }
+ }
+
+ GSCFO_exit:
+ if (fLocalSynchLock)
+ {
+ ReleaseLocalSynchLock(pthrCurrent);
+ }
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::AllocateObjectSynchData
+
+ Returns a new SynchData for an object of given type and domain
+ --*/
+ PAL_ERROR CPalSynchronizationManager::AllocateObjectSynchData(
+ CObjectType *potObjectType,
+ ObjectDomain odObjectDomain,
+ VOID **ppvSynchData)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ CSynchData * psdSynchData = NULL;
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+
+ if (SharedObject == odObjectDomain)
+ {
+ SharedID shridSynchData = m_cacheSHRSynchData.Get(pthrCurrent);
+ if (NULL == shridSynchData)
+ {
+ ERROR("Unable to allocate shared memory\n");
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ psdSynchData = SharedIDToTypePointer(CSynchData, shridSynchData);
+
+ VALIDATEOBJECT(psdSynchData);
+
+ _ASSERT_MSG(NULL != psdSynchData, "Bad shared memory pointer\n");
+
+ // Initialize waiting list pointers
+ psdSynchData->SetWTLHeadShrPtr(NULL);
+ psdSynchData->SetWTLTailShrPtr(NULL);
+
+ // Store shared pointer to this object
+ psdSynchData->SetSharedThis(shridSynchData);
+
+ *ppvSynchData = reinterpret_cast<void *>(shridSynchData);
+ }
+ else
+ {
+ psdSynchData = m_cacheSynchData.Get(pthrCurrent);
+ if (NULL == psdSynchData)
+ {
+ ERROR("Unable to allocate memory\n");
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ // Initialize waiting list pointers
+ psdSynchData->SetWTLHeadPtr(NULL);
+ psdSynchData->SetWTLTailPtr(NULL);
+
+ // Set shared this pointer to NULL
+ psdSynchData->SetSharedThis(NULL);
+
+ *ppvSynchData = static_cast<void *>(psdSynchData);
+ }
+
+ // Initialize object domain and object type;
+ psdSynchData->SetObjectDomain(odObjectDomain);
+ psdSynchData->SetObjectType(potObjectType);
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::FreeObjectSynchData
+
+ Called to return a no longer used SynchData to the Synchronization Manager.
+ The SynchData may actually survive this call, since it is a ref-counted
+ object and at FreeObjectSynchData time it may still be used from within
+ the Synchronization Manager itself (e.g. the worker thread).
+ --*/
+ void CPalSynchronizationManager::FreeObjectSynchData(
+ CObjectType *potObjectType,
+ ObjectDomain odObjectDomain,
+ VOID *pvSynchData)
+ {
+ CSynchData * psdSynchData;
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+
+ if (odObjectDomain == SharedObject)
+ {
+ psdSynchData = SharedIDToTypePointer(CSynchData,
+ reinterpret_cast<SharedID>(pvSynchData));
+
+ if (NULL == psdSynchData)
+ {
+ ASSERT("Bad shared memory pointer\n");
+ return;
+ }
+ }
+ else
+ {
+ psdSynchData = static_cast<CSynchData *>(pvSynchData);
+ }
+
+ psdSynchData->Release(pthrCurrent);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::CreateSynchStateController
+
+ Creates a state controller for the given object
+ --*/
+ PAL_ERROR CPalSynchronizationManager::CreateSynchStateController(
+ CPalThread *pthrCurrent,
+ CObjectType *potObjectType,
+ VOID *pvSynchData,
+ ObjectDomain odObjectDomain,
+ ISynchStateController **ppStateController)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ CSynchStateController * pCtrlr = NULL;
+ WaitDomain wdWaitDomain = (SharedObject == odObjectDomain) ? SharedWait : LocalWait;
+ CSynchData * psdSynchData;
+
+ psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(CSynchData, reinterpret_cast<SharedID>(pvSynchData))
+ : static_cast<CSynchData *>(pvSynchData);
+
+ VALIDATEOBJECT(psdSynchData);
+
+ pCtrlr = m_cacheStateCtrlrs.Get(pthrCurrent);
+ if (NULL == pCtrlr)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ pCtrlr->Init(pthrCurrent,
+ CSynchControllerBase::StateController,
+ odObjectDomain,
+ potObjectType,
+ psdSynchData,
+ wdWaitDomain);
+
+ // Succeeded
+ *ppStateController = (ISynchStateController *)pCtrlr;
+
+ if (NO_ERROR != palErr)
+ {
+ m_cacheStateCtrlrs.Add(pthrCurrent, pCtrlr);
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::CreateSynchWaitController
+
+ Creates a wait controller for the given object
+ --*/
+ PAL_ERROR CPalSynchronizationManager::CreateSynchWaitController(
+ CPalThread *pthrCurrent,
+ CObjectType *potObjectType,
+ VOID *pvSynchData,
+ ObjectDomain odObjectDomain,
+ ISynchWaitController **ppWaitController)
+ {
+ CSynchWaitController * pCtrlr = NULL;
+ WaitDomain wdWaitDomain = (SharedObject == odObjectDomain) ? SharedWait : LocalWait;
+ CSynchData * psdSynchData;
+
+ psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(
+ CSynchData, reinterpret_cast<SharedID>(pvSynchData)) :
+ static_cast<CSynchData *>(pvSynchData);
+
+ VALIDATEOBJECT(psdSynchData);
+
+ pCtrlr = m_cacheWaitCtrlrs.Get(pthrCurrent);
+ if (NULL == pCtrlr)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ pCtrlr->Init(pthrCurrent,
+ CSynchControllerBase::WaitController,
+ odObjectDomain,
+ potObjectType,
+ psdSynchData,
+ wdWaitDomain);
+
+ // Succeeded
+ *ppWaitController = (ISynchWaitController *)pCtrlr;
+
+ return NO_ERROR;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::CreatePalSynchronizationManager
+
+ Creates the Synchronization Manager.
+ Private method, it is called only by CPalSynchMgrController.
+ --*/
+ IPalSynchronizationManager * CPalSynchronizationManager::CreatePalSynchronizationManager()
+ {
+ if (s_pObjSynchMgr != NULL)
+ {
+ ASSERT("Multiple PAL Synchronization manager initializations\n");
+ return NULL;
+ }
+
+ Initialize();
+ return static_cast<IPalSynchronizationManager *>(s_pObjSynchMgr);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::Initialize
+
+ Internal Synchronization Manager initialization
+ --*/
+ PAL_ERROR CPalSynchronizationManager::Initialize()
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ LONG lInit;
+ CPalSynchronizationManager * pSynchManager = NULL;
+
+ lInit = InterlockedCompareExchange(&s_lInitStatus,
+ (LONG)SynchMgrStatusInitializing,
+ (LONG)SynchMgrStatusIdle);
+
+ if ((LONG)SynchMgrStatusIdle != lInit)
+ {
+ ASSERT("Synchronization Manager already being initialized");
+ palErr = ERROR_INTERNAL_ERROR;
+ goto I_exit;
+ }
+
+ InternalInitializeCriticalSection(&s_csSynchProcessLock);
+ InternalInitializeCriticalSection(&s_csMonitoredProcessesLock);
+
+ pSynchManager = InternalNew<CPalSynchronizationManager>();
+ if (NULL == pSynchManager)
+ {
+ ERROR("Failed to allocate memory for Synchronization Manager");
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ goto I_exit;
+ }
+
+ if (!pSynchManager->CreateProcessPipe())
+ {
+ ERROR("Unable to create process pipe \n");
+ palErr = ERROR_OPEN_FAILED;
+ goto I_exit;
+ }
+
+ s_pObjSynchMgr = pSynchManager;
+
+ // Initialization was successful
+ g_pSynchronizationManager =
+ static_cast<IPalSynchronizationManager *>(pSynchManager);
+ s_lInitStatus = (LONG)SynchMgrStatusRunning;
+
+ I_exit:
+ if (NO_ERROR != palErr)
+ {
+ s_lInitStatus = (LONG)SynchMgrStatusError;
+ if (NULL != pSynchManager)
+ {
+ pSynchManager->ShutdownProcessPipe();
+ }
+
+ s_pObjSynchMgr = NULL;
+ g_pSynchronizationManager = NULL;
+ InternalDelete(pSynchManager);
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::StartWorker
+
+ Starts the Synchronization Manager's Worker Thread.
+ Private method, it is called only by CPalSynchMgrController.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::StartWorker(
+ CPalThread * pthrCurrent)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ CPalSynchronizationManager * pSynchManager = GetInstance();
+
+ if ((NULL == pSynchManager) || ((LONG)SynchMgrStatusRunning != s_lInitStatus))
+ {
+ ERROR("Trying to to create worker thread in invalid state\n");
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ HANDLE hWorkerThread = NULL;
+ SIZE_T osThreadId = 0;
+ palErr = InternalCreateThread(pthrCurrent,
+ NULL,
+ 0,
+ &WorkerThread,
+ (PVOID)pSynchManager,
+ 0,
+ PalWorkerThread,
+ &osThreadId,
+ &hWorkerThread);
+
+ if (NO_ERROR == palErr)
+ {
+ pSynchManager->m_dwWorkerThreadTid = (DWORD)osThreadId;
+ palErr = InternalGetThreadDataFromHandle(pthrCurrent,
+ hWorkerThread,
+ &pSynchManager->m_pthrWorker,
+ &pSynchManager->m_pipoThread);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Unable to get worker thread data\n");
+ }
+ }
+ else
+ {
+ ERROR("Unable to create worker thread\n");
+ }
+
+ if (NULL != hWorkerThread)
+ {
+ CloseHandle(hWorkerThread);
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::PrepareForShutdown
+
+ This method performs the part of Synchronization Manager's shutdown that
+ needs to be carried out when core PAL subsystems are still active.
+ Private method, it is called only by CPalSynchMgrController.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::PrepareForShutdown()
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ CPalSynchronizationManager * pSynchManager = GetInstance();
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+ int iRet;
+ ThreadNativeWaitData * ptnwdWorkerThreadNativeData;
+ struct timespec tsAbsTmo = { 0, 0 };
+
+ LONG lInit = InterlockedCompareExchange(&s_lInitStatus,
+ (LONG)SynchMgrStatusShuttingDown, (LONG)SynchMgrStatusRunning);
+
+ if ((LONG)SynchMgrStatusRunning != lInit)
+ {
+ ASSERT("Unexpected initialization status found "
+ "in PrepareForShutdown [expected=%d current=%d]\n",
+ SynchMgrStatusRunning, lInit);
+ // We intentionally not set s_lInitStatus to SynchMgrStatusError
+ // cause this could interfere with a previous thread already
+ // executing shutdown
+ palErr = ERROR_INTERNAL_ERROR;
+ goto PFS_exit;
+ }
+
+ // Discard process monitoring for process waits
+ pSynchManager->DiscardMonitoredProcesses(pthrCurrent);
+
+ if (NULL == pSynchManager->m_pipoThread)
+ {
+ // If m_pipoThread is NULL here, that means that StartWorker has
+ // never been called. That may happen if PAL_Initialize fails
+ // sometime after having called CreatePalSynchronizationManager,
+ // but before calling StartWorker. Nothing else to do here.
+ goto PFS_exit;
+ }
+
+ palErr = pSynchManager->WakeUpLocalWorkerThread(SynchWorkerCmdShutdown);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Failed stopping worker thread [palErr=%u]\n", palErr);
+ s_lInitStatus = SynchMgrStatusError;
+ goto PFS_exit;
+ }
+
+ ptnwdWorkerThreadNativeData =
+ &pSynchManager->m_pthrWorker->synchronizationInfo.m_tnwdNativeData;
+
+ palErr = GetAbsoluteTimeout(WorkerThreadTerminationTimeout, &tsAbsTmo, /*fPreferMonotonicClock*/ TRUE);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Failed to convert timeout to absolute timeout\n");
+ s_lInitStatus = SynchMgrStatusError;
+ goto PFS_exit;
+ }
+
+ // Using the worker thread's predicate/condition/mutex
+ // to wait for worker thread to be done
+ iRet = pthread_mutex_lock(&ptnwdWorkerThreadNativeData->mutex);
+ if (0 != iRet)
+ {
+ // pthread calls might fail if the shutdown is called
+ // from a signal handler. In this case just don't wait
+ // for the worker thread
+ ERROR("Cannot lock mutex [err=%d]\n", iRet);
+ palErr = ERROR_INTERNAL_ERROR;
+ s_lInitStatus = SynchMgrStatusError;
+ goto PFS_exit;
+ }
+
+ while (FALSE == ptnwdWorkerThreadNativeData->iPred)
+ {
+ iRet = pthread_cond_timedwait(&ptnwdWorkerThreadNativeData->cond,
+ &ptnwdWorkerThreadNativeData->mutex,
+ &tsAbsTmo);
+ if (0 != iRet)
+ {
+ if (ETIMEDOUT == iRet)
+ {
+ WARN("Timed out waiting for worker thread to exit "
+ "(tmo=%u ms)\n", WorkerThreadTerminationTimeout);
+ }
+ else
+ {
+ ERROR("pthread_cond_timedwait returned %d [errno=%d (%s)]\n",
+ iRet, errno, strerror(errno));
+ }
+ break;
+ }
+ }
+ if (0 == iRet)
+ {
+ ptnwdWorkerThreadNativeData->iPred = FALSE;
+ }
+ iRet = pthread_mutex_unlock(&ptnwdWorkerThreadNativeData->mutex);
+ if (0 != iRet)
+ {
+ ERROR("Cannot unlock mutex [err=%d]\n", iRet);
+ palErr = ERROR_INTERNAL_ERROR;
+ s_lInitStatus = SynchMgrStatusError;
+ goto PFS_exit;
+ }
+
+ PFS_exit:
+ if (NO_ERROR == palErr)
+ {
+ if (NULL != pSynchManager->m_pipoThread)
+ {
+ pSynchManager->m_pipoThread->ReleaseReference(pthrCurrent);
+
+ // After this release both m_pipoThread and m_pthrWorker
+ // are no longer valid
+ pSynchManager->m_pipoThread = NULL;
+ pSynchManager->m_pthrWorker = NULL;
+ }
+
+ // Ready for process shutdown
+ s_lInitStatus = SynchMgrStatusReadyForProcessShutDown;
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::WorkerThread
+
+ Synchronization Manager's Worker Thread
+ --*/
+ DWORD PALAPI CPalSynchronizationManager::WorkerThread(LPVOID pArg)
+ {
+ PAL_ERROR palErr;
+ bool fShuttingDown = false;
+ bool fWorkerIsDone = false;
+ int iPollTimeout = INFTIM;
+ SynchWorkerCmd swcCmd;
+ ThreadWakeupReason twrWakeUpReason;
+ SharedID shridMarshaledData;
+ DWORD dwData;
+ CPalSynchronizationManager * pSynchManager =
+ reinterpret_cast<CPalSynchronizationManager*>(pArg);
+ CPalThread * pthrWorker = InternalGetCurrentThread();
+
+ while (!fWorkerIsDone)
+ {
+ LONG lProcessCount;
+
+ palErr = pSynchManager->ReadCmdFromProcessPipe(iPollTimeout,
+ &swcCmd,
+ &shridMarshaledData,
+ &dwData);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Received error %x from ReadCmdFromProcessPipe()\n",
+ palErr);
+ continue;
+ }
+ switch (swcCmd)
+ {
+ case SynchWorkerCmdNop:
+ TRACE("Synch Worker: received SynchWorkerCmdNop\n");
+ if (fShuttingDown)
+ {
+ TRACE("Synch Worker: received a timeout when "
+ "fShuttingDown==true: worker is done, bailing "
+ "out from the loop\n");
+
+ // Whether WorkerThreadShuttingDownTimeout has elapsed
+ // or the last process with a descriptor opened for
+ // write on our process pipe, has just closed it,
+ // causing an EOF on the read fd (that can happen only
+ // at shutdown time since during normal run time we
+ // hold a fd opened for write within this process).
+ // In both the case it is time to go for the worker
+ // thread.
+ fWorkerIsDone = true;
+ }
+ else
+ {
+ lProcessCount = pSynchManager->DoMonitorProcesses(pthrWorker);
+ if (lProcessCount > 0)
+ {
+ iPollTimeout = WorkerThreadProcMonitoringTimeout;
+ }
+ else
+ {
+ iPollTimeout = INFTIM;
+ }
+ }
+ break;
+ case SynchWorkerCmdRemoteSignal:
+ {
+ // Note: this cannot be a wait all
+ WaitingThreadsListNode * pWLNode;
+ ThreadWaitInfo * ptwiWaitInfo;
+ DWORD dwObjIndex;
+ bool fSharedSynchLock = false;
+
+ // Lock
+ AcquireLocalSynchLock(pthrWorker);
+ AcquireSharedSynchLock(pthrWorker);
+ fSharedSynchLock = true;
+
+ pWLNode = SharedIDToTypePointer(WaitingThreadsListNode,
+ shridMarshaledData);
+
+ _ASSERT_MSG(NULL != pWLNode, "Received bad Shared ID %p\n",
+ shridMarshaledData);
+ _ASSERT_MSG(gPID == pWLNode->dwProcessId,
+ "Remote signal apparently sent to the wrong "
+ "process [target pid=%u current pid=%u]\n",
+ pWLNode->dwProcessId, gPID);
+ _ASSERT_MSG(0 == (WTLN_FLAG_WAIT_ALL & pWLNode->dwFlags),
+ "Wait all with remote awakening delegated "
+ "through SynchWorkerCmdRemoteSignal rather than "
+ "SynchWorkerCmdDelegatedObjectSignaling\n");
+
+
+ // Get the object index
+ dwObjIndex = pWLNode->dwObjIndex;
+
+ // Get the WaitInfo
+ ptwiWaitInfo = pWLNode->ptwiWaitInfo;
+
+ // Initialize the WakeUpReason to WaitSucceeded
+ twrWakeUpReason = WaitSucceeded;
+
+ CSynchData * psdSynchData =
+ SharedIDToTypePointer(CSynchData,
+ pWLNode->ptrOwnerObjSynchData.shrid);
+
+ TRACE("Synch Worker: received REMOTE SIGNAL cmd "
+ "[WInfo=%p {Type=%u Domain=%u ObjCount=%d TgtThread=%x} "
+ "SynchData={shriId=%p p=%p} {SigCount=%d IsAbandoned=%d}\n",
+ ptwiWaitInfo, ptwiWaitInfo->wtWaitType, ptwiWaitInfo->wdWaitDomain,
+ ptwiWaitInfo->lObjCount, ptwiWaitInfo->pthrOwner->GetThreadId(),
+ (VOID *)pWLNode->ptrOwnerObjSynchData.shrid, psdSynchData,
+ psdSynchData->GetSignalCount(), psdSynchData->IsAbandoned());
+
+ if (CObjectType::OwnershipTracked ==
+ psdSynchData->GetObjectType()->GetOwnershipSemantics())
+ {
+ // Abandoned status is not propagated through process
+ // pipe: need to get it from the object itself before
+ // resetting the data by acquiring the object ownership
+ if (psdSynchData->IsAbandoned())
+ {
+ twrWakeUpReason = MutexAbondoned;
+ }
+
+ // Acquire ownership
+ palErr = psdSynchData->AssignOwnershipToThread(
+ pthrWorker,
+ ptwiWaitInfo->pthrOwner);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Synch Worker: AssignOwnershipToThread "
+ "failed with error %u; ownership data on "
+ "object with SynchData %p may be "
+ "corrupted\n", palErr, psdSynchData);
+ }
+ }
+
+ // Unregister the wait
+ pSynchManager->UnRegisterWait(pthrWorker,
+ ptwiWaitInfo,
+ fSharedSynchLock);
+
+ // pWLNode is no longer valid after UnRegisterWait
+ pWLNode = NULL;
+
+ TRACE("Synch Worker: Waking up local thread %x "
+ "{WakeUpReason=%u ObjIndex=%u}\n",
+ ptwiWaitInfo->pthrOwner->GetThreadId(),
+ twrWakeUpReason, dwObjIndex);
+
+ // Wake up the target thread
+ palErr = WakeUpLocalThread(
+ pthrWorker,
+ ptwiWaitInfo->pthrOwner,
+ twrWakeUpReason,
+ dwObjIndex);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Synch Worker: Failed to wake up local thread "
+ "%#x while propagating remote signaling: "
+ "object signaling may be lost\n",
+ ptwiWaitInfo->pthrOwner->GetThreadId());
+ }
+
+ // Unlock
+ ReleaseSharedSynchLock(pthrWorker);
+ fSharedSynchLock = false;
+ ReleaseLocalSynchLock(pthrWorker);
+
+ break;
+ }
+ case SynchWorkerCmdDelegatedObjectSignaling:
+ {
+ CSynchData * psdSynchData;
+
+ TRACE("Synch Worker: received "
+ "SynchWorkerCmdDelegatedObjectSignaling\n");
+
+ psdSynchData = SharedIDToTypePointer(CSynchData,
+ shridMarshaledData);
+
+ _ASSERT_MSG(NULL != psdSynchData, "Received bad Shared ID %p\n",
+ shridMarshaledData);
+ _ASSERT_MSG(0 < dwData && (DWORD)INT_MAX > dwData,
+ "Received remote signaling with invalid signal "
+ "count\n");
+
+ // Lock
+ AcquireLocalSynchLock(pthrWorker);
+ AcquireSharedSynchLock(pthrWorker);
+
+ TRACE("Synch Worker: received DELEGATED OBJECT SIGNALING "
+ "cmd [SynchData={shriId=%p p=%p} SigCount=%u] [Current obj SigCount=%d "
+ "IsAbandoned=%d]\n", (VOID *)shridMarshaledData,
+ psdSynchData, dwData, psdSynchData->GetSignalCount(),
+ psdSynchData->IsAbandoned());
+
+ psdSynchData->Signal(pthrWorker,
+ psdSynchData->GetSignalCount() + dwData,
+ true);
+
+ // Current SynchData has been AddRef'd by remote process in
+ // order to be marshaled to the current one, therefore at
+ // this point we need to release it
+ psdSynchData->Release(pthrWorker);
+
+ // Unlock
+ ReleaseSharedSynchLock(pthrWorker);
+ ReleaseLocalSynchLock(pthrWorker);
+
+ break;
+ }
+ case SynchWorkerCmdShutdown:
+ TRACE("Synch Worker: received SynchWorkerCmdShutdown\n");
+
+ // Shutdown the process pipe: this will cause the process
+ // pipe to be unlinked and its write-only file descriptor
+ // to be closed, so that when the last fd opened for write
+ // on the fifo (from another process) will be closed, we
+ // will receive an EOF on the read end (i.e. poll in
+ // ReadBytesFromProcessPipe will return 1 with no data to
+ // be read). That will allow the worker thread to process
+ // possible commands already successfully written to the
+ // pipe by some other process, before shutting down.
+ pSynchManager->ShutdownProcessPipe();
+
+ // Shutting down: this will cause the worker thread to
+ // fetch residual cmds from the process pipe until an
+ // EOF is converted to a SynchWorkerCmdNop or the
+ // WorkerThreadShuttingDownTimeout has elapsed without
+ // receiving any cmd.
+ fShuttingDown = true;
+
+ // Set the timeout to WorkerThreadShuttingDownTimeout
+ iPollTimeout = WorkerThreadShuttingDownTimeout;
+ break;
+ default:
+ ASSERT("Synch Worker: Unknown worker cmd [swcWorkerCmd=%d]\n",
+ swcCmd);
+ break;
+ }
+ }
+
+ int iRet;
+ ThreadNativeWaitData * ptnwdWorkerThreadNativeData =
+ &pthrWorker->synchronizationInfo.m_tnwdNativeData;
+
+ // Using the worker thread's predicate/condition/mutex
+ // (that normally are never used) to signal the shutting
+ // down thread that the worker thread is done
+ iRet = pthread_mutex_lock(&ptnwdWorkerThreadNativeData->mutex);
+ _ASSERT_MSG(0 == iRet, "Cannot lock mutex [err=%d]\n", iRet);
+
+ ptnwdWorkerThreadNativeData->iPred = TRUE;
+
+ iRet = pthread_cond_signal(&ptnwdWorkerThreadNativeData->cond);
+ if (0 != iRet)
+ {
+ ERROR ("pthread_cond_signal returned %d [errno=%d (%s)]\n",
+ iRet, errno, strerror(errno));
+ }
+
+ iRet = pthread_mutex_unlock(&ptnwdWorkerThreadNativeData->mutex);
+ _ASSERT_MSG(0 == iRet, "Cannot lock mutex [err=%d]\n", iRet);
+
+ // Sleep forever
+ ThreadPrepareForShutdown();
+
+ return 0;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ReadCmdFromProcessPipe
+
+ Reads a worker thread cmd from the process pipe. If there is no data
+ to be read on the pipe, it blocks until there is data available or the
+ timeout expires.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::ReadCmdFromProcessPipe(
+ int iPollTimeout,
+ SynchWorkerCmd * pswcWorkerCmd,
+ SharedID * pshridMarshaledData,
+ DWORD * pdwData)
+ {
+ int iRet;
+ BYTE byVal;
+ SynchWorkerCmd swcWorkerCmd = SynchWorkerCmdNop;
+
+ _ASSERTE(NULL != pswcWorkerCmd);
+ _ASSERTE(NULL != pshridMarshaledData);
+ _ASSERTE(NULL != pdwData);
+
+ iRet = ReadBytesFromProcessPipe(iPollTimeout, &byVal, sizeof(BYTE));
+
+ if (0 > iRet)
+ {
+ ERROR("Failed polling the process pipe [ret=%d errno=%d (%s)]\n",
+ iRet, errno, strerror(errno));
+
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ if (iRet != 0)
+ {
+ _ASSERT_MSG(sizeof(BYTE) == iRet,
+ "Got %d bytes from process pipe while expecting for %d\n",
+ iRet, sizeof(BYTE));
+
+ swcWorkerCmd = (SynchWorkerCmd)byVal;
+
+ if (SynchWorkerCmdLast <= swcWorkerCmd)
+ {
+ ERROR("Got unknown worker command code %d from the process "
+ "pipe!\n", swcWorkerCmd);
+
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ _ASSERT_MSG(SynchWorkerCmdNop == swcWorkerCmd ||
+ SynchWorkerCmdRemoteSignal == swcWorkerCmd ||
+ SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd ||
+ SynchWorkerCmdShutdown == swcWorkerCmd,
+ "Unknown worker command code %u\n", swcWorkerCmd);
+
+ TRACE("Got cmd %u from process pipe\n", swcWorkerCmd);
+ }
+
+ if (SynchWorkerCmdRemoteSignal == swcWorkerCmd ||
+ SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd)
+ {
+ SharedID shridMarshaledId = NULL;
+
+ TRACE("Received %s cmd\n",
+ (swcWorkerCmd == SynchWorkerCmdRemoteSignal) ?
+ "REMOTE SIGNAL" : "DELEGATED OBJECT SIGNALING" );
+
+ iRet = ReadBytesFromProcessPipe(WorkerCmdCompletionTimeout,
+ (BYTE *)&shridMarshaledId,
+ sizeof(shridMarshaledId));
+ if (sizeof(shridMarshaledId) != iRet)
+ {
+ ERROR("Unable to read marshaled Shared ID from the "
+ "process pipe [pipe=%d ret=%d errno=%d (%s)]\n",
+ m_iProcessPipeRead, iRet, errno, strerror(errno));
+
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ TRACE("Received marshaled shrid=%p\n", (VOID *)shridMarshaledId);
+
+ *pshridMarshaledData = shridMarshaledId;
+ }
+
+ if (SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd)
+ {
+ DWORD dwData;
+
+ iRet = ReadBytesFromProcessPipe(WorkerCmdCompletionTimeout,
+ (BYTE *)&dwData,
+ sizeof(dwData));
+ if (sizeof(dwData) != iRet)
+ {
+ ERROR("Unable to read signal count from the "
+ "process pipe [pipe=%d ret=%d errno=%d (%s)]\n",
+ m_iProcessPipeRead, iRet, errno, strerror(errno));
+
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ TRACE("Received signal count %u\n", dwData);
+
+ *pdwData = dwData;
+ }
+
+ *pswcWorkerCmd = swcWorkerCmd;
+ return NO_ERROR;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ReadBytesFromProcessPipe
+
+ Reads the specified number of bytes from the process pipe. If there is
+ no data to be read on the pipe, it blocks until there is data available
+ or the timeout expires.
+ --*/
+ int CPalSynchronizationManager::ReadBytesFromProcessPipe(
+ int iTimeout,
+ BYTE * pRecvBuf,
+ LONG iBytes)
+ {
+#if !HAVE_KQUEUE
+ struct pollfd Poll;
+#endif // !HAVE_KQUEUE
+ int iRet = -1;
+ int iConsecutiveEintrs = 0;
+ LONG iBytesRead = 0;
+ BYTE * pPos = pRecvBuf;
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ struct kevent keChanges;
+ struct timespec ts, *pts;
+ int iNChanges;
+#endif // HAVE_KQUEUE
+
+ _ASSERTE(0 <= iBytes);
+
+ do
+ {
+ while (TRUE)
+ {
+ int iErrno = 0;
+#if HAVE_KQUEUE
+#if HAVE_BROKEN_FIFO_KEVENT
+#if HAVE_BROKEN_FIFO_SELECT
+#error Found no way to wait on a FIFO.
+#endif
+
+ timeval *ptv;
+ timeval tv;
+
+ if (INFTIM == iTimeout)
+ {
+ ptv = NULL;
+ }
+ else
+ {
+ tv.tv_usec = (iTimeout % tccSecondsToMillieSeconds) *
+ tccMillieSecondsToMicroSeconds;
+ tv.tv_sec = iTimeout / tccSecondsToMillieSeconds;
+ ptv = &tv;
+ }
+
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(m_iProcessPipeRead, &readfds);
+ iRet = select(m_iProcessPipeRead + 1, &readfds, NULL, NULL, ptv);
+
+#else // HAVE_BROKEN_FIFO_KEVENT
+
+ // Note: FreeBSD needs to use kqueue/kevent support here, since on this
+ // platform the EOF notification on FIFOs is not surfaced through poll,
+ // and process pipe shutdown relies on this feature.
+ // If a thread is polling a FIFO or a pipe for POLLIN, when the last
+ // write descriptor for that pipe is closed, poll() is supposed to
+ // return with a POLLIN event but no data to be read on the FIFO/pipe,
+ // which means EOF.
+ // On FreeBSD such feature works for pipes but it doesn't for FIFOs.
+ // Using kevent the EOF is instead surfaced correctly.
+
+ if (iBytes > m_keProcessPipeEvent.data)
+ {
+ if (INFTIM == iTimeout)
+ {
+ pts = NULL;
+ }
+ else
+ {
+ ts.tv_nsec = (iTimeout % tccSecondsToMillieSeconds) *
+ tccMillieSecondsToNanoSeconds;
+ ts.tv_sec = iTimeout / tccSecondsToMillieSeconds;
+ pts = &ts;
+ }
+
+ if (0 != (EV_EOF & m_keProcessPipeEvent.flags))
+ {
+ TRACE("Refreshing kevent settings\n");
+ EV_SET(&keChanges, m_iProcessPipeRead, EVFILT_READ,
+ EV_ADD | EV_CLEAR, 0, 0, 0);
+ iNChanges = 1;
+ }
+ else
+ {
+ iNChanges = 0;
+ }
+
+ iRet = kevent(m_iKQueue, &keChanges, iNChanges,
+ &m_keProcessPipeEvent, 1, pts);
+
+ if (0 < iRet)
+ {
+ _ASSERTE(1 == iRet);
+ _ASSERTE(EVFILT_READ == m_keProcessPipeEvent.filter);
+
+ if (EV_ERROR & m_keProcessPipeEvent.flags)
+ {
+ ERROR("EV_ERROR from kevent [ident=%d filter=%d flags=%x]\n", m_keProcessPipeEvent.ident, m_keProcessPipeEvent.filter, m_keProcessPipeEvent.flags);
+ iRet = -1;
+ iErrno = m_keProcessPipeEvent.data;
+ m_keProcessPipeEvent.data = 0;
+ }
+ }
+ else if (0 > iRet)
+ {
+ iErrno = errno;
+ }
+
+ TRACE("Woken up from kevent() with ret=%d flags=%#x data=%d "
+ "[iTimeout=%d]\n", iRet, m_keProcessPipeEvent.flags,
+ m_keProcessPipeEvent.data, iTimeout);
+ }
+ else
+ {
+ // There is enough data already available in the buffer, just use that.
+ iRet = 1;
+ }
+
+#endif // HAVE_BROKEN_FIFO_KEVENT
+#else // HAVE_KQUEUE
+
+ Poll.fd = m_iProcessPipeRead;
+ Poll.events = POLLIN;
+ Poll.revents = 0;
+
+ iRet = poll(&Poll, 1, iTimeout);
+
+ TRACE("Woken up from poll() with ret=%d [iTimeout=%d]\n",
+ iRet, iTimeout);
+
+ if (1 == iRet &&
+ ((POLLERR | POLLHUP | POLLNVAL) & Poll.revents))
+ {
+ // During PAL shutdown the pipe gets closed and Poll.revents is set to POLLHUP
+ // (note: no other flags are set). We will also receive an EOF on from the read call.
+ // Please see the comment for SynchWorkerCmdShutdown in CPalSynchronizationManager::WorkerThread.
+ if (!PALIsShuttingDown() || (Poll.revents != POLLHUP))
+ {
+ ERROR("Unexpected revents=%x while polling pipe %d\n",
+ Poll.revents, Poll.fd);
+ iErrno = EINVAL;
+ iRet = -1;
+ }
+ }
+ else if (0 > iRet)
+ {
+ iErrno = errno;
+ }
+
+#endif // HAVE_KQUEUE
+
+ if (0 == iRet || 1 == iRet)
+ {
+ // 0 == wait timed out
+ // 1 == FIFO has data available
+ break;
+ }
+ else
+ {
+ if (1 < iRet)
+ {
+ // Unexpected iRet > 1
+ ASSERT("Unexpected return code %d from blocking poll/kevent call\n",
+ iRet);
+ goto RBFPP_exit;
+ }
+
+ if (EINTR != iErrno)
+ {
+ // Unexpected error
+ ASSERT("Unexpected error from blocking poll/kevent call: %d (%s)\n",
+ iErrno, strerror(iErrno));
+ goto RBFPP_exit;
+ }
+
+ iConsecutiveEintrs++;
+ TRACE("poll() failed with EINTR; re-polling\n");
+
+ if (iConsecutiveEintrs >= MaxWorkerConsecutiveEintrs)
+ {
+ if (iTimeout != INFTIM)
+ {
+ WARN("Receiving too many EINTRs; converting one of them "
+ "to a timeout");
+ iRet = 0;
+ break;
+ }
+ else if (0 == (iConsecutiveEintrs % MaxWorkerConsecutiveEintrs))
+ {
+ WARN("Receiving too many EINTRs [%d so far]",
+ iConsecutiveEintrs);
+ }
+ }
+ }
+ }
+
+ if (0 == iRet)
+ {
+ // Time out
+ break;
+ }
+ else
+ {
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ if (0 != (EV_EOF & m_keProcessPipeEvent.flags) && 0 == m_keProcessPipeEvent.data)
+ {
+ // EOF
+ TRACE("Received an EOF on process pipe via kevent\n");
+ goto RBFPP_exit;
+ }
+#endif // HAVE_KQUEUE
+
+ iRet = read(m_iProcessPipeRead, pPos, iBytes - iBytesRead);
+
+ if (0 == iRet)
+ {
+ // Poll returned 1 and read returned zero: this is an EOF,
+ // i.e. no other process has the pipe still open for write
+ TRACE("Received an EOF on process pipe via poll\n");
+ goto RBFPP_exit;
+ }
+ else if (0 > iRet)
+ {
+ ERROR("Unable to read %d bytes from the the process pipe "
+ "[pipe=%d ret=%d errno=%d (%s)]\n", iBytes - iBytesRead,
+ m_iProcessPipeRead, iRet, errno, strerror(errno));
+ goto RBFPP_exit;
+ }
+
+ TRACE("Read %d bytes from process pipe\n", iRet);
+
+ iBytesRead += iRet;
+ pPos += iRet;
+
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ // Update available data count
+ m_keProcessPipeEvent.data -= iRet;
+ _ASSERTE(0 <= m_keProcessPipeEvent.data);
+#endif // HAVE_KQUEUE
+ }
+ } while(iBytesRead < iBytes);
+
+ RBFPP_exit:
+ return (iRet < 0) ? iRet : iBytesRead;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::WakeUpLocalThread
+
+ Wakes up a local thead currently sleeping for a wait or a sleep
+ --*/
+ PAL_ERROR CPalSynchronizationManager::WakeUpLocalThread(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget,
+ ThreadWakeupReason twrWakeupReason,
+ DWORD dwObjectIndex)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ ThreadNativeWaitData * ptnwdNativeWaitData =
+ pthrTarget->synchronizationInfo.GetNativeData();
+
+ TRACE("Waking up a local thread [WakeUpReason=%u ObjectIndex=%u "
+ "ptnwdNativeWaitData=%p]\n", twrWakeupReason, dwObjectIndex,
+ ptnwdNativeWaitData);
+
+ // Set wakeup reason and signaled object index
+ ptnwdNativeWaitData->twrWakeupReason = twrWakeupReason;
+ ptnwdNativeWaitData->dwObjectIndex = dwObjectIndex;
+
+#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ if (0 < GetLocalSynchLockCount(pthrCurrent))
+ {
+ // Defer the actual thread signaling to right after
+ // releasing the synch lock(s), so that signaling
+ // can happen from a thread-suspension safe area
+ palErr = DeferThreadConditionSignaling(pthrCurrent, pthrTarget);
+ }
+ else
+ {
+ // Signal the target thread's condition
+ palErr = SignalThreadCondition(ptnwdNativeWaitData);
+ }
+#else // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ // Signal the target thread's condition
+ palErr = SignalThreadCondition(ptnwdNativeWaitData);
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+
+ return palErr;
+ }
+
+#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ /*++
+ Method:
+ CPalSynchronizationManager::DeferThreadConditionSignaling
+
+ Defers thread signaling to the final release of synchronization
+ lock(s), so that condition signaling can happen when the signaling
+ thread is marked as safe for thread suspension.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::DeferThreadConditionSignaling(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ LONG lCount = pthrCurrent->synchronizationInfo.m_lPendingSignalingCount;
+
+ _ASSERTE(pthrTarget != pthrCurrent);
+
+ if (CThreadSynchronizationInfo::PendingSignalingsArraySize > lCount)
+ {
+ // If there is available room, add the target thread object to
+ // the array of pending thread signalings.
+ pthrCurrent->synchronizationInfo.m_rgpthrPendingSignalings[lCount] = pthrTarget;
+ }
+ else
+ {
+ // If the array is full, add the target thread object at the end
+ // of the overflow list
+ DeferredSignalingListNode * pdsln =
+ InternalNew<DeferredSignalingListNode>();
+
+ if (pdsln)
+ {
+ pdsln->pthrTarget = pthrTarget;
+
+ // Add the note to the end of the list.
+ // Note: no need to synchronize the access to this list since
+ // it is meant to be accessed only by the owner thread.
+ InsertTailList(&pthrCurrent->synchronizationInfo.m_lePendingSignalingsOverflowList,
+ &pdsln->Link);
+ }
+ else
+ {
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ if (NO_ERROR == palErr)
+ {
+ // Increment the count of pending signalings
+ pthrCurrent->synchronizationInfo.m_lPendingSignalingCount += 1;
+
+ // Add a reference to the target CPalThread object; this is
+ // needed since deferring signaling after releasing the synch
+ // locks implies accessing the target thread object without
+ // holding the local synch lock. In rare circumstances, the
+ // target thread may have already exited while deferred signaling
+ // takes place, therefore invalidating the thread object. The
+ // reference added here ensures that the thread object is still
+ // good, even if the target thread has exited.
+ pthrTarget->AddThreadReference();
+ }
+
+ return palErr;
+ }
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+
+ /*++
+ Method:
+ CPalSynchronizationManager::SignalThreadCondition
+
+ Performs the actual condition signaling in to wake up the target thread
+ --*/
+ PAL_ERROR CPalSynchronizationManager::SignalThreadCondition(
+ ThreadNativeWaitData * ptnwdNativeWaitData)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ int iRet;
+
+ // Lock the mutex
+ iRet = pthread_mutex_lock(&ptnwdNativeWaitData->mutex);
+ if (0 != iRet)
+ {
+ ERROR("Cannot lock mutex [err=%d]\n", iRet);
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ // Set the predicate
+ ptnwdNativeWaitData->iPred = TRUE;
+
+ // Signal the condition
+ iRet = pthread_cond_signal(&ptnwdNativeWaitData->cond);
+ if (0 != iRet)
+ {
+ ERROR("Failed to signal condition: pthread_cond_signal "
+ "returned %d [errno=%d (%s)]\n", iRet, errno,
+ strerror(errno));
+ palErr = ERROR_INTERNAL_ERROR;
+ // Continue in order to unlock the mutex anyway
+ }
+
+ // Unlock the mutex
+ iRet = pthread_mutex_unlock(&ptnwdNativeWaitData->mutex);
+ if (0 != iRet)
+ {
+ ERROR("Cannot unlock mutex [err=%d]\n", iRet);
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ReadBytesFromProcessPipe
+
+ Wakes up a remote thead currently sleeping for a wait or a sleep
+ by sending the appropriate cmd to the remote process' worker
+ thread, which will take care to convert this command into a
+ WakeUpLocalThread in the remote process
+ --*/
+ PAL_ERROR CPalSynchronizationManager::WakeUpRemoteThread(
+ SharedID shridWLNode)
+ {
+ const int MsgSize = sizeof(BYTE) + sizeof(SharedID);
+ PAL_ERROR palErr = NO_ERROR;
+ BYTE rgSendBuf[MsgSize];
+ BYTE * pbySrc, * pbyDst = rgSendBuf;
+ WaitingThreadsListNode * pWLNode = SharedIDToTypePointer(WaitingThreadsListNode, shridWLNode);
+
+
+ _ASSERT_MSG(NULL != pWLNode, "Bad shared wait list node identifier (%p)\n", (VOID*)shridWLNode);
+ _ASSERT_MSG(gPID != pWLNode->dwProcessId, "WakeUpRemoteThread called on local thread\n");
+ _ASSERT_MSG(NULL != shridWLNode, "NULL shared identifier\n");
+ _ASSERT_MSG(MsgSize <= PIPE_BUF, "Message too long [MsgSize=%d PIPE_BUF=%d]\n", MsgSize, (int)PIPE_BUF);
+
+ TRACE("Waking up remote thread {pid=%x, tid=%x} by sending cmd=%u and shridWLNode=%p over process pipe\n",
+ pWLNode->dwProcessId, pWLNode->dwThreadId, SynchWorkerCmdRemoteSignal, (VOID *)shridWLNode);
+
+ // Prepare the message
+ // Cmd
+ *pbyDst++ = (BYTE)(SynchWorkerCmdRemoteSignal & 0xFF);
+
+ // WaitingThreadsListNode (not aligned, copy byte by byte)
+ pbySrc = (BYTE *)&shridWLNode;
+ for (int i = 0; i < (int)sizeof(SharedID); i++)
+ {
+ *pbyDst++ = *pbySrc++;
+ }
+
+ _ASSERT_MSG(pbyDst <= rgSendBuf + MsgSize + 1, "Buffer overrun");
+
+ // Send the message
+ palErr = SendMsgToRemoteWorker(pWLNode->dwProcessId, rgSendBuf, MsgSize);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Failed sending message to remote worker in process %u\n", pWLNode->dwProcessId);
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::DelegateSignalingToRemoteProcess
+
+ This method transfers an object signaling operation to a remote process,
+ where it will be performed by the worker thread. Such delegation takes
+ place when the currently processed thread (among those waiting on the
+ signald object) lives in a different process as the signaling thread,
+ and it is performing a wait all. In this case generally is not possible
+ to find out whether or not the wait all is satisfied, therefore the
+ signaling operation must be continued in the target process.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::DelegateSignalingToRemoteProcess(
+ CPalThread * pthrCurrent,
+ DWORD dwTargetProcessId,
+ SharedID shridSynchData)
+ {
+ const int MsgSize = sizeof(BYTE) + sizeof(SharedID) + sizeof(DWORD);
+ int i;
+ PAL_ERROR palErr = NO_ERROR;
+ BYTE rgSendBuf[MsgSize];
+ BYTE * pbySrc, * pbyDst = rgSendBuf;
+ DWORD dwSigCount;
+ CSynchData * psdSynchData =
+ SharedIDToTypePointer(CSynchData, shridSynchData);
+
+ _ASSERT_MSG(gPID != dwTargetProcessId, " called on local thread\n");
+ _ASSERT_MSG(NULL != shridSynchData, "NULL shared identifier\n");
+ _ASSERT_MSG(NULL != psdSynchData, "Bad shared SynchData identifier (%p)\n", (VOID*)shridSynchData);
+ _ASSERT_MSG(MsgSize <= PIPE_BUF, "Message too long [MsgSize=%d PIPE_BUF=%d]\n", MsgSize, (int)PIPE_BUF);
+
+ TRACE("Transfering wait all signaling to remote process pid=%x by sending cmd=%u and shridSynchData=%p over process pipe\n",
+ dwTargetProcessId, SynchWorkerCmdDelegatedObjectSignaling, (VOID *)shridSynchData);
+
+ dwSigCount = psdSynchData->GetSignalCount();
+
+ // AddRef SynchData to be marshaled to remote process
+ psdSynchData->AddRef();
+
+ //
+ // Prepare the message
+ //
+
+ // Cmd
+ *pbyDst++ = (BYTE)(SynchWorkerCmdDelegatedObjectSignaling & 0xFF);
+
+ // CSynchData (not aligned, copy byte by byte)
+ pbySrc = (BYTE *)&shridSynchData;
+ for (i=0; i<(int)sizeof(SharedID); i++)
+ {
+ *pbyDst++ = *pbySrc++;
+ }
+
+ // Signal Count (not aligned, copy byte by byte)
+ pbySrc = (BYTE *)&dwSigCount;
+ for (i=0; i<(int)sizeof(DWORD); i++)
+ {
+ *pbyDst++ = *pbySrc++;
+ }
+
+ _ASSERT_MSG(pbyDst <= rgSendBuf + MsgSize + 1, "Buffer overrun");
+
+ // Send the message
+ palErr = SendMsgToRemoteWorker(dwTargetProcessId, rgSendBuf, MsgSize);
+ if (NO_ERROR != palErr)
+ {
+ TRACE("Failed sending message to remote worker in process %u\n", dwTargetProcessId);
+
+ // Undo refcounting
+ psdSynchData->Release(pthrCurrent);
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::SendMsgToRemoteWorker
+
+ Sends a message (command + data) to a remote process's worker thread.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::SendMsgToRemoteWorker(
+ DWORD dwProcessId,
+ BYTE * pMsg,
+ int iMsgSize)
+ {
+#ifndef CORECLR
+ PAL_ERROR palErr = NO_ERROR;
+ int iProcessPipe, iBytesToWrite, iRetryCount;
+ ssize_t sszRet;
+ char strPipeFilename[MAX_PATH];
+ BYTE * pPos = pMsg;
+ bool fRet;
+ CPalThread *pthrCurrent = InternalGetCurrentThread();
+
+ _ASSERT_MSG(gPID != dwProcessId, "SendMsgToRemoteWorker called with local process as target process\n");
+
+ fRet = GetProcessPipeName(strPipeFilename, MAX_PATH, dwProcessId);
+
+ _ASSERT_MSG(fRet, "Failed to retrieve process pipe's name!\n");
+
+ iProcessPipe = InternalOpen(strPipeFilename, O_WRONLY);
+ if (-1 == iProcessPipe)
+ {
+ ERROR("Unable to open a process pipe to wake up a remote thread "
+ "[pid=%u errno=%d (%s) PipeFilename=%s]\n", dwProcessId,
+ errno, strerror(errno), strPipeFilename);
+ palErr = ERROR_INTERNAL_ERROR;
+ goto SMTRW_exit;
+ }
+
+ pPos = pMsg;
+ iBytesToWrite = iMsgSize;
+ while (0 < iBytesToWrite)
+ {
+ iRetryCount = 0;
+ do
+ {
+ sszRet = write(iProcessPipe, pPos, iBytesToWrite);
+ } while (-1 == sszRet &&
+ EAGAIN == errno &&
+ ++iRetryCount < MaxConsecutiveEagains &&
+ 0 == sched_yield());
+
+ if (0 >= sszRet)
+ {
+ ERROR("Error writing message to process pipe %d [target_pid=%u "
+ "bytes_to_write=%d bytes_written=%d ret=%d errno=%d (%s) "
+ "PipeFilename=%s]\n", iProcessPipe, dwProcessId, iMsgSize,
+ iMsgSize - iBytesToWrite, (int)sszRet, errno, strerror(errno),
+ strPipeFilename);
+ palErr = ERROR_INTERNAL_ERROR;
+ break;
+ }
+ iBytesToWrite -= (int)sszRet;
+ pPos += sszRet;
+
+ _ASSERT_MSG(0 == iBytesToWrite,
+ "Interleaved messages while writing to process pipe %d\n",
+ iProcessPipe);
+ }
+
+ // Close the opened pipe
+ close(iProcessPipe);
+
+ SMTRW_exit:
+ return palErr;
+#else // !CORECLR
+ ASSERT("There should never be a reason to send a message to a remote worker\n");
+ return ERROR_INTERNAL_ERROR;
+#endif // !CORECLR
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::WakeUpLocalWorkerThread
+
+ Wakes up the local worker thread by writing a 'nop' cmd to the
+ process pipe.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::WakeUpLocalWorkerThread(
+ SynchWorkerCmd swcWorkerCmd)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+
+ _ASSERT_MSG((swcWorkerCmd & 0xFF) == swcWorkerCmd,
+ "Value too big for swcWorkerCmd\n");
+
+ _ASSERT_MSG((SynchWorkerCmdNop == swcWorkerCmd) ||
+ (SynchWorkerCmdShutdown == swcWorkerCmd),
+ "WakeUpLocalWorkerThread supports only SynchWorkerCmdNop, and SynchWorkerCmdShutdown."
+ "[received cmd=%d]\n", swcWorkerCmd);
+
+ BYTE byCmd = (BYTE)(swcWorkerCmd & 0xFF);
+
+ TRACE("Waking up Synch Worker Thread for %u [byCmd=%u]\n",
+ swcWorkerCmd, (unsigned int)byCmd);
+
+ // As long as we use pipes and we keep the message size
+ // within PIPE_BUF, there's no need to lock here, since the
+ // write is guaranteed not to be interleaved with/into other
+ // writes of PIPE_BUF bytes or less.
+ _ASSERT_MSG(sizeof(BYTE) <= PIPE_BUF, "Message too long\n");
+
+ int iRetryCount = 0;
+ ssize_t sszWritten;
+ do
+ {
+ sszWritten = write(m_iProcessPipeWrite, &byCmd, sizeof(BYTE));
+ } while (-1 == sszWritten &&
+ EAGAIN == errno &&
+ ++iRetryCount < MaxConsecutiveEagains &&
+ 0 == sched_yield());
+
+ if (sszWritten != sizeof(BYTE))
+ {
+ ERROR("Unable to write to the process pipe to wake up the "
+ "worker thread [errno=%d (%s)]\n", errno, strerror(errno));
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::GetThreadWaitInfo
+
+ Returns a pointer to the WaitInfo structure for the passed CPalThread object
+ --*/
+ ThreadWaitInfo * CPalSynchronizationManager::GetThreadWaitInfo(
+ CPalThread * pthrCurrent)
+ {
+ return &pthrCurrent->synchronizationInfo.m_twiWaitInfo;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::UnRegisterWait
+
+ Unregister the wait described by ptwiWaitInfo that in general involves
+ a thread other than the current one (most of the times the deregistration
+ is performed by the signaling thread)
+
+ Note: this method must be called while holding the local process
+ synchronization lock.
+ --*/
+ void CPalSynchronizationManager::UnRegisterWait(
+ CPalThread * pthrCurrent,
+ ThreadWaitInfo * ptwiWaitInfo,
+ bool fHaveSharedLock)
+ {
+ int i = 0;
+ CSynchData * psdSynchData = NULL;
+ bool fSharedSynchLock = false;
+
+ if (!fHaveSharedLock && LocalWait != ptwiWaitInfo->wdWaitDomain)
+ {
+ AcquireSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = true;
+ }
+
+ TRACE("Unregistering wait for thread=%u [ObjCount=%d WaitType=%u WaitDomain=%u]\n",
+ ptwiWaitInfo->pthrOwner->GetThreadId(),
+ ptwiWaitInfo->lObjCount, ptwiWaitInfo->wtWaitType,
+ ptwiWaitInfo->wdWaitDomain);
+
+ for (i=0; i < ptwiWaitInfo->lObjCount; i++)
+ {
+ WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i];
+
+ VALIDATEOBJECT(pwtlnItem);
+
+ if (pwtlnItem->dwFlags & WTLN_FLAG_OWNER_OBJECT_IS_SHARED)
+ {
+ // Shared object
+ WaitingThreadsListNode * pwtlnItemNext, * pwtlnItemPrev;
+
+ psdSynchData = SharedIDToTypePointer(CSynchData,
+ pwtlnItem->ptrOwnerObjSynchData.shrid);
+
+ VALIDATEOBJECT(psdSynchData);
+
+ pwtlnItemNext = SharedIDToTypePointer(WaitingThreadsListNode,
+ pwtlnItem->ptrNext.shrid);
+ pwtlnItemPrev = SharedIDToTypePointer(WaitingThreadsListNode,
+ pwtlnItem->ptrPrev.shrid);
+ if (pwtlnItemPrev)
+ {
+ VALIDATEOBJECT(pwtlnItemPrev);
+ pwtlnItemPrev->ptrNext.shrid = pwtlnItem->ptrNext.shrid;
+ }
+ else
+ {
+ psdSynchData->SetWTLHeadShrPtr(pwtlnItem->ptrNext.shrid);
+ }
+
+ if (pwtlnItemNext)
+ {
+ VALIDATEOBJECT(pwtlnItemNext);
+ pwtlnItemNext->ptrPrev.shrid = pwtlnItem->ptrPrev.shrid;
+ }
+ else
+ {
+ psdSynchData->SetWTLTailShrPtr(pwtlnItem->ptrPrev.shrid);
+ }
+
+ m_cacheSHRWTListNodes.Add(pthrCurrent, pwtlnItem->shridSHRThis);
+ }
+ else
+ {
+ // Local object
+ psdSynchData = pwtlnItem->ptrOwnerObjSynchData.ptr;
+
+ VALIDATEOBJECT(psdSynchData);
+
+ if (pwtlnItem->ptrPrev.ptr)
+ {
+ VALIDATEOBJECT(pwtlnItem);
+ pwtlnItem->ptrPrev.ptr->ptrNext.ptr = pwtlnItem->ptrNext.ptr;
+ }
+ else
+ {
+ psdSynchData->SetWTLHeadPtr(pwtlnItem->ptrNext.ptr);
+ }
+
+ if (pwtlnItem->ptrNext.ptr)
+ {
+ VALIDATEOBJECT(pwtlnItem);
+ pwtlnItem->ptrNext.ptr->ptrPrev.ptr = pwtlnItem->ptrPrev.ptr;
+ }
+ else
+ {
+ psdSynchData->SetWTLTailPtr(pwtlnItem->ptrPrev.ptr);
+ }
+
+ m_cacheWTListNodes.Add(pthrCurrent, pwtlnItem);
+ }
+
+ // Release the node's refcount on the synch data, and decerement
+ // waiting thread count
+ psdSynchData->DecrementWaitingThreadCount();
+ psdSynchData->Release(pthrCurrent);
+ }
+
+ // Reset wait data in ThreadWaitInfo structure: it is enough
+ // to reset lObjCount, lSharedObjCount and wdWaitDomain.
+ ptwiWaitInfo->lObjCount = 0;
+ ptwiWaitInfo->lSharedObjCount = 0;
+ ptwiWaitInfo->wdWaitDomain = LocalWait;
+
+ // Done
+ if (fSharedSynchLock)
+ {
+ ReleaseSharedSynchLock(pthrCurrent);
+ }
+
+ return;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll
+
+ Unsignals all the objects involved in a wait all, except the target
+ one (i.e. psdTgtObjectSynchData)
+
+ Note: this method must be called while holding the synchronization locks
+ appropriate to all the objects involved in the wait-all. If any
+ of the objects is shared, the caller must own both local and
+ shared synch locks; if no shared object is involved in the wait,
+ only the local synch lock is needed.
+ --*/
+ void CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget,
+ WaitingThreadsListNode * pwtlnNode,
+ CSynchData * psdTgtObjectSynchData)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ CSynchData * psdSynchDataItem = NULL;
+
+#ifdef _DEBUG
+ bool bOriginatingNodeFound = false;
+#endif
+
+ VALIDATEOBJECT(psdTgtObjectSynchData);
+ VALIDATEOBJECT(pwtlnNode);
+
+ _ASSERT_MSG(0 != (WTLN_FLAG_WAIT_ALL & pwtlnNode->dwFlags),
+ "UnsignalRestOfLocalAwakeningWaitAll() called on a normal (non wait all) wait");
+
+ _ASSERT_MSG(gPID == pwtlnNode->dwProcessId,
+ "UnsignalRestOfLocalAwakeningWaitAll() called on a wait all with remote awakening");
+
+ ThreadWaitInfo *ptwiWaitInfo = pwtlnNode->ptwiWaitInfo;
+
+ int iObjCount = ptwiWaitInfo->lObjCount;
+ for (int i = 0; i < iObjCount; i++)
+ {
+ WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i];
+
+ VALIDATEOBJECT(pwtlnItem);
+
+ if (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnItem->dwFlags))
+ {
+ psdSynchDataItem = SharedIDToTypePointer(CSynchData, pwtlnItem->ptrOwnerObjSynchData.shrid);
+ }
+ else
+ {
+ psdSynchDataItem = pwtlnItem->ptrOwnerObjSynchData.ptr;
+ }
+
+ VALIDATEOBJECT(psdSynchDataItem);
+
+ // Skip originating node
+ if (psdTgtObjectSynchData == psdSynchDataItem)
+ {
+#ifdef _DEBUG
+ bOriginatingNodeFound = true;
+#endif
+ continue;
+ }
+
+ palErr = psdSynchDataItem->ReleaseWaiterWithoutBlocking(pthrCurrent, pthrTarget);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("ReleaseWaiterWithoutBlocking failed on SynchData @ %p [palErr = %u]\n", psdSynchDataItem, palErr);
+ }
+ }
+
+ _ASSERT_MSG(bOriginatingNodeFound, "Couldn't find originating node while unsignaling rest of the wait all\n");
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::MarkWaitForDelegatedObjectSignalingInProgress
+
+ Marks all the thread waiting list nodes involved in the the current wait-all
+ for "delegated object signaling in progress", so that this wait cannot be
+ involved in another delegated object signaling that may happen while the
+ current object singaling is being tranfered to the target process (while
+ transfering it, synchronization locks are released in this process and later
+ grabbed again in the target process; in this time window another thread
+ could signal another object part of the same wait-all. In this case no
+ signal delegation must take place.
+
+ Note: this method must be called while holding the synchronization locks
+ appropriate to the target object described by pwtlnNode (i.e. the
+ local process synch lock if the target object is local, both local
+ and shared one if the object is shared).
+ --*/
+ void CPalSynchronizationManager::MarkWaitForDelegatedObjectSignalingInProgress(
+ CPalThread * pthrCurrent,
+ WaitingThreadsListNode * pwtlnNode)
+ {
+ bool fSharedSynchLock = false;
+ bool fTargetObjectIsShared = (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNode->dwFlags));
+
+ VALIDATEOBJECT(pwtlnNode);
+
+ _ASSERT_MSG(gPID == pwtlnNode->dwProcessId,
+ "MarkWaitForDelegatedObjectSignalingInProgress() called from the wrong process");
+
+ ThreadWaitInfo *ptwiWaitInfo = pwtlnNode->ptwiWaitInfo;
+
+ if (!fSharedSynchLock && !fTargetObjectIsShared &&
+ LocalWait != ptwiWaitInfo->wdWaitDomain)
+ {
+ AcquireSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = true;
+ }
+
+ _ASSERT_MSG(MultipleObjectsWaitAll == ptwiWaitInfo->wtWaitType,
+ "MarkWaitForDelegatedObjectSignalingInProgress() called on a normal (non wait-all) wait");
+
+ // Unmark all nodes other than the target one
+ int iTgtCount = ptwiWaitInfo->lObjCount;
+ for (int i = 0; i < iTgtCount; i++)
+ {
+ VALIDATEOBJECT(ptwiWaitInfo->rgpWTLNodes[i]);
+ ptwiWaitInfo->rgpWTLNodes[i]->dwFlags &= ~WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;
+ }
+
+ // Mark the target node
+ pwtlnNode->dwFlags |= WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;
+
+ // Done
+ if (fSharedSynchLock)
+ {
+ ReleaseSharedSynchLock(pthrCurrent);
+ }
+
+ return;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::UnmarkTWListForDelegatedObjectSignalingInProgress
+
+ Resets the "delegated object signaling in progress" flags in all the
+ nodes of the thread waitin list for the target waitable objects (represented
+ by its SynchData)
+
+ Note: this method must be called while holding the appropriate
+ synchronization locks (the local process synch lock if the target
+ object is local, both local and shared one if the object is shared).
+ --*/
+ void CPalSynchronizationManager::UnmarkTWListForDelegatedObjectSignalingInProgress(
+ CSynchData * pTgtObjectSynchData)
+ {
+ bool fSharedObject = (SharedObject == pTgtObjectSynchData->GetObjectDomain());
+ WaitingThreadsListNode * pwtlnNode;
+
+ VALIDATEOBJECT(pTgtObjectSynchData);
+
+ pwtlnNode = fSharedObject ? SharedIDToTypePointer(WaitingThreadsListNode, pTgtObjectSynchData->GetWTLHeadShmPtr())
+ : pTgtObjectSynchData->GetWTLHeadPtr();
+
+ while (pwtlnNode)
+ {
+ VALIDATEOBJECT(pwtlnNode);
+
+ pwtlnNode->dwFlags &= ~WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;
+ pwtlnNode = fSharedObject ? SharedIDToTypePointer(WaitingThreadsListNode, pwtlnNode->ptrNext.shrid)
+ : pwtlnNode->ptrNext.ptr;
+ }
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::RegisterProcessForMonitoring
+
+ Registers the process object represented by the passed psdSynchData and
+ pProcLocalData. The worker thread will monitor the actual process and,
+ upon process termination, it will set the exit code in pProcLocalData,
+ and it will signal the process object, by signaling its psdSynchData.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::RegisterProcessForMonitoring(
+ CPalThread * pthrCurrent,
+ CSynchData *psdSynchData,
+ IPalObject *pProcessObject,
+ CProcProcessLocalData * pProcLocalData)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ MonitoredProcessesListNode * pmpln;
+ bool fWakeUpWorker = false;
+ bool fMonitoredProcessesLock = false;
+
+ VALIDATEOBJECT(psdSynchData);
+
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+
+ fMonitoredProcessesLock = true;
+
+ pmpln = m_pmplnMonitoredProcesses;
+ while (pmpln)
+ {
+ if (psdSynchData == pmpln->psdSynchData)
+ {
+ _ASSERT_MSG(pmpln->dwPid == pProcLocalData->dwProcessId, "Invalid node in Monitored Processes List\n");
+ break;
+ }
+
+ pmpln = pmpln->pNext;
+ }
+
+ if (pmpln)
+ {
+ pmpln->lRefCount++;
+ }
+ else
+ {
+ pmpln = InternalNew<MonitoredProcessesListNode>();
+ if (NULL == pmpln)
+ {
+ ERROR("No memory to allocate MonitoredProcessesListNode structure\n");
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ goto RPFM_exit;
+ }
+
+ pmpln->lRefCount = 1;
+ pmpln->dwPid = pProcLocalData->dwProcessId;
+ pmpln->dwExitCode = 0;
+ pmpln->pProcessObject = pProcessObject;
+ pmpln->pProcessObject->AddReference();
+ pmpln->pProcLocalData = pProcLocalData;
+
+ // Acquire SynchData and AddRef it
+ pmpln->psdSynchData = psdSynchData;
+ psdSynchData->AddRef();
+
+ pmpln->pNext = m_pmplnMonitoredProcesses;
+ m_pmplnMonitoredProcesses = pmpln;
+ m_lMonitoredProcessesCount++;
+
+ fWakeUpWorker = true;
+ }
+
+ // Unlock
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = false;
+
+ if (fWakeUpWorker)
+ {
+ CPalSynchronizationManager * pSynchManager = GetInstance();
+
+ palErr = pSynchManager->WakeUpLocalWorkerThread(SynchWorkerCmdNop);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Failed waking up worker thread for process "
+ "monitoring registration [errno=%d {%s%}]\n",
+ errno, strerror(errno));
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+ }
+
+ RPFM_exit:
+ if (fMonitoredProcessesLock)
+ {
+ InternalLeaveCriticalSection(pthrCurrent,
+ &s_csMonitoredProcessesLock);
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::UnRegisterProcessForMonitoring
+
+ Unregisters a process object currently monitored by the worker thread
+ (typically called if the wait timed out before the process exited, or
+ if the wait was a normal (i.e. non wait-all) wait that involved othter
+ objects, and another object has been signaled).
+ --*/
+ PAL_ERROR CPalSynchronizationManager::UnRegisterProcessForMonitoring(
+ CPalThread * pthrCurrent,
+ CSynchData *psdSynchData,
+ DWORD dwPid)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ MonitoredProcessesListNode * pmpln, * pmplnPrev = NULL;
+
+ VALIDATEOBJECT(psdSynchData);
+
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+
+ pmpln = m_pmplnMonitoredProcesses;
+ while (pmpln)
+ {
+ if (psdSynchData == pmpln->psdSynchData)
+ {
+ _ASSERT_MSG(dwPid == pmpln->dwPid, "Invalid node in Monitored Processes List\n");
+ break;
+ }
+
+ pmplnPrev = pmpln;
+ pmpln = pmpln->pNext;
+ }
+
+ if (pmpln)
+ {
+ if (0 == --pmpln->lRefCount)
+ {
+ if (NULL != pmplnPrev)
+ {
+ pmplnPrev->pNext = pmpln->pNext;
+ }
+ else
+ {
+ m_pmplnMonitoredProcesses = pmpln->pNext;
+ }
+
+ m_lMonitoredProcessesCount--;
+ pmpln->pProcessObject->ReleaseReference(pthrCurrent);
+ pmpln->psdSynchData->Release(pthrCurrent);
+ InternalDelete(pmpln);
+ }
+ }
+ else
+ {
+ palErr = ERROR_NOT_FOUND;
+ }
+
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ThreadPrepareForShutdown
+
+ Used to hijack thread execution from known spots within the
+ Synchronization Manager in case a PAL shutdown is initiated
+ or the thread is being terminated by another thread.
+ --*/
+ void CPalSynchronizationManager::ThreadPrepareForShutdown()
+ {
+ TRACE("The Synchronization Manager hijacked the current thread "
+ "for process shutdown or thread termination\n");
+ while (true)
+ {
+ poll(NULL, 0, INFTIM);
+ sched_yield();
+ }
+
+ ASSERT("This code should never be executed\n");
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::DoMonitorProcesses
+
+ This method is called by the worker thread to execute one step of
+ monitoring for all the process currently registered for monitoring
+ --*/
+ LONG CPalSynchronizationManager::DoMonitorProcesses(
+ CPalThread * pthrCurrent)
+ {
+ MonitoredProcessesListNode * pNode, * pPrev = NULL, * pNext;
+ LONG lInitialNodeCount;
+ LONG lRemovingCount = 0;
+ bool fLocalSynchLock = false;
+ bool fSharedSynchLock = false;
+ bool fMonitoredProcessesLock = false;
+
+ // Note: we first need to grab the monitored processes lock to walk
+ // the list of monitored processes, and then, if there is any
+ // which exited, to grab the synchronization lock(s) to signal
+ // the process object. Anyway we cannot grab the synchronization
+ // lock(s) while holding the monitored processes lock; that
+ // would cause deadlock, since RegisterProcessForMonitoring and
+ // UnRegisterProcessForMonitoring call stacks grab the locks
+ // in the opposite order. Grabbing the synch lock(s) first (and
+ // therefore all the times) would cause unacceptable contention
+ // (process monitoring is done in polling mode).
+ // Therefore we need to remove list nodes for processes that
+ // exited copying them to the exited array, while holding only
+ // the monitored processes lock, and then to signal them from that
+ // array holding synch lock(s) and monitored processes lock,
+ // acquired in this order. Holding again the monitored processes
+ // lock is needed in order to support object promotion.
+
+ // Grab the monitored processes lock
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = true;
+
+ lInitialNodeCount = m_lMonitoredProcessesCount;
+
+ pNode = m_pmplnMonitoredProcesses;
+ while (pNode)
+ {
+ pNext = pNode->pNext;
+
+ if (HasProcessExited(pNode->dwPid,
+ &pNode->dwExitCode,
+ &pNode->fIsActualExitCode))
+ {
+ TRACE("Process %u exited with return code %u\n",
+ pNode->dwPid,
+ pNode->fIsActualExitCode ? "actual" : "guessed",
+ pNode->dwExitCode);
+
+ if (NULL != pPrev)
+ {
+ pPrev->pNext = pNext;
+ }
+ else
+ {
+ m_pmplnMonitoredProcesses = pNext;
+ }
+
+ m_lMonitoredProcessesCount--;
+
+ // Insert in the list of nodes for exited processes
+ pNode->pNext = m_pmplnExitedNodes;
+ m_pmplnExitedNodes = pNode;
+ lRemovingCount++;
+ }
+ else
+ {
+ pPrev = pNode;
+ }
+
+ // Go to the next
+ pNode = pNext;
+ }
+
+ // Release the monitored processes lock
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = false;
+
+ if (lRemovingCount > 0)
+ {
+ // First grab the local synch lock
+ AcquireLocalSynchLock(pthrCurrent);
+ fLocalSynchLock = true;
+
+ // Acquire the monitored processes lock
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = true;
+
+ if (!fSharedSynchLock)
+ {
+ bool fSharedSynchLockIsNeeded = false;
+
+ // See if the shared lock is needed
+ pNode = m_pmplnExitedNodes;
+ while (pNode)
+ {
+ if (SharedObject == pNode->psdSynchData->GetObjectDomain())
+ {
+ fSharedSynchLockIsNeeded = true;
+ break;
+ }
+
+ pNode = pNode->pNext;
+ }
+
+ if (fSharedSynchLockIsNeeded)
+ {
+ // Release the monitored processes lock
+ InternalLeaveCriticalSection(pthrCurrent,
+ &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = false;
+
+ // Acquire the shared synch lock
+ AcquireSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = true;
+
+ // Acquire again the monitored processes lock
+ InternalEnterCriticalSection(pthrCurrent,
+ &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = true;
+ }
+ }
+
+ // Start from the beginning of the exited processes list
+ pNode = m_pmplnExitedNodes;
+
+ // Invalidate the list
+ m_pmplnExitedNodes = NULL;
+
+ while (pNode)
+ {
+ pNext = pNode->pNext;
+
+ TRACE("Process pid=%u exited with exitcode=%u\n",
+ pNode->dwPid, pNode->dwExitCode);
+
+ // Store the exit code in the process local data
+ if (pNode->fIsActualExitCode)
+ {
+ pNode->pProcLocalData->dwExitCode = pNode->dwExitCode;
+ }
+
+ // Set process status to PS_DONE
+ pNode->pProcLocalData->ps = PS_DONE;
+
+ // Set signal count
+ pNode->psdSynchData->SetSignalCount(1);
+
+ // Releasing all local waiters
+ //
+ // We just called directly in CSynchData::SetSignalCount(), so
+ // we need to take care of waking up waiting threads according
+ // to the Process object semantics (i.e. every thread must be
+ // awakend). Anyway if a process object is shared among two or
+ // more processes and threads from different processes are
+ // waiting on it, the object will be registered for monitoring
+ // in each of the processes. As result its signal count will
+ // be set to one more times (which is not a problem, given the
+ // process object semantics) and each worker thread will wake
+ // up waiting threads. Therefore we need to make sure that each
+ // worker wakes up only threads in its own process: we do that
+ // by calling ReleaseAllLocalWaiters
+ pNode->psdSynchData->ReleaseAllLocalWaiters(pthrCurrent);
+
+ // We are done with pProcLocalData, so we can release the process object
+ pNode->pProcessObject->ReleaseReference(pthrCurrent);
+
+ // Release the reference to the SynchData
+ pNode->psdSynchData->Release(pthrCurrent);
+
+ // Delete the node
+ InternalDelete(pNode);
+
+ // Go to the next
+ pNode = pNext;
+ }
+ }
+
+ if (fMonitoredProcessesLock)
+ {
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ }
+
+ if (fSharedSynchLock)
+ {
+ ReleaseSharedSynchLock(pthrCurrent);
+ }
+
+ if (fLocalSynchLock)
+ {
+ ReleaseLocalSynchLock(pthrCurrent);
+ }
+
+ return (lInitialNodeCount - lRemovingCount);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::DiscardMonitoredProcesses
+
+ This method is called at shutdown time to discard all the registration
+ for the processes currently monitored by the worker thread.
+ This method must be called at shutdown time, otherwise some shared memory
+ may be leaked at process shutdown.
+ --*/
+ void CPalSynchronizationManager::DiscardMonitoredProcesses(
+ CPalThread * pthrCurrent)
+ {
+ MonitoredProcessesListNode * pNode;
+
+ // Grab the monitored processes lock
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+
+ while (m_pmplnMonitoredProcesses)
+ {
+ pNode = m_pmplnMonitoredProcesses;
+ m_pmplnMonitoredProcesses = pNode->pNext;
+ pNode->pProcessObject->ReleaseReference(pthrCurrent);
+ pNode->psdSynchData->Release(pthrCurrent);
+ InternalDelete(pNode);
+ }
+
+ // Release the monitored processes lock
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::CreateProcessPipe
+
+ Creates the process pipe for the current process
+ --*/
+ bool CPalSynchronizationManager::CreateProcessPipe()
+ {
+ bool fRet = true;
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ int iKq = -1;
+#endif // HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+
+#ifndef CORECLR
+ int iPipeRd = -1, iPipeWr = -1;
+ char szPipeFilename[MAX_PATH];
+
+ /* Create the blocking pipe */
+ if (!GetProcessPipeName(szPipeFilename, MAX_PATH, gPID))
+ {
+ ERROR("couldn't get process pipe's name\n");
+ szPipeFilename[0] = 0;
+ fRet = false;
+ goto CPP_exit;
+ }
+
+ /* create the pipe, with full access to the owner only */
+ if (mkfifo(szPipeFilename, S_IRWXU) == -1)
+ {
+ if (errno == EEXIST)
+ {
+ /* Some how no one deleted the pipe, perhaps it was left behind
+ from a crash?? Delete the pipe and try again. */
+ if (-1 == unlink(szPipeFilename))
+ {
+ ERROR( "Unable to delete the process pipe that was left behind.\n" );
+ fRet = false;
+ goto CPP_exit;
+ }
+ else
+ {
+ if (mkfifo(szPipeFilename, S_IRWXU) == -1)
+ {
+ ERROR( "Still unable to create the process pipe...giving up!\n" );
+ fRet = false;
+ goto CPP_exit;
+ }
+ }
+ }
+ else
+ {
+ ERROR( "Unable to create the process pipe.\n" );
+ fRet = false;
+ goto CPP_exit;
+ }
+ }
+
+ iPipeRd = InternalOpen(szPipeFilename, O_RDONLY | O_NONBLOCK);
+ if (iPipeRd == -1)
+ {
+ ERROR("Unable to open the process pipe for read\n");
+ fRet = false;
+ goto CPP_exit;
+ }
+
+ iPipeWr = InternalOpen(szPipeFilename, O_WRONLY | O_NONBLOCK);
+ if (iPipeWr == -1)
+ {
+ ERROR("Unable to open the process pipe for write\n");
+ fRet = false;
+ goto CPP_exit;
+ }
+#else // !CORECLR
+ int rgiPipe[] = { -1, -1 };
+ int pipeRv =
+#if HAVE_PIPE2
+ pipe2(rgiPipe, O_CLOEXEC);
+#else
+ pipe(rgiPipe);
+#endif // HAVE_PIPE2
+ if (pipeRv == -1)
+ {
+ ERROR("Unable to create the process pipe\n");
+ fRet = false;
+ goto CPP_exit;
+ }
+#if !HAVE_PIPE2
+ fcntl(rgiPipe[0], F_SETFD, FD_CLOEXEC); // make pipe non-inheritable, if possible
+ fcntl(rgiPipe[1], F_SETFD, FD_CLOEXEC);
+#endif // !HAVE_PIPE2
+#endif // !CORECLR
+
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ iKq = kqueue();
+ if (-1 == iKq)
+ {
+ ERROR("Failed to create kqueue associated to process pipe\n");
+ fRet = false;
+ goto CPP_exit;
+ }
+#endif // HAVE_KQUEUE
+
+ CPP_exit:
+ if (fRet)
+ {
+ // Succeeded
+#ifndef CORECLR
+ m_iProcessPipeRead = iPipeRd;
+ m_iProcessPipeWrite = iPipeWr;
+#else // !CORECLR
+ m_iProcessPipeRead = rgiPipe[0];
+ m_iProcessPipeWrite = rgiPipe[1];
+#endif // !CORECLR
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ m_iKQueue = iKq;
+#endif // HAVE_KQUEUE
+ }
+ else
+ {
+#ifndef CORECLR
+ // Failed
+ if (0 != szPipeFilename[0])
+ {
+ unlink(szPipeFilename);
+ }
+ if (-1 != iPipeRd)
+ {
+ close(iPipeRd);
+ }
+ if (-1 != iPipeWr)
+ {
+ close(iPipeWr);
+ }
+#else // !CORECLR
+ if (-1 != rgiPipe[0])
+ {
+ close(rgiPipe[0]);
+ close(rgiPipe[1]);
+ }
+#endif // !CORECLR
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ if (-1 != iKq)
+ {
+ close(iKq);
+ }
+#endif // HAVE_KQUEUE
+ }
+
+ return fRet;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ShutdownProcessPipe
+
+ Shuts down the process pipe and removes the fifo so that other processes
+ can no longer open it. It also closes the local write end of the pipe (see
+ comment below). From this moment on the worker thread will process any
+ possible data already received in the pipe (but not yet consumed) and any
+ data written by processes that still have a opened write end of this pipe;
+ it will wait (with timeout) until the last remote process which has a write
+ end opened closes it, and then it will yield to process shutdown.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::ShutdownProcessPipe()
+ {
+ PAL_ERROR palErr = NO_ERROR;
+#ifndef CORECLR
+ char szPipeFilename[MAX_PATH];
+
+ if (GetProcessPipeName(szPipeFilename, MAX_PATH, gPID))
+ {
+ if (unlink(szPipeFilename) == -1)
+ {
+ ERROR("Unable to unlink the pipe file name errno=%d (%s)\n",
+ errno, strerror(errno));
+ palErr = ERROR_INTERNAL_ERROR;
+ // go on anyway
+ }
+ }
+ else
+ {
+ ERROR("Couldn't get the process pipe's name\n");
+ palErr = ERROR_INTERNAL_ERROR;
+ // go on anyway
+ }
+#endif // CORECLR
+
+ if (-1 != m_iProcessPipeWrite)
+ {
+ // Closing the write end of the process pipe. When the last process
+ // that still has a open write-fd on this pipe will close it, the
+ // worker thread will receive an EOF; the worker thread will wait
+ // for this EOF before shutting down, so to ensure to process any
+ // possible data already written to the pipe by other processes
+ // when the shutdown has been initiated in the current process.
+ // Note: no need here to worry about platforms where close(pipe)
+ // blocks on outstanding syscalls, since we are the only one using
+ // this fd.
+ TRACE("Closing the write end of process pipe\n");
+ if (close(m_iProcessPipeWrite) == -1)
+ {
+ ERROR("Unable to close the write end of process pipe\n");
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+
+ m_iProcessPipeWrite = -1;
+ }
+
+ return palErr;
+ }
+
+#ifndef CORECLR
+ /*++
+ Method:
+ CPalSynchronizationManager::GetProcessPipeName
+
+ Returns the process pipe name for the target process (identified by its PID)
+ --*/
+ bool CPalSynchronizationManager::GetProcessPipeName(
+ LPSTR pDest,
+ int iDestSize,
+ DWORD dwPid)
+ {
+ CHAR config_dir[MAX_PATH];
+ int needed_size;
+
+ _ASSERT_MSG(NULL != pDest, "Destination pointer is NULL!\n");
+ _ASSERT_MSG(0 < iDestSize,"Invalid buffer size %d\n", iDestSize);
+
+ if (!PALGetPalConfigDir(config_dir, MAX_PATH))
+ {
+ ASSERT("Unable to determine the PAL config directory.\n");
+ pDest[0] = '\0';
+ return false;
+ }
+ needed_size = snprintf(pDest, iDestSize, "%s/%s-%u", config_dir,
+ PROCESS_PIPE_NAME_PREFIX, dwPid);
+ pDest[iDestSize-1] = 0;
+ if(needed_size >= iDestSize)
+ {
+ ERROR("threadpipe name needs %d characters, buffer only has room for "
+ "%d\n", needed_size, iDestSize+1);
+ return false;
+ }
+ return true;
+ }
+#endif // !CORECLR
+
+ /*++
+ Method:
+ CPalSynchronizationManager::AcquireProcessLock
+
+ Acquires the local Process Lock (which currently is the same as the
+ the local Process Synch Lock)
+ --*/
+ void CPalSynchronizationManager::AcquireProcessLock(CPalThread * pthrCurrent)
+ {
+ AcquireLocalSynchLock(pthrCurrent);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ReleaseProcessLock
+
+ Releases the local Process Lock (which currently is the same as the
+ the local Process Synch Lock)
+ --*/
+ void CPalSynchronizationManager::ReleaseProcessLock(CPalThread * pthrCurrent)
+ {
+ ReleaseLocalSynchLock(pthrCurrent);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::PromoteObjectSynchData
+
+ Promotes an object's synchdata from local to shared
+ --*/
+ PAL_ERROR CPalSynchronizationManager::PromoteObjectSynchData(
+ CPalThread *pthrCurrent,
+ VOID *pvLocalSynchData,
+ VOID **ppvSharedSynchData)
+ {
+ PAL_ERROR palError = NO_ERROR;
+ CSynchData *psdLocal = reinterpret_cast<CSynchData *>(pvLocalSynchData);
+ CSynchData *psdShared = NULL;
+ SharedID shridSynchData = NULL;
+ SharedID *rgshridWTLNodes = NULL;
+ CObjectType *pot = NULL;
+ ULONG ulcWaitingThreads;
+
+ _ASSERTE(NULL != pthrCurrent);
+ _ASSERTE(NULL != pvLocalSynchData);
+ _ASSERTE(NULL != ppvSharedSynchData);
+ _ASSERTE(ProcessLocalObject == psdLocal->GetObjectDomain());
+
+#if _DEBUG
+
+ //
+ // TODO: Verify that the proper locks are held
+ //
+#endif
+
+ //
+ // Allocate shared memory CSynchData and map to local memory
+ //
+
+ shridSynchData = m_cacheSHRSynchData.Get(pthrCurrent);
+ if (NULL == shridSynchData)
+ {
+ ERROR("Unable to allocate shared memory\n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto POSD_exit;
+ }
+
+ psdShared = SharedIDToTypePointer(CSynchData, shridSynchData);
+ _ASSERTE(NULL != psdShared);
+
+ //
+ // Allocate shared memory WaitingThreadListNodes if there are
+ // any threads currently waiting on this object
+ //
+
+ ulcWaitingThreads = psdLocal->GetWaitingThreadCount();
+ if (0 < ulcWaitingThreads)
+ {
+ int i;
+
+ rgshridWTLNodes = InternalNewArray<SharedID>(ulcWaitingThreads);
+ if (NULL == rgshridWTLNodes)
+ {
+ palError = ERROR_OUTOFMEMORY;
+ goto POSD_exit;
+ }
+
+ i = m_cacheSHRWTListNodes.Get(
+ pthrCurrent,
+ ulcWaitingThreads,
+ rgshridWTLNodes
+ );
+
+ if (static_cast<ULONG>(i) != ulcWaitingThreads)
+ {
+ for (i -= 1; i >= 0; i -= 1)
+ {
+ m_cacheSHRWTListNodes.Add(pthrCurrent, rgshridWTLNodes[i]);
+ }
+
+ palError = ERROR_OUTOFMEMORY;
+ goto POSD_exit;
+ }
+ }
+
+ //
+ // If the synch data is for a process object we need to grab
+ // the monitored process list lock here
+ //
+
+ pot = psdLocal->GetObjectType();
+ _ASSERTE(NULL != pot);
+
+ if (otiProcess == pot->GetId())
+ {
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ }
+
+ //
+ // Copy pertinent CSynchData info to the shared memory version (and
+ // initialize other members)
+ //
+
+ psdShared->SetSharedThis(shridSynchData);
+ psdShared->SetObjectDomain(SharedObject);
+ psdShared->SetObjectType(psdLocal->GetObjectType());
+ psdShared->SetSignalCount(psdLocal->GetSignalCount());
+
+#ifdef SYNCH_STATISTICS
+ psdShared->SetStatContentionCount(psdLocal->GetStatContentionCount());
+ psdShared->SetStatWaitCount(psdLocal->GetStatWaitCount());
+#endif
+
+ //
+ // Rebuild the waiting thread list, and update the wait domain
+ // for the waiting threads
+ //
+
+ psdShared->SetWTLHeadShrPtr(NULL);
+ psdShared->SetWTLTailShrPtr(NULL);
+
+ if (0 < ulcWaitingThreads)
+ {
+ WaitingThreadsListNode *pwtlnOld;
+ WaitingThreadsListNode *pwtlnNew;
+ int i = 0;
+
+ for (pwtlnOld = psdLocal->GetWTLHeadPtr();
+ pwtlnOld != NULL;
+ pwtlnOld = pwtlnOld->ptrNext.ptr, i += 1)
+ {
+ pwtlnNew = SharedIDToTypePointer(
+ WaitingThreadsListNode,
+ rgshridWTLNodes[i]
+ );
+
+ _ASSERTE(NULL != pwtlnNew);
+
+ pwtlnNew->shridSHRThis = rgshridWTLNodes[i];
+ pwtlnNew->ptrOwnerObjSynchData.shrid = shridSynchData;
+
+ pwtlnNew->dwThreadId = pwtlnOld->dwThreadId;
+ pwtlnNew->dwProcessId = pwtlnOld->dwProcessId;
+ pwtlnNew->dwObjIndex = pwtlnOld->dwObjIndex;
+ pwtlnNew->dwFlags = pwtlnOld->dwFlags | WTLN_FLAG_OWNER_OBJECT_IS_SHARED;
+ pwtlnNew->shridWaitingState = pwtlnOld->shridWaitingState;
+ pwtlnNew->ptwiWaitInfo = pwtlnOld->ptwiWaitInfo;
+
+ psdShared->SharedWaiterEnqueue(rgshridWTLNodes[i], false);
+ psdShared->AddRef();
+
+ _ASSERTE(pwtlnOld = pwtlnOld->ptwiWaitInfo->rgpWTLNodes[pwtlnOld->dwObjIndex]);
+ pwtlnNew->ptwiWaitInfo->rgpWTLNodes[pwtlnNew->dwObjIndex] = pwtlnNew;
+
+ pwtlnNew->ptwiWaitInfo->lSharedObjCount += 1;
+ if (pwtlnNew->ptwiWaitInfo->lSharedObjCount
+ == pwtlnNew->ptwiWaitInfo->lObjCount)
+ {
+ pwtlnNew->ptwiWaitInfo->wdWaitDomain = SharedWait;
+ }
+ else
+ {
+ _ASSERTE(pwtlnNew->ptwiWaitInfo->lSharedObjCount
+ < pwtlnNew->ptwiWaitInfo->lObjCount);
+
+ pwtlnNew->ptwiWaitInfo->wdWaitDomain = MixedWait;
+ }
+ }
+
+ _ASSERTE(psdShared->GetWaitingThreadCount() == ulcWaitingThreads);
+ }
+
+ //
+ // If the object tracks ownership and has a current owner update
+ // the OwnedObjectsListNode to point to the shared memory synch
+ // data
+ //
+
+ if (CObjectType::OwnershipTracked == pot->GetOwnershipSemantics())
+ {
+ OwnedObjectsListNode *pooln;
+
+ pooln = psdLocal->GetOwnershipListNode();
+ if (NULL != pooln)
+ {
+ pooln->pPalObjSynchData = psdShared;
+ psdShared->SetOwnershipListNode(pooln);
+ psdShared->AddRef();
+
+ //
+ // Copy over other ownership info.
+ //
+
+ psdShared->SetOwner(psdLocal->GetOwnerThread());
+ psdShared->SetOwnershipCount(psdLocal->GetOwnershipCount());
+ _ASSERTE(!psdShared->IsAbandoned());
+ }
+ else
+ {
+ _ASSERTE(0 == psdLocal->GetOwnershipCount());
+ _ASSERTE(0 == psdShared->GetOwnershipCount());
+ psdShared->SetAbandoned(psdLocal->IsAbandoned());
+ }
+ }
+
+ //
+ // If the synch data is for a process object update the monitored
+ // process list nodes to point to the shared memory object data,
+ // and release the monitored process list lock
+ //
+
+ if (otiProcess == pot->GetId())
+ {
+ MonitoredProcessesListNode *pmpn;
+
+ pmpn = m_pmplnMonitoredProcesses;
+ while (NULL != pmpn)
+ {
+ if (psdLocal == pmpn->psdSynchData)
+ {
+ pmpn->psdSynchData = psdShared;
+ psdShared->AddRef();
+ }
+
+ pmpn = pmpn->pNext;
+ }
+
+ pmpn = m_pmplnExitedNodes;
+ while (NULL != pmpn)
+ {
+ if (psdLocal == pmpn->psdSynchData)
+ {
+ pmpn->psdSynchData = psdShared;
+ psdShared->AddRef();
+ }
+
+ pmpn = pmpn->pNext;
+ }
+
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ }
+
+ *ppvSharedSynchData = reinterpret_cast<VOID*>(shridSynchData);
+
+ //
+ // Free the local memory items to caches
+ //
+
+ if (0 < ulcWaitingThreads)
+ {
+ WaitingThreadsListNode *pwtln;
+
+ pwtln = psdLocal->GetWTLHeadPtr();
+ while (NULL != pwtln)
+ {
+ WaitingThreadsListNode *pwtlnTemp;
+
+ pwtlnTemp = pwtln;
+ pwtln = pwtln->ptrNext.ptr;
+ m_cacheWTListNodes.Add(pthrCurrent, pwtlnTemp);
+ }
+ }
+
+ m_cacheSynchData.Add(pthrCurrent, psdLocal);
+
+ POSD_exit:
+
+ if (NULL != rgshridWTLNodes)
+ {
+ InternalDeleteArray(rgshridWTLNodes);
+ }
+
+ return palError;
+ }
+
+
+ /////////////////////////////
+ // //
+ // _ThreadNativeWaitData //
+ // //
+ /////////////////////////////
+
+ _ThreadNativeWaitData::~_ThreadNativeWaitData()
+ {
+ if (fInitialized)
+ {
+ fInitialized = false;
+ pthread_cond_destroy(&cond);
+ pthread_mutex_destroy(&mutex);
+ }
+ }
+
+
+ //////////////////////////////////
+ // //
+ // CThreadSynchronizationInfo //
+ // //
+ //////////////////////////////////
+
+ CThreadSynchronizationInfo::CThreadSynchronizationInfo() :
+ m_tsThreadState(TS_IDLE),
+ m_shridWaitAwakened(NULL),
+ m_lLocalSynchLockCount(0),
+ m_lSharedSynchLockCount(0)
+ {
+ InitializeListHead(&m_leOwnedObjsList);
+
+#ifdef SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ m_lPendingSignalingCount = 0;
+ InitializeListHead(&m_lePendingSignalingsOverflowList);
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ }
+
+ CThreadSynchronizationInfo::~CThreadSynchronizationInfo()
+ {
+ if (NULL != m_shridWaitAwakened)
+ {
+ free(m_shridWaitAwakened);
+ }
+ }
+
+ void CThreadSynchronizationInfo::AcquireNativeWaitLock()
+ {
+#if !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ int iRet;
+ iRet = pthread_mutex_lock(&m_tnwdNativeData.mutex);
+ _ASSERT_MSG(0 == iRet, "pthread_mutex_lock failed with error=%d\n", iRet);
+#endif // !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ }
+
+ void CThreadSynchronizationInfo::ReleaseNativeWaitLock()
+ {
+#if !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ int iRet;
+ iRet = pthread_mutex_unlock(&m_tnwdNativeData.mutex);
+ _ASSERT_MSG(0 == iRet, "pthread_mutex_unlock failed with error=%d\n", iRet);
+#endif // !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ }
+
+ bool CThreadSynchronizationInfo::TryAcquireNativeWaitLock()
+ {
+ bool fRet = true;
+#if !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ int iRet;
+ iRet = pthread_mutex_trylock(&m_tnwdNativeData.mutex);
+ _ASSERT_MSG(0 == iRet || EBUSY == iRet,
+ "pthread_mutex_trylock failed with error=%d\n", iRet);
+ fRet = (0 == iRet);
+#endif // !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ return fRet;
+ }
+
+ /*++
+ Method:
+ CThreadSynchronizationInfo::InitializePreCreate
+
+ Part of CThreadSynchronizationInfo's initialization to be carried out
+ before actual thread creation
+ --*/
+ PAL_ERROR CThreadSynchronizationInfo::InitializePreCreate(void)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ DWORD * pdwWaitState = NULL;
+ int iRet;
+ const int MaxUnavailableResourceRetries = 10;
+ int iEagains;
+ pthread_condattr_t attrs;
+ pthread_condattr_t *attrsPtr = nullptr;
+
+ m_shridWaitAwakened = malloc(sizeof(DWORD));
+ if (NULL == m_shridWaitAwakened)
+ {
+ ERROR("Fail allocating thread wait status shared object\n");
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ goto IPrC_exit;
+ }
+
+ pdwWaitState = SharedIDToTypePointer(DWORD,
+ m_shridWaitAwakened);
+
+ _ASSERT_MSG(NULL != pdwWaitState,
+ "Unable to map shared wait state: bad shared ID [shrid=%p]\n", (VOID*)m_shridWaitAwakened);
+
+ VolatileStore<DWORD>(pdwWaitState, TWS_ACTIVE);
+ m_tsThreadState = TS_STARTING;
+
+#if HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
+ attrsPtr = &attrs;
+ iRet = pthread_condattr_init(&attrs);
+ if (0 != iRet)
+ {
+ ERROR("Failed to initialize thread synchronization condition attribute "
+ "[error=%d (%s)]\n", iRet, strerror(iRet));
+ if (ENOMEM == iRet)
+ {
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+ goto IPrC_exit;
+ }
+
+ // Ensure that the pthread_cond_timedwait will use CLOCK_MONOTONIC
+ iRet = pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
+ if (0 != iRet)
+ {
+ ERROR("Failed set thread synchronization condition timed wait clock "
+ "[error=%d (%s)]\n", iRet, strerror(iRet));
+ palErr = ERROR_INTERNAL_ERROR;
+ pthread_condattr_destroy(&attrs);
+ goto IPrC_exit;
+ }
+#endif // HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
+
+ iEagains = 0;
+ Mutex_retry:
+ iRet = pthread_mutex_init(&m_tnwdNativeData.mutex, NULL);
+ if (0 != iRet)
+ {
+ ERROR("Failed creating thread synchronization mutex [error=%d (%s)]\n", iRet, strerror(iRet));
+ if (EAGAIN == iRet && MaxUnavailableResourceRetries >= ++iEagains)
+ {
+ poll(NULL, 0, std::min(100,10*iEagains));
+ goto Mutex_retry;
+ }
+ else if (ENOMEM == iRet)
+ {
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+
+ goto IPrC_exit;
+ }
+
+ iEagains = 0;
+ Cond_retry:
+
+ iRet = pthread_cond_init(&m_tnwdNativeData.cond, attrsPtr);
+
+ if (0 != iRet)
+ {
+ ERROR("Failed creating thread synchronization condition "
+ "[error=%d (%s)]\n", iRet, strerror(iRet));
+ if (EAGAIN == iRet && MaxUnavailableResourceRetries >= ++iEagains)
+ {
+ poll(NULL, 0, std::min(100,10*iEagains));
+ goto Cond_retry;
+ }
+ else if (ENOMEM == iRet)
+ {
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+ pthread_mutex_destroy(&m_tnwdNativeData.mutex);
+ goto IPrC_exit;
+ }
+
+ m_tnwdNativeData.fInitialized = true;
+
+ IPrC_exit:
+ if (attrsPtr != nullptr)
+ {
+ pthread_condattr_destroy(attrsPtr);
+ }
+ if (NO_ERROR != palErr)
+ {
+ m_tsThreadState = TS_FAILED;
+ }
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CThreadSynchronizationInfo::InitializePostCreate
+
+ Part of CThreadSynchronizationInfo's initialization to be carried out
+ after actual thread creation
+ --*/
+ PAL_ERROR CThreadSynchronizationInfo::InitializePostCreate(
+ CPalThread *pthrCurrent,
+ SIZE_T threadId,
+ DWORD dwLwpId)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+
+ if (TS_FAILED == m_tsThreadState)
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+
+ m_twiWaitInfo.pthrOwner = pthrCurrent;
+
+ return palErr;
+ }
+
+
+ /*++
+ Method:
+ CThreadSynchronizationInfo::AddObjectToOwnedList
+
+ Adds an object to the list of currently owned objects.
+ --*/
+ void CThreadSynchronizationInfo::AddObjectToOwnedList(POwnedObjectsListNode pooln)
+ {
+ InsertTailList(&m_leOwnedObjsList, &pooln->Link);
+ }
+
+ /*++
+ Method:
+ CThreadSynchronizationInfo::RemoveObjectFromOwnedList
+
+ Removes an object from the list of currently owned objects.
+ --*/
+ void CThreadSynchronizationInfo::RemoveObjectFromOwnedList(POwnedObjectsListNode pooln)
+ {
+ RemoveEntryList(&pooln->Link);
+ }
+
+ /*++
+ Method:
+ CThreadSynchronizationInfo::RemoveFirstObjectFromOwnedList
+
+ Removes the first object from the list of currently owned objects.
+ --*/
+ POwnedObjectsListNode CThreadSynchronizationInfo::RemoveFirstObjectFromOwnedList()
+ {
+ OwnedObjectsListNode * poolnItem;
+
+ if (IsListEmpty(&m_leOwnedObjsList))
+ {
+ poolnItem = NULL;
+ }
+ else
+ {
+ PLIST_ENTRY pLink = RemoveHeadList(&m_leOwnedObjsList);
+ poolnItem = CONTAINING_RECORD(pLink, OwnedObjectsListNode, Link);
+ }
+
+ return poolnItem;
+ }
+
+#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+
+ /*++
+ Method:
+ CThreadSynchronizationInfo::RunDeferredThreadConditionSignalings
+
+ Carries out all the pending condition signalings for the current thread.
+ --*/
+ PAL_ERROR CThreadSynchronizationInfo::RunDeferredThreadConditionSignalings()
+ {
+ PAL_ERROR palErr = NO_ERROR;
+
+ _ASSERTE(0 <= m_lPendingSignalingCount);
+
+ if (0 < m_lPendingSignalingCount)
+ {
+ LONG lArrayPendingSignalingCount = std::min(PendingSignalingsArraySize, m_lPendingSignalingCount);
+ LONG lIdx = 0;
+ PAL_ERROR palTempErr;
+
+ // Signal all the pending signalings from the array
+ for (lIdx = 0; lIdx < lArrayPendingSignalingCount; lIdx++)
+ {
+ // Do the actual signaling
+ palTempErr = CPalSynchronizationManager::SignalThreadCondition(
+ m_rgpthrPendingSignalings[lIdx]->synchronizationInfo.GetNativeData());
+ if (NO_ERROR != palTempErr)
+ {
+ palErr = palTempErr;
+ }
+
+ // Release the thread reference
+ m_rgpthrPendingSignalings[lIdx]->ReleaseThreadReference();
+ }
+
+ // Signal any pending signalings from the array overflow list
+ if (m_lPendingSignalingCount > PendingSignalingsArraySize)
+ {
+ PLIST_ENTRY pLink;
+ DeferredSignalingListNode * pdsln;
+
+ while (!IsListEmpty(&m_lePendingSignalingsOverflowList))
+ {
+ // Remove a node from the head of the queue
+ // Note: no need to synchronize the access to this list since
+ // it is meant to be accessed only by the owner thread.
+ pLink = RemoveHeadList(&m_lePendingSignalingsOverflowList);
+ pdsln = CONTAINING_RECORD(pLink,
+ DeferredSignalingListNode,
+ Link);
+
+ // Do the actual signaling
+ palTempErr = CPalSynchronizationManager::SignalThreadCondition(
+ pdsln->pthrTarget->synchronizationInfo.GetNativeData());
+ if (NO_ERROR != palTempErr)
+ {
+ palErr = palTempErr;
+ }
+
+ // Release the thread reference
+ pdsln->pthrTarget->ReleaseThreadReference();
+
+ // Delete the node
+ InternalDelete(pdsln);
+
+ lIdx += 1;
+ }
+
+ _ASSERTE(lIdx == m_lPendingSignalingCount);
+ }
+
+ // Reset the counter of pending signalings for this thread
+ m_lPendingSignalingCount = 0;
+ }
+
+ return palErr;
+ }
+
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+
+ /*++
+ Method:
+ CPalSynchronizationManager::HasProcessExited
+
+ Tests whether or not a process has exited
+ --*/
+ bool CPalSynchronizationManager::HasProcessExited(
+ DWORD dwPid,
+ DWORD * pdwExitCode,
+ bool * pfIsActualExitCode)
+ {
+ pid_t pidWaitRetval;
+ int iStatus;
+ bool fRet = false;
+
+ TRACE("Looking for status of process; trying wait()\n");
+
+ while(1)
+ {
+ /* try to get state of process, using non-blocking call */
+ pidWaitRetval = waitpid(dwPid, &iStatus, WNOHANG);
+
+ if ((DWORD)pidWaitRetval == dwPid)
+ {
+ /* success; get the exit code */
+ if (WIFEXITED(iStatus))
+ {
+ *pdwExitCode = WEXITSTATUS(iStatus);
+ *pfIsActualExitCode = true;
+ TRACE("Exit code was %d\n", *pdwExitCode);
+ }
+ else
+ {
+ WARN("Process terminated without exiting; can't get exit "
+ "code. Assuming EXIT_FAILURE.\n");
+ *pfIsActualExitCode = true;
+ *pdwExitCode = EXIT_FAILURE;
+ }
+
+ fRet = true;
+ }
+ else if (0 == pidWaitRetval)
+ {
+ // The process is still running.
+ TRACE("Process %#x is still active.\n", dwPid);
+ }
+ else
+ {
+ // A legitimate cause of failure is EINTR; if this happens we
+ // have to try again. A second legitimate cause is ECHILD, which
+ // happens if we're trying to retrieve the status of a currently-
+ // running process that isn't a child of this process.
+ if(EINTR == errno)
+ {
+ TRACE("waitpid() failed with EINTR; re-waiting\n");
+ continue;
+ }
+ else if (ECHILD == errno)
+ {
+ TRACE("waitpid() failed with ECHILD; calling kill instead\n");
+ if (kill(dwPid, 0) != 0)
+ {
+ if (ESRCH == errno)
+ {
+ WARN("kill() failed with ESRCH, i.e. target "
+ "process exited and it wasn't a child, "
+ "so can't get the exit code, assuming "
+ "it was 0.\n");
+ *pfIsActualExitCode = false;
+ *pdwExitCode = 0;
+ }
+ else
+ {
+ ERROR("kill(pid, 0) failed; errno is %d (%s)\n",
+ errno, strerror(errno));
+ *pfIsActualExitCode = false;
+ *pdwExitCode = EXIT_FAILURE;
+ }
+
+ fRet = true;
+ }
+ }
+ else
+ {
+ // Ignoring unexpected waitpid errno and assuming that
+ // the process is still running
+ ERROR("waitpid(pid=%u) failed with errno=%d (%s)\n",
+ dwPid, errno, strerror(errno));
+ }
+ }
+
+ // Break out of the loop in all cases except EINTR.
+ break;
+ }
+
+ return fRet;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::InterlockedAwaken
+
+ Tries to change the target wait status to 'active' in an interlocked fashion
+ --*/
+ bool CPalSynchronizationManager::InterlockedAwaken(
+ DWORD *pWaitState,
+ bool fAlertOnly)
+ {
+ DWORD dwPrevState;
+
+ dwPrevState = InterlockedCompareExchange((LONG *)pWaitState, TWS_ACTIVE, TWS_ALERTABLE);
+ if (TWS_ALERTABLE != dwPrevState)
+ {
+ if (fAlertOnly)
+ {
+ return false;
+ }
+
+ dwPrevState = InterlockedCompareExchange((LONG *)pWaitState, TWS_ACTIVE, TWS_WAITING);
+ if (TWS_WAITING == dwPrevState)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::GetAbsoluteTimeout
+
+ Converts a relative timeout to an absolute one.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::GetAbsoluteTimeout(DWORD dwTimeout, struct timespec * ptsAbsTmo, BOOL fPreferMonotonicClock)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ int iRet;
+
+#if HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
+ if (fPreferMonotonicClock)
+ {
+ iRet = clock_gettime(CLOCK_MONOTONIC, ptsAbsTmo);
+ }
+ else
+ {
+#endif
+#if HAVE_WORKING_CLOCK_GETTIME
+ // Not every platform implements a (working) clock_gettime
+ iRet = clock_gettime(CLOCK_REALTIME, ptsAbsTmo);
+#elif HAVE_WORKING_GETTIMEOFDAY
+ // Not every platform implements a (working) gettimeofday
+ struct timeval tv;
+ iRet = gettimeofday(&tv, NULL);
+ if (0 == iRet)
+ {
+ ptsAbsTmo->tv_sec = tv.tv_sec;
+ ptsAbsTmo->tv_nsec = tv.tv_usec * tccMicroSecondsToNanoSeconds;
+ }
+#else
+#ifdef DBI_COMPONENT_MONO
+ return ERROR_INTERNAL_ERROR;
+#else
+ #error "Don't know how to get hi-res current time on this platform"
+#endif
+#endif // HAVE_WORKING_CLOCK_GETTIME, HAVE_WORKING_GETTIMEOFDAY
+#if HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
+ }
+#endif
+ if (0 == iRet)
+ {
+ ptsAbsTmo->tv_sec += dwTimeout / tccSecondsToMillieSeconds;
+ ptsAbsTmo->tv_nsec += (dwTimeout % tccSecondsToMillieSeconds) * tccMillieSecondsToNanoSeconds;
+ while (ptsAbsTmo->tv_nsec >= tccSecondsToNanoSeconds)
+ {
+ ptsAbsTmo->tv_sec += 1;
+ ptsAbsTmo->tv_nsec -= tccSecondsToNanoSeconds;
+ }
+ }
+ else
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+
+ return palErr;
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+
+
+Module Name:
+
+ synchmanager.hpp
+
+Abstract:
+ Private header file for synchronization manager and
+ controllers implementation
+
+
+
+--*/
+#ifndef _SYNCHMANAGER_HPP_
+#define _SYNCHMANAGER_HPP_
+
+#include "pal/synchobjects.hpp"
+#include "pal/synchcache.hpp"
+#include "pal/cs.hpp"
+#include "pal/corunix.hpp"
+#include "pal/thread.hpp"
+#include "pal/procobj.hpp"
+#include "pal/init.h"
+#include "pal/process.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#if HAVE_KQUEUE
+#include <sys/event.h>
+#endif // HAVE_KQUEUE
+#include "pal/dbgmsg.h"
+
+#ifdef _DEBUG
+// #define SYNCH_OBJECT_VALIDATION
+// #define SYNCH_STATISTICS
+#endif
+
+#ifdef SYNCH_OBJECT_VALIDATION
+#define VALIDATEOBJECT(obj) ((obj)->ValidateObject())
+#else
+#define VALIDATEOBJECT(obj)
+#endif
+
+namespace CorUnix
+{
+ const DWORD WTLN_FLAG_OWNER_OBJECT_IS_SHARED = 1<<0;
+ const DWORD WTLN_FLAG_WAIT_ALL = 1<<1;
+ const DWORD WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS = 1<<2;
+
+#ifdef SYNCH_OBJECT_VALIDATION
+ const DWORD HeadSignature = 0x48454144;
+ const DWORD TailSignature = 0x5441494C;
+ const DWORD EmptySignature = 0xBAADF00D;
+#endif
+
+ enum THREAD_WAIT_STATE
+ {
+ TWS_ACTIVE,
+ TWS_WAITING,
+ TWS_ALERTABLE,
+ TWS_EARLYDEATH,
+ };
+
+ enum WaitCompletionState
+ {
+ WaitIsNotSatisfied,
+ WaitIsSatisfied,
+ WaitMayBeSatisfied
+ };
+
+ typedef union _SynchDataGenrPtr
+ {
+ SharedID shrid;
+ CSynchData * ptr;
+ } SynchDataGenrPtr;
+
+ typedef union _WTLNodeGenrPtr
+ {
+ SharedID shrid;
+ struct _WaitingThreadsListNode * ptr;
+ } WTLNodeGenrPtr;
+
+ typedef struct _WaitingThreadsListNode
+ {
+#ifdef SYNCH_OBJECT_VALIDATION
+ DWORD dwDebugHeadSignature;
+#endif
+ WTLNodeGenrPtr ptrNext;
+ WTLNodeGenrPtr ptrPrev;
+ SharedID shridSHRThis;
+
+ // Data
+ DWORD dwThreadId;
+ DWORD dwProcessId;
+ DWORD dwObjIndex;
+ DWORD dwFlags;
+
+ // Pointers to related objects
+ SharedID shridWaitingState;
+ SynchDataGenrPtr ptrOwnerObjSynchData;
+ struct _ThreadWaitInfo * ptwiWaitInfo; // valid only in the
+ // target process
+#ifdef SYNCH_OBJECT_VALIDATION
+ _WaitingThreadsListNode();
+ ~_WaitingThreadsListNode();
+ void ValidateObject(void);
+ void ValidateEmptyObject(void);
+ void InvalidateObject(void);
+
+ DWORD dwDebugTailSignature;
+#endif
+ } WaitingThreadsListNode;
+
+ typedef struct _DeferredSignalingListNode
+ {
+ LIST_ENTRY Link;
+ CPalThread * pthrTarget;
+ } DeferredSignalingListNode;
+
+ typedef struct _OwnedObjectsListNode
+ {
+ LIST_ENTRY Link;
+ CSynchData * pPalObjSynchData;
+ } OwnedObjectsListNode;
+
+ class CPalSynchronizationManager; // fwd declaration
+ class CProcProcessLocalData; // fwd declaration
+
+ class CSynchData
+ {
+#ifdef SYNCH_OBJECT_VALIDATION
+ DWORD m_dwDebugHeadSignature;
+#endif
+ // NB: For perforformance purposes this class is supposed
+ // to have no virtual methods, and no destructor.
+
+ WTLNodeGenrPtr m_ptrWTLHead;
+ WTLNodeGenrPtr m_ptrWTLTail;
+ ULONG m_ulcWaitingThreads;
+ SharedID m_shridThis;
+ ObjectDomain m_odObjectDomain;
+ PalObjectTypeId m_otiObjectTypeId;
+ LONG m_lRefCount;
+ LONG m_lSignalCount;
+
+ // Ownership data
+ LONG m_lOwnershipCount;
+ DWORD m_dwOwnerPid;
+ DWORD m_dwOwnerTid; // used only by remote processes
+ // (thread ids may be recycled)
+ CPalThread * m_pOwnerThread; // valid only on the target process
+ OwnedObjectsListNode * m_poolnOwnedObjectListNode;
+ bool m_fAbandoned;
+
+#ifdef SYNCH_STATISTICS
+ ULONG m_lStatWaitCount;
+ ULONG m_lStatContentionCount;
+#endif
+
+ public:
+ CSynchData()
+ : m_ulcWaitingThreads(0), m_shridThis(NULL), m_lRefCount(1),
+ m_lSignalCount(0), m_lOwnershipCount(0), m_dwOwnerPid(0),
+ m_dwOwnerTid(0), m_pOwnerThread(NULL),
+ m_poolnOwnedObjectListNode(NULL), m_fAbandoned(false)
+ {
+ // m_ptrWTLHead, m_ptrWTLTail, m_odObjectDomain
+ // and m_otiObjectTypeId are initialized by
+ // CPalSynchronizationManager::AllocateObjectSynchData
+#ifdef SYNCH_STATISTICS
+ m_lStatWaitCount = 0;
+ m_lStatContentionCount = 0;
+#endif
+#ifdef SYNCH_OBJECT_VALIDATION
+ ValidateEmptyObject();
+ m_dwDebugHeadSignature = HeadSignature;;
+ m_dwDebugTailSignature = TailSignature;
+#endif
+ }
+
+ LONG AddRef()
+ {
+ return InterlockedIncrement(&m_lRefCount);
+ }
+
+ LONG Release(CPalThread * pthrCurrent);
+
+ bool CanWaiterWaitWithoutBlocking(
+ CPalThread * pWaiterThread,
+ bool * pfAbandoned);
+
+ PAL_ERROR ReleaseWaiterWithoutBlocking(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget);
+
+ void WaiterEnqueue(WaitingThreadsListNode * pwtlnNewNode, bool fPrioritize);
+ void SharedWaiterEnqueue(SharedID shridNewNode, bool fPrioritize);
+
+ // Object Domain accessor methods
+ ObjectDomain GetObjectDomain(void)
+ {
+ return m_odObjectDomain;
+ }
+ void SetObjectDomain(ObjectDomain odObjectDomain)
+ {
+ m_odObjectDomain = odObjectDomain;
+ }
+
+ // Object Type accessor methods
+ CObjectType * GetObjectType(void)
+ {
+ return CObjectType::GetObjectTypeById(m_otiObjectTypeId);
+ }
+ PalObjectTypeId GetObjectTypeId(void)
+ {
+ return m_otiObjectTypeId;
+ }
+ void SetObjectType(CObjectType * pot)
+ {
+ m_otiObjectTypeId = pot->GetId();
+ }
+ void SetObjectType(PalObjectTypeId oti)
+ {
+ m_otiObjectTypeId = oti;
+ }
+
+ // Object shared 'this' pointer accessor methods
+ SharedID GetSharedThis (void)
+ {
+ return m_shridThis;
+ }
+ void SetSharedThis (SharedID shridThis)
+ {
+ m_shridThis = shridThis;
+ }
+
+ void Signal(
+ CPalThread * pthrCurrent,
+ LONG lSignalCount,
+ bool fWorkerThread);
+
+ bool ReleaseFirstWaiter(
+ CPalThread * pthrCurrent,
+ bool * pfDelegated,
+ bool fWorkerThread);
+
+ LONG ReleaseAllLocalWaiters(
+ CPalThread * pthrCurrent);
+
+ WaitCompletionState IsRestOfWaitAllSatisfied(
+ WaitingThreadsListNode * pwtlnNode);
+
+ // Object signal count accessor methods
+ LONG GetSignalCount(void)
+ {
+ _ASSERTE(m_lSignalCount >= 0);
+ return m_lSignalCount;
+ }
+ void SetSignalCount(LONG lSignalCount)
+ {
+ _ASSERTE(m_lSignalCount >= 0);
+ _ASSERTE(lSignalCount >= 0);
+ m_lSignalCount = lSignalCount;
+ }
+ LONG DecrementSignalCount(void)
+ {
+ _ASSERTE(m_lSignalCount > 0);
+ return --m_lSignalCount;
+ }
+
+ // Object ownership accessor methods
+ void SetOwner(CPalThread * pOwnerThread);
+ void ResetOwnership(void);
+ PAL_ERROR AssignOwnershipToThread(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget);
+ DWORD GetOwnerProcessID(void)
+ {
+ return m_dwOwnerPid;
+ }
+ DWORD GetOwnerThreadID(void)
+ {
+ return m_dwOwnerTid;
+ }
+ CPalThread * GetOwnerThread(void)
+ {
+ return m_pOwnerThread;
+ }
+ OwnedObjectsListNode * GetOwnershipListNode(void)
+ {
+ return m_poolnOwnedObjectListNode;
+ }
+ void SetOwnershipListNode(OwnedObjectsListNode * pooln)
+ {
+ m_poolnOwnedObjectListNode = pooln;
+ }
+
+ // Object ownership count accessor methods
+ LONG GetOwnershipCount(void)
+ {
+ return m_lOwnershipCount;
+ }
+ void SetOwnershipCount(LONG lOwnershipCount)
+ {
+ m_lOwnershipCount = lOwnershipCount;
+ }
+
+ // Object abandoned flag accessor methods
+ void SetAbandoned(bool fAbandoned)
+ { m_fAbandoned = fAbandoned; }
+ bool IsAbandoned(void) { return m_fAbandoned; }
+
+ void IncrementWaitingThreadCount(void)
+ {
+ m_ulcWaitingThreads += 1;
+ }
+ void DecrementWaitingThreadCount(void)
+ {
+ m_ulcWaitingThreads -= 1;
+ }
+ ULONG GetWaitingThreadCount(void)
+ {
+ return m_ulcWaitingThreads;
+ }
+
+
+#ifdef SYNCH_STATISTICS
+ void IncrementStatWaitCount(void)
+ {
+ m_lStatWaitCount++;
+ }
+ LONG GetStatWaitCount(void)
+ {
+ return m_lStatWaitCount;
+ }
+ void IncrementStatContentionCount(void)
+ {
+ m_lStatContentionCount++;
+ }
+ LONG GetStatContentionCount(void)
+ {
+ return m_lStatContentionCount;
+ }
+#endif
+ //
+ // Wating threads list access methods
+ //
+ WaitingThreadsListNode * GetWTLHeadPtr(void)
+ {
+ return m_ptrWTLHead.ptr;
+ }
+ WaitingThreadsListNode * GetWTLTailPtr(void)
+ {
+ return m_ptrWTLTail.ptr;
+ }
+ SharedID GetWTLHeadShmPtr(void)
+ {
+ return m_ptrWTLHead.shrid;
+ }
+ SharedID GetWTLTailShmPtr(void)
+ {
+ return m_ptrWTLTail.shrid;
+ }
+ void SetWTLHeadPtr(WaitingThreadsListNode * p)
+ {
+ m_ptrWTLHead.ptr = p;
+ }
+ void SetWTLTailPtr(WaitingThreadsListNode * p)
+ {
+ m_ptrWTLTail.ptr = p;
+ }
+ void SetWTLHeadShrPtr(SharedID shrid)
+ {
+ m_ptrWTLHead.shrid = shrid;
+ }
+ void SetWTLTailShrPtr(SharedID shrid)
+ {
+ m_ptrWTLTail.shrid = shrid;
+ }
+#ifdef SYNCH_OBJECT_VALIDATION
+ ~CSynchData();
+ void ValidateObject(bool fDestructor = false);
+ void ValidateEmptyObject(void);
+ void InvalidateObject(void);
+
+ DWORD m_dwDebugTailSignature;
+#endif
+ };
+
+
+ class CSynchControllerBase
+ {
+ friend class CPalSynchronizationManager;
+
+ // NB: For perforformance purposes this class is supposed
+ // to have no virtual methods, contructor and
+ // destructor
+ public:
+ enum ControllerType { WaitController, StateController };
+
+ protected:
+ CPalThread * m_pthrOwner;
+ ControllerType m_ctCtrlrType;
+ ObjectDomain m_odObjectDomain;
+ CObjectType * m_potObjectType;
+ CSynchData * m_psdSynchData;
+ WaitDomain m_wdWaitDomain;
+
+ PAL_ERROR Init(
+ CPalThread * pthrCurrent,
+ ControllerType ctCtrlrType,
+ ObjectDomain odObjectDomain,
+ CObjectType *potObjectType,
+ CSynchData * psdSynchData,
+ WaitDomain wdWaitDomain);
+
+ void Release(void);
+
+ void SetSynchData(CSynchData * psdSynchData)
+ {
+ m_psdSynchData = psdSynchData;
+ }
+ CSynchData * GetSynchData()
+ {
+ return m_psdSynchData;
+ }
+ };
+
+ class CSynchWaitController : public CSynchControllerBase,
+ public ISynchWaitController
+ {
+ // Per-object-type specific data
+ //
+ // Process (otiProcess)
+ IPalObject *m_pProcessObject; // process that owns m_pProcLocalData, this is stored without a reference
+ CProcProcessLocalData * m_pProcLocalData;
+
+ public:
+ CSynchWaitController() : m_pProcessObject(NULL), m_pProcLocalData(NULL) {}
+ virtual ~CSynchWaitController() = default;
+
+ //
+ // ISynchWaitController methods
+ //
+ virtual PAL_ERROR CanThreadWaitWithoutBlocking(
+ bool * pfCanWaitWithoutBlocking,
+ bool * pfAbandoned);
+
+ virtual PAL_ERROR ReleaseWaitingThreadWithoutBlocking(void);
+
+ virtual PAL_ERROR RegisterWaitingThread(
+ WaitType wtWaitType,
+ DWORD dwIndex,
+ bool fAlertable,
+ bool fPrioritize);
+
+ virtual void ReleaseController(void);
+
+ CProcProcessLocalData * GetProcessLocalData(void);
+
+ void SetProcessData(IPalObject* pProcessObject, CProcProcessLocalData * pProcLocalData);
+ };
+
+ class CSynchStateController : public CSynchControllerBase,
+ public ISynchStateController
+ {
+ public:
+ // NB: For perforformance purposes this class is supposed
+ // to have no constructor
+ virtual ~CSynchStateController() = default;
+
+ //
+ // ISynchStateController methods
+ //
+ virtual PAL_ERROR GetSignalCount(LONG *plSignalCount);
+ virtual PAL_ERROR SetSignalCount(LONG lNewCount);
+ virtual PAL_ERROR IncrementSignalCount(LONG lAmountToIncrement);
+ virtual PAL_ERROR DecrementSignalCount(LONG lAmountToDecrement);
+ virtual PAL_ERROR SetOwner(CPalThread *pNewOwningThread);
+ virtual PAL_ERROR DecrementOwnershipCount(void);
+ virtual void ReleaseController(void);
+ };
+
+ class CPalSynchronizationManager : public IPalSynchronizationManager
+ {
+ friend class CPalSynchMgrController;
+ template <class T> friend T *CorUnix::InternalNew();
+
+ public:
+ // types
+ typedef CSynchCache<CSynchWaitController> CSynchWaitControllerCache;
+ typedef CSynchCache<CSynchStateController> CSynchStateControllerCache;
+ typedef CSynchCache<CSynchData> CSynchDataCache;
+ typedef CSHRSynchCache<CSynchData> CSHRSynchDataCache;
+ typedef CSynchCache<WaitingThreadsListNode> CWaitingThreadsListNodeCache;
+ typedef CSHRSynchCache<WaitingThreadsListNode> CSHRWaitingThreadsListNodeCache;
+ typedef CSynchCache<OwnedObjectsListNode> COwnedObjectsListNodeCache;
+
+ private:
+ // types
+ enum InitStatus
+ {
+ SynchMgrStatusIdle,
+ SynchMgrStatusInitializing,
+ SynchMgrStatusRunning,
+ SynchMgrStatusShuttingDown,
+ SynchMgrStatusReadyForProcessShutDown,
+ SynchMgrStatusError
+ };
+ enum SynchWorkerCmd
+ {
+ SynchWorkerCmdNop,
+ SynchWorkerCmdRemoteSignal,
+ SynchWorkerCmdDelegatedObjectSignaling,
+ SynchWorkerCmdShutdown,
+ SynchWorkerCmdTerminationRequest,
+ SynchWorkerCmdLast
+ };
+
+ typedef struct _MonitoredProcessesListNode
+ {
+ struct _MonitoredProcessesListNode * pNext;
+ LONG lRefCount;
+ CSynchData * psdSynchData;
+ DWORD dwPid;
+ DWORD dwExitCode;
+ bool fIsActualExitCode;
+
+ // Object that owns pProcLocalData. This is stored, with a reference, to
+ // ensure that pProcLocalData is not deleted.
+ IPalObject *pProcessObject;
+ CProcProcessLocalData * pProcLocalData;
+ } MonitoredProcessesListNode;
+
+ // constants
+ static const int CtrlrsCacheMaxSize = 256;
+ static const int SynchDataCacheMaxSize = 256;
+ static const int WTListNodeCacheMaxSize = 256;
+ static const int OwnedObjectsListCacheMaxSize = 16;
+ static const int MaxWorkerConsecutiveEintrs = 128;
+ static const int MaxConsecutiveEagains = 128;
+ static const int WorkerThreadProcMonitoringTimeout = 250; // ms
+ static const int WorkerThreadShuttingDownTimeout = 1000; // ms
+ static const int WorkerCmdCompletionTimeout = 250; // ms
+ static const DWORD SecondNativeWaitTimeout = INFINITE;
+ static const DWORD WorkerThreadTerminationTimeout = 2000; // ms
+
+ // static members
+ static CPalSynchronizationManager * s_pObjSynchMgr;
+ static Volatile<LONG> s_lInitStatus;
+ static CRITICAL_SECTION s_csSynchProcessLock;
+ static CRITICAL_SECTION s_csMonitoredProcessesLock;
+
+ // members
+ DWORD m_dwWorkerThreadTid;
+ IPalObject * m_pipoThread;
+ CPalThread * m_pthrWorker;
+ int m_iProcessPipeRead;
+ int m_iProcessPipeWrite;
+#if HAVE_KQUEUE
+ int m_iKQueue;
+ struct kevent m_keProcessPipeEvent;
+#endif // HAVE_KQUEUE
+
+ MonitoredProcessesListNode * m_pmplnMonitoredProcesses;
+ LONG m_lMonitoredProcessesCount;
+ MonitoredProcessesListNode * m_pmplnExitedNodes;
+
+ // caches
+ CSynchWaitControllerCache m_cacheWaitCtrlrs;
+ CSynchStateControllerCache m_cacheStateCtrlrs;
+ CSynchDataCache m_cacheSynchData;
+ CSHRSynchDataCache m_cacheSHRSynchData;
+ CWaitingThreadsListNodeCache m_cacheWTListNodes;
+ CSHRWaitingThreadsListNodeCache m_cacheSHRWTListNodes;
+ COwnedObjectsListNodeCache m_cacheOwnedObjectsListNodes;
+
+ // static methods
+ static PAL_ERROR Initialize();
+ static DWORD PALAPI WorkerThread(LPVOID pArg);
+
+ protected:
+ CPalSynchronizationManager();
+
+ PAL_ERROR GetSynchControllersForObjects(
+ CPalThread *pthrCurrent,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ void ** ppvControllers,
+ CSynchControllerBase::ControllerType ctCtrlrType);
+
+ private:
+ static IPalSynchronizationManager * CreatePalSynchronizationManager();
+ static PAL_ERROR StartWorker(CPalThread * pthrCurrent);
+ static PAL_ERROR PrepareForShutdown(void);
+
+ public:
+ virtual ~CPalSynchronizationManager();
+
+ static CPalSynchronizationManager * GetInstance(void)
+ {
+ // No need here to check for NULL and in case create the
+ // singleton, since its creation is enforced by the PAL
+ // initialization code.
+ return s_pObjSynchMgr;
+ }
+
+ //
+ // Inline utility methods
+ //
+ static void AcquireLocalSynchLock(CPalThread * pthrCurrent)
+ {
+ _ASSERTE(0 <= pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount);
+
+ if (1 == ++pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount)
+ {
+ InternalEnterCriticalSection(pthrCurrent, &s_csSynchProcessLock);
+ }
+ }
+ static void ReleaseLocalSynchLock(CPalThread * pthrCurrent)
+ {
+ _ASSERTE(0 < pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount);
+ if (0 == --pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount)
+ {
+ InternalLeaveCriticalSection(pthrCurrent, &s_csSynchProcessLock);
+
+#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ pthrCurrent->synchronizationInfo.RunDeferredThreadConditionSignalings();
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ }
+ }
+ static LONG ResetLocalSynchLock(CPalThread * pthrCurrent)
+ {
+ LONG lRet = pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount;
+
+ _ASSERTE(0 <= lRet);
+ if (0 < lRet)
+ {
+ pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount = 0;
+ InternalLeaveCriticalSection(pthrCurrent, &s_csSynchProcessLock);
+
+#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ pthrCurrent->synchronizationInfo.RunDeferredThreadConditionSignalings();
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ }
+ return lRet;
+ }
+ static LONG GetLocalSynchLockCount(CPalThread * pthrCurrent)
+ {
+ _ASSERTE(0 <= pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount);
+ return pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount;
+ }
+
+ static void AcquireSharedSynchLock(CPalThread * pthrCurrent)
+ {
+ _ASSERTE(0 <= pthrCurrent->synchronizationInfo.m_lSharedSynchLockCount);
+ _ASSERT_MSG(0 < pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount,
+ "The local synch lock should be acquired before grabbing the "
+ "shared one.\n");
+ if (1 == ++pthrCurrent->synchronizationInfo.m_lSharedSynchLockCount)
+ {
+ SHMLock();
+ }
+ }
+ static void ReleaseSharedSynchLock(CPalThread * pthrCurrent)
+ {
+ _ASSERTE(0 < pthrCurrent->synchronizationInfo.m_lSharedSynchLockCount);
+ if (0 == --pthrCurrent->synchronizationInfo.m_lSharedSynchLockCount)
+ {
+ _ASSERT_MSG(0 < pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount,
+ "Final release of the shared synch lock while not holding the "
+ "local one. Local synch lock should always be acquired first and "
+ "released last.\n");
+ SHMRelease();
+ }
+ }
+ static LONG ResetSharedSynchLock(CPalThread * pthrCurrent)
+ {
+ LONG lRet = pthrCurrent->synchronizationInfo.m_lSharedSynchLockCount;
+
+ _ASSERTE(0 <= lRet);
+ _ASSERTE(0 == lRet ||
+ 0 < pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount);
+ if (0 < lRet)
+ {
+ pthrCurrent->synchronizationInfo.m_lSharedSynchLockCount = 0;
+ SHMRelease();
+ }
+ return lRet;
+ }
+ static LONG GetSharedSynchLockCount(CPalThread * pthrCurrent)
+ {
+ _ASSERTE(0 <= pthrCurrent->synchronizationInfo.m_lSharedSynchLockCount);
+ _ASSERTE(0 == pthrCurrent->synchronizationInfo.m_lSharedSynchLockCount ||
+ 0 < pthrCurrent->synchronizationInfo.m_lLocalSynchLockCount);
+ return pthrCurrent->synchronizationInfo.m_lSharedSynchLockCount;
+ }
+
+ CSynchWaitController * CacheGetWaitCtrlr(CPalThread * pthrCurrent)
+ {
+ return m_cacheWaitCtrlrs.Get(pthrCurrent);
+ }
+ int CacheGetWaitCtrlr(
+ CPalThread * pthrCurrent,
+ int n,
+ CSynchWaitController * prgCtrlrs[])
+ {
+ return m_cacheWaitCtrlrs.Get(pthrCurrent, n, prgCtrlrs);
+ }
+ void CacheAddWaitCtrlr(
+ CPalThread * pthrCurrent,
+ CSynchWaitController * pCtrlr)
+ {
+ m_cacheWaitCtrlrs.Add(pthrCurrent, pCtrlr);
+ }
+ CSynchStateController * CacheGetStateCtrlr(CPalThread * pthrCurrent)
+ {
+ return m_cacheStateCtrlrs.Get(pthrCurrent);
+ }
+ int CacheGetStateCtrlr(
+ CPalThread * pthrCurrent,
+ int n,
+ CSynchStateController * prgCtrlrs[])
+ {
+ return m_cacheStateCtrlrs.Get(pthrCurrent, n, prgCtrlrs);
+ }
+ void CacheAddStateCtrlr(
+ CPalThread * pthrCurrent,
+ CSynchStateController * pCtrlr)
+ {
+ m_cacheStateCtrlrs.Add(pthrCurrent, pCtrlr);
+ }
+
+ CSynchData * CacheGetLocalSynchData(CPalThread * pthrCurrent)
+ {
+ return m_cacheSynchData.Get(pthrCurrent);
+ }
+ void CacheAddLocalSynchData(
+ CPalThread * pthrCurrent,
+ CSynchData * psdSynchData)
+ {
+ m_cacheSynchData.Add(pthrCurrent, psdSynchData);
+ }
+ SharedID CacheGetSharedSynchData(CPalThread * pthrCurrent)
+ {
+ return m_cacheSHRSynchData.Get(pthrCurrent);
+ }
+ void CacheAddSharedSynchData(
+ CPalThread * pthrCurrent,
+ SharedID shridSData)
+ {
+ m_cacheSHRSynchData.Add(pthrCurrent, shridSData);
+ }
+
+ WaitingThreadsListNode * CacheGetLocalWTListNode(
+ CPalThread * pthrCurrent)
+ {
+ return m_cacheWTListNodes.Get(pthrCurrent);
+ }
+ void CacheAddLocalWTListNode(
+ CPalThread * pthrCurrent,
+ WaitingThreadsListNode * pWTLNode)
+ {
+ m_cacheWTListNodes.Add(pthrCurrent, pWTLNode);
+ }
+ SharedID CacheGetSharedWTListNode(CPalThread * pthrCurrent)
+ {
+ return m_cacheSHRWTListNodes.Get(pthrCurrent);
+ }
+ void CacheAddSharedWTListNode(
+ CPalThread * pthrCurrent,
+ SharedID shridWTLNode)
+ {
+ m_cacheSHRWTListNodes.Add(pthrCurrent, shridWTLNode);
+ }
+
+ OwnedObjectsListNode * CacheGetOwnedObjsListNode(
+ CPalThread * pthrCurrent)
+ {
+ return m_cacheOwnedObjectsListNodes.Get(pthrCurrent);
+ }
+ void CacheAddOwnedObjsListNode(
+ CPalThread * pthrCurrent,
+ OwnedObjectsListNode * pNode)
+ {
+ m_cacheOwnedObjectsListNodes.Add(pthrCurrent, pNode);
+ }
+
+
+ //
+ // IPalSynchronizationManager methods
+ //
+ virtual PAL_ERROR BlockThread(
+ CPalThread *pthrCurrent,
+ DWORD dwTimeout,
+ bool fAlertable,
+ bool fIsSleep,
+ ThreadWakeupReason *ptwrWakeupReason,
+ DWORD *pdwSignaledObject);
+
+ virtual PAL_ERROR AbandonObjectsOwnedByThread(
+ CPalThread *pthrCurrent,
+ CPalThread *pthrTarget);
+
+ virtual PAL_ERROR GetSynchWaitControllersForObjects(
+ CPalThread *pthrCurrent,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ ISynchWaitController *rgControllers[]);
+
+ virtual PAL_ERROR GetSynchStateControllersForObjects(
+ CPalThread *pthrCurrent,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ ISynchStateController *rgControllers[]);
+
+ virtual PAL_ERROR AllocateObjectSynchData(
+ CObjectType *potObjectType,
+ ObjectDomain odObjectDomain,
+ VOID **ppvSynchData);
+
+ virtual void FreeObjectSynchData(
+ CObjectType *potObjectType,
+ ObjectDomain odObjectDomain,
+ VOID *pvSynchData);
+
+ virtual PAL_ERROR PromoteObjectSynchData(
+ CPalThread *pthrCurrent,
+ VOID *pvLocalSynchData,
+ VOID **ppvSharedSynchData);
+
+ virtual PAL_ERROR CreateSynchStateController(
+ CPalThread *pthrCurrent,
+ CObjectType *potObjectType,
+ VOID *pvSynchData,
+ ObjectDomain odObjectDomain,
+ ISynchStateController **ppStateController);
+
+ virtual PAL_ERROR CreateSynchWaitController(
+ CPalThread *pthrCurrent,
+ CObjectType *potObjectType,
+ VOID *pvSynchData,
+ ObjectDomain odObjectDomain,
+ ISynchWaitController **ppWaitController);
+
+ virtual void AcquireProcessLock(CPalThread *pthrCurrent);
+
+ virtual void ReleaseProcessLock(CPalThread *pthrCurrent);
+
+ //
+ // Static helper methods
+ //
+ public:
+ static PAL_ERROR WakeUpLocalThread(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget,
+ ThreadWakeupReason twrWakeupReason,
+ DWORD dwObjectIndex);
+
+ static PAL_ERROR SignalThreadCondition(
+ ThreadNativeWaitData * ptnwdNativeWaitData);
+
+ static PAL_ERROR DeferThreadConditionSignaling(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget);
+
+ static PAL_ERROR WakeUpRemoteThread(
+ SharedID shridWLNode);
+
+ static PAL_ERROR DelegateSignalingToRemoteProcess(
+ CPalThread * pthrCurrent,
+ DWORD dwTargetProcessId,
+ SharedID shridSynchData);
+
+ static PAL_ERROR SendMsgToRemoteWorker(
+ DWORD dwProcessId,
+ BYTE * pMsg,
+ int iMsgSize);
+
+ static ThreadWaitInfo * GetThreadWaitInfo(
+ CPalThread * pthrCurrent);
+
+ //
+ // The following methods must be called only by a Sync*Controller or
+ // while holding the required synchronization global locks
+ //
+ static void UnsignalRestOfLocalAwakeningWaitAll(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget,
+ WaitingThreadsListNode * pwtlnNode,
+ CSynchData * psdTgtObjectSynchData);
+
+ static void MarkWaitForDelegatedObjectSignalingInProgress(
+ CPalThread * pthrCurrent,
+ WaitingThreadsListNode * pwtlnNode);
+
+ static void UnmarkTWListForDelegatedObjectSignalingInProgress(
+ CSynchData * pTgtObjectSynchData);
+
+ static PAL_ERROR ThreadNativeWait(
+ ThreadNativeWaitData * ptnwdNativeWaitData,
+ DWORD dwTimeout,
+ ThreadWakeupReason * ptwrWakeupReason,
+ DWORD * pdwSignaledObject);
+
+ static void ThreadPrepareForShutdown(void);
+
+#ifndef CORECLR
+ static bool GetProcessPipeName(
+ LPSTR pDest,
+ int iDestSize,
+ DWORD dwPid);
+#endif // !CORECLR
+
+ //
+ // Non-static helper methods
+ //
+ private:
+ LONG DoMonitorProcesses(CPalThread * pthrCurrent);
+
+ void DiscardMonitoredProcesses(CPalThread * pthrCurrent);
+
+ PAL_ERROR ReadCmdFromProcessPipe(
+ int iPollTimeout,
+ SynchWorkerCmd * pswcWorkerCmd,
+ SharedID * pshridMarshaledData,
+ DWORD * pdwData);
+
+ PAL_ERROR WakeUpLocalWorkerThread(
+ SynchWorkerCmd swcWorkerCmd);
+
+ int ReadBytesFromProcessPipe(
+ int iTimeout,
+ BYTE * pRecvBuf,
+ LONG lBytes);
+
+ bool CreateProcessPipe();
+
+ PAL_ERROR ShutdownProcessPipe();
+
+ public:
+ //
+ // The following methods must be called only by a Sync*Controller or
+ // while holding the required synchronization global locks
+ //
+ void UnRegisterWait(
+ CPalThread * pthrCurrent,
+ ThreadWaitInfo * ptwiWaitInfo,
+ bool fHaveSharedLock);
+
+ PAL_ERROR RegisterProcessForMonitoring(
+ CPalThread * pthrCurrent,
+ CSynchData *psdSynchData,
+ IPalObject *pProcessObject,
+ CProcProcessLocalData * pProcLocalData);
+
+ PAL_ERROR UnRegisterProcessForMonitoring(
+ CPalThread * pthrCurrent,
+ CSynchData *psdSynchData,
+ DWORD dwPid);
+
+ //
+ // Utility static methods, no lock required
+ //
+ static bool HasProcessExited(
+ DWORD dwPid,
+ DWORD * pdwExitCode,
+ bool * pfIsActualExitCode);
+
+ static bool InterlockedAwaken(
+ DWORD *pWaitState,
+ bool fAlertOnly);
+
+ static PAL_ERROR GetAbsoluteTimeout(
+ DWORD dwTimeout,
+ struct timespec * ptsAbsTmo,
+ BOOL fPreferMonotonicClock);
+ };
+}
+
+#endif // _SYNCHMANAGER_HPP_
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+
+
+Module Name:
+
+ wait.cpp
+
+Abstract:
+
+ Implementation of waiting functions as described in
+ the WIN32 API
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/synchobjects.hpp"
+#include "pal/handlemgr.hpp"
+#include "pal/mutex.hpp"
+#include "pal/malloc.hpp"
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(SYNC);
+
+#define MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE (MAXIMUM_WAIT_OBJECTS / 4)
+
+using namespace CorUnix;
+
+static PalObjectTypeId sg_rgWaitObjectsIds[] =
+ {
+ otiProcess,
+ otiThread
+ };
+static CAllowedObjectTypes sg_aotWaitObject(sg_rgWaitObjectsIds,
+ sizeof(sg_rgWaitObjectsIds)/sizeof(sg_rgWaitObjectsIds[0]));
+
+/*++
+Function:
+ WaitForSingleObject
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+WaitForSingleObject(IN HANDLE hHandle,
+ IN DWORD dwMilliseconds)
+{
+ DWORD dwRet;
+
+ PERF_ENTRY(WaitForSingleObject);
+ ENTRY("WaitForSingleObject(hHandle=%p, dwMilliseconds=%u)\n",
+ hHandle, dwMilliseconds);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE,
+ dwMilliseconds, FALSE);
+
+ LOGEXIT("WaitForSingleObject returns DWORD %u\n", dwRet);
+ PERF_EXIT(WaitForSingleObject);
+ return dwRet;
+}
+
+DWORD CorUnix::InternalWaitForMultipleObjectsEx(
+ CPalThread * pThread,
+ DWORD nCount,
+ CONST HANDLE *lpHandles,
+ BOOL bWaitAll,
+ DWORD dwMilliseconds,
+ BOOL bAlertable,
+ BOOL bPrioritize)
+{
+ DWORD dwRet = WAIT_FAILED;
+ PAL_ERROR palErr = NO_ERROR;
+ int i, iSignaledObjCount, iSignaledObjIndex = -1;
+ bool fWAll = (bool)bWaitAll, fNeedToBlock = false;
+ bool fAbandoned = false;
+ WaitType wtWaitType;
+
+ IPalObject * pIPalObjStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL };
+ ISynchWaitController * pISyncStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL };
+ IPalObject ** ppIPalObjs = pIPalObjStackArray;
+ ISynchWaitController ** ppISyncWaitCtrlrs = pISyncStackArray;
+
+ if ((nCount == 0) || (nCount > MAXIMUM_WAIT_OBJECTS))
+ {
+ ppIPalObjs = NULL; // make delete at the end safe
+ ppISyncWaitCtrlrs = NULL; // make delete at the end safe
+ ERROR("Invalid object count=%d [range: 1 to %d]\n",
+ nCount, MAXIMUM_WAIT_OBJECTS)
+ pThread->SetLastError(ERROR_INVALID_PARAMETER);
+ goto WFMOExIntExit;
+ }
+ else if (nCount == 1)
+ {
+ fWAll = false; // makes no difference when nCount is 1
+ wtWaitType = SingleObject;
+ }
+ else
+ {
+ wtWaitType = fWAll ? MultipleObjectsWaitAll : MultipleObjectsWaitOne;
+ if (nCount > MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE)
+ {
+ ppIPalObjs = InternalNewArray<IPalObject*>(nCount);
+ ppISyncWaitCtrlrs = InternalNewArray<ISynchWaitController*>(nCount);
+ if ((NULL == ppIPalObjs) || (NULL == ppISyncWaitCtrlrs))
+ {
+ ERROR("Out of memory allocating internal structures\n");
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto WFMOExIntExit;
+ }
+ }
+ }
+
+ palErr = g_pObjectManager->ReferenceMultipleObjectsByHandleArray(pThread,
+ (VOID **)lpHandles,
+ nCount,
+ &sg_aotWaitObject,
+ ppIPalObjs);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Unable to obtain object for some or all of the handles [error=%u]\n",
+ palErr);
+ if (palErr == ERROR_INVALID_HANDLE)
+ pThread->SetLastError(ERROR_INVALID_HANDLE);
+ else
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto WFMOExIntExit;
+ }
+
+ if (nCount > 1)
+ {
+ ERROR("Attempt to wait for any or all handles including a cross-process sync object", ERROR_NOT_SUPPORTED);
+ pThread->SetLastError(ERROR_NOT_SUPPORTED);
+ goto WFMOExIntCleanup;
+ }
+
+ if (fWAll)
+ {
+ // For a wait-all operation, check for duplicate wait objects in the array. This just uses a brute-force O(n^2)
+ // algorithm, but since MAXIMUM_WAIT_OBJECTS is small, the worst case is not so bad, and the average case would involve
+ // significantly fewer items.
+ for (DWORD i = 0; i < nCount - 1; ++i)
+ {
+ IPalObject *const objectToCheck = ppIPalObjs[i];
+ for (DWORD j = i + 1; j < nCount; ++j)
+ {
+ if (ppIPalObjs[j] == objectToCheck)
+ {
+ ERROR("Duplicate handle provided for a wait-all operation [error=%u]\n", ERROR_INVALID_PARAMETER);
+ pThread->SetLastError(ERROR_INVALID_PARAMETER);
+ goto WFMOExIntCleanup;
+ }
+ }
+ }
+ }
+
+ palErr = g_pSynchronizationManager->GetSynchWaitControllersForObjects(
+ pThread, ppIPalObjs, nCount, ppISyncWaitCtrlrs);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Unable to obtain ISynchWaitController interface for some or all "
+ "of the objects [error=%u]\n", palErr);
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto WFMOExIntCleanup;
+ }
+
+ if (bAlertable)
+ {
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ dwRet = WAIT_FAILED;
+ goto WFMOExIntCleanup;
+ }
+
+ iSignaledObjCount = 0;
+ iSignaledObjIndex = -1;
+ for (i=0;i<(int)nCount;i++)
+ {
+ bool fValue, fWaitObjectAbandoned = false;
+ palErr = ppISyncWaitCtrlrs[i]->CanThreadWaitWithoutBlocking(&fValue, &fWaitObjectAbandoned);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("ISynchWaitController::CanThreadWaitWithoutBlocking() failed for "
+ "%d-th object [handle=%p error=%u]\n", i, lpHandles[i], palErr);
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto WFMOExIntReleaseControllers;
+ }
+ if (fWaitObjectAbandoned)
+ {
+ fAbandoned = true;
+ }
+ if (fValue)
+ {
+ iSignaledObjCount++;
+ iSignaledObjIndex = i;
+ if (!fWAll)
+ break;
+ }
+ }
+
+ fNeedToBlock = (iSignaledObjCount == 0) || (fWAll && (iSignaledObjCount < (int)nCount));
+ if (!fNeedToBlock)
+ {
+ // At least one object signaled, or bWaitAll==TRUE and all object signaled.
+ // No need to wait, let's unsignal the object(s) and return without blocking
+ int iStartIdx, iEndIdx;
+
+ if (fWAll)
+ {
+ iStartIdx = 0;
+ iEndIdx = nCount;
+ }
+ else
+ {
+ iStartIdx = iSignaledObjIndex;
+ iEndIdx = iStartIdx + 1;
+ }
+
+ // Unsignal objects
+ if( iStartIdx < 0 )
+ {
+ ERROR("Buffer underflow due to iStartIdx < 0");
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ dwRet = WAIT_FAILED;
+ goto WFMOExIntCleanup;
+ }
+ for (i = iStartIdx; i < iEndIdx; i++)
+ {
+ palErr = ppISyncWaitCtrlrs[i]->ReleaseWaitingThreadWithoutBlocking();
+ if (NO_ERROR != palErr)
+ {
+ ERROR("ReleaseWaitingThreadWithoutBlocking() failed for %d-th "
+ "object [handle=%p error=%u]\n",
+ i, lpHandles[i], palErr);
+ pThread->SetLastError(palErr);
+ goto WFMOExIntReleaseControllers;
+ }
+ }
+
+ dwRet = (fAbandoned ? WAIT_ABANDONED_0 : WAIT_OBJECT_0);
+ }
+ else if (0 == dwMilliseconds)
+ {
+ // Not enough objects signaled, but timeout is zero: no actual wait
+ dwRet = WAIT_TIMEOUT;
+ fNeedToBlock = false;
+ }
+ else
+ {
+ // Register the thread for waiting on all objects
+ for (i=0;i<(int)nCount;i++)
+ {
+ palErr = ppISyncWaitCtrlrs[i]->RegisterWaitingThread(
+ wtWaitType,
+ i,
+ (TRUE == bAlertable),
+ bPrioritize != FALSE);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("RegisterWaitingThread() failed for %d-th object "
+ "[handle=%p error=%u]\n", i, lpHandles[i], palErr);
+ pThread->SetLastError(palErr);
+ goto WFMOExIntReleaseControllers;
+ }
+ }
+ }
+
+WFMOExIntReleaseControllers:
+ // Release all controllers before going to sleep
+ for (i = 0; i < (int)nCount; i++)
+ {
+ ppISyncWaitCtrlrs[i]->ReleaseController();
+ ppISyncWaitCtrlrs[i] = NULL;
+ }
+ if (NO_ERROR != palErr)
+ goto WFMOExIntCleanup;
+
+ if (fNeedToBlock)
+ {
+ ThreadWakeupReason twrWakeupReason;
+
+ //
+ // Going to sleep
+ //
+ palErr = g_pSynchronizationManager->BlockThread(pThread,
+ dwMilliseconds,
+ (TRUE == bAlertable),
+ false,
+ &twrWakeupReason,
+ (DWORD *)&iSignaledObjIndex);
+ //
+ // Awakened
+ //
+ if (NO_ERROR != palErr)
+ {
+ ERROR("IPalSynchronizationManager::BlockThread failed for thread "
+ "pThread=%p [error=%u]\n", pThread, palErr);
+ pThread->SetLastError(palErr);
+ goto WFMOExIntCleanup;
+ }
+ switch (twrWakeupReason)
+ {
+ case WaitSucceeded:
+ dwRet = WAIT_OBJECT_0; // offset added later
+ break;
+ case MutexAbondoned:
+ dwRet = WAIT_ABANDONED_0; // offset added later
+ break;
+ case WaitTimeout:
+ dwRet = WAIT_TIMEOUT;
+ break;
+ case WaitFailed:
+ default:
+ ERROR("Thread %p awakened with some failure\n", pThread);
+ dwRet = WAIT_FAILED;
+ break;
+ }
+ }
+
+ if (!fWAll && ((WAIT_OBJECT_0 == dwRet) || (WAIT_ABANDONED_0 == dwRet)))
+ {
+ _ASSERT_MSG(0 <= iSignaledObjIndex,
+ "Failed to identify signaled/abandoned object\n");
+ _ASSERT_MSG(iSignaledObjIndex >= 0 && nCount > static_cast<DWORD>(iSignaledObjIndex),
+ "SignaledObjIndex object out of range "
+ "[index=%d obj_count=%u\n",
+ iSignaledObjCount, nCount);
+
+ if (iSignaledObjIndex < 0)
+ {
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ dwRet = WAIT_FAILED;
+ goto WFMOExIntCleanup;
+ }
+ dwRet += iSignaledObjIndex;
+ }
+
+WFMOExIntCleanup:
+ for (i = 0; i < (int)nCount; i++)
+ {
+ ppIPalObjs[i]->ReleaseReference(pThread);
+ ppIPalObjs[i] = NULL;
+ }
+
+WFMOExIntExit:
+ if (nCount > MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE)
+ {
+ InternalDeleteArray(ppIPalObjs);
+ InternalDeleteArray(ppISyncWaitCtrlrs);
+ }
+
+ return dwRet;
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+Module Name:
+
+ mutex.cpp
+
+Abstract:
+
+ Implementation of mutex synchroniztion object as described in
+ the WIN32 API
+
+Revision History:
+
+--*/
+
+#include "pal/mutex.hpp"
+
+/* Basic spinlock implementation */
+void SPINLOCKAcquire (LONG * lock, unsigned int flags)
+{
+ size_t loop_seed = 1, loop_count = 0;
+
+ if (flags & SYNCSPINLOCK_F_ASYMMETRIC)
+ {
+ loop_seed = ((size_t)pthread_self() % 10) + 1;
+ }
+ while (InterlockedCompareExchange(lock, 1, 0))
+ {
+ if (!(flags & SYNCSPINLOCK_F_ASYMMETRIC) || (++loop_count % loop_seed))
+ {
+#if PAL_IGNORE_NORMAL_THREAD_PRIORITY
+ struct timespec tsSleepTime;
+ tsSleepTime.tv_sec = 0;
+ tsSleepTime.tv_nsec = 1;
+ nanosleep(&tsSleepTime, NULL);
+#else
+ sched_yield();
+#endif
+ }
+ }
+
+}
+
+void SPINLOCKRelease (LONG * lock)
+{
+ VolatileStore(lock, 0);
+}
+
+DWORD SPINLOCKTryAcquire (LONG * lock)
+{
+ return InterlockedCompareExchange(lock, 1, 0);
+ // only returns 0 or 1.
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+Module Name:
+
+ process.cpp
+
+Abstract:
+
+ Implementation of process object and functions related to processes.
+
+--*/
+
+#include "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so do this first
+
+#include "pal/procobj.hpp"
+#include "pal/thread.hpp"
+#include "pal/file.hpp"
+#include "pal/handlemgr.hpp"
+#include "pal/module.h"
+#include "procprivate.hpp"
+#include "pal/palinternal.h"
+#include "pal/process.h"
+#include "pal/init.h"
+#include "pal/critsect.h"
+#include "pal/debug.h"
+#include "pal/utils.h"
+#include "pal/environ.h"
+#include "pal/virtual.h"
+#include "pal/stackstring.hpp"
+
+#include <errno.h>
+#if HAVE_POLL
+#include <poll.h>
+#else
+#include "pal/fakepoll.h"
+#endif // HAVE_POLL
+
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#if HAVE_PRCTL_H
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#endif
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <debugmacrosext.h>
+#include <semaphore.h>
+#include <stdint.h>
+#include <dlfcn.h>
+#include <limits.h>
+#include <vector>
+
+#ifdef __linux__
+#include <sys/syscall.h> // __NR_membarrier
+// Ensure __NR_membarrier is defined for portable builds.
+# if !defined(__NR_membarrier)
+# if defined(__amd64__)
+# define __NR_membarrier 324
+# elif defined(__i386__)
+# define __NR_membarrier 375
+# elif defined(__arm__)
+# define __NR_membarrier 389
+# elif defined(__aarch64__)
+# define __NR_membarrier 283
+# else
+# error Unknown architecture
+# endif
+# endif
+#endif
+
+#ifdef __APPLE__
+#include <libproc.h>
+#include <sys/sysctl.h>
+#include <sys/posix_sem.h>
+#include <mach/task.h>
+#include <mach/vm_map.h>
+extern "C"
+{
+ #include <mach/thread_state.h>
+}
+#endif // __APPLE__
+
+#ifdef __NetBSD__
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <kvm.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#endif
+
+extern char *g_szCoreCLRPath;
+
+using namespace CorUnix;
+
+CObjectType CorUnix::otProcess(
+ otiProcess,
+ NULL, // No cleanup routine
+ NULL, // No initialization routine
+ 0, // No immutable data
+ NULL, // No immutable data copy routine
+ NULL, // No immutable data cleanup routine
+ sizeof(CProcProcessLocalData),
+ NULL, // No process local data cleanup routine
+ 0, // No shared data
+ PROCESS_ALL_ACCESS,
+ CObjectType::SecuritySupported,
+ CObjectType::SecurityInfoNotPersisted,
+ CObjectType::UnnamedObject,
+ CObjectType::CrossProcessDuplicationAllowed,
+ CObjectType::WaitableObject,
+ CObjectType::SingleTransitionObject,
+ CObjectType::ThreadReleaseHasNoSideEffects,
+ CObjectType::NoOwner
+ );
+
+//
+// Helper membarrier function
+//
+#ifdef __NR_membarrier
+# define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__)
+#else
+# define membarrier(...) -ENOSYS
+#endif
+
+enum membarrier_cmd
+{
+ MEMBARRIER_CMD_QUERY = 0,
+ MEMBARRIER_CMD_GLOBAL = (1 << 0),
+ MEMBARRIER_CMD_GLOBAL_EXPEDITED = (1 << 1),
+ MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED = (1 << 2),
+ MEMBARRIER_CMD_PRIVATE_EXPEDITED = (1 << 3),
+ MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED = (1 << 4),
+ MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 5),
+ MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 6)
+};
+
+//
+// Tracks if the OS supports FlushProcessWriteBuffers using membarrier
+//
+static int s_flushUsingMemBarrier = 0;
+
+//
+// Helper memory page used by the FlushProcessWriteBuffers
+//
+static int* s_helperPage = 0;
+
+//
+// Mutex to make the FlushProcessWriteBuffersMutex thread safe
+//
+pthread_mutex_t flushProcessWriteBuffersMutex;
+
+CAllowedObjectTypes aotProcess(otiProcess);
+
+//
+// The representative IPalObject for this process
+//
+IPalObject* CorUnix::g_pobjProcess;
+
+//
+// Critical section that protects process data (e.g., the
+// list of active threads)/
+//
+CRITICAL_SECTION g_csProcess;
+
+//
+// List and count of active threads
+//
+CPalThread* CorUnix::pGThreadList;
+DWORD g_dwThreadCount;
+
+//
+// The command line and app name for the process
+//
+LPWSTR g_lpwstrCmdLine = NULL;
+LPWSTR g_lpwstrAppDir = NULL;
+
+// Thread ID of thread that has started the ExitProcess process
+Volatile<LONG> terminator = 0;
+
+// Process and session ID of this process.
+DWORD gPID = (DWORD) -1;
+DWORD gSID = (DWORD) -1;
+
+// The lowest common supported semaphore length, including null character
+// NetBSD-7.99.25: 15 characters
+// MacOSX 10.11: 31 -- Core 1.0 RC2 compatibility
+#if defined(__NetBSD__)
+#define CLR_SEM_MAX_NAMELEN 15
+#elif defined(__APPLE__)
+#define CLR_SEM_MAX_NAMELEN PSEMNAMLEN
+#elif defined(NAME_MAX)
+#define CLR_SEM_MAX_NAMELEN (NAME_MAX - 4)
+#else
+// On Solaris, MAXNAMLEN is 512, which is higher than MAX_PATH defined by pal.h
+#define CLR_SEM_MAX_NAMELEN MAX_PATH
+#endif
+
+static_assert_no_msg(CLR_SEM_MAX_NAMELEN <= MAX_PATH);
+
+//
+// Key used for associating CPalThread's with the underlying pthread
+// (through pthread_setspecific)
+//
+pthread_key_t CorUnix::thObjKey;
+
+static WCHAR W16_WHITESPACE[]= {0x0020, 0x0009, 0x000D, 0};
+static WCHAR W16_WHITESPACE_DQUOTE[]= {0x0020, 0x0009, 0x000D, '"', 0};
+
+enum FILETYPE
+{
+ FILE_ERROR,/*ERROR*/
+ FILE_UNIX, /*Unix Executable*/
+ FILE_DIR /*Directory*/
+};
+
+#pragma pack(push,1)
+// When creating the semaphore name on Mac running in a sandbox, We reference this structure as a byte array
+// in order to encode its data into a string. Its important to make sure there is no padding between the fields
+// and also at the end of the buffer. Hence, this structure is defined inside a pack(1)
+struct UnambiguousProcessDescriptor
+{
+ UnambiguousProcessDescriptor()
+ {
+ }
+
+ UnambiguousProcessDescriptor(DWORD processId, UINT64 disambiguationKey)
+ {
+ Init(processId, disambiguationKey);
+ }
+
+ void Init(DWORD processId, UINT64 disambiguationKey)
+ {
+ m_processId = processId;
+ m_disambiguationKey = disambiguationKey;
+ }
+ UINT64 m_disambiguationKey;
+ DWORD m_processId;
+};
+#pragma pack(pop)
+
+static
+DWORD
+StartupHelperThread(
+ LPVOID p);
+
+static
+BOOL
+GetProcessIdDisambiguationKey(
+ IN DWORD processId,
+ OUT UINT64 *disambiguationKey);
+
+static
+void
+CreateSemaphoreName(
+ char semName[CLR_SEM_MAX_NAMELEN],
+ LPCSTR semaphoreName,
+ const UnambiguousProcessDescriptor& unambiguousProcessDescriptor,
+ LPCSTR applicationGroupId);
+
+static BOOL getFileName(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, PathCharString& lpFileName);
+static char ** buildArgv(LPCWSTR lpCommandLine, PathCharString& lpAppPath, UINT *pnArg);
+static BOOL getPath(PathCharString& lpFileName, PathCharString& lpPathFileName);
+static int checkFileType(LPCSTR lpFileName);
+static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUnconditionally);
+
+ProcessModules *GetProcessModulesFromHandle(IN HANDLE hProcess, OUT LPDWORD lpCount);
+ProcessModules *CreateProcessModules(IN DWORD dwProcessId, OUT LPDWORD lpCount);
+void DestroyProcessModules(IN ProcessModules *listHead);
+
+/*++
+Function:
+ GetCurrentProcessId
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+GetCurrentProcessId(
+ VOID)
+{
+ PERF_ENTRY(GetCurrentProcessId);
+ ENTRY("GetCurrentProcessId()\n" );
+
+ LOGEXIT("GetCurrentProcessId returns DWORD %#x\n", gPID);
+ PERF_EXIT(GetCurrentProcessId);
+ return gPID;
+}
+
+
+/*++
+Function:
+ GetCurrentSessionId
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+GetCurrentSessionId(
+ VOID)
+{
+ PERF_ENTRY(GetCurrentSessionId);
+ ENTRY("GetCurrentSessionId()\n" );
+
+ LOGEXIT("GetCurrentSessionId returns DWORD %#x\n", gSID);
+ PERF_EXIT(GetCurrentSessionId);
+ return gSID;
+}
+
+
+/*++
+Function:
+ GetCurrentProcess
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+GetCurrentProcess(
+ VOID)
+{
+ PERF_ENTRY(GetCurrentProcess);
+ ENTRY("GetCurrentProcess()\n" );
+
+ LOGEXIT("GetCurrentProcess returns HANDLE %p\n", hPseudoCurrentProcess);
+ PERF_EXIT(GetCurrentProcess);
+
+ /* return a pseudo handle */
+ return hPseudoCurrentProcess;
+}
+
+/*++
+Function:
+ CreateProcessW
+
+Note:
+ Only Standard handles need to be inherited.
+ Security attributes parameters are not used.
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+CreateProcessW(
+ IN LPCWSTR lpApplicationName,
+ IN LPWSTR lpCommandLine,
+ IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ IN BOOL bInheritHandles,
+ IN DWORD dwCreationFlags,
+ IN LPVOID lpEnvironment,
+ IN LPCWSTR lpCurrentDirectory,
+ IN LPSTARTUPINFOW lpStartupInfo,
+ OUT LPPROCESS_INFORMATION lpProcessInformation)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread;
+
+ PERF_ENTRY(CreateProcessW);
+ ENTRY("CreateProcessW(lpAppName=%p (%S), lpCmdLine=%p (%S), lpProcessAttr=%p,"
+ "lpThreadAttr=%p, bInherit=%d, dwFlags=%#x, lpEnv=%p,"
+ "lpCurrentDir=%p (%S), lpStartupInfo=%p, lpProcessInfo=%p)\n",
+ lpApplicationName?lpApplicationName:W16_NULLSTRING,
+ lpApplicationName?lpApplicationName:W16_NULLSTRING,
+ lpCommandLine?lpCommandLine:W16_NULLSTRING,
+ lpCommandLine?lpCommandLine:W16_NULLSTRING,lpProcessAttributes,
+ lpThreadAttributes, bInheritHandles, dwCreationFlags,lpEnvironment,
+ lpCurrentDirectory?lpCurrentDirectory:W16_NULLSTRING,
+ lpCurrentDirectory?lpCurrentDirectory:W16_NULLSTRING,
+ lpStartupInfo, lpProcessInformation);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalCreateProcess(
+ pThread,
+ lpApplicationName,
+ lpCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ dwCreationFlags,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ lpProcessInformation
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("CreateProcessW returns BOOL %d\n", NO_ERROR == palError);
+ PERF_EXIT(CreateProcessW);
+
+ return NO_ERROR == palError;
+}
+
+PAL_ERROR
+PrepareStandardHandle(
+ CPalThread *pThread,
+ HANDLE hFile,
+ IPalObject **ppobjFile,
+ int *piFd
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjFile = NULL;
+ IDataLock *pDataLock = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ int iError = 0;
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ &pobjFile
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Bad handle passed through CreateProcess\n");
+ goto PrepareStandardHandleExit;
+ }
+
+ palError = pobjFile->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to access file data\n");
+ goto PrepareStandardHandleExit;
+ }
+
+ //
+ // The passed in file needs to be inheritable
+ //
+
+ if (!pLocalData->inheritable)
+ {
+ ERROR("Non-inheritable handle passed through CreateProcess\n");
+ palError = ERROR_INVALID_HANDLE;
+ goto PrepareStandardHandleExit;
+ }
+
+ iError = fcntl(pLocalData->unix_fd, F_SETFD, 0);
+ if (-1 == iError)
+ {
+ ERROR("Unable to remove close-on-exec for file (errno %i)\n", errno);
+ palError = ERROR_INVALID_HANDLE;
+ goto PrepareStandardHandleExit;
+ }
+
+ *piFd = pLocalData->unix_fd;
+ pDataLock->ReleaseLock(pThread, FALSE);
+ pDataLock = NULL;
+
+ //
+ // Transfer pobjFile reference to out parameter
+ //
+
+ *ppobjFile = pobjFile;
+ pobjFile = NULL;
+
+PrepareStandardHandleExit:
+
+ if (NULL != pDataLock)
+ {
+ pDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pobjFile)
+ {
+ pobjFile->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+PAL_ERROR
+CorUnix::InternalCreateProcess(
+ CPalThread *pThread,
+ LPCWSTR lpApplicationName,
+ LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjProcess = NULL;
+ IPalObject *pobjProcessRegistered = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ CProcProcessLocalData *pLocalData;
+ IDataLock *pSharedDataLock = NULL;
+ CPalThread *pDummyThread = NULL;
+ HANDLE hDummyThread = NULL;
+ HANDLE hProcess = NULL;
+ CObjectAttributes oa(NULL, lpProcessAttributes);
+
+ IPalObject *pobjFileIn = NULL;
+ int iFdIn = -1;
+ IPalObject *pobjFileOut = NULL;
+ int iFdOut = -1;
+ IPalObject *pobjFileErr = NULL;
+ int iFdErr = -1;
+
+ pid_t processId;
+ PathCharString lpFileNamePS;
+ char **lppArgv = NULL;
+ UINT nArg;
+ int iRet;
+ char **EnvironmentArray=NULL;
+ int child_blocking_pipe = -1;
+ int parent_blocking_pipe = -1;
+
+ /* Validate parameters */
+
+ /* note : specs indicate lpApplicationName should always
+ be NULL; however support for it is already implemented. Leaving the code
+ in, specs can change; but rejecting non-NULL for now to conform to the
+ spec. */
+ if( NULL != lpApplicationName )
+ {
+ ASSERT("lpApplicationName should be NULL, but is %S instead\n",
+ lpApplicationName);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreateProcessExit;
+ }
+
+ if (0 != (dwCreationFlags & ~(CREATE_SUSPENDED|CREATE_NEW_CONSOLE)))
+ {
+ ASSERT("Unexpected creation flags (%#x)\n", dwCreationFlags);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreateProcessExit;
+ }
+
+ /* Security attributes parameters are ignored */
+ if (lpProcessAttributes != NULL &&
+ (lpProcessAttributes->lpSecurityDescriptor != NULL ||
+ lpProcessAttributes->bInheritHandle != TRUE))
+ {
+ ASSERT("lpProcessAttributes is invalid, parameter ignored (%p)\n",
+ lpProcessAttributes);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreateProcessExit;
+ }
+
+ if (lpThreadAttributes != NULL)
+ {
+ ASSERT("lpThreadAttributes parameter must be NULL (%p)\n",
+ lpThreadAttributes);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreateProcessExit;
+ }
+
+ /* note : Win32 crashes in this case */
+ if(NULL == lpStartupInfo)
+ {
+ ERROR("lpStartupInfo is NULL\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreateProcessExit;
+ }
+
+ /* Validate lpStartupInfo.cb field */
+ if (lpStartupInfo->cb < sizeof(STARTUPINFOW))
+ {
+ ASSERT("lpStartupInfo parameter structure size is invalid (%u)\n",
+ lpStartupInfo->cb);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreateProcessExit;
+ }
+
+ /* lpStartupInfo should be either zero or STARTF_USESTDHANDLES */
+ if (lpStartupInfo->dwFlags & ~STARTF_USESTDHANDLES)
+ {
+ ASSERT("lpStartupInfo parameter invalid flags (%#x)\n",
+ lpStartupInfo->dwFlags);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreateProcessExit;
+ }
+
+ /* validate given standard handles if we have any */
+ if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES)
+ {
+ palError = PrepareStandardHandle(
+ pThread,
+ lpStartupInfo->hStdInput,
+ &pobjFileIn,
+ &iFdIn
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateProcessExit;
+ }
+
+ palError = PrepareStandardHandle(
+ pThread,
+ lpStartupInfo->hStdOutput,
+ &pobjFileOut,
+ &iFdOut
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateProcessExit;
+ }
+
+ palError = PrepareStandardHandle(
+ pThread,
+ lpStartupInfo->hStdError,
+ &pobjFileErr,
+ &iFdErr
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateProcessExit;
+ }
+ }
+
+ if (!getFileName(lpApplicationName, lpCommandLine, lpFileNamePS))
+ {
+ ERROR("Can't find executable!\n");
+ palError = ERROR_FILE_NOT_FOUND;
+ goto InternalCreateProcessExit;
+ }
+
+ /* check type of file */
+ iRet = checkFileType(lpFileNamePS);
+
+ switch (iRet)
+ {
+ case FILE_ERROR: /* file not found, or not an executable */
+ WARN ("File is not valid (%s)", lpFileNamePS.GetString());
+ palError = ERROR_FILE_NOT_FOUND;
+ goto InternalCreateProcessExit;
+
+ case FILE_UNIX: /* Unix binary file */
+ break; /* nothing to do */
+
+ case FILE_DIR:/*Directory*/
+ WARN ("File is a Directory (%s)", lpFileNamePS.GetString());
+ palError = ERROR_ACCESS_DENIED;
+ goto InternalCreateProcessExit;
+ break;
+
+ default: /* not supposed to get here */
+ ASSERT ("Invalid return type from checkFileType");
+ palError = ERROR_FILE_NOT_FOUND;
+ goto InternalCreateProcessExit;
+ }
+
+ /* build Argument list, lppArgv is allocated in buildArgv function and
+ requires to be freed */
+ lppArgv = buildArgv(lpCommandLine, lpFileNamePS, &nArg);
+
+ /* set the Environment variable */
+ if (lpEnvironment != NULL)
+ {
+ unsigned i;
+ // Since CREATE_UNICODE_ENVIRONMENT isn't supported we know the string is ansi
+ unsigned EnvironmentEntries = 0;
+ // Convert the environment block to array of strings
+ // Count the number of entries
+ // Is it a string that contains null terminated string, the end is delimited
+ // by two null in a row.
+ for (i = 0; ((char *)lpEnvironment)[i]!='\0'; i++)
+ {
+ EnvironmentEntries ++;
+ for (;((char *)lpEnvironment)[i]!='\0'; i++)
+ {
+ }
+ }
+ EnvironmentEntries++;
+ EnvironmentArray = (char **)InternalMalloc(EnvironmentEntries * sizeof(char *));
+
+ EnvironmentEntries = 0;
+ // Convert the environment block to array of strings
+ // Count the number of entries
+ // Is it a string that contains null terminated string, the end is delimited
+ // by two null in a row.
+ for (i = 0; ((char *)lpEnvironment)[i]!='\0'; i++)
+ {
+ EnvironmentArray[EnvironmentEntries] = &((char *)lpEnvironment)[i];
+ EnvironmentEntries ++;
+ for (;((char *)lpEnvironment)[i]!='\0'; i++)
+ {
+ }
+ }
+ EnvironmentArray[EnvironmentEntries] = NULL;
+ }
+
+ //
+ // Allocate and register the process object for the new process
+ //
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otProcess,
+ &oa,
+ &pobjProcess
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to allocate object for new proccess\n");
+ goto InternalCreateProcessExit;
+ }
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pobjProcess,
+ &aotProcess,
+ &hProcess,
+ &pobjProcessRegistered
+ );
+
+ //
+ // pobjProcess is invalidated by the above call, so
+ // NULL it out here
+ //
+
+ pobjProcess = NULL;
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to register new process object\n");
+ goto InternalCreateProcessExit;
+ }
+
+ //
+ // Create a new "dummy" thread object
+ //
+
+ palError = InternalCreateDummyThread(
+ pThread,
+ lpThreadAttributes,
+ &pDummyThread,
+ &hDummyThread
+ );
+
+ if (dwCreationFlags & CREATE_SUSPENDED)
+ {
+ int pipe_descs[2];
+
+ if (-1 == pipe(pipe_descs))
+ {
+ ERROR("pipe() failed! error is %d (%s)\n", errno, strerror(errno));
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto InternalCreateProcessExit;
+ }
+
+ /* [0] is read end, [1] is write end */
+ pDummyThread->suspensionInfo.SetBlockingPipe(pipe_descs[1]);
+ parent_blocking_pipe = pipe_descs[1];
+ child_blocking_pipe = pipe_descs[0];
+ }
+
+ palError = pobjProcessRegistered->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pLocalDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to obtain local data for new process object\n");
+ goto InternalCreateProcessExit;
+ }
+
+
+ /* fork the new process */
+ processId = fork();
+
+ if (processId == -1)
+ {
+ ASSERT("Unable to create a new process with fork()\n");
+ if (-1 != child_blocking_pipe)
+ {
+ close(child_blocking_pipe);
+ close(parent_blocking_pipe);
+ }
+
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalCreateProcessExit;
+ }
+
+ /* From the time the child process begins running, to when it reaches execve,
+ the child process is not a real PAL process and does not own any PAL
+ resources, although it has access to the PAL resources of its parent process.
+ Thus, while the child process is in this window, it is dangerous for it to affect
+ its parent's PAL resources. As a consequence, no PAL code should be used
+ in this window; all code should make unix calls. Note the use of _exit
+ instead of exit to avoid calling PAL_Terminate and the lack of TRACE's and
+ ASSERT's. */
+
+ if (processId == 0) /* child process */
+ {
+ // At this point, the PAL should be considered uninitialized for this child process.
+
+ // Don't want to enter the init_critsec here since we're trying to avoid
+ // calling PAL functions. Furthermore, nothing should be changing
+ // the init_count in the child process at this point since this is the only
+ // thread executing.
+ init_count = 0;
+
+ sigset_t sm;
+
+ //
+ // Clear out the signal mask for the new process.
+ //
+
+ sigemptyset(&sm);
+ iRet = sigprocmask(SIG_SETMASK, &sm, NULL);
+ if (iRet != 0)
+ {
+ _exit(EXIT_FAILURE);
+ }
+
+ if (dwCreationFlags & CREATE_SUSPENDED)
+ {
+ BYTE resume_code = 0;
+ ssize_t read_ret;
+
+ /* close the write end of the pipe, the child doesn't need it */
+ close(parent_blocking_pipe);
+
+ read_again:
+ /* block until ResumeThread writes something to the pipe */
+ read_ret = read(child_blocking_pipe, &resume_code, sizeof(resume_code));
+ if (sizeof(resume_code) != read_ret)
+ {
+ if (read_ret == -1 && EINTR == errno)
+ {
+ goto read_again;
+ }
+ else
+ {
+ /* note : read might return 0 (and return EAGAIN) if the other
+ end of the pipe gets closed - for example because the parent
+ process dies (very) abruptly */
+ _exit(EXIT_FAILURE);
+ }
+ }
+ if (WAKEUPCODE != resume_code)
+ {
+ // resume_code should always equal WAKEUPCODE.
+ _exit(EXIT_FAILURE);
+ }
+
+ close(child_blocking_pipe);
+ }
+
+ /* Set the current directory */
+ if (lpCurrentDirectory)
+ {
+ SetCurrentDirectoryW(lpCurrentDirectory);
+ }
+
+ /* Set the standard handles to the incoming values */
+ if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES)
+ {
+ /* For each handle, we need to duplicate the incoming unix
+ fd to the corresponding standard one. The API that I use,
+ dup2, will copy the source to the destination, automatically
+ closing the existing destination, in an atomic way */
+ if (dup2(iFdIn, STDIN_FILENO) == -1)
+ {
+ // Didn't duplicate standard in.
+ _exit(EXIT_FAILURE);
+ }
+
+ if (dup2(iFdOut, STDOUT_FILENO) == -1)
+ {
+ // Didn't duplicate standard out.
+ _exit(EXIT_FAILURE);
+ }
+
+ if (dup2(iFdErr, STDERR_FILENO) == -1)
+ {
+ // Didn't duplicate standard error.
+ _exit(EXIT_FAILURE);
+ }
+
+ /* now close the original FDs, we don't need them anymore */
+ close(iFdIn);
+ close(iFdOut);
+ close(iFdErr);
+ }
+
+ /* execute the new process */
+
+ if (EnvironmentArray)
+ {
+ execve(lpFileNamePS, lppArgv, EnvironmentArray);
+ }
+ else
+ {
+ execve(lpFileNamePS, lppArgv, palEnvironment);
+ }
+
+ /* if we get here, it means the execve function call failed so just exit */
+ _exit(EXIT_FAILURE);
+ }
+
+ /* parent process */
+
+ /* close the read end of the pipe, the parent doesn't need it */
+ close(child_blocking_pipe);
+
+ /* Set the process ID */
+ pLocalData->dwProcessId = processId;
+ pLocalDataLock->ReleaseLock(pThread, TRUE);
+ pLocalDataLock = NULL;
+
+ //
+ // Release file handle info; we don't need them anymore. Note that
+ // this must happen after we've released the data locks, as
+ // otherwise a deadlock could result.
+ //
+
+ if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES)
+ {
+ pobjFileIn->ReleaseReference(pThread);
+ pobjFileIn = NULL;
+ pobjFileOut->ReleaseReference(pThread);
+ pobjFileOut = NULL;
+ pobjFileErr->ReleaseReference(pThread);
+ pobjFileErr = NULL;
+ }
+
+ /* fill PROCESS_INFORMATION strucutre */
+ lpProcessInformation->hProcess = hProcess;
+ lpProcessInformation->hThread = hDummyThread;
+ lpProcessInformation->dwProcessId = processId;
+ lpProcessInformation->dwThreadId_PAL_Undefined = 0;
+
+
+ TRACE("New process created: id=%#x\n", processId);
+
+InternalCreateProcessExit:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pSharedDataLock)
+ {
+ pSharedDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pobjProcess)
+ {
+ pobjProcess->ReleaseReference(pThread);
+ }
+
+ if (NULL != pobjProcessRegistered)
+ {
+ pobjProcessRegistered->ReleaseReference(pThread);
+ }
+
+ if (NO_ERROR != palError)
+ {
+ if (NULL != hProcess)
+ {
+ g_pObjectManager->RevokeHandle(pThread, hProcess);
+ }
+
+ if (NULL != hDummyThread)
+ {
+ g_pObjectManager->RevokeHandle(pThread, hDummyThread);
+ }
+ }
+
+ if (EnvironmentArray)
+ {
+ free(EnvironmentArray);
+ }
+
+ /* if we still have the file structures at this point, it means we
+ encountered an error sometime between when we acquired them and when we
+ fork()ed. We not only have to release them, we have to give them back
+ their close-on-exec flag */
+ if (NULL != pobjFileIn)
+ {
+ if(-1 == fcntl(iFdIn, F_SETFD, 1))
+ {
+ WARN("couldn't restore close-on-exec flag to stdin descriptor! "
+ "errno is %d (%s)\n", errno, strerror(errno));
+ }
+ pobjFileIn->ReleaseReference(pThread);
+ }
+
+ if (NULL != pobjFileOut)
+ {
+ if(-1 == fcntl(iFdOut, F_SETFD, 1))
+ {
+ WARN("couldn't restore close-on-exec flag to stdout descriptor! "
+ "errno is %d (%s)\n", errno, strerror(errno));
+ }
+ pobjFileOut->ReleaseReference(pThread);
+ }
+
+ if (NULL != pobjFileErr)
+ {
+ if(-1 == fcntl(iFdErr, F_SETFD, 1))
+ {
+ WARN("couldn't restore close-on-exec flag to stderr descriptor! "
+ "errno is %d (%s)\n", errno, strerror(errno));
+ }
+ pobjFileErr->ReleaseReference(pThread);
+ }
+
+ /* free allocated memory */
+ if (lppArgv)
+ {
+ free(*lppArgv);
+ free(lppArgv);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ TerminateProcess
+
+Note:
+ hProcess is a handle on the current process.
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+TerminateProcess(
+ IN HANDLE hProcess,
+ IN UINT uExitCode)
+{
+ BOOL ret;
+
+ PERF_ENTRY(TerminateProcess);
+ ENTRY("TerminateProcess(hProcess=%p, uExitCode=%u)\n",hProcess, uExitCode );
+
+ ret = PROCEndProcess(hProcess, uExitCode, TRUE);
+
+ LOGEXIT("TerminateProcess returns BOOL %d\n", ret);
+ PERF_EXIT(TerminateProcess);
+ return ret;
+}
+
+/*++
+Function:
+ RaiseFailFastException
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+RaiseFailFastException(
+ IN PEXCEPTION_RECORD pExceptionRecord,
+ IN PCONTEXT pContextRecord,
+ IN DWORD dwFlags)
+{
+ PERF_ENTRY(RaiseFailFastException);
+ ENTRY("RaiseFailFastException");
+
+ TerminateCurrentProcessNoExit(TRUE);
+ PROCAbort();
+
+ LOGEXIT("RaiseFailFastException");
+ PERF_EXIT(RaiseFailFastException);
+}
+
+/*++
+Function:
+ PROCEndProcess
+
+ Called from TerminateProcess and ExitProcess. This does the work of
+ TerminateProcess, but also takes a flag that determines whether we
+ shut down unconditionally. If the flag is set, the PAL will do very
+ little extra work before exiting. Most importantly, it won't shut
+ down any DLLs that are loaded.
+
+--*/
+static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUnconditionally)
+{
+ DWORD dwProcessId;
+ BOOL ret = FALSE;
+
+ dwProcessId = PROCGetProcessIDFromHandle(hProcess);
+ if (dwProcessId == 0)
+ {
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+ else if(dwProcessId != GetCurrentProcessId())
+ {
+ if (uExitCode != 0)
+ WARN("exit code 0x%x ignored for external process.\n", uExitCode);
+
+ if (kill(dwProcessId, SIGKILL) == 0)
+ {
+ ret = TRUE;
+ }
+ else
+ {
+ switch (errno) {
+ case ESRCH:
+ SetLastError(ERROR_INVALID_HANDLE);
+ break;
+ case EPERM:
+ SetLastError(ERROR_ACCESS_DENIED);
+ break;
+ default:
+ // Unexpected failure.
+ ASSERT(FALSE);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ break;
+ }
+ }
+ }
+ else
+ {
+ // WARN/ERROR before starting the termination process and/or leaving the PAL.
+ if (bTerminateUnconditionally)
+ {
+ WARN("exit code 0x%x ignored for terminate.\n", uExitCode);
+ }
+ else if ((uExitCode & 0xff) != uExitCode)
+ {
+ // TODO: Convert uExitCodes into sysexits(3)?
+ ERROR("exit() only supports the lower 8-bits of an exit code. "
+ "status will only see error 0x%x instead of 0x%x.\n", uExitCode & 0xff, uExitCode);
+ }
+
+ TerminateCurrentProcessNoExit(bTerminateUnconditionally);
+
+ LOGEXIT("PROCEndProcess will not return\n");
+
+ if (bTerminateUnconditionally)
+ {
+ // abort() has the semantics that
+ // (1) it doesn't run atexit handlers
+ PROCAbort();
+ }
+ else
+ {
+ exit(uExitCode);
+ }
+
+ ASSERT(FALSE); // we shouldn't get here
+ }
+
+ return ret;
+}
+
+static bool IsCoreClrModule(const char* pModulePath)
+{
+ // Strip off everything up to and including the last slash in the path to get name
+ const char* pModuleName = pModulePath;
+ while (strchr(pModuleName, '/') != NULL)
+ {
+ pModuleName = strchr(pModuleName, '/');
+ pModuleName++; // pass the slash
+ }
+
+ return _stricmp(pModuleName, MAKEDLLNAME_A("coreclr")) == 0;
+}
+
+// Build the semaphore names using the PID and a value that can be used for distinguishing
+// between processes with the same PID (which ran at different times). This is to avoid
+// cases where a prior process with the same PID exited abnormally without having a chance
+// to clean up its semaphore.
+// Note to anyone modifying these names in the future: Semaphore names on OS X are limited
+// to SEM_NAME_LEN characters, including null. SEM_NAME_LEN is 31 (at least on OS X 10.11).
+// NetBSD limits semaphore names to 15 characters, including null (at least up to 7.99.25).
+// Keep 31 length for Core 1.0 RC2 compatibility
+#if defined(__NetBSD__)
+static const char* RuntimeSemaphoreNameFormat = "/clr%s%08llx";
+#else
+static const char* RuntimeSemaphoreNameFormat = "/clr%s%08x%016llx";
+#endif
+
+static const char* RuntimeStartupSemaphoreName = "st";
+static const char* RuntimeContinueSemaphoreName = "co";
+
+#if defined(__NetBSD__)
+static uint64_t HashSemaphoreName(uint64_t a, uint64_t b)
+{
+ return (a ^ b) & 0xffffffff;
+}
+#else
+#define HashSemaphoreName(a,b) a,b
+#endif
+
+static const char *const TwoWayNamedPipePrefix = "clr-debug-pipe";
+static const char* IpcNameFormat = "%s-%d-%llu-%s";
+
+class PAL_RuntimeStartupHelper
+{
+ LONG m_ref;
+ bool m_canceled;
+ PPAL_STARTUP_CALLBACK m_callback;
+ PVOID m_parameter;
+ DWORD m_threadId;
+ HANDLE m_threadHandle;
+ DWORD m_processId;
+#ifdef __APPLE__
+ char m_applicationGroupId[MAX_APPLICATION_GROUP_ID_LENGTH+1];
+#endif // __APPLE__
+ char m_startupSemName[CLR_SEM_MAX_NAMELEN];
+ char m_continueSemName[CLR_SEM_MAX_NAMELEN];
+
+ // A value that, used in conjunction with the process ID, uniquely identifies a process.
+ // See the format we use for debugger semaphore names for why this is necessary.
+ UINT64 m_processIdDisambiguationKey;
+
+ // Debugger waits on this semaphore and the runtime signals it on startup.
+ sem_t *m_startupSem;
+
+ // Debuggee waits on this semaphore and the debugger signals it after the startup callback
+ // registered (m_callback) returns.
+ sem_t *m_continueSem;
+
+ LPCSTR GetApplicationGroupId() const
+ {
+#ifdef __APPLE__
+ return m_applicationGroupId[0] == '\0' ? nullptr : m_applicationGroupId;
+#else // __APPLE__
+ return nullptr;
+#endif // __APPLE__
+ }
+
+public:
+ PAL_RuntimeStartupHelper(DWORD dwProcessId, PPAL_STARTUP_CALLBACK pfnCallback, PVOID parameter) :
+ m_ref(1),
+ m_canceled(false),
+ m_callback(pfnCallback),
+ m_parameter(parameter),
+ m_threadId(0),
+ m_threadHandle(NULL),
+ m_processId(dwProcessId),
+ m_startupSem(SEM_FAILED),
+ m_continueSem(SEM_FAILED)
+ {
+ }
+
+ ~PAL_RuntimeStartupHelper()
+ {
+ if (m_startupSem != SEM_FAILED)
+ {
+ sem_close(m_startupSem);
+ sem_unlink(m_startupSemName);
+ }
+
+ if (m_continueSem != SEM_FAILED)
+ {
+ sem_close(m_continueSem);
+ sem_unlink(m_continueSemName);
+ }
+
+ if (m_threadHandle != NULL)
+ {
+ CloseHandle(m_threadHandle);
+ }
+ }
+
+ LONG AddRef()
+ {
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+ }
+
+ LONG Release()
+ {
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ InternalDelete(this);
+ }
+ return ref;
+ }
+
+ PAL_ERROR GetSemError()
+ {
+ PAL_ERROR pe;
+ switch (errno)
+ {
+ case ENOENT:
+ pe = ERROR_NOT_FOUND;
+ break;
+ case EACCES:
+ pe = ERROR_INVALID_ACCESS;
+ break;
+ case EINVAL:
+ case ENAMETOOLONG:
+ pe = ERROR_INVALID_NAME;
+ break;
+ case ENOMEM:
+ pe = ERROR_OUTOFMEMORY;
+ break;
+ case EEXIST:
+ pe = ERROR_ALREADY_EXISTS;
+ break;
+ case ENOSPC:
+ pe = ERROR_TOO_MANY_SEMAPHORES;
+ break;
+ default:
+ pe = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ return pe;
+ }
+
+ PAL_ERROR Register(LPCWSTR lpApplicationGroupId)
+ {
+ CPalThread *pThread = InternalGetCurrentThread();
+ PAL_ERROR pe = NO_ERROR;
+ BOOL ret;
+ UnambiguousProcessDescriptor unambiguousProcessDescriptor;
+ SIZE_T osThreadId = 0;
+
+#ifdef __APPLE__
+ if (lpApplicationGroupId != NULL)
+ {
+ /* Convert to ASCII */
+ int applicationGroupIdLength = WideCharToMultiByte(CP_ACP, 0, lpApplicationGroupId, -1, m_applicationGroupId, sizeof(m_applicationGroupId), NULL, NULL);
+ if (applicationGroupIdLength == 0)
+ {
+ pe = GetLastError();
+ TRACE("applicationGroupId: Failed to convert to multibyte string (%u)\n", pe);
+ if (pe == ERROR_INSUFFICIENT_BUFFER)
+ {
+ pe = ERROR_BAD_LENGTH;
+ }
+ goto exit;
+ }
+ }
+ else
+ {
+ // Indicate that group ID is not being used
+ m_applicationGroupId[0] = '\0';
+ }
+#endif // __APPLE__
+
+ // See semaphore name format for details about this value. We store it so that
+ // it can be used by the cleanup code that removes the semaphore with sem_unlink.
+ ret = GetProcessIdDisambiguationKey(m_processId, &m_processIdDisambiguationKey);
+
+ // If GetProcessIdDisambiguationKey failed for some reason, it should set the value
+ // to 0. We expect that anyone else opening the semaphore name will also fail and thus
+ // will also try to use 0 as the value.
+ _ASSERTE(ret == TRUE || m_processIdDisambiguationKey == 0);
+
+ unambiguousProcessDescriptor.Init(m_processId, m_processIdDisambiguationKey);
+ CreateSemaphoreName(m_startupSemName, RuntimeStartupSemaphoreName, unambiguousProcessDescriptor, GetApplicationGroupId());
+ CreateSemaphoreName(m_continueSemName, RuntimeContinueSemaphoreName, unambiguousProcessDescriptor, GetApplicationGroupId());
+
+ TRACE("PAL_RuntimeStartupHelper.Register creating startup '%s' continue '%s'\n", m_startupSemName, m_continueSemName);
+
+ // Create the continue semaphore first so we don't race with PAL_NotifyRuntimeStarted. This open will fail if another
+ // debugger is trying to attach to this process because the name will already exist.
+ m_continueSem = sem_open(m_continueSemName, O_CREAT | O_EXCL, S_IRWXU, 0);
+ if (m_continueSem == SEM_FAILED)
+ {
+ TRACE("sem_open(continue) failed: errno is %d (%s)\n", errno, strerror(errno));
+ pe = GetSemError();
+ goto exit;
+ }
+
+ // Create the debuggee startup semaphore so the runtime (debuggee) knows to wait for a debugger connection.
+ m_startupSem = sem_open(m_startupSemName, O_CREAT | O_EXCL, S_IRWXU, 0);
+ if (m_startupSem == SEM_FAILED)
+ {
+ TRACE("sem_open(startup) failed: errno is %d (%s)\n", errno, strerror(errno));
+ pe = GetSemError();
+ goto exit;
+ }
+
+ // Add a reference for the thread handler
+ AddRef();
+ pe = InternalCreateThread(
+ pThread,
+ NULL,
+ 0,
+ ::StartupHelperThread,
+ this,
+ 0,
+ UserCreatedThread,
+ &osThreadId,
+ &m_threadHandle);
+
+ if (NO_ERROR != pe)
+ {
+ TRACE("InternalCreateThread failed %d\n", pe);
+ Release();
+ goto exit;
+ }
+ m_threadId = (DWORD)osThreadId;
+ exit:
+ return pe;
+ }
+
+ void Unregister()
+ {
+ m_canceled = true;
+
+ // Tell the runtime to continue
+ if (sem_post(m_continueSem) != 0)
+ {
+ ASSERT("sem_post(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno));
+ }
+
+ // Tell the worker thread to continue
+ if (sem_post(m_startupSem) != 0)
+ {
+ ASSERT("sem_post(startupSem) failed: errno is %d (%s)\n", errno, strerror(errno));
+ }
+
+ // Don't need to wait for the worker thread if unregister called on it
+ if (m_threadId != (DWORD)THREADSilentGetCurrentThreadId())
+ {
+ // Wait for work thread to exit
+ if (WaitForSingleObject(m_threadHandle, INFINITE) != WAIT_OBJECT_0)
+ {
+ ASSERT("WaitForSingleObject\n");
+ }
+ }
+ }
+
+ //
+ // There are a couple race conditions that need to be considered here:
+ //
+ // * On launch, between the fork and execv in the PAL's CreateProcess where the target process
+ // may contain a coreclr module image if the debugger process is running managed code. This
+ // makes just checking if the coreclr module exists not enough.
+ //
+ // * On launch (after the execv) or attach when the coreclr is loaded but before the DAC globals
+ // table is initialized where it is too soon to use/initialize the DAC on the debugger side.
+ //
+ // They are both fixed by check if the one of transport pipe files has been created.
+ //
+ bool IsCoreClrProcessReady()
+ {
+ char pipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH];
+
+ PAL_GetTransportPipeName(pipeName, m_processId, GetApplicationGroupId(), "in");
+
+ struct stat buf;
+ if (stat(pipeName, &buf) == 0)
+ {
+ TRACE("IsCoreClrProcessReady: stat(%s) SUCCEEDED\n", pipeName);
+ return true;
+ }
+ TRACE("IsCoreClrProcessReady: stat(%s) FAILED: errno is %d (%s)\n", pipeName, errno, strerror(errno));
+ return false;
+ }
+
+ PAL_ERROR InvokeStartupCallback()
+ {
+ ProcessModules *listHead = NULL;
+ PAL_ERROR pe = NO_ERROR;
+ DWORD count;
+
+ if (m_canceled)
+ {
+ goto exit;
+ }
+
+ // Enumerate all the modules in the process and invoke the callback
+ // for the coreclr module if found.
+ listHead = CreateProcessModules(m_processId, &count);
+ if (listHead == NULL)
+ {
+ TRACE("CreateProcessModules failed for pid %d\n", m_processId);
+ pe = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
+ {
+ if (IsCoreClrModule(entry->Name))
+ {
+ PAL_CPP_TRY
+ {
+ TRACE("InvokeStartupCallback executing callback %p %s\n", entry->BaseAddress, entry->Name);
+ m_callback(entry->Name, entry->BaseAddress, m_parameter);
+ }
+ PAL_CPP_CATCH_ALL
+ {
+ }
+ PAL_CPP_ENDTRY
+
+ // Currently only the first coreclr module in a process is supported
+ break;
+ }
+ }
+
+ exit:
+ // Wake up the runtime
+ if (sem_post(m_continueSem) != 0)
+ {
+ ASSERT("sem_post(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno));
+ }
+ if (listHead != NULL)
+ {
+ DestroyProcessModules(listHead);
+ }
+ return pe;
+ }
+
+ void StartupHelperThread()
+ {
+ PAL_ERROR pe = NO_ERROR;
+
+ if (IsCoreClrProcessReady())
+ {
+ pe = InvokeStartupCallback();
+ }
+ else {
+ TRACE("sem_wait(startup)\n");
+
+ // Wait until the coreclr runtime (debuggee) starts up
+ while (sem_wait(m_startupSem) != 0)
+ {
+ if (EINTR == errno)
+ {
+ TRACE("sem_wait() failed with EINTR; re-waiting");
+ continue;
+ }
+ TRACE("sem_wait(startup) failed: errno is %d (%s)\n", errno, strerror(errno));
+ pe = GetSemError();
+ }
+
+ if (pe == NO_ERROR)
+ {
+ pe = InvokeStartupCallback();
+ }
+ }
+
+ // Invoke the callback on errors
+ if (pe != NO_ERROR && !m_canceled)
+ {
+ SetLastError(pe);
+ m_callback(NULL, NULL, m_parameter);
+ }
+ }
+};
+
+static
+DWORD
+StartupHelperThread(LPVOID p)
+{
+ TRACE("PAL's StartupHelperThread starting\n");
+
+ PAL_RuntimeStartupHelper *helper = (PAL_RuntimeStartupHelper *)p;
+ helper->StartupHelperThread();
+ helper->Release();
+ return 0;
+}
+
+/*++
+ PAL_RegisterForRuntimeStartup
+
+Parameters:
+ dwProcessId - process id of runtime process
+ lpApplicationGroupId - A string representing the application group ID of a sandboxed
+ process running in Mac. Pass NULL if the process is not
+ running in a sandbox and other platforms.
+ pfnCallback - function to callback for coreclr module found
+ parameter - data to pass to callback
+ ppUnregisterToken - pointer to put PAL_UnregisterForRuntimeStartup token.
+
+Return value:
+ PAL_ERROR
+
+Note:
+ If the modulePath or hModule is NULL when the callback is invoked, an error occured
+ and GetLastError() will return the Win32 error code.
+
+ The callback is always invoked on a separate thread and this API returns immediately.
+
+ Only the first coreclr module is currently supported.
+
+--*/
+DWORD
+PALAPI
+PAL_RegisterForRuntimeStartup(
+ IN DWORD dwProcessId,
+ IN LPCWSTR lpApplicationGroupId,
+ IN PPAL_STARTUP_CALLBACK pfnCallback,
+ IN PVOID parameter,
+ OUT PVOID *ppUnregisterToken)
+{
+ _ASSERTE(pfnCallback != NULL);
+ _ASSERTE(ppUnregisterToken != NULL);
+
+ PAL_RuntimeStartupHelper *helper = InternalNew<PAL_RuntimeStartupHelper>(dwProcessId, pfnCallback, parameter);
+
+ // Create the debuggee startup semaphore so the runtime (debuggee) knows to wait for
+ // a debugger connection.
+ PAL_ERROR pe = helper->Register(lpApplicationGroupId);
+ if (NO_ERROR != pe)
+ {
+ helper->Release();
+ helper = NULL;
+ }
+
+ *ppUnregisterToken = helper;
+ return pe;
+}
+
+/*++
+ PAL_UnregisterForRuntimeStartup
+
+ Stops/cancels startup notification. This API can be called in the startup callback. Otherwise,
+ it will block until the callback thread finishes and no more callbacks will be initiated after
+ this API returns.
+
+Parameters:
+ dwUnregisterToken - token from PAL_RegisterForRuntimeStartup or NULL.
+
+Return value:
+ PAL_ERROR
+--*/
+DWORD
+PALAPI
+PAL_UnregisterForRuntimeStartup(
+ IN PVOID pUnregisterToken)
+{
+ if (pUnregisterToken != NULL)
+ {
+ PAL_RuntimeStartupHelper *helper = (PAL_RuntimeStartupHelper *)pUnregisterToken;
+ helper->Unregister();
+ helper->Release();
+ }
+ return NO_ERROR;
+}
+
+#ifdef __APPLE__
+
+// We use 7bits from each byte, so this computes the extra size we need to encode a given byte count
+constexpr int GetExtraEncodedAreaSize(UINT rawByteCount)
+{
+ return (rawByteCount+6)/7;
+}
+const int SEMAPHORE_ENCODED_NAME_EXTRA_LENGTH = GetExtraEncodedAreaSize(sizeof(UnambiguousProcessDescriptor));
+const int SEMAPHORE_ENCODED_NAME_LENGTH =
+ sizeof(UnambiguousProcessDescriptor) + /* For process ID + disambiguationKey */
+ SEMAPHORE_ENCODED_NAME_EXTRA_LENGTH; /* For base 255 extra encoding space */
+
+static_assert_no_msg(MAX_APPLICATION_GROUP_ID_LENGTH
+ + 1 /* For / */
+ + 2 /* For ST/CO name prefix */
+ + SEMAPHORE_ENCODED_NAME_LENGTH /* For encoded name string */
+ + 1 /* For null terminator */
+ <= CLR_SEM_MAX_NAMELEN);
+
+// In Apple we are limited by the length of the semaphore name. However, the characters which can be used in the
+// name can be anything between 1 and 255 (since 0 will terminate the string). Thus, we encode each byte b in
+// unambiguousProcessDescriptor as b ? b : 1, and mark an additional bit indicating if b is 0 or not. We use 7 bits
+// out of each extra byte so 1 bit will always be '1'. This will ensure that our extra bytes are never 0 which are
+// invalid characters. Thus we need an extra byte for each 7 input bytes. Hence, only extra 2 bytes for the name string.
+void EncodeSemaphoreName(char *encodedSemName, const UnambiguousProcessDescriptor& unambiguousProcessDescriptor)
+{
+ const unsigned char *buffer = (const unsigned char *)&unambiguousProcessDescriptor;
+ char *extraEncodingBits = encodedSemName + sizeof(UnambiguousProcessDescriptor);
+
+ // Reset the extra encoding bit area
+ for (int i=0; i<SEMAPHORE_ENCODED_NAME_EXTRA_LENGTH; i++)
+ {
+ extraEncodingBits[i] = 0x80;
+ }
+
+ // Encode each byte in unambiguousProcessDescriptor
+ for (int i=0; i<sizeof(UnambiguousProcessDescriptor); i++)
+ {
+ unsigned char b = buffer[i];
+ encodedSemName[i] = b ? b : 1;
+ extraEncodingBits[i/7] |= (b ? 0 : 1) << (i%7);
+ }
+}
+#endif
+
+void CreateSemaphoreName(char semName[CLR_SEM_MAX_NAMELEN], LPCSTR semaphoreName, const UnambiguousProcessDescriptor& unambiguousProcessDescriptor, LPCSTR applicationGroupId)
+{
+ int length = 0;
+
+#ifdef __APPLE__
+ if (applicationGroupId != nullptr)
+ {
+ // We assume here that applicationGroupId has been already tested for length and is less than MAX_APPLICATION_GROUP_ID_LENGTH
+ length = sprintf_s(semName, CLR_SEM_MAX_NAMELEN, "%s/%s", applicationGroupId, semaphoreName);
+ _ASSERTE(length > 0 && length < CLR_SEM_MAX_NAMELEN);
+
+ EncodeSemaphoreName(semName+length, unambiguousProcessDescriptor);
+ length += SEMAPHORE_ENCODED_NAME_LENGTH;
+ semName[length] = 0;
+ }
+ else
+#endif // __APPLE__
+ {
+ length = sprintf_s(
+ semName,
+ CLR_SEM_MAX_NAMELEN,
+ RuntimeSemaphoreNameFormat,
+ semaphoreName,
+ HashSemaphoreName(unambiguousProcessDescriptor.m_processId, unambiguousProcessDescriptor.m_disambiguationKey));
+ }
+
+ _ASSERTE(length > 0 && length < CLR_SEM_MAX_NAMELEN );
+}
+
+/*++
+ Function:
+ GetProcessIdDisambiguationKey
+
+ Get a numeric value that can be used to disambiguate between processes with the same PID,
+ provided that one of them is still running. The numeric value can mean different things
+ on different platforms, so it should not be used for any other purpose. Under the hood,
+ it is implemented based on the creation time of the process.
+--*/
+BOOL
+GetProcessIdDisambiguationKey(DWORD processId, UINT64 *disambiguationKey)
+{
+ if (disambiguationKey == nullptr)
+ {
+ _ASSERTE(!"disambiguationKey argument cannot be null!");
+ return FALSE;
+ }
+
+ *disambiguationKey = 0;
+
+#if defined(__APPLE__) || defined(__FreeBSD__)
+
+ // On OS X, we return the process start time expressed in Unix time (the number of seconds
+ // since the start of the Unix epoch).
+ struct kinfo_proc info = {};
+ size_t size = sizeof(info);
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, processId };
+ int ret = ::sysctl(mib, sizeof(mib)/sizeof(*mib), &info, &size, nullptr, 0);
+
+ if (ret == 0)
+ {
+#if defined(__APPLE__)
+ timeval procStartTime = info.kp_proc.p_starttime;
+#else // __FreeBSD__
+ timeval procStartTime = info.ki_start;
+#endif
+ long secondsSinceEpoch = procStartTime.tv_sec;
+
+ *disambiguationKey = secondsSinceEpoch;
+ return TRUE;
+ }
+ else
+ {
+ _ASSERTE(!"Failed to get start time of a process.");
+ return FALSE;
+ }
+
+#elif defined(__NetBSD__)
+
+ // On NetBSD, we return the process start time expressed in Unix time (the number of seconds
+ // since the start of the Unix epoch).
+ kvm_t *kd;
+ int cnt;
+ struct kinfo_proc2 *info;
+
+ kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, "kvm_open");
+ if (kd == nullptr)
+ {
+ _ASSERTE(!"Failed to get start time of a process.");
+ return FALSE;
+ }
+
+ info = kvm_getproc2(kd, KERN_PROC_PID, processId, sizeof(struct kinfo_proc2), &cnt);
+ if (info == nullptr || cnt < 1)
+ {
+ kvm_close(kd);
+ _ASSERTE(!"Failed to get start time of a process.");
+ return FALSE;
+ }
+
+ kvm_close(kd);
+
+ long secondsSinceEpoch = info->p_ustart_sec;
+ *disambiguationKey = secondsSinceEpoch;
+
+ return TRUE;
+
+#elif HAVE_PROCFS_STAT
+
+ // Here we read /proc/<pid>/stat file to get the start time for the process.
+ // We return this value (which is expressed in jiffies since boot time).
+
+ // Making something like: /proc/123/stat
+ char statFileName[64];
+
+ INDEBUG(int chars = )
+ snprintf(statFileName, sizeof(statFileName), "/proc/%d/stat", processId);
+ _ASSERTE(chars > 0 && chars <= (int)sizeof(statFileName));
+
+ FILE *statFile = fopen(statFileName, "r");
+ if (statFile == nullptr)
+ {
+ TRACE("GetProcessIdDisambiguationKey: fopen() FAILED");
+ SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ char *line = nullptr;
+ size_t lineLen = 0;
+ if (getline(&line, &lineLen, statFile) == -1)
+ {
+ TRACE("GetProcessIdDisambiguationKey: getline() FAILED");
+ SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ unsigned long long starttime;
+
+ // According to `man proc`, the second field in the stat file is the filename of the executable,
+ // in parentheses. Tokenizing the stat file using spaces as separators breaks when that name
+ // has spaces in it, so we start using sscanf_s after skipping everything up to and including the
+ // last closing paren and the space after it.
+ char *scanStartPosition = strrchr(line, ')') + 2;
+
+ // All the format specifiers for the fields in the stat file are provided by 'man proc'.
+ int sscanfRet = sscanf_s(scanStartPosition,
+ "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %llu \n",
+ &starttime);
+
+ if (sscanfRet != 1)
+ {
+ _ASSERTE(!"Failed to parse stat file contents with sscanf_s.");
+ return FALSE;
+ }
+
+ free(line);
+ fclose(statFile);
+
+ *disambiguationKey = starttime;
+ return TRUE;
+
+#else
+ // If this is not OS X and we don't have /proc, we just return FALSE.
+ WARN("GetProcessIdDisambiguationKey was called but is not implemented on this platform!");
+ return FALSE;
+#endif
+}
+
+/*++
+ Function:
+ PAL_GetTransportName
+
+ Builds the transport IPC names from the process id.
+--*/
+VOID
+PALAPI
+PAL_GetTransportName(
+ const unsigned int MAX_TRANSPORT_NAME_LENGTH,
+ OUT char *name,
+ IN const char *prefix,
+ IN DWORD id,
+ IN const char *applicationGroupId,
+ IN const char *suffix)
+{
+ *name = '\0';
+ DWORD dwRetVal = 0;
+ UINT64 disambiguationKey = 0;
+ PathCharString formatBufferString;
+ BOOL ret = GetProcessIdDisambiguationKey(id, &disambiguationKey);
+ char *formatBuffer = formatBufferString.OpenStringBuffer(MAX_TRANSPORT_NAME_LENGTH-1);
+ if (formatBuffer == nullptr)
+ {
+ ERROR("Out Of Memory");
+ return;
+ }
+
+ // If GetProcessIdDisambiguationKey failed for some reason, it should set the value
+ // to 0. We expect that anyone else making the pipe name will also fail and thus will
+ // also try to use 0 as the value.
+ _ASSERTE(ret == TRUE || disambiguationKey == 0);
+#ifdef __APPLE__
+ if (nullptr != applicationGroupId)
+ {
+ // Verify the length of the application group ID
+ int applicationGroupIdLength = strlen(applicationGroupId);
+ if (applicationGroupIdLength > MAX_APPLICATION_GROUP_ID_LENGTH)
+ {
+ ERROR("The length of applicationGroupId is larger than MAX_APPLICATION_GROUP_ID_LENGTH");
+ return;
+ }
+
+ // In sandbox, all IPC files (locks, pipes) should be written to the application group
+ // container. The path returned by GetTempPathA will be unique for each process and cannot
+ // be used for IPC between two different processes
+ if (!GetApplicationContainerFolder(formatBufferString, applicationGroupId, applicationGroupIdLength))
+ {
+ ERROR("Out Of Memory");
+ return;
+ }
+
+ // Verify the size of the path won't exceed maximum allowed size
+ if (formatBufferString.GetCount() >= MAX_TRANSPORT_NAME_LENGTH)
+ {
+ ERROR("GetApplicationContainerFolder returned a path that was larger than MAX_TRANSPORT_NAME_LENGTH");
+ return;
+ }
+ }
+ else
+#endif // __APPLE__
+ {
+ // Get a temp file location
+ dwRetVal = ::GetTempPathA(MAX_TRANSPORT_NAME_LENGTH, formatBuffer);
+ if (dwRetVal == 0)
+ {
+ ERROR("GetTempPath failed (0x%08x)", ::GetLastError());
+ return;
+ }
+ if (dwRetVal > MAX_TRANSPORT_NAME_LENGTH)
+ {
+ ERROR("GetTempPath returned a path that was larger than MAX_TRANSPORT_NAME_LENGTH");
+ return;
+ }
+ }
+
+ if (strncat_s(formatBuffer, MAX_TRANSPORT_NAME_LENGTH, IpcNameFormat, strlen(IpcNameFormat)) == STRUNCATE)
+ {
+ ERROR("TransportPipeName was larger than MAX_TRANSPORT_NAME_LENGTH");
+ return;
+ }
+
+ int chars = snprintf(name, MAX_TRANSPORT_NAME_LENGTH, formatBuffer, prefix, id, disambiguationKey, suffix);
+ _ASSERTE(chars > 0 && (unsigned int)chars < MAX_TRANSPORT_NAME_LENGTH);
+}
+
+/*++
+ Function:
+ PAL_GetTransportPipeName
+
+ Builds the transport pipe names from the process id.
+--*/
+VOID
+PALAPI
+PAL_GetTransportPipeName(
+ OUT char *name,
+ IN DWORD id,
+ IN const char *applicationGroupId,
+ IN const char *suffix)
+{
+ PAL_GetTransportName(
+ MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH,
+ name,
+ TwoWayNamedPipePrefix,
+ id,
+ applicationGroupId,
+ suffix);
+}
+
+/*++
+Function:
+ OpenProcess
+
+See MSDN doc.
+
+Notes :
+dwDesiredAccess is ignored (all supported operations will be allowed)
+bInheritHandle is ignored (no inheritance)
+--*/
+HANDLE
+PALAPI
+OpenProcess(
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ DWORD dwProcessId)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+ IPalObject *pobjProcess = NULL;
+ IPalObject *pobjProcessRegistered = NULL;
+ IDataLock *pDataLock;
+ CProcProcessLocalData *pLocalData;
+ CObjectAttributes oa;
+ HANDLE hProcess = NULL;
+
+ PERF_ENTRY(OpenProcess);
+ ENTRY("OpenProcess(dwDesiredAccess=0x%08x, bInheritHandle=%d, "
+ "dwProcessId = 0x%08x)\n",
+ dwDesiredAccess, bInheritHandle, dwProcessId );
+
+ pThread = InternalGetCurrentThread();
+
+ if (0 == dwProcessId)
+ {
+ palError = ERROR_INVALID_PARAMETER;
+ goto OpenProcessExit;
+ }
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otProcess,
+ &oa,
+ &pobjProcess
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto OpenProcessExit;
+ }
+
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto OpenProcessExit;
+ }
+
+ pLocalData->dwProcessId = dwProcessId;
+ pDataLock->ReleaseLock(pThread, TRUE);
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pobjProcess,
+ &aotProcess,
+ &hProcess,
+ &pobjProcessRegistered
+ );
+
+ //
+ // pobjProcess was invalidated by the above call, so NULL
+ // it out here
+ //
+
+ pobjProcess = NULL;
+
+ //
+ // TODO: check to see if the process actually exists?
+ //
+
+OpenProcessExit:
+
+ if (NULL != pobjProcess)
+ {
+ pobjProcess->ReleaseReference(pThread);
+ }
+
+ if (NULL != pobjProcessRegistered)
+ {
+ pobjProcessRegistered->ReleaseReference(pThread);
+ }
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("OpenProcess returns HANDLE %p\n", hProcess);
+ PERF_EXIT(OpenProcess);
+ return hProcess;
+}
+
+/*++
+Function:
+ EnumProcessModules
+
+Abstract
+ Returns a process's module list
+
+Return
+ TRUE if it succeeded, FALSE otherwise
+
+Notes
+ This API is tricky because the module handles are never closed/freed so there can't be any
+ allocations for the module handle or name strings, etc. The "handles" are actually the base
+ addresses of the modules. The module handles should only be used by GetModuleFileNameExW
+ below.
+--*/
+BOOL
+PALAPI
+EnumProcessModules(
+ IN HANDLE hProcess,
+ OUT HMODULE *lphModule,
+ IN DWORD cb,
+ OUT LPDWORD lpcbNeeded)
+{
+ PERF_ENTRY(EnumProcessModules);
+ ENTRY("EnumProcessModules(hProcess=0x%08x, cb=%d)\n", hProcess, cb);
+
+ BOOL result = TRUE;
+ DWORD count = 0;
+ ProcessModules *listHead = GetProcessModulesFromHandle(hProcess, &count);
+ if (listHead != NULL)
+ {
+ for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
+ {
+ if (cb <= 0)
+ {
+ break;
+ }
+ cb -= sizeof(HMODULE);
+ *lphModule = (HMODULE)entry->BaseAddress;
+ lphModule++;
+ }
+ }
+ else
+ {
+ result = FALSE;
+ }
+
+ if (lpcbNeeded)
+ {
+ // This return value isn't exactly up to spec because it should return the actual
+ // number of modules in the process even if "cb" isn't big enough but for our use
+ // it works just fine.
+ (*lpcbNeeded) = count * sizeof(HMODULE);
+ }
+
+ LOGEXIT("EnumProcessModules returns %d\n", result);
+ PERF_EXIT(EnumProcessModules);
+ return result;
+}
+
+/*++
+Function:
+ GetModuleFileNameExW
+
+ Used only with module handles returned from EnumProcessModule (for dbgshim).
+
+--*/
+DWORD
+PALAPI
+GetModuleFileNameExW(
+ IN HANDLE hProcess,
+ IN HMODULE hModule,
+ OUT LPWSTR lpFilename,
+ IN DWORD nSize
+)
+{
+ DWORD result = 0;
+ DWORD count = 0;
+
+ ProcessModules *listHead = GetProcessModulesFromHandle(hProcess, &count);
+ if (listHead != NULL)
+ {
+ for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
+ {
+ if ((HMODULE)entry->BaseAddress == hModule)
+ {
+ // Convert CHAR string into WCHAR string
+ result = MultiByteToWideChar(CP_ACP, 0, entry->Name, -1, lpFilename, nSize);
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*++
+Function:
+ GetProcessModulesFromHandle
+
+Abstract
+ Returns a process's module list
+
+Return
+ ProcessModules * list
+
+--*/
+ProcessModules *
+GetProcessModulesFromHandle(
+ IN HANDLE hProcess,
+ OUT LPDWORD lpCount)
+{
+ CPalThread* pThread = InternalGetCurrentThread();
+ CProcProcessLocalData *pLocalData = NULL;
+ ProcessModules *listHead = NULL;
+ IPalObject *pobjProcess = NULL;
+ IDataLock *pDataLock = NULL;
+ PAL_ERROR palError = NO_ERROR;
+ DWORD dwProcessId = 0;
+ DWORD count = 0;
+
+ _ASSERTE(lpCount != NULL);
+
+ if (hPseudoCurrentProcess == hProcess)
+ {
+ pobjProcess = g_pobjProcess;
+ pobjProcess->AddReference();
+ }
+ else
+ {
+ CAllowedObjectTypes aotProcess(otiProcess);
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hProcess,
+ &aotProcess,
+ &pobjProcess);
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(ERROR_INVALID_HANDLE);
+ goto exit;
+ }
+ }
+
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData));
+
+ _ASSERTE(NO_ERROR == palError);
+
+ dwProcessId = pLocalData->dwProcessId;
+ listHead = pLocalData->pProcessModules;
+ count = pLocalData->cProcessModules;
+
+ // If the module list hasn't been created yet, create it now
+ if (listHead == NULL)
+ {
+ listHead = CreateProcessModules(dwProcessId, &count);
+ if (listHead == NULL)
+ {
+ pThread->SetLastError(ERROR_INVALID_PARAMETER);
+ goto exit;
+ }
+
+ if (pLocalData != NULL)
+ {
+ pLocalData->pProcessModules = listHead;
+ pLocalData->cProcessModules = count;
+ }
+ }
+
+exit:
+ if (NULL != pDataLock)
+ {
+ pDataLock->ReleaseLock(pThread, TRUE);
+ }
+ if (NULL != pobjProcess)
+ {
+ pobjProcess->ReleaseReference(pThread);
+ }
+
+ *lpCount = count;
+ return listHead;
+}
+
+/*++
+Function:
+ CreateProcessModules
+
+Abstract
+ Returns a process's module list
+
+Return
+ ProcessModules * list
+
+--*/
+ProcessModules *
+CreateProcessModules(
+ IN DWORD dwProcessId,
+ OUT LPDWORD lpCount)
+{
+ ProcessModules *listHead = NULL;
+ _ASSERTE(lpCount != NULL);
+
+#if defined(__APPLE__)
+
+ // For OS X, the "vmmap" command outputs something similar to the /proc/*/maps file so popen the
+ // command and read the relevant lines:
+ //
+ // ...
+ // ==== regions for process 347 (non-writable and writable regions are interleaved)
+ // REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
+ // __TEXT 000000010446d000-0000000104475000 [ 32K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun
+ // __DATA 0000000104475000-0000000104476000 [ 4K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun
+ // __LINKEDIT 0000000104476000-000000010447a000 [ 16K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun
+ // Kernel Alloc Once 000000010447a000-000000010447b000 [ 4K] rw-/rwx SM=PRV
+ // MALLOC (admin) 000000010447b000-000000010447c000 [ 4K] r--/rwx SM=ZER
+ // ...
+ // MALLOC (admin) 00000001044ab000-00000001044ac000 [ 4K] r--/rwx SM=PRV
+ // __TEXT 00000001044ac000-0000000104c84000 [ 8032K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __TEXT 0000000104c84000-0000000104c85000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __TEXT 0000000104c85000-000000010513b000 [ 4824K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __TEXT 000000010513b000-000000010513c000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __TEXT 000000010513c000-000000010516f000 [ 204K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __DATA 000000010516f000-00000001051ce000 [ 380K] rw-/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __DATA 00000001051ce000-00000001051fa000 [ 176K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __LINKEDIT 00000001051fa000-0000000105bac000 [ 9928K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // VM_ALLOCATE 0000000105bac000-0000000105bad000 [ 4K] r--/rw- SM=SHM
+ // MALLOC (admin) 0000000105bad000-0000000105bae000 [ 4K] r--/rwx SM=ZER
+ // MALLOC 0000000105bae000-0000000105baf000 [ 4K] rw-/rwx SM=ZER
+
+ // OS X Sierra (10.12.4 Beta)
+ // REGION TYPE START - END [ VSIZE RSDNT DIRTY SWAP] PRT/MAX SHRMOD PURGE REGION DETAIL
+ // Stack 00007fff5a930000-00007fff5b130000 [ 8192K 32K 32K 0K] rw-/rwx SM=PRV thread 0
+ // __TEXT 00007fffa4a0b000-00007fffa4a0d000 [ 8K 8K 0K 0K] r-x/r-x SM=COW /usr/lib/libSystem.B.dylib
+ // __TEXT 00007fffa4bbe000-00007fffa4c15000 [ 348K 348K 0K 0K] r-x/r-x SM=COW /usr/lib/libc++.1.dylib
+
+ // NOTE: the module path can have spaces in the name
+ // __TEXT 0000000196220000-00000001965b4000 [ 3664K 2340K 0K 0K] r-x/rwx SM=COW /Volumes/Builds/builds/devmain/rawproduct/debug/build/out/Applications/Microsoft Excel.app/Contents/SharedSupport/PowerQuery/libcoreclr.dylib
+
+ // NOTE: Sometimes vmmap hides full paths to some process modules (.dylibs in non-system folders), causing debugger not to work.
+ // __TEXT 000000010d8bd000-000000010ddce000 [ 5188K 5188K 0K 0K] r-x/rwx SM=COW /Users/USER/*/libcoreclr.dylib
+ // So now we get modules information by iterating over regions using proc_pidinfo(). See dotnet/runtime#42888.
+ int count = 0;
+
+ uint64_t addr = 0;
+ while (true)
+ {
+ struct proc_regionwithpathinfo rwpi;
+ int sz = proc_pidinfo(dwProcessId, PROC_PIDREGIONPATHINFO, addr, &rwpi, sizeof rwpi);
+ if (sz != sizeof rwpi)
+ {
+ if (sz == 0 && errno == EINVAL)
+ break; // ok
+
+ DestroyProcessModules(listHead);
+ listHead = NULL;
+ count = 0;
+ break; // unknown error
+ }
+
+ const char *moduleName = rwpi.prp_vip.vip_path;
+
+ bool dup = false;
+ for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
+ {
+ if (strcmp(moduleName, entry->Name) == 0)
+ {
+ dup = true;
+ break;
+ }
+ }
+
+ if (!dup)
+ {
+ int cbModuleName = strlen(moduleName) + 1;
+ ProcessModules *entry = (ProcessModules *)InternalMalloc(sizeof(ProcessModules) + cbModuleName);
+ if (entry == NULL)
+ {
+ DestroyProcessModules(listHead);
+ listHead = NULL;
+ count = 0;
+ break; // no memory
+ }
+ memcpy_s(entry->Name, cbModuleName, moduleName, cbModuleName);
+ entry->BaseAddress = (void *)rwpi.prp_prinfo.pri_address;
+ entry->Next = listHead;
+ listHead = entry;
+ count++;
+ }
+
+ addr = rwpi.prp_prinfo.pri_address + rwpi.prp_prinfo.pri_size;
+ }
+
+ *lpCount = count;
+
+#elif HAVE_PROCFS_MAPS
+
+ // Here we read /proc/<pid>/maps file in order to parse it and figure out what it says
+ // about a library we are looking for. This file looks something like this:
+ //
+ // [address] [perms] [offset] [dev] [inode] [pathname] - HEADER is not preset in an actual file
+ //
+ // 35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so
+ // 35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so
+ // 35b1a20000-35b1a21000 rw-p 00020000 08:02 135522 /usr/lib64/ld-2.15.so
+ // 35b1a21000-35b1a22000 rw-p 00000000 00:00 0 [heap]
+ // 35b1c00000-35b1dac000 r-xp 00000000 08:02 135870 /usr/lib64/libc-2.15.so
+ // 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so
+ // 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so
+ // 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870 /usr/lib64/libc-2.15.so
+
+ // Making something like: /proc/123/maps
+ char mapFileName[100];
+ char *line = NULL;
+ size_t lineLen = 0;
+ int count = 0;
+ ssize_t read;
+
+ INDEBUG(int chars = )
+ snprintf(mapFileName, sizeof(mapFileName), "/proc/%d/maps", dwProcessId);
+ _ASSERTE(chars > 0 && chars <= (int)sizeof(mapFileName));
+
+ FILE *mapsFile = fopen(mapFileName, "r");
+ if (mapsFile == NULL)
+ {
+ goto exit;
+ }
+
+ // Reading maps file line by line
+ while ((read = getline(&line, &lineLen, mapsFile)) != -1)
+ {
+ void *startAddress, *endAddress, *offset;
+ int devHi, devLo, inode;
+ char moduleName[PATH_MAX];
+
+ if (sscanf_s(line, "%p-%p %*[-rwxsp] %p %x:%x %d %s\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, moduleName, ARRAY_SIZE(moduleName)) == 7)
+ {
+ if (inode != 0)
+ {
+ bool dup = false;
+ for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
+ {
+ if (strcmp(moduleName, entry->Name) == 0)
+ {
+ dup = true;
+ break;
+ }
+ }
+
+ if (!dup)
+ {
+ int cbModuleName = strlen(moduleName) + 1;
+ ProcessModules *entry = (ProcessModules *)InternalMalloc(sizeof(ProcessModules) + cbModuleName);
+ if (entry == NULL)
+ {
+ DestroyProcessModules(listHead);
+ listHead = NULL;
+ count = 0;
+ break;
+ }
+ strcpy_s(entry->Name, cbModuleName, moduleName);
+ entry->BaseAddress = startAddress;
+ entry->Next = listHead;
+ listHead = entry;
+ count++;
+ }
+ }
+ }
+ }
+
+ *lpCount = count;
+
+ free(line); // We didn't allocate line, but as per contract of getline we should free it
+ fclose(mapsFile);
+exit:
+
+#else
+ _ASSERTE(!"Not implemented on this platform");
+#endif
+ return listHead;
+}
+
+/*++
+Function:
+ DestroyProcessModules
+
+Abstract
+ Cleans up the process module table.
+
+Return
+ None
+
+--*/
+VOID
+DestroyProcessModules(IN ProcessModules *listHead)
+{
+ for (ProcessModules *entry = listHead; entry != NULL; )
+ {
+ ProcessModules *next = entry->Next;
+ free(entry);
+ entry = next;
+ }
+}
+
+/*++
+Function:
+ PROCAbort()
+
+ Aborts the process after calling the shutdown cleanup handler. This function
+ should be called instead of calling abort() directly.
+
+Parameters:
+ none
+
+ Does not return
+--*/
+PAL_NORETURN
+VOID
+PROCAbort()
+{
+ // Abort the process after waiting for the core dump to complete
+ abort();
+}
+
+/*++
+Function:
+ PROCGetProcessIDFromHandle
+
+Abstract
+ Return the process ID from a process handle
+
+Parameter
+ hProcess: process handle
+
+Return
+ Return the process ID, or 0 if it's not a valid handle
+--*/
+DWORD
+PROCGetProcessIDFromHandle(
+ HANDLE hProcess)
+{
+ PAL_ERROR palError;
+ IPalObject *pobjProcess = NULL;
+ CPalThread *pThread = InternalGetCurrentThread();
+
+ DWORD dwProcessId = 0;
+
+ if (hPseudoCurrentProcess == hProcess)
+ {
+ dwProcessId = gPID;
+ goto PROCGetProcessIDFromHandleExit;
+ }
+
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hProcess,
+ &aotProcess,
+ &pobjProcess
+ );
+
+ if (NO_ERROR == palError)
+ {
+ IDataLock *pDataLock;
+ CProcProcessLocalData *pLocalData;
+
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR == palError)
+ {
+ dwProcessId = pLocalData->dwProcessId;
+ pDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ pobjProcess->ReleaseReference(pThread);
+ }
+
+PROCGetProcessIDFromHandleExit:
+
+ return dwProcessId;
+}
+
+PAL_ERROR
+CorUnix::InitializeProcessData(
+ void
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ bool fLockInitialized = FALSE;
+
+ pGThreadList = NULL;
+ g_dwThreadCount = 0;
+
+ InternalInitializeCriticalSection(&g_csProcess);
+ fLockInitialized = TRUE;
+
+ if (NO_ERROR != palError)
+ {
+ if (fLockInitialized)
+ {
+ InternalDeleteCriticalSection(&g_csProcess);
+ }
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ CreateInitialProcessAndThreadObjects
+
+Abstract
+ Creates the IPalObjects that represent the current process
+ and the initial thread
+
+Parameter
+ pThread - the initial thread
+
+Return
+ PAL_ERROR
+--*/
+
+PAL_ERROR
+CorUnix::CreateInitialProcessAndThreadObjects(
+ CPalThread *pThread
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ HANDLE hThread;
+ IPalObject *pobjProcess = NULL;
+ IDataLock *pDataLock;
+ CProcProcessLocalData *pLocalData;
+ CObjectAttributes oa;
+ HANDLE hProcess;
+
+ //
+ // Create initial thread object
+ //
+
+ palError = CreateThreadObject(pThread, pThread, &hThread);
+ if (NO_ERROR != palError)
+ {
+ goto CreateInitialProcessAndThreadObjectsExit;
+ }
+
+ //
+ // This handle isn't needed
+ //
+
+ (void) g_pObjectManager->RevokeHandle(pThread, hThread);
+
+ //
+ // Create and initialize process object
+ //
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otProcess,
+ &oa,
+ &pobjProcess
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to allocate process object");
+ goto CreateInitialProcessAndThreadObjectsExit;
+ }
+
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to access local data");
+ goto CreateInitialProcessAndThreadObjectsExit;
+ }
+
+ pLocalData->dwProcessId = gPID;
+ pLocalData->ps = PS_RUNNING;
+ pDataLock->ReleaseLock(pThread, TRUE);
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pobjProcess,
+ &aotProcess,
+ &hProcess,
+ &g_pobjProcess
+ );
+
+ //
+ // pobjProcess is invalidated by the call to RegisterObject, so
+ // NULL it out here to prevent it from being released later
+ //
+
+ pobjProcess = NULL;
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Failure registering process object");
+ goto CreateInitialProcessAndThreadObjectsExit;
+ }
+
+ //
+ // There's no need to keep this handle around, so revoke
+ // it now
+ //
+
+ g_pObjectManager->RevokeHandle(pThread, hProcess);
+
+CreateInitialProcessAndThreadObjectsExit:
+
+ if (NULL != pobjProcess)
+ {
+ pobjProcess->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+
+/*++
+Function:
+ PROCCleanupInitialProcess
+
+Abstract
+ Cleanup all the structures for the initial process.
+
+Parameter
+ VOID
+
+Return
+ VOID
+
+--*/
+VOID
+PROCCleanupInitialProcess(VOID)
+{
+ CPalThread *pThread = InternalGetCurrentThread();
+
+ InternalEnterCriticalSection(pThread, &g_csProcess);
+
+ /* Free the application directory */
+ free(g_lpwstrAppDir);
+
+ /* Free the stored command line */
+ free(g_lpwstrCmdLine);
+
+ InternalLeaveCriticalSection(pThread, &g_csProcess);
+
+ //
+ // Object manager shutdown will handle freeing the underlying
+ // thread and process data
+ //
+
+}
+
+/*++
+Function:
+ PROCAddThread
+
+Abstract
+ Add a thread to the thread list of the current process
+
+Parameter
+ pThread: Thread object
+
+--*/
+VOID
+CorUnix::PROCAddThread(
+ CPalThread *pCurrentThread,
+ CPalThread *pTargetThread
+ )
+{
+ /* protect the access of the thread list with critical section for
+ mutithreading access */
+ InternalEnterCriticalSection(pCurrentThread, &g_csProcess);
+
+ pTargetThread->SetNext(pGThreadList);
+ pGThreadList = pTargetThread;
+ g_dwThreadCount += 1;
+
+ TRACE("Thread 0x%p (id %#x) added to the process thread list\n",
+ pTargetThread, pTargetThread->GetThreadId());
+
+ InternalLeaveCriticalSection(pCurrentThread, &g_csProcess);
+}
+
+
+/*++
+Function:
+ PROCRemoveThread
+
+Abstract
+ Remove a thread form the thread list of the current process
+
+Parameter
+ CPalThread *pThread : thread object to remove
+
+(no return value)
+--*/
+VOID
+CorUnix::PROCRemoveThread(
+ CPalThread *pCurrentThread,
+ CPalThread *pTargetThread
+ )
+{
+ CPalThread *curThread, *prevThread;
+
+ /* protect the access of the thread list with critical section for
+ mutithreading access */
+ InternalEnterCriticalSection(pCurrentThread, &g_csProcess);
+
+ curThread = pGThreadList;
+
+ /* if thread list is empty */
+ if (curThread == NULL)
+ {
+ ASSERT("Thread list is empty.\n");
+ goto EXIT;
+ }
+
+ /* do we remove the first thread? */
+ if (curThread == pTargetThread)
+ {
+ pGThreadList = curThread->GetNext();
+ TRACE("Thread 0x%p (id %#x) removed from the process thread list\n",
+ pTargetThread, pTargetThread->GetThreadId());
+ goto EXIT;
+ }
+
+ prevThread = curThread;
+ curThread = curThread->GetNext();
+ /* find the thread to remove */
+ while (curThread != NULL)
+ {
+ if (curThread == pTargetThread)
+ {
+ /* found, fix the chain list */
+ prevThread->SetNext(curThread->GetNext());
+ g_dwThreadCount -= 1;
+ TRACE("Thread %p removed from the process thread list\n", pTargetThread);
+ goto EXIT;
+ }
+
+ prevThread = curThread;
+ curThread = curThread->GetNext();
+ }
+
+ WARN("Thread %p not removed (it wasn't found in the list)\n", pTargetThread);
+
+EXIT:
+ InternalLeaveCriticalSection(pCurrentThread, &g_csProcess);
+}
+
+
+/*++
+Function:
+ PROCProcessLock
+
+Abstract
+ Enter the critical section associated to the current process
+
+Parameter
+ void
+
+Return
+ void
+--*/
+VOID
+PROCProcessLock(
+ VOID)
+{
+ CPalThread * pThread =
+ (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : NULL);
+
+ InternalEnterCriticalSection(pThread, &g_csProcess);
+}
+
+
+/*++
+Function:
+ PROCProcessUnlock
+
+Abstract
+ Leave the critical section associated to the current process
+
+Parameter
+ void
+
+Return
+ void
+--*/
+VOID
+PROCProcessUnlock(
+ VOID)
+{
+ CPalThread * pThread =
+ (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : NULL);
+
+ InternalLeaveCriticalSection(pThread, &g_csProcess);
+}
+
+
+/*++
+Function:
+ TerminateCurrentProcessNoExit
+
+Abstract:
+ Terminate current Process, but leave the caller alive
+
+Parameters:
+ BOOL bTerminateUnconditionally - If this is set, the PAL will exit as
+ quickly as possible. In particular, it will not unload DLLs.
+
+Return value :
+ No return
+
+Note:
+ This function is used in ExitThread and TerminateProcess
+
+--*/
+VOID
+CorUnix::TerminateCurrentProcessNoExit(BOOL bTerminateUnconditionally)
+{
+ BOOL locked;
+ DWORD old_terminator;
+
+ old_terminator = InterlockedCompareExchange(&terminator, GetCurrentThreadId(), 0);
+
+ if (0 != old_terminator && GetCurrentThreadId() != old_terminator)
+ {
+ /* another thread has already initiated the termination process. we
+ could just block on the PALInitLock critical section, but then
+ PROCSuspendOtherThreads would hang... so sleep forever here, we're
+ terminating anyway
+
+ Update: [TODO] PROCSuspendOtherThreads has been removed. Can this
+ code be changed? */
+
+ /* note that if *this* thread has already started the termination
+ process, we want to proceed. the only way this can happen is if a
+ call to DllMain (from ExitProcess) brought us here (because DllMain
+ called ExitProcess, or TerminateProcess, or ExitThread);
+ TerminateProcess won't call DllMain, so there's no danger to get
+ caught in an infinite loop */
+ WARN("termination already started from another thread; blocking.\n");
+ poll(NULL, 0, INFTIM);
+ }
+
+ /* Try to lock the initialization count to prevent multiple threads from
+ terminating/initializing the PAL simultaneously */
+
+ /* note : it's also important to take this lock before the process lock,
+ because Init/Shutdown take the init lock, and the functions they call
+ may take the process lock. We must do it in the same order to avoid
+ deadlocks */
+
+ locked = PALInitLock();
+ if(locked && PALIsInitialized())
+ {
+ PALCommonCleanup();
+ }
+}
+#ifdef __APPLE__
+bool GetApplicationContainerFolder(PathCharString& buffer, const char *applicationGroupId, int applicationGroupIdLength)
+{
+ const char *homeDir = getpwuid(getuid())->pw_dir;
+ int homeDirLength = strlen(homeDir);
+
+ // The application group container folder is defined as:
+ // /user/{loginname}/Library/Group Containers/{AppGroupId}/
+ return buffer.Set(homeDir, homeDirLength)
+ && buffer.Append(APPLICATION_CONTAINER_BASE_PATH_SUFFIX)
+ && buffer.Append(applicationGroupId, applicationGroupIdLength)
+ && buffer.Append('/');
+}
+#endif // __APPLE__
+
+
+/* Internal function definitions **********************************************/
+
+/*++
+Function:
+ getFileName
+
+Abstract:
+ Helper function for CreateProcessW, it retrieves the executable filename
+ from the application name, and the command line.
+
+Parameters:
+ IN lpApplicationName: first parameter from CreateProcessW (an unicode string)
+ IN lpCommandLine: second parameter from CreateProcessW (an unicode string)
+ OUT lpFileName: file to be executed (the new process)
+
+Return:
+ TRUE: if the file name is retrieved
+ FALSE: otherwise
+
+--*/
+static
+BOOL
+getFileName(
+ LPCWSTR lpApplicationName,
+ LPWSTR lpCommandLine,
+ PathCharString& lpPathFileName)
+{
+ LPWSTR lpEnd;
+ WCHAR wcEnd;
+ char * lpFileName;
+ PathCharString lpFileNamePS;
+ char *lpTemp;
+
+ if (lpApplicationName)
+ {
+ int length = WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1,
+ NULL, 0, NULL, NULL);
+
+ /* if only a file name is specified, prefix it with "./" */
+ if ((*lpApplicationName != '.') && (*lpApplicationName != '/') &&
+ (*lpApplicationName != '\\'))
+ {
+ length += 2;
+ lpTemp = lpPathFileName.OpenStringBuffer(length);
+
+ if (strcpy_s(lpTemp, length, "./") != SAFECRT_SUCCESS)
+ {
+ ERROR("strcpy_s failed!\n");
+ return FALSE;
+ }
+ lpTemp+=2;
+
+ }
+ else
+ {
+ lpTemp = lpPathFileName.OpenStringBuffer(length);
+ }
+
+ /* Convert to ASCII */
+ length = WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1,
+ lpTemp, length, NULL, NULL);
+ if (length == 0)
+ {
+ lpPathFileName.CloseBuffer(0);
+ ASSERT("WideCharToMultiByte failure\n");
+ return FALSE;
+ }
+
+ lpPathFileName.CloseBuffer(length -1);
+
+ /* Replace '\' by '/' */
+ FILEDosToUnixPathA(lpTemp);
+
+ return TRUE;
+ }
+ else
+ {
+ /* use the Command line */
+
+ /* filename should be the first token of the command line */
+
+ /* first skip all leading whitespace */
+ lpCommandLine = UTIL_inverse_wcspbrk(lpCommandLine,W16_WHITESPACE);
+ if(NULL == lpCommandLine)
+ {
+ ERROR("CommandLine contains only whitespace!\n");
+ return FALSE;
+ }
+
+ /* check if it is starting with a quote (") character */
+ if (*lpCommandLine == 0x0022)
+ {
+ lpCommandLine++; /* skip the quote */
+
+ /* file name ends with another quote */
+ lpEnd = PAL_wcschr(lpCommandLine+1, 0x0022);
+
+ /* if no quotes found, set lpEnd to the end of the Command line */
+ if (lpEnd == NULL)
+ lpEnd = lpCommandLine + PAL_wcslen(lpCommandLine);
+ }
+ else
+ {
+ /* filename is end out by a whitespace */
+ lpEnd = PAL_wcspbrk(lpCommandLine, W16_WHITESPACE);
+
+ /* if no whitespace found, set lpEnd to end of the Command line */
+ if (lpEnd == NULL)
+ {
+ lpEnd = lpCommandLine + PAL_wcslen(lpCommandLine);
+ }
+ }
+
+ if (lpEnd == lpCommandLine)
+ {
+ ERROR("application name and command line are both empty!\n");
+ return FALSE;
+ }
+
+ /* replace the last character by a null */
+ wcEnd = *lpEnd;
+ *lpEnd = 0x0000;
+
+ /* Convert to ASCII */
+ int size = 0;
+ int length = (PAL_wcslen(lpCommandLine)+1) * sizeof(WCHAR);
+ lpFileName = lpFileNamePS.OpenStringBuffer(length);
+ if (NULL == lpFileName)
+ {
+ ERROR("Not Enough Memory!\n");
+ return FALSE;
+ }
+ if (!(size = WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1,
+ lpFileName, length, NULL, NULL)))
+ {
+ ASSERT("WideCharToMultiByte failure\n");
+ return FALSE;
+ }
+
+ lpFileNamePS.CloseBuffer(size - 1);
+ /* restore last character */
+ *lpEnd = wcEnd;
+
+ /* Replace '\' by '/' */
+ FILEDosToUnixPathA(lpFileName);
+ if (!getPath(lpFileNamePS, lpPathFileName))
+ {
+ /* file is not in the path */
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*++
+Function:
+ checkFileType
+
+Abstract:
+ Return the type of the file.
+
+Parameters:
+ IN lpFileName: file name
+
+Return:
+ FILE_DIR: Directory
+ FILE_UNIX: Unix executable file
+ FILE_ERROR: Error
+--*/
+static
+int
+checkFileType( LPCSTR lpFileName)
+{
+ struct stat stat_data;
+
+ /* check if the file exist */
+ if ( access(lpFileName, F_OK) != 0 )
+ {
+ return FILE_ERROR;
+ }
+
+ /* if it's not a PE/COFF file, check if it is executable */
+ if ( -1 != stat( lpFileName, &stat_data ) )
+ {
+ if((stat_data.st_mode & S_IFMT) == S_IFDIR )
+ {
+ /*The given file is a directory*/
+ return FILE_DIR;
+ }
+ if ( UTIL_IsExecuteBitsSet( &stat_data ) )
+ {
+ return FILE_UNIX;
+ }
+ else
+ {
+ return FILE_ERROR;
+ }
+ }
+ return FILE_ERROR;
+
+}
+
+
+/*++
+Function:
+ buildArgv
+
+Abstract:
+ Helper function for CreateProcessW, it builds the array of argument in
+ a format than can be passed to execve function.lppArgv is allocated
+ in this function and must be freed by the caller.
+
+Parameters:
+ IN lpCommandLine: second parameter from CreateProcessW (an unicode string)
+ IN lpAppPath: cannonical name of the application to launched
+ OUT lppArgv: array of arguments to be passed to the new process
+
+Return:
+ the number of arguments
+
+note: this doesn't yet match precisely the behavior of Windows, but should be
+sufficient.
+what's here:
+1) stripping nonquoted whitespace
+2) handling of quoted parameters and quoted "parts" of parameters, removal of
+ doublequotes (<aaaa"b bbb b"ccc> becomes <aaaab bbb bccc>)
+3) \" as an escaped doublequote, both within doublequoted sequences and out
+what's known missing :
+1) \\ as an escaped backslash, but only if the string of '\'
+ is followed by a " (escaped or not)
+2) "alternate" escape sequence : double-doublequote within a double-quoted
+ argument (<"aaa a""aa aaa">) expands to a single-doublequote(<aaa a"aa aaa>)
+note that there may be other special cases
+--*/
+static
+char **
+buildArgv(
+ LPCWSTR lpCommandLine,
+ PathCharString& lpAppPath,
+ UINT *pnArg)
+{
+ CPalThread *pThread = NULL;
+ UINT iWlen;
+ char *lpAsciiCmdLine;
+ char *pChar;
+ char **lppArgv;
+ char **lppTemp;
+ UINT i,j;
+
+ *pnArg = 0;
+
+ iWlen = WideCharToMultiByte(CP_ACP,0,lpCommandLine,-1,NULL,0,NULL,NULL);
+
+ if(0 == iWlen)
+ {
+ ASSERT("Can't determine length of command line\n");
+ return NULL;
+ }
+
+ pThread = InternalGetCurrentThread();
+ /* make sure to allocate enough space, up for the worst case scenario */
+ int iLength = (iWlen + lpAppPath.GetCount() + 2);
+ lpAsciiCmdLine = (char *) InternalMalloc(iLength);
+
+ if (lpAsciiCmdLine == NULL)
+ {
+ ERROR("Unable to allocate memory\n");
+ return NULL;
+ }
+
+ pChar = lpAsciiCmdLine;
+
+ /* put the cannonical name of the application as the first parameter */
+ if ((strcpy_s(lpAsciiCmdLine, iLength, "\"") != SAFECRT_SUCCESS) ||
+ (strcat_s(lpAsciiCmdLine, iLength, lpAppPath) != SAFECRT_SUCCESS) ||
+ (strcat_s(lpAsciiCmdLine, iLength, "\"") != SAFECRT_SUCCESS) ||
+ (strcat_s(lpAsciiCmdLine, iLength, " ") != SAFECRT_SUCCESS))
+ {
+ ERROR("strcpy_s/strcat_s failed!\n");
+ return NULL;
+ }
+
+ pChar = lpAsciiCmdLine + strlen (lpAsciiCmdLine);
+
+ /* let's skip the first argument in the command line */
+
+ /* strip leading whitespace; function returns NULL if there's only
+ whitespace, so the if statement below will work correctly */
+ lpCommandLine = UTIL_inverse_wcspbrk((LPWSTR)lpCommandLine, W16_WHITESPACE);
+
+ if (lpCommandLine)
+ {
+ LPCWSTR stringstart = lpCommandLine;
+
+ do
+ {
+ /* find first whitespace or dquote character */
+ lpCommandLine = PAL_wcspbrk(lpCommandLine,W16_WHITESPACE_DQUOTE);
+ if(NULL == lpCommandLine)
+ {
+ /* no whitespace or dquote found : first arg is only arg */
+ break;
+ }
+ else if('"' == *lpCommandLine)
+ {
+ /* got a dquote; skip over it if it's escaped; make sure we
+ don't try to look before the first character in the
+ string */
+ if(lpCommandLine > stringstart && '\\' == lpCommandLine[-1])
+ {
+ lpCommandLine++;
+ continue;
+ }
+
+ /* found beginning of dquoted sequence, run to the end */
+ /* don't stop if we hit an escaped dquote */
+ lpCommandLine++;
+ while( *lpCommandLine )
+ {
+ lpCommandLine = PAL_wcschr(lpCommandLine, '"');
+ if(NULL == lpCommandLine)
+ {
+ /* no ending dquote, arg runs to end of string */
+ break;
+ }
+ if('\\' != lpCommandLine[-1])
+ {
+ /* dquote is not escaped, dquoted sequence is over*/
+ break;
+ }
+ lpCommandLine++;
+ }
+ if(NULL == lpCommandLine || '\0' == *lpCommandLine)
+ {
+ /* no terminating dquote */
+ break;
+ }
+
+ /* step over dquote, keep looking for end of arg */
+ lpCommandLine++;
+ }
+ else
+ {
+ /* found whitespace : end of arg. */
+ lpCommandLine++;
+ break;
+ }
+ }while(lpCommandLine);
+ }
+
+ /* Convert to ASCII */
+ if (lpCommandLine)
+ {
+ if (!WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1,
+ pChar, iWlen+1, NULL, NULL))
+ {
+ ASSERT("Unable to convert to a multibyte string\n");
+ free(lpAsciiCmdLine);
+ return NULL;
+ }
+ }
+
+ pChar = lpAsciiCmdLine;
+
+ /* loops through all the arguments, to find out how many arguments there
+ are; while looping replace whitespace by \0 */
+
+ /* skip leading whitespace (and replace by '\0') */
+ /* note : there shouldn't be any, command starts either with PE loader name
+ or computed application path, but this won't hurt */
+ while (*pChar)
+ {
+ if (!isspace((unsigned char) *pChar))
+ {
+ break;
+ }
+ WARN("unexpected whitespace in command line!\n");
+ *pChar++ = '\0';
+ }
+
+ while (*pChar)
+ {
+ (*pnArg)++;
+
+ /* find end of current arg */
+ while(*pChar && !isspace((unsigned char) *pChar))
+ {
+ if('"' == *pChar)
+ {
+ /* skip over dquote if it's escaped; make sure we don't try to
+ look before the start of the string for the \ */
+ if(pChar > lpAsciiCmdLine && '\\' == pChar[-1])
+ {
+ pChar++;
+ continue;
+ }
+
+ /* found leading dquote : look for ending dquote */
+ pChar++;
+ while (*pChar)
+ {
+ pChar = strchr(pChar,'"');
+ if(NULL == pChar)
+ {
+ /* no ending dquote found : argument extends to the end
+ of the string*/
+ break;
+ }
+ if('\\' != pChar[-1])
+ {
+ /* found a dquote, and it's not escaped : quoted
+ sequence is over*/
+ break;
+ }
+ /* found a dquote, but it was escaped : skip over it, keep
+ looking */
+ pChar++;
+ }
+ if(NULL == pChar || '\0' == *pChar)
+ {
+ /* reached the end of the string : we're done */
+ break;
+ }
+ }
+ pChar++;
+ }
+ if(NULL == pChar)
+ {
+ /* reached the end of the string : we're done */
+ break;
+ }
+ /* reached end of arg; replace trailing whitespace by '\0', to split
+ arguments into separate strings */
+ while (isspace((unsigned char) *pChar))
+ {
+ *pChar++ = '\0';
+ }
+ }
+
+ /* allocate lppargv according to the number of arguments
+ in the command line */
+ lppArgv = (char **) InternalMalloc((((*pnArg)+1) * sizeof(char *)));
+
+ if (lppArgv == NULL)
+ {
+ free(lpAsciiCmdLine);
+ return NULL;
+ }
+
+ lppTemp = lppArgv;
+
+ /* at this point all parameters are separated by NULL
+ we need to fill the array of arguments; we must also remove all dquotes
+ from arguments (new process shouldn't see them) */
+ for (i = *pnArg, pChar = lpAsciiCmdLine; i; i--)
+ {
+ /* skip NULLs */
+ while (!*pChar)
+ {
+ pChar++;
+ }
+
+ *lppTemp = pChar;
+
+ /* go to the next parameter, removing dquotes as we go along */
+ j = 0;
+ while (*pChar)
+ {
+ /* copy character if it's not a dquote */
+ if('"' != *pChar)
+ {
+ /* if it's the \ of an escaped dquote, skip over it, we'll
+ copy the " instead */
+ if( '\\' == pChar[0] && '"' == pChar[1] )
+ {
+ pChar++;
+ }
+ (*lppTemp)[j++] = *pChar;
+ }
+ pChar++;
+ }
+ /* re-NULL terminate the argument */
+ (*lppTemp)[j] = '\0';
+
+ lppTemp++;
+ }
+
+ *lppTemp = NULL;
+
+ return lppArgv;
+}
+
+
+/*++
+Function:
+ getPath
+
+Abstract:
+ Helper function for CreateProcessW, it looks in the path environment
+ variable to find where the process to executed is.
+
+Parameters:
+ IN lpFileName: file name to search in the path
+ OUT lpPathFileName: returned string containing the path and the filename
+
+Return:
+ TRUE if found
+ FALSE otherwise
+--*/
+static
+BOOL
+getPath(
+ PathCharString& lpFileNameString,
+ PathCharString& lpPathFileName)
+{
+ LPSTR lpPath;
+ LPSTR lpNext;
+ LPSTR lpCurrent;
+ LPWSTR lpwstr;
+ INT n;
+ INT nextLen;
+ INT slashLen;
+ CPalThread *pThread = NULL;
+ LPCSTR lpFileName = lpFileNameString.GetString();
+
+ /* if a path is specified, only look there */
+ if(strchr(lpFileName, '/'))
+ {
+ if (access (lpFileName, F_OK) == 0)
+ {
+ if (!lpPathFileName.Set(lpFileNameString))
+ {
+ TRACE("Set of StackString failed!\n");
+ return FALSE;
+ }
+
+ TRACE("file %s exists\n", lpFileName);
+ return TRUE;
+ }
+ else
+ {
+ TRACE("file %s doesn't exist.\n", lpFileName);
+ return FALSE;
+ }
+ }
+
+ /* first look in directory from which the application loaded */
+ lpwstr = g_lpwstrAppDir;
+
+ if (lpwstr)
+ {
+ /* convert path to multibyte, check buffer size */
+ n = WideCharToMultiByte(CP_ACP, 0, lpwstr, -1, NULL, 0,
+ NULL, NULL);
+
+ if (!lpPathFileName.Reserve(n + lpFileNameString.GetCount() + 1 ))
+ {
+ ERROR("StackString Reserve failed!\n");
+ return FALSE;
+ }
+
+ lpPath = lpPathFileName.OpenStringBuffer(n);
+
+ n = WideCharToMultiByte(CP_ACP, 0, lpwstr, -1, lpPath, n,
+ NULL, NULL);
+
+ if (n == 0)
+ {
+ lpPathFileName.CloseBuffer(0);
+ ASSERT("WideCharToMultiByte failure!\n");
+ return FALSE;
+ }
+
+ lpPathFileName.CloseBuffer(n - 1);
+
+ lpPathFileName.Append("/", 1);
+ lpPathFileName.Append(lpFileNameString);
+
+ if (access(lpPathFileName, F_OK) == 0)
+ {
+ TRACE("found %s in application directory (%s)\n", lpFileName, lpPathFileName.GetString());
+ return TRUE;
+ }
+ }
+
+ /* then try the current directory */
+ if (!lpPathFileName.Reserve(lpFileNameString.GetCount() + 2))
+ {
+ ERROR("StackString Reserve failed!\n");
+ return FALSE;
+ }
+
+ lpPathFileName.Set("./", 2);
+ lpPathFileName.Append(lpFileNameString);
+
+ if (access (lpPathFileName, R_OK) == 0)
+ {
+ TRACE("found %s in current directory.\n", lpFileName);
+ return TRUE;
+ }
+
+ pThread = InternalGetCurrentThread();
+
+ /* Then try to look in the path */
+ lpPath = EnvironGetenv("PATH");
+
+ if (!lpPath)
+ {
+ ERROR("EnvironGetenv returned NULL for $PATH\n");
+ return FALSE;
+ }
+
+ lpNext = lpPath;
+
+ /* search in every path directory */
+ TRACE("looking for file %s in $PATH (%s)\n", lpFileName, lpPath);
+ while (lpNext)
+ {
+ /* skip all leading ':' */
+ while(*lpNext==':')
+ {
+ lpNext++;
+ }
+
+ /* search for ':' */
+ lpCurrent = strchr(lpNext, ':');
+ if (lpCurrent)
+ {
+ *lpCurrent++ = '\0';
+ }
+
+ nextLen = strlen(lpNext);
+ slashLen = (lpNext[nextLen-1] == '/') ? 0:1;
+
+ if (!lpPathFileName.Reserve(nextLen + lpFileNameString.GetCount() + 1))
+ {
+ free(lpPath);
+ ERROR("StackString ran out of memory for full path\n");
+ return FALSE;
+ }
+
+ lpPathFileName.Set(lpNext, nextLen);
+
+ if( slashLen == 1)
+ {
+ /* append a '/' if there's no '/' at the end of the path */
+ lpPathFileName.Append("/", 1);
+ }
+
+ lpPathFileName.Append(lpFileNameString);
+
+ if ( access (lpPathFileName, F_OK) == 0)
+ {
+ TRACE("Found %s in $PATH element %s\n", lpFileName, lpNext);
+ free(lpPath);
+ return TRUE;
+ }
+
+ lpNext = lpCurrent; /* search in the next directory */
+ }
+
+ free(lpPath);
+ TRACE("File %s not found in $PATH\n", lpFileName);
+ return FALSE;
+}
+
+/*++
+Function:
+ ~CProcProcessLocalData
+
+Process data destructor
+--*/
+CorUnix::CProcProcessLocalData::~CProcProcessLocalData()
+{
+ if (pProcessModules != NULL)
+ {
+ DestroyProcessModules(pProcessModules);
+ }
+}
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*++
+
+
+
+Module Name:
+
+ thread/procprivate.hpp
+
+Abstract:
+
+ Private process structures and routines
+
+Revision History:
+
+
+
+--*/
+
+#ifndef _PAL_PROCPRIVATE_HPP_
+#define _PAL_PROCPRIVATE_HPP_
+
+#include "pal/thread.hpp"
+
+namespace CorUnix
+{
+
+ /*++
+ Function:
+ PROCAddThread
+
+ Abstract
+ Add a thread to the thread list of the current process
+ --*/
+ void PROCAddThread(CPalThread *pCurrentThread, CPalThread *pTargetThread);
+
+ extern CPalThread *pGThreadList;
+
+ /*++
+ Function:
+ PROCRemoveThread
+
+ Abstract
+ Remove a thread form the thread list of the current process
+ --*/
+ void PROCRemoveThread(CPalThread *pCurrentThread, CPalThread *pTargetThread);
+
+ /*++
+ Function:
+ PROCGetNumberOfThreads
+
+ Abstract
+ Return the number of threads in the thread list.
+ --*/
+ INT PROCGetNumberOfThreads(void);
+
+
+ /*++
+ Function:
+ TerminateCurrentProcessNoExit
+
+ Parameters:
+ BOOL bTerminateUnconditionally - If this is set, the PAL will exit as
+ quickly as possible. In particular, it will not unload DLLs.
+
+ Abstract:
+ Terminate Current Process, but leave the caller alive
+ --*/
+ void TerminateCurrentProcessNoExit(BOOL bTerminateUnconditionally);
+
+}
+
+#endif //_PAL_PROCPRIVATE_HPP_
+
+
#include "pal/corunix.hpp"
#include "pal/thread.hpp"
+#include "pal/mutex.hpp"
#include "pal/handlemgr.hpp"
#include "pal/cs.hpp"
+#include "procprivate.hpp"
#include "pal/process.h"
#include "pal/module.h"
#include "pal/environ.h"
#include "pal/init.h"
#include "pal/utils.h"
+#include "pal/virtual.h"
#if defined(__NetBSD__) && !HAVE_PTHREAD_GETCPUCLOCKID
#include <sys/cdefs.h>
using namespace CorUnix;
+void
+ThreadCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup,
+ bool fShutdown,
+ bool fCleanupSharedState
+ );
+
+PAL_ERROR
+ThreadInitializationRoutine(
+ CPalThread *pThread,
+ CObjectType *pObjectType,
+ void *pImmutableData,
+ void *pSharedData,
+ void *pProcessLocalData
+ );
+
+CObjectType CorUnix::otThread(
+ otiThread,
+ ThreadCleanupRoutine,
+ ThreadInitializationRoutine,
+ 0, // sizeof(CThreadImmutableData),
+ NULL, // No immutable data copy routine
+ NULL, // No immutable data cleanup routine
+ sizeof(CThreadProcessLocalData),
+ NULL, // No process local data cleanup routine
+ 0, // sizeof(CThreadSharedData),
+ 0, // THREAD_ALL_ACCESS,
+ CObjectType::SecuritySupported,
+ CObjectType::SecurityInfoNotPersisted,
+ CObjectType::UnnamedObject,
+ CObjectType::LocalDuplicationOnly,
+ CObjectType::WaitableObject,
+ CObjectType::SingleTransitionObject,
+ CObjectType::ThreadReleaseHasNoSideEffects,
+ CObjectType::NoOwner
+ );
+
+CAllowedObjectTypes aotThread(otiThread);
+
/*++
Function:
InternalEndCurrentThreadWrapper
will lock its own critical section */
LOADCallDllMain(DLL_THREAD_DETACH, NULL);
+ InternalEndCurrentThread(pThread);
pthread_setspecific(thObjKey, NULL);
}
free(pThread);
}
+/*++
+Function:
+ GetCurrentThreadId
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+GetCurrentThreadId(
+ VOID)
+{
+ DWORD dwThreadId;
+
+ PERF_ENTRY(GetCurrentThreadId);
+ ENTRY("GetCurrentThreadId()\n");
+
+ //
+ // TODO: should do perf test to see how this compares
+ // with calling InternalGetCurrentThread (i.e., is our lookaside
+ // cache faster on average than pthread_self?)
+ //
+
+ dwThreadId = (DWORD)THREADSilentGetCurrentThreadId();
+
+ LOGEXIT("GetCurrentThreadId returns DWORD %#x\n", dwThreadId);
+ PERF_EXIT(GetCurrentThreadId);
+
+ return dwThreadId;
+}
+
+PAL_ERROR
+CorUnix::InternalCreateThread(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ DWORD dwStackSize,
+ LPTHREAD_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter,
+ DWORD dwCreationFlags,
+ PalThreadType eThreadType,
+ SIZE_T* pThreadId,
+ HANDLE *phThread
+ )
+{
+ PAL_ERROR palError;
+ CPalThread *pNewThread = NULL;
+ CObjectAttributes oa;
+ bool fAttributesInitialized = FALSE;
+ bool fThreadDataAddedToProcessList = FALSE;
+ HANDLE hNewThread = NULL;
+
+ pthread_t pthread;
+ pthread_attr_t pthreadAttr;
+#if PTHREAD_CREATE_MODIFIES_ERRNO
+ int storedErrno;
+#endif // PTHREAD_CREATE_MODIFIES_ERRNO
+ BOOL fHoldingProcessLock = FALSE;
+ int iError = 0;
+ size_t alignedStackSize;
+
+ /* Validate parameters */
+
+ if (lpThreadAttributes != NULL)
+ {
+ ASSERT("lpThreadAttributes parameter must be NULL (%p)\n",
+ lpThreadAttributes);
+ palError = ERROR_INVALID_PARAMETER;
+ goto EXIT;
+ }
+
+ alignedStackSize = dwStackSize;
+ if (alignedStackSize != 0)
+ {
+ // Some systems require the stack size to be aligned to the page size
+ if (sizeof(alignedStackSize) <= sizeof(dwStackSize) && alignedStackSize + (GetVirtualPageSize() - 1) < alignedStackSize)
+ {
+ // When coming here from the public API surface, the incoming value is originally a nonnegative signed int32, so
+ // this shouldn't happen
+ ASSERT(
+ "Couldn't align the requested stack size (%Iu) to the page size because the stack size was too large\n",
+ alignedStackSize);
+ palError = ERROR_INVALID_PARAMETER;
+ goto EXIT;
+ }
+ alignedStackSize = ALIGN_UP(alignedStackSize, GetVirtualPageSize());
+ }
+
+ // Ignore the STACK_SIZE_PARAM_IS_A_RESERVATION flag
+ dwCreationFlags &= ~STACK_SIZE_PARAM_IS_A_RESERVATION;
+
+ if ((dwCreationFlags != 0) && (dwCreationFlags != CREATE_SUSPENDED))
+ {
+ ASSERT("dwCreationFlags parameter is invalid (%#x)\n", dwCreationFlags);
+ palError = ERROR_INVALID_PARAMETER;
+ goto EXIT;
+ }
+
+ //
+ // Create the CPalThread for the thread
+ //
+
+ pNewThread = AllocTHREAD();
+ if (NULL == pNewThread)
+ {
+ palError = ERROR_OUTOFMEMORY;
+ goto EXIT;
+ }
+
+ palError = pNewThread->RunPreCreateInitializers();
+ if (NO_ERROR != palError)
+ {
+ goto EXIT;
+ }
+
+ pNewThread->m_lpStartAddress = lpStartAddress;
+ pNewThread->m_lpStartParameter = lpParameter;
+ pNewThread->m_bCreateSuspended = (dwCreationFlags & CREATE_SUSPENDED) == CREATE_SUSPENDED;
+ pNewThread->m_eThreadType = eThreadType;
+
+ if (0 != pthread_attr_init(&pthreadAttr))
+ {
+ ERROR("couldn't initialize pthread attributes\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+
+ fAttributesInitialized = TRUE;
+
+ if (alignedStackSize == 0)
+ {
+ // The thread is to be created with default stack size. Use the default stack size
+ // override that was determined during the PAL initialization.
+ alignedStackSize = g_defaultStackSize;
+ }
+
+ /* adjust the stack size if necessary */
+ if (alignedStackSize != 0)
+ {
+#ifdef PTHREAD_STACK_MIN
+ size_t MinStackSize = ALIGN_UP(PTHREAD_STACK_MIN, GetVirtualPageSize());
+#else // !PTHREAD_STACK_MIN
+ size_t MinStackSize = 64 * 1024; // this value is typically accepted by pthread_attr_setstacksize()
+#endif // PTHREAD_STACK_MIN
+ if (alignedStackSize < MinStackSize)
+ {
+ // Adjust the stack size to a minimum value that is likely to be accepted by pthread_attr_setstacksize(). If this
+ // function fails, typically the caller will end up throwing OutOfMemoryException under the assumption that the
+ // requested stack size is too large or the system does not have sufficient memory to create a thread. Try to
+ // prevent failing just just because the stack size value is too low.
+ alignedStackSize = MinStackSize;
+ }
+
+ TRACE("setting thread stack size to %Iu\n", alignedStackSize);
+ if (0 != pthread_attr_setstacksize(&pthreadAttr, alignedStackSize))
+ {
+ ERROR("couldn't set pthread stack size to %Iu\n", alignedStackSize);
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+ }
+ else
+ {
+ TRACE("using the system default thread stack size\n");
+ }
+
+#if HAVE_THREAD_SELF || HAVE__LWP_SELF
+ /* Create new threads as "bound", so each pthread is permanently bound
+ to an LWP. Get/SetThreadContext() depend on this 1:1 mapping. */
+ pthread_attr_setscope(&pthreadAttr, PTHREAD_SCOPE_SYSTEM);
+#endif // HAVE_THREAD_SELF || HAVE__LWP_SELF
+
+ //
+ // We never call pthread_join, so create the new thread as detached
+ //
+
+ iError = pthread_attr_setdetachstate(&pthreadAttr, PTHREAD_CREATE_DETACHED);
+ _ASSERTE(0 == iError);
+
+ //
+ // Create the IPalObject for the thread and store it in the object
+ //
+
+ palError = CreateThreadObject(
+ pThread,
+ pNewThread,
+ &hNewThread);
+
+ if (NO_ERROR != palError)
+ {
+ goto EXIT;
+ }
+
+ //
+ // Add the thread to the process list
+ //
+
+ //
+ // We use the process lock to ensure that we're not interrupted
+ // during the creation process. After adding the CPalThread reference
+ // to the process list, we want to make sure the actual thread has been
+ // started. Otherwise, there's a window where the thread can be found
+ // in the process list but doesn't yet exist in the system.
+ //
+
+ PROCProcessLock();
+ fHoldingProcessLock = TRUE;
+
+ PROCAddThread(pThread, pNewThread);
+ fThreadDataAddedToProcessList = TRUE;
+
+ //
+ // Spawn the new pthread
+ //
+
+#if PTHREAD_CREATE_MODIFIES_ERRNO
+ storedErrno = errno;
+#endif // PTHREAD_CREATE_MODIFIES_ERRNO
+
+ iError = pthread_create(&pthread, &pthreadAttr, CPalThread::ThreadEntry, pNewThread);
+
+#if PTHREAD_CREATE_MODIFIES_ERRNO
+ if (iError == 0)
+ {
+ // Restore errno if pthread_create succeeded.
+ errno = storedErrno;
+ }
+#endif // PTHREAD_CREATE_MODIFIES_ERRNO
+
+ if (0 != iError)
+ {
+ ERROR("pthread_create failed, error is %d (%s)\n", iError, strerror(iError));
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto EXIT;
+ }
+
+ //
+ // Wait for the new thread to finish its initial startup tasks
+ // (i.e., the ones that might fail)
+ //
+ if (pNewThread->WaitForStartStatus())
+ {
+ //
+ // Everything succeeded. Store the handle for the new thread and
+ // the thread's ID in the out params
+ //
+ *phThread = hNewThread;
+
+ if (NULL != pThreadId)
+ {
+ *pThreadId = pNewThread->GetThreadId();
+ }
+ }
+ else
+ {
+ ERROR("error occurred in THREADEntry, thread creation failed.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+
+ //
+ // If we're here, then we've locked the process list and both pthread_create
+ // and WaitForStartStatus succeeded. Thus, we can now unlock the process list.
+ // Since palError == NO_ERROR, we won't call this again in the exit block.
+ //
+ PROCProcessUnlock();
+ fHoldingProcessLock = FALSE;
+
+EXIT:
+
+ if (fAttributesInitialized)
+ {
+ if (0 != pthread_attr_destroy(&pthreadAttr))
+ {
+ WARN("pthread_attr_destroy() failed\n");
+ }
+ }
+
+ if (NO_ERROR != palError)
+ {
+ //
+ // We either were not able to create the new thread, or a failure
+ // occurred in the new thread's entry routine. Free up the associated
+ // resources here
+ //
+
+ if (fThreadDataAddedToProcessList)
+ {
+ PROCRemoveThread(pThread, pNewThread);
+ }
+ //
+ // Once we remove the thread from the process list, we can call
+ // PROCProcessUnlock.
+ //
+ if (fHoldingProcessLock)
+ {
+ PROCProcessUnlock();
+ }
+ fHoldingProcessLock = FALSE;
+ }
+
+ _ASSERT_MSG(!fHoldingProcessLock, "Exiting InternalCreateThread while still holding the process critical section.\n");
+
+ return palError;
+}
+
+/*++
+Function:
+ InternalEndCurrentThread
+
+Does any necessary memory clean up, signals waiting threads, and then forces
+the current thread to exit.
+--*/
+
+VOID
+CorUnix::InternalEndCurrentThread(
+ CPalThread *pThread
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ ISynchStateController *pSynchStateController = NULL;
+
+#ifdef PAL_PERF
+ PERFDisableThreadProfile(UserCreatedThread != pThread->GetThreadType());
+#endif
+
+ //
+ // Abandon any objects owned by this thread
+ //
+
+ palError = g_pSynchronizationManager->AbandonObjectsOwnedByThread(
+ pThread,
+ pThread
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Failure abandoning owned objects");
+ }
+
+ //
+ // Need to synchronize setting the thread state to TS_DONE since
+ // this is checked for in InternalSuspendThreadFromData.
+ // TODO: Is this still needed after removing InternalSuspendThreadFromData?
+ //
+
+ pThread->suspensionInfo.AcquireSuspensionLock(pThread);
+ pThread->synchronizationInfo.SetThreadState(TS_DONE);
+ pThread->suspensionInfo.ReleaseSuspensionLock(pThread);
+
+ //
+ // Mark the thread object as signaled
+ //
+
+ palError = pThread->GetThreadObject()->GetSynchStateController(
+ pThread,
+ &pSynchStateController
+ );
+
+ if (NO_ERROR == palError)
+ {
+ palError = pSynchStateController->SetSignalCount(1);
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to mark thread object as signaled");
+ }
+
+ pSynchStateController->ReleaseController();
+ }
+ else
+ {
+ ASSERT("Unable to obtain state controller for thread");
+ }
+
+ //
+ // Add a reference to the thread data before releasing the
+ // thread object, so we can still use it
+ //
+
+ pThread->AddThreadReference();
+
+ //
+ // Release the reference to the IPalObject for this thread
+ //
+
+ pThread->GetThreadObject()->ReleaseReference(pThread);
+
+ /* Remove thread for the thread list of the process
+ (don't do if this is the last thread -> gets handled by
+ TerminateProcess->PROCCleanupProcess->PROCTerminateOtherThreads) */
+
+ PROCRemoveThread(pThread, pThread);
+
+ //
+ // Now release our reference to the thread data. We cannot touch
+ // it after this point
+ //
+
+ pThread->ReleaseThreadReference();
+}
+
+void *
+CPalThread::ThreadEntry(
+ void *pvParam
+ )
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+ PTHREAD_START_ROUTINE pfnStartRoutine;
+ LPVOID pvPar;
+ DWORD retValue;
+#if HAVE_SCHED_GETAFFINITY && HAVE_SCHED_SETAFFINITY
+ cpu_set_t cpuSet;
+ int st;
+#endif
+
+ pThread = reinterpret_cast<CPalThread*>(pvParam);
+
+ if (NULL == pThread)
+ {
+ ASSERT("THREAD pointer is NULL!\n");
+ goto fail;
+ }
+
+#if HAVE_SCHED_GETAFFINITY && HAVE_SCHED_SETAFFINITY
+ // Threads inherit their parent's affinity mask on Linux. This is not desired, so we reset
+ // the current thread's affinity mask to the mask of the current process.
+ //
+ // Typically, we would use pthread_attr_setaffinity_np() and have pthread_create() create the thread with the specified
+ // affinity. At least one implementation of pthread_create() following a pthread_attr_setaffinity_np() calls
+ // sched_setaffinity(<newThreadPid>, ...), which is not allowed under Snap's default strict confinement without manually
+ // connecting the process-control plug. To work around that, have the thread set the affinity after it starts.
+ // sched_setaffinity(<currentThreadPid>, ...) is also currently not allowed, only sched_setaffinity(0, ...).
+ // pthread_setaffinity_np(pthread_self(), ...) seems to call sched_setaffinity(<currentThreadPid>, ...) in at least one
+ // implementation, and does not work. Use sched_setaffinity(0, ...) instead. See the following for more information:
+ // - https://github.com/dotnet/runtime/pull/38795
+ // - https://github.com/dotnet/runtime/issues/1634
+ // - https://forum.snapcraft.io/t/requesting-autoconnect-for-interfaces-in-pigmeat-process-control-home/17987/13
+
+ CPU_ZERO(&cpuSet);
+
+ st = sched_getaffinity(gPID, sizeof(cpu_set_t), &cpuSet);
+ if (st != 0)
+ {
+ ASSERT("sched_getaffinity failed!\n");
+ // The sched_getaffinity should never fail for getting affinity of the current process
+ palError = ERROR_INTERNAL_ERROR;
+ goto fail;
+ }
+
+ st = sched_setaffinity(0, sizeof(cpu_set_t), &cpuSet);
+ if (st != 0)
+ {
+ ASSERT("sched_setaffinity failed!\n");
+ // The sched_setaffinity should never fail when passed the mask extracted using sched_getaffinity
+ palError = ERROR_INTERNAL_ERROR;
+ goto fail;
+ }
+#endif // HAVE_SCHED_GETAFFINITY && HAVE_SCHED_SETAFFINITY
+
+ pThread->m_threadId = THREADSilentGetCurrentThreadId();
+ pThread->m_pthreadSelf = pthread_self();
+#if HAVE_THREAD_SELF
+ pThread->m_dwLwpId = (DWORD) thread_self();
+#elif HAVE__LWP_SELF
+ pThread->m_dwLwpId = (DWORD) _lwp_self();
+#else
+ pThread->m_dwLwpId = 0;
+#endif
+
+ palError = pThread->RunPostCreateInitializers();
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Error %i initializing thread data (post creation)\n", palError);
+ goto fail;
+ }
+
+ // Check if the thread should be started suspended.
+ if (pThread->GetCreateSuspended())
+ {
+ palError = pThread->suspensionInfo.InternalSuspendNewThreadFromData(pThread);
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Error %i attempting to suspend new thread\n", palError);
+ goto fail;
+ }
+ }
+ else
+ {
+ //
+ // All startup operations that might have failed have succeeded,
+ // so thread creation is successful. Let CreateThread return.
+ //
+
+ pThread->SetStartStatus(TRUE);
+ }
+
+ pThread->synchronizationInfo.SetThreadState(TS_RUNNING);
+
+ if (UserCreatedThread == pThread->GetThreadType())
+ {
+ /* Inform all loaded modules that a thread has been created */
+ /* note : no need to take a critical section to serialize here; the loader
+ will take the module critical section */
+ LOADCallDllMain(DLL_THREAD_ATTACH, NULL);
+ }
+
+#ifdef PAL_PERF
+ PERFAllocThreadInfo();
+ PERFEnableThreadProfile(UserCreatedThread != pThread->GetThreadType());
+#endif
+
+ /* call the startup routine */
+ pfnStartRoutine = pThread->GetStartAddress();
+ pvPar = pThread->GetStartParameter();
+
+ retValue = (*pfnStartRoutine)(pvPar);
+
+ TRACE("Thread exited (%u)\n", retValue);
+ pThread->SetExitCode(retValue);
+
+ return NULL;
+
+fail:
+
+ //
+ // Notify InternalCreateThread that a failure occurred
+ //
+
+ if (NULL != pThread)
+ {
+ pThread->synchronizationInfo.SetThreadState(TS_FAILED);
+ pThread->SetStartStatus(FALSE);
+ }
+
+ /* do not call ExitThread : we don't want to call DllMain(), and the thread
+ isn't in a clean state (e.g. lpThread isn't in TLS). the cleanup work
+ above should release all resources */
+ return NULL;
+}
+
/*++
Function:
CreateThreadData
return palError;
}
+/*++
+Function:
+ CreateThreadData
+
+Abstract:
+ Creates the IPalObject for a thread, storing
+ the reference in the CPalThread
+
+Parameters:
+ pThread - the thread data for the creating thread
+ pNewThread - the thread data for the thread being initialized
+
+Return:
+ PAL_ERROR
+--*/
+
+PAL_ERROR
+CorUnix::CreateThreadObject(
+ CPalThread *pThread,
+ CPalThread *pNewThread,
+ HANDLE *phThread
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjThread = NULL;
+ IDataLock *pDataLock;
+ HANDLE hThread = NULL;
+ CThreadProcessLocalData *pLocalData = NULL;
+ CObjectAttributes oa;
+ BOOL fThreadDataStoredInObject = FALSE;
+ IPalObject *pobjRegisteredThread = NULL;
+
+ //
+ // Create the IPalObject for the thread
+ //
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otThread,
+ &oa,
+ &pobjThread
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto CreateThreadObjectExit;
+ }
+
+ //
+ // Store the CPalThread inside of the IPalObject
+ //
+
+ palError = pobjThread->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto CreateThreadObjectExit;
+ }
+
+ pLocalData->pThread = pNewThread;
+ pDataLock->ReleaseLock(pThread, TRUE);
+ fThreadDataStoredInObject = TRUE;
+
+ //
+ // Register the IPalObject (obtaining a handle)
+ //
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pobjThread,
+ &aotThread,
+ &hThread,
+ &pobjRegisteredThread
+ );
+
+ //
+ // pobjThread is invalidated by the call to RegisterObject, so NULL
+ // it out here to prevent it from being released
+ //
+
+ pobjThread = NULL;
+
+ if (NO_ERROR != palError)
+ {
+ goto CreateThreadObjectExit;
+ }
+
+ //
+ // Store the registered object inside of the thread object,
+ // adding a reference for the thread itself
+ //
+
+ pNewThread->m_pThreadObject = pobjRegisteredThread;
+ pNewThread->m_pThreadObject->AddReference();
+
+ *phThread = hThread;
+
+CreateThreadObjectExit:
+
+ if (NO_ERROR != palError)
+ {
+ if (NULL != hThread)
+ {
+ g_pObjectManager->RevokeHandle(pThread, hThread);
+ }
+
+ if (NULL != pNewThread->m_pThreadObject)
+ {
+ //
+ // Release the new thread's reference on the underlying thread
+ // object
+ //
+
+ pNewThread->m_pThreadObject->ReleaseReference(pThread);
+ }
+
+ if (!fThreadDataStoredInObject)
+ {
+ //
+ // The CPalThread for the new thread was never stored in
+ // an IPalObject instance, so we need to release the initial
+ // reference here. (If it has been stored it will get freed in
+ // the owning object's cleanup routine)
+ //
+
+ pNewThread->ReleaseThreadReference();
+ }
+ }
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pThread);
+ }
+
+ if (NULL != pobjRegisteredThread)
+ {
+ pobjRegisteredThread->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+PAL_ERROR
+CorUnix::InternalCreateDummyThread(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ CPalThread **ppDummyThread,
+ HANDLE *phThread
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pDummyThread = NULL;
+ IPalObject *pobjThread = NULL;
+ IPalObject *pobjThreadRegistered = NULL;
+ IDataLock *pDataLock;
+ CThreadProcessLocalData *pLocalData;
+ CObjectAttributes oa(NULL, lpThreadAttributes);
+ bool fThreadDataStoredInObject = FALSE;
+
+ pDummyThread = AllocTHREAD();
+ if (NULL == pDummyThread)
+ {
+ palError = ERROR_OUTOFMEMORY;
+ goto InternalCreateDummyThreadExit;
+ }
+
+ pDummyThread->m_fIsDummy = TRUE;
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otThread,
+ &oa,
+ &pobjThread
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateDummyThreadExit;
+ }
+
+ palError = pobjThread->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateDummyThreadExit;
+ }
+
+ pLocalData->pThread = pDummyThread;
+ pDataLock->ReleaseLock(pThread, TRUE);
+ fThreadDataStoredInObject = TRUE;
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pobjThread,
+ &aotThread,
+ phThread,
+ &pobjThreadRegistered
+ );
+
+ //
+ // pobjThread is invalidated by the above call, so NULL
+ // it out here
+ //
+
+ pobjThread = NULL;
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateDummyThreadExit;
+ }
+
+ //
+ // Note the we do NOT store the registered object for the
+ // thread w/in pDummyThread. Since this thread is not actually
+ // executing that reference would never be released (and thus
+ // the thread object would never be cleaned up...)
+ //
+
+ *ppDummyThread = pDummyThread;
+
+InternalCreateDummyThreadExit:
+
+ if (NULL != pobjThreadRegistered)
+ {
+ pobjThreadRegistered->ReleaseReference(pThread);
+ }
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pThread);
+ }
+
+ if (NO_ERROR != palError
+ && NULL != pDummyThread
+ && !fThreadDataStoredInObject)
+ {
+ pDummyThread->ReleaseThreadReference();
+ }
+
+ return palError;
+}
+
+PAL_ERROR
+CorUnix::InternalGetThreadDataFromHandle(
+ CPalThread *pThread,
+ HANDLE hThread,
+ CPalThread **ppTargetThread,
+ IPalObject **ppobjThread
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobj;
+ IDataLock *pLock;
+ CThreadProcessLocalData *pData;
+
+ *ppobjThread = NULL;
+
+ if (hPseudoCurrentThread == hThread)
+ {
+ *ppTargetThread = pThread;
+ }
+ else
+ {
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hThread,
+ &aotThread,
+ &pobj
+ );
+
+ if (NO_ERROR == palError)
+ {
+ palError = pobj->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLock,
+ reinterpret_cast<void**>(&pData)
+ );
+
+ if (NO_ERROR == palError)
+ {
+ *ppTargetThread = pData->pThread;
+ pLock->ReleaseLock(pThread, FALSE);
+
+ //
+ // Transfer object reference to out param
+ //
+
+ *ppobjThread = pobj;
+ }
+ else
+ {
+ pobj->ReleaseReference(pThread);
+ }
+ }
+ }
+
+ return palError;
+}
+
PAL_ERROR
CPalThread::RunPreCreateInitializers(
void
InternalInitializeCriticalSection(&m_csLock);
m_fLockInitialized = TRUE;
+ iError = pthread_mutex_init(&m_startMutex, NULL);
+ if (0 != iError)
+ {
+ goto RunPreCreateInitializersExit;
+ }
+
+ iError = pthread_cond_init(&m_startCond, NULL);
+ if (0 != iError)
+ {
+ pthread_mutex_destroy(&m_startMutex);
+ goto RunPreCreateInitializersExit;
+ }
+
+ m_fStartItemsInitialized = TRUE;
+
//
// Call the pre-create initializers for embedded classes
//
+ palError = synchronizationInfo.InitializePreCreate();
+ if (NO_ERROR != palError)
+ {
+ goto RunPreCreateInitializersExit;
+ }
+
+ palError = suspensionInfo.InitializePreCreate();
+ if (NO_ERROR != palError)
+ {
+ goto RunPreCreateInitializersExit;
+ }
+
palError = crtInfo.InitializePreCreate();
if (NO_ERROR != palError)
{
{
InternalDeleteCriticalSection(&m_csLock);
}
+
+ if (m_fStartItemsInitialized)
+ {
+ int iError;
+
+ iError = pthread_cond_destroy(&m_startCond);
+ _ASSERTE(0 == iError);
+
+ iError = pthread_mutex_destroy(&m_startMutex);
+ _ASSERTE(0 == iError);
+ }
}
void
// Call the post-create initializers for embedded classes
//
+ if (pthread_setspecific(thObjKey, reinterpret_cast<void*>(this)))
+ {
+ ASSERT("Unable to set the thread object key's value\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto RunPostCreateInitializersExit;
+ }
+
+ palError = synchronizationInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
+ if (NO_ERROR != palError)
+ {
+ goto RunPostCreateInitializersExit;
+ }
+
+ palError = suspensionInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
+ if (NO_ERROR != palError)
+ {
+ goto RunPostCreateInitializersExit;
+ }
+
palError = crtInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
if (NO_ERROR != palError)
{
return palError;
}
+void
+CPalThread::SetStartStatus(
+ bool fStartSucceeded
+ )
+{
+ int iError;
+
+#if _DEBUG
+ if (m_fStartStatusSet)
+ {
+ ASSERT("Multiple calls to CPalThread::SetStartStatus\n");
+ }
+#endif
+
+ //
+ // This routine may get called from CPalThread::ThreadEntry
+ //
+ // If we've reached this point there are no further thread
+ // suspensions that happen at creation time, so reset
+ // m_bCreateSuspended
+ //
+
+ m_bCreateSuspended = FALSE;
+
+ iError = pthread_mutex_lock(&m_startMutex);
+ if (0 != iError)
+ {
+ ASSERT("pthread primitive failure\n");
+ // bugcheck?
+ }
+
+ m_fStartStatus = fStartSucceeded;
+ m_fStartStatusSet = TRUE;
+
+ iError = pthread_cond_signal(&m_startCond);
+ if (0 != iError)
+ {
+ ASSERT("pthread primitive failure\n");
+ // bugcheck?
+ }
+
+ iError = pthread_mutex_unlock(&m_startMutex);
+ if (0 != iError)
+ {
+ ASSERT("pthread primitive failure\n");
+ // bugcheck?
+ }
+}
+
+bool
+CPalThread::WaitForStartStatus(
+ void
+ )
+{
+ int iError;
+
+ iError = pthread_mutex_lock(&m_startMutex);
+ if (0 != iError)
+ {
+ ASSERT("pthread primitive failure\n");
+ // bugcheck?
+ }
+
+ while (!m_fStartStatusSet)
+ {
+ iError = pthread_cond_wait(&m_startCond, &m_startMutex);
+ if (0 != iError)
+ {
+ ASSERT("pthread primitive failure\n");
+ // bugcheck?
+ }
+ }
+
+ iError = pthread_mutex_unlock(&m_startMutex);
+ if (0 != iError)
+ {
+ ASSERT("pthread primitive failure\n");
+ // bugcheck?
+ }
+
+ return m_fStartStatus;
+}
+
+void
+ThreadCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup,
+ bool fShutdown,
+ bool fCleanupSharedState
+ )
+{
+ CThreadProcessLocalData *pThreadData = NULL;
+ CPalThread *pThreadToCleanup = NULL;
+ IDataLock *pDataLock = NULL;
+ PAL_ERROR palError = NO_ERROR;
+
+ //
+ // Free the CPalThread data for the passed in thread
+ //
+
+ palError = pObjectToCleanup->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void**>(&pThreadData)
+ );
+
+ if (NO_ERROR == palError)
+ {
+ //
+ // Note that we may be cleaning up the data for the calling
+ // thread (i.e., pThread == pThreadToCleanup), so the release
+ // of the thread reference needs to be the last thing that
+ // we do (though in that case it's very likely that the person
+ // calling us will be holding an extra reference to allow
+ // for the thread data to be available while the rest of the
+ // object cleanup takes place).
+ //
+
+ pThreadToCleanup = pThreadData->pThread;
+ pThreadData->pThread = NULL;
+ pDataLock->ReleaseLock(pThread, TRUE);
+ pThreadToCleanup->ReleaseThreadReference();
+ }
+ else
+ {
+ ASSERT("Unable to obtain thread data");
+ }
+
+}
+
+PAL_ERROR
+ThreadInitializationRoutine(
+ CPalThread *pThread,
+ CObjectType *pObjectType,
+ void *pImmutableData,
+ void *pSharedData,
+ void *pProcessLocalData
+ )
+{
+ return NO_ERROR;
+}
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+/*++
+
+
+
+Module Name:
+
+ threadsusp.cpp
+
+Abstract:
+
+ Implementation of functions related to threads.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/corunix.hpp"
+#include "pal/thread.hpp"
+#include "pal/mutex.hpp"
+#include "pal/init.h"
+#include "pal/dbgmsg.h"
+
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <debugmacrosext.h>
+
+using namespace CorUnix;
+
+/* ------------------- Definitions ------------------------------*/
+SET_DEFAULT_DEBUG_CHANNEL(THREAD);
+
+/* This code is written to the blocking pipe of a thread that was created
+ in suspended state in order to resume it. */
+CONST BYTE WAKEUPCODE=0x2A;
+
+// #define USE_GLOBAL_LOCK_FOR_SUSPENSION // Uncomment this define to use the global suspension lock.
+/* The global suspension lock can be used in place of each thread having its own
+suspension mutex or spinlock. The downside is that it restricts us to only
+performing one suspension or resumption in the PAL at a time. */
+#ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
+static LONG g_ssSuspensionLock = 0;
+#endif
+
+/*++
+Function:
+ InternalSuspendNewThreadFromData
+
+ On platforms where we use pipes for starting threads suspended, this
+ function sets the blocking pipe for the thread and blocks until the
+ wakeup code is written to the pipe by ResumeThread.
+
+--*/
+PAL_ERROR
+CThreadSuspensionInfo::InternalSuspendNewThreadFromData(
+ CPalThread *pThread
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ AcquireSuspensionLock(pThread);
+ pThread->suspensionInfo.SetSelfSusp(TRUE);
+ ReleaseSuspensionLock(pThread);
+
+ int pipe_descs[2];
+ int pipeRv =
+#if HAVE_PIPE2
+ pipe2(pipe_descs, O_CLOEXEC);
+#else
+ pipe(pipe_descs);
+#endif // HAVE_PIPE2
+ if (pipeRv == -1)
+ {
+ ERROR("pipe() failed! error is %d (%s)\n", errno, strerror(errno));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+#if !HAVE_PIPE2
+ fcntl(pipe_descs[0], F_SETFD, FD_CLOEXEC); // make pipe non-inheritable, if possible
+ fcntl(pipe_descs[1], F_SETFD, FD_CLOEXEC);
+#endif // !HAVE_PIPE2
+
+ // [0] is the read end of the pipe, and [1] is the write end.
+ pThread->suspensionInfo.SetBlockingPipe(pipe_descs[1]);
+ pThread->SetStartStatus(TRUE);
+
+ BYTE resume_code = 0;
+ ssize_t read_ret;
+
+ // Block until ResumeThread writes something to the pipe
+ while ((read_ret = read(pipe_descs[0], &resume_code, sizeof(resume_code))) != sizeof(resume_code))
+ {
+ if (read_ret != -1 || EINTR != errno)
+ {
+ // read might return 0 (with EAGAIN) if the other end of the pipe gets closed
+ palError = ERROR_INTERNAL_ERROR;
+ break;
+ }
+ }
+
+ if (palError == NO_ERROR && resume_code != WAKEUPCODE)
+ {
+ // If we did read successfully but the byte didn't match WAKEUPCODE, we treat it as a failure.
+ palError = ERROR_INTERNAL_ERROR;
+ }
+
+ if (palError == NO_ERROR)
+ {
+ AcquireSuspensionLock(pThread);
+ pThread->suspensionInfo.SetSelfSusp(FALSE);
+ ReleaseSuspensionLock(pThread);
+ }
+
+ // Close the pipes regardless of whether we were successful.
+ close(pipe_descs[0]);
+ close(pipe_descs[1]);
+
+ return palError;
+}
+
+/*++
+Function:
+
+ ResumeThread
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+ResumeThread(
+ IN HANDLE hThread
+ )
+{
+ PAL_ERROR palError;
+ CPalThread *pthrResumer;
+ DWORD dwSuspendCount = (DWORD)-1;
+
+ PERF_ENTRY(ResumeThread);
+ ENTRY("ResumeThread(hThread=%p)\n", hThread);
+
+ pthrResumer = InternalGetCurrentThread();
+ palError = InternalResumeThread(
+ pthrResumer,
+ hThread,
+ &dwSuspendCount
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pthrResumer->SetLastError(palError);
+ dwSuspendCount = (DWORD) -1;
+ }
+ else
+ {
+ _ASSERT_MSG(dwSuspendCount != static_cast<DWORD>(-1), "InternalResumeThread returned success but dwSuspendCount did not change.\n");
+ }
+
+ LOGEXIT("ResumeThread returns DWORD %u\n", dwSuspendCount);
+ PERF_EXIT(ResumeThread);
+ return dwSuspendCount;
+}
+
+/*++
+Function:
+ InternalResumeThread
+
+InternalResumeThread converts the handle of the target thread to a
+CPalThread, and passes both the resumer and target thread references
+to InternalResumeThreadFromData. A reference to the suspend count from
+the resumption attempt is passed back to the caller of this function.
+--*/
+PAL_ERROR
+CorUnix::InternalResumeThread(
+ CPalThread *pthrResumer,
+ HANDLE hTargetThread,
+ DWORD *pdwSuspendCount
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthrTarget = NULL;
+ IPalObject *pobjThread = NULL;
+
+ palError = InternalGetThreadDataFromHandle(
+ pthrResumer,
+ hTargetThread,
+ &pthrTarget,
+ &pobjThread
+ );
+
+ if (NO_ERROR == palError)
+ {
+ palError = pthrResumer->suspensionInfo.InternalResumeThreadFromData(
+ pthrResumer,
+ pthrTarget,
+ pdwSuspendCount
+ );
+ }
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pthrResumer);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ InternalResumeThreadFromData
+
+InternalResumeThreadFromData resumes the target thread. First, the suspension
+mutexes of the threads are acquired. Next, there's a check to ensure that the
+target thread was actually suspended. Finally, the resume attempt is made
+and the suspension mutexes are released. The suspend count of the
+target thread is passed back to the caller of this function.
+
+Note that ReleaseSuspensionLock(s) is called before hitting ASSERTs in error
+paths. Currently, this seems unnecessary since asserting within
+InternalResumeThreadFromData will not cause cleanup to occur. However,
+this may change since it would be preferable to perform cleanup. Thus, calls
+to release suspension locks remain in the error paths.
+--*/
+PAL_ERROR
+CThreadSuspensionInfo::InternalResumeThreadFromData(
+ CPalThread *pthrResumer,
+ CPalThread *pthrTarget,
+ DWORD *pdwSuspendCount
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ int nWrittenBytes = -1;
+
+ if (SignalHandlerThread == pthrTarget->GetThreadType())
+ {
+ ASSERT("Attempting to resume the signal handling thread, which can never be suspended.\n");
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalResumeThreadFromDataExit;
+ }
+
+ // Acquire suspension mutex
+ AcquireSuspensionLocks(pthrResumer, pthrTarget);
+
+ // Check target thread's state to ensure it hasn't died.
+ // Setting a thread's state to TS_DONE is protected by the
+ // target's suspension mutex.
+ if (pthrTarget->synchronizationInfo.GetThreadState() == TS_DONE)
+ {
+ palError = ERROR_INVALID_HANDLE;
+ ReleaseSuspensionLocks(pthrResumer, pthrTarget);
+ goto InternalResumeThreadFromDataExit;
+ }
+
+ // If this is a dummy thread, then it represents a process that was created with CREATE_SUSPENDED
+ // and it should have a blocking pipe set. If GetBlockingPipe returns -1 for a dummy thread, then
+ // something is wrong - either CREATE_SUSPENDED wasn't used or the process was already resumed.
+ if (pthrTarget->IsDummy() && -1 == pthrTarget->suspensionInfo.GetBlockingPipe())
+ {
+ palError = ERROR_INVALID_HANDLE;
+ ERROR("Tried to wake up dummy thread without a blocking pipe.\n");
+ ReleaseSuspensionLocks(pthrResumer, pthrTarget);
+ goto InternalResumeThreadFromDataExit;
+ }
+
+ // If there is a blocking pipe on this thread, resume it by writing the wake up code to that pipe.
+ if (-1 != pthrTarget->suspensionInfo.GetBlockingPipe())
+ {
+ // If write() is interrupted by a signal before writing data,
+ // it returns -1 and sets errno to EINTR. In this case, we
+ // attempt the write() again.
+ writeAgain:
+ nWrittenBytes = write(pthrTarget->suspensionInfo.GetBlockingPipe(), &WAKEUPCODE, sizeof(WAKEUPCODE));
+
+ // The size of WAKEUPCODE is 1 byte. If write returns 0, we'll treat it as an error.
+ if (sizeof(WAKEUPCODE) != nWrittenBytes)
+ {
+ // If we are here during process creation, this is most likely caused by the target
+ // process dying before reaching this point and thus breaking the pipe.
+ if (nWrittenBytes == -1 && EPIPE == errno)
+ {
+ palError = ERROR_INVALID_HANDLE;
+ ReleaseSuspensionLocks(pthrResumer, pthrTarget);
+ ERROR("Write failed with EPIPE\n");
+ goto InternalResumeThreadFromDataExit;
+ }
+ else if (nWrittenBytes == 0 || (nWrittenBytes == -1 && EINTR == errno))
+ {
+ TRACE("write() failed with EINTR; re-attempting write\n");
+ goto writeAgain;
+ }
+ else
+ {
+ // Some other error occurred; need to release suspension mutexes before leaving ResumeThread.
+ palError = ERROR_INTERNAL_ERROR;
+ ReleaseSuspensionLocks(pthrResumer, pthrTarget);
+ ASSERT("Write() failed; error is %d (%s)\n", errno, strerror(errno));
+ goto InternalResumeThreadFromDataExit;
+ }
+ }
+
+ // Reset blocking pipe to -1 since we're done using it.
+ pthrTarget->suspensionInfo.SetBlockingPipe(-1);
+
+ ReleaseSuspensionLocks(pthrResumer, pthrTarget);
+ goto InternalResumeThreadFromDataExit;
+ }
+ else
+ {
+ *pdwSuspendCount = 0;
+ palError = ERROR_BAD_COMMAND;
+ }
+
+InternalResumeThreadFromDataExit:
+
+ if (NO_ERROR == palError)
+ {
+ *pdwSuspendCount = 1;
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ TryAcquireSuspensionLock
+
+TryAcquireSuspensionLock is a utility function that tries to acquire a thread's
+suspension mutex or spinlock. If it succeeds, the function returns TRUE.
+Otherwise, it returns FALSE. This function is used in AcquireSuspensionLocks.
+Note that the global lock cannot be acquired in this function since it makes
+no sense to do so. A thread holding the global lock is the only thread that
+can perform suspend or resume operations so it doesn't need to acquire
+a second lock.
+--*/
+BOOL
+CThreadSuspensionInfo::TryAcquireSuspensionLock(
+ CPalThread* pthrTarget
+ )
+{
+ int iPthreadRet = 0;
+#if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+{
+ iPthreadRet = SPINLOCKTryAcquire(pthrTarget->suspensionInfo.GetSuspensionSpinlock());
+}
+#else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+{
+ iPthreadRet = pthread_mutex_trylock(pthrTarget->suspensionInfo.GetSuspensionMutex());
+ _ASSERT_MSG(iPthreadRet == 0 || iPthreadRet == EBUSY, "pthread_mutex_trylock returned %d\n", iPthreadRet);
+}
+#endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+
+ // If iPthreadRet is 0, lock acquisition was successful. Otherwise, it failed.
+ return (iPthreadRet == 0);
+}
+
+/*++
+Function:
+ AcquireSuspensionLock
+
+AcquireSuspensionLock acquires a thread's suspension mutex or spinlock.
+If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined, it will acquire the global lock.
+A thread in this function blocks until it acquires
+its lock, unlike in TryAcquireSuspensionLock.
+--*/
+void
+CThreadSuspensionInfo::AcquireSuspensionLock(
+ CPalThread* pthrCurrent
+ )
+{
+#ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
+{
+ SPINLOCKAcquire(&g_ssSuspensionLock, 0);
+}
+#else // USE_GLOBAL_LOCK_FOR_SUSPENSION
+{
+ #if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ {
+ SPINLOCKAcquire(&pthrCurrent->suspensionInfo.m_nSpinlock, 0);
+ }
+ #else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ {
+ INDEBUG(int iPthreadError = )
+ pthread_mutex_lock(&pthrCurrent->suspensionInfo.m_ptmSuspmutex);
+ _ASSERT_MSG(iPthreadError == 0, "pthread_mutex_lock returned %d\n", iPthreadError);
+ }
+ #endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+}
+#endif // USE_GLOBAL_LOCK_FOR_SUSPENSION
+}
+
+/*++
+Function:
+ ReleaseSuspensionLock
+
+ReleaseSuspensionLock is a function that releases a thread's suspension mutex
+or spinlock. If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined,
+it will release the global lock.
+--*/
+void
+CThreadSuspensionInfo::ReleaseSuspensionLock(
+ CPalThread* pthrCurrent
+ )
+{
+#ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
+{
+ SPINLOCKRelease(&g_ssSuspensionLock);
+}
+#else // USE_GLOBAL_LOCK_FOR_SUSPENSION
+{
+ #if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ {
+ SPINLOCKRelease(&pthrCurrent->suspensionInfo.m_nSpinlock);
+ }
+ #else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ {
+ INDEBUG(int iPthreadError = )
+ pthread_mutex_unlock(&pthrCurrent->suspensionInfo.m_ptmSuspmutex);
+ _ASSERT_MSG(iPthreadError == 0, "pthread_mutex_unlock returned %d\n", iPthreadError);
+ }
+ #endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+}
+#endif // USE_GLOBAL_LOCK_FOR_SUSPENSION
+}
+
+/*++
+Function:
+ AcquireSuspensionLocks
+
+AcquireSuspensionLocks is used to acquire the suspension locks
+of a suspender (or resumer) and target thread. The thread will
+perform a blocking call to acquire its own suspension lock
+and will then try to acquire the target thread's lock without blocking.
+If it fails to acquire the target's lock, it releases its own lock
+and the thread will try to acquire both locks again. The key
+is that both locks must be acquired together.
+
+Originally, only blocking calls were used to acquire the suspender
+and the target lock. However, this was problematic since a thread
+could acquire its own lock and then block on acquiring the target
+lock. In the meantime, the target could have already acquired its
+own lock and be attempting to suspend the suspender thread. This
+clearly causes deadlock. A second approach used locking hierarchies,
+where locks were acquired use thread id ordering. This was better but
+suffered from the scenario where thread A acquires thread B's
+suspension mutex first. In the meantime, thread C acquires thread A's
+suspension mutex and its own. Thus, thread A is suspended while
+holding thread B's mutex. This is problematic if thread C now wants
+to suspend thread B. The issue here is that a thread can be
+suspended while holding someone else's mutex but not holding its own.
+In the end, the correct approach is to always acquire your suspension
+mutex first. This prevents you from being suspended while holding the
+target's mutex. Then, attempt to acquire the target's mutex. If the mutex
+cannot be acquired, release your own and try again. This all or nothing
+approach is the safest and avoids nasty race conditions.
+
+If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined, the calling thread
+will acquire the global lock when possible.
+--*/
+VOID
+CThreadSuspensionInfo::AcquireSuspensionLocks(
+ CPalThread *pthrSuspender,
+ CPalThread *pthrTarget
+ )
+{
+ BOOL fReacquire = FALSE;
+
+#ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
+ AcquireSuspensionLock(pthrSuspender);
+#else // USE_GLOBAL_LOCK_FOR_SUSPENSION
+ do
+ {
+ fReacquire = FALSE;
+ AcquireSuspensionLock(pthrSuspender);
+ if (!TryAcquireSuspensionLock(pthrTarget))
+ {
+ // pthread_mutex_trylock returned EBUSY so release the first lock and try again.
+ ReleaseSuspensionLock(pthrSuspender);
+ fReacquire = TRUE;
+ sched_yield();
+ }
+ } while (fReacquire);
+#endif // USE_GLOBAL_LOCK_FOR_SUSPENSION
+
+ // Whenever the native implementation for the wait subsystem's thread
+ // blocking requires a lock as protection (as pthread conditions do with
+ // the associated mutex), we need to grab that lock to prevent the target
+ // thread from being suspended while holding the lock.
+ // Failing to do so can lead to a multiple threads deadlocking such as the
+ // one described in VSW 363793.
+ // In general, in similar scenarios, we need to grab the protecting lock
+ // every time suspension safety/unsafety is unbalanced on the two sides
+ // using the same condition (or any other native blocking support which
+ // needs an associated native lock), i.e. when either the signaling
+ // thread(s) is(are) signaling from an unsafe area and the waiting
+ // thread(s) is(are) waiting from a safe one, or vice versa (the scenario
+ // described in VSW 363793 is a good example of the first type of
+ // unbalanced suspension safety/unsafety).
+ // Instead, whenever signaling and waiting sides are both marked safe or
+ // unsafe, the deadlock cannot take place since either the suspending
+ // thread will suspend them anyway (regardless of the native lock), or it
+ // won't suspend any of them, since they are both marked unsafe.
+ // Such a balanced scenario applies, for instance, to critical sections
+ // where depending on whether the target CS is internal or not, both the
+ // signaling and the waiting side will access the mutex/condition from
+ // respectively an unsafe or safe region.
+
+ pthrTarget->AcquireNativeWaitLock();
+}
+
+/*++
+Function:
+ ReleaseSuspensionLocks
+
+ReleaseSuspensionLocks releases both thread's suspension mutexes.
+Note that the locks are released in the opposite order they're acquired.
+This prevents a suspending or resuming thread from being suspended
+while holding the target's lock.
+If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined, it simply releases the global lock.
+--*/
+VOID
+CThreadSuspensionInfo::ReleaseSuspensionLocks(
+ CPalThread *pthrSuspender,
+ CPalThread *pthrTarget
+ )
+{
+ // See comment in AcquireSuspensionLocks
+ pthrTarget->ReleaseNativeWaitLock();
+
+#ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
+ ReleaseSuspensionLock(pthrSuspender);
+#else // USE_GLOBAL_LOCK_FOR_SUSPENSION
+ ReleaseSuspensionLock(pthrTarget);
+ ReleaseSuspensionLock(pthrSuspender);
+#endif // USE_GLOBAL_LOCK_FOR_SUSPENSION
+}
+
+/*++
+Function:
+ InitializeSuspensionLock
+
+InitializeSuspensionLock initializes a thread's suspension spinlock
+or suspension mutex. It is called from the CThreadSuspensionInfo
+constructor.
+--*/
+VOID
+CThreadSuspensionInfo::InitializeSuspensionLock()
+{
+#if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ SPINLOCKInit(&m_nSpinlock);
+#else
+ int iError = pthread_mutex_init(&m_ptmSuspmutex, NULL);
+ if (0 != iError )
+ {
+ ASSERT("pthread_mutex_init(&suspmutex) returned %d\n", iError);
+ return;
+ }
+ m_fSuspmutexInitialized = TRUE;
+#endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+}
+
+/*++
+Function:
+ InitializePreCreate
+
+InitializePreCreate initializes the semaphores and signal masks used
+for thread suspension. At the end, it sets the calling thread's
+signal mask to the default signal mask.
+--*/
+PAL_ERROR
+CThreadSuspensionInfo::InitializePreCreate()
+{
+ PAL_ERROR palError = ERROR_INTERNAL_ERROR;
+ int iError = 0;
+#if SEM_INIT_MODIFIES_ERRNO
+ int nStoredErrno;
+#endif // SEM_INIT_MODIFIES_ERRNO
+
+#if USE_POSIX_SEMAPHORES
+
+#if SEM_INIT_MODIFIES_ERRNO
+ nStoredErrno = errno;
+#endif // SEM_INIT_MODIFIES_ERRNO
+
+ // initialize suspension semaphore
+ iError = sem_init(&m_semSusp, 0, 0);
+
+#if SEM_INIT_MODIFIES_ERRNO
+ if (iError == 0)
+ {
+ // Restore errno if sem_init succeeded.
+ errno = nStoredErrno;
+ }
+#endif // SEM_INIT_MODIFIES_ERRNO
+
+ if (0 != iError )
+ {
+ ASSERT("sem_init(&suspsem) returned %d\n", iError);
+ goto InitializePreCreateExit;
+ }
+
+#if SEM_INIT_MODIFIES_ERRNO
+ nStoredErrno = errno;
+#endif // SEM_INIT_MODIFIES_ERRNO
+
+ // initialize resume semaphore
+ iError = sem_init(&m_semResume, 0, 0);
+
+#if SEM_INIT_MODIFIES_ERRNO
+ if (iError == 0)
+ {
+ // Restore errno if sem_init succeeded.
+ errno = nStoredErrno;
+ }
+#endif // SEM_INIT_MODIFIES_ERRNO
+
+ if (0 != iError )
+ {
+ ASSERT("sem_init(&suspsem) returned %d\n", iError);
+ sem_destroy(&m_semSusp);
+ goto InitializePreCreateExit;
+ }
+
+ m_fSemaphoresInitialized = TRUE;
+
+#elif USE_SYSV_SEMAPHORES
+ // preparing to initialize the SysV semaphores.
+ union semun semunData;
+ m_nSemsuspid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
+ if (m_nSemsuspid == -1)
+ {
+ ASSERT("semget for suspension sem id returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ goto InitializePreCreateExit;
+ }
+
+ m_nSemrespid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
+ if (m_nSemrespid == -1)
+ {
+ ASSERT("semget for resumption sem id returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ goto InitializePreCreateExit;
+ }
+
+ if (m_nSemsuspid == m_nSemrespid)
+ {
+ ASSERT("Suspension and Resumption Semaphores have the same id\n");
+ goto InitializePreCreateExit;
+ }
+
+ semunData.val = 0;
+ iError = semctl(m_nSemsuspid, 0, SETVAL, semunData);
+ if (iError == -1)
+ {
+ ASSERT("semctl for suspension sem id returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ goto InitializePreCreateExit;
+ }
+
+ semunData.val = 0;
+ iError = semctl(m_nSemrespid, 0, SETVAL, semunData);
+ if (iError == -1)
+ {
+ ASSERT("semctl for resumption sem id returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ goto InitializePreCreateExit;
+ }
+
+ // initialize suspend semaphore
+ m_sbSemwait.sem_num = 0;
+ m_sbSemwait.sem_op = -1;
+ m_sbSemwait.sem_flg = 0;
+
+ // initialize resume semaphore
+ m_sbSempost.sem_num = 0;
+ m_sbSempost.sem_op = 1;
+ m_sbSempost.sem_flg = 0;
+#elif USE_PTHREAD_CONDVARS
+ iError = pthread_cond_init(&m_condSusp, NULL);
+ if (iError != 0)
+ {
+ ASSERT("pthread_cond_init for suspension returned %d (%s)\n", iError, strerror(iError));
+ goto InitializePreCreateExit;
+ }
+
+ iError = pthread_mutex_init(&m_mutexSusp, NULL);
+ if (iError != 0)
+ {
+ ASSERT("pthread_mutex_init for suspension returned %d (%s)\n", iError, strerror(iError));
+ goto InitializePreCreateExit;
+ }
+
+ iError = pthread_cond_init(&m_condResume, NULL);
+ if (iError != 0)
+ {
+ ASSERT("pthread_cond_init for resume returned %d (%s)\n", iError, strerror(iError));
+ goto InitializePreCreateExit;
+ }
+
+ iError = pthread_mutex_init(&m_mutexResume, NULL);
+ if (iError != 0)
+ {
+ ASSERT("pthread_mutex_init for resume returned %d (%s)\n", iError, strerror(iError));
+ goto InitializePreCreateExit;
+ }
+
+ m_fSemaphoresInitialized = TRUE;
+#endif // USE_POSIX_SEMAPHORES
+
+ // Initialization was successful.
+ palError = NO_ERROR;
+
+InitializePreCreateExit:
+
+ if (NO_ERROR == palError && 0 != iError)
+ {
+ switch (iError)
+ {
+ case ENOMEM:
+ case EAGAIN:
+ {
+ palError = ERROR_OUTOFMEMORY;
+ break;
+ }
+ default:
+ {
+ ASSERT("A pthrSuspender init call returned %d (%s)\n", iError, strerror(iError));
+ palError = ERROR_INTERNAL_ERROR;
+ }
+ }
+ }
+
+ return palError;
+}
+
+CThreadSuspensionInfo::~CThreadSuspensionInfo()
+{
+#if !DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ if (m_fSuspmutexInitialized)
+ {
+ INDEBUG(int iError = )
+ pthread_mutex_destroy(&m_ptmSuspmutex);
+ _ASSERT_MSG(0 == iError, "pthread_mutex_destroy returned %d (%s)\n", iError, strerror(iError));
+ }
+#endif
+
+#if USE_POSIX_SEMAPHORES
+ if (m_fSemaphoresInitialized)
+ {
+ int iError;
+
+ iError = sem_destroy(&m_semSusp);
+ _ASSERT_MSG(0 == iError, "sem_destroy failed and set errno to %d (%s)\n", errno, strerror(errno));
+
+ iError = sem_destroy(&m_semResume);
+ _ASSERT_MSG(0 == iError, "sem_destroy failed and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+#elif USE_SYSV_SEMAPHORES
+ DestroySemaphoreIds();
+#elif USE_PTHREAD_CONDVARS
+ if (m_fSemaphoresInitialized)
+ {
+ int iError;
+
+ iError = pthread_cond_destroy(&m_condSusp);
+ _ASSERT_MSG(0 == iError, "pthread_cond_destroy failed with %d (%s)\n", iError, strerror(iError));
+
+ iError = pthread_mutex_destroy(&m_mutexSusp);
+ _ASSERT_MSG(0 == iError, "pthread_mutex_destroy failed with %d (%s)\n", iError, strerror(iError));
+
+ iError = pthread_cond_destroy(&m_condResume);
+ _ASSERT_MSG(0 == iError, "pthread_cond_destroy failed with %d (%s)\n", iError, strerror(iError));
+
+ iError = pthread_mutex_destroy(&m_mutexResume);
+ _ASSERT_MSG(0 == iError, "pthread_mutex_destroy failed with %d (%s)\n", iError, strerror(iError));
+ }
+#endif // USE_POSIX_SEMAPHORES
+}
+
+#if USE_SYSV_SEMAPHORES
+/*++
+Function:
+ DestroySemaphoreIds
+
+DestroySemaphoreIds is called from the CThreadSuspensionInfo destructor and
+from PROCCleanupThreadSemIds. If a thread exits before shutdown or is suspended
+during shutdown, its destructor will be invoked and the semaphore ids destroyed.
+In assert or exceptions situations that are suspension unsafe,
+PROCCleanupThreadSemIds is called, which uses DestroySemaphoreIds.
+--*/
+void
+CThreadSuspensionInfo::DestroySemaphoreIds()
+{
+ union semun semunData;
+ if (m_nSemsuspid != 0)
+ {
+ semunData.val = 0;
+ if (0 != semctl(m_nSemsuspid, 0, IPC_RMID, semunData))
+ {
+ ERROR("semctl(Semsuspid) failed and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+ else
+ {
+ m_nSemsuspid = 0;
+ }
+ }
+ if (this->m_nSemrespid)
+ {
+ semunData.val = 0;
+ if (0 != semctl(m_nSemrespid, 0, IPC_RMID, semunData))
+ {
+ ERROR("semctl(Semrespid) failed and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+ else
+ {
+ m_nSemrespid = 0;
+ }
+ }
+}
+#endif // USE_SYSV_SEMAPHORES
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories(${ROOT_DIR}/src)
-include_directories(${ROOT_DIR}/src/pal/inc)
-include_directories(${ROOT_DIR}/src/pal/inc/rt)
-
-# Include the dummy c++ include files
-include_directories(${ROOT_DIR}/src/pal/inc/rt/cpp)
-
-# This prevents inclusion of standard C compiler headers
-add_compile_options(-nostdinc)
-
-add_compile_options(-fPIC)
-add_definitions(-DUNICODE)
-add_definitions(-D_UNICODE)
set(PALRT_SOURCES
bstr.cpp
--- /dev/null
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_definitions(-D_BLD_CLR)
+
+set(UTILCODE_COMMON_SOURCES
+ clrhost_nodependencies.cpp
+ ex.cpp
+ sbuffer.cpp
+ sstring_com.cpp
+ fstring.cpp
+ namespaceutil.cpp
+ check.cpp
+ sstring.cpp
+ safewrap.cpp
+ debug.cpp
+ pedecoder.cpp
+ longfilepathwrappers.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(CLR_CMAKE_TARGET_WIN32)
+ list(APPEND UTILCODE_COMMON_SOURCES
+ dlwrap.cpp
+ securitywrapper.cpp
+ securityutil.cpp
+ )
+endif(CLR_CMAKE_TARGET_WIN32)
+
+set(UTILCODE_STATICNOHOST_SOURCES
+ ${UTILCODE_COMMON_SOURCES}
+ hostimpl.cpp
+)
+
+convert_to_absolute_path(UTILCODE_STATICNOHOST_SOURCES ${UTILCODE_STATICNOHOST_SOURCES})
+
+add_library_clr(utilcodestaticnohost STATIC ${UTILCODE_STATICNOHOST_SOURCES})
+
+if(CLR_CMAKE_HOST_WIN32)
+ target_compile_definitions(utilcodestaticnohost PRIVATE _CRTIMP=) # use static version of crt
+endif(CLR_CMAKE_HOST_WIN32)
+
+target_compile_definitions(utilcodestaticnohost PRIVATE FEATURE_UTILCODE_NO_DEPENDENCIES)
+target_compile_definitions(utilcodestaticnohost PRIVATE SELF_NO_HOST)
+target_precompile_headers(utilcodestaticnohost PRIVATE [["stdafx.h"]])
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//================================================================================
+// 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;
+
+thread_local LONG CHECK::t_count;
+#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;
+
+#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(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|ModeViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultNotFatal);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|TakesLockViolation);
+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|FaultViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultNotFatal|ModeViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultNotFatal|TakesLockViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultNotFatal|TakesLockViolation|ModeViolation);
+SPECIALIZED_VIOLATION(GCViolation|ModeViolation);
+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_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_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_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;
+
+ // 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;
+}
+
+#endif
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+
+#include "stdafx.h"
+
+#include "clrhost.h"
+#include "utilcode.h"
+#include "ex.h"
+#include "clrnt.h"
+#include "contract.h"
+
+#if defined __llvm__
+# if defined(__has_feature) && __has_feature(address_sanitizer)
+# define HAS_ADDRESS_SANITIZER
+# endif
+#endif
+
+#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;
+}
+
+#ifdef HAS_ADDRESS_SANITIZER
+// use the functionality from address santizier (which does not throw exceptions)
+#else
+
+#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 // HAS_ADDRESS_SANITIZER
+#endif //_DEBUG_IMPL
+
+#if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
+
+thread_local ClrDebugState* t_pClrDebugState;
+
+// Fls callback to deallocate ClrDebugState when our FLS block goes away.
+void FreeClrDebugState(LPVOID pTlsData)
+{
+ 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))
+ {
+ // 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);
+ }
+}
+
+//=============================================================================================
+// 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()
+{
+ // 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 );
+ gBadClrDebugState.SetOkToThrow();
+
+ ClrDebugState *pNewClrDebugState = NULL;
+ ClrDebugState *pClrDebugState = NULL;
+ DbgStateLockData *pNewLockData = NULL;
+
+ // 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.
+ pNewClrDebugState = (ClrDebugState*)HeapAlloc(GetProcessHeap(), 0, sizeof(ClrDebugState));
+ if (pNewClrDebugState != NULL)
+ {
+ // Only allocate a DbgStateLockData if its owning ClrDebugState was successfully allocated
+ pNewLockData = (DbgStateLockData *)HeapAlloc(GetProcessHeap(), 0, sizeof(DbgStateLockData));
+ }
+
+ 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 = t_pClrDebugState;
+ 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);
+
+ t_pClrDebugState = pClrDebugState;
+
+ // 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));
+
+ 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);
+ }
+ }
+
+ return pClrDebugState;
+} // CLRInitDebugState
+
+#endif //defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
+
+const NoThrow nothrow = { 0 };
+
+#if defined(HAS_ADDRESS_SANITIZER) || defined(DACCESS_COMPILE)
+// use standard heap functions for address santizier
+#else
+
+#ifdef _DEBUG
+#ifdef TARGET_X86
+#define OS_HEAP_ALIGN 8
+#else
+#define OS_HEAP_ALIGN 16
+#endif
+#define CLRALLOC_TAG 0x2ce145f1
+#endif
+
+#ifdef HOST_WINDOWS
+static HANDLE g_hProcessHeap;
+#endif
+
+FORCEINLINE void* ClrMalloc(size_t size)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+#ifdef FAILPOINTS_ENABLED
+ if (RFS_HashStack())
+ return NULL;
+#endif
+
+ void* p;
+
+#ifdef _DEBUG
+ size += OS_HEAP_ALIGN;
+#endif
+
+#ifdef HOST_WINDOWS
+ HANDLE hHeap = g_hProcessHeap;
+ if (hHeap == NULL)
+ {
+ InterlockedCompareExchangeT(&g_hProcessHeap, ::GetProcessHeap(), NULL);
+ hHeap = g_hProcessHeap;
+ }
+
+ p = HeapAlloc(hHeap, 0, size);
+#else
+ p = malloc(size);
+#endif
+
+#ifdef _DEBUG
+ // Store the tag to detect heap contamination
+ if (p != NULL)
+ {
+ *((DWORD*)p) = CLRALLOC_TAG;
+ p = (BYTE*)p + OS_HEAP_ALIGN;
+ }
+#endif
+
+#ifndef SELF_NO_HOST
+ if (p == NULL
+ // If we have not created StressLog ring buffer, we should not try to use it.
+ // StressLog is going to do a memory allocation. We may enter an endless loop.
+ && StressLog::t_pCurrentThreadLog != NULL)
+ {
+ STRESS_LOG_OOM_STACK(size);
+ }
+#endif
+
+ return p;
+}
+
+FORCEINLINE void ClrFree(void* p)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+#ifdef _DEBUG
+ if (p != NULL)
+ {
+ // Check the heap handle to detect heap contamination
+ p = (BYTE*)p - OS_HEAP_ALIGN;
+ if (*((DWORD*)p) != CLRALLOC_TAG)
+ _ASSERTE(!"Heap contamination detected! HeapFree was called on a heap other than the one that memory was allocated from.\n"
+ "Possible cause: you used new (executable) to allocate the memory, but didn't use DeleteExecutable() to free it.");
+ }
+#endif
+
+#ifdef HOST_WINDOWS
+ if (p != NULL)
+ HeapFree(g_hProcessHeap, 0, p);
+#else
+ free(p);
+#endif
+}
+
+void * __cdecl
+operator new(size_t n)
+{
+#ifdef _DEBUG_IMPL
+ CLRThrowsExceptionWorker();
+#endif
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ void* result = ClrMalloc(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_SUPPORTS_DAC_HOST_ONLY;
+
+ void* result = ClrMalloc(n);
+ if (result == NULL) {
+ ThrowOutOfMemory();
+ }
+ TRASH_LASTERROR;
+ return result;
+};
+
+#endif // HAS_ADDRESS_SANITIZER || DACCESS_COMPILE
+
+void * __cdecl operator new(size_t n, const NoThrow&) NOEXCEPT
+{
+#if defined(HAS_ADDRESS_SANITIZER) || defined(DACCESS_COMPILE)
+ // use standard heap functions for address santizier (which doesn't provide for NoThrow)
+ void * result = operator new(n);
+#else
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ void* result = ClrMalloc(n);
+#endif // HAS_ADDRESS_SANITIZER || DACCESS_COMPILE
+ TRASH_LASTERROR;
+ return result;
+}
+
+void * __cdecl operator new[](size_t n, const NoThrow&) NOEXCEPT
+{
+#if defined(HAS_ADDRESS_SANITIZER) || defined(DACCESS_COMPILE)
+ // use standard heap functions for address santizier (which doesn't provide for NoThrow)
+ void * result = operator new[](n);
+#else
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ void* result = ClrMalloc(n);
+#endif // HAS_ADDRESS_SANITIZER || DACCESS_COMPILE
+ TRASH_LASTERROR;
+ return result;
+}
+
+#if defined(HAS_ADDRESS_SANITIZER) || defined(DACCESS_COMPILE)
+// use standard heap functions for address santizier
+#else
+void __cdecl
+operator delete(void *p) NOEXCEPT
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ ClrFree(p);
+
+ TRASH_LASTERROR;
+}
+
+void __cdecl
+operator delete[](void *p) NOEXCEPT
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ ClrFree(p);
+ TRASH_LASTERROR;
+}
+
+#endif // HAS_ADDRESS_SANITIZER || DACCESS_COMPILE
+
+/* ------------------------------------------------------------------------ *
+ * New operator overloading for the executable heap
+ * ------------------------------------------------------------------------ */
+
+#ifdef HOST_WINDOWS
+
+HANDLE ClrGetProcessExecutableHeap()
+{
+ // Note: this can be called a little early for real contracts, so we use static contracts instead.
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ static HANDLE g_ExecutableHeapHandle;
+
+ //
+ // Create the executable heap lazily
+ //
+ if (g_ExecutableHeapHandle == NULL)
+ {
+
+ HANDLE ExecutableHeapHandle = HeapCreate(
+ HEAP_CREATE_ENABLE_EXECUTE, // heap allocation attributes
+ 0, // initial heap size
+ 0 // maximum heap size; 0 == growable
+ );
+
+ if (ExecutableHeapHandle == NULL)
+ return NULL;
+
+ HANDLE ExistingValue = InterlockedCompareExchangeT(&g_ExecutableHeapHandle, ExecutableHeapHandle, NULL);
+ if (ExistingValue != NULL)
+ {
+ HeapDestroy(ExecutableHeapHandle);
+ }
+ }
+
+ return g_ExecutableHeapHandle;
+}
+
+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;
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL) {
+ ThrowOutOfMemory();
+ }
+
+ void * result = HeapAlloc(hExecutableHeap, 0, 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;
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL) {
+ ThrowOutOfMemory();
+ }
+
+ void * result = HeapAlloc(hExecutableHeap, 0, 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;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL)
+ return NULL;
+
+ void * result = HeapAlloc(hExecutableHeap, 0, 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;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL)
+ return NULL;
+
+ void * result = HeapAlloc(hExecutableHeap, 0, n);
+ TRASH_LASTERROR;
+ return result;
+}
+
+#endif // HOST_WINDOWS
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// Debug.cpp
+//
+// Helper code for debugging.
+//*****************************************************************************
+//
+
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "ex.h"
+#include "corexcep.h"
+
+#include "log.h"
+
+extern "C" _CRTIMP int __cdecl _flushall(void);
+
+Volatile<LONG> g_DbgSuppressAllocationAsserts = 0;
+
+#ifdef _DEBUG
+
+VOID LogAssert(
+ LPCSTR szFile,
+ int iLine,
+ LPCSTR szExpr
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ 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 TARGET_UNIX
+ GetLocalTime(&st);
+#else
+ GetSystemTime(&st);
+#endif
+
+ PathString exename;
+ WszGetModuleFileName(NULL, exename);
+
+ 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.GetUnicode()));
+}
+
+//*****************************************************************************
+// This function is called in order to ultimately return an out of memory
+// failed hresult. But this code 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 allocation, 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;
+ 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.
+//*****************************************************************************
+bool _DbgBreakCheck(
+ LPCSTR szFile,
+ int iLine,
+ LPCSTR szExpr,
+ BOOL fConstrained)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ 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("\nAssert failure(PID %d [0x%08x], Thread: %d [0x%04x]): %hs\n")
+ W(" File: %hs Line: %d\n")
+ W(" Image: "),
+ GetCurrentProcessId(), GetCurrentProcessId(),
+ GetCurrentThreadId(), GetCurrentThreadId(),
+ szExpr, szFile, iLine);
+ debugOutput.Append(modulePath);
+ debugOutput.Append(W("\n\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/0x%04x)"),
+ 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 (IsDebuggerPresent())
+ {
+ return true; // like a retry
+ }
+
+ TerminateProcess(GetCurrentProcess(), 1);
+ return false;
+}
+
+bool _DbgBreakCheckNoThrow(
+ LPCSTR szFile,
+ int iLine,
+ LPCSTR szExpr,
+ BOOL fConstrained)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ bool failed = false;
+ bool result = false;
+ EX_TRY
+ {
+ result = _DbgBreakCheck(szFile, iLine, szExpr, fConstrained);
+ }
+ EX_CATCH
+ {
+ failed = true;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (failed)
+ {
+ return true;
+ }
+ return result;
+}
+
+// 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++;
+}
+void *dbgForceToMemory; // dummy pointer that pessimises enregistration
+
+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 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;
+
+ LONG lAlreadyOwned = InterlockedExchange((LPLONG)&g_BufferLock, 1);
+ if (lAlreadyOwned == 1)
+ {
+ if (_DbgBreakCheckNoThrow(szFile, iLine, szExpr, FALSE))
+ {
+ _DbgBreak();
+ }
+ }
+ else
+ {
+ char *szExprToDisplay = (char*)szExpr;
+ if (_DbgBreakCheckNoThrow(szFile, iLine, szExprToDisplay, FALSE))
+ {
+ _DbgBreak();
+ }
+
+ g_BufferLock = 0;
+ }
+} // DbgAssertDialog
+
+#endif // _DEBUG
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#include "stdafx.h" // Precompiled header key.
+#include "utilcode.h"
+#include "metadata.h"
+#include "ex.h"
+#include "pedecoder.h"
+
+#include <wininet.h>
+#include <urlmon.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;
+
+}
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+// ---------------------------------------------------------------------------
+// Ex.cpp
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "string.h"
+#include "ex.h"
+#include "holder.h"
+
+// error codes
+#include "corerror.h"
+
+#include "resource.h"
+
+#include "olectl.h"
+
+#include "corexcep.h"
+
+#define MAX_EXCEPTION_MSG 200
+
+// Set if fatal error (like stack overflow or out of memory) occurred 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(Exception **ppException);
+
+// Helper function to get pointer to clr module base
+void* GetClrModuleBase();
+
+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((void*)&g_OOMExceptionInstance, (void*)&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;
+ 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_CVTRES_NOT_FOUND)
+ CASE_HRESULT(COR_E_APPDOMAINUNLOADED)
+ CASE_HRESULT(COR_E_CANNOTUNLOADAPPDOMAIN)
+ CASE_HRESULT(MSEE_E_ASSEMBLYLOADINPROGRESS)
+ CASE_HRESULT(COR_E_FIXUPSINEXE)
+ 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_PRIVATE_ASM_DISALLOWED)
+ CASE_HRESULT(FUSION_E_SIGNATURE_CHECK_FAILED)
+ CASE_HRESULT(FUSION_E_INVALID_NAME)
+ CASE_HRESULT(FUSION_E_CODE_DOWNLOAD_DISABLED)
+ CASE_HRESULT(CLDB_E_FILE_BADREAD)
+ CASE_HRESULT(CLDB_E_FILE_BADWRITE)
+ CASE_HRESULT(CLDB_S_TRUNCATION)
+ CASE_HRESULT(CLDB_E_FILE_OLDVER)
+ CASE_HRESULT(CLDB_E_SMDUPLICATE)
+ CASE_HRESULT(CLDB_E_NO_DATA)
+ CASE_HRESULT(CLDB_E_INCOMPATIBLE)
+ CASE_HRESULT(CLDB_E_FILE_CORRUPT)
+ CASE_HRESULT(CLDB_E_BADUPDATEMODE)
+ CASE_HRESULT(CLDB_E_INDEX_NOTFOUND)
+ CASE_HRESULT(CLDB_E_RECORD_NOTFOUND)
+ CASE_HRESULT(CLDB_E_RECORD_OUTOFORDER)
+ CASE_HRESULT(CLDB_E_TOO_BIG)
+ CASE_HRESULT(META_E_BADMETADATA)
+ CASE_HRESULT(META_E_BAD_SIGNATURE)
+ CASE_HRESULT(META_E_BAD_INPUT_PARAMETER)
+ CASE_HRESULT(META_E_CANNOTRESOLVETYPEREF)
+ CASE_HRESULT(META_S_DUPLICATE)
+ CASE_HRESULT(META_E_STRINGSPACE_FULL)
+ CASE_HRESULT(META_E_HAS_UNMARKALL)
+ CASE_HRESULT(META_E_MUST_CALL_UNMARKALL)
+ 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_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_CONSTSIZE)
+ CASE_HRESULT(META_E_CA_FIXEDSTR_SIZE_REQUIRED)
+ CASE_HRESULT(META_E_CA_CUSTMARSH_TYPE_REQUIRED)
+ CASE_HRESULT(META_E_CA_BAD_FRIENDS_ARGS)
+ CASE_HRESULT(VLDTR_E_RID_OUTOFRANGE)
+ CASE_HRESULT(VLDTR_E_STRING_INVALID)
+ CASE_HRESULT(VLDTR_E_GUID_INVALID)
+ CASE_HRESULT(VLDTR_E_BLOB_INVALID)
+ CASE_HRESULT(VLDTR_E_MR_BADCALLINGCONV)
+ CASE_HRESULT(VLDTR_E_SIGNULL)
+ 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_TD_ENCLNOTNESTED)
+ CASE_HRESULT(VLDTR_E_FMD_PINVOKENOTSTATIC)
+ CASE_HRESULT(VLDTR_E_SIG_SENTINMETHODDEF)
+ CASE_HRESULT(VLDTR_E_SIG_SENTMUSTVARARG)
+ CASE_HRESULT(VLDTR_E_SIG_MULTSENTINELS)
+ CASE_HRESULT(VLDTR_E_SIG_MISSARG)
+ CASE_HRESULT(VLDTR_E_SIG_BYREFINFIELD)
+ 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_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_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_S_FUNC_EVAL_ABORTED)
+ CASE_HRESULT(CORDBG_E_STATIC_VAR_NOT_AVAILABLE)
+ 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_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_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_CANT_ADD_FIELD_TO_VALUE_OR_LAYOUT_CLASS)
+ CASE_HRESULT(CORDBG_E_FIELD_NOT_STATIC)
+ CASE_HRESULT(CORDBG_E_FIELD_NOT_INSTANCE)
+ CASE_HRESULT(CORDBG_E_ENC_JIT_CANT_UPDATE)
+ 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_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_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_FUNCTION_NOT_IL)
+ CASE_HRESULT(CORPROF_E_NOT_MANAGED_THREAD)
+ CASE_HRESULT(CORPROF_E_CALL_ONLY_FROM_INIT)
+ CASE_HRESULT(CORPROF_E_NOT_YET_AVAILABLE)
+ 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_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_INVALID_IMAGE_FORMAT)
+ CASE_HRESULT(CORSEC_E_CRYPTO)
+ CASE_HRESULT(CORSEC_E_CRYPTO_UNEX_OPER)
+ CASE_HRESULT(CORSECATTR_E_BAD_ACTION)
+ 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_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_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_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(VER_E_FIELD_SIG)
+ 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(&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)
+{
+ 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 ThrowWin32(DWORD err)
+{
+ 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;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+#ifndef DACCESS_COMPILE
+
+ // Use volatile store to prevent compiler from optimizing the static variable away
+ VolatileStoreWithoutBarrier<HRESULT>(&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"
+
+//--------------------------------------------------------------------------------
+// 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
+
+#ifdef _MSC_VER
+#pragma optimize("", off)
+#endif // _MSC_VER
+
+void ExThrowTrap(const char *fcn, const char *file, int line, const char *szType, HRESULT hr, const char *args)
+{
+ SUPPORTS_DAC;
+ return;
+}
+
+#ifdef _MSC_VER
+#pragma optimize("", on)
+#endif // _MSC_VER
+
+#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;
+
+ fHaveDescr = strDescr.FormatMessage(dwFlags, 0, hr, 0);
+ }
+
+ 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(" ("));
+ }
+
+ 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);
+}
+
+//===========================================================================================
+// 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)
+ exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE - 1] = (ULONG_PTR)GetClrModuleBase();
+#endif // !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+
+ 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_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)
+ if ((ULONG_PTR)GetClrModuleBase() != pcER->ExceptionInformation[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE - 1] )
+ {
+ return FALSE;
+ }
+ return TRUE;
+#else // !(!defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+ return FALSE;
+#endif // !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// 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);
+}
+
+//===========================================================================================
+//===========================================================================================
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// 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();
+ }
+
+ // Remove the count of null terminator, to be consistent with the all-ASCII case.
+ --*pLength;
+
+ if (*pLength > MAX_LENGTH)
+ {
+ return COR_E_OVERFLOW;
+ }
+ }
+
+ return S_OK;
+}
+
+
+// UNICODE to UTF8
+HRESULT Unicode_Utf8(_In_z_ LPCWSTR pString, bool allAscii, _Out_writes_bytes_(length) 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 + 1, 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();
+ }
+
+ // Remove the count of null terminator, to be consistent with the all-ASCII case.
+ --*pLength;
+
+ if (* pLength > MAX_LENGTH)
+ {
+ return COR_E_OVERFLOW;
+ }
+ }
+
+ return S_OK;
+}
+
+
+// UTF8 to Unicode
+
+HRESULT Utf8_Unicode(_In_z_ LPCSTR pString, bool allAscii, _Out_writes_bytes_(length) 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 + 1);
+
+ if (length == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ }
+
+ return S_OK;
+}
+
+
+HRESULT ConvertUnicode_Utf8(_In_z_ LPCWSTR pString, _Outptr_result_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, _Outptr_result_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
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h"
+
+#include "mscoree.h"
+#include "clrinternal.h"
+#include "clrhost.h"
+#include "ex.h"
+
+thread_local size_t t_ThreadType;
+
+CRITSEC_COOKIE ClrCreateCriticalSection(CrstType crstType, CrstFlags flags)
+{
+ CRITICAL_SECTION *cs = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
+ InitializeCriticalSection(cs);
+ return (CRITSEC_COOKIE)cs;
+}
+
+void ClrDeleteCriticalSection(CRITSEC_COOKIE cookie)
+{
+ _ASSERTE(cookie);
+ DeleteCriticalSection((CRITICAL_SECTION*)cookie);
+ free(cookie);
+}
+
+void ClrEnterCriticalSection(CRITSEC_COOKIE cookie)
+{
+ _ASSERTE(cookie);
+ EnterCriticalSection((CRITICAL_SECTION*)cookie);
+}
+
+void ClrLeaveCriticalSection(CRITSEC_COOKIE cookie)
+{
+ _ASSERTE(cookie);
+ LeaveCriticalSection((CRITICAL_SECTION*)cookie);
+}
+
+LPVOID 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 ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return VirtualFree(lpAddress, dwSize, dwFreeType);
+}
+
+SIZE_T ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ return VirtualQuery(lpAddress, lpBuffer, dwLength);
+}
+
+BOOL ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ return VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
+}
+
+//------------------------------------------------------------------------------
+// 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 GetLastThrownObjectExceptionFromThread(Exception** ppException)
+{
+ *ppException = NULL;
+}
+
+#ifdef HOST_WINDOWS
+void CreateCrashDumpIfEnabled(bool stackoverflow)
+{
+}
+#endif
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h"
+#include "windows.h"
+#include "longfilepathwrappers.h"
+#include "sstring.h"
+#include "ex.h"
+
+class LongFile
+{
+private:
+#ifdef HOST_WINDOWS
+ static const WCHAR* ExtendedPrefix;
+ static const WCHAR* DevicePathPrefix;
+ static const WCHAR* UNCPathPrefix;
+ static const WCHAR* UNCExtendedPathPrefix;
+ static const WCHAR VolumeSeparatorChar;
+ #define UNCPATHPREFIX W("\\\\")
+#endif //HOST_WINDOWS
+ static const WCHAR DirectorySeparatorChar;
+ static const WCHAR AltDirectorySeparatorChar;
+public:
+ static BOOL ContainsDirectorySeparator(SString & path);
+ static BOOL IsDirectorySeparator(WCHAR c);
+ static BOOL IsPathNotFullyQualified(const SString & path);
+
+ static HRESULT NormalizePath(SString& path);
+
+#ifdef HOST_WINDOWS
+ static BOOL IsExtended(const SString & path);
+ static BOOL IsUNCExtended(const SString & path);
+ static BOOL IsDevice(const SString & path);
+ static void NormalizeDirectorySeparators(SString& path);
+#endif
+};
+
+HMODULE
+LoadLibraryExWrapper(
+ LPCWSTR lpLibFileName,
+ HANDLE hFile,
+ DWORD dwFlags
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ HMODULE ret = NULL;
+ DWORD lastError;
+
+ EX_TRY
+ {
+
+ LongPathString path(LongPathString::Literal, lpLibFileName);
+
+ if (LongFile::IsPathNotFullyQualified(path) || SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+#ifdef HOST_WINDOWS
+ //Adding the assert to ensure relative paths which are not just filenames are not used for LoadLibrary Calls
+ _ASSERTE(!LongFile::IsPathNotFullyQualified(path) || !LongFile::ContainsDirectorySeparator(path));
+ LongFile::NormalizeDirectorySeparators(path);
+#endif //HOST_WINDOWS
+
+ ret = LoadLibraryExW(path.GetUnicode(), hFile, dwFlags);
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if(ret == NULL)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+HANDLE
+CreateFileWrapper(
+ _In_ LPCWSTR lpFileName,
+ _In_ DWORD dwDesiredAccess,
+ _In_ DWORD dwShareMode,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ _In_ DWORD dwCreationDisposition,
+ _In_ DWORD dwFlagsAndAttributes,
+ _In_opt_ HANDLE hTemplateFile
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD lastError;
+ HANDLE ret = INVALID_HANDLE_VALUE;
+
+ EX_TRY
+ {
+ LongPathString path(LongPathString::Literal, lpFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+ ret = CreateFileW(path.GetUnicode(),
+ dwDesiredAccess,
+ dwShareMode,
+ lpSecurityAttributes,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ hTemplateFile);
+
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == INVALID_HANDLE_VALUE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+DWORD
+GetFileAttributesWrapper(
+ _In_ LPCWSTR lpFileName
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = INVALID_FILE_ATTRIBUTES;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString path(LongPathString::Literal, lpFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+ ret = GetFileAttributesW(
+ path.GetUnicode()
+ );
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == INVALID_FILE_ATTRIBUTES)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+BOOL
+GetFileAttributesExWrapper(
+ _In_ LPCWSTR lpFileName,
+ _In_ GET_FILEEX_INFO_LEVELS fInfoLevelId,
+ _Out_writes_bytes_(sizeof(WIN32_FILE_ATTRIBUTE_DATA)) LPVOID lpFileInformation
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ BOOL ret = FALSE;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString path(LongPathString::Literal, lpFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+ ret = GetFileAttributesExW(
+ path.GetUnicode(),
+ fInfoLevelId,
+ lpFileInformation
+ );
+
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == FALSE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+BOOL
+DeleteFileWrapper(
+ _In_ LPCWSTR lpFileName
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ BOOL ret = FALSE;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString path(LongPathString::Literal, lpFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+ ret = DeleteFileW(
+ path.GetUnicode()
+ );
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == FALSE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+BOOL
+MoveFileExWrapper(
+ _In_ LPCWSTR lpExistingFileName,
+ _In_opt_ LPCWSTR lpNewFileName,
+ _In_ DWORD dwFlags
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ BOOL ret = FALSE;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
+ LongPathString Newpath(LongPathString::Literal, lpNewFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(Newpath)))
+ {
+ ret = MoveFileExW(
+ Existingpath.GetUnicode(),
+ Newpath.GetUnicode(),
+ dwFlags
+ );
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == FALSE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+
+}
+
+DWORD
+SearchPathWrapper(
+ _In_opt_ LPCWSTR lpPath,
+ _In_ LPCWSTR lpFileName,
+ _In_opt_ LPCWSTR lpExtension,
+ _In_ BOOL getPath,
+ SString& lpBuffer,
+ _Out_opt_ LPWSTR * lpFilePart
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString Existingpath(LongPathString::Literal, lpPath);
+
+ if (lpPath != NULL)
+ {
+ if (FAILED(LongFile::NormalizePath(Existingpath)))
+ {
+ ret = FALSE;
+ }
+ else
+ {
+ lpPath = Existingpath.GetUnicode();
+ }
+ }
+
+ if (!getPath)
+ {
+ ret = SearchPathW(
+ lpPath,
+ lpFileName,
+ lpExtension,
+ 0,
+ NULL,
+ NULL
+ );
+ }
+ else
+ {
+ COUNT_T size = lpBuffer.GetUnicodeAllocation() + 1;
+
+ ret = SearchPathW(
+ lpPath,
+ lpFileName,
+ lpExtension,
+ size,
+ lpBuffer.OpenUnicodeBuffer(size - 1),
+ lpFilePart
+ );
+
+ if (ret > size)
+ {
+ lpBuffer.CloseBuffer();
+ ret = SearchPathW(
+ lpPath,
+ lpFileName,
+ lpExtension,
+ ret,
+ lpBuffer.OpenUnicodeBuffer(ret - 1),
+ lpFilePart
+ );
+ }
+
+ lpBuffer.CloseBuffer(ret);
+
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+
+}
+
+DWORD
+GetModuleFileNameWrapper(
+ _In_opt_ HMODULE hModule,
+ SString& buffer
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ COUNT_T size = buffer.GetUnicodeAllocation() + 1;
+
+ ret = GetModuleFileNameW(
+ hModule,
+ buffer.OpenUnicodeBuffer(size - 1),
+ (DWORD)size
+ );
+
+
+ while (ret == size )
+ {
+ buffer.CloseBuffer();
+ size = size * 2;
+ ret = GetModuleFileNameW(
+ hModule,
+ buffer.OpenUnicodeBuffer(size - 1),
+ (DWORD)size
+ );
+
+ }
+
+
+ lastError = GetLastError();
+ buffer.CloseBuffer(ret);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+UINT WINAPI GetTempFileNameWrapper(
+ _In_ LPCTSTR lpPathName,
+ _In_ LPCTSTR lpPrefixString,
+ _In_ UINT uUnique,
+ SString& lpTempFileName
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ UINT ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ //Change the behaviour in Redstone to retry
+ COUNT_T size = MAX_LONGPATH;
+ WCHAR* buffer = lpTempFileName.OpenUnicodeBuffer(size - 1);
+ ret = GetTempFileNameW(
+ lpPathName,
+ lpPrefixString,
+ uUnique,
+ buffer
+ );
+
+ lastError = GetLastError();
+ size = (COUNT_T)wcslen(buffer);
+ lpTempFileName.CloseBuffer(size);
+
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+DWORD WINAPI GetTempPathWrapper(
+ SString& lpBuffer
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ //Change the behaviour in Redstone to retry
+ COUNT_T size = MAX_LONGPATH;
+
+ ret = GetTempPathW(
+ size,
+ lpBuffer.OpenUnicodeBuffer(size - 1)
+ );
+
+ lastError = GetLastError();
+ lpBuffer.CloseBuffer(ret);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+DWORD WINAPI GetCurrentDirectoryWrapper(
+ SString& lpBuffer
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ //Change the behaviour in Redstone to retry
+ COUNT_T size = MAX_LONGPATH;
+
+ ret = GetCurrentDirectoryW(
+ size,
+ lpBuffer.OpenUnicodeBuffer(size - 1)
+ );
+
+ lastError = GetLastError();
+ lpBuffer.CloseBuffer(ret);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+DWORD WINAPI GetEnvironmentVariableWrapper(
+ _In_opt_ LPCTSTR lpName,
+ _Out_opt_ SString& lpBuffer
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+
+ COUNT_T size = lpBuffer.GetUnicodeAllocation() + 1;
+
+ ret = GetEnvironmentVariableW(
+ lpName,
+ lpBuffer.OpenUnicodeBuffer(size - 1),
+ size
+ );
+
+ // We loop round getting the length of the env var and then trying to copy
+ // the value into a the allocated buffer. Usually we'll go through this loop
+ // precisely once, but the caution is ncessary in case the variable mutates
+ // beneath us, as the environment variable can be modified by another thread
+ //between two calls to GetEnvironmentVariableW
+
+ while (ret > size)
+ {
+ size = ret;
+ lpBuffer.CloseBuffer();
+ ret = GetEnvironmentVariableW(
+ lpName,
+ lpBuffer.OpenUnicodeBuffer(size - 1),
+ size);
+ }
+
+ lastError = GetLastError();
+ lpBuffer.CloseBuffer(ret);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+
+#ifdef HOST_WINDOWS
+
+BOOL
+CopyFileExWrapper(
+ _In_ LPCWSTR lpExistingFileName,
+ _In_ LPCWSTR lpNewFileName,
+ _In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine,
+ _In_opt_ LPVOID lpData,
+ _When_(pbCancel != NULL, _Pre_satisfies_(*pbCancel == FALSE))
+ _Inout_opt_ LPBOOL pbCancel,
+ _In_ DWORD dwCopyFlags
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ BOOL ret = FALSE;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
+ LongPathString Newpath(LongPathString::Literal, lpNewFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(Newpath)))
+ {
+ ret = CopyFileExW(
+ Existingpath.GetUnicode(),
+ Newpath.GetUnicode(),
+ lpProgressRoutine,
+ lpData,
+ pbCancel,
+ dwCopyFlags
+ );
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == FALSE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+HANDLE
+FindFirstFileExWrapper(
+ _In_ LPCWSTR lpFileName,
+ _In_ FINDEX_INFO_LEVELS fInfoLevelId,
+ _Out_writes_bytes_(sizeof(WIN32_FIND_DATAW)) LPVOID lpFindFileData,
+ _In_ FINDEX_SEARCH_OPS fSearchOp,
+ _Reserved_ LPVOID lpSearchFilter,
+ _In_ DWORD dwAdditionalFlags
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ HANDLE ret = INVALID_HANDLE_VALUE;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString path(LongPathString::Literal, lpFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+ ret = FindFirstFileExW(
+ path.GetUnicode(),
+ fInfoLevelId,
+ lpFindFileData,
+ fSearchOp,
+ lpSearchFilter,
+ dwAdditionalFlags
+ );
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == INVALID_HANDLE_VALUE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+#endif // HOST_WINDOWS
+
+//Implementation of LongFile Helpers
+const WCHAR LongFile::DirectorySeparatorChar = W('\\');
+const WCHAR LongFile::AltDirectorySeparatorChar = W('/');
+#ifdef HOST_WINDOWS
+const WCHAR LongFile::VolumeSeparatorChar = W(':');
+const WCHAR* LongFile::ExtendedPrefix = W("\\\\?\\");
+const WCHAR* LongFile::DevicePathPrefix = W("\\\\.\\");
+const WCHAR* LongFile::UNCExtendedPathPrefix = W("\\\\?\\UNC\\");
+const WCHAR* LongFile::UNCPathPrefix = UNCPATHPREFIX;
+
+void LongFile::NormalizeDirectorySeparators(SString& path)
+{
+ for(SString::Iterator i = path.Begin(); i < path.End(); ++i)
+ {
+ if (*i == AltDirectorySeparatorChar)
+ {
+ path.Replace(i, DirectorySeparatorChar);
+ }
+ }
+}
+
+BOOL LongFile::IsExtended(const SString & path)
+{
+ return path.BeginsWith(SL(ExtendedPrefix));
+}
+
+BOOL LongFile::IsUNCExtended(const SString & path)
+{
+ return path.BeginsWith(SL(UNCExtendedPathPrefix));
+}
+
+// Relative here means it could be relative to current directory on the relevant drive
+// NOTE: Relative segments ( \..\) are not considered relative
+// Returns true if the path specified is relative to the current drive or working directory.
+// Returns false if the path is fixed to a specific drive or UNC path. This method does no
+// validation of the path (URIs will be returned as relative as a result).
+// Handles paths that use the alternate directory separator. It is a frequent mistake to
+// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case.
+
+BOOL LongFile::IsPathNotFullyQualified(const SString & path)
+{
+ if (path.GetCount() < 2)
+ {
+ return TRUE; // It isn't fixed, it must be relative. There is no way to specify a fixed path with one character (or less).
+ }
+
+ if (IsDirectorySeparator(path[0]))
+ {
+ return !IsDirectorySeparator(path[1]); // There is no valid way to specify a relative path with two initial slashes
+ }
+
+ return !((path.GetCount() >= 3) //The only way to specify a fixed path that doesn't begin with two slashes is the drive, colon, slash format- i.e. "C:\"
+ && (path[1] == VolumeSeparatorChar)
+ && IsDirectorySeparator(path[2]));
+}
+
+BOOL LongFile::IsDevice(const SString & path)
+{
+ return path.BeginsWith(SL(DevicePathPrefix));
+}
+
+// This function will normalize paths if the path length exceeds MAX_PATH
+// The normalization examples are :
+// C:\foo\<long>\bar => \\?\C:\foo\<long>\bar
+// \\server\<long>\bar => \\?\UNC\server\<long>\bar
+HRESULT LongFile::NormalizePath(SString & path)
+{
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ COUNT_T prefixLen = 0;
+ if (path.IsEmpty()|| IsDevice(path) || IsExtended(path) || IsUNCExtended(path))
+ return S_OK;
+
+ if (!IsPathNotFullyQualified(path) && path.GetCount() < MAX_LONGPATH)
+ return S_OK;
+
+ //Now the path will be normalized
+
+ SString originalPath(path);
+ SString prefix(ExtendedPrefix);
+ prefixLen = prefix.GetCount();
+
+ if (path.BeginsWith(SL(UNCPathPrefix)))
+ {
+ prefix.Set(UNCExtendedPathPrefix);
+ //In this case if path is \\server the extended syntax should be like \\?\UNC\server
+ //The below logic populates the path from prefixLen offset from the start. This ensures that first 2 characters are overwritten
+ //
+ prefixLen = prefix.GetCount() - (COUNT_T)wcslen(UNCPATHPREFIX);
+ _ASSERTE(prefixLen > 0 );
+ }
+
+
+ COUNT_T size = path.GetUnicodeAllocation() + 1;
+ WCHAR* buffer = path.OpenUnicodeBuffer(size - 1);
+
+ ret = GetFullPathNameW(
+ originalPath.GetUnicode(),
+ size - prefixLen, //memory avilable for path after reserving for prefix
+ (buffer + prefixLen), //reserve memory for prefix
+ NULL
+ );
+
+ if (ret == 0)
+ {
+ return E_FAIL;
+ }
+
+ if (ret > size - prefixLen)
+ {
+ path.CloseBuffer();
+ size = ret + prefixLen;
+ buffer = path.OpenUnicodeBuffer(size -1);
+
+ ret = GetFullPathNameW(
+ originalPath.GetUnicode(),
+ ret, // memory required for the path
+ (buffer + prefixLen), //reserve memory for prefix
+ NULL
+ );
+
+ _ASSERTE(ret < size - prefixLen);
+
+ if (ret == 0)
+ {
+ return E_FAIL;
+ }
+ }
+
+ SString fullpath(SString::Literal,buffer + prefixLen);
+
+ //Check if the resolved path is a UNC. By default we assume relative path to resolve to disk
+ if (fullpath.BeginsWith(SL(UNCPathPrefix)) && prefixLen != prefix.GetCount() - (COUNT_T)wcslen(UNCPATHPREFIX))
+ {
+ //Remove the leading '\\' from the UNC path to be replaced with UNCExtendedPathPrefix
+ fullpath.Replace(fullpath.Begin(), (COUNT_T)wcslen(UNCPATHPREFIX), UNCExtendedPathPrefix);
+ path.CloseBuffer();
+ path.Set(fullpath);
+ }
+ else
+ {
+ //wcscpy_s always termintes with NULL, so we are saving the character that will be overwriiten
+ WCHAR temp = buffer[prefix.GetCount()];
+ wcscpy_s(buffer, prefix.GetCount() + 1, prefix.GetUnicode());
+ buffer[prefix.GetCount()] = temp;
+ path.CloseBuffer(ret + prefixLen);
+ }
+
+ return S_OK;
+}
+#else
+BOOL LongFile::IsPathNotFullyQualified(const SString & path)
+{
+ return TRUE;
+}
+
+//Don't need to do anything For XPlat
+HRESULT LongFile::NormalizePath(SString & path)
+{
+ return S_OK;
+}
+#endif //HOST_WINDOWS
+
+BOOL LongFile::ContainsDirectorySeparator(SString & path)
+{
+ return path.Find(path.Begin(), DirectorySeparatorChar) || path.Find(path.Begin(), AltDirectorySeparatorChar);
+}
+
+BOOL LongFile::IsDirectorySeparator(WCHAR c)
+{
+ return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
+}
+
+
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// 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_writes_(cchNameSpace) WCHAR *szNameSpace, // Output for namespace value.
+ int cchNameSpace, // Max chars for output.
+ _Out_writes_(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_writes_opt_ (cchNameSpace) LPUTF8 szNameSpace, // Output for namespace value.
+ int cchNameSpace, // Max chars for output.
+ _Out_writes_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_writes_(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_writes_(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_writes_(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_writes_(dwBuffer) WCHAR* pBuffer, // Buffer to receive 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_writes_ (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);
+}
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// --------------------------------------------------------------------------------
+// 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 (IsILOnly())
+ CHECK(CheckILOnly());
+ }
+ }
+
+ 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_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_OK;
+}
+
+BOOL PEDecoder::HasNTHeaders() const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ PRECONDITION(HasContents());
+ }
+ 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());
+ }
+ 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)));
+
+ 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.
+ // Also in the case of files embedded within a single-file app, the default alignment for assemblies is 16 bytes.
+ CHECK(CheckAligned(m_base, 16));
+ }
+
+ // @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)
+ // 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;
+ }
+ 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
+#if TARGET_WINDOWS
+ // On Windows we expect section starts to be a multiple of SectionAlignment.
+ // That is not an explicit requirement in the documentation, but it looks like OS loader expects it.
+ // In contrast to this, in Unix R2R files we keep lower 16bits of sections RVA and
+ // this condition does not hold.
+ CHECK(CheckAligned(addressStart, VAL32(pNT->OptionalHeader.SectionAlignment)));
+#endif
+ 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;
+}
+
+BOOL PEDecoder::HasWriteableSections() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckFormat());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_CHECK_END;
+
+ PTR_IMAGE_SECTION_HEADER pSection = FindFirstSection();
+ _ASSERTE(pSection != NULL);
+
+ PTR_IMAGE_SECTION_HEADER pSectionEnd = pSection + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+
+ while (pSection < pSectionEnd)
+ {
+ if ((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_WRITE)) != 0)
+ {
+ return TRUE;
+ }
+
+ pSection++;
+ }
+
+ return FALSE;
+}
+
+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;
+ }
+ 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;
+ }
+ 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;
+ }
+ 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;
+ }
+ 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;
+ }
+ 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;
+ }
+ 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));
+ }
+ 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;
+ 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;
+ }
+ 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;
+ }
+ 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;
+ 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;
+ 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;
+ 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;
+ }
+ 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();
+
+ // Currently composite r2r images miss some information, for example the version is 0.0.
+ // We may want to change that to something more conforming and explicit.
+ // For now, for compatibility purposes, we will accept that as a valid format.
+ bool possiblyCompositeR2R =
+ pCor->MinorRuntimeVersion == 0 &&
+ pCor->MajorRuntimeVersion == 0;
+
+ //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.
+ if (!possiblyCompositeR2R)
+ CHECK(VAL16(pCor->MajorRuntimeVersion) > 1 && VAL16(pCor->MajorRuntimeVersion) <= COR_VERSION_MAJOR);
+
+#ifdef HOST_WINDOWS
+ CHECK(CheckDirectory(&pCor->MetaData, IMAGE_SCN_MEM_WRITE, NULL_NOT_OK));
+#else
+ CHECK(CheckDirectory(
+ &pCor->MetaData,
+ possiblyCompositeR2R ? 0 : IMAGE_SCN_MEM_WRITE,
+ NULL_NOT_OK));
+#endif
+ 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 or ReadyToRun images)
+ // only they can have a native image header
+ if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0 && !possiblyCompositeR2R)
+ {
+ 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));
+
+ // Forbid HOT_MODEL_STREAM
+ 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;
+}
+
+
+
+// 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());
+ PRECONDITION(type == METADATA_SECTION_FULL);
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDirRet = &GetCorHeader()->MetaData;
+
+ 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());
+
+ if (HasReadyToRunHeader())
+ {
+ // Pretend R2R images are IL-only
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_IL_ONLY_CHECKED;
+ CHECK_OK;
+ }
+
+ // 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 HOST_64BIT
+ // 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
+
+
+bool ReadResourceDirectoryHeader(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rva, IMAGE_RESOURCE_DIRECTORY_ENTRY** ppDirectoryEntries, IMAGE_RESOURCE_DIRECTORY **ppResourceDirectory)
+{
+ if (!pDecoder->CheckRva(rva, sizeof(IMAGE_RESOURCE_DIRECTORY)))
+ {
+ return false;
+ }
+
+ *ppResourceDirectory = (IMAGE_RESOURCE_DIRECTORY *)pDecoder->GetRvaData(rva);
+
+ // Check to see if entire resource directory is accessible
+ if (!pDecoder->CheckRva(rva + sizeof(IMAGE_RESOURCE_DIRECTORY),
+ (sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * (*ppResourceDirectory)->NumberOfNamedEntries) +
+ (sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * (*ppResourceDirectory)->NumberOfIdEntries)))
+ {
+ return false;
+ }
+
+ *ppDirectoryEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)pDecoder->GetRvaData(rva + sizeof(IMAGE_RESOURCE_DIRECTORY));
+ return true;
+}
+
+bool ReadNameFromResourceDirectoryEntry(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries, DWORD iEntry, DWORD *pNameUInt, WCHAR **pNameStr)
+{
+ *pNameStr = NULL;
+ *pNameUInt = 0;
+
+ if (!IS_INTRESOURCE(pDirectoryEntries[iEntry].Name))
+ {
+ DWORD entryName = pDirectoryEntries[iEntry].Name;
+ if (!(entryName & IMAGE_RESOURCE_NAME_IS_STRING))
+ return false;
+ DWORD entryNameRva = (entryName & ~IMAGE_RESOURCE_NAME_IS_STRING) + rvaOfResourceSection;
+
+ if (!pDecoder->CheckRva(entryNameRva, sizeof(WORD)))
+ return false;
+
+ size_t entryNameLen = *(WORD*)pDecoder->GetRvaData(entryNameRva);
+ if (!pDecoder->CheckRva(entryNameRva, (COUNT_T)(sizeof(WORD) * (1 + entryNameLen))))
+ return false;
+ *pNameStr = new(nothrow) WCHAR[entryNameLen + 1];
+ if ((*pNameStr) == NULL)
+ return false;
+ memcpy((*pNameStr), (WCHAR*)pDecoder->GetRvaData(entryNameRva + sizeof(WORD)), entryNameLen * sizeof(WCHAR));
+ (*pNameStr)[entryNameLen] = 0;
+ }
+ else
+ {
+ DWORD name = pDirectoryEntries[iEntry].Name;
+ if (!IS_INTRESOURCE(name))
+ return false;
+
+ *pNameUInt = name;
+ }
+
+ return true;
+}
+
+DWORD ReadResourceDirectory(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rva, LPCWSTR name, BOOL *pisDirectory)
+{
+ *pisDirectory = FALSE;
+
+ IMAGE_RESOURCE_DIRECTORY* pResourceDirectory;
+ IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries;
+ if (!ReadResourceDirectoryHeader(pDecoder, rvaOfResourceSection, rva, &pDirectoryEntries, &pResourceDirectory))
+ {
+ return 0;
+ }
+
+ // A fast implementation of resource lookup uses a binary search, but our needs are simple, and a linear search
+ // is easier to prove correct, so do that instead.
+ DWORD iEntryCount = (DWORD)pResourceDirectory->NumberOfNamedEntries + (DWORD)pResourceDirectory->NumberOfIdEntries;
+
+ for (DWORD iEntry = 0; iEntry < iEntryCount; iEntry++)
+ {
+ BOOL foundEntry = FALSE;
+
+ if (IS_INTRESOURCE(name))
+ {
+ // name is id
+ if (pDirectoryEntries[iEntry].Name == (DWORD)(SIZE_T)name)
+ foundEntry = TRUE;
+ }
+ else
+ {
+ // name is string
+ DWORD entryName = pDirectoryEntries[iEntry].Name;
+ if (!(entryName & IMAGE_RESOURCE_NAME_IS_STRING))
+ continue;
+
+ DWORD entryNameRva = (entryName & ~IMAGE_RESOURCE_NAME_IS_STRING) + rvaOfResourceSection;
+
+ if (!pDecoder->CheckRva(entryNameRva, sizeof(WORD)))
+ return 0;
+
+ size_t entryNameLen = *(WORD*)pDecoder->GetRvaData(entryNameRva);
+ if (wcslen(name) != entryNameLen)
+ continue;
+
+ if (!pDecoder->CheckRva(entryNameRva, (COUNT_T)(sizeof(WORD) * (1 + entryNameLen))))
+ return 0;
+
+ if (memcmp((WCHAR*)pDecoder->GetRvaData(entryNameRva + sizeof(WORD)), name, entryNameLen * sizeof(WCHAR)) == 0)
+ foundEntry = TRUE;
+ }
+
+ if (!foundEntry)
+ continue;
+
+ *pisDirectory = !!(pDirectoryEntries[iEntry].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY);
+ DWORD offsetToData = pDirectoryEntries[iEntry].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY;
+ DWORD dataRva = rvaOfResourceSection + offsetToData;
+ return dataRva;
+ }
+
+ return 0;
+}
+
+DWORD ReadResourceDataEntry(const PEDecoder *pDecoder, DWORD rva, COUNT_T *pSize)
+{
+ *pSize = 0;
+
+ if (!pDecoder->CheckRva(rva, sizeof(IMAGE_RESOURCE_DATA_ENTRY)))
+ {
+ return 0;
+ }
+
+ IMAGE_RESOURCE_DATA_ENTRY *pDataEntry = (IMAGE_RESOURCE_DATA_ENTRY *)pDecoder->GetRvaData(rva);
+ *pSize = pDataEntry->Size;
+ return pDataEntry->OffsetToData;
+}
+
+void * PEDecoder::GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSize /*=NULL*/) const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ PRECONDITION(IsMapped());
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ COUNT_T sizeUnused = 0; // Use this variable if pSize is null
+ if (pSize == NULL)
+ pSize = &sizeUnused;
+
+ *pSize = 0;
+
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE))
+ return NULL;
+
+ COUNT_T resourceDataSize = 0;
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE);
+
+ if (pDir->VirtualAddress == 0)
+ return NULL;
+
+ BOOL isDirectory = FALSE;
+ DWORD nameTableRva = ReadResourceDirectory(this, pDir->VirtualAddress, pDir->VirtualAddress, lpType, &isDirectory);
+
+ if (!isDirectory)
+ return NULL;
+
+ if (nameTableRva == 0)
+ return NULL;
+
+ DWORD languageTableRva = ReadResourceDirectory(this, pDir->VirtualAddress, nameTableRva, lpName, &isDirectory);
+ if (!isDirectory)
+ return NULL;
+
+ if (languageTableRva == 0)
+ return NULL;
+
+ // This api is designed to find resources with LANGID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
+ // This translates to LANGID 0 as the initial lookup point, which is sufficient for the needs of this api at this time
+ // (FindResource in the Windows api implements a large number of fallback paths which this api does not implement)
+
+ DWORD resourceDataEntryRva = ReadResourceDirectory(this, pDir->VirtualAddress, languageTableRva, 0, &isDirectory);
+ if (isDirectory) // This must not be a resource directory itself
+ return NULL;
+
+ if (resourceDataEntryRva == 0)
+ return NULL;
+
+ DWORD resourceDataRva = ReadResourceDataEntry(this, resourceDataEntryRva, pSize);
+ if (!CheckRva(resourceDataRva, *pSize))
+ {
+ *pSize = 0;
+ return NULL;
+ }
+
+ return (void*)GetRvaData(resourceDataRva);
+}
+
+typedef bool (*PEDecoder_EnumerateResourceTableFunction)(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context);
+
+struct ResourceEnumerateNamesState
+{
+ PEDecoder_ResourceNamesCallbackFunction namesCallback;
+ PEDecoder_ResourceCallbackFunction langIDcallback;
+ void *context;
+ LPCWSTR nameType;
+ LPCWSTR nameName;
+ PEDecoder_EnumerateResourceTableFunction callbackPerName;
+ PEDecoder_EnumerateResourceTableFunction callbackPerLangID;
+};
+
+struct ResourceEnumerateTypesState
+{
+ PEDecoder_ResourceTypesCallbackFunction callback;
+ void *context;
+ LPCWSTR nameType;
+};
+
+bool EnumerateWin32ResourceTable(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rvaOfResourceTable, PEDecoder_EnumerateResourceTableFunction resourceTableEnumerator, void *context)
+{
+ IMAGE_RESOURCE_DIRECTORY* pResourceDirectory;
+ IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries;
+ if (!ReadResourceDirectoryHeader(pDecoder, rvaOfResourceSection, rvaOfResourceTable, &pDirectoryEntries, &pResourceDirectory))
+ {
+ return false;
+ }
+
+ DWORD iEntryCount = (DWORD)pResourceDirectory->NumberOfNamedEntries + (DWORD)pResourceDirectory->NumberOfIdEntries;
+
+ for (DWORD iEntry = 0; iEntry < iEntryCount; iEntry++)
+ {
+ DWORD nameUInt;
+ NewArrayHolder<WCHAR> nameString;
+ if (!ReadNameFromResourceDirectoryEntry(pDecoder, rvaOfResourceSection, pDirectoryEntries, iEntry, &nameUInt, &nameString))
+ return false;
+
+ LPCWSTR name = MAKEINTRESOURCEW(nameUInt);
+ if (nameString != NULL)
+ name = &nameString[0];
+
+ bool isDirectory = !!(pDirectoryEntries[iEntry].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY);
+ DWORD offsetToData = pDirectoryEntries[iEntry].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY;
+ DWORD dataRva = rvaOfResourceSection + offsetToData;
+
+ if (!resourceTableEnumerator(pDecoder, rvaOfResourceSection, isDirectory, name, dataRva, context))
+ return false;
+ }
+
+ return true;
+}
+
+bool DoesResourceNameMatch(LPCWSTR nameA, LPCWSTR nameB)
+{
+ bool foundEntry = false;
+
+ if (IS_INTRESOURCE(nameA))
+ {
+ // name is id
+ if (nameA == nameB)
+ foundEntry = true;
+ }
+ else
+ {
+ // name is a string.
+
+ // Check for name enumerated is an id. If so, it doesn't match, skip to next.
+ if (IS_INTRESOURCE(nameB))
+ return false;
+ else
+ foundEntry = !wcscmp(nameB, nameA);
+ }
+
+ return foundEntry;
+}
+
+bool EnumerateLangIDs(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context)
+{
+ ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context;
+ if (isDirectory)
+ return false;
+
+ // Only LangIDs are permitted here
+ if (!IS_INTRESOURCE(name))
+ return false;
+
+ if (dataRVA == 0)
+ return false;
+
+ COUNT_T cbData;
+ DWORD resourceDataRva = ReadResourceDataEntry(pDecoder, dataRVA, &cbData);
+ if (!pDecoder->CheckRva(resourceDataRva, cbData))
+ {
+ return false;
+ }
+
+ BYTE *pData = (BYTE*)pDecoder->GetRvaData(resourceDataRva);
+
+ return state->langIDcallback(state->nameName, state->nameType, (DWORD)(uintptr_t)name, pData, cbData, state->context);
+}
+
+
+bool EnumerateNames(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context)
+{
+ ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context;
+ if (!isDirectory)
+ return false;
+
+ state->nameName = name;
+ return state->namesCallback(state->nameName, state->nameType, state->context);
+}
+
+bool EnumerateNamesForLangID(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context)
+{
+ ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context;
+ if (!isDirectory)
+ return false;
+
+ bool foundEntry = DoesResourceNameMatch(state->nameName, name);
+
+ if (foundEntry)
+ return EnumerateWin32ResourceTable(pDecoder, rvaOfResourceSection, dataRVA, state->callbackPerLangID, context);
+ else
+ return true; // Keep scanning
+}
+
+
+bool EnumerateTypes(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context)
+{
+ ResourceEnumerateTypesState *state = (ResourceEnumerateTypesState*)context;
+ if (!isDirectory)
+ return false;
+
+ state->nameType = name;
+ return state->callback(name, state->context);
+}
+
+bool EnumerateTypesForNames(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context)
+{
+ ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context;
+ if (!isDirectory)
+ return false;
+
+ bool foundEntry = DoesResourceNameMatch(state->nameType, name);
+
+ if (foundEntry)
+ return EnumerateWin32ResourceTable(pDecoder, rvaOfResourceSection, dataRVA, state->callbackPerName, context);
+ else
+ return true; // Keep scanning
+}
+
+
+bool PEDecoder::EnumerateWin32ResourceTypes(PEDecoder_ResourceTypesCallbackFunction callback, void* context) const
+{
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE))
+ return true;
+
+ COUNT_T resourceDataSize = 0;
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE);
+
+ if (pDir->VirtualAddress == 0)
+ return true;
+
+ DWORD rvaOfResourceSection = pDir->VirtualAddress;
+
+ ResourceEnumerateTypesState state;
+ state.context = context;
+ state.callback = callback;
+
+ return EnumerateWin32ResourceTable(this, rvaOfResourceSection, rvaOfResourceSection, EnumerateTypes, &state);
+}
+
+bool PEDecoder::EnumerateWin32ResourceNames(LPCWSTR lpType, PEDecoder_ResourceNamesCallbackFunction callback, void* context) const
+{
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE))
+ return true;
+
+ COUNT_T resourceDataSize = 0;
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE);
+
+ if (pDir->VirtualAddress == 0)
+ return true;
+
+ DWORD rvaOfResourceSection = pDir->VirtualAddress;
+
+ ResourceEnumerateNamesState state;
+ state.context = context;
+ state.namesCallback = callback;
+ state.langIDcallback = NULL;
+ state.nameType = lpType;
+ state.nameName = NULL;
+ state.callbackPerName = EnumerateNames;
+ state.callbackPerLangID = NULL;
+
+ return EnumerateWin32ResourceTable(this, rvaOfResourceSection, rvaOfResourceSection, EnumerateTypesForNames, &state);
+}
+
+bool PEDecoder::EnumerateWin32Resources(LPCWSTR lpName, LPCWSTR lpType, PEDecoder_ResourceCallbackFunction callback, void* context) const
+{
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE))
+ return true;
+
+ COUNT_T resourceDataSize = 0;
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE);
+
+ if (pDir->VirtualAddress == 0)
+ return true;
+
+ DWORD rvaOfResourceSection = pDir->VirtualAddress;
+
+ ResourceEnumerateNamesState state;
+ state.context = context;
+ state.namesCallback = NULL;
+ state.langIDcallback = callback;
+ state.nameType = lpType;
+ state.nameName = lpName;
+ state.callbackPerName = EnumerateNamesForLangID;
+ state.callbackPerLangID = EnumerateLangIDs;
+
+ return EnumerateWin32ResourceTable(this, rvaOfResourceSection, rvaOfResourceSection, EnumerateTypesForNames, &state);
+}
+
+READYTORUN_HEADER * PEDecoder::FindReadyToRunHeader() const
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ 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;
+}
+
+PTR_VOID PEDecoder::GetExport(LPCSTR exportName) const
+{
+ // Get the export directory entry
+ PIMAGE_DATA_DIRECTORY pExportDirectoryEntry = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_EXPORT);
+ if (pExportDirectoryEntry->VirtualAddress == 0 || pExportDirectoryEntry->Size == 0)
+ {
+ return NULL;
+ }
+
+ PTR_IMAGE_EXPORT_DIRECTORY pExportDir = dac_cast<PTR_IMAGE_EXPORT_DIRECTORY>(GetDirectoryData(pExportDirectoryEntry));
+
+ uint32_t namePointerCount = VAL32(pExportDir->NumberOfNames);
+ uint32_t addressTableRVA = VAL32(pExportDir->AddressOfFunctions);
+ uint32_t ordinalTableRVA = VAL32(pExportDir->AddressOfNameOrdinals);
+ uint32_t nameTableRVA = VAL32(pExportDir->AddressOfNames);
+
+ for (uint32_t nameIndex = 0; nameIndex < namePointerCount; nameIndex++)
+ {
+ uint32_t namePointerRVA = *dac_cast<PTR_UINT32>(GetRvaData(nameTableRVA + sizeof(uint32_t) * nameIndex));
+ if (namePointerRVA != 0)
+ {
+ const char *namePointer = dac_cast<PTR_CSTR>(GetRvaData(namePointerRVA));
+ if (!strcmp(namePointer, exportName))
+ {
+ uint16_t ordinalForNamedExport = *dac_cast<PTR_UINT16>(GetRvaData(ordinalTableRVA + sizeof(uint16_t) * nameIndex));
+ uint32_t exportRVA = *dac_cast<PTR_UINT32>(GetRvaData(addressTableRVA + sizeof(uint32_t) * ordinalForNamedExport));
+ return dac_cast<PTR_VOID>(GetRvaData(exportRVA));
+ }
+ }
+ }
+
+ 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;
+}
+
+
+PTR_CVOID PEDecoder::GetNativeManifestMetadata(COUNT_T *pSize) const
+{
+ CONTRACT(PTR_CVOID)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(HasReadyToRunHeader());
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); // TBD - may not store metadata for IJW
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = NULL;
+ {
+ READYTORUN_HEADER * pHeader = GetReadyToRunHeader();
+
+ PTR_READYTORUN_SECTION pSections = dac_cast<PTR_READYTORUN_SECTION>(dac_cast<TADDR>(pHeader) + sizeof(READYTORUN_HEADER));
+ for (DWORD i = 0; i < pHeader->CoreHeader.NumberOfSections; i++)
+ {
+ // Verify that section types are sorted
+ _ASSERTE(i == 0 || (pSections[i - 1].Type < pSections[i].Type));
+
+ READYTORUN_SECTION * pSection = pSections + i;
+ if (pSection->Type == ReadyToRunSectionType::ManifestMetadata)
+ {
+ // Set pDir to the address of the manifest metadata section
+ pDir = &pSection->Section;
+ break;
+ }
+ }
+
+ // ReadyToRun file without large version bubble support doesn't have the ManifestMetadata
+ if (pDir == NULL)
+ {
+ if (pSize != NULL)
+ {
+ *pSize = 0;
+ }
+
+ RETURN NULL;
+ }
+ }
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN dac_cast<PTR_VOID>(GetDirectoryData(pDir));
+}
+
+// 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();
+}
+
+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_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
+
+//
+// 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;
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// 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 len = WszGetCurrentDirectory(value);
+
+
+ // An actual API failure in GetCurrentDirectory failure should be very rare, so we'll throw on those.
+ if (len == 0)
+ {
+ ThrowLastError();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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;
+}
+
+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);
+}
+
+ClrDirectoryEnumerator::ClrDirectoryEnumerator(LPCWSTR pBaseDirectory, LPCWSTR pMask /*= W("*")*/)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ StackSString strMask(pBaseDirectory);
+ SString s(SString::Literal, DIRECTORY_SEPARATOR_STR_W);
+ if (!strMask.EndsWith(s))
+ {
+ strMask.Append(s);
+ }
+ strMask.Append(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 TARGET_UNIX
+ 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 // TARGET_UNIX
+ // UNIXTODO: Report the event somewhere?
+ return ERROR_SUCCESS;
+#endif // TARGET_UNIX
+}
+
+// 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 TARGET_UNIX
+// 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 // TARGET_UNIX
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+
+//
+// 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 egregious 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;
+}
+
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#include "stdafx.h"
+
+#include "securityutil.h"
+#include "ex.h"
+
+#include "securitywrapper.h"
+
+// 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.
+ if (SUCCEEDED(sidCurrentProcess.InitFromProcessNoThrow(GetCurrentProcessId())))
+ {
+ pCurrentProcessSid = sidCurrentProcess.GetSid().RawSid();
+ cSid++;
+ }
+
+ // Get sid for target process.
+ if (SUCCEEDED(sidTargetProcess.InitFromProcessNoThrow(pid)))
+ {
+ pTargetProcessSid = sidTargetProcess.GetSid().RawSid();
+ cSid++;
+ }
+
+ //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_QUERY, &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;
+
+ 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;
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// 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 = 0;
+ 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 retrieve 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 hToken = NULL;
+ BOOL fIsLowBox = FALSE;
+
+ HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
+ if (hProcess == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ // 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;
+ 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 have 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);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// SString.cpp
+//
+
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "sstring.h"
+#include "ex.h"
+#include "holder.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()
+
+UINT SString::s_ACP = 0;
+
+#ifndef DACCESS_COMPILE
+static BYTE s_EmptySpace[sizeof(SString)] = { 0 };
+#endif // DACCESS_COMPILE
+
+SPTR_IMPL(SString,SString,s_Empty);
+
+void SString::Startup()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (s_ACP == 0)
+ {
+ UINT ACP = GetACP();
+
+#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)
+{
+ WRAPPER_NO_CONTRACT;
+
+ WCHAR wTmp;
+
+#ifndef TARGET_UNIX
+
+ int iRet = ::LCMapStringEx(LOCALE_NAME_INVARIANT, dwFlags, &wc, 1, &wTmp, 1, NULL, NULL, 0);
+ if (!iRet) {
+ // This can fail in non-exceptional cases becauseof unknown unicode characters.
+ wTmp = wc;
+ }
+
+#else // !TARGET_UNIX
+ // For PAL, no locale specific processing is done
+
+ if (dwFlags == LCMAP_UPPERCASE)
+ {
+ wTmp =
+#ifdef SELF_NO_HOST
+ toupper(wc);
+#else
+ PAL_ToUpperInvariant(wc);
+#endif
+ }
+ else
+ {
+ _ASSERTE(dwFlags == LCMAP_LOWERCASE);
+ wTmp =
+#ifdef SELF_NO_HOST
+ tolower(wc);
+#else
+ PAL_ToLowerInvariant(wc);
+#endif
+ }
+#endif // !TARGET_UNIX
+
+ 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, 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))
+ - (CAN_SIMPLE_UPCASE(ch2) ? SIMPLE_UPCASE(ch2) : MapChar(ch2, LCMAP_UPPERCASE)));
+ }
+ 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))
+
+/* static */
+int SString::CaseCompareHelperA(const CHAR *buffer1, const CHAR *buffer2, COUNT_T count, 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;
+ diff = ch1 - ch2;
+ if (diff != 0 || stopOnNull)
+ {
+ if (ch1 == 0 || ch2 == 0)
+ {
+ break;
+ }
+ diff = (SIMPLE_UPCASE_ANSI(ch1) - SIMPLE_UPCASE_ANSI(ch2));
+ if (diff != 0)
+ {
+ break;
+ }
+ }
+ buffer1++;
+ buffer2++;
+ }
+ return diff;
+}
+
+
+int CaseHashHelper(const WCHAR *buffer, COUNT_T count)
+{
+ 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);
+
+ 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() 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());
+ 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();
+ FALLTHROUGH;
+
+ 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();
+ FALLTHROUGH;
+
+ 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) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return MatchCaseInsensitive(Begin(), s);
+}
+
+//-----------------------------------------------------------------------------
+// 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) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Need this check due to iterator arithmetic below.
+ if (GetCount() < s.GetCount())
+ {
+ return FALSE;
+ }
+
+ return MatchCaseInsensitive(End() - s.GetCount(), s);
+}
+
+//-----------------------------------------------------------------------------
+// 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) 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:
+ case REPRESENTATION_ANSI:
+ result = CaseCompareHelper(GetRawUnicode(), source.GetRawUnicode(), smaller, FALSE, TRUE);
+ break;
+
+ case REPRESENTATION_ASCII:
+ result = CaseCompareHelperA(GetRawASCII(), source.GetRawASCII(), smaller, 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) 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:
+ case REPRESENTATION_ANSI:
+ RETURN (CaseCompareHelper(GetRawUnicode(), source.GetRawUnicode(), count, FALSE, TRUE) == 0);
+
+ case REPRESENTATION_ASCII:
+ RETURN (CaseCompareHelperA(GetRawASCII(), source.GetRawASCII(), count, 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) 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, FALSE, TRUE) == 0);
+
+ case REPRESENTATION_ASCII:
+ RETURN (CaseCompareHelperA(i.GetASCII(), source.GetRawASCII(), count, 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) 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))
+ == (CAN_SIMPLE_UPCASE(c) ? SIMPLE_UPCASE(c) : MapChar(c, LCMAP_UPPERCASE))));
+}
+
+//-----------------------------------------------------------------------------
+// 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));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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 (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 (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 (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 (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
+
+#if defined(_MSC_VER)
+#undef va_copy
+#define va_copy(dest,src) (dest = src)
+#endif
+
+void SString::VPrintf(const CHAR *format, va_list args)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(format));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ va_list ap;
+ // 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
+ va_copy(ap, args);
+ int result = _vsnprintf_s(GetRawANSI(), GetRawCount()+1, _TRUNCATE, format, ap);
+ va_end(ap);
+
+ 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;
+
+ va_copy(ap, args);
+ int result = _vsnprintf_s(GetRawANSI(), GetRawCount()+1, _TRUNCATE, format, ap);
+ va_end(ap);
+
+ 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_s 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;
+
+ va_list ap;
+ // 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
+ va_copy(ap, args);
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
+ va_end(ap);
+
+ 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;
+
+ va_copy(ap, args);
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
+ va_end(ap);
+
+ 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_s 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;
+
+ va_list ap;
+ // 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
+ va_copy(ap, args);
+#if defined(FEATURE_CORESYSTEM)
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
+#else
+ int result = _vswprintf_p(GetRawUnicode(), GetRawCount()+1, format, ap);
+#endif
+ va_end(ap);
+ 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;
+
+ va_copy(ap, args);
+#if defined(FEATURE_CORESYSTEM)
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
+#else
+ int result = _vswprintf_p(GetRawUnicode(), GetRawCount()+1, format, ap);
+#endif
+ va_end(ap);
+
+ 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_s 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);
+ FALLTHROUGH;
+
+ 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();
+ FALLTHROUGH;
+
+ 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;
+ 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,
+ _Inout_updates_z_(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;
+ FALLTHROUGH;
+ 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
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// 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
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// 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"
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ </PropertyGroup>
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Checked|x64">
+ <Configuration>Checked</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="RelWithDebInfo|x64">
+ <Configuration>RelWithDebInfo</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{8C35FEF8-1101-38F6-ACD0-462A1EA53A7D}</ProjectGuid>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ <Keyword>Win32Proj</Keyword>
+ <Platform>x64</Platform>
+ <ProjectName>utilcode</ProjectName>
+ <VCProjectUpgraderObjectName>NoUpgrade</VCProjectUpgraderObjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
+ </ImportGroup>
+ <ImportGroup 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>10.0.20506.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode\Debug\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">utilcodestaticnohost.dir\Debug\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">utilcodestaticnohost</TargetName>
+ <TargetExt Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.lib</TargetExt>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode\Checked\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">utilcodestaticnohost.dir\Checked\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">utilcodestaticnohost</TargetName>
+ <TargetExt Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">.lib</TargetExt>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode\Release\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">utilcodestaticnohost.dir\Release\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">utilcodestaticnohost</TargetName>
+ <TargetExt Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.lib</TargetExt>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode\RelWithDebInfo\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">utilcodestaticnohost.dir\RelWithDebInfo\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">utilcodestaticnohost</TargetName>
+ <TargetExt Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">.lib</TargetExt>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 /homeparams</AdditionalOptions>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091</DisableSpecificWarnings>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <OmitFramePointers>false</OmitFramePointers>
+ <Optimization>Disabled</Optimization>
+ <RemoveUnreferencedCodeData>true</RemoveUnreferencedCodeData>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <StructMemberAlignment>8Bytes</StructMemberAlignment>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TreatSpecificWarningsAsErrors>4007;4013;4102;4551;4700;4640;4806</TreatSpecificWarningsAsErrors>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <UseFullPaths>true</UseFullPaths>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Debug;BUILDENV_DEBUG=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;DEBUG;_DBG;URTBLDENV_FRIENDLY=Debug;BUILDENV_DEBUG=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <MASM>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Debug;BUILDENV_DEBUG=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR="Debug";;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <IncludePaths>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths)</IncludePaths>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont</AdditionalOptions>
+ </MASM>
+ <Midl>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+ <HeaderFileName>%(Filename).h</HeaderFileName>
+ <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+ <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+ <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+ </Midl>
+ <Lib>
+ <AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Checked|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8</AdditionalOptions>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091</DisableSpecificWarnings>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <OmitFramePointers>false</OmitFramePointers>
+ <Optimization>MaxSpeed</Optimization>
+ <RemoveUnreferencedCodeData>true</RemoveUnreferencedCodeData>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <StructMemberAlignment>8Bytes</StructMemberAlignment>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TreatSpecificWarningsAsErrors>4007;4013;4102;4551;4700;4640;4806</TreatSpecificWarningsAsErrors>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <UseFullPaths>true</UseFullPaths>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR="Checked";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR=\"Checked\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <MASM>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;DEBUG;_DEBUG;_DBG;URTBLDENV_FRIENDLY=Checked;BUILDENV_CHECKED=1;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR="Checked";;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <IncludePaths>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths)</IncludePaths>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont</AdditionalOptions>
+ </MASM>
+ <Midl>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+ <HeaderFileName>%(Filename).h</HeaderFileName>
+ <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+ <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+ <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+ </Midl>
+ <Lib>
+ <AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8</AdditionalOptions>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091</DisableSpecificWarnings>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <OmitFramePointers>false</OmitFramePointers>
+ <Optimization>Full</Optimization>
+ <RemoveUnreferencedCodeData>true</RemoveUnreferencedCodeData>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <StructMemberAlignment>8Bytes</StructMemberAlignment>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TreatSpecificWarningsAsErrors>4007;4013;4102;4551;4700;4640;4806</TreatSpecificWarningsAsErrors>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <UseFullPaths>true</UseFullPaths>
+ <WarningLevel>Level3</WarningLevel>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR="Release";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR=\"Release\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <MASM>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR="Release";;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <IncludePaths>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths)</IncludePaths>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont</AdditionalOptions>
+ </MASM>
+ <Midl>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+ <HeaderFileName>%(Filename).h</HeaderFileName>
+ <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+ <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+ <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+ </Midl>
+ <Lib>
+ <AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
+ <LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8</AdditionalOptions>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091</DisableSpecificWarnings>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <OmitFramePointers>false</OmitFramePointers>
+ <Optimization>MaxSpeed</Optimization>
+ <RemoveUnreferencedCodeData>true</RemoveUnreferencedCodeData>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <StructMemberAlignment>8Bytes</StructMemberAlignment>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TreatSpecificWarningsAsErrors>4007;4013;4102;4551;4700;4640;4806</TreatSpecificWarningsAsErrors>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <UseFullPaths>true</UseFullPaths>
+ <WarningLevel>Level3</WarningLevel>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR="RelWithDebInfo";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR=\"RelWithDebInfo\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <MASM>
+ <PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;_CRTIMP=;FEATURE_UTILCODE_NO_DEPENDENCIES;SELF_NO_HOST;URTBLDENV_FRIENDLY=Retail;HOST_AMD64;HOST_64BIT;HOST_WINDOWS;_FILE_OFFSET_BITS=64;TARGET_AMD64;TARGET_64BIT;TARGET_WINDOWS;_AMD64_;_WIN64;AMD64;BIT64=1;_TARGET_64BIT_=1;_TARGET_AMD64_=1;DBG_TARGET_64BIT=1;DBG_TARGET_AMD64=1;DBG_TARGET_WIN64=1;_WIN32;WINVER=0x0602;_WIN32_WINNT=0x0602;WIN32_LEAN_AND_MEAN=1;_CRT_SECURE_NO_WARNINGS;UNICODE;_UNICODE;FEATURE_CORESYSTEM;FEATURE_COMINTEROP;FEATURE_HIJACK;_BLD_CLR;CMAKE_INTDIR="RelWithDebInfo";;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <IncludePaths>$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths)</IncludePaths>
+ <AdditionalOptions>%(AdditionalOptions) /guard:ehcont</AdditionalOptions>
+ </MASM>
+ <Midl>
+ <AdditionalIncludeDirectories>$(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+ <HeaderFileName>%(Filename).h</HeaderFileName>
+ <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+ <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+ <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+ </Midl>
+ <Lib>
+ <AdditionalOptions>%(AdditionalOptions) /machine:x64 /IGNORE:4221</AdditionalOptions>
+ <LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="clrhost_nodependencies.cpp">
+ </ClCompile>
+ <ClCompile Include="ex.cpp">
+ </ClCompile>
+ <ClCompile Include="sbuffer.cpp">
+ </ClCompile>
+ <ClCompile Include="sstring_com.cpp">
+ </ClCompile>
+ <ClCompile Include="fstring.cpp">
+ </ClCompile>
+ <ClCompile Include="namespaceutil.cpp">
+ </ClCompile>
+ <ClCompile Include="check.cpp">
+ </ClCompile>
+ <ClCompile Include="sstring.cpp">
+ </ClCompile>
+ <ClCompile Include="safewrap.cpp">
+ </ClCompile>
+ <ClCompile Include="debug.cpp">
+ </ClCompile>
+ <ClCompile Include="pedecoder.cpp">
+ </ClCompile>
+ <ClCompile Include="longfilepathwrappers.cpp">
+ </ClCompile>
+ <ClCompile Include="dlwrap.cpp">
+ </ClCompile>
+ <ClCompile Include="securitywrapper.cpp">
+ </ClCompile>
+ <ClCompile Include="securityutil.cpp">
+ </ClCompile>
+ <ClCompile Include="hostimpl.cpp">
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="CMakeLists.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="stdafx.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="clrhost_nodependencies.cpp">
+ </ClCompile>
+ <ClCompile Include="ex.cpp">
+ </ClCompile>
+ <ClCompile Include="sbuffer.cpp">
+ </ClCompile>
+ <ClCompile Include="sstring_com.cpp">
+ </ClCompile>
+ <ClCompile Include="fstring.cpp">
+ </ClCompile>
+ <ClCompile Include="namespaceutil.cpp">
+ </ClCompile>
+ <ClCompile Include="check.cpp">
+ </ClCompile>
+ <ClCompile Include="sstring.cpp">
+ </ClCompile>
+ <ClCompile Include="safewrap.cpp">
+ </ClCompile>
+ <ClCompile Include="debug.cpp">
+ </ClCompile>
+ <ClCompile Include="pedecoder.cpp">
+ </ClCompile>
+ <ClCompile Include="longfilepathwrappers.cpp">
+ </ClCompile>
+ <ClCompile Include="dlwrap.cpp">
+ </ClCompile>
+ <ClCompile Include="securitywrapper.cpp">
+ </ClCompile>
+ <ClCompile Include="securityutil.cpp">
+ </ClCompile>
+ <ClCompile Include="hostimpl.cpp">
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="CMakeLists.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="stdafx.h" />
+ </ItemGroup>
+</Project>
\ No newline at end of file