From: Mike McLaughlin Date: Tue, 8 Feb 2022 02:01:35 +0000 (-0800) Subject: Port dbgshim to the diagnostics repo (#2842) X-Git-Tag: accepted/tizen/unified/20221103.165810~28^2^2~90 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6629b4424ac5cf9920fcc4e53d29d4c94a27c42b;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Port dbgshim to the diagnostics repo (#2842) Port dbgshim to the diagnostics repo runtime repo commit hash: 442a42147ef23c3b9742abcd8b997e8f472af68a Add a subset of utilcode. Add more runtime include files. Add PAL_RegisterForRuntimeStartup and the other PAL support it needs like WaitForSingleObject, OpenProcess, EnumProcessModules, etc. Add dbgshim packaging support. Sync diagnostics/eng/native to the runtime Make IsShipping* properties consistent Change eeversion not to require the DAC to display runtime version --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 93edd8885..488392bd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,7 @@ # 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) @@ -32,26 +30,26 @@ endif (MSVC) 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) @@ -62,6 +60,17 @@ endif(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) #----------------------------------- @@ -138,14 +147,17 @@ else () 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 @@ -159,9 +171,9 @@ if(CLR_CMAKE_HOST_UNIX) 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) diff --git a/diagnostics.sln b/diagnostics.sln index 8f3e2edc5..35fb836a1 100644 --- a/diagnostics.sln +++ b/diagnostics.sln @@ -26,8 +26,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Strike", "src\SOS\Strike\St 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}" @@ -78,13 +76,20 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "inc", "inc", "{41BDFD6D-D16 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 @@ -92,19 +97,27 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "inc", "inc", "{41BDFD6D-D16 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 @@ -113,39 +126,69 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "inc", "inc", "{41BDFD6D-D16 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}" @@ -203,6 +246,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExitCodeTracee", "src\tests 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 @@ -468,30 +515,6 @@ Global {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 @@ -1660,6 +1683,54 @@ Global {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 @@ -1674,7 +1745,6 @@ Global {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} @@ -1712,6 +1782,8 @@ Global {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} diff --git a/eng/ci-prepare-artifacts.cmd b/eng/ci-prepare-artifacts.cmd index 23b321c14..5632c47db 100644 --- a/eng/ci-prepare-artifacts.cmd +++ b/eng/ci-prepare-artifacts.cmd @@ -12,6 +12,10 @@ echo Creating bundles 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 diff --git a/eng/native/build-commons.sh b/eng/native/build-commons.sh index 373091b13..294e9832a 100755 --- a/eng/native/build-commons.sh +++ b/eng/native/build-commons.sh @@ -197,7 +197,7 @@ usage() 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)" @@ -270,6 +270,10 @@ while :; do __BuildArch=arm ;; + armv6|-armv6) + __BuildArch=armv6 + ;; + arm64|-arm64) __BuildArch=arm64 ;; diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index fad1ac58d..b67594cb6 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -213,6 +213,9 @@ elseif (CLR_CMAKE_HOST_ARCH_I386) 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) @@ -238,6 +241,8 @@ if (CLR_CMAKE_HOST_UNIX) 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) @@ -301,6 +306,12 @@ elseif (CLR_CMAKE_TARGET_ARCH_ARM) set(ARCH_TARGET_NAME arm) add_compile_definitions($<$>>:TARGET_ARM>) add_compile_definitions($<$>>:TARGET_32BIT>) +elseif (CLR_CMAKE_TARGET_ARCH_ARMV6) + set(ARCH_SOURCES_DIR arm) + set(ARCH_TARGET_NAME armv6) + add_compile_definitions($<$>>:TARGET_ARM>) + add_compile_definitions($<$>>:TARGET_ARMV6>) + add_compile_definitions($<$>>:TARGET_32BIT>) elseif (CLR_CMAKE_TARGET_ARCH_I386) set(ARCH_TARGET_NAME x86) set(ARCH_SOURCES_DIR i386) @@ -507,6 +518,14 @@ if(CLR_CMAKE_HOST_UNIX_ARM) 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() diff --git a/eng/native/configureplatform.cmake b/eng/native/configureplatform.cmake index cdf33430b..ecf10a8d4 100644 --- a/eng/native/configureplatform.cmake +++ b/eng/native/configureplatform.cmake @@ -41,6 +41,8 @@ if(CLR_CMAKE_HOST_OS STREQUAL Linux) 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) @@ -217,6 +219,13 @@ if(CLR_CMAKE_HOST_UNIX_ARM) 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") @@ -277,6 +286,8 @@ if (CLR_CMAKE_TARGET_ARCH STREQUAL x64) 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) @@ -379,6 +390,8 @@ if(CLR_CMAKE_TARGET_UNIX) 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) diff --git a/eng/native/configuretools.cmake b/eng/native/configuretools.cmake index 136cd6792..ad5dc3810 100644 --- a/eng/native/configuretools.cmake +++ b/eng/native/configuretools.cmake @@ -52,8 +52,8 @@ if(NOT WIN32 AND NOT CLR_CMAKE_TARGET_BROWSER) 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 "") diff --git a/eng/native/functions.cmake b/eng/native/functions.cmake index 4e68a9be0..0c28f7570 100644 --- a/eng/native/functions.cmake +++ b/eng/native/functions.cmake @@ -4,10 +4,75 @@ function(clr_unknown_arch) 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 @@ -90,6 +155,10 @@ function(find_unwind_libs UnwindLibs) 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() diff --git a/eng/native/init-os-and-arch.sh b/eng/native/init-os-and-arch.sh index 586534be1..eda07a5fe 100644 --- a/eng/native/init-os-and-arch.sh +++ b/eng/native/init-os-and-arch.sh @@ -53,6 +53,10 @@ case "$CPUName" in fi ;; + armv6l) + arch=armv6 + ;; + i[3-6]86) echo "Unsupported CPU $CPUName detected, build might not succeed!" arch=x86 diff --git a/eng/native/tryrun.cmake b/eng/native/tryrun.cmake index e8a04c569..fca410fcb 100644 --- a/eng/native/tryrun.cmake +++ b/eng/native/tryrun.cmake @@ -68,7 +68,7 @@ if(DARWIN) 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) @@ -146,9 +146,9 @@ elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|arm64|loongarch64|s390x|x86)$" OR F 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() diff --git a/eng/testassets/pack.csproj b/eng/testassets/pack.csproj index 31f1e5400..9eebfe97c 100644 --- a/eng/testassets/pack.csproj +++ b/eng/testassets/pack.csproj @@ -2,6 +2,7 @@ netstandard2.0 true + false false diff --git a/eng/testassets/rebuild/pack.csproj b/eng/testassets/rebuild/pack.csproj index 5f08b4bc7..3bc4b8965 100644 --- a/eng/testassets/rebuild/pack.csproj +++ b/eng/testassets/rebuild/pack.csproj @@ -2,6 +2,7 @@ netstandard2.0 true + false false diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 194b91559..bec77d775 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,11 +1,27 @@ +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) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7dfea314a..a57c548c0 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -3,8 +3,7 @@ - true - false + false false true true diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Microsoft.Diagnostics.DebugServices.Implementation.csproj b/src/Microsoft.Diagnostics.DebugServices.Implementation/Microsoft.Diagnostics.DebugServices.Implementation.csproj index a7ede1880..d36528d89 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Microsoft.Diagnostics.DebugServices.Implementation.csproj +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Microsoft.Diagnostics.DebugServices.Implementation.csproj @@ -9,8 +9,8 @@ $(Description) true true - false - true + true + false diff --git a/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj b/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj index 2e6484914..a441d0e6b 100644 --- a/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj +++ b/src/Microsoft.Diagnostics.DebugServices/Microsoft.Diagnostics.DebugServices.csproj @@ -9,6 +9,7 @@ $(Description) true true + true false diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Microsoft.Diagnostics.ExtensionCommands.csproj b/src/Microsoft.Diagnostics.ExtensionCommands/Microsoft.Diagnostics.ExtensionCommands.csproj index 1d7fac112..a18bae922 100644 --- a/src/Microsoft.Diagnostics.ExtensionCommands/Microsoft.Diagnostics.ExtensionCommands.csproj +++ b/src/Microsoft.Diagnostics.ExtensionCommands/Microsoft.Diagnostics.ExtensionCommands.csproj @@ -6,12 +6,12 @@ ;1591;1701 Diagnostics extension commands true - false Diagnostic $(Description) true true - true + true + false diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj index efc358643..733d18da1 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.csproj @@ -12,6 +12,7 @@ true false + true true 5.0.0 diff --git a/src/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.csproj b/src/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.csproj index edbd3ef9a..0f4d550e7 100644 --- a/src/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.csproj +++ b/src/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.csproj @@ -12,6 +12,7 @@ true false + true true 5.0.0 diff --git a/src/Microsoft.Diagnostics.Repl/Microsoft.Diagnostics.Repl.csproj b/src/Microsoft.Diagnostics.Repl/Microsoft.Diagnostics.Repl.csproj index 9016c7419..4dd5472a3 100644 --- a/src/Microsoft.Diagnostics.Repl/Microsoft.Diagnostics.Repl.csproj +++ b/src/Microsoft.Diagnostics.Repl/Microsoft.Diagnostics.Repl.csproj @@ -3,12 +3,13 @@ netstandard2.0 true ;1591;1701 - Diagnostic utility functions and helpers + Diagnostic command line REPL support true Diagnostic $(Description) true true + true false diff --git a/src/SOS/CMakeLists.txt b/src/SOS/CMakeLists.txt index 7f2681373..3dc1781e0 100644 --- a/src/SOS/CMakeLists.txt +++ b/src/SOS/CMakeLists.txt @@ -1,12 +1,9 @@ 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}") @@ -19,7 +16,7 @@ if(WIN32) 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) @@ -34,13 +31,3 @@ add_subdirectory(extensions) 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) diff --git a/src/SOS/SOS.Extensions/SOS.Extensions.csproj b/src/SOS/SOS.Extensions/SOS.Extensions.csproj index fc0ab84f3..0185dcec4 100644 --- a/src/SOS/SOS.Extensions/SOS.Extensions.csproj +++ b/src/SOS/SOS.Extensions/SOS.Extensions.csproj @@ -6,6 +6,7 @@ true ;1591;1701 .NET Diagnostic Extensions support + true false diff --git a/src/SOS/SOS.Hosting/SOS.Hosting.csproj b/src/SOS/SOS.Hosting/SOS.Hosting.csproj index 0abaa83e9..5eab9ca5b 100644 --- a/src/SOS/SOS.Hosting/SOS.Hosting.csproj +++ b/src/SOS/SOS.Hosting/SOS.Hosting.csproj @@ -5,6 +5,8 @@ ;1591;1701 SOS Hosting support true + true + false diff --git a/src/SOS/SOS.InstallHelper/SOS.InstallHelper.csproj b/src/SOS/SOS.InstallHelper/SOS.InstallHelper.csproj index aa0ced1dd..9f007f064 100644 --- a/src/SOS/SOS.InstallHelper/SOS.InstallHelper.csproj +++ b/src/SOS/SOS.InstallHelper/SOS.InstallHelper.csproj @@ -3,8 +3,11 @@ netstandard2.0 SOS.InstallHelper ;1591;1701 + false Diagnostic SOS Install Helper $(Description) SOS + true + false diff --git a/src/SOS/SOS.Package/SOS.Package.csproj b/src/SOS/SOS.Package/SOS.Package.csproj index 4a8e97e2d..8ae407ed7 100644 --- a/src/SOS/SOS.Package/SOS.Package.csproj +++ b/src/SOS/SOS.Package/SOS.Package.csproj @@ -11,6 +11,7 @@ tools $(ArtifactsPackagesDir)\GalleryManifest.xml GenerateGalleryZip;GenerateSymbolsZip + true false diff --git a/src/SOS/SOS.Package/SOS.Symbol.Package.csproj b/src/SOS/SOS.Package/SOS.Symbol.Package.csproj index d5256031e..83cd791cf 100644 --- a/src/SOS/SOS.Package/SOS.Symbol.Package.csproj +++ b/src/SOS/SOS.Package/SOS.Symbol.Package.csproj @@ -7,9 +7,10 @@ $(Description) true false - false true tools + true + false diff --git a/src/SOS/SOS.UnitTests/Debuggees/Directory.Build.props b/src/SOS/SOS.UnitTests/Debuggees/Directory.Build.props index 832fbf046..84fd31988 100644 --- a/src/SOS/SOS.UnitTests/Debuggees/Directory.Build.props +++ b/src/SOS/SOS.UnitTests/Debuggees/Directory.Build.props @@ -4,7 +4,6 @@ full true - false false netcoreapp3.1;net5.0;net6.0 diff --git a/src/SOS/Strike/CMakeLists.txt b/src/SOS/Strike/CMakeLists.txt index 4d70c6b32..589abfc8f 100644 --- a/src/SOS/Strike/CMakeLists.txt +++ b/src/SOS/Strike/CMakeLists.txt @@ -145,8 +145,6 @@ else(WIN32) include_directories(BEFORE xplat) - add_compile_options(-fPIC) - set(SOS_SOURCES disasm.cpp eeheap.cpp diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index aabb53e32..eafce1b5e 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -10907,7 +10907,7 @@ extern char sccsid[]; \**********************************************************************/ DECLARE_API(EEVersion) { - INIT_API(); + INIT_API_NO_RET_ON_FAILURE(); static const int fileVersionBufferSize = 1024; ArrayHolder fileVersionBuffer = new char[fileVersionBufferSize]; @@ -10949,15 +10949,20 @@ DECLARE_API(EEVersion) } } - 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 @@ -11171,7 +11176,7 @@ DECLARE_API (ProcInfo) 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"); @@ -11270,7 +11275,7 @@ DECLARE_API (ProcInfo) 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"); diff --git a/src/SOS/Strike/symbols.cpp b/src/SOS/Strike/symbols.cpp index dbd92db0b..da46121b8 100644 --- a/src/SOS/Strike/symbols.cpp +++ b/src/SOS/Strike/symbols.cpp @@ -256,7 +256,7 @@ BOOL GetProcAddressT(PCSTR FunctionName, PCSTR DllName, T* OutFunctionPointer, H 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; } diff --git a/src/SOS/Strike/util.cpp b/src/SOS/Strike/util.cpp index 0bf6aac45..ff722fb0e 100644 --- a/src/SOS/Strike/util.cpp +++ b/src/SOS/Strike/util.cpp @@ -243,8 +243,8 @@ const WCHAR GetTargetDirectorySeparatorW() // 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; @@ -3604,7 +3604,7 @@ BOOL GetSOSVersion(VS_FIXEDFILEINFO *pFileInfo) { 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; diff --git a/src/SOS/dbgutil/CMakeLists.txt b/src/SOS/dbgutil/CMakeLists.txt index 34bacd142..84bac1501 100644 --- a/src/SOS/dbgutil/CMakeLists.txt +++ b/src/SOS/dbgutil/CMakeLists.txt @@ -3,7 +3,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) 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) @@ -22,5 +22,4 @@ if(NOT DEFINED CLR_CMAKE_HOST_OSX) ) endif(NOT DEFINED CLR_CMAKE_HOST_OSX) - add_library(dbgutil STATIC ${DBGUTIL_SOURCES}) diff --git a/src/SOS/debugshim/CMakeLists.txt b/src/SOS/debugshim/CMakeLists.txt deleted file mode 100644 index e20056431..000000000 --- a/src/SOS/debugshim/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -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}) diff --git a/src/SOS/debugshim/debugshim.cpp b/src/SOS/debugshim/debugshim.cpp deleted file mode 100644 index f7d7a7a8c..000000000 --- a/src/SOS/debugshim/debugshim.cpp +++ /dev/null @@ -1,789 +0,0 @@ -// 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 -#include //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 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", 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(this); - pItf->AddRef(); - *ppvObject = pItf; - } - else if (riid == __uuidof(ICLRDebugging)) - { - ICLRDebugging *pItf = static_cast(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; -} diff --git a/src/SOS/debugshim/debugshim.h b/src/SOS/debugshim/debugshim.h deleted file mode 100644 index d762e4a58..000000000 --- a/src/SOS/debugshim/debugshim.h +++ /dev/null @@ -1,91 +0,0 @@ -// 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 -#include - -#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 diff --git a/src/SOS/debugshim/debugshim.vcxproj b/src/SOS/debugshim/debugshim.vcxproj deleted file mode 100644 index 4bd8dba7f..000000000 --- a/src/SOS/debugshim/debugshim.vcxproj +++ /dev/null @@ -1,318 +0,0 @@ - - - - - Debug - x64 - - - Checked - x64 - - - Release - x64 - - - RelWithDebInfo - x64 - - - - {6A94C5FE-8706-3505-834E-DA16242F3864} - 10.0 - Win32Proj - x64 - debugshim - NoUpgrade - - - - StaticLibrary - MultiByte - v142 - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - - - - - - - - - <_ProjectFileVersion>10.0.20506.1 - $(ArtifactsObjDir)Windows_NT.x64.Debug\src\SOS\debugshim\Debug\ - debugshim.dir\Debug\ - debugshim - .lib - $(ArtifactsObjDir)Windows_NT.x64.Debug\src\SOS\debugshim\Checked\ - debugshim.dir\Checked\ - debugshim - .lib - $(ArtifactsObjDir)Windows_NT.x64.Debug\src\SOS\debugshim\Release\ - debugshim.dir\Release\ - debugshim - .lib - $(ArtifactsObjDir)Windows_NT.x64.Debug\src\SOS\debugshim\RelWithDebInfo\ - debugshim.dir\RelWithDebInfo\ - debugshim - .lib - - - - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - %(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8 /homeparams - Debug/ - true - CompileAsCpp - Guard - ProgramDatabase - 4960;4961;4603;4627;4838;4456;4457;4458;4459;4091 - Async - true - WarningControl.h - true - true - false - true - true - false - Disabled - NotUsing - MultiThreaded - false - true - 8Bytes - true - 4640 - false - true - _MT - true - Level3 - 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) - $(IntDir) - - - 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) - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - - - 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) - %(AdditionalOptions) /ZH:SHA_256 - ..\..\pal\prebuilt\inc;..\..\inc;%(IncludePaths) - - - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - $(ProjectDir)/$(IntDir) - %(Filename).h - %(Filename).tlb - %(Filename)_i.c - %(Filename)_p.c - - - %(AdditionalOptions) /machine:x64 /IGNORE:4221 - - - - - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - %(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8 - Checked/ - true - CompileAsCpp - Guard - ProgramDatabase - 4960;4961;4603;4627;4838;4456;4457;4458;4459;4091 - Async - true - WarningControl.h - true - true - false - true - true - false - MinSpace - NotUsing - MultiThreaded - false - true - 8Bytes - true - 4640 - false - true - _MT - true - Level3 - 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) - $(IntDir) - - - 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) - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - - - 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) - %(AdditionalOptions) /ZH:SHA_256 - ..\..\pal\prebuilt\inc;..\..\inc;%(IncludePaths) - - - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - $(ProjectDir)/$(IntDir) - %(Filename).h - %(Filename).tlb - %(Filename)_i.c - %(Filename)_p.c - - - %(AdditionalOptions) /machine:x64 /IGNORE:4221 - - - - - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - %(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8 - Release/ - true - CompileAsCpp - Guard - ProgramDatabase - 4960;4961;4603;4627;4838;4456;4457;4458;4459;4091 - Async - true - WarningControl.h - true - true - false - true - true - false - MinSpace - NotUsing - MultiThreaded - false - true - 8Bytes - true - 4640 - false - true - _MT - true - Level3 - true - 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) - $(IntDir) - - - 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) - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - - - 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) - %(AdditionalOptions) /ZH:SHA_256 - ..\..\pal\prebuilt\inc;..\..\inc;%(IncludePaths) - - - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - $(ProjectDir)/$(IntDir) - %(Filename).h - %(Filename).tlb - %(Filename)_i.c - %(Filename)_p.c - - - %(AdditionalOptions) /machine:x64 /IGNORE:4221 - true - - - - - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - %(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8 - RelWithDebInfo/ - true - CompileAsCpp - Guard - ProgramDatabase - 4960;4961;4603;4627;4838;4456;4457;4458;4459;4091 - Async - true - WarningControl.h - true - true - false - true - true - false - MinSpace - NotUsing - MultiThreaded - false - true - 8Bytes - true - 4640 - false - true - _MT - true - Level3 - true - 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) - $(IntDir) - - - 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) - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - - - 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) - %(AdditionalOptions) /ZH:SHA_256 - ..\..\pal\prebuilt\inc;..\..\inc;%(IncludePaths) - - - ..\..\pal\prebuilt\inc;..\..\inc;%(AdditionalIncludeDirectories) - $(ProjectDir)/$(IntDir) - %(Filename).h - %(Filename).tlb - %(Filename)_i.c - %(Filename)_p.c - - - %(AdditionalOptions) /machine:x64 /IGNORE:4221 - true - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/SOS/extensions/CMakeLists.txt b/src/SOS/extensions/CMakeLists.txt index b7ab844b0..1ffba4df6 100644 --- a/src/SOS/extensions/CMakeLists.txt +++ b/src/SOS/extensions/CMakeLists.txt @@ -23,7 +23,6 @@ endif(WIN32 AND NOT CLR_CMAKE_HOST_ARCH_ARM64) 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) diff --git a/src/SOS/extensions/hostcoreclr.cpp b/src/SOS/extensions/hostcoreclr.cpp index f694d3e5b..757e4ad95 100644 --- a/src/SOS/extensions/hostcoreclr.cpp +++ b/src/SOS/extensions/hostcoreclr.cpp @@ -294,7 +294,7 @@ static bool FindDotNetVersion(int majorFilter, int minorFilter, std::string& hos static bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable) { ArrayHolder hostPath = new char[MAX_LONGPATH+1]; - if (::GetModuleFileName(NULL, hostPath, MAX_LONGPATH) == 0) + if (::GetModuleFileNameA(NULL, hostPath, MAX_LONGPATH) == 0) { return false; } diff --git a/src/SOS/gcdump/gcdump.cpp b/src/SOS/gcdump/gcdump.cpp index 84cb0beed..2ce81887f 100644 --- a/src/SOS/gcdump/gcdump.cpp +++ b/src/SOS/gcdump/gcdump.cpp @@ -10,9 +10,6 @@ * to the standard code-manager spec. */ -#ifndef TARGET_UNIX -#include "utilcode.h" // For _ASSERTE() -#endif //!TARGET_UNIX #include "gcdump.h" /*****************************************************************************/ diff --git a/src/SOS/gcdump/i386/gcdumpx86.cpp b/src/SOS/gcdump/i386/gcdumpx86.cpp index 1bf3e6101..868235fc3 100644 --- a/src/SOS/gcdump/i386/gcdumpx86.cpp +++ b/src/SOS/gcdump/i386/gcdumpx86.cpp @@ -8,9 +8,6 @@ #ifdef TARGET_X86 /*****************************************************************************/ -#ifndef TARGET_UNIX -#include "utilcode.h" // For _ASSERTE() -#endif //!TARGET_UNIX #include "gcdump.h" diff --git a/src/dbgshim/CMakeLists.txt b/src/dbgshim/CMakeLists.txt new file mode 100644 index 000000000..1625bb281 --- /dev/null +++ b/src/dbgshim/CMakeLists.txt @@ -0,0 +1,77 @@ +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 . ) diff --git a/src/dbgshim/dbgshim.cpp b/src/dbgshim/dbgshim.cpp new file mode 100644 index 000000000..40e4d7dea --- /dev/null +++ b/src/dbgshim/dbgshim.cpp @@ -0,0 +1,2005 @@ +// 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 +#include +#include +#include +#include +#include +#ifndef TARGET_UNIX +#include +#endif + +#include +#include // for Version nunmbers +#include +#include +#include +#include + +#ifndef TARGET_UNIX +#define PSAPI_VERSION 2 +#include +#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(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( + 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(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 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 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 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 +} diff --git a/src/dbgshim/dbgshim.h b/src/dbgshim/dbgshim.h new file mode 100644 index 000000000..004fd2a38 --- /dev/null +++ b/src/dbgshim/dbgshim.h @@ -0,0 +1,91 @@ +// 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 + +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); diff --git a/src/dbgshim/dbgshim.ntdef b/src/dbgshim/dbgshim.ntdef new file mode 100644 index 000000000..2e254ab9d --- /dev/null +++ b/src/dbgshim/dbgshim.ntdef @@ -0,0 +1,18 @@ +; 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 diff --git a/src/dbgshim/dbgshim.rc b/src/dbgshim/dbgshim.rc new file mode 100644 index 000000000..9b4ac3b91 --- /dev/null +++ b/src/dbgshim/dbgshim.rc @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#define FX_VER_FILEDESCRIPTION_STR "Microsoft .NET Runtime Multi-CLR Debugging Helper\0" + +#include +#include diff --git a/src/dbgshim/dbgshim.vcxproj b/src/dbgshim/dbgshim.vcxproj new file mode 100644 index 000000000..608c68f68 --- /dev/null +++ b/src/dbgshim/dbgshim.vcxproj @@ -0,0 +1,391 @@ + + + + x64 + + + + Debug + x64 + + + Checked + x64 + + + Release + x64 + + + RelWithDebInfo + x64 + + + + {BD779298-8631-3F5D-AA59-82897E5454A7} + 10.0.19041.0 + Win32Proj + x64 + dbgshim + NoUpgrade + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + $(ArtifactsObjDir)Windows_NT.x64.Debug\src\dbgshim\Debug\ + dbgshim.dir\Debug\ + dbgshim + .dll + true + false + $(ArtifactsObjDir)Windows_NT.x64.Debug\src\dbgshim\Checked\ + dbgshim.dir\Checked\ + dbgshim + .dll + false + false + $(ArtifactsObjDir)Windows_NT.x64.Debug\src\dbgshim\Release\ + dbgshim.dir\Release\ + dbgshim + .dll + false + false + $(ArtifactsObjDir)Windows_NT.x64.Debug\src\dbgshim\RelWithDebInfo\ + dbgshim.dir\RelWithDebInfo\ + dbgshim + .dll + true + false + + + + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 /homeparams + $(IntDir) + EnableFastChecks + true + Guard + ProgramDatabase + 4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091 + Sync + Precise + true + true + Disabled + true + false + true + false + Disabled + NotUsing + true + MultiThreadedDebug + false + 8Bytes + true + 4007;4013;4102;4551;4700;4640;4806 + true + true + true + Level3 + 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) + $(IntDir) + + + 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) + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + + + 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) + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths) + %(AdditionalOptions) /guard:ehcont + + + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + ..\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 + %(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 /guard:cf /PDBCOMPRESS /IGNORE:4197,4013,4254,4070,4221 /SUBSYSTEM:WINDOWS,6.01 /guard:ehcont /CETCOMPAT + true + %(IgnoreSpecificDefaultLibraries) + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Debug/dbgshim.lib + true + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/dbgshim.def + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Debug/dbgshim.pdb + Console + + + false + + + + + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 + $(IntDir) + true + Guard + ProgramDatabase + 4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091 + Sync + Precise + true + true + true + false + true + false + MaxSpeed + NotUsing + true + MultiThreadedDebug + false + 8Bytes + true + 4007;4013;4102;4551;4700;4640;4806 + true + true + true + Level3 + 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) + $(IntDir) + + + 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) + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + + + 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) + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths) + %(AdditionalOptions) /guard:ehcont + + + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + ..\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 + %(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 /guard:cf /PDBCOMPRESS /IGNORE:4197,4013,4254,4070,4221 /SUBSYSTEM:WINDOWS,6.01 /guard:ehcont /CETCOMPAT + false + true + %(IgnoreSpecificDefaultLibraries) + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Checked/dbgshim.lib + true + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/dbgshim.def + true + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Checked/dbgshim.pdb + Console + + + false + + + + + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 + $(IntDir) + true + Guard + ProgramDatabase + 4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091 + Sync + Precise + true + true + AnySuitable + true + false + true + false + Full + NotUsing + true + MultiThreaded + false + 8Bytes + true + 4007;4013;4102;4551;4700;4640;4806 + true + true + true + Level3 + true + 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) + $(IntDir) + + + 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) + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + + + 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) + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths) + %(AdditionalOptions) /guard:ehcont + + + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + ..\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 + %(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 /guard:cf /PDBCOMPRESS /IGNORE:4197,4013,4254,4070,4221 /SUBSYSTEM:WINDOWS,6.01 /guard:ehcont /CETCOMPAT /DEFAULTLIB:ucrt.lib + true + true + libucrt.lib;%(IgnoreSpecificDefaultLibraries) + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Release/dbgshim.lib + true + UseLinkTimeCodeGeneration + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/dbgshim.def + true + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/Release/dbgshim.pdb + Console + + + false + + + + + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 + $(IntDir) + true + Guard + ProgramDatabase + 4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091 + Sync + Precise + true + true + OnlyExplicitInline + true + false + true + false + MaxSpeed + NotUsing + true + MultiThreaded + false + 8Bytes + true + 4007;4013;4102;4551;4700;4640;4806 + true + true + true + Level3 + true + 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) + $(IntDir) + + + 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) + $(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + + + 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) + $(RepoRoot)artifacts\obj;$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths) + %(AdditionalOptions) /guard:ehcont + + + $(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + ..\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 + %(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 /guard:cf /PDBCOMPRESS /IGNORE:4197,4013,4254,4070,4221 /SUBSYSTEM:WINDOWS,6.01 /guard:ehcont /CETCOMPAT + true + true + %(IgnoreSpecificDefaultLibraries) + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/RelWithDebInfo/dbgshim.lib + true + UseLinkTimeCodeGeneration + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/dbgshim.def + true + $(ArtifactsObjDir)Windows_NT.x64.Debug/src/dbgshim/RelWithDebInfo/dbgshim.pdb + Console + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dbgshim/dbgshim.vcxproj.filters b/src/dbgshim/dbgshim.vcxproj.filters new file mode 100644 index 000000000..aa9a2523c --- /dev/null +++ b/src/dbgshim/dbgshim.vcxproj.filters @@ -0,0 +1,65 @@ + + + + + + + + + + pkg + + + pkg + + + pkg + + + pkg + + + pkg + + + pkg + + + pkg + + + pkg + + + pkg + + + pkg + + + pkg + + + pkg + + + pkg + + + pkg + + + + + + + + + + {ac4ed0ef-d259-4eed-8152-b4c1db4df94c} + + + + + + \ No newline at end of file diff --git a/src/dbgshim/dbgshim_unixexports.src b/src/dbgshim/dbgshim_unixexports.src new file mode 100644 index 000000000..b1daf52e5 --- /dev/null +++ b/src/dbgshim/dbgshim_unixexports.src @@ -0,0 +1,17 @@ +; 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 diff --git a/src/dbgshim/debugshim.cpp b/src/dbgshim/debugshim.cpp new file mode 100644 index 000000000..3dcfbcc25 --- /dev/null +++ b/src/dbgshim/debugshim.cpp @@ -0,0 +1,785 @@ +// 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 +#include //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 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", 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(this); + pItf->AddRef(); + *ppvObject = pItf; + } + else if (riid == __uuidof(ICLRDebugging)) + { + ICLRDebugging *pItf = static_cast(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; +} diff --git a/src/dbgshim/debugshim.h b/src/dbgshim/debugshim.h new file mode 100644 index 000000000..93df46386 --- /dev/null +++ b/src/dbgshim/debugshim.h @@ -0,0 +1,89 @@ +// 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 +#include + +#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 diff --git a/src/dbgshim/pkg/Directory.Build.props b/src/dbgshim/pkg/Directory.Build.props new file mode 100644 index 000000000..f01385030 --- /dev/null +++ b/src/dbgshim/pkg/Directory.Build.props @@ -0,0 +1,43 @@ + + + + true + true + Internal implementation package not meant for direct consumption. Please do not reference directly. + + + + + + Windows_NT + .exe + .dll + .lib + .pdb + + + + + OSX + lib + .dylib + .a + .dwarf + + + + + Linux + Linux-musl + lib + .so + .a + .dbg + + + + + + $(ArtifactsBinDir)\$(OsFolderName).$(PackageArch).$(Configuration)\ + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-arm.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-arm.proj new file mode 100644 index 000000000..039146be0 --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-arm.proj @@ -0,0 +1,7 @@ + + + linux-arm + arm + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-arm64.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-arm64.proj new file mode 100644 index 000000000..bc7268cfd --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-arm64.proj @@ -0,0 +1,7 @@ + + + linux-arm64 + arm64 + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-musl-arm64.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-musl-arm64.proj new file mode 100644 index 000000000..f0ed0b096 --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-musl-arm64.proj @@ -0,0 +1,7 @@ + + + linux-musl-x64 + x64 + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-musl-x64.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-musl-x64.proj new file mode 100644 index 000000000..f0ed0b096 --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-musl-x64.proj @@ -0,0 +1,7 @@ + + + linux-musl-x64 + x64 + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-x64.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-x64.proj new file mode 100644 index 000000000..b68d4f6e3 --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.linux-x64.proj @@ -0,0 +1,7 @@ + + + linux-x64 + x64 + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.osx-arm64.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.osx-arm64.proj new file mode 100644 index 000000000..1db7896ba --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.osx-arm64.proj @@ -0,0 +1,7 @@ + + + osx-arm64 + arm64 + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.osx-x64.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.osx-x64.proj new file mode 100644 index 000000000..8e7c9ec48 --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.osx-x64.proj @@ -0,0 +1,7 @@ + + + osx-x64 + x64 + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.proj new file mode 100644 index 000000000..13da9be6f --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.proj @@ -0,0 +1,26 @@ + + + netstandard2.0 + true + + false + + $(NoWarn);NU5128 + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.props b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.props new file mode 100644 index 000000000..045b82e1c --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.props @@ -0,0 +1,34 @@ + + + netstandard2.0 + true + + false + true + false + none + true + .pdb;.dbg;.dwarf + + + true + + $(NoWarn);NU5128 + + + + + + + + + + + + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-arm.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-arm.proj new file mode 100644 index 000000000..a017c118c --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-arm.proj @@ -0,0 +1,7 @@ + + + win-arm + arm + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-arm64.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-arm64.proj new file mode 100644 index 000000000..07c9fc387 --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-arm64.proj @@ -0,0 +1,7 @@ + + + win-arm64 + arm64 + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-x64.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-x64.proj new file mode 100644 index 000000000..87f3cef75 --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-x64.proj @@ -0,0 +1,7 @@ + + + win-x64 + x64 + + + diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-x86.proj b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-x86.proj new file mode 100644 index 000000000..a3699b17e --- /dev/null +++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.win-x86.proj @@ -0,0 +1,7 @@ + + + win-x86 + x86 + + + diff --git a/src/inc/CMakeLists.txt b/src/inc/CMakeLists.txt index 360c2fd6f..677ffb3fe 100644 --- a/src/inc/CMakeLists.txt +++ b/src/inc/CMakeLists.txt @@ -1,7 +1,3 @@ -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 diff --git a/src/inc/check.h b/src/inc/check.h new file mode 100644 index 000000000..c033965d4 --- /dev/null +++ b/src/inc/check.h @@ -0,0 +1,726 @@ +// 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 +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 +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 +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 +CHECK CheckPointer(TYPENAME *o, IsNullOK ok = NULL_NOT_OK) +{ + if (o == NULL) + { + CHECK_MSG(ok, "Illegal null pointer"); + } + + CHECK_OK; +} + +template +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, "", __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_ diff --git a/src/inc/check.inl b/src/inc/check.inl new file mode 100644 index 000000000..f234e988f --- /dev/null +++ b/src/inc/check.inl @@ -0,0 +1,325 @@ +// 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(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(rangeBase), rangeSize)); + CHECK(offset <= rangeSize); + CHECK_OK; +} + +inline CHECK CheckBounds(const void *rangeBase, UINT32 rangeSize, UINT32 offset, UINT32 size) +{ + CHECK(CheckOverflow(dac_cast(rangeBase), rangeSize)); + CHECK(CheckOverflow(offset, size)); + CHECK(offset + size <= rangeSize); + CHECK_OK; +} + +#endif // CHECK_INL_ + diff --git a/src/inc/clrhost.h b/src/inc/clrhost.h new file mode 100644 index 000000000..d8fe0c597 --- /dev/null +++ b/src/inc/clrhost.h @@ -0,0 +1,130 @@ +// 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_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, 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 diff --git a/src/inc/clrnt.h b/src/inc/clrnt.h new file mode 100644 index 000000000..e12c58870 --- /dev/null +++ b/src/inc/clrnt.h @@ -0,0 +1,1032 @@ +// 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((FunctionEntry)->BeginAddress) +#define RUNTIME_FUNCTION__SetBeginAddress(FunctionEntry,address) ((FunctionEntry)->BeginAddress = DataPointerToThumbCode(address)) + +#define RUNTIME_FUNCTION__EndAddress(FunctionEntry, ImageBase) ThumbCodeToDataPointer(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_ diff --git a/src/inc/clrprivappxhosting.idl b/src/inc/clrprivappxhosting.idl deleted file mode 100644 index 04a4cf940..000000000 --- a/src/inc/clrprivappxhosting.idl +++ /dev/null @@ -1,66 +0,0 @@ -// 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); -}; diff --git a/src/inc/clrprivbinding.idl b/src/inc/clrprivbinding.idl deleted file mode 100644 index 9f4ee2f3d..000000000 --- a/src/inc/clrprivbinding.idl +++ /dev/null @@ -1,99 +0,0 @@ -// 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); -}; diff --git a/src/inc/clrprivhosting.idl b/src/inc/clrprivhosting.idl deleted file mode 100644 index b288c286b..000000000 --- a/src/inc/clrprivhosting.idl +++ /dev/null @@ -1,92 +0,0 @@ -// 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 - ); -}; - diff --git a/src/inc/clrprivruntimebinders.idl b/src/inc/clrprivruntimebinders.idl deleted file mode 100644 index 0da7e3acd..000000000 --- a/src/inc/clrprivruntimebinders.idl +++ /dev/null @@ -1,39 +0,0 @@ -// 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; - }; -}; - diff --git a/src/inc/clrtypes.h b/src/inc/clrtypes.h new file mode 100644 index 000000000..605093681 --- /dev/null +++ b/src/inc/clrtypes.h @@ -0,0 +1,409 @@ +// 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 +#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 +T Min(T v1, T v2) +{ + STATIC_CONTRACT_LEAF; + return v1 < v2 ? v1 : v2; +} + +template +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_ diff --git a/src/inc/contract.h b/src/inc/contract.h new file mode 100644 index 000000000..a880d77c2 --- /dev/null +++ b/src/inc/contract.h @@ -0,0 +1,2236 @@ +// 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 +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 + 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 + class PseudoTemplate + { + public: + FORCEINLINE void *operator new (size_t size) + { + return NULL; + } + + FORCEINLINE Box__USE_PARENS_WITH_THIS_EXPRESSION operator>(const T &value) + { + return Box__USE_PARENS_WITH_THIS_EXPRESSION(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 + class Returner + { + RETURNTYPE &m_value; + BOOL m_got; + public: + + FORCEINLINE Returner(RETURNTYPE &value) + : m_value(value), + m_got(FALSE) + { + } + + template + FORCEINLINE RETURNTYPE operator,(Box__USE_PARENS_WITH_THIS_EXPRESSION 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 +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 +class AutoCleanupContractViolationHolder : ContractViolationHolder +{ +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 __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 __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 __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 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_ diff --git a/src/inc/contract.inl b/src/inc/contract.inl new file mode 100644 index 000000000..06ee1ef06 --- /dev/null +++ b/src/inc/contract.inl @@ -0,0 +1,635 @@ +// 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 + +#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_ diff --git a/src/inc/corhlprpriv.h b/src/inc/corhlprpriv.h new file mode 100644 index 000000000..38faa6f6e --- /dev/null +++ b/src/inc/corhlprpriv.h @@ -0,0 +1,789 @@ +// 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 + struct _AllocBytes; + + template <> + struct _AllocBytes + { + static BYTE *Invoke(SIZE_T iItems) + { + return NEW_THROWS(iItems); + } + }; + + template <> + struct _AllocBytes + { + static BYTE *Invoke(SIZE_T iItems) + { + return NEW_NOTHROW(iItems); + } + }; +}; + +void DECLSPEC_NORETURN ThrowHR(HRESULT hr); + +template +class CQuickMemoryBase +{ +protected: + template + static ELEM_T Min(ELEM_T a, ELEM_T b) + { return a < b ? a : b; } + + template + 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 + void *_Alloc(SIZE_T iItems) + { +#if defined(_BLD_CLR) && defined(_DEBUG) + { // Exercise heap for OOM-fault injection purposes + BYTE * pb = NSQuickBytesHelper::_AllocBytes::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::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(iItems); + } + + void *AllocNoThrow(SIZE_T iItems) + { + return _Alloc(iItems); + } + + void ReSizeThrows(SIZE_T iItems) + { + _Alloc(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(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 +{ +}; + + +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 +class CQuickBytesSpecifySizeBase : public CQuickMemoryBase +{ +}; + +template +class CQuickBytesSpecifySize : public CQuickBytesSpecifySizeBase +{ +public: + CQuickBytesSpecifySize() + { + this->Init(); + } + + ~CQuickBytesSpecifySize() + { + this->Destroy(); + } +}; + +/* to be used as static variable - no constructor/destructor, assumes zero + initialized memory */ +template +class CQuickBytesSpecifySizeStatic : public CQuickBytesSpecifySizeBase +{ +}; + +template 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 CQuickArray : public CQuickArrayBase +{ +public: + CQuickArray() + { + this->Init(); + } + + ~CQuickArray() + { + 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 CQuickArrayList : protected CQuickArray +{ +private: + SIZE_T m_curSize; + +public: + // Make these specific functions public. + using CQuickArray::AllocThrows; + using CQuickArray::ReSizeThrows; + using CQuickArray::AllocNoThrow; + using CQuickArray::ReSizeNoThrow; + using CQuickArray::MaxSize; + using CQuickArray::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::operator[](ix); + } + + // Can only access values that have been pushed. + const T& operator[] (SIZE_T ix) const + { + _ASSERTE(ix < m_curSize); + return CQuickArray::operator[](ix); + } + + // THROWS: Resizes if necessary. + void Push(const T & value) + { + // Resize if necessary - thows. + if (m_curSize + 1 >= CQuickArray::Size()) + ReSizeThrows((m_curSize + 1) * 2); + + // Append element to end of array. + _ASSERTE(m_curSize + 1 < CQuickArray::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::Size()) { + if (ReSizeNoThrow((m_curSize + 1) * 2) != NOERROR) + return FALSE; + } + + // Append element to end of array. + _ASSERTE(m_curSize + 1 < CQuickArray::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::Shrink(m_curSize); + } +}; + + +/* to be used as static variable - no constructor/destructor, assumes zero + initialized memory */ +template class CQuickArrayStatic : public CQuickArrayBase +{ +}; + +typedef CQuickArrayBase CQuickWSTRBase; +typedef CQuickArray CQuickWSTR; +typedef CQuickArrayStatic CQuickWSTRStatic; + +typedef CQuickArrayBase CQuickSTRBase; +typedef CQuickArray CQuickSTR; +typedef CQuickArrayStatic 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 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__ diff --git a/src/inc/crsttypes.h b/src/inc/crsttypes.h new file mode 100644 index 000000000..d462cbd3c --- /dev/null +++ b/src/inc/crsttypes.h @@ -0,0 +1,410 @@ +// +// 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) diff --git a/src/inc/crtwrap.h b/src/inc/crtwrap.h new file mode 100644 index 000000000..3a54ccfb8 --- /dev/null +++ b/src/inc/crtwrap.h @@ -0,0 +1,29 @@ +// 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 +#include +#include +#include +#include "debugmacros.h" +#include +#include +#include +#include + +#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__ diff --git a/src/inc/daccess.h b/src/inc/daccess.h index 4ba48f7e0..5c41b9e06 100644 --- a/src/inc/daccess.h +++ b/src/inc/daccess.h @@ -15,6 +15,9 @@ #ifdef PAL_STDCPP_COMPAT #include +#else +#include "clr_std/type_traits" +#include "crosscomp.h" #endif // @@ -35,6 +38,13 @@ typedef ULONG_PTR TADDR; // 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 @@ -67,6 +77,17 @@ typedef const void* PTR_CVOID; #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 @@ -131,6 +152,8 @@ inline Tgt dac_cast(Src src) // 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) } //---------------------------------------------------------------------------- @@ -192,6 +215,7 @@ typedef DPTR(IMAGE_NT_HEADERS) PTR_IMAGE_NT_HEADERS; 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; //---------------------------------------------------------------------------- @@ -204,4 +228,22 @@ typedef TADDR PCODE; 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__ diff --git a/src/inc/dbgenginemetrics.h b/src/inc/dbgenginemetrics.h new file mode 100644 index 000000000..ebb7fc140 --- /dev/null +++ b/src/inc/dbgenginemetrics.h @@ -0,0 +1,29 @@ +// 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__ diff --git a/src/inc/debugmacros.h b/src/inc/debugmacros.h index e6124828d..99cfd60b1 100644 --- a/src/inc/debugmacros.h +++ b/src/inc/debugmacros.h @@ -10,7 +10,6 @@ #ifndef __DebugMacros_h__ #define __DebugMacros_h__ -#include "stacktrace.h" #include "debugmacrosext.h" #include "palclr.h" @@ -31,9 +30,6 @@ bool _DbgBreakCheck(LPCSTR szFile, int iLine, LPCSTR szExpr, BOOL fConstrained = 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 */ @@ -199,31 +195,4 @@ do { hr = (EXPR); if(hr != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(hr); goto LA #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 diff --git a/src/inc/debugreturn.h b/src/inc/debugreturn.h new file mode 100644 index 000000000..d052364ff --- /dev/null +++ b/src/inc/debugreturn.h @@ -0,0 +1,124 @@ +// 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_ diff --git a/src/inc/entrypoints.h b/src/inc/entrypoints.h new file mode 100644 index 000000000..dcd6cb5d0 --- /dev/null +++ b/src/inc/entrypoints.h @@ -0,0 +1,28 @@ +// 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__ + + diff --git a/src/inc/ex.h b/src/inc/ex.h new file mode 100644 index 000000000..7fbc9278e --- /dev/null +++ b/src/inc/ex.h @@ -0,0 +1,1332 @@ +// 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; +#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 +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 __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 __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 +// +// 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 +// +// 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 +// +// 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 = 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_ diff --git a/src/inc/fstring.h b/src/inc/fstring.h new file mode 100644 index 000000000..1be37e242 --- /dev/null +++ b/src/inc/fstring.h @@ -0,0 +1,44 @@ +// 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_ diff --git a/src/inc/getproductversionnumber.h b/src/inc/getproductversionnumber.h new file mode 100644 index 000000000..068ed7849 --- /dev/null +++ b/src/inc/getproductversionnumber.h @@ -0,0 +1,76 @@ +// 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 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(&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__ diff --git a/src/inc/holder.h b/src/inc/holder.h new file mode 100644 index 000000000..4ec7b106c --- /dev/null +++ b/src/inc/holder.h @@ -0,0 +1,1404 @@ +// 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 +#include "cor.h" +#include "staticcontract.h" +#include "volatile.h" +#include "palclr.h" + +#ifdef PAL_STDCPP_COMPAT +#include +#include +#else +#include "clr_std/utility" +#include "clr_std/type_traits" +#endif + +#if defined(FEATURE_COMINTEROP) && !defined(STRIKE) +#include +#include +#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=_rProp._rProp[3].asStr> +// HolderBase=_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 +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 +BOOL CompareDefault(TYPE value, TYPE defaultValue) +{ + STATIC_CONTRACT_SUPPORTS_DAC; + return value == defaultValue; +} + +#else + +template +BOOL CompareDefault(TYPE value, TYPE defaultValue) +{ + return FALSE; +} + +#endif + + +template +BOOL NoNull(TYPE value, TYPE defaultValue) +{ + return FALSE; +} + +// Used e.g. for retail version of code:SyncAccessHolder +template +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 + > +class BaseHolder : protected BASE +{ + friend class ClrDataAccess; + protected: + BOOL m_acquired; // Have we acquired the resource? + + static_assert(!std::is_pointer::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 +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 +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 > +class BaseWrapper : public BaseHolder +{ + typedef BaseHolder 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 &m_holder; + + public: + FORCEINLINE AddressInitHolder(BaseWrapper &holder) + : m_holder(holder) + { + // + // We must clear the value, to avoid the following scenario: + // + // ReleaseHolder pMyType; + // hr = MyFunction(&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(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(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(&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 + FORCEINLINE bool operator==(T const & value) const + { + return !!(this->m_value == TYPE(value)); + } + template + 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 +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 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 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 +class FunctionBase : protected HolderBase +{ + friend class ClrDataAccess; + protected: + + FORCEINLINE FunctionBase(TYPE value) + : HolderBase(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, + // 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, + DEFAULTVALUE, IS_NULL> +{ + typedef BaseHolder, + 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, + // 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, + DEFAULTVALUE, IS_NULL> +{ + typedef BaseWrapper, + 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 +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 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 +FORCEINLINE void DoTheRelease(TYPE *value) +{ + if (value) + { + value->Release(); + } +} + +template +using DoNothingHolder = SpecializedWrapper<_TYPE, DoNothing<_TYPE*>>; + +template +using ReleaseHolder = SpecializedWrapper<_TYPE, DoTheRelease<_TYPE>>; + +template +using NonVMComHolder = SpecializedWrapper<_TYPE, DoTheRelease<_TYPE>>; + + +//----------------------------------------------------------------------------- +// StubHolder : holder for stubs +// +// Usage example: +// +// { +// StubHolder foo; +// foo = new Stub(); +// foo->AddRef(); +// // Note StubHolder doesn't call AddRef for you. +// } // foo->DecRef() on out of scope +// +//----------------------------------------------------------------------------- +template +class ExecutableWriterHolder; + +template +FORCEINLINE void StubRelease(TYPE* value) +{ + if (value) + { + ExecutableWriterHolder stubWriterHolder(value, sizeof(TYPE)); + stubWriterHolder.GetRW()->DecRef(); + } +} + +template +using StubHolder = SpecializedWrapper<_TYPE, StubRelease<_TYPE>>; + +//----------------------------------------------------------------------------- +// CoTaskMemHolder : CoTaskMemAlloc allocated memory holder +// +// { +// CoTaskMemHolder foo = (Foo*) CoTaskMemAlloc(sizeof(Foo)); +// } // delete foo on out of scope +//----------------------------------------------------------------------------- + +template +FORCEINLINE void DeleteCoTaskMem(TYPE *value) +{ + if (value) + CoTaskMemFree(value); +} + +template +using CoTaskMemHolder = SpecializedWrapper<_TYPE, DeleteCoTaskMem<_TYPE>>; + +//----------------------------------------------------------------------------- +// NewHolder : New'ed memory holder +// +// { +// NewHolder foo = new Foo (); +// } // delete foo on out of scope +//----------------------------------------------------------------------------- + +template +FORCEINLINE void Delete(TYPE *value) +{ + STATIC_CONTRACT_LEAF; + + static_assert(!std::is_same::type, WCHAR>::value, + "Must use NewArrayHolder (not NewHolder) for strings."); + static_assert(!std::is_same::type, CHAR>::value, + "Must use NewArrayHolder (not NewHolder) for strings."); + + delete value; +} + +template +using NewHolder = SpecializedWrapper<_TYPE, Delete<_TYPE>>; + + //----------------------------------------------------------------------------- +// NewExecutableHolder : New'ed memory holder for executable memory. +// +// { +// NewExecutableHolder foo = (Foo*) new (executable) Byte[num]; +// } // delete foo on out of scope +//----------------------------------------------------------------------------- +// IJW +template void DeleteExecutable(T *p); + +template +using NewExecutableHolder = SpecializedWrapper<_TYPE, DeleteExecutable<_TYPE>>; + +//----------------------------------------------------------------------------- +// NewArrayHolder : New []'ed pointer holder +// { +// NewArrayHolder foo = new Foo [30]; +// } // delete [] foo on out of scope +//----------------------------------------------------------------------------- + +template +FORCEINLINE void DeleteArray(TYPE *value) +{ + STATIC_CONTRACT_WRAPPER; + delete [] value; + value = NULL; +} + +template +using NewArrayHolder = SpecializedWrapper<_TYPE, DeleteArray<_TYPE>>; +typedef NewArrayHolder AStringHolder; +typedef NewArrayHolder 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 +class NewInterfaceArrayHolder : public NewArrayHolder +{ +public: + NewInterfaceArrayHolder() : + NewArrayHolder(), + m_cElements(0) + { + STATIC_CONTRACT_WRAPPER; + } + + NewInterfaceArrayHolder& operator=(INTERFACE ** value) + { + STATIC_CONTRACT_WRAPPER; + NewArrayHolder::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 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 + struct ZeroMem + { + static VISIBLE void Invoke(T * pVal) + { + ZeroMemory(pVal, sizeof(T)); + } + }; + + template + struct ZeroMem + { + static VISIBLE void Invoke(T ** pVal) + { + *pVal = NULL; + } + }; + +} +#undef VISIBLE + +template +using ResetPointerHolder = SpecializedWrapper<_TYPE, detail::ZeroMem<_TYPE>::Invoke>; +template +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 +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, VoidCloseHandle, (UINT_PTR) -1> HandleHolder; +typedef Wrapper, VoidCloseFileHandle, (UINT_PTR) -1> FileHandleHolder; +typedef Wrapper, VoidFindClose, (UINT_PTR) -1> FindHandleHolder; + +typedef Wrapper 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, VoidDeleteFile, NULL> DeleteFileHolder; +#endif // WszDeleteFile + + +//----------------------------------------------------------------------------- +// Misc holders +//----------------------------------------------------------------------------- + +// A holder for HMODULE. +FORCEINLINE void HolderFreeLibrary(HMODULE h) { FreeLibrary(h); } + +typedef Wrapper, HolderFreeLibrary, NULL> HModuleHolder; + +template FORCEINLINE +void DoLocalFree(T* pMem) +{ +#ifdef HOST_WINDOWS + (LocalFree)((void*)pMem); +#else + (free)((void*)pMem); +#endif +} + +template +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. +// + +FORCEINLINE void CounterIncrease(RAW_KEYWORD(volatile) LONG* p) {InterlockedIncrement(p);}; +FORCEINLINE void CounterDecrease(RAW_KEYWORD(volatile) LONG* p) {InterlockedDecrement(p);}; + +typedef Wrapper> CounterHolder; + + +#ifdef HOST_WINDOWS +FORCEINLINE void RegKeyRelease(HKEY k) {RegCloseKey(k);}; +typedef Wrapper 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 , 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 &holder) + { + } + + FORCEINLINE DacHolder(const Holder &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 + static inline + typename std::enable_if< std::is_pointer::value, ItfT >::type + SafeAddRef(ItfT pItf); + + template < typename ItfT > __checkReturn + ItfT * + SafeAddRef(ReleaseHolder & 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 + char IsHolderHelper(HolderBase*); + int IsHolderHelper(...); + + template + struct IsHolder : public std::conditional< + sizeof(IsHolderHelper(reinterpret_cast(NULL))) == sizeof(char), + std::true_type, + std::false_type>::type + {}; + } + + template < typename T > + typename std::enable_if::value, ULONG>::type + SafeRelease(T& arg) + { + STATIC_CONTRACT_LIMITED_METHOD; + return arg.Release(); + } +} + +#endif // __HOLDER_H_ diff --git a/src/inc/iterator.h b/src/inc/iterator.h new file mode 100644 index 000000000..b7bb14266 --- /dev/null +++ b/src/inc/iterator.h @@ -0,0 +1,640 @@ +// 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 +class ScannerPrototype +{ + public: + + typedef ScannerPrototype 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 +class EnumeratorPrototype +{ + public: + + typedef EnumeratorPrototype Iterator; + + ELEMENT &operator*(); + ELEMENT *operator->(); + + Iterator &operator++(); + Iterator operator++(int); + + bool operator==(const Iterator &i); + bool operator!=(const Iterator &i); +}; + +template +class IndexerPrototype +{ + public: + typedef IndexerPrototype 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 +class EnumerablePrototype +{ + typedef EnumeratorPrototype Iterator; + + Iterator Begin(); + Iterator End(); +}; + +template +class ScannablePrototype +{ + typedef ScannerPrototype Iterator; + + Iterator Begin(); + Iterator End(); +}; + +template +class IndexablePrototype +{ + typedef IndexerPrototype 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 +class EnumeratorBasePrototype +{ + protected: + EnumeratorBasePrototype(CONTAINER *container, BOOL begin); + ELEMENT &Get() const; + void Next(); + BOOL Equal(const EnumeratorBasePrototype &i) const; + CHECK DoCheck() const; +}; + +template +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 +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 +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 +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 +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 +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_ diff --git a/src/inc/mdcommon.h b/src/inc/mdcommon.h new file mode 100644 index 000000000..eeeb32fc7 --- /dev/null +++ b/src/inc/mdcommon.h @@ -0,0 +1,58 @@ +// 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__ diff --git a/src/inc/memoryrange.h b/src/inc/memoryrange.h new file mode 100644 index 000000000..ae1d440f6 --- /dev/null +++ b/src/inc/memoryrange.h @@ -0,0 +1,97 @@ +// 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(pAddress) - dac_cast(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(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 + diff --git a/src/inc/metadata.h b/src/inc/metadata.h new file mode 100644 index 000000000..cf69f3a90 --- /dev/null +++ b/src/inc/metadata.h @@ -0,0 +1,1531 @@ +// 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 +{ +}; + + +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_ diff --git a/src/inc/nibblemapmacros.h b/src/inc/nibblemapmacros.h new file mode 100644 index 000000000..9554b5d1d --- /dev/null +++ b/src/inc/nibblemapmacros.h @@ -0,0 +1,55 @@ +// 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_ diff --git a/src/inc/nsutilpriv.h b/src/inc/nsutilpriv.h new file mode 100644 index 000000000..5ea9d0eba --- /dev/null +++ b/src/inc/nsutilpriv.h @@ -0,0 +1,265 @@ +// 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 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 &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 diff --git a/src/inc/ostype.h b/src/inc/ostype.h new file mode 100644 index 000000000..ddb0ec5d0 --- /dev/null +++ b/src/inc/ostype.h @@ -0,0 +1,100 @@ +// 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 diff --git a/src/inc/pedecoder.h b/src/inc/pedecoder.h new file mode 100644 index 000000000..bb4ea867d --- /dev/null +++ b/src/inc/pedecoder.h @@ -0,0 +1,429 @@ +// 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 +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_ diff --git a/src/inc/pedecoder.inl b/src/inc/pedecoder.inl new file mode 100644 index 000000000..44339248c --- /dev/null +++ b/src/inc/pedecoder.inl @@ -0,0 +1,1234 @@ +// 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(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(FindNTHeaders()); +} + +inline IMAGE_NT_HEADERS64 *PEDecoder::GetNTHeaders64() const +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(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( + dac_cast(GetNTHeaders32()) + + offsetof(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory) + + entry * sizeof(IMAGE_DATA_DIRECTORY)); + else + RETURN dac_cast( + dac_cast(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(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(this)->m_pCorHeader = + dac_cast(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( + dac_cast(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(rangeBase), rangeSize)); + CHECK(dac_cast(pointer) >= dac_cast(rangeBase)); + CHECK(dac_cast(pointer) <= dac_cast(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(pointer) >= dac_cast(rangeBase)); + CHECK(dac_cast(pointer) + size <= dac_cast(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_ diff --git a/src/inc/readytorun.h b/src/inc/readytorun.h new file mode 100644 index 000000000..25bd45d37 --- /dev/null +++ b/src/inc/readytorun.h @@ -0,0 +1,429 @@ +// 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__ diff --git a/src/inc/readytoruninstructionset.h b/src/inc/readytoruninstructionset.h new file mode 100644 index 000000000..1b66c6e52 --- /dev/null +++ b/src/inc/readytoruninstructionset.h @@ -0,0 +1,40 @@ +// 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 diff --git a/src/inc/registrywrapper.h b/src/inc/registrywrapper.h new file mode 100644 index 000000000..8504d9f82 --- /dev/null +++ b/src/inc/registrywrapper.h @@ -0,0 +1,19 @@ +// 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 diff --git a/src/inc/resource.h b/src/inc/resource.h new file mode 100644 index 000000000..7a8f14845 --- /dev/null +++ b/src/inc/resource.h @@ -0,0 +1,595 @@ +// 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 diff --git a/src/inc/safewrap.h b/src/inc/safewrap.h new file mode 100644 index 000000000..c2f260a4b --- /dev/null +++ b/src/inc/safewrap.h @@ -0,0 +1,168 @@ +// 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_ diff --git a/src/inc/sbuffer.h b/src/inc/sbuffer.h new file mode 100644 index 000000000..05c75faa5 --- /dev/null +++ b/src/inc/sbuffer.h @@ -0,0 +1,574 @@ +// 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((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; + + class EMPTY_BASES_DECL Index : public CheckedIteratorBase + { + friend class SBuffer; + + friend class CIterator; + friend class Indexer; + + friend class Iterator; + friend class Indexer; + + 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 + { + friend class SBuffer; + + public: + CIterator() + { + } + + CIterator(const SBuffer *buffer, int index) + : Index(const_cast(buffer), index) + { + } + }; + + class EMPTY_BASES_DECL Iterator : public Index, public Indexer + { + 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 +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 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 + +#endif // _SBUFFER_H_ diff --git a/src/inc/sbuffer.inl b/src/inc/sbuffer.inl new file mode 100644 index 000000000..3da80bcc7 --- /dev/null +++ b/src/inc/sbuffer.inl @@ -0,0 +1,1701 @@ +// 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(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(this)->ReallocateBuffer(allocation, PRESERVE); + + RETURN; +} + +inline void SBuffer::Trim() const +{ + CONTRACT_VOID + { + INSTANCE_CHECK; + THROWS; + GC_NOTRIGGER; + } + CONTRACT_END; + + if (!IsImmutable()) + const_cast(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(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 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(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(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(this)->CheckedIteratorBase::Resync(const_cast(buffer)); + const_cast(this)->m_ptr = value; + + RETURN; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // _SBUFFER_INL_ diff --git a/src/inc/securityutil.h b/src/inc/securityutil.h new file mode 100644 index 000000000..1cda814eb --- /dev/null +++ b/src/inc/securityutil.h @@ -0,0 +1,52 @@ +// 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 diff --git a/src/inc/securitywrapper.h b/src/inc/securitywrapper.h new file mode 100644 index 000000000..63d7e1743 --- /dev/null +++ b/src/inc/securitywrapper.h @@ -0,0 +1,102 @@ +// 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 diff --git a/src/inc/sigparser.h b/src/inc/sigparser.h index 82430e6f0..e7fb503c6 100644 --- a/src/inc/sigparser.h +++ b/src/inc/sigparser.h @@ -9,16 +9,22 @@ #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 diff --git a/src/inc/sstring.h b/src/inc/sstring.h new file mode 100644 index 000000000..c2c3b9c7d --- /dev/null +++ b/src/inc/sstring.h @@ -0,0 +1,1033 @@ +// 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; + + 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 + { + 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; + friend class Indexer; + + 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 + { + 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(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 + { + 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 + 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 +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 &operator= (const SString &s) + { + WRAPPER_NO_CONTRACT; + Set(s); + return *this; + } + + FORCEINLINE InlineSString &operator= (const InlineSString &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 +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 + +#endif // _SSTRING_H_ diff --git a/src/inc/sstring.inl b/src/inc/sstring.inl new file mode 100644 index 000000000..03fc26fe9 --- /dev/null +++ b/src/inc/sstring.inl @@ -0,0 +1,2228 @@ +// 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<= 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(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(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(this)->SBuffer::SetFlag3(); +} + +//---------------------------------------------------------------------------- +// Clear normalization +//---------------------------------------------------------------------------- +inline void SString::ClearNormalized() const +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC_HOST_ONLY; + + const_cast(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 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(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<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 CHECK SString::Index::DoCheck(SCOUNT_T delta) const +{ + CANNOT_HAVE_CONTRACT; +#if _DEBUG + const SString *string = (const SString *) GetContainerDebug(); + + CHECK(m_ptr + (delta<= string->m_buffer); + CHECK(m_ptr + (delta<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(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_ diff --git a/src/inc/stacktrace.h b/src/inc/stacktrace.h deleted file mode 100644 index b843eee74..000000000 --- a/src/inc/stacktrace.h +++ /dev/null @@ -1,90 +0,0 @@ -// 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 - -// -//--- 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
: ! + 0x\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
: ! + 0x\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
: ! + 0x -******************************************************************** 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
: ! + 0x -******************************************************************** 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 diff --git a/src/inc/stdmacros.h b/src/inc/stdmacros.h new file mode 100644 index 000000000..2d0a05761 --- /dev/null +++ b/src/inc/stdmacros.h @@ -0,0 +1,347 @@ +// 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<= val ); // check for overflow + return result; +} + +template 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) ? "" : 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_ diff --git a/src/inc/stresslog.h b/src/inc/stresslog.h index c9cb99bc7..cd6f10abb 100644 --- a/src/inc/stresslog.h +++ b/src/inc/stresslog.h @@ -30,11 +30,12 @@ #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 // offsetof #else //STRESS_LOG_READONLY @@ -44,6 +45,9 @@ #ifndef _ASSERTE #define _ASSERTE(expr) #endif +#else +#include // 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 @@ -508,6 +512,21 @@ typedef USHORT static StressLog theLog; // We only have one log, and this is it }; +#ifndef STRESS_LOG_ANALYZER +typedef Holder> 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 */ diff --git a/src/inc/switches.h b/src/inc/switches.h index 65cda94ed..01b5c0dff 100644 --- a/src/inc/switches.h +++ b/src/inc/switches.h @@ -15,10 +15,6 @@ // 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 @@ -27,22 +23,6 @@ #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 diff --git a/src/inc/utilcode.h b/src/inc/utilcode.h index 04addc4f9..7c359ef97 100644 --- a/src/inc/utilcode.h +++ b/src/inc/utilcode.h @@ -1,8 +1,7 @@ // 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. // @@ -11,6 +10,4541 @@ #ifndef __UtilCode_h__ #define __UtilCode_h__ +#include "crtwrap.h" +#include "winwrap.h" +#include +#include +#include +#include +#include +#include +#include "clrtypes.h" +#include "safewrap.h" +#include "volatile.h" +#include +#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 +#else +#include "clr_std/type_traits" +#endif + +#include "contract.h" +#include "entrypoints.h" + #include +#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 +inline ResultType DataPointerToThumbCode(SourceType pCode) +{ + return (ResultType)(((UINT_PTR)pCode) | THUMB_CODE); +} + +template +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(pc); +#else + return dac_cast(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(addr); +#else + return dac_cast(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 // for offsetof +#include + +#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: All HRESULT_FROM_WIN32(GetLastError()) should be replaced by calls to +// this helper function avoid code bloat +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(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(pcBits), iBit, bOn); +} +#endif + +template +class SimpleListNode +{ +public: + SimpleListNode(const T& _t) + { + data = _t; + next = 0; + } + + T data; + SimpleListNode* next; +}; + +template +class SimpleList +{ +public: + typedef SimpleListNode NodeType; + + SimpleList() + { + 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 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(m_iCount)); + return static_cast(m_iCount); + } + +private: + T *Grow(); + T *GrowNoThrow(); +}; + + +#ifndef DACCESS_COMPILE + +//***************************************************************************** +// Increase the size of the array. +//***************************************************************************** +template +T *CUnorderedArrayWithAllocator::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 +T *CUnorderedArrayWithAllocator::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 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 CUnorderedArray : public CUnorderedArrayWithAllocator > +{ +public: + + CUnorderedArray () + { + LIMITED_METHOD_CONTRACT; + } +}; + + +//Used by the debugger. Included here in hopes somebody else might, too +typedef CUnorderedArray 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 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 INTARRAY; +typedef CDynArray SHORTARRAY; +typedef CDynArray LONGARRAY; +typedef CDynArray USHORTARRAY; +typedef CDynArray ULONGARRAY; +typedef CDynArray BYTEARRAY; +typedef CDynArray TOKENARRAY; + +template 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 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 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 +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 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(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 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 +HRESULT CHashTableAndData::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 ::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 +void CHashTableAndData::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 +int CHashTableAndData::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(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 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); +//***************************************************************************** +templateclass CClosedHashEx : public CClosedHash +{ +public: + CClosedHashEx( + int iBuckets, // How many buckets should we start with. + bool bPerfect=false) : // true if bucket size will hash with no collisions. + CClosedHash (iBuckets, bPerfect) + { + WRAPPER_NO_CONTRACT; + } + + unsigned int Hash(const void *pData) + { + WRAPPER_NO_CONTRACT; + return static_cast(this)->Hash((const T*)pData); + } + + unsigned int Compare(const void *p1, BYTE *p2) + { + WRAPPER_NO_CONTRACT; + return static_cast(this)->Compare((const T*)p1, (T*)p2); + } + + typename CClosedHash::ELEMENTSTATUS Status(BYTE *p) + { + WRAPPER_NO_CONTRACT; + return static_cast(this)->Status((T*)p); + } + + void SetStatus(BYTE *p, typename CClosedHash::ELEMENTSTATUS s) + { + WRAPPER_NO_CONTRACT; + static_cast(this)->SetStatus((T*)p, s); + } + + void* GetKey(BYTE *p) + { + WRAPPER_NO_CONTRACT; + return static_cast(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 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; iiNext = 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; iiNext = 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. +//***************************************************************************** +HRESULT Utf2Quick( + LPCUTF8 pStr, // The string to convert. + CQuickArray &rStr, // The QuickArray 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 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 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 + struct InterlockedCompareExchangeHelper; + + template + struct InterlockedCompareExchangeHelper + { + static inline T InterlockedExchange( + T volatile * target, + T value) + { + static_assert_no_msg(sizeof(T) == sizeof(LONG)); + LONG res = ::InterlockedExchange( + reinterpret_cast(target), + *reinterpret_cast(/*::operator*/&(value))); + return *reinterpret_cast(&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(destination), + *reinterpret_cast(/*::operator*/&(exchange)), + *reinterpret_cast(/*::operator*/&(comparand))); + return *reinterpret_cast(&res); + } + }; + + template + struct InterlockedCompareExchangeHelper + { + static inline T InterlockedExchange( + T volatile * target, + T value) + { + static_assert_no_msg(sizeof(T) == sizeof(LONGLONG)); + LONGLONG res = ::InterlockedExchange64( + reinterpret_cast(target), + *reinterpret_cast(/*::operator*/&(value))); + return *reinterpret_cast(&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(destination), + *reinterpret_cast(/*::operator*/&(exchange)), + *reinterpret_cast(/*::operator*/&(comparand))); + return *reinterpret_cast(&res); + } + }; +} + +template +inline T InterlockedExchangeT( + T volatile * target, + T value) +{ + return ::UtilCode::InterlockedCompareExchangeHelper::InterlockedExchange( + target, value); +} + +template +inline T InterlockedCompareExchangeT( + T volatile * destination, + T exchange, + T comparand) +{ + return ::UtilCode::InterlockedCompareExchangeHelper::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 +inline T* InterlockedExchangeT( + T* volatile * target, + T* value) +{ + //STATIC_ASSERT(value == 0); + typedef typename std::remove_const::type * non_const_ptr_t; + return reinterpret_cast(InterlockedExchangePointer( + reinterpret_cast(const_cast(target)), + reinterpret_cast(const_cast(value)))); +} + +template +inline T* InterlockedCompareExchangeT( + T* volatile * destination, + T* exchange, + T* comparand) +{ + //STATIC_ASSERT(exchange == 0); + typedef typename std::remove_const::type * non_const_ptr_t; + return reinterpret_cast(InterlockedCompareExchangePointer( + reinterpret_cast(const_cast(destination)), + reinterpret_cast(const_cast(exchange)), + reinterpret_cast(const_cast(comparand)))); +} + +// NULL pointer variants of the above to avoid having to cast NULL +// to the appropriate pointer type. +template +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(value)); +} + +template +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(exchange), comparand); +} + +template +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(comparand)); +} + +// NULL pointer variants of the above to avoid having to cast NULL +// to the appropriate pointer type. +template +inline T* InterlockedExchangeT( + T* volatile * target, + int value) // When NULL is provided as argument. +{ + //STATIC_ASSERT(value == 0); + return InterlockedExchangeT(target, nullptr); +} + +template +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 +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 + static inline + typename std::enable_if< std::is_pointer::value, ItfT >::type + SafeAddRef(ItfT pItf) + { + STATIC_CONTRACT_LIMITED_METHOD; + if (pItf != nullptr) + { + pItf->AddRef(); + } + return pItf; + } + + //================================================================================================================= + template + typename std::enable_if< std::is_pointer::value && std::is_reference::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 std::enable_if< std::is_pointer::value && !std::is_reference::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 + static inline + typename std::enable_if< std::is_pointer::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__ diff --git a/src/inc/volatile.h b/src/inc/volatile.h index 610a408f3..e127fe25f 100644 --- a/src/inc/volatile.h +++ b/src/inc/volatile.h @@ -73,7 +73,10 @@ #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 diff --git a/src/inc/win64unwind.h b/src/inc/win64unwind.h new file mode 100644 index 000000000..d9990a351 --- /dev/null +++ b/src/inc/win64unwind.h @@ -0,0 +1,125 @@ +// 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_ diff --git a/src/inc/winwrap.h b/src/inc/winwrap.h new file mode 100644 index 000000000..3bf11baea --- /dev/null +++ b/src/inc/winwrap.h @@ -0,0 +1,491 @@ +// 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 +#include +#include +#include + +#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(""); + if (lpCaption == NULL) + lpCaption = W(""); + 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 = ""; + if (lpCaption == NULL) + lpCaption = ""; + + 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__ diff --git a/src/inc/yieldprocessornormalized.h b/src/inc/yieldprocessornormalized.h new file mode 100644 index 000000000..121e60b03 --- /dev/null +++ b/src/inc/yieldprocessornormalized.h @@ -0,0 +1,295 @@ +// 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 diff --git a/src/pal/CMakeLists.txt b/src/pal/CMakeLists.txt index ef4ff06d5..59f43049a 100644 --- a/src/pal/CMakeLists.txt +++ b/src/pal/CMakeLists.txt @@ -6,4 +6,7 @@ include_directories(${COREPAL_SOURCE_DIR}/src) add_compile_options(-fexceptions) add_definitions(-DUSE_STL) +remove_definitions(-DUNICODE) +remove_definitions(-D_UNICODE) + add_subdirectory(src) diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index 6c5fa1101..43dfd6a2a 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -343,28 +343,70 @@ typedef __int64 time_t; #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 @@ -882,6 +924,10 @@ DWORD PALAPI GetCurrentSessionId(); +PALIMPORT +HANDLE +PALAPI +GetCurrentProcess(); PALIMPORT DWORD @@ -925,6 +971,55 @@ typedef struct _PROCESS_INFORMATION { 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 @@ -2256,6 +2351,15 @@ typedef struct _RUNTIME_FUNCTION { #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 @@ -2989,7 +3093,6 @@ GetSystemInfo( #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 @@ -3182,7 +3285,6 @@ PALIMPORT DLLEXPORT const WCHAR * __cdecl PAL_wcschr(const WCHAR *, WCHAR); 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); diff --git a/src/pal/inc/unixasmmacros.inc b/src/pal/inc/unixasmmacros.inc index 1fe285eef..a1bad6300 100644 --- a/src/pal/inc/unixasmmacros.inc +++ b/src/pal/inc/unixasmmacros.inc @@ -39,6 +39,8 @@ #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) diff --git a/src/pal/inc/unixasmmacrosarm.inc b/src/pal/inc/unixasmmacrosarm.inc index e0c0016cc..d323ccc49 100644 --- a/src/pal/inc/unixasmmacrosarm.inc +++ b/src/pal/inc/unixasmmacrosarm.inc @@ -197,7 +197,11 @@ C_FUNC(\Name\()_End): .endm .macro EMIT_BREAKPOINT +#ifdef __armv6__ + .inst 0xe7f001f0 +#else .inst.w 0xde01 +#endif .endm .macro PROLOG_PUSH RegList diff --git a/src/pal/prebuilt/inc/buildnumber.h b/src/pal/prebuilt/inc/buildnumber.h deleted file mode 100644 index 5aee76ab5..000000000 --- a/src/pal/prebuilt/inc/buildnumber.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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" diff --git a/src/pal/prebuilt/inc/clrprivbinding.h b/src/pal/prebuilt/inc/clrprivbinding.h deleted file mode 100644 index 3c810676a..000000000 --- a/src/pal/prebuilt/inc/clrprivbinding.h +++ /dev/null @@ -1,305 +0,0 @@ -// 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 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 -#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 - - diff --git a/src/pal/prebuilt/inc/mscorsvc.h b/src/pal/prebuilt/inc/mscorsvc.h deleted file mode 100644 index a00db395e..000000000 --- a/src/pal/prebuilt/inc/mscorsvc.h +++ /dev/null @@ -1,2823 +0,0 @@ -// 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 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 -#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 - - diff --git a/src/pal/src/CMakeLists.txt b/src/pal/src/CMakeLists.txt index 3f7278de3..8eccc0706 100644 --- a/src/pal/src/CMakeLists.txt +++ b/src/pal/src/CMakeLists.txt @@ -33,6 +33,9 @@ if(CLR_CMAKE_HOST_ARCH_AMD64) 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) @@ -101,7 +104,6 @@ set(SOURCES cruntime/string.cpp cruntime/stringtls.cpp cruntime/wchar.cpp - cruntime/wchartls.cpp debug/debug.cpp file/directory.cpp file/file.cpp @@ -113,14 +115,12 @@ set(SOURCES 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 @@ -167,7 +167,13 @@ set(SOURCES 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 diff --git a/src/pal/src/cruntime/printfcpp.cpp b/src/pal/src/cruntime/printfcpp.cpp index d7e992c09..fee79c91d 100644 --- a/src/pal/src/cruntime/printfcpp.cpp +++ b/src/pal/src/cruntime/printfcpp.cpp @@ -86,7 +86,7 @@ static int Internal_Convertfwrite(CPalThread *pthrCurrent, const void *buffer, s 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); diff --git a/src/pal/src/cruntime/wchar.cpp b/src/pal/src/cruntime/wchar.cpp index c2d425a48..6a404217d 100644 --- a/src/pal/src/cruntime/wchar.cpp +++ b/src/pal/src/cruntime/wchar.cpp @@ -3,8 +3,6 @@ /*++ - - Module Name: wchar.c @@ -13,11 +11,8 @@ Abstract: Implementation of wide char string functions. - - --*/ - #include "pal/palinternal.h" #include "pal/cruntime.h" #include "pal/dbgmsg.h" @@ -25,7 +20,6 @@ Abstract: #include "pal/thread.hpp" #include "pal/threadsusp.hpp" - #if HAVE_CONFIG_H #include "config.h" #endif diff --git a/src/pal/src/cruntime/wchartls.cpp b/src/pal/src/cruntime/wchartls.cpp deleted file mode 100644 index 35b733598..000000000 --- a/src/pal/src/cruntime/wchartls.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// 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); -} - diff --git a/src/pal/src/include/pal/corunix.hpp b/src/pal/src/include/pal/corunix.hpp index 6c5a052ec..79bdf0260 100644 --- a/src/pal/src/include/pal/corunix.hpp +++ b/src/pal/src/include/pal/corunix.hpp @@ -183,17 +183,10 @@ namespace CorUnix 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 }; @@ -1097,6 +1090,148 @@ namespace CorUnix }; 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 diff --git a/src/pal/src/include/pal/modulename.h b/src/pal/src/include/pal/modulename.h deleted file mode 100644 index 87d54b77a..000000000 --- a/src/pal/src/include/pal/modulename.h +++ /dev/null @@ -1,35 +0,0 @@ -// 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_*/ diff --git a/src/pal/src/include/pal/mutex.hpp b/src/pal/src/include/pal/mutex.hpp new file mode 100644 index 000000000..bd6dde17d --- /dev/null +++ b/src/pal/src/include/pal/mutex.hpp @@ -0,0 +1,37 @@ +// 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 + +#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_ diff --git a/src/pal/src/include/pal/process.h b/src/pal/src/include/pal/process.h index e5a6f9f5a..f023f840a 100644 --- a/src/pal/src/include/pal/process.h +++ b/src/pal/src/include/pal/process.h @@ -35,11 +35,58 @@ extern "C" calls to CreateThread from succeeding once shutdown has started [defined in process.c] */ +extern Volatile 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() diff --git a/src/pal/src/include/pal/procobj.hpp b/src/pal/src/include/pal/procobj.hpp new file mode 100644 index 000000000..32357c40b --- /dev/null +++ b/src/pal/src/include/pal/procobj.hpp @@ -0,0 +1,123 @@ +// 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_ + diff --git a/src/pal/src/include/pal/synchcache.hpp b/src/pal/src/include/pal/synchcache.hpp new file mode 100644 index 000000000..821f5096a --- /dev/null +++ b/src/pal/src/include/pal/synchcache.hpp @@ -0,0 +1,396 @@ +// 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 class CSynchCache + { + typedef union _USynchCacheStackNode + { + union _USynchCacheStackNode * next; + BYTE objraw[sizeof(T)]; + } USynchCacheStackNode; + + static const int MaxDepth = 256; + + Volatile m_pHead; + CRITICAL_SECTION m_cs; + Volatile 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(); + if (NULL == pvObjRaw) + break; +#ifdef _DEBUG + memset(pvObjRaw, 0, sizeof(USynchCacheStackNode)); +#endif + ppObjs[j] = reinterpret_cast(pvObjRaw); + } + + for (i=0;i(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 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 m_pHead; + CRITICAL_SECTION m_cs; + Volatile 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(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(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_ + diff --git a/src/pal/src/include/pal/synchobjects.hpp b/src/pal/src/include/pal/synchobjects.hpp new file mode 100644 index 000000000..bf53413a0 --- /dev/null +++ b/src/pal/src/include/pal/synchobjects.hpp @@ -0,0 +1,183 @@ +// 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 + +#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 m_lLocalSynchLockCount; + Volatile 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_ + diff --git a/src/pal/src/include/pal/thread.hpp b/src/pal/src/include/pal/thread.hpp index f335b8667..735e060a8 100644 --- a/src/pal/src/include/pal/thread.hpp +++ b/src/pal/src/include/pal/thread.hpp @@ -28,6 +28,7 @@ Abstract: #include "threadsusp.hpp" #include "threadinfo.hpp" +#include "synchobjects.hpp" #include namespace CorUnix @@ -39,11 +40,53 @@ 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. */ @@ -69,15 +112,48 @@ namespace CorUnix 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; @@ -96,6 +172,13 @@ namespace CorUnix 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 // @@ -104,22 +187,76 @@ namespace CorUnix 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) { }; @@ -140,6 +277,24 @@ namespace CorUnix 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 @@ -156,6 +311,35 @@ namespace CorUnix 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 @@ -174,6 +358,24 @@ namespace CorUnix 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 @@ -198,6 +400,53 @@ namespace CorUnix 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 @@ -239,6 +488,37 @@ namespace CorUnix 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 diff --git a/src/pal/src/include/pal/threadsusp.hpp b/src/pal/src/include/pal/threadsusp.hpp index 98b8559e9..0c84a5183 100644 --- a/src/pal/src/include/pal/threadsusp.hpp +++ b/src/pal/src/include/pal/threadsusp.hpp @@ -22,13 +22,281 @@ Abstract: // 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 -#include "pal/sharedmemory.h" +#include +#include +// 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 +#elif HAVE_SEMAPHORE_H +#include +#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 + +#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 +#include + +#else +#error "Don't know how to synchronize thread suspends and resumes on this platform" +#endif // HAS_POSIX_SEMAPHORES + +#include + +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 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 diff --git a/src/pal/src/include/pal/utils.h b/src/pal/src/include/pal/utils.h index bb9e0fe61..83cf2b104 100644 --- a/src/pal/src/include/pal/utils.h +++ b/src/pal/src/include/pal/utils.h @@ -24,7 +24,6 @@ Abstract: #include #include #include -#include //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Alignment helpers (copied for PAL use from stdmacros.h) diff --git a/src/pal/src/init/pal.cpp b/src/pal/src/init/pal.cpp index 21f36c04b..7f1127027 100644 --- a/src/pal/src/init/pal.cpp +++ b/src/pal/src/init/pal.cpp @@ -17,13 +17,17 @@ Abstract: 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" @@ -91,6 +95,7 @@ using namespace CorUnix; extern "C" BOOL CRTInitStdStreams( void ); Volatile init_count = 0; +Volatile shutdown_intent = 0; static BOOL g_fThreadDataAvailable = FALSE; static pthread_mutex_t init_critsec_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -107,16 +112,6 @@ static PCRITICAL_SECTION init_critsec = NULL; 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() { @@ -132,12 +127,15 @@ 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 @@ -146,35 +144,32 @@ Return: --*/ 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: @@ -187,20 +182,6 @@ Abstract: 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) { @@ -301,14 +282,6 @@ Initialize( 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()) { @@ -324,7 +297,25 @@ Initialize( // 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 // @@ -332,9 +323,11 @@ Initialize( 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 // @@ -372,6 +365,19 @@ Initialize( } 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 { @@ -382,6 +388,16 @@ Initialize( 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. */ @@ -401,6 +417,19 @@ Initialize( 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 */ @@ -442,12 +471,21 @@ Initialize( 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: @@ -596,6 +634,50 @@ BOOL PALIsThreadDataInitialized() 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 @@ -687,60 +769,3 @@ static BOOL INIT_IncreaseDescriptorLimit(void) #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; -} - diff --git a/src/pal/src/init/sxs.cpp b/src/pal/src/init/sxs.cpp index 0edc8a98a..95daa7cb3 100644 --- a/src/pal/src/init/sxs.cpp +++ b/src/pal/src/init/sxs.cpp @@ -11,7 +11,7 @@ #include "pal/dbgmsg.h" #include "pal/thread.hpp" -#include "pal/init.h" +#include "../thread/procprivate.hpp" #include "pal/module.h" #include "pal/process.h" @@ -62,6 +62,22 @@ AllocatePalThread(CPalThread **ppThread) 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; diff --git a/src/pal/src/loader/module.cpp b/src/pal/src/loader/module.cpp index 832503277..d2c4c7b69 100644 --- a/src/pal/src/loader/module.cpp +++ b/src/pal/src/loader/module.cpp @@ -30,7 +30,6 @@ SET_DEFAULT_DEBUG_CHANNEL(LOADER); // some headers have code with asserts, so do #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" @@ -340,9 +339,10 @@ GetProcAddress( /* 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) { @@ -354,6 +354,10 @@ GetProcAddress( module, libName); } } + else + { + TRACE("GetProcAddress: dladdr() call failed!\n"); + } } } else @@ -874,11 +878,17 @@ void LOADCallDllMain(DWORD dwReason, LPVOID lpReserved) 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: diff --git a/src/pal/src/loader/modulename.cpp b/src/pal/src/loader/modulename.cpp deleted file mode 100644 index 40c1c6de5..000000000 --- a/src/pal/src/loader/modulename.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// 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 - -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; - } -} - diff --git a/src/pal/src/memory/local.cpp b/src/pal/src/memory/local.cpp deleted file mode 100644 index fc62ef428..000000000 --- a/src/pal/src/memory/local.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// 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; -} diff --git a/src/pal/src/misc/fmtmessage.cpp b/src/pal/src/misc/fmtmessage.cpp index 81502bfc7..e9d87d19c 100644 --- a/src/pal/src/misc/fmtmessage.cpp +++ b/src/pal/src/misc/fmtmessage.cpp @@ -63,7 +63,7 @@ static LPWSTR FMTMSG_GetMessageString( DWORD dwErrCode ) 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) { @@ -135,7 +135,7 @@ static INT FMTMSG__watoi( LPWSTR str ) UINT NumOfBytes = 0; \ nSize *= 2; \ NumOfBytes = nSize * sizeof( WCHAR ); \ - lpTemp = static_cast( LocalAlloc( LMEM_FIXED, NumOfBytes ) ); \ + lpTemp = static_cast( PAL_malloc( NumOfBytes ) ); \ TRACE( "Growing the buffer.\n" );\ \ if ( !lpTemp ) \ @@ -149,7 +149,7 @@ static INT FMTMSG__watoi( LPWSTR str ) \ *lpWorkingString = '\0';\ PAL_wcscpy( lpTemp, lpReturnString );\ - LocalFree( lpReturnString ); \ + free( lpReturnString ); \ lpWorkingString = lpReturnString = lpTemp; \ lpWorkingString += nCount; \ } \ @@ -341,7 +341,7 @@ FormatMessageW( /* 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; } @@ -418,7 +418,7 @@ FormatMessageW( } lpWorkingString = static_cast( - LocalAlloc( LMEM_FIXED, nSize * sizeof( WCHAR ) ) ); + PAL_malloc( nSize * sizeof( WCHAR ) ) ); if ( !lpWorkingString ) { ERROR( "Unable to allocate memory for the working string.\n" ); @@ -675,14 +675,14 @@ exit: /* Function clean-up and exit. */ { 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 ); diff --git a/src/pal/src/objmgr/shmobject.cpp b/src/pal/src/objmgr/shmobject.cpp index de9b7cbd6..35cfc76f3 100644 --- a/src/pal/src/objmgr/shmobject.cpp +++ b/src/pal/src/objmgr/shmobject.cpp @@ -989,3 +989,240 @@ CSharedMemoryObject::GetObjectSynchData( 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; +} + diff --git a/src/pal/src/objmgr/shmobject.hpp b/src/pal/src/objmgr/shmobject.hpp index fe60f5b26..f50d10e0b 100644 --- a/src/pal/src/objmgr/shmobject.hpp +++ b/src/pal/src/objmgr/shmobject.hpp @@ -295,6 +295,79 @@ namespace CorUnix ); }; + + class CSharedMemoryWaitableObject : public CSharedMemoryObject + { + template 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 diff --git a/src/pal/src/objmgr/shmobjectmanager.cpp b/src/pal/src/objmgr/shmobjectmanager.cpp index 166dcb742..731614180 100644 --- a/src/pal/src/objmgr/shmobjectmanager.cpp +++ b/src/pal/src/objmgr/shmobjectmanager.cpp @@ -1,6 +1,5 @@ // 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. /*++ @@ -21,6 +20,7 @@ Abstract: #include "shmobject.hpp" #include "pal/cs.hpp" #include "pal/thread.hpp" +#include "pal/procobj.hpp" #include "pal/dbgmsg.h" SET_DEFAULT_DEBUG_CHANNEL(PAL); @@ -162,7 +162,7 @@ CSharedMemoryObjectManager::AllocateObject( if (CObjectType::WaitableObject == pot->GetSynchronizationSupport()) { - _ASSERTE(FALSE); + pshmobj = InternalNew(pot, &m_csListLock); } else { @@ -1001,7 +1001,7 @@ CSharedMemoryObjectManager::ImportSharedObjectIntoProcess( ) { PAL_ERROR palError = NO_ERROR; - CSharedMemoryObject *pshmobj = NULL; + CSharedMemoryObject *pshmobj; PLIST_ENTRY pleObjectList; _ASSERTE(NULL != pthr); @@ -1025,7 +1025,11 @@ CSharedMemoryObjectManager::ImportSharedObjectIntoProcess( if (CObjectType::WaitableObject == pot->GetSynchronizationSupport()) { - _ASSERTE(FALSE); + pshmobj = InternalNew(pot, + &m_csListLock, + shmSharedObjectData, + psmod, + fAddRefSharedData); } else { @@ -1073,14 +1077,6 @@ ImportSharedObjectIntoProcessExit: return palError; } -static PalObjectTypeId RemotableObjectTypes[] = - {otiManualResetEvent, otiAutoResetEvent, otiMutex, otiProcess}; - -static CAllowedObjectTypes aotRemotable( - RemotableObjectTypes, - sizeof(RemotableObjectTypes) / sizeof(RemotableObjectTypes[0]) - ); - /*++ Function: CheckObjectTypeAndRights diff --git a/src/pal/src/synchmgr/synchcontrollers.cpp b/src/pal/src/synchmgr/synchcontrollers.cpp new file mode 100644 index 000000000..22bba4aaf --- /dev/null +++ b/src/pal/src/synchmgr/synchcontrollers.cpp @@ -0,0 +1,2042 @@ +// 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 +#include +#include +#include +#include +#include +#include + +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(this) is + // equal to this-sizeof(void*), given that CSynchWaitController + // has a virtual table, while CSynchControllerBase doesn't. + pSynchManager->CacheAddWaitCtrlr(m_pthrOwner, + static_cast(this)); + } + else + { + // The cast here must be static_cast and not reinterpet_cast + pSynchManager->CacheAddStateCtrlr(m_pthrOwner, + static_cast(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 +} + diff --git a/src/pal/src/synchmgr/synchmanager.cpp b/src/pal/src/synchmgr/synchmanager.cpp new file mode 100644 index 000000000..b359c2f73 --- /dev/null +++ b/src/pal/src/synchmgr/synchmanager.cpp @@ -0,0 +1,4150 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_POLL +#include +#else +#include "pal/fakepoll.h" +#endif // HAVE_POLL + +#include + +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 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; uIdxGetObjectDomain()) + { + ++uSharedObjectCount; + } + + if (uSharedObjectCount > 0 && uSharedObjectCount <= uIdx) + { + wdWaitDomain = MixedWait; + break; + } + } + + if (dwObjectCount == uSharedObjectCount) + { + wdWaitDomain = SharedWait; + } + + for (uIdx=0;uIdxGetObjectDomain(); + + palErr = rgObjects[uIdx]->GetObjectSynchData((void **)&pvSData); + if (NO_ERROR != palErr) + { + break; + } + + psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer( + CSynchData, reinterpret_cast(pvSData)) : + static_cast(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( + static_cast(Ctrlrs.pWaitCtrlrs[uIdx])); + } + } + else + { + for (uIdx=0;uIdx( + static_cast(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; uIdxRelease(); + } + + // Return to the cache not yet initialized wait controllers + for (uIdx=uErrCleanupIdxFirstNotInitializedCtrlr; uIdxRelease(); + } + + // Return to the cache not yet initialized state controllers + for (uIdx=uErrCleanupIdxFirstNotInitializedCtrlr; uIdxSetWTLHeadShrPtr(NULL); + psdSynchData->SetWTLTailShrPtr(NULL); + + // Store shared pointer to this object + psdSynchData->SetSharedThis(shridSynchData); + + *ppvSynchData = reinterpret_cast(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(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(pvSynchData)); + + if (NULL == psdSynchData) + { + ASSERT("Bad shared memory pointer\n"); + return; + } + } + else + { + psdSynchData = static_cast(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(pvSynchData)) + : static_cast(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(pvSynchData)) : + static_cast(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(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(); + 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(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(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(); + + 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(); + 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(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(ulcWaitingThreads); + if (NULL == rgshridWTLNodes) + { + palError = ERROR_OUTOFMEMORY; + goto POSD_exit; + } + + i = m_cacheSHRWTListNodes.Get( + pthrCurrent, + ulcWaitingThreads, + rgshridWTLNodes + ); + + if (static_cast(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(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(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; + } +} diff --git a/src/pal/src/synchmgr/synchmanager.hpp b/src/pal/src/synchmgr/synchmanager.hpp new file mode 100644 index 000000000..4338a6108 --- /dev/null +++ b/src/pal/src/synchmgr/synchmanager.hpp @@ -0,0 +1,986 @@ +// 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 +#include +#if HAVE_KQUEUE +#include +#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 friend T *CorUnix::InternalNew(); + + public: + // types + typedef CSynchCache CSynchWaitControllerCache; + typedef CSynchCache CSynchStateControllerCache; + typedef CSynchCache CSynchDataCache; + typedef CSHRSynchCache CSHRSynchDataCache; + typedef CSynchCache CWaitingThreadsListNodeCache; + typedef CSHRSynchCache CSHRWaitingThreadsListNodeCache; + typedef CSynchCache 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 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_ diff --git a/src/pal/src/synchmgr/wait.cpp b/src/pal/src/synchmgr/wait.cpp new file mode 100644 index 000000000..1b1206d8e --- /dev/null +++ b/src/pal/src/synchmgr/wait.cpp @@ -0,0 +1,360 @@ +// 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(nCount); + ppISyncWaitCtrlrs = InternalNewArray(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(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; +} diff --git a/src/pal/src/synchobj/mutex.cpp b/src/pal/src/synchobj/mutex.cpp new file mode 100644 index 000000000..42125228d --- /dev/null +++ b/src/pal/src/synchobj/mutex.cpp @@ -0,0 +1,56 @@ +// 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. +} diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp new file mode 100644 index 000000000..294c12fc6 --- /dev/null +++ b/src/pal/src/thread/process.cpp @@ -0,0 +1,3661 @@ +// 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 +#if HAVE_POLL +#include +#else +#include "pal/fakepoll.h" +#endif // HAVE_POLL + +#include +#include +#include +#include +#include +#if HAVE_PRCTL_H +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include // __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 +#include +#include +#include +#include +extern "C" +{ + #include +} +#endif // __APPLE__ + +#ifdef __NetBSD__ +#include +#include +#include +#include +#endif + +#ifdef __FreeBSD__ +#include +#include +#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 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(&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(&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(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 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//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(&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(&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//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(&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(&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 ( becomes ) +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() +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); + } +} + diff --git a/src/pal/src/thread/procprivate.hpp b/src/pal/src/thread/procprivate.hpp new file mode 100644 index 000000000..4f3af2724 --- /dev/null +++ b/src/pal/src/thread/procprivate.hpp @@ -0,0 +1,77 @@ +// 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_ + + diff --git a/src/pal/src/thread/thread.cpp b/src/pal/src/thread/thread.cpp index 47b6819b4..638058620 100644 --- a/src/pal/src/thread/thread.cpp +++ b/src/pal/src/thread/thread.cpp @@ -22,13 +22,16 @@ SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do #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 @@ -78,6 +81,46 @@ extern "C" int _lwp_self (); 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 @@ -105,6 +148,7 @@ static void InternalEndCurrentThreadWrapper(void *arg) will lock its own critical section */ LOADCallDllMain(DLL_THREAD_DETACH, NULL); + InternalEndCurrentThread(pThread); pthread_setspecific(thObjKey, NULL); } @@ -177,7 +221,546 @@ static void FreeTHREAD(CPalThread *pThread) 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(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(, ...), 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(, ...) is also currently not allowed, only sched_setaffinity(0, ...). + // pthread_setaffinity_np(pthread_self(), ...) seems to call sched_setaffinity(, ...) 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 @@ -251,6 +834,316 @@ CreateThreadDataExit: 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(&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(&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(&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 @@ -266,10 +1159,37 @@ CPalThread::RunPreCreateInitializers( 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) { @@ -297,6 +1217,17 @@ CPalThread::~CPalThread() { 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 @@ -332,6 +1263,25 @@ CPalThread::RunPostCreateInitializers( // Call the post-create initializers for embedded classes // + if (pthread_setspecific(thObjKey, reinterpret_cast(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) { @@ -343,3 +1293,146 @@ RunPostCreateInitializersExit: 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(&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; +} + diff --git a/src/pal/src/thread/threadsusp.cpp b/src/pal/src/thread/threadsusp.cpp new file mode 100644 index 000000000..6c8fc1e30 --- /dev/null +++ b/src/pal/src/thread/threadsusp.cpp @@ -0,0 +1,823 @@ +// 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 +#include +#include +#include +#include +#include +#include + +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(-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 diff --git a/src/palrt/CMakeLists.txt b/src/palrt/CMakeLists.txt index 4ca96bc45..68151fa2a 100644 --- a/src/palrt/CMakeLists.txt +++ b/src/palrt/CMakeLists.txt @@ -2,18 +2,6 @@ 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 diff --git a/src/utilcode/CMakeLists.txt b/src/utilcode/CMakeLists.txt new file mode 100644 index 000000000..6f0f6d43d --- /dev/null +++ b/src/utilcode/CMakeLists.txt @@ -0,0 +1,46 @@ +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"]]) diff --git a/src/utilcode/check.cpp b/src/utilcode/check.cpp new file mode 100644 index 000000000..4d7a0c741 --- /dev/null +++ b/src/utilcode/check.cpp @@ -0,0 +1,281 @@ +// 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 +#include +#include +#include + +#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::Enter() \ +{ \ + SCAN_SCOPE_BEGIN; \ + ANNOTATION_VIOLATION(mask); \ + EnterInternal(mask); \ +}; + +#define SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER(mask) \ +template<> AutoCleanupContractViolationHolder::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 pScratch(NULL); + NewHolder 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 : ""); + +#if _DEBUG + pMessage->AppendASCII("FAILED: "); + pMessage->AppendASCII(m_condition); +#endif + + messageString = pMessage->GetANSI(*pScratch); + } + EX_CATCH + { + messageString = ""; + } + 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 diff --git a/src/utilcode/clrhost_nodependencies.cpp b/src/utilcode/clrhost_nodependencies.cpp new file mode 100644 index 000000000..ecba72b62 --- /dev/null +++ b/src/utilcode/clrhost_nodependencies.cpp @@ -0,0 +1,537 @@ +// 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 diff --git a/src/utilcode/debug.cpp b/src/utilcode/debug.cpp new file mode 100644 index 000000000..85a71f8c7 --- /dev/null +++ b/src/utilcode/debug.cpp @@ -0,0 +1,299 @@ +// 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 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 diff --git a/src/utilcode/dlwrap.cpp b/src/utilcode/dlwrap.cpp new file mode 100644 index 000000000..8cb312f8f --- /dev/null +++ b/src/utilcode/dlwrap.cpp @@ -0,0 +1,77 @@ +// 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 +#include + +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; + +} + diff --git a/src/utilcode/ex.cpp b/src/utilcode/ex.cpp new file mode 100644 index 000000000..47cb488de --- /dev/null +++ b/src/utilcode/ex.cpp @@ -0,0 +1,1324 @@ +// 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 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 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 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(&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); +} + +//=========================================================================================== +//=========================================================================================== diff --git a/src/utilcode/fstring.cpp b/src/utilcode/fstring.cpp new file mode 100644 index 000000000..9bcd12d1f --- /dev/null +++ b/src/utilcode/fstring.cpp @@ -0,0 +1,321 @@ +// 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 diff --git a/src/utilcode/hostimpl.cpp b/src/utilcode/hostimpl.cpp new file mode 100644 index 000000000..bd5c25a6f --- /dev/null +++ b/src/utilcode/hostimpl.cpp @@ -0,0 +1,77 @@ +// 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 diff --git a/src/utilcode/longfilepathwrappers.cpp b/src/utilcode/longfilepathwrappers.cpp new file mode 100644 index 000000000..d1372faf3 --- /dev/null +++ b/src/utilcode/longfilepathwrappers.cpp @@ -0,0 +1,942 @@ +// 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\\bar => \\?\C:\foo\\bar +// \\server\\bar => \\?\UNC\server\\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; +} + + + diff --git a/src/utilcode/namespaceutil.cpp b/src/utilcode/namespaceutil.cpp new file mode 100644 index 000000000..5b6d3d9f9 --- /dev/null +++ b/src/utilcode/namespaceutil.cpp @@ -0,0 +1,678 @@ +// 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: this isn't dbcs safe if this were ansi, but this is utf8. Still an issue? +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(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 &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); +} + diff --git a/src/utilcode/pedecoder.cpp b/src/utilcode/pedecoder.cpp new file mode 100644 index 000000000..ad4f01823 --- /dev/null +++ b/src/utilcode/pedecoder.cpp @@ -0,0 +1,2564 @@ +// 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(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(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(this)->m_pNTHeaders = dac_cast(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( + dac_cast(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( + dac_cast(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(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(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(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(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(data); + TADDR taddrBase = dac_cast(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(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::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(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(NextStorageStream(pSS)); + ctMD -= (COUNT_T)(pcMD - dac_cast(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(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::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::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(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(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(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(&GetNTHeaders32()->OptionalHeader), + GetNTHeaders32()->FileHeader.SizeOfOptionalHeader, + dac_cast(GetNTHeaders32()->OptionalHeader.DataDirectory + entry), + sizeof(IMAGE_DATA_DIRECTORY)); + else + CheckBounds(dac_cast(&GetNTHeaders64()->OptionalHeader), + GetNTHeaders32()->FileHeader.SizeOfOptionalHeader, + dac_cast(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(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(-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 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(this)->m_pReadyToRunHeader = pHeader; + return pHeader; + } + } + + const_cast(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(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(GetRvaData(nameTableRVA + sizeof(uint32_t) * nameIndex)); + if (namePointerRVA != 0) + { + const char *namePointer = dac_cast(GetRvaData(namePointerRVA)); + if (!strcmp(namePointer, exportName)) + { + uint16_t ordinalForNamedExport = *dac_cast(GetRvaData(ordinalTableRVA + sizeof(uint16_t) * nameIndex)); + uint32_t exportRVA = *dac_cast(GetRvaData(addressTableRVA + sizeof(uint32_t) * ordinalForNamedExport)); + return dac_cast(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(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(dac_cast(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(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(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; +} diff --git a/src/utilcode/safewrap.cpp b/src/utilcode/safewrap.cpp new file mode 100644 index 000000000..209038628 --- /dev/null +++ b/src/utilcode/safewrap.cpp @@ -0,0 +1,336 @@ +// 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 diff --git a/src/utilcode/sbuffer.cpp b/src/utilcode/sbuffer.cpp new file mode 100644 index 000000000..215efefb1 --- /dev/null +++ b/src/utilcode/sbuffer.cpp @@ -0,0 +1,145 @@ +// 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; +} + + diff --git a/src/utilcode/securityutil.cpp b/src/utilcode/securityutil.cpp new file mode 100644 index 000000000..d3cab8ac1 --- /dev/null +++ b/src/utilcode/securityutil.cpp @@ -0,0 +1,494 @@ +// 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(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(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 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 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 sacl = new (nothrow) BYTE[cbSacl]; + + m_pSacl = NULL; + + if (sacl == NULL) + { + return E_OUTOFMEMORY; + } + ZeroMemory(sacl.GetValue(), cbSacl); + PACL pSacl = reinterpret_cast(sacl.GetValue()); + SYSTEM_MANDATORY_LABEL_ACE * pLabelAce = reinterpret_cast(sacl.GetValue() + sizeof(ACL)); + PSID psid = reinterpret_cast(&pLabelAce->SidStart); + + // Our buffer looks like this now: (not drawn to scale) + // sacl pSacl pLabelAce psid + // - - + // | | + // | - - + // | | + // | | - + // | - | + // - - + + DWORD dwIntegrityLevel = *(GetIntegrityLevelFromMandatorySID(psidIntegrityLevelLabel)); + + if (dwIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID) + { + // No need to set the integrity level unless it's lower than medium + return S_OK; + } + + if(!InitializeAcl(pSacl, cbSacl, ACL_REVISION)) + { + return HRESULT_FROM_GetLastError(); + } + + pSacl->AceCount = 1; + + pLabelAce->Header.AceType = SYSTEM_MANDATORY_LABEL_ACE_TYPE; + pLabelAce->Header.AceSize = WORD(cbAceStart + cbSid); + pLabelAce->Mask = SYSTEM_MANDATORY_LABEL_NO_WRITE_UP; + + memcpy(psid, psidIntegrityLevelLabel, cbSid); + + if(!SetSecurityDescriptorSacl(m_SA.lpSecurityDescriptor, TRUE, pSacl, FALSE)) + { + return HRESULT_FROM_GetLastError(); + } + + // No need to delete the sacl buffer, it will be deleted in the + // destructor of this class + m_pSacl = (PACL)sacl.Extract(); + return S_OK; +} + +//***************************************************************** +// Return SECURITY_ATTRIBUTES that we form in the Init function +// +// No clean up is needed after calling this function. The destructor of the +// instance will do the right thing. Note that this is designed such that +// we minimize memory allocation, ie the SECURITY_DESCRIPTOR and +// SECURITY_ATTRIBUTES are embedded in the SecurityUtil instance. +// +// Caller should not modify the returned SECURITY_ATTRIBUTES!!! +//***************************************************************** +HRESULT SecurityUtil::GetSA(SECURITY_ATTRIBUTES **ppSA) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(ppSA); + + if (m_fInitialized == false) + { + _ASSERTE(!"Bad code path!"); + *ppSA = NULL; + return E_FAIL; + } + + *ppSA = &m_SA; + return S_OK; +} diff --git a/src/utilcode/securitywrapper.cpp b/src/utilcode/securitywrapper.cpp new file mode 100644 index 000000000..8f7ac38f4 --- /dev/null +++ b/src/utilcode/securitywrapper.cpp @@ -0,0 +1,749 @@ +// 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 +#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(pvTokenInfo)); + } + + if (pSid) + { + delete [] (reinterpret_cast(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(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); + } +} diff --git a/src/utilcode/sstring.cpp b/src/utilcode/sstring.cpp new file mode 100644 index 000000000..6b5f7af90 --- /dev/null +++ b/src/utilcode/sstring.cpp @@ -0,0 +1,2790 @@ +// 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(this))); + } + else + { + StackSString s; + ConvertToUnicode(s); + PREFIX_ASSUME(!s.IsImmutable()); + (const_cast(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(this))); + } + else + { + StackSString s; + ConvertToUnicode(s); + (const_cast(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: use WC_NO_BEST_FIT_CHARS + 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< 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++ == '%') + { + // Handle the complex format strings like %blahS + 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 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(this)->SetRepresentation(REPRESENTATION_ASCII); + RETURN TRUE; + } + else + const_cast(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(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(pContent), -1, NULL, 0); + } + if (pBuffer && cBufChars) + { + if (!WszMultiByteToWideChar(iPage, 0, reinterpret_cast(pContent), -1, pBuffer, cBufChars)) + { + return false; + } + } + return true; + + default: + DacNotImpl(); + return false; + } + } + return false; +} + +#endif //DACCESS_COMPILE diff --git a/src/utilcode/sstring_com.cpp b/src/utilcode/sstring_com.cpp new file mode 100644 index 000000000..396ec95d7 --- /dev/null +++ b/src/utilcode/sstring_com.cpp @@ -0,0 +1,101 @@ +// 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 diff --git a/src/utilcode/stdafx.h b/src/utilcode/stdafx.h new file mode 100644 index 000000000..f04d0e47f --- /dev/null +++ b/src/utilcode/stdafx.h @@ -0,0 +1,21 @@ +// 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 +#include + +#define IN_WINFIX_CPP + +#include + +#include "volatile.h" +#include "static_assert.h" + diff --git a/src/utilcode/utilcode.vcxproj b/src/utilcode/utilcode.vcxproj new file mode 100644 index 000000000..7fe96f2d6 --- /dev/null +++ b/src/utilcode/utilcode.vcxproj @@ -0,0 +1,340 @@ + + + + x64 + + + + Debug + x64 + + + Checked + x64 + + + Release + x64 + + + RelWithDebInfo + x64 + + + + {8C35FEF8-1101-38F6-ACD0-462A1EA53A7D} + 10.0.19041.0 + Win32Proj + x64 + utilcode + NoUpgrade + + + + StaticLibrary + Unicode + v142 + + + StaticLibrary + Unicode + v142 + + + StaticLibrary + Unicode + v142 + + + StaticLibrary + Unicode + v142 + + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode\Debug\ + utilcodestaticnohost.dir\Debug\ + utilcodestaticnohost + .lib + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode\Checked\ + utilcodestaticnohost.dir\Checked\ + utilcodestaticnohost + .lib + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode\Release\ + utilcodestaticnohost.dir\Release\ + utilcodestaticnohost + .lib + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode\RelWithDebInfo\ + utilcodestaticnohost.dir\RelWithDebInfo\ + utilcodestaticnohost + .lib + + + + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 /homeparams + $(IntDir) + EnableFastChecks + true + Guard + ProgramDatabase + 4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091 + Sync + Precise + true + true + Disabled + true + false + true + false + Disabled + true + MultiThreadedDebug + false + 8Bytes + true + 4007;4013;4102;4551;4700;4640;4806 + true + true + true + Level3 + 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) + $(IntDir) + + + 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) + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + + + 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) + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths) + %(AdditionalOptions) /guard:ehcont + + + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 /IGNORE:4221 + + + + + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 + $(IntDir) + true + Guard + ProgramDatabase + 4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091 + Sync + Precise + true + true + true + false + true + false + MaxSpeed + true + MultiThreadedDebug + false + 8Bytes + true + 4007;4013;4102;4551;4700;4640;4806 + true + true + true + Level3 + 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) + $(IntDir) + + + 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) + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + + + 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) + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths) + %(AdditionalOptions) /guard:ehcont + + + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 /IGNORE:4221 + + + + + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 + $(IntDir) + true + Guard + ProgramDatabase + 4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091 + Sync + Precise + true + true + AnySuitable + true + false + true + false + Full + true + MultiThreaded + false + 8Bytes + true + 4007;4013;4102;4551;4700;4640;4806 + true + true + true + Level3 + true + 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) + $(IntDir) + + + 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) + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + + + 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) + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths) + %(AdditionalOptions) /guard:ehcont + + + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 /IGNORE:4221 + true + + + + + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /guard:ehcont /Zm200 /Zc:strictStrings /w34092 /w34121 /w34125 /w34130 /w34132 /w34212 /w34530 /w35038 /w44177 /ZH:SHA_256 /source-charset:utf-8 + $(IntDir) + true + Guard + ProgramDatabase + 4065;4100;4127;4189;4200;4201;4245;4291;4456;4457;4458;4733;4838;4960;4961;5105;4603;4627;4459;4091 + Sync + Precise + true + true + OnlyExplicitInline + true + false + true + false + MaxSpeed + true + MultiThreaded + false + 8Bytes + true + 4007;4013;4102;4551;4700;4640;4806 + true + true + true + Level3 + true + 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) + $(IntDir) + + + 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) + $(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + + + 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) + $(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(IncludePaths) + %(AdditionalOptions) /guard:ehcont + + + $(ArtifactsObjDir)\Windows_NT.x64.Debug\src\utilcode;$(RepoRoot)src\utilcode;$(ArtifactsObjDir);$(RepoRoot)src;$(RepoRoot)src\pal\prebuilt\inc;$(RepoRoot)src\inc;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 /IGNORE:4221 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/utilcode/utilcode.vcxproj.filters b/src/utilcode/utilcode.vcxproj.filters new file mode 100644 index 000000000..622fe67ea --- /dev/null +++ b/src/utilcode/utilcode.vcxproj.filters @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file