summaryrefslogtreecommitdiff
path: root/src/pal/src
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/src')
-rw-r--r--src/pal/src/.tpattributes1
-rw-r--r--src/pal/src/CMakeLists.txt311
-rw-r--r--src/pal/src/arch/arm/asmconstants.h61
-rw-r--r--src/pal/src/arch/arm/context2.S174
-rw-r--r--src/pal/src/arch/arm/debugbreak.S14
-rw-r--r--src/pal/src/arch/arm/exceptionhelper.S30
-rw-r--r--src/pal/src/arch/arm/processor.cpp42
-rw-r--r--src/pal/src/arch/arm64/context2.S300
-rw-r--r--src/pal/src/arch/arm64/debugbreak.S11
-rw-r--r--src/pal/src/arch/arm64/exceptionhelper.S9
-rw-r--r--src/pal/src/arch/arm64/processor.cpp41
-rw-r--r--src/pal/src/arch/i386/activationhandlerwrapper.S30
-rw-r--r--src/pal/src/arch/i386/asmconstants.h106
-rw-r--r--src/pal/src/arch/i386/context.S21
-rw-r--r--src/pal/src/arch/i386/context2.S259
-rw-r--r--src/pal/src/arch/i386/debugbreak.S12
-rw-r--r--src/pal/src/arch/i386/dispatchexceptionwrapper.S51
-rw-r--r--src/pal/src/arch/i386/exceptionhelper.S42
-rw-r--r--src/pal/src/arch/i386/optimizedtls.cpp237
-rw-r--r--src/pal/src/arch/i386/processor.cpp44
-rw-r--r--src/pal/src/build_tools/mdtool_dummy7
-rw-r--r--src/pal/src/build_tools/mdtool_gcc.in43
-rw-r--r--src/pal/src/config.h.in155
-rw-r--r--src/pal/src/configure.cmake1281
-rw-r--r--src/pal/src/cruntime/file.cpp954
-rw-r--r--src/pal/src/cruntime/filecrt.cpp571
-rw-r--r--src/pal/src/cruntime/lstr.cpp316
-rw-r--r--src/pal/src/cruntime/malloc.cpp116
-rw-r--r--src/pal/src/cruntime/math.cpp424
-rw-r--r--src/pal/src/cruntime/mbstring.cpp257
-rw-r--r--src/pal/src/cruntime/misc.cpp314
-rw-r--r--src/pal/src/cruntime/misctls.cpp399
-rw-r--r--src/pal/src/cruntime/path.cpp596
-rw-r--r--src/pal/src/cruntime/printf.cpp1758
-rw-r--r--src/pal/src/cruntime/printfcpp.cpp2556
-rw-r--r--src/pal/src/cruntime/silent_printf.cpp990
-rw-r--r--src/pal/src/cruntime/string.cpp348
-rw-r--r--src/pal/src/cruntime/stringtls.cpp78
-rw-r--r--src/pal/src/cruntime/thread.cpp52
-rw-r--r--src/pal/src/cruntime/wchar.cpp1885
-rw-r--r--src/pal/src/cruntime/wchartls.cpp127
-rw-r--r--src/pal/src/debug/debug.cpp1844
-rw-r--r--src/pal/src/examples/CMakeLists.txt18
-rw-r--r--src/pal/src/examples/example1.c49
-rw-r--r--src/pal/src/exception/machexception.cpp1612
-rw-r--r--src/pal/src/exception/machexception.h48
-rw-r--r--src/pal/src/exception/machmessage.cpp1383
-rw-r--r--src/pal/src/exception/machmessage.h441
-rw-r--r--src/pal/src/exception/seh-unwind.cpp737
-rw-r--r--src/pal/src/exception/seh.cpp431
-rw-r--r--src/pal/src/exception/signal.cpp705
-rw-r--r--src/pal/src/exception/signal.hpp59
-rw-r--r--src/pal/src/file/directory.cpp725
-rw-r--r--src/pal/src/file/disk.cpp180
-rw-r--r--src/pal/src/file/file.cpp4900
-rw-r--r--src/pal/src/file/filetime.cpp788
-rw-r--r--src/pal/src/file/find.cpp999
-rw-r--r--src/pal/src/file/path.cpp1624
-rw-r--r--src/pal/src/file/shmfilelockmgr.cpp1032
-rw-r--r--src/pal/src/file/shmfilelockmgr.hpp185
-rw-r--r--src/pal/src/handlemgr/handleapi.cpp338
-rw-r--r--src/pal/src/handlemgr/handlemgr.cpp327
-rw-r--r--src/pal/src/include/pal/cert.hpp33
-rw-r--r--src/pal/src/include/pal/context.h641
-rw-r--r--src/pal/src/include/pal/corunix.hpp1359
-rw-r--r--src/pal/src/include/pal/corunix.inl55
-rw-r--r--src/pal/src/include/pal/critsect.h45
-rw-r--r--src/pal/src/include/pal/cruntime.h247
-rw-r--r--src/pal/src/include/pal/cs.hpp54
-rw-r--r--src/pal/src/include/pal/dbgmsg.h628
-rw-r--r--src/pal/src/include/pal/debug.h86
-rw-r--r--src/pal/src/include/pal/dtraceprotocol.h39
-rw-r--r--src/pal/src/include/pal/environ.h78
-rw-r--r--src/pal/src/include/pal/event.hpp69
-rw-r--r--src/pal/src/include/pal/fakepoll.h68
-rw-r--r--src/pal/src/include/pal/file.h304
-rw-r--r--src/pal/src/include/pal/file.hpp365
-rw-r--r--src/pal/src/include/pal/filetime.h80
-rw-r--r--src/pal/src/include/pal/handleapi.hpp48
-rw-r--r--src/pal/src/include/pal/handlemgr.hpp180
-rw-r--r--src/pal/src/include/pal/identity.hpp57
-rw-r--r--src/pal/src/include/pal/init.h111
-rw-r--r--src/pal/src/include/pal/list.h141
-rw-r--r--src/pal/src/include/pal/locale.h75
-rw-r--r--src/pal/src/include/pal/malloc.hpp152
-rw-r--r--src/pal/src/include/pal/map.h52
-rw-r--r--src/pal/src/include/pal/map.hpp209
-rw-r--r--src/pal/src/include/pal/misc.h83
-rw-r--r--src/pal/src/include/pal/module.h203
-rw-r--r--src/pal/src/include/pal/modulename.h39
-rw-r--r--src/pal/src/include/pal/mutex.hpp191
-rw-r--r--src/pal/src/include/pal/palinternal.h696
-rw-r--r--src/pal/src/include/pal/perftrace.h70
-rw-r--r--src/pal/src/include/pal/printfcpp.hpp133
-rw-r--r--src/pal/src/include/pal/process.h162
-rw-r--r--src/pal/src/include/pal/procobj.hpp125
-rw-r--r--src/pal/src/include/pal/seh.hpp185
-rw-r--r--src/pal/src/include/pal/semaphore.hpp74
-rw-r--r--src/pal/src/include/pal/sharedmemory.h268
-rw-r--r--src/pal/src/include/pal/sharedmemory.inl53
-rw-r--r--src/pal/src/include/pal/shm.hpp83
-rw-r--r--src/pal/src/include/pal/shmemory.h331
-rw-r--r--src/pal/src/include/pal/stackstring.hpp239
-rw-r--r--src/pal/src/include/pal/synchcache.hpp397
-rw-r--r--src/pal/src/include/pal/synchobjects.hpp216
-rw-r--r--src/pal/src/include/pal/thread.hpp838
-rw-r--r--src/pal/src/include/pal/threadinfo.hpp89
-rw-r--r--src/pal/src/include/pal/threadsusp.hpp382
-rw-r--r--src/pal/src/include/pal/tls.hpp65
-rw-r--r--src/pal/src/include/pal/unicode_data.h69
-rw-r--r--src/pal/src/include/pal/utf8.h53
-rw-r--r--src/pal/src/include/pal/utils.h157
-rw-r--r--src/pal/src/include/pal/virtual.h208
-rw-r--r--src/pal/src/init/pal.cpp1480
-rw-r--r--src/pal/src/init/sxs.cpp322
-rw-r--r--src/pal/src/loader/module.cpp1771
-rw-r--r--src/pal/src/loader/modulename.cpp207
-rw-r--r--src/pal/src/locale/UnicodeData.txt12860
-rw-r--r--src/pal/src/locale/unicode.cpp1026
-rw-r--r--src/pal/src/locale/unicode_data.cpp1852
-rw-r--r--src/pal/src/locale/utf8.cpp2904
-rw-r--r--src/pal/src/map/common.cpp66
-rw-r--r--src/pal/src/map/common.h43
-rw-r--r--src/pal/src/map/map.cpp2749
-rw-r--r--src/pal/src/map/virtual.cpp2064
-rw-r--r--src/pal/src/memory/heap.cpp389
-rw-r--r--src/pal/src/memory/local.cpp141
-rw-r--r--src/pal/src/misc/dbgmsg.cpp968
-rw-r--r--src/pal/src/misc/environ.cpp1096
-rw-r--r--src/pal/src/misc/error.cpp126
-rw-r--r--src/pal/src/misc/errorstrings.cpp172
-rw-r--r--src/pal/src/misc/errorstrings.h10
-rw-r--r--src/pal/src/misc/eventlog.cpp423
-rw-r--r--src/pal/src/misc/fmtmessage.cpp701
-rw-r--r--src/pal/src/misc/identity.cpp410
-rw-r--r--src/pal/src/misc/miscpalapi.cpp381
-rw-r--r--src/pal/src/misc/msgbox.cpp416
-rw-r--r--src/pal/src/misc/perftrace.cpp1522
-rw-r--r--src/pal/src/misc/strutil.cpp96
-rw-r--r--src/pal/src/misc/sysinfo.cpp363
-rw-r--r--src/pal/src/misc/time.cpp396
-rw-r--r--src/pal/src/misc/tracepointprovider.cpp109
-rw-r--r--src/pal/src/misc/utils.cpp320
-rw-r--r--src/pal/src/misc/version.cpp119
-rw-r--r--src/pal/src/objmgr/palobjbase.cpp359
-rw-r--r--src/pal/src/objmgr/palobjbase.hpp191
-rw-r--r--src/pal/src/objmgr/shmobject.cpp1471
-rw-r--r--src/pal/src/objmgr/shmobject.hpp391
-rw-r--r--src/pal/src/objmgr/shmobjectmanager.cpp1567
-rw-r--r--src/pal/src/objmgr/shmobjectmanager.hpp167
-rw-r--r--src/pal/src/poll/fakepoll.cpp133
-rw-r--r--src/pal/src/safecrt/cruntime.h98
-rw-r--r--src/pal/src/safecrt/input.inl1314
-rw-r--r--src/pal/src/safecrt/internal.h1097
-rw-r--r--src/pal/src/safecrt/internal_securecrt.h292
-rw-r--r--src/pal/src/safecrt/makepath_s.c31
-rw-r--r--src/pal/src/safecrt/mbusafecrt.c254
-rw-r--r--src/pal/src/safecrt/mbusafecrt_internal.h88
-rw-r--r--src/pal/src/safecrt/memcpy_s.c82
-rw-r--r--src/pal/src/safecrt/memmove_s.c69
-rw-r--r--src/pal/src/safecrt/output.inl1624
-rw-r--r--src/pal/src/safecrt/safecrt_input_s.c46
-rw-r--r--src/pal/src/safecrt/safecrt_output_l.c1467
-rw-r--r--src/pal/src/safecrt/safecrt_output_s.c46
-rw-r--r--src/pal/src/safecrt/safecrt_winput_s.c55
-rw-r--r--src/pal/src/safecrt/safecrt_woutput_s.c59
-rw-r--r--src/pal/src/safecrt/snprintf.c18
-rw-r--r--src/pal/src/safecrt/splitpath_s.c31
-rw-r--r--src/pal/src/safecrt/sprintf.c98
-rw-r--r--src/pal/src/safecrt/sscanf.c249
-rw-r--r--src/pal/src/safecrt/strcat_s.c33
-rw-r--r--src/pal/src/safecrt/strcpy_s.c29
-rw-r--r--src/pal/src/safecrt/strlen_s.c58
-rw-r--r--src/pal/src/safecrt/strncat_s.c31
-rw-r--r--src/pal/src/safecrt/strncpy_s.c30
-rw-r--r--src/pal/src/safecrt/strtok_s.c27
-rw-r--r--src/pal/src/safecrt/swprintf.c120
-rw-r--r--src/pal/src/safecrt/tcscat_s.inl51
-rw-r--r--src/pal/src/safecrt/tcscpy_s.inl39
-rw-r--r--src/pal/src/safecrt/tcsncat_s.inl81
-rw-r--r--src/pal/src/safecrt/tcsncpy_s.inl71
-rw-r--r--src/pal/src/safecrt/tcstok_s.inl71
-rw-r--r--src/pal/src/safecrt/tmakepath_s.inl116
-rw-r--r--src/pal/src/safecrt/tsplitpath_s.inl280
-rw-r--r--src/pal/src/safecrt/vsprintf.c268
-rw-r--r--src/pal/src/safecrt/vswprint.c282
-rw-r--r--src/pal/src/safecrt/wcscat_s.c36
-rw-r--r--src/pal/src/safecrt/wcscpy_s.c33
-rw-r--r--src/pal/src/safecrt/wcslen_s.c58
-rw-r--r--src/pal/src/safecrt/wcsncat_s.c35
-rw-r--r--src/pal/src/safecrt/wcsncpy_s.c34
-rw-r--r--src/pal/src/safecrt/wcstok_s.c27
-rw-r--r--src/pal/src/safecrt/wmakepath_s.c30
-rw-r--r--src/pal/src/safecrt/wsplitpath_s.c30
-rw-r--r--src/pal/src/safecrt/xtoa_s.c29
-rw-r--r--src/pal/src/safecrt/xtow_s.c28
-rw-r--r--src/pal/src/safecrt/xtox_s.inl450
-rw-r--r--src/pal/src/sharedmemory/sharedmemory.cpp1136
-rw-r--r--src/pal/src/shmemory/shmemory.cpp1734
-rw-r--r--src/pal/src/sync/cs.cpp1603
-rw-r--r--src/pal/src/synchmgr/synchcontrollers.cpp1976
-rw-r--r--src/pal/src/synchmgr/synchmanager.cpp4556
-rw-r--r--src/pal/src/synchmgr/synchmanager.hpp1022
-rw-r--r--src/pal/src/synchmgr/wait.cpp711
-rw-r--r--src/pal/src/synchobj/event.cpp589
-rw-r--r--src/pal/src/synchobj/mutex.cpp1584
-rw-r--r--src/pal/src/synchobj/semaphore.cpp680
-rw-r--r--src/pal/src/thread/context.cpp1360
-rw-r--r--src/pal/src/thread/process.cpp4615
-rw-r--r--src/pal/src/thread/procprivate.hpp78
-rw-r--r--src/pal/src/thread/thread.cpp2871
-rw-r--r--src/pal/src/thread/threadsusp.cpp1046
-rw-r--r--src/pal/src/thread/tls.cpp226
213 files changed, 118996 insertions, 0 deletions
diff --git a/src/pal/src/.tpattributes b/src/pal/src/.tpattributes
new file mode 100644
index 0000000000..ca534f0fbf
--- /dev/null
+++ b/src/pal/src/.tpattributes
@@ -0,0 +1 @@
+configure:x
diff --git a/src/pal/src/CMakeLists.txt b/src/pal/src/CMakeLists.txt
new file mode 100644
index 0000000000..7df0aaf360
--- /dev/null
+++ b/src/pal/src/CMakeLists.txt
@@ -0,0 +1,311 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+include_directories(SYSTEM /usr/local/include)
+
+# set kernel version to detect Alpine
+EXEC_PROGRAM(uname ARGS -v OUTPUT_VARIABLE CMAKE_SYSTEM_KERNEL_VERSION)
+string(FIND "${CMAKE_SYSTEM_KERNEL_VERSION}" "Alpine" PAL_SYSTEM_ALPINE)
+if(PAL_SYSTEM_ALPINE EQUAL -1)
+ unset(PAL_SYSTEM_ALPINE)
+endif()
+
+include(configure.cmake)
+
+project(coreclrpal)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+if(CORECLR_SET_RPATH)
+ # Enable @rpath support for shared libraries.
+ set(MACOSX_RPATH ON)
+endif(CORECLR_SET_RPATH)
+
+if(CMAKE_VERSION VERSION_EQUAL 3.0 OR CMAKE_VERSION VERSION_GREATER 3.0)
+ cmake_policy(SET CMP0042 NEW)
+endif()
+
+# Include directories
+
+include_directories(include)
+
+# Compile options
+
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL amd64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64)
+ set(PAL_CMAKE_PLATFORM_ARCH_AMD64 1)
+ add_definitions(-D_AMD64_)
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l)
+ set(PAL_CMAKE_PLATFORM_ARCH_ARM 1)
+ add_definitions(-D_ARM_)
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
+ set(PAL_CMAKE_PLATFORM_ARCH_ARM64 1)
+ add_definitions(-D_ARM64_)
+else()
+ message(FATAL_ERROR "Only ARM and AMD64 is supported")
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+ add_definitions(-D_TARGET_MAC64)
+ set(PLATFORM_SOURCES
+ arch/i386/activationhandlerwrapper.S
+ arch/i386/context.S
+ arch/i386/dispatchexceptionwrapper.S
+ exception/machexception.cpp
+ exception/machmessage.cpp
+ )
+endif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+
+add_definitions(-DPLATFORM_UNIX=1)
+add_definitions(-DLP64COMPATIBLE=1)
+add_definitions(-DFEATURE_PAL=1)
+add_definitions(-DCORECLR=1)
+add_definitions(-DPIC=1)
+add_definitions(-D_FILE_OFFSET_BITS=64)
+if(PAL_CMAKE_PLATFORM_ARCH_AMD64)
+ add_definitions(-DBIT64=1)
+ add_definitions(-D_WIN64=1)
+elseif(PAL_CMAKE_PLATFORM_ARCH_ARM)
+ add_definitions(-DBIT32=1)
+elseif(PAL_CMAKE_PLATFORM_ARCH_ARM64)
+ add_definitions(-DBIT64=1)
+ add_definitions(-D_WIN64=1)
+endif()
+
+# turn off capability to remove unused functions (which was enabled in debug build with sanitizers)
+set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -Wl,--no-gc-sections")
+
+add_compile_options(-fPIC)
+
+if(PAL_CMAKE_PLATFORM_ARCH_AMD64)
+ set(ARCH_SOURCES
+ arch/i386/context2.S
+ arch/i386/debugbreak.S
+ arch/i386/exceptionhelper.S
+ arch/i386/processor.cpp
+ )
+elseif(PAL_CMAKE_PLATFORM_ARCH_ARM)
+ set(ARCH_SOURCES
+ arch/arm/context2.S
+ arch/arm/debugbreak.S
+ arch/arm/exceptionhelper.S
+ arch/arm/processor.cpp
+ )
+elseif(PAL_CMAKE_PLATFORM_ARCH_ARM64)
+ set(ARCH_SOURCES
+ arch/arm64/context2.S
+ arch/arm64/debugbreak.S
+ arch/arm64/exceptionhelper.S
+ arch/arm64/processor.cpp
+ )
+endif()
+
+if(PAL_CMAKE_PLATFORM_ARCH_ARM)
+ set_source_files_properties(exception/seh.cpp PROPERTIES COMPILE_FLAGS -Wno-error=inline-asm)
+endif(PAL_CMAKE_PLATFORM_ARCH_ARM)
+
+set(SOURCES
+ cruntime/file.cpp
+ cruntime/filecrt.cpp
+ cruntime/lstr.cpp
+ cruntime/malloc.cpp
+ cruntime/math.cpp
+ cruntime/mbstring.cpp
+ cruntime/misc.cpp
+ cruntime/misctls.cpp
+ cruntime/path.cpp
+ cruntime/printf.cpp
+ cruntime/printfcpp.cpp
+ cruntime/silent_printf.cpp
+ cruntime/string.cpp
+ cruntime/stringtls.cpp
+ cruntime/thread.cpp
+ cruntime/wchar.cpp
+ cruntime/wchartls.cpp
+ debug/debug.cpp
+ exception/seh.cpp
+ exception/signal.cpp
+ file/directory.cpp
+ file/disk.cpp
+ file/file.cpp
+ file/filetime.cpp
+ file/find.cpp
+ file/path.cpp
+ file/shmfilelockmgr.cpp
+ handlemgr/handleapi.cpp
+ handlemgr/handlemgr.cpp
+ init/pal.cpp
+ init/sxs.cpp
+ loader/module.cpp
+ loader/modulename.cpp
+ locale/unicode.cpp
+ locale/unicode_data.cpp
+ locale/utf8.cpp
+ map/common.cpp
+ map/map.cpp
+ map/virtual.cpp
+ memory/heap.cpp
+ memory/local.cpp
+ misc/dbgmsg.cpp
+ misc/environ.cpp
+ misc/error.cpp
+ misc/errorstrings.cpp
+ misc/fmtmessage.cpp
+ misc/identity.cpp
+ misc/miscpalapi.cpp
+ misc/msgbox.cpp
+ misc/strutil.cpp
+ misc/sysinfo.cpp
+ misc/time.cpp
+ misc/utils.cpp
+ misc/version.cpp
+ objmgr/palobjbase.cpp
+ objmgr/shmobject.cpp
+ objmgr/shmobjectmanager.cpp
+ safecrt/makepath_s.c
+ safecrt/memcpy_s.c
+ safecrt/memmove_s.c
+ safecrt/mbusafecrt.c
+ safecrt/safecrt_input_s.c
+ safecrt/safecrt_output_l.c
+ safecrt/safecrt_output_s.c
+ safecrt/safecrt_winput_s.c
+ safecrt/safecrt_woutput_s.c
+ safecrt/splitpath_s.c
+ safecrt/sprintf.c
+ safecrt/sscanf.c
+ safecrt/strcat_s.c
+ safecrt/strcpy_s.c
+ safecrt/strlen_s.c
+ safecrt/strncat_s.c
+ safecrt/strncpy_s.c
+ safecrt/strtok_s.c
+ safecrt/swprintf.c
+ safecrt/vsprintf.c
+ safecrt/vswprint.c
+ safecrt/wcscat_s.c
+ safecrt/wcscpy_s.c
+ safecrt/wcslen_s.c
+ safecrt/wcsncat_s.c
+ safecrt/wcsncpy_s.c
+ safecrt/wcstok_s.c
+ safecrt/wmakepath_s.c
+ safecrt/wsplitpath_s.c
+ safecrt/xtoa_s.c
+ safecrt/xtow_s.c
+ sharedmemory/sharedmemory.cpp
+ shmemory/shmemory.cpp
+ sync/cs.cpp
+ synchobj/event.cpp
+ synchobj/semaphore.cpp
+ synchobj/mutex.cpp
+ synchmgr/synchcontrollers.cpp
+ synchmgr/synchmanager.cpp
+ synchmgr/wait.cpp
+ thread/context.cpp
+ thread/process.cpp
+ thread/thread.cpp
+ thread/threadsusp.cpp
+ thread/tls.cpp
+)
+
+add_library(coreclrpal
+ STATIC
+ ${SOURCES}
+ ${ARCH_SOURCES}
+ ${PLATFORM_SOURCES}
+)
+
+add_library(tracepointprovider
+ STATIC
+ misc/tracepointprovider.cpp
+)
+
+if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+ find_library(COREFOUNDATION CoreFoundation)
+ find_library(CORESERVICES CoreServices)
+ find_library(SECURITY Security)
+ find_library(SYSTEM System)
+ target_link_libraries(coreclrpal
+ ${COREFOUNDATION}
+ ${CORESERVICES}
+ ${SECURITY}
+ ${SYSTEM}
+ )
+endif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+
+if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
+ find_library(UNWIND unwind)
+ find_library(INTL intl)
+ target_link_libraries(coreclrpal
+ pthread
+ rt
+ ${UNWIND}
+ ${INTL}
+ )
+endif(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
+
+if(CMAKE_SYSTEM_NAME STREQUAL Linux)
+ if(PAL_CMAKE_PLATFORM_ARCH_ARM)
+ find_library(UNWIND_ARCH NAMES unwind-arm)
+ endif()
+
+ if(PAL_CMAKE_PLATFORM_ARCH_AMD64)
+ find_library(UNWIND_ARCH NAMES unwind-x86_64)
+ endif()
+
+ if(PAL_SYSTEM_ALPINE)
+ find_library(INTL intl)
+ endif()
+
+ find_library(UNWIND NAMES unwind)
+ find_library(UNWIND_GENERIC NAMES unwind-generic)
+
+ target_link_libraries(coreclrpal
+ gcc_s
+ pthread
+ rt
+ dl
+ uuid
+ )
+
+ if(UNWIND STREQUAL UNWIND-NOTFOUND)
+ message(FATAL_ERROR "Cannot find libunwind. Try installing libunwind8-dev and libunwind8.")
+ endif(UNWIND STREQUAL UNWIND-NOTFOUND)
+
+ target_link_libraries(coreclrpal ${UNWIND})
+
+ if(NOT UNWIND_GENERIC STREQUAL UNWIND_GENERIC-NOTFOUND)
+ target_link_libraries(coreclrpal ${UNWIND_GENERIC})
+ endif(NOT UNWIND_GENERIC STREQUAL UNWIND_GENERIC-NOTFOUND)
+
+ if(NOT UNWIND_ARCH STREQUAL UNWIND_ARCH-NOTFOUND)
+ target_link_libraries(coreclrpal ${UNWIND_ARCH})
+ endif(NOT UNWIND_ARCH STREQUAL UNWIND_ARCH-NOTFOUND)
+
+ if(NOT INTL STREQUAL INTL-NOTFOUND)
+ target_link_libraries(coreclrpal ${INTL})
+ endif(NOT INTL STREQUAL INTL-NOTFOUND)
+
+endif(CMAKE_SYSTEM_NAME STREQUAL Linux)
+
+if(CMAKE_SYSTEM_NAME STREQUAL NetBSD)
+ add_definitions(-D_KMEMUSER)
+ find_library(UNWIND unwind)
+ find_library(INTL intl)
+ find_library(KVM kvm)
+ target_link_libraries(coreclrpal
+ pthread
+ rt
+ ${UNWIND}
+ ${INTL}
+ ${KVM}
+ )
+endif(CMAKE_SYSTEM_NAME STREQUAL NetBSD)
+
+add_subdirectory(examples)
+
+if(FEATURE_EVENT_TRACE)
+ add_subdirectory($ENV{__IntermediatesDir}/Generated/eventprovider ${CMAKE_CURRENT_BINARY_DIR}/eventprovider)
+endif(FEATURE_EVENT_TRACE)
+
+# Install the static PAL library for VS
+install (TARGETS coreclrpal DESTINATION lib)
diff --git a/src/pal/src/arch/arm/asmconstants.h b/src/pal/src/arch/arm/asmconstants.h
new file mode 100644
index 0000000000..ae4b01b8dc
--- /dev/null
+++ b/src/pal/src/arch/arm/asmconstants.h
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __PAL_ARM_ASMCONSTANTS_H__
+#define __PAL_ARM_ASMCONSTANTS_H__
+
+#define CONTEXT_ContextFlags 0
+#define CONTEXT_R0 CONTEXT_ContextFlags+4
+#define CONTEXT_R1 CONTEXT_R0+4
+#define CONTEXT_R2 CONTEXT_R1+4
+#define CONTEXT_R3 CONTEXT_R2+4
+#define CONTEXT_R4 CONTEXT_R3+4
+#define CONTEXT_R5 CONTEXT_R4+4
+#define CONTEXT_R6 CONTEXT_R5+4
+#define CONTEXT_R7 CONTEXT_R6+4
+#define CONTEXT_R8 CONTEXT_R7+4
+#define CONTEXT_R9 CONTEXT_R8+4
+#define CONTEXT_R10 CONTEXT_R9+4
+#define CONTEXT_R11 CONTEXT_R10+4
+#define CONTEXT_R12 CONTEXT_R11+4
+#define CONTEXT_Sp CONTEXT_R12+4
+#define CONTEXT_Lr CONTEXT_Sp+4
+#define CONTEXT_Pc CONTEXT_Lr+4
+#define CONTEXT_Cpsr CONTEXT_Pc+4
+#define CONTEXT_Fpscr CONTEXT_Cpsr+4
+#define CONTEXT_Padding CONTEXT_Fpscr+4
+#define CONTEXT_D0 CONTEXT_Padding+4
+#define CONTEXT_D1 CONTEXT_D0+8
+#define CONTEXT_D2 CONTEXT_D1+8
+#define CONTEXT_D3 CONTEXT_D2+8
+#define CONTEXT_D4 CONTEXT_D3+8
+#define CONTEXT_D5 CONTEXT_D4+8
+#define CONTEXT_D6 CONTEXT_D5+8
+#define CONTEXT_D7 CONTEXT_D6+8
+#define CONTEXT_D8 CONTEXT_D7+8
+#define CONTEXT_D9 CONTEXT_D8+8
+#define CONTEXT_D10 CONTEXT_D9+8
+#define CONTEXT_D11 CONTEXT_D10+8
+#define CONTEXT_D12 CONTEXT_D11+8
+#define CONTEXT_D13 CONTEXT_D12+8
+#define CONTEXT_D14 CONTEXT_D13+8
+#define CONTEXT_D15 CONTEXT_D14+8
+#define CONTEXT_D16 CONTEXT_D15+8
+#define CONTEXT_D17 CONTEXT_D16+8
+#define CONTEXT_D18 CONTEXT_D17+8
+#define CONTEXT_D19 CONTEXT_D18+8
+#define CONTEXT_D20 CONTEXT_D19+8
+#define CONTEXT_D21 CONTEXT_D20+8
+#define CONTEXT_D22 CONTEXT_D21+8
+#define CONTEXT_D23 CONTEXT_D22+8
+#define CONTEXT_D24 CONTEXT_D23+8
+#define CONTEXT_D25 CONTEXT_D24+8
+#define CONTEXT_D26 CONTEXT_D25+8
+#define CONTEXT_D27 CONTEXT_D26+8
+#define CONTEXT_D28 CONTEXT_D27+8
+#define CONTEXT_D29 CONTEXT_D28+8
+#define CONTEXT_D30 CONTEXT_D29+8
+#define CONTEXT_D31 CONTEXT_D30+8
+
+#endif
diff --git a/src/pal/src/arch/arm/context2.S b/src/pal/src/arch/arm/context2.S
new file mode 100644
index 0000000000..61e9ab8463
--- /dev/null
+++ b/src/pal/src/arch/arm/context2.S
@@ -0,0 +1,174 @@
+// 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.
+//
+// Implementation of _CONTEXT_CaptureContext for the ARM platform.
+// This function is processor dependent. It is used by exception handling,
+// and is always apply to the current thread.
+//
+
+#include "unixasmmacros.inc"
+#include "asmconstants.h"
+
+.syntax unified
+.thumb
+
+#define CONTEXT_ARM 0x00200000
+
+#define CONTEXT_CONTROL 1 // Sp, Lr, Pc, Cpsr
+#define CONTEXT_INTEGER 2 // R0-R12
+#define CONTEXT_SEGMENTS 4 //
+#define CONTEXT_FLOATING_POINT 8
+#define CONTEXT_DEBUG_REGISTERS 16 //
+
+#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT)
+
+
+// Incoming:
+// r0: Context*
+//
+LEAF_ENTRY CONTEXT_CaptureContext, _TEXT
+ // Ensure we save these registers
+ push {r4-r11}
+ // Save processor flags before calling any of the following 'test' instructions
+ // because they will modify state of some flags
+ push {r1}
+ mrs r1, apsr // Get APSR - equivalent to eflags
+ push {r1} // Save APSR
+ END_PROLOGUE
+
+ push {r2}
+ ldr r2, [r0, #(CONTEXT_ContextFlags)]
+ tst r2, #(CONTEXT_INTEGER)
+ pop {r2}
+
+ // Add 4 to stack so we point at R1, pop, then sub 8 to point at APSR
+ add sp, sp, #4
+ pop {r1}
+ sub sp, sp, #8
+
+ itttt ne
+ strne r0, [r0, #(CONTEXT_R0)]
+ addne r0, CONTEXT_R1
+ stmiane r0, {r1-r12}
+ subne r0, CONTEXT_R1
+
+ ldr r2, [r0, #(CONTEXT_ContextFlags)]
+ tst r2, #(CONTEXT_CONTROL)
+
+ ittt ne
+ addne sp, sp, #(10*4) // This needs to put the stack in the same state as it started
+ strne sp, [r0, #(CONTEXT_Sp)]
+ subne sp, sp, #(10*4)
+
+ itt ne
+ strne lr, [r0, #(CONTEXT_Lr)]
+ strne lr, [r0, #(CONTEXT_Pc)]
+
+ // Get the APSR pushed onto the stack at the start
+ pop {r1}
+ it ne
+ strne r1, [r0, #(CONTEXT_Cpsr)]
+
+ ldr r2, [r0, #(CONTEXT_ContextFlags)]
+ tst r2, #(CONTEXT_FLOATING_POINT)
+
+ itt ne
+ vmrsne r3, fpscr
+ strne r3, [r0, #(CONTEXT_Fpscr)]
+
+ itttt ne
+ addne r0, CONTEXT_D0
+ vstmiane r0!, {d0-d15}
+ vstmiane r0!, {d16-d31}
+ subne r0, CONTEXT_D31
+
+ // Make sure sp is restored
+ add sp, sp, #4
+
+ // Restore callee saved registers
+ pop {r4-r11}
+ bx lr
+LEAF_END CONTEXT_CaptureContext, _TEXT
+
+// Incoming:
+// R0: Context*
+//
+LEAF_ENTRY RtlCaptureContext, _TEXT
+ push {r1}
+ mov r1, #0
+ orr r1, r1, #CONTEXT_ARM
+ orr r1, r1, #CONTEXT_INTEGER
+ orr r1, r1, #CONTEXT_CONTROL
+ orr r1, r1, #CONTEXT_FLOATING_POINT
+ str r1, [r0, #(CONTEXT_ContextFlags)]
+ pop {r1}
+ b C_FUNC(CONTEXT_CaptureContext)
+LEAF_END RtlCaptureContext, _TEXT
+
+// Incoming:
+// r0: Context*
+// r1: Exception*
+//
+LEAF_ENTRY RtlRestoreContext, _TEXT
+ END_PROLOGUE
+
+ ldr r2, [r0, #(CONTEXT_ContextFlags)]
+ tst r2, #(CONTEXT_FLOATING_POINT)
+
+ itttt ne
+ addne r0, CONTEXT_D0
+ vldmiane r0!, {d0-d15}
+ vldmiane r0, {d16-d31}
+ subne r0, CONTEXT_D16
+
+ itt ne
+ ldrne r3, [r0, #(CONTEXT_Fpscr)]
+ vmrsne r3, FPSCR
+
+ ldr r2, [r0, #(CONTEXT_ContextFlags)]
+ tst r2, #(CONTEXT_CONTROL)
+
+ it eq
+ beq LOCAL_LABEL(No_Restore_CONTEXT_CONTROL)
+
+ ldr r2, [r0, #(CONTEXT_ContextFlags)]
+ tst r2, #(CONTEXT_INTEGER)
+
+ it eq
+ beq LOCAL_LABEL(No_Restore_CONTEXT_INTEGER)
+
+ ldr R2, [r0, #(CONTEXT_Cpsr)]
+ msr APSR, r2
+
+ // Ideally, we would like to use `ldmia r0, {r0-r12, sp, lr, pc}` here,
+ // but clang 3.6 and later, as per ARM recommendation, disallows using
+ // Sp in the register list, and Pc and Lr simultaneously.
+ // So we are going to use the IPC register r12 to copy Sp, Lr and Pc
+ // which should be ok -- TODO: Is this really ok?
+ add r12, r0, CONTEXT_R0
+ ldm r12, {r0-r11}
+ ldr sp, [r12, #(CONTEXT_Sp - (CONTEXT_R0))]
+ ldr lr, [r12, #(CONTEXT_Lr - (CONTEXT_R0))]
+ ldr pc, [r12, #(CONTEXT_Pc - (CONTEXT_R0))]
+
+LOCAL_LABEL(No_Restore_CONTEXT_INTEGER):
+
+ ldr r2, [r0, #(CONTEXT_Cpsr)]
+ msr APSR, r2
+
+ ldr sp, [r0, #(CONTEXT_Sp)]
+ ldr lr, [r0, #(CONTEXT_Lr)]
+ ldr pc, [r0, #(CONTEXT_Pc)]
+
+LOCAL_LABEL(No_Restore_CONTEXT_CONTROL):
+ ldr r2, [r0, #(CONTEXT_ContextFlags)]
+ tst r2, #(CONTEXT_INTEGER)
+
+ itt ne
+ addne r0, CONTEXT_R0
+ ldmiane r0, {r0-r12}
+
+ sub sp, sp, #4
+ bx lr
+LEAF_END RtlRestoreContext, _TEXT
diff --git a/src/pal/src/arch/arm/debugbreak.S b/src/pal/src/arch/arm/debugbreak.S
new file mode 100644
index 0000000000..863d7cf40b
--- /dev/null
+++ b/src/pal/src/arch/arm/debugbreak.S
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "unixasmmacros.inc"
+
+.syntax unified
+.thumb
+
+LEAF_ENTRY DBG_DebugBreak, _TEXT
+ EMIT_BREAKPOINT
+ bx lr
+LEAF_END_MARKED DBG_DebugBreak, _TEXT
+
diff --git a/src/pal/src/arch/arm/exceptionhelper.S b/src/pal/src/arch/arm/exceptionhelper.S
new file mode 100644
index 0000000000..ed1c9c3dc2
--- /dev/null
+++ b/src/pal/src/arch/arm/exceptionhelper.S
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "unixasmmacros.inc"
+#include "asmconstants.h"
+
+.syntax unified
+.thumb
+
+// EXTERN_C void ThrowExceptionFromContextInternal(CONTEXT* context, PAL_SEHException* ex);
+LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT
+ // Ported from src/pal/src/arch/i386/exceptionhelper.S
+ push_nonvol_reg {r7} /* FP. x64-RBP */
+
+ ldr r4, [r0, #(CONTEXT_R4)]
+ ldr r5, [r0, #(CONTEXT_R5)]
+ ldr r6, [r0, #(CONTEXT_R6)]
+ ldr r7, [r0, #(CONTEXT_R7)]
+ ldr r8, [r0, #(CONTEXT_R8)]
+ ldr r9, [r0, #(CONTEXT_R9)]
+ ldr r10, [r0, #(CONTEXT_R10)]
+ ldr r11, [r0, #(CONTEXT_R11)]
+ ldr sp, [r0, #(CONTEXT_Sp)]
+ ldr lr, [r0, #(CONTEXT_Pc)]
+
+ // The PAL_SEHException pointer
+ mov r0, r1
+ b EXTERNAL_C_FUNC(ThrowExceptionHelper)
+LEAF_END ThrowExceptionFromContextInternal, _TEXT
diff --git a/src/pal/src/arch/arm/processor.cpp b/src/pal/src/arch/arm/processor.cpp
new file mode 100644
index 0000000000..f41caff1e0
--- /dev/null
+++ b/src/pal/src/arch/arm/processor.cpp
@@ -0,0 +1,42 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ processor.cpp
+
+Abstract:
+
+ Implementation of processor related functions for the ARM
+ platform. These functions are processor dependent.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+
+/*++
+Function:
+YieldProcessor
+
+The YieldProcessor function signals to the processor to give resources
+to threads that are waiting for them. This macro is only effective on
+processors that support technology allowing multiple threads running
+on a single processor, such as Intel's Hyper-Threading technology.
+
+--*/
+void
+PALAPI
+YieldProcessor(
+ VOID)
+{
+ // Pretty sure ARM has no useful function here?
+ return;
+}
+
diff --git a/src/pal/src/arch/arm64/context2.S b/src/pal/src/arch/arm64/context2.S
new file mode 100644
index 0000000000..a64e62c94d
--- /dev/null
+++ b/src/pal/src/arch/arm64/context2.S
@@ -0,0 +1,300 @@
+// 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.
+//
+// Implementation of _CONTEXT_CaptureContext for the ARM platform.
+// This function is processor dependent. It is used by exception handling,
+// and is always apply to the current thread.
+//
+
+#include "unixasmmacros.inc"
+
+#define CONTEXT_ARM64 0x00400000L
+
+#define CONTEXT_CONTROL (CONTEXT_ARM64 | 0x1L)
+#define CONTEXT_INTEGER (CONTEXT_ARM64 | 0x2L)
+#define CONTEXT_FLOATING_POINT (CONTEXT_ARM64 | 0x4L)
+#define CONTEXT_DEBUG_REGISTERS (CONTEXT_ARM64 | 0x8L)
+
+#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT)
+
+#define CONTEXT_ContextFlags 0
+#define CONTEXT_Cpsr CONTEXT_ContextFlags+4
+#define CONTEXT_X0 CONTEXT_Cpsr+4
+#define CONTEXT_X1 CONTEXT_X0+8
+#define CONTEXT_X2 CONTEXT_X1+8
+#define CONTEXT_X3 CONTEXT_X2+8
+#define CONTEXT_X4 CONTEXT_X3+8
+#define CONTEXT_X5 CONTEXT_X4+8
+#define CONTEXT_X6 CONTEXT_X5+8
+#define CONTEXT_X7 CONTEXT_X6+8
+#define CONTEXT_X8 CONTEXT_X7+8
+#define CONTEXT_X9 CONTEXT_X8+8
+#define CONTEXT_X10 CONTEXT_X9+8
+#define CONTEXT_X11 CONTEXT_X10+8
+#define CONTEXT_X12 CONTEXT_X11+8
+#define CONTEXT_X13 CONTEXT_X12+8
+#define CONTEXT_X14 CONTEXT_X13+8
+#define CONTEXT_X15 CONTEXT_X14+8
+#define CONTEXT_X16 CONTEXT_X15+8
+#define CONTEXT_X17 CONTEXT_X16+8
+#define CONTEXT_X18 CONTEXT_X17+8
+#define CONTEXT_X19 CONTEXT_X18+8
+#define CONTEXT_X20 CONTEXT_X19+8
+#define CONTEXT_X21 CONTEXT_X20+8
+#define CONTEXT_X22 CONTEXT_X21+8
+#define CONTEXT_X23 CONTEXT_X22+8
+#define CONTEXT_X24 CONTEXT_X23+8
+#define CONTEXT_X25 CONTEXT_X24+8
+#define CONTEXT_X26 CONTEXT_X25+8
+#define CONTEXT_X27 CONTEXT_X26+8
+#define CONTEXT_X28 CONTEXT_X27+8
+#define CONTEXT_Fp CONTEXT_X28+8
+#define CONTEXT_Lr CONTEXT_Fp+8
+#define CONTEXT_Sp CONTEXT_Lr+8
+#define CONTEXT_Pc CONTEXT_Sp+8
+#define CONTEXT_NEON_OFFSET CONTEXT_Pc+8
+#define CONTEXT_V0 0
+#define CONTEXT_V1 CONTEXT_V0+16
+#define CONTEXT_V2 CONTEXT_V1+16
+#define CONTEXT_V3 CONTEXT_V2+16
+#define CONTEXT_V4 CONTEXT_V3+16
+#define CONTEXT_V5 CONTEXT_V4+16
+#define CONTEXT_V6 CONTEXT_V5+16
+#define CONTEXT_V7 CONTEXT_V6+16
+#define CONTEXT_V8 CONTEXT_V7+16
+#define CONTEXT_V9 CONTEXT_V8+16
+#define CONTEXT_V10 CONTEXT_V9+16
+#define CONTEXT_V11 CONTEXT_V10+16
+#define CONTEXT_V12 CONTEXT_V11+16
+#define CONTEXT_V13 CONTEXT_V12+16
+#define CONTEXT_V14 CONTEXT_V13+16
+#define CONTEXT_V15 CONTEXT_V14+16
+#define CONTEXT_V16 CONTEXT_V15+16
+#define CONTEXT_V17 CONTEXT_V16+16
+#define CONTEXT_V18 CONTEXT_V17+16
+#define CONTEXT_V19 CONTEXT_V18+16
+#define CONTEXT_V20 CONTEXT_V19+16
+#define CONTEXT_V21 CONTEXT_V20+16
+#define CONTEXT_V22 CONTEXT_V21+16
+#define CONTEXT_V23 CONTEXT_V22+16
+#define CONTEXT_V24 CONTEXT_V23+16
+#define CONTEXT_V25 CONTEXT_V24+16
+#define CONTEXT_V26 CONTEXT_V25+16
+#define CONTEXT_V27 CONTEXT_V26+16
+#define CONTEXT_V28 CONTEXT_V27+16
+#define CONTEXT_V29 CONTEXT_V28+16
+#define CONTEXT_V30 CONTEXT_V29+16
+#define CONTEXT_V31 CONTEXT_V30+16
+#define CONTEXT_FLOAT_CONTROL_OFFSET CONTEXT_V31
+#define CONTEXT_Fpcr 0
+#define CONTEXT_Fpsr CONTEXT_Fpcr+4
+
+// Incoming:
+// x0: Context*
+//
+LEAF_ENTRY CONTEXT_CaptureContext, _TEXT
+ sub sp, sp, #32
+ // save x1, x2 and x3 on stack so we can use them as scratch
+ stp x1, x2, [sp]
+ str x3, [sp, 16]
+ // save the current flags on the stack
+ mrs x1, nzcv
+ str x1, [sp, 24]
+
+ ldr w1, [x0, CONTEXT_ContextFlags]
+ // clangs assembler doesn't seem to support the mov Wx, imm32 yet
+ movz w2, #0x40, lsl #16
+ movk w2, #0x1
+ mov w3, w2
+ and w2, w1, w2
+ cmp w2, w3
+ b.ne LOCAL_LABEL(Done_CONTEXT_CONTROL)
+
+ // save the cpsr
+ ldr x2, [sp, 24]
+ str w2, [x0, CONTEXT_Cpsr]
+ stp fp, lr, [x0, CONTEXT_Fp]
+ add sp, sp, #32
+ mov x2, sp
+ stp x2, lr, [x0, CONTEXT_Sp]
+ sub sp, sp, #32
+
+LOCAL_LABEL(Done_CONTEXT_CONTROL):
+ // we dont clobber x1 in the CONTEXT_CONTROL case
+ // ldr w1, [x0, CONTEXT_ContextFlags]
+ // clangs assembler doesn't seem to support the mov Wx, imm32 yet
+ movz w2, #0x40, lsl #16
+ movk w2, #0x2
+ mov w3, w2
+ and w2, w1, w2
+ cmp w2, w3
+ b.ne LOCAL_LABEL(Done_CONTEXT_INTEGER)
+
+ ldp x1, x2, [sp]
+ ldr x3, [sp, 16]
+
+ stp x0, x1, [x0, CONTEXT_X0]
+ stp x2, x3, [x0, CONTEXT_X2]
+ stp x4, x5, [x0, CONTEXT_X4]
+ stp x6, x7, [x0, CONTEXT_X6]
+ stp x8, x9, [x0, CONTEXT_X8]
+ stp x10, x11, [x0, CONTEXT_X10]
+ stp x12, x13, [x0, CONTEXT_X12]
+ stp x14, x15, [x0, CONTEXT_X14]
+ stp x16, x17, [x0, CONTEXT_X16]
+ stp x18, x19, [x0, CONTEXT_X18]
+ stp x20, x21, [x0, CONTEXT_X20]
+ stp x22, x23, [x0, CONTEXT_X22]
+ stp x24, x25, [x0, CONTEXT_X24]
+ stp x26, x27, [x0, CONTEXT_X26]
+ str x28, [x0, CONTEXT_X28]
+
+
+LOCAL_LABEL(Done_CONTEXT_INTEGER):
+ ldr w1, [x0, CONTEXT_ContextFlags]
+ // clangs assembler doesn't seem to support the mov Wx, imm32 yet
+ movz w2, #0x40, lsl #16
+ movk w2, #0x4
+ mov w3, w2
+ and w2, w1, w2
+ cmp w2, w3
+ b.ne LOCAL_LABEL(Done_CONTEXT_FLOATING_POINT)
+
+ add x0, x0, CONTEXT_NEON_OFFSET
+ stp q0, q1, [x0, CONTEXT_V0]
+ stp q2, q3, [x0, CONTEXT_V2]
+ stp q4, q5, [x0, CONTEXT_V4]
+ stp q6, q7, [x0, CONTEXT_V6]
+ stp q8, q9, [x0, CONTEXT_V8]
+ stp q10, q11, [x0, CONTEXT_V10]
+ stp q12, q13, [x0, CONTEXT_V12]
+ stp q14, q15, [x0, CONTEXT_V14]
+ stp q16, q17, [x0, CONTEXT_V16]
+ stp q18, q19, [x0, CONTEXT_V18]
+ stp q20, q21, [x0, CONTEXT_V20]
+ stp q22, q23, [x0, CONTEXT_V22]
+ stp q24, q25, [x0, CONTEXT_V24]
+ stp q26, q27, [x0, CONTEXT_V26]
+ stp q28, q29, [x0, CONTEXT_V28]
+ stp q30, q31, [x0, CONTEXT_V30]
+ add x0, x0, CONTEXT_FLOAT_CONTROL_OFFSET
+ mrs x1, fpcr
+ mrs x2, fpsr
+ sub x0, x0, CONTEXT_FLOAT_CONTROL_OFFSET
+ stp x1, x2, [x0, CONTEXT_Fpcr]
+ sub x0, x0, CONTEXT_NEON_OFFSET
+
+LOCAL_LABEL(Done_CONTEXT_FLOATING_POINT):
+
+ add sp, sp, #32
+ ret
+LEAF_END CONTEXT_CaptureContext, _TEXT
+
+// Incoming:
+// x0: Context*
+
+LEAF_ENTRY RtlCaptureContext, _TEXT
+ sub sp, sp, #16
+ str x1, [sp]
+ // same as above, clang doesn't like mov with #imm32
+ // keep this in sync if CONTEXT_FULL changes
+ movz w1, #0x40, lsl #16
+ orr w1, w1, #0x1
+ orr w1, w1, #0x2
+ orr w1, w1, #0x4
+ orr w1, w1, #0x8
+ str w1, [x0, CONTEXT_ContextFlags]
+ ldr x1, [sp]
+ add sp, sp, #16
+ b C_FUNC(CONTEXT_CaptureContext)
+LEAF_END RtlCaptureContext, _TEXT
+
+// Incoming:
+// x0: Context*
+// x1: Exception*
+//
+LEAF_ENTRY RtlRestoreContext, _TEXT
+ // aarch64 specifies:
+ // IP0 and IP1, the Intra-Procedure Call temporary registers,
+ // are available for use by e.g. veneers or branch islands during a procedure call.
+ // They are otherwise corruptible.
+ // Since we cannot control $pc directly, we're going to corrupt x16 and x17
+ // so that we can restore control
+ // since we potentially clobber x0 below, we'll bank it in x16
+ mov x16, x0
+
+ ldr w2, [x16, CONTEXT_ContextFlags]
+ // clangs assembler doesn't seem to support the mov Wx, imm32 yet
+ movz w3, #0x40, lsl #16
+ movk w3, #0x4
+ mov w4, w3
+ and w3, w2, w3
+ cmp w3, w4
+ b.ne LOCAL_LABEL(No_Restore_CONTEXT_FLOATING_POINT)
+
+ add x16, x16, CONTEXT_NEON_OFFSET
+ ldp q0, q1, [x16, CONTEXT_V0]
+ ldp q2, q3, [x16, CONTEXT_V2]
+ ldp q4, q5, [x16, CONTEXT_V4]
+ ldp q6, q7, [x16, CONTEXT_V6]
+ ldp q8, q9, [x16, CONTEXT_V8]
+ ldp q10, q11, [x16, CONTEXT_V10]
+ ldp q12, q13, [x16, CONTEXT_V12]
+ ldp q14, q15, [x16, CONTEXT_V14]
+ ldp q16, q17, [x16, CONTEXT_V16]
+ ldp q18, q19, [x16, CONTEXT_V18]
+ ldp q20, q21, [x16, CONTEXT_V20]
+ ldp q22, q23, [x16, CONTEXT_V22]
+ ldp q24, q25, [x16, CONTEXT_V24]
+ ldp q26, q27, [x16, CONTEXT_V26]
+ ldp q28, q29, [x16, CONTEXT_V28]
+ ldp q30, q31, [x16, CONTEXT_V30]
+ ldp x1, x2, [x16, CONTEXT_Fpcr]
+ msr fpcr, x1
+ msr fpsr, x2
+ sub x16, x16, CONTEXT_NEON_OFFSET
+
+LOCAL_LABEL(No_Restore_CONTEXT_FLOATING_POINT):
+ movz w2, #0x40, lsl #16
+ movk w2, #0x2
+ mov w3, w2
+ and w2, w1, w2
+ cmp w2, w3
+ b.ne LOCAL_LABEL(No_Restore_CONTEXT_INTEGER)
+
+ ldp x0, x1, [x16, CONTEXT_X0]
+ ldp x2, x3, [x16, CONTEXT_X2]
+ ldp x4, x5, [x16, CONTEXT_X4]
+ ldp x6, x7, [x16, CONTEXT_X6]
+ ldp x8, x9, [x16, CONTEXT_X8]
+ ldp x10, x11, [x16, CONTEXT_X10]
+ ldp x12, x13, [x16, CONTEXT_X12]
+ ldp x14, x15, [x16, CONTEXT_X14]
+ ldp x18, x19, [x16, CONTEXT_X18]
+ ldp x20, x21, [x16, CONTEXT_X20]
+ ldp x22, x23, [x16, CONTEXT_X22]
+ ldp x24, x25, [x16, CONTEXT_X24]
+ ldp x26, x27, [x16, CONTEXT_X26]
+ ldr x28, [x16, CONTEXT_X28]
+
+LOCAL_LABEL(No_Restore_CONTEXT_INTEGER):
+ movz w2, #0x40, lsl #16
+ movk w2, #0x2
+ mov w3, w2
+ and w2, w1, w2
+ cmp w2, w3
+ b.ne LOCAL_LABEL(No_Restore_CONTEXT_CONTROL)
+
+ ldr w17, [x16, CONTEXT_Cpsr]
+ msr nzcv, x17
+ ldp fp, lr, [x16, CONTEXT_Fp]
+ ldr x17, [x16, CONTEXT_Sp]
+ mov sp, x17
+ ldr x17, [x16, CONTEXT_Pc]
+ br x17
+
+LOCAL_LABEL(No_Restore_CONTEXT_CONTROL):
+ ret
+
+LEAF_END RtlRestoreContext, _TEXT
diff --git a/src/pal/src/arch/arm64/debugbreak.S b/src/pal/src/arch/arm64/debugbreak.S
new file mode 100644
index 0000000000..0dc5bb6bd3
--- /dev/null
+++ b/src/pal/src/arch/arm64/debugbreak.S
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "unixasmmacros.inc"
+
+LEAF_ENTRY DBG_DebugBreak, _TEXT
+ EMIT_BREAKPOINT
+ ret
+LEAF_END_MARKED DBG_DebugBreak, _TEXT
+
diff --git a/src/pal/src/arch/arm64/exceptionhelper.S b/src/pal/src/arch/arm64/exceptionhelper.S
new file mode 100644
index 0000000000..4fdcfc5eb1
--- /dev/null
+++ b/src/pal/src/arch/arm64/exceptionhelper.S
@@ -0,0 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "unixasmmacros.inc"
+
+LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT
+ EMIT_BREAKPOINT
+LEAF_END ThrowExceptionFromContextInternal, _TEXT
diff --git a/src/pal/src/arch/arm64/processor.cpp b/src/pal/src/arch/arm64/processor.cpp
new file mode 100644
index 0000000000..6c7851a2b1
--- /dev/null
+++ b/src/pal/src/arch/arm64/processor.cpp
@@ -0,0 +1,41 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ processor.cpp
+
+Abstract:
+
+ Implementation of processor related functions for the ARM64
+ platform. These functions are processor dependent.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+
+/*++
+Function:
+YieldProcessor
+
+The YieldProcessor function signals to the processor to give resources
+to threads that are waiting for them. This macro is only effective on
+processors that support technology allowing multiple threads running
+on a single processor, such as Intel's Hyper-Threading technology.
+
+--*/
+void
+PALAPI
+YieldProcessor(
+ VOID)
+{
+ return;
+}
+
diff --git a/src/pal/src/arch/i386/activationhandlerwrapper.S b/src/pal/src/arch/i386/activationhandlerwrapper.S
new file mode 100644
index 0000000000..63f718e81f
--- /dev/null
+++ b/src/pal/src/arch/i386/activationhandlerwrapper.S
@@ -0,0 +1,30 @@
+// 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.
+
+.intel_syntax noprefix
+#include "unixasmmacros.inc"
+#include "asmconstants.h"
+
+#ifdef BIT64
+// Offset of the return address from the ActivationHandler in the ActivationHandlerWrapper
+.globl C_FUNC(ActivationHandlerReturnOffset)
+C_FUNC(ActivationHandlerReturnOffset):
+ .int LOCAL_LABEL(ActivationHandlerReturn)-C_FUNC(ActivationHandlerWrapper)
+
+NESTED_ENTRY ActivationHandlerWrapper, _TEXT, NoHandler
+ push_nonvol_reg rbp
+ mov rbp, rsp
+ alloc_stack (CONTEXT_Size)
+ set_cfa_register rbp, (2*8)
+ mov rdi, rsp
+ int3
+ call C_FUNC(ActivationHandler)
+LOCAL_LABEL(ActivationHandlerReturn):
+ int3
+ free_stack (CONTEXT_Size)
+ pop_nonvol_reg rbp
+ ret
+NESTED_END ActivationHandlerWrapper, _TEXT
+
+#endif // BIT64
diff --git a/src/pal/src/arch/i386/asmconstants.h b/src/pal/src/arch/i386/asmconstants.h
new file mode 100644
index 0000000000..182c1191e4
--- /dev/null
+++ b/src/pal/src/arch/i386/asmconstants.h
@@ -0,0 +1,106 @@
+// 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.
+
+#ifdef BIT64
+
+#define CONTEXT_AMD64 0x100000
+
+#define CONTEXT_CONTROL 1 // SegSs, Rsp, SegCs, Rip, and EFlags
+#define CONTEXT_INTEGER 2 // Rax, Rcx, Rdx, Rbx, Rbp, Rsi, Rdi, R8-R15
+#define CONTEXT_SEGMENTS 4 // SegDs, SegEs, SegFs, SegGs
+#define CONTEXT_FLOATING_POINT 8
+#define CONTEXT_DEBUG_REGISTERS 16 // Dr0-Dr3 and Dr6-Dr7
+
+#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT)
+
+#define CONTEXT_XSTATE 64
+
+#define CONTEXT_ContextFlags 6*8
+#define CONTEXT_SegCs CONTEXT_ContextFlags+8
+#define CONTEXT_SegDs CONTEXT_SegCs+2
+#define CONTEXT_SegEs CONTEXT_SegDs+2
+#define CONTEXT_SegFs CONTEXT_SegEs+2
+#define CONTEXT_SegGs CONTEXT_SegFs+2
+#define CONTEXT_SegSs CONTEXT_SegGs+2
+#define CONTEXT_EFlags CONTEXT_SegSs+2
+#define CONTEXT_Dr0 CONTEXT_EFlags+4
+#define CONTEXT_Dr1 CONTEXT_Dr0+8
+#define CONTEXT_Dr2 CONTEXT_Dr1+8
+#define CONTEXT_Dr3 CONTEXT_Dr2+8
+#define CONTEXT_Dr6 CONTEXT_Dr3+8
+#define CONTEXT_Dr7 CONTEXT_Dr6+8
+#define CONTEXT_Rax CONTEXT_Dr7+8
+#define CONTEXT_Rcx CONTEXT_Rax+8
+#define CONTEXT_Rdx CONTEXT_Rcx+8
+#define CONTEXT_Rbx CONTEXT_Rdx+8
+#define CONTEXT_Rsp CONTEXT_Rbx+8
+#define CONTEXT_Rbp CONTEXT_Rsp+8
+#define CONTEXT_Rsi CONTEXT_Rbp+8
+#define CONTEXT_Rdi CONTEXT_Rsi+8
+#define CONTEXT_R8 CONTEXT_Rdi+8
+#define CONTEXT_R9 CONTEXT_R8+8
+#define CONTEXT_R10 CONTEXT_R9+8
+#define CONTEXT_R11 CONTEXT_R10+8
+#define CONTEXT_R12 CONTEXT_R11+8
+#define CONTEXT_R13 CONTEXT_R12+8
+#define CONTEXT_R14 CONTEXT_R13+8
+#define CONTEXT_R15 CONTEXT_R14+8
+#define CONTEXT_Rip CONTEXT_R15+8
+#define CONTEXT_FltSave CONTEXT_Rip+8
+#define FLOATING_SAVE_AREA_SIZE 4*8+24*16+96
+#define CONTEXT_Xmm0 CONTEXT_FltSave+10*16
+#define CONTEXT_Xmm1 CONTEXT_Xmm0+16
+#define CONTEXT_Xmm2 CONTEXT_Xmm1+16
+#define CONTEXT_Xmm3 CONTEXT_Xmm2+16
+#define CONTEXT_Xmm4 CONTEXT_Xmm3+16
+#define CONTEXT_Xmm5 CONTEXT_Xmm4+16
+#define CONTEXT_Xmm6 CONTEXT_Xmm5+16
+#define CONTEXT_Xmm7 CONTEXT_Xmm6+16
+#define CONTEXT_Xmm8 CONTEXT_Xmm7+16
+#define CONTEXT_Xmm9 CONTEXT_Xmm8+16
+#define CONTEXT_Xmm10 CONTEXT_Xmm9+16
+#define CONTEXT_Xmm11 CONTEXT_Xmm10+16
+#define CONTEXT_Xmm12 CONTEXT_Xmm11+16
+#define CONTEXT_Xmm13 CONTEXT_Xmm12+16
+#define CONTEXT_Xmm14 CONTEXT_Xmm13+16
+#define CONTEXT_Xmm15 CONTEXT_Xmm14+16
+#define CONTEXT_VectorRegister CONTEXT_FltSave+FLOATING_SAVE_AREA_SIZE
+#define CONTEXT_VectorControl CONTEXT_VectorRegister+16*26
+#define CONTEXT_DebugControl CONTEXT_VectorControl+8
+#define CONTEXT_LastBranchToRip CONTEXT_DebugControl+8
+#define CONTEXT_LastBranchFromRip CONTEXT_LastBranchToRip+8
+#define CONTEXT_LastExceptionToRip CONTEXT_LastBranchFromRip+8
+#define CONTEXT_LastExceptionFromRip CONTEXT_LastExceptionToRip+8
+#define CONTEXT_Size CONTEXT_LastExceptionFromRip+8
+
+#else // BIT64
+
+#define CONTEXT_ContextFlags 0
+#define CONTEXT_FLOATING_POINT 8
+#define CONTEXT_FloatSave 7*4
+#define FLOATING_SAVE_AREA_SIZE 8*4+80
+#define CONTEXT_Edi CONTEXT_FloatSave + FLOATING_SAVE_AREA_SIZE + 4*4
+#define CONTEXT_Esi CONTEXT_Edi+4
+#define CONTEXT_Ebx CONTEXT_Esi+4
+#define CONTEXT_Edx CONTEXT_Ebx+4
+#define CONTEXT_Ecx CONTEXT_Edx+4
+#define CONTEXT_Eax CONTEXT_Ecx+4
+#define CONTEXT_Ebp CONTEXT_Eax+4
+#define CONTEXT_Eip CONTEXT_Ebp+4
+#define CONTEXT_SegCs CONTEXT_Eip+4
+#define CONTEXT_EFlags CONTEXT_SegCs+4
+#define CONTEXT_Esp CONTEXT_EFlags+4
+#define CONTEXT_SegSs CONTEXT_Esp+4
+#define CONTEXT_EXTENDED_REGISTERS 32
+#define CONTEXT_ExtendedRegisters CONTEXT_SegSs+4
+#define CONTEXT_Xmm0 CONTEXT_ExtendedRegisters+160
+#define CONTEXT_Xmm1 CONTEXT_Xmm0+16
+#define CONTEXT_Xmm2 CONTEXT_Xmm1+16
+#define CONTEXT_Xmm3 CONTEXT_Xmm2+16
+#define CONTEXT_Xmm4 CONTEXT_Xmm3+16
+#define CONTEXT_Xmm5 CONTEXT_Xmm4+16
+#define CONTEXT_Xmm6 CONTEXT_Xmm5+16
+#define CONTEXT_Xmm7 CONTEXT_Xmm6+16
+
+#endif // BIT64
diff --git a/src/pal/src/arch/i386/context.S b/src/pal/src/arch/i386/context.S
new file mode 100644
index 0000000000..f8a2dca89c
--- /dev/null
+++ b/src/pal/src/arch/i386/context.S
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+#if defined(_DEBUG)
+ .text
+ .globl _DBG_CheckStackAlignment
+
+_DBG_CheckStackAlignment:
+ // Prolog - at this point we are at aligned - 8 (for the call)
+ pushq %rbp // aligned -16
+ movq %rsp, %rbp
+
+ testl $0xf,%esp // can get away with esp even on AMD64.
+ jz .+3
+ int3
+
+ // Epilog
+ popq %rbp
+ ret
+#endif
diff --git a/src/pal/src/arch/i386/context2.S b/src/pal/src/arch/i386/context2.S
new file mode 100644
index 0000000000..0e93e81a55
--- /dev/null
+++ b/src/pal/src/arch/i386/context2.S
@@ -0,0 +1,259 @@
+// 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.
+//
+// Implementation of _CONTEXT_CaptureContext for the Intel x86 platform.
+// This function is processor dependent. It is used by exception handling,
+// and is always apply to the current thread.
+//
+
+.intel_syntax noprefix
+#include "unixasmmacros.inc"
+#include "asmconstants.h"
+
+#ifdef BIT64
+
+#define IRETFRAME_Rip 0
+#define IRETFRAME_SegCs IRETFRAME_Rip+8
+#define IRETFRAME_EFlags IRETFRAME_SegCs+8
+#define IRETFRAME_Rsp IRETFRAME_EFlags+8
+#define IRETFRAME_SegSs IRETFRAME_Rsp+8
+#define IRetFrameLength IRETFRAME_SegSs+8
+#define IRetFrameLengthAligned 16*((IRetFrameLength+8)/16)
+
+// Incoming:
+// RDI: Context*
+//
+LEAF_ENTRY CONTEXT_CaptureContext, _TEXT
+ // Save processor flags before calling any of the following 'test' instructions
+ // because they will modify state of some flags
+ push_eflags
+ END_PROLOGUE
+
+ test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_INTEGER
+ je LOCAL_LABEL(Done_CONTEXT_INTEGER)
+ mov [rdi + CONTEXT_Rdi], rdi
+ mov [rdi + CONTEXT_Rsi], rsi
+ mov [rdi + CONTEXT_Rbx], rbx
+ mov [rdi + CONTEXT_Rdx], rdx
+ mov [rdi + CONTEXT_Rcx], rcx
+ mov [rdi + CONTEXT_Rax], rax
+ mov [rdi + CONTEXT_Rbp], rbp
+ mov [rdi + CONTEXT_R8], r8
+ mov [rdi + CONTEXT_R9], r9
+ mov [rdi + CONTEXT_R10], r10
+ mov [rdi + CONTEXT_R11], r11
+ mov [rdi + CONTEXT_R12], r12
+ mov [rdi + CONTEXT_R13], r13
+ mov [rdi + CONTEXT_R14], r14
+ mov [rdi + CONTEXT_R15], r15
+LOCAL_LABEL(Done_CONTEXT_INTEGER):
+
+ test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_CONTROL
+ je LOCAL_LABEL(Done_CONTEXT_CONTROL)
+
+ // Return address is @ (RSP + 8)
+ mov rdx, [rsp + 8]
+ mov [rdi + CONTEXT_Rip], rdx
+.att_syntax
+ mov %cs, CONTEXT_SegCs(%rdi)
+.intel_syntax noprefix
+ // Get the value of EFlags that was pushed on stack at the beginning of the function
+ mov rdx, [rsp]
+ mov [rdi + CONTEXT_EFlags], edx
+ lea rdx, [rsp + 16]
+ mov [rdi + CONTEXT_Rsp], rdx
+.att_syntax
+ mov %ss, CONTEXT_SegSs(%rdi)
+.intel_syntax noprefix
+LOCAL_LABEL(Done_CONTEXT_CONTROL):
+
+ // Need to double check this is producing the right result
+ // also that FFSXR (fast save/restore) is not turned on
+ // otherwise it omits the xmm registers.
+ test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_FLOATING_POINT
+ je LOCAL_LABEL(Done_CONTEXT_FLOATING_POINT)
+ fxsave [rdi + CONTEXT_FltSave]
+LOCAL_LABEL(Done_CONTEXT_FLOATING_POINT):
+
+ test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_DEBUG_REGISTERS
+ je LOCAL_LABEL(Done_CONTEXT_DEBUG_REGISTERS)
+ mov rdx, dr0
+ mov [rdi + CONTEXT_Dr0], rdx
+ mov rdx, dr1
+ mov [rdi + CONTEXT_Dr1], rdx
+ mov rdx, dr2
+ mov [rdi + CONTEXT_Dr2], rdx
+ mov rdx, dr3
+ mov [rdi + CONTEXT_Dr3], rdx
+ mov rdx, dr6
+ mov [rdi + CONTEXT_Dr6], rdx
+ mov rdx, dr7
+ mov [rdi + CONTEXT_Dr7], rdx
+LOCAL_LABEL(Done_CONTEXT_DEBUG_REGISTERS):
+
+ free_stack 8
+ ret
+LEAF_END CONTEXT_CaptureContext, _TEXT
+
+LEAF_ENTRY RtlCaptureContext, _TEXT
+ mov DWORD PTR [rdi + CONTEXT_ContextFlags], (CONTEXT_AMD64 | CONTEXT_FULL | CONTEXT_SEGMENTS)
+ jmp C_FUNC(CONTEXT_CaptureContext)
+LEAF_END RtlCaptureContext, _TEXT
+
+LEAF_ENTRY RtlRestoreContext, _TEXT
+ push_nonvol_reg rbp
+ alloc_stack (IRetFrameLengthAligned)
+
+ test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_DEBUG_REGISTERS
+ je LOCAL_LABEL(Done_Restore_CONTEXT_DEBUG_REGISTERS)
+ mov rdx, [rdi + CONTEXT_Dr0]
+ mov dr0, rdx
+ mov rdx, [rdi + CONTEXT_Dr1]
+ mov dr1, rdx
+ mov rdx, [rdi + CONTEXT_Dr2]
+ mov dr2, rdx
+ mov rdx, [rdi + CONTEXT_Dr3]
+ mov dr3, rdx
+ mov rdx, [rdi + CONTEXT_Dr6]
+ mov dr6, rdx
+ mov rdx, [rdi + CONTEXT_Dr7]
+ mov dr7, rdx
+LOCAL_LABEL(Done_Restore_CONTEXT_DEBUG_REGISTERS):
+
+ test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_FLOATING_POINT
+ je LOCAL_LABEL(Done_Restore_CONTEXT_FLOATING_POINT)
+ fxrstor [rdi + CONTEXT_FltSave]
+LOCAL_LABEL(Done_Restore_CONTEXT_FLOATING_POINT):
+
+ test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_XSTATE
+ je LOCAL_LABEL(Done_Restore_CONTEXT_XSTATE)
+
+ // Restore the extended state (for now, this is just the upper halves of YMM registers)
+ vinsertf128 ymm0, ymm0, xmmword ptr [rdi + (CONTEXT_VectorRegister + 0 * 16)], 1
+ vinsertf128 ymm1, ymm1, xmmword ptr [rdi + (CONTEXT_VectorRegister + 1 * 16)], 1
+ vinsertf128 ymm2, ymm2, xmmword ptr [rdi + (CONTEXT_VectorRegister + 2 * 16)], 1
+ vinsertf128 ymm3, ymm3, xmmword ptr [rdi + (CONTEXT_VectorRegister + 3 * 16)], 1
+ vinsertf128 ymm4, ymm4, xmmword ptr [rdi + (CONTEXT_VectorRegister + 4 * 16)], 1
+ vinsertf128 ymm5, ymm5, xmmword ptr [rdi + (CONTEXT_VectorRegister + 5 * 16)], 1
+ vinsertf128 ymm6, ymm6, xmmword ptr [rdi + (CONTEXT_VectorRegister + 6 * 16)], 1
+ vinsertf128 ymm7, ymm7, xmmword ptr [rdi + (CONTEXT_VectorRegister + 7 * 16)], 1
+ vinsertf128 ymm8, ymm8, xmmword ptr [rdi + (CONTEXT_VectorRegister + 8 * 16)], 1
+ vinsertf128 ymm9, ymm9, xmmword ptr [rdi + (CONTEXT_VectorRegister + 9 * 16)], 1
+ vinsertf128 ymm10, ymm10, xmmword ptr [rdi + (CONTEXT_VectorRegister + 10 * 16)], 1
+ vinsertf128 ymm11, ymm11, xmmword ptr [rdi + (CONTEXT_VectorRegister + 11 * 16)], 1
+ vinsertf128 ymm12, ymm12, xmmword ptr [rdi + (CONTEXT_VectorRegister + 12 * 16)], 1
+ vinsertf128 ymm13, ymm13, xmmword ptr [rdi + (CONTEXT_VectorRegister + 13 * 16)], 1
+ vinsertf128 ymm14, ymm14, xmmword ptr [rdi + (CONTEXT_VectorRegister + 14 * 16)], 1
+ vinsertf128 ymm15, ymm15, xmmword ptr [rdi + (CONTEXT_VectorRegister + 15 * 16)], 1
+LOCAL_LABEL(Done_Restore_CONTEXT_XSTATE):
+
+ test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_CONTROL
+ je LOCAL_LABEL(Done_Restore_CONTEXT_CONTROL)
+
+ // The control registers are restored via the iret instruction
+ // so we build the frame for the iret on the stack.
+#ifdef __APPLE__
+.att_syntax
+ // On OSX, we cannot read SS via the thread_get_context and RtlRestoreContext
+ // needs to be used on context extracted by thread_get_context. So we
+ // don't change the SS.
+ mov %ss, %ax
+.intel_syntax noprefix
+#else
+ mov ax, [rdi + CONTEXT_SegSs]
+#endif
+ mov [rsp + IRETFRAME_SegSs], ax
+ mov rax, [rdi + CONTEXT_Rsp]
+ mov [rsp + IRETFRAME_Rsp], rax
+ mov eax, [rdi + CONTEXT_EFlags]
+ mov [rsp + IRETFRAME_EFlags], eax
+ mov ax, [rdi + CONTEXT_SegCs]
+ mov [rsp + IRETFRAME_SegCs], ax
+ mov rax, [rdi + CONTEXT_Rip]
+ mov [rsp + IRETFRAME_Rip], rax
+
+LOCAL_LABEL(Done_Restore_CONTEXT_CONTROL):
+ // Remember the result of the test for the CONTEXT_CONTROL
+ push_eflags
+ test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_INTEGER
+ je LOCAL_LABEL(Done_Restore_CONTEXT_INTEGER)
+ mov rsi, [rdi + CONTEXT_Rsi]
+ mov rbx, [rdi + CONTEXT_Rbx]
+ mov rdx, [rdi + CONTEXT_Rdx]
+ mov rcx, [rdi + CONTEXT_Rcx]
+ mov rax, [rdi + CONTEXT_Rax]
+ mov rbp, [rdi + CONTEXT_Rbp]
+ mov r8, [rdi + CONTEXT_R8]
+ mov r9, [rdi + CONTEXT_R9]
+ mov r10, [rdi + CONTEXT_R10]
+ mov r11, [rdi + CONTEXT_R11]
+ mov r12, [rdi + CONTEXT_R12]
+ mov r13, [rdi + CONTEXT_R13]
+ mov r14, [rdi + CONTEXT_R14]
+ mov r15, [rdi + CONTEXT_R15]
+ mov rdi, [rdi + CONTEXT_Rdi]
+LOCAL_LABEL(Done_Restore_CONTEXT_INTEGER):
+
+ // Restore the result of the test for the CONTEXT_CONTROL
+ pop_eflags
+ je LOCAL_LABEL(No_Restore_CONTEXT_CONTROL)
+ // The function was asked to restore the control registers, so
+ // we perform iretq that restores them all.
+ // We don't return to the caller in this case.
+ iretq
+LOCAL_LABEL(No_Restore_CONTEXT_CONTROL):
+
+ // The function was not asked to restore the control registers
+ // so we return back to the caller.
+ free_stack (IRetFrameLengthAligned)
+ pop_nonvol_reg rbp
+ ret
+LEAF_END RtlRestoreContext, _TEXT
+
+#else
+
+ .globl C_FUNC(CONTEXT_CaptureContext)
+C_FUNC(CONTEXT_CaptureContext):
+ push %eax
+ mov 8(%esp), %eax
+ mov %edi, CONTEXT_Edi(%eax)
+ mov %esi, CONTEXT_Esi(%eax)
+ mov %ebx, CONTEXT_Ebx(%eax)
+ mov %edx, CONTEXT_Edx(%eax)
+ mov %ecx, CONTEXT_Ecx(%eax)
+ pop %ecx
+ mov %ecx, CONTEXT_Eax(%eax)
+ mov %ebp, CONTEXT_Ebp(%eax)
+ mov (%esp), %edx
+ mov %edx, CONTEXT_Eip(%eax)
+ push %cs
+ pop %edx
+ mov %edx, CONTEXT_SegCs(%eax)
+ pushf
+ pop %edx
+ mov %edx, CONTEXT_EFlags(%eax)
+ lea 4(%esp), %edx
+ mov %edx, CONTEXT_Esp(%eax)
+ push %ss
+ pop %edx
+ mov %edx, CONTEXT_SegSs(%eax)
+ testb $CONTEXT_FLOATING_POINT, CONTEXT_ContextFlags(%eax)
+ je 0f
+ fnsave CONTEXT_FloatSave(%eax)
+ frstor CONTEXT_FloatSave(%eax)
+0:
+ testb $CONTEXT_EXTENDED_REGISTERS, CONTEXT_ContextFlags(%eax)
+ je 2f
+ movdqu %xmm0, CONTEXT_Xmm0(%eax)
+ movdqu %xmm1, CONTEXT_Xmm1(%eax)
+ movdqu %xmm2, CONTEXT_Xmm2(%eax)
+ movdqu %xmm3, CONTEXT_Xmm3(%eax)
+ movdqu %xmm4, CONTEXT_Xmm4(%eax)
+ movdqu %xmm5, CONTEXT_Xmm5(%eax)
+ movdqu %xmm6, CONTEXT_Xmm6(%eax)
+ movdqu %xmm7, CONTEXT_Xmm7(%eax)
+2:
+ ret
+
+#endif
diff --git a/src/pal/src/arch/i386/debugbreak.S b/src/pal/src/arch/i386/debugbreak.S
new file mode 100644
index 0000000000..3065e4064c
--- /dev/null
+++ b/src/pal/src/arch/i386/debugbreak.S
@@ -0,0 +1,12 @@
+// 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.
+
+.intel_syntax noprefix
+#include "unixasmmacros.inc"
+
+LEAF_ENTRY DBG_DebugBreak, _TEXT
+ int3
+ ret
+LEAF_END_MARKED DBG_DebugBreak, _TEXT
+
diff --git a/src/pal/src/arch/i386/dispatchexceptionwrapper.S b/src/pal/src/arch/i386/dispatchexceptionwrapper.S
new file mode 100644
index 0000000000..ee5ff468d6
--- /dev/null
+++ b/src/pal/src/arch/i386/dispatchexceptionwrapper.S
@@ -0,0 +1,51 @@
+// 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.
+
+// ==++==
+//
+
+// ==--==
+//
+// Implementation of the PAL_DispatchExceptionWrapper that is
+// interposed between a function that caused a hardware fault
+// and PAL_DispatchException that throws an SEH exception for
+// the fault, to make the stack unwindable.
+//
+
+.intel_syntax noprefix
+#include "unixasmmacros.inc"
+
+// Offset of the return address from the PAL_DispatchException in the PAL_DispatchExceptionWrapper
+.globl C_FUNC(PAL_DispatchExceptionReturnOffset)
+C_FUNC(PAL_DispatchExceptionReturnOffset):
+ .int LOCAL_LABEL(PAL_DispatchExceptionReturn) - C_FUNC(PAL_DispatchExceptionWrapper)
+
+//
+// PAL_DispatchExceptionWrapper will never be called; it only serves
+// to be referenced from a stack frame on the faulting thread. Its
+// unwinding behavior is equivalent to any standard function having
+// an ebp frame. It is analogous to the following source file.
+//
+// extern "C" void PAL_DispatchException(CONTEXT *pContext, EXCEPTION_RECORD *pExceptionRecord, MachExceptionInfo *pMachExceptionInfo);
+//
+// extern "C" void PAL_DispatchExceptionWrapper()
+// {
+// CONTEXT Context;
+// EXCEPTION_RECORD ExceptionRecord;
+// MachExceptionInfo MachExceptionInfo;
+// PAL_DispatchException(&Context, &ExceptionRecord, &MachExceptionInfo);
+// }
+//
+
+NESTED_ENTRY PAL_DispatchExceptionWrapper, _TEXT, NoHandler
+ push_nonvol_reg rbp
+ mov rbp, rsp
+ set_cfa_register rbp, (2*8)
+ int3
+ call C_FUNC(PAL_DispatchException)
+LOCAL_LABEL(PAL_DispatchExceptionReturn):
+ int3
+ pop_nonvol_reg rbp
+ ret
+NESTED_END PAL_DispatchExceptionWrapper, _TEXT
diff --git a/src/pal/src/arch/i386/exceptionhelper.S b/src/pal/src/arch/i386/exceptionhelper.S
new file mode 100644
index 0000000000..b7b34ace41
--- /dev/null
+++ b/src/pal/src/arch/i386/exceptionhelper.S
@@ -0,0 +1,42 @@
+// 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.
+
+.intel_syntax noprefix
+#include "unixasmmacros.inc"
+#include "asmconstants.h"
+
+//////////////////////////////////////////////////////////////////////////
+//
+// This function creates a stack frame right below the target frame, restores all callee
+// saved registers from the passed in context, sets the RSP to that frame and sets the
+// return address to the target frame's RIP.
+// Then it uses the ThrowExceptionHelper to throw the passed in exception from that context.
+// EXTERN_C void ThrowExceptionFromContextInternal(CONTEXT* context, PAL_SEHException* ex);
+LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT
+ // Save the RBP to the stack so that the unwind can work at the instruction after
+ // loading the RBP from the context, but before loading the RSP from the context.
+ push_nonvol_reg rbp
+ mov r12, [rdi + CONTEXT_R12]
+ mov r13, [rdi + CONTEXT_R13]
+ mov r14, [rdi + CONTEXT_R14]
+ mov r15, [rdi + CONTEXT_R15]
+ mov rbx, [rdi + CONTEXT_Rbx]
+ mov rbp, [rdi + CONTEXT_Rbp]
+ mov rsp, [rdi + CONTEXT_Rsp]
+ // The RSP was set to the target frame's value, so the current function's
+ // CFA is now right at the RSP.
+ .cfi_def_cfa_offset 0
+
+ // Indicate that now that we have moved the RSP to the target address,
+ // the RBP is no longer saved in the current stack frame.
+ .cfi_restore rbp
+
+ mov rax, [rdi + CONTEXT_Rip]
+
+ // Store return address to the stack
+ push_register rax
+ // The PAL_SEHException pointer
+ mov rdi, rsi
+ jmp EXTERNAL_C_FUNC(ThrowExceptionHelper)
+LEAF_END ThrowExceptionFromContextInternal, _TEXT
diff --git a/src/pal/src/arch/i386/optimizedtls.cpp b/src/pal/src/arch/i386/optimizedtls.cpp
new file mode 100644
index 0000000000..910a6eb931
--- /dev/null
+++ b/src/pal/src/arch/i386/optimizedtls.cpp
@@ -0,0 +1,237 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ optimizedtls.cpp
+
+Abstract:
+
+ Implementation of platform-specific Thread local storage functions.
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+
+#include <pthread.h>
+
+#include "pal/dbgmsg.h"
+#include "pal/misc.h"
+#include "pal/debug.h"
+
+#include <stddef.h>
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(THREAD);
+
+#if defined(USE_OPTIMIZEDTLSGETTER)
+
+#define PAL_safe_offsetof(s,m) ((size_t)((ptrdiff_t)&(char&)(((s *)64)->m))-64)
+
+/*++
+Function:
+ CorUnix::TLSMakeOptimizedGetter
+
+ Creates a platform-optimized version of TlsGetValue compiled
+ for a particular index.
+
+ Generates the hot part of CorUnix::InternalGetCurrentThread
+ as a chunk of highly optimized machine-specific code at runtime.
+
+ Check the difference between CorUnix::InternalGetCurrentThread and
+ CorUnix::InternalGetCurrentThreadSlow to see the C/C++ code that matches
+ the code generated by this function.
+--*/
+PAL_POPTIMIZEDTLSGETTER
+CorUnix::TLSMakeOptimizedGetter(
+ IN CPalThread* pThread,
+ IN DWORD dwTlsIndex)
+{
+#ifdef BIT64
+#pragma unused(pThread, dwTlsIndex)
+ ERROR("TLSMakeOptimizedGetter not rewritten for amd64 yet.");
+ return NULL;
+#else
+ PAL_POPTIMIZEDTLSGETTER Ret = NULL;
+ BYTE* p;
+ int i = 0;
+
+#ifdef __APPLE__
+#define TLS_OPTIMIZED_GETTER_SIZE 118
+#else
+#define TLS_OPTIMIZED_GETTER_SIZE 115
+#endif
+
+ p = (BYTE*)InternalMalloc(pThread, TLS_OPTIMIZED_GETTER_SIZE * sizeof(BYTE));
+
+ if (p == NULL)
+ {
+ return Ret;
+ }
+
+ // Need to preserve %ecx, %edx, and %esi registers as specified in
+ // GetThreadGeneric(void) in vm/i386/asmhelpers.s
+ p[i++] = 0x51; // push %ecx
+ p[i++] = 0x52; // push %edx
+ p[i++] = 0x89; // mov %esp,%eax // %eax = sp;
+ p[i++] = 0xe0;
+ p[i++] = 0xc1; // shr $0x11,%eax // sp >> 17;
+ p[i++] = 0xe8;
+ p[i++] = 0x11;
+ p[i++] = 0x89; // mov %eax,%edx // key = sp >> 17;
+ p[i++] = 0xc2;
+ p[i++] = 0xc1; // sar $0x7,%edx // key >> 7;
+ p[i++] = 0xfa;
+ p[i++] = 0x07;
+ p[i++] = 0x29; // sub %edx,%eax // key -= key >> 7;
+ p[i++] = 0xd0;
+ p[i++] = 0x89; // mov %eax,%edx
+ p[i++] = 0xc2;
+ p[i++] = 0xc1; // sar $0x5,%edx // key >> 5;
+ p[i++] = 0xfa;
+ p[i++] = 0x05;
+ p[i++] = 0x29; // sub %edx,%eax // key -= key >> 5;
+ p[i++] = 0xd0;
+ p[i++] = 0x89; // mov %eax,%edx
+ p[i++] = 0xc2;
+ p[i++] = 0xc1; // sar $0x3,%edx // key >> 3;
+ p[i++] = 0xfa;
+ p[i++] = 0x03;
+ p[i++] = 0x29; // sub %edx,%eax // key -= key >> 3;
+ p[i++] = 0xd0;
+ p[i++] = 0x25; // and $0xff,%eax // key &= 0xFF;
+ p[i++] = 0xff;
+ p[i++] = 0x00;
+ p[i++] = 0x00;
+ p[i++] = 0x00;
+ p[i++] = 0x8b; // mov (flush_counter),%ecx // %ecx = counter = flush_counter;
+ p[i++] = 0x0d;
+ *((DWORD*) &p[i]) = (DWORD)&flush_counter;
+ i += sizeof(DWORD);
+ p[i++] = 0x8b; // mov (thread_hints,%eax,4),%eax // %edx = pThread = thread_hints[key];
+ p[i++] = 0x14;
+ p[i++] = 0x85;
+ *((DWORD*) &p[i]) = (DWORD)&thread_hints;
+ i += sizeof(DWORD);
+ p[i++] = 0x39; // cmp %esp,offsetof(CPalThread,tlsInfo)+offsetof(CThreadTLSInfo,minStack)(%edx)
+ // if ((size_t)pThread->tlsInfo.minStack <= sp)
+ p[i++] = 0xa2;
+ *((DWORD*) &p[i]) = (DWORD)(PAL_safe_offsetof(CPalThread,tlsInfo)+PAL_safe_offsetof(CThreadTLSInfo,minStack));
+ i += sizeof(DWORD);
+ p[i++] = 0x77; // ja CallInternalGetCurrentThreadSlow:
+ p[i++] = 0x19;
+ p[i++] = 0x3b; // cmp offsetof(CPalThread,tlsInfo)+offsetof(CThreadTLSInfo,maxStack)(%edx),%esp
+ // if (sp < (size_t)pThread->tlsInfo.maxStack)
+ p[i++] = 0xa2;
+ *((DWORD*) &p[i]) = (DWORD)(PAL_safe_offsetof(CPalThread,tlsInfo)+PAL_safe_offsetof(CThreadTLSInfo,maxStack));
+ i += sizeof(DWORD);
+ p[i++] = 0x73; // jae CallInternalGetCurrentThreadSlow:
+ p[i++] = 0x11;
+ p[i++] = 0x39; // cmp (flush_counter),%ecx // if (counter == flush_counter)
+ p[i++] = 0x0d;
+ *((DWORD*) &p[i]) = (DWORD)&flush_counter;
+ i += sizeof(DWORD);
+ p[i++] = 0x75; // jne CallInternalGetCurrentThreadSlow:
+ p[i++] = 0x09;
+ if (dwTlsIndex != THREAD_OBJECT_TLS_INDEX)
+ {
+ p[i++] = 0x8b; // mov offsetof(pThread->tlsSlots[dwTlsIndex])(%edx),%eax // %eax = pThread->tlsSlots[dwTlsIndex];
+ p[i++] = 0x82;
+ *((DWORD*) &p[i]) = (DWORD)(PAL_safe_offsetof(CPalThread,tlsInfo)+PAL_safe_offsetof(CThreadTLSInfo,tlsSlots[dwTlsIndex]));
+ i += sizeof(DWORD);
+ }
+ else
+ {
+ p[i++] = 0x89; // mov %edx,%eax // %eax = pThread;
+ p[i++] = 0xd0;
+ p[i++] = 0x90; // nop
+ p[i++] = 0x90; // nop
+ p[i++] = 0x90; // nop
+ p[i++] = 0x90; // nop
+ }
+ p[i++] = 0x5a; // pop %edx
+ p[i++] = 0x59; // pop %ecx
+ p[i++] = 0xc3; // ret
+ // CallInternalGetCurrentThreadSlow:
+ p[i++] = 0x5a; // pop %edx
+ p[i++] = 0x59; // pop %ecx
+ p[i++] = 0x8d; // lea (thread_hints,%eax,4),%eax // %eax = &thread_hints[key];
+ p[i++] = 0x04;
+ p[i++] = 0x85;
+ *((DWORD*) &p[i]) = (DWORD)&thread_hints;
+ i += sizeof(DWORD);
+ p[i++] = 0x55; // push %ebp
+ p[i++] = 0x89; // mov %esp,%ebp
+ p[i++] = 0xe5;
+ p[i++] = 0x51; // push %ecx
+ p[i++] = 0x89; // mov %esp,%ecx // this is the reference esp - need to match the reference esp used in the fast path.
+ p[i++] = 0xe1;
+ p[i++] = 0x52; // push %edx
+#ifdef __APPLE__
+ // establish 16-byte stack alignment
+ p[i++] = 0x83; // subl $8,%esp
+ p[i++] = 0xec;
+ p[i++] = 0x08;
+#endif
+ p[i++] = 0x50; // push %eax // store &thread_hints[key] on stack as 2nd argument;
+ p[i++] = 0x51; // push %ecx // reference esp - The 1st argument for call to InternalGetCurrentThreadSlow.
+ p[i++] = 0xe8; // call InternalGetCurrentThreadSlow
+ *((DWORD*) &p[i]) = (DWORD)&InternalGetCurrentThreadSlow - (DWORD)(&p[i+sizeof(DWORD)]);
+ i += sizeof(DWORD);
+#ifdef __APPLE__
+ p[i++] = 0x83; // addl $16,%esp
+ p[i++] = 0xc4;
+ p[i++] = 0x10;
+#else
+ p[i++] = 0x83; // addl $8,%esp
+ p[i++] = 0xc4;
+ p[i++] = 0x08;
+#endif
+ if (dwTlsIndex != THREAD_OBJECT_TLS_INDEX)
+ {
+ p[i++] = 0x8b; // mov offsetof(pThread->tlsSlots[dwTlsIndex])(%eax),%eax // %eax = pThread->tlsSlots[dwTlsIndex];
+ p[i++] = 0x80;
+ *((DWORD*) &p[i]) = (DWORD)(PAL_safe_offsetof(CPalThread,tlsInfo)+PAL_safe_offsetof(CThreadTLSInfo,tlsSlots[dwTlsIndex]));
+ i += sizeof(DWORD);
+ }
+ p[i++] = 0x5a; // pop %edx
+ p[i++] = 0x59; // pop %ecx
+ p[i++] = 0xc9; // leave
+ p[i++] = 0xc3; // ret
+
+ if (i > TLS_OPTIMIZED_GETTER_SIZE)
+ {
+ ASSERT("Invalid TLS_OPTIMIZED_GETTER_SIZE %d\n", i);
+ }
+
+ DBG_FlushInstructionCache(p, TLS_OPTIMIZED_GETTER_SIZE * sizeof(BYTE));
+
+ Ret = (PAL_POPTIMIZEDTLSGETTER)p;
+
+ return Ret;
+#endif // BIT64 else
+}
+
+/*++
+Function:
+ TLSFreeOptimizedGetter
+
+ Frees a function created by MakeOptimizedTlsGetter().
+--*/
+VOID
+CorUnix::TLSFreeOptimizedGetter(
+ IN PAL_POPTIMIZEDTLSGETTER pOptimizedTlsGetter)
+{
+ InternalFree(InternalGetCurrentThread(), (void *)pOptimizedTlsGetter);
+}
+
+#endif // USE_OPTIMIZEDTLSGETTER
diff --git a/src/pal/src/arch/i386/processor.cpp b/src/pal/src/arch/i386/processor.cpp
new file mode 100644
index 0000000000..4fd3a4abc8
--- /dev/null
+++ b/src/pal/src/arch/i386/processor.cpp
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ processor.cpp
+
+Abstract:
+
+ Implementation of processor related functions for the Intel x86/x64
+ platforms. These functions are processor dependent.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+
+/*++
+Function:
+YieldProcessor
+
+The YieldProcessor function signals to the processor to give resources
+to threads that are waiting for them. This macro is only effective on
+processors that support technology allowing multiple threads running
+on a single processor, such as Intel's Hyper-Threading technology.
+
+--*/
+void
+PALAPI
+YieldProcessor(
+ VOID)
+{
+ __asm__ __volatile__ (
+ "rep\n"
+ "nop"
+ );
+}
+
diff --git a/src/pal/src/build_tools/mdtool_dummy b/src/pal/src/build_tools/mdtool_dummy
new file mode 100644
index 0000000000..1005a3496f
--- /dev/null
+++ b/src/pal/src/build_tools/mdtool_dummy
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# 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.
+
+echo mdtool_dummy : not generating any dependencies
diff --git a/src/pal/src/build_tools/mdtool_gcc.in b/src/pal/src/build_tools/mdtool_gcc.in
new file mode 100644
index 0000000000..bef95d5cce
--- /dev/null
+++ b/src/pal/src/build_tools/mdtool_gcc.in
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+# 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.
+#
+# mdtool_gcc
+#
+#Abstract:
+# Generates dependencies for a makefile
+# (for gcc compilers)
+#
+
+str=`grep -n '#mdtool output goes here>' obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile`
+if [ "$str" = "" ] ; then
+#didn't find our placeholder string : don't change file
+ echo mdtool_gcc : makefile not setup for mdtool : ignoring
+ exit 0
+fi
+
+#truncate makefile to remove old dependencies
+sed /'#mdtool output goes here>'/q obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile > obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.final
+
+echo '#dependencies generated by mdtool_gcc :' >> obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.final
+echo >> obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.final
+# gcc -M generates make-friendly dependencies;
+# -MM ignores system includes (#include <file.h>)
+@CC@ @MDTOOL_CFLAGS@ $@ >> obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.depfromgcc
+#Prepend ../.. to the source's filename
+#with some sed magic, since gcc doesn't do it for us.
+sed 's#: \(.*\)$#: ../../\1#g' obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.depfromgcc > obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.aftersed
+cat obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.aftersed >> obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.final
+
+if [ "$?" = "0" ] ; then
+ echo mdtool_gcc : dependencies generated
+ # replace old makefile by new one.
+ mv -f obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.final obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile
+ rm -f obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.aftersed
+ rm -f obj${BUILD_ALT_DIR}/${_BUILDARCH}/makefile.depfromgcc
+ exit 0
+fi
+exit 1
+
diff --git a/src/pal/src/config.h.in b/src/pal/src/config.h.in
new file mode 100644
index 0000000000..7a53c8cb5d
--- /dev/null
+++ b/src/pal/src/config.h.in
@@ -0,0 +1,155 @@
+#ifndef _PAL_CONFIG_H_INCLUDED
+#define _PAL_CONFIG_H_INCLUDED 1
+
+#cmakedefine01 HAVE_IEEEFP_H
+#cmakedefine01 HAVE_SYS_VMPARAM_H
+#cmakedefine01 HAVE_MACH_VM_TYPES_H
+#cmakedefine01 HAVE_MACH_VM_PARAM_H
+#cmakedefine01 HAVE_MACHINE_NPX_H
+#cmakedefine01 HAVE_MACHINE_REG_H
+#cmakedefine01 HAVE_MACHINE_VMPARAM_H
+#cmakedefine01 HAVE_PROCFS_H
+#cmakedefine01 HAVE_CRT_EXTERNS_H
+#cmakedefine01 HAVE_SYS_TIME_H
+#cmakedefine01 HAVE_PTHREAD_NP_H
+#cmakedefine01 HAVE_SYS_LWP_H
+#cmakedefine01 HAVE_LWP_H
+#cmakedefine01 HAVE_LIBUNWIND_H
+#cmakedefine01 HAVE_LIBUUID_H
+#cmakedefine01 HAVE_BSD_UUID_H
+#cmakedefine01 HAVE_RUNETYPE_H
+#cmakedefine01 HAVE_SYS_SYSCTL_H
+#cmakedefine01 HAVE_GNU_LIBNAMES_H
+
+#cmakedefine01 HAVE_KQUEUE
+#cmakedefine01 HAVE_GETPWUID_R
+#cmakedefine01 HAVE_PTHREAD_SUSPEND
+#cmakedefine01 HAVE_PTHREAD_SUSPEND_NP
+#cmakedefine01 HAVE_PTHREAD_CONTINUE
+#cmakedefine01 HAVE_PTHREAD_RESUME_NP
+#cmakedefine01 HAVE_PTHREAD_CONTINUE_NP
+#cmakedefine01 HAVE_PTHREAD_ATTR_GET_NP
+#cmakedefine01 HAVE_PTHREAD_GETATTR_NP
+#cmakedefine01 HAVE_PTHREAD_GETCPUCLOCKID
+#cmakedefine01 HAVE_PTHREAD_SIGQUEUE
+#cmakedefine01 HAVE_SIGRETURN
+#cmakedefine01 HAVE__THREAD_SYS_SIGRETURN
+#cmakedefine01 HAVE_COPYSIGN
+#cmakedefine01 HAVE_FSYNC
+#cmakedefine01 HAVE_FUTIMES
+#cmakedefine01 HAVE_UTIMES
+#cmakedefine01 HAVE_SYSCTL
+#cmakedefine01 HAVE_SYSCONF
+#cmakedefine01 HAVE_LOCALTIME_R
+#cmakedefine01 HAVE_GMTIME_R
+#cmakedefine01 HAVE_TIMEGM
+#cmakedefine01 HAVE__SNWPRINTF
+#cmakedefine01 HAVE_POLL
+#cmakedefine01 HAVE_STATVFS
+#cmakedefine01 HAVE_THREAD_SELF
+#cmakedefine01 HAVE__LWP_SELF
+#cmakedefine01 HAVE_MACH_THREADS
+#cmakedefine01 HAVE_MACH_EXCEPTIONS
+#cmakedefine01 HAVE_VM_ALLOCATE
+#cmakedefine01 HAVE_VM_READ
+#cmakedefine01 HAS_SYSV_SEMAPHORES
+#cmakedefine01 HAS_PTHREAD_MUTEXES
+#cmakedefine01 HAVE_TTRACE
+#cmakedefine HAVE_UNW_GET_SAVE_LOC
+#cmakedefine HAVE_UNW_GET_ACCESSORS
+
+#cmakedefine01 HAVE_STAT_TIMESPEC
+#cmakedefine01 HAVE_STAT_NSEC
+#cmakedefine01 HAVE_TM_GMTOFF
+
+#cmakedefine01 HAVE_BSD_REGS_T
+#cmakedefine01 HAVE_PT_REGS
+#cmakedefine01 HAVE_GREGSET_T
+#cmakedefine01 HAVE___GREGSET_T
+#cmakedefine01 HAVE_SIGINFO_T
+#cmakedefine01 HAVE_UCONTEXT_T
+#cmakedefine01 HAVE_PTHREAD_RWLOCK_T
+#cmakedefine01 HAVE_PRWATCH_T
+#cmakedefine SIZEOF_OFF_T @SIZEOF_OFF_T@
+
+#cmakedefine01 HAVE_YIELD_SYSCALL
+#cmakedefine01 HAVE_INFTIM
+#cmakedefine01 HAVE_CHAR_BIT
+#cmakedefine01 USER_H_DEFINES_DEBUG
+#cmakedefine01 HAVE__SC_PHYS_PAGES
+#cmakedefine01 HAVE__SC_AVPHYS_PAGES
+
+#cmakedefine01 REALPATH_SUPPORTS_NONEXISTENT_FILES
+#cmakedefine01 SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
+#cmakedefine01 SSCANF_SUPPORT_ll
+#cmakedefine01 HAVE_LARGE_SNPRINTF_SUPPORT
+#cmakedefine01 HAVE_BROKEN_FIFO_SELECT
+#cmakedefine01 HAVE_BROKEN_FIFO_KEVENT
+#cmakedefine01 HAS_FTRUNCATE_LENGTH_ISSUE
+#cmakedefine01 HAVE_SCHED_GET_PRIORITY
+#cmakedefine01 HAVE_SCHED_GETCPU
+#cmakedefine01 HAVE_WORKING_GETTIMEOFDAY
+#cmakedefine01 HAVE_WORKING_CLOCK_GETTIME
+#cmakedefine01 HAVE_CLOCK_MONOTONIC
+#cmakedefine01 HAVE_CLOCK_MONOTONIC_COARSE
+#cmakedefine01 HAVE_MACH_ABSOLUTE_TIME
+#cmakedefine01 HAVE_CLOCK_THREAD_CPUTIME
+#cmakedefine01 STATVFS64_PROTOTYPE_BROKEN
+#cmakedefine01 HAVE_MMAP_DEV_ZERO
+#cmakedefine01 MMAP_ANON_IGNORES_PROTECTION
+#cmakedefine01 ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+#cmakedefine01 PTHREAD_CREATE_MODIFIES_ERRNO
+#cmakedefine01 SEM_INIT_MODIFIES_ERRNO
+#cmakedefine01 HAVE_PROCFS_CTL
+#cmakedefine01 HAVE_PROCFS_MAPS
+#cmakedefine01 HAVE_PROCFS_STAT
+#cmakedefine01 HAVE_PROCFS_STATUS
+#cmakedefine01 HAVE_COMPATIBLE_ACOS
+#cmakedefine01 HAVE_COMPATIBLE_ASIN
+#cmakedefine01 HAVE_COMPATIBLE_POW
+#cmakedefine01 HAVE_VALID_NEGATIVE_INF_POW
+#cmakedefine01 HAVE_VALID_POSITIVE_INF_POW
+#cmakedefine01 HAVE_COMPATIBLE_ATAN2
+#cmakedefine01 HAVE_COMPATIBLE_EXP
+#cmakedefine01 HAVE_COMPATIBLE_LOG
+#cmakedefine01 HAVE_COMPATIBLE_LOG10
+#cmakedefine01 UNGETC_NOT_RETURN_EOF
+#cmakedefine01 HAS_POSIX_SEMAPHORES
+#cmakedefine01 GETPWUID_R_SETS_ERRNO
+#cmakedefine01 FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
+
+#define PAL_THREAD_PRIORITY_MIN 0
+#define PAL_THREAD_PRIORITY_MAX 0
+
+#cmakedefine01 HAVE_COREFOUNDATION
+#cmakedefine01 HAVE__NSGETENVIRON
+#cmakedefine01 DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+#cmakedefine PAL_PTRACE(cmd, pid, addr, data) @PAL_PTRACE@
+#cmakedefine PAL_PT_ATTACH @PAL_PT_ATTACH@
+#cmakedefine PAL_PT_DETACH @PAL_PT_DETACH@
+#cmakedefine PAL_PT_READ_D @PAL_PT_READ_D@
+#cmakedefine PAL_PT_WRITE_D @PAL_PT_WRITE_D@
+#cmakedefine01 SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+#cmakedefine01 ERROR_FUNC_FOR_GLOB_HAS_FIXED_PARAMS
+#cmakedefine01 HAS_FTRUNCATE_LENGTH_ISSUE
+#cmakedefine01 UNWIND_CONTEXT_IS_UCONTEXT_T
+#cmakedefine01 HAVE_FULLY_FEATURED_PTHREAD_MUTEXES
+#cmakedefine01 HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES
+#cmakedefine BSD_REGS_STYLE(reg, RR, rr) @BSD_REGS_STYLE@
+#cmakedefine01 HAVE_SCHED_OTHER_ASSIGNABLE
+
+#define CHECK_TRACE_SPECIFIERS 0
+#define HAVE_GETHRTIME 0
+#define HAVE_LOWERCASE_ISO_NAME 0
+#define HAVE_READ_REAL_TIME 0
+#define HAVE_UNDERSCORE_ISO_NAME 0
+#define MKSTEMP64_IS_USED_INSTEAD_OF_MKSTEMP 0
+#define NEED_DLCOMPAT 0
+#define OPEN64_IS_USED_INSTEAD_OF_OPEN 0
+#define PAL_IGNORE_NORMAL_THREAD_PRIORITY 0
+#define SELF_SUSPEND_FAILS_WITH_NATIVE_SUSPENSION 0
+#define SET_SCHEDPARAM_NEEDS_PRIVS 0
+#define SIGWAIT_FAILS_WHEN_PASSED_FULL_SIGSET 0
+#define WRITE_0_BYTES_HANGS_TTY 0
+#define HAVE_FTRUNCATE_LARGE_LENGTH_SUPPORT 1
+#endif
diff --git a/src/pal/src/configure.cmake b/src/pal/src/configure.cmake
new file mode 100644
index 0000000000..cc38bc8541
--- /dev/null
+++ b/src/pal/src/configure.cmake
@@ -0,0 +1,1281 @@
+include(CheckCXXSourceCompiles)
+include(CheckCXXSourceRuns)
+include(CheckCXXSymbolExists)
+include(CheckFunctionExists)
+include(CheckIncludeFiles)
+include(CheckStructHasMember)
+include(CheckTypeSize)
+include(CheckLibraryExists)
+
+if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
+ set(CMAKE_REQUIRED_INCLUDES /usr/local/include)
+elseif(CMAKE_SYSTEM_NAME STREQUAL SunOS)
+ set(CMAKE_REQUIRED_INCLUDES /opt/local/include)
+endif()
+if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin AND NOT CMAKE_SYSTEM_NAME STREQUAL FreeBSD AND NOT CMAKE_SYSTEM_NAME STREQUAL NetBSD)
+ set(CMAKE_REQUIRED_DEFINITIONS "-D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L")
+endif()
+
+list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_FILE_OFFSET_BITS=64)
+
+check_include_files(ieeefp.h HAVE_IEEEFP_H)
+check_include_files(sys/vmparam.h HAVE_SYS_VMPARAM_H)
+check_include_files(mach/vm_types.h HAVE_MACH_VM_TYPES_H)
+check_include_files(mach/vm_param.h HAVE_MACH_VM_PARAM_H)
+check_include_files("sys/param.h;sys/types.h;machine/npx.h" HAVE_MACHINE_NPX_H)
+check_include_files("sys/param.h;sys/cdefs.h;machine/reg.h" HAVE_MACHINE_REG_H)
+check_include_files(machine/vmparam.h HAVE_MACHINE_VMPARAM_H)
+check_include_files(procfs.h HAVE_PROCFS_H)
+check_include_files(crt_externs.h HAVE_CRT_EXTERNS_H)
+check_include_files(sys/time.h HAVE_SYS_TIME_H)
+check_include_files(pthread_np.h HAVE_PTHREAD_NP_H)
+check_include_files(sys/lwp.h HAVE_SYS_LWP_H)
+check_include_files(lwp.h HAVE_LWP_H)
+check_include_files(libunwind.h HAVE_LIBUNWIND_H)
+check_include_files(runetype.h HAVE_RUNETYPE_H)
+check_include_files(lttng/tracepoint.h HAVE_LTTNG_TRACEPOINT_H)
+check_include_files(uuid/uuid.h HAVE_LIBUUID_H)
+check_include_files(sys/sysctl.h HAVE_SYS_SYSCTL_H)
+check_include_files(gnu/lib-names.h HAVE_GNU_LIBNAMES_H)
+
+check_function_exists(kqueue HAVE_KQUEUE)
+check_function_exists(getpwuid_r HAVE_GETPWUID_R)
+check_library_exists(pthread pthread_suspend "" HAVE_PTHREAD_SUSPEND)
+check_library_exists(pthread pthread_suspend_np "" HAVE_PTHREAD_SUSPEND_NP)
+check_library_exists(pthread pthread_continue "" HAVE_PTHREAD_CONTINUE)
+check_library_exists(pthread pthread_continue_np "" HAVE_PTHREAD_CONTINUE_NP)
+check_library_exists(pthread pthread_resume_np "" HAVE_PTHREAD_RESUME_NP)
+check_library_exists(pthread pthread_attr_get_np "" HAVE_PTHREAD_ATTR_GET_NP)
+check_library_exists(pthread pthread_getattr_np "" HAVE_PTHREAD_GETATTR_NP)
+check_library_exists(pthread pthread_getcpuclockid "" HAVE_PTHREAD_GETCPUCLOCKID)
+check_library_exists(pthread pthread_sigqueue "" HAVE_PTHREAD_SIGQUEUE)
+check_function_exists(sigreturn HAVE_SIGRETURN)
+check_function_exists(_thread_sys_sigreturn HAVE__THREAD_SYS_SIGRETURN)
+set(CMAKE_REQUIRED_LIBRARIES m)
+check_function_exists(copysign HAVE_COPYSIGN)
+set(CMAKE_REQUIRED_LIBRARIES)
+check_function_exists(fsync HAVE_FSYNC)
+check_function_exists(futimes HAVE_FUTIMES)
+check_function_exists(utimes HAVE_UTIMES)
+check_function_exists(sysctl HAVE_SYSCTL)
+check_function_exists(sysconf HAVE_SYSCONF)
+check_function_exists(localtime_r HAVE_LOCALTIME_R)
+check_function_exists(gmtime_r HAVE_GMTIME_R)
+check_function_exists(timegm HAVE_TIMEGM)
+check_function_exists(_snwprintf HAVE__SNWPRINTF)
+check_function_exists(poll HAVE_POLL)
+check_function_exists(statvfs HAVE_STATVFS)
+check_function_exists(thread_self HAVE_THREAD_SELF)
+check_function_exists(_lwp_self HAVE__LWP_SELF)
+check_function_exists(pthread_mach_thread_np HAVE_MACH_THREADS)
+check_function_exists(thread_set_exception_ports HAVE_MACH_EXCEPTIONS)
+check_function_exists(vm_allocate HAVE_VM_ALLOCATE)
+check_function_exists(vm_read HAVE_VM_READ)
+check_function_exists(directio HAVE_DIRECTIO)
+check_function_exists(semget HAS_SYSV_SEMAPHORES)
+check_function_exists(pthread_mutex_init HAS_PTHREAD_MUTEXES)
+check_function_exists(ttrace HAVE_TTRACE)
+set(CMAKE_REQUIRED_LIBRARIES unwind unwind-generic)
+check_cxx_source_compiles("
+#include <libunwind.h>
+
+int main(int argc, char **argv) {
+ unw_cursor_t cursor;
+ unw_save_loc_t saveLoc;
+ int reg = UNW_REG_IP;
+ unw_get_save_loc(&cursor, reg, &saveLoc);
+
+ return 0;
+}" HAVE_UNW_GET_SAVE_LOC)
+check_cxx_source_compiles("
+#include <libunwind.h>
+
+int main(int argc, char **argv) {
+ unw_addr_space_t as;
+ unw_get_accessors(as);
+
+ return 0;
+}" HAVE_UNW_GET_ACCESSORS)
+set(CMAKE_REQUIRED_LIBRARIES)
+
+check_struct_has_member ("struct stat" st_atimespec "sys/types.h;sys/stat.h" HAVE_STAT_TIMESPEC)
+check_struct_has_member ("struct stat" st_atimensec "sys/types.h;sys/stat.h" HAVE_STAT_NSEC)
+check_struct_has_member ("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
+check_struct_has_member ("ucontext_t" uc_mcontext.gregs[0] ucontext.h HAVE_GREGSET_T)
+check_struct_has_member ("ucontext_t" uc_mcontext.__gregs[0] ucontext.h HAVE___GREGSET_T)
+
+set(CMAKE_EXTRA_INCLUDE_FILES machine/reg.h)
+check_type_size("struct reg" BSD_REGS_T)
+set(CMAKE_EXTRA_INCLUDE_FILES)
+set(CMAKE_EXTRA_INCLUDE_FILES asm/ptrace.h)
+check_type_size("struct pt_regs" PT_REGS)
+set(CMAKE_EXTRA_INCLUDE_FILES)
+set(CMAKE_EXTRA_INCLUDE_FILES signal.h)
+check_type_size(siginfo_t SIGINFO_T)
+set(CMAKE_EXTRA_INCLUDE_FILES)
+set(CMAKE_EXTRA_INCLUDE_FILES ucontext.h)
+check_type_size(ucontext_t UCONTEXT_T)
+set(CMAKE_EXTRA_INCLUDE_FILES)
+set(CMAKE_EXTRA_INCLUDE_FILES pthread.h)
+check_type_size(pthread_rwlock_t PTHREAD_RWLOCK_T)
+set(CMAKE_EXTRA_INCLUDE_FILES)
+set(CMAKE_EXTRA_INCLUDE_FILE procfs.h)
+check_type_size(prwatch_t PRWATCH_T)
+set(CMAKE_EXTRA_INCLUDE_FILE)
+check_type_size(off_t SIZEOF_OFF_T)
+
+check_cxx_symbol_exists(SYS_yield sys/syscall.h HAVE_YIELD_SYSCALL)
+check_cxx_symbol_exists(INFTIM poll.h HAVE_INFTIM)
+check_cxx_symbol_exists(CHAR_BIT limits.h HAVE_CHAR_BIT)
+check_cxx_symbol_exists(_DEBUG sys/user.h USER_H_DEFINES_DEBUG)
+check_cxx_symbol_exists(_SC_PHYS_PAGES unistd.h HAVE__SC_PHYS_PAGES)
+check_cxx_symbol_exists(_SC_AVPHYS_PAGES unistd.h HAVE__SC_AVPHYS_PAGES)
+
+check_cxx_source_runs("
+#include <uuid.h>
+
+int main(void) {
+ uuid_t uuid;
+ uint32_t status;
+ uuid_create(&uuid, &status);
+ return 0;
+}" HAVE_BSD_UUID_H)
+
+check_cxx_source_runs("
+#include <sys/param.h>
+#include <stdlib.h>
+
+int main(void) {
+ char *path;
+#ifdef PATH_MAX
+ char resolvedPath[PATH_MAX];
+#elif defined(MAXPATHLEN)
+ char resolvedPath[MAXPATHLEN];
+#else
+ char resolvedPath[1024];
+#endif
+ path = realpath(\"a_nonexistent_file\", resolvedPath);
+ if (path == NULL) {
+ exit(1);
+ }
+ exit(0);
+}" REALPATH_SUPPORTS_NONEXISTENT_FILES)
+check_cxx_source_runs("
+#include <stdio.h>
+#include <stdlib.h>
+int main(void)
+{
+ long long n = 0;
+ sscanf(\"5000000000\", \"%qu\", &n);
+ exit (n != 5000000000);
+ }" SSCANF_SUPPORT_ll)
+check_cxx_source_runs("
+#include <stdio.h>
+#include <stdlib.h>
+
+int main()
+{
+ int ret;
+ float f = 0;
+ char * strin = \"12.34e\";
+
+ ret = sscanf (strin, \"%e\", &f);
+ if (ret <= 0)
+ exit (0);
+ exit(1);
+}" SSCANF_CANNOT_HANDLE_MISSING_EXPONENT)
+check_cxx_source_runs("
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+ char buf[256] = { 0 };
+ snprintf(buf, 0x7fffffff, \"%#x\", 0x12345678);
+ if (buf[0] == 0x0) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_LARGE_SNPRINTF_SUPPORT)
+check_cxx_source_runs("
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+int main(void) {
+ int fd, numFDs;
+ fd_set readFDs, writeFDs, exceptFDs;
+ struct timeval time = { 0 };
+ char * filename = NULL;
+
+ filename = (char *)malloc(L_tmpnam * sizeof(char)); /* ok to leak this at exit */
+ if (NULL == filename) {
+ exit(0);
+ }
+
+ /* On some platforms (e.g. HP-UX) the multithreading c-runtime does not
+ support the tmpnam(NULL) semantics, and it returns NULL. Therefore
+ we need to use the tmpnam(pbuffer) version.
+ */
+ if (NULL == tmpnam(filename)) {
+ exit(0);
+ }
+ if (mkfifo(filename, S_IRWXU) != 0) {
+ if (unlink(filename) != 0) {
+ exit(0);
+ }
+ if (mkfifo(filename, S_IRWXU) != 0) {
+ exit(0);
+ }
+ }
+ fd = open(filename, O_RDWR | O_NONBLOCK);
+ if (fd == -1) {
+ exit(0);
+ }
+
+ FD_ZERO(&readFDs);
+ FD_ZERO(&writeFDs);
+ FD_ZERO(&exceptFDs);
+ FD_SET(fd, &readFDs);
+ numFDs = select(fd + 1, &readFDs, &writeFDs, &exceptFDs, &time);
+
+ close(fd);
+ unlink(filename);
+
+ /* numFDs is zero if select() works correctly */
+ exit(numFD==0);
+}" HAVE_BROKEN_FIFO_SELECT)
+check_cxx_source_runs("
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+int main(void)
+{
+ int ikq;
+ int iRet;
+ int fd;
+ struct kevent ke, keChangeList;
+ struct timespec ts = { 0, 0 };
+
+ char * filename = NULL;
+
+ filename = (char *)malloc(L_tmpnam * sizeof(char)); /* ok to leak this at exit */
+ if (NULL == filename)
+ {
+ exit(1);
+ }
+
+ /* On some platforms (e.g. HP-UX) the multithreading c-runtime does not
+ support the tmpnam(NULL) semantics, and it returns NULL. Therefore
+ we need to use the tmpnam(pbuffer) version.
+ */
+ if (NULL == tmpnam(filename)) {
+ exit(0);
+ }
+ if (mkfifo(filename, S_IRWXU) != 0) {
+ if (unlink(filename) != 0) {
+ exit(0);
+ }
+ if (mkfifo(filename, S_IRWXU) != 0) {
+ exit(0);
+ }
+ }
+ fd = open(filename, O_RDWR | O_NONBLOCK);
+ if (fd == -1) {
+ exit(0);
+ }
+
+ EV_SET(&keChangeList, fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, NULL);
+ ikq = kqueue();
+ iRet = kevent(ikq, &keChangeList, 1, &ke, 1, &ts);
+
+ close(fd);
+ unlink(filename);
+
+ /* iRet is zero is kevent() works correctly */
+ return(iRet==0);
+}" HAVE_BROKEN_FIFO_KEVENT)
+set(CMAKE_REQUIRED_LIBRARIES pthread)
+check_cxx_source_runs("
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <sched.h>
+
+int main(void)
+{
+ int policy;
+ struct sched_param schedParam;
+ int max_priority;
+ int min_priority;
+
+ if (0 != pthread_getschedparam(pthread_self(), &policy, &schedParam))
+ {
+ exit(1);
+ }
+
+ max_priority = sched_get_priority_max(policy);
+ min_priority = sched_get_priority_min(policy);
+
+ exit(-1 == max_priority || -1 == min_priority);
+}" HAVE_SCHED_GET_PRIORITY)
+set(CMAKE_REQUIRED_LIBRARIES pthread)
+check_cxx_source_runs("
+#include <stdlib.h>
+#include <sched.h>
+
+int main(void)
+{
+ if (sched_getcpu() >= 0)
+ {
+ exit(0);
+ }
+ exit(1);
+}" HAVE_SCHED_GETCPU)
+set(CMAKE_REQUIRED_LIBRARIES)
+check_cxx_source_runs("
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+
+int main()
+{
+ int ret;
+ struct timeval tv;
+ ret = gettimeofday(&tv, NULL);
+
+ exit(ret);
+}" HAVE_WORKING_GETTIMEOFDAY)
+check_cxx_source_runs("
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+
+int main()
+{
+ int ret;
+ struct timespec ts;
+ ret = clock_gettime(CLOCK_REALTIME, &ts);
+
+ exit(ret);
+}" HAVE_WORKING_CLOCK_GETTIME)
+check_cxx_source_runs("
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+
+int main()
+{
+ int ret;
+ struct timespec ts;
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ exit(ret);
+}" HAVE_CLOCK_MONOTONIC)
+check_cxx_source_runs("
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+
+int main()
+{
+ int ret;
+ struct timespec ts;
+ ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
+
+ exit(ret);
+}" HAVE_CLOCK_MONOTONIC_COARSE)
+check_cxx_source_runs("
+#include <stdlib.h>
+#include <mach/mach_time.h>
+
+int main()
+{
+ int ret;
+ mach_timebase_info_data_t timebaseInfo;
+ ret = mach_timebase_info(&timebaseInfo);
+ mach_absolute_time();
+ exit(ret);
+}" HAVE_MACH_ABSOLUTE_TIME)
+check_cxx_source_runs("
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+
+int main()
+{
+ int ret;
+ struct timespec ts;
+ ret = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+
+ exit(ret);
+}" HAVE_CLOCK_THREAD_CPUTIME)
+check_cxx_source_runs("
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+int main(void) {
+ int devzero;
+ void *retval;
+
+ devzero = open(\"/dev/zero\", O_RDWR);
+ if (-1 == devzero) {
+ exit(1);
+ }
+ retval = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, devzero, 0);
+ if (retval == (void *)-1) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_MMAP_DEV_ZERO)
+check_cxx_source_runs("
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifndef MAP_ANON
+#define MAP_ANON MAP_ANONYMOUS
+#endif
+
+void *handle_signal(int signal) {
+ /* If we reach this, we've crashed due to mmap honoring
+ PROT_NONE. */
+ _exit(1);
+}
+
+int main(void) {
+ int *ptr;
+ struct sigaction action;
+
+ ptr = (int *) mmap(NULL, getpagesize(), PROT_NONE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (ptr == (int *) MAP_FAILED) {
+ exit(0);
+ }
+ action.sa_handler = &handle_signal;
+ action.sa_flags = 0;
+ sigemptyset(&action.sa_mask);
+ if (sigaction(SIGBUS, &action, NULL) != 0) {
+ exit(0);
+ }
+ if (sigaction(SIGSEGV, &action, NULL) != 0) {
+ exit(0);
+ }
+ /* This will drop us into the signal handler if PROT_NONE
+ is honored. */
+ *ptr = 123;
+ exit(0);
+}" MMAP_ANON_IGNORES_PROTECTION)
+check_cxx_source_runs("
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#define MEM_SIZE 1024
+
+int main(void)
+{
+ char * fname;
+ int fd;
+ int ret;
+ void * pAddr0, * pAddr1;
+
+ fname = (char *)malloc(MEM_SIZE);
+ if (!fname)
+ exit(1);
+ strcpy(fname, \"/tmp/name/multiplemaptestXXXXXX\");
+
+ fd = mkstemp(fname);
+ if (fd < 0)
+ exit(1);
+
+ ret = write (fd, (void *)fname, MEM_SIZE);
+ if (ret < 0)
+ exit(1);
+
+ pAddr0 = mmap(0, MEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ pAddr1 = mmap(0, MEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ /* In theory we should look for (pAddr1 == MAP_FAILED) && (pAddr1 != MAP_FAILED)
+ but in case the first test also failed, i.e. we failed to run the test,
+ let's assume that the system might not allow multiple shared mapping of the
+ same file region in the same process. The code enabled in this case is
+ only a fall-back code path. In case the double mmap actually works, virtually
+ nothing will change and the normal code path will be executed */
+ if (pAddr1 == MAP_FAILED)
+ ret = 1;
+ else
+ ret = 0;
+
+ if (pAddr0)
+ munmap (pAddr0, MEM_SIZE);
+ if (pAddr1)
+ munmap (pAddr1, MEM_SIZE);
+ close(fd);
+ unlink(fname);
+ free(fname);
+
+ exit(ret != 1);
+}" ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS)
+set(CMAKE_REQUIRED_LIBRARIES pthread)
+check_cxx_source_runs("
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+void *start_routine(void *param) { return NULL; }
+
+int main() {
+ int result;
+ pthread_t tid;
+
+ errno = 0;
+ result = pthread_create(&tid, NULL, start_routine, NULL);
+ if (result != 0) {
+ exit(1);
+ }
+ if (errno != 0) {
+ exit(0);
+ }
+ exit(1);
+}" PTHREAD_CREATE_MODIFIES_ERRNO)
+set(CMAKE_REQUIRED_LIBRARIES)
+set(CMAKE_REQUIRED_LIBRARIES pthread)
+check_cxx_source_runs("
+#include <errno.h>
+#include <semaphore.h>
+#include <stdlib.h>
+
+int main() {
+ int result;
+ sem_t sema;
+
+ errno = 50;
+ result = sem_init(&sema, 0, 0);
+ if (result != 0)
+ {
+ exit(1);
+ }
+ if (errno != 50)
+ {
+ exit(0);
+ }
+ exit(1);
+}" SEM_INIT_MODIFIES_ERRNO)
+set(CMAKE_REQUIRED_LIBRARIES)
+check_cxx_source_runs("
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void) {
+ int fd;
+#ifdef PATH_MAX
+ char path[PATH_MAX];
+#elif defined(MAXPATHLEN)
+ char path[MAXPATHLEN];
+#else
+ char path[1024];
+#endif
+
+ sprintf(path, \"/proc/%u/ctl\", getpid());
+ fd = open(path, O_WRONLY);
+ if (fd == -1) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_PROCFS_CTL)
+set(CMAKE_REQUIRED_LIBRARIES)
+check_cxx_source_runs("
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void) {
+ int fd;
+#ifdef PATH_MAX
+ char path[PATH_MAX];
+#elif defined(MAXPATHLEN)
+ char path[MAXPATHLEN];
+#else
+ char path[1024];
+#endif
+
+ sprintf(path, \"/proc/%u/maps\", getpid());
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_PROCFS_MAPS)
+set(CMAKE_REQUIRED_LIBRARIES)
+check_cxx_source_runs("
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void) {
+ int fd;
+#ifdef PATH_MAX
+ char path[PATH_MAX];
+#elif defined(MAXPATHLEN)
+ char path[MAXPATHLEN];
+#else
+ char path[1024];
+#endif
+
+ sprintf(path, \"/proc/%u/stat\", getpid());
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_PROCFS_STAT)
+set(CMAKE_REQUIRED_LIBRARIES)
+check_cxx_source_runs("
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void) {
+ int fd;
+#ifdef PATH_MAX
+ char path[PATH_MAX];
+#elif defined(MAXPATHLEN)
+ char path[MAXPATHLEN];
+#else
+ char path[1024];
+#endif
+
+ sprintf(path, \"/proc/%u/status\", getpid());
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_PROCFS_STATUS)
+set(CMAKE_REQUIRED_LIBRARIES m)
+check_cxx_source_runs("
+#include <math.h>
+#include <stdlib.h>
+
+int main(void) {
+ if (!isnan(acos(10))) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_COMPATIBLE_ACOS)
+set(CMAKE_REQUIRED_LIBRARIES)
+set(CMAKE_REQUIRED_LIBRARIES m)
+check_cxx_source_runs("
+#include <math.h>
+#include <stdlib.h>
+
+int main(void) {
+ if (!isnan(asin(10))) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_COMPATIBLE_ASIN)
+set(CMAKE_REQUIRED_LIBRARIES)
+set(CMAKE_REQUIRED_LIBRARIES m)
+check_cxx_source_runs("
+#include <math.h>
+#include <stdlib.h>
+
+int main(void) {
+ double infinity = 1.0 / 0.0;
+ if (pow(1.0, infinity) != 1.0 || pow(1.0, -infinity) != 1.0) {
+ exit(1)
+ }
+ if (!isnan(pow(-1.0, infinity)) || !isnan(pow(-1.0, -infinity))) {
+ exit(1);
+ }
+ if (pow(0.0, infinity) != 0.0) {
+ exit(1);
+ }
+ if (pow(0.0, -infinity) != infinity) {
+ exit(1);
+ }
+ if (pow(-1.1, infinity) != infinity || pow(1.1, infinity) != infinity) {
+ exit(1);
+ }
+ if (pow(-1.1, -infinity) != 0.0 || pow(1.1, infinity) != 0.0) {
+ exit(1);
+ }
+ if (pow(-0.0, -1) != -infinity) {
+ exit(1);
+ }
+ if (pow(0.0, -1) != infinity) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_COMPATIBLE_POW)
+set(CMAKE_REQUIRED_LIBRARIES)
+set(CMAKE_REQUIRED_LIBRARIES m)
+check_cxx_source_runs("
+#include <math.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+ double result;
+
+ result = pow(-3.2e-10, -5e14 + 1);
+ if (result != -1.0 / 0.0) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_VALID_NEGATIVE_INF_POW)
+set(CMAKE_REQUIRED_LIBRARIES)
+set(CMAKE_REQUIRED_LIBRARIES m)
+check_cxx_source_runs("
+#include <math.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+ double result;
+
+ result = pow(-3.5, 3e100);
+ if (result != 1.0 / 0.0) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_VALID_POSITIVE_INF_POW)
+set(CMAKE_REQUIRED_LIBRARIES)
+set(CMAKE_REQUIRED_LIBRARIES m)
+check_cxx_source_runs("
+#include <math.h>
+#include <stdlib.h>
+
+int main(void) {
+ double pi = 3.14159265358979323846;
+ double result;
+
+ result = atan2(0.0, -0.0);
+ if (fabs(pi - result) > 0.0000001) {
+ exit(1);
+ }
+
+ result = atan2(-0.0, -0.0);
+ if (fabs(-pi - result) > 0.0000001) {
+ exit(1);
+ }
+
+ result = atan2 (-0.0, 0.0);
+ if (result != 0.0 || copysign (1.0, result) > 0) {
+ exit(1);
+ }
+
+ result = atan2 (0.0, 0.0);
+ if (result != 0.0 || copysign (1.0, result) < 0) {
+ exit(1);
+ }
+
+ exit (0);
+}" HAVE_COMPATIBLE_ATAN2)
+set(CMAKE_REQUIRED_LIBRARIES)
+set(CMAKE_REQUIRED_LIBRARIES m)
+check_cxx_source_runs("
+#include <math.h>
+#include <stdlib.h>
+
+int main(void) {
+ double d = exp(1.0), e = M_E;
+
+ /* Used memcmp rather than == to test that the doubles are equal to
+ prevent gcc's optimizer from using its 80 bit internal long
+ doubles. If you use ==, then on BSD you get a false negative since
+ exp(1.0) == M_E to 64 bits, but not 80.
+ */
+
+ if (memcmp (&d, &e, sizeof (double)) == 0) {
+ exit(0);
+ }
+ exit(1);
+}" HAVE_COMPATIBLE_EXP)
+set(CMAKE_REQUIRED_LIBRARIES)
+set(CMAKE_REQUIRED_LIBRARIES m)
+check_cxx_source_runs("
+#include <math.h>
+#include <stdlib.h>
+
+int main(void) {
+ if (!isnan(log(-10000))) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_COMPATIBLE_LOG)
+set(CMAKE_REQUIRED_LIBRARIES)
+set(CMAKE_REQUIRED_LIBRARIES m)
+check_cxx_source_runs("
+#include <math.h>
+#include <stdlib.h>
+
+int main(void) {
+ if (!isnan(log10(-10000))) {
+ exit(1);
+ }
+ exit(0);
+}" HAVE_COMPATIBLE_LOG10)
+set(CMAKE_REQUIRED_LIBRARIES)
+check_cxx_source_runs("
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main(void)
+{
+ char* szFileName;
+ FILE* pFile = NULL;
+ int ret = 1;
+
+ szFileName = tempnam(\".\", \"tmp\");
+
+ /* open the file write-only */
+ pFile = fopen(szFileName, \"a\");
+ if (pFile == NULL)
+ {
+ exit(0);
+ }
+ if (ungetc('A', pFile) != EOF)
+ {
+ ret = 0;
+ }
+ unlink(szFileName);
+ exit(ret);
+}" UNGETC_NOT_RETURN_EOF)
+set(CMAKE_REQUIRED_LIBRARIES pthread)
+check_cxx_source_runs("
+#include <stdlib.h>
+#include <errno.h>
+#include <semaphore.h>
+
+int main() {
+ sem_t sema;
+ if (sem_init(&sema, 0, 0) == -1){
+ exit(1);
+ }
+ exit(0);
+}" HAS_POSIX_SEMAPHORES)
+set(CMAKE_REQUIRED_LIBRARIES)
+check_cxx_source_runs("
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+int main(void)
+{
+ struct passwd sPasswd;
+ struct passwd *pPasswd;
+ char buf[1];
+ int bufLen = sizeof(buf)/sizeof(buf[0]);
+ int euid = geteuid();
+ int ret = 0;
+
+ errno = 0; // clear errno
+ ret = getpwuid_r(euid, &sPasswd, buf, bufLen, &pPasswd);
+ if (0 != ret)
+ {
+ if (ERANGE == errno)
+ {
+ return 0;
+ }
+ }
+
+ return 1; // assume errno is NOT set for all other cases
+}" GETPWUID_R_SETS_ERRNO)
+check_cxx_source_runs("
+#include <stdio.h>
+#include <stdlib.h>
+
+int main()
+{
+ FILE *fp = NULL;
+ char *fileName = \"/dev/zero\";
+ char buf[10];
+
+ /*
+ * Open the file in append mode and try to read some text.
+ * And, make sure ferror() is set.
+ */
+ fp = fopen (fileName, \"a\");
+ if ( (NULL == fp) ||
+ (fread (buf, sizeof(buf), 1, fp) > 0) ||
+ (!ferror(fp))
+ )
+ {
+ return 0;
+ }
+
+ /*
+ * Now that ferror() is set, try to close the file.
+ * If we get an error, we can conclude that this
+ * fgets() depended on the previous ferror().
+ */
+ if ( fclose(fp) != 0 )
+ {
+ return 0;
+ }
+
+ return 1;
+}" FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL)
+set(CMAKE_REQUIRED_DEFINITIONS)
+
+set(SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING 1)
+set(ERROR_FUNC_FOR_GLOB_HAS_FIXED_PARAMS 1)
+
+check_cxx_source_compiles("
+#include <libunwind.h>
+#include <ucontext.h>
+
+int main(int argc, char **argv)
+{
+ unw_context_t libUnwindContext;
+ ucontext_t uContext;
+
+ libUnwindContext = uContext;
+ return 0;
+}" UNWIND_CONTEXT_IS_UCONTEXT_T)
+
+set(CMAKE_REQUIRED_LIBRARIES pthread)
+check_cxx_source_compiles("
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+int main()
+{
+ pthread_mutexattr_t mutexAttributes;
+ pthread_mutexattr_init(&mutexAttributes);
+ pthread_mutexattr_setpshared(&mutexAttributes, PTHREAD_PROCESS_SHARED);
+ pthread_mutexattr_settype(&mutexAttributes, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutexattr_setrobust(&mutexAttributes, PTHREAD_MUTEX_ROBUST);
+
+ pthread_mutex_t mutex;
+ pthread_mutex_init(&mutex, &mutexAttributes);
+
+ pthread_mutexattr_destroy(&mutexAttributes);
+
+ struct timespec timeoutTime;
+ timeoutTime.tv_sec = 1; // not the right way to specify absolute time, but just checking availability of timed lock
+ timeoutTime.tv_nsec = 0;
+ pthread_mutex_timedlock(&mutex, &timeoutTime);
+ pthread_mutex_consistent(&mutex);
+
+ pthread_mutex_destroy(&mutex);
+
+ int error = EOWNERDEAD;
+ error = ENOTRECOVERABLE;
+ error = ETIMEDOUT;
+ error = 0;
+ return error;
+}" HAVE_FULLY_FEATURED_PTHREAD_MUTEXES)
+set(CMAKE_REQUIRED_LIBRARIES)
+
+if(NOT CLR_CMAKE_PLATFORM_ARCH_ARM AND NOT CLR_CMAKE_PLATFORM_ARCH_ARM64)
+ set(CMAKE_REQUIRED_LIBRARIES pthread)
+ check_cxx_source_runs("
+ // This test case verifies the pthread process-shared robust mutex's cross-process abandon detection. The parent process starts
+ // a child process that locks the mutex, the process process then waits to acquire the lock, and the child process abandons the
+ // mutex by exiting the process while holding the lock. The parent process should then be released from its wait, be assigned
+ // ownership of the lock, and be notified that the mutex was abandoned.
+
+ #include <sys/mman.h>
+ #include <sys/time.h>
+
+ #include <errno.h>
+ #include <pthread.h>
+ #include <stdio.h>
+ #include <unistd.h>
+
+ #include <new>
+ using namespace std;
+
+ struct Shm
+ {
+ pthread_mutex_t syncMutex;
+ pthread_cond_t syncCondition;
+ pthread_mutex_t robustMutex;
+ int conditionValue;
+
+ Shm() : conditionValue(0)
+ {
+ }
+ } *shm;
+
+ int GetFailTimeoutTime(struct timespec *timeoutTimeRef)
+ {
+ int getTimeResult = clock_gettime(CLOCK_REALTIME, timeoutTimeRef);
+ if (getTimeResult != 0)
+ {
+ struct timeval tv;
+ getTimeResult = gettimeofday(&tv, NULL);
+ if (getTimeResult != 0)
+ return 1;
+ timeoutTimeRef->tv_sec = tv.tv_sec;
+ timeoutTimeRef->tv_nsec = tv.tv_usec * 1000;
+ }
+ timeoutTimeRef->tv_sec += 30;
+ return 0;
+ }
+
+ int WaitForConditionValue(int desiredConditionValue)
+ {
+ struct timespec timeoutTime;
+ if (GetFailTimeoutTime(&timeoutTime) != 0)
+ return 1;
+ if (pthread_mutex_timedlock(&shm->syncMutex, &timeoutTime) != 0)
+ return 1;
+
+ if (shm->conditionValue != desiredConditionValue)
+ {
+ if (GetFailTimeoutTime(&timeoutTime) != 0)
+ return 1;
+ if (pthread_cond_timedwait(&shm->syncCondition, &shm->syncMutex, &timeoutTime) != 0)
+ return 1;
+ if (shm->conditionValue != desiredConditionValue)
+ return 1;
+ }
+
+ if (pthread_mutex_unlock(&shm->syncMutex) != 0)
+ return 1;
+ return 0;
+ }
+
+ int SetConditionValue(int newConditionValue)
+ {
+ struct timespec timeoutTime;
+ if (GetFailTimeoutTime(&timeoutTime) != 0)
+ return 1;
+ if (pthread_mutex_timedlock(&shm->syncMutex, &timeoutTime) != 0)
+ return 1;
+
+ shm->conditionValue = newConditionValue;
+ if (pthread_cond_signal(&shm->syncCondition) != 0)
+ return 1;
+
+ if (pthread_mutex_unlock(&shm->syncMutex) != 0)
+ return 1;
+ return 0;
+ }
+
+ void DoTest_Child();
+
+ int DoTest()
+ {
+ // Map some shared memory
+ void *shmBuffer = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (shmBuffer == MAP_FAILED)
+ return 1;
+ shm = new(shmBuffer) Shm;
+
+ // Create sync mutex
+ pthread_mutexattr_t syncMutexAttributes;
+ if (pthread_mutexattr_init(&syncMutexAttributes) != 0)
+ return 1;
+ if (pthread_mutexattr_setpshared(&syncMutexAttributes, PTHREAD_PROCESS_SHARED) != 0)
+ return 1;
+ if (pthread_mutex_init(&shm->syncMutex, &syncMutexAttributes) != 0)
+ return 1;
+ if (pthread_mutexattr_destroy(&syncMutexAttributes) != 0)
+ return 1;
+
+ // Create sync condition
+ pthread_condattr_t syncConditionAttributes;
+ if (pthread_condattr_init(&syncConditionAttributes) != 0)
+ return 1;
+ if (pthread_condattr_setpshared(&syncConditionAttributes, PTHREAD_PROCESS_SHARED) != 0)
+ return 1;
+ if (pthread_cond_init(&shm->syncCondition, &syncConditionAttributes) != 0)
+ return 1;
+ if (pthread_condattr_destroy(&syncConditionAttributes) != 0)
+ return 1;
+
+ // Create the robust mutex that will be tested
+ pthread_mutexattr_t robustMutexAttributes;
+ if (pthread_mutexattr_init(&robustMutexAttributes) != 0)
+ return 1;
+ if (pthread_mutexattr_setpshared(&robustMutexAttributes, PTHREAD_PROCESS_SHARED) != 0)
+ return 1;
+ if (pthread_mutexattr_setrobust(&robustMutexAttributes, PTHREAD_MUTEX_ROBUST) != 0)
+ return 1;
+ if (pthread_mutex_init(&shm->robustMutex, &robustMutexAttributes) != 0)
+ return 1;
+ if (pthread_mutexattr_destroy(&robustMutexAttributes) != 0)
+ return 1;
+
+ // Start child test process
+ int error = fork();
+ if (error == -1)
+ return 1;
+ if (error == 0)
+ {
+ DoTest_Child();
+ return -1;
+ }
+
+ // Wait for child to take a lock
+ WaitForConditionValue(1);
+
+ // Wait to try to take a lock. Meanwhile, child abandons the robust mutex.
+ struct timespec timeoutTime;
+ if (GetFailTimeoutTime(&timeoutTime) != 0)
+ return 1;
+ error = pthread_mutex_timedlock(&shm->robustMutex, &timeoutTime);
+ if (error != EOWNERDEAD) // expect to be notified that the robust mutex was abandoned
+ return 1;
+ if (pthread_mutex_consistent(&shm->robustMutex) != 0)
+ return 1;
+
+ if (pthread_mutex_unlock(&shm->robustMutex) != 0)
+ return 1;
+ if (pthread_mutex_destroy(&shm->robustMutex) != 0)
+ return 1;
+ return 0;
+ }
+
+ void DoTest_Child()
+ {
+ // Lock the robust mutex
+ struct timespec timeoutTime;
+ if (GetFailTimeoutTime(&timeoutTime) != 0)
+ return;
+ if (pthread_mutex_timedlock(&shm->robustMutex, &timeoutTime) != 0)
+ return;
+
+ // Notify parent that robust mutex is locked
+ if (SetConditionValue(1) != 0)
+ return;
+
+ // Wait a short period to let the parent block on waiting for a lock
+ sleep(1);
+
+ // Abandon the mutex by exiting the process while holding the lock. Parent's wait should be released by EOWNERDEAD.
+ }
+
+ int main()
+ {
+ int result = DoTest();
+ return result >= 0 ? result : 0;
+ }" HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES)
+ set(CMAKE_REQUIRED_LIBRARIES)
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+ if(NOT HAVE_LIBUUID_H)
+ unset(HAVE_LIBUUID_H CACHE)
+ message(FATAL_ERROR "Cannot find libuuid. Try installing uuid-dev or the appropriate packages for your platform")
+ endif()
+ set(HAVE_COREFOUNDATION 1)
+ set(HAVE__NSGETENVIRON 1)
+ set(DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX 1)
+ set(PAL_PTRACE "ptrace((cmd), (pid), (caddr_t)(addr), (data))")
+ set(PAL_PT_ATTACH PT_ATTACH)
+ set(PAL_PT_DETACH PT_DETACH)
+ set(PAL_PT_READ_D PT_READ_D)
+ set(PAL_PT_WRITE_D PT_WRITE_D)
+ set(HAS_FTRUNCATE_LENGTH_ISSUE 1)
+ set(HAVE_SCHED_OTHER_ASSIGNABLE 1)
+
+elseif(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
+ if(NOT HAVE_LIBUNWIND_H)
+ unset(HAVE_LIBUNWIND_H CACHE)
+ message(FATAL_ERROR "Cannot find libunwind. Try installing libunwind8 and libunwind8-dev (or the appropriate packages for your platform)")
+ endif()
+ if(NOT HAVE_BSD_UUID_H)
+ unset(HAVE_BSD_UUID_H CACHE)
+ message(FATAL_ERROR "Cannot find uuid.h")
+ endif()
+ set(DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX 0)
+ set(PAL_PTRACE "ptrace((cmd), (pid), (caddr_t)(addr), (data))")
+ set(PAL_PT_ATTACH PT_ATTACH)
+ set(PAL_PT_DETACH PT_DETACH)
+ set(PAL_PT_READ_D PT_READ_D)
+ set(PAL_PT_WRITE_D PT_WRITE_D)
+ set(HAS_FTRUNCATE_LENGTH_ISSUE 0)
+ set(BSD_REGS_STYLE "((reg).r_##rr)")
+ set(HAVE_SCHED_OTHER_ASSIGNABLE 1)
+elseif(CMAKE_SYSTEM_NAME STREQUAL NetBSD)
+ if(NOT HAVE_LIBUNWIND_H)
+ unset(HAVE_LIBUNWIND_H CACHE)
+ message(FATAL_ERROR "Cannot find libunwind. Try installing libunwind8 and libunwind8-dev (or the appropriate packages for your platform)")
+ endif()
+ if(NOT HAVE_BSD_UUID_H)
+ unset(HAVE_BSD_UUID_H CACHE)
+ message(FATAL_ERROR "Cannot find uuid.h")
+ endif()
+ set(DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX 0)
+ set(PAL_PTRACE "ptrace((cmd), (pid), (void*)(addr), (data))")
+ set(PAL_PT_ATTACH PT_ATTACH)
+ set(PAL_PT_DETACH PT_DETACH)
+ set(PAL_PT_READ_D PT_READ_D)
+ set(PAL_PT_WRITE_D PT_WRITE_D)
+ set(HAS_FTRUNCATE_LENGTH_ISSUE 0)
+ set(BSD_REGS_STYLE "((reg).regs[_REG_##RR])")
+ set(HAVE_SCHED_OTHER_ASSIGNABLE 0)
+
+elseif(CMAKE_SYSTEM_NAME STREQUAL SunOS)
+ if(NOT HAVE_LIBUNWIND_H)
+ unset(HAVE_LIBUNWIND_H CACHE)
+ message(FATAL_ERROR "Cannot find libunwind. Try installing libunwind8 and libunwind8-dev (or the appropriate packages for your platform)")
+ endif()
+ if(NOT HAVE_LIBUUID_H)
+ unset(HAVE_LIBUUID_H CACHE)
+ message(FATAL_ERROR "Cannot find libuuid. Try installing uuid-dev or the appropriate packages for your platform")
+ endif()
+ set(DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX 0)
+ set(PAL_PTRACE "ptrace((cmd), (pid), (caddr_t)(addr), (data))")
+ set(PAL_PT_ATTACH PT_ATTACH)
+ set(PAL_PT_DETACH PT_DETACH)
+ set(PAL_PT_READ_D PT_READ_D)
+ set(PAL_PT_WRITE_D PT_WRITE_D)
+ set(HAS_FTRUNCATE_LENGTH_ISSUE 0)
+else() # Anything else is Linux
+ if(NOT HAVE_LIBUNWIND_H)
+ unset(HAVE_LIBUNWIND_H CACHE)
+ message(FATAL_ERROR "Cannot find libunwind. Try installing libunwind8 and libunwind8-dev (or the appropriate packages for your platform)")
+ endif()
+ if(NOT HAVE_LTTNG_TRACEPOINT_H AND FEATURE_EVENT_TRACE)
+ unset(HAVE_LTTNG_TRACEPOINT_H CACHE)
+ message(FATAL_ERROR "Cannot find liblttng-ust-dev. Try installing liblttng-ust-dev (or the appropriate packages for your platform)")
+ endif()
+ if(NOT HAVE_LIBUUID_H)
+ unset(HAVE_LIBUUID_H CACHE)
+ message(FATAL_ERROR "Cannot find libuuid. Try installing uuid-dev or the appropriate packages for your platform")
+ endif()
+ set(DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX 0)
+ set(PAL_PTRACE "ptrace((cmd), (pid), (void*)(addr), (data))")
+ set(PAL_PT_ATTACH PTRACE_ATTACH)
+ set(PAL_PT_DETACH PTRACE_DETACH)
+ set(PAL_PT_READ_D PTRACE_PEEKDATA)
+ set(PAL_PT_WRITE_D PTRACE_POKEDATA)
+ set(HAS_FTRUNCATE_LENGTH_ISSUE 0)
+ set(HAVE_SCHED_OTHER_ASSIGNABLE 1)
+endif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
diff --git a/src/pal/src/cruntime/file.cpp b/src/pal/src/cruntime/file.cpp
new file mode 100644
index 0000000000..5fe2b671f3
--- /dev/null
+++ b/src/pal/src/cruntime/file.cpp
@@ -0,0 +1,954 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ file.c
+
+Abstract:
+
+ Implementation of the file functions in the C runtime library that
+ are Windows specific.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/cruntime.h"
+
+#include "pal/thread.hpp"
+#include "pal/threadsusp.hpp"
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <pthread.h>
+
+#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
+ #define CLEARERR(f) clearerr((f)->bsdFilePtr)
+#else
+ #define CLEARERR(f)
+#endif
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+/* Global variables storing the std streams.*/
+PAL_FILE PAL_Stdout;
+PAL_FILE PAL_Stdin;
+PAL_FILE PAL_Stderr;
+
+/*++
+
+Function:
+
+ CRTInitStdStreams.
+
+ Initilizes the standard streams.
+ Returns TRUE on success, FALSE otherwise.
+--*/
+BOOL CRTInitStdStreams()
+{
+ /* stdout */
+ PAL_Stdout.bsdFilePtr = stdout;
+ PAL_Stdout.PALferrorCode = PAL_FILE_NOERROR;
+ PAL_Stdout.bTextMode = TRUE;
+
+ /* stdin */
+ PAL_Stdin.bsdFilePtr = stdin;
+ PAL_Stdin.PALferrorCode = PAL_FILE_NOERROR;
+ PAL_Stdin.bTextMode = TRUE;
+
+ /* stderr */
+ PAL_Stderr.bsdFilePtr = stderr;
+ PAL_Stderr.PALferrorCode = PAL_FILE_NOERROR;
+ PAL_Stderr.bTextMode = TRUE;
+ return TRUE;
+}
+
+/*++
+Function :
+
+ MapFileOpenModes
+
+ Maps Windows file open modes to Unix fopen modes and validates.
+
+--*/
+static LPSTR MapFileOpenModes(LPSTR str , BOOL * bTextMode)
+{
+ LPSTR retval = NULL;
+ LPSTR temp = NULL;
+
+ if (NULL == bTextMode)
+ {
+ ASSERT("MapFileOpenModes called with a NULL parameter for bTextMode.\n");
+ return NULL;
+ }
+
+ *bTextMode = TRUE;
+
+ if (NULL == str)
+ {
+ ASSERT("MapFileOpenModes called with a NULL parameter for str.\n");
+ return NULL;
+ }
+
+ /* The PAL behaves differently for some Windows file open modes:
+
+ c, n, S, R, and T: these are all hints to the system that aren't supported
+ by the PAL. Since the user cannot depend on this behavior, it's safe to
+ simply ignore these modes.
+
+ D: specifies a file as temporary. This file is expected to be deleted when
+ the last file descriptor is closed. The PAL does not support this behavior
+ and asserts when this mode is used.
+
+ t: represents opening in text mode. Calls to fdopen on Unix don't accept
+ 't' so it is silently stripped out. However, the PAL supports the mode by
+ having the PAL wrappers do the translation of CR-LF to LF and vice versa.
+
+ t vs. b: To get binary mode, you must explicitly use 'b'. If neither mode
+ is specified on Windows, the default mode is defined by the global
+ variable _fmode. The PAL simply defaults to text mode. After examining
+ CLR usage patterns, the PAL behavior seems acceptable. */
+
+ /* Check if the mode specifies deleting the temporary file
+ automatically when the last file descriptor is closed.
+ The PAL does not support this behavior. */
+ if (NULL != strchr(str,'D'))
+ {
+ ASSERT("The PAL doesn't support the 'D' flag for _fdopen and fopen.\n");
+ return NULL;
+ }
+
+ /* Check if the mode specifies opening in binary.
+ If so, set the bTextMode to false. */
+ if(NULL != strchr(str,'b'))
+ {
+ *bTextMode = FALSE;
+ }
+
+ retval = (LPSTR)PAL_malloc( ( strlen( str ) + 1 ) * sizeof( CHAR ) );
+ if (NULL == retval)
+ {
+ ERROR("Unable to allocate memory.\n");
+ return NULL;
+ }
+
+ temp = retval;
+ while ( *str )
+ {
+ if ( *str == 'r' || *str == 'w' || *str == 'a' )
+ {
+ *temp = *str;
+ temp++;
+ if ( ( ++str != NULL ) && *str == '+' )
+ {
+ *temp = *str;
+ temp++;
+ str++;
+ }
+ }
+ else
+ {
+ str++;
+ }
+ }
+ *temp = '\0';
+ return retval;
+}
+
+#if UNGETC_NOT_RETURN_EOF
+/*++
+Function :
+
+ WriteOnlyMode
+
+ Returns TRUE to if a file is opened in write-only mode,
+ Otherwise FALSE.
+
+--*/
+static BOOL WriteOnlyMode(FILE* pFile)
+{
+ INT fd, flags;
+
+ if (pFile != NULL)
+ {
+ fd = fileno(pFile);
+ if ((flags = fcntl(fd, F_GETFL)) >= 0)
+ {
+ if ((flags & O_ACCMODE) == O_WRONLY)
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+#endif //UNGETC_NOT_RETURN_EOF
+
+/*++
+Function:
+ _getw
+
+Gets an integer from a stream.
+
+Return Value
+
+_getw returns the integer value read. A return value of EOF indicates
+either an error or end of file. However, because the EOF value is also
+a legitimate integer value, use feof or ferror to verify an
+end-of-file or error condition.
+
+Parameter
+
+file Pointer to FILE structure
+
+--*/
+int
+__cdecl
+_getw(PAL_FILE *f)
+{
+ INT ret = 0;
+
+ PERF_ENTRY(_getw);
+ ENTRY("_getw (f=%p)\n", f);
+
+ _ASSERTE(f != NULL);
+
+ CLEARERR(f);
+
+ ret = getw( f->bsdFilePtr );
+ LOGEXIT( "returning %d\n", ret );
+ PERF_EXIT(_getw);
+
+ return ret;
+}
+
+
+/*++
+Function:
+ _fdopen
+
+see MSDN
+
+--*/
+PAL_FILE *
+__cdecl
+_fdopen(
+ int handle,
+ const char *mode)
+{
+ PAL_FILE *f = NULL;
+ LPSTR supported = NULL;
+ BOOL bTextMode = TRUE;
+
+ PERF_ENTRY(_fdopen);
+ ENTRY("_fdopen (handle=%d mode=%p (%s))\n", handle, mode, mode);
+
+ _ASSERTE(mode != NULL);
+
+ f = (PAL_FILE*)PAL_malloc( sizeof( PAL_FILE ) );
+ if ( f )
+ {
+ supported = MapFileOpenModes( (char*)mode , &bTextMode);
+ if ( !supported )
+ {
+ PAL_free(f);
+ f = NULL;
+ goto EXIT;
+ }
+
+ f->bsdFilePtr = (FILE *)fdopen( handle, supported );
+ f->PALferrorCode = PAL_FILE_NOERROR;
+ /* Make sure fdopen did not fail. */
+ if ( !f->bsdFilePtr )
+ {
+ PAL_free( f );
+ f = NULL;
+ }
+
+ PAL_free( supported );
+ supported = NULL;
+ }
+ else
+ {
+ ERROR( "Unable to allocate memory for the PAL_FILE wrapper!\n" );
+ }
+
+EXIT:
+ LOGEXIT( "_fdopen returns FILE* %p\n", f );
+ PERF_EXIT(_fdopen);
+ return f;
+}
+
+
+/*++
+
+Function :
+ fopen
+
+see MSDN doc.
+
+--*/
+PAL_FILE *
+__cdecl
+PAL_fopen(const char * fileName, const char * mode)
+{
+ PAL_FILE *f = NULL;
+ LPSTR supported = NULL;
+ LPSTR UnixFileName = NULL;
+ struct stat stat_data;
+ BOOL bTextMode = TRUE;
+
+ PERF_ENTRY(fopen);
+ ENTRY("fopen ( fileName=%p (%s) mode=%p (%s))\n", fileName, fileName, mode , mode );
+
+ _ASSERTE(fileName != NULL);
+ _ASSERTE(mode != NULL);
+
+ if ( *mode == 'r' || *mode == 'w' || *mode == 'a' )
+ {
+ supported = MapFileOpenModes( (char*)mode,&bTextMode);
+
+ if ( !supported )
+ {
+ goto done;
+ }
+
+ UnixFileName = PAL__strdup(fileName);
+ if (UnixFileName == NULL )
+ {
+ ERROR("PAL__strdup() failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+
+ FILEDosToUnixPathA( UnixFileName );
+
+ /*I am not checking for the case where stat fails
+ *as fopen will handle the error more gracefully in case
+ *UnixFileName is invalid*/
+ if ((stat(UnixFileName, &stat_data) == 0 ) &&
+ ((stat_data.st_mode & S_IFMT) == S_IFDIR))
+ {
+ goto done;
+ }
+
+ f = (PAL_FILE*)PAL_malloc( sizeof( PAL_FILE ) );
+ if ( f )
+ {
+ f->bsdFilePtr = (FILE*)fopen( UnixFileName, supported );
+ f->PALferrorCode = PAL_FILE_NOERROR;
+ f->bTextMode = bTextMode;
+ if ( !f->bsdFilePtr )
+ {
+ /* Failed */
+ PAL_free( f );
+ f = NULL;
+ }
+#if UNGETC_NOT_RETURN_EOF
+ else
+ {
+ f->bWriteOnlyMode = WriteOnlyMode(f->bsdFilePtr);
+ }
+#endif //UNGETC_NOT_RETURN_EOF
+ }
+ else
+ {
+ ERROR( "Unable to allocate memory to the PAL_FILE wrapper\n" );
+ }
+ }
+ else
+ {
+ ERROR( "The mode flags must start with either an a, w, or r.\n" );
+ }
+
+done:
+ PAL_free( supported );
+ supported = NULL;
+ PAL_free( UnixFileName );
+
+ LOGEXIT( "fopen returns FILE* %p\n", f );
+ PERF_EXIT(fopen);
+ return f;
+}
+
+/*++
+Function:
+ _wfopen
+
+see MSDN doc.
+
+--*/
+PAL_FILE *
+__cdecl
+_wfopen(
+ const wchar_16 *fileName,
+ const wchar_16 *mode)
+{
+ CHAR mbFileName[ _MAX_PATH ];
+ CHAR mbMode[ 10 ];
+ PAL_FILE * filePtr = NULL;
+
+ PERF_ENTRY(_wfopen);
+ ENTRY("_wfopen(fileName:%p (%S), mode:%p (%S))\n", fileName, fileName, mode, mode);
+
+ _ASSERTE(fileName != NULL);
+ _ASSERTE(mode != NULL);
+
+ /* Convert the parameters to ASCII and defer to PAL_fopen */
+ if ( WideCharToMultiByte( CP_ACP, 0, fileName, -1, mbFileName,
+ sizeof mbFileName, NULL, NULL ) != 0 )
+ {
+ if ( WideCharToMultiByte( CP_ACP, 0, mode, -1, mbMode,
+ sizeof mbMode, NULL, NULL ) != 0 )
+ {
+ filePtr = PAL_fopen(mbFileName, mbMode);
+ }
+ else
+ {
+ ERROR( "An error occurred while converting mode to ANSI.\n" );
+ }
+ }
+ else
+ {
+ ERROR( "An error occurred while converting"
+ " fileName to ANSI string.\n" );
+ }
+ LOGEXIT("_wfopen returning FILE* %p\n", filePtr);
+ PERF_EXIT(_wfopen);
+ return filePtr;
+}
+
+/*++
+Function:
+_wfsopen
+
+see MSDN doc.
+
+--*/
+PAL_FILE *
+__cdecl
+_wfsopen(
+ const wchar_16 *fileName,
+ const wchar_16 *mode,
+ int shflag)
+{
+ // UNIXTODO: Implement this.
+ ERROR("Needs Implementation!!!");
+ return NULL;
+}
+
+/*++
+Function:
+ _putw
+
+Writes an integer to a stream.
+
+Return Value
+
+_putw returns the value written. A return value of EOF may indicate an
+error. Because EOF is also a legitimate integer value, use ferror to
+verify an error.
+
+Parameters
+
+c Binary integer to be output
+file Pointer to FILE structure
+
+--*/
+int
+__cdecl
+_putw(int c, PAL_FILE *f)
+{
+ INT ret = 0;
+
+ PERF_ENTRY(_putw);
+ ENTRY("_putw (c=0x%x, f=%p)\n", c, f);
+
+ _ASSERTE(f != NULL);
+
+ CLEARERR(f);
+
+ ret = putw(c, f->bsdFilePtr );
+ LOGEXIT( "returning %d\n", ret );
+ PERF_EXIT(_putw);
+
+ return ret;
+}
+
+
+/*++
+Function
+ PAL_get_stdout.
+
+ Returns the stdout stream.
+--*/
+PAL_FILE * __cdecl PAL_get_stdout(int caller)
+{
+ PERF_ENTRY(get_stdout);
+ ENTRY("PAL_get_stdout\n");
+ LOGEXIT("PAL_get_stdout returns PAL_FILE * %p\n", &PAL_Stdout );
+ PERF_EXIT(get_stdout);
+ return &PAL_Stdout;
+}
+
+/*++
+Function
+ PAL_get_stdin.
+
+ Returns the stdin stream.
+--*/
+PAL_FILE * __cdecl PAL_get_stdin(int caller)
+{
+ PERF_ENTRY(get_stdin);
+ ENTRY("PAL_get_stdin\n");
+ LOGEXIT("PAL_get_stdin returns PAL_FILE * %p\n", &PAL_Stdin );
+ PERF_EXIT(get_stdin);
+ return &PAL_Stdin;
+}
+
+/*++
+Function
+ PAL_get_stderr.
+
+ Returns the stderr stream.
+--*/
+PAL_FILE * __cdecl PAL_get_stderr(int caller)
+{
+ PERF_ENTRY(get_stderr);
+ ENTRY("PAL_get_stderr\n");
+ LOGEXIT("PAL_get_stderr returns PAL_FILE * %p\n", &PAL_Stderr );
+ PERF_EXIT(get_stderr);
+ return &PAL_Stderr;
+}
+
+
+/*++
+
+Function:
+
+ _close
+
+See msdn for more details.
+--*/
+int __cdecl PAL__close(int handle)
+{
+ INT nRetVal = 0;
+
+ PERF_ENTRY(_close);
+ ENTRY( "_close( handle=%d )\n", handle );
+
+ nRetVal = close( handle );
+
+ LOGEXIT( "_close returning %d.\n", nRetVal );
+ PERF_EXIT(_close);
+ return nRetVal;
+}
+
+ int __cdecl PAL__flushall()
+ {
+ return fflush(NULL);
+ }
+
+wchar_16 *
+__cdecl
+PAL_fgetws(wchar_16 *s, int n, PAL_FILE *f)
+{
+ ASSERT (0);
+ return NULL;
+}
+
+
+/*++
+Function :
+
+ fread
+
+ See MSDN for more details.
+--*/
+
+size_t
+__cdecl
+PAL_fread(void * buffer, size_t size, size_t count, PAL_FILE * f)
+{
+ size_t nReadBytes = 0;
+
+ PERF_ENTRY(fread);
+ ENTRY( "fread( buffer=%p, size=%d, count=%d, f=%p )\n",
+ buffer, size, count, f );
+
+ _ASSERTE(f != NULL);
+
+ CLEARERR(f);
+
+ if(f->bTextMode != TRUE)
+ {
+ nReadBytes = fread( buffer, size, count, f->bsdFilePtr );
+ }
+ else
+ {
+ size_t i=0;
+ if(size > 0)
+ {
+ size_t j=0;
+ LPSTR temp = (LPSTR)buffer;
+ int nChar = 0;
+ int nCount =0;
+
+ for(i=0; i< count; i++)
+ {
+ for(j=0; j< size; j++)
+ {
+ if((nChar = PAL_getc(f)) == EOF)
+ {
+ nReadBytes = i;
+ goto done;
+ }
+ else
+ {
+ temp[nCount++]=nChar;
+ }
+ }
+ }
+ }
+ nReadBytes = i;
+ }
+
+done:
+ LOGEXIT( "fread returning size_t %d\n", nReadBytes );
+ PERF_EXIT(fread);
+ return nReadBytes;
+}
+
+
+/*++
+Function :
+
+ ferror
+
+ See MSDN for more details.
+--*/
+int
+_cdecl
+PAL_ferror(PAL_FILE * f)
+{
+ INT nErrorCode = PAL_FILE_NOERROR;
+
+ PERF_ENTRY(ferror);
+ ENTRY( "ferror( f=%p )\n", f );
+
+ _ASSERTE(f != NULL);
+
+ nErrorCode = ferror( f->bsdFilePtr );
+ if ( 0 == nErrorCode )
+ {
+ /* See if the PAL file error code is set. */
+ nErrorCode = f->PALferrorCode;
+ }
+
+ LOGEXIT( "ferror returns %d\n", nErrorCode );
+ PERF_EXIT(ferror);
+ return nErrorCode;
+}
+
+
+/*++
+Function :
+
+ fclose
+
+ See MSDN for more details.
+--*/
+int
+_cdecl
+PAL_fclose(PAL_FILE * f)
+{
+ INT nRetVal = 0;
+
+ PERF_ENTRY(fclose);
+ ENTRY( "fclose( f=%p )\n", f );
+
+ _ASSERTE(f != NULL);
+
+ CLEARERR(f);
+
+ nRetVal = fclose( f->bsdFilePtr );
+ PAL_free( f );
+
+ LOGEXIT( "fclose returning %d\n", nRetVal );
+ PERF_EXIT(fclose);
+ return nRetVal;
+}
+
+/*++
+Function :
+
+ setbuf
+
+ See MSDN for more details.
+--*/
+void
+_cdecl
+PAL_setbuf(PAL_FILE * f, char * buffer)
+{
+ PERF_ENTRY(setbuf);
+ ENTRY( "setbuf( %p, %p )\n", f, buffer );
+
+ _ASSERTE(f != NULL);
+
+ setbuf( f->bsdFilePtr, buffer );
+
+ LOGEXIT( "setbuf\n" );
+ PERF_EXIT(setbuf);
+}
+
+/*++
+Function :
+
+ fputs
+
+ See MSDN for more details.
+--*/
+int
+_cdecl
+PAL_fputs(const char * str, PAL_FILE * f)
+{
+ INT nRetVal = 0;
+
+ PERF_ENTRY(fputs);
+ ENTRY( "fputs( %p (%s), %p )\n", str, str, f);
+
+ _ASSERTE(str != NULL);
+ _ASSERTE(f != NULL);
+
+ CLEARERR(f);
+
+ nRetVal = fputs( str, f->bsdFilePtr );
+
+ LOGEXIT( "fputs returning %d\n", nRetVal );
+ PERF_EXIT(fputs);
+ return nRetVal;
+}
+
+/*--
+Function :
+
+ fputc
+
+ See MSDN for more details.
+--*/
+int
+_cdecl
+PAL_fputc(int c, PAL_FILE * f)
+{
+ INT nRetVal = 0;
+
+ PERF_ENTRY(fputc);
+ ENTRY( "fputc( 0x%x (%c), %p )\n", c, c, f);
+
+ _ASSERTE(f != NULL);
+
+ CLEARERR(f);
+
+ nRetVal = fputc( c, f->bsdFilePtr );
+
+ LOGEXIT( "fputc returning %d\n", nRetVal );
+ PERF_EXIT(fputc);
+ return nRetVal;
+}
+
+/*--
+Function :
+
+ putchar
+
+ See MSDN for more details.
+--*/
+int
+_cdecl
+PAL_putchar( int c )
+{
+ INT nRetVal = 0;
+
+ PERF_ENTRY(putchar);
+ ENTRY( "putchar( 0x%x (%c) )\n", c, c);
+
+ nRetVal = putchar( c );
+
+ LOGEXIT( "putchar returning %d\n", nRetVal );
+ PERF_EXIT(putchar);
+ return nRetVal;
+}
+
+/*++
+Function :
+
+ ftell
+
+ See MSDN for more details.
+--*/
+LONG
+_cdecl
+PAL_ftell(PAL_FILE * f)
+{
+ long lRetVal = 0;
+
+ PERF_ENTRY(ftell);
+ ENTRY( "ftell( %p )\n", f );
+
+ _ASSERTE(f != NULL);
+ lRetVal = ftell( f->bsdFilePtr );
+
+#ifdef BIT64
+ /* Windows does not set an error if the file pointer's position
+ is greater than _I32_MAX. It just returns -1. */
+ if (lRetVal > _I32_MAX)
+ {
+ lRetVal = -1;
+ }
+#endif
+
+ LOGEXIT( "ftell returning %ld\n", lRetVal );
+ PERF_EXIT(ftell);
+ /* This explicit cast to LONG is used to silence any potential warnings
+ due to implicitly casting the native long lRetVal to LONG when returning. */
+ return (LONG)lRetVal;
+}
+
+/*++
+Function :
+
+ feof
+
+ See MSDN for more details.
+--*/
+int
+_cdecl
+PAL_feof(PAL_FILE * f)
+{
+ INT nRetVal = 0;
+
+ PERF_ENTRY(feof);
+ ENTRY( "feof( %p )\n", f );
+
+ _ASSERTE(f != NULL);
+ nRetVal = feof( f->bsdFilePtr );
+
+ LOGEXIT( "feof returning %d\n", nRetVal );
+ PERF_EXIT(feof);
+ return nRetVal;
+}
+
+/*++
+Function :
+
+ getc
+
+ See MSDN for more details.
+--*/
+int
+_cdecl
+PAL_getc(PAL_FILE * f)
+{
+ INT nRetVal = 0;
+ INT temp =0;
+
+ PERF_ENTRY(getc);
+ ENTRY( "getc( %p )\n", f );
+
+ _ASSERTE(f != NULL);
+
+ CLEARERR(f);
+
+ nRetVal = getc( f->bsdFilePtr );
+
+ if ( (f->bTextMode) && (nRetVal == '\r') )
+ {
+ if ((temp = getc( f->bsdFilePtr ))== '\n')
+ {
+ nRetVal ='\n';
+ }
+ else if (EOF == ungetc( temp, f->bsdFilePtr ))
+ {
+ ERROR("ungetc operation failed\n");
+ }
+ }
+
+ LOGEXIT( "getc returning %d\n", nRetVal );
+ PERF_EXIT(getc);
+ return nRetVal;
+}
+
+/*++
+Function :
+
+ ungetc
+
+ See MSDN for more details.
+--*/
+int
+_cdecl
+PAL_ungetc(int c, PAL_FILE * f)
+{
+ INT nRetVal = 0;
+
+ PERF_ENTRY(ungetc);
+ ENTRY( "ungetc( %c, %p )\n", c, f );
+
+ _ASSERTE(f != NULL);
+
+#if UNGETC_NOT_RETURN_EOF
+ /* On some Unix platform such as Solaris, ungetc does not return EOF
+ on write-only file. */
+ if (f->bWriteOnlyMode)
+ {
+ nRetVal = EOF;
+ }
+ else
+#endif //UNGETC_NOT_RETURN_EOF
+ {
+ CLEARERR(f);
+
+ nRetVal = ungetc( c, f->bsdFilePtr );
+ }
+
+ LOGEXIT( "ungetc returning %d\n", nRetVal );
+ PERF_EXIT(ungetc);
+ return nRetVal;
+}
+
+
+
+/*++
+Function :
+
+ setvbuf
+
+ See MSDN for more details.
+--*/
+int
+_cdecl
+PAL_setvbuf(PAL_FILE *f, char *buf, int type, size_t size)
+{
+ INT nRetVal = 0;
+
+ PERF_ENTRY(setvbuf);
+ ENTRY( "setvbuf( %p, %p, %d, %ul )\n", f, buf, type, size);
+
+ _ASSERTE(f != NULL);
+
+ nRetVal = setvbuf(f->bsdFilePtr, buf, type, size);
+
+ LOGEXIT( "setvbuf returning %d\n", nRetVal );
+ PERF_EXIT(setvbuf);
+ return nRetVal;
+}
diff --git a/src/pal/src/cruntime/filecrt.cpp b/src/pal/src/cruntime/filecrt.cpp
new file mode 100644
index 0000000000..48079b309c
--- /dev/null
+++ b/src/pal/src/cruntime/filecrt.cpp
@@ -0,0 +1,571 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ filecrt.cpp
+
+Abstract:
+
+ Implementation of the file functions in the C runtime library that
+ are Windows specific.
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/file.hpp"
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/cruntime.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#ifdef __APPLE__
+#include <sys/syscall.h>
+#endif // __APPLE__
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+/*++
+Function:
+ _open_osfhandle
+
+See MSDN doc.
+--*/
+int
+__cdecl
+_open_osfhandle( INT_PTR osfhandle, int flags )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthrCurrent = NULL;
+ IPalObject *pobjFile = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pDataLock = NULL;
+ INT nRetVal = -1;
+ INT openFlags = 0;
+
+ PERF_ENTRY(_open_osfhandle);
+ ENTRY( "_open_osfhandle (osfhandle=%#x, flags=%#x)\n", osfhandle, flags );
+
+ pthrCurrent = InternalGetCurrentThread();
+
+ if (flags != _O_RDONLY)
+ {
+ ASSERT("flag(%#x) not supported\n", flags);
+ goto EXIT;
+ }
+
+ openFlags |= O_RDONLY;
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pthrCurrent,
+ reinterpret_cast<HANDLE>(osfhandle),
+ &aotFile,
+ 0,
+ &pobjFile
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Error dereferencing file handle\n");
+ goto EXIT;
+ }
+
+ palError = pobjFile->GetProcessLocalData(
+ pthrCurrent,
+ ReadLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR == palError)
+ {
+ if ('\0' != pLocalData->unix_filename[0])
+ {
+ nRetVal = InternalOpen(pLocalData->unix_filename, openFlags);
+ }
+ else /* the only file object with no unix_filename is a pipe */
+ {
+ /* check if the file pipe descrptor is for read or write */
+ if (pLocalData->open_flags == O_WRONLY)
+ {
+ ERROR( "Couldn't open a write pipe on read mode\n");
+ goto EXIT;
+ }
+
+ nRetVal = pLocalData->unix_fd;
+ }
+
+ if ( nRetVal == -1 )
+ {
+ ERROR( "Error: %s.\n", strerror( errno ) );
+ }
+ }
+ else
+ {
+ ASSERT("Unable to access file data");
+ }
+
+EXIT:
+
+ if (NULL != pDataLock)
+ {
+ pDataLock->ReleaseLock(pthrCurrent, FALSE);
+ }
+
+ if (NULL != pobjFile)
+ {
+ pobjFile->ReleaseReference(pthrCurrent);
+ }
+
+ LOGEXIT( "_open_osfhandle return nRetVal:%d\n", nRetVal);
+ PERF_EXIT(_open_osfhandle);
+ return nRetVal;
+}
+
+
+/*++
+Function:
+ PAL_fflush
+
+See MSDN for more details.
+--*/
+int
+_cdecl
+PAL_fflush( PAL_FILE *stream )
+{
+ int nRetVal = 0;
+
+ PERF_ENTRY(fflush);
+ ENTRY( "fflush( %p )\n", stream );
+
+ nRetVal = fflush(stream ? stream->bsdFilePtr : NULL);
+
+ LOGEXIT( "fflush returning %d\n", nRetVal );
+ PERF_EXIT(fflush);
+ return nRetVal;
+}
+
+
+/*++
+PAL__getcwd
+
+Wrapper function for getcwd.
+
+Input parameters:
+
+szBuf = a copy of the absolute pathname of the current working directory
+is copied into szBuf.
+nSize = size, in bytes, of the array referenced by szBuf.
+
+Return value:
+ A pointer to the pathname if successful, otherwise NULL is returned
+--*/
+char *
+__cdecl
+PAL__getcwd(
+ char *szBuf,
+ size_t nSize
+ )
+{
+ return (char *)getcwd(szBuf, nSize);
+}
+
+
+/*++
+PAL_mkstemp
+
+Wrapper function for InternalMkstemp.
+
+Input parameters:
+
+szNameTemplate = template to follow when naming the created file
+
+Return value:
+ Open file descriptor on success, -1 if file could not be created
+--*/
+int
+__cdecl
+PAL_mkstemp(char *szNameTemplate)
+{
+ return InternalMkstemp(szNameTemplate);
+}
+
+/*++
+InternalMkstemp
+
+Wrapper for mkstemp.
+
+Input parameters:
+
+szNameTemplate = template to follow when naming the created file
+
+Return value:
+ Open file descriptor on success, -1 if file could not be created
+--*/
+int
+CorUnix::InternalMkstemp(
+ char *szNameTemplate
+ )
+{
+ int nRet = -1;
+#if MKSTEMP64_IS_USED_INSTEAD_OF_MKSTEMP
+ nRet = mkstemp64(szNameTemplate);
+#else
+ nRet = mkstemp(szNameTemplate);
+#endif
+ return nRet;
+}
+
+
+/*++
+PAL__open
+
+Wrapper function for InternalOpen.
+
+Input parameters:
+
+szPath = pointer to a pathname of a file to be opened
+nFlags = arguments that control how the file should be accessed
+mode = file permission settings that are used only when a file is created
+
+Return value:
+ File descriptor on success, -1 on failure
+--*/
+int
+__cdecl
+PAL__open(
+ const char *szPath,
+ int nFlags,
+ ...
+ )
+{
+ int nRet = -1;
+ int mode = 0;
+ va_list ap;
+
+ // If nFlags does not contain O_CREAT, the mode parameter will be ignored.
+ if (nFlags & O_CREAT)
+ {
+ va_start(ap, nFlags);
+ mode = va_arg(ap, int);
+ va_end(ap);
+ }
+
+ nRet = InternalOpen(szPath, nFlags, mode);
+ return nRet;
+}
+
+/*++
+InternalOpen
+
+Wrapper for open.
+
+Input parameters:
+
+szPath = pointer to a pathname of a file to be opened
+nFlags = arguments that control how the file should be accessed
+mode = file permission settings that are used only when a file is created
+
+Return value:
+ File descriptor on success, -1 on failure
+--*/
+int
+CorUnix::InternalOpen(
+ const char *szPath,
+ int nFlags,
+ ...
+ )
+{
+ int nRet = -1;
+ int mode = 0;
+ va_list ap;
+
+ // If nFlags does not contain O_CREAT, the mode parameter will be ignored.
+ if (nFlags & O_CREAT)
+ {
+ va_start(ap, nFlags);
+ mode = va_arg(ap, int);
+ va_end(ap);
+ }
+
+#if OPEN64_IS_USED_INSTEAD_OF_OPEN
+ nRet = open64(szPath, nFlags, mode);
+#else
+ nRet = open(szPath, nFlags, mode);
+#endif
+ return nRet;
+}
+
+
+/*++
+PAL_rename
+
+Wrapper function for rename.
+
+Input parameters:
+
+szOldName = pointer to the pathname of the file to be renamed
+szNewName = pointer to the new pathname of the file
+
+Return value:
+ Returns 0 on success and -1 on failure
+--*/
+int
+__cdecl
+PAL_rename(
+ const char *szOldName,
+ const char *szNewName
+ )
+{
+ return rename(szOldName, szNewName);
+}
+
+
+/*++
+PAL_fgets
+
+Wrapper function for InternalFgets.
+
+Input parameters:
+
+sz = stores characters read from the given file stream
+nSize = number of characters to be read
+pf = stream to read characters from
+
+Return value:
+ Returns a pointer to the string storing the characters on success
+ and NULL on failure.
+--*/
+char *
+__cdecl
+PAL_fgets(
+ char *sz,
+ int nSize,
+ PAL_FILE *pf
+ )
+{
+ char * szBuf;
+
+ PERF_ENTRY(fgets);
+ ENTRY( "fgets(sz=%p (%s) nSize=%d pf=%p)\n", sz, sz, nSize, pf);
+
+ if (pf != NULL)
+ {
+ szBuf = InternalFgets(sz, nSize, pf->bsdFilePtr, pf->bTextMode);
+ }
+ else
+ {
+ szBuf = NULL;
+ }
+
+ LOGEXIT("fgets() returns %p\n", szBuf);
+ PERF_EXIT(fgets);
+
+ return szBuf;
+}
+
+/*++
+InternalFgets
+
+Wrapper for fgets.
+
+Input parameters:
+
+sz = stores characters read from the given file stream
+nSize = number of characters to be read
+f = stream to read characters from
+fTextMode = flag that indicates if file contents are text or binary
+
+Return value:
+ Returns a pointer to the string storing the characters on success
+ and NULL on failure.
+
+Notes:
+In Unix systems, fgets() can return an error if it gets interrupted by a
+signal before reading anything, and errno is set to EINTR. When this
+happens, it is SOP to call fgets again.
+--*/
+char *
+CorUnix::InternalFgets(
+ char *sz,
+ int nSize,
+ FILE *f,
+ bool fTextMode
+ )
+{
+ char *retval = NULL;
+
+ _ASSERTE(sz != NULL);
+ _ASSERTE(f != NULL);
+
+#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
+ clearerr(f);
+#endif
+
+ do
+ {
+ retval = fgets(sz, nSize, f);
+ if (NULL==retval)
+ {
+ if (feof(f))
+ {
+ TRACE("Reached EOF\n");
+ break;
+ }
+ /* The man page suggests using ferror and feof to distinguish
+ between error and EOF, but feof and errno is sufficient.
+ Not all cases that set errno also flag ferror, so just
+ checking errno is the best solution. */
+ if (EINTR != errno)
+ {
+ WARN("got error; errno is %d (%s)\n",errno, strerror(errno));
+ break;
+ }
+ /* we ignored a EINTR error, reset the stream's error state */
+ clearerr(f);
+ TRACE("call got interrupted (EINTR), trying again\n");
+ }
+ if (fTextMode)
+ {
+ int len = strlen(sz);
+ if ((len>=2) && (sz[len-1]=='\n') && (sz[len-2]=='\r'))
+ {
+ sz[len-2]='\n';
+ sz[len-1]='\0';
+ }
+ }
+ } while(NULL == retval);
+
+ return retval;
+}
+
+/*++
+PAL_fwrite
+
+Wrapper function for InternalFwrite.
+
+Input parameters:
+
+pvBuffer = array of objects to write to the given file stream
+nSize = size of a object in bytes
+nCount = number of objects to write
+pf = stream to write characters to
+
+Return value:
+ Returns the number of objects written.
+--*/
+size_t
+__cdecl
+PAL_fwrite(
+ const void *pvBuffer,
+ size_t nSize,
+ size_t nCount,
+ PAL_FILE *pf
+ )
+{
+ size_t nWrittenBytes = 0;
+
+ PERF_ENTRY(fwrite);
+ ENTRY( "fwrite( pvBuffer=%p, nSize=%d, nCount=%d, pf=%p )\n",
+ pvBuffer, nSize, nCount, pf);
+ _ASSERTE(pf != NULL);
+
+ nWrittenBytes = InternalFwrite(pvBuffer, nSize, nCount, pf->bsdFilePtr, &pf->PALferrorCode);
+
+ LOGEXIT( "fwrite returning size_t %d\n", nWrittenBytes );
+ PERF_EXIT(fwrite);
+ return nWrittenBytes;
+}
+
+/*++
+InternalFwrite
+
+Wrapper for fwrite.
+
+Input parameters:
+
+pvBuffer = array of objects to write to the given file stream
+nSize = size of a object in bytes
+nCount = number of objects to write
+f = stream to write characters to
+pnErrorCode = reference to a PAL_FILE's fwrite error code field
+
+Return value:
+ Returns the number of objects written.
+--*/
+size_t
+CorUnix::InternalFwrite(
+ const void *pvBuffer,
+ size_t nSize,
+ size_t nCount,
+ FILE *f,
+ INT *pnErrorCode
+ )
+{
+ size_t nWrittenBytes = 0;
+ _ASSERTE(f != NULL);
+
+#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
+ clearerr(f);
+#endif
+
+ nWrittenBytes = fwrite(pvBuffer, nSize, nCount, f);
+
+ // Make sure no error ocurred.
+ if ( nWrittenBytes < nCount )
+ {
+ // Set the FILE* error code
+ *pnErrorCode = PAL_FILE_ERROR;
+ }
+
+ return nWrittenBytes;
+}
+
+
+/*++
+PAL_fseek
+
+Wrapper function for fseek.
+
+Input parameters:
+
+pf = a given file stream
+lOffset = distance from position to set file-position indicator
+nWhence = method used to determine the file_position indicator location relative to lOffset
+
+Return value:
+ 0 on success, -1 on failure.
+--*/
+int
+_cdecl
+PAL_fseek(
+ PAL_FILE * pf,
+ LONG lOffset,
+ int nWhence
+ )
+{
+ int nRet = 0;
+
+ PERF_ENTRY(fseek);
+ ENTRY( "fseek( %p, %ld, %d )\n", pf, lOffset, nWhence );
+
+ nRet = fseek(pf ? pf->bsdFilePtr : NULL, lOffset, nWhence);
+
+ LOGEXIT("fseek returning %d\n", nRet);
+ PERF_EXIT(fseek);
+ return nRet;
+}
diff --git a/src/pal/src/cruntime/lstr.cpp b/src/pal/src/cruntime/lstr.cpp
new file mode 100644
index 0000000000..2267d8491b
--- /dev/null
+++ b/src/pal/src/cruntime/lstr.cpp
@@ -0,0 +1,316 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ lstr.c
+
+Abstract:
+
+ Implementation of functions manipulating unicode/ansi strings. (lstr*A/W)
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+
+
+/*++
+Function:
+ lstrcatW
+
+The lstrcat function appends one string to another.
+
+Parameters
+
+lpString1 [in/out] Pointer to a null-terminated string. The buffer must be large
+ enough to contain both strings.
+lpString2 [in] Pointer to the null-terminated string to be appended to the
+ string specified in the lpString1 parameter.
+
+Return Values
+
+If the function succeeds, the return value is a pointer to the buffer.
+If the function fails, the return value is NULL.
+
+--*/
+LPWSTR
+PALAPI
+lstrcatW(
+ IN OUT LPWSTR lpString1,
+ IN LPCWSTR lpString2)
+{
+ LPWSTR lpStart = lpString1;
+
+ PERF_ENTRY(lstrcatW);
+ ENTRY("lstrcatW (lpString1=%p (%S), lpString2=%p (%S))\n",
+ lpString1?lpString1:W16_NULLSTRING,
+ lpString1?lpString1:W16_NULLSTRING, lpString2?lpString2:W16_NULLSTRING, lpString2?lpString2:W16_NULLSTRING);
+
+ if (lpString1 == NULL)
+ {
+ ERROR("invalid lpString1 argument\n");
+ LOGEXIT("lstrcatW returning LPWSTR NULL\n");
+ PERF_EXIT(lstrcatW);
+ return NULL;
+ }
+
+ if (lpString2 == NULL)
+ {
+ ERROR("invalid lpString2 argument\n");
+ LOGEXIT("lstrcatW returning LPWSTR NULL\n");
+ PERF_EXIT(lstrcatW);
+ return NULL;
+ }
+
+ /* find end of source string */
+ while (*lpString1)
+ {
+ lpString1++;
+ }
+
+ /* concatenate new string */
+ while(*lpString2)
+ {
+ *lpString1++ = *lpString2++;
+ }
+
+ /* add terminating null */
+ *lpString1 = '\0';
+
+ LOGEXIT("lstrcatW returning LPWSTR %p (%S)\n", lpStart, lpStart);
+ PERF_EXIT(lstrcatW);
+ return lpStart;
+}
+
+
+/*++
+Function:
+ lstrcpyW
+
+The lstrcpy function copies a string to a buffer.
+
+To copy a specified number of characters, use the lstrcpyn function.
+
+Parameters
+
+lpString1 [out] Pointer to a buffer to receive the contents of the string pointed
+ to by the lpString2 parameter. The buffer must be large enough to
+ contain the string, including the terminating null character.
+
+lpString2 [in] Pointer to the null-terminated string to be copied.
+
+Return Values
+
+If the function succeeds, the return value is a pointer to the buffer.
+If the function fails, the return value is NULL.
+
+--*/
+LPWSTR
+PALAPI
+lstrcpyW(
+ OUT LPWSTR lpString1,
+ IN LPCWSTR lpString2)
+{
+ LPWSTR lpStart = lpString1;
+
+ PERF_ENTRY(lstrcpyW);
+ ENTRY("lstrcpyW (lpString1=%p, lpString2=%p (%S))\n",
+ lpString1?lpString1:W16_NULLSTRING, lpString2?lpString2:W16_NULLSTRING, lpString2?lpString2:W16_NULLSTRING);
+
+ if (lpString1 == NULL)
+ {
+ ERROR("invalid lpString1 argument\n");
+ LOGEXIT("lstrcpyW returning LPWSTR NULL\n");
+ PERF_EXIT(lstrcpyW);
+ return NULL;
+ }
+
+ if (lpString2 == NULL)
+ {
+ ERROR("invalid lpString2 argument\n");
+ LOGEXIT("lstrcpyW returning LPWSTR NULL\n");
+ PERF_EXIT(lstrcpyW);
+ return NULL;
+ }
+
+ /* copy source string to destination string */
+ while(*lpString2)
+ {
+ *lpString1++ = *lpString2++;
+ }
+
+ /* add terminating null */
+ *lpString1 = '\0';
+
+ LOGEXIT("lstrcpyW returning LPWSTR %p (%S)\n", lpStart, lpStart);
+ PERF_EXIT(lstrcpyW);
+ return lpStart;
+}
+
+
+/*++
+Function:
+ lstrlenA
+
+
+The lstrlen function returns the length in bytes (ANSI version) or
+characters (Unicode version) of the specified string (not including
+the terminating null character).
+
+Parameters
+
+lpString [in] Pointer to a null-terminated string.
+
+Return Values
+
+The return value specifies the length of the string, in TCHARs. This
+refers to bytes for ANSI versions of the function or characters for
+Unicode versions.
+
+--*/
+int
+PALAPI
+lstrlenA( IN LPCSTR lpString)
+{
+ int nChar = 0;
+
+ PERF_ENTRY(lstrlenA);
+ ENTRY("lstrlenA (lpString=%p (%s))\n", lpString?lpString:"NULL", lpString?lpString:"NULL");
+ if (lpString)
+ {
+ while (*lpString++)
+ {
+ nChar++;
+ }
+ }
+ LOGEXIT("lstrlenA returning int %d\n", nChar);
+ PERF_EXIT(lstrlenA);
+ return nChar;
+}
+
+
+/*++
+Function:
+ lstrlenW
+
+The lstrlen function returns the length in bytes (ANSI version) or
+characters (Unicode version) of the specified string (not including
+the terminating null character).
+
+Parameters
+
+lpString [in] Pointer to a null-terminated string.
+
+Return Values
+
+The return value specifies the length of the string, in TCHARs. This
+refers to bytes for ANSI versions of the function or characters for
+Unicode versions.
+
+--*/
+int
+PALAPI
+lstrlenW(
+ IN LPCWSTR lpString)
+{
+ int nChar = 0;
+
+ PERF_ENTRY(lstrlenW);
+ ENTRY("lstrlenW (lpString=%p (%S))\n", lpString?lpString:W16_NULLSTRING, lpString?lpString:W16_NULLSTRING);
+ if (lpString != NULL)
+ {
+ while (*lpString++)
+ {
+ nChar++;
+ }
+ }
+ LOGEXIT("lstrlenW returning int %d\n", nChar);
+ PERF_EXIT(lstrlenW);
+ return nChar;
+}
+
+
+/*++
+Function:
+ lstrcpynW
+
+The lstrcpyn function copies a specified number of characters from a
+source string into a buffer.
+
+Parameters
+
+lpString1 [out] Pointer to a buffer into which the function copies characters.
+ The buffer must be large enough to contain the number of TCHARs
+ specified by iMaxLength, including room for a terminating null character.
+lpString2 [in] Pointer to a null-terminated string from which the function copies
+ characters.
+iMaxLength [in] Specifies the number of TCHARs to be copied from the string pointed
+ to by lpString2 into the buffer pointed to by lpString1, including a
+ terminating null character.
+
+Return Values
+
+If the function succeeds, the return value is a pointer to the buffer.
+If the function fails, the return value is NULL.
+
+--*/
+LPWSTR
+PALAPI
+lstrcpynW(
+ OUT LPWSTR lpString1,
+ IN LPCWSTR lpString2,
+ IN int iMaxLength)
+{
+ LPWSTR lpStart = lpString1;
+
+ PERF_ENTRY(lstrcpynW);
+ ENTRY("lstrcpynW (lpString1=%p, lpString2=%p (%S), iMaxLength=%d)\n",
+ lpString1?lpString1:W16_NULLSTRING, lpString2?lpString2:W16_NULLSTRING, lpString2?lpString2:W16_NULLSTRING, iMaxLength);
+
+ if (lpString1 == NULL)
+ {
+ ERROR("invalid lpString1 argument\n");
+ LOGEXIT("lstrcpynW returning LPWSTR NULL\n");
+ PERF_EXIT(lstrcpynW);
+ return NULL;
+ }
+
+ if (lpString2 == NULL)
+ {
+ ERROR("invalid lpString2 argument\n");
+ LOGEXIT("lstrcpynW returning LPWSTR NULL\n");
+ PERF_EXIT(lstrcpynW);
+ return NULL;
+ }
+
+ /* copy source string to destination string */
+ while(iMaxLength > 1 && *lpString2)
+ {
+ *lpString1++ = *lpString2++;
+ iMaxLength--;
+ }
+
+ /* add terminating null */
+ if (iMaxLength > 0)
+ {
+ *lpString1 = '\0';
+ }
+
+ LOGEXIT("lstrcpynW returning LPWSTR %p (%S)\n", lpStart, lpStart);
+ PERF_EXIT(lstrcpynW);
+ return lpStart;
+
+}
+
+
diff --git a/src/pal/src/cruntime/malloc.cpp b/src/pal/src/cruntime/malloc.cpp
new file mode 100644
index 0000000000..3083a2005f
--- /dev/null
+++ b/src/pal/src/cruntime/malloc.cpp
@@ -0,0 +1,116 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ malloc.cpp
+
+Abstract:
+
+ Implementation of suspension safe memory allocation functions.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/corunix.hpp"
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+#include "pal/dbgmsg.h"
+
+#include <string.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+using namespace CorUnix;
+
+void *
+__cdecl
+PAL_realloc(
+ void* pvMemblock,
+ size_t szSize
+ )
+{
+ return InternalRealloc(pvMemblock, szSize);
+}
+
+void *
+CorUnix::InternalRealloc(
+ void* pvMemblock,
+ size_t szSize
+ )
+{
+ void *pvMem;
+
+ PERF_ENTRY(InternalRealloc);
+ ENTRY("realloc (memblock:%p size=%d)\n", pvMemblock, szSize);
+
+ if (szSize == 0)
+ {
+ // If pvMemblock is NULL, there's no reason to call free.
+ if (pvMemblock != NULL)
+ {
+ free(pvMemblock);
+ }
+ pvMem = NULL;
+ }
+ else
+ {
+ pvMem = realloc(pvMemblock, szSize);
+ }
+
+ LOGEXIT("realloc returns void * %p\n", pvMem);
+ PERF_EXIT(InternalRealloc);
+ return pvMem;
+}
+
+void
+__cdecl
+PAL_free(
+ void *pvMem
+ )
+{
+ free(pvMem);
+}
+
+void *
+__cdecl
+PAL_malloc(
+ size_t szSize
+ )
+{
+ return InternalMalloc(szSize);
+}
+
+void *
+CorUnix::InternalMalloc(
+ size_t szSize
+ )
+{
+ void *pvMem;
+
+ if (szSize == 0)
+ {
+ // malloc may return null for a requested size of zero bytes. Force a nonzero size to get a valid pointer.
+ szSize = 1;
+ }
+
+ pvMem = (void*)malloc(szSize);
+ return pvMem;
+}
+
+char *
+__cdecl
+PAL__strdup(
+ const char *c_szStr
+ )
+{
+ return strdup(c_szStr);
+} \ No newline at end of file
diff --git a/src/pal/src/cruntime/math.cpp b/src/pal/src/cruntime/math.cpp
new file mode 100644
index 0000000000..7075fd60f9
--- /dev/null
+++ b/src/pal/src/cruntime/math.cpp
@@ -0,0 +1,424 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ math.cpp
+
+Abstract:
+
+ Implementation of math family functions.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+
+#include <math.h>
+
+#if HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif // HAVE_IEEEFP_H
+
+#include <errno.h>
+
+#define PAL_NAN_DBL sqrt(-1.0)
+#define PAL_POSINF_DBL -log(0.0)
+#define PAL_NEGINF_DBL log(0.0)
+
+#define IS_DBL_NEGZERO(x) (((*((INT64*)((void*)&x))) & I64(0xFFFFFFFFFFFFFFFF)) == I64(0x8000000000000000))
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+/*++
+Function:
+ _finite
+
+Determines whether given double-precision floating point value is finite.
+
+Return Value
+
+_finite returns a nonzero value (TRUE) if its argument x is not
+infinite, that is, if -INF < x < +INF. It returns 0 (FALSE) if the
+argument is infinite or a NaN.
+
+Parameter
+
+x Double-precision floating-point value
+
+--*/
+int __cdecl _finite(double x)
+{
+ int ret;
+ PERF_ENTRY(_finite);
+ ENTRY("_finite (x=%f)\n", x);
+
+#if defined(_IA64_) && defined (_HPUX_)
+ ret = !isnan(x) && (x != PAL_POSINF_DBL) && (x != PAL_NEGINF_DBL);
+#else
+ ret = isfinite(x);
+#endif
+
+ LOGEXIT("_finite returns int %d\n", ret);
+ PERF_EXIT(_finite);
+ return ret;
+}
+
+/*++
+Function:
+ _isnan
+
+See MSDN doc
+--*/
+int __cdecl _isnan(double x)
+{
+ int ret;
+ PERF_ENTRY(_isnan);
+ ENTRY("_isnan (x=%f)\n", x);
+
+ ret = isnan(x);
+
+ LOGEXIT("_isnan returns int %d\n", ret);
+ PERF_EXIT(_isnan);
+ return ret;
+}
+
+/*++
+Function:
+ _copysign
+
+See MSDN doc
+--*/
+double __cdecl _copysign(double x, double y)
+{
+ double ret;
+ PERF_ENTRY(_copysign);
+ ENTRY("_copysign (x=%f, y=%f)\n", x, y);
+
+ ret = copysign(x, y);
+
+ LOGEXIT("_copysign returns double %f\n", ret);
+ PERF_EXIT(_copysign);
+ return ret;
+}
+
+/*++
+Function:
+ acos
+
+See MSDN.
+--*/
+PALIMPORT double __cdecl PAL_acos(double x)
+{
+ double ret;
+ PERF_ENTRY(acos);
+ ENTRY("acos (x=%f)\n", x);
+
+#if !HAVE_COMPATIBLE_ACOS
+ errno = 0;
+#endif // HAVE_COMPATIBLE_ACOS
+
+ ret = acos(x);
+
+#if !HAVE_COMPATIBLE_ACOS
+ if (errno == EDOM)
+ {
+ ret = PAL_NAN_DBL; // NaN
+ }
+#endif // HAVE_COMPATIBLE_ACOS
+
+ LOGEXIT("acos returns double %f\n", ret);
+ PERF_EXIT(acos);
+ return ret;
+}
+
+/*++
+Function:
+ asin
+
+See MSDN.
+--*/
+PALIMPORT double __cdecl PAL_asin(double x)
+{
+ double ret;
+ PERF_ENTRY(asin);
+ ENTRY("asin (x=%f)\n", x);
+
+#if !HAVE_COMPATIBLE_ASIN
+ errno = 0;
+#endif // HAVE_COMPATIBLE_ASIN
+
+ ret = asin(x);
+
+#if !HAVE_COMPATIBLE_ASIN
+ if (errno == EDOM)
+ {
+ ret = PAL_NAN_DBL; // NaN
+ }
+#endif // HAVE_COMPATIBLE_ASIN
+
+ LOGEXIT("asin returns double %f\n", ret);
+ PERF_EXIT(asin);
+ return ret;
+}
+
+/*++
+Function:
+ atan2
+
+See MSDN.
+--*/
+PALIMPORT double __cdecl PAL_atan2(double y, double x)
+{
+ double ret;
+ PERF_ENTRY(atan2);
+ ENTRY("atan2 (y=%f, x=%f)\n", y, x);
+
+#if !HAVE_COMPATIBLE_ATAN2
+ errno = 0;
+#endif // !HAVE_COMPATIBLE_ATAN2
+
+ ret = atan2(y, x);
+
+#if !HAVE_COMPATIBLE_ATAN2
+ if ((errno == EDOM) && (x == 0.0) && (y == 0.0))
+ {
+ const double sign_x = copysign(1.0, x);
+ const double sign_y = copysign(1.0, y);
+
+ if (sign_x > 0)
+ {
+ ret = copysign(0.0, sign_y);
+ }
+ else
+ {
+ ret = copysign(atan2(0.0, -1.0), sign_y);
+ }
+ }
+#endif // !HAVE_COMPATIBLE_ATAN2
+
+ LOGEXIT("atan2 returns double %f\n", ret);
+ PERF_EXIT(atan2);
+ return ret;
+}
+
+/*++
+Function:
+ exp
+
+See MSDN.
+--*/
+PALIMPORT double __cdecl PAL_exp(double x)
+{
+ double ret;
+ PERF_ENTRY(exp);
+ ENTRY("exp (x=%f)\n", x);
+
+#if !HAVE_COMPATIBLE_EXP
+ if (x == 1.0)
+ {
+ ret = M_E;
+ }
+ else
+ {
+#endif // HAVE_COMPATIBLE_EXP
+
+ ret = exp(x);
+
+#if !HAVE_COMPATIBLE_EXP
+ }
+#endif // HAVE_COMPATIBLE_EXP
+
+ LOGEXIT("exp returns double %f\n", ret);
+ PERF_EXIT(exp);
+ return ret;
+}
+
+/*++
+Function:
+ labs
+
+See MSDN.
+--*/
+PALIMPORT LONG __cdecl PAL_labs(LONG l)
+{
+ long lRet;
+ PERF_ENTRY(labs);
+ ENTRY("labs (l=%ld)\n", l);
+
+ lRet = labs(l);
+
+ LOGEXIT("labs returns long %ld\n", lRet);
+ PERF_EXIT(labs);
+ return (LONG)lRet; // This explicit cast to LONG is used to silence any potential warnings due to implicitly casting the native long lRet to LONG when returning.
+}
+
+/*++
+Function:
+ log
+
+See MSDN.
+--*/
+PALIMPORT double __cdecl PAL_log(double x)
+{
+ double ret;
+ PERF_ENTRY(log);
+ ENTRY("log (x=%f)\n", x);
+
+#if !HAVE_COMPATIBLE_LOG
+ errno = 0;
+#endif // !HAVE_COMPATIBLE_LOG
+
+ ret = log(x);
+
+#if !HAVE_COMPATIBLE_LOG
+ if ((errno == EDOM) && (x < 0))
+ {
+ ret = PAL_NAN_DBL; // NaN
+ }
+#endif // !HAVE_COMPATIBLE_LOG
+
+ LOGEXIT("log returns double %f\n", ret);
+ PERF_EXIT(log);
+ return ret;
+}
+
+/*++
+Function:
+ log10
+
+See MSDN.
+--*/
+PALIMPORT double __cdecl PAL_log10(double x)
+{
+ double ret;
+ PERF_ENTRY(log10);
+ ENTRY("log10 (x=%f)\n", x);
+
+#if !HAVE_COMPATIBLE_LOG10
+ errno = 0;
+#endif // !HAVE_COMPATIBLE_LOG10
+
+ ret = log10(x);
+
+#if !HAVE_COMPATIBLE_LOG10
+ if ((errno == EDOM) && (x < 0))
+ {
+ ret = PAL_NAN_DBL; // NaN
+ }
+#endif // !HAVE_COMPATIBLE_LOG10
+
+ LOGEXIT("log10 returns double %f\n", ret);
+ PERF_EXIT(log10);
+ return ret;
+}
+
+/*++
+Function:
+ pow
+
+See MSDN.
+--*/
+PALIMPORT double __cdecl PAL_pow(double x, double y)
+{
+ double ret;
+ PERF_ENTRY(pow);
+ ENTRY("pow (x=%f, y=%f)\n", x, y);
+
+#if !HAVE_COMPATIBLE_POW
+ if ((y == PAL_POSINF_DBL) && !isnan(x)) // +Inf
+ {
+ if (x == 1.0)
+ {
+ ret = x;
+ }
+ else if (x == -1.0)
+ {
+ ret = PAL_NAN_DBL; // NaN
+ }
+ else if ((x > -1.0) && (x < 1.0))
+ {
+ ret = 0.0;
+ }
+ else
+ {
+ ret = PAL_POSINF_DBL; // +Inf
+ }
+ }
+ else if ((y == PAL_NEGINF_DBL) && !isnan(x)) // -Inf
+ {
+ if (x == 1.0)
+ {
+ ret = x;
+ }
+ else if (x == -1.0)
+ {
+ ret = PAL_NAN_DBL; // NaN
+ }
+ else if ((x > -1.0) && (x < 1.0))
+ {
+ ret = PAL_POSINF_DBL; // +Inf
+ }
+ else
+ {
+ ret = 0.0;
+ }
+ }
+ else if (IS_DBL_NEGZERO(x) && (y == -1.0))
+ {
+ ret = PAL_NEGINF_DBL; // -Inf
+ }
+ else if ((x == 0.0) && (y < 0.0))
+ {
+ ret = PAL_POSINF_DBL; // +Inf
+ }
+ else
+#endif // !HAVE_COMPATIBLE_POW
+
+ if ((y == 0.0) && isnan(x))
+ {
+ // Windows returns NaN for pow(NaN, 0), but POSIX specifies
+ // a return value of 1 for that case. We need to return
+ // the same result as Windows.
+ ret = PAL_NAN_DBL;
+ }
+ else
+ {
+ ret = pow(x, y);
+ }
+
+#if !HAVE_VALID_NEGATIVE_INF_POW
+ if ((ret == PAL_POSINF_DBL) && (x < 0) && isfinite(x) && (ceil(y / 2) != floor(y / 2)))
+ {
+ ret = PAL_NEGINF_DBL; // -Inf
+ }
+#endif // !HAVE_VALID_NEGATIVE_INF_POW
+
+#if !HAVE_VALID_POSITIVE_INF_POW
+ /*
+ * The even/odd test in the if (this one and the one above) used to be ((long long) y % 2 == 0)
+ * on SPARC (long long) y for large y (>2**63) is always 0x7fffffff7fffffff, which
+ * is an odd number, so the test ((long long) y % 2 == 0) will always fail for
+ * large y. Since large double numbers are always even (e.g., the representation of
+ * 1E20+1 is the same as that of 1E20, the last .+1. is too insignificant to be part
+ * of the representation), this test will always return the wrong result for large y.
+ *
+ * The (ceil(y/2) == floor(y/2)) test is slower, but more robust.
+ */
+ if ((ret == PAL_NEGINF_DBL) && (x < 0) && isfinite(x) && (ceil(y / 2) == floor(y / 2)))
+ {
+ ret = PAL_POSINF_DBL; // +Inf
+ }
+#endif // !HAVE_VALID_POSITIVE_INF_POW
+
+ LOGEXIT("pow returns double %f\n", ret);
+ PERF_EXIT(pow);
+ return ret;
+}
diff --git a/src/pal/src/cruntime/mbstring.cpp b/src/pal/src/cruntime/mbstring.cpp
new file mode 100644
index 0000000000..dd4bcbbdce
--- /dev/null
+++ b/src/pal/src/cruntime/mbstring.cpp
@@ -0,0 +1,257 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ mbstring.c
+
+Abstract:
+
+ Implementation of the multi-byte string functions in the C runtime library that
+ are Windows specific.
+
+Implementation Notes:
+
+ Assuming it is not possible to change multi-byte code page using
+ the PAL (_setmbcp does not seem to be required), these functions
+ should have a trivial implementation (treat as single-byte). If it
+ is possible, then support for multi-byte code pages will have to
+ be implemented before these functions can behave correctly for
+ multi-byte strings.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+
+/*++
+Function:
+ _mbslen
+
+Determines the number of characters (code points) in a multibyte
+character string.
+
+Parameters
+
+string Points to a multibyte character string.
+
+Return Values
+
+The mbslen subroutine returns the number of multibyte characters in a
+multibyte character string. It returns 0 if the string parameter
+points to a null character or if a character cannot be formed from the
+string pointed to by this parameter.
+
+--*/
+size_t
+__cdecl
+_mbslen(
+ const unsigned char *string)
+{
+ size_t ret = 0;
+ CPINFO cpinfo;
+ PERF_ENTRY(_mbslen);
+ ENTRY("_mbslen (string=%p (%s))\n", string, string);
+
+ if (string)
+ {
+ if (GetCPInfo(CP_ACP, &cpinfo) && cpinfo.MaxCharSize == 1)
+ {
+ ret = strlen((const char*)string);
+ }
+ else
+ {
+ while (*string)
+ {
+ if (IsDBCSLeadByteEx(CP_ACP, *string))
+ {
+ ++string;
+ }
+ ++string;
+ ++ret;
+ }
+ }
+ }
+
+ LOGEXIT("_mbslen returning size_t %u\n", ret);
+ PERF_EXIT(_mbslen);
+ return ret;
+}
+
+/*++
+Function:
+ _mbsinc
+
+Return Value
+
+Returns a pointer to the character that immediately follows string.
+
+Parameter
+
+string Character pointer
+
+Remarks
+
+The _mbsinc function returns a pointer to the first byte of the
+multibyte character that immediately follows string.
+
+--*/
+unsigned char *
+__cdecl
+_mbsinc(
+ const unsigned char *string)
+{
+ unsigned char *ret;
+
+ PERF_ENTRY(_mbsinc);
+ ENTRY("_mbsinc (string=%p)\n", string);
+
+ if (string == NULL)
+ {
+ ret = NULL;
+ }
+ else
+ {
+ ret = (unsigned char *) string;
+ if (IsDBCSLeadByteEx(CP_ACP, *ret))
+ {
+ ++ret;
+ }
+ ++ret;
+ }
+
+ LOGEXIT("_mbsinc returning unsigned char* %p (%s)\n", ret, ret);
+ PERF_EXIT(_mbsinc);
+ return ret;
+}
+
+
+/*++
+Function:
+ _mbsninc
+
+Return Value
+
+Returns a pointer to string after string has been incremented by count
+characters, or NULL if the supplied pointer is NULL. If count is
+greater than or equal to the number of characters in string, the
+result is undefined.
+
+Parameters
+
+string Source string
+count Number of characters to increment string pointer
+
+Remarks
+
+The _mbsninc function increments string by count multibyte
+characters. _mbsninc recognizes multibyte-character sequences
+according to the multibyte code page currently in use.
+
+--*/
+unsigned char *
+__cdecl
+_mbsninc(
+ const unsigned char *string, size_t count)
+{
+ unsigned char *ret;
+ CPINFO cpinfo;
+
+ PERF_ENTRY(_mbsninc);
+ ENTRY("_mbsninc (string=%p, count=%lu)\n", string, count);
+ if (string == NULL)
+ {
+ ret = NULL;
+ }
+ else
+ {
+ ret = (unsigned char *) string;
+ if (GetCPInfo(CP_ACP, &cpinfo) && cpinfo.MaxCharSize == 1)
+ {
+ ret += min(count, strlen((const char*)string));
+ }
+ else
+ {
+ while (count-- && (*ret != 0))
+ {
+ if (IsDBCSLeadByteEx(CP_ACP, *ret))
+ {
+ ++ret;
+ }
+ ++ret;
+ }
+ }
+ }
+ LOGEXIT("_mbsninc returning unsigned char* %p (%s)\n", ret, ret);
+ PERF_EXIT(_mbsninc);
+ return ret;
+}
+
+/*++
+Function:
+ _mbsdec
+
+Return Value
+
+_mbsdec returns a pointer to the character that immediately precedes
+current; _mbsdec returns NULL if the value of start is greater than or
+equal to that of current.
+
+Parameters
+
+start Pointer to first byte of any multibyte character in the source
+ string; start must precede current in the source string
+
+current Pointer to first byte of any multibyte character in the source
+ string; current must follow start in the source string
+
+--*/
+unsigned char *
+__cdecl
+_mbsdec(
+ const unsigned char *start,
+ const unsigned char *current)
+{
+ unsigned char *ret;
+ unsigned char *strPtr;
+ CPINFO cpinfo;
+
+ PERF_ENTRY(_mbsdec);
+ ENTRY("_mbsdec (start=%p, current=%p)\n", start, current);
+
+ if (current <= start)
+ {
+ ret = NULL;
+ }
+ else if (GetCPInfo(CP_ACP, &cpinfo) && cpinfo.MaxCharSize == 1)
+ {
+ ret = (unsigned char *) current - 1;
+ }
+ else
+ {
+ ret = strPtr = (unsigned char *) start;
+ while (strPtr < current)
+ {
+ ret = strPtr;
+ if (IsDBCSLeadByteEx(CP_ACP, *strPtr))
+ {
+ ++strPtr;
+ }
+ ++strPtr;
+ }
+ }
+ LOGEXIT("_mbsdec returning unsigned int %p (%s)\n", ret, ret);
+ PERF_EXIT(_mbsdec);
+ return ret;
+}
diff --git a/src/pal/src/cruntime/misc.cpp b/src/pal/src/cruntime/misc.cpp
new file mode 100644
index 0000000000..9e5acf65ff
--- /dev/null
+++ b/src/pal/src/cruntime/misc.cpp
@@ -0,0 +1,314 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ cruntime/misc.cpp
+
+Abstract:
+
+ Implementation of C runtime functions that don't fit anywhere else.
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/threadsusp.hpp"
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/misc.h"
+
+#include <errno.h>
+/* <stdarg.h> needs to be included after "palinternal.h" to avoid name
+ collision for va_start and va_end */
+#include <stdarg.h>
+#include <time.h>
+#include <limits.h>
+
+#if defined(_AMD64_) || defined(_x86_)
+#include <xmmintrin.h>
+#endif // defined(_AMD64_) || defined(_x86_)
+#if defined(_DEBUG)
+#include <assert.h>
+#endif //defined(_DEBUG)
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+using namespace CorUnix;
+
+/*++
+Function:
+ _gcvt_s
+
+See MSDN doc.
+--*/
+char *
+__cdecl
+_gcvt_s( char * buffer, int iSize, double value, int digits )
+{
+ PERF_ENTRY(_gcvt);
+ ENTRY( "_gcvt( value:%f digits=%d, buffer=%p )\n", value, digits, buffer );
+
+ if ( !buffer )
+ {
+ ERROR( "buffer was an invalid pointer.\n" );
+ }
+
+ switch ( digits )
+ {
+ case 7 :
+ /* Fall through */
+ case 8 :
+ /* Fall through */
+ case 15 :
+ /* Fall through */
+ case 17 :
+
+ sprintf_s( buffer, iSize, "%.*g", digits, value );
+ break;
+
+ default :
+ ASSERT( "Only the digits 7, 8, 15, and 17 are valid.\n" );
+ *buffer = '\0';
+ }
+
+ LOGEXIT( "_gcvt returns %p (%s)\n", buffer , buffer );
+ PERF_EXIT(_gcvt);
+ return buffer;
+}
+
+
+/*++
+Function :
+
+ __iscsym
+
+See MSDN for more details.
+--*/
+int
+__cdecl
+__iscsym( int c )
+{
+ PERF_ENTRY(__iscsym);
+ ENTRY( "__iscsym( c=%d )\n", c );
+
+ if ( isalnum( c ) || c == '_' )
+ {
+ LOGEXIT( "__iscsym returning 1\n" );
+ PERF_EXIT(__iscsym);
+ return 1;
+ }
+
+ LOGEXIT( "__iscsym returning 0\n" );
+ PERF_EXIT(__iscsym);
+ return 0;
+}
+
+
+/*++
+
+Function :
+
+ PAL_errno
+
+ Returns the address of the errno.
+
+--*/
+int * __cdecl PAL_errno( int caller )
+{
+ int *retval;
+ PERF_ENTRY(errno);
+ ENTRY( "PAL_errno( void )\n" );
+ retval = (INT*)(&errno);
+ LOGEXIT("PAL_errno returns %p\n",retval);
+ PERF_EXIT(errno);
+ return retval;
+}
+
+/*++
+Function:
+
+ mktime
+
+See MSDN for more details.
+--*/
+
+PAL_time_t
+__cdecl
+PAL_mktime(struct PAL_tm *tm)
+{
+ time_t result;
+ struct tm tmpTm;
+
+ PERF_ENTRY(mktime);
+ ENTRY( "mktime( tm=%p )\n",tm );
+
+ /*copy the value of Windows struct into BSD struct*/
+ tmpTm.tm_sec = tm->tm_sec;
+ tmpTm.tm_min = tm->tm_min;
+ tmpTm.tm_hour = tm->tm_hour;
+ tmpTm.tm_mday = tm->tm_mday;
+ tmpTm.tm_mon = tm->tm_mon;
+ tmpTm.tm_year = tm->tm_year;
+ tmpTm.tm_wday = tm->tm_wday;
+ tmpTm.tm_yday = tm->tm_yday;
+ tmpTm.tm_isdst = tm->tm_isdst;
+
+ result = mktime(&tmpTm);
+
+ LOGEXIT( "mktime returned %#lx\n",result );
+ PERF_EXIT(mktime);
+ return result;
+}
+
+/*++
+Function:
+
+ rand
+
+ The RAND_MAX value can vary by platform.
+
+See MSDN for more details.
+--*/
+int
+__cdecl
+PAL_rand(void)
+{
+ int ret;
+ PERF_ENTRY(rand);
+ ENTRY("rand(void)\n");
+
+ ret = (rand() % (PAL_RAND_MAX + 1));
+
+ LOGEXIT("rand() returning %d\n", ret);
+ PERF_EXIT(rand);
+ return ret;
+}
+
+
+/*++
+Function:
+
+ time
+
+See MSDN for more details.
+--*/
+PAL_time_t
+__cdecl
+PAL_time(PAL_time_t *tloc)
+{
+ time_t result;
+
+ PERF_ENTRY(time);
+ ENTRY( "time( tloc=%p )\n",tloc );
+
+ result = time(tloc);
+
+ LOGEXIT( "time returning %#lx\n",result );
+ PERF_EXIT(time);
+ return result;
+}
+
+
+PALIMPORT
+void __cdecl
+PAL_qsort(void *base, size_t nmemb, size_t size,
+ int (__cdecl *compar )(const void *, const void *))
+{
+ PERF_ENTRY(qsort);
+ ENTRY("qsort(base=%p, nmemb=%lu, size=%lu, compar=%p\n",
+ base,(unsigned long) nmemb,(unsigned long) size, compar);
+
+/* reset ENTRY nesting level back to zero, qsort will invoke app-defined
+ callbacks and we want their entry traces... */
+#if _ENABLE_DEBUG_MESSAGES_
+{
+ int old_level;
+ old_level = DBG_change_entrylevel(0);
+#endif /* _ENABLE_DEBUG_MESSAGES_ */
+
+ qsort(base,nmemb,size,compar);
+
+/* ...and set nesting level back to what it was */
+#if _ENABLE_DEBUG_MESSAGES_
+ DBG_change_entrylevel(old_level);
+}
+#endif /* _ENABLE_DEBUG_MESSAGES_ */
+
+ LOGEXIT("qsort returns\n");
+ PERF_EXIT(qsort);
+}
+
+PALIMPORT
+void * __cdecl
+PAL_bsearch(const void *key, const void *base, size_t nmemb, size_t size,
+ int (__cdecl *compar)(const void *, const void *))
+{
+ void *retval;
+
+ PERF_ENTRY(bsearch);
+ ENTRY("bsearch(key=%p, base=%p, nmemb=%lu, size=%lu, compar=%p\n",
+ key, base, (unsigned long) nmemb, (unsigned long) size, compar);
+
+/* reset ENTRY nesting level back to zero, bsearch will invoke app-defined
+ callbacks and we want their entry traces... */
+#if _ENABLE_DEBUG_MESSAGES_
+{
+ int old_level;
+ old_level = DBG_change_entrylevel(0);
+#endif /* _ENABLE_DEBUG_MESSAGES_ */
+
+ retval = bsearch(key,base,nmemb,size,compar);
+
+/* ...and set nesting level back to what it was */
+#if _ENABLE_DEBUG_MESSAGES_
+ DBG_change_entrylevel(old_level);
+}
+#endif /* _ENABLE_DEBUG_MESSAGES_ */
+
+ LOGEXIT("bsearch returns %p\n",retval);
+ PERF_EXIT(bsearch);
+ return retval;
+}
+
+#ifdef _AMD64_
+
+PALIMPORT
+unsigned int PAL__mm_getcsr(void)
+{
+ return _mm_getcsr();
+}
+
+PALIMPORT
+void PAL__mm_setcsr(unsigned int i)
+{
+ _mm_setcsr(i);
+}
+
+#endif // _AMD64_
+
+/*++
+Function:
+PAL_memcpy
+
+Overlapping buffer-safe version of memcpy.
+See MSDN doc for memcpy
+--*/
+EXTERN_C
+PALIMPORT
+void *PAL_memcpy (void *dest, const void *src, size_t count)
+{
+ UINT_PTR x = (UINT_PTR)dest, y = (UINT_PTR)src;
+ _ASSERTE((x + count <= y) || (y + count <= x));
+
+ void *ret;
+ #undef memcpy
+ ret = memcpy(dest, src, count);
+ return ret;
+}
diff --git a/src/pal/src/cruntime/misctls.cpp b/src/pal/src/cruntime/misctls.cpp
new file mode 100644
index 0000000000..e46582ec17
--- /dev/null
+++ b/src/pal/src/cruntime/misctls.cpp
@@ -0,0 +1,399 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ cruntime/misctls.ccpp
+
+Abstract:
+
+ Implementation of C runtime functions that don't fit anywhere else
+ and depend on per-thread data
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/palinternal.h"
+
+extern "C"
+{
+#include "pal/dbgmsg.h"
+#include "pal/misc.h"
+}
+
+#include <errno.h>
+/* <stdarg.h> needs to be included after "palinternal.h" to avoid name
+ collision for va_start and va_end */
+#include <stdarg.h>
+#include <time.h>
+#if HAVE_CRT_EXTERNS_H
+#include <crt_externs.h>
+#endif // HAVE_CRT_EXTERNS_H
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+/*++
+Function:
+
+ localtime
+
+See MSDN for more details.
+--*/
+
+struct PAL_tm *
+__cdecl
+PAL_localtime(const PAL_time_t *clock)
+{
+ CPalThread *pThread = NULL;
+ struct tm tmpResult;
+ struct PAL_tm *result = NULL;
+
+ PERF_ENTRY(localtime);
+ ENTRY( "localtime( clock=%p )\n",clock );
+
+ /* Get the per-thread buffer from the thread structure. */
+ pThread = InternalGetCurrentThread();
+
+ result = &pThread->crtInfo.localtimeBuffer;
+
+ localtime_r(reinterpret_cast<const time_t*>(clock), &tmpResult);
+
+ // Copy the result into the Windows struct.
+ result->tm_sec = tmpResult.tm_sec;
+ result->tm_min = tmpResult.tm_min;
+ result->tm_hour = tmpResult.tm_hour;
+ result->tm_mday = tmpResult.tm_mday;
+ result->tm_mon = tmpResult.tm_mon;
+ result->tm_year = tmpResult.tm_year;
+ result->tm_wday = tmpResult.tm_wday;
+ result->tm_yday = tmpResult.tm_yday;
+ result->tm_isdst = tmpResult.tm_isdst;
+
+ LOGEXIT( "localtime returned %p\n", result );
+ PERF_EXIT(localtime);
+
+ return result;
+}
+
+/*++
+Function:
+
+ ctime
+
+ There appears to be a difference between the FreeBSD and windows
+ implementations. FreeBSD gives Wed Dec 31 18:59:59 1969 for a
+ -1 param, and Windows returns NULL
+
+See MSDN for more details.
+--*/
+char *
+__cdecl
+PAL_ctime( const PAL_time_t *clock )
+{
+ CPalThread *pThread = NULL;
+ char * retval = NULL;
+
+ PERF_ENTRY(ctime);
+ ENTRY( "ctime( clock=%p )\n",clock );
+ if(*clock < 0)
+ {
+ /*If the input param is less than zero the value
+ *returned is less than the Unix epoch
+ *1st of January 1970*/
+ WARN("The input param is less than zero");
+ goto done;
+ }
+
+ /* Get the per-thread buffer from the thread structure. */
+ pThread = InternalGetCurrentThread();
+
+ retval = pThread->crtInfo.ctimeBuffer;
+
+ ctime_r(reinterpret_cast<const time_t*>(clock),retval);
+
+done:
+
+ LOGEXIT( "ctime() returning %p (%s)\n",retval,retval);
+ PERF_EXIT(ctime);
+
+ return retval;
+}
+
+/**
+Function:
+
+ _ecvt
+
+See MSDN for more information.
+
+NOTES:
+ There is a difference between PAL _ecvt and Win32 _ecvt.
+
+ If Window's _ecvt receives a double 0.000000000000000000005, and count 50
+ the result is "49999999999999998000000000000000000000000000000000"
+
+ Under BSD the same call will result in :
+ 49999999999999998021734900744965462766153934333829
+
+ The difference is due to the difference between BSD and Win32 sprintf.
+
+--*/
+char * __cdecl
+_ecvt( double value, int count, int * dec, int * sign )
+{
+ CONST CHAR * FORMAT_STRING = "%.348e";
+ CHAR TempBuffer[ ECVT_MAX_BUFFER_SIZE ];
+ CPalThread *pThread = NULL;
+ LPSTR lpReturnBuffer = NULL;
+ LPSTR lpStartOfReturnBuffer = NULL;
+ LPSTR lpTempBuffer = NULL;
+ LPSTR lpEndOfTempBuffer = NULL;
+ INT nTempBufferLength = 0;
+ CHAR ExponentBuffer[ 6 ];
+ INT nExponentValue = 0;
+ INT LoopIndex = 0;
+
+ PERF_ENTRY(_ecvt);
+ ENTRY( "_ecvt( value=%.30g, count=%d, dec=%p, sign=%p )\n",
+ value, count, dec, sign );
+
+ /* Get the per-thread buffer from the thread structure. */
+ pThread = InternalGetCurrentThread();
+
+ lpStartOfReturnBuffer = lpReturnBuffer = pThread->crtInfo.ECVTBuffer;
+
+ /* Sanity checks */
+ if ( !dec || !sign )
+ {
+ ERROR( "dec and sign have to be valid pointers.\n" );
+ *lpReturnBuffer = '\0';
+ goto done;
+ }
+ else
+ {
+ *dec = *sign = 0;
+ }
+
+ if ( value < 0.0 )
+ {
+ *sign = 1;
+ }
+
+ if ( count > ECVT_MAX_COUNT_SIZE )
+ {
+ count = ECVT_MAX_COUNT_SIZE;
+ }
+
+ /* Get the string to work with. */
+ sprintf_s( TempBuffer, sizeof(TempBuffer), FORMAT_STRING, value );
+
+ /* Check to see if value was a valid number. */
+ if ( strcmp( "NaN", TempBuffer ) == 0 || strcmp( "-NaN", TempBuffer ) == 0 )
+ {
+ TRACE( "value was not a number!\n" );
+ if (strcpy_s( lpStartOfReturnBuffer, ECVT_MAX_BUFFER_SIZE, "1#QNAN0" ) != SAFECRT_SUCCESS)
+ {
+ ERROR( "strcpy_s failed!\n" );
+ *lpStartOfReturnBuffer = '\0';
+ goto done;
+ }
+
+ *dec = 1;
+ goto done;
+ }
+
+ /* Check to see if it is infinite. */
+ if ( strcmp( "Inf", TempBuffer ) == 0 || strcmp( "-Inf", TempBuffer ) == 0 )
+ {
+ TRACE( "value is infinite!\n" );
+ if (strcpy_s( lpStartOfReturnBuffer, ECVT_MAX_BUFFER_SIZE, "1#INF00" ) != SAFECRT_SUCCESS)
+ {
+ ERROR( "strcpy_s failed!\n" );
+ *lpStartOfReturnBuffer = '\0';
+ goto done;
+ }
+
+ *dec = 1;
+ if ( *TempBuffer == '-' )
+ {
+ *sign = 1;
+ }
+ goto done;
+ }
+
+ nTempBufferLength = strlen( TempBuffer );
+ lpEndOfTempBuffer = &(TempBuffer[ nTempBufferLength ]);
+
+ /* Extract the exponent, and convert it to integer. */
+ while ( *lpEndOfTempBuffer != 'e' && nTempBufferLength > 0 )
+ {
+ nTempBufferLength--;
+ lpEndOfTempBuffer--;
+ }
+
+ ExponentBuffer[ 0 ] = '\0';
+ if (strncat_s( ExponentBuffer, sizeof(ExponentBuffer), lpEndOfTempBuffer + 1, 5 ) != SAFECRT_SUCCESS)
+ {
+ ERROR( "strncat_s failed!\n" );
+ *lpStartOfReturnBuffer = '\0';
+ goto done;
+ }
+
+ nExponentValue = atoi( ExponentBuffer );
+
+ /* End the string at the 'e' */
+ *lpEndOfTempBuffer = '\0';
+ nTempBufferLength--;
+
+ /* Determine decimal location. */
+ if ( nExponentValue == 0 )
+ {
+ *dec = 1;
+ }
+ else
+ {
+ *dec = nExponentValue + 1;
+ }
+
+ if ( value == 0.0 )
+ {
+ *dec = 0;
+ }
+ /* Copy the string from the temp buffer upto count characters,
+ removing the sign, and decimal as required. */
+ lpTempBuffer = TempBuffer;
+ *lpReturnBuffer = '0';
+ lpReturnBuffer++;
+
+ while ( LoopIndex < ECVT_MAX_COUNT_SIZE )
+ {
+ if ( isdigit(*lpTempBuffer) )
+ {
+ *lpReturnBuffer = *lpTempBuffer;
+ LoopIndex++;
+ lpReturnBuffer++;
+ }
+ lpTempBuffer++;
+
+ if ( LoopIndex == count + 1 )
+ {
+ break;
+ }
+ }
+
+ *lpReturnBuffer = '\0';
+
+ /* Round if needed. If count is less then 0
+ then windows does not round for some reason.*/
+ nTempBufferLength = strlen( lpStartOfReturnBuffer ) - 1;
+
+ /* Add one for the preceeding zero. */
+ lpReturnBuffer = ( lpStartOfReturnBuffer + 1 );
+
+ if ( nTempBufferLength >= count && count >= 0 )
+ {
+ /* Determine whether I need to round up. */
+ if ( *(lpReturnBuffer + count) >= '5' )
+ {
+ CHAR cNumberToBeRounded;
+ if ( count != 0 )
+ {
+ cNumberToBeRounded = *(lpReturnBuffer + count - 1);
+ }
+ else
+ {
+ cNumberToBeRounded = *lpReturnBuffer;
+ }
+
+ if ( cNumberToBeRounded < '9' )
+ {
+ if ( count > 0 )
+ {
+ /* Add one to the character. */
+ (*(lpReturnBuffer + count - 1))++;
+ }
+ else
+ {
+ if ( cNumberToBeRounded >= '5' )
+ {
+ (*dec)++;
+ }
+ }
+ }
+ else
+ {
+ LPSTR lpRounding = NULL;
+
+ if ( count > 0 )
+ {
+ lpRounding = lpReturnBuffer + count - 1;
+ }
+ else
+ {
+ lpRounding = lpReturnBuffer + count;
+ }
+
+ while ( cNumberToBeRounded == '9' )
+ {
+ cNumberToBeRounded = *lpRounding;
+
+ if ( cNumberToBeRounded == '9' )
+ {
+ *lpRounding = '0';
+ lpRounding--;
+ }
+ }
+
+ if ( lpRounding == lpStartOfReturnBuffer )
+ {
+ /* Overflow. number is a whole number now. */
+ *lpRounding = '1';
+ memset( ++lpRounding, '0', count);
+
+ /* The decimal has moved. */
+ (*dec)++;
+ }
+ else
+ {
+ *lpRounding = ++cNumberToBeRounded;
+ }
+ }
+ }
+ else
+ {
+ /* Get rid of the preceding 0 */
+ lpStartOfReturnBuffer++;
+ }
+ }
+
+ if ( *lpStartOfReturnBuffer == '0' )
+ {
+ lpStartOfReturnBuffer++;
+ }
+
+ if ( count >= 0 )
+ {
+ *(lpStartOfReturnBuffer + count) = '\0';
+ }
+ else
+ {
+ *lpStartOfReturnBuffer = '\0';
+ }
+
+done:
+
+ LOGEXIT( "_ecvt returning %p (%s)\n", lpStartOfReturnBuffer , lpStartOfReturnBuffer );
+ PERF_EXIT(_ecvt);
+
+ return lpStartOfReturnBuffer;
+}
+
diff --git a/src/pal/src/cruntime/path.cpp b/src/pal/src/cruntime/path.cpp
new file mode 100644
index 0000000000..e5b955ebd9
--- /dev/null
+++ b/src/pal/src/cruntime/path.cpp
@@ -0,0 +1,596 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ path.c
+
+Abstract:
+
+ Implementation of path functions part of Windows runtime library.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/printfcpp.hpp"
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <limits.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+
+/* ON_ERROR. A Helper macro for _?splitpath functions. */
+#define ON_ERROR if ( drive ) \
+ {\
+ drive[0] = 0;\
+ }\
+ if(dir)\
+ {\
+ dir[0] = 0;\
+ }\
+ if(fname)\
+ {\
+ fname[0] = 0;\
+ }\
+ if(ext)\
+ {\
+ ext[0] = 0;\
+ }\
+ goto done;\
+
+/*++
+Function:
+ _wsplitpath
+
+See MSDN doc.
+
+Notes :
+ This implementation ignores drive letters as they should not be
+ present. If the drive argument is non-NULL, it always returns an empty
+ string.
+ File names in which the only period is at the beginning (like .bashrc, but
+ not .bashrc.bak), the file is treated as having no extension
+ (fname is ".bashrc", ext is "")
+
+--*/
+void
+__cdecl
+_wsplitpath(
+ const wchar_16 *dospath,
+ wchar_16 *drive,
+ wchar_16 *dir,
+ wchar_16 *fname,
+ wchar_16 *ext)
+{
+ WCHAR path[_MAX_PATH+1];
+ LPCWSTR slash_ptr = NULL;
+ LPCWSTR period_ptr = NULL;
+ INT size = 0;
+
+ PERF_ENTRY(_wsplitpath);
+ ENTRY("_wsplitpath (path=%p (%S), drive=%p, dir=%p, fname=%p, ext=%p)\n",
+ dospath?dospath:W16_NULLSTRING,
+ dospath?dospath:W16_NULLSTRING, drive, dir, fname, ext);
+
+ /* Do performance intensive error checking only in debug builds.
+
+ NOTE: This function must fail predictably across all platforms.
+ Under Windows this function throw an access violation if NULL
+ was passed in as the value for path.
+
+ */
+#if _DEBUG
+ if ( !dospath )
+ {
+ ERROR( "path cannot be NULL!\n" );
+ }
+#endif
+
+ if( lstrlenW( dospath ) >= _MAX_PATH )
+ {
+ ERROR("Path length is > _MAX_PATH (%d)!\n", _MAX_PATH);
+ ON_ERROR;
+ }
+
+
+ PAL_wcscpy(path, dospath);
+ FILEDosToUnixPathW(path);
+
+ /* no drive letters in the PAL */
+ if( drive != NULL )
+ {
+ drive[0] = 0;
+ }
+
+ /* find last path separator char */
+ slash_ptr = PAL_wcsrchr(path, '/');
+
+ if( slash_ptr == NULL )
+ {
+ TRACE("No path separator in path\n");
+ slash_ptr = path - 1;
+ }
+ /* find extension separator, if any */
+ period_ptr = PAL_wcsrchr(path, '.');
+
+ /* make sure we only consider periods after the last path separator */
+ if( period_ptr < slash_ptr )
+ {
+ period_ptr = NULL;
+ }
+
+ /* if the only period in the file is a leading period (denoting a hidden
+ file), don't treat what follows as an extension */
+ if( period_ptr == slash_ptr+1 )
+ {
+ period_ptr = NULL;
+ }
+
+ if( period_ptr == NULL )
+ {
+ TRACE("No extension in path\n");
+ period_ptr = path + lstrlenW(path);
+ }
+
+ size = slash_ptr - path + 1;
+ if( dir != NULL )
+ {
+ INT i;
+
+ if( (size + 1 ) > _MAX_DIR )
+ {
+ ERROR("Directory component needs %d characters, _MAX_DIR is %d\n",
+ size+1, _MAX_DIR);
+ ON_ERROR;
+ }
+
+ memcpy(dir, path, size*sizeof(WCHAR));
+ dir[size] = 0;
+
+ /* only allow / separators in returned path */
+ i = 0;
+ while( dir[ i ] )
+ {
+ if( dir[ i ] == '\\' )
+ {
+ dir[i]='/';
+ }
+ i++;
+ }
+ }
+
+ size = period_ptr-slash_ptr-1;
+ if( fname != NULL )
+ {
+ if( (size+1) > _MAX_FNAME )
+ {
+ ERROR("Filename component needs %d characters, _MAX_FNAME is %d\n",
+ size+1, _MAX_FNAME);
+ ON_ERROR;
+ }
+ memcpy(fname, slash_ptr+1, size*sizeof(WCHAR));
+ fname[size] = 0;
+ }
+
+ size = 1 + lstrlenW( period_ptr );
+ if( ext != NULL )
+ {
+ if( size > _MAX_EXT )
+ {
+ ERROR("Extension component needs %d characters, _MAX_EXT is %d\n",
+ size, _MAX_EXT);
+ ON_ERROR;
+ }
+ memcpy(ext, period_ptr, size*sizeof(WCHAR));
+ ext[size-1] = 0;
+ }
+
+ TRACE("Path components are '%S' '%S' '%S'\n", dir, fname, ext);
+
+done:
+
+ LOGEXIT("_wsplitpath returns.\n");
+ PERF_EXIT(_wsplitpath);
+}
+
+
+/*++
+Function:
+ _splitpath
+
+See description above for _wsplitpath.
+
+--*/
+void
+__cdecl
+_splitpath(
+ const char *path,
+ char *drive,
+ char *dir,
+ char *fname,
+ char *ext)
+{
+ WCHAR w_path[_MAX_PATH];
+ WCHAR w_dir[_MAX_DIR];
+ WCHAR w_fname[_MAX_FNAME];
+ WCHAR w_ext[_MAX_EXT];
+
+ PERF_ENTRY(_splitpath);
+ ENTRY("_splitpath (path=%p (%s), drive=%p, dir=%p, fname=%p, ext=%p)\n",
+ path?path:"NULL",
+ path?path:"NULL", drive, dir, fname, ext);
+
+ /* Do performance intensive error checking only in debug builds.
+
+ NOTE: This function must fail predictably across all platforms.
+ Under Windows this function throw an access violation if NULL
+ was passed in as the value for path.
+
+ */
+#if _DEBUG
+ if ( !path )
+ {
+ ERROR( "path cannot be NULL!\n" );
+ }
+
+ if( strlen( path ) >= _MAX_PATH )
+ {
+ ERROR( "Path length is > _MAX_PATH (%d)!\n", _MAX_PATH);
+ }
+#endif
+
+ /* no drive letters in the PAL */
+ if(drive)
+ {
+ drive[0] = '\0';
+ }
+
+ if(0 == MultiByteToWideChar(CP_ACP, 0, path, -1, w_path, _MAX_PATH))
+ {
+ ASSERT("MultiByteToWideChar failed!\n");
+ ON_ERROR;
+ }
+
+ /* Call up to Unicode version; pass NULL for parameters the caller doesn't
+ care about */
+ _wsplitpath(w_path, NULL, dir?w_dir:NULL,
+ fname?w_fname:NULL, ext?w_ext:NULL);
+
+ /* Convert result back to MultiByte; report conversion errors but don't
+ stop because of them */
+
+ if(dir)
+ {
+ if(0 == WideCharToMultiByte(CP_ACP, 0, w_dir, -1, dir, _MAX_DIR,
+ NULL, NULL))
+ {
+ ASSERT("WideCharToMultiByte failed!\n");
+ ON_ERROR;
+ }
+ }
+ if(fname)
+ {
+ if(0 == WideCharToMultiByte(CP_ACP, 0, w_fname, -1, fname, _MAX_FNAME,
+ NULL, NULL))
+ {
+ ASSERT("WideCharToMultiByte failed!\n");
+ ON_ERROR;
+ }
+ }
+ if(ext)
+ {
+ if(0 == WideCharToMultiByte(CP_ACP, 0, w_ext, -1, ext, _MAX_EXT,
+ NULL, NULL))
+ {
+ ASSERT("WideCharToMultiByte failed!\n");
+ ON_ERROR;
+ }
+ }
+
+done:
+ LOGEXIT("_splitpath returns.\n");
+ PERF_EXIT(_splitpath);
+}
+
+
+
+/*++
+Function:
+ _makepath
+
+See MSDN doc.
+
+--*/
+void
+__cdecl
+_makepath(
+ char *path,
+ const char *drive,
+ const char *dir,
+ const char *fname,
+ const char *ext)
+{
+ UINT Length = 0;
+
+ PERF_ENTRY(_makepath);
+ ENTRY( "_makepath (path=%p, drive=%p (%s), dir=%p (%s), fname=%p (%s), ext=%p (%s))\n",
+ path, drive ? drive:"NULL", drive ? drive:"NULL", dir ? dir:"NULL", dir ? dir:"NULL", fname ? fname:"NULL", fname ? fname:"NULL",
+ ext ? ext:"NULL",
+ ext ? ext:"NULL");
+
+ path[ 0 ] = '\0';
+
+ /* According to the pal documentation, host operating systems that
+ don't support drive letters, the "drive" parameter must always be null. */
+ if ( drive != NULL && drive[0] != '\0' )
+ {
+ ASSERT( "The drive parameter must always be NULL on systems that don't"
+ "support drive letters. drive is being ignored!.\n" );
+ }
+
+ if ( dir != NULL && dir[ 0 ] != '\0' )
+ {
+ UINT DirLength = strlen( dir );
+ Length += DirLength ;
+
+ if ( Length < _MAX_PATH )
+ {
+ strncat( path, dir, DirLength );
+ if ( dir[ DirLength - 1 ] != '/' && dir[ DirLength - 1 ] != '\\' )
+ {
+ if ( Length + 1 < _MAX_PATH )
+ {
+ path[ Length ] = '/';
+ Length++;
+ path[ Length ] = '\0';
+ }
+ else
+ {
+ goto Max_Path_Error;
+ }
+ }
+ }
+ else
+ {
+ goto Max_Path_Error;
+ }
+ }
+
+ if ( fname != NULL && fname[ 0 ] != '\0' )
+ {
+ UINT fNameLength = strlen( fname );
+ Length += fNameLength;
+
+ if ( Length < _MAX_PATH )
+ {
+ strncat( path, fname, fNameLength );
+ }
+ else
+ {
+ goto Max_Path_Error;
+ }
+ }
+
+ if ( ext != NULL && ext[ 0 ] != '\0' )
+ {
+ UINT ExtLength = strlen( ext );
+ Length += ExtLength;
+
+ if ( ext[ 0 ] != '.' )
+ {
+ /* Add a '.' */
+ if ( Length + 1 < _MAX_PATH )
+ {
+ path[ Length - ExtLength ] = '.';
+ Length++;
+ path[ Length - ExtLength ] = '\0';
+ strncat( path, ext, ExtLength );
+ }
+ else
+ {
+ goto Max_Path_Error;
+ }
+ }
+ else
+ {
+ /* Already has a '.' */
+ if ( Length < _MAX_PATH )
+ {
+ strncat( path, ext, ExtLength );
+ }
+ else
+ {
+ goto Max_Path_Error;
+ }
+ }
+ }
+
+ FILEDosToUnixPathA( path );
+ LOGEXIT( "_makepath returning void.\n" );
+ PERF_EXIT(_makepath);
+ return;
+
+Max_Path_Error:
+
+ ERROR( "path cannot be greater then _MAX_PATH\n" );
+ path[ 0 ] = '\0';
+ LOGEXIT( "_makepath returning void \n" );
+ PERF_EXIT(_makepath);
+ return;
+}
+
+/*++
+Function:
+ _wmakepath
+
+See MSDN doc.
+
+--*/
+void
+__cdecl
+_wmakepath(
+ wchar_16 *path,
+ const wchar_16 *drive,
+ const wchar_16 *dir,
+ const wchar_16 *fname,
+ const wchar_16 *ext)
+{
+ CHAR Dir[ _MAX_DIR ]={0};
+ CHAR FileName[ _MAX_FNAME ]={0};
+ CHAR Ext[ _MAX_EXT ]={0};
+ CHAR Path[ _MAX_PATH ]={0};
+
+ PERF_ENTRY(_wmakepath);
+ ENTRY("_wmakepath (path=%p, drive=%p (%S), dir=%p (%S), fname=%p (%S), ext=%p (%S))\n",
+ path, drive ? drive:W16_NULLSTRING, drive ? drive:W16_NULLSTRING, dir ? dir:W16_NULLSTRING, dir ? dir:W16_NULLSTRING,
+ fname ? fname:W16_NULLSTRING,
+ fname ? fname:W16_NULLSTRING, ext ? ext:W16_NULLSTRING, ext ? ext:W16_NULLSTRING);
+
+ /* According to the pal documentation, host operating systems that
+ don't support drive letters, the "drive" parameter must always be null. */
+ if ( drive != NULL && drive[0] != '\0' )
+ {
+ ASSERT( "The drive parameter must always be NULL on systems that don't"
+ "support drive letters. drive is being ignored!.\n" );
+ }
+
+ if ((dir != NULL) && WideCharToMultiByte( CP_ACP, 0, dir, -1, Dir,
+ _MAX_DIR, NULL, NULL ) == 0 )
+ {
+ ASSERT( "An error occurred while converting dir to multibyte."
+ "Possible error: Length of dir is greater than _MAX_DIR.\n" );
+ goto error;
+ }
+
+ if ((fname != NULL) && WideCharToMultiByte( CP_ACP, 0, fname, -1, FileName,
+ _MAX_FNAME, NULL, NULL ) == 0 )
+ {
+ ASSERT( "An error occurred while converting fname to multibyte."
+ "Possible error: Length of fname is greater than _MAX_FNAME.\n" );
+ goto error;
+ }
+
+ if ((ext != NULL) && WideCharToMultiByte( CP_ACP, 0, ext, -1, Ext,
+ _MAX_EXT, NULL, NULL ) == 0 )
+ {
+ ASSERT( "An error occurred while converting ext to multibyte."
+ "Possible error: Length of ext is greater than _MAX_EXT.\n" );
+ goto error;
+ }
+
+ /* Call up to the ANSI _makepath. */
+ _makepath_s( Path, sizeof(Path), NULL, Dir, FileName, Ext );
+
+ if ( MultiByteToWideChar( CP_ACP, 0, Path, -1, path, _MAX_PATH ) == 0 )
+ {
+ ASSERT( "An error occurred while converting the back wide char."
+ "Possible error: The length of combined path is greater "
+ "than _MAX_PATH.\n" );
+ goto error;
+ }
+
+ LOGEXIT("_wmakepath returns void\n");
+ PERF_EXIT(_wmakepath);
+ return;
+
+error:
+ *path = '\0';
+ LOGEXIT("_wmakepath returns void\n");
+ PERF_EXIT(_wmakepath);
+}
+
+
+/*++
+Function:
+ _fullpath
+
+See MSDN doc.
+
+--*/
+char *
+__cdecl
+_fullpath(
+ char *absPath,
+ const char *relPath,
+ size_t maxLength)
+{
+ char realpath_buf[PATH_MAX+1];
+ char path_copy[PATH_MAX+1];
+ char *retval = NULL;
+ DWORD cPathCopy = sizeof(path_copy)/sizeof(path_copy[0]);
+ size_t min_length;
+ BOOL fBufAllocated = FALSE;
+
+ PERF_ENTRY(_fullpath);
+ ENTRY("_fullpath (absPath=%p, relPath=%p (%s), maxLength = %lu)\n",
+ absPath, relPath ? relPath:"NULL", relPath ? relPath:"NULL", maxLength);
+
+ if (strncpy_s(path_copy, sizeof(path_copy), relPath ? relPath : ".", cPathCopy) != SAFECRT_SUCCESS)
+ {
+ TRACE("_fullpath: strncpy_s failed!\n");
+ goto fullpathExit;
+ }
+
+ FILEDosToUnixPathA(path_copy);
+
+ if(NULL == realpath(path_copy, realpath_buf))
+ {
+ ERROR("realpath() failed; problem path is '%s'. errno is %d (%s)\n",
+ realpath_buf, errno, strerror(errno));
+ goto fullpathExit;
+ }
+
+ TRACE("real path is %s\n", realpath_buf);
+ min_length = strlen(realpath_buf)+1; // +1 for the NULL terminator
+
+ if(NULL == absPath)
+ {
+ absPath = static_cast<char *>(
+ PAL_malloc(_MAX_PATH * sizeof(char)));
+ if (!absPath)
+ {
+ ERROR("PAL_malloc failed with error %d\n", errno);
+ goto fullpathExit;
+ }
+ maxLength = _MAX_PATH;
+ fBufAllocated = TRUE;
+ }
+
+ if(min_length > maxLength)
+ {
+ ERROR("maxLength is %lu, we need at least %lu\n",
+ maxLength, min_length);
+ if (fBufAllocated)
+ {
+ PAL_free(absPath);
+ fBufAllocated = FALSE;
+ }
+ goto fullpathExit;
+ }
+
+ strcpy_s(absPath, maxLength, realpath_buf);
+ retval = absPath;
+
+fullpathExit:
+ LOGEXIT("_fullpath returns char * %p\n", retval);
+ PERF_EXIT(_fullpath);
+ return retval;
+}
+
+
+
diff --git a/src/pal/src/cruntime/printf.cpp b/src/pal/src/cruntime/printf.cpp
new file mode 100644
index 0000000000..2d9d6e4b94
--- /dev/null
+++ b/src/pal/src/cruntime/printf.cpp
@@ -0,0 +1,1758 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ printf.c
+
+Abstract:
+
+ Implementation of the printf family functions.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/cruntime.h"
+#include "pal/thread.hpp"
+#include "pal/threadsusp.hpp"
+#include "pal/printfcpp.hpp"
+
+/* <stdarg.h> needs to be included after "palinternal.h" to avoid name
+ collision for va_start and va_end */
+#include <stdarg.h>
+#include <errno.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+#if SSCANF_SUPPORT_ll
+const static char *scanf_longlongfmt = "ll";
+#else
+const static char *scanf_longlongfmt = "q";
+#endif
+
+#if SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
+static int SscanfFloatCheckExponent(LPCSTR buff, LPCSTR floatFmt,
+ void * voidPtr, int * pn);
+#endif // SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
+
+/*******************************************************************************
+Function:
+ Internal_AddPaddingA
+
+Parameters:
+ Out
+ - buffer to place padding and given string (In)
+ Count
+ - maximum chars to be copied so as not to overrun given buffer
+ In
+ - string to place into (Out) accompanied with padding
+ Padding
+ - number of padding chars to add
+ Flags
+ - padding style flags (PRINTF_FORMAT_FLAGS)
+*******************************************************************************/
+BOOL Internal_AddPaddingA(LPSTR *Out, INT Count, LPSTR In,
+ INT Padding, INT Flags)
+{
+ LPSTR OutOriginal = *Out;
+ INT PaddingOriginal = Padding;
+ INT LengthInStr;
+ LengthInStr = strlen(In);
+
+
+ if (Padding < 0)
+ {
+ /* this is used at the bottom to determine if the buffer ran out */
+ PaddingOriginal = 0;
+ }
+ if (Flags & PFF_MINUS) /* pad on right */
+ {
+ if (strncpy_s(*Out, Count, In, min(LengthInStr + 1, Count)) != SAFECRT_SUCCESS)
+ {
+ return FALSE;
+ }
+
+ *Out += min(LengthInStr, Count);
+ }
+ if (Padding > 0)
+ {
+ if (Flags & PFF_ZERO) /* '0', pad with zeros */
+ {
+ while (Padding-- && Count > *Out - OutOriginal)
+ {
+ *(*Out)++ = '0';
+ }
+ }
+ else /* pad left with spaces */
+ {
+ while (Padding-- && Count > *Out - OutOriginal)
+ {
+ *(*Out)++ = ' ';
+ }
+ }
+ }
+ if (!(Flags & PFF_MINUS)) /* put 'In' after padding */
+ {
+ if (strncpy_s(*Out, Count, In,
+ min(LengthInStr + 1, Count - (*Out - OutOriginal))) != SAFECRT_SUCCESS)
+ {
+ return FALSE;
+ }
+
+ *Out += min(LengthInStr, Count - (*Out - OutOriginal));
+ }
+
+ if (LengthInStr + PaddingOriginal > Count)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+/*******************************************************************************
+Function:
+ PAL_printf_arg_remover
+
+Parameters:
+ ap
+ - pointer to the va_list from which to remove arguments
+ Width
+ - the width of the current format operation
+ Precision
+ - the precision of the current format option
+ Type
+ - the type of the argument for the current format option
+ Prefix
+ - the prefix for the current format option
+*******************************************************************************/
+void PAL_printf_arg_remover(va_list *ap, INT Width, INT Precision, INT Type, INT Prefix)
+{
+ /* remove arg and precision if needed */
+ if (PRECISION_STAR == Precision ||
+ PRECISION_INVALID == Precision)
+ {
+ (void)va_arg(*ap, int);
+ }
+ if (WIDTH_STAR == Width ||
+ WIDTH_INVALID == Width)
+ {
+ (void)va_arg(*ap, int);
+ }
+ if (Type == PFF_TYPE_FLOAT)
+ {
+ (void)va_arg(*ap, double);
+ }
+ else if (Type == PFF_TYPE_INT && Prefix == PFF_PREFIX_LONGLONG)
+ {
+ (void)va_arg(*ap, INT64);
+ }
+ else if (Type == PFF_TYPE_INT || Type == PFF_TYPE_CHAR)
+ {
+ (void)va_arg(*ap, int);
+ }
+ else
+ {
+ (void)va_arg(*ap, void *);
+ }
+}
+
+/*++
+Function:
+ PAL_printf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_printf(
+ const char *format,
+ ...)
+{
+ LONG Length;
+ va_list ap;
+
+ PERF_ENTRY(printf);
+ ENTRY("PAL_printf (format=%p (%s))\n", format, format);
+
+ va_start(ap, format);
+ Length = PAL_vprintf(format, ap);
+ va_end(ap);
+
+ LOGEXIT("PAL_printf returns int %d\n", Length);
+ PERF_EXIT(printf);
+ return Length;
+}
+
+/*++
+Function:
+ PAL_fprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_fprintf(PAL_FILE *stream,const char *format,...)
+{
+ LONG Length = 0;
+ va_list ap;
+
+ PERF_ENTRY(fprintf);
+ ENTRY("PAL_fprintf(stream=%p,format=%p (%s))\n",stream, format, format);
+
+ va_start(ap, format);
+ Length = PAL_vfprintf( stream, format, ap);
+ va_end(ap);
+
+ LOGEXIT("PAL_fprintf returns int %d\n", Length);
+ PERF_EXIT(fprintf);
+ return Length;
+}
+
+/*++
+Function:
+ PAL_wprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_wprintf(
+ const wchar_16 *format,
+ ...)
+{
+ LONG Length;
+ va_list ap;
+
+ PERF_ENTRY(wprintf);
+ ENTRY("PAL_wprintf (format=%p (%S))\n", format, format);
+
+ va_start(ap, format);
+ Length = PAL_vfwprintf( PAL_get_stdout(PAL_get_caller), format, ap);
+ va_end(ap);
+
+ LOGEXIT("PAL_wprintf returns int %d\n", Length);
+ PERF_EXIT(wprintf);
+ return Length;
+}
+
+
+
+/*++
+Function:
+ PAL_vprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_vprintf(
+ const char *format,
+ va_list ap)
+{
+ LONG Length;
+
+ PERF_ENTRY(vprintf);
+ ENTRY("PAL_vprintf (format=%p (%s))\n", format, format);
+
+ Length = PAL_vfprintf( PAL_get_stdout(PAL_get_caller), format, ap);
+
+ LOGEXIT("PAL_vprintf returns int %d\n", Length);
+ PERF_EXIT(vprintf);
+ return Length;
+}
+
+
+/*++
+Function:
+ wsprintfA
+
+See MSDN doc.
+--*/
+int
+PALAPIV
+wsprintfA(
+ OUT LPSTR buffer,
+ IN LPCSTR format,
+ ...)
+{
+ LONG Length;
+ va_list ap;
+
+ PERF_ENTRY(wsprintfA);
+ ENTRY("wsprintfA (buffer=%p, format=%p (%s))\n", buffer, format, format);
+
+ va_start(ap, format);
+ Length = InternalVsnprintf(CorUnix::InternalGetCurrentThread(), buffer, 1024, format, ap);
+ va_end(ap);
+
+ LOGEXIT("wsprintfA returns int %d\n", Length);
+ PERF_EXIT(wsprintfA);
+ return Length;
+}
+
+/*++
+Function:
+ wsprintfW
+
+See MSDN doc.
+--*/
+int
+PALAPIV
+wsprintfW(
+ OUT LPWSTR buffer,
+ IN LPCWSTR format,
+ ...)
+{
+ LONG Length;
+ va_list ap;
+
+ PERF_ENTRY(wsprintfW);
+ ENTRY("wsprintfW (buffer=%p, format=%p (%S))\n", buffer, format, format);
+
+ va_start(ap, format);
+ Length = PAL__wvsnprintf(buffer, 1024, format, ap);
+ va_end(ap);
+
+ LOGEXIT("wsprintfW returns int %d\n", Length);
+ PERF_EXIT(wsprintfW);
+ return Length;
+}
+
+
+/*++
+Function:
+ _snprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+_snprintf(
+ char *buffer,
+ size_t count,
+ const char *format,
+ ...)
+{
+ LONG Length;
+ va_list ap;
+
+ PERF_ENTRY(_snprintf);
+ ENTRY("_snprintf (buffer=%p, count=%lu, format=%p (%s))\n",
+ buffer, (unsigned long) count, format, format);
+
+ va_start(ap, format);
+ Length = InternalVsnprintf(CorUnix::InternalGetCurrentThread(), buffer, count, format, ap);
+ va_end(ap);
+
+ LOGEXIT("_snprintf returns int %d\n", Length);
+ PERF_EXIT(_snprintf);
+ return Length;
+}
+
+
+/*++
+Function:
+ _snwprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+_snwprintf(
+ wchar_16 *buffer,
+ size_t count,
+ const wchar_16 *format,
+ ...)
+{
+ LONG Length;
+ va_list ap;
+
+ PERF_ENTRY(_snwprintf);
+ ENTRY("_snwprintf (buffer=%p, count=%lu, format=%p (%S))\n",
+ buffer, (unsigned long) count, format, format);
+
+ va_start(ap, format);
+ Length = PAL__wvsnprintf(buffer, count, format, ap);
+ va_end(ap);
+
+ LOGEXIT("_snwprintf returns int %d\n", Length);
+ PERF_EXIT(_snwprintf);
+ return Length;
+}
+
+/*++
+Function:
+ fwprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_fwprintf(
+ PAL_FILE *stream,
+ const wchar_16 *format,
+ ...)
+{
+ LONG Length;
+ va_list ap;
+
+ PERF_ENTRY(fwprintf);
+ ENTRY("PAL_fwprintf (stream=%p, format=%p (%S))\n", stream, format, format);
+
+ va_start(ap, format);
+ Length = PAL_vfwprintf( stream, format, ap);
+ va_end(ap);
+
+ LOGEXIT("PAL_fwprintf returns int %d\n", Length);
+ PERF_EXIT(fwprintf);
+ return Length;
+}
+
+/*******************************************************************************
+Function:
+ Internal_ScanfExtractFormatA
+
+Paramaters:
+ Fmt
+ - format string to parse
+ - first character must be a '%'
+ - paramater gets updated to point to the character after
+ the %<foo> format string
+ Out
+ - buffer will contain the %<foo> format string
+ Store
+ - boolean value representing whether to store the type to be parsed
+ - '*' flag
+ Width
+ - will contain the width specified by the format string
+ - -1 if none given
+ Prefix
+ - an enumeration of the type prefix
+ Type
+ - an enumeration of the value type to be parsed
+
+Notes:
+ - I'm also handling the undocumented %ws, %wc, %w...
+*******************************************************************************/
+
+#define CHECK_OUT_IN_ITS_RANGE(Out,BeginOut,EndOut) \
+ if ((Out)<(BeginOut) || (Out)>=(EndOut)) \
+ { \
+ SetLastError(ERROR_INSUFFICIENT_BUFFER); \
+ ERROR("Pointer Out wanted to access 0x%p. However the range of buffer is [0x%p,0x%p).",\
+ (Out), (BeginOut), (EndOut)); \
+ return false; \
+ }
+
+static BOOL Internal_ScanfExtractFormatA(LPCSTR *Fmt, LPSTR Out, int iOutSize, LPBOOL Store,
+ LPINT Width, LPINT Prefix, LPINT Type)
+{
+ BOOL Result = FALSE;
+ LPSTR TempStr;
+ LPSTR TempStrPtr;
+ LPSTR BaseOut = Out;
+ LPSTR EndOut = Out + iOutSize;
+
+ *Width = -1;
+ *Store = TRUE;
+ *Prefix = -1;
+ *Type = -1;
+
+ if (*Fmt && **Fmt == '%')
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = *(*Fmt)++;
+ }
+ else
+ {
+ return Result;
+ }
+
+ /* we'll never need a temp string longer than the original */
+ TempStrPtr = TempStr = (LPSTR) PAL_malloc(strlen(*Fmt)+1);
+ if (!TempStr)
+ {
+ ERROR("PAL_malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return Result;
+ }
+
+ /* parse '*' flag which means don't store */
+ if (**Fmt == '*')
+ {
+ *Store = FALSE;
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = *(*Fmt)++;
+ }
+
+ /* grab width specifier */
+ if (isdigit((unsigned char) **Fmt))
+ {
+ TempStrPtr = TempStr;
+ while (isdigit((unsigned char) **Fmt))
+ {
+ *TempStrPtr++ = **Fmt;
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = *(*Fmt)++;
+ }
+ *TempStrPtr = 0; /* end string */
+ *Width = atoi(TempStr);
+ if (*Width < 0)
+ {
+ ERROR("atoi returned a negative value indicative of an overflow.\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ return Result;
+ }
+ }
+
+#ifdef BIT64
+ if (**Fmt == 'p')
+ {
+ *Prefix = SCANF_PREFIX_LONGLONG;
+ }
+#endif
+ /* grab prefix of 'I64' for __int64 */
+ if ((*Fmt)[0] == 'I' && (*Fmt)[1] == '6' && (*Fmt)[2] == '4')
+ {
+ /* convert to 'q'/'ll' so Unix sscanf can handle it */
+ *Fmt += 3;
+ *Prefix = SCANF_PREFIX_LONGLONG;
+ }
+ /* grab a prefix of 'h' */
+ else if (**Fmt == 'h')
+ {
+ *Prefix = SCANF_PREFIX_SHORT;
+ ++(*Fmt);
+ }
+ /* grab prefix of 'l' or the undocumented 'w' (at least in MSDN) */
+ else if (**Fmt == 'l' || **Fmt == 'w')
+ {
+ ++(*Fmt);
+#ifdef BIT64
+ // Only want to change the prefix on 64 bit when inputing characters.
+ if (**Fmt == 'c' || **Fmt == 's')
+#endif
+ {
+ *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (**Fmt == 'l')
+ {
+ *Prefix = SCANF_PREFIX_LONGLONG;
+ ++(*Fmt);
+ }
+ }
+ else if (**Fmt == 'L')
+ {
+ /* a prefix of 'L' seems to be ignored */
+ ++(*Fmt);
+ }
+
+ /* grab type 'c' */
+ if (**Fmt == 'c' || **Fmt == 'C')
+ {
+ *Type = SCANF_TYPE_CHAR;
+ if (*Prefix != SCANF_PREFIX_SHORT && **Fmt == 'C')
+ {
+ *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (*Prefix == SCANF_PREFIX_LONG)
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = 'l';
+ }
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = 'c';
+ ++(*Fmt);
+ Result = TRUE;
+ }
+ /* grab type 's' */
+ else if (**Fmt == 's' || **Fmt == 'S')
+ {
+ *Type = SCANF_TYPE_STRING;
+ if (*Prefix != SCANF_PREFIX_SHORT && **Fmt == 'S')
+ {
+ *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (*Prefix == SCANF_PREFIX_LONG)
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = 'l';
+ }
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = 's';
+ ++(*Fmt);
+ Result = TRUE;
+ }
+ /* grab int types */
+ else if (**Fmt == 'd' || **Fmt == 'i' || **Fmt == 'o' ||
+ **Fmt == 'u' || **Fmt == 'x' || **Fmt == 'X' ||
+ **Fmt == 'p')
+ {
+ *Type = SCANF_TYPE_INT;
+ if (*Prefix == SCANF_PREFIX_SHORT)
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = 'h';
+ }
+ else if (*Prefix == SCANF_PREFIX_LONG)
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = 'l';
+ }
+ else if (*Prefix == SCANF_PREFIX_LONGLONG)
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+
+ if (strcpy_s(Out, iOutSize-(Out-BaseOut), scanf_longlongfmt) != SAFECRT_SUCCESS)
+ {
+ ERROR("strcpy_s failed\n");
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+
+ Out += strlen(scanf_longlongfmt);
+ }
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = *(*Fmt)++;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'e' || **Fmt == 'E' || **Fmt == 'f' ||
+ **Fmt == 'g' || **Fmt == 'G')
+ {
+ /* we can safely ignore the prefixes and only add the type*/
+ *Type = SCANF_TYPE_FLOAT;
+ /* this gets rid of %E/%G since they're they're the
+ same when scanning */
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = tolower( *(*Fmt)++ );
+ Result = TRUE;
+ }
+ else if (**Fmt == 'n')
+ {
+ if (*Prefix == SCANF_PREFIX_SHORT)
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = 'h';
+ }
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = *(*Fmt)++;
+ *Type = SCANF_TYPE_N;
+ Result = TRUE;
+ }
+ else if (**Fmt == '[')
+ {
+ /* There is a small compatibility problem in the handling of the []
+ option in FreeBSD vs. Windows. In Windows, you can have [z-a]
+ as well as [a-z]. In FreeBSD, [z-a] fails. So, we need to
+ reverse the instances of z-a to a-z (and [m-e] to [e-m], etc). */
+
+ /* step 1 : copy the leading [ */
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = '[';
+ (*Fmt)++;
+
+ /* step 2 : copy a leading ^, if present */
+ if( '^' == **Fmt )
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = '^';
+ (*Fmt)++;
+ }
+
+ /* step 3 : copy a leading ], if present; a ] immediately after the
+ leading [ (or [^) does *not* end the sequence, it is part of the
+ characters to match */
+ if( ']' == **Fmt )
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = ']';
+ (*Fmt)++;
+ }
+
+ /* step 4 : if the next character is already a '-', it's not part of an
+ interval specifier, so just copy it */
+ if('-' == **Fmt )
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = '-';
+ (*Fmt)++;
+ }
+
+ /* ok then, process the rest of it */
+ while( '\0' != **Fmt )
+ {
+ if(']' == **Fmt)
+ {
+ /* ']' marks end of the format specifier; we're done */
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = ']';
+ (*Fmt)++;
+ break;
+ }
+ if('-' == **Fmt)
+ {
+ if( ']' == (*Fmt)[1] )
+ {
+ /* got a '-', next character is the terminating ']';
+ copy '-' literally */
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = '-';
+ (*Fmt)++;
+ }
+ else
+ {
+ /* got a '-' indicating an interval specifier */
+ unsigned char prev, next;
+
+ /* get the interval boundaries */
+ prev = (*Fmt)[-1];
+ next = (*Fmt)[1];
+
+ /* if boundaries were inverted, replace the already-copied
+ low boundary by the 'real' low boundary */
+ if( prev > next )
+ {
+ CHECK_OUT_IN_ITS_RANGE(Out-1,BaseOut,EndOut)
+ Out[-1] = next;
+
+ /* ...and save the 'real' upper boundary, which will be
+ copied to 'Out' below */
+ next = prev;
+ }
+
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = '-';
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = next;
+
+ /* skip over the '-' and the next character, which we
+ already copied */
+ (*Fmt)+=2;
+ }
+ }
+ else
+ {
+ /* plain character; just copy it */
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = **Fmt;
+ (*Fmt)++;
+ }
+ }
+
+ *Type = SCANF_TYPE_BRACKETS;
+ Result = TRUE;
+ }
+ else if (**Fmt == ' ')
+ {
+ *Type = SCANF_TYPE_SPACE;
+ }
+
+ /* add %n so we know how far to increment the pointer */
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = '%';
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out++ = 'n';
+
+ CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut)
+ *Out = 0; /* end the string */
+ PAL_free(TempStr);
+ return Result;
+}
+
+/*******************************************************************************
+Function:
+ Internal_ScanfExtractFormatW
+
+ -- see Internal_ScanfExtractFormatA above
+*******************************************************************************/
+static BOOL Internal_ScanfExtractFormatW(LPCWSTR *Fmt, LPSTR Out, int iOutSize, LPBOOL Store,
+ LPINT Width, LPINT Prefix, LPINT Type)
+{
+ BOOL Result = FALSE;
+ LPSTR TempStr;
+ LPSTR TempStrPtr;
+
+ *Width = -1;
+ *Store = TRUE;
+ *Prefix = -1;
+ *Type = -1;
+
+ if (*Fmt && **Fmt == '%')
+ {
+ *Out++ = *(*Fmt)++;
+ }
+ else
+ {
+ return Result;
+ }
+
+ /* we'll never need a temp string longer than the original */
+ TempStrPtr = TempStr = (LPSTR) PAL_malloc(PAL_wcslen(*Fmt)+1);
+ if (!TempStr)
+ {
+ ERROR("PAL_malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return Result;
+ }
+
+ /* parse '*' flag which means don't store */
+ if (**Fmt == '*')
+ {
+ *Store = FALSE;
+ *Out++ = *(*Fmt)++;
+ }
+
+ /* grab width specifier */
+ if (isdigit(**Fmt))
+ {
+ TempStrPtr = TempStr;
+ while (isdigit(**Fmt))
+ {
+ *TempStrPtr++ = **Fmt;
+ *Out++ = *(*Fmt)++;
+ }
+ *TempStrPtr = 0; /* end string */
+ *Width = atoi(TempStr);
+ if (*Width < 0)
+ {
+ ERROR("atoi returned a negative value indicative of an overflow.\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ return Result;
+ }
+ }
+
+#ifdef BIT64
+ if (**Fmt == 'p')
+ {
+ *Prefix = SCANF_PREFIX_LONGLONG;
+ }
+#endif
+ /* grab prefix of 'I64' for __int64 */
+ if ((*Fmt)[0] == 'I' && (*Fmt)[1] == '6' && (*Fmt)[2] == '4')
+ {
+ /* convert to 'q'/'ll' so that Unix sscanf can handle it */
+ *Fmt += 3;
+ *Prefix = SCANF_PREFIX_LONGLONG;
+ }
+ /* grab a prefix of 'h' */
+ else if (**Fmt == 'h')
+ {
+ *Prefix = SCANF_PREFIX_SHORT;
+ ++(*Fmt);
+ }
+ /* grab prefix of 'l' or the undocumented 'w' (at least in MSDN) */
+ else if (**Fmt == 'l' || **Fmt == 'w')
+ {
+ ++(*Fmt);
+#ifdef BIT64
+ // Only want to change the prefix on 64 bit when inputing characters.
+ if (**Fmt == 'C' || **Fmt == 'S')
+#endif
+ {
+ *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (**Fmt == 'l')
+ {
+ *Prefix = SCANF_PREFIX_LONGLONG;
+ ++(*Fmt);
+ }
+ }
+ else if (**Fmt == 'L')
+ {
+ /* a prefix of 'L' seems to be ignored */
+ ++(*Fmt);
+ }
+
+ /* grab type 'c' */
+ if (**Fmt == 'c' || **Fmt == 'C')
+ {
+ *Type = SCANF_TYPE_CHAR;
+ if (*Prefix != SCANF_PREFIX_SHORT && **Fmt == 'c')
+ {
+ *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (*Prefix == SCANF_PREFIX_LONG)
+ {
+ *Out++ = 'l';
+ }
+ *Out++ = 'c';
+ ++(*Fmt);
+ Result = TRUE;
+ }
+ /* grab type 's' */
+ else if (**Fmt == 's' || **Fmt == 'S')
+ {
+ *Type = SCANF_TYPE_STRING;
+ if (*Prefix != SCANF_PREFIX_SHORT && **Fmt == 's')
+ {
+ *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (*Prefix == SCANF_PREFIX_LONG)
+ {
+ *Out++ = 'l';
+ }
+ *Out++ = 's';
+ ++(*Fmt);
+ Result = TRUE;
+ }
+ /* grab int types */
+ else if (**Fmt == 'd' || **Fmt == 'i' || **Fmt == 'o' ||
+ **Fmt == 'u' || **Fmt == 'x' || **Fmt == 'X' ||
+ **Fmt == 'p')
+ {
+ *Type = SCANF_TYPE_INT;
+ if (*Prefix == SCANF_PREFIX_SHORT)
+ {
+ *Out++ = 'h';
+ }
+ else if (*Prefix == SCANF_PREFIX_LONG)
+ {
+ *Out++ = 'l';
+ }
+ else if (*Prefix == SCANF_PREFIX_LONGLONG)
+ {
+ if (strcpy_s(Out, iOutSize, scanf_longlongfmt) != SAFECRT_SUCCESS)
+ {
+ ERROR("strcpy_s failed\n");
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+
+ Out += strlen(scanf_longlongfmt);
+ }
+ *Out++ = *(*Fmt)++;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'e' || **Fmt == 'E' || **Fmt == 'f' ||
+ **Fmt == 'g' || **Fmt == 'G')
+ {
+ /* we can safely ignore the prefixes and only add the type*/
+ *Type = SCANF_TYPE_FLOAT;
+ /* this gets rid of %E/%G since they're they're the
+ same when scanning */
+ *Out++ = tolower( *(*Fmt)++ );
+ Result = TRUE;
+ }
+ else if (**Fmt == 'n')
+ {
+ if (*Prefix == SCANF_PREFIX_SHORT)
+ {
+ *Out++ = 'h';
+ }
+ *Out++ = *(*Fmt)++;
+ *Type = SCANF_TYPE_N;
+ Result = TRUE;
+ }
+ else if (**Fmt == '[')
+ {
+ /* There is a small compatibility problem in the handling of the []
+ option in FreeBSD vs. Windows. In Windows, you can have [z-a]
+ as well as [a-z]. In FreeBSD, [z-a] fails. So, we need to
+ reverse the instances of z-a to a-z (and [m-e] to [e-m], etc). */
+
+ /* step 1 : copy the leading [ */
+ *Out++ = '[';
+ (*Fmt)++;
+
+ /* step 2 : copy a leading ^, if present */
+ if( '^' == **Fmt )
+ {
+ *Out++ = '^';
+ (*Fmt)++;
+ }
+
+ /* step 3 : copy a leading ], if present; a ] immediately after the
+ leading [ (or [^) does *not* end the sequence, it is part of the
+ characters to match */
+ if( ']' == **Fmt )
+ {
+ *Out++ = ']';
+ (*Fmt)++;
+ }
+
+ /* step 4 : if the next character is already a '-', it's not part of an
+ interval specifier, so just copy it */
+ if('-' == **Fmt )
+ {
+ *Out++ = '-';
+ (*Fmt)++;
+ }
+
+ /* ok then, process the rest of it */
+ while( '\0' != **Fmt )
+ {
+ if(']' == **Fmt)
+ {
+ /* ']' marks end of the format specifier; we're done */
+ *Out++ = ']';
+ (*Fmt)++;
+ break;
+ }
+ if('-' == **Fmt)
+ {
+ if( ']' == (*Fmt)[1] )
+ {
+ /* got a '-', next character is the terminating ']';
+ copy '-' literally */
+ *Out++ = '-';
+ (*Fmt)++;
+ }
+ else
+ {
+ /* got a '-' indicating an interval specifier */
+ unsigned char prev, next;
+
+ /* get the interval boundaries */
+ prev = (*Fmt)[-1];
+ next = (*Fmt)[1];
+
+ /* if boundaries were inverted, replace the already-copied
+ low boundary by the 'real' low boundary */
+ if( prev > next )
+ {
+ Out[-1] = next;
+
+ /* ...and save the 'real' upper boundary, which will be
+ copied to 'Out' below */
+ next = prev;
+ }
+
+ *Out++ = '-';
+ *Out++ = next;
+
+ /* skip over the '-' and the next character, which we
+ already copied */
+ (*Fmt)+=2;
+ }
+ }
+ else
+ {
+ /* plain character; just copy it */
+ *Out++ = **Fmt;
+ (*Fmt)++;
+ }
+ }
+
+ *Type = SCANF_TYPE_BRACKETS;
+ Result = TRUE;
+ }
+ else if (**Fmt == ' ')
+ {
+ *Type = SCANF_TYPE_SPACE;
+ }
+
+ /* add %n so we know how far to increment the pointer */
+ *Out++ = '%';
+ *Out++ = 'n';
+
+ *Out = 0; /* end the string */
+ PAL_free(TempStr);
+ return Result;
+}
+
+/*******************************************************************************
+Function:
+ PAL_vsscanf
+
+Parameters:
+ Buffer
+ - buffer to parse values from
+ Format
+ - format string
+ ap
+ - stdarg parameter list
+*******************************************************************************/
+int PAL_vsscanf(LPCSTR Buffer, LPCSTR Format, va_list ap)
+{
+ INT Length = 0;
+ LPCSTR Buff = Buffer;
+ LPCSTR Fmt = Format;
+ CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */
+ BOOL Store;
+ INT Width;
+ INT Prefix;
+ INT Type = -1;
+
+ while (*Fmt)
+ {
+ if (!*Buff && Length == 0)
+ {
+ Length = EOF;
+ break;
+ }
+ /* remove any number of blanks */
+ else if (isspace((unsigned char) *Fmt))
+ {
+ while (isspace((unsigned char) *Buff))
+ {
+ ++Buff;
+ }
+ ++Fmt;
+ }
+ else if (*Fmt == '%' &&
+ Internal_ScanfExtractFormatA(&Fmt, TempBuff, sizeof(TempBuff), &Store,
+ &Width, &Prefix, &Type))
+ {
+ if (Prefix == SCANF_PREFIX_LONG &&
+ (Type == SCANF_TYPE_STRING || Type == SCANF_TYPE_CHAR))
+ {
+ int len = 0;
+ int res;
+ WCHAR *charPtr = 0;
+
+ /* a single character */
+ if (Type == SCANF_TYPE_CHAR && Width == -1)
+ {
+ len = Width = 1;
+ }
+
+ /* calculate length of string to copy */
+ while (Buff[len] && !isspace((unsigned char) Buff[len]))
+ {
+ if (Width != -1 && len >= Width)
+ {
+ break;
+ }
+ ++len;
+ }
+
+ if (Store)
+ {
+ charPtr = va_arg(ap, WCHAR *);
+
+ res = MultiByteToWideChar(CP_ACP, 0, Buff, len,
+ charPtr, len);
+ if (!res)
+ {
+ ASSERT("MultiByteToWideChar failed. Error is %d\n",
+ GetLastError());
+ return -1;
+ }
+ if (Type == SCANF_TYPE_STRING)
+ {
+ /* end string */
+ charPtr[res] = 0;
+ }
+ ++Length;
+ }
+ Buff += len;
+ }
+ /* this places the number of bytes stored into the next arg */
+ else if (Type == SCANF_TYPE_N)
+ {
+ if (Prefix == SCANF_PREFIX_SHORT)
+ {
+ *(va_arg(ap, short *)) = Buff - Buffer;
+ }
+ else
+ {
+ *(va_arg(ap, LPLONG)) = Buff - Buffer;
+ }
+ }
+ /* types that sscanf can handle */
+ else
+ {
+ int ret;
+ int n;
+ LPVOID voidPtr = NULL;
+
+ if (Store)
+ {
+ // sscanf_s requires that if we are trying to read "%s" or "%c" or “%[“, then
+ // the size of the buffer must follow the buffer we are trying to read into.
+ voidPtr = va_arg(ap, LPVOID);
+ unsigned typeLen = 0;
+ if ((Type == SCANF_TYPE_STRING) || (Type == SCANF_TYPE_BRACKETS))
+ {
+ // Since this is not a Safe CRT API we don’t really know the size of the destination
+ // buffer provided by the caller. So we have to assume that the caller has allocated
+ // enough space to hold either the width specified in the format or the entire input
+ // string plus ‘\0’.
+ typeLen = ((Width > 0) ? Width : strlen(Buffer)) + 1;
+ }
+ else if (Type == SCANF_TYPE_CHAR)
+ {
+ // Check whether the format string contains number of characters
+ // that should be read from the input string.
+ // Note: ‘\0’ does not get appended in the “%c” case.
+ typeLen = (Width > 0) ? Width : 1;
+ }
+
+ if (typeLen > 0)
+ {
+ ret = sscanf_s(Buff, TempBuff, voidPtr, typeLen, &n);
+ }
+ else
+ {
+ ret = sscanf_s(Buff, TempBuff, voidPtr, &n);
+ }
+ }
+ else
+ {
+ ret = sscanf_s(Buff, TempBuff, &n);
+ }
+
+#if SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
+ if ((ret == 0) && (Type == SCANF_TYPE_FLOAT))
+ {
+ ret = SscanfFloatCheckExponent(Buff, TempBuff, voidPtr, &n);
+ }
+#endif // SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
+
+ if (ret > 0)
+ {
+ Length += ret;
+ }
+ else
+ {
+ /* no match, break scan */
+ break;
+ }
+ Buff += n;
+ }
+ }
+ else
+ {
+ /* grab, but not store */
+ if (*Fmt == *Buff && Type != SCANF_TYPE_SPACE)
+ {
+ ++Fmt;
+ ++Buff;
+ }
+ /* doesn't match, break scan */
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return Length;
+}
+
+/*******************************************************************************
+Function:
+ PAL_wvsscanf
+
+ -- see PAL_vsscanf above
+*******************************************************************************/
+int PAL_wvsscanf(LPCWSTR Buffer, LPCWSTR Format, va_list ap)
+{
+ INT Length = 0;
+ LPCWSTR Buff = Buffer;
+ LPCWSTR Fmt = Format;
+ CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */
+ BOOL Store;
+ INT Width;
+ INT Prefix;
+ INT Type = -1;
+
+ while (*Fmt)
+ {
+ if (!*Buff && Length == 0)
+ {
+ Length = EOF;
+ break;
+ }
+ /* remove any number of blanks */
+ else if (isspace(*Fmt))
+ {
+ while (isspace(*Buff))
+ {
+ ++Buff;
+ }
+ ++Fmt;
+ }
+ else if (*Fmt == '%' &&
+ Internal_ScanfExtractFormatW(&Fmt, TempBuff, sizeof(TempBuff), &Store,
+ &Width, &Prefix, &Type))
+ {
+ if (Prefix == SCANF_PREFIX_LONG &&
+ (Type == SCANF_TYPE_STRING || Type == SCANF_TYPE_CHAR))
+ {
+ int len = 0;
+ WCHAR *charPtr = 0;
+
+ /* a single character */
+ if (Type == SCANF_TYPE_CHAR && Width == -1)
+ {
+ len = Width = 1;
+ }
+
+ /* calculate length of string to copy */
+ while (Buff[len] && !isspace(Buff[len]))
+ {
+ if (Width != -1 && len >= Width)
+ {
+ break;
+ }
+ ++len;
+ }
+
+ if (Store)
+ {
+ int i;
+ charPtr = va_arg(ap, WCHAR *);
+
+ for (i = 0; i < len; i++)
+ {
+ charPtr[i] = Buff[i];
+ }
+ if (Type == SCANF_TYPE_STRING)
+ {
+ /* end string */
+ charPtr[len] = 0;
+ }
+ ++Length;
+ }
+ Buff += len;
+ }
+ /* this places the number of bytes stored into the next arg */
+ else if (Type == SCANF_TYPE_N)
+ {
+ if (Prefix == SCANF_PREFIX_SHORT)
+ {
+ *(va_arg(ap, short *)) = Buff - Buffer;
+ }
+ else
+ {
+ *(va_arg(ap, LPLONG)) = Buff - Buffer;
+ }
+ }
+ /* types that sscanf can handle */
+ else
+ {
+ int ret;
+ int n;
+ int size;
+ LPSTR newBuff = 0;
+ LPVOID voidPtr = NULL;
+
+ size = WideCharToMultiByte(CP_ACP, 0, Buff, -1, 0, 0, 0, 0);
+ if (!size)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ return -1;
+ }
+ newBuff = (LPSTR) PAL_malloc(size);
+ if (!newBuff)
+ {
+ ERROR("PAL_malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return -1;
+ }
+ size = WideCharToMultiByte(CP_ACP, 0, Buff, size,
+ newBuff, size, 0, 0);
+ if (!size)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ PAL_free(newBuff);
+ return -1;
+ }
+
+ if (Store)
+ {
+ if (Type == SCANF_TYPE_BRACKETS)
+ {
+ WCHAR *strPtr;
+ int i;
+
+ /* add a '*' to %[] --> %*[] */
+ i = strlen(TempBuff) + 1;
+ while (i)
+ {
+ /* shift everything right one */
+ TempBuff[i] = TempBuff[i - 1];
+ --i;
+ }
+ TempBuff[0] = '%';
+ TempBuff[1] = '*';
+
+ /* %n doesn't count as a conversion. Since we're
+ suppressing conversion of the %[], sscanf will
+ always return 0, so we can't use the return value
+ to determine success. Set n to 0 before the call; if
+ it's still 0 afterwards, we know the call failed */
+ n = 0;
+ sscanf_s(newBuff, TempBuff, &n);
+ if(0 == n)
+ {
+ /* sscanf failed, nothing matched. set ret to 0,
+ so we know we have to break */
+ ret = 0;
+ }
+ else
+ {
+ strPtr = va_arg(ap, WCHAR *);
+ for (i = 0; i < n; i++)
+ {
+ strPtr[i] = Buff[i];
+ }
+ strPtr[n] = 0; /* end string */
+ ret = 1;
+ }
+ }
+ else
+ {
+ voidPtr = va_arg(ap, LPVOID);
+ // sscanf_s requires that if we are trying to read "%s" or "%c", then
+ // the size of the buffer must follow the buffer we are trying to read into.
+ unsigned typeLen = 0;
+ if (Type == SCANF_TYPE_STRING)
+ {
+ // We don’t really know the size of the destination buffer provided by the
+ // caller. So we have to assume that the caller has allocated enough space
+ // to hold either the width specified in the format or the entire input
+ // string plus ‘\0’.
+ typeLen = ((Width > 0) ? Width : PAL_wcslen(Buffer)) + 1;
+ }
+ else if (Type == SCANF_TYPE_CHAR)
+ {
+ // Check whether the format string contains number of characters
+ // that should be read from the input string.
+ // Note: ‘\0’ does not get appended in the “%c” case.
+ typeLen = (Width > 0) ? Width : 1;
+ }
+
+ if (typeLen > 0)
+ {
+ ret = sscanf_s(newBuff, TempBuff, voidPtr, typeLen, &n);
+ }
+ else
+ ret = sscanf_s(newBuff, TempBuff, voidPtr, &n);
+ }
+ }
+ else
+ {
+ ret = sscanf_s(newBuff, TempBuff, &n);
+ }
+
+#if SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
+ if ((ret == 0) && (Type == SCANF_TYPE_FLOAT))
+ {
+ ret = SscanfFloatCheckExponent(newBuff, TempBuff, voidPtr, &n);
+ }
+#endif // SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
+
+ PAL_free(newBuff);
+ if (ret > 0)
+ {
+ Length += ret;
+ }
+ else
+ {
+ /* no match; break scan */
+ break;
+ }
+ Buff += n;
+ }
+ }
+ else
+ {
+ /* grab, but not store */
+ if (*Fmt == *Buff && Type != SCANF_TYPE_SPACE)
+ {
+ ++Fmt;
+ ++Buff;
+ }
+ /* doesn't match, break scan */
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return Length;
+}
+
+/*++
+Function:
+ PAL_sscanf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_sscanf(
+ const char *buffer,
+ const char *format,
+ ...)
+{
+ int Length;
+ va_list ap;
+
+ PERF_ENTRY(sscanf);
+ ENTRY("PAL_sscanf (buffer=%p (%s), format=%p (%s))\n", buffer, buffer, format, format);
+
+ va_start(ap, format);
+ Length = PAL_vsscanf(buffer, format, ap);
+ va_end(ap);
+
+ LOGEXIT("PAL_sscanf returns int %d\n", Length);
+ PERF_EXIT(sscanf);
+ return Length;
+}
+
+/*++
+Function:
+ PAL_sprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_sprintf(
+ char *buffer,
+ const char *format,
+ ...)
+{
+ LONG Length;
+ va_list ap;
+
+ PERF_ENTRY(sprintf);
+ ENTRY("PAL_sprintf (buffer=%p, format=%p (%s))\n", buffer, format, format);
+
+ va_start(ap, format);
+ Length = InternalVsnprintf(CorUnix::InternalGetCurrentThread(), buffer, 0x7fffffff, format, ap);
+ va_end(ap);
+
+ LOGEXIT("PAL_sprintf returns int %d\n", Length);
+ PERF_EXIT(sprintf);
+ return Length;
+}
+
+
+/*++
+Function:
+ PAL_swprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_swprintf(
+ wchar_16 *buffer,
+ const wchar_16 *format,
+ ...)
+{
+ LONG Length;
+ va_list ap;
+
+ PERF_ENTRY(swprintf);
+ ENTRY("PAL_swprintf (buffer=%p, format=%p (%S))\n", buffer, format, format);
+
+ va_start(ap, format);
+ Length = PAL__wvsnprintf(buffer, 0x7fffffff, format, ap);
+ va_end(ap);
+
+ LOGEXIT("PAL_swprintf returns int %d\n", Length);
+ PERF_EXIT(swprintf);
+ return Length;
+}
+
+/*++
+Function:
+ PAL_swscanf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_swscanf(
+ const wchar_16 *buffer,
+ const wchar_16 *format,
+ ...)
+{
+ int Length;
+ va_list ap;
+
+ PERF_ENTRY(swscanf);
+ ENTRY("PAL_swscanf (buffer=%p (%S), format=%p (%S))\n", buffer, buffer, format, format);
+
+ va_start(ap, format);
+ Length = PAL_wvsscanf(buffer, format, ap);
+ va_end(ap);
+
+ LOGEXIT("PAL_swscanf returns int %d\n", Length);
+ PERF_EXIT(swscanf);
+ return Length;
+}
+
+
+/*++
+Function:
+ PAL_vsprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_vsprintf(char *buffer,
+ const char *format,
+ va_list argptr)
+{
+ LONG Length;
+
+ PERF_ENTRY(vsprintf);
+ ENTRY("PAL_vsprintf (buffer=%p, format=%p (%s), argptr=%p)\n",
+ buffer, format, format, argptr);
+
+ Length = InternalVsnprintf(CorUnix::InternalGetCurrentThread(), buffer, 0x7fffffff, format, argptr);
+
+ LOGEXIT("PAL_vsprintf returns int %d\n", Length);
+ PERF_EXIT(vsprintf);
+
+ return Length;
+}
+
+
+/*++
+Function:
+ PAL_vswprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+PAL_vswprintf(wchar_16 *buffer,
+ const wchar_16 *format,
+ va_list argptr)
+{
+ LONG Length;
+
+ PERF_ENTRY(vswprintf);
+ ENTRY("PAL_vswprintf (buffer=%p, format=%p (%S), argptr=%p)\n",
+ buffer, format, format, argptr);
+
+ Length = PAL__wvsnprintf(buffer, 0x7fffffff, format, argptr);
+
+ LOGEXIT("PAL_vswprintf returns int %d\n", Length);
+ PERF_EXIT(vswprintf);
+
+ return Length;
+}
+
+
+/*++
+Function:
+ _vsnwprintf
+
+See MSDN doc.
+--*/
+int
+__cdecl
+_vsnwprintf(wchar_16 *buffer,
+ size_t count,
+ const wchar_16 *format,
+ va_list argptr)
+{
+ LONG Length;
+
+ PERF_ENTRY(_vsnwprintf);
+ ENTRY("_vsnwprintf (buffer=%p, count=%lu, format=%p (%S), argptr=%p)\n",
+ buffer, (unsigned long) count, format, format, argptr);
+
+ Length = PAL__wvsnprintf(buffer, count, format, argptr);
+
+ LOGEXIT("_vsnwprintf returns int %d\n", Length);
+ PERF_EXIT(_vsnwprintf);
+
+ return Length;
+}
+
+#if SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
+/*++
+Function:
+ SscanfFloatCheckExponent
+
+ Parameters:
+ buff: pointer to the buffer to be parsed; the target float must be at
+ the beginning of the buffer, except for any number of leading
+ spaces
+ floatFmt: must be "%e%n" (or "%f%n" or "%g%n")
+ voidptr: optional pointer to output variable (which should be a float)
+ pn: pointer to an int to receive the number of bytes parsed.
+
+ Notes:
+ On some platforms (specifically AIX) sscanf fails to parse a float from
+ a string such as 12.34e (while it succeeds for e.g. 12.34a). Sscanf
+ initially interprets the 'e' as the keyword for the beginning of a
+ 10-exponent of a floating point in scientific notation (as in 12.34e5),
+ but then it fails to parse the actual exponent. At this point sscanf should
+ be able to fall back on the narrower pattern, and parse the floating point
+ in common decimal notation (i.e. 12.34). However AIX's sscanf fails to do
+ so and it does not parse any number.
+ This function checks the given string for a such case and removes
+ the 'e' before parsing the float.
+
+--*/
+
+static int SscanfFloatCheckExponent(LPCSTR buff, LPCSTR floatFmt,
+ void * voidPtr, int * pn)
+{
+ int ret = 0;
+ int digits = 0;
+ int points = 0;
+ LPCSTR pos = buff;
+
+ /* skip initial spaces */
+ while (*pos && isspace(*pos))
+ pos++;
+
+ /* go to the end of a float, if there is one */
+ while (*pos)
+ {
+ if (isdigit(*pos))
+ digits++;
+ else if (*pos == '.')
+ {
+ if (++points > 1)
+ break;
+ }
+ else
+ break;
+
+ pos++;
+ }
+
+ /* check if it is something like 12.34e and the trailing 'e' is not
+ the suffix of a valid exponent of 10, such as 12.34e+5 */
+ if ( digits > 0 && *pos && tolower(*pos) == 'e' &&
+ !( *(pos+1) &&
+ ( isdigit(*(pos+1)) ||
+ ( (*(pos+1) == '+' || *(pos+1) == '-') && isdigit(*(pos+2)) )
+ )
+ )
+ )
+ {
+ CHAR * pLocBuf = (CHAR *)PAL_malloc((pos-buff+1)*sizeof(CHAR));
+ if (pLocBuf)
+ {
+ memcpy(pLocBuf, buff, (pos-buff)*sizeof(CHAR));
+ pLocBuf[pos-buff] = 0;
+ if (voidPtr)
+ ret = sscanf_s(pLocBuf, floatFmt, voidPtr, pn);
+ else
+ ret = sscanf_s(pLocBuf, floatFmt, pn);
+ PAL_free (pLocBuf);
+ }
+ }
+ return ret;
+}
+#endif // SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
diff --git a/src/pal/src/cruntime/printfcpp.cpp b/src/pal/src/cruntime/printfcpp.cpp
new file mode 100644
index 0000000000..ea074a604b
--- /dev/null
+++ b/src/pal/src/cruntime/printfcpp.cpp
@@ -0,0 +1,2556 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ printfcpp.cpp
+
+Abstract:
+
+ Implementation of suspension safe printf functions.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/corunix.hpp"
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+#include "pal/file.hpp"
+#include "pal/printfcpp.hpp"
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/cruntime.h"
+
+#include <errno.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+using namespace CorUnix;
+
+int CoreWvsnprintf(CPalThread *pthrCurrent, LPWSTR Buffer, size_t Count, LPCWSTR Format, va_list ap);
+int CoreVsnprintf(CPalThread *pthrCurrent, LPSTR Buffer, size_t Count, LPCSTR Format, va_list ap);
+int CoreVfprintf(CPalThread *pthrCurrent, PAL_FILE *stream, const char *format, va_list ap);
+int CoreVfwprintf(CPalThread *pthrCurrent, PAL_FILE *stream, const wchar_16 *format, va_list ap);
+
+extern "C"
+{
+
+/*******************************************************************************
+Function:
+ Internal_Convertfwrite
+ This function is a wrapper around fwrite for cases where the buffer has
+ to be converted from WideChar to MultiByte
+*******************************************************************************/
+
+static int Internal_Convertfwrite(CPalThread *pthrCurrent, const void *buffer, size_t size, size_t count, FILE *stream, BOOL convert)
+{
+ int ret;
+ int iError = 0;
+
+#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
+ clearerr (stream);
+#endif
+
+ if(convert)
+ {
+ int nsize;
+ LPSTR newBuff = 0;
+ nsize = WideCharToMultiByte(CP_ACP, 0,(LPCWSTR)buffer, count, 0, 0, 0, 0);
+ if (!nsize)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n", GetLastError());
+ return -1;
+ }
+ newBuff = (LPSTR) InternalMalloc(nsize);
+ if (!newBuff)
+ {
+ ERROR("InternalMalloc failed\n");
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return -1;
+ }
+ nsize = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)buffer, count, newBuff, nsize, 0, 0);
+ if (!nsize)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n", GetLastError());
+ free(newBuff);
+ return -1;
+ }
+ ret = InternalFwrite(newBuff, 1, count, stream, &iError);
+ if (iError != 0)
+ {
+ ERROR("InternalFwrite did not write the whole buffer. Error is %d\n", iError);
+ free(newBuff);
+ return -1;
+ }
+ free(newBuff);
+ }
+ else
+ {
+ ret = InternalFwrite(buffer, size, count, stream, &iError);
+ if (iError != 0)
+ {
+ ERROR("InternalFwrite did not write the whole buffer. Error is %d\n", iError);
+ return -1;
+ }
+ }
+ return ret;
+
+}
+
+/*******************************************************************************
+Function:
+ Internal_ExtractFormatA
+
+Paramaters:
+ Fmt
+ - format string to parse
+ - first character must be a '%'
+ - paramater gets updated to point to the character after
+ the %<foo> format string
+ Out
+ - buffer will contain the %<foo> format string
+ Flags
+ - paramater will be set with the PRINTF_FORMAT_FLAGS defined above
+ Width
+ - will contain the width specified by the format string
+ - -1 if none given
+ Precision
+ - will contain the precision specified in the format string
+ - -1 if none given
+ Prefix
+ - an enumeration of the type prefix
+ Type
+ - an enumeration of the type value
+
+Notes:
+ - I'm also handling the undocumented %ws, %wc, %w...
+ - %#10x, when we have a width greater than the length (i.e padding) the
+ length of the padding is not consistent with MS's wsprintf
+ (MS adds an extra 2 padding chars, length of "0x")
+ - MS's wsprintf seems to ingore a 'h' prefix for number types
+ - MS's "%p" is different than gcc's
+ e.g. printf("%p", NULL);
+ MS --> 00000000
+ gcc --> 0x0
+ - the length of the exponent (precision) for floating types is different
+ between MS and gcc
+ e.g. printf("%E", 256.0);
+ MS --> 2.560000E+002
+ gcc --> 2.560000E+02
+*******************************************************************************/
+BOOL Internal_ExtractFormatA(CPalThread *pthrCurrent, LPCSTR *Fmt, LPSTR Out, LPINT Flags,
+ LPINT Width, LPINT Precision, LPINT Prefix, LPINT Type)
+{
+ BOOL Result = FALSE;
+ LPSTR TempStr;
+ LPSTR TempStrPtr;
+
+ *Width = WIDTH_DEFAULT;
+ *Precision = PRECISION_DEFAULT;
+ *Flags = PFF_NONE;
+ *Prefix = PFF_PREFIX_DEFAULT;
+ *Type = PFF_TYPE_DEFAULT;
+
+ if (*Fmt && **Fmt == '%')
+ {
+ *Out++ = *(*Fmt)++;
+ }
+ else
+ {
+ return Result;
+ }
+
+ /* we'll never need a temp string longer than the original */
+ TempStrPtr = TempStr = (LPSTR) InternalMalloc(strlen(*Fmt)+1);
+ if (!TempStr)
+ {
+ ERROR("InternalMalloc failed\n");
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return Result;
+ }
+
+ /* parse flags */
+ while (**Fmt && (**Fmt == '-' || **Fmt == '+' ||
+ **Fmt == '0' || **Fmt == ' ' || **Fmt == '#'))
+ {
+ switch (**Fmt)
+ {
+ case '-':
+ *Flags |= PFF_MINUS; break;
+ case '+':
+ *Flags |= PFF_PLUS; break;
+ case '0':
+ *Flags |= PFF_ZERO; break;
+ case ' ':
+ *Flags |= PFF_SPACE; break;
+ case '#':
+ *Flags |= PFF_POUND; break;
+ }
+ *Out++ = *(*Fmt)++;
+ }
+ /* '-' flag negates '0' flag */
+ if ((*Flags & PFF_MINUS) && (*Flags & PFF_ZERO))
+ {
+ *Flags -= PFF_ZERO;
+ }
+
+ /* grab width specifier */
+ if (isdigit((unsigned char) **Fmt))
+ {
+ TempStrPtr = TempStr;
+ while (isdigit((unsigned char) **Fmt))
+ {
+ *TempStrPtr++ = **Fmt;
+ *Out++ = *(*Fmt)++;
+ }
+ *TempStrPtr = 0; /* end string */
+ *Width = atoi(TempStr);
+ if (*Width < 0)
+ {
+ ERROR("atoi returned a negative value indicative of an overflow.\n");
+ pthrCurrent->SetLastError(ERROR_INTERNAL_ERROR);
+ return Result;
+ }
+ }
+ else if (**Fmt == '*')
+ {
+ *Width = WIDTH_STAR;
+ *Out++ = *(*Fmt)++;
+ if (isdigit((unsigned char) **Fmt))
+ {
+ /* this is an invalid width because we have a * then a number */
+ /* printf handles this by just printing the whole string */
+ *Width = WIDTH_INVALID;
+ while (isdigit((unsigned char) **Fmt))
+ {
+ *Out++ = *(*Fmt)++;
+ }
+ }
+ }
+
+
+ /* grab precision specifier */
+ if (**Fmt == '.')
+ {
+ *Out++ = *(*Fmt)++;
+ if (isdigit((unsigned char) **Fmt))
+ {
+ TempStrPtr = TempStr;
+ while (isdigit((unsigned char) **Fmt))
+ {
+ *TempStrPtr++ = **Fmt;
+ *Out++ = *(*Fmt)++;
+ }
+ *TempStrPtr = 0; /* end string */
+ *Precision = atoi(TempStr);
+ if (*Precision < 0)
+ {
+ ERROR("atoi returned a negative value indicative of an overflow.\n");
+ pthrCurrent->SetLastError(ERROR_INTERNAL_ERROR);
+ return Result;
+ }
+ }
+ else if (**Fmt == '*')
+ {
+ *Precision = PRECISION_STAR;
+ *Out++ = *(*Fmt)++;
+ if (isdigit((unsigned char) **Fmt))
+ {
+ /* this is an invalid precision because we have a .* then a number */
+ /* printf handles this by just printing the whole string */
+ *Precision = PRECISION_INVALID;
+ while (isdigit((unsigned char) **Fmt))
+ {
+ *Out++ = *(*Fmt)++;
+ }
+ }
+ }
+ else
+ {
+ *Precision = PRECISION_DOT;
+ }
+ }
+
+#ifdef BIT64
+ if (**Fmt == 'p')
+ {
+ *Prefix = PFF_PREFIX_LONGLONG;
+ }
+#endif
+ if ((*Fmt)[0] == 'I')
+ {
+ /* grab prefix of 'I64' for __int64 */
+ if ((*Fmt)[1] == '6' && (*Fmt)[2] == '4')
+ {
+ /* convert to 'll' so that Unix snprintf can handle it */
+ *Fmt += 3;
+ *Prefix = PFF_PREFIX_LONGLONG;
+ }
+ /* grab prefix of 'I32' for __int32 */
+ else if ((*Fmt)[1] == '3' && (*Fmt)[2] == '2')
+ {
+ *Fmt += 3;
+ }
+ else
+ {
+ ++(*Fmt);
+ #ifdef BIT64
+ /* convert to 'll' so that Unix snprintf can handle it */
+ *Prefix = PFF_PREFIX_LONGLONG;
+ #endif
+ }
+ }
+ /* grab a prefix of 'h' */
+ else if (**Fmt == 'h')
+ {
+ *Prefix = PFF_PREFIX_SHORT;
+ ++(*Fmt);
+ }
+ /* grab prefix of 'l' or the undocumented 'w' (at least in MSDN) */
+ else if (**Fmt == 'l' || **Fmt == 'w')
+ {
+ ++(*Fmt);
+#ifdef BIT64
+ // Only want to change the prefix on 64 bit when printing characters.
+ if (**Fmt == 'c' || **Fmt == 's')
+#endif
+ {
+ *Prefix = PFF_PREFIX_LONG;
+ }
+ if (**Fmt == 'l')
+ {
+ *Prefix = PFF_PREFIX_LONGLONG;
+ ++(*Fmt);
+ }
+ }
+ else if (**Fmt == 'L')
+ {
+ /* a prefix of 'L' seems to be ignored */
+ ++(*Fmt);
+ }
+
+ /* grab type 'c' */
+ if (**Fmt == 'c' || **Fmt == 'C')
+ {
+ *Type = PFF_TYPE_CHAR;
+ if (*Prefix != PFF_PREFIX_SHORT && **Fmt == 'C')
+ {
+ *Prefix = PFF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (*Prefix == PFF_PREFIX_LONG)
+ {
+ *Out++ = 'l';
+ }
+ *Out++ = 'c';
+ ++(*Fmt);
+ Result = TRUE;
+ }
+ /* grab type 's' */
+ else if (**Fmt == 's' || **Fmt == 'S')
+ {
+ *Type = PFF_TYPE_STRING;
+ if (*Prefix != PFF_PREFIX_SHORT && **Fmt == 'S')
+ {
+ *Prefix = PFF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (*Prefix == PFF_PREFIX_LONG)
+ {
+ *Out++ = 'l';
+ }
+ *Out++ = 's';
+ ++(*Fmt);
+ Result = TRUE;
+ }
+ /* grab int types */
+ else if (**Fmt == 'd' || **Fmt == 'i' || **Fmt == 'o' ||
+ **Fmt == 'u' || **Fmt == 'x' || **Fmt == 'X')
+ {
+ *Type = PFF_TYPE_INT;
+ if (*Prefix == PFF_PREFIX_SHORT)
+ {
+ *Out++ = 'h';
+ }
+ else if (*Prefix == PFF_PREFIX_LONG)
+ {
+ *Out++ = 'l';
+ }
+ else if (*Prefix == PFF_PREFIX_LONGLONG)
+ {
+ *Out++ = 'l';
+ *Out++ = 'l';
+ }
+ *Out++ = *(*Fmt)++;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'e' || **Fmt == 'E' || **Fmt == 'f' ||
+ **Fmt == 'g' || **Fmt == 'G')
+ {
+ /* we can safely ignore the prefixes and only add the type*/
+ *Type = PFF_TYPE_FLOAT;
+ *Out++ = *(*Fmt)++;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'n')
+ {
+ if (*Prefix == PFF_PREFIX_SHORT)
+ {
+ *Out++ = 'h';
+ }
+ *Out++ = *(*Fmt)++;
+ *Type = PFF_TYPE_N;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'p')
+ {
+ *Type = PFF_TYPE_P;
+ (*Fmt)++;
+
+ if (*Prefix == PFF_PREFIX_LONGLONG)
+ {
+ if (*Precision == PRECISION_DEFAULT)
+ {
+ *Precision = 16;
+ *Out++ = '.';
+ *Out++ = '1';
+ *Out++ = '6';
+ }
+ /* native *printf does not support %I64p
+ (actually %llp), so we need to cheat a little bit */
+ *Out++ = 'l';
+ *Out++ = 'l';
+ }
+ else
+ {
+ if (*Precision == PRECISION_DEFAULT)
+ {
+ *Precision = 8;
+ *Out++ = '.';
+ *Out++ = '8';
+ }
+ }
+ *Out++ = 'X';
+ Result = TRUE;
+ }
+
+ *Out = 0; /* end the string */
+ free(TempStr);
+ return Result;
+}
+
+/*******************************************************************************
+Function:
+ Internal_ExtractFormatW
+
+ -- see Internal_ExtractFormatA above
+*******************************************************************************/
+BOOL Internal_ExtractFormatW(CPalThread *pthrCurrent, LPCWSTR *Fmt, LPSTR Out, LPINT Flags,
+ LPINT Width, LPINT Precision, LPINT Prefix, LPINT Type)
+{
+ BOOL Result = FALSE;
+ LPSTR TempStr;
+ LPSTR TempStrPtr;
+
+ *Width = WIDTH_DEFAULT;
+ *Precision = PRECISION_DEFAULT;
+ *Flags = PFF_NONE;
+ *Prefix = PFF_PREFIX_DEFAULT;
+ *Type = PFF_TYPE_DEFAULT;
+
+ if (*Fmt && **Fmt == '%')
+ {
+ *Out++ = (CHAR) *(*Fmt)++;
+ }
+ else
+ {
+ return Result;
+ }
+
+ /* we'll never need a temp string longer than the original */
+ TempStrPtr = TempStr = (LPSTR) InternalMalloc(PAL_wcslen(*Fmt)+1);
+ if (!TempStr)
+ {
+ ERROR("InternalMalloc failed\n");
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return Result;
+ }
+
+ /* parse flags */
+ while (**Fmt && (**Fmt == '-' || **Fmt == '+' ||
+ **Fmt == '0' || **Fmt == ' ' || **Fmt == '#'))
+ {
+ switch (**Fmt)
+ {
+ case '-':
+ *Flags |= PFF_MINUS; break;
+ case '+':
+ *Flags |= PFF_PLUS; break;
+ case '0':
+ *Flags |= PFF_ZERO; break;
+ case ' ':
+ *Flags |= PFF_SPACE; break;
+ case '#':
+ *Flags |= PFF_POUND; break;
+ }
+ *Out++ = (CHAR) *(*Fmt)++;
+ }
+ /* '-' flag negates '0' flag */
+ if ((*Flags & PFF_MINUS) && (*Flags & PFF_ZERO))
+ {
+ *Flags -= PFF_ZERO;
+ }
+
+ /* grab width specifier */
+ if (isdigit(**Fmt))
+ {
+ TempStrPtr = TempStr;
+ while (isdigit(**Fmt))
+ {
+ *TempStrPtr++ = (CHAR) **Fmt;
+ *Out++ = (CHAR) *(*Fmt)++;
+ }
+ *TempStrPtr = 0; /* end string */
+ *Width = atoi(TempStr);
+ if (*Width < 0)
+ {
+ ERROR("atoi returned a negative value indicative of an overflow.\n");
+ pthrCurrent->SetLastError(ERROR_INTERNAL_ERROR);
+ return Result;
+ }
+ }
+ else if (**Fmt == '*')
+ {
+ *Width = WIDTH_STAR;
+ *Out++ = (CHAR) *(*Fmt)++;
+ if (isdigit(**Fmt))
+ {
+ /* this is an invalid width because we have a * then a number */
+ /* printf handles this by just printing the whole string */
+ *Width = WIDTH_INVALID;
+ while (isdigit(**Fmt))
+ {
+ *Out++ = (CHAR) *(*Fmt)++;
+ }
+ }
+ }
+
+ /* grab precision specifier */
+ if (**Fmt == '.')
+ {
+ *Out++ = (CHAR) *(*Fmt)++;
+ if (isdigit(**Fmt))
+ {
+ TempStrPtr = TempStr;
+ while (isdigit(**Fmt))
+ {
+ *TempStrPtr++ = (CHAR) **Fmt;
+ *Out++ = (CHAR) *(*Fmt)++;
+ }
+ *TempStrPtr = 0; /* end string */
+ *Precision = atoi(TempStr);
+ if (*Precision < 0)
+ {
+ ERROR("atoi returned a negative value indicative of an overflow.\n");
+ pthrCurrent->SetLastError(ERROR_INTERNAL_ERROR);
+ return Result;
+ }
+ }
+ else if (**Fmt == '*')
+ {
+ *Precision = PRECISION_STAR;
+ *Out++ = (CHAR) *(*Fmt)++;
+ if (isdigit(**Fmt))
+ {
+ /* this is an invalid precision because we have a .* then a number */
+ /* printf handles this by just printing the whole string */
+ *Precision = PRECISION_INVALID;
+ while (isdigit(**Fmt))
+ {
+ *Out++ = (CHAR) *(*Fmt)++;
+ }
+ }
+ }
+ else
+ {
+ *Precision = PRECISION_DOT;
+ }
+ }
+
+#ifdef BIT64
+ if (**Fmt == 'p')
+ {
+ *Prefix = PFF_PREFIX_LONGLONG;
+ }
+#endif
+ if ((*Fmt)[0] == 'I')
+ {
+ /* grab prefix of 'I64' for __int64 */
+ if ((*Fmt)[1] == '6' && (*Fmt)[2] == '4')
+ {
+ /* convert to 'll' so that Unix snprintf can handle it */
+ *Fmt += 3;
+ *Prefix = PFF_PREFIX_LONGLONG;
+ }
+ /* grab prefix of 'I32' for __int32 */
+ else if ((*Fmt)[1] == '3' && (*Fmt)[2] == '2')
+ {
+ *Fmt += 3;
+ }
+ else
+ {
+ ++(*Fmt);
+ #ifdef BIT64
+ /* convert to 'll' so that Unix snprintf can handle it */
+ *Prefix = PFF_PREFIX_LONGLONG;
+ #endif
+ }
+ }
+ /* grab a prefix of 'h' */
+ else if (**Fmt == 'h')
+ {
+ *Prefix = PFF_PREFIX_SHORT;
+ ++(*Fmt);
+ }
+ else if (**Fmt == 'l' || **Fmt == 'w')
+ {
+ ++(*Fmt);
+ #ifdef BIT64
+ // Only want to change the prefix on 64 bit when printing characters.
+ if (**Fmt == 'C' || **Fmt == 'S')
+#endif
+ {
+ *Prefix = PFF_PREFIX_LONG_W;
+ }
+ if (**Fmt == 'l')
+ {
+ *Prefix = PFF_PREFIX_LONGLONG;
+ ++(*Fmt);
+ }
+ }
+ else if (**Fmt == 'L')
+ {
+ /* a prefix of 'L' seems to be ignored */
+ ++(*Fmt);
+ }
+
+
+ /* grab type 'c' */
+ if (**Fmt == 'c' || **Fmt == 'C')
+ {
+ *Type = PFF_TYPE_CHAR;
+ if (*Prefix != PFF_PREFIX_SHORT && **Fmt == 'c')
+ {
+ *Prefix = PFF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (*Prefix == PFF_PREFIX_LONG || *Prefix == PFF_PREFIX_LONG_W)
+ {
+ *Out++ = 'l';
+ *Prefix = PFF_PREFIX_LONG;
+ }
+ *Out++ = 'c';
+ ++(*Fmt);
+ Result = TRUE;
+ }
+ /* grab type 's' */
+ else if (**Fmt == 's' || **Fmt == 'S' )
+ {
+ if ( **Fmt == 'S' )
+ {
+ *Type = PFF_TYPE_WSTRING;
+ }
+ else
+ {
+ *Type = PFF_TYPE_STRING;
+ }
+ if (*Prefix != PFF_PREFIX_SHORT && **Fmt == 's')
+ {
+ *Prefix = PFF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (*Prefix == PFF_PREFIX_LONG)
+ {
+ *Out++ = 'l';
+ }
+
+ *Out++ = 's';
+ ++(*Fmt);
+ Result = TRUE;
+ }
+ /* grab int types */
+ else if (**Fmt == 'd' || **Fmt == 'i' || **Fmt == 'o' ||
+ **Fmt == 'u' || **Fmt == 'x' || **Fmt == 'X')
+ {
+ *Type = PFF_TYPE_INT;
+ if (*Prefix == PFF_PREFIX_SHORT)
+ {
+ *Out++ = 'h';
+ }
+ else if (*Prefix == PFF_PREFIX_LONG || *Prefix == PFF_PREFIX_LONG_W)
+ {
+ *Out++ = 'l';
+ *Prefix = PFF_PREFIX_LONG;
+ }
+ else if (*Prefix == PFF_PREFIX_LONGLONG)
+ {
+ *Out++ = 'l';
+ *Out++ = 'l';
+ }
+ *Out++ = *(*Fmt)++;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'e' || **Fmt == 'E' || **Fmt == 'f' ||
+ **Fmt == 'g' || **Fmt == 'G')
+ {
+ /* we can safely ignore the prefixes and only add the type*/
+ if (*Prefix == PFF_PREFIX_LONG_W)
+ {
+ *Prefix = PFF_PREFIX_LONG;
+ }
+
+ *Type = PFF_TYPE_FLOAT;
+ *Out++ = *(*Fmt)++;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'n')
+ {
+ if (*Prefix == PFF_PREFIX_LONG_W)
+ {
+ *Prefix = PFF_PREFIX_LONG;
+ }
+
+ if (*Prefix == PFF_PREFIX_SHORT)
+ {
+ *Out++ = 'h';
+ }
+ *Out++ = *(*Fmt)++;
+ *Type = PFF_TYPE_N;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'p')
+ {
+ *Type = PFF_TYPE_P;
+ (*Fmt)++;
+
+ if (*Prefix == PFF_PREFIX_LONGLONG)
+ {
+ if (*Precision == PRECISION_DEFAULT)
+ {
+ *Precision = 16;
+ *Out++ = '.';
+ *Out++ = '1';
+ *Out++ = '6';
+ }
+ /* native *printf does not support %I64p
+ (actually %llp), so we need to cheat a little bit */
+ *Out++ = 'l';
+ *Out++ = 'l';
+ }
+ else
+ {
+ if (*Precision == PRECISION_DEFAULT)
+ {
+ *Precision = 8;
+ *Out++ = '.';
+ *Out++ = '8';
+ }
+ if (*Prefix == PFF_PREFIX_LONG_W)
+ {
+ *Prefix = PFF_PREFIX_LONG;
+ }
+ }
+ *Out++ = 'X';
+ Result = TRUE;
+ }
+
+ *Out = 0; /* end the string */
+ free(TempStr);
+ return Result;
+}
+
+/*******************************************************************************
+Function:
+ Internal_AddPaddingW
+
+Parameters:
+ Out
+ - buffer to place padding and given string (In)
+ Count
+ - maximum chars to be copied so as not to overrun given buffer
+ In
+ - string to place into (Out) accompanied with padding
+ Padding
+ - number of padding chars to add
+ Flags
+ - padding style flags (PRINTF_FORMAT_FLAGS)
+*******************************************************************************/
+
+BOOL Internal_AddPaddingW(LPWSTR *Out, INT Count, LPWSTR In, INT Padding, INT Flags)
+{
+ LPWSTR OutOriginal = *Out;
+ INT PaddingOriginal = Padding;
+ INT LengthInStr;
+ LengthInStr = PAL_wcslen(In);
+
+
+ if (Padding < 0)
+ {
+ /* this is used at the bottom to determine if the buffer ran out */
+ PaddingOriginal = 0;
+ }
+ if (Flags & PFF_MINUS) /* pad on right */
+ {
+ if (wcsncpy_s(*Out, Count, In, min(LengthInStr + 1, Count - 1)) != SAFECRT_SUCCESS)
+ {
+ return FALSE;
+ }
+
+ *Out += min(LengthInStr, Count - 1);
+ }
+ if (Padding > 0)
+ {
+ if (Flags & PFF_ZERO) /* '0', pad with zeros */
+ {
+ while (Padding-- && Count > *Out - OutOriginal)
+ {
+ *(*Out)++ = '0';
+ }
+ }
+ else /* pad left with spaces */
+ {
+ while (Padding-- && Count > *Out - OutOriginal)
+ {
+ *(*Out)++ = ' ';
+ }
+ }
+ }
+ if (!(Flags & PFF_MINUS)) /* put 'In' after padding */
+ {
+ if (wcsncpy_s(*Out, Count - (*Out - OutOriginal), In,
+ min(LengthInStr, Count - (*Out - OutOriginal) - 1)) != SAFECRT_SUCCESS)
+ {
+ return FALSE;
+ }
+
+ *Out += min(LengthInStr, Count - (*Out - OutOriginal) - 1);
+ }
+
+ if (LengthInStr + PaddingOriginal > Count - 1)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+/*******************************************************************************
+Function:
+ Internal_AddPaddingVfprintf
+
+Parameters:
+ stream
+ - file stream to place padding and given string (In)
+ In
+ - string to place into (Out) accompanied with padding
+ Padding
+ - number of padding chars to add
+ Flags
+ - padding style flags (PRINTF_FORMAT_FLAGS)
+*******************************************************************************/
+
+INT Internal_AddPaddingVfprintf(CPalThread *pthrCurrent, PAL_FILE *stream, LPSTR In,
+ INT Padding, INT Flags)
+{
+ LPSTR Out;
+ INT LengthInStr;
+ INT Length;
+ LPSTR OutOriginal;
+ INT Written;
+
+ LengthInStr = strlen(In);
+ Length = LengthInStr;
+
+ if (Padding > 0)
+ {
+ Length += Padding;
+ }
+ Out = (LPSTR) InternalMalloc(Length+1);
+ int iLength = Length+1;
+ if (!Out)
+ {
+ ERROR("InternalMalloc failed\n");
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return -1;
+ }
+ OutOriginal = Out;
+
+ if (Flags & PFF_MINUS) /* pad on right */
+ {
+ if (strcpy_s(Out, iLength, In) != SAFECRT_SUCCESS)
+ {
+ ERROR("strcpy_s failed\n");
+ pthrCurrent->SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ Written = -1;
+ goto Done;
+ }
+
+ Out += LengthInStr;
+ iLength -= LengthInStr;
+ }
+ if (Padding > 0)
+ {
+ iLength -= Padding;
+ if (Flags & PFF_ZERO) /* '0', pad with zeros */
+ {
+ while (Padding--)
+ {
+ *Out++ = '0';
+ }
+ }
+ else /* pad with spaces */
+ {
+ while (Padding--)
+ {
+ *Out++ = ' ';
+ }
+ }
+ }
+ if (!(Flags & PFF_MINUS)) /* put 'In' after padding */
+ {
+ if (strcpy_s(Out, iLength, In) != SAFECRT_SUCCESS)
+ {
+ ERROR("strcpy_s failed\n");
+ pthrCurrent->SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ Written = -1;
+ goto Done;
+ }
+
+ Out += LengthInStr;
+ iLength -= LengthInStr;
+ }
+
+#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
+ clearerr (stream->bsdFilePtr);
+#endif
+
+ Written = InternalFwrite(OutOriginal, 1, Length, stream->bsdFilePtr, &stream->PALferrorCode);
+ if (stream->PALferrorCode == PAL_FILE_ERROR)
+ {
+ ERROR("fwrite() failed with errno == %d\n", errno);
+ }
+
+Done:
+ free(OutOriginal);
+
+ return Written;
+}
+
+/*******************************************************************************
+Function:
+ Internal_AddPaddingVfwprintf
+
+Parameters:
+ stream
+ - file stream to place padding and given string (In)
+ In
+ - string to place into (Out) accompanied with padding
+ Padding
+ - number of padding chars to add
+ Flags
+ - padding style flags (PRINTF_FORMAT_FLAGS)
+*******************************************************************************/
+static INT Internal_AddPaddingVfwprintf(CPalThread *pthrCurrent, PAL_FILE *stream, LPWSTR In,
+ INT Padding, INT Flags,BOOL convert)
+{
+ LPWSTR Out;
+ LPWSTR OutOriginal;
+ INT LengthInStr;
+ INT Length;
+ INT Written = 0;
+
+ LengthInStr = PAL_wcslen(In);
+ Length = LengthInStr;
+
+ if (Padding > 0)
+ {
+ Length += Padding;
+ }
+
+ int iLen = (Length+1);
+ Out = (LPWSTR) InternalMalloc(iLen * sizeof(WCHAR));
+ if (!Out)
+ {
+ ERROR("InternalMalloc failed\n");
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return -1;
+ }
+ OutOriginal = Out;
+
+ if (Flags & PFF_MINUS) /* pad on right */
+ {
+ if (wcscpy_s(Out, iLen, In) != SAFECRT_SUCCESS)
+ {
+ ERROR("wcscpy_s failed!\n");
+ free(OutOriginal);
+ pthrCurrent->SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return -1;
+ }
+ Out += LengthInStr;
+ iLen -= LengthInStr;
+ }
+ if (Padding > 0)
+ {
+ iLen -= Padding;
+ if (Flags & PFF_ZERO) /* '0', pad with zeros */
+ {
+ while (Padding--)
+ {
+ *Out++ = '0';
+ }
+ }
+ else /* pad with spaces */
+ {
+ while (Padding--)
+ {
+ *Out++ = ' ';
+ }
+ }
+ }
+ if (!(Flags & PFF_MINUS)) /* put 'In' after padding */
+ {
+ if (wcscpy_s(Out, iLen, In) != SAFECRT_SUCCESS)
+ {
+ ERROR("wcscpy_s failed!\n");
+ free(OutOriginal);
+ pthrCurrent->SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return -1;
+ }
+
+ Out += LengthInStr;
+ iLen -= LengthInStr;
+ }
+
+ if (Length > 0) {
+ Written = Internal_Convertfwrite(pthrCurrent, OutOriginal, sizeof(wchar_16), Length,
+ (FILE*)(stream->bsdFilePtr), convert);
+
+ if (-1 == Written)
+ {
+ ERROR("fwrite() failed with errno == %d\n", errno);
+ }
+ free(OutOriginal);
+ }
+
+ return Written;
+}
+
+/*******************************************************************************
+Function:
+ PAL_vsnprintf
+
+Parameters:
+ Buffer
+ - out buffer
+ Count
+ - buffer size
+ Format
+ - format string
+ ap
+ - stdarg parameter list
+*******************************************************************************/
+
+int __cdecl PAL__vsnprintf(LPSTR Buffer, size_t Count, LPCSTR Format, va_list ap)
+{
+ LONG Length;
+
+ PERF_ENTRY(PAL__vsnprintf);
+ ENTRY("PAL__vsnprintf (buffer=%p, count=%d, format=%p (%s), argptr=%p)\n",
+ Buffer, Count, Format, Format, ap);
+
+ Length = CoreVsnprintf(InternalGetCurrentThread(), Buffer, Count, Format, ap);
+
+ LOGEXIT("PAL__vsnprintf returns int %d\n", Length);
+ PERF_EXIT(PAL__vsnprintf);
+
+ return Length;
+}
+
+/*******************************************************************************
+Function:
+ PAL_wvsnprintf
+
+ -- see PAL_vsnprintf above
+*******************************************************************************/
+
+int __cdecl PAL__wvsnprintf(LPWSTR Buffer, size_t Count, LPCWSTR Format, va_list ap)
+{
+ return CoreWvsnprintf(InternalGetCurrentThread(), Buffer, Count, Format, ap);
+}
+
+/*******************************************************************************
+Function:
+ PAL_vfprintf
+
+Parameters:
+ stream
+ - out stream
+ Format
+ - format string
+ ap
+ - stdarg parameter list
+*******************************************************************************/
+
+int __cdecl PAL_vfprintf(PAL_FILE *stream, const char *format, va_list ap)
+{
+ return CoreVfprintf(InternalGetCurrentThread(), stream, format, ap);
+}
+
+/*******************************************************************************
+Function:
+ PAL_vfwprintf
+
+Parameters:
+ stream
+ - out stream
+ Format
+ - format string
+ ap
+ - stdarg parameter list
+*******************************************************************************/
+
+int __cdecl PAL_vfwprintf(PAL_FILE *stream, const wchar_16 *format, va_list ap)
+{
+ return CoreVfwprintf(InternalGetCurrentThread(), stream, format, ap);
+}
+
+} // end extern "C"
+
+int CorUnix::InternalWvsnprintf(CPalThread *pthrCurrent, LPWSTR Buffer, size_t Count, LPCWSTR Format, va_list ap)
+{
+ return CoreWvsnprintf(pthrCurrent, Buffer, Count, Format, ap);
+}
+
+int CorUnix::InternalVsnprintf(CPalThread *pthrCurrent, LPSTR Buffer, size_t Count, LPCSTR Format, va_list ap)
+{
+ return CoreVsnprintf(pthrCurrent, Buffer, Count, Format, ap);
+}
+
+int CorUnix::InternalVfprintf(CPalThread *pthrCurrent, PAL_FILE *stream, const char *format, va_list ap)
+{
+ return CoreVfprintf(pthrCurrent, stream, format, ap);
+}
+
+int CorUnix::InternalVfwprintf(CPalThread *pthrCurrent, PAL_FILE *stream, const wchar_16 *format, va_list ap)
+{
+ return CoreVfwprintf(pthrCurrent, stream, format, ap);
+}
+
+int CoreVfwprintf(CPalThread *pthrCurrent, PAL_FILE *stream, const wchar_16 *format, va_list aparg)
+{
+ CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */
+ LPCWSTR Fmt = format;
+ LPWSTR TempWStr = NULL;
+ LPWSTR WorkingWStr = NULL;
+ WCHAR TempWChar[2];
+ INT Flags;
+ INT Width;
+ INT Precision;
+ INT Prefix;
+ INT Type;
+ INT TempInt;
+ BOOL WStrWasMalloced = FALSE;
+ int mbtowcResult;
+ int written=0;
+ int paddingReturnValue;
+ int ret;
+ va_list ap;
+
+ /* fwprintf for now in the PAL is always used on file opened
+ in text mode. In those case the output should be ANSI not Unicode */
+ BOOL textMode = TRUE;
+
+ PERF_ENTRY(vfwprintf);
+ ENTRY("vfwprintf (stream=%p, format=%p (%S))\n",
+ stream, format, format);
+
+ va_copy(ap, aparg);
+
+ while (*Fmt)
+ {
+ if(*Fmt == '%' &&
+ TRUE == Internal_ExtractFormatW(pthrCurrent, &Fmt, TempBuff, &Flags,
+ &Width, &Precision,
+ &Prefix, &Type))
+ {
+ if (((Prefix == PFF_PREFIX_LONG || Prefix == PFF_PREFIX_LONG_W) &&
+ (Type == PFF_TYPE_STRING || Type == PFF_TYPE_WSTRING)) ||
+ (Type == PFF_TYPE_WSTRING && (Flags & PFF_ZERO) != 0))
+ {
+ WStrWasMalloced = FALSE;
+
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ else if (WIDTH_INVALID == Width)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+ else if (PRECISION_INVALID == Precision)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ if (Type == PFF_TYPE_STRING || Prefix == PFF_PREFIX_LONG_W)
+ {
+ TempWStr = va_arg(ap, LPWSTR);
+ }
+ else
+ {
+ /* %lS assumes a LPSTR argument. */
+ LPSTR s = va_arg(ap, LPSTR );
+ UINT Length = 0;
+ Length = MultiByteToWideChar( CP_ACP, 0, s, -1, NULL, 0 );
+ if ( Length != 0 )
+ {
+ TempWStr =
+ (LPWSTR)InternalMalloc( (Length) * sizeof( WCHAR ) );
+ if ( TempWStr )
+ {
+ WStrWasMalloced = TRUE;
+ MultiByteToWideChar( CP_ACP, 0, s, -1,
+ TempWStr, Length );
+ }
+ else
+ {
+ ERROR( "InternalMalloc failed.\n" );
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ va_end(ap);
+ return -1;
+ }
+ }
+ else
+ {
+ ASSERT( "Unable to convert from multibyte "
+ " to wide char.\n" );
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ va_end(ap);
+ return -1;
+ }
+ }
+
+ INT Length = PAL_wcslen(TempWStr);
+ WorkingWStr = (LPWSTR) InternalMalloc((sizeof(WCHAR) * (Length + 1)));
+ if (!WorkingWStr)
+ {
+ ERROR("InternalMalloc failed\n");
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ if (WStrWasMalloced)
+ {
+ free(TempWStr);
+ }
+ va_end(ap);
+ return -1;
+ }
+ if (PRECISION_DOT == Precision)
+ {
+ /* copy nothing */
+ *WorkingWStr = 0;
+ Length = 0;
+ }
+ else if (Precision > 0 && Precision < Length)
+ {
+ if (wcsncpy_s(WorkingWStr, (Length + 1), TempWStr, Precision+1) != SAFECRT_SUCCESS)
+ {
+ ERROR("Internal_AddPaddingVfwprintf failed\n");
+ if (WStrWasMalloced)
+ {
+ free(TempWStr);
+ }
+ free(WorkingWStr);
+ LOGEXIT("wcsncpy_s failed!\n");
+ PERF_EXIT(vfwprintf);
+ va_end(ap);
+ return (-1);
+ }
+
+ Length = Precision;
+ }
+ /* copy everything */
+ else
+ {
+ PAL_wcscpy(WorkingWStr, TempWStr);
+ }
+
+
+ /* do the padding (if needed)*/
+ paddingReturnValue =
+ Internal_AddPaddingVfwprintf( pthrCurrent, stream, WorkingWStr,
+ Width - Length,
+ Flags,textMode);
+
+ if (paddingReturnValue == -1)
+ {
+ ERROR("Internal_AddPaddingVfwprintf failed\n");
+ if (WStrWasMalloced)
+ {
+ free(TempWStr);
+ }
+ free(WorkingWStr);
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ va_end(ap);
+ return (-1);
+ }
+ written += paddingReturnValue;
+
+ free(WorkingWStr);
+ if (WStrWasMalloced)
+ {
+ free(TempWStr);
+ }
+ }
+ else if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_CHAR)
+ {
+ if (WIDTH_STAR == Width ||
+ WIDTH_INVALID == Width)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ if (PRECISION_STAR == Precision ||
+ PRECISION_INVALID == Precision)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ TempWChar[0] = va_arg(ap, int);
+ TempWChar[1] = 0;
+
+ /* do the padding (if needed)*/
+ paddingReturnValue =
+ Internal_AddPaddingVfwprintf(pthrCurrent, stream, TempWChar,
+ Width - 1,
+ Flags,textMode);
+ if (paddingReturnValue == -1)
+ {
+ ERROR("Internal_AddPaddingVfwprintf failed\n");
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ va_end(ap);
+ return(-1);
+ }
+ written += paddingReturnValue;
+ }
+ /* this places the number of bytes written to the buffer in the
+ next arg */
+ else if (Type == PFF_TYPE_N)
+ {
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+
+ if (Prefix == PFF_PREFIX_SHORT)
+ {
+ *(va_arg(ap, short *)) = written;
+ }
+ else
+ {
+ *(va_arg(ap, LPLONG)) = written;
+ }
+ }
+ else
+ {
+ // Types that sprintf can handle.
+
+ /* note: I'm using the wide buffer as a (char *) buffer when I
+ pass it to sprintf(). After I get the buffer back I make a
+ backup of the chars copied and then convert them to wide
+ and place them in the buffer (BufferPtr) */
+
+ // This argument will be limited to 1024 characters.
+ // It should be enough.
+ size_t TEMP_COUNT = 1024;
+ char TempSprintfStrBuffer[1024];
+ char *TempSprintfStrPtr = NULL;
+ char *TempSprintfStr = TempSprintfStrBuffer;
+ LPWSTR TempWideBuffer;
+
+ TempInt = 0;
+ // %h (short) doesn't seem to be handled properly by local sprintf,
+ // so we do the truncation ourselves for some cases.
+ if (Type == PFF_TYPE_P && Prefix == PFF_PREFIX_SHORT)
+ {
+ // Convert from pointer -> int -> short to avoid warnings.
+ long trunc1;
+ short trunc2;
+
+ trunc1 = va_arg(ap, LONG);
+ trunc2 = (short)trunc1;
+ trunc1 = trunc2;
+
+ TempInt = snprintf(TempSprintfStr, TEMP_COUNT, TempBuff, trunc1);
+
+ if (TempInt < 0 || static_cast<size_t>(TempInt) >= TEMP_COUNT)
+ {
+ if (NULL == (TempSprintfStrPtr = (char*)InternalMalloc(++TempInt)))
+ {
+ ERROR("InternalMalloc failed\n");
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ va_end(ap);
+ return -1;
+ }
+
+ TempSprintfStr = TempSprintfStrPtr;
+ snprintf(TempSprintfStr, TempInt, TempBuff, trunc2);
+ }
+ }
+ else if (Type == PFF_TYPE_INT && Prefix == PFF_PREFIX_SHORT)
+ {
+ // Convert explicitly from int to short to get
+ // correct sign extension for shorts on all systems.
+ int n;
+ short s;
+
+ n = va_arg(ap, int);
+ s = (short) n;
+
+ TempInt = snprintf(TempSprintfStr, TEMP_COUNT, TempBuff, s);
+
+ if (TempInt < 0 || static_cast<size_t>(TempInt) >= TEMP_COUNT)
+ {
+ if (NULL == (TempSprintfStrPtr = (char*)InternalMalloc(++TempInt)))
+ {
+ ERROR("InternalMalloc failed\n");
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ va_end(ap);
+ return -1;
+ }
+
+ TempSprintfStr = TempSprintfStrPtr;
+ snprintf(TempSprintfStr, TempInt, TempBuff, s);
+ }
+ }
+ else
+ {
+ va_list apcopy;
+
+ va_copy(apcopy, ap);
+ TempInt = vsnprintf(TempSprintfStr, TEMP_COUNT, TempBuff, apcopy);
+ va_end(apcopy);
+ PAL_printf_arg_remover(&ap, Width, Precision, Type, Prefix);
+
+ if (TempInt < 0 || static_cast<size_t>(TempInt) >= TEMP_COUNT)
+ {
+ if (NULL == (TempSprintfStrPtr = (char*)InternalMalloc(++TempInt)))
+ {
+ ERROR("InternalMalloc failed\n");
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ va_end(ap);
+ return -1;
+ }
+
+ TempSprintfStr = TempSprintfStrPtr;
+ va_copy(apcopy, ap);
+ vsnprintf(TempSprintfStr, TempInt, TempBuff, apcopy);
+ va_end(apcopy);
+ PAL_printf_arg_remover(&ap, Width, Precision, Type, Prefix);
+ }
+ }
+
+ mbtowcResult = MultiByteToWideChar(CP_ACP, 0,
+ TempSprintfStr, -1,
+ NULL, 0);
+
+ if (mbtowcResult == 0)
+ {
+ ERROR("MultiByteToWideChar failed\n");
+ if(TempSprintfStrPtr)
+ {
+ free(TempSprintfStrPtr);
+ }
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ va_end(ap);
+ return -1;
+ }
+
+ TempWideBuffer = (LPWSTR) InternalMalloc(mbtowcResult*sizeof(WCHAR));
+ if (!TempWideBuffer)
+ {
+ ERROR("InternalMalloc failed\n");
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ if(TempSprintfStrPtr)
+ {
+ free(TempSprintfStrPtr);
+ }
+ va_end(ap);
+ return -1;
+ }
+
+ MultiByteToWideChar(CP_ACP, 0, TempSprintfStr, -1,
+ TempWideBuffer, mbtowcResult);
+
+ ret = Internal_Convertfwrite(
+ pthrCurrent,
+ TempWideBuffer,
+ sizeof(wchar_16),
+ mbtowcResult-1,
+ (FILE*)stream->bsdFilePtr,
+ textMode);
+
+ if (-1 == ret)
+ {
+ ERROR("fwrite() failed with errno == %d (%s)\n", errno, strerror(errno));
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ free(TempWideBuffer);
+ if(TempSprintfStrPtr)
+ {
+ free(TempSprintfStrPtr);
+ }
+ va_end(ap);
+ return -1;
+ }
+ if(TempSprintfStrPtr)
+ {
+ free(TempSprintfStrPtr);
+ }
+ free(TempWideBuffer);
+ }
+ }
+ else
+ {
+ ret = Internal_Convertfwrite(
+ pthrCurrent,
+ Fmt++,
+ sizeof(wchar_16),
+ 1,
+ (FILE*)stream->bsdFilePtr,
+ textMode); /* copy regular chars into buffer */
+
+ if (-1 == ret)
+ {
+ ERROR("fwrite() failed with errno == %d\n", errno);
+ LOGEXIT("vfwprintf returns int -1\n");
+ PERF_EXIT(vfwprintf);
+ va_end(ap);
+ return -1;
+ }
+ ++written;
+ }
+ }
+
+ LOGEXIT("vfwprintf returns int %d\n", written);
+ PERF_EXIT(vfwprintf);
+ va_end(ap);
+ return (written);
+}
+
+int CoreVsnprintf(CPalThread *pthrCurrent, LPSTR Buffer, size_t Count, LPCSTR Format, va_list aparg)
+{
+ BOOL BufferRanOut = FALSE;
+ CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */
+ LPSTR BufferPtr = Buffer;
+ LPCSTR Fmt = Format;
+ LPWSTR TempWStr;
+ LPSTR TempStr;
+ WCHAR TempWChar;
+ INT Flags;
+ INT Width;
+ INT Precision;
+ INT Prefix;
+ INT Type;
+ INT Length;
+ INT TempInt;
+ int wctombResult;
+ va_list ap;
+
+ va_copy(ap, aparg);
+
+ while (*Fmt)
+ {
+ if (BufferRanOut || (BufferPtr - Buffer) >= static_cast<int>(Count)) //Count is assumed to be in the range of int
+ {
+ BufferRanOut = TRUE;
+ break;
+ }
+ else if(*Fmt == '%' &&
+ TRUE == Internal_ExtractFormatA(pthrCurrent, &Fmt, TempBuff, &Flags,
+ &Width, &Precision,
+ &Prefix, &Type))
+ {
+ if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_STRING)
+ {
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ else if (WIDTH_INVALID == Width)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+ else if (PRECISION_INVALID == Precision)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ TempWStr = va_arg(ap, LPWSTR);
+ Length = WideCharToMultiByte(CP_ACP, 0, TempWStr, -1, 0,
+ 0, 0, 0);
+ if (!Length)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ va_end(ap);
+ return -1;
+ }
+ TempStr = (LPSTR) InternalMalloc(Length);
+ if (!TempStr)
+ {
+ ERROR("InternalMalloc failed\n");
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ va_end(ap);
+ return -1;
+ }
+ if (PRECISION_DOT == Precision)
+ {
+ /* copy nothing */
+ *TempStr = 0;
+ Length = 0;
+ }
+ else if (Precision > 0 && Precision < Length - 1)
+ {
+ Length = WideCharToMultiByte(CP_ACP, 0, TempWStr,
+ Precision, TempStr, Length,
+ 0, 0);
+ if (!Length)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ free(TempStr);
+ va_end(ap);
+ return -1;
+ }
+ TempStr[Length] = 0;
+ Length = Precision;
+ }
+ /* copy everything */
+ else
+ {
+ wctombResult = WideCharToMultiByte(CP_ACP, 0, TempWStr, -1,
+ TempStr, Length, 0, 0);
+ if (!wctombResult)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ free(TempStr);
+ va_end(ap);
+ return -1;
+ }
+ --Length; /* exclude null char */
+ }
+
+ /* do the padding (if needed)*/
+ BufferRanOut = !Internal_AddPaddingA(&BufferPtr,
+ Count - (BufferPtr - Buffer),
+ TempStr,
+ Width - Length,
+ Flags);
+
+ free(TempStr);
+ }
+ else if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_CHAR)
+ {
+ CHAR TempBuffer[5];
+
+ if (WIDTH_STAR == Width ||
+ WIDTH_INVALID == Width)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+ if (PRECISION_STAR == Precision ||
+ PRECISION_INVALID == Precision)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ TempWChar = va_arg(ap, int);
+ Length = WideCharToMultiByte(CP_ACP, 0, &TempWChar, 1,
+ TempBuffer, sizeof(TempBuffer),
+ 0, 0);
+ if (!Length)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ va_end(ap);
+ return -1;
+ }
+ TempBuffer[Length] = 0;
+
+ /* do the padding (if needed)*/
+ BufferRanOut = !Internal_AddPaddingA(&BufferPtr,
+ Count - (BufferPtr - Buffer),
+ TempBuffer,
+ Width - Length,
+ Flags);
+
+ }
+ /* this places the number of bytes written to the buffer in the
+ next arg */
+ else if (Type == PFF_TYPE_N)
+ {
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+
+ if (Prefix == PFF_PREFIX_SHORT)
+ {
+ *(va_arg(ap, short *)) = BufferPtr - Buffer;
+ }
+ else
+ {
+ *(va_arg(ap, LPLONG)) = BufferPtr - Buffer;
+ }
+ }
+ else if (Type == PFF_TYPE_CHAR && (Flags & PFF_ZERO) != 0)
+ {
+ // Some versions of sprintf don't support 0-padded chars,
+ // so we handle them here.
+ char ch[2];
+
+ ch[0] = (char) va_arg(ap, int);
+ ch[1] = '\0';
+ Length = 1;
+ BufferRanOut = !Internal_AddPaddingA(&BufferPtr,
+ Count - (BufferPtr - Buffer),
+ ch,
+ Width - Length,
+ Flags);
+ }
+ else if (Type == PFF_TYPE_STRING && (Flags & PFF_ZERO) != 0)
+ {
+ // Some versions of sprintf don't support 0-padded strings,
+ // so we handle them here.
+ char *tempStr;
+
+ tempStr = va_arg(ap, char *);
+ Length = strlen(tempStr);
+ BufferRanOut = !Internal_AddPaddingA(&BufferPtr,
+ Count - (BufferPtr - Buffer),
+ tempStr,
+ Width - Length,
+ Flags);
+ }
+ else
+ {
+ // Types that sprintf can handle
+ size_t TempCount = Count - (BufferPtr - Buffer);
+
+#if !HAVE_LARGE_SNPRINTF_SUPPORT
+ // Limit TempCount to 0x40000000, which is sufficient
+ // for platforms on which snprintf fails for very large
+ // sizes.
+ if (TempCount > 0x40000000)
+ {
+ TempCount = 0x40000000;
+ }
+#endif // HAVE_LARGE_SNPRINTF_SUPPORT
+
+ TempInt = 0;
+ // %h (short) doesn't seem to be handled properly by local sprintf,
+ // so we do the truncation ourselves for some cases.
+ if (Type == PFF_TYPE_P && Prefix == PFF_PREFIX_SHORT)
+ {
+ // Convert from pointer -> int -> short to avoid warnings.
+ long trunc1;
+ short trunc2;
+
+ trunc1 = va_arg(ap, LONG);
+ trunc2 = (short) trunc1;
+ trunc1 = trunc2;
+
+ TempInt = snprintf(BufferPtr, TempCount, TempBuff, trunc1);
+ }
+ else if (Type == PFF_TYPE_INT && Prefix == PFF_PREFIX_SHORT)
+ {
+ // Convert explicitly from int to short to get
+ // correct sign extension for shorts on all systems.
+ int n;
+ short s;
+
+ n = va_arg(ap, int);
+ s = (short) n;
+
+ TempInt = snprintf(BufferPtr, TempCount, TempBuff, s);
+ }
+ else
+ {
+ va_list apcopy;
+ va_copy(apcopy, ap);
+ TempInt = vsnprintf(BufferPtr, TempCount, TempBuff, apcopy);
+ va_end(apcopy);
+ PAL_printf_arg_remover(&ap, Width, Precision, Type, Prefix);
+ }
+
+ if (TempInt < 0 || static_cast<size_t>(TempInt) >= TempCount) /* buffer not long enough */
+ {
+ BufferPtr += TempCount;
+ BufferRanOut = TRUE;
+ }
+ else
+ {
+ BufferPtr += TempInt;
+ }
+ }
+ }
+ else
+ {
+ *BufferPtr++ = *Fmt++; /* copy regular chars into buffer */
+ }
+ }
+
+ if (static_cast<int>(Count) > (BufferPtr - Buffer)) //Count is assumed to be in the range of int
+ {
+ *BufferPtr = 0; /* end the string */
+ }
+
+ va_end(ap);
+
+ if (BufferRanOut)
+ {
+ errno = ERANGE;
+ return -1;
+ }
+ else
+ {
+ return BufferPtr - Buffer;
+ }
+}
+
+int CoreWvsnprintf(CPalThread *pthrCurrent, LPWSTR Buffer, size_t Count, LPCWSTR Format, va_list aparg)
+{
+ BOOL BufferRanOut = FALSE;
+ CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */
+ LPWSTR BufferPtr = Buffer;
+ LPCWSTR Fmt = Format;
+ LPWSTR TempWStr = NULL;
+ LPWSTR WorkingWStr = NULL;
+ WCHAR TempWChar[2];
+ INT Flags;
+ INT Width;
+ INT Precision;
+ INT Prefix;
+ INT Type;
+ INT TempInt;
+ LPSTR TempNumberBuffer;
+ int mbtowcResult;
+ va_list(ap);
+
+ PERF_ENTRY(wvsnprintf);
+ ENTRY("wvsnprintf (buffer=%p, count=%u, format=%p (%S))\n",
+ Buffer, Count, Format, Format);
+
+ va_copy(ap, aparg);
+
+ while (*Fmt)
+ {
+ if (BufferRanOut || (BufferPtr - Buffer) >= static_cast<int>(Count)) //Count is assumed to be in the range of int
+ {
+ BufferRanOut = TRUE;
+ break;
+ }
+ else if(*Fmt == '%' &&
+ TRUE == Internal_ExtractFormatW(pthrCurrent, &Fmt, TempBuff, &Flags,
+ &Width, &Precision,
+ &Prefix, &Type))
+ {
+ if (((Prefix == PFF_PREFIX_LONG || Prefix == PFF_PREFIX_LONG_W) &&
+ (Type == PFF_TYPE_STRING || Type == PFF_TYPE_WSTRING)) ||
+ (Prefix == PFF_PREFIX_SHORT && Type == PFF_TYPE_STRING) ||
+ (Type == PFF_TYPE_WSTRING && (Flags & PFF_ZERO) != 0))
+ {
+ BOOL needToFree = FALSE;
+
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ else if (WIDTH_INVALID == Width)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+ else if (PRECISION_INVALID == Precision)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ if ((Type == PFF_TYPE_STRING && Prefix == PFF_PREFIX_LONG) ||
+ Prefix == PFF_PREFIX_LONG_W)
+ {
+ TempWStr = va_arg(ap, LPWSTR);
+ }
+ else
+ {
+ // %lS and %hs assume an LPSTR argument.
+ LPSTR s = va_arg(ap, LPSTR );
+ UINT Length = 0;
+ Length = MultiByteToWideChar( CP_ACP, 0, s, -1, NULL, 0 );
+ if ( Length != 0 )
+ {
+ TempWStr =
+ (LPWSTR)InternalMalloc((Length + 1 ) * sizeof( WCHAR ) );
+ if ( TempWStr )
+ {
+ needToFree = TRUE;
+ MultiByteToWideChar( CP_ACP, 0, s, -1,
+ TempWStr, Length );
+ }
+ else
+ {
+ ERROR( "InternalMalloc failed.\n" );
+ va_end(ap);
+ return -1;
+ }
+ }
+ else
+ {
+ ASSERT( "Unable to convert from multibyte "
+ " to wide char.\n" );
+ va_end(ap);
+ return -1;
+ }
+
+ }
+
+ INT Length = PAL_wcslen(TempWStr);
+ WorkingWStr = (LPWSTR) InternalMalloc(sizeof(WCHAR) * (Length + 1));
+ if (!WorkingWStr)
+ {
+ ERROR("InternalMalloc failed\n");
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ if (needToFree)
+ {
+ free(TempWStr);
+ }
+ va_end(ap);
+ return -1;
+ }
+ if (PRECISION_DOT == Precision)
+ {
+ // Copy nothing
+ *WorkingWStr = 0;
+ Length = 0;
+ }
+ else if (Precision > 0 && Precision < Length)
+ {
+ if (wcsncpy_s(WorkingWStr, (Length + 1), TempWStr, Precision+1) != SAFECRT_SUCCESS)
+ {
+ ERROR("CoreWvsnprintf failed\n");
+ if (needToFree)
+ {
+ free(TempWStr);
+ }
+ free(WorkingWStr);
+ LOGEXIT("wcsncpy_s failed!\n");
+ PERF_EXIT(wvsnprintf);
+ va_end(ap);
+ return (-1);
+ }
+
+ Length = Precision;
+ }
+ else
+ {
+ // Copy everything
+ PAL_wcscpy(WorkingWStr, TempWStr);
+ }
+
+ // Add padding if needed.
+ BufferRanOut = !Internal_AddPaddingW(&BufferPtr,
+ Count - (BufferPtr - Buffer),
+ WorkingWStr,
+ Width - Length,
+ Flags);
+
+ if (needToFree)
+ {
+ free(TempWStr);
+ }
+ free(WorkingWStr);
+ }
+ else if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_CHAR)
+ {
+ if (WIDTH_STAR == Width ||
+ WIDTH_INVALID == Width)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ if (PRECISION_STAR == Precision ||
+ PRECISION_INVALID == Precision)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ TempWChar[0] = va_arg(ap, int);
+ TempWChar[1] = 0;
+
+ /* do the padding (if needed)*/
+ BufferRanOut = !Internal_AddPaddingW(&BufferPtr,
+ Count - (BufferPtr - Buffer),
+ TempWChar,
+ Width - 1,
+ Flags);
+
+ }
+ /* this places the number of bytes written to the buffer in the
+ next arg */
+ else if (Type == PFF_TYPE_N)
+ {
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+
+ if (Prefix == PFF_PREFIX_SHORT)
+ {
+ *(va_arg(ap, short *)) = BufferPtr - Buffer;
+ }
+ else
+ {
+ *(va_arg(ap, LPLONG)) = BufferPtr - Buffer;
+ }
+ }
+ else
+ {
+ // Types that sprintf can handle
+
+ /* note: I'm using the wide buffer as a (char *) buffer when I
+ pass it to sprintf(). After I get the buffer back I make a
+ backup of the chars copied and then convert them to wide
+ and place them in the buffer (BufferPtr) */
+ size_t TempCount = Count - (BufferPtr - Buffer);
+ TempInt = 0;
+
+#if !HAVE_LARGE_SNPRINTF_SUPPORT
+ // Limit TempCount to 0x40000000, which is sufficient
+ // for platforms on which snprintf fails for very large
+ // sizes.
+ if (TempCount > 0x40000000)
+ {
+ TempCount = 0x40000000;
+ }
+#endif // HAVE_LARGE_SNPRINTF_SUPPORT
+
+ // %h (short) doesn't seem to be handled properly by local sprintf,
+ // so we do the truncation ourselves for some cases.
+ if (Type == PFF_TYPE_P && Prefix == PFF_PREFIX_SHORT)
+ {
+ // Convert from pointer -> int -> short to avoid warnings.
+ long trunc1;
+ short trunc2;
+
+ trunc1 = va_arg(ap, LONG);
+ trunc2 = (short)trunc1;
+ trunc1 = trunc2;
+
+ TempInt = snprintf((LPSTR)BufferPtr, TempCount, TempBuff, trunc1);
+ }
+ else if (Type == PFF_TYPE_INT && Prefix == PFF_PREFIX_SHORT)
+ {
+ // Convert explicitly from int to short to get
+ // correct sign extension for shorts on all systems.
+ int n;
+ short s;
+
+ n = va_arg(ap, int);
+ s = (short) n;
+
+ TempInt = snprintf((LPSTR)BufferPtr, TempCount, TempBuff, s);
+ }
+ else
+ {
+ va_list apcopy;
+ va_copy(apcopy, ap);
+ TempInt = vsnprintf((LPSTR) BufferPtr, TempCount, TempBuff, apcopy);
+ va_end(apcopy);
+ PAL_printf_arg_remover(&ap, Width, Precision, Type, Prefix);
+ }
+
+ if (TempInt == 0)
+ {
+ // The argument is "".
+ continue;
+ }
+ if (TempInt < 0 || static_cast<size_t>(TempInt) >= TempCount) /* buffer not long enough */
+ {
+ TempNumberBuffer = (LPSTR) InternalMalloc(TempCount+1);
+ if (!TempNumberBuffer)
+ {
+ ERROR("InternalMalloc failed\n");
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ errno = ENOMEM;
+ va_end(ap);
+ return -1;
+ }
+
+ if (strncpy_s(TempNumberBuffer, TempCount+1, (LPSTR) BufferPtr, TempCount) != SAFECRT_SUCCESS)
+ {
+ ASSERT("strncpy_s failed!\n");
+ free(TempNumberBuffer);
+ va_end(ap);
+ return -1;
+ }
+
+ mbtowcResult = MultiByteToWideChar(CP_ACP, 0,
+ TempNumberBuffer,
+ TempCount,
+ BufferPtr, TempCount);
+ if (!mbtowcResult)
+ {
+ ASSERT("MultiByteToWideChar failed. Error is %d\n",
+ GetLastError());
+ free(TempNumberBuffer);
+ va_end(ap);
+ return -1;
+ }
+ BufferPtr += TempCount;
+ BufferRanOut = TRUE;
+ }
+ else
+ {
+ TempNumberBuffer = (LPSTR) InternalMalloc(TempInt+1);
+ if (!TempNumberBuffer)
+ {
+ ERROR("InternalMalloc failed\n");
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ va_end(ap);
+ return -1;
+ }
+
+ if (strncpy_s(TempNumberBuffer, TempInt+1, (LPSTR) BufferPtr, TempInt) != SAFECRT_SUCCESS)
+ {
+ ASSERT("strncpy_s failed!\n");
+ free(TempNumberBuffer);
+ va_end(ap);
+ return -1;
+ }
+
+ mbtowcResult = MultiByteToWideChar(CP_ACP, 0,
+ TempNumberBuffer,
+ TempInt,
+ BufferPtr, TempInt);
+ if (!mbtowcResult)
+ {
+ ASSERT("MultiByteToWideChar failed. Error is %d\n",
+ GetLastError());
+ free(TempNumberBuffer);
+ va_end(ap);
+ return -1;
+ }
+ BufferPtr += TempInt;
+ }
+ free(TempNumberBuffer);
+ }
+ }
+ else
+ {
+ *BufferPtr++ = *Fmt++; /* copy regular chars into buffer */
+ }
+ }
+
+ if (static_cast<int>(Count) > (BufferPtr - Buffer)) //Count is assumed to be in the range of int
+ {
+ *BufferPtr = 0; /* end the string */
+ }
+
+ va_end(ap);
+
+ if (BufferRanOut)
+ {
+ errno = ERANGE;
+ return -1;
+ }
+ else
+ {
+ return BufferPtr - Buffer;
+ }
+}
+
+int CoreVfprintf(CPalThread *pthrCurrent, PAL_FILE *stream, const char *format, va_list aparg)
+{
+ CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */
+ LPCSTR Fmt = format;
+ LPWSTR TempWStr;
+ LPSTR TempStr;
+ WCHAR TempWChar;
+ INT Flags;
+ INT Width;
+ INT Precision;
+ INT Prefix;
+ INT Type;
+ INT Length;
+ INT TempInt;
+ int wctombResult;
+ int written = 0;
+ int paddingReturnValue;
+ va_list ap;
+
+ PERF_ENTRY(vfprintf);
+
+ va_copy(ap, aparg);
+
+ while (*Fmt)
+ {
+ if (*Fmt == '%' &&
+ TRUE == Internal_ExtractFormatA(pthrCurrent, &Fmt, TempBuff, &Flags,
+ &Width, &Precision,
+ &Prefix, &Type))
+ {
+ if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_STRING)
+ {
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ else if (WIDTH_INVALID == Width)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+ else if (PRECISION_INVALID == Precision)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ TempWStr = va_arg(ap, LPWSTR);
+ Length = WideCharToMultiByte(CP_ACP, 0, TempWStr, -1, 0,
+ 0, 0, 0);
+ if (!Length)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ PERF_EXIT(vfprintf);
+ va_end(ap);
+ return -1;
+ }
+ TempStr = (LPSTR) InternalMalloc(Length);
+ if (!TempStr)
+ {
+ ERROR("InternalMalloc failed\n");
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ PERF_EXIT(vfprintf);
+ va_end(ap);
+ return -1;
+ }
+ if (PRECISION_DOT == Precision)
+ {
+ /* copy nothing */
+ *TempStr = 0;
+ Length = 0;
+ }
+ else if (Precision > 0 && Precision < Length - 1)
+ {
+ Length = WideCharToMultiByte(CP_ACP, 0, TempWStr,
+ Precision, TempStr, Length,
+ 0, 0);
+ if (!Length)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ free(TempStr);
+ PERF_EXIT(vfprintf);
+ va_end(ap);
+ return -1;
+ }
+ TempStr[Length] = 0;
+ Length = Precision;
+ }
+ /* copy everything */
+ else
+ {
+ wctombResult = WideCharToMultiByte(CP_ACP, 0, TempWStr, -1,
+ TempStr, Length, 0, 0);
+ if (!wctombResult)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ free(TempStr);
+ PERF_EXIT(vfprintf);
+ va_end(ap);
+ return -1;
+ }
+ --Length; /* exclude null char */
+ }
+
+ /* do the padding (if needed)*/
+ paddingReturnValue =
+ Internal_AddPaddingVfprintf(pthrCurrent, stream, TempStr,
+ Width - Length, Flags);
+ if (-1 == paddingReturnValue)
+ {
+ ERROR("Internal_AddPaddingVfprintf failed\n");
+ free(TempStr);
+ PERF_EXIT(vfprintf);
+ va_end(ap);
+ return -1;
+ }
+ written += paddingReturnValue;
+
+ free(TempStr);
+ }
+ else if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_CHAR)
+ {
+ CHAR TempBuffer[5];
+ if (WIDTH_STAR == Width ||
+ WIDTH_INVALID == Width)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+ if (PRECISION_STAR == Precision ||
+ PRECISION_INVALID == Precision)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ TempWChar = va_arg(ap, int);
+ Length = WideCharToMultiByte(CP_ACP, 0, &TempWChar, 1,
+ TempBuffer, sizeof(TempBuffer),
+ 0, 0);
+ if (!Length)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ PERF_EXIT(vfprintf);
+ va_end(ap);
+ return -1;
+ }
+ TempBuffer[Length] = 0;
+
+ /* do the padding (if needed)*/
+ paddingReturnValue =
+ Internal_AddPaddingVfprintf(pthrCurrent, stream, TempBuffer,
+ Width - Length, Flags);
+ if (-1 == paddingReturnValue)
+ {
+ ERROR("Internal_AddPaddingVfprintf failed\n");
+ PERF_EXIT(vfprintf);
+ va_end(ap);
+ return -1;
+ }
+ written += paddingReturnValue;
+
+ }
+ /* this places the number of bytes written to the buffer in the
+ next arg */
+ else if (Type == PFF_TYPE_N)
+ {
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+
+ if (Prefix == PFF_PREFIX_SHORT)
+ {
+ *(va_arg(ap, short *)) = written;
+ }
+ else
+ {
+ *(va_arg(ap, LPLONG)) = written;
+ }
+ }
+ else if (Type == PFF_TYPE_CHAR && (Flags & PFF_ZERO) != 0)
+ {
+ // Some versions of fprintf don't support 0-padded chars,
+ // so we handle them here.
+ char ch[2];
+
+ ch[0] = (char) va_arg(ap, int);
+ ch[1] = '\0';
+ Length = 1;
+ paddingReturnValue = Internal_AddPaddingVfprintf(
+ pthrCurrent,
+ stream,
+ ch,
+ Width - Length,
+ Flags);
+ if (-1 == paddingReturnValue)
+ {
+ ERROR("Internal_AddPaddingVfprintf failed\n");
+ PERF_EXIT(vfprintf);
+ va_end(ap);
+ return -1;
+ }
+ written += paddingReturnValue;
+ }
+ else if (Type == PFF_TYPE_STRING && (Flags & PFF_ZERO) != 0)
+ {
+ // Some versions of fprintf don't support 0-padded strings,
+ // so we handle them here.
+ char *tempStr;
+
+ tempStr = va_arg(ap, char *);
+ Length = strlen(tempStr);
+ paddingReturnValue = Internal_AddPaddingVfprintf(
+ pthrCurrent,
+ stream,
+ tempStr,
+ Width - Length,
+ Flags);
+ if (-1 == paddingReturnValue)
+ {
+ ERROR("Internal_AddPaddingVfprintf failed\n");
+ PERF_EXIT(vfprintf);
+ va_end(ap);
+ return -1;
+ }
+ written += paddingReturnValue;
+ }
+ else
+ {
+ // Types that fprintf can handle.
+ TempInt = 0;
+
+ // %h (short) doesn't seem to be handled properly by local sprintf,
+ // so we do the truncation ourselves for some cases.
+ if (Type == PFF_TYPE_P && Prefix == PFF_PREFIX_SHORT)
+ {
+ // Convert from pointer -> int -> short to avoid warnings.
+ long trunc1;
+ short trunc2;
+
+ trunc1 = va_arg(ap, LONG);
+ trunc2 = (short)trunc1;
+ trunc1 = trunc2;
+
+ TempInt = fprintf(stream->bsdFilePtr, TempBuff, trunc1);
+ }
+ else if (Type == PFF_TYPE_INT && Prefix == PFF_PREFIX_SHORT)
+ {
+ // Convert explicitly from int to short to get
+ // correct sign extension for shorts on all systems.
+ int n;
+ short s;
+
+ n = va_arg(ap, int);
+ s = (short) n;
+
+ TempInt = fprintf( stream->bsdFilePtr, TempBuff, s);
+ }
+ else
+ {
+ va_list apcopy;
+ va_copy(apcopy, ap);
+ TempInt = vfprintf(stream->bsdFilePtr, TempBuff, apcopy);
+ va_end(apcopy);
+ PAL_printf_arg_remover(&ap, Width, Precision, Type, Prefix);
+ }
+
+ if (-1 == TempInt)
+ {
+ ERROR("vfprintf returned an error\n");
+ }
+ else
+ {
+ written += TempInt;
+ }
+ }
+ }
+ else
+ {
+
+#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
+ clearerr (stream->bsdFilePtr);
+#endif
+
+ InternalFwrite(Fmt++, 1, 1, stream->bsdFilePtr, &stream->PALferrorCode); /* copy regular chars into buffer */
+ if (stream->PALferrorCode == PAL_FILE_ERROR)
+ {
+ ERROR("fwrite() failed with errno == %d\n", errno);
+ PERF_EXIT(vfprintf);
+ va_end(ap);
+ return -1;
+ }
+ ++written;
+ }
+ }
+
+ va_end(ap);
+
+ PERF_EXIT(vfprintf);
+ return written;
+}
diff --git a/src/pal/src/cruntime/silent_printf.cpp b/src/pal/src/cruntime/silent_printf.cpp
new file mode 100644
index 0000000000..1d10963973
--- /dev/null
+++ b/src/pal/src/cruntime/silent_printf.cpp
@@ -0,0 +1,990 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ silent_printf.c
+
+Abstract:
+
+ Implementation of a silent version of PAL_vsprintf and PAL_vfprintf function.
+ (without any reference to TRACE/ERROR/... macros, needed by the tracing macros)
+
+Revision History:
+
+
+
+--*/
+
+
+#include "pal/palinternal.h"
+#include "pal/cruntime.h"
+#include "pal/locale.h"
+#include "pal/printfcpp.hpp"
+#include "pal/thread.hpp"
+
+/* clip strings (%s, %S) at this number of characters */
+#define MAX_STR_LEN 300
+
+static int Silent_WideCharToMultiByte(LPCWSTR lpWideCharStr, int cchWideChar,
+ LPSTR lpMultiByteStr, int cbMultiByte);
+static BOOL Silent_ExtractFormatA(LPCSTR *Fmt, LPSTR Out, LPINT Flags, LPINT Width,
+ LPINT Precision, LPINT Prefix, LPINT Type);
+static INT Silent_AddPaddingVfprintf(PAL_FILE *stream, LPSTR In, INT Padding,
+ INT Flags);
+
+static size_t Silent_PAL_wcslen(const wchar_16 *string);
+
+/*******************************************************************************
+Function:
+ PAL_vsnprintf (silent version)
+ for more details, see PAL_vsnprintf in printf.c
+*******************************************************************************/
+INT Silent_PAL_vsnprintf(LPSTR Buffer, INT Count, LPCSTR Format, va_list aparg)
+{
+ BOOL BufferRanOut = FALSE;
+ CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */
+ LPSTR BufferPtr = Buffer;
+ LPCSTR Fmt = Format;
+ LPWSTR TempWStr;
+ CHAR TempStr[MAX_STR_LEN+1];
+ WCHAR TempWChar;
+ INT Flags;
+ INT Width;
+ INT Precision;
+ INT Prefix;
+ INT Type;
+ INT Length;
+ INT TempInt;
+ int wctombResult;
+ va_list ap;
+
+ va_copy(ap, aparg);
+
+ while (*Fmt)
+ {
+ if ((BufferPtr - Buffer) >= Count)
+ {
+ BufferRanOut = TRUE;
+ break;
+ }
+ else if(*Fmt == '%' &&
+ TRUE == Silent_ExtractFormatA(&Fmt, TempBuff, &Flags,
+ &Width, &Precision,
+ &Prefix, &Type))
+ {
+ if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_STRING)
+ {
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ else if (WIDTH_INVALID == Width)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+ else if (PRECISION_INVALID == Precision)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ TempWStr = va_arg(ap, LPWSTR);
+ Length = Silent_WideCharToMultiByte(TempWStr, -1, 0, 0);
+ if (!Length)
+ {
+ va_end(ap);
+ return -1;
+ }
+
+ /* clip string output to MAX_STR_LEN characters */
+ if (PRECISION_DOT == Precision)
+ {
+ Precision = MAX_STR_LEN;
+ }
+
+ if (PRECISION_DOT == Precision)
+ {
+ /* copy nothing */
+ *TempStr = 0;
+ Length = 0;
+ }
+ else if (Precision > 0 && Precision < Length - 1)
+ {
+ Length = Silent_WideCharToMultiByte(TempWStr, Precision,
+ TempStr, Length);
+ if (!Length)
+ {
+ va_end(ap);
+ return -1;
+ }
+ TempStr[Length] = 0;
+ Length = Precision;
+ }
+ /* copy everything */
+ else
+ {
+ wctombResult = Silent_WideCharToMultiByte(TempWStr, -1,
+ TempStr, Length);
+ if (!wctombResult)
+ {
+ PAL_free(TempStr);
+ va_end(ap);
+ return -1;
+ }
+ --Length; /* exclude null char */
+ }
+
+ /* do the padding (if needed)*/
+ BufferRanOut = !Internal_AddPaddingA(&BufferPtr,
+ Count - (BufferPtr - Buffer),
+ TempStr,
+ Width - Length,
+ Flags);
+ }
+ else if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_CHAR)
+ {
+ CHAR TempBuffer[4];
+
+ if (WIDTH_STAR == Width ||
+ WIDTH_INVALID == Width)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+ if (PRECISION_STAR == Precision ||
+ PRECISION_INVALID == Precision)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ TempWChar = va_arg(ap, int);
+ Length = Silent_WideCharToMultiByte(&TempWChar, 1, TempBuffer, 4);
+ if (!Length)
+ {
+ va_end(ap);
+ return -1;
+ }
+ TempBuffer[Length] = 0;
+
+ /* do the padding (if needed)*/
+ BufferRanOut = !Internal_AddPaddingA(&BufferPtr,
+ Count - (BufferPtr - Buffer),
+ TempBuffer,
+ Width - Length,
+ Flags);
+
+ }
+ /* this places the number of bytes written to the buffer in the
+ next arg */
+ else if (Type == PFF_TYPE_N)
+ {
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+ if (Prefix == PFF_PREFIX_SHORT)
+ {
+ *(va_arg(ap, short *)) = BufferPtr - Buffer;
+ }
+ else
+ {
+ *(va_arg(ap, LPLONG)) = BufferPtr - Buffer;
+ }
+ }
+ else if (Type == PFF_TYPE_CHAR && (Flags & PFF_ZERO) != 0)
+ {
+ // Some versions of sprintf don't support 0-padded chars,
+ // so we handle them here.
+ char ch[2];
+
+ ch[0] = (char) va_arg(ap, int);
+ ch[1] = '\0';
+ Length = 1;
+ BufferRanOut = !Internal_AddPaddingA(&BufferPtr,
+ Count - (BufferPtr - Buffer),
+ ch,
+ Width - Length,
+ Flags);
+ }
+ else if (Type == PFF_TYPE_STRING && (Flags & PFF_ZERO) != 0)
+ {
+ // Some versions of sprintf don't support 0-padded strings,
+ // so we handle them here.
+ char *tempStr;
+
+ tempStr = va_arg(ap, char *);
+ Length = strlen(tempStr);
+ BufferRanOut = !Internal_AddPaddingA(&BufferPtr,
+ Count - (BufferPtr - Buffer),
+ tempStr,
+ Width - Length,
+ Flags);
+ }
+ /* types that sprintf can handle */
+ else
+ {
+ size_t TempCount = Count - (BufferPtr - Buffer);
+
+ TempInt = 0;
+ /* %h (short) doesn't seem to be handled properly by local sprintf,
+ so lets do the truncation ourselves. (ptr -> int -> short to avoid
+ warnings */
+ if (Type == PFF_TYPE_P && Prefix == PFF_PREFIX_SHORT)
+ {
+ long trunc1;
+ short trunc2;
+
+ trunc1 = va_arg(ap, LONG);
+ trunc2 = (short)trunc1;
+
+ TempInt = snprintf(BufferPtr, TempCount, TempBuff, trunc2);
+ }
+ else if (Type == PFF_TYPE_INT && Prefix == PFF_PREFIX_SHORT)
+ {
+ // Convert explicitly from int to short to get
+ // correct sign extension for shorts on all systems.
+ int n;
+ short s;
+
+ n = va_arg(ap, int);
+ s = (short) n;
+
+ TempInt = snprintf(BufferPtr, TempCount, TempBuff, s);
+ }
+ else
+ {
+ /* limit string output (%s) to 300 characters */
+ if(TempBuff[0] == '%' && TempBuff[1] == 's')
+ {
+ if (strcpy_s(TempBuff, sizeof(TempBuff), "%.300s") != SAFECRT_SUCCESS)
+ {
+ va_end(ap);
+ return -1;
+ }
+ }
+ va_list apcopy;
+ va_copy(apcopy, ap);
+ TempInt = InternalVsnprintf(CorUnix::InternalGetCurrentThread(), BufferPtr, TempCount, TempBuff, apcopy);
+ va_end(apcopy);
+ PAL_printf_arg_remover(&ap, Width, Precision, Type, Prefix);
+ }
+
+ if (TempInt < 0 || static_cast<size_t>(TempInt) >= TempCount) /* buffer not long enough */
+ {
+ BufferPtr += TempCount;
+ BufferRanOut = TRUE;
+ }
+ else
+ {
+ BufferPtr += TempInt;
+ }
+ }
+ }
+ else
+ {
+ *BufferPtr++ = *Fmt++; /* copy regular chars into buffer */
+ }
+ }
+
+ if (Count > (BufferPtr - Buffer))
+ {
+ *BufferPtr = 0; /* end the string */
+ }
+
+ va_end(ap);
+
+ if (BufferRanOut)
+ {
+ return -1;
+ }
+ else
+ {
+ return BufferPtr - Buffer;
+ }
+}
+
+/*++
+Function:
+ PAL_vfprintf (silent version)
+
+ for more details, see PAL_vfprintf in printf.c
+--*/
+int Silent_PAL_vfprintf(PAL_FILE *stream, const char *format, va_list aparg)
+{
+ CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */
+ LPCSTR Fmt = format;
+ LPWSTR TempWStr;
+ LPSTR TempStr;
+ WCHAR TempWChar;
+ INT Flags;
+ INT Width;
+ INT Precision;
+ INT Prefix;
+ INT Type;
+ INT Length;
+ INT TempInt;
+ int wctombResult;
+ int written = 0;
+ int paddingReturnValue;
+ va_list ap;
+
+ va_copy(ap, aparg);
+
+ while (*Fmt)
+ {
+ if (*Fmt == '%' &&
+ TRUE == Silent_ExtractFormatA(&Fmt, TempBuff, &Flags, &Width,
+ &Precision, &Prefix, &Type))
+ {
+ if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_STRING)
+ {
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ else if (WIDTH_INVALID == Width)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+ else if (PRECISION_INVALID == Precision)
+ {
+ /* both a '*' and a number, ignore, but remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ TempWStr = va_arg(ap, LPWSTR);
+ Length = Silent_WideCharToMultiByte(TempWStr, -1, 0, 0);
+ if (!Length)
+ {
+ va_end(ap);
+ return -1;
+ }
+ TempStr = (LPSTR) PAL_malloc(Length);
+ if (!TempStr)
+ {
+ va_end(ap);
+ return -1;
+ }
+ if (PRECISION_DOT == Precision)
+ {
+ /* copy nothing */
+ *TempStr = 0;
+ Length = 0;
+ }
+ else if (Precision > 0 && Precision < Length - 1)
+ {
+ Length = Silent_WideCharToMultiByte(TempWStr, Precision,
+ TempStr, Length);
+ if (!Length)
+ {
+ PAL_free(TempStr);
+ va_end(ap);
+ return -1;
+ }
+ TempStr[Length] = 0;
+ Length = Precision;
+ }
+ /* copy everything */
+ else
+ {
+ wctombResult = Silent_WideCharToMultiByte(TempWStr, -1,
+ TempStr, Length);
+ if (!wctombResult)
+ {
+ PAL_free(TempStr);
+ va_end(ap);
+ return -1;
+ }
+ --Length; /* exclude null char */
+ }
+
+ /* do the padding (if needed)*/
+ paddingReturnValue =
+ Silent_AddPaddingVfprintf(stream, TempStr, Width - Length, Flags);
+ if (-1 == paddingReturnValue)
+ {
+ PAL_free(TempStr);
+ va_end(ap);
+ return -1;
+ }
+ written += paddingReturnValue;
+
+ PAL_free(TempStr);
+ }
+ else if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_CHAR)
+ {
+ CHAR TempBuffer[4];
+ if (WIDTH_STAR == Width ||
+ WIDTH_INVALID == Width)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+ if (PRECISION_STAR == Precision ||
+ PRECISION_INVALID == Precision)
+ {
+ /* ignore (because it's a char), and remove arg */
+ TempInt = va_arg(ap, INT); /* value not used */
+ }
+
+ TempWChar = va_arg(ap, int);
+ Length = Silent_WideCharToMultiByte(&TempWChar, 1, TempBuffer, 4);
+ if (!Length)
+ {
+ va_end(ap);
+ return -1;
+ }
+ TempBuffer[Length] = 0;
+
+ /* do the padding (if needed)*/
+ paddingReturnValue =
+ Silent_AddPaddingVfprintf(stream, TempBuffer,
+ Width - Length, Flags);
+ if (-1 == paddingReturnValue)
+ {
+ va_end(ap);
+ return -1;
+ }
+ written += paddingReturnValue;
+
+ }
+ /* this places the number of bytes written to the buffer in the
+ next arg */
+ else if (Type == PFF_TYPE_N)
+ {
+ if (WIDTH_STAR == Width)
+ {
+ Width = va_arg(ap, INT);
+ }
+ if (PRECISION_STAR == Precision)
+ {
+ Precision = va_arg(ap, INT);
+ }
+
+ if (Prefix == PFF_PREFIX_SHORT)
+ {
+ *(va_arg(ap, short *)) = written;
+ }
+ else
+ {
+ *(va_arg(ap, LPLONG)) = written;
+ }
+ }
+ /* types that sprintf can handle */
+ else
+ {
+ TempInt = 0;
+
+ /* %h (short) doesn't seem to be handled properly by local sprintf,
+ so lets do the truncation ourselves. (ptr -> int -> short to avoid
+ warnings */
+ if (Type == PFF_TYPE_P && Prefix == PFF_PREFIX_SHORT)
+ {
+ long trunc1;
+ short trunc2;
+
+ trunc1 = va_arg(ap, LONG);
+ trunc2 = (short)trunc1;
+
+ TempInt = fprintf((FILE*)stream, TempBuff, trunc2);
+ }
+ else if (Type == PFF_TYPE_INT && Prefix == PFF_PREFIX_SHORT)
+ {
+ // Convert explicitly from int to short to get
+ // correct sign extension for shorts on all systems.
+ int n;
+ short s;
+
+ n = va_arg(ap, int);
+ s = (short)n;
+
+ TempInt = fprintf((FILE*)stream, TempBuff, s);
+ }
+ else
+ {
+ va_list apcopy;
+ va_copy(apcopy, ap);
+ TempInt = PAL_vfprintf(stream, TempBuff, apcopy);
+ va_end(apcopy);
+ PAL_printf_arg_remover(&ap, Width, Precision, Type, Prefix);
+ }
+
+ if (-1 != TempInt)
+ {
+ written += TempInt;
+ }
+ }
+ }
+ else
+ {
+#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
+ clearerr((FILE*)stream);
+#endif
+
+ PAL_fwrite(Fmt++, 1, 1, stream); /* copy regular chars into buffer */
+ if (stream->PALferrorCode == PAL_FILE_ERROR)
+ {
+ va_end(ap);
+ return -1;
+ }
+ ++written;
+ }
+ }
+
+ va_end(ap);
+ return written;
+}
+
+/*++
+Function:
+ WideCharToMultiByte (reduced and silent version)
+
+See MSDN doc.
+--*/
+int Silent_WideCharToMultiByte(LPCWSTR lpWideCharStr, int cchWideChar,
+ LPSTR lpMultiByteStr, int cbMultiByte)
+{
+ INT retval =0;
+
+ if ((lpWideCharStr == NULL)||
+ (lpWideCharStr == (LPCWSTR) lpMultiByteStr))
+ {
+ goto EXIT;
+ }
+
+ if (cchWideChar == -1)
+ {
+ cchWideChar = Silent_PAL_wcslen(lpWideCharStr) + 1;
+ }
+
+ if (cbMultiByte == 0)
+ {
+ retval = cchWideChar;
+ goto EXIT;
+ }
+ else if(cbMultiByte < cchWideChar)
+ {
+ retval = 0;
+ goto EXIT;
+ }
+
+ retval = cchWideChar;
+ while(cchWideChar > 0)
+ {
+ if(*lpWideCharStr > 255)
+ {
+ *lpMultiByteStr = '?';
+ }
+ else
+ {
+ *lpMultiByteStr = (unsigned char)*lpWideCharStr;
+ }
+ lpMultiByteStr++;
+ lpWideCharStr++;
+ cchWideChar--;
+ }
+
+EXIT:
+ return retval;
+}
+
+/*******************************************************************************
+Function:
+ Internal_ExtractFormatA (silent version)
+
+ see Internal_ExtractFormatA function in printf.c
+*******************************************************************************/
+BOOL Silent_ExtractFormatA(LPCSTR *Fmt, LPSTR Out, LPINT Flags, LPINT Width, LPINT Precision, LPINT Prefix, LPINT Type)
+{
+ BOOL Result = FALSE;
+ LPSTR TempStr;
+ LPSTR TempStrPtr;
+
+ *Width = WIDTH_DEFAULT;
+ *Precision = PRECISION_DEFAULT;
+ *Flags = PFF_NONE;
+ *Prefix = PFF_PREFIX_DEFAULT;
+ *Type = PFF_TYPE_DEFAULT;
+
+ if (*Fmt && **Fmt == '%')
+ {
+ *Out++ = *(*Fmt)++;
+ }
+ else
+ {
+ return Result;
+ }
+
+ /* we'll never need a temp string longer than the original */
+ TempStrPtr = TempStr = (LPSTR) PAL_malloc(strlen(*Fmt)+1);
+ if (!TempStr)
+ {
+ return Result;
+ }
+
+ /* parse flags */
+ while (**Fmt && (**Fmt == '-' || **Fmt == '+' ||
+ **Fmt == '0' || **Fmt == ' ' || **Fmt == '#'))
+ {
+ switch (**Fmt)
+ {
+ case '-':
+ *Flags |= PFF_MINUS; break;
+ case '+':
+ *Flags |= PFF_PLUS; break;
+ case '0':
+ *Flags |= PFF_ZERO; break;
+ case ' ':
+ *Flags |= PFF_SPACE; break;
+ case '#':
+ *Flags |= PFF_POUND; break;
+ }
+ *Out++ = *(*Fmt)++;
+ }
+ /* '-' flag negates '0' flag */
+ if ((*Flags & PFF_MINUS) && (*Flags & PFF_ZERO))
+ {
+ *Flags -= PFF_ZERO;
+ }
+
+ /* grab width specifier */
+ if (isdigit((unsigned char) **Fmt))
+ {
+ TempStrPtr = TempStr;
+ while (isdigit((unsigned char) **Fmt))
+ {
+ *TempStrPtr++ = **Fmt;
+ *Out++ = *(*Fmt)++;
+ }
+ *TempStrPtr = 0; /* end string */
+ *Width = atoi(TempStr);
+ if (*Width < 0)
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ return Result;
+ }
+ }
+ else if (**Fmt == '*')
+ {
+ *Width = WIDTH_STAR;
+ *Out++ = *(*Fmt)++;
+ if (isdigit((unsigned char) **Fmt))
+ {
+ /* this is an invalid width because we have a * then a number */
+ /* printf handles this by just printing the whole string */
+ *Width = WIDTH_INVALID;
+ while (isdigit((unsigned char) **Fmt))
+ {
+ *Out++ = *(*Fmt)++;
+ }
+ }
+ }
+
+
+ /* grab precision specifier */
+ if (**Fmt == '.')
+ {
+ *Out++ = *(*Fmt)++;
+ if (isdigit((unsigned char) **Fmt))
+ {
+ TempStrPtr = TempStr;
+ while (isdigit((unsigned char) **Fmt))
+ {
+ *TempStrPtr++ = **Fmt;
+ *Out++ = *(*Fmt)++;
+ }
+ *TempStrPtr = 0; /* end string */
+ *Precision = atoi(TempStr);
+ if (*Precision < 0)
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ return Result;
+ }
+ }
+ else if (**Fmt == '*')
+ {
+ *Precision = PRECISION_STAR;
+ *Out++ = *(*Fmt)++;
+ if (isdigit((unsigned char) **Fmt))
+ {
+ /* this is an invalid precision because we have a .* then a
+ number */
+ /* printf handles this by just printing the whole string */
+ *Precision = PRECISION_INVALID;
+ while (isdigit((unsigned char) **Fmt))
+ {
+ *Out++ = *(*Fmt)++;
+ }
+ }
+ }
+ else
+ {
+ *Precision = PRECISION_DOT;
+ }
+ }
+
+#ifdef BIT64
+ if (**Fmt == 'p')
+ {
+ *Prefix = PFF_PREFIX_LONGLONG;
+ }
+#endif
+ /* grab prefix of 'I64' for __int64 */
+ if ((*Fmt)[0] == 'I' && (*Fmt)[1] == '6' && (*Fmt)[2] == '4')
+ {
+ /* convert to 'll' so BSD's snprintf can handle it */
+ *Fmt += 3;
+ *Prefix = PFF_PREFIX_LONGLONG;
+ }
+ /* grab a prefix of 'h' */
+ else if (**Fmt == 'h')
+ {
+ *Prefix = PFF_PREFIX_SHORT;
+ ++(*Fmt);
+ }
+ /* grab prefix of 'l' or the undocumented 'w' (at least in MSDN) */
+ else if (**Fmt == 'l' || **Fmt == 'w')
+ {
+ ++(*Fmt);
+#ifdef BIT64
+ // Only want to change the prefix on 64 bit when printing characters.
+ if (**Fmt == 'c' || **Fmt == 's')
+#endif
+ {
+ *Prefix = PFF_PREFIX_LONG;
+ }
+ }
+ else if (**Fmt == 'L')
+ {
+ /* a prefix of 'L' seems to be ignored */
+ ++(*Fmt);
+ }
+
+ /* grab type 'c' */
+ if (**Fmt == 'c' || **Fmt == 'C')
+ {
+ *Type = PFF_TYPE_CHAR;
+ if (*Prefix != PFF_PREFIX_SHORT && **Fmt == 'C')
+ {
+ *Prefix = PFF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (*Prefix == PFF_PREFIX_LONG)
+ {
+ *Out++ = 'l';
+ }
+ *Out++ = 'c';
+ ++(*Fmt);
+ Result = TRUE;
+ }
+ /* grab type 's' */
+ else if (**Fmt == 's' || **Fmt == 'S')
+ {
+ *Type = PFF_TYPE_STRING;
+ if (*Prefix != PFF_PREFIX_SHORT && **Fmt == 'S')
+ {
+ *Prefix = PFF_PREFIX_LONG; /* give it a wide prefix */
+ }
+ if (*Prefix == PFF_PREFIX_LONG)
+ {
+ *Out++ = 'l';
+ }
+ *Out++ = 's';
+ ++(*Fmt);
+ Result = TRUE;
+ }
+ /* grab int types types */
+ else if (**Fmt == 'd' || **Fmt == 'i' || **Fmt == 'o' ||
+ **Fmt == 'u' || **Fmt == 'x' || **Fmt == 'X')
+ {
+ *Type = PFF_TYPE_INT;
+ if (*Prefix == PFF_PREFIX_SHORT)
+ {
+ *Out++ = 'h';
+ }
+ else if (*Prefix == PFF_PREFIX_LONG)
+ {
+ *Out++ = 'l';
+ }
+ else if (*Prefix == PFF_PREFIX_LONGLONG)
+ {
+ *Out++ = 'l';
+ *Out++ = 'l';
+ }
+ *Out++ = *(*Fmt)++;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'e' || **Fmt == 'E' || **Fmt == 'f' ||
+ **Fmt == 'g' || **Fmt == 'G')
+ {
+ /* we can safely ignore the prefixes and only add the type*/
+ *Type = PFF_TYPE_FLOAT;
+ *Out++ = *(*Fmt)++;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'n')
+ {
+ if (*Prefix == PFF_PREFIX_SHORT)
+ {
+ *Out++ = 'h';
+ }
+ *Out++ = *(*Fmt)++;
+ *Type = PFF_TYPE_N;
+ Result = TRUE;
+ }
+ else if (**Fmt == 'p')
+ {
+ *Type = PFF_TYPE_P;
+ *Out++ = *(*Fmt)++;
+
+ if (*Prefix == PFF_PREFIX_LONGLONG)
+ {
+ if (*Precision == PRECISION_DEFAULT)
+ {
+ *Precision = 16;
+ }
+ }
+ else
+ {
+ if (*Precision == PRECISION_DEFAULT)
+ {
+ *Precision = 8;
+ }
+ }
+ Result = TRUE;
+ }
+
+ *Out = 0; /* end the string */
+ PAL_free(TempStr);
+ return Result;
+}
+
+/*******************************************************************************
+Function:
+ AddPaddingVfprintf (silent version)
+ see Internal_AddPaddingVfprintf in printf.c
+*******************************************************************************/
+INT Silent_AddPaddingVfprintf(PAL_FILE *stream, LPSTR In, INT Padding, INT Flags)
+{
+ LPSTR Out;
+ INT LengthInStr;
+ INT Length;
+ LPSTR OutOriginal;
+ INT Written;
+
+ LengthInStr = strlen(In);
+ Length = LengthInStr;
+
+
+ if (Padding > 0)
+ {
+ Length += Padding;
+ }
+ Out = (LPSTR) PAL_malloc(Length+1);
+ int iLen = Length+1;
+ if (!Out)
+ {
+ return -1;
+ }
+ OutOriginal = Out;
+
+ if (Flags & PFF_MINUS) /* pad on right */
+ {
+ if (strcpy_s(Out, iLen, In) != SAFECRT_SUCCESS)
+ {
+ Written = -1;
+ goto Done;
+ }
+
+ Out += LengthInStr;
+ iLen -= LengthInStr;
+ }
+ if (Padding > 0)
+ {
+ iLen -= Padding;
+ if (Flags & PFF_ZERO) /* '0', pad with zeros */
+ {
+ while (Padding--)
+ {
+ *Out++ = '0';
+ }
+ }
+ else /* pad with spaces */
+ {
+ while (Padding--)
+ {
+ *Out++ = ' ';
+ }
+ }
+ }
+ if (!(Flags & PFF_MINUS)) /* put 'In' after padding */
+ {
+ if (strcpy_s(Out, Length+1, In) != SAFECRT_SUCCESS)
+ {
+ Written = -1;
+ goto Done;
+ }
+
+ Out += LengthInStr;
+ iLen -= LengthInStr;
+ }
+
+#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
+ clearerr((FILE*)stream);
+#endif
+
+ Written = PAL_fwrite(OutOriginal, 1, Length, stream);
+ if (stream->PALferrorCode == PAL_FILE_ERROR)
+ {
+ Written = -1;
+ }
+
+Done:
+ PAL_free(OutOriginal);
+ return Written;
+}
+
+/*++
+Function:
+ PAL_wcslen (silent version)
+
+See MSDN or the man page for wcslen.
+
+--*/
+size_t Silent_PAL_wcslen(const wchar_16 *string)
+{
+ size_t nChar = 0;
+
+ if ( !string )
+ {
+ return 0;
+ }
+ while (*string++)
+ {
+ nChar++;
+ }
+
+ return nChar;
+}
diff --git a/src/pal/src/cruntime/string.cpp b/src/pal/src/cruntime/string.cpp
new file mode 100644
index 0000000000..23781d8b39
--- /dev/null
+++ b/src/pal/src/cruntime/string.cpp
@@ -0,0 +1,348 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ string.cpp
+
+Abstract:
+
+ Implementation of the string functions in the C runtime library that are Windows specific.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/cruntime.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <pthread.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+/*++
+Function:
+ _strnicmp
+
+compare at most count characters from two strings, ignoring case
+
+The strnicmp() function compares, with case insensitivity, at most count
+characters from s1 to s2. All uppercase characters from s1 and s2 are
+mapped to lowercase for the purposes of doing the comparison.
+
+Returns:
+
+Value Meaning
+
+< 0 s1 is less than s2
+0 s1 is equal to s2
+> 0 s1 is greater than s2
+
+--*/
+int
+__cdecl
+_strnicmp( const char *s1, const char *s2, size_t count )
+{
+ int ret;
+
+ PERF_ENTRY(_strnicmp);
+ ENTRY("_strnicmp (s1=%p (%s), s2=%p (%s), count=%d)\n", s1?s1:"NULL", s1?s1:"NULL", s2?s2:"NULL", s2?s2:"NULL", count);
+
+ ret = strncasecmp(s1, s2, count );
+
+ LOGEXIT("_strnicmp returning int %d\n", ret);
+ PERF_EXIT(_strnicmp);
+ return ret;
+}
+
+/*++
+Function:
+ _stricmp
+
+compare two strings, ignoring case
+
+The stricmp() function compares, with case insensitivity, the string
+pointed to by s1 to the string pointed to by s2. All uppercase
+characters from s1 and s2 are mapped to lowercase for the purposes of
+doing the comparison.
+
+Returns:
+
+Value Meaning
+
+< 0 s1 is less than s2
+0 s1 is equal to s2
+> 0 s1 is greater than s2
+
+--*/
+int
+__cdecl
+_stricmp(
+ const char *s1,
+ const char *s2)
+{
+ int ret;
+
+ PERF_ENTRY(_stricmp);
+ ENTRY("_stricmp (s1=%p (%s), s2=%p (%s))\n", s1?s1:"NULL", s1?s1:"NULL", s2?s2:"NULL", s2?s2:"NULL");
+
+ ret = strcasecmp(s1, s2);
+
+ LOGEXIT("_stricmp returning int %d\n", ret);
+ PERF_EXIT(_stricmp);
+ return ret;
+}
+
+
+/*++
+Function:
+ _strlwr
+
+Convert a string to lowercase.
+
+
+This function returns a pointer to the converted string. Because the
+modification is done in place, the pointer returned is the same as the
+pointer passed as the input argument. No return value is reserved to
+indicate an error.
+
+Parameter
+
+string Null-terminated string to convert to lowercase
+
+Remarks
+
+The _strlwr function converts any uppercase letters in string to
+lowercase as determined by the LC_CTYPE category setting of the
+current locale. Other characters are not affected. For more
+information on LC_CTYPE, see setlocale.
+
+--*/
+char *
+__cdecl
+_strlwr(
+ char *str)
+{
+ char *orig = str;
+
+ PERF_ENTRY(_strlwr);
+ ENTRY("_strlwr (str=%p (%s))\n", str?str:"NULL", str?str:"NULL");
+
+ while (*str)
+ {
+ *str = tolower(*str);
+ str++;
+ }
+
+ LOGEXIT("_strlwr returning char* %p (%s)\n", orig?orig:"NULL", orig?orig:"NULL");
+ PERF_EXIT(_strlwr);
+ return orig;
+}
+
+
+/*++
+Function:
+ _swab
+
+Swaps bytes.
+
+Return Value
+
+None
+
+Parameters
+
+src Data to be copied and swapped
+dest Storage location for swapped data
+n Number of bytes to be copied and swapped
+
+Remarks
+
+The _swab function copies n bytes from src, swaps each pair of
+adjacent bytes, and stores the result at dest. The integer n should be
+an even number to allow for swapping. _swab is typically used to
+prepare binary data for transfer to a machine that uses a different
+byte order.
+
+Example
+
+char from[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+char to[] = "..........................";
+
+printf("Before:\n%s\n%s\n\n", from, to);
+_swab(from, to, strlen(from));
+printf("After:\n%s\n%s\n\n", from, to);
+
+Before:
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+..........................
+
+After:
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+BADCFEHGJILKNMPORQTSVUXWZY
+
+--*/
+void
+__cdecl
+_swab(char *src, char *dest, int n)
+{
+ PERF_ENTRY(_swab);
+ ENTRY("_swab (src=%p (%s), dest=%p (%s), n=%d)\n", src?src:"NULL", src?src:"NULL", dest?dest:"NULL", dest?dest:"NULL", n);
+ swab(src, dest, n);
+ LOGEXIT("_swab returning\n");
+ PERF_EXIT(_swab);
+}
+
+
+/*++
+Function:
+ PAL_strtoul
+
+Convert string to an unsigned long-integer value.
+
+Return Value
+
+strtoul returns the converted value, if any, or ULONG_MAX on
+overflow. It returns 0 if no conversion can be performed. errno is
+set to ERANGE if overflow or underflow occurs.
+
+Parameters
+
+szNumber Null-terminated string to convert to a ULONG
+pszEnd Pointer to character that stops scan
+nBase Number base to use
+
+Remarks
+
+strtoul stops reading the string szNumber at the first character it cannot
+recognize as part of a number. This may be the terminating null
+character, or it may be the first numeric character greater than or
+equal to base. The LC_NUMERIC category setting of the current locale
+determines recognition of the radix character in szNumber; for more
+information, see setlocale. If pszEnd is not NULL, a pointer to the
+character that stopped the scan is stored at the location pointed to
+by pszEnd. If no conversion can be performed (no valid digits were
+found or an invalid base was specified), the value of szNumber is stored
+at the location pointed to by pszEnd.
+
+Notes :
+ MSDN states that only space and tab are accepted as leading whitespace, but
+ tests indicate that other whitespace characters (newline, carriage return,
+ etc) are also accepted. This matches the behavior on Unix systems.
+
+ For strtoul, we need to check if the value to be returned
+ is outside the 32 bit range. If so, the returned value needs to be set
+ as appropriate, according to the MSDN pages and in all instances errno
+ must be set to ERANGE (The one exception is converting a string
+ representing a negative value to unsigned long).
+ Note that on 64 bit Windows, long's are still 32 bit. Thus, to match
+ Windows behavior, we must return long's in the 32 bit range.
+ --*/
+
+/* The use of ULONG is by design, to ensure that a 32 bit value is always
+returned from this function. If "unsigned long" is used instead of ULONG,
+then a 64 bit value could be returned on 64 bit platforms like HP-UX, thus
+breaking Windows behavior. */
+ULONG
+__cdecl
+PAL_strtoul(const char *szNumber, char **pszEnd, int nBase)
+{
+ unsigned long ulResult;
+
+ PERF_ENTRY(strtoul);
+ ENTRY("strtoul (szNumber=%p (%s), pszEnd=%p, nBase=%d)\n",
+ szNumber?szNumber:"NULL",
+ szNumber?szNumber:"NULL",
+ pszEnd,
+ nBase);
+
+ ulResult = strtoul(szNumber, pszEnd, nBase);
+
+#ifdef BIT64
+ if (ulResult > _UI32_MAX)
+ {
+ char ch = *szNumber;
+ while (isspace(ch))
+ {
+ ch = *szNumber++;
+ }
+ /* If the string represents a positive number that is greater than
+ _UI32_MAX, set errno to ERANGE. Otherwise, don't set errno
+ to match Windows behavior. */
+ if (ch != '-')
+ {
+ ulResult = _UI32_MAX;
+ errno = ERANGE;
+ }
+ }
+#endif
+
+ LOGEXIT("strtoul returning unsigned long %lu\n", ulResult);
+ PERF_EXIT(wcstoul);
+
+ /* When returning unsigned long res from this function, it will be
+ implicitly cast to ULONG. This handles situations where a string that
+ represents a negative number is passed in to strtoul. The Windows
+ behavior is analogous to taking the binary equivalent of the negative
+ value and treating it as a positive number. Returning a ULONG from
+ this function, as opposed to native unsigned long, allows us to match
+ this behavior. The explicit cast to ULONG below is used to silence any
+ potential warnings due to the implicit casting. */
+ return (ULONG)ulResult;
+
+}
+
+
+/*++
+Function:
+ PAL_atol
+
+Convert string to a long value.
+
+Return Value
+
+atol returns the converted value, if any. In the case of overflow,
+the return value is undefined.
+
+Parameters
+
+szNumber Null-terminated string to convert to a LONG
+--*/
+
+/* The use of LONG is by design, to ensure that a 32 bit value is always
+returned from this function. If "long" is used instead of LONG, then a 64 bit
+value could be returned on 64 bit platforms like HP-UX, thus breaking
+Windows behavior. */
+LONG
+__cdecl
+PAL_atol(const char *szNumber)
+{
+ long lResult;
+
+ PERF_ENTRY(atol);
+ ENTRY("atol (szNumber=%p (%s))\n",
+ szNumber?szNumber:"NULL"
+ );
+
+ lResult = atol(szNumber);
+
+ LOGEXIT("atol returning long %ld\n", (LONG)lResult);
+ PERF_EXIT(atol);
+ /* This explicit cast to LONG is used to silence any potential warnings
+ due to implicitly casting the native long lResult to LONG when returning. */
+ return (LONG)lResult;
+
+}
+
diff --git a/src/pal/src/cruntime/stringtls.cpp b/src/pal/src/cruntime/stringtls.cpp
new file mode 100644
index 0000000000..1504813f91
--- /dev/null
+++ b/src/pal/src/cruntime/stringtls.cpp
@@ -0,0 +1,78 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ stringtls.cpp
+
+Abstract:
+
+ Implementation of the string functions in the C runtime library that
+ are Windows specific and depend on per-thread data
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/dbgmsg.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <pthread.h>
+#include <limits.h>
+#include <unistd.h>
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+/*++
+Function:
+ PAL_strtok
+
+Finds the next token in a 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 cotaining token(s)
+strDelimit Set of delimiter characters
+
+Remarks:
+In FreeBSD, strtok is not re-entrant, strtok_r is. It manages re-entrancy
+by using a passed-in context pointer (which will be stored in thread local
+storage) According to the strtok MSDN documentation, "Calling these functions
+simultaneously from multiple threads does not have undesirable effects", so
+we need to use strtok_r.
+--*/
+char *
+__cdecl
+PAL_strtok(char *strToken, const char *strDelimit)
+{
+ CPalThread *pThread = NULL;
+ char *retval=NULL;
+
+ PERF_ENTRY(strtok);
+ ENTRY("strtok (strToken=%p (%s), strDelimit=%p (%s))\n",
+ strToken?strToken:"NULL",
+ strToken?strToken:"NULL", strDelimit?strDelimit:"NULL", strDelimit?strDelimit:"NULL");
+
+ pThread = InternalGetCurrentThread();
+
+ retval = strtok_r(strToken, strDelimit, &pThread->crtInfo.strtokContext);
+
+ LOGEXIT("strtok returns %p (%s)\n", retval?retval:"NULL", retval?retval:"NULL");
+ PERF_EXIT(strtok);
+
+ return retval;
+}
diff --git a/src/pal/src/cruntime/thread.cpp b/src/pal/src/cruntime/thread.cpp
new file mode 100644
index 0000000000..6c96b1b511
--- /dev/null
+++ b/src/pal/src/cruntime/thread.cpp
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ thread.c
+
+Abstract:
+
+ Implementation of the threads/process functions in the C runtime library
+ that are Windows specific.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/init.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+void
+PAL_exit(int status)
+{
+ PERF_ENTRY(exit);
+ ENTRY ("exit(status=%d)\n", status);
+
+ /* should also clean up any resources allocated by pal/cruntime, if any */
+ ExitProcess(status);
+
+ LOGEXIT ("exit returns void");
+ PERF_EXIT(exit);
+}
+
+int
+PAL_atexit(void (__cdecl *function)(void))
+{
+ int ret;
+
+ PERF_ENTRY(atexit);
+ ENTRY ("atexit(function=%p)\n", function);
+ ret = atexit(function);
+ LOGEXIT ("atexit returns int %d", ret);
+ PERF_EXIT(atexit);
+ return ret;
+}
diff --git a/src/pal/src/cruntime/wchar.cpp b/src/pal/src/cruntime/wchar.cpp
new file mode 100644
index 0000000000..2d244a639f
--- /dev/null
+++ b/src/pal/src/cruntime/wchar.cpp
@@ -0,0 +1,1885 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ wchar.c
+
+Abstract:
+
+ Implementation of wide char string functions.
+
+
+
+--*/
+
+
+#include "pal/palinternal.h"
+#include "pal/cruntime.h"
+#include "pal/dbgmsg.h"
+#include "pal/unicode_data.h"
+
+#include "pal/thread.hpp"
+#include "pal/threadsusp.hpp"
+
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_COREFOUNDATION
+#define CF_EXCLUDE_CSTD_HEADERS
+#include <CoreFoundation/CoreFoundation.h>
+#include <wctype.h>
+#else
+#include <wctype.h>
+#endif
+
+#include <errno.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(CRT);
+
+
+/*--
+Function:
+ wtolower (internal)
+
+16-bit wide character version of the ANSI tolower() function.
+
+ --*/
+static
+wchar_16
+wtolower(wchar_16 c)
+{
+ /* Note: Surrogate pairs unicode character are not supported */
+
+#if HAVE_TOWLOWER
+
+ wchar_t w;
+ w = (wchar_t) c;
+ w = towlower(w);
+ return (wchar_16) w;
+
+#else
+
+ return PAL_towlower(c);
+
+#endif
+
+}
+
+/*******************************************************************************
+Function:
+ Internal_i64tow
+
+Parameters:
+ value
+ - INT64 value to be converted to a string
+ string
+ - out buffer to place interger string
+ radix
+ - numeric base to convert to
+ isI64
+ - TRUE if value is INT64, FALSE if value is a long
+
+Note:
+ - only a radix of ten (and value < 0) will result in a negative
+ sign in the output buffer
+*******************************************************************************/
+LPWSTR Internal_i64tow(INT64 value, LPWSTR string, int radix, BOOL isI64)
+{
+ int length = 0;
+ int n;
+ int r;
+ UINT64 uval = value;
+ LPWSTR stringPtr = string;
+ int start = 0;
+ int end;
+ WCHAR tempCh;
+
+ if (radix < 2 || radix > 36)
+ {
+ ASSERT( "Invalid radix, radix must be between 2 and 36\n" );
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return string;
+ }
+ if (FALSE == isI64)
+ {
+ uval = (ULONG) uval;
+ }
+ if (10 == radix && value < 0)
+ {
+ uval = value * -1;
+ }
+ if(0 == uval)
+ {
+ ++length;
+ *stringPtr++ = '0';
+ }
+ else while (uval > 0)
+ {
+ ++length;
+ n = uval / radix;
+ r = uval - (n * radix);
+ uval /= radix;
+ if (r > 9)
+ {
+ *stringPtr++ = r + 87;
+ }
+ else
+ {
+ *stringPtr++ = r + 48;
+ }
+ }
+ if (10 == radix && value < 0)
+ {
+ *stringPtr++ = '-';
+ ++length;
+ }
+ *stringPtr = 0; /* end the string */
+
+ /* reverse the string */
+ end = length - 1;
+ while (start < end)
+ {
+ tempCh = string[start];
+ string[start] = string[end];
+ string[end] = tempCh;
+ ++start;
+ --end;
+ }
+
+ return string;
+}
+
+/*--
+Function:
+ _itow
+
+16-bit wide character version of the ANSI tolower() function.
+
+ --*/
+wchar_16 *
+__cdecl
+_itow(
+ int value,
+ wchar_16 *string,
+ int radix)
+{
+ wchar_16 *ret;
+
+ PERF_ENTRY(_itow);
+ ENTRY("_itow (value=%d, string=%p, radix=%d)\n",
+ value, string, radix);
+
+ ret = Internal_i64tow(value, string, radix, FALSE);
+
+ LOGEXIT("_itow returns wchar_t* %p\n", ret);
+ PERF_EXIT(_itow);
+
+ return ret;
+}
+
+/*--
+Function:
+ _i64tow
+
+See MSDN doc
+--*/
+wchar_16 *
+ __cdecl
+_i64tow(
+ __int64 value,
+ wchar_16 *string,
+ int radix)
+{
+ wchar_16 *ret;
+
+ PERF_ENTRY(_i64tow);
+ ENTRY("_i64tow (value=%ld, string=%p, radix=%d)\n",
+ value, string, radix);
+
+ ret = Internal_i64tow(value, string, radix, TRUE);
+
+ LOGEXIT("_i64tow returns wchar_t* %p\n", ret);
+ PERF_EXIT(_i64tow);
+
+ return ret;
+}
+
+
+/*--
+Function:
+ _wtoi
+
+See MSDN doc
+--*/
+int
+__cdecl
+_wtoi(
+ const wchar_16 *string)
+{
+ int len;
+ int ret;
+ char *tempStr;
+
+ PERF_ENTRY(_wtoi);
+ ENTRY("_wtoi (string=%p)\n", string);
+
+ len = WideCharToMultiByte(CP_ACP, 0, string, -1, 0, 0, 0, 0);
+ if (!len)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ return -1;
+ }
+ tempStr = (char *) PAL_malloc(len);
+ if (!tempStr)
+ {
+ ERROR("PAL_malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return -1;
+ }
+ len = WideCharToMultiByte(CP_ACP, 0, string, -1, tempStr, len, 0, 0);
+ if (!len)
+ {
+ ASSERT("WideCharToMultiByte failed. Error is %d\n",
+ GetLastError());
+ PAL_free(tempStr);
+ return -1;
+ }
+ ret = atoi(tempStr);
+
+ PAL_free(tempStr);
+ LOGEXIT("_wtoi returns int %d\n", ret);
+ PERF_EXIT(_wtoi);
+ return ret;
+}
+
+
+/*--
+Function:
+ PAL_iswspace
+
+See MSDN doc
+--*/
+int
+__cdecl
+PAL_iswspace(wchar_16 c)
+{
+ int ret;
+
+ PERF_ENTRY(iswspace);
+ ENTRY("PAL_iswspace (c=%C)\n", c);
+
+ ret = iswspace(c);
+
+ LOGEXIT("PAL_iswspace returns int %d\n", ret);
+ PERF_EXIT(iswspace);
+ return ret;
+}
+
+/*++
+Function:
+ _wcsnicmp
+
+Compare characters of two strings without regard to case
+
+Return Value
+
+The return value indicates the relationship between the substrings as follows.
+
+Return Value
+
+Description
+
+< 0 string1 substring less than string2 substring
+ 0 string1 substring identical to string2 substring
+> 0 string1 substring greater than string2 substring
+
+Parameters
+
+string1, string2 Null-terminated strings to compare
+count Number of characters to compare
+
+Remarks
+
+The _strnicmp function lexicographically compares, at most, the first
+count characters of string1 and string2. The comparison is performed
+without regard to case; _strnicmp is a case-insensitive version of
+strncmp. The comparison ends if a terminating null character is
+reached in either string before count characters are compared. If the
+strings are equal when a terminating null character is reached in
+either string before count characters are compared, the shorter string
+is lesser.
+
+--*/
+int
+__cdecl
+_wcsnicmp(
+ const wchar_16 *string1,
+ const wchar_16 *string2,
+ size_t count)
+{
+ size_t i;
+ int diff = 0;
+
+ PERF_ENTRY(_wcsnicmp);
+ ENTRY("_wcsnicmp (string1=%p (%S), string2=%p (%S), count=%lu)\n",
+ string1?string1:W16_NULLSTRING,
+ string1?string1:W16_NULLSTRING, string2?string2:W16_NULLSTRING, string2?string2:W16_NULLSTRING,
+ (unsigned long) count);
+
+ for (i = 0; i < count; i++)
+ {
+ diff = wtolower(string1[i]) - wtolower(string2[i]);
+ if (diff != 0 || 0 == string1[i] || 0 == string2[i])
+ {
+ break;
+ }
+ }
+ LOGEXIT("_wcsnicmp returning int %d\n", diff);
+ PERF_EXIT(_wcsnicmp);
+ return diff;
+}
+
+/*++
+Function:
+ _wcsicmp
+
+Compare characters of two strings without regard to case
+
+Return Value
+
+The return value indicates the relationship between the substrings as follows.
+
+Return Value
+
+Description
+
+< 0 string1 substring less than string2 substring
+ 0 string1 substring identical to string2 substring
+> 0 string1 substring greater than string2 substring
+
+Parameters
+
+string1, string2 Null-terminated strings to compare
+
+--*/
+int
+__cdecl
+_wcsicmp(
+ const wchar_16 *string1,
+ const wchar_16 *string2)
+{
+ int ret;
+
+ PERF_ENTRY(_wcsicmp);
+ ENTRY("_wcsicmp (string1=%p (%S), string2=%p (%S))\n",
+ string1?string1:W16_NULLSTRING,
+ string1?string1:W16_NULLSTRING, string2?string2:W16_NULLSTRING, string2?string2:W16_NULLSTRING);
+
+ ret = _wcsnicmp(string1, string2, 0x7fffffff);
+
+ LOGEXIT("_wcsnicmp returns int %d\n", ret);
+ PERF_EXIT(_wcsicmp);
+ return ret;
+}
+
+
+/*++
+Function:
+ _wcslwr
+
+Convert a string to lowercase.
+
+Return Value
+
+Returns a pointer to the converted string. Because the modification is
+done in place, the pointer returned is the same as the pointer passed
+as the input argument. No return value is reserved to indicate an
+error.
+
+Parameter
+
+string Null-terminated string to convert to lowercase
+
+Remarks
+
+--*/
+wchar_16 *
+__cdecl
+_wcslwr(
+ wchar_16 *string)
+{
+ int i;
+
+ PERF_ENTRY(_wcslwr);
+ ENTRY("_wcslwr (string=%p (%S))\n", string?string:W16_NULLSTRING, string?string:W16_NULLSTRING);
+
+ for (i=0 ; string[i] != 0; i++)
+ {
+ string[i] = wtolower(string[i]);
+ }
+
+ LOGEXIT("_wcslwr returning wchar_t %p (%S)\n", string?string:W16_NULLSTRING, string?string:W16_NULLSTRING);
+ PERF_EXIT(_wcslwr);
+ return string;
+}
+
+
+/*++
+Function:
+ PAL_wcstol
+
+Convert string to a long-integer value.
+
+Return Value
+
+wcstol returns the value represented in the string nptr, except when
+the representation would cause an overflow, in which case it returns
+LONG_MAX or LONG_MIN. strtol returns 0 if no conversion can be
+performed. errno is set to ERANGE if overflow or underflow occurs.
+
+Parameters
+
+nptr Null-terminated string to convert
+endptr Pointer to character that stops scan
+base Number base to use
+
+Remarks
+
+The wcstol function converts nptr to a long. It stops reading the
+string nptr at the first character it cannot recognize as part of a
+number. This may be the terminating null character, or it may be the
+first numeric character greater than or equal to base.
+
+Notes :
+ MSDN states that only space and tab are accepted as leading whitespace, but
+ tests indicate that other whitespace characters (newline, carriage return,
+ etc) are also accepted. This matches the behavior on Unix systems.
+
+ For wcstol and wcstoul, we need to check if the value to be returned
+ is outside the 32 bit range. If so, the returned value needs to be set
+ as appropriate, according to the MSDN pages for wcstol and wcstoul,
+ and in all instances errno must be set to ERANGE (The one exception
+ is converting a string representing a negative value to unsigned long).
+ Note that on 64 bit Windows, long's are still 32 bit. Thus, to match
+ Windows behavior, we must return long's in the 32 bit range.
+--*/
+
+/* The use of LONG is by design, to ensure that a 32 bit value is always
+returned from this function. If "long" is used instead of LONG, then a 64 bit
+value could be returned on 64 bit platforms like HP-UX, thus breaking
+Windows behavior. */
+LONG
+__cdecl
+PAL_wcstol(
+ const wchar_16 *nptr,
+ wchar_16 **endptr,
+ int base)
+{
+ char *s_nptr = 0;
+ char *s_endptr = 0;
+ long res;
+ int size;
+ DWORD dwLastError = 0;
+
+ PERF_ENTRY(wcstol);
+ ENTRY("wcstol (nptr=%p (%S), endptr=%p, base=%d)\n", nptr?nptr:W16_NULLSTRING, nptr?nptr:W16_NULLSTRING,
+ endptr, base);
+
+ size = WideCharToMultiByte(CP_ACP, 0, nptr, -1, NULL, 0, NULL, NULL);
+ if (!size)
+ {
+ dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failed. Error is %d\n", dwLastError);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ res = 0;
+ goto PAL_wcstolExit;
+ }
+ s_nptr = (char *)PAL_malloc(size);
+ if (!s_nptr)
+ {
+ ERROR("PAL_malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ res = 0;
+ goto PAL_wcstolExit;
+ }
+ size = WideCharToMultiByte(CP_ACP, 0, nptr, -1, s_nptr, size, NULL, NULL);
+ if( size==0 )
+ {
+ dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failed. Error is %d\n", dwLastError);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ res = 0;
+ goto PAL_wcstolExit;
+ }
+
+ res = strtol(s_nptr, &s_endptr, base);
+
+#ifdef BIT64
+ if (res > _I32_MAX)
+ {
+ res = _I32_MAX;
+ errno = ERANGE;
+ }
+ else if (res < _I32_MIN)
+ {
+ res = _I32_MIN;
+ errno = ERANGE;
+ }
+#endif
+
+ /* only ASCII characters will be accepted by strtol, and those always get
+ mapped to single-byte characters, so the first rejected character will
+ have the same index in the multibyte and widechar strings */
+ if( endptr )
+ {
+ size = s_endptr - s_nptr;
+ *endptr = (wchar_16 *)&nptr[size];
+ }
+
+PAL_wcstolExit:
+ PAL_free(s_nptr);
+ LOGEXIT("wcstol returning long %ld\n", res);
+ PERF_EXIT(wcstol);
+ /* This explicit cast to LONG is used to silence any potential warnings
+ due to implicitly casting the native long res to LONG when returning. */
+ return (LONG)res;
+}
+
+
+/*++
+Function:
+ PAL_wcstoul
+
+Convert string to an unsigned long-integer value.
+
+Return Value
+
+wcstoul returns the converted value, if any, or ULONG_MAX on
+overflow. It returns 0 if no conversion can be performed. errno is
+set to ERANGE if overflow or underflow occurs.
+
+Parameters
+
+nptr Null-terminated string to convert
+endptr Pointer to character that stops scan
+base Number base to use
+
+Remarks
+
+wcstoul stops reading the string nptr at the first character it cannot
+recognize as part of a number. This may be the terminating null
+character, or it may be the first numeric character greater than or
+equal to base. The LC_NUMERIC category setting of the current locale
+determines recognition of the radix character in nptr; for more
+information, see setlocale. If endptr is not NULL, a pointer to the
+character that stopped the scan is stored at the location pointed to
+by endptr. If no conversion can be performed (no valid digits were
+found or an invalid base was specified), the value of nptr is stored
+at the location pointed to by endptr.
+
+Notes :
+ MSDN states that only space and tab are accepted as leading whitespace, but
+ tests indicate that other whitespace characters (newline, carriage return,
+ etc) are also accepted. This matches the behavior on Unix systems.
+
+ For wcstol and wcstoul, we need to check if the value to be returned
+ is outside the 32 bit range. If so, the returned value needs to be set
+ as appropriate, according to the MSDN pages for wcstol and wcstoul,
+ and in all instances errno must be set to ERANGE (The one exception
+ is converting a string representing a negative value to unsigned long).
+ Note that on 64 bit Windows, long's are still 32 bit. Thus, to match
+ Windows behavior, we must return long's in the 32 bit range.
+--*/
+
+/* The use of ULONG is by design, to ensure that a 32 bit value is always
+returned from this function. If "unsigned long" is used instead of ULONG,
+then a 64 bit value could be returned on 64 bit platforms like HP-UX, thus
+breaking Windows behavior .*/
+ULONG
+__cdecl
+PAL_wcstoul(
+ const wchar_16 *nptr,
+ wchar_16 **endptr,
+ int base)
+{
+ char *s_nptr = 0;
+ char *s_endptr = 0;
+ unsigned long res;
+ int size;
+ DWORD dwLastError = 0;
+
+ PERF_ENTRY(wcstoul);
+ ENTRY("wcstoul (nptr=%p (%S), endptr=%p, base=%d)\n", nptr?nptr:W16_NULLSTRING, nptr?nptr:W16_NULLSTRING,
+ endptr, base);
+
+ size = WideCharToMultiByte(CP_ACP, 0, nptr, -1, NULL, 0, NULL, NULL);
+ if (!size)
+ {
+ dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failed. Error is %d\n", dwLastError);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ res = 0;
+ goto PAL_wcstoulExit;
+ }
+ s_nptr = (char *)PAL_malloc(size);
+ if (!s_nptr)
+ {
+ ERROR("PAL_malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ res = 0;
+ goto PAL_wcstoulExit;
+ }
+ size = WideCharToMultiByte(CP_ACP, 0, nptr, -1, s_nptr, size, NULL, NULL);
+ if (!size)
+ {
+ dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failed. Error is %d\n", dwLastError);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ res = 0;
+ goto PAL_wcstoulExit;
+ }
+
+ res = strtoul(s_nptr, &s_endptr, base);
+
+#ifdef BIT64
+ if (res > _UI32_MAX)
+ {
+ wchar_16 wc = *nptr;
+ while (PAL_iswspace(wc))
+ {
+ wc = *nptr++;
+ }
+ /* If the string represents a positive number that is greater than
+ _UI32_MAX, set errno to ERANGE. Otherwise, don't set errno
+ to match Windows behavior. */
+ if (wc != '-')
+ {
+ res = _UI32_MAX;
+ errno = ERANGE;
+ }
+ }
+#endif
+
+ /* only ASCII characters will be accepted by strtol, and those always get
+ mapped to single-byte characters, so the first rejected character will
+ have the same index in the multibyte and widechar strings */
+ if( endptr )
+ {
+ size = s_endptr - s_nptr;
+ *endptr = (wchar_16 *)&nptr[size];
+ }
+
+PAL_wcstoulExit:
+ PAL_free(s_nptr);
+ LOGEXIT("wcstoul returning unsigned long %lu\n", res);
+ PERF_EXIT(wcstoul);
+
+ /* When returning unsigned long res from this function, it will be
+ implicitly cast to ULONG. This handles situations where a string that
+ represents a negative number is passed in to wcstoul. The Windows
+ behavior is analogous to taking the binary equivalent of the negative
+ value and treating it as a positive number. Returning a ULONG from
+ this function, as opposed to native unsigned long, allows us to match
+ this behavior. The explicit case to ULONG below is used to silence any
+ potential warnings due to the implicit casting. */
+ return (ULONG)res;
+}
+
+ULONGLONG
+__cdecl
+PAL__wcstoui64(
+ const wchar_16 *nptr,
+ wchar_16 **endptr,
+ int base)
+{
+ char *s_nptr = 0;
+ char *s_endptr = 0;
+ unsigned long long res;
+ int size;
+ DWORD dwLastError = 0;
+
+ PERF_ENTRY(wcstoul);
+ ENTRY("_wcstoui64 (nptr=%p (%S), endptr=%p, base=%d)\n", nptr?nptr:W16_NULLSTRING, nptr?nptr:W16_NULLSTRING,
+ endptr, base);
+
+ size = WideCharToMultiByte(CP_ACP, 0, nptr, -1, NULL, 0, NULL, NULL);
+ if (!size)
+ {
+ dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failed. Error is %d\n", dwLastError);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ res = 0;
+ goto PAL__wcstoui64Exit;
+ }
+ s_nptr = (char *)PAL_malloc(size);
+ if (!s_nptr)
+ {
+ ERROR("PAL_malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ res = 0;
+ goto PAL__wcstoui64Exit;
+ }
+ size = WideCharToMultiByte(CP_ACP, 0, nptr, -1, s_nptr, size, NULL, NULL);
+ if (!size)
+ {
+ dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failed. Error is %d\n", dwLastError);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ res = 0;
+ goto PAL__wcstoui64Exit;
+ }
+
+ res = strtoull(s_nptr, &s_endptr, base);
+
+ /* only ASCII characters will be accepted by strtoull, and those always get
+ mapped to single-byte characters, so the first rejected character will
+ have the same index in the multibyte and widechar strings */
+ if( endptr )
+ {
+ size = s_endptr - s_nptr;
+ *endptr = (wchar_16 *)&nptr[size];
+ }
+
+PAL__wcstoui64Exit:
+ PAL_free(s_nptr);
+ LOGEXIT("_wcstoui64 returning unsigned long long %llu\n", res);
+ PERF_EXIT(_wcstoui64);
+
+ return res;
+}
+
+/*++
+Function:
+ PAL_towlower
+
+See MSDN
+
+--*/
+wchar_16
+__cdecl
+PAL_towlower( wchar_16 c )
+{
+#if HAVE_COREFOUNDATION
+ PERF_ENTRY(towlower);
+ ENTRY("towlower (c=%d)\n", c);
+ if (!PAL_iswlower(c))
+ {
+ CFMutableStringRef cfString = CFStringCreateMutable(
+ kCFAllocatorDefault, 1);
+ if (cfString != NULL)
+ {
+ CFStringAppendCharacters(cfString, (const UniChar*)&c, 1);
+ CFStringLowercase(cfString, NULL);
+ c = CFStringGetCharacterAtIndex(cfString, 0);
+ CFRelease(cfString);
+ }
+ }
+ LOGEXIT("towlower returns int %d\n", c );
+ PERF_EXIT(towlower);
+ return c;
+#else /* HAVE_COREFOUNDATION */
+ UnicodeDataRec dataRec;
+
+ PERF_ENTRY(towlower);
+ ENTRY("towlower (c=%d)\n", c);
+
+ if (!GetUnicodeData(c, &dataRec))
+ {
+ TRACE( "Unable to retrieve unicode data for the character %c.\n", c );
+ LOGEXIT("towlower returns int %d\n", c );
+ PERF_EXIT(towlower);
+ return c;
+ }
+
+ if ( (dataRec.C1_TYPE_FLAGS & C1_LOWER) || (dataRec.nOpposingCase == 0 ))
+ {
+ LOGEXIT("towlower returns int %d\n", c );
+ PERF_EXIT(towlower);
+ return c;
+ }
+ else
+ {
+ LOGEXIT("towlower returns int %d\n", dataRec.nOpposingCase );
+ PERF_EXIT(towlower);
+ return dataRec.nOpposingCase;
+ }
+#endif /* HAVE_COREFOUNDATION */
+}
+
+
+/*++
+Function:
+ PAL_towupper
+
+See MSDN
+
+--*/
+wchar_16
+__cdecl
+PAL_towupper( wchar_16 c )
+{
+#if HAVE_COREFOUNDATION
+ PERF_ENTRY(towupper);
+ ENTRY("towupper (c=%d)\n", c);
+ if (!PAL_iswupper(c))
+ {
+ CFMutableStringRef cfString = CFStringCreateMutable(
+ kCFAllocatorDefault, 1);
+ if (cfString != NULL)
+ {
+ CFStringAppendCharacters(cfString, (const UniChar*)&c, 1);
+ CFStringUppercase(cfString, NULL);
+ c = CFStringGetCharacterAtIndex(cfString, 0);
+ CFRelease(cfString);
+ }
+ }
+ LOGEXIT("towupper returns int %d\n", c );
+ PERF_EXIT(towupper);
+ return c;
+#else /* HAVE_COREFOUNDATION */
+ UnicodeDataRec dataRec;
+
+ PERF_ENTRY(towupper);
+ ENTRY("towupper (c=%d)\n", c);
+
+ if (!GetUnicodeData(c, &dataRec))
+ {
+ TRACE( "Unable to retrieve unicode data for the character %c.\n", c );
+ LOGEXIT("towupper returns int %d\n", c );
+ PERF_EXIT(towupper);
+ return c;
+ }
+
+ if ( (dataRec.C1_TYPE_FLAGS & C1_UPPER) || (dataRec.nOpposingCase == 0 ))
+ {
+ LOGEXIT("towupper returns int %d\n", c );
+ PERF_EXIT(towupper);
+ return c;
+ }
+ else
+ {
+ LOGEXIT("towupper returns int %d\n", dataRec.nOpposingCase );
+ PERF_EXIT(towupper);
+ return dataRec.nOpposingCase;
+ }
+#endif /* HAVE_COREFOUNDATION */
+}
+
+/*++
+Function:
+ PAL_iswupper
+
+See MSDN
+
+--*/
+int
+__cdecl
+PAL_iswupper( wchar_16 c )
+{
+ BOOL bRetVal = FALSE;
+#if HAVE_COREFOUNDATION
+ static CFCharacterSetRef sUppercaseSet;
+
+ if (sUppercaseSet == NULL)
+ {
+ sUppercaseSet = CFCharacterSetGetPredefined(
+ kCFCharacterSetUppercaseLetter);
+ }
+ PERF_ENTRY(iswupper);
+ ENTRY( "iswupper (c=%d)\n", c );
+ bRetVal = CFCharacterSetIsCharacterMember(sUppercaseSet, c);
+#else /* HAVE_COREFOUNDATION */
+ UnicodeDataRec dataRec;
+
+ PERF_ENTRY(iswupper);
+ ENTRY( "iswupper (c=%d)\n", c );
+
+ if (!GetUnicodeData(c, &dataRec))
+ {
+ TRACE( "Unable to retrieve unicode data for the character %c.\n", c );
+ goto exit;
+ }
+
+ if (dataRec.C1_TYPE_FLAGS & C1_UPPER)
+ {
+ bRetVal = TRUE;
+ }
+exit:
+#endif /* HAVE_COREFOUNDATION */
+ LOGEXIT( "iswupper returns %s.\n", bRetVal == TRUE ? "TRUE" : "FALSE" );
+ PERF_EXIT(iswupper);
+ return bRetVal;
+}
+
+/*++
+Function:
+ PAL_iswlower
+
+See MSDN
+
+--*/
+int
+__cdecl
+PAL_iswlower( wchar_16 c )
+{
+ BOOL bRetVal = FALSE;
+#if HAVE_COREFOUNDATION
+ static CFCharacterSetRef sLowercaseSet;
+
+ if (sLowercaseSet == NULL)
+ {
+ sLowercaseSet = CFCharacterSetGetPredefined(
+ kCFCharacterSetLowercaseLetter);
+ }
+ PERF_ENTRY(iswlower);
+ ENTRY("PAL_iswlower (c=%d)\n", c);
+ bRetVal = CFCharacterSetIsCharacterMember(sLowercaseSet, c);
+#else /* HAVE_COREFOUNDATION */
+ UnicodeDataRec dataRec;
+
+ PERF_ENTRY(iswlower);
+ ENTRY("PAL_iswlower (c=%d)\n", c);
+
+ if (!GetUnicodeData(c, &dataRec))
+ {
+ TRACE( "Unable to retrieve unicode data for the character %c.\n", c );
+ goto exit;
+ }
+
+ if (dataRec.C1_TYPE_FLAGS & C1_LOWER)
+ {
+ bRetVal = TRUE;
+ }
+exit:
+#endif /* HAVE_COREFOUNDATION */
+ LOGEXIT("PAL_iswlower returns %s.\n", bRetVal == TRUE ? "TRUE" : "FALSE");
+ PERF_EXIT(iswlower);
+ return bRetVal;
+}
+
+/*++
+Function:
+ PAL_iswalpha
+
+See MSDN
+
+--*/
+int
+__cdecl
+PAL_iswalpha( wchar_16 c )
+{
+ PERF_ENTRY(iswalpha);
+ ENTRY( "PAL_iswalpha (c=%d)\n", c);
+
+ if ( PAL_iswupper( c ) || PAL_iswlower( c ) )
+ {
+ LOGEXIT( "PAL_iswalpha returns 1.\n" );
+ PERF_EXIT(iswalpha);
+ return 1;
+ }
+
+ LOGEXIT( "PAL_iswalpha returns 0.\n" );
+ PERF_EXIT(iswalpha);
+ return 0;
+}
+
+
+/*++
+Function:
+ PAL_wcscat
+
+See MSDN or the man page for mcscat.
+
+--*/
+wchar_16 *
+__cdecl
+PAL_wcscat(
+ wchar_16 *strDestination,
+ const wchar_16 *strSource)
+{
+ wchar_16 *ret;
+ PERF_ENTRY(wcscat);
+ ENTRY("wcscat (strDestination=%p (%S), strSource=%p (%S))\n",
+ strDestination?strDestination:W16_NULLSTRING,
+ strDestination?strDestination:W16_NULLSTRING, strSource?strSource:W16_NULLSTRING, strSource?strSource:W16_NULLSTRING);
+
+ ret = PAL_wcsncat( strDestination, strSource, PAL_wcslen( strSource ) );
+
+ LOGEXIT("wcscat returnng wchar_t %p (%S)\n", ret, ret);
+ PERF_EXIT(wcscat);
+ return ret;
+}
+
+
+/*++
+Function:
+ PAL_wcscpy
+
+See MSDN or the man page for mcscpy.
+
+--*/
+wchar_16 *
+__cdecl
+PAL_wcscpy(
+ wchar_16 *strDestination,
+ const wchar_16 *strSource)
+{
+ wchar_16 *start = strDestination;
+
+ PERF_ENTRY(wcscpy);
+ ENTRY("wcscpy (strDestination=%p, strSource=%p (%S))\n",
+ strDestination, strSource ? strSource:W16_NULLSTRING, strSource ? strSource:W16_NULLSTRING);
+
+ if (strDestination == NULL)
+ {
+ ERROR("invalid strDestination argument\n");
+ LOGEXIT("wcscpy returning wchar_t NULL\n");
+ PERF_EXIT(wcscpy);
+ return NULL;
+ }
+
+ if (strSource == NULL)
+ {
+ ERROR("invalid strSource argument\n");
+ LOGEXIT("wcscpy returning wchar_t NULL\n");
+ PERF_EXIT(wcscpy);
+ return NULL;
+ }
+
+ /* copy source string to destination string */
+ while(*strSource)
+ {
+ *strDestination++ = *strSource++;
+ }
+
+ /* add terminating null */
+ *strDestination = '\0';
+
+ LOGEXIT("wcscpy returning wchar_t %p (%S)\n", start, start);
+ PERF_EXIT(wcscpy);
+ return start;
+}
+
+
+/*++
+Function:
+ PAL_wcslen
+
+See MSDN or the man page for wcslen.
+
+--*/
+size_t
+__cdecl
+PAL_wcslen(
+ const wchar_16 *string)
+{
+ size_t nChar = 0;
+
+ PERF_ENTRY(wcslen);
+ ENTRY("wcslen (string=%p (%S))\n", string?string:W16_NULLSTRING, string?string:W16_NULLSTRING);
+
+ if ( !string )
+ {
+ LOGEXIT("wcslen returning size_t %u\n", 0);
+ PERF_EXIT(wcslen);
+ return 0;
+ }
+ while (*string++)
+ {
+ nChar++;
+ }
+
+ LOGEXIT("wcslen returning size_t %u\n", nChar);
+ PERF_EXIT(wcslen);
+ return nChar;
+}
+
+
+/*++
+Function:
+ PAL_wcsncmp
+
+See MSDN or the man page for wcsncmp.
+--*/
+int
+__cdecl
+PAL_wcsncmp(
+ const wchar_16 *string1,
+ const wchar_16 *string2,
+ size_t count)
+{
+ size_t i;
+ int diff = 0;
+
+ PERF_ENTRY(wcsncmp);
+ ENTRY("wcsncmp (string1=%p (%S), string2=%p (%S) count=%lu)\n",
+ string1?string1:W16_NULLSTRING,
+ string1?string1:W16_NULLSTRING, string2?string2:W16_NULLSTRING, string2?string2:W16_NULLSTRING,
+ (unsigned long) count);
+
+ for (i = 0; i < count; i++)
+ {
+ diff = string1[i] - string2[i];
+ if (diff != 0)
+ {
+ break;
+ }
+
+ /* stop if we reach the end of the string */
+ if(string1[i]==0)
+ {
+ break;
+ }
+ }
+ LOGEXIT("wcsncmp returning int %d\n", diff);
+ PERF_EXIT(wcsncmp);
+ return diff;
+}
+
+/*++
+Function:
+ PAL_wcscmp
+
+See MSDN or the man page for wcscmp.
+--*/
+int
+__cdecl
+PAL_wcscmp(
+ const wchar_16 *string1,
+ const wchar_16 *string2)
+{
+ int ret;
+
+ PERF_ENTRY(wcscmp);
+ ENTRY("wcscmp (string1=%p (%S), string2=%p (%S))\n",
+ string1?string1:W16_NULLSTRING,
+ string1?string1:W16_NULLSTRING, string2?string2:W16_NULLSTRING, string2?string2:W16_NULLSTRING);
+
+ ret = PAL_wcsncmp(string1, string2, 0x7fffffff);
+
+ LOGEXIT("wcscmp returns int %d\n", ret);
+ PERF_EXIT(wcscmp);
+ return ret;
+}
+
+/*++
+Function:
+ PAL_wcschr
+
+See MSDN or man page for wcschr.
+
+--*/
+wchar_16 _WConst_return *
+__cdecl
+PAL_wcschr(
+ const wchar_16 * string,
+ wchar_16 c)
+{
+ PERF_ENTRY(wcschr);
+ ENTRY("wcschr (string=%p (%S), c=%C)\n", string?string:W16_NULLSTRING, string?string:W16_NULLSTRING, c);
+
+ while (*string)
+ {
+ if (*string == c)
+ {
+ LOGEXIT("wcschr returning wchar_t %p (%S)\n", string?string:W16_NULLSTRING, string?string:W16_NULLSTRING);
+ PERF_EXIT(wcschr);
+ return (wchar_16 *) string;
+ }
+ string++;
+ }
+
+ // Check if the comparand was \000
+ if (*string == c)
+ return (wchar_16 *) string;
+
+ LOGEXIT("wcschr returning wchar_t NULL\n");
+ PERF_EXIT(wcschr);
+ return NULL;
+}
+
+
+/*++
+Function:
+ PAL_wcsrchr
+
+See MSDN or man page for wcsrchr.
+
+--*/
+wchar_16 _WConst_return *
+__cdecl
+PAL_wcsrchr(
+ const wchar_16 * string,
+ wchar_16 c)
+{
+ wchar_16 *last = NULL;
+
+ PERF_ENTRY(wcsrchr);
+ ENTRY("wcsrchr (string=%p (%S), c=%C)\n", string?string:W16_NULLSTRING, string?string:W16_NULLSTRING, c);
+
+ while (*string)
+ {
+ if (*string == c)
+ {
+ last = (wchar_16 *) string;
+ }
+ string++;
+ }
+
+ LOGEXIT("wcsrchr returning wchar_t %p (%S)\n", last?last:W16_NULLSTRING, last?last:W16_NULLSTRING);
+ PERF_EXIT(wcsrchr);
+ return (wchar_16 *)last;
+}
+
+
+/*++
+Function:
+ PAL_wcsspn
+
+See MSDN or man page for wcspbrk.
+--*/
+size_t
+__cdecl
+PAL_wcsspn (const wchar_16 *string, const wchar_16 *stringCharSet)
+{
+ ASSERT(0);
+ return 0;
+}
+
+
+/*++
+Function:
+ PAL_wcspbrk
+
+See MSDN or man page for wcspbrk.
+--*/
+const wchar_16 *
+__cdecl
+PAL_wcspbrk(
+ const wchar_16 *string,
+ const wchar_16 *strCharSet)
+{
+ PERF_ENTRY(wcspbrk);
+ ENTRY("wcspbrk (string=%p (%S), strCharSet=%p (%S))\n",
+ string?string:W16_NULLSTRING,
+ string?string:W16_NULLSTRING, strCharSet?strCharSet:W16_NULLSTRING, strCharSet?strCharSet:W16_NULLSTRING);
+
+ while (*string)
+ {
+ if (PAL_wcschr(strCharSet, *string) != NULL)
+ {
+ LOGEXIT("wcspbrk returning wchar_t %p (%S)\n", string?string:W16_NULLSTRING, string?string:W16_NULLSTRING);
+ PERF_EXIT(wcspbrk);
+ return (wchar_16 *) string;
+ }
+
+ string++;
+ }
+
+ LOGEXIT("wcspbrk returning wchar_t NULL\n");
+ PERF_EXIT(wcspbrk);
+ return NULL;
+}
+
+
+/*++
+Function:
+ PAL_wcsstr
+
+See MSDN or man page for wcsstr.
+--*/
+const wchar_16 *
+__cdecl
+PAL_wcsstr(
+ const wchar_16 *string,
+ const wchar_16 *strCharSet)
+{
+ wchar_16 *ret = NULL;
+ int i;
+
+ PERF_ENTRY(wcsstr);
+ ENTRY("wcsstr (string=%p (%S), strCharSet=%p (%S))\n",
+ string?string:W16_NULLSTRING,
+ string?string:W16_NULLSTRING, strCharSet?strCharSet:W16_NULLSTRING, strCharSet?strCharSet:W16_NULLSTRING);
+
+ if (string == NULL)
+ {
+ ret = NULL;
+ goto leave;
+ }
+
+ if (strCharSet == NULL)
+ {
+ ret = NULL;
+ goto leave;
+ }
+
+ if (*strCharSet == 0)
+ {
+ ret = (wchar_16 *)string;
+ goto leave;
+ }
+
+ while (*string != 0)
+ {
+ i = 0;
+ while (1)
+ {
+ if (*(string + i) == 0 || *(strCharSet + i) == 0)
+ {
+ ret = (wchar_16 *) string;
+ goto leave;
+ }
+ if (*(string + i) != *(strCharSet + i))
+ {
+ break;
+ }
+ i++;
+ }
+ string++;
+ }
+
+ leave:
+ LOGEXIT("wcsstr returning wchar_t %p (%S)\n", ret?ret:W16_NULLSTRING, ret?ret:W16_NULLSTRING);
+ PERF_EXIT(wcsstr);
+ return ret;
+}
+
+/*++
+Function :
+
+ PAL_wcsncpy
+
+see msdn doc.
+--*/
+wchar_16 *
+__cdecl
+PAL_wcsncpy( wchar_16 * strDest, const wchar_16 *strSource, size_t count )
+{
+ UINT length = sizeof( wchar_16 ) * count;
+ PERF_ENTRY(wcsncpy);
+ ENTRY("wcsncpy( strDest:%p, strSource:%p (%S), count:%lu)\n",
+ strDest, strSource, strSource, (unsigned long) count);
+
+ memset( strDest, 0, length );
+ length = min( count, PAL_wcslen( strSource ) ) * sizeof( wchar_16 );
+ memcpy( strDest, strSource, length );
+
+ LOGEXIT("wcsncpy returning (wchar_16*): %p\n", strDest);
+ PERF_EXIT(wcsncpy);
+ return strDest;
+}
+
+/*++
+Function :
+
+ wcsncat
+
+see msdn doc.
+--*/
+wchar_16 *
+__cdecl
+PAL_wcsncat( wchar_16 * strDest, const wchar_16 *strSource, size_t count )
+{
+ wchar_16 *start = strDest;
+ UINT LoopCount = 0;
+ UINT StrSourceLength = 0;
+
+ PERF_ENTRY(wcsncat);
+ ENTRY( "wcsncat (strDestination=%p (%S), strSource=%p (%S), count=%lu )\n",
+ strDest ? strDest : W16_NULLSTRING,
+ strDest ? strDest : W16_NULLSTRING,
+ strSource ? strSource : W16_NULLSTRING,
+ strSource ? strSource : W16_NULLSTRING, (unsigned long) count);
+
+ if ( strDest == NULL )
+ {
+ ERROR("invalid strDest argument\n");
+ LOGEXIT("wcsncat returning wchar_t NULL\n");
+ PERF_EXIT(wcsncat);
+ return NULL;
+ }
+
+ if ( strSource == NULL )
+ {
+ ERROR("invalid strSource argument\n");
+ LOGEXIT("wcsncat returning wchar_t NULL\n");
+ PERF_EXIT(wcsncat);
+ return NULL;
+ }
+
+ /* find end of source string */
+ while ( *strDest )
+ {
+ strDest++;
+ }
+
+ StrSourceLength = PAL_wcslen( strSource );
+ if ( StrSourceLength < count )
+ {
+ count = StrSourceLength;
+ }
+
+ /* concatenate new string */
+ while( *strSource && LoopCount < count )
+ {
+ *strDest++ = *strSource++;
+ LoopCount++;
+ }
+
+ /* add terminating null */
+ *strDest = '\0';
+
+ LOGEXIT("wcsncat returning wchar_t %p (%S)\n", start, start);
+ PERF_EXIT(wcsncat);
+ return start;
+}
+
+static BOOL MISC_CRT_WCSTOD_IsValidCharacter( WCHAR c )
+{
+ if ( c == '+' || c == '-' || c == '.' || ( c >= '0' && c <= '9' ) ||
+ c == 'e' || c == 'E' || c == 'd' || c == 'D' )
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*++
+Function :
+
+ wcstod
+
+ There is a slight difference between the Windows version of wcstod
+ and the BSD versio of wcstod.
+
+ Under Windows the string " -1b " returns -1.000000 stop char = 'b'
+ Under BSD the same string returns 0.000000 stop ' '
+
+see msdn doc.
+--*/
+double
+__cdecl
+PAL_wcstod( const wchar_16 * nptr, wchar_16 **endptr )
+{
+ double RetVal = 0.0;
+ LPSTR lpStringRep = NULL;
+ LPWSTR lpStartOfExpression = (LPWSTR)nptr;
+ LPWSTR lpEndOfExpression = NULL;
+ UINT Length = 0;
+
+ PERF_ENTRY(wcstod);
+ ENTRY( "wcstod( %p (%S), %p (%S) )\n", nptr, nptr, endptr , endptr );
+
+ if ( !nptr )
+ {
+ ERROR( "nptr is invalid.\n" );
+ LOGEXIT( "wcstod returning 0.0\n" );
+ PERF_EXIT(wcstod);
+ return 0.0;
+ }
+
+ /* Eat white space. */
+ while ( PAL_iswspace( *lpStartOfExpression ) )
+ {
+ lpStartOfExpression++;
+ }
+
+ /* Get the end of the expression. */
+ lpEndOfExpression = lpStartOfExpression;
+ while ( *lpEndOfExpression )
+ {
+ if ( !MISC_CRT_WCSTOD_IsValidCharacter( *lpEndOfExpression ) )
+ {
+ break;
+ }
+ lpEndOfExpression++;
+ }
+
+ if ( lpEndOfExpression != lpStartOfExpression )
+ {
+ Length = lpEndOfExpression - lpStartOfExpression;
+ lpStringRep = (LPSTR)PAL_malloc( Length + 1);
+
+ if ( lpStringRep )
+ {
+ if ( WideCharToMultiByte( CP_ACP, 0, lpStartOfExpression, Length,
+ lpStringRep, Length + 1 ,
+ NULL, 0 ) != 0 )
+ {
+ LPSTR ScanStop = NULL;
+ lpStringRep[Length]= 0;
+ RetVal = strtod( lpStringRep, &ScanStop );
+
+ /* See if strtod failed. */
+ if ( RetVal == 0.0 && ScanStop == lpStringRep )
+ {
+ ASSERT( "An error occurred in the conversion.\n" );
+ lpEndOfExpression = (LPWSTR)nptr;
+ }
+ }
+ else
+ {
+ ASSERT( "Wide char to multibyte conversion failed.\n" );
+ lpEndOfExpression = (LPWSTR)nptr;
+ }
+ }
+ else
+ {
+ ERROR( "Not enough memory.\n" );
+ lpEndOfExpression = (LPWSTR)nptr;
+ }
+ }
+ else
+ {
+ ERROR( "Malformed expression.\n" );
+ lpEndOfExpression = (LPWSTR)nptr;
+ }
+
+ /* Set the stop scan character. */
+ if ( endptr != NULL )
+ {
+ *endptr = lpEndOfExpression;
+ }
+
+ PAL_free( lpStringRep );
+ LOGEXIT( "wcstod returning %f.\n", RetVal );
+ PERF_EXIT(wcstod);
+ return RetVal;
+}
+
+/*++
+Function :
+
+ _ui64tow
+
+See MSDN for more details.
+--*/
+wchar_16 *
+__cdecl
+_ui64tow( unsigned __int64 value , wchar_16 * string , int radix )
+{
+ UINT ReversedIndex = 0;
+ WCHAR ReversedString[ 65 ];
+ LPWSTR lpString = string;
+ UINT Index = 0;
+
+ PERF_ENTRY(_ui64tow);
+ ENTRY( "_ui64tow( value=%I64d, string=%p (%S), radix=%d )\n",
+ value, string, string, radix );
+
+ if ( !string )
+ {
+ ERROR( "string has to be a valid pointer.\n" );
+ LOGEXIT( "_ui64tow returning NULL.\n" );
+ PERF_EXIT(_ui64tow);
+ return NULL;
+ }
+ if ( radix < 2 || radix > 36 )
+ {
+ ERROR( "radix has to be between 2 and 36.\n" );
+ LOGEXIT( "_ui64tow returning NULL.\n" );
+ PERF_EXIT(_ui64tow);
+ return NULL;
+ }
+
+ if(0 == value)
+ {
+ ReversedString[0] = '0';
+ Index++;
+ }
+ else while ( value )
+ {
+ int temp = value % radix;
+ value /= radix;
+
+ if ( temp < 10 )
+ {
+ ReversedString[ Index ] = temp + '0';
+ Index++;
+ }
+ else
+ {
+ ReversedString[ Index ] = temp - 10 + 'a';
+ Index++;
+ }
+ }
+
+ /* Reverse the string. */
+ ReversedIndex = Index;
+ for ( Index = 0; ReversedIndex > 0; ReversedIndex--, Index++ )
+ {
+ string[ Index ] = ReversedString[ ReversedIndex - 1 ];
+ }
+
+ string[ Index ] = '\0';
+ LOGEXIT( "_ui64tow returning %p (%S).\n", lpString , lpString );
+ PERF_EXIT(_ui64tow);
+ return lpString;
+}
+
+
+/*++
+Function:
+
+ iswdigit
+
+See MSDN for more details.
+--*/
+int
+__cdecl
+PAL_iswdigit( wchar_16 c )
+{
+ UINT nRetVal = 0;
+#if HAVE_COREFOUNDATION
+ static CFCharacterSetRef sDigitSet;
+
+ if (sDigitSet == NULL)
+ {
+ sDigitSet = CFCharacterSetGetPredefined(
+ kCFCharacterSetDecimalDigit);
+ }
+ PERF_ENTRY(iswdigit);
+ ENTRY("PAL_iswdigit (c=%d)\n", c);
+ nRetVal = CFCharacterSetIsCharacterMember(sDigitSet, c);
+#else /* HAVE_COREFOUNDATION */
+ UnicodeDataRec dataRec;
+
+ PERF_ENTRY(iswdigit);
+ ENTRY("PAL_iswdigit (c=%d)\n", c);
+
+ if (GetUnicodeData(c, &dataRec))
+ {
+ if (dataRec.C1_TYPE_FLAGS & C1_DIGIT)
+ {
+ nRetVal = 1;
+ }
+ else
+ {
+ nRetVal = 0;
+ }
+ }
+ else
+ {
+ TRACE( "No corresonding unicode record for character %d.\n", c );
+ }
+#endif /* HAVE_COREFOUNDATION */
+ LOGEXIT("PAL_iswdigit returning %d\n", nRetVal);
+ PERF_EXIT(iswdigit);
+ return nRetVal;
+}
+
+/*++
+Function:
+
+ iswxdigit
+
+See MSDN for more details.
+
+Notes :
+the information in UnicodeData doesn't help us, it doesn't have enough
+granularity. Results in windows show that only ASCII and "Fullwidth" (>0xFF10)
+numbers and letters are considered as "hex"; other "numbers"
+(nGeneralCategory==8) aren't.
+--*/
+int
+__cdecl
+PAL_iswxdigit( wchar_16 c )
+{
+ UINT nRetVal = 0;
+
+ PERF_ENTRY(iswxdigit);
+ ENTRY("PAL_iswxdigit( c=%d )\n", c);
+
+ /* ASCII characters */
+ if((c>= 'A' && c<='F') || /* uppercase hex letters */
+ (c>= 'a' && c<='f') || /* lowercase hex letters */
+ (c>= '0' && c<='9')) /* digits */
+ {
+ nRetVal = 1;
+ }
+ else
+ /* "fullwidth" characters, whatever that is */
+ if((c>= 0xFF10 && c<=0xFF19) || /* digits */
+ (c>= 0xFF21 && c<=0xFF26) || /* uppercase hex letters */
+ (c>= 0xFF41 && c<=0xFF46)) /* lowercase hex letters */
+ {
+ nRetVal = 1;
+ }
+ else
+ {
+ nRetVal = 0;
+ }
+ LOGEXIT("PAL_iswxdigit returning %d\n", nRetVal);
+ PERF_EXIT(iswxdigit);
+ return nRetVal;
+}
+
+
+/*++
+Function:
+
+ iswprint
+
+See MSDN for more details.
+--*/
+int
+__cdecl
+PAL_iswprint( wchar_16 c )
+{
+ int ret;
+
+
+ PERF_ENTRY(iswprint);
+ ENTRY("PAL_iswprint (%#X)\n", c);
+
+ ret = iswprint(c);
+
+ LOGEXIT("PAL_iswprint returns %d\n", ret);
+ PERF_EXIT(iswprint);
+ return (ret);
+}
+
+
+/*++
+Function:
+ PAL_wcscspn
+
+Finds the number of consecutive characters from the start of the string
+that are not in the set.
+
+Return value:
+
+The number of characters from the start of the string that are not in
+the set.
+
+Parameters:
+string String
+strCharSet Set of delimiter characters
+
+--*/
+size_t
+__cdecl
+PAL_wcscspn(const wchar_16 *string, const wchar_16 *strCharSet)
+{
+ const wchar_16 *temp;
+ size_t count = 0;
+
+ PERF_ENTRY(wcscspn);
+
+ while(*string != 0)
+ {
+ for(temp = strCharSet; *temp != 0; temp++)
+ {
+ if (*string == *temp)
+ {
+ PERF_EXIT(wcscspn);
+ return count;
+ }
+ }
+ count++;
+ string++;
+ }
+ PERF_EXIT(wcscspn);
+ return count;
+}
+
+#if HAVE_COREFOUNDATION
+/*--
+Function:
+ PAL_iswblank
+
+Returns TRUE if c is a Win32 "blank" character.
+--*/
+int
+__cdecl
+PAL_iswblank(wchar_16 c)
+{
+ int ret;
+ static CFCharacterSetRef sSpaceAndNewlineSet;
+
+ if (sSpaceAndNewlineSet == NULL)
+ {
+ sSpaceAndNewlineSet = CFCharacterSetGetPredefined(
+ kCFCharacterSetWhitespaceAndNewline);
+ }
+ switch (c)
+ {
+ case 0x0085:
+ case 0x1680:
+ case 0x202f:
+ case 0xfeff:
+ // These are blank characters on Windows, but are not part
+ // of the SpaceAndNewline character set in Core Foundation.
+ ret = TRUE;
+ break;
+ case 0x2028:
+ case 0x2029:
+ // These are not blank characters on Windows, but are part
+ // of the SpaceAndNewline character set in Core Foundation.
+ ret = FALSE;
+ break;
+ default:
+ ret = CFCharacterSetIsCharacterMember(sSpaceAndNewlineSet, c);
+ break;
+ }
+ return ret;
+}
+
+/*--
+Function:
+ PAL_iswcntrl
+
+Returns TRUE if c is a control character.
+--*/
+int
+__cdecl
+PAL_iswcntrl(wchar_16 c)
+{
+ int ret;
+ static CFCharacterSetRef sControlSet;
+
+ if (sControlSet == NULL)
+ {
+ sControlSet = CFCharacterSetGetPredefined(kCFCharacterSetControl);
+ }
+ ret = CFCharacterSetIsCharacterMember(sControlSet, c);
+ return ret;
+}
+
+/*--
+Function:
+ PAL_iswpunct
+
+Returns TRUE if c is a punctuation character.
+--*/
+int
+__cdecl
+PAL_iswpunct(wchar_16 c)
+{
+ int ret;
+ static CFCharacterSetRef sPunctuationSet = NULL;
+ static CFCharacterSetRef sSymbolSet = NULL;
+
+ if (sPunctuationSet == NULL)
+ {
+ sPunctuationSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation);
+ }
+ if (sSymbolSet == NULL)
+ {
+ sSymbolSet = CFCharacterSetGetPredefined(kCFCharacterSetSymbol);
+ }
+ ret = CFCharacterSetIsCharacterMember(sPunctuationSet, c) ||
+ CFCharacterSetIsCharacterMember(sSymbolSet, c);
+ return ret;
+}
+#endif // HAVE_COREFOUNDATION
diff --git a/src/pal/src/cruntime/wchartls.cpp b/src/pal/src/cruntime/wchartls.cpp
new file mode 100644
index 0000000000..5350f0551b
--- /dev/null
+++ b/src/pal/src/cruntime/wchartls.cpp
@@ -0,0 +1,127 @@
+// 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.
+
+/*++
+
+
+
+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"
+#include "pal/unicode_data.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/debug/debug.cpp b/src/pal/src/debug/debug.cpp
new file mode 100644
index 0000000000..5461ac6265
--- /dev/null
+++ b/src/pal/src/debug/debug.cpp
@@ -0,0 +1,1844 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ Implementation of Win32 debugging API functions.
+
+Revision History:
+
+
+
+--*/
+
+#ifndef BIT64
+#undef _LARGEFILE64_SOURCE
+#undef _FILE_OFFSET_BITS
+#endif
+
+#include "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(DEBUG); // some headers have code with asserts, so do this first
+
+#include "pal/thread.hpp"
+#include "pal/procobj.hpp"
+#include "pal/file.hpp"
+
+#include "pal/palinternal.h"
+#include "pal/process.h"
+#include "pal/context.h"
+#include "pal/debug.h"
+#include "pal/environ.h"
+#include "pal/malloc.hpp"
+#include "pal/module.h"
+#include "pal/stackstring.hpp"
+#include "pal/virtual.h"
+
+#include <signal.h>
+#include <unistd.h>
+#if HAVE_PROCFS_CTL
+#include <unistd.h>
+#elif HAVE_TTRACE // HAVE_PROCFS_CTL
+#include <sys/ttrace.h>
+#else // HAVE_TTRACE
+#include <sys/ptrace.h>
+#endif // HAVE_PROCFS_CTL
+#if HAVE_VM_READ
+#include <mach/mach.h>
+#endif // HAVE_VM_READ
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#if HAVE_PROCFS_H
+#include <procfs.h>
+#endif // HAVE_PROCFS_H
+
+#if HAVE_MACH_EXCEPTIONS
+#include "../exception/machexception.h"
+#endif // HAVE_MACH_EXCEPTIONS
+
+using namespace CorUnix;
+
+extern "C" void DBG_DebugBreak_End();
+
+#if HAVE_PROCFS_CTL
+#define CTL_ATTACH "attach"
+#define CTL_DETACH "detach"
+#define CTL_WAIT "wait"
+#endif // HAVE_PROCFS_CTL
+
+/* ------------------- Constant definitions ----------------------------------*/
+
+#if !HAVE_VM_READ && !HAVE_PROCFS_CTL
+const BOOL DBG_ATTACH = TRUE;
+const BOOL DBG_DETACH = FALSE;
+#endif
+static const char PAL_OUTPUTDEBUGSTRING[] = "PAL_OUTPUTDEBUGSTRING";
+
+#ifdef _DEBUG
+#define ENABLE_RUN_ON_DEBUG_BREAK 1
+#endif // _DEBUG
+
+#ifdef ENABLE_RUN_ON_DEBUG_BREAK
+static const char PAL_RUN_ON_DEBUG_BREAK[] = "PAL_RUN_ON_DEBUG_BREAK";
+#endif // ENABLE_RUN_ON_DEBUG_BREAK
+
+/* ------------------- Static function prototypes ----------------------------*/
+
+#if !HAVE_VM_READ && !HAVE_PROCFS_CTL && !HAVE_TTRACE
+static int
+DBGWriteProcMem_Int(DWORD processId, int *addr, int data);
+static int
+DBGWriteProcMem_IntWithMask(DWORD processId, int *addr, int data,
+ unsigned int mask);
+#endif // !HAVE_VM_READ && !HAVE_PROCFS_CTL && !HAVE_TTRACE
+
+#if !HAVE_VM_READ && !HAVE_PROCFS_CTL
+
+static BOOL
+DBGAttachProcess(CPalThread *pThread, HANDLE hProcess, DWORD dwProcessId);
+
+static BOOL
+DBGDetachProcess(CPalThread *pThread, HANDLE hProcess, DWORD dwProcessId);
+
+static int
+DBGSetProcessAttached(CPalThread *pThread, HANDLE hProcess, BOOL bAttach);
+
+#endif // !HAVE_VM_READ && !HAVE_PROCFS_CTL
+
+extern "C" {
+
+/*++
+Function:
+ FlushInstructionCache
+
+The FlushInstructionCache function flushes the instruction cache for
+the specified process.
+
+Remarks
+
+This is a no-op for x86 architectures where the instruction and data
+caches are coherent in hardware. For non-X86 architectures, this call
+usually maps to a kernel API to flush the D-caches on all processors.
+
+--*/
+BOOL
+PALAPI
+FlushInstructionCache(
+ IN HANDLE hProcess,
+ IN LPCVOID lpBaseAddress,
+ IN SIZE_T dwSize)
+{
+ BOOL Ret;
+
+ PERF_ENTRY(FlushInstructionCache);
+ ENTRY("FlushInstructionCache (hProcess=%p, lpBaseAddress=%p dwSize=%d)\
+ \n", hProcess, lpBaseAddress, dwSize);
+
+ if (lpBaseAddress != NULL)
+ {
+ Ret = DBG_FlushInstructionCache(lpBaseAddress, dwSize);
+ }
+ else
+ {
+ Ret = TRUE;
+ }
+
+ LOGEXIT("FlushInstructionCache returns BOOL %d\n", Ret);
+ PERF_EXIT(FlushInstructionCache);
+ return Ret;
+}
+
+
+/*++
+Function:
+ OutputDebugStringA
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+OutputDebugStringA(
+ IN LPCSTR lpOutputString)
+{
+ PERF_ENTRY(OutputDebugStringA);
+ ENTRY("OutputDebugStringA (lpOutputString=%p (%s))\n",
+ lpOutputString ? lpOutputString : "NULL",
+ lpOutputString ? lpOutputString : "NULL");
+
+ // As we don't support debug events, we are going to output the debug string
+ // to stderr instead of generating OUT_DEBUG_STRING_EVENT. It's safe to tell
+ // EnvironGetenv not to make a copy of the value here since we only want to
+ // check whether it exists, not actually use it.
+ if ((lpOutputString != NULL) &&
+ (NULL != EnvironGetenv(PAL_OUTPUTDEBUGSTRING, /* copyValue */ FALSE)))
+ {
+ fprintf(stderr, "%s", lpOutputString);
+ }
+
+ LOGEXIT("OutputDebugStringA returns\n");
+ PERF_EXIT(OutputDebugStringA);
+}
+
+/*++
+Function:
+ OutputDebugStringW
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+OutputDebugStringW(
+ IN LPCWSTR lpOutputString)
+{
+ CHAR *lpOutputStringA;
+ int strLen;
+
+ PERF_ENTRY(OutputDebugStringW);
+ ENTRY("OutputDebugStringW (lpOutputString=%p (%S))\n",
+ lpOutputString ? lpOutputString: W16_NULLSTRING,
+ lpOutputString ? lpOutputString: W16_NULLSTRING);
+
+ if (lpOutputString == NULL)
+ {
+ OutputDebugStringA("");
+ goto EXIT;
+ }
+
+ if ((strLen = WideCharToMultiByte(CP_ACP, 0, lpOutputString, -1, NULL, 0,
+ NULL, NULL))
+ == 0)
+ {
+ ASSERT("failed to get wide chars length\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto EXIT;
+ }
+
+ /* strLen includes the null terminator */
+ if ((lpOutputStringA = (LPSTR) InternalMalloc((strLen * sizeof(CHAR)))) == NULL)
+ {
+ ERROR("Insufficient memory available !\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto EXIT;
+ }
+
+ if(! WideCharToMultiByte(CP_ACP, 0, lpOutputString, -1,
+ lpOutputStringA, strLen, NULL, NULL))
+ {
+ ASSERT("failed to convert wide chars to multibytes\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ free(lpOutputStringA);
+ goto EXIT;
+ }
+
+ OutputDebugStringA(lpOutputStringA);
+ free(lpOutputStringA);
+
+EXIT:
+ LOGEXIT("OutputDebugStringW returns\n");
+ PERF_EXIT(OutputDebugStringW);
+}
+
+#ifdef ENABLE_RUN_ON_DEBUG_BREAK
+/*
+ When DebugBreak() is called, if PAL_RUN_ON_DEBUG_BREAK is set,
+ DebugBreak() will execute whatever command is in there.
+
+ PAL_RUN_ON_DEBUG_BREAK must be no longer than 255 characters.
+
+ This command string inherits the current process's environment,
+ with two additions:
+ PAL_EXE_PID - the process ID of the current process
+ PAL_EXE_NAME - the name of the executable of the current process
+
+ When DebugBreak() runs this string, it periodically polls the child process
+ and blocks until it finishes. If you use this mechanism to start a
+ debugger, you can break this poll loop by setting the "spin" variable in
+ run_debug_command()'s frame to 0, and then the parent process can
+ continue.
+
+ suggested values for PAL_RUN_ON_DEBUG_BREAK:
+ to halt the process for later inspection:
+ 'echo stopping $PAL_EXE_PID; kill -STOP $PAL_EXE_PID; sleep 10'
+
+ to print out the stack trace:
+ 'pstack $PAL_EXE_PID'
+
+ to invoke the gdb debugger on the process:
+ 'set -x; gdb $PAL_EXE_NAME $PAL_EXE_PID'
+
+ to invoke the ddd debugger on the process (requires X11):
+ 'set -x; ddd $PAL_EXE_NAME $PAL_EXE_PID'
+*/
+
+static
+int
+run_debug_command (const char *command)
+{
+ int pid;
+ Volatile<int> spin = 1;
+
+ if (!command) {
+ return 1;
+ }
+
+ printf("Spawning command: %s\n", command);
+
+ pid = fork();
+ if (pid == -1) {
+ return -1;
+ }
+ if (pid == 0) {
+ const char *argv[4] = { "sh", "-c", command, 0 };
+ execv("/bin/sh", (char **)argv);
+ exit(127);
+ }
+
+ /* We continue either when the spawned process has stopped, or when
+ an attached debugger sets spin to 0 */
+ while (spin != 0) {
+ int status = 0;
+ int ret = waitpid(pid, &status, WNOHANG);
+ if (ret == 0) {
+ int i;
+ /* I tried to use sleep for this, and that works everywhere except
+ FreeBSD. The problem on FreeBSD is that if the process gets a
+ signal while blocked in sleep(), gdb is confused by the stack */
+ for (i = 0; i < 1000000; i++)
+ ;
+ }
+ else if (ret == -1) {
+ if (errno != EINTR) {
+ return -1;
+ }
+ }
+ else if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ }
+ else {
+ fprintf (stderr, "unexpected return from waitpid\n");
+ return -1;
+ }
+ };
+ return 0;
+}
+#endif // ENABLE_RUN_ON_DEBUG_BREAK
+
+#define PID_TEXT "PAL_EXE_PID="
+#define EXE_TEXT "PAL_EXE_NAME="
+
+static
+int
+DebugBreakCommand()
+{
+#ifdef ENABLE_RUN_ON_DEBUG_BREAK
+ extern MODSTRUCT exe_module;
+
+ char *command_string = EnvironGetenv(PAL_RUN_ON_DEBUG_BREAK);
+ if (command_string)
+ {
+ char pid_buf[sizeof (PID_TEXT) + 32];
+ PathCharString exe_bufString;
+ int libNameLength = 10;
+
+ if (exe_module.lib_name != NULL)
+ {
+ libNameLength = PAL_wcslen(exe_module.lib_name);
+ }
+
+ SIZE_T dwexe_buf = strlen(EXE_TEXT) + libNameLength + 1;
+ CHAR * exe_buf = exe_bufString.OpenStringBuffer(dwexe_buf);
+
+ if (NULL == exe_buf)
+ {
+ goto FAILED;
+ }
+
+ if (snprintf (pid_buf, sizeof (pid_buf), PID_TEXT "%d", getpid()) <= 0)
+ {
+ goto FAILED;
+ }
+
+ if (snprintf (exe_buf, sizeof (CHAR) * (dwexe_buf + 1), EXE_TEXT "%ls", (wchar_t *)exe_module.lib_name) <= 0)
+ {
+ goto FAILED;
+ }
+
+ exe_bufString.CloseBuffer(dwexe_buf);
+ /* strictly speaking, we might want to only set these environment
+ variables in the child process, but if we do that we can't check
+ for errors. putenv/setenv can fail when out of memory */
+
+ if (!EnvironPutenv (pid_buf, FALSE) || !EnvironPutenv (exe_buf, FALSE))
+ {
+ goto FAILED;
+ }
+
+ if (run_debug_command (command_string))
+ {
+ goto FAILED;
+ }
+
+ free(command_string);
+ return 1;
+ }
+
+ return 0;
+
+FAILED:
+ if (command_string)
+ {
+ free(command_string);
+ }
+
+ fprintf (stderr, "Failed to execute command: '%s'\n", command_string);
+ return -1;
+#else // ENABLE_RUN_ON_DEBUG_BREAK
+ return 0;
+#endif // ENABLE_RUN_ON_DEBUG_BREAK
+}
+
+/*++
+Function:
+ DebugBreak
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+DebugBreak(
+ VOID)
+{
+ PERF_ENTRY(DebugBreak);
+ ENTRY("DebugBreak()\n");
+
+ if (DebugBreakCommand() <= 0) {
+ // either didn't do anything, or failed
+ TRACE("Calling DBG_DebugBreak\n");
+ DBG_DebugBreak();
+ }
+
+ LOGEXIT("DebugBreak returns\n");
+ PERF_EXIT(DebugBreak);
+}
+
+/*++
+Function:
+ IsInDebugBreak(addr)
+
+ Returns true if the address is in DBG_DebugBreak.
+
+--*/
+BOOL
+IsInDebugBreak(void *addr)
+{
+ return (addr >= (void *)DBG_DebugBreak) && (addr <= (void *)DBG_DebugBreak_End);
+}
+
+/*++
+Function:
+ GetThreadContext
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetThreadContext(
+ IN HANDLE hThread,
+ IN OUT LPCONTEXT lpContext)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+ CPalThread *pTargetThread;
+ IPalObject *pobjThread = NULL;
+ BOOL ret = FALSE;
+
+ PERF_ENTRY(GetThreadContext);
+ ENTRY("GetThreadContext (hThread=%p, lpContext=%p)\n",hThread,lpContext);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalGetThreadDataFromHandle(
+ pThread,
+ hThread,
+ 0, // THREAD_GET_CONTEXT
+ &pTargetThread,
+ &pobjThread
+ );
+
+ if (NO_ERROR == palError)
+ {
+ if (!pTargetThread->IsDummy())
+ {
+ ret = CONTEXT_GetThreadContext(
+ GetCurrentProcessId(),
+ pTargetThread->GetPThreadSelf(),
+ lpContext
+ );
+ }
+ else
+ {
+ ASSERT("Dummy thread handle passed to GetThreadContext\n");
+ pThread->SetLastError(ERROR_INVALID_HANDLE);
+ }
+ }
+ else
+ {
+ pThread->SetLastError(palError);
+ }
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pThread);
+ }
+
+ LOGEXIT("GetThreadContext returns ret:%d\n", ret);
+ PERF_EXIT(GetThreadContext);
+ return ret;
+}
+
+/*++
+Function:
+ SetThreadContext
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+SetThreadContext(
+ IN HANDLE hThread,
+ IN CONST CONTEXT *lpContext)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+ CPalThread *pTargetThread;
+ IPalObject *pobjThread = NULL;
+ BOOL ret = FALSE;
+
+ PERF_ENTRY(SetThreadContext);
+ ENTRY("SetThreadContext (hThread=%p, lpContext=%p)\n",hThread,lpContext);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalGetThreadDataFromHandle(
+ pThread,
+ hThread,
+ 0, // THREAD_SET_CONTEXT
+ &pTargetThread,
+ &pobjThread
+ );
+
+ if (NO_ERROR == palError)
+ {
+ if (!pTargetThread->IsDummy())
+ {
+ ret = CONTEXT_SetThreadContext(
+ GetCurrentProcessId(),
+ pTargetThread->GetPThreadSelf(),
+ lpContext
+ );
+ }
+ else
+ {
+ ASSERT("Dummy thread handle passed to SetThreadContext\n");
+ pThread->SetLastError(ERROR_INVALID_HANDLE);
+ }
+ }
+ else
+ {
+ pThread->SetLastError(palError);
+ }
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pThread);
+ }
+
+ return ret;
+}
+
+#if !HAVE_VM_READ && !HAVE_PROCFS_CTL && !HAVE_TTRACE
+/*++
+Function:
+ DBGWriteProcMem_Int
+
+Abstract
+ write one int to a process memory address
+
+Parameter
+ processId : process handle
+ addr : memory address where the int should be written
+ data : int to be written in addr
+
+Return
+ Return 1 if it succeeds, or 0 if it's fails
+--*/
+static
+int
+DBGWriteProcMem_Int(IN DWORD processId,
+ IN int *addr,
+ IN int data)
+{
+ if (PAL_PTRACE( PAL_PT_WRITE_D, processId, addr, data ) == -1)
+ {
+ if (errno == EFAULT)
+ {
+ ERROR("ptrace(PT_WRITE_D, pid:%d caddr_t:%p data:%x) failed "
+ "errno:%d (%s)\n", processId, addr, data, errno, strerror(errno));
+ SetLastError(ERROR_INVALID_ADDRESS);
+ }
+ else
+ {
+ ASSERT("ptrace(PT_WRITE_D, pid:%d caddr_t:%p data:%x) failed "
+ "errno:%d (%s)\n", processId, addr, data, errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+/*++
+Function:
+ DBGWriteProcMem_IntWithMask
+
+Abstract
+ write one int to a process memory address space using mask
+
+Parameter
+ processId : process ID
+ addr : memory address where the int should be written
+ data : int to be written in addr
+ mask : the mask used to write only a parts of data
+
+Return
+ Return 1 if it succeeds, or 0 if it's fails
+--*/
+static
+int
+DBGWriteProcMem_IntWithMask(IN DWORD processId,
+ IN int *addr,
+ IN int data,
+ IN unsigned int mask )
+{
+ int readInt;
+
+ if (mask != ~0)
+ {
+ errno = 0;
+ if (((readInt = PAL_PTRACE( PAL_PT_READ_D, processId, addr, 0 )) == -1)
+ && errno)
+ {
+ if (errno == EFAULT)
+ {
+ ERROR("ptrace(PT_READ_D, pid:%d, caddr_t:%p, 0) failed "
+ "errno:%d (%s)\n", processId, addr, errno, strerror(errno));
+ SetLastError(ERROR_INVALID_ADDRESS);
+ }
+ else
+ {
+ ASSERT("ptrace(PT_READ_D, pid:%d, caddr_t:%p, 0) failed "
+ "errno:%d (%s)\n", processId, addr, errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+
+ return 0;
+ }
+ data = (data & mask) | (readInt & ~mask);
+ }
+ return DBGWriteProcMem_Int(processId, addr, data);
+}
+#endif // !HAVE_VM_READ && !HAVE_PROCFS_CTL && !HAVE_TTRACE
+
+#if !HAVE_VM_READ && !HAVE_PROCFS_CTL
+
+/*++
+Function:
+ DBGAttachProcess
+
+Abstract
+
+ Attach the indicated process to the current process.
+
+ if the indicated process is already attached by the current process, then
+ increment the number of attachment pending. if ot, attach it to the current
+ process (with PT_ATTACH).
+
+Parameter
+ hProcess : handle to process to attach to
+ processId : process ID to attach
+Return
+ Return true if it succeeds, or false if it's fails
+--*/
+static
+BOOL
+DBGAttachProcess(
+ CPalThread *pThread,
+ HANDLE hProcess,
+ DWORD processId
+ )
+{
+ int attchmentCount;
+ int savedErrno;
+#if HAVE_PROCFS_CTL
+ int fd = -1;
+ char ctlPath[1024];
+#endif // HAVE_PROCFS_CTL
+
+ attchmentCount =
+ DBGSetProcessAttached(pThread, hProcess, DBG_ATTACH);
+
+ if (attchmentCount == -1)
+ {
+ /* Failed to set the process as attached */
+ goto EXIT;
+ }
+
+ if (attchmentCount == 1)
+ {
+#if HAVE_PROCFS_CTL
+ struct timespec waitTime;
+
+ // FreeBSD has some trouble when a series of attach/detach sequences
+ // occurs too close together. When this happens, we'll be able to
+ // attach to the process, but waiting for the process to stop
+ // (either via writing "wait" to /proc/<pid>/ctl or via waitpid)
+ // will hang. If we pause for a very short amount of time before
+ // trying to attach, we don't run into this situation.
+ waitTime.tv_sec = 0;
+ waitTime.tv_nsec = 50000000;
+ nanosleep(&waitTime, NULL);
+
+ sprintf_s(ctlPath, sizeof(ctlPath), "/proc/%d/ctl", processId);
+ fd = InternalOpen(ctlPath, O_WRONLY);
+ if (fd == -1)
+ {
+ ERROR("Failed to open %s: errno is %d (%s)\n", ctlPath,
+ errno, strerror(errno));
+ goto DETACH1;
+ }
+
+ if (write(fd, CTL_ATTACH, sizeof(CTL_ATTACH)) < (int)sizeof(CTL_ATTACH))
+ {
+ ERROR("Failed to attach to %s: errno is %d (%s)\n", ctlPath,
+ errno, strerror(errno));
+ close(fd);
+ goto DETACH1;
+ }
+
+ if (write(fd, CTL_WAIT, sizeof(CTL_WAIT)) < (int)sizeof(CTL_WAIT))
+ {
+ ERROR("Failed to wait for %s: errno is %d (%s)\n", ctlPath,
+ errno, strerror(errno));
+ goto DETACH2;
+ }
+
+ close(fd);
+#elif HAVE_TTRACE
+ if (ttrace(TT_PROC_ATTACH, processId, 0, TT_DETACH_ON_EXIT, TT_VERSION, 0) == -1)
+ {
+ if (errno != ESRCH)
+ {
+ ASSERT("ttrace(TT_PROC_ATTACH, pid:%d) failed errno:%d (%s)\n",
+ processId, errno, strerror(errno));
+ }
+ goto DETACH1;
+ }
+#else // HAVE_TTRACE
+ if (PAL_PTRACE( PAL_PT_ATTACH, processId, 0, 0 ) == -1)
+ {
+ if (errno != ESRCH)
+ {
+ ASSERT("ptrace(PT_ATTACH, pid:%d) failed errno:%d (%s)\n",
+ processId, errno, strerror(errno));
+ }
+ goto DETACH1;
+ }
+
+ if (waitpid(processId, NULL, WUNTRACED) == -1)
+ {
+ if (errno != ESRCH)
+ {
+ ASSERT("waitpid(pid:%d, NULL, WUNTRACED) failed.errno:%d"
+ " (%s)\n", processId, errno, strerror(errno));
+ }
+ goto DETACH2;
+ }
+#endif // HAVE_PROCFS_CTL
+ }
+
+ return TRUE;
+
+#if HAVE_PROCFS_CTL
+DETACH2:
+ if (write(fd, CTL_DETACH, sizeof(CTL_DETACH)) < (int)sizeof(CTL_DETACH))
+ {
+ ASSERT("Failed to detach from %s: errno is %d (%s)\n", ctlPath,
+ errno, strerror(errno));
+ }
+ close(fd);
+#elif !HAVE_TTRACE
+DETACH2:
+ if (PAL_PTRACE(PAL_PT_DETACH, processId, 0, 0) == -1)
+ {
+ ASSERT("ptrace(PT_DETACH, pid:%d) failed. errno:%d (%s)\n", processId,
+ errno, strerror(errno));
+ }
+#endif // HAVE_PROCFS_CTL
+
+DETACH1:
+ savedErrno = errno;
+ DBGSetProcessAttached(pThread, hProcess, DBG_DETACH);
+ errno = savedErrno;
+EXIT:
+ if (errno == ESRCH || errno == ENOENT || errno == EBADF)
+ {
+ ERROR("Invalid process ID:%d\n", processId);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ else
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ return FALSE;
+}
+
+/*++
+Function:
+ DBGDetachProcess
+
+Abstract
+ Detach the indicated process from the current process.
+
+ if the indicated process is already attached by the current process, then
+ decrement the number of attachment pending and detach it from the current
+ process (with PT_DETACH) if there's no more attachment left.
+
+Parameter
+ hProcess : process handle
+ processId : process ID
+
+Return
+ Return true if it succeeds, or true if it's fails
+--*/
+static
+BOOL
+DBGDetachProcess(
+ CPalThread *pThread,
+ HANDLE hProcess,
+ DWORD processId
+ )
+{
+ int nbAttachLeft;
+#if HAVE_PROCFS_CTL
+ int fd = -1;
+ char ctlPath[1024];
+#endif // HAVE_PROCFS_CTL
+
+ nbAttachLeft = DBGSetProcessAttached(pThread, hProcess, DBG_DETACH);
+
+ if (nbAttachLeft == -1)
+ {
+ /* Failed to set the process as detached */
+ return FALSE;
+ }
+
+ /* check if there's no more attachment left on processId */
+ if (nbAttachLeft == 0)
+ {
+#if HAVE_PROCFS_CTL
+ sprintf(ctlPath, sizeof(ctlPath), "/proc/%d/ctl", processId);
+ fd = InternalOpen(pThread, ctlPath, O_WRONLY);
+ if (fd == -1)
+ {
+ if (errno == ENOENT)
+ {
+ ERROR("Invalid process ID: %d\n", processId);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ else
+ {
+ ERROR("Failed to open %s: errno is %d (%s)\n", ctlPath,
+ errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ return FALSE;
+ }
+
+ if (write(fd, CTL_DETACH, sizeof(CTL_DETACH)) < (int)sizeof(CTL_DETACH))
+ {
+ ERROR("Failed to detach from %s: errno is %d (%s)\n", ctlPath,
+ errno, strerror(errno));
+ close(fd);
+ return FALSE;
+ }
+ close(fd);
+
+#elif HAVE_TTRACE
+ if (ttrace(TT_PROC_DETACH, processId, 0, 0, 0, 0) == -1)
+ {
+ if (errno == ESRCH)
+ {
+ ERROR("Invalid process ID: %d\n", processId);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ else
+ {
+ ASSERT("ttrace(TT_PROC_DETACH, pid:%d) failed. errno:%d (%s)\n",
+ processId, errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ return FALSE;
+ }
+#else // HAVE_TTRACE
+ if (PAL_PTRACE(PAL_PT_DETACH, processId, 1, 0) == -1)
+ {
+ if (errno == ESRCH)
+ {
+ ERROR("Invalid process ID: %d\n", processId);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ else
+ {
+ ASSERT("ptrace(PT_DETACH, pid:%d) failed. errno:%d (%s)\n",
+ processId, errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ return FALSE;
+ }
+#endif // HAVE_PROCFS_CTL
+
+#if !HAVE_TTRACE
+ if (kill(processId, SIGCONT) == -1)
+ {
+ ERROR("Failed to continue the detached process:%d errno:%d (%s)\n",
+ processId, errno, strerror(errno));
+ return FALSE;
+ }
+#endif // !HAVE_TTRACE
+ }
+ return TRUE;
+}
+
+/*++
+Function:
+ DBGSetProcessAttached
+
+Abstract
+ saves the current process Id in the attached process structure
+
+Parameter
+ hProcess : process handle
+ bAttach : true (false) to set the process as attached (as detached)
+Return
+ returns the number of attachment left on attachedProcId, or -1 if it fails
+--*/
+static int
+DBGSetProcessAttached(
+ CPalThread *pThread,
+ HANDLE hProcess,
+ BOOL bAttach
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjProcess = NULL;
+ IDataLock *pDataLock = NULL;
+ CProcProcessLocalData *pLocalData = NULL;
+ int ret = -1;
+ CAllowedObjectTypes aotProcess(otiProcess);
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hProcess,
+ &aotProcess,
+ 0,
+ &pobjProcess
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto DBGSetProcessAttachedExit;
+ }
+
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto DBGSetProcessAttachedExit;
+ }
+
+ if (bAttach)
+ {
+ pLocalData->lAttachCount += 1;
+ }
+ else
+ {
+ pLocalData->lAttachCount -= 1;
+
+ if (pLocalData->lAttachCount < 0)
+ {
+ ASSERT("pLocalData->lAttachCount < 0 check for extra DBGDetachProcess calls\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto DBGSetProcessAttachedExit;
+ }
+ }
+
+ ret = pLocalData->lAttachCount;
+
+DBGSetProcessAttachedExit:
+
+ if (NULL != pDataLock)
+ {
+ pDataLock->ReleaseLock(pThread, TRUE);
+ }
+
+ if (NULL != pobjProcess)
+ {
+ pobjProcess->ReleaseReference(pThread);
+ }
+
+ return ret;
+}
+
+#endif // !HAVE_VM_READ && !HAVE_PROCFS_CTL
+
+/*++
+Function:
+ PAL_CreateExecWatchpoint
+
+Abstract
+ Creates an OS exec watchpoint for the specified instruction
+ and thread. This function should only be called on architectures
+ that do not support a hardware single-step mode (e.g., SPARC).
+
+Parameter
+ hThread : the thread for which the watchpoint is to apply
+ pvInstruction : the instruction on which the watchpoint is to be set
+
+Return
+ A Win32 error code
+--*/
+
+DWORD
+PAL_CreateExecWatchpoint(
+ HANDLE hThread,
+ PVOID pvInstruction
+ )
+{
+ PERF_ENTRY(PAL_CreateExecWatchpoint);
+ ENTRY("PAL_CreateExecWatchpoint (hThread=%p, pvInstruction=%p)\n", hThread, pvInstruction);
+
+ DWORD dwError = ERROR_NOT_SUPPORTED;
+
+#if HAVE_PRWATCH_T
+
+ CPalThread *pThread = NULL;
+ CPalThread *pTargetThread = NULL;
+ IPalObject *pobjThread = NULL;
+ int fd = -1;
+ char ctlPath[50];
+
+ struct
+ {
+ long ctlCode;
+ prwatch_t prwatch;
+ } ctlStruct;
+
+ //
+ // We must never set a watchpoint on an instruction that enters a syscall;
+ // if such a request comes in we succeed it w/o actually creating the
+ // watchpoint. This mirrors the behavior of setting the single-step flag
+ // in a thread context when the thread is w/in a system service -- the
+ // flag is ignored and will not be present when the thread returns
+ // to user mode.
+ //
+
+#if defined(_SPARC_)
+ if (*(DWORD*)pvInstruction == 0x91d02008) // ta 8
+ {
+ TRACE("Watchpoint requested on sysenter instruction -- ignoring");
+ dwError = ERROR_SUCCESS;
+ goto PAL_CreateExecWatchpointExit;
+ }
+#else
+#error Need syscall instruction for this platform
+#endif // _SPARC_
+
+ pThread = InternalGetCurrentThread();
+
+ dwError = InternalGetThreadDataFromHandle(
+ pThread,
+ hThread,
+ 0, // THREAD_SET_CONTEXT
+ &pTargetThread,
+ &pobjThread
+ );
+
+ if (NO_ERROR != dwError)
+ {
+ goto PAL_CreateExecWatchpointExit;
+ }
+
+ snprintf(ctlPath, sizeof(ctlPath), "/proc/%u/lwp/%u/lwpctl", getpid(), pTargetThread->GetLwpId());
+
+ fd = InternalOpen(pThread, ctlPath, O_WRONLY);
+ if (-1 == fd)
+ {
+ ERROR("Failed to open %s\n", ctlPath);
+ dwError = ERROR_INVALID_ACCESS;
+ goto PAL_CreateExecWatchpointExit;
+ }
+
+ ctlStruct.ctlCode = PCWATCH;
+ ctlStruct.prwatch.pr_vaddr = (uintptr_t) pvInstruction;
+ ctlStruct.prwatch.pr_size = sizeof(DWORD);
+ ctlStruct.prwatch.pr_wflags = WA_EXEC | WA_TRAPAFTER;
+
+ if (write(fd, (void*) &ctlStruct, sizeof(ctlStruct)) != sizeof(ctlStruct))
+ {
+ ERROR("Failure writing control structure (errno = %u)\n", errno);
+ dwError = ERROR_INTERNAL_ERROR;
+ goto PAL_CreateExecWatchpointExit;
+ }
+
+ dwError = ERROR_SUCCESS;
+
+PAL_CreateExecWatchpointExit:
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pThread);
+ }
+
+ if (-1 != fd)
+ {
+ close(fd);
+ }
+
+#endif // HAVE_PRWATCH_T
+
+ LOGEXIT("PAL_CreateExecWatchpoint returns ret:%d\n", dwError);
+ PERF_EXIT(PAL_CreateExecWatchpoint);
+ return dwError;
+}
+
+/*++
+Function:
+ PAL_DeleteExecWatchpoint
+
+Abstract
+ Deletes an OS exec watchpoint for the specified instruction
+ and thread. This function should only be called on architectures
+ that do not support a hardware single-step mode (e.g., SPARC).
+
+Parameter
+ hThread : the thread to remove the watchpoint from
+ pvInstruction : the instruction for which the watchpoint is to be removed
+
+Return
+ A Win32 error code. Attempting to delete a watchpoint that does not exist
+ may or may not result in an error, depending on the behavior of the
+ underlying operating system.
+--*/
+
+DWORD
+PAL_DeleteExecWatchpoint(
+ HANDLE hThread,
+ PVOID pvInstruction
+ )
+{
+ PERF_ENTRY(PAL_DeleteExecWatchpoint);
+ ENTRY("PAL_DeleteExecWatchpoint (hThread=%p, pvInstruction=%p)\n", hThread, pvInstruction);
+
+ DWORD dwError = ERROR_NOT_SUPPORTED;
+
+#if HAVE_PRWATCH_T
+
+ CPalThread *pThread = NULL;
+ CPalThread *pTargetThread = NULL;
+ IPalObject *pobjThread = NULL;
+ int fd = -1;
+ char ctlPath[50];
+
+ struct
+ {
+ long ctlCode;
+ prwatch_t prwatch;
+ } ctlStruct;
+
+
+ pThread = InternalGetCurrentThread();
+
+ dwError = InternalGetThreadDataFromHandle(
+ pThread,
+ hThread,
+ 0, // THREAD_SET_CONTEXT
+ &pTargetThread,
+ &pobjThread
+ );
+
+ if (NO_ERROR != dwError)
+ {
+ goto PAL_DeleteExecWatchpointExit;
+ }
+
+ snprintf(ctlPath, sizeof(ctlPath), "/proc/%u/lwp/%u/lwpctl", getpid(), pTargetThread->GetLwpId());
+
+ fd = InternalOpen(pThread, ctlPath, O_WRONLY);
+ if (-1 == fd)
+ {
+ ERROR("Failed to open %s\n", ctlPath);
+ dwError = ERROR_INVALID_ACCESS;
+ goto PAL_DeleteExecWatchpointExit;
+ }
+
+ ctlStruct.ctlCode = PCWATCH;
+ ctlStruct.prwatch.pr_vaddr = (uintptr_t) pvInstruction;
+ ctlStruct.prwatch.pr_size = sizeof(DWORD);
+ ctlStruct.prwatch.pr_wflags = 0;
+
+ if (write(fd, (void*) &ctlStruct, sizeof(ctlStruct)) != sizeof(ctlStruct))
+ {
+ ERROR("Failure writing control structure (errno = %u)\n", errno);
+ dwError = ERROR_INTERNAL_ERROR;
+ goto PAL_DeleteExecWatchpointExit;
+ }
+
+ dwError = ERROR_SUCCESS;
+
+PAL_DeleteExecWatchpointExit:
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pThread);
+ }
+
+ if (-1 != fd)
+ {
+ close(fd);
+ }
+
+#endif // HAVE_PRWATCH_T
+
+ LOGEXIT("PAL_DeleteExecWatchpoint returns ret:%d\n", dwError);
+ PERF_EXIT(PAL_DeleteExecWatchpoint);
+ return dwError;
+}
+
+// We want to enable hardware exception handling for ReadProcessMemory
+// and WriteProcessMemory in all cases since it is acceptable if they
+// hit AVs, so redefine HardwareExceptionHolder for these two functions
+// (here to the end of the file).
+#undef HardwareExceptionHolder
+#define HardwareExceptionHolder CatchHardwareExceptionHolder __catchHardwareException;
+
+/*++
+Function:
+ ReadProcessMemory
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+ReadProcessMemory(
+ IN HANDLE hProcess,
+ IN LPCVOID lpBaseAddress,
+ IN LPVOID lpBuffer,
+ IN SIZE_T nSize,
+ OUT SIZE_T * lpNumberOfBytesRead
+ )
+{
+ CPalThread *pThread;
+ DWORD processId;
+ Volatile<BOOL> ret = FALSE;
+ Volatile<SIZE_T> numberOfBytesRead = 0;
+#if HAVE_VM_READ
+ kern_return_t result;
+ vm_map_t task;
+ LONG_PTR bytesToRead;
+#elif HAVE_PROCFS_CTL
+ int fd = -1;
+ char memPath[64];
+ off_t offset;
+#elif !HAVE_TTRACE
+ SIZE_T nbInts;
+ int* ptrInt;
+ int* lpTmpBuffer;
+#endif
+#if !HAVE_PROCFS_CTL && !HAVE_TTRACE
+ int* lpBaseAddressAligned;
+ SIZE_T offset;
+#endif // !HAVE_PROCFS_CTL && !HAVE_TTRACE
+
+ PERF_ENTRY(ReadProcessMemory);
+ ENTRY("ReadProcessMemory (hProcess=%p,lpBaseAddress=%p, lpBuffer=%p, "
+ "nSize=%u, lpNumberOfBytesRead=%p)\n",hProcess,lpBaseAddress,
+ lpBuffer, (unsigned int)nSize, lpNumberOfBytesRead);
+
+ pThread = InternalGetCurrentThread();
+
+ if (!(processId = PROCGetProcessIDFromHandle(hProcess)))
+ {
+ ERROR("Invalid process handler hProcess:%p.",hProcess);
+ SetLastError(ERROR_INVALID_HANDLE);
+ goto EXIT;
+ }
+
+ // Check if the read request is for the current process.
+ // We don't need ptrace in that case.
+ if (GetCurrentProcessId() == processId)
+ {
+ TRACE("We are in the same process, so ptrace is not needed\n");
+
+ struct Param
+ {
+ LPCVOID lpBaseAddress;
+ LPVOID lpBuffer;
+ SIZE_T nSize;
+ SIZE_T numberOfBytesRead;
+ BOOL ret;
+ } param;
+ param.lpBaseAddress = lpBaseAddress;
+ param.lpBuffer = lpBuffer;
+ param.nSize = nSize;
+ param.numberOfBytesRead = numberOfBytesRead;
+ param.ret = ret;
+
+ PAL_TRY(Param *, pParam, &param)
+ {
+ SIZE_T i;
+
+ // Seg fault in memcpy can't be caught
+ // so we simulate the memcpy here
+
+ for (i = 0; i<pParam->nSize; i++)
+ {
+ *((char*)(pParam->lpBuffer)+i) = *((char*)(pParam->lpBaseAddress)+i);
+ }
+
+ pParam->numberOfBytesRead = pParam->nSize;
+ pParam->ret = TRUE;
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastError(ERROR_ACCESS_DENIED);
+ }
+ PAL_ENDTRY
+
+ numberOfBytesRead = param.numberOfBytesRead;
+ ret = param.ret;
+ goto EXIT;
+ }
+
+#if HAVE_VM_READ
+ result = task_for_pid(mach_task_self(), processId, &task);
+ if (result != KERN_SUCCESS)
+ {
+ ERROR("No Mach task for pid %d: %d\n", processId, ret.Load());
+ SetLastError(ERROR_INVALID_HANDLE);
+ goto EXIT;
+ }
+ // vm_read_overwrite usually requires that the address be page-aligned
+ // and the size be a multiple of the page size. We can't differentiate
+ // between the cases in which that's required and those in which it
+ // isn't, so we do it all the time.
+ lpBaseAddressAligned = (int*)((SIZE_T) lpBaseAddress & ~VIRTUAL_PAGE_MASK);
+ offset = ((SIZE_T) lpBaseAddress & VIRTUAL_PAGE_MASK);
+ char *data;
+ data = (char*)alloca(VIRTUAL_PAGE_SIZE);
+ while (nSize > 0)
+ {
+ vm_size_t bytesRead;
+
+ bytesToRead = VIRTUAL_PAGE_SIZE - offset;
+ if (bytesToRead > (LONG_PTR)nSize)
+ {
+ bytesToRead = nSize;
+ }
+ bytesRead = VIRTUAL_PAGE_SIZE;
+ result = vm_read_overwrite(task, (vm_address_t) lpBaseAddressAligned,
+ VIRTUAL_PAGE_SIZE, (vm_address_t) data, &bytesRead);
+ if (result != KERN_SUCCESS || bytesRead != VIRTUAL_PAGE_SIZE)
+ {
+ ERROR("vm_read_overwrite failed for %d bytes from %p in %d: %d\n",
+ VIRTUAL_PAGE_SIZE, (char *) lpBaseAddressAligned, task, result);
+ if (result <= KERN_RETURN_MAX)
+ {
+ SetLastError(ERROR_INVALID_ACCESS);
+ }
+ else
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ goto EXIT;
+ }
+ memcpy((LPSTR)lpBuffer + numberOfBytesRead, data + offset, bytesToRead);
+ numberOfBytesRead.Store(numberOfBytesRead.Load() + bytesToRead);
+ lpBaseAddressAligned = (int*)((char*)lpBaseAddressAligned + VIRTUAL_PAGE_SIZE);
+ nSize -= bytesToRead;
+ offset = 0;
+ }
+ ret = TRUE;
+#else // HAVE_VM_READ
+#if HAVE_PROCFS_CTL
+ snprintf(memPath, sizeof(memPath), "/proc/%u/%s", processId, PROCFS_MEM_NAME);
+ fd = InternalOpen(memPath, O_RDONLY);
+ if (fd == -1)
+ {
+ ERROR("Failed to open %s\n", memPath);
+ SetLastError(ERROR_INVALID_ACCESS);
+ goto PROCFSCLEANUP;
+ }
+
+ //
+ // off_t may be greater in size than void*, so first cast to
+ // an unsigned type to ensure that no sign extension takes place
+ //
+
+ offset = (off_t) (UINT_PTR) lpBaseAddress;
+
+ if (lseek(fd, offset, SEEK_SET) == -1)
+ {
+ ERROR("Failed to seek to base address\n");
+ SetLastError(ERROR_INVALID_ACCESS);
+ goto PROCFSCLEANUP;
+ }
+
+ numberOfBytesRead = read(fd, lpBuffer, nSize);
+ ret = TRUE;
+
+#else // HAVE_PROCFS_CTL
+ // Attach the process before calling ttrace/ptrace otherwise it fails.
+ if (DBGAttachProcess(pThread, hProcess, processId))
+ {
+#if HAVE_TTRACE
+ if (ttrace(TT_PROC_RDDATA, processId, 0, (__uint64_t)lpBaseAddress, (__uint64_t)nSize, (__uint64_t)lpBuffer) == -1)
+ {
+ if (errno == EFAULT)
+ {
+ ERROR("ttrace(TT_PROC_RDDATA, pid:%d, 0, addr:%p, data:%d, addr2:%d) failed"
+ " errno=%d (%s)\n", processId, lpBaseAddress, (int)nSize, lpBuffer,
+ errno, strerror(errno));
+
+ SetLastError(ERROR_ACCESS_DENIED);
+ }
+ else
+ {
+ ASSERT("ttrace(TT_PROC_RDDATA, pid:%d, 0, addr:%p, data:%d, addr2:%d) failed"
+ " errno=%d (%s)\n", processId, lpBaseAddress, (int)nSize, lpBuffer,
+ errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+
+ goto CLEANUP1;
+ }
+
+ numberOfBytesRead = nSize;
+ ret = TRUE;
+
+#else // HAVE_TTRACE
+
+ offset = (SIZE_T)lpBaseAddress % sizeof(int);
+ lpBaseAddressAligned = (int*)((char*)lpBaseAddress - offset);
+ nbInts = (nSize + offset)/sizeof(int) +
+ ((nSize + offset)%sizeof(int) ? 1:0);
+
+ /* before transferring any data to lpBuffer we should make sure that all
+ data is accessible for read. so we need to use a temp buffer for that.*/
+ if (!(lpTmpBuffer = (int*)InternalMalloc((nbInts * sizeof(int)))))
+ {
+ ERROR("Insufficient memory available !\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto CLEANUP1;
+ }
+
+ for (ptrInt = lpTmpBuffer; nbInts; ptrInt++,
+ lpBaseAddressAligned++, nbInts--)
+ {
+ errno = 0;
+ *ptrInt =
+ PAL_PTRACE(PAL_PT_READ_D, processId, lpBaseAddressAligned, 0);
+ if (*ptrInt == -1 && errno)
+ {
+ if (errno == EFAULT)
+ {
+ ERROR("ptrace(PT_READ_D, pid:%d, addr:%p, data:0) failed"
+ " errno=%d (%s)\n", processId, lpBaseAddressAligned,
+ errno, strerror(errno));
+
+ SetLastError(ptrInt == lpTmpBuffer ? ERROR_ACCESS_DENIED :
+ ERROR_PARTIAL_COPY);
+ }
+ else
+ {
+ ASSERT("ptrace(PT_READ_D, pid:%d, addr:%p, data:0) failed"
+ " errno=%d (%s)\n", processId, lpBaseAddressAligned,
+ errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+
+ goto CLEANUP2;
+ }
+ }
+
+ /* transfer data from temp buffer to lpBuffer */
+ memcpy( (char *)lpBuffer, ((char*)lpTmpBuffer) + offset, nSize);
+ numberOfBytesRead = nSize;
+ ret = TRUE;
+#endif // HAVE_TTRACE
+ }
+ else
+ {
+ /* Failed to attach processId */
+ goto EXIT;
+ }
+#endif // HAVE_PROCFS_CTL
+
+#if HAVE_PROCFS_CTL
+PROCFSCLEANUP:
+ if (fd != -1)
+ {
+ close(fd);
+ }
+#elif !HAVE_TTRACE
+CLEANUP2:
+ if (lpTmpBuffer)
+ {
+ free(lpTmpBuffer);
+ }
+#endif // !HAVE_TTRACE
+
+#if !HAVE_PROCFS_CTL
+CLEANUP1:
+ if (!DBGDetachProcess(pThread, hProcess, processId))
+ {
+ /* Failed to detach processId */
+ ret = FALSE;
+ }
+#endif // HAVE_PROCFS_CTL
+#endif // HAVE_VM_READ
+
+EXIT:
+ if (lpNumberOfBytesRead)
+ {
+ *lpNumberOfBytesRead = numberOfBytesRead;
+ }
+ LOGEXIT("ReadProcessMemory returns BOOL %d\n", ret.Load());
+ PERF_EXIT(ReadProcessMemory);
+ return ret;
+}
+
+/*++
+Function:
+ WriteProcessMemory
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+WriteProcessMemory(
+ IN HANDLE hProcess,
+ IN LPVOID lpBaseAddress,
+ IN LPCVOID lpBuffer,
+ IN SIZE_T nSize,
+ OUT SIZE_T * lpNumberOfBytesWritten
+ )
+
+{
+ CPalThread *pThread;
+ DWORD processId;
+ Volatile<BOOL> ret = FALSE;
+ Volatile<SIZE_T> numberOfBytesWritten = 0;
+#if HAVE_VM_READ
+ kern_return_t result;
+ vm_map_t task;
+#elif HAVE_PROCFS_CTL
+ int fd = -1;
+ char memPath[64];
+ LONG_PTR bytesWritten;
+ off_t offset;
+#elif !HAVE_TTRACE
+ SIZE_T FirstIntOffset;
+ SIZE_T LastIntOffset;
+ unsigned int FirstIntMask;
+ unsigned int LastIntMask;
+ SIZE_T nbInts;
+ int *lpTmpBuffer = 0, *lpInt;
+ int* lpBaseAddressAligned;
+#endif
+
+ PERF_ENTRY(WriteProcessMemory);
+ ENTRY("WriteProcessMemory (hProcess=%p,lpBaseAddress=%p, lpBuffer=%p, "
+ "nSize=%u, lpNumberOfBytesWritten=%p)\n",
+ hProcess,lpBaseAddress, lpBuffer, (unsigned int)nSize, lpNumberOfBytesWritten);
+
+ pThread = InternalGetCurrentThread();
+
+ if (!(nSize && (processId = PROCGetProcessIDFromHandle(hProcess))))
+ {
+ ERROR("Invalid nSize:%u number or invalid process handler "
+ "hProcess:%p\n", (unsigned int)nSize, hProcess);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto EXIT;
+ }
+
+ // Check if the write request is for the current process.
+ // In that case we don't need ptrace.
+ if (GetCurrentProcessId() == processId)
+ {
+ TRACE("We are in the same process so we don't need ptrace\n");
+
+ struct Param
+ {
+ LPVOID lpBaseAddress;
+ LPCVOID lpBuffer;
+ SIZE_T nSize;
+ SIZE_T numberOfBytesWritten;
+ BOOL ret;
+ } param;
+ param.lpBaseAddress = lpBaseAddress;
+ param.lpBuffer = lpBuffer;
+ param.nSize = nSize;
+ param.numberOfBytesWritten = numberOfBytesWritten;
+ param.ret = ret;
+
+ PAL_TRY(Param *, pParam, &param)
+ {
+ SIZE_T i;
+
+ // Seg fault in memcpy can't be caught
+ // so we simulate the memcpy here
+
+ for (i = 0; i<pParam->nSize; i++)
+ {
+ *((char*)(pParam->lpBaseAddress)+i) = *((char*)(pParam->lpBuffer)+i);
+ }
+
+ pParam->numberOfBytesWritten = pParam->nSize;
+ pParam->ret = TRUE;
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastError(ERROR_ACCESS_DENIED);
+ }
+ PAL_ENDTRY
+
+ numberOfBytesWritten = param.numberOfBytesWritten;
+ ret = param.ret;
+ goto EXIT;
+ }
+
+#if HAVE_VM_READ
+ result = task_for_pid(mach_task_self(), processId, &task);
+ if (result != KERN_SUCCESS)
+ {
+ ERROR("No Mach task for pid %d: %d\n", processId, ret.Load());
+ SetLastError(ERROR_INVALID_HANDLE);
+ goto EXIT;
+ }
+ result = vm_write(task, (vm_address_t) lpBaseAddress,
+ (vm_address_t) lpBuffer, nSize);
+ if (result != KERN_SUCCESS)
+ {
+ ERROR("vm_write failed for %d bytes from %p in %d: %d\n",
+ (int)nSize, lpBaseAddress, task, result);
+ if (result <= KERN_RETURN_MAX)
+ {
+ SetLastError(ERROR_ACCESS_DENIED);
+ }
+ else
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ goto EXIT;
+ }
+ numberOfBytesWritten = nSize;
+ ret = TRUE;
+#else // HAVE_VM_READ
+#if HAVE_PROCFS_CTL
+ snprintf(memPath, sizeof(memPath), "/proc/%u/%s", processId, PROCFS_MEM_NAME);
+ fd = InternalOpen(memPath, O_WRONLY);
+ if (fd == -1)
+ {
+ ERROR("Failed to open %s\n", memPath);
+ SetLastError(ERROR_INVALID_ACCESS);
+ goto PROCFSCLEANUP;
+ }
+
+ //
+ // off_t may be greater in size than void*, so first cast to
+ // an unsigned type to ensure that no sign extension takes place
+ //
+
+ offset = (off_t) (UINT_PTR) lpBaseAddress;
+
+ if (lseek(fd, offset, SEEK_SET) == -1)
+ {
+ ERROR("Failed to seek to base address\n");
+ SetLastError(ERROR_INVALID_ACCESS);
+ goto PROCFSCLEANUP;
+ }
+
+ bytesWritten = write(fd, lpBuffer, nSize);
+ if (bytesWritten < 0)
+ {
+ ERROR("Failed to write to %s\n", memPath);
+ SetLastError(ERROR_INVALID_ACCESS);
+ goto PROCFSCLEANUP;
+ }
+
+ numberOfBytesWritten = bytesWritten;
+ ret = TRUE;
+
+#else // HAVE_PROCFS_CTL
+ /* Attach the process before calling ptrace otherwise it fails */
+ if (DBGAttachProcess(pThread, hProcess, processId))
+ {
+#if HAVE_TTRACE
+ if (ttrace(TT_PROC_WRDATA, processId, 0, (__uint64_t)lpBaseAddress, (__uint64_t)nSize, (__uint64_t)lpBuffer) == -1)
+ {
+ if (errno == EFAULT)
+ {
+ ERROR("ttrace(TT_PROC_WRDATA, pid:%d, addr:%p, data:%d, addr2:%d) failed"
+ " errno=%d (%s)\n", processId, lpBaseAddress, nSize, lpBuffer,
+ errno, strerror(errno));
+
+ SetLastError(ERROR_ACCESS_DENIED);
+ }
+ else
+ {
+ ASSERT("ttrace(TT_PROC_WRDATA, pid:%d, addr:%p, data:%d, addr2:%d) failed"
+ " errno=%d (%s)\n", processId, lpBaseAddress, nSize, lpBuffer,
+ errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+
+ goto CLEANUP1;
+ }
+
+ numberOfBytesWritten = nSize;
+ ret = TRUE;
+
+#else // HAVE_TTRACE
+
+ FirstIntOffset = (SIZE_T)lpBaseAddress % sizeof(int);
+ FirstIntMask = -1;
+ FirstIntMask <<= (FirstIntOffset * 8);
+
+ nbInts = (nSize + FirstIntOffset) / sizeof(int) +
+ (((nSize + FirstIntOffset)%sizeof(int)) ? 1:0);
+ lpBaseAddressAligned = (int*)((char*)lpBaseAddress - FirstIntOffset);
+
+ if ((lpTmpBuffer = (int*)InternalMalloc((nbInts * sizeof(int)))) == NULL)
+ {
+ ERROR("Insufficient memory available !\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto CLEANUP1;
+ }
+
+ memcpy((char *)lpTmpBuffer + FirstIntOffset, (char *)lpBuffer, nSize);
+ lpInt = lpTmpBuffer;
+
+ LastIntOffset = (nSize + FirstIntOffset) % sizeof(int);
+ LastIntMask = -1;
+ LastIntMask >>= ((sizeof(int) - LastIntOffset) * 8);
+
+ if (nbInts == 1)
+ {
+ if (DBGWriteProcMem_IntWithMask(processId, lpBaseAddressAligned,
+ *lpInt,
+ LastIntMask & FirstIntMask)
+ == 0)
+ {
+ goto CLEANUP2;
+ }
+ numberOfBytesWritten = nSize;
+ ret = TRUE;
+ goto CLEANUP2;
+ }
+
+ if (DBGWriteProcMem_IntWithMask(processId,
+ lpBaseAddressAligned++,
+ *lpInt++, FirstIntMask)
+ == 0)
+ {
+ goto CLEANUP2;
+ }
+
+ while (--nbInts > 1)
+ {
+ if (DBGWriteProcMem_Int(processId, lpBaseAddressAligned++,
+ *lpInt++) == 0)
+ {
+ goto CLEANUP2;
+ }
+ }
+
+ if (DBGWriteProcMem_IntWithMask(processId, lpBaseAddressAligned,
+ *lpInt, LastIntMask ) == 0)
+ {
+ goto CLEANUP2;
+ }
+
+ numberOfBytesWritten = nSize;
+ ret = TRUE;
+#endif // HAVE_TTRACE
+ }
+ else
+ {
+ /* Failed to attach processId */
+ goto EXIT;
+ }
+#endif // HAVE_PROCFS_CTL
+
+#if HAVE_PROCFS_CTL
+PROCFSCLEANUP:
+ if (fd != -1)
+ {
+ close(fd);
+ }
+#elif !HAVE_TTRACE
+CLEANUP2:
+ if (lpTmpBuffer)
+ {
+ free(lpTmpBuffer);
+ }
+#endif // !HAVE_TTRACE
+
+#if !HAVE_PROCFS_CTL
+CLEANUP1:
+ if (!DBGDetachProcess(pThread, hProcess, processId))
+ {
+ /* Failed to detach processId */
+ ret = FALSE;
+ }
+#endif // !HAVE_PROCFS_CTL
+#endif // HAVE_VM_READ
+
+EXIT:
+ if (lpNumberOfBytesWritten)
+ {
+ *lpNumberOfBytesWritten = numberOfBytesWritten;
+ }
+
+ LOGEXIT("WriteProcessMemory returns BOOL %d\n", ret.Load());
+ PERF_EXIT(WriteProcessMemory);
+ return ret;
+}
+
+} // extern "C"
diff --git a/src/pal/src/examples/CMakeLists.txt b/src/pal/src/examples/CMakeLists.txt
new file mode 100644
index 0000000000..2cef914892
--- /dev/null
+++ b/src/pal/src/examples/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+project(palexmpl)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ example1.c
+)
+
+add_executable(palexmpl
+ ${SOURCES}
+)
+
+add_dependencies(palexmpl coreclrpal)
+
+target_link_libraries(palexmpl
+ coreclrpal
+)
diff --git a/src/pal/src/examples/example1.c b/src/pal/src/examples/example1.c
new file mode 100644
index 0000000000..071f42e4f9
--- /dev/null
+++ b/src/pal/src/examples/example1.c
@@ -0,0 +1,49 @@
+// 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.
+
+/*
+
+ *
+ * Example of minimal program running under PAL.
+ *
+ * Run it using:
+ * export PAL_DBG_CHANNELS="+all.all"
+ * export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:..
+ * ./example1
+ *
+ * With the PAL_DEBUG_CHANNELS environment variable set as above you
+ * should see a trace output when the program runs. Setting
+ * LD_LIBRARY_PATH is necessary unless you have installed librotor_pal.so in
+ * a standard location.
+ *
+ * Build notes :
+ * Since the PAL uses pthreads, some options must be passed to gcc to tell it
+ * to link against thread-safe versions of the standard libraries.
+ * On FreeBSD, use gcc -pthread
+ *
+ */
+
+#include <pal.h>
+extern void *dlopen(const char *file, int mode);
+
+int main(int argc, char *argv[])
+{
+ WCHAR src[4] = {'f', 'o', 'o', '\0'};
+ WCHAR dest[4] = {'b', 'a', 'r', '\0'};
+ WCHAR dir[5] = {'/', 't', 'm', 'p', '\0'};
+ HANDLE h;
+ unsigned int b;
+
+ PAL_Initialize(argc, (const char**)argv);
+ SetCurrentDirectoryW(dir);
+ SetCurrentDirectoryW(dir);
+ h = CreateFileW(src, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, 0, NULL);
+ WriteFile(h, "Testing\n", 8, &b, FALSE);
+ CloseHandle(h);
+ CopyFileW(src, dest, FALSE);
+ DeleteFileW(src);
+ PAL_Terminate();
+ return 0;
+}
+
diff --git a/src/pal/src/exception/machexception.cpp b/src/pal/src/exception/machexception.cpp
new file mode 100644
index 0000000000..8b0d7f22a8
--- /dev/null
+++ b/src/pal/src/exception/machexception.cpp
@@ -0,0 +1,1612 @@
+// 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.
+
+/*++
+
+Module Name:
+
+ machexception.cpp
+
+Abstract:
+
+ Implementation of MACH exception API functions.
+
+--*/
+
+#include "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do this first
+
+#include "pal/thread.hpp"
+#include "pal/seh.hpp"
+#include "pal/palinternal.h"
+#if HAVE_MACH_EXCEPTIONS
+#include "machexception.h"
+#include "pal/critsect.h"
+#include "pal/debug.h"
+#include "pal/init.h"
+#include "pal/utils.h"
+#include "pal/context.h"
+#include "pal/malloc.hpp"
+#include "pal/process.h"
+#include "pal/virtual.h"
+#include "pal/map.hpp"
+#include "pal/environ.h"
+
+#include "machmessage.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <dlfcn.h>
+#include <mach-o/loader.h>
+
+using namespace CorUnix;
+
+// The port we use to handle exceptions and to set the thread context
+mach_port_t s_ExceptionPort;
+
+static BOOL s_DebugInitialized = FALSE;
+
+static DWORD s_PalInitializeFlags = 0;
+
+static const char * PAL_MACH_EXCEPTION_MODE = "PAL_MachExceptionMode";
+
+// This struct is used to track the threads that need to have an exception forwarded
+// to the next thread level port in the chain (if exists). An entry is added by the
+// faulting sending a special message to the exception thread which saves it on an
+// list that is searched when the restarted exception notification is received again.
+struct ForwardedException
+{
+ ForwardedException *m_next;
+ thread_act_t Thread;
+ exception_type_t ExceptionType;
+ CPalThread *PalThread;
+};
+
+// The singly linked list and enumerator for the ForwardException struct
+struct ForwardedExceptionList
+{
+private:
+ ForwardedException *m_head;
+ ForwardedException *m_previous;
+
+public:
+ ForwardedException *Current;
+
+ ForwardedExceptionList()
+ {
+ m_head = NULL;
+ MoveFirst();
+ }
+
+ void MoveFirst()
+ {
+ Current = m_head;
+ m_previous = NULL;
+ }
+
+ bool IsEOL()
+ {
+ return Current == NULL;
+ }
+
+ void MoveNext()
+ {
+ m_previous = Current;
+ Current = Current->m_next;
+ }
+
+ void Add(ForwardedException *item)
+ {
+ item->m_next = m_head;
+ m_head = item;
+ }
+
+ void Delete()
+ {
+ if (m_previous == NULL)
+ {
+ m_head = Current->m_next;
+ }
+ else
+ {
+ m_previous->m_next = Current->m_next;
+ }
+ free(Current);
+
+ Current = m_head;
+ m_previous = NULL;
+ }
+};
+
+enum MachExceptionMode
+{
+ // special value to indicate we've not initialized yet
+ MachException_Uninitialized = -1,
+
+ // These can be combined with bitwise OR to incrementally turn off
+ // functionality for diagnostics purposes.
+ //
+ // In practice, the following values are probably useful:
+ // 1: Don't turn illegal instructions into SEH exceptions.
+ // On Intel, stack misalignment usually shows up as an
+ // illegal instruction. PAL client code shouldn't
+ // expect to see any of these, so this option should
+ // always be safe to set.
+ // 2: Don't listen for breakpoint exceptions. This makes an
+ // SEH-based debugger (i.e., managed debugger) unusable,
+ // but you may need this option if you find that native
+ // breakpoints you set in PAL-dependent code don't work
+ // (causing hangs or crashes in the native debugger).
+ // 3: Combination of the above.
+ // This is the typical setting for development
+ // (unless you're working on the managed debugger).
+ // 7: In addition to the above, don't turn bad accesses and
+ // arithmetic exceptions into SEH.
+ // This is the typical setting for stress.
+ MachException_SuppressIllegal = 1,
+ MachException_SuppressDebugging = 2,
+ MachException_SuppressManaged = 4,
+
+ // Default value to use if environment variable not set.
+ MachException_Default = 0,
+};
+
+/*++
+Function :
+ GetExceptionMask()
+
+ Returns the mach exception mask for the exceptions to hook for a thread.
+
+Return value :
+ mach exception mask
+--*/
+static
+exception_mask_t
+GetExceptionMask()
+{
+ static MachExceptionMode exMode = MachException_Uninitialized;
+
+ if (exMode == MachException_Uninitialized)
+ {
+ exMode = MachException_Default;
+
+ char* exceptionSettings = EnvironGetenv(PAL_MACH_EXCEPTION_MODE);
+ if (exceptionSettings)
+ {
+ exMode = (MachExceptionMode)atoi(exceptionSettings);
+ free(exceptionSettings);
+ }
+ else
+ {
+ if (PAL_IsDebuggerPresent())
+ {
+ exMode = MachException_SuppressDebugging;
+ }
+ }
+ }
+
+ exception_mask_t machExceptionMask = 0;
+ if (!(exMode & MachException_SuppressIllegal))
+ {
+ machExceptionMask |= PAL_EXC_ILLEGAL_MASK;
+ }
+ if (!(exMode & MachException_SuppressDebugging) && (s_PalInitializeFlags & PAL_INITIALIZE_DEBUGGER_EXCEPTIONS))
+ {
+#ifdef FEATURE_PAL_SXS
+ // Always hook exception ports for breakpoint exceptions.
+ // The reason is that we don't know when a managed debugger
+ // will attach, so we have to be prepared. We don't want
+ // to later go through the thread list and hook exception
+ // ports for exactly those threads that currently are in
+ // this PAL.
+ machExceptionMask |= PAL_EXC_DEBUGGING_MASK;
+#else // FEATURE_PAL_SXS
+ if (s_DebugInitialized)
+ {
+ machExceptionMask |= PAL_EXC_DEBUGGING_MASK;
+ }
+#endif // FEATURE_PAL_SXS
+ }
+ if (!(exMode & MachException_SuppressManaged))
+ {
+ machExceptionMask |= PAL_EXC_MANAGED_MASK;
+ }
+
+ return machExceptionMask;
+}
+
+#ifdef FEATURE_PAL_SXS
+
+/*++
+Function :
+ CPalThread::EnableMachExceptions
+
+ Hook Mach exceptions, i.e., call thread_swap_exception_ports
+ to replace the thread's current exception ports with our own.
+ The previously active exception ports are saved. Called when
+ this thread enters a region of code that depends on this PAL.
+
+Return value :
+ ERROR_SUCCESS, if enabling succeeded
+ an error code, otherwise
+--*/
+PAL_ERROR CorUnix::CPalThread::EnableMachExceptions()
+{
+ TRACE("%08X: Enter()\n", (unsigned int)(size_t)this);
+
+ exception_mask_t machExceptionMask = GetExceptionMask();
+ if (machExceptionMask != 0)
+ {
+#ifdef _DEBUG
+ // verify that the arrays we've allocated to hold saved exception ports
+ // are the right size.
+ exception_mask_t countBits = PAL_EXC_ALL_MASK;
+ countBits = ((countBits & 0xAAAAAAAA) >> 1) + (countBits & 0x55555555);
+ countBits = ((countBits & 0xCCCCCCCC) >> 2) + (countBits & 0x33333333);
+ countBits = ((countBits & 0xF0F0F0F0) >> 4) + (countBits & 0x0F0F0F0F);
+ countBits = ((countBits & 0xFF00FF00) >> 8) + (countBits & 0x00FF00FF);
+ countBits = ((countBits & 0xFFFF0000) >> 16) + (countBits & 0x0000FFFF);
+ if (countBits != static_cast<exception_mask_t>(CThreadMachExceptionHandlers::s_nPortsMax))
+ {
+ ASSERT("s_nPortsMax is %u, but needs to be %u\n",
+ CThreadMachExceptionHandlers::s_nPortsMax, countBits);
+ }
+#endif // _DEBUG
+
+ NONPAL_TRACE("Enabling handlers for thread %08x exception mask %08x exception port %08x\n",
+ GetMachPortSelf(), machExceptionMask, s_ExceptionPort);
+
+ CThreadMachExceptionHandlers *pSavedHandlers = GetSavedMachHandlers();
+
+ // Swap current handlers into temporary storage first. That's because it's possible (even likely) that
+ // some or all of the handlers might still be ours. In those cases we don't want to overwrite the
+ // chain-back entries with these useless self-references.
+ kern_return_t machret;
+ kern_return_t machretDeallocate;
+ thread_port_t thread = mach_thread_self();
+
+ machret = thread_swap_exception_ports(
+ thread,
+ machExceptionMask,
+ s_ExceptionPort,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
+ THREAD_STATE_NONE,
+ pSavedHandlers->m_masks,
+ &pSavedHandlers->m_nPorts,
+ pSavedHandlers->m_handlers,
+ pSavedHandlers->m_behaviors,
+ pSavedHandlers->m_flavors);
+
+ machretDeallocate = mach_port_deallocate(mach_task_self(), thread);
+ CHECK_MACH("mach_port_deallocate", machretDeallocate);
+
+ if (machret != KERN_SUCCESS)
+ {
+ ASSERT("thread_swap_exception_ports failed: %d %s\n", machret, mach_error_string(machret));
+ return UTIL_MachErrorToPalError(machret);
+ }
+
+#ifdef _DEBUG
+ NONPAL_TRACE("EnableMachExceptions: THREAD PORT count %d\n", pSavedHandlers->m_nPorts);
+ for (mach_msg_type_number_t i = 0; i < pSavedHandlers->m_nPorts; i++)
+ {
+ _ASSERTE(pSavedHandlers->m_handlers[i] != s_ExceptionPort);
+ NONPAL_TRACE("EnableMachExceptions: THREAD PORT mask %08x handler: %08x behavior %08x flavor %u\n",
+ pSavedHandlers->m_masks[i],
+ pSavedHandlers->m_handlers[i],
+ pSavedHandlers->m_behaviors[i],
+ pSavedHandlers->m_flavors[i]);
+ }
+#endif // _DEBUG
+ }
+ return ERROR_SUCCESS;
+}
+
+/*++
+Function :
+ CPalThread::DisableMachExceptions
+
+ Unhook Mach exceptions, i.e., call thread_set_exception_ports
+ to restore the thread's exception ports with those we saved
+ in EnableMachExceptions. Called when this thread leaves a
+ region of code that depends on this PAL.
+
+Return value :
+ ERROR_SUCCESS, if disabling succeeded
+ an error code, otherwise
+--*/
+PAL_ERROR CorUnix::CPalThread::DisableMachExceptions()
+{
+ TRACE("%08X: Leave()\n", (unsigned int)(size_t)this);
+
+ PAL_ERROR palError = NO_ERROR;
+
+ // We only store exceptions when we're installing exceptions.
+ if (0 == GetExceptionMask())
+ return palError;
+
+ // Get the handlers to restore.
+ CThreadMachExceptionHandlers *savedPorts = GetSavedMachHandlers();
+
+ kern_return_t MachRet = KERN_SUCCESS;
+ for (int i = 0; i < savedPorts->m_nPorts; i++)
+ {
+ // If no handler was ever set, thread_swap_exception_ports returns
+ // MACH_PORT_NULL for the handler and zero values for behavior
+ // and flavor. Unfortunately, the latter are invalid even for
+ // MACH_PORT_NULL when you use thread_set_exception_ports.
+ exception_behavior_t behavior = savedPorts->m_behaviors[i] ? savedPorts->m_behaviors[i] : EXCEPTION_DEFAULT;
+ thread_state_flavor_t flavor = savedPorts->m_flavors[i] ? savedPorts->m_flavors[i] : MACHINE_THREAD_STATE;
+ thread_port_t thread = mach_thread_self();
+ MachRet = thread_set_exception_ports(thread,
+ savedPorts->m_masks[i],
+ savedPorts->m_handlers[i],
+ behavior,
+ flavor);
+
+ kern_return_t MachRetDeallocate = mach_port_deallocate(mach_task_self(), thread);
+ CHECK_MACH("mach_port_deallocate", MachRetDeallocate);
+
+ if (MachRet != KERN_SUCCESS)
+ break;
+ }
+
+ if (MachRet != KERN_SUCCESS)
+ {
+ ASSERT("thread_set_exception_ports failed: %d\n", MachRet);
+ palError = UTIL_MachErrorToPalError(MachRet);
+ }
+
+ return palError;
+}
+
+#else // FEATURE_PAL_SXS
+
+/*++
+Function :
+ SEHEnableMachExceptions
+
+ Enable SEH-related stuff related to mach exceptions
+
+ (no parameters)
+
+Return value :
+ TRUE if enabling succeeded
+ FALSE otherwise
+--*/
+BOOL SEHEnableMachExceptions()
+{
+ exception_mask_t machExceptionMask = GetExceptionMask();
+ if (machExceptionMask != 0)
+ {
+ kern_return_t MachRet;
+ MachRet = task_set_exception_ports(mach_task_self(),
+ machExceptionMask,
+ s_ExceptionPort,
+ EXCEPTION_DEFAULT,
+ MACHINE_THREAD_STATE);
+
+ if (MachRet != KERN_SUCCESS)
+ {
+ ASSERT("task_set_exception_ports failed: %d\n", MachRet);
+ UTIL_SetLastErrorFromMach(MachRet);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*++
+Function :
+ SEHDisableMachExceptions
+
+ Disable SEH-related stuff related to mach exceptions
+
+ (no parameters)
+
+Return value :
+ TRUE if enabling succeeded
+ FALSE otherwise
+--*/
+BOOL SEHDisableMachExceptions()
+{
+ exception_mask_t machExceptionMask = GetExceptionMask();
+ if (machExceptionMask != 0)
+ {
+ kern_return_t MachRet;
+ MachRet = task_set_exception_ports(mach_task_self(),
+ machExceptionMask,
+ MACH_PORT_NULL,
+ EXCEPTION_DEFAULT,
+ MACHINE_THREAD_STATE);
+
+ if (MachRet != KERN_SUCCESS)
+ {
+ ASSERT("task_set_exception_ports failed: %d\n", MachRet);
+ UTIL_SetLastErrorFromMach(MachRet);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+#endif // FEATURE_PAL_SXS
+
+#if !defined(_AMD64_)
+extern "C"
+void PAL_DispatchException(PCONTEXT pContext, PEXCEPTION_RECORD pExRecord, MachExceptionInfo *pMachExceptionInfo)
+#else // defined(_AMD64_)
+
+// Since HijackFaultingThread pushed the context, exception record and info on the stack, we need to adjust the
+// signature of PAL_DispatchException such that the corresponding arguments are considered to be on the stack
+// per GCC64 calling convention rules. Hence, the first 6 dummy arguments (corresponding to RDI, RSI, RDX,RCX, R8, R9).
+extern "C"
+void PAL_DispatchException(DWORD64 dwRDI, DWORD64 dwRSI, DWORD64 dwRDX, DWORD64 dwRCX, DWORD64 dwR8, DWORD64 dwR9, PCONTEXT pContext, PEXCEPTION_RECORD pExRecord, MachExceptionInfo *pMachExceptionInfo)
+#endif // !defined(_AMD64_)
+{
+ CPalThread *pThread = InternalGetCurrentThread();
+
+#if FEATURE_PAL_SXS
+ if (!pThread->IsInPal())
+ {
+ // It's now possible to observe system exceptions in code running outside the PAL (as the result of a
+ // p/invoke since we no longer revert our Mach exception ports in this case). In that scenario we need
+ // to re-enter the PAL now as the exception signals the end of the p/invoke.
+ PAL_Reenter(PAL_BoundaryBottom);
+ }
+#endif // FEATURE_PAL_SXS
+
+ CONTEXT *contextRecord;
+ EXCEPTION_RECORD *exceptionRecord;
+ AllocateExceptionRecords(&exceptionRecord, &contextRecord);
+
+ *contextRecord = *pContext;
+ *exceptionRecord = *pExRecord;
+
+ contextRecord->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
+ bool continueExecution;
+
+ {
+ // The exception object takes ownership of the exceptionRecord and contextRecord
+ PAL_SEHException exception(exceptionRecord, contextRecord);
+
+ TRACE("PAL_DispatchException(EC %08x EA %p)\n", pExRecord->ExceptionCode, pExRecord->ExceptionAddress);
+
+ continueExecution = SEHProcessException(&exception);
+ if (continueExecution)
+ {
+ // Make a copy of the exception records so that we can free them before restoring the context
+ *pContext = *contextRecord;
+ *pExRecord = *exceptionRecord;
+ }
+
+ // The exception records are destroyed by the PAL_SEHException destructor now.
+ }
+
+ if (continueExecution)
+ {
+ RtlRestoreContext(pContext, pExRecord);
+ }
+
+ // Send the forward request to the exception thread to process
+ MachMessage sSendMessage;
+ sSendMessage.SendForwardException(s_ExceptionPort, pMachExceptionInfo, pThread);
+
+ // Spin wait until this thread is hijacked by the exception thread
+ while (TRUE)
+ {
+ sched_yield();
+ }
+}
+
+#if defined(_X86_) || defined(_AMD64_)
+extern "C" void PAL_DispatchExceptionWrapper();
+extern "C" int PAL_DispatchExceptionReturnOffset;
+#endif // _X86_ || _AMD64_
+
+/*++
+Function :
+ BuildExceptionRecord
+
+ Sets up up an ExceptionRecord from an exception message
+
+Parameters :
+ exceptionInfo - exception info to build the exception record
+ pExceptionRecord - exception record to setup
+*/
+static
+void
+BuildExceptionRecord(
+ MachExceptionInfo& exceptionInfo, // [in] exception info
+ EXCEPTION_RECORD *pExceptionRecord) // [out] Used to return exception parameters
+{
+ memset(pExceptionRecord, 0, sizeof(EXCEPTION_RECORD));
+
+ DWORD exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+
+ switch(exceptionInfo.ExceptionType)
+ {
+ // Could not access memory. subcode contains the bad memory address.
+ case EXC_BAD_ACCESS:
+ if (exceptionInfo.SubcodeCount != 2)
+ {
+ NONPAL_RETAIL_ASSERT("Got an unexpected subcode");
+ exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+ }
+ else
+ {
+ exceptionCode = EXCEPTION_ACCESS_VIOLATION;
+
+ pExceptionRecord->NumberParameters = 2;
+ pExceptionRecord->ExceptionInformation[0] = 0;
+ pExceptionRecord->ExceptionInformation[1] = exceptionInfo.Subcodes[1];
+ NONPAL_TRACE("subcodes[1] = %llx\n", exceptionInfo.Subcodes[1]);
+ }
+ break;
+
+ // Instruction failed. Illegal or undefined instruction or operand.
+ case EXC_BAD_INSTRUCTION :
+ // TODO: Identify privileged instruction. Need to get the thread state and read the machine code. May
+ // be better to do this in the place that calls SEHProcessException, similar to how it's done on Linux.
+ exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+ break;
+
+ // Arithmetic exception; exact nature of exception is in subcode field.
+ case EXC_ARITHMETIC:
+ if (exceptionInfo.SubcodeCount != 2)
+ {
+ NONPAL_RETAIL_ASSERT("Got an unexpected subcode");
+ exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+ }
+ else
+ {
+ switch (exceptionInfo.Subcodes[0])
+ {
+#if defined(_X86_) || defined(_AMD64_)
+ case EXC_I386_DIV:
+ exceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO;
+ break;
+ case EXC_I386_INTO:
+ exceptionCode = EXCEPTION_INT_OVERFLOW;
+ break;
+ case EXC_I386_EXTOVR:
+ exceptionCode = EXCEPTION_FLT_OVERFLOW;
+ break;
+ case EXC_I386_BOUND:
+ exceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
+ break;
+#else
+#error Trap code to exception mapping not defined for this architecture
+#endif
+ default:
+ exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+ break;
+ }
+ }
+ break;
+
+ case EXC_SOFTWARE:
+#if defined(_X86_) || defined(_AMD64_)
+ exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+ break;
+#else
+#error Trap code to exception mapping not defined for this architecture
+#endif
+
+ // Trace, breakpoint, etc. Details in subcode field.
+ case EXC_BREAKPOINT:
+#if defined(_X86_) || defined(_AMD64_)
+ if (exceptionInfo.Subcodes[0] == EXC_I386_SGL)
+ {
+ exceptionCode = EXCEPTION_SINGLE_STEP;
+ }
+ else if (exceptionInfo.Subcodes[0] == EXC_I386_BPT)
+ {
+ exceptionCode = EXCEPTION_BREAKPOINT;
+ }
+#else
+#error Trap code to exception mapping not defined for this architecture
+#endif
+ else
+ {
+ WARN("unexpected subcode %d for EXC_BREAKPOINT", exceptionInfo.Subcodes[0]);
+ exceptionCode = EXCEPTION_BREAKPOINT;
+ }
+ break;
+
+
+ // System call requested. Details in subcode field.
+ case EXC_SYSCALL:
+ exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+ break;
+
+ // System call with a number in the Mach call range requested. Details in subcode field.
+ case EXC_MACH_SYSCALL:
+ exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+ break;
+
+ default:
+ NONPAL_ASSERT("Got unknown trap code %d\n", exceptionInfo.ExceptionType);
+ break;
+ }
+
+ pExceptionRecord->ExceptionCode = exceptionCode;
+}
+
+#ifdef _DEBUG
+const char *
+GetExceptionString(
+ exception_type_t exception
+)
+{
+ switch(exception)
+ {
+ case EXC_BAD_ACCESS:
+ return "EXC_BAD_ACCESS";
+
+ case EXC_BAD_INSTRUCTION:
+ return "EXC_BAD_INSTRUCTION";
+
+ case EXC_ARITHMETIC:
+ return "EXC_ARITHMETIC";
+
+ case EXC_SOFTWARE:
+ return "EXC_SOFTWARE";
+
+ case EXC_BREAKPOINT:
+ return "EXC_BREAKPOINT";
+
+ case EXC_SYSCALL:
+ return "EXC_SYSCALL";
+
+ case EXC_MACH_SYSCALL:
+ return "EXC_MACH_SYSCALL";
+
+ default:
+ NONPAL_ASSERT("Got unknown trap code %d\n", exception);
+ break;
+ }
+ return "INVALID CODE";
+}
+#endif // _DEBUG
+
+/*++
+Function :
+ HijackFaultingThread
+
+ Sets the faulting thread up to return to PAL_DispatchException with an
+ ExceptionRecord and thread CONTEXT.
+
+Parameters:
+ thread - thread the exception happened
+ task - task the exception happened
+ message - exception message
+
+Return value :
+ None
+--*/
+static
+void
+HijackFaultingThread(
+ mach_port_t thread, // [in] thread the exception happened on
+ mach_port_t task, // [in] task the exception happened on
+ MachMessage& message) // [in] exception message
+{
+ MachExceptionInfo exceptionInfo(thread, message);
+ EXCEPTION_RECORD exceptionRecord;
+ CONTEXT threadContext;
+ kern_return_t machret;
+
+ // Fill in the exception record from the exception info
+ BuildExceptionRecord(exceptionInfo, &exceptionRecord);
+
+#ifdef _X86_
+ threadContext.ContextFlags = CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS;
+#else
+ threadContext.ContextFlags = CONTEXT_FLOATING_POINT;
+#endif
+ CONTEXT_GetThreadContextFromThreadState(x86_FLOAT_STATE, (thread_state_t)&exceptionInfo.FloatState, &threadContext);
+
+ threadContext.ContextFlags |= CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
+ CONTEXT_GetThreadContextFromThreadState(x86_THREAD_STATE, (thread_state_t)&exceptionInfo.ThreadState, &threadContext);
+
+#if defined(CORECLR) && (defined(_X86_) || defined(_AMD64_))
+ // For CoreCLR we look more deeply at access violations to determine whether they're the result of a stack
+ // overflow. If so we'll terminate the process immediately (the current default policy of the CoreCLR EE).
+ // Otherwise we'll either A/V ourselves trying to set up the SEH exception record and context on the
+ // target thread's stack (unlike Windows there's no extra stack reservation to guarantee this can be done)
+ // or, and this the case we're trying to avoid, it's possible we'll succeed and the runtime will go ahead
+ // and process the SO like it was a simple AV. Since the runtime doesn't currently implement stack probing
+ // on non-Windows platforms, this could lead to data corruption (we have SO intolerant code in the runtime
+ // which manipulates global state under the assumption that an SO cannot occur due to a prior stack
+ // probe).
+
+ // Determining whether an AV is really an SO is not quite straightforward. We can get stack bounds
+ // information from pthreads but (a) we only have the target Mach thread port and no way to map to a
+ // pthread easily and (b) the pthread functions lie about the bounds on the main thread.
+
+ // Instead we inspect the target thread SP we just retrieved above and compare it with the AV address. If
+ // they both lie in the same page or the SP is at a higher address than the AV but in the same VM region,
+ // then we'll consider the AV to be an SO. Note that we can't assume that SP will be in the same page as
+ // the AV on an SO, even though we force GCC to generate stack probes on stack extension (-fstack-check).
+ // That's because GCC currently generates the probe *before* altering SP. Since a given stack extension can
+ // involve multiple pages and GCC generates all the required probes before updating SP in a single
+ // operation, the faulting probe can be at an address that is far removed from the thread's current value
+ // of SP.
+
+ // In the case where the AV and SP aren't in the same or adjacent pages we check if the first page
+ // following the faulting address belongs in the same VM region as the current value of SP. Since all pages
+ // in a VM region have the same attributes this check eliminates the possibility that there's another guard
+ // page in the range between the fault and the SP, effectively establishing that the AV occurred in the
+ // guard page associated with the stack associated with the SP.
+
+ // We are assuming here that thread stacks are always allocated in a single VM region. I've seen no
+ // evidence thus far that this is not the case (and the mere fact we rely on Mach apis already puts us on
+ // brittle ground anyway).
+
+ // (a) SP always marks the current limit of the stack (in that all valid stack accesses will be of
+ // the form [SP + delta]). The Mac x86 ABI appears to guarantee this (or rather it does not
+ // guarantee that stack slots below SP will not be invalidated by asynchronous events such as
+ // interrupts, which mostly amounts to the same thing for user mode code). Note that the Mac PPC
+ // ABI does allow some (constrained) access below SP, but we're not currently supporting this
+ // platform.
+ // (b) All code will extend the stack "carefully" (by which we mean that stack extensions of more
+ // than one page in size will touch at least one byte in each intervening page (in decreasing
+ // address order), to guarantee that the guard page is hit before memory beyond the guard page is
+ // corrupted). Our managed jits always generate code which does this as does MSVC. GCC, however,
+ // does not do this by default. We have to explicitly provide the -fstack-check compiler option
+ // to enable the behavior.
+#if (defined(_X86_) || defined(_AMD64_)) && defined(__APPLE__)
+ if (exceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ {
+ // Assume this AV isn't an SO to begin with.
+ bool fIsStackOverflow = false;
+
+ // Calculate the page base addresses for the fault and the faulting thread's SP.
+ int cbPage = getpagesize();
+ char *pFaultPage = (char*)(exceptionRecord.ExceptionInformation[1] & ~(cbPage - 1));
+#ifdef _X86_
+ char *pStackTopPage = (char*)(threadContext.Esp & ~(cbPage - 1));
+#elif defined(_AMD64_)
+ char *pStackTopPage = (char*)(threadContext.Rsp & ~(cbPage - 1));
+#endif
+
+ if (pFaultPage == pStackTopPage || pFaultPage == (pStackTopPage - cbPage))
+ {
+ // The easy case is when the AV occurred in the same or adjacent page as the stack pointer.
+ fIsStackOverflow = true;
+ }
+ else if (pFaultPage < pStackTopPage)
+ {
+ // Calculate the address of the page immediately following the fault and check that it
+ // lies in the same VM region as the stack pointer.
+ vm_address_t vm_address;
+ vm_size_t vm_size;
+ vm_region_flavor_t vm_flavor;
+ mach_msg_type_number_t infoCnt;
+#ifdef BIT64
+ vm_region_basic_info_data_64_t info;
+ infoCnt = VM_REGION_BASIC_INFO_COUNT_64;
+ vm_flavor = VM_REGION_BASIC_INFO_64;
+#else
+ vm_region_basic_info_data_t info;
+ infoCnt = VM_REGION_BASIC_INFO_COUNT;
+ vm_flavor = VM_REGION_BASIC_INFO;
+#endif
+ mach_port_t object_name;
+
+ vm_address = (vm_address_t)(pFaultPage + cbPage);
+
+#ifdef BIT64
+ machret = vm_region_64(
+#else
+ machret = vm_region(
+#endif
+ mach_task_self(),
+ &vm_address,
+ &vm_size,
+ vm_flavor,
+ (vm_region_info_t)&info,
+ &infoCnt,
+ &object_name);
+#ifdef _X86_
+ CHECK_MACH("vm_region", machret);
+#elif defined(_AMD64_)
+ CHECK_MACH("vm_region_64", machret);
+#endif
+
+ // If vm_region updated the address we gave it then that address was not part of a region at all
+ // (and so this cannot be an SO). Otherwise check that the ESP lies in the region returned.
+ char *pRegionStart = (char*)vm_address;
+ char *pRegionEnd = (char*)vm_address + vm_size;
+ if (pRegionStart == (pFaultPage + cbPage) && pStackTopPage < pRegionEnd)
+ fIsStackOverflow = true;
+ }
+
+#if defined(_AMD64_)
+ if (!fIsStackOverflow)
+ {
+ // Check if we can read pointer sizeD bytes below the target thread's stack pointer.
+ // If we are unable to, then it implies we have run into SO.
+ void **targetSP = (void **)threadContext.Rsp;
+ vm_address_t targetAddr = (mach_vm_address_t)(targetSP);
+ targetAddr -= sizeof(void *);
+ vm_size_t vm_size = sizeof(void *);
+ char arr[8];
+ vm_size_t data_count = 8;
+ machret = vm_read_overwrite(mach_task_self(), targetAddr, vm_size, (pointer_t)arr, &data_count);
+ if (machret == KERN_INVALID_ADDRESS)
+ {
+ fIsStackOverflow = true;
+ }
+ }
+#endif // _AMD64_
+
+ if (fIsStackOverflow)
+ {
+ // We have a stack overflow. Abort the process immediately. It would be nice to let the VM do this
+ // but the Windows mechanism (where a stack overflow SEH exception is delivered on the faulting
+ // thread) will not work most of the time since non-Windows OSs don't keep a reserve stack
+ // extension allocated for this purpose.
+
+ // TODO: Once our event reporting story is further along we probably want to report something
+ // here. If our runtime policy for SO ever changes (the most likely candidate being "unload
+ // appdomain on SO) then we'll have to do something more complex here, probably involving a
+ // handshake with the runtime in order to report the SO without attempting to extend the faulting
+ // thread's stack any further. Note that we cannot call most PAL functions from the context of
+ // this thread since we're not a PAL thread.
+
+ write(STDERR_FILENO, StackOverflowMessage, sizeof(StackOverflowMessage) - 1);
+ abort();
+ }
+ }
+#else // (_X86_ || _AMD64_) && __APPLE__
+#error Platform not supported for correct stack overflow handling
+#endif // (_X86_ || _AMD64_) && __APPLE__
+#endif // CORECLR && _X86_
+
+#if defined(_X86_)
+ NONPAL_ASSERTE(exceptionInfo.ThreadState.tsh.flavor == x86_THREAD_STATE32);
+
+ // Make a copy of the thread state because the one in exceptionInfo needs to be preserved to restore
+ // the state if the exception is forwarded.
+ x86_thread_state32_t ts32 = exceptionInfo.ThreadState.uts.ts32;
+
+ // If we're in single step mode, disable it since we're going to call PAL_DispatchException
+ if (exceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP)
+ {
+ ts32.eflags &= ~EFL_TF;
+ }
+
+ exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL;
+ exceptionRecord.ExceptionRecord = NULL;
+ exceptionRecord.ExceptionAddress = (void *)ts32.eip;
+
+ void **FramePointer = (void **)ts32.esp;
+
+ *--FramePointer = (void *)ts32.eip;
+
+ // Construct a stack frame for a pretend activation of the function
+ // PAL_DispatchExceptionWrapper that serves only to make the stack
+ // correctly unwindable by the system exception unwinder.
+ // PAL_DispatchExceptionWrapper has an ebp frame, its local variables
+ // are the context and exception record, and it has just "called"
+ // PAL_DispatchException.
+ *--FramePointer = (void *)ts32.ebp;
+ ts32.ebp = (unsigned)FramePointer;
+
+ // Put the context on the stack
+ FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(CONTEXT));
+ // Make sure it's aligned - CONTEXT has 8-byte alignment
+ FramePointer = (void **)((ULONG_PTR)FramePointer - ((ULONG_PTR)FramePointer % 8));
+ CONTEXT *pContext = (CONTEXT *)FramePointer;
+ *pContext = threadContext;
+
+ // Put the exception record on the stack
+ FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(EXCEPTION_RECORD));
+ EXCEPTION_RECORD *pExceptionRecord = (EXCEPTION_RECORD *)FramePointer;
+ *pExceptionRecord = exceptionRecord;
+
+ FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(MachExceptionInfo));
+ MachExceptionInfo *pMachExceptionInfo = (MachExceptionInfo *)FramePointer;
+ *pMachExceptionInfo = exceptionInfo;
+
+ // Push arguments to PAL_DispatchException
+ FramePointer = (void **)((ULONG_PTR)FramePointer - 3 * sizeof(void *));
+
+ // Make sure it's aligned - ABI requires 16-byte alignment
+ FramePointer = (void **)((ULONG_PTR)FramePointer - ((ULONG_PTR)FramePointer % 16));
+ FramePointer[0] = pContext;
+ FramePointer[1] = pExceptionRecord;
+ FramePointer[2] = pMachExceptionInfo;
+
+ // Place the return address to right after the fake call in PAL_DispatchExceptionWrapper
+ FramePointer[-1] = (void *)((ULONG_PTR)PAL_DispatchExceptionWrapper + PAL_DispatchExceptionReturnOffset);
+
+ // Make the instruction register point to DispatchException
+ ts32.eip = (unsigned)PAL_DispatchException;
+ ts32.esp = (unsigned)&FramePointer[-1]; // skip return address
+
+ // Now set the thread state for the faulting thread so that PAL_DispatchException executes next
+ machret = thread_set_state(thread, x86_THREAD_STATE32, (thread_state_t)&ts32, x86_THREAD_STATE32_COUNT);
+ CHECK_MACH("thread_set_state(thread)", machret);
+#elif defined(_AMD64_)
+ NONPAL_ASSERTE(exceptionInfo.ThreadState.tsh.flavor == x86_THREAD_STATE64);
+
+ // Make a copy of the thread state because the one in exceptionInfo needs to be preserved to restore
+ // the state if the exception is forwarded.
+ x86_thread_state64_t ts64 = exceptionInfo.ThreadState.uts.ts64;
+
+ // If we're in single step mode, disable it since we're going to call PAL_DispatchException
+ if (exceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP)
+ {
+ ts64.__rflags &= ~EFL_TF;
+ }
+
+ exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL;
+ exceptionRecord.ExceptionRecord = NULL;
+ exceptionRecord.ExceptionAddress = (void *)ts64.__rip;
+
+ void **FramePointer = (void **)ts64.__rsp;
+
+ *--FramePointer = (void *)ts64.__rip;
+
+ // Construct a stack frame for a pretend activation of the function
+ // PAL_DispatchExceptionWrapper that serves only to make the stack
+ // correctly unwindable by the system exception unwinder.
+ // PAL_DispatchExceptionWrapper has an ebp frame, its local variables
+ // are the context and exception record, and it has just "called"
+ // PAL_DispatchException.
+ *--FramePointer = (void *)ts64.__rbp;
+ ts64.__rbp = (SIZE_T)FramePointer;
+
+ // Put the context on the stack
+ FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(CONTEXT));
+ // Make sure it's aligned - CONTEXT has 16-byte alignment
+ FramePointer = (void **)((ULONG_PTR)FramePointer - ((ULONG_PTR)FramePointer % 16));
+ CONTEXT *pContext = (CONTEXT *)FramePointer;
+ *pContext = threadContext;
+
+ // Put the exception record on the stack
+ FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(EXCEPTION_RECORD));
+ EXCEPTION_RECORD *pExceptionRecord = (EXCEPTION_RECORD *)FramePointer;
+ *pExceptionRecord = exceptionRecord;
+
+ FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(MachExceptionInfo));
+ MachExceptionInfo *pMachExceptionInfo = (MachExceptionInfo *)FramePointer;
+ *pMachExceptionInfo = exceptionInfo;
+
+ // Push arguments to PAL_DispatchException
+ FramePointer = (void **)((ULONG_PTR)FramePointer - 3 * sizeof(void *));
+
+ // Make sure it's aligned - ABI requires 16-byte alignment
+ FramePointer = (void **)((ULONG_PTR)FramePointer - ((ULONG_PTR)FramePointer % 16));
+ FramePointer[0] = pContext;
+ FramePointer[1] = pExceptionRecord;
+ FramePointer[2] = pMachExceptionInfo;
+
+ // Place the return address to right after the fake call in PAL_DispatchExceptionWrapper
+ FramePointer[-1] = (void *)((ULONG_PTR)PAL_DispatchExceptionWrapper + PAL_DispatchExceptionReturnOffset);
+
+ // Make the instruction register point to DispatchException
+ ts64.__rip = (SIZE_T)PAL_DispatchException;
+ ts64.__rsp = (SIZE_T)&FramePointer[-1]; // skip return address
+
+ // Now set the thread state for the faulting thread so that PAL_DispatchException executes next
+ machret = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)&ts64, x86_THREAD_STATE64_COUNT);
+ CHECK_MACH("thread_set_state(thread)", machret);
+#else
+#error HijackFaultingThread not defined for this architecture
+#endif
+}
+
+/*++
+Function :
+ SuspendMachThread
+
+ Suspend the specified thread.
+
+Parameters:
+ thread - mach thread port
+
+Return value :
+ None
+--*/
+static
+void
+SuspendMachThread(thread_act_t thread)
+{
+ kern_return_t machret;
+
+ while (true)
+ {
+ machret = thread_suspend(thread);
+ CHECK_MACH("thread_suspend", machret);
+
+ // Ensure that if the thread was running in the kernel, the kernel operation
+ // is safely aborted so that it can be restarted later.
+ machret = thread_abort_safely(thread);
+ if (machret == KERN_SUCCESS)
+ {
+ break;
+ }
+
+ // The thread was running in the kernel executing a non-atomic operation
+ // that cannot be restarted, so we need to resume the thread and retry
+ machret = thread_resume(thread);
+ CHECK_MACH("thread_resume", machret);
+ }
+}
+
+/*++
+Function :
+ SEHExceptionThread
+
+ Entry point for the thread that will listen for exception in any other thread.
+
+#ifdef FEATURE_PAL_SXS
+ NOTE: This thread is not a PAL thread, and it must not be one. If it was,
+ exceptions on this thread would be delivered to the port this thread itself
+ is listening on.
+
+ In particular, if another thread overflows its stack, the exception handling
+ thread receives a message. It will try to create a PAL_DispatchException
+ frame on the faulting thread, which will likely fault. If the exception
+ processing thread is not a PAL thread, the process gets terminated with a
+ bus error; if the exception processing thread was a PAL thread, we would see
+ a hang (since no thread is listening for the exception message that gets sent).
+ Of the two ugly behaviors, the bus error is definitely favorable.
+
+ This means: no printf, no TRACE, no PAL allocation, no ExitProcess,
+ no LastError in this function and its helpers. To report fatal failure,
+ use NONPAL_RETAIL_ASSERT.
+#endif // FEATURE_PAL_SXS
+
+Parameters :
+ void *args - not used
+
+Return value :
+ Never returns
+--*/
+void *
+SEHExceptionThread(void *args)
+{
+ ForwardedExceptionList feList;
+ MachMessage sReplyOrForward;
+ MachMessage sMessage;
+ kern_return_t machret;
+ thread_act_t thread;
+
+ // Loop processing incoming messages forever.
+ while (true)
+ {
+ // Receive the next message.
+ sMessage.Receive(s_ExceptionPort);
+
+ NONPAL_TRACE("Received message %s (%08x) from (remote) %08x to (local) %08x\n",
+ sMessage.GetMessageTypeName(),
+ sMessage.GetMessageType(),
+ sMessage.GetRemotePort(),
+ sMessage.GetLocalPort());
+
+ if (sMessage.IsSetThreadRequest())
+ {
+ // Handle a request to set the thread context for the specified target thread.
+ CONTEXT sContext;
+ thread = sMessage.GetThreadContext(&sContext);
+
+ // Suspend the target thread
+ SuspendMachThread(thread);
+
+ machret = CONTEXT_SetThreadContextOnPort(thread, &sContext);
+ CHECK_MACH("CONTEXT_SetThreadContextOnPort", machret);
+
+ machret = thread_resume(thread);
+ CHECK_MACH("thread_resume", machret);
+ }
+ else if (sMessage.IsExceptionNotification())
+ {
+ // This is a notification of an exception occurring on another thread.
+ exception_type_t exceptionType = sMessage.GetException();
+ thread = sMessage.GetThread();
+
+#ifdef _DEBUG
+ if (NONPAL_TRACE_ENABLED)
+ {
+ NONPAL_TRACE("ExceptionNotification %s (%u) thread %08x flavor %u\n",
+ GetExceptionString(exceptionType),
+ exceptionType,
+ thread,
+ sMessage.GetThreadStateFlavor());
+
+ int subcode_count = sMessage.GetExceptionCodeCount();
+ for (int i = 0; i < subcode_count; i++)
+ NONPAL_TRACE("ExceptionNotification subcode[%d] = %llx\n", i, sMessage.GetExceptionCode(i));
+
+ x86_thread_state64_t threadStateActual;
+ unsigned int count = sizeof(threadStateActual) / sizeof(unsigned);
+ machret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&threadStateActual, &count);
+ CHECK_MACH("thread_get_state", machret);
+
+ NONPAL_TRACE("ExceptionNotification actual rip %016llx rsp %016llx rbp %016llx rax %016llx r15 %016llx eflags %08llx\n",
+ threadStateActual.__rip,
+ threadStateActual.__rsp,
+ threadStateActual.__rbp,
+ threadStateActual.__rax,
+ threadStateActual.__r15,
+ threadStateActual.__rflags);
+
+ x86_exception_state64_t threadExceptionState;
+ unsigned int ehStateCount = sizeof(threadExceptionState) / sizeof(unsigned);
+ machret = thread_get_state(thread, x86_EXCEPTION_STATE64, (thread_state_t)&threadExceptionState, &ehStateCount);
+ CHECK_MACH("thread_get_state", machret);
+
+ NONPAL_TRACE("ExceptionNotification trapno %04x cpu %04x err %08x faultAddr %016llx\n",
+ threadExceptionState.__trapno,
+ threadExceptionState.__cpu,
+ threadExceptionState.__err,
+ threadExceptionState.__faultvaddr);
+ }
+#endif // _DEBUG
+
+ bool feFound = false;
+ feList.MoveFirst();
+
+ while (!feList.IsEOL())
+ {
+ mach_port_type_t ePortType;
+ if (mach_port_type(mach_task_self(), feList.Current->Thread, &ePortType) != KERN_SUCCESS || (ePortType & MACH_PORT_TYPE_DEAD_NAME))
+ {
+ NONPAL_TRACE("Forwarded exception: invalid thread port %08x\n", feList.Current->Thread);
+
+ // Unlink and delete the forwarded exception instance
+ feList.Delete();
+ }
+ else
+ {
+ if (feList.Current->Thread == thread)
+ {
+ bool isSameException = feList.Current->ExceptionType == exceptionType;
+ feFound = true;
+
+ // Locate the record of previously installed handlers that the target thread keeps.
+ CThreadMachExceptionHandlers *pHandlers = feList.Current->PalThread->GetSavedMachHandlers();
+
+ // Unlink and delete the forwarded exception instance
+ feList.Delete();
+
+ // Check if the current exception type matches the forwarded one and whether
+ // there's a handler for the particular exception we've been handed.
+ MachExceptionHandler sHandler;
+ if (isSameException && pHandlers->GetHandler(exceptionType, &sHandler))
+ {
+ NONPAL_TRACE("ForwardNotification thread %08x to handler %08x\n", thread, sHandler.m_handler);
+ sReplyOrForward.ForwardNotification(&sHandler, sMessage);
+ }
+ else
+ {
+ NONPAL_TRACE("ReplyToNotification KERN_FAILURE thread %08x port %08x sameException %d\n",
+ thread, sMessage.GetRemotePort(), isSameException);
+ sReplyOrForward.ReplyToNotification(sMessage, KERN_FAILURE);
+ }
+ break;
+ }
+
+ feList.MoveNext();
+ }
+ }
+
+ if (!feFound)
+ {
+ NONPAL_TRACE("HijackFaultingThread thread %08x\n", thread);
+ HijackFaultingThread(thread, mach_task_self(), sMessage);
+
+ // Send the result of handling the exception back in a reply.
+ NONPAL_TRACE("ReplyToNotification KERN_SUCCESS thread %08x port %08x\n", thread, sMessage.GetRemotePort());
+ sReplyOrForward.ReplyToNotification(sMessage, KERN_SUCCESS);
+ }
+ }
+ else if (sMessage.IsForwardExceptionRequest())
+ {
+ thread = sMessage.GetThread();
+
+ NONPAL_TRACE("ForwardExceptionRequest for thread %08x\n", thread);
+
+ // Suspend the faulting thread.
+ SuspendMachThread(thread);
+
+ // Set the context back to the original faulting state.
+ MachExceptionInfo *pExceptionInfo = sMessage.GetExceptionInfo();
+ pExceptionInfo->RestoreState(thread);
+
+ // Allocate an forwarded exception entry
+ ForwardedException *pfe = (ForwardedException *)malloc(sizeof(ForwardedException));
+ if (pfe == NULL)
+ {
+ NONPAL_RETAIL_ASSERT("Exception thread ran out of memory to track forwarded exception notifications");
+ }
+
+ // Save the forwarded exception entry away for the restarted exception message
+ pfe->Thread = thread;
+ pfe->ExceptionType = pExceptionInfo->ExceptionType;
+ pfe->PalThread = sMessage.GetPalThread();
+ feList.Add(pfe);
+
+ // Now let the thread run at the original exception context to restart the exception
+ NONPAL_TRACE("ForwardExceptionRequest resuming thread %08x exception type %08x\n", thread, pfe->ExceptionType);
+ machret = thread_resume(thread);
+ CHECK_MACH("thread_resume", machret);
+ }
+ else
+ {
+ NONPAL_RETAIL_ASSERT("Unknown message type: %u", sMessage.GetMessageType());
+ }
+ }
+}
+
+/*++
+Function :
+ MachExceptionInfo constructor
+
+ Saves the exception info from the exception notification message and
+ the current thread state.
+
+Parameters:
+ thread - thread port to restore
+ message - exception message
+
+Return value :
+ none
+--*/
+MachExceptionInfo::MachExceptionInfo(mach_port_t thread, MachMessage& message)
+{
+ kern_return_t machret;
+
+ ExceptionType = message.GetException();
+ SubcodeCount = message.GetExceptionCodeCount();
+ NONPAL_RETAIL_ASSERTE(SubcodeCount >= 0 && SubcodeCount <= 2);
+
+ for (int i = 0; i < SubcodeCount; i++)
+ Subcodes[i] = message.GetExceptionCode(i);
+
+ mach_msg_type_number_t count = x86_THREAD_STATE_COUNT;
+ machret = thread_get_state(thread, x86_THREAD_STATE, (thread_state_t)&ThreadState, &count);
+ CHECK_MACH("thread_get_state", machret);
+
+ count = x86_FLOAT_STATE_COUNT;
+ machret = thread_get_state(thread, x86_FLOAT_STATE, (thread_state_t)&FloatState, &count);
+ CHECK_MACH("thread_get_state(float)", machret);
+
+ count = x86_DEBUG_STATE_COUNT;
+ machret = thread_get_state(thread, x86_DEBUG_STATE, (thread_state_t)&DebugState, &count);
+ CHECK_MACH("thread_get_state(debug)", machret);
+}
+
+/*++
+Function :
+ MachExceptionInfo::RestoreState
+
+ Restore the thread to the saved exception info state.
+
+Parameters:
+ thread - thread port to restore
+
+Return value :
+ none
+--*/
+void MachExceptionInfo::RestoreState(mach_port_t thread)
+{
+ // If we are restarting a breakpoint, we need to bump the IP back one to
+ // point at the actual int 3 instructions.
+ if (ExceptionType == EXC_BREAKPOINT)
+ {
+ if (Subcodes[0] == EXC_I386_BPT)
+ {
+#ifdef _X86_
+ ThreadState.uts.ts32.eip--;
+#elif defined(_AMD64_)
+ ThreadState.uts.ts64.__rip--;
+#else
+#error Platform not supported
+#endif
+ }
+ }
+ kern_return_t machret = thread_set_state(thread, x86_THREAD_STATE, (thread_state_t)&ThreadState, x86_THREAD_STATE_COUNT);
+ CHECK_MACH("thread_set_state(thread)", machret);
+
+ machret = thread_set_state(thread, x86_FLOAT_STATE, (thread_state_t)&FloatState, x86_FLOAT_STATE_COUNT);
+ CHECK_MACH("thread_set_state(float)", machret);
+
+ machret = thread_set_state(thread, x86_DEBUG_STATE, (thread_state_t)&DebugState, x86_DEBUG_STATE_COUNT);
+ CHECK_MACH("thread_set_state(debug)", machret);
+}
+
+/*++
+Function :
+ MachSetThreadContext
+
+ Sets the context of the current thread by sending a notification
+ to the exception thread.
+
+Parameters:
+ lpContext - the CONTEXT to set the current thread
+
+Return value :
+ Doesn't return
+--*/
+PAL_NORETURN
+void
+MachSetThreadContext(CONTEXT *lpContext)
+{
+ // We need to send a message to the worker thread so that it can set our thread context.
+ MachMessage sRequest;
+ sRequest.SendSetThread(s_ExceptionPort, lpContext);
+
+ // Make sure we don't do anything
+ while (TRUE)
+ {
+ sched_yield();
+ }
+}
+
+
+/*++
+Function :
+ SEHInitializeMachExceptions
+
+ Initialize all SEH-related stuff related to mach exceptions
+
+ flags - PAL_INITIALIZE flags
+
+Return value :
+ TRUE if SEH support initialization succeeded
+ FALSE otherwise
+--*/
+BOOL
+SEHInitializeMachExceptions(DWORD flags)
+{
+ pthread_t exception_thread;
+ kern_return_t machret;
+
+ s_PalInitializeFlags = flags;
+
+ // Allocate a mach port that will listen in on exceptions
+ machret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &s_ExceptionPort);
+ if (machret != KERN_SUCCESS)
+ {
+ ASSERT("mach_port_allocate failed: %d\n", machret);
+ UTIL_SetLastErrorFromMach(machret);
+ return FALSE;
+ }
+
+ // Insert the send right into the task
+ machret = mach_port_insert_right(mach_task_self(), s_ExceptionPort, s_ExceptionPort, MACH_MSG_TYPE_MAKE_SEND);
+ if (machret != KERN_SUCCESS)
+ {
+ ASSERT("mach_port_insert_right failed: %d\n", machret);
+ UTIL_SetLastErrorFromMach(machret);
+ return FALSE;
+ }
+
+ // Create the thread that will listen to the exception for all threads
+ int createret = pthread_create(&exception_thread, NULL, SEHExceptionThread, NULL);
+ if (createret != 0)
+ {
+ ERROR("pthread_create failed, error is %d (%s)\n", createret, strerror(createret));
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+#ifdef _DEBUG
+ if (NONPAL_TRACE_ENABLED)
+ {
+ CThreadMachExceptionHandlers taskHandlers;
+ machret = task_get_exception_ports(mach_task_self(),
+ PAL_EXC_ALL_MASK,
+ taskHandlers.m_masks,
+ &taskHandlers.m_nPorts,
+ taskHandlers.m_handlers,
+ taskHandlers.m_behaviors,
+ taskHandlers.m_flavors);
+
+ if (machret == KERN_SUCCESS)
+ {
+ NONPAL_TRACE("SEHInitializeMachExceptions: TASK PORT count %d\n", taskHandlers.m_nPorts);
+ for (mach_msg_type_number_t i = 0; i < taskHandlers.m_nPorts; i++)
+ {
+ NONPAL_TRACE("SEHInitializeMachExceptions: TASK PORT mask %08x handler: %08x behavior %08x flavor %u\n",
+ taskHandlers.m_masks[i],
+ taskHandlers.m_handlers[i],
+ taskHandlers.m_behaviors[i],
+ taskHandlers.m_flavors[i]);
+ }
+ }
+ else
+ {
+ NONPAL_TRACE("SEHInitializeMachExceptions: task_get_exception_ports FAILED %d %s\n", machret, mach_error_string(machret));
+ }
+ }
+#endif // _DEBUG
+
+#ifndef FEATURE_PAL_SXS
+ if (!SEHEnableMachExceptions())
+ {
+ return FALSE;
+ }
+#endif // !FEATURE_PAL_SXS
+
+ // Tell the system to ignore SIGPIPE signals rather than use the default
+ // behavior of terminating the process. Ignoring SIGPIPE will cause
+ // calls that would otherwise raise that signal to return EPIPE instead.
+ // The PAL expects EPIPE from those functions and won't handle a
+ // SIGPIPE signal.
+ signal(SIGPIPE, SIG_IGN);
+
+ // We're done
+ return TRUE;
+}
+
+/*++
+Function :
+ MachExceptionInitializeDebug
+
+ Initialize the mach exception handlers necessary for a managed debugger
+ to work
+
+Return value :
+ None
+--*/
+void MachExceptionInitializeDebug(void)
+{
+ if (s_DebugInitialized == FALSE)
+ {
+#ifndef FEATURE_PAL_SXS
+ kern_return_t MachRet;
+ MachRet = task_set_exception_ports(mach_task_self(),
+ PAL_EXC_DEBUGGING_MASK,
+ s_ExceptionPort,
+ EXCEPTION_DEFAULT,
+ MACHINE_THREAD_STATE);
+ if (MachRet != KERN_SUCCESS)
+ {
+ ASSERT("task_set_exception_ports failed: %d\n", MachRet);
+ TerminateProcess(GetCurrentProcess(), (UINT)(-1));
+ }
+#endif // !FEATURE_PAL_SXS
+ s_DebugInitialized = TRUE;
+ }
+}
+
+/*++
+Function :
+ SEHCleanupExceptionPort
+
+ Restore default exception port handler
+
+ (no parameters, no return value)
+
+Note :
+During PAL_Terminate, we reach a point where SEH isn't possible any more
+(handle manager is off, etc). Past that point, we can't avoid crashing on
+an exception.
+--*/
+void
+SEHCleanupExceptionPort(void)
+{
+ TRACE("Restoring default exception ports\n");
+#ifndef FEATURE_PAL_SXS
+ SEHDisableMachExceptions();
+#endif // !FEATURE_PAL_SXS
+ s_DebugInitialized = FALSE;
+}
+
+extern "C"
+void
+ActivationHandler(CONTEXT* context)
+{
+ if (g_activationFunction != NULL)
+ {
+ g_activationFunction(context);
+ }
+
+ RtlRestoreContext(context, NULL);
+ DebugBreak();
+}
+
+extern "C" void ActivationHandlerWrapper();
+extern "C" int ActivationHandlerReturnOffset;
+
+/*++
+Function :
+ InjectActivationInternal
+
+ Sets up the specified thread to call the ActivationHandler.
+
+Parameters:
+ pThread - PAL thread instance
+
+Return value :
+ PAL_ERROR
+--*/
+PAL_ERROR
+InjectActivationInternal(CPalThread* pThread)
+{
+ PAL_ERROR palError;
+
+ mach_port_t threadPort = pThread->GetMachPortSelf();
+ kern_return_t MachRet = thread_suspend(threadPort);
+ palError = (MachRet == KERN_SUCCESS) ? NO_ERROR : ERROR_GEN_FAILURE;
+
+ if (palError == NO_ERROR)
+ {
+ mach_msg_type_number_t count;
+
+ x86_exception_state64_t ExceptionState;
+ count = x86_EXCEPTION_STATE64_COUNT;
+ MachRet = thread_get_state(threadPort,
+ x86_EXCEPTION_STATE64,
+ (thread_state_t)&ExceptionState,
+ &count);
+ _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_get_state for x86_EXCEPTION_STATE64\n");
+
+ // Inject the activation only if the thread doesn't have a pending hardware exception
+ static const int MaxHardwareExceptionVector = 31;
+ if (ExceptionState.__trapno > MaxHardwareExceptionVector)
+ {
+ x86_thread_state64_t ThreadState;
+ count = x86_THREAD_STATE64_COUNT;
+ MachRet = thread_get_state(threadPort,
+ x86_THREAD_STATE64,
+ (thread_state_t)&ThreadState,
+ &count);
+ _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_get_state for x86_THREAD_STATE64\n");
+
+ if ((g_safeActivationCheckFunction != NULL) && g_safeActivationCheckFunction(ThreadState.__rip, /* checkingCurrentThread */ FALSE))
+ {
+ // TODO: it would be nice to preserve the red zone in case a jitter would want to use it
+ // Do we really care about unwinding through the wrapper?
+ size_t* sp = (size_t*)ThreadState.__rsp;
+ *(--sp) = ThreadState.__rip;
+ *(--sp) = ThreadState.__rbp;
+ size_t rbpAddress = (size_t)sp;
+ size_t contextAddress = (((size_t)sp) - sizeof(CONTEXT)) & ~15;
+ size_t returnAddressAddress = contextAddress - sizeof(size_t);
+ *(size_t*)(returnAddressAddress) = ActivationHandlerReturnOffset + (size_t)ActivationHandlerWrapper;
+
+ // Fill in the context in the helper frame with the full context of the suspended thread.
+ // The ActivationHandler will use the context to resume the execution of the thread
+ // after the activation function returns.
+ CONTEXT *pContext = (CONTEXT *)contextAddress;
+ pContext->ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS;
+ MachRet = CONTEXT_GetThreadContextFromPort(threadPort, pContext);
+ _ASSERT_MSG(MachRet == KERN_SUCCESS, "CONTEXT_GetThreadContextFromPort\n");
+
+ // Make the instruction register point to ActivationHandler
+ ThreadState.__rip = (size_t)ActivationHandler;
+ ThreadState.__rsp = returnAddressAddress;
+ ThreadState.__rbp = rbpAddress;
+ ThreadState.__rdi = contextAddress;
+
+ MachRet = thread_set_state(threadPort,
+ x86_THREAD_STATE64,
+ (thread_state_t)&ThreadState,
+ count);
+ _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_set_state\n");
+ }
+ }
+
+ MachRet = thread_resume(threadPort);
+ palError = (MachRet == ERROR_SUCCESS) ? NO_ERROR : ERROR_GEN_FAILURE;
+ }
+ else
+ {
+ printf("Suspension failed with error 0x%x\n", palError);
+ }
+
+ return palError;
+}
+
+#endif // HAVE_MACH_EXCEPTIONS
diff --git a/src/pal/src/exception/machexception.h b/src/pal/src/exception/machexception.h
new file mode 100644
index 0000000000..18e31501b2
--- /dev/null
+++ b/src/pal/src/exception/machexception.h
@@ -0,0 +1,48 @@
+// 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.
+
+/*++
+
+Module Name:
+
+ machexception.h
+
+Abstract:
+ Private mach exception handling utilities for SEH
+
+--*/
+
+#ifndef _MACHEXCEPTION_H_
+#define _MACHEXCEPTION_H_
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <mach/thread_status.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+#define HIJACK_ON_SIGNAL 1
+
+// List of exception types we will be watching for
+// NOTE: if you change any of these, you need to adapt s_nMachExceptionPortsMax in thread.hpp
+#define PAL_EXC_ILLEGAL_MASK (EXC_MASK_BAD_INSTRUCTION | EXC_MASK_EMULATION)
+#define PAL_EXC_DEBUGGING_MASK (EXC_MASK_BREAKPOINT | EXC_MASK_SOFTWARE)
+#define PAL_EXC_MANAGED_MASK (EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC)
+#define PAL_EXC_ALL_MASK (PAL_EXC_ILLEGAL_MASK | PAL_EXC_DEBUGGING_MASK | PAL_EXC_MANAGED_MASK)
+
+// Process and thread initialization/cleanup/context routines
+BOOL SEHInitializeMachExceptions(DWORD flags);
+void SEHCleanupExceptionPort (void);
+void MachExceptionInitializeDebug(void);
+PAL_NORETURN void MachSetThreadContext(CONTEXT *lpContext);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _MACHEXCEPTION_H_ */
+
diff --git a/src/pal/src/exception/machmessage.cpp b/src/pal/src/exception/machmessage.cpp
new file mode 100644
index 0000000000..a6f7e57484
--- /dev/null
+++ b/src/pal/src/exception/machmessage.cpp
@@ -0,0 +1,1383 @@
+// 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.
+
+/*++
+
+Module Name:
+
+ machmessage.cpp
+
+Abstract:
+
+ Abstraction over Mach messages used during exception handling.
+
+--*/
+
+#include "config.h"
+#include "pal/dbgmsg.h"
+#include "pal/environ.h"
+#include "pal/malloc.hpp"
+#include "pal/thread.hpp"
+#include "machmessage.h"
+
+#if HAVE_MACH_EXCEPTIONS
+
+// Construct an empty message. Use Receive() to form a message that can be inspected or SendSetThread(),
+// ForwardNotification(), ReplyToNotification() or ForwardReply() to construct a message and sent it.
+MachMessage::MachMessage()
+{
+ m_fPortsOwned = false;
+ ResetMessage();
+}
+
+// Listen for the next message on the given port and initialize this class with the contents. The message type
+// must match one of the MessageTypes indicated above (or the process will be aborted).
+void MachMessage::Receive(mach_port_t hPort)
+{
+ kern_return_t machret;
+
+ // Erase any stale data.
+ ResetMessage();
+
+ // Pull the next Mach message into the buffer.
+ machret = mach_msg((mach_msg_header_t*)m_rgMessageBuffer,
+ MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_NOTIFY,
+ 0,
+ kcbMaxMessageSize,
+ hPort,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ CHECK_MACH("mach_msg()", machret);
+
+ // Check it's one of the messages we're expecting.
+ switch (m_pMessage->header.msgh_id)
+ {
+ case SET_THREAD_MESSAGE_ID:
+ case FORWARD_EXCEPTION_MESSAGE_ID:
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+ case NOTIFY_SEND_ONCE_MESSAGE_ID:
+ break;
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
+ }
+
+ m_fPortsOwned = true;
+}
+
+// Indicates whether the message is a request to set the context of a thread.
+bool MachMessage::IsSetThreadRequest()
+{
+ return m_pMessage->header.msgh_id == SET_THREAD_MESSAGE_ID;
+}
+
+// Indicates whether the message is a request to forward the exception
+bool MachMessage::IsForwardExceptionRequest()
+{
+ return m_pMessage->header.msgh_id == FORWARD_EXCEPTION_MESSAGE_ID;
+}
+
+// Indicates whether the message is a notification that a send-once message was destroyed by the receiver.
+bool MachMessage::IsSendOnceDestroyedNotify()
+{
+ return m_pMessage->header.msgh_id == NOTIFY_SEND_ONCE_MESSAGE_ID;
+}
+
+// Indicates whether the message is a notification of an exception.
+bool MachMessage::IsExceptionNotification()
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Indicates whether the message is a reply to a notification of an exception.
+bool MachMessage::IsExceptionReply()
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+ case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Returns the type code for a received message.
+MachMessage::MessageType MachMessage::GetMessageType()
+{
+ return (MessageType)m_pMessage->header.msgh_id;
+}
+
+// Returns a textual form of the type of a received message. Useful for logging.
+const char *MachMessage::GetMessageTypeName()
+{
+ switch (GetMessageType())
+ {
+ case SET_THREAD_MESSAGE_ID:
+ return "SET_THREAD";
+ case FORWARD_EXCEPTION_MESSAGE_ID:
+ return "FORWARD_EXCEPTION";
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ return "EXCEPTION_RAISE";
+ case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+ return "EXCEPTION_RAISE_REPLY";
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ return "EXCEPTION_RAISE_STATE";
+ case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+ return "EXCEPTION_RAISE_STATE_REPLY";
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ return "EXCEPTION_RAISE_STATE_IDENTITY";
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+ return "EXCEPTION_RAISE_STATE_IDENTITY_REPLY";
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ return "EXCEPTION_RAISE_64";
+ case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+ return "EXCEPTION_RAISE_REPLY_64";
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ return "EXCEPTION_RAISE_STATE_64";
+ case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+ return "EXCEPTION_RAISE_STATE_REPLY_64";
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ return "EXCEPTION_RAISE_STATE_IDENTITY_64";
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+ return "EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64";
+ case NOTIFY_SEND_ONCE_MESSAGE_ID:
+ return "NOTIFY_SEND_ONCE";
+ default:
+ return "<unknown message type>";
+ }
+}
+
+// Returns the destination port (i.e. the port we listened on to receive this message).
+mach_port_t MachMessage::GetLocalPort()
+{
+ return m_pMessage->header.msgh_local_port;
+}
+
+// Returns the source port (the port sending the message) unless no reply is expected, in which case
+// MACH_PORT_NULL is returned instead.
+mach_port_t MachMessage::GetRemotePort()
+{
+ return m_pMessage->header.msgh_remote_port;
+}
+
+// Do the work of getting ports from the message.
+// * fCalculate -- calculate the thread port if the message did not contain it.
+// * fValidate -- failfast if the message was not one expected to have a (calculable) thread port.
+void MachMessage::GetPorts(bool fCalculate, bool fValidThread)
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case SET_THREAD_MESSAGE_ID:
+ m_hThread = m_pMessage->data.set_thread.thread;
+ break;
+
+ case FORWARD_EXCEPTION_MESSAGE_ID:
+ m_hThread = m_pMessage->data.forward_exception.thread;
+ break;
+
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ m_hThread = m_pMessage->data.raise.thread_port.name;
+ m_hTask = m_pMessage->data.raise.task_port.name;
+ break;
+
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ m_hThread = m_pMessage->data.raise_64.thread_port.name;
+ m_hTask = m_pMessage->data.raise_64.task_port.name;
+ break;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ if (fCalculate && m_hThread == MACH_PORT_NULL)
+ {
+ // This is a tricky case since the message itself doesn't contain the target thread.
+ m_hThread = GetThreadFromState(m_pMessage->data.raise_state.flavor,
+ m_pMessage->data.raise_state.old_state);
+ }
+ break;
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ if (fCalculate && m_hThread == MACH_PORT_NULL)
+ {
+ // This is a tricky case since the message itself doesn't contain the target thread.
+ m_hThread = GetThreadFromState(m_pMessage->data.raise_state_64.flavor,
+ m_pMessage->data.raise_state_64.old_state);
+ }
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ m_hThread = m_pMessage->data.raise_state_identity.thread_port.name;
+ m_hTask = m_pMessage->data.raise_state_identity.task_port.name;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ m_hThread = m_pMessage->data.raise_state_identity_64.thread_port.name;
+ m_hTask = m_pMessage->data.raise_state_identity_64.task_port.name;
+ break;
+
+ default:
+ if (fValidThread)
+ {
+ NONPAL_RETAIL_ASSERT("Can only get thread from notification message.");
+ }
+ break;
+ }
+}
+
+// Get the properties of a set thread or forward exception request. Fills in the provided
+// context structure with the context from the message and returns the target thread to
+// which the context should be applied.
+thread_act_t MachMessage::GetThreadContext(CONTEXT *pContext)
+{
+ NONPAL_ASSERTE(IsSetThreadRequest());
+
+ memcpy(pContext, &m_pMessage->data.set_thread.new_context, sizeof(CONTEXT));
+ m_hThread = m_pMessage->data.set_thread.thread;
+ return m_hThread;
+}
+
+// Get the target thread for an exception notification message.
+thread_act_t MachMessage::GetThread()
+{
+ GetPorts(true /* fCalculate */, true /* fValidThread */);
+ return m_hThread;
+}
+
+// Get the exception type for an exception notification message.
+exception_type_t MachMessage::GetException()
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ return m_pMessage->data.raise.exception;
+
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ return m_pMessage->data.raise_64.exception;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ return m_pMessage->data.raise_state.exception;
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_64.exception;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity.exception;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity_64.exception;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Can only get exception from notification message.");
+ }
+}
+
+// Get the count of sub-codes for an exception notification message.
+int MachMessage::GetExceptionCodeCount()
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ return m_pMessage->data.raise.code_count;
+
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ return m_pMessage->data.raise_64.code_count;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ return m_pMessage->data.raise_state.code_count;
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_64.code_count;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity.code_count;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity_64.code_count;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Can only get exception code count from notification message.");
+ }
+}
+
+// Get the exception sub-code at the specified zero-based index for an exception notification message.
+MACH_EH_TYPE(exception_data_type_t) MachMessage::GetExceptionCode(int iIndex)
+{
+ if (iIndex < 0 || iIndex >= GetExceptionCodeCount())
+ {
+ NONPAL_RETAIL_ASSERT("GetExceptionCode() index out of range.");
+ }
+
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ return (MACH_EH_TYPE(exception_data_type_t))m_pMessage->data.raise.code[iIndex];
+
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ return m_pMessage->data.raise_64.code[iIndex];
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ return (MACH_EH_TYPE(exception_data_type_t))m_pMessage->data.raise_state.code[iIndex];
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_64.code[iIndex];
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ return (MACH_EH_TYPE(exception_data_type_t))m_pMessage->data.raise_state_identity.code[iIndex];
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity_64.code[iIndex];
+
+ default:
+ NONPAL_RETAIL_ASSERT("Can only get exception code from notification message.");
+ }
+}
+
+// Fetch the thread state flavor from a notification or reply message (return THREAD_STATE_NONE for the
+// messages that don't contain a thread state).
+thread_state_flavor_t MachMessage::GetThreadStateFlavor()
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+ return THREAD_STATE_NONE;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ return m_pMessage->data.raise_state.flavor;
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_64.flavor;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity.flavor;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity_64.flavor;
+
+ case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+ return m_pMessage->data.raise_state_reply.flavor;
+
+ case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_reply_64.flavor;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity_reply.flavor;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity_reply_64.flavor;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
+ }
+}
+
+// Get the thread state with the given flavor from the exception or exception reply message. If the message
+// doesn't contain a thread state or the flavor of the state in the message doesn't match, the state will be
+// fetched directly from the target thread instead (which can be computed implicitly for exception messages or
+// passed explicitly for reply messages).
+mach_msg_type_number_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, thread_act_t thread)
+{
+ mach_msg_type_number_t count;
+ kern_return_t machret;
+
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+ // No state in the message, fall through to get it directly from the thread.
+ break;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ {
+ // There's a state in the message, but we need to check that the flavor matches what the caller's
+ // after (if not we'll fall through and get the correct flavor below).
+ if (m_pMessage->data.raise_state.flavor == eFlavor)
+ {
+ count = m_pMessage->data.raise_state.old_state_count;
+ memcpy(pState, m_pMessage->data.raise_state.old_state, count * sizeof(natural_t));
+ return count;
+ }
+ break;
+ }
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ {
+ // There's a state in the message, but we need to check that the flavor matches what the caller's
+ // after (if not we'll fall through and get the correct flavor below).
+ if (m_pMessage->data.raise_state_64.flavor == eFlavor)
+ {
+ count = m_pMessage->data.raise_state_64.old_state_count;
+ memcpy(pState, m_pMessage->data.raise_state_64.old_state, count * sizeof(natural_t));
+ return count;
+ }
+ break;
+ }
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ {
+ // There's a state in the message, but we need to check that the flavor matches what the caller's
+ // after (if not we'll fall through and get the correct flavor below).
+ if (m_pMessage->data.raise_state_identity.flavor == eFlavor)
+ {
+ count = m_pMessage->data.raise_state_identity.old_state_count;
+ memcpy(pState, m_pMessage->data.raise_state_identity.old_state, count * sizeof(natural_t));
+ return count;
+ }
+ break;
+ }
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ {
+ // There's a state in the message, but we need to check that the flavor matches what the caller's
+ // after (if not we'll fall through and get the correct flavor below).
+ if (m_pMessage->data.raise_state_identity_64.flavor == eFlavor)
+ {
+ count = m_pMessage->data.raise_state_identity_64.old_state_count;
+ memcpy(pState, m_pMessage->data.raise_state_identity_64.old_state, count * sizeof(natural_t));
+ return count;
+ }
+ break;
+ }
+
+ case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+ {
+ // There's a state in the message, but we need to check that the flavor matches what the caller's
+ // after (if not we'll fall through and get the correct flavor below).
+ if (m_pMessage->data.raise_state_reply.flavor == eFlavor)
+ {
+ count = m_pMessage->data.raise_state_reply.new_state_count;
+ memcpy(pState, m_pMessage->data.raise_state_reply.new_state, count * sizeof(natural_t));
+ return count;
+ }
+ break;
+ }
+
+ case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+ {
+ // There's a state in the message, but we need to check that the flavor matches what the caller's
+ // after (if not we'll fall through and get the correct flavor below).
+ if (m_pMessage->data.raise_state_reply_64.flavor == eFlavor)
+ {
+ count = m_pMessage->data.raise_state_reply_64.new_state_count;
+ memcpy(pState, m_pMessage->data.raise_state_reply_64.new_state, count * sizeof(natural_t));
+ return count;
+ }
+ break;
+ }
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+ {
+ // There's a state in the message, but we need to check that the flavor matches what the caller's
+ // after (if not we'll fall through and get the correct flavor below).
+ if (m_pMessage->data.raise_state_identity_reply.flavor == eFlavor)
+ {
+ count = m_pMessage->data.raise_state_identity_reply.new_state_count;
+ memcpy(pState, m_pMessage->data.raise_state_identity_reply.new_state, count * sizeof(natural_t));
+ return count;
+ }
+ break;
+ }
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+ {
+ // There's a state in the message, but we need to check that the flavor matches what the caller's
+ // after (if not we'll fall through and get the correct flavor below).
+ if (m_pMessage->data.raise_state_identity_reply_64.flavor == eFlavor)
+ {
+ count = m_pMessage->data.raise_state_identity_reply_64.new_state_count;
+ memcpy(pState, m_pMessage->data.raise_state_identity_reply_64.new_state, count * sizeof(natural_t));
+ return count;
+ }
+ break;
+ }
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type for requesting thread state.");
+ }
+
+ // No state in the message or the flavor didn't match. Get the requested flavor of state directly from the
+ // thread instead.
+ count = THREAD_STATE_MAX;
+ machret = thread_get_state(thread ? thread : GetThread(), eFlavor, (thread_state_t)pState, &count);
+ CHECK_MACH("thread_get_state()", machret);
+
+ return count;
+}
+
+// Fetch the return code from a reply type message.
+kern_return_t MachMessage::GetReturnCode()
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+ return m_pMessage->data.raise_reply.ret;
+
+ case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+ return m_pMessage->data.raise_reply_64.ret;
+
+ case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+ return m_pMessage->data.raise_state_reply.ret;
+
+ case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_reply_64.ret;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity_reply.ret;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+ return m_pMessage->data.raise_state_identity_reply_64.ret;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
+ }
+}
+
+// Initialize and send a request to set the register context of a particular thread.
+void MachMessage::SendSetThread(mach_port_t hServerPort, CONTEXT *pContext)
+{
+ kern_return_t machret;
+
+ // Set the message type.
+ m_pMessage->header.msgh_id = SET_THREAD_MESSAGE_ID;
+
+ // Initialize the fields that don't need any further input (this depends on the message type having been
+ // set above).
+ InitFixedFields();
+
+ // Initialize type-specific fields. The receiving end is responsible for deallocating the thread port.
+ m_pMessage->data.set_thread.thread = mach_thread_self();
+ memcpy(&m_pMessage->data.set_thread.new_context, pContext, sizeof(CONTEXT));
+
+ // Initialize header fields.
+ m_pMessage->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ m_pMessage->header.msgh_remote_port = hServerPort; // Destination port
+ m_pMessage->header.msgh_local_port = MACH_PORT_NULL; // We expect no reply
+
+ // Set the message header size field based on the contents of the message (call this function after all
+ // other fields have been initialized).
+ InitMessageSize();
+
+ // Send the formatted message.
+ machret = mach_msg((mach_msg_header_t*)m_pMessage,
+ MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
+ m_pMessage->header.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ CHECK_MACH("mach_msg()", machret);
+
+ // Erase any stale data. (This may not finish executing; nothing is needed to be freed here.)
+ ResetMessage();
+}
+
+void MachMessage::SendForwardException(mach_port_t hServerPort, MachExceptionInfo *pExceptionInfo, CPalThread *ppalThread)
+{
+ kern_return_t machret;
+
+ // Set the message type.
+ m_pMessage->header.msgh_id = FORWARD_EXCEPTION_MESSAGE_ID;
+
+ // Initialize the fields that don't need any further input (this depends on the message type having been
+ // set above).
+ InitFixedFields();
+
+ // Initialize type-specific fields. The receiving end is responsible for deallocating the thread port.
+ m_pMessage->data.forward_exception.thread = mach_thread_self();
+ m_pMessage->data.forward_exception.ppalThread = ppalThread;
+ memcpy(&m_pMessage->data.forward_exception.exception_info, pExceptionInfo, sizeof(MachExceptionInfo));
+
+ // Initialize header fields.
+ m_pMessage->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ m_pMessage->header.msgh_remote_port = hServerPort; // Destination port
+ m_pMessage->header.msgh_local_port = MACH_PORT_NULL; // We expect no reply
+
+ // Set the message header size field based on the contents of the message (call this function after all
+ // other fields have been initialized).
+ InitMessageSize();
+
+ // Send the formatted message.
+ machret = mach_msg((mach_msg_header_t*)m_pMessage,
+ MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
+ m_pMessage->header.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ CHECK_MACH("mach_msg()", machret);
+
+ // Erase any stale data.
+ ResetMessage();
+}
+
+// Returns the pal thread instance for the forward exception message
+CPalThread *MachMessage::GetPalThread()
+{
+ NONPAL_ASSERTE(IsForwardExceptionRequest());
+ return m_pMessage->data.forward_exception.ppalThread;
+}
+
+MachExceptionInfo *MachMessage::GetExceptionInfo()
+{
+ NONPAL_ASSERTE(IsForwardExceptionRequest());
+ return &m_pMessage->data.forward_exception.exception_info;
+}
+
+// Initialize the message to represent a forwarded version of the given exception notification message and
+// send that message to the chain-back handler previously registered for the exception type being notified.
+// The new message takes account of the fact that the target handler may not have requested the same notification
+// behavior or flavor as our handler. A new Mach port is created to receive the reply, and this port is returned
+// to the caller. Clean up the message afterwards.
+void MachMessage::ForwardNotification(MachExceptionHandler *pHandler, MachMessage& message)
+{
+ kern_return_t machret;
+
+ // Set the message type.
+ m_pMessage->header.msgh_id = MapBehaviorToNotificationType(pHandler->m_behavior);
+
+ // Initialize the fields that don't need any further input (this depends on the message type having been
+ // set above).
+ InitFixedFields();
+
+ // Copy data from the incoming message. Use the getter and setter abstractions to simplify the act that
+ // the two messages may be in different formats (e.g. RAISE vs RAISE_STATE). We silently drop data that is
+ // not needed in the outgoing message and synthesize any required data that is not present in the incoming
+ // message.
+ SetThread(message.GetThread());
+ SetException(message.GetException());
+
+ int cCodes = message.GetExceptionCodeCount();
+ SetExceptionCodeCount(cCodes);
+ for (int i = 0; i < cCodes; i++)
+ SetExceptionCode(i, message.GetExceptionCode(i));
+
+ // Don't bother fetching thread state unless the destination actually requires it.
+ if (pHandler->m_flavor != THREAD_STATE_NONE)
+ {
+ thread_state_data_t threadState;
+ mach_msg_type_number_t count = message.GetThreadState(pHandler->m_flavor, (thread_state_t)&threadState);
+ SetThreadState(pHandler->m_flavor, (thread_state_t)&threadState, count);
+ }
+
+ // Initialize header fields.
+ m_pMessage->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ m_pMessage->header.msgh_remote_port = pHandler->m_handler; // Forward to here
+ m_pMessage->header.msgh_local_port = message.GetRemotePort(); // The reply will come here
+
+ // Set the message header size field based on the contents of the message (call this function after all
+ // other fields have been initialized).
+ InitMessageSize();
+
+ // Send the formatted message.
+ machret = mach_msg((mach_msg_header_t*)m_pMessage,
+ MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
+ m_pMessage->header.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ CHECK_MACH("mach_msg()", machret);
+
+ // Erase any stale data.
+ ResetMessage();
+}
+
+// Initialize the message to represent a reply to the given exception notification message
+// and send that reply back to the original sender of the notification. This is used when
+// our handler handles the exception rather than forwarding it to a chain-back handler.
+// Clean up the message afterwards.
+void MachMessage::ReplyToNotification(MachMessage& message, kern_return_t eResult)
+{
+ kern_return_t machret;
+
+ // Set the message type.
+ m_pMessage->header.msgh_id = MapNotificationToReplyType(message.m_pMessage->header.msgh_id);
+
+ // Initialize the fields that don't need any further input (this depends on the message type having been
+ // set above).
+ InitFixedFields();
+
+ SetReturnCode(eResult);
+
+ thread_state_flavor_t eNotificationFlavor = message.GetThreadStateFlavor();
+ if (eNotificationFlavor != THREAD_STATE_NONE)
+ {
+ // If the reply requires a thread state be sure to get it from the thread directly rather than the
+ // notification message (handling the exception is likely to have changed the thread state).
+ thread_state_data_t threadState;
+ mach_msg_type_number_t count = THREAD_STATE_MAX;
+ machret = thread_get_state(message.GetThread(), eNotificationFlavor, (thread_state_t)&threadState, &count);
+
+ SetThreadState(eNotificationFlavor, (thread_state_t)&threadState, count);
+ }
+
+ // Initialize header fields.
+ m_pMessage->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0);
+ m_pMessage->header.msgh_remote_port = message.GetRemotePort(); // Reply goes back to sender
+ m_pMessage->header.msgh_local_port = 0; // No reply to this expected
+
+ // Set the message header size field based on the contents of the message (call this function after all
+ // other fields have been initialized).
+ InitMessageSize();
+
+ // Send the formatted message.
+ machret = mach_msg((mach_msg_header_t*)m_pMessage,
+ MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
+ m_pMessage->header.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ CHECK_MACH("mach_msg()", machret);
+
+ // Erase any stale data.
+ ResetMessage();
+}
+
+// Re-initializes this data structure (to the same state as default construction, containing no message).
+void MachMessage::ResetMessage()
+{
+ // Clean up ports if we own them.
+ if (m_fPortsOwned)
+ {
+ kern_return_t machret;
+
+ GetPorts(false /* fCalculate */, false /* fValidThread */);
+ if (m_hThread != MACH_PORT_NULL)
+ {
+ machret = mach_port_deallocate(mach_task_self(), m_hThread);
+ CHECK_MACH("mach_port_deallocate(m_hThread)", machret);
+ }
+
+ if (m_hTask != MACH_PORT_NULL)
+ {
+ machret = mach_port_deallocate(mach_task_self(), m_hTask);
+ CHECK_MACH("mach_port_deallocate(m_hTask)", machret);
+ }
+ }
+
+#ifdef _DEBUG
+ memset(this, 0xcc, sizeof(*this));
+#endif
+
+ m_pMessage = (mach_message_t*)m_rgMessageBuffer;
+ m_hThread = MACH_PORT_NULL;
+ m_hTask = MACH_PORT_NULL;
+ m_fPortsOwned = false;
+}
+
+// Initialize those fields of a message that are invariant. This method expects that the msgh_id field has
+// been filled in prior to the call so it can determine which non-header fields to initialize.
+void MachMessage::InitFixedFields()
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case SET_THREAD_MESSAGE_ID:
+ break;
+
+ case FORWARD_EXCEPTION_MESSAGE_ID:
+ break;
+
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ m_pMessage->data.raise.msgh_body.msgh_descriptor_count = 0;
+ m_pMessage->data.raise.ndr = NDR_record;
+ m_pMessage->data.raise.task_port.name = mach_task_self();
+ m_pMessage->data.raise.task_port.pad1 = 0;
+ m_pMessage->data.raise.task_port.pad2 = 0;
+ m_pMessage->data.raise.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
+ m_pMessage->data.raise.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
+ m_hTask = mach_task_self();
+ break;
+
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ m_pMessage->data.raise_64.msgh_body.msgh_descriptor_count = 0;
+ m_pMessage->data.raise_64.ndr = NDR_record;
+ m_pMessage->data.raise_64.task_port.name = mach_task_self();
+ m_pMessage->data.raise_64.task_port.pad1 = 0;
+ m_pMessage->data.raise_64.task_port.pad2 = 0;
+ m_pMessage->data.raise_64.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
+ m_pMessage->data.raise_64.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
+ m_hTask = mach_task_self();
+ break;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ m_pMessage->data.raise_state.ndr = NDR_record;
+ break;
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_64.ndr = NDR_record;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity.msgh_body.msgh_descriptor_count = 0;
+ m_pMessage->data.raise_state_identity.ndr = NDR_record;
+ m_pMessage->data.raise_state_identity.task_port.name = mach_task_self();
+ m_pMessage->data.raise_state_identity.task_port.pad1 = 0;
+ m_pMessage->data.raise_state_identity.task_port.pad2 = 0;
+ m_pMessage->data.raise_state_identity.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
+ m_pMessage->data.raise_state_identity.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
+ m_hTask = mach_task_self();
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_64.msgh_body.msgh_descriptor_count = 0;
+ m_pMessage->data.raise_state_identity_64.ndr = NDR_record;
+ m_pMessage->data.raise_state_identity_64.task_port.name = mach_task_self();
+ m_pMessage->data.raise_state_identity_64.task_port.pad1 = 0;
+ m_pMessage->data.raise_state_identity_64.task_port.pad2 = 0;
+ m_pMessage->data.raise_state_identity_64.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
+ m_pMessage->data.raise_state_identity_64.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
+ m_hTask = mach_task_self();
+ break;
+
+ case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+ m_pMessage->data.raise_reply.ndr = NDR_record;
+ break;
+
+ case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+ m_pMessage->data.raise_reply_64.ndr = NDR_record;
+ break;
+
+ case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+ m_pMessage->data.raise_state_reply.ndr = NDR_record;
+ break;
+
+ case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_reply_64.ndr = NDR_record;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_reply.ndr = NDR_record;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_reply_64.ndr = NDR_record;
+ break;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unhandled message type: %u", m_pMessage->header.msgh_id);
+ }
+
+ m_pMessage->header.msgh_reserved = 0;
+
+ if (m_hTask)
+ {
+ kern_return_t machret;
+ // Addref the task, because the receiver will expect it to own it. (or, if we
+ // free it unsent, we'll expect to deallocate it).
+ machret = mach_port_mod_refs(mach_task_self(), m_hTask, MACH_PORT_RIGHT_SEND, 1);
+ }
+}
+
+// Initialize the size field of the message header (msgh_size) based on the message type and other fields.
+// This should be called after all other fields have been initialized.
+void MachMessage::InitMessageSize()
+{
+ // Note that in particular the kernel is very particular about the size of messages with embedded thread
+ // states. The size of the message must reflect the exact size of the state flavor contained, not the
+ // maximum size of a thread state that the message format implies.
+
+ switch (m_pMessage->header.msgh_id)
+ {
+ case SET_THREAD_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(set_thread_request_t);
+ break;
+
+ case FORWARD_EXCEPTION_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(forward_exception_request_t);
+ break;
+
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(exception_raise_notification_t);
+ break;
+
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(exception_raise_notification_64_t);
+ break;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
+ offsetof(exception_raise_state_notification_t, old_state) +
+ (m_pMessage->data.raise_state.old_state_count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
+ offsetof(exception_raise_state_notification_64_t, old_state) +
+ (m_pMessage->data.raise_state_64.old_state_count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
+ offsetof(exception_raise_state_identity_notification_t, old_state) +
+ (m_pMessage->data.raise_state_identity.old_state_count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
+ offsetof(exception_raise_state_identity_notification_64_t, old_state) +
+ (m_pMessage->data.raise_state_identity_64.old_state_count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(exception_raise_reply_t);
+ break;
+
+ case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(exception_raise_reply_64_t);
+ break;
+
+ case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
+ offsetof(exception_raise_state_reply_t, new_state) +
+ (m_pMessage->data.raise_state_reply.new_state_count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
+ offsetof(exception_raise_state_reply_64_t, new_state) +
+ (m_pMessage->data.raise_state_reply_64.new_state_count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
+ offsetof(exception_raise_state_identity_reply_t, new_state) +
+ (m_pMessage->data.raise_state_identity_reply.new_state_count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+ m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
+ offsetof(exception_raise_state_identity_reply_64_t, new_state) +
+ (m_pMessage->data.raise_state_identity_reply_64.new_state_count * sizeof(natural_t));
+ break;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unhandled message type: %u", m_pMessage->header.msgh_id);
+ }
+}
+
+// Given a thread's register context, locate and return the Mach port representing that thread. Only the
+// x86_THREAD_STATE and x86_THREAD_STATE32 state flavors are supported for 32-bit.
+thread_act_t MachMessage::GetThreadFromState(thread_state_flavor_t eFlavor, thread_state_t pState)
+{
+ SIZE_T targetSP;
+
+ // Determine SP from the state provided based on its flavor (this algorithm only works with SP, so
+ // flavors that don't report this register can't be used). However, hosts that use RAISE_STATE and a
+ // flavor of state that don't contain SP should be very, very rare indeed (it's hard to imagine many
+ // useful exception handlers that receive neither the exception thread or the general registers of that
+ // thread).
+ switch (eFlavor)
+ {
+#ifdef _X86_
+ case x86_THREAD_STATE:
+ targetSP = ((x86_thread_state_t*)pState)->uts.ts32.esp;
+ break;
+
+ case x86_THREAD_STATE32:
+ targetSP = ((x86_thread_state32_t*)pState)->esp;
+ break;
+#elif defined(_AMD64_)
+ case x86_THREAD_STATE:
+ targetSP = ((x86_thread_state_t*)pState)->uts.ts64.__rsp;
+ break;
+
+ case x86_THREAD_STATE64:
+ targetSP = ((x86_thread_state64_t*)pState)->__rsp;
+ break;
+#else
+#error Unexpected architecture.
+#endif
+ default:
+ NONPAL_RETAIL_ASSERT("Unhandled thread state flavor: %u", eFlavor);
+ }
+
+ // Capture the list of threads in the current task. Obviously this changes asynchronously to us, but that
+ // doesn't matter since we know the thread we're after is suspended in the kernel and can't go anywhere.
+ mach_msg_type_number_t cThreads;
+ thread_act_t *pThreads;
+ kern_return_t machret = task_threads(mach_task_self(), &pThreads, &cThreads);
+ CHECK_MACH("task_threads()", machret);
+
+ // Iterate through each of the threads in the list.
+ for (mach_msg_type_number_t i = 0; i < cThreads; i++)
+ {
+ // Get the general register state of each thread.
+ x86_thread_state_t threadState;
+ mach_msg_type_number_t count = x86_THREAD_STATE_COUNT;
+ machret = thread_get_state(pThreads[i], x86_THREAD_STATE, (thread_state_t)&threadState, &count);
+ if (machret == KERN_SUCCESS)
+ {
+ // If a thread has the same SP as our target it should be the same thread (otherwise we have two
+ // threads sharing the same stack which is very bad). Conversely the thread we're looking for is
+ // suspended in the kernel so its SP should not change. We should always be able to find an exact
+ // match as a result.
+#ifdef _X86_
+ if (threadState.uts.ts32.esp == targetSP)
+#elif defined(_AMD64_)
+ if (threadState.uts.ts64.__rsp == targetSP)
+#else
+#error Unexpected architecture.
+#endif
+ {
+ thread_act_t thread = pThreads[i];
+
+ // Increment the refcount; the thread is a "send" right.
+ machret = mach_port_mod_refs(mach_task_self(), thread, MACH_PORT_RIGHT_SEND, 1);
+ CHECK_MACH("mach_port_mod_refs()", machret);
+
+ // Deallocate the thread list now we're done with it.
+ machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t));
+ CHECK_MACH("vm_deallocate()", machret);
+
+ // Return the thread we found.
+ return thread;
+ }
+ }
+ }
+
+ // If we got here no thread matched. That shouldn't be possible.
+ NONPAL_RETAIL_ASSERT("Failed to locate thread from state.");
+}
+
+// Transform a exception handler behavior type into the corresponding Mach message ID for the notification.
+mach_msg_id_t MachMessage::MapBehaviorToNotificationType(exception_behavior_t eBehavior)
+{
+ switch ((uint)eBehavior)
+ {
+ case EXCEPTION_DEFAULT:
+ return EXCEPTION_RAISE_MESSAGE_ID;
+ case EXCEPTION_STATE:
+ return EXCEPTION_RAISE_STATE_MESSAGE_ID;
+ case EXCEPTION_STATE_IDENTITY:
+ return EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID;
+ case MACH_EXCEPTION_CODES|EXCEPTION_DEFAULT:
+ return EXCEPTION_RAISE_64_MESSAGE_ID;
+ case MACH_EXCEPTION_CODES|EXCEPTION_STATE:
+ return EXCEPTION_RAISE_STATE_64_MESSAGE_ID;
+ case MACH_EXCEPTION_CODES|EXCEPTION_STATE_IDENTITY:
+ return EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID;
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported exception behavior type: %u", eBehavior);
+ }
+}
+
+// Transform a Mach message ID for an exception notification into the corresponding ID for the reply.
+mach_msg_id_t MachMessage::MapNotificationToReplyType(mach_msg_id_t eNotificationType)
+{
+ switch (eNotificationType)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ return EXCEPTION_RAISE_REPLY_MESSAGE_ID;
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ return EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID;
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ return EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID;
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ return EXCEPTION_RAISE_REPLY_64_MESSAGE_ID;
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ return EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID;
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ return EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID;
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type: %u", eNotificationType);
+ }
+}
+
+// Set faulting thread in an exception notification message.
+void MachMessage::SetThread(thread_act_t thread)
+{
+ bool fSet = false;
+
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ m_pMessage->data.raise.thread_port.name = thread;
+ m_pMessage->data.raise.thread_port.pad1 = 0;
+ m_pMessage->data.raise.thread_port.pad2 = 0;
+ m_pMessage->data.raise.thread_port.disposition = MACH_MSG_TYPE_COPY_SEND;
+ m_pMessage->data.raise.thread_port.type = MACH_MSG_PORT_DESCRIPTOR;
+ fSet = true;
+ break;
+
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ m_pMessage->data.raise_64.thread_port.name = thread;
+ m_pMessage->data.raise_64.thread_port.pad1 = 0;
+ m_pMessage->data.raise_64.thread_port.pad2 = 0;
+ m_pMessage->data.raise_64.thread_port.disposition = MACH_MSG_TYPE_COPY_SEND;
+ m_pMessage->data.raise_64.thread_port.type = MACH_MSG_PORT_DESCRIPTOR;
+ fSet = true;
+ break;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ // No thread field in RAISE_STATE messages.
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity.thread_port.name = thread;
+ m_pMessage->data.raise_state_identity.thread_port.pad1 = 0;
+ m_pMessage->data.raise_state_identity.thread_port.pad2 = 0;
+ m_pMessage->data.raise_state_identity.thread_port.disposition = MACH_MSG_TYPE_COPY_SEND;
+ m_pMessage->data.raise_state_identity.thread_port.type = MACH_MSG_PORT_DESCRIPTOR;
+ fSet = true;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_64.thread_port.name = thread;
+ m_pMessage->data.raise_state_identity_64.thread_port.pad1 = 0;
+ m_pMessage->data.raise_state_identity_64.thread_port.pad2 = 0;
+ m_pMessage->data.raise_state_identity_64.thread_port.disposition = MACH_MSG_TYPE_COPY_SEND;
+ m_pMessage->data.raise_state_identity_64.thread_port.type = MACH_MSG_PORT_DESCRIPTOR;
+ fSet = true;
+ break;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
+ }
+
+ if (fSet)
+ {
+ // Addref the thread port.
+ kern_return_t machret;
+ machret = mach_port_mod_refs(mach_task_self(), thread, MACH_PORT_RIGHT_SEND, 1);
+ }
+}
+
+// Set exception type in an exception notification message.
+void MachMessage::SetException(exception_type_t eException)
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ m_pMessage->data.raise.exception = eException;
+ break;
+
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ m_pMessage->data.raise_64.exception = eException;
+ break;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ m_pMessage->data.raise_state.exception = eException;
+ break;
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_64.exception = eException;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity.exception = eException;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_64.exception = eException;
+ break;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
+ }
+}
+
+// Set exception sub-code count in an exception notification message.
+void MachMessage::SetExceptionCodeCount(int cCodes)
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ m_pMessage->data.raise.code_count = cCodes;
+ break;
+
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ m_pMessage->data.raise_64.code_count = cCodes;
+ break;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ m_pMessage->data.raise_state.code_count = cCodes;
+ break;
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_64.code_count = cCodes;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity.code_count = cCodes;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_64.code_count = cCodes;
+ break;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
+ }
+}
+
+// Set exception sub-code in an exception notification message.
+void MachMessage::SetExceptionCode(int iIndex, MACH_EH_TYPE(exception_data_type_t) iCode)
+{
+ if (iIndex < 0 || iIndex > 1)
+ NONPAL_RETAIL_ASSERT("Exception code index out of range");
+
+ // Note that although the 64-bit message variants support 64-bit exception sub-codes the CoreCLR only
+ // supports 32-bit processes. We should never see the upper 32-bits containing a non-zero value therefore.
+
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ m_pMessage->data.raise.code[iIndex] = (int)iCode;
+ break;
+
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ m_pMessage->data.raise_64.code[iIndex] = iCode;
+ break;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ m_pMessage->data.raise_state.code[iIndex] = (int)iCode;
+ break;
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_64.code[iIndex] = iCode;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity.code[iIndex] = (int)iCode;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_64.code[iIndex] = iCode;
+ break;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
+ }
+}
+
+// Set return code in a reply message.
+void MachMessage::SetReturnCode(kern_return_t eReturnCode)
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+ m_pMessage->data.raise_reply.ret = eReturnCode;
+ break;
+
+ case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+ m_pMessage->data.raise_reply_64.ret = eReturnCode;
+ break;
+
+ case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+ m_pMessage->data.raise_state_reply.ret = eReturnCode;
+ break;
+
+ case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_reply_64.ret = eReturnCode;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_reply.ret = eReturnCode;
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_reply_64.ret = eReturnCode;
+ break;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
+ }
+}
+
+// Set faulting thread register state in an exception notification or reply message.
+void MachMessage::SetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, mach_msg_type_number_t count)
+{
+ switch (m_pMessage->header.msgh_id)
+ {
+ case EXCEPTION_RAISE_MESSAGE_ID:
+ case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+ case EXCEPTION_RAISE_64_MESSAGE_ID:
+ case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+ // No thread state in RAISE or RAISE_REPLY messages.
+ break;
+
+ case EXCEPTION_RAISE_STATE_MESSAGE_ID:
+ m_pMessage->data.raise_state.flavor = eFlavor;
+ m_pMessage->data.raise_state.old_state_count = count;
+ memcpy(m_pMessage->data.raise_state.old_state, pState, count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_64.flavor = eFlavor;
+ m_pMessage->data.raise_state_64.old_state_count = count;
+ memcpy(m_pMessage->data.raise_state_64.old_state, pState, count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity.flavor = eFlavor;
+ m_pMessage->data.raise_state_identity.old_state_count = count;
+ memcpy(m_pMessage->data.raise_state_identity.old_state, pState, count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_64.flavor = eFlavor;
+ m_pMessage->data.raise_state_identity_64.old_state_count = count;
+ memcpy(m_pMessage->data.raise_state_identity_64.old_state, pState, count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+ m_pMessage->data.raise_state_reply.flavor = eFlavor;
+ m_pMessage->data.raise_state_reply.new_state_count = count;
+ memcpy(m_pMessage->data.raise_state_reply.new_state, pState, count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_reply_64.flavor = eFlavor;
+ m_pMessage->data.raise_state_reply_64.new_state_count = count;
+ memcpy(m_pMessage->data.raise_state_reply_64.new_state, pState, count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_reply.flavor = eFlavor;
+ m_pMessage->data.raise_state_identity_reply.new_state_count = count;
+ memcpy(m_pMessage->data.raise_state_identity_reply.new_state, pState, count * sizeof(natural_t));
+ break;
+
+ case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+ m_pMessage->data.raise_state_identity_reply_64.flavor = eFlavor;
+ m_pMessage->data.raise_state_identity_reply_64.new_state_count = count;
+ memcpy(m_pMessage->data.raise_state_identity_reply_64.new_state, pState, count * sizeof(natural_t));
+ break;
+
+ default:
+ NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
+ }
+}
+
+#endif // HAVE_MACH_EXCEPTIONS
diff --git a/src/pal/src/exception/machmessage.h b/src/pal/src/exception/machmessage.h
new file mode 100644
index 0000000000..244396cd35
--- /dev/null
+++ b/src/pal/src/exception/machmessage.h
@@ -0,0 +1,441 @@
+// 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.
+
+/*++
+
+Module Name:
+
+ machmessage.h
+
+Abstract:
+
+ Abstraction over Mach messages used during exception handling.
+
+--*/
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <mach/thread_status.h>
+
+using namespace CorUnix;
+
+#if HAVE_MACH_EXCEPTIONS
+
+#if defined(_AMD64_)
+#define MACH_EH_TYPE(x) mach_##x
+#else
+#define MACH_EH_TYPE(x) x
+#endif // defined(_AMD64_)
+
+// The vast majority of Mach calls we make in this module are critical: we cannot recover from failures of
+// these methods (principally because we're handling hardware exceptions in the context of a single dedicated
+// handler thread). The following macro encapsulates checking the return code from Mach methods and emitting
+// some useful data and aborting the process on failure.
+#define CHECK_MACH(_msg, machret) do { \
+ if (machret != KERN_SUCCESS) \
+ { \
+ char _szError[1024]; \
+ sprintf(_szError, "%s: %u: %s", __FUNCTION__, __LINE__, _msg); \
+ mach_error(_szError, machret); \
+ abort(); \
+ } \
+ } while (false)
+
+// This macro terminates the process with some useful debug info as above, but for the general failure points
+// that have nothing to do with Mach.
+#define NONPAL_RETAIL_ASSERT(_msg, ...) do { \
+ printf("%s: %u: " _msg "\n", __FUNCTION__, __LINE__, ## __VA_ARGS__); \
+ abort(); \
+ } while (false)
+
+#define NONPAL_RETAIL_ASSERTE(_expr) do { \
+ if (!(_expr)) \
+ NONPAL_RETAIL_ASSERT("ASSERT: %s\n", #_expr); \
+ } while (false)
+
+#ifdef _DEBUG
+
+#define NONPAL_TRACE_ENABLED EnvironGetenv("NONPAL_TRACING", /* copyValue */ false)
+
+#define NONPAL_ASSERT(_msg, ...) NONPAL_RETAIL_ASSERT(_msg, __VA_ARGS__)
+
+// Assert macro that doesn't rely on the PAL.
+#define NONPAL_ASSERTE(_expr) do { \
+ if (!(_expr)) \
+ NONPAL_RETAIL_ASSERT("ASSERT: %s\n", #_expr); \
+ } while (false)
+
+// Debug-only output with printf-style formatting.
+#define NONPAL_TRACE(_format, ...) do { \
+ if (NONPAL_TRACE_ENABLED) printf("NONPAL_TRACE: " _format, ## __VA_ARGS__); \
+ } while (false)
+
+#else // _DEBUG
+
+#define NONPAL_TRACE_ENABLED false
+#define NONPAL_ASSERT(_msg, ...)
+#define NONPAL_ASSERTE(_expr)
+#define NONPAL_TRACE(_format, ...)
+
+#endif // _DEBUG
+
+class MachMessage;
+
+// Contains all the exception and thread state information needed to forward the exception.
+struct MachExceptionInfo
+{
+ exception_type_t ExceptionType;
+ mach_msg_type_number_t SubcodeCount;
+ MACH_EH_TYPE(exception_data_type_t) Subcodes[2];
+ x86_thread_state_t ThreadState;
+ x86_float_state_t FloatState;
+ x86_debug_state_t DebugState;
+
+ MachExceptionInfo(mach_port_t thread, MachMessage& message);
+ void RestoreState(mach_port_t thread);
+};
+
+// Abstraction of a subset of Mach message types. Provides accessors that hide the subtle differences in the
+// message layout of similar message types.
+class MachMessage
+{
+public:
+ // The message types handled by this class. The values are the actual type codes set in the Mach message
+ // header.
+ enum MessageType
+ {
+ SET_THREAD_MESSAGE_ID = 1,
+ FORWARD_EXCEPTION_MESSAGE_ID = 2,
+ NOTIFY_SEND_ONCE_MESSAGE_ID = 71,
+ EXCEPTION_RAISE_MESSAGE_ID = 2401,
+ EXCEPTION_RAISE_STATE_MESSAGE_ID = 2402,
+ EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID = 2403,
+ EXCEPTION_RAISE_64_MESSAGE_ID = 2405,
+ EXCEPTION_RAISE_STATE_64_MESSAGE_ID = 2406,
+ EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID = 2407,
+ EXCEPTION_RAISE_REPLY_MESSAGE_ID = 2501,
+ EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID = 2502,
+ EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID = 2503,
+ EXCEPTION_RAISE_REPLY_64_MESSAGE_ID = 2505,
+ EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID = 2506,
+ EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID = 2507
+ };
+
+ // Construct an empty message. Use Receive() to form a message that can be inspected or SendSetThread(),
+ // ForwardNotification() or ReplyToNotification() to construct a message and sent it.
+ MachMessage();
+
+ // Listen for the next message on the given port and initialize this class with the contents. The message
+ // type must match one of the MessageTypes indicated above (or the process will be aborted).
+ void Receive(mach_port_t hPort);
+
+ // Indicate whether a received message belongs to a particular semantic class.
+ bool IsSetThreadRequest(); // Message is a request to set the context of a particular thread
+ bool IsForwardExceptionRequest(); // Message is a request to forward the exception
+ bool IsSendOnceDestroyedNotify(); // Message is a notification that a send-once message was destroyed by the receiver
+ bool IsExceptionNotification(); // Message is a notification of an exception
+ bool IsExceptionReply(); // Message is a reply to the notification of an exception
+
+ // Get properties of a received message header.
+ MessageType GetMessageType(); // The message type
+ const char *GetMessageTypeName(); // An ASCII representation of the message type for logging purposes
+ mach_port_t GetLocalPort(); // The destination port the message was sent to
+ mach_port_t GetRemotePort(); // The source port the message came from (if a reply is expected)
+
+ // Get the properties of a set thread request. Fills in the provided context structure with the context
+ // from the message and returns the target thread to which the context should be applied.
+ thread_act_t GetThreadContext(CONTEXT *pContext);
+
+ // Returns the pal thread instance for the forward exception message
+ CPalThread *GetPalThread();
+
+ // Returns the exception info from the forward exception message
+ MachExceptionInfo *GetExceptionInfo();
+
+ // Get properties of the type-specific portion of the message. The following properties are supported by
+ // exception notification messages only.
+ thread_act_t GetThread(); // Get the faulting thread
+ exception_type_t GetException(); // Get the exception type (e.g. EXC_BAD_ACCESS)
+ int GetExceptionCodeCount(); // Get the number of exception sub-codes
+ MACH_EH_TYPE(exception_data_type_t) GetExceptionCode(int iIndex); // Get the exception sub-code at the given index
+
+ // Fetch the thread state flavor from a notification or reply message (return THREAD_STATE_NONE for the
+ // messages that don't contain a thread state).
+ thread_state_flavor_t GetThreadStateFlavor();
+
+ // Get the thread state with the given flavor from the exception or exception reply message. If the
+ // message doesn't contain a thread state or the flavor of the state in the message doesn't match, the
+ // state will be fetched directly from the target thread instead (which can be computed implicitly for
+ // exception messages or passed explicitly for reply messages).
+ mach_msg_type_number_t GetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, thread_act_t thread = NULL);
+
+ // Fetch the return code from a reply type message.
+ kern_return_t GetReturnCode();
+
+ // Initialize and send a request to set the register context of a particular thread.
+ void SendSetThread(mach_port_t hServerPort, CONTEXT *pContext);
+
+ // Initialize and send a request to forward the exception message to the notification thread
+ void SendForwardException(mach_port_t hServerPort, MachExceptionInfo *pExceptionInfo, CPalThread *ppalThread);
+
+ // Initialize the message (overwriting any previous content) to represent a forwarded version of the given
+ // exception notification message and send that message to the chain-back handler previously registered
+ // for the exception type being notified. The new message takes account of the fact that the target
+ // handler may not have requested the same notification behavior or flavor as our handler.
+ void ForwardNotification(MachExceptionHandler *pHandler, MachMessage& message);
+
+ // Initialize the message (overwriting any previous content) to represent a reply to the given exception
+ // notification and send that reply back to the original sender of the notification. This is used when our
+ // handler handles the exception rather than forwarding it to a chain-back handler.
+ void ReplyToNotification(MachMessage& message, kern_return_t eResult);
+
+private:
+ // The maximum size in bytes of any Mach message we can send or receive. Calculating an exact size for
+ // this is non trivial (basically because of the security trailers that Mach appends) but the current
+ // value has proven to be more than enough so far.
+ static const size_t kcbMaxMessageSize = 1500;
+
+ // The following are structures describing the formats of the Mach messages we understand.
+
+ // Request to set the register context on a particular thread.
+ // SET_THREAD_MESSAGE_ID
+ struct set_thread_request_t
+ {
+ thread_act_t thread;
+ CONTEXT new_context;
+ };
+
+ // Request to forward the exception notification
+ // FORWARD_EXCEPTION_MESSAGE_ID
+ struct forward_exception_request_t
+ {
+ thread_act_t thread;
+ CPalThread *ppalThread;
+ MachExceptionInfo exception_info;
+ };
+
+#pragma pack(4)
+
+ // EXCEPTION_RAISE_MESSAGE_ID
+ struct exception_raise_notification_t
+ {
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread_port;
+ mach_msg_port_descriptor_t task_port;
+ NDR_record_t ndr;
+ exception_type_t exception;
+ mach_msg_type_number_t code_count;
+ exception_data_type_t code[2];
+ };
+
+ // EXCEPTION_RAISE_REPLY_MESSAGE_ID
+ struct exception_raise_reply_t
+ {
+ NDR_record_t ndr;
+ kern_return_t ret;
+ };
+
+ // EXCEPTION_RAISE_64_MESSAGE_ID
+ struct exception_raise_notification_64_t
+ {
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread_port;
+ mach_msg_port_descriptor_t task_port;
+ NDR_record_t ndr;
+ exception_type_t exception;
+ mach_msg_type_number_t code_count;
+ mach_exception_data_type_t code[2];
+ };
+
+ // EXCEPTION_RAISE_REPLY_64_MESSAGE_ID
+ struct exception_raise_reply_64_t
+ {
+ NDR_record_t ndr;
+ kern_return_t ret;
+ };
+
+ // EXCEPTION_RAISE_STATE_MESSAGE_ID
+ struct exception_raise_state_notification_t
+ {
+ NDR_record_t ndr;
+ exception_type_t exception;
+ mach_msg_type_number_t code_count;
+ exception_data_type_t code[2];
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t old_state_count;
+ natural_t old_state[THREAD_STATE_MAX];
+ };
+
+ // EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID
+ struct exception_raise_state_reply_t
+ {
+ NDR_record_t ndr;
+ kern_return_t ret;
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t new_state_count;
+ natural_t new_state[THREAD_STATE_MAX];
+ };
+
+ // EXCEPTION_RAISE_STATE_64_MESSAGE_ID
+ struct exception_raise_state_notification_64_t
+ {
+ NDR_record_t ndr;
+ exception_type_t exception;
+ mach_msg_type_number_t code_count;
+ mach_exception_data_type_t code[2];
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t old_state_count;
+ natural_t old_state[THREAD_STATE_MAX];
+ };
+
+ // EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID
+ struct exception_raise_state_reply_64_t
+ {
+ NDR_record_t ndr;
+ kern_return_t ret;
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t new_state_count;
+ natural_t new_state[THREAD_STATE_MAX];
+ };
+
+ // EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID
+ struct exception_raise_state_identity_notification_t
+ {
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread_port;
+ mach_msg_port_descriptor_t task_port;
+ NDR_record_t ndr;
+ exception_type_t exception;
+ mach_msg_type_number_t code_count;
+ exception_data_type_t code[2];
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t old_state_count;
+ natural_t old_state[THREAD_STATE_MAX];
+ };
+
+ // EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID
+ struct exception_raise_state_identity_reply_t
+ {
+ NDR_record_t ndr;
+ kern_return_t ret;
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t new_state_count;
+ natural_t new_state[THREAD_STATE_MAX];
+ };
+
+ // EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID
+ struct exception_raise_state_identity_notification_64_t
+ {
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread_port;
+ mach_msg_port_descriptor_t task_port;
+ NDR_record_t ndr;
+ exception_type_t exception;
+ mach_msg_type_number_t code_count;
+ mach_exception_data_type_t code[2];
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t old_state_count;
+ natural_t old_state[THREAD_STATE_MAX];
+ };
+
+ // EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID
+ struct exception_raise_state_identity_reply_64_t
+ {
+ NDR_record_t ndr;
+ kern_return_t ret;
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t new_state_count;
+ natural_t new_state[THREAD_STATE_MAX];
+ };
+
+#pragma pack()
+
+ // All the above messages are sent with a standard Mach header prepended. This structure unifies the
+ // message formats.
+ struct mach_message_t
+ {
+ mach_msg_header_t header;
+ union
+ {
+ set_thread_request_t set_thread;
+ forward_exception_request_t forward_exception;
+ exception_raise_notification_t raise;
+ exception_raise_state_notification_t raise_state;
+ exception_raise_state_identity_notification_t raise_state_identity;
+ exception_raise_notification_64_t raise_64;
+ exception_raise_state_notification_64_t raise_state_64;
+ exception_raise_state_identity_notification_64_t raise_state_identity_64;
+ exception_raise_reply_t raise_reply;
+ exception_raise_state_reply_t raise_state_reply;
+ exception_raise_state_identity_reply_t raise_state_identity_reply;
+ exception_raise_reply_64_t raise_reply_64;
+ exception_raise_state_reply_64_t raise_state_reply_64;
+ exception_raise_state_identity_reply_64_t raise_state_identity_reply_64;
+ } data;
+ } __attribute__((packed));;
+
+ // Re-initializes this data structure (to the same state as default construction, containing no message).
+ void ResetMessage();
+
+ // Initialize those fields of a message that are invariant. This method expects that the msgh_id field has
+ // been filled in prior to the call so it can determine which non-header fields to initialize.
+ void InitFixedFields();
+
+ // Initialize the size field of the message header (msgh_size) based on the message type and other fields.
+ // This should be called after all other fields have been initialized.
+ void InitMessageSize();
+
+ // Do the work of getting ports from the message.
+ // * fCalculate -- calculate the thread port if the message did not contain it.
+ // * fValidate -- failfast if the message was not one expected to have a (calculable) thread port.
+ void GetPorts(bool fCalculate, bool fValidThread);
+
+ // Given a thread's register context, locate and return the Mach port representing that thread. Only the
+ // x86_THREAD_STATE and x86_THREAD_STATE32 state flavors are supported.
+ thread_act_t GetThreadFromState(thread_state_flavor_t eFlavor, thread_state_t pState);
+
+ // Transform a exception handler behavior type into the corresponding Mach message ID for the
+ // notification.
+ mach_msg_id_t MapBehaviorToNotificationType(exception_behavior_t eBehavior);
+
+ // Transform a Mach message ID for an exception notification into the corresponding ID for the reply.
+ mach_msg_id_t MapNotificationToReplyType(mach_msg_id_t eNotificationType);
+
+ // The following methods initialize fields on the message prior to transmission. Each is valid for either
+ // notification, replies or both. If a particular setter is defined for replies, say, then it will be a
+ // no-op for any replies which don't contain that field. This makes transforming between notifications and
+ // replies of different types simpler (we can copy a super-set of all fields between the two, but only
+ // those operations that make sense will do any work).
+
+ // Defined for notifications:
+ void SetThread(thread_act_t thread);
+ void SetException(exception_type_t eException);
+ void SetExceptionCodeCount(int cCodes);
+ void SetExceptionCode(int iIndex, MACH_EH_TYPE(exception_data_type_t) iCode);
+
+ // Defined for replies:
+ void SetReturnCode(kern_return_t eReturnCode);
+
+ // Defined for both notifications and replies.
+ void SetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, mach_msg_type_number_t count);
+
+ // Maximally sized buffer for the message to be received into or transmitted out of this class.
+ unsigned char m_rgMessageBuffer[kcbMaxMessageSize];
+
+ // Initialized by ResetMessage() to point to the buffer above. Gives a typed view of the encapsulated Mach
+ // message.
+ mach_message_t *m_pMessage;
+
+ // Cached value of GetThread() or MACH_PORT_NULL if that has not been computed yet.
+ thread_act_t m_hThread;
+
+ // Cached value of the task port or MACH_PORT_NULL if the message doesn't have one.
+ mach_port_t m_hTask;
+
+ // Considered whether we are responsible for the deallocation of the ports in
+ // this message. It is true for messages we receive, and false for messages we send.
+ bool m_fPortsOwned;
+};
+
+#endif // HAVE_MACH_EXCEPTIONS
diff --git a/src/pal/src/exception/seh-unwind.cpp b/src/pal/src/exception/seh-unwind.cpp
new file mode 100644
index 0000000000..24eebbbf94
--- /dev/null
+++ b/src/pal/src/exception/seh-unwind.cpp
@@ -0,0 +1,737 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ seh-unwind.cpp
+
+Abstract:
+
+ Implementation of exception API functions based on
+ the Unwind API.
+
+
+
+--*/
+
+#ifndef FEATURE_PAL_SXS
+#error FEATURE_PAL_SXS needs to be defined for this file.
+#endif // !FEATURE_PAL_SXS
+
+#include "pal/context.h"
+#include "pal.h"
+#include <dlfcn.h>
+
+#if HAVE_LIBUNWIND_H
+#ifndef __linux__
+#define UNW_LOCAL_ONLY
+#endif // !__linux__
+#include <libunwind.h>
+#ifdef __linux__
+#ifdef HAVE_LIBUNWIND_PTRACE
+#include <libunwind-ptrace.h>
+#endif // HAVE_LIBUNWIND_PTRACE
+#endif // __linux__
+#endif // HAVE_LIBUNWIND_H
+
+
+//----------------------------------------------------------------------
+// Virtual Unwinding
+//----------------------------------------------------------------------
+
+#if HAVE_LIBUNWIND_H
+#if UNWIND_CONTEXT_IS_UCONTEXT_T
+
+#if defined(_AMD64_)
+#define ASSIGN_UNWIND_REGS \
+ ASSIGN_REG(Rip) \
+ ASSIGN_REG(Rsp) \
+ ASSIGN_REG(Rbp) \
+ ASSIGN_REG(Rbx) \
+ ASSIGN_REG(R12) \
+ ASSIGN_REG(R13) \
+ ASSIGN_REG(R14) \
+ ASSIGN_REG(R15)
+#elif defined(_ARM64_)
+#define ASSIGN_UNWIND_REGS \
+ ASSIGN_REG(Pc) \
+ ASSIGN_REG(Sp) \
+ ASSIGN_REG(Fp) \
+ ASSIGN_REG(Lr) \
+ ASSIGN_REG(X19) \
+ ASSIGN_REG(X20) \
+ ASSIGN_REG(X21) \
+ ASSIGN_REG(X22) \
+ ASSIGN_REG(X23) \
+ ASSIGN_REG(X24) \
+ ASSIGN_REG(X25) \
+ ASSIGN_REG(X26) \
+ ASSIGN_REG(X27) \
+ ASSIGN_REG(X28)
+#else
+#error unsupported architecture
+#endif
+
+static void WinContextToUnwindContext(CONTEXT *winContext, unw_context_t *unwContext)
+{
+#define ASSIGN_REG(reg) MCREG_##reg(unwContext->uc_mcontext) = winContext->reg;
+ ASSIGN_UNWIND_REGS
+#undef ASSIGN_REG
+}
+#else
+static void WinContextToUnwindContext(CONTEXT *winContext, unw_context_t *unwContext)
+{
+#if defined(_ARM_)
+ // Assuming that unw_set_reg() on cursor will point the cursor to the
+ // supposed stack frame is dangerous for libunwind-arm in Linux.
+ // It is because libunwind's unw_cursor_t has other data structure
+ // initialized by unw_init_local(), which are not updated by
+ // unw_set_reg().
+ unwContext->regs[0] = 0;
+ unwContext->regs[1] = 0;
+ unwContext->regs[2] = 0;
+ unwContext->regs[3] = 0;
+ unwContext->regs[4] = winContext->R4;
+ unwContext->regs[5] = winContext->R5;
+ unwContext->regs[6] = winContext->R6;
+ unwContext->regs[7] = winContext->R7;
+ unwContext->regs[8] = winContext->R8;
+ unwContext->regs[9] = winContext->R9;
+ unwContext->regs[10] = winContext->R10;
+ unwContext->regs[11] = winContext->R11;
+ unwContext->regs[12] = 0;
+ unwContext->regs[13] = winContext->Sp;
+ unwContext->regs[14] = winContext->Lr;
+ unwContext->regs[15] = winContext->Pc;
+#endif
+}
+
+static void WinContextToUnwindCursor(CONTEXT *winContext, unw_cursor_t *cursor)
+{
+#if defined(_AMD64_)
+ unw_set_reg(cursor, UNW_REG_IP, winContext->Rip);
+ unw_set_reg(cursor, UNW_REG_SP, winContext->Rsp);
+ unw_set_reg(cursor, UNW_X86_64_RBP, winContext->Rbp);
+ unw_set_reg(cursor, UNW_X86_64_RBX, winContext->Rbx);
+ unw_set_reg(cursor, UNW_X86_64_R12, winContext->R12);
+ unw_set_reg(cursor, UNW_X86_64_R13, winContext->R13);
+ unw_set_reg(cursor, UNW_X86_64_R14, winContext->R14);
+ unw_set_reg(cursor, UNW_X86_64_R15, winContext->R15);
+#endif
+}
+#endif
+
+static void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext)
+{
+#if defined(_AMD64_)
+ unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Rip);
+ unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Rsp);
+ unw_get_reg(cursor, UNW_X86_64_RBP, (unw_word_t *) &winContext->Rbp);
+ unw_get_reg(cursor, UNW_X86_64_RBX, (unw_word_t *) &winContext->Rbx);
+ unw_get_reg(cursor, UNW_X86_64_R12, (unw_word_t *) &winContext->R12);
+ unw_get_reg(cursor, UNW_X86_64_R13, (unw_word_t *) &winContext->R13);
+ unw_get_reg(cursor, UNW_X86_64_R14, (unw_word_t *) &winContext->R14);
+ unw_get_reg(cursor, UNW_X86_64_R15, (unw_word_t *) &winContext->R15);
+#elif defined(_ARM_)
+ unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Sp);
+ unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Pc);
+ unw_get_reg(cursor, UNW_ARM_R14, (unw_word_t *) &winContext->Lr);
+ unw_get_reg(cursor, UNW_ARM_R4, (unw_word_t *) &winContext->R4);
+ unw_get_reg(cursor, UNW_ARM_R5, (unw_word_t *) &winContext->R5);
+ unw_get_reg(cursor, UNW_ARM_R6, (unw_word_t *) &winContext->R6);
+ unw_get_reg(cursor, UNW_ARM_R7, (unw_word_t *) &winContext->R7);
+ unw_get_reg(cursor, UNW_ARM_R8, (unw_word_t *) &winContext->R8);
+ unw_get_reg(cursor, UNW_ARM_R9, (unw_word_t *) &winContext->R9);
+ unw_get_reg(cursor, UNW_ARM_R10, (unw_word_t *) &winContext->R10);
+ unw_get_reg(cursor, UNW_ARM_R11, (unw_word_t *) &winContext->R11);
+#elif defined(_ARM64_)
+ unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Pc);
+ unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Sp);
+ unw_get_reg(cursor, UNW_AARCH64_X29, (unw_word_t *) &winContext->Fp);
+ unw_get_reg(cursor, UNW_AARCH64_X30, (unw_word_t *) &winContext->Lr);
+ unw_get_reg(cursor, UNW_AARCH64_X19, (unw_word_t *) &winContext->X19);
+ unw_get_reg(cursor, UNW_AARCH64_X20, (unw_word_t *) &winContext->X20);
+ unw_get_reg(cursor, UNW_AARCH64_X21, (unw_word_t *) &winContext->X21);
+ unw_get_reg(cursor, UNW_AARCH64_X22, (unw_word_t *) &winContext->X22);
+ unw_get_reg(cursor, UNW_AARCH64_X23, (unw_word_t *) &winContext->X23);
+ unw_get_reg(cursor, UNW_AARCH64_X24, (unw_word_t *) &winContext->X24);
+ unw_get_reg(cursor, UNW_AARCH64_X25, (unw_word_t *) &winContext->X25);
+ unw_get_reg(cursor, UNW_AARCH64_X26, (unw_word_t *) &winContext->X26);
+ unw_get_reg(cursor, UNW_AARCH64_X27, (unw_word_t *) &winContext->X27);
+ unw_get_reg(cursor, UNW_AARCH64_X28, (unw_word_t *) &winContext->X28);
+#else
+#error unsupported architecture
+#endif
+}
+
+static void GetContextPointer(unw_cursor_t *cursor, unw_context_t *unwContext, int reg, SIZE_T **contextPointer)
+{
+#if defined(HAVE_UNW_GET_SAVE_LOC)
+ unw_save_loc_t saveLoc;
+ unw_get_save_loc(cursor, reg, &saveLoc);
+ if (saveLoc.type == UNW_SLT_MEMORY)
+ {
+ SIZE_T *pLoc = (SIZE_T *)saveLoc.u.addr;
+ // Filter out fake save locations that point to unwContext
+ if (unwContext == NULL || (pLoc < (SIZE_T *)unwContext) || ((SIZE_T *)(unwContext + 1) <= pLoc))
+ *contextPointer = (SIZE_T *)saveLoc.u.addr;
+ }
+#else
+ // Returning NULL indicates that we don't have context pointers available
+ *contextPointer = NULL;
+#endif
+}
+
+static void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
+{
+#if defined(_AMD64_)
+ GetContextPointer(cursor, unwContext, UNW_X86_64_RBP, &contextPointers->Rbp);
+ GetContextPointer(cursor, unwContext, UNW_X86_64_RBX, &contextPointers->Rbx);
+ GetContextPointer(cursor, unwContext, UNW_X86_64_R12, &contextPointers->R12);
+ GetContextPointer(cursor, unwContext, UNW_X86_64_R13, &contextPointers->R13);
+ GetContextPointer(cursor, unwContext, UNW_X86_64_R14, &contextPointers->R14);
+ GetContextPointer(cursor, unwContext, UNW_X86_64_R15, &contextPointers->R15);
+#elif defined(_ARM_)
+ GetContextPointer(cursor, unwContext, UNW_ARM_R4, &contextPointers->R4);
+ GetContextPointer(cursor, unwContext, UNW_ARM_R5, &contextPointers->R5);
+ GetContextPointer(cursor, unwContext, UNW_ARM_R6, &contextPointers->R6);
+ GetContextPointer(cursor, unwContext, UNW_ARM_R7, &contextPointers->R7);
+ GetContextPointer(cursor, unwContext, UNW_ARM_R8, &contextPointers->R8);
+ GetContextPointer(cursor, unwContext, UNW_ARM_R9, &contextPointers->R9);
+ GetContextPointer(cursor, unwContext, UNW_ARM_R10, &contextPointers->R10);
+ GetContextPointer(cursor, unwContext, UNW_ARM_R11, &contextPointers->R11);
+#elif defined(_ARM64_)
+ GetContextPointer(cursor, unwContext, UNW_AARCH64_X19, &contextPointers->X19);
+ GetContextPointer(cursor, unwContext, UNW_AARCH64_X20, &contextPointers->X20);
+ GetContextPointer(cursor, unwContext, UNW_AARCH64_X21, &contextPointers->X21);
+ GetContextPointer(cursor, unwContext, UNW_AARCH64_X22, &contextPointers->X22);
+ GetContextPointer(cursor, unwContext, UNW_AARCH64_X23, &contextPointers->X23);
+ GetContextPointer(cursor, unwContext, UNW_AARCH64_X24, &contextPointers->X24);
+ GetContextPointer(cursor, unwContext, UNW_AARCH64_X25, &contextPointers->X25);
+ GetContextPointer(cursor, unwContext, UNW_AARCH64_X26, &contextPointers->X26);
+ GetContextPointer(cursor, unwContext, UNW_AARCH64_X27, &contextPointers->X27);
+ GetContextPointer(cursor, unwContext, UNW_AARCH64_X28, &contextPointers->X28);
+#else
+#error unsupported architecture
+#endif
+}
+
+BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
+{
+ int st;
+ unw_context_t unwContext;
+ unw_cursor_t cursor;
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(_ARM64_) || defined(_ARM_)
+ DWORD64 curPc;
+#endif
+
+ if ((context->ContextFlags & CONTEXT_EXCEPTION_ACTIVE) != 0)
+ {
+ // The current frame is a source of hardware exception. Due to the fact that
+ // we use the low level unwinder to unwind just one frame a time, the
+ // unwinder doesn't have the signal_frame flag set. So it doesn't
+ // know that it should not decrement the PC before looking up the unwind info.
+ // So we compensate it by incrementing the PC before passing it to the unwinder.
+ // Without it, the unwinder would not find unwind info if the hardware exception
+ // happened in the first instruction of a function.
+ CONTEXTSetPC(context, CONTEXTGetPC(context) + 1);
+ }
+
+#if !UNWIND_CONTEXT_IS_UCONTEXT_T
+ st = unw_getcontext(&unwContext);
+ if (st < 0)
+ {
+ return FALSE;
+ }
+#endif
+
+ WinContextToUnwindContext(context, &unwContext);
+
+ st = unw_init_local(&cursor, &unwContext);
+ if (st < 0)
+ {
+ return FALSE;
+ }
+
+#if !UNWIND_CONTEXT_IS_UCONTEXT_T
+ // Set the unwind context to the specified windows context
+ WinContextToUnwindCursor(context, &cursor);
+#endif
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(_ARM64_) || defined(_ARM_)
+ // FreeBSD, NetBSD and OSX appear to do two different things when unwinding
+ // 1: If it reaches where it cannot unwind anymore, say a
+ // managed frame. It wil return 0, but also update the $pc
+ // 2: If it unwinds all the way to _start it will return
+ // 0 from the step, but $pc will stay the same.
+ // The behaviour of libunwind from nongnu.org is to null the PC
+ // So we bank the original PC here, so we can compare it after
+ // the step
+ curPc = CONTEXTGetPC(context);
+#endif
+
+ st = unw_step(&cursor);
+ if (st < 0)
+ {
+ return FALSE;
+ }
+
+ // Check if the frame we have unwound to is a frame that caused
+ // synchronous signal, like a hardware exception and record it
+ // in the context flags.
+ if (unw_is_signal_frame(&cursor) > 0)
+ {
+ context->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
+#if defined(_ARM_) || defined(_ARM64_)
+ context->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL;
+#endif // _ARM_ || _ARM64_
+ }
+ else
+ {
+ context->ContextFlags &= ~CONTEXT_EXCEPTION_ACTIVE;
+#if defined(_ARM_) || defined(_ARM64_)
+ context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
+#endif // _ARM_ || _ARM64_
+ }
+
+ // Update the passed in windows context to reflect the unwind
+ //
+ UnwindContextToWinContext(&cursor, context);
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(_ARM64_) || defined(_ARM_)
+ if (st == 0 && CONTEXTGetPC(context) == curPc)
+ {
+ CONTEXTSetPC(context, 0);
+ }
+#endif
+
+ if (contextPointers != NULL)
+ {
+ GetContextPointers(&cursor, &unwContext, contextPointers);
+ }
+ return TRUE;
+}
+
+#else
+#error don't know how to unwind on this platform
+#endif
+
+// These methods are only used on the AMD64 build
+#ifdef _AMD64_
+#ifdef HAVE_UNW_GET_ACCESSORS
+
+static struct LibunwindCallbacksInfoType
+{
+ CONTEXT *Context;
+ ReadMemoryWordCallback readMemCallback;
+} LibunwindCallbacksInfo;
+
+static int get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t *dilap, void *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg)
+{
+ if (write)
+ {
+ ASSERT("Memory write must never be called by libunwind during stackwalk");
+ return -UNW_EINVAL;
+ }
+
+ // access_mem sometimes gets called by _UPT_find_proc_info, in such cases arg has a pointer to libunwind internal data
+ // returned by _UPT_create. It makes it impossible to use arg for passing readMemCallback. That's why we have to use global variable.
+ if (LibunwindCallbacksInfo.readMemCallback((SIZE_T)addr, (SIZE_T *)valp))
+ {
+ return UNW_ESUCCESS;
+ }
+ else
+ {
+ return -UNW_EUNSPEC;
+ }
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg)
+{
+ if (write)
+ {
+ ASSERT("Register write must never be called by libunwind during stackwalk");
+ return -UNW_EREADONLYREG;
+ }
+
+ CONTEXT *winContext = LibunwindCallbacksInfo.Context;
+
+ switch (regnum)
+ {
+#if defined(_AMD64_)
+ case UNW_REG_IP: *valp = (unw_word_t) winContext->Rip; break;
+ case UNW_REG_SP: *valp = (unw_word_t) winContext->Rsp; break;
+ case UNW_X86_64_RBP: *valp = (unw_word_t) winContext->Rbp; break;
+ case UNW_X86_64_RBX: *valp = (unw_word_t) winContext->Rbx; break;
+ case UNW_X86_64_R12: *valp = (unw_word_t) winContext->R12; break;
+ case UNW_X86_64_R13: *valp = (unw_word_t) winContext->R13; break;
+ case UNW_X86_64_R14: *valp = (unw_word_t) winContext->R14; break;
+ case UNW_X86_64_R15: *valp = (unw_word_t) winContext->R15; break;
+#elif defined(_ARM_)
+ case UNW_ARM_R13: *valp = (unw_word_t) winContext->Sp; break;
+ case UNW_ARM_R14: *valp = (unw_word_t) winContext->Lr; break;
+ case UNW_ARM_R15: *valp = (unw_word_t) winContext->Pc; break;
+ case UNW_ARM_R4: *valp = (unw_word_t) winContext->R4; break;
+ case UNW_ARM_R5: *valp = (unw_word_t) winContext->R5; break;
+ case UNW_ARM_R6: *valp = (unw_word_t) winContext->R6; break;
+ case UNW_ARM_R7: *valp = (unw_word_t) winContext->R7; break;
+ case UNW_ARM_R8: *valp = (unw_word_t) winContext->R8; break;
+ case UNW_ARM_R9: *valp = (unw_word_t) winContext->R9; break;
+ case UNW_ARM_R10: *valp = (unw_word_t) winContext->R10; break;
+ case UNW_ARM_R11: *valp = (unw_word_t) winContext->R11; break;
+#elif defined(_ARM64_)
+ case UNW_REG_IP: *valp = (unw_word_t) winContext->Pc; break;
+ case UNW_REG_SP: *valp = (unw_word_t) winContext->Sp; break;
+ case UNW_AARCH64_X29: *valp = (unw_word_t) winContext->Fp; break;
+ case UNW_AARCH64_X30: *valp = (unw_word_t) winContext->Lr; break;
+ case UNW_AARCH64_X19: *valp = (unw_word_t) winContext->X19; break;
+ case UNW_AARCH64_X20: *valp = (unw_word_t) winContext->X20; break;
+ case UNW_AARCH64_X21: *valp = (unw_word_t) winContext->X21; break;
+ case UNW_AARCH64_X22: *valp = (unw_word_t) winContext->X22; break;
+ case UNW_AARCH64_X23: *valp = (unw_word_t) winContext->X23; break;
+ case UNW_AARCH64_X24: *valp = (unw_word_t) winContext->X24; break;
+ case UNW_AARCH64_X25: *valp = (unw_word_t) winContext->X25; break;
+ case UNW_AARCH64_X26: *valp = (unw_word_t) winContext->X26; break;
+ case UNW_AARCH64_X27: *valp = (unw_word_t) winContext->X27; break;
+ case UNW_AARCH64_X28: *valp = (unw_word_t) winContext->X28; break;
+#else
+#error unsupported architecture
+#endif
+ default:
+ ASSERT("Attempt to read an unknown register.");
+ return -UNW_EBADREG;
+ }
+ return UNW_ESUCCESS;
+}
+
+static int access_fpreg(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *fpvalp, int write, void *arg)
+{
+ ASSERT("Not supposed to be ever called");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t as, unw_cursor_t *cp, void *arg)
+{
+ ASSERT("Not supposed to be ever called");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg)
+{
+ ASSERT("Not supposed to be ever called");
+ return -UNW_EINVAL;
+}
+
+int find_proc_info(unw_addr_space_t as,
+ unw_word_t ip, unw_proc_info_t *pip,
+ int need_unwind_info, void *arg)
+{
+#ifdef HAVE_LIBUNWIND_PTRACE
+ // UNIXTODO: libunwind RPM package on Fedora/CentOS/RedHat doesn't have libunwind-ptrace.so
+ // and we can't use it from a shared library like libmscordaccore.so.
+ // That's why all calls to ptrace parts of libunwind ifdeffed out for now.
+ return _UPT_find_proc_info(as, ip, pip, need_unwind_info, arg);
+#else
+ return -UNW_EINVAL;
+#endif
+}
+
+void put_unwind_info(unw_addr_space_t as, unw_proc_info_t *pip, void *arg)
+{
+#ifdef HAVE_LIBUNWIND_PTRACE
+ return _UPT_put_unwind_info(as, pip, arg);
+#endif
+}
+
+static unw_accessors_t unwind_accessors =
+{
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name
+};
+
+BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context,
+ KNONVOLATILE_CONTEXT_POINTERS *contextPointers,
+ DWORD pid,
+ ReadMemoryWordCallback readMemCallback)
+{
+ // This function can be executed only by one thread at a time.
+ // The reason for this is that we need to pass context and read mem function to libunwind callbacks
+ // but "arg" is already used by the pointer returned from _UPT_create().
+ // So we resort to using global variables and a lock.
+ struct Lock
+ {
+ CRITICAL_SECTION cs;
+ Lock()
+ {
+ // ctor of a static variable is a thread-safe way to initialize critical section exactly once (clang,gcc)
+ InitializeCriticalSection(&cs);
+ }
+ };
+ struct LockHolder
+ {
+ CRITICAL_SECTION *cs;
+ LockHolder(CRITICAL_SECTION *cs)
+ {
+ this->cs = cs;
+ EnterCriticalSection(cs);
+ }
+
+ ~LockHolder()
+ {
+ LeaveCriticalSection(cs);
+ cs = NULL;
+ }
+ };
+ static Lock lock;
+ LockHolder lockHolder(&lock.cs);
+
+ int st;
+ unw_cursor_t cursor;
+ unw_addr_space_t addrSpace = 0;
+ void *libunwindUptPtr = NULL;
+ BOOL result = FALSE;
+
+ LibunwindCallbacksInfo.Context = context;
+ LibunwindCallbacksInfo.readMemCallback = readMemCallback;
+
+ addrSpace = unw_create_addr_space(&unwind_accessors, 0);
+#ifdef HAVE_LIBUNWIND_PTRACE
+ libunwindUptPtr = _UPT_create(pid);
+#endif
+ st = unw_init_remote(&cursor, addrSpace, libunwindUptPtr);
+ if (st < 0)
+ {
+ result = FALSE;
+ goto Exit;
+ }
+
+ st = unw_step(&cursor);
+ if (st < 0)
+ {
+ result = FALSE;
+ goto Exit;
+ }
+
+ UnwindContextToWinContext(&cursor, context);
+
+ if (contextPointers != NULL)
+ {
+ GetContextPointers(&cursor, NULL, contextPointers);
+ }
+ result = TRUE;
+
+Exit:
+#ifdef HAVE_LIBUNWIND_PTRACE
+ if (libunwindUptPtr != NULL)
+ {
+ _UPT_destroy(libunwindUptPtr);
+ }
+#endif
+ if (addrSpace != 0)
+ {
+ unw_destroy_addr_space(addrSpace);
+ }
+ return result;
+}
+#else // HAVE_UNW_GET_ACCESSORS
+
+BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context,
+ KNONVOLATILE_CONTEXT_POINTERS *contextPointers,
+ DWORD pid,
+ ReadMemoryWordCallback readMemCallback)
+{
+ //UNIXTODO: Implement for Mac flavor of libunwind
+ return FALSE;
+}
+
+#endif // !HAVE_UNW_GET_ACCESSORS
+#endif // _AMD64_
+
+struct ExceptionRecords
+{
+ CONTEXT ContextRecord;
+ EXCEPTION_RECORD ExceptionRecord;
+};
+
+// Max number of fallback contexts that are used when malloc fails to allocate ExceptionRecords structure
+static const int MaxFallbackContexts = sizeof(size_t) * 8;
+// Array of fallback contexts
+static ExceptionRecords s_fallbackContexts[MaxFallbackContexts];
+// Bitmap used for allocating fallback contexts - bits set to 1 represent already allocated context.
+static volatile size_t s_allocatedContextsBitmap = 0;
+
+/*++
+Function:
+ AllocateExceptionRecords
+
+ Allocate EXCEPTION_RECORD and CONTEXT structures for an exception.
+Parameters:
+ exceptionRecord - output pointer to the allocated exception record
+ contextRecord - output pointer to the allocated context record
+--*/
+VOID
+AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord)
+{
+ ExceptionRecords* records;
+ if (posix_memalign((void**)&records, alignof(ExceptionRecords), sizeof(ExceptionRecords)) != 0)
+ {
+ size_t bitmap;
+ size_t newBitmap;
+ int index;
+
+ do
+ {
+ bitmap = s_allocatedContextsBitmap;
+ index = __builtin_ffsl(~bitmap) - 1;
+ if (index < 0)
+ {
+ PROCAbort();
+ }
+
+ newBitmap = bitmap | ((size_t)1 << index);
+ }
+ while (__sync_val_compare_and_swap(&s_allocatedContextsBitmap, bitmap, newBitmap) != bitmap);
+
+ records = &s_fallbackContexts[index];
+ }
+
+ *contextRecord = &records->ContextRecord;
+ *exceptionRecord = &records->ExceptionRecord;
+}
+
+/*++
+Function:
+ PAL_FreeExceptionRecords
+
+ Free EXCEPTION_RECORD and CONTEXT structures of an exception that were allocated by the
+ AllocateExceptionRecords.
+Parameters:
+ exceptionRecord - exception record
+ contextRecord - context record
+--*/
+VOID
+PALAPI
+PAL_FreeExceptionRecords(IN EXCEPTION_RECORD *exceptionRecord, IN CONTEXT *contextRecord)
+{
+ // Both records are allocated at once and the allocated memory starts at the contextRecord
+ ExceptionRecords* records = (ExceptionRecords*)contextRecord;
+ if ((records >= &s_fallbackContexts[0]) && (records < &s_fallbackContexts[MaxFallbackContexts]))
+ {
+ int index = records - &s_fallbackContexts[0];
+ __sync_fetch_and_and(&s_allocatedContextsBitmap, ~((size_t)1 << index));
+ }
+ else
+ {
+ free(contextRecord);
+ }
+}
+
+/*++
+Function:
+ RtlpRaiseException
+
+Parameters:
+ ExceptionRecord - the Windows exception record to throw
+
+Note:
+ The name of this function and the name of the ExceptionRecord
+ parameter is used in the sos lldb plugin code to read the exception
+ record. See coreclr\src\ToolBox\SOS\lldbplugin\services.cpp.
+
+ This function must not be inlined or optimized so the below PAL_VirtualUnwind
+ calls end up with RaiseException caller's context and so the above debugger
+ code finds the function and ExceptionRecord parameter.
+--*/
+PAL_NORETURN
+__attribute__((noinline))
+__attribute__((optnone))
+static void
+RtlpRaiseException(EXCEPTION_RECORD *ExceptionRecord, CONTEXT *ContextRecord)
+{
+ throw PAL_SEHException(ExceptionRecord, ContextRecord);
+}
+
+/*++
+Function:
+ RaiseException
+
+See MSDN doc.
+--*/
+// no PAL_NORETURN, as callers must assume this can return for continuable exceptions.
+__attribute__((noinline))
+VOID
+PALAPI
+RaiseException(IN DWORD dwExceptionCode,
+ IN DWORD dwExceptionFlags,
+ IN DWORD nNumberOfArguments,
+ IN CONST ULONG_PTR *lpArguments)
+{
+ // PERF_ENTRY_ONLY is used here because RaiseException may or may not
+ // return. We can not get latency data without PERF_EXIT. For this reason,
+ // PERF_ENTRY_ONLY is used to profile frequency only.
+ PERF_ENTRY_ONLY(RaiseException);
+ ENTRY("RaiseException(dwCode=%#x, dwFlags=%#x, nArgs=%u, lpArguments=%p)\n",
+ dwExceptionCode, dwExceptionFlags, nNumberOfArguments, lpArguments);
+
+ /* Validate parameters */
+ if (dwExceptionCode & RESERVED_SEH_BIT)
+ {
+ WARN("Exception code %08x has bit 28 set; clearing it.\n", dwExceptionCode);
+ dwExceptionCode ^= RESERVED_SEH_BIT;
+ }
+
+ if (nNumberOfArguments > EXCEPTION_MAXIMUM_PARAMETERS)
+ {
+ WARN("Number of arguments (%d) exceeds the limit "
+ "EXCEPTION_MAXIMUM_PARAMETERS (%d); ignoring extra parameters.\n",
+ nNumberOfArguments, EXCEPTION_MAXIMUM_PARAMETERS);
+ nNumberOfArguments = EXCEPTION_MAXIMUM_PARAMETERS;
+ }
+
+ CONTEXT *contextRecord;
+ EXCEPTION_RECORD *exceptionRecord;
+ AllocateExceptionRecords(&exceptionRecord, &contextRecord);
+
+ ZeroMemory(exceptionRecord, sizeof(EXCEPTION_RECORD));
+
+ exceptionRecord->ExceptionCode = dwExceptionCode;
+ exceptionRecord->ExceptionFlags = dwExceptionFlags;
+ exceptionRecord->ExceptionRecord = NULL;
+ exceptionRecord->ExceptionAddress = NULL; // will be set by RtlpRaiseException
+ exceptionRecord->NumberParameters = nNumberOfArguments;
+ if (nNumberOfArguments)
+ {
+ CopyMemory(exceptionRecord->ExceptionInformation, lpArguments,
+ nNumberOfArguments * sizeof(ULONG_PTR));
+ }
+
+ // Capture the context of RaiseException.
+ ZeroMemory(contextRecord, sizeof(CONTEXT));
+ contextRecord->ContextFlags = CONTEXT_FULL;
+ CONTEXT_CaptureContext(contextRecord);
+
+ // We have to unwind one level to get the actual context user code could be resumed at.
+ PAL_VirtualUnwind(contextRecord, NULL);
+
+ exceptionRecord->ExceptionAddress = (void *)CONTEXTGetPC(contextRecord);
+
+ RtlpRaiseException(exceptionRecord, contextRecord);
+
+ LOGEXIT("RaiseException returns\n");
+}
diff --git a/src/pal/src/exception/seh.cpp b/src/pal/src/exception/seh.cpp
new file mode 100644
index 0000000000..38779bf59b
--- /dev/null
+++ b/src/pal/src/exception/seh.cpp
@@ -0,0 +1,431 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ seh.cpp
+
+Abstract:
+
+ Implementation of exception API functions.
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/handleapi.hpp"
+#include "pal/seh.hpp"
+#include "pal/dbgmsg.h"
+#include "pal/critsect.h"
+#include "pal/debug.h"
+#include "pal/init.h"
+#include "pal/process.h"
+#include "pal/malloc.hpp"
+#include "signal.hpp"
+
+#if HAVE_MACH_EXCEPTIONS
+#include "machexception.h"
+#else
+#include <signal.h>
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+// Define the std::move so that we don't have to include the <utility> header
+// which on some platforms pulls in STL stuff that collides with PAL stuff.
+// The std::move is needed to enable using move constructor and assignment operator
+// for PAL_SEHException.
+namespace std
+{
+ template<typename T>
+ struct remove_reference
+ {
+ typedef T type;
+ };
+
+ template<typename T>
+ struct remove_reference<T&>
+ {
+ typedef T type;
+ };
+
+ template<typename T>
+ struct remove_reference<T&&>
+ {
+ typedef T type;
+ };
+
+ template<class T> inline
+ typename remove_reference<T>::type&& move(T&& arg)
+ { // forward arg as movable
+ return ((typename remove_reference<T>::type&&)arg);
+ }
+}
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(EXCEPT);
+
+/* Constant and type definitions **********************************************/
+
+/* Bit 28 of exception codes is reserved. */
+const UINT RESERVED_SEH_BIT = 0x800000;
+
+/* Internal variables definitions **********************************************/
+
+PHARDWARE_EXCEPTION_HANDLER g_hardwareExceptionHandler = NULL;
+// Function to check if an activation can be safely injected at a specified context
+PHARDWARE_EXCEPTION_SAFETY_CHECK_FUNCTION g_safeExceptionCheckFunction = NULL;
+
+PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode = NULL;
+
+/* Internal function definitions **********************************************/
+
+/*++
+Function :
+ SEHInitialize
+
+ Initialize all SEH-related stuff (signals, etc)
+
+Parameters :
+ CPalThread * pthrCurrent : reference to the current thread.
+ PAL initialize flags
+
+Return value :
+ TRUE if SEH support initialization succeeded
+ FALSE otherwise
+--*/
+BOOL
+SEHInitialize (CPalThread *pthrCurrent, DWORD flags)
+{
+#if !HAVE_MACH_EXCEPTIONS
+ if (!SEHInitializeSignals(flags))
+ {
+ ERROR("SEHInitializeSignals failed!\n");
+ SEHCleanup();
+ return FALSE;
+ }
+#endif
+
+ return TRUE;
+}
+
+/*++
+Function :
+ SEHCleanup
+
+ Undo work done by SEHInitialize
+
+Parameters :
+ None
+
+ (no return value)
+
+--*/
+VOID
+SEHCleanup()
+{
+ TRACE("Cleaning up SEH\n");
+
+#if HAVE_MACH_EXCEPTIONS
+ SEHCleanupExceptionPort();
+#else
+ SEHCleanupSignals();
+#endif
+}
+
+/*++
+Function:
+ PAL_SetHardwareExceptionHandler
+
+ Register a hardware exception handler.
+
+Parameters:
+ handler - exception handler
+
+Return value:
+ None
+--*/
+VOID
+PALAPI
+PAL_SetHardwareExceptionHandler(
+ IN PHARDWARE_EXCEPTION_HANDLER exceptionHandler,
+ IN PHARDWARE_EXCEPTION_SAFETY_CHECK_FUNCTION exceptionCheckFunction)
+{
+ g_hardwareExceptionHandler = exceptionHandler;
+ g_safeExceptionCheckFunction = exceptionCheckFunction;
+}
+
+/*++
+Function:
+ PAL_SetGetGcMarkerExceptionCode
+
+ Register a function that determines if the specified IP has code that is a GC marker for GCCover.
+
+Parameters:
+ getGcMarkerExceptionCode - the function to register
+
+Return value:
+ None
+--*/
+VOID
+PALAPI
+PAL_SetGetGcMarkerExceptionCode(
+ IN PGET_GCMARKER_EXCEPTION_CODE getGcMarkerExceptionCode)
+{
+ g_getGcMarkerExceptionCode = getGcMarkerExceptionCode;
+}
+
+EXTERN_C void ThrowExceptionFromContextInternal(CONTEXT* context, PAL_SEHException* ex);
+
+/*++
+Function:
+ PAL_ThrowExceptionFromContext
+
+ This function creates a stack frame right below the target frame, restores all callee
+ saved registers from the passed in context, sets the RSP to that frame and sets the
+ return address to the target frame's RIP.
+ Then it uses the ThrowExceptionHelper to throw the passed in exception from that context.
+
+Parameters:
+ CONTEXT* context - context from which the exception will be thrown
+ PAL_SEHException* ex - the exception to throw.
+--*/
+VOID
+PALAPI
+PAL_ThrowExceptionFromContext(CONTEXT* context, PAL_SEHException* ex)
+{
+ // We need to make a copy of the exception off stack, since the "ex" is located in one of the stack
+ // frames that will become obsolete by the ThrowExceptionFromContextInternal and the ThrowExceptionHelper
+ // could overwrite the "ex" object by stack e.g. when allocating the low level exception object for "throw".
+ static __thread BYTE threadLocalExceptionStorage[sizeof(PAL_SEHException)];
+ ThrowExceptionFromContextInternal(context, new (threadLocalExceptionStorage) PAL_SEHException(std::move(*ex)));
+}
+
+/*++
+Function:
+ ThrowExceptionHelper
+
+ Helper function to throw the passed in exception.
+ It is called from the assembler function ThrowExceptionFromContextInternal
+
+Parameters:
+ PAL_SEHException* ex - the exception to throw.
+--*/
+extern "C"
+void ThrowExceptionHelper(PAL_SEHException* ex)
+{
+ throw std::move(*ex);
+}
+
+/*++
+Function:
+ SEHProcessException
+
+ Send the PAL exception to any handler registered.
+
+Parameters:
+ PAL_SEHException* exception
+
+Return value:
+ Returns TRUE if the exception happened in managed code and the execution should
+ continue (with possibly modified context).
+ Returns FALSE if the exception happened in managed code and it was not handled.
+ In case the exception was handled by calling a catch handler, it doesn't return at all.
+--*/
+BOOL
+SEHProcessException(PAL_SEHException* exception)
+{
+ CONTEXT* contextRecord = exception->GetContextRecord();
+ EXCEPTION_RECORD* exceptionRecord = exception->GetExceptionRecord();
+
+ if (!IsInDebugBreak(exceptionRecord->ExceptionAddress))
+ {
+ if (g_hardwareExceptionHandler != NULL)
+ {
+ _ASSERTE(g_safeExceptionCheckFunction != NULL);
+ // Check if it is safe to handle the hardware exception (the exception happened in managed code
+ // or in a jitter helper or it is a debugger breakpoint)
+ if (g_safeExceptionCheckFunction(contextRecord, exceptionRecord))
+ {
+ if (exceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ {
+ // Check if the failed access has hit a stack guard page. In such case, it
+ // was a stack probe that detected that there is not enough stack left.
+ void* stackLimit = CPalThread::GetStackLimit();
+ void* stackGuard = (void*)((size_t)stackLimit - getpagesize());
+ void* violationAddr = (void*)exceptionRecord->ExceptionInformation[1];
+ if ((violationAddr >= stackGuard) && (violationAddr < stackLimit))
+ {
+ // The exception happened in the page right below the stack limit,
+ // so it is a stack overflow
+ write(STDERR_FILENO, StackOverflowMessage, sizeof(StackOverflowMessage) - 1);
+ PROCAbort();
+ }
+ }
+
+ if (g_hardwareExceptionHandler(exception))
+ {
+ // The exception happened in managed code and the execution should continue.
+ return TRUE;
+ }
+
+ // The exception was a single step or a breakpoint and it was not handled by the debugger.
+ }
+ }
+
+ if (CatchHardwareExceptionHolder::IsEnabled())
+ {
+ PAL_ThrowExceptionFromContext(exception->GetContextRecord(), exception);
+ }
+ }
+
+ // Unhandled hardware exception pointers->ExceptionRecord->ExceptionCode at pointers->ExceptionRecord->ExceptionAddress
+ return FALSE;
+}
+
+/*++
+Function :
+ SEHEnable
+
+ Enable SEH-related stuff on this thread
+
+Parameters:
+ CPalThread * pthrCurrent : reference to the current thread.
+
+Return value :
+ TRUE if enabling succeeded
+ FALSE otherwise
+--*/
+extern "C"
+PAL_ERROR SEHEnable(CPalThread *pthrCurrent)
+{
+#if HAVE_MACH_EXCEPTIONS
+ return pthrCurrent->EnableMachExceptions();
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ // TODO: This needs to be implemented. Cannot put an ASSERT here
+ // because it will make other parts of PAL fail.
+ return NO_ERROR;
+#else// HAVE_MACH_EXCEPTIONS
+#error not yet implemented
+#endif // HAVE_MACH_EXCEPTIONS
+}
+
+/*++
+Function :
+ SEHDisable
+
+ Disable SEH-related stuff on this thread
+
+Parameters:
+ CPalThread * pthrCurrent : reference to the current thread.
+
+Return value :
+ TRUE if enabling succeeded
+ FALSE otherwise
+--*/
+extern "C"
+PAL_ERROR SEHDisable(CPalThread *pthrCurrent)
+{
+#if HAVE_MACH_EXCEPTIONS
+ return pthrCurrent->DisableMachExceptions();
+ // TODO: This needs to be implemented. Cannot put an ASSERT here
+ // because it will make other parts of PAL fail.
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ return NO_ERROR;
+#else // HAVE_MACH_EXCEPTIONS
+#error not yet implemented
+#endif // HAVE_MACH_EXCEPTIONS
+}
+
+/*++
+
+ CatchHardwareExceptionHolder implementation
+
+--*/
+
+CatchHardwareExceptionHolder::CatchHardwareExceptionHolder()
+{
+ CPalThread *pThread = InternalGetCurrentThread();
+ ++pThread->m_hardwareExceptionHolderCount;
+}
+
+CatchHardwareExceptionHolder::~CatchHardwareExceptionHolder()
+{
+ CPalThread *pThread = InternalGetCurrentThread();
+ --pThread->m_hardwareExceptionHolderCount;
+}
+
+bool CatchHardwareExceptionHolder::IsEnabled()
+{
+ CPalThread *pThread = InternalGetCurrentThread();
+ return pThread->IsHardwareExceptionsEnabled();
+}
+
+/*++
+
+ NativeExceptionHolderBase implementation
+
+--*/
+
+#ifdef __llvm__
+__thread
+#else // __llvm__
+__declspec(thread)
+#endif // !__llvm__
+static NativeExceptionHolderBase *t_nativeExceptionHolderHead = nullptr;
+
+NativeExceptionHolderBase::NativeExceptionHolderBase()
+{
+ m_head = nullptr;
+ m_next = nullptr;
+}
+
+NativeExceptionHolderBase::~NativeExceptionHolderBase()
+{
+ // Only destroy if Push was called
+ if (m_head != nullptr)
+ {
+ *m_head = m_next;
+ m_head = nullptr;
+ m_next = nullptr;
+ }
+}
+
+void
+NativeExceptionHolderBase::Push()
+{
+ NativeExceptionHolderBase **head = &t_nativeExceptionHolderHead;
+ m_head = head;
+ m_next = *head;
+ *head = this;
+}
+
+NativeExceptionHolderBase *
+NativeExceptionHolderBase::FindNextHolder(NativeExceptionHolderBase *currentHolder, void *stackLowAddress, void *stackHighAddress)
+{
+ NativeExceptionHolderBase *holder = (currentHolder == nullptr) ? t_nativeExceptionHolderHead : currentHolder->m_next;
+
+ while (holder != nullptr)
+ {
+ if (((void *)holder >= stackLowAddress) && ((void *)holder < stackHighAddress))
+ {
+ return holder;
+ }
+ // Get next holder
+ holder = holder->m_next;
+ }
+
+ return nullptr;
+}
+
+#include "seh-unwind.cpp"
diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp
new file mode 100644
index 0000000000..c2c217993a
--- /dev/null
+++ b/src/pal/src/exception/signal.cpp
@@ -0,0 +1,705 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ exception/signal.cpp
+
+Abstract:
+
+ Signal handler implementation (map signals to exceptions)
+
+
+
+--*/
+
+#include "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do this first
+
+#include "pal/corunix.hpp"
+#include "pal/handleapi.hpp"
+#include "pal/thread.hpp"
+#include "pal/threadinfo.hpp"
+#include "pal/threadsusp.hpp"
+#include "pal/seh.hpp"
+
+#include "pal/palinternal.h"
+#if !HAVE_MACH_EXCEPTIONS
+#include "pal/init.h"
+#include "pal/process.h"
+#include "pal/debug.h"
+
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/ucontext.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "pal/context.h"
+
+using namespace CorUnix;
+
+#ifdef SIGRTMIN
+#define INJECT_ACTIVATION_SIGNAL SIGRTMIN
+#endif
+
+#if !defined(INJECT_ACTIVATION_SIGNAL) && defined(FEATURE_HIJACK)
+#error FEATURE_HIJACK requires INJECT_ACTIVATION_SIGNAL to be defined
+#endif
+
+/* local type definitions *****************************************************/
+
+#if !HAVE_SIGINFO_T
+/* This allows us to compile on platforms that don't have siginfo_t.
+ * Exceptions will work poorly on those platforms. */
+#warning Exceptions will work poorly on this platform
+typedef void *siginfo_t;
+#endif /* !HAVE_SIGINFO_T */
+typedef void (*SIGFUNC)(int, siginfo_t *, void *);
+
+/* internal function declarations *********************************************/
+
+static void sigill_handler(int code, siginfo_t *siginfo, void *context);
+static void sigfpe_handler(int code, siginfo_t *siginfo, void *context);
+static void sigsegv_handler(int code, siginfo_t *siginfo, void *context);
+static void sigtrap_handler(int code, siginfo_t *siginfo, void *context);
+static void sigbus_handler(int code, siginfo_t *siginfo, void *context);
+static void sigint_handler(int code, siginfo_t *siginfo, void *context);
+static void sigquit_handler(int code, siginfo_t *siginfo, void *context);
+static void sigterm_handler(int code, siginfo_t *siginfo, void *context);
+
+static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...);
+
+#ifdef INJECT_ACTIVATION_SIGNAL
+static void inject_activation_handler(int code, siginfo_t *siginfo, void *context);
+#endif
+
+static void handle_signal(int signal_id, SIGFUNC sigfunc, struct sigaction *previousAction);
+static void restore_signal(int signal_id, struct sigaction *previousAction);
+
+/* internal data declarations *********************************************/
+
+struct sigaction g_previous_sigill;
+struct sigaction g_previous_sigtrap;
+struct sigaction g_previous_sigfpe;
+struct sigaction g_previous_sigbus;
+struct sigaction g_previous_sigsegv;
+struct sigaction g_previous_sigint;
+struct sigaction g_previous_sigquit;
+struct sigaction g_previous_sigterm;
+
+static bool registered_sigterm_handler = false;
+
+#ifdef INJECT_ACTIVATION_SIGNAL
+struct sigaction g_previous_activation;
+#endif
+
+/* public function definitions ************************************************/
+
+/*++
+Function :
+ SEHInitializeSignals
+
+ Set up signal handlers to catch signals and translate them to exceptions
+
+Parameters :
+ None
+
+Return :
+ TRUE in case of a success, FALSE otherwise
+--*/
+BOOL SEHInitializeSignals(DWORD flags)
+{
+ TRACE("Initializing signal handlers\n");
+
+ /* we call handle_signal for every possible signal, even
+ if we don't provide a signal handler.
+
+ handle_signal will set SA_RESTART flag for specified signal.
+ Therefore, all signals will have SA_RESTART flag set, preventing
+ slow Unix system calls from being interrupted. On systems without
+ siginfo_t, SIGKILL and SIGSTOP can't be restarted, so we don't
+ handle those signals. Both the Darwin and FreeBSD man pages say
+ that SIGKILL and SIGSTOP can't be handled, but FreeBSD allows us
+ to register a handler for them anyway. We don't do that.
+
+ see sigaction man page for more details
+ */
+ handle_signal(SIGILL, sigill_handler, &g_previous_sigill);
+ handle_signal(SIGTRAP, sigtrap_handler, &g_previous_sigtrap);
+ handle_signal(SIGFPE, sigfpe_handler, &g_previous_sigfpe);
+ handle_signal(SIGBUS, sigbus_handler, &g_previous_sigbus);
+ handle_signal(SIGSEGV, sigsegv_handler, &g_previous_sigsegv);
+ handle_signal(SIGINT, sigint_handler, &g_previous_sigint);
+ handle_signal(SIGQUIT, sigquit_handler, &g_previous_sigquit);
+
+ if (flags & PAL_INITIALIZE_REGISTER_SIGTERM_HANDLER)
+ {
+ handle_signal(SIGTERM, sigterm_handler, &g_previous_sigterm);
+ registered_sigterm_handler = true;
+ }
+
+#ifdef INJECT_ACTIVATION_SIGNAL
+ handle_signal(INJECT_ACTIVATION_SIGNAL, inject_activation_handler, &g_previous_activation);
+#endif
+
+ /* The default action for SIGPIPE is process termination.
+ Since SIGPIPE can be signaled when trying to write on a socket for which
+ the connection has been dropped, we need to tell the system we want
+ to ignore this signal.
+
+ Instead of terminating the process, the system call which would had
+ issued a SIGPIPE will, instead, report an error and set errno to EPIPE.
+ */
+ signal(SIGPIPE, SIG_IGN);
+
+ return TRUE;
+}
+
+/*++
+Function :
+ SEHCleanupSignals
+
+ Restore default signal handlers
+
+Parameters :
+ None
+
+ (no return value)
+
+note :
+reason for this function is that during PAL_Terminate, we reach a point where
+SEH isn't possible anymore (handle manager is off, etc). Past that point,
+we can't avoid crashing on a signal.
+--*/
+void SEHCleanupSignals()
+{
+ TRACE("Restoring default signal handlers\n");
+
+ restore_signal(SIGILL, &g_previous_sigill);
+ restore_signal(SIGTRAP, &g_previous_sigtrap);
+ restore_signal(SIGFPE, &g_previous_sigfpe);
+ restore_signal(SIGBUS, &g_previous_sigbus);
+ restore_signal(SIGSEGV, &g_previous_sigsegv);
+ restore_signal(SIGINT, &g_previous_sigint);
+ restore_signal(SIGQUIT, &g_previous_sigquit);
+
+ if (registered_sigterm_handler)
+ {
+ restore_signal(SIGTERM, &g_previous_sigterm);
+ }
+
+#ifdef INJECT_ACTIVATION_SIGNAL
+ restore_signal(INJECT_ACTIVATION_SIGNAL, &g_previous_activation);
+#endif
+}
+
+/* internal function definitions **********************************************/
+
+/*++
+Function :
+ sigill_handler
+
+ handle SIGILL signal (EXCEPTION_ILLEGAL_INSTRUCTION, others?)
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigill_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ if (common_signal_handler(code, siginfo, context, 0))
+ {
+ return;
+ }
+ }
+
+ if (g_previous_sigill.sa_sigaction != NULL)
+ {
+ g_previous_sigill.sa_sigaction(code, siginfo, context);
+ }
+ else
+ {
+ // Restore the original or default handler and restart h/w exception
+ restore_signal(code, &g_previous_sigill);
+ }
+
+ PROCNotifyProcessShutdown();
+}
+
+/*++
+Function :
+ sigfpe_handler
+
+ handle SIGFPE signal (division by zero, floating point exception)
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigfpe_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ if (common_signal_handler(code, siginfo, context, 0))
+ {
+ return;
+ }
+ }
+
+ if (g_previous_sigfpe.sa_sigaction != NULL)
+ {
+ g_previous_sigfpe.sa_sigaction(code, siginfo, context);
+ }
+ else
+ {
+ // Restore the original or default handler and restart h/w exception
+ restore_signal(code, &g_previous_sigfpe);
+ }
+
+ PROCNotifyProcessShutdown();
+}
+
+/*++
+Function :
+ sigsegv_handler
+
+ handle SIGSEGV signal (EXCEPTION_ACCESS_VIOLATION, others)
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigsegv_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ // TODO: First variable parameter says whether a read (0) or write (non-0) caused the
+ // fault. We must disassemble the instruction at record.ExceptionAddress
+ // to correctly fill in this value.
+ if (common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr))
+ {
+ return;
+ }
+ }
+
+ if (g_previous_sigsegv.sa_sigaction != NULL)
+ {
+ g_previous_sigsegv.sa_sigaction(code, siginfo, context);
+ }
+ else
+ {
+ // Restore the original or default handler and restart h/w exception
+ restore_signal(code, &g_previous_sigsegv);
+ }
+
+ PROCNotifyProcessShutdown();
+}
+
+/*++
+Function :
+ sigtrap_handler
+
+ handle SIGTRAP signal (EXCEPTION_SINGLE_STEP, EXCEPTION_BREAKPOINT)
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigtrap_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ if (common_signal_handler(code, siginfo, context, 0))
+ {
+ return;
+ }
+ }
+
+ if (g_previous_sigtrap.sa_sigaction != NULL)
+ {
+ g_previous_sigtrap.sa_sigaction(code, siginfo, context);
+ }
+ else
+ {
+ // We abort instead of restore the original or default handler and returning
+ // because returning from a SIGTRAP handler continues execution past the trap.
+ PROCAbort();
+ }
+
+ PROCNotifyProcessShutdown();
+}
+
+/*++
+Function :
+ sigbus_handler
+
+ handle SIGBUS signal (EXCEPTION_ACCESS_VIOLATION?)
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigbus_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ // TODO: First variable parameter says whether a read (0) or write (non-0) caused the
+ // fault. We must disassemble the instruction at record.ExceptionAddress
+ // to correctly fill in this value.
+ if (common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr))
+ {
+ return;
+ }
+ }
+
+ if (g_previous_sigbus.sa_sigaction != NULL)
+ {
+ g_previous_sigbus.sa_sigaction(code, siginfo, context);
+ }
+ else
+ {
+ // Restore the original or default handler and restart h/w exception
+ restore_signal(code, &g_previous_sigbus);
+ }
+
+ PROCNotifyProcessShutdown();
+}
+
+/*++
+Function :
+ sigint_handler
+
+ handle SIGINT signal
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigint_handler(int code, siginfo_t *siginfo, void *context)
+{
+ PROCNotifyProcessShutdown();
+
+ // Restore the original or default handler and resend signal
+ restore_signal(code, &g_previous_sigint);
+ kill(gPID, code);
+}
+
+/*++
+Function :
+ sigquit_handler
+
+ handle SIGQUIT signal
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigquit_handler(int code, siginfo_t *siginfo, void *context)
+{
+ PROCNotifyProcessShutdown();
+
+ // Restore the original or default handler and resend signal
+ restore_signal(code, &g_previous_sigquit);
+ kill(gPID, code);
+}
+
+/*++
+Function :
+ sigterm_handler
+
+ handle SIGTERM signal
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+ (no return value)
+--*/
+static void sigterm_handler(int code, siginfo_t *siginfo, void *context)
+{
+ if (PALIsInitialized())
+ {
+ // g_pSynchronizationManager shouldn't be null if PAL is initialized.
+ _ASSERTE(g_pSynchronizationManager != nullptr);
+
+ g_pSynchronizationManager->SendTerminationRequestToWorkerThread();
+ }
+ else
+ {
+ if (g_previous_sigterm.sa_sigaction != NULL)
+ {
+ g_previous_sigterm.sa_sigaction(code, siginfo, context);
+ }
+ }
+}
+
+#ifdef INJECT_ACTIVATION_SIGNAL
+/*++
+Function :
+ inject_activation_handler
+
+ Handle the INJECT_ACTIVATION_SIGNAL signal. This signal interrupts a running thread
+ so it can call the activation function that was specified when sending the signal.
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+(no return value)
+--*/
+static void inject_activation_handler(int code, siginfo_t *siginfo, void *context)
+{
+ // Only accept activations from the current process
+ if (g_activationFunction != NULL && siginfo->si_pid == getpid())
+ {
+ _ASSERTE(g_safeActivationCheckFunction != NULL);
+
+ native_context_t *ucontext = (native_context_t *)context;
+
+ CONTEXT winContext;
+ CONTEXTFromNativeContext(
+ ucontext,
+ &winContext,
+ CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT);
+
+ if (g_safeActivationCheckFunction(CONTEXTGetPC(&winContext), /* checkingCurrentThread */ TRUE))
+ {
+ g_activationFunction(&winContext);
+ // Activation function may have modified the context, so update it.
+ CONTEXTToNativeContext(&winContext, ucontext);
+ }
+ }
+ else if (g_previous_activation.sa_sigaction != NULL)
+ {
+ g_previous_activation.sa_sigaction(code, siginfo, context);
+ }
+}
+#endif
+
+/*++
+Function :
+ InjectActivationInternal
+
+ Interrupt the specified thread and have it call the activationFunction passed in
+
+Parameters :
+ pThread - target PAL thread
+ activationFunction - function to call
+
+(no return value)
+--*/
+PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread)
+{
+#ifdef INJECT_ACTIVATION_SIGNAL
+ int status = pthread_kill(pThread->GetPThreadSelf(), INJECT_ACTIVATION_SIGNAL);
+ if (status != 0)
+ {
+ // Failure to send the signal is fatal. There are only two cases when sending
+ // the signal can fail. First, if the signal ID is invalid and second,
+ // if the thread doesn't exist anymore.
+ PROCAbort();
+ }
+
+ return NO_ERROR;
+#else
+ return ERROR_CANCELLED;
+#endif
+}
+
+/*++
+Function :
+ SEHSetSafeState
+
+ specify whether the current thread is in a state where exception handling
+ of signals can be done safely
+
+Parameters:
+ BOOL state : TRUE if the thread is safe, FALSE otherwise
+
+(no return value)
+--*/
+void SEHSetSafeState(CPalThread *pthrCurrent, BOOL state)
+{
+ if (NULL == pthrCurrent)
+ {
+ ASSERT( "Unable to get the thread object.\n" );
+ return;
+ }
+ pthrCurrent->sehInfo.safe_state = state;
+}
+
+/*++
+Function :
+ SEHGetSafeState
+
+ determine whether the current thread is in a state where exception handling
+ of signals can be done safely
+
+ (no parameters)
+
+Return value :
+ TRUE if the thread is in a safe state, FALSE otherwise
+--*/
+BOOL SEHGetSafeState(CPalThread *pthrCurrent)
+{
+ if (NULL == pthrCurrent)
+ {
+ ASSERT( "Unable to get the thread object.\n" );
+ return FALSE;
+ }
+ return pthrCurrent->sehInfo.safe_state;
+}
+
+/*++
+Function :
+ common_signal_handler
+
+ common code for all signal handlers
+
+Parameters :
+ int code : signal received
+ siginfo_t *siginfo : siginfo passed to the signal handler
+ void *context : context structure passed to the signal handler
+ int numParams : number of variable parameters of the exception
+ ... : variable parameters of the exception (each of size_t type)
+
+ Returns true if the execution should continue or false if the exception was unhandled
+Note:
+ the "pointers" parameter should contain a valid exception record pointer,
+ but the ContextRecord pointer will be overwritten.
+--*/
+static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...)
+{
+ sigset_t signal_set;
+ CONTEXT *contextRecord;
+ EXCEPTION_RECORD *exceptionRecord;
+ native_context_t *ucontext;
+
+ ucontext = (native_context_t *)sigcontext;
+
+ AllocateExceptionRecords(&exceptionRecord, &contextRecord);
+
+ exceptionRecord->ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext);
+ exceptionRecord->ExceptionFlags = EXCEPTION_IS_SIGNAL;
+ exceptionRecord->ExceptionRecord = NULL;
+ exceptionRecord->ExceptionAddress = GetNativeContextPC(ucontext);
+ exceptionRecord->NumberParameters = numParams;
+
+ va_list params;
+ va_start(params, numParams);
+
+ for (int i = 0; i < numParams; i++)
+ {
+ exceptionRecord->ExceptionInformation[i] = va_arg(params, size_t);
+ }
+
+ // Pre-populate context with data from current frame, because ucontext doesn't have some data (e.g. SS register)
+ // which is required for restoring context
+ RtlCaptureContext(contextRecord);
+
+ ULONG contextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT;
+
+#if defined(_AMD64_)
+ contextFlags |= CONTEXT_XSTATE;
+#endif
+
+ // Fill context record with required information. from pal.h:
+ // On non-Win32 platforms, the CONTEXT pointer in the
+ // PEXCEPTION_POINTERS will contain at least the CONTEXT_CONTROL registers.
+ CONTEXTFromNativeContext(ucontext, contextRecord, contextFlags);
+
+ /* Unmask signal so we can receive it again */
+ sigemptyset(&signal_set);
+ sigaddset(&signal_set, code);
+ int sigmaskRet = pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
+ if (sigmaskRet != 0)
+ {
+ ASSERT("pthread_sigmask failed; error number is %d\n", sigmaskRet);
+ }
+
+ contextRecord->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
+ // The exception object takes ownership of the exceptionRecord and contextRecord
+ PAL_SEHException exception(exceptionRecord, contextRecord);
+
+ if (SEHProcessException(&exception))
+ {
+ // Exception handling may have modified the context, so update it.
+ CONTEXTToNativeContext(contextRecord, ucontext);
+ return true;
+ }
+
+ return false;
+}
+
+/*++
+Function :
+ handle_signal
+
+ register handler for specified signal
+
+Parameters :
+ int signal_id : signal to handle
+ SIGFUNC sigfunc : signal handler
+ previousAction : previous sigaction struct
+
+ (no return value)
+
+note : if sigfunc is NULL, the default signal handler is restored
+--*/
+void handle_signal(int signal_id, SIGFUNC sigfunc, struct sigaction *previousAction)
+{
+ struct sigaction newAction;
+
+ newAction.sa_flags = SA_RESTART;
+#if HAVE_SIGINFO_T
+ newAction.sa_handler = NULL;
+ newAction.sa_sigaction = sigfunc;
+ newAction.sa_flags |= SA_SIGINFO;
+#else /* HAVE_SIGINFO_T */
+ newAction.sa_handler = SIG_DFL;
+#endif /* HAVE_SIGINFO_T */
+ sigemptyset(&newAction.sa_mask);
+
+ if (-1 == sigaction(signal_id, &newAction, previousAction))
+ {
+ ASSERT("handle_signal: sigaction() call failed with error code %d (%s)\n",
+ errno, strerror(errno));
+ }
+}
+
+/*++
+Function :
+ restore_signal
+
+ restore handler for specified signal
+
+Parameters :
+ int signal_id : signal to handle
+ previousAction : previous sigaction struct to restore
+
+ (no return value)
+--*/
+void restore_signal(int signal_id, struct sigaction *previousAction)
+{
+ if (-1 == sigaction(signal_id, previousAction, NULL))
+ {
+ ASSERT("restore_signal: sigaction() call failed with error code %d (%s)\n",
+ errno, strerror(errno));
+ }
+}
+
+#endif // !HAVE_MACH_EXCEPTIONS
diff --git a/src/pal/src/exception/signal.hpp b/src/pal/src/exception/signal.hpp
new file mode 100644
index 0000000000..c0c950ed4d
--- /dev/null
+++ b/src/pal/src/exception/signal.hpp
@@ -0,0 +1,59 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ exception/signal.hpp
+
+Abstract:
+ Private signal handling utilities for SEH
+
+
+
+--*/
+
+#ifndef _PAL_SIGNAL_HPP_
+#define _PAL_SIGNAL_HPP_
+
+#if !HAVE_MACH_EXCEPTIONS
+
+/*++
+Function :
+ SEHInitializeSignals
+
+ Set-up signal handlers to catch signals and translate them to exceptions
+
+Parameters :
+ flags: PAL initialization flags
+
+Return :
+ TRUE in case of a success, FALSE otherwise
+--*/
+BOOL SEHInitializeSignals(DWORD flags);
+
+/*++
+Function :
+ SEHCleanupSignals
+
+ Restore default signal handlers
+
+ (no parameters, no return value)
+--*/
+void SEHCleanupSignals();
+
+#if (__GNUC__ > 3 || \
+ (__GNUC__ == 3 && __GNUC_MINOR__ > 2))
+// For gcc > 3.2, sjlj exceptions semantics are no longer available
+// Therefore we need to hijack out of signal handlers before second pass
+#define HIJACK_ON_SIGNAL 1
+#endif
+
+#endif // !HAVE_MACH_EXCEPTIONS
+
+#endif /* _PAL_SIGNAL_HPP_ */
+
diff --git a/src/pal/src/file/directory.cpp b/src/pal/src/file/directory.cpp
new file mode 100644
index 0000000000..8e54f94d69
--- /dev/null
+++ b/src/pal/src/file/directory.cpp
@@ -0,0 +1,725 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ directory.c
+
+Abstract:
+
+ Implementation of the file WIN API for the PAL
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/stackstring.hpp"
+
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(FILE);
+
+
+
+/*++
+Function:
+ CreateDirectoryW
+
+Note:
+ lpSecurityAttributes always NULL.
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+CreateDirectoryW(
+ IN LPCWSTR lpPathName,
+ IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
+{
+ BOOL bRet = FALSE;
+ DWORD dwLastError = 0;
+ int mb_size;
+ char *mb_dir = NULL;
+
+ PERF_ENTRY(CreateDirectoryW);
+ ENTRY("CreateDirectoryW(lpPathName=%p (%S), lpSecurityAttr=%p)\n",
+ lpPathName?lpPathName:W16_NULLSTRING,
+ lpPathName?lpPathName:W16_NULLSTRING, lpSecurityAttributes);
+
+ if ( lpSecurityAttributes )
+ {
+ ASSERT("lpSecurityAttributes is not NULL as it should be\n");
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* translate the wide char lpPathName string to multibyte string */
+ if(0 == (mb_size = WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, NULL, 0,
+ NULL, NULL )))
+ {
+ ASSERT("WideCharToMultiByte failure! error is %d\n", GetLastError());
+ dwLastError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+
+ if (((mb_dir = (char *)PAL_malloc(mb_size)) == NULL) ||
+ (WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, mb_dir, mb_size, NULL,
+ NULL) != mb_size))
+ {
+ ASSERT("WideCharToMultiByte or PAL_malloc failure! LastError:%d errno:%d\n",
+ GetLastError(), errno);
+ dwLastError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+
+ bRet = CreateDirectoryA(mb_dir,NULL);
+done:
+ if( dwLastError )
+ {
+ SetLastError( dwLastError );
+ }
+ if (mb_dir != NULL)
+ {
+ PAL_free(mb_dir);
+ }
+ LOGEXIT("CreateDirectoryW returns BOOL %d\n", bRet);
+ PERF_EXIT(CreateDirectoryW);
+ return bRet;
+}
+
+/*++
+Routine Name:
+
+ RemoveDirectoryHelper
+
+Routine Description:
+
+ Core function which removes a directory. Called by RemoveDirectory[AW]
+
+Parameters:
+
+ LPSTR lpPathName - [in/out]
+ The directory name to remove. It is converted in place to a unix path.
+
+Return Value:
+
+ BOOL -
+ TRUE <=> successful
+
+--*/
+
+static
+BOOL
+RemoveDirectoryHelper (
+ PathCharString& lpPathName,
+ LPDWORD dwLastError
+)
+{
+ BOOL bRet = FALSE;
+ *dwLastError = 0;
+
+ FILEDosToUnixPathA( lpPathName );
+
+ if ( rmdir(lpPathName) != 0 )
+ {
+ TRACE("Removal of directory [%s] was unsuccessful, errno = %d.\n",
+ lpPathName.GetString(), errno);
+
+ switch( errno )
+ {
+ case ENOTDIR:
+ /* FALL THROUGH */
+ case ENOENT:
+ {
+ struct stat stat_data;
+
+ if ( stat( lpPathName, &stat_data) == 0 &&
+ (stat_data.st_mode & S_IFMT) == S_IFREG )
+ {
+ /* Not a directory, it is a file. */
+ *dwLastError = ERROR_DIRECTORY;
+ }
+ else
+ {
+ FILEGetProperNotFoundError( lpPathName, dwLastError );
+ }
+ break;
+ }
+ case ENOTEMPTY:
+ *dwLastError = ERROR_DIR_NOT_EMPTY;
+ break;
+ default:
+ *dwLastError = ERROR_ACCESS_DENIED;
+ }
+ }
+ else {
+ TRACE("Removal of directory [%s] was successful.\n", lpPathName.GetString());
+ bRet = TRUE;
+ }
+
+ return bRet;
+}
+
+/*++
+Function:
+ RemoveDirectoryA
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+RemoveDirectoryA(
+ IN LPCSTR lpPathName)
+{
+ DWORD dwLastError = 0;
+ BOOL bRet = FALSE;
+ PathCharString mb_dirPathString;
+
+ PERF_ENTRY(RemoveDirectoryA);
+ ENTRY("RemoveDirectoryA(lpPathName=%p (%s))\n",
+ lpPathName,
+ lpPathName);
+
+ if (lpPathName == NULL)
+ {
+ dwLastError = ERROR_PATH_NOT_FOUND;
+ goto done;
+ }
+
+ if (!mb_dirPathString.Set(lpPathName, strlen(lpPathName)))
+ {
+ WARN("Set failed !\n");
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ bRet = RemoveDirectoryHelper (mb_dirPathString, &dwLastError);
+
+done:
+ if( dwLastError )
+ {
+ SetLastError( dwLastError );
+ }
+
+ LOGEXIT("RemoveDirectoryA returns BOOL %d\n", bRet);
+ PERF_EXIT(RemoveDirectoryA);
+ return bRet;
+}
+
+/*++
+Function:
+ RemoveDirectoryW
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+RemoveDirectoryW(
+ IN LPCWSTR lpPathName)
+{
+ PathCharString mb_dirPathString;
+ int mb_size;
+ DWORD dwLastError = 0;
+ BOOL bRet = FALSE;
+ size_t length;
+ char * mb_dir = NULL;
+
+ PERF_ENTRY(RemoveDirectoryW);
+ ENTRY("RemoveDirectoryW(lpPathName=%p (%S))\n",
+ lpPathName?lpPathName:W16_NULLSTRING,
+ lpPathName?lpPathName:W16_NULLSTRING);
+
+ if (lpPathName == NULL)
+ {
+ dwLastError = ERROR_PATH_NOT_FOUND;
+ goto done;
+ }
+
+ length = (PAL_wcslen(lpPathName)+1) * 3;
+ mb_dir = mb_dirPathString.OpenStringBuffer(length);
+ if (NULL == mb_dir)
+ {
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ mb_size = WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, mb_dir, length,
+ NULL, NULL );
+
+ if( mb_size == 0 )
+ {
+ mb_dirPathString.CloseBuffer(0);
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ dwLastError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+
+ mb_dirPathString.CloseBuffer(mb_size - 1);
+
+ if ((bRet = RemoveDirectoryHelper (mb_dirPathString, &dwLastError)))
+ {
+ TRACE("Removal of directory [%s] was successful.\n", mb_dir);
+ }
+
+done:
+ if( dwLastError )
+ {
+ SetLastError( dwLastError );
+ }
+
+ LOGEXIT("RemoveDirectoryW returns BOOL %d\n", bRet);
+ PERF_EXIT(RemoveDirectoryW);
+ return bRet;
+}
+
+
+/*++
+Function:
+ GetCurrentDirectoryA
+
+--*/
+DWORD
+GetCurrentDirectoryA(PathCharString& lpBuffer)
+{
+ DWORD dwDirLen = 0;
+ DWORD dwLastError = 0;
+
+ char *current_dir;
+
+ PERF_ENTRY(GetCurrentDirectoryA);
+ ENTRY("GetCurrentDirectoryA(lpBuffer=%p)\n", lpBuffer.GetString());
+
+ current_dir = lpBuffer.OpenStringBuffer(MAX_PATH);
+ /* NULL first arg means getcwd will allocate the string */
+ current_dir = PAL__getcwd( current_dir, MAX_PATH);
+
+ if (current_dir != NULL )
+ {
+ dwDirLen = strlen( current_dir );
+ lpBuffer.CloseBuffer(dwDirLen);
+ goto done;
+ }
+ else if ( errno == ERANGE )
+ {
+ lpBuffer.CloseBuffer(0);
+ current_dir = PAL__getcwd( NULL, 0);
+ }
+
+ if ( !current_dir )
+ {
+ WARN("Getcwd failed with errno=%d [%s]\n", errno, strerror(errno));
+ dwLastError = DIRGetLastErrorFromErrno();
+ dwDirLen = 0;
+ goto done;
+ }
+
+ dwDirLen = strlen( current_dir );
+ lpBuffer.Set(current_dir, dwDirLen);
+ PAL_free(current_dir);
+done:
+
+ if ( dwLastError )
+ {
+ SetLastError(dwLastError);
+ }
+
+ LOGEXIT("GetCurrentDirectoryA returns DWORD %u\n", dwDirLen);
+ PERF_EXIT(GetCurrentDirectoryA);
+ return dwDirLen;
+}
+
+/*++
+Function:
+ GetCurrentDirectoryA
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+GetCurrentDirectoryA(
+ IN DWORD nBufferLength,
+ OUT LPSTR lpBuffer)
+{
+
+ PathCharString lpBufferString;
+ DWORD dwDirLen = GetCurrentDirectoryA(lpBufferString);
+
+ /* if the supplied buffer isn't long enough, return the required
+ length, including room for the NULL terminator */
+ if ( nBufferLength <= dwDirLen )
+ {
+ ++dwDirLen; /* include space for the NULL */
+ }
+ else
+ {
+ strcpy_s( lpBuffer, nBufferLength, lpBufferString );
+ }
+
+ return dwDirLen;
+}
+
+/*++
+Function:
+ GetCurrentDirectoryW
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+GetCurrentDirectoryW(
+ IN DWORD nBufferLength,
+ OUT LPWSTR lpBuffer)
+{
+ DWORD dwWideLen = 0;
+ DWORD dwLastError = ERROR_BAD_PATHNAME;
+ int dir_len;
+ PathCharString current_dir;
+
+ PERF_ENTRY(GetCurrentDirectoryW);
+ ENTRY("GetCurrentDirectoryW(nBufferLength=%u, lpBuffer=%p)\n",
+ nBufferLength, lpBuffer);
+
+
+ dir_len = GetCurrentDirectoryA(current_dir);
+
+ if( dir_len == 0)
+ {
+ dwLastError = DIRGetLastErrorFromErrno();
+ goto done;
+ }
+
+ dwWideLen = MultiByteToWideChar( CP_ACP, 0,
+ current_dir, dir_len,
+ NULL, 0 );
+
+ /* if the supplied buffer isn't long enough, return the required
+ length, including room for the NULL terminator */
+ if ( nBufferLength > dwWideLen )
+ {
+ if(!MultiByteToWideChar( CP_ACP, 0, current_dir, dir_len + 1,
+ lpBuffer, nBufferLength ))
+ {
+ ASSERT("MultiByteToWideChar failure!\n");
+ dwWideLen = 0;
+ dwLastError = ERROR_INTERNAL_ERROR;
+ }
+ }
+ else
+ {
+ ++dwWideLen; /* include the space for the NULL */
+ }
+
+done:
+
+ if ( dwLastError )
+ {
+ SetLastError(dwLastError);
+ }
+
+ LOGEXIT("GetCurrentDirectoryW returns DWORD %u\n", dwWideLen);
+ PERF_EXIT(GetCurrentDirectoryW);
+ return dwWideLen;
+}
+
+
+/*++
+Function:
+ SetCurrentDirectoryW
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+SetCurrentDirectoryW(
+ IN LPCWSTR lpPathName)
+{
+ BOOL bRet;
+ DWORD dwLastError = 0;
+ PathCharString dirPathString;
+ int size;
+ size_t length;
+ char * dir = NULL;
+
+ PERF_ENTRY(SetCurrentDirectoryW);
+ ENTRY("SetCurrentDirectoryW(lpPathName=%p (%S))\n",
+ lpPathName?lpPathName:W16_NULLSTRING,
+ lpPathName?lpPathName:W16_NULLSTRING);
+
+ /*check if the given path is null. If so
+ return FALSE*/
+ if (lpPathName == NULL )
+ {
+ ERROR("Invalid path/directory name\n");
+ dwLastError = ERROR_INVALID_NAME;
+ bRet = FALSE;
+ goto done;
+ }
+
+ length = (PAL_wcslen(lpPathName)+1) * 3;
+ dir = dirPathString.OpenStringBuffer(length);
+ if (NULL == dir)
+ {
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ bRet = FALSE;
+ goto done;
+ }
+
+ size = WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, dir, length,
+ NULL, NULL );
+
+ if( size == 0 )
+ {
+ dirPathString.CloseBuffer(0);
+ dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ dwLastError = ERROR_INTERNAL_ERROR;
+ bRet = FALSE;
+ goto done;
+ }
+
+ dirPathString.CloseBuffer(size - 1);
+ bRet = SetCurrentDirectoryA(dir);
+done:
+ if( dwLastError )
+ {
+ SetLastError(dwLastError);
+ }
+
+ LOGEXIT("SetCurrentDirectoryW returns BOOL %d\n", bRet);
+ PERF_EXIT(SetCurrentDirectoryW);
+ return bRet;
+}
+
+/*++
+Function:
+ CreateDirectoryA
+
+Note:
+ lpSecurityAttributes always NULL.
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+CreateDirectoryA(
+ IN LPCSTR lpPathName,
+ IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
+{
+ BOOL bRet = FALSE;
+ DWORD dwLastError = 0;
+ PathCharString realPath;
+ char* realPathBuf;
+ LPSTR unixPathName = NULL;
+ int pathLength;
+ int i;
+ const int mode = S_IRWXU | S_IRWXG | S_IRWXO;
+
+ PERF_ENTRY(CreateDirectoryA);
+ ENTRY("CreateDirectoryA(lpPathName=%p (%s), lpSecurityAttr=%p)\n",
+ lpPathName?lpPathName:"NULL",
+ lpPathName?lpPathName:"NULL", lpSecurityAttributes);
+
+ if ( lpSecurityAttributes )
+ {
+ ASSERT("lpSecurityAttributes is not NULL as it should be\n");
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ // Windows returns ERROR_PATH_NOT_FOUND when called with NULL.
+ // If we don't have this check, strdup(NULL) segfaults.
+ if (lpPathName == NULL)
+ {
+ ERROR("CreateDirectoryA called with NULL pathname!\n");
+ dwLastError = ERROR_PATH_NOT_FOUND;
+ goto done;
+ }
+
+ unixPathName = PAL__strdup(lpPathName);
+ if (unixPathName == NULL )
+ {
+ ERROR("PAL__strdup() failed\n");
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ FILEDosToUnixPathA( unixPathName );
+ // Remove any trailing slashes at the end because mkdir might not
+ // handle them appropriately on all platforms.
+ pathLength = strlen(unixPathName);
+ i = pathLength;
+ while(i > 1)
+ {
+ if(unixPathName[i - 1] =='/')
+ {
+ unixPathName[i - 1]='\0';
+ i--;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+
+ // Get an absolute path.
+ if (unixPathName[0] == '/')
+ {
+ realPathBuf = unixPathName;
+ }
+ else
+ {
+
+ DWORD len = GetCurrentDirectoryA(realPath);
+ if (len == 0 || !realPath.Reserve(realPath.GetCount() + pathLength + 1 ))
+ {
+ dwLastError = DIRGetLastErrorFromErrno();
+ WARN("Getcwd failed with errno=%d \n", dwLastError);
+ goto done;
+ }
+
+ realPath.Append("/", 1);
+ realPath.Append(unixPathName, pathLength);
+ realPathBuf = realPath.OpenStringBuffer(realPath.GetCount());
+ }
+
+ // Canonicalize the path so we can determine its length.
+ FILECanonicalizePath(realPathBuf);
+
+ if ( mkdir(realPathBuf, mode) != 0 )
+ {
+ TRACE("Creation of directory [%s] was unsuccessful, errno = %d.\n",
+ unixPathName, errno);
+
+ switch( errno )
+ {
+ case ENOTDIR:
+ /* FALL THROUGH */
+ case ENOENT:
+ FILEGetProperNotFoundError( realPathBuf, &dwLastError );
+ goto done;
+ case EEXIST:
+ dwLastError = ERROR_ALREADY_EXISTS;
+ break;
+ default:
+ dwLastError = ERROR_ACCESS_DENIED;
+ }
+ }
+ else
+ {
+ TRACE("Creation of directory [%s] was successful.\n", unixPathName);
+ bRet = TRUE;
+ }
+
+ realPath.CloseBuffer(0); //The PathCharString usage is done
+done:
+ if( dwLastError )
+ {
+ SetLastError( dwLastError );
+ }
+ PAL_free( unixPathName );
+ LOGEXIT("CreateDirectoryA returns BOOL %d\n", bRet);
+ PERF_EXIT(CreateDirectoryA);
+ return bRet;
+}
+
+/*++
+Function:
+ SetCurrentDirectoryA
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+SetCurrentDirectoryA(
+ IN LPCSTR lpPathName)
+{
+ BOOL bRet = FALSE;
+ DWORD dwLastError = 0;
+ int result;
+ LPSTR unixPathName = NULL;
+
+ PERF_ENTRY(SetCurrentDirectoryA);
+ ENTRY("SetCurrentDirectoryA(lpPathName=%p (%s))\n",
+ lpPathName?lpPathName:"NULL",
+ lpPathName?lpPathName:"NULL");
+
+ /*check if the given path is null. If so
+ return FALSE*/
+ if (lpPathName == NULL )
+ {
+ ERROR("Invalid path/directory name\n");
+ dwLastError = ERROR_INVALID_NAME;
+ goto done;
+ }
+
+ unixPathName = PAL__strdup(lpPathName);
+ if (unixPathName == NULL )
+ {
+ ERROR("PAL__strdup() failed\n");
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ FILEDosToUnixPathA( unixPathName );
+
+ TRACE("Attempting to open Unix dir [%s]\n", unixPathName);
+ result = chdir(unixPathName);
+
+ if ( result == 0 )
+ {
+ bRet = TRUE;
+ }
+ else
+ {
+ if ( errno == ENOTDIR || errno == ENOENT )
+ {
+ struct stat stat_data;
+
+ if ( stat( unixPathName, &stat_data) == 0 &&
+ (stat_data.st_mode & S_IFMT) == S_IFREG )
+ {
+ /* Not a directory, it is a file. */
+ dwLastError = ERROR_DIRECTORY;
+ }
+ else
+ {
+ FILEGetProperNotFoundError( unixPathName, &dwLastError );
+ }
+ TRACE("chdir() failed, path was invalid.\n");
+ }
+ else
+ {
+ dwLastError = ERROR_ACCESS_DENIED;
+ ERROR("chdir() failed; errno is %d (%s)\n", errno, strerror(errno));
+ }
+ }
+
+
+done:
+ if( dwLastError )
+ {
+ SetLastError(dwLastError);
+ }
+
+ if(unixPathName != NULL)
+ {
+ PAL_free( unixPathName );
+ }
+
+ LOGEXIT("SetCurrentDirectoryA returns BOOL %d\n", bRet);
+ PERF_EXIT(SetCurrentDirectoryA);
+ return bRet;
+}
diff --git a/src/pal/src/file/disk.cpp b/src/pal/src/file/disk.cpp
new file mode 100644
index 0000000000..ef1d488b28
--- /dev/null
+++ b/src/pal/src/file/disk.cpp
@@ -0,0 +1,180 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ disk.c
+
+Abstract:
+
+ Implementation of the disk information functions.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/stackstring.hpp"
+
+#include <sys/param.h>
+#if !defined(_AIX)
+// do we actually need this on other platforms. We don't seem to be using anything from there
+#include <sys/mount.h>
+#endif
+#include <errno.h>
+#if HAVE_STATVFS
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#define statfs statvfs
+#if STATVFS64_PROTOTYPE_BROKEN
+typedef statvfs_t pal_statfs;
+#else // STATVFS64_PROTOTYPE_BROKEN
+typedef struct statvfs pal_statfs;
+#endif // STATVFS64_PROTOTYPE_BROKEN
+#else // HAVE_STATVFS
+typedef struct statfs pal_statfs;
+#endif // HAVE_STATVFS
+
+SET_DEFAULT_DEBUG_CHANNEL(FILE);
+
+/*++
+
+Function:
+
+ GetDiskFreeSpaceW
+
+See MSDN doc.
+--*/
+PALIMPORT
+BOOL
+PALAPI
+GetDiskFreeSpaceW(
+ LPCWSTR lpDirectoryName,
+ LPDWORD lpSectorsPerCluster,
+ LPDWORD lpBytesPerSector,
+ LPDWORD lpNumberOfFreeClusters, /* Caller will ignore output value */
+ LPDWORD lpTotalNumberOfClusters) /* Caller will ignore output value */
+{
+ BOOL bRetVal = FALSE;
+ pal_statfs fsInfoBuffer;
+ INT statfsRetVal = 0;
+ DWORD dwLastError = NO_ERROR;
+ PathCharString dirNameBufferPathString;
+ size_t length;
+ char * dirNameBuffer;
+ int size;
+
+ PERF_ENTRY(GetDiskFreeSpaceW);
+ ENTRY( "GetDiskFreeSpaceW( lpDirectoryName=%p (%S), lpSectorsPerCluster=%p,"
+ "lpBytesPerSector=%p, lpNumberOfFreeClusters=%p, "
+ "lpTotalNumberOfClusters=%p )\n", lpDirectoryName ? lpDirectoryName :
+ W16_NULLSTRING, lpDirectoryName ? lpDirectoryName :
+ W16_NULLSTRING, lpSectorsPerCluster, lpBytesPerSector,
+ lpNumberOfFreeClusters, lpTotalNumberOfClusters );
+
+ /* Sanity checks. */
+ if ( !lpSectorsPerCluster )
+ {
+ ERROR( "lpSectorsPerCluster cannot be NULL!\n" );
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+ if ( !lpBytesPerSector )
+ {
+ ERROR( "lpBytesPerSector cannot be NULL!\n" );
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+ if ( lpNumberOfFreeClusters || lpTotalNumberOfClusters )
+ {
+ TRACE("GetDiskFreeSpaceW is ignoring lpNumberOfFreeClusters"
+ " and lpTotalNumberOfClusters\n" );
+ }
+ if ( lpDirectoryName && PAL_wcslen( lpDirectoryName ) == 0 )
+ {
+ ERROR( "lpDirectoryName is empty.\n" );
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ /* Fusion uses this API to round file sizes up to their actual size
+ on-disk based on the BytesPerSector * SectorsPerCluster.
+ The intent is to avoid computing the sum of all file sizes in the
+ cache just in bytes and not account for the cluster-sized slop, when
+ determining if the cache is too large or not. */
+
+ if ( lpDirectoryName )
+ {
+ length = (PAL_wcslen(lpDirectoryName)+1) * 3;
+ dirNameBuffer = dirNameBufferPathString.OpenStringBuffer(length);
+ if (NULL == dirNameBuffer)
+ {
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto exit;
+ }
+
+ size = WideCharToMultiByte( CP_ACP, 0, lpDirectoryName, -1,
+ dirNameBuffer,length, 0, 0 );
+ dirNameBufferPathString.CloseBuffer(size);
+ if ( size != 0 )
+ {
+ FILEDosToUnixPathA( dirNameBuffer );
+ statfsRetVal = statfs( dirNameBuffer, &fsInfoBuffer );
+ }
+ else
+ {
+ ASSERT( "Unable to convert the lpDirectoryName to multibyte.\n" );
+ dwLastError = ERROR_INTERNAL_ERROR;
+ goto exit;
+ }
+ }
+ else
+ {
+ statfsRetVal = statfs( "/", &fsInfoBuffer );
+ }
+
+ if ( statfsRetVal == 0 )
+ {
+ *lpBytesPerSector = fsInfoBuffer.f_bsize;
+ *lpSectorsPerCluster = 1;
+ bRetVal = TRUE;
+ }
+ else
+ {
+ if ( errno == ENOTDIR || errno == ENOENT )
+ {
+ FILEGetProperNotFoundError( dirNameBuffer, &dwLastError );
+ goto exit;
+ }
+ dwLastError = FILEGetLastErrorFromErrno();
+ if ( ERROR_INTERNAL_ERROR == dwLastError )
+ {
+ ASSERT("statfs() not expected to fail with errno:%d (%s)\n",
+ errno, strerror(errno));
+ }
+ else
+ {
+ TRACE("statfs() failed, errno:%d (%s)\n", errno, strerror(errno));
+ }
+ }
+
+exit:
+ if ( NO_ERROR != dwLastError )
+ {
+ SetLastError( dwLastError );
+ }
+
+ LOGEXIT( "GetDiskFreeSpace returning %s.\n", bRetVal == TRUE ? "TRUE" : "FALSE" );
+ PERF_EXIT(GetDiskFreeSpaceW);
+ return bRetVal;
+}
+
diff --git a/src/pal/src/file/file.cpp b/src/pal/src/file/file.cpp
new file mode 100644
index 0000000000..6443a5e7b9
--- /dev/null
+++ b/src/pal/src/file/file.cpp
@@ -0,0 +1,4900 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ file.cpp
+
+Abstract:
+
+ Implementation of the file WIN API for the PAL
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/file.hpp"
+#include "shmfilelockmgr.hpp"
+#include "pal/malloc.hpp"
+#include "pal/stackstring.hpp"
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/filetime.h"
+#include "pal/utils.h"
+
+#include <time.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <limits.h>
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(FILE);
+
+int MaxWCharToAcpLengthFactor = 3;
+
+PAL_ERROR
+InternalSetFilePointerForUnixFd(
+ int iUnixFd,
+ LONG lDistanceToMove,
+ PLONG lpDistanceToMoveHigh,
+ DWORD dwMoveMethod,
+ PLONG lpNewFilePointerLow
+ );
+
+void
+FileCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup,
+ bool fShutdown,
+ bool fCleanupSharedState
+ );
+
+CObjectType CorUnix::otFile(
+ otiFile,
+ FileCleanupRoutine,
+ NULL, // No initialization routine
+ 0, // No immutable data
+ sizeof(CFileProcessLocalData),
+ 0, // No shared data
+ GENERIC_READ|GENERIC_WRITE, // Ignored -- no Win32 object security support
+ CObjectType::SecuritySupported,
+ CObjectType::OSPersistedSecurityInfo,
+ CObjectType::UnnamedObject,
+ CObjectType::LocalDuplicationOnly,
+ CObjectType::UnwaitableObject,
+ CObjectType::SignalingNotApplicable,
+ CObjectType::ThreadReleaseNotApplicable,
+ CObjectType::OwnershipNotApplicable
+ );
+
+CAllowedObjectTypes CorUnix::aotFile(otiFile);
+static CSharedMemoryFileLockMgr _FileLockManager;
+IFileLockManager *CorUnix::g_pFileLockManager = &_FileLockManager;
+
+void
+FileCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup,
+ bool fShutdown,
+ bool fCleanupSharedState
+ )
+{
+ PAL_ERROR palError;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+
+ palError = pObjectToCleanup->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to obtain data to cleanup file object");
+ return;
+ }
+
+ if (pLocalData->pLockController != NULL)
+ {
+ pLocalData->pLockController->ReleaseController();
+ }
+
+ if (!fShutdown && -1 != pLocalData->unix_fd)
+ {
+ close(pLocalData->unix_fd);
+ }
+
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+}
+
+typedef enum
+{
+ PIID_STDIN_HANDLE,
+ PIID_STDOUT_HANDLE,
+ PIID_STDERR_HANDLE
+} PROCINFO_ID;
+
+#define PAL_LEGAL_FLAGS_ATTRIBS (FILE_ATTRIBUTE_NORMAL| \
+ FILE_FLAG_SEQUENTIAL_SCAN| \
+ FILE_FLAG_WRITE_THROUGH| \
+ FILE_FLAG_NO_BUFFERING| \
+ FILE_FLAG_RANDOM_ACCESS| \
+ FILE_FLAG_BACKUP_SEMANTICS)
+
+/* Static global. The init function must be called
+before any other functions and if it is not successful,
+no other functions should be done. */
+static HANDLE pStdIn = INVALID_HANDLE_VALUE;
+static HANDLE pStdOut = INVALID_HANDLE_VALUE;
+static HANDLE pStdErr = INVALID_HANDLE_VALUE;
+
+/*++
+Function :
+ FILEGetProperNotFoundError
+
+Returns the proper error code, based on the
+Windows behavior.
+
+ IN LPSTR lpPath - The path to check.
+ LPDWORD lpErrorCode - The error to set.
+*/
+void FILEGetProperNotFoundError( LPCSTR lpPath, LPDWORD lpErrorCode )
+{
+ struct stat stat_data;
+ LPSTR lpDupedPath = NULL;
+ LPSTR lpLastPathSeparator = NULL;
+
+ TRACE( "FILEGetProperNotFoundError( %s )\n", lpPath?lpPath:"(null)" );
+
+ if ( !lpErrorCode )
+ {
+ ASSERT( "lpErrorCode has to be valid\n" );
+ return;
+ }
+
+ if ( NULL == ( lpDupedPath = strdup(lpPath) ) )
+ {
+ ERROR( "strdup() failed!\n" );
+ *lpErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+ return;
+ }
+
+ /* Determine whether it's a file not found or path not found. */
+ lpLastPathSeparator = strrchr( lpDupedPath, '/');
+ if ( lpLastPathSeparator != NULL )
+ {
+ *lpLastPathSeparator = '\0';
+
+ /* If the last path component is a directory,
+ we return file not found. If it's a file or
+ doesn't exist, we return path not found. */
+ if ( '\0' == *lpDupedPath ||
+ ( stat( lpDupedPath, &stat_data ) == 0 &&
+ ( stat_data.st_mode & S_IFMT ) == S_IFDIR ) )
+ {
+ TRACE( "ERROR_FILE_NOT_FOUND\n" );
+ *lpErrorCode = ERROR_FILE_NOT_FOUND;
+ }
+ else
+ {
+ TRACE( "ERROR_PATH_NOT_FOUND\n" );
+ *lpErrorCode = ERROR_PATH_NOT_FOUND;
+ }
+ }
+ else
+ {
+ TRACE( "ERROR_FILE_NOT_FOUND\n" );
+ *lpErrorCode = ERROR_FILE_NOT_FOUND;
+ }
+
+ free(lpDupedPath);
+ lpDupedPath = NULL;
+ TRACE( "FILEGetProperNotFoundError returning TRUE\n" );
+ return;
+}
+
+/*++
+Function :
+ FILEGetLastErrorFromErrnoAndFilename
+
+Returns the proper error code for errno, or, if errno is ENOENT,
+based on the Windows behavior for nonexistent filenames.
+
+ IN LPSTR lpPath - The path to check.
+*/
+PAL_ERROR FILEGetLastErrorFromErrnoAndFilename(LPCSTR lpPath)
+{
+ PAL_ERROR palError;
+ if (ENOENT == errno)
+ {
+ FILEGetProperNotFoundError(lpPath, &palError);
+ }
+ else
+ {
+ palError = FILEGetLastErrorFromErrno();
+ }
+ return palError;
+}
+
+BOOL
+CorUnix::RealPathHelper(LPCSTR lpUnixPath, PathCharString& lpBuffer)
+{
+ StringHolder lpRealPath;
+ lpRealPath = realpath(lpUnixPath, NULL);
+ if (lpRealPath.IsNull())
+ {
+ return FALSE;
+ }
+
+ lpBuffer.Set(lpRealPath, strlen(lpRealPath));
+ return TRUE;
+}
+/*++
+InternalCanonicalizeRealPath
+ Wraps realpath() to hide platform differences. See the man page for
+ realpath(3) for details of how realpath() works.
+
+ On systems on which realpath() allows the last path component to not
+ exist, this is a straight thunk through to realpath(). On other
+ systems, we remove the last path component, then call realpath().
+
+--*/
+PAL_ERROR
+CorUnix::InternalCanonicalizeRealPath(LPCSTR lpUnixPath, PathCharString& lpBuffer)
+{
+ PAL_ERROR palError = NO_ERROR;
+
+#if !REALPATH_SUPPORTS_NONEXISTENT_FILES
+ StringHolder lpExistingPath;
+ LPSTR pchSeparator = NULL;
+ LPSTR lpFilename = NULL;
+ DWORD cchBuffer = 0;
+ DWORD cchFilename = 0;
+#endif // !REALPATH_SUPPORTS_NONEXISTENT_FILES
+
+ if (lpUnixPath == NULL)
+ {
+ ERROR ("Invalid argument to InternalCanonicalizeRealPath\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto LExit;
+ }
+
+#if REALPATH_SUPPORTS_NONEXISTENT_FILES
+ RealPathHelper(lpUnixPath, lpBuffer);
+#else // !REALPATH_SUPPORTS_NONEXISTENT_FILES
+
+ lpExistingPath = strdup(lpUnixPath);
+ if (lpExistingPath.IsNull())
+ {
+ ERROR ("strdup failed with error %d\n", errno);
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto LExit;
+ }
+
+ pchSeparator = strrchr(lpExistingPath, '/');
+ if (pchSeparator == NULL)
+ {
+ PathCharString pszCwdBuffer;
+
+ if (GetCurrentDirectoryA(pszCwdBuffer)== 0)
+ {
+ WARN("getcwd(NULL) failed with error %d\n", errno);
+ palError = DIRGetLastErrorFromErrno();
+ goto LExit;
+ }
+
+ if (! RealPathHelper(pszCwdBuffer, lpBuffer))
+ {
+ WARN("realpath() failed with error %d\n", errno);
+ palError = FILEGetLastErrorFromErrno();
+#if defined(_AMD64_)
+ // If we are here, then we tried to invoke realpath
+ // against a directory.
+ //
+ // On Mac64, realpath implementation differs from Mac32
+ // by *not* supporting invalid filenames in the path (while
+ // Mac32 implementation does).
+ //
+ // Thus, if we are here, and the error code we see is
+ // ERROR_FILE_NOT_FOUND, then we should map it to
+ // ERROR_PATH_NOT_FOUND since it was a directory that
+ // was not found (and not a file).
+ if (palError == ERROR_FILE_NOT_FOUND)
+ {
+ // Since lpBuffer can be modified by the realpath call,
+ // and can result in a truncated subset of the original buffer,
+ // we use strstr as a level of safety.
+ if (strstr(pszCwdBuffer, lpBuffer) != 0)
+ {
+ palError = ERROR_PATH_NOT_FOUND;
+ }
+ }
+#endif // defined(_AMD64_)
+
+ goto LExit;
+ }
+ lpFilename = lpExistingPath;
+ }
+ else
+ {
+#if defined(_AMD64_)
+ bool fSetFilename = true;
+ // Since realpath implementation cannot handle inexistent filenames,
+ // check if we are going to truncate the "/" corresponding to the
+ // root folder (e.g. case of "/Volumes"). If so:
+ //
+ // 1) Set the seperator to point to the NULL terminator of the specified
+ // file/folder name.
+ //
+ // 2) Null terminate lpBuffer
+ //
+ // 3) Since there is no explicit filename component in lpExistingPath (as
+ // we only have "/" corresponding to the root), set lpFilename to NULL,
+ // alongwith a flag indicating that it has already been set.
+ if (pchSeparator == lpExistingPath)
+ {
+ pchSeparator = lpExistingPath+strlen(lpExistingPath);
+
+ // Set the lpBuffer to NULL
+ lpBuffer.Clear();
+ lpFilename = NULL;
+ fSetFilename = false;
+ }
+ else
+#endif // defined(_AMD64_)
+ *pchSeparator = '\0';
+
+ if (!RealPathHelper(lpExistingPath, lpBuffer))
+ {
+ WARN("realpath() failed with error %d\n", errno);
+ palError = FILEGetLastErrorFromErrno();
+
+#if defined(_AMD64_)
+ // If we are here, then we tried to invoke realpath
+ // against a directory after stripping out the filename
+ // from the original path.
+ //
+ // On Mac64, realpath implementation differs from Mac32
+ // by *not* supporting invalid filenames in the path (while
+ // Mac32 implementation does).
+ //
+ // Thus, if we are here, and the error code we see is
+ // ERROR_FILE_NOT_FOUND, then we should map it to
+ // ERROR_PATH_NOT_FOUND since it was a directory that
+ // was not found (and not a file).
+ if (palError == ERROR_FILE_NOT_FOUND)
+ {
+ // Since lpBuffer can be modified by the realpath call,
+ // and can result in a truncated subset of the original buffer,
+ // we use strstr as a level of safety.
+ if (strstr(lpExistingPath, lpBuffer) != 0)
+ {
+ palError = ERROR_PATH_NOT_FOUND;
+ }
+ }
+#endif // defined(_AMD64_)
+
+ goto LExit;
+ }
+
+#if defined(_AMD64_)
+ if (fSetFilename == true)
+#endif // defined(_AMD64_)
+ lpFilename = pchSeparator + 1;
+ }
+
+#if defined(_AMD64_)
+ if (lpFilename == NULL)
+ goto LExit;
+#endif // _AMD64_
+
+ if (!lpBuffer.Append("/",1) || !lpBuffer.Append(lpFilename, strlen(lpFilename)))
+ {
+ ERROR ("Append failed!\n");
+ palError = ERROR_INSUFFICIENT_BUFFER;
+
+ // Doing a goto here since we want to exit now. This will work
+ // incase someone else adds another if clause below us.
+ goto LExit;
+ }
+
+#endif // REALPATH_SUPPORTS_NONEXISTENT_FILES
+LExit:
+
+ if ((palError == NO_ERROR) && lpBuffer.IsEmpty())
+ {
+ // convert all these into ERROR_PATH_NOT_FOUND
+ palError = ERROR_PATH_NOT_FOUND;
+ }
+
+ return palError;
+}
+
+PAL_ERROR
+CorUnix::InternalCreateFile(
+ CPalThread *pThread,
+ LPCSTR lpFileName,
+ DWORD dwDesiredAccess,
+ DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes,
+ HANDLE hTemplateFile,
+ HANDLE *phFile
+ )
+{
+ PAL_ERROR palError = 0;
+ IPalObject *pFileObject = NULL;
+ IPalObject *pRegisteredFile = NULL;
+ IDataLock *pDataLock = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IFileLockController *pLockController = NULL;
+ CObjectAttributes oaFile(NULL, lpSecurityAttributes);
+ BOOL fFileExists = FALSE;
+
+ BOOL inheritable = FALSE;
+ PathCharString lpUnixPath;
+ int filed = -1;
+ int create_flags = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ int open_flags = 0;
+ int lock_mode = LOCK_SH;
+
+ // track whether we've created the file with the intended name,
+ // so that it can be removed on failure exit
+ BOOL bFileCreated = FALSE;
+
+ const char* szNonfilePrefix = "\\\\.\\";
+ PathCharString lpFullUnixPath;
+
+ /* for dwShareMode only three flags are accepted */
+ if ( dwShareMode & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) )
+ {
+ ASSERT( "dwShareMode is invalid\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if ( lpFileName == NULL )
+ {
+ ERROR("InternalCreateFile called with NULL filename\n");
+ palError = ERROR_PATH_NOT_FOUND;
+ goto done;
+ }
+
+ if ( strncmp(lpFileName, szNonfilePrefix, strlen(szNonfilePrefix)) == 0 )
+ {
+ ERROR("InternalCreateFile does not support paths beginning with %s\n", szNonfilePrefix);
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if( !lpUnixPath.Set(lpFileName,strlen(lpFileName)))
+ {
+ ERROR("strdup() failed\n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ FILEDosToUnixPathA( lpUnixPath );
+
+ // Compute the absolute pathname to the file. This pathname is used
+ // to determine if two file names represent the same file.
+ palError = InternalCanonicalizeRealPath(lpUnixPath, lpFullUnixPath);
+ if (palError != NO_ERROR)
+ {
+ goto done;
+ }
+
+ lpUnixPath.Set(lpFullUnixPath);
+
+ switch( dwDesiredAccess )
+ {
+ case 0:
+ /* Device Query Access was requested. let's use open() with
+ no flags, it's basically the equivalent of O_RDONLY, since
+ O_RDONLY is defined as 0x0000 */
+ break;
+ case( GENERIC_READ ):
+ open_flags |= O_RDONLY;
+ break;
+ case( GENERIC_WRITE ):
+ open_flags |= O_WRONLY;
+ break;
+ case( GENERIC_READ | GENERIC_WRITE ):
+ open_flags |= O_RDWR;
+ break;
+ default:
+ ERROR("dwDesiredAccess value of %d is invalid\n", dwDesiredAccess);
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ TRACE("open flags are 0x%lx\n", open_flags);
+
+ if ( lpSecurityAttributes )
+ {
+ if ( lpSecurityAttributes->nLength != sizeof( SECURITY_ATTRIBUTES ) ||
+ lpSecurityAttributes->lpSecurityDescriptor != NULL ||
+ !lpSecurityAttributes->bInheritHandle )
+ {
+ ASSERT("lpSecurityAttributes points to invalid values.\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+ inheritable = TRUE;
+ }
+
+ if ( (dwFlagsAndAttributes & PAL_LEGAL_FLAGS_ATTRIBS) !=
+ dwFlagsAndAttributes)
+ {
+ ASSERT("Bad dwFlagsAndAttributes\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+ else if (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS)
+ {
+ /* Override the open flags, and always open as readonly. This
+ flag is used when opening a directory, to change its
+ creation/modification/access times. On Windows, the directory
+ must be open for write, but on Unix, it needs to be readonly. */
+ open_flags = O_RDONLY;
+ } else {
+ struct stat st;
+
+ if (stat(lpUnixPath, &st) == 0 && (st.st_mode & S_IFDIR))
+ {
+ /* The file exists and it is a directory. Without
+ FILE_FLAG_BACKUP_SEMANTICS, Win32 CreateFile always fails
+ to open directories. */
+ palError = ERROR_ACCESS_DENIED;
+ goto done;
+ }
+ }
+
+ if ( hTemplateFile )
+ {
+ ASSERT("hTemplateFile is not NULL, as it should be.\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ //
+ // The file sharing mode checks are performed by the lock manager so we need
+ // to get the lock controller for this file.
+ // Do this before modifying the file system since we wouldn't want to, for
+ // instance, truncate a file before finding out if we have write access to it.
+ // It may seem odd that in some cases we will acquire a lock on a file that
+ // doesn't exist yet but the lock manager does not care -- files are
+ // abstract entities represented by a name from its point of view.
+ //
+
+ palError = g_pFileLockManager->GetLockControllerForFile(
+ pThread,
+ lpUnixPath,
+ dwDesiredAccess,
+ dwShareMode,
+ &pLockController
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto done;
+ }
+
+ /* NB: According to MSDN docs, When CREATE_ALWAYS or OPEN_ALWAYS is
+ set, CreateFile should SetLastError to ERROR_ALREADY_EXISTS,
+ even though/if CreateFile will be successful.
+ */
+ switch( dwCreationDisposition )
+ {
+ case( CREATE_ALWAYS ):
+ // check whether the file exists
+ if ( access( lpUnixPath, F_OK ) == 0 )
+ {
+ fFileExists = TRUE;
+ }
+ open_flags |= O_CREAT | O_TRUNC;
+ break;
+ case( CREATE_NEW ):
+ open_flags |= O_CREAT | O_EXCL;
+ break;
+ case( OPEN_EXISTING ):
+ /* don't need to do anything here */
+ break;
+ case( OPEN_ALWAYS ):
+ if ( access( lpUnixPath, F_OK ) == 0 )
+ {
+ fFileExists = TRUE;
+ }
+ open_flags |= O_CREAT;
+ break;
+ case( TRUNCATE_EXISTING ):
+ open_flags |= O_TRUNC;
+ break;
+ default:
+ ASSERT("dwCreationDisposition value of %d is not valid\n",
+ dwCreationDisposition);
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if ( dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING )
+ {
+ TRACE("I/O will be unbuffered\n");
+#ifdef O_DIRECT
+ open_flags |= O_DIRECT;
+#endif
+ }
+ else
+ {
+ TRACE("I/O will be buffered\n");
+ }
+
+ filed = InternalOpen(lpUnixPath, open_flags, create_flags);
+ TRACE("Allocated file descriptor [%d]\n", filed);
+
+ if ( filed < 0 )
+ {
+ TRACE("open() failed; error is %s (%d)\n", strerror(errno), errno);
+ palError = FILEGetLastErrorFromErrnoAndFilename(lpUnixPath);
+ goto done;
+ }
+
+ // Deduce whether we created a file in the previous operation (there's a
+ // small timing window between the access() used to determine fFileExists
+ // and the open() operation, but there's not much we can do about that.
+ bFileCreated = (dwCreationDisposition == CREATE_ALWAYS ||
+ dwCreationDisposition == CREATE_NEW ||
+ dwCreationDisposition == OPEN_ALWAYS) &&
+ !fFileExists;
+
+
+ // While the lock manager is able to provide support for share modes within an instance of
+ // the PAL, other PALs will ignore these locks. In order to support a basic level of cross
+ // process locking, we'll use advisory locks. FILE_SHARE_NONE implies a exclusive lock on the
+ // file and all other modes use a shared lock. While this is not as granular as Windows,
+ // you can atleast implement a lock file using this.
+ lock_mode = (dwShareMode == 0 /* FILE_SHARE_NONE */) ? LOCK_EX : LOCK_SH;
+
+ if(flock(filed, lock_mode | LOCK_NB) != 0)
+ {
+ TRACE("flock() failed; error is %s (%d)\n", strerror(errno), errno);
+ if (errno == EWOULDBLOCK)
+ {
+ palError = ERROR_SHARING_VIOLATION;
+ }
+ else
+ {
+ palError = FILEGetLastErrorFromErrno();
+ }
+
+ goto done;
+ }
+
+#ifndef O_DIRECT
+ if ( dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING )
+ {
+#ifdef F_NOCACHE
+ if (-1 == fcntl(filed, F_NOCACHE, 1))
+ {
+ ASSERT("Can't set F_NOCACHE; fcntl() failed. errno is %d (%s)\n",
+ errno, strerror(errno));
+ palError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+#else
+#error Insufficient support for uncached I/O on this platform
+#endif
+ }
+#endif
+
+ /* make file descriptor close-on-exec; inheritable handles will get
+ "uncloseonexeced" in CreateProcess if they are actually being inherited*/
+ if(-1 == fcntl(filed,F_SETFD,1))
+ {
+ ASSERT("can't set close-on-exec flag; fcntl() failed. errno is %d "
+ "(%s)\n", errno, strerror(errno));
+ palError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otFile,
+ &oaFile,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto done;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto done;
+ }
+
+ if (strcpy_s(pLocalData->unix_filename, sizeof(pLocalData->unix_filename), lpUnixPath) != SAFECRT_SUCCESS)
+ {
+ palError = ERROR_INSUFFICIENT_BUFFER;
+ TRACE("strcpy_s failed!\n");
+ goto done;
+ }
+
+ pLocalData->inheritable = inheritable;
+ pLocalData->unix_fd = filed;
+ pLocalData->dwDesiredAccess = dwDesiredAccess;
+ pLocalData->open_flags = open_flags;
+ pLocalData->open_flags_deviceaccessonly = (dwDesiredAccess == 0);
+
+ //
+ // Transfer the lock controller reference from our local variable
+ // to the local file data
+ //
+
+ pLocalData->pLockController = pLockController;
+ pLockController = NULL;
+
+ //
+ // We've finished initializing our local data, so release that lock
+ //
+
+ pDataLock->ReleaseLock(pThread, TRUE);
+ pDataLock = NULL;
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pFileObject,
+ &aotFile,
+ dwDesiredAccess,
+ phFile,
+ &pRegisteredFile
+ );
+
+ //
+ // pFileObject is invalidated by the call to RegisterObject, so NULL it
+ // out here to ensure that we don't try to release a reference on
+ // it down the line.
+ //
+
+ pFileObject = NULL;
+
+done:
+
+ // At this point, if we've been successful, palError will be NO_ERROR.
+ // CreateFile can return ERROR_ALREADY_EXISTS in some success cases;
+ // those cases are flagged by fFileExists and are handled below.
+ if (NO_ERROR != palError)
+ {
+ if (filed >= 0)
+ {
+ close(filed);
+ }
+ if (bFileCreated)
+ {
+ if (-1 == unlink(lpUnixPath))
+ {
+ WARN("can't delete file; unlink() failed with errno %d (%s)\n",
+ errno, strerror(errno));
+ }
+ }
+ }
+
+ if (NULL != pLockController)
+ {
+ pLockController->ReleaseController();
+ }
+
+ if (NULL != pDataLock)
+ {
+ pDataLock->ReleaseLock(pThread, TRUE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ if (NULL != pRegisteredFile)
+ {
+ pRegisteredFile->ReleaseReference(pThread);
+ }
+
+ if (NO_ERROR == palError && fFileExists)
+ {
+ palError = ERROR_ALREADY_EXISTS;
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ CreateFileA
+
+Note:
+ Only bInherit flag is used from the LPSECURITY_ATTRIBUTES struct.
+ Desired access is READ, WRITE or 0
+ Share mode is READ, WRITE or DELETE
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+CreateFileA(
+ IN LPCSTR lpFileName,
+ IN DWORD dwDesiredAccess,
+ IN DWORD dwShareMode,
+ IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ IN DWORD dwCreationDisposition,
+ IN DWORD dwFlagsAndAttributes,
+ IN HANDLE hTemplateFile
+ )
+{
+ CPalThread *pThread;
+ PAL_ERROR palError = NO_ERROR;
+ HANDLE hRet = INVALID_HANDLE_VALUE;
+
+ PERF_ENTRY(CreateFileA);
+ ENTRY("CreateFileA(lpFileName=%p (%s), dwAccess=%#x, dwShareMode=%#x, "
+ "lpSecurityAttr=%p, dwDisposition=%#x, dwFlags=%#x, "
+ "hTemplateFile=%p )\n",lpFileName?lpFileName:"NULL",lpFileName?lpFileName:"NULL", dwDesiredAccess,
+ dwShareMode, lpSecurityAttributes, dwCreationDisposition,
+ dwFlagsAndAttributes, hTemplateFile);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalCreateFile(
+ pThread,
+ lpFileName,
+ dwDesiredAccess,
+ dwShareMode,
+ lpSecurityAttributes,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ hTemplateFile,
+ &hRet
+ );
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pThread->SetLastError(palError);
+
+ LOGEXIT("CreateFileA returns HANDLE %p\n", hRet);
+ PERF_EXIT(CreateFileA);
+ return hRet;
+}
+
+
+/*++
+Function:
+ CreateFileW
+
+Note:
+ Only bInherit flag is used from the LPSECURITY_ATTRIBUTES struct.
+ Desired access is READ, WRITE or 0
+ Share mode is READ, WRITE or DELETE
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+CreateFileW(
+ IN LPCWSTR lpFileName,
+ IN DWORD dwDesiredAccess,
+ IN DWORD dwShareMode,
+ IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ IN DWORD dwCreationDisposition,
+ IN DWORD dwFlagsAndAttributes,
+ IN HANDLE hTemplateFile)
+{
+ CPalThread *pThread;
+ PAL_ERROR palError = NO_ERROR;
+ PathCharString namePathString;
+ char * name;
+ int size;
+ int length = 0;
+ HANDLE hRet = INVALID_HANDLE_VALUE;
+
+ PERF_ENTRY(CreateFileW);
+ ENTRY("CreateFileW(lpFileName=%p (%S), dwAccess=%#x, dwShareMode=%#x, "
+ "lpSecurityAttr=%p, dwDisposition=%#x, dwFlags=%#x, hTemplateFile=%p )\n",
+ lpFileName?lpFileName:W16_NULLSTRING,
+ lpFileName?lpFileName:W16_NULLSTRING, dwDesiredAccess, dwShareMode,
+ lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+ hTemplateFile);
+
+ pThread = InternalGetCurrentThread();
+
+ if (lpFileName != NULL)
+ {
+ length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor;
+ }
+
+ name = namePathString.OpenStringBuffer(length);
+ if (NULL == name)
+ {
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length,
+ NULL, NULL );
+
+ if( size == 0 )
+ {
+ namePathString.CloseBuffer(0);
+ DWORD dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ palError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+
+ namePathString.CloseBuffer(size - 1);
+
+ palError = InternalCreateFile(
+ pThread,
+ name,
+ dwDesiredAccess,
+ dwShareMode,
+ lpSecurityAttributes,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ hTemplateFile,
+ &hRet
+ );
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+done:
+ pThread->SetLastError(palError);
+ LOGEXIT( "CreateFileW returns HANDLE %p\n", hRet );
+ PERF_EXIT(CreateFileW);
+ return hRet;
+}
+
+
+/*++
+Function:
+ CopyFileW
+
+See MSDN doc.
+
+Notes:
+ There are several (most) error paths here that do not call SetLastError().
+This is because we know that CreateFile, ReadFile, and WriteFile will do so,
+and will have a much better idea of the specific error.
+--*/
+BOOL
+PALAPI
+CopyFileW(
+ IN LPCWSTR lpExistingFileName,
+ IN LPCWSTR lpNewFileName,
+ IN BOOL bFailIfExists)
+{
+ CPalThread *pThread;
+ PathCharString sourcePathString;
+ PathCharString destPathString;
+ char * source;
+ char * dest;
+ int src_size, dest_size, length = 0;
+ BOOL bRet = FALSE;
+
+ PERF_ENTRY(CopyFileW);
+ ENTRY("CopyFileW(lpExistingFileName=%p (%S), lpNewFileName=%p (%S), bFailIfExists=%d)\n",
+ lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
+ lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
+ lpNewFileName?lpNewFileName:W16_NULLSTRING,
+ lpNewFileName?lpNewFileName:W16_NULLSTRING, bFailIfExists);
+
+ pThread = InternalGetCurrentThread();
+ if (lpExistingFileName != NULL)
+ {
+ length = (PAL_wcslen(lpExistingFileName)+1) * MaxWCharToAcpLengthFactor;
+ }
+
+ source = sourcePathString.OpenStringBuffer(length);
+ if (NULL == source)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+
+ src_size = WideCharToMultiByte( CP_ACP, 0, lpExistingFileName, -1, source, length,
+ NULL, NULL );
+
+ if( src_size == 0 )
+ {
+ sourcePathString.CloseBuffer(0);
+ DWORD dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ sourcePathString.CloseBuffer(src_size - 1);
+ length = 0;
+
+ if (lpNewFileName != NULL)
+ {
+ length = (PAL_wcslen(lpNewFileName)+1) * MaxWCharToAcpLengthFactor;
+ }
+
+ dest = destPathString.OpenStringBuffer(length);
+ if (NULL == dest)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ dest_size = WideCharToMultiByte( CP_ACP, 0, lpNewFileName, -1, dest, length,
+ NULL, NULL );
+
+ if( dest_size == 0 )
+ {
+ destPathString.CloseBuffer(0);
+ DWORD dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ destPathString.CloseBuffer(dest_size - 1);
+ bRet = CopyFileA(source,dest,bFailIfExists);
+
+done:
+ LOGEXIT("CopyFileW returns BOOL %d\n", bRet);
+ PERF_EXIT(CopyFileW);
+ return bRet;
+}
+
+
+/*++
+Function:
+ DeleteFileA
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+DeleteFileA(
+ IN LPCSTR lpFileName)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread;
+ int result;
+ BOOL bRet = FALSE;
+ DWORD dwLastError = 0;
+ PathCharString lpunixFileName;
+ PathCharString lpFullunixFileName;
+
+ PERF_ENTRY(DeleteFileA);
+ ENTRY("DeleteFileA(lpFileName=%p (%s))\n", lpFileName?lpFileName:"NULL", lpFileName?lpFileName:"NULL");
+
+ pThread = InternalGetCurrentThread();
+
+ if( !lpunixFileName.Set(lpFileName, strlen(lpFileName)))
+ {
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ FILEDosToUnixPathA( lpunixFileName );
+
+ // Compute the absolute pathname to the file. This pathname is used
+ // to determine if two file names represent the same file.
+ palError = InternalCanonicalizeRealPath(lpunixFileName, lpFullunixFileName);
+ if (palError != NO_ERROR)
+ {
+ if (!lpFullunixFileName.Set(lpunixFileName, strlen(lpunixFileName)))
+ {
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ }
+
+ result = unlink( lpFullunixFileName );
+
+ if (result < 0)
+ {
+ TRACE("unlink returns %d\n", result);
+ dwLastError = FILEGetLastErrorFromErrnoAndFilename(lpFullunixFileName);
+ }
+ else
+ {
+ bRet = TRUE;
+ }
+
+done:
+ if(dwLastError)
+ {
+ pThread->SetLastError( dwLastError );
+ }
+
+ LOGEXIT("DeleteFileA returns BOOL %d\n", bRet);
+ PERF_EXIT(DeleteFileA);
+ return bRet;
+}
+
+/*++
+Function:
+ DeleteFileW
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+DeleteFileW(
+ IN LPCWSTR lpFileName)
+{
+ CPalThread *pThread;
+ int size;
+ PathCharString namePS;
+ char * name;
+ int length = 0;
+ BOOL bRet = FALSE;
+
+ PERF_ENTRY(DeleteFileW);
+ ENTRY("DeleteFileW(lpFileName=%p (%S))\n",
+ lpFileName?lpFileName:W16_NULLSTRING,
+ lpFileName?lpFileName:W16_NULLSTRING);
+
+ pThread = InternalGetCurrentThread();
+
+ if (lpFileName != NULL)
+ {
+ length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor;
+ }
+
+ name = namePS.OpenStringBuffer(length);
+ if (NULL == name)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+
+ size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length,
+ NULL, NULL );
+
+ if( size == 0 )
+ {
+ namePS.CloseBuffer(0);
+ DWORD dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ bRet = FALSE;
+ goto done;
+ }
+
+ namePS.CloseBuffer(size - 1);
+ bRet = DeleteFileA( name );
+
+done:
+ LOGEXIT("DeleteFileW returns BOOL %d\n", bRet);
+ PERF_EXIT(DeleteFileW);
+ return bRet;
+}
+
+
+/*++
+Function:
+ MoveFileA
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+MoveFileA(
+ IN LPCSTR lpExistingFileName,
+ IN LPCSTR lpNewFileName)
+{
+ BOOL bRet;
+
+ PERF_ENTRY(MoveFileA);
+ ENTRY("MoveFileA(lpExistingFileName=%p (%s), lpNewFileName=%p (%s))\n",
+ lpExistingFileName?lpExistingFileName:"NULL",
+ lpExistingFileName?lpExistingFileName:"NULL",
+ lpNewFileName?lpNewFileName:"NULL",
+ lpNewFileName?lpNewFileName:"NULL");
+
+ bRet = MoveFileExA( lpExistingFileName,
+ lpNewFileName,
+ MOVEFILE_COPY_ALLOWED );
+
+ LOGEXIT("MoveFileA returns BOOL %d\n", bRet);
+ PERF_EXIT(MoveFileA);
+ return bRet;
+}
+
+
+/*++
+Function:
+ MoveFileW
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+MoveFileW(
+ IN LPCWSTR lpExistingFileName,
+ IN LPCWSTR lpNewFileName)
+{
+ BOOL bRet;
+
+ PERF_ENTRY(MoveFileW);
+ ENTRY("MoveFileW(lpExistingFileName=%p (%S), lpNewFileName=%p (%S))\n",
+ lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
+ lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
+ lpNewFileName?lpNewFileName:W16_NULLSTRING,
+ lpNewFileName?lpNewFileName:W16_NULLSTRING);
+
+ bRet = MoveFileExW( lpExistingFileName,
+ lpNewFileName,
+ MOVEFILE_COPY_ALLOWED );
+
+ LOGEXIT("MoveFileW returns BOOL %d\n", bRet);
+ PERF_EXIT(MoveFileW);
+ return bRet;
+}
+
+/*++
+Function:
+ MoveFileExA
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+MoveFileExA(
+ IN LPCSTR lpExistingFileName,
+ IN LPCSTR lpNewFileName,
+ IN DWORD dwFlags)
+{
+ CPalThread *pThread;
+ int result;
+ PathCharString source;
+ PathCharString dest;
+ BOOL bRet = TRUE;
+ DWORD dwLastError = 0;
+
+ PERF_ENTRY(MoveFileExA);
+ ENTRY("MoveFileExA(lpExistingFileName=%p (%S), lpNewFileName=%p (%S), "
+ "dwFlags=%#x)\n",
+ lpExistingFileName?lpExistingFileName:"NULL",
+ lpExistingFileName?lpExistingFileName:"NULL",
+ lpNewFileName?lpNewFileName:"NULL",
+ lpNewFileName?lpNewFileName:"NULL", dwFlags);
+
+ pThread = InternalGetCurrentThread();
+ /* only two flags are accepted */
+ if ( dwFlags & ~(MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) )
+ {
+ ASSERT( "dwFlags is invalid\n" );
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+
+ if( !source.Set(lpExistingFileName, strlen(lpExistingFileName)))
+ {
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ FILEDosToUnixPathA( source );
+
+ if( !dest.Set(lpNewFileName, strlen(lpNewFileName)))
+ {
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ FILEDosToUnixPathA( dest );
+
+ if ( !(dwFlags & MOVEFILE_REPLACE_EXISTING) )
+ {
+#if HAVE_CASE_SENSITIVE_FILESYSTEM
+ if ( strcmp(source, dest) != 0 )
+#else // HAVE_CASE_SENSITIVE_FILESYSTEM
+ if ( strcasecmp(source, dest) != 0 )
+#endif // HAVE_CASE_SENSITIVE_FILESYSTEM
+ {
+ // Let things proceed normally if source and
+ // dest are the same.
+ if ( access(dest, F_OK) == 0 )
+ {
+ dwLastError = ERROR_ALREADY_EXISTS;
+ goto done;
+ }
+ }
+ }
+
+ result = rename( source, dest );
+ if ((result < 0) && (dwFlags & MOVEFILE_REPLACE_EXISTING) &&
+ ((errno == ENOTDIR) || (errno == EEXIST)))
+ {
+ bRet = DeleteFileA( lpNewFileName );
+
+ if ( bRet )
+ {
+ result = rename( source, dest );
+ }
+ else
+ {
+ dwLastError = GetLastError();
+ }
+ }
+
+ if ( result < 0 )
+ {
+ switch( errno )
+ {
+ case EXDEV: /* we tried to link across devices */
+
+ if ( dwFlags & MOVEFILE_COPY_ALLOWED )
+ {
+ BOOL bFailIfExists = !(dwFlags & MOVEFILE_REPLACE_EXISTING);
+
+ /* if CopyFile fails here, so should MoveFailEx */
+ bRet = CopyFileA( lpExistingFileName,
+ lpNewFileName,
+ bFailIfExists );
+ /* CopyFile should set the appropriate error */
+ if ( !bRet )
+ {
+ dwLastError = GetLastError();
+ }
+ else
+ {
+ if (!DeleteFileA(lpExistingFileName))
+ {
+ ERROR("Failed to delete the source file\n");
+ dwLastError = GetLastError();
+
+ /* Delete the destination file if we're unable to delete
+ the source file */
+ if (!DeleteFileA(lpNewFileName))
+ {
+ ERROR("Failed to delete the destination file\n");
+ }
+ }
+ }
+ }
+ else
+ {
+ dwLastError = ERROR_ACCESS_DENIED;
+ }
+ break;
+ case EINVAL: // tried to rename "." or ".."
+ dwLastError = ERROR_SHARING_VIOLATION;
+ break;
+ case ENOENT:
+ {
+ struct stat buf;
+ if (lstat(source, &buf) == -1)
+ {
+ FILEGetProperNotFoundError(source, &dwLastError);
+ }
+ else
+ {
+ dwLastError = ERROR_PATH_NOT_FOUND;
+ }
+ }
+ break;
+ default:
+ dwLastError = FILEGetLastErrorFromErrno();
+ break;
+ }
+ }
+
+done:
+ if ( dwLastError )
+ {
+ pThread->SetLastError( dwLastError );
+ bRet = FALSE;
+ }
+
+ LOGEXIT( "MoveFileExA returns BOOL %d\n", bRet );
+ PERF_EXIT(MoveFileExA);
+ return bRet;
+}
+
+/*++
+Function:
+ MoveFileExW
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+MoveFileExW(
+ IN LPCWSTR lpExistingFileName,
+ IN LPCWSTR lpNewFileName,
+ IN DWORD dwFlags)
+{
+ CPalThread *pThread;
+ PathCharString sourcePS;
+ PathCharString destPS;
+ char * source;
+ char * dest;
+ int length = 0;
+ int src_size,dest_size;
+ BOOL bRet = FALSE;
+
+ PERF_ENTRY(MoveFileExW);
+ ENTRY("MoveFileExW(lpExistingFileName=%p (%S), lpNewFileName=%p (%S), dwFlags=%#x)\n",
+ lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
+ lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
+ lpNewFileName?lpNewFileName:W16_NULLSTRING,
+ lpNewFileName?lpNewFileName:W16_NULLSTRING, dwFlags);
+
+ pThread = InternalGetCurrentThread();
+
+ if (lpExistingFileName != NULL)
+ {
+ length = (PAL_wcslen(lpExistingFileName)+1) * MaxWCharToAcpLengthFactor;
+ }
+
+ source = sourcePS.OpenStringBuffer(length);
+ if (NULL == source)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ src_size = WideCharToMultiByte( CP_ACP, 0, lpExistingFileName, -1, source, length,
+ NULL, NULL );
+ if( src_size == 0 )
+ {
+ sourcePS.CloseBuffer(0);
+ DWORD dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ sourcePS.CloseBuffer(src_size - 1);
+ length = 0;
+ if (lpNewFileName != NULL)
+ {
+ length = (PAL_wcslen(lpNewFileName)+1) * MaxWCharToAcpLengthFactor;
+ }
+
+ dest = destPS.OpenStringBuffer(length);
+ if (NULL == dest)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ dest_size = WideCharToMultiByte( CP_ACP, 0, lpNewFileName, -1, dest, length,
+ NULL, NULL );
+
+ if( dest_size == 0 )
+ {
+ destPS.CloseBuffer(0);
+ DWORD dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ destPS.CloseBuffer(dest_size - 1);
+ bRet = MoveFileExA(source,dest,dwFlags);
+
+done:
+ LOGEXIT("MoveFileExW returns BOOL %d\n", bRet);
+ PERF_EXIT(MoveFileExW);
+ return bRet;
+}
+
+/*++
+Function:
+ GetFileAttributesA
+
+Note:
+ Checking for directory and read-only file, according to Rotor spec.
+
+Caveats:
+ There are some important things to note about this implementation, which
+are due to the differences between the FAT filesystem and Unix filesystems:
+
+- fifo's, sockets, and symlinks will return -1, and GetLastError() will
+ return ERROR_ACCESS_DENIED
+
+- if a file is write-only, or has no permissions at all, it is treated
+ the same as if it had mode 'rw'. This is consistent with behaviour on
+ NTFS files with the same permissions.
+
+- the following flags will never be returned:
+
+FILE_ATTRIBUTE_SYSTEM
+FILE_ATTRIBUTE_ARCHIVE
+FILE_ATTRIBUTE_HIDDEN
+
+--*/
+DWORD
+PALAPI
+GetFileAttributesA(
+ IN LPCSTR lpFileName)
+{
+ CPalThread *pThread;
+ struct stat stat_data;
+ DWORD dwAttr = 0;
+ DWORD dwLastError = 0;
+ PathCharString unixFileName;
+
+ PERF_ENTRY(GetFileAttributesA);
+ ENTRY("GetFileAttributesA(lpFileName=%p (%s))\n", lpFileName?lpFileName:"NULL", lpFileName?lpFileName:"NULL");
+
+ pThread = InternalGetCurrentThread();
+ if (lpFileName == NULL)
+ {
+ dwLastError = ERROR_PATH_NOT_FOUND;
+ goto done;
+ }
+
+
+ if( !unixFileName.Set(lpFileName, strlen(lpFileName)))
+ {
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ FILEDosToUnixPathA( unixFileName );
+
+ if ( stat(unixFileName, &stat_data) != 0 )
+ {
+ dwLastError = FILEGetLastErrorFromErrnoAndFilename(unixFileName);
+ goto done;
+ }
+
+ if ( (stat_data.st_mode & S_IFMT) == S_IFDIR )
+ {
+ dwAttr |= FILE_ATTRIBUTE_DIRECTORY;
+ }
+ else if ( (stat_data.st_mode & S_IFMT) != S_IFREG )
+ {
+ ERROR("Not a regular file or directory, S_IFMT is %#x\n",
+ stat_data.st_mode & S_IFMT);
+ dwLastError = ERROR_ACCESS_DENIED;
+ goto done;
+ }
+
+ if ( UTIL_IsReadOnlyBitsSet( &stat_data ) )
+ {
+ dwAttr |= FILE_ATTRIBUTE_READONLY;
+ }
+
+ /* finally, if nothing is set... */
+ if ( dwAttr == 0 )
+ {
+ dwAttr = FILE_ATTRIBUTE_NORMAL;
+ }
+
+done:
+ if (dwLastError)
+ {
+ pThread->SetLastError(dwLastError);
+ dwAttr = INVALID_FILE_ATTRIBUTES;
+ }
+
+ LOGEXIT("GetFileAttributesA returns DWORD %#x\n", dwAttr);
+ PERF_EXIT(GetFileAttributesA);
+ return dwAttr;
+}
+
+
+
+
+/*++
+Function:
+ GetFileAttributesW
+
+Note:
+ Checking for directory and read-only file
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+GetFileAttributesW(
+ IN LPCWSTR lpFileName)
+{
+ CPalThread *pThread;
+ int size;
+ PathCharString filenamePS;
+ int length = 0;
+ char * filename;
+ DWORD dwRet = (DWORD) -1;
+
+ PERF_ENTRY(GetFileAttributesW);
+ ENTRY("GetFileAttributesW(lpFileName=%p (%S))\n",
+ lpFileName?lpFileName:W16_NULLSTRING,
+ lpFileName?lpFileName:W16_NULLSTRING);
+
+ pThread = InternalGetCurrentThread();
+ if (lpFileName == NULL)
+ {
+ pThread->SetLastError(ERROR_PATH_NOT_FOUND);
+ goto done;
+ }
+
+ length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor;
+ filename = filenamePS.OpenStringBuffer(length);
+ if (NULL == filename)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, filename, length,
+ NULL, NULL );
+
+ if( size == 0 )
+ {
+ filenamePS.CloseBuffer(0);
+ DWORD dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ else
+ {
+ filenamePS.CloseBuffer(size - 1);
+ dwRet = GetFileAttributesA( filename );
+ }
+
+done:
+ LOGEXIT("GetFileAttributesW returns DWORD %#x\n", dwRet);
+ PERF_EXIT(GetFileAttributesW);
+ return dwRet;
+}
+
+
+/*++
+Function:
+ GetFileAttributesExW
+
+See MSDN doc, and notes for GetFileAttributesW.
+--*/
+BOOL
+PALAPI
+GetFileAttributesExW(
+ IN LPCWSTR lpFileName,
+ IN GET_FILEEX_INFO_LEVELS fInfoLevelId,
+ OUT LPVOID lpFileInformation)
+{
+ CPalThread *pThread;
+ BOOL bRet = FALSE;
+ DWORD dwLastError = 0;
+ LPWIN32_FILE_ATTRIBUTE_DATA attr_data;
+
+ struct stat stat_data;
+
+ char * name;
+ PathCharString namePS;
+ int length = 0;
+ int size;
+
+ PERF_ENTRY(GetFileAttributesExW);
+ ENTRY("GetFileAttributesExW(lpFileName=%p (%S), fInfoLevelId=%d, "
+ "lpFileInformation=%p)\n", lpFileName?lpFileName:W16_NULLSTRING, lpFileName?lpFileName:W16_NULLSTRING,
+ fInfoLevelId, lpFileInformation);
+
+ pThread = InternalGetCurrentThread();
+ if ( fInfoLevelId != GetFileExInfoStandard )
+ {
+ ASSERT("Unrecognized value for fInfoLevelId=%d\n", fInfoLevelId);
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if ( !lpFileInformation )
+ {
+ ASSERT("lpFileInformation is NULL\n");
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (lpFileName == NULL)
+ {
+ dwLastError = ERROR_PATH_NOT_FOUND;
+ goto done;
+ }
+
+ length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor;
+ name = namePS.OpenStringBuffer(length);
+ if (NULL == name)
+ {
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length,
+ NULL, NULL );
+
+ if( size == 0 )
+ {
+ namePS.CloseBuffer(0);
+ dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ dwLastError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+
+ namePS.CloseBuffer(size - 1);
+ attr_data = (LPWIN32_FILE_ATTRIBUTE_DATA)lpFileInformation;
+
+ attr_data->dwFileAttributes = GetFileAttributesW(lpFileName);
+ /* assume that GetFileAttributes will call SetLastError appropriately */
+ if ( attr_data->dwFileAttributes == (DWORD)-1 )
+ {
+ goto done;
+ }
+
+ FILEDosToUnixPathA(name);
+ /* do the stat */
+ if ( stat(name, &stat_data) != 0 )
+ {
+ ERROR("stat failed on %S\n", lpFileName);
+ dwLastError = FILEGetLastErrorFromErrnoAndFilename(name);
+ goto done;
+ }
+
+ /* get the file times */
+ attr_data->ftCreationTime =
+ FILEUnixTimeToFileTime( stat_data.st_ctime,
+ ST_CTIME_NSEC(&stat_data) );
+ attr_data->ftLastAccessTime =
+ FILEUnixTimeToFileTime( stat_data.st_atime,
+ ST_ATIME_NSEC(&stat_data) );
+ attr_data->ftLastWriteTime =
+ FILEUnixTimeToFileTime( stat_data.st_mtime,
+ ST_MTIME_NSEC(&stat_data) );
+
+ /* Get the file size. GetFileSize is not used because it gets the
+ size of an already-open file */
+ attr_data->nFileSizeLow = (DWORD) stat_data.st_size;
+#if SIZEOF_OFF_T > 4
+ attr_data->nFileSizeHigh = (DWORD)(stat_data.st_size >> 32);
+#else
+ attr_data->nFileSizeHigh = 0;
+#endif
+
+ bRet = TRUE;
+
+done:
+ if (dwLastError) pThread->SetLastError(dwLastError);
+
+ LOGEXIT("GetFileAttributesExW returns BOOL %d\n", bRet);
+ PERF_EXIT(GetFileAttributesExW);
+ return bRet;
+}
+
+/*++
+Function:
+ SetFileAttributesW
+
+Notes:
+ Used for setting read-only attribute on file only.
+
+--*/
+BOOL
+PALAPI
+SetFileAttributesW(
+ IN LPCWSTR lpFileName,
+ IN DWORD dwFileAttributes)
+{
+ CPalThread *pThread;
+ char * name;
+ PathCharString namePS;
+ int length = 0;
+ int size;
+
+ DWORD dwLastError = 0;
+ BOOL bRet = FALSE;
+
+ PERF_ENTRY(SetFileAttributesW);
+ ENTRY("SetFileAttributesW(lpFileName=%p (%S), dwFileAttributes=%#x)\n",
+ lpFileName?lpFileName:W16_NULLSTRING,
+ lpFileName?lpFileName:W16_NULLSTRING, dwFileAttributes);
+
+ pThread = InternalGetCurrentThread();
+ if (lpFileName == NULL)
+ {
+ dwLastError = ERROR_PATH_NOT_FOUND;
+ goto done;
+ }
+
+ length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor;
+ name = namePS.OpenStringBuffer(length);
+ if (NULL == name)
+ {
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length,
+ NULL, NULL );
+
+ if( size == 0 )
+ {
+ namePS.CloseBuffer(0);
+ dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+ namePS.CloseBuffer(size - 1);
+ bRet = SetFileAttributesA(name,dwFileAttributes);
+
+done:
+ if (dwLastError) pThread->SetLastError(dwLastError);
+
+ LOGEXIT("SetFileAttributes returns BOOL %d\n", bRet);
+ PERF_EXIT(SetFileAttributesW);
+ return bRet;
+}
+
+PAL_ERROR
+CorUnix::InternalWriteFile(
+ CPalThread *pThread,
+ HANDLE hFile,
+ LPCVOID lpBuffer,
+ DWORD nNumberOfBytesToWrite,
+ LPDWORD lpNumberOfBytesWritten,
+ LPOVERLAPPED lpOverlapped
+ )
+{
+ PAL_ERROR palError = 0;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ IFileTransactionLock *pTransactionLock = NULL;
+ int ifd;
+
+ LONG writeOffsetStartLow = 0, writeOffsetStartHigh = 0;
+ int res;
+
+ if (NULL != lpNumberOfBytesWritten)
+ {
+ //
+ // This must be set to 0 before any other error checking takes
+ // place, per MSDN
+ //
+
+ *lpNumberOfBytesWritten = 0;
+ }
+ else
+ {
+ ASSERT( "lpNumberOfBytesWritten is NULL\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ // Win32 WriteFile disallows writing to STD_INPUT_HANDLE
+ if (hFile == INVALID_HANDLE_VALUE || hFile == pStdIn)
+ {
+ palError = ERROR_INVALID_HANDLE;
+ goto done;
+ }
+ else if ( lpOverlapped )
+ {
+ ASSERT( "lpOverlapped is not NULL, as it should be.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_WRITE,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto done;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto done;
+ }
+
+ if (pLocalData->open_flags_deviceaccessonly == TRUE)
+ {
+ ERROR("File open for device access only\n");
+ palError = ERROR_ACCESS_DENIED;
+ goto done;
+ }
+
+ ifd = pLocalData->unix_fd;
+
+ //
+ // Inform the lock controller for this file (if any) of our intention
+ // to perform a write. (Note that pipes don't have lock controllers.)
+ //
+
+ if (NULL != pLocalData->pLockController)
+ {
+ /* Get the current file position to calculate the region to lock */
+ palError = InternalSetFilePointerForUnixFd(
+ ifd,
+ 0,
+ &writeOffsetStartHigh,
+ FILE_CURRENT,
+ &writeOffsetStartLow
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Failed to get the current file position\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+
+ palError = pLocalData->pLockController->GetTransactionLock(
+ pThread,
+ IFileLockController::WriteLock,
+ writeOffsetStartLow,
+ writeOffsetStartHigh,
+ nNumberOfBytesToWrite,
+ 0,
+ &pTransactionLock
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to obtain write transaction lock");
+ goto done;
+ }
+ }
+
+ //
+ // Release the data lock before performing the (possibly blocking)
+ // write call
+ //
+
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ pLocalDataLock = NULL;
+ pLocalData = NULL;
+
+#if WRITE_0_BYTES_HANGS_TTY
+ if( nNumberOfBytesToWrite == 0 && isatty(ifd) )
+ {
+ res = 0;
+ *lpNumberOfBytesWritten = 0;
+ goto done;
+ }
+#endif
+
+ res = write( ifd, lpBuffer, nNumberOfBytesToWrite );
+ TRACE("write() returns %d\n", res);
+
+ if ( res >= 0 )
+ {
+ *lpNumberOfBytesWritten = res;
+ }
+ else
+ {
+ palError = FILEGetLastErrorFromErrno();
+ }
+
+done:
+
+ if (NULL != pTransactionLock)
+ {
+ pTransactionLock->ReleaseLock();
+ }
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+
+/*++
+Function:
+ WriteFileW
+
+Note:
+ lpOverlapped always NULL.
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+WriteFile(
+ IN HANDLE hFile,
+ IN LPCVOID lpBuffer,
+ IN DWORD nNumberOfBytesToWrite,
+ OUT LPDWORD lpNumberOfBytesWritten,
+ IN LPOVERLAPPED lpOverlapped)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+
+ PERF_ENTRY(WriteFile);
+ ENTRY("WriteFile(hFile=%p, lpBuffer=%p, nToWrite=%u, lpWritten=%p, "
+ "lpOverlapped=%p)\n", hFile, lpBuffer, nNumberOfBytesToWrite,
+ lpNumberOfBytesWritten, lpOverlapped);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalWriteFile(
+ pThread,
+ hFile,
+ lpBuffer,
+ nNumberOfBytesToWrite,
+ lpNumberOfBytesWritten,
+ lpOverlapped
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("WriteFile returns BOOL %d\n", NO_ERROR == palError);
+ PERF_EXIT(WriteFile);
+ return NO_ERROR == palError;
+}
+
+PAL_ERROR
+CorUnix::InternalReadFile(
+ CPalThread *pThread,
+ HANDLE hFile,
+ LPVOID lpBuffer,
+ DWORD nNumberOfBytesToRead,
+ LPDWORD lpNumberOfBytesRead,
+ LPOVERLAPPED lpOverlapped
+ )
+{
+ PAL_ERROR palError = 0;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ IFileTransactionLock *pTransactionLock = NULL;
+ int ifd;
+
+ LONG readOffsetStartLow = 0, readOffsetStartHigh = 0;
+ int res;
+
+ if (NULL != lpNumberOfBytesRead)
+ {
+ //
+ // This must be set to 0 before any other error checking takes
+ // place, per MSDN
+ //
+
+ *lpNumberOfBytesRead = 0;
+ }
+ else
+ {
+ ERROR( "lpNumberOfBytesRead is NULL\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto done;
+ }
+ else if (NULL != lpOverlapped)
+ {
+ ASSERT( "lpOverlapped is not NULL, as it should be.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+ else if (NULL == lpBuffer)
+ {
+ ERROR( "Invalid parameter. (lpBuffer:%p)\n", lpBuffer);
+ palError = ERROR_NOACCESS;
+ goto done;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto done;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto done;
+ }
+
+ if (pLocalData->open_flags_deviceaccessonly == TRUE)
+ {
+ ERROR("File open for device access only\n");
+ palError = ERROR_ACCESS_DENIED;
+ goto done;
+ }
+
+ ifd = pLocalData->unix_fd;
+
+ //
+ // Inform the lock controller for this file (if any) of our intention
+ // to perform a read. (Note that pipes don't have lock controllers.)
+ //
+
+ if (NULL != pLocalData->pLockController)
+ {
+ /* Get the current file position to calculate the region to lock */
+ palError = InternalSetFilePointerForUnixFd(
+ ifd,
+ 0,
+ &readOffsetStartHigh,
+ FILE_CURRENT,
+ &readOffsetStartLow
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Failed to get the current file position\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+
+ palError = pLocalData->pLockController->GetTransactionLock(
+ pThread,
+ IFileLockController::ReadLock,
+ readOffsetStartLow,
+ readOffsetStartHigh,
+ nNumberOfBytesToRead,
+ 0,
+ &pTransactionLock
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to obtain read transaction lock");
+ goto done;
+ }
+ }
+
+ //
+ // Release the data lock before performing the (possibly blocking)
+ // read call
+ //
+
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ pLocalDataLock = NULL;
+ pLocalData = NULL;
+
+Read:
+ TRACE("Reading from file descriptor %d\n", ifd);
+ res = read(ifd, lpBuffer, nNumberOfBytesToRead);
+ TRACE("read() returns %d\n", res);
+
+ if (res >= 0)
+ {
+ *lpNumberOfBytesRead = res;
+ }
+ else if (errno == EINTR)
+ {
+ // Try to read again.
+ goto Read;
+ }
+ else
+ {
+ palError = FILEGetLastErrorFromErrno();
+ }
+
+done:
+
+ if (NULL != pTransactionLock)
+ {
+ pTransactionLock->ReleaseLock();
+ }
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ ReadFile
+
+Note:
+ lpOverlapped always NULL.
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+ReadFile(
+ IN HANDLE hFile,
+ OUT LPVOID lpBuffer,
+ IN DWORD nNumberOfBytesToRead,
+ OUT LPDWORD lpNumberOfBytesRead,
+ IN LPOVERLAPPED lpOverlapped)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+
+ PERF_ENTRY(ReadFile);
+ ENTRY("ReadFile(hFile=%p, lpBuffer=%p, nToRead=%u, "
+ "lpRead=%p, lpOverlapped=%p)\n",
+ hFile, lpBuffer, nNumberOfBytesToRead,
+ lpNumberOfBytesRead, lpOverlapped);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalReadFile(
+ pThread,
+ hFile,
+ lpBuffer,
+ nNumberOfBytesToRead,
+ lpNumberOfBytesRead,
+ lpOverlapped
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("ReadFile returns BOOL %d\n", NO_ERROR == palError);
+ PERF_EXIT(ReadFile);
+ return NO_ERROR == palError;
+}
+
+
+/*++
+Function:
+ GetStdHandle
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+GetStdHandle(
+ IN DWORD nStdHandle)
+{
+ CPalThread *pThread;
+ HANDLE hRet = INVALID_HANDLE_VALUE;
+
+ PERF_ENTRY(GetStdHandle);
+ ENTRY("GetStdHandle(nStdHandle=%#x)\n", nStdHandle);
+
+ pThread = InternalGetCurrentThread();
+ switch( nStdHandle )
+ {
+ case STD_INPUT_HANDLE:
+ hRet = pStdIn;
+ break;
+ case STD_OUTPUT_HANDLE:
+ hRet = pStdOut;
+ break;
+ case STD_ERROR_HANDLE:
+ hRet = pStdErr;
+ break;
+ default:
+ ERROR("nStdHandle is invalid\n");
+ pThread->SetLastError(ERROR_INVALID_PARAMETER);
+ break;
+ }
+
+ LOGEXIT("GetStdHandle returns HANDLE %p\n", hRet);
+ PERF_EXIT(GetStdHandle);
+ return hRet;
+}
+
+PAL_ERROR
+CorUnix::InternalSetEndOfFile(
+ CPalThread *pThread,
+ HANDLE hFile
+ )
+{
+ PAL_ERROR palError = 0;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+
+ off_t curr = 0;
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalSetEndOfFileExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_WRITE,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalSetEndOfFileExit;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalSetEndOfFileExit;
+ }
+
+ if (pLocalData->open_flags_deviceaccessonly == TRUE)
+ {
+ ERROR("File open for device access only\n");
+ palError = ERROR_ACCESS_DENIED;
+ goto InternalSetEndOfFileExit;
+ }
+
+ curr = lseek(pLocalData->unix_fd, 0, SEEK_CUR);
+
+ TRACE("current file pointer offset is %u\n", curr);
+ if ( curr < 0 )
+ {
+ ERROR("lseek returned %ld\n", curr);
+ palError = FILEGetLastErrorFromErrno();
+ goto InternalSetEndOfFileExit;
+ }
+
+#if SIZEOF_OFF_T > 4
+#if !HAVE_FTRUNCATE_LARGE_LENGTH_SUPPORT
+ // ftruncate will return the wrong value for some large lengths.
+ // We'll short-circuit the process and simply return failure for
+ // the set of values that covers those cases, all of which would
+ // have failed anyway on any standard-sized hard drive.
+ if (curr >= 0xFFFFFFFF000ULL)
+ {
+ ERROR("Skipping ftruncate because the offset is too large\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalSetEndOfFileExit;
+ }
+#endif // !HAVE_FTRUNCATE_LARGE_LENGTH_SUPPORT
+#endif // SIZEOF_OFF_T
+
+#if HAS_FTRUNCATE_LENGTH_ISSUE
+ // Perform an additional check to make sure that there's likely to be enough free space to satisfy the
+ // request. Do this because it's been observed on Mac OSX that ftruncate can return failure but still
+ // extend the file to consume the remainder of free space.
+ //
+ struct statfs sFileSystemStats;
+ off_t cbFreeSpace;
+ if (fstatfs(pLocalData->unix_fd, &sFileSystemStats) != 0)
+ {
+ ERROR("fstatfs failed\n");
+ palError = FILEGetLastErrorFromErrno();
+ goto InternalSetEndOfFileExit;
+ }
+
+ // Free space is free blocks times the size of each block in bytes.
+ cbFreeSpace = (off_t)sFileSystemStats.f_bavail * (off_t)sFileSystemStats.f_bsize;
+
+ if (curr > cbFreeSpace)
+ {
+ ERROR("Not enough disk space for ftruncate\n");
+ palError = ERROR_DISK_FULL;
+ goto InternalSetEndOfFileExit;
+ }
+#endif // HAS_FTRUNCATE_LENGTH_ISSUE
+
+ if ( ftruncate(pLocalData->unix_fd, curr) != 0 )
+ {
+ ERROR("ftruncate failed\n");
+ if ( errno == EACCES )
+ {
+ ERROR("file may not be writable\n");
+ }
+ palError = FILEGetLastErrorFromErrno();
+ goto InternalSetEndOfFileExit;
+ }
+
+
+InternalSetEndOfFileExit:
+
+ // Windows starts returning ERROR_INVALID_PARAMETER at an arbitrary file size (~16TB). The file system
+ // underneath us may be able to support larger and it would be a shame to prevent that. As a compromise,
+ // if the operation fails and the file size was above the Windows limit map ERROR_DISK_FULL to
+ // ERROR_INVALID_PARAMETER.
+ // curr has been checked to be positive after getting the value from lseek. The following cast is put to
+ // suppress the compilation warning.
+ if (palError == ERROR_DISK_FULL && (static_cast<UINT64>(curr) > 0x00000fffffff0000ULL ) )
+ palError = ERROR_INVALID_PARAMETER;
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+
+
+/*++
+Function:
+ SetEndOfFile
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+SetEndOfFile(
+ IN HANDLE hFile)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread;;
+
+ PERF_ENTRY(SetEndOfFile);
+ ENTRY("SetEndOfFile(hFile=%p)\n", hFile);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalSetEndOfFile(
+ pThread,
+ hFile
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("SetEndOfFile returns BOOL %d\n", NO_ERROR == palError);
+ PERF_EXIT(SetEndOfFile);
+ return NO_ERROR == palError;
+}
+
+//
+// We need to break out the actual mechanics of setting the file pointer
+// on the unix FD for InternalReadFile and InternalWriteFile, as they
+// need to call this routine in order to determine the value of the
+// current file pointer when computing the scope of their transaction
+// lock. If we didn't break out this logic we'd end up referencing the file
+// handle multiple times, and, in the process, would attempt to recursively
+// obtain the local process data lock for the underlying file object.
+//
+
+PAL_ERROR
+InternalSetFilePointerForUnixFd(
+ int iUnixFd,
+ LONG lDistanceToMove,
+ PLONG lpDistanceToMoveHigh,
+ DWORD dwMoveMethod,
+ PLONG lpNewFilePointerLow
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ int seek_whence = 0;
+ __int64 seek_offset = 0LL;
+ __int64 seek_res = 0LL;
+ off_t old_offset;
+
+ switch( dwMoveMethod )
+ {
+ case FILE_BEGIN:
+ seek_whence = SEEK_SET;
+ break;
+ case FILE_CURRENT:
+ seek_whence = SEEK_CUR;
+ break;
+ case FILE_END:
+ seek_whence = SEEK_END;
+ break;
+ default:
+ ERROR("dwMoveMethod = %d is invalid\n", dwMoveMethod);
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ //
+ // According to MSDN, if lpDistanceToMoveHigh is not null,
+ // lDistanceToMove is treated as unsigned;
+ // it is treated as signed otherwise
+ //
+
+ if ( lpDistanceToMoveHigh )
+ {
+ /* set the high 32 bits of the offset */
+ seek_offset = ((__int64)*lpDistanceToMoveHigh << 32);
+
+ /* set the low 32 bits */
+ /* cast to unsigned long to avoid sign extension */
+ seek_offset |= (ULONG) lDistanceToMove;
+ }
+ else
+ {
+ seek_offset |= lDistanceToMove;
+ }
+
+ /* store the current position, in case the lseek moves the pointer
+ before the beginning of the file */
+ old_offset = lseek(iUnixFd, 0, SEEK_CUR);
+ if (old_offset == -1)
+ {
+ ERROR("lseek(fd,0,SEEK_CUR) failed errno:%d (%s)\n",
+ errno, strerror(errno));
+ palError = ERROR_ACCESS_DENIED;
+ goto done;
+ }
+
+ // Check to see if we're going to seek to a negative offset.
+ // If we're seeking from the beginning or the current mark,
+ // this is simple.
+ if ((seek_whence == SEEK_SET && seek_offset < 0) ||
+ (seek_whence == SEEK_CUR && seek_offset + old_offset < 0))
+ {
+ palError = ERROR_NEGATIVE_SEEK;
+ goto done;
+ }
+ else if (seek_whence == SEEK_END && seek_offset < 0)
+ {
+ // We need to determine if we're seeking past the
+ // beginning of the file, but we don't want to adjust
+ // the mark in the process. stat is the only way to
+ // do that.
+ struct stat fileData;
+ int result;
+
+ result = fstat(iUnixFd, &fileData);
+ if (result == -1)
+ {
+ // It's a bad fd. This shouldn't happen because
+ // we've already called lseek on it, but you
+ // never know. This is the best we can do.
+ palError = ERROR_ACCESS_DENIED;
+ goto done;
+ }
+ if (fileData.st_size < -seek_offset)
+ {
+ // Seeking past the beginning.
+ palError = ERROR_NEGATIVE_SEEK;
+ goto done;
+ }
+ }
+
+ seek_res = (__int64)lseek( iUnixFd,
+ seek_offset,
+ seek_whence );
+ if ( seek_res < 0 )
+ {
+ /* lseek() returns -1 on error, but also can seek to negative
+ file offsets, so -1 can also indicate a successful seek to offset
+ -1. Win32 doesn't allow negative file offsets, so either case
+ is an error. */
+ ERROR("lseek failed errno:%d (%s)\n", errno, strerror(errno));
+ lseek(iUnixFd, old_offset, SEEK_SET);
+ palError = ERROR_ACCESS_DENIED;
+ }
+ else
+ {
+ /* store high-order DWORD */
+ if ( lpDistanceToMoveHigh )
+ *lpDistanceToMoveHigh = (DWORD)(seek_res >> 32);
+
+ /* return low-order DWORD of seek result */
+ *lpNewFilePointerLow = (DWORD)seek_res;
+ }
+
+done:
+
+ return palError;
+}
+
+PAL_ERROR
+CorUnix::InternalSetFilePointer(
+ CPalThread *pThread,
+ HANDLE hFile,
+ LONG lDistanceToMove,
+ PLONG lpDistanceToMoveHigh,
+ DWORD dwMoveMethod,
+ PLONG lpNewFilePointerLow
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalSetFilePointerExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalSetFilePointerExit;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalSetFilePointerExit;
+ }
+
+ palError = InternalSetFilePointerForUnixFd(
+ pLocalData->unix_fd,
+ lDistanceToMove,
+ lpDistanceToMoveHigh,
+ dwMoveMethod,
+ lpNewFilePointerLow
+ );
+
+InternalSetFilePointerExit:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ SetFilePointer
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+SetFilePointer(
+ IN HANDLE hFile,
+ IN LONG lDistanceToMove,
+ IN PLONG lpDistanceToMoveHigh,
+ IN DWORD dwMoveMethod)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread;
+ LONG lNewFilePointerLow = 0;
+
+ PERF_ENTRY(SetFilePointer);
+ ENTRY("SetFilePointer(hFile=%p, lDistance=%d, lpDistanceHigh=%p, "
+ "dwMoveMethod=%#x)\n", hFile, lDistanceToMove,
+ lpDistanceToMoveHigh, dwMoveMethod);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalSetFilePointer(
+ pThread,
+ hFile,
+ lDistanceToMove,
+ lpDistanceToMoveHigh,
+ dwMoveMethod,
+ &lNewFilePointerLow
+ );
+
+ if (NO_ERROR != palError)
+ {
+ lNewFilePointerLow = INVALID_SET_FILE_POINTER;
+ }
+
+ /* This function must always call SetLastError - even if successful.
+ If we seek to a value greater than 2^32 - 1, we will effectively be
+ returning a negative value from this function. Now, let's say that
+ returned value is -1. Furthermore, assume that win32error has been
+ set before even entering this function. Then, when this function
+ returns to SetFilePointer in win32native.cs, it will have returned
+ -1 and win32error will have been set, which will cause an error to be
+ returned. Since -1 may not be an error in this case and since we
+ can't assume that the win32error is related to SetFilePointer,
+ we need to always call SetLastError here. That way, if this function
+ succeeds, SetFilePointer in win32native won't mistakenly determine
+ that it failed. */
+ pThread->SetLastError(palError);
+
+ LOGEXIT("SetFilePointer returns DWORD %#x\n", lNewFilePointerLow);
+ PERF_EXIT(SetFilePointer);
+ return lNewFilePointerLow;
+}
+
+/*++
+Function:
+ SetFilePointerEx
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+SetFilePointerEx(
+ IN HANDLE hFile,
+ IN LARGE_INTEGER liDistanceToMove,
+ OUT PLARGE_INTEGER lpNewFilePointer,
+ IN DWORD dwMoveMethod)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread;
+ BOOL Ret = FALSE;
+
+ PERF_ENTRY(SetFilePointerEx);
+ ENTRY("SetFilePointerEx(hFile=%p, liDistanceToMove=0x%llx, "
+ "lpNewFilePointer=%p (0x%llx), dwMoveMethod=0x%x)\n", hFile,
+ liDistanceToMove.QuadPart, lpNewFilePointer,
+ (lpNewFilePointer) ? (*lpNewFilePointer).QuadPart : 0, dwMoveMethod);
+
+ LONG lDistanceToMove;
+ lDistanceToMove = (LONG)liDistanceToMove.u.LowPart;
+ LONG lDistanceToMoveHigh;
+ lDistanceToMoveHigh = liDistanceToMove.u.HighPart;
+
+ LONG lNewFilePointerLow = 0;
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalSetFilePointer(
+ pThread,
+ hFile,
+ lDistanceToMove,
+ &lDistanceToMoveHigh,
+ dwMoveMethod,
+ &lNewFilePointerLow
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+ else
+ {
+ if (lpNewFilePointer != NULL)
+ {
+ lpNewFilePointer->u.LowPart = (DWORD)lNewFilePointerLow;
+ lpNewFilePointer->u.HighPart = (DWORD)lDistanceToMoveHigh;
+ }
+ Ret = TRUE;
+ }
+
+ LOGEXIT("SetFilePointerEx returns BOOL %d\n", Ret);
+ PERF_EXIT(SetFilePointerEx);
+ return Ret;
+}
+
+PAL_ERROR
+CorUnix::InternalGetFileSize(
+ CPalThread *pThread,
+ HANDLE hFile,
+ DWORD *pdwFileSizeLow,
+ DWORD *pdwFileSizeHigh
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+
+ struct stat stat_data;
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalGetFileSizeExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalGetFileSizeExit;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalGetFileSizeExit;
+ }
+
+ if (fstat(pLocalData->unix_fd, &stat_data) != 0)
+ {
+ ERROR("fstat failed of file descriptor %d\n", pLocalData->unix_fd);
+ palError = FILEGetLastErrorFromErrno();
+ goto InternalGetFileSizeExit;
+ }
+
+ *pdwFileSizeLow = (DWORD)stat_data.st_size;
+
+ if (NULL != pdwFileSizeHigh)
+ {
+#if SIZEOF_OFF_T > 4
+ *pdwFileSizeHigh = (DWORD)(stat_data.st_size >> 32);
+#else
+ *pdwFileSizeHigh = 0;
+#endif
+ }
+
+InternalGetFileSizeExit:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ GetFileSize
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+GetFileSize(
+ IN HANDLE hFile,
+ OUT LPDWORD lpFileSizeHigh)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread;
+ DWORD dwFileSizeLow;
+
+ PERF_ENTRY(GetFileSize);
+ ENTRY("GetFileSize(hFile=%p, lpFileSizeHigh=%p)\n", hFile, lpFileSizeHigh);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalGetFileSize(
+ pThread,
+ hFile,
+ &dwFileSizeLow,
+ lpFileSizeHigh
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ dwFileSizeLow = INVALID_FILE_SIZE;
+ }
+
+ LOGEXIT("GetFileSize returns DWORD %u\n", dwFileSizeLow);
+ PERF_EXIT(GetFileSize);
+ return dwFileSizeLow;
+}
+
+/*++
+Function:
+GetFileSizeEx
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI GetFileSizeEx(
+IN HANDLE hFile,
+OUT PLARGE_INTEGER lpFileSize)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread;
+ DWORD dwFileSizeHigh;
+ DWORD dwFileSizeLow;
+
+ PERF_ENTRY(GetFileSizeEx);
+ ENTRY("GetFileSizeEx(hFile=%p, lpFileSize=%p)\n", hFile, lpFileSize);
+
+ pThread = InternalGetCurrentThread();
+
+ if (lpFileSize != NULL)
+ {
+ palError = InternalGetFileSize(
+ pThread,
+ hFile,
+ &dwFileSizeLow,
+ &dwFileSizeHigh
+ );
+
+ lpFileSize->u.LowPart = dwFileSizeLow;
+ lpFileSize->u.HighPart = dwFileSizeHigh;
+ }
+ else
+ {
+ palError = ERROR_INVALID_PARAMETER;
+ }
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("GetFileSizeEx returns BOOL %d\n", NO_ERROR == palError);
+ PERF_EXIT(GetFileSizeEx);
+ return NO_ERROR == palError;
+}
+
+PAL_ERROR
+CorUnix::InternalFlushFileBuffers(
+ CPalThread *pThread,
+ HANDLE hFile
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalFlushFileBuffersExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_WRITE,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalFlushFileBuffersExit;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalFlushFileBuffersExit;
+ }
+
+ if (pLocalData->open_flags_deviceaccessonly == TRUE)
+ {
+ ERROR("File open for device access only\n");
+ palError = ERROR_ACCESS_DENIED;
+ goto InternalFlushFileBuffersExit;
+ }
+
+#if HAVE_FSYNC || defined(__APPLE__)
+ do
+ {
+
+#if defined(__APPLE__)
+ if (fcntl(pLocalData->unix_fd, F_FULLFSYNC) != -1)
+ break;
+#else // __APPLE__
+ if (fsync(pLocalData->unix_fd) == 0)
+ break;
+#endif // __APPLE__
+
+ switch (errno)
+ {
+ case EINTR:
+ // Execution was interrupted by a signal, so restart.
+ TRACE("fsync(%d) was interrupted. Restarting\n", pLocalData->unix_fd);
+ break;
+
+ default:
+ palError = FILEGetLastErrorFromErrno();
+ WARN("fsync(%d) failed with error %d\n", pLocalData->unix_fd, errno);
+ break;
+ }
+ } while (NO_ERROR == palError);
+#else // HAVE_FSYNC
+ /* flush all buffers out to disk - there is no way to flush
+ an individual file descriptor's buffers out. */
+ sync();
+#endif // HAVE_FSYNC else
+
+
+InternalFlushFileBuffersExit:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+
+/*++
+Function:
+ FlushFileBuffers
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+FlushFileBuffers(
+ IN HANDLE hFile)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread;
+
+ PERF_ENTRY(FlushFileBuffers);
+ ENTRY("FlushFileBuffers(hFile=%p)\n", hFile);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalFlushFileBuffers(
+ pThread,
+ hFile
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("FlushFileBuffers returns BOOL %d\n", NO_ERROR == palError);
+ PERF_EXIT(FlushFileBuffers);
+ return NO_ERROR == palError;
+}
+
+PAL_ERROR
+CorUnix::InternalGetFileType(
+ CPalThread *pThread,
+ HANDLE hFile,
+ DWORD *pdwFileType
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+
+ struct stat stat_data;
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalGetFileTypeExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalGetFileTypeExit;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalGetFileTypeExit;
+ }
+
+ if (pLocalData->open_flags_deviceaccessonly == TRUE)
+ {
+ ERROR("File open for device access only\n");
+ palError = ERROR_ACCESS_DENIED;
+ goto InternalGetFileTypeExit;
+ }
+
+ if (fstat(pLocalData->unix_fd, &stat_data) != 0)
+ {
+ ERROR("fstat failed of file descriptor %d\n", pLocalData->unix_fd);
+ palError = FILEGetLastErrorFromErrno();
+ goto InternalGetFileTypeExit;
+ }
+
+ TRACE("st_mode & S_IFMT = %#x\n", stat_data.st_mode & S_IFMT);
+ if (S_ISREG(stat_data.st_mode) || S_ISDIR(stat_data.st_mode))
+ {
+ *pdwFileType = FILE_TYPE_DISK;
+ }
+ else if (S_ISCHR(stat_data.st_mode))
+ {
+ *pdwFileType = FILE_TYPE_CHAR;
+ }
+ else if (S_ISFIFO(stat_data.st_mode))
+ {
+ *pdwFileType = FILE_TYPE_PIPE;
+ }
+ else
+ {
+ *pdwFileType = FILE_TYPE_UNKNOWN;
+ }
+
+
+InternalGetFileTypeExit:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+
+}
+
+
+/*++
+Function:
+ GetFileType
+
+See MSDN doc.
+
+--*/
+DWORD
+PALAPI
+GetFileType(
+ IN HANDLE hFile)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread;
+ DWORD dwFileType;
+
+ PERF_ENTRY(GetFileType);
+ ENTRY("GetFileType(hFile=%p)\n", hFile);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalGetFileType(
+ pThread,
+ hFile,
+ &dwFileType
+ );
+
+ if (NO_ERROR != palError)
+ {
+ dwFileType = FILE_TYPE_UNKNOWN;
+ pThread->SetLastError(palError);
+ }
+ else if (FILE_TYPE_UNKNOWN == dwFileType)
+ {
+ pThread->SetLastError(palError);
+ }
+
+
+ LOGEXIT("GetFileType returns DWORD %#x\n", dwFileType);
+ PERF_EXIT(GetFileType);
+ return dwFileType;
+}
+
+#define ENSURE_UNIQUE_NOT_ZERO \
+ if ( uUniqueSeed == 0 ) \
+ {\
+ uUniqueSeed++;\
+ }
+
+/*++
+ Function:
+ GetTempFileNameA
+
+uUnique is always 0.
+ --*/
+const int MAX_PREFIX = 3;
+const int MAX_SEEDSIZE = 8; /* length of "unique portion of
+ the string, plus extension(FFFF.TMP). */
+static USHORT uUniqueSeed = 0;
+static BOOL IsInitialized = FALSE;
+
+UINT
+PALAPI
+GetTempFileNameA(
+ IN LPCSTR lpPathName,
+ IN LPCSTR lpPrefixString,
+ IN UINT uUnique,
+ OUT LPSTR lpTempFileName)
+{
+ CPalThread *pThread;
+ CHAR * full_name;
+ PathCharString full_namePS;
+ int length;
+ CHAR * file_template;
+ PathCharString file_templatePS;
+ CHAR chLastPathNameChar;
+
+ HANDLE hTempFile;
+ UINT uRet = 0;
+ DWORD dwError;
+ USHORT uLoopCounter = 0;
+
+ PERF_ENTRY(GetTempFileNameA);
+ ENTRY("GetTempFileNameA(lpPathName=%p (%s), lpPrefixString=%p (%s), uUnique=%u, "
+ "lpTempFileName=%p)\n", lpPathName?lpPathName:"NULL", lpPathName?lpPathName:"NULL",
+ lpPrefixString?lpPrefixString:"NULL",
+ lpPrefixString?lpPrefixString:"NULL", uUnique,
+ lpTempFileName?lpTempFileName:"NULL");
+
+ pThread = InternalGetCurrentThread();
+ if ( !IsInitialized )
+ {
+ uUniqueSeed = (USHORT)( time( NULL ) );
+
+ /* On the off chance 0 is returned.
+ 0 being the error return code. */
+ ENSURE_UNIQUE_NOT_ZERO
+ IsInitialized = TRUE;
+ }
+
+ if ( !lpPathName || *lpPathName == '\0' )
+ {
+ pThread->SetLastError( ERROR_DIRECTORY );
+ goto done;
+ }
+
+ if ( NULL == lpTempFileName )
+ {
+ ERROR( "lpTempFileName cannot be NULL\n" );
+ pThread->SetLastError( ERROR_INVALID_PARAMETER );
+ goto done;
+ }
+
+ if ( strlen( lpPathName ) + MAX_SEEDSIZE + MAX_PREFIX >= MAX_LONGPATH )
+ {
+ WARN( "File names larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH );
+ pThread->SetLastError( ERROR_FILENAME_EXCED_RANGE );
+ goto done;
+ }
+
+ length = strlen(lpPathName) + MAX_SEEDSIZE + MAX_PREFIX + 10;
+ file_template = file_templatePS.OpenStringBuffer(length);
+ if (NULL == file_template)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ *file_template = '\0';
+ strcat_s( file_template, file_templatePS.GetSizeOf(), lpPathName );
+ file_templatePS.CloseBuffer(length);
+
+ chLastPathNameChar = file_template[strlen(file_template)-1];
+ if (chLastPathNameChar != '\\' && chLastPathNameChar != '/')
+ {
+ strcat_s( file_template, file_templatePS.GetSizeOf(), "\\" );
+ }
+
+ if ( lpPrefixString )
+ {
+ strncat_s( file_template, file_templatePS.GetSizeOf(), lpPrefixString, MAX_PREFIX );
+ }
+ FILEDosToUnixPathA( file_template );
+ strncat_s( file_template, file_templatePS.GetSizeOf(), "%.4x.TMP", MAX_SEEDSIZE );
+
+ /* Create the file. */
+ dwError = GetLastError();
+ pThread->SetLastError( NOERROR );
+
+ length = strlen(file_template) + MAX_SEEDSIZE + MAX_PREFIX;
+ full_name = full_namePS.OpenStringBuffer(length);
+ if (NULL == full_name)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ sprintf_s( full_name, full_namePS.GetSizeOf(), file_template, (0 == uUnique) ? uUniqueSeed : uUnique);
+ full_namePS.CloseBuffer(length);
+
+ hTempFile = CreateFileA( full_name, GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, CREATE_NEW, 0, NULL );
+
+ if (uUnique == 0)
+ {
+ /* The USHORT will overflow back to 0 if we go past
+ 65536 files, so break the loop after 65536 iterations.
+ If the CreateFile call was not successful within that
+ number of iterations, then there are no temp file names
+ left for that directory. */
+ while ( ERROR_PATH_NOT_FOUND != GetLastError() &&
+ INVALID_HANDLE_VALUE == hTempFile && uLoopCounter < 0xFFFF )
+ {
+ uUniqueSeed++;
+ ENSURE_UNIQUE_NOT_ZERO;
+
+ pThread->SetLastError( NOERROR );
+ sprintf_s( full_name, full_namePS.GetSizeOf(), file_template, uUniqueSeed );
+ hTempFile = CreateFileA( full_name, GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, CREATE_NEW, 0, NULL );
+ uLoopCounter++;
+
+ }
+ }
+
+ /* Reset the error code.*/
+ if ( NOERROR == GetLastError() )
+ {
+ pThread->SetLastError( dwError );
+ }
+
+ /* Windows sets ERROR_FILE_EXISTS,if there
+ are no available temp files. */
+ if ( INVALID_HANDLE_VALUE != hTempFile )
+ {
+ if (0 == uUnique)
+ {
+ uRet = uUniqueSeed;
+ uUniqueSeed++;
+ ENSURE_UNIQUE_NOT_ZERO;
+ }
+ else
+ {
+ uRet = uUnique;
+ }
+
+ if ( CloseHandle( hTempFile ) )
+ {
+ if (strcpy_s( lpTempFileName, MAX_LONGPATH, full_name ) != SAFECRT_SUCCESS)
+ {
+ ERROR( "strcpy_s failed!\n");
+ pThread->SetLastError( ERROR_FILENAME_EXCED_RANGE );
+ *lpTempFileName = '\0';
+ uRet = 0;
+ }
+ }
+ else
+ {
+ ASSERT( "Unable to close the handle %p\n", hTempFile );
+ pThread->SetLastError( ERROR_INTERNAL_ERROR );
+ *lpTempFileName = '\0';
+ uRet = 0;
+ }
+ }
+ else if ( INVALID_HANDLE_VALUE == hTempFile && uLoopCounter < 0xFFFF )
+ {
+ ERROR( "Unable to create temp file. \n" );
+ uRet = 0;
+
+ if ( ERROR_PATH_NOT_FOUND == GetLastError() )
+ {
+ /* CreateFile failed because it could not
+ find the path. */
+ pThread->SetLastError( ERROR_DIRECTORY );
+ } /* else use the lasterror value from CreateFileA */
+ }
+ else
+ {
+ TRACE( "65535 files already exist in the directory. "
+ "No temp files available for creation.\n" );
+ pThread->SetLastError( ERROR_FILE_EXISTS );
+ }
+
+done:
+ LOGEXIT("GetTempFileNameA returns UINT %u\n", uRet);
+ PERF_EXIT(GetTempFileNameA);
+ return uRet;
+
+}
+
+/*++
+Function:
+ GetTempFileNameW
+
+uUnique is always 0.
+--*/
+UINT
+PALAPI
+GetTempFileNameW(
+ IN LPCWSTR lpPathName,
+ IN LPCWSTR lpPrefixString,
+ IN UINT uUnique,
+ OUT LPWSTR lpTempFileName)
+{
+ CPalThread *pThread;
+ INT path_size = 0;
+ INT prefix_size = 0;
+ CHAR * full_name;
+ CHAR * prefix_string;
+ CHAR * tempfile_name;
+ PathCharString full_namePS, prefix_stringPS;
+ INT length = 0;
+ UINT uRet;
+
+ PERF_ENTRY(GetTempFileNameW);
+ ENTRY("GetTempFileNameW(lpPathName=%p (%S), lpPrefixString=%p (%S), uUnique=%u, "
+ "lpTempFileName=%p)\n", lpPathName?lpPathName:W16_NULLSTRING, lpPathName?lpPathName:W16_NULLSTRING,
+ lpPrefixString?lpPrefixString:W16_NULLSTRING,
+ lpPrefixString?lpPrefixString:W16_NULLSTRING,uUnique, lpTempFileName);
+
+ pThread = InternalGetCurrentThread();
+ /* Sanity checks. */
+ if ( !lpPathName || *lpPathName == '\0' )
+ {
+ pThread->SetLastError( ERROR_DIRECTORY );
+ uRet = 0;
+ goto done;
+ }
+
+ length = (PAL_wcslen(lpPathName)+1) * MaxWCharToAcpLengthFactor;
+ full_name = full_namePS.OpenStringBuffer(length);
+ if (NULL == full_name)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ uRet = 0;
+ goto done;
+ }
+ path_size = WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, full_name,
+ length, NULL, NULL );
+
+ if( path_size == 0 )
+ {
+ full_namePS.CloseBuffer(0);
+ DWORD dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ uRet = 0;
+ goto done;
+ }
+
+ full_namePS.CloseBuffer(path_size - 1);
+
+ if (lpPrefixString != NULL)
+ {
+ length = (PAL_wcslen(lpPrefixString)+1) * MaxWCharToAcpLengthFactor;
+ prefix_string = prefix_stringPS.OpenStringBuffer(length);
+ if (NULL == prefix_string)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ uRet = 0;
+ goto done;
+ }
+ prefix_size = WideCharToMultiByte( CP_ACP, 0, lpPrefixString, -1,
+ prefix_string,
+ MAX_LONGPATH - path_size - MAX_SEEDSIZE,
+ NULL, NULL );
+
+ if( prefix_size == 0 )
+ {
+ prefix_stringPS.CloseBuffer(0);
+ DWORD dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ uRet = 0;
+ goto done;
+ }
+ prefix_stringPS.CloseBuffer(prefix_size - 1);
+ }
+
+ tempfile_name = (char*)InternalMalloc(MAX_LONGPATH);
+ if (tempfile_name == NULL)
+ {
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ uRet = 0;
+ goto done;
+ }
+
+ uRet = GetTempFileNameA(full_name,
+ (lpPrefixString == NULL) ? NULL : prefix_string,
+ 0, tempfile_name);
+ if (uRet)
+ {
+ path_size = MultiByteToWideChar( CP_ACP, 0, tempfile_name, -1,
+ lpTempFileName, MAX_LONGPATH );
+
+ free(tempfile_name);
+ tempfile_name = NULL;
+ if (!path_size)
+ {
+ DWORD dwLastError = GetLastError();
+ if (dwLastError == ERROR_INSUFFICIENT_BUFFER)
+ {
+ WARN("File names larger than MAX_PATH_FNAME (%d)! \n", MAX_LONGPATH);
+ dwLastError = ERROR_FILENAME_EXCED_RANGE;
+ }
+ else
+ {
+ ASSERT("MultiByteToWideChar failure! error is %d", dwLastError);
+ dwLastError = ERROR_INTERNAL_ERROR;
+ }
+ pThread->SetLastError(dwLastError);
+ uRet = 0;
+ }
+ }
+
+done:
+ LOGEXIT("GetTempFileNameW returns UINT %u\n", uRet);
+ PERF_EXIT(GetTempFileNameW);
+ return uRet;
+}
+
+/*++
+Function:
+ FILEGetLastErrorFromErrno
+
+Convert errno into the appropriate win32 error and return it.
+--*/
+DWORD FILEGetLastErrorFromErrno( void )
+{
+ DWORD dwRet;
+
+ switch(errno)
+ {
+ case 0:
+ dwRet = ERROR_SUCCESS;
+ break;
+ case ENAMETOOLONG:
+ dwRet = ERROR_FILENAME_EXCED_RANGE;
+ break;
+ case ENOTDIR:
+ dwRet = ERROR_PATH_NOT_FOUND;
+ break;
+ case ENOENT:
+ dwRet = ERROR_FILE_NOT_FOUND;
+ break;
+ case EACCES:
+ case EPERM:
+ case EROFS:
+ case EISDIR:
+ dwRet = ERROR_ACCESS_DENIED;
+ break;
+ case EEXIST:
+ dwRet = ERROR_ALREADY_EXISTS;
+ break;
+#if !defined(_AIX)
+ // ENOTEMPTY is the same as EEXIST on AIX. Meaningful when involving directory operations
+ case ENOTEMPTY:
+ dwRet = ERROR_DIR_NOT_EMPTY;
+ break;
+#endif
+ case EBADF:
+ dwRet = ERROR_INVALID_HANDLE;
+ break;
+ case ENOMEM:
+ dwRet = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ case EBUSY:
+ dwRet = ERROR_BUSY;
+ break;
+ case ENOSPC:
+ case EDQUOT:
+ dwRet = ERROR_DISK_FULL;
+ break;
+ case ELOOP:
+ dwRet = ERROR_BAD_PATHNAME;
+ break;
+ case EIO:
+ dwRet = ERROR_WRITE_FAULT;
+ break;
+ case ERANGE:
+ dwRet = ERROR_BAD_PATHNAME;
+ break;
+ default:
+ ERROR("unexpected errno %d (%s); returning ERROR_GEN_FAILURE\n",
+ errno, strerror(errno));
+ dwRet = ERROR_GEN_FAILURE;
+ }
+
+ TRACE("errno = %d (%s), LastError = %d\n", errno, strerror(errno), dwRet);
+
+ return dwRet;
+}
+
+/*++
+Function:
+ DIRGetLastErrorFromErrno
+
+Convert errno into the appropriate win32 error and return it.
+--*/
+DWORD DIRGetLastErrorFromErrno( void )
+{
+ if (errno == ENOENT)
+ return ERROR_PATH_NOT_FOUND;
+ else
+ return FILEGetLastErrorFromErrno();
+}
+
+
+/*++
+Function:
+ CopyFileA
+
+See MSDN doc.
+
+Notes:
+ There are several (most) error paths here that do not call SetLastError().
+This is because we know that CreateFile, ReadFile, and WriteFile will do so,
+and will have a much better idea of the specific error.
+--*/
+BOOL
+PALAPI
+CopyFileA(
+ IN LPCSTR lpExistingFileName,
+ IN LPCSTR lpNewFileName,
+ IN BOOL bFailIfExists)
+{
+ CPalThread *pThread;
+ HANDLE hSource = INVALID_HANDLE_VALUE;
+ HANDLE hDest = INVALID_HANDLE_VALUE;
+ DWORD dwDestCreationMode;
+ BOOL bGood = FALSE;
+ DWORD dwSrcFileAttributes;
+ struct stat SrcFileStats;
+
+ LPSTR lpUnixPath = NULL;
+ const int buffer_size = 16*1024;
+ char *buffer = (char*)alloca(buffer_size);
+ DWORD bytes_read;
+ DWORD bytes_written;
+ int permissions;
+
+
+ PERF_ENTRY(CopyFileA);
+ ENTRY("CopyFileA(lpExistingFileName=%p (%s), lpNewFileName=%p (%s), bFailIfExists=%d)\n",
+ lpExistingFileName?lpExistingFileName:"NULL",
+ lpExistingFileName?lpExistingFileName:"NULL",
+ lpNewFileName?lpNewFileName:"NULL",
+ lpNewFileName?lpNewFileName:"NULL", bFailIfExists);
+
+ pThread = InternalGetCurrentThread();
+ if ( bFailIfExists )
+ {
+ dwDestCreationMode = CREATE_NEW;
+ }
+ else
+ {
+ dwDestCreationMode = CREATE_ALWAYS;
+ }
+
+ hSource = CreateFileA( lpExistingFileName,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL );
+
+ if ( hSource == INVALID_HANDLE_VALUE )
+ {
+ ERROR("CreateFileA failed for %s\n", lpExistingFileName);
+ goto done;
+ }
+
+ /* Need to preserve the file attributes */
+ dwSrcFileAttributes = GetFileAttributes(lpExistingFileName);
+ if (dwSrcFileAttributes == 0xffffffff)
+ {
+ ERROR("GetFileAttributes failed for %s\n", lpExistingFileName);
+ goto done;
+ }
+
+ /* Need to preserve the owner/group and chmod() flags */
+ lpUnixPath = strdup(lpExistingFileName);
+ if ( lpUnixPath == NULL )
+ {
+ ERROR("strdup() failed\n");
+ pThread->SetLastError(FILEGetLastErrorFromErrno());
+ goto done;
+ }
+ FILEDosToUnixPathA(lpUnixPath);
+ if (stat (lpUnixPath, &SrcFileStats) == -1)
+ {
+ ERROR("stat() failed for %s\n", lpExistingFileName);
+ pThread->SetLastError(FILEGetLastErrorFromErrnoAndFilename(lpUnixPath));
+ goto done;
+ }
+
+ hDest = CreateFileA( lpNewFileName,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ dwDestCreationMode,
+ 0,
+ NULL );
+
+ if ( hDest == INVALID_HANDLE_VALUE )
+ {
+ ERROR("CreateFileA failed for %s\n", lpNewFileName);
+ goto done;
+ }
+
+ free(lpUnixPath);
+ lpUnixPath = strdup(lpNewFileName);
+ if ( lpUnixPath == NULL )
+ {
+ ERROR("strdup() failed\n");
+ pThread->SetLastError(FILEGetLastErrorFromErrno());
+ goto done;
+ }
+ FILEDosToUnixPathA( lpUnixPath );
+
+
+ // We don't set file attributes in CreateFile. The only attribute
+ // that is reflected on disk in Unix is read-only, and we set that
+ // here.
+ permissions = (S_IRWXU | S_IRWXG | S_IRWXO);
+ if ((dwSrcFileAttributes & FILE_ATTRIBUTE_READONLY) != 0)
+ {
+ permissions &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ }
+
+ /* Make sure the new file has the same chmod() flags. */
+ if (chmod(lpUnixPath, SrcFileStats.st_mode & permissions) == -1)
+ {
+ WARN ("chmod() failed to set mode 0x%x on new file\n",
+ SrcFileStats.st_mode & permissions);
+ pThread->SetLastError(FILEGetLastErrorFromErrnoAndFilename(lpUnixPath));
+ goto done;
+ }
+
+ while( (bGood = ReadFile( hSource, buffer, buffer_size, &bytes_read, NULL ))
+ && bytes_read > 0 )
+ {
+ bGood = ( WriteFile( hDest, buffer, bytes_read, &bytes_written, NULL )
+ && bytes_written == bytes_read);
+ if (!bGood) break;
+ }
+
+ if (!bGood)
+ {
+ ERROR("Copy failed\n");
+
+ if ( !CloseHandle(hDest) ||
+ !DeleteFileA(lpNewFileName) )
+ {
+ ERROR("Unable to clean up partial copy\n");
+ }
+ hDest = INVALID_HANDLE_VALUE;
+
+ goto done;
+ }
+
+done:
+
+ if ( hSource != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle( hSource );
+ }
+ if ( hDest != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle( hDest );
+ }
+ if (lpUnixPath)
+ {
+ free(lpUnixPath);
+ }
+
+ LOGEXIT("CopyFileA returns BOOL %d\n", bGood);
+ PERF_EXIT(CopyFileA);
+ return bGood;
+}
+
+
+/*++
+Function:
+ SetFileAttributesA
+
+Notes:
+ Used for setting read-only attribute on file only.
+
+--*/
+BOOL
+PALAPI
+SetFileAttributesA(
+ IN LPCSTR lpFileName,
+ IN DWORD dwFileAttributes)
+{
+ CPalThread *pThread;
+ struct stat stat_data;
+ mode_t new_mode;
+
+ DWORD dwLastError = 0;
+ BOOL bRet = FALSE;
+ LPSTR unixFileName = NULL;
+
+ PERF_ENTRY(SetFileAttributesA);
+ ENTRY("SetFileAttributesA(lpFileName=%p (%s), dwFileAttributes=%#x)\n",
+ lpFileName?lpFileName:"NULL",
+ lpFileName?lpFileName:"NULL", dwFileAttributes);
+
+ pThread = InternalGetCurrentThread();
+
+ /* Windows behavior for SetFileAttributes is that any valid attributes
+ are set on a file and any invalid attributes are ignored. SetFileAttributes
+ returns success and does not set an error even if some or all of the
+ attributes are invalid. If all the attributes are invalid, SetFileAttributes
+ sets a file's attribute to NORMAL. */
+
+ /* If dwFileAttributes does not contain READONLY or NORMAL, set it to NORMAL
+ and print a warning message. */
+ if ( !(dwFileAttributes & (FILE_ATTRIBUTE_READONLY |FILE_ATTRIBUTE_NORMAL)) )
+ {
+ dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ WARN("dwFileAttributes(%#x) contains attributes that are either not supported "
+ "or cannot be set via SetFileAttributes.\n");
+ }
+
+ if ( (dwFileAttributes & FILE_ATTRIBUTE_NORMAL) &&
+ (dwFileAttributes != FILE_ATTRIBUTE_NORMAL) )
+ {
+ WARN("Ignoring FILE_ATTRIBUTE_NORMAL -- it must be used alone\n");
+ }
+
+ if (lpFileName == NULL)
+ {
+ dwLastError = ERROR_FILE_NOT_FOUND;
+ goto done;
+ }
+
+ if ((unixFileName = strdup(lpFileName)) == NULL)
+ {
+ ERROR("strdup() failed\n");
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ FILEDosToUnixPathA( unixFileName );
+ if ( stat(unixFileName, &stat_data) != 0 )
+ {
+ TRACE("stat failed on %s; errno is %d (%s)\n",
+ unixFileName, errno, strerror(errno));
+ dwLastError = FILEGetLastErrorFromErrnoAndFilename(unixFileName);
+ goto done;
+ }
+
+ new_mode = stat_data.st_mode;
+ TRACE("st_mode is %#x\n", new_mode);
+
+ /* if we can't do GetFileAttributes on it, don't do SetFileAttributes */
+ if ( !(new_mode & S_IFREG) && !(new_mode & S_IFDIR) )
+ {
+ ERROR("Not a regular file or directory, S_IFMT is %#x\n",
+ new_mode & S_IFMT);
+ dwLastError = ERROR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* set or unset the "read-only" attribute */
+ if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+ {
+ /* remove the write bit from everybody */
+ new_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ }
+ else
+ {
+ /* give write permission to the owner if the owner
+ * already has read permission */
+ if ( new_mode & S_IRUSR )
+ {
+ new_mode |= S_IWUSR;
+ }
+ }
+ TRACE("new mode is %#x\n", new_mode);
+
+ bRet = TRUE;
+ if ( new_mode != stat_data.st_mode )
+ {
+ if ( chmod(unixFileName, new_mode) != 0 )
+ {
+ ERROR("chmod(%s, %#x) failed\n", unixFileName, new_mode);
+ dwLastError = FILEGetLastErrorFromErrnoAndFilename(unixFileName);
+ bRet = FALSE;
+ }
+ }
+
+done:
+ if (dwLastError)
+ {
+ pThread->SetLastError(dwLastError);
+ }
+
+ free(unixFileName);
+
+ LOGEXIT("SetFileAttributesA returns BOOL %d\n", bRet);
+ PERF_EXIT(SetFileAttributesA);
+ return bRet;
+}
+
+PAL_ERROR
+CorUnix::InternalCreatePipe(
+ CPalThread *pThread,
+ HANDLE *phReadPipe,
+ HANDLE *phWritePipe,
+ LPSECURITY_ATTRIBUTES lpPipeAttributes,
+ DWORD nSize
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pReadFileObject = NULL;
+ IPalObject *pReadRegisteredFile = NULL;
+ IPalObject *pWriteFileObject = NULL;
+ IPalObject *pWriteRegisteredFile = NULL;
+ IDataLock *pDataLock = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ CObjectAttributes oaFile(NULL, lpPipeAttributes);
+
+ int readWritePipeDes[2] = {-1, -1};
+
+ if ((phReadPipe == NULL) || (phWritePipe == NULL))
+ {
+ ERROR("One of the two parameters hReadPipe(%p) and hWritePipe(%p) is Null\n",phReadPipe,phWritePipe);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreatePipeExit;
+ }
+
+ if ((lpPipeAttributes == NULL) ||
+ (lpPipeAttributes->bInheritHandle == FALSE) ||
+ (lpPipeAttributes->lpSecurityDescriptor != NULL))
+ {
+ ASSERT("invalid security attributes!\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreatePipeExit;
+ }
+
+ if (pipe(readWritePipeDes) == -1)
+ {
+ ERROR("pipe() call failed errno:%d (%s) \n", errno, strerror(errno));
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalCreatePipeExit;
+ }
+
+ /* enable close-on-exec for both pipes; if one gets passed to CreateProcess
+ it will be "uncloseonexeced" in order to be inherited */
+ if(-1 == fcntl(readWritePipeDes[0],F_SETFD,1))
+ {
+ ASSERT("can't set close-on-exec flag; fcntl() failed. errno is %d "
+ "(%s)\n", errno, strerror(errno));
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalCreatePipeExit;
+ }
+ if(-1 == fcntl(readWritePipeDes[1],F_SETFD,1))
+ {
+ ASSERT("can't set close-on-exec flag; fcntl() failed. errno is %d "
+ "(%s)\n", errno, strerror(errno));
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalCreatePipeExit;
+ }
+
+ //
+ // Setup the object for the read end of the pipe
+ //
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otFile,
+ &oaFile,
+ &pReadFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreatePipeExit;
+ }
+
+ palError = pReadFileObject->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreatePipeExit;
+ }
+
+ pLocalData->inheritable = TRUE;
+ pLocalData->open_flags = O_RDONLY;
+
+ //
+ // After storing the file descriptor in the object's local data
+ // we want to clear it from the array to prevent a possible double
+ // close if an error occurs.
+ //
+
+ pLocalData->unix_fd = readWritePipeDes[0];
+ readWritePipeDes[0] = -1;
+
+ pDataLock->ReleaseLock(pThread, TRUE);
+ pDataLock = NULL;
+
+ //
+ // Setup the object for the write end of the pipe
+ //
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otFile,
+ &oaFile,
+ &pWriteFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreatePipeExit;
+ }
+
+ palError = pWriteFileObject->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreatePipeExit;
+ }
+
+ pLocalData->inheritable = TRUE;
+ pLocalData->open_flags = O_WRONLY;
+
+ //
+ // After storing the file descriptor in the object's local data
+ // we want to clear it from the array to prevent a possible double
+ // close if an error occurs.
+ //
+
+ pLocalData->unix_fd = readWritePipeDes[1];
+ readWritePipeDes[1] = -1;
+
+ pDataLock->ReleaseLock(pThread, TRUE);
+ pDataLock = NULL;
+
+ //
+ // Register the pipe objects
+ //
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pReadFileObject,
+ &aotFile,
+ GENERIC_READ,
+ phReadPipe,
+ &pReadRegisteredFile
+ );
+
+ //
+ // pReadFileObject is invalidated by the call to RegisterObject, so NULL it
+ // out here to ensure that we don't try to release a reference on
+ // it down the line.
+ //
+
+ pReadFileObject = NULL;
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreatePipeExit;
+ }
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pWriteFileObject,
+ &aotFile,
+ GENERIC_WRITE,
+ phWritePipe,
+ &pWriteRegisteredFile
+ );
+
+ //
+ // pWriteFileObject is invalidated by the call to RegisterObject, so NULL it
+ // out here to ensure that we don't try to release a reference on
+ // it down the line.
+ //
+
+ pWriteFileObject = NULL;
+
+InternalCreatePipeExit:
+
+ if (NO_ERROR != palError)
+ {
+ if (-1 != readWritePipeDes[0])
+ {
+ close(readWritePipeDes[0]);
+ }
+
+ if (-1 != readWritePipeDes[1])
+ {
+ close(readWritePipeDes[1]);
+ }
+ }
+
+ if (NULL != pReadFileObject)
+ {
+ pReadFileObject->ReleaseReference(pThread);
+ }
+
+ if (NULL != pReadRegisteredFile)
+ {
+ pReadRegisteredFile->ReleaseReference(pThread);
+ }
+
+ if (NULL != pWriteFileObject)
+ {
+ pWriteFileObject->ReleaseReference(pThread);
+ }
+
+ if (NULL != pWriteRegisteredFile)
+ {
+ pWriteRegisteredFile->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ CreatePipe
+
+See MSDN doc.
+--*/
+PALIMPORT
+BOOL
+PALAPI
+CreatePipe(
+ OUT PHANDLE hReadPipe,
+ OUT PHANDLE hWritePipe,
+ IN LPSECURITY_ATTRIBUTES lpPipeAttributes,
+ IN DWORD nSize)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+
+ PERF_ENTRY(CreatePipe);
+ ENTRY("CreatePipe(hReadPipe:%p, hWritePipe:%p, lpPipeAttributes:%p, nSize:%d\n",
+ hReadPipe, hWritePipe, lpPipeAttributes, nSize);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalCreatePipe(
+ pThread,
+ hReadPipe,
+ hWritePipe,
+ lpPipeAttributes,
+ nSize
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("CreatePipe return %s\n", NO_ERROR == palError ? "TRUE":"FALSE");
+ PERF_EXIT(CreatePipe);
+ return NO_ERROR == palError;
+}
+
+PAL_ERROR
+CorUnix::InternalLockFile(
+ CPalThread *pThread,
+ HANDLE hFile,
+ DWORD dwFileOffsetLow,
+ DWORD dwFileOffsetHigh,
+ DWORD nNumberOfBytesToLockLow,
+ DWORD nNumberOfBytesToLockHigh
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalLockFileExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalLockFileExit;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalLockFileExit;
+ }
+
+ if (NULL != pLocalData->pLockController)
+ {
+ palError = pLocalData->pLockController->CreateFileLock(
+ pThread,
+ dwFileOffsetLow,
+ dwFileOffsetHigh,
+ nNumberOfBytesToLockLow,
+ nNumberOfBytesToLockHigh,
+ IFileLockController::ExclusiveFileLock,
+ IFileLockController::FailImmediately
+ );
+ }
+ else
+ {
+ //
+ // This isn't a lockable file (e.g., it may be a pipe)
+ //
+
+ palError = ERROR_ACCESS_DENIED;
+ goto InternalLockFileExit;
+ }
+
+InternalLockFileExit:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+
+/*++
+Function:
+ LockFile
+
+See MSDN doc.
+--*/
+PALIMPORT
+BOOL
+PALAPI
+LockFile(HANDLE hFile,
+ DWORD dwFileOffsetLow,
+ DWORD dwFileOffsetHigh,
+ DWORD nNumberOfBytesToLockLow,
+ DWORD nNumberOfBytesToLockHigh)
+{
+ CPalThread *pThread;
+ PAL_ERROR palError = NO_ERROR;
+
+ PERF_ENTRY(LockFile);
+ ENTRY("LockFile(hFile:%p, offsetLow:%u, offsetHigh:%u, nbBytesLow:%u,"
+ " nbBytesHigh:%u\n", hFile, dwFileOffsetLow, dwFileOffsetHigh,
+ nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalLockFile(
+ pThread,
+ hFile,
+ dwFileOffsetLow,
+ dwFileOffsetHigh,
+ nNumberOfBytesToLockLow,
+ nNumberOfBytesToLockHigh
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("LockFile returns %s\n", NO_ERROR == palError ? "TRUE":"FALSE");
+ PERF_EXIT(LockFile);
+ return NO_ERROR == palError;
+}
+
+PAL_ERROR
+CorUnix::InternalUnlockFile(
+ CPalThread *pThread,
+ HANDLE hFile,
+ DWORD dwFileOffsetLow,
+ DWORD dwFileOffsetHigh,
+ DWORD nNumberOfBytesToUnlockLow,
+ DWORD nNumberOfBytesToUnlockHigh
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalUnlockFileExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalUnlockFileExit;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalUnlockFileExit;
+ }
+
+ if (NULL != pLocalData->pLockController)
+ {
+ palError = pLocalData->pLockController->ReleaseFileLock(
+ pThread,
+ dwFileOffsetLow,
+ dwFileOffsetHigh,
+ nNumberOfBytesToUnlockLow,
+ nNumberOfBytesToUnlockHigh
+ );
+ }
+ else
+ {
+ //
+ // This isn't a lockable file (e.g., it may be a pipe)
+ //
+
+ palError = ERROR_ACCESS_DENIED;
+ goto InternalUnlockFileExit;
+ }
+
+InternalUnlockFileExit:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ UnlockFile
+
+See MSDN doc.
+--*/
+PALIMPORT
+BOOL
+PALAPI
+UnlockFile(HANDLE hFile,
+ DWORD dwFileOffsetLow,
+ DWORD dwFileOffsetHigh,
+ DWORD nNumberOfBytesToUnlockLow,
+ DWORD nNumberOfBytesToUnlockHigh)
+{
+ CPalThread *pThread;
+ PAL_ERROR palError = NO_ERROR;
+
+ PERF_ENTRY(UnlockFile);
+ ENTRY("UnlockFile(hFile:%p, offsetLow:%u, offsetHigh:%u, nbBytesLow:%u,"
+ "nbBytesHigh:%u\n", hFile, dwFileOffsetLow, dwFileOffsetHigh,
+ nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalUnlockFile(
+ pThread,
+ hFile,
+ dwFileOffsetLow,
+ dwFileOffsetHigh,
+ nNumberOfBytesToUnlockLow,
+ nNumberOfBytesToUnlockHigh
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("UnlockFile returns %s\n", NO_ERROR == palError ? "TRUE" : "FALSE");
+ PERF_EXIT(UnlockFile);
+ return NO_ERROR == palError;
+}
+
+/*++
+init_std_handle [static]
+
+utility function for FILEInitStdHandles. do the work that is common to all
+three standard handles
+
+Parameters:
+ HANDLE pStd : Defines which standard handle to assign
+ FILE *stream : file stream to associate to handle
+
+Return value:
+ handle for specified stream, or INVALID_HANDLE_VALUE on failure
+--*/
+static HANDLE init_std_handle(HANDLE * pStd, FILE *stream)
+{
+ CPalThread *pThread = InternalGetCurrentThread();
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileObject = NULL;
+ IPalObject *pRegisteredFile = NULL;
+ IDataLock *pDataLock = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IFileLockController *pLockController = NULL;
+ CObjectAttributes oa;
+
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ int new_fd = -1;
+
+ /* duplicate the FILE *, so that we can fclose() in FILECloseHandle without
+ closing the original */
+ new_fd = dup(fileno(stream));
+ if(-1 == new_fd)
+ {
+ ERROR("dup() failed; errno is %d (%s)\n", errno, strerror(errno));
+ goto done;
+ }
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otFile,
+ &oa,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto done;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto done;
+ }
+
+ pLocalData->inheritable = TRUE;
+ pLocalData->unix_fd = new_fd;
+ pLocalData->dwDesiredAccess = 0;
+ pLocalData->open_flags = 0;
+ pLocalData->open_flags_deviceaccessonly = FALSE;
+
+ //
+ // Transfer the lock controller reference from our local variable
+ // to the local file data
+ //
+
+ pLocalData->pLockController = pLockController;
+ pLockController = NULL;
+
+ //
+ // We've finished initializing our local data, so release that lock
+ //
+
+ pDataLock->ReleaseLock(pThread, TRUE);
+ pDataLock = NULL;
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pFileObject,
+ &aotFile,
+ 0,
+ &hFile,
+ &pRegisteredFile
+ );
+
+ //
+ // pFileObject is invalidated by the call to RegisterObject, so NULL it
+ // out here to ensure that we don't try to release a reference on
+ // it down the line.
+ //
+
+ pFileObject = NULL;
+
+done:
+
+ if (NULL != pLockController)
+ {
+ pLockController->ReleaseController();
+ }
+
+ if (NULL != pDataLock)
+ {
+ pDataLock->ReleaseLock(pThread, TRUE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ if (NULL != pRegisteredFile)
+ {
+ pRegisteredFile->ReleaseReference(pThread);
+ }
+
+ if (NO_ERROR == palError)
+ {
+ *pStd = hFile;
+ }
+ else if (-1 != new_fd)
+ {
+ close(new_fd);
+ }
+
+ return hFile;
+}
+
+
+/*++
+FILEInitStdHandles
+
+Create handle objects for stdin, stdout and stderr
+
+(no parameters)
+
+Return value:
+ TRUE on success, FALSE on failure
+--*/
+BOOL FILEInitStdHandles(void)
+{
+ HANDLE stdin_handle;
+ HANDLE stdout_handle;
+ HANDLE stderr_handle;
+
+ TRACE("creating handle objects for stdin, stdout, stderr\n");
+
+ stdin_handle = init_std_handle(&pStdIn, stdin);
+ if(INVALID_HANDLE_VALUE == stdin_handle)
+ {
+ ERROR("failed to create stdin handle\n");
+ goto fail;
+ }
+
+ stdout_handle = init_std_handle(&pStdOut, stdout);
+ if(INVALID_HANDLE_VALUE == stdout_handle)
+ {
+ ERROR("failed to create stdout handle\n");
+ CloseHandle(stdin_handle);
+ goto fail;
+ }
+
+ stderr_handle = init_std_handle(&pStdErr, stderr);
+ if(INVALID_HANDLE_VALUE == stderr_handle)
+ {
+ ERROR("failed to create stderr handle\n");
+ CloseHandle(stdin_handle);
+ CloseHandle(stdout_handle);
+ goto fail;
+ }
+ return TRUE;
+
+fail:
+ pStdIn = INVALID_HANDLE_VALUE;
+ pStdOut = INVALID_HANDLE_VALUE;
+ pStdErr = INVALID_HANDLE_VALUE;
+ return FALSE;
+}
+
+/*++
+FILECleanupStdHandles
+
+Remove all regions, locked by a file pointer, from shared memory
+
+(no parameters)
+
+--*/
+void FILECleanupStdHandles(void)
+{
+ HANDLE stdin_handle;
+ HANDLE stdout_handle;
+ HANDLE stderr_handle;
+
+ TRACE("closing standard handles\n");
+ stdin_handle = pStdIn;
+ stdout_handle = pStdOut;
+ stderr_handle = pStdErr;
+
+ pStdIn = INVALID_HANDLE_VALUE;
+ pStdOut = INVALID_HANDLE_VALUE;
+ pStdErr = INVALID_HANDLE_VALUE;
+
+ if (stdin_handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(stdin_handle);
+ }
+
+ if (stdout_handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(stdout_handle);
+ }
+
+ if (stderr_handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(stderr_handle);
+ }
+}
+
+/*++
+Function:
+ GetFileInformationByHandle
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetFileInformationByHandle(
+ IN HANDLE hFile,
+ OUT LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
+{
+ CPalThread *pThread;
+ BOOL bRet = FALSE;
+ DWORD dwLastError = 0;
+
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+
+ DWORD dwAttr = 0;
+ struct stat stat_data;
+
+ PERF_ENTRY(GetFileInformationByHandle);
+ ENTRY("GetFileInformationByHandle(hFile=%p, lpFileInformation=%p)\n",
+ hFile, lpFileInformation);
+
+ pThread = InternalGetCurrentThread();
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ dwLastError = ERROR_INVALID_HANDLE;
+ goto done;
+ }
+
+ dwLastError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != dwLastError)
+ {
+ goto done;
+ }
+
+ dwLastError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != dwLastError)
+ {
+ goto done;
+ }
+
+ if ( fstat(pLocalData->unix_fd, &stat_data) != 0 )
+ {
+ if ((dwLastError = FILEGetLastErrorFromErrno()) == ERROR_INTERNAL_ERROR)
+ {
+ ASSERT("fstat() not expected to fail with errno:%d (%s)\n",
+ errno, strerror(errno));
+ }
+ goto done;
+ }
+
+ if ( (stat_data.st_mode & S_IFMT) == S_IFDIR )
+ {
+ dwAttr |= FILE_ATTRIBUTE_DIRECTORY;
+ }
+ else if ( (stat_data.st_mode & S_IFMT) != S_IFREG )
+ {
+ ERROR("Not a regular file or directory, S_IFMT is %#x\n",
+ stat_data.st_mode & S_IFMT);
+ dwLastError = ERROR_ACCESS_DENIED;
+ goto done;
+ }
+
+ if ( UTIL_IsReadOnlyBitsSet( &stat_data ) )
+ {
+ dwAttr |= FILE_ATTRIBUTE_READONLY;
+ }
+
+ /* finally, if nothing is set... */
+ if ( dwAttr == 0 )
+ {
+ dwAttr = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ lpFileInformation->dwFileAttributes = dwAttr;
+
+ /* get the file times */
+ lpFileInformation->ftCreationTime =
+ FILEUnixTimeToFileTime( stat_data.st_ctime,
+ ST_CTIME_NSEC(&stat_data) );
+ lpFileInformation->ftLastAccessTime =
+ FILEUnixTimeToFileTime( stat_data.st_atime,
+ ST_ATIME_NSEC(&stat_data) );
+ lpFileInformation->ftLastWriteTime =
+ FILEUnixTimeToFileTime( stat_data.st_mtime,
+ ST_MTIME_NSEC(&stat_data) );
+
+ lpFileInformation->dwVolumeSerialNumber = stat_data.st_dev;
+
+ /* Get the file size. GetFileSize is not used because it gets the
+ size of an already-open file */
+ lpFileInformation->nFileSizeLow = (DWORD) stat_data.st_size;
+#if SIZEOF_OFF_T > 4
+ lpFileInformation->nFileSizeHigh = (DWORD)(stat_data.st_size >> 32);
+#else
+ lpFileInformation->nFileSizeHigh = 0;
+#endif
+
+ lpFileInformation->nNumberOfLinks = stat_data.st_nlink;
+ lpFileInformation->nFileIndexHigh = 0;
+ lpFileInformation->nFileIndexLow = stat_data.st_ino;
+
+ bRet = TRUE;
+
+done:
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ if (dwLastError) pThread->SetLastError(dwLastError);
+
+ LOGEXIT("GetFileInformationByHandle returns BOOL %d\n", bRet);
+ PERF_EXIT(GetFileInformationByHandle);
+ return bRet;
+}
diff --git a/src/pal/src/file/filetime.cpp b/src/pal/src/file/filetime.cpp
new file mode 100644
index 0000000000..a8666b0dff
--- /dev/null
+++ b/src/pal/src/file/filetime.cpp
@@ -0,0 +1,788 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ filetime.cpp
+
+Abstract:
+
+ Implementation of the file WIN API related to file time.
+
+Notes:
+
+One very important thing to note is that on BSD systems, the stat structure
+stores nanoseconds for the time-related fields. This is implemented by
+replacing the time_t fields st_atime, st_mtime, and st_ctime by timespec
+structures, instead named st_atimespec, st_mtimespec, and st_ctimespec.
+
+However, if _POSIX_SOURCE is defined, the fields are time_t values and use
+their POSIX names. For compatibility purposes, when _POSIX_SOURCE is NOT
+defined, the time-related fields are defined in sys/stat.h as:
+
+#ifndef _POSIX_SOURCE
+#define st_atime st_atimespec.tv_sec
+#define st_mtime st_mtimespec.tv_sec
+#define st_ctime st_ctimespec.tv_sec
+#endif
+
+Furthermore, if _POSIX_SOURCE is defined, the structure still has
+additional fields for nanoseconds, named st_atimensec, st_mtimensec, and
+st_ctimensec.
+
+In the PAL, there is a configure check to see if the system supports
+nanoseconds for the time-related fields. This source file also sets macros
+so that STAT_ATIME_NSEC etc. will always refer to the appropriate field
+if it exists, and are defined as 0 otherwise.
+
+--
+
+Also note that there is no analog to "creation time" on Unix systems.
+Instead, we use the inode change time, which is set to the current time
+whenever mtime changes or when chmod, chown, etc. syscalls modify the
+file status.
+
+
+
+--*/
+
+#include "pal/corunix.hpp"
+#include "pal/dbgmsg.h"
+#include "pal/filetime.h"
+#include "pal/thread.hpp"
+#include "pal/file.hpp"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <utime.h>
+#include <time.h>
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif // HAVE_SYS_TIME_H
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(FILE);
+
+// In safemath.h, Template SafeInt uses macro _ASSERTE, which need to use variable
+// defdbgchan defined by SET_DEFAULT_DEBUG_CHANNEL. Therefore, the include statement
+// should be placed after the SET_DEFAULT_DEBUG_CHANNEL(FILE)
+#include <safemath.h>
+
+/* Magic number explanation:
+
+ To 1970:
+ Both epochs are Gregorian. 1970 - 1601 = 369. Assuming a leap
+ year every four years, 369 / 4 = 92. However, 1700, 1800, and 1900
+ were NOT leap years, so 89 leap years, 280 non-leap years.
+ 89 * 366 + 280 * 365 = 134744 days between epochs. Of course
+ 60 * 60 * 24 = 86400 seconds per day, so 134744 * 86400 =
+ 11644473600 = SECS_BETWEEN_1601_AND_1970_EPOCHS.
+
+ To 2001:
+ Again, both epochs are Gregorian. 2001 - 1601 = 400. Assuming a leap
+ year every four years, 400 / 4 = 100. However, 1700, 1800, and 1900
+ were NOT leap years (2000 was because it was divisible by 400), so
+ 97 leap years, 303 non-leap years.
+ 97 * 366 + 303 * 365 = 146097 days between epochs. 146097 * 86400 =
+ 12622780800 = SECS_BETWEEN_1601_AND_2001_EPOCHS.
+
+ This result is also confirmed in the MSDN documentation on how
+ to convert a time_t value to a win32 FILETIME.
+*/
+static const __int64 SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL;
+static const __int64 SECS_TO_100NS = 10000000; /* 10^7 */
+
+#ifdef __APPLE__
+static const __int64 SECS_BETWEEN_1601_AND_2001_EPOCHS = 12622780800LL;
+#endif // __APPLE__
+
+/*++
+Function:
+ CompareFileTime
+
+See MSDN doc.
+--*/
+LONG
+PALAPI
+CompareFileTime(
+ IN CONST FILETIME *lpFileTime1,
+ IN CONST FILETIME *lpFileTime2)
+{
+ __int64 First;
+ __int64 Second;
+
+ long Ret;
+
+ PERF_ENTRY(CompareFileTime);
+ ENTRY("CompareFileTime(lpFileTime1=%p lpFileTime2=%p)\n",
+ lpFileTime1, lpFileTime2);
+
+ First = ((__int64)lpFileTime1->dwHighDateTime << 32) +
+ lpFileTime1->dwLowDateTime;
+ Second = ((__int64)lpFileTime2->dwHighDateTime << 32) +
+ lpFileTime2->dwLowDateTime;
+
+ if ( First < Second )
+ {
+ Ret = -1;
+ }
+ else if ( First > Second )
+ {
+ Ret = 1;
+ }
+ else
+ {
+ Ret = 0;
+ }
+
+ LOGEXIT("CompareFileTime returns LONG %ld\n", Ret);
+ PERF_EXIT(CompareFileTime);
+ return Ret;
+}
+
+
+
+/*++
+Function:
+ SetFileTime
+
+Notes: This function will drop one digit (radix 10) of precision from
+the supplied times, since Unix can set to the microsecond (at most, i.e.
+if the futimes() function is available).
+
+As noted in the file header, there is no analog to "creation time" on Unix
+systems, so the lpCreationTime argument to this function will always be
+ignored, and the inode change time will be set to the current time.
+--*/
+BOOL
+PALAPI
+SetFileTime(
+ IN HANDLE hFile,
+ IN CONST FILETIME *lpCreationTime,
+ IN CONST FILETIME *lpLastAccessTime,
+ IN CONST FILETIME *lpLastWriteTime)
+{
+ CPalThread *pThread;
+ PAL_ERROR palError = NO_ERROR;
+ const UINT64 MAX_FILETIMEVALUE = 0x8000000000000000LL;
+
+ PERF_ENTRY(SetFileTime);
+ ENTRY("SetFileTime(hFile=%p, lpCreationTime=%p, lpLastAccessTime=%p, "
+ "lpLastWriteTime=%p)\n", hFile, lpCreationTime, lpLastAccessTime,
+ lpLastWriteTime);
+
+ pThread = InternalGetCurrentThread();
+
+ /* validate filetime values */
+ if ( (lpCreationTime && (((UINT64)lpCreationTime->dwHighDateTime << 32) +
+ lpCreationTime->dwLowDateTime >= MAX_FILETIMEVALUE)) ||
+ (lpLastAccessTime && (((UINT64)lpLastAccessTime->dwHighDateTime << 32) +
+ lpLastAccessTime->dwLowDateTime >= MAX_FILETIMEVALUE)) ||
+ (lpLastWriteTime && (((UINT64)lpLastWriteTime->dwHighDateTime << 32) +
+ lpLastWriteTime->dwLowDateTime >= MAX_FILETIMEVALUE)))
+ {
+ pThread->SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ palError = InternalSetFileTime(
+ pThread,
+ hFile,
+ lpCreationTime,
+ lpLastAccessTime,
+ lpLastWriteTime
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("SetFileTime returns BOOL %s\n", NO_ERROR == palError ? "TRUE":"FALSE");
+ PERF_EXIT(SetFileTime);
+ return NO_ERROR == palError;
+}
+
+PAL_ERROR
+CorUnix::InternalSetFileTime(
+ CPalThread *pThread,
+ IN HANDLE hFile,
+ IN CONST FILETIME *lpCreationTime,
+ IN CONST FILETIME *lpLastAccessTime,
+ IN CONST FILETIME *lpLastWriteTime)
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ struct timeval Times[2];
+ int fd;
+ long nsec;
+ struct stat stat_buf;
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalSetFileTimeExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalSetFileTimeExit;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalSetFileTimeExit;
+ }
+
+ if (lpCreationTime)
+ {
+ palError = ERROR_NOT_SUPPORTED;
+ goto InternalSetFileTimeExit;
+ }
+
+ if( !lpLastAccessTime && !lpLastWriteTime )
+ {
+ // if both pointers are NULL, the function simply returns.
+ goto InternalSetFileTimeExit;
+ }
+ else if( !lpLastAccessTime || !lpLastWriteTime )
+ {
+ // if either pointer is NULL, fstat will need to be called.
+ fd = pLocalData->unix_fd;
+ if ( fd == -1 )
+ {
+ TRACE("pLocalData = [%p], fd = %d\n", pLocalData, fd);
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalSetFileTimeExit;
+ }
+
+ if ( fstat(fd, &stat_buf) != 0 )
+ {
+ TRACE("fstat failed on file descriptor %d\n", fd);
+ palError = FILEGetLastErrorFromErrno();
+ goto InternalSetFileTimeExit;
+ }
+ }
+
+ if (lpLastAccessTime)
+ {
+ Times[0].tv_sec = FILEFileTimeToUnixTime( *lpLastAccessTime, &nsec );
+ Times[0].tv_usec = nsec / 1000; /* convert to microseconds */
+ }
+ else
+ {
+ Times[0].tv_sec = stat_buf.st_atime;
+ Times[0].tv_usec = ST_ATIME_NSEC(&stat_buf) / 1000;
+ }
+
+ if (lpLastWriteTime)
+ {
+ Times[1].tv_sec = FILEFileTimeToUnixTime( *lpLastWriteTime, &nsec );
+ Times[1].tv_usec = nsec / 1000; /* convert to microseconds */
+ }
+ else
+ {
+ Times[1].tv_sec = stat_buf.st_mtime;
+ Times[1].tv_usec = ST_MTIME_NSEC(&stat_buf) / 1000;
+ }
+
+ TRACE("Setting atime = [%ld.%ld], mtime = [%ld.%ld]\n",
+ Times[0].tv_sec, Times[0].tv_usec,
+ Times[1].tv_sec, Times[1].tv_usec);
+
+#if HAVE_FUTIMES
+ if ( futimes(pLocalData->unix_fd, Times) != 0 )
+#elif HAVE_UTIMES
+ if ( utimes(pLocalData->unix_filename, Times) != 0 )
+#else
+ #error Operating system not supported
+#endif
+ {
+ palError = FILEGetLastErrorFromErrno();
+ }
+
+InternalSetFileTimeExit:
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+
+/*++
+Function:
+ GetFileTime
+
+Notes: As noted at the top of this file, there is no analog to "creation
+time" on Unix systems, so the inode change time is used instead. Also, Win32
+LastAccessTime is updated after a write operation, but it is not on Unix.
+To be consistent with Win32, this function returns the greater of mtime and
+atime for LastAccessTime.
+--*/
+BOOL
+PALAPI
+GetFileTime(
+ IN HANDLE hFile,
+ OUT LPFILETIME lpCreationTime,
+ OUT LPFILETIME lpLastAccessTime,
+ OUT LPFILETIME lpLastWriteTime)
+{
+ CPalThread *pThread;
+ PAL_ERROR palError = NO_ERROR;
+
+ PERF_ENTRY(GetFileTime);
+ ENTRY("GetFileTime(hFile=%p, lpCreationTime=%p, lpLastAccessTime=%p, "
+ "lpLastWriteTime=%p)\n",
+ hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalGetFileTime(
+ pThread,
+ hFile,
+ lpCreationTime,
+ lpLastAccessTime,
+ lpLastWriteTime
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("GetFileTime returns BOOL %s\n", NO_ERROR == palError ? "TRUE":"FALSE");
+ PERF_EXIT(GetFileTime);
+ return NO_ERROR == palError;
+}
+
+PAL_ERROR
+CorUnix::InternalGetFileTime(
+ CPalThread *pThread,
+ IN HANDLE hFile,
+ OUT LPFILETIME lpCreationTime,
+ OUT LPFILETIME lpLastAccessTime,
+ OUT LPFILETIME lpLastWriteTime)
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ int Fd = -1;
+
+ struct stat StatData;
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalGetFileTimeExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalGetFileTimeExit;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalGetFileTimeExit;
+ }
+
+ Fd = pLocalData->unix_fd;
+
+ if ( Fd == -1 )
+ {
+ TRACE("pLocalData = [%p], Fd = %d\n", pLocalData, Fd);
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalGetFileTimeExit;
+ }
+
+ if ( fstat(Fd, &StatData) != 0 )
+ {
+ TRACE("fstat failed on file descriptor %d\n", Fd);
+ palError = FILEGetLastErrorFromErrno();
+ goto InternalGetFileTimeExit;
+ }
+
+ if ( lpCreationTime )
+ {
+ *lpCreationTime = FILEUnixTimeToFileTime(StatData.st_ctime,
+ ST_CTIME_NSEC(&StatData));
+ }
+ if ( lpLastWriteTime )
+ {
+ *lpLastWriteTime = FILEUnixTimeToFileTime(StatData.st_mtime,
+ ST_MTIME_NSEC(&StatData));
+ }
+ if ( lpLastAccessTime )
+ {
+ *lpLastAccessTime = FILEUnixTimeToFileTime(StatData.st_atime,
+ ST_ATIME_NSEC(&StatData));
+ /* if Unix mtime is greater than atime, return mtime as the last
+ access time */
+ if ( lpLastWriteTime &&
+ CompareFileTime(lpLastAccessTime, lpLastWriteTime) < 0 )
+ {
+ *lpLastAccessTime = *lpLastWriteTime;
+ }
+ }
+
+InternalGetFileTimeExit:
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+
+
+
+
+
+/*++
+Function:
+ GetSystemTimeAsFileTime
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+GetSystemTimeAsFileTime(
+ OUT LPFILETIME lpSystemTimeAsFileTime)
+{
+ struct timeval Time;
+
+ PERF_ENTRY(GetSystemTimeAsFileTime);
+ ENTRY("GetSystemTimeAsFileTime(lpSystemTimeAsFileTime=%p)\n",
+ lpSystemTimeAsFileTime);
+
+ if ( gettimeofday( &Time, NULL ) != 0 )
+ {
+ ASSERT("gettimeofday() failed");
+ /* no way to indicate failure, so set time to zero */
+ *lpSystemTimeAsFileTime = FILEUnixTimeToFileTime( 0, 0 );
+ }
+ else
+ {
+ /* use (tv_usec * 1000) because 2nd arg is in nanoseconds */
+ *lpSystemTimeAsFileTime = FILEUnixTimeToFileTime( Time.tv_sec,
+ Time.tv_usec * 1000 );
+ }
+
+ LOGEXIT("GetSystemTimeAsFileTime returns.\n");
+ PERF_EXIT(GetSystemTimeAsFileTime);
+}
+
+
+#ifdef __APPLE__
+/*++
+Function:
+ FILECFAbsoluteTimeToFileTime
+
+Convert a CFAbsoluteTime value to a win32 FILETIME structure, as described
+in MSDN documentation. CFAbsoluteTime is the number of seconds elapsed since
+00:00 01 January 2001 UTC (Mac OS X epoch), while FILETIME represents a
+64-bit number of 100-nanosecond intervals that have passed since 00:00
+01 January 1601 UTC (win32 epoch).
+--*/
+FILETIME FILECFAbsoluteTimeToFileTime( CFAbsoluteTime sec )
+{
+ __int64 Result;
+ FILETIME Ret;
+
+ Result = ((__int64)sec + SECS_BETWEEN_1601_AND_2001_EPOCHS) * SECS_TO_100NS;
+
+ Ret.dwLowDateTime = (DWORD)Result;
+ Ret.dwHighDateTime = (DWORD)(Result >> 32);
+
+ TRACE("CFAbsoluteTime = [%9f] converts to Win32 FILETIME = [%#x:%#x]\n",
+ sec, Ret.dwHighDateTime, Ret.dwLowDateTime);
+
+ return Ret;
+}
+#endif // __APPLE__
+
+
+/*++
+Function:
+ FILEUnixTimeToFileTime
+
+Convert a time_t value to a win32 FILETIME structure, as described in
+MSDN documentation. time_t is the number of seconds elapsed since
+00:00 01 January 1970 UTC (Unix epoch), while FILETIME represents a
+64-bit number of 100-nanosecond intervals that have passed since 00:00
+01 January 1601 UTC (win32 epoch).
+--*/
+FILETIME FILEUnixTimeToFileTime( time_t sec, long nsec )
+{
+ __int64 Result;
+ FILETIME Ret;
+
+ Result = ((__int64)sec + SECS_BETWEEN_1601_AND_1970_EPOCHS) * SECS_TO_100NS +
+ (nsec / 100);
+
+ Ret.dwLowDateTime = (DWORD)Result;
+ Ret.dwHighDateTime = (DWORD)(Result >> 32);
+
+ TRACE("Unix time = [%ld.%09ld] converts to Win32 FILETIME = [%#x:%#x]\n",
+ sec, nsec, Ret.dwHighDateTime, Ret.dwLowDateTime);
+
+ return Ret;
+}
+
+
+/*++
+Function:
+ FILEFileTimeToUnixTime
+
+See FILEUnixTimeToFileTime above.
+
+This function takes a win32 FILETIME structures, returns the equivalent
+time_t value, and, if the nsec parameter is non-null, also returns the
+nanoseconds.
+
+NOTE: a 32-bit time_t is only capable of representing dates between
+13 December 1901 and 19 January 2038. This function will calculate the
+number of seconds (positive or negative) since the Unix epoch, however if
+this value is outside of the range of 32-bit numbers, the result will be
+truncated on systems with a 32-bit time_t.
+--*/
+time_t FILEFileTimeToUnixTime( FILETIME FileTime, long *nsec )
+{
+ __int64 UnixTime;
+
+ /* get the full win32 value, in 100ns */
+ UnixTime = ((__int64)FileTime.dwHighDateTime << 32) +
+ FileTime.dwLowDateTime;
+
+ /* convert to the Unix epoch */
+ UnixTime -= (SECS_BETWEEN_1601_AND_1970_EPOCHS * SECS_TO_100NS);
+
+ TRACE("nsec=%p\n", nsec);
+
+ if ( nsec )
+ {
+ /* get the number of 100ns, convert to ns */
+ *nsec = (UnixTime % SECS_TO_100NS) * 100;
+ }
+
+ UnixTime /= SECS_TO_100NS; /* now convert to seconds */
+
+ if ( (time_t)UnixTime != UnixTime )
+ {
+ WARN("Resulting value is too big for a time_t value\n");
+ }
+
+ TRACE("Win32 FILETIME = [%#x:%#x] converts to Unix time = [%ld.%09ld]\n",
+ FileTime.dwHighDateTime, FileTime.dwLowDateTime ,(long) UnixTime,
+ nsec?*nsec:0L);
+
+ return (time_t)UnixTime;
+}
+
+
+
+/**
+Function
+
+ FileTimeToSystemTime()
+
+ Helper function for FileTimeToDosTime.
+ Converts the necessary file time attibutes to system time, for
+ easier manipulation in FileTimeToDosTime.
+
+--*/
+BOOL PALAPI FileTimeToSystemTime( CONST FILETIME * lpFileTime,
+ LPSYSTEMTIME lpSystemTime )
+{
+ UINT64 FileTime = 0;
+ time_t UnixFileTime = 0;
+ struct tm * UnixSystemTime = 0;
+
+ /* Combine the file time. */
+ FileTime = lpFileTime->dwHighDateTime;
+ FileTime <<= 32;
+ FileTime |= (UINT)lpFileTime->dwLowDateTime;
+ bool isSafe = ClrSafeInt<UINT64>::subtraction(
+ FileTime,
+ SECS_BETWEEN_1601_AND_1970_EPOCHS * SECS_TO_100NS,
+ FileTime);
+
+ if (isSafe == true)
+ {
+#if HAVE_GMTIME_R
+ struct tm timeBuf;
+#endif /* HAVE_GMTIME_R */
+ /* Convert file time to unix time. */
+ if (((INT64)FileTime) < 0)
+ {
+ UnixFileTime = -1 - ( ( -FileTime - 1 ) / 10000000 );
+ }
+ else
+ {
+ UnixFileTime = FileTime / 10000000;
+ }
+
+ /* Convert unix file time to Unix System time. */
+#if HAVE_GMTIME_R
+ UnixSystemTime = gmtime_r( &UnixFileTime, &timeBuf );
+#else /* HAVE_GMTIME_R */
+ UnixSystemTime = gmtime( &UnixFileTime );
+#endif /* HAVE_GMTIME_R */
+
+ /* Convert unix system time to Windows system time. */
+ lpSystemTime->wDay = UnixSystemTime->tm_mday;
+
+ /* Unix time counts January as a 0, under Windows it is 1*/
+ lpSystemTime->wMonth = UnixSystemTime->tm_mon + 1;
+ /* Unix time returns the year - 1900, Windows returns the current year*/
+ lpSystemTime->wYear = UnixSystemTime->tm_year + 1900;
+
+ lpSystemTime->wSecond = UnixSystemTime->tm_sec;
+ lpSystemTime->wMinute = UnixSystemTime->tm_min;
+ lpSystemTime->wHour = UnixSystemTime->tm_hour;
+ return TRUE;
+ }
+ else
+ {
+ ERROR( "The file time is to large.\n" );
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+}
+
+
+
+/**
+Function:
+ FileTimeToDosDateTime
+
+ Notes due to the difference between how BSD and Windows
+ calculates time, this function can only repersent dates between
+ 1980 and 2037. 2037 is the upperlimit for the BSD time functions( 1900 -
+ 2037 range ).
+
+See msdn for more details.
+--*/
+BOOL
+PALAPI
+FileTimeToDosDateTime(
+ IN CONST FILETIME *lpFileTime,
+ OUT LPWORD lpFatDate,
+ OUT LPWORD lpFatTime )
+{
+ BOOL bRetVal = FALSE;
+
+ PERF_ENTRY(FileTimeToDosDateTime);
+ ENTRY( "FileTimeToDosDateTime( lpFileTime=%p, lpFatDate=%p, lpFatTime=%p )\n",
+ lpFileTime, lpFatDate, lpFatTime );
+
+ /* Sanity checks. */
+ if ( !lpFileTime || !lpFatDate || !lpFatTime )
+ {
+ ERROR( "Incorrect parameters.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ }
+ else
+ {
+ /* Do conversion. */
+ SYSTEMTIME SysTime;
+ if ( FileTimeToSystemTime( lpFileTime, &SysTime ) )
+ {
+ if ( SysTime.wYear >= 1980 && SysTime.wYear <= 2037 )
+ {
+ *lpFatDate = 0;
+ *lpFatTime = 0;
+
+ *lpFatDate |= ( SysTime.wDay & 0x1F );
+ *lpFatDate |= ( ( SysTime.wMonth & 0xF ) << 5 );
+ *lpFatDate |= ( ( ( SysTime.wYear - 1980 ) & 0x7F ) << 9 );
+
+ if ( SysTime.wSecond % 2 == 0 )
+ {
+ *lpFatTime |= ( ( SysTime.wSecond / 2 ) & 0x1F );
+ }
+ else
+ {
+ *lpFatTime |= ( ( SysTime.wSecond / 2 + 1 ) & 0x1F );
+ }
+
+ *lpFatTime |= ( ( SysTime.wMinute & 0x3F ) << 5 );
+ *lpFatTime |= ( ( SysTime.wHour & 0x1F ) << 11 );
+
+ bRetVal = TRUE;
+ }
+ else
+ {
+ ERROR( "The function can only repersent dates between 1/1/1980"
+ " and 12/31/2037\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ }
+ }
+ else
+ {
+ ERROR( "Unable to convert file time to system time.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ bRetVal = FALSE;
+ }
+ }
+
+ LOGEXIT( "returning BOOL %d\n", bRetVal );
+ PERF_EXIT(FileTimeToDosDateTime);
+ return bRetVal;
+}
+
diff --git a/src/pal/src/file/find.cpp b/src/pal/src/file/find.cpp
new file mode 100644
index 0000000000..18639d3d14
--- /dev/null
+++ b/src/pal/src/file/find.cpp
@@ -0,0 +1,999 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ find.c
+
+Abstract:
+
+ Implementation of the FindFile function family
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+#include "pal/file.hpp"
+#include "pal/stackstring.hpp"
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/filetime.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(FILE);
+
+namespace CorUnix
+{
+ int InternalGlob(
+ const char *szPattern,
+ int nFlags,
+#if ERROR_FUNC_FOR_GLOB_HAS_FIXED_PARAMS
+ int (*pnErrFunc)(const char *, int),
+#else
+ int (*pnErrFunc)(...),
+#endif
+ glob_t *pgGlob
+ );
+
+ /*++
+ InternalGlob
+
+ Input parameters:
+
+ szPattern = pointer to a pathname pattern to be expanded
+ nFlags = arguments to modify the behavior of glob
+ pnErrFunc = pointer to a routine that handles errors during the glob call
+ pgGlob = pointer to a glob structure
+
+ Return value:
+ 0 on success, -1 on failure.
+
+ Some platforms expect the error function for glob to take a variable number
+ of parameters, whereas other platforms insist that the error function take
+ a const char * and an int. A test in configure determines which is the case
+ for each platform and sets ERROR_FUNC_FOR_GLOB_HAS_FIXED_PARAMS
+ to 1 if the error func must have the char * and int parameters.
+ --*/
+ int
+ InternalGlob(
+ const char *szPattern,
+ int nFlags,
+#if ERROR_FUNC_FOR_GLOB_HAS_FIXED_PARAMS
+ int (*pnErrFunc)(const char *, int),
+#else
+ int (*pnErrFunc)(...),
+#endif
+ glob_t *pgGlob
+ )
+ {
+ int nRet = -1;
+ nRet = glob(szPattern, nFlags, pnErrFunc, pgGlob);
+ return nRet;
+ }
+}
+
+static BOOL FILEDosGlobA(
+ CPalThread *pthrCurrent,
+ const char *pattern,
+ int flags,
+ glob_t *pgGlob );
+
+static int FILEGlobQsortCompare(const void *in_str1, const void *in_str2);
+
+static int FILEGlobFromSplitPath(
+ const char *dir,
+ const char *fname,
+ const char *ext,
+ int flags,
+ glob_t *pgGlob );
+
+/*++
+Function:
+ FindFirstFileA
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+FindFirstFileA(
+ IN LPCSTR lpFileName,
+ OUT LPWIN32_FIND_DATAA lpFindFileData)
+{
+ HANDLE hRet = INVALID_HANDLE_VALUE;
+ DWORD dwLastError = NO_ERROR;
+ find_obj *find_data = NULL;
+ CPalThread *pthrCurrent = InternalGetCurrentThread();
+
+ PERF_ENTRY(FindFirstFileA);
+ ENTRY("FindFirstFileA(lpFileName=%p (%s), lpFindFileData=%p)\n",
+ lpFileName?lpFileName:"NULL",
+ lpFileName?lpFileName:"NULL", lpFindFileData);
+
+ if(NULL == lpFileName)
+ {
+ ERROR("lpFileName is NULL!\n");
+ dwLastError = ERROR_PATH_NOT_FOUND;
+ goto done;
+ }
+ if(NULL == lpFindFileData)
+ {
+ ASSERT("lpFindFileData is NULL!\n");
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ find_data = (find_obj *)InternalMalloc(sizeof(find_obj));
+ if ( find_data == NULL )
+ {
+ ERROR("Unable to allocate memory for find_data\n");
+ dwLastError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ find_data->self_addr = find_data;
+
+ // Clear the glob_t so we can safely call globfree() on it
+ // regardless of whether FILEDosGlobA ends up calling glob().
+ memset(&(find_data->gGlob), 0, sizeof(find_data->gGlob));
+
+ if (!FILEDosGlobA(pthrCurrent, lpFileName, 0, &(find_data->gGlob)))
+ {
+ // FILEDosGlobA will call SetLastError() on failure.
+ goto done;
+ }
+ else
+ {
+ // Check if there's at least one match.
+ if (find_data->gGlob.gl_pathc == 0)
+ {
+ /* Testing has indicated that for this API the
+ * last errors are as follows
+ * c:\temp\foo.txt - no error
+ * c:\temp\foo - ERROR_FILE_NOT_FOUND
+ * c:\temp\foo\bar - ERROR_PATH_NOT_FOUND
+ * c:\temp\foo.txt\bar - ERROR_DIRECTORY
+ *
+ */
+ LPSTR lpTemp = strdup((LPSTR)lpFileName);
+ if ( !lpTemp )
+ {
+ ERROR( "strdup failed!\n" );
+ SetLastError( ERROR_INTERNAL_ERROR );
+ goto done;
+ }
+ FILEDosToUnixPathA( lpTemp );
+ FILEGetProperNotFoundError( lpTemp, &dwLastError );
+
+ if ( ERROR_PATH_NOT_FOUND == dwLastError )
+ {
+ /* If stripping the last segment reveals a file name then
+ the error is ERROR_DIRECTORY. */
+ struct stat stat_data;
+ LPSTR lpLastPathSeparator = NULL;
+
+ lpLastPathSeparator = strrchr( lpTemp, '/');
+
+ if ( lpLastPathSeparator != NULL )
+ {
+ *lpLastPathSeparator = '\0';
+
+ if ( stat( lpTemp, &stat_data) == 0 &&
+ (stat_data.st_mode & S_IFMT) == S_IFREG )
+ {
+ dwLastError = ERROR_DIRECTORY;
+ }
+ }
+ }
+ free(lpTemp);
+ lpTemp = NULL;
+ goto done;
+ }
+
+ find_data->next = find_data->gGlob.gl_pathv;
+ }
+
+ if ( FindNextFileA( (HANDLE)find_data, lpFindFileData ) )
+ {
+ hRet = (HANDLE)find_data;
+ }
+
+done:
+
+ if ( hRet == INVALID_HANDLE_VALUE )
+ {
+ if(NULL != find_data)
+ {
+ // Call globfree only when there is any pattern match
+ // otherwise, HPUX C library segfaults.
+ if (NULL != find_data->gGlob.gl_pathv)
+ {
+ globfree( &(find_data->gGlob) );
+ }
+ free(find_data);
+ }
+ if (dwLastError)
+ {
+ SetLastError(dwLastError);
+ }
+ }
+
+ LOGEXIT("FindFirstFileA returns HANDLE %p\n", hRet );
+ PERF_EXIT(FindFirstFileA);
+ return hRet;
+}
+
+
+/*++
+Function:
+ FindFirstFileW
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+FindFirstFileW(
+ IN LPCWSTR lpFileName,
+ OUT LPWIN32_FIND_DATAW lpFindFileData)
+{
+ // MAX_PATH_FNAME in this context is a file name, not a full path to a file.
+ HANDLE retval = INVALID_HANDLE_VALUE;
+ CHAR FileNameA[MAX_PATH_FNAME];
+ WIN32_FIND_DATAA FindFileDataA;
+
+ PERF_ENTRY(FindFirstFileW);
+ ENTRY("FindFirstFileW(lpFileName=%p (%S), lpFindFileData=%p)\n",
+ lpFileName?lpFileName:W16_NULLSTRING,
+ lpFileName?lpFileName:W16_NULLSTRING, lpFindFileData);
+
+ if(NULL == lpFileName)
+ {
+ ERROR("lpFileName is NULL!\n");
+ SetLastError(ERROR_PATH_NOT_FOUND);
+ goto done;
+ }
+
+ if(NULL == lpFindFileData)
+ {
+ ERROR("lpFindFileData is NULL!\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+ if( 0 == WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, lpFileName, -1,
+ FileNameA, MAX_PATH_FNAME, NULL, NULL))
+ {
+ DWORD dwLastError = GetLastError();
+ if (dwLastError == ERROR_INSUFFICIENT_BUFFER)
+ {
+ WARN("lpFileName is larger than MAX_PATH_FNAME (%d)!\n", MAX_PATH_FNAME);
+ SetLastError(ERROR_FILENAME_EXCED_RANGE);
+ }
+ else
+ {
+ ASSERT("WideCharToMultiByte failed! error is %d\n", dwLastError);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ goto done;
+ }
+
+ retval = FindFirstFileA(FileNameA, &FindFileDataA);
+ if( INVALID_HANDLE_VALUE == retval )
+ {
+ TRACE("FindFirstFileA failed!\n");
+ goto done;
+ }
+
+ lpFindFileData->dwFileAttributes = FindFileDataA.dwFileAttributes;
+ lpFindFileData->dwReserved0 = FindFileDataA.dwReserved0;
+ lpFindFileData->dwReserved1 = FindFileDataA.dwReserved1;
+ lpFindFileData->ftCreationTime = FindFileDataA.ftCreationTime;
+ lpFindFileData->ftLastAccessTime = FindFileDataA.ftLastAccessTime;
+ lpFindFileData->ftLastWriteTime = FindFileDataA.ftLastWriteTime;
+ lpFindFileData->nFileSizeHigh = FindFileDataA.nFileSizeHigh;
+ lpFindFileData->nFileSizeLow = FindFileDataA.nFileSizeLow;
+
+ /* no 8.3 file names */
+ lpFindFileData->cAlternateFileName[0] = 0;
+
+ if( 0 == MultiByteToWideChar(CP_ACP, 0, FindFileDataA.cFileName, -1,
+ lpFindFileData->cFileName, MAX_PATH_FNAME))
+ {
+ DWORD dwLastError = GetLastError();
+ if (dwLastError == ERROR_INSUFFICIENT_BUFFER)
+ {
+ WARN("FindFileDataA.cFileName is larger than MAX_PATH_FNAME (%d)!\n", MAX_PATH_FNAME);
+ SetLastError(ERROR_FILENAME_EXCED_RANGE);
+ }
+ else
+ {
+ ASSERT("MultiByteToWideChar failed! error is %d\n", dwLastError);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ FindClose(retval);
+ retval = INVALID_HANDLE_VALUE;
+ }
+done:
+ LOGEXIT("FindFirstFileW returns HANDLE %p\n", retval);
+ PERF_EXIT(FindFirstFileW);
+ return retval;
+}
+
+
+/*++
+Function:
+ FindNextFileA
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+FindNextFileA(
+ IN HANDLE hFindFile,
+ OUT LPWIN32_FIND_DATAA lpFindFileData)
+{
+ find_obj *find_data;
+
+ BOOL bRet = FALSE;
+ DWORD dwLastError = 0;
+ DWORD Attr;
+
+ PERF_ENTRY(FindNextFileA);
+ ENTRY("FindNextFileA(hFindFile=%p, lpFindFileData=%p)\n",
+ hFindFile, lpFindFileData);
+
+ find_data = (find_obj*)hFindFile;
+
+ if ( hFindFile == INVALID_HANDLE_VALUE ||
+ find_data == NULL ||
+ find_data->self_addr != find_data )
+ {
+ TRACE("FindNextFileA received an invalid handle\n");
+ dwLastError = ERROR_INVALID_HANDLE;
+ goto done;
+ }
+
+ if ( find_data->next)
+ {
+ struct stat stat_data;
+ char ext[_MAX_EXT];
+ int stat_result;
+
+ while (*(find_data->next))
+ {
+ char *path = *(find_data->next);
+
+ TRACE("Found [%s]\n", path);
+
+ // Split the path into a dir and filename.
+ if (_splitpath_s(path, NULL, 0, find_data->dir, _MAX_DIR, find_data->fname, _MAX_PATH, ext, _MAX_EXT) != 0)
+ {
+ ASSERT("_splitpath failed on %s\n", path);
+ dwLastError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+ strcat_s( find_data->fname, sizeof(find_data->fname), ext );
+
+ /* get the attributes, but continue if it fails */
+ Attr = GetFileAttributesA(path);
+ if (Attr == INVALID_FILE_ATTRIBUTES)
+ {
+ WARN("GetFileAttributes returned -1 on file [%s]\n",
+ *(find_data->next));
+ }
+ lpFindFileData->dwFileAttributes = Attr;
+
+ /* Note that cFileName is NOT the relative path */
+ if (strcpy_s( lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName), find_data->fname ) != SAFECRT_SUCCESS)
+ {
+ TRACE("strcpy_s failed!\n");
+ dwLastError = ERROR_FILENAME_EXCED_RANGE;
+ goto done;
+ }
+
+ /* we don't support 8.3 filenames, so just leave it empty */
+ lpFindFileData->cAlternateFileName[0] = 0;
+
+ /* get the filetimes */
+ stat_result = stat(path, &stat_data) == 0 ||
+ lstat(path, &stat_data) == 0;
+
+ find_data->next++;
+
+ if ( stat_result )
+ {
+ lpFindFileData->ftCreationTime =
+ FILEUnixTimeToFileTime( stat_data.st_ctime,
+ ST_CTIME_NSEC(&stat_data) );
+ lpFindFileData->ftLastAccessTime =
+ FILEUnixTimeToFileTime( stat_data.st_atime,
+ ST_ATIME_NSEC(&stat_data) );
+ lpFindFileData->ftLastWriteTime =
+ FILEUnixTimeToFileTime( stat_data.st_mtime,
+ ST_MTIME_NSEC(&stat_data) );
+
+ /* if Unix mtime is greater than atime, return mtime
+ as the last access time */
+ if (CompareFileTime(&lpFindFileData->ftLastAccessTime,
+ &lpFindFileData->ftLastWriteTime) < 0)
+ {
+ lpFindFileData->ftLastAccessTime = lpFindFileData->ftLastWriteTime;
+ }
+
+ /* get file size */
+ lpFindFileData->nFileSizeLow = (DWORD)stat_data.st_size;
+ #if SIZEOF_OFF_T > 4
+ lpFindFileData->nFileSizeHigh =
+ (DWORD)(stat_data.st_size >> 32);
+ #else
+ lpFindFileData->nFileSizeHigh = 0;
+ #endif
+
+ bRet = TRUE;
+ break;
+ }
+ }
+ if(!bRet)
+ {
+ dwLastError = ERROR_NO_MORE_FILES;
+ }
+ }
+ else
+ {
+
+ ASSERT("find_data->next is (mysteriously) NULL\n");
+ }
+
+done:
+ if (dwLastError)
+ {
+ SetLastError(dwLastError);
+ }
+
+ LOGEXIT("FindNextFileA returns BOOL %d\n", bRet);
+ PERF_EXIT(FindNextFileA);
+ return bRet;
+}
+
+
+/*++
+Function:
+ FindNextFileW
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+FindNextFileW(
+ IN HANDLE hFindFile,
+ OUT LPWIN32_FIND_DATAW lpFindFileData)
+{
+ BOOL retval = FALSE;
+ WIN32_FIND_DATAA FindFileDataA;
+
+ PERF_ENTRY(FindNextFileW);
+ ENTRY("FindNextFileW(hFindFile=%p, lpFindFileData=%p)\n",
+ hFindFile, lpFindFileData);
+
+ retval = FindNextFileA(hFindFile, &FindFileDataA);
+ if(!retval)
+ {
+ WARN("FindNextFileA failed!\n");
+ goto done;
+ }
+
+ lpFindFileData->dwFileAttributes = FindFileDataA.dwFileAttributes;
+ lpFindFileData->dwReserved0 = FindFileDataA.dwReserved0;
+ lpFindFileData->dwReserved1 = FindFileDataA.dwReserved1;
+ lpFindFileData->ftCreationTime = FindFileDataA.ftCreationTime;
+ lpFindFileData->ftLastAccessTime = FindFileDataA.ftLastAccessTime;
+ lpFindFileData->ftLastWriteTime = FindFileDataA.ftLastWriteTime;
+ lpFindFileData->nFileSizeHigh = FindFileDataA.nFileSizeHigh;
+ lpFindFileData->nFileSizeLow = FindFileDataA.nFileSizeLow;
+
+ /* no 8.3 file names */
+ lpFindFileData->cAlternateFileName[0] = 0;
+
+ if( 0 == MultiByteToWideChar(CP_ACP, 0, FindFileDataA.cFileName, -1,
+ lpFindFileData->cFileName, MAX_PATH_FNAME))
+ {
+ DWORD dwLastError = GetLastError();
+ if (dwLastError == ERROR_INSUFFICIENT_BUFFER)
+ {
+ WARN("FindFileDataA.cFileName is larger than MAX_PATH_FNAME (%d)!\n", MAX_PATH_FNAME);
+ SetLastError(ERROR_FILENAME_EXCED_RANGE);
+ }
+ else
+ {
+ ASSERT("MultiByteToWideChar failed! error is %d\n", dwLastError);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ retval = FALSE;
+ }
+
+done:
+ LOGEXIT("FindNextFileW returns BOOL %d\n", retval);
+ PERF_EXIT(FindNextFileW);
+ return retval;
+}
+
+
+/*++
+Function:
+ FindClose
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+FindClose(
+ IN OUT HANDLE hFindFile)
+{
+ find_obj *find_data;
+ BOOL hRet = TRUE;
+ DWORD dwLastError = 0;
+
+ PERF_ENTRY(FindClose);
+ ENTRY("FindClose(hFindFile=%p)\n", hFindFile);
+
+ find_data = (find_obj*)hFindFile;
+
+ if ( hFindFile == INVALID_HANDLE_VALUE ||
+ find_data == NULL ||
+ find_data->self_addr != find_data )
+ {
+ ERROR("Invalid find handle\n");
+ hRet = FALSE;
+ dwLastError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ find_data->self_addr = NULL;
+
+ // Call globfree only when there is any pattern match
+ // otherwise, HPUX C library segfaults.
+ if (NULL != find_data->gGlob.gl_pathv)
+ {
+ globfree( &(find_data->gGlob) );
+ }
+ free(find_data);
+
+done:
+ if (dwLastError)
+ {
+ SetLastError(dwLastError);
+ }
+
+ LOGEXIT("FindClose returns BOOL %d\n", hRet);
+ PERF_EXIT(FindClose);
+ return hRet;
+}
+
+
+/*++
+Function:
+ FILEMakePathA
+
+Mimics _makepath from windows, except it's a bit safer.
+Any or all of dir, fname, and ext can be NULL.
+--*/
+static int FILEMakePathA( char *buff,
+ int buff_size,
+ const char *dir,
+ const char *fname,
+ const char *ext )
+{
+ int dir_len = 0;
+ int fname_len = 0;
+ int ext_len = 0;
+ int len;
+ char *p;
+
+ TRACE("Attempting to assemble path from [%s][%s][%s], buff_size = %d\n",
+ dir?dir:"NULL", fname?fname:"NULL", ext?ext:"NULL", buff_size);
+
+ if (dir) dir_len = strlen(dir);
+ if (fname) fname_len = strlen(fname);
+ if (ext) ext_len = strlen(ext);
+
+ len = dir_len + fname_len + ext_len + 1;
+
+ TRACE("Required buffer size is %d bytes\n", len);
+
+ if ( len > buff_size )
+ {
+ ERROR("Buffer is too small (%d bytes), needs %d bytes\n",
+ buff_size, len);
+ return -1;
+ }
+ else
+ {
+ buff[0] = 0;
+
+ p = buff;
+ if (dir_len > 0)
+ {
+ if (strncpy_s( buff, buff_size, dir, dir_len + 1 ) != SAFECRT_SUCCESS)
+ {
+ ERROR("FILEMakePathA: strncpy_s failed\n");
+ return -1;
+ }
+
+ p += dir_len;
+ buff_size-= dir_len;
+ }
+ if (fname_len > 0)
+ {
+ if (strncpy_s( p, buff_size, fname, fname_len + 1 ) != SAFECRT_SUCCESS)
+ {
+ ERROR("FILEMakePathA: strncpy_s failed\n");
+ return -1;
+ }
+
+ p += fname_len;
+ buff_size-=fname_len;
+ }
+ if (ext_len > 0)
+ {
+ if (strncpy_s( p, buff_size, ext, ext_len + 1) != SAFECRT_SUCCESS)
+ {
+ ERROR("FILEMakePathA: strncpy_s failed\n");
+ return -1;
+ }
+ }
+
+ TRACE("FILEMakePathA assembled [%s]\n", buff);
+ return len - 1;
+ }
+}
+
+
+/*++
+ FILEGlobQsortCompare
+
+ Comparison function required by qsort, so that the
+ . and .. directories end up on top of the sorted list
+ of directories.
+--*/
+static int FILEGlobQsortCompare(const void *in_str1, const void *in_str2)
+{
+ char **str1 = (char**)in_str1;
+ char **str2 = (char**)in_str2;
+ const int FIRST_ARG_LESS = -1;
+ const int FIRST_ARG_EQUAL = 0;
+ const int FIRST_ARG_GREATER = 1;
+
+ /* If both strings are equal, return immediately */
+ if (strcmp(*(str1), *(str2)) == 0)
+ {
+ return(FIRST_ARG_EQUAL);
+ }
+
+ /* Have '.' always on top than any other search result */
+ if (strcmp(*(str1), ".") == 0)
+ {
+ return (FIRST_ARG_LESS);
+ }
+ if (strcmp(*(str2), ".") == 0)
+ {
+ return (FIRST_ARG_GREATER);
+ }
+
+ /* Have '..' next on top, over any other search result */
+ if (strcmp(*(str1), "..") == 0)
+ {
+ return (FIRST_ARG_LESS);
+ }
+ if (strcmp(*(str2), "..") == 0)
+ {
+ return (FIRST_ARG_GREATER);
+ }
+
+ /* Finally, let strcmp do the rest for us */
+ return (strcmp(*(str1),*(str2)));
+}
+
+/*++
+Function:
+ FILEEscapeSquareBrackets
+
+Simple helper function to insert backslashes before square brackets
+to prevent glob from using them as wildcards.
+
+note: this functions assumes all backslashes have previously been
+ converted into forwardslashes by _splitpath.
+--*/
+static void FILEEscapeSquareBrackets(char *pattern, char *escaped_pattern)
+{
+ TRACE("Entering FILEEscapeSquareBrackets: [%p (%s)][%p]\n",
+ pattern,pattern,escaped_pattern);
+
+#if _ENABLE_DEBUG_MESSAGES_
+ char *escaped_pattern_base = escaped_pattern;
+#endif // _ENABLE_DEBUG_MESSAGES_
+
+ while(*pattern)
+ {
+ if('[' == *pattern || ']' == *pattern)
+ {
+ *escaped_pattern = '\\';
+ escaped_pattern++;
+ }
+ *escaped_pattern = *pattern;
+ pattern++;
+ escaped_pattern++;
+ }
+ *escaped_pattern='\0';
+
+ TRACE("FILEEscapeSquareBrackets done. escaped_pattern=%s\n",
+ escaped_pattern_base);
+}
+
+
+/*++
+Function:
+ FILEGlobFromSplitPath
+
+Simple wrapper function around glob(3), except that the pattern is accepted
+in broken-down form like _splitpath produces.
+
+ie. calling splitpath on a pattern then calling this function should
+produce the same result as just calling glob() on the pattern.
+--*/
+static int FILEGlobFromSplitPath( const char *dir,
+ const char *fname,
+ const char *ext,
+ int flags,
+ glob_t *pgGlob )
+{
+ int Ret;
+ PathCharString PatternPS;
+ PathCharString EscapedPatternPS;
+ char * Pattern;
+ int length = 0;
+ char * EscapedPattern;
+
+ TRACE("We shall attempt to glob from components [%s][%s][%s]\n",
+ dir?dir:"NULL", fname?fname:"NULL", ext?ext:"NULL");
+
+ if (dir) length = strlen(dir);
+ if (fname) length += strlen(fname);
+ if (ext) length += strlen(ext);
+
+ Pattern = PatternPS.OpenStringBuffer(length);
+ if (NULL == Pattern)
+ {
+ ERROR("Not Enough memory.");
+ return -1;
+ }
+ FILEMakePathA( Pattern, length+1, dir, fname, ext );
+ PatternPS.CloseBuffer(length);
+ TRACE("Assembled Pattern = [%s]\n", Pattern);
+
+ /* special handling is needed to handle the case where
+ filename contains '[' and ']' */
+ EscapedPattern = EscapedPatternPS.OpenStringBuffer(length*2);
+ if (NULL == EscapedPattern)
+ {
+ ERROR("Not Enough memory.");
+ return -1;
+ }
+ FILEEscapeSquareBrackets( Pattern, EscapedPattern);
+ EscapedPatternPS.CloseBuffer(strlen(EscapedPattern));
+#ifdef GLOB_QUOTE
+ flags |= GLOB_QUOTE;
+#endif // GLOB_QUOTE
+ Ret = InternalGlob(EscapedPattern, flags, NULL, pgGlob);
+
+#ifdef GLOB_NOMATCH
+ if (Ret == GLOB_NOMATCH)
+ {
+ // pgGlob->gl_pathc will be 0 in this case. We'll check
+ // the return value to see if an error occurred, so we
+ // don't want to return an error if we simply didn't match
+ // anything.
+ Ret = 0;
+ }
+#endif // GLOB_NOMATCH
+
+ /* Ensure that . and .. are placed in front, and sort the rest */
+ qsort(pgGlob->gl_pathv, pgGlob->gl_pathc, sizeof(char*),
+ FILEGlobQsortCompare);
+ TRACE("Result of glob() is %d\n", Ret);
+
+ return Ret;
+}
+
+
+/*++
+Function:
+ FILEDosGlobA
+
+Generate pathnames matching a DOS globbing pattern. This function has a similar
+prototype to glob(3), and fulfils the same purpose. However, DOS globbing
+is slightly different than Unix in the following ways:
+
+- '.*' at the end of a pattern means "any file extension, or none at all",
+whereas Unix has no concept of file extensions, and will match the '.' like
+any other character
+
+- on Unix, filenames beginning with '.' must be explicitly matched. This is
+not true in DOS
+
+- in DOS, the first two entries (if they match) will be '.' and '..', followed
+by all other matching entries sorted in ASCII order. In Unix, all entries are
+treated equally, so '+file' would appear before '.' and '..'
+
+- DOS globbing will fail if any wildcard characters occur before the last path
+separator
+
+This implementation of glob implements the DOS behavior in all these cases,
+but otherwise attempts to behave exactly like POSIX glob. The only exception
+is its return value -- it returns TRUE if it succeeded (finding matches or
+finding no matches but without any error occurring) or FALSE if any error
+occurs. It calls SetLastError() if it returns FALSE.
+
+Sorting doesn't seem to be consistent on all Windows platform, and it's
+not required for Rotor to have the same sorting alogrithm than Windows 2000.
+This implementation will give slightly different result for the sort list
+than Windows 2000.
+
+--*/
+static BOOL FILEDosGlobA( CPalThread *pthrCurrent,
+ const char *pattern,
+ int flags,
+ glob_t *pgGlob )
+{
+ char Dir[_MAX_DIR];
+ char FilenameBuff[_MAX_FNAME + 1];
+ char *Filename = FilenameBuff + 1;
+ char Ext[_MAX_EXT];
+ int A, B, C;
+ BOOL result = TRUE;
+ int globResult = 0;
+
+ Dir[0] = 0;
+ FilenameBuff[0] = '.';
+ FilenameBuff[1] = 0;
+ Ext[0] = 0;
+
+ _splitpath_s( pattern, NULL, 0, Dir, _MAX_DIR, Filename, _MAX_FNAME+1, Ext, _MAX_EXT);
+
+ /* check to see if _splitpath failed */
+ if ( Filename[0] == 0 )
+ {
+ if ( Dir[0] == 0 )
+ {
+ ERROR("_splitpath failed on path [%s]\n", pattern);
+ }
+ else
+ {
+ ERROR("Pattern contains a trailing backslash\n");
+ }
+ SetLastError(ERROR_PATH_NOT_FOUND);
+ result = FALSE;
+ goto done;
+ }
+
+ TRACE("glob pattern [%s] split into [%s][%s][%s]\n",
+ pattern, Dir, Filename, Ext);
+
+ if ( strchr(Dir, '*') != NULL || strchr(Dir, '?') != NULL )
+ {
+ ERROR("Found wildcard character(s) ('*' and/or '?') before "
+ "last path separator\n");
+ SetLastError(ERROR_PATH_NOT_FOUND);
+ result = FALSE;
+ goto done;
+ }
+
+ if (Dir[0] != 0)
+ {
+ FILEDosToUnixPathA( Dir );
+ }
+
+ /* The meat of the routine happens below. Basically, there are three
+ special things to check for:
+
+ (A) If the extension is _exactly_ '.*', we will need to do two globs,
+ one for 'filename.*' and one for 'filename', EXCEPT if (B) the last
+ character of filename is '*', in which case we can eliminate the
+ extension altogether, since '*.*' and '*' are the same in DOS.
+ (C) If the first character of the filename is '*', we need to do
+ an additional glob for each one we have already done, except with
+ '.' prepended to the filename of the patterns, because in Unix,
+ hidden files need to be matched explicitly.
+
+ We can ignore the extension by calling FILEGlobFromSplitPath with
+ the extension parameter as "", and we can prepend '.' to the
+ filename by using (Filename - 1), since Filename conveniently points
+ to the second character of a buffer which happens to have '.' as
+ its first character.
+ */
+
+ A = strncmp(Ext, ".*", 3) == 0;
+ B = (Filename[strlen(Filename) - 1] == '*');
+ C = (*Filename == '*');
+
+ TRACE("Extension IS%s '.*', filename DOES%s end with '*', "
+ "and filename DOES%s begin with '*'\n",
+ A?"":" NOT", B?"":" NOT", C?"":" NOT");
+
+ if ( !(A && B) )
+ {
+ /* the original pattern */
+ globResult = FILEGlobFromSplitPath(Dir, Filename, Ext, 0, pgGlob);
+ if ( globResult != 0 )
+ {
+ goto done;
+ }
+
+ if (C)
+ {
+ /* the original pattern but '.' prepended to filename */
+ globResult = FILEGlobFromSplitPath(Dir, Filename - 1, Ext,
+ GLOB_APPEND, pgGlob);
+ if ( globResult != 0 )
+ {
+ goto done;
+ }
+ }
+ }
+
+ if (A)
+ {
+ /* if (A && B), this is the first glob() call. The first call
+ to glob must use flags = 0, while proceeding calls should
+ set the GLOB_APPEND flag. */
+ globResult = FILEGlobFromSplitPath(Dir, Filename, "",
+ (A && B)?0:GLOB_APPEND, pgGlob);
+ if ( globResult != 0 )
+ {
+ goto done;
+ }
+
+ if (C)
+ {
+ /* omit the extension and prepend '.' to filename */
+ globResult = FILEGlobFromSplitPath(Dir, Filename - 1, "",
+ GLOB_APPEND, pgGlob);
+ if ( globResult != 0 )
+ {
+ goto done;
+ }
+ }
+ }
+
+done:
+ if (globResult != 0)
+ {
+ if (globResult == GLOB_NOSPACE)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ }
+ else
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ result = FALSE;
+ }
+ TRACE("Returning %d\n", result);
+ return result;
+}
+
+
diff --git a/src/pal/src/file/path.cpp b/src/pal/src/file/path.cpp
new file mode 100644
index 0000000000..c4ef31be32
--- /dev/null
+++ b/src/pal/src/file/path.cpp
@@ -0,0 +1,1624 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ path.c
+
+Abstract:
+
+ Implementation of all functions related to path support
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/malloc.hpp"
+#include "pal/stackstring.hpp"
+
+#include <errno.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(FILE);
+
+
+// In safemath.h, Template SafeInt uses macro _ASSERTE, which need to use variable
+// defdbgchan defined by SET_DEFAULT_DEBUG_CHANNEL. Therefore, the include statement
+// should be placed after the SET_DEFAULT_DEBUG_CHANNEL(FILE)
+#include <safemath.h>
+
+int MaxWCharToAcpLengthRatio = 3;
+/*++
+Function:
+ GetFullPathNameA
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+GetFullPathNameA(
+ IN LPCSTR lpFileName,
+ IN DWORD nBufferLength,
+ OUT LPSTR lpBuffer,
+ OUT LPSTR *lpFilePart)
+{
+ DWORD nReqPathLen, nRet = 0;
+ PathCharString unixPath;
+ LPSTR unixPathBuf;
+ BOOL fullPath = FALSE;
+
+ PERF_ENTRY(GetFullPathNameA);
+ ENTRY("GetFullPathNameA(lpFileName=%p (%s), nBufferLength=%u, lpBuffer=%p, "
+ "lpFilePart=%p)\n",
+ lpFileName?lpFileName:"NULL",
+ lpFileName?lpFileName:"NULL", nBufferLength, lpBuffer, lpFilePart);
+
+ if(NULL == lpFileName)
+ {
+ WARN("lpFileName is NULL\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ /* find out if lpFileName is a partial or full path */
+ if ('\\' == *lpFileName || '/' == *lpFileName)
+ {
+ fullPath = TRUE;
+ }
+
+ if(fullPath)
+ {
+ if( !unixPath.Set(lpFileName, strlen(lpFileName)))
+ {
+ ERROR("Set() failed;\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ }
+ else
+ {
+
+ /* build full path */
+ if(!GetCurrentDirectoryA(unixPath))
+ {
+ /* no reason for this to fail now... */
+ ASSERT("GetCurrentDirectoryA() failed! lasterror is %#xd\n",
+ GetLastError());
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ if (!unixPath.Append("/", 1) ||
+ !unixPath.Append(lpFileName,strlen(lpFileName))
+ )
+ {
+ ERROR("Append failed!\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+
+ }
+
+ unixPathBuf = unixPath.OpenStringBuffer(unixPath.GetCount());
+ /* do conversion to Unix path */
+ FILEDosToUnixPathA( unixPathBuf );
+
+ /* now we can canonicalize this */
+ FILECanonicalizePath(unixPathBuf);
+
+ /* at last, we can figure out how long this path is */
+ nReqPathLen = strlen(unixPathBuf);
+
+ unixPath.CloseBuffer(nReqPathLen);
+ nReqPathLen++;
+ if(nBufferLength < nReqPathLen)
+ {
+ TRACE("reporting insufficient buffer : minimum is %d, caller "
+ "provided %d\n", nReqPathLen, nBufferLength);
+ nRet = nReqPathLen;
+ goto done;
+ }
+
+ nRet = nReqPathLen-1;
+ strcpy_s(lpBuffer, nBufferLength, unixPath);
+
+ /* locate the filename component if caller cares */
+ if(lpFilePart)
+ {
+ *lpFilePart = strrchr(lpBuffer, '/');
+
+ if (*lpFilePart == NULL)
+ {
+ ASSERT("Not able to find '/' in the full path.\n");
+ SetLastError( ERROR_INTERNAL_ERROR );
+ nRet = 0;
+ goto done;
+ }
+ else
+ {
+ (*lpFilePart)++;
+ }
+ }
+
+done:
+ LOGEXIT("GetFullPathNameA returns DWORD %u\n", nRet);
+ PERF_EXIT(GetFullPathNameA);
+ return nRet;
+}
+
+
+/*++
+Function:
+ GetFullPathNameW
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+GetFullPathNameW(
+ IN LPCWSTR lpFileName,
+ IN DWORD nBufferLength,
+ OUT LPWSTR lpBuffer,
+ OUT LPWSTR *lpFilePart)
+{
+ LPSTR fileNameA;
+ CHAR * bufferA;
+ size_t bufferASize = 0;
+ PathCharString bufferAPS;
+ LPSTR lpFilePartA;
+ int fileNameLength;
+ int srcSize;
+ DWORD length;
+ DWORD nRet = 0;
+
+ PERF_ENTRY(GetFullPathNameW);
+ ENTRY("GetFullPathNameW(lpFileName=%p (%S), nBufferLength=%u, lpBuffer=%p"
+ ", lpFilePart=%p)\n",
+ lpFileName?lpFileName:W16_NULLSTRING,
+ lpFileName?lpFileName:W16_NULLSTRING, nBufferLength,
+ lpBuffer, lpFilePart);
+
+
+ fileNameLength = WideCharToMultiByte(CP_ACP, 0, lpFileName,
+ -1, NULL, 0, NULL, NULL);
+ if (fileNameLength == 0)
+ {
+ /* Couldn't convert to ANSI. That's odd. */
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+ else
+ {
+ fileNameA = static_cast<LPSTR>(alloca(fileNameLength));
+ }
+
+ /* Now convert lpFileName to ANSI. */
+ srcSize = WideCharToMultiByte (CP_ACP, 0, lpFileName,
+ -1, fileNameA, fileNameLength,
+ NULL, NULL );
+ if( srcSize == 0 )
+ {
+ DWORD dwLastError = GetLastError();
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ bufferASize = nBufferLength * MaxWCharToAcpLengthRatio;
+ bufferA = bufferAPS.OpenStringBuffer(bufferASize);
+ if (NULL == bufferA)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ length = GetFullPathNameA(fileNameA, bufferASize, bufferA, &lpFilePartA);
+ bufferAPS.CloseBuffer(length);
+
+ if (length == 0 || length > bufferASize)
+ {
+ /* Last error is set by GetFullPathNameA */
+ nRet = length;
+ goto done;
+ }
+
+ /* Convert back to Unicode the result */
+ nRet = MultiByteToWideChar( CP_ACP, 0, bufferA, -1,
+ lpBuffer, nBufferLength );
+
+ if (nRet == 0)
+ {
+ if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
+ {
+ /* get the required length */
+ nRet = MultiByteToWideChar( CP_ACP, 0, bufferA, -1,
+ NULL, 0 );
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ }
+
+ goto done;
+ }
+
+ /* MultiByteToWideChar counts the trailing NULL, but
+ GetFullPathName does not. */
+ nRet--;
+
+ /* now set lpFilePart */
+ if (lpFilePart != NULL)
+ {
+ *lpFilePart = lpBuffer;
+ *lpFilePart += MultiByteToWideChar( CP_ACP, 0, bufferA,
+ lpFilePartA - bufferA, NULL, 0);
+ }
+
+done:
+ LOGEXIT("GetFullPathNameW returns DWORD %u\n", nRet);
+ PERF_EXIT(GetFullPathNameW);
+ return nRet;
+}
+
+
+/*++
+Function:
+ GetLongPathNameW
+
+See MSDN doc.
+
+Note:
+ Since short path names are not implemented (nor supported) in the PAL,
+ this function simply copies the given path into the new buffer.
+
+--*/
+DWORD
+PALAPI
+GetLongPathNameW(
+ IN LPCWSTR lpszShortPath,
+ OUT LPWSTR lpszLongPath,
+ IN DWORD cchBuffer)
+{
+ DWORD dwPathLen = 0;
+
+ PERF_ENTRY(GetLongPathNameW);
+ ENTRY("GetLongPathNameW(lpszShortPath=%p (%S), lpszLongPath=%p (%S), "
+ "cchBuffer=%d\n", lpszShortPath, lpszShortPath, lpszLongPath, lpszLongPath, cchBuffer);
+
+ if ( !lpszShortPath )
+ {
+ ERROR( "lpszShortPath was not a valid pointer.\n" )
+ SetLastError( ERROR_INVALID_PARAMETER );
+ LOGEXIT("GetLongPathNameW returns DWORD 0\n");
+ PERF_EXIT(GetLongPathNameW);
+ return 0;
+ }
+ else if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW( lpszShortPath ))
+ {
+ // last error has been set by GetFileAttributes
+ ERROR( "lpszShortPath does not exist.\n" )
+ LOGEXIT("GetLongPathNameW returns DWORD 0\n");
+ PERF_EXIT(GetLongPathNameW);
+ return 0;
+ }
+
+ /* all lengths are # of TCHAR characters */
+ /* "required size" includes space for the terminating null character */
+ dwPathLen = PAL_wcslen(lpszShortPath)+1;
+
+ /* lpszLongPath == 0 means caller is asking only for size */
+ if ( lpszLongPath )
+ {
+ if ( dwPathLen > cchBuffer )
+ {
+ ERROR("Buffer is too small, need %d characters\n", dwPathLen);
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ } else
+ {
+ if ( lpszShortPath != lpszLongPath )
+ {
+ // Note: MSDN doesn't specify the behavior of GetLongPathName API
+ // if the buffers are overlap.
+ PAL_wcsncpy( lpszLongPath, lpszShortPath, cchBuffer );
+ }
+
+ /* actual size not including terminating null is returned */
+ dwPathLen--;
+ }
+ }
+
+ LOGEXIT("GetLongPathNameW returns DWORD %u\n", dwPathLen);
+ PERF_EXIT(GetLongPathNameW);
+ return dwPathLen;
+}
+
+
+/*++
+Function:
+ GetShortPathNameW
+
+See MSDN doc.
+
+Note:
+ Since short path names are not implemented (nor supported) in the PAL,
+ this function simply copies the given path into the new buffer.
+
+--*/
+DWORD
+PALAPI
+GetShortPathNameW(
+ IN LPCWSTR lpszLongPath,
+ OUT LPWSTR lpszShortPath,
+ IN DWORD cchBuffer)
+{
+ DWORD dwPathLen = 0;
+
+ PERF_ENTRY(GetShortPathNameW);
+ ENTRY("GetShortPathNameW(lpszLongPath=%p (%S), lpszShortPath=%p (%S), "
+ "cchBuffer=%d\n", lpszLongPath, lpszLongPath, lpszShortPath, lpszShortPath, cchBuffer);
+
+ if ( !lpszLongPath )
+ {
+ ERROR( "lpszLongPath was not a valid pointer.\n" )
+ SetLastError( ERROR_INVALID_PARAMETER );
+ LOGEXIT("GetShortPathNameW returns DWORD 0\n");
+ PERF_EXIT(GetShortPathNameW);
+ return 0;
+ }
+ else if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW( lpszLongPath ))
+ {
+ // last error has been set by GetFileAttributes
+ ERROR( "lpszLongPath does not exist.\n" )
+ LOGEXIT("GetShortPathNameW returns DWORD 0\n");
+ PERF_EXIT(GetShortPathNameW);
+ return 0;
+ }
+
+ /* all lengths are # of TCHAR characters */
+ /* "required size" includes space for the terminating null character */
+ dwPathLen = PAL_wcslen(lpszLongPath)+1;
+
+ /* lpszShortPath == 0 means caller is asking only for size */
+ if ( lpszShortPath )
+ {
+ if ( dwPathLen > cchBuffer )
+ {
+ ERROR("Buffer is too small, need %d characters\n", dwPathLen);
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ } else
+ {
+ if ( lpszLongPath != lpszShortPath )
+ {
+ // Note: MSDN doesn't specify the behavior of GetShortPathName API
+ // if the buffers are overlap.
+ PAL_wcsncpy( lpszShortPath, lpszLongPath, cchBuffer );
+ }
+
+ /* actual size not including terminating null is returned */
+ dwPathLen--;
+ }
+ }
+
+ LOGEXIT("GetShortPathNameW returns DWORD %u\n", dwPathLen);
+ PERF_EXIT(GetShortPathNameW);
+ return dwPathLen;
+}
+
+
+/*++
+Function:
+ GetTempPathA
+
+See MSDN.
+
+Notes:
+ On Windows, the temp path is determined by the following steps:
+ 1. The value of the "TMP" environment variable, or if it doesn't exist,
+ 2. The value of the "TEMP" environment variable, or if it doesn't exist,
+ 3. The Windows directory.
+
+ On Unix, we follow in spirit:
+ 1. The value of the "TMPDIR" environment variable, or if it doesn't exist,
+ 2. The /tmp directory.
+ This is the same approach employed by mktemp.
+
+--*/
+DWORD
+PALAPI
+GetTempPathA(
+ IN DWORD nBufferLength,
+ OUT LPSTR lpBuffer)
+{
+ DWORD dwPathLen = 0;
+
+ PERF_ENTRY(GetTempPathA);
+ ENTRY("GetTempPathA(nBufferLength=%u, lpBuffer=%p)\n",
+ nBufferLength, lpBuffer);
+
+ if ( !lpBuffer )
+ {
+ ERROR( "lpBuffer was not a valid pointer.\n" )
+ SetLastError( ERROR_INVALID_PARAMETER );
+ LOGEXIT("GetTempPathA returns DWORD %u\n", dwPathLen);
+ PERF_EXIT(GetTempPathA);
+ return 0;
+ }
+
+ /* Try the TMPDIR environment variable. This is the same env var checked by mktemp. */
+ dwPathLen = GetEnvironmentVariableA("TMPDIR", lpBuffer, nBufferLength);
+ if (dwPathLen > 0)
+ {
+ /* The env var existed. dwPathLen will be the length without null termination
+ * if the entire value was successfully retrieved, or it'll be the length
+ * required to store the value with null termination.
+ */
+ if (dwPathLen < nBufferLength)
+ {
+ /* The environment variable fit in the buffer. Make sure it ends with '/'. */
+ if (lpBuffer[dwPathLen - 1] != '/')
+ {
+ /* If adding the slash would still fit in our provided buffer, do it. Otherwise,
+ * let the caller know how much space would be needed.
+ */
+ if (dwPathLen + 2 <= nBufferLength)
+ {
+ lpBuffer[dwPathLen++] = '/';
+ lpBuffer[dwPathLen] = '\0';
+ }
+ else
+ {
+ dwPathLen += 2;
+ }
+ }
+ }
+ else /* dwPathLen >= nBufferLength */
+ {
+ /* The value is too long for the supplied buffer. dwPathLen will now be the
+ * length required to hold the value, but we don't know whether that value
+ * is going to be '/' terminated. Since we'll need enough space for the '/', and since
+ * a caller would assume that the dwPathLen we return will be sufficient,
+ * we make sure to account for it in dwPathLen even if that means we end up saying
+ * one more byte of space is needed than actually is.
+ */
+ dwPathLen++;
+ }
+ }
+ else /* env var not found or was empty */
+ {
+ /* no luck, use /tmp/ */
+ const char *defaultDir = "/tmp/";
+ int defaultDirLen = strlen(defaultDir);
+ if (defaultDirLen < nBufferLength)
+ {
+ dwPathLen = defaultDirLen;
+ strcpy_s(lpBuffer, nBufferLength, defaultDir);
+ }
+ else
+ {
+ /* get the required length */
+ dwPathLen = defaultDirLen + 1;
+ }
+ }
+
+ if ( dwPathLen >= nBufferLength )
+ {
+ ERROR("Buffer is too small, need space for %d characters including null termination\n", dwPathLen);
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ }
+
+ LOGEXIT("GetTempPathA returns DWORD %u\n", dwPathLen);
+ PERF_EXIT(GetTempPathA);
+ return dwPathLen;
+}
+
+/*++
+Function:
+ GetTempPathW
+
+See MSDN.
+See also the comment for GetTempPathA.
+--*/
+DWORD
+PALAPI
+GetTempPathW(
+ IN DWORD nBufferLength,
+ OUT LPWSTR lpBuffer)
+{
+ PERF_ENTRY(GetTempPathW);
+ ENTRY("GetTempPathW(nBufferLength=%u, lpBuffer=%p)\n",
+ nBufferLength, lpBuffer);
+
+ if (!lpBuffer)
+ {
+ ERROR("lpBuffer was not a valid pointer.\n")
+ SetLastError(ERROR_INVALID_PARAMETER);
+ LOGEXIT("GetTempPathW returns DWORD 0\n");
+ PERF_EXIT(GetTempPathW);
+ return 0;
+ }
+
+ char TempBuffer[nBufferLength > 0 ? nBufferLength : 1];
+ DWORD dwRetVal = GetTempPathA( nBufferLength, TempBuffer );
+
+ if ( dwRetVal >= nBufferLength )
+ {
+ ERROR( "lpBuffer was not large enough.\n" )
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ *lpBuffer = '\0';
+ }
+ else if ( dwRetVal != 0 )
+ {
+ /* Convert to wide. */
+ if ( 0 == MultiByteToWideChar( CP_ACP, 0, TempBuffer, -1,
+ lpBuffer, dwRetVal + 1 ) )
+ {
+ ASSERT( "An error occurred while converting the string to wide.\n" );
+ SetLastError( ERROR_INTERNAL_ERROR );
+ dwRetVal = 0;
+ }
+ }
+ else
+ {
+ ERROR( "The function failed.\n" );
+ *lpBuffer = '\0';
+ }
+
+ LOGEXIT("GetTempPathW returns DWORD %u\n", dwRetVal );
+ PERF_EXIT(GetTempPathW);
+ return dwRetVal;
+}
+
+
+
+/*++
+Function:
+ FileDosToUnixPathA
+
+Abstract:
+ Change a DOS path to a Unix path.
+
+ Replaces '\' by '/', removes any trailing dots on directory/filenames,
+ and changes '*.*' to be equal to '*'
+
+Parameter:
+ IN/OUT lpPath: path to be modified
+--*/
+void
+FILEDosToUnixPathA(
+ LPSTR lpPath)
+{
+ LPSTR p;
+ LPSTR pPointAtDot=NULL;
+ char charBeforeFirstDot='\0';
+
+ TRACE("Original DOS path = [%s]\n", lpPath);
+
+ if (!lpPath)
+ {
+ return;
+ }
+
+ for (p = lpPath; *p; p++)
+ {
+ /* Make the \\ to / switch first */
+ if (*p == '\\')
+ {
+ /* Replace \ with / */
+ *p = '/';
+ }
+
+ if (pPointAtDot)
+ {
+ /* If pPointAtDot is not NULL, it is pointing at the first encountered
+ dot. If we encountered a \, that means it could be a trailing dot */
+ if (*p == '/')
+ {
+ /* If char before the first dot is a '\' or '.' (special case if the
+ dot is the first char in the path) , then we leave it alone,
+ because it is either . or .., otherwise it is a trailing dot
+ pattern and will be truncated */
+ if (charBeforeFirstDot != '.' && charBeforeFirstDot != '/')
+ {
+ memmove(pPointAtDot,p,(strlen(p)*sizeof(char))+1);
+ p = pPointAtDot;
+ }
+ pPointAtDot = NULL; /* Need to reset this */
+ }
+ else if (*p == '*')
+ {
+ /* Check our size before doing anything with our pointers */
+ if ((p - lpPath) >= 3)
+ {
+ /* At this point, we know that there is 1 or more dots and
+ then a star. AND we know the size of our string at this
+ point is at least 3 (so we can go backwards from our pointer
+ safely AND there could possilby be two characters back)
+ So lets check if there is a '*' and a '.' before, if there
+ is, replace just a '*'. Otherwise, reset pPointAtDot to NULL
+ and do nothing */
+ if (p[-2] == '*' &&
+ p[-1] == '.' &&
+ p[0] == '*')
+ {
+ memmove(&(p[-2]),p,(strlen(p)*sizeof(char))+1);
+ }
+
+ pPointAtDot = NULL;
+ }
+ }
+ else if (*p != '.')
+ {
+ /* If we are here, that means that this is NOT a trailing dot,
+ some other character is here, so forget our pointer */
+ pPointAtDot = NULL;
+ }
+ }
+ else
+ {
+ if (*p == '.')
+ {
+ /* If pPointAtDot is NULL, and we encounter a dot, save the pointer */
+ pPointAtDot = p;
+ if (pPointAtDot != lpPath)
+ {
+ charBeforeFirstDot = p[-1];
+ }
+ else
+ {
+ charBeforeFirstDot = lpPath[0];
+ }
+ }
+ }
+ }
+
+ /* If pPointAtDot still points at anything, then we still have trailing dots.
+ Truncate at pPointAtDot, unless the dots are path specifiers (. or ..) */
+ if (pPointAtDot)
+ {
+ /* make sure the trailing dots don't follow a '/', and that they aren't
+ the only thing in the name */
+ if(pPointAtDot != lpPath && *(pPointAtDot-1) != '/')
+ {
+ *pPointAtDot = '\0';
+ }
+ }
+
+ TRACE("Resulting Unix path = [%s]\n", lpPath);
+}
+
+void
+FILEDosToUnixPathA(
+ PathCharString& lpPath)
+{
+
+ SIZE_T len = lpPath.GetCount();
+ LPSTR lpPathBuf = lpPath.OpenStringBuffer(len);
+ FILEDosToUnixPathA(lpPathBuf);
+ lpPath.CloseBuffer(len);
+
+}
+
+/*++
+Function:
+ FileDosToUnixPathW
+
+Abstract:
+ Change a DOS path to a Unix path.
+
+ Replaces '\' by '/', removes any trailing dots on directory/filenames,
+ and changes '*.*' to be equal to '*'
+
+Parameter:
+ IN/OUT lpPath: path to be modified
+--*/
+void
+FILEDosToUnixPathW(
+ LPWSTR lpPath)
+{
+ LPWSTR p;
+ LPWSTR pPointAtDot=NULL;
+ WCHAR charBeforeFirstDot='\0';
+
+ TRACE("Original DOS path = [%S]\n", lpPath);
+
+ if (!lpPath)
+ {
+ return;
+ }
+
+ for (p = lpPath; *p; p++)
+ {
+ /* Make the \\ to / switch first */
+ if (*p == '\\')
+ {
+ /* Replace \ with / */
+ *p = '/';
+ }
+
+ if (pPointAtDot)
+ {
+ /* If pPointAtDot is not NULL, it is pointing at the first encountered
+ dot. If we encountered a \, that means it could be a trailing dot */
+ if (*p == '/')
+ {
+ /* If char before the first dot is a '\' or '.' (special case if the
+ dot is the first char in the path) , then we leave it alone,
+ because it is either . or .., otherwise it is a trailing dot
+ pattern and will be truncated */
+ if (charBeforeFirstDot != '.' && charBeforeFirstDot != '/')
+ {
+ memmove(pPointAtDot,p,((PAL_wcslen(p)+1)*sizeof(WCHAR)));
+ p = pPointAtDot;
+ }
+ pPointAtDot = NULL; /* Need to reset this */
+ }
+ else if (*p == '*')
+ {
+ /* Check our size before doing anything with our pointers */
+ if ((p - lpPath) >= 3)
+ {
+ /* At this point, we know that there is 1 or more dots and
+ then a star. AND we know the size of our string at this
+ point is at least 3 (so we can go backwards from our pointer
+ safely AND there could possilby be two characters back)
+ So lets check if there is a '*' and a '.' before, if there
+ is, replace just a '*'. Otherwise, reset pPointAtDot to NULL
+ and do nothing */
+ if (p[-2] == '*' &&
+ p[-1] == '.' &&
+ p[0] == '*')
+ {
+ memmove(&(p[-2]),p,(PAL_wcslen(p)*sizeof(WCHAR)));
+ }
+
+ pPointAtDot = NULL;
+ }
+ }
+ else if (*p != '.')
+ {
+ /* If we are here, that means that this is NOT a trailing dot,
+ some other character is here, so forget our pointer */
+ pPointAtDot = NULL;
+ }
+ }
+ else
+ {
+ if (*p == '.')
+ {
+ /* If pPointAtDot is NULL, and we encounter a dot, save the pointer */
+ pPointAtDot = p;
+ if (pPointAtDot != lpPath)
+ {
+ charBeforeFirstDot = p[-1];
+ }
+ else
+ {
+ charBeforeFirstDot = lpPath[0];
+ }
+ }
+ }
+ }
+
+ /* If pPointAtDot still points at anything, then we still have trailing dots.
+ Truncate at pPointAtDot, unless the dots are path specifiers (. or ..) */
+ if (pPointAtDot)
+ {
+ /* make sure the trailing dots don't follow a '/', and that they aren't
+ the only thing in the name */
+ if(pPointAtDot != lpPath && *(pPointAtDot-1) != '/')
+ {
+ *pPointAtDot = '\0';
+ }
+ }
+
+ TRACE("Resulting Unix path = [%S]\n", lpPath);
+}
+
+
+/*++
+Function:
+ FileUnixToDosPathA
+
+Abstract:
+ Change a Unix path to a DOS path. Replace '/' by '\'.
+
+Parameter:
+ IN/OUT lpPath: path to be modified
+--*/
+void
+FILEUnixToDosPathA(
+ LPSTR lpPath)
+{
+ LPSTR p;
+
+ TRACE("Original Unix path = [%s]\n", lpPath);
+
+ if (!lpPath)
+ return;
+
+ for (p = lpPath; *p; p++)
+ {
+ if (*p == '/')
+ *p = '\\';
+ }
+
+ TRACE("Resulting DOS path = [%s]\n", lpPath);
+}
+
+
+/*++
+Function:
+ FILEGetDirectoryFromFullPathA
+
+Parse the given path. If it contains a directory part and a file part,
+put the directory part into the supplied buffer, and return the number of
+characters written to the buffer. If the buffer is not large enough,
+return the required size of the buffer including the NULL character. If
+there is no directory part in the path, return 0.
+--*/
+DWORD FILEGetDirectoryFromFullPathA( LPCSTR lpFullPath,
+ DWORD nBufferLength,
+ LPSTR lpBuffer )
+{
+ int full_len, dir_len, i;
+ LPCSTR lpDirEnd;
+ DWORD dwRetLength;
+
+ full_len = lstrlenA( lpFullPath );
+
+ /* look for the first path separator backwards */
+ lpDirEnd = lpFullPath + full_len - 1;
+ while( lpDirEnd >= lpFullPath && *lpDirEnd != '/' && *lpDirEnd != '\\')
+ --lpDirEnd;
+
+ dir_len = lpDirEnd - lpFullPath + 1; /* +1 for fencepost */
+
+ if ( dir_len <= 0 )
+ {
+ dwRetLength = 0;
+ }
+ else if (static_cast<DWORD>(dir_len) >= nBufferLength)
+ {
+ dwRetLength = dir_len + 1; /* +1 for NULL char */
+ }
+ else
+ {
+ /* put the directory into the buffer, including 1 or more
+ trailing path separators */
+ for( i = 0; i < dir_len; ++i )
+ *(lpBuffer + i) = *(lpFullPath + i);
+
+ *(lpBuffer + i) = '\0';
+
+ dwRetLength = dir_len;
+ }
+
+ return( dwRetLength );
+}
+
+/*++
+Function:
+ FILEGetFileNameFromFullPath
+
+Given a full path, return a pointer to the first char of the filename part.
+--*/
+LPCSTR FILEGetFileNameFromFullPathA( LPCSTR lpFullPath )
+{
+ int DirLen = FILEGetDirectoryFromFullPathA( lpFullPath, 0, NULL );
+
+ if ( DirLen > 0 )
+ {
+ return lpFullPath + DirLen - 1;
+ }
+ else
+ {
+ return lpFullPath;
+ }
+}
+
+/*++
+FILECanonicalizePath
+ Removes all instances of '/./', '/../' and '//' from an absolute path.
+
+Parameters:
+ LPSTR lpUnixPath : absolute path to modify, in Unix format
+
+(no return value)
+
+Notes :
+-behavior is undefined if path is not absolute
+-the order of steps *is* important: /one/./../two would give /one/two
+ instead of /two if step 3 was done before step 2
+-reason for this function is that GetFullPathName can't use realpath(), since
+ realpath() requires the given path to be valid and GetFullPathName does not.
+--*/
+void FILECanonicalizePath(LPSTR lpUnixPath)
+{
+ LPSTR slashslashptr;
+ LPSTR dotdotptr;
+ LPSTR slashdotptr;
+ LPSTR slashptr;
+
+ /* step 1 : replace '//' sequences by a single '/' */
+
+ slashslashptr = lpUnixPath;
+ while(1)
+ {
+ slashslashptr = strstr(slashslashptr,"//");
+ if(NULL == slashslashptr)
+ {
+ break;
+ }
+ /* remove extra '/' */
+ TRACE("stripping '//' from %s\n", lpUnixPath);
+ memmove(slashslashptr,slashslashptr+1,strlen(slashslashptr+1)+1);
+ }
+
+ /* step 2 : replace '/./' sequences by a single '/' */
+
+ slashdotptr = lpUnixPath;
+ while(1)
+ {
+ slashdotptr = strstr(slashdotptr,"/./");
+ if(NULL == slashdotptr)
+ {
+ break;
+ }
+ /* strip the extra '/.' */
+ TRACE("removing '/./' sequence from %s\n", lpUnixPath);
+ memmove(slashdotptr,slashdotptr+2,strlen(slashdotptr+2)+1);
+ }
+
+ /* step 3 : replace '/<name>/../' sequences by a single '/' */
+
+ while(1)
+ {
+ dotdotptr = strstr(lpUnixPath,"/../");
+ if(NULL == dotdotptr)
+ {
+ break;
+ }
+ if(dotdotptr == lpUnixPath)
+ {
+ /* special case : '/../' at the beginning of the path are replaced
+ by a single '/' */
+ TRACE("stripping leading '/../' from %s\n", lpUnixPath);
+ memmove(lpUnixPath, lpUnixPath+3,strlen(lpUnixPath+3)+1);
+ continue;
+ }
+
+ /* null-terminate the string before the '/../', so that strrchr will
+ start looking right before it */
+ *dotdotptr = '\0';
+ slashptr = strrchr(lpUnixPath,'/');
+ if(NULL == slashptr)
+ {
+ /* this happens if this function was called with a relative path.
+ don't do that. */
+ ASSERT("can't find leading '/' before '/../ sequence\n");
+ break;
+ }
+ TRACE("removing '/<dir>/../' sequence from %s\n", lpUnixPath);
+ memmove(slashptr,dotdotptr+3,strlen(dotdotptr+3)+1);
+ }
+
+ /* step 4 : remove a trailing '/..' */
+
+ dotdotptr = strstr(lpUnixPath,"/..");
+ if(dotdotptr == lpUnixPath)
+ {
+ /* if the full path is simply '/..', replace it by '/' */
+ lpUnixPath[1] = '\0';
+ }
+ else if(NULL != dotdotptr && '\0' == dotdotptr[3])
+ {
+ *dotdotptr = '\0';
+ slashptr = strrchr(lpUnixPath,'/');
+ if(NULL != slashptr)
+ {
+ /* make sure the last slash isn't the root */
+ if (slashptr == lpUnixPath)
+ {
+ lpUnixPath[1] = '\0';
+ }
+ else
+ {
+ *slashptr = '\0';
+ }
+ }
+ }
+
+ /* step 5 : remove a traling '/.' */
+
+ slashdotptr = strstr(lpUnixPath,"/.");
+ if (slashdotptr != NULL && slashdotptr[2] == '\0')
+ {
+ if(slashdotptr == lpUnixPath)
+ {
+ // if the full path is simply '/.', replace it by '/' */
+ lpUnixPath[1] = '\0';
+ }
+ else
+ {
+ *slashdotptr = '\0';
+ }
+ }
+}
+
+
+/*++
+Function:
+ SearchPathA
+
+See MSDN doc.
+
+PAL-specific notes :
+-lpPath must be non-NULL; path delimiters are platform-dependent (':' for Unix)
+-lpFileName must be non-NULL, may be an absolute path
+-lpExtension must be NULL
+-lpFilePart (if non-NULL) doesn't need to be used (but we do)
+--*/
+DWORD
+PALAPI
+SearchPathA(
+ IN LPCSTR lpPath,
+ IN LPCSTR lpFileName,
+ IN LPCSTR lpExtension,
+ IN DWORD nBufferLength,
+ OUT LPSTR lpBuffer,
+ OUT LPSTR *lpFilePart
+ )
+{
+ DWORD nRet = 0;
+ CHAR * FullPath;
+ size_t FullPathLength = 0;
+ PathCharString FullPathPS;
+ PathCharString CanonicalFullPathPS;
+ CHAR * CanonicalFullPath;
+ LPCSTR pPathStart;
+ LPCSTR pPathEnd;
+ size_t PathLength;
+ size_t FileNameLength;
+ DWORD length;
+ DWORD dw;
+
+ PERF_ENTRY(SearchPathA);
+ ENTRY("SearchPathA(lpPath=%p (%s), lpFileName=%p (%s), lpExtension=%p, "
+ "nBufferLength=%u, lpBuffer=%p, lpFilePart=%p)\n",
+ lpPath,
+ lpPath, lpFileName, lpFileName, lpExtension, nBufferLength, lpBuffer,
+ lpFilePart);
+
+ /* validate parameters */
+
+ if(NULL == lpPath)
+ {
+ ASSERT("lpPath may not be NULL\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+ if(NULL == lpFileName)
+ {
+ ASSERT("lpFileName may not be NULL\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+ if(NULL != lpExtension)
+ {
+ ASSERT("lpExtension must be NULL, is %p instead\n", lpExtension);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ FileNameLength = strlen(lpFileName);
+
+ /* special case : if file name contains absolute path, don't search the
+ provided path */
+ if('\\' == lpFileName[0] || '/' == lpFileName[0])
+ {
+ /* Canonicalize the path to deal with back-to-back '/', etc. */
+ length = FileNameLength;
+ CanonicalFullPath = CanonicalFullPathPS.OpenStringBuffer(length);
+ if (NULL == CanonicalFullPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ dw = GetFullPathNameA(lpFileName, length+1, CanonicalFullPath, NULL);
+ CanonicalFullPathPS.CloseBuffer(dw);
+
+ if (length+1 < dw)
+ {
+ CanonicalFullPath = CanonicalFullPathPS.OpenStringBuffer(dw-1);
+ if (NULL == CanonicalFullPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ dw = GetFullPathNameA(lpFileName, dw,
+ CanonicalFullPath, NULL);
+ CanonicalFullPathPS.CloseBuffer(dw);
+ }
+
+ if (dw == 0)
+ {
+ WARN("couldn't canonicalize path <%s>, error is %#x. failing.\n",
+ lpFileName, GetLastError());
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ /* see if the file exists */
+ if(0 == access(CanonicalFullPath, F_OK))
+ {
+ /* found it */
+ nRet = dw;
+ }
+ }
+ else
+ {
+ LPCSTR pNextPath;
+
+ pNextPath = lpPath;
+
+ while (*pNextPath)
+ {
+ pPathStart = pNextPath;
+
+ /* get a pointer to the end of the first path in pPathStart */
+ pPathEnd = strchr(pPathStart, ':');
+ if (!pPathEnd)
+ {
+ pPathEnd = pPathStart + strlen(pPathStart);
+ /* we want to break out of the loop after this pass, so let
+ *pNextPath be '\0' */
+ pNextPath = pPathEnd;
+ }
+ else
+ {
+ /* point to the next component in the path string */
+ pNextPath = pPathEnd+1;
+ }
+
+ PathLength = pPathEnd-pPathStart;
+
+ if(0 == PathLength)
+ {
+ /* empty component : there were 2 consecutive ':' */
+ continue;
+ }
+
+ /* Construct a pathname by concatenating one path from lpPath, '/'
+ and lpFileName */
+ FullPathLength = PathLength + FileNameLength;
+ FullPath = FullPathPS.OpenStringBuffer(FullPathLength+1);
+ if (NULL == FullPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ memcpy(FullPath, pPathStart, PathLength);
+ FullPath[PathLength] = '/';
+ if (strcpy_s(&FullPath[PathLength+1], FullPathLength+1-PathLength, lpFileName) != SAFECRT_SUCCESS)
+ {
+ ERROR("strcpy_s failed!\n");
+ SetLastError( ERROR_FILENAME_EXCED_RANGE );
+ nRet = 0;
+ goto done;
+ }
+
+ FullPathPS.CloseBuffer(FullPathLength+1);
+ /* Canonicalize the path to deal with back-to-back '/', etc. */
+ length = MAX_LONGPATH; //Use it for first try
+ CanonicalFullPath = CanonicalFullPathPS.OpenStringBuffer(length);
+ if (NULL == CanonicalFullPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ dw = GetFullPathNameA(FullPath, length+1,
+ CanonicalFullPath, NULL);
+ CanonicalFullPathPS.CloseBuffer(dw);
+
+ if (length+1 < dw)
+ {
+ CanonicalFullPath = CanonicalFullPathPS.OpenStringBuffer(dw-1);
+ dw = GetFullPathNameA(FullPath, dw,
+ CanonicalFullPath, NULL);
+ CanonicalFullPathPS.CloseBuffer(dw);
+ }
+
+ if (dw == 0)
+ {
+ /* Call failed - possibly low memory. Skip the path */
+ WARN("couldn't canonicalize path <%s>, error is %#x. "
+ "skipping it\n", FullPath, GetLastError());
+ continue;
+ }
+
+ /* see if the file exists */
+ if(0 == access(CanonicalFullPath, F_OK))
+ {
+ /* found it */
+ nRet = dw;
+ break;
+ }
+ }
+ }
+
+ if (nRet == 0)
+ {
+ /* file not found anywhere; say so. in Windows, this always seems to say
+ FILE_NOT_FOUND, even if path doesn't exist */
+ SetLastError(ERROR_FILE_NOT_FOUND);
+ }
+ else
+ {
+ if (nRet < nBufferLength)
+ {
+ if(NULL == lpBuffer)
+ {
+ /* Windows merily crashes here, but let's not */
+ ERROR("caller told us buffer size was %d, but buffer is NULL\n",
+ nBufferLength);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ nRet = 0;
+ goto done;
+ }
+
+ if (strcpy_s(lpBuffer, nBufferLength, CanonicalFullPath) != SAFECRT_SUCCESS)
+ {
+ ERROR("strcpy_s failed!\n");
+ SetLastError( ERROR_FILENAME_EXCED_RANGE );
+ nRet = 0;
+ goto done;
+ }
+
+ if(NULL != lpFilePart)
+ {
+ *lpFilePart = strrchr(lpBuffer,'/');
+ if(NULL == *lpFilePart)
+ {
+ ASSERT("no '/' in full path!\n");
+ }
+ else
+ {
+ /* point to character after last '/' */
+ (*lpFilePart)++;
+ }
+ }
+ }
+ else
+ {
+ /* if buffer is too small, report required length, including
+ terminating null */
+ nRet++;
+ }
+ }
+done:
+ LOGEXIT("SearchPathA returns DWORD %u\n", nRet);
+ PERF_EXIT(SearchPathA);
+ return nRet;
+}
+
+
+/*++
+Function:
+ SearchPathW
+
+See MSDN doc.
+
+PAL-specific notes :
+-lpPath must be non-NULL; path delimiters are platform-dependent (':' for Unix)
+-lpFileName must be non-NULL, may be an absolute path
+-lpExtension must be NULL
+-lpFilePart (if non-NULL) doesn't need to be used (but we do)
+--*/
+DWORD
+PALAPI
+SearchPathW(
+ IN LPCWSTR lpPath,
+ IN LPCWSTR lpFileName,
+ IN LPCWSTR lpExtension,
+ IN DWORD nBufferLength,
+ OUT LPWSTR lpBuffer,
+ OUT LPWSTR *lpFilePart
+ )
+{
+ DWORD nRet = 0;
+ WCHAR * FullPath;
+ size_t FullPathLength = 0;
+ PathWCharString FullPathPS;
+ LPCWSTR pPathStart;
+ LPCWSTR pPathEnd;
+ size_t PathLength;
+ size_t FileNameLength;
+ DWORD dw;
+ DWORD length;
+ char * AnsiPath;
+ PathCharString AnsiPathPS;
+ size_t CanonicalPathLength;
+ int canonical_size;
+ WCHAR * CanonicalPath;
+ PathWCharString CanonicalPathPS;
+
+ PERF_ENTRY(SearchPathW);
+ ENTRY("SearchPathW(lpPath=%p (%S), lpFileName=%p (%S), lpExtension=%p, "
+ "nBufferLength=%u, lpBuffer=%p, lpFilePart=%p)\n",
+ lpPath,
+ lpPath, lpFileName, lpFileName, lpExtension, nBufferLength, lpBuffer,
+ lpFilePart);
+
+ /* validate parameters */
+
+ if(NULL == lpPath)
+ {
+ ASSERT("lpPath may not be NULL\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+ if(NULL == lpFileName)
+ {
+ ASSERT("lpFileName may not be NULL\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+ if(NULL != lpExtension)
+ {
+ ASSERT("lpExtension must be NULL, is %p instead\n", lpExtension);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ /* special case : if file name contains absolute path, don't search the
+ provided path */
+ if('\\' == lpFileName[0] || '/' == lpFileName[0])
+ {
+ /* Canonicalize the path to deal with back-to-back '/', etc. */
+ length = MAX_LONGPATH; //Use it for first try
+ CanonicalPath = CanonicalPathPS.OpenStringBuffer(length);
+ if (NULL == CanonicalPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ dw = GetFullPathNameW(lpFileName, length+1, CanonicalPath, NULL);
+ CanonicalPathPS.CloseBuffer(dw);
+ if (length+1 < dw)
+ {
+ CanonicalPath = CanonicalPathPS.OpenStringBuffer(dw-1);
+ if (NULL == CanonicalPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ dw = GetFullPathNameW(lpFileName, dw, CanonicalPath, NULL);
+ CanonicalPathPS.CloseBuffer(dw);
+ }
+
+ if (dw == 0)
+ {
+ WARN("couldn't canonicalize path <%S>, error is %#x. failing.\n",
+ lpPath, GetLastError());
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ /* see if the file exists */
+ CanonicalPathLength = (PAL_wcslen(CanonicalPath)+1) * MaxWCharToAcpLengthRatio;
+ AnsiPath = AnsiPathPS.OpenStringBuffer(CanonicalPathLength);
+ if (NULL == AnsiPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ canonical_size = WideCharToMultiByte(CP_ACP, 0, CanonicalPath, -1,
+ AnsiPath, CanonicalPathLength, NULL, NULL);
+ AnsiPathPS.CloseBuffer(canonical_size);
+
+ if(0 == access(AnsiPath, F_OK))
+ {
+ /* found it */
+ nRet = dw;
+ }
+ }
+ else
+ {
+ LPCWSTR pNextPath;
+
+ pNextPath = lpPath;
+
+ FileNameLength = PAL_wcslen(lpFileName);
+
+ while (*pNextPath)
+ {
+ pPathStart = pNextPath;
+
+ /* get a pointer to the end of the first path in pPathStart */
+ pPathEnd = PAL_wcschr(pPathStart, ':');
+ if (!pPathEnd)
+ {
+ pPathEnd = pPathStart + PAL_wcslen(pPathStart);
+ /* we want to break out of the loop after this pass, so let
+ *pNextPath be '\0' */
+ pNextPath = pPathEnd;
+ }
+ else
+ {
+ /* point to the next component in the path string */
+ pNextPath = pPathEnd+1;
+ }
+
+ PathLength = pPathEnd-pPathStart;
+
+ if(0 == PathLength)
+ {
+ /* empty component : there were 2 consecutive ':' */
+ continue;
+ }
+
+ /* Construct a pathname by concatenating one path from lpPath, '/'
+ and lpFileName */
+ FullPathLength = PathLength + FileNameLength;
+ FullPath = FullPathPS.OpenStringBuffer(FullPathLength+1);
+ if (NULL == FullPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ memcpy(FullPath, pPathStart, PathLength*sizeof(WCHAR));
+ FullPath[PathLength] = '/';
+ PAL_wcscpy(&FullPath[PathLength+1], lpFileName);
+
+ FullPathPS.CloseBuffer(FullPathLength+1);
+
+ /* Canonicalize the path to deal with back-to-back '/', etc. */
+ length = MAX_LONGPATH; //Use it for first try
+ CanonicalPath = CanonicalPathPS.OpenStringBuffer(length);
+ if (NULL == CanonicalPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ dw = GetFullPathNameW(FullPath, length+1,
+ CanonicalPath, NULL);
+ CanonicalPathPS.CloseBuffer(dw);
+
+ if (length+1 < dw)
+ {
+ CanonicalPath = CanonicalPathPS.OpenStringBuffer(dw-1);
+ if (NULL == CanonicalPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ dw = GetFullPathNameW(FullPath, dw, CanonicalPath, NULL);
+ CanonicalPathPS.CloseBuffer(dw);
+ }
+
+ if (dw == 0)
+ {
+ /* Call failed - possibly low memory. Skip the path */
+ WARN("couldn't canonicalize path <%S>, error is %#x. "
+ "skipping it\n", FullPath, GetLastError());
+ continue;
+ }
+
+ /* see if the file exists */
+ CanonicalPathLength = (PAL_wcslen(CanonicalPath)+1) * MaxWCharToAcpLengthRatio;
+ AnsiPath = AnsiPathPS.OpenStringBuffer(CanonicalPathLength);
+ if (NULL == AnsiPath)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ canonical_size = WideCharToMultiByte(CP_ACP, 0, CanonicalPath, -1,
+ AnsiPath, CanonicalPathLength, NULL, NULL);
+ AnsiPathPS.CloseBuffer(canonical_size);
+
+ if(0 == access(AnsiPath, F_OK))
+ {
+ /* found it */
+ nRet = dw;
+ break;
+ }
+ }
+ }
+
+ if (nRet == 0)
+ {
+ /* file not found anywhere; say so. in Windows, this always seems to say
+ FILE_NOT_FOUND, even if path doesn't exist */
+ SetLastError(ERROR_FILE_NOT_FOUND);
+ }
+ else
+ {
+ /* find out the required buffer size, copy path to buffer if it's
+ large enough */
+ nRet = PAL_wcslen(CanonicalPath)+1;
+ if(nRet <= nBufferLength)
+ {
+ if(NULL == lpBuffer)
+ {
+ /* Windows merily crashes here, but let's not */
+ ERROR("caller told us buffer size was %d, but buffer is NULL\n",
+ nBufferLength);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ nRet = 0;
+ goto done;
+ }
+ PAL_wcscpy(lpBuffer, CanonicalPath);
+
+ /* don't include the null-terminator in the count if buffer was
+ large enough */
+ nRet--;
+
+ if(NULL != lpFilePart)
+ {
+ *lpFilePart = PAL_wcsrchr(lpBuffer, '/');
+ if(NULL == *lpFilePart)
+ {
+ ASSERT("no '/' in full path!\n");
+ }
+ else
+ {
+ /* point to character after last '/' */
+ (*lpFilePart)++;
+ }
+ }
+ }
+ }
+done:
+ LOGEXIT("SearchPathW returns DWORD %u\n", nRet);
+ PERF_EXIT(SearchPathW);
+ return nRet;
+}
+
+/*++
+Function:
+ PathFindFileNameW
+
+See MSDN doc.
+--*/
+LPWSTR
+PALAPI
+PathFindFileNameW(
+ IN LPCWSTR pPath
+ )
+{
+ PERF_ENTRY(PathFindFileNameW);
+ ENTRY("PathFindFileNameW(pPath=%p (%S))\n",
+ pPath?pPath:W16_NULLSTRING,
+ pPath?pPath:W16_NULLSTRING);
+
+ LPWSTR ret = (LPWSTR)pPath;
+ if (ret != NULL && *ret != W('\0'))
+ {
+ ret = PAL_wcschr(ret, W('\0')) - 1;
+ if (ret > pPath && *ret == W('/'))
+ {
+ ret--;
+ }
+ while (ret > pPath && *ret != W('/'))
+ {
+ ret--;
+ }
+ if (*ret == W('/') && *(ret + 1) != W('\0'))
+ {
+ ret++;
+ }
+ }
+
+ LOGEXIT("PathFindFileNameW returns %S\n", ret);
+ PERF_EXIT(PathFindFileNameW);
+ return ret;
+}
diff --git a/src/pal/src/file/shmfilelockmgr.cpp b/src/pal/src/file/shmfilelockmgr.cpp
new file mode 100644
index 0000000000..a586abddbd
--- /dev/null
+++ b/src/pal/src/file/shmfilelockmgr.cpp
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ shmfilelockmgr.cpp
+
+Abstract:
+ Shared memory based file lock manager
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+#include "pal/dbgmsg.h"
+#include "shmfilelockmgr.hpp"
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(FILE);
+
+PAL_ERROR
+FILEAddNewLockedRgn(
+ SHMFILELOCKS* fileLocks,
+ PVOID pvControllerInstance,
+ SHMFILELOCKRGNS *insertAfter,
+ UINT64 lockRgnStart,
+ UINT64 nbBytesToLock,
+ LOCK_TYPE lockType
+ );
+
+PAL_ERROR
+FILELockFileRegion(
+ SHMPTR shmFileLocks,
+ PVOID pvControllerInstance,
+ UINT64 lockRgnStart,
+ UINT64 nbBytesToLock,
+ LOCK_TYPE lockAction
+ );
+
+PAL_ERROR
+FILEUnlockFileRegion(
+ SHMPTR shmFileLocks,
+ PVOID pvControllerInstance,
+ UINT64 unlockRgnStart,
+ UINT64 nbBytesToUnlock,
+ LOCK_TYPE unlockType
+ );
+
+void
+FILECleanUpLockedRgn(
+ SHMPTR shmFileLocks,
+ DWORD dwAccessRights,
+ PVOID pvControllerInstance
+ );
+
+PAL_ERROR
+FILEGetSHMFileLocks(
+ LPCSTR filename,
+ SHMPTR *pshmFileLocks,
+ BOOL noCreate
+ );
+
+/* return TRUE if LockToTest region is behind lockRgn, FALSE otherwise */
+#define IS_LOCK_BEFORE(LockToTest, lockRgn) \
+ (((LockToTest)->lockRgnStart + (LockToTest)->nbBytesLocked) <= \
+ (lockRgn)->lockRgnStart)
+
+/* return TRUE if LockToTest region intersect with lockRgn, FALSE otherwise */
+#define IS_LOCK_INTERSECT(LockToTest, lockRgn) \
+ (!IS_LOCK_BEFORE(LockToTest, lockRgn) && !IS_LOCK_BEFORE(lockRgn, LockToTest))
+
+/* return TRUE if LockToTest region and lockRgn have the same file pointer and
+ the same process Id, FALSE otherwise */
+#define IS_LOCK_HAVE_SAME_OWNER(LockToTest, lockRgn) \
+ (((LockToTest)->pvControllerInstance == (lockRgn)->pvControllerInstance) && \
+ ((LockToTest)->processId == (lockRgn)->processId))
+
+/* return TRUE if LockToTest region and lockRgn represent the same lock,
+ FALSE otherwise*/
+#define IS_LOCK_EQUAL(LockToTest, lockRgn) \
+ (((LockToTest)->processId == (lockRgn)->processId) && \
+ ((LockToTest)->pvControllerInstance == (lockRgn)->pvControllerInstance) && \
+ ((LockToTest)->lockRgnStart == (lockRgn)->lockRgnStart) && \
+ ((LockToTest)->nbBytesLocked == (lockRgn)->nbBytesLocked) && \
+ ((LockToTest)->lockType == (lockRgn)->lockType))
+
+PAL_ERROR
+CSharedMemoryFileLockMgr::GetLockControllerForFile(
+ CPalThread *pThread, // IN, OPTIONAL
+ LPCSTR szFileName,
+ DWORD dwAccessRights,
+ DWORD dwShareMode,
+ IFileLockController **ppLockController // OUT
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ SHMPTR shmFileLocks = SHMNULL;
+ SHMFILELOCKS* fileLocks = NULL;
+ CSharedMemoryFileLockController *pController = NULL;
+
+ SHMLock();
+
+ palError = FILEGetSHMFileLocks(szFileName, &shmFileLocks, FALSE);
+ if (NO_ERROR != palError)
+ {
+ goto GetLockControllerForFileExit;
+ }
+
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, fileLocks, shmFileLocks) == FALSE || fileLocks == NULL)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto GetLockControllerForFileExit;
+ }
+
+ if(SHARE_MODE_NOT_INITALIZED == fileLocks->share_mode)
+ {
+ /* this is the first time this file is open */
+ fileLocks->share_mode = (int) dwShareMode;
+ }
+ /* start checking for dwDesired access and dwShareMode conditions */
+ else if(0 == fileLocks->share_mode)
+ {
+ /* file is exclusively locked */
+ palError = ERROR_SHARING_VIOLATION;
+ goto GetLockControllerForFileExit;
+ }
+ /* check for if the desired access is allowed by the share mode */
+ else if( (dwAccessRights & GENERIC_READ) &&
+ !(fileLocks->share_mode & FILE_SHARE_READ) )
+ {
+ palError = ERROR_SHARING_VIOLATION;
+ goto GetLockControllerForFileExit;
+ }
+ else if( (dwAccessRights & GENERIC_WRITE) &&
+ !(fileLocks->share_mode & FILE_SHARE_WRITE) )
+ {
+ palError = ERROR_SHARING_VIOLATION;
+ goto GetLockControllerForFileExit;
+ }
+ /* The case when changing to a conflicting share mode is particular.
+ The general rule is: changing from conflicting share mode is invalid
+ (i.e changing from FILE_SHARE_WRITE to FILE_SHARE_READ is invalid).
+ However, if one of the share flags is the same
+ (i.e changing from FILE_SHARE_WRITE to FILE_SHARE_READ | FILE_SHARE_WRITE)
+ the result is valid. (Please note that FILE_SHARE_READ is ignored
+ in this case).
+ */
+ else if( (dwShareMode & FILE_SHARE_READ) &&
+ !(dwShareMode & FILE_SHARE_WRITE) &&
+ !(fileLocks->share_mode & FILE_SHARE_READ))
+
+ {
+ palError = ERROR_SHARING_VIOLATION;
+ goto GetLockControllerForFileExit;
+ }
+ else if( (dwShareMode & FILE_SHARE_WRITE) &&
+ !(dwShareMode & FILE_SHARE_READ) &&
+ !(fileLocks->share_mode & FILE_SHARE_WRITE))
+ {
+ palError = ERROR_SHARING_VIOLATION;
+ goto GetLockControllerForFileExit;
+ }
+ /* Changing to a less permissive sharing permissions is valid
+ if the file handle doesn't have an access right that conflicts with
+ the sharing permissions we are trying to set
+ (ex: changing from FILE_SHARE_READ|FILE_SHARE_WRITE to FILE_SHARE_WRITE
+ isn't valid if the file descriptor still has a GENERIC_READ permission).
+ */
+ else if( (fileLocks->nbReadAccess) &&
+ !(dwShareMode & FILE_SHARE_READ) )
+ {
+ palError = ERROR_SHARING_VIOLATION;
+ goto GetLockControllerForFileExit;
+ }
+ else if( (fileLocks->nbWriteAccess) &&
+ !(dwShareMode & FILE_SHARE_WRITE) )
+ {
+ palError = ERROR_SHARING_VIOLATION;
+ goto GetLockControllerForFileExit;
+ }
+
+ /* we are trying to change to a less restrictive sharing permission set
+ keep the current permissions */
+ if( (dwShareMode & FILE_SHARE_READ) &&
+ !(fileLocks->share_mode & FILE_SHARE_READ) )
+ {
+ dwShareMode = fileLocks->share_mode;
+ }
+
+ if( (dwShareMode & FILE_SHARE_WRITE) &&
+ !(fileLocks->share_mode & FILE_SHARE_WRITE) )
+ {
+ dwShareMode = fileLocks->share_mode;
+ }
+
+ pController = InternalNew<CSharedMemoryFileLockController>(dwAccessRights, shmFileLocks);
+ if (NULL == pController)
+ {
+ palError = ERROR_OUTOFMEMORY;
+ goto GetLockControllerForFileExit;
+ }
+
+ //
+ // pController now owns the shared memory pointer, so make sure we
+ // don't attempt to free it below.
+ //
+
+ shmFileLocks = SHMNULL;
+
+ /* set the share mode again, it's possible that the share mode is now more
+ restrictive than the previous mode set. */
+ fileLocks->share_mode = dwShareMode;
+ if( dwAccessRights & GENERIC_READ )
+ {
+ fileLocks->nbReadAccess++;
+ }
+ if( dwAccessRights & GENERIC_WRITE )
+ {
+ fileLocks->nbWriteAccess++;
+ }
+
+ // ************** NOTE **************
+ // If you add any error paths after this point you must communicate the value of dwAccessRights to
+ // FILECleanUpLockedRgn() in the cleanup code below so that it can correctly undo the changes to
+ // fileLocks->nbReadAccess and nbWriteAccess made above.
+ // ************** NOTE **************
+
+GetLockControllerForFileExit:
+
+ if (NO_ERROR == palError)
+ {
+ *ppLockController = pController;
+ }
+ else
+ {
+ if (NULL != pController)
+ {
+ pController->ReleaseController();
+ }
+
+ if (SHMNULL != shmFileLocks)
+ {
+ FILECleanUpLockedRgn(
+ shmFileLocks,
+ 0,
+ NULL
+ );
+ }
+ }
+
+ SHMRelease();
+
+ return palError;
+}
+
+PAL_ERROR
+CSharedMemoryFileLockMgr::GetFileShareModeForFile(
+ LPCSTR szFileName,
+ DWORD* pdwShareMode)
+{
+ PAL_ERROR palError = NO_ERROR;
+ *pdwShareMode = SHARE_MODE_NOT_INITALIZED;
+ SHMPTR shmFileLocks = SHMNULL;
+ SHMFILELOCKS* fileLocks = NULL;
+
+ SHMLock();
+
+ palError = FILEGetSHMFileLocks(szFileName, &shmFileLocks, TRUE);
+ if (NO_ERROR != palError || shmFileLocks == SHMNULL)
+ {
+ goto GetLockControllerForFileExit;
+ }
+
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, fileLocks, shmFileLocks) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto GetLockControllerForFileExit;
+ }
+
+ *pdwShareMode = fileLocks->share_mode;
+
+GetLockControllerForFileExit:
+
+ if (SHMNULL != shmFileLocks)
+ {
+ FILECleanUpLockedRgn(
+ shmFileLocks,
+ 0,
+ NULL
+ );
+ }
+
+ SHMRelease();
+
+ return palError;
+}
+
+PAL_ERROR
+CSharedMemoryFileLockController::GetTransactionLock(
+ CPalThread *pThread, // IN, OPTIONAL
+ FileTransactionLockType eLockType,
+ DWORD dwOffsetLow,
+ DWORD dwOffsetHigh,
+ DWORD nNumberOfBytesToLockLow,
+ DWORD nNumberOfBytesToLockHigh,
+ IFileTransactionLock **ppTransactionLock // OUT
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ UINT64 lockRgnStart;
+ UINT64 nbBytesToLock;
+
+ lockRgnStart = ((UINT64)dwOffsetHigh) << 32 | dwOffsetLow;
+ nbBytesToLock = ((UINT64)nNumberOfBytesToLockHigh) << 32 |
+ nNumberOfBytesToLockLow;
+
+ palError = FILELockFileRegion(
+ m_shmFileLocks,
+ reinterpret_cast<PVOID>(this),
+ lockRgnStart,
+ nbBytesToLock,
+ RDWR_LOCK_RGN
+ );
+
+ if (NO_ERROR == palError)
+ {
+ *ppTransactionLock = InternalNew<CSharedMemoryFileTransactionLock>(m_shmFileLocks,
+ reinterpret_cast<PVOID>(this),
+ lockRgnStart,
+ nbBytesToLock);
+ if (NULL == *ppTransactionLock)
+ {
+ palError = ERROR_OUTOFMEMORY;
+ FILEUnlockFileRegion(
+ m_shmFileLocks,
+ reinterpret_cast<PVOID>(this),
+ lockRgnStart,
+ nbBytesToLock,
+ RDWR_LOCK_RGN
+ );
+ }
+ }
+
+ return palError;
+}
+
+PAL_ERROR
+CSharedMemoryFileLockController::CreateFileLock(
+ CPalThread *pThread, // IN, OPTIONAL
+ DWORD dwOffsetLow,
+ DWORD dwOffsetHigh,
+ DWORD nNumberOfBytesToLockLow,
+ DWORD nNumberOfBytesToLockHigh,
+ FileLockExclusivity eFileLockExclusivity,
+ FileLockWaitMode eFileLockWaitMode
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ UINT64 lockRgnStart;
+ UINT64 nbBytesToLock;
+
+ if (ExclusiveFileLock != eFileLockExclusivity
+ || FailImmediately != eFileLockWaitMode)
+ {
+ ASSERT("LockFileEx functionality not yet supported");
+ palError = ERROR_NOT_SUPPORTED;
+ goto CreateFileLockExit;
+ }
+
+ lockRgnStart = ((UINT64)dwOffsetHigh) << 32 | dwOffsetLow;
+ nbBytesToLock = ((UINT64)nNumberOfBytesToLockHigh) << 32 |
+ nNumberOfBytesToLockLow;
+
+ palError = FILELockFileRegion(
+ m_shmFileLocks,
+ reinterpret_cast<PVOID>(this),
+ lockRgnStart,
+ nbBytesToLock,
+ USER_LOCK_RGN
+ );
+
+CreateFileLockExit:
+
+ return palError;
+}
+
+PAL_ERROR
+CSharedMemoryFileLockController::ReleaseFileLock(
+ CPalThread *pThread, // IN, OPTIONAL
+ DWORD dwOffsetLow,
+ DWORD dwOffsetHigh,
+ DWORD nNumberOfBytesToUnlockLow,
+ DWORD nNumberOfBytesToUnlockHigh
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ UINT64 unlockRgnStart;
+ UINT64 nbBytesToUnlock;
+
+ unlockRgnStart = ((UINT64)dwOffsetHigh) << 32 | dwOffsetLow;
+ nbBytesToUnlock = ((UINT64)nNumberOfBytesToUnlockHigh) << 32 |
+ nNumberOfBytesToUnlockLow;
+
+ palError = FILEUnlockFileRegion(
+ m_shmFileLocks,
+ reinterpret_cast<PVOID>(this),
+ unlockRgnStart,
+ nbBytesToUnlock,
+ USER_LOCK_RGN
+ );
+
+ return palError;
+}
+
+void
+CSharedMemoryFileLockController::ReleaseController()
+{
+ if (SHMNULL != m_shmFileLocks)
+ {
+ FILECleanUpLockedRgn(
+ m_shmFileLocks,
+ m_dwAccessRights,
+ reinterpret_cast<PVOID>(this)
+ );
+ }
+
+ InternalDelete(this);
+}
+
+void
+CSharedMemoryFileTransactionLock::ReleaseLock()
+{
+ FILEUnlockFileRegion(
+ m_shmFileLocks,
+ m_pvControllerInstance,
+ m_lockRgnStart,
+ m_nbBytesToLock,
+ RDWR_LOCK_RGN
+ );
+
+ InternalDelete(this);
+}
+
+PAL_ERROR
+FILELockFileRegion(
+ SHMPTR shmFileLocks,
+ PVOID pvControllerInstance,
+ UINT64 lockRgnStart,
+ UINT64 nbBytesToLock,
+ LOCK_TYPE lockAction
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ SHMFILELOCKRGNS *curLock, *prevLock, *insertAfter,
+ lockRgn, fakeLock = {0,0,0,0};
+ SHMFILELOCKS *fileLocks;
+
+ SHMLock();
+
+ /* nothing to do if the region to lock is empty */
+ if (nbBytesToLock == 0)
+ {
+ TRACE("Locking an empty region (%I64d, %I64d)\n", lockRgnStart, nbBytesToLock);
+ goto EXIT;
+ }
+
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, fileLocks, shmFileLocks) == FALSE || fileLocks == NULL)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+
+ if (fileLocks->fileLockedRgns != 0)
+ {
+ prevLock = &fakeLock;
+
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLock, fileLocks->fileLockedRgns)
+ == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+
+ lockRgn.lockRgnStart = lockRgnStart;
+ lockRgn.nbBytesLocked = nbBytesToLock;
+ lockRgn.pvControllerInstance = pvControllerInstance;
+ lockRgn.processId = GetCurrentProcessId();
+ lockRgn.lockType = lockAction;
+
+ while((curLock != NULL) && IS_LOCK_BEFORE(curLock, &lockRgn))
+ {
+ prevLock = curLock;
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLock, curLock->next) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+ }
+
+ while((curLock != NULL) && IS_LOCK_INTERSECT(curLock, &lockRgn))
+ {
+ /* we couldn't lock the requested region if it overlap with other
+ region locked explicitly (by LockFile call) by other file pointer */
+ if ((lockAction == USER_LOCK_RGN) ||
+ ((curLock->lockType == USER_LOCK_RGN) &&
+ !IS_LOCK_HAVE_SAME_OWNER(curLock, &lockRgn)))
+ {
+ WARN("The requested lock region overlaps an existing locked region\n");
+ palError = ERROR_LOCK_VIOLATION;
+ goto EXIT;
+ }
+
+ prevLock = curLock;
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLock, curLock->next) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+ }
+
+ /* save the previous lock in case we need to insert the requested lock */
+ insertAfter = prevLock;
+
+ while(((curLock != NULL) && IS_LOCK_INTERSECT(&lockRgn, curLock)))
+ {
+ /* we couldn't lock the requested region if it overlap with other region
+ locked explicitly (by LockFile call) by other file pointer */
+ if ((lockAction == USER_LOCK_RGN) ||
+ ((curLock->lockType == USER_LOCK_RGN) &&
+ !IS_LOCK_HAVE_SAME_OWNER(curLock, &lockRgn)))
+ {
+ WARN("The requested lock region overlaps an existing locked region\n");
+ palError = ERROR_LOCK_VIOLATION;
+ goto EXIT;
+ }
+
+ prevLock = curLock;
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLock, curLock->next) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+ }
+
+ if (insertAfter == &fakeLock)
+ {
+ insertAfter = NULL;
+ }
+
+ palError = FILEAddNewLockedRgn(
+ fileLocks,
+ pvControllerInstance,
+ insertAfter,
+ lockRgnStart,
+ nbBytesToLock,
+ lockAction
+ );
+
+ if (NO_ERROR != palError)
+ {
+ WARN("Couldn't add the new locked region into SHM\n");
+ goto EXIT;
+ }
+ }
+ else /* lock region list is empty. */
+ {
+ palError = FILEAddNewLockedRgn(
+ fileLocks,
+ pvControllerInstance,
+ NULL,
+ lockRgnStart,
+ nbBytesToLock,
+ lockAction
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Couldn't add the first file locked region \n");
+ goto EXIT;
+ }
+ }
+
+EXIT:
+ SHMRelease();
+ return palError;
+}
+
+PAL_ERROR
+FILEUnlockFileRegion(
+ SHMPTR shmFileLocks,
+ PVOID pvControllerInstance,
+ UINT64 unlockRgnStart,
+ UINT64 nbBytesToUnlock,
+ LOCK_TYPE unlockType
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ SHMFILELOCKRGNS *prevLock = NULL, *curLockRgn = NULL, unlockRgn;
+ SHMPTR shmcurLockRgn;
+ SHMFILELOCKS *fileLocks;
+
+ SHMLock();
+
+
+ /* check if the region to unlock is empty or not */
+ if (nbBytesToUnlock == 0)
+ {
+ palError = ERROR_NOT_LOCKED;
+ WARN("Attempt to unlock an empty region\n");
+ goto EXIT;
+ }
+
+ if ((SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, fileLocks, shmFileLocks) == FALSE) ||
+ (fileLocks == NULL) ||
+ (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, fileLocks->fileLockedRgns) == FALSE))
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+
+ unlockRgn.processId = GetCurrentProcessId();
+ unlockRgn.pvControllerInstance = pvControllerInstance;
+ unlockRgn.lockRgnStart = unlockRgnStart;
+ unlockRgn.nbBytesLocked = nbBytesToUnlock;
+ unlockRgn.lockType = unlockType;
+
+ shmcurLockRgn = fileLocks->fileLockedRgns;
+
+ while((curLockRgn != NULL) && !IS_LOCK_EQUAL(curLockRgn, &unlockRgn))
+ {
+ prevLock = curLockRgn;
+ shmcurLockRgn = curLockRgn->next;
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ goto EXIT;
+ }
+ }
+
+ if (curLockRgn != NULL)
+ {
+ TRACE("removing the lock region (%I64u, %I64u)\n",
+ curLockRgn->lockRgnStart, curLockRgn->nbBytesLocked);
+
+ if (prevLock == NULL)
+ {
+ /* removing the first lock */
+ fileLocks->fileLockedRgns = curLockRgn->next;
+ }
+ else
+ {
+ prevLock->next = curLockRgn->next;
+ }
+ SHMfree(shmcurLockRgn);
+ }
+ else
+ {
+ /* the lock doesn't exist */
+ WARN("Attempt to unlock a non locked region\n");
+ palError = ERROR_NOT_LOCKED;
+ goto EXIT;
+ }
+
+EXIT:
+ SHMRelease();
+ return palError;
+}
+
+
+PAL_ERROR
+FILEGetSHMFileLocks(
+ LPCSTR filename,
+ SHMPTR *pshmFileLocks,
+ BOOL noCreate
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ SHMPTR shmPtrRet = 0;
+ SHMFILELOCKS *filelocksPtr, *nextFilelocksPtr;
+ char *unix_filename;
+
+ SHMLock();
+
+ shmPtrRet = SHMGetInfo(SIID_FILE_LOCKS);
+
+ while(shmPtrRet != 0)
+ {
+ if ( (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, filelocksPtr, shmPtrRet) == FALSE) ||
+ (SHMPTR_TO_TYPED_PTR_BOOL(char, unix_filename, filelocksPtr->unix_filename) == FALSE))
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+
+ if (unix_filename == NULL)
+ {
+ ERROR("Unexpected lock file name value.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+
+ if (strcmp(unix_filename, filename) == 0)
+ {
+ filelocksPtr->refCount++;
+ goto EXIT;
+ }
+
+ shmPtrRet = filelocksPtr->next;
+ }
+
+ /* the file has never been locked before.*/
+ shmPtrRet = 0;
+ if (noCreate)
+ {
+ goto EXIT;
+ }
+
+ TRACE("Create a new entry in the file lock list in SHM\n");
+
+ /* Create a new entry in the file lock list in SHM */
+ if ((shmPtrRet = SHMalloc(sizeof(SHMFILELOCKS))) == 0)
+ {
+ ERROR("Can't allocate SHMFILELOCKS structure\n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto EXIT;
+ }
+
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, filelocksPtr, shmPtrRet) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto CLEANUP1;
+ }
+
+ filelocksPtr->unix_filename = SHMStrDup(filename);
+ if (filelocksPtr->unix_filename == 0)
+ {
+ ERROR("Can't allocate shared memory for filename\n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto CLEANUP1;
+ }
+
+ filelocksPtr->fileLockedRgns = 0;
+ filelocksPtr->prev = 0;
+ filelocksPtr->next = SHMGetInfo(SIID_FILE_LOCKS);
+ filelocksPtr->refCount = 1;
+ filelocksPtr->share_mode = SHARE_MODE_NOT_INITALIZED;
+ filelocksPtr->nbReadAccess = 0;
+ filelocksPtr->nbWriteAccess = 0;
+
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, nextFilelocksPtr, filelocksPtr->next) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto CLEANUP2;
+ }
+
+ if (nextFilelocksPtr != NULL)
+ {
+ nextFilelocksPtr->prev = shmPtrRet;
+ }
+
+ SHMSetInfo(SIID_FILE_LOCKS, shmPtrRet);
+ goto EXIT;
+
+CLEANUP2:
+ SHMfree(filelocksPtr->unix_filename);
+CLEANUP1:
+ SHMfree(shmPtrRet);
+ shmPtrRet = 0;
+EXIT:
+ SHMRelease();
+
+ if (NO_ERROR == palError)
+ {
+ *pshmFileLocks = shmPtrRet;
+ }
+
+ return palError;
+}
+
+PAL_ERROR
+FILEAddNewLockedRgn(
+ SHMFILELOCKS* fileLocks,
+ PVOID pvControllerInstance,
+ SHMFILELOCKRGNS *insertAfter,
+ UINT64 lockRgnStart,
+ UINT64 nbBytesToLock,
+ LOCK_TYPE lockType
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ SHMFILELOCKRGNS *newLockRgn, *lockRgnPtr;
+ SHMPTR shmNewLockRgn = SHMNULL;
+
+ if ((fileLocks == NULL) || (pvControllerInstance == NULL))
+ {
+ ASSERT("Invalid Null parameter.\n");
+ return FALSE;
+ }
+
+ SHMLock();
+
+ /* Create a new entry for the new locked region */
+ TRACE("Create a new entry for the new lock region (%I64u %I64u)\n",
+ lockRgnStart, nbBytesToLock);
+
+ if ((shmNewLockRgn = SHMalloc(sizeof(SHMFILELOCKRGNS))) == SHMNULL)
+ {
+ ERROR("Can't allocate SHMFILELOCKRGNS structure\n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto EXIT;
+ }
+
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, newLockRgn, shmNewLockRgn) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+
+ newLockRgn->processId = GetCurrentProcessId();
+ newLockRgn->pvControllerInstance = pvControllerInstance;
+ newLockRgn->lockRgnStart = lockRgnStart;
+ newLockRgn->nbBytesLocked = nbBytesToLock;
+ newLockRgn->lockType = lockType;
+
+ /* All locked regions with the same offset should be sorted ascending */
+ /* the sort is based on the length of the locked byte range */
+ if (insertAfter != NULL)
+ {
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, lockRgnPtr, insertAfter->next) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+ }
+ else
+ {
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, lockRgnPtr, fileLocks->fileLockedRgns) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+ }
+
+ while(lockRgnPtr != NULL)
+ {
+ if ( (lockRgnPtr->lockRgnStart == newLockRgn->lockRgnStart) &&
+ (newLockRgn->nbBytesLocked > lockRgnPtr->nbBytesLocked))
+ {
+ insertAfter = lockRgnPtr;
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, lockRgnPtr, lockRgnPtr->next) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+ continue;
+ }
+
+ break;
+ }
+
+ if (insertAfter != NULL)
+ {
+ TRACE("Adding lock after the lock rgn (%I64d %I64d)\n",
+ insertAfter->lockRgnStart,insertAfter->nbBytesLocked);
+ newLockRgn->next = insertAfter->next;
+ insertAfter->next = shmNewLockRgn;
+ }
+ else
+ {
+ TRACE("adding lock into the head of the list\n");
+ newLockRgn->next = fileLocks->fileLockedRgns;
+ fileLocks->fileLockedRgns = shmNewLockRgn;
+ }
+
+EXIT:
+
+ if (NO_ERROR != palError && SHMNULL != shmNewLockRgn)
+ {
+ SHMfree(shmNewLockRgn);
+ }
+
+ SHMRelease();
+
+ return palError;
+}
+
+void
+FILECleanUpLockedRgn(
+ SHMPTR shmFileLocks,
+ DWORD dwAccessRights,
+ PVOID pvControllerInstance
+ )
+{
+ SHMFILELOCKRGNS *curLockRgn = NULL, *prevLock = NULL;
+ SHMFILELOCKS *fileLocks, *prevFileLocks, *nextFileLocks;
+ SHMPTR shmcurLockRgn;
+
+ SHMLock();
+
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, fileLocks, shmFileLocks) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ goto EXIT;
+ }
+
+ if (fileLocks != NULL)
+ {
+ if(fileLocks->fileLockedRgns !=0)
+ {
+ shmcurLockRgn = fileLocks->fileLockedRgns;
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ goto EXIT;
+ }
+
+ while(curLockRgn != NULL)
+ {
+ if ((curLockRgn->pvControllerInstance == pvControllerInstance) &&
+ (curLockRgn->processId == GetCurrentProcessId()))
+ {
+ /* found the locked rgn to remove from SHM */
+ TRACE("Removing the locked region (%I64u, %I64u) from SMH\n",
+ curLockRgn->lockRgnStart, curLockRgn->nbBytesLocked);
+
+ if (prevLock == NULL)
+ {
+ /* removing the first lock */
+ fileLocks->fileLockedRgns = curLockRgn->next;
+ SHMfree(shmcurLockRgn);
+ shmcurLockRgn = fileLocks->fileLockedRgns;
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ goto EXIT;
+ }
+ }
+ else
+ {
+ prevLock->next = curLockRgn->next;
+ SHMfree(shmcurLockRgn);
+ shmcurLockRgn = prevLock->next;
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ goto EXIT;
+ }
+ }
+ continue;
+ }
+
+ prevLock = curLockRgn;
+ shmcurLockRgn = curLockRgn->next;
+ if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ goto EXIT;
+ }
+ }
+ }
+
+ if (dwAccessRights & GENERIC_READ)
+ {
+ fileLocks->nbReadAccess--;
+ }
+ if (dwAccessRights & GENERIC_WRITE)
+ {
+ fileLocks->nbWriteAccess--;
+ }
+
+ /* remove the SHMFILELOCKS structure from SHM if there's no more locked
+ region left and no more reference to it */
+ if ((--(fileLocks->refCount) == 0) && (fileLocks->fileLockedRgns == 0))
+ {
+ TRACE("Removing the SHMFILELOCKS structure from SHM\n");
+
+ if ( (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, prevFileLocks, fileLocks->prev) == FALSE) ||
+ (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, nextFileLocks, fileLocks->next) == FALSE))
+ {
+ ASSERT("Unable to get pointer from shm pointer.\n");
+ goto EXIT;
+ }
+
+ if (prevFileLocks == NULL)
+ {
+ /* removing the first lock file*/
+ SHMSetInfo(SIID_FILE_LOCKS, fileLocks->next);
+ }
+ else
+ {
+ prevFileLocks->next = fileLocks->next;
+ }
+
+ if (nextFileLocks != NULL)
+ {
+ nextFileLocks->prev = fileLocks->prev;
+ }
+
+ if (fileLocks->unix_filename)
+ SHMfree(fileLocks->unix_filename);
+
+ SHMfree(shmFileLocks);
+ }
+ }
+EXIT:
+ SHMRelease();
+ return;
+}
+
diff --git a/src/pal/src/file/shmfilelockmgr.hpp b/src/pal/src/file/shmfilelockmgr.hpp
new file mode 100644
index 0000000000..dd7d9b8014
--- /dev/null
+++ b/src/pal/src/file/shmfilelockmgr.hpp
@@ -0,0 +1,185 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ shmfilelockmgr.hpp
+
+Abstract:
+ Shared memory based file lock manager
+
+
+
+--*/
+
+#ifndef _PAL_SHMFILELOCKMGR_H_
+#define _PAL_SHMFILELOCKMGR_H_
+
+#include "pal/corunix.hpp"
+#include "pal/shm.hpp"
+
+namespace CorUnix
+{
+ #define SHARE_MODE_NOT_INITALIZED 0xFFFFFFFF
+
+ typedef struct
+ {
+ SHMPTR unix_filename;
+ SHMPTR fileLockedRgns;
+ UINT refCount;
+ SHMPTR next;
+ SHMPTR prev;
+ DWORD share_mode; /* FILE_SHARE_READ, FILE_SHARE_WRITE,
+ FILE_SHARE_DELETE, 0 ( not shared ) or
+ SHARE_MODE_NOT_INITALIZED */
+ int nbReadAccess; /* used to keep track of the minimal
+ access permissions */
+ int nbWriteAccess;
+ } SHMFILELOCKS;
+
+ typedef enum
+ {
+ USER_LOCK_RGN, /* Used only for user locks (LockFile or UnlockFile call) */
+ RDWR_LOCK_RGN /* Used to distinguish between the user locks and the internal
+ locks made when reading, writing or truncating file */
+ } LOCK_TYPE;
+
+ typedef struct
+ {
+ DWORD processId;
+ PVOID pvControllerInstance;
+ UINT64 lockRgnStart;
+ UINT64 nbBytesLocked;
+ LOCK_TYPE lockType;
+
+ SHMPTR next;
+ } SHMFILELOCKRGNS;
+
+ class CSharedMemoryFileLockMgr : public IFileLockManager
+ {
+ public:
+
+ virtual
+ PAL_ERROR
+ GetLockControllerForFile(
+ CPalThread *pThread, // IN, OPTIONAL
+ LPCSTR szFileName,
+ DWORD dwAccessRights,
+ DWORD dwShareMode,
+ IFileLockController **ppLockController // OUT
+ );
+
+ virtual
+ PAL_ERROR
+ GetFileShareModeForFile(
+ LPCSTR szFileName,
+ DWORD* pdwShareMode);
+ };
+
+ class CSharedMemoryFileLockController : public IFileLockController
+ {
+ template <class T> friend void InternalDelete(T *p);
+
+ private:
+ DWORD m_dwAccessRights;
+ SHMPTR m_shmFileLocks;
+ protected:
+ virtual ~CSharedMemoryFileLockController()
+ {
+ };
+
+ public:
+
+ CSharedMemoryFileLockController(
+ DWORD dwAccessRights,
+ SHMPTR shmFileLocks
+ )
+ :
+ m_dwAccessRights(dwAccessRights),
+ m_shmFileLocks(shmFileLocks)
+ {
+ };
+
+ virtual
+ PAL_ERROR
+ GetTransactionLock(
+ CPalThread *pThread, // IN, OPTIONAL
+ FileTransactionLockType eLockType,
+ DWORD dwOffsetLow,
+ DWORD dwOffsetHigh,
+ DWORD nNumberOfBytesToLockLow,
+ DWORD nNumberOfBytesToLockHigh,
+ IFileTransactionLock **ppTransactionLock // OUT
+ );
+
+ virtual
+ PAL_ERROR
+ CreateFileLock(
+ CPalThread *pThread, // IN, OPTIONAL
+ DWORD dwOffsetLow,
+ DWORD dwOffsetHigh,
+ DWORD nNumberOfBytesToLockLow,
+ DWORD nNumberOfBytesToLockHigh,
+ FileLockExclusivity eFileLockExclusivity,
+ FileLockWaitMode eFileLockWaitMode
+ );
+
+ virtual
+ PAL_ERROR
+ ReleaseFileLock(
+ CPalThread *pThread, // IN, OPTIONAL
+ DWORD dwOffsetLow,
+ DWORD dwOffsetHigh,
+ DWORD nNumberOfBytesToUnlockLow,
+ DWORD nNumberOfBytesToUnlockHigh
+ );
+
+ virtual
+ void
+ ReleaseController();
+ };
+
+ class CSharedMemoryFileTransactionLock : public IFileTransactionLock
+ {
+ template <class T> friend void InternalDelete(T *p);
+
+ private:
+
+ SHMPTR m_shmFileLocks;
+ PVOID m_pvControllerInstance;
+ UINT64 m_lockRgnStart;
+ UINT64 m_nbBytesToLock;
+ protected:
+ virtual ~CSharedMemoryFileTransactionLock()
+ {
+ };
+
+ public:
+
+ CSharedMemoryFileTransactionLock(
+ SHMPTR shmFileLocks,
+ PVOID pvControllerInstance,
+ UINT64 lockRgnStart,
+ UINT64 nbBytesToLock
+ )
+ :
+ m_shmFileLocks(shmFileLocks),
+ m_pvControllerInstance(pvControllerInstance),
+ m_lockRgnStart(lockRgnStart),
+ m_nbBytesToLock(nbBytesToLock)
+ {
+ };
+
+ virtual
+ void
+ ReleaseLock();
+ };
+}
+
+#endif /* _PAL_SHMFILELOCKMGR_H_ */
+
diff --git a/src/pal/src/handlemgr/handleapi.cpp b/src/pal/src/handlemgr/handleapi.cpp
new file mode 100644
index 0000000000..6b99a85cf7
--- /dev/null
+++ b/src/pal/src/handlemgr/handleapi.cpp
@@ -0,0 +1,338 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ handleapi.cpp
+
+Abstract:
+
+ Implementation of the handle management APIs
+
+
+
+--*/
+
+#include "pal/handleapi.hpp"
+#include "pal/handlemgr.hpp"
+#include "pal/thread.hpp"
+#include "pal/procobj.hpp"
+#include "pal/dbgmsg.h"
+#include "pal/process.h"
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(HANDLE);
+
+CAllowedObjectTypes aotDuplicateHandle(TRUE);
+
+PAL_ERROR
+CloseSpecialHandle(
+ HANDLE hObject
+ );
+
+/*++
+Function:
+ DuplicateHandle
+
+See MSDN doc.
+
+PAL-specific behavior :
+ -Source and Target process needs to be the current process.
+ -lpTargetHandle must be non-NULL
+ -dwDesiredAccess is ignored
+ -bInheritHandle must be FALSE
+ -dwOptions must be a combo of DUPLICATE_SAME_ACCESS and
+ DUPLICATE_CLOSE_SOURCE
+
+--*/
+BOOL
+PALAPI
+DuplicateHandle(
+ IN HANDLE hSourceProcessHandle,
+ IN HANDLE hSourceHandle,
+ IN HANDLE hTargetProcessHandle,
+ OUT LPHANDLE lpTargetHandle,
+ IN DWORD dwDesiredAccess,
+ IN BOOL bInheritHandle,
+ IN DWORD dwOptions)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+
+ PERF_ENTRY(DuplicateHandle);
+ ENTRY("DuplicateHandle( hSrcProcHandle=%p, hSrcHandle=%p, "
+ "hTargetProcHandle=%p, lpTargetHandle=%p, dwAccess=%#x, "
+ "bInheritHandle=%d, dwOptions=%#x) \n", hSourceProcessHandle,
+ hSourceHandle, hTargetProcessHandle, lpTargetHandle,
+ dwDesiredAccess, bInheritHandle, dwOptions);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalDuplicateHandle(
+ pThread,
+ hSourceProcessHandle,
+ hSourceHandle,
+ hTargetProcessHandle,
+ lpTargetHandle,
+ dwDesiredAccess,
+ bInheritHandle,
+ dwOptions
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("DuplicateHandle returns BOOL %d\n", (NO_ERROR == palError));
+ PERF_EXIT(DuplicateHandle);
+ return (NO_ERROR == palError);
+}
+
+PAL_ERROR
+CorUnix::InternalDuplicateHandle(
+ CPalThread *pThread,
+ HANDLE hSourceProcess,
+ HANDLE hSource,
+ HANDLE hTargetProcess,
+ LPHANDLE phDuplicate,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ DWORD dwOptions
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjSource = NULL;
+
+ DWORD source_process_id;
+ DWORD target_process_id;
+ DWORD cur_process_id;
+
+ cur_process_id = GetCurrentProcessId();
+ source_process_id = PROCGetProcessIDFromHandle(hSourceProcess);
+ target_process_id = PROCGetProcessIDFromHandle(hTargetProcess);
+
+ /* Check validity of process handles */
+ if (0 == source_process_id || 0 == target_process_id)
+ {
+ ASSERT("Can't duplicate handle: invalid source or destination process");
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalDuplicateHandleExit;
+ }
+
+ /* At least source or target process should be the current process. */
+ if (source_process_id != cur_process_id
+ && target_process_id != cur_process_id)
+ {
+ ASSERT("Can't duplicate handle : neither source or destination"
+ "processes are from current process");
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalDuplicateHandleExit;
+ }
+
+ if (FALSE != bInheritHandle)
+ {
+ ASSERT("Can't duplicate handle : bInheritHandle is not FALSE.\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalDuplicateHandleExit;
+ }
+
+ if (dwOptions & ~(DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ {
+ ASSERT(
+ "Can't duplicate handle : dwOptions is %#x which is not "
+ "a subset of (DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE) "
+ "(%#x).\n",
+ dwOptions,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalDuplicateHandleExit;
+ }
+
+ if (0 == (dwOptions & DUPLICATE_SAME_ACCESS))
+ {
+ ASSERT(
+ "Can't duplicate handle : dwOptions is %#x which does not "
+ "include DUPLICATE_SAME_ACCESS (%#x).\n",
+ dwOptions,
+ DUPLICATE_SAME_ACCESS);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalDuplicateHandleExit;
+ }
+
+ if (NULL == phDuplicate)
+ {
+ ASSERT("Can't duplicate handle : lpTargetHandle is NULL.\n");
+ goto InternalDuplicateHandleExit;
+ }
+
+ /* Since handles can be remoted to others processes using PAL_LocalHsndleToRemote
+ and PAL_RemoteHandleToLocal, DuplicateHandle needs some special handling
+ when this scenario occurs.
+
+ if hSourceProcessHandle is from another process OR
+ hTargetProcessHandle is from another process but both aren't
+ ( handled above ) return hSourceHandle.
+ */
+ if (source_process_id != cur_process_id
+ || target_process_id != cur_process_id)
+ {
+ *phDuplicate = hSource;
+ palError = NO_ERROR;
+ goto InternalDuplicateHandleExit;
+ }
+
+ //
+ // Obtain the source IPalObject
+ //
+
+ if (!HandleIsSpecial(hSource))
+ {
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hSource,
+ &aotDuplicateHandle,
+ dwDesiredAccess,
+ &pobjSource
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to get object for source handle %p (%i)\n", hSource, palError);
+ goto InternalDuplicateHandleExit;
+ }
+ }
+ else if (hPseudoCurrentProcess == hSource)
+ {
+ TRACE("Duplicating process pseudo handle(%p)\n", hSource);
+
+ pobjSource = g_pobjProcess;
+ pobjSource->AddReference();
+ }
+ else if (hPseudoCurrentThread == hSource)
+ {
+ TRACE("Duplicating thread pseudo handle(%p)\n", hSource);
+
+ pobjSource = pThread->GetThreadObject();
+ pobjSource->AddReference();
+ }
+ else
+ {
+ ASSERT("Duplication not supported for this special handle (%p)\n", hSource);
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalDuplicateHandleExit;
+ }
+
+ palError = g_pObjectManager->ObtainHandleForObject(
+ pThread,
+ pobjSource,
+ dwDesiredAccess,
+ bInheritHandle,
+ NULL,
+ phDuplicate
+ );
+
+InternalDuplicateHandleExit:
+
+ if (NULL != pobjSource)
+ {
+ pobjSource->ReleaseReference(pThread);
+ }
+
+ if (dwOptions & DUPLICATE_CLOSE_SOURCE)
+ {
+ //
+ // Since DUPLICATE_CLOSE_SOURCE was specified the source handle
+ // MUST be closed, even if an error occurred during the duplication
+ // process
+ //
+
+ TRACE("DuplicateHandle closing source handle %p\n", hSource);
+ InternalCloseHandle(pThread, hSource);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ CloseHandle
+
+See MSDN doc.
+
+Note : according to MSDN, FALSE is returned in case of error. But also
+according to MSDN, closing an invalid handle raises an exception when running a
+debugger [or, alternately, if a special registry key is set]. This behavior is
+not required in the PAL, so we'll always return FALSE.
+--*/
+BOOL
+PALAPI
+CloseHandle(
+ IN OUT HANDLE hObject)
+{
+ CPalThread *pThread;
+ PAL_ERROR palError;
+
+ PERF_ENTRY(CloseHandle);
+ ENTRY("CloseHandle (hObject=%p) \n", hObject);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalCloseHandle(
+ pThread,
+ hObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("CloseHandle returns BOOL %d\n", (NO_ERROR == palError));
+ PERF_EXIT(CloseHandle);
+ return (NO_ERROR == palError);
+}
+
+PAL_ERROR
+CorUnix::InternalCloseHandle(
+ CPalThread * pThread,
+ HANDLE hObject
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ if (!HandleIsSpecial(hObject))
+ {
+ palError = g_pObjectManager->RevokeHandle(
+ pThread,
+ hObject
+ );
+ }
+ else
+ {
+ palError = CloseSpecialHandle(hObject);
+ }
+
+ return palError;
+}
+
+PAL_ERROR
+CloseSpecialHandle(
+ HANDLE hObject
+ )
+{
+ if ((hObject == hPseudoCurrentThread) ||
+ (hObject == hPseudoCurrentProcess))
+ {
+ return NO_ERROR;
+ }
+
+ return ERROR_INVALID_HANDLE;
+}
+
diff --git a/src/pal/src/handlemgr/handlemgr.cpp b/src/pal/src/handlemgr/handlemgr.cpp
new file mode 100644
index 0000000000..3516287af5
--- /dev/null
+++ b/src/pal/src/handlemgr/handlemgr.cpp
@@ -0,0 +1,327 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ handlemgr.cpp
+
+Abstract:
+
+ Implementation of a basic handle table
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/handlemgr.hpp"
+#include "pal/cs.hpp"
+#include "pal/malloc.hpp"
+#include "pal/dbgmsg.h"
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(HANDLE);
+
+/* Constants */
+/* Special handles */
+/* Pseudo handles constant for current thread and process */
+const HANDLE hPseudoCurrentProcess = (HANDLE) 0xFFFFFF01;
+const HANDLE hPseudoCurrentThread = (HANDLE) 0xFFFFFF03;
+/* Pseudo handle constant for the global IO Completion port */
+const HANDLE hPseudoGlobalIOCP = (HANDLE) 0xFFFFFF05;
+
+PAL_ERROR
+CSimpleHandleManager::Initialize(
+ void
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ InternalInitializeCriticalSection(&m_csLock);
+ m_fLockInitialized = TRUE;
+
+ m_dwTableGrowthRate = c_BasicGrowthRate;
+
+ /* initialize the handle table - the free list is stored in the 'object'
+ field, with the head in the global 'm_hiFreeListStart'. */
+ m_dwTableSize = m_dwTableGrowthRate;
+
+ m_rghteHandleTable = reinterpret_cast<HANDLE_TABLE_ENTRY*>(InternalMalloc((m_dwTableSize * sizeof(HANDLE_TABLE_ENTRY))));
+ if(NULL == m_rghteHandleTable)
+ {
+ ERROR("Unable to create initial handle table array");
+ palError = ERROR_OUTOFMEMORY;
+ goto InitializeExit;
+ }
+
+ for (DWORD i = 0; i < m_dwTableSize; i++)
+ {
+ m_rghteHandleTable[i].u.hiNextIndex = i + 1;
+ m_rghteHandleTable[i].fEntryAllocated = FALSE;
+ }
+
+ m_rghteHandleTable[m_dwTableSize - 1].u.hiNextIndex = (HANDLE_INDEX)-1;
+
+ m_hiFreeListStart = 0;
+ m_hiFreeListEnd = m_dwTableSize - 1;
+
+ TRACE("Handle Manager initialization complete.\n");
+
+InitializeExit:
+
+ return palError;
+}
+
+PAL_ERROR
+CSimpleHandleManager::AllocateHandle(
+ CPalThread *pThread,
+ IPalObject *pObject,
+ DWORD dwAccessRights,
+ bool fInheritable,
+ HANDLE *ph
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ DWORD dwIndex;
+
+ Lock(pThread);
+
+ /* if no free handles are available, we need to grow the handle table and
+ add new handles to the pool */
+ if (m_hiFreeListStart == c_hiInvalid)
+ {
+ HANDLE_TABLE_ENTRY* rghteTempTable;
+
+ TRACE("Handle pool empty (%d handles allocated), growing handle table "
+ "by %d entries.\n", m_dwTableSize, m_dwTableGrowthRate );
+
+ /* make sure handle values don't overflow */
+ if (m_dwTableSize + m_dwTableGrowthRate >= c_MaxIndex)
+ {
+ WARN("Unable to allocate handle : maximum (%d) reached!\n",
+ m_dwTableSize);
+ palError = ERROR_OUTOFMEMORY;
+ goto AllocateHandleExit;
+ }
+
+ /* grow handle table */
+ rghteTempTable = reinterpret_cast<HANDLE_TABLE_ENTRY*>(InternalRealloc(
+ m_rghteHandleTable,
+ (m_dwTableSize + m_dwTableGrowthRate) * sizeof(HANDLE_TABLE_ENTRY)));
+
+ if (NULL == rghteTempTable)
+ {
+ WARN("not enough memory to grow handle table!\n");
+ palError = ERROR_OUTOFMEMORY;
+ goto AllocateHandleExit;
+ }
+ m_rghteHandleTable = rghteTempTable;
+
+ /* update handle table and handle pool */
+ for (DWORD dw = m_dwTableSize; dw < m_dwTableSize + m_dwTableGrowthRate; dw += 1)
+ {
+ /* new handles are initially invalid */
+ /* the last "old" handle was m_dwTableSize-1, so the new
+ handles range from m_dwTableSize to
+ m_dwTableSize+m_dwTableGrowthRate-1 */
+ m_rghteHandleTable[dw].u.hiNextIndex = dw + 1;
+ m_rghteHandleTable[dw].fEntryAllocated = FALSE;
+ }
+
+ m_hiFreeListStart = m_dwTableSize;
+ m_dwTableSize += m_dwTableGrowthRate;
+ m_rghteHandleTable[m_dwTableSize - 1].u.hiNextIndex = (HANDLE_INDEX)-1;
+ m_hiFreeListEnd = m_dwTableSize - 1;
+
+ }
+
+ /* take the next free handle */
+ dwIndex = m_hiFreeListStart;
+
+ /* remove the handle from the pool */
+ m_hiFreeListStart = m_rghteHandleTable[dwIndex].u.hiNextIndex;
+
+ /* clear the tail record if this is the last handle slot available */
+ if(m_hiFreeListStart == c_hiInvalid)
+ {
+ m_hiFreeListEnd = c_hiInvalid;
+ }
+
+ /* save the data associated with the new handle */
+ *ph = HandleIndexToHandle(dwIndex);
+
+ pObject->AddReference();
+ m_rghteHandleTable[dwIndex].u.pObject = pObject;
+ m_rghteHandleTable[dwIndex].dwAccessRights = dwAccessRights;
+ m_rghteHandleTable[dwIndex].fInheritable = fInheritable;
+ m_rghteHandleTable[dwIndex].fEntryAllocated = TRUE;
+
+AllocateHandleExit:
+
+ Unlock(pThread);
+
+ return palError;
+}
+
+PAL_ERROR
+CSimpleHandleManager::GetObjectFromHandle(
+ CPalThread *pThread,
+ HANDLE h,
+ DWORD *pdwRightsGranted,
+ IPalObject **ppObject
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ HANDLE_INDEX hi;
+
+ Lock(pThread);
+
+ if (!ValidateHandle(h))
+ {
+ ERROR("Tried to dereference an invalid handle %p\n", h);
+ palError = ERROR_INVALID_HANDLE;
+ goto GetObjectFromHandleExit;
+ }
+
+ hi = HandleToHandleIndex(h);
+
+ *pdwRightsGranted = m_rghteHandleTable[hi].dwAccessRights;
+ *ppObject = m_rghteHandleTable[hi].u.pObject;
+ (*ppObject)->AddReference();
+
+GetObjectFromHandleExit:
+
+ Unlock(pThread);
+
+ return palError;
+}
+
+PAL_ERROR
+CSimpleHandleManager::FreeHandle(
+ CPalThread *pThread,
+ HANDLE h
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobj = NULL;
+ HANDLE_INDEX hi = HandleToHandleIndex(h);
+
+ Lock(pThread);
+
+ if (!ValidateHandle(h))
+ {
+ ERROR("Trying to free invalid handle %p.\n", h);
+ palError = ERROR_INVALID_HANDLE;
+ goto FreeHandleExit;
+ }
+
+ if (HandleIsSpecial(h))
+ {
+ ASSERT("Trying to free Special Handle %p.\n", h);
+ palError = ERROR_INVALID_HANDLE;
+ goto FreeHandleExit;
+ }
+
+ pobj = m_rghteHandleTable[hi].u.pObject;
+ m_rghteHandleTable[hi].fEntryAllocated = FALSE;
+
+ /* add handle to the free pool */
+ if(m_hiFreeListEnd != c_hiInvalid)
+ {
+ m_rghteHandleTable[m_hiFreeListEnd].u.hiNextIndex = hi;
+ }
+ else
+ {
+ m_hiFreeListStart = hi;
+ }
+
+ m_rghteHandleTable[hi].u.hiNextIndex = c_hiInvalid;
+ m_hiFreeListEnd = hi;
+
+FreeHandleExit:
+
+ Unlock(pThread);
+
+ if (NULL != pobj)
+ {
+ pobj->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function :
+ ValidateHandle
+
+ Check if a handle was allocated by this handle manager
+
+Parameters :
+ HANDLE handle : handle to check.
+
+Return Value :
+ TRUE if valid, FALSE if invalid.
+--*/
+bool CSimpleHandleManager::ValidateHandle(HANDLE handle)
+{
+ DWORD dwIndex;
+
+ if (NULL == m_rghteHandleTable)
+ {
+ ASSERT("Handle Manager is not initialized!\n");
+ return FALSE;
+ }
+
+ if (handle == INVALID_HANDLE_VALUE || handle == 0)
+ {
+ TRACE( "INVALID_HANDLE_VALUE or NULL value is not a valid handle.\n" );
+ return FALSE;
+ }
+
+ if (HandleIsSpecial(handle))
+ {
+ //
+ // Special handles are valid in the general sense. They are not valid
+ // in this context, though, as they were not allocated by the handle
+ // manager. Hitting this case indicates a logic error within the PAL
+ // (since clients of the handle manager should have already dealt with
+ // the specialness of the handle) so we assert here.
+ //
+
+ ASSERT ("Handle %p is a special handle, returning FALSE.\n", handle);
+ return FALSE;
+ }
+
+ dwIndex = HandleToHandleIndex(handle);
+
+ if (dwIndex >= m_dwTableSize)
+ {
+ WARN( "The handle value(%p) is out of the bounds for the handle table.\n", handle );
+ return FALSE;
+ }
+
+ if (!m_rghteHandleTable[dwIndex].fEntryAllocated)
+ {
+ WARN("The handle value (%p) has not been allocated\n", handle);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+bool
+CorUnix::HandleIsSpecial(
+ HANDLE h
+ )
+{
+ return (hPseudoCurrentProcess == h ||
+ hPseudoCurrentThread == h ||
+ hPseudoGlobalIOCP == h);
+}
+
diff --git a/src/pal/src/include/pal/cert.hpp b/src/pal/src/include/pal/cert.hpp
new file mode 100644
index 0000000000..77c7f28d1a
--- /dev/null
+++ b/src/pal/src/include/pal/cert.hpp
@@ -0,0 +1,33 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/cert.hpp
+
+Abstract:
+ Header file for cert structures
+
+
+
+--*/
+
+#ifndef _PAL_CERT_HPP_
+#define _PAL_CERT_HPP_
+
+#include "corunix.hpp"
+
+#include <Security/Security.h>
+
+CorUnix::PAL_ERROR OIDToStr(CSSM_DATA &data, CHAR *&oidStrOut);
+
+CSSM_RETURN InitCSSMModule(const CSSM_GUID *inGuid, CSSM_SERVICE_TYPE inService,
+ CSSM_MODULE_HANDLE_PTR outModule);
+CSSM_RETURN TermCSSMModule(const CSSM_GUID *inGuid, CSSM_MODULE_HANDLE_PTR inModule);
+
+#endif // !_PAL_CERT_HPP_
diff --git a/src/pal/src/include/pal/context.h b/src/pal/src/include/pal/context.h
new file mode 100644
index 0000000000..5e378942fb
--- /dev/null
+++ b/src/pal/src/include/pal/context.h
@@ -0,0 +1,641 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/context.h
+
+Abstract:
+
+ Header file for thread context utility functions.
+
+
+
+--*/
+
+#ifndef _PAL_CONTEXT_H_
+#define _PAL_CONTEXT_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+#include <signal.h>
+#include <pthread.h>
+
+#if !HAVE_MACH_EXCEPTIONS
+/* A type to wrap the native context type, which is ucontext_t on some
+ * platforms and another type elsewhere. */
+#if HAVE_UCONTEXT_T
+#include <ucontext.h>
+
+typedef ucontext_t native_context_t;
+#else // HAVE_UCONTEXT_T
+#error Native context type is not known on this platform!
+#endif // HAVE_UCONTEXT_T
+#else // !HAVE_MACH_EXCEPTIONS
+#include <mach/kern_return.h>
+#include <mach/mach_port.h>
+#endif // !HAVE_MACH_EXCEPTIONS else
+
+#if HAVE___GREGSET_T
+
+#ifdef BIT64
+#define MCREG_Rbx(mc) ((mc).__gregs[_REG_RBX])
+#define MCREG_Rcx(mc) ((mc).__gregs[_REG_RCX])
+#define MCREG_Rdx(mc) ((mc).__gregs[_REG_RDX])
+#define MCREG_Rsi(mc) ((mc).__gregs[_REG_RSI])
+#define MCREG_Rdi(mc) ((mc).__gregs[_REG_RDI])
+#define MCREG_Rbp(mc) ((mc).__gregs[_REG_RBP])
+#define MCREG_Rax(mc) ((mc).__gregs[_REG_RAX])
+#define MCREG_Rip(mc) ((mc).__gregs[_REG_RIP])
+#define MCREG_Rsp(mc) ((mc).__gregs[_REG_RSP])
+#define MCREG_SegCs(mc) ((mc).__gregs[_REG_CS])
+#define MCREG_SegSs(mc) ((mc).__gregs[_REG_SS])
+#define MCREG_R8(mc) ((mc).__gregs[_REG_R8])
+#define MCREG_R9(mc) ((mc).__gregs[_REG_R9])
+#define MCREG_R10(mc) ((mc).__gregs[_REG_R10])
+#define MCREG_R11(mc) ((mc).__gregs[_REG_R11])
+#define MCREG_R12(mc) ((mc).__gregs[_REG_R12])
+#define MCREG_R13(mc) ((mc).__gregs[_REG_R13])
+#define MCREG_R14(mc) ((mc).__gregs[_REG_R14])
+#define MCREG_R15(mc) ((mc).__gregs[_REG_R15])
+#define MCREG_EFlags(mc) ((mc).__gregs[_REG_RFLAGS])
+
+#define FPREG_Xmm(uc, index) *(M128A*)&(((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_xmm[index])
+
+#define FPREG_St(uc, index) *(M128A*)&(((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_87_ac[index])
+
+#define FPREG_ControlWord(uc) (((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_cw)
+#define FPREG_StatusWord(uc) (((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_sw)
+#define FPREG_TagWord(uc) (((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_tw)
+#define FPREG_ErrorOffset(uc) (*(DWORD*) &(((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_ip))
+#define FPREG_ErrorSelector(uc) *((WORD*) &(((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_ip) + 2)
+#define FPREG_DataOffset(uc) (*(DWORD*) &(((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_dp))
+#define FPREG_DataSelector(uc) *((WORD*) &(((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_dp) + 2)
+#define FPREG_MxCsr(uc) (((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_mxcsr)
+#define FPREG_MxCsr_Mask(uc) (((struct fxsave*)(&(uc)->uc_mcontext.__fpregs))->fx_mxcsr_mask)
+
+#else // BIT64
+
+#define MCREG_Ebx(mc) ((mc).__gregs[_REG_EBX])
+#define MCREG_Ecx(mc) ((mc).__gregs[_REG_ECX])
+#define MCREG_Edx(mc) ((mc).__gregs[_REG_EDX])
+#define MCREG_Esi(mc) ((mc).__gregs[_REG_ESI])
+#define MCREG_Edi(mc) ((mc).__gregs[_REG_EDI])
+#define MCREG_Ebp(mc) ((mc).__gregs[_REG_EBP])
+#define MCREG_Eax(mc) ((mc).__gregs[_REG_EAX])
+#define MCREG_Eip(mc) ((mc).__gregs[_REG_EIP])
+#define MCREG_Esp(mc) ((mc).__gregs[_REG_ESP])
+#define MCREG_SegCs(mc) ((mc).__gregs[_REG_CS])
+#define MCREG_SegSs(mc) ((mc).__gregs[_REG_SS])
+#define MCREG_EFlags(mc) ((mc).__gregs[_REG_RFLAGS])
+
+#endif // BIT64
+
+#elif HAVE_GREGSET_T
+
+#ifdef BIT64
+#define MCREG_Rbx(mc) ((mc).gregs[REG_RBX])
+#define MCREG_Rcx(mc) ((mc).gregs[REG_RCX])
+#define MCREG_Rdx(mc) ((mc).gregs[REG_RDX])
+#define MCREG_Rsi(mc) ((mc).gregs[REG_RSI])
+#define MCREG_Rdi(mc) ((mc).gregs[REG_RDI])
+#define MCREG_Rbp(mc) ((mc).gregs[REG_RBP])
+#define MCREG_Rax(mc) ((mc).gregs[REG_RAX])
+#define MCREG_Rip(mc) ((mc).gregs[REG_RIP])
+#define MCREG_Rsp(mc) ((mc).gregs[REG_RSP])
+#define MCREG_SegCs(mc) (*(WORD*)&((mc).gregs[REG_CSGSFS]))
+#define MCREG_R8(mc) ((mc).gregs[REG_R8])
+#define MCREG_R9(mc) ((mc).gregs[REG_R9])
+#define MCREG_R10(mc) ((mc).gregs[REG_R10])
+#define MCREG_R11(mc) ((mc).gregs[REG_R11])
+#define MCREG_R12(mc) ((mc).gregs[REG_R12])
+#define MCREG_R13(mc) ((mc).gregs[REG_R13])
+#define MCREG_R14(mc) ((mc).gregs[REG_R14])
+#define MCREG_R15(mc) ((mc).gregs[REG_R15])
+
+#define FPREG_Fpstate(uc) ((uc)->uc_mcontext.fpregs)
+#define FPREG_Xmm(uc, index) *(M128A*)&(FPREG_Fpstate(uc)->_xmm[index])
+
+#define FPREG_St(uc, index) *(M128A*)&(FPREG_Fpstate(uc)->_st[index])
+
+#define FPREG_ControlWord(uc) (FPREG_Fpstate(uc)->cwd)
+#define FPREG_StatusWord(uc) (FPREG_Fpstate(uc)->swd)
+#define FPREG_TagWord(uc) (FPREG_Fpstate(uc)->ftw)
+#define FPREG_ErrorOffset(uc) *(DWORD*)&(FPREG_Fpstate(uc)->rip)
+#define FPREG_ErrorSelector(uc) *(((WORD*)&(FPREG_Fpstate(uc)->rip)) + 2)
+#define FPREG_DataOffset(uc) *(DWORD*)&(FPREG_Fpstate(uc)->rdp)
+#define FPREG_DataSelector(uc) *(((WORD*)&(FPREG_Fpstate(uc)->rdp)) + 2)
+#define FPREG_MxCsr(uc) (FPREG_Fpstate(uc)->mxcsr)
+#define FPREG_MxCsr_Mask(uc) (FPREG_Fpstate(uc)->mxcr_mask)
+
+/////////////////////
+// Extended state
+
+inline _fpx_sw_bytes *FPREG_FpxSwBytes(const ucontext_t *uc)
+{
+ // Bytes 464..511 in the FXSAVE format are available for software to use for any purpose. In this case, they are used to
+ // indicate information about extended state.
+ _ASSERTE(reinterpret_cast<UINT8 *>(&FPREG_Fpstate(uc)->padding[12]) - reinterpret_cast<UINT8 *>(FPREG_Fpstate(uc)) == 464);
+
+ _ASSERTE(FPREG_Fpstate(uc) != nullptr);
+
+ return reinterpret_cast<_fpx_sw_bytes *>(&FPREG_Fpstate(uc)->padding[12]);
+}
+
+inline UINT32 FPREG_ExtendedSize(const ucontext_t *uc)
+{
+ _ASSERTE(FPREG_FpxSwBytes(uc)->magic1 == FP_XSTATE_MAGIC1);
+ return FPREG_FpxSwBytes(uc)->extended_size;
+}
+
+inline bool FPREG_HasExtendedState(const ucontext_t *uc)
+{
+ // See comments in /usr/include/x86_64-linux-gnu/asm/sigcontext.h for info on how to detect if extended state is present
+ static_assert_no_msg(FP_XSTATE_MAGIC2_SIZE == sizeof(UINT32));
+
+ if (FPREG_FpxSwBytes(uc)->magic1 != FP_XSTATE_MAGIC1)
+ {
+ return false;
+ }
+
+ UINT32 extendedSize = FPREG_ExtendedSize(uc);
+ if (extendedSize < sizeof(_xstate))
+ {
+ return false;
+ }
+
+ _ASSERTE(extendedSize >= FP_XSTATE_MAGIC2_SIZE);
+ return *reinterpret_cast<UINT32 *>(reinterpret_cast<UINT8 *>(FPREG_Fpstate(uc)) + (extendedSize - FP_XSTATE_MAGIC2_SIZE))
+ == FP_XSTATE_MAGIC2;
+}
+
+inline void *FPREG_Xstate_Ymmh(const ucontext_t *uc)
+{
+ static_assert_no_msg(sizeof(reinterpret_cast<_xstate *>(FPREG_Fpstate(uc))->ymmh.ymmh_space) == 16 * 16);
+ _ASSERTE(FPREG_HasExtendedState(uc));
+
+ return reinterpret_cast<_xstate *>(FPREG_Fpstate(uc))->ymmh.ymmh_space;
+}
+
+/////////////////////
+
+#else // BIT64
+
+#define MCREG_Ebx(mc) ((mc).gregs[REG_EBX])
+#define MCREG_Ecx(mc) ((mc).gregs[REG_ECX])
+#define MCREG_Edx(mc) ((mc).gregs[REG_EDX])
+#define MCREG_Esi(mc) ((mc).gregs[REG_ESI])
+#define MCREG_Edi(mc) ((mc).gregs[REG_EDI])
+#define MCREG_Ebp(mc) ((mc).gregs[REG_EBP])
+#define MCREG_Eax(mc) ((mc).gregs[REG_EAX])
+#define MCREG_Eip(mc) ((mc).gregs[REG_EIP])
+#define MCREG_Esp(mc) ((mc).gregs[REG_ESP])
+#define MCREG_SegCs(mc) ((mc).gregs[REG_CS])
+#define MCREG_SegSs(mc) ((mc).gregs[REG_SS])
+
+#endif // BIT64
+
+#define MCREG_EFlags(mc) ((mc).gregs[REG_EFL])
+
+#else // HAVE_GREGSET_T
+
+#ifdef BIT64
+
+#if defined(_ARM64_)
+#define MCREG_X0(mc) ((mc).regs[0])
+#define MCREG_X1(mc) ((mc).regs[1])
+#define MCREG_X2(mc) ((mc).regs[2])
+#define MCREG_X3(mc) ((mc).regs[3])
+#define MCREG_X4(mc) ((mc).regs[4])
+#define MCREG_X5(mc) ((mc).regs[5])
+#define MCREG_X6(mc) ((mc).regs[6])
+#define MCREG_X7(mc) ((mc).regs[7])
+#define MCREG_X8(mc) ((mc).regs[8])
+#define MCREG_X9(mc) ((mc).regs[9])
+#define MCREG_X10(mc) ((mc).regs[10])
+#define MCREG_X11(mc) ((mc).regs[11])
+#define MCREG_X12(mc) ((mc).regs[12])
+#define MCREG_X13(mc) ((mc).regs[13])
+#define MCREG_X14(mc) ((mc).regs[14])
+#define MCREG_X15(mc) ((mc).regs[15])
+#define MCREG_X16(mc) ((mc).regs[16])
+#define MCREG_X17(mc) ((mc).regs[17])
+#define MCREG_X18(mc) ((mc).regs[18])
+#define MCREG_X19(mc) ((mc).regs[19])
+#define MCREG_X20(mc) ((mc).regs[20])
+#define MCREG_X21(mc) ((mc).regs[21])
+#define MCREG_X22(mc) ((mc).regs[22])
+#define MCREG_X23(mc) ((mc).regs[23])
+#define MCREG_X24(mc) ((mc).regs[24])
+#define MCREG_X25(mc) ((mc).regs[25])
+#define MCREG_X26(mc) ((mc).regs[26])
+#define MCREG_X27(mc) ((mc).regs[27])
+#define MCREG_X28(mc) ((mc).regs[28])
+#define MCREG_Fp(mc) ((mc).regs[29])
+#define MCREG_Lr(mc) ((mc).regs[30])
+
+#define MCREG_Sp(mc) ((mc).sp)
+#define MCREG_Pc(mc) ((mc).pc)
+#define MCREG_PState(mc) ((mc).pstate)
+#define MCREG_Cpsr(mc) ((mc).cpsr)
+#else
+ // For FreeBSD, as found in x86/ucontext.h
+#define MCREG_Rbp(mc) ((mc).mc_rbp)
+#define MCREG_Rip(mc) ((mc).mc_rip)
+#define MCREG_Rsp(mc) ((mc).mc_rsp)
+#define MCREG_Rsi(mc) ((mc).mc_rsi)
+#define MCREG_Rdi(mc) ((mc).mc_rdi)
+#define MCREG_Rbx(mc) ((mc).mc_rbx)
+#define MCREG_Rdx(mc) ((mc).mc_rdx)
+#define MCREG_Rcx(mc) ((mc).mc_rcx)
+#define MCREG_Rax(mc) ((mc).mc_rax)
+#define MCREG_R8(mc) ((mc).mc_r8)
+#define MCREG_R9(mc) ((mc).mc_r9)
+#define MCREG_R10(mc) ((mc).mc_r10)
+#define MCREG_R11(mc) ((mc).mc_r11)
+#define MCREG_R12(mc) ((mc).mc_r12)
+#define MCREG_R13(mc) ((mc).mc_r13)
+#define MCREG_R14(mc) ((mc).mc_r14)
+#define MCREG_R15(mc) ((mc).mc_r15)
+#define MCREG_EFlags(mc) ((mc).mc_rflags)
+#define MCREG_SegCs(mc) ((mc).mc_cs)
+
+ // from x86/fpu.h: struct __envxmm64
+#define FPSTATE(uc) ((savefpu*)((uc)->uc_mcontext.mc_fpstate))
+#define FPREG_ControlWord(uc) FPSTATE(uc)->sv_env.en_cw
+#define FPREG_StatusWord(uc) FPSTATE(uc)->sv_env.en_sw
+#define FPREG_TagWord(uc) FPSTATE(uc)->sv_env.en_tw
+#define FPREG_MxCsr(uc) FPSTATE(uc)->sv_env.en_mxcsr
+#define FPREG_MxCsr_Mask(uc) FPSTATE(uc)->sv_env.en_mxcsr_mask
+#define FPREG_ErrorOffset(uc) *(DWORD*) &(FPSTATE(uc)->sv_env.en_rip)
+#define FPREG_ErrorSelector(uc) *((WORD*) &(FPSTATE(uc)->sv_env.en_rip) + 2)
+#define FPREG_DataOffset(uc) *(DWORD*) &(FPSTATE(uc)->sv_env.en_rdp)
+#define FPREG_DataSelector(uc) *((WORD*) &(FPSTATE(uc)->sv_env.en_rdp) + 2)
+
+#define FPREG_Xmm(uc, index) *(M128A*) &(FPSTATE(uc)->sv_xmm[index])
+#define FPREG_St(uc, index) *(M128A*) &(FPSTATE(uc)->sv_fp[index].fp_acc)
+#endif
+
+#else // BIT64
+
+#if defined(_ARM_)
+
+#define MCREG_R0(mc) ((mc).arm_r0)
+#define MCREG_R1(mc) ((mc).arm_r1)
+#define MCREG_R2(mc) ((mc).arm_r2)
+#define MCREG_R3(mc) ((mc).arm_r3)
+#define MCREG_R4(mc) ((mc).arm_r4)
+#define MCREG_R5(mc) ((mc).arm_r5)
+#define MCREG_R6(mc) ((mc).arm_r6)
+#define MCREG_R7(mc) ((mc).arm_r7)
+#define MCREG_R8(mc) ((mc).arm_r8)
+#define MCREG_R9(mc) ((mc).arm_r9)
+#define MCREG_R10(mc) ((mc).arm_r10)
+#define MCREG_R11(mc) ((mc).arm_fp)
+#define MCREG_R12(mc) ((mc).arm_ip)
+#define MCREG_Sp(mc) ((mc).arm_sp)
+#define MCREG_Lr(mc) ((mc).arm_lr)
+#define MCREG_Pc(mc) ((mc).arm_pc)
+#define MCREG_Cpsr(mc) ((mc).arm_cpsr)
+
+#elif defined(_X86_)
+
+#define MCREG_Ebx(mc) ((mc).mc_ebx)
+#define MCREG_Ecx(mc) ((mc).mc_ecx)
+#define MCREG_Edx(mc) ((mc).mc_edx)
+#define MCREG_Esi(mc) ((mc).mc_esi)
+#define MCREG_Edi(mc) ((mc).mc_edi)
+#define MCREG_Ebp(mc) ((mc).mc_ebp)
+#define MCREG_Eax(mc) ((mc).mc_eax)
+#define MCREG_Eip(mc) ((mc).mc_eip)
+#define MCREG_SegCs(mc) ((mc).mc_cs)
+#define MCREG_EFlags(mc) ((mc).mc_eflags)
+#define MCREG_Esp(mc) ((mc).mc_esp)
+#define MCREG_SegSs(mc) ((mc).mc_ss)
+
+#else
+#error "Unsupported arch"
+#endif
+
+#endif // BIT64
+
+#endif // HAVE_GREGSET_T
+
+
+#if HAVE_PT_REGS
+
+#ifdef BIT64
+#define PTREG_Rbx(ptreg) ((ptreg).rbx)
+#define PTREG_Rcx(ptreg) ((ptreg).rcx)
+#define PTREG_Rdx(ptreg) ((ptreg).rdx)
+#define PTREG_Rsi(ptreg) ((ptreg).rsi)
+#define PTREG_Rdi(ptreg) ((ptreg).rdi)
+#define PTREG_Rbp(ptreg) ((ptreg).rbp)
+#define PTREG_Rax(ptreg) ((ptreg).rax)
+#define PTREG_Rip(ptreg) ((ptreg).rip)
+#define PTREG_SegCs(ptreg) ((ptreg).cs)
+#define PTREG_SegSs(ptreg) ((ptreg).ss)
+#define PTREG_Rsp(ptreg) ((ptreg).rsp)
+#define PTREG_R8(ptreg) ((ptreg).r8)
+#define PTREG_R9(ptreg) ((ptreg).r9)
+#define PTREG_R10(ptreg) ((ptreg).r10)
+#define PTREG_R11(ptreg) ((ptreg).r11)
+#define PTREG_R12(ptreg) ((ptreg).r12)
+#define PTREG_R13(ptreg) ((ptreg).r13)
+#define PTREG_R14(ptreg) ((ptreg).r14)
+#define PTREG_R15(ptreg) ((ptreg).r15)
+
+#else // BIT64
+
+#if defined(_ARM_)
+#define PTREG_R0(ptreg) ((ptreg).uregs[0])
+#define PTREG_R1(ptreg) ((ptreg).uregs[1])
+#define PTREG_R2(ptreg) ((ptreg).uregs[2])
+#define PTREG_R3(ptreg) ((ptreg).uregs[3])
+#define PTREG_R4(ptreg) ((ptreg).uregs[4])
+#define PTREG_R5(ptreg) ((ptreg).uregs[5])
+#define PTREG_R6(ptreg) ((ptreg).uregs[6])
+#define PTREG_R7(ptreg) ((ptreg).uregs[7])
+#define PTREG_R8(ptreg) ((ptreg).uregs[8])
+#define PTREG_R9(ptreg) ((ptreg).uregs[9])
+#define PTREG_R10(ptreg) ((ptreg).uregs[10])
+#define PTREG_R11(ptreg) ((ptreg).uregs[11])
+#define PTREG_R12(ptreg) ((ptreg).uregs[12])
+#define PTREG_Sp(ptreg) ((ptreg).uregs[13])
+#define PTREG_Lr(ptreg) ((ptreg).uregs[14])
+#define PTREG_Pc(ptreg) ((ptreg).uregs[15])
+#define PTREG_Cpsr(ptreg) ((ptreg).uregs[16])
+#elif defined(_X86_)
+#define PTREG_Ebx(ptreg) ((ptreg).ebx)
+#define PTREG_Ecx(ptreg) ((ptreg).ecx)
+#define PTREG_Edx(ptreg) ((ptreg).edx)
+#define PTREG_Esi(ptreg) ((ptreg).esi)
+#define PTREG_Edi(ptreg) ((ptreg).edi)
+#define PTREG_Ebp(ptreg) ((ptreg).ebp)
+#define PTREG_Eax(ptreg) ((ptreg).eax)
+#define PTREG_Eip(ptreg) ((ptreg).eip)
+#define PTREG_SegCs(ptreg) ((ptreg).xcs)
+#define PTREG_SegSs(ptreg) ((ptreg).xss)
+#define PTREG_Esp(ptreg) ((ptreg).esp)
+#else
+#error "Unsupported arch"
+#endif
+
+#endif // BIT64
+
+
+#define PTREG_EFlags(ptreg) ((ptreg).eflags)
+
+#endif // HAVE_PT_REGS
+
+
+
+#if HAVE_BSD_REGS_T
+
+#ifndef BSD_REGS_STYLE
+#error "struct reg" has unrecognized format
+#endif
+
+#ifdef BIT64
+
+#define BSDREG_Rbx(reg) BSD_REGS_STYLE(reg,RBX,rbx)
+#define BSDREG_Rcx(reg) BSD_REGS_STYLE(reg,RCX,rcx)
+#define BSDREG_Rdx(reg) BSD_REGS_STYLE(reg,RDX,rdx)
+#define BSDREG_Rsi(reg) BSD_REGS_STYLE(reg,RSI,rsi)
+#define BSDREG_Rdi(reg) BSD_REGS_STYLE(reg,RDI,rdi)
+#define BSDREG_Rbp(reg) BSD_REGS_STYLE(reg,RBP,rbp)
+#define BSDREG_Rax(reg) BSD_REGS_STYLE(reg,RAX,rax)
+#define BSDREG_Rip(reg) BSD_REGS_STYLE(reg,RIP,rip)
+#define BSDREG_SegCs(reg) BSD_REGS_STYLE(reg,CS,cs)
+#define BSDREG_SegSs(reg) BSD_REGS_STYLE(reg,SS,ss)
+#define BSDREG_Rsp(reg) BSD_REGS_STYLE(reg,RSP,rsp)
+#define BSDREG_R8(reg) BSD_REGS_STYLE(reg,R8,r8)
+#define BSDREG_R9(reg) BSD_REGS_STYLE(reg,R9,r9)
+#define BSDREG_R10(reg) BSD_REGS_STYLE(reg,R10,r10)
+#define BSDREG_R11(reg) BSD_REGS_STYLE(reg,R11,r11)
+#define BSDREG_R12(reg) BSD_REGS_STYLE(reg,R12,r12)
+#define BSDREG_R13(reg) BSD_REGS_STYLE(reg,R13,r13)
+#define BSDREG_R14(reg) BSD_REGS_STYLE(reg,R14,r14)
+#define BSDREG_R15(reg) BSD_REGS_STYLE(reg,R15,r15)
+#define BSDREG_EFlags(reg) BSD_REGS_STYLE(reg,RFLAGS,rflags)
+
+#else // BIT64
+
+#define BSDREG_Ebx(reg) BSD_REGS_STYLE(reg,EBX,ebx)
+#define BSDREG_Ecx(reg) BSD_REGS_STYLE(reg,ECX,ecx)
+#define BSDREG_Edx(reg) BSD_REGS_STYLE(reg,EDX,edx)
+#define BSDREG_Esi(reg) BSD_REGS_STYLE(reg,ESI,esi)
+#define BSDREG_Edi(reg) BSD_REGS_STYLE(reg,EDI,edi)
+#define BSDREG_Ebp(reg) BSD_REGS_STYLE(reg,EDP,ebp)
+#define BSDREG_Eax(reg) BSD_REGS_STYLE(reg,EAX,eax)
+#define BSDREG_Eip(reg) BSD_REGS_STYLE(reg,EIP,eip)
+#define BSDREG_SegCs(reg) BSD_REGS_STYLE(reg,CS,cs)
+#define BSDREG_EFlags(reg) BSD_REGS_STYLE(reg,EFLAGS,eflags)
+#define BSDREG_Esp(reg) BSD_REGS_STYLE(reg,ESP,esp)
+#define BSDREG_SegSs(reg) BSD_REGS_STYLE(reg,SS,ss)
+
+#endif // BIT64
+
+#endif // HAVE_BSD_REGS_T
+
+inline static DWORD64 CONTEXTGetPC(LPCONTEXT pContext)
+{
+#if defined(_AMD64_)
+ return pContext->Rip;
+#elif defined(_ARM64_) || defined(_ARM_)
+ return pContext->Pc;
+#else
+#error don't know how to get the program counter for this architecture
+#endif
+}
+
+inline static void CONTEXTSetPC(LPCONTEXT pContext, DWORD64 pc)
+{
+#if defined(_AMD64_)
+ pContext->Rip = pc;
+#elif defined(_ARM64_) || defined(_ARM_)
+ pContext->Pc = pc;
+#else
+#error don't know how to set the program counter for this architecture
+#endif
+}
+
+/*++
+Function :
+ CONTEXT_CaptureContext
+
+ Captures the context of the caller.
+ The returned context is suitable for performing
+ a virtual unwind.
+
+Parameters :
+ LPCONTEXT lpContext : new context
+
+--*/
+void
+CONTEXT_CaptureContext(
+ LPCONTEXT lpContext
+ );
+
+/*++
+Function :
+ CONTEXT_SetThreadContext
+
+ Processor-dependent implementation of SetThreadContext
+
+Parameters :
+ HANDLE hThread : thread whose context is to be set
+ CONTEXT *lpContext : new context
+
+Return value :
+ TRUE on success, FALSE on failure
+
+--*/
+BOOL
+CONTEXT_SetThreadContext(
+ DWORD dwProcessId,
+ pthread_t self,
+ CONST CONTEXT *lpContext
+ );
+
+/*++
+Function :
+ CONTEXT_GetThreadContext
+
+ Processor-dependent implementation of GetThreadContext
+
+Parameters :
+ HANDLE hThread : thread whose context is to retrieved
+ LPCONTEXT lpContext : destination for thread's context
+
+Return value :
+ TRUE on success, FALSE on failure
+
+--*/
+BOOL
+CONTEXT_GetThreadContext(
+ DWORD dwProcessId,
+ pthread_t self,
+ LPCONTEXT lpContext);
+
+#if HAVE_MACH_EXCEPTIONS
+/*++
+Function:
+ CONTEXT_GetThreadContextFromPort
+
+ Helper for GetThreadContext that uses a mach_port
+--*/
+kern_return_t
+CONTEXT_GetThreadContextFromPort(
+ mach_port_t Port,
+ LPCONTEXT lpContext);
+
+/*++
+Function:
+ SetThreadContextOnPort
+
+ Helper for CONTEXT_SetThreadContext
+--*/
+kern_return_t
+CONTEXT_SetThreadContextOnPort(
+ mach_port_t Port,
+ IN CONST CONTEXT *lpContext);
+
+/*++
+Function:
+ GetThreadContextFromThreadState
+
+ Helper for mach exception support
+--*/
+void
+CONTEXT_GetThreadContextFromThreadState(
+ thread_state_flavor_t stateFlavor,
+ thread_state_t threadState,
+ LPCONTEXT lpContext);
+
+#else // HAVE_MACH_EXCEPTIONS
+/*++
+Function :
+ CONTEXTToNativeContext
+
+ Converts a CONTEXT record to a native context.
+
+Parameters :
+ CONST CONTEXT *lpContext : CONTEXT to convert, including
+ flags that determine which registers are valid in
+ lpContext and which ones to set in native
+ native_context_t *native : native context to fill in
+
+Return value :
+ None
+
+--*/
+void CONTEXTToNativeContext(CONST CONTEXT *lpContext, native_context_t *native);
+
+/*++
+Function :
+ CONTEXTFromNativeContext
+
+ Converts a native context to a CONTEXT record.
+
+Parameters :
+ const native_context_t *native : native context to convert
+ LPCONTEXT lpContext : CONTEXT to fill in
+ ULONG contextFlags : flags that determine which registers are valid in
+ native and which ones to set in lpContext
+
+Return value :
+ None
+
+--*/
+void CONTEXTFromNativeContext(const native_context_t *native, LPCONTEXT lpContext,
+ ULONG contextFlags);
+
+/*++
+Function :
+ GetNativeContextPC
+
+ Returns the program counter from the native context.
+
+Parameters :
+ const native_context_t *context : native context
+
+Return value :
+ The program counter from the native context.
+
+--*/
+LPVOID GetNativeContextPC(const native_context_t *context);
+
+/*++
+Function :
+ CONTEXTGetExceptionCodeForSignal
+
+ Translates signal and context information to a Win32 exception code.
+
+Parameters :
+ const siginfo_t *siginfo : signal information from a signal handler
+ const native_context_t *context : context information
+
+Return value :
+ The Win32 exception code that corresponds to the signal and context
+ information.
+
+--*/
+DWORD CONTEXTGetExceptionCodeForSignal(const siginfo_t *siginfo,
+ const native_context_t *context);
+
+#endif // HAVE_MACH_EXCEPTIONS else
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // _PAL_CONTEXT_H_
diff --git a/src/pal/src/include/pal/corunix.hpp b/src/pal/src/include/pal/corunix.hpp
new file mode 100644
index 0000000000..e9e9503ed3
--- /dev/null
+++ b/src/pal/src/include/pal/corunix.hpp
@@ -0,0 +1,1359 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ corunix.hpp
+
+Abstract:
+
+ Internal interface and object definitions
+
+
+
+--*/
+
+#ifndef _CORUNIX_H
+#define _CORUNIX_H
+
+#include "palinternal.h"
+
+namespace CorUnix
+{
+ typedef DWORD PAL_ERROR;
+
+ //
+ // Forward declarations for classes defined in other headers
+ //
+
+ class CPalThread;
+
+ //
+ // Forward declarations for items in this header
+ //
+
+ class CObjectType;
+ class IPalObject;
+
+ //
+ // A simple counted string class. Using counted strings
+ // allows for some optimizations when searching for a matching string.
+ //
+
+ class CPalString
+ {
+ protected:
+
+ const WCHAR *m_pwsz; // NULL terminated
+
+ //
+ // Length of string, not including terminating NULL
+ //
+
+ DWORD m_dwStringLength;
+
+ //
+ // Length of buffer backing string; must be at least 1+dwStringLength
+ //
+
+ DWORD m_dwMaxLength;
+
+ public:
+
+ CPalString()
+ :
+ m_pwsz(NULL),
+ m_dwStringLength(0),
+ m_dwMaxLength(0)
+ {
+ };
+
+ CPalString(
+ const WCHAR *pwsz
+ )
+ {
+ SetString(pwsz);
+ };
+
+ void
+ SetString(
+ const WCHAR *pwsz
+ )
+ {
+ SetStringWithLength(pwsz, PAL_wcslen(pwsz));
+ };
+
+ void
+ SetStringWithLength(
+ const WCHAR *pwsz,
+ DWORD dwStringLength
+ )
+ {
+ m_pwsz = pwsz;
+ m_dwStringLength = dwStringLength;
+ m_dwMaxLength = m_dwStringLength + 1;
+
+ };
+
+ PAL_ERROR
+ CopyString(
+ CPalString *psSource
+ );
+
+ void
+ FreeBuffer();
+
+ const WCHAR *
+ GetString()
+ {
+ return m_pwsz;
+ };
+
+ DWORD
+ GetStringLength()
+ {
+ return m_dwStringLength;
+ };
+
+ DWORD
+ GetMaxLength()
+ {
+ return m_dwMaxLength;
+ };
+
+ };
+
+ //
+ // Signature of the cleanup routine that is to be called for an object
+ // type when:
+ // 1) The object's refcount drops to 0
+ // 2) A process is shutting down
+ // 3) A process has released all local references to the object
+ //
+ // The cleanup routine must only cleanup the object's shared state
+ // when the last parameter (fCleanupSharedSate) is TRUE. When
+ // fCleanupSharedState is FALSE the cleanup routine must not attempt
+ // to access the shared data for the object, as another process may
+ // have already deleted it. ($$REIVEW -- would someone ever need access
+ // to the shared data in order to cleanup process local state?)
+ //
+ // When the third paramter (fShutdown) is TRUE the process is in
+ // the act of exiting. The cleanup routine should not perform any
+ // unnecessary cleanup operations (e.g., closing file descriptors,
+ // since the OS will automatically close them when the process exits)
+ // in this situation.
+ //
+
+ typedef void (*OBJECTCLEANUPROUTINE) (
+ CPalThread *, // pThread
+ IPalObject *, // pObjectToCleanup
+ bool, // fShutdown
+ bool // fCleanupSharedState
+ );
+
+ //
+ // Signature of the initialization routine that is to be called
+ // when the first reference within a process to an existing
+ // object comes into existence. This routine is responsible for
+ // initializing the object's process local data, based on the
+ // immutable and shared data. The thread that this routine is
+ // called on holds an implicit read lock on the shared data.
+ //
+
+ typedef PAL_ERROR (*OBJECTINITROUTINE) (
+ CPalThread *, // pThread
+ CObjectType *, // pObjectType
+ void *, // pImmutableData
+ void *, // pSharedData
+ void * // pProcessLocalData
+ );
+
+ enum PalObjectTypeId
+ {
+ otiAutoResetEvent = 0,
+ otiManualResetEvent,
+ otiMutex,
+ otiNamedMutex,
+ otiSemaphore,
+ otiFile,
+ otiFileMapping,
+ otiSocket,
+ otiProcess,
+ otiThread,
+ otiIOCompletionPort,
+ ObjectTypeIdCount // This entry must come last in the enumeration
+ };
+
+ //
+ // There should be one instance of CObjectType for each supported
+ // type in a process; this allows for pointer equality tests
+ // to be used (though in general it's probably better to use
+ // checks based on the type ID). All members of this structure are
+ // immutable.
+ //
+ // The data size members control how much space will be allocated for
+ // instances of this object. Any or all of those members may be 0.
+ //
+ // dwSupportedAccessRights is the mask of valid access bits for this
+ // object type. Supported generic rights should not be included in
+ // this member.
+ //
+ // The generic access rights mapping (structure TBD) defines how the
+ // supported generic access rights (e.g., GENERIC_READ) map to the
+ // specific access rights for this object type.
+ //
+ // If instances of this object may have a security descriptor set on
+ // them eSecuritySupport should be set to SecuritySupported. If the OS can
+ // persist security information for the object type (as would be the case
+ // for, say, files) eSecurityPersistence should be set to
+ // OSPersistedSecurityInfo.
+ //
+ // If the object may have a name eObjectNameSupport should be
+ // ObjectCanHaveName. A named object can be opened in more than one
+ // process.
+ //
+ // If it is possible to duplicate a handle to an object across process
+ // boundaries then eHandleDuplicationSupport should be set to
+ // CrossProcessDuplicationAllowed. Note that it is possible to have
+ // an object type where eObjectNameSupport is ObjectCanHaveName and
+ // eHandleDuplicationSupport is LocalDuplicationOnly. For these object
+ // types an unnamed object instance will only have references from
+ // the creating process.
+ //
+ // If the object may be waited on eSynchronizationSupport should be
+ // WaitableObject. (Note that this implies that object type supports
+ // the SYNCHRONIZE access right.)
+ //
+ // The remaining members describe the wait-object semantics for the
+ // object type when eSynchronizationSupport is WaitableObject:
+ //
+ // * eSignalingSemantics: SingleTransitionObject for objects that, once
+ // they transition to the signaled state, can never transition back to
+ // the unsignaled state (e.g., processes and threads)
+ //
+ // * eThreadReleaseSemantics: if ThreadReleaseAltersSignalCount the object's
+ // signal count is decremented when a waiting thread is released; otherwise,
+ // the signal count is not modified (as is desired for a manual reset event).
+ // Must be ThreadReleaseHasNoSideEffects if eSignalingSemantics is
+ // SingleTransitionObject
+ //
+ // * eOwnershipSemantics: OwnershipTracked only for mutexes, for which the
+ // previous two items must also ObjectCanBeUnsignaled and
+ // ThreadReleaseAltersSignalCount.
+ //
+
+ class CObjectType
+ {
+ public:
+
+ enum SecuritySupport
+ {
+ SecuritySupported,
+ SecurityNotSupported
+ };
+
+ enum SecurityPersistence
+ {
+ OSPersistedSecurityInfo,
+ SecurityInfoNotPersisted
+ };
+
+ enum ObjectNameSupport
+ {
+ ObjectCanHaveName,
+ UnnamedObject
+ };
+
+ enum HandleDuplicationSupport
+ {
+ CrossProcessDuplicationAllowed,
+ LocalDuplicationOnly
+ };
+
+ enum SynchronizationSupport
+ {
+ WaitableObject,
+ UnwaitableObject
+ };
+
+ enum SignalingSemantics
+ {
+ ObjectCanBeUnsignaled,
+ SingleTransitionObject,
+ SignalingNotApplicable
+ };
+
+ enum ThreadReleaseSemantics
+ {
+ ThreadReleaseAltersSignalCount,
+ ThreadReleaseHasNoSideEffects,
+ ThreadReleaseNotApplicable
+ };
+
+ enum OwnershipSemantics
+ {
+ OwnershipTracked,
+ NoOwner,
+ OwnershipNotApplicable
+ };
+
+ private:
+
+ //
+ // Array that maps object type IDs to the corresponding
+ // CObjectType instance
+ //
+
+ static CObjectType* s_rgotIdMapping[];
+
+ PalObjectTypeId m_eTypeId;
+ OBJECTCLEANUPROUTINE m_pCleanupRoutine;
+ OBJECTINITROUTINE m_pInitRoutine;
+ DWORD m_dwImmutableDataSize;
+ DWORD m_dwProcessLocalDataSize;
+ DWORD m_dwSharedDataSize;
+ DWORD m_dwSupportedAccessRights;
+ // Generic access rights mapping
+ SecuritySupport m_eSecuritySupport;
+ SecurityPersistence m_eSecurityPersistence;
+ ObjectNameSupport m_eObjectNameSupport;
+ HandleDuplicationSupport m_eHandleDuplicationSupport;
+ SynchronizationSupport m_eSynchronizationSupport;
+ SignalingSemantics m_eSignalingSemantics;
+ ThreadReleaseSemantics m_eThreadReleaseSemantics;
+ OwnershipSemantics m_eOwnershipSemantics;
+
+ public:
+
+ CObjectType(
+ PalObjectTypeId eTypeId,
+ OBJECTCLEANUPROUTINE pCleanupRoutine,
+ OBJECTINITROUTINE pInitRoutine,
+ DWORD dwImmutableDataSize,
+ DWORD dwProcessLocalDataSize,
+ DWORD dwSharedDataSize,
+ DWORD dwSupportedAccessRights,
+ SecuritySupport eSecuritySupport,
+ SecurityPersistence eSecurityPersistence,
+ ObjectNameSupport eObjectNameSupport,
+ HandleDuplicationSupport eHandleDuplicationSupport,
+ SynchronizationSupport eSynchronizationSupport,
+ SignalingSemantics eSignalingSemantics,
+ ThreadReleaseSemantics eThreadReleaseSemantics,
+ OwnershipSemantics eOwnershipSemantics
+ )
+ :
+ m_eTypeId(eTypeId),
+ m_pCleanupRoutine(pCleanupRoutine),
+ m_pInitRoutine(pInitRoutine),
+ m_dwImmutableDataSize(dwImmutableDataSize),
+ m_dwProcessLocalDataSize(dwProcessLocalDataSize),
+ m_dwSharedDataSize(dwSharedDataSize),
+ m_dwSupportedAccessRights(dwSupportedAccessRights),
+ m_eSecuritySupport(eSecuritySupport),
+ m_eSecurityPersistence(eSecurityPersistence),
+ m_eObjectNameSupport(eObjectNameSupport),
+ m_eHandleDuplicationSupport(eHandleDuplicationSupport),
+ m_eSynchronizationSupport(eSynchronizationSupport),
+ m_eSignalingSemantics(eSignalingSemantics),
+ m_eThreadReleaseSemantics(eThreadReleaseSemantics),
+ m_eOwnershipSemantics(eOwnershipSemantics)
+ {
+ s_rgotIdMapping[eTypeId] = this;
+ };
+
+ static
+ CObjectType *
+ GetObjectTypeById(
+ PalObjectTypeId otid
+ )
+ {
+ return s_rgotIdMapping[otid];
+ };
+
+ PalObjectTypeId
+ GetId(
+ void
+ )
+ {
+ return m_eTypeId;
+ };
+
+ OBJECTCLEANUPROUTINE
+ GetObjectCleanupRoutine(
+ void
+ )
+ {
+ return m_pCleanupRoutine;
+ };
+
+ OBJECTINITROUTINE
+ GetObjectInitRoutine(
+ void
+ )
+ {
+ return m_pInitRoutine;
+ };
+
+ DWORD
+ GetImmutableDataSize(
+ void
+ )
+ {
+ return m_dwImmutableDataSize;
+ };
+
+ DWORD
+ GetProcessLocalDataSize(
+ void
+ )
+ {
+ return m_dwProcessLocalDataSize;
+ };
+
+ DWORD
+ GetSharedDataSize(
+ void
+ )
+ {
+ return m_dwSharedDataSize;
+ };
+
+ DWORD
+ GetSupportedAccessRights(
+ void
+ )
+ {
+ return m_dwSupportedAccessRights;
+ };
+
+ // Generic access rights mapping
+
+ SecuritySupport
+ GetSecuritySupport(
+ void
+ )
+ {
+ return m_eSecuritySupport;
+ };
+
+ SecurityPersistence
+ GetSecurityPersistence(
+ void
+ )
+ {
+ return m_eSecurityPersistence;
+ };
+
+ ObjectNameSupport
+ GetObjectNameSupport(
+ void
+ )
+ {
+ return m_eObjectNameSupport;
+ };
+
+ HandleDuplicationSupport
+ GetHandleDuplicationSupport(
+ void
+ )
+ {
+ return m_eHandleDuplicationSupport;
+ };
+
+ SynchronizationSupport
+ GetSynchronizationSupport(
+ void
+ )
+ {
+ return m_eSynchronizationSupport;
+ };
+
+ SignalingSemantics
+ GetSignalingSemantics(
+ void
+ )
+ {
+ return m_eSignalingSemantics;
+ };
+
+ ThreadReleaseSemantics
+ GetThreadReleaseSemantics(
+ void
+ )
+ {
+ return m_eThreadReleaseSemantics;
+ };
+
+ OwnershipSemantics
+ GetOwnershipSemantics(
+ void
+ )
+ {
+ return m_eOwnershipSemantics;
+ };
+ };
+
+ class CAllowedObjectTypes
+ {
+ private:
+
+ bool m_rgfAllowedTypes[ObjectTypeIdCount];
+
+ public:
+
+ bool
+ IsTypeAllowed(PalObjectTypeId eTypeId);
+
+ //
+ // Constructor for multiple allowed types
+ //
+
+ CAllowedObjectTypes(
+ PalObjectTypeId rgAllowedTypes[],
+ DWORD dwAllowedTypeCount
+ );
+
+ //
+ // Single allowed type constructor
+ //
+
+ CAllowedObjectTypes(
+ PalObjectTypeId eAllowedType
+ );
+
+ //
+ // Allow all types or no types constructor
+ //
+
+ CAllowedObjectTypes(
+ bool fAllowAllObjectTypes
+ )
+ {
+ for (DWORD dw = 0; dw < ObjectTypeIdCount; dw += 1)
+ {
+ m_rgfAllowedTypes[dw] = fAllowAllObjectTypes;
+ }
+ };
+
+ ~CAllowedObjectTypes()
+ {
+ };
+ };
+
+ //
+ // Attributes for a given object instance. If the object does not have
+ // a name the sObjectName member should be zero'd out. If the default
+ // security attributes are desired then pSecurityAttributes should
+ // be NULL.
+ //
+
+ class CObjectAttributes
+ {
+ public:
+
+ CPalString sObjectName;
+ LPSECURITY_ATTRIBUTES pSecurityAttributes;
+
+ CObjectAttributes(
+ const WCHAR *pwszObjectName,
+ LPSECURITY_ATTRIBUTES pSecurityAttributes_
+ )
+ :
+ pSecurityAttributes(pSecurityAttributes_)
+ {
+ if (NULL != pwszObjectName)
+ {
+ sObjectName.SetString(pwszObjectName);
+ }
+ };
+
+ CObjectAttributes()
+ :
+ pSecurityAttributes(NULL)
+ {
+ };
+ };
+
+ //
+ // ISynchStateController is used to modify any object's synchronization
+ // state. It is intended to be used from within the APIs exposed for
+ // various objects (e.g., SetEvent, ReleaseMutex, etc.).
+ //
+ // Each ISynchStateController instance implicitly holds what should be
+ // viewed as the global dispatcher lock, and as such should be released
+ // as quickly as possible. An ISynchStateController instance is bound to
+ // the thread that requested it; it may not be passed to a different
+ // thread.
+ //
+
+ class ISynchStateController
+ {
+ public:
+
+ virtual
+ PAL_ERROR
+ GetSignalCount(
+ LONG *plSignalCount
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ SetSignalCount(
+ LONG lNewCount
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ IncrementSignalCount(
+ LONG lAmountToIncrement
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ DecrementSignalCount(
+ LONG lAmountToDecrement
+ ) = 0;
+
+ //
+ // The following two routines may only be used for object types
+ // where eOwnershipSemantics is OwnershipTracked (i.e., mutexes).
+ //
+
+ //
+ // SetOwner is intended to be used in the implementation of
+ // CreateMutex when bInitialOwner is TRUE. It must be called
+ // before the new object instance is registered with the
+ // handle manager. Any other call to this method is an error.
+ //
+
+ virtual
+ PAL_ERROR
+ SetOwner(
+ CPalThread *pNewOwningThread
+ ) = 0;
+
+ //
+ // DecrementOwnershipCount returns an error if the object
+ // is unowned, or if the thread this controller is bound to
+ // is not the owner of the object.
+ //
+
+ virtual
+ PAL_ERROR
+ DecrementOwnershipCount(
+ void
+ ) = 0;
+
+ virtual
+ void
+ ReleaseController(
+ void
+ ) = 0;
+ };
+
+ //
+ // ISynchWaitController is used to indicate a thread's desire to wait for
+ // an object (which possibly includes detecting instances where the wait
+ // can be satisfied without blocking). It is intended to be used by object
+ // wait function (WaitForSingleObject, etc.).
+ //
+ // Each ISynchWaitController instance implicitly holds what should be
+ // viewed as the global dispatcher lock, and as such should be released
+ // as quickly as possible. An ISynchWaitController instance is bound to
+ // the thread that requested it; it may not be passed to a different
+ // thread.
+ //
+ // A thread may hold multiple ISynchWaitController instances
+ // simultaneously.
+ //
+
+ enum WaitType
+ {
+ SingleObject,
+ MultipleObjectsWaitOne,
+ MultipleObjectsWaitAll
+ };
+
+ class ISynchWaitController
+ {
+ public:
+
+ //
+ // CanThreadWaitWithoutBlocking informs the caller if a wait
+ // operation may succeed immediately, but does not actually
+ // alter any object state. ReleaseWaitingThreadWithoutBlocking
+ // alters the object state, and will return an error if it is
+ // not possible for the wait to be immediately satisfied.
+ //
+
+ virtual
+ PAL_ERROR
+ CanThreadWaitWithoutBlocking(
+ bool *pfCanWaitWithoutBlocking, // OUT
+ bool *pfAbandoned
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ ReleaseWaitingThreadWithoutBlocking(
+ ) = 0;
+
+ //
+ // dwIndex is intended for MultipleObjectsWaitOne situations. The
+ // index for the object that becomes signaled and satisfies the
+ // wait will be returned in the call to BlockThread.
+ //
+
+ virtual
+ PAL_ERROR
+ RegisterWaitingThread(
+ WaitType eWaitType,
+ DWORD dwIndex,
+ bool fAltertable
+ ) = 0;
+
+ //
+ // Why is there no unregister waiting thread routine? Unregistration
+ // is the responsibility of the synchronization provider, not the
+ // implementation of the wait object routines. (I can be convinced
+ // that this isn't the best approach, though...)
+ //
+
+ virtual
+ void
+ ReleaseController(
+ void
+ ) = 0;
+ };
+
+ enum LockType
+ {
+ ReadLock,
+ WriteLock
+ };
+
+ class IDataLock
+ {
+ public:
+
+ //
+ // If a thread obtains a write lock but does not actually
+ // modify any data it should set fDataChanged to FALSE. If
+ // a thread obtain a read lock and does actually modify any
+ // data it should be taken out back and shot.
+ //
+
+ virtual
+ void
+ ReleaseLock(
+ CPalThread *pThread, // IN, OPTIONAL
+ bool fDataChanged
+ ) = 0;
+ };
+
+ //
+ // The following two enums are part of the local object
+ // optimizations
+ //
+
+ enum ObjectDomain
+ {
+ ProcessLocalObject,
+ SharedObject
+ };
+
+ enum WaitDomain
+ {
+ LocalWait, // All objects in the wait set are local to this process
+ MixedWait, // Some objects are local; some are shared
+ SharedWait // All objects in the wait set are shared
+ };
+
+ class IPalObject
+ {
+ public:
+
+ virtual
+ CObjectType *
+ GetObjectType(
+ VOID
+ ) = 0;
+
+ virtual
+ CObjectAttributes *
+ GetObjectAttributes(
+ VOID
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ GetImmutableData(
+ void **ppvImmutableData // OUT
+ ) = 0;
+
+ //
+ // The following two routines obtain either a read or write
+ // lock on the data in question. If a thread needs to examine
+ // both process-local and shared data simultaneously it must obtain
+ // the shared data first. A thread may not hold data locks
+ // on two different objects at the same time.
+ //
+
+ virtual
+ PAL_ERROR
+ GetProcessLocalData(
+ CPalThread *pThread, // IN, OPTIONAL
+ LockType eLockRequest,
+ IDataLock **ppDataLock, // OUT
+ void **ppvProcessLocalData // OUT
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ GetSharedData(
+ CPalThread *pThread, // IN, OPTIONAL
+ LockType eLockRequest,
+ IDataLock **ppDataLock, // OUT
+ void **ppvSharedData // OUT
+ ) = 0;
+
+ //
+ // The following two routines obtain the global dispatcher lock.
+ // If a thread needs to make use of a synchronization interface
+ // and examine object data it must obtain the synchronization
+ // interface first. A thread is allowed to hold synchronization
+ // interfaces for multiple objects at the same time if it obtains
+ // all of the interfaces through a single call (see IPalSynchronizationManager
+ // below).
+ //
+ // The single-call restriction allows the underlying implementation
+ // to possibly segement the global dispatcher lock. If this restriction
+ // were not in place (i.e., if a single thread were allowed to call
+ // GetSynchXXXController for multiple objects) no such segmentation
+ // would be possible as there would be no way know in what order a
+ // thread would choose to obtain the controllers.
+ //
+ // Note: this design precludes simultaneous acquisition of both
+ // the state and wait controller for an object but there are
+ // currently no places where doing so would be necessary.
+ //
+
+ virtual
+ PAL_ERROR
+ GetSynchStateController(
+ CPalThread *pThread, // IN, OPTIONAL
+ ISynchStateController **ppStateController // OUT
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ GetSynchWaitController(
+ CPalThread *pThread, // IN, OPTIONAL
+ ISynchWaitController **ppWaitController // OUT
+ ) = 0;
+
+ virtual
+ DWORD
+ AddReference(
+ void
+ ) = 0;
+
+ virtual
+ DWORD
+ ReleaseReference(
+ CPalThread *pThread
+ ) = 0;
+
+ //
+ // This routine is mainly intended for the synchronization
+ // manager. The promotion / process synch lock must be held
+ // before calling this routine.
+ //
+
+ virtual
+ ObjectDomain
+ GetObjectDomain(
+ void
+ ) = 0;
+
+ //
+ // This routine is only for use by the synchronization manager
+ // (specifically, for GetSynch*ControllersForObjects). The
+ // caller must have acquired the appropriate lock before
+ // (whatever exactly that must be) before calling this routine.
+ //
+
+ virtual
+ PAL_ERROR
+ GetObjectSynchData(
+ VOID **ppvSynchData // OUT
+ ) = 0;
+
+ };
+
+ class IPalProcess
+ {
+ public:
+ virtual
+ DWORD
+ GetProcessID(
+ void
+ ) = 0;
+ };
+
+ class IPalObjectManager
+ {
+ public:
+
+ //
+ // Object creation (e.g., what is done by CreateEvent) is a two step
+ // process. First, the new object is allocated and the initial
+ // properties set (e.g., initially signaled). Next, the object is
+ // registered, yielding a handle. If an object of the same name
+ // and appropriate type already existed the returned handle will refer
+ // to the previously existing object, and the newly allocated object
+ // will have been thrown away.
+ //
+ // (The two phase process minimizes the amount of time that any
+ // namespace locks need to be held. While some wasted work may be
+ // done in the existing object case that work only impacts the calling
+ // thread. Checking first for existence and then allocating and
+ // initializing on failure requires any namespace lock to be held for
+ // a much longer period of time, impacting the entire system.)
+ //
+
+ virtual
+ PAL_ERROR
+ AllocateObject(
+ CPalThread *pThread, // IN, OPTIONAL
+ CObjectType *pType,
+ CObjectAttributes *pAttributes,
+ IPalObject **ppNewObject // OUT
+ ) = 0;
+
+ //
+ // After calling RegisterObject pObjectToRegister is no
+ // longer valid. If successful there are two references
+ // on the returned object -- one for the handle, and one
+ // for the instance returned in ppRegisteredObject. The
+ // caller, therefore, is responsible for releasing the
+ // latter.
+ //
+ // For named object pAllowedTypes specifies what type of
+ // existing objects can be returned in ppRegisteredObjects.
+ // This is primarily intended for CreateEvent, so that
+ // a ManualResetEvent can be returned when attempting to
+ // register an AutoResetEvent (and vice-versa). pAllowedTypes
+ // must include the type of pObjectToRegister.
+ //
+
+ virtual
+ PAL_ERROR
+ RegisterObject(
+ CPalThread *pThread, // IN, OPTIONAL
+ IPalObject *pObjectToRegister,
+ CAllowedObjectTypes *pAllowedTypes,
+ DWORD dwRightsRequested,
+ HANDLE *pHandle, // OUT
+ IPalObject **ppRegisteredObject // OUT
+ ) = 0;
+
+ //
+ // LocateObject is used for OpenXXX routines. ObtainHandleForObject
+ // is needed for the OpenXXX routines and DuplicateHandle.
+ //
+
+ virtual
+ PAL_ERROR
+ LocateObject(
+ CPalThread *pThread, // IN, OPTIONAL
+ CPalString *psObjectToLocate,
+ CAllowedObjectTypes *pAllowedTypes,
+ IPalObject **ppObject // OUT
+ ) = 0;
+
+ //
+ // pProcessForHandle is to support cross-process handle
+ // duplication. It only needs to be specified when acquiring
+ // a handle meant for use in a different process; it should
+ // be left NULL when acquiring a handle for the current
+ // process.
+ //
+
+ virtual
+ PAL_ERROR
+ ObtainHandleForObject(
+ CPalThread *pThread, // IN, OPTIONAL
+ IPalObject *pObject,
+ DWORD dwRightsRequested,
+ bool fInheritHandle,
+ IPalProcess *pProcessForHandle, // IN, OPTIONAL
+ HANDLE *pNewHandle // OUT
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ RevokeHandle(
+ CPalThread *pThread, // IN, OPTIONAL
+ HANDLE hHandleToRevoke
+ ) = 0;
+
+ //
+ // The Reference routines are called to obtain the
+ // object that a handle refers to. The caller must
+ // specify the rights that the handle must hold for
+ // the operation that it is about to perform. The caller
+ // is responsible for converting generic rights to specific
+ // rights. The caller must also specify what object types
+ // are permissible for the object.
+ //
+ // The returned object[s], on success, are referenced,
+ // and the caller is responsible for releasing those references
+ // when appropriate.
+ //
+
+ virtual
+ PAL_ERROR
+ ReferenceObjectByHandle(
+ CPalThread *pThread, // IN, OPTIONAL
+ HANDLE hHandleToReference,
+ CAllowedObjectTypes *pAllowedTypes,
+ DWORD dwRightsRequired,
+ IPalObject **ppObject // OUT
+ ) = 0;
+
+ //
+ // This routine is intended for WaitForMultipleObjects[Ex]
+ //
+
+ virtual
+ PAL_ERROR
+ ReferenceMultipleObjectsByHandleArray(
+ CPalThread *pThread, // IN, OPTIONAL
+ HANDLE rghHandlesToReference[],
+ DWORD dwHandleCount,
+ CAllowedObjectTypes *pAllowedTypes,
+ DWORD dwRightsRequired,
+ IPalObject *rgpObjects[] // OUT
+ ) = 0;
+
+ //
+ // This routine is for cross-process handle duplication.
+ //
+
+ virtual
+ PAL_ERROR
+ ReferenceObjectByForeignHandle(
+ CPalThread *pThread, // IN, OPTIONAL
+ HANDLE hForeignHandle,
+ IPalProcess *pForeignProcess,
+ CAllowedObjectTypes *pAllowedTypes,
+ DWORD dwRightsRequired,
+ IPalObject **ppObject // OUT
+ ) = 0;
+
+ };
+
+ extern IPalObjectManager *g_pObjectManager;
+
+ enum ThreadWakeupReason
+ {
+ WaitSucceeded,
+ Alerted,
+ 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;
+
+ virtual
+ PAL_ERROR
+ QueueUserAPC(
+ CPalThread *pThread,
+ CPalThread *pTargetThread,
+ PAPCFUNC pfnAPC,
+ ULONG_PTR dwData
+ ) = 0;
+
+ virtual
+ bool
+ AreAPCsPending(
+ CPalThread *pThread
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ DispatchPendingAPCs(
+ CPalThread *pThread
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ SendTerminationRequestToWorkerThread() = 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;
+
+ class IFileTransactionLock
+ {
+ public:
+
+ //
+ // Called when the transaction completes (which includes
+ // error completions, or the outright failure to queue
+ // the transaction).
+ //
+
+ virtual
+ void
+ ReleaseLock() = 0;
+ };
+
+ class IFileLockController
+ {
+ public:
+
+ //
+ // A transaction lock is acquired before a read or write
+ // operation, and released when that operation completes.
+ // The lock is not tied to the calling thread, since w/
+ // asynch file IO the completion may occur on a different
+ // thread.
+ //
+
+ enum FileTransactionLockType
+ {
+ ReadLock,
+ WriteLock
+ };
+
+ virtual
+ PAL_ERROR
+ GetTransactionLock(
+ CPalThread *pThread, // IN, OPTIONAL
+ FileTransactionLockType eLockType,
+ DWORD dwOffsetLow,
+ DWORD dwOffsetHigh,
+ DWORD nNumberOfBytesToLockLow,
+ DWORD nNumberOfBytesToLockHigh,
+ IFileTransactionLock **ppTransactionLock // OUT
+ ) = 0;
+
+ enum FileLockExclusivity
+ {
+ ExclusiveFileLock,
+ SharedFileLock
+ };
+
+ enum FileLockWaitMode
+ {
+ FailImmediately,
+ WaitForLockAcquisition
+ };
+
+ virtual
+ PAL_ERROR
+ CreateFileLock(
+ CPalThread *pThread, // IN, OPTIONAL
+ DWORD dwOffsetLow,
+ DWORD dwOffsetHigh,
+ DWORD nNumberOfBytesToLockLow,
+ DWORD nNumberOfBytesToLockHigh,
+ FileLockExclusivity eFileLockExclusivity,
+ FileLockWaitMode eFileLockWaitMode
+ ) = 0;
+
+ virtual
+ PAL_ERROR
+ ReleaseFileLock(
+ CPalThread *pThread, // IN, OPTIONAL
+ DWORD dwOffsetLow,
+ DWORD dwOffsetHigh,
+ DWORD nNumberOfBytesToUnlockLow,
+ DWORD nNumberOfBytesToUnlockHigh
+ ) = 0;
+
+ //
+ // ReleaseController should be called from the file object's
+ // cleanup routine. It must always be called, even if fShutdown is
+ // TRUE or fCleanupSharedState is FALSE.
+ //
+
+ virtual
+ void
+ ReleaseController() = 0;
+ };
+
+ class IFileLockManager
+ {
+ public:
+
+ //
+ // GetLockControllerForFile should be called by CreateFile.
+ // It will fail if the requested access rights and share
+ // mode are not compatible with existing lock controllers
+ // for the file.
+ //
+
+ virtual
+ PAL_ERROR
+ GetLockControllerForFile(
+ CPalThread *pThread, // IN, OPTIONAL
+ LPCSTR szFileName,
+ DWORD dwAccessRights,
+ DWORD dwShareMode,
+ IFileLockController **ppLockController // OUT
+ ) = 0;
+
+ //
+ // Gets the share mode for the file
+ // (returns SHARE_MODE_NOT_INITIALIZED if file lock controller
+ // not found)
+ //
+ virtual
+ PAL_ERROR
+ GetFileShareModeForFile(
+ LPCSTR szFileName,
+ DWORD* pdwShareMode) = 0;
+ };
+
+ extern IFileLockManager *g_pFileLockManager;
+}
+
+#endif // _CORUNIX_H
+
diff --git a/src/pal/src/include/pal/corunix.inl b/src/pal/src/include/pal/corunix.inl
new file mode 100644
index 0000000000..ab0ac70462
--- /dev/null
+++ b/src/pal/src/include/pal/corunix.inl
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+
+
+--*/
+
+#ifndef _CORUNIX_INL
+#define _CORUNIX_INL
+
+#include "corunix.hpp"
+#include "dbgmsg.h"
+
+namespace CorUnix
+{
+
+ bool CAllowedObjectTypes::IsTypeAllowed(PalObjectTypeId eTypeId)
+ {
+ _ASSERTE(eTypeId != ObjectTypeIdCount);
+ return m_rgfAllowedTypes[eTypeId];
+ };
+
+ CAllowedObjectTypes::CAllowedObjectTypes(
+ PalObjectTypeId rgAllowedTypes[],
+ DWORD dwAllowedTypeCount
+ )
+ {
+ ZeroMemory(m_rgfAllowedTypes, sizeof(m_rgfAllowedTypes));
+ for (DWORD dw = 0; dw < dwAllowedTypeCount; dw += 1)
+ {
+ _ASSERTE(rgAllowedTypes[dw] != ObjectTypeIdCount);
+ m_rgfAllowedTypes[rgAllowedTypes[dw]] = TRUE;
+ }
+ };
+
+ CAllowedObjectTypes::CAllowedObjectTypes(
+ PalObjectTypeId eAllowedType
+ )
+ {
+ ZeroMemory(m_rgfAllowedTypes, sizeof(m_rgfAllowedTypes));
+
+ _ASSERTE(eAllowedType != ObjectTypeIdCount);
+ m_rgfAllowedTypes[eAllowedType] = TRUE;
+ };
+}
+
+#endif // _CORUNIX_H
+
diff --git a/src/pal/src/include/pal/critsect.h b/src/pal/src/include/pal/critsect.h
new file mode 100644
index 0000000000..50dea95bc5
--- /dev/null
+++ b/src/pal/src/include/pal/critsect.h
@@ -0,0 +1,45 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/critsect.h
+
+Abstract:
+
+ Header file for the critical sections functions.
+
+
+
+--*/
+
+#ifndef _PAL_CRITSECT_H_
+#define _PAL_CRITSECT_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+VOID InternalInitializeCriticalSection(CRITICAL_SECTION *pcs);
+VOID InternalDeleteCriticalSection(CRITICAL_SECTION *pcs);
+
+/* The following PALCEnterCriticalSection and PALCLeaveCriticalSection
+ functions are intended to provide CorUnix's InternalEnterCriticalSection
+ and InternalLeaveCriticalSection functionalities to legacy C code,
+ which has no knowledge of CPalThread, classes and namespaces.
+*/
+VOID PALCEnterCriticalSection(CRITICAL_SECTION *pcs);
+VOID PALCLeaveCriticalSection(CRITICAL_SECTION *pcs);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_CRITSECT_H_ */
+
diff --git a/src/pal/src/include/pal/cruntime.h b/src/pal/src/include/pal/cruntime.h
new file mode 100644
index 0000000000..65bf33c952
--- /dev/null
+++ b/src/pal/src/include/pal/cruntime.h
@@ -0,0 +1,247 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/cruntime.h
+
+Abstract:
+
+ Header file for C runtime utility functions.
+
+
+
+--*/
+
+#ifndef _PAL_CRUNTIME_H_
+#define _PAL_CRUNTIME_H_
+
+#include <string.h>
+#include <stdarg.h>
+#include <pthread.h>
+
+#ifdef __cplusplus
+typedef char16_t wchar_16; // __wchar_16 (which is defined in palinternal.h) is defined as wchar_16_cpp.
+
+extern "C"
+{
+#endif // __cplusplus
+
+typedef enum
+{
+ PFF_NONE = 0,
+ PFF_MINUS = 1,
+ PFF_POUND = 2,
+ PFF_ZERO = 4,
+ PFF_SPACE = 8,
+ PFF_PLUS = 16
+}PRINTF_FORMAT_FLAGS;
+
+typedef enum
+{
+ WIDTH_DEFAULT = -1,
+ WIDTH_STAR = -2, /* e.g. "%*.10s" */
+ WIDTH_INVALID = -3 /* e.g. "%*3.10s" */
+}WIDTH_FLAGS;
+
+typedef enum
+{
+ PRECISION_DEFAULT = -1,
+ PRECISION_STAR = -2, /* e.g. "%10.*s" */
+ PRECISION_DOT = -3, /* e.g. "%10.s" */
+ PRECISION_INVALID = -4 /* e.g. "%10.*3s" */
+}PRECISION_FLAGS;
+
+typedef enum
+{
+ PFF_PREFIX_DEFAULT = -1,
+ PFF_PREFIX_SHORT = 1,
+ PFF_PREFIX_LONG = 2,
+ PFF_PREFIX_LONGLONG = 3,
+ PFF_PREFIX_LONG_W = 4
+}PRINTF_PREFIXES;
+
+typedef enum
+{
+ PFF_TYPE_DEFAULT = -1,
+ PFF_TYPE_CHAR = 1,
+ PFF_TYPE_STRING = 2,
+ PFF_TYPE_WSTRING = 3,
+ PFF_TYPE_INT = 4,
+ PFF_TYPE_P = 5,
+ PFF_TYPE_N = 6,
+ PFF_TYPE_FLOAT = 7
+}PRINTF_TYPES;
+
+typedef enum
+{
+ SCANF_PREFIX_SHORT = 1,
+ SCANF_PREFIX_LONG = 2,
+ SCANF_PREFIX_LONGLONG = 3
+}SCANF_PREFIXES;
+
+typedef enum
+{
+ SCANF_TYPE_CHAR = 1,
+ SCANF_TYPE_STRING = 2,
+ SCANF_TYPE_INT = 3,
+ SCANF_TYPE_N = 4,
+ SCANF_TYPE_FLOAT = 5,
+ SCANF_TYPE_BRACKETS = 6,
+ SCANF_TYPE_SPACE = 7
+}SCANF_TYPES;
+
+/*******************************************************************************
+Function:
+ Internal_AddPaddingA
+
+Parameters:
+ Out
+ - buffer to place padding and given string (In)
+ Count
+ - maximum chars to be copied so as not to overrun given buffer
+ In
+ - string to place into (Out) accompanied with padding
+ Padding
+ - number of padding chars to add
+ Flags
+ - padding style flags (PRINTF_FORMAT_FLAGS)
+*******************************************************************************/
+BOOL Internal_AddPaddingA(LPSTR *Out, INT Count, LPSTR In, INT Padding, INT Flags);
+
+/*******************************************************************************
+Function:
+ PAL_printf_arg_remover
+
+Parameters:
+ ap
+ - pointer to the va_list from which to remove arguments
+ Width
+ - the width of the current format option
+ Precision
+ - the precision of the current format option
+ Type
+ - the type of the argument for the current format option
+ Prefix
+ - the prefix for the current format option
+*******************************************************************************/
+void PAL_printf_arg_remover(va_list *ap, INT Width, INT Precision, INT Type, INT Prefix);
+
+/*++
+Function:
+ Silent_PAL_vsnprintf
+
+See MSDN doc.
+--*/
+INT Silent_PAL_vsnprintf(LPSTR Buffer, INT Count, LPCSTR Format, va_list ap);
+
+/*++
+Function:
+ Silent_PAL_vfprintf
+
+See MSDN doc.
+--*/
+int Silent_PAL_vfprintf(PAL_FILE *stream, const char *format, va_list ap);
+
+
+
+/*++
+Function:
+ PAL_iswlower
+
+See MSDN
+
+--*/
+int __cdecl PAL_iswlower( wchar_16 c );
+
+
+/*++
+Function:
+ PAL_iswalpha
+
+See MSDN
+
+--*/
+int __cdecl PAL_iswalpha( wchar_16 c );
+
+#if HAVE_COREFOUNDATION
+/*--
+Function:
+ PAL_iswblank
+
+Returns TRUE if c is a Win32 "blank" character.
+--*/
+int __cdecl PAL_iswblank(wchar_16 c);
+
+/*--
+Function:
+ PAL_iswcntrl
+
+Returns TRUE if c is a control character.
+--*/
+int __cdecl PAL_iswcntrl(wchar_16 c);
+
+/*--
+Function:
+ PAL_iswcntrl
+
+Returns TRUE if c is a control character.
+--*/
+int __cdecl PAL_iswpunct(wchar_16 c);
+#endif // HAVE_COREFOUNDATION
+
+/*++
+
+struct PAL_FILE.
+Used to mimic the behavior of windows.
+fwrite under windows can set the ferror flag,
+under BSD fwrite doesn't.
+--*/
+struct _FILE
+{
+ FILE * bsdFilePtr; /* The BSD file to be passed to the
+ functions needing it. */
+
+ INT PALferrorCode; /* The ferror code that fwrite sets,
+ incase of error */
+
+ BOOL bTextMode; /* Boolean variable to denote that the
+ fle is opened in text/binary mode*/
+#if UNGETC_NOT_RETURN_EOF
+ BOOL bWriteOnlyMode;/* Boolean variable to denote that the
+ fle is opened in write-only mode*/
+#endif //UNGETC_NOT_RETURN_EOF
+};
+
+enum CRT_ERROR_CODES
+{
+ PAL_FILE_NOERROR = 0,
+ PAL_FILE_ERROR
+};
+
+/* Global variables storing the std streams. Defined in cruntime/file.c. */
+extern PAL_FILE PAL_Stdout;
+extern PAL_FILE PAL_Stdin;
+extern PAL_FILE PAL_Stderr;
+
+/*++
+
+Functio:
+
+ CRTInitStdStreams.
+
+ Initilizes the standard streams.
+ Returns TRUE on success, FALSE otherwise.
+--*/
+BOOL CRTInitStdStreams( void );
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_CRUNTIME_H_ */
diff --git a/src/pal/src/include/pal/cs.hpp b/src/pal/src/include/pal/cs.hpp
new file mode 100644
index 0000000000..76e268566b
--- /dev/null
+++ b/src/pal/src/include/pal/cs.hpp
@@ -0,0 +1,54 @@
+// 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.
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// File:
+// cs.cpp
+//
+// Purpose:
+// Header file for critical sections implementation
+//
+
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _PAL_CS_HPP
+#define _PAL_CS_HPP
+
+#include "corunix.hpp"
+#include "critsect.h"
+
+namespace CorUnix
+{
+ void CriticalSectionSubSysInitialize(void);
+
+ void InternalInitializeCriticalSectionAndSpinCount(
+ PCRITICAL_SECTION pCriticalSection,
+ DWORD dwSpinCount,
+ bool fInternal);
+
+ void InternalEnterCriticalSection(
+ CPalThread *pThread,
+ CRITICAL_SECTION *pcs
+ );
+
+ void InternalLeaveCriticalSection(
+ CPalThread *pThread,
+ CRITICAL_SECTION *pcs
+ );
+
+ bool InternalTryEnterCriticalSection(
+ CPalThread * pThread,
+ PCRITICAL_SECTION pCriticalSection);
+
+#ifdef _DEBUG
+ void PALCS_ReportStatisticalData(void);
+ void PALCS_DumpCSList();
+#endif // _DEBUG
+
+}
+
+#endif // _PAL_CS_HPP
+
diff --git a/src/pal/src/include/pal/dbgmsg.h b/src/pal/src/include/pal/dbgmsg.h
new file mode 100644
index 0000000000..7a49fc0ad6
--- /dev/null
+++ b/src/pal/src/include/pal/dbgmsg.h
@@ -0,0 +1,628 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/dbgmsg.h
+
+Abstract:
+ Header file for Debug Message utilities. Output macros, type definitions,
+ extern variables. See overview section below for usage details.
+
+--*/
+
+/*
+Overview of Debug Message utilities
+
+Use debug channels to selectively output information to the console.
+
+Available macros :
+
+ - SET_DEFAULT_DEBUG_CHANNEL
+
+ This defines the channel to use with the macros TRACE, ERROR, etc
+ Use this macro once at the beginning of your source file.
+ (impl. details : this declares a constant static variable defdbgchan and
+ sets it to the apropriate channel)
+
+ usage : SET_DEFAULT_DEBUG_CHANNEL(somechannel);
+
+ - TRACE, ENTRY, WARN, ERROR, DBGOUT
+
+ Use this to output debug messages to the default debug channel (set with
+ SET_DEFAULT_DEBUG_CHANNEL). Messages will only be output if the channel is
+ active for the specified level.
+
+ usage : TRACE("printf format string", params...);
+
+ - TRACE_, ENTRY_, WARN_, ERROR_, DBGOUT_
+
+ Use this to autput debug messages to a channel other than the default.
+
+ usage : TRACE_(someotherchannel)("printf format string",params...);
+ ^ ^^ ^
+ don't forget the double set of parentheses!
+
+Available channels :
+ PAL : PAL-specific functionalities (PAL_Initialize, etc.)
+ LOADER : Loading API (LoadLibrary, etc); loader application
+ HANDLE : Handle manager (CloseHandle, etc.)
+ SHMEM : Shared Memory functions (for IPC)
+ PROCESS : Process related APIs
+ THREAD : Threading mechanism
+ EXCEPT : Structured Exception Handling functions
+ CRT : PAL implementation of the C Runtime Library functions
+ UNICODE : Unicode support API
+ ARCH : platform-dependent stuff
+ SYNC : Management of synchronization objects
+ FILE : File I/O API
+ VIRTUAL : Virtual memory and File mapping
+ MEM : Memory management (except Virtual* stuff)
+ SOCKET : WINSOCK implementation
+ DEBUG : Debugging API (ReadProcessMemory, etc.)
+ LOCALE : Locale support API
+ MISC : what doesn't fit anywhere else.
+ MUTEX : Mutex management functions
+ CRITSEC : Critical section API
+ POLL : ?
+ CRYPT : Cryptographic functions
+ SHFOLDER: Shared (well-known) folder functions
+ SXS : Side-by-side PALs (if supported)
+
+ Note : Most channels correspond to subdirectories $(PALROOT)
+ Note 2 : DON'T write TRACE("PAL") or TRACE(DCI_PAL), write TRACE(PAL)
+
+Available debug levels :
+ ENTRY : use this at the beginning of a function to print parameters.
+ TRACE : use this to output informational messages.
+ WARN : use this to report non-critical problems.
+ ERROR : use this to report critical problems.
+
+ DBGOUT: same as TRACE, but does not output line headers (thread ID, etc)
+
+Format specifiers :
+ These trace functions currently use the native fprintf() to output data.
+ All standard printf format specifiers should therefore work, while Microsoft
+ extensions will not.
+ There is one special case to consider : wide strings and wide characters.
+ Microsoft's extensions to printf include the specifiers %S and %C for
+ printing strings and characters of wchar_t. In the C99 standard,
+ the specifiers %ls and %ls serve the same purpose. However, Windows defines
+ wchar_t as a 16bit int, which is NOT guaranteed to match implementations
+ on other platforms. glibc on a x86 defines wchar_t as a 32bit int.
+ For this reason, %S and %C should be used in TRACE functions to output
+ Windows wide strings (of type wchar_t or WCHAR). To output wide-strings
+ in a platforms native format (litterals L"string" or variables of type
+ wchar_native), the specifiers %ls and %lc should be used instead.
+
+Using Debug channels at Run Time
+ To tell the PAL which debug channels should be open and which should be
+ closed, set the environment variable PAL_DBG_CHANNELS according to the
+ following syntax :
+ [+|-]<channel>.<level>[: ...]
+ + opens a channel, - closes it;
+ <channel> must be one of PAL, FILE, (etc), or the wildcard "all"
+ <level> must be TRACE, ENTRY, WARN, ERROR or "all"
+
+ Examples (for bash):
+
+ export PAL_DBG_CHANNELS="+PAL.TRACE:-FILE.ERROR"
+ export PAL_DBG_CHANNELS="+all.ENTRY"
+ export PAL_DBG_CHANNELS="-all.all"
+
+ To explicitly redirect the output of debug messages to a file (instead of
+ relying on the shell's > and |), set the environment variable
+ PAL_API_TRACING to the name of the file to write to. It can also be set to
+ "stdout" or "stderr". If PAL_API_TRACING is not set, output will go to
+ stderr.
+
+ ASSERT() messages cannot be controlled with PAL_DBG_CHANNELS; they can be
+ globally disabled (in debug builds) by setting the environment variable
+ PAL_DISABLE_ASSERTS to 1. In release builds, they will always be disabled
+
+ The environment variable "PAL_API_LEVELS" determines how many levels of
+ nesting will be allowed in ENTRY calls; if not set, the default is 1; a
+ value of 0 will allow infinite nesting, but will not indent the output
+
+ It is possible to disable/enable all channels during the execution of a
+ process; this involves using a debugger to modify a variable within the
+ address space of the running process. the variable is named
+ 'dbg_master_switch'; if set to zero, all debug chanels will be closed; if
+ set to nonzero, channels will be open or closed based on PAL_DBG_CHANNELS
+
+ Notes :
+ If _ENABLE_DEBUG_MESSAGES_ was not defined at build-time, no debug messages
+ will be generated.
+ If _ENABLE_DEBUG_MESSAGES_ was defined, all debug levels will be enabled,
+ but all channels will be closed by default
+
+ Another configure option is --enable-appendtraces
+ Normally, if the file specified by PAL_API_TRACING exists, its content will
+ be overwritten when a PAL process starts using it. If --enable-appendtraces
+ is used, debug output will be appended at the end of the file instead.
+
+
+
+ */
+
+#ifndef _PAL_DBGMSG_H_
+#define _PAL_DBGMSG_H_
+
+#include "pal/palinternal.h"
+#include "config.h"
+#include "pal/perftrace.h"
+#include "pal/debug.h"
+#include "pal/thread.hpp"
+#include "pal/tls.hpp"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/* Channel identifiers */
+typedef enum
+{
+ DCI_PAL,
+ DCI_LOADER,
+ DCI_HANDLE,
+ DCI_SHMEM,
+ DCI_PROCESS,
+ DCI_THREAD,
+ DCI_EXCEPT,
+ DCI_CRT,
+ DCI_UNICODE,
+ DCI_ARCH,
+ DCI_SYNC,
+ DCI_FILE,
+ DCI_VIRTUAL,
+ DCI_MEM,
+ DCI_SOCKET,
+ DCI_DEBUG,
+ DCI_LOCALE,
+ DCI_MISC,
+ DCI_MUTEX,
+ DCI_CRITSEC,
+ DCI_POLL,
+ DCI_CRYPT,
+ DCI_SHFOLDER,
+#ifdef FEATURE_PAL_SXS
+ DCI_SXS,
+#endif // FEATURE_PAL_SXS
+
+ DCI_LAST
+} DBG_CHANNEL_ID;
+
+/* Level identifiers */
+typedef enum
+{
+ DLI_ENTRY,
+ DLI_TRACE,
+ DLI_WARN,
+ DLI_ERROR,
+ DLI_ASSERT,
+ DLI_EXIT,
+
+ DLI_LAST
+} DBG_LEVEL_ID;
+
+
+/* extern variables */
+
+// Change W16_NULLSTRING to external variable to avoid multiple warnings showing up in prefast
+extern LPCWSTR W16_NULLSTRING;
+
+extern DWORD dbg_channel_flags[DCI_LAST];
+extern BOOL g_Dbg_asserts_enabled;
+
+/* we must use stdio functions directly rather that rely on PAL functions for
+ output, because those functions do tracing and we need to avoid recursion */
+extern FILE *output_file;
+
+/* master switch for debug channel enablement, to be modified by debugger */
+extern Volatile<BOOL> dbg_master_switch ;
+
+
+/* conditionnal compilation for other debug messages */
+#if !_ENABLE_DEBUG_MESSAGES_
+
+/* compile out these trace levels; see the definition of NOTRACE */
+#define TRACE NOTRACE
+#define TRACE_(x) NOTRACE
+#define WARN NOTRACE
+#define WARN_(x) NOTRACE
+#define ENTRY_EXTERNAL NOTRACE
+#define ENTRY NOTRACE
+#define ENTRY_(x) NOTRACE
+#define LOGEXIT NOTRACE
+#define LOGEXIT_(x) NOTRACE
+#define DBGOUT NOTRACE
+#define DBGOUT_(x) NOTRACE
+#define ERROR NOTRACE
+#define ERROR_(x) NOTRACE
+#define DBG_PRINTF(level, channel, bHeader) NOTRACE
+
+#define CHECK_STACK_ALIGN
+
+#define SET_DEFAULT_DEBUG_CHANNEL(x)
+#define DBG_ENABLED(level, channel)
+
+#else /* _ENABLE_DEBUG_MESSAGES_ */
+
+/* output macros */
+
+#define SET_DEFAULT_DEBUG_CHANNEL(x) \
+ static const DBG_CHANNEL_ID defdbgchan = DCI_##x
+
+/* Is debug output enabled for the given level and channel? */
+#define DBG_ENABLED(level, channel) (output_file && \
+ dbg_master_switch && \
+ (dbg_channel_flags[channel] & (1 << (level))))
+#define TRACE \
+ DBG_PRINTF(DLI_TRACE,defdbgchan,TRUE)
+
+#define TRACE_(x) \
+ DBG_PRINTF(DLI_TRACE,DCI_##x,TRUE)
+
+#define WARN \
+ DBG_PRINTF(DLI_WARN,defdbgchan,TRUE)
+
+#define WARN_(x) \
+ DBG_PRINTF(DLI_WARN,DCI_##x,TRUE)
+
+#if _DEBUG && defined(__APPLE__)
+bool DBG_ShouldCheckStackAlignment();
+#define CHECK_STACK_ALIGN if (DBG_ShouldCheckStackAlignment()) DBG_CheckStackAlignment()
+#else
+#define CHECK_STACK_ALIGN
+#endif
+
+#define ENTRY_EXTERNAL \
+ CHECK_STACK_ALIGN; \
+ DBG_PRINTF(DLI_ENTRY, defdbgchan,TRUE)
+
+#define ENTRY \
+ CHECK_STACK_ALIGN; \
+ DBG_PRINTF(DLI_ENTRY, defdbgchan,TRUE)
+
+#define ENTRY_(x) \
+ CHECK_STACK_ALIGN; \
+ DBG_PRINTF(DLI_ENTRY, DCI_##x,TRUE)
+
+#define LOGEXIT \
+ DBG_PRINTF(DLI_EXIT, defdbgchan,TRUE)
+
+#define LOGEXIT_(x) \
+ DBG_PRINTF(DLI_EXIT, DCI_##x,TRUE)
+
+#define DBGOUT \
+ DBG_PRINTF(DLI_TRACE,defdbgchan,FALSE)
+
+#define DBGOUT_(x) \
+ DBG_PRINTF(DLI_TRACE,DCI_##x,FALSE)
+
+/*Added this code here to stop error messages
+ *from appearing in retail build*/
+#define ERROR \
+ DBG_PRINTF(DLI_ERROR,defdbgchan,TRUE)
+
+#define ERROR_(x) \
+ DBG_PRINTF(DLI_ERROR,DCI_##x,TRUE)
+
+#define DBG_PRINTF(level, channel, bHeader) \
+{\
+ if( DBG_ENABLED(level, channel) ) { \
+ DBG_CHANNEL_ID __chanid=channel;\
+ DBG_LEVEL_ID __levid=level;\
+ BOOL __bHeader = bHeader;\
+ DBG_PRINTF2
+
+#ifdef __GNUC__
+#define DBG_PRINTF2(args...)\
+ DBG_printf_gcc(__chanid,__levid,__bHeader,__FUNCTION__,__FILE__,\
+ __LINE__,args);\
+ }\
+}
+#else /* __GNUC__ */
+#define DBG_PRINTF2(...)\
+ DBG_printf_c99(__chanid,__levid,__bHeader,__FILE__,__LINE__,__VA_ARGS__);\
+ }\
+}
+#endif /* __GNUC__ */
+
+#endif /* _ENABLE_DEBUG_MESSAGES_ */
+
+/* Use GNU C-specific features if available : __FUNCTION__ pseudo-macro,
+ variable-argument macros */
+#ifdef __GNUC__
+
+/* define NOTRACE as nothing; this will absorb the variable-argument list used
+ in tracing macros */
+#define NOTRACE(args...)
+
+#if defined(__cplusplus) && defined(FEATURE_PAL_SXS)
+#define __ASSERT_ENTER() \
+ /* DBG_printf_gcc() and DebugBreak() need a PAL thread */ \
+ PAL_EnterHolder __holder(PALIsThreadDataInitialized() && \
+ (CorUnix::InternalGetCurrentThread() == NULL || \
+ !CorUnix::InternalGetCurrentThread()->IsInPal()));
+#else /* __cplusplus && FEATURE_PAL_SXS */
+#define __ASSERT_ENTER()
+#endif /* __cplusplus && FEATURE_PAL_SXS */
+
+#if !defined(_DEBUG)
+
+#define ASSERT(args...)
+#define _ASSERT(expr)
+#define _ASSERTE(expr)
+#define _ASSERT_MSG(args...)
+
+#else /* defined(_DEBUG) */
+
+#define ASSERT(args...) \
+{ \
+ __ASSERT_ENTER(); \
+ if (output_file && dbg_master_switch) \
+ { \
+ DBG_printf_gcc(defdbgchan,DLI_ASSERT,TRUE,__FUNCTION__,__FILE__,__LINE__,args); \
+ } \
+ if (g_Dbg_asserts_enabled) \
+ { \
+ DebugBreak(); \
+ } \
+}
+
+#define _ASSERT(expr) do { if (!(expr)) { ASSERT(""); } } while(0)
+#define _ASSERTE(expr) do { if (!(expr)) { ASSERT("Expression: " #expr "\n"); } } while(0)
+#define _ASSERT_MSG(expr, args...) \
+ do { \
+ if (!(expr)) \
+ { \
+ ASSERT("Expression: " #expr ", Description: " args); \
+ } \
+ } while(0)
+
+#endif /* defined(_DEBUG) */
+
+#else /* __GNUC__ */
+/* Not GNU C : C99 [the latest version of the ISO C Standard] specifies
+ a different syntax for variable-argument macros, so try using that*/
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >=199901L
+
+/* define NOTRACE as nothing; this will absorb the variable-argument list used
+ in tracing macros */
+#define NOTRACE(...)
+
+#if !defined(_DEBUG)
+
+#define ASSERT(...)
+#define _ASSERT(expr)
+#define _ASSERTE(expr)
+#define _ASSERT_MSG(...)
+
+#else /* defined(_DEBUG) */
+
+#define ASSERT(...) \
+{ \
+ __ASSERT_ENTER(); \
+ if (output_file && dbg_master_switch) \
+ { \
+ DBG_printf_c99(defdbgchan,DLI_ASSERT,TRUE,__FILE__,__LINE__,__VA_ARGS__); \
+ } \
+ if(g_Dbg_asserts_enabled) \
+ { \
+ PAL_Leave(); \
+ DebugBreak(); \
+ } \
+}
+
+#define _ASSERT(expr) do { if (!(expr)) { ASSERT(""); } } while(0)
+#define _ASSERTE(expr) do { if (!(expr)) { ASSERT("Expression: " #expr "\n"); } } while(0)
+#define _ASSERT_MSG(expr, ...) \
+ do { \
+ if (!(expr)) \
+ { \
+ ASSERT("Expression: " #expr ", Description: " __VA_ARGS__); \
+ } \
+ } while(0)
+
+#endif /* !_DEBUG */
+
+#else /* __STDC_VERSION__ */
+/* Not GNU C, not C99 :
+ possible work around for the lack of variable-argument macros:
+ by using 2 function calls; must wrap the whole thing in a critical
+ section to avoid interleaved output from multiple threads */
+
+#error The compiler is missing support for variable-argument macros.
+
+#endif /* __STDC_VERSION__*/
+#endif /* __GNUC__ */
+
+/* Function declarations */
+
+/*++
+Function :
+ DBG_init_channels
+
+ Initialize debug channel information based on environment settings
+ Call this only once at startup.
+
+ (no parameters, no return value)
+--*/
+BOOL DBG_init_channels(void);
+
+/*++
+Function :
+ DBG_close_channels
+
+ Close the output file for debug messages.
+
+ (no parameters, no return value)
+--*/
+void DBG_close_channels(void);
+
+/*++
+Function :
+ DBG_preprintf
+
+ Internal function for debug channels; don't use.
+ This function outputs the header information for debug messages (channel,
+ level, etc).
+
+Parameters :
+ DBG_CHANNEL_ID channel : debug channel to use
+ DBG_LEVEL_ID level : debug message level
+ BOOL bHeader : whether or not to output message header (thread id, etc)
+ LPSTR file : current file
+ INT line : line number
+
+Return Value :
+ TRUE if there's an output file, FALSE otherwise. this is so that
+ DBG_printf_plain doesn't get called unnecessarily.
+
+Notes :
+ This function is only used with compilers that don't support
+ variable-argument macros. It enters a critical section, which is left in
+ DBG_printf_plain.
+--*/
+BOOL DBG_preprintf(DBG_CHANNEL_ID channel, DBG_LEVEL_ID level, BOOL bHeader,
+ LPSTR file, INT line);
+
+/*++
+Function :
+ DBG_printf_gcc
+
+ Internal function for debug channels; don't use.
+ This function outputs a complete debug message, including the function name.
+
+Parameters :
+ DBG_CHANNEL_ID channel : debug channel to use
+ DBG_LEVEL_ID level : debug message level
+ BOOL bHeader : whether or not to output message header (thread id, etc)
+ LPSTR function : current function
+ LPSTR file : current file
+ INT line : line number
+ LPSTR format, ... : standard printf parameter list.
+
+Return Value :
+ always 1.
+
+Notes :
+ This version is for gnu compilers that support variable-argument macros
+ and the __FUNCTION__ pseudo-macro.
+
+--*/
+#if __GNUC__ && CHECK_TRACE_SPECIFIERS
+/* if requested, use an __attribute__ feature to ask gcc to check that format
+ specifiers match their parameters */
+int DBG_printf_gcc(DBG_CHANNEL_ID channel, DBG_LEVEL_ID level, BOOL bHeader,
+ LPCSTR function, LPCSTR file, INT line, LPCSTR format, ...)
+ __attribute__ ((format (printf,7, 8)));
+#else
+int DBG_printf_gcc(DBG_CHANNEL_ID channel, DBG_LEVEL_ID level, BOOL bHeader,
+ LPCSTR function, LPCSTR file, INT line, LPCSTR format, ...);
+#endif
+
+/*++
+Function :
+ DBG_printf_c99
+
+ Internal function for debug channels; don't use.
+ This function outputs a complete debug message, without function name.
+
+Parameters :
+ DBG_CHANNEL_ID channel : debug channel to use
+ DBG_LEVEL_ID level : debug message level
+ BOOL bHeader : whether or not to output message header (thread id, etc)
+ LPSTR file : current file
+ INT line : line number
+ LPSTR format, ... : standard printf parameter list.
+
+Return Value :
+ always 1.
+
+Notes :
+ This version is for compilers that support the C99 flavor of
+ variable-argument macros but not the gnu flavor, and do not support the
+ __FUNCTION__ pseudo-macro.
+
+--*/
+int DBG_printf_c99(DBG_CHANNEL_ID channel, DBG_LEVEL_ID level, BOOL bHeader,
+ LPSTR file, INT line, LPSTR format, ...);
+
+/*++
+Function :
+ DBG_printf_plain
+
+ Internal function for debug channels; don't use.
+ This function output the user-specified part of a debug-message.
+
+Parameters :
+ LPSTR format, ... : standard printf parameter list.
+
+Return value :
+ always 1.
+
+Notes :
+ This function is only used with compilers that don't support
+ variable-argument macros. It will leave the critical section entered in
+ DBG_preprintf.
+
+--*/
+int DBG_printf_plain(LPSTR format, ...);
+
+/*++
+Function :
+ DBG_change_entrylevel
+
+ retrieve current ENTRY nesting level and [optionnally] modify it
+
+Parameters :
+ int new_level : value to which the nesting level must be set, or -1
+
+Return value :
+ nesting level at the time the function was called
+
+Notes:
+if new_level is -1, the nesting level will not be modified
+--*/
+int DBG_change_entrylevel(int new_level);
+
+#ifdef __APPLE__
+/*++
+Function :
+ PAL_DisplayDialog
+
+ Display a simple modal dialog with an alert icon and a single OK button. Caller supplies the title of the
+ dialog and the main text. The dialog is displayed only if the COMPlus_EnableAssertDialog environment
+ variable is set to the value "1".
+
+--*/
+void PAL_DisplayDialog(const char *szTitle, const char *szText);
+
+/*++
+Function :
+ PAL_DisplayDialogFormatted
+
+ As above but takes a printf-style format string and insertion values to form the main text.
+
+--*/
+void PAL_DisplayDialogFormatted(const char *szTitle, const char *szTextFormat, ...);
+#else // __APPLE__
+#define PAL_DisplayDialog(_szTitle, _szText)
+#define PAL_DisplayDialogFormatted(_szTitle, _szTextFormat, args...)
+#endif // __APPLE__
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_DBGMSG_H_ */
+
+
diff --git a/src/pal/src/include/pal/debug.h b/src/pal/src/include/pal/debug.h
new file mode 100644
index 0000000000..78cdeff0c3
--- /dev/null
+++ b/src/pal/src/include/pal/debug.h
@@ -0,0 +1,86 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/debug.h
+
+Abstract:
+
+ Debug API utility functions
+
+
+
+--*/
+
+#ifndef _PAL_DEBUG_H_
+#define _PAL_DEBUG_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/*++
+Function :
+ DBG_DebugBreak
+
+ Processor-dependent implementation of DebugBreak
+
+(no parameters, no return value)
+--*/
+extern "C" VOID
+DBG_DebugBreak();
+
+/*++
+Function:
+ IsInDebugBreak(addr)
+
+ Returns true if the address is in DBG_DebugBreak.
+
+--*/
+BOOL
+IsInDebugBreak(void *addr);
+
+/*++
+Function :
+ DBG_FlushInstructionCache
+
+ Processor-dependent implementation of FlushInstructionCache
+
+Parameters :
+ LPCVOID lpBaseAddress: start of region to flush
+ SIZE_T dwSize : length of region to flush
+
+Return value :
+ TRUE on success, FALSE on failure
+
+--*/
+BOOL
+DBG_FlushInstructionCache(
+ IN LPCVOID lpBaseAddress,
+ IN SIZE_T dwSize);
+
+#if defined(__APPLE__)
+/*++
+Function:
+ DBG_CheckStackAlignment
+
+ The Apple ABI requires 16-byte alignment on the stack pointer.
+ This function traps/interrupts otherwise.
+--*/
+VOID
+DBG_CheckStackAlignment();
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif //PAL_DEBUG_H_
diff --git a/src/pal/src/include/pal/dtraceprotocol.h b/src/pal/src/include/pal/dtraceprotocol.h
new file mode 100644
index 0000000000..d1a17a71ae
--- /dev/null
+++ b/src/pal/src/include/pal/dtraceprotocol.h
@@ -0,0 +1,39 @@
+// 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.
+//
+// File: rotor/pal/corunix/include/pal/dtrace_protocal.h
+//
+
+//
+// Header file for the protocals between CLR and Dtrace server
+//
+// ======================================================================================
+
+#ifndef DTRACE_PROTOCOL_H
+#define DTRACE_PROTOCOL_H
+
+// Start DTrace Consumer by Unix Domain App
+#define kServerSocketPath "/Library/Application Support/com.microsoft.clr.CFDtraceServer/Socket"
+#define kPacketTypeStartDtrace 1
+#define kPacketTypeReply 3
+#define kMaxMessageSize 318
+#define kPacketMaximumSize 102400
+
+struct PacketHeader {
+ int fType; // for request from client to server, it should be kPacketTypeStartDtrace
+ // for reply from server to client, it should be kPacketTypeReply
+ unsigned int fSize; // includes size of header itself
+};
+
+struct PacketStartDTrace { // reply: PacketReply
+ PacketHeader fHeader; // fType is kPacketTypeStartDtrace
+ char fMessage[kMaxMessageSize]; // message to print
+};
+
+struct PacketReply { // reply: n/a
+ PacketHeader fHeader; // fType is kPacketTypeReply
+ int fErr; // result of operation, errno-style
+};
+
+#endif // DTRACE_PROTOCOL
diff --git a/src/pal/src/include/pal/environ.h b/src/pal/src/include/pal/environ.h
new file mode 100644
index 0000000000..1c0bce21ca
--- /dev/null
+++ b/src/pal/src/include/pal/environ.h
@@ -0,0 +1,78 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/environ.h
+
+Abstract:
+ Header file for functions manipulating environment variables
+
+
+--*/
+
+#ifndef __ENVIRON_H_
+#define __ENVIRON_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/*++
+Variables :
+
+ palEnvironment: a global variable equivalent to environ on systems on
+ which that exists, and a pointer to an array of environment
+ strings on systems without environ.
+ gcsEnvironment: critical section to synchronize access to palEnvironment
+--*/
+extern char **palEnvironment;
+extern CRITICAL_SECTION gcsEnvironment;
+
+/*++
+
+Function:
+ EnvironInitialize
+
+Initialization function for the PAL environment code.
+--*/
+BOOL EnvironInitialize();
+
+/*++
+Function:
+ EnvironGetenv
+
+Get the value of environment variable with the given name.
+--*/
+char *EnvironGetenv(const char *name, BOOL copyValue = TRUE);
+
+/*++
+Function:
+ EnvironPutenv
+
+Add the environment variable string provided to the PAL version
+of the environment.
+--*/
+BOOL EnvironPutenv(const char *string, BOOL deleteIfEmpty);
+
+/*++
+Function:
+ EnvironUnsetenv
+
+Remove the environment variable with the given name from the PAL
+version of the environment if it exists.
+--*/
+void EnvironUnsetenv(const char *name);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* __ENVIRON_H_ */
+
diff --git a/src/pal/src/include/pal/event.hpp b/src/pal/src/include/pal/event.hpp
new file mode 100644
index 0000000000..98eeaee5db
--- /dev/null
+++ b/src/pal/src/include/pal/event.hpp
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ event.hpp
+
+Abstract:
+
+ Event object structure definition.
+
+
+
+--*/
+
+#ifndef _PAL_EVENT_H_
+#define _PAL_EVENT_H_
+
+#include "corunix.hpp"
+
+namespace CorUnix
+{
+ extern CObjectType otManualResetEvent;
+ extern CObjectType otAutoResetEvent;
+
+ PAL_ERROR
+ InternalCreateEvent(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpEventAttributes,
+ BOOL bManualReset,
+ BOOL bInitialState,
+ LPCWSTR lpName,
+ HANDLE *phEvent
+ );
+
+ PAL_ERROR
+ InternalSetEvent(
+ CPalThread *pThread,
+ HANDLE hEvent,
+ BOOL fSetEvent
+ );
+
+ PAL_ERROR
+ InternalOpenEvent(
+ CPalThread *pThread,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ LPCWSTR lpName,
+ HANDLE *phEvent
+ );
+
+}
+
+#endif //PAL_EVENT_H_
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pal/src/include/pal/fakepoll.h b/src/pal/src/include/pal/fakepoll.h
new file mode 100644
index 0000000000..eec40d6612
--- /dev/null
+++ b/src/pal/src/include/pal/fakepoll.h
@@ -0,0 +1,68 @@
+// 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.
+
+// fakepoll.h
+// poll using select
+// Warning: a call to this poll() takes about 4K of stack space.
+
+// Greg Parker gparker@cs.stanford.edu December 2000
+// This code is in the public domain and may be copied or modified without
+// permission.
+
+// Located at <http://www.sealiesoftware.com/fakepoll.h>.
+
+
+
+
+#ifndef _FAKE_POLL_H
+#define _FAKE_POLL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+#ifdef FD_SETSIZE
+#undef FD_SETSIZE
+#endif
+#define FD_SETSIZE OPEN_MAX
+
+typedef struct pollfd {
+ int fd; /* file desc to poll */
+ short events; /* events of interest on fd */
+ short revents; /* events that occurred on fd */
+} pollfd_t;
+
+// Typically defined in sys/stropts.h and used for an infinite timeout.
+#ifndef _INFTIM
+#define _INFTIM -1
+#endif
+#ifndef INFTIM
+#define INFTIM _INFTIM
+#endif
+
+// poll flags
+#define POLLIN 0x0001
+#define POLLOUT 0x0004
+#define POLLERR 0x0008
+
+// synonyms
+#define POLLNORM POLLIN
+#define POLLPRI POLLIN
+#define POLLRDNORM POLLIN
+#define POLLRDBAND POLLIN
+#define POLLWRNORM POLLOUT
+#define POLLWRBAND POLLOUT
+
+// ignored
+#define POLLHUP 0x0010
+#define POLLNVAL 0x0020
+
+int poll(struct pollfd *pollSet, int pollCount, int pollTimeout);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _FAKE_POLL_H */
diff --git a/src/pal/src/include/pal/file.h b/src/pal/src/include/pal/file.h
new file mode 100644
index 0000000000..93c8ad9784
--- /dev/null
+++ b/src/pal/src/include/pal/file.h
@@ -0,0 +1,304 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/file.h
+
+Abstract:
+ Header file for file utility functions.
+
+Revision History:
+
+
+
+--*/
+
+#ifndef _PAL_FILE_H_
+#define _PAL_FILE_H_
+
+#include "pal/shmemory.h"
+#include "pal/stackstring.hpp"
+#include <sys/types.h>
+#include <dirent.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+typedef struct _find_handle
+{
+ struct _find_handle *self_addr; /* for pointer verification */
+
+ char dir[_MAX_DIR];
+ char fname[MAX_PATH_FNAME]; /* includes extension */
+ glob_t gGlob;
+ char **next;
+} find_obj;
+
+/*++
+FILECanonicalizePath
+ Removes all instances of '/./', '/../' and '//' from an absolute path.
+
+Parameters:
+ LPSTR lpUnixPath : absolute path to modify, in Unix format
+
+(no return value)
+
+Notes :
+-behavior is undefined if path is not absolute
+-the order of steps *is* important: /one/./../two would give /one/two
+ instead of /two if step 3 was done before step 2
+-reason for this function is that GetFullPathName can't use realpath(), since
+ realpath() requires the given path to be valid and GetFullPathName does not.
+--*/
+void FILECanonicalizePath(LPSTR lpUnixPath);
+
+/*++
+Function:
+ FileDosToUnixPathA
+
+Abstract:
+ Change a DOS path to a Unix path. Replace '\' by '/'.
+
+Parameter:
+ IN/OUT lpPath: path to be modified
+--*/
+void
+FILEDosToUnixPathA(LPSTR lpPath);
+
+/*++
+Function:
+ FileDosToUnixPathW
+
+Abstract:
+ Change a DOS path to a Unix path. Replace '\' by '/'.
+
+Parameter:
+ IN/OUT lpPath: path to be modified
+ --*/
+void
+FILEDosToUnixPathW(LPWSTR lpPath);
+
+/*++
+Function:
+ FileUnixToDosPathA
+
+Abstract:
+ Change a Unix path to a DOS path. Replace '/' by '\'.
+
+Parameter:
+ IN/OUT lpPath: path to be modified
+--*/
+void
+FILEUnixToDosPathA(LPSTR lpPath);
+
+/*++
+Function:
+ FILEGetDirectoryFromFullPathA
+
+Parse the given path. If it contains a directory part and a file part,
+put the directory part into the supplied buffer, and return the number of
+characters written to the buffer. If the buffer is not large enough,
+return the required size of the buffer including the NULL character. If
+there is no directory part in the path, return 0.
+--*/
+DWORD FILEGetDirectoryFromFullPathA( LPCSTR lpFullPath,
+ DWORD nBufferLength,
+ LPSTR lpBuffer );
+
+/*++
+Function:
+ FILEGetFileNameFromFullPath
+
+Given a full path, return a pointer to the first char of the filename part.
+--*/
+LPCSTR FILEGetFileNameFromFullPathA( LPCSTR lpFullPath );
+
+/*++
+Function:
+ FILEGetLastErrorFromErrno
+
+Convert errno into the appropriate win32 error and return it.
+--*/
+DWORD FILEGetLastErrorFromErrno( void );
+
+/*++
+Function:
+ DIRGetLastErrorFromErrno
+
+Convert errno into the appropriate win32 error and return it.
+--*/
+DWORD DIRGetLastErrorFromErrno( void );
+
+/*++
+FILEInitStdHandles
+
+Create handle objects for stdin, stdout and stderr
+
+(no parameters)
+
+Return value:
+ TRUE on success, FALSE on failure
+--*/
+BOOL FILEInitStdHandles(void);
+
+/*++
+FILECleanupStdHandles
+
+Close promary handles for stdin, stdout and stderr
+
+(no parameters, no return value)
+--*/
+void FILECleanupStdHandles(void);
+
+/*++
+
+Function :
+ FILEGetProperNotFoundError
+
+Returns the proper error code, based on the
+Windows behavoir.
+
+ IN LPSTR lpPath - The path to check.
+ LPDWORD lpErrorCode - The error to set.
+*/
+void FILEGetProperNotFoundError( LPCSTR lpPath, LPDWORD lpErrorCode );
+
+/*++
+PAL__getcwd
+
+Calls getcwd
+
+Input parameters:
+
+char *szBuf = a copy of the absolute pathname of the current working directory
+is copied into szBuf.
+size_t nSize = size, in bytes, of the array referenced by szBuf.
+
+Return value:
+ A pointer to the pathname if successful, otherwise NULL is returned.
+--*/
+char * __cdecl PAL__getcwd(char *szBuf, size_t nSize);
+
+/*++
+PAL_fflush
+
+Calls fflush
+
+Input parameters:
+
+PAL_FILE *stream = stream to be flushed.
+
+Return value:
+ 0 is returned on success, otherwise EOF is returned.
+--*/
+int _cdecl PAL_fflush( PAL_FILE *stream );
+
+/*++
+PAL_mkstemp
+
+Calls InternalMkstemp to call mkstemp
+
+Input parameters:
+
+char *szNameTemplate = the pattern to follow when creating a new file.
+
+Return value:
+ file descriptor of opened file on success, -1 on failure.
+--*/
+int __cdecl PAL_mkstemp(char *szNameTemplate);
+
+/*++
+PAL_rename
+
+Calls rename
+
+Input parameters:
+
+szOldName = pointer to the pathname of the file to be renamed
+szNewName = pointer to the new pathname of the file
+
+Return value:
+ Returns 0 on success and -1 on failure
+--*/
+int __cdecl PAL_rename(const char *szOldName, const char *szNewName);
+
+/*++
+PAL_fgets
+
+Wrapper function for InternalFgets.
+
+Input parameters:
+
+sz = stores characters read from the given file stream
+nSize = number of characters to be read
+pf = stream to read characters from
+
+Return value:
+ Returns a pointer to the string storing the characters on success
+ and NULL on failure.
+--*/
+char * __cdecl PAL_fgets(char *sz, int nSize, PAL_FILE *pf);
+
+/*++
+PAL_fwrite
+
+Wrapper function for InternalFwrite
+
+Input parameters:
+
+pvBuffer = array of objects to write to the given file stream
+nSize = size of a object in bytes
+nCount = number of objects to write
+pf = stream to write characters to
+
+Return value:
+ Returns the number of objects written.
+--*/
+size_t __cdecl PAL_fwrite(const void *pvBuffer, size_t nSize, size_t nCount, PAL_FILE *pf);
+
+/*++
+PAL__open
+
+Wrapper function for InternalOpen.
+
+Input parameters:
+
+szPath = pointer to a pathname of a file to be opened
+nFlags = arguments that control how the file should be accessed
+mode = file permission settings that are used only when a file is created
+
+Return value:
+ File descriptor on success, -1 on failure
+--*/
+int __cdecl PAL__open(const char *szPath, int nFlags, ...);
+
+/*++
+PAL_fseek
+
+Wrapper function for fseek
+
+Input parameters:
+
+pf = a given file stream
+lOffset = distance from position to set file-position indicator
+nWhence = method used to determine the file_position indicator location relative to lOffset
+
+Return value:
+ 0 on success, -1 on failure.
+--*/
+int _cdecl PAL_fseek(PAL_FILE *pf, LONG lOffset, int nWhence);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_FILE_H_ */
+
diff --git a/src/pal/src/include/pal/file.hpp b/src/pal/src/include/pal/file.hpp
new file mode 100644
index 0000000000..5acccb0a24
--- /dev/null
+++ b/src/pal/src/include/pal/file.hpp
@@ -0,0 +1,365 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/file.hpp
+
+Abstract:
+ Header file for file utility functions.
+
+Revision History:
+
+
+
+--*/
+
+#ifndef _PAL_FILE_HPP_
+#define _PAL_FILE_HPP_
+
+#include "corunix.hpp"
+#include "pal/stackstring.hpp"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <dirent.h>
+
+
+namespace CorUnix
+{
+ extern CObjectType otFile;
+ extern CAllowedObjectTypes aotFile;
+
+ class CFileProcessLocalData
+ {
+ public:
+ IFileLockController *pLockController;
+
+ int unix_fd;
+ DWORD dwDesiredAccess; /* Unix assumes files are always opened for reading.
+ In Windows we can open a file for writing only */
+ int open_flags; /* stores Unix file creation flags */
+ BOOL open_flags_deviceaccessonly;
+ char unix_filename[MAXPATHLEN];
+ BOOL inheritable;
+ };
+
+ PAL_ERROR
+ InternalCreateFile(
+ CPalThread *pThread,
+ LPCSTR lpFileName,
+ DWORD dwDesiredAccess,
+ DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes,
+ HANDLE hTemplateFile,
+ HANDLE *pFileHandle
+ );
+
+ PAL_ERROR
+ InternalWriteFile(
+ CPalThread *pThread,
+ HANDLE hFile,
+ LPCVOID lpBuffer,
+ DWORD nNumberOfBytesToWrite,
+ LPDWORD lpNumberOfBytesWritten,
+ LPOVERLAPPED lpOverlapped
+ );
+
+ PAL_ERROR
+ InternalReadFile(
+ CPalThread *pThread,
+ HANDLE hFile,
+ LPVOID lpBuffer,
+ DWORD nNumberOfBytesToRead,
+ LPDWORD lpNumberOfBytesRead,
+ LPOVERLAPPED lpOverlapped
+ );
+
+ PAL_ERROR
+ InternalSetEndOfFile(
+ CPalThread *pThread,
+ HANDLE hFile
+ );
+
+ PAL_ERROR
+ InternalGetFileSize(
+ CPalThread *pThread,
+ HANDLE hFile,
+ DWORD *pdwFileSizeLow,
+ DWORD *pdwFileSizeHigh
+ );
+
+ PAL_ERROR
+ InternalFlushFileBuffers(
+ CPalThread *pThread,
+ HANDLE hFile
+ );
+
+ PAL_ERROR
+ InternalGetFileType(
+ CPalThread *pThread,
+ HANDLE hFile,
+ DWORD *pdwFileType
+ );
+
+ PAL_ERROR
+ InternalCreatePipe(
+ CPalThread *pThread,
+ HANDLE *phReadPipe,
+ HANDLE *phWritePipe,
+ LPSECURITY_ATTRIBUTES lpPipeAttributes,
+ DWORD nSize
+ );
+
+ PAL_ERROR
+ InternalLockFile(
+ CPalThread *pThread,
+ HANDLE hFile,
+ DWORD dwFileOffsetLow,
+ DWORD dwFileOffsetHigh,
+ DWORD nNumberOfBytesToLockLow,
+ DWORD nNumberOfBytesToLockHigh
+ );
+
+ PAL_ERROR
+ InternalUnlockFile(
+ CPalThread *pThread,
+ HANDLE hFile,
+ DWORD dwFileOffsetLow,
+ DWORD dwFileOffsetHigh,
+ DWORD nNumberOfBytesToUnlockLow,
+ DWORD nNumberOfBytesToUnlockHigh
+ );
+
+ PAL_ERROR
+ InternalSetFilePointer(
+ CPalThread *pThread,
+ HANDLE hFile,
+ LONG lDistanceToMove,
+ PLONG lpDistanceToMoveHigh,
+ DWORD dwMoveMethod,
+ PLONG lpNewFilePointerLow
+ );
+
+ PAL_ERROR
+ InternalSetFileTime(
+ CPalThread *pThread,
+ IN HANDLE hFile,
+ IN CONST FILETIME *lpCreationTime,
+ IN CONST FILETIME *lpLastAccessTime,
+ IN CONST FILETIME *lpLastWriteTime
+ );
+
+ PAL_ERROR
+ InternalGetFileTime(
+ CPalThread *pThread,
+ IN HANDLE hFile,
+ OUT LPFILETIME lpCreationTime,
+ OUT LPFILETIME lpLastAccessTime,
+ OUT LPFILETIME lpLastWriteTime
+ );
+
+ BOOL
+ RealPathHelper(LPCSTR lpUnixPath, PathCharString& lpBuffer);
+ /*++
+ InternalCanonicalizeRealPath
+ Wraps realpath() to hide platform differences. See the man page for
+ realpath(3) for details of how realpath() works.
+
+ On systems on which realpath() allows the last path component to not
+ exist, this is a straight thunk through to realpath(). On other
+ systems, we remove the last path component, then call realpath().
+ --*/
+ PAL_ERROR
+ InternalCanonicalizeRealPath(
+ LPCSTR lpUnixPath,
+ PathCharString& lpBuffer
+ );
+
+ /*++
+ InternalMkstemp
+ Wraps mkstemp
+ --*/
+ int
+ InternalMkstemp(
+ char *szNameTemplate
+ );
+
+ /*++
+ InternalFgets
+ Wraps fgets
+ --*/
+ char *
+ InternalFgets(
+ char *sz,
+ int nSize,
+ FILE *f,
+ bool fTextMode
+ );
+
+ /*++
+ InternalFwrite
+ Wraps fwrite
+ --*/
+ size_t
+ InternalFwrite(
+ const void *pvBuffer,
+ size_t nSize,
+ size_t nCount,
+ FILE *f,
+ INT *pnErrorCode
+ );
+
+ /*++
+ InternalOpen
+ Wraps open
+ --*/
+ int
+ InternalOpen(
+ const char *szFilename,
+ int nFlags,
+ ...
+ );
+}
+
+extern "C"
+{
+
+//
+// These routines should all be separated out into something along the lines
+// of fileutils.* (instead of being commingled with the core file object
+// code).
+//
+
+/*++
+FILECanonicalizePath
+ Removes all instances of '/./', '/../' and '//' from an absolute path.
+
+Parameters:
+ LPSTR lpUnixPath : absolute path to modify, in Unix format
+
+(no return value)
+
+Notes :
+-behavior is undefined if path is not absolute
+-the order of steps *is* important: /one/./../two would give /one/two
+ instead of /two if step 3 was done before step 2
+-reason for this function is that GetFullPathName can't use realpath(), since
+ realpath() requires the given path to be valid and GetFullPathName does not.
+--*/
+void FILECanonicalizePath(LPSTR lpUnixPath);
+
+/*++
+Function:
+ FileDosToUnixPathA
+
+Abstract:
+ Change a DOS path to a Unix path. Replace '\' by '/'.
+
+Parameter:
+ IN/OUT lpPath: path to be modified
+--*/
+void
+FILEDosToUnixPathA(LPSTR lpPath);
+
+/*++
+Function:
+ FileDosToUnixPathW
+
+Abstract:
+ Change a DOS path to a Unix path. Replace '\' by '/'.
+
+Parameter:
+ IN/OUT lpPath: path to be modified
+ --*/
+void
+FILEDosToUnixPathW(LPWSTR lpPath);
+
+/*++
+Function:
+ FileUnixToDosPathA
+
+Abstract:
+ Change a Unix path to a DOS path. Replace '/' by '\'.
+
+Parameter:
+ IN/OUT lpPath: path to be modified
+--*/
+void
+FILEUnixToDosPathA(LPSTR lpPath);
+
+
+/*++
+Function:
+ FILEGetDirectoryFromFullPathA
+
+Parse the given path. If it contains a directory part and a file part,
+put the directory part into the supplied buffer, and return the number of
+characters written to the buffer. If the buffer is not large enough,
+return the required size of the buffer including the NULL character. If
+there is no directory part in the path, return 0.
+--*/
+DWORD FILEGetDirectoryFromFullPathA( LPCSTR lpFullPath,
+ DWORD nBufferLength,
+ LPSTR lpBuffer );
+
+/*++
+Function:
+ FILEGetFileNameFromFullPath
+
+Given a full path, return a pointer to the first char of the filename part.
+--*/
+LPCSTR FILEGetFileNameFromFullPathA( LPCSTR lpFullPath );
+
+/*++
+Function:
+ FILEGetLastErrorFromErrno
+
+Convert errno into the appropriate win32 error and return it.
+--*/
+DWORD FILEGetLastErrorFromErrno( void );
+
+/*++
+FILEInitStdHandles
+
+Create handle objects for stdin, stdout and stderr
+
+(no parameters)
+
+Return value:
+ TRUE on success, FALSE on failure
+--*/
+BOOL FILEInitStdHandles(void);
+
+/*++
+FILECleanupStdHandles
+
+Close primary handles for stdin, stdout and stderr
+
+(no parameters, no return value)
+--*/
+void FILECleanupStdHandles(void);
+
+/*++
+
+Function :
+ FILEGetProperNotFoundError
+
+Returns the proper error code, based on the
+Windows behavoir.
+
+ IN LPSTR lpPath - The path to check.
+ LPDWORD lpErrorCode - The error to set.
+*/
+void FILEGetProperNotFoundError( LPCSTR lpPath, LPDWORD lpErrorCode );
+
+}
+
+#endif /* _PAL_FILE_HPP_ */
+
diff --git a/src/pal/src/include/pal/filetime.h b/src/pal/src/include/pal/filetime.h
new file mode 100644
index 0000000000..cb37b4115a
--- /dev/null
+++ b/src/pal/src/include/pal/filetime.h
@@ -0,0 +1,80 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/filetime.h
+
+Abstract:
+
+ Header file for utility functions having to do with file times.
+
+Revision History:
+
+
+
+--*/
+
+#ifndef _PAL_FILETIME_H_
+#define _PAL_FILETIME_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/* Provide consistent access to nanosecond fields, if they exist. */
+
+#if HAVE_STAT_TIMESPEC
+
+#define ST_ATIME_NSEC(statstruct) ((statstruct)->st_atimespec.tv_nsec)
+#define ST_MTIME_NSEC(statstruct) ((statstruct)->st_mtimespec.tv_nsec)
+#define ST_CTIME_NSEC(statstruct) ((statstruct)->st_ctimespec.tv_nsec)
+
+#else /* HAVE_STAT_TIMESPEC */
+
+#if HAVE_STAT_NSEC
+
+#define ST_ATIME_NSEC(statstruct) ((statstruct)->st_atimensec)
+#define ST_MTIME_NSEC(statstruct) ((statstruct)->st_mtimensec)
+#define ST_CTIME_NSEC(statstruct) ((statstruct)->st_ctimensec)
+
+#else /* HAVE_STAT_NSEC */
+
+#define ST_ATIME_NSEC(statstruct) 0
+#define ST_MTIME_NSEC(statstruct) 0
+#define ST_CTIME_NSEC(statstruct) 0
+
+#endif /* HAVE_STAT_NSEC */
+#endif /* HAVE_STAT_TIMESPEC */
+
+FILETIME FILEUnixTimeToFileTime( time_t sec, long nsec );
+time_t FILEFileTimeToUnixTime( FILETIME FileTime, long *nsec );
+
+#ifdef __APPLE__
+#include <CoreFoundation/CFDate.h>
+
+FILETIME FILECFAbsoluteTimeToFileTime( CFAbsoluteTime sec );
+#endif // __APPLE__
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_FILE_H_ */
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pal/src/include/pal/handleapi.hpp b/src/pal/src/include/pal/handleapi.hpp
new file mode 100644
index 0000000000..7974432a65
--- /dev/null
+++ b/src/pal/src/include/pal/handleapi.hpp
@@ -0,0 +1,48 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ handleapi.hpp
+
+Abstract:
+
+ Declaration of the handle management APIs
+
+
+
+--*/
+
+#ifndef _HANDLEAPI_HPP
+#define _HANDLEAPI_HPP
+
+#include "corunix.hpp"
+
+namespace CorUnix
+{
+ PAL_ERROR
+ InternalDuplicateHandle(
+ CPalThread *pThread,
+ HANDLE hSourceProcess,
+ HANDLE hSource,
+ HANDLE hTargetProcess,
+ LPHANDLE phDuplicate,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ DWORD dwOptions
+ );
+
+ PAL_ERROR
+ InternalCloseHandle(
+ CPalThread *pThread,
+ HANDLE hObject
+ );
+}
+
+#endif // _HANDLEAPI_HPP
+
diff --git a/src/pal/src/include/pal/handlemgr.hpp b/src/pal/src/include/pal/handlemgr.hpp
new file mode 100644
index 0000000000..1fbdb87199
--- /dev/null
+++ b/src/pal/src/include/pal/handlemgr.hpp
@@ -0,0 +1,180 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ handlemgr.hpp
+
+Abstract:
+
+ Simple handle table manager class
+
+
+
+--*/
+
+#ifndef _PAL_HANDLEMGR_H_
+#define _PAL_HANDLEMGR_H_
+
+
+#include "corunix.hpp"
+#include "cs.hpp"
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+
+
+/* Pseudo handles constant for current thread and process */
+extern const HANDLE hPseudoCurrentProcess;
+extern const HANDLE hPseudoCurrentThread;
+extern const HANDLE hPseudoGlobalIOCP;
+
+namespace CorUnix
+{
+ class CSimpleHandleManager
+ {
+ private:
+ enum { c_BasicGrowthRate = 1024 };
+ enum { c_MaxIndex = 0x3FFFFFFE };
+
+ typedef UINT_PTR HANDLE_INDEX;
+ static const HANDLE_INDEX c_hiInvalid = (HANDLE_INDEX) -1;
+
+ HANDLE
+ HandleIndexToHandle(HANDLE_INDEX hi)
+ {
+ return (HANDLE) ((hi + 1) << 2);
+ };
+
+ HANDLE_INDEX
+ HandleToHandleIndex(HANDLE h)
+ {
+ return (HANDLE_INDEX) (((UINT_PTR) h) >> 2) - 1;
+ };
+
+ typedef struct _HANDLE_TABLE_ENTRY
+ {
+ union
+ {
+ IPalObject *pObject;
+ HANDLE_INDEX hiNextIndex;
+ } u;
+
+ DWORD dwAccessRights;
+ bool fInheritable;
+
+ bool fEntryAllocated;
+ } HANDLE_TABLE_ENTRY;
+
+ HANDLE_INDEX m_hiFreeListStart;
+ HANDLE_INDEX m_hiFreeListEnd;
+
+ DWORD m_dwTableSize;
+ DWORD m_dwTableGrowthRate;
+ HANDLE_TABLE_ENTRY* m_rghteHandleTable;
+
+ CRITICAL_SECTION m_csLock;
+ bool m_fLockInitialized;
+
+ bool ValidateHandle(HANDLE h);
+
+ public:
+
+ CSimpleHandleManager()
+ :
+ m_hiFreeListStart(c_hiInvalid),
+ m_hiFreeListEnd(c_hiInvalid),
+ m_dwTableSize(0),
+ m_dwTableGrowthRate(c_BasicGrowthRate),
+ m_rghteHandleTable(NULL),
+ m_fLockInitialized(FALSE)
+ {
+ };
+
+ virtual
+ ~CSimpleHandleManager()
+ {
+ if (m_fLockInitialized)
+ {
+ DeleteCriticalSection(&m_csLock);
+ }
+
+ if (NULL != m_rghteHandleTable)
+ {
+ free(m_rghteHandleTable);
+ }
+ }
+
+ PAL_ERROR
+ Initialize(
+ void
+ );
+
+ PAL_ERROR
+ AllocateHandle(
+ CPalThread *pThread,
+ IPalObject *pObject,
+ DWORD dwAccessRights,
+ bool fInheritable,
+ HANDLE *ph
+ );
+
+ //
+ // On success this will add a reference to the returned object.
+ //
+
+ PAL_ERROR
+ GetObjectFromHandle(
+ CPalThread *pThread,
+ HANDLE h,
+ DWORD *pdwRightsGranted,
+ IPalObject **ppObject
+ );
+
+ PAL_ERROR
+ FreeHandle(
+ CPalThread *pThread,
+ HANDLE h
+ );
+
+ void
+ Lock(
+ CPalThread *pThread
+ )
+ {
+ InternalEnterCriticalSection(pThread, &m_csLock);
+ };
+
+ void
+ Unlock(
+ CPalThread *pThread
+ )
+ {
+ InternalLeaveCriticalSection(pThread, &m_csLock);
+ };
+ };
+
+ bool
+ HandleIsSpecial(
+ HANDLE h
+ );
+}
+
+#endif // _PAL_HANDLEMGR_H_
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pal/src/include/pal/identity.hpp b/src/pal/src/include/pal/identity.hpp
new file mode 100644
index 0000000000..bd64a659ac
--- /dev/null
+++ b/src/pal/src/include/pal/identity.hpp
@@ -0,0 +1,57 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/identity.hpp
+
+Abstract:
+
+ Header file for identity functions.
+
+
+
+--*/
+
+#ifndef _PAL_IDENTITY_HPP_
+#define _PAL_IDENTITY_HPP_
+
+#include "config.h"
+#include "pal/palinternal.h"
+
+/*++
+
+Function:
+ IdentityInitialize
+
+--*/
+BOOL IdentityInitialize();
+
+/*++
+Function:
+ IdentityCleanup
+
+--*/
+VOID IdentityCleanup();
+
+#if HAVE_GETPWUID_R
+namespace CorUnix
+{
+ int
+ InternalGetpwuid_r(
+ CPalThread *pPalThread,
+ uid_t uid,
+ struct passwd *pPasswd,
+ char *pchBuffer,
+ size_t nBufSize,
+ struct passwd **ppResult
+ );
+}
+#endif /* HAVE_GETPWUID_R */
+
+#endif /* _PAL_IDENTITY_HPP_ */
diff --git a/src/pal/src/include/pal/init.h b/src/pal/src/include/pal/init.h
new file mode 100644
index 0000000000..d478ed275b
--- /dev/null
+++ b/src/pal/src/include/pal/init.h
@@ -0,0 +1,111 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/init.h
+
+Abstract:
+ Header file for PAL init utility functions. Those functions
+ are only use by the PAL itself.
+
+Revision History:
+
+
+
+--*/
+
+#ifndef _PAL_INIT_H_
+#define _PAL_INIT_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/*++
+Function:
+ PALCommonCleanup
+
+Utility function to prepare for shutdown.
+
+--*/
+void PALCommonCleanup();
+
+extern Volatile<INT> init_count;
+
+/*++
+MACRO:
+ PALIsInitialized
+
+Returns TRUE if the PAL is in an initialized state
+(#calls to PAL_Initialize > #calls to PAL_Terminate)
+
+Warning : this will only report the PAL's state at the moment it is called.
+If it is necessary to ensure the PAL remains initialized (or not) while doing
+some work, the Initialization lock (PALInitLock()) should be held.
+--*/
+#define PALIsInitialized() (0 < init_count)
+
+/*++
+Function:
+ PALIsThreadDataInitialized
+
+Returns TRUE if startup has reached a point where thread data is available
+--*/
+BOOL
+PALIsThreadDataInitialized();
+
+/*++
+Function:
+ PALIsShuttingDown
+
+Returns TRUE if the some thread has declared intent to shutdown
+--*/
+BOOL
+PALIsShuttingDown();
+
+/*++
+Function:
+ PALSetShutdownIntent
+
+Delcares intent to shutdown
+--*/
+void
+PALSetShutdownIntent();
+
+/*++
+Function:
+ PALInitLock
+
+Take the initializaiton critical section (init_critsec). necessary to serialize
+TerminateProcess along with PAL_Terminate and PAL_Initialize
+
+(no parameters)
+
+Return value :
+ TRUE if critical section existed (and was acquired)
+ FALSE if critical section doens't exist yet
+--*/
+BOOL PALInitLock(void);
+
+/*++
+Function:
+ PALInitUnlock
+
+Release the initialization critical section (init_critsec).
+
+(no parameters, no return value)
+--*/
+void PALInitUnlock(void);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_INIT_H_ */
diff --git a/src/pal/src/include/pal/list.h b/src/pal/src/include/pal/list.h
new file mode 100644
index 0000000000..cd78c0f03a
--- /dev/null
+++ b/src/pal/src/include/pal/list.h
@@ -0,0 +1,141 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ list.h
+
+Abstract:
+
+ Doubly-linked list manipulation macros (from ntrtl.h)
+
+Revision History:
+
+
+
+--*/
+
+#ifndef _LIST_H_INCLUDED
+#define _LIST_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+typedef struct _LIST_ENTRY {
+ struct _LIST_ENTRY *Flink;
+ struct _LIST_ENTRY *Blink;
+} LIST_ENTRY, *PLIST_ENTRY;
+
+//
+// VOID
+// InitializeListHead(
+// PLIST_ENTRY ListHead
+// );
+//
+
+#define InitializeListHead(ListHead) (\
+ (ListHead)->Flink = (ListHead)->Blink = (ListHead))
+
+//
+// BOOLEAN
+// IsListEmpty(
+// PLIST_ENTRY ListHead
+// );
+//
+
+#define IsListEmpty(ListHead) \
+ ((ListHead)->Flink == (ListHead))
+
+//
+// PLIST_ENTRY
+// RemoveHeadList(
+// PLIST_ENTRY ListHead
+// );
+//
+
+#define RemoveHeadList(ListHead) \
+ (ListHead)->Flink;\
+ {RemoveEntryList((ListHead)->Flink)}
+
+//
+// PLIST_ENTRY
+// RemoveTailList(
+// PLIST_ENTRY ListHead
+// );
+//
+
+#define RemoveTailList(ListHead) \
+ (ListHead)->Blink;\
+ {RemoveEntryList((ListHead)->Blink)}
+
+//
+// VOID
+// RemoveEntryList(
+// PLIST_ENTRY Entry
+// );
+//
+
+#define RemoveEntryList(Entry) {\
+ PLIST_ENTRY _EX_Blink;\
+ PLIST_ENTRY _EX_Flink;\
+ _EX_Flink = (Entry)->Flink;\
+ _EX_Blink = (Entry)->Blink;\
+ _EX_Blink->Flink = _EX_Flink;\
+ _EX_Flink->Blink = _EX_Blink;\
+ }
+
+//
+// VOID
+// InsertTailList(
+// PLIST_ENTRY ListHead,
+// PLIST_ENTRY Entry
+// );
+//
+
+#define InsertTailList(ListHead,Entry) {\
+ PLIST_ENTRY _EX_Blink;\
+ PLIST_ENTRY _EX_ListHead;\
+ _EX_ListHead = (ListHead);\
+ _EX_Blink = _EX_ListHead->Blink;\
+ (Entry)->Flink = _EX_ListHead;\
+ (Entry)->Blink = _EX_Blink;\
+ _EX_Blink->Flink = (Entry);\
+ _EX_ListHead->Blink = (Entry);\
+ }
+
+//
+// VOID
+// InsertHeadList(
+// PLIST_ENTRY ListHead,
+// PLIST_ENTRY Entry
+// );
+//
+
+#define InsertHeadList(ListHead,Entry) {\
+ PLIST_ENTRY _EX_Flink;\
+ PLIST_ENTRY _EX_ListHead;\
+ _EX_ListHead = (ListHead);\
+ _EX_Flink = _EX_ListHead->Flink;\
+ (Entry)->Flink = _EX_Flink;\
+ (Entry)->Blink = _EX_ListHead;\
+ _EX_Flink->Blink = (Entry);\
+ _EX_ListHead->Flink = (Entry);\
+ }
+
+#define CONTAINING_RECORD(address, type, field) ((type *)( \
+ (PCHAR)(address) - \
+ (ULONG_PTR)(&((type *)0)->field)))
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // _LIST_H_INCLUDED
+
diff --git a/src/pal/src/include/pal/locale.h b/src/pal/src/include/pal/locale.h
new file mode 100644
index 0000000000..f59ce2e174
--- /dev/null
+++ b/src/pal/src/include/pal/locale.h
@@ -0,0 +1,75 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ locale.h
+
+Abstract:
+
+ Prototypes for codepage initialization, and control of the readwrite locks
+ for systems that use them.
+
+Revision History:
+
+
+
+--*/
+
+#ifndef _PAL_LOCALE_H_
+#define _PAL_LOCALE_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+#if HAVE_LOWERCASE_ISO_NAME
+#define ISO_NAME(region, encoding, part) region ".iso" encoding part
+#elif HAVE_UNDERSCORE_ISO_NAME
+#define ISO_NAME(region, encoding, part) region ".ISO_" encoding "-" part
+#else
+#define ISO_NAME(region, encoding, part) region ".ISO" encoding "-" part
+#endif
+
+#if HAVE_COREFOUNDATION
+#define CF_EXCLUDE_CSTD_HEADERS
+#include <CoreFoundation/CoreFoundation.h>
+#endif // HAVE_COREFOUNDATION
+
+#if HAVE_COREFOUNDATION
+#if !ENABLE_DOWNLEVEL_FOR_NLS
+BOOL LocaleInitialize( void );
+void LocaleCleanup( void );
+#endif // !ENABLE_DOWNLEVEL_FOR_NLS
+
+typedef
+struct _CP_MAPPING
+{
+ UINT nCodePage; /* Code page identifier. */
+ CFStringEncoding nCFEncoding; /* The equivalent CFString encoding. */
+ UINT nMaxByteSize; /* The max byte size of any character. */
+ BYTE LeadByte[ MAX_LEADBYTES ]; /* The lead byte array. */
+} CP_MAPPING;
+#elif HAVE_PTHREAD_RWLOCK_T
+typedef
+struct _CP_MAPPING
+{
+ UINT nCodePage; // Code page identifier.
+ LPCSTR lpBSDEquivalent; // The equivalent BSD locale identifier.
+ UINT nMaxByteSize; // The max byte size of any character.
+ BYTE LeadByte[ MAX_LEADBYTES ]; // The lead byte array.
+} CP_MAPPING;
+#else
+#error Insufficient platform support for text encodings
+#endif
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_LOCALE_H_ */
diff --git a/src/pal/src/include/pal/malloc.hpp b/src/pal/src/include/pal/malloc.hpp
new file mode 100644
index 0000000000..c7333419a7
--- /dev/null
+++ b/src/pal/src/include/pal/malloc.hpp
@@ -0,0 +1,152 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ pal/malloc.hpp
+
+Abstract:
+ Declarations for suspension safe memory allocation functions
+
+
+
+--*/
+
+#ifndef _MALLOC_HPP
+#define _MALLOC_HPP
+
+#include "pal/corunix.hpp"
+#include "pal/thread.hpp"
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+extern "C"
+{
+ void *
+ __cdecl
+ PAL_realloc(
+ void* pvMemblock,
+ size_t szSize
+ );
+
+ void *
+ __cdecl
+ PAL_malloc(
+ size_t szSize
+ );
+
+ void
+ __cdecl
+ PAL_free(
+ void *pvMem
+ );
+
+ char *
+ __cdecl
+ PAL__strdup(
+ const char *c_szStr
+ );
+}
+
+inline void* operator new(size_t, void* p) throw () { return p; }
+inline void* operator new[](size_t, void* p) throw () { return p; }
+
+namespace CorUnix{
+
+ void *
+ InternalRealloc(
+ void *pvMemblock,
+ size_t szSize
+ );
+
+ void *
+ InternalMalloc(
+ size_t szSize
+ );
+
+ // Define common code for "new" style allocators below.
+#define INTERNAL_NEW_COMMON() \
+ T *pMem = (T*)InternalMalloc(sizeof(T)); \
+ if (pMem == NULL) \
+ return NULL;
+
+ // Define "new" style allocators (which allocate then call a constructor) for different numbers of
+ // constructor arguments. Added based on usage.
+
+ // Default constructor (0 args) case.
+ template<class T>
+ T* InternalNew()
+ {
+ INTERNAL_NEW_COMMON();
+ return new (pMem) T();
+ }
+
+ // 2 args case.
+ template<class T, class A1, class A2>
+ T* InternalNew(A1 arg1, A2 arg2)
+ {
+ INTERNAL_NEW_COMMON();
+ return new (pMem) T(arg1, arg2);
+ }
+
+ // 4 args case.
+ template<class T, class A1, class A2, class A3, class A4>
+ T* InternalNew(A1 arg1, A2 arg2, A3 arg3, A4 arg4)
+ {
+ INTERNAL_NEW_COMMON();
+ return new (pMem) T(arg1, arg2, arg3, arg4);
+ }
+
+ // 5 args case.
+ template<class T, class A1, class A2, class A3, class A4, class A5>
+ T* InternalNew(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5)
+ {
+ INTERNAL_NEW_COMMON();
+ return new (pMem) T(arg1, arg2, arg3, arg4, arg5);
+ }
+
+ template<class T> T* InternalNewArray(size_t cElements)
+ {
+ size_t cbSize = (cElements * sizeof(T)) + sizeof(size_t);
+ T *pMem;
+
+ pMem = (T*)InternalMalloc(cbSize);
+
+ if (pMem == NULL)
+ return NULL;
+
+ *(size_t*)pMem = cElements;
+ pMem = (T*)((size_t*)pMem + 1);
+
+ return new (pMem) T[cElements]();
+ }
+
+ template<class T> void InternalDelete(T *p)
+ {
+ if (p)
+ {
+ p->~T();
+ free(p);
+ }
+ }
+
+ template<class T> void InternalDeleteArray(T *p)
+ {
+ if (p)
+ {
+ size_t *pRealMem = (size_t*)p - 1;
+ size_t cElements = *pRealMem;
+ for (size_t i = 0; i < cElements; i++)
+ p[i].~T();
+ free(pRealMem);
+ }
+ }
+}
+
+#endif // _MALLOC_HPP
diff --git a/src/pal/src/include/pal/map.h b/src/pal/src/include/pal/map.h
new file mode 100644
index 0000000000..96f538fbd1
--- /dev/null
+++ b/src/pal/src/include/pal/map.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.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/map.h
+
+Abstract:
+
+ Header file for file mapping functions.
+
+
+
+--*/
+
+#ifndef _PAL_MAP_H_
+#define _PAL_MAP_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/*++
+Function :
+ MAPGetRegionInfo
+
+ Parameters:
+ lpAddress: pointer to the starting memory location, not necessary
+ to be rounded to the page location
+
+ lpBuffer: if this function finds information about the specified address,
+ the information is stored in this struct
+
+ Note: This function is to be used in virtual.c
+
+ Returns TRUE if this function finds information about the specified address
+--*/
+
+BOOL MAPGetRegionInfo(LPVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_MAP_H_ */
+
diff --git a/src/pal/src/include/pal/map.hpp b/src/pal/src/include/pal/map.hpp
new file mode 100644
index 0000000000..854e6c549a
--- /dev/null
+++ b/src/pal/src/include/pal/map.hpp
@@ -0,0 +1,209 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/map.hpp
+
+Abstract:
+
+ Header file for file mapping functions.
+
+
+
+--*/
+
+#ifndef _PAL_MAP_H_
+#define _PAL_MAP_H_
+
+#include "corunix.hpp"
+#include <sys/param.h>
+
+extern "C"
+{
+#include "list.h"
+
+#ifndef NO_INO
+#define NO_INO ((ino_t)-1)
+#endif
+
+ /*++
+ Function :
+ MapInitialize
+
+ Initialize the critical sections.
+
+ Return value:
+ TRUE if initialization succeeded
+ FALSE otherwise
+ --*/
+ BOOL MAPInitialize( void );
+
+ /*++
+ Function :
+ MapCleanup
+
+ Deletes the critical sections.
+
+ --*/
+ void MAPCleanup( void );
+
+ /*++
+ Function :
+ MAPGetRegionInfo
+
+ Parameters:
+ lpAddress: pointer to the starting memory location, not necessary
+ to be rounded to the page location
+
+ lpBuffer: if this function finds information about the specified address,
+ the information is stored in this struct
+
+ Note: This function is to be used in virtual.c
+
+ Returns TRUE if this function finds information about the specified address
+ --*/
+
+ BOOL MAPGetRegionInfo(LPVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer);
+
+ /*++
+ MAPMapPEFile -
+
+ Map a PE format file into memory like Windows LoadLibrary() would do.
+ Doesn't apply base relocations if the function is relocated.
+
+ Parameters:
+ IN hFile - file to map
+
+ Return value:
+ non-NULL - the base address of the mapped image
+ NULL - error, with last error set.
+ --*/
+
+ void * MAPMapPEFile(HANDLE hFile);
+
+ /*++
+ Function :
+ MAPUnmapPEFile - unmap a PE file, and remove it from the recorded list of PE files mapped
+
+ returns TRUE if successful, FALSE otherwise
+ --*/
+ BOOL MAPUnmapPEFile(LPCVOID lpAddress);
+}
+
+namespace CorUnix
+{
+ extern CObjectType otFileMapping;
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ typedef struct _NativeMapHolder
+ {
+ Volatile<LONG> ref_count;
+ LPVOID address;
+ SIZE_T size;
+ SIZE_T offset; /* for future use */
+ } NativeMapHolder;
+#endif
+
+ /* Process specific information. This
+ structure is not stored in shared memory.*/
+ typedef struct _MVL
+ {
+ LIST_ENTRY Link;
+
+ //
+ // Each MVL entry holds a reference to its parent file
+ // mapping object.
+ //
+
+ IPalObject *pFileMapping;
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ NativeMapHolder * pNMHolder; /* Ref-counted holder for memory mapping */
+ dev_t MappedFileDevNum; /* ID of device containing the file to be mapped */
+ ino_t MappedFileInodeNum; /* Inode number of file to be mapped.
+ These two fields are used used to uniquely
+ identify files on systems that do not allow
+ more than one shared mmapping per region of
+ physical file, per process */
+#endif
+ LPVOID lpAddress; /* The pointer to the mapped memory. */
+ SIZE_T NumberOfBytesToMap; /* Number of bytes to map. */
+ DWORD dwDesiredAccess; /* Desired access. */
+ LPVOID lpPEBaseAddress; /* If this mapping is part of a PE file mapping, this is the
+ base address pointer of the PE file (used to find all
+ parts of the PE file mapping to allow PE file unload).
+ Otherwise, it is NULL. */
+ } MAPPED_VIEW_LIST, * PMAPPED_VIEW_LIST;
+
+ class CFileMappingImmutableData
+ {
+ public:
+ CHAR szFileName[MAXPATHLEN];
+ UINT MaxSize; // The max size of the file mapping object
+ DWORD flProtect; // Protection desired for the file view
+ BOOL bPALCreatedTempFile; // TRUE if it's a PAL created file
+ DWORD dwDesiredAccessWhenOpened; // FILE_MAP_WRITE etc
+ };
+
+ class CFileMappingProcessLocalData
+ {
+ public:
+ INT UnixFd; /* File descriptor. */
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ dev_t MappedFileDevNum; /* ID of device containing the file to be mapped */
+ ino_t MappedFileInodeNum; /* Inode number of file to be mapped.
+ These two fields are used used to uniquely
+ identify files on systems that do not allow
+ more than one shared mmapping per region of
+ physical file, per process */
+#endif
+ };
+
+ PAL_ERROR
+ InternalCreateFileMapping(
+ CPalThread *pThread,
+ HANDLE hFile,
+ LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
+ DWORD flProtect,
+ DWORD dwMaximumSizeHigh,
+ DWORD dwMaximumSizeLow,
+ LPCWSTR lpName,
+ HANDLE *phMapping
+ );
+
+ PAL_ERROR
+ InternalOpenFileMapping(
+ CPalThread *pThread,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ LPCWSTR lpName,
+ HANDLE *phMapping
+ );
+
+ PAL_ERROR
+ InternalMapViewOfFile(
+ CPalThread *pThread,
+ HANDLE hFileMappingObject,
+ DWORD dwDesiredAccess,
+ DWORD dwFileOffsetHigh,
+ DWORD dwFileOffsetLow,
+ SIZE_T dwNumberOfBytesToMap,
+ LPVOID *ppvBaseAddress
+ );
+
+ PAL_ERROR
+ InternalUnmapViewOfFile(
+ CPalThread *pThread,
+ LPCVOID lpBaseAddress
+ );
+
+}
+
+#endif /* _PAL_MAP_H_ */
diff --git a/src/pal/src/include/pal/misc.h b/src/pal/src/include/pal/misc.h
new file mode 100644
index 0000000000..65d59aee60
--- /dev/null
+++ b/src/pal/src/include/pal/misc.h
@@ -0,0 +1,83 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/misc.h
+
+Abstract:
+ Header file for the initialization and clean up functions
+ for the misc Win32 functions
+
+
+
+--*/
+
+#ifndef __MISC_H_
+#define __MISC_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/*++
+Function :
+
+ PAL_rand
+
+ Calls rand and mitigates the difference between RAND_MAX
+ on Windows and FreeBSD.
+--*/
+int __cdecl PAL_rand(void);
+
+/*++
+Function :
+
+ PAL_time
+--*/
+PAL_time_t __cdecl PAL_time(PAL_time_t*);
+
+/*++
+Function:
+TIMEInitialize
+
+Return value:
+TRUE if initialize succeeded
+FALSE otherwise
+
+--*/
+BOOL TIMEInitialize( void );
+
+/*++
+Function :
+ MsgBoxInitialize
+
+ Initialize the critical sections.
+
+Return value:
+ TRUE if initialize succeeded
+ FALSE otherwise
+
+--*/
+BOOL MsgBoxInitialize( void );
+
+/*++
+Function :
+ MsgBoxCleanup
+
+ Deletes the critical sections.
+
+--*/
+void MsgBoxCleanup( void );
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* __MISC_H_ */
diff --git a/src/pal/src/include/pal/module.h b/src/pal/src/include/pal/module.h
new file mode 100644
index 0000000000..95fa605c21
--- /dev/null
+++ b/src/pal/src/include/pal/module.h
@@ -0,0 +1,203 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/module.h
+
+Abstract:
+ Header file for modle management utilities.
+
+
+
+--*/
+
+#ifndef _PAL_MODULE_H_
+#define _PAL_MODULE_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+typedef BOOL (__stdcall *PDLLMAIN)(HINSTANCE, DWORD, LPVOID); /* entry point of module */
+typedef HINSTANCE (PALAPI *PREGISTER_MODULE)(LPCSTR); /* used to create the HINSTANCE for above DLLMain entry point */
+typedef VOID (PALAPI *PUNREGISTER_MODULE)(HINSTANCE); /* used to cleanup the HINSTANCE for above DLLMain entry point */
+
+typedef struct _MODSTRUCT
+{
+ HMODULE self; /* circular reference to this module */
+ void *dl_handle; /* handle returned by dlopen() */
+ HINSTANCE hinstance; /* handle returned by PAL_RegisterLibrary */
+ LPWSTR lib_name; /* full path of module */
+ INT refcount; /* reference count */
+ /* -1 means infinite reference count - module is never released */
+ BOOL threadLibCalls; /* TRUE for DLL_THREAD_ATTACH/DETACH notifications enabled, FALSE if they are disabled */
+
+#if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN
+ ino_t inode;
+ dev_t device;
+#endif
+
+ PDLLMAIN pDllMain; /* entry point of module */
+
+ /* reference to next and previous modules in list (in load order) */
+ struct _MODSTRUCT *next;
+ struct _MODSTRUCT *prev;
+} MODSTRUCT;
+
+
+/*++
+Function :
+ LOADInitializeModules
+
+ Initialize the process-wide list of modules
+
+Parameters :
+ None
+
+Return value :
+ TRUE on success, FALSE on failure
+
+--*/
+BOOL LOADInitializeModules();
+
+/*++
+Function :
+ LOADSetExeName
+
+ Set the exe name path
+
+Parameters :
+ LPWSTR man exe path and name
+
+Return value :
+ TRUE if initialization succeedded
+ FALSE otherwise
+
+--*/
+BOOL LOADSetExeName(LPWSTR name);
+
+/*++
+Function :
+ LOADCallDllMain
+
+ Call DllMain for all modules (that have one) with the given "fwReason"
+
+Parameters :
+ DWORD dwReason : parameter to pass down to DllMain, one of DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH,
+ DLL_THREAD_ATTACH, DLL_THREAD_DETACH
+
+ LPVOID lpReserved : parameter to pass down to DllMain
+ If dwReason is DLL_PROCESS_ATTACH, lpvReserved is NULL for dynamic loads and non-NULL for static loads.
+ If dwReason is DLL_PROCESS_DETACH, lpvReserved is NULL if DllMain has been called by using FreeLibrary
+ and non-NULL if DllMain has been called during process termination.
+
+(no return value)
+
+Notes :
+ This is used to send DLL_THREAD_*TACH messages to modules
+--*/
+void LOADCallDllMain(DWORD dwReason, LPVOID lpReserved);
+
+/*++
+Function:
+ LockModuleList
+
+Abstract
+ Enter the critical section associated to the module list
+
+Parameter
+ void
+
+Return
+ void
+--*/
+void LockModuleList();
+
+/*++
+Function:
+ UnlockModuleList
+
+Abstract
+ Leave the critical section associated to the module list
+
+Parameter
+ void
+
+Return
+ void
+--*/
+void UnlockModuleList();
+
+/*++
+Function:
+ PAL_LOADLoadPEFile
+
+Abstract
+ Loads a PE file into memory. Properly maps all of the sections in the PE file. Returns a pointer to the
+ loaded base.
+
+Parameters:
+ IN hFile - The file to load
+
+Return value:
+ A valid base address if successful.
+ 0 if failure
+--*/
+void * PAL_LOADLoadPEFile(HANDLE hFile);
+
+/*++
+ PAL_LOADUnloadPEFile
+
+ Unload a PE file that was loaded by PAL_LOADLoadPEFile().
+
+Parameters:
+ IN ptr - the file pointer returned by PAL_LOADLoadPEFile()
+
+Return value:
+ TRUE - success
+ FALSE - failure (incorrect ptr, etc.)
+--*/
+BOOL PAL_LOADUnloadPEFile(void * ptr);
+
+/*++
+ LOADInitializeCoreCLRModule
+
+ Run the initialization methods for CoreCLR module.
+
+Parameters:
+ None
+
+Return value:
+ TRUE if successful
+ FALSE if failure
+--*/
+BOOL LOADInitializeCoreCLRModule();
+
+/*++
+Function :
+ LOADGetPalLibrary
+
+ Load and initialize the PAL module.
+
+Parameters :
+ None
+
+Return value :
+ handle to loaded module
+
+--*/
+MODSTRUCT *LOADGetPalLibrary();
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_MODULE_H_ */
+
diff --git a/src/pal/src/include/pal/modulename.h b/src/pal/src/include/pal/modulename.h
new file mode 100644
index 0000000000..70b0a610dc
--- /dev/null
+++ b/src/pal/src/include/pal/modulename.h
@@ -0,0 +1,39 @@
+// 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.
+
+/*++
+
+
+
+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);
+#if defined(_AIX)
+int GetLibRotorNameViaLoadQuery(LPSTR pszBuf);
+#endif
+
+#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 0000000000..6a46689d7d
--- /dev/null
+++ b/src/pal/src/include/pal/mutex.hpp
@@ -0,0 +1,191 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ mutex.hpp
+
+Abstract:
+
+ Mutex object structure definition.
+
+
+
+--*/
+
+#ifndef _PAL_MUTEX_H_
+#define _PAL_MUTEX_H_
+
+#include "corunix.hpp"
+#include "sharedmemory.h"
+
+#include <pthread.h>
+
+namespace CorUnix
+{
+ extern CObjectType otMutex;
+ extern CObjectType otNamedMutex;
+
+ PAL_ERROR
+ InternalCreateMutex(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCSTR lpName,
+ HANDLE *phMutex
+ );
+
+ PAL_ERROR
+ InternalReleaseMutex(
+ CPalThread *pThread,
+ HANDLE hMutex
+ );
+
+ PAL_ERROR
+ InternalOpenMutex(
+ CPalThread *pThread,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ LPCSTR lpName,
+ HANDLE *phMutex
+ );
+
+}
+
+#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);
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Named mutex
+
+// Temporarily disabling usage of pthread process-shared mutexes on ARM/ARM64 due to functional issues that cannot easily be
+// detected with code due to hangs. See https://github.com/dotnet/coreclr/issues/5456.
+#if HAVE_FULLY_FEATURED_PTHREAD_MUTEXES && HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES && !(defined(_ARM_) || defined(_ARM64_))
+ #define NAMED_MUTEX_USE_PTHREAD_MUTEX 1
+#else
+ #define NAMED_MUTEX_USE_PTHREAD_MUTEX 0
+#endif
+
+enum class NamedMutexError : DWORD
+{
+ MaximumRecursiveLocksReached = ERROR_NOT_ENOUGH_MEMORY,
+ ThreadHasNotAcquiredMutex = ERROR_NOT_OWNER,
+ Unknown = ERROR_NOT_ENOUGH_MEMORY
+};
+
+enum class MutexTryAcquireLockResult
+{
+ AcquiredLock,
+ AcquiredLockButMutexWasAbandoned,
+ TimedOut
+};
+
+#if NAMED_MUTEX_USE_PTHREAD_MUTEX
+class MutexHelpers
+{
+public:
+ static void InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t *mutex);
+ static void DestroyMutex(pthread_mutex_t *mutex);
+
+ static MutexTryAcquireLockResult TryAcquireLock(pthread_mutex_t *mutex, DWORD timeoutMilliseconds);
+ static void ReleaseLock(pthread_mutex_t *mutex);
+};
+#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
+
+class NamedMutexSharedData
+{
+private:
+#if NAMED_MUTEX_USE_PTHREAD_MUTEX
+ pthread_mutex_t m_lock;
+#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ UINT32 m_timedWaiterCount;
+#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
+ UINT32 m_lockOwnerProcessId;
+ UINT64 m_lockOwnerThreadId;
+ bool m_isAbandoned;
+
+public:
+ NamedMutexSharedData();
+ ~NamedMutexSharedData();
+
+#if NAMED_MUTEX_USE_PTHREAD_MUTEX
+public:
+ pthread_mutex_t *GetLock();
+#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+public:
+ bool HasAnyTimedWaiters() const;
+ void IncTimedWaiterCount();
+ void DecTimedWaiterCount();
+#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
+
+public:
+ bool IsAbandoned() const;
+ void SetIsAbandoned(bool isAbandoned);
+
+public:
+ bool IsLockOwnedByAnyThread() const;
+ bool IsLockOwnedByCurrentThread() const;
+ void SetLockOwnerToCurrentThread();
+ void ClearLockOwner();
+};
+
+class NamedMutexProcessData : public SharedMemoryProcessDataBase
+{
+private:
+ static const UINT8 SyncSystemVersion;
+ static const DWORD PollLoopMaximumSleepMilliseconds;
+
+private:
+ SharedMemoryProcessDataHeader *m_processDataHeader;
+ NamedMutexSharedData *m_sharedData;
+ SIZE_T m_lockCount;
+#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ HANDLE m_processLockHandle;
+ int m_sharedLockFileDescriptor;
+#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ CorUnix::CPalThread *m_lockOwnerThread;
+ NamedMutexProcessData *m_nextInThreadOwnedNamedMutexList;
+
+public:
+ static SharedMemoryProcessDataHeader *CreateOrOpen(LPCSTR name, bool acquireLockIfCreated, bool *createdRef);
+ static SharedMemoryProcessDataHeader *Open(LPCSTR name);
+private:
+ static SharedMemoryProcessDataHeader *CreateOrOpen(LPCSTR name, bool createIfNotExist, bool acquireLockIfCreated, bool *createdRef);
+
+public:
+ NamedMutexProcessData(
+ SharedMemoryProcessDataHeader *processDataHeader
+ #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ ,
+ int sharedLockFileDescriptor
+ #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ );
+ virtual void Close(bool isAbruptShutdown, bool releaseSharedData) override;
+
+private:
+ NamedMutexSharedData *GetSharedData() const;
+ void SetLockOwnerThread(CorUnix::CPalThread *lockOwnerThread);
+public:
+ NamedMutexProcessData *GetNextInThreadOwnedNamedMutexList() const;
+ void SetNextInThreadOwnedNamedMutexList(NamedMutexProcessData *next);
+
+public:
+ MutexTryAcquireLockResult TryAcquireLock(DWORD timeoutMilliseconds);
+ void ReleaseLock();
+ void Abandon();
+private:
+ void ActuallyReleaseLock();
+};
+
+#endif //_PAL_MUTEX_H_
diff --git a/src/pal/src/include/pal/palinternal.h b/src/pal/src/include/pal/palinternal.h
new file mode 100644
index 0000000000..7348192e6d
--- /dev/null
+++ b/src/pal/src/include/pal/palinternal.h
@@ -0,0 +1,696 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ palinternal.h
+
+Abstract:
+
+ Rotor Platform Adaptation Layer (PAL) header file used by source
+ file part of the PAL implementation. This is a wrapper over
+ unix/inc/pal.h. It allows avoiding name collisions when including
+ system header files, and it allows redirecting calls to 'standard' functions
+ to their PAL counterpart
+
+Details :
+
+A] Rationale (see B] for the quick recipe)
+There are 2 types of namespace collisions that must be handled.
+
+1) standard functions declared in pal.h, which do not need to be
+ implemented in the PAL because the system's implementation is sufficient.
+
+ (examples : memcpy, strlen, fclose)
+
+ The problem with these is that a prototype for them is provided both in
+ pal.h and in a system header (stdio.h, etc). If a PAL file needs to
+ include the files containing both prototypes, the compiler may complain
+ about the multiple declarations.
+
+ To avoid this, the inclusion of pal.h must be wrapped in a
+ #define/#undef pair, which will effectiveily "hide" the pal.h
+ declaration by renaming it to something else. this is done by palinternal.h
+ in this way :
+
+ #define some_function DUMMY_some_function
+ #include <pal.h>
+ #undef some_function
+
+ when a PAL source file includes palinternal.h, it will see a prototype for
+ DUMMY_some_function instead of some_function; so when it includes the
+ system header with the "real" prototype, no collision occurs.
+
+ (note : technically, no functions should ever be treated this way, all
+ system functions should be wrapped according to method 2, so that call
+ logging through ENTRY macros is done for all functions n the PAL. However
+ this reason alone is not currently considered enough to warrant a wrapper)
+
+2) standard functions which must be reimplemented by the PAL, because the
+ system's implementation does not offer suitable functionnality.
+
+ (examples : widestring functions, networking)
+
+ Here, the problem is more complex. The PAL must provide functions with the
+ same name as system functions. Due to the nature of Unix dynamic linking,
+ if this is done, the PAL's implementation will effectively mask the "real"
+ function, so that all calls are directed to it. This makes it impossible for
+ a function to be implemented as calling its counterpart in the system, plus
+ some extra work, because instead of calling the system's implementation, the
+ function would only call itself in an infinitely recursing nightmare. Even
+ worse, if by bad luck the system libraries attempt to call the function for
+ which the PAL provides an implementation, it is the PAL's version that will
+ be called.
+ It is therefore necessary to give the PAL's implementation of such functions
+ a different name. However, PAL consumers (applications built on top of the
+ PAL) must be able to call the function by its 'official' name, not the PAL's
+ internal name.
+ This can be done with some more macro magic, by #defining the official name
+ to the internal name *in pal.h*. :
+
+ #define some_function PAL_some_function
+
+ This way, while PAL consumer code can use the official name, it is the
+ internal name that wil be seen at compile time.
+ However, one extra step is needed. While PAL consumers must use the PAL's
+ implementation of these functions, the PAL itself must still have access to
+ the "real" functions. This is done by #undefining in palinternal.h the names
+ #defined in pal.h :
+
+ #include <pal.h>
+ #undef some_function.
+
+ At this point, code in the PAL implementation can access *both* its own
+ implementation of the function (with PAL_some_function) *and* the system's
+ implementation (with some_function)
+
+ [side note : for the Win32 PAL, this can be accomplished without touching
+ pal.h. In Windows, symbols in in dynamic libraries are resolved at
+ compile time. if an application that uses some_function is only linked to
+ pal.dll, some_function will be resolved to the version in that DLL,
+ even if other DLLs in the system provide other implementations. In addition,
+ the function in the DLL can actually have a different name (e.g.
+ PAL_some_function), to which the 'official' name is aliased when the DLL
+ is compiled. All this is not possible with Unix dynamic linking, where
+ symbols are resolved at run-time in a first-found-first-used order. A
+ module may end up using the symbols from a module it was never linked with,
+ simply because that module was located somewhere in the dependency chain. ]
+
+ It should be mentionned that even if a function name is not documented as
+ being implemented in the system, it can still cause problems if it exists.
+ This is especially a problem for functions in the "reserved" namespace
+ (names starting with an underscore : _exit, etc). (We shouldn't really be
+ implementing functions with such a name, but we don't really have a choice)
+ If such a case is detected, it should be wrapped according to method 2
+
+ Note that for all this to work, it is important for the PAL's implementation
+ files to #include palinternal.h *before* any system files, and to never
+ include pal.h directly.
+
+B] Procedure for name conflict resolution :
+
+When adding a function to pal.h, which is implemented by the system and
+which does not need a different implementation :
+
+- add a #define function_name DUMMY_function_name to palinternal.h, after all
+ the other DUMMY_ #defines (above the #include <pal.h> line)
+- add the function's prototype to pal.h (if that isn't already done)
+- add a #undef function_name to palinternal.h near all the other #undefs
+ (after the #include <pal.h> line)
+
+When overriding a system function with the PAL's own implementation :
+
+- add a #define function_name PAL_function_name to pal.h, somewhere
+ before the function's prototype, inside a #ifndef _MSCVER/#endif pair
+ (to avoid affecting the Win32 build)
+- add a #undef function_name to palinternal.h near all the other #undefs
+ (after the #include <pal.h> line)
+- implement the function in the pal, naming it PAL_function_name
+- within the PAL, call PAL_function_name() to call the PAL's implementation,
+function_name() to call the system's implementation
+
+
+
+--*/
+
+#ifndef _PAL_INTERNAL_H_
+#define _PAL_INTERNAL_H_
+
+#define PAL_IMPLEMENTATION
+
+/* Include our configuration information so it's always present when
+ compiling PAL implementation files. */
+#include "config.h"
+
+#ifdef DEBUG
+#define _ENABLE_DEBUG_MESSAGES_ 1
+#else
+#define _ENABLE_DEBUG_MESSAGES_ 0
+#endif
+
+#ifdef PAL_PERF
+#include "pal_perf.h"
+#endif
+
+/* C runtime functions needed to be renamed to avoid duplicate definition
+ of those functions when including standard C header files */
+#define div DUMMY_div
+#define div_t DUMMY_div_t
+#if !defined(_DEBUG)
+#define memcpy DUMMY_memcpy
+#endif //!defined(_DEBUG)
+#define memcmp DUMMY_memcmp
+#define memset DUMMY_memset
+#define memmove DUMMY_memmove
+#define memchr DUMMY_memchr
+#define strlen DUMMY_strlen
+#define strnlen DUMMY_strnlen
+#define stricmp DUMMY_stricmp
+#define strstr DUMMY_strstr
+#define strcmp DUMMY_strcmp
+#define strcat DUMMY_strcat
+#define strncat DUMMY_strncat
+#define strcpy DUMMY_strcpy
+#define strcspn DUMMY_strcspn
+#define strncmp DUMMY_strncmp
+#define strncpy DUMMY_strncpy
+#define strchr DUMMY_strchr
+#define strrchr DUMMY_strrchr
+#define strpbrk DUMMY_strpbrk
+#define strtod DUMMY_strtod
+#define strspn DUMMY_strspn
+#if HAVE__SNPRINTF
+#define _snprintf DUMMY__snprintf
+#endif /* HAVE__SNPRINTF */
+#if HAVE__SNWPRINTF
+#define _snwprintf DUMMY__snwprintf
+#endif /* HAVE__SNWPRINTF */
+#define tolower DUMMY_tolower
+#define toupper DUMMY_toupper
+#define islower DUMMY_islower
+#define isupper DUMMY_isupper
+#define isprint DUMMY_isprint
+#define isdigit DUMMY_isdigit
+#define srand DUMMY_srand
+#define atoi DUMMY_atoi
+#define atof DUMMY_atof
+#define tm PAL_tm
+#define size_t DUMMY_size_t
+#define time_t PAL_time_t
+#define va_list DUMMY_va_list
+#define abs DUMMY_abs
+#define llabs DUMMY_llabs
+#define ceil DUMMY_ceil
+#define cos DUMMY_cos
+#define cosh DUMMY_cosh
+#define fabs DUMMY_fabs
+#define floor DUMMY_floor
+#define fmod DUMMY_fmod
+#define modf DUMMY_modf
+#define sin DUMMY_sin
+#define sinh DUMMY_sinh
+#define sqrt DUMMY_sqrt
+#define tan DUMMY_tan
+#define tanh DUMMY_tanh
+#define fabsf DUMMY_fabsf
+#define fmodf DUMMY_fmodf
+#define modff DUMMY_modff
+
+/* RAND_MAX needed to be renamed to avoid duplicate definition when including
+ stdlib.h header files. PAL_RAND_MAX should have the same value as RAND_MAX
+ defined in pal.h */
+#define PAL_RAND_MAX 0x7fff
+
+/* The standard headers define isspace and isxdigit as macros and functions,
+ To avoid redefinition problems, undefine those macros. */
+#ifdef isspace
+#undef isspace
+#endif
+#ifdef isxdigit
+#undef isxdigit
+#endif
+#ifdef isalpha
+#undef isalpha
+#endif
+#ifdef isalnum
+#undef isalnum
+#endif
+#define isspace DUMMY_isspace
+#define isxdigit DUMMY_isxdigit
+#define isalpha DUMMY_isalpha
+#define isalnum DUMMY_isalnum
+
+#ifdef stdin
+#undef stdin
+#endif
+#ifdef stdout
+#undef stdout
+#endif
+#ifdef stderr
+#undef stderr
+#endif
+
+#ifdef SCHAR_MIN
+#undef SCHAR_MIN
+#endif
+#ifdef SCHAR_MAX
+#undef SCHAR_MAX
+#endif
+#ifdef SHRT_MIN
+#undef SHRT_MIN
+#endif
+#ifdef SHRT_MAX
+#undef SHRT_MAX
+#endif
+#ifdef UCHAR_MAX
+#undef UCHAR_MAX
+#endif
+#ifdef USHRT_MAX
+#undef USHRT_MAX
+#endif
+#ifdef ULONG_MAX
+#undef ULONG_MAX
+#endif
+#ifdef LONG_MIN
+#undef LONG_MIN
+#endif
+#ifdef LONG_MAX
+#undef LONG_MAX
+#endif
+#ifdef RAND_MAX
+#undef RAND_MAX
+#endif
+#ifdef DBL_MAX
+#undef DBL_MAX
+#endif
+#ifdef FLT_MAX
+#undef FLT_MAX
+#endif
+#ifdef __record_type_class
+#undef __record_type_class
+#endif
+#ifdef __real_type_class
+#undef __real_type_class
+#endif
+
+// The standard headers define va_start and va_end as macros,
+// To avoid redefinition problems, undefine those macros.
+#ifdef va_start
+#undef va_start
+#endif
+#ifdef va_end
+#undef va_end
+#endif
+#ifdef va_copy
+#undef va_copy
+#endif
+
+
+#ifdef _VAC_
+#define wchar_16 wchar_t
+#else
+#define wchar_t wchar_16
+#endif // _VAC_
+
+#define ptrdiff_t PAL_ptrdiff_t
+#define intptr_t PAL_intptr_t
+#define uintptr_t PAL_uintptr_t
+#define timeval PAL_timeval
+#define FILE PAL_FILE
+
+#include "pal.h"
+#include "palprivate.h"
+
+#include "mbusafecrt.h"
+
+#ifdef _VAC_
+#undef CHAR_BIT
+#undef va_arg
+#endif
+
+#if !defined(_MSC_VER) && defined(FEATURE_PAL) && defined(_WIN64)
+#undef _BitScanForward64
+#endif
+
+/* pal.h defines alloca(3) as a compiler builtin.
+ Redefining it to native libc will result in undefined breakage because
+ a compiler is allowed to make assumptions about the stack and frame
+ pointers. */
+
+/* Undef all functions and types previously defined so those functions and
+ types could be mapped to the C runtime and socket implementation of the
+ native OS */
+#undef exit
+#undef atexit
+#undef div
+#undef div_t
+#if !defined(_DEBUG)
+#undef memcpy
+#endif //!defined(_DEBUG)
+#undef memcmp
+#undef memset
+#undef memmove
+#undef memchr
+#undef strlen
+#undef strnlen
+#undef stricmp
+#undef strstr
+#undef strcmp
+#undef strcat
+#undef strcspn
+#undef strncat
+#undef strcpy
+#undef strncmp
+#undef strncpy
+#undef strchr
+#undef strrchr
+#undef strpbrk
+#undef strtoul
+#undef strtod
+#undef strspn
+#undef strtok
+#undef strdup
+#undef tolower
+#undef toupper
+#undef islower
+#undef isupper
+#undef isprint
+#undef isdigit
+#undef isspace
+#undef iswdigit
+#undef iswxdigit
+#undef iswalpha
+#undef iswprint
+#undef isxdigit
+#undef isalpha
+#undef isalnum
+#undef atoi
+#undef atol
+#undef atof
+#undef malloc
+#undef realloc
+#undef free
+#undef qsort
+#undef bsearch
+#undef time
+#undef tm
+#undef localtime
+#undef mktime
+#undef FILE
+#undef fclose
+#undef setbuf
+#undef fopen
+#undef fread
+#undef feof
+#undef ferror
+#undef ftell
+#undef fflush
+#undef fwrite
+#undef fgets
+#undef fgetws
+#undef fputc
+#undef putchar
+#undef fputs
+#undef fseek
+#undef fgetpos
+#undef fsetpos
+#undef getcwd
+#undef getc
+#undef fgetc
+#undef ungetc
+#undef _flushall
+#undef setvbuf
+#undef mkstemp
+#undef rename
+#undef unlink
+#undef size_t
+#undef time_t
+#undef va_list
+#undef va_start
+#undef va_end
+#undef va_copy
+#undef stdin
+#undef stdout
+#undef stderr
+#undef abs
+#undef labs
+#undef llabs
+#undef acos
+#undef asin
+#undef atan
+#undef atan2
+#undef ceil
+#undef cos
+#undef cosh
+#undef exp
+#undef fabs
+#undef floor
+#undef fmod
+#undef log
+#undef log10
+#undef modf
+#undef pow
+#undef sin
+#undef sinh
+#undef sqrt
+#undef tan
+#undef tanh
+#undef fabsf
+#undef fmodf
+#undef modff
+#undef rand
+#undef srand
+#undef errno
+#undef getenv
+#undef wcsspn
+#undef open
+#undef glob
+
+#undef wchar_t
+#undef ptrdiff_t
+#undef intptr_t
+#undef uintptr_t
+#undef timeval
+
+
+#undef printf
+#undef fprintf
+#undef fwprintf
+#undef vfprintf
+#undef vfwprintf
+#undef vprintf
+#undef wprintf
+#undef sprintf
+#undef swprintf
+#undef _snprintf
+#if HAVE__SNWPRINTF
+#undef _snwprintf
+#endif /* HAVE__SNWPRINTF */
+#undef sscanf
+#undef wcstod
+#undef wcstol
+#undef wcstoul
+#undef _wcstoui64
+#undef wcscat
+#undef wcscpy
+#undef wcslen
+#undef wcsncmp
+#undef wcschr
+#undef wcsrchr
+#undef wsprintf
+#undef swscanf
+#undef wcspbrk
+#undef wcsstr
+#undef wcscmp
+#undef wcsncat
+#undef wcsncpy
+#undef wcstok
+#undef wcscspn
+#undef iswupper
+#undef iswspace
+#undef towlower
+#undef towupper
+#undef vsprintf
+#undef vswprintf
+#undef _vsnprintf
+#undef _vsnwprintf
+#undef vsnprintf
+#undef wvsnprintf
+
+#ifdef _AMD64_
+#undef _mm_getcsr
+#undef _mm_setcsr
+#endif // _AMD64_
+
+#undef ctime
+
+#undef SCHAR_MIN
+#undef SCHAR_MAX
+#undef UCHAR_MAX
+#undef SHRT_MIN
+#undef SHRT_MAX
+#undef USHRT_MAX
+#undef LONG_MIN
+#undef LONG_MAX
+#undef ULONG_MAX
+#undef RAND_MAX
+#undef DBL_MAX
+#undef FLT_MAX
+#undef __record_type_class
+#undef __real_type_class
+
+#if HAVE_CHAR_BIT
+#undef CHAR_BIT
+#endif
+
+// We need a sigsetjmp prototype in pal.h for the SEH macros, but we
+// can't use the "real" prototype (because we don't want to define sigjmp_buf).
+// So we must rename the "real" sigsetjmp to avoid redefinition errors.
+#define sigsetjmp REAL_sigsetjmp
+#define siglongjmp REAL_siglongjmp
+#include <setjmp.h>
+#undef sigsetjmp
+#undef siglongjmp
+
+#undef _SIZE_T_DEFINED
+#undef _WCHAR_T_DEFINED
+
+#define _DONT_USE_CTYPE_INLINE_
+#if HAVE_RUNETYPE_H
+#include <runetype.h>
+#endif
+#include <ctype.h>
+
+// Don't use C++ wrappers for stdlib.h
+// https://gcc.gnu.org/ml/libstdc++/2016-01/msg00025.html
+#define _GLIBCXX_INCLUDE_NEXT_C_HEADERS 1
+
+#define _WITH_GETLINE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glob.h>
+
+#ifdef __APPLE__
+
+#undef GetCurrentThread
+#include <CoreServices/CoreServices.h>
+
+#include <malloc/malloc.h>
+
+#endif // __APPLE__
+
+/* we don't really need this header here, but by including it we make sure
+ we'll catch any definition conflicts */
+#include <sys/socket.h>
+
+#if !HAVE_INFTIM
+#define INFTIM -1
+#endif // !HAVE_INFTIM
+
+#if (__GNUC__ >= 4)
+#define OffsetOf(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER)
+#else
+#define OffsetOf(s, f) (INT)(SIZE_T)&(((s*)0)->f)
+#endif /* __GNUC__ version check*/
+
+#undef assert
+#define assert (Use__ASSERTE_instead_of_assert) assert
+
+#define PROCESS_PIPE_NAME_PREFIX ".dotnet-pal-processpipe"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+typedef enum _TimeConversionConstants
+{
+ tccSecondsToMillieSeconds = 1000, // 10^3
+ tccSecondsToMicroSeconds = 1000000, // 10^6
+ tccSecondsToNanoSeconds = 1000000000, // 10^9
+ tccMillieSecondsToMicroSeconds = 1000, // 10^3
+ tccMillieSecondsToNanoSeconds = 1000000, // 10^6
+ tccMicroSecondsToNanoSeconds = 1000, // 10^3
+ tccSecondsTo100NanoSeconds = 10000000, // 10^7
+ tccMicroSecondsTo100NanoSeconds = 10 // 10^1
+} TimeConversionConstants;
+
+#ifdef __cplusplus
+}
+
+/* This is duplicated in utilcode.h for CLR, with cooler type-traits */
+template <typename T>
+inline
+T* InterlockedExchangePointerT(
+ T* volatile *Target,
+ T* Value)
+{
+ return (T*)(InterlockedExchangePointer(
+ (PVOID volatile*)Target,
+ (PVOID)Value));
+}
+
+template <typename T>
+inline
+T* InterlockedCompareExchangePointerT(
+ T* volatile *destination,
+ T* exchange,
+ T* comparand)
+{
+ return (T*)(InterlockedCompareExchangePointer(
+ (PVOID volatile*)destination,
+ (PVOID)exchange,
+ (PVOID)comparand));
+}
+
+template <typename T>
+inline T* InterlockedExchangePointerT(
+ T* volatile * target,
+ int value) // When NULL is provided as argument.
+{
+ //STATIC_ASSERT(value == 0);
+ return InterlockedExchangePointerT(target, reinterpret_cast<T*>(value));
+}
+
+template <typename T>
+inline T* InterlockedCompareExchangePointerT(
+ T* volatile * destination,
+ int exchange, // When NULL is provided as argument.
+ T* comparand)
+{
+ //STATIC_ASSERT(exchange == 0);
+ return InterlockedCompareExchangePointerT(destination, reinterpret_cast<T*>(exchange), comparand);
+}
+
+template <typename T>
+inline T* InterlockedCompareExchangePointerT(
+ T* volatile * destination,
+ T* exchange,
+ int comparand) // When NULL is provided as argument.
+{
+ //STATIC_ASSERT(comparand == 0);
+ return InterlockedCompareExchangePointerT(destination, exchange, reinterpret_cast<T*>(comparand));
+}
+
+#undef InterlockedExchangePointer
+#define InterlockedExchangePointer InterlockedExchangePointerT
+#undef InterlockedCompareExchangePointer
+#define InterlockedCompareExchangePointer InterlockedCompareExchangePointerT
+
+#include "volatile.h"
+
+const char StackOverflowMessage[] = "Process is terminated due to StackOverflowException.\n";
+
+#endif // __cplusplus
+
+#endif /* _PAL_INTERNAL_H_ */
diff --git a/src/pal/src/include/pal/perftrace.h b/src/pal/src/include/pal/perftrace.h
new file mode 100644
index 0000000000..fec46e2330
--- /dev/null
+++ b/src/pal/src/include/pal/perftrace.h
@@ -0,0 +1,70 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/perftrace.h
+
+Abstract:
+ Header file for PAL Performance trace utilities.
+
+
+
+--*/
+
+/*
+Overview of PAL Performance utilities
+
+ */
+
+#ifndef _PAL_PERFTRACE_H_
+#define _PAL_PERFTRACE_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+#if PAL_PERF
+#define PERF_ENTRY(x) \
+ ULONGLONG pal_perf_start_tick = 0;\
+ PERFLogFunctionEntry( PAL_PERF_##x, &pal_perf_start_tick )
+#define PERF_EXIT(x) \
+ PERFLogFunctionExit( PAL_PERF_##x, &pal_perf_start_tick )
+#define PERF_ENTRY_ONLY(x) \
+ PERFNoLatencyProfileEntry( PAL_PERF_##x )
+
+BOOL PERFInitialize(LPWSTR command_line, LPWSTR exe_path) ;
+void PERFTerminate( );
+BOOL PERFAllocThreadInfo( );
+void PERFLogFunctionExit(unsigned int pal_api_id, ULONGLONG *pal_perf_start_tick);
+void PERFLogFunctionEntry(unsigned int pal_api_id, ULONGLONG *pal_perf_start_tick);
+void PERFEnableThreadProfile(BOOL isInternal);
+void PERFDisableThreadProfile(BOOL isInternal);
+void PERFEnableProcessProfile( );
+void PERFDisableProcessProfile( );
+BOOL PERFIsProcessProfileEnabled( );
+void PERFNoLatencyProfileEntry(unsigned int pal_api_id );
+void PERFCalibrate(const char* msg);
+
+#else /* PAL_PERF */
+
+#define PERF_ENTRY(x)
+#define PERF_ENTRY_ONLY(x)
+#define PERF_EXIT(x)
+
+#endif /* PAL_PERF */
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_PERFTRACE_H_ */
+
+
+
diff --git a/src/pal/src/include/pal/printfcpp.hpp b/src/pal/src/include/pal/printfcpp.hpp
new file mode 100644
index 0000000000..0a728c9fd7
--- /dev/null
+++ b/src/pal/src/include/pal/printfcpp.hpp
@@ -0,0 +1,133 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ pal/printfcpp.hpp
+
+Abstract:
+ Declarations for suspension safe memory allocation functions
+
+
+
+--*/
+
+#ifndef _PRINTFCPP_HPP
+#define _PRINTFCPP_HPP
+
+#ifdef __cplusplus
+#include "pal/threadinfo.hpp"
+#endif
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+typedef char16_t wchar_16; // __wchar_16_cpp (which is defined in palinternal.h) needs to be redefined to wchar_16.
+
+extern "C"
+{
+ int
+ __cdecl
+ PAL__vsnprintf(
+ LPSTR Buffer,
+ size_t Count,
+ LPCSTR Format,
+ va_list ap);
+
+ int
+ __cdecl
+ PAL__wvsnprintf(
+ LPWSTR Buffer,
+ size_t Count,
+ LPCWSTR Format,
+ va_list ap);
+
+ int
+ __cdecl
+ PAL_vfprintf(
+ PAL_FILE *stream,
+ const char *format,
+ va_list ap);
+
+ int
+ __cdecl
+ PAL_vfwprintf(
+ PAL_FILE *stream,
+ const wchar_16 *format,
+ va_list ap);
+}
+
+namespace CorUnix
+{
+ int
+ InternalVfprintf(
+ CPalThread *pthrCurrent,
+ PAL_FILE *stream,
+ const char *format,
+ va_list ap);
+
+ int
+ InternalWvsnprintf(
+ CPalThread *pthrCurrent,
+ LPWSTR Buffer,
+ size_t Count,
+ LPCWSTR Format,
+ va_list ap);
+
+ int
+ InternalVsnprintf(
+ CPalThread *pthrCurrent,
+ LPSTR Buffer,
+ size_t Count,
+ LPCSTR Format,
+ va_list ap);
+
+ int
+ InternalVfwprintf(
+ CPalThread *pthrCurrent,
+ PAL_FILE *stream,
+ const wchar_16 *format,
+ va_list ap);
+
+}
+#else // __cplusplus
+
+ int
+ __cdecl
+ PAL__vsnprintf(
+ LPSTR Buffer,
+ size_t Count,
+ LPCSTR Format,
+ va_list ap);
+
+ int
+ __cdecl
+ PAL__wvsnprintf(
+ LPWSTR Buffer,
+ size_t Count,
+ LPCWSTR Format,
+ va_list ap);
+
+ int
+ __cdecl
+ PAL_vfprintf(
+ PAL_FILE *stream,
+ const char *format,
+ va_list ap);
+
+ int
+ __cdecl
+ PAL_vfwprintf(
+ PAL_FILE *stream,
+ const wchar_16 *format,
+ va_list ap);
+
+#endif // __cplusplus
+
+#endif // _PRINTFCPP_HPP
+
diff --git a/src/pal/src/include/pal/process.h b/src/pal/src/include/pal/process.h
new file mode 100644
index 0000000000..990aec5b21
--- /dev/null
+++ b/src/pal/src/include/pal/process.h
@@ -0,0 +1,162 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/process.h
+
+Abstract:
+
+ Miscellaneous process related functions.
+
+Revision History:
+
+
+
+--*/
+
+#ifndef _PAL_PROCESS_H_
+#define _PAL_PROCESS_H_
+
+#include "pal/palinternal.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/* thread ID of thread that has initiated an ExitProcess (or TerminateProcess).
+ this is to make sure only one thread cleans up the PAL, and also to prevent
+ calls to CreateThread from succeeding once shutdown has started
+ [defined in process.c]
+*/
+extern Volatile<LONG> terminator;
+
+// The process and session ID of this process, so we can avoid excessive calls to getpid() and getsid().
+extern DWORD gPID;
+extern DWORD gSID;
+
+extern LPWSTR pAppDir;
+
+/*++
+Function:
+ PROCGetProcessIDFromHandle
+
+Abstract
+ Return the process ID from a process handle
+--*/
+DWORD PROCGetProcessIDFromHandle(HANDLE hProcess);
+
+/*++
+Function:
+ PROCCreateInitialProcess
+
+Abstract
+ Initialize all the structures for the initial process.
+
+Parameter
+ lpwstrCmdLine: Command line.
+ lpwstrFullPath : Full path to executable
+
+Return
+ TRUE: if successful
+ FALSE: otherwise
+
+Notes :
+ This function takes ownership of lpwstrCmdLine, but not of lpwstrFullPath
+--*/
+BOOL PROCCreateInitialProcess(LPWSTR lpwstrCmdLine, LPWSTR lpwstrFullPath);
+
+/*++
+Function:
+ PROCCleanupInitialProcess
+
+Abstract
+ Cleanup all the structures for the initial process.
+
+Parameter
+ VOID
+
+Return
+ VOID
+
+--*/
+VOID PROCCleanupInitialProcess(VOID);
+
+#if USE_SYSV_SEMAPHORES
+/*++
+Function:
+ PROCCleanupThreadSemIds(VOID);
+
+Abstract
+ Cleanup SysV semaphore ids for all threads.
+
+(no parameters, no return value)
+--*/
+VOID PROCCleanupThreadSemIds(VOID);
+#endif
+
+/*++
+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()
+
+ 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();
+
+/*++
+Function:
+ PROCNotifyProcessShutdown
+
+ Calls the abort handler to do any shutdown cleanup. Call be
+ called from the unhandled native exception handler.
+
+(no return value)
+--*/
+void PROCNotifyProcessShutdown();
+
+/*++
+Function:
+ InitializeFlushProcessWriteBuffers
+
+Abstract
+ This function initializes data structures needed for the FlushProcessWriteBuffers
+Return
+ TRUE if it succeeded, FALSE otherwise
+--*/
+BOOL InitializeFlushProcessWriteBuffers();
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif //PAL_PROCESS_H_
+
diff --git a/src/pal/src/include/pal/procobj.hpp b/src/pal/src/include/pal/procobj.hpp
new file mode 100644
index 0000000000..a75c764246
--- /dev/null
+++ b/src/pal/src/include/pal/procobj.hpp
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+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,
+ BOOL bInheritHandles,
+ 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/seh.hpp b/src/pal/src/include/pal/seh.hpp
new file mode 100644
index 0000000000..3ac93d655a
--- /dev/null
+++ b/src/pal/src/include/pal/seh.hpp
@@ -0,0 +1,185 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/seh.hpp
+
+Abstract:
+ Header file for public Structured Exception Handling stuff
+
+
+
+--*/
+
+#ifndef _PAL_SEH_HPP_
+#define _PAL_SEH_HPP_
+
+#include "config.h"
+#include "pal/palinternal.h"
+#include "pal/corunix.hpp"
+
+// Uncomment this define to turn off the signal handling thread.
+// #define DO_NOT_USE_SIGNAL_HANDLING_THREAD
+
+/*++
+Function :
+ SEHInitialize
+
+ Initialize all SEH-related stuff (signals, etc)
+
+Parameters:
+ CPalThread * pthrCurrent : reference to the current thread.
+ flags : PAL initialize flags
+
+Return value:
+ TRUE if SEH support initialization succeeded,
+ FALSE otherwise
+
+--*/
+BOOL
+SEHInitialize(CorUnix::CPalThread *pthrCurrent, DWORD flags);
+
+/*++
+Function :
+ SEHCleanup
+
+ Clean up SEH-related stuff(signals, etc)
+
+Parameters:
+ None
+
+ (no return value)
+--*/
+VOID
+SEHCleanup();
+
+/*++
+Function:
+ SEHProcessException
+
+ Send the PAL exception to any handler registered.
+
+Parameters:
+ PAL_SEHException* exception
+
+Return value:
+ Returns TRUE if the exception happened in managed code and the execution should
+ continue (with possibly modified context).
+ Returns FALSE if the exception happened in managed code and it was not handled.
+ In case the exception was handled by calling a catch handler, it doesn't return at all.
+--*/
+BOOL
+SEHProcessException(PAL_SEHException* exception);
+
+/*++
+Function:
+ AllocateExceptionRecords
+
+Parameters:
+ exceptionRecord - output pointer to the allocated Windows exception record
+ contextRecord - output pointer to the allocated Windows context record
+--*/
+VOID
+AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord);
+
+#if !HAVE_MACH_EXCEPTIONS
+// TODO: Implement for Mach exceptions. Not in CoreCLR surface area.
+/*++
+Function :
+ SEHHandleControlEvent
+
+ handle Control-C and Control-Break events (call handler routines,
+ notify debugger)
+
+Parameters :
+ DWORD event : event that occurred
+ LPVOID eip : instruction pointer when exception occurred
+
+(no return value)
+
+Notes :
+ Handlers are called on a last-installed, first called basis, until a
+ handler returns TRUE. If no handler returns TRUE (or no hanlder is
+ installed), the default behavior is to call ExitProcess
+--*/
+void SEHHandleControlEvent(DWORD event, LPVOID eip);
+#endif // !HAVE_MACH_EXCEPTIONS
+
+#if !HAVE_MACH_EXCEPTIONS
+/*++
+Function :
+ SEHSetSafeState
+
+ specify whether the current thread is in a state where exception handling
+ of signals can be done safely
+
+Parameters:
+ CPalThread * pthrCurrent : reference to the current thread.
+ BOOL state : TRUE if the thread is safe, FALSE otherwise
+
+(no return value)
+--*/
+void SEHSetSafeState(CorUnix::CPalThread *pthrCurrent, BOOL state);
+
+/*++
+Function :
+ SEHGetSafeState
+
+ determine whether the current thread is in a state where exception handling
+ of signals can be done safely
+
+Parameters:
+ CPalThread * pthrCurrent : reference to the current thread.
+
+Return value :
+ TRUE if the thread is in a safe state, FALSE otherwise
+--*/
+BOOL SEHGetSafeState(CorUnix::CPalThread *pthrCurrent);
+#endif // !HAVE_MACH_EXCEPTIONS
+
+extern "C"
+{
+
+#ifdef FEATURE_PAL_SXS
+/*++
+Function :
+ SEHEnable
+
+ Enable SEH-related stuff on this thread
+
+Parameters:
+ CPalThread * pthrCurrent : reference to the current thread.
+
+Return value :
+ ERROR_SUCCESS, if enabling succeeded
+ an error code, otherwise
+--*/
+CorUnix::PAL_ERROR SEHEnable(CorUnix::CPalThread *pthrCurrent);
+
+/*++
+Function :
+ SEHDisable
+
+ Disable SEH-related stuff on this thread
+
+Parameters:
+ CPalThread * pthrCurrent : reference to the current thread.
+
+Return value :
+ ERROR_SUCCESS, if enabling succeeded
+ an error code, otherwise
+--*/
+CorUnix::PAL_ERROR SEHDisable(CorUnix::CPalThread *pthrCurrent);
+
+#endif // FEATURE_PAL_SXS
+
+}
+
+#endif /* _PAL_SEH_HPP_ */
+
diff --git a/src/pal/src/include/pal/semaphore.hpp b/src/pal/src/include/pal/semaphore.hpp
new file mode 100644
index 0000000000..2943d61c3d
--- /dev/null
+++ b/src/pal/src/include/pal/semaphore.hpp
@@ -0,0 +1,74 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ semaphore.hpp
+
+Abstract:
+
+ Semaphore object structure definition.
+
+
+
+--*/
+
+#ifndef _PAL_SEMAPHORE_H_
+#define _PAL_SEMAPHORE_H_
+
+#include "corunix.hpp"
+
+namespace CorUnix
+{
+ extern CObjectType otSemaphore;
+
+ typedef struct
+ {
+ LONG lMaximumCount;
+ } SemaphoreImmutableData;
+
+ PAL_ERROR
+ InternalCreateSemaphore(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
+ LONG lInitialCount,
+ LONG lMaximumCount,
+ LPCWSTR lpName,
+ HANDLE *phSemaphore
+ );
+
+ PAL_ERROR
+ InternalReleaseSemaphore(
+ CPalThread *pThread,
+ HANDLE hSemaphore,
+ LONG lReleaseCount,
+ LPLONG lpPreviousCount
+ );
+
+ PAL_ERROR
+ InternalOpenSemaphore(
+ CPalThread *pThread,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ LPCWSTR lpName,
+ HANDLE *phSemaphore
+ );
+
+}
+
+#endif //_PAL_SEMAPHORE_H_
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pal/src/include/pal/sharedmemory.h b/src/pal/src/include/pal/sharedmemory.h
new file mode 100644
index 0000000000..45cc4b2c8d
--- /dev/null
+++ b/src/pal/src/include/pal/sharedmemory.h
@@ -0,0 +1,268 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef _PAL_SHARED_MEMORY_H_
+#define _PAL_SHARED_MEMORY_H_
+
+#include "corunix.hpp"
+
+#ifndef static_assert_no_msg
+#define static_assert_no_msg( cond ) static_assert( cond, #cond )
+#endif // !static_assert_no_msg
+
+#ifndef _countof
+#define _countof(a) (sizeof(a) / sizeof(a[0]))
+#endif // !_countof
+
+// - Global shared memory files go in:
+// /tmp/.dotnet/shm/global/<fileName>
+// - Session-scoped shared memory files go in:
+// /tmp/.dotnet/shm/session<sessionId>/<fileName>
+// - Lock files associated with global shared memory files go in:
+// /tmp/.dotnet/lockfiles/global/<fileName>
+// - Lock files associated with session-scoped shared memory files go in:
+// /tmp/.dotnet/lockfiles/session<sessionId>/<fileName>
+
+#define SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT (_MAX_FNAME - 1)
+#define SHARED_MEMORY_MAX_NAME_CHAR_COUNT (_countof("Global\\") - 1 + SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT)
+
+#define SHARED_MEMORY_TEMP_DIRECTORY_PATH "/tmp"
+#define SHARED_MEMORY_RUNTIME_TEMP_DIRECTORY_PATH "/tmp/.dotnet"
+
+#define SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH "/tmp/.dotnet/shm"
+#define SHARED_MEMORY_LOCK_FILES_DIRECTORY_PATH "/tmp/.dotnet/lockfiles"
+static_assert_no_msg(_countof(SHARED_MEMORY_LOCK_FILES_DIRECTORY_PATH) >= _countof(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH));
+
+#define SHARED_MEMORY_GLOBAL_DIRECTORY_NAME "global"
+#define SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX "session"
+static_assert_no_msg(_countof(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) >= _countof(SHARED_MEMORY_GLOBAL_DIRECTORY_NAME));
+
+#define SHARED_MEMORY_UNIQUE_TEMP_NAME_TEMPLATE "/tmp/.coreclr.XXXXXX"
+
+#define SHARED_MEMORY_MAX_SESSION_ID_CHAR_COUNT (10)
+
+#define SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT \
+ ( \
+ _countof(SHARED_MEMORY_LOCK_FILES_DIRECTORY_PATH) - 1 + \
+ 1 /* path separator */ + \
+ _countof(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) - 1 + \
+ SHARED_MEMORY_MAX_SESSION_ID_CHAR_COUNT + \
+ 1 /* path separator */ + \
+ SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT \
+ )
+static_assert_no_msg(SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ <= MAX_LONGPATH);
+
+class AutoFreeBuffer
+{
+private:
+ void *m_buffer;
+ bool m_cancel;
+
+public:
+ AutoFreeBuffer(void *buffer);
+ ~AutoFreeBuffer();
+
+public:
+ void Cancel();
+};
+
+enum class SharedMemoryError : DWORD
+{
+ NameEmpty = ERROR_INVALID_PARAMETER,
+ NameTooLong = ERROR_FILENAME_EXCED_RANGE,
+ NameInvalid = ERROR_INVALID_NAME,
+ HeaderMismatch = ERROR_INVALID_HANDLE,
+ OutOfMemory = ERROR_NOT_ENOUGH_MEMORY,
+ IO = ERROR_OPEN_FAILED
+};
+
+class SharedMemoryException
+{
+private:
+ DWORD m_errorCode;
+
+public:
+ SharedMemoryException(DWORD errorCode);
+ DWORD GetErrorCode() const;
+};
+
+class SharedMemoryHelpers
+{
+private:
+ static const mode_t PermissionsMask_AllUsers_ReadWrite;
+ static const mode_t PermissionsMask_AllUsers_ReadWriteExecute;
+public:
+ static const UINT32 InvalidProcessId;
+ static const SIZE_T InvalidThreadId;
+ static const UINT64 InvalidSharedThreadId;
+
+public:
+ static SIZE_T AlignDown(SIZE_T value, SIZE_T alignment);
+ static SIZE_T AlignUp(SIZE_T value, SIZE_T alignment);
+
+ static void *Alloc(SIZE_T byteCount);
+
+ template<SIZE_T DestinationByteCount, SIZE_T SourceByteCount> static SIZE_T CopyString(char (&destination)[DestinationByteCount], SIZE_T destinationStartOffset, const char (&source)[SourceByteCount]);
+ template<SIZE_T DestinationByteCount> static SIZE_T CopyString(char (&destination)[DestinationByteCount], SIZE_T destinationStartOffset, LPCSTR source, SIZE_T sourceCharCount);
+ template<SIZE_T DestinationByteCount> static SIZE_T AppendUInt32String(char (&destination)[DestinationByteCount], SIZE_T destinationStartOffset, UINT32 value);
+
+ static bool EnsureDirectoryExists(const char *path, bool isGlobalLockAcquired, bool createIfNotExist = true);
+private:
+ static int Open(LPCSTR path, int flags, mode_t mode = static_cast<mode_t>(0));
+public:
+ static int OpenDirectory(LPCSTR path);
+ static int CreateOrOpenFile(LPCSTR path, bool createIfNotExist = true, bool *createdRef = nullptr);
+ static void CloseFile(int fileDescriptor);
+
+ static SIZE_T GetFileSize(int fileDescriptor);
+ static void SetFileSize(int fileDescriptor, SIZE_T byteCount);
+
+ static void *MemoryMapFile(int fileDescriptor, SIZE_T byteCount);
+
+ static bool TryAcquireFileLock(int fileDescriptor, int operation);
+ static void ReleaseFileLock(int fileDescriptor);
+};
+
+class SharedMemoryId
+{
+private:
+ LPCSTR m_name;
+ SIZE_T m_nameCharCount;
+ bool m_isSessionScope; // false indicates global scope
+
+public:
+ SharedMemoryId();
+ SharedMemoryId(LPCSTR name, SIZE_T nameCharCount, bool isSessionScope);
+ SharedMemoryId(LPCSTR name);
+
+public:
+ LPCSTR GetName() const;
+ SIZE_T GetNameCharCount() const;
+ bool IsSessionScope() const;
+ bool Equals(SharedMemoryId *other) const;
+
+public:
+ SIZE_T AppendSessionDirectoryName(char (&path)[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1], SIZE_T pathCharCount) const;
+};
+
+enum class SharedMemoryType : UINT8
+{
+ Mutex
+};
+
+class SharedMemorySharedDataHeader
+{
+private:
+ union
+ {
+ struct
+ {
+ SharedMemoryType m_type;
+ UINT8 m_version;
+ };
+ UINT64 _raw; // use the same size for the header on all archs, and align the data to a pointer
+ };
+
+public:
+ static SIZE_T DetermineTotalByteCount(SIZE_T dataByteCount);
+
+public:
+ SharedMemorySharedDataHeader(SharedMemoryType type, UINT8 version);
+
+public:
+ SharedMemoryType GetType() const;
+ UINT8 GetVersion() const;
+ void *GetData();
+};
+
+class SharedMemoryProcessDataBase
+{
+public:
+ virtual void Close(bool isAbruptShutdown, bool releaseSharedData)
+ {
+ }
+
+ virtual ~SharedMemoryProcessDataBase()
+ {
+ }
+};
+
+class SharedMemoryProcessDataHeader
+{
+private:
+ SIZE_T m_refCount;
+ SharedMemoryId m_id;
+ SharedMemoryProcessDataBase *m_data;
+ int m_fileDescriptor;
+ SharedMemorySharedDataHeader *m_sharedDataHeader;
+ SIZE_T m_sharedDataTotalByteCount;
+ SharedMemoryProcessDataHeader *m_nextInProcessDataHeaderList;
+
+public:
+ static SharedMemoryProcessDataHeader *CreateOrOpen(LPCSTR name, SharedMemorySharedDataHeader requiredSharedDataHeader, SIZE_T sharedDataByteCount, bool createIfNotExist, bool *createdRef);
+
+public:
+ static SharedMemoryProcessDataHeader *PalObject_GetProcessDataHeader(CorUnix::IPalObject *object);
+ static void PalObject_SetProcessDataHeader(CorUnix::IPalObject *object, SharedMemoryProcessDataHeader *processDataHeader);
+ static void PalObject_Close(CorUnix::CPalThread *thread, CorUnix::IPalObject *object, bool isShuttingDown, bool cleanUpPalSharedState);
+
+private:
+ SharedMemoryProcessDataHeader(SharedMemoryId *id, int fileDescriptor, SharedMemorySharedDataHeader *sharedDataHeader, SIZE_T sharedDataTotalByteCount);
+public:
+ static SharedMemoryProcessDataHeader *New(SharedMemoryId *id, int fileDescriptor, SharedMemorySharedDataHeader *sharedDataHeader, SIZE_T sharedDataTotalByteCount);
+ ~SharedMemoryProcessDataHeader();
+ void Close();
+
+public:
+ SharedMemoryId *GetId();
+ SharedMemoryProcessDataBase *GetData() const;
+ void SetData(SharedMemoryProcessDataBase *data);
+ SharedMemorySharedDataHeader *GetSharedDataHeader() const;
+ SIZE_T GetSharedDataTotalByteCount() const;
+ SharedMemoryProcessDataHeader *GetNextInProcessDataHeaderList() const;
+ void SetNextInProcessDataHeaderList(SharedMemoryProcessDataHeader *next);
+
+public:
+ void IncRefCount();
+ void DecRefCount();
+};
+
+class SharedMemoryManager
+{
+private:
+ static CRITICAL_SECTION s_creationDeletionProcessLock;
+ static int s_creationDeletionLockFileDescriptor;
+
+private:
+ static SharedMemoryProcessDataHeader *s_processDataHeaderListHead;
+
+#ifdef _DEBUG
+private:
+ static SIZE_T s_creationDeletionProcessLockOwnerThreadId;
+ static SIZE_T s_creationDeletionFileLockOwnerThreadId;
+#endif // _DEBUG
+
+public:
+ static void StaticInitialize();
+ static void StaticClose();
+
+public:
+ static void AcquireCreationDeletionProcessLock();
+ static void ReleaseCreationDeletionProcessLock();
+ static void AcquireCreationDeletionFileLock();
+ static void ReleaseCreationDeletionFileLock();
+
+#ifdef _DEBUG
+public:
+ static bool IsCreationDeletionProcessLockAcquired();
+ static bool IsCreationDeletionFileLockAcquired();
+#endif // _DEBUG
+
+public:
+ static void AddProcessDataHeader(SharedMemoryProcessDataHeader *processDataHeader);
+ static void RemoveProcessDataHeader(SharedMemoryProcessDataHeader *processDataHeader);
+ static SharedMemoryProcessDataHeader *FindProcessDataHeader(SharedMemoryId *id);
+};
+
+#endif // !_PAL_SHARED_MEMORY_H_
diff --git a/src/pal/src/include/pal/sharedmemory.inl b/src/pal/src/include/pal/sharedmemory.inl
new file mode 100644
index 0000000000..69b8704b65
--- /dev/null
+++ b/src/pal/src/include/pal/sharedmemory.inl
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef _PAL_SHARED_MEMORY_INL_
+#define _PAL_SHARED_MEMORY_INL_
+
+#include "sharedmemory.h"
+
+#include "dbgmsg.h"
+
+#include <string.h>
+
+template<SIZE_T DestinationByteCount, SIZE_T SourceByteCount>
+SIZE_T SharedMemoryHelpers::CopyString(
+ char (&destination)[DestinationByteCount],
+ SIZE_T destinationStartOffset,
+ const char(&source)[SourceByteCount])
+{
+ return CopyString(destination, destinationStartOffset, source, SourceByteCount - 1);
+}
+
+template<SIZE_T DestinationByteCount>
+SIZE_T SharedMemoryHelpers::CopyString(
+ char (&destination)[DestinationByteCount],
+ SIZE_T destinationStartOffset,
+ LPCSTR source,
+ SIZE_T sourceCharCount)
+{
+ _ASSERTE(destinationStartOffset < DestinationByteCount);
+ _ASSERTE(sourceCharCount < DestinationByteCount - destinationStartOffset);
+ _ASSERTE(strlen(source) == sourceCharCount);
+
+ memcpy_s(&destination[destinationStartOffset], DestinationByteCount - destinationStartOffset, source, sourceCharCount + 1);
+ return destinationStartOffset + sourceCharCount;
+}
+
+template<SIZE_T DestinationByteCount>
+SIZE_T SharedMemoryHelpers::AppendUInt32String(
+ char (&destination)[DestinationByteCount],
+ SIZE_T destinationStartOffset,
+ UINT32 value)
+{
+ _ASSERTE(destination != nullptr);
+ _ASSERTE(destinationStartOffset < DestinationByteCount);
+
+ int valueCharCount =
+ sprintf_s(&destination[destinationStartOffset], DestinationByteCount - destinationStartOffset, "%u", value);
+ _ASSERTE(valueCharCount > 0);
+ return destinationStartOffset + valueCharCount;
+}
+
+#endif // !_PAL_SHARED_MEMORY_INL_
diff --git a/src/pal/src/include/pal/shm.hpp b/src/pal/src/include/pal/shm.hpp
new file mode 100644
index 0000000000..de1d09e636
--- /dev/null
+++ b/src/pal/src/include/pal/shm.hpp
@@ -0,0 +1,83 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/shm.hpp
+
+Abstract:
+ C++ typesafe accessors for shared memory routines
+
+
+
+--*/
+
+#ifndef _SHM_HPP_
+#define _SHM_HPP_
+
+#include "shmemory.h"
+
+//
+// Some compilers (e.g., HPUX/IA64) warn about using NULL to initialize
+// something of type SHMPTR, since SHMPTR is defined as DWORD_PTR, which
+// isn't considered a pointer type...
+//
+
+#define SHMNULL 0
+
+#ifndef _DEBUG
+
+inline
+void *
+ShmPtrToPtrFast(SHMPTR shmptr)
+{
+ void *pv = NULL;
+
+ if (SHMNULL != shmptr)
+ {
+ int segment = shmptr >> 24;
+
+ if (segment < shm_numsegments)
+ {
+ pv = reinterpret_cast<void*>(
+ reinterpret_cast<DWORD_PTR>(shm_segment_bases[(uint)segment].Load())
+ + (shmptr & 0x00FFFFFF)
+ );
+ }
+ else
+ {
+ pv = SHMPtrToPtr(shmptr);
+ }
+ }
+
+ return pv;
+}
+
+//
+// We could use a function template here to avoid the cast / macro
+//
+
+#define SHMPTR_TO_TYPED_PTR(type, shmptr) reinterpret_cast<type*>(ShmPtrToPtrFast((shmptr)))
+
+#else
+
+#define SHMPTR_TO_TYPED_PTR(type, shmptr) reinterpret_cast<type*>(SHMPtrToPtr((shmptr)))
+
+#endif
+
+/* Set ptr to NULL if shmPtr == 0, else set ptr to SHMPTR_TO_TYPED_PTR(type, shmptr)
+ return FALSE if SHMPTR_TO_TYPED_PTR returns NULL ptr from non null shmptr,
+ TRUE otherwise */
+#define SHMPTR_TO_TYPED_PTR_BOOL(type, ptr, shmptr) \
+ ((shmptr != 0) ? ((ptr = SHMPTR_TO_TYPED_PTR(type, shmptr)) != NULL) : ((ptr = NULL) == NULL))
+
+
+
+
+#endif // _SHM_HPP_
+
diff --git a/src/pal/src/include/pal/shmemory.h b/src/pal/src/include/pal/shmemory.h
new file mode 100644
index 0000000000..5ca848148c
--- /dev/null
+++ b/src/pal/src/include/pal/shmemory.h
@@ -0,0 +1,331 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/shmemory.h
+
+Abstract:
+ Header file for interface to shared memory
+
+How to use :
+
+The SHMalloc function can be used to allocate memory in the shared memory area.
+It returns a value of type SHMPTR, which will be useable in all participating
+processes. The SHMPTR_TO_PTR macro can be used to convert a SHMPTR value into
+an address valid *only* within the current process. Do NOT store pointers in
+shared memory, since those will not be valid for other processes. If you need
+to construct linked lists or other strctures that usually use pointers, use
+SHMPTR values instead of pointers. In addition, Lock/Release functions must be
+used when manipulating data in shared memory, to ensure inter-process synchronization.
+
+Example :
+
+//a simple linked list type
+typedef struct
+{
+int count;
+SHMPTR string;
+SHMPTR next;
+}SHMLIST;
+
+// Allocate a new list item
+SHMPTR new_item = SHMalloc(sizeof(SHMLIST));
+
+// get a pointer to it
+SHMLIST *item_ptr = (SHMLIST *)SHMPTR_TO_PTR(new_item);
+
+// Allocate memory for the "string" member, initialize it
+item_ptr->string = SHMalloc(strlen("string"));
+LPSTR str_ptr = (LPSTR)SHMPTR_TO_PTR(item_ptr->string);
+strcpy(str_ptr, "string");
+
+//Take the shared memory lock to prevent anyone from modifying the linked list
+SHMLock();
+
+//get the list's head from somewhere
+SHMPTR list_head = get_list_head();
+
+//link the list to our new item
+item_ptr->next = list_head
+
+//get a pointer to the list head's structure
+SHMLIST *head_ptr = (SHMLIST *)SHMPTR_TO_PTR(list_head);
+
+//set the new item's count value based on the head's count value
+item_ptr->count = head_ptr->count + 1;
+
+//save the new item as the new head of the list
+set_list_head(new_item);
+
+//We're done modifying the list, release the lock
+SHMRelease
+
+
+
+--*/
+
+#ifndef _PAL_SHMEMORY_H_
+#define _PAL_SHMEMORY_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/*
+Type for shared memory blocks. use SHMPTR_TO_PTR to get a useable address.
+ */
+typedef DWORD_PTR SHMPTR;
+
+#define MAX_SEGMENTS 256
+
+
+typedef enum {
+ SIID_PROCESS_INFO,/* pointers to PROCESS structures? */
+ SIID_NAMED_OBJECTS,
+ SIID_FILE_LOCKS,
+
+ SIID_LAST
+} SHM_INFO_ID;
+
+typedef enum
+{
+ SHM_NAMED_MAPPINGS, /* structs with map name, file name & flags? */
+ SHM_NAMED_EVENTS, /* structs with event names & ThreadWaitingList struct? */
+ SHM_NAMED_MUTEXS, /* structs with mutext names, and ThreadWaitingList struct */
+
+ SHM_NAMED_LAST
+} SHM_NAMED_OBJECTS_ID;
+
+typedef struct _SMNO
+{
+ SHM_NAMED_OBJECTS_ID ObjectType;
+ SHMPTR ShmNext;
+ SHMPTR ShmObjectName;
+ SHMPTR ShmSelf;
+
+}SHM_NAMED_OBJECTS, * PSHM_NAMED_OBJECTS;
+
+
+/*
+SHMPTR_TO_PTR
+
+Macro to convert a SHMPTR value into a valid (for this process) pointer.
+
+In debug builds, we always call the function to do full checks.
+In release builds, check if the segment is known, and if it is, do only minimal
+validation (if segment is unknown, we have to call the function)
+ */
+#if _DEBUG
+
+#define SHMPTR_TO_PTR(shmptr) \
+ SHMPtrToPtr(shmptr)
+
+#else /* !_DEBUG */
+
+extern int shm_numsegments;
+
+/* array containing the base address of each segment */
+extern Volatile<LPVOID> shm_segment_bases[MAX_SEGMENTS];
+
+#define SHMPTR_TO_PTR(shmptr)\
+ ((shmptr)?(((static_cast<int>(shmptr)>>24)<shm_numsegments)?\
+ reinterpret_cast<LPVOID>(reinterpret_cast<size_t>(shm_segment_bases[static_cast<int>(shmptr)>>24].Load())+(static_cast<int>(shmptr)&0x00FFFFFF)):\
+ SHMPtrToPtr(shmptr)): static_cast<LPVOID>(NULL))
+
+
+#endif /* _DEBUG */
+
+/* Set ptr to NULL if shmPtr == 0, else set ptr to SHMPTR_TO_PTR(shmptr)
+ return FALSE if SHMPTR_TO_PTR returns NULL ptr from non null shmptr,
+ TRUE otherwise */
+#define SHMPTR_TO_PTR_BOOL(ptr, shmptr) \
+ ((shmptr != 0) ? ((ptr = SHMPTR_TO_PTR(shmptr)) != NULL) : ((ptr = NULL) == NULL))
+
+/*++
+SHMPtrToPtr
+
+Convert a SHMPTR value into a useable pointer.
+
+Unlike the macro defined above, this function performs as much validation as
+possible, and can handle cases when the SHMPTR is located in an aread of shared
+memory the process doesn't yet know about.
+--*/
+LPVOID SHMPtrToPtr(SHMPTR shmptr);
+
+/*++
+SHMInitialize
+
+Hook this process into the PAL shared memory system; initialize the shared
+memory if no other process has done it.
+--*/
+BOOL SHMInitialize(void);
+
+/*++
+SHMCleanup
+
+Release all shared memory resources held; remove ourselves from the list of
+registered processes, and remove all shared memory files if no process remains
+--*/
+void SHMCleanup(void);
+
+/*++
+SHMalloc
+
+Allocate a block of memory of the specified size
+
+Parameters :
+ size_t size : size of block required
+
+Return value :
+ A SHMPTR identifying the new block, or 0 on failure. Use SHMPtrToPtr to
+ convert a SHMPTR into a useable pointer (but remember to lock the shared
+ memory first!)
+
+Notes :
+ SHMalloc will fail if the requested size is larger than a certain maximum.
+ At the moment, the maximum is 520 bytes (MAX_PATH_FNAME*2).
+--*/
+SHMPTR SHMalloc(size_t size);
+
+/*++
+SHMfree
+
+Release a block of shared memory and put it back in the shared memory pool
+
+Parameters :
+ SHMPTR shmptr : identifier of block to release
+
+(no return value)
+--*/
+void SHMfree(SHMPTR shmptr);
+
+/*++
+SHMLock
+
+Restrict shared memory access to the current thread of the current process
+
+(no parameters)
+
+Return value :
+ New lock count
+--*/
+int SHMLock(void);
+
+/*++
+SHMRelease
+
+Release a lock on shared memory taken with SHMLock.
+
+(no parameters)
+
+Return value :
+ New lock count
+--*/
+int SHMRelease(void);
+
+
+/*++
+Function :
+ SHMGetInfo
+
+ Retrieve some information from shared memory
+
+Parameters :
+ SHM_INFO_ID element : identifier of element to retrieve
+
+Return value :
+ Value of specified element
+
+Notes :
+ The SHM lock should be held while manipulating shared memory
+--*/
+SHMPTR SHMGetInfo(SHM_INFO_ID element);
+
+/*++
+Function :
+ SHMSetInfo
+
+ Place some information into shared memory
+
+Parameters :
+ SHM_INFO_ID element : identifier of element to save
+ SHMPTR value : new value of element
+
+Return value :
+ TRUE if successfull, FALSE otherwise.
+
+Notes :
+ The SHM lock should be held while manipulating shared memory
+--*/
+BOOL SHMSetInfo(SHM_INFO_ID element, SHMPTR value);
+
+
+/********************** Shared memory help functions ********************/
+
+/*++
+SHMStrDup
+
+Duplicates the string in shared memory.
+
+Returns the new address as SHMPTR on success.
+Returns (SHMPTR)NULL on failure.
+--*/
+SHMPTR SHMStrDup( LPCSTR string );
+
+/*++
+SHMWStrDup
+
+Duplicates the wide string in shared memory.
+
+Returns the new address as SHMPTR on success.
+Returns (SHMPTR)NULL on failure.
+--*/
+SHMPTR SHMWStrDup( LPCWSTR string );
+
+
+/*++
+SHMFindNamedObjectByName
+
+Searches for an object whose name matches the name and ID passed in.
+
+Returns a SHMPTR to its location in shared memory. If no object
+matches the name, the function returns NULL and sets pbNameExists to FALSE.
+If an object matches the name but is of a different type, the function
+returns NULL and sets pbNameExists to TRUE.
+
+--*/
+SHMPTR SHMFindNamedObjectByName( LPCWSTR lpName, SHM_NAMED_OBJECTS_ID oid,
+ BOOL *pbNameExists );
+
+/*++
+SHMRemoveNamedObject
+
+Removes the specified named object from the list
+
+No return.
+
+note : the caller is reponsible for releasing all associated memory
+--*/
+void SHMRemoveNamedObject( SHMPTR shmNamedObject );
+
+/*++ SHMAddNamedObject
+
+Adds the specified named object to the list.
+
+No return.
+--*/
+void SHMAddNamedObject( SHMPTR shmNewNamedObject );
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_SHMEMORY_H_ */
+
diff --git a/src/pal/src/include/pal/stackstring.hpp b/src/pal/src/include/pal/stackstring.hpp
new file mode 100644
index 0000000000..1f18d5fe03
--- /dev/null
+++ b/src/pal/src/include/pal/stackstring.hpp
@@ -0,0 +1,239 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __STACKSTRING_H_
+#define __STACKSTRING_H_
+
+template <SIZE_T STACKCOUNT, class T>
+class StackString
+{
+private:
+ T m_innerBuffer[STACKCOUNT + 1];
+ T * m_buffer;
+ SIZE_T m_size; // actual allocated size
+ SIZE_T m_count; // actual length of string
+
+ void NullTerminate()
+ {
+ m_buffer[m_count] = 0;
+ }
+
+ void DeleteBuffer()
+ {
+ if (m_innerBuffer != m_buffer)
+ PAL_free(m_buffer);
+
+ m_buffer = NULL;
+ return;
+ }
+
+ BOOL ReallocateBuffer(SIZE_T count)
+ {
+ // count is always > STACKCOUNT here.
+ // We got so far, we will allocate a little extra
+ // to prevent frequent allocations
+#if _DEBUG
+ SIZE_T count_allocated = count;
+#else
+ SIZE_T count_allocated = count + 100;
+#endif //_DEBUG
+
+ BOOL dataOnStack = m_buffer == m_innerBuffer;
+ if( dataOnStack )
+ {
+ m_buffer = NULL;
+ }
+
+ T * newBuffer = (T *)PAL_realloc(m_buffer, (count_allocated + 1) * sizeof(T));
+ if (NULL == newBuffer)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+
+ DeleteBuffer();
+ m_count = 0;
+ m_buffer = m_innerBuffer;
+ return FALSE;
+ }
+
+ if( dataOnStack)
+ {
+ CopyMemory(newBuffer, m_innerBuffer, (m_count + 1) * sizeof(T));
+ }
+
+ m_buffer = newBuffer;
+ m_count = count;
+ m_size = count_allocated + 1;
+
+ return TRUE;
+ }
+
+ BOOL HasAvailableMemory(SIZE_T count)
+ {
+ return (count < m_size);
+ }
+
+ //NOTE: Always call this before modifying the underlying buffer
+ BOOL Resize(SIZE_T count)
+ {
+
+ if (NULL == m_buffer)
+ {
+ m_buffer = m_innerBuffer;
+ }
+
+ if (HasAvailableMemory(count))
+ {
+ m_count = count;
+ }
+ else
+ {
+ if (count > STACKCOUNT)
+ {
+ return ReallocateBuffer(count);
+ }
+ else
+ {
+ m_count = count;
+ m_size = STACKCOUNT+1;
+ }
+ }
+
+ return TRUE;
+ }
+
+ StackString(const StackString &s)
+ {
+ Set(s);
+ }
+
+public:
+ StackString()
+ : m_buffer(m_innerBuffer), m_size(0), m_count(0)
+ {
+ }
+
+
+ BOOL Set(const T * buffer, SIZE_T count)
+ {
+ if (!Resize(count))
+ return FALSE;
+
+ CopyMemory(m_buffer, buffer, (count + 1) * sizeof(T));
+ NullTerminate();
+ return TRUE;
+ }
+
+ BOOL Set(const StackString &s)
+ {
+ return Set(s.m_buffer, s.m_count);
+ }
+
+ SIZE_T GetCount() const
+ {
+ return m_count;
+ }
+
+ SIZE_T GetSizeOf() const
+ {
+ return m_size * sizeof(T);
+ }
+
+ CONST T * GetString() const
+ {
+ return (const T *)m_buffer;
+ }
+
+ operator const T * () const { return GetString(); }
+
+ //Always preserves the existing content
+ T * OpenStringBuffer(SIZE_T count)
+ {
+ T * result = NULL;
+ if (Resize(count))
+ {
+ result = (T *)m_buffer;
+ }
+ return result;
+ }
+
+ //count should not include the terminating null
+ void CloseBuffer(SIZE_T count)
+ {
+ if (m_count > count)
+ m_count = count;
+
+ NullTerminate();
+ return;
+ }
+
+ //Call this with the best estimate if you want to
+ //prevent possible reallocations on further operations
+ BOOL Reserve(SIZE_T count)
+ {
+ SIZE_T endpos = m_count;
+
+ if (!Resize(count))
+ return FALSE;
+
+ m_count = endpos;
+ NullTerminate();
+
+ return TRUE;
+ }
+
+ //count Should not include the terminating null
+ BOOL Append(const T * buffer, SIZE_T count)
+ {
+ SIZE_T endpos = m_count;
+ if (!Resize(m_count + count))
+ return FALSE;
+
+ CopyMemory(&m_buffer[endpos], buffer, (count + 1) * sizeof(T));
+ NullTerminate();
+ return TRUE;
+ }
+
+ BOOL Append(const StackString &s)
+ {
+ return Append(s.GetString(), s.GetCount());
+ }
+
+ BOOL IsEmpty()
+ {
+ return 0 == m_buffer[0];
+ }
+
+ void Clear()
+ {
+ m_count = 0;
+ NullTerminate();
+ }
+ ~StackString()
+ {
+ DeleteBuffer();
+ }
+};
+
+#if _DEBUG
+typedef StackString<32, CHAR> PathCharString;
+typedef StackString<32, WCHAR> PathWCharString;
+#else
+typedef StackString<260, CHAR> PathCharString;
+typedef StackString<260, WCHAR> PathWCharString;
+#endif
+#endif
+
+// Some Helper Definitions
+BOOL
+PAL_GetPALDirectoryW(
+ PathWCharString& lpDirectoryName);
+BOOL
+PAL_GetPALDirectoryA(
+ PathCharString& lpDirectoryName);
+DWORD
+GetCurrentDirectoryA(
+ PathCharString& lpBuffer);
+void
+FILEDosToUnixPathA(
+ PathCharString& lpPath);
diff --git a/src/pal/src/include/pal/synchcache.hpp b/src/pal/src/include/pal/synchcache.hpp
new file mode 100644
index 0000000000..c172842292
--- /dev/null
+++ b/src/pal/src/include/pal/synchcache.hpp
@@ -0,0 +1,397 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/synchcache.hpp
+
+Abstract:
+ Simple look-aside cache for unused objects with default
+ constructor or no constructor
+
+
+
+--*/
+
+#ifndef _SYNCH_CACHE_H_
+#define _SYNCH_CACHE_H_
+
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+
+namespace CorUnix
+{
+ template <typename T> class CSynchCache
+ {
+ typedef union _USynchCacheStackNode
+ {
+ union _USynchCacheStackNode * next;
+ BYTE objraw[sizeof(T)];
+ } USynchCacheStackNode;
+
+ static const int MaxDepth = 256;
+
+ Volatile<USynchCacheStackNode*> m_pHead;
+ CRITICAL_SECTION m_cs;
+ Volatile<int> m_iDepth;
+ int m_iMaxDepth;
+#ifdef _DEBUG
+ int m_iMaxTrackedDepth;
+#endif
+
+ void Lock(CPalThread * pthrCurrent)
+ { InternalEnterCriticalSection(pthrCurrent, &m_cs); }
+ void Unlock(CPalThread * pthrCurrent)
+ { InternalLeaveCriticalSection(pthrCurrent, &m_cs); }
+
+ public:
+ CSynchCache(int iMaxDepth = MaxDepth) :
+ m_pHead(NULL),
+ m_iDepth(0),
+ m_iMaxDepth(iMaxDepth)
+#ifdef _DEBUG
+ ,m_iMaxTrackedDepth(0)
+#endif
+ {
+ InternalInitializeCriticalSection(&m_cs);
+ if (m_iMaxDepth < 0)
+ {
+ m_iMaxDepth = 0;
+ }
+ }
+
+ ~CSynchCache()
+ {
+ Flush(NULL, true);
+ InternalDeleteCriticalSection(&m_cs);
+ }
+
+#ifdef _DEBUG
+ int GetMaxTrackedDepth() { return m_iMaxTrackedDepth; }
+#endif
+
+ T * Get(CPalThread * pthrCurrent)
+ {
+ T * pObj = NULL;
+
+ Get(pthrCurrent, 1, &pObj);
+ return pObj;
+ }
+
+ int Get(CPalThread * pthrCurrent, int n, T ** ppObjs)
+ {
+ void * pvObjRaw;
+ USynchCacheStackNode * pNode;
+ int i = 0,j;
+
+ Lock(pthrCurrent);
+ pNode = m_pHead;
+ while (pNode && i < n)
+ {
+ ppObjs[i] = (T *)pNode;
+ pNode = pNode->next;
+ i++;
+ }
+ m_pHead = pNode;
+ m_iDepth -= i;
+
+#ifdef _DEBUG
+ if (NULL == m_pHead && m_iDepth != 0)
+ {
+ // Can't use ASSERT here, since this is header
+ // is included by other headers with inline methods
+ // which causes template instatiation in the header
+ // where the DEBUG CHANNEL is not defined and cannot
+ // be defined
+ fprintf(stderr,"SYNCCACHE: Invalid cache depth value");
+ DebugBreak();
+ }
+#endif // _DEBUG
+
+ Unlock(pthrCurrent);
+
+ for (j=i;j<n;j++)
+ {
+ pvObjRaw = (void *) InternalNew<USynchCacheStackNode>();
+ if (NULL == pvObjRaw)
+ break;
+#ifdef _DEBUG
+ memset(pvObjRaw, 0, sizeof(USynchCacheStackNode));
+#endif
+ ppObjs[j] = reinterpret_cast<T*>(pvObjRaw);
+ }
+
+ for (i=0;i<j;i++)
+ {
+ new ((void *)ppObjs[i]) T;
+ }
+
+ return j;
+ }
+
+ void Add(CPalThread * pthrCurrent, T * pobj)
+ {
+ USynchCacheStackNode * pNode = reinterpret_cast<USynchCacheStackNode *>(pobj);
+
+ if (NULL == pobj)
+ {
+ return;
+ }
+
+ pobj->~T();
+
+ Lock(pthrCurrent);
+ if (m_iDepth < m_iMaxDepth)
+ {
+#ifdef _DEBUG
+ if (m_iDepth > m_iMaxTrackedDepth)
+ {
+ m_iMaxTrackedDepth = m_iDepth;
+ }
+#endif
+ pNode->next = m_pHead;
+ m_pHead = pNode;
+ m_iDepth++;
+ }
+ else
+ {
+ InternalDelete((char *)pNode);
+ }
+ Unlock(pthrCurrent);
+ }
+
+ void Flush(CPalThread * pthrCurrent, bool fDontLock = false)
+ {
+ USynchCacheStackNode * pNode, * pTemp;
+
+ if (!fDontLock)
+ {
+ Lock(pthrCurrent);
+ }
+ pNode = m_pHead;
+ m_pHead = NULL;
+ m_iDepth = 0;
+ if (!fDontLock)
+ {
+ Unlock(pthrCurrent);
+ }
+
+ while (pNode)
+ {
+ pTemp = pNode;
+ pNode = pNode->next;
+ InternalDelete((char *)pTemp);
+ }
+ }
+ };
+
+ template <typename T> class CSHRSynchCache
+ {
+ union _USHRSynchCacheStackNode; // fwd declaration
+ typedef struct _SHRCachePTRs
+ {
+ union _USHRSynchCacheStackNode * pNext;
+ SharedID shrid;
+ } SHRCachePTRs;
+ typedef union _USHRSynchCacheStackNode
+ {
+ SHRCachePTRs pointers;
+ BYTE objraw[sizeof(T)];
+ } USHRSynchCacheStackNode;
+
+ static const int MaxDepth = 256;
+ static const int PreAllocFactor = 10; // Everytime a Get finds no available
+ // cached raw intances, it preallocates
+ // MaxDepth/PreAllocFactor new raw
+ // instances and store them into the
+ // cache before continuing
+
+ Volatile<USHRSynchCacheStackNode*> m_pHead;
+ CRITICAL_SECTION m_cs;
+ Volatile<int> m_iDepth;
+ int m_iMaxDepth;
+#ifdef _DEBUG
+ int m_iMaxTrackedDepth;
+#endif
+
+ void Lock(CPalThread * pthrCurrent)
+ { InternalEnterCriticalSection(pthrCurrent, &m_cs); }
+ void Unlock(CPalThread * pthrCurrent)
+ { InternalLeaveCriticalSection(pthrCurrent, &m_cs); }
+
+ public:
+ CSHRSynchCache(int iMaxDepth = MaxDepth) :
+ m_pHead(NULL),
+ m_iDepth(0),
+ m_iMaxDepth(iMaxDepth)
+#ifdef _DEBUG
+ ,m_iMaxTrackedDepth(0)
+#endif
+ {
+ InternalInitializeCriticalSection(&m_cs);
+ if (m_iMaxDepth < 0)
+ {
+ m_iMaxDepth = 0;
+ }
+ }
+
+ ~CSHRSynchCache()
+ {
+ Flush(NULL, true);
+ InternalDeleteCriticalSection(&m_cs);
+ }
+
+#ifdef _DEBUG
+ int GetMaxTrackedDepth() { return m_iMaxTrackedDepth; }
+#endif
+
+ SharedID Get(CPalThread * pthrCurrent)
+ {
+ SharedID shridObj = NULLSharedID;
+
+ Get(pthrCurrent, 1, &shridObj);
+ return shridObj;
+ }
+
+ int Get(CPalThread * pthrCurrent, int n, SharedID * shridpObjs)
+ {
+ SharedID shridObj;
+ void * pvObjRaw = NULL;
+ USHRSynchCacheStackNode * pNode;
+ int i = 0, j, k;
+
+ Lock(pthrCurrent);
+ pNode = m_pHead;
+ while (pNode && i < n)
+ {
+ shridpObjs[i] = pNode->pointers.shrid;
+ pvObjRaw = (void *)pNode;
+ pNode = pNode->pointers.pNext;
+ i++;
+ }
+ m_pHead = pNode;
+ m_iDepth -= i;
+
+#ifdef _DEBUG
+ if (NULL == m_pHead && m_iDepth != 0)
+ {
+ // Can't use ASSERT here, since this is header
+ // (see comment above)
+ fprintf(stderr,"SYNCCACHE: Invalid cache depth value");
+ DebugBreak();
+ }
+#endif // _DEBUG
+
+ if (0 == m_iDepth)
+ {
+ for (k=0; k<m_iMaxDepth/PreAllocFactor-n+i; k++)
+ {
+ shridObj = RawSharedObjectAlloc(sizeof(USHRSynchCacheStackNode), DefaultSharedPool);
+ if (NULLSharedID == shridObj)
+ {
+ Flush(pthrCurrent, true);
+ break;
+ }
+ pNode = SharedIDToTypePointer(USHRSynchCacheStackNode, shridObj);
+#ifdef _DEBUG
+ memset(reinterpret_cast<void*>(pNode), 0, sizeof(USHRSynchCacheStackNode));
+#endif
+ pNode->pointers.shrid = shridObj;
+ pNode->pointers.pNext = m_pHead;
+ m_pHead = pNode;
+ m_iDepth++;
+ }
+ }
+
+ Unlock(pthrCurrent);
+
+ for (j=i;j<n;j++)
+ {
+ shridObj = RawSharedObjectAlloc(sizeof(USHRSynchCacheStackNode), DefaultSharedPool);
+ if (NULLSharedID == shridObj)
+ break;
+#ifdef _DEBUG
+ pvObjRaw = SharedIDToPointer(shridObj);
+ memset(pvObjRaw, 0, sizeof(USHRSynchCacheStackNode));
+#endif
+ shridpObjs[j] = shridObj;
+ }
+
+ for (i=0;i<j;i++)
+ {
+ pvObjRaw = SharedIDToPointer(shridpObjs[i]);
+ new (pvObjRaw) T;
+ }
+
+ return j;
+ }
+
+ void Add(CPalThread * pthrCurrent, SharedID shridObj)
+ {
+ if (NULLSharedID == shridObj)
+ {
+ return;
+ }
+
+ USHRSynchCacheStackNode * pNode = SharedIDToTypePointer(USHRSynchCacheStackNode, shridObj);
+ T * pObj = reinterpret_cast<T *>(pNode);
+
+ pObj->~T();
+
+ pNode->pointers.shrid = shridObj;
+
+ Lock(pthrCurrent);
+ if (m_iDepth < m_iMaxDepth)
+ {
+ m_iDepth++;
+#ifdef _DEBUG
+ if (m_iDepth > m_iMaxTrackedDepth)
+ {
+ m_iMaxTrackedDepth = m_iDepth;
+ }
+#endif
+ pNode->pointers.pNext = m_pHead;
+ m_pHead = pNode;
+ }
+ else
+ {
+ RawSharedObjectFree(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;
+ RawSharedObjectFree(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 0000000000..aa3a8f1aa6
--- /dev/null
+++ b/src/pal/src/include/pal/synchobjects.hpp
@@ -0,0 +1,216 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/synchobjects.hpp
+
+Abstract:
+ Header file for synchronization manager and controllers
+
+
+
+--*/
+
+#ifndef _SINCHOBJECTS_HPP_
+#define _SINCHOBJECTS_HPP_
+
+#include "corunix.hpp"
+#include "threadinfo.hpp"
+#include "mutex.hpp"
+#include "shm.hpp"
+#include "list.h"
+
+#include <pthread.h>
+
+#define SharedID SHMPTR
+#define SharedPoolId ULONG_PTR
+#define DefaultSharedPool ((ULONG_PTR)0)
+#define NULLSharedID ((SHMPTR)NULL)
+#define SharedIDToPointer(shID) SHMPTR_TO_TYPED_PTR(PVOID, shID)
+#define SharedIDToTypePointer(TYPE,shID) SHMPTR_TO_TYPED_PTR(TYPE, shID)
+#define RawSharedObjectAlloc(szSize, shPoolId) SHMalloc(szSize)
+#define RawSharedObjectFree(shID) SHMfree(shID)
+
+namespace CorUnix
+{
+ DWORD InternalWaitForMultipleObjectsEx(
+ CPalThread * pthrCurrent,
+ DWORD nCount,
+ CONST HANDLE *lpHandles,
+ BOOL bWaitAll,
+ DWORD dwMilliseconds,
+ BOOL bAlertable);
+
+ PAL_ERROR InternalSleepEx(
+ CPalThread * pthrCurrent,
+ DWORD dwMilliseconds,
+ BOOL bAlertable);
+
+ enum THREAD_STATE
+ {
+ TS_IDLE,
+ TS_STARTING,
+ TS_RUNNING,
+ TS_FAILED,
+ TS_DONE,
+ };
+
+ // forward declarations
+ struct _ThreadWaitInfo;
+ struct _WaitingThreadsListNode;
+ class CSynchData;
+
+ typedef struct _WaitingThreadsListNode * PWaitingThreadsListNode;
+ typedef struct _OwnedObjectsListNode * POwnedObjectsListNode;
+ typedef struct _ThreadApcInfoNode * PThreadApcInfoNode;
+
+ typedef struct _ThreadWaitInfo
+ {
+ WaitType wtWaitType;
+ WaitDomain wdWaitDomain;
+ LONG lObjCount;
+ LONG lSharedObjCount;
+ CPalThread * pthrOwner;
+ PWaitingThreadsListNode rgpWTLNodes[MAXIMUM_WAIT_OBJECTS];
+
+ _ThreadWaitInfo() : wtWaitType(SingleObject), wdWaitDomain(LocalWait),
+ lObjCount(0), lSharedObjCount(0),
+ pthrOwner(NULL) {}
+ } ThreadWaitInfo;
+
+ typedef struct _ThreadNativeWaitData
+ {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int iPred;
+ DWORD dwObjectIndex;
+ ThreadWakeupReason twrWakeupReason;
+ bool fInitialized;
+
+ _ThreadNativeWaitData() :
+ iPred(0),
+ dwObjectIndex(0),
+ twrWakeupReason(WaitSucceeded),
+ fInitialized(false)
+ {
+ }
+
+ ~_ThreadNativeWaitData();
+ } ThreadNativeWaitData;
+
+ class CThreadSynchronizationInfo : public CThreadInfoInitializer
+ {
+ friend class CPalSynchronizationManager;
+ friend class CSynchWaitController;
+
+ THREAD_STATE m_tsThreadState;
+ SharedID m_shridWaitAwakened;
+ Volatile<LONG> m_lLocalSynchLockCount;
+ Volatile<LONG> m_lSharedSynchLockCount;
+ LIST_ENTRY m_leOwnedObjsList;
+
+ CRITICAL_SECTION m_ownedNamedMutexListLock;
+ NamedMutexProcessData *m_ownedNamedMutexListHead;
+
+ 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);
+
+ void AddOwnedNamedMutex(NamedMutexProcessData *processData);
+ void RemoveOwnedNamedMutex(NamedMutexProcessData *processData);
+ NamedMutexProcessData *RemoveFirstOwnedNamedMutex();
+ bool OwnsNamedMutex(NamedMutexProcessData *processData);
+
+ // 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 CThreadApcInfo : public CThreadInfoInitializer
+ {
+ friend class CPalSynchronizationManager;
+
+ PThreadApcInfoNode m_ptainHead;
+ PThreadApcInfoNode m_ptainTail;
+
+ public:
+ CThreadApcInfo() :
+ m_ptainHead(NULL),
+ m_ptainTail(NULL)
+ {
+ }
+ };
+
+ 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
new file mode 100644
index 0000000000..e6dacd2136
--- /dev/null
+++ b/src/pal/src/include/pal/thread.hpp
@@ -0,0 +1,838 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/thread.hpp
+
+Abstract:
+ Header file for thread structures
+
+
+
+--*/
+
+#ifndef _PAL_THREAD_HPP_
+#define _PAL_THREAD_HPP_
+
+#include "corunix.hpp"
+#include "shm.hpp"
+#include "cs.hpp"
+
+#include <pthread.h>
+#include <sys/syscall.h>
+#if HAVE_MACH_EXCEPTIONS
+#include <mach/mach.h>
+#endif // HAVE_MACH_EXCEPTIONS
+
+#include "threadsusp.hpp"
+#include "tls.hpp"
+#include "synchobjects.hpp"
+#include <errno.h>
+
+namespace CorUnix
+{
+ enum PalThreadType
+ {
+ UserCreatedThread,
+ PalWorkerThread,
+ SignalHandlerThread
+ };
+
+ PAL_ERROR
+ InternalCreateThread(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ DWORD dwStackSize,
+ LPTHREAD_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter,
+ DWORD dwCreationFlags,
+ PalThreadType eThreadType,
+ LPDWORD lpThreadId,
+ HANDLE *phThread
+ );
+
+ PAL_ERROR
+ InternalGetThreadPriority(
+ CPalThread *pThread,
+ HANDLE hTargetThread,
+ int *piNewPriority
+ );
+
+ PAL_ERROR
+ InternalSetThreadPriority(
+ CPalThread *pThread,
+ HANDLE hTargetThread,
+ int iNewPriority
+ );
+
+ PAL_ERROR
+ InternalGetThreadDataFromHandle(
+ CPalThread *pThread,
+ HANDLE hThread,
+ DWORD dwRightsRequired,
+ CPalThread **ppTargetThread,
+ IPalObject **ppobjThread
+ );
+
+ VOID
+ InternalEndCurrentThread(
+ CPalThread *pThread
+ );
+
+ PAL_ERROR
+ InternalCreateDummyThread(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ CPalThread **ppDummyThread,
+ HANDLE *phThread
+ );
+
+ PAL_ERROR
+ InitializeGlobalThreadData(
+ void
+ );
+
+ PAL_ERROR
+ CreateThreadData(
+ CPalThread **ppThread
+ );
+
+ PAL_ERROR
+ CreateThreadObject(
+ CPalThread *pThread,
+ CPalThread *pNewThread,
+ HANDLE *phThread
+ );
+
+ PAL_ERROR
+ InitializeEndingThreadsData(
+ void
+ );
+
+ BOOL
+ GetThreadTimesInternal(
+ IN HANDLE hThread,
+ OUT LPFILETIME lpKernelTime,
+ OUT LPFILETIME lpUserTime);
+
+#ifdef FEATURE_PAL_SXS
+#if HAVE_MACH_EXCEPTIONS
+
+ // Structure used to return data about a single handler to a caller.
+ struct MachExceptionHandler
+ {
+ exception_mask_t m_mask;
+ exception_handler_t m_handler;
+ exception_behavior_t m_behavior;
+ thread_state_flavor_t m_flavor;
+ };
+
+ // Class abstracting previously registered Mach exception handlers for a thread.
+ struct CThreadMachExceptionHandlers
+ {
+ public:
+ // Maximum number of exception ports we hook. Must be the count
+ // of all bits set in the exception masks defined in machexception.h.
+ static const int s_nPortsMax = 6;
+
+ // Saved exception ports, exactly as returned by
+ // thread_swap_exception_ports.
+ mach_msg_type_number_t m_nPorts;
+ exception_mask_t m_masks[s_nPortsMax];
+ exception_handler_t m_handlers[s_nPortsMax];
+ exception_behavior_t m_behaviors[s_nPortsMax];
+ thread_state_flavor_t m_flavors[s_nPortsMax];
+
+ CThreadMachExceptionHandlers() :
+ m_nPorts(-1)
+ {
+ }
+
+ // Get handler details for a given type of exception. If successful the structure pointed at by
+ // pHandler is filled in and true is returned. Otherwise false is returned.
+ bool GetHandler(exception_type_t eException, MachExceptionHandler *pHandler);
+
+ private:
+ // Look for a handler for the given exception within the given handler node. Return its index if
+ // successful or -1 otherwise.
+ int GetIndexOfHandler(exception_mask_t bmExceptionMask);
+ };
+#endif // HAVE_MACH_EXCEPTIONS
+#endif // FEATURE_PAL_SXS
+
+ class CThreadSEHInfo : public CThreadInfoInitializer
+ {
+ public:
+#if !HAVE_MACH_EXCEPTIONS
+ BOOL safe_state;
+ int signal_code;
+#endif // !HAVE_MACH_EXCEPTIONSG
+
+ CThreadSEHInfo()
+ {
+ };
+ };
+
+ /* 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. */
+#define ECVT_MAX_COUNT_SIZE 348
+#define ECVT_MAX_BUFFER_SIZE 357
+
+ /*STR_TIME_SIZE is defined as 26 the size of the
+ return val by ctime_r*/
+#define STR_TIME_SIZE 26
+
+ class CThreadCRTInfo : public CThreadInfoInitializer
+ {
+ public:
+ CHAR * strtokContext; // Context for strtok function
+ WCHAR * wcstokContext; // Context for wcstok function
+ struct PAL_tm localtimeBuffer; // Buffer for localtime function
+ CHAR ctimeBuffer[ STR_TIME_SIZE ]; // Buffer for ctime function
+ CHAR ECVTBuffer[ ECVT_MAX_BUFFER_SIZE ]; // Buffer for _ecvt function.
+
+ CThreadCRTInfo() :
+ strtokContext(NULL),
+ wcstokContext(NULL)
+ {
+ ZeroMemory(&localtimeBuffer, sizeof(localtimeBuffer));
+ ZeroMemory(ctimeBuffer, sizeof(ctimeBuffer));
+ ZeroMemory(ECVTBuffer, sizeof(ECVTBuffer));
+ };
+ };
+
+ class CPalThread
+ {
+ friend
+ PAL_ERROR
+ CorUnix::InternalCreateThread(
+ CPalThread *,
+ LPSECURITY_ATTRIBUTES,
+ DWORD,
+ LPTHREAD_START_ROUTINE,
+ LPVOID,
+ DWORD,
+ PalThreadType,
+ LPDWORD,
+ HANDLE*
+ );
+
+ friend
+ PAL_ERROR
+ InternalCreateDummyThread(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ CPalThread **ppDummyThread,
+ HANDLE *phThread
+ );
+
+ friend
+ PAL_ERROR
+ InternalSetThreadPriority(
+ CPalThread *,
+ HANDLE,
+ int
+ );
+
+ friend
+ PAL_ERROR
+ InitializeGlobalThreadData(
+ void
+ );
+
+ friend
+ PAL_ERROR
+ CreateThreadData(
+ CPalThread **ppThread
+ );
+
+ friend
+ PAL_ERROR
+ CreateThreadObject(
+ CPalThread *pThread,
+ CPalThread *pNewThread,
+ HANDLE *phThread
+ );
+
+ friend CatchHardwareExceptionHolder;
+
+ private:
+
+ CPalThread *m_pNext;
+ DWORD m_dwExitCode;
+ BOOL m_fExitCodeSet;
+ CRITICAL_SECTION m_csLock;
+ bool m_fLockInitialized;
+ bool m_fIsDummy;
+
+ //
+ // Minimal reference count, used primarily for cleanup purposes. A
+ // new thread object has an initial refcount of 1. This initial
+ // reference is removed by CorUnix::InternalEndCurrentThread.
+ //
+ // The only other spot the refcount is touched is from within
+ // CPalObjectBase::ReleaseReference -- incremented before the
+ // destructors for an ojbect are called, and decremented afterwords.
+ // This permits the freeing of the thread structure to happen after
+ // the freeing of the enclosing thread object has completed.
+ //
+
+ 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
+ //
+
+ SIZE_T m_threadId;
+ DWORD m_dwLwpId;
+ pthread_t m_pthreadSelf;
+
+#if HAVE_MACH_THREADS
+ mach_port_t m_machPortSelf;
+#endif
+
+ // > 0 when there is an exception holder which causes h/w
+ // exceptions to be sent down the C++ exception chain.
+ int m_hardwareExceptionHolderCount;
+
+ //
+ // Start info
+ //
+
+ LPTHREAD_START_ROUTINE m_lpStartAddress;
+ LPVOID m_lpStartParameter;
+ BOOL m_bCreateSuspended;
+
+ int m_iThreadPriority;
+ 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;
+
+ // The default stack size of a newly created thread (currently 256KB)
+ // when the dwStackSize paramter of PAL_CreateThread()
+ // is zero. This value can be set by setting the
+ // environment variable PAL_THREAD_DEFAULT_STACK_SIZE
+ // (the value should be in bytes and in hex).
+ static DWORD s_dwDefaultThreadStackSize;
+
+ //
+ // The thread entry routine (called from InternalCreateThread)
+ //
+
+ static void* ThreadEntry(void * pvParam);
+
+#ifdef FEATURE_PAL_SXS
+ //
+ // Data for PAL side-by-side support
+ //
+
+ private:
+ // This is set whenever this thread is currently executing within
+ // a region of code that depends on this instance of the PAL
+ // in the process.
+ bool m_fInPal;
+
+#if HAVE_MACH_EXCEPTIONS
+ // Record of Mach exception handlers that were already registered when we register our own CoreCLR
+ // specific handlers.
+ CThreadMachExceptionHandlers m_sMachExceptionHandlers;
+#endif // HAVE_MACH_EXCEPTIONS
+#endif // FEATURE_PAL_SXS
+
+ public:
+
+ //
+ // Embedded information for areas owned by other subsystems
+ //
+
+ CThreadSynchronizationInfo synchronizationInfo;
+ CThreadSuspensionInfo suspensionInfo;
+ CThreadSEHInfo sehInfo;
+ CThreadTLSInfo tlsInfo;
+ CThreadApcInfo apcInfo;
+ 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),
+#if HAVE_MACH_THREADS
+ m_machPortSelf(0),
+#endif
+ m_hardwareExceptionHolderCount(0),
+ m_lpStartAddress(NULL),
+ m_lpStartParameter(NULL),
+ m_bCreateSuspended(FALSE),
+ m_iThreadPriority(THREAD_PRIORITY_NORMAL),
+ m_eThreadType(UserCreatedThread),
+ m_fStartItemsInitialized(FALSE),
+ m_fStartStatus(FALSE),
+ m_fStartStatusSet(FALSE),
+ m_stackBase(NULL),
+ m_stackLimit(NULL)
+#ifdef FEATURE_PAL_SXS
+ , m_fInPal(TRUE)
+#endif // FEATURE_PAL_SXS
+ {
+ };
+
+ virtual ~CPalThread();
+
+ PAL_ERROR
+ RunPreCreateInitializers(
+ void
+ );
+
+ //
+ // m_threadId and m_dwLwpId must be set before calling
+ // RunPostCreateInitializers
+ //
+
+ PAL_ERROR
+ RunPostCreateInitializers(
+ 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
+ )
+ {
+ InternalEnterCriticalSection(pThread, &m_csLock);
+ };
+
+ void
+ Unlock(
+ CPalThread *pThread
+ )
+ {
+ InternalLeaveCriticalSection(pThread, &m_csLock);
+ };
+
+ //
+ // The following three methods provide access to the
+ // native lock used to protect thread native wait data.
+ //
+
+ void
+ AcquireNativeWaitLock(
+ void
+ )
+ {
+ synchronizationInfo.AcquireNativeWaitLock();
+ }
+
+ void
+ ReleaseNativeWaitLock(
+ void
+ )
+ {
+ synchronizationInfo.ReleaseNativeWaitLock();
+ }
+
+ bool
+ TryAcquireNativeWaitLock(
+ void
+ )
+ {
+ return synchronizationInfo.TryAcquireNativeWaitLock();
+ }
+
+ static void
+ SetLastError(
+ DWORD dwLastError
+ )
+ {
+ // Reuse errno to store last error
+ errno = dwLastError;
+ };
+
+ static DWORD
+ GetLastError(
+ void
+ )
+ {
+ // Reuse errno to store last error
+ return errno;
+ };
+
+ void
+ SetExitCode(
+ DWORD dwExitCode
+ )
+ {
+ m_dwExitCode = dwExitCode;
+ m_fExitCodeSet = TRUE;
+ };
+
+ BOOL
+ GetExitCode(
+ DWORD *pdwExitCode
+ )
+ {
+ *pdwExitCode = m_dwExitCode;
+ return m_fExitCodeSet;
+ };
+
+ SIZE_T
+ GetThreadId(
+ void
+ )
+ {
+ return m_threadId;
+ };
+
+ DWORD
+ GetLwpId(
+ void
+ )
+ {
+ return m_dwLwpId;
+ };
+
+ pthread_t
+ GetPThreadSelf(
+ void
+ )
+ {
+ return m_pthreadSelf;
+ };
+
+#if HAVE_MACH_THREADS
+ mach_port_t
+ GetMachPortSelf(
+ void
+ )
+ {
+ return m_machPortSelf;
+ };
+#endif
+
+ bool
+ IsHardwareExceptionsEnabled()
+ {
+ return m_hardwareExceptionHolderCount > 0;
+ }
+
+ 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;
+ };
+
+ int
+ GetThreadPriority(
+ void
+ )
+ {
+ return m_iThreadPriority;
+ };
+
+ IPalObject *
+ GetThreadObject(
+ void
+ )
+ {
+ return m_pThreadObject;
+ }
+
+ BOOL
+ IsDummy(
+ void
+ )
+ {
+ return m_fIsDummy;
+ };
+
+ CPalThread*
+ GetNext(
+ void
+ )
+ {
+ return m_pNext;
+ };
+
+ void
+ SetNext(
+ CPalThread *pNext
+ )
+ {
+ m_pNext = pNext;
+ };
+
+ void
+ AddThreadReference(
+ void
+ );
+
+ void
+ ReleaseThreadReference(
+ void
+ );
+
+ // Get base address of the current thread's stack
+ static
+ void *
+ GetStackBase(
+ void
+ );
+
+ // Get cached base address of this thread's stack
+ // Can be called only for the current thread.
+ void *
+ GetCachedStackBase(
+ void
+ );
+
+ // Get limit address of the current thread's stack
+ static
+ void *
+ GetStackLimit(
+ void
+ );
+
+ // Get cached limit address of this thread's stack
+ // Can be called only for the current thread.
+ void *
+ GetCachedStackLimit(
+ void
+ );
+
+#ifdef FEATURE_PAL_SXS
+ //
+ // Functions for PAL side-by-side support
+ //
+
+ // This function needs to be called on a thread when it enters
+ // a region of code that depends on this instance of the PAL
+ // in the process.
+ PAL_ERROR Enter(PAL_Boundary boundary);
+
+ // This function needs to be called on a thread when it leaves
+ // a region of code that depends on this instance of the PAL
+ // in the process.
+ PAL_ERROR Leave(PAL_Boundary boundary);
+
+ // Returns TRUE whenever this thread is executing in a region
+ // of code that depends on this instance of the PAL in the process.
+ BOOL IsInPal()
+ {
+ return m_fInPal;
+ };
+
+#if HAVE_MACH_EXCEPTIONS
+ // Hook Mach exceptions, i.e., call thread_swap_exception_ports
+ // to replace the thread's current exception ports with our own.
+ // The previously active exception ports are saved. Called when
+ // this thread enters a region of code that depends on this PAL.
+ // Should only fail on internal errors.
+ PAL_ERROR EnableMachExceptions();
+
+ // Unhook Mach exceptions, i.e., call thread_set_exception_ports
+ // to restore the thread's exception ports with those we saved
+ // in EnableMachExceptions. Called when this thread leaves a
+ // region of code that depends on this PAL. Should only fail
+ // on internal errors.
+ PAL_ERROR DisableMachExceptions();
+
+ // The exception handling thread needs to be able to get at the list of handlers that installing our
+ // own handler on a thread has displaced (in case we need to forward an exception that we don't want
+ // to handle).
+ CThreadMachExceptionHandlers *GetSavedMachHandlers()
+ {
+ return &m_sMachExceptionHandlers;
+ }
+#endif // HAVE_MACH_EXCEPTIONS
+#endif // FEATURE_PAL_SXS
+ };
+
+#if defined(FEATURE_PAL_SXS)
+ extern "C" CPalThread *CreateCurrentThreadData();
+#endif // FEATURE_PAL_SXS
+
+ inline CPalThread *GetCurrentPalThread()
+ {
+ return reinterpret_cast<CPalThread*>(pthread_getspecific(thObjKey));
+ }
+
+ inline CPalThread *InternalGetCurrentThread()
+ {
+ CPalThread *pThread = GetCurrentPalThread();
+#if defined(FEATURE_PAL_SXS)
+ if (pThread == nullptr)
+ pThread = CreateCurrentThreadData();
+#endif // FEATURE_PAL_SXS
+ 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
+TLSInitialize(
+ void
+ );
+
+VOID
+TLSCleanup(
+ void
+ );
+
+VOID
+WaitForEndingThreads(
+ void
+ );
+
+extern int free_threads_spinlock;
+
+extern PAL_ActivationFunction g_activationFunction;
+extern PAL_SafeActivationCheckFunction g_safeActivationCheckFunction;
+
+/*++
+Macro:
+ THREADSilentGetCurrentThreadId
+
+Abstract:
+ Same as GetCurrentThreadId, but it doesn't output any traces.
+ It is useful for tracing functions to display the thread ID
+ without generating any new traces.
+
+ TODO: how does the perf of pthread_self compare to
+ InternalGetCurrentThread when we find the thread in the
+ cache?
+
+ If the perf of pthread_self is comparable to that of the stack
+ bounds based lookaside system, why aren't we using it in the
+ cache?
+
+ In order to match the thread ids that debuggers use at least for
+ linux we need to use gettid().
+
+--*/
+#if defined(__linux__)
+#define THREADSilentGetCurrentThreadId() (SIZE_T)syscall(SYS_gettid)
+#elif defined(__APPLE__)
+inline SIZE_T THREADSilentGetCurrentThreadId() {
+ uint64_t tid;
+ pthread_threadid_np(pthread_self(), &tid);
+ return (SIZE_T)tid;
+}
+#elif defined(__NetBSD__)
+#include <lwp.h>
+#define THREADSilentGetCurrentThreadId() (SIZE_T)_lwp_self()
+#else
+#define THREADSilentGetCurrentThreadId() (SIZE_T)pthread_self()
+#endif
+
+#endif // _PAL_THREAD_HPP_
diff --git a/src/pal/src/include/pal/threadinfo.hpp b/src/pal/src/include/pal/threadinfo.hpp
new file mode 100644
index 0000000000..93ba0ababd
--- /dev/null
+++ b/src/pal/src/include/pal/threadinfo.hpp
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/threadinfo.hpp
+
+Abstract:
+ Header file for thread info initialzer
+
+
+
+--*/
+
+#ifndef _PAL_THREADINFO_H_
+#define _PAL_THREADINFO_H_
+
+#include "corunix.hpp"
+
+namespace CorUnix
+{
+ //
+ // There are a number of different functional areas for which we need to
+ // store per-thread data:
+ // * synchronization
+ // * structure exception handling
+ // * asynchronous procedure calls
+ // * thread suspension
+ // * thread-local storage
+ // * CRT per-thread buffers
+ //
+ // For each of the above functional areas we build a class that stores
+ // the necessary data. An instance of each of these classes is embedded
+ // in the main thread class. The classes must not have any failure paths
+ // in their constructors. Each class inherits from a common parent class
+ // that exposes two virtual initialization routines (which may return an
+ // error). The first initialization routine is called after the thread
+ // object is allocated, but before the new thread is created. Any
+ // initialization that is not dependant on knowledge of the new thread's
+ // ID (and by extension need not run in the context of the new thread)
+ // should take place in the first routine. Work that must run in the
+ // context of the new thread or that must know the new thread's ID
+ // should take place in the second initialization routine.
+ //
+
+ class CThreadInfoInitializer
+ {
+ public:
+
+ //
+ // InitializePreCreate is called before the new thread is started.
+ // Any allocations or other initializations that may fail that do
+ // not need to run in the context of the new thread (or know the
+ // new thread's ID) should take place in this routine.
+ //
+
+ virtual
+ PAL_ERROR
+ InitializePreCreate(
+ void
+ )
+ {
+ return NO_ERROR;
+ };
+
+ //
+ // InitializePostCreate is called from within the context of the
+ // new thread.
+ //
+
+ virtual
+ PAL_ERROR
+ InitializePostCreate(
+ CPalThread *pThread,
+ SIZE_T threadId,
+ DWORD dwLwpId
+ )
+ {
+ return NO_ERROR;
+ };
+ };
+}
+
+#endif // _PAL_THREADINFO_H_
diff --git a/src/pal/src/include/pal/threadsusp.hpp b/src/pal/src/include/pal/threadsusp.hpp
new file mode 100644
index 0000000000..e1e85e265c
--- /dev/null
+++ b/src/pal/src/include/pal/threadsusp.hpp
@@ -0,0 +1,382 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/threadsusp.hpp
+
+Abstract:
+ Declarations for thread suspension
+
+
+
+--*/
+
+#ifndef _PAL_THREADSUSP_HPP
+#define _PAL_THREADSUSP_HPP
+
+// 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"
+#if !HAVE_MACH_EXCEPTIONS
+#include <signal.h>
+#endif // !HAVE_MACH_EXCEPTIONS
+#include <semaphore.h>
+#include <sched.h>
+
+// We have a variety of options for synchronizing thread suspensions and resumptions between the requestor and
+// target threads. Analyze the various capabilities given to us by configure and define one of three macros
+// here for simplicity:
+// USE_POSIX_SEMAPHORES
+// USE_SYSV_SEMAPHORES
+// USE_PTHREAD_CONDVARS
+#if HAS_POSIX_SEMAPHORES
+
+// Favor posix semaphores.
+#define USE_POSIX_SEMAPHORES 1
+
+#if HAVE_SYS_SEMAPHORE_H
+#include <sys/semaphore.h>
+#endif // HAVE_SYS_SEMAPHORE_H
+
+#elif HAS_PTHREAD_MUTEXES && HAVE_MACH_EXCEPTIONS
+
+// Can only use the pthread solution if we're not using signals since pthread mutexes are not signal safe.
+#define USE_PTHREAD_CONDVARS 1
+
+#include <pthread.h>
+
+#elif HAS_SYSV_SEMAPHORES
+
+// SYSV semaphores are our last choice since they're shared across processes so it's possible to leak them
+// on abnormal process termination.
+#define USE_SYSV_SEMAPHORES 1
+
+#include <sys/sem.h>
+#include <sys/types.h>
+
+#else
+#error "Don't know how to synchronize thread suspends and resumes on this platform"
+#endif // HAS_POSIX_SEMAPHORES
+
+#include <stdarg.h>
+
+namespace CorUnix
+{
+#ifdef _DEBUG
+#define MAX_TRACKED_CRITSECS 8
+#endif
+
+ PAL_ERROR
+ InternalResumeThread(
+ CPalThread *pthrResumer,
+ HANDLE hTarget,
+ DWORD *pdwSuspendCount
+ );
+
+ class CThreadSuspensionInfo : public CThreadInfoInitializer
+ {
+ private:
+ BOOL m_fPending; // TRUE if a suspension is pending on a thread (because the thread is in an unsafe region)
+ BOOL m_fSelfsusp; // TRUE if thread is self suspending and while thread is self suspended
+ BOOL m_fSuspendedForShutdown; // TRUE once the thread is suspended during PAL cleanup
+ int m_nBlockingPipe; // blocking pipe used for a process that was created suspended
+#ifdef _DEBUG
+ Volatile<LONG> m_lNumThreadsSuspendedByThisThread; // number of threads that this thread has suspended; used for suspension diagnostics
+#endif
+#if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ int m_nSpinlock; // thread's suspension spinlock, which is used to synchronize suspension and resumption attempts
+#else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ pthread_mutex_t m_ptmSuspmutex; // thread's suspension mutex, which is used to synchronize suspension and resumption attempts
+ BOOL m_fSuspmutexInitialized;
+#endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+#if USE_POSIX_SEMAPHORES
+ sem_t m_semSusp; // suspension semaphore
+ sem_t m_semResume; // resumption semaphore
+ BOOL m_fSemaphoresInitialized;
+#elif USE_SYSV_SEMAPHORES
+ // necessary id's and sembuf structures for SysV semaphores
+ int m_nSemsuspid; // id for the suspend semaphore
+ int m_nSemrespid; // id for the resume semaphore
+ struct sembuf m_sbSemwait; // struct representing a wait operation
+ struct sembuf m_sbSempost; // struct representing a post operation
+#elif USE_PTHREAD_CONDVARS
+ pthread_cond_t m_condSusp; // suspension condition variable
+ pthread_mutex_t m_mutexSusp; // mutex associated with the condition above
+ BOOL m_fSuspended; // set to true once the suspend has been acknowledged
+
+ pthread_cond_t m_condResume; // resumption condition variable
+ pthread_mutex_t m_mutexResume; // mutex associated with the condition above
+ BOOL m_fResumed; // set to true once the resume has been acknowledged
+
+ BOOL m_fSemaphoresInitialized;
+#endif // USE_POSIX_SEMAPHORES
+
+ /* Most of the variables above are either accessed by a thread
+ holding the appropriate suspension mutex(es) or are only
+ accessed by their own threads (and thus don't require
+ synchronization).
+
+ m_fPending, m_fSuspendedForShutdown,
+ m_fSuspendSignalSent, and m_fResumeSignalSent
+ may be set by a different thread than the owner and thus
+ require synchronization.
+
+ m_fSelfsusp is set to TRUE only by its own thread but may be later
+ accessed by other threads.
+
+ m_lNumThreadsSuspendedByThisThread is accessed by its owning
+ thread and therefore does not require synchronization. */
+
+#ifdef _DEBUG
+ VOID
+ IncrNumThreadsSuspendedByThisThread(
+ )
+ {
+ InterlockedIncrement(&m_lNumThreadsSuspendedByThisThread);
+ };
+
+ VOID
+ DecrNumThreadsSuspendedByThisThread(
+ )
+ {
+ InterlockedDecrement(&m_lNumThreadsSuspendedByThisThread);
+ };
+#endif
+
+ 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
+ SetSuspPending(
+ BOOL fPending
+ )
+ {
+ m_fPending = fPending;
+ };
+
+ BOOL
+ GetSuspPending(
+ void
+ )
+ {
+ return m_fPending;
+ };
+
+ void
+ SetSelfSusp(
+ BOOL fSelfsusp
+ )
+ {
+ m_fSelfsusp = fSelfsusp;
+ };
+
+ BOOL
+ GetSelfSusp(
+ void
+ )
+ {
+ return m_fSelfsusp;
+ };
+
+ void
+ PostOnSuspendSemaphore();
+
+ void
+ WaitOnSuspendSemaphore();
+
+ void
+ PostOnResumeSemaphore();
+
+ void
+ WaitOnResumeSemaphore();
+
+ 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();
+
+#ifdef _DEBUG
+ LONG
+ GetNumThreadsSuspendedByThisThread(
+ void
+ )
+ {
+ return m_lNumThreadsSuspendedByThisThread;
+ };
+#endif // _DEBUG
+
+#if USE_SYSV_SEMAPHORES
+ void
+ DestroySemaphoreIds(
+ void
+ );
+#endif
+ void
+ SetSuspendedForShutdown(
+ BOOL fSuspendedForShutdown
+ )
+ {
+ m_fSuspendedForShutdown = fSuspendedForShutdown;
+ };
+
+ BOOL
+ GetSuspendedForShutdown(
+ void
+ )
+ {
+ return m_fSuspendedForShutdown;
+ };
+
+ 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/tls.hpp b/src/pal/src/include/pal/tls.hpp
new file mode 100644
index 0000000000..a4d9926c49
--- /dev/null
+++ b/src/pal/src/include/pal/tls.hpp
@@ -0,0 +1,65 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/tls.hpp
+
+Abstract:
+ Header file for thread local storage
+
+
+
+--*/
+
+#ifndef _PAL_TLS_HPP
+#define _PAL_TLS_HPP
+
+#include "threadinfo.hpp"
+
+namespace CorUnix
+{
+ /* This is the number of slots available for use in TlsAlloc().
+ sTlsSlotFields in thread/localstorage.c must be this number
+ of bits. */
+#define TLS_SLOT_SIZE 64
+
+ class CThreadTLSInfo : public CThreadInfoInitializer
+ {
+ public:
+ LPVOID tlsSlots[TLS_SLOT_SIZE];
+
+ virtual
+ PAL_ERROR
+ InitializePostCreate(
+ CPalThread *pThread,
+ SIZE_T threadId,
+ DWORD dwLwpId
+ );
+
+ CThreadTLSInfo()
+ {
+ ZeroMemory(tlsSlots, sizeof(tlsSlots));
+ };
+ };
+
+ //
+ // InternalGetCurrentThread obtains the CPalThread instance for the
+ // calling thread. That instance should only be used by the calling
+ // thread. If another thread will at some point need access to this
+ // thread information it should be given a referenced pointer to
+ // the IPalObject stored within the CPalThread.
+ //
+
+ extern pthread_key_t thObjKey;
+
+ CPalThread *InternalGetCurrentThread();
+}
+
+#endif // _PAL_TLS_HPP
+
diff --git a/src/pal/src/include/pal/unicode_data.h b/src/pal/src/include/pal/unicode_data.h
new file mode 100644
index 0000000000..0b7fe07447
--- /dev/null
+++ b/src/pal/src/include/pal/unicode_data.h
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/unicode_data.h
+
+Abstract:
+
+ Data, data retrieval function declarations.
+
+
+
+--*/
+
+#ifndef _UNICODE_DATA_H_
+#define _UNICODE_DATA_H_
+
+#include "pal/palinternal.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+#if !HAVE_COREFOUNDATION
+
+typedef struct
+{
+ WCHAR nUnicodeValue;
+ WORD C1_TYPE_FLAGS;
+ WCHAR nOpposingCase; /* 0 if no opposing case. */
+ WORD rangeValue;
+} UnicodeDataRec;
+
+/* Global variables. */
+extern CONST UnicodeDataRec UnicodeData[];
+extern CONST UINT UNICODE_DATA_SIZE;
+extern CONST UINT UNICODE_DATA_DIRECT_ACCESS;
+
+/*++
+Function:
+ GetUnicodeData
+ This function is used to get information about a Unicode character.
+
+Parameters:
+nUnicodeValue
+ The numeric value of the Unicode character to get information about.
+pDataRec
+ The UnicodeDataRec to fill in with the data for the Unicode character.
+
+Return value:
+ TRUE if the Unicode character was found.
+
+--*/
+BOOL GetUnicodeData(INT nUnicodeValue, UnicodeDataRec *pDataRec);
+
+#endif /* !HAVE_COREFOUNDATION */
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _UNICODE_DATA_H_ */
diff --git a/src/pal/src/include/pal/utf8.h b/src/pal/src/include/pal/utf8.h
new file mode 100644
index 0000000000..2516caafb0
--- /dev/null
+++ b/src/pal/src/include/pal/utf8.h
@@ -0,0 +1,53 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/utf8.h
+
+Abstract:
+ Header file for UTF-8 conversion functions.
+
+Revision History:
+
+
+
+--*/
+
+#ifndef _PAL_UTF8_H_
+#define _PAL_UTF8_H_
+
+#include <pal/palinternal.h> /* for WCHAR */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/*++
+Function :
+ UTF8ToUnicode
+
+ Convert a string from UTF-8 to UTF-16 (UCS-2)
+--*/
+int UTF8ToUnicode(LPCSTR lpSrcStr, int cchSrc, LPWSTR lpDestStr, int cchDest, DWORD dwFlags);
+
+
+/*++
+Function :
+ UnicodeToUTF8
+
+ Convert a string from UTF-16 (UCS-2) to UTF-8
+--*/
+int UnicodeToUTF8(LPCWSTR lpSrcStr, int cchSrc, LPSTR lpDestStr, int cchDest);
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif /* _PAL_UTF8_H_ */
diff --git a/src/pal/src/include/pal/utils.h b/src/pal/src/include/pal/utils.h
new file mode 100644
index 0000000000..3ddad4ae2f
--- /dev/null
+++ b/src/pal/src/include/pal/utils.h
@@ -0,0 +1,157 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/utils.h
+
+Abstract:
+ Miscellaneous helper functions for the PAL, which don't fit anywhere else
+
+
+
+--*/
+
+#ifndef _PAL_UTILS_H_
+#define _PAL_UTILS_H_
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/*++
+Function:
+ UTIL_inverse_wcspbrk
+
+ Opposite of wcspbrk : searches a string for the first character NOT in the
+ given set
+
+Parameters :
+ LPWSTR lpwstr : string to search
+ LPCWSTR charset : list of characters to search for
+
+Return value :
+ pointer to first character of lpwstr that isn't in the set
+ NULL if all characters are in the set
+--*/
+LPWSTR UTIL_inverse_wcspbrk(LPWSTR lpwstr, LPCWSTR charset);
+
+/*++
+Function :
+ UTIL_IsReadOnlyBitsSet
+
+ Takes a struct stat *
+ Returns true if the file is read only,
+--*/
+BOOL UTIL_IsReadOnlyBitsSet( struct stat * stat_data );
+
+/*++
+Function :
+ UTIL_IsExecuteBitsSet
+
+ Takes a struct stat *
+ Returns true if the file is executable.
+--*/
+BOOL UTIL_IsExecuteBitsSet( struct stat * stat_data );
+
+
+/*++
+Function :
+ UTIL_WCToMB_Alloc
+
+ Converts a wide string to a multibyte string, allocating the required buffer
+
+Parameters :
+ LPCWSTR lpWideCharStr : string to convert
+ int cchWideChar : number of wide characters to convert
+ (-1 to convert a complete null-termnated string)
+
+Return Value :
+ newly allocated buffer containing the converted string. Conversion is
+ performed using CP_ACP. Buffer is allocated with malloc(), release it
+ with free().
+ In case if failure, LastError will be set.
+--*/
+LPSTR UTIL_WCToMB_Alloc(LPCWSTR lpWideCharStr, int cchWideChar);
+
+/*++
+Function :
+ UTIL_MBToWC_Alloc
+
+ Converts a multibyte string to a wide string, allocating the required buffer
+
+Parameters :
+ LPCSTR lpMultiByteStr : string to convert
+ int cbMultiByte : number of bytes to convert
+ (-1 to convert a complete null-termnated string)
+
+Return Value :
+ newly allocated buffer containing the converted string. Conversion is
+ performed using CP_ACP. Buffer is allocated with malloc(), release it
+ with free().
+ In case if failure, LastError will be set.
+--*/
+LPWSTR UTIL_MBToWC_Alloc(LPCSTR lpMultiByteStr, int cbMultiByte);
+
+#if HAVE_VM_ALLOCATE
+#include <mach/kern_return.h>
+
+/*++
+Function:
+ UTIL_MachErrorToPalError
+
+ Maps a Mach kern_return_t to a Win32 error code.
+--*/
+DWORD UTIL_MachErrorToPalError(kern_return_t MachReturn);
+
+/*++
+Function:
+ UTIL_SetLastErrorFromMach
+
+ Sets Win32 LastError according to the argument Mach kern_return_t value,
+ provided it indicates an error. If the argument indicates success, does
+ not modify LastError.
+--*/
+void UTIL_SetLastErrorFromMach(kern_return_t MachReturn);
+
+#endif //HAVE_VM_ALLOCATE
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+class StringHolder
+ {
+ private:
+ LPSTR data;
+ public:
+ StringHolder() : data(NULL) { }
+ ~StringHolder()
+ {
+ PAL_free( data);
+ }
+
+ operator LPSTR () { return data;}
+
+ StringHolder& operator= (LPSTR value)
+ {
+ data = value;
+ return *this;
+ }
+
+ BOOL IsNull()
+ {
+ return data == NULL;
+ }
+
+ };
+#endif /* _PAL_UTILS_H_ */
diff --git a/src/pal/src/include/pal/virtual.h b/src/pal/src/include/pal/virtual.h
new file mode 100644
index 0000000000..a4e225281e
--- /dev/null
+++ b/src/pal/src/include/pal/virtual.h
@@ -0,0 +1,208 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/virtual.h
+
+Abstract:
+ Header file for virtual memory management.
+
+
+
+--*/
+
+#ifndef _PAL_VIRTUAL_H_
+#define _PAL_VIRTUAL_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+typedef struct _CMI {
+
+ struct _CMI * pNext; /* Link to the next entry. */
+ struct _CMI * pPrevious; /* Link to the previous entry. */
+
+ UINT_PTR startBoundary; /* Starting location of the region. */
+ SIZE_T memSize; /* Size of the entire region.. */
+
+ DWORD accessProtection; /* Initial allocation access protection. */
+ DWORD allocationType; /* Initial allocation type. */
+
+ BYTE * pAllocState; /* Individual allocation type tracking for each */
+ /* page in the region. */
+
+ BYTE * pProtectionState; /* Individual allocation type tracking for each */
+ /* page in the region. */
+
+} CMI, * PCMI;
+
+enum VIRTUAL_CONSTANTS
+{
+ /* Allocation type. */
+ VIRTUAL_COMMIT_ALL_BITS = 0xFF,
+ VIRTUAL_RESERVE_ALL_BITS = 0x0,
+
+ /* Protection Type. */
+ VIRTUAL_READONLY,
+ VIRTUAL_READWRITE,
+ VIRTUAL_EXECUTE_READWRITE,
+ VIRTUAL_NOACCESS,
+ VIRTUAL_EXECUTE,
+ VIRTUAL_EXECUTE_READ,
+
+ /* Page manipulation constants. */
+#ifdef __sparc__
+ VIRTUAL_PAGE_SIZE = 0x2000,
+#else // __sparc__
+ VIRTUAL_PAGE_SIZE = 0x1000,
+#endif // __sparc__
+ VIRTUAL_PAGE_MASK = VIRTUAL_PAGE_SIZE - 1,
+ BOUNDARY_64K = 0xffff
+};
+
+/*++
+Function :
+ VIRTUALInitialize
+
+ Initialize the critical sections.
+
+Return value:
+ TRUE if initialization succeeded
+ FALSE otherwise.
+--*/
+BOOL VIRTUALInitialize(bool initializeExecutableMemoryAllocator);
+
+/*++
+Function :
+ VIRTUALCleanup
+
+ Deletes the critical sections.
+
+--*/
+void VIRTUALCleanup( void );
+
+#ifdef __cplusplus
+}
+
+/*++
+Class:
+ ExecutableMemoryAllocator
+
+ This class implements a virtual memory allocator for JIT'ed code.
+ The purpose of this allocator is to opportunistically reserve a chunk of virtual memory
+ that is located near the coreclr library (within 2GB range) that can be later used by
+ JIT. Having executable memory close to the coreclr library allows JIT to generate more
+ efficient code (by avoiding usage of jump stubs) and thus it can significantly improve
+ performance of the application.
+
+ This allocator is integrated with the VirtualAlloc/Reserve code. If VirtualAlloc has been
+ called with the MEM_RESERVE_EXECUTABLE flag then it will first try to obtain the requested size
+ of virtual memory from ExecutableMemoryAllocator. If ExecutableMemoryAllocator runs out of
+ the reserved memory (or fails to allocate it during initialization) then VirtualAlloc/Reserve code
+ will simply fall back to reserving memory using OS APIs.
+
+ Notes:
+ - the memory allocated by this class is NOT committed by default. It is responsibility
+ of the caller to commit the virtual memory before accessing it.
+ - in addition, this class does not provide ability to free the reserved memory. The caller
+ has full control of the memory it got from this allocator (i.e. the caller becomes
+ the owner of the allocated memory), so it is caller's responsibility to free the memory
+ if it is no longer needed.
+--*/
+class ExecutableMemoryAllocator
+{
+public:
+ /*++
+ Function:
+ Initialize
+
+ This function initializes the allocator. It should be called early during process startup
+ (when process address space is pretty much empty) in order to have a chance to reserve
+ sufficient amount of memory that is close to the coreclr library.
+ --*/
+ void Initialize();
+
+ /*++
+ Function:
+ AllocateMemory
+
+ This function attempts to allocate the requested amount of memory from its reserved virtual
+ address space. The function will return NULL if the allocation request cannot
+ be satisfied by the memory that is currently available in the allocator.
+ --*/
+ void* AllocateMemory(SIZE_T allocationSize);
+
+private:
+ /*++
+ Function:
+ TryReserveInitialMemory
+
+ This function is called during initialization. It opportunistically tries to reserve
+ a large chunk of virtual memory that can be later used to store JIT'ed code.
+ --*/
+ void TryReserveInitialMemory();
+
+ /*++
+ Function:
+ GenerateRandomStartOffset
+
+ This function returns a random offset (in multiples of the virtual page size)
+ at which the allocator should start allocating memory from its reserved memory range.
+ --*/
+ int32_t GenerateRandomStartOffset();
+
+private:
+ // There does not seem to be an easy way find the size of a library on Unix.
+ // So this constant represents an approximation of the libcoreclr size (on debug build)
+ // that can be used to calculate an approximate location of the memory that
+ // is in 2GB range from the coreclr library. In addition, having precise size of libcoreclr
+ // is not necessary for the calculations.
+ const int32_t CoreClrLibrarySize = 100 * 1024 * 1024;
+
+ // This constant represent the max size of the virtual memory that this allocator
+ // will try to reserve during initialization. We want all JIT-ed code and the
+ // entire libcoreclr to be located in a 2GB range.
+ const int32_t MaxExecutableMemorySize = 0x7FFF0000 - CoreClrLibrarySize;
+
+ // Start address of the reserved virtual address space
+ void* m_startAddress;
+
+ // Next available address in the reserved address space
+ void* m_nextFreeAddress;
+
+ // Total size of the virtual memory that the allocator has been able to
+ // reserve during its initialization.
+ int32_t m_totalSizeOfReservedMemory;
+
+ // Remaining size of the reserved virtual memory that can be used to satisfy allocation requests.
+ int32_t m_remainingReservedMemory;
+};
+
+#endif // __cplusplus
+
+/*++
+Function :
+ ReserveMemoryFromExecutableAllocator
+
+ This function is used to reserve a region of virual memory (not commited)
+ that is located close to the coreclr library. The memory comes from the virtual
+ address range that is managed by ExecutableMemoryAllocator.
+--*/
+void* ReserveMemoryFromExecutableAllocator(CorUnix::CPalThread* pthrCurrent, SIZE_T allocationSize);
+
+#endif /* _PAL_VIRTUAL_H_ */
+
+
+
+
+
+
+
diff --git a/src/pal/src/init/pal.cpp b/src/pal/src/init/pal.cpp
new file mode 100644
index 0000000000..a5edb36428
--- /dev/null
+++ b/src/pal/src/init/pal.cpp
@@ -0,0 +1,1480 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ init/pal.cpp
+
+Abstract:
+
+ Implementation of PAL exported functions not part of the Win32 API.
+
+
+
+--*/
+
+#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/seh.hpp"
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.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"
+#include "pal/environ.h"
+#include "pal/utils.h"
+#include "pal/debug.h"
+#include "pal/locale.h"
+#include "pal/init.h"
+#include "pal/stackstring.hpp"
+
+#if HAVE_MACH_EXCEPTIONS
+#include "../exception/machexception.h"
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <string.h>
+#include <fcntl.h>
+
+#if HAVE_POLL
+#include <poll.h>
+#else
+#include "pal/fakepoll.h"
+#endif // HAVE_POLL
+
+#if defined(__APPLE__)
+#include <sys/sysctl.h>
+int CacheLineSize;
+#endif //__APPLE__
+
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif // __APPLE__
+
+#ifdef __NetBSD__
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <kvm.h>
+#endif
+
+using namespace CorUnix;
+
+//
+// $$TODO The C++ compiler doesn't like pal/cruntime.h so duplicate the
+// necessary prototype here
+//
+
+extern "C" BOOL CRTInitStdStreams( void );
+
+
+SET_DEFAULT_DEBUG_CHANNEL(PAL);
+
+Volatile<INT> init_count = 0;
+Volatile<BOOL> shutdown_intent = 0;
+Volatile<LONG> g_coreclrInitialized = 0;
+static BOOL g_fThreadDataAvailable = FALSE;
+static pthread_mutex_t init_critsec_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* critical section to protect access to init_count. This is allocated on the
+ very first PAL_Initialize call, and is freed afterward. */
+static PCRITICAL_SECTION init_critsec = NULL;
+
+static int Initialize(int argc, const char *const argv[], DWORD flags);
+static BOOL INIT_IncreaseDescriptorLimit(void);
+static LPWSTR INIT_FormatCommandLine (int argc, const char * const *argv);
+static LPWSTR INIT_FindEXEPath(LPCSTR exe_name);
+
+#ifdef _DEBUG
+extern void PROCDumpThreadList(void);
+#endif
+
+#if defined(__APPLE__)
+static bool RunningNatively()
+{
+ int ret = 0;
+ size_t sz = sizeof(ret);
+ if (sysctlbyname("sysctl.proc_native", &ret, &sz, NULL, 0) != 0)
+ {
+ // if the sysctl failed, we'll assume this OS does not support
+ // binary translation - so we must be running natively.
+ return true;
+ }
+ return ret != 0;
+}
+#endif // __APPLE__
+
+/*++
+Function:
+ PAL_Initialize
+
+Abstract:
+ 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
+ -1 if it failed
+
+--*/
+int
+PALAPI
+PAL_Initialize(
+ int argc,
+ const char *const argv[])
+{
+ return Initialize(argc, argv, PAL_INITIALIZE);
+}
+
+/*++
+Function:
+ PAL_InitializeDLL
+
+Abstract:
+ Initializes the non-runtime DLLs/modules like the DAC and SOS.
+
+Return:
+ 0 if successful
+ -1 if it failed
+
+--*/
+int
+PALAPI
+PAL_InitializeDLL()
+{
+ return Initialize(0, NULL, PAL_INITIALIZE_DLL);
+}
+
+/*++
+Function:
+ Initialize
+
+Abstract:
+ Common PAL initialization function.
+
+Return:
+ 0 if successful
+ -1 if it failed
+
+--*/
+int
+Initialize(
+ int argc,
+ const char *const argv[],
+ DWORD flags)
+{
+ PAL_ERROR palError = ERROR_GEN_FAILURE;
+ CPalThread *pThread = NULL;
+ CSharedMemoryObjectManager *pshmom = NULL;
+ LPWSTR command_line = NULL;
+ LPWSTR exe_path = NULL;
+ int retval = -1;
+ bool fFirstTimeInit = false;
+
+ /* the first ENTRY within the first call to PAL_Initialize is a special
+ case, since debug channels are not initialized yet. So in that case the
+ ENTRY will be called after the DBG channels initialization */
+ ENTRY_EXTERNAL("PAL_Initialize(argc = %d argv = %p)\n", argc, argv);
+
+ /*Firstly initiate a lastError */
+ SetLastError(ERROR_GEN_FAILURE);
+
+#ifdef __APPLE__
+ if (!RunningNatively())
+ {
+ SetLastError(ERROR_BAD_FORMAT);
+ goto exit;
+ }
+#endif // __APPLE__
+
+ CriticalSectionSubSysInitialize();
+
+ if(NULL == init_critsec)
+ {
+ pthread_mutex_lock(&init_critsec_mutex); // prevents race condition of two threads
+ // initializing the critical section.
+ if(NULL == init_critsec)
+ {
+ static CRITICAL_SECTION temp_critsec;
+
+ // Want this critical section to NOT be internal to avoid the use of unsafe region markers.
+ InternalInitializeCriticalSectionAndSpinCount(&temp_critsec, 0, false);
+
+ if(NULL != InterlockedCompareExchangePointer(&init_critsec, &temp_critsec, NULL))
+ {
+ // Another thread got in before us! shouldn't happen, if the PAL
+ // isn't initialized there shouldn't be any other threads
+ WARN("Another thread initialized the critical section\n");
+ InternalDeleteCriticalSection(&temp_critsec);
+ }
+ }
+ pthread_mutex_unlock(&init_critsec_mutex);
+ }
+
+ InternalEnterCriticalSection(pThread, init_critsec); // here pThread is always NULL
+
+ if (init_count == 0)
+ {
+ // Set our pid and sid.
+ gPID = getpid();
+ gSID = getsid(gPID);
+
+ fFirstTimeInit = true;
+
+ // Initialize the TLS lookaside cache
+ if (FALSE == TLSInitialize())
+ {
+ goto done;
+ }
+
+ // Initialize the environment.
+ if (FALSE == EnvironInitialize())
+ {
+ goto CLEANUP0;
+ }
+
+ // Initialize debug channel settings before anything else.
+ // This depends on the environment, so it must come after
+ // EnvironInitialize.
+ if (FALSE == DBG_init_channels())
+ {
+ goto CLEANUP0;
+ }
+
+#if _DEBUG
+ // Verify that our page size is what we think it is. If it's
+ // different, we can't run.
+ if (VIRTUAL_PAGE_SIZE != getpagesize())
+ {
+ ASSERT("VIRTUAL_PAGE_SIZE is incorrect for this system!\n"
+ "Change include/pal/virtual.h and clr/src/inc/stdmacros.h "
+ "to reflect the correct page size of %d.\n", getpagesize());
+ }
+#endif // _DEBUG
+
+ if (!INIT_IncreaseDescriptorLimit())
+ {
+ ERROR("Unable to increase the file descriptor limit!\n");
+ // We can continue if this fails; we'll just have problems if
+ // we use large numbers of threads or have many open files.
+ }
+
+ SharedMemoryManager::StaticInitialize();
+
+ /* initialize the shared memory infrastructure */
+ if (!SHMInitialize())
+ {
+ ERROR("Shared memory initialization failed!\n");
+ goto CLEANUP0;
+ }
+
+ //
+ // Initialize global process data
+ //
+
+ palError = InitializeProcessData();
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to initialize process data\n");
+ goto CLEANUP1;
+ }
+
+#if HAVE_MACH_EXCEPTIONS
+ // Mach exception port needs to be set up before the thread
+ // data or threads are set up.
+ if (!SEHInitializeMachExceptions(flags))
+ {
+ ERROR("SEHInitializeMachExceptions failed!\n");
+ palError = ERROR_GEN_FAILURE;
+ goto CLEANUP1;
+ }
+#endif // HAVE_MACH_EXCEPTIONS
+
+ //
+ // Initialize global thread data
+ //
+
+ palError = InitializeGlobalThreadData();
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to initialize thread data\n");
+ goto CLEANUP1;
+ }
+
+ //
+ // Allocate the initial thread data
+ //
+
+ palError = CreateThreadData(&pThread);
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to create initial thread data\n");
+ goto CLEANUP1a;
+ }
+
+ PROCAddThread(pThread, pThread);
+
+ //
+ // Initialize mutex and condition variable used to synchronize the ending threads count
+ //
+
+ palError = InitializeEndingThreadsData();
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to create ending threads data\n");
+ goto CLEANUP1b;
+ }
+
+ //
+ // It's now safe to access our thread data
+ //
+
+ g_fThreadDataAvailable = TRUE;
+
+ //
+ // Initialize module manager
+ //
+ if (FALSE == LOADInitializeModules())
+ {
+ ERROR("Unable to initialize module manager\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto CLEANUP1b;
+ }
+
+ //
+ // Initialize the object manager
+ //
+
+ pshmom = InternalNew<CSharedMemoryObjectManager>();
+ if (NULL == pshmom)
+ {
+ ERROR("Unable to allocate new object manager\n");
+ palError = ERROR_OUTOFMEMORY;
+ goto CLEANUP1b;
+ }
+
+ palError = pshmom->Initialize();
+ if (NO_ERROR != palError)
+ {
+ ERROR("object manager initialization failed!\n");
+ InternalDelete(pshmom);
+ goto CLEANUP1b;
+ }
+
+ 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
+ {
+ pThread = InternalGetCurrentThread();
+ }
+
+ palError = ERROR_GEN_FAILURE;
+
+ if (argc > 0 && argv != NULL)
+ {
+ /* build the command line */
+ command_line = INIT_FormatCommandLine(argc, argv);
+ if (NULL == command_line)
+ {
+ ERROR("Error building command line\n");
+ goto CLEANUP1d;
+ }
+
+ /* find out the application's full path */
+ exe_path = INIT_FindEXEPath(argv[0]);
+ if (NULL == exe_path)
+ {
+ ERROR("Unable to find exe path\n");
+ goto CLEANUP1e;
+ }
+
+ if (NULL == command_line || NULL == exe_path)
+ {
+ ERROR("Failed to process command-line parameters!\n");
+ goto CLEANUP2;
+ }
+
+ palError = InitializeProcessCommandLine(
+ command_line,
+ exe_path);
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to initialize command line\n");
+ goto CLEANUP2;
+ }
+
+ // InitializeProcessCommandLine took ownership of this memory.
+ command_line = NULL;
+
+#ifdef PAL_PERF
+ // Initialize the Profiling structure
+ if(FALSE == PERFInitialize(command_line, exe_path))
+ {
+ ERROR("Performance profiling initial failed\n");
+ goto CLEANUP2;
+ }
+ PERFAllocThreadInfo();
+#endif
+
+ if (!LOADSetExeName(exe_path))
+ {
+ ERROR("Unable to set exe name\n");
+ goto CLEANUP2;
+ }
+
+ // LOADSetExeName took ownership of this memory.
+ exe_path = NULL;
+ }
+
+ 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;
+ }
+
+ 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 CLEANUP5;
+ }
+ }
+
+ palError = ERROR_GEN_FAILURE;
+
+ /* initialize structured exception handling stuff (signals, etc) */
+ if (FALSE == SEHInitialize(pThread, flags))
+ {
+ ERROR("Unable to initialize SEH support\n");
+ goto CLEANUP5;
+ }
+
+ if (FALSE == TIMEInitialize())
+ {
+ ERROR("Unable to initialize TIME support\n");
+ goto CLEANUP6;
+ }
+
+ /* Initialize the File mapping critical section. */
+ if (FALSE == MAPInitialize())
+ {
+ ERROR("Unable to initialize file mapping support\n");
+ goto CLEANUP6;
+ }
+
+ /* Initialize the Virtual* functions. */
+ bool initializeExecutableMemoryAllocator = (flags & PAL_INITIALIZE_EXEC_ALLOCATOR) != 0;
+ if (FALSE == VIRTUALInitialize(initializeExecutableMemoryAllocator))
+ {
+ ERROR("Unable to initialize virtual memory support\n");
+ goto CLEANUP10;
+ }
+
+ if (flags & PAL_INITIALIZE_STD_HANDLES)
+ {
+ /* create file objects for standard handles */
+ if (!FILEInitStdHandles())
+ {
+ ERROR("Unable to initialize standard file handles\n");
+ goto CLEANUP13;
+ }
+ }
+
+ if (FALSE == CRTInitStdStreams())
+ {
+ ERROR("Unable to initialize CRT standard streams\n");
+ goto CLEANUP15;
+ }
+
+ TRACE("First-time PAL initialization complete.\n");
+ init_count++;
+
+ /* Set LastError to a non-good value - functions within the
+ PAL startup may set lasterror to a nonzero value. */
+ SetLastError(NO_ERROR);
+ retval = 0;
+ }
+ else
+ {
+ init_count++;
+
+ // Behave the same wrt entering the PAL independent of whether this
+ // is the first call to PAL_Initialize or not. The first call implied
+ // PAL_Enter by virtue of creating the CPalThread for the current
+ // thread, and its starting state is to be in the PAL.
+ (void)PAL_Enter(PAL_BoundaryTop);
+
+ TRACE("Initialization count increases to %d\n", init_count.Load());
+
+ SetLastError(NO_ERROR);
+ retval = 0;
+ }
+ goto done;
+
+ /* No cleanup required for CRTInitStdStreams */
+CLEANUP15:
+ FILECleanupStdHandles();
+CLEANUP13:
+ VIRTUALCleanup();
+CLEANUP10:
+ MAPCleanup();
+CLEANUP6:
+ SEHCleanup();
+CLEANUP5:
+ PROCCleanupInitialProcess();
+CLEANUP2:
+ free(exe_path);
+CLEANUP1e:
+ free(command_line);
+CLEANUP1d:
+ // Cleanup synchronization manager
+CLEANUP1c:
+ // Cleanup object manager
+CLEANUP1b:
+ // Cleanup initial thread data
+CLEANUP1a:
+ // Cleanup global process data
+CLEANUP1:
+ SHMCleanup();
+CLEANUP0:
+ TLSCleanup();
+ ERROR("PAL_Initialize failed\n");
+ SetLastError(palError);
+done:
+#ifdef PAL_PERF
+ if( retval == 0)
+ {
+ PERFEnableProcessProfile();
+ PERFEnableThreadProfile(FALSE);
+ PERFCalibrate("Overhead of PERF entry/exit");
+ }
+#endif
+
+ InternalLeaveCriticalSection(pThread, init_critsec);
+
+ if (fFirstTimeInit && 0 == retval)
+ {
+ _ASSERTE(NULL != pThread);
+ }
+
+ if (retval != 0 && GetLastError() == ERROR_SUCCESS)
+ {
+ ASSERT("returning failure, but last error not set\n");
+ }
+
+#ifdef __APPLE__
+exit :
+#endif // __APPLE__
+ LOGEXIT("PAL_Initialize returns int %d\n", retval);
+ return retval;
+}
+
+
+/*++
+Function:
+ PAL_InitializeCoreCLR
+
+Abstract:
+ A replacement for PAL_Initialize when loading CoreCLR. Instead of taking a command line (which CoreCLR
+ instances aren't given anyway) the path into which the CoreCLR is installed is supplied instead. This is
+ cached so that PAL_GetPALDirectoryW can return it later.
+
+ This routine also makes sure the psuedo dynamic libraries PALRT and mscorwks have their initialization
+ methods called.
+
+Return:
+ ERROR_SUCCESS if successful
+ An error code, if it failed
+
+--*/
+PAL_ERROR
+PALAPI
+PAL_InitializeCoreCLR(const char *szExePath)
+{
+ // Fake up a command line to call PAL initialization with.
+ int result = Initialize(1, &szExePath, PAL_INITIALIZE_CORECLR);
+ if (result != 0)
+ {
+ return GetLastError();
+ }
+
+ // Check for a repeated call (this is a no-op).
+ if (InterlockedIncrement(&g_coreclrInitialized) > 1)
+ {
+ PAL_Enter(PAL_BoundaryTop);
+ return ERROR_SUCCESS;
+ }
+
+ // Now that the PAL is initialized it's safe to call the initialization methods for the code that used to
+ // be dynamically loaded libraries but is now statically linked into CoreCLR just like the PAL, i.e. the
+ // PAL RT and mscorwks.
+ if (!LOADInitializeCoreCLRModule())
+ {
+ return ERROR_DLL_INIT_FAILED;
+ }
+
+ if (!InitializeFlushProcessWriteBuffers())
+ {
+ return ERROR_GEN_FAILURE;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+/*++
+Function:
+PAL_IsDebuggerPresent
+
+Abstract:
+This function should be used to determine if a debugger is attached to the process.
+--*/
+PALIMPORT
+BOOL
+PALAPI
+PAL_IsDebuggerPresent()
+{
+#if defined(__linux__)
+ BOOL debugger_present = FALSE;
+ char buf[2048];
+
+ int status_fd = open("/proc/self/status", O_RDONLY);
+ if (status_fd == -1)
+ {
+ return FALSE;
+ }
+ ssize_t num_read = read(status_fd, buf, sizeof(buf) - 1);
+
+ if (num_read > 0)
+ {
+ static const char TracerPid[] = "TracerPid:";
+ char *tracer_pid;
+
+ buf[num_read] = '\0';
+ tracer_pid = strstr(buf, TracerPid);
+ if (tracer_pid)
+ {
+ debugger_present = !!atoi(tracer_pid + sizeof(TracerPid) - 1);
+ }
+ }
+
+ close(status_fd);
+
+ return debugger_present;
+#elif defined(__APPLE__)
+ struct kinfo_proc info = {};
+ size_t size = sizeof(info);
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
+ int ret = sysctl(mib, sizeof(mib)/sizeof(*mib), &info, &size, NULL, 0);
+
+ if (ret == 0)
+ return ((info.kp_proc.p_flag & P_TRACED) != 0);
+
+ return FALSE;
+#elif defined(__NetBSD__)
+ int traced;
+ kvm_t *kd;
+ int cnt;
+
+ struct kinfo_proc *info;
+
+ kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
+ if (kd == NULL)
+ return FALSE;
+
+ info = kvm_getprocs(kd, KERN_PROC_PID, getpid(), &cnt);
+ if (info == NULL || cnt < 1)
+ {
+ kvm_close(kd);
+ return FALSE;
+ }
+
+ traced = info->kp_proc.p_slflag & PSL_TRACED;
+ kvm_close(kd);
+
+ if (traced != 0)
+ return TRUE;
+ else
+ return FALSE;
+#else
+ return FALSE;
+#endif
+}
+
+/*++
+Function:
+ PAL_EntryPoint
+
+Abstract:
+ This function should be used to wrap code that uses PAL library on thread that was not created by PAL.
+--*/
+PALIMPORT
+DWORD_PTR
+PALAPI
+PAL_EntryPoint(
+ IN LPTHREAD_START_ROUTINE lpStartAddress,
+ IN LPVOID lpParameter)
+{
+ CPalThread *pThread;
+ DWORD_PTR retval = (DWORD) -1;
+
+ ENTRY("PAL_EntryPoint(lpStartAddress=%p, lpParameter=%p)\n", lpStartAddress, lpParameter);
+
+ pThread = InternalGetCurrentThread();
+ if (NULL == pThread)
+ {
+ /* This function works only for thread that called PAL_Initialize for now. */
+ ERROR( "Unable to get the thread object.\n" );
+ goto done;
+ }
+
+ retval = (*lpStartAddress)(lpParameter);
+
+done:
+ LOGEXIT("PAL_EntryPoint returns int %d\n", retval);
+ return retval;
+}
+
+/*++
+Function:
+ PAL_Shutdown
+
+Abstract:
+ This function shuts down the PAL WITHOUT exiting the current process.
+--*/
+void
+PALAPI
+PAL_Shutdown(
+ void)
+{
+ TerminateCurrentProcessNoExit(FALSE /* bTerminateUnconditionally */);
+}
+
+/*++
+Function:
+ PAL_Terminate
+
+Abstract:
+ This function is the called when a thread has finished using the PAL
+ library. It shuts down PAL and exits the current process.
+--*/
+void
+PALAPI
+PAL_Terminate(
+ void)
+{
+ PAL_TerminateEx(0);
+}
+
+/*++
+Function:
+PAL_TerminateEx
+
+Abstract:
+This function is the called when a thread has finished using the PAL
+library. It shuts down PAL and exits the current process with
+the specified exit code.
+--*/
+void
+PALAPI
+PAL_TerminateEx(
+ int exitCode)
+{
+ ENTRY_EXTERNAL("PAL_TerminateEx()\n");
+
+ if (NULL == init_critsec)
+ {
+ /* note that these macros probably won't output anything, since the
+ debug channels haven't been initialized yet */
+ ASSERT("PAL_Initialize has never been called!\n");
+ LOGEXIT("PAL_Terminate returns.\n");
+ }
+
+ // Declare the beginning of shutdown
+ PALSetShutdownIntent();
+
+ LOGEXIT("PAL_TerminateEx is exiting the current process.\n");
+ exit(exitCode);
+}
+
+/*++
+Function:
+ PAL_InitializeDebug
+
+Abstract:
+ This function is the called when cordbg attaches to the process.
+--*/
+void
+PALAPI
+PAL_InitializeDebug(
+ void)
+{
+ PERF_ENTRY(PAL_InitializeDebug);
+ ENTRY("PAL_InitializeDebug()\n");
+#if HAVE_MACH_EXCEPTIONS
+ MachExceptionInitializeDebug();
+#endif
+ LOGEXIT("PAL_InitializeDebug returns\n");
+ PERF_EXIT(PAL_InitializeDebug);
+}
+
+/*++
+Function:
+ PALIsThreadDataInitialized
+
+Returns TRUE if startup has reached a point where thread data is available
+--*/
+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();
+
+ SharedMemoryManager::StaticClose();
+
+#ifdef _DEBUG
+ PROCDumpThreadList();
+#endif
+ }
+
+ // Mark that the PAL is uninitialized
+ init_count = 0;
+}
+
+BOOL PALIsShuttingDown()
+{
+ /* ROTORTODO: 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()
+{
+ /* ROTORTODO: See comment in PALIsShuttingDown */
+ shutdown_intent = TRUE;
+}
+
+/*++
+Function:
+ PALInitLock
+
+Take the initializaiton critical section (init_critsec). necessary to serialize
+TerminateProcess along with PAL_Terminate and PAL_Initialize
+
+(no parameters)
+
+Return value :
+ TRUE if critical section existed (and was acquired)
+ FALSE if critical section doens't exist yet
+--*/
+BOOL PALInitLock(void)
+{
+ if(!init_critsec)
+ {
+ return FALSE;
+ }
+
+ CPalThread * pThread =
+ (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : NULL);
+
+ InternalEnterCriticalSection(pThread, init_critsec);
+ return TRUE;
+}
+
+/*++
+Function:
+ PALInitUnlock
+
+Release the initialization critical section (init_critsec).
+
+(no parameters, no return value)
+--*/
+void PALInitUnlock(void)
+{
+ if(!init_critsec)
+ {
+ return;
+ }
+
+ CPalThread * pThread =
+ (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : NULL);
+
+ InternalLeaveCriticalSection(pThread, init_critsec);
+}
+
+/* Internal functions *********************************************************/
+
+/*++
+Function:
+ INIT_IncreaseDescriptorLimit [internal]
+
+Abstract:
+ Calls setrlimit(2) to increase the maximum number of file descriptors
+ this process can open.
+
+Return value:
+ TRUE if the call to setrlimit succeeded; FALSE otherwise.
+--*/
+static BOOL INIT_IncreaseDescriptorLimit(void)
+{
+ struct rlimit rlp;
+ int result;
+
+ result = getrlimit(RLIMIT_NOFILE, &rlp);
+ if (result != 0)
+ {
+ return FALSE;
+ }
+ // Set our soft limit for file descriptors to be the same
+ // as the max limit.
+ rlp.rlim_cur = rlp.rlim_max;
+ result = setrlimit(RLIMIT_NOFILE, &rlp);
+ if (result != 0)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*++
+Function:
+ INIT_FormatCommandLine [Internal]
+
+Abstract:
+ This function converts an array of arguments (argv) into a Unicode
+ command-line for use by GetCommandLineW
+
+Parameters :
+ int argc : number of arguments in argv
+ char **argv : argument list in an array of NULL-terminated strings
+
+Return value :
+ pointer to Unicode command line. This is a buffer allocated with malloc;
+ caller is responsible for freeing it with free()
+
+Note : not all peculiarities of Windows command-line processing are supported;
+
+-what is supported :
+ -arguments with white-space must be double quoted (we'll just double-quote
+ all arguments to simplify things)
+ -some characters must be escaped with \ : particularly, the double-quote,
+ to avoid confusion with the double-quotes at the start and end of
+ arguments, and \ itself, to avoid confusion with escape sequences.
+-what is not supported:
+ -under Windows, \\ is interpreted as an escaped \ ONLY if it's followed by
+ an escaped double-quote \". \\\" is passed to argv as \", but \\a is
+ passed to argv as \\a... there may be other similar cases
+ -there may be other characters which must be escaped
+--*/
+static LPWSTR INIT_FormatCommandLine (int argc, const char * const *argv)
+{
+ LPWSTR retval;
+ LPSTR command_line=NULL, command_ptr;
+ LPCSTR arg_ptr;
+ INT length, i,j;
+ BOOL bQuoted = FALSE;
+
+ /* list of characters that need no be escaped with \ when building the
+ command line. currently " and \ */
+ LPCSTR ESCAPE_CHARS="\"\\";
+
+ /* allocate temporary memory for the string. Play it safe :
+ double the length of each argument (in case they're composed
+ exclusively of escaped characters), and add 3 (for the double-quotes
+ and separating space). This is temporary anyway, we return a LPWSTR */
+ length=0;
+ for(i=0; i<argc; i++)
+ {
+ TRACE("argument %d is %s\n", i, argv[i]);
+ length+=3;
+ length+=strlen(argv[i])*2;
+ }
+ command_line = reinterpret_cast<LPSTR>(InternalMalloc(length));
+
+ if(!command_line)
+ {
+ ERROR("couldn't allocate memory for command line!\n");
+ return NULL;
+ }
+
+ command_ptr=command_line;
+ for(i=0; i<argc; i++)
+ {
+ /* double-quote at beginning of argument containing at least one space */
+ for(j = 0; (argv[i][j] != 0) && (!isspace((unsigned char) argv[i][j])); j++);
+
+ if (argv[i][j] != 0)
+ {
+ *command_ptr++='"';
+ bQuoted = TRUE;
+ }
+ /* process the argument one character at a time */
+ for(arg_ptr=argv[i]; *arg_ptr; arg_ptr++)
+ {
+ /* if character needs to be escaped, prepend a \ to it. */
+ if( strchr(ESCAPE_CHARS,*arg_ptr))
+ {
+ *command_ptr++='\\';
+ }
+
+ /* now we can copy the actual character over. */
+ *command_ptr++=*arg_ptr;
+ }
+ /* double-quote at end of argument; space to separate arguments */
+ if (bQuoted == TRUE)
+ {
+ *command_ptr++='"';
+ bQuoted = FALSE;
+ }
+ *command_ptr++=' ';
+ }
+ /* replace the last space with a NULL terminator */
+ command_ptr--;
+ *command_ptr='\0';
+
+ /* convert to Unicode */
+ i = MultiByteToWideChar(CP_ACP, 0,command_line, -1, NULL, 0);
+ if (i == 0)
+ {
+ ASSERT("MultiByteToWideChar failure\n");
+ free(command_line);
+ return NULL;
+ }
+
+ retval = reinterpret_cast<LPWSTR>(InternalMalloc((sizeof(WCHAR)*i)));
+ if(retval == NULL)
+ {
+ ERROR("can't allocate memory for Unicode command line!\n");
+ free(command_line);
+ return NULL;
+ }
+
+ if(!MultiByteToWideChar(CP_ACP, 0,command_line, i, retval, i))
+ {
+ ASSERT("MultiByteToWideChar failure\n");
+ free(retval);
+ retval = NULL;
+ }
+ else
+ TRACE("Command line is %s\n", command_line);
+
+ free(command_line);
+ return retval;
+}
+
+/*++
+Function:
+ INIT_FindEXEPath
+
+Abstract:
+ Determine the full, canonical path of the current executable by searching
+ $PATH.
+
+Parameters:
+ LPCSTR exe_name : file to search for
+
+Return:
+ pointer to buffer containing the full path. This buffer must be released
+ by the caller using free()
+
+Notes :
+ this function assumes that "exe_name" is in Unix style (no \)
+
+Notes 2:
+ This doesn't handle the case of directories with the desired name
+ (and directories are usually executable...)
+--*/
+static LPWSTR INIT_FindEXEPath(LPCSTR exe_name)
+{
+#ifndef __APPLE__
+ PathCharString real_path;
+ LPSTR env_path;
+ LPSTR path_ptr;
+ LPSTR cur_dir;
+ INT exe_name_length;
+ BOOL need_slash;
+ LPWSTR return_value;
+ INT return_size;
+ struct stat theStats;
+ /* if a path is specified, only search there */
+ if (strchr(exe_name, '/'))
+ {
+ if ( -1 == stat( exe_name, &theStats ) )
+ {
+ ERROR( "The file does not exist\n" );
+ return NULL;
+ }
+
+ if ( UTIL_IsExecuteBitsSet( &theStats ) )
+ {
+ if (!CorUnix::RealPathHelper(exe_name, real_path))
+ {
+ ERROR("realpath() failed!\n");
+ return NULL;
+ }
+
+ return_size=MultiByteToWideChar(CP_ACP,0,real_path,-1,NULL,0);
+ if ( 0 == return_size )
+ {
+ ASSERT("MultiByteToWideChar failure\n");
+ return NULL;
+ }
+
+ return_value = reinterpret_cast<LPWSTR>(InternalMalloc((return_size*sizeof(WCHAR))));
+ if ( NULL == return_value )
+ {
+ ERROR("Not enough memory to create full path\n");
+ return NULL;
+ }
+ else
+ {
+ if (!MultiByteToWideChar(CP_ACP, 0, real_path, -1,
+ return_value, return_size))
+ {
+ ASSERT("MultiByteToWideChar failure\n");
+ free(return_value);
+ return_value = NULL;
+ }
+ else
+ {
+ TRACE("full path to executable is %s\n", real_path.GetString());
+ }
+ }
+ return return_value;
+ }
+ }
+
+ /* no path was specified : search $PATH */
+
+ env_path = EnvironGetenv("PATH");
+ if (!env_path || *env_path=='\0')
+ {
+ WARN("$PATH isn't set.\n");
+ if (env_path != NULL)
+ {
+ free(env_path);
+ }
+
+ goto last_resort;
+ }
+
+ exe_name_length=strlen(exe_name);
+
+ cur_dir=env_path;
+
+ while (cur_dir)
+ {
+ LPSTR full_path;
+ struct stat theStats;
+
+ /* skip all leading ':' */
+ while (*cur_dir==':')
+ {
+ cur_dir++;
+ }
+ if (*cur_dir=='\0')
+ {
+ break;
+ }
+
+ /* cut string at next ':' */
+ path_ptr = strchr(cur_dir, ':');
+ if (path_ptr)
+ {
+ /* check if we need to add a '/' between the path and filename */
+ need_slash=(*(path_ptr-1))!='/';
+
+ /* NULL_terminate path element */
+ *path_ptr++='\0';
+ }
+ else
+ {
+ /* check if we need to add a '/' between the path and filename */
+ need_slash=(cur_dir[strlen(cur_dir)-1])!='/';
+ }
+
+ TRACE("looking for %s in %s\n", exe_name, cur_dir);
+
+ /* build tentative full file name */
+ int iLength = (strlen(cur_dir)+exe_name_length+2);
+ full_path = reinterpret_cast<LPSTR>(InternalMalloc(iLength));
+ if (!full_path)
+ {
+ ERROR("Not enough memory!\n");
+ break;
+ }
+
+ if (strcpy_s(full_path, iLength, cur_dir) != SAFECRT_SUCCESS)
+ {
+ ERROR("strcpy_s failed!\n");
+ free(full_path);
+ free(env_path);
+ return NULL;
+ }
+
+ if (need_slash)
+ {
+ if (strcat_s(full_path, iLength, "/") != SAFECRT_SUCCESS)
+ {
+ ERROR("strcat_s failed!\n");
+ free(full_path);
+ free(env_path);
+ return NULL;
+ }
+ }
+
+ if (strcat_s(full_path, iLength, exe_name) != SAFECRT_SUCCESS)
+ {
+ ERROR("strcat_s failed!\n");
+ free(full_path);
+ free(env_path);
+ return NULL;
+ }
+
+ /* see if file exists AND is executable */
+ if ( -1 != stat( full_path, &theStats ) )
+ {
+ if( UTIL_IsExecuteBitsSet( &theStats ) )
+ {
+ /* generate canonical path */
+ if (!CorUnix::RealPathHelper(full_path, real_path))
+ {
+ ERROR("realpath() failed!\n");
+ free(full_path);
+ free(env_path);
+ return NULL;
+ }
+ free(full_path);
+
+ return_size = MultiByteToWideChar(CP_ACP,0,real_path,-1,NULL,0);
+ if ( 0 == return_size )
+ {
+ ASSERT("MultiByteToWideChar failure\n");
+ free(env_path);
+ return NULL;
+ }
+
+ return_value = reinterpret_cast<LPWSTR>(InternalMalloc((return_size*sizeof(WCHAR))));
+ if ( NULL == return_value )
+ {
+ ERROR("Not enough memory to create full path\n");
+ free(env_path);
+ return NULL;
+ }
+
+ if (!MultiByteToWideChar(CP_ACP, 0, real_path, -1, return_value,
+ return_size))
+ {
+ ASSERT("MultiByteToWideChar failure\n");
+ free(return_value);
+ return_value = NULL;
+ }
+ else
+ {
+ TRACE("found %s in %s; real path is %s\n", exe_name,
+ cur_dir,real_path.GetString());
+ }
+
+ free(env_path);
+ return return_value;
+ }
+ }
+
+ /* file doesn't exist : keep searching */
+ free(full_path);
+
+ /* path_ptr is NULL if there's no ':' after this directory */
+ cur_dir=path_ptr;
+ }
+
+ free(env_path);
+ TRACE("No %s found in $PATH (%s)\n", exe_name, EnvironGetenv("PATH", FALSE));
+
+last_resort:
+ /* last resort : see if the executable is in the current directory. This is
+ possible if it comes from a exec*() call. */
+ if (0 == stat(exe_name,&theStats))
+ {
+ if ( UTIL_IsExecuteBitsSet( &theStats ) )
+ {
+ if (!CorUnix::RealPathHelper(exe_name, real_path))
+ {
+ ERROR("realpath() failed!\n");
+ return NULL;
+ }
+
+ return_size = MultiByteToWideChar(CP_ACP,0,real_path,-1,NULL,0);
+ if (0 == return_size)
+ {
+ ASSERT("MultiByteToWideChar failure\n");
+ return NULL;
+ }
+
+ return_value = reinterpret_cast<LPWSTR>(InternalMalloc((return_size*sizeof(WCHAR))));
+ if (NULL == return_value)
+ {
+ ERROR("Not enough memory to create full path\n");
+ return NULL;
+ }
+ else
+ {
+ if (!MultiByteToWideChar(CP_ACP, 0, real_path, -1,
+ return_value, return_size))
+ {
+ ASSERT("MultiByteToWideChar failure\n");
+ free(return_value);
+ return_value = NULL;
+ }
+ else
+ {
+ TRACE("full path to executable is %s\n", real_path.GetString());
+ }
+ }
+
+ return return_value;
+ }
+ else
+ {
+ ERROR("found %s in current directory, but it isn't executable!\n",
+ exe_name);
+ }
+ }
+ else
+ {
+ TRACE("last resort failed : executable %s is not in the current "
+ "directory\n",exe_name);
+ }
+
+ ERROR("executable %s not found anywhere!\n", exe_name);
+ return NULL;
+#else // !__APPLE__
+ // On the Mac we can just directly ask the OS for the executable path.
+
+ LPWSTR return_value;
+ INT return_size;
+
+ PathCharString exec_pathPS;
+ LPSTR exec_path = exec_pathPS.OpenStringBuffer(MAX_PATH);
+ uint32_t bufsize = exec_pathPS.GetCount();
+
+ if (-1 == _NSGetExecutablePath(exec_path, &bufsize))
+ {
+ exec_pathPS.CloseBuffer(exec_pathPS.GetCount());
+ exec_path = exec_pathPS.OpenStringBuffer(bufsize);
+ }
+
+ if (_NSGetExecutablePath(exec_path, &bufsize))
+ {
+ ASSERT("_NSGetExecutablePath failure\n");
+ return NULL;
+ }
+
+ exec_pathPS.CloseBuffer(bufsize);
+
+ return_size = MultiByteToWideChar(CP_ACP,0,exec_path,-1,NULL,0);
+ if (0 == return_size)
+ {
+ ASSERT("MultiByteToWideChar failure\n");
+ return NULL;
+ }
+
+ return_value = reinterpret_cast<LPWSTR>(InternalMalloc((return_size*sizeof(WCHAR))));
+ if (NULL == return_value)
+ {
+ ERROR("Not enough memory to create full path\n");
+ return NULL;
+ }
+ else
+ {
+ if (!MultiByteToWideChar(CP_ACP, 0, exec_path, -1,
+ return_value, return_size))
+ {
+ ASSERT("MultiByteToWideChar failure\n");
+ free(return_value);
+ return_value = NULL;
+ }
+ else
+ {
+ TRACE("full path to executable is %s\n", exec_path);
+ }
+ }
+
+ return return_value;
+#endif // !__APPLE__
+}
diff --git a/src/pal/src/init/sxs.cpp b/src/pal/src/init/sxs.cpp
new file mode 100644
index 0000000000..225f91684b
--- /dev/null
+++ b/src/pal/src/init/sxs.cpp
@@ -0,0 +1,322 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+
+
+--*/
+
+#include "pal/dbgmsg.h"
+#include "pal/thread.hpp"
+#include "../thread/procprivate.hpp"
+#include "pal/module.h"
+#include "pal/process.h"
+#include "pal/seh.hpp"
+
+using namespace CorUnix;
+
+#ifdef FEATURE_PAL_SXS
+
+SET_DEFAULT_DEBUG_CHANNEL(SXS);
+
+PAL_ERROR AllocatePalThread(CPalThread **ppThread);
+
+/************************* Enter *************************/
+
+/*++
+Function:
+ PAL_Enter
+
+Abstract:
+ This function needs to be called on a thread when it enters
+ a region of code that depends on this instance of the PAL
+ in the process, and the current thread may or may not be
+ known to the PAL. This function can fail (for something else
+ than an internal error) if this is the first time that the
+ current thread entered this PAL. Note that PAL_Initialize
+ implies a call to this function.
+
+ NOTE: This function must not modify LastError.
+--*/
+PAL_ERROR
+PALAPI
+PAL_Enter(PAL_Boundary boundary)
+{
+ ENTRY_EXTERNAL("PAL_Enter(boundary=%u)\n", boundary);
+
+ PAL_ERROR palError = ERROR_SUCCESS;
+ CPalThread *pThread = GetCurrentPalThread();
+ if (pThread != NULL)
+ {
+ palError = pThread->Enter(boundary);
+ }
+ else
+ {
+ // If this assert fires, we'll have to pipe this information so that
+ // CPalThread's RunPostCreateInitializers call to SEHEnable
+ // can know what direction.
+ _ASSERT_MSG(PAL_BoundaryTop == boundary, "How are we entering a PAL "
+ "thread for the first time not from the top? (boundary=%u)", boundary);
+
+ palError = AllocatePalThread(&pThread);
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to allocate pal thread: error %d\n", palError);
+ }
+ }
+
+ LOGEXIT("PAL_Enter returns %d\n", palError);
+ return palError;
+}
+
+/*++
+Function:
+ CreateCurrentThreadData
+
+Abstract:
+ This function is called by the InternalGetOrCreateCurrentThread inlined
+ function to create the thread data when it is null meaning the thread has
+ never been in this PAL.
+
+Warning:
+ If the allocation fails, this function asserts and exits the process.
+--*/
+extern "C" CPalThread *
+CreateCurrentThreadData()
+{
+ CPalThread *pThread = NULL;
+
+ if (PALIsThreadDataInitialized()) {
+ PAL_ERROR palError = AllocatePalThread(&pThread);
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to allocate pal thread: error %d - aborting\n", palError);
+ PROCAbort();
+ }
+ }
+
+ return pThread;
+}
+
+PAL_ERROR
+AllocatePalThread(CPalThread **ppThread)
+{
+ CPalThread *pThread = NULL;
+
+ PAL_ERROR palError = CreateThreadData(&pThread);
+ if (NO_ERROR != palError)
+ {
+ 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;
+}
+
+PALIMPORT
+DWORD
+PALAPI
+PAL_EnterTop()
+{
+ return PAL_Enter(PAL_BoundaryTop);
+}
+
+
+/*++
+Function:
+ PAL_Reenter
+
+Abstract:
+ This function needs to be called on a thread when it enters
+ a region of code that depends on this instance of the PAL
+ in the process, and the current thread is already known to
+ the PAL.
+
+ NOTE: This function must not modify LastError.
+--*/
+VOID
+PALAPI
+PAL_Reenter(PAL_Boundary boundary)
+{
+ ENTRY_EXTERNAL("PAL_Reenter(boundary=%u)\n", boundary);
+
+ CPalThread *pThread = GetCurrentPalThread();
+ if (pThread == NULL)
+ {
+ ASSERT("PAL_Reenter called on a thread unknown to this PAL\n");
+ }
+
+ // We ignore the return code. This call should only fail on internal
+ // error, and we assert at the actual failure.
+ pThread->Enter(boundary);
+
+ LOGEXIT("PAL_Reenter returns\n");
+}
+
+/*++
+Function:
+ PAL_HasEntered
+
+Abstract:
+ This function can be called to determine if the thread has entered the
+ PAL through PAL_Enter or related calls.
+--*/
+BOOL
+PALAPI
+PAL_HasEntered()
+{
+ ENTRY_EXTERNAL("PAL_HasEntered()\n");
+
+ CPalThread *pThread = GetCurrentPalThread();
+ if (pThread == NULL)
+ {
+ ASSERT("PAL_Reenter called on a thread unknown to this PAL\n");
+ }
+
+ LOGEXIT("PAL_HasEntered returned\n");
+
+ return pThread->IsInPal();
+}
+
+/*++
+Function:
+ PAL_ReenterForEH
+
+Abstract:
+ This function needs to be called on a thread when it enters
+ a region of code that depends on this instance of the PAL
+ in the process, and it is unknown whether the current thread
+ is already running in the PAL. Returns TRUE if and only if
+ the thread was not running in the PAL previously.
+
+ NOTE: This function must not modify LastError.
+--*/
+BOOL
+PALAPI
+PAL_ReenterForEH()
+{
+ // Only trace if we actually reenter (otherwise, too verbose)
+ // ENTRY_EXTERNAL("PAL_ReenterForEH()\n");
+ // Thus we have to split up what ENTRY_EXTERNAL does.
+ CHECK_STACK_ALIGN;
+
+ BOOL fEntered = FALSE;
+
+ CPalThread *pThread = GetCurrentPalThread();
+ if (pThread == NULL)
+ {
+ ASSERT("PAL_ReenterForEH called on a thread unknown to this PAL\n");
+ }
+ else if (!pThread->IsInPal())
+ {
+#if _ENABLE_DEBUG_MESSAGES_
+ DBG_PRINTF(DLI_ENTRY, defdbgchan, TRUE)("PAL_ReenterForEH()\n");
+#endif
+
+ // We ignore the return code. This call should only fail on internal
+ // error, and we assert at the actual failure.
+ pThread->Enter(PAL_BoundaryEH);
+ fEntered = TRUE;
+ LOGEXIT("PAL_ReenterForEH returns TRUE\n");
+ }
+ else
+ {
+ // LOGEXIT("PAL_ReenterForEH returns FALSE\n");
+ }
+
+ return fEntered;
+}
+
+PAL_ERROR CPalThread::Enter(PAL_Boundary /* boundary */)
+{
+ if (m_fInPal)
+ {
+ WARN("Enter called on a thread that already runs in this PAL\n");
+ return NO_ERROR;
+ }
+ m_fInPal = TRUE;
+
+ return ERROR_SUCCESS;
+}
+
+
+/************************* Leave *************************/
+
+/*++
+Function:
+ PAL_Leave
+
+Abstract:
+ This function needs to be called on a thread when it leaves a region
+ of code that depends on this instance of the PAL in the process.
+
+ NOTE: This function must not modify LastError.
+--*/
+VOID
+PALAPI
+PAL_Leave(PAL_Boundary boundary)
+{
+ ENTRY("PAL_Leave(boundary=%u)\n", boundary);
+
+ CPalThread *pThread = GetCurrentPalThread();
+ // We ignore the return code. This call should only fail on internal
+ // error, and we assert at the actual failure.
+ pThread->Leave(boundary);
+
+ LOGEXIT("PAL_Leave returns\n");
+}
+
+PALIMPORT
+VOID
+PALAPI
+PAL_LeaveBottom()
+{
+ PAL_Leave(PAL_BoundaryBottom);
+}
+
+PALIMPORT
+VOID
+PALAPI
+PAL_LeaveTop()
+{
+ PAL_Leave(PAL_BoundaryTop);
+}
+
+
+PAL_ERROR CPalThread::Leave(PAL_Boundary /* boundary */)
+{
+ if (!m_fInPal)
+ {
+ WARN("Leave called on a thread that is not running in this PAL\n");
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ PAL_ERROR palError = ERROR_SUCCESS;
+
+ m_fInPal = FALSE;
+
+ return palError;
+}
+
+#endif // FEATURE_PAL_SXS
diff --git a/src/pal/src/loader/module.cpp b/src/pal/src/loader/module.cpp
new file mode 100644
index 0000000000..a4fc4949e4
--- /dev/null
+++ b/src/pal/src/loader/module.cpp
@@ -0,0 +1,1771 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ module.c
+
+Abstract:
+
+ Implementation of module related functions in the Win32 API
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+#include "pal/file.hpp"
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/module.h"
+#include "pal/cs.hpp"
+#include "pal/process.h"
+#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"
+#include "pal/stackstring.hpp"
+
+#include <sys/param.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#if NEED_DLCOMPAT
+#include "dlcompat.h"
+#else // NEED_DLCOMPAT
+#include <dlfcn.h>
+#endif // NEED_DLCOMPAT
+#include <stdlib.h>
+
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#include <mach-o/loader.h>
+#endif // __APPLE__
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#if HAVE_GNU_LIBNAMES_H
+#include <gnu/lib-names.h>
+#endif
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(LOADER);
+
+// In safemath.h, Template SafeInt uses macro _ASSERTE, which need to use variable
+// defdbgchan defined by SET_DEFAULT_DEBUG_CHANNEL. Therefore, the include statement
+// should be placed after the SET_DEFAULT_DEBUG_CHANNEL(LOADER)
+#include <safemath.h>
+
+/* macro definitions **********************************************************/
+
+/* get the full name of a module if available, and the short name otherwise*/
+#define MODNAME(x) ((x)->lib_name)
+
+/* Which path should FindLibrary search? */
+#if defined(__APPLE__)
+#define LIBSEARCHPATH "DYLD_LIBRARY_PATH"
+#else
+#define LIBSEARCHPATH "LD_LIBRARY_PATH"
+#endif
+
+#define LIBC_NAME_WITHOUT_EXTENSION "libc"
+
+/* static variables ***********************************************************/
+
+/* critical section that regulates access to the module list */
+CRITICAL_SECTION module_critsec;
+
+/* always the first, in the in-load-order list */
+MODSTRUCT exe_module;
+MODSTRUCT *pal_module = nullptr;
+
+char * g_szCoreCLRPath = nullptr;
+
+int MaxWCharToAcpLength = 3;
+
+/* static function declarations ***********************************************/
+
+template<class TChar> static bool LOADVerifyLibraryPath(const TChar *libraryPath);
+static bool LOADConvertLibraryPathWideStringToMultibyteString(
+ LPCWSTR wideLibraryPath,
+ LPSTR multibyteLibraryPath,
+ INT *multibyteLibraryPathLengthRef);
+static BOOL LOADValidateModule(MODSTRUCT *module);
+static LPWSTR LOADGetModuleFileName(MODSTRUCT *module);
+static MODSTRUCT *LOADAddModule(void *dl_handle, LPCSTR libraryNameOrPath);
+static void *LOADLoadLibraryDirect(LPCSTR libraryNameOrPath);
+static BOOL LOADFreeLibrary(MODSTRUCT *module, BOOL fCallDllMain);
+static HMODULE LOADRegisterLibraryDirect(void *dl_handle, LPCSTR libraryNameOrPath, BOOL fDynamic);
+static HMODULE LOADLoadLibrary(LPCSTR shortAsciiName, BOOL fDynamic);
+static BOOL LOADCallDllMainSafe(MODSTRUCT *module, DWORD dwReason, LPVOID lpReserved);
+
+/* API function definitions ***************************************************/
+
+/*++
+Function:
+ LoadLibraryA
+
+See MSDN doc.
+--*/
+HMODULE
+PALAPI
+LoadLibraryA(
+ IN LPCSTR lpLibFileName)
+{
+ return LoadLibraryExA(lpLibFileName, nullptr, 0);
+}
+
+/*++
+Function:
+ LoadLibraryW
+
+See MSDN doc.
+--*/
+HMODULE
+PALAPI
+LoadLibraryW(
+ IN LPCWSTR lpLibFileName)
+{
+ return LoadLibraryExW(lpLibFileName, nullptr, 0);
+}
+
+/*++
+Function:
+LoadLibraryExA
+
+See MSDN doc.
+--*/
+HMODULE
+PALAPI
+LoadLibraryExA(
+ IN LPCSTR lpLibFileName,
+ IN /*Reserved*/ HANDLE hFile,
+ IN DWORD dwFlags)
+{
+ if (dwFlags != 0)
+ {
+ // UNIXTODO: Implement this!
+ ASSERT("Needs Implementation!!!");
+ return nullptr;
+ }
+
+ LPSTR lpstr = nullptr;
+ HMODULE hModule = nullptr;
+
+ PERF_ENTRY(LoadLibraryA);
+ ENTRY("LoadLibraryExA (lpLibFileName=%p (%s)) \n",
+ (lpLibFileName) ? lpLibFileName : "NULL",
+ (lpLibFileName) ? lpLibFileName : "NULL");
+
+ if (!LOADVerifyLibraryPath(lpLibFileName))
+ {
+ goto Done;
+ }
+
+ /* do the Dos/Unix conversion on our own copy of the name */
+ lpstr = strdup(lpLibFileName);
+ if (!lpstr)
+ {
+ ERROR("strdup failure!\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto Done;
+ }
+ FILEDosToUnixPathA(lpstr);
+
+ hModule = LOADLoadLibrary(lpstr, TRUE);
+
+ /* let LOADLoadLibrary call SetLastError */
+ Done:
+ if (lpstr != nullptr)
+ {
+ free(lpstr);
+ }
+
+ LOGEXIT("LoadLibraryExA returns HMODULE %p\n", hModule);
+ PERF_EXIT(LoadLibraryExA);
+ return hModule;
+
+}
+
+/*++
+Function:
+LoadLibraryExW
+
+See MSDN doc.
+--*/
+HMODULE
+PALAPI
+LoadLibraryExW(
+ IN LPCWSTR lpLibFileName,
+ IN /*Reserved*/ HANDLE hFile,
+ IN DWORD dwFlags)
+{
+ if (dwFlags != 0)
+ {
+ // UNIXTODO: Implement this!
+ ASSERT("Needs Implementation!!!");
+ return nullptr;
+ }
+
+ CHAR * lpstr;
+ INT name_length;
+ PathCharString pathstr;
+ HMODULE hModule = nullptr;
+
+ PERF_ENTRY(LoadLibraryExW);
+ ENTRY("LoadLibraryExW (lpLibFileName=%p (%S)) \n",
+ lpLibFileName ? lpLibFileName : W16_NULLSTRING,
+ lpLibFileName ? lpLibFileName : W16_NULLSTRING);
+
+ if (!LOADVerifyLibraryPath(lpLibFileName))
+ {
+ goto done;
+ }
+
+ lpstr = pathstr.OpenStringBuffer((PAL_wcslen(lpLibFileName)+1) * MaxWCharToAcpLength);
+ if (nullptr == lpstr)
+ {
+ goto done;
+ }
+ if (!LOADConvertLibraryPathWideStringToMultibyteString(lpLibFileName, lpstr, &name_length))
+ {
+ goto done;
+ }
+
+ /* do the Dos/Unix conversion on our own copy of the name */
+ FILEDosToUnixPathA(lpstr);
+ pathstr.CloseBuffer(name_length);
+
+ /* let LOADLoadLibrary call SetLastError in case of failure */
+ hModule = LOADLoadLibrary(lpstr, TRUE);
+
+done:
+ LOGEXIT("LoadLibraryExW returns HMODULE %p\n", hModule);
+ PERF_EXIT(LoadLibraryExW);
+ return hModule;
+}
+
+/*++
+Function:
+ GetProcAddress
+
+See MSDN doc.
+--*/
+FARPROC
+PALAPI
+GetProcAddress(
+ IN HMODULE hModule,
+ IN LPCSTR lpProcName)
+{
+ MODSTRUCT *module;
+ FARPROC ProcAddress = nullptr;
+ LPCSTR symbolName = lpProcName;
+
+ PERF_ENTRY(GetProcAddress);
+ ENTRY("GetProcAddress (hModule=%p, lpProcName=%p (%s))\n",
+ hModule, lpProcName ? lpProcName : "NULL", lpProcName ? lpProcName : "NULL");
+
+ LockModuleList();
+
+ module = (MODSTRUCT *) hModule;
+
+ /* parameter validation */
+
+ if ((lpProcName == nullptr) || (*lpProcName == '\0'))
+ {
+ TRACE("No function name given\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ if (!LOADValidateModule(module))
+ {
+ TRACE("Invalid module handle %p\n", hModule);
+ SetLastError(ERROR_INVALID_HANDLE);
+ goto done;
+ }
+
+ /* try to assert on attempt to locate symbol by ordinal */
+ /* this can't be an exact test for HIWORD((DWORD)lpProcName) == 0
+ because of the address range reserved for ordinals contain can
+ be a valid string address on non-Windows systems
+ */
+ if ((DWORD_PTR)lpProcName < VIRTUAL_PAGE_SIZE)
+ {
+ ASSERT("Attempt to locate symbol by ordinal?!\n");
+ }
+
+ // Get the symbol's address.
+
+ // If we're looking for a symbol inside the PAL, we try the PAL_ variant
+ // first because otherwise we run the risk of having the non-PAL_
+ // variant preferred over the PAL's implementation.
+ if (pal_module && module->dl_handle == pal_module->dl_handle)
+ {
+ int iLen = 4 + strlen(lpProcName) + 1;
+ LPSTR lpPALProcName = (LPSTR) alloca(iLen);
+
+ if (strcpy_s(lpPALProcName, iLen, "PAL_") != SAFECRT_SUCCESS)
+ {
+ ERROR("strcpy_s failed!\n");
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ goto done;
+ }
+
+ if (strcat_s(lpPALProcName, iLen, lpProcName) != SAFECRT_SUCCESS)
+ {
+ ERROR("strcat_s failed!\n");
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ goto done;
+ }
+
+ ProcAddress = (FARPROC) dlsym(module->dl_handle, lpPALProcName);
+ symbolName = lpPALProcName;
+ }
+
+ // If we aren't looking inside the PAL or we didn't find a PAL_ variant
+ // inside the PAL, fall back to a normal search.
+ if (ProcAddress == nullptr)
+ {
+ ProcAddress = (FARPROC) dlsym(module->dl_handle, lpProcName);
+ }
+
+ if (ProcAddress)
+ {
+ TRACE("Symbol %s found at address %p in module %p (named %S)\n",
+ lpProcName, ProcAddress, module, MODNAME(module));
+
+ /* 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)
+ {
+ module->lib_name = UTIL_MBToWC_Alloc(libName, -1);
+ if (nullptr == module->lib_name)
+ {
+ ERROR("MBToWC failure; can't save module's full name\n");
+ }
+ else
+ {
+ TRACE("Saving full path of module %p as %s\n",
+ module, libName);
+ }
+ }
+ }
+ }
+ else
+ {
+ TRACE("Symbol %s not found in module %p (named %S), dlerror message is \"%s\"\n",
+ lpProcName, module, MODNAME(module), dlerror());
+ SetLastError(ERROR_PROC_NOT_FOUND);
+ }
+done:
+ UnlockModuleList();
+ LOGEXIT("GetProcAddress returns FARPROC %p\n", ProcAddress);
+ PERF_EXIT(GetProcAddress);
+ return ProcAddress;
+}
+
+/*++
+Function:
+ FreeLibrary
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+FreeLibrary(
+ IN OUT HMODULE hLibModule)
+{
+ BOOL retval = FALSE;
+
+ PERF_ENTRY(FreeLibrary);
+ ENTRY("FreeLibrary (hLibModule=%p)\n", hLibModule);
+
+ retval = LOADFreeLibrary((MODSTRUCT *)hLibModule, TRUE /* fCallDllMain */);
+
+ LOGEXIT("FreeLibrary returns BOOL %d\n", retval);
+ PERF_EXIT(FreeLibrary);
+ return retval;
+}
+
+/*++
+Function:
+ FreeLibraryAndExitThread
+
+See MSDN doc.
+
+--*/
+PALIMPORT
+VOID
+PALAPI
+FreeLibraryAndExitThread(
+ IN HMODULE hLibModule,
+ IN DWORD dwExitCode)
+{
+ PERF_ENTRY(FreeLibraryAndExitThread);
+ ENTRY("FreeLibraryAndExitThread()\n");
+ FreeLibrary(hLibModule);
+ ExitThread(dwExitCode);
+ LOGEXIT("FreeLibraryAndExitThread\n");
+ PERF_EXIT(FreeLibraryAndExitThread);
+}
+
+/*++
+Function:
+ GetModuleFileNameA
+
+See MSDN doc.
+
+Notes :
+ because of limitations in the dlopen() mechanism, this will only return the
+ full path name if a relative or absolute path was given to LoadLibrary, or
+ if the module was used in a GetProcAddress call. otherwise, this will return
+ the short name as given to LoadLibrary. The exception is if hModule is
+ NULL : in this case, the full path of the executable is always returned.
+--*/
+DWORD
+PALAPI
+GetModuleFileNameA(
+ IN HMODULE hModule,
+ OUT LPSTR lpFileName,
+ IN DWORD nSize)
+{
+ INT name_length;
+ DWORD retval = 0;
+ LPWSTR wide_name = nullptr;
+
+ PERF_ENTRY(GetModuleFileNameA);
+ ENTRY("GetModuleFileNameA (hModule=%p, lpFileName=%p, nSize=%u)\n",
+ hModule, lpFileName, nSize);
+
+ LockModuleList();
+ if (hModule && !LOADValidateModule((MODSTRUCT *)hModule))
+ {
+ TRACE("Can't find name for invalid module handle %p\n", hModule);
+ SetLastError(ERROR_INVALID_HANDLE);
+ goto done;
+ }
+ wide_name = LOADGetModuleFileName((MODSTRUCT *)hModule);
+
+ if (!wide_name)
+ {
+ ASSERT("Can't find name for valid module handle %p\n", hModule);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ /* Convert module name to Ascii, place it in the supplied buffer */
+
+ name_length = WideCharToMultiByte(CP_ACP, 0, wide_name, -1, lpFileName,
+ nSize, nullptr, nullptr);
+ if (name_length == 0)
+ {
+ TRACE("Buffer too small to copy module's file name.\n");
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ goto done;
+ }
+
+ TRACE("File name of module %p is %s\n", hModule, lpFileName);
+ retval = name_length;
+done:
+ UnlockModuleList();
+ LOGEXIT("GetModuleFileNameA returns DWORD %d\n", retval);
+ PERF_EXIT(GetModuleFileNameA);
+ return retval;
+}
+
+/*++
+Function:
+ GetModuleFileNameW
+
+See MSDN doc.
+
+Notes :
+ because of limitations in the dlopen() mechanism, this will only return the
+ full path name if a relative or absolute path was given to LoadLibrary, or
+ if the module was used in a GetProcAddress call. otherwise, this will return
+ the short name as given to LoadLibrary. The exception is if hModule is
+ NULL : in this case, the full path of the executable is always returned.
+--*/
+DWORD
+PALAPI
+GetModuleFileNameW(
+ IN HMODULE hModule,
+ OUT LPWSTR lpFileName,
+ IN DWORD nSize)
+{
+ INT name_length;
+ DWORD retval = 0;
+ LPWSTR wide_name = nullptr;
+
+ PERF_ENTRY(GetModuleFileNameW);
+ ENTRY("GetModuleFileNameW (hModule=%p, lpFileName=%p, nSize=%u)\n",
+ hModule, lpFileName, nSize);
+
+ LockModuleList();
+
+ wcscpy_s(lpFileName, nSize, W(""));
+
+ if (hModule && !LOADValidateModule((MODSTRUCT *)hModule))
+ {
+ TRACE("Can't find name for invalid module handle %p\n", hModule);
+ SetLastError(ERROR_INVALID_HANDLE);
+ goto done;
+ }
+ wide_name = LOADGetModuleFileName((MODSTRUCT *)hModule);
+
+ if (!wide_name)
+ {
+ TRACE("Can't find name for valid module handle %p\n", hModule);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ /* Copy module name into supplied buffer */
+
+ name_length = lstrlenW(wide_name);
+ if (name_length >= (INT)nSize)
+ {
+ TRACE("Buffer too small (%u) to copy module's file name (%u).\n", nSize, name_length);
+ retval = (INT)nSize;
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ goto done;
+ }
+
+ wcscpy_s(lpFileName, nSize, wide_name);
+
+ TRACE("file name of module %p is %S\n", hModule, lpFileName);
+ retval = name_length;
+done:
+ UnlockModuleList();
+ LOGEXIT("GetModuleFileNameW returns DWORD %u\n", retval);
+ PERF_EXIT(GetModuleFileNameW);
+ return retval;
+}
+
+/*
+Function:
+ PAL_LoadLibraryDirect
+
+ Loads a library using a system call, without registering the library with the module list.
+
+ Returns the system handle to the loaded library, or nullptr upon failure (error is set via SetLastError()).
+*/
+void *
+PALAPI
+PAL_LoadLibraryDirect(
+ IN LPCWSTR lpLibFileName)
+{
+ PathCharString pathstr;
+ CHAR * lpstr = nullptr;
+ INT name_length;
+ void *dl_handle = nullptr;
+
+ PERF_ENTRY(LoadLibraryDirect);
+ ENTRY("LoadLibraryDirect (lpLibFileName=%p (%S)) \n",
+ lpLibFileName ? lpLibFileName : W16_NULLSTRING,
+ lpLibFileName ? lpLibFileName : W16_NULLSTRING);
+
+ if (!LOADVerifyLibraryPath(lpLibFileName))
+ {
+ goto done;
+ }
+
+ lpstr = pathstr.OpenStringBuffer((PAL_wcslen(lpLibFileName)+1) * MaxWCharToAcpLength);
+ if (nullptr == lpstr)
+ {
+ goto done;
+ }
+ if (!LOADConvertLibraryPathWideStringToMultibyteString(lpLibFileName, lpstr, &name_length))
+ {
+ goto done;
+ }
+
+ /* do the Dos/Unix conversion on our own copy of the name */
+ FILEDosToUnixPathA(lpstr);
+ pathstr.CloseBuffer(name_length);
+
+ dl_handle = LOADLoadLibraryDirect(lpstr);
+
+done:
+ LOGEXIT("LoadLibraryDirect returns HMODULE %p\n", dl_handle);
+ PERF_EXIT(LoadLibraryDirect);
+ return dl_handle;
+}
+
+/*
+Function:
+ PAL_RegisterLibraryDirect
+
+ Registers a system handle to a loaded library with the module list.
+
+ Returns a PAL handle to the loaded library, or nullptr upon failure (error is set via SetLastError()).
+*/
+HMODULE
+PALAPI
+PAL_RegisterLibraryDirect(
+ IN void *dl_handle,
+ IN LPCWSTR lpLibFileName)
+{
+ PathCharString pathstr;
+ CHAR * lpstr = nullptr;
+ INT name_length;
+ HMODULE hModule = nullptr;
+
+ PERF_ENTRY(RegisterLibraryDirect);
+ ENTRY("RegisterLibraryDirect (lpLibFileName=%p (%S)) \n",
+ lpLibFileName ? lpLibFileName : W16_NULLSTRING,
+ lpLibFileName ? lpLibFileName : W16_NULLSTRING);
+
+ if (!LOADVerifyLibraryPath(lpLibFileName))
+ {
+ goto done;
+ }
+
+ lpstr = pathstr.OpenStringBuffer((PAL_wcslen(lpLibFileName)+1) * MaxWCharToAcpLength);
+ if (nullptr == lpstr)
+ {
+ goto done;
+ }
+ if (!LOADConvertLibraryPathWideStringToMultibyteString(lpLibFileName, lpstr, &name_length))
+ {
+ goto done;
+ }
+
+ /* do the Dos/Unix conversion on our own copy of the name */
+ FILEDosToUnixPathA(lpstr);
+ pathstr.CloseBuffer(name_length);
+
+ /* let LOADRegisterLibraryDirect call SetLastError in case of failure */
+ LockModuleList();
+ hModule = LOADRegisterLibraryDirect((void *)dl_handle, lpstr, true /* fDynamic */);
+ UnlockModuleList();
+
+done:
+ LOGEXIT("RegisterLibraryDirect returns HMODULE %p\n", hModule);
+ PERF_EXIT(RegisterLibraryDirect);
+ return hModule;
+}
+
+/*++
+Function:
+ PAL_RegisterModule
+
+ Register the module with the target module and return a module handle in
+ the target module's context. Doesn't call the DllMain because it is used
+ as part of calling DllMain in the calling module.
+
+--*/
+HINSTANCE
+PALAPI
+PAL_RegisterModule(
+ IN LPCSTR lpLibFileName)
+{
+ HINSTANCE hinstance = nullptr;
+
+ int err = PAL_InitializeDLL();
+ if (err == 0)
+ {
+ PERF_ENTRY(PAL_RegisterModule);
+ ENTRY("PAL_RegisterModule(%s)\n", lpLibFileName ? lpLibFileName : "");
+
+ LockModuleList();
+
+ void *dl_handle = LOADLoadLibraryDirect(lpLibFileName);
+ if (dl_handle)
+ {
+ // This only creates/adds the module handle and doesn't call DllMain
+ hinstance = LOADAddModule(dl_handle, lpLibFileName);
+ }
+
+ UnlockModuleList();
+
+ LOGEXIT("PAL_RegisterModule returns HINSTANCE %p\n", hinstance);
+ PERF_EXIT(PAL_RegisterModule);
+ }
+
+ return hinstance;
+}
+
+/*++
+Function:
+ PAL_UnregisterModule
+
+ Used to cleanup the module HINSTANCE from PAL_RegisterModule.
+--*/
+VOID
+PALAPI
+PAL_UnregisterModule(
+ IN HINSTANCE hInstance)
+{
+ PERF_ENTRY(PAL_UnregisterModule);
+ ENTRY("PAL_UnregisterModule(hInstance=%p)\n", hInstance);
+
+ LOADFreeLibrary((MODSTRUCT *)hInstance, FALSE /* fCallDllMain */);
+
+ LOGEXIT("PAL_UnregisterModule returns\n");
+ PERF_EXIT(PAL_UnregisterModule);
+}
+
+/*++
+ PAL_LOADLoadPEFile
+
+ Map a PE format file into memory like Windows LoadLibrary() would do.
+ Doesn't apply base relocations if the function is relocated.
+
+Parameters:
+ IN hFile - file to map
+
+Return value:
+ non-NULL - the base address of the mapped image
+ NULL - error, with last error set.
+--*/
+void *
+PALAPI
+PAL_LOADLoadPEFile(HANDLE hFile)
+{
+ ENTRY("PAL_LOADLoadPEFile (hFile=%p)\n", hFile);
+
+ void * loadedBase = MAPMapPEFile(hFile);
+
+#ifdef _DEBUG
+ if (loadedBase != nullptr)
+ {
+ char* envVar = EnvironGetenv("PAL_ForcePEMapFailure");
+ if (envVar)
+ {
+ if (strlen(envVar) > 0)
+ {
+ TRACE("Forcing failure of PE file map, and retry\n");
+ PAL_LOADUnloadPEFile(loadedBase); // unload it
+ loadedBase = MAPMapPEFile(hFile); // load it again
+ }
+
+ free(envVar);
+ }
+ }
+#endif // _DEBUG
+
+ LOGEXIT("PAL_LOADLoadPEFile returns %p\n", loadedBase);
+ return loadedBase;
+}
+
+/*++
+ PAL_LOADUnloadPEFile
+
+ Unload a PE file that was loaded by PAL_LOADLoadPEFile().
+
+Parameters:
+ IN ptr - the file pointer returned by PAL_LOADLoadPEFile()
+
+Return value:
+ TRUE - success
+ FALSE - failure (incorrect ptr, etc.)
+--*/
+BOOL
+PALAPI
+PAL_LOADUnloadPEFile(void * ptr)
+{
+ BOOL retval = FALSE;
+
+ ENTRY("PAL_LOADUnloadPEFile (ptr=%p)\n", ptr);
+
+ if (nullptr == ptr)
+ {
+ ERROR( "Invalid pointer value\n" );
+ }
+ else
+ {
+ retval = MAPUnmapPEFile(ptr);
+ }
+
+ LOGEXIT("PAL_LOADUnloadPEFile returns %d\n", retval);
+ return retval;
+}
+
+/*++
+ PAL_GetSymbolModuleBase
+
+ Get base address of the module containing a given symbol
+
+Parameters:
+ void *symbol - address of symbol
+
+Return value:
+ module base address
+--*/
+LPCVOID
+PALAPI
+PAL_GetSymbolModuleBase(void *symbol)
+{
+ LPCVOID retval = nullptr;
+
+ PERF_ENTRY(PAL_GetPalModuleBase);
+ ENTRY("PAL_GetPalModuleBase\n");
+
+ if (symbol == nullptr)
+ {
+ TRACE("Can't get base address. Argument symbol == nullptr\n");
+ SetLastError(ERROR_INVALID_DATA);
+ }
+ else
+ {
+ Dl_info info;
+ if (dladdr(symbol, &info) != 0)
+ {
+ retval = info.dli_fbase;
+ }
+ else
+ {
+ TRACE("Can't get base address of the current module\n");
+ SetLastError(ERROR_INVALID_DATA);
+ }
+ }
+
+ LOGEXIT("PAL_GetPalModuleBase returns %p\n", retval);
+ PERF_EXIT(PAL_GetPalModuleBase);
+ return retval;
+}
+
+/* Internal PAL functions *****************************************************/
+
+/*++
+Function :
+ LOADInitializeModules
+
+ Initialize the process-wide list of modules
+
+Parameters :
+ None
+
+Return value :
+ TRUE if initialization succeedded
+ FALSE otherwise
+
+--*/
+extern "C"
+BOOL LOADInitializeModules()
+{
+ _ASSERTE(exe_module.prev == nullptr);
+
+ InternalInitializeCriticalSection(&module_critsec);
+
+ // Initialize module for main executable
+ TRACE("Initializing module for main executable\n");
+
+ exe_module.self = (HMODULE)&exe_module;
+ exe_module.dl_handle = dlopen(nullptr, RTLD_LAZY);
+ if (exe_module.dl_handle == nullptr)
+ {
+ ERROR("Executable module will be broken : dlopen(nullptr) failed dlerror message is \"%s\" \n", dlerror());
+ return FALSE;
+ }
+ exe_module.lib_name = nullptr;
+ exe_module.refcount = -1;
+ exe_module.next = &exe_module;
+ exe_module.prev = &exe_module;
+ exe_module.pDllMain = nullptr;
+ exe_module.hinstance = nullptr;
+ exe_module.threadLibCalls = TRUE;
+ return TRUE;
+}
+
+/*++
+Function :
+ LOADSetExeName
+
+ Set the exe name path
+
+Parameters :
+ LPWSTR man exe path and name
+
+Return value :
+ TRUE if initialization succeedded
+ FALSE otherwise
+
+--*/
+extern "C"
+BOOL LOADSetExeName(LPWSTR name)
+{
+#if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN
+ LPSTR pszExeName = nullptr;
+#endif
+ BOOL result = FALSE;
+
+ LockModuleList();
+
+ // Save the exe path in the exe module struct
+ free(exe_module.lib_name);
+ exe_module.lib_name = name;
+
+ // For platforms where we can't trust the handle to be constant, we need to
+ // store the inode/device pairs for the modules we just initialized.
+#if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN
+ {
+ struct stat stat_buf;
+ pszExeName = UTIL_WCToMB_Alloc(name, -1);
+ if (nullptr == pszExeName)
+ {
+ ERROR("WCToMB failure, unable to get full name of exe\n");
+ goto exit;
+ }
+ if (-1 == stat(pszExeName, &stat_buf))
+ {
+ SetLastError(ERROR_MOD_NOT_FOUND);
+ goto exit;
+ }
+ TRACE("Executable has inode %d and device %d\n", stat_buf.st_ino, stat_buf.st_dev);
+
+ exe_module.inode = stat_buf.st_ino;
+ exe_module.device = stat_buf.st_dev;
+ }
+#endif
+ result = TRUE;
+
+#if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN
+exit:
+ if (pszExeName)
+ {
+ free(pszExeName);
+ }
+#endif
+ UnlockModuleList();
+ return result;
+}
+
+/*++
+Function :
+ LOADCallDllMain
+
+ Call DllMain for all modules (that have one) with the given "fwReason"
+
+Parameters :
+ DWORD dwReason : parameter to pass down to DllMain, one of DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH,
+ DLL_THREAD_ATTACH, DLL_THREAD_DETACH
+
+ LPVOID lpReserved : parameter to pass down to DllMain
+ If dwReason is DLL_PROCESS_ATTACH, lpvReserved is NULL for dynamic loads and non-NULL for static loads.
+ If dwReason is DLL_PROCESS_DETACH, lpvReserved is NULL if DllMain has been called by using FreeLibrary
+ and non-NULL if DllMain has been called during process termination.
+
+(no return value)
+
+Notes :
+ This is used to send DLL_THREAD_*TACH messages to modules
+--*/
+extern "C"
+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:
+ ASSERT("got called with DLL_PROCESS_ATTACH parameter! Why?\n");
+ break;
+ case DLL_PROCESS_DETACH:
+ ASSERT("got called with DLL_PROCESS_DETACH parameter! Why?\n");
+ InLoadOrder = FALSE;
+ break;
+ case DLL_THREAD_ATTACH:
+ TRACE("Calling DllMain(DLL_THREAD_ATTACH) on all known modules.\n");
+ break;
+ case DLL_THREAD_DETACH:
+ TRACE("Calling DllMain(DLL_THREAD_DETACH) on all known modules.\n");
+ InLoadOrder = FALSE;
+ break;
+ default:
+ ASSERT("LOADCallDllMain called with unknown parameter %d!\n", dwReason);
+ return;
+ }
+
+ LockModuleList();
+
+ module = &exe_module;
+
+ do
+ {
+ if (!InLoadOrder)
+ module = module->prev;
+
+ if (module->threadLibCalls)
+ {
+ if (module->pDllMain)
+ {
+ LOADCallDllMainSafe(module, dwReason, lpReserved);
+ }
+ }
+
+ if (InLoadOrder)
+ module = module->next;
+
+ } while (module != &exe_module);
+
+ UnlockModuleList();
+}
+
+/*++
+Function:
+ LOADFreeLibrary
+
+Parameters:
+ MODSTRUCT * module - module to free
+ BOOL fCallDllMain - if TRUE, call the DllMain function
+
+Returns:
+ TRUE if successful
+
+--*/
+static BOOL LOADFreeLibrary(MODSTRUCT *module, BOOL fCallDllMain)
+{
+ BOOL retval = FALSE;
+
+ LockModuleList();
+
+ if (terminator)
+ {
+ /* PAL shutdown is in progress - ignore FreeLibrary calls */
+ retval = TRUE;
+ goto done;
+ }
+
+ if (!LOADValidateModule(module))
+ {
+ TRACE("Can't free invalid module %p\n", module);
+ SetLastError(ERROR_INVALID_HANDLE);
+ goto done;
+ }
+
+ if (module->refcount == -1)
+ {
+ /* special module - never released */
+ retval = TRUE;
+ goto done;
+ }
+
+ module->refcount--;
+ TRACE("Reference count for module %p (named %S) decreases to %d\n",
+ module, MODNAME(module), module->refcount);
+
+ if (module->refcount != 0)
+ {
+ retval = TRUE;
+ goto done;
+ }
+
+ /* Releasing the last reference : call dlclose(), remove module from the
+ process-wide module list */
+
+ TRACE("Reference count for module %p (named %S) now 0; destroying module structure\n",
+ module, MODNAME(module));
+
+ /* unlink the module structure from the list */
+ module->prev->next = module->next;
+ module->next->prev = module->prev;
+
+ /* remove the circular reference so that LOADValidateModule will fail */
+ module->self = nullptr;
+
+ /* Call DllMain if the module contains one */
+ if (fCallDllMain && module->pDllMain)
+ {
+ LOADCallDllMainSafe(module, DLL_PROCESS_DETACH, nullptr);
+ }
+
+ if (module->hinstance)
+ {
+ PUNREGISTER_MODULE unregisterModule = (PUNREGISTER_MODULE)dlsym(module->dl_handle, "PAL_UnregisterModule");
+ if (unregisterModule != nullptr)
+ {
+ unregisterModule(module->hinstance);
+ }
+ module->hinstance = nullptr;
+ }
+
+ if (module->dl_handle && 0 != dlclose(module->dl_handle))
+ {
+ /* report dlclose() failure, but proceed anyway. */
+ WARN("dlclose() call failed! error message is \"%s\"\n", dlerror());
+ }
+
+ /* release all memory */
+ free(module->lib_name);
+ free(module);
+
+ retval = TRUE;
+
+done:
+ UnlockModuleList();
+ return retval;
+}
+
+/*++
+Function :
+ LOADCallDllMainSafe
+
+ Exception-safe call to DllMain.
+
+Parameters :
+ MODSTRUCT *module : module whose DllMain must be called
+
+ DWORD dwReason : parameter to pass down to DllMain, one of DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH,
+ DLL_THREAD_ATTACH, DLL_THREAD_DETACH
+
+ LPVOID lpvReserved : parameter to pass down to DllMain,
+ If dwReason is DLL_PROCESS_ATTACH, lpvReserved is NULL for dynamic loads and non-NULL for static loads.
+ If dwReason is DLL_PROCESS_DETACH, lpvReserved is NULL if DllMain has been called by using FreeLibrary
+ and non-NULL if DllMain has been called during process termination.
+
+Returns:
+ BOOL : DllMain's return value
+*/
+static BOOL LOADCallDllMainSafe(MODSTRUCT *module, DWORD dwReason, LPVOID lpReserved)
+{
+#if _ENABLE_DEBUG_MESSAGES_
+ /* reset ENTRY nesting level back to zero while inside the callback... */
+ int old_level = DBG_change_entrylevel(0);
+#endif /* _ENABLE_DEBUG_MESSAGES_ */
+
+ struct Param
+ {
+ MODSTRUCT *module;
+ DWORD dwReason;
+ LPVOID lpReserved;
+ BOOL ret;
+ } param;
+ param.module = module;
+ param.dwReason = dwReason;
+ param.lpReserved = lpReserved;
+ param.ret = FALSE;
+
+ PAL_TRY(Param *, pParam, &param)
+ {
+ TRACE("Calling DllMain (%p) for module %S\n",
+ pParam->module->pDllMain,
+ pParam->module->lib_name ? pParam->module->lib_name : W16_NULLSTRING);
+
+ {
+ // This module may be foreign to our PAL, so leave our PAL.
+ // If it depends on us, it will re-enter.
+ PAL_LeaveHolder holder;
+ pParam->ret = pParam->module->pDllMain(pParam->module->hinstance, pParam->dwReason, pParam->lpReserved);
+ }
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ WARN("Call to DllMain (%p) got an unhandled exception; ignoring.\n", module->pDllMain);
+ }
+ PAL_ENDTRY
+
+#if _ENABLE_DEBUG_MESSAGES_
+ /* ...and set nesting level back to what it was */
+ DBG_change_entrylevel(old_level);
+#endif /* _ENABLE_DEBUG_MESSAGES_ */
+
+ return param.ret;
+}
+
+/*++
+Function:
+ DisableThreadLibraryCalls
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+DisableThreadLibraryCalls(
+ IN HMODULE hLibModule)
+{
+ BOOL ret = FALSE;
+ MODSTRUCT *module;
+ PERF_ENTRY(DisableThreadLibraryCalls);
+ ENTRY("DisableThreadLibraryCalls(hLibModule=%p)\n", hLibModule);
+
+ LockModuleList();
+
+ if (terminator)
+ {
+ /* PAL shutdown in progress - ignore DisableThreadLibraryCalls */
+ ret = TRUE;
+ goto done;
+ }
+
+ module = (MODSTRUCT *) hLibModule;
+
+ if (!LOADValidateModule(module))
+ {
+ // DisableThreadLibraryCalls() does nothing when given
+ // an invalid module handle. This matches the Windows
+ // behavior, though it is counter to MSDN.
+ WARN("Invalid module handle %p\n", hLibModule);
+ ret = TRUE;
+ goto done;
+ }
+
+ module->threadLibCalls = FALSE;
+ ret = TRUE;
+
+done:
+ UnlockModuleList();
+ LOGEXIT("DisableThreadLibraryCalls returns BOOL %d\n", ret);
+ PERF_EXIT(DisableThreadLibraryCalls);
+ return ret;
+}
+
+// Checks the library path for null or empty string. On error, calls SetLastError() and returns false.
+template<class TChar>
+static bool LOADVerifyLibraryPath(const TChar *libraryPath)
+{
+ if (libraryPath == nullptr)
+ {
+ ERROR("libraryPath is null\n");
+ SetLastError(ERROR_MOD_NOT_FOUND);
+ return false;
+ }
+ if (libraryPath[0] == '\0')
+ {
+ ERROR("libraryPath is empty\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+ return true;
+}
+
+// Converts the wide char library path string into a multibyte-char string. On error, calls SetLastError() and returns false.
+static bool LOADConvertLibraryPathWideStringToMultibyteString(
+ LPCWSTR wideLibraryPath,
+ LPSTR multibyteLibraryPath,
+ INT *multibyteLibraryPathLengthRef)
+{
+ _ASSERTE(multibyteLibraryPathLengthRef != nullptr);
+ _ASSERTE(wideLibraryPath != nullptr);
+
+ size_t length = (PAL_wcslen(wideLibraryPath)+1) * MaxWCharToAcpLength;
+ *multibyteLibraryPathLengthRef = WideCharToMultiByte(CP_ACP, 0, wideLibraryPath, -1, multibyteLibraryPath,
+ length, nullptr, nullptr);
+
+ if (*multibyteLibraryPathLengthRef == 0)
+ {
+ DWORD dwLastError = GetLastError();
+
+ ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
+
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+ return true;
+}
+
+/*++
+Function :
+ LOADValidateModule
+
+ Check whether the given MODSTRUCT pointer is valid
+
+Parameters :
+ MODSTRUCT *module : module to check
+
+Return value :
+ TRUE if module is valid, FALSE otherwise
+
+NOTE :
+ The module lock MUST be owned.
+
+--*/
+static BOOL LOADValidateModule(MODSTRUCT *module)
+{
+ MODSTRUCT *modlist_enum = &exe_module;
+
+ /* enumerate through the list of modules to make sure the given handle is
+ really a module (HMODULEs are actually MODSTRUCT pointers) */
+ do
+ {
+ if (module == modlist_enum)
+ {
+ /* found it; check its integrity to be on the safe side */
+ if (module->self != module)
+ {
+ ERROR("Found corrupt module %p!\n",module);
+ return FALSE;
+ }
+ TRACE("Module %p is valid (name : %S)\n", module, MODNAME(module));
+ return TRUE;
+ }
+ modlist_enum = modlist_enum->next;
+ }
+ while (modlist_enum != &exe_module);
+
+ TRACE("Module %p is NOT valid.\n", module);
+ return FALSE;
+}
+
+/*++
+Function :
+ LOADGetModuleFileName [internal]
+
+ Retrieve the module's full path if it is known, the short name given to
+ LoadLibrary otherwise.
+
+Parameters :
+ MODSTRUCT *module : module to check
+
+Return value :
+ pointer to internal buffer with name of module (Unicode)
+
+Notes :
+ this function assumes that the module critical section is held, and that
+ the module has already been validated.
+--*/
+static LPWSTR LOADGetModuleFileName(MODSTRUCT *module)
+{
+ LPWSTR module_name;
+ /* special case : if module is NULL, we want the name of the executable */
+ if (!module)
+ {
+ module_name = exe_module.lib_name;
+ TRACE("Returning name of main executable\n");
+ return module_name;
+ }
+
+ /* return "real" name of module if it is known. we have this if LoadLibrary
+ was given an absolute or relative path; we can also determine it at the
+ first GetProcAdress call. */
+ TRACE("Returning full path name of module\n");
+ return module->lib_name;
+}
+
+/*
+Function:
+ LOADLoadLibraryDirect [internal]
+
+ Loads a library using a system call, without registering the library with the module list.
+
+Parameters:
+ LPCSTR libraryNameOrPath: The library to load.
+
+Return value:
+ System handle to the loaded library, or nullptr upon failure (error is set via SetLastError()).
+*/
+static void *LOADLoadLibraryDirect(LPCSTR libraryNameOrPath)
+{
+ _ASSERTE(libraryNameOrPath != nullptr);
+ _ASSERTE(libraryNameOrPath[0] != '\0');
+
+ void *dl_handle = dlopen(libraryNameOrPath, RTLD_LAZY);
+ if (dl_handle == nullptr)
+ {
+ WARN("dlopen() failed; dlerror says '%s'\n", dlerror());
+ SetLastError(ERROR_MOD_NOT_FOUND);
+ }
+ else
+ {
+ TRACE("dlopen() found module %s\n", libraryNameOrPath);
+ }
+
+ return dl_handle;
+}
+
+/*++
+Function :
+ LOADAllocModule
+
+ Allocate and initialize a new MODSTRUCT structure
+
+Parameters :
+ void *dl_handle : handle returned by dl_open, goes in MODSTRUCT::dl_handle
+
+ char *name : name of new module. after conversion to widechar,
+ goes in MODSTRUCT::lib_name
+
+Return value:
+ a pointer to a new, initialized MODSTRUCT strucutre, or NULL on failure.
+
+Notes :
+ 'name' is used to initialize MODSTRUCT::lib_name. The other member is set to NULL
+ In case of failure (in malloc or MBToWC), this function sets LastError.
+--*/
+static MODSTRUCT *LOADAllocModule(void *dl_handle, LPCSTR name)
+{
+ MODSTRUCT *module;
+ LPWSTR wide_name;
+
+ /* no match found : try to create a new module structure */
+ module = (MODSTRUCT *)InternalMalloc(sizeof(MODSTRUCT));
+ if (nullptr == module)
+ {
+ ERROR("malloc() failed! errno is %d (%s)\n", errno, strerror(errno));
+ return nullptr;
+ }
+
+ wide_name = UTIL_MBToWC_Alloc(name, -1);
+ if (nullptr == wide_name)
+ {
+ ERROR("couldn't convert name to a wide-character string\n");
+ free(module);
+ return nullptr;
+ }
+
+ module->dl_handle = dl_handle;
+#if NEED_DLCOMPAT
+ if (isdylib(module))
+ {
+ module->refcount = -1;
+ }
+ else
+ {
+ module->refcount = 1;
+ }
+#else // NEED_DLCOMPAT
+ module->refcount = 1;
+#endif // NEED_DLCOMPAT
+ module->self = module;
+ module->hinstance = nullptr;
+ module->threadLibCalls = TRUE;
+ module->pDllMain = nullptr;
+ module->next = nullptr;
+ module->prev = nullptr;
+
+ module->lib_name = wide_name;
+
+ return module;
+}
+
+/*
+Function:
+ LOADAddModule [internal]
+
+ Registers a system handle to a loaded library with the module list.
+
+Parameters:
+ void *dl_handle: System handle to the loaded library.
+ LPCSTR libraryNameOrPath: The library that was loaded.
+
+Return value:
+ PAL handle to the loaded library, or nullptr upon failure (error is set via SetLastError()).
+*/
+static MODSTRUCT *LOADAddModule(void *dl_handle, LPCSTR libraryNameOrPath)
+{
+ _ASSERTE(dl_handle != nullptr);
+ _ASSERTE(libraryNameOrPath != nullptr);
+ _ASSERTE(libraryNameOrPath[0] != '\0');
+
+#if !RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN
+ /* search module list for a match. */
+ MODSTRUCT *module = &exe_module;
+ do
+ {
+ if (dl_handle == module->dl_handle)
+ {
+ /* found the handle. increment the refcount and return the
+ existing module structure */
+ TRACE("Found matching module %p for module name %s\n", module, libraryNameOrPath);
+
+ if (module->refcount != -1)
+ {
+ module->refcount++;
+ }
+ dlclose(dl_handle);
+ return module;
+ }
+ module = module->next;
+
+ } while (module != &exe_module);
+#endif
+
+ TRACE("Module doesn't exist : creating %s.\n", libraryNameOrPath);
+
+ module = LOADAllocModule(dl_handle, libraryNameOrPath);
+ if (nullptr == module)
+ {
+ ERROR("couldn't create new module\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ dlclose(dl_handle);
+ return nullptr;
+ }
+
+ /* We now get the address of DllMain if the module contains one. */
+ module->pDllMain = (PDLLMAIN)dlsym(module->dl_handle, "DllMain");
+
+ /* Add the new module on to the end of the list */
+ module->prev = exe_module.prev;
+ module->next = &exe_module;
+ exe_module.prev->next = module;
+ exe_module.prev = module;
+
+#if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN
+ module->inode = stat_buf.st_ino;
+ module->device = stat_buf.st_dev;
+#endif
+
+ return module;
+}
+
+/*
+Function:
+ LOADRegisterLibraryDirect [internal]
+
+ Registers a system handle to a loaded library with the module list.
+
+Parameters:
+ void *dl_handle: System handle to the loaded library.
+ LPCSTR libraryNameOrPath: The library that was loaded.
+ BOOL fDynamic: TRUE if dynamic load through LoadLibrary, FALSE if static load through RegisterLibrary.
+
+Return value:
+ PAL handle to the loaded library, or nullptr upon failure (error is set via SetLastError()).
+*/
+static HMODULE LOADRegisterLibraryDirect(void *dl_handle, LPCSTR libraryNameOrPath, BOOL fDynamic)
+{
+ MODSTRUCT *module = LOADAddModule(dl_handle, libraryNameOrPath);
+ if (module == nullptr)
+ {
+ return nullptr;
+ }
+
+ /* If the module contains a DllMain, call it. */
+ if (module->pDllMain)
+ {
+ TRACE("Calling DllMain (%p) for module %S\n",
+ module->pDllMain,
+ module->lib_name ? module->lib_name : W16_NULLSTRING);
+
+ if (nullptr == module->hinstance)
+ {
+ PREGISTER_MODULE registerModule = (PREGISTER_MODULE)dlsym(module->dl_handle, "PAL_RegisterModule");
+ if (registerModule != nullptr)
+ {
+ module->hinstance = registerModule(libraryNameOrPath);
+ }
+ else
+ {
+ // If the target module doesn't have the PAL_RegisterModule export, then use this PAL's
+ // module handle assuming that the target module is referencing this PAL's exported
+ // functions on said handle.
+ module->hinstance = (HINSTANCE)module;
+ }
+ }
+
+ BOOL dllMainRetVal = LOADCallDllMainSafe(module, DLL_PROCESS_ATTACH, fDynamic ? nullptr : (LPVOID)-1);
+
+ // If DlMain(DLL_PROCESS_ATTACH) returns FALSE, we must immediately unload the module
+ if (!dllMainRetVal)
+ {
+ ERROR("DllMain returned FALSE; unloading module.\n");
+ module->pDllMain = nullptr;
+ FreeLibrary((HMODULE)module);
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ module = nullptr;
+ }
+ }
+ else
+ {
+ TRACE("Module does not contain a DllMain function.\n");
+ }
+
+ return module;
+}
+
+/*++
+Function :
+ LOADLoadLibrary [internal]
+
+ implementation of LoadLibrary (for use by the A/W variants)
+
+Parameters :
+ LPSTR shortAsciiName : name of module as specified to LoadLibrary
+
+ BOOL fDynamic : TRUE if dynamic load through LoadLibrary, FALSE if static load through RegisterLibrary
+
+Return value :
+ handle to loaded module
+
+--*/
+static HMODULE LOADLoadLibrary(LPCSTR shortAsciiName, BOOL fDynamic)
+{
+ HMODULE module = nullptr;
+ void *dl_handle = nullptr;
+
+ // Check whether we have been requested to load 'libc'. If that's the case, then:
+ // * For Linux, use the full name of the library that is defined in <gnu/lib-names.h> by the
+ // LIBC_SO constant. The problem is that calling dlopen("libc.so") will fail for libc even
+ // though it works for other libraries. The reason is that libc.so is just linker script
+ // (i.e. a test file).
+ // As a result, we have to use the full name (i.e. lib.so.6) that is defined by LIBC_SO.
+ // * For macOS, use constant value absolute path "/usr/lib/libc.dylib".
+ // * For FreeBSD, use constant value "libc.so.7".
+ // * For rest of Unices, use constant value "libc.so".
+ if (strcmp(shortAsciiName, LIBC_NAME_WITHOUT_EXTENSION) == 0)
+ {
+#if defined(__APPLE__)
+ shortAsciiName = "/usr/lib/libc.dylib";
+#elif defined(__FreeBSD__)
+ shortAsciiName = "libc.so.7";
+#elif defined(LIBC_SO)
+ shortAsciiName = LIBC_SO;
+#else
+ shortAsciiName = "libc.so";
+#endif
+ }
+
+ LockModuleList();
+
+ dl_handle = LOADLoadLibraryDirect(shortAsciiName);
+ if (dl_handle)
+ {
+ module = LOADRegisterLibraryDirect(dl_handle, shortAsciiName, fDynamic);
+ }
+
+ UnlockModuleList();
+
+ return module;
+}
+
+/*++
+ LOADInitializeCoreCLRModule
+
+ Run the initialization methods for CoreCLR module (the module containing this PAL).
+
+Parameters:
+ None
+
+Return value:
+ TRUE if successful
+ FALSE if failure
+--*/
+BOOL LOADInitializeCoreCLRModule()
+{
+ MODSTRUCT *module = LOADGetPalLibrary();
+ if (!module)
+ {
+ ERROR("Can not load the PAL module\n");
+ return FALSE;
+ }
+ PDLLMAIN pRuntimeDllMain = (PDLLMAIN)dlsym(module->dl_handle, "CoreDllMain");
+ if (!pRuntimeDllMain)
+ {
+ ERROR("Can not find the CoreDllMain entry point\n");
+ return FALSE;
+ }
+ return pRuntimeDllMain(module->hinstance, DLL_PROCESS_ATTACH, nullptr);
+}
+
+/*++
+Function :
+ LOADGetPalLibrary
+
+ Load and initialize the PAL module.
+
+Parameters :
+ None
+
+Return value :
+ pointer to module struct
+
+--*/
+MODSTRUCT *LOADGetPalLibrary()
+{
+ if (pal_module == nullptr)
+ {
+ // Initialize the pal module (the module containing LOADGetPalLibrary). Assumes that
+ // the PAL is linked into the coreclr module because we use the module name containing
+ // this function for the coreclr path.
+ TRACE("Loading module for PAL library\n");
+
+ Dl_info info;
+ if (dladdr((PVOID)&LOADGetPalLibrary, &info) == 0)
+ {
+ ERROR("LOADGetPalLibrary: dladdr() failed. dlerror message is \"%s\"\n", dlerror());
+ goto exit;
+ }
+ // Stash a copy of the CoreCLR installation path in a global variable.
+ // Make sure it's terminated with a slash.
+ if (g_szCoreCLRPath == nullptr)
+ {
+ size_t cbszCoreCLRPath = strlen(info.dli_fname) + 1;
+ g_szCoreCLRPath = (char*) InternalMalloc(cbszCoreCLRPath);
+
+ if (g_szCoreCLRPath == nullptr)
+ {
+ ERROR("LOADGetPalLibrary: InternalMalloc failed!");
+ goto exit;
+ }
+
+ if (strcpy_s(g_szCoreCLRPath, cbszCoreCLRPath, info.dli_fname) != SAFECRT_SUCCESS)
+ {
+ ERROR("LOADGetPalLibrary: strcpy_s failed!");
+ goto exit;
+ }
+ }
+
+ pal_module = (MODSTRUCT *)LOADLoadLibrary(info.dli_fname, FALSE);
+ }
+
+exit:
+ return pal_module;
+}
+
+/*++
+Function:
+ LockModuleList
+
+Abstract
+ Enter the critical section associated to the module list
+
+Parameter
+ void
+
+Return
+ void
+--*/
+extern "C"
+void LockModuleList()
+{
+ CPalThread * pThread =
+ (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : nullptr);
+
+ InternalEnterCriticalSection(pThread, &module_critsec);
+}
+
+/*++
+Function:
+ UnlockModuleList
+
+Abstract
+ Leave the critical section associated to the module list
+
+Parameter
+ void
+
+Return
+ void
+--*/
+extern "C"
+void UnlockModuleList()
+{
+ CPalThread * pThread =
+ (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : nullptr);
+
+ InternalLeaveCriticalSection(pThread, &module_critsec);
+}
diff --git a/src/pal/src/loader/modulename.cpp b/src/pal/src/loader/modulename.cpp
new file mode 100644
index 0000000000..026f89b3ea
--- /dev/null
+++ b/src/pal/src/loader/modulename.cpp
@@ -0,0 +1,207 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+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"
+
+#if NEED_DLCOMPAT
+#include "dlcompat.h"
+#else // NEED_DLCOMPAT
+#include <dlfcn.h>
+#endif // NEED_DLCOMPAT
+
+#if defined(_AIX)
+#include <sys/ldr.h>
+#endif
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(LOADER);
+
+#if defined(_AIX)
+/*++
+ GetLibRotorNameViaLoadQuery
+
+ Retrieve the full path of the librotor_pal.so using loadquery()
+
+Parameters:
+ pszBuf - CHAR array of MAX_PATH_FNAME length
+
+Return value:
+ 0 on success
+ -1 on failure, with last error set
+--*/
+int GetLibRotorNameViaLoadQuery(LPSTR pszBuf)
+{
+ CHAR* pLoadQueryBuf = NULL;
+ UINT cbBuf = 1024;
+ struct ld_info * pInfo = NULL;
+ INT iLQRetVal = -1;
+ INT iRetVal = -1;
+ CPalThread *pThread = NULL;
+
+ if (!pszBuf)
+ {
+ ASSERT("GetLibRotorNameViaLoadQuery requires non-NULL pszBuf\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto Done;
+ }
+
+ pThread = InternalGetCurrentThread();
+ // Loop trying to call loadquery with enough memory until either
+ // 1) we succeed, 2) we run out of memory or 3) loadquery throws
+ // an error other than ENOMEM
+ while (iLQRetVal != 0)
+ {
+ pLoadQueryBuf = (CHAR*) InternalMalloc (pThread, cbBuf * sizeof(char));
+ if (!pLoadQueryBuf)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto Done;
+ }
+ iLQRetVal = loadquery(L_GETINFO, pLoadQueryBuf, cbBuf);
+ if (iLQRetVal < 0)
+ {
+ free(pThread, pLoadQueryBuf);
+ pLoadQueryBuf = NULL;
+ DWORD dwLastError = GetLastError();
+ if (dwLastError == ERROR_NOT_ENOUGH_MEMORY)
+ {
+ // The buffer's too small. Try twice as large as a guess...
+ cbBuf *= 2;
+ }
+ else
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto Done;
+ }
+ }
+ }
+
+ // We successfully called loadquery, so now see if we can find
+ // librotor_pal.a in the module list
+ if (pLoadQueryBuf)
+ {
+ pInfo = (struct ld_info *)pLoadQueryBuf;
+ while (TRUE)
+ {
+ if (strstr(pInfo->ldinfo_filename, "librotor_pal.a"))
+ {
+ UINT cchFileName = strlen(pInfo->ldinfo_filename);
+ if (cchFileName + 1 > MAX_PATH_FNAME)
+ {
+ ASSERT("Filename returned by loadquery was longer than MAX_PATH_FNAME!\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto Done;
+ }
+ else
+ {
+ // The buffer should be large enough to accomodate the filename.
+ // So, we send in the size of the filename+1
+ strcpy_s(pszBuf, MAX_PATH_FNAME, pInfo->ldinfo_filename);
+ iRetVal = 0;
+ goto Done;
+ }
+ }
+ else
+ {
+ // The (wacky) design of ld_info is that the value of next is an offset in
+ // bytes rather than a pointer. So we need this weird cast to char * to get
+ // the pointer math correct.
+ if (pInfo->ldinfo_next == 0)
+ break;
+ else
+ pInfo = (struct ld_info *) ((char *)pInfo + pInfo->ldinfo_next);
+ }
+ }
+ }
+Done:
+ if (pLoadQueryBuf)
+ free(pThread, pLoadQueryBuf);
+ return iRetVal;
+}
+#endif // defined(_AIX)
+
+/*++
+ PAL_dladdr
+
+ Internal wrapper for dladder used only to get module name
+
+Parameters:
+ None
+
+Return value:
+ Pointer to string with the fullpath to the librotor_pal.so being
+ used.
+
+ 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)
+{
+#if defined(_AIX) || defined(__hppa__)
+ /* dladdr is not supported on AIX or 32-bit HPUX-PARISC */
+ return (NULL);
+#elif defined(_HPUX_) && defined(_IA64_)
+ /* dladdr is not supported on HP-UX/IA64. That said, PAL_dladdr just returns to module name
+ and we can get that via dlgetname. So use that for HPUX. */
+ {
+ char* pszName = NULL;
+ load_module_desc desc;
+ __uint64_t uimodret = NULL;
+ uimodret = dlmodinfo((__uint64_t)ProcAddress, &desc, sizeof(desc), NULL, 0, 0);
+ if (!uimodret)
+ {
+ WARN("dlmodinfo call failed! dlerror says '%s'\n", dlerror());
+ return NULL;
+ }
+ pszName = dlgetname(&desc, sizeof(desc), NULL, 0, 0);
+ if (!pszName)
+ {
+ WARN("dlgetname desc didn't describe a loaded module?! dlerror says '%s'\n", dlerror());
+ return NULL;
+ }
+ return pszName;
+ }
+#else
+ Dl_info dl_info;
+ if (!dladdr(ProcAddress, &dl_info))
+ {
+ WARN("dladdr() call failed! dlerror says '%s'\n", dlerror());
+ /* If we get an error, return NULL */
+ return (NULL);
+ }
+ else
+ {
+ /* Return the module name */
+ return dl_info.dli_fname;
+ }
+#endif
+}
+
diff --git a/src/pal/src/locale/UnicodeData.txt b/src/pal/src/locale/UnicodeData.txt
new file mode 100644
index 0000000000..a137245b52
--- /dev/null
+++ b/src/pal/src/locale/UnicodeData.txt
@@ -0,0 +1,12860 @@
+0000;<control>;Cc;0;BN;;;;;N;NULL;;;;
+0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;;
+0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;;
+0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;;
+0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;;
+0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;;
+0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;;
+0007;<control>;Cc;0;BN;;;;;N;BELL;;;;
+0008;<control>;Cc;0;BN;;;;;N;BACKSPACE;;;;
+0009;<control>;Cc;0;S;;;;;N;HORIZONTAL TABULATION;;;;
+000A;<control>;Cc;0;B;;;;;N;LINE FEED;;;;
+000B;<control>;Cc;0;S;;;;;N;VERTICAL TABULATION;;;;
+000C;<control>;Cc;0;WS;;;;;N;FORM FEED;;;;
+000D;<control>;Cc;0;B;;;;;N;CARRIAGE RETURN;;;;
+000E;<control>;Cc;0;BN;;;;;N;SHIFT OUT;;;;
+000F;<control>;Cc;0;BN;;;;;N;SHIFT IN;;;;
+0010;<control>;Cc;0;BN;;;;;N;DATA LINK ESCAPE;;;;
+0011;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL ONE;;;;
+0012;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL TWO;;;;
+0013;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL THREE;;;;
+0014;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL FOUR;;;;
+0015;<control>;Cc;0;BN;;;;;N;NEGATIVE ACKNOWLEDGE;;;;
+0016;<control>;Cc;0;BN;;;;;N;SYNCHRONOUS IDLE;;;;
+0017;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION BLOCK;;;;
+0018;<control>;Cc;0;BN;;;;;N;CANCEL;;;;
+0019;<control>;Cc;0;BN;;;;;N;END OF MEDIUM;;;;
+001A;<control>;Cc;0;BN;;;;;N;SUBSTITUTE;;;;
+001B;<control>;Cc;0;BN;;;;;N;ESCAPE;;;;
+001C;<control>;Cc;0;B;;;;;N;FILE SEPARATOR;;;;
+001D;<control>;Cc;0;B;;;;;N;GROUP SEPARATOR;;;;
+001E;<control>;Cc;0;B;;;;;N;RECORD SEPARATOR;;;;
+001F;<control>;Cc;0;S;;;;;N;UNIT SEPARATOR;;;;
+0020;SPACE;Zs;0;WS;;;;;N;;;;;
+0021;EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+0022;QUOTATION MARK;Po;0;ON;;;;;N;;;;;
+0023;NUMBER SIGN;Po;0;ET;;;;;N;;;;;
+0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;;
+0025;PERCENT SIGN;Po;0;ET;;;;;N;;;;;
+0026;AMPERSAND;Po;0;ON;;;;;N;;;;;
+0027;APOSTROPHE;Po;0;ON;;;;;N;APOSTROPHE-QUOTE;;;;
+0028;LEFT PARENTHESIS;Ps;0;ON;;;;;Y;OPENING PARENTHESIS;;;;
+0029;RIGHT PARENTHESIS;Pe;0;ON;;;;;Y;CLOSING PARENTHESIS;;;;
+002A;ASTERISK;Po;0;ON;;;;;N;;;;;
+002B;PLUS SIGN;Sm;0;ET;;;;;N;;;;;
+002C;COMMA;Po;0;CS;;;;;N;;;;;
+002D;HYPHEN-MINUS;Pd;0;ET;;;;;N;;;;;
+002E;FULL STOP;Po;0;CS;;;;;N;PERIOD;;;;
+002F;SOLIDUS;Po;0;ES;;;;;N;SLASH;;;;
+0030;DIGIT ZERO;Nd;0;EN;;0;0;0;N;;;;;
+0031;DIGIT ONE;Nd;0;EN;;1;1;1;N;;;;;
+0032;DIGIT TWO;Nd;0;EN;;2;2;2;N;;;;;
+0033;DIGIT THREE;Nd;0;EN;;3;3;3;N;;;;;
+0034;DIGIT FOUR;Nd;0;EN;;4;4;4;N;;;;;
+0035;DIGIT FIVE;Nd;0;EN;;5;5;5;N;;;;;
+0036;DIGIT SIX;Nd;0;EN;;6;6;6;N;;;;;
+0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;;
+0038;DIGIT EIGHT;Nd;0;EN;;8;8;8;N;;;;;
+0039;DIGIT NINE;Nd;0;EN;;9;9;9;N;;;;;
+003A;COLON;Po;0;CS;;;;;N;;;;;
+003B;SEMICOLON;Po;0;ON;;;;;N;;;;;
+003C;LESS-THAN SIGN;Sm;0;ON;;;;;Y;;;;;
+003D;EQUALS SIGN;Sm;0;ON;;;;;N;;;;;
+003E;GREATER-THAN SIGN;Sm;0;ON;;;;;Y;;;;;
+003F;QUESTION MARK;Po;0;ON;;;;;N;;;;;
+0040;COMMERCIAL AT;Po;0;ON;;;;;N;;;;;
+0041;LATIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0061;
+0042;LATIN CAPITAL LETTER B;Lu;0;L;;;;;N;;;;0062;
+0043;LATIN CAPITAL LETTER C;Lu;0;L;;;;;N;;;;0063;
+0044;LATIN CAPITAL LETTER D;Lu;0;L;;;;;N;;;;0064;
+0045;LATIN CAPITAL LETTER E;Lu;0;L;;;;;N;;;;0065;
+0046;LATIN CAPITAL LETTER F;Lu;0;L;;;;;N;;;;0066;
+0047;LATIN CAPITAL LETTER G;Lu;0;L;;;;;N;;;;0067;
+0048;LATIN CAPITAL LETTER H;Lu;0;L;;;;;N;;;;0068;
+0049;LATIN CAPITAL LETTER I;Lu;0;L;;;;;N;;;;0069;
+004A;LATIN CAPITAL LETTER J;Lu;0;L;;;;;N;;;;006A;
+004B;LATIN CAPITAL LETTER K;Lu;0;L;;;;;N;;;;006B;
+004C;LATIN CAPITAL LETTER L;Lu;0;L;;;;;N;;;;006C;
+004D;LATIN CAPITAL LETTER M;Lu;0;L;;;;;N;;;;006D;
+004E;LATIN CAPITAL LETTER N;Lu;0;L;;;;;N;;;;006E;
+004F;LATIN CAPITAL LETTER O;Lu;0;L;;;;;N;;;;006F;
+0050;LATIN CAPITAL LETTER P;Lu;0;L;;;;;N;;;;0070;
+0051;LATIN CAPITAL LETTER Q;Lu;0;L;;;;;N;;;;0071;
+0052;LATIN CAPITAL LETTER R;Lu;0;L;;;;;N;;;;0072;
+0053;LATIN CAPITAL LETTER S;Lu;0;L;;;;;N;;;;0073;
+0054;LATIN CAPITAL LETTER T;Lu;0;L;;;;;N;;;;0074;
+0055;LATIN CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0075;
+0056;LATIN CAPITAL LETTER V;Lu;0;L;;;;;N;;;;0076;
+0057;LATIN CAPITAL LETTER W;Lu;0;L;;;;;N;;;;0077;
+0058;LATIN CAPITAL LETTER X;Lu;0;L;;;;;N;;;;0078;
+0059;LATIN CAPITAL LETTER Y;Lu;0;L;;;;;N;;;;0079;
+005A;LATIN CAPITAL LETTER Z;Lu;0;L;;;;;N;;;;007A;
+005B;LEFT SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING SQUARE BRACKET;;;;
+005C;REVERSE SOLIDUS;Po;0;ON;;;;;N;BACKSLASH;;;;
+005D;RIGHT SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING SQUARE BRACKET;;;;
+005E;CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;SPACING CIRCUMFLEX;;;;
+005F;LOW LINE;Pc;0;ON;;;;;N;SPACING UNDERSCORE;;;;
+0060;GRAVE ACCENT;Sk;0;ON;;;;;N;SPACING GRAVE;;;;
+0061;LATIN SMALL LETTER A;Ll;0;L;;;;;N;;;0041;;0041
+0062;LATIN SMALL LETTER B;Ll;0;L;;;;;N;;;0042;;0042
+0063;LATIN SMALL LETTER C;Ll;0;L;;;;;N;;;0043;;0043
+0064;LATIN SMALL LETTER D;Ll;0;L;;;;;N;;;0044;;0044
+0065;LATIN SMALL LETTER E;Ll;0;L;;;;;N;;;0045;;0045
+0066;LATIN SMALL LETTER F;Ll;0;L;;;;;N;;;0046;;0046
+0067;LATIN SMALL LETTER G;Ll;0;L;;;;;N;;;0047;;0047
+0068;LATIN SMALL LETTER H;Ll;0;L;;;;;N;;;0048;;0048
+0069;LATIN SMALL LETTER I;Ll;0;L;;;;;N;;;0049;;0049
+006A;LATIN SMALL LETTER J;Ll;0;L;;;;;N;;;004A;;004A
+006B;LATIN SMALL LETTER K;Ll;0;L;;;;;N;;;004B;;004B
+006C;LATIN SMALL LETTER L;Ll;0;L;;;;;N;;;004C;;004C
+006D;LATIN SMALL LETTER M;Ll;0;L;;;;;N;;;004D;;004D
+006E;LATIN SMALL LETTER N;Ll;0;L;;;;;N;;;004E;;004E
+006F;LATIN SMALL LETTER O;Ll;0;L;;;;;N;;;004F;;004F
+0070;LATIN SMALL LETTER P;Ll;0;L;;;;;N;;;0050;;0050
+0071;LATIN SMALL LETTER Q;Ll;0;L;;;;;N;;;0051;;0051
+0072;LATIN SMALL LETTER R;Ll;0;L;;;;;N;;;0052;;0052
+0073;LATIN SMALL LETTER S;Ll;0;L;;;;;N;;;0053;;0053
+0074;LATIN SMALL LETTER T;Ll;0;L;;;;;N;;;0054;;0054
+0075;LATIN SMALL LETTER U;Ll;0;L;;;;;N;;;0055;;0055
+0076;LATIN SMALL LETTER V;Ll;0;L;;;;;N;;;0056;;0056
+0077;LATIN SMALL LETTER W;Ll;0;L;;;;;N;;;0057;;0057
+0078;LATIN SMALL LETTER X;Ll;0;L;;;;;N;;;0058;;0058
+0079;LATIN SMALL LETTER Y;Ll;0;L;;;;;N;;;0059;;0059
+007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A
+007B;LEFT CURLY BRACKET;Ps;0;ON;;;;;Y;OPENING CURLY BRACKET;;;;
+007C;VERTICAL LINE;Sm;0;ON;;;;;N;VERTICAL BAR;;;;
+007D;RIGHT CURLY BRACKET;Pe;0;ON;;;;;Y;CLOSING CURLY BRACKET;;;;
+007E;TILDE;Sm;0;ON;;;;;N;;;;;
+007F;<control>;Cc;0;BN;;;;;N;DELETE;;;;
+0080;<control>;Cc;0;BN;;;;;N;;;;;
+0081;<control>;Cc;0;BN;;;;;N;;;;;
+0082;<control>;Cc;0;BN;;;;;N;BREAK PERMITTED HERE;;;;
+0083;<control>;Cc;0;BN;;;;;N;NO BREAK HERE;;;;
+0084;<control>;Cc;0;BN;;;;;N;;;;;
+0085;<control>;Cc;0;B;;;;;N;NEXT LINE;;;;
+0086;<control>;Cc;0;BN;;;;;N;START OF SELECTED AREA;;;;
+0087;<control>;Cc;0;BN;;;;;N;END OF SELECTED AREA;;;;
+0088;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION SET;;;;
+0089;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION WITH JUSTIFICATION;;;;
+008A;<control>;Cc;0;BN;;;;;N;LINE TABULATION SET;;;;
+008B;<control>;Cc;0;BN;;;;;N;PARTIAL LINE DOWN;;;;
+008C;<control>;Cc;0;BN;;;;;N;PARTIAL LINE UP;;;;
+008D;<control>;Cc;0;BN;;;;;N;REVERSE LINE FEED;;;;
+008E;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT TWO;;;;
+008F;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT THREE;;;;
+0090;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL STRING;;;;
+0091;<control>;Cc;0;BN;;;;;N;PRIVATE USE ONE;;;;
+0092;<control>;Cc;0;BN;;;;;N;PRIVATE USE TWO;;;;
+0093;<control>;Cc;0;BN;;;;;N;SET TRANSMIT STATE;;;;
+0094;<control>;Cc;0;BN;;;;;N;CANCEL CHARACTER;;;;
+0095;<control>;Cc;0;BN;;;;;N;MESSAGE WAITING;;;;
+0096;<control>;Cc;0;BN;;;;;N;START OF GUARDED AREA;;;;
+0097;<control>;Cc;0;BN;;;;;N;END OF GUARDED AREA;;;;
+0098;<control>;Cc;0;BN;;;;;N;START OF STRING;;;;
+0099;<control>;Cc;0;BN;;;;;N;;;;;
+009A;<control>;Cc;0;BN;;;;;N;SINGLE CHARACTER INTRODUCER;;;;
+009B;<control>;Cc;0;BN;;;;;N;CONTROL SEQUENCE INTRODUCER;;;;
+009C;<control>;Cc;0;BN;;;;;N;STRING TERMINATOR;;;;
+009D;<control>;Cc;0;BN;;;;;N;OPERATING SYSTEM COMMAND;;;;
+009E;<control>;Cc;0;BN;;;;;N;PRIVACY MESSAGE;;;;
+009F;<control>;Cc;0;BN;;;;;N;APPLICATION PROGRAM COMMAND;;;;
+00A0;NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;NON-BREAKING SPACE;;;;
+00A1;INVERTED EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+00A2;CENT SIGN;Sc;0;ET;;;;;N;;;;;
+00A3;POUND SIGN;Sc;0;ET;;;;;N;;;;;
+00A4;CURRENCY SIGN;Sc;0;ET;;;;;N;;;;;
+00A5;YEN SIGN;Sc;0;ET;;;;;N;;;;;
+00A6;BROKEN BAR;So;0;ON;;;;;N;BROKEN VERTICAL BAR;;;;
+00A7;SECTION SIGN;So;0;ON;;;;;N;;;;;
+00A8;DIAERESIS;Sk;0;ON;<compat> 0020 0308;;;;N;SPACING DIAERESIS;;;;
+00A9;COPYRIGHT SIGN;So;0;ON;;;;;N;;;;;
+00AA;FEMININE ORDINAL INDICATOR;Ll;0;L;<super> 0061;;;;N;;;;;
+00AB;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING GUILLEMET;*;;;
+00AC;NOT SIGN;Sm;0;ON;;;;;N;;;;;
+00AD;SOFT HYPHEN;Pd;0;ON;;;;;N;;;;;
+00AE;REGISTERED SIGN;So;0;ON;;;;;N;REGISTERED TRADE MARK SIGN;;;;
+00AF;MACRON;Sk;0;ON;<compat> 0020 0304;;;;N;SPACING MACRON;;;;
+00B0;DEGREE SIGN;So;0;ET;;;;;N;;;;;
+00B1;PLUS-MINUS SIGN;Sm;0;ET;;;;;N;PLUS-OR-MINUS SIGN;;;;
+00B2;SUPERSCRIPT TWO;No;0;EN;<super> 0032;2;2;2;N;SUPERSCRIPT DIGIT TWO;;;;
+00B3;SUPERSCRIPT THREE;No;0;EN;<super> 0033;3;3;3;N;SUPERSCRIPT DIGIT THREE;;;;
+00B4;ACUTE ACCENT;Sk;0;ON;<compat> 0020 0301;;;;N;SPACING ACUTE;;;;
+00B5;MICRO SIGN;Ll;0;L;<compat> 03BC;;;;N;;;039C;;039C
+00B6;PILCROW SIGN;So;0;ON;;;;;N;PARAGRAPH SIGN;;;;
+00B7;MIDDLE DOT;Po;0;ON;;;;;N;;;;;
+00B8;CEDILLA;Sk;0;ON;<compat> 0020 0327;;;;N;SPACING CEDILLA;;;;
+00B9;SUPERSCRIPT ONE;No;0;EN;<super> 0031;1;1;1;N;SUPERSCRIPT DIGIT ONE;;;;
+00BA;MASCULINE ORDINAL INDICATOR;Ll;0;L;<super> 006F;;;;N;;;;;
+00BB;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING GUILLEMET;*;;;
+00BC;VULGAR FRACTION ONE QUARTER;No;0;ON;<fraction> 0031 2044 0034;;;1/4;N;FRACTION ONE QUARTER;;;;
+00BD;VULGAR FRACTION ONE HALF;No;0;ON;<fraction> 0031 2044 0032;;;1/2;N;FRACTION ONE HALF;;;;
+00BE;VULGAR FRACTION THREE QUARTERS;No;0;ON;<fraction> 0033 2044 0034;;;3/4;N;FRACTION THREE QUARTERS;;;;
+00BF;INVERTED QUESTION MARK;Po;0;ON;;;;;N;;;;;
+00C0;LATIN CAPITAL LETTER A WITH GRAVE;Lu;0;L;0041 0300;;;;N;LATIN CAPITAL LETTER A GRAVE;;;00E0;
+00C1;LATIN CAPITAL LETTER A WITH ACUTE;Lu;0;L;0041 0301;;;;N;LATIN CAPITAL LETTER A ACUTE;;;00E1;
+00C2;LATIN CAPITAL LETTER A WITH CIRCUMFLEX;Lu;0;L;0041 0302;;;;N;LATIN CAPITAL LETTER A CIRCUMFLEX;;;00E2;
+00C3;LATIN CAPITAL LETTER A WITH TILDE;Lu;0;L;0041 0303;;;;N;LATIN CAPITAL LETTER A TILDE;;;00E3;
+00C4;LATIN CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0041 0308;;;;N;LATIN CAPITAL LETTER A DIAERESIS;;;00E4;
+00C5;LATIN CAPITAL LETTER A WITH RING ABOVE;Lu;0;L;0041 030A;;;;N;LATIN CAPITAL LETTER A RING;;;00E5;
+00C6;LATIN CAPITAL LETTER AE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER A E;ash *;;00E6;
+00C7;LATIN CAPITAL LETTER C WITH CEDILLA;Lu;0;L;0043 0327;;;;N;LATIN CAPITAL LETTER C CEDILLA;;;00E7;
+00C8;LATIN CAPITAL LETTER E WITH GRAVE;Lu;0;L;0045 0300;;;;N;LATIN CAPITAL LETTER E GRAVE;;;00E8;
+00C9;LATIN CAPITAL LETTER E WITH ACUTE;Lu;0;L;0045 0301;;;;N;LATIN CAPITAL LETTER E ACUTE;;;00E9;
+00CA;LATIN CAPITAL LETTER E WITH CIRCUMFLEX;Lu;0;L;0045 0302;;;;N;LATIN CAPITAL LETTER E CIRCUMFLEX;;;00EA;
+00CB;LATIN CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;0045 0308;;;;N;LATIN CAPITAL LETTER E DIAERESIS;;;00EB;
+00CC;LATIN CAPITAL LETTER I WITH GRAVE;Lu;0;L;0049 0300;;;;N;LATIN CAPITAL LETTER I GRAVE;;;00EC;
+00CD;LATIN CAPITAL LETTER I WITH ACUTE;Lu;0;L;0049 0301;;;;N;LATIN CAPITAL LETTER I ACUTE;;;00ED;
+00CE;LATIN CAPITAL LETTER I WITH CIRCUMFLEX;Lu;0;L;0049 0302;;;;N;LATIN CAPITAL LETTER I CIRCUMFLEX;;;00EE;
+00CF;LATIN CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0049 0308;;;;N;LATIN CAPITAL LETTER I DIAERESIS;;;00EF;
+00D0;LATIN CAPITAL LETTER ETH;Lu;0;L;;;;;N;;Icelandic;;00F0;
+00D1;LATIN CAPITAL LETTER N WITH TILDE;Lu;0;L;004E 0303;;;;N;LATIN CAPITAL LETTER N TILDE;;;00F1;
+00D2;LATIN CAPITAL LETTER O WITH GRAVE;Lu;0;L;004F 0300;;;;N;LATIN CAPITAL LETTER O GRAVE;;;00F2;
+00D3;LATIN CAPITAL LETTER O WITH ACUTE;Lu;0;L;004F 0301;;;;N;LATIN CAPITAL LETTER O ACUTE;;;00F3;
+00D4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX;Lu;0;L;004F 0302;;;;N;LATIN CAPITAL LETTER O CIRCUMFLEX;;;00F4;
+00D5;LATIN CAPITAL LETTER O WITH TILDE;Lu;0;L;004F 0303;;;;N;LATIN CAPITAL LETTER O TILDE;;;00F5;
+00D6;LATIN CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;004F 0308;;;;N;LATIN CAPITAL LETTER O DIAERESIS;;;00F6;
+00D7;MULTIPLICATION SIGN;Sm;0;ON;;;;;N;;;;;
+00D8;LATIN CAPITAL LETTER O WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O SLASH;;;00F8;
+00D9;LATIN CAPITAL LETTER U WITH GRAVE;Lu;0;L;0055 0300;;;;N;LATIN CAPITAL LETTER U GRAVE;;;00F9;
+00DA;LATIN CAPITAL LETTER U WITH ACUTE;Lu;0;L;0055 0301;;;;N;LATIN CAPITAL LETTER U ACUTE;;;00FA;
+00DB;LATIN CAPITAL LETTER U WITH CIRCUMFLEX;Lu;0;L;0055 0302;;;;N;LATIN CAPITAL LETTER U CIRCUMFLEX;;;00FB;
+00DC;LATIN CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0055 0308;;;;N;LATIN CAPITAL LETTER U DIAERESIS;;;00FC;
+00DD;LATIN CAPITAL LETTER Y WITH ACUTE;Lu;0;L;0059 0301;;;;N;LATIN CAPITAL LETTER Y ACUTE;;;00FD;
+00DE;LATIN CAPITAL LETTER THORN;Lu;0;L;;;;;N;;Icelandic;;00FE;
+00DF;LATIN SMALL LETTER SHARP S;Ll;0;L;;;;;N;;German;;;
+00E0;LATIN SMALL LETTER A WITH GRAVE;Ll;0;L;0061 0300;;;;N;LATIN SMALL LETTER A GRAVE;;00C0;;00C0
+00E1;LATIN SMALL LETTER A WITH ACUTE;Ll;0;L;0061 0301;;;;N;LATIN SMALL LETTER A ACUTE;;00C1;;00C1
+00E2;LATIN SMALL LETTER A WITH CIRCUMFLEX;Ll;0;L;0061 0302;;;;N;LATIN SMALL LETTER A CIRCUMFLEX;;00C2;;00C2
+00E3;LATIN SMALL LETTER A WITH TILDE;Ll;0;L;0061 0303;;;;N;LATIN SMALL LETTER A TILDE;;00C3;;00C3
+00E4;LATIN SMALL LETTER A WITH DIAERESIS;Ll;0;L;0061 0308;;;;N;LATIN SMALL LETTER A DIAERESIS;;00C4;;00C4
+00E5;LATIN SMALL LETTER A WITH RING ABOVE;Ll;0;L;0061 030A;;;;N;LATIN SMALL LETTER A RING;;00C5;;00C5
+00E6;LATIN SMALL LETTER AE;Ll;0;L;;;;;N;LATIN SMALL LETTER A E;ash *;00C6;;00C6
+00E7;LATIN SMALL LETTER C WITH CEDILLA;Ll;0;L;0063 0327;;;;N;LATIN SMALL LETTER C CEDILLA;;00C7;;00C7
+00E8;LATIN SMALL LETTER E WITH GRAVE;Ll;0;L;0065 0300;;;;N;LATIN SMALL LETTER E GRAVE;;00C8;;00C8
+00E9;LATIN SMALL LETTER E WITH ACUTE;Ll;0;L;0065 0301;;;;N;LATIN SMALL LETTER E ACUTE;;00C9;;00C9
+00EA;LATIN SMALL LETTER E WITH CIRCUMFLEX;Ll;0;L;0065 0302;;;;N;LATIN SMALL LETTER E CIRCUMFLEX;;00CA;;00CA
+00EB;LATIN SMALL LETTER E WITH DIAERESIS;Ll;0;L;0065 0308;;;;N;LATIN SMALL LETTER E DIAERESIS;;00CB;;00CB
+00EC;LATIN SMALL LETTER I WITH GRAVE;Ll;0;L;0069 0300;;;;N;LATIN SMALL LETTER I GRAVE;;00CC;;00CC
+00ED;LATIN SMALL LETTER I WITH ACUTE;Ll;0;L;0069 0301;;;;N;LATIN SMALL LETTER I ACUTE;;00CD;;00CD
+00EE;LATIN SMALL LETTER I WITH CIRCUMFLEX;Ll;0;L;0069 0302;;;;N;LATIN SMALL LETTER I CIRCUMFLEX;;00CE;;00CE
+00EF;LATIN SMALL LETTER I WITH DIAERESIS;Ll;0;L;0069 0308;;;;N;LATIN SMALL LETTER I DIAERESIS;;00CF;;00CF
+00F0;LATIN SMALL LETTER ETH;Ll;0;L;;;;;N;;Icelandic;00D0;;00D0
+00F1;LATIN SMALL LETTER N WITH TILDE;Ll;0;L;006E 0303;;;;N;LATIN SMALL LETTER N TILDE;;00D1;;00D1
+00F2;LATIN SMALL LETTER O WITH GRAVE;Ll;0;L;006F 0300;;;;N;LATIN SMALL LETTER O GRAVE;;00D2;;00D2
+00F3;LATIN SMALL LETTER O WITH ACUTE;Ll;0;L;006F 0301;;;;N;LATIN SMALL LETTER O ACUTE;;00D3;;00D3
+00F4;LATIN SMALL LETTER O WITH CIRCUMFLEX;Ll;0;L;006F 0302;;;;N;LATIN SMALL LETTER O CIRCUMFLEX;;00D4;;00D4
+00F5;LATIN SMALL LETTER O WITH TILDE;Ll;0;L;006F 0303;;;;N;LATIN SMALL LETTER O TILDE;;00D5;;00D5
+00F6;LATIN SMALL LETTER O WITH DIAERESIS;Ll;0;L;006F 0308;;;;N;LATIN SMALL LETTER O DIAERESIS;;00D6;;00D6
+00F7;DIVISION SIGN;Sm;0;ON;;;;;N;;;;;
+00F8;LATIN SMALL LETTER O WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER O SLASH;;00D8;;00D8
+00F9;LATIN SMALL LETTER U WITH GRAVE;Ll;0;L;0075 0300;;;;N;LATIN SMALL LETTER U GRAVE;;00D9;;00D9
+00FA;LATIN SMALL LETTER U WITH ACUTE;Ll;0;L;0075 0301;;;;N;LATIN SMALL LETTER U ACUTE;;00DA;;00DA
+00FB;LATIN SMALL LETTER U WITH CIRCUMFLEX;Ll;0;L;0075 0302;;;;N;LATIN SMALL LETTER U CIRCUMFLEX;;00DB;;00DB
+00FC;LATIN SMALL LETTER U WITH DIAERESIS;Ll;0;L;0075 0308;;;;N;LATIN SMALL LETTER U DIAERESIS;;00DC;;00DC
+00FD;LATIN SMALL LETTER Y WITH ACUTE;Ll;0;L;0079 0301;;;;N;LATIN SMALL LETTER Y ACUTE;;00DD;;00DD
+00FE;LATIN SMALL LETTER THORN;Ll;0;L;;;;;N;;Icelandic;00DE;;00DE
+00FF;LATIN SMALL LETTER Y WITH DIAERESIS;Ll;0;L;0079 0308;;;;N;LATIN SMALL LETTER Y DIAERESIS;;0178;;0178
+0100;LATIN CAPITAL LETTER A WITH MACRON;Lu;0;L;0041 0304;;;;N;LATIN CAPITAL LETTER A MACRON;;;0101;
+0101;LATIN SMALL LETTER A WITH MACRON;Ll;0;L;0061 0304;;;;N;LATIN SMALL LETTER A MACRON;;0100;;0100
+0102;LATIN CAPITAL LETTER A WITH BREVE;Lu;0;L;0041 0306;;;;N;LATIN CAPITAL LETTER A BREVE;;;0103;
+0103;LATIN SMALL LETTER A WITH BREVE;Ll;0;L;0061 0306;;;;N;LATIN SMALL LETTER A BREVE;;0102;;0102
+0104;LATIN CAPITAL LETTER A WITH OGONEK;Lu;0;L;0041 0328;;;;N;LATIN CAPITAL LETTER A OGONEK;;;0105;
+0105;LATIN SMALL LETTER A WITH OGONEK;Ll;0;L;0061 0328;;;;N;LATIN SMALL LETTER A OGONEK;;0104;;0104
+0106;LATIN CAPITAL LETTER C WITH ACUTE;Lu;0;L;0043 0301;;;;N;LATIN CAPITAL LETTER C ACUTE;;;0107;
+0107;LATIN SMALL LETTER C WITH ACUTE;Ll;0;L;0063 0301;;;;N;LATIN SMALL LETTER C ACUTE;;0106;;0106
+0108;LATIN CAPITAL LETTER C WITH CIRCUMFLEX;Lu;0;L;0043 0302;;;;N;LATIN CAPITAL LETTER C CIRCUMFLEX;;;0109;
+0109;LATIN SMALL LETTER C WITH CIRCUMFLEX;Ll;0;L;0063 0302;;;;N;LATIN SMALL LETTER C CIRCUMFLEX;;0108;;0108
+010A;LATIN CAPITAL LETTER C WITH DOT ABOVE;Lu;0;L;0043 0307;;;;N;LATIN CAPITAL LETTER C DOT;;;010B;
+010B;LATIN SMALL LETTER C WITH DOT ABOVE;Ll;0;L;0063 0307;;;;N;LATIN SMALL LETTER C DOT;;010A;;010A
+010C;LATIN CAPITAL LETTER C WITH CARON;Lu;0;L;0043 030C;;;;N;LATIN CAPITAL LETTER C HACEK;;;010D;
+010D;LATIN SMALL LETTER C WITH CARON;Ll;0;L;0063 030C;;;;N;LATIN SMALL LETTER C HACEK;;010C;;010C
+010E;LATIN CAPITAL LETTER D WITH CARON;Lu;0;L;0044 030C;;;;N;LATIN CAPITAL LETTER D HACEK;;;010F;
+010F;LATIN SMALL LETTER D WITH CARON;Ll;0;L;0064 030C;;;;N;LATIN SMALL LETTER D HACEK;;010E;;010E
+0110;LATIN CAPITAL LETTER D WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D BAR;;;0111;
+0111;LATIN SMALL LETTER D WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER D BAR;;0110;;0110
+0112;LATIN CAPITAL LETTER E WITH MACRON;Lu;0;L;0045 0304;;;;N;LATIN CAPITAL LETTER E MACRON;;;0113;
+0113;LATIN SMALL LETTER E WITH MACRON;Ll;0;L;0065 0304;;;;N;LATIN SMALL LETTER E MACRON;;0112;;0112
+0114;LATIN CAPITAL LETTER E WITH BREVE;Lu;0;L;0045 0306;;;;N;LATIN CAPITAL LETTER E BREVE;;;0115;
+0115;LATIN SMALL LETTER E WITH BREVE;Ll;0;L;0065 0306;;;;N;LATIN SMALL LETTER E BREVE;;0114;;0114
+0116;LATIN CAPITAL LETTER E WITH DOT ABOVE;Lu;0;L;0045 0307;;;;N;LATIN CAPITAL LETTER E DOT;;;0117;
+0117;LATIN SMALL LETTER E WITH DOT ABOVE;Ll;0;L;0065 0307;;;;N;LATIN SMALL LETTER E DOT;;0116;;0116
+0118;LATIN CAPITAL LETTER E WITH OGONEK;Lu;0;L;0045 0328;;;;N;LATIN CAPITAL LETTER E OGONEK;;;0119;
+0119;LATIN SMALL LETTER E WITH OGONEK;Ll;0;L;0065 0328;;;;N;LATIN SMALL LETTER E OGONEK;;0118;;0118
+011A;LATIN CAPITAL LETTER E WITH CARON;Lu;0;L;0045 030C;;;;N;LATIN CAPITAL LETTER E HACEK;;;011B;
+011B;LATIN SMALL LETTER E WITH CARON;Ll;0;L;0065 030C;;;;N;LATIN SMALL LETTER E HACEK;;011A;;011A
+011C;LATIN CAPITAL LETTER G WITH CIRCUMFLEX;Lu;0;L;0047 0302;;;;N;LATIN CAPITAL LETTER G CIRCUMFLEX;;;011D;
+011D;LATIN SMALL LETTER G WITH CIRCUMFLEX;Ll;0;L;0067 0302;;;;N;LATIN SMALL LETTER G CIRCUMFLEX;;011C;;011C
+011E;LATIN CAPITAL LETTER G WITH BREVE;Lu;0;L;0047 0306;;;;N;LATIN CAPITAL LETTER G BREVE;;;011F;
+011F;LATIN SMALL LETTER G WITH BREVE;Ll;0;L;0067 0306;;;;N;LATIN SMALL LETTER G BREVE;;011E;;011E
+0120;LATIN CAPITAL LETTER G WITH DOT ABOVE;Lu;0;L;0047 0307;;;;N;LATIN CAPITAL LETTER G DOT;;;0121;
+0121;LATIN SMALL LETTER G WITH DOT ABOVE;Ll;0;L;0067 0307;;;;N;LATIN SMALL LETTER G DOT;;0120;;0120
+0122;LATIN CAPITAL LETTER G WITH CEDILLA;Lu;0;L;0047 0327;;;;N;LATIN CAPITAL LETTER G CEDILLA;;;0123;
+0123;LATIN SMALL LETTER G WITH CEDILLA;Ll;0;L;0067 0327;;;;N;LATIN SMALL LETTER G CEDILLA;;0122;;0122
+0124;LATIN CAPITAL LETTER H WITH CIRCUMFLEX;Lu;0;L;0048 0302;;;;N;LATIN CAPITAL LETTER H CIRCUMFLEX;;;0125;
+0125;LATIN SMALL LETTER H WITH CIRCUMFLEX;Ll;0;L;0068 0302;;;;N;LATIN SMALL LETTER H CIRCUMFLEX;;0124;;0124
+0126;LATIN CAPITAL LETTER H WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER H BAR;;;0127;
+0127;LATIN SMALL LETTER H WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER H BAR;;0126;;0126
+0128;LATIN CAPITAL LETTER I WITH TILDE;Lu;0;L;0049 0303;;;;N;LATIN CAPITAL LETTER I TILDE;;;0129;
+0129;LATIN SMALL LETTER I WITH TILDE;Ll;0;L;0069 0303;;;;N;LATIN SMALL LETTER I TILDE;;0128;;0128
+012A;LATIN CAPITAL LETTER I WITH MACRON;Lu;0;L;0049 0304;;;;N;LATIN CAPITAL LETTER I MACRON;;;012B;
+012B;LATIN SMALL LETTER I WITH MACRON;Ll;0;L;0069 0304;;;;N;LATIN SMALL LETTER I MACRON;;012A;;012A
+012C;LATIN CAPITAL LETTER I WITH BREVE;Lu;0;L;0049 0306;;;;N;LATIN CAPITAL LETTER I BREVE;;;012D;
+012D;LATIN SMALL LETTER I WITH BREVE;Ll;0;L;0069 0306;;;;N;LATIN SMALL LETTER I BREVE;;012C;;012C
+012E;LATIN CAPITAL LETTER I WITH OGONEK;Lu;0;L;0049 0328;;;;N;LATIN CAPITAL LETTER I OGONEK;;;012F;
+012F;LATIN SMALL LETTER I WITH OGONEK;Ll;0;L;0069 0328;;;;N;LATIN SMALL LETTER I OGONEK;;012E;;012E
+0130;LATIN CAPITAL LETTER I WITH DOT ABOVE;Lu;0;L;0049 0307;;;;N;LATIN CAPITAL LETTER I DOT;;;0069;
+0131;LATIN SMALL LETTER DOTLESS I;Ll;0;L;;;;;N;;;0049;;0049
+0132;LATIN CAPITAL LIGATURE IJ;Lu;0;L;<compat> 0049 004A;;;;N;LATIN CAPITAL LETTER I J;;;0133;
+0133;LATIN SMALL LIGATURE IJ;Ll;0;L;<compat> 0069 006A;;;;N;LATIN SMALL LETTER I J;;0132;;0132
+0134;LATIN CAPITAL LETTER J WITH CIRCUMFLEX;Lu;0;L;004A 0302;;;;N;LATIN CAPITAL LETTER J CIRCUMFLEX;;;0135;
+0135;LATIN SMALL LETTER J WITH CIRCUMFLEX;Ll;0;L;006A 0302;;;;N;LATIN SMALL LETTER J CIRCUMFLEX;;0134;;0134
+0136;LATIN CAPITAL LETTER K WITH CEDILLA;Lu;0;L;004B 0327;;;;N;LATIN CAPITAL LETTER K CEDILLA;;;0137;
+0137;LATIN SMALL LETTER K WITH CEDILLA;Ll;0;L;006B 0327;;;;N;LATIN SMALL LETTER K CEDILLA;;0136;;0136
+0138;LATIN SMALL LETTER KRA;Ll;0;L;;;;;N;;Greenlandic;;;
+0139;LATIN CAPITAL LETTER L WITH ACUTE;Lu;0;L;004C 0301;;;;N;LATIN CAPITAL LETTER L ACUTE;;;013A;
+013A;LATIN SMALL LETTER L WITH ACUTE;Ll;0;L;006C 0301;;;;N;LATIN SMALL LETTER L ACUTE;;0139;;0139
+013B;LATIN CAPITAL LETTER L WITH CEDILLA;Lu;0;L;004C 0327;;;;N;LATIN CAPITAL LETTER L CEDILLA;;;013C;
+013C;LATIN SMALL LETTER L WITH CEDILLA;Ll;0;L;006C 0327;;;;N;LATIN SMALL LETTER L CEDILLA;;013B;;013B
+013D;LATIN CAPITAL LETTER L WITH CARON;Lu;0;L;004C 030C;;;;N;LATIN CAPITAL LETTER L HACEK;;;013E;
+013E;LATIN SMALL LETTER L WITH CARON;Ll;0;L;006C 030C;;;;N;LATIN SMALL LETTER L HACEK;;013D;;013D
+013F;LATIN CAPITAL LETTER L WITH MIDDLE DOT;Lu;0;L;<compat> 004C 00B7;;;;N;;;;0140;
+0140;LATIN SMALL LETTER L WITH MIDDLE DOT;Ll;0;L;<compat> 006C 00B7;;;;N;;;013F;;013F
+0141;LATIN CAPITAL LETTER L WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER L SLASH;;;0142;
+0142;LATIN SMALL LETTER L WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER L SLASH;;0141;;0141
+0143;LATIN CAPITAL LETTER N WITH ACUTE;Lu;0;L;004E 0301;;;;N;LATIN CAPITAL LETTER N ACUTE;;;0144;
+0144;LATIN SMALL LETTER N WITH ACUTE;Ll;0;L;006E 0301;;;;N;LATIN SMALL LETTER N ACUTE;;0143;;0143
+0145;LATIN CAPITAL LETTER N WITH CEDILLA;Lu;0;L;004E 0327;;;;N;LATIN CAPITAL LETTER N CEDILLA;;;0146;
+0146;LATIN SMALL LETTER N WITH CEDILLA;Ll;0;L;006E 0327;;;;N;LATIN SMALL LETTER N CEDILLA;;0145;;0145
+0147;LATIN CAPITAL LETTER N WITH CARON;Lu;0;L;004E 030C;;;;N;LATIN CAPITAL LETTER N HACEK;;;0148;
+0148;LATIN SMALL LETTER N WITH CARON;Ll;0;L;006E 030C;;;;N;LATIN SMALL LETTER N HACEK;;0147;;0147
+0149;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE;Ll;0;L;<compat> 02BC 006E;;;;N;LATIN SMALL LETTER APOSTROPHE N;;;;
+014A;LATIN CAPITAL LETTER ENG;Lu;0;L;;;;;N;;Sami;;014B;
+014B;LATIN SMALL LETTER ENG;Ll;0;L;;;;;N;;Sami;014A;;014A
+014C;LATIN CAPITAL LETTER O WITH MACRON;Lu;0;L;004F 0304;;;;N;LATIN CAPITAL LETTER O MACRON;;;014D;
+014D;LATIN SMALL LETTER O WITH MACRON;Ll;0;L;006F 0304;;;;N;LATIN SMALL LETTER O MACRON;;014C;;014C
+014E;LATIN CAPITAL LETTER O WITH BREVE;Lu;0;L;004F 0306;;;;N;LATIN CAPITAL LETTER O BREVE;;;014F;
+014F;LATIN SMALL LETTER O WITH BREVE;Ll;0;L;006F 0306;;;;N;LATIN SMALL LETTER O BREVE;;014E;;014E
+0150;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE;Lu;0;L;004F 030B;;;;N;LATIN CAPITAL LETTER O DOUBLE ACUTE;;;0151;
+0151;LATIN SMALL LETTER O WITH DOUBLE ACUTE;Ll;0;L;006F 030B;;;;N;LATIN SMALL LETTER O DOUBLE ACUTE;;0150;;0150
+0152;LATIN CAPITAL LIGATURE OE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O E;;;0153;
+0153;LATIN SMALL LIGATURE OE;Ll;0;L;;;;;N;LATIN SMALL LETTER O E;;0152;;0152
+0154;LATIN CAPITAL LETTER R WITH ACUTE;Lu;0;L;0052 0301;;;;N;LATIN CAPITAL LETTER R ACUTE;;;0155;
+0155;LATIN SMALL LETTER R WITH ACUTE;Ll;0;L;0072 0301;;;;N;LATIN SMALL LETTER R ACUTE;;0154;;0154
+0156;LATIN CAPITAL LETTER R WITH CEDILLA;Lu;0;L;0052 0327;;;;N;LATIN CAPITAL LETTER R CEDILLA;;;0157;
+0157;LATIN SMALL LETTER R WITH CEDILLA;Ll;0;L;0072 0327;;;;N;LATIN SMALL LETTER R CEDILLA;;0156;;0156
+0158;LATIN CAPITAL LETTER R WITH CARON;Lu;0;L;0052 030C;;;;N;LATIN CAPITAL LETTER R HACEK;;;0159;
+0159;LATIN SMALL LETTER R WITH CARON;Ll;0;L;0072 030C;;;;N;LATIN SMALL LETTER R HACEK;;0158;;0158
+015A;LATIN CAPITAL LETTER S WITH ACUTE;Lu;0;L;0053 0301;;;;N;LATIN CAPITAL LETTER S ACUTE;;;015B;
+015B;LATIN SMALL LETTER S WITH ACUTE;Ll;0;L;0073 0301;;;;N;LATIN SMALL LETTER S ACUTE;;015A;;015A
+015C;LATIN CAPITAL LETTER S WITH CIRCUMFLEX;Lu;0;L;0053 0302;;;;N;LATIN CAPITAL LETTER S CIRCUMFLEX;;;015D;
+015D;LATIN SMALL LETTER S WITH CIRCUMFLEX;Ll;0;L;0073 0302;;;;N;LATIN SMALL LETTER S CIRCUMFLEX;;015C;;015C
+015E;LATIN CAPITAL LETTER S WITH CEDILLA;Lu;0;L;0053 0327;;;;N;LATIN CAPITAL LETTER S CEDILLA;*;;015F;
+015F;LATIN SMALL LETTER S WITH CEDILLA;Ll;0;L;0073 0327;;;;N;LATIN SMALL LETTER S CEDILLA;*;015E;;015E
+0160;LATIN CAPITAL LETTER S WITH CARON;Lu;0;L;0053 030C;;;;N;LATIN CAPITAL LETTER S HACEK;;;0161;
+0161;LATIN SMALL LETTER S WITH CARON;Ll;0;L;0073 030C;;;;N;LATIN SMALL LETTER S HACEK;;0160;;0160
+0162;LATIN CAPITAL LETTER T WITH CEDILLA;Lu;0;L;0054 0327;;;;N;LATIN CAPITAL LETTER T CEDILLA;*;;0163;
+0163;LATIN SMALL LETTER T WITH CEDILLA;Ll;0;L;0074 0327;;;;N;LATIN SMALL LETTER T CEDILLA;*;0162;;0162
+0164;LATIN CAPITAL LETTER T WITH CARON;Lu;0;L;0054 030C;;;;N;LATIN CAPITAL LETTER T HACEK;;;0165;
+0165;LATIN SMALL LETTER T WITH CARON;Ll;0;L;0074 030C;;;;N;LATIN SMALL LETTER T HACEK;;0164;;0164
+0166;LATIN CAPITAL LETTER T WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T BAR;;;0167;
+0167;LATIN SMALL LETTER T WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER T BAR;;0166;;0166
+0168;LATIN CAPITAL LETTER U WITH TILDE;Lu;0;L;0055 0303;;;;N;LATIN CAPITAL LETTER U TILDE;;;0169;
+0169;LATIN SMALL LETTER U WITH TILDE;Ll;0;L;0075 0303;;;;N;LATIN SMALL LETTER U TILDE;;0168;;0168
+016A;LATIN CAPITAL LETTER U WITH MACRON;Lu;0;L;0055 0304;;;;N;LATIN CAPITAL LETTER U MACRON;;;016B;
+016B;LATIN SMALL LETTER U WITH MACRON;Ll;0;L;0075 0304;;;;N;LATIN SMALL LETTER U MACRON;;016A;;016A
+016C;LATIN CAPITAL LETTER U WITH BREVE;Lu;0;L;0055 0306;;;;N;LATIN CAPITAL LETTER U BREVE;;;016D;
+016D;LATIN SMALL LETTER U WITH BREVE;Ll;0;L;0075 0306;;;;N;LATIN SMALL LETTER U BREVE;;016C;;016C
+016E;LATIN CAPITAL LETTER U WITH RING ABOVE;Lu;0;L;0055 030A;;;;N;LATIN CAPITAL LETTER U RING;;;016F;
+016F;LATIN SMALL LETTER U WITH RING ABOVE;Ll;0;L;0075 030A;;;;N;LATIN SMALL LETTER U RING;;016E;;016E
+0170;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0055 030B;;;;N;LATIN CAPITAL LETTER U DOUBLE ACUTE;;;0171;
+0171;LATIN SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0075 030B;;;;N;LATIN SMALL LETTER U DOUBLE ACUTE;;0170;;0170
+0172;LATIN CAPITAL LETTER U WITH OGONEK;Lu;0;L;0055 0328;;;;N;LATIN CAPITAL LETTER U OGONEK;;;0173;
+0173;LATIN SMALL LETTER U WITH OGONEK;Ll;0;L;0075 0328;;;;N;LATIN SMALL LETTER U OGONEK;;0172;;0172
+0174;LATIN CAPITAL LETTER W WITH CIRCUMFLEX;Lu;0;L;0057 0302;;;;N;LATIN CAPITAL LETTER W CIRCUMFLEX;;;0175;
+0175;LATIN SMALL LETTER W WITH CIRCUMFLEX;Ll;0;L;0077 0302;;;;N;LATIN SMALL LETTER W CIRCUMFLEX;;0174;;0174
+0176;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX;Lu;0;L;0059 0302;;;;N;LATIN CAPITAL LETTER Y CIRCUMFLEX;;;0177;
+0177;LATIN SMALL LETTER Y WITH CIRCUMFLEX;Ll;0;L;0079 0302;;;;N;LATIN SMALL LETTER Y CIRCUMFLEX;;0176;;0176
+0178;LATIN CAPITAL LETTER Y WITH DIAERESIS;Lu;0;L;0059 0308;;;;N;LATIN CAPITAL LETTER Y DIAERESIS;;;00FF;
+0179;LATIN CAPITAL LETTER Z WITH ACUTE;Lu;0;L;005A 0301;;;;N;LATIN CAPITAL LETTER Z ACUTE;;;017A;
+017A;LATIN SMALL LETTER Z WITH ACUTE;Ll;0;L;007A 0301;;;;N;LATIN SMALL LETTER Z ACUTE;;0179;;0179
+017B;LATIN CAPITAL LETTER Z WITH DOT ABOVE;Lu;0;L;005A 0307;;;;N;LATIN CAPITAL LETTER Z DOT;;;017C;
+017C;LATIN SMALL LETTER Z WITH DOT ABOVE;Ll;0;L;007A 0307;;;;N;LATIN SMALL LETTER Z DOT;;017B;;017B
+017D;LATIN CAPITAL LETTER Z WITH CARON;Lu;0;L;005A 030C;;;;N;LATIN CAPITAL LETTER Z HACEK;;;017E;
+017E;LATIN SMALL LETTER Z WITH CARON;Ll;0;L;007A 030C;;;;N;LATIN SMALL LETTER Z HACEK;;017D;;017D
+017F;LATIN SMALL LETTER LONG S;Ll;0;L;<compat> 0073;;;;N;;;0053;;0053
+0180;LATIN SMALL LETTER B WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER B BAR;;;;
+0181;LATIN CAPITAL LETTER B WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B HOOK;;;0253;
+0182;LATIN CAPITAL LETTER B WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B TOPBAR;;;0183;
+0183;LATIN SMALL LETTER B WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER B TOPBAR;;0182;;0182
+0184;LATIN CAPITAL LETTER TONE SIX;Lu;0;L;;;;;N;;;;0185;
+0185;LATIN SMALL LETTER TONE SIX;Ll;0;L;;;;;N;;;0184;;0184
+0186;LATIN CAPITAL LETTER OPEN O;Lu;0;L;;;;;N;;;;0254;
+0187;LATIN CAPITAL LETTER C WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER C HOOK;;;0188;
+0188;LATIN SMALL LETTER C WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER C HOOK;;0187;;0187
+0189;LATIN CAPITAL LETTER AFRICAN D;Lu;0;L;;;;;N;;*;;0256;
+018A;LATIN CAPITAL LETTER D WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D HOOK;;;0257;
+018B;LATIN CAPITAL LETTER D WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D TOPBAR;;;018C;
+018C;LATIN SMALL LETTER D WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER D TOPBAR;;018B;;018B
+018D;LATIN SMALL LETTER TURNED DELTA;Ll;0;L;;;;;N;;;;;
+018E;LATIN CAPITAL LETTER REVERSED E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER TURNED E;;;01DD;
+018F;LATIN CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;0259;
+0190;LATIN CAPITAL LETTER OPEN E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER EPSILON;;;025B;
+0191;LATIN CAPITAL LETTER F WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER F HOOK;;;0192;
+0192;LATIN SMALL LETTER F WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT F;;0191;;0191
+0193;LATIN CAPITAL LETTER G WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G HOOK;;;0260;
+0194;LATIN CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;0263;
+0195;LATIN SMALL LETTER HV;Ll;0;L;;;;;N;LATIN SMALL LETTER H V;hwair;01F6;;01F6
+0196;LATIN CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;0269;
+0197;LATIN CAPITAL LETTER I WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED I;;;0268;
+0198;LATIN CAPITAL LETTER K WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER K HOOK;;;0199;
+0199;LATIN SMALL LETTER K WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER K HOOK;;0198;;0198
+019A;LATIN SMALL LETTER L WITH BAR;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED L;;;;
+019B;LATIN SMALL LETTER LAMBDA WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED LAMBDA;;;;
+019C;LATIN CAPITAL LETTER TURNED M;Lu;0;L;;;;;N;;;;026F;
+019D;LATIN CAPITAL LETTER N WITH LEFT HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER N HOOK;;;0272;
+019E;LATIN SMALL LETTER N WITH LONG RIGHT LEG;Ll;0;L;;;;;N;;;;;
+019F;LATIN CAPITAL LETTER O WITH MIDDLE TILDE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED O;*;;0275;
+01A0;LATIN CAPITAL LETTER O WITH HORN;Lu;0;L;004F 031B;;;;N;LATIN CAPITAL LETTER O HORN;;;01A1;
+01A1;LATIN SMALL LETTER O WITH HORN;Ll;0;L;006F 031B;;;;N;LATIN SMALL LETTER O HORN;;01A0;;01A0
+01A2;LATIN CAPITAL LETTER OI;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O I;gha;;01A3;
+01A3;LATIN SMALL LETTER OI;Ll;0;L;;;;;N;LATIN SMALL LETTER O I;gha;01A2;;01A2
+01A4;LATIN CAPITAL LETTER P WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER P HOOK;;;01A5;
+01A5;LATIN SMALL LETTER P WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER P HOOK;;01A4;;01A4
+01A6;LATIN LETTER YR;Lu;0;L;;;;;N;LATIN LETTER Y R;*;;0280;
+01A7;LATIN CAPITAL LETTER TONE TWO;Lu;0;L;;;;;N;;;;01A8;
+01A8;LATIN SMALL LETTER TONE TWO;Ll;0;L;;;;;N;;;01A7;;01A7
+01A9;LATIN CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;0283;
+01AA;LATIN LETTER REVERSED ESH LOOP;Ll;0;L;;;;;N;;;;;
+01AB;LATIN SMALL LETTER T WITH PALATAL HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T PALATAL HOOK;;;;
+01AC;LATIN CAPITAL LETTER T WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T HOOK;;;01AD;
+01AD;LATIN SMALL LETTER T WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T HOOK;;01AC;;01AC
+01AE;LATIN CAPITAL LETTER T WITH RETROFLEX HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T RETROFLEX HOOK;;;0288;
+01AF;LATIN CAPITAL LETTER U WITH HORN;Lu;0;L;0055 031B;;;;N;LATIN CAPITAL LETTER U HORN;;;01B0;
+01B0;LATIN SMALL LETTER U WITH HORN;Ll;0;L;0075 031B;;;;N;LATIN SMALL LETTER U HORN;;01AF;;01AF
+01B1;LATIN CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;028A;
+01B2;LATIN CAPITAL LETTER V WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER SCRIPT V;;;028B;
+01B3;LATIN CAPITAL LETTER Y WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Y HOOK;;;01B4;
+01B4;LATIN SMALL LETTER Y WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Y HOOK;;01B3;;01B3
+01B5;LATIN CAPITAL LETTER Z WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Z BAR;;;01B6;
+01B6;LATIN SMALL LETTER Z WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER Z BAR;;01B5;;01B5
+01B7;LATIN CAPITAL LETTER EZH;Lu;0;L;;;;;N;LATIN CAPITAL LETTER YOGH;;;0292;
+01B8;LATIN CAPITAL LETTER EZH REVERSED;Lu;0;L;;;;;N;LATIN CAPITAL LETTER REVERSED YOGH;;;01B9;
+01B9;LATIN SMALL LETTER EZH REVERSED;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED YOGH;;01B8;;01B8
+01BA;LATIN SMALL LETTER EZH WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH WITH TAIL;;;;
+01BB;LATIN LETTER TWO WITH STROKE;Lo;0;L;;;;;N;LATIN LETTER TWO BAR;;;;
+01BC;LATIN CAPITAL LETTER TONE FIVE;Lu;0;L;;;;;N;;;;01BD;
+01BD;LATIN SMALL LETTER TONE FIVE;Ll;0;L;;;;;N;;;01BC;;01BC
+01BE;LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER INVERTED GLOTTAL STOP BAR;;;;
+01BF;LATIN LETTER WYNN;Ll;0;L;;;;;N;;;01F7;;01F7
+01C0;LATIN LETTER DENTAL CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE;;;;
+01C1;LATIN LETTER LATERAL CLICK;Lo;0;L;;;;;N;LATIN LETTER DOUBLE PIPE;;;;
+01C2;LATIN LETTER ALVEOLAR CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE DOUBLE BAR;;;;
+01C3;LATIN LETTER RETROFLEX CLICK;Lo;0;L;;;;;N;LATIN LETTER EXCLAMATION MARK;;;;
+01C4;LATIN CAPITAL LETTER DZ WITH CARON;Lu;0;L;<compat> 0044 017D;;;;N;LATIN CAPITAL LETTER D Z HACEK;;;01C6;01C5
+01C5;LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON;Lt;0;L;<compat> 0044 017E;;;;N;LATIN LETTER CAPITAL D SMALL Z HACEK;;01C4;01C6;
+01C6;LATIN SMALL LETTER DZ WITH CARON;Ll;0;L;<compat> 0064 017E;;;;N;LATIN SMALL LETTER D Z HACEK;;01C4;;01C5
+01C7;LATIN CAPITAL LETTER LJ;Lu;0;L;<compat> 004C 004A;;;;N;LATIN CAPITAL LETTER L J;;;01C9;01C8
+01C8;LATIN CAPITAL LETTER L WITH SMALL LETTER J;Lt;0;L;<compat> 004C 006A;;;;N;LATIN LETTER CAPITAL L SMALL J;;01C7;01C9;
+01C9;LATIN SMALL LETTER LJ;Ll;0;L;<compat> 006C 006A;;;;N;LATIN SMALL LETTER L J;;01C7;;01C8
+01CA;LATIN CAPITAL LETTER NJ;Lu;0;L;<compat> 004E 004A;;;;N;LATIN CAPITAL LETTER N J;;;01CC;01CB
+01CB;LATIN CAPITAL LETTER N WITH SMALL LETTER J;Lt;0;L;<compat> 004E 006A;;;;N;LATIN LETTER CAPITAL N SMALL J;;01CA;01CC;
+01CC;LATIN SMALL LETTER NJ;Ll;0;L;<compat> 006E 006A;;;;N;LATIN SMALL LETTER N J;;01CA;;01CB
+01CD;LATIN CAPITAL LETTER A WITH CARON;Lu;0;L;0041 030C;;;;N;LATIN CAPITAL LETTER A HACEK;;;01CE;
+01CE;LATIN SMALL LETTER A WITH CARON;Ll;0;L;0061 030C;;;;N;LATIN SMALL LETTER A HACEK;;01CD;;01CD
+01CF;LATIN CAPITAL LETTER I WITH CARON;Lu;0;L;0049 030C;;;;N;LATIN CAPITAL LETTER I HACEK;;;01D0;
+01D0;LATIN SMALL LETTER I WITH CARON;Ll;0;L;0069 030C;;;;N;LATIN SMALL LETTER I HACEK;;01CF;;01CF
+01D1;LATIN CAPITAL LETTER O WITH CARON;Lu;0;L;004F 030C;;;;N;LATIN CAPITAL LETTER O HACEK;;;01D2;
+01D2;LATIN SMALL LETTER O WITH CARON;Ll;0;L;006F 030C;;;;N;LATIN SMALL LETTER O HACEK;;01D1;;01D1
+01D3;LATIN CAPITAL LETTER U WITH CARON;Lu;0;L;0055 030C;;;;N;LATIN CAPITAL LETTER U HACEK;;;01D4;
+01D4;LATIN SMALL LETTER U WITH CARON;Ll;0;L;0075 030C;;;;N;LATIN SMALL LETTER U HACEK;;01D3;;01D3
+01D5;LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON;Lu;0;L;00DC 0304;;;;N;LATIN CAPITAL LETTER U DIAERESIS MACRON;;;01D6;
+01D6;LATIN SMALL LETTER U WITH DIAERESIS AND MACRON;Ll;0;L;00FC 0304;;;;N;LATIN SMALL LETTER U DIAERESIS MACRON;;01D5;;01D5
+01D7;LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE;Lu;0;L;00DC 0301;;;;N;LATIN CAPITAL LETTER U DIAERESIS ACUTE;;;01D8;
+01D8;LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE;Ll;0;L;00FC 0301;;;;N;LATIN SMALL LETTER U DIAERESIS ACUTE;;01D7;;01D7
+01D9;LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON;Lu;0;L;00DC 030C;;;;N;LATIN CAPITAL LETTER U DIAERESIS HACEK;;;01DA;
+01DA;LATIN SMALL LETTER U WITH DIAERESIS AND CARON;Ll;0;L;00FC 030C;;;;N;LATIN SMALL LETTER U DIAERESIS HACEK;;01D9;;01D9
+01DB;LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE;Lu;0;L;00DC 0300;;;;N;LATIN CAPITAL LETTER U DIAERESIS GRAVE;;;01DC;
+01DC;LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE;Ll;0;L;00FC 0300;;;;N;LATIN SMALL LETTER U DIAERESIS GRAVE;;01DB;;01DB
+01DD;LATIN SMALL LETTER TURNED E;Ll;0;L;;;;;N;;;018E;;018E
+01DE;LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON;Lu;0;L;00C4 0304;;;;N;LATIN CAPITAL LETTER A DIAERESIS MACRON;;;01DF;
+01DF;LATIN SMALL LETTER A WITH DIAERESIS AND MACRON;Ll;0;L;00E4 0304;;;;N;LATIN SMALL LETTER A DIAERESIS MACRON;;01DE;;01DE
+01E0;LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON;Lu;0;L;0226 0304;;;;N;LATIN CAPITAL LETTER A DOT MACRON;;;01E1;
+01E1;LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON;Ll;0;L;0227 0304;;;;N;LATIN SMALL LETTER A DOT MACRON;;01E0;;01E0
+01E2;LATIN CAPITAL LETTER AE WITH MACRON;Lu;0;L;00C6 0304;;;;N;LATIN CAPITAL LETTER A E MACRON;ash *;;01E3;
+01E3;LATIN SMALL LETTER AE WITH MACRON;Ll;0;L;00E6 0304;;;;N;LATIN SMALL LETTER A E MACRON;ash *;01E2;;01E2
+01E4;LATIN CAPITAL LETTER G WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G BAR;;;01E5;
+01E5;LATIN SMALL LETTER G WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER G BAR;;01E4;;01E4
+01E6;LATIN CAPITAL LETTER G WITH CARON;Lu;0;L;0047 030C;;;;N;LATIN CAPITAL LETTER G HACEK;;;01E7;
+01E7;LATIN SMALL LETTER G WITH CARON;Ll;0;L;0067 030C;;;;N;LATIN SMALL LETTER G HACEK;;01E6;;01E6
+01E8;LATIN CAPITAL LETTER K WITH CARON;Lu;0;L;004B 030C;;;;N;LATIN CAPITAL LETTER K HACEK;;;01E9;
+01E9;LATIN SMALL LETTER K WITH CARON;Ll;0;L;006B 030C;;;;N;LATIN SMALL LETTER K HACEK;;01E8;;01E8
+01EA;LATIN CAPITAL LETTER O WITH OGONEK;Lu;0;L;004F 0328;;;;N;LATIN CAPITAL LETTER O OGONEK;;;01EB;
+01EB;LATIN SMALL LETTER O WITH OGONEK;Ll;0;L;006F 0328;;;;N;LATIN SMALL LETTER O OGONEK;;01EA;;01EA
+01EC;LATIN CAPITAL LETTER O WITH OGONEK AND MACRON;Lu;0;L;01EA 0304;;;;N;LATIN CAPITAL LETTER O OGONEK MACRON;;;01ED;
+01ED;LATIN SMALL LETTER O WITH OGONEK AND MACRON;Ll;0;L;01EB 0304;;;;N;LATIN SMALL LETTER O OGONEK MACRON;;01EC;;01EC
+01EE;LATIN CAPITAL LETTER EZH WITH CARON;Lu;0;L;01B7 030C;;;;N;LATIN CAPITAL LETTER YOGH HACEK;;;01EF;
+01EF;LATIN SMALL LETTER EZH WITH CARON;Ll;0;L;0292 030C;;;;N;LATIN SMALL LETTER YOGH HACEK;;01EE;;01EE
+01F0;LATIN SMALL LETTER J WITH CARON;Ll;0;L;006A 030C;;;;N;LATIN SMALL LETTER J HACEK;;;;
+01F1;LATIN CAPITAL LETTER DZ;Lu;0;L;<compat> 0044 005A;;;;N;;;;01F3;01F2
+01F2;LATIN CAPITAL LETTER D WITH SMALL LETTER Z;Lt;0;L;<compat> 0044 007A;;;;N;;;01F1;01F3;
+01F3;LATIN SMALL LETTER DZ;Ll;0;L;<compat> 0064 007A;;;;N;;;01F1;;01F2
+01F4;LATIN CAPITAL LETTER G WITH ACUTE;Lu;0;L;0047 0301;;;;N;;;;01F5;
+01F5;LATIN SMALL LETTER G WITH ACUTE;Ll;0;L;0067 0301;;;;N;;;01F4;;01F4
+01F6;LATIN CAPITAL LETTER HWAIR;Lu;0;L;;;;;N;;;;0195;
+01F7;LATIN CAPITAL LETTER WYNN;Lu;0;L;;;;;N;;;;01BF;
+01F8;LATIN CAPITAL LETTER N WITH GRAVE;Lu;0;L;004E 0300;;;;N;;;;01F9;
+01F9;LATIN SMALL LETTER N WITH GRAVE;Ll;0;L;006E 0300;;;;N;;;01F8;;01F8
+01FA;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE;Lu;0;L;00C5 0301;;;;N;;;;01FB;
+01FB;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE;Ll;0;L;00E5 0301;;;;N;;;01FA;;01FA
+01FC;LATIN CAPITAL LETTER AE WITH ACUTE;Lu;0;L;00C6 0301;;;;N;;ash *;;01FD;
+01FD;LATIN SMALL LETTER AE WITH ACUTE;Ll;0;L;00E6 0301;;;;N;;ash *;01FC;;01FC
+01FE;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE;Lu;0;L;00D8 0301;;;;N;;;;01FF;
+01FF;LATIN SMALL LETTER O WITH STROKE AND ACUTE;Ll;0;L;00F8 0301;;;;N;;;01FE;;01FE
+0200;LATIN CAPITAL LETTER A WITH DOUBLE GRAVE;Lu;0;L;0041 030F;;;;N;;;;0201;
+0201;LATIN SMALL LETTER A WITH DOUBLE GRAVE;Ll;0;L;0061 030F;;;;N;;;0200;;0200
+0202;LATIN CAPITAL LETTER A WITH INVERTED BREVE;Lu;0;L;0041 0311;;;;N;;;;0203;
+0203;LATIN SMALL LETTER A WITH INVERTED BREVE;Ll;0;L;0061 0311;;;;N;;;0202;;0202
+0204;LATIN CAPITAL LETTER E WITH DOUBLE GRAVE;Lu;0;L;0045 030F;;;;N;;;;0205;
+0205;LATIN SMALL LETTER E WITH DOUBLE GRAVE;Ll;0;L;0065 030F;;;;N;;;0204;;0204
+0206;LATIN CAPITAL LETTER E WITH INVERTED BREVE;Lu;0;L;0045 0311;;;;N;;;;0207;
+0207;LATIN SMALL LETTER E WITH INVERTED BREVE;Ll;0;L;0065 0311;;;;N;;;0206;;0206
+0208;LATIN CAPITAL LETTER I WITH DOUBLE GRAVE;Lu;0;L;0049 030F;;;;N;;;;0209;
+0209;LATIN SMALL LETTER I WITH DOUBLE GRAVE;Ll;0;L;0069 030F;;;;N;;;0208;;0208
+020A;LATIN CAPITAL LETTER I WITH INVERTED BREVE;Lu;0;L;0049 0311;;;;N;;;;020B;
+020B;LATIN SMALL LETTER I WITH INVERTED BREVE;Ll;0;L;0069 0311;;;;N;;;020A;;020A
+020C;LATIN CAPITAL LETTER O WITH DOUBLE GRAVE;Lu;0;L;004F 030F;;;;N;;;;020D;
+020D;LATIN SMALL LETTER O WITH DOUBLE GRAVE;Ll;0;L;006F 030F;;;;N;;;020C;;020C
+020E;LATIN CAPITAL LETTER O WITH INVERTED BREVE;Lu;0;L;004F 0311;;;;N;;;;020F;
+020F;LATIN SMALL LETTER O WITH INVERTED BREVE;Ll;0;L;006F 0311;;;;N;;;020E;;020E
+0210;LATIN CAPITAL LETTER R WITH DOUBLE GRAVE;Lu;0;L;0052 030F;;;;N;;;;0211;
+0211;LATIN SMALL LETTER R WITH DOUBLE GRAVE;Ll;0;L;0072 030F;;;;N;;;0210;;0210
+0212;LATIN CAPITAL LETTER R WITH INVERTED BREVE;Lu;0;L;0052 0311;;;;N;;;;0213;
+0213;LATIN SMALL LETTER R WITH INVERTED BREVE;Ll;0;L;0072 0311;;;;N;;;0212;;0212
+0214;LATIN CAPITAL LETTER U WITH DOUBLE GRAVE;Lu;0;L;0055 030F;;;;N;;;;0215;
+0215;LATIN SMALL LETTER U WITH DOUBLE GRAVE;Ll;0;L;0075 030F;;;;N;;;0214;;0214
+0216;LATIN CAPITAL LETTER U WITH INVERTED BREVE;Lu;0;L;0055 0311;;;;N;;;;0217;
+0217;LATIN SMALL LETTER U WITH INVERTED BREVE;Ll;0;L;0075 0311;;;;N;;;0216;;0216
+0218;LATIN CAPITAL LETTER S WITH COMMA BELOW;Lu;0;L;0053 0326;;;;N;;*;;0219;
+0219;LATIN SMALL LETTER S WITH COMMA BELOW;Ll;0;L;0073 0326;;;;N;;*;0218;;0218
+021A;LATIN CAPITAL LETTER T WITH COMMA BELOW;Lu;0;L;0054 0326;;;;N;;*;;021B;
+021B;LATIN SMALL LETTER T WITH COMMA BELOW;Ll;0;L;0074 0326;;;;N;;*;021A;;021A
+021C;LATIN CAPITAL LETTER YOGH;Lu;0;L;;;;;N;;;;021D;
+021D;LATIN SMALL LETTER YOGH;Ll;0;L;;;;;N;;;021C;;021C
+021E;LATIN CAPITAL LETTER H WITH CARON;Lu;0;L;0048 030C;;;;N;;;;021F;
+021F;LATIN SMALL LETTER H WITH CARON;Ll;0;L;0068 030C;;;;N;;;021E;;021E
+0222;LATIN CAPITAL LETTER OU;Lu;0;L;;;;;N;;;;0223;
+0223;LATIN SMALL LETTER OU;Ll;0;L;;;;;N;;;0222;;0222
+0224;LATIN CAPITAL LETTER Z WITH HOOK;Lu;0;L;;;;;N;;;;0225;
+0225;LATIN SMALL LETTER Z WITH HOOK;Ll;0;L;;;;;N;;;0224;;0224
+0226;LATIN CAPITAL LETTER A WITH DOT ABOVE;Lu;0;L;0041 0307;;;;N;;;;0227;
+0227;LATIN SMALL LETTER A WITH DOT ABOVE;Ll;0;L;0061 0307;;;;N;;;0226;;0226
+0228;LATIN CAPITAL LETTER E WITH CEDILLA;Lu;0;L;0045 0327;;;;N;;;;0229;
+0229;LATIN SMALL LETTER E WITH CEDILLA;Ll;0;L;0065 0327;;;;N;;;0228;;0228
+022A;LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON;Lu;0;L;00D6 0304;;;;N;;;;022B;
+022B;LATIN SMALL LETTER O WITH DIAERESIS AND MACRON;Ll;0;L;00F6 0304;;;;N;;;022A;;022A
+022C;LATIN CAPITAL LETTER O WITH TILDE AND MACRON;Lu;0;L;00D5 0304;;;;N;;;;022D;
+022D;LATIN SMALL LETTER O WITH TILDE AND MACRON;Ll;0;L;00F5 0304;;;;N;;;022C;;022C
+022E;LATIN CAPITAL LETTER O WITH DOT ABOVE;Lu;0;L;004F 0307;;;;N;;;;022F;
+022F;LATIN SMALL LETTER O WITH DOT ABOVE;Ll;0;L;006F 0307;;;;N;;;022E;;022E
+0230;LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON;Lu;0;L;022E 0304;;;;N;;;;0231;
+0231;LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON;Ll;0;L;022F 0304;;;;N;;;0230;;0230
+0232;LATIN CAPITAL LETTER Y WITH MACRON;Lu;0;L;0059 0304;;;;N;;;;0233;
+0233;LATIN SMALL LETTER Y WITH MACRON;Ll;0;L;0079 0304;;;;N;;;0232;;0232
+0250;LATIN SMALL LETTER TURNED A;Ll;0;L;;;;;N;;;;;
+0251;LATIN SMALL LETTER ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT A;;;;
+0252;LATIN SMALL LETTER TURNED ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED SCRIPT A;;;;
+0253;LATIN SMALL LETTER B WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER B HOOK;;0181;;0181
+0254;LATIN SMALL LETTER OPEN O;Ll;0;L;;;;;N;;;0186;;0186
+0255;LATIN SMALL LETTER C WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER C CURL;;;;
+0256;LATIN SMALL LETTER D WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER D RETROFLEX HOOK;;0189;;0189
+0257;LATIN SMALL LETTER D WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER D HOOK;;018A;;018A
+0258;LATIN SMALL LETTER REVERSED E;Ll;0;L;;;;;N;;;;;
+0259;LATIN SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;018F;;018F
+025A;LATIN SMALL LETTER SCHWA WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCHWA HOOK;;;;
+025B;LATIN SMALL LETTER OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER EPSILON;;0190;;0190
+025C;LATIN SMALL LETTER REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON;;;;
+025D;LATIN SMALL LETTER REVERSED OPEN E WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON HOOK;;;;
+025E;LATIN SMALL LETTER CLOSED REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED REVERSED EPSILON;;;;
+025F;LATIN SMALL LETTER DOTLESS J WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR;;;;
+0260;LATIN SMALL LETTER G WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER G HOOK;;0193;;0193
+0261;LATIN SMALL LETTER SCRIPT G;Ll;0;L;;;;;N;;;;;
+0262;LATIN LETTER SMALL CAPITAL G;Ll;0;L;;;;;N;;;;;
+0263;LATIN SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0194;;0194
+0264;LATIN SMALL LETTER RAMS HORN;Ll;0;L;;;;;N;LATIN SMALL LETTER BABY GAMMA;;;;
+0265;LATIN SMALL LETTER TURNED H;Ll;0;L;;;;;N;;;;;
+0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;;;
+0267;LATIN SMALL LETTER HENG WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER HENG HOOK;;;;
+0268;LATIN SMALL LETTER I WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED I;;0197;;0197
+0269;LATIN SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0196;;0196
+026A;LATIN LETTER SMALL CAPITAL I;Ll;0;L;;;;;N;;;;;
+026B;LATIN SMALL LETTER L WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+026C;LATIN SMALL LETTER L WITH BELT;Ll;0;L;;;;;N;LATIN SMALL LETTER L BELT;;;;
+026D;LATIN SMALL LETTER L WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER L RETROFLEX HOOK;;;;
+026E;LATIN SMALL LETTER LEZH;Ll;0;L;;;;;N;LATIN SMALL LETTER L YOGH;;;;
+026F;LATIN SMALL LETTER TURNED M;Ll;0;L;;;;;N;;;019C;;019C
+0270;LATIN SMALL LETTER TURNED M WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+0271;LATIN SMALL LETTER M WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER M HOOK;;;;
+0272;LATIN SMALL LETTER N WITH LEFT HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N HOOK;;019D;;019D
+0273;LATIN SMALL LETTER N WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N RETROFLEX HOOK;;;;
+0274;LATIN LETTER SMALL CAPITAL N;Ll;0;L;;;;;N;;;;;
+0275;LATIN SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;019F;;019F
+0276;LATIN LETTER SMALL CAPITAL OE;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL O E;;;;
+0277;LATIN SMALL LETTER CLOSED OMEGA;Ll;0;L;;;;;N;;;;;
+0278;LATIN SMALL LETTER PHI;Ll;0;L;;;;;N;;;;;
+0279;LATIN SMALL LETTER TURNED R;Ll;0;L;;;;;N;;;;;
+027A;LATIN SMALL LETTER TURNED R WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+027B;LATIN SMALL LETTER TURNED R WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED R HOOK;;;;
+027C;LATIN SMALL LETTER R WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+027D;LATIN SMALL LETTER R WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER R HOOK;;;;
+027E;LATIN SMALL LETTER R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER FISHHOOK R;;;;
+027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;;
+0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;*;01A6;;01A6
+0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;;
+0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;;;
+0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9
+0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;;
+0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;;
+0286;LATIN SMALL LETTER ESH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER ESH CURL;;;;
+0287;LATIN SMALL LETTER TURNED T;Ll;0;L;;;;;N;;;;;
+0288;LATIN SMALL LETTER T WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T RETROFLEX HOOK;;01AE;;01AE
+0289;LATIN SMALL LETTER U BAR;Ll;0;L;;;;;N;;;;;
+028A;LATIN SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;01B1;;01B1
+028B;LATIN SMALL LETTER V WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT V;;01B2;;01B2
+028C;LATIN SMALL LETTER TURNED V;Ll;0;L;;;;;N;;;;;
+028D;LATIN SMALL LETTER TURNED W;Ll;0;L;;;;;N;;;;;
+028E;LATIN SMALL LETTER TURNED Y;Ll;0;L;;;;;N;;;;;
+028F;LATIN LETTER SMALL CAPITAL Y;Ll;0;L;;;;;N;;;;;
+0290;LATIN SMALL LETTER Z WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Z RETROFLEX HOOK;;;;
+0291;LATIN SMALL LETTER Z WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER Z CURL;;;;
+0292;LATIN SMALL LETTER EZH;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH;;01B7;;01B7
+0293;LATIN SMALL LETTER EZH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH CURL;;;;
+0294;LATIN LETTER GLOTTAL STOP;Ll;0;L;;;;;N;;;;;
+0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;;
+0296;LATIN LETTER INVERTED GLOTTAL STOP;Ll;0;L;;;;;N;;;;;
+0297;LATIN LETTER STRETCHED C;Ll;0;L;;;;;N;;;;;
+0298;LATIN LETTER BILABIAL CLICK;Ll;0;L;;;;;N;LATIN LETTER BULLSEYE;;;;
+0299;LATIN LETTER SMALL CAPITAL B;Ll;0;L;;;;;N;;;;;
+029A;LATIN SMALL LETTER CLOSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED EPSILON;;;;
+029B;LATIN LETTER SMALL CAPITAL G WITH HOOK;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL G HOOK;;;;
+029C;LATIN LETTER SMALL CAPITAL H;Ll;0;L;;;;;N;;;;;
+029D;LATIN SMALL LETTER J WITH CROSSED-TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER CROSSED-TAIL J;;;;
+029E;LATIN SMALL LETTER TURNED K;Ll;0;L;;;;;N;;;;;
+029F;LATIN LETTER SMALL CAPITAL L;Ll;0;L;;;;;N;;;;;
+02A0;LATIN SMALL LETTER Q WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Q HOOK;;;;
+02A1;LATIN LETTER GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER GLOTTAL STOP BAR;;;;
+02A2;LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP BAR;;;;
+02A3;LATIN SMALL LETTER DZ DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z;;;;
+02A4;LATIN SMALL LETTER DEZH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D YOGH;;;;
+02A5;LATIN SMALL LETTER DZ DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z CURL;;;;
+02A6;LATIN SMALL LETTER TS DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T S;;;;
+02A7;LATIN SMALL LETTER TESH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T ESH;;;;
+02A8;LATIN SMALL LETTER TC DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER T C CURL;;;;
+02A9;LATIN SMALL LETTER FENG DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AA;LATIN SMALL LETTER LS DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AB;LATIN SMALL LETTER LZ DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AC;LATIN LETTER BILABIAL PERCUSSIVE;Ll;0;L;;;;;N;;;;;
+02AD;LATIN LETTER BIDENTAL PERCUSSIVE;Ll;0;L;;;;;N;;;;;
+02B0;MODIFIER LETTER SMALL H;Lm;0;L;<super> 0068;;;;N;;;;;
+02B1;MODIFIER LETTER SMALL H WITH HOOK;Lm;0;L;<super> 0266;;;;N;MODIFIER LETTER SMALL H HOOK;;;;
+02B2;MODIFIER LETTER SMALL J;Lm;0;L;<super> 006A;;;;N;;;;;
+02B3;MODIFIER LETTER SMALL R;Lm;0;L;<super> 0072;;;;N;;;;;
+02B4;MODIFIER LETTER SMALL TURNED R;Lm;0;L;<super> 0279;;;;N;;;;;
+02B5;MODIFIER LETTER SMALL TURNED R WITH HOOK;Lm;0;L;<super> 027B;;;;N;MODIFIER LETTER SMALL TURNED R HOOK;;;;
+02B6;MODIFIER LETTER SMALL CAPITAL INVERTED R;Lm;0;L;<super> 0281;;;;N;;;;;
+02B7;MODIFIER LETTER SMALL W;Lm;0;L;<super> 0077;;;;N;;;;;
+02B8;MODIFIER LETTER SMALL Y;Lm;0;L;<super> 0079;;;;N;;;;;
+02B9;MODIFIER LETTER PRIME;Sk;0;ON;;;;;N;;;;;
+02BA;MODIFIER LETTER DOUBLE PRIME;Sk;0;ON;;;;;N;;;;;
+02BB;MODIFIER LETTER TURNED COMMA;Lm;0;L;;;;;N;;;;;
+02BC;MODIFIER LETTER APOSTROPHE;Lm;0;L;;;;;N;;;;;
+02BD;MODIFIER LETTER REVERSED COMMA;Lm;0;L;;;;;N;;;;;
+02BE;MODIFIER LETTER RIGHT HALF RING;Lm;0;L;;;;;N;;;;;
+02BF;MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;;
+02C0;MODIFIER LETTER GLOTTAL STOP;Lm;0;L;;;;;N;;;;;
+02C1;MODIFIER LETTER REVERSED GLOTTAL STOP;Lm;0;L;;;;;N;;;;;
+02C2;MODIFIER LETTER LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C3;MODIFIER LETTER RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C4;MODIFIER LETTER UP ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C5;MODIFIER LETTER DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C6;MODIFIER LETTER CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER CIRCUMFLEX;;;;
+02C7;CARON;Sk;0;ON;;;;;N;MODIFIER LETTER HACEK;Mandarin Chinese third tone;;;
+02C8;MODIFIER LETTER VERTICAL LINE;Sk;0;ON;;;;;N;;;;;
+02C9;MODIFIER LETTER MACRON;Sk;0;ON;;;;;N;;Mandarin Chinese first tone;;;
+02CA;MODIFIER LETTER ACUTE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER ACUTE;Mandarin Chinese second tone;;;
+02CB;MODIFIER LETTER GRAVE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER GRAVE;Mandarin Chinese fourth tone;;;
+02CC;MODIFIER LETTER LOW VERTICAL LINE;Sk;0;ON;;;;;N;;;;;
+02CD;MODIFIER LETTER LOW MACRON;Sk;0;ON;;;;;N;;;;;
+02CE;MODIFIER LETTER LOW GRAVE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER LOW GRAVE;;;;
+02CF;MODIFIER LETTER LOW ACUTE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER LOW ACUTE;;;;
+02D0;MODIFIER LETTER TRIANGULAR COLON;Lm;0;L;;;;;N;;;;;
+02D1;MODIFIER LETTER HALF TRIANGULAR COLON;Lm;0;L;;;;;N;;;;;
+02D2;MODIFIER LETTER CENTRED RIGHT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED RIGHT HALF RING;;;;
+02D3;MODIFIER LETTER CENTRED LEFT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED LEFT HALF RING;;;;
+02D4;MODIFIER LETTER UP TACK;Sk;0;ON;;;;;N;;;;;
+02D5;MODIFIER LETTER DOWN TACK;Sk;0;ON;;;;;N;;;;;
+02D6;MODIFIER LETTER PLUS SIGN;Sk;0;ON;;;;;N;;;;;
+02D7;MODIFIER LETTER MINUS SIGN;Sk;0;ON;;;;;N;;;;;
+02D8;BREVE;Sk;0;ON;<compat> 0020 0306;;;;N;SPACING BREVE;;;;
+02D9;DOT ABOVE;Sk;0;ON;<compat> 0020 0307;;;;N;SPACING DOT ABOVE;Mandarin Chinese light tone;;;
+02DA;RING ABOVE;Sk;0;ON;<compat> 0020 030A;;;;N;SPACING RING ABOVE;;;;
+02DB;OGONEK;Sk;0;ON;<compat> 0020 0328;;;;N;SPACING OGONEK;;;;
+02DC;SMALL TILDE;Sk;0;ON;<compat> 0020 0303;;;;N;SPACING TILDE;;;;
+02DD;DOUBLE ACUTE ACCENT;Sk;0;ON;<compat> 0020 030B;;;;N;SPACING DOUBLE ACUTE;;;;
+02DE;MODIFIER LETTER RHOTIC HOOK;Sk;0;ON;;;;;N;;;;;
+02DF;MODIFIER LETTER CROSS ACCENT;Sk;0;ON;;;;;N;;;;;
+02E0;MODIFIER LETTER SMALL GAMMA;Lm;0;L;<super> 0263;;;;N;;;;;
+02E1;MODIFIER LETTER SMALL L;Lm;0;L;<super> 006C;;;;N;;;;;
+02E2;MODIFIER LETTER SMALL S;Lm;0;L;<super> 0073;;;;N;;;;;
+02E3;MODIFIER LETTER SMALL X;Lm;0;L;<super> 0078;;;;N;;;;;
+02E4;MODIFIER LETTER SMALL REVERSED GLOTTAL STOP;Lm;0;L;<super> 0295;;;;N;;;;;
+02E5;MODIFIER LETTER EXTRA-HIGH TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E6;MODIFIER LETTER HIGH TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E7;MODIFIER LETTER MID TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E8;MODIFIER LETTER LOW TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E9;MODIFIER LETTER EXTRA-LOW TONE BAR;Sk;0;ON;;;;;N;;;;;
+02EA;MODIFIER LETTER YIN DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;;
+02EB;MODIFIER LETTER YANG DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;;
+02EC;MODIFIER LETTER VOICING;Sk;0;ON;;;;;N;;;;;
+02ED;MODIFIER LETTER UNASPIRATED;Sk;0;ON;;;;;N;;;;;
+02EE;MODIFIER LETTER DOUBLE APOSTROPHE;Lm;0;L;;;;;N;;;;;
+0300;COMBINING GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING GRAVE;Varia;;;
+0301;COMBINING ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING ACUTE;Oxia;;;
+0302;COMBINING CIRCUMFLEX ACCENT;Mn;230;NSM;;;;;N;NON-SPACING CIRCUMFLEX;;;;
+0303;COMBINING TILDE;Mn;230;NSM;;;;;N;NON-SPACING TILDE;;;;
+0304;COMBINING MACRON;Mn;230;NSM;;;;;N;NON-SPACING MACRON;;;;
+0305;COMBINING OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING OVERSCORE;;;;
+0306;COMBINING BREVE;Mn;230;NSM;;;;;N;NON-SPACING BREVE;Vrachy;;;
+0307;COMBINING DOT ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOT ABOVE;;;;
+0308;COMBINING DIAERESIS;Mn;230;NSM;;;;;N;NON-SPACING DIAERESIS;Dialytika;;;
+0309;COMBINING HOOK ABOVE;Mn;230;NSM;;;;;N;NON-SPACING HOOK ABOVE;;;;
+030A;COMBINING RING ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RING ABOVE;;;;
+030B;COMBINING DOUBLE ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE ACUTE;;;;
+030C;COMBINING CARON;Mn;230;NSM;;;;;N;NON-SPACING HACEK;;;;
+030D;COMBINING VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL LINE ABOVE;Tonos;;;
+030E;COMBINING DOUBLE VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE VERTICAL LINE ABOVE;;;;
+030F;COMBINING DOUBLE GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE GRAVE;;;;
+0310;COMBINING CANDRABINDU;Mn;230;NSM;;;;;N;NON-SPACING CANDRABINDU;;;;
+0311;COMBINING INVERTED BREVE;Mn;230;NSM;;;;;N;NON-SPACING INVERTED BREVE;;;;
+0312;COMBINING TURNED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING TURNED COMMA ABOVE;;;;
+0313;COMBINING COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING COMMA ABOVE;Psili;;;
+0314;COMBINING REVERSED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING REVERSED COMMA ABOVE;Dasia;;;
+0315;COMBINING COMMA ABOVE RIGHT;Mn;232;NSM;;;;;N;NON-SPACING COMMA ABOVE RIGHT;;;;
+0316;COMBINING GRAVE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING GRAVE BELOW;;;;
+0317;COMBINING ACUTE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING ACUTE BELOW;;;;
+0318;COMBINING LEFT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT TACK BELOW;;;;
+0319;COMBINING RIGHT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT TACK BELOW;;;;
+031A;COMBINING LEFT ANGLE ABOVE;Mn;232;NSM;;;;;N;NON-SPACING LEFT ANGLE ABOVE;;;;
+031B;COMBINING HORN;Mn;216;NSM;;;;;N;NON-SPACING HORN;;;;
+031C;COMBINING LEFT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT HALF RING BELOW;;;;
+031D;COMBINING UP TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING UP TACK BELOW;;;;
+031E;COMBINING DOWN TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOWN TACK BELOW;;;;
+031F;COMBINING PLUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING PLUS SIGN BELOW;;;;
+0320;COMBINING MINUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING MINUS SIGN BELOW;;;;
+0321;COMBINING PALATALIZED HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING PALATALIZED HOOK BELOW;;;;
+0322;COMBINING RETROFLEX HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING RETROFLEX HOOK BELOW;;;;
+0323;COMBINING DOT BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOT BELOW;;;;
+0324;COMBINING DIAERESIS BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE DOT BELOW;;;;
+0325;COMBINING RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RING BELOW;;;;
+0326;COMBINING COMMA BELOW;Mn;220;NSM;;;;;N;NON-SPACING COMMA BELOW;;;;
+0327;COMBINING CEDILLA;Mn;202;NSM;;;;;N;NON-SPACING CEDILLA;;;;
+0328;COMBINING OGONEK;Mn;202;NSM;;;;;N;NON-SPACING OGONEK;;;;
+0329;COMBINING VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;NON-SPACING VERTICAL LINE BELOW;;;;
+032A;COMBINING BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BRIDGE BELOW;;;;
+032B;COMBINING INVERTED DOUBLE ARCH BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED DOUBLE ARCH BELOW;;;;
+032C;COMBINING CARON BELOW;Mn;220;NSM;;;;;N;NON-SPACING HACEK BELOW;;;;
+032D;COMBINING CIRCUMFLEX ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING CIRCUMFLEX BELOW;;;;
+032E;COMBINING BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BREVE BELOW;;;;
+032F;COMBINING INVERTED BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BREVE BELOW;;;;
+0330;COMBINING TILDE BELOW;Mn;220;NSM;;;;;N;NON-SPACING TILDE BELOW;;;;
+0331;COMBINING MACRON BELOW;Mn;220;NSM;;;;;N;NON-SPACING MACRON BELOW;;;;
+0332;COMBINING LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING UNDERSCORE;;;;
+0333;COMBINING DOUBLE LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE UNDERSCORE;;;;
+0334;COMBINING TILDE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING TILDE OVERLAY;;;;
+0335;COMBINING SHORT STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT BAR OVERLAY;;;;
+0336;COMBINING LONG STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG BAR OVERLAY;;;;
+0337;COMBINING SHORT SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT SLASH OVERLAY;;;;
+0338;COMBINING LONG SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG SLASH OVERLAY;;;;
+0339;COMBINING RIGHT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT HALF RING BELOW;;;;
+033A;COMBINING INVERTED BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BRIDGE BELOW;;;;
+033B;COMBINING SQUARE BELOW;Mn;220;NSM;;;;;N;NON-SPACING SQUARE BELOW;;;;
+033C;COMBINING SEAGULL BELOW;Mn;220;NSM;;;;;N;NON-SPACING SEAGULL BELOW;;;;
+033D;COMBINING X ABOVE;Mn;230;NSM;;;;;N;NON-SPACING X ABOVE;;;;
+033E;COMBINING VERTICAL TILDE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL TILDE;;;;
+033F;COMBINING DOUBLE OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE OVERSCORE;;;;
+0340;COMBINING GRAVE TONE MARK;Mn;230;NSM;0300;;;;N;NON-SPACING GRAVE TONE MARK;Vietnamese;;;
+0341;COMBINING ACUTE TONE MARK;Mn;230;NSM;0301;;;;N;NON-SPACING ACUTE TONE MARK;Vietnamese;;;
+0342;COMBINING GREEK PERISPOMENI;Mn;230;NSM;;;;;N;;;;;
+0343;COMBINING GREEK KORONIS;Mn;230;NSM;0313;;;;N;;;;;
+0344;COMBINING GREEK DIALYTIKA TONOS;Mn;230;NSM;0308 0301;;;;N;GREEK NON-SPACING DIAERESIS TONOS;;;;
+0345;COMBINING GREEK YPOGEGRAMMENI;Mn;240;NSM;;;;;N;GREEK NON-SPACING IOTA BELOW;;0399;;0399
+0346;COMBINING BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;
+0347;COMBINING EQUALS SIGN BELOW;Mn;220;NSM;;;;;N;;;;;
+0348;COMBINING DOUBLE VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;;;;;
+0349;COMBINING LEFT ANGLE BELOW;Mn;220;NSM;;;;;N;;;;;
+034A;COMBINING NOT TILDE ABOVE;Mn;230;NSM;;;;;N;;;;;
+034B;COMBINING HOMOTHETIC ABOVE;Mn;230;NSM;;;;;N;;;;;
+034C;COMBINING ALMOST EQUAL TO ABOVE;Mn;230;NSM;;;;;N;;;;;
+034D;COMBINING LEFT RIGHT ARROW BELOW;Mn;220;NSM;;;;;N;;;;;
+034E;COMBINING UPWARDS ARROW BELOW;Mn;220;NSM;;;;;N;;;;;
+0360;COMBINING DOUBLE TILDE;Mn;234;NSM;;;;;N;;;;;
+0361;COMBINING DOUBLE INVERTED BREVE;Mn;234;NSM;;;;;N;;;;;
+0362;COMBINING DOUBLE RIGHTWARDS ARROW BELOW;Mn;233;NSM;;;;;N;;;;;
+0374;GREEK NUMERAL SIGN;Sk;0;ON;02B9;;;;N;GREEK UPPER NUMERAL SIGN;Dexia keraia;;;
+0375;GREEK LOWER NUMERAL SIGN;Sk;0;ON;;;;;N;;Aristeri keraia;;;
+037A;GREEK YPOGEGRAMMENI;Lm;0;L;<compat> 0020 0345;;;;N;GREEK SPACING IOTA BELOW;;;;
+037E;GREEK QUESTION MARK;Po;0;ON;003B;;;;N;;Erotimatiko;;;
+0384;GREEK TONOS;Sk;0;ON;<compat> 0020 0301;;;;N;GREEK SPACING TONOS;;;;
+0385;GREEK DIALYTIKA TONOS;Sk;0;ON;00A8 0301;;;;N;GREEK SPACING DIAERESIS TONOS;;;;
+0386;GREEK CAPITAL LETTER ALPHA WITH TONOS;Lu;0;L;0391 0301;;;;N;GREEK CAPITAL LETTER ALPHA TONOS;;;03AC;
+0387;GREEK ANO TELEIA;Po;0;ON;00B7;;;;N;;;;;
+0388;GREEK CAPITAL LETTER EPSILON WITH TONOS;Lu;0;L;0395 0301;;;;N;GREEK CAPITAL LETTER EPSILON TONOS;;;03AD;
+0389;GREEK CAPITAL LETTER ETA WITH TONOS;Lu;0;L;0397 0301;;;;N;GREEK CAPITAL LETTER ETA TONOS;;;03AE;
+038A;GREEK CAPITAL LETTER IOTA WITH TONOS;Lu;0;L;0399 0301;;;;N;GREEK CAPITAL LETTER IOTA TONOS;;;03AF;
+038C;GREEK CAPITAL LETTER OMICRON WITH TONOS;Lu;0;L;039F 0301;;;;N;GREEK CAPITAL LETTER OMICRON TONOS;;;03CC;
+038E;GREEK CAPITAL LETTER UPSILON WITH TONOS;Lu;0;L;03A5 0301;;;;N;GREEK CAPITAL LETTER UPSILON TONOS;;;03CD;
+038F;GREEK CAPITAL LETTER OMEGA WITH TONOS;Lu;0;L;03A9 0301;;;;N;GREEK CAPITAL LETTER OMEGA TONOS;;;03CE;
+0390;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS;Ll;0;L;03CA 0301;;;;N;GREEK SMALL LETTER IOTA DIAERESIS TONOS;;;;
+0391;GREEK CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;03B1;
+0392;GREEK CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;03B2;
+0393;GREEK CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;03B3;
+0394;GREEK CAPITAL LETTER DELTA;Lu;0;L;;;;;N;;;;03B4;
+0395;GREEK CAPITAL LETTER EPSILON;Lu;0;L;;;;;N;;;;03B5;
+0396;GREEK CAPITAL LETTER ZETA;Lu;0;L;;;;;N;;;;03B6;
+0397;GREEK CAPITAL LETTER ETA;Lu;0;L;;;;;N;;;;03B7;
+0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8;
+0399;GREEK CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;03B9;
+039A;GREEK CAPITAL LETTER KAPPA;Lu;0;L;;;;;N;;;;03BA;
+039B;GREEK CAPITAL LETTER LAMDA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER LAMBDA;;;03BB;
+039C;GREEK CAPITAL LETTER MU;Lu;0;L;;;;;N;;;;03BC;
+039D;GREEK CAPITAL LETTER NU;Lu;0;L;;;;;N;;;;03BD;
+039E;GREEK CAPITAL LETTER XI;Lu;0;L;;;;;N;;;;03BE;
+039F;GREEK CAPITAL LETTER OMICRON;Lu;0;L;;;;;N;;;;03BF;
+03A0;GREEK CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;03C0;
+03A1;GREEK CAPITAL LETTER RHO;Lu;0;L;;;;;N;;;;03C1;
+03A3;GREEK CAPITAL LETTER SIGMA;Lu;0;L;;;;;N;;;;03C3;
+03A4;GREEK CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;03C4;
+03A5;GREEK CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;03C5;
+03A6;GREEK CAPITAL LETTER PHI;Lu;0;L;;;;;N;;;;03C6;
+03A7;GREEK CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;03C7;
+03A8;GREEK CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;03C8;
+03A9;GREEK CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;03C9;
+03AA;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA;Lu;0;L;0399 0308;;;;N;GREEK CAPITAL LETTER IOTA DIAERESIS;;;03CA;
+03AB;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA;Lu;0;L;03A5 0308;;;;N;GREEK CAPITAL LETTER UPSILON DIAERESIS;;;03CB;
+03AC;GREEK SMALL LETTER ALPHA WITH TONOS;Ll;0;L;03B1 0301;;;;N;GREEK SMALL LETTER ALPHA TONOS;;0386;;0386
+03AD;GREEK SMALL LETTER EPSILON WITH TONOS;Ll;0;L;03B5 0301;;;;N;GREEK SMALL LETTER EPSILON TONOS;;0388;;0388
+03AE;GREEK SMALL LETTER ETA WITH TONOS;Ll;0;L;03B7 0301;;;;N;GREEK SMALL LETTER ETA TONOS;;0389;;0389
+03AF;GREEK SMALL LETTER IOTA WITH TONOS;Ll;0;L;03B9 0301;;;;N;GREEK SMALL LETTER IOTA TONOS;;038A;;038A
+03B0;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS;Ll;0;L;03CB 0301;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS TONOS;;;;
+03B1;GREEK SMALL LETTER ALPHA;Ll;0;L;;;;;N;;;0391;;0391
+03B2;GREEK SMALL LETTER BETA;Ll;0;L;;;;;N;;;0392;;0392
+03B3;GREEK SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0393;;0393
+03B4;GREEK SMALL LETTER DELTA;Ll;0;L;;;;;N;;;0394;;0394
+03B5;GREEK SMALL LETTER EPSILON;Ll;0;L;;;;;N;;;0395;;0395
+03B6;GREEK SMALL LETTER ZETA;Ll;0;L;;;;;N;;;0396;;0396
+03B7;GREEK SMALL LETTER ETA;Ll;0;L;;;;;N;;;0397;;0397
+03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398
+03B9;GREEK SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0399;;0399
+03BA;GREEK SMALL LETTER KAPPA;Ll;0;L;;;;;N;;;039A;;039A
+03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B
+03BC;GREEK SMALL LETTER MU;Ll;0;L;;;;;N;;;039C;;039C
+03BD;GREEK SMALL LETTER NU;Ll;0;L;;;;;N;;;039D;;039D
+03BE;GREEK SMALL LETTER XI;Ll;0;L;;;;;N;;;039E;;039E
+03BF;GREEK SMALL LETTER OMICRON;Ll;0;L;;;;;N;;;039F;;039F
+03C0;GREEK SMALL LETTER PI;Ll;0;L;;;;;N;;;03A0;;03A0
+03C1;GREEK SMALL LETTER RHO;Ll;0;L;;;;;N;;;03A1;;03A1
+03C2;GREEK SMALL LETTER FINAL SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3
+03C3;GREEK SMALL LETTER SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3
+03C4;GREEK SMALL LETTER TAU;Ll;0;L;;;;;N;;;03A4;;03A4
+03C5;GREEK SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;03A5;;03A5
+03C6;GREEK SMALL LETTER PHI;Ll;0;L;;;;;N;;;03A6;;03A6
+03C7;GREEK SMALL LETTER CHI;Ll;0;L;;;;;N;;;03A7;;03A7
+03C8;GREEK SMALL LETTER PSI;Ll;0;L;;;;;N;;;03A8;;03A8
+03C9;GREEK SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;03A9;;03A9
+03CA;GREEK SMALL LETTER IOTA WITH DIALYTIKA;Ll;0;L;03B9 0308;;;;N;GREEK SMALL LETTER IOTA DIAERESIS;;03AA;;03AA
+03CB;GREEK SMALL LETTER UPSILON WITH DIALYTIKA;Ll;0;L;03C5 0308;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS;;03AB;;03AB
+03CC;GREEK SMALL LETTER OMICRON WITH TONOS;Ll;0;L;03BF 0301;;;;N;GREEK SMALL LETTER OMICRON TONOS;;038C;;038C
+03CD;GREEK SMALL LETTER UPSILON WITH TONOS;Ll;0;L;03C5 0301;;;;N;GREEK SMALL LETTER UPSILON TONOS;;038E;;038E
+03CE;GREEK SMALL LETTER OMEGA WITH TONOS;Ll;0;L;03C9 0301;;;;N;GREEK SMALL LETTER OMEGA TONOS;;038F;;038F
+03D0;GREEK BETA SYMBOL;Ll;0;L;<compat> 03B2;;;;N;GREEK SMALL LETTER CURLED BETA;;0392;;0392
+03D1;GREEK THETA SYMBOL;Ll;0;L;<compat> 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398
+03D2;GREEK UPSILON WITH HOOK SYMBOL;Lu;0;L;<compat> 03A5;;;;N;GREEK CAPITAL LETTER UPSILON HOOK;;;;
+03D3;GREEK UPSILON WITH ACUTE AND HOOK SYMBOL;Lu;0;L;03D2 0301;;;;N;GREEK CAPITAL LETTER UPSILON HOOK TONOS;;;;
+03D4;GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL;Lu;0;L;03D2 0308;;;;N;GREEK CAPITAL LETTER UPSILON HOOK DIAERESIS;;;;
+03D5;GREEK PHI SYMBOL;Ll;0;L;<compat> 03C6;;;;N;GREEK SMALL LETTER SCRIPT PHI;;03A6;;03A6
+03D6;GREEK PI SYMBOL;Ll;0;L;<compat> 03C0;;;;N;GREEK SMALL LETTER OMEGA PI;;03A0;;03A0
+03D7;GREEK KAI SYMBOL;Ll;0;L;;;;;N;;;;;
+03DA;GREEK LETTER STIGMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER STIGMA;;;03DB;
+03DB;GREEK SMALL LETTER STIGMA;Ll;0;L;;;;;N;;;03DA;;03DA
+03DC;GREEK LETTER DIGAMMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DIGAMMA;;;03DD;
+03DD;GREEK SMALL LETTER DIGAMMA;Ll;0;L;;;;;N;;;03DC;;03DC
+03DE;GREEK LETTER KOPPA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KOPPA;;;03DF;
+03DF;GREEK SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;03DE;;03DE
+03E0;GREEK LETTER SAMPI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SAMPI;;;03E1;
+03E1;GREEK SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;03E0;;03E0
+03E2;COPTIC CAPITAL LETTER SHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHEI;;;03E3;
+03E3;COPTIC SMALL LETTER SHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER SHEI;;03E2;;03E2
+03E4;COPTIC CAPITAL LETTER FEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER FEI;;;03E5;
+03E5;COPTIC SMALL LETTER FEI;Ll;0;L;;;;;N;GREEK SMALL LETTER FEI;;03E4;;03E4
+03E6;COPTIC CAPITAL LETTER KHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KHEI;;;03E7;
+03E7;COPTIC SMALL LETTER KHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER KHEI;;03E6;;03E6
+03E8;COPTIC CAPITAL LETTER HORI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER HORI;;;03E9;
+03E9;COPTIC SMALL LETTER HORI;Ll;0;L;;;;;N;GREEK SMALL LETTER HORI;;03E8;;03E8
+03EA;COPTIC CAPITAL LETTER GANGIA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER GANGIA;;;03EB;
+03EB;COPTIC SMALL LETTER GANGIA;Ll;0;L;;;;;N;GREEK SMALL LETTER GANGIA;;03EA;;03EA
+03EC;COPTIC CAPITAL LETTER SHIMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHIMA;;;03ED;
+03ED;COPTIC SMALL LETTER SHIMA;Ll;0;L;;;;;N;GREEK SMALL LETTER SHIMA;;03EC;;03EC
+03EE;COPTIC CAPITAL LETTER DEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DEI;;;03EF;
+03EF;COPTIC SMALL LETTER DEI;Ll;0;L;;;;;N;GREEK SMALL LETTER DEI;;03EE;;03EE
+03F0;GREEK KAPPA SYMBOL;Ll;0;L;<compat> 03BA;;;;N;GREEK SMALL LETTER SCRIPT KAPPA;;039A;;039A
+03F1;GREEK RHO SYMBOL;Ll;0;L;<compat> 03C1;;;;N;GREEK SMALL LETTER TAILED RHO;;03A1;;03A1
+03F2;GREEK LUNATE SIGMA SYMBOL;Ll;0;L;<compat> 03C2;;;;N;GREEK SMALL LETTER LUNATE SIGMA;;03A3;;03A3
+03F3;GREEK LETTER YOT;Ll;0;L;;;;;N;;;;;
+03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L;<compat> 0398;;;;N;;;;03B8;
+03F5;GREEK LUNATE EPSILON SYMBOL;Ll;0;L;<compat> 03B5;;;;N;;;0395;;0395
+0400;CYRILLIC CAPITAL LETTER IE WITH GRAVE;Lu;0;L;0415 0300;;;;N;;;;0450;
+0401;CYRILLIC CAPITAL LETTER IO;Lu;0;L;0415 0308;;;;N;;;;0451;
+0402;CYRILLIC CAPITAL LETTER DJE;Lu;0;L;;;;;N;;Serbocroatian;;0452;
+0403;CYRILLIC CAPITAL LETTER GJE;Lu;0;L;0413 0301;;;;N;;;;0453;
+0404;CYRILLIC CAPITAL LETTER UKRAINIAN IE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER E;;;0454;
+0405;CYRILLIC CAPITAL LETTER DZE;Lu;0;L;;;;;N;;;;0455;
+0406;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER I;;;0456;
+0407;CYRILLIC CAPITAL LETTER YI;Lu;0;L;0406 0308;;;;N;;Ukrainian;;0457;
+0408;CYRILLIC CAPITAL LETTER JE;Lu;0;L;;;;;N;;;;0458;
+0409;CYRILLIC CAPITAL LETTER LJE;Lu;0;L;;;;;N;;;;0459;
+040A;CYRILLIC CAPITAL LETTER NJE;Lu;0;L;;;;;N;;;;045A;
+040B;CYRILLIC CAPITAL LETTER TSHE;Lu;0;L;;;;;N;;Serbocroatian;;045B;
+040C;CYRILLIC CAPITAL LETTER KJE;Lu;0;L;041A 0301;;;;N;;;;045C;
+040D;CYRILLIC CAPITAL LETTER I WITH GRAVE;Lu;0;L;0418 0300;;;;N;;;;045D;
+040E;CYRILLIC CAPITAL LETTER SHORT U;Lu;0;L;0423 0306;;;;N;;Byelorussian;;045E;
+040F;CYRILLIC CAPITAL LETTER DZHE;Lu;0;L;;;;;N;;;;045F;
+0410;CYRILLIC CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0430;
+0411;CYRILLIC CAPITAL LETTER BE;Lu;0;L;;;;;N;;;;0431;
+0412;CYRILLIC CAPITAL LETTER VE;Lu;0;L;;;;;N;;;;0432;
+0413;CYRILLIC CAPITAL LETTER GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE;;;0433;
+0414;CYRILLIC CAPITAL LETTER DE;Lu;0;L;;;;;N;;;;0434;
+0415;CYRILLIC CAPITAL LETTER IE;Lu;0;L;;;;;N;;;;0435;
+0416;CYRILLIC CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;0436;
+0417;CYRILLIC CAPITAL LETTER ZE;Lu;0;L;;;;;N;;;;0437;
+0418;CYRILLIC CAPITAL LETTER I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER II;;;0438;
+0419;CYRILLIC CAPITAL LETTER SHORT I;Lu;0;L;0418 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT II;;;0439;
+041A;CYRILLIC CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;043A;
+041B;CYRILLIC CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;043B;
+041C;CYRILLIC CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;043C;
+041D;CYRILLIC CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;043D;
+041E;CYRILLIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;043E;
+041F;CYRILLIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;043F;
+0420;CYRILLIC CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;0440;
+0421;CYRILLIC CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;0441;
+0422;CYRILLIC CAPITAL LETTER TE;Lu;0;L;;;;;N;;;;0442;
+0423;CYRILLIC CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0443;
+0424;CYRILLIC CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;0444;
+0425;CYRILLIC CAPITAL LETTER HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA;;;0445;
+0426;CYRILLIC CAPITAL LETTER TSE;Lu;0;L;;;;;N;;;;0446;
+0427;CYRILLIC CAPITAL LETTER CHE;Lu;0;L;;;;;N;;;;0447;
+0428;CYRILLIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0448;
+0429;CYRILLIC CAPITAL LETTER SHCHA;Lu;0;L;;;;;N;;;;0449;
+042A;CYRILLIC CAPITAL LETTER HARD SIGN;Lu;0;L;;;;;N;;;;044A;
+042B;CYRILLIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER YERI;;;044B;
+042C;CYRILLIC CAPITAL LETTER SOFT SIGN;Lu;0;L;;;;;N;;;;044C;
+042D;CYRILLIC CAPITAL LETTER E;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED E;;;044D;
+042E;CYRILLIC CAPITAL LETTER YU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IU;;;044E;
+042F;CYRILLIC CAPITAL LETTER YA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IA;;;044F;
+0430;CYRILLIC SMALL LETTER A;Ll;0;L;;;;;N;;;0410;;0410
+0431;CYRILLIC SMALL LETTER BE;Ll;0;L;;;;;N;;;0411;;0411
+0432;CYRILLIC SMALL LETTER VE;Ll;0;L;;;;;N;;;0412;;0412
+0433;CYRILLIC SMALL LETTER GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE;;0413;;0413
+0434;CYRILLIC SMALL LETTER DE;Ll;0;L;;;;;N;;;0414;;0414
+0435;CYRILLIC SMALL LETTER IE;Ll;0;L;;;;;N;;;0415;;0415
+0436;CYRILLIC SMALL LETTER ZHE;Ll;0;L;;;;;N;;;0416;;0416
+0437;CYRILLIC SMALL LETTER ZE;Ll;0;L;;;;;N;;;0417;;0417
+0438;CYRILLIC SMALL LETTER I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER II;;0418;;0418
+0439;CYRILLIC SMALL LETTER SHORT I;Ll;0;L;0438 0306;;;;N;CYRILLIC SMALL LETTER SHORT II;;0419;;0419
+043A;CYRILLIC SMALL LETTER KA;Ll;0;L;;;;;N;;;041A;;041A
+043B;CYRILLIC SMALL LETTER EL;Ll;0;L;;;;;N;;;041B;;041B
+043C;CYRILLIC SMALL LETTER EM;Ll;0;L;;;;;N;;;041C;;041C
+043D;CYRILLIC SMALL LETTER EN;Ll;0;L;;;;;N;;;041D;;041D
+043E;CYRILLIC SMALL LETTER O;Ll;0;L;;;;;N;;;041E;;041E
+043F;CYRILLIC SMALL LETTER PE;Ll;0;L;;;;;N;;;041F;;041F
+0440;CYRILLIC SMALL LETTER ER;Ll;0;L;;;;;N;;;0420;;0420
+0441;CYRILLIC SMALL LETTER ES;Ll;0;L;;;;;N;;;0421;;0421
+0442;CYRILLIC SMALL LETTER TE;Ll;0;L;;;;;N;;;0422;;0422
+0443;CYRILLIC SMALL LETTER U;Ll;0;L;;;;;N;;;0423;;0423
+0444;CYRILLIC SMALL LETTER EF;Ll;0;L;;;;;N;;;0424;;0424
+0445;CYRILLIC SMALL LETTER HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA;;0425;;0425
+0446;CYRILLIC SMALL LETTER TSE;Ll;0;L;;;;;N;;;0426;;0426
+0447;CYRILLIC SMALL LETTER CHE;Ll;0;L;;;;;N;;;0427;;0427
+0448;CYRILLIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;0428;;0428
+0449;CYRILLIC SMALL LETTER SHCHA;Ll;0;L;;;;;N;;;0429;;0429
+044A;CYRILLIC SMALL LETTER HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A
+044B;CYRILLIC SMALL LETTER YERU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER YERI;;042B;;042B
+044C;CYRILLIC SMALL LETTER SOFT SIGN;Ll;0;L;;;;;N;;;042C;;042C
+044D;CYRILLIC SMALL LETTER E;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED E;;042D;;042D
+044E;CYRILLIC SMALL LETTER YU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IU;;042E;;042E
+044F;CYRILLIC SMALL LETTER YA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IA;;042F;;042F
+0450;CYRILLIC SMALL LETTER IE WITH GRAVE;Ll;0;L;0435 0300;;;;N;;;0400;;0400
+0451;CYRILLIC SMALL LETTER IO;Ll;0;L;0435 0308;;;;N;;;0401;;0401
+0452;CYRILLIC SMALL LETTER DJE;Ll;0;L;;;;;N;;Serbocroatian;0402;;0402
+0453;CYRILLIC SMALL LETTER GJE;Ll;0;L;0433 0301;;;;N;;;0403;;0403
+0454;CYRILLIC SMALL LETTER UKRAINIAN IE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER E;;0404;;0404
+0455;CYRILLIC SMALL LETTER DZE;Ll;0;L;;;;;N;;;0405;;0405
+0456;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER I;;0406;;0406
+0457;CYRILLIC SMALL LETTER YI;Ll;0;L;0456 0308;;;;N;;Ukrainian;0407;;0407
+0458;CYRILLIC SMALL LETTER JE;Ll;0;L;;;;;N;;;0408;;0408
+0459;CYRILLIC SMALL LETTER LJE;Ll;0;L;;;;;N;;;0409;;0409
+045A;CYRILLIC SMALL LETTER NJE;Ll;0;L;;;;;N;;;040A;;040A
+045B;CYRILLIC SMALL LETTER TSHE;Ll;0;L;;;;;N;;Serbocroatian;040B;;040B
+045C;CYRILLIC SMALL LETTER KJE;Ll;0;L;043A 0301;;;;N;;;040C;;040C
+045D;CYRILLIC SMALL LETTER I WITH GRAVE;Ll;0;L;0438 0300;;;;N;;;040D;;040D
+045E;CYRILLIC SMALL LETTER SHORT U;Ll;0;L;0443 0306;;;;N;;Byelorussian;040E;;040E
+045F;CYRILLIC SMALL LETTER DZHE;Ll;0;L;;;;;N;;;040F;;040F
+0460;CYRILLIC CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;0461;
+0461;CYRILLIC SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;0460;;0460
+0462;CYRILLIC CAPITAL LETTER YAT;Lu;0;L;;;;;N;;;;0463;
+0463;CYRILLIC SMALL LETTER YAT;Ll;0;L;;;;;N;;;0462;;0462
+0464;CYRILLIC CAPITAL LETTER IOTIFIED E;Lu;0;L;;;;;N;;;;0465;
+0465;CYRILLIC SMALL LETTER IOTIFIED E;Ll;0;L;;;;;N;;;0464;;0464
+0466;CYRILLIC CAPITAL LETTER LITTLE YUS;Lu;0;L;;;;;N;;;;0467;
+0467;CYRILLIC SMALL LETTER LITTLE YUS;Ll;0;L;;;;;N;;;0466;;0466
+0468;CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS;Lu;0;L;;;;;N;;;;0469;
+0469;CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS;Ll;0;L;;;;;N;;;0468;;0468
+046A;CYRILLIC CAPITAL LETTER BIG YUS;Lu;0;L;;;;;N;;;;046B;
+046B;CYRILLIC SMALL LETTER BIG YUS;Ll;0;L;;;;;N;;;046A;;046A
+046C;CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS;Lu;0;L;;;;;N;;;;046D;
+046D;CYRILLIC SMALL LETTER IOTIFIED BIG YUS;Ll;0;L;;;;;N;;;046C;;046C
+046E;CYRILLIC CAPITAL LETTER KSI;Lu;0;L;;;;;N;;;;046F;
+046F;CYRILLIC SMALL LETTER KSI;Ll;0;L;;;;;N;;;046E;;046E
+0470;CYRILLIC CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;0471;
+0471;CYRILLIC SMALL LETTER PSI;Ll;0;L;;;;;N;;;0470;;0470
+0472;CYRILLIC CAPITAL LETTER FITA;Lu;0;L;;;;;N;;;;0473;
+0473;CYRILLIC SMALL LETTER FITA;Ll;0;L;;;;;N;;;0472;;0472
+0474;CYRILLIC CAPITAL LETTER IZHITSA;Lu;0;L;;;;;N;;;;0475;
+0475;CYRILLIC SMALL LETTER IZHITSA;Ll;0;L;;;;;N;;;0474;;0474
+0476;CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Lu;0;L;0474 030F;;;;N;CYRILLIC CAPITAL LETTER IZHITSA DOUBLE GRAVE;;;0477;
+0477;CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Ll;0;L;0475 030F;;;;N;CYRILLIC SMALL LETTER IZHITSA DOUBLE GRAVE;;0476;;0476
+0478;CYRILLIC CAPITAL LETTER UK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER UK DIGRAPH;;;0479;
+0479;CYRILLIC SMALL LETTER UK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER UK DIGRAPH;;0478;;0478
+047A;CYRILLIC CAPITAL LETTER ROUND OMEGA;Lu;0;L;;;;;N;;;;047B;
+047B;CYRILLIC SMALL LETTER ROUND OMEGA;Ll;0;L;;;;;N;;;047A;;047A
+047C;CYRILLIC CAPITAL LETTER OMEGA WITH TITLO;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER OMEGA TITLO;;;047D;
+047D;CYRILLIC SMALL LETTER OMEGA WITH TITLO;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER OMEGA TITLO;;047C;;047C
+047E;CYRILLIC CAPITAL LETTER OT;Lu;0;L;;;;;N;;;;047F;
+047F;CYRILLIC SMALL LETTER OT;Ll;0;L;;;;;N;;;047E;;047E
+0480;CYRILLIC CAPITAL LETTER KOPPA;Lu;0;L;;;;;N;;;;0481;
+0481;CYRILLIC SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;0480;;0480
+0482;CYRILLIC THOUSANDS SIGN;So;0;L;;;;;N;;;;;
+0483;COMBINING CYRILLIC TITLO;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING TITLO;;;;
+0484;COMBINING CYRILLIC PALATALIZATION;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PALATALIZATION;;;;
+0485;COMBINING CYRILLIC DASIA PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING DASIA PNEUMATA;;;;
+0486;COMBINING CYRILLIC PSILI PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PSILI PNEUMATA;;;;
+0488;COMBINING CYRILLIC HUNDRED THOUSANDS SIGN;Me;0;NSM;;;;;N;;;;;
+0489;COMBINING CYRILLIC MILLIONS SIGN;Me;0;NSM;;;;;N;;;;;
+048C;CYRILLIC CAPITAL LETTER SEMISOFT SIGN;Lu;0;L;;;;;N;;;;048D;
+048D;CYRILLIC SMALL LETTER SEMISOFT SIGN;Ll;0;L;;;;;N;;;048C;;048C
+048E;CYRILLIC CAPITAL LETTER ER WITH TICK;Lu;0;L;;;;;N;;;;048F;
+048F;CYRILLIC SMALL LETTER ER WITH TICK;Ll;0;L;;;;;N;;;048E;;048E
+0490;CYRILLIC CAPITAL LETTER GHE WITH UPTURN;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE WITH UPTURN;;;0491;
+0491;CYRILLIC SMALL LETTER GHE WITH UPTURN;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE WITH UPTURN;;0490;;0490
+0492;CYRILLIC CAPITAL LETTER GHE WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE BAR;;;0493;
+0493;CYRILLIC SMALL LETTER GHE WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE BAR;;0492;;0492
+0494;CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE HOOK;;;0495;
+0495;CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE HOOK;;0494;;0494
+0496;CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZHE WITH RIGHT DESCENDER;;;0497;
+0497;CYRILLIC SMALL LETTER ZHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZHE WITH RIGHT DESCENDER;;0496;;0496
+0498;CYRILLIC CAPITAL LETTER ZE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZE CEDILLA;;;0499;
+0499;CYRILLIC SMALL LETTER ZE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZE CEDILLA;;0498;;0498
+049A;CYRILLIC CAPITAL LETTER KA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA WITH RIGHT DESCENDER;;;049B;
+049B;CYRILLIC SMALL LETTER KA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA WITH RIGHT DESCENDER;;049A;;049A
+049C;CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA VERTICAL BAR;;;049D;
+049D;CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA VERTICAL BAR;;049C;;049C
+049E;CYRILLIC CAPITAL LETTER KA WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA BAR;;;049F;
+049F;CYRILLIC SMALL LETTER KA WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA BAR;;049E;;049E
+04A0;CYRILLIC CAPITAL LETTER BASHKIR KA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED GE KA;;;04A1;
+04A1;CYRILLIC SMALL LETTER BASHKIR KA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED GE KA;;04A0;;04A0
+04A2;CYRILLIC CAPITAL LETTER EN WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN WITH RIGHT DESCENDER;;;04A3;
+04A3;CYRILLIC SMALL LETTER EN WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN WITH RIGHT DESCENDER;;04A2;;04A2
+04A4;CYRILLIC CAPITAL LIGATURE EN GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN GE;;;04A5;
+04A5;CYRILLIC SMALL LIGATURE EN GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN GE;;04A4;;04A4
+04A6;CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER PE HOOK;Abkhasian;;04A7;
+04A7;CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER PE HOOK;Abkhasian;04A6;;04A6
+04A8;CYRILLIC CAPITAL LETTER ABKHASIAN HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER O HOOK;;;04A9;
+04A9;CYRILLIC SMALL LETTER ABKHASIAN HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER O HOOK;;04A8;;04A8
+04AA;CYRILLIC CAPITAL LETTER ES WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ES CEDILLA;;;04AB;
+04AB;CYRILLIC SMALL LETTER ES WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ES CEDILLA;;04AA;;04AA
+04AC;CYRILLIC CAPITAL LETTER TE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE WITH RIGHT DESCENDER;;;04AD;
+04AD;CYRILLIC SMALL LETTER TE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE WITH RIGHT DESCENDER;;04AC;;04AC
+04AE;CYRILLIC CAPITAL LETTER STRAIGHT U;Lu;0;L;;;;;N;;;;04AF;
+04AF;CYRILLIC SMALL LETTER STRAIGHT U;Ll;0;L;;;;;N;;;04AE;;04AE
+04B0;CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER STRAIGHT U BAR;;;04B1;
+04B1;CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER STRAIGHT U BAR;;04B0;;04B0
+04B2;CYRILLIC CAPITAL LETTER HA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA WITH RIGHT DESCENDER;;;04B3;
+04B3;CYRILLIC SMALL LETTER HA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA WITH RIGHT DESCENDER;;04B2;;04B2
+04B4;CYRILLIC CAPITAL LIGATURE TE TSE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE TSE;Abkhasian;;04B5;
+04B5;CYRILLIC SMALL LIGATURE TE TSE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE TSE;Abkhasian;04B4;;04B4
+04B6;CYRILLIC CAPITAL LETTER CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH RIGHT DESCENDER;;;04B7;
+04B7;CYRILLIC SMALL LETTER CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH RIGHT DESCENDER;;04B6;;04B6
+04B8;CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE VERTICAL BAR;;;04B9;
+04B9;CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE VERTICAL BAR;;04B8;;04B8
+04BA;CYRILLIC CAPITAL LETTER SHHA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER H;;;04BB;
+04BB;CYRILLIC SMALL LETTER SHHA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER H;;04BA;;04BA
+04BC;CYRILLIC CAPITAL LETTER ABKHASIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK;;;04BD;
+04BD;CYRILLIC SMALL LETTER ABKHASIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK;;04BC;;04BC
+04BE;CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK OGONEK;;;04BF;
+04BF;CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK OGONEK;;04BE;;04BE
+04C0;CYRILLIC LETTER PALOCHKA;Lu;0;L;;;;;N;CYRILLIC LETTER I;;;;
+04C1;CYRILLIC CAPITAL LETTER ZHE WITH BREVE;Lu;0;L;0416 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT ZHE;;;04C2;
+04C2;CYRILLIC SMALL LETTER ZHE WITH BREVE;Ll;0;L;0436 0306;;;;N;CYRILLIC SMALL LETTER SHORT ZHE;;04C1;;04C1
+04C3;CYRILLIC CAPITAL LETTER KA WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA HOOK;;;04C4;
+04C4;CYRILLIC SMALL LETTER KA WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA HOOK;;04C3;;04C3
+04C7;CYRILLIC CAPITAL LETTER EN WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN HOOK;;;04C8;
+04C8;CYRILLIC SMALL LETTER EN WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN HOOK;;04C7;;04C7
+04CB;CYRILLIC CAPITAL LETTER KHAKASSIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH LEFT DESCENDER;;;04CC;
+04CC;CYRILLIC SMALL LETTER KHAKASSIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH LEFT DESCENDER;;04CB;;04CB
+04D0;CYRILLIC CAPITAL LETTER A WITH BREVE;Lu;0;L;0410 0306;;;;N;;;;04D1;
+04D1;CYRILLIC SMALL LETTER A WITH BREVE;Ll;0;L;0430 0306;;;;N;;;04D0;;04D0
+04D2;CYRILLIC CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0410 0308;;;;N;;;;04D3;
+04D3;CYRILLIC SMALL LETTER A WITH DIAERESIS;Ll;0;L;0430 0308;;;;N;;;04D2;;04D2
+04D4;CYRILLIC CAPITAL LIGATURE A IE;Lu;0;L;;;;;N;;;;04D5;
+04D5;CYRILLIC SMALL LIGATURE A IE;Ll;0;L;;;;;N;;;04D4;;04D4
+04D6;CYRILLIC CAPITAL LETTER IE WITH BREVE;Lu;0;L;0415 0306;;;;N;;;;04D7;
+04D7;CYRILLIC SMALL LETTER IE WITH BREVE;Ll;0;L;0435 0306;;;;N;;;04D6;;04D6
+04D8;CYRILLIC CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;04D9;
+04D9;CYRILLIC SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;04D8;;04D8
+04DA;CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS;Lu;0;L;04D8 0308;;;;N;;;;04DB;
+04DB;CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS;Ll;0;L;04D9 0308;;;;N;;;04DA;;04DA
+04DC;CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS;Lu;0;L;0416 0308;;;;N;;;;04DD;
+04DD;CYRILLIC SMALL LETTER ZHE WITH DIAERESIS;Ll;0;L;0436 0308;;;;N;;;04DC;;04DC
+04DE;CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS;Lu;0;L;0417 0308;;;;N;;;;04DF;
+04DF;CYRILLIC SMALL LETTER ZE WITH DIAERESIS;Ll;0;L;0437 0308;;;;N;;;04DE;;04DE
+04E0;CYRILLIC CAPITAL LETTER ABKHASIAN DZE;Lu;0;L;;;;;N;;;;04E1;
+04E1;CYRILLIC SMALL LETTER ABKHASIAN DZE;Ll;0;L;;;;;N;;;04E0;;04E0
+04E2;CYRILLIC CAPITAL LETTER I WITH MACRON;Lu;0;L;0418 0304;;;;N;;;;04E3;
+04E3;CYRILLIC SMALL LETTER I WITH MACRON;Ll;0;L;0438 0304;;;;N;;;04E2;;04E2
+04E4;CYRILLIC CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0418 0308;;;;N;;;;04E5;
+04E5;CYRILLIC SMALL LETTER I WITH DIAERESIS;Ll;0;L;0438 0308;;;;N;;;04E4;;04E4
+04E6;CYRILLIC CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;041E 0308;;;;N;;;;04E7;
+04E7;CYRILLIC SMALL LETTER O WITH DIAERESIS;Ll;0;L;043E 0308;;;;N;;;04E6;;04E6
+04E8;CYRILLIC CAPITAL LETTER BARRED O;Lu;0;L;;;;;N;;;;04E9;
+04E9;CYRILLIC SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;04E8;;04E8
+04EA;CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS;Lu;0;L;04E8 0308;;;;N;;;;04EB;
+04EB;CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS;Ll;0;L;04E9 0308;;;;N;;;04EA;;04EA
+04EC;CYRILLIC CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;042D 0308;;;;N;;;;04ED;
+04ED;CYRILLIC SMALL LETTER E WITH DIAERESIS;Ll;0;L;044D 0308;;;;N;;;04EC;;04EC
+04EE;CYRILLIC CAPITAL LETTER U WITH MACRON;Lu;0;L;0423 0304;;;;N;;;;04EF;
+04EF;CYRILLIC SMALL LETTER U WITH MACRON;Ll;0;L;0443 0304;;;;N;;;04EE;;04EE
+04F0;CYRILLIC CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0423 0308;;;;N;;;;04F1;
+04F1;CYRILLIC SMALL LETTER U WITH DIAERESIS;Ll;0;L;0443 0308;;;;N;;;04F0;;04F0
+04F2;CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0423 030B;;;;N;;;;04F3;
+04F3;CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0443 030B;;;;N;;;04F2;;04F2
+04F4;CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS;Lu;0;L;0427 0308;;;;N;;;;04F5;
+04F5;CYRILLIC SMALL LETTER CHE WITH DIAERESIS;Ll;0;L;0447 0308;;;;N;;;04F4;;04F4
+04F8;CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS;Lu;0;L;042B 0308;;;;N;;;;04F9;
+04F9;CYRILLIC SMALL LETTER YERU WITH DIAERESIS;Ll;0;L;044B 0308;;;;N;;;04F8;;04F8
+0531;ARMENIAN CAPITAL LETTER AYB;Lu;0;L;;;;;N;;;;0561;
+0532;ARMENIAN CAPITAL LETTER BEN;Lu;0;L;;;;;N;;;;0562;
+0533;ARMENIAN CAPITAL LETTER GIM;Lu;0;L;;;;;N;;;;0563;
+0534;ARMENIAN CAPITAL LETTER DA;Lu;0;L;;;;;N;;;;0564;
+0535;ARMENIAN CAPITAL LETTER ECH;Lu;0;L;;;;;N;;;;0565;
+0536;ARMENIAN CAPITAL LETTER ZA;Lu;0;L;;;;;N;;;;0566;
+0537;ARMENIAN CAPITAL LETTER EH;Lu;0;L;;;;;N;;;;0567;
+0538;ARMENIAN CAPITAL LETTER ET;Lu;0;L;;;;;N;;;;0568;
+0539;ARMENIAN CAPITAL LETTER TO;Lu;0;L;;;;;N;;;;0569;
+053A;ARMENIAN CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;056A;
+053B;ARMENIAN CAPITAL LETTER INI;Lu;0;L;;;;;N;;;;056B;
+053C;ARMENIAN CAPITAL LETTER LIWN;Lu;0;L;;;;;N;;;;056C;
+053D;ARMENIAN CAPITAL LETTER XEH;Lu;0;L;;;;;N;;;;056D;
+053E;ARMENIAN CAPITAL LETTER CA;Lu;0;L;;;;;N;;;;056E;
+053F;ARMENIAN CAPITAL LETTER KEN;Lu;0;L;;;;;N;;;;056F;
+0540;ARMENIAN CAPITAL LETTER HO;Lu;0;L;;;;;N;;;;0570;
+0541;ARMENIAN CAPITAL LETTER JA;Lu;0;L;;;;;N;;;;0571;
+0542;ARMENIAN CAPITAL LETTER GHAD;Lu;0;L;;;;;N;ARMENIAN CAPITAL LETTER LAD;;;0572;
+0543;ARMENIAN CAPITAL LETTER CHEH;Lu;0;L;;;;;N;;;;0573;
+0544;ARMENIAN CAPITAL LETTER MEN;Lu;0;L;;;;;N;;;;0574;
+0545;ARMENIAN CAPITAL LETTER YI;Lu;0;L;;;;;N;;;;0575;
+0546;ARMENIAN CAPITAL LETTER NOW;Lu;0;L;;;;;N;;;;0576;
+0547;ARMENIAN CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0577;
+0548;ARMENIAN CAPITAL LETTER VO;Lu;0;L;;;;;N;;;;0578;
+0549;ARMENIAN CAPITAL LETTER CHA;Lu;0;L;;;;;N;;;;0579;
+054A;ARMENIAN CAPITAL LETTER PEH;Lu;0;L;;;;;N;;;;057A;
+054B;ARMENIAN CAPITAL LETTER JHEH;Lu;0;L;;;;;N;;;;057B;
+054C;ARMENIAN CAPITAL LETTER RA;Lu;0;L;;;;;N;;;;057C;
+054D;ARMENIAN CAPITAL LETTER SEH;Lu;0;L;;;;;N;;;;057D;
+054E;ARMENIAN CAPITAL LETTER VEW;Lu;0;L;;;;;N;;;;057E;
+054F;ARMENIAN CAPITAL LETTER TIWN;Lu;0;L;;;;;N;;;;057F;
+0550;ARMENIAN CAPITAL LETTER REH;Lu;0;L;;;;;N;;;;0580;
+0551;ARMENIAN CAPITAL LETTER CO;Lu;0;L;;;;;N;;;;0581;
+0552;ARMENIAN CAPITAL LETTER YIWN;Lu;0;L;;;;;N;;;;0582;
+0553;ARMENIAN CAPITAL LETTER PIWR;Lu;0;L;;;;;N;;;;0583;
+0554;ARMENIAN CAPITAL LETTER KEH;Lu;0;L;;;;;N;;;;0584;
+0555;ARMENIAN CAPITAL LETTER OH;Lu;0;L;;;;;N;;;;0585;
+0556;ARMENIAN CAPITAL LETTER FEH;Lu;0;L;;;;;N;;;;0586;
+0559;ARMENIAN MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;;
+055A;ARMENIAN APOSTROPHE;Po;0;L;;;;;N;ARMENIAN MODIFIER LETTER RIGHT HALF RING;;;;
+055B;ARMENIAN EMPHASIS MARK;Po;0;L;;;;;N;;;;;
+055C;ARMENIAN EXCLAMATION MARK;Po;0;L;;;;;N;;;;;
+055D;ARMENIAN COMMA;Po;0;L;;;;;N;;;;;
+055E;ARMENIAN QUESTION MARK;Po;0;L;;;;;N;;;;;
+055F;ARMENIAN ABBREVIATION MARK;Po;0;L;;;;;N;;;;;
+0561;ARMENIAN SMALL LETTER AYB;Ll;0;L;;;;;N;;;0531;;0531
+0562;ARMENIAN SMALL LETTER BEN;Ll;0;L;;;;;N;;;0532;;0532
+0563;ARMENIAN SMALL LETTER GIM;Ll;0;L;;;;;N;;;0533;;0533
+0564;ARMENIAN SMALL LETTER DA;Ll;0;L;;;;;N;;;0534;;0534
+0565;ARMENIAN SMALL LETTER ECH;Ll;0;L;;;;;N;;;0535;;0535
+0566;ARMENIAN SMALL LETTER ZA;Ll;0;L;;;;;N;;;0536;;0536
+0567;ARMENIAN SMALL LETTER EH;Ll;0;L;;;;;N;;;0537;;0537
+0568;ARMENIAN SMALL LETTER ET;Ll;0;L;;;;;N;;;0538;;0538
+0569;ARMENIAN SMALL LETTER TO;Ll;0;L;;;;;N;;;0539;;0539
+056A;ARMENIAN SMALL LETTER ZHE;Ll;0;L;;;;;N;;;053A;;053A
+056B;ARMENIAN SMALL LETTER INI;Ll;0;L;;;;;N;;;053B;;053B
+056C;ARMENIAN SMALL LETTER LIWN;Ll;0;L;;;;;N;;;053C;;053C
+056D;ARMENIAN SMALL LETTER XEH;Ll;0;L;;;;;N;;;053D;;053D
+056E;ARMENIAN SMALL LETTER CA;Ll;0;L;;;;;N;;;053E;;053E
+056F;ARMENIAN SMALL LETTER KEN;Ll;0;L;;;;;N;;;053F;;053F
+0570;ARMENIAN SMALL LETTER HO;Ll;0;L;;;;;N;;;0540;;0540
+0571;ARMENIAN SMALL LETTER JA;Ll;0;L;;;;;N;;;0541;;0541
+0572;ARMENIAN SMALL LETTER GHAD;Ll;0;L;;;;;N;ARMENIAN SMALL LETTER LAD;;0542;;0542
+0573;ARMENIAN SMALL LETTER CHEH;Ll;0;L;;;;;N;;;0543;;0543
+0574;ARMENIAN SMALL LETTER MEN;Ll;0;L;;;;;N;;;0544;;0544
+0575;ARMENIAN SMALL LETTER YI;Ll;0;L;;;;;N;;;0545;;0545
+0576;ARMENIAN SMALL LETTER NOW;Ll;0;L;;;;;N;;;0546;;0546
+0577;ARMENIAN SMALL LETTER SHA;Ll;0;L;;;;;N;;;0547;;0547
+0578;ARMENIAN SMALL LETTER VO;Ll;0;L;;;;;N;;;0548;;0548
+0579;ARMENIAN SMALL LETTER CHA;Ll;0;L;;;;;N;;;0549;;0549
+057A;ARMENIAN SMALL LETTER PEH;Ll;0;L;;;;;N;;;054A;;054A
+057B;ARMENIAN SMALL LETTER JHEH;Ll;0;L;;;;;N;;;054B;;054B
+057C;ARMENIAN SMALL LETTER RA;Ll;0;L;;;;;N;;;054C;;054C
+057D;ARMENIAN SMALL LETTER SEH;Ll;0;L;;;;;N;;;054D;;054D
+057E;ARMENIAN SMALL LETTER VEW;Ll;0;L;;;;;N;;;054E;;054E
+057F;ARMENIAN SMALL LETTER TIWN;Ll;0;L;;;;;N;;;054F;;054F
+0580;ARMENIAN SMALL LETTER REH;Ll;0;L;;;;;N;;;0550;;0550
+0581;ARMENIAN SMALL LETTER CO;Ll;0;L;;;;;N;;;0551;;0551
+0582;ARMENIAN SMALL LETTER YIWN;Ll;0;L;;;;;N;;;0552;;0552
+0583;ARMENIAN SMALL LETTER PIWR;Ll;0;L;;;;;N;;;0553;;0553
+0584;ARMENIAN SMALL LETTER KEH;Ll;0;L;;;;;N;;;0554;;0554
+0585;ARMENIAN SMALL LETTER OH;Ll;0;L;;;;;N;;;0555;;0555
+0586;ARMENIAN SMALL LETTER FEH;Ll;0;L;;;;;N;;;0556;;0556
+0587;ARMENIAN SMALL LIGATURE ECH YIWN;Ll;0;L;<compat> 0565 0582;;;;N;;;;;
+0589;ARMENIAN FULL STOP;Po;0;L;;;;;N;ARMENIAN PERIOD;;;;
+058A;ARMENIAN HYPHEN;Pd;0;ON;;;;;N;;;;;
+0591;HEBREW ACCENT ETNAHTA;Mn;220;NSM;;;;;N;;;;;
+0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;;
+0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;;
+0594;HEBREW ACCENT ZAQEF QATAN;Mn;230;NSM;;;;;N;;;;;
+0595;HEBREW ACCENT ZAQEF GADOL;Mn;230;NSM;;;;;N;;;;;
+0596;HEBREW ACCENT TIPEHA;Mn;220;NSM;;;;;N;;*;;;
+0597;HEBREW ACCENT REVIA;Mn;230;NSM;;;;;N;;;;;
+0598;HEBREW ACCENT ZARQA;Mn;230;NSM;;;;;N;;*;;;
+0599;HEBREW ACCENT PASHTA;Mn;230;NSM;;;;;N;;;;;
+059A;HEBREW ACCENT YETIV;Mn;222;NSM;;;;;N;;;;;
+059B;HEBREW ACCENT TEVIR;Mn;220;NSM;;;;;N;;;;;
+059C;HEBREW ACCENT GERESH;Mn;230;NSM;;;;;N;;;;;
+059D;HEBREW ACCENT GERESH MUQDAM;Mn;230;NSM;;;;;N;;;;;
+059E;HEBREW ACCENT GERSHAYIM;Mn;230;NSM;;;;;N;;;;;
+059F;HEBREW ACCENT QARNEY PARA;Mn;230;NSM;;;;;N;;;;;
+05A0;HEBREW ACCENT TELISHA GEDOLA;Mn;230;NSM;;;;;N;;;;;
+05A1;HEBREW ACCENT PAZER;Mn;230;NSM;;;;;N;;;;;
+05A3;HEBREW ACCENT MUNAH;Mn;220;NSM;;;;;N;;;;;
+05A4;HEBREW ACCENT MAHAPAKH;Mn;220;NSM;;;;;N;;;;;
+05A5;HEBREW ACCENT MERKHA;Mn;220;NSM;;;;;N;;*;;;
+05A6;HEBREW ACCENT MERKHA KEFULA;Mn;220;NSM;;;;;N;;;;;
+05A7;HEBREW ACCENT DARGA;Mn;220;NSM;;;;;N;;;;;
+05A8;HEBREW ACCENT QADMA;Mn;230;NSM;;;;;N;;*;;;
+05A9;HEBREW ACCENT TELISHA QETANA;Mn;230;NSM;;;;;N;;;;;
+05AA;HEBREW ACCENT YERAH BEN YOMO;Mn;220;NSM;;;;;N;;*;;;
+05AB;HEBREW ACCENT OLE;Mn;230;NSM;;;;;N;;;;;
+05AC;HEBREW ACCENT ILUY;Mn;230;NSM;;;;;N;;;;;
+05AD;HEBREW ACCENT DEHI;Mn;222;NSM;;;;;N;;;;;
+05AE;HEBREW ACCENT ZINOR;Mn;228;NSM;;;;;N;;;;;
+05AF;HEBREW MARK MASORA CIRCLE;Mn;230;NSM;;;;;N;;;;;
+05B0;HEBREW POINT SHEVA;Mn;10;NSM;;;;;N;;;;;
+05B1;HEBREW POINT HATAF SEGOL;Mn;11;NSM;;;;;N;;;;;
+05B2;HEBREW POINT HATAF PATAH;Mn;12;NSM;;;;;N;;;;;
+05B3;HEBREW POINT HATAF QAMATS;Mn;13;NSM;;;;;N;;;;;
+05B4;HEBREW POINT HIRIQ;Mn;14;NSM;;;;;N;;;;;
+05B5;HEBREW POINT TSERE;Mn;15;NSM;;;;;N;;;;;
+05B6;HEBREW POINT SEGOL;Mn;16;NSM;;;;;N;;;;;
+05B7;HEBREW POINT PATAH;Mn;17;NSM;;;;;N;;;;;
+05B8;HEBREW POINT QAMATS;Mn;18;NSM;;;;;N;;;;;
+05B9;HEBREW POINT HOLAM;Mn;19;NSM;;;;;N;;;;;
+05BB;HEBREW POINT QUBUTS;Mn;20;NSM;;;;;N;;;;;
+05BC;HEBREW POINT DAGESH OR MAPIQ;Mn;21;NSM;;;;;N;HEBREW POINT DAGESH;or shuruq;;;
+05BD;HEBREW POINT METEG;Mn;22;NSM;;;;;N;;*;;;
+05BE;HEBREW PUNCTUATION MAQAF;Po;0;R;;;;;N;;;;;
+05BF;HEBREW POINT RAFE;Mn;23;NSM;;;;;N;;;;;
+05C0;HEBREW PUNCTUATION PASEQ;Po;0;R;;;;;N;HEBREW POINT PASEQ;*;;;
+05C1;HEBREW POINT SHIN DOT;Mn;24;NSM;;;;;N;;;;;
+05C2;HEBREW POINT SIN DOT;Mn;25;NSM;;;;;N;;;;;
+05C3;HEBREW PUNCTUATION SOF PASUQ;Po;0;R;;;;;N;;*;;;
+05C4;HEBREW MARK UPPER DOT;Mn;230;NSM;;;;;N;;;;;
+05D0;HEBREW LETTER ALEF;Lo;0;R;;;;;N;;;;;
+05D1;HEBREW LETTER BET;Lo;0;R;;;;;N;;;;;
+05D2;HEBREW LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+05D3;HEBREW LETTER DALET;Lo;0;R;;;;;N;;;;;
+05D4;HEBREW LETTER HE;Lo;0;R;;;;;N;;;;;
+05D5;HEBREW LETTER VAV;Lo;0;R;;;;;N;;;;;
+05D6;HEBREW LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+05D7;HEBREW LETTER HET;Lo;0;R;;;;;N;;;;;
+05D8;HEBREW LETTER TET;Lo;0;R;;;;;N;;;;;
+05D9;HEBREW LETTER YOD;Lo;0;R;;;;;N;;;;;
+05DA;HEBREW LETTER FINAL KAF;Lo;0;R;;;;;N;;;;;
+05DB;HEBREW LETTER KAF;Lo;0;R;;;;;N;;;;;
+05DC;HEBREW LETTER LAMED;Lo;0;R;;;;;N;;;;;
+05DD;HEBREW LETTER FINAL MEM;Lo;0;R;;;;;N;;;;;
+05DE;HEBREW LETTER MEM;Lo;0;R;;;;;N;;;;;
+05DF;HEBREW LETTER FINAL NUN;Lo;0;R;;;;;N;;;;;
+05E0;HEBREW LETTER NUN;Lo;0;R;;;;;N;;;;;
+05E1;HEBREW LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+05E2;HEBREW LETTER AYIN;Lo;0;R;;;;;N;;;;;
+05E3;HEBREW LETTER FINAL PE;Lo;0;R;;;;;N;;;;;
+05E4;HEBREW LETTER PE;Lo;0;R;;;;;N;;;;;
+05E5;HEBREW LETTER FINAL TSADI;Lo;0;R;;;;;N;;;;;
+05E6;HEBREW LETTER TSADI;Lo;0;R;;;;;N;;;;;
+05E7;HEBREW LETTER QOF;Lo;0;R;;;;;N;;;;;
+05E8;HEBREW LETTER RESH;Lo;0;R;;;;;N;;;;;
+05E9;HEBREW LETTER SHIN;Lo;0;R;;;;;N;;;;;
+05EA;HEBREW LETTER TAV;Lo;0;R;;;;;N;;;;;
+05F0;HEBREW LIGATURE YIDDISH DOUBLE VAV;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE VAV;;;;
+05F1;HEBREW LIGATURE YIDDISH VAV YOD;Lo;0;R;;;;;N;HEBREW LETTER VAV YOD;;;;
+05F2;HEBREW LIGATURE YIDDISH DOUBLE YOD;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE YOD;;;;
+05F3;HEBREW PUNCTUATION GERESH;Po;0;R;;;;;N;;;;;
+05F4;HEBREW PUNCTUATION GERSHAYIM;Po;0;R;;;;;N;;;;;
+060C;ARABIC COMMA;Po;0;CS;;;;;N;;;;;
+061B;ARABIC SEMICOLON;Po;0;AL;;;;;N;;;;;
+061F;ARABIC QUESTION MARK;Po;0;AL;;;;;N;;;;;
+0621;ARABIC LETTER HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH;;;;
+0622;ARABIC LETTER ALEF WITH MADDA ABOVE;Lo;0;AL;0627 0653;;;;N;ARABIC LETTER MADDAH ON ALEF;;;;
+0623;ARABIC LETTER ALEF WITH HAMZA ABOVE;Lo;0;AL;0627 0654;;;;N;ARABIC LETTER HAMZAH ON ALEF;;;;
+0624;ARABIC LETTER WAW WITH HAMZA ABOVE;Lo;0;AL;0648 0654;;;;N;ARABIC LETTER HAMZAH ON WAW;;;;
+0625;ARABIC LETTER ALEF WITH HAMZA BELOW;Lo;0;AL;0627 0655;;;;N;ARABIC LETTER HAMZAH UNDER ALEF;;;;
+0626;ARABIC LETTER YEH WITH HAMZA ABOVE;Lo;0;AL;064A 0654;;;;N;ARABIC LETTER HAMZAH ON YA;;;;
+0627;ARABIC LETTER ALEF;Lo;0;AL;;;;;N;;;;;
+0628;ARABIC LETTER BEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA;;;;
+0629;ARABIC LETTER TEH MARBUTA;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH;;;;
+062A;ARABIC LETTER TEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA;;;;
+062B;ARABIC LETTER THEH;Lo;0;AL;;;;;N;ARABIC LETTER THAA;;;;
+062C;ARABIC LETTER JEEM;Lo;0;AL;;;;;N;;;;;
+062D;ARABIC LETTER HAH;Lo;0;AL;;;;;N;ARABIC LETTER HAA;;;;
+062E;ARABIC LETTER KHAH;Lo;0;AL;;;;;N;ARABIC LETTER KHAA;;;;
+062F;ARABIC LETTER DAL;Lo;0;AL;;;;;N;;;;;
+0630;ARABIC LETTER THAL;Lo;0;AL;;;;;N;;;;;
+0631;ARABIC LETTER REH;Lo;0;AL;;;;;N;ARABIC LETTER RA;;;;
+0632;ARABIC LETTER ZAIN;Lo;0;AL;;;;;N;;;;;
+0633;ARABIC LETTER SEEN;Lo;0;AL;;;;;N;;;;;
+0634;ARABIC LETTER SHEEN;Lo;0;AL;;;;;N;;;;;
+0635;ARABIC LETTER SAD;Lo;0;AL;;;;;N;;;;;
+0636;ARABIC LETTER DAD;Lo;0;AL;;;;;N;;;;;
+0637;ARABIC LETTER TAH;Lo;0;AL;;;;;N;;;;;
+0638;ARABIC LETTER ZAH;Lo;0;AL;;;;;N;ARABIC LETTER DHAH;;;;
+0639;ARABIC LETTER AIN;Lo;0;AL;;;;;N;;;;;
+063A;ARABIC LETTER GHAIN;Lo;0;AL;;;;;N;;;;;
+0640;ARABIC TATWEEL;Lm;0;AL;;;;;N;;;;;
+0641;ARABIC LETTER FEH;Lo;0;AL;;;;;N;ARABIC LETTER FA;;;;
+0642;ARABIC LETTER QAF;Lo;0;AL;;;;;N;;;;;
+0643;ARABIC LETTER KAF;Lo;0;AL;;;;;N;ARABIC LETTER CAF;;;;
+0644;ARABIC LETTER LAM;Lo;0;AL;;;;;N;;;;;
+0645;ARABIC LETTER MEEM;Lo;0;AL;;;;;N;;;;;
+0646;ARABIC LETTER NOON;Lo;0;AL;;;;;N;;;;;
+0647;ARABIC LETTER HEH;Lo;0;AL;;;;;N;ARABIC LETTER HA;;;;
+0648;ARABIC LETTER WAW;Lo;0;AL;;;;;N;;;;;
+0649;ARABIC LETTER ALEF MAKSURA;Lo;0;AL;;;;;N;ARABIC LETTER ALEF MAQSURAH;;;;
+064A;ARABIC LETTER YEH;Lo;0;AL;;;;;N;ARABIC LETTER YA;;;;
+064B;ARABIC FATHATAN;Mn;27;NSM;;;;;N;;;;;
+064C;ARABIC DAMMATAN;Mn;28;NSM;;;;;N;;;;;
+064D;ARABIC KASRATAN;Mn;29;NSM;;;;;N;;;;;
+064E;ARABIC FATHA;Mn;30;NSM;;;;;N;ARABIC FATHAH;;;;
+064F;ARABIC DAMMA;Mn;31;NSM;;;;;N;ARABIC DAMMAH;;;;
+0650;ARABIC KASRA;Mn;32;NSM;;;;;N;ARABIC KASRAH;;;;
+0651;ARABIC SHADDA;Mn;33;NSM;;;;;N;ARABIC SHADDAH;;;;
+0652;ARABIC SUKUN;Mn;34;NSM;;;;;N;;;;;
+0653;ARABIC MADDAH ABOVE;Mn;230;NSM;;;;;N;;;;;
+0654;ARABIC HAMZA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0655;ARABIC HAMZA BELOW;Mn;220;NSM;;;;;N;;;;;
+0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;;
+0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;;
+0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;;
+0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;;
+0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;;
+0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;;
+0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;;
+0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;;
+0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;;
+0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;;
+066A;ARABIC PERCENT SIGN;Po;0;ET;;;;;N;;;;;
+066B;ARABIC DECIMAL SEPARATOR;Po;0;AN;;;;;N;;;;;
+066C;ARABIC THOUSANDS SEPARATOR;Po;0;AN;;;;;N;;;;;
+066D;ARABIC FIVE POINTED STAR;Po;0;AL;;;;;N;;;;;
+0670;ARABIC LETTER SUPERSCRIPT ALEF;Mn;35;NSM;;;;;N;ARABIC ALEF ABOVE;;;;
+0671;ARABIC LETTER ALEF WASLA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAT WASL ON ALEF;;;;
+0672;ARABIC LETTER ALEF WITH WAVY HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH ON ALEF;;;;
+0673;ARABIC LETTER ALEF WITH WAVY HAMZA BELOW;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH UNDER ALEF;;;;
+0674;ARABIC LETTER HIGH HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HIGH HAMZAH;;;;
+0675;ARABIC LETTER HIGH HAMZA ALEF;Lo;0;AL;<compat> 0627 0674;;;;N;ARABIC LETTER HIGH HAMZAH ALEF;;;;
+0676;ARABIC LETTER HIGH HAMZA WAW;Lo;0;AL;<compat> 0648 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW;;;;
+0677;ARABIC LETTER U WITH HAMZA ABOVE;Lo;0;AL;<compat> 06C7 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW WITH DAMMAH;;;;
+0678;ARABIC LETTER HIGH HAMZA YEH;Lo;0;AL;<compat> 064A 0674;;;;N;ARABIC LETTER HIGH HAMZAH YA;;;;
+0679;ARABIC LETTER TTEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH SMALL TAH;;;;
+067A;ARABIC LETTER TTEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH TWO DOTS VERTICAL ABOVE;;;;
+067B;ARABIC LETTER BEEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH TWO DOTS VERTICAL BELOW;;;;
+067C;ARABIC LETTER TEH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH RING;;;;
+067D;ARABIC LETTER TEH WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS ABOVE DOWNWARD;;;;
+067E;ARABIC LETTER PEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS BELOW;;;;
+067F;ARABIC LETTER TEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH FOUR DOTS ABOVE;;;;
+0680;ARABIC LETTER BEHEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH FOUR DOTS BELOW;;;;
+0681;ARABIC LETTER HAH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH ON HAA;;;;
+0682;ARABIC LETTER HAH WITH TWO DOTS VERTICAL ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH TWO DOTS VERTICAL ABOVE;;;;
+0683;ARABIC LETTER NYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS;;;;
+0684;ARABIC LETTER DYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS VERTICAL;;;;
+0685;ARABIC LETTER HAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH THREE DOTS ABOVE;;;;
+0686;ARABIC LETTER TCHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE THREE DOTS DOWNWARD;;;;
+0687;ARABIC LETTER TCHEHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE FOUR DOTS;;;;
+0688;ARABIC LETTER DDAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH SMALL TAH;;;;
+0689;ARABIC LETTER DAL WITH RING;Lo;0;AL;;;;;N;;;;;
+068A;ARABIC LETTER DAL WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+068B;ARABIC LETTER DAL WITH DOT BELOW AND SMALL TAH;Lo;0;AL;;;;;N;;;;;
+068C;ARABIC LETTER DAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS ABOVE;;;;
+068D;ARABIC LETTER DDAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS BELOW;;;;
+068E;ARABIC LETTER DUL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE;;;;
+068F;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARD;;;;
+0690;ARABIC LETTER DAL WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+0691;ARABIC LETTER RREH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL TAH;;;;
+0692;ARABIC LETTER REH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V;;;;
+0693;ARABIC LETTER REH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH RING;;;;
+0694;ARABIC LETTER REH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW;;;;
+0695;ARABIC LETTER REH WITH SMALL V BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V BELOW;;;;
+0696;ARABIC LETTER REH WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW AND DOT ABOVE;;;;
+0697;ARABIC LETTER REH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH TWO DOTS ABOVE;;;;
+0698;ARABIC LETTER JEH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH THREE DOTS ABOVE;;;;
+0699;ARABIC LETTER REH WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH FOUR DOTS ABOVE;;;;
+069A;ARABIC LETTER SEEN WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+069B;ARABIC LETTER SEEN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+069C;ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+069D;ARABIC LETTER SAD WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+069E;ARABIC LETTER SAD WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+069F;ARABIC LETTER TAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A0;ARABIC LETTER AIN WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A1;ARABIC LETTER DOTLESS FEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS FA;;;;
+06A2;ARABIC LETTER FEH WITH DOT MOVED BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT MOVED BELOW;;;;
+06A3;ARABIC LETTER FEH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT BELOW;;;;
+06A4;ARABIC LETTER VEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS ABOVE;;;;
+06A5;ARABIC LETTER FEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS BELOW;;;;
+06A6;ARABIC LETTER PEHEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH FOUR DOTS ABOVE;;;;
+06A7;ARABIC LETTER QAF WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06A8;ARABIC LETTER QAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A9;ARABIC LETTER KEHEH;Lo;0;AL;;;;;N;ARABIC LETTER OPEN CAF;;;;
+06AA;ARABIC LETTER SWASH KAF;Lo;0;AL;;;;;N;ARABIC LETTER SWASH CAF;;;;
+06AB;ARABIC LETTER KAF WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH RING;;;;
+06AC;ARABIC LETTER KAF WITH DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH DOT ABOVE;;;;
+06AD;ARABIC LETTER NG;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS ABOVE;;;;
+06AE;ARABIC LETTER KAF WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS BELOW;;;;
+06AF;ARABIC LETTER GAF;Lo;0;AL;;;;;N;;*;;;
+06B0;ARABIC LETTER GAF WITH RING;Lo;0;AL;;;;;N;;;;;
+06B1;ARABIC LETTER NGOEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS ABOVE;;;;
+06B2;ARABIC LETTER GAF WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+06B3;ARABIC LETTER GUEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS VERTICAL BELOW;;;;
+06B4;ARABIC LETTER GAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06B5;ARABIC LETTER LAM WITH SMALL V;Lo;0;AL;;;;;N;;;;;
+06B6;ARABIC LETTER LAM WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06B7;ARABIC LETTER LAM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06B8;ARABIC LETTER LAM WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+06B9;ARABIC LETTER NOON WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06BA;ARABIC LETTER NOON GHUNNA;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON;;;;
+06BB;ARABIC LETTER RNOON;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON WITH SMALL TAH;;;;
+06BC;ARABIC LETTER NOON WITH RING;Lo;0;AL;;;;;N;;;;;
+06BD;ARABIC LETTER NOON WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06BE;ARABIC LETTER HEH DOACHASHMEE;Lo;0;AL;;;;;N;ARABIC LETTER KNOTTED HA;;;;
+06BF;ARABIC LETTER TCHEH WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06C0;ARABIC LETTER HEH WITH YEH ABOVE;Lo;0;AL;06D5 0654;;;;N;ARABIC LETTER HAMZAH ON HA;;;;
+06C1;ARABIC LETTER HEH GOAL;Lo;0;AL;;;;;N;ARABIC LETTER HA GOAL;;;;
+06C2;ARABIC LETTER HEH GOAL WITH HAMZA ABOVE;Lo;0;AL;06C1 0654;;;;N;ARABIC LETTER HAMZAH ON HA GOAL;;;;
+06C3;ARABIC LETTER TEH MARBUTA GOAL;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH GOAL;;;;
+06C4;ARABIC LETTER WAW WITH RING;Lo;0;AL;;;;;N;;;;;
+06C5;ARABIC LETTER KIRGHIZ OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH BAR;;;;
+06C6;ARABIC LETTER OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH SMALL V;;;;
+06C7;ARABIC LETTER U;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH DAMMAH;;;;
+06C8;ARABIC LETTER YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH ALEF ABOVE;;;;
+06C9;ARABIC LETTER KIRGHIZ YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH INVERTED SMALL V;;;;
+06CA;ARABIC LETTER WAW WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06CB;ARABIC LETTER VE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH THREE DOTS ABOVE;;;;
+06CC;ARABIC LETTER FARSI YEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS YA;;;;
+06CD;ARABIC LETTER YEH WITH TAIL;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TAIL;;;;
+06CE;ARABIC LETTER YEH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH SMALL V;;;;
+06CF;ARABIC LETTER WAW WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06D0;ARABIC LETTER E;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TWO DOTS VERTICAL BELOW;*;;;
+06D1;ARABIC LETTER YEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH THREE DOTS BELOW;;;;
+06D2;ARABIC LETTER YEH BARREE;Lo;0;AL;;;;;N;ARABIC LETTER YA BARREE;;;;
+06D3;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE;Lo;0;AL;06D2 0654;;;;N;ARABIC LETTER HAMZAH ON YA BARREE;;;;
+06D4;ARABIC FULL STOP;Po;0;AL;;;;;N;ARABIC PERIOD;;;;
+06D5;ARABIC LETTER AE;Lo;0;AL;;;;;N;;;;;
+06D6;ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;
+06D7;ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;
+06D8;ARABIC SMALL HIGH MEEM INITIAL FORM;Mn;230;NSM;;;;;N;;;;;
+06D9;ARABIC SMALL HIGH LAM ALEF;Mn;230;NSM;;;;;N;;;;;
+06DA;ARABIC SMALL HIGH JEEM;Mn;230;NSM;;;;;N;;;;;
+06DB;ARABIC SMALL HIGH THREE DOTS;Mn;230;NSM;;;;;N;;;;;
+06DC;ARABIC SMALL HIGH SEEN;Mn;230;NSM;;;;;N;;;;;
+06DD;ARABIC END OF AYAH;Me;0;NSM;;;;;N;;;;;
+06DE;ARABIC START OF RUB EL HIZB;Me;0;NSM;;;;;N;;;;;
+06DF;ARABIC SMALL HIGH ROUNDED ZERO;Mn;230;NSM;;;;;N;;;;;
+06E0;ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO;Mn;230;NSM;;;;;N;;;;;
+06E1;ARABIC SMALL HIGH DOTLESS HEAD OF KHAH;Mn;230;NSM;;;;;N;;;;;
+06E2;ARABIC SMALL HIGH MEEM ISOLATED FORM;Mn;230;NSM;;;;;N;;;;;
+06E3;ARABIC SMALL LOW SEEN;Mn;220;NSM;;;;;N;;;;;
+06E4;ARABIC SMALL HIGH MADDA;Mn;230;NSM;;;;;N;;;;;
+06E5;ARABIC SMALL WAW;Lm;0;AL;;;;;N;;;;;
+06E6;ARABIC SMALL YEH;Lm;0;AL;;;;;N;;;;;
+06E7;ARABIC SMALL HIGH YEH;Mn;230;NSM;;;;;N;;;;;
+06E8;ARABIC SMALL HIGH NOON;Mn;230;NSM;;;;;N;;;;;
+06E9;ARABIC PLACE OF SAJDAH;So;0;ON;;;;;N;;;;;
+06EA;ARABIC EMPTY CENTRE LOW STOP;Mn;220;NSM;;;;;N;;;;;
+06EB;ARABIC EMPTY CENTRE HIGH STOP;Mn;230;NSM;;;;;N;;;;;
+06EC;ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE;Mn;230;NSM;;;;;N;;;;;
+06ED;ARABIC SMALL LOW MEEM;Mn;220;NSM;;;;;N;;;;;
+06F0;EXTENDED ARABIC-INDIC DIGIT ZERO;Nd;0;EN;;0;0;0;N;EASTERN ARABIC-INDIC DIGIT ZERO;;;;
+06F1;EXTENDED ARABIC-INDIC DIGIT ONE;Nd;0;EN;;1;1;1;N;EASTERN ARABIC-INDIC DIGIT ONE;;;;
+06F2;EXTENDED ARABIC-INDIC DIGIT TWO;Nd;0;EN;;2;2;2;N;EASTERN ARABIC-INDIC DIGIT TWO;;;;
+06F3;EXTENDED ARABIC-INDIC DIGIT THREE;Nd;0;EN;;3;3;3;N;EASTERN ARABIC-INDIC DIGIT THREE;;;;
+06F4;EXTENDED ARABIC-INDIC DIGIT FOUR;Nd;0;EN;;4;4;4;N;EASTERN ARABIC-INDIC DIGIT FOUR;;;;
+06F5;EXTENDED ARABIC-INDIC DIGIT FIVE;Nd;0;EN;;5;5;5;N;EASTERN ARABIC-INDIC DIGIT FIVE;;;;
+06F6;EXTENDED ARABIC-INDIC DIGIT SIX;Nd;0;EN;;6;6;6;N;EASTERN ARABIC-INDIC DIGIT SIX;;;;
+06F7;EXTENDED ARABIC-INDIC DIGIT SEVEN;Nd;0;EN;;7;7;7;N;EASTERN ARABIC-INDIC DIGIT SEVEN;;;;
+06F8;EXTENDED ARABIC-INDIC DIGIT EIGHT;Nd;0;EN;;8;8;8;N;EASTERN ARABIC-INDIC DIGIT EIGHT;;;;
+06F9;EXTENDED ARABIC-INDIC DIGIT NINE;Nd;0;EN;;9;9;9;N;EASTERN ARABIC-INDIC DIGIT NINE;;;;
+06FA;ARABIC LETTER SHEEN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FB;ARABIC LETTER DAD WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FC;ARABIC LETTER GHAIN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FD;ARABIC SIGN SINDHI AMPERSAND;So;0;AL;;;;;N;;;;;
+06FE;ARABIC SIGN SINDHI POSTPOSITION MEN;So;0;AL;;;;;N;;;;;
+0700;SYRIAC END OF PARAGRAPH;Po;0;AL;;;;;N;;;;;
+0701;SYRIAC SUPRALINEAR FULL STOP;Po;0;AL;;;;;N;;;;;
+0702;SYRIAC SUBLINEAR FULL STOP;Po;0;AL;;;;;N;;;;;
+0703;SYRIAC SUPRALINEAR COLON;Po;0;AL;;;;;N;;;;;
+0704;SYRIAC SUBLINEAR COLON;Po;0;AL;;;;;N;;;;;
+0705;SYRIAC HORIZONTAL COLON;Po;0;AL;;;;;N;;;;;
+0706;SYRIAC COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;;
+0707;SYRIAC COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;;
+0708;SYRIAC SUPRALINEAR COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;;
+0709;SYRIAC SUBLINEAR COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;;
+070A;SYRIAC CONTRACTION;Po;0;AL;;;;;N;;;;;
+070B;SYRIAC HARKLEAN OBELUS;Po;0;AL;;;;;N;;;;;
+070C;SYRIAC HARKLEAN METOBELUS;Po;0;AL;;;;;N;;;;;
+070D;SYRIAC HARKLEAN ASTERISCUS;Po;0;AL;;;;;N;;;;;
+070F;SYRIAC ABBREVIATION MARK;Cf;0;BN;;;;;N;;;;;
+0710;SYRIAC LETTER ALAPH;Lo;0;AL;;;;;N;;;;;
+0711;SYRIAC LETTER SUPERSCRIPT ALAPH;Mn;36;NSM;;;;;N;;;;;
+0712;SYRIAC LETTER BETH;Lo;0;AL;;;;;N;;;;;
+0713;SYRIAC LETTER GAMAL;Lo;0;AL;;;;;N;;;;;
+0714;SYRIAC LETTER GAMAL GARSHUNI;Lo;0;AL;;;;;N;;;;;
+0715;SYRIAC LETTER DALATH;Lo;0;AL;;;;;N;;;;;
+0716;SYRIAC LETTER DOTLESS DALATH RISH;Lo;0;AL;;;;;N;;;;;
+0717;SYRIAC LETTER HE;Lo;0;AL;;;;;N;;;;;
+0718;SYRIAC LETTER WAW;Lo;0;AL;;;;;N;;;;;
+0719;SYRIAC LETTER ZAIN;Lo;0;AL;;;;;N;;;;;
+071A;SYRIAC LETTER HETH;Lo;0;AL;;;;;N;;;;;
+071B;SYRIAC LETTER TETH;Lo;0;AL;;;;;N;;;;;
+071C;SYRIAC LETTER TETH GARSHUNI;Lo;0;AL;;;;;N;;;;;
+071D;SYRIAC LETTER YUDH;Lo;0;AL;;;;;N;;;;;
+071E;SYRIAC LETTER YUDH HE;Lo;0;AL;;;;;N;;;;;
+071F;SYRIAC LETTER KAPH;Lo;0;AL;;;;;N;;;;;
+0720;SYRIAC LETTER LAMADH;Lo;0;AL;;;;;N;;;;;
+0721;SYRIAC LETTER MIM;Lo;0;AL;;;;;N;;;;;
+0722;SYRIAC LETTER NUN;Lo;0;AL;;;;;N;;;;;
+0723;SYRIAC LETTER SEMKATH;Lo;0;AL;;;;;N;;;;;
+0724;SYRIAC LETTER FINAL SEMKATH;Lo;0;AL;;;;;N;;;;;
+0725;SYRIAC LETTER E;Lo;0;AL;;;;;N;;;;;
+0726;SYRIAC LETTER PE;Lo;0;AL;;;;;N;;;;;
+0727;SYRIAC LETTER REVERSED PE;Lo;0;AL;;;;;N;;;;;
+0728;SYRIAC LETTER SADHE;Lo;0;AL;;;;;N;;;;;
+0729;SYRIAC LETTER QAPH;Lo;0;AL;;;;;N;;;;;
+072A;SYRIAC LETTER RISH;Lo;0;AL;;;;;N;;;;;
+072B;SYRIAC LETTER SHIN;Lo;0;AL;;;;;N;;;;;
+072C;SYRIAC LETTER TAW;Lo;0;AL;;;;;N;;;;;
+0730;SYRIAC PTHAHA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0731;SYRIAC PTHAHA BELOW;Mn;220;NSM;;;;;N;;;;;
+0732;SYRIAC PTHAHA DOTTED;Mn;230;NSM;;;;;N;;;;;
+0733;SYRIAC ZQAPHA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0734;SYRIAC ZQAPHA BELOW;Mn;220;NSM;;;;;N;;;;;
+0735;SYRIAC ZQAPHA DOTTED;Mn;230;NSM;;;;;N;;;;;
+0736;SYRIAC RBASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0737;SYRIAC RBASA BELOW;Mn;220;NSM;;;;;N;;;;;
+0738;SYRIAC DOTTED ZLAMA HORIZONTAL;Mn;220;NSM;;;;;N;;;;;
+0739;SYRIAC DOTTED ZLAMA ANGULAR;Mn;220;NSM;;;;;N;;;;;
+073A;SYRIAC HBASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+073B;SYRIAC HBASA BELOW;Mn;220;NSM;;;;;N;;;;;
+073C;SYRIAC HBASA-ESASA DOTTED;Mn;220;NSM;;;;;N;;;;;
+073D;SYRIAC ESASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+073E;SYRIAC ESASA BELOW;Mn;220;NSM;;;;;N;;;;;
+073F;SYRIAC RWAHA;Mn;230;NSM;;;;;N;;;;;
+0740;SYRIAC FEMININE DOT;Mn;230;NSM;;;;;N;;;;;
+0741;SYRIAC QUSHSHAYA;Mn;230;NSM;;;;;N;;;;;
+0742;SYRIAC RUKKAKHA;Mn;220;NSM;;;;;N;;;;;
+0743;SYRIAC TWO VERTICAL DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+0744;SYRIAC TWO VERTICAL DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+0745;SYRIAC THREE DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+0746;SYRIAC THREE DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+0747;SYRIAC OBLIQUE LINE ABOVE;Mn;230;NSM;;;;;N;;;;;
+0748;SYRIAC OBLIQUE LINE BELOW;Mn;220;NSM;;;;;N;;;;;
+0749;SYRIAC MUSIC;Mn;230;NSM;;;;;N;;;;;
+074A;SYRIAC BARREKH;Mn;230;NSM;;;;;N;;;;;
+0780;THAANA LETTER HAA;Lo;0;AL;;;;;N;;;;;
+0781;THAANA LETTER SHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0782;THAANA LETTER NOONU;Lo;0;AL;;;;;N;;;;;
+0783;THAANA LETTER RAA;Lo;0;AL;;;;;N;;;;;
+0784;THAANA LETTER BAA;Lo;0;AL;;;;;N;;;;;
+0785;THAANA LETTER LHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0786;THAANA LETTER KAAFU;Lo;0;AL;;;;;N;;;;;
+0787;THAANA LETTER ALIFU;Lo;0;AL;;;;;N;;;;;
+0788;THAANA LETTER VAAVU;Lo;0;AL;;;;;N;;;;;
+0789;THAANA LETTER MEEMU;Lo;0;AL;;;;;N;;;;;
+078A;THAANA LETTER FAAFU;Lo;0;AL;;;;;N;;;;;
+078B;THAANA LETTER DHAALU;Lo;0;AL;;;;;N;;;;;
+078C;THAANA LETTER THAA;Lo;0;AL;;;;;N;;;;;
+078D;THAANA LETTER LAAMU;Lo;0;AL;;;;;N;;;;;
+078E;THAANA LETTER GAAFU;Lo;0;AL;;;;;N;;;;;
+078F;THAANA LETTER GNAVIYANI;Lo;0;AL;;;;;N;;;;;
+0790;THAANA LETTER SEENU;Lo;0;AL;;;;;N;;;;;
+0791;THAANA LETTER DAVIYANI;Lo;0;AL;;;;;N;;;;;
+0792;THAANA LETTER ZAVIYANI;Lo;0;AL;;;;;N;;;;;
+0793;THAANA LETTER TAVIYANI;Lo;0;AL;;;;;N;;;;;
+0794;THAANA LETTER YAA;Lo;0;AL;;;;;N;;;;;
+0795;THAANA LETTER PAVIYANI;Lo;0;AL;;;;;N;;;;;
+0796;THAANA LETTER JAVIYANI;Lo;0;AL;;;;;N;;;;;
+0797;THAANA LETTER CHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0798;THAANA LETTER TTAA;Lo;0;AL;;;;;N;;;;;
+0799;THAANA LETTER HHAA;Lo;0;AL;;;;;N;;;;;
+079A;THAANA LETTER KHAA;Lo;0;AL;;;;;N;;;;;
+079B;THAANA LETTER THAALU;Lo;0;AL;;;;;N;;;;;
+079C;THAANA LETTER ZAA;Lo;0;AL;;;;;N;;;;;
+079D;THAANA LETTER SHEENU;Lo;0;AL;;;;;N;;;;;
+079E;THAANA LETTER SAADHU;Lo;0;AL;;;;;N;;;;;
+079F;THAANA LETTER DAADHU;Lo;0;AL;;;;;N;;;;;
+07A0;THAANA LETTER TO;Lo;0;AL;;;;;N;;;;;
+07A1;THAANA LETTER ZO;Lo;0;AL;;;;;N;;;;;
+07A2;THAANA LETTER AINU;Lo;0;AL;;;;;N;;;;;
+07A3;THAANA LETTER GHAINU;Lo;0;AL;;;;;N;;;;;
+07A4;THAANA LETTER QAAFU;Lo;0;AL;;;;;N;;;;;
+07A5;THAANA LETTER WAAVU;Lo;0;AL;;;;;N;;;;;
+07A6;THAANA ABAFILI;Mn;0;NSM;;;;;N;;;;;
+07A7;THAANA AABAAFILI;Mn;0;NSM;;;;;N;;;;;
+07A8;THAANA IBIFILI;Mn;0;NSM;;;;;N;;;;;
+07A9;THAANA EEBEEFILI;Mn;0;NSM;;;;;N;;;;;
+07AA;THAANA UBUFILI;Mn;0;NSM;;;;;N;;;;;
+07AB;THAANA OOBOOFILI;Mn;0;NSM;;;;;N;;;;;
+07AC;THAANA EBEFILI;Mn;0;NSM;;;;;N;;;;;
+07AD;THAANA EYBEYFILI;Mn;0;NSM;;;;;N;;;;;
+07AE;THAANA OBOFILI;Mn;0;NSM;;;;;N;;;;;
+07AF;THAANA OABOAFILI;Mn;0;NSM;;;;;N;;;;;
+07B0;THAANA SUKUN;Mn;0;NSM;;;;;N;;;;;
+0901;DEVANAGARI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0902;DEVANAGARI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0903;DEVANAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0905;DEVANAGARI LETTER A;Lo;0;L;;;;;N;;;;;
+0906;DEVANAGARI LETTER AA;Lo;0;L;;;;;N;;;;;
+0907;DEVANAGARI LETTER I;Lo;0;L;;;;;N;;;;;
+0908;DEVANAGARI LETTER II;Lo;0;L;;;;;N;;;;;
+0909;DEVANAGARI LETTER U;Lo;0;L;;;;;N;;;;;
+090A;DEVANAGARI LETTER UU;Lo;0;L;;;;;N;;;;;
+090B;DEVANAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+090C;DEVANAGARI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+090D;DEVANAGARI LETTER CANDRA E;Lo;0;L;;;;;N;;;;;
+090E;DEVANAGARI LETTER SHORT E;Lo;0;L;;;;;N;;;;;
+090F;DEVANAGARI LETTER E;Lo;0;L;;;;;N;;;;;
+0910;DEVANAGARI LETTER AI;Lo;0;L;;;;;N;;;;;
+0911;DEVANAGARI LETTER CANDRA O;Lo;0;L;;;;;N;;;;;
+0912;DEVANAGARI LETTER SHORT O;Lo;0;L;;;;;N;;;;;
+0913;DEVANAGARI LETTER O;Lo;0;L;;;;;N;;;;;
+0914;DEVANAGARI LETTER AU;Lo;0;L;;;;;N;;;;;
+0915;DEVANAGARI LETTER KA;Lo;0;L;;;;;N;;;;;
+0916;DEVANAGARI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0917;DEVANAGARI LETTER GA;Lo;0;L;;;;;N;;;;;
+0918;DEVANAGARI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0919;DEVANAGARI LETTER NGA;Lo;0;L;;;;;N;;;;;
+091A;DEVANAGARI LETTER CA;Lo;0;L;;;;;N;;;;;
+091B;DEVANAGARI LETTER CHA;Lo;0;L;;;;;N;;;;;
+091C;DEVANAGARI LETTER JA;Lo;0;L;;;;;N;;;;;
+091D;DEVANAGARI LETTER JHA;Lo;0;L;;;;;N;;;;;
+091E;DEVANAGARI LETTER NYA;Lo;0;L;;;;;N;;;;;
+091F;DEVANAGARI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0920;DEVANAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0921;DEVANAGARI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0922;DEVANAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0923;DEVANAGARI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0924;DEVANAGARI LETTER TA;Lo;0;L;;;;;N;;;;;
+0925;DEVANAGARI LETTER THA;Lo;0;L;;;;;N;;;;;
+0926;DEVANAGARI LETTER DA;Lo;0;L;;;;;N;;;;;
+0927;DEVANAGARI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0928;DEVANAGARI LETTER NA;Lo;0;L;;;;;N;;;;;
+0929;DEVANAGARI LETTER NNNA;Lo;0;L;0928 093C;;;;N;;;;;
+092A;DEVANAGARI LETTER PA;Lo;0;L;;;;;N;;;;;
+092B;DEVANAGARI LETTER PHA;Lo;0;L;;;;;N;;;;;
+092C;DEVANAGARI LETTER BA;Lo;0;L;;;;;N;;;;;
+092D;DEVANAGARI LETTER BHA;Lo;0;L;;;;;N;;;;;
+092E;DEVANAGARI LETTER MA;Lo;0;L;;;;;N;;;;;
+092F;DEVANAGARI LETTER YA;Lo;0;L;;;;;N;;;;;
+0930;DEVANAGARI LETTER RA;Lo;0;L;;;;;N;;;;;
+0931;DEVANAGARI LETTER RRA;Lo;0;L;0930 093C;;;;N;;;;;
+0932;DEVANAGARI LETTER LA;Lo;0;L;;;;;N;;;;;
+0933;DEVANAGARI LETTER LLA;Lo;0;L;;;;;N;;;;;
+0934;DEVANAGARI LETTER LLLA;Lo;0;L;0933 093C;;;;N;;;;;
+0935;DEVANAGARI LETTER VA;Lo;0;L;;;;;N;;;;;
+0936;DEVANAGARI LETTER SHA;Lo;0;L;;;;;N;;;;;
+0937;DEVANAGARI LETTER SSA;Lo;0;L;;;;;N;;;;;
+0938;DEVANAGARI LETTER SA;Lo;0;L;;;;;N;;;;;
+0939;DEVANAGARI LETTER HA;Lo;0;L;;;;;N;;;;;
+093C;DEVANAGARI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+093D;DEVANAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+093E;DEVANAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+093F;DEVANAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0940;DEVANAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0941;DEVANAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0942;DEVANAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0943;DEVANAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0944;DEVANAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+0945;DEVANAGARI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;;
+0946;DEVANAGARI VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;;
+0947;DEVANAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0948;DEVANAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0949;DEVANAGARI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;;
+094A;DEVANAGARI VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;;
+094B;DEVANAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+094C;DEVANAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+094D;DEVANAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0950;DEVANAGARI OM;Lo;0;L;;;;;N;;;;;
+0951;DEVANAGARI STRESS SIGN UDATTA;Mn;230;NSM;;;;;N;;;;;
+0952;DEVANAGARI STRESS SIGN ANUDATTA;Mn;220;NSM;;;;;N;;;;;
+0953;DEVANAGARI GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;;
+0954;DEVANAGARI ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;;
+0958;DEVANAGARI LETTER QA;Lo;0;L;0915 093C;;;;N;;;;;
+0959;DEVANAGARI LETTER KHHA;Lo;0;L;0916 093C;;;;N;;;;;
+095A;DEVANAGARI LETTER GHHA;Lo;0;L;0917 093C;;;;N;;;;;
+095B;DEVANAGARI LETTER ZA;Lo;0;L;091C 093C;;;;N;;;;;
+095C;DEVANAGARI LETTER DDDHA;Lo;0;L;0921 093C;;;;N;;;;;
+095D;DEVANAGARI LETTER RHA;Lo;0;L;0922 093C;;;;N;;;;;
+095E;DEVANAGARI LETTER FA;Lo;0;L;092B 093C;;;;N;;;;;
+095F;DEVANAGARI LETTER YYA;Lo;0;L;092F 093C;;;;N;;;;;
+0960;DEVANAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0961;DEVANAGARI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0962;DEVANAGARI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+0963;DEVANAGARI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+0964;DEVANAGARI DANDA;Po;0;L;;;;;N;;;;;
+0965;DEVANAGARI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+0966;DEVANAGARI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0967;DEVANAGARI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0968;DEVANAGARI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0969;DEVANAGARI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+096A;DEVANAGARI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+096B;DEVANAGARI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+096C;DEVANAGARI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+096D;DEVANAGARI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+096E;DEVANAGARI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+096F;DEVANAGARI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0970;DEVANAGARI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+0981;BENGALI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0982;BENGALI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0983;BENGALI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0985;BENGALI LETTER A;Lo;0;L;;;;;N;;;;;
+0986;BENGALI LETTER AA;Lo;0;L;;;;;N;;;;;
+0987;BENGALI LETTER I;Lo;0;L;;;;;N;;;;;
+0988;BENGALI LETTER II;Lo;0;L;;;;;N;;;;;
+0989;BENGALI LETTER U;Lo;0;L;;;;;N;;;;;
+098A;BENGALI LETTER UU;Lo;0;L;;;;;N;;;;;
+098B;BENGALI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+098C;BENGALI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+098F;BENGALI LETTER E;Lo;0;L;;;;;N;;;;;
+0990;BENGALI LETTER AI;Lo;0;L;;;;;N;;;;;
+0993;BENGALI LETTER O;Lo;0;L;;;;;N;;;;;
+0994;BENGALI LETTER AU;Lo;0;L;;;;;N;;;;;
+0995;BENGALI LETTER KA;Lo;0;L;;;;;N;;;;;
+0996;BENGALI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0997;BENGALI LETTER GA;Lo;0;L;;;;;N;;;;;
+0998;BENGALI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0999;BENGALI LETTER NGA;Lo;0;L;;;;;N;;;;;
+099A;BENGALI LETTER CA;Lo;0;L;;;;;N;;;;;
+099B;BENGALI LETTER CHA;Lo;0;L;;;;;N;;;;;
+099C;BENGALI LETTER JA;Lo;0;L;;;;;N;;;;;
+099D;BENGALI LETTER JHA;Lo;0;L;;;;;N;;;;;
+099E;BENGALI LETTER NYA;Lo;0;L;;;;;N;;;;;
+099F;BENGALI LETTER TTA;Lo;0;L;;;;;N;;;;;
+09A0;BENGALI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+09A1;BENGALI LETTER DDA;Lo;0;L;;;;;N;;;;;
+09A2;BENGALI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+09A3;BENGALI LETTER NNA;Lo;0;L;;;;;N;;;;;
+09A4;BENGALI LETTER TA;Lo;0;L;;;;;N;;;;;
+09A5;BENGALI LETTER THA;Lo;0;L;;;;;N;;;;;
+09A6;BENGALI LETTER DA;Lo;0;L;;;;;N;;;;;
+09A7;BENGALI LETTER DHA;Lo;0;L;;;;;N;;;;;
+09A8;BENGALI LETTER NA;Lo;0;L;;;;;N;;;;;
+09AA;BENGALI LETTER PA;Lo;0;L;;;;;N;;;;;
+09AB;BENGALI LETTER PHA;Lo;0;L;;;;;N;;;;;
+09AC;BENGALI LETTER BA;Lo;0;L;;;;;N;;;;;
+09AD;BENGALI LETTER BHA;Lo;0;L;;;;;N;;;;;
+09AE;BENGALI LETTER MA;Lo;0;L;;;;;N;;;;;
+09AF;BENGALI LETTER YA;Lo;0;L;;;;;N;;;;;
+09B0;BENGALI LETTER RA;Lo;0;L;;;;;N;;;;;
+09B2;BENGALI LETTER LA;Lo;0;L;;;;;N;;;;;
+09B6;BENGALI LETTER SHA;Lo;0;L;;;;;N;;;;;
+09B7;BENGALI LETTER SSA;Lo;0;L;;;;;N;;;;;
+09B8;BENGALI LETTER SA;Lo;0;L;;;;;N;;;;;
+09B9;BENGALI LETTER HA;Lo;0;L;;;;;N;;;;;
+09BC;BENGALI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+09BE;BENGALI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+09BF;BENGALI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+09C0;BENGALI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+09C1;BENGALI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+09C2;BENGALI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+09C3;BENGALI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+09C4;BENGALI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+09C7;BENGALI VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+09C8;BENGALI VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+09CB;BENGALI VOWEL SIGN O;Mc;0;L;09C7 09BE;;;;N;;;;;
+09CC;BENGALI VOWEL SIGN AU;Mc;0;L;09C7 09D7;;;;N;;;;;
+09CD;BENGALI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+09D7;BENGALI AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+09DC;BENGALI LETTER RRA;Lo;0;L;09A1 09BC;;;;N;;;;;
+09DD;BENGALI LETTER RHA;Lo;0;L;09A2 09BC;;;;N;;;;;
+09DF;BENGALI LETTER YYA;Lo;0;L;09AF 09BC;;;;N;;;;;
+09E0;BENGALI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+09E1;BENGALI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+09E2;BENGALI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+09E3;BENGALI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+09E6;BENGALI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+09E7;BENGALI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+09E8;BENGALI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+09E9;BENGALI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+09EA;BENGALI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+09EB;BENGALI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+09EC;BENGALI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+09ED;BENGALI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+09EE;BENGALI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+09EF;BENGALI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+09F0;BENGALI LETTER RA WITH MIDDLE DIAGONAL;Lo;0;L;;;;;N;;Assamese;;;
+09F1;BENGALI LETTER RA WITH LOWER DIAGONAL;Lo;0;L;;;;;N;BENGALI LETTER VA WITH LOWER DIAGONAL;Assamese;;;
+09F2;BENGALI RUPEE MARK;Sc;0;ET;;;;;N;;;;;
+09F3;BENGALI RUPEE SIGN;Sc;0;ET;;;;;N;;;;;
+09F4;BENGALI CURRENCY NUMERATOR ONE;No;0;L;;;;1;N;;;;;
+09F5;BENGALI CURRENCY NUMERATOR TWO;No;0;L;;;;2;N;;;;;
+09F6;BENGALI CURRENCY NUMERATOR THREE;No;0;L;;;;3;N;;;;;
+09F7;BENGALI CURRENCY NUMERATOR FOUR;No;0;L;;;;4;N;;;;;
+09F8;BENGALI CURRENCY NUMERATOR ONE LESS THAN THE DENOMINATOR;No;0;L;;;;;N;;;;;
+09F9;BENGALI CURRENCY DENOMINATOR SIXTEEN;No;0;L;;;;16;N;;;;;
+09FA;BENGALI ISSHAR;So;0;L;;;;;N;;;;;
+0A02;GURMUKHI SIGN BINDI;Mn;0;NSM;;;;;N;;;;;
+0A05;GURMUKHI LETTER A;Lo;0;L;;;;;N;;;;;
+0A06;GURMUKHI LETTER AA;Lo;0;L;;;;;N;;;;;
+0A07;GURMUKHI LETTER I;Lo;0;L;;;;;N;;;;;
+0A08;GURMUKHI LETTER II;Lo;0;L;;;;;N;;;;;
+0A09;GURMUKHI LETTER U;Lo;0;L;;;;;N;;;;;
+0A0A;GURMUKHI LETTER UU;Lo;0;L;;;;;N;;;;;
+0A0F;GURMUKHI LETTER EE;Lo;0;L;;;;;N;;;;;
+0A10;GURMUKHI LETTER AI;Lo;0;L;;;;;N;;;;;
+0A13;GURMUKHI LETTER OO;Lo;0;L;;;;;N;;;;;
+0A14;GURMUKHI LETTER AU;Lo;0;L;;;;;N;;;;;
+0A15;GURMUKHI LETTER KA;Lo;0;L;;;;;N;;;;;
+0A16;GURMUKHI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0A17;GURMUKHI LETTER GA;Lo;0;L;;;;;N;;;;;
+0A18;GURMUKHI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0A19;GURMUKHI LETTER NGA;Lo;0;L;;;;;N;;;;;
+0A1A;GURMUKHI LETTER CA;Lo;0;L;;;;;N;;;;;
+0A1B;GURMUKHI LETTER CHA;Lo;0;L;;;;;N;;;;;
+0A1C;GURMUKHI LETTER JA;Lo;0;L;;;;;N;;;;;
+0A1D;GURMUKHI LETTER JHA;Lo;0;L;;;;;N;;;;;
+0A1E;GURMUKHI LETTER NYA;Lo;0;L;;;;;N;;;;;
+0A1F;GURMUKHI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0A20;GURMUKHI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0A21;GURMUKHI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0A22;GURMUKHI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0A23;GURMUKHI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0A24;GURMUKHI LETTER TA;Lo;0;L;;;;;N;;;;;
+0A25;GURMUKHI LETTER THA;Lo;0;L;;;;;N;;;;;
+0A26;GURMUKHI LETTER DA;Lo;0;L;;;;;N;;;;;
+0A27;GURMUKHI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0A28;GURMUKHI LETTER NA;Lo;0;L;;;;;N;;;;;
+0A2A;GURMUKHI LETTER PA;Lo;0;L;;;;;N;;;;;
+0A2B;GURMUKHI LETTER PHA;Lo;0;L;;;;;N;;;;;
+0A2C;GURMUKHI LETTER BA;Lo;0;L;;;;;N;;;;;
+0A2D;GURMUKHI LETTER BHA;Lo;0;L;;;;;N;;;;;
+0A2E;GURMUKHI LETTER MA;Lo;0;L;;;;;N;;;;;
+0A2F;GURMUKHI LETTER YA;Lo;0;L;;;;;N;;;;;
+0A30;GURMUKHI LETTER RA;Lo;0;L;;;;;N;;;;;
+0A32;GURMUKHI LETTER LA;Lo;0;L;;;;;N;;;;;
+0A33;GURMUKHI LETTER LLA;Lo;0;L;0A32 0A3C;;;;N;;;;;
+0A35;GURMUKHI LETTER VA;Lo;0;L;;;;;N;;;;;
+0A36;GURMUKHI LETTER SHA;Lo;0;L;0A38 0A3C;;;;N;;;;;
+0A38;GURMUKHI LETTER SA;Lo;0;L;;;;;N;;;;;
+0A39;GURMUKHI LETTER HA;Lo;0;L;;;;;N;;;;;
+0A3C;GURMUKHI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0A3E;GURMUKHI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0A3F;GURMUKHI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0A40;GURMUKHI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0A41;GURMUKHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0A42;GURMUKHI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0A47;GURMUKHI VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;;
+0A48;GURMUKHI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0A4B;GURMUKHI VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;;
+0A4C;GURMUKHI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0A4D;GURMUKHI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0A59;GURMUKHI LETTER KHHA;Lo;0;L;0A16 0A3C;;;;N;;;;;
+0A5A;GURMUKHI LETTER GHHA;Lo;0;L;0A17 0A3C;;;;N;;;;;
+0A5B;GURMUKHI LETTER ZA;Lo;0;L;0A1C 0A3C;;;;N;;;;;
+0A5C;GURMUKHI LETTER RRA;Lo;0;L;;;;;N;;;;;
+0A5E;GURMUKHI LETTER FA;Lo;0;L;0A2B 0A3C;;;;N;;;;;
+0A66;GURMUKHI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0A67;GURMUKHI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0A68;GURMUKHI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0A69;GURMUKHI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0A6A;GURMUKHI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0A6B;GURMUKHI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0A6C;GURMUKHI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0A6D;GURMUKHI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0A6E;GURMUKHI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0A6F;GURMUKHI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0A70;GURMUKHI TIPPI;Mn;0;NSM;;;;;N;;;;;
+0A71;GURMUKHI ADDAK;Mn;0;NSM;;;;;N;;;;;
+0A72;GURMUKHI IRI;Lo;0;L;;;;;N;;;;;
+0A73;GURMUKHI URA;Lo;0;L;;;;;N;;;;;
+0A74;GURMUKHI EK ONKAR;Lo;0;L;;;;;N;;;;;
+0A81;GUJARATI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0A82;GUJARATI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0A83;GUJARATI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0A85;GUJARATI LETTER A;Lo;0;L;;;;;N;;;;;
+0A86;GUJARATI LETTER AA;Lo;0;L;;;;;N;;;;;
+0A87;GUJARATI LETTER I;Lo;0;L;;;;;N;;;;;
+0A88;GUJARATI LETTER II;Lo;0;L;;;;;N;;;;;
+0A89;GUJARATI LETTER U;Lo;0;L;;;;;N;;;;;
+0A8A;GUJARATI LETTER UU;Lo;0;L;;;;;N;;;;;
+0A8B;GUJARATI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0A8D;GUJARATI VOWEL CANDRA E;Lo;0;L;;;;;N;;;;;
+0A8F;GUJARATI LETTER E;Lo;0;L;;;;;N;;;;;
+0A90;GUJARATI LETTER AI;Lo;0;L;;;;;N;;;;;
+0A91;GUJARATI VOWEL CANDRA O;Lo;0;L;;;;;N;;;;;
+0A93;GUJARATI LETTER O;Lo;0;L;;;;;N;;;;;
+0A94;GUJARATI LETTER AU;Lo;0;L;;;;;N;;;;;
+0A95;GUJARATI LETTER KA;Lo;0;L;;;;;N;;;;;
+0A96;GUJARATI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0A97;GUJARATI LETTER GA;Lo;0;L;;;;;N;;;;;
+0A98;GUJARATI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0A99;GUJARATI LETTER NGA;Lo;0;L;;;;;N;;;;;
+0A9A;GUJARATI LETTER CA;Lo;0;L;;;;;N;;;;;
+0A9B;GUJARATI LETTER CHA;Lo;0;L;;;;;N;;;;;
+0A9C;GUJARATI LETTER JA;Lo;0;L;;;;;N;;;;;
+0A9D;GUJARATI LETTER JHA;Lo;0;L;;;;;N;;;;;
+0A9E;GUJARATI LETTER NYA;Lo;0;L;;;;;N;;;;;
+0A9F;GUJARATI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0AA0;GUJARATI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0AA1;GUJARATI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0AA2;GUJARATI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0AA3;GUJARATI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0AA4;GUJARATI LETTER TA;Lo;0;L;;;;;N;;;;;
+0AA5;GUJARATI LETTER THA;Lo;0;L;;;;;N;;;;;
+0AA6;GUJARATI LETTER DA;Lo;0;L;;;;;N;;;;;
+0AA7;GUJARATI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0AA8;GUJARATI LETTER NA;Lo;0;L;;;;;N;;;;;
+0AAA;GUJARATI LETTER PA;Lo;0;L;;;;;N;;;;;
+0AAB;GUJARATI LETTER PHA;Lo;0;L;;;;;N;;;;;
+0AAC;GUJARATI LETTER BA;Lo;0;L;;;;;N;;;;;
+0AAD;GUJARATI LETTER BHA;Lo;0;L;;;;;N;;;;;
+0AAE;GUJARATI LETTER MA;Lo;0;L;;;;;N;;;;;
+0AAF;GUJARATI LETTER YA;Lo;0;L;;;;;N;;;;;
+0AB0;GUJARATI LETTER RA;Lo;0;L;;;;;N;;;;;
+0AB2;GUJARATI LETTER LA;Lo;0;L;;;;;N;;;;;
+0AB3;GUJARATI LETTER LLA;Lo;0;L;;;;;N;;;;;
+0AB5;GUJARATI LETTER VA;Lo;0;L;;;;;N;;;;;
+0AB6;GUJARATI LETTER SHA;Lo;0;L;;;;;N;;;;;
+0AB7;GUJARATI LETTER SSA;Lo;0;L;;;;;N;;;;;
+0AB8;GUJARATI LETTER SA;Lo;0;L;;;;;N;;;;;
+0AB9;GUJARATI LETTER HA;Lo;0;L;;;;;N;;;;;
+0ABC;GUJARATI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0ABD;GUJARATI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0ABE;GUJARATI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0ABF;GUJARATI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0AC0;GUJARATI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0AC1;GUJARATI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0AC2;GUJARATI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0AC3;GUJARATI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0AC4;GUJARATI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+0AC5;GUJARATI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;;
+0AC7;GUJARATI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0AC8;GUJARATI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0AC9;GUJARATI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;;
+0ACB;GUJARATI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+0ACC;GUJARATI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+0ACD;GUJARATI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0AD0;GUJARATI OM;Lo;0;L;;;;;N;;;;;
+0AE0;GUJARATI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0AE6;GUJARATI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0AE7;GUJARATI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0AE8;GUJARATI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0AE9;GUJARATI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0AEA;GUJARATI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0AEB;GUJARATI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0AEC;GUJARATI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0AED;GUJARATI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0AEE;GUJARATI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0AEF;GUJARATI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0B01;ORIYA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0B02;ORIYA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0B03;ORIYA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0B05;ORIYA LETTER A;Lo;0;L;;;;;N;;;;;
+0B06;ORIYA LETTER AA;Lo;0;L;;;;;N;;;;;
+0B07;ORIYA LETTER I;Lo;0;L;;;;;N;;;;;
+0B08;ORIYA LETTER II;Lo;0;L;;;;;N;;;;;
+0B09;ORIYA LETTER U;Lo;0;L;;;;;N;;;;;
+0B0A;ORIYA LETTER UU;Lo;0;L;;;;;N;;;;;
+0B0B;ORIYA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0B0C;ORIYA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0B0F;ORIYA LETTER E;Lo;0;L;;;;;N;;;;;
+0B10;ORIYA LETTER AI;Lo;0;L;;;;;N;;;;;
+0B13;ORIYA LETTER O;Lo;0;L;;;;;N;;;;;
+0B14;ORIYA LETTER AU;Lo;0;L;;;;;N;;;;;
+0B15;ORIYA LETTER KA;Lo;0;L;;;;;N;;;;;
+0B16;ORIYA LETTER KHA;Lo;0;L;;;;;N;;;;;
+0B17;ORIYA LETTER GA;Lo;0;L;;;;;N;;;;;
+0B18;ORIYA LETTER GHA;Lo;0;L;;;;;N;;;;;
+0B19;ORIYA LETTER NGA;Lo;0;L;;;;;N;;;;;
+0B1A;ORIYA LETTER CA;Lo;0;L;;;;;N;;;;;
+0B1B;ORIYA LETTER CHA;Lo;0;L;;;;;N;;;;;
+0B1C;ORIYA LETTER JA;Lo;0;L;;;;;N;;;;;
+0B1D;ORIYA LETTER JHA;Lo;0;L;;;;;N;;;;;
+0B1E;ORIYA LETTER NYA;Lo;0;L;;;;;N;;;;;
+0B1F;ORIYA LETTER TTA;Lo;0;L;;;;;N;;;;;
+0B20;ORIYA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0B21;ORIYA LETTER DDA;Lo;0;L;;;;;N;;;;;
+0B22;ORIYA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0B23;ORIYA LETTER NNA;Lo;0;L;;;;;N;;;;;
+0B24;ORIYA LETTER TA;Lo;0;L;;;;;N;;;;;
+0B25;ORIYA LETTER THA;Lo;0;L;;;;;N;;;;;
+0B26;ORIYA LETTER DA;Lo;0;L;;;;;N;;;;;
+0B27;ORIYA LETTER DHA;Lo;0;L;;;;;N;;;;;
+0B28;ORIYA LETTER NA;Lo;0;L;;;;;N;;;;;
+0B2A;ORIYA LETTER PA;Lo;0;L;;;;;N;;;;;
+0B2B;ORIYA LETTER PHA;Lo;0;L;;;;;N;;;;;
+0B2C;ORIYA LETTER BA;Lo;0;L;;;;;N;;;;;
+0B2D;ORIYA LETTER BHA;Lo;0;L;;;;;N;;;;;
+0B2E;ORIYA LETTER MA;Lo;0;L;;;;;N;;;;;
+0B2F;ORIYA LETTER YA;Lo;0;L;;;;;N;;;;;
+0B30;ORIYA LETTER RA;Lo;0;L;;;;;N;;;;;
+0B32;ORIYA LETTER LA;Lo;0;L;;;;;N;;;;;
+0B33;ORIYA LETTER LLA;Lo;0;L;;;;;N;;;;;
+0B36;ORIYA LETTER SHA;Lo;0;L;;;;;N;;;;;
+0B37;ORIYA LETTER SSA;Lo;0;L;;;;;N;;;;;
+0B38;ORIYA LETTER SA;Lo;0;L;;;;;N;;;;;
+0B39;ORIYA LETTER HA;Lo;0;L;;;;;N;;;;;
+0B3C;ORIYA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0B3D;ORIYA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0B3E;ORIYA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0B3F;ORIYA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0B40;ORIYA VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0B41;ORIYA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0B42;ORIYA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0B43;ORIYA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0B47;ORIYA VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0B48;ORIYA VOWEL SIGN AI;Mc;0;L;0B47 0B56;;;;N;;;;;
+0B4B;ORIYA VOWEL SIGN O;Mc;0;L;0B47 0B3E;;;;N;;;;;
+0B4C;ORIYA VOWEL SIGN AU;Mc;0;L;0B47 0B57;;;;N;;;;;
+0B4D;ORIYA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0B56;ORIYA AI LENGTH MARK;Mn;0;NSM;;;;;N;;;;;
+0B57;ORIYA AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0B5C;ORIYA LETTER RRA;Lo;0;L;0B21 0B3C;;;;N;;;;;
+0B5D;ORIYA LETTER RHA;Lo;0;L;0B22 0B3C;;;;N;;;;;
+0B5F;ORIYA LETTER YYA;Lo;0;L;;;;;N;;;;;
+0B60;ORIYA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0B61;ORIYA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0B66;ORIYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0B67;ORIYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0B68;ORIYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0B69;ORIYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0B6A;ORIYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0B6B;ORIYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0B6C;ORIYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0B6D;ORIYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0B6E;ORIYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0B6F;ORIYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0B70;ORIYA ISSHAR;So;0;L;;;;;N;;;;;
+0B82;TAMIL SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0B83;TAMIL SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0B85;TAMIL LETTER A;Lo;0;L;;;;;N;;;;;
+0B86;TAMIL LETTER AA;Lo;0;L;;;;;N;;;;;
+0B87;TAMIL LETTER I;Lo;0;L;;;;;N;;;;;
+0B88;TAMIL LETTER II;Lo;0;L;;;;;N;;;;;
+0B89;TAMIL LETTER U;Lo;0;L;;;;;N;;;;;
+0B8A;TAMIL LETTER UU;Lo;0;L;;;;;N;;;;;
+0B8E;TAMIL LETTER E;Lo;0;L;;;;;N;;;;;
+0B8F;TAMIL LETTER EE;Lo;0;L;;;;;N;;;;;
+0B90;TAMIL LETTER AI;Lo;0;L;;;;;N;;;;;
+0B92;TAMIL LETTER O;Lo;0;L;;;;;N;;;;;
+0B93;TAMIL LETTER OO;Lo;0;L;;;;;N;;;;;
+0B94;TAMIL LETTER AU;Lo;0;L;0B92 0BD7;;;;N;;;;;
+0B95;TAMIL LETTER KA;Lo;0;L;;;;;N;;;;;
+0B99;TAMIL LETTER NGA;Lo;0;L;;;;;N;;;;;
+0B9A;TAMIL LETTER CA;Lo;0;L;;;;;N;;;;;
+0B9C;TAMIL LETTER JA;Lo;0;L;;;;;N;;;;;
+0B9E;TAMIL LETTER NYA;Lo;0;L;;;;;N;;;;;
+0B9F;TAMIL LETTER TTA;Lo;0;L;;;;;N;;;;;
+0BA3;TAMIL LETTER NNA;Lo;0;L;;;;;N;;;;;
+0BA4;TAMIL LETTER TA;Lo;0;L;;;;;N;;;;;
+0BA8;TAMIL LETTER NA;Lo;0;L;;;;;N;;;;;
+0BA9;TAMIL LETTER NNNA;Lo;0;L;;;;;N;;;;;
+0BAA;TAMIL LETTER PA;Lo;0;L;;;;;N;;;;;
+0BAE;TAMIL LETTER MA;Lo;0;L;;;;;N;;;;;
+0BAF;TAMIL LETTER YA;Lo;0;L;;;;;N;;;;;
+0BB0;TAMIL LETTER RA;Lo;0;L;;;;;N;;;;;
+0BB1;TAMIL LETTER RRA;Lo;0;L;;;;;N;;;;;
+0BB2;TAMIL LETTER LA;Lo;0;L;;;;;N;;;;;
+0BB3;TAMIL LETTER LLA;Lo;0;L;;;;;N;;;;;
+0BB4;TAMIL LETTER LLLA;Lo;0;L;;;;;N;;;;;
+0BB5;TAMIL LETTER VA;Lo;0;L;;;;;N;;;;;
+0BB7;TAMIL LETTER SSA;Lo;0;L;;;;;N;;;;;
+0BB8;TAMIL LETTER SA;Lo;0;L;;;;;N;;;;;
+0BB9;TAMIL LETTER HA;Lo;0;L;;;;;N;;;;;
+0BBE;TAMIL VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0BBF;TAMIL VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0BC0;TAMIL VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0BC1;TAMIL VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0BC2;TAMIL VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0BC6;TAMIL VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0BC7;TAMIL VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+0BC8;TAMIL VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+0BCA;TAMIL VOWEL SIGN O;Mc;0;L;0BC6 0BBE;;;;N;;;;;
+0BCB;TAMIL VOWEL SIGN OO;Mc;0;L;0BC7 0BBE;;;;N;;;;;
+0BCC;TAMIL VOWEL SIGN AU;Mc;0;L;0BC6 0BD7;;;;N;;;;;
+0BCD;TAMIL SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0BD7;TAMIL AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0BE7;TAMIL DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0BE8;TAMIL DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0BE9;TAMIL DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0BEA;TAMIL DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0BEB;TAMIL DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0BEC;TAMIL DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0BED;TAMIL DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0BEE;TAMIL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0BEF;TAMIL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0BF0;TAMIL NUMBER TEN;No;0;L;;;;10;N;;;;;
+0BF1;TAMIL NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;;
+0BF2;TAMIL NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;;
+0C01;TELUGU SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;;
+0C02;TELUGU SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0C03;TELUGU SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0C05;TELUGU LETTER A;Lo;0;L;;;;;N;;;;;
+0C06;TELUGU LETTER AA;Lo;0;L;;;;;N;;;;;
+0C07;TELUGU LETTER I;Lo;0;L;;;;;N;;;;;
+0C08;TELUGU LETTER II;Lo;0;L;;;;;N;;;;;
+0C09;TELUGU LETTER U;Lo;0;L;;;;;N;;;;;
+0C0A;TELUGU LETTER UU;Lo;0;L;;;;;N;;;;;
+0C0B;TELUGU LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0C0C;TELUGU LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0C0E;TELUGU LETTER E;Lo;0;L;;;;;N;;;;;
+0C0F;TELUGU LETTER EE;Lo;0;L;;;;;N;;;;;
+0C10;TELUGU LETTER AI;Lo;0;L;;;;;N;;;;;
+0C12;TELUGU LETTER O;Lo;0;L;;;;;N;;;;;
+0C13;TELUGU LETTER OO;Lo;0;L;;;;;N;;;;;
+0C14;TELUGU LETTER AU;Lo;0;L;;;;;N;;;;;
+0C15;TELUGU LETTER KA;Lo;0;L;;;;;N;;;;;
+0C16;TELUGU LETTER KHA;Lo;0;L;;;;;N;;;;;
+0C17;TELUGU LETTER GA;Lo;0;L;;;;;N;;;;;
+0C18;TELUGU LETTER GHA;Lo;0;L;;;;;N;;;;;
+0C19;TELUGU LETTER NGA;Lo;0;L;;;;;N;;;;;
+0C1A;TELUGU LETTER CA;Lo;0;L;;;;;N;;;;;
+0C1B;TELUGU LETTER CHA;Lo;0;L;;;;;N;;;;;
+0C1C;TELUGU LETTER JA;Lo;0;L;;;;;N;;;;;
+0C1D;TELUGU LETTER JHA;Lo;0;L;;;;;N;;;;;
+0C1E;TELUGU LETTER NYA;Lo;0;L;;;;;N;;;;;
+0C1F;TELUGU LETTER TTA;Lo;0;L;;;;;N;;;;;
+0C20;TELUGU LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0C21;TELUGU LETTER DDA;Lo;0;L;;;;;N;;;;;
+0C22;TELUGU LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0C23;TELUGU LETTER NNA;Lo;0;L;;;;;N;;;;;
+0C24;TELUGU LETTER TA;Lo;0;L;;;;;N;;;;;
+0C25;TELUGU LETTER THA;Lo;0;L;;;;;N;;;;;
+0C26;TELUGU LETTER DA;Lo;0;L;;;;;N;;;;;
+0C27;TELUGU LETTER DHA;Lo;0;L;;;;;N;;;;;
+0C28;TELUGU LETTER NA;Lo;0;L;;;;;N;;;;;
+0C2A;TELUGU LETTER PA;Lo;0;L;;;;;N;;;;;
+0C2B;TELUGU LETTER PHA;Lo;0;L;;;;;N;;;;;
+0C2C;TELUGU LETTER BA;Lo;0;L;;;;;N;;;;;
+0C2D;TELUGU LETTER BHA;Lo;0;L;;;;;N;;;;;
+0C2E;TELUGU LETTER MA;Lo;0;L;;;;;N;;;;;
+0C2F;TELUGU LETTER YA;Lo;0;L;;;;;N;;;;;
+0C30;TELUGU LETTER RA;Lo;0;L;;;;;N;;;;;
+0C31;TELUGU LETTER RRA;Lo;0;L;;;;;N;;;;;
+0C32;TELUGU LETTER LA;Lo;0;L;;;;;N;;;;;
+0C33;TELUGU LETTER LLA;Lo;0;L;;;;;N;;;;;
+0C35;TELUGU LETTER VA;Lo;0;L;;;;;N;;;;;
+0C36;TELUGU LETTER SHA;Lo;0;L;;;;;N;;;;;
+0C37;TELUGU LETTER SSA;Lo;0;L;;;;;N;;;;;
+0C38;TELUGU LETTER SA;Lo;0;L;;;;;N;;;;;
+0C39;TELUGU LETTER HA;Lo;0;L;;;;;N;;;;;
+0C3E;TELUGU VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
+0C3F;TELUGU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0C40;TELUGU VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0C41;TELUGU VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0C42;TELUGU VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0C43;TELUGU VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+0C44;TELUGU VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+0C46;TELUGU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0C47;TELUGU VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;;
+0C48;TELUGU VOWEL SIGN AI;Mn;0;NSM;0C46 0C56;;;;N;;;;;
+0C4A;TELUGU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+0C4B;TELUGU VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;;
+0C4C;TELUGU VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0C4D;TELUGU SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0C55;TELUGU LENGTH MARK;Mn;84;NSM;;;;;N;;;;;
+0C56;TELUGU AI LENGTH MARK;Mn;91;NSM;;;;;N;;;;;
+0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0C66;TELUGU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0C67;TELUGU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0C68;TELUGU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0C69;TELUGU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0C6A;TELUGU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0C6B;TELUGU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0C6C;TELUGU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0C82;KANNADA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0C83;KANNADA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0C85;KANNADA LETTER A;Lo;0;L;;;;;N;;;;;
+0C86;KANNADA LETTER AA;Lo;0;L;;;;;N;;;;;
+0C87;KANNADA LETTER I;Lo;0;L;;;;;N;;;;;
+0C88;KANNADA LETTER II;Lo;0;L;;;;;N;;;;;
+0C89;KANNADA LETTER U;Lo;0;L;;;;;N;;;;;
+0C8A;KANNADA LETTER UU;Lo;0;L;;;;;N;;;;;
+0C8B;KANNADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0C8C;KANNADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0C8E;KANNADA LETTER E;Lo;0;L;;;;;N;;;;;
+0C8F;KANNADA LETTER EE;Lo;0;L;;;;;N;;;;;
+0C90;KANNADA LETTER AI;Lo;0;L;;;;;N;;;;;
+0C92;KANNADA LETTER O;Lo;0;L;;;;;N;;;;;
+0C93;KANNADA LETTER OO;Lo;0;L;;;;;N;;;;;
+0C94;KANNADA LETTER AU;Lo;0;L;;;;;N;;;;;
+0C95;KANNADA LETTER KA;Lo;0;L;;;;;N;;;;;
+0C96;KANNADA LETTER KHA;Lo;0;L;;;;;N;;;;;
+0C97;KANNADA LETTER GA;Lo;0;L;;;;;N;;;;;
+0C98;KANNADA LETTER GHA;Lo;0;L;;;;;N;;;;;
+0C99;KANNADA LETTER NGA;Lo;0;L;;;;;N;;;;;
+0C9A;KANNADA LETTER CA;Lo;0;L;;;;;N;;;;;
+0C9B;KANNADA LETTER CHA;Lo;0;L;;;;;N;;;;;
+0C9C;KANNADA LETTER JA;Lo;0;L;;;;;N;;;;;
+0C9D;KANNADA LETTER JHA;Lo;0;L;;;;;N;;;;;
+0C9E;KANNADA LETTER NYA;Lo;0;L;;;;;N;;;;;
+0C9F;KANNADA LETTER TTA;Lo;0;L;;;;;N;;;;;
+0CA0;KANNADA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0CA1;KANNADA LETTER DDA;Lo;0;L;;;;;N;;;;;
+0CA2;KANNADA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0CA3;KANNADA LETTER NNA;Lo;0;L;;;;;N;;;;;
+0CA4;KANNADA LETTER TA;Lo;0;L;;;;;N;;;;;
+0CA5;KANNADA LETTER THA;Lo;0;L;;;;;N;;;;;
+0CA6;KANNADA LETTER DA;Lo;0;L;;;;;N;;;;;
+0CA7;KANNADA LETTER DHA;Lo;0;L;;;;;N;;;;;
+0CA8;KANNADA LETTER NA;Lo;0;L;;;;;N;;;;;
+0CAA;KANNADA LETTER PA;Lo;0;L;;;;;N;;;;;
+0CAB;KANNADA LETTER PHA;Lo;0;L;;;;;N;;;;;
+0CAC;KANNADA LETTER BA;Lo;0;L;;;;;N;;;;;
+0CAD;KANNADA LETTER BHA;Lo;0;L;;;;;N;;;;;
+0CAE;KANNADA LETTER MA;Lo;0;L;;;;;N;;;;;
+0CAF;KANNADA LETTER YA;Lo;0;L;;;;;N;;;;;
+0CB0;KANNADA LETTER RA;Lo;0;L;;;;;N;;;;;
+0CB1;KANNADA LETTER RRA;Lo;0;L;;;;;N;;;;;
+0CB2;KANNADA LETTER LA;Lo;0;L;;;;;N;;;;;
+0CB3;KANNADA LETTER LLA;Lo;0;L;;;;;N;;;;;
+0CB5;KANNADA LETTER VA;Lo;0;L;;;;;N;;;;;
+0CB6;KANNADA LETTER SHA;Lo;0;L;;;;;N;;;;;
+0CB7;KANNADA LETTER SSA;Lo;0;L;;;;;N;;;;;
+0CB8;KANNADA LETTER SA;Lo;0;L;;;;;N;;;;;
+0CB9;KANNADA LETTER HA;Lo;0;L;;;;;N;;;;;
+0CBE;KANNADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0CBF;KANNADA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0CC0;KANNADA VOWEL SIGN II;Mc;0;L;0CBF 0CD5;;;;N;;;;;
+0CC1;KANNADA VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0CC2;KANNADA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0CC3;KANNADA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+0CC4;KANNADA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+0CC6;KANNADA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0CC7;KANNADA VOWEL SIGN EE;Mc;0;L;0CC6 0CD5;;;;N;;;;;
+0CC8;KANNADA VOWEL SIGN AI;Mc;0;L;0CC6 0CD6;;;;N;;;;;
+0CCA;KANNADA VOWEL SIGN O;Mc;0;L;0CC6 0CC2;;;;N;;;;;
+0CCB;KANNADA VOWEL SIGN OO;Mc;0;L;0CCA 0CD5;;;;N;;;;;
+0CCC;KANNADA VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;;
+0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0CE1;KANNADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0CE6;KANNADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0CE7;KANNADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0CE8;KANNADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0CE9;KANNADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0CEA;KANNADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0CEB;KANNADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0CEC;KANNADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0CED;KANNADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0CEE;KANNADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0CEF;KANNADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0D02;MALAYALAM SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0D03;MALAYALAM SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0D05;MALAYALAM LETTER A;Lo;0;L;;;;;N;;;;;
+0D06;MALAYALAM LETTER AA;Lo;0;L;;;;;N;;;;;
+0D07;MALAYALAM LETTER I;Lo;0;L;;;;;N;;;;;
+0D08;MALAYALAM LETTER II;Lo;0;L;;;;;N;;;;;
+0D09;MALAYALAM LETTER U;Lo;0;L;;;;;N;;;;;
+0D0A;MALAYALAM LETTER UU;Lo;0;L;;;;;N;;;;;
+0D0B;MALAYALAM LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0D0C;MALAYALAM LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0D0E;MALAYALAM LETTER E;Lo;0;L;;;;;N;;;;;
+0D0F;MALAYALAM LETTER EE;Lo;0;L;;;;;N;;;;;
+0D10;MALAYALAM LETTER AI;Lo;0;L;;;;;N;;;;;
+0D12;MALAYALAM LETTER O;Lo;0;L;;;;;N;;;;;
+0D13;MALAYALAM LETTER OO;Lo;0;L;;;;;N;;;;;
+0D14;MALAYALAM LETTER AU;Lo;0;L;;;;;N;;;;;
+0D15;MALAYALAM LETTER KA;Lo;0;L;;;;;N;;;;;
+0D16;MALAYALAM LETTER KHA;Lo;0;L;;;;;N;;;;;
+0D17;MALAYALAM LETTER GA;Lo;0;L;;;;;N;;;;;
+0D18;MALAYALAM LETTER GHA;Lo;0;L;;;;;N;;;;;
+0D19;MALAYALAM LETTER NGA;Lo;0;L;;;;;N;;;;;
+0D1A;MALAYALAM LETTER CA;Lo;0;L;;;;;N;;;;;
+0D1B;MALAYALAM LETTER CHA;Lo;0;L;;;;;N;;;;;
+0D1C;MALAYALAM LETTER JA;Lo;0;L;;;;;N;;;;;
+0D1D;MALAYALAM LETTER JHA;Lo;0;L;;;;;N;;;;;
+0D1E;MALAYALAM LETTER NYA;Lo;0;L;;;;;N;;;;;
+0D1F;MALAYALAM LETTER TTA;Lo;0;L;;;;;N;;;;;
+0D20;MALAYALAM LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0D21;MALAYALAM LETTER DDA;Lo;0;L;;;;;N;;;;;
+0D22;MALAYALAM LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0D23;MALAYALAM LETTER NNA;Lo;0;L;;;;;N;;;;;
+0D24;MALAYALAM LETTER TA;Lo;0;L;;;;;N;;;;;
+0D25;MALAYALAM LETTER THA;Lo;0;L;;;;;N;;;;;
+0D26;MALAYALAM LETTER DA;Lo;0;L;;;;;N;;;;;
+0D27;MALAYALAM LETTER DHA;Lo;0;L;;;;;N;;;;;
+0D28;MALAYALAM LETTER NA;Lo;0;L;;;;;N;;;;;
+0D2A;MALAYALAM LETTER PA;Lo;0;L;;;;;N;;;;;
+0D2B;MALAYALAM LETTER PHA;Lo;0;L;;;;;N;;;;;
+0D2C;MALAYALAM LETTER BA;Lo;0;L;;;;;N;;;;;
+0D2D;MALAYALAM LETTER BHA;Lo;0;L;;;;;N;;;;;
+0D2E;MALAYALAM LETTER MA;Lo;0;L;;;;;N;;;;;
+0D2F;MALAYALAM LETTER YA;Lo;0;L;;;;;N;;;;;
+0D30;MALAYALAM LETTER RA;Lo;0;L;;;;;N;;;;;
+0D31;MALAYALAM LETTER RRA;Lo;0;L;;;;;N;;;;;
+0D32;MALAYALAM LETTER LA;Lo;0;L;;;;;N;;;;;
+0D33;MALAYALAM LETTER LLA;Lo;0;L;;;;;N;;;;;
+0D34;MALAYALAM LETTER LLLA;Lo;0;L;;;;;N;;;;;
+0D35;MALAYALAM LETTER VA;Lo;0;L;;;;;N;;;;;
+0D36;MALAYALAM LETTER SHA;Lo;0;L;;;;;N;;;;;
+0D37;MALAYALAM LETTER SSA;Lo;0;L;;;;;N;;;;;
+0D38;MALAYALAM LETTER SA;Lo;0;L;;;;;N;;;;;
+0D39;MALAYALAM LETTER HA;Lo;0;L;;;;;N;;;;;
+0D3E;MALAYALAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0D3F;MALAYALAM VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0D40;MALAYALAM VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0D41;MALAYALAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0D42;MALAYALAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0D43;MALAYALAM VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0D46;MALAYALAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0D47;MALAYALAM VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+0D48;MALAYALAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+0D4A;MALAYALAM VOWEL SIGN O;Mc;0;L;0D46 0D3E;;;;N;;;;;
+0D4B;MALAYALAM VOWEL SIGN OO;Mc;0;L;0D47 0D3E;;;;N;;;;;
+0D4C;MALAYALAM VOWEL SIGN AU;Mc;0;L;0D46 0D57;;;;N;;;;;
+0D4D;MALAYALAM SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0D57;MALAYALAM AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0D60;MALAYALAM LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0D61;MALAYALAM LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0D66;MALAYALAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0D67;MALAYALAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0D68;MALAYALAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0D69;MALAYALAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0D6A;MALAYALAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0D6B;MALAYALAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0D6C;MALAYALAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0D6D;MALAYALAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0D6E;MALAYALAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0D6F;MALAYALAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0D82;SINHALA SIGN ANUSVARAYA;Mc;0;L;;;;;N;;;;;
+0D83;SINHALA SIGN VISARGAYA;Mc;0;L;;;;;N;;;;;
+0D85;SINHALA LETTER AYANNA;Lo;0;L;;;;;N;;;;;
+0D86;SINHALA LETTER AAYANNA;Lo;0;L;;;;;N;;;;;
+0D87;SINHALA LETTER AEYANNA;Lo;0;L;;;;;N;;;;;
+0D88;SINHALA LETTER AEEYANNA;Lo;0;L;;;;;N;;;;;
+0D89;SINHALA LETTER IYANNA;Lo;0;L;;;;;N;;;;;
+0D8A;SINHALA LETTER IIYANNA;Lo;0;L;;;;;N;;;;;
+0D8B;SINHALA LETTER UYANNA;Lo;0;L;;;;;N;;;;;
+0D8C;SINHALA LETTER UUYANNA;Lo;0;L;;;;;N;;;;;
+0D8D;SINHALA LETTER IRUYANNA;Lo;0;L;;;;;N;;;;;
+0D8E;SINHALA LETTER IRUUYANNA;Lo;0;L;;;;;N;;;;;
+0D8F;SINHALA LETTER ILUYANNA;Lo;0;L;;;;;N;;;;;
+0D90;SINHALA LETTER ILUUYANNA;Lo;0;L;;;;;N;;;;;
+0D91;SINHALA LETTER EYANNA;Lo;0;L;;;;;N;;;;;
+0D92;SINHALA LETTER EEYANNA;Lo;0;L;;;;;N;;;;;
+0D93;SINHALA LETTER AIYANNA;Lo;0;L;;;;;N;;;;;
+0D94;SINHALA LETTER OYANNA;Lo;0;L;;;;;N;;;;;
+0D95;SINHALA LETTER OOYANNA;Lo;0;L;;;;;N;;;;;
+0D96;SINHALA LETTER AUYANNA;Lo;0;L;;;;;N;;;;;
+0D9A;SINHALA LETTER ALPAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;;
+0D9B;SINHALA LETTER MAHAAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;;
+0D9C;SINHALA LETTER ALPAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;;
+0D9D;SINHALA LETTER MAHAAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;;
+0D9E;SINHALA LETTER KANTAJA NAASIKYAYA;Lo;0;L;;;;;N;;;;;
+0D9F;SINHALA LETTER SANYAKA GAYANNA;Lo;0;L;;;;;N;;;;;
+0DA0;SINHALA LETTER ALPAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;;
+0DA1;SINHALA LETTER MAHAAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;;
+0DA2;SINHALA LETTER ALPAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA3;SINHALA LETTER MAHAAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA4;SINHALA LETTER TAALUJA NAASIKYAYA;Lo;0;L;;;;;N;;;;;
+0DA5;SINHALA LETTER TAALUJA SANYOOGA NAAKSIKYAYA;Lo;0;L;;;;;N;;;;;
+0DA6;SINHALA LETTER SANYAKA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA7;SINHALA LETTER ALPAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;;
+0DA8;SINHALA LETTER MAHAAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;;
+0DA9;SINHALA LETTER ALPAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAA;SINHALA LETTER MAHAAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAB;SINHALA LETTER MUURDHAJA NAYANNA;Lo;0;L;;;;;N;;;;;
+0DAC;SINHALA LETTER SANYAKA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAD;SINHALA LETTER ALPAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;;
+0DAE;SINHALA LETTER MAHAAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;;
+0DAF;SINHALA LETTER ALPAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB0;SINHALA LETTER MAHAAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB1;SINHALA LETTER DANTAJA NAYANNA;Lo;0;L;;;;;N;;;;;
+0DB3;SINHALA LETTER SANYAKA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB4;SINHALA LETTER ALPAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;;
+0DB5;SINHALA LETTER MAHAAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;;
+0DB6;SINHALA LETTER ALPAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DB7;SINHALA LETTER MAHAAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DB8;SINHALA LETTER MAYANNA;Lo;0;L;;;;;N;;;;;
+0DB9;SINHALA LETTER AMBA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DBA;SINHALA LETTER YAYANNA;Lo;0;L;;;;;N;;;;;
+0DBB;SINHALA LETTER RAYANNA;Lo;0;L;;;;;N;;;;;
+0DBD;SINHALA LETTER DANTAJA LAYANNA;Lo;0;L;;;;;N;;;;;
+0DC0;SINHALA LETTER VAYANNA;Lo;0;L;;;;;N;;;;;
+0DC1;SINHALA LETTER TAALUJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC2;SINHALA LETTER MUURDHAJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC3;SINHALA LETTER DANTAJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC4;SINHALA LETTER HAYANNA;Lo;0;L;;;;;N;;;;;
+0DC5;SINHALA LETTER MUURDHAJA LAYANNA;Lo;0;L;;;;;N;;;;;
+0DC6;SINHALA LETTER FAYANNA;Lo;0;L;;;;;N;;;;;
+0DCA;SINHALA SIGN AL-LAKUNA;Mn;9;NSM;;;;;N;;;;;
+0DCF;SINHALA VOWEL SIGN AELA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD0;SINHALA VOWEL SIGN KETTI AEDA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD1;SINHALA VOWEL SIGN DIGA AEDA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD2;SINHALA VOWEL SIGN KETTI IS-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD3;SINHALA VOWEL SIGN DIGA IS-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD4;SINHALA VOWEL SIGN KETTI PAA-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD6;SINHALA VOWEL SIGN DIGA PAA-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD8;SINHALA VOWEL SIGN GAETTA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD9;SINHALA VOWEL SIGN KOMBUVA;Mc;0;L;;;;;N;;;;;
+0DDA;SINHALA VOWEL SIGN DIGA KOMBUVA;Mc;0;L;0DD9 0DCA;;;;N;;;;;
+0DDB;SINHALA VOWEL SIGN KOMBU DEKA;Mc;0;L;;;;;N;;;;;
+0DDC;SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA;Mc;0;L;0DD9 0DCF;;;;N;;;;;
+0DDD;SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA;Mc;0;L;0DDC 0DCA;;;;N;;;;;
+0DDE;SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA;Mc;0;L;0DD9 0DDF;;;;N;;;;;
+0DDF;SINHALA VOWEL SIGN GAYANUKITTA;Mc;0;L;;;;;N;;;;;
+0DF2;SINHALA VOWEL SIGN DIGA GAETTA-PILLA;Mc;0;L;;;;;N;;;;;
+0DF3;SINHALA VOWEL SIGN DIGA GAYANUKITTA;Mc;0;L;;;;;N;;;;;
+0DF4;SINHALA PUNCTUATION KUNDDALIYA;Po;0;L;;;;;N;;;;;
+0E01;THAI CHARACTER KO KAI;Lo;0;L;;;;;N;THAI LETTER KO KAI;;;;
+0E02;THAI CHARACTER KHO KHAI;Lo;0;L;;;;;N;THAI LETTER KHO KHAI;;;;
+0E03;THAI CHARACTER KHO KHUAT;Lo;0;L;;;;;N;THAI LETTER KHO KHUAT;;;;
+0E04;THAI CHARACTER KHO KHWAI;Lo;0;L;;;;;N;THAI LETTER KHO KHWAI;;;;
+0E05;THAI CHARACTER KHO KHON;Lo;0;L;;;;;N;THAI LETTER KHO KHON;;;;
+0E06;THAI CHARACTER KHO RAKHANG;Lo;0;L;;;;;N;THAI LETTER KHO RAKHANG;;;;
+0E07;THAI CHARACTER NGO NGU;Lo;0;L;;;;;N;THAI LETTER NGO NGU;;;;
+0E08;THAI CHARACTER CHO CHAN;Lo;0;L;;;;;N;THAI LETTER CHO CHAN;;;;
+0E09;THAI CHARACTER CHO CHING;Lo;0;L;;;;;N;THAI LETTER CHO CHING;;;;
+0E0A;THAI CHARACTER CHO CHANG;Lo;0;L;;;;;N;THAI LETTER CHO CHANG;;;;
+0E0B;THAI CHARACTER SO SO;Lo;0;L;;;;;N;THAI LETTER SO SO;;;;
+0E0C;THAI CHARACTER CHO CHOE;Lo;0;L;;;;;N;THAI LETTER CHO CHOE;;;;
+0E0D;THAI CHARACTER YO YING;Lo;0;L;;;;;N;THAI LETTER YO YING;;;;
+0E0E;THAI CHARACTER DO CHADA;Lo;0;L;;;;;N;THAI LETTER DO CHADA;;;;
+0E0F;THAI CHARACTER TO PATAK;Lo;0;L;;;;;N;THAI LETTER TO PATAK;;;;
+0E10;THAI CHARACTER THO THAN;Lo;0;L;;;;;N;THAI LETTER THO THAN;;;;
+0E11;THAI CHARACTER THO NANGMONTHO;Lo;0;L;;;;;N;THAI LETTER THO NANGMONTHO;;;;
+0E12;THAI CHARACTER THO PHUTHAO;Lo;0;L;;;;;N;THAI LETTER THO PHUTHAO;;;;
+0E13;THAI CHARACTER NO NEN;Lo;0;L;;;;;N;THAI LETTER NO NEN;;;;
+0E14;THAI CHARACTER DO DEK;Lo;0;L;;;;;N;THAI LETTER DO DEK;;;;
+0E15;THAI CHARACTER TO TAO;Lo;0;L;;;;;N;THAI LETTER TO TAO;;;;
+0E16;THAI CHARACTER THO THUNG;Lo;0;L;;;;;N;THAI LETTER THO THUNG;;;;
+0E17;THAI CHARACTER THO THAHAN;Lo;0;L;;;;;N;THAI LETTER THO THAHAN;;;;
+0E18;THAI CHARACTER THO THONG;Lo;0;L;;;;;N;THAI LETTER THO THONG;;;;
+0E19;THAI CHARACTER NO NU;Lo;0;L;;;;;N;THAI LETTER NO NU;;;;
+0E1A;THAI CHARACTER BO BAIMAI;Lo;0;L;;;;;N;THAI LETTER BO BAIMAI;;;;
+0E1B;THAI CHARACTER PO PLA;Lo;0;L;;;;;N;THAI LETTER PO PLA;;;;
+0E1C;THAI CHARACTER PHO PHUNG;Lo;0;L;;;;;N;THAI LETTER PHO PHUNG;;;;
+0E1D;THAI CHARACTER FO FA;Lo;0;L;;;;;N;THAI LETTER FO FA;;;;
+0E1E;THAI CHARACTER PHO PHAN;Lo;0;L;;;;;N;THAI LETTER PHO PHAN;;;;
+0E1F;THAI CHARACTER FO FAN;Lo;0;L;;;;;N;THAI LETTER FO FAN;;;;
+0E20;THAI CHARACTER PHO SAMPHAO;Lo;0;L;;;;;N;THAI LETTER PHO SAMPHAO;;;;
+0E21;THAI CHARACTER MO MA;Lo;0;L;;;;;N;THAI LETTER MO MA;;;;
+0E22;THAI CHARACTER YO YAK;Lo;0;L;;;;;N;THAI LETTER YO YAK;;;;
+0E23;THAI CHARACTER RO RUA;Lo;0;L;;;;;N;THAI LETTER RO RUA;;;;
+0E24;THAI CHARACTER RU;Lo;0;L;;;;;N;THAI LETTER RU;;;;
+0E25;THAI CHARACTER LO LING;Lo;0;L;;;;;N;THAI LETTER LO LING;;;;
+0E26;THAI CHARACTER LU;Lo;0;L;;;;;N;THAI LETTER LU;;;;
+0E27;THAI CHARACTER WO WAEN;Lo;0;L;;;;;N;THAI LETTER WO WAEN;;;;
+0E28;THAI CHARACTER SO SALA;Lo;0;L;;;;;N;THAI LETTER SO SALA;;;;
+0E29;THAI CHARACTER SO RUSI;Lo;0;L;;;;;N;THAI LETTER SO RUSI;;;;
+0E2A;THAI CHARACTER SO SUA;Lo;0;L;;;;;N;THAI LETTER SO SUA;;;;
+0E2B;THAI CHARACTER HO HIP;Lo;0;L;;;;;N;THAI LETTER HO HIP;;;;
+0E2C;THAI CHARACTER LO CHULA;Lo;0;L;;;;;N;THAI LETTER LO CHULA;;;;
+0E2D;THAI CHARACTER O ANG;Lo;0;L;;;;;N;THAI LETTER O ANG;;;;
+0E2E;THAI CHARACTER HO NOKHUK;Lo;0;L;;;;;N;THAI LETTER HO NOK HUK;;;;
+0E2F;THAI CHARACTER PAIYANNOI;Lo;0;L;;;;;N;THAI PAI YAN NOI;paiyan noi;;;
+0E30;THAI CHARACTER SARA A;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA A;;;;
+0E31;THAI CHARACTER MAI HAN-AKAT;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI HAN-AKAT;;;;
+0E32;THAI CHARACTER SARA AA;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AA;;;;
+0E33;THAI CHARACTER SARA AM;Lo;0;L;<compat> 0E4D 0E32;;;;N;THAI VOWEL SIGN SARA AM;;;;
+0E34;THAI CHARACTER SARA I;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA I;;;;
+0E35;THAI CHARACTER SARA II;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA II;;;;
+0E36;THAI CHARACTER SARA UE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UE;;;;
+0E37;THAI CHARACTER SARA UEE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UEE;sara uue;;;
+0E38;THAI CHARACTER SARA U;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA U;;;;
+0E39;THAI CHARACTER SARA UU;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA UU;;;;
+0E3A;THAI CHARACTER PHINTHU;Mn;9;NSM;;;;;N;THAI VOWEL SIGN PHINTHU;;;;
+0E3F;THAI CURRENCY SYMBOL BAHT;Sc;0;ET;;;;;N;THAI BAHT SIGN;;;;
+0E40;THAI CHARACTER SARA E;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA E;;;;
+0E41;THAI CHARACTER SARA AE;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AE;;;;
+0E42;THAI CHARACTER SARA O;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA O;;;;
+0E43;THAI CHARACTER SARA AI MAIMUAN;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MUAN;sara ai mai muan;;;
+0E44;THAI CHARACTER SARA AI MAIMALAI;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MALAI;sara ai mai malai;;;
+0E45;THAI CHARACTER LAKKHANGYAO;Lo;0;L;;;;;N;THAI LAK KHANG YAO;lakkhang yao;;;
+0E46;THAI CHARACTER MAIYAMOK;Lm;0;L;;;;;N;THAI MAI YAMOK;mai yamok;;;
+0E47;THAI CHARACTER MAITAIKHU;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI TAI KHU;mai taikhu;;;
+0E48;THAI CHARACTER MAI EK;Mn;107;NSM;;;;;N;THAI TONE MAI EK;;;;
+0E49;THAI CHARACTER MAI THO;Mn;107;NSM;;;;;N;THAI TONE MAI THO;;;;
+0E4A;THAI CHARACTER MAI TRI;Mn;107;NSM;;;;;N;THAI TONE MAI TRI;;;;
+0E4B;THAI CHARACTER MAI CHATTAWA;Mn;107;NSM;;;;;N;THAI TONE MAI CHATTAWA;;;;
+0E4C;THAI CHARACTER THANTHAKHAT;Mn;0;NSM;;;;;N;THAI THANTHAKHAT;;;;
+0E4D;THAI CHARACTER NIKHAHIT;Mn;0;NSM;;;;;N;THAI NIKKHAHIT;nikkhahit;;;
+0E4E;THAI CHARACTER YAMAKKAN;Mn;0;NSM;;;;;N;THAI YAMAKKAN;;;;
+0E4F;THAI CHARACTER FONGMAN;Po;0;L;;;;;N;THAI FONGMAN;;;;
+0E50;THAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0E51;THAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0E52;THAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0E53;THAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0E54;THAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0E55;THAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0E56;THAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0E57;THAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0E58;THAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0E59;THAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0E5A;THAI CHARACTER ANGKHANKHU;Po;0;L;;;;;N;THAI ANGKHANKHU;;;;
+0E5B;THAI CHARACTER KHOMUT;Po;0;L;;;;;N;THAI KHOMUT;;;;
+0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;;
+0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;;
+0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;;
+0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;;
+0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;;
+0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;;
+0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;;
+0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;;
+0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;;
+0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;;
+0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;;
+0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;;
+0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;;
+0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;;
+0E9C;LAO LETTER PHO SUNG;Lo;0;L;;;;;N;;;;;
+0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;;
+0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;;
+0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;;
+0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;;
+0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;;
+0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;;
+0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;;
+0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;;
+0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;;
+0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;;
+0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;;
+0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;;
+0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;;
+0EB0;LAO VOWEL SIGN A;Lo;0;L;;;;;N;;;;;
+0EB1;LAO VOWEL SIGN MAI KAN;Mn;0;NSM;;;;;N;;;;;
+0EB2;LAO VOWEL SIGN AA;Lo;0;L;;;;;N;;;;;
+0EB3;LAO VOWEL SIGN AM;Lo;0;L;<compat> 0ECD 0EB2;;;;N;;;;;
+0EB4;LAO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0EB5;LAO VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0EB6;LAO VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;;
+0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
+0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;;
+0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;;
+0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;;
+0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;;
+0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;;
+0EC0;LAO VOWEL SIGN E;Lo;0;L;;;;;N;;;;;
+0EC1;LAO VOWEL SIGN EI;Lo;0;L;;;;;N;;;;;
+0EC2;LAO VOWEL SIGN O;Lo;0;L;;;;;N;;;;;
+0EC3;LAO VOWEL SIGN AY;Lo;0;L;;;;;N;;;;;
+0EC4;LAO VOWEL SIGN AI;Lo;0;L;;;;;N;;;;;
+0EC6;LAO KO LA;Lm;0;L;;;;;N;;;;;
+0EC8;LAO TONE MAI EK;Mn;122;NSM;;;;;N;;;;;
+0EC9;LAO TONE MAI THO;Mn;122;NSM;;;;;N;;;;;
+0ECA;LAO TONE MAI TI;Mn;122;NSM;;;;;N;;;;;
+0ECB;LAO TONE MAI CATAWA;Mn;122;NSM;;;;;N;;;;;
+0ECC;LAO CANCELLATION MARK;Mn;0;NSM;;;;;N;;;;;
+0ECD;LAO NIGGAHITA;Mn;0;NSM;;;;;N;;;;;
+0ED0;LAO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0ED1;LAO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0ED2;LAO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0ED3;LAO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0ED4;LAO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0ED5;LAO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0ED6;LAO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0ED7;LAO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0ED8;LAO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0ED9;LAO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0EDC;LAO HO NO;Lo;0;L;<compat> 0EAB 0E99;;;;N;;;;;
+0EDD;LAO HO MO;Lo;0;L;<compat> 0EAB 0EA1;;;;N;;;;;
+0F00;TIBETAN SYLLABLE OM;Lo;0;L;;;;;N;;;;;
+0F01;TIBETAN MARK GTER YIG MGO TRUNCATED A;So;0;L;;;;;N;;ter yik go a thung;;;
+0F02;TIBETAN MARK GTER YIG MGO -UM RNAM BCAD MA;So;0;L;;;;;N;;ter yik go wum nam chey ma;;;
+0F03;TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA;So;0;L;;;;;N;;ter yik go wum ter tsek ma;;;
+0F04;TIBETAN MARK INITIAL YIG MGO MDUN MA;Po;0;L;;;;;N;TIBETAN SINGLE ORNAMENT;yik go dun ma;;;
+0F05;TIBETAN MARK CLOSING YIG MGO SGAB MA;Po;0;L;;;;;N;;yik go kab ma;;;
+0F06;TIBETAN MARK CARET YIG MGO PHUR SHAD MA;Po;0;L;;;;;N;;yik go pur shey ma;;;
+0F07;TIBETAN MARK YIG MGO TSHEG SHAD MA;Po;0;L;;;;;N;;yik go tsek shey ma;;;
+0F08;TIBETAN MARK SBRUL SHAD;Po;0;L;;;;;N;TIBETAN RGYANSHAD;drul shey;;;
+0F09;TIBETAN MARK BSKUR YIG MGO;Po;0;L;;;;;N;;kur yik go;;;
+0F0A;TIBETAN MARK BKA- SHOG YIG MGO;Po;0;L;;;;;N;;ka sho yik go;;;
+0F0B;TIBETAN MARK INTERSYLLABIC TSHEG;Po;0;L;;;;;N;TIBETAN TSEG;tsek;;;
+0F0C;TIBETAN MARK DELIMITER TSHEG BSTAR;Po;0;L;<noBreak> 0F0B;;;;N;;tsek tar;;;
+0F0D;TIBETAN MARK SHAD;Po;0;L;;;;;N;TIBETAN SHAD;shey;;;
+0F0E;TIBETAN MARK NYIS SHAD;Po;0;L;;;;;N;TIBETAN DOUBLE SHAD;nyi shey;;;
+0F0F;TIBETAN MARK TSHEG SHAD;Po;0;L;;;;;N;;tsek shey;;;
+0F10;TIBETAN MARK NYIS TSHEG SHAD;Po;0;L;;;;;N;;nyi tsek shey;;;
+0F11;TIBETAN MARK RIN CHEN SPUNGS SHAD;Po;0;L;;;;;N;TIBETAN RINCHANPHUNGSHAD;rinchen pung shey;;;
+0F12;TIBETAN MARK RGYA GRAM SHAD;Po;0;L;;;;;N;;gya tram shey;;;
+0F13;TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN;So;0;L;;;;;N;;dzu ta me long chen;;;
+0F14;TIBETAN MARK GTER TSHEG;So;0;L;;;;;N;TIBETAN COMMA;ter tsek;;;
+0F15;TIBETAN LOGOTYPE SIGN CHAD RTAGS;So;0;L;;;;;N;;che ta;;;
+0F16;TIBETAN LOGOTYPE SIGN LHAG RTAGS;So;0;L;;;;;N;;hlak ta;;;
+0F17;TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS;So;0;L;;;;;N;;trachen char ta;;;
+0F18;TIBETAN ASTROLOGICAL SIGN -KHYUD PA;Mn;220;NSM;;;;;N;;kyu pa;;;
+0F19;TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS;Mn;220;NSM;;;;;N;;dong tsu;;;
+0F1A;TIBETAN SIGN RDEL DKAR GCIG;So;0;L;;;;;N;;deka chig;;;
+0F1B;TIBETAN SIGN RDEL DKAR GNYIS;So;0;L;;;;;N;;deka nyi;;;
+0F1C;TIBETAN SIGN RDEL DKAR GSUM;So;0;L;;;;;N;;deka sum;;;
+0F1D;TIBETAN SIGN RDEL NAG GCIG;So;0;L;;;;;N;;dena chig;;;
+0F1E;TIBETAN SIGN RDEL NAG GNYIS;So;0;L;;;;;N;;dena nyi;;;
+0F1F;TIBETAN SIGN RDEL DKAR RDEL NAG;So;0;L;;;;;N;;deka dena;;;
+0F20;TIBETAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0F21;TIBETAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0F22;TIBETAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0F23;TIBETAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0F24;TIBETAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0F25;TIBETAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0F26;TIBETAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0F27;TIBETAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0F28;TIBETAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0F29;TIBETAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0F2A;TIBETAN DIGIT HALF ONE;No;0;L;;;;1/2;N;;;;;
+0F2B;TIBETAN DIGIT HALF TWO;No;0;L;;;;3/2;N;;;;;
+0F2C;TIBETAN DIGIT HALF THREE;No;0;L;;;;5/2;N;;;;;
+0F2D;TIBETAN DIGIT HALF FOUR;No;0;L;;;;7/2;N;;;;;
+0F2E;TIBETAN DIGIT HALF FIVE;No;0;L;;;;9/2;N;;;;;
+0F2F;TIBETAN DIGIT HALF SIX;No;0;L;;;;11/2;N;;;;;
+0F30;TIBETAN DIGIT HALF SEVEN;No;0;L;;;;13/2;N;;;;;
+0F31;TIBETAN DIGIT HALF EIGHT;No;0;L;;;;15/2;N;;;;;
+0F32;TIBETAN DIGIT HALF NINE;No;0;L;;;;17/2;N;;;;;
+0F33;TIBETAN DIGIT HALF ZERO;No;0;L;;;;-1/2;N;;;;;
+0F34;TIBETAN MARK BSDUS RTAGS;So;0;L;;;;;N;;du ta;;;
+0F35;TIBETAN MARK NGAS BZUNG NYI ZLA;Mn;220;NSM;;;;;N;TIBETAN HONORIFIC UNDER RING;nge zung nyi da;;;
+0F36;TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN;So;0;L;;;;;N;;dzu ta shi mig chen;;;
+0F37;TIBETAN MARK NGAS BZUNG SGOR RTAGS;Mn;220;NSM;;;;;N;TIBETAN UNDER RING;nge zung gor ta;;;
+0F38;TIBETAN MARK CHE MGO;So;0;L;;;;;N;;che go;;;
+0F39;TIBETAN MARK TSA -PHRU;Mn;216;NSM;;;;;N;TIBETAN LENITION MARK;tsa tru;;;
+0F3A;TIBETAN MARK GUG RTAGS GYON;Ps;0;ON;;;;;N;;gug ta yun;;;
+0F3B;TIBETAN MARK GUG RTAGS GYAS;Pe;0;ON;;;;;N;;gug ta ye;;;
+0F3C;TIBETAN MARK ANG KHANG GYON;Ps;0;ON;;;;;N;TIBETAN LEFT BRACE;ang kang yun;;;
+0F3D;TIBETAN MARK ANG KHANG GYAS;Pe;0;ON;;;;;N;TIBETAN RIGHT BRACE;ang kang ye;;;
+0F3E;TIBETAN SIGN YAR TSHES;Mc;0;L;;;;;N;;yar tse;;;
+0F3F;TIBETAN SIGN MAR TSHES;Mc;0;L;;;;;N;;mar tse;;;
+0F40;TIBETAN LETTER KA;Lo;0;L;;;;;N;;;;;
+0F41;TIBETAN LETTER KHA;Lo;0;L;;;;;N;;;;;
+0F42;TIBETAN LETTER GA;Lo;0;L;;;;;N;;;;;
+0F43;TIBETAN LETTER GHA;Lo;0;L;0F42 0FB7;;;;N;;;;;
+0F44;TIBETAN LETTER NGA;Lo;0;L;;;;;N;;;;;
+0F45;TIBETAN LETTER CA;Lo;0;L;;;;;N;;;;;
+0F46;TIBETAN LETTER CHA;Lo;0;L;;;;;N;;;;;
+0F47;TIBETAN LETTER JA;Lo;0;L;;;;;N;;;;;
+0F49;TIBETAN LETTER NYA;Lo;0;L;;;;;N;;;;;
+0F4A;TIBETAN LETTER TTA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED TA;;;;
+0F4B;TIBETAN LETTER TTHA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED THA;;;;
+0F4C;TIBETAN LETTER DDA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED DA;;;;
+0F4D;TIBETAN LETTER DDHA;Lo;0;L;0F4C 0FB7;;;;N;;;;;
+0F4E;TIBETAN LETTER NNA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED NA;;;;
+0F4F;TIBETAN LETTER TA;Lo;0;L;;;;;N;;;;;
+0F50;TIBETAN LETTER THA;Lo;0;L;;;;;N;;;;;
+0F51;TIBETAN LETTER DA;Lo;0;L;;;;;N;;;;;
+0F52;TIBETAN LETTER DHA;Lo;0;L;0F51 0FB7;;;;N;;;;;
+0F53;TIBETAN LETTER NA;Lo;0;L;;;;;N;;;;;
+0F54;TIBETAN LETTER PA;Lo;0;L;;;;;N;;;;;
+0F55;TIBETAN LETTER PHA;Lo;0;L;;;;;N;;;;;
+0F56;TIBETAN LETTER BA;Lo;0;L;;;;;N;;;;;
+0F57;TIBETAN LETTER BHA;Lo;0;L;0F56 0FB7;;;;N;;;;;
+0F58;TIBETAN LETTER MA;Lo;0;L;;;;;N;;;;;
+0F59;TIBETAN LETTER TSA;Lo;0;L;;;;;N;;;;;
+0F5A;TIBETAN LETTER TSHA;Lo;0;L;;;;;N;;;;;
+0F5B;TIBETAN LETTER DZA;Lo;0;L;;;;;N;;;;;
+0F5C;TIBETAN LETTER DZHA;Lo;0;L;0F5B 0FB7;;;;N;;;;;
+0F5D;TIBETAN LETTER WA;Lo;0;L;;;;;N;;;;;
+0F5E;TIBETAN LETTER ZHA;Lo;0;L;;;;;N;;;;;
+0F5F;TIBETAN LETTER ZA;Lo;0;L;;;;;N;;;;;
+0F60;TIBETAN LETTER -A;Lo;0;L;;;;;N;TIBETAN LETTER AA;;;;
+0F61;TIBETAN LETTER YA;Lo;0;L;;;;;N;;;;;
+0F62;TIBETAN LETTER RA;Lo;0;L;;;;;N;;*;;;
+0F63;TIBETAN LETTER LA;Lo;0;L;;;;;N;;;;;
+0F64;TIBETAN LETTER SHA;Lo;0;L;;;;;N;;;;;
+0F65;TIBETAN LETTER SSA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED SHA;;;;
+0F66;TIBETAN LETTER SA;Lo;0;L;;;;;N;;;;;
+0F67;TIBETAN LETTER HA;Lo;0;L;;;;;N;;;;;
+0F68;TIBETAN LETTER A;Lo;0;L;;;;;N;;;;;
+0F69;TIBETAN LETTER KSSA;Lo;0;L;0F40 0FB5;;;;N;;;;;
+0F6A;TIBETAN LETTER FIXED-FORM RA;Lo;0;L;;;;;N;;*;;;
+0F71;TIBETAN VOWEL SIGN AA;Mn;129;NSM;;;;;N;;;;;
+0F72;TIBETAN VOWEL SIGN I;Mn;130;NSM;;;;;N;;;;;
+0F73;TIBETAN VOWEL SIGN II;Mn;0;NSM;0F71 0F72;;;;N;;;;;
+0F74;TIBETAN VOWEL SIGN U;Mn;132;NSM;;;;;N;;;;;
+0F75;TIBETAN VOWEL SIGN UU;Mn;0;NSM;0F71 0F74;;;;N;;;;;
+0F76;TIBETAN VOWEL SIGN VOCALIC R;Mn;0;NSM;0FB2 0F80;;;;N;;;;;
+0F77;TIBETAN VOWEL SIGN VOCALIC RR;Mn;0;NSM;<compat> 0FB2 0F81;;;;N;;;;;
+0F78;TIBETAN VOWEL SIGN VOCALIC L;Mn;0;NSM;0FB3 0F80;;;;N;;;;;
+0F79;TIBETAN VOWEL SIGN VOCALIC LL;Mn;0;NSM;<compat> 0FB3 0F81;;;;N;;;;;
+0F7A;TIBETAN VOWEL SIGN E;Mn;130;NSM;;;;;N;;;;;
+0F7B;TIBETAN VOWEL SIGN EE;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AI;;;;
+0F7C;TIBETAN VOWEL SIGN O;Mn;130;NSM;;;;;N;;;;;
+0F7D;TIBETAN VOWEL SIGN OO;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AU;;;;
+0F7E;TIBETAN SIGN RJES SU NGA RO;Mn;0;NSM;;;;;N;TIBETAN ANUSVARA;je su nga ro;;;
+0F7F;TIBETAN SIGN RNAM BCAD;Mc;0;L;;;;;N;TIBETAN VISARGA;nam chey;;;
+0F80;TIBETAN VOWEL SIGN REVERSED I;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN SHORT I;;;;
+0F81;TIBETAN VOWEL SIGN REVERSED II;Mn;0;NSM;0F71 0F80;;;;N;;;;;
+0F82;TIBETAN SIGN NYI ZLA NAA DA;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU WITH ORNAMENT;nyi da na da;;;
+0F83;TIBETAN SIGN SNA LDAN;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU;nan de;;;
+0F84;TIBETAN MARK HALANTA;Mn;9;NSM;;;;;N;TIBETAN VIRAMA;;;;
+0F85;TIBETAN MARK PALUTA;Po;0;L;;;;;N;TIBETAN CHUCHENYIGE;;;;
+0F86;TIBETAN SIGN LCI RTAGS;Mn;230;NSM;;;;;N;;ji ta;;;
+0F87;TIBETAN SIGN YANG RTAGS;Mn;230;NSM;;;;;N;;yang ta;;;
+0F88;TIBETAN SIGN LCE TSA CAN;Lo;0;L;;;;;N;;che tsa chen;;;
+0F89;TIBETAN SIGN MCHU CAN;Lo;0;L;;;;;N;;chu chen;;;
+0F8A;TIBETAN SIGN GRU CAN RGYINGS;Lo;0;L;;;;;N;;tru chen ging;;;
+0F8B;TIBETAN SIGN GRU MED RGYINGS;Lo;0;L;;;;;N;;tru me ging;;;
+0F90;TIBETAN SUBJOINED LETTER KA;Mn;0;NSM;;;;;N;;;;;
+0F91;TIBETAN SUBJOINED LETTER KHA;Mn;0;NSM;;;;;N;;;;;
+0F92;TIBETAN SUBJOINED LETTER GA;Mn;0;NSM;;;;;N;;;;;
+0F93;TIBETAN SUBJOINED LETTER GHA;Mn;0;NSM;0F92 0FB7;;;;N;;;;;
+0F94;TIBETAN SUBJOINED LETTER NGA;Mn;0;NSM;;;;;N;;;;;
+0F95;TIBETAN SUBJOINED LETTER CA;Mn;0;NSM;;;;;N;;;;;
+0F96;TIBETAN SUBJOINED LETTER CHA;Mn;0;NSM;;;;;N;;;;;
+0F97;TIBETAN SUBJOINED LETTER JA;Mn;0;NSM;;;;;N;;;;;
+0F99;TIBETAN SUBJOINED LETTER NYA;Mn;0;NSM;;;;;N;;;;;
+0F9A;TIBETAN SUBJOINED LETTER TTA;Mn;0;NSM;;;;;N;;;;;
+0F9B;TIBETAN SUBJOINED LETTER TTHA;Mn;0;NSM;;;;;N;;;;;
+0F9C;TIBETAN SUBJOINED LETTER DDA;Mn;0;NSM;;;;;N;;;;;
+0F9D;TIBETAN SUBJOINED LETTER DDHA;Mn;0;NSM;0F9C 0FB7;;;;N;;;;;
+0F9E;TIBETAN SUBJOINED LETTER NNA;Mn;0;NSM;;;;;N;;;;;
+0F9F;TIBETAN SUBJOINED LETTER TA;Mn;0;NSM;;;;;N;;;;;
+0FA0;TIBETAN SUBJOINED LETTER THA;Mn;0;NSM;;;;;N;;;;;
+0FA1;TIBETAN SUBJOINED LETTER DA;Mn;0;NSM;;;;;N;;;;;
+0FA2;TIBETAN SUBJOINED LETTER DHA;Mn;0;NSM;0FA1 0FB7;;;;N;;;;;
+0FA3;TIBETAN SUBJOINED LETTER NA;Mn;0;NSM;;;;;N;;;;;
+0FA4;TIBETAN SUBJOINED LETTER PA;Mn;0;NSM;;;;;N;;;;;
+0FA5;TIBETAN SUBJOINED LETTER PHA;Mn;0;NSM;;;;;N;;;;;
+0FA6;TIBETAN SUBJOINED LETTER BA;Mn;0;NSM;;;;;N;;;;;
+0FA7;TIBETAN SUBJOINED LETTER BHA;Mn;0;NSM;0FA6 0FB7;;;;N;;;;;
+0FA8;TIBETAN SUBJOINED LETTER MA;Mn;0;NSM;;;;;N;;;;;
+0FA9;TIBETAN SUBJOINED LETTER TSA;Mn;0;NSM;;;;;N;;;;;
+0FAA;TIBETAN SUBJOINED LETTER TSHA;Mn;0;NSM;;;;;N;;;;;
+0FAB;TIBETAN SUBJOINED LETTER DZA;Mn;0;NSM;;;;;N;;;;;
+0FAC;TIBETAN SUBJOINED LETTER DZHA;Mn;0;NSM;0FAB 0FB7;;;;N;;;;;
+0FAD;TIBETAN SUBJOINED LETTER WA;Mn;0;NSM;;;;;N;;*;;;
+0FAE;TIBETAN SUBJOINED LETTER ZHA;Mn;0;NSM;;;;;N;;;;;
+0FAF;TIBETAN SUBJOINED LETTER ZA;Mn;0;NSM;;;;;N;;;;;
+0FB0;TIBETAN SUBJOINED LETTER -A;Mn;0;NSM;;;;;N;;;;;
+0FB1;TIBETAN SUBJOINED LETTER YA;Mn;0;NSM;;;;;N;;*;;;
+0FB2;TIBETAN SUBJOINED LETTER RA;Mn;0;NSM;;;;;N;;*;;;
+0FB3;TIBETAN SUBJOINED LETTER LA;Mn;0;NSM;;;;;N;;;;;
+0FB4;TIBETAN SUBJOINED LETTER SHA;Mn;0;NSM;;;;;N;;;;;
+0FB5;TIBETAN SUBJOINED LETTER SSA;Mn;0;NSM;;;;;N;;;;;
+0FB6;TIBETAN SUBJOINED LETTER SA;Mn;0;NSM;;;;;N;;;;;
+0FB7;TIBETAN SUBJOINED LETTER HA;Mn;0;NSM;;;;;N;;;;;
+0FB8;TIBETAN SUBJOINED LETTER A;Mn;0;NSM;;;;;N;;;;;
+0FB9;TIBETAN SUBJOINED LETTER KSSA;Mn;0;NSM;0F90 0FB5;;;;N;;;;;
+0FBA;TIBETAN SUBJOINED LETTER FIXED-FORM WA;Mn;0;NSM;;;;;N;;*;;;
+0FBB;TIBETAN SUBJOINED LETTER FIXED-FORM YA;Mn;0;NSM;;;;;N;;*;;;
+0FBC;TIBETAN SUBJOINED LETTER FIXED-FORM RA;Mn;0;NSM;;;;;N;;*;;;
+0FBE;TIBETAN KU RU KHA;So;0;L;;;;;N;;kuruka;;;
+0FBF;TIBETAN KU RU KHA BZHI MIG CAN;So;0;L;;;;;N;;kuruka shi mik chen;;;
+0FC0;TIBETAN CANTILLATION SIGN HEAVY BEAT;So;0;L;;;;;N;;;;;
+0FC1;TIBETAN CANTILLATION SIGN LIGHT BEAT;So;0;L;;;;;N;;;;;
+0FC2;TIBETAN CANTILLATION SIGN CANG TE-U;So;0;L;;;;;N;;chang tyu;;;
+0FC3;TIBETAN CANTILLATION SIGN SBUB -CHAL;So;0;L;;;;;N;;bub chey;;;
+0FC4;TIBETAN SYMBOL DRIL BU;So;0;L;;;;;N;;drilbu;;;
+0FC5;TIBETAN SYMBOL RDO RJE;So;0;L;;;;;N;;dorje;;;
+0FC6;TIBETAN SYMBOL PADMA GDAN;Mn;220;NSM;;;;;N;;pema den;;;
+0FC7;TIBETAN SYMBOL RDO RJE RGYA GRAM;So;0;L;;;;;N;;dorje gya dram;;;
+0FC8;TIBETAN SYMBOL PHUR PA;So;0;L;;;;;N;;phurba;;;
+0FC9;TIBETAN SYMBOL NOR BU;So;0;L;;;;;N;;norbu;;;
+0FCA;TIBETAN SYMBOL NOR BU NYIS -KHYIL;So;0;L;;;;;N;;norbu nyi khyi;;;
+0FCB;TIBETAN SYMBOL NOR BU GSUM -KHYIL;So;0;L;;;;;N;;norbu sum khyi;;;
+0FCC;TIBETAN SYMBOL NOR BU BZHI -KHYIL;So;0;L;;;;;N;;norbu shi khyi;;;
+0FCF;TIBETAN SIGN RDEL NAG GSUM;So;0;L;;;;;N;;dena sum;;;
+1000;MYANMAR LETTER KA;Lo;0;L;;;;;N;;;;;
+1001;MYANMAR LETTER KHA;Lo;0;L;;;;;N;;;;;
+1002;MYANMAR LETTER GA;Lo;0;L;;;;;N;;;;;
+1003;MYANMAR LETTER GHA;Lo;0;L;;;;;N;;;;;
+1004;MYANMAR LETTER NGA;Lo;0;L;;;;;N;;;;;
+1005;MYANMAR LETTER CA;Lo;0;L;;;;;N;;;;;
+1006;MYANMAR LETTER CHA;Lo;0;L;;;;;N;;;;;
+1007;MYANMAR LETTER JA;Lo;0;L;;;;;N;;;;;
+1008;MYANMAR LETTER JHA;Lo;0;L;;;;;N;;;;;
+1009;MYANMAR LETTER NYA;Lo;0;L;;;;;N;;;;;
+100A;MYANMAR LETTER NNYA;Lo;0;L;;;;;N;;;;;
+100B;MYANMAR LETTER TTA;Lo;0;L;;;;;N;;;;;
+100C;MYANMAR LETTER TTHA;Lo;0;L;;;;;N;;;;;
+100D;MYANMAR LETTER DDA;Lo;0;L;;;;;N;;;;;
+100E;MYANMAR LETTER DDHA;Lo;0;L;;;;;N;;;;;
+100F;MYANMAR LETTER NNA;Lo;0;L;;;;;N;;;;;
+1010;MYANMAR LETTER TA;Lo;0;L;;;;;N;;;;;
+1011;MYANMAR LETTER THA;Lo;0;L;;;;;N;;;;;
+1012;MYANMAR LETTER DA;Lo;0;L;;;;;N;;;;;
+1013;MYANMAR LETTER DHA;Lo;0;L;;;;;N;;;;;
+1014;MYANMAR LETTER NA;Lo;0;L;;;;;N;;;;;
+1015;MYANMAR LETTER PA;Lo;0;L;;;;;N;;;;;
+1016;MYANMAR LETTER PHA;Lo;0;L;;;;;N;;;;;
+1017;MYANMAR LETTER BA;Lo;0;L;;;;;N;;;;;
+1018;MYANMAR LETTER BHA;Lo;0;L;;;;;N;;;;;
+1019;MYANMAR LETTER MA;Lo;0;L;;;;;N;;;;;
+101A;MYANMAR LETTER YA;Lo;0;L;;;;;N;;;;;
+101B;MYANMAR LETTER RA;Lo;0;L;;;;;N;;;;;
+101C;MYANMAR LETTER LA;Lo;0;L;;;;;N;;;;;
+101D;MYANMAR LETTER WA;Lo;0;L;;;;;N;;;;;
+101E;MYANMAR LETTER SA;Lo;0;L;;;;;N;;;;;
+101F;MYANMAR LETTER HA;Lo;0;L;;;;;N;;;;;
+1020;MYANMAR LETTER LLA;Lo;0;L;;;;;N;;;;;
+1021;MYANMAR LETTER A;Lo;0;L;;;;;N;;;;;
+1023;MYANMAR LETTER I;Lo;0;L;;;;;N;;;;;
+1024;MYANMAR LETTER II;Lo;0;L;;;;;N;;;;;
+1025;MYANMAR LETTER U;Lo;0;L;;;;;N;;;;;
+1026;MYANMAR LETTER UU;Lo;0;L;1025 102E;;;;N;;;;;
+1027;MYANMAR LETTER E;Lo;0;L;;;;;N;;;;;
+1029;MYANMAR LETTER O;Lo;0;L;;;;;N;;;;;
+102A;MYANMAR LETTER AU;Lo;0;L;;;;;N;;;;;
+102C;MYANMAR VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+102D;MYANMAR VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+102E;MYANMAR VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+102F;MYANMAR VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1030;MYANMAR VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+1031;MYANMAR VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+1032;MYANMAR VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+1036;MYANMAR SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+1037;MYANMAR SIGN DOT BELOW;Mn;7;NSM;;;;;N;;;;;
+1038;MYANMAR SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+1039;MYANMAR SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+1040;MYANMAR DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1041;MYANMAR DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1042;MYANMAR DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1043;MYANMAR DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1044;MYANMAR DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1045;MYANMAR DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1046;MYANMAR DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1047;MYANMAR DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1048;MYANMAR DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1049;MYANMAR DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+104A;MYANMAR SIGN LITTLE SECTION;Po;0;L;;;;;N;;;;;
+104B;MYANMAR SIGN SECTION;Po;0;L;;;;;N;;;;;
+104C;MYANMAR SYMBOL LOCATIVE;Po;0;L;;;;;N;;;;;
+104D;MYANMAR SYMBOL COMPLETED;Po;0;L;;;;;N;;;;;
+104E;MYANMAR SYMBOL AFOREMENTIONED;Po;0;L;;;;;N;;;;;
+104F;MYANMAR SYMBOL GENITIVE;Po;0;L;;;;;N;;;;;
+1050;MYANMAR LETTER SHA;Lo;0;L;;;;;N;;;;;
+1051;MYANMAR LETTER SSA;Lo;0;L;;;;;N;;;;;
+1052;MYANMAR LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+1053;MYANMAR LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+1054;MYANMAR LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+1055;MYANMAR LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1056;MYANMAR VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+1057;MYANMAR VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+1058;MYANMAR VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+1059;MYANMAR VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+10A0;GEORGIAN CAPITAL LETTER AN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A1;GEORGIAN CAPITAL LETTER BAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A2;GEORGIAN CAPITAL LETTER GAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A3;GEORGIAN CAPITAL LETTER DON;Lu;0;L;;;;;N;;Khutsuri;;;
+10A4;GEORGIAN CAPITAL LETTER EN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A5;GEORGIAN CAPITAL LETTER VIN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A6;GEORGIAN CAPITAL LETTER ZEN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A7;GEORGIAN CAPITAL LETTER TAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A8;GEORGIAN CAPITAL LETTER IN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A9;GEORGIAN CAPITAL LETTER KAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10AA;GEORGIAN CAPITAL LETTER LAS;Lu;0;L;;;;;N;;Khutsuri;;;
+10AB;GEORGIAN CAPITAL LETTER MAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10AC;GEORGIAN CAPITAL LETTER NAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10AD;GEORGIAN CAPITAL LETTER ON;Lu;0;L;;;;;N;;Khutsuri;;;
+10AE;GEORGIAN CAPITAL LETTER PAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10AF;GEORGIAN CAPITAL LETTER ZHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B0;GEORGIAN CAPITAL LETTER RAE;Lu;0;L;;;;;N;;Khutsuri;;;
+10B1;GEORGIAN CAPITAL LETTER SAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B2;GEORGIAN CAPITAL LETTER TAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B3;GEORGIAN CAPITAL LETTER UN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B4;GEORGIAN CAPITAL LETTER PHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B5;GEORGIAN CAPITAL LETTER KHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B6;GEORGIAN CAPITAL LETTER GHAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B7;GEORGIAN CAPITAL LETTER QAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B8;GEORGIAN CAPITAL LETTER SHIN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B9;GEORGIAN CAPITAL LETTER CHIN;Lu;0;L;;;;;N;;Khutsuri;;;
+10BA;GEORGIAN CAPITAL LETTER CAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10BB;GEORGIAN CAPITAL LETTER JIL;Lu;0;L;;;;;N;;Khutsuri;;;
+10BC;GEORGIAN CAPITAL LETTER CIL;Lu;0;L;;;;;N;;Khutsuri;;;
+10BD;GEORGIAN CAPITAL LETTER CHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10BE;GEORGIAN CAPITAL LETTER XAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10BF;GEORGIAN CAPITAL LETTER JHAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10C0;GEORGIAN CAPITAL LETTER HAE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C1;GEORGIAN CAPITAL LETTER HE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C2;GEORGIAN CAPITAL LETTER HIE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C3;GEORGIAN CAPITAL LETTER WE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C4;GEORGIAN CAPITAL LETTER HAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10C5;GEORGIAN CAPITAL LETTER HOE;Lu;0;L;;;;;N;;Khutsuri;;;
+10D0;GEORGIAN LETTER AN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER AN;;;;
+10D1;GEORGIAN LETTER BAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER BAN;;;;
+10D2;GEORGIAN LETTER GAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GAN;;;;
+10D3;GEORGIAN LETTER DON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER DON;;;;
+10D4;GEORGIAN LETTER EN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER EN;;;;
+10D5;GEORGIAN LETTER VIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER VIN;;;;
+10D6;GEORGIAN LETTER ZEN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZEN;;;;
+10D7;GEORGIAN LETTER TAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAN;;;;
+10D8;GEORGIAN LETTER IN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER IN;;;;
+10D9;GEORGIAN LETTER KAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KAN;;;;
+10DA;GEORGIAN LETTER LAS;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER LAS;;;;
+10DB;GEORGIAN LETTER MAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER MAN;;;;
+10DC;GEORGIAN LETTER NAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER NAR;;;;
+10DD;GEORGIAN LETTER ON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ON;;;;
+10DE;GEORGIAN LETTER PAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PAR;;;;
+10DF;GEORGIAN LETTER ZHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZHAR;;;;
+10E0;GEORGIAN LETTER RAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER RAE;;;;
+10E1;GEORGIAN LETTER SAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SAN;;;;
+10E2;GEORGIAN LETTER TAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAR;;;;
+10E3;GEORGIAN LETTER UN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER UN;;;;
+10E4;GEORGIAN LETTER PHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PHAR;;;;
+10E5;GEORGIAN LETTER KHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KHAR;;;;
+10E6;GEORGIAN LETTER GHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GHAN;;;;
+10E7;GEORGIAN LETTER QAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER QAR;;;;
+10E8;GEORGIAN LETTER SHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SHIN;;;;
+10E9;GEORGIAN LETTER CHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHIN;;;;
+10EA;GEORGIAN LETTER CAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CAN;;;;
+10EB;GEORGIAN LETTER JIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JIL;;;;
+10EC;GEORGIAN LETTER CIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CIL;;;;
+10ED;GEORGIAN LETTER CHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHAR;;;;
+10EE;GEORGIAN LETTER XAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER XAN;;;;
+10EF;GEORGIAN LETTER JHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JHAN;;;;
+10F0;GEORGIAN LETTER HAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAE;;;;
+10F1;GEORGIAN LETTER HE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HE;;;;
+10F2;GEORGIAN LETTER HIE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HIE;;;;
+10F3;GEORGIAN LETTER WE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER WE;;;;
+10F4;GEORGIAN LETTER HAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAR;;;;
+10F5;GEORGIAN LETTER HOE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HOE;;;;
+10F6;GEORGIAN LETTER FI;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER FI;;;;
+10FB;GEORGIAN PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;;
+1100;HANGUL CHOSEONG KIYEOK;Lo;0;L;;;;;N;;g *;;;
+1101;HANGUL CHOSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;gg *;;;
+1102;HANGUL CHOSEONG NIEUN;Lo;0;L;;;;;N;;n *;;;
+1103;HANGUL CHOSEONG TIKEUT;Lo;0;L;;;;;N;;d *;;;
+1104;HANGUL CHOSEONG SSANGTIKEUT;Lo;0;L;;;;;N;;dd *;;;
+1105;HANGUL CHOSEONG RIEUL;Lo;0;L;;;;;N;;r *;;;
+1106;HANGUL CHOSEONG MIEUM;Lo;0;L;;;;;N;;m *;;;
+1107;HANGUL CHOSEONG PIEUP;Lo;0;L;;;;;N;;b *;;;
+1108;HANGUL CHOSEONG SSANGPIEUP;Lo;0;L;;;;;N;;bb *;;;
+1109;HANGUL CHOSEONG SIOS;Lo;0;L;;;;;N;;s *;;;
+110A;HANGUL CHOSEONG SSANGSIOS;Lo;0;L;;;;;N;;ss *;;;
+110B;HANGUL CHOSEONG IEUNG;Lo;0;L;;;;;N;;;;;
+110C;HANGUL CHOSEONG CIEUC;Lo;0;L;;;;;N;;j *;;;
+110D;HANGUL CHOSEONG SSANGCIEUC;Lo;0;L;;;;;N;;jj *;;;
+110E;HANGUL CHOSEONG CHIEUCH;Lo;0;L;;;;;N;;c *;;;
+110F;HANGUL CHOSEONG KHIEUKH;Lo;0;L;;;;;N;;k *;;;
+1110;HANGUL CHOSEONG THIEUTH;Lo;0;L;;;;;N;;t *;;;
+1111;HANGUL CHOSEONG PHIEUPH;Lo;0;L;;;;;N;;p *;;;
+1112;HANGUL CHOSEONG HIEUH;Lo;0;L;;;;;N;;h *;;;
+1113;HANGUL CHOSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;;
+1114;HANGUL CHOSEONG SSANGNIEUN;Lo;0;L;;;;;N;;;;;
+1115;HANGUL CHOSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;;
+1116;HANGUL CHOSEONG NIEUN-PIEUP;Lo;0;L;;;;;N;;;;;
+1117;HANGUL CHOSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;;
+1118;HANGUL CHOSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;;
+1119;HANGUL CHOSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;;
+111A;HANGUL CHOSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;;;;
+111B;HANGUL CHOSEONG KAPYEOUNRIEUL;Lo;0;L;;;;;N;;;;;
+111C;HANGUL CHOSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;;
+111D;HANGUL CHOSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;;
+111E;HANGUL CHOSEONG PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;;
+111F;HANGUL CHOSEONG PIEUP-NIEUN;Lo;0;L;;;;;N;;;;;
+1120;HANGUL CHOSEONG PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;;
+1121;HANGUL CHOSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;;;;
+1122;HANGUL CHOSEONG PIEUP-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+1123;HANGUL CHOSEONG PIEUP-SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+1124;HANGUL CHOSEONG PIEUP-SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+1125;HANGUL CHOSEONG PIEUP-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+1126;HANGUL CHOSEONG PIEUP-SIOS-CIEUC;Lo;0;L;;;;;N;;;;;
+1127;HANGUL CHOSEONG PIEUP-CIEUC;Lo;0;L;;;;;N;;;;;
+1128;HANGUL CHOSEONG PIEUP-CHIEUCH;Lo;0;L;;;;;N;;;;;
+1129;HANGUL CHOSEONG PIEUP-THIEUTH;Lo;0;L;;;;;N;;;;;
+112A;HANGUL CHOSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;;
+112B;HANGUL CHOSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+112C;HANGUL CHOSEONG KAPYEOUNSSANGPIEUP;Lo;0;L;;;;;N;;;;;
+112D;HANGUL CHOSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+112E;HANGUL CHOSEONG SIOS-NIEUN;Lo;0;L;;;;;N;;;;;
+112F;HANGUL CHOSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+1130;HANGUL CHOSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;;
+1131;HANGUL CHOSEONG SIOS-MIEUM;Lo;0;L;;;;;N;;;;;
+1132;HANGUL CHOSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+1133;HANGUL CHOSEONG SIOS-PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;;
+1134;HANGUL CHOSEONG SIOS-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+1135;HANGUL CHOSEONG SIOS-IEUNG;Lo;0;L;;;;;N;;;;;
+1136;HANGUL CHOSEONG SIOS-CIEUC;Lo;0;L;;;;;N;;;;;
+1137;HANGUL CHOSEONG SIOS-CHIEUCH;Lo;0;L;;;;;N;;;;;
+1138;HANGUL CHOSEONG SIOS-KHIEUKH;Lo;0;L;;;;;N;;;;;
+1139;HANGUL CHOSEONG SIOS-THIEUTH;Lo;0;L;;;;;N;;;;;
+113A;HANGUL CHOSEONG SIOS-PHIEUPH;Lo;0;L;;;;;N;;;;;
+113B;HANGUL CHOSEONG SIOS-HIEUH;Lo;0;L;;;;;N;;;;;
+113C;HANGUL CHOSEONG CHITUEUMSIOS;Lo;0;L;;;;;N;;;;;
+113D;HANGUL CHOSEONG CHITUEUMSSANGSIOS;Lo;0;L;;;;;N;;;;;
+113E;HANGUL CHOSEONG CEONGCHIEUMSIOS;Lo;0;L;;;;;N;;;;;
+113F;HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS;Lo;0;L;;;;;N;;;;;
+1140;HANGUL CHOSEONG PANSIOS;Lo;0;L;;;;;N;;;;;
+1141;HANGUL CHOSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;;
+1142;HANGUL CHOSEONG IEUNG-TIKEUT;Lo;0;L;;;;;N;;;;;
+1143;HANGUL CHOSEONG IEUNG-MIEUM;Lo;0;L;;;;;N;;;;;
+1144;HANGUL CHOSEONG IEUNG-PIEUP;Lo;0;L;;;;;N;;;;;
+1145;HANGUL CHOSEONG IEUNG-SIOS;Lo;0;L;;;;;N;;;;;
+1146;HANGUL CHOSEONG IEUNG-PANSIOS;Lo;0;L;;;;;N;;;;;
+1147;HANGUL CHOSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;;
+1148;HANGUL CHOSEONG IEUNG-CIEUC;Lo;0;L;;;;;N;;;;;
+1149;HANGUL CHOSEONG IEUNG-CHIEUCH;Lo;0;L;;;;;N;;;;;
+114A;HANGUL CHOSEONG IEUNG-THIEUTH;Lo;0;L;;;;;N;;;;;
+114B;HANGUL CHOSEONG IEUNG-PHIEUPH;Lo;0;L;;;;;N;;;;;
+114C;HANGUL CHOSEONG YESIEUNG;Lo;0;L;;;;;N;;;;;
+114D;HANGUL CHOSEONG CIEUC-IEUNG;Lo;0;L;;;;;N;;;;;
+114E;HANGUL CHOSEONG CHITUEUMCIEUC;Lo;0;L;;;;;N;;;;;
+114F;HANGUL CHOSEONG CHITUEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;;
+1150;HANGUL CHOSEONG CEONGCHIEUMCIEUC;Lo;0;L;;;;;N;;;;;
+1151;HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;;
+1152;HANGUL CHOSEONG CHIEUCH-KHIEUKH;Lo;0;L;;;;;N;;;;;
+1153;HANGUL CHOSEONG CHIEUCH-HIEUH;Lo;0;L;;;;;N;;;;;
+1154;HANGUL CHOSEONG CHITUEUMCHIEUCH;Lo;0;L;;;;;N;;;;;
+1155;HANGUL CHOSEONG CEONGCHIEUMCHIEUCH;Lo;0;L;;;;;N;;;;;
+1156;HANGUL CHOSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;;
+1157;HANGUL CHOSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;;
+1158;HANGUL CHOSEONG SSANGHIEUH;Lo;0;L;;;;;N;;;;;
+1159;HANGUL CHOSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+115F;HANGUL CHOSEONG FILLER;Lo;0;L;;;;;N;;;;;
+1160;HANGUL JUNGSEONG FILLER;Lo;0;L;;;;;N;;;;;
+1161;HANGUL JUNGSEONG A;Lo;0;L;;;;;N;;;;;
+1162;HANGUL JUNGSEONG AE;Lo;0;L;;;;;N;;;;;
+1163;HANGUL JUNGSEONG YA;Lo;0;L;;;;;N;;;;;
+1164;HANGUL JUNGSEONG YAE;Lo;0;L;;;;;N;;;;;
+1165;HANGUL JUNGSEONG EO;Lo;0;L;;;;;N;;;;;
+1166;HANGUL JUNGSEONG E;Lo;0;L;;;;;N;;;;;
+1167;HANGUL JUNGSEONG YEO;Lo;0;L;;;;;N;;;;;
+1168;HANGUL JUNGSEONG YE;Lo;0;L;;;;;N;;;;;
+1169;HANGUL JUNGSEONG O;Lo;0;L;;;;;N;;;;;
+116A;HANGUL JUNGSEONG WA;Lo;0;L;;;;;N;;;;;
+116B;HANGUL JUNGSEONG WAE;Lo;0;L;;;;;N;;;;;
+116C;HANGUL JUNGSEONG OE;Lo;0;L;;;;;N;;;;;
+116D;HANGUL JUNGSEONG YO;Lo;0;L;;;;;N;;;;;
+116E;HANGUL JUNGSEONG U;Lo;0;L;;;;;N;;;;;
+116F;HANGUL JUNGSEONG WEO;Lo;0;L;;;;;N;;;;;
+1170;HANGUL JUNGSEONG WE;Lo;0;L;;;;;N;;;;;
+1171;HANGUL JUNGSEONG WI;Lo;0;L;;;;;N;;;;;
+1172;HANGUL JUNGSEONG YU;Lo;0;L;;;;;N;;;;;
+1173;HANGUL JUNGSEONG EU;Lo;0;L;;;;;N;;;;;
+1174;HANGUL JUNGSEONG YI;Lo;0;L;;;;;N;;;;;
+1175;HANGUL JUNGSEONG I;Lo;0;L;;;;;N;;;;;
+1176;HANGUL JUNGSEONG A-O;Lo;0;L;;;;;N;;;;;
+1177;HANGUL JUNGSEONG A-U;Lo;0;L;;;;;N;;;;;
+1178;HANGUL JUNGSEONG YA-O;Lo;0;L;;;;;N;;;;;
+1179;HANGUL JUNGSEONG YA-YO;Lo;0;L;;;;;N;;;;;
+117A;HANGUL JUNGSEONG EO-O;Lo;0;L;;;;;N;;;;;
+117B;HANGUL JUNGSEONG EO-U;Lo;0;L;;;;;N;;;;;
+117C;HANGUL JUNGSEONG EO-EU;Lo;0;L;;;;;N;;;;;
+117D;HANGUL JUNGSEONG YEO-O;Lo;0;L;;;;;N;;;;;
+117E;HANGUL JUNGSEONG YEO-U;Lo;0;L;;;;;N;;;;;
+117F;HANGUL JUNGSEONG O-EO;Lo;0;L;;;;;N;;;;;
+1180;HANGUL JUNGSEONG O-E;Lo;0;L;;;;;N;;;;;
+1181;HANGUL JUNGSEONG O-YE;Lo;0;L;;;;;N;;;;;
+1182;HANGUL JUNGSEONG O-O;Lo;0;L;;;;;N;;;;;
+1183;HANGUL JUNGSEONG O-U;Lo;0;L;;;;;N;;;;;
+1184;HANGUL JUNGSEONG YO-YA;Lo;0;L;;;;;N;;;;;
+1185;HANGUL JUNGSEONG YO-YAE;Lo;0;L;;;;;N;;;;;
+1186;HANGUL JUNGSEONG YO-YEO;Lo;0;L;;;;;N;;;;;
+1187;HANGUL JUNGSEONG YO-O;Lo;0;L;;;;;N;;;;;
+1188;HANGUL JUNGSEONG YO-I;Lo;0;L;;;;;N;;;;;
+1189;HANGUL JUNGSEONG U-A;Lo;0;L;;;;;N;;;;;
+118A;HANGUL JUNGSEONG U-AE;Lo;0;L;;;;;N;;;;;
+118B;HANGUL JUNGSEONG U-EO-EU;Lo;0;L;;;;;N;;;;;
+118C;HANGUL JUNGSEONG U-YE;Lo;0;L;;;;;N;;;;;
+118D;HANGUL JUNGSEONG U-U;Lo;0;L;;;;;N;;;;;
+118E;HANGUL JUNGSEONG YU-A;Lo;0;L;;;;;N;;;;;
+118F;HANGUL JUNGSEONG YU-EO;Lo;0;L;;;;;N;;;;;
+1190;HANGUL JUNGSEONG YU-E;Lo;0;L;;;;;N;;;;;
+1191;HANGUL JUNGSEONG YU-YEO;Lo;0;L;;;;;N;;;;;
+1192;HANGUL JUNGSEONG YU-YE;Lo;0;L;;;;;N;;;;;
+1193;HANGUL JUNGSEONG YU-U;Lo;0;L;;;;;N;;;;;
+1194;HANGUL JUNGSEONG YU-I;Lo;0;L;;;;;N;;;;;
+1195;HANGUL JUNGSEONG EU-U;Lo;0;L;;;;;N;;;;;
+1196;HANGUL JUNGSEONG EU-EU;Lo;0;L;;;;;N;;;;;
+1197;HANGUL JUNGSEONG YI-U;Lo;0;L;;;;;N;;;;;
+1198;HANGUL JUNGSEONG I-A;Lo;0;L;;;;;N;;;;;
+1199;HANGUL JUNGSEONG I-YA;Lo;0;L;;;;;N;;;;;
+119A;HANGUL JUNGSEONG I-O;Lo;0;L;;;;;N;;;;;
+119B;HANGUL JUNGSEONG I-U;Lo;0;L;;;;;N;;;;;
+119C;HANGUL JUNGSEONG I-EU;Lo;0;L;;;;;N;;;;;
+119D;HANGUL JUNGSEONG I-ARAEA;Lo;0;L;;;;;N;;;;;
+119E;HANGUL JUNGSEONG ARAEA;Lo;0;L;;;;;N;;;;;
+119F;HANGUL JUNGSEONG ARAEA-EO;Lo;0;L;;;;;N;;;;;
+11A0;HANGUL JUNGSEONG ARAEA-U;Lo;0;L;;;;;N;;;;;
+11A1;HANGUL JUNGSEONG ARAEA-I;Lo;0;L;;;;;N;;;;;
+11A2;HANGUL JUNGSEONG SSANGARAEA;Lo;0;L;;;;;N;;;;;
+11A8;HANGUL JONGSEONG KIYEOK;Lo;0;L;;;;;N;;g *;;;
+11A9;HANGUL JONGSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;gg *;;;
+11AA;HANGUL JONGSEONG KIYEOK-SIOS;Lo;0;L;;;;;N;;gs *;;;
+11AB;HANGUL JONGSEONG NIEUN;Lo;0;L;;;;;N;;n *;;;
+11AC;HANGUL JONGSEONG NIEUN-CIEUC;Lo;0;L;;;;;N;;nj *;;;
+11AD;HANGUL JONGSEONG NIEUN-HIEUH;Lo;0;L;;;;;N;;nh *;;;
+11AE;HANGUL JONGSEONG TIKEUT;Lo;0;L;;;;;N;;d *;;;
+11AF;HANGUL JONGSEONG RIEUL;Lo;0;L;;;;;N;;l *;;;
+11B0;HANGUL JONGSEONG RIEUL-KIYEOK;Lo;0;L;;;;;N;;lg *;;;
+11B1;HANGUL JONGSEONG RIEUL-MIEUM;Lo;0;L;;;;;N;;lm *;;;
+11B2;HANGUL JONGSEONG RIEUL-PIEUP;Lo;0;L;;;;;N;;lb *;;;
+11B3;HANGUL JONGSEONG RIEUL-SIOS;Lo;0;L;;;;;N;;ls *;;;
+11B4;HANGUL JONGSEONG RIEUL-THIEUTH;Lo;0;L;;;;;N;;lt *;;;
+11B5;HANGUL JONGSEONG RIEUL-PHIEUPH;Lo;0;L;;;;;N;;lp *;;;
+11B6;HANGUL JONGSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;lh *;;;
+11B7;HANGUL JONGSEONG MIEUM;Lo;0;L;;;;;N;;m *;;;
+11B8;HANGUL JONGSEONG PIEUP;Lo;0;L;;;;;N;;b *;;;
+11B9;HANGUL JONGSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;bs *;;;
+11BA;HANGUL JONGSEONG SIOS;Lo;0;L;;;;;N;;s *;;;
+11BB;HANGUL JONGSEONG SSANGSIOS;Lo;0;L;;;;;N;;ss *;;;
+11BC;HANGUL JONGSEONG IEUNG;Lo;0;L;;;;;N;;ng *;;;
+11BD;HANGUL JONGSEONG CIEUC;Lo;0;L;;;;;N;;j *;;;
+11BE;HANGUL JONGSEONG CHIEUCH;Lo;0;L;;;;;N;;c *;;;
+11BF;HANGUL JONGSEONG KHIEUKH;Lo;0;L;;;;;N;;k *;;;
+11C0;HANGUL JONGSEONG THIEUTH;Lo;0;L;;;;;N;;t *;;;
+11C1;HANGUL JONGSEONG PHIEUPH;Lo;0;L;;;;;N;;p *;;;
+11C2;HANGUL JONGSEONG HIEUH;Lo;0;L;;;;;N;;h *;;;
+11C3;HANGUL JONGSEONG KIYEOK-RIEUL;Lo;0;L;;;;;N;;;;;
+11C4;HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+11C5;HANGUL JONGSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;;
+11C6;HANGUL JONGSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;;
+11C7;HANGUL JONGSEONG NIEUN-SIOS;Lo;0;L;;;;;N;;;;;
+11C8;HANGUL JONGSEONG NIEUN-PANSIOS;Lo;0;L;;;;;N;;;;;
+11C9;HANGUL JONGSEONG NIEUN-THIEUTH;Lo;0;L;;;;;N;;;;;
+11CA;HANGUL JONGSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;;
+11CB;HANGUL JONGSEONG TIKEUT-RIEUL;Lo;0;L;;;;;N;;;;;
+11CC;HANGUL JONGSEONG RIEUL-KIYEOK-SIOS;Lo;0;L;;;;;N;;;;;
+11CD;HANGUL JONGSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;;
+11CE;HANGUL JONGSEONG RIEUL-TIKEUT;Lo;0;L;;;;;N;;;;;
+11CF;HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH;Lo;0;L;;;;;N;;;;;
+11D0;HANGUL JONGSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;;
+11D1;HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;;
+11D2;HANGUL JONGSEONG RIEUL-MIEUM-SIOS;Lo;0;L;;;;;N;;;;;
+11D3;HANGUL JONGSEONG RIEUL-PIEUP-SIOS;Lo;0;L;;;;;N;;;;;
+11D4;HANGUL JONGSEONG RIEUL-PIEUP-HIEUH;Lo;0;L;;;;;N;;;;;
+11D5;HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+11D6;HANGUL JONGSEONG RIEUL-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+11D7;HANGUL JONGSEONG RIEUL-PANSIOS;Lo;0;L;;;;;N;;;;;
+11D8;HANGUL JONGSEONG RIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;;
+11D9;HANGUL JONGSEONG RIEUL-YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+11DA;HANGUL JONGSEONG MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;;
+11DB;HANGUL JONGSEONG MIEUM-RIEUL;Lo;0;L;;;;;N;;;;;
+11DC;HANGUL JONGSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;;
+11DD;HANGUL JONGSEONG MIEUM-SIOS;Lo;0;L;;;;;N;;;;;
+11DE;HANGUL JONGSEONG MIEUM-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+11DF;HANGUL JONGSEONG MIEUM-PANSIOS;Lo;0;L;;;;;N;;;;;
+11E0;HANGUL JONGSEONG MIEUM-CHIEUCH;Lo;0;L;;;;;N;;;;;
+11E1;HANGUL JONGSEONG MIEUM-HIEUH;Lo;0;L;;;;;N;;;;;
+11E2;HANGUL JONGSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;;
+11E3;HANGUL JONGSEONG PIEUP-RIEUL;Lo;0;L;;;;;N;;;;;
+11E4;HANGUL JONGSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;;
+11E5;HANGUL JONGSEONG PIEUP-HIEUH;Lo;0;L;;;;;N;;;;;
+11E6;HANGUL JONGSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+11E7;HANGUL JONGSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+11E8;HANGUL JONGSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+11E9;HANGUL JONGSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;;
+11EA;HANGUL JONGSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+11EB;HANGUL JONGSEONG PANSIOS;Lo;0;L;;;;;N;;;;;
+11EC;HANGUL JONGSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;;
+11ED;HANGUL JONGSEONG IEUNG-SSANGKIYEOK;Lo;0;L;;;;;N;;;;;
+11EE;HANGUL JONGSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;;
+11EF;HANGUL JONGSEONG IEUNG-KHIEUKH;Lo;0;L;;;;;N;;;;;
+11F0;HANGUL JONGSEONG YESIEUNG;Lo;0;L;;;;;N;;;;;
+11F1;HANGUL JONGSEONG YESIEUNG-SIOS;Lo;0;L;;;;;N;;;;;
+11F2;HANGUL JONGSEONG YESIEUNG-PANSIOS;Lo;0;L;;;;;N;;;;;
+11F3;HANGUL JONGSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;;
+11F4;HANGUL JONGSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;;
+11F5;HANGUL JONGSEONG HIEUH-NIEUN;Lo;0;L;;;;;N;;;;;
+11F6;HANGUL JONGSEONG HIEUH-RIEUL;Lo;0;L;;;;;N;;;;;
+11F7;HANGUL JONGSEONG HIEUH-MIEUM;Lo;0;L;;;;;N;;;;;
+11F8;HANGUL JONGSEONG HIEUH-PIEUP;Lo;0;L;;;;;N;;;;;
+11F9;HANGUL JONGSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+1200;ETHIOPIC SYLLABLE HA;Lo;0;L;;;;;N;;;;;
+1201;ETHIOPIC SYLLABLE HU;Lo;0;L;;;;;N;;;;;
+1202;ETHIOPIC SYLLABLE HI;Lo;0;L;;;;;N;;;;;
+1203;ETHIOPIC SYLLABLE HAA;Lo;0;L;;;;;N;;;;;
+1204;ETHIOPIC SYLLABLE HEE;Lo;0;L;;;;;N;;;;;
+1205;ETHIOPIC SYLLABLE HE;Lo;0;L;;;;;N;;;;;
+1206;ETHIOPIC SYLLABLE HO;Lo;0;L;;;;;N;;;;;
+1208;ETHIOPIC SYLLABLE LA;Lo;0;L;;;;;N;;;;;
+1209;ETHIOPIC SYLLABLE LU;Lo;0;L;;;;;N;;;;;
+120A;ETHIOPIC SYLLABLE LI;Lo;0;L;;;;;N;;;;;
+120B;ETHIOPIC SYLLABLE LAA;Lo;0;L;;;;;N;;;;;
+120C;ETHIOPIC SYLLABLE LEE;Lo;0;L;;;;;N;;;;;
+120D;ETHIOPIC SYLLABLE LE;Lo;0;L;;;;;N;;;;;
+120E;ETHIOPIC SYLLABLE LO;Lo;0;L;;;;;N;;;;;
+120F;ETHIOPIC SYLLABLE LWA;Lo;0;L;;;;;N;;;;;
+1210;ETHIOPIC SYLLABLE HHA;Lo;0;L;;;;;N;;;;;
+1211;ETHIOPIC SYLLABLE HHU;Lo;0;L;;;;;N;;;;;
+1212;ETHIOPIC SYLLABLE HHI;Lo;0;L;;;;;N;;;;;
+1213;ETHIOPIC SYLLABLE HHAA;Lo;0;L;;;;;N;;;;;
+1214;ETHIOPIC SYLLABLE HHEE;Lo;0;L;;;;;N;;;;;
+1215;ETHIOPIC SYLLABLE HHE;Lo;0;L;;;;;N;;;;;
+1216;ETHIOPIC SYLLABLE HHO;Lo;0;L;;;;;N;;;;;
+1217;ETHIOPIC SYLLABLE HHWA;Lo;0;L;;;;;N;;;;;
+1218;ETHIOPIC SYLLABLE MA;Lo;0;L;;;;;N;;;;;
+1219;ETHIOPIC SYLLABLE MU;Lo;0;L;;;;;N;;;;;
+121A;ETHIOPIC SYLLABLE MI;Lo;0;L;;;;;N;;;;;
+121B;ETHIOPIC SYLLABLE MAA;Lo;0;L;;;;;N;;;;;
+121C;ETHIOPIC SYLLABLE MEE;Lo;0;L;;;;;N;;;;;
+121D;ETHIOPIC SYLLABLE ME;Lo;0;L;;;;;N;;;;;
+121E;ETHIOPIC SYLLABLE MO;Lo;0;L;;;;;N;;;;;
+121F;ETHIOPIC SYLLABLE MWA;Lo;0;L;;;;;N;;;;;
+1220;ETHIOPIC SYLLABLE SZA;Lo;0;L;;;;;N;;;;;
+1221;ETHIOPIC SYLLABLE SZU;Lo;0;L;;;;;N;;;;;
+1222;ETHIOPIC SYLLABLE SZI;Lo;0;L;;;;;N;;;;;
+1223;ETHIOPIC SYLLABLE SZAA;Lo;0;L;;;;;N;;;;;
+1224;ETHIOPIC SYLLABLE SZEE;Lo;0;L;;;;;N;;;;;
+1225;ETHIOPIC SYLLABLE SZE;Lo;0;L;;;;;N;;;;;
+1226;ETHIOPIC SYLLABLE SZO;Lo;0;L;;;;;N;;;;;
+1227;ETHIOPIC SYLLABLE SZWA;Lo;0;L;;;;;N;;;;;
+1228;ETHIOPIC SYLLABLE RA;Lo;0;L;;;;;N;;;;;
+1229;ETHIOPIC SYLLABLE RU;Lo;0;L;;;;;N;;;;;
+122A;ETHIOPIC SYLLABLE RI;Lo;0;L;;;;;N;;;;;
+122B;ETHIOPIC SYLLABLE RAA;Lo;0;L;;;;;N;;;;;
+122C;ETHIOPIC SYLLABLE REE;Lo;0;L;;;;;N;;;;;
+122D;ETHIOPIC SYLLABLE RE;Lo;0;L;;;;;N;;;;;
+122E;ETHIOPIC SYLLABLE RO;Lo;0;L;;;;;N;;;;;
+122F;ETHIOPIC SYLLABLE RWA;Lo;0;L;;;;;N;;;;;
+1230;ETHIOPIC SYLLABLE SA;Lo;0;L;;;;;N;;;;;
+1231;ETHIOPIC SYLLABLE SU;Lo;0;L;;;;;N;;;;;
+1232;ETHIOPIC SYLLABLE SI;Lo;0;L;;;;;N;;;;;
+1233;ETHIOPIC SYLLABLE SAA;Lo;0;L;;;;;N;;;;;
+1234;ETHIOPIC SYLLABLE SEE;Lo;0;L;;;;;N;;;;;
+1235;ETHIOPIC SYLLABLE SE;Lo;0;L;;;;;N;;;;;
+1236;ETHIOPIC SYLLABLE SO;Lo;0;L;;;;;N;;;;;
+1237;ETHIOPIC SYLLABLE SWA;Lo;0;L;;;;;N;;;;;
+1238;ETHIOPIC SYLLABLE SHA;Lo;0;L;;;;;N;;;;;
+1239;ETHIOPIC SYLLABLE SHU;Lo;0;L;;;;;N;;;;;
+123A;ETHIOPIC SYLLABLE SHI;Lo;0;L;;;;;N;;;;;
+123B;ETHIOPIC SYLLABLE SHAA;Lo;0;L;;;;;N;;;;;
+123C;ETHIOPIC SYLLABLE SHEE;Lo;0;L;;;;;N;;;;;
+123D;ETHIOPIC SYLLABLE SHE;Lo;0;L;;;;;N;;;;;
+123E;ETHIOPIC SYLLABLE SHO;Lo;0;L;;;;;N;;;;;
+123F;ETHIOPIC SYLLABLE SHWA;Lo;0;L;;;;;N;;;;;
+1240;ETHIOPIC SYLLABLE QA;Lo;0;L;;;;;N;;;;;
+1241;ETHIOPIC SYLLABLE QU;Lo;0;L;;;;;N;;;;;
+1242;ETHIOPIC SYLLABLE QI;Lo;0;L;;;;;N;;;;;
+1243;ETHIOPIC SYLLABLE QAA;Lo;0;L;;;;;N;;;;;
+1244;ETHIOPIC SYLLABLE QEE;Lo;0;L;;;;;N;;;;;
+1245;ETHIOPIC SYLLABLE QE;Lo;0;L;;;;;N;;;;;
+1246;ETHIOPIC SYLLABLE QO;Lo;0;L;;;;;N;;;;;
+1248;ETHIOPIC SYLLABLE QWA;Lo;0;L;;;;;N;;;;;
+124A;ETHIOPIC SYLLABLE QWI;Lo;0;L;;;;;N;;;;;
+124B;ETHIOPIC SYLLABLE QWAA;Lo;0;L;;;;;N;;;;;
+124C;ETHIOPIC SYLLABLE QWEE;Lo;0;L;;;;;N;;;;;
+124D;ETHIOPIC SYLLABLE QWE;Lo;0;L;;;;;N;;;;;
+1250;ETHIOPIC SYLLABLE QHA;Lo;0;L;;;;;N;;;;;
+1251;ETHIOPIC SYLLABLE QHU;Lo;0;L;;;;;N;;;;;
+1252;ETHIOPIC SYLLABLE QHI;Lo;0;L;;;;;N;;;;;
+1253;ETHIOPIC SYLLABLE QHAA;Lo;0;L;;;;;N;;;;;
+1254;ETHIOPIC SYLLABLE QHEE;Lo;0;L;;;;;N;;;;;
+1255;ETHIOPIC SYLLABLE QHE;Lo;0;L;;;;;N;;;;;
+1256;ETHIOPIC SYLLABLE QHO;Lo;0;L;;;;;N;;;;;
+1258;ETHIOPIC SYLLABLE QHWA;Lo;0;L;;;;;N;;;;;
+125A;ETHIOPIC SYLLABLE QHWI;Lo;0;L;;;;;N;;;;;
+125B;ETHIOPIC SYLLABLE QHWAA;Lo;0;L;;;;;N;;;;;
+125C;ETHIOPIC SYLLABLE QHWEE;Lo;0;L;;;;;N;;;;;
+125D;ETHIOPIC SYLLABLE QHWE;Lo;0;L;;;;;N;;;;;
+1260;ETHIOPIC SYLLABLE BA;Lo;0;L;;;;;N;;;;;
+1261;ETHIOPIC SYLLABLE BU;Lo;0;L;;;;;N;;;;;
+1262;ETHIOPIC SYLLABLE BI;Lo;0;L;;;;;N;;;;;
+1263;ETHIOPIC SYLLABLE BAA;Lo;0;L;;;;;N;;;;;
+1264;ETHIOPIC SYLLABLE BEE;Lo;0;L;;;;;N;;;;;
+1265;ETHIOPIC SYLLABLE BE;Lo;0;L;;;;;N;;;;;
+1266;ETHIOPIC SYLLABLE BO;Lo;0;L;;;;;N;;;;;
+1267;ETHIOPIC SYLLABLE BWA;Lo;0;L;;;;;N;;;;;
+1268;ETHIOPIC SYLLABLE VA;Lo;0;L;;;;;N;;;;;
+1269;ETHIOPIC SYLLABLE VU;Lo;0;L;;;;;N;;;;;
+126A;ETHIOPIC SYLLABLE VI;Lo;0;L;;;;;N;;;;;
+126B;ETHIOPIC SYLLABLE VAA;Lo;0;L;;;;;N;;;;;
+126C;ETHIOPIC SYLLABLE VEE;Lo;0;L;;;;;N;;;;;
+126D;ETHIOPIC SYLLABLE VE;Lo;0;L;;;;;N;;;;;
+126E;ETHIOPIC SYLLABLE VO;Lo;0;L;;;;;N;;;;;
+126F;ETHIOPIC SYLLABLE VWA;Lo;0;L;;;;;N;;;;;
+1270;ETHIOPIC SYLLABLE TA;Lo;0;L;;;;;N;;;;;
+1271;ETHIOPIC SYLLABLE TU;Lo;0;L;;;;;N;;;;;
+1272;ETHIOPIC SYLLABLE TI;Lo;0;L;;;;;N;;;;;
+1273;ETHIOPIC SYLLABLE TAA;Lo;0;L;;;;;N;;;;;
+1274;ETHIOPIC SYLLABLE TEE;Lo;0;L;;;;;N;;;;;
+1275;ETHIOPIC SYLLABLE TE;Lo;0;L;;;;;N;;;;;
+1276;ETHIOPIC SYLLABLE TO;Lo;0;L;;;;;N;;;;;
+1277;ETHIOPIC SYLLABLE TWA;Lo;0;L;;;;;N;;;;;
+1278;ETHIOPIC SYLLABLE CA;Lo;0;L;;;;;N;;;;;
+1279;ETHIOPIC SYLLABLE CU;Lo;0;L;;;;;N;;;;;
+127A;ETHIOPIC SYLLABLE CI;Lo;0;L;;;;;N;;;;;
+127B;ETHIOPIC SYLLABLE CAA;Lo;0;L;;;;;N;;;;;
+127C;ETHIOPIC SYLLABLE CEE;Lo;0;L;;;;;N;;;;;
+127D;ETHIOPIC SYLLABLE CE;Lo;0;L;;;;;N;;;;;
+127E;ETHIOPIC SYLLABLE CO;Lo;0;L;;;;;N;;;;;
+127F;ETHIOPIC SYLLABLE CWA;Lo;0;L;;;;;N;;;;;
+1280;ETHIOPIC SYLLABLE XA;Lo;0;L;;;;;N;;;;;
+1281;ETHIOPIC SYLLABLE XU;Lo;0;L;;;;;N;;;;;
+1282;ETHIOPIC SYLLABLE XI;Lo;0;L;;;;;N;;;;;
+1283;ETHIOPIC SYLLABLE XAA;Lo;0;L;;;;;N;;;;;
+1284;ETHIOPIC SYLLABLE XEE;Lo;0;L;;;;;N;;;;;
+1285;ETHIOPIC SYLLABLE XE;Lo;0;L;;;;;N;;;;;
+1286;ETHIOPIC SYLLABLE XO;Lo;0;L;;;;;N;;;;;
+1288;ETHIOPIC SYLLABLE XWA;Lo;0;L;;;;;N;;;;;
+128A;ETHIOPIC SYLLABLE XWI;Lo;0;L;;;;;N;;;;;
+128B;ETHIOPIC SYLLABLE XWAA;Lo;0;L;;;;;N;;;;;
+128C;ETHIOPIC SYLLABLE XWEE;Lo;0;L;;;;;N;;;;;
+128D;ETHIOPIC SYLLABLE XWE;Lo;0;L;;;;;N;;;;;
+1290;ETHIOPIC SYLLABLE NA;Lo;0;L;;;;;N;;;;;
+1291;ETHIOPIC SYLLABLE NU;Lo;0;L;;;;;N;;;;;
+1292;ETHIOPIC SYLLABLE NI;Lo;0;L;;;;;N;;;;;
+1293;ETHIOPIC SYLLABLE NAA;Lo;0;L;;;;;N;;;;;
+1294;ETHIOPIC SYLLABLE NEE;Lo;0;L;;;;;N;;;;;
+1295;ETHIOPIC SYLLABLE NE;Lo;0;L;;;;;N;;;;;
+1296;ETHIOPIC SYLLABLE NO;Lo;0;L;;;;;N;;;;;
+1297;ETHIOPIC SYLLABLE NWA;Lo;0;L;;;;;N;;;;;
+1298;ETHIOPIC SYLLABLE NYA;Lo;0;L;;;;;N;;;;;
+1299;ETHIOPIC SYLLABLE NYU;Lo;0;L;;;;;N;;;;;
+129A;ETHIOPIC SYLLABLE NYI;Lo;0;L;;;;;N;;;;;
+129B;ETHIOPIC SYLLABLE NYAA;Lo;0;L;;;;;N;;;;;
+129C;ETHIOPIC SYLLABLE NYEE;Lo;0;L;;;;;N;;;;;
+129D;ETHIOPIC SYLLABLE NYE;Lo;0;L;;;;;N;;;;;
+129E;ETHIOPIC SYLLABLE NYO;Lo;0;L;;;;;N;;;;;
+129F;ETHIOPIC SYLLABLE NYWA;Lo;0;L;;;;;N;;;;;
+12A0;ETHIOPIC SYLLABLE GLOTTAL A;Lo;0;L;;;;;N;;;;;
+12A1;ETHIOPIC SYLLABLE GLOTTAL U;Lo;0;L;;;;;N;;;;;
+12A2;ETHIOPIC SYLLABLE GLOTTAL I;Lo;0;L;;;;;N;;;;;
+12A3;ETHIOPIC SYLLABLE GLOTTAL AA;Lo;0;L;;;;;N;;;;;
+12A4;ETHIOPIC SYLLABLE GLOTTAL EE;Lo;0;L;;;;;N;;;;;
+12A5;ETHIOPIC SYLLABLE GLOTTAL E;Lo;0;L;;;;;N;;;;;
+12A6;ETHIOPIC SYLLABLE GLOTTAL O;Lo;0;L;;;;;N;;;;;
+12A7;ETHIOPIC SYLLABLE GLOTTAL WA;Lo;0;L;;;;;N;;;;;
+12A8;ETHIOPIC SYLLABLE KA;Lo;0;L;;;;;N;;;;;
+12A9;ETHIOPIC SYLLABLE KU;Lo;0;L;;;;;N;;;;;
+12AA;ETHIOPIC SYLLABLE KI;Lo;0;L;;;;;N;;;;;
+12AB;ETHIOPIC SYLLABLE KAA;Lo;0;L;;;;;N;;;;;
+12AC;ETHIOPIC SYLLABLE KEE;Lo;0;L;;;;;N;;;;;
+12AD;ETHIOPIC SYLLABLE KE;Lo;0;L;;;;;N;;;;;
+12AE;ETHIOPIC SYLLABLE KO;Lo;0;L;;;;;N;;;;;
+12B0;ETHIOPIC SYLLABLE KWA;Lo;0;L;;;;;N;;;;;
+12B2;ETHIOPIC SYLLABLE KWI;Lo;0;L;;;;;N;;;;;
+12B3;ETHIOPIC SYLLABLE KWAA;Lo;0;L;;;;;N;;;;;
+12B4;ETHIOPIC SYLLABLE KWEE;Lo;0;L;;;;;N;;;;;
+12B5;ETHIOPIC SYLLABLE KWE;Lo;0;L;;;;;N;;;;;
+12B8;ETHIOPIC SYLLABLE KXA;Lo;0;L;;;;;N;;;;;
+12B9;ETHIOPIC SYLLABLE KXU;Lo;0;L;;;;;N;;;;;
+12BA;ETHIOPIC SYLLABLE KXI;Lo;0;L;;;;;N;;;;;
+12BB;ETHIOPIC SYLLABLE KXAA;Lo;0;L;;;;;N;;;;;
+12BC;ETHIOPIC SYLLABLE KXEE;Lo;0;L;;;;;N;;;;;
+12BD;ETHIOPIC SYLLABLE KXE;Lo;0;L;;;;;N;;;;;
+12BE;ETHIOPIC SYLLABLE KXO;Lo;0;L;;;;;N;;;;;
+12C0;ETHIOPIC SYLLABLE KXWA;Lo;0;L;;;;;N;;;;;
+12C2;ETHIOPIC SYLLABLE KXWI;Lo;0;L;;;;;N;;;;;
+12C3;ETHIOPIC SYLLABLE KXWAA;Lo;0;L;;;;;N;;;;;
+12C4;ETHIOPIC SYLLABLE KXWEE;Lo;0;L;;;;;N;;;;;
+12C5;ETHIOPIC SYLLABLE KXWE;Lo;0;L;;;;;N;;;;;
+12C8;ETHIOPIC SYLLABLE WA;Lo;0;L;;;;;N;;;;;
+12C9;ETHIOPIC SYLLABLE WU;Lo;0;L;;;;;N;;;;;
+12CA;ETHIOPIC SYLLABLE WI;Lo;0;L;;;;;N;;;;;
+12CB;ETHIOPIC SYLLABLE WAA;Lo;0;L;;;;;N;;;;;
+12CC;ETHIOPIC SYLLABLE WEE;Lo;0;L;;;;;N;;;;;
+12CD;ETHIOPIC SYLLABLE WE;Lo;0;L;;;;;N;;;;;
+12CE;ETHIOPIC SYLLABLE WO;Lo;0;L;;;;;N;;;;;
+12D0;ETHIOPIC SYLLABLE PHARYNGEAL A;Lo;0;L;;;;;N;;;;;
+12D1;ETHIOPIC SYLLABLE PHARYNGEAL U;Lo;0;L;;;;;N;;;;;
+12D2;ETHIOPIC SYLLABLE PHARYNGEAL I;Lo;0;L;;;;;N;;;;;
+12D3;ETHIOPIC SYLLABLE PHARYNGEAL AA;Lo;0;L;;;;;N;;;;;
+12D4;ETHIOPIC SYLLABLE PHARYNGEAL EE;Lo;0;L;;;;;N;;;;;
+12D5;ETHIOPIC SYLLABLE PHARYNGEAL E;Lo;0;L;;;;;N;;;;;
+12D6;ETHIOPIC SYLLABLE PHARYNGEAL O;Lo;0;L;;;;;N;;;;;
+12D8;ETHIOPIC SYLLABLE ZA;Lo;0;L;;;;;N;;;;;
+12D9;ETHIOPIC SYLLABLE ZU;Lo;0;L;;;;;N;;;;;
+12DA;ETHIOPIC SYLLABLE ZI;Lo;0;L;;;;;N;;;;;
+12DB;ETHIOPIC SYLLABLE ZAA;Lo;0;L;;;;;N;;;;;
+12DC;ETHIOPIC SYLLABLE ZEE;Lo;0;L;;;;;N;;;;;
+12DD;ETHIOPIC SYLLABLE ZE;Lo;0;L;;;;;N;;;;;
+12DE;ETHIOPIC SYLLABLE ZO;Lo;0;L;;;;;N;;;;;
+12DF;ETHIOPIC SYLLABLE ZWA;Lo;0;L;;;;;N;;;;;
+12E0;ETHIOPIC SYLLABLE ZHA;Lo;0;L;;;;;N;;;;;
+12E1;ETHIOPIC SYLLABLE ZHU;Lo;0;L;;;;;N;;;;;
+12E2;ETHIOPIC SYLLABLE ZHI;Lo;0;L;;;;;N;;;;;
+12E3;ETHIOPIC SYLLABLE ZHAA;Lo;0;L;;;;;N;;;;;
+12E4;ETHIOPIC SYLLABLE ZHEE;Lo;0;L;;;;;N;;;;;
+12E5;ETHIOPIC SYLLABLE ZHE;Lo;0;L;;;;;N;;;;;
+12E6;ETHIOPIC SYLLABLE ZHO;Lo;0;L;;;;;N;;;;;
+12E7;ETHIOPIC SYLLABLE ZHWA;Lo;0;L;;;;;N;;;;;
+12E8;ETHIOPIC SYLLABLE YA;Lo;0;L;;;;;N;;;;;
+12E9;ETHIOPIC SYLLABLE YU;Lo;0;L;;;;;N;;;;;
+12EA;ETHIOPIC SYLLABLE YI;Lo;0;L;;;;;N;;;;;
+12EB;ETHIOPIC SYLLABLE YAA;Lo;0;L;;;;;N;;;;;
+12EC;ETHIOPIC SYLLABLE YEE;Lo;0;L;;;;;N;;;;;
+12ED;ETHIOPIC SYLLABLE YE;Lo;0;L;;;;;N;;;;;
+12EE;ETHIOPIC SYLLABLE YO;Lo;0;L;;;;;N;;;;;
+12F0;ETHIOPIC SYLLABLE DA;Lo;0;L;;;;;N;;;;;
+12F1;ETHIOPIC SYLLABLE DU;Lo;0;L;;;;;N;;;;;
+12F2;ETHIOPIC SYLLABLE DI;Lo;0;L;;;;;N;;;;;
+12F3;ETHIOPIC SYLLABLE DAA;Lo;0;L;;;;;N;;;;;
+12F4;ETHIOPIC SYLLABLE DEE;Lo;0;L;;;;;N;;;;;
+12F5;ETHIOPIC SYLLABLE DE;Lo;0;L;;;;;N;;;;;
+12F6;ETHIOPIC SYLLABLE DO;Lo;0;L;;;;;N;;;;;
+12F7;ETHIOPIC SYLLABLE DWA;Lo;0;L;;;;;N;;;;;
+12F8;ETHIOPIC SYLLABLE DDA;Lo;0;L;;;;;N;;;;;
+12F9;ETHIOPIC SYLLABLE DDU;Lo;0;L;;;;;N;;;;;
+12FA;ETHIOPIC SYLLABLE DDI;Lo;0;L;;;;;N;;;;;
+12FB;ETHIOPIC SYLLABLE DDAA;Lo;0;L;;;;;N;;;;;
+12FC;ETHIOPIC SYLLABLE DDEE;Lo;0;L;;;;;N;;;;;
+12FD;ETHIOPIC SYLLABLE DDE;Lo;0;L;;;;;N;;;;;
+12FE;ETHIOPIC SYLLABLE DDO;Lo;0;L;;;;;N;;;;;
+12FF;ETHIOPIC SYLLABLE DDWA;Lo;0;L;;;;;N;;;;;
+1300;ETHIOPIC SYLLABLE JA;Lo;0;L;;;;;N;;;;;
+1301;ETHIOPIC SYLLABLE JU;Lo;0;L;;;;;N;;;;;
+1302;ETHIOPIC SYLLABLE JI;Lo;0;L;;;;;N;;;;;
+1303;ETHIOPIC SYLLABLE JAA;Lo;0;L;;;;;N;;;;;
+1304;ETHIOPIC SYLLABLE JEE;Lo;0;L;;;;;N;;;;;
+1305;ETHIOPIC SYLLABLE JE;Lo;0;L;;;;;N;;;;;
+1306;ETHIOPIC SYLLABLE JO;Lo;0;L;;;;;N;;;;;
+1307;ETHIOPIC SYLLABLE JWA;Lo;0;L;;;;;N;;;;;
+1308;ETHIOPIC SYLLABLE GA;Lo;0;L;;;;;N;;;;;
+1309;ETHIOPIC SYLLABLE GU;Lo;0;L;;;;;N;;;;;
+130A;ETHIOPIC SYLLABLE GI;Lo;0;L;;;;;N;;;;;
+130B;ETHIOPIC SYLLABLE GAA;Lo;0;L;;;;;N;;;;;
+130C;ETHIOPIC SYLLABLE GEE;Lo;0;L;;;;;N;;;;;
+130D;ETHIOPIC SYLLABLE GE;Lo;0;L;;;;;N;;;;;
+130E;ETHIOPIC SYLLABLE GO;Lo;0;L;;;;;N;;;;;
+1310;ETHIOPIC SYLLABLE GWA;Lo;0;L;;;;;N;;;;;
+1312;ETHIOPIC SYLLABLE GWI;Lo;0;L;;;;;N;;;;;
+1313;ETHIOPIC SYLLABLE GWAA;Lo;0;L;;;;;N;;;;;
+1314;ETHIOPIC SYLLABLE GWEE;Lo;0;L;;;;;N;;;;;
+1315;ETHIOPIC SYLLABLE GWE;Lo;0;L;;;;;N;;;;;
+1318;ETHIOPIC SYLLABLE GGA;Lo;0;L;;;;;N;;;;;
+1319;ETHIOPIC SYLLABLE GGU;Lo;0;L;;;;;N;;;;;
+131A;ETHIOPIC SYLLABLE GGI;Lo;0;L;;;;;N;;;;;
+131B;ETHIOPIC SYLLABLE GGAA;Lo;0;L;;;;;N;;;;;
+131C;ETHIOPIC SYLLABLE GGEE;Lo;0;L;;;;;N;;;;;
+131D;ETHIOPIC SYLLABLE GGE;Lo;0;L;;;;;N;;;;;
+131E;ETHIOPIC SYLLABLE GGO;Lo;0;L;;;;;N;;;;;
+1320;ETHIOPIC SYLLABLE THA;Lo;0;L;;;;;N;;;;;
+1321;ETHIOPIC SYLLABLE THU;Lo;0;L;;;;;N;;;;;
+1322;ETHIOPIC SYLLABLE THI;Lo;0;L;;;;;N;;;;;
+1323;ETHIOPIC SYLLABLE THAA;Lo;0;L;;;;;N;;;;;
+1324;ETHIOPIC SYLLABLE THEE;Lo;0;L;;;;;N;;;;;
+1325;ETHIOPIC SYLLABLE THE;Lo;0;L;;;;;N;;;;;
+1326;ETHIOPIC SYLLABLE THO;Lo;0;L;;;;;N;;;;;
+1327;ETHIOPIC SYLLABLE THWA;Lo;0;L;;;;;N;;;;;
+1328;ETHIOPIC SYLLABLE CHA;Lo;0;L;;;;;N;;;;;
+1329;ETHIOPIC SYLLABLE CHU;Lo;0;L;;;;;N;;;;;
+132A;ETHIOPIC SYLLABLE CHI;Lo;0;L;;;;;N;;;;;
+132B;ETHIOPIC SYLLABLE CHAA;Lo;0;L;;;;;N;;;;;
+132C;ETHIOPIC SYLLABLE CHEE;Lo;0;L;;;;;N;;;;;
+132D;ETHIOPIC SYLLABLE CHE;Lo;0;L;;;;;N;;;;;
+132E;ETHIOPIC SYLLABLE CHO;Lo;0;L;;;;;N;;;;;
+132F;ETHIOPIC SYLLABLE CHWA;Lo;0;L;;;;;N;;;;;
+1330;ETHIOPIC SYLLABLE PHA;Lo;0;L;;;;;N;;;;;
+1331;ETHIOPIC SYLLABLE PHU;Lo;0;L;;;;;N;;;;;
+1332;ETHIOPIC SYLLABLE PHI;Lo;0;L;;;;;N;;;;;
+1333;ETHIOPIC SYLLABLE PHAA;Lo;0;L;;;;;N;;;;;
+1334;ETHIOPIC SYLLABLE PHEE;Lo;0;L;;;;;N;;;;;
+1335;ETHIOPIC SYLLABLE PHE;Lo;0;L;;;;;N;;;;;
+1336;ETHIOPIC SYLLABLE PHO;Lo;0;L;;;;;N;;;;;
+1337;ETHIOPIC SYLLABLE PHWA;Lo;0;L;;;;;N;;;;;
+1338;ETHIOPIC SYLLABLE TSA;Lo;0;L;;;;;N;;;;;
+1339;ETHIOPIC SYLLABLE TSU;Lo;0;L;;;;;N;;;;;
+133A;ETHIOPIC SYLLABLE TSI;Lo;0;L;;;;;N;;;;;
+133B;ETHIOPIC SYLLABLE TSAA;Lo;0;L;;;;;N;;;;;
+133C;ETHIOPIC SYLLABLE TSEE;Lo;0;L;;;;;N;;;;;
+133D;ETHIOPIC SYLLABLE TSE;Lo;0;L;;;;;N;;;;;
+133E;ETHIOPIC SYLLABLE TSO;Lo;0;L;;;;;N;;;;;
+133F;ETHIOPIC SYLLABLE TSWA;Lo;0;L;;;;;N;;;;;
+1340;ETHIOPIC SYLLABLE TZA;Lo;0;L;;;;;N;;;;;
+1341;ETHIOPIC SYLLABLE TZU;Lo;0;L;;;;;N;;;;;
+1342;ETHIOPIC SYLLABLE TZI;Lo;0;L;;;;;N;;;;;
+1343;ETHIOPIC SYLLABLE TZAA;Lo;0;L;;;;;N;;;;;
+1344;ETHIOPIC SYLLABLE TZEE;Lo;0;L;;;;;N;;;;;
+1345;ETHIOPIC SYLLABLE TZE;Lo;0;L;;;;;N;;;;;
+1346;ETHIOPIC SYLLABLE TZO;Lo;0;L;;;;;N;;;;;
+1348;ETHIOPIC SYLLABLE FA;Lo;0;L;;;;;N;;;;;
+1349;ETHIOPIC SYLLABLE FU;Lo;0;L;;;;;N;;;;;
+134A;ETHIOPIC SYLLABLE FI;Lo;0;L;;;;;N;;;;;
+134B;ETHIOPIC SYLLABLE FAA;Lo;0;L;;;;;N;;;;;
+134C;ETHIOPIC SYLLABLE FEE;Lo;0;L;;;;;N;;;;;
+134D;ETHIOPIC SYLLABLE FE;Lo;0;L;;;;;N;;;;;
+134E;ETHIOPIC SYLLABLE FO;Lo;0;L;;;;;N;;;;;
+134F;ETHIOPIC SYLLABLE FWA;Lo;0;L;;;;;N;;;;;
+1350;ETHIOPIC SYLLABLE PA;Lo;0;L;;;;;N;;;;;
+1351;ETHIOPIC SYLLABLE PU;Lo;0;L;;;;;N;;;;;
+1352;ETHIOPIC SYLLABLE PI;Lo;0;L;;;;;N;;;;;
+1353;ETHIOPIC SYLLABLE PAA;Lo;0;L;;;;;N;;;;;
+1354;ETHIOPIC SYLLABLE PEE;Lo;0;L;;;;;N;;;;;
+1355;ETHIOPIC SYLLABLE PE;Lo;0;L;;;;;N;;;;;
+1356;ETHIOPIC SYLLABLE PO;Lo;0;L;;;;;N;;;;;
+1357;ETHIOPIC SYLLABLE PWA;Lo;0;L;;;;;N;;;;;
+1358;ETHIOPIC SYLLABLE RYA;Lo;0;L;;;;;N;;;;;
+1359;ETHIOPIC SYLLABLE MYA;Lo;0;L;;;;;N;;;;;
+135A;ETHIOPIC SYLLABLE FYA;Lo;0;L;;;;;N;;;;;
+1361;ETHIOPIC WORDSPACE;Po;0;L;;;;;N;;;;;
+1362;ETHIOPIC FULL STOP;Po;0;L;;;;;N;;;;;
+1363;ETHIOPIC COMMA;Po;0;L;;;;;N;;;;;
+1364;ETHIOPIC SEMICOLON;Po;0;L;;;;;N;;;;;
+1365;ETHIOPIC COLON;Po;0;L;;;;;N;;;;;
+1366;ETHIOPIC PREFACE COLON;Po;0;L;;;;;N;;;;;
+1367;ETHIOPIC QUESTION MARK;Po;0;L;;;;;N;;;;;
+1368;ETHIOPIC PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;;
+1369;ETHIOPIC DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+136A;ETHIOPIC DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+136B;ETHIOPIC DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+136C;ETHIOPIC DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+136D;ETHIOPIC DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+136E;ETHIOPIC DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+136F;ETHIOPIC DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1370;ETHIOPIC DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1371;ETHIOPIC DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1372;ETHIOPIC NUMBER TEN;No;0;L;;;;10;N;;;;;
+1373;ETHIOPIC NUMBER TWENTY;No;0;L;;;;20;N;;;;;
+1374;ETHIOPIC NUMBER THIRTY;No;0;L;;;;30;N;;;;;
+1375;ETHIOPIC NUMBER FORTY;No;0;L;;;;40;N;;;;;
+1376;ETHIOPIC NUMBER FIFTY;No;0;L;;;;50;N;;;;;
+1377;ETHIOPIC NUMBER SIXTY;No;0;L;;;;60;N;;;;;
+1378;ETHIOPIC NUMBER SEVENTY;No;0;L;;;;70;N;;;;;
+1379;ETHIOPIC NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
+137A;ETHIOPIC NUMBER NINETY;No;0;L;;;;90;N;;;;;
+137B;ETHIOPIC NUMBER HUNDRED;No;0;L;;;;100;N;;;;;
+137C;ETHIOPIC NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;;
+13A0;CHEROKEE LETTER A;Lo;0;L;;;;;N;;;;;
+13A1;CHEROKEE LETTER E;Lo;0;L;;;;;N;;;;;
+13A2;CHEROKEE LETTER I;Lo;0;L;;;;;N;;;;;
+13A3;CHEROKEE LETTER O;Lo;0;L;;;;;N;;;;;
+13A4;CHEROKEE LETTER U;Lo;0;L;;;;;N;;;;;
+13A5;CHEROKEE LETTER V;Lo;0;L;;;;;N;;;;;
+13A6;CHEROKEE LETTER GA;Lo;0;L;;;;;N;;;;;
+13A7;CHEROKEE LETTER KA;Lo;0;L;;;;;N;;;;;
+13A8;CHEROKEE LETTER GE;Lo;0;L;;;;;N;;;;;
+13A9;CHEROKEE LETTER GI;Lo;0;L;;;;;N;;;;;
+13AA;CHEROKEE LETTER GO;Lo;0;L;;;;;N;;;;;
+13AB;CHEROKEE LETTER GU;Lo;0;L;;;;;N;;;;;
+13AC;CHEROKEE LETTER GV;Lo;0;L;;;;;N;;;;;
+13AD;CHEROKEE LETTER HA;Lo;0;L;;;;;N;;;;;
+13AE;CHEROKEE LETTER HE;Lo;0;L;;;;;N;;;;;
+13AF;CHEROKEE LETTER HI;Lo;0;L;;;;;N;;;;;
+13B0;CHEROKEE LETTER HO;Lo;0;L;;;;;N;;;;;
+13B1;CHEROKEE LETTER HU;Lo;0;L;;;;;N;;;;;
+13B2;CHEROKEE LETTER HV;Lo;0;L;;;;;N;;;;;
+13B3;CHEROKEE LETTER LA;Lo;0;L;;;;;N;;;;;
+13B4;CHEROKEE LETTER LE;Lo;0;L;;;;;N;;;;;
+13B5;CHEROKEE LETTER LI;Lo;0;L;;;;;N;;;;;
+13B6;CHEROKEE LETTER LO;Lo;0;L;;;;;N;;;;;
+13B7;CHEROKEE LETTER LU;Lo;0;L;;;;;N;;;;;
+13B8;CHEROKEE LETTER LV;Lo;0;L;;;;;N;;;;;
+13B9;CHEROKEE LETTER MA;Lo;0;L;;;;;N;;;;;
+13BA;CHEROKEE LETTER ME;Lo;0;L;;;;;N;;;;;
+13BB;CHEROKEE LETTER MI;Lo;0;L;;;;;N;;;;;
+13BC;CHEROKEE LETTER MO;Lo;0;L;;;;;N;;;;;
+13BD;CHEROKEE LETTER MU;Lo;0;L;;;;;N;;;;;
+13BE;CHEROKEE LETTER NA;Lo;0;L;;;;;N;;;;;
+13BF;CHEROKEE LETTER HNA;Lo;0;L;;;;;N;;;;;
+13C0;CHEROKEE LETTER NAH;Lo;0;L;;;;;N;;;;;
+13C1;CHEROKEE LETTER NE;Lo;0;L;;;;;N;;;;;
+13C2;CHEROKEE LETTER NI;Lo;0;L;;;;;N;;;;;
+13C3;CHEROKEE LETTER NO;Lo;0;L;;;;;N;;;;;
+13C4;CHEROKEE LETTER NU;Lo;0;L;;;;;N;;;;;
+13C5;CHEROKEE LETTER NV;Lo;0;L;;;;;N;;;;;
+13C6;CHEROKEE LETTER QUA;Lo;0;L;;;;;N;;;;;
+13C7;CHEROKEE LETTER QUE;Lo;0;L;;;;;N;;;;;
+13C8;CHEROKEE LETTER QUI;Lo;0;L;;;;;N;;;;;
+13C9;CHEROKEE LETTER QUO;Lo;0;L;;;;;N;;;;;
+13CA;CHEROKEE LETTER QUU;Lo;0;L;;;;;N;;;;;
+13CB;CHEROKEE LETTER QUV;Lo;0;L;;;;;N;;;;;
+13CC;CHEROKEE LETTER SA;Lo;0;L;;;;;N;;;;;
+13CD;CHEROKEE LETTER S;Lo;0;L;;;;;N;;;;;
+13CE;CHEROKEE LETTER SE;Lo;0;L;;;;;N;;;;;
+13CF;CHEROKEE LETTER SI;Lo;0;L;;;;;N;;;;;
+13D0;CHEROKEE LETTER SO;Lo;0;L;;;;;N;;;;;
+13D1;CHEROKEE LETTER SU;Lo;0;L;;;;;N;;;;;
+13D2;CHEROKEE LETTER SV;Lo;0;L;;;;;N;;;;;
+13D3;CHEROKEE LETTER DA;Lo;0;L;;;;;N;;;;;
+13D4;CHEROKEE LETTER TA;Lo;0;L;;;;;N;;;;;
+13D5;CHEROKEE LETTER DE;Lo;0;L;;;;;N;;;;;
+13D6;CHEROKEE LETTER TE;Lo;0;L;;;;;N;;;;;
+13D7;CHEROKEE LETTER DI;Lo;0;L;;;;;N;;;;;
+13D8;CHEROKEE LETTER TI;Lo;0;L;;;;;N;;;;;
+13D9;CHEROKEE LETTER DO;Lo;0;L;;;;;N;;;;;
+13DA;CHEROKEE LETTER DU;Lo;0;L;;;;;N;;;;;
+13DB;CHEROKEE LETTER DV;Lo;0;L;;;;;N;;;;;
+13DC;CHEROKEE LETTER DLA;Lo;0;L;;;;;N;;;;;
+13DD;CHEROKEE LETTER TLA;Lo;0;L;;;;;N;;;;;
+13DE;CHEROKEE LETTER TLE;Lo;0;L;;;;;N;;;;;
+13DF;CHEROKEE LETTER TLI;Lo;0;L;;;;;N;;;;;
+13E0;CHEROKEE LETTER TLO;Lo;0;L;;;;;N;;;;;
+13E1;CHEROKEE LETTER TLU;Lo;0;L;;;;;N;;;;;
+13E2;CHEROKEE LETTER TLV;Lo;0;L;;;;;N;;;;;
+13E3;CHEROKEE LETTER TSA;Lo;0;L;;;;;N;;;;;
+13E4;CHEROKEE LETTER TSE;Lo;0;L;;;;;N;;;;;
+13E5;CHEROKEE LETTER TSI;Lo;0;L;;;;;N;;;;;
+13E6;CHEROKEE LETTER TSO;Lo;0;L;;;;;N;;;;;
+13E7;CHEROKEE LETTER TSU;Lo;0;L;;;;;N;;;;;
+13E8;CHEROKEE LETTER TSV;Lo;0;L;;;;;N;;;;;
+13E9;CHEROKEE LETTER WA;Lo;0;L;;;;;N;;;;;
+13EA;CHEROKEE LETTER WE;Lo;0;L;;;;;N;;;;;
+13EB;CHEROKEE LETTER WI;Lo;0;L;;;;;N;;;;;
+13EC;CHEROKEE LETTER WO;Lo;0;L;;;;;N;;;;;
+13ED;CHEROKEE LETTER WU;Lo;0;L;;;;;N;;;;;
+13EE;CHEROKEE LETTER WV;Lo;0;L;;;;;N;;;;;
+13EF;CHEROKEE LETTER YA;Lo;0;L;;;;;N;;;;;
+13F0;CHEROKEE LETTER YE;Lo;0;L;;;;;N;;;;;
+13F1;CHEROKEE LETTER YI;Lo;0;L;;;;;N;;;;;
+13F2;CHEROKEE LETTER YO;Lo;0;L;;;;;N;;;;;
+13F3;CHEROKEE LETTER YU;Lo;0;L;;;;;N;;;;;
+13F4;CHEROKEE LETTER YV;Lo;0;L;;;;;N;;;;;
+1401;CANADIAN SYLLABICS E;Lo;0;L;;;;;N;;;;;
+1402;CANADIAN SYLLABICS AAI;Lo;0;L;;;;;N;;;;;
+1403;CANADIAN SYLLABICS I;Lo;0;L;;;;;N;;;;;
+1404;CANADIAN SYLLABICS II;Lo;0;L;;;;;N;;;;;
+1405;CANADIAN SYLLABICS O;Lo;0;L;;;;;N;;;;;
+1406;CANADIAN SYLLABICS OO;Lo;0;L;;;;;N;;;;;
+1407;CANADIAN SYLLABICS Y-CREE OO;Lo;0;L;;;;;N;;;;;
+1408;CANADIAN SYLLABICS CARRIER EE;Lo;0;L;;;;;N;;;;;
+1409;CANADIAN SYLLABICS CARRIER I;Lo;0;L;;;;;N;;;;;
+140A;CANADIAN SYLLABICS A;Lo;0;L;;;;;N;;;;;
+140B;CANADIAN SYLLABICS AA;Lo;0;L;;;;;N;;;;;
+140C;CANADIAN SYLLABICS WE;Lo;0;L;;;;;N;;;;;
+140D;CANADIAN SYLLABICS WEST-CREE WE;Lo;0;L;;;;;N;;;;;
+140E;CANADIAN SYLLABICS WI;Lo;0;L;;;;;N;;;;;
+140F;CANADIAN SYLLABICS WEST-CREE WI;Lo;0;L;;;;;N;;;;;
+1410;CANADIAN SYLLABICS WII;Lo;0;L;;;;;N;;;;;
+1411;CANADIAN SYLLABICS WEST-CREE WII;Lo;0;L;;;;;N;;;;;
+1412;CANADIAN SYLLABICS WO;Lo;0;L;;;;;N;;;;;
+1413;CANADIAN SYLLABICS WEST-CREE WO;Lo;0;L;;;;;N;;;;;
+1414;CANADIAN SYLLABICS WOO;Lo;0;L;;;;;N;;;;;
+1415;CANADIAN SYLLABICS WEST-CREE WOO;Lo;0;L;;;;;N;;;;;
+1416;CANADIAN SYLLABICS NASKAPI WOO;Lo;0;L;;;;;N;;;;;
+1417;CANADIAN SYLLABICS WA;Lo;0;L;;;;;N;;;;;
+1418;CANADIAN SYLLABICS WEST-CREE WA;Lo;0;L;;;;;N;;;;;
+1419;CANADIAN SYLLABICS WAA;Lo;0;L;;;;;N;;;;;
+141A;CANADIAN SYLLABICS WEST-CREE WAA;Lo;0;L;;;;;N;;;;;
+141B;CANADIAN SYLLABICS NASKAPI WAA;Lo;0;L;;;;;N;;;;;
+141C;CANADIAN SYLLABICS AI;Lo;0;L;;;;;N;;;;;
+141D;CANADIAN SYLLABICS Y-CREE W;Lo;0;L;;;;;N;;;;;
+141E;CANADIAN SYLLABICS GLOTTAL STOP;Lo;0;L;;;;;N;;;;;
+141F;CANADIAN SYLLABICS FINAL ACUTE;Lo;0;L;;;;;N;;;;;
+1420;CANADIAN SYLLABICS FINAL GRAVE;Lo;0;L;;;;;N;;;;;
+1421;CANADIAN SYLLABICS FINAL BOTTOM HALF RING;Lo;0;L;;;;;N;;;;;
+1422;CANADIAN SYLLABICS FINAL TOP HALF RING;Lo;0;L;;;;;N;;;;;
+1423;CANADIAN SYLLABICS FINAL RIGHT HALF RING;Lo;0;L;;;;;N;;;;;
+1424;CANADIAN SYLLABICS FINAL RING;Lo;0;L;;;;;N;;;;;
+1425;CANADIAN SYLLABICS FINAL DOUBLE ACUTE;Lo;0;L;;;;;N;;;;;
+1426;CANADIAN SYLLABICS FINAL DOUBLE SHORT VERTICAL STROKES;Lo;0;L;;;;;N;;;;;
+1427;CANADIAN SYLLABICS FINAL MIDDLE DOT;Lo;0;L;;;;;N;;;;;
+1428;CANADIAN SYLLABICS FINAL SHORT HORIZONTAL STROKE;Lo;0;L;;;;;N;;;;;
+1429;CANADIAN SYLLABICS FINAL PLUS;Lo;0;L;;;;;N;;;;;
+142A;CANADIAN SYLLABICS FINAL DOWN TACK;Lo;0;L;;;;;N;;;;;
+142B;CANADIAN SYLLABICS EN;Lo;0;L;;;;;N;;;;;
+142C;CANADIAN SYLLABICS IN;Lo;0;L;;;;;N;;;;;
+142D;CANADIAN SYLLABICS ON;Lo;0;L;;;;;N;;;;;
+142E;CANADIAN SYLLABICS AN;Lo;0;L;;;;;N;;;;;
+142F;CANADIAN SYLLABICS PE;Lo;0;L;;;;;N;;;;;
+1430;CANADIAN SYLLABICS PAAI;Lo;0;L;;;;;N;;;;;
+1431;CANADIAN SYLLABICS PI;Lo;0;L;;;;;N;;;;;
+1432;CANADIAN SYLLABICS PII;Lo;0;L;;;;;N;;;;;
+1433;CANADIAN SYLLABICS PO;Lo;0;L;;;;;N;;;;;
+1434;CANADIAN SYLLABICS POO;Lo;0;L;;;;;N;;;;;
+1435;CANADIAN SYLLABICS Y-CREE POO;Lo;0;L;;;;;N;;;;;
+1436;CANADIAN SYLLABICS CARRIER HEE;Lo;0;L;;;;;N;;;;;
+1437;CANADIAN SYLLABICS CARRIER HI;Lo;0;L;;;;;N;;;;;
+1438;CANADIAN SYLLABICS PA;Lo;0;L;;;;;N;;;;;
+1439;CANADIAN SYLLABICS PAA;Lo;0;L;;;;;N;;;;;
+143A;CANADIAN SYLLABICS PWE;Lo;0;L;;;;;N;;;;;
+143B;CANADIAN SYLLABICS WEST-CREE PWE;Lo;0;L;;;;;N;;;;;
+143C;CANADIAN SYLLABICS PWI;Lo;0;L;;;;;N;;;;;
+143D;CANADIAN SYLLABICS WEST-CREE PWI;Lo;0;L;;;;;N;;;;;
+143E;CANADIAN SYLLABICS PWII;Lo;0;L;;;;;N;;;;;
+143F;CANADIAN SYLLABICS WEST-CREE PWII;Lo;0;L;;;;;N;;;;;
+1440;CANADIAN SYLLABICS PWO;Lo;0;L;;;;;N;;;;;
+1441;CANADIAN SYLLABICS WEST-CREE PWO;Lo;0;L;;;;;N;;;;;
+1442;CANADIAN SYLLABICS PWOO;Lo;0;L;;;;;N;;;;;
+1443;CANADIAN SYLLABICS WEST-CREE PWOO;Lo;0;L;;;;;N;;;;;
+1444;CANADIAN SYLLABICS PWA;Lo;0;L;;;;;N;;;;;
+1445;CANADIAN SYLLABICS WEST-CREE PWA;Lo;0;L;;;;;N;;;;;
+1446;CANADIAN SYLLABICS PWAA;Lo;0;L;;;;;N;;;;;
+1447;CANADIAN SYLLABICS WEST-CREE PWAA;Lo;0;L;;;;;N;;;;;
+1448;CANADIAN SYLLABICS Y-CREE PWAA;Lo;0;L;;;;;N;;;;;
+1449;CANADIAN SYLLABICS P;Lo;0;L;;;;;N;;;;;
+144A;CANADIAN SYLLABICS WEST-CREE P;Lo;0;L;;;;;N;;;;;
+144B;CANADIAN SYLLABICS CARRIER H;Lo;0;L;;;;;N;;;;;
+144C;CANADIAN SYLLABICS TE;Lo;0;L;;;;;N;;;;;
+144D;CANADIAN SYLLABICS TAAI;Lo;0;L;;;;;N;;;;;
+144E;CANADIAN SYLLABICS TI;Lo;0;L;;;;;N;;;;;
+144F;CANADIAN SYLLABICS TII;Lo;0;L;;;;;N;;;;;
+1450;CANADIAN SYLLABICS TO;Lo;0;L;;;;;N;;;;;
+1451;CANADIAN SYLLABICS TOO;Lo;0;L;;;;;N;;;;;
+1452;CANADIAN SYLLABICS Y-CREE TOO;Lo;0;L;;;;;N;;;;;
+1453;CANADIAN SYLLABICS CARRIER DEE;Lo;0;L;;;;;N;;;;;
+1454;CANADIAN SYLLABICS CARRIER DI;Lo;0;L;;;;;N;;;;;
+1455;CANADIAN SYLLABICS TA;Lo;0;L;;;;;N;;;;;
+1456;CANADIAN SYLLABICS TAA;Lo;0;L;;;;;N;;;;;
+1457;CANADIAN SYLLABICS TWE;Lo;0;L;;;;;N;;;;;
+1458;CANADIAN SYLLABICS WEST-CREE TWE;Lo;0;L;;;;;N;;;;;
+1459;CANADIAN SYLLABICS TWI;Lo;0;L;;;;;N;;;;;
+145A;CANADIAN SYLLABICS WEST-CREE TWI;Lo;0;L;;;;;N;;;;;
+145B;CANADIAN SYLLABICS TWII;Lo;0;L;;;;;N;;;;;
+145C;CANADIAN SYLLABICS WEST-CREE TWII;Lo;0;L;;;;;N;;;;;
+145D;CANADIAN SYLLABICS TWO;Lo;0;L;;;;;N;;;;;
+145E;CANADIAN SYLLABICS WEST-CREE TWO;Lo;0;L;;;;;N;;;;;
+145F;CANADIAN SYLLABICS TWOO;Lo;0;L;;;;;N;;;;;
+1460;CANADIAN SYLLABICS WEST-CREE TWOO;Lo;0;L;;;;;N;;;;;
+1461;CANADIAN SYLLABICS TWA;Lo;0;L;;;;;N;;;;;
+1462;CANADIAN SYLLABICS WEST-CREE TWA;Lo;0;L;;;;;N;;;;;
+1463;CANADIAN SYLLABICS TWAA;Lo;0;L;;;;;N;;;;;
+1464;CANADIAN SYLLABICS WEST-CREE TWAA;Lo;0;L;;;;;N;;;;;
+1465;CANADIAN SYLLABICS NASKAPI TWAA;Lo;0;L;;;;;N;;;;;
+1466;CANADIAN SYLLABICS T;Lo;0;L;;;;;N;;;;;
+1467;CANADIAN SYLLABICS TTE;Lo;0;L;;;;;N;;;;;
+1468;CANADIAN SYLLABICS TTI;Lo;0;L;;;;;N;;;;;
+1469;CANADIAN SYLLABICS TTO;Lo;0;L;;;;;N;;;;;
+146A;CANADIAN SYLLABICS TTA;Lo;0;L;;;;;N;;;;;
+146B;CANADIAN SYLLABICS KE;Lo;0;L;;;;;N;;;;;
+146C;CANADIAN SYLLABICS KAAI;Lo;0;L;;;;;N;;;;;
+146D;CANADIAN SYLLABICS KI;Lo;0;L;;;;;N;;;;;
+146E;CANADIAN SYLLABICS KII;Lo;0;L;;;;;N;;;;;
+146F;CANADIAN SYLLABICS KO;Lo;0;L;;;;;N;;;;;
+1470;CANADIAN SYLLABICS KOO;Lo;0;L;;;;;N;;;;;
+1471;CANADIAN SYLLABICS Y-CREE KOO;Lo;0;L;;;;;N;;;;;
+1472;CANADIAN SYLLABICS KA;Lo;0;L;;;;;N;;;;;
+1473;CANADIAN SYLLABICS KAA;Lo;0;L;;;;;N;;;;;
+1474;CANADIAN SYLLABICS KWE;Lo;0;L;;;;;N;;;;;
+1475;CANADIAN SYLLABICS WEST-CREE KWE;Lo;0;L;;;;;N;;;;;
+1476;CANADIAN SYLLABICS KWI;Lo;0;L;;;;;N;;;;;
+1477;CANADIAN SYLLABICS WEST-CREE KWI;Lo;0;L;;;;;N;;;;;
+1478;CANADIAN SYLLABICS KWII;Lo;0;L;;;;;N;;;;;
+1479;CANADIAN SYLLABICS WEST-CREE KWII;Lo;0;L;;;;;N;;;;;
+147A;CANADIAN SYLLABICS KWO;Lo;0;L;;;;;N;;;;;
+147B;CANADIAN SYLLABICS WEST-CREE KWO;Lo;0;L;;;;;N;;;;;
+147C;CANADIAN SYLLABICS KWOO;Lo;0;L;;;;;N;;;;;
+147D;CANADIAN SYLLABICS WEST-CREE KWOO;Lo;0;L;;;;;N;;;;;
+147E;CANADIAN SYLLABICS KWA;Lo;0;L;;;;;N;;;;;
+147F;CANADIAN SYLLABICS WEST-CREE KWA;Lo;0;L;;;;;N;;;;;
+1480;CANADIAN SYLLABICS KWAA;Lo;0;L;;;;;N;;;;;
+1481;CANADIAN SYLLABICS WEST-CREE KWAA;Lo;0;L;;;;;N;;;;;
+1482;CANADIAN SYLLABICS NASKAPI KWAA;Lo;0;L;;;;;N;;;;;
+1483;CANADIAN SYLLABICS K;Lo;0;L;;;;;N;;;;;
+1484;CANADIAN SYLLABICS KW;Lo;0;L;;;;;N;;;;;
+1485;CANADIAN SYLLABICS SOUTH-SLAVEY KEH;Lo;0;L;;;;;N;;;;;
+1486;CANADIAN SYLLABICS SOUTH-SLAVEY KIH;Lo;0;L;;;;;N;;;;;
+1487;CANADIAN SYLLABICS SOUTH-SLAVEY KOH;Lo;0;L;;;;;N;;;;;
+1488;CANADIAN SYLLABICS SOUTH-SLAVEY KAH;Lo;0;L;;;;;N;;;;;
+1489;CANADIAN SYLLABICS CE;Lo;0;L;;;;;N;;;;;
+148A;CANADIAN SYLLABICS CAAI;Lo;0;L;;;;;N;;;;;
+148B;CANADIAN SYLLABICS CI;Lo;0;L;;;;;N;;;;;
+148C;CANADIAN SYLLABICS CII;Lo;0;L;;;;;N;;;;;
+148D;CANADIAN SYLLABICS CO;Lo;0;L;;;;;N;;;;;
+148E;CANADIAN SYLLABICS COO;Lo;0;L;;;;;N;;;;;
+148F;CANADIAN SYLLABICS Y-CREE COO;Lo;0;L;;;;;N;;;;;
+1490;CANADIAN SYLLABICS CA;Lo;0;L;;;;;N;;;;;
+1491;CANADIAN SYLLABICS CAA;Lo;0;L;;;;;N;;;;;
+1492;CANADIAN SYLLABICS CWE;Lo;0;L;;;;;N;;;;;
+1493;CANADIAN SYLLABICS WEST-CREE CWE;Lo;0;L;;;;;N;;;;;
+1494;CANADIAN SYLLABICS CWI;Lo;0;L;;;;;N;;;;;
+1495;CANADIAN SYLLABICS WEST-CREE CWI;Lo;0;L;;;;;N;;;;;
+1496;CANADIAN SYLLABICS CWII;Lo;0;L;;;;;N;;;;;
+1497;CANADIAN SYLLABICS WEST-CREE CWII;Lo;0;L;;;;;N;;;;;
+1498;CANADIAN SYLLABICS CWO;Lo;0;L;;;;;N;;;;;
+1499;CANADIAN SYLLABICS WEST-CREE CWO;Lo;0;L;;;;;N;;;;;
+149A;CANADIAN SYLLABICS CWOO;Lo;0;L;;;;;N;;;;;
+149B;CANADIAN SYLLABICS WEST-CREE CWOO;Lo;0;L;;;;;N;;;;;
+149C;CANADIAN SYLLABICS CWA;Lo;0;L;;;;;N;;;;;
+149D;CANADIAN SYLLABICS WEST-CREE CWA;Lo;0;L;;;;;N;;;;;
+149E;CANADIAN SYLLABICS CWAA;Lo;0;L;;;;;N;;;;;
+149F;CANADIAN SYLLABICS WEST-CREE CWAA;Lo;0;L;;;;;N;;;;;
+14A0;CANADIAN SYLLABICS NASKAPI CWAA;Lo;0;L;;;;;N;;;;;
+14A1;CANADIAN SYLLABICS C;Lo;0;L;;;;;N;;;;;
+14A2;CANADIAN SYLLABICS SAYISI TH;Lo;0;L;;;;;N;;;;;
+14A3;CANADIAN SYLLABICS ME;Lo;0;L;;;;;N;;;;;
+14A4;CANADIAN SYLLABICS MAAI;Lo;0;L;;;;;N;;;;;
+14A5;CANADIAN SYLLABICS MI;Lo;0;L;;;;;N;;;;;
+14A6;CANADIAN SYLLABICS MII;Lo;0;L;;;;;N;;;;;
+14A7;CANADIAN SYLLABICS MO;Lo;0;L;;;;;N;;;;;
+14A8;CANADIAN SYLLABICS MOO;Lo;0;L;;;;;N;;;;;
+14A9;CANADIAN SYLLABICS Y-CREE MOO;Lo;0;L;;;;;N;;;;;
+14AA;CANADIAN SYLLABICS MA;Lo;0;L;;;;;N;;;;;
+14AB;CANADIAN SYLLABICS MAA;Lo;0;L;;;;;N;;;;;
+14AC;CANADIAN SYLLABICS MWE;Lo;0;L;;;;;N;;;;;
+14AD;CANADIAN SYLLABICS WEST-CREE MWE;Lo;0;L;;;;;N;;;;;
+14AE;CANADIAN SYLLABICS MWI;Lo;0;L;;;;;N;;;;;
+14AF;CANADIAN SYLLABICS WEST-CREE MWI;Lo;0;L;;;;;N;;;;;
+14B0;CANADIAN SYLLABICS MWII;Lo;0;L;;;;;N;;;;;
+14B1;CANADIAN SYLLABICS WEST-CREE MWII;Lo;0;L;;;;;N;;;;;
+14B2;CANADIAN SYLLABICS MWO;Lo;0;L;;;;;N;;;;;
+14B3;CANADIAN SYLLABICS WEST-CREE MWO;Lo;0;L;;;;;N;;;;;
+14B4;CANADIAN SYLLABICS MWOO;Lo;0;L;;;;;N;;;;;
+14B5;CANADIAN SYLLABICS WEST-CREE MWOO;Lo;0;L;;;;;N;;;;;
+14B6;CANADIAN SYLLABICS MWA;Lo;0;L;;;;;N;;;;;
+14B7;CANADIAN SYLLABICS WEST-CREE MWA;Lo;0;L;;;;;N;;;;;
+14B8;CANADIAN SYLLABICS MWAA;Lo;0;L;;;;;N;;;;;
+14B9;CANADIAN SYLLABICS WEST-CREE MWAA;Lo;0;L;;;;;N;;;;;
+14BA;CANADIAN SYLLABICS NASKAPI MWAA;Lo;0;L;;;;;N;;;;;
+14BB;CANADIAN SYLLABICS M;Lo;0;L;;;;;N;;;;;
+14BC;CANADIAN SYLLABICS WEST-CREE M;Lo;0;L;;;;;N;;;;;
+14BD;CANADIAN SYLLABICS MH;Lo;0;L;;;;;N;;;;;
+14BE;CANADIAN SYLLABICS ATHAPASCAN M;Lo;0;L;;;;;N;;;;;
+14BF;CANADIAN SYLLABICS SAYISI M;Lo;0;L;;;;;N;;;;;
+14C0;CANADIAN SYLLABICS NE;Lo;0;L;;;;;N;;;;;
+14C1;CANADIAN SYLLABICS NAAI;Lo;0;L;;;;;N;;;;;
+14C2;CANADIAN SYLLABICS NI;Lo;0;L;;;;;N;;;;;
+14C3;CANADIAN SYLLABICS NII;Lo;0;L;;;;;N;;;;;
+14C4;CANADIAN SYLLABICS NO;Lo;0;L;;;;;N;;;;;
+14C5;CANADIAN SYLLABICS NOO;Lo;0;L;;;;;N;;;;;
+14C6;CANADIAN SYLLABICS Y-CREE NOO;Lo;0;L;;;;;N;;;;;
+14C7;CANADIAN SYLLABICS NA;Lo;0;L;;;;;N;;;;;
+14C8;CANADIAN SYLLABICS NAA;Lo;0;L;;;;;N;;;;;
+14C9;CANADIAN SYLLABICS NWE;Lo;0;L;;;;;N;;;;;
+14CA;CANADIAN SYLLABICS WEST-CREE NWE;Lo;0;L;;;;;N;;;;;
+14CB;CANADIAN SYLLABICS NWA;Lo;0;L;;;;;N;;;;;
+14CC;CANADIAN SYLLABICS WEST-CREE NWA;Lo;0;L;;;;;N;;;;;
+14CD;CANADIAN SYLLABICS NWAA;Lo;0;L;;;;;N;;;;;
+14CE;CANADIAN SYLLABICS WEST-CREE NWAA;Lo;0;L;;;;;N;;;;;
+14CF;CANADIAN SYLLABICS NASKAPI NWAA;Lo;0;L;;;;;N;;;;;
+14D0;CANADIAN SYLLABICS N;Lo;0;L;;;;;N;;;;;
+14D1;CANADIAN SYLLABICS CARRIER NG;Lo;0;L;;;;;N;;;;;
+14D2;CANADIAN SYLLABICS NH;Lo;0;L;;;;;N;;;;;
+14D3;CANADIAN SYLLABICS LE;Lo;0;L;;;;;N;;;;;
+14D4;CANADIAN SYLLABICS LAAI;Lo;0;L;;;;;N;;;;;
+14D5;CANADIAN SYLLABICS LI;Lo;0;L;;;;;N;;;;;
+14D6;CANADIAN SYLLABICS LII;Lo;0;L;;;;;N;;;;;
+14D7;CANADIAN SYLLABICS LO;Lo;0;L;;;;;N;;;;;
+14D8;CANADIAN SYLLABICS LOO;Lo;0;L;;;;;N;;;;;
+14D9;CANADIAN SYLLABICS Y-CREE LOO;Lo;0;L;;;;;N;;;;;
+14DA;CANADIAN SYLLABICS LA;Lo;0;L;;;;;N;;;;;
+14DB;CANADIAN SYLLABICS LAA;Lo;0;L;;;;;N;;;;;
+14DC;CANADIAN SYLLABICS LWE;Lo;0;L;;;;;N;;;;;
+14DD;CANADIAN SYLLABICS WEST-CREE LWE;Lo;0;L;;;;;N;;;;;
+14DE;CANADIAN SYLLABICS LWI;Lo;0;L;;;;;N;;;;;
+14DF;CANADIAN SYLLABICS WEST-CREE LWI;Lo;0;L;;;;;N;;;;;
+14E0;CANADIAN SYLLABICS LWII;Lo;0;L;;;;;N;;;;;
+14E1;CANADIAN SYLLABICS WEST-CREE LWII;Lo;0;L;;;;;N;;;;;
+14E2;CANADIAN SYLLABICS LWO;Lo;0;L;;;;;N;;;;;
+14E3;CANADIAN SYLLABICS WEST-CREE LWO;Lo;0;L;;;;;N;;;;;
+14E4;CANADIAN SYLLABICS LWOO;Lo;0;L;;;;;N;;;;;
+14E5;CANADIAN SYLLABICS WEST-CREE LWOO;Lo;0;L;;;;;N;;;;;
+14E6;CANADIAN SYLLABICS LWA;Lo;0;L;;;;;N;;;;;
+14E7;CANADIAN SYLLABICS WEST-CREE LWA;Lo;0;L;;;;;N;;;;;
+14E8;CANADIAN SYLLABICS LWAA;Lo;0;L;;;;;N;;;;;
+14E9;CANADIAN SYLLABICS WEST-CREE LWAA;Lo;0;L;;;;;N;;;;;
+14EA;CANADIAN SYLLABICS L;Lo;0;L;;;;;N;;;;;
+14EB;CANADIAN SYLLABICS WEST-CREE L;Lo;0;L;;;;;N;;;;;
+14EC;CANADIAN SYLLABICS MEDIAL L;Lo;0;L;;;;;N;;;;;
+14ED;CANADIAN SYLLABICS SE;Lo;0;L;;;;;N;;;;;
+14EE;CANADIAN SYLLABICS SAAI;Lo;0;L;;;;;N;;;;;
+14EF;CANADIAN SYLLABICS SI;Lo;0;L;;;;;N;;;;;
+14F0;CANADIAN SYLLABICS SII;Lo;0;L;;;;;N;;;;;
+14F1;CANADIAN SYLLABICS SO;Lo;0;L;;;;;N;;;;;
+14F2;CANADIAN SYLLABICS SOO;Lo;0;L;;;;;N;;;;;
+14F3;CANADIAN SYLLABICS Y-CREE SOO;Lo;0;L;;;;;N;;;;;
+14F4;CANADIAN SYLLABICS SA;Lo;0;L;;;;;N;;;;;
+14F5;CANADIAN SYLLABICS SAA;Lo;0;L;;;;;N;;;;;
+14F6;CANADIAN SYLLABICS SWE;Lo;0;L;;;;;N;;;;;
+14F7;CANADIAN SYLLABICS WEST-CREE SWE;Lo;0;L;;;;;N;;;;;
+14F8;CANADIAN SYLLABICS SWI;Lo;0;L;;;;;N;;;;;
+14F9;CANADIAN SYLLABICS WEST-CREE SWI;Lo;0;L;;;;;N;;;;;
+14FA;CANADIAN SYLLABICS SWII;Lo;0;L;;;;;N;;;;;
+14FB;CANADIAN SYLLABICS WEST-CREE SWII;Lo;0;L;;;;;N;;;;;
+14FC;CANADIAN SYLLABICS SWO;Lo;0;L;;;;;N;;;;;
+14FD;CANADIAN SYLLABICS WEST-CREE SWO;Lo;0;L;;;;;N;;;;;
+14FE;CANADIAN SYLLABICS SWOO;Lo;0;L;;;;;N;;;;;
+14FF;CANADIAN SYLLABICS WEST-CREE SWOO;Lo;0;L;;;;;N;;;;;
+1500;CANADIAN SYLLABICS SWA;Lo;0;L;;;;;N;;;;;
+1501;CANADIAN SYLLABICS WEST-CREE SWA;Lo;0;L;;;;;N;;;;;
+1502;CANADIAN SYLLABICS SWAA;Lo;0;L;;;;;N;;;;;
+1503;CANADIAN SYLLABICS WEST-CREE SWAA;Lo;0;L;;;;;N;;;;;
+1504;CANADIAN SYLLABICS NASKAPI SWAA;Lo;0;L;;;;;N;;;;;
+1505;CANADIAN SYLLABICS S;Lo;0;L;;;;;N;;;;;
+1506;CANADIAN SYLLABICS ATHAPASCAN S;Lo;0;L;;;;;N;;;;;
+1507;CANADIAN SYLLABICS SW;Lo;0;L;;;;;N;;;;;
+1508;CANADIAN SYLLABICS BLACKFOOT S;Lo;0;L;;;;;N;;;;;
+1509;CANADIAN SYLLABICS MOOSE-CREE SK;Lo;0;L;;;;;N;;;;;
+150A;CANADIAN SYLLABICS NASKAPI SKW;Lo;0;L;;;;;N;;;;;
+150B;CANADIAN SYLLABICS NASKAPI S-W;Lo;0;L;;;;;N;;;;;
+150C;CANADIAN SYLLABICS NASKAPI SPWA;Lo;0;L;;;;;N;;;;;
+150D;CANADIAN SYLLABICS NASKAPI STWA;Lo;0;L;;;;;N;;;;;
+150E;CANADIAN SYLLABICS NASKAPI SKWA;Lo;0;L;;;;;N;;;;;
+150F;CANADIAN SYLLABICS NASKAPI SCWA;Lo;0;L;;;;;N;;;;;
+1510;CANADIAN SYLLABICS SHE;Lo;0;L;;;;;N;;;;;
+1511;CANADIAN SYLLABICS SHI;Lo;0;L;;;;;N;;;;;
+1512;CANADIAN SYLLABICS SHII;Lo;0;L;;;;;N;;;;;
+1513;CANADIAN SYLLABICS SHO;Lo;0;L;;;;;N;;;;;
+1514;CANADIAN SYLLABICS SHOO;Lo;0;L;;;;;N;;;;;
+1515;CANADIAN SYLLABICS SHA;Lo;0;L;;;;;N;;;;;
+1516;CANADIAN SYLLABICS SHAA;Lo;0;L;;;;;N;;;;;
+1517;CANADIAN SYLLABICS SHWE;Lo;0;L;;;;;N;;;;;
+1518;CANADIAN SYLLABICS WEST-CREE SHWE;Lo;0;L;;;;;N;;;;;
+1519;CANADIAN SYLLABICS SHWI;Lo;0;L;;;;;N;;;;;
+151A;CANADIAN SYLLABICS WEST-CREE SHWI;Lo;0;L;;;;;N;;;;;
+151B;CANADIAN SYLLABICS SHWII;Lo;0;L;;;;;N;;;;;
+151C;CANADIAN SYLLABICS WEST-CREE SHWII;Lo;0;L;;;;;N;;;;;
+151D;CANADIAN SYLLABICS SHWO;Lo;0;L;;;;;N;;;;;
+151E;CANADIAN SYLLABICS WEST-CREE SHWO;Lo;0;L;;;;;N;;;;;
+151F;CANADIAN SYLLABICS SHWOO;Lo;0;L;;;;;N;;;;;
+1520;CANADIAN SYLLABICS WEST-CREE SHWOO;Lo;0;L;;;;;N;;;;;
+1521;CANADIAN SYLLABICS SHWA;Lo;0;L;;;;;N;;;;;
+1522;CANADIAN SYLLABICS WEST-CREE SHWA;Lo;0;L;;;;;N;;;;;
+1523;CANADIAN SYLLABICS SHWAA;Lo;0;L;;;;;N;;;;;
+1524;CANADIAN SYLLABICS WEST-CREE SHWAA;Lo;0;L;;;;;N;;;;;
+1525;CANADIAN SYLLABICS SH;Lo;0;L;;;;;N;;;;;
+1526;CANADIAN SYLLABICS YE;Lo;0;L;;;;;N;;;;;
+1527;CANADIAN SYLLABICS YAAI;Lo;0;L;;;;;N;;;;;
+1528;CANADIAN SYLLABICS YI;Lo;0;L;;;;;N;;;;;
+1529;CANADIAN SYLLABICS YII;Lo;0;L;;;;;N;;;;;
+152A;CANADIAN SYLLABICS YO;Lo;0;L;;;;;N;;;;;
+152B;CANADIAN SYLLABICS YOO;Lo;0;L;;;;;N;;;;;
+152C;CANADIAN SYLLABICS Y-CREE YOO;Lo;0;L;;;;;N;;;;;
+152D;CANADIAN SYLLABICS YA;Lo;0;L;;;;;N;;;;;
+152E;CANADIAN SYLLABICS YAA;Lo;0;L;;;;;N;;;;;
+152F;CANADIAN SYLLABICS YWE;Lo;0;L;;;;;N;;;;;
+1530;CANADIAN SYLLABICS WEST-CREE YWE;Lo;0;L;;;;;N;;;;;
+1531;CANADIAN SYLLABICS YWI;Lo;0;L;;;;;N;;;;;
+1532;CANADIAN SYLLABICS WEST-CREE YWI;Lo;0;L;;;;;N;;;;;
+1533;CANADIAN SYLLABICS YWII;Lo;0;L;;;;;N;;;;;
+1534;CANADIAN SYLLABICS WEST-CREE YWII;Lo;0;L;;;;;N;;;;;
+1535;CANADIAN SYLLABICS YWO;Lo;0;L;;;;;N;;;;;
+1536;CANADIAN SYLLABICS WEST-CREE YWO;Lo;0;L;;;;;N;;;;;
+1537;CANADIAN SYLLABICS YWOO;Lo;0;L;;;;;N;;;;;
+1538;CANADIAN SYLLABICS WEST-CREE YWOO;Lo;0;L;;;;;N;;;;;
+1539;CANADIAN SYLLABICS YWA;Lo;0;L;;;;;N;;;;;
+153A;CANADIAN SYLLABICS WEST-CREE YWA;Lo;0;L;;;;;N;;;;;
+153B;CANADIAN SYLLABICS YWAA;Lo;0;L;;;;;N;;;;;
+153C;CANADIAN SYLLABICS WEST-CREE YWAA;Lo;0;L;;;;;N;;;;;
+153D;CANADIAN SYLLABICS NASKAPI YWAA;Lo;0;L;;;;;N;;;;;
+153E;CANADIAN SYLLABICS Y;Lo;0;L;;;;;N;;;;;
+153F;CANADIAN SYLLABICS BIBLE-CREE Y;Lo;0;L;;;;;N;;;;;
+1540;CANADIAN SYLLABICS WEST-CREE Y;Lo;0;L;;;;;N;;;;;
+1541;CANADIAN SYLLABICS SAYISI YI;Lo;0;L;;;;;N;;;;;
+1542;CANADIAN SYLLABICS RE;Lo;0;L;;;;;N;;;;;
+1543;CANADIAN SYLLABICS R-CREE RE;Lo;0;L;;;;;N;;;;;
+1544;CANADIAN SYLLABICS WEST-CREE LE;Lo;0;L;;;;;N;;;;;
+1545;CANADIAN SYLLABICS RAAI;Lo;0;L;;;;;N;;;;;
+1546;CANADIAN SYLLABICS RI;Lo;0;L;;;;;N;;;;;
+1547;CANADIAN SYLLABICS RII;Lo;0;L;;;;;N;;;;;
+1548;CANADIAN SYLLABICS RO;Lo;0;L;;;;;N;;;;;
+1549;CANADIAN SYLLABICS ROO;Lo;0;L;;;;;N;;;;;
+154A;CANADIAN SYLLABICS WEST-CREE LO;Lo;0;L;;;;;N;;;;;
+154B;CANADIAN SYLLABICS RA;Lo;0;L;;;;;N;;;;;
+154C;CANADIAN SYLLABICS RAA;Lo;0;L;;;;;N;;;;;
+154D;CANADIAN SYLLABICS WEST-CREE LA;Lo;0;L;;;;;N;;;;;
+154E;CANADIAN SYLLABICS RWAA;Lo;0;L;;;;;N;;;;;
+154F;CANADIAN SYLLABICS WEST-CREE RWAA;Lo;0;L;;;;;N;;;;;
+1550;CANADIAN SYLLABICS R;Lo;0;L;;;;;N;;;;;
+1551;CANADIAN SYLLABICS WEST-CREE R;Lo;0;L;;;;;N;;;;;
+1552;CANADIAN SYLLABICS MEDIAL R;Lo;0;L;;;;;N;;;;;
+1553;CANADIAN SYLLABICS FE;Lo;0;L;;;;;N;;;;;
+1554;CANADIAN SYLLABICS FAAI;Lo;0;L;;;;;N;;;;;
+1555;CANADIAN SYLLABICS FI;Lo;0;L;;;;;N;;;;;
+1556;CANADIAN SYLLABICS FII;Lo;0;L;;;;;N;;;;;
+1557;CANADIAN SYLLABICS FO;Lo;0;L;;;;;N;;;;;
+1558;CANADIAN SYLLABICS FOO;Lo;0;L;;;;;N;;;;;
+1559;CANADIAN SYLLABICS FA;Lo;0;L;;;;;N;;;;;
+155A;CANADIAN SYLLABICS FAA;Lo;0;L;;;;;N;;;;;
+155B;CANADIAN SYLLABICS FWAA;Lo;0;L;;;;;N;;;;;
+155C;CANADIAN SYLLABICS WEST-CREE FWAA;Lo;0;L;;;;;N;;;;;
+155D;CANADIAN SYLLABICS F;Lo;0;L;;;;;N;;;;;
+155E;CANADIAN SYLLABICS THE;Lo;0;L;;;;;N;;;;;
+155F;CANADIAN SYLLABICS N-CREE THE;Lo;0;L;;;;;N;;;;;
+1560;CANADIAN SYLLABICS THI;Lo;0;L;;;;;N;;;;;
+1561;CANADIAN SYLLABICS N-CREE THI;Lo;0;L;;;;;N;;;;;
+1562;CANADIAN SYLLABICS THII;Lo;0;L;;;;;N;;;;;
+1563;CANADIAN SYLLABICS N-CREE THII;Lo;0;L;;;;;N;;;;;
+1564;CANADIAN SYLLABICS THO;Lo;0;L;;;;;N;;;;;
+1565;CANADIAN SYLLABICS THOO;Lo;0;L;;;;;N;;;;;
+1566;CANADIAN SYLLABICS THA;Lo;0;L;;;;;N;;;;;
+1567;CANADIAN SYLLABICS THAA;Lo;0;L;;;;;N;;;;;
+1568;CANADIAN SYLLABICS THWAA;Lo;0;L;;;;;N;;;;;
+1569;CANADIAN SYLLABICS WEST-CREE THWAA;Lo;0;L;;;;;N;;;;;
+156A;CANADIAN SYLLABICS TH;Lo;0;L;;;;;N;;;;;
+156B;CANADIAN SYLLABICS TTHE;Lo;0;L;;;;;N;;;;;
+156C;CANADIAN SYLLABICS TTHI;Lo;0;L;;;;;N;;;;;
+156D;CANADIAN SYLLABICS TTHO;Lo;0;L;;;;;N;;;;;
+156E;CANADIAN SYLLABICS TTHA;Lo;0;L;;;;;N;;;;;
+156F;CANADIAN SYLLABICS TTH;Lo;0;L;;;;;N;;;;;
+1570;CANADIAN SYLLABICS TYE;Lo;0;L;;;;;N;;;;;
+1571;CANADIAN SYLLABICS TYI;Lo;0;L;;;;;N;;;;;
+1572;CANADIAN SYLLABICS TYO;Lo;0;L;;;;;N;;;;;
+1573;CANADIAN SYLLABICS TYA;Lo;0;L;;;;;N;;;;;
+1574;CANADIAN SYLLABICS NUNAVIK HE;Lo;0;L;;;;;N;;;;;
+1575;CANADIAN SYLLABICS NUNAVIK HI;Lo;0;L;;;;;N;;;;;
+1576;CANADIAN SYLLABICS NUNAVIK HII;Lo;0;L;;;;;N;;;;;
+1577;CANADIAN SYLLABICS NUNAVIK HO;Lo;0;L;;;;;N;;;;;
+1578;CANADIAN SYLLABICS NUNAVIK HOO;Lo;0;L;;;;;N;;;;;
+1579;CANADIAN SYLLABICS NUNAVIK HA;Lo;0;L;;;;;N;;;;;
+157A;CANADIAN SYLLABICS NUNAVIK HAA;Lo;0;L;;;;;N;;;;;
+157B;CANADIAN SYLLABICS NUNAVIK H;Lo;0;L;;;;;N;;;;;
+157C;CANADIAN SYLLABICS NUNAVUT H;Lo;0;L;;;;;N;;;;;
+157D;CANADIAN SYLLABICS HK;Lo;0;L;;;;;N;;;;;
+157E;CANADIAN SYLLABICS QAAI;Lo;0;L;;;;;N;;;;;
+157F;CANADIAN SYLLABICS QI;Lo;0;L;;;;;N;;;;;
+1580;CANADIAN SYLLABICS QII;Lo;0;L;;;;;N;;;;;
+1581;CANADIAN SYLLABICS QO;Lo;0;L;;;;;N;;;;;
+1582;CANADIAN SYLLABICS QOO;Lo;0;L;;;;;N;;;;;
+1583;CANADIAN SYLLABICS QA;Lo;0;L;;;;;N;;;;;
+1584;CANADIAN SYLLABICS QAA;Lo;0;L;;;;;N;;;;;
+1585;CANADIAN SYLLABICS Q;Lo;0;L;;;;;N;;;;;
+1586;CANADIAN SYLLABICS TLHE;Lo;0;L;;;;;N;;;;;
+1587;CANADIAN SYLLABICS TLHI;Lo;0;L;;;;;N;;;;;
+1588;CANADIAN SYLLABICS TLHO;Lo;0;L;;;;;N;;;;;
+1589;CANADIAN SYLLABICS TLHA;Lo;0;L;;;;;N;;;;;
+158A;CANADIAN SYLLABICS WEST-CREE RE;Lo;0;L;;;;;N;;;;;
+158B;CANADIAN SYLLABICS WEST-CREE RI;Lo;0;L;;;;;N;;;;;
+158C;CANADIAN SYLLABICS WEST-CREE RO;Lo;0;L;;;;;N;;;;;
+158D;CANADIAN SYLLABICS WEST-CREE RA;Lo;0;L;;;;;N;;;;;
+158E;CANADIAN SYLLABICS NGAAI;Lo;0;L;;;;;N;;;;;
+158F;CANADIAN SYLLABICS NGI;Lo;0;L;;;;;N;;;;;
+1590;CANADIAN SYLLABICS NGII;Lo;0;L;;;;;N;;;;;
+1591;CANADIAN SYLLABICS NGO;Lo;0;L;;;;;N;;;;;
+1592;CANADIAN SYLLABICS NGOO;Lo;0;L;;;;;N;;;;;
+1593;CANADIAN SYLLABICS NGA;Lo;0;L;;;;;N;;;;;
+1594;CANADIAN SYLLABICS NGAA;Lo;0;L;;;;;N;;;;;
+1595;CANADIAN SYLLABICS NG;Lo;0;L;;;;;N;;;;;
+1596;CANADIAN SYLLABICS NNG;Lo;0;L;;;;;N;;;;;
+1597;CANADIAN SYLLABICS SAYISI SHE;Lo;0;L;;;;;N;;;;;
+1598;CANADIAN SYLLABICS SAYISI SHI;Lo;0;L;;;;;N;;;;;
+1599;CANADIAN SYLLABICS SAYISI SHO;Lo;0;L;;;;;N;;;;;
+159A;CANADIAN SYLLABICS SAYISI SHA;Lo;0;L;;;;;N;;;;;
+159B;CANADIAN SYLLABICS WOODS-CREE THE;Lo;0;L;;;;;N;;;;;
+159C;CANADIAN SYLLABICS WOODS-CREE THI;Lo;0;L;;;;;N;;;;;
+159D;CANADIAN SYLLABICS WOODS-CREE THO;Lo;0;L;;;;;N;;;;;
+159E;CANADIAN SYLLABICS WOODS-CREE THA;Lo;0;L;;;;;N;;;;;
+159F;CANADIAN SYLLABICS WOODS-CREE TH;Lo;0;L;;;;;N;;;;;
+15A0;CANADIAN SYLLABICS LHI;Lo;0;L;;;;;N;;;;;
+15A1;CANADIAN SYLLABICS LHII;Lo;0;L;;;;;N;;;;;
+15A2;CANADIAN SYLLABICS LHO;Lo;0;L;;;;;N;;;;;
+15A3;CANADIAN SYLLABICS LHOO;Lo;0;L;;;;;N;;;;;
+15A4;CANADIAN SYLLABICS LHA;Lo;0;L;;;;;N;;;;;
+15A5;CANADIAN SYLLABICS LHAA;Lo;0;L;;;;;N;;;;;
+15A6;CANADIAN SYLLABICS LH;Lo;0;L;;;;;N;;;;;
+15A7;CANADIAN SYLLABICS TH-CREE THE;Lo;0;L;;;;;N;;;;;
+15A8;CANADIAN SYLLABICS TH-CREE THI;Lo;0;L;;;;;N;;;;;
+15A9;CANADIAN SYLLABICS TH-CREE THII;Lo;0;L;;;;;N;;;;;
+15AA;CANADIAN SYLLABICS TH-CREE THO;Lo;0;L;;;;;N;;;;;
+15AB;CANADIAN SYLLABICS TH-CREE THOO;Lo;0;L;;;;;N;;;;;
+15AC;CANADIAN SYLLABICS TH-CREE THA;Lo;0;L;;;;;N;;;;;
+15AD;CANADIAN SYLLABICS TH-CREE THAA;Lo;0;L;;;;;N;;;;;
+15AE;CANADIAN SYLLABICS TH-CREE TH;Lo;0;L;;;;;N;;;;;
+15AF;CANADIAN SYLLABICS AIVILIK B;Lo;0;L;;;;;N;;;;;
+15B0;CANADIAN SYLLABICS BLACKFOOT E;Lo;0;L;;;;;N;;;;;
+15B1;CANADIAN SYLLABICS BLACKFOOT I;Lo;0;L;;;;;N;;;;;
+15B2;CANADIAN SYLLABICS BLACKFOOT O;Lo;0;L;;;;;N;;;;;
+15B3;CANADIAN SYLLABICS BLACKFOOT A;Lo;0;L;;;;;N;;;;;
+15B4;CANADIAN SYLLABICS BLACKFOOT WE;Lo;0;L;;;;;N;;;;;
+15B5;CANADIAN SYLLABICS BLACKFOOT WI;Lo;0;L;;;;;N;;;;;
+15B6;CANADIAN SYLLABICS BLACKFOOT WO;Lo;0;L;;;;;N;;;;;
+15B7;CANADIAN SYLLABICS BLACKFOOT WA;Lo;0;L;;;;;N;;;;;
+15B8;CANADIAN SYLLABICS BLACKFOOT NE;Lo;0;L;;;;;N;;;;;
+15B9;CANADIAN SYLLABICS BLACKFOOT NI;Lo;0;L;;;;;N;;;;;
+15BA;CANADIAN SYLLABICS BLACKFOOT NO;Lo;0;L;;;;;N;;;;;
+15BB;CANADIAN SYLLABICS BLACKFOOT NA;Lo;0;L;;;;;N;;;;;
+15BC;CANADIAN SYLLABICS BLACKFOOT KE;Lo;0;L;;;;;N;;;;;
+15BD;CANADIAN SYLLABICS BLACKFOOT KI;Lo;0;L;;;;;N;;;;;
+15BE;CANADIAN SYLLABICS BLACKFOOT KO;Lo;0;L;;;;;N;;;;;
+15BF;CANADIAN SYLLABICS BLACKFOOT KA;Lo;0;L;;;;;N;;;;;
+15C0;CANADIAN SYLLABICS SAYISI HE;Lo;0;L;;;;;N;;;;;
+15C1;CANADIAN SYLLABICS SAYISI HI;Lo;0;L;;;;;N;;;;;
+15C2;CANADIAN SYLLABICS SAYISI HO;Lo;0;L;;;;;N;;;;;
+15C3;CANADIAN SYLLABICS SAYISI HA;Lo;0;L;;;;;N;;;;;
+15C4;CANADIAN SYLLABICS CARRIER GHU;Lo;0;L;;;;;N;;;;;
+15C5;CANADIAN SYLLABICS CARRIER GHO;Lo;0;L;;;;;N;;;;;
+15C6;CANADIAN SYLLABICS CARRIER GHE;Lo;0;L;;;;;N;;;;;
+15C7;CANADIAN SYLLABICS CARRIER GHEE;Lo;0;L;;;;;N;;;;;
+15C8;CANADIAN SYLLABICS CARRIER GHI;Lo;0;L;;;;;N;;;;;
+15C9;CANADIAN SYLLABICS CARRIER GHA;Lo;0;L;;;;;N;;;;;
+15CA;CANADIAN SYLLABICS CARRIER RU;Lo;0;L;;;;;N;;;;;
+15CB;CANADIAN SYLLABICS CARRIER RO;Lo;0;L;;;;;N;;;;;
+15CC;CANADIAN SYLLABICS CARRIER RE;Lo;0;L;;;;;N;;;;;
+15CD;CANADIAN SYLLABICS CARRIER REE;Lo;0;L;;;;;N;;;;;
+15CE;CANADIAN SYLLABICS CARRIER RI;Lo;0;L;;;;;N;;;;;
+15CF;CANADIAN SYLLABICS CARRIER RA;Lo;0;L;;;;;N;;;;;
+15D0;CANADIAN SYLLABICS CARRIER WU;Lo;0;L;;;;;N;;;;;
+15D1;CANADIAN SYLLABICS CARRIER WO;Lo;0;L;;;;;N;;;;;
+15D2;CANADIAN SYLLABICS CARRIER WE;Lo;0;L;;;;;N;;;;;
+15D3;CANADIAN SYLLABICS CARRIER WEE;Lo;0;L;;;;;N;;;;;
+15D4;CANADIAN SYLLABICS CARRIER WI;Lo;0;L;;;;;N;;;;;
+15D5;CANADIAN SYLLABICS CARRIER WA;Lo;0;L;;;;;N;;;;;
+15D6;CANADIAN SYLLABICS CARRIER HWU;Lo;0;L;;;;;N;;;;;
+15D7;CANADIAN SYLLABICS CARRIER HWO;Lo;0;L;;;;;N;;;;;
+15D8;CANADIAN SYLLABICS CARRIER HWE;Lo;0;L;;;;;N;;;;;
+15D9;CANADIAN SYLLABICS CARRIER HWEE;Lo;0;L;;;;;N;;;;;
+15DA;CANADIAN SYLLABICS CARRIER HWI;Lo;0;L;;;;;N;;;;;
+15DB;CANADIAN SYLLABICS CARRIER HWA;Lo;0;L;;;;;N;;;;;
+15DC;CANADIAN SYLLABICS CARRIER THU;Lo;0;L;;;;;N;;;;;
+15DD;CANADIAN SYLLABICS CARRIER THO;Lo;0;L;;;;;N;;;;;
+15DE;CANADIAN SYLLABICS CARRIER THE;Lo;0;L;;;;;N;;;;;
+15DF;CANADIAN SYLLABICS CARRIER THEE;Lo;0;L;;;;;N;;;;;
+15E0;CANADIAN SYLLABICS CARRIER THI;Lo;0;L;;;;;N;;;;;
+15E1;CANADIAN SYLLABICS CARRIER THA;Lo;0;L;;;;;N;;;;;
+15E2;CANADIAN SYLLABICS CARRIER TTU;Lo;0;L;;;;;N;;;;;
+15E3;CANADIAN SYLLABICS CARRIER TTO;Lo;0;L;;;;;N;;;;;
+15E4;CANADIAN SYLLABICS CARRIER TTE;Lo;0;L;;;;;N;;;;;
+15E5;CANADIAN SYLLABICS CARRIER TTEE;Lo;0;L;;;;;N;;;;;
+15E6;CANADIAN SYLLABICS CARRIER TTI;Lo;0;L;;;;;N;;;;;
+15E7;CANADIAN SYLLABICS CARRIER TTA;Lo;0;L;;;;;N;;;;;
+15E8;CANADIAN SYLLABICS CARRIER PU;Lo;0;L;;;;;N;;;;;
+15E9;CANADIAN SYLLABICS CARRIER PO;Lo;0;L;;;;;N;;;;;
+15EA;CANADIAN SYLLABICS CARRIER PE;Lo;0;L;;;;;N;;;;;
+15EB;CANADIAN SYLLABICS CARRIER PEE;Lo;0;L;;;;;N;;;;;
+15EC;CANADIAN SYLLABICS CARRIER PI;Lo;0;L;;;;;N;;;;;
+15ED;CANADIAN SYLLABICS CARRIER PA;Lo;0;L;;;;;N;;;;;
+15EE;CANADIAN SYLLABICS CARRIER P;Lo;0;L;;;;;N;;;;;
+15EF;CANADIAN SYLLABICS CARRIER GU;Lo;0;L;;;;;N;;;;;
+15F0;CANADIAN SYLLABICS CARRIER GO;Lo;0;L;;;;;N;;;;;
+15F1;CANADIAN SYLLABICS CARRIER GE;Lo;0;L;;;;;N;;;;;
+15F2;CANADIAN SYLLABICS CARRIER GEE;Lo;0;L;;;;;N;;;;;
+15F3;CANADIAN SYLLABICS CARRIER GI;Lo;0;L;;;;;N;;;;;
+15F4;CANADIAN SYLLABICS CARRIER GA;Lo;0;L;;;;;N;;;;;
+15F5;CANADIAN SYLLABICS CARRIER KHU;Lo;0;L;;;;;N;;;;;
+15F6;CANADIAN SYLLABICS CARRIER KHO;Lo;0;L;;;;;N;;;;;
+15F7;CANADIAN SYLLABICS CARRIER KHE;Lo;0;L;;;;;N;;;;;
+15F8;CANADIAN SYLLABICS CARRIER KHEE;Lo;0;L;;;;;N;;;;;
+15F9;CANADIAN SYLLABICS CARRIER KHI;Lo;0;L;;;;;N;;;;;
+15FA;CANADIAN SYLLABICS CARRIER KHA;Lo;0;L;;;;;N;;;;;
+15FB;CANADIAN SYLLABICS CARRIER KKU;Lo;0;L;;;;;N;;;;;
+15FC;CANADIAN SYLLABICS CARRIER KKO;Lo;0;L;;;;;N;;;;;
+15FD;CANADIAN SYLLABICS CARRIER KKE;Lo;0;L;;;;;N;;;;;
+15FE;CANADIAN SYLLABICS CARRIER KKEE;Lo;0;L;;;;;N;;;;;
+15FF;CANADIAN SYLLABICS CARRIER KKI;Lo;0;L;;;;;N;;;;;
+1600;CANADIAN SYLLABICS CARRIER KKA;Lo;0;L;;;;;N;;;;;
+1601;CANADIAN SYLLABICS CARRIER KK;Lo;0;L;;;;;N;;;;;
+1602;CANADIAN SYLLABICS CARRIER NU;Lo;0;L;;;;;N;;;;;
+1603;CANADIAN SYLLABICS CARRIER NO;Lo;0;L;;;;;N;;;;;
+1604;CANADIAN SYLLABICS CARRIER NE;Lo;0;L;;;;;N;;;;;
+1605;CANADIAN SYLLABICS CARRIER NEE;Lo;0;L;;;;;N;;;;;
+1606;CANADIAN SYLLABICS CARRIER NI;Lo;0;L;;;;;N;;;;;
+1607;CANADIAN SYLLABICS CARRIER NA;Lo;0;L;;;;;N;;;;;
+1608;CANADIAN SYLLABICS CARRIER MU;Lo;0;L;;;;;N;;;;;
+1609;CANADIAN SYLLABICS CARRIER MO;Lo;0;L;;;;;N;;;;;
+160A;CANADIAN SYLLABICS CARRIER ME;Lo;0;L;;;;;N;;;;;
+160B;CANADIAN SYLLABICS CARRIER MEE;Lo;0;L;;;;;N;;;;;
+160C;CANADIAN SYLLABICS CARRIER MI;Lo;0;L;;;;;N;;;;;
+160D;CANADIAN SYLLABICS CARRIER MA;Lo;0;L;;;;;N;;;;;
+160E;CANADIAN SYLLABICS CARRIER YU;Lo;0;L;;;;;N;;;;;
+160F;CANADIAN SYLLABICS CARRIER YO;Lo;0;L;;;;;N;;;;;
+1610;CANADIAN SYLLABICS CARRIER YE;Lo;0;L;;;;;N;;;;;
+1611;CANADIAN SYLLABICS CARRIER YEE;Lo;0;L;;;;;N;;;;;
+1612;CANADIAN SYLLABICS CARRIER YI;Lo;0;L;;;;;N;;;;;
+1613;CANADIAN SYLLABICS CARRIER YA;Lo;0;L;;;;;N;;;;;
+1614;CANADIAN SYLLABICS CARRIER JU;Lo;0;L;;;;;N;;;;;
+1615;CANADIAN SYLLABICS SAYISI JU;Lo;0;L;;;;;N;;;;;
+1616;CANADIAN SYLLABICS CARRIER JO;Lo;0;L;;;;;N;;;;;
+1617;CANADIAN SYLLABICS CARRIER JE;Lo;0;L;;;;;N;;;;;
+1618;CANADIAN SYLLABICS CARRIER JEE;Lo;0;L;;;;;N;;;;;
+1619;CANADIAN SYLLABICS CARRIER JI;Lo;0;L;;;;;N;;;;;
+161A;CANADIAN SYLLABICS SAYISI JI;Lo;0;L;;;;;N;;;;;
+161B;CANADIAN SYLLABICS CARRIER JA;Lo;0;L;;;;;N;;;;;
+161C;CANADIAN SYLLABICS CARRIER JJU;Lo;0;L;;;;;N;;;;;
+161D;CANADIAN SYLLABICS CARRIER JJO;Lo;0;L;;;;;N;;;;;
+161E;CANADIAN SYLLABICS CARRIER JJE;Lo;0;L;;;;;N;;;;;
+161F;CANADIAN SYLLABICS CARRIER JJEE;Lo;0;L;;;;;N;;;;;
+1620;CANADIAN SYLLABICS CARRIER JJI;Lo;0;L;;;;;N;;;;;
+1621;CANADIAN SYLLABICS CARRIER JJA;Lo;0;L;;;;;N;;;;;
+1622;CANADIAN SYLLABICS CARRIER LU;Lo;0;L;;;;;N;;;;;
+1623;CANADIAN SYLLABICS CARRIER LO;Lo;0;L;;;;;N;;;;;
+1624;CANADIAN SYLLABICS CARRIER LE;Lo;0;L;;;;;N;;;;;
+1625;CANADIAN SYLLABICS CARRIER LEE;Lo;0;L;;;;;N;;;;;
+1626;CANADIAN SYLLABICS CARRIER LI;Lo;0;L;;;;;N;;;;;
+1627;CANADIAN SYLLABICS CARRIER LA;Lo;0;L;;;;;N;;;;;
+1628;CANADIAN SYLLABICS CARRIER DLU;Lo;0;L;;;;;N;;;;;
+1629;CANADIAN SYLLABICS CARRIER DLO;Lo;0;L;;;;;N;;;;;
+162A;CANADIAN SYLLABICS CARRIER DLE;Lo;0;L;;;;;N;;;;;
+162B;CANADIAN SYLLABICS CARRIER DLEE;Lo;0;L;;;;;N;;;;;
+162C;CANADIAN SYLLABICS CARRIER DLI;Lo;0;L;;;;;N;;;;;
+162D;CANADIAN SYLLABICS CARRIER DLA;Lo;0;L;;;;;N;;;;;
+162E;CANADIAN SYLLABICS CARRIER LHU;Lo;0;L;;;;;N;;;;;
+162F;CANADIAN SYLLABICS CARRIER LHO;Lo;0;L;;;;;N;;;;;
+1630;CANADIAN SYLLABICS CARRIER LHE;Lo;0;L;;;;;N;;;;;
+1631;CANADIAN SYLLABICS CARRIER LHEE;Lo;0;L;;;;;N;;;;;
+1632;CANADIAN SYLLABICS CARRIER LHI;Lo;0;L;;;;;N;;;;;
+1633;CANADIAN SYLLABICS CARRIER LHA;Lo;0;L;;;;;N;;;;;
+1634;CANADIAN SYLLABICS CARRIER TLHU;Lo;0;L;;;;;N;;;;;
+1635;CANADIAN SYLLABICS CARRIER TLHO;Lo;0;L;;;;;N;;;;;
+1636;CANADIAN SYLLABICS CARRIER TLHE;Lo;0;L;;;;;N;;;;;
+1637;CANADIAN SYLLABICS CARRIER TLHEE;Lo;0;L;;;;;N;;;;;
+1638;CANADIAN SYLLABICS CARRIER TLHI;Lo;0;L;;;;;N;;;;;
+1639;CANADIAN SYLLABICS CARRIER TLHA;Lo;0;L;;;;;N;;;;;
+163A;CANADIAN SYLLABICS CARRIER TLU;Lo;0;L;;;;;N;;;;;
+163B;CANADIAN SYLLABICS CARRIER TLO;Lo;0;L;;;;;N;;;;;
+163C;CANADIAN SYLLABICS CARRIER TLE;Lo;0;L;;;;;N;;;;;
+163D;CANADIAN SYLLABICS CARRIER TLEE;Lo;0;L;;;;;N;;;;;
+163E;CANADIAN SYLLABICS CARRIER TLI;Lo;0;L;;;;;N;;;;;
+163F;CANADIAN SYLLABICS CARRIER TLA;Lo;0;L;;;;;N;;;;;
+1640;CANADIAN SYLLABICS CARRIER ZU;Lo;0;L;;;;;N;;;;;
+1641;CANADIAN SYLLABICS CARRIER ZO;Lo;0;L;;;;;N;;;;;
+1642;CANADIAN SYLLABICS CARRIER ZE;Lo;0;L;;;;;N;;;;;
+1643;CANADIAN SYLLABICS CARRIER ZEE;Lo;0;L;;;;;N;;;;;
+1644;CANADIAN SYLLABICS CARRIER ZI;Lo;0;L;;;;;N;;;;;
+1645;CANADIAN SYLLABICS CARRIER ZA;Lo;0;L;;;;;N;;;;;
+1646;CANADIAN SYLLABICS CARRIER Z;Lo;0;L;;;;;N;;;;;
+1647;CANADIAN SYLLABICS CARRIER INITIAL Z;Lo;0;L;;;;;N;;;;;
+1648;CANADIAN SYLLABICS CARRIER DZU;Lo;0;L;;;;;N;;;;;
+1649;CANADIAN SYLLABICS CARRIER DZO;Lo;0;L;;;;;N;;;;;
+164A;CANADIAN SYLLABICS CARRIER DZE;Lo;0;L;;;;;N;;;;;
+164B;CANADIAN SYLLABICS CARRIER DZEE;Lo;0;L;;;;;N;;;;;
+164C;CANADIAN SYLLABICS CARRIER DZI;Lo;0;L;;;;;N;;;;;
+164D;CANADIAN SYLLABICS CARRIER DZA;Lo;0;L;;;;;N;;;;;
+164E;CANADIAN SYLLABICS CARRIER SU;Lo;0;L;;;;;N;;;;;
+164F;CANADIAN SYLLABICS CARRIER SO;Lo;0;L;;;;;N;;;;;
+1650;CANADIAN SYLLABICS CARRIER SE;Lo;0;L;;;;;N;;;;;
+1651;CANADIAN SYLLABICS CARRIER SEE;Lo;0;L;;;;;N;;;;;
+1652;CANADIAN SYLLABICS CARRIER SI;Lo;0;L;;;;;N;;;;;
+1653;CANADIAN SYLLABICS CARRIER SA;Lo;0;L;;;;;N;;;;;
+1654;CANADIAN SYLLABICS CARRIER SHU;Lo;0;L;;;;;N;;;;;
+1655;CANADIAN SYLLABICS CARRIER SHO;Lo;0;L;;;;;N;;;;;
+1656;CANADIAN SYLLABICS CARRIER SHE;Lo;0;L;;;;;N;;;;;
+1657;CANADIAN SYLLABICS CARRIER SHEE;Lo;0;L;;;;;N;;;;;
+1658;CANADIAN SYLLABICS CARRIER SHI;Lo;0;L;;;;;N;;;;;
+1659;CANADIAN SYLLABICS CARRIER SHA;Lo;0;L;;;;;N;;;;;
+165A;CANADIAN SYLLABICS CARRIER SH;Lo;0;L;;;;;N;;;;;
+165B;CANADIAN SYLLABICS CARRIER TSU;Lo;0;L;;;;;N;;;;;
+165C;CANADIAN SYLLABICS CARRIER TSO;Lo;0;L;;;;;N;;;;;
+165D;CANADIAN SYLLABICS CARRIER TSE;Lo;0;L;;;;;N;;;;;
+165E;CANADIAN SYLLABICS CARRIER TSEE;Lo;0;L;;;;;N;;;;;
+165F;CANADIAN SYLLABICS CARRIER TSI;Lo;0;L;;;;;N;;;;;
+1660;CANADIAN SYLLABICS CARRIER TSA;Lo;0;L;;;;;N;;;;;
+1661;CANADIAN SYLLABICS CARRIER CHU;Lo;0;L;;;;;N;;;;;
+1662;CANADIAN SYLLABICS CARRIER CHO;Lo;0;L;;;;;N;;;;;
+1663;CANADIAN SYLLABICS CARRIER CHE;Lo;0;L;;;;;N;;;;;
+1664;CANADIAN SYLLABICS CARRIER CHEE;Lo;0;L;;;;;N;;;;;
+1665;CANADIAN SYLLABICS CARRIER CHI;Lo;0;L;;;;;N;;;;;
+1666;CANADIAN SYLLABICS CARRIER CHA;Lo;0;L;;;;;N;;;;;
+1667;CANADIAN SYLLABICS CARRIER TTSU;Lo;0;L;;;;;N;;;;;
+1668;CANADIAN SYLLABICS CARRIER TTSO;Lo;0;L;;;;;N;;;;;
+1669;CANADIAN SYLLABICS CARRIER TTSE;Lo;0;L;;;;;N;;;;;
+166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;;
+166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;;
+166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;;
+166D;CANADIAN SYLLABICS CHI SIGN;Po;0;L;;;;;N;;;;;
+166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;;
+166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;;
+1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;;
+1671;CANADIAN SYLLABICS NNGI;Lo;0;L;;;;;N;;;;;
+1672;CANADIAN SYLLABICS NNGII;Lo;0;L;;;;;N;;;;;
+1673;CANADIAN SYLLABICS NNGO;Lo;0;L;;;;;N;;;;;
+1674;CANADIAN SYLLABICS NNGOO;Lo;0;L;;;;;N;;;;;
+1675;CANADIAN SYLLABICS NNGA;Lo;0;L;;;;;N;;;;;
+1676;CANADIAN SYLLABICS NNGAA;Lo;0;L;;;;;N;;;;;
+1680;OGHAM SPACE MARK;Zs;0;WS;;;;;N;;;;;
+1681;OGHAM LETTER BEITH;Lo;0;L;;;;;N;;;;;
+1682;OGHAM LETTER LUIS;Lo;0;L;;;;;N;;;;;
+1683;OGHAM LETTER FEARN;Lo;0;L;;;;;N;;;;;
+1684;OGHAM LETTER SAIL;Lo;0;L;;;;;N;;;;;
+1685;OGHAM LETTER NION;Lo;0;L;;;;;N;;;;;
+1686;OGHAM LETTER UATH;Lo;0;L;;;;;N;;;;;
+1687;OGHAM LETTER DAIR;Lo;0;L;;;;;N;;;;;
+1688;OGHAM LETTER TINNE;Lo;0;L;;;;;N;;;;;
+1689;OGHAM LETTER COLL;Lo;0;L;;;;;N;;;;;
+168A;OGHAM LETTER CEIRT;Lo;0;L;;;;;N;;;;;
+168B;OGHAM LETTER MUIN;Lo;0;L;;;;;N;;;;;
+168C;OGHAM LETTER GORT;Lo;0;L;;;;;N;;;;;
+168D;OGHAM LETTER NGEADAL;Lo;0;L;;;;;N;;;;;
+168E;OGHAM LETTER STRAIF;Lo;0;L;;;;;N;;;;;
+168F;OGHAM LETTER RUIS;Lo;0;L;;;;;N;;;;;
+1690;OGHAM LETTER AILM;Lo;0;L;;;;;N;;;;;
+1691;OGHAM LETTER ONN;Lo;0;L;;;;;N;;;;;
+1692;OGHAM LETTER UR;Lo;0;L;;;;;N;;;;;
+1693;OGHAM LETTER EADHADH;Lo;0;L;;;;;N;;;;;
+1694;OGHAM LETTER IODHADH;Lo;0;L;;;;;N;;;;;
+1695;OGHAM LETTER EABHADH;Lo;0;L;;;;;N;;;;;
+1696;OGHAM LETTER OR;Lo;0;L;;;;;N;;;;;
+1697;OGHAM LETTER UILLEANN;Lo;0;L;;;;;N;;;;;
+1698;OGHAM LETTER IFIN;Lo;0;L;;;;;N;;;;;
+1699;OGHAM LETTER EAMHANCHOLL;Lo;0;L;;;;;N;;;;;
+169A;OGHAM LETTER PEITH;Lo;0;L;;;;;N;;;;;
+169B;OGHAM FEATHER MARK;Ps;0;ON;;;;;N;;;;;
+169C;OGHAM REVERSED FEATHER MARK;Pe;0;ON;;;;;N;;;;;
+16A0;RUNIC LETTER FEHU FEOH FE F;Lo;0;L;;;;;N;;;;;
+16A1;RUNIC LETTER V;Lo;0;L;;;;;N;;;;;
+16A2;RUNIC LETTER URUZ UR U;Lo;0;L;;;;;N;;;;;
+16A3;RUNIC LETTER YR;Lo;0;L;;;;;N;;;;;
+16A4;RUNIC LETTER Y;Lo;0;L;;;;;N;;;;;
+16A5;RUNIC LETTER W;Lo;0;L;;;;;N;;;;;
+16A6;RUNIC LETTER THURISAZ THURS THORN;Lo;0;L;;;;;N;;;;;
+16A7;RUNIC LETTER ETH;Lo;0;L;;;;;N;;;;;
+16A8;RUNIC LETTER ANSUZ A;Lo;0;L;;;;;N;;;;;
+16A9;RUNIC LETTER OS O;Lo;0;L;;;;;N;;;;;
+16AA;RUNIC LETTER AC A;Lo;0;L;;;;;N;;;;;
+16AB;RUNIC LETTER AESC;Lo;0;L;;;;;N;;;;;
+16AC;RUNIC LETTER LONG-BRANCH-OSS O;Lo;0;L;;;;;N;;;;;
+16AD;RUNIC LETTER SHORT-TWIG-OSS O;Lo;0;L;;;;;N;;;;;
+16AE;RUNIC LETTER O;Lo;0;L;;;;;N;;;;;
+16AF;RUNIC LETTER OE;Lo;0;L;;;;;N;;;;;
+16B0;RUNIC LETTER ON;Lo;0;L;;;;;N;;;;;
+16B1;RUNIC LETTER RAIDO RAD REID R;Lo;0;L;;;;;N;;;;;
+16B2;RUNIC LETTER KAUNA;Lo;0;L;;;;;N;;;;;
+16B3;RUNIC LETTER CEN;Lo;0;L;;;;;N;;;;;
+16B4;RUNIC LETTER KAUN K;Lo;0;L;;;;;N;;;;;
+16B5;RUNIC LETTER G;Lo;0;L;;;;;N;;;;;
+16B6;RUNIC LETTER ENG;Lo;0;L;;;;;N;;;;;
+16B7;RUNIC LETTER GEBO GYFU G;Lo;0;L;;;;;N;;;;;
+16B8;RUNIC LETTER GAR;Lo;0;L;;;;;N;;;;;
+16B9;RUNIC LETTER WUNJO WYNN W;Lo;0;L;;;;;N;;;;;
+16BA;RUNIC LETTER HAGLAZ H;Lo;0;L;;;;;N;;;;;
+16BB;RUNIC LETTER HAEGL H;Lo;0;L;;;;;N;;;;;
+16BC;RUNIC LETTER LONG-BRANCH-HAGALL H;Lo;0;L;;;;;N;;;;;
+16BD;RUNIC LETTER SHORT-TWIG-HAGALL H;Lo;0;L;;;;;N;;;;;
+16BE;RUNIC LETTER NAUDIZ NYD NAUD N;Lo;0;L;;;;;N;;;;;
+16BF;RUNIC LETTER SHORT-TWIG-NAUD N;Lo;0;L;;;;;N;;;;;
+16C0;RUNIC LETTER DOTTED-N;Lo;0;L;;;;;N;;;;;
+16C1;RUNIC LETTER ISAZ IS ISS I;Lo;0;L;;;;;N;;;;;
+16C2;RUNIC LETTER E;Lo;0;L;;;;;N;;;;;
+16C3;RUNIC LETTER JERAN J;Lo;0;L;;;;;N;;;;;
+16C4;RUNIC LETTER GER;Lo;0;L;;;;;N;;;;;
+16C5;RUNIC LETTER LONG-BRANCH-AR AE;Lo;0;L;;;;;N;;;;;
+16C6;RUNIC LETTER SHORT-TWIG-AR A;Lo;0;L;;;;;N;;;;;
+16C7;RUNIC LETTER IWAZ EOH;Lo;0;L;;;;;N;;;;;
+16C8;RUNIC LETTER PERTHO PEORTH P;Lo;0;L;;;;;N;;;;;
+16C9;RUNIC LETTER ALGIZ EOLHX;Lo;0;L;;;;;N;;;;;
+16CA;RUNIC LETTER SOWILO S;Lo;0;L;;;;;N;;;;;
+16CB;RUNIC LETTER SIGEL LONG-BRANCH-SOL S;Lo;0;L;;;;;N;;;;;
+16CC;RUNIC LETTER SHORT-TWIG-SOL S;Lo;0;L;;;;;N;;;;;
+16CD;RUNIC LETTER C;Lo;0;L;;;;;N;;;;;
+16CE;RUNIC LETTER Z;Lo;0;L;;;;;N;;;;;
+16CF;RUNIC LETTER TIWAZ TIR TYR T;Lo;0;L;;;;;N;;;;;
+16D0;RUNIC LETTER SHORT-TWIG-TYR T;Lo;0;L;;;;;N;;;;;
+16D1;RUNIC LETTER D;Lo;0;L;;;;;N;;;;;
+16D2;RUNIC LETTER BERKANAN BEORC BJARKAN B;Lo;0;L;;;;;N;;;;;
+16D3;RUNIC LETTER SHORT-TWIG-BJARKAN B;Lo;0;L;;;;;N;;;;;
+16D4;RUNIC LETTER DOTTED-P;Lo;0;L;;;;;N;;;;;
+16D5;RUNIC LETTER OPEN-P;Lo;0;L;;;;;N;;;;;
+16D6;RUNIC LETTER EHWAZ EH E;Lo;0;L;;;;;N;;;;;
+16D7;RUNIC LETTER MANNAZ MAN M;Lo;0;L;;;;;N;;;;;
+16D8;RUNIC LETTER LONG-BRANCH-MADR M;Lo;0;L;;;;;N;;;;;
+16D9;RUNIC LETTER SHORT-TWIG-MADR M;Lo;0;L;;;;;N;;;;;
+16DA;RUNIC LETTER LAUKAZ LAGU LOGR L;Lo;0;L;;;;;N;;;;;
+16DB;RUNIC LETTER DOTTED-L;Lo;0;L;;;;;N;;;;;
+16DC;RUNIC LETTER INGWAZ;Lo;0;L;;;;;N;;;;;
+16DD;RUNIC LETTER ING;Lo;0;L;;;;;N;;;;;
+16DE;RUNIC LETTER DAGAZ DAEG D;Lo;0;L;;;;;N;;;;;
+16DF;RUNIC LETTER OTHALAN ETHEL O;Lo;0;L;;;;;N;;;;;
+16E0;RUNIC LETTER EAR;Lo;0;L;;;;;N;;;;;
+16E1;RUNIC LETTER IOR;Lo;0;L;;;;;N;;;;;
+16E2;RUNIC LETTER CWEORTH;Lo;0;L;;;;;N;;;;;
+16E3;RUNIC LETTER CALC;Lo;0;L;;;;;N;;;;;
+16E4;RUNIC LETTER CEALC;Lo;0;L;;;;;N;;;;;
+16E5;RUNIC LETTER STAN;Lo;0;L;;;;;N;;;;;
+16E6;RUNIC LETTER LONG-BRANCH-YR;Lo;0;L;;;;;N;;;;;
+16E7;RUNIC LETTER SHORT-TWIG-YR;Lo;0;L;;;;;N;;;;;
+16E8;RUNIC LETTER ICELANDIC-YR;Lo;0;L;;;;;N;;;;;
+16E9;RUNIC LETTER Q;Lo;0;L;;;;;N;;;;;
+16EA;RUNIC LETTER X;Lo;0;L;;;;;N;;;;;
+16EB;RUNIC SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+16EC;RUNIC MULTIPLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+16ED;RUNIC CROSS PUNCTUATION;Po;0;L;;;;;N;;;;;
+16EE;RUNIC ARLAUG SYMBOL;Nl;0;L;;;;17;N;;golden number 17;;;
+16EF;RUNIC TVIMADUR SYMBOL;Nl;0;L;;;;18;N;;golden number 18;;;
+16F0;RUNIC BELGTHOR SYMBOL;Nl;0;L;;;;19;N;;golden number 19;;;
+1780;KHMER LETTER KA;Lo;0;L;;;;;N;;;;;
+1781;KHMER LETTER KHA;Lo;0;L;;;;;N;;;;;
+1782;KHMER LETTER KO;Lo;0;L;;;;;N;;;;;
+1783;KHMER LETTER KHO;Lo;0;L;;;;;N;;;;;
+1784;KHMER LETTER NGO;Lo;0;L;;;;;N;;;;;
+1785;KHMER LETTER CA;Lo;0;L;;;;;N;;;;;
+1786;KHMER LETTER CHA;Lo;0;L;;;;;N;;;;;
+1787;KHMER LETTER CO;Lo;0;L;;;;;N;;;;;
+1788;KHMER LETTER CHO;Lo;0;L;;;;;N;;;;;
+1789;KHMER LETTER NYO;Lo;0;L;;;;;N;;;;;
+178A;KHMER LETTER DA;Lo;0;L;;;;;N;;;;;
+178B;KHMER LETTER TTHA;Lo;0;L;;;;;N;;;;;
+178C;KHMER LETTER DO;Lo;0;L;;;;;N;;;;;
+178D;KHMER LETTER TTHO;Lo;0;L;;;;;N;;;;;
+178E;KHMER LETTER NNO;Lo;0;L;;;;;N;;;;;
+178F;KHMER LETTER TA;Lo;0;L;;;;;N;;;;;
+1790;KHMER LETTER THA;Lo;0;L;;;;;N;;;;;
+1791;KHMER LETTER TO;Lo;0;L;;;;;N;;;;;
+1792;KHMER LETTER THO;Lo;0;L;;;;;N;;;;;
+1793;KHMER LETTER NO;Lo;0;L;;;;;N;;;;;
+1794;KHMER LETTER BA;Lo;0;L;;;;;N;;;;;
+1795;KHMER LETTER PHA;Lo;0;L;;;;;N;;;;;
+1796;KHMER LETTER PO;Lo;0;L;;;;;N;;;;;
+1797;KHMER LETTER PHO;Lo;0;L;;;;;N;;;;;
+1798;KHMER LETTER MO;Lo;0;L;;;;;N;;;;;
+1799;KHMER LETTER YO;Lo;0;L;;;;;N;;;;;
+179A;KHMER LETTER RO;Lo;0;L;;;;;N;;;;;
+179B;KHMER LETTER LO;Lo;0;L;;;;;N;;;;;
+179C;KHMER LETTER VO;Lo;0;L;;;;;N;;;;;
+179D;KHMER LETTER SHA;Lo;0;L;;;;;N;;;;;
+179E;KHMER LETTER SSO;Lo;0;L;;;;;N;;;;;
+179F;KHMER LETTER SA;Lo;0;L;;;;;N;;;;;
+17A0;KHMER LETTER HA;Lo;0;L;;;;;N;;;;;
+17A1;KHMER LETTER LA;Lo;0;L;;;;;N;;;;;
+17A2;KHMER LETTER QA;Lo;0;L;;;;;N;;;;;
+17A3;KHMER INDEPENDENT VOWEL QAQ;Lo;0;L;;;;;N;;;;;
+17A4;KHMER INDEPENDENT VOWEL QAA;Lo;0;L;;;;;N;;;;;
+17A5;KHMER INDEPENDENT VOWEL QI;Lo;0;L;;;;;N;;;;;
+17A6;KHMER INDEPENDENT VOWEL QII;Lo;0;L;;;;;N;;;;;
+17A7;KHMER INDEPENDENT VOWEL QU;Lo;0;L;;;;;N;;;;;
+17A8;KHMER INDEPENDENT VOWEL QUK;Lo;0;L;;;;;N;;;;;
+17A9;KHMER INDEPENDENT VOWEL QUU;Lo;0;L;;;;;N;;;;;
+17AA;KHMER INDEPENDENT VOWEL QUUV;Lo;0;L;;;;;N;;;;;
+17AB;KHMER INDEPENDENT VOWEL RY;Lo;0;L;;;;;N;;;;;
+17AC;KHMER INDEPENDENT VOWEL RYY;Lo;0;L;;;;;N;;;;;
+17AD;KHMER INDEPENDENT VOWEL LY;Lo;0;L;;;;;N;;;;;
+17AE;KHMER INDEPENDENT VOWEL LYY;Lo;0;L;;;;;N;;;;;
+17AF;KHMER INDEPENDENT VOWEL QE;Lo;0;L;;;;;N;;;;;
+17B0;KHMER INDEPENDENT VOWEL QAI;Lo;0;L;;;;;N;;;;;
+17B1;KHMER INDEPENDENT VOWEL QOO TYPE ONE;Lo;0;L;;;;;N;;;;;
+17B2;KHMER INDEPENDENT VOWEL QOO TYPE TWO;Lo;0;L;;;;;N;;;;;
+17B3;KHMER INDEPENDENT VOWEL QAU;Lo;0;L;;;;;N;;;;;
+17B4;KHMER VOWEL INHERENT AQ;Mc;0;L;;;;;N;;;;;
+17B5;KHMER VOWEL INHERENT AA;Mc;0;L;;;;;N;;;;;
+17B6;KHMER VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+17B7;KHMER VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+17B8;KHMER VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+17B9;KHMER VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;;
+17BA;KHMER VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
+17BB;KHMER VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+17BC;KHMER VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+17BD;KHMER VOWEL SIGN UA;Mn;0;NSM;;;;;N;;;;;
+17BE;KHMER VOWEL SIGN OE;Mc;0;L;;;;;N;;;;;
+17BF;KHMER VOWEL SIGN YA;Mc;0;L;;;;;N;;;;;
+17C0;KHMER VOWEL SIGN IE;Mc;0;L;;;;;N;;;;;
+17C1;KHMER VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+17C2;KHMER VOWEL SIGN AE;Mc;0;L;;;;;N;;;;;
+17C3;KHMER VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+17C4;KHMER VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+17C5;KHMER VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+17C6;KHMER SIGN NIKAHIT;Mn;0;NSM;;;;;N;;;;;
+17C7;KHMER SIGN REAHMUK;Mc;0;L;;;;;N;;;;;
+17C8;KHMER SIGN YUUKALEAPINTU;Mc;0;L;;;;;N;;;;;
+17C9;KHMER SIGN MUUSIKATOAN;Mn;0;NSM;;;;;N;;;;;
+17CA;KHMER SIGN TRIISAP;Mn;0;NSM;;;;;N;;;;;
+17CB;KHMER SIGN BANTOC;Mn;0;NSM;;;;;N;;;;;
+17CC;KHMER SIGN ROBAT;Mn;0;NSM;;;;;N;;;;;
+17CD;KHMER SIGN TOANDAKHIAT;Mn;0;NSM;;;;;N;;;;;
+17CE;KHMER SIGN KAKABAT;Mn;0;NSM;;;;;N;;;;;
+17CF;KHMER SIGN AHSDA;Mn;0;NSM;;;;;N;;;;;
+17D0;KHMER SIGN SAMYOK SANNYA;Mn;0;NSM;;;;;N;;;;;
+17D1;KHMER SIGN VIRIAM;Mn;0;NSM;;;;;N;;;;;
+17D2;KHMER SIGN COENG;Mn;9;NSM;;;;;N;;;;;
+17D3;KHMER SIGN BATHAMASAT;Mn;0;NSM;;;;;N;;;;;
+17D4;KHMER SIGN KHAN;Po;0;L;;;;;N;;;;;
+17D5;KHMER SIGN BARIYOOSAN;Po;0;L;;;;;N;;;;;
+17D6;KHMER SIGN CAMNUC PII KUUH;Po;0;L;;;;;N;;;;;
+17D7;KHMER SIGN LEK TOO;Po;0;L;;;;;N;;;;;
+17D8;KHMER SIGN BEYYAL;Po;0;L;;;;;N;;;;;
+17D9;KHMER SIGN PHNAEK MUAN;Po;0;L;;;;;N;;;;;
+17DA;KHMER SIGN KOOMUUT;Po;0;L;;;;;N;;;;;
+17DB;KHMER CURRENCY SYMBOL RIEL;Sc;0;ET;;;;;N;;;;;
+17DC;KHMER SIGN AVAKRAHASANYA;Po;0;L;;;;;N;;;;;
+17E0;KHMER DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+17E1;KHMER DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+17E2;KHMER DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+17E3;KHMER DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+17E4;KHMER DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+17E5;KHMER DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+17E6;KHMER DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+17E7;KHMER DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+17E8;KHMER DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+17E9;KHMER DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1800;MONGOLIAN BIRGA;Po;0;ON;;;;;N;;;;;
+1801;MONGOLIAN ELLIPSIS;Po;0;ON;;;;;N;;;;;
+1802;MONGOLIAN COMMA;Po;0;ON;;;;;N;;;;;
+1803;MONGOLIAN FULL STOP;Po;0;ON;;;;;N;;;;;
+1804;MONGOLIAN COLON;Po;0;ON;;;;;N;;;;;
+1805;MONGOLIAN FOUR DOTS;Po;0;ON;;;;;N;;;;;
+1806;MONGOLIAN TODO SOFT HYPHEN;Pd;0;ON;;;;;N;;;;;
+1807;MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER;Po;0;ON;;;;;N;;;;;
+1808;MONGOLIAN MANCHU COMMA;Po;0;ON;;;;;N;;;;;
+1809;MONGOLIAN MANCHU FULL STOP;Po;0;ON;;;;;N;;;;;
+180A;MONGOLIAN NIRUGU;Po;0;ON;;;;;N;;;;;
+180B;MONGOLIAN FREE VARIATION SELECTOR ONE;Cf;0;BN;;;;;N;;;;;
+180C;MONGOLIAN FREE VARIATION SELECTOR TWO;Cf;0;BN;;;;;N;;;;;
+180D;MONGOLIAN FREE VARIATION SELECTOR THREE;Cf;0;BN;;;;;N;;;;;
+180E;MONGOLIAN VOWEL SEPARATOR;Cf;0;BN;;;;;N;;;;;
+1810;MONGOLIAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1811;MONGOLIAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1812;MONGOLIAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1813;MONGOLIAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1814;MONGOLIAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1815;MONGOLIAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1816;MONGOLIAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1817;MONGOLIAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1818;MONGOLIAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1819;MONGOLIAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1820;MONGOLIAN LETTER A;Lo;0;L;;;;;N;;;;;
+1821;MONGOLIAN LETTER E;Lo;0;L;;;;;N;;;;;
+1822;MONGOLIAN LETTER I;Lo;0;L;;;;;N;;;;;
+1823;MONGOLIAN LETTER O;Lo;0;L;;;;;N;;;;;
+1824;MONGOLIAN LETTER U;Lo;0;L;;;;;N;;;;;
+1825;MONGOLIAN LETTER OE;Lo;0;L;;;;;N;;;;;
+1826;MONGOLIAN LETTER UE;Lo;0;L;;;;;N;;;;;
+1827;MONGOLIAN LETTER EE;Lo;0;L;;;;;N;;;;;
+1828;MONGOLIAN LETTER NA;Lo;0;L;;;;;N;;;;;
+1829;MONGOLIAN LETTER ANG;Lo;0;L;;;;;N;;;;;
+182A;MONGOLIAN LETTER BA;Lo;0;L;;;;;N;;;;;
+182B;MONGOLIAN LETTER PA;Lo;0;L;;;;;N;;;;;
+182C;MONGOLIAN LETTER QA;Lo;0;L;;;;;N;;;;;
+182D;MONGOLIAN LETTER GA;Lo;0;L;;;;;N;;;;;
+182E;MONGOLIAN LETTER MA;Lo;0;L;;;;;N;;;;;
+182F;MONGOLIAN LETTER LA;Lo;0;L;;;;;N;;;;;
+1830;MONGOLIAN LETTER SA;Lo;0;L;;;;;N;;;;;
+1831;MONGOLIAN LETTER SHA;Lo;0;L;;;;;N;;;;;
+1832;MONGOLIAN LETTER TA;Lo;0;L;;;;;N;;;;;
+1833;MONGOLIAN LETTER DA;Lo;0;L;;;;;N;;;;;
+1834;MONGOLIAN LETTER CHA;Lo;0;L;;;;;N;;;;;
+1835;MONGOLIAN LETTER JA;Lo;0;L;;;;;N;;;;;
+1836;MONGOLIAN LETTER YA;Lo;0;L;;;;;N;;;;;
+1837;MONGOLIAN LETTER RA;Lo;0;L;;;;;N;;;;;
+1838;MONGOLIAN LETTER WA;Lo;0;L;;;;;N;;;;;
+1839;MONGOLIAN LETTER FA;Lo;0;L;;;;;N;;;;;
+183A;MONGOLIAN LETTER KA;Lo;0;L;;;;;N;;;;;
+183B;MONGOLIAN LETTER KHA;Lo;0;L;;;;;N;;;;;
+183C;MONGOLIAN LETTER TSA;Lo;0;L;;;;;N;;;;;
+183D;MONGOLIAN LETTER ZA;Lo;0;L;;;;;N;;;;;
+183E;MONGOLIAN LETTER HAA;Lo;0;L;;;;;N;;;;;
+183F;MONGOLIAN LETTER ZRA;Lo;0;L;;;;;N;;;;;
+1840;MONGOLIAN LETTER LHA;Lo;0;L;;;;;N;;;;;
+1841;MONGOLIAN LETTER ZHI;Lo;0;L;;;;;N;;;;;
+1842;MONGOLIAN LETTER CHI;Lo;0;L;;;;;N;;;;;
+1843;MONGOLIAN LETTER TODO LONG VOWEL SIGN;Lm;0;L;;;;;N;;;;;
+1844;MONGOLIAN LETTER TODO E;Lo;0;L;;;;;N;;;;;
+1845;MONGOLIAN LETTER TODO I;Lo;0;L;;;;;N;;;;;
+1846;MONGOLIAN LETTER TODO O;Lo;0;L;;;;;N;;;;;
+1847;MONGOLIAN LETTER TODO U;Lo;0;L;;;;;N;;;;;
+1848;MONGOLIAN LETTER TODO OE;Lo;0;L;;;;;N;;;;;
+1849;MONGOLIAN LETTER TODO UE;Lo;0;L;;;;;N;;;;;
+184A;MONGOLIAN LETTER TODO ANG;Lo;0;L;;;;;N;;;;;
+184B;MONGOLIAN LETTER TODO BA;Lo;0;L;;;;;N;;;;;
+184C;MONGOLIAN LETTER TODO PA;Lo;0;L;;;;;N;;;;;
+184D;MONGOLIAN LETTER TODO QA;Lo;0;L;;;;;N;;;;;
+184E;MONGOLIAN LETTER TODO GA;Lo;0;L;;;;;N;;;;;
+184F;MONGOLIAN LETTER TODO MA;Lo;0;L;;;;;N;;;;;
+1850;MONGOLIAN LETTER TODO TA;Lo;0;L;;;;;N;;;;;
+1851;MONGOLIAN LETTER TODO DA;Lo;0;L;;;;;N;;;;;
+1852;MONGOLIAN LETTER TODO CHA;Lo;0;L;;;;;N;;;;;
+1853;MONGOLIAN LETTER TODO JA;Lo;0;L;;;;;N;;;;;
+1854;MONGOLIAN LETTER TODO TSA;Lo;0;L;;;;;N;;;;;
+1855;MONGOLIAN LETTER TODO YA;Lo;0;L;;;;;N;;;;;
+1856;MONGOLIAN LETTER TODO WA;Lo;0;L;;;;;N;;;;;
+1857;MONGOLIAN LETTER TODO KA;Lo;0;L;;;;;N;;;;;
+1858;MONGOLIAN LETTER TODO GAA;Lo;0;L;;;;;N;;;;;
+1859;MONGOLIAN LETTER TODO HAA;Lo;0;L;;;;;N;;;;;
+185A;MONGOLIAN LETTER TODO JIA;Lo;0;L;;;;;N;;;;;
+185B;MONGOLIAN LETTER TODO NIA;Lo;0;L;;;;;N;;;;;
+185C;MONGOLIAN LETTER TODO DZA;Lo;0;L;;;;;N;;;;;
+185D;MONGOLIAN LETTER SIBE E;Lo;0;L;;;;;N;;;;;
+185E;MONGOLIAN LETTER SIBE I;Lo;0;L;;;;;N;;;;;
+185F;MONGOLIAN LETTER SIBE IY;Lo;0;L;;;;;N;;;;;
+1860;MONGOLIAN LETTER SIBE UE;Lo;0;L;;;;;N;;;;;
+1861;MONGOLIAN LETTER SIBE U;Lo;0;L;;;;;N;;;;;
+1862;MONGOLIAN LETTER SIBE ANG;Lo;0;L;;;;;N;;;;;
+1863;MONGOLIAN LETTER SIBE KA;Lo;0;L;;;;;N;;;;;
+1864;MONGOLIAN LETTER SIBE GA;Lo;0;L;;;;;N;;;;;
+1865;MONGOLIAN LETTER SIBE HA;Lo;0;L;;;;;N;;;;;
+1866;MONGOLIAN LETTER SIBE PA;Lo;0;L;;;;;N;;;;;
+1867;MONGOLIAN LETTER SIBE SHA;Lo;0;L;;;;;N;;;;;
+1868;MONGOLIAN LETTER SIBE TA;Lo;0;L;;;;;N;;;;;
+1869;MONGOLIAN LETTER SIBE DA;Lo;0;L;;;;;N;;;;;
+186A;MONGOLIAN LETTER SIBE JA;Lo;0;L;;;;;N;;;;;
+186B;MONGOLIAN LETTER SIBE FA;Lo;0;L;;;;;N;;;;;
+186C;MONGOLIAN LETTER SIBE GAA;Lo;0;L;;;;;N;;;;;
+186D;MONGOLIAN LETTER SIBE HAA;Lo;0;L;;;;;N;;;;;
+186E;MONGOLIAN LETTER SIBE TSA;Lo;0;L;;;;;N;;;;;
+186F;MONGOLIAN LETTER SIBE ZA;Lo;0;L;;;;;N;;;;;
+1870;MONGOLIAN LETTER SIBE RAA;Lo;0;L;;;;;N;;;;;
+1871;MONGOLIAN LETTER SIBE CHA;Lo;0;L;;;;;N;;;;;
+1872;MONGOLIAN LETTER SIBE ZHA;Lo;0;L;;;;;N;;;;;
+1873;MONGOLIAN LETTER MANCHU I;Lo;0;L;;;;;N;;;;;
+1874;MONGOLIAN LETTER MANCHU KA;Lo;0;L;;;;;N;;;;;
+1875;MONGOLIAN LETTER MANCHU RA;Lo;0;L;;;;;N;;;;;
+1876;MONGOLIAN LETTER MANCHU FA;Lo;0;L;;;;;N;;;;;
+1877;MONGOLIAN LETTER MANCHU ZHA;Lo;0;L;;;;;N;;;;;
+1880;MONGOLIAN LETTER ALI GALI ANUSVARA ONE;Lo;0;L;;;;;N;;;;;
+1881;MONGOLIAN LETTER ALI GALI VISARGA ONE;Lo;0;L;;;;;N;;;;;
+1882;MONGOLIAN LETTER ALI GALI DAMARU;Lo;0;L;;;;;N;;;;;
+1883;MONGOLIAN LETTER ALI GALI UBADAMA;Lo;0;L;;;;;N;;;;;
+1884;MONGOLIAN LETTER ALI GALI INVERTED UBADAMA;Lo;0;L;;;;;N;;;;;
+1885;MONGOLIAN LETTER ALI GALI BALUDA;Lo;0;L;;;;;N;;;;;
+1886;MONGOLIAN LETTER ALI GALI THREE BALUDA;Lo;0;L;;;;;N;;;;;
+1887;MONGOLIAN LETTER ALI GALI A;Lo;0;L;;;;;N;;;;;
+1888;MONGOLIAN LETTER ALI GALI I;Lo;0;L;;;;;N;;;;;
+1889;MONGOLIAN LETTER ALI GALI KA;Lo;0;L;;;;;N;;;;;
+188A;MONGOLIAN LETTER ALI GALI NGA;Lo;0;L;;;;;N;;;;;
+188B;MONGOLIAN LETTER ALI GALI CA;Lo;0;L;;;;;N;;;;;
+188C;MONGOLIAN LETTER ALI GALI TTA;Lo;0;L;;;;;N;;;;;
+188D;MONGOLIAN LETTER ALI GALI TTHA;Lo;0;L;;;;;N;;;;;
+188E;MONGOLIAN LETTER ALI GALI DDA;Lo;0;L;;;;;N;;;;;
+188F;MONGOLIAN LETTER ALI GALI NNA;Lo;0;L;;;;;N;;;;;
+1890;MONGOLIAN LETTER ALI GALI TA;Lo;0;L;;;;;N;;;;;
+1891;MONGOLIAN LETTER ALI GALI DA;Lo;0;L;;;;;N;;;;;
+1892;MONGOLIAN LETTER ALI GALI PA;Lo;0;L;;;;;N;;;;;
+1893;MONGOLIAN LETTER ALI GALI PHA;Lo;0;L;;;;;N;;;;;
+1894;MONGOLIAN LETTER ALI GALI SSA;Lo;0;L;;;;;N;;;;;
+1895;MONGOLIAN LETTER ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+1896;MONGOLIAN LETTER ALI GALI ZA;Lo;0;L;;;;;N;;;;;
+1897;MONGOLIAN LETTER ALI GALI AH;Lo;0;L;;;;;N;;;;;
+1898;MONGOLIAN LETTER TODO ALI GALI TA;Lo;0;L;;;;;N;;;;;
+1899;MONGOLIAN LETTER TODO ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+189A;MONGOLIAN LETTER MANCHU ALI GALI GHA;Lo;0;L;;;;;N;;;;;
+189B;MONGOLIAN LETTER MANCHU ALI GALI NGA;Lo;0;L;;;;;N;;;;;
+189C;MONGOLIAN LETTER MANCHU ALI GALI CA;Lo;0;L;;;;;N;;;;;
+189D;MONGOLIAN LETTER MANCHU ALI GALI JHA;Lo;0;L;;;;;N;;;;;
+189E;MONGOLIAN LETTER MANCHU ALI GALI TTA;Lo;0;L;;;;;N;;;;;
+189F;MONGOLIAN LETTER MANCHU ALI GALI DDHA;Lo;0;L;;;;;N;;;;;
+18A0;MONGOLIAN LETTER MANCHU ALI GALI TA;Lo;0;L;;;;;N;;;;;
+18A1;MONGOLIAN LETTER MANCHU ALI GALI DHA;Lo;0;L;;;;;N;;;;;
+18A2;MONGOLIAN LETTER MANCHU ALI GALI SSA;Lo;0;L;;;;;N;;;;;
+18A3;MONGOLIAN LETTER MANCHU ALI GALI CYA;Lo;0;L;;;;;N;;;;;
+18A4;MONGOLIAN LETTER MANCHU ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+18A5;MONGOLIAN LETTER MANCHU ALI GALI ZA;Lo;0;L;;;;;N;;;;;
+18A6;MONGOLIAN LETTER ALI GALI HALF U;Lo;0;L;;;;;N;;;;;
+18A7;MONGOLIAN LETTER ALI GALI HALF YA;Lo;0;L;;;;;N;;;;;
+18A8;MONGOLIAN LETTER MANCHU ALI GALI BHA;Lo;0;L;;;;;N;;;;;
+18A9;MONGOLIAN LETTER ALI GALI DAGALGA;Mn;228;NSM;;;;;N;;;;;
+1E00;LATIN CAPITAL LETTER A WITH RING BELOW;Lu;0;L;0041 0325;;;;N;;;;1E01;
+1E01;LATIN SMALL LETTER A WITH RING BELOW;Ll;0;L;0061 0325;;;;N;;;1E00;;1E00
+1E02;LATIN CAPITAL LETTER B WITH DOT ABOVE;Lu;0;L;0042 0307;;;;N;;;;1E03;
+1E03;LATIN SMALL LETTER B WITH DOT ABOVE;Ll;0;L;0062 0307;;;;N;;;1E02;;1E02
+1E04;LATIN CAPITAL LETTER B WITH DOT BELOW;Lu;0;L;0042 0323;;;;N;;;;1E05;
+1E05;LATIN SMALL LETTER B WITH DOT BELOW;Ll;0;L;0062 0323;;;;N;;;1E04;;1E04
+1E06;LATIN CAPITAL LETTER B WITH LINE BELOW;Lu;0;L;0042 0331;;;;N;;;;1E07;
+1E07;LATIN SMALL LETTER B WITH LINE BELOW;Ll;0;L;0062 0331;;;;N;;;1E06;;1E06
+1E08;LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE;Lu;0;L;00C7 0301;;;;N;;;;1E09;
+1E09;LATIN SMALL LETTER C WITH CEDILLA AND ACUTE;Ll;0;L;00E7 0301;;;;N;;;1E08;;1E08
+1E0A;LATIN CAPITAL LETTER D WITH DOT ABOVE;Lu;0;L;0044 0307;;;;N;;;;1E0B;
+1E0B;LATIN SMALL LETTER D WITH DOT ABOVE;Ll;0;L;0064 0307;;;;N;;;1E0A;;1E0A
+1E0C;LATIN CAPITAL LETTER D WITH DOT BELOW;Lu;0;L;0044 0323;;;;N;;;;1E0D;
+1E0D;LATIN SMALL LETTER D WITH DOT BELOW;Ll;0;L;0064 0323;;;;N;;;1E0C;;1E0C
+1E0E;LATIN CAPITAL LETTER D WITH LINE BELOW;Lu;0;L;0044 0331;;;;N;;;;1E0F;
+1E0F;LATIN SMALL LETTER D WITH LINE BELOW;Ll;0;L;0064 0331;;;;N;;;1E0E;;1E0E
+1E10;LATIN CAPITAL LETTER D WITH CEDILLA;Lu;0;L;0044 0327;;;;N;;;;1E11;
+1E11;LATIN SMALL LETTER D WITH CEDILLA;Ll;0;L;0064 0327;;;;N;;;1E10;;1E10
+1E12;LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW;Lu;0;L;0044 032D;;;;N;;;;1E13;
+1E13;LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW;Ll;0;L;0064 032D;;;;N;;;1E12;;1E12
+1E14;LATIN CAPITAL LETTER E WITH MACRON AND GRAVE;Lu;0;L;0112 0300;;;;N;;;;1E15;
+1E15;LATIN SMALL LETTER E WITH MACRON AND GRAVE;Ll;0;L;0113 0300;;;;N;;;1E14;;1E14
+1E16;LATIN CAPITAL LETTER E WITH MACRON AND ACUTE;Lu;0;L;0112 0301;;;;N;;;;1E17;
+1E17;LATIN SMALL LETTER E WITH MACRON AND ACUTE;Ll;0;L;0113 0301;;;;N;;;1E16;;1E16
+1E18;LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW;Lu;0;L;0045 032D;;;;N;;;;1E19;
+1E19;LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW;Ll;0;L;0065 032D;;;;N;;;1E18;;1E18
+1E1A;LATIN CAPITAL LETTER E WITH TILDE BELOW;Lu;0;L;0045 0330;;;;N;;;;1E1B;
+1E1B;LATIN SMALL LETTER E WITH TILDE BELOW;Ll;0;L;0065 0330;;;;N;;;1E1A;;1E1A
+1E1C;LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE;Lu;0;L;0228 0306;;;;N;;;;1E1D;
+1E1D;LATIN SMALL LETTER E WITH CEDILLA AND BREVE;Ll;0;L;0229 0306;;;;N;;;1E1C;;1E1C
+1E1E;LATIN CAPITAL LETTER F WITH DOT ABOVE;Lu;0;L;0046 0307;;;;N;;;;1E1F;
+1E1F;LATIN SMALL LETTER F WITH DOT ABOVE;Ll;0;L;0066 0307;;;;N;;;1E1E;;1E1E
+1E20;LATIN CAPITAL LETTER G WITH MACRON;Lu;0;L;0047 0304;;;;N;;;;1E21;
+1E21;LATIN SMALL LETTER G WITH MACRON;Ll;0;L;0067 0304;;;;N;;;1E20;;1E20
+1E22;LATIN CAPITAL LETTER H WITH DOT ABOVE;Lu;0;L;0048 0307;;;;N;;;;1E23;
+1E23;LATIN SMALL LETTER H WITH DOT ABOVE;Ll;0;L;0068 0307;;;;N;;;1E22;;1E22
+1E24;LATIN CAPITAL LETTER H WITH DOT BELOW;Lu;0;L;0048 0323;;;;N;;;;1E25;
+1E25;LATIN SMALL LETTER H WITH DOT BELOW;Ll;0;L;0068 0323;;;;N;;;1E24;;1E24
+1E26;LATIN CAPITAL LETTER H WITH DIAERESIS;Lu;0;L;0048 0308;;;;N;;;;1E27;
+1E27;LATIN SMALL LETTER H WITH DIAERESIS;Ll;0;L;0068 0308;;;;N;;;1E26;;1E26
+1E28;LATIN CAPITAL LETTER H WITH CEDILLA;Lu;0;L;0048 0327;;;;N;;;;1E29;
+1E29;LATIN SMALL LETTER H WITH CEDILLA;Ll;0;L;0068 0327;;;;N;;;1E28;;1E28
+1E2A;LATIN CAPITAL LETTER H WITH BREVE BELOW;Lu;0;L;0048 032E;;;;N;;;;1E2B;
+1E2B;LATIN SMALL LETTER H WITH BREVE BELOW;Ll;0;L;0068 032E;;;;N;;;1E2A;;1E2A
+1E2C;LATIN CAPITAL LETTER I WITH TILDE BELOW;Lu;0;L;0049 0330;;;;N;;;;1E2D;
+1E2D;LATIN SMALL LETTER I WITH TILDE BELOW;Ll;0;L;0069 0330;;;;N;;;1E2C;;1E2C
+1E2E;LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE;Lu;0;L;00CF 0301;;;;N;;;;1E2F;
+1E2F;LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE;Ll;0;L;00EF 0301;;;;N;;;1E2E;;1E2E
+1E30;LATIN CAPITAL LETTER K WITH ACUTE;Lu;0;L;004B 0301;;;;N;;;;1E31;
+1E31;LATIN SMALL LETTER K WITH ACUTE;Ll;0;L;006B 0301;;;;N;;;1E30;;1E30
+1E32;LATIN CAPITAL LETTER K WITH DOT BELOW;Lu;0;L;004B 0323;;;;N;;;;1E33;
+1E33;LATIN SMALL LETTER K WITH DOT BELOW;Ll;0;L;006B 0323;;;;N;;;1E32;;1E32
+1E34;LATIN CAPITAL LETTER K WITH LINE BELOW;Lu;0;L;004B 0331;;;;N;;;;1E35;
+1E35;LATIN SMALL LETTER K WITH LINE BELOW;Ll;0;L;006B 0331;;;;N;;;1E34;;1E34
+1E36;LATIN CAPITAL LETTER L WITH DOT BELOW;Lu;0;L;004C 0323;;;;N;;;;1E37;
+1E37;LATIN SMALL LETTER L WITH DOT BELOW;Ll;0;L;006C 0323;;;;N;;;1E36;;1E36
+1E38;LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON;Lu;0;L;1E36 0304;;;;N;;;;1E39;
+1E39;LATIN SMALL LETTER L WITH DOT BELOW AND MACRON;Ll;0;L;1E37 0304;;;;N;;;1E38;;1E38
+1E3A;LATIN CAPITAL LETTER L WITH LINE BELOW;Lu;0;L;004C 0331;;;;N;;;;1E3B;
+1E3B;LATIN SMALL LETTER L WITH LINE BELOW;Ll;0;L;006C 0331;;;;N;;;1E3A;;1E3A
+1E3C;LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW;Lu;0;L;004C 032D;;;;N;;;;1E3D;
+1E3D;LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW;Ll;0;L;006C 032D;;;;N;;;1E3C;;1E3C
+1E3E;LATIN CAPITAL LETTER M WITH ACUTE;Lu;0;L;004D 0301;;;;N;;;;1E3F;
+1E3F;LATIN SMALL LETTER M WITH ACUTE;Ll;0;L;006D 0301;;;;N;;;1E3E;;1E3E
+1E40;LATIN CAPITAL LETTER M WITH DOT ABOVE;Lu;0;L;004D 0307;;;;N;;;;1E41;
+1E41;LATIN SMALL LETTER M WITH DOT ABOVE;Ll;0;L;006D 0307;;;;N;;;1E40;;1E40
+1E42;LATIN CAPITAL LETTER M WITH DOT BELOW;Lu;0;L;004D 0323;;;;N;;;;1E43;
+1E43;LATIN SMALL LETTER M WITH DOT BELOW;Ll;0;L;006D 0323;;;;N;;;1E42;;1E42
+1E44;LATIN CAPITAL LETTER N WITH DOT ABOVE;Lu;0;L;004E 0307;;;;N;;;;1E45;
+1E45;LATIN SMALL LETTER N WITH DOT ABOVE;Ll;0;L;006E 0307;;;;N;;;1E44;;1E44
+1E46;LATIN CAPITAL LETTER N WITH DOT BELOW;Lu;0;L;004E 0323;;;;N;;;;1E47;
+1E47;LATIN SMALL LETTER N WITH DOT BELOW;Ll;0;L;006E 0323;;;;N;;;1E46;;1E46
+1E48;LATIN CAPITAL LETTER N WITH LINE BELOW;Lu;0;L;004E 0331;;;;N;;;;1E49;
+1E49;LATIN SMALL LETTER N WITH LINE BELOW;Ll;0;L;006E 0331;;;;N;;;1E48;;1E48
+1E4A;LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW;Lu;0;L;004E 032D;;;;N;;;;1E4B;
+1E4B;LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW;Ll;0;L;006E 032D;;;;N;;;1E4A;;1E4A
+1E4C;LATIN CAPITAL LETTER O WITH TILDE AND ACUTE;Lu;0;L;00D5 0301;;;;N;;;;1E4D;
+1E4D;LATIN SMALL LETTER O WITH TILDE AND ACUTE;Ll;0;L;00F5 0301;;;;N;;;1E4C;;1E4C
+1E4E;LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS;Lu;0;L;00D5 0308;;;;N;;;;1E4F;
+1E4F;LATIN SMALL LETTER O WITH TILDE AND DIAERESIS;Ll;0;L;00F5 0308;;;;N;;;1E4E;;1E4E
+1E50;LATIN CAPITAL LETTER O WITH MACRON AND GRAVE;Lu;0;L;014C 0300;;;;N;;;;1E51;
+1E51;LATIN SMALL LETTER O WITH MACRON AND GRAVE;Ll;0;L;014D 0300;;;;N;;;1E50;;1E50
+1E52;LATIN CAPITAL LETTER O WITH MACRON AND ACUTE;Lu;0;L;014C 0301;;;;N;;;;1E53;
+1E53;LATIN SMALL LETTER O WITH MACRON AND ACUTE;Ll;0;L;014D 0301;;;;N;;;1E52;;1E52
+1E54;LATIN CAPITAL LETTER P WITH ACUTE;Lu;0;L;0050 0301;;;;N;;;;1E55;
+1E55;LATIN SMALL LETTER P WITH ACUTE;Ll;0;L;0070 0301;;;;N;;;1E54;;1E54
+1E56;LATIN CAPITAL LETTER P WITH DOT ABOVE;Lu;0;L;0050 0307;;;;N;;;;1E57;
+1E57;LATIN SMALL LETTER P WITH DOT ABOVE;Ll;0;L;0070 0307;;;;N;;;1E56;;1E56
+1E58;LATIN CAPITAL LETTER R WITH DOT ABOVE;Lu;0;L;0052 0307;;;;N;;;;1E59;
+1E59;LATIN SMALL LETTER R WITH DOT ABOVE;Ll;0;L;0072 0307;;;;N;;;1E58;;1E58
+1E5A;LATIN CAPITAL LETTER R WITH DOT BELOW;Lu;0;L;0052 0323;;;;N;;;;1E5B;
+1E5B;LATIN SMALL LETTER R WITH DOT BELOW;Ll;0;L;0072 0323;;;;N;;;1E5A;;1E5A
+1E5C;LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON;Lu;0;L;1E5A 0304;;;;N;;;;1E5D;
+1E5D;LATIN SMALL LETTER R WITH DOT BELOW AND MACRON;Ll;0;L;1E5B 0304;;;;N;;;1E5C;;1E5C
+1E5E;LATIN CAPITAL LETTER R WITH LINE BELOW;Lu;0;L;0052 0331;;;;N;;;;1E5F;
+1E5F;LATIN SMALL LETTER R WITH LINE BELOW;Ll;0;L;0072 0331;;;;N;;;1E5E;;1E5E
+1E60;LATIN CAPITAL LETTER S WITH DOT ABOVE;Lu;0;L;0053 0307;;;;N;;;;1E61;
+1E61;LATIN SMALL LETTER S WITH DOT ABOVE;Ll;0;L;0073 0307;;;;N;;;1E60;;1E60
+1E62;LATIN CAPITAL LETTER S WITH DOT BELOW;Lu;0;L;0053 0323;;;;N;;;;1E63;
+1E63;LATIN SMALL LETTER S WITH DOT BELOW;Ll;0;L;0073 0323;;;;N;;;1E62;;1E62
+1E64;LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE;Lu;0;L;015A 0307;;;;N;;;;1E65;
+1E65;LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE;Ll;0;L;015B 0307;;;;N;;;1E64;;1E64
+1E66;LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE;Lu;0;L;0160 0307;;;;N;;;;1E67;
+1E67;LATIN SMALL LETTER S WITH CARON AND DOT ABOVE;Ll;0;L;0161 0307;;;;N;;;1E66;;1E66
+1E68;LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE;Lu;0;L;1E62 0307;;;;N;;;;1E69;
+1E69;LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE;Ll;0;L;1E63 0307;;;;N;;;1E68;;1E68
+1E6A;LATIN CAPITAL LETTER T WITH DOT ABOVE;Lu;0;L;0054 0307;;;;N;;;;1E6B;
+1E6B;LATIN SMALL LETTER T WITH DOT ABOVE;Ll;0;L;0074 0307;;;;N;;;1E6A;;1E6A
+1E6C;LATIN CAPITAL LETTER T WITH DOT BELOW;Lu;0;L;0054 0323;;;;N;;;;1E6D;
+1E6D;LATIN SMALL LETTER T WITH DOT BELOW;Ll;0;L;0074 0323;;;;N;;;1E6C;;1E6C
+1E6E;LATIN CAPITAL LETTER T WITH LINE BELOW;Lu;0;L;0054 0331;;;;N;;;;1E6F;
+1E6F;LATIN SMALL LETTER T WITH LINE BELOW;Ll;0;L;0074 0331;;;;N;;;1E6E;;1E6E
+1E70;LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW;Lu;0;L;0054 032D;;;;N;;;;1E71;
+1E71;LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW;Ll;0;L;0074 032D;;;;N;;;1E70;;1E70
+1E72;LATIN CAPITAL LETTER U WITH DIAERESIS BELOW;Lu;0;L;0055 0324;;;;N;;;;1E73;
+1E73;LATIN SMALL LETTER U WITH DIAERESIS BELOW;Ll;0;L;0075 0324;;;;N;;;1E72;;1E72
+1E74;LATIN CAPITAL LETTER U WITH TILDE BELOW;Lu;0;L;0055 0330;;;;N;;;;1E75;
+1E75;LATIN SMALL LETTER U WITH TILDE BELOW;Ll;0;L;0075 0330;;;;N;;;1E74;;1E74
+1E76;LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW;Lu;0;L;0055 032D;;;;N;;;;1E77;
+1E77;LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW;Ll;0;L;0075 032D;;;;N;;;1E76;;1E76
+1E78;LATIN CAPITAL LETTER U WITH TILDE AND ACUTE;Lu;0;L;0168 0301;;;;N;;;;1E79;
+1E79;LATIN SMALL LETTER U WITH TILDE AND ACUTE;Ll;0;L;0169 0301;;;;N;;;1E78;;1E78
+1E7A;LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS;Lu;0;L;016A 0308;;;;N;;;;1E7B;
+1E7B;LATIN SMALL LETTER U WITH MACRON AND DIAERESIS;Ll;0;L;016B 0308;;;;N;;;1E7A;;1E7A
+1E7C;LATIN CAPITAL LETTER V WITH TILDE;Lu;0;L;0056 0303;;;;N;;;;1E7D;
+1E7D;LATIN SMALL LETTER V WITH TILDE;Ll;0;L;0076 0303;;;;N;;;1E7C;;1E7C
+1E7E;LATIN CAPITAL LETTER V WITH DOT BELOW;Lu;0;L;0056 0323;;;;N;;;;1E7F;
+1E7F;LATIN SMALL LETTER V WITH DOT BELOW;Ll;0;L;0076 0323;;;;N;;;1E7E;;1E7E
+1E80;LATIN CAPITAL LETTER W WITH GRAVE;Lu;0;L;0057 0300;;;;N;;;;1E81;
+1E81;LATIN SMALL LETTER W WITH GRAVE;Ll;0;L;0077 0300;;;;N;;;1E80;;1E80
+1E82;LATIN CAPITAL LETTER W WITH ACUTE;Lu;0;L;0057 0301;;;;N;;;;1E83;
+1E83;LATIN SMALL LETTER W WITH ACUTE;Ll;0;L;0077 0301;;;;N;;;1E82;;1E82
+1E84;LATIN CAPITAL LETTER W WITH DIAERESIS;Lu;0;L;0057 0308;;;;N;;;;1E85;
+1E85;LATIN SMALL LETTER W WITH DIAERESIS;Ll;0;L;0077 0308;;;;N;;;1E84;;1E84
+1E86;LATIN CAPITAL LETTER W WITH DOT ABOVE;Lu;0;L;0057 0307;;;;N;;;;1E87;
+1E87;LATIN SMALL LETTER W WITH DOT ABOVE;Ll;0;L;0077 0307;;;;N;;;1E86;;1E86
+1E88;LATIN CAPITAL LETTER W WITH DOT BELOW;Lu;0;L;0057 0323;;;;N;;;;1E89;
+1E89;LATIN SMALL LETTER W WITH DOT BELOW;Ll;0;L;0077 0323;;;;N;;;1E88;;1E88
+1E8A;LATIN CAPITAL LETTER X WITH DOT ABOVE;Lu;0;L;0058 0307;;;;N;;;;1E8B;
+1E8B;LATIN SMALL LETTER X WITH DOT ABOVE;Ll;0;L;0078 0307;;;;N;;;1E8A;;1E8A
+1E8C;LATIN CAPITAL LETTER X WITH DIAERESIS;Lu;0;L;0058 0308;;;;N;;;;1E8D;
+1E8D;LATIN SMALL LETTER X WITH DIAERESIS;Ll;0;L;0078 0308;;;;N;;;1E8C;;1E8C
+1E8E;LATIN CAPITAL LETTER Y WITH DOT ABOVE;Lu;0;L;0059 0307;;;;N;;;;1E8F;
+1E8F;LATIN SMALL LETTER Y WITH DOT ABOVE;Ll;0;L;0079 0307;;;;N;;;1E8E;;1E8E
+1E90;LATIN CAPITAL LETTER Z WITH CIRCUMFLEX;Lu;0;L;005A 0302;;;;N;;;;1E91;
+1E91;LATIN SMALL LETTER Z WITH CIRCUMFLEX;Ll;0;L;007A 0302;;;;N;;;1E90;;1E90
+1E92;LATIN CAPITAL LETTER Z WITH DOT BELOW;Lu;0;L;005A 0323;;;;N;;;;1E93;
+1E93;LATIN SMALL LETTER Z WITH DOT BELOW;Ll;0;L;007A 0323;;;;N;;;1E92;;1E92
+1E94;LATIN CAPITAL LETTER Z WITH LINE BELOW;Lu;0;L;005A 0331;;;;N;;;;1E95;
+1E95;LATIN SMALL LETTER Z WITH LINE BELOW;Ll;0;L;007A 0331;;;;N;;;1E94;;1E94
+1E96;LATIN SMALL LETTER H WITH LINE BELOW;Ll;0;L;0068 0331;;;;N;;;;;
+1E97;LATIN SMALL LETTER T WITH DIAERESIS;Ll;0;L;0074 0308;;;;N;;;;;
+1E98;LATIN SMALL LETTER W WITH RING ABOVE;Ll;0;L;0077 030A;;;;N;;;;;
+1E99;LATIN SMALL LETTER Y WITH RING ABOVE;Ll;0;L;0079 030A;;;;N;;;;;
+1E9A;LATIN SMALL LETTER A WITH RIGHT HALF RING;Ll;0;L;<compat> 0061 02BE;;;;N;;;;;
+1E9B;LATIN SMALL LETTER LONG S WITH DOT ABOVE;Ll;0;L;017F 0307;;;;N;;;1E60;;1E60
+1EA0;LATIN CAPITAL LETTER A WITH DOT BELOW;Lu;0;L;0041 0323;;;;N;;;;1EA1;
+1EA1;LATIN SMALL LETTER A WITH DOT BELOW;Ll;0;L;0061 0323;;;;N;;;1EA0;;1EA0
+1EA2;LATIN CAPITAL LETTER A WITH HOOK ABOVE;Lu;0;L;0041 0309;;;;N;;;;1EA3;
+1EA3;LATIN SMALL LETTER A WITH HOOK ABOVE;Ll;0;L;0061 0309;;;;N;;;1EA2;;1EA2
+1EA4;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00C2 0301;;;;N;;;;1EA5;
+1EA5;LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00E2 0301;;;;N;;;1EA4;;1EA4
+1EA6;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00C2 0300;;;;N;;;;1EA7;
+1EA7;LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00E2 0300;;;;N;;;1EA6;;1EA6
+1EA8;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00C2 0309;;;;N;;;;1EA9;
+1EA9;LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00E2 0309;;;;N;;;1EA8;;1EA8
+1EAA;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE;Lu;0;L;00C2 0303;;;;N;;;;1EAB;
+1EAB;LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE;Ll;0;L;00E2 0303;;;;N;;;1EAA;;1EAA
+1EAC;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EA0 0302;;;;N;;;;1EAD;
+1EAD;LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EA1 0302;;;;N;;;1EAC;;1EAC
+1EAE;LATIN CAPITAL LETTER A WITH BREVE AND ACUTE;Lu;0;L;0102 0301;;;;N;;;;1EAF;
+1EAF;LATIN SMALL LETTER A WITH BREVE AND ACUTE;Ll;0;L;0103 0301;;;;N;;;1EAE;;1EAE
+1EB0;LATIN CAPITAL LETTER A WITH BREVE AND GRAVE;Lu;0;L;0102 0300;;;;N;;;;1EB1;
+1EB1;LATIN SMALL LETTER A WITH BREVE AND GRAVE;Ll;0;L;0103 0300;;;;N;;;1EB0;;1EB0
+1EB2;LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE;Lu;0;L;0102 0309;;;;N;;;;1EB3;
+1EB3;LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE;Ll;0;L;0103 0309;;;;N;;;1EB2;;1EB2
+1EB4;LATIN CAPITAL LETTER A WITH BREVE AND TILDE;Lu;0;L;0102 0303;;;;N;;;;1EB5;
+1EB5;LATIN SMALL LETTER A WITH BREVE AND TILDE;Ll;0;L;0103 0303;;;;N;;;1EB4;;1EB4
+1EB6;LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW;Lu;0;L;1EA0 0306;;;;N;;;;1EB7;
+1EB7;LATIN SMALL LETTER A WITH BREVE AND DOT BELOW;Ll;0;L;1EA1 0306;;;;N;;;1EB6;;1EB6
+1EB8;LATIN CAPITAL LETTER E WITH DOT BELOW;Lu;0;L;0045 0323;;;;N;;;;1EB9;
+1EB9;LATIN SMALL LETTER E WITH DOT BELOW;Ll;0;L;0065 0323;;;;N;;;1EB8;;1EB8
+1EBA;LATIN CAPITAL LETTER E WITH HOOK ABOVE;Lu;0;L;0045 0309;;;;N;;;;1EBB;
+1EBB;LATIN SMALL LETTER E WITH HOOK ABOVE;Ll;0;L;0065 0309;;;;N;;;1EBA;;1EBA
+1EBC;LATIN CAPITAL LETTER E WITH TILDE;Lu;0;L;0045 0303;;;;N;;;;1EBD;
+1EBD;LATIN SMALL LETTER E WITH TILDE;Ll;0;L;0065 0303;;;;N;;;1EBC;;1EBC
+1EBE;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00CA 0301;;;;N;;;;1EBF;
+1EBF;LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00EA 0301;;;;N;;;1EBE;;1EBE
+1EC0;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00CA 0300;;;;N;;;;1EC1;
+1EC1;LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00EA 0300;;;;N;;;1EC0;;1EC0
+1EC2;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00CA 0309;;;;N;;;;1EC3;
+1EC3;LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00EA 0309;;;;N;;;1EC2;;1EC2
+1EC4;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE;Lu;0;L;00CA 0303;;;;N;;;;1EC5;
+1EC5;LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE;Ll;0;L;00EA 0303;;;;N;;;1EC4;;1EC4
+1EC6;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EB8 0302;;;;N;;;;1EC7;
+1EC7;LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EB9 0302;;;;N;;;1EC6;;1EC6
+1EC8;LATIN CAPITAL LETTER I WITH HOOK ABOVE;Lu;0;L;0049 0309;;;;N;;;;1EC9;
+1EC9;LATIN SMALL LETTER I WITH HOOK ABOVE;Ll;0;L;0069 0309;;;;N;;;1EC8;;1EC8
+1ECA;LATIN CAPITAL LETTER I WITH DOT BELOW;Lu;0;L;0049 0323;;;;N;;;;1ECB;
+1ECB;LATIN SMALL LETTER I WITH DOT BELOW;Ll;0;L;0069 0323;;;;N;;;1ECA;;1ECA
+1ECC;LATIN CAPITAL LETTER O WITH DOT BELOW;Lu;0;L;004F 0323;;;;N;;;;1ECD;
+1ECD;LATIN SMALL LETTER O WITH DOT BELOW;Ll;0;L;006F 0323;;;;N;;;1ECC;;1ECC
+1ECE;LATIN CAPITAL LETTER O WITH HOOK ABOVE;Lu;0;L;004F 0309;;;;N;;;;1ECF;
+1ECF;LATIN SMALL LETTER O WITH HOOK ABOVE;Ll;0;L;006F 0309;;;;N;;;1ECE;;1ECE
+1ED0;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00D4 0301;;;;N;;;;1ED1;
+1ED1;LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00F4 0301;;;;N;;;1ED0;;1ED0
+1ED2;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00D4 0300;;;;N;;;;1ED3;
+1ED3;LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00F4 0300;;;;N;;;1ED2;;1ED2
+1ED4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00D4 0309;;;;N;;;;1ED5;
+1ED5;LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00F4 0309;;;;N;;;1ED4;;1ED4
+1ED6;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE;Lu;0;L;00D4 0303;;;;N;;;;1ED7;
+1ED7;LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE;Ll;0;L;00F4 0303;;;;N;;;1ED6;;1ED6
+1ED8;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1ECC 0302;;;;N;;;;1ED9;
+1ED9;LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1ECD 0302;;;;N;;;1ED8;;1ED8
+1EDA;LATIN CAPITAL LETTER O WITH HORN AND ACUTE;Lu;0;L;01A0 0301;;;;N;;;;1EDB;
+1EDB;LATIN SMALL LETTER O WITH HORN AND ACUTE;Ll;0;L;01A1 0301;;;;N;;;1EDA;;1EDA
+1EDC;LATIN CAPITAL LETTER O WITH HORN AND GRAVE;Lu;0;L;01A0 0300;;;;N;;;;1EDD;
+1EDD;LATIN SMALL LETTER O WITH HORN AND GRAVE;Ll;0;L;01A1 0300;;;;N;;;1EDC;;1EDC
+1EDE;LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE;Lu;0;L;01A0 0309;;;;N;;;;1EDF;
+1EDF;LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE;Ll;0;L;01A1 0309;;;;N;;;1EDE;;1EDE
+1EE0;LATIN CAPITAL LETTER O WITH HORN AND TILDE;Lu;0;L;01A0 0303;;;;N;;;;1EE1;
+1EE1;LATIN SMALL LETTER O WITH HORN AND TILDE;Ll;0;L;01A1 0303;;;;N;;;1EE0;;1EE0
+1EE2;LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW;Lu;0;L;01A0 0323;;;;N;;;;1EE3;
+1EE3;LATIN SMALL LETTER O WITH HORN AND DOT BELOW;Ll;0;L;01A1 0323;;;;N;;;1EE2;;1EE2
+1EE4;LATIN CAPITAL LETTER U WITH DOT BELOW;Lu;0;L;0055 0323;;;;N;;;;1EE5;
+1EE5;LATIN SMALL LETTER U WITH DOT BELOW;Ll;0;L;0075 0323;;;;N;;;1EE4;;1EE4
+1EE6;LATIN CAPITAL LETTER U WITH HOOK ABOVE;Lu;0;L;0055 0309;;;;N;;;;1EE7;
+1EE7;LATIN SMALL LETTER U WITH HOOK ABOVE;Ll;0;L;0075 0309;;;;N;;;1EE6;;1EE6
+1EE8;LATIN CAPITAL LETTER U WITH HORN AND ACUTE;Lu;0;L;01AF 0301;;;;N;;;;1EE9;
+1EE9;LATIN SMALL LETTER U WITH HORN AND ACUTE;Ll;0;L;01B0 0301;;;;N;;;1EE8;;1EE8
+1EEA;LATIN CAPITAL LETTER U WITH HORN AND GRAVE;Lu;0;L;01AF 0300;;;;N;;;;1EEB;
+1EEB;LATIN SMALL LETTER U WITH HORN AND GRAVE;Ll;0;L;01B0 0300;;;;N;;;1EEA;;1EEA
+1EEC;LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE;Lu;0;L;01AF 0309;;;;N;;;;1EED;
+1EED;LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE;Ll;0;L;01B0 0309;;;;N;;;1EEC;;1EEC
+1EEE;LATIN CAPITAL LETTER U WITH HORN AND TILDE;Lu;0;L;01AF 0303;;;;N;;;;1EEF;
+1EEF;LATIN SMALL LETTER U WITH HORN AND TILDE;Ll;0;L;01B0 0303;;;;N;;;1EEE;;1EEE
+1EF0;LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW;Lu;0;L;01AF 0323;;;;N;;;;1EF1;
+1EF1;LATIN SMALL LETTER U WITH HORN AND DOT BELOW;Ll;0;L;01B0 0323;;;;N;;;1EF0;;1EF0
+1EF2;LATIN CAPITAL LETTER Y WITH GRAVE;Lu;0;L;0059 0300;;;;N;;;;1EF3;
+1EF3;LATIN SMALL LETTER Y WITH GRAVE;Ll;0;L;0079 0300;;;;N;;;1EF2;;1EF2
+1EF4;LATIN CAPITAL LETTER Y WITH DOT BELOW;Lu;0;L;0059 0323;;;;N;;;;1EF5;
+1EF5;LATIN SMALL LETTER Y WITH DOT BELOW;Ll;0;L;0079 0323;;;;N;;;1EF4;;1EF4
+1EF6;LATIN CAPITAL LETTER Y WITH HOOK ABOVE;Lu;0;L;0059 0309;;;;N;;;;1EF7;
+1EF7;LATIN SMALL LETTER Y WITH HOOK ABOVE;Ll;0;L;0079 0309;;;;N;;;1EF6;;1EF6
+1EF8;LATIN CAPITAL LETTER Y WITH TILDE;Lu;0;L;0059 0303;;;;N;;;;1EF9;
+1EF9;LATIN SMALL LETTER Y WITH TILDE;Ll;0;L;0079 0303;;;;N;;;1EF8;;1EF8
+1F00;GREEK SMALL LETTER ALPHA WITH PSILI;Ll;0;L;03B1 0313;;;;N;;;1F08;;1F08
+1F01;GREEK SMALL LETTER ALPHA WITH DASIA;Ll;0;L;03B1 0314;;;;N;;;1F09;;1F09
+1F02;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA;Ll;0;L;1F00 0300;;;;N;;;1F0A;;1F0A
+1F03;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA;Ll;0;L;1F01 0300;;;;N;;;1F0B;;1F0B
+1F04;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA;Ll;0;L;1F00 0301;;;;N;;;1F0C;;1F0C
+1F05;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA;Ll;0;L;1F01 0301;;;;N;;;1F0D;;1F0D
+1F06;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI;Ll;0;L;1F00 0342;;;;N;;;1F0E;;1F0E
+1F07;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI;Ll;0;L;1F01 0342;;;;N;;;1F0F;;1F0F
+1F08;GREEK CAPITAL LETTER ALPHA WITH PSILI;Lu;0;L;0391 0313;;;;N;;;;1F00;
+1F09;GREEK CAPITAL LETTER ALPHA WITH DASIA;Lu;0;L;0391 0314;;;;N;;;;1F01;
+1F0A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA;Lu;0;L;1F08 0300;;;;N;;;;1F02;
+1F0B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA;Lu;0;L;1F09 0300;;;;N;;;;1F03;
+1F0C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA;Lu;0;L;1F08 0301;;;;N;;;;1F04;
+1F0D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA;Lu;0;L;1F09 0301;;;;N;;;;1F05;
+1F0E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI;Lu;0;L;1F08 0342;;;;N;;;;1F06;
+1F0F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI;Lu;0;L;1F09 0342;;;;N;;;;1F07;
+1F10;GREEK SMALL LETTER EPSILON WITH PSILI;Ll;0;L;03B5 0313;;;;N;;;1F18;;1F18
+1F11;GREEK SMALL LETTER EPSILON WITH DASIA;Ll;0;L;03B5 0314;;;;N;;;1F19;;1F19
+1F12;GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA;Ll;0;L;1F10 0300;;;;N;;;1F1A;;1F1A
+1F13;GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA;Ll;0;L;1F11 0300;;;;N;;;1F1B;;1F1B
+1F14;GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA;Ll;0;L;1F10 0301;;;;N;;;1F1C;;1F1C
+1F15;GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA;Ll;0;L;1F11 0301;;;;N;;;1F1D;;1F1D
+1F18;GREEK CAPITAL LETTER EPSILON WITH PSILI;Lu;0;L;0395 0313;;;;N;;;;1F10;
+1F19;GREEK CAPITAL LETTER EPSILON WITH DASIA;Lu;0;L;0395 0314;;;;N;;;;1F11;
+1F1A;GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA;Lu;0;L;1F18 0300;;;;N;;;;1F12;
+1F1B;GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA;Lu;0;L;1F19 0300;;;;N;;;;1F13;
+1F1C;GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA;Lu;0;L;1F18 0301;;;;N;;;;1F14;
+1F1D;GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA;Lu;0;L;1F19 0301;;;;N;;;;1F15;
+1F20;GREEK SMALL LETTER ETA WITH PSILI;Ll;0;L;03B7 0313;;;;N;;;1F28;;1F28
+1F21;GREEK SMALL LETTER ETA WITH DASIA;Ll;0;L;03B7 0314;;;;N;;;1F29;;1F29
+1F22;GREEK SMALL LETTER ETA WITH PSILI AND VARIA;Ll;0;L;1F20 0300;;;;N;;;1F2A;;1F2A
+1F23;GREEK SMALL LETTER ETA WITH DASIA AND VARIA;Ll;0;L;1F21 0300;;;;N;;;1F2B;;1F2B
+1F24;GREEK SMALL LETTER ETA WITH PSILI AND OXIA;Ll;0;L;1F20 0301;;;;N;;;1F2C;;1F2C
+1F25;GREEK SMALL LETTER ETA WITH DASIA AND OXIA;Ll;0;L;1F21 0301;;;;N;;;1F2D;;1F2D
+1F26;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI;Ll;0;L;1F20 0342;;;;N;;;1F2E;;1F2E
+1F27;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI;Ll;0;L;1F21 0342;;;;N;;;1F2F;;1F2F
+1F28;GREEK CAPITAL LETTER ETA WITH PSILI;Lu;0;L;0397 0313;;;;N;;;;1F20;
+1F29;GREEK CAPITAL LETTER ETA WITH DASIA;Lu;0;L;0397 0314;;;;N;;;;1F21;
+1F2A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA;Lu;0;L;1F28 0300;;;;N;;;;1F22;
+1F2B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA;Lu;0;L;1F29 0300;;;;N;;;;1F23;
+1F2C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA;Lu;0;L;1F28 0301;;;;N;;;;1F24;
+1F2D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA;Lu;0;L;1F29 0301;;;;N;;;;1F25;
+1F2E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI;Lu;0;L;1F28 0342;;;;N;;;;1F26;
+1F2F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI;Lu;0;L;1F29 0342;;;;N;;;;1F27;
+1F30;GREEK SMALL LETTER IOTA WITH PSILI;Ll;0;L;03B9 0313;;;;N;;;1F38;;1F38
+1F31;GREEK SMALL LETTER IOTA WITH DASIA;Ll;0;L;03B9 0314;;;;N;;;1F39;;1F39
+1F32;GREEK SMALL LETTER IOTA WITH PSILI AND VARIA;Ll;0;L;1F30 0300;;;;N;;;1F3A;;1F3A
+1F33;GREEK SMALL LETTER IOTA WITH DASIA AND VARIA;Ll;0;L;1F31 0300;;;;N;;;1F3B;;1F3B
+1F34;GREEK SMALL LETTER IOTA WITH PSILI AND OXIA;Ll;0;L;1F30 0301;;;;N;;;1F3C;;1F3C
+1F35;GREEK SMALL LETTER IOTA WITH DASIA AND OXIA;Ll;0;L;1F31 0301;;;;N;;;1F3D;;1F3D
+1F36;GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI;Ll;0;L;1F30 0342;;;;N;;;1F3E;;1F3E
+1F37;GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI;Ll;0;L;1F31 0342;;;;N;;;1F3F;;1F3F
+1F38;GREEK CAPITAL LETTER IOTA WITH PSILI;Lu;0;L;0399 0313;;;;N;;;;1F30;
+1F39;GREEK CAPITAL LETTER IOTA WITH DASIA;Lu;0;L;0399 0314;;;;N;;;;1F31;
+1F3A;GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA;Lu;0;L;1F38 0300;;;;N;;;;1F32;
+1F3B;GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA;Lu;0;L;1F39 0300;;;;N;;;;1F33;
+1F3C;GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA;Lu;0;L;1F38 0301;;;;N;;;;1F34;
+1F3D;GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA;Lu;0;L;1F39 0301;;;;N;;;;1F35;
+1F3E;GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI;Lu;0;L;1F38 0342;;;;N;;;;1F36;
+1F3F;GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI;Lu;0;L;1F39 0342;;;;N;;;;1F37;
+1F40;GREEK SMALL LETTER OMICRON WITH PSILI;Ll;0;L;03BF 0313;;;;N;;;1F48;;1F48
+1F41;GREEK SMALL LETTER OMICRON WITH DASIA;Ll;0;L;03BF 0314;;;;N;;;1F49;;1F49
+1F42;GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA;Ll;0;L;1F40 0300;;;;N;;;1F4A;;1F4A
+1F43;GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA;Ll;0;L;1F41 0300;;;;N;;;1F4B;;1F4B
+1F44;GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA;Ll;0;L;1F40 0301;;;;N;;;1F4C;;1F4C
+1F45;GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA;Ll;0;L;1F41 0301;;;;N;;;1F4D;;1F4D
+1F48;GREEK CAPITAL LETTER OMICRON WITH PSILI;Lu;0;L;039F 0313;;;;N;;;;1F40;
+1F49;GREEK CAPITAL LETTER OMICRON WITH DASIA;Lu;0;L;039F 0314;;;;N;;;;1F41;
+1F4A;GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA;Lu;0;L;1F48 0300;;;;N;;;;1F42;
+1F4B;GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA;Lu;0;L;1F49 0300;;;;N;;;;1F43;
+1F4C;GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA;Lu;0;L;1F48 0301;;;;N;;;;1F44;
+1F4D;GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA;Lu;0;L;1F49 0301;;;;N;;;;1F45;
+1F50;GREEK SMALL LETTER UPSILON WITH PSILI;Ll;0;L;03C5 0313;;;;N;;;;;
+1F51;GREEK SMALL LETTER UPSILON WITH DASIA;Ll;0;L;03C5 0314;;;;N;;;1F59;;1F59
+1F52;GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA;Ll;0;L;1F50 0300;;;;N;;;;;
+1F53;GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA;Ll;0;L;1F51 0300;;;;N;;;1F5B;;1F5B
+1F54;GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA;Ll;0;L;1F50 0301;;;;N;;;;;
+1F55;GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA;Ll;0;L;1F51 0301;;;;N;;;1F5D;;1F5D
+1F56;GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI;Ll;0;L;1F50 0342;;;;N;;;;;
+1F57;GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI;Ll;0;L;1F51 0342;;;;N;;;1F5F;;1F5F
+1F59;GREEK CAPITAL LETTER UPSILON WITH DASIA;Lu;0;L;03A5 0314;;;;N;;;;1F51;
+1F5B;GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA;Lu;0;L;1F59 0300;;;;N;;;;1F53;
+1F5D;GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA;Lu;0;L;1F59 0301;;;;N;;;;1F55;
+1F5F;GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI;Lu;0;L;1F59 0342;;;;N;;;;1F57;
+1F60;GREEK SMALL LETTER OMEGA WITH PSILI;Ll;0;L;03C9 0313;;;;N;;;1F68;;1F68
+1F61;GREEK SMALL LETTER OMEGA WITH DASIA;Ll;0;L;03C9 0314;;;;N;;;1F69;;1F69
+1F62;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA;Ll;0;L;1F60 0300;;;;N;;;1F6A;;1F6A
+1F63;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA;Ll;0;L;1F61 0300;;;;N;;;1F6B;;1F6B
+1F64;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA;Ll;0;L;1F60 0301;;;;N;;;1F6C;;1F6C
+1F65;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA;Ll;0;L;1F61 0301;;;;N;;;1F6D;;1F6D
+1F66;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI;Ll;0;L;1F60 0342;;;;N;;;1F6E;;1F6E
+1F67;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI;Ll;0;L;1F61 0342;;;;N;;;1F6F;;1F6F
+1F68;GREEK CAPITAL LETTER OMEGA WITH PSILI;Lu;0;L;03A9 0313;;;;N;;;;1F60;
+1F69;GREEK CAPITAL LETTER OMEGA WITH DASIA;Lu;0;L;03A9 0314;;;;N;;;;1F61;
+1F6A;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA;Lu;0;L;1F68 0300;;;;N;;;;1F62;
+1F6B;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA;Lu;0;L;1F69 0300;;;;N;;;;1F63;
+1F6C;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA;Lu;0;L;1F68 0301;;;;N;;;;1F64;
+1F6D;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA;Lu;0;L;1F69 0301;;;;N;;;;1F65;
+1F6E;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI;Lu;0;L;1F68 0342;;;;N;;;;1F66;
+1F6F;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI;Lu;0;L;1F69 0342;;;;N;;;;1F67;
+1F70;GREEK SMALL LETTER ALPHA WITH VARIA;Ll;0;L;03B1 0300;;;;N;;;1FBA;;1FBA
+1F71;GREEK SMALL LETTER ALPHA WITH OXIA;Ll;0;L;03AC;;;;N;;;1FBB;;1FBB
+1F72;GREEK SMALL LETTER EPSILON WITH VARIA;Ll;0;L;03B5 0300;;;;N;;;1FC8;;1FC8
+1F73;GREEK SMALL LETTER EPSILON WITH OXIA;Ll;0;L;03AD;;;;N;;;1FC9;;1FC9
+1F74;GREEK SMALL LETTER ETA WITH VARIA;Ll;0;L;03B7 0300;;;;N;;;1FCA;;1FCA
+1F75;GREEK SMALL LETTER ETA WITH OXIA;Ll;0;L;03AE;;;;N;;;1FCB;;1FCB
+1F76;GREEK SMALL LETTER IOTA WITH VARIA;Ll;0;L;03B9 0300;;;;N;;;1FDA;;1FDA
+1F77;GREEK SMALL LETTER IOTA WITH OXIA;Ll;0;L;03AF;;;;N;;;1FDB;;1FDB
+1F78;GREEK SMALL LETTER OMICRON WITH VARIA;Ll;0;L;03BF 0300;;;;N;;;1FF8;;1FF8
+1F79;GREEK SMALL LETTER OMICRON WITH OXIA;Ll;0;L;03CC;;;;N;;;1FF9;;1FF9
+1F7A;GREEK SMALL LETTER UPSILON WITH VARIA;Ll;0;L;03C5 0300;;;;N;;;1FEA;;1FEA
+1F7B;GREEK SMALL LETTER UPSILON WITH OXIA;Ll;0;L;03CD;;;;N;;;1FEB;;1FEB
+1F7C;GREEK SMALL LETTER OMEGA WITH VARIA;Ll;0;L;03C9 0300;;;;N;;;1FFA;;1FFA
+1F7D;GREEK SMALL LETTER OMEGA WITH OXIA;Ll;0;L;03CE;;;;N;;;1FFB;;1FFB
+1F80;GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F00 0345;;;;N;;;1F88;;1F88
+1F81;GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F01 0345;;;;N;;;1F89;;1F89
+1F82;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F02 0345;;;;N;;;1F8A;;1F8A
+1F83;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F03 0345;;;;N;;;1F8B;;1F8B
+1F84;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F04 0345;;;;N;;;1F8C;;1F8C
+1F85;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F05 0345;;;;N;;;1F8D;;1F8D
+1F86;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F06 0345;;;;N;;;1F8E;;1F8E
+1F87;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F07 0345;;;;N;;;1F8F;;1F8F
+1F88;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F08 0345;;;;N;;;;1F80;
+1F89;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F09 0345;;;;N;;;;1F81;
+1F8A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0A 0345;;;;N;;;;1F82;
+1F8B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0B 0345;;;;N;;;;1F83;
+1F8C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0C 0345;;;;N;;;;1F84;
+1F8D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0D 0345;;;;N;;;;1F85;
+1F8E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0E 0345;;;;N;;;;1F86;
+1F8F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0F 0345;;;;N;;;;1F87;
+1F90;GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F20 0345;;;;N;;;1F98;;1F98
+1F91;GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F21 0345;;;;N;;;1F99;;1F99
+1F92;GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F22 0345;;;;N;;;1F9A;;1F9A
+1F93;GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F23 0345;;;;N;;;1F9B;;1F9B
+1F94;GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F24 0345;;;;N;;;1F9C;;1F9C
+1F95;GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F25 0345;;;;N;;;1F9D;;1F9D
+1F96;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F26 0345;;;;N;;;1F9E;;1F9E
+1F97;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F27 0345;;;;N;;;1F9F;;1F9F
+1F98;GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F28 0345;;;;N;;;;1F90;
+1F99;GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F29 0345;;;;N;;;;1F91;
+1F9A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2A 0345;;;;N;;;;1F92;
+1F9B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2B 0345;;;;N;;;;1F93;
+1F9C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2C 0345;;;;N;;;;1F94;
+1F9D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2D 0345;;;;N;;;;1F95;
+1F9E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2E 0345;;;;N;;;;1F96;
+1F9F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2F 0345;;;;N;;;;1F97;
+1FA0;GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F60 0345;;;;N;;;1FA8;;1FA8
+1FA1;GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F61 0345;;;;N;;;1FA9;;1FA9
+1FA2;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F62 0345;;;;N;;;1FAA;;1FAA
+1FA3;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F63 0345;;;;N;;;1FAB;;1FAB
+1FA4;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F64 0345;;;;N;;;1FAC;;1FAC
+1FA5;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F65 0345;;;;N;;;1FAD;;1FAD
+1FA6;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F66 0345;;;;N;;;1FAE;;1FAE
+1FA7;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F67 0345;;;;N;;;1FAF;;1FAF
+1FA8;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F68 0345;;;;N;;;;1FA0;
+1FA9;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F69 0345;;;;N;;;;1FA1;
+1FAA;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6A 0345;;;;N;;;;1FA2;
+1FAB;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6B 0345;;;;N;;;;1FA3;
+1FAC;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6C 0345;;;;N;;;;1FA4;
+1FAD;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6D 0345;;;;N;;;;1FA5;
+1FAE;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6E 0345;;;;N;;;;1FA6;
+1FAF;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6F 0345;;;;N;;;;1FA7;
+1FB0;GREEK SMALL LETTER ALPHA WITH VRACHY;Ll;0;L;03B1 0306;;;;N;;;1FB8;;1FB8
+1FB1;GREEK SMALL LETTER ALPHA WITH MACRON;Ll;0;L;03B1 0304;;;;N;;;1FB9;;1FB9
+1FB2;GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F70 0345;;;;N;;;;;
+1FB3;GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI;Ll;0;L;03B1 0345;;;;N;;;1FBC;;1FBC
+1FB4;GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AC 0345;;;;N;;;;;
+1FB6;GREEK SMALL LETTER ALPHA WITH PERISPOMENI;Ll;0;L;03B1 0342;;;;N;;;;;
+1FB7;GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FB6 0345;;;;N;;;;;
+1FB8;GREEK CAPITAL LETTER ALPHA WITH VRACHY;Lu;0;L;0391 0306;;;;N;;;;1FB0;
+1FB9;GREEK CAPITAL LETTER ALPHA WITH MACRON;Lu;0;L;0391 0304;;;;N;;;;1FB1;
+1FBA;GREEK CAPITAL LETTER ALPHA WITH VARIA;Lu;0;L;0391 0300;;;;N;;;;1F70;
+1FBB;GREEK CAPITAL LETTER ALPHA WITH OXIA;Lu;0;L;0386;;;;N;;;;1F71;
+1FBC;GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI;Lt;0;L;0391 0345;;;;N;;;;1FB3;
+1FBD;GREEK KORONIS;Sk;0;ON;<compat> 0020 0313;;;;N;;;;;
+1FBE;GREEK PROSGEGRAMMENI;Ll;0;L;03B9;;;;N;;;0399;;0399
+1FBF;GREEK PSILI;Sk;0;ON;<compat> 0020 0313;;;;N;;;;;
+1FC0;GREEK PERISPOMENI;Sk;0;ON;<compat> 0020 0342;;;;N;;;;;
+1FC1;GREEK DIALYTIKA AND PERISPOMENI;Sk;0;ON;00A8 0342;;;;N;;;;;
+1FC2;GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F74 0345;;;;N;;;;;
+1FC3;GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI;Ll;0;L;03B7 0345;;;;N;;;1FCC;;1FCC
+1FC4;GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AE 0345;;;;N;;;;;
+1FC6;GREEK SMALL LETTER ETA WITH PERISPOMENI;Ll;0;L;03B7 0342;;;;N;;;;;
+1FC7;GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FC6 0345;;;;N;;;;;
+1FC8;GREEK CAPITAL LETTER EPSILON WITH VARIA;Lu;0;L;0395 0300;;;;N;;;;1F72;
+1FC9;GREEK CAPITAL LETTER EPSILON WITH OXIA;Lu;0;L;0388;;;;N;;;;1F73;
+1FCA;GREEK CAPITAL LETTER ETA WITH VARIA;Lu;0;L;0397 0300;;;;N;;;;1F74;
+1FCB;GREEK CAPITAL LETTER ETA WITH OXIA;Lu;0;L;0389;;;;N;;;;1F75;
+1FCC;GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI;Lt;0;L;0397 0345;;;;N;;;;1FC3;
+1FCD;GREEK PSILI AND VARIA;Sk;0;ON;1FBF 0300;;;;N;;;;;
+1FCE;GREEK PSILI AND OXIA;Sk;0;ON;1FBF 0301;;;;N;;;;;
+1FCF;GREEK PSILI AND PERISPOMENI;Sk;0;ON;1FBF 0342;;;;N;;;;;
+1FD0;GREEK SMALL LETTER IOTA WITH VRACHY;Ll;0;L;03B9 0306;;;;N;;;1FD8;;1FD8
+1FD1;GREEK SMALL LETTER IOTA WITH MACRON;Ll;0;L;03B9 0304;;;;N;;;1FD9;;1FD9
+1FD2;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA;Ll;0;L;03CA 0300;;;;N;;;;;
+1FD3;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA;Ll;0;L;0390;;;;N;;;;;
+1FD6;GREEK SMALL LETTER IOTA WITH PERISPOMENI;Ll;0;L;03B9 0342;;;;N;;;;;
+1FD7;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CA 0342;;;;N;;;;;
+1FD8;GREEK CAPITAL LETTER IOTA WITH VRACHY;Lu;0;L;0399 0306;;;;N;;;;1FD0;
+1FD9;GREEK CAPITAL LETTER IOTA WITH MACRON;Lu;0;L;0399 0304;;;;N;;;;1FD1;
+1FDA;GREEK CAPITAL LETTER IOTA WITH VARIA;Lu;0;L;0399 0300;;;;N;;;;1F76;
+1FDB;GREEK CAPITAL LETTER IOTA WITH OXIA;Lu;0;L;038A;;;;N;;;;1F77;
+1FDD;GREEK DASIA AND VARIA;Sk;0;ON;1FFE 0300;;;;N;;;;;
+1FDE;GREEK DASIA AND OXIA;Sk;0;ON;1FFE 0301;;;;N;;;;;
+1FDF;GREEK DASIA AND PERISPOMENI;Sk;0;ON;1FFE 0342;;;;N;;;;;
+1FE0;GREEK SMALL LETTER UPSILON WITH VRACHY;Ll;0;L;03C5 0306;;;;N;;;1FE8;;1FE8
+1FE1;GREEK SMALL LETTER UPSILON WITH MACRON;Ll;0;L;03C5 0304;;;;N;;;1FE9;;1FE9
+1FE2;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA;Ll;0;L;03CB 0300;;;;N;;;;;
+1FE3;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA;Ll;0;L;03B0;;;;N;;;;;
+1FE4;GREEK SMALL LETTER RHO WITH PSILI;Ll;0;L;03C1 0313;;;;N;;;;;
+1FE5;GREEK SMALL LETTER RHO WITH DASIA;Ll;0;L;03C1 0314;;;;N;;;1FEC;;1FEC
+1FE6;GREEK SMALL LETTER UPSILON WITH PERISPOMENI;Ll;0;L;03C5 0342;;;;N;;;;;
+1FE7;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CB 0342;;;;N;;;;;
+1FE8;GREEK CAPITAL LETTER UPSILON WITH VRACHY;Lu;0;L;03A5 0306;;;;N;;;;1FE0;
+1FE9;GREEK CAPITAL LETTER UPSILON WITH MACRON;Lu;0;L;03A5 0304;;;;N;;;;1FE1;
+1FEA;GREEK CAPITAL LETTER UPSILON WITH VARIA;Lu;0;L;03A5 0300;;;;N;;;;1F7A;
+1FEB;GREEK CAPITAL LETTER UPSILON WITH OXIA;Lu;0;L;038E;;;;N;;;;1F7B;
+1FEC;GREEK CAPITAL LETTER RHO WITH DASIA;Lu;0;L;03A1 0314;;;;N;;;;1FE5;
+1FED;GREEK DIALYTIKA AND VARIA;Sk;0;ON;00A8 0300;;;;N;;;;;
+1FEE;GREEK DIALYTIKA AND OXIA;Sk;0;ON;0385;;;;N;;;;;
+1FEF;GREEK VARIA;Sk;0;ON;0060;;;;N;;;;;
+1FF2;GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F7C 0345;;;;N;;;;;
+1FF3;GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI;Ll;0;L;03C9 0345;;;;N;;;1FFC;;1FFC
+1FF4;GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03CE 0345;;;;N;;;;;
+1FF6;GREEK SMALL LETTER OMEGA WITH PERISPOMENI;Ll;0;L;03C9 0342;;;;N;;;;;
+1FF7;GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FF6 0345;;;;N;;;;;
+1FF8;GREEK CAPITAL LETTER OMICRON WITH VARIA;Lu;0;L;039F 0300;;;;N;;;;1F78;
+1FF9;GREEK CAPITAL LETTER OMICRON WITH OXIA;Lu;0;L;038C;;;;N;;;;1F79;
+1FFA;GREEK CAPITAL LETTER OMEGA WITH VARIA;Lu;0;L;03A9 0300;;;;N;;;;1F7C;
+1FFB;GREEK CAPITAL LETTER OMEGA WITH OXIA;Lu;0;L;038F;;;;N;;;;1F7D;
+1FFC;GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI;Lt;0;L;03A9 0345;;;;N;;;;1FF3;
+1FFD;GREEK OXIA;Sk;0;ON;00B4;;;;N;;;;;
+1FFE;GREEK DASIA;Sk;0;ON;<compat> 0020 0314;;;;N;;;;;
+2000;EN QUAD;Zs;0;WS;2002;;;;N;;;;;
+2001;EM QUAD;Zs;0;WS;2003;;;;N;;;;;
+2002;EN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2003;EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2004;THREE-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2005;FOUR-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2006;SIX-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2007;FIGURE SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;;
+2008;PUNCTUATION SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2009;THIN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+200A;HAIR SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+200B;ZERO WIDTH SPACE;Zs;0;BN;;;;;N;;;;;
+200C;ZERO WIDTH NON-JOINER;Cf;0;BN;;;;;N;;;;;
+200D;ZERO WIDTH JOINER;Cf;0;BN;;;;;N;;;;;
+200E;LEFT-TO-RIGHT MARK;Cf;0;L;;;;;N;;;;;
+200F;RIGHT-TO-LEFT MARK;Cf;0;R;;;;;N;;;;;
+2010;HYPHEN;Pd;0;ON;;;;;N;;;;;
+2011;NON-BREAKING HYPHEN;Pd;0;ON;<noBreak> 2010;;;;N;;;;;
+2012;FIGURE DASH;Pd;0;ON;;;;;N;;;;;
+2013;EN DASH;Pd;0;ON;;;;;N;;;;;
+2014;EM DASH;Pd;0;ON;;;;;N;;;;;
+2015;HORIZONTAL BAR;Pd;0;ON;;;;;N;QUOTATION DASH;;;;
+2016;DOUBLE VERTICAL LINE;Po;0;ON;;;;;N;DOUBLE VERTICAL BAR;;;;
+2017;DOUBLE LOW LINE;Po;0;ON;<compat> 0020 0333;;;;N;SPACING DOUBLE UNDERSCORE;;;;
+2018;LEFT SINGLE QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE TURNED COMMA QUOTATION MARK;;;;
+2019;RIGHT SINGLE QUOTATION MARK;Pf;0;ON;;;;;N;SINGLE COMMA QUOTATION MARK;;;;
+201A;SINGLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW SINGLE COMMA QUOTATION MARK;;;;
+201B;SINGLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE REVERSED COMMA QUOTATION MARK;;;;
+201C;LEFT DOUBLE QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE TURNED COMMA QUOTATION MARK;;;;
+201D;RIGHT DOUBLE QUOTATION MARK;Pf;0;ON;;;;;N;DOUBLE COMMA QUOTATION MARK;;;;
+201E;DOUBLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW DOUBLE COMMA QUOTATION MARK;;;;
+201F;DOUBLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE REVERSED COMMA QUOTATION MARK;;;;
+2020;DAGGER;Po;0;ON;;;;;N;;;;;
+2021;DOUBLE DAGGER;Po;0;ON;;;;;N;;;;;
+2022;BULLET;Po;0;ON;;;;;N;;;;;
+2023;TRIANGULAR BULLET;Po;0;ON;;;;;N;;;;;
+2024;ONE DOT LEADER;Po;0;ON;<compat> 002E;;;;N;;;;;
+2025;TWO DOT LEADER;Po;0;ON;<compat> 002E 002E;;;;N;;;;;
+2026;HORIZONTAL ELLIPSIS;Po;0;ON;<compat> 002E 002E 002E;;;;N;;;;;
+2027;HYPHENATION POINT;Po;0;ON;;;;;N;;;;;
+2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;;
+2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;;
+202A;LEFT-TO-RIGHT EMBEDDING;Cf;0;LRE;;;;;N;;;;;
+202B;RIGHT-TO-LEFT EMBEDDING;Cf;0;RLE;;;;;N;;;;;
+202C;POP DIRECTIONAL FORMATTING;Cf;0;PDF;;;;;N;;;;;
+202D;LEFT-TO-RIGHT OVERRIDE;Cf;0;LRO;;;;;N;;;;;
+202E;RIGHT-TO-LEFT OVERRIDE;Cf;0;RLO;;;;;N;;;;;
+202F;NARROW NO-BREAK SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;;
+2030;PER MILLE SIGN;Po;0;ET;;;;;N;;;;;
+2031;PER TEN THOUSAND SIGN;Po;0;ET;;;;;N;;;;;
+2032;PRIME;Po;0;ET;;;;;N;;;;;
+2033;DOUBLE PRIME;Po;0;ET;<compat> 2032 2032;;;;N;;;;;
+2034;TRIPLE PRIME;Po;0;ET;<compat> 2032 2032 2032;;;;N;;;;;
+2035;REVERSED PRIME;Po;0;ON;;;;;N;;;;;
+2036;REVERSED DOUBLE PRIME;Po;0;ON;<compat> 2035 2035;;;;N;;;;;
+2037;REVERSED TRIPLE PRIME;Po;0;ON;<compat> 2035 2035 2035;;;;N;;;;;
+2038;CARET;Po;0;ON;;;;;N;;;;;
+2039;SINGLE LEFT-POINTING ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING SINGLE GUILLEMET;;;;
+203A;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING SINGLE GUILLEMET;;;;
+203B;REFERENCE MARK;Po;0;ON;;;;;N;;;;;
+203C;DOUBLE EXCLAMATION MARK;Po;0;ON;<compat> 0021 0021;;;;N;;;;;
+203D;INTERROBANG;Po;0;ON;;;;;N;;;;;
+203E;OVERLINE;Po;0;ON;<compat> 0020 0305;;;;N;SPACING OVERSCORE;;;;
+203F;UNDERTIE;Pc;0;ON;;;;;N;;Enotikon;;;
+2040;CHARACTER TIE;Pc;0;ON;;;;;N;;;;;
+2041;CARET INSERTION POINT;Po;0;ON;;;;;N;;;;;
+2042;ASTERISM;Po;0;ON;;;;;N;;;;;
+2043;HYPHEN BULLET;Po;0;ON;;;;;N;;;;;
+2044;FRACTION SLASH;Sm;0;ON;;;;;N;;;;;
+2045;LEFT SQUARE BRACKET WITH QUILL;Ps;0;ON;;;;;Y;;;;;
+2046;RIGHT SQUARE BRACKET WITH QUILL;Pe;0;ON;;;;;Y;;;;;
+2048;QUESTION EXCLAMATION MARK;Po;0;ON;<compat> 003F 0021;;;;N;;;;;
+2049;EXCLAMATION QUESTION MARK;Po;0;ON;<compat> 0021 003F;;;;N;;;;;
+204A;TIRONIAN SIGN ET;Po;0;ON;;;;;N;;;;;
+204B;REVERSED PILCROW SIGN;Po;0;ON;;;;;N;;;;;
+204C;BLACK LEFTWARDS BULLET;Po;0;ON;;;;;N;;;;;
+204D;BLACK RIGHTWARDS BULLET;Po;0;ON;;;;;N;;;;;
+206A;INHIBIT SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;;
+206B;ACTIVATE SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;;
+206C;INHIBIT ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;;
+206D;ACTIVATE ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;;
+206E;NATIONAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;;
+206F;NOMINAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;;
+2070;SUPERSCRIPT ZERO;No;0;EN;<super> 0030;0;0;0;N;SUPERSCRIPT DIGIT ZERO;;;;
+2074;SUPERSCRIPT FOUR;No;0;EN;<super> 0034;4;4;4;N;SUPERSCRIPT DIGIT FOUR;;;;
+2075;SUPERSCRIPT FIVE;No;0;EN;<super> 0035;5;5;5;N;SUPERSCRIPT DIGIT FIVE;;;;
+2076;SUPERSCRIPT SIX;No;0;EN;<super> 0036;6;6;6;N;SUPERSCRIPT DIGIT SIX;;;;
+2077;SUPERSCRIPT SEVEN;No;0;EN;<super> 0037;7;7;7;N;SUPERSCRIPT DIGIT SEVEN;;;;
+2078;SUPERSCRIPT EIGHT;No;0;EN;<super> 0038;8;8;8;N;SUPERSCRIPT DIGIT EIGHT;;;;
+2079;SUPERSCRIPT NINE;No;0;EN;<super> 0039;9;9;9;N;SUPERSCRIPT DIGIT NINE;;;;
+207A;SUPERSCRIPT PLUS SIGN;Sm;0;ET;<super> 002B;;;;N;;;;;
+207B;SUPERSCRIPT MINUS;Sm;0;ET;<super> 2212;;;;N;SUPERSCRIPT HYPHEN-MINUS;;;;
+207C;SUPERSCRIPT EQUALS SIGN;Sm;0;ON;<super> 003D;;;;N;;;;;
+207D;SUPERSCRIPT LEFT PARENTHESIS;Ps;0;ON;<super> 0028;;;;Y;SUPERSCRIPT OPENING PARENTHESIS;;;;
+207E;SUPERSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<super> 0029;;;;Y;SUPERSCRIPT CLOSING PARENTHESIS;;;;
+207F;SUPERSCRIPT LATIN SMALL LETTER N;Ll;0;L;<super> 006E;;;;N;;;;;
+2080;SUBSCRIPT ZERO;No;0;EN;<sub> 0030;0;0;0;N;SUBSCRIPT DIGIT ZERO;;;;
+2081;SUBSCRIPT ONE;No;0;EN;<sub> 0031;1;1;1;N;SUBSCRIPT DIGIT ONE;;;;
+2082;SUBSCRIPT TWO;No;0;EN;<sub> 0032;2;2;2;N;SUBSCRIPT DIGIT TWO;;;;
+2083;SUBSCRIPT THREE;No;0;EN;<sub> 0033;3;3;3;N;SUBSCRIPT DIGIT THREE;;;;
+2084;SUBSCRIPT FOUR;No;0;EN;<sub> 0034;4;4;4;N;SUBSCRIPT DIGIT FOUR;;;;
+2085;SUBSCRIPT FIVE;No;0;EN;<sub> 0035;5;5;5;N;SUBSCRIPT DIGIT FIVE;;;;
+2086;SUBSCRIPT SIX;No;0;EN;<sub> 0036;6;6;6;N;SUBSCRIPT DIGIT SIX;;;;
+2087;SUBSCRIPT SEVEN;No;0;EN;<sub> 0037;7;7;7;N;SUBSCRIPT DIGIT SEVEN;;;;
+2088;SUBSCRIPT EIGHT;No;0;EN;<sub> 0038;8;8;8;N;SUBSCRIPT DIGIT EIGHT;;;;
+2089;SUBSCRIPT NINE;No;0;EN;<sub> 0039;9;9;9;N;SUBSCRIPT DIGIT NINE;;;;
+208A;SUBSCRIPT PLUS SIGN;Sm;0;ET;<sub> 002B;;;;N;;;;;
+208B;SUBSCRIPT MINUS;Sm;0;ET;<sub> 2212;;;;N;SUBSCRIPT HYPHEN-MINUS;;;;
+208C;SUBSCRIPT EQUALS SIGN;Sm;0;ON;<sub> 003D;;;;N;;;;;
+208D;SUBSCRIPT LEFT PARENTHESIS;Ps;0;ON;<sub> 0028;;;;Y;SUBSCRIPT OPENING PARENTHESIS;;;;
+208E;SUBSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<sub> 0029;;;;Y;SUBSCRIPT CLOSING PARENTHESIS;;;;
+20A0;EURO-CURRENCY SIGN;Sc;0;ET;;;;;N;;;;;
+20A1;COLON SIGN;Sc;0;ET;;;;;N;;;;;
+20A2;CRUZEIRO SIGN;Sc;0;ET;;;;;N;;;;;
+20A3;FRENCH FRANC SIGN;Sc;0;ET;;;;;N;;;;;
+20A4;LIRA SIGN;Sc;0;ET;;;;;N;;;;;
+20A5;MILL SIGN;Sc;0;ET;;;;;N;;;;;
+20A6;NAIRA SIGN;Sc;0;ET;;;;;N;;;;;
+20A7;PESETA SIGN;Sc;0;ET;;;;;N;;;;;
+20A8;RUPEE SIGN;Sc;0;ET;<compat> 0052 0073;;;;N;;;;;
+20A9;WON SIGN;Sc;0;ET;;;;;N;;;;;
+20AA;NEW SHEQEL SIGN;Sc;0;ET;;;;;N;;;;;
+20AB;DONG SIGN;Sc;0;ET;;;;;N;;;;;
+20AC;EURO SIGN;Sc;0;ET;;;;;N;;;;;
+20AD;KIP SIGN;Sc;0;ET;;;;;N;;;;;
+20AE;TUGRIK SIGN;Sc;0;ET;;;;;N;;;;;
+20AF;DRACHMA SIGN;Sc;0;ET;;;;;N;;;;;
+20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;;
+20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;;
+20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;;
+20D3;COMBINING SHORT VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT VERTICAL BAR OVERLAY;;;;
+20D4;COMBINING ANTICLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING ANTICLOCKWISE ARROW ABOVE;;;;
+20D5;COMBINING CLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING CLOCKWISE ARROW ABOVE;;;;
+20D6;COMBINING LEFT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT ARROW ABOVE;;;;
+20D7;COMBINING RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT ARROW ABOVE;;;;
+20D8;COMBINING RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING RING OVERLAY;;;;
+20D9;COMBINING CLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING CLOCKWISE RING OVERLAY;;;;
+20DA;COMBINING ANTICLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING ANTICLOCKWISE RING OVERLAY;;;;
+20DB;COMBINING THREE DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING THREE DOTS ABOVE;;;;
+20DC;COMBINING FOUR DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING FOUR DOTS ABOVE;;;;
+20DD;COMBINING ENCLOSING CIRCLE;Me;0;NSM;;;;;N;ENCLOSING CIRCLE;;;;
+20DE;COMBINING ENCLOSING SQUARE;Me;0;NSM;;;;;N;ENCLOSING SQUARE;;;;
+20DF;COMBINING ENCLOSING DIAMOND;Me;0;NSM;;;;;N;ENCLOSING DIAMOND;;;;
+20E0;COMBINING ENCLOSING CIRCLE BACKSLASH;Me;0;NSM;;;;;N;ENCLOSING CIRCLE SLASH;;;;
+20E1;COMBINING LEFT RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT RIGHT ARROW ABOVE;;;;
+20E2;COMBINING ENCLOSING SCREEN;Me;0;NSM;;;;;N;;;;;
+20E3;COMBINING ENCLOSING KEYCAP;Me;0;NSM;;;;;N;;;;;
+2100;ACCOUNT OF;So;0;ON;<compat> 0061 002F 0063;;;;N;;;;;
+2101;ADDRESSED TO THE SUBJECT;So;0;ON;<compat> 0061 002F 0073;;;;N;;;;;
+2102;DOUBLE-STRUCK CAPITAL C;Lu;0;L;<font> 0043;;;;N;DOUBLE-STRUCK C;;;;
+2103;DEGREE CELSIUS;So;0;ON;<compat> 00B0 0043;;;;N;DEGREES CENTIGRADE;;;;
+2104;CENTRE LINE SYMBOL;So;0;ON;;;;;N;C L SYMBOL;;;;
+2105;CARE OF;So;0;ON;<compat> 0063 002F 006F;;;;N;;;;;
+2106;CADA UNA;So;0;ON;<compat> 0063 002F 0075;;;;N;;;;;
+2107;EULER CONSTANT;Lu;0;L;<compat> 0190;;;;N;EULERS;;;;
+2108;SCRUPLE;So;0;ON;;;;;N;;;;;
+2109;DEGREE FAHRENHEIT;So;0;ON;<compat> 00B0 0046;;;;N;DEGREES FAHRENHEIT;;;;
+210A;SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+210B;SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;SCRIPT H;;;;
+210C;BLACK-LETTER CAPITAL H;Lu;0;L;<font> 0048;;;;N;BLACK-LETTER H;;;;
+210D;DOUBLE-STRUCK CAPITAL H;Lu;0;L;<font> 0048;;;;N;DOUBLE-STRUCK H;;;;
+210E;PLANCK CONSTANT;Ll;0;L;<font> 0068;;;;N;;;;;
+210F;PLANCK CONSTANT OVER TWO PI;Ll;0;L;<font> 0127;;;;N;PLANCK CONSTANT OVER 2 PI;;;;
+2110;SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;SCRIPT I;;;;
+2111;BLACK-LETTER CAPITAL I;Lu;0;L;<font> 0049;;;;N;BLACK-LETTER I;;;;
+2112;SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;SCRIPT L;;;;
+2113;SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+2114;L B BAR SYMBOL;So;0;ON;;;;;N;;;;;
+2115;DOUBLE-STRUCK CAPITAL N;Lu;0;L;<font> 004E;;;;N;DOUBLE-STRUCK N;;;;
+2116;NUMERO SIGN;So;0;ON;<compat> 004E 006F;;;;N;NUMERO;;;;
+2117;SOUND RECORDING COPYRIGHT;So;0;ON;;;;;N;;;;;
+2118;SCRIPT CAPITAL P;So;0;ON;;;;;N;SCRIPT P;;;;
+2119;DOUBLE-STRUCK CAPITAL P;Lu;0;L;<font> 0050;;;;N;DOUBLE-STRUCK P;;;;
+211A;DOUBLE-STRUCK CAPITAL Q;Lu;0;L;<font> 0051;;;;N;DOUBLE-STRUCK Q;;;;
+211B;SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;SCRIPT R;;;;
+211C;BLACK-LETTER CAPITAL R;Lu;0;L;<font> 0052;;;;N;BLACK-LETTER R;;;;
+211D;DOUBLE-STRUCK CAPITAL R;Lu;0;L;<font> 0052;;;;N;DOUBLE-STRUCK R;;;;
+211E;PRESCRIPTION TAKE;So;0;ON;;;;;N;;;;;
+211F;RESPONSE;So;0;ON;;;;;N;;;;;
+2120;SERVICE MARK;So;0;ON;<super> 0053 004D;;;;N;;;;;
+2121;TELEPHONE SIGN;So;0;ON;<compat> 0054 0045 004C;;;;N;T E L SYMBOL;;;;
+2122;TRADE MARK SIGN;So;0;ON;<super> 0054 004D;;;;N;TRADEMARK;;;;
+2123;VERSICLE;So;0;ON;;;;;N;;;;;
+2124;DOUBLE-STRUCK CAPITAL Z;Lu;0;L;<font> 005A;;;;N;DOUBLE-STRUCK Z;;;;
+2125;OUNCE SIGN;So;0;ON;;;;;N;OUNCE;;;;
+2126;OHM SIGN;Lu;0;L;03A9;;;;N;OHM;;;03C9;
+2127;INVERTED OHM SIGN;So;0;ON;;;;;N;MHO;;;;
+2128;BLACK-LETTER CAPITAL Z;Lu;0;L;<font> 005A;;;;N;BLACK-LETTER Z;;;;
+2129;TURNED GREEK SMALL LETTER IOTA;So;0;ON;;;;;N;;;;;
+212A;KELVIN SIGN;Lu;0;L;004B;;;;N;DEGREES KELVIN;;;006B;
+212B;ANGSTROM SIGN;Lu;0;L;00C5;;;;N;ANGSTROM UNIT;;;00E5;
+212C;SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;SCRIPT B;;;;
+212D;BLACK-LETTER CAPITAL C;Lu;0;L;<font> 0043;;;;N;BLACK-LETTER C;;;;
+212E;ESTIMATED SYMBOL;So;0;ET;;;;;N;;;;;
+212F;SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+2130;SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;SCRIPT E;;;;
+2131;SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;SCRIPT F;;;;
+2132;TURNED CAPITAL F;So;0;ON;;;;;N;TURNED F;;;;
+2133;SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;SCRIPT M;;;;
+2134;SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+2135;ALEF SYMBOL;Lo;0;L;<compat> 05D0;;;;N;FIRST TRANSFINITE CARDINAL;;;;
+2136;BET SYMBOL;Lo;0;L;<compat> 05D1;;;;N;SECOND TRANSFINITE CARDINAL;;;;
+2137;GIMEL SYMBOL;Lo;0;L;<compat> 05D2;;;;N;THIRD TRANSFINITE CARDINAL;;;;
+2138;DALET SYMBOL;Lo;0;L;<compat> 05D3;;;;N;FOURTH TRANSFINITE CARDINAL;;;;
+2139;INFORMATION SOURCE;Ll;0;L;<font> 0069;;;;N;;;;;
+213A;ROTATED CAPITAL Q;So;0;ON;;;;;N;;;;;
+2153;VULGAR FRACTION ONE THIRD;No;0;ON;<fraction> 0031 2044 0033;;;1/3;N;FRACTION ONE THIRD;;;;
+2154;VULGAR FRACTION TWO THIRDS;No;0;ON;<fraction> 0032 2044 0033;;;2/3;N;FRACTION TWO THIRDS;;;;
+2155;VULGAR FRACTION ONE FIFTH;No;0;ON;<fraction> 0031 2044 0035;;;1/5;N;FRACTION ONE FIFTH;;;;
+2156;VULGAR FRACTION TWO FIFTHS;No;0;ON;<fraction> 0032 2044 0035;;;2/5;N;FRACTION TWO FIFTHS;;;;
+2157;VULGAR FRACTION THREE FIFTHS;No;0;ON;<fraction> 0033 2044 0035;;;3/5;N;FRACTION THREE FIFTHS;;;;
+2158;VULGAR FRACTION FOUR FIFTHS;No;0;ON;<fraction> 0034 2044 0035;;;4/5;N;FRACTION FOUR FIFTHS;;;;
+2159;VULGAR FRACTION ONE SIXTH;No;0;ON;<fraction> 0031 2044 0036;;;1/6;N;FRACTION ONE SIXTH;;;;
+215A;VULGAR FRACTION FIVE SIXTHS;No;0;ON;<fraction> 0035 2044 0036;;;5/6;N;FRACTION FIVE SIXTHS;;;;
+215B;VULGAR FRACTION ONE EIGHTH;No;0;ON;<fraction> 0031 2044 0038;;;1/8;N;FRACTION ONE EIGHTH;;;;
+215C;VULGAR FRACTION THREE EIGHTHS;No;0;ON;<fraction> 0033 2044 0038;;;3/8;N;FRACTION THREE EIGHTHS;;;;
+215D;VULGAR FRACTION FIVE EIGHTHS;No;0;ON;<fraction> 0035 2044 0038;;;5/8;N;FRACTION FIVE EIGHTHS;;;;
+215E;VULGAR FRACTION SEVEN EIGHTHS;No;0;ON;<fraction> 0037 2044 0038;;;7/8;N;FRACTION SEVEN EIGHTHS;;;;
+215F;FRACTION NUMERATOR ONE;No;0;ON;<fraction> 0031 2044;;;1;N;;;;;
+2160;ROMAN NUMERAL ONE;Nl;0;L;<compat> 0049;;;1;N;;;;2170;
+2161;ROMAN NUMERAL TWO;Nl;0;L;<compat> 0049 0049;;;2;N;;;;2171;
+2162;ROMAN NUMERAL THREE;Nl;0;L;<compat> 0049 0049 0049;;;3;N;;;;2172;
+2163;ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0049 0056;;;4;N;;;;2173;
+2164;ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0056;;;5;N;;;;2174;
+2165;ROMAN NUMERAL SIX;Nl;0;L;<compat> 0056 0049;;;6;N;;;;2175;
+2166;ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0056 0049 0049;;;7;N;;;;2176;
+2167;ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0056 0049 0049 0049;;;8;N;;;;2177;
+2168;ROMAN NUMERAL NINE;Nl;0;L;<compat> 0049 0058;;;9;N;;;;2178;
+2169;ROMAN NUMERAL TEN;Nl;0;L;<compat> 0058;;;10;N;;;;2179;
+216A;ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0058 0049;;;11;N;;;;217A;
+216B;ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0058 0049 0049;;;12;N;;;;217B;
+216C;ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 004C;;;50;N;;;;217C;
+216D;ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0043;;;100;N;;;;217D;
+216E;ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0044;;;500;N;;;;217E;
+216F;ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 004D;;;1000;N;;;;217F;
+2170;SMALL ROMAN NUMERAL ONE;Nl;0;L;<compat> 0069;;;1;N;;;2160;;2160
+2171;SMALL ROMAN NUMERAL TWO;Nl;0;L;<compat> 0069 0069;;;2;N;;;2161;;2161
+2172;SMALL ROMAN NUMERAL THREE;Nl;0;L;<compat> 0069 0069 0069;;;3;N;;;2162;;2162
+2173;SMALL ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0069 0076;;;4;N;;;2163;;2163
+2174;SMALL ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0076;;;5;N;;;2164;;2164
+2175;SMALL ROMAN NUMERAL SIX;Nl;0;L;<compat> 0076 0069;;;6;N;;;2165;;2165
+2176;SMALL ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0076 0069 0069;;;7;N;;;2166;;2166
+2177;SMALL ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0076 0069 0069 0069;;;8;N;;;2167;;2167
+2178;SMALL ROMAN NUMERAL NINE;Nl;0;L;<compat> 0069 0078;;;9;N;;;2168;;2168
+2179;SMALL ROMAN NUMERAL TEN;Nl;0;L;<compat> 0078;;;10;N;;;2169;;2169
+217A;SMALL ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0078 0069;;;11;N;;;216A;;216A
+217B;SMALL ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0078 0069 0069;;;12;N;;;216B;;216B
+217C;SMALL ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 006C;;;50;N;;;216C;;216C
+217D;SMALL ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0063;;;100;N;;;216D;;216D
+217E;SMALL ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0064;;;500;N;;;216E;;216E
+217F;SMALL ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 006D;;;1000;N;;;216F;;216F
+2180;ROMAN NUMERAL ONE THOUSAND C D;Nl;0;L;;;;1000;N;;;;;
+2181;ROMAN NUMERAL FIVE THOUSAND;Nl;0;L;;;;5000;N;;;;;
+2182;ROMAN NUMERAL TEN THOUSAND;Nl;0;L;;;;10000;N;;;;;
+2183;ROMAN NUMERAL REVERSED ONE HUNDRED;Nl;0;L;;;;;N;;;;;
+2190;LEFTWARDS ARROW;Sm;0;ON;;;;;N;LEFT ARROW;;;;
+2191;UPWARDS ARROW;Sm;0;ON;;;;;N;UP ARROW;;;;
+2192;RIGHTWARDS ARROW;Sm;0;ON;;;;;N;RIGHT ARROW;;;;
+2193;DOWNWARDS ARROW;Sm;0;ON;;;;;N;DOWN ARROW;;;;
+2194;LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;;
+2195;UP DOWN ARROW;So;0;ON;;;;;N;;;;;
+2196;NORTH WEST ARROW;So;0;ON;;;;;N;UPPER LEFT ARROW;;;;
+2197;NORTH EAST ARROW;So;0;ON;;;;;N;UPPER RIGHT ARROW;;;;
+2198;SOUTH EAST ARROW;So;0;ON;;;;;N;LOWER RIGHT ARROW;;;;
+2199;SOUTH WEST ARROW;So;0;ON;;;;;N;LOWER LEFT ARROW;;;;
+219A;LEFTWARDS ARROW WITH STROKE;Sm;0;ON;2190 0338;;;;N;LEFT ARROW WITH STROKE;;;;
+219B;RIGHTWARDS ARROW WITH STROKE;Sm;0;ON;2192 0338;;;;N;RIGHT ARROW WITH STROKE;;;;
+219C;LEFTWARDS WAVE ARROW;So;0;ON;;;;;N;LEFT WAVE ARROW;;;;
+219D;RIGHTWARDS WAVE ARROW;So;0;ON;;;;;N;RIGHT WAVE ARROW;;;;
+219E;LEFTWARDS TWO HEADED ARROW;So;0;ON;;;;;N;LEFT TWO HEADED ARROW;;;;
+219F;UPWARDS TWO HEADED ARROW;So;0;ON;;;;;N;UP TWO HEADED ARROW;;;;
+21A0;RIGHTWARDS TWO HEADED ARROW;Sm;0;ON;;;;;N;RIGHT TWO HEADED ARROW;;;;
+21A1;DOWNWARDS TWO HEADED ARROW;So;0;ON;;;;;N;DOWN TWO HEADED ARROW;;;;
+21A2;LEFTWARDS ARROW WITH TAIL;So;0;ON;;;;;N;LEFT ARROW WITH TAIL;;;;
+21A3;RIGHTWARDS ARROW WITH TAIL;Sm;0;ON;;;;;N;RIGHT ARROW WITH TAIL;;;;
+21A4;LEFTWARDS ARROW FROM BAR;So;0;ON;;;;;N;LEFT ARROW FROM BAR;;;;
+21A5;UPWARDS ARROW FROM BAR;So;0;ON;;;;;N;UP ARROW FROM BAR;;;;
+21A6;RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;RIGHT ARROW FROM BAR;;;;
+21A7;DOWNWARDS ARROW FROM BAR;So;0;ON;;;;;N;DOWN ARROW FROM BAR;;;;
+21A8;UP DOWN ARROW WITH BASE;So;0;ON;;;;;N;;;;;
+21A9;LEFTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;LEFT ARROW WITH HOOK;;;;
+21AA;RIGHTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;RIGHT ARROW WITH HOOK;;;;
+21AB;LEFTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;LEFT ARROW WITH LOOP;;;;
+21AC;RIGHTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;RIGHT ARROW WITH LOOP;;;;
+21AD;LEFT RIGHT WAVE ARROW;So;0;ON;;;;;N;;;;;
+21AE;LEFT RIGHT ARROW WITH STROKE;Sm;0;ON;2194 0338;;;;N;;;;;
+21AF;DOWNWARDS ZIGZAG ARROW;So;0;ON;;;;;N;DOWN ZIGZAG ARROW;;;;
+21B0;UPWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP LEFT;;;;
+21B1;UPWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP RIGHT;;;;
+21B2;DOWNWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP LEFT;;;;
+21B3;DOWNWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP RIGHT;;;;
+21B4;RIGHTWARDS ARROW WITH CORNER DOWNWARDS;So;0;ON;;;;;N;RIGHT ARROW WITH CORNER DOWN;;;;
+21B5;DOWNWARDS ARROW WITH CORNER LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH CORNER LEFT;;;;
+21B6;ANTICLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21B7;CLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21B8;NORTH WEST ARROW TO LONG BAR;So;0;ON;;;;;N;UPPER LEFT ARROW TO LONG BAR;;;;
+21B9;LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR OVER RIGHT ARROW TO BAR;;;;
+21BA;ANTICLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21BB;CLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21BC;LEFTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB UP;;;;
+21BD;LEFTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB DOWN;;;;
+21BE;UPWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB RIGHT;;;;
+21BF;UPWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB LEFT;;;;
+21C0;RIGHTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB UP;;;;
+21C1;RIGHTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB DOWN;;;;
+21C2;DOWNWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB RIGHT;;;;
+21C3;DOWNWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB LEFT;;;;
+21C4;RIGHTWARDS ARROW OVER LEFTWARDS ARROW;So;0;ON;;;;;N;RIGHT ARROW OVER LEFT ARROW;;;;
+21C5;UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW;So;0;ON;;;;;N;UP ARROW LEFT OF DOWN ARROW;;;;
+21C6;LEFTWARDS ARROW OVER RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT ARROW OVER RIGHT ARROW;;;;
+21C7;LEFTWARDS PAIRED ARROWS;So;0;ON;;;;;N;LEFT PAIRED ARROWS;;;;
+21C8;UPWARDS PAIRED ARROWS;So;0;ON;;;;;N;UP PAIRED ARROWS;;;;
+21C9;RIGHTWARDS PAIRED ARROWS;So;0;ON;;;;;N;RIGHT PAIRED ARROWS;;;;
+21CA;DOWNWARDS PAIRED ARROWS;So;0;ON;;;;;N;DOWN PAIRED ARROWS;;;;
+21CB;LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON;So;0;ON;;;;;N;LEFT HARPOON OVER RIGHT HARPOON;;;;
+21CC;RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON;So;0;ON;;;;;N;RIGHT HARPOON OVER LEFT HARPOON;;;;
+21CD;LEFTWARDS DOUBLE ARROW WITH STROKE;So;0;ON;21D0 0338;;;;N;LEFT DOUBLE ARROW WITH STROKE;;;;
+21CE;LEFT RIGHT DOUBLE ARROW WITH STROKE;Sm;0;ON;21D4 0338;;;;N;;;;;
+21CF;RIGHTWARDS DOUBLE ARROW WITH STROKE;Sm;0;ON;21D2 0338;;;;N;RIGHT DOUBLE ARROW WITH STROKE;;;;
+21D0;LEFTWARDS DOUBLE ARROW;So;0;ON;;;;;N;LEFT DOUBLE ARROW;;;;
+21D1;UPWARDS DOUBLE ARROW;So;0;ON;;;;;N;UP DOUBLE ARROW;;;;
+21D2;RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;RIGHT DOUBLE ARROW;;;;
+21D3;DOWNWARDS DOUBLE ARROW;So;0;ON;;;;;N;DOWN DOUBLE ARROW;;;;
+21D4;LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+21D5;UP DOWN DOUBLE ARROW;So;0;ON;;;;;N;;;;;
+21D6;NORTH WEST DOUBLE ARROW;So;0;ON;;;;;N;UPPER LEFT DOUBLE ARROW;;;;
+21D7;NORTH EAST DOUBLE ARROW;So;0;ON;;;;;N;UPPER RIGHT DOUBLE ARROW;;;;
+21D8;SOUTH EAST DOUBLE ARROW;So;0;ON;;;;;N;LOWER RIGHT DOUBLE ARROW;;;;
+21D9;SOUTH WEST DOUBLE ARROW;So;0;ON;;;;;N;LOWER LEFT DOUBLE ARROW;;;;
+21DA;LEFTWARDS TRIPLE ARROW;So;0;ON;;;;;N;LEFT TRIPLE ARROW;;;;
+21DB;RIGHTWARDS TRIPLE ARROW;So;0;ON;;;;;N;RIGHT TRIPLE ARROW;;;;
+21DC;LEFTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;LEFT SQUIGGLE ARROW;;;;
+21DD;RIGHTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;RIGHT SQUIGGLE ARROW;;;;
+21DE;UPWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;UP ARROW WITH DOUBLE STROKE;;;;
+21DF;DOWNWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;DOWN ARROW WITH DOUBLE STROKE;;;;
+21E0;LEFTWARDS DASHED ARROW;So;0;ON;;;;;N;LEFT DASHED ARROW;;;;
+21E1;UPWARDS DASHED ARROW;So;0;ON;;;;;N;UP DASHED ARROW;;;;
+21E2;RIGHTWARDS DASHED ARROW;So;0;ON;;;;;N;RIGHT DASHED ARROW;;;;
+21E3;DOWNWARDS DASHED ARROW;So;0;ON;;;;;N;DOWN DASHED ARROW;;;;
+21E4;LEFTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR;;;;
+21E5;RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;RIGHT ARROW TO BAR;;;;
+21E6;LEFTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE LEFT ARROW;;;;
+21E7;UPWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE UP ARROW;;;;
+21E8;RIGHTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE RIGHT ARROW;;;;
+21E9;DOWNWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE DOWN ARROW;;;;
+21EA;UPWARDS WHITE ARROW FROM BAR;So;0;ON;;;;;N;WHITE UP ARROW FROM BAR;;;;
+21EB;UPWARDS WHITE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;;
+21EC;UPWARDS WHITE ARROW ON PEDESTAL WITH HORIZONTAL BAR;So;0;ON;;;;;N;;;;;
+21ED;UPWARDS WHITE ARROW ON PEDESTAL WITH VERTICAL BAR;So;0;ON;;;;;N;;;;;
+21EE;UPWARDS WHITE DOUBLE ARROW;So;0;ON;;;;;N;;;;;
+21EF;UPWARDS WHITE DOUBLE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;;
+21F0;RIGHTWARDS WHITE ARROW FROM WALL;So;0;ON;;;;;N;;;;;
+21F1;NORTH WEST ARROW TO CORNER;So;0;ON;;;;;N;;;;;
+21F2;SOUTH EAST ARROW TO CORNER;So;0;ON;;;;;N;;;;;
+21F3;UP DOWN WHITE ARROW;So;0;ON;;;;;N;;;;;
+2200;FOR ALL;Sm;0;ON;;;;;N;;;;;
+2201;COMPLEMENT;Sm;0;ON;;;;;Y;;;;;
+2202;PARTIAL DIFFERENTIAL;Sm;0;ON;;;;;Y;;;;;
+2203;THERE EXISTS;Sm;0;ON;;;;;Y;;;;;
+2204;THERE DOES NOT EXIST;Sm;0;ON;2203 0338;;;;Y;;;;;
+2205;EMPTY SET;Sm;0;ON;;;;;N;;;;;
+2206;INCREMENT;Sm;0;ON;;;;;N;;;;;
+2207;NABLA;Sm;0;ON;;;;;N;;;;;
+2208;ELEMENT OF;Sm;0;ON;;;;;Y;;;;;
+2209;NOT AN ELEMENT OF;Sm;0;ON;2208 0338;;;;Y;;;;;
+220A;SMALL ELEMENT OF;Sm;0;ON;;;;;Y;;;;;
+220B;CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;;
+220C;DOES NOT CONTAIN AS MEMBER;Sm;0;ON;220B 0338;;;;Y;;;;;
+220D;SMALL CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;;
+220E;END OF PROOF;Sm;0;ON;;;;;N;;;;;
+220F;N-ARY PRODUCT;Sm;0;ON;;;;;N;;;;;
+2210;N-ARY COPRODUCT;Sm;0;ON;;;;;N;;;;;
+2211;N-ARY SUMMATION;Sm;0;ON;;;;;Y;;;;;
+2212;MINUS SIGN;Sm;0;ET;;;;;N;;;;;
+2213;MINUS-OR-PLUS SIGN;Sm;0;ET;;;;;N;;;;;
+2214;DOT PLUS;Sm;0;ON;;;;;N;;;;;
+2215;DIVISION SLASH;Sm;0;ON;;;;;Y;;;;;
+2216;SET MINUS;Sm;0;ON;;;;;Y;;;;;
+2217;ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;;
+2218;RING OPERATOR;Sm;0;ON;;;;;N;;;;;
+2219;BULLET OPERATOR;Sm;0;ON;;;;;N;;;;;
+221A;SQUARE ROOT;Sm;0;ON;;;;;Y;;;;;
+221B;CUBE ROOT;Sm;0;ON;;;;;Y;;;;;
+221C;FOURTH ROOT;Sm;0;ON;;;;;Y;;;;;
+221D;PROPORTIONAL TO;Sm;0;ON;;;;;Y;;;;;
+221E;INFINITY;Sm;0;ON;;;;;N;;;;;
+221F;RIGHT ANGLE;Sm;0;ON;;;;;Y;;;;;
+2220;ANGLE;Sm;0;ON;;;;;Y;;;;;
+2221;MEASURED ANGLE;Sm;0;ON;;;;;Y;;;;;
+2222;SPHERICAL ANGLE;Sm;0;ON;;;;;Y;;;;;
+2223;DIVIDES;Sm;0;ON;;;;;N;;;;;
+2224;DOES NOT DIVIDE;Sm;0;ON;2223 0338;;;;Y;;;;;
+2225;PARALLEL TO;Sm;0;ON;;;;;N;;;;;
+2226;NOT PARALLEL TO;Sm;0;ON;2225 0338;;;;Y;;;;;
+2227;LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2228;LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2229;INTERSECTION;Sm;0;ON;;;;;N;;;;;
+222A;UNION;Sm;0;ON;;;;;N;;;;;
+222B;INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+222C;DOUBLE INTEGRAL;Sm;0;ON;<compat> 222B 222B;;;;Y;;;;;
+222D;TRIPLE INTEGRAL;Sm;0;ON;<compat> 222B 222B 222B;;;;Y;;;;;
+222E;CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+222F;SURFACE INTEGRAL;Sm;0;ON;<compat> 222E 222E;;;;Y;;;;;
+2230;VOLUME INTEGRAL;Sm;0;ON;<compat> 222E 222E 222E;;;;Y;;;;;
+2231;CLOCKWISE INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2232;CLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2233;ANTICLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2234;THEREFORE;Sm;0;ON;;;;;N;;;;;
+2235;BECAUSE;Sm;0;ON;;;;;N;;;;;
+2236;RATIO;Sm;0;ON;;;;;N;;;;;
+2237;PROPORTION;Sm;0;ON;;;;;N;;;;;
+2238;DOT MINUS;Sm;0;ON;;;;;N;;;;;
+2239;EXCESS;Sm;0;ON;;;;;Y;;;;;
+223A;GEOMETRIC PROPORTION;Sm;0;ON;;;;;N;;;;;
+223B;HOMOTHETIC;Sm;0;ON;;;;;Y;;;;;
+223C;TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+223D;REVERSED TILDE;Sm;0;ON;;;;;Y;;lazy S;;;
+223E;INVERTED LAZY S;Sm;0;ON;;;;;Y;;;;;
+223F;SINE WAVE;Sm;0;ON;;;;;Y;;;;;
+2240;WREATH PRODUCT;Sm;0;ON;;;;;Y;;;;;
+2241;NOT TILDE;Sm;0;ON;223C 0338;;;;Y;;;;;
+2242;MINUS TILDE;Sm;0;ON;;;;;Y;;;;;
+2243;ASYMPTOTICALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2244;NOT ASYMPTOTICALLY EQUAL TO;Sm;0;ON;2243 0338;;;;Y;;;;;
+2245;APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2246;APPROXIMATELY BUT NOT ACTUALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2247;NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO;Sm;0;ON;2245 0338;;;;Y;;;;;
+2248;ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2249;NOT ALMOST EQUAL TO;Sm;0;ON;2248 0338;;;;Y;;;;;
+224A;ALMOST EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+224B;TRIPLE TILDE;Sm;0;ON;;;;;Y;;;;;
+224C;ALL EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+224D;EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+224E;GEOMETRICALLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+224F;DIFFERENCE BETWEEN;Sm;0;ON;;;;;N;;;;;
+2250;APPROACHES THE LIMIT;Sm;0;ON;;;;;N;;;;;
+2251;GEOMETRICALLY EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2252;APPROXIMATELY EQUAL TO OR THE IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+2253;IMAGE OF OR APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2254;COLON EQUALS;Sm;0;ON;;;;;Y;COLON EQUAL;;;;
+2255;EQUALS COLON;Sm;0;ON;;;;;Y;EQUAL COLON;;;;
+2256;RING IN EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2257;RING EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2258;CORRESPONDS TO;Sm;0;ON;;;;;N;;;;;
+2259;ESTIMATES;Sm;0;ON;;;;;N;;;;;
+225A;EQUIANGULAR TO;Sm;0;ON;;;;;N;;;;;
+225B;STAR EQUALS;Sm;0;ON;;;;;N;;;;;
+225C;DELTA EQUAL TO;Sm;0;ON;;;;;N;;;;;
+225D;EQUAL TO BY DEFINITION;Sm;0;ON;;;;;N;;;;;
+225E;MEASURED BY;Sm;0;ON;;;;;N;;;;;
+225F;QUESTIONED EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2260;NOT EQUAL TO;Sm;0;ON;003D 0338;;;;Y;;;;;
+2261;IDENTICAL TO;Sm;0;ON;;;;;N;;;;;
+2262;NOT IDENTICAL TO;Sm;0;ON;2261 0338;;;;Y;;;;;
+2263;STRICTLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+2264;LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUAL TO;;;;
+2265;GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUAL TO;;;;
+2266;LESS-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OVER EQUAL TO;;;;
+2267;GREATER-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OVER EQUAL TO;;;;
+2268;LESS-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUAL TO;;;;
+2269;GREATER-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUAL TO;;;;
+226A;MUCH LESS-THAN;Sm;0;ON;;;;;Y;MUCH LESS THAN;;;;
+226B;MUCH GREATER-THAN;Sm;0;ON;;;;;Y;MUCH GREATER THAN;;;;
+226C;BETWEEN;Sm;0;ON;;;;;N;;;;;
+226D;NOT EQUIVALENT TO;Sm;0;ON;224D 0338;;;;N;;;;;
+226E;NOT LESS-THAN;Sm;0;ON;003C 0338;;;;Y;NOT LESS THAN;;;;
+226F;NOT GREATER-THAN;Sm;0;ON;003E 0338;;;;Y;NOT GREATER THAN;;;;
+2270;NEITHER LESS-THAN NOR EQUAL TO;Sm;0;ON;2264 0338;;;;Y;NEITHER LESS THAN NOR EQUAL TO;;;;
+2271;NEITHER GREATER-THAN NOR EQUAL TO;Sm;0;ON;2265 0338;;;;Y;NEITHER GREATER THAN NOR EQUAL TO;;;;
+2272;LESS-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUIVALENT TO;;;;
+2273;GREATER-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUIVALENT TO;;;;
+2274;NEITHER LESS-THAN NOR EQUIVALENT TO;Sm;0;ON;2272 0338;;;;Y;NEITHER LESS THAN NOR EQUIVALENT TO;;;;
+2275;NEITHER GREATER-THAN NOR EQUIVALENT TO;Sm;0;ON;2273 0338;;;;Y;NEITHER GREATER THAN NOR EQUIVALENT TO;;;;
+2276;LESS-THAN OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN OR GREATER THAN;;;;
+2277;GREATER-THAN OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN OR LESS THAN;;;;
+2278;NEITHER LESS-THAN NOR GREATER-THAN;Sm;0;ON;2276 0338;;;;Y;NEITHER LESS THAN NOR GREATER THAN;;;;
+2279;NEITHER GREATER-THAN NOR LESS-THAN;Sm;0;ON;2277 0338;;;;Y;NEITHER GREATER THAN NOR LESS THAN;;;;
+227A;PRECEDES;Sm;0;ON;;;;;Y;;;;;
+227B;SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+227C;PRECEDES OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+227D;SUCCEEDS OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+227E;PRECEDES OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+227F;SUCCEEDS OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+2280;DOES NOT PRECEDE;Sm;0;ON;227A 0338;;;;Y;;;;;
+2281;DOES NOT SUCCEED;Sm;0;ON;227B 0338;;;;Y;;;;;
+2282;SUBSET OF;Sm;0;ON;;;;;Y;;;;;
+2283;SUPERSET OF;Sm;0;ON;;;;;Y;;;;;
+2284;NOT A SUBSET OF;Sm;0;ON;2282 0338;;;;Y;;;;;
+2285;NOT A SUPERSET OF;Sm;0;ON;2283 0338;;;;Y;;;;;
+2286;SUBSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2287;SUPERSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2288;NEITHER A SUBSET OF NOR EQUAL TO;Sm;0;ON;2286 0338;;;;Y;;;;;
+2289;NEITHER A SUPERSET OF NOR EQUAL TO;Sm;0;ON;2287 0338;;;;Y;;;;;
+228A;SUBSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUBSET OF OR NOT EQUAL TO;;;;
+228B;SUPERSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUPERSET OF OR NOT EQUAL TO;;;;
+228C;MULTISET;Sm;0;ON;;;;;Y;;;;;
+228D;MULTISET MULTIPLICATION;Sm;0;ON;;;;;N;;;;;
+228E;MULTISET UNION;Sm;0;ON;;;;;N;;;;;
+228F;SQUARE IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+2290;SQUARE ORIGINAL OF;Sm;0;ON;;;;;Y;;;;;
+2291;SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2292;SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2293;SQUARE CAP;Sm;0;ON;;;;;N;;;;;
+2294;SQUARE CUP;Sm;0;ON;;;;;N;;;;;
+2295;CIRCLED PLUS;Sm;0;ON;;;;;N;;;;;
+2296;CIRCLED MINUS;Sm;0;ON;;;;;N;;;;;
+2297;CIRCLED TIMES;Sm;0;ON;;;;;N;;;;;
+2298;CIRCLED DIVISION SLASH;Sm;0;ON;;;;;Y;;;;;
+2299;CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+229A;CIRCLED RING OPERATOR;Sm;0;ON;;;;;N;;;;;
+229B;CIRCLED ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;;
+229C;CIRCLED EQUALS;Sm;0;ON;;;;;N;;;;;
+229D;CIRCLED DASH;Sm;0;ON;;;;;N;;;;;
+229E;SQUARED PLUS;Sm;0;ON;;;;;N;;;;;
+229F;SQUARED MINUS;Sm;0;ON;;;;;N;;;;;
+22A0;SQUARED TIMES;Sm;0;ON;;;;;N;;;;;
+22A1;SQUARED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+22A2;RIGHT TACK;Sm;0;ON;;;;;Y;;;;;
+22A3;LEFT TACK;Sm;0;ON;;;;;Y;;;;;
+22A4;DOWN TACK;Sm;0;ON;;;;;N;;;;;
+22A5;UP TACK;Sm;0;ON;;;;;N;;;;;
+22A6;ASSERTION;Sm;0;ON;;;;;Y;;;;;
+22A7;MODELS;Sm;0;ON;;;;;Y;;;;;
+22A8;TRUE;Sm;0;ON;;;;;Y;;;;;
+22A9;FORCES;Sm;0;ON;;;;;Y;;;;;
+22AA;TRIPLE VERTICAL BAR RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+22AB;DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+22AC;DOES NOT PROVE;Sm;0;ON;22A2 0338;;;;Y;;;;;
+22AD;NOT TRUE;Sm;0;ON;22A8 0338;;;;Y;;;;;
+22AE;DOES NOT FORCE;Sm;0;ON;22A9 0338;;;;Y;;;;;
+22AF;NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;22AB 0338;;;;Y;;;;;
+22B0;PRECEDES UNDER RELATION;Sm;0;ON;;;;;Y;;;;;
+22B1;SUCCEEDS UNDER RELATION;Sm;0;ON;;;;;Y;;;;;
+22B2;NORMAL SUBGROUP OF;Sm;0;ON;;;;;Y;;;;;
+22B3;CONTAINS AS NORMAL SUBGROUP;Sm;0;ON;;;;;Y;;;;;
+22B4;NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22B5;CONTAINS AS NORMAL SUBGROUP OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22B6;ORIGINAL OF;Sm;0;ON;;;;;Y;;;;;
+22B7;IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+22B8;MULTIMAP;Sm;0;ON;;;;;Y;;;;;
+22B9;HERMITIAN CONJUGATE MATRIX;Sm;0;ON;;;;;N;;;;;
+22BA;INTERCALATE;Sm;0;ON;;;;;N;;;;;
+22BB;XOR;Sm;0;ON;;;;;N;;;;;
+22BC;NAND;Sm;0;ON;;;;;N;;;;;
+22BD;NOR;Sm;0;ON;;;;;N;;;;;
+22BE;RIGHT ANGLE WITH ARC;Sm;0;ON;;;;;Y;;;;;
+22BF;RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;;
+22C0;N-ARY LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+22C1;N-ARY LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+22C2;N-ARY INTERSECTION;Sm;0;ON;;;;;N;;;;;
+22C3;N-ARY UNION;Sm;0;ON;;;;;N;;;;;
+22C4;DIAMOND OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C5;DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C6;STAR OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C7;DIVISION TIMES;Sm;0;ON;;;;;N;;;;;
+22C8;BOWTIE;Sm;0;ON;;;;;N;;;;;
+22C9;LEFT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CA;RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CB;LEFT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CC;RIGHT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CD;REVERSED TILDE EQUALS;Sm;0;ON;;;;;Y;;;;;
+22CE;CURLY LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+22CF;CURLY LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+22D0;DOUBLE SUBSET;Sm;0;ON;;;;;Y;;;;;
+22D1;DOUBLE SUPERSET;Sm;0;ON;;;;;Y;;;;;
+22D2;DOUBLE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+22D3;DOUBLE UNION;Sm;0;ON;;;;;N;;;;;
+22D4;PITCHFORK;Sm;0;ON;;;;;N;;;;;
+22D5;EQUAL AND PARALLEL TO;Sm;0;ON;;;;;N;;;;;
+22D6;LESS-THAN WITH DOT;Sm;0;ON;;;;;Y;LESS THAN WITH DOT;;;;
+22D7;GREATER-THAN WITH DOT;Sm;0;ON;;;;;Y;GREATER THAN WITH DOT;;;;
+22D8;VERY MUCH LESS-THAN;Sm;0;ON;;;;;Y;VERY MUCH LESS THAN;;;;
+22D9;VERY MUCH GREATER-THAN;Sm;0;ON;;;;;Y;VERY MUCH GREATER THAN;;;;
+22DA;LESS-THAN EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN EQUAL TO OR GREATER THAN;;;;
+22DB;GREATER-THAN EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN EQUAL TO OR LESS THAN;;;;
+22DC;EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR LESS THAN;;;;
+22DD;EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR GREATER THAN;;;;
+22DE;EQUAL TO OR PRECEDES;Sm;0;ON;;;;;Y;;;;;
+22DF;EQUAL TO OR SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+22E0;DOES NOT PRECEDE OR EQUAL;Sm;0;ON;227C 0338;;;;Y;;;;;
+22E1;DOES NOT SUCCEED OR EQUAL;Sm;0;ON;227D 0338;;;;Y;;;;;
+22E2;NOT SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;2291 0338;;;;Y;;;;;
+22E3;NOT SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;2292 0338;;;;Y;;;;;
+22E4;SQUARE IMAGE OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22E5;SQUARE ORIGINAL OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22E6;LESS-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUIVALENT TO;;;;
+22E7;GREATER-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUIVALENT TO;;;;
+22E8;PRECEDES BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+22E9;SUCCEEDS BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+22EA;NOT NORMAL SUBGROUP OF;Sm;0;ON;22B2 0338;;;;Y;;;;;
+22EB;DOES NOT CONTAIN AS NORMAL SUBGROUP;Sm;0;ON;22B3 0338;;;;Y;;;;;
+22EC;NOT NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;22B4 0338;;;;Y;;;;;
+22ED;DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL;Sm;0;ON;22B5 0338;;;;Y;;;;;
+22EE;VERTICAL ELLIPSIS;Sm;0;ON;;;;;N;;;;;
+22EF;MIDLINE HORIZONTAL ELLIPSIS;Sm;0;ON;;;;;N;;;;;
+22F0;UP RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;;
+22F1;DOWN RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;;
+2300;DIAMETER SIGN;So;0;ON;;;;;N;;;;;
+2301;ELECTRIC ARROW;So;0;ON;;;;;N;;;;;
+2302;HOUSE;So;0;ON;;;;;N;;;;;
+2303;UP ARROWHEAD;So;0;ON;;;;;N;;;;;
+2304;DOWN ARROWHEAD;So;0;ON;;;;;N;;;;;
+2305;PROJECTIVE;So;0;ON;;;;;N;;;;;
+2306;PERSPECTIVE;So;0;ON;;;;;N;;;;;
+2307;WAVY LINE;So;0;ON;;;;;N;;;;;
+2308;LEFT CEILING;Sm;0;ON;;;;;Y;;;;;
+2309;RIGHT CEILING;Sm;0;ON;;;;;Y;;;;;
+230A;LEFT FLOOR;Sm;0;ON;;;;;Y;;;;;
+230B;RIGHT FLOOR;Sm;0;ON;;;;;Y;;;;;
+230C;BOTTOM RIGHT CROP;So;0;ON;;;;;N;;;;;
+230D;BOTTOM LEFT CROP;So;0;ON;;;;;N;;;;;
+230E;TOP RIGHT CROP;So;0;ON;;;;;N;;;;;
+230F;TOP LEFT CROP;So;0;ON;;;;;N;;;;;
+2310;REVERSED NOT SIGN;So;0;ON;;;;;N;;;;;
+2311;SQUARE LOZENGE;So;0;ON;;;;;N;;;;;
+2312;ARC;So;0;ON;;;;;N;;;;;
+2313;SEGMENT;So;0;ON;;;;;N;;;;;
+2314;SECTOR;So;0;ON;;;;;N;;;;;
+2315;TELEPHONE RECORDER;So;0;ON;;;;;N;;;;;
+2316;POSITION INDICATOR;So;0;ON;;;;;N;;;;;
+2317;VIEWDATA SQUARE;So;0;ON;;;;;N;;;;;
+2318;PLACE OF INTEREST SIGN;So;0;ON;;;;;N;COMMAND KEY;;;;
+2319;TURNED NOT SIGN;So;0;ON;;;;;N;;;;;
+231A;WATCH;So;0;ON;;;;;N;;;;;
+231B;HOURGLASS;So;0;ON;;;;;N;;;;;
+231C;TOP LEFT CORNER;So;0;ON;;;;;N;;;;;
+231D;TOP RIGHT CORNER;So;0;ON;;;;;N;;;;;
+231E;BOTTOM LEFT CORNER;So;0;ON;;;;;N;;;;;
+231F;BOTTOM RIGHT CORNER;So;0;ON;;;;;N;;;;;
+2320;TOP HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2321;BOTTOM HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2322;FROWN;So;0;ON;;;;;N;;;;;
+2323;SMILE;So;0;ON;;;;;N;;;;;
+2324;UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS;So;0;ON;;;;;N;ENTER KEY;;;;
+2325;OPTION KEY;So;0;ON;;;;;N;;;;;
+2326;ERASE TO THE RIGHT;So;0;ON;;;;;N;DELETE TO THE RIGHT KEY;;;;
+2327;X IN A RECTANGLE BOX;So;0;ON;;;;;N;CLEAR KEY;;;;
+2328;KEYBOARD;So;0;ON;;;;;N;;;;;
+2329;LEFT-POINTING ANGLE BRACKET;Ps;0;ON;3008;;;;Y;BRA;;;;
+232A;RIGHT-POINTING ANGLE BRACKET;Pe;0;ON;3009;;;;Y;KET;;;;
+232B;ERASE TO THE LEFT;So;0;ON;;;;;N;DELETE TO THE LEFT KEY;;;;
+232C;BENZENE RING;So;0;ON;;;;;N;;;;;
+232D;CYLINDRICITY;So;0;ON;;;;;N;;;;;
+232E;ALL AROUND-PROFILE;So;0;ON;;;;;N;;;;;
+232F;SYMMETRY;So;0;ON;;;;;N;;;;;
+2330;TOTAL RUNOUT;So;0;ON;;;;;N;;;;;
+2331;DIMENSION ORIGIN;So;0;ON;;;;;N;;;;;
+2332;CONICAL TAPER;So;0;ON;;;;;N;;;;;
+2333;SLOPE;So;0;ON;;;;;N;;;;;
+2334;COUNTERBORE;So;0;ON;;;;;N;;;;;
+2335;COUNTERSINK;So;0;ON;;;;;N;;;;;
+2336;APL FUNCTIONAL SYMBOL I-BEAM;So;0;L;;;;;N;;;;;
+2337;APL FUNCTIONAL SYMBOL SQUISH QUAD;So;0;L;;;;;N;;;;;
+2338;APL FUNCTIONAL SYMBOL QUAD EQUAL;So;0;L;;;;;N;;;;;
+2339;APL FUNCTIONAL SYMBOL QUAD DIVIDE;So;0;L;;;;;N;;;;;
+233A;APL FUNCTIONAL SYMBOL QUAD DIAMOND;So;0;L;;;;;N;;;;;
+233B;APL FUNCTIONAL SYMBOL QUAD JOT;So;0;L;;;;;N;;;;;
+233C;APL FUNCTIONAL SYMBOL QUAD CIRCLE;So;0;L;;;;;N;;;;;
+233D;APL FUNCTIONAL SYMBOL CIRCLE STILE;So;0;L;;;;;N;;;;;
+233E;APL FUNCTIONAL SYMBOL CIRCLE JOT;So;0;L;;;;;N;;;;;
+233F;APL FUNCTIONAL SYMBOL SLASH BAR;So;0;L;;;;;N;;;;;
+2340;APL FUNCTIONAL SYMBOL BACKSLASH BAR;So;0;L;;;;;N;;;;;
+2341;APL FUNCTIONAL SYMBOL QUAD SLASH;So;0;L;;;;;N;;;;;
+2342;APL FUNCTIONAL SYMBOL QUAD BACKSLASH;So;0;L;;;;;N;;;;;
+2343;APL FUNCTIONAL SYMBOL QUAD LESS-THAN;So;0;L;;;;;N;;;;;
+2344;APL FUNCTIONAL SYMBOL QUAD GREATER-THAN;So;0;L;;;;;N;;;;;
+2345;APL FUNCTIONAL SYMBOL LEFTWARDS VANE;So;0;L;;;;;N;;;;;
+2346;APL FUNCTIONAL SYMBOL RIGHTWARDS VANE;So;0;L;;;;;N;;;;;
+2347;APL FUNCTIONAL SYMBOL QUAD LEFTWARDS ARROW;So;0;L;;;;;N;;;;;
+2348;APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS ARROW;So;0;L;;;;;N;;;;;
+2349;APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH;So;0;L;;;;;N;;;;;
+234A;APL FUNCTIONAL SYMBOL DOWN TACK UNDERBAR;So;0;L;;;;;N;;*;;;
+234B;APL FUNCTIONAL SYMBOL DELTA STILE;So;0;L;;;;;N;;;;;
+234C;APL FUNCTIONAL SYMBOL QUAD DOWN CARET;So;0;L;;;;;N;;;;;
+234D;APL FUNCTIONAL SYMBOL QUAD DELTA;So;0;L;;;;;N;;;;;
+234E;APL FUNCTIONAL SYMBOL DOWN TACK JOT;So;0;L;;;;;N;;*;;;
+234F;APL FUNCTIONAL SYMBOL UPWARDS VANE;So;0;L;;;;;N;;;;;
+2350;APL FUNCTIONAL SYMBOL QUAD UPWARDS ARROW;So;0;L;;;;;N;;;;;
+2351;APL FUNCTIONAL SYMBOL UP TACK OVERBAR;So;0;L;;;;;N;;*;;;
+2352;APL FUNCTIONAL SYMBOL DEL STILE;So;0;L;;;;;N;;;;;
+2353;APL FUNCTIONAL SYMBOL QUAD UP CARET;So;0;L;;;;;N;;;;;
+2354;APL FUNCTIONAL SYMBOL QUAD DEL;So;0;L;;;;;N;;;;;
+2355;APL FUNCTIONAL SYMBOL UP TACK JOT;So;0;L;;;;;N;;*;;;
+2356;APL FUNCTIONAL SYMBOL DOWNWARDS VANE;So;0;L;;;;;N;;;;;
+2357;APL FUNCTIONAL SYMBOL QUAD DOWNWARDS ARROW;So;0;L;;;;;N;;;;;
+2358;APL FUNCTIONAL SYMBOL QUOTE UNDERBAR;So;0;L;;;;;N;;;;;
+2359;APL FUNCTIONAL SYMBOL DELTA UNDERBAR;So;0;L;;;;;N;;;;;
+235A;APL FUNCTIONAL SYMBOL DIAMOND UNDERBAR;So;0;L;;;;;N;;;;;
+235B;APL FUNCTIONAL SYMBOL JOT UNDERBAR;So;0;L;;;;;N;;;;;
+235C;APL FUNCTIONAL SYMBOL CIRCLE UNDERBAR;So;0;L;;;;;N;;;;;
+235D;APL FUNCTIONAL SYMBOL UP SHOE JOT;So;0;L;;;;;N;;;;;
+235E;APL FUNCTIONAL SYMBOL QUOTE QUAD;So;0;L;;;;;N;;;;;
+235F;APL FUNCTIONAL SYMBOL CIRCLE STAR;So;0;L;;;;;N;;;;;
+2360;APL FUNCTIONAL SYMBOL QUAD COLON;So;0;L;;;;;N;;;;;
+2361;APL FUNCTIONAL SYMBOL UP TACK DIAERESIS;So;0;L;;;;;N;;*;;;
+2362;APL FUNCTIONAL SYMBOL DEL DIAERESIS;So;0;L;;;;;N;;;;;
+2363;APL FUNCTIONAL SYMBOL STAR DIAERESIS;So;0;L;;;;;N;;;;;
+2364;APL FUNCTIONAL SYMBOL JOT DIAERESIS;So;0;L;;;;;N;;;;;
+2365;APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS;So;0;L;;;;;N;;;;;
+2366;APL FUNCTIONAL SYMBOL DOWN SHOE STILE;So;0;L;;;;;N;;;;;
+2367;APL FUNCTIONAL SYMBOL LEFT SHOE STILE;So;0;L;;;;;N;;;;;
+2368;APL FUNCTIONAL SYMBOL TILDE DIAERESIS;So;0;L;;;;;N;;;;;
+2369;APL FUNCTIONAL SYMBOL GREATER-THAN DIAERESIS;So;0;L;;;;;N;;;;;
+236A;APL FUNCTIONAL SYMBOL COMMA BAR;So;0;L;;;;;N;;;;;
+236B;APL FUNCTIONAL SYMBOL DEL TILDE;So;0;L;;;;;N;;;;;
+236C;APL FUNCTIONAL SYMBOL ZILDE;So;0;L;;;;;N;;;;;
+236D;APL FUNCTIONAL SYMBOL STILE TILDE;So;0;L;;;;;N;;;;;
+236E;APL FUNCTIONAL SYMBOL SEMICOLON UNDERBAR;So;0;L;;;;;N;;;;;
+236F;APL FUNCTIONAL SYMBOL QUAD NOT EQUAL;So;0;L;;;;;N;;;;;
+2370;APL FUNCTIONAL SYMBOL QUAD QUESTION;So;0;L;;;;;N;;;;;
+2371;APL FUNCTIONAL SYMBOL DOWN CARET TILDE;So;0;L;;;;;N;;;;;
+2372;APL FUNCTIONAL SYMBOL UP CARET TILDE;So;0;L;;;;;N;;;;;
+2373;APL FUNCTIONAL SYMBOL IOTA;So;0;L;;;;;N;;;;;
+2374;APL FUNCTIONAL SYMBOL RHO;So;0;L;;;;;N;;;;;
+2375;APL FUNCTIONAL SYMBOL OMEGA;So;0;L;;;;;N;;;;;
+2376;APL FUNCTIONAL SYMBOL ALPHA UNDERBAR;So;0;L;;;;;N;;;;;
+2377;APL FUNCTIONAL SYMBOL EPSILON UNDERBAR;So;0;L;;;;;N;;;;;
+2378;APL FUNCTIONAL SYMBOL IOTA UNDERBAR;So;0;L;;;;;N;;;;;
+2379;APL FUNCTIONAL SYMBOL OMEGA UNDERBAR;So;0;L;;;;;N;;;;;
+237A;APL FUNCTIONAL SYMBOL ALPHA;So;0;L;;;;;N;;;;;
+237B;NOT CHECK MARK;So;0;ON;;;;;N;;;;;
+237D;SHOULDERED OPEN BOX;So;0;ON;;;;;N;;;;;
+237E;BELL SYMBOL;So;0;ON;;;;;N;;;;;
+237F;VERTICAL LINE WITH MIDDLE DOT;So;0;ON;;;;;N;;;;;
+2380;INSERTION SYMBOL;So;0;ON;;;;;N;;;;;
+2381;CONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;;
+2382;DISCONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;;
+2383;EMPHASIS SYMBOL;So;0;ON;;;;;N;;;;;
+2384;COMPOSITION SYMBOL;So;0;ON;;;;;N;;;;;
+2385;WHITE SQUARE WITH CENTRE VERTICAL LINE;So;0;ON;;;;;N;;;;;
+2386;ENTER SYMBOL;So;0;ON;;;;;N;;;;;
+2387;ALTERNATIVE KEY SYMBOL;So;0;ON;;;;;N;;;;;
+2388;HELM SYMBOL;So;0;ON;;;;;N;;;;;
+2389;CIRCLED HORIZONTAL BAR WITH NOTCH;So;0;ON;;;;;N;;pause;;;
+238A;CIRCLED TRIANGLE DOWN;So;0;ON;;;;;N;;break;;;
+238B;BROKEN CIRCLE WITH NORTHWEST ARROW;So;0;ON;;;;;N;;escape;;;
+238C;UNDO SYMBOL;So;0;ON;;;;;N;;;;;
+238D;MONOSTABLE SYMBOL;So;0;ON;;;;;N;;;;;
+238E;HYSTERESIS SYMBOL;So;0;ON;;;;;N;;;;;
+238F;OPEN-CIRCUIT-OUTPUT H-TYPE SYMBOL;So;0;ON;;;;;N;;;;;
+2390;OPEN-CIRCUIT-OUTPUT L-TYPE SYMBOL;So;0;ON;;;;;N;;;;;
+2391;PASSIVE-PULL-DOWN-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;;
+2392;PASSIVE-PULL-UP-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;;
+2393;DIRECT CURRENT SYMBOL FORM TWO;So;0;ON;;;;;N;;;;;
+2394;SOFTWARE-FUNCTION SYMBOL;So;0;ON;;;;;N;;;;;
+2395;APL FUNCTIONAL SYMBOL QUAD;So;0;L;;;;;N;;;;;
+2396;DECIMAL SEPARATOR KEY SYMBOL;So;0;ON;;;;;N;;;;;
+2397;PREVIOUS PAGE;So;0;ON;;;;;N;;;;;
+2398;NEXT PAGE;So;0;ON;;;;;N;;;;;
+2399;PRINT SCREEN SYMBOL;So;0;ON;;;;;N;;;;;
+239A;CLEAR SCREEN SYMBOL;So;0;ON;;;;;N;;;;;
+2400;SYMBOL FOR NULL;So;0;ON;;;;;N;GRAPHIC FOR NULL;;;;
+2401;SYMBOL FOR START OF HEADING;So;0;ON;;;;;N;GRAPHIC FOR START OF HEADING;;;;
+2402;SYMBOL FOR START OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR START OF TEXT;;;;
+2403;SYMBOL FOR END OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR END OF TEXT;;;;
+2404;SYMBOL FOR END OF TRANSMISSION;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION;;;;
+2405;SYMBOL FOR ENQUIRY;So;0;ON;;;;;N;GRAPHIC FOR ENQUIRY;;;;
+2406;SYMBOL FOR ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR ACKNOWLEDGE;;;;
+2407;SYMBOL FOR BELL;So;0;ON;;;;;N;GRAPHIC FOR BELL;;;;
+2408;SYMBOL FOR BACKSPACE;So;0;ON;;;;;N;GRAPHIC FOR BACKSPACE;;;;
+2409;SYMBOL FOR HORIZONTAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR HORIZONTAL TABULATION;;;;
+240A;SYMBOL FOR LINE FEED;So;0;ON;;;;;N;GRAPHIC FOR LINE FEED;;;;
+240B;SYMBOL FOR VERTICAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR VERTICAL TABULATION;;;;
+240C;SYMBOL FOR FORM FEED;So;0;ON;;;;;N;GRAPHIC FOR FORM FEED;;;;
+240D;SYMBOL FOR CARRIAGE RETURN;So;0;ON;;;;;N;GRAPHIC FOR CARRIAGE RETURN;;;;
+240E;SYMBOL FOR SHIFT OUT;So;0;ON;;;;;N;GRAPHIC FOR SHIFT OUT;;;;
+240F;SYMBOL FOR SHIFT IN;So;0;ON;;;;;N;GRAPHIC FOR SHIFT IN;;;;
+2410;SYMBOL FOR DATA LINK ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR DATA LINK ESCAPE;;;;
+2411;SYMBOL FOR DEVICE CONTROL ONE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL ONE;;;;
+2412;SYMBOL FOR DEVICE CONTROL TWO;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL TWO;;;;
+2413;SYMBOL FOR DEVICE CONTROL THREE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL THREE;;;;
+2414;SYMBOL FOR DEVICE CONTROL FOUR;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL FOUR;;;;
+2415;SYMBOL FOR NEGATIVE ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR NEGATIVE ACKNOWLEDGE;;;;
+2416;SYMBOL FOR SYNCHRONOUS IDLE;So;0;ON;;;;;N;GRAPHIC FOR SYNCHRONOUS IDLE;;;;
+2417;SYMBOL FOR END OF TRANSMISSION BLOCK;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION BLOCK;;;;
+2418;SYMBOL FOR CANCEL;So;0;ON;;;;;N;GRAPHIC FOR CANCEL;;;;
+2419;SYMBOL FOR END OF MEDIUM;So;0;ON;;;;;N;GRAPHIC FOR END OF MEDIUM;;;;
+241A;SYMBOL FOR SUBSTITUTE;So;0;ON;;;;;N;GRAPHIC FOR SUBSTITUTE;;;;
+241B;SYMBOL FOR ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR ESCAPE;;;;
+241C;SYMBOL FOR FILE SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR FILE SEPARATOR;;;;
+241D;SYMBOL FOR GROUP SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR GROUP SEPARATOR;;;;
+241E;SYMBOL FOR RECORD SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR RECORD SEPARATOR;;;;
+241F;SYMBOL FOR UNIT SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR UNIT SEPARATOR;;;;
+2420;SYMBOL FOR SPACE;So;0;ON;;;;;N;GRAPHIC FOR SPACE;;;;
+2421;SYMBOL FOR DELETE;So;0;ON;;;;;N;GRAPHIC FOR DELETE;;;;
+2422;BLANK SYMBOL;So;0;ON;;;;;N;BLANK;;;;
+2423;OPEN BOX;So;0;ON;;;;;N;;;;;
+2424;SYMBOL FOR NEWLINE;So;0;ON;;;;;N;GRAPHIC FOR NEWLINE;;;;
+2425;SYMBOL FOR DELETE FORM TWO;So;0;ON;;;;;N;;;;;
+2426;SYMBOL FOR SUBSTITUTE FORM TWO;So;0;ON;;;;;N;;;;;
+2440;OCR HOOK;So;0;ON;;;;;N;;;;;
+2441;OCR CHAIR;So;0;ON;;;;;N;;;;;
+2442;OCR FORK;So;0;ON;;;;;N;;;;;
+2443;OCR INVERTED FORK;So;0;ON;;;;;N;;;;;
+2444;OCR BELT BUCKLE;So;0;ON;;;;;N;;;;;
+2445;OCR BOW TIE;So;0;ON;;;;;N;;;;;
+2446;OCR BRANCH BANK IDENTIFICATION;So;0;ON;;;;;N;;;;;
+2447;OCR AMOUNT OF CHECK;So;0;ON;;;;;N;;;;;
+2448;OCR DASH;So;0;ON;;;;;N;;;;;
+2449;OCR CUSTOMER ACCOUNT NUMBER;So;0;ON;;;;;N;;;;;
+244A;OCR DOUBLE BACKSLASH;So;0;ON;;;;;N;;;;;
+2460;CIRCLED DIGIT ONE;No;0;EN;<circle> 0031;;1;1;N;;;;;
+2461;CIRCLED DIGIT TWO;No;0;EN;<circle> 0032;;2;2;N;;;;;
+2462;CIRCLED DIGIT THREE;No;0;EN;<circle> 0033;;3;3;N;;;;;
+2463;CIRCLED DIGIT FOUR;No;0;EN;<circle> 0034;;4;4;N;;;;;
+2464;CIRCLED DIGIT FIVE;No;0;EN;<circle> 0035;;5;5;N;;;;;
+2465;CIRCLED DIGIT SIX;No;0;EN;<circle> 0036;;6;6;N;;;;;
+2466;CIRCLED DIGIT SEVEN;No;0;EN;<circle> 0037;;7;7;N;;;;;
+2467;CIRCLED DIGIT EIGHT;No;0;EN;<circle> 0038;;8;8;N;;;;;
+2468;CIRCLED DIGIT NINE;No;0;EN;<circle> 0039;;9;9;N;;;;;
+2469;CIRCLED NUMBER TEN;No;0;EN;<circle> 0031 0030;;;10;N;;;;;
+246A;CIRCLED NUMBER ELEVEN;No;0;EN;<circle> 0031 0031;;;11;N;;;;;
+246B;CIRCLED NUMBER TWELVE;No;0;EN;<circle> 0031 0032;;;12;N;;;;;
+246C;CIRCLED NUMBER THIRTEEN;No;0;EN;<circle> 0031 0033;;;13;N;;;;;
+246D;CIRCLED NUMBER FOURTEEN;No;0;EN;<circle> 0031 0034;;;14;N;;;;;
+246E;CIRCLED NUMBER FIFTEEN;No;0;EN;<circle> 0031 0035;;;15;N;;;;;
+246F;CIRCLED NUMBER SIXTEEN;No;0;EN;<circle> 0031 0036;;;16;N;;;;;
+2470;CIRCLED NUMBER SEVENTEEN;No;0;EN;<circle> 0031 0037;;;17;N;;;;;
+2471;CIRCLED NUMBER EIGHTEEN;No;0;EN;<circle> 0031 0038;;;18;N;;;;;
+2472;CIRCLED NUMBER NINETEEN;No;0;EN;<circle> 0031 0039;;;19;N;;;;;
+2473;CIRCLED NUMBER TWENTY;No;0;EN;<circle> 0032 0030;;;20;N;;;;;
+2474;PARENTHESIZED DIGIT ONE;No;0;EN;<compat> 0028 0031 0029;;1;1;N;;;;;
+2475;PARENTHESIZED DIGIT TWO;No;0;EN;<compat> 0028 0032 0029;;2;2;N;;;;;
+2476;PARENTHESIZED DIGIT THREE;No;0;EN;<compat> 0028 0033 0029;;3;3;N;;;;;
+2477;PARENTHESIZED DIGIT FOUR;No;0;EN;<compat> 0028 0034 0029;;4;4;N;;;;;
+2478;PARENTHESIZED DIGIT FIVE;No;0;EN;<compat> 0028 0035 0029;;5;5;N;;;;;
+2479;PARENTHESIZED DIGIT SIX;No;0;EN;<compat> 0028 0036 0029;;6;6;N;;;;;
+247A;PARENTHESIZED DIGIT SEVEN;No;0;EN;<compat> 0028 0037 0029;;7;7;N;;;;;
+247B;PARENTHESIZED DIGIT EIGHT;No;0;EN;<compat> 0028 0038 0029;;8;8;N;;;;;
+247C;PARENTHESIZED DIGIT NINE;No;0;EN;<compat> 0028 0039 0029;;9;9;N;;;;;
+247D;PARENTHESIZED NUMBER TEN;No;0;EN;<compat> 0028 0031 0030 0029;;;10;N;;;;;
+247E;PARENTHESIZED NUMBER ELEVEN;No;0;EN;<compat> 0028 0031 0031 0029;;;11;N;;;;;
+247F;PARENTHESIZED NUMBER TWELVE;No;0;EN;<compat> 0028 0031 0032 0029;;;12;N;;;;;
+2480;PARENTHESIZED NUMBER THIRTEEN;No;0;EN;<compat> 0028 0031 0033 0029;;;13;N;;;;;
+2481;PARENTHESIZED NUMBER FOURTEEN;No;0;EN;<compat> 0028 0031 0034 0029;;;14;N;;;;;
+2482;PARENTHESIZED NUMBER FIFTEEN;No;0;EN;<compat> 0028 0031 0035 0029;;;15;N;;;;;
+2483;PARENTHESIZED NUMBER SIXTEEN;No;0;EN;<compat> 0028 0031 0036 0029;;;16;N;;;;;
+2484;PARENTHESIZED NUMBER SEVENTEEN;No;0;EN;<compat> 0028 0031 0037 0029;;;17;N;;;;;
+2485;PARENTHESIZED NUMBER EIGHTEEN;No;0;EN;<compat> 0028 0031 0038 0029;;;18;N;;;;;
+2486;PARENTHESIZED NUMBER NINETEEN;No;0;EN;<compat> 0028 0031 0039 0029;;;19;N;;;;;
+2487;PARENTHESIZED NUMBER TWENTY;No;0;EN;<compat> 0028 0032 0030 0029;;;20;N;;;;;
+2488;DIGIT ONE FULL STOP;No;0;EN;<compat> 0031 002E;;1;1;N;DIGIT ONE PERIOD;;;;
+2489;DIGIT TWO FULL STOP;No;0;EN;<compat> 0032 002E;;2;2;N;DIGIT TWO PERIOD;;;;
+248A;DIGIT THREE FULL STOP;No;0;EN;<compat> 0033 002E;;3;3;N;DIGIT THREE PERIOD;;;;
+248B;DIGIT FOUR FULL STOP;No;0;EN;<compat> 0034 002E;;4;4;N;DIGIT FOUR PERIOD;;;;
+248C;DIGIT FIVE FULL STOP;No;0;EN;<compat> 0035 002E;;5;5;N;DIGIT FIVE PERIOD;;;;
+248D;DIGIT SIX FULL STOP;No;0;EN;<compat> 0036 002E;;6;6;N;DIGIT SIX PERIOD;;;;
+248E;DIGIT SEVEN FULL STOP;No;0;EN;<compat> 0037 002E;;7;7;N;DIGIT SEVEN PERIOD;;;;
+248F;DIGIT EIGHT FULL STOP;No;0;EN;<compat> 0038 002E;;8;8;N;DIGIT EIGHT PERIOD;;;;
+2490;DIGIT NINE FULL STOP;No;0;EN;<compat> 0039 002E;;9;9;N;DIGIT NINE PERIOD;;;;
+2491;NUMBER TEN FULL STOP;No;0;EN;<compat> 0031 0030 002E;;;10;N;NUMBER TEN PERIOD;;;;
+2492;NUMBER ELEVEN FULL STOP;No;0;EN;<compat> 0031 0031 002E;;;11;N;NUMBER ELEVEN PERIOD;;;;
+2493;NUMBER TWELVE FULL STOP;No;0;EN;<compat> 0031 0032 002E;;;12;N;NUMBER TWELVE PERIOD;;;;
+2494;NUMBER THIRTEEN FULL STOP;No;0;EN;<compat> 0031 0033 002E;;;13;N;NUMBER THIRTEEN PERIOD;;;;
+2495;NUMBER FOURTEEN FULL STOP;No;0;EN;<compat> 0031 0034 002E;;;14;N;NUMBER FOURTEEN PERIOD;;;;
+2496;NUMBER FIFTEEN FULL STOP;No;0;EN;<compat> 0031 0035 002E;;;15;N;NUMBER FIFTEEN PERIOD;;;;
+2497;NUMBER SIXTEEN FULL STOP;No;0;EN;<compat> 0031 0036 002E;;;16;N;NUMBER SIXTEEN PERIOD;;;;
+2498;NUMBER SEVENTEEN FULL STOP;No;0;EN;<compat> 0031 0037 002E;;;17;N;NUMBER SEVENTEEN PERIOD;;;;
+2499;NUMBER EIGHTEEN FULL STOP;No;0;EN;<compat> 0031 0038 002E;;;18;N;NUMBER EIGHTEEN PERIOD;;;;
+249A;NUMBER NINETEEN FULL STOP;No;0;EN;<compat> 0031 0039 002E;;;19;N;NUMBER NINETEEN PERIOD;;;;
+249B;NUMBER TWENTY FULL STOP;No;0;EN;<compat> 0032 0030 002E;;;20;N;NUMBER TWENTY PERIOD;;;;
+249C;PARENTHESIZED LATIN SMALL LETTER A;So;0;L;<compat> 0028 0061 0029;;;;N;;;;;
+249D;PARENTHESIZED LATIN SMALL LETTER B;So;0;L;<compat> 0028 0062 0029;;;;N;;;;;
+249E;PARENTHESIZED LATIN SMALL LETTER C;So;0;L;<compat> 0028 0063 0029;;;;N;;;;;
+249F;PARENTHESIZED LATIN SMALL LETTER D;So;0;L;<compat> 0028 0064 0029;;;;N;;;;;
+24A0;PARENTHESIZED LATIN SMALL LETTER E;So;0;L;<compat> 0028 0065 0029;;;;N;;;;;
+24A1;PARENTHESIZED LATIN SMALL LETTER F;So;0;L;<compat> 0028 0066 0029;;;;N;;;;;
+24A2;PARENTHESIZED LATIN SMALL LETTER G;So;0;L;<compat> 0028 0067 0029;;;;N;;;;;
+24A3;PARENTHESIZED LATIN SMALL LETTER H;So;0;L;<compat> 0028 0068 0029;;;;N;;;;;
+24A4;PARENTHESIZED LATIN SMALL LETTER I;So;0;L;<compat> 0028 0069 0029;;;;N;;;;;
+24A5;PARENTHESIZED LATIN SMALL LETTER J;So;0;L;<compat> 0028 006A 0029;;;;N;;;;;
+24A6;PARENTHESIZED LATIN SMALL LETTER K;So;0;L;<compat> 0028 006B 0029;;;;N;;;;;
+24A7;PARENTHESIZED LATIN SMALL LETTER L;So;0;L;<compat> 0028 006C 0029;;;;N;;;;;
+24A8;PARENTHESIZED LATIN SMALL LETTER M;So;0;L;<compat> 0028 006D 0029;;;;N;;;;;
+24A9;PARENTHESIZED LATIN SMALL LETTER N;So;0;L;<compat> 0028 006E 0029;;;;N;;;;;
+24AA;PARENTHESIZED LATIN SMALL LETTER O;So;0;L;<compat> 0028 006F 0029;;;;N;;;;;
+24AB;PARENTHESIZED LATIN SMALL LETTER P;So;0;L;<compat> 0028 0070 0029;;;;N;;;;;
+24AC;PARENTHESIZED LATIN SMALL LETTER Q;So;0;L;<compat> 0028 0071 0029;;;;N;;;;;
+24AD;PARENTHESIZED LATIN SMALL LETTER R;So;0;L;<compat> 0028 0072 0029;;;;N;;;;;
+24AE;PARENTHESIZED LATIN SMALL LETTER S;So;0;L;<compat> 0028 0073 0029;;;;N;;;;;
+24AF;PARENTHESIZED LATIN SMALL LETTER T;So;0;L;<compat> 0028 0074 0029;;;;N;;;;;
+24B0;PARENTHESIZED LATIN SMALL LETTER U;So;0;L;<compat> 0028 0075 0029;;;;N;;;;;
+24B1;PARENTHESIZED LATIN SMALL LETTER V;So;0;L;<compat> 0028 0076 0029;;;;N;;;;;
+24B2;PARENTHESIZED LATIN SMALL LETTER W;So;0;L;<compat> 0028 0077 0029;;;;N;;;;;
+24B3;PARENTHESIZED LATIN SMALL LETTER X;So;0;L;<compat> 0028 0078 0029;;;;N;;;;;
+24B4;PARENTHESIZED LATIN SMALL LETTER Y;So;0;L;<compat> 0028 0079 0029;;;;N;;;;;
+24B5;PARENTHESIZED LATIN SMALL LETTER Z;So;0;L;<compat> 0028 007A 0029;;;;N;;;;;
+24B6;CIRCLED LATIN CAPITAL LETTER A;So;0;L;<circle> 0041;;;;N;;;;24D0;
+24B7;CIRCLED LATIN CAPITAL LETTER B;So;0;L;<circle> 0042;;;;N;;;;24D1;
+24B8;CIRCLED LATIN CAPITAL LETTER C;So;0;L;<circle> 0043;;;;N;;;;24D2;
+24B9;CIRCLED LATIN CAPITAL LETTER D;So;0;L;<circle> 0044;;;;N;;;;24D3;
+24BA;CIRCLED LATIN CAPITAL LETTER E;So;0;L;<circle> 0045;;;;N;;;;24D4;
+24BB;CIRCLED LATIN CAPITAL LETTER F;So;0;L;<circle> 0046;;;;N;;;;24D5;
+24BC;CIRCLED LATIN CAPITAL LETTER G;So;0;L;<circle> 0047;;;;N;;;;24D6;
+24BD;CIRCLED LATIN CAPITAL LETTER H;So;0;L;<circle> 0048;;;;N;;;;24D7;
+24BE;CIRCLED LATIN CAPITAL LETTER I;So;0;L;<circle> 0049;;;;N;;;;24D8;
+24BF;CIRCLED LATIN CAPITAL LETTER J;So;0;L;<circle> 004A;;;;N;;;;24D9;
+24C0;CIRCLED LATIN CAPITAL LETTER K;So;0;L;<circle> 004B;;;;N;;;;24DA;
+24C1;CIRCLED LATIN CAPITAL LETTER L;So;0;L;<circle> 004C;;;;N;;;;24DB;
+24C2;CIRCLED LATIN CAPITAL LETTER M;So;0;L;<circle> 004D;;;;N;;;;24DC;
+24C3;CIRCLED LATIN CAPITAL LETTER N;So;0;L;<circle> 004E;;;;N;;;;24DD;
+24C4;CIRCLED LATIN CAPITAL LETTER O;So;0;L;<circle> 004F;;;;N;;;;24DE;
+24C5;CIRCLED LATIN CAPITAL LETTER P;So;0;L;<circle> 0050;;;;N;;;;24DF;
+24C6;CIRCLED LATIN CAPITAL LETTER Q;So;0;L;<circle> 0051;;;;N;;;;24E0;
+24C7;CIRCLED LATIN CAPITAL LETTER R;So;0;L;<circle> 0052;;;;N;;;;24E1;
+24C8;CIRCLED LATIN CAPITAL LETTER S;So;0;L;<circle> 0053;;;;N;;;;24E2;
+24C9;CIRCLED LATIN CAPITAL LETTER T;So;0;L;<circle> 0054;;;;N;;;;24E3;
+24CA;CIRCLED LATIN CAPITAL LETTER U;So;0;L;<circle> 0055;;;;N;;;;24E4;
+24CB;CIRCLED LATIN CAPITAL LETTER V;So;0;L;<circle> 0056;;;;N;;;;24E5;
+24CC;CIRCLED LATIN CAPITAL LETTER W;So;0;L;<circle> 0057;;;;N;;;;24E6;
+24CD;CIRCLED LATIN CAPITAL LETTER X;So;0;L;<circle> 0058;;;;N;;;;24E7;
+24CE;CIRCLED LATIN CAPITAL LETTER Y;So;0;L;<circle> 0059;;;;N;;;;24E8;
+24CF;CIRCLED LATIN CAPITAL LETTER Z;So;0;L;<circle> 005A;;;;N;;;;24E9;
+24D0;CIRCLED LATIN SMALL LETTER A;So;0;L;<circle> 0061;;;;N;;;24B6;;24B6
+24D1;CIRCLED LATIN SMALL LETTER B;So;0;L;<circle> 0062;;;;N;;;24B7;;24B7
+24D2;CIRCLED LATIN SMALL LETTER C;So;0;L;<circle> 0063;;;;N;;;24B8;;24B8
+24D3;CIRCLED LATIN SMALL LETTER D;So;0;L;<circle> 0064;;;;N;;;24B9;;24B9
+24D4;CIRCLED LATIN SMALL LETTER E;So;0;L;<circle> 0065;;;;N;;;24BA;;24BA
+24D5;CIRCLED LATIN SMALL LETTER F;So;0;L;<circle> 0066;;;;N;;;24BB;;24BB
+24D6;CIRCLED LATIN SMALL LETTER G;So;0;L;<circle> 0067;;;;N;;;24BC;;24BC
+24D7;CIRCLED LATIN SMALL LETTER H;So;0;L;<circle> 0068;;;;N;;;24BD;;24BD
+24D8;CIRCLED LATIN SMALL LETTER I;So;0;L;<circle> 0069;;;;N;;;24BE;;24BE
+24D9;CIRCLED LATIN SMALL LETTER J;So;0;L;<circle> 006A;;;;N;;;24BF;;24BF
+24DA;CIRCLED LATIN SMALL LETTER K;So;0;L;<circle> 006B;;;;N;;;24C0;;24C0
+24DB;CIRCLED LATIN SMALL LETTER L;So;0;L;<circle> 006C;;;;N;;;24C1;;24C1
+24DC;CIRCLED LATIN SMALL LETTER M;So;0;L;<circle> 006D;;;;N;;;24C2;;24C2
+24DD;CIRCLED LATIN SMALL LETTER N;So;0;L;<circle> 006E;;;;N;;;24C3;;24C3
+24DE;CIRCLED LATIN SMALL LETTER O;So;0;L;<circle> 006F;;;;N;;;24C4;;24C4
+24DF;CIRCLED LATIN SMALL LETTER P;So;0;L;<circle> 0070;;;;N;;;24C5;;24C5
+24E0;CIRCLED LATIN SMALL LETTER Q;So;0;L;<circle> 0071;;;;N;;;24C6;;24C6
+24E1;CIRCLED LATIN SMALL LETTER R;So;0;L;<circle> 0072;;;;N;;;24C7;;24C7
+24E2;CIRCLED LATIN SMALL LETTER S;So;0;L;<circle> 0073;;;;N;;;24C8;;24C8
+24E3;CIRCLED LATIN SMALL LETTER T;So;0;L;<circle> 0074;;;;N;;;24C9;;24C9
+24E4;CIRCLED LATIN SMALL LETTER U;So;0;L;<circle> 0075;;;;N;;;24CA;;24CA
+24E5;CIRCLED LATIN SMALL LETTER V;So;0;L;<circle> 0076;;;;N;;;24CB;;24CB
+24E6;CIRCLED LATIN SMALL LETTER W;So;0;L;<circle> 0077;;;;N;;;24CC;;24CC
+24E7;CIRCLED LATIN SMALL LETTER X;So;0;L;<circle> 0078;;;;N;;;24CD;;24CD
+24E8;CIRCLED LATIN SMALL LETTER Y;So;0;L;<circle> 0079;;;;N;;;24CE;;24CE
+24E9;CIRCLED LATIN SMALL LETTER Z;So;0;L;<circle> 007A;;;;N;;;24CF;;24CF
+24EA;CIRCLED DIGIT ZERO;No;0;EN;<circle> 0030;;0;0;N;;;;;
+2500;BOX DRAWINGS LIGHT HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT HORIZONTAL;;;;
+2501;BOX DRAWINGS HEAVY HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY HORIZONTAL;;;;
+2502;BOX DRAWINGS LIGHT VERTICAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL;;;;
+2503;BOX DRAWINGS HEAVY VERTICAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL;;;;
+2504;BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH HORIZONTAL;;;;
+2505;BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH HORIZONTAL;;;;
+2506;BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH VERTICAL;;;;
+2507;BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH VERTICAL;;;;
+2508;BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH HORIZONTAL;;;;
+2509;BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH HORIZONTAL;;;;
+250A;BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH VERTICAL;;;;
+250B;BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH VERTICAL;;;;
+250C;BOX DRAWINGS LIGHT DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND RIGHT;;;;
+250D;BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT HEAVY;;;;
+250E;BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT LIGHT;;;;
+250F;BOX DRAWINGS HEAVY DOWN AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND RIGHT;;;;
+2510;BOX DRAWINGS LIGHT DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND LEFT;;;;
+2511;BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT HEAVY;;;;
+2512;BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT LIGHT;;;;
+2513;BOX DRAWINGS HEAVY DOWN AND LEFT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND LEFT;;;;
+2514;BOX DRAWINGS LIGHT UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT UP AND RIGHT;;;;
+2515;BOX DRAWINGS UP LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT HEAVY;;;;
+2516;BOX DRAWINGS UP HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT LIGHT;;;;
+2517;BOX DRAWINGS HEAVY UP AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY UP AND RIGHT;;;;
+2518;BOX DRAWINGS LIGHT UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT UP AND LEFT;;;;
+2519;BOX DRAWINGS UP LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT HEAVY;;;;
+251A;BOX DRAWINGS UP HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT LIGHT;;;;
+251B;BOX DRAWINGS HEAVY UP AND LEFT;So;0;ON;;;;;N;FORMS HEAVY UP AND LEFT;;;;
+251C;BOX DRAWINGS LIGHT VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND RIGHT;;;;
+251D;BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND RIGHT HEAVY;;;;
+251E;BOX DRAWINGS UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT DOWN LIGHT;;;;
+251F;BOX DRAWINGS DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT UP LIGHT;;;;
+2520;BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND RIGHT LIGHT;;;;
+2521;BOX DRAWINGS DOWN LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT UP HEAVY;;;;
+2522;BOX DRAWINGS UP LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT DOWN HEAVY;;;;
+2523;BOX DRAWINGS HEAVY VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND RIGHT;;;;
+2524;BOX DRAWINGS LIGHT VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND LEFT;;;;
+2525;BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND LEFT HEAVY;;;;
+2526;BOX DRAWINGS UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT DOWN LIGHT;;;;
+2527;BOX DRAWINGS DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT UP LIGHT;;;;
+2528;BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND LEFT LIGHT;;;;
+2529;BOX DRAWINGS DOWN LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT UP HEAVY;;;;
+252A;BOX DRAWINGS UP LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT DOWN HEAVY;;;;
+252B;BOX DRAWINGS HEAVY VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND LEFT;;;;
+252C;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOWN AND HORIZONTAL;;;;
+252D;BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT DOWN LIGHT;;;;
+252E;BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT DOWN LIGHT;;;;
+252F;BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND HORIZONTAL HEAVY;;;;
+2530;BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND HORIZONTAL LIGHT;;;;
+2531;BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT DOWN HEAVY;;;;
+2532;BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT DOWN HEAVY;;;;
+2533;BOX DRAWINGS HEAVY DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOWN AND HORIZONTAL;;;;
+2534;BOX DRAWINGS LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT UP AND HORIZONTAL;;;;
+2535;BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT UP LIGHT;;;;
+2536;BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT UP LIGHT;;;;
+2537;BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND HORIZONTAL HEAVY;;;;
+2538;BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND HORIZONTAL LIGHT;;;;
+2539;BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT UP HEAVY;;;;
+253A;BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT UP HEAVY;;;;
+253B;BOX DRAWINGS HEAVY UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY UP AND HORIZONTAL;;;;
+253C;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND HORIZONTAL;;;;
+253D;BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT VERTICAL LIGHT;;;;
+253E;BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT VERTICAL LIGHT;;;;
+253F;BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND HORIZONTAL HEAVY;;;;
+2540;BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND DOWN HORIZONTAL LIGHT;;;;
+2541;BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND UP HORIZONTAL LIGHT;;;;
+2542;BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND HORIZONTAL LIGHT;;;;
+2543;BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT UP HEAVY AND RIGHT DOWN LIGHT;;;;
+2544;BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT UP HEAVY AND LEFT DOWN LIGHT;;;;
+2545;BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT DOWN HEAVY AND RIGHT UP LIGHT;;;;
+2546;BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT DOWN HEAVY AND LEFT UP LIGHT;;;;
+2547;BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND UP HORIZONTAL HEAVY;;;;
+2548;BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND DOWN HORIZONTAL HEAVY;;;;
+2549;BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT VERTICAL HEAVY;;;;
+254A;BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT VERTICAL HEAVY;;;;
+254B;BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND HORIZONTAL;;;;
+254C;BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH HORIZONTAL;;;;
+254D;BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH HORIZONTAL;;;;
+254E;BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH VERTICAL;;;;
+254F;BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH VERTICAL;;;;
+2550;BOX DRAWINGS DOUBLE HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE HORIZONTAL;;;;
+2551;BOX DRAWINGS DOUBLE VERTICAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL;;;;
+2552;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND RIGHT DOUBLE;;;;
+2553;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND RIGHT SINGLE;;;;
+2554;BOX DRAWINGS DOUBLE DOWN AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND RIGHT;;;;
+2555;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND LEFT DOUBLE;;;;
+2556;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND LEFT SINGLE;;;;
+2557;BOX DRAWINGS DOUBLE DOWN AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND LEFT;;;;
+2558;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND RIGHT DOUBLE;;;;
+2559;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND RIGHT SINGLE;;;;
+255A;BOX DRAWINGS DOUBLE UP AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE UP AND RIGHT;;;;
+255B;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND LEFT DOUBLE;;;;
+255C;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND LEFT SINGLE;;;;
+255D;BOX DRAWINGS DOUBLE UP AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE UP AND LEFT;;;;
+255E;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND RIGHT DOUBLE;;;;
+255F;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND RIGHT SINGLE;;;;
+2560;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND RIGHT;;;;
+2561;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND LEFT DOUBLE;;;;
+2562;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND LEFT SINGLE;;;;
+2563;BOX DRAWINGS DOUBLE VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND LEFT;;;;
+2564;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND HORIZONTAL DOUBLE;;;;
+2565;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND HORIZONTAL SINGLE;;;;
+2566;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND HORIZONTAL;;;;
+2567;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND HORIZONTAL DOUBLE;;;;
+2568;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND HORIZONTAL SINGLE;;;;
+2569;BOX DRAWINGS DOUBLE UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE UP AND HORIZONTAL;;;;
+256A;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND HORIZONTAL DOUBLE;;;;
+256B;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND HORIZONTAL SINGLE;;;;
+256C;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND HORIZONTAL;;;;
+256D;BOX DRAWINGS LIGHT ARC DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND RIGHT;;;;
+256E;BOX DRAWINGS LIGHT ARC DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND LEFT;;;;
+256F;BOX DRAWINGS LIGHT ARC UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND LEFT;;;;
+2570;BOX DRAWINGS LIGHT ARC UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND RIGHT;;;;
+2571;BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;;;;
+2572;BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;;;;
+2573;BOX DRAWINGS LIGHT DIAGONAL CROSS;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL CROSS;;;;
+2574;BOX DRAWINGS LIGHT LEFT;So;0;ON;;;;;N;FORMS LIGHT LEFT;;;;
+2575;BOX DRAWINGS LIGHT UP;So;0;ON;;;;;N;FORMS LIGHT UP;;;;
+2576;BOX DRAWINGS LIGHT RIGHT;So;0;ON;;;;;N;FORMS LIGHT RIGHT;;;;
+2577;BOX DRAWINGS LIGHT DOWN;So;0;ON;;;;;N;FORMS LIGHT DOWN;;;;
+2578;BOX DRAWINGS HEAVY LEFT;So;0;ON;;;;;N;FORMS HEAVY LEFT;;;;
+2579;BOX DRAWINGS HEAVY UP;So;0;ON;;;;;N;FORMS HEAVY UP;;;;
+257A;BOX DRAWINGS HEAVY RIGHT;So;0;ON;;;;;N;FORMS HEAVY RIGHT;;;;
+257B;BOX DRAWINGS HEAVY DOWN;So;0;ON;;;;;N;FORMS HEAVY DOWN;;;;
+257C;BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT;So;0;ON;;;;;N;FORMS LIGHT LEFT AND HEAVY RIGHT;;;;
+257D;BOX DRAWINGS LIGHT UP AND HEAVY DOWN;So;0;ON;;;;;N;FORMS LIGHT UP AND HEAVY DOWN;;;;
+257E;BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT;So;0;ON;;;;;N;FORMS HEAVY LEFT AND LIGHT RIGHT;;;;
+257F;BOX DRAWINGS HEAVY UP AND LIGHT DOWN;So;0;ON;;;;;N;FORMS HEAVY UP AND LIGHT DOWN;;;;
+2580;UPPER HALF BLOCK;So;0;ON;;;;;N;;;;;
+2581;LOWER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2582;LOWER ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;;
+2583;LOWER THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2584;LOWER HALF BLOCK;So;0;ON;;;;;N;;;;;
+2585;LOWER FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2586;LOWER THREE QUARTERS BLOCK;So;0;ON;;;;;N;LOWER THREE QUARTER BLOCK;;;;
+2587;LOWER SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2588;FULL BLOCK;So;0;ON;;;;;N;;;;;
+2589;LEFT SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258A;LEFT THREE QUARTERS BLOCK;So;0;ON;;;;;N;LEFT THREE QUARTER BLOCK;;;;
+258B;LEFT FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258C;LEFT HALF BLOCK;So;0;ON;;;;;N;;;;;
+258D;LEFT THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258E;LEFT ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;;
+258F;LEFT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2590;RIGHT HALF BLOCK;So;0;ON;;;;;N;;;;;
+2591;LIGHT SHADE;So;0;ON;;;;;N;;;;;
+2592;MEDIUM SHADE;So;0;ON;;;;;N;;;;;
+2593;DARK SHADE;So;0;ON;;;;;N;;;;;
+2594;UPPER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2595;RIGHT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+25A0;BLACK SQUARE;So;0;ON;;;;;N;;;;;
+25A1;WHITE SQUARE;So;0;ON;;;;;N;;;;;
+25A2;WHITE SQUARE WITH ROUNDED CORNERS;So;0;ON;;;;;N;;;;;
+25A3;WHITE SQUARE CONTAINING BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25A4;SQUARE WITH HORIZONTAL FILL;So;0;ON;;;;;N;;;;;
+25A5;SQUARE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;;
+25A6;SQUARE WITH ORTHOGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;;
+25A7;SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL;So;0;ON;;;;;N;;;;;
+25A8;SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL;So;0;ON;;;;;N;;;;;
+25A9;SQUARE WITH DIAGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;;
+25AA;BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25AB;WHITE SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25AC;BLACK RECTANGLE;So;0;ON;;;;;N;;;;;
+25AD;WHITE RECTANGLE;So;0;ON;;;;;N;;;;;
+25AE;BLACK VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;;
+25AF;WHITE VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;;
+25B0;BLACK PARALLELOGRAM;So;0;ON;;;;;N;;;;;
+25B1;WHITE PARALLELOGRAM;So;0;ON;;;;;N;;;;;
+25B2;BLACK UP-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING TRIANGLE;;;;
+25B3;WHITE UP-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE;;;;
+25B4;BLACK UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING SMALL TRIANGLE;;;;
+25B5;WHITE UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING SMALL TRIANGLE;;;;
+25B6;BLACK RIGHT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING TRIANGLE;;;;
+25B7;WHITE RIGHT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE RIGHT POINTING TRIANGLE;;;;
+25B8;BLACK RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING SMALL TRIANGLE;;;;
+25B9;WHITE RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE RIGHT POINTING SMALL TRIANGLE;;;;
+25BA;BLACK RIGHT-POINTING POINTER;So;0;ON;;;;;N;BLACK RIGHT POINTING POINTER;;;;
+25BB;WHITE RIGHT-POINTING POINTER;So;0;ON;;;;;N;WHITE RIGHT POINTING POINTER;;;;
+25BC;BLACK DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING TRIANGLE;;;;
+25BD;WHITE DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING TRIANGLE;;;;
+25BE;BLACK DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING SMALL TRIANGLE;;;;
+25BF;WHITE DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING SMALL TRIANGLE;;;;
+25C0;BLACK LEFT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING TRIANGLE;;;;
+25C1;WHITE LEFT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE LEFT POINTING TRIANGLE;;;;
+25C2;BLACK LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING SMALL TRIANGLE;;;;
+25C3;WHITE LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE LEFT POINTING SMALL TRIANGLE;;;;
+25C4;BLACK LEFT-POINTING POINTER;So;0;ON;;;;;N;BLACK LEFT POINTING POINTER;;;;
+25C5;WHITE LEFT-POINTING POINTER;So;0;ON;;;;;N;WHITE LEFT POINTING POINTER;;;;
+25C6;BLACK DIAMOND;So;0;ON;;;;;N;;;;;
+25C7;WHITE DIAMOND;So;0;ON;;;;;N;;;;;
+25C8;WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND;So;0;ON;;;;;N;;;;;
+25C9;FISHEYE;So;0;ON;;;;;N;;;;;
+25CA;LOZENGE;So;0;ON;;;;;N;;;;;
+25CB;WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25CC;DOTTED CIRCLE;So;0;ON;;;;;N;;;;;
+25CD;CIRCLE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;;
+25CE;BULLSEYE;So;0;ON;;;;;N;;;;;
+25CF;BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D0;CIRCLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;;
+25D1;CIRCLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;;
+25D2;CIRCLE WITH LOWER HALF BLACK;So;0;ON;;;;;N;;;;;
+25D3;CIRCLE WITH UPPER HALF BLACK;So;0;ON;;;;;N;;;;;
+25D4;CIRCLE WITH UPPER RIGHT QUADRANT BLACK;So;0;ON;;;;;N;;;;;
+25D5;CIRCLE WITH ALL BUT UPPER LEFT QUADRANT BLACK;So;0;ON;;;;;N;;;;;
+25D6;LEFT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D7;RIGHT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D8;INVERSE BULLET;So;0;ON;;;;;N;;;;;
+25D9;INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DA;UPPER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DB;LOWER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DC;UPPER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DD;UPPER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DE;LOWER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DF;LOWER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25E0;UPPER HALF CIRCLE;So;0;ON;;;;;N;;;;;
+25E1;LOWER HALF CIRCLE;So;0;ON;;;;;N;;;;;
+25E2;BLACK LOWER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E3;BLACK LOWER LEFT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E4;BLACK UPPER LEFT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E5;BLACK UPPER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E6;WHITE BULLET;So;0;ON;;;;;N;;;;;
+25E7;SQUARE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;;
+25E8;SQUARE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;;
+25E9;SQUARE WITH UPPER LEFT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;;
+25EA;SQUARE WITH LOWER RIGHT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;;
+25EB;WHITE SQUARE WITH VERTICAL BISECTING LINE;So;0;ON;;;;;N;;;;;
+25EC;WHITE UP-POINTING TRIANGLE WITH DOT;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE WITH DOT;;;;
+25ED;UP-POINTING TRIANGLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH LEFT HALF BLACK;;;;
+25EE;UP-POINTING TRIANGLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH RIGHT HALF BLACK;;;;
+25EF;LARGE CIRCLE;So;0;ON;;;;;N;;;;;
+25F0;WHITE SQUARE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F1;WHITE SQUARE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F2;WHITE SQUARE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F3;WHITE SQUARE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F4;WHITE CIRCLE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F5;WHITE CIRCLE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F6;WHITE CIRCLE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F7;WHITE CIRCLE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+2600;BLACK SUN WITH RAYS;So;0;ON;;;;;N;;;;;
+2601;CLOUD;So;0;ON;;;;;N;;;;;
+2602;UMBRELLA;So;0;ON;;;;;N;;;;;
+2603;SNOWMAN;So;0;ON;;;;;N;;;;;
+2604;COMET;So;0;ON;;;;;N;;;;;
+2605;BLACK STAR;So;0;ON;;;;;N;;;;;
+2606;WHITE STAR;So;0;ON;;;;;N;;;;;
+2607;LIGHTNING;So;0;ON;;;;;N;;;;;
+2608;THUNDERSTORM;So;0;ON;;;;;N;;;;;
+2609;SUN;So;0;ON;;;;;N;;;;;
+260A;ASCENDING NODE;So;0;ON;;;;;N;;;;;
+260B;DESCENDING NODE;So;0;ON;;;;;N;;;;;
+260C;CONJUNCTION;So;0;ON;;;;;N;;;;;
+260D;OPPOSITION;So;0;ON;;;;;N;;;;;
+260E;BLACK TELEPHONE;So;0;ON;;;;;N;;;;;
+260F;WHITE TELEPHONE;So;0;ON;;;;;N;;;;;
+2610;BALLOT BOX;So;0;ON;;;;;N;;;;;
+2611;BALLOT BOX WITH CHECK;So;0;ON;;;;;N;;;;;
+2612;BALLOT BOX WITH X;So;0;ON;;;;;N;;;;;
+2613;SALTIRE;So;0;ON;;;;;N;;;;;
+2619;REVERSED ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;;
+261A;BLACK LEFT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261B;BLACK RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261C;WHITE LEFT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261D;WHITE UP POINTING INDEX;So;0;ON;;;;;N;;;;;
+261E;WHITE RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261F;WHITE DOWN POINTING INDEX;So;0;ON;;;;;N;;;;;
+2620;SKULL AND CROSSBONES;So;0;ON;;;;;N;;;;;
+2621;CAUTION SIGN;So;0;ON;;;;;N;;;;;
+2622;RADIOACTIVE SIGN;So;0;ON;;;;;N;;;;;
+2623;BIOHAZARD SIGN;So;0;ON;;;;;N;;;;;
+2624;CADUCEUS;So;0;ON;;;;;N;;;;;
+2625;ANKH;So;0;ON;;;;;N;;;;;
+2626;ORTHODOX CROSS;So;0;ON;;;;;N;;;;;
+2627;CHI RHO;So;0;ON;;;;;N;;;;;
+2628;CROSS OF LORRAINE;So;0;ON;;;;;N;;;;;
+2629;CROSS OF JERUSALEM;So;0;ON;;;;;N;;;;;
+262A;STAR AND CRESCENT;So;0;ON;;;;;N;;;;;
+262B;FARSI SYMBOL;So;0;ON;;;;;N;SYMBOL OF IRAN;;;;
+262C;ADI SHAKTI;So;0;ON;;;;;N;;;;;
+262D;HAMMER AND SICKLE;So;0;ON;;;;;N;;;;;
+262E;PEACE SYMBOL;So;0;ON;;;;;N;;;;;
+262F;YIN YANG;So;0;ON;;;;;N;;;;;
+2630;TRIGRAM FOR HEAVEN;So;0;ON;;;;;N;;;;;
+2631;TRIGRAM FOR LAKE;So;0;ON;;;;;N;;;;;
+2632;TRIGRAM FOR FIRE;So;0;ON;;;;;N;;;;;
+2633;TRIGRAM FOR THUNDER;So;0;ON;;;;;N;;;;;
+2634;TRIGRAM FOR WIND;So;0;ON;;;;;N;;;;;
+2635;TRIGRAM FOR WATER;So;0;ON;;;;;N;;;;;
+2636;TRIGRAM FOR MOUNTAIN;So;0;ON;;;;;N;;;;;
+2637;TRIGRAM FOR EARTH;So;0;ON;;;;;N;;;;;
+2638;WHEEL OF DHARMA;So;0;ON;;;;;N;;;;;
+2639;WHITE FROWNING FACE;So;0;ON;;;;;N;;;;;
+263A;WHITE SMILING FACE;So;0;ON;;;;;N;;;;;
+263B;BLACK SMILING FACE;So;0;ON;;;;;N;;;;;
+263C;WHITE SUN WITH RAYS;So;0;ON;;;;;N;;;;;
+263D;FIRST QUARTER MOON;So;0;ON;;;;;N;;;;;
+263E;LAST QUARTER MOON;So;0;ON;;;;;N;;;;;
+263F;MERCURY;So;0;ON;;;;;N;;;;;
+2640;FEMALE SIGN;So;0;ON;;;;;N;;;;;
+2641;EARTH;So;0;ON;;;;;N;;;;;
+2642;MALE SIGN;So;0;ON;;;;;N;;;;;
+2643;JUPITER;So;0;ON;;;;;N;;;;;
+2644;SATURN;So;0;ON;;;;;N;;;;;
+2645;URANUS;So;0;ON;;;;;N;;;;;
+2646;NEPTUNE;So;0;ON;;;;;N;;;;;
+2647;PLUTO;So;0;ON;;;;;N;;;;;
+2648;ARIES;So;0;ON;;;;;N;;;;;
+2649;TAURUS;So;0;ON;;;;;N;;;;;
+264A;GEMINI;So;0;ON;;;;;N;;;;;
+264B;CANCER;So;0;ON;;;;;N;;;;;
+264C;LEO;So;0;ON;;;;;N;;;;;
+264D;VIRGO;So;0;ON;;;;;N;;;;;
+264E;LIBRA;So;0;ON;;;;;N;;;;;
+264F;SCORPIUS;So;0;ON;;;;;N;;;;;
+2650;SAGITTARIUS;So;0;ON;;;;;N;;;;;
+2651;CAPRICORN;So;0;ON;;;;;N;;;;;
+2652;AQUARIUS;So;0;ON;;;;;N;;;;;
+2653;PISCES;So;0;ON;;;;;N;;;;;
+2654;WHITE CHESS KING;So;0;ON;;;;;N;;;;;
+2655;WHITE CHESS QUEEN;So;0;ON;;;;;N;;;;;
+2656;WHITE CHESS ROOK;So;0;ON;;;;;N;;;;;
+2657;WHITE CHESS BISHOP;So;0;ON;;;;;N;;;;;
+2658;WHITE CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+2659;WHITE CHESS PAWN;So;0;ON;;;;;N;;;;;
+265A;BLACK CHESS KING;So;0;ON;;;;;N;;;;;
+265B;BLACK CHESS QUEEN;So;0;ON;;;;;N;;;;;
+265C;BLACK CHESS ROOK;So;0;ON;;;;;N;;;;;
+265D;BLACK CHESS BISHOP;So;0;ON;;;;;N;;;;;
+265E;BLACK CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+265F;BLACK CHESS PAWN;So;0;ON;;;;;N;;;;;
+2660;BLACK SPADE SUIT;So;0;ON;;;;;N;;;;;
+2661;WHITE HEART SUIT;So;0;ON;;;;;N;;;;;
+2662;WHITE DIAMOND SUIT;So;0;ON;;;;;N;;;;;
+2663;BLACK CLUB SUIT;So;0;ON;;;;;N;;;;;
+2664;WHITE SPADE SUIT;So;0;ON;;;;;N;;;;;
+2665;BLACK HEART SUIT;So;0;ON;;;;;N;;;;;
+2666;BLACK DIAMOND SUIT;So;0;ON;;;;;N;;;;;
+2667;WHITE CLUB SUIT;So;0;ON;;;;;N;;;;;
+2668;HOT SPRINGS;So;0;ON;;;;;N;;;;;
+2669;QUARTER NOTE;So;0;ON;;;;;N;;;;;
+266A;EIGHTH NOTE;So;0;ON;;;;;N;;;;;
+266B;BEAMED EIGHTH NOTES;So;0;ON;;;;;N;BARRED EIGHTH NOTES;;;;
+266C;BEAMED SIXTEENTH NOTES;So;0;ON;;;;;N;BARRED SIXTEENTH NOTES;;;;
+266D;MUSIC FLAT SIGN;So;0;ON;;;;;N;FLAT;;;;
+266E;MUSIC NATURAL SIGN;So;0;ON;;;;;N;NATURAL;;;;
+266F;MUSIC SHARP SIGN;Sm;0;ON;;;;;N;SHARP;;;;
+2670;WEST SYRIAC CROSS;So;0;ON;;;;;N;;;;;
+2671;EAST SYRIAC CROSS;So;0;ON;;;;;N;;;;;
+2701;UPPER BLADE SCISSORS;So;0;ON;;;;;N;;;;;
+2702;BLACK SCISSORS;So;0;ON;;;;;N;;;;;
+2703;LOWER BLADE SCISSORS;So;0;ON;;;;;N;;;;;
+2704;WHITE SCISSORS;So;0;ON;;;;;N;;;;;
+2706;TELEPHONE LOCATION SIGN;So;0;ON;;;;;N;;;;;
+2707;TAPE DRIVE;So;0;ON;;;;;N;;;;;
+2708;AIRPLANE;So;0;ON;;;;;N;;;;;
+2709;ENVELOPE;So;0;ON;;;;;N;;;;;
+270C;VICTORY HAND;So;0;ON;;;;;N;;;;;
+270D;WRITING HAND;So;0;ON;;;;;N;;;;;
+270E;LOWER RIGHT PENCIL;So;0;ON;;;;;N;;;;;
+270F;PENCIL;So;0;ON;;;;;N;;;;;
+2710;UPPER RIGHT PENCIL;So;0;ON;;;;;N;;;;;
+2711;WHITE NIB;So;0;ON;;;;;N;;;;;
+2712;BLACK NIB;So;0;ON;;;;;N;;;;;
+2713;CHECK MARK;So;0;ON;;;;;N;;;;;
+2714;HEAVY CHECK MARK;So;0;ON;;;;;N;;;;;
+2715;MULTIPLICATION X;So;0;ON;;;;;N;;;;;
+2716;HEAVY MULTIPLICATION X;So;0;ON;;;;;N;;;;;
+2717;BALLOT X;So;0;ON;;;;;N;;;;;
+2718;HEAVY BALLOT X;So;0;ON;;;;;N;;;;;
+2719;OUTLINED GREEK CROSS;So;0;ON;;;;;N;;;;;
+271A;HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;;
+271B;OPEN CENTRE CROSS;So;0;ON;;;;;N;OPEN CENTER CROSS;;;;
+271C;HEAVY OPEN CENTRE CROSS;So;0;ON;;;;;N;HEAVY OPEN CENTER CROSS;;;;
+271D;LATIN CROSS;So;0;ON;;;;;N;;;;;
+271E;SHADOWED WHITE LATIN CROSS;So;0;ON;;;;;N;;;;;
+271F;OUTLINED LATIN CROSS;So;0;ON;;;;;N;;;;;
+2720;MALTESE CROSS;So;0;ON;;;;;N;;;;;
+2721;STAR OF DAVID;So;0;ON;;;;;N;;;;;
+2722;FOUR TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2723;FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2724;HEAVY FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2725;FOUR CLUB-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2726;BLACK FOUR POINTED STAR;So;0;ON;;;;;N;;;;;
+2727;WHITE FOUR POINTED STAR;So;0;ON;;;;;N;;;;;
+2729;STRESS OUTLINED WHITE STAR;So;0;ON;;;;;N;;;;;
+272A;CIRCLED WHITE STAR;So;0;ON;;;;;N;;;;;
+272B;OPEN CENTRE BLACK STAR;So;0;ON;;;;;N;OPEN CENTER BLACK STAR;;;;
+272C;BLACK CENTRE WHITE STAR;So;0;ON;;;;;N;BLACK CENTER WHITE STAR;;;;
+272D;OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;;
+272E;HEAVY OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;;
+272F;PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+2730;SHADOWED WHITE STAR;So;0;ON;;;;;N;;;;;
+2731;HEAVY ASTERISK;So;0;ON;;;;;N;;;;;
+2732;OPEN CENTRE ASTERISK;So;0;ON;;;;;N;OPEN CENTER ASTERISK;;;;
+2733;EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2734;EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+2735;EIGHT POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+2736;SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+2737;EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;;
+2738;HEAVY EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;;
+2739;TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+273A;SIXTEEN POINTED ASTERISK;So;0;ON;;;;;N;;;;;
+273B;TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+273C;OPEN CENTRE TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;OPEN CENTER TEARDROP-SPOKED ASTERISK;;;;
+273D;HEAVY TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+273E;SIX PETALLED BLACK AND WHITE FLORETTE;So;0;ON;;;;;N;;;;;
+273F;BLACK FLORETTE;So;0;ON;;;;;N;;;;;
+2740;WHITE FLORETTE;So;0;ON;;;;;N;;;;;
+2741;EIGHT PETALLED OUTLINED BLACK FLORETTE;So;0;ON;;;;;N;;;;;
+2742;CIRCLED OPEN CENTRE EIGHT POINTED STAR;So;0;ON;;;;;N;CIRCLED OPEN CENTER EIGHT POINTED STAR;;;;
+2743;HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK;So;0;ON;;;;;N;;;;;
+2744;SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2745;TIGHT TRIFOLIATE SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2746;HEAVY CHEVRON SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2747;SPARKLE;So;0;ON;;;;;N;;;;;
+2748;HEAVY SPARKLE;So;0;ON;;;;;N;;;;;
+2749;BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+274A;EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;;
+274B;HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;;
+274D;SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+274F;LOWER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2750;UPPER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2751;LOWER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2752;UPPER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2756;BLACK DIAMOND MINUS WHITE X;So;0;ON;;;;;N;;;;;
+2758;LIGHT VERTICAL BAR;So;0;ON;;;;;N;;;;;
+2759;MEDIUM VERTICAL BAR;So;0;ON;;;;;N;;;;;
+275A;HEAVY VERTICAL BAR;So;0;ON;;;;;N;;;;;
+275B;HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275C;HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275D;HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275E;HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2761;CURVED STEM PARAGRAPH SIGN ORNAMENT;So;0;ON;;;;;N;;;;;
+2762;HEAVY EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2763;HEAVY HEART EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2764;HEAVY BLACK HEART;So;0;ON;;;;;N;;;;;
+2765;ROTATED HEAVY BLACK HEART BULLET;So;0;ON;;;;;N;;;;;
+2766;FLORAL HEART;So;0;ON;;;;;N;;;;;
+2767;ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;;
+2776;DINGBAT NEGATIVE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED DIGIT ONE;;;;
+2777;DINGBAT NEGATIVE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED DIGIT TWO;;;;
+2778;DINGBAT NEGATIVE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED DIGIT THREE;;;;
+2779;DINGBAT NEGATIVE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED DIGIT FOUR;;;;
+277A;DINGBAT NEGATIVE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED DIGIT FIVE;;;;
+277B;DINGBAT NEGATIVE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED DIGIT SIX;;;;
+277C;DINGBAT NEGATIVE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED DIGIT SEVEN;;;;
+277D;DINGBAT NEGATIVE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED DIGIT EIGHT;;;;
+277E;DINGBAT NEGATIVE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED DIGIT NINE;;;;
+277F;DINGBAT NEGATIVE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED NUMBER TEN;;;;
+2780;DINGBAT CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;CIRCLED SANS-SERIF DIGIT ONE;;;;
+2781;DINGBAT CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;CIRCLED SANS-SERIF DIGIT TWO;;;;
+2782;DINGBAT CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;CIRCLED SANS-SERIF DIGIT THREE;;;;
+2783;DINGBAT CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;CIRCLED SANS-SERIF DIGIT FOUR;;;;
+2784;DINGBAT CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;CIRCLED SANS-SERIF DIGIT FIVE;;;;
+2785;DINGBAT CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;CIRCLED SANS-SERIF DIGIT SIX;;;;
+2786;DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;CIRCLED SANS-SERIF DIGIT SEVEN;;;;
+2787;DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;CIRCLED SANS-SERIF DIGIT EIGHT;;;;
+2788;DINGBAT CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;CIRCLED SANS-SERIF DIGIT NINE;;;;
+2789;DINGBAT CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;CIRCLED SANS-SERIF NUMBER TEN;;;;
+278A;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED SANS-SERIF DIGIT ONE;;;;
+278B;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED SANS-SERIF DIGIT TWO;;;;
+278C;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED SANS-SERIF DIGIT THREE;;;;
+278D;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED SANS-SERIF DIGIT FOUR;;;;
+278E;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED SANS-SERIF DIGIT FIVE;;;;
+278F;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED SANS-SERIF DIGIT SIX;;;;
+2790;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED SANS-SERIF DIGIT SEVEN;;;;
+2791;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED SANS-SERIF DIGIT EIGHT;;;;
+2792;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED SANS-SERIF DIGIT NINE;;;;
+2793;DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED SANS-SERIF NUMBER TEN;;;;
+2794;HEAVY WIDE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WIDE-HEADED RIGHT ARROW;;;;
+2798;HEAVY SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT ARROW;;;;
+2799;HEAVY RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY RIGHT ARROW;;;;
+279A;HEAVY NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT ARROW;;;;
+279B;DRAFTING POINT RIGHTWARDS ARROW;So;0;ON;;;;;N;DRAFTING POINT RIGHT ARROW;;;;
+279C;HEAVY ROUND-TIPPED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY ROUND-TIPPED RIGHT ARROW;;;;
+279D;TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;TRIANGLE-HEADED RIGHT ARROW;;;;
+279E;HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TRIANGLE-HEADED RIGHT ARROW;;;;
+279F;DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;DASHED TRIANGLE-HEADED RIGHT ARROW;;;;
+27A0;HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY DASHED TRIANGLE-HEADED RIGHT ARROW;;;;
+27A1;BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK RIGHT ARROW;;;;
+27A2;THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D TOP-LIGHTED RIGHT ARROWHEAD;;;;
+27A3;THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D BOTTOM-LIGHTED RIGHT ARROWHEAD;;;;
+27A4;BLACK RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;BLACK RIGHT ARROWHEAD;;;;
+27A5;HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED DOWN AND RIGHT ARROW;;;;
+27A6;HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED UP AND RIGHT ARROW;;;;
+27A7;SQUAT BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;SQUAT BLACK RIGHT ARROW;;;;
+27A8;HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY CONCAVE-POINTED BLACK RIGHT ARROW;;;;
+27A9;RIGHT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;RIGHT-SHADED WHITE RIGHT ARROW;;;;
+27AA;LEFT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT-SHADED WHITE RIGHT ARROW;;;;
+27AB;BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;BACK-TILTED SHADOWED WHITE RIGHT ARROW;;;;
+27AC;FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;FRONT-TILTED SHADOWED WHITE RIGHT ARROW;;;;
+27AD;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27AE;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27AF;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27B1;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27B2;CIRCLED HEAVY WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;CIRCLED HEAVY WHITE RIGHT ARROW;;;;
+27B3;WHITE-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;WHITE-FEATHERED RIGHT ARROW;;;;
+27B4;BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED LOWER RIGHT ARROW;;;;
+27B5;BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK-FEATHERED RIGHT ARROW;;;;
+27B6;BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED UPPER RIGHT ARROW;;;;
+27B7;HEAVY BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED LOWER RIGHT ARROW;;;;
+27B8;HEAVY BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED RIGHT ARROW;;;;
+27B9;HEAVY BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED UPPER RIGHT ARROW;;;;
+27BA;TEARDROP-BARBED RIGHTWARDS ARROW;So;0;ON;;;;;N;TEARDROP-BARBED RIGHT ARROW;;;;
+27BB;HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TEARDROP-SHANKED RIGHT ARROW;;;;
+27BC;WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;WEDGE-TAILED RIGHT ARROW;;;;
+27BD;HEAVY WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WEDGE-TAILED RIGHT ARROW;;;;
+27BE;OPEN-OUTLINED RIGHTWARDS ARROW;So;0;ON;;;;;N;OPEN-OUTLINED RIGHT ARROW;;;;
+2800;BRAILLE PATTERN BLANK;So;0;ON;;;;;N;;;;;
+2801;BRAILLE PATTERN DOTS-1;So;0;ON;;;;;N;;;;;
+2802;BRAILLE PATTERN DOTS-2;So;0;ON;;;;;N;;;;;
+2803;BRAILLE PATTERN DOTS-12;So;0;ON;;;;;N;;;;;
+2804;BRAILLE PATTERN DOTS-3;So;0;ON;;;;;N;;;;;
+2805;BRAILLE PATTERN DOTS-13;So;0;ON;;;;;N;;;;;
+2806;BRAILLE PATTERN DOTS-23;So;0;ON;;;;;N;;;;;
+2807;BRAILLE PATTERN DOTS-123;So;0;ON;;;;;N;;;;;
+2808;BRAILLE PATTERN DOTS-4;So;0;ON;;;;;N;;;;;
+2809;BRAILLE PATTERN DOTS-14;So;0;ON;;;;;N;;;;;
+280A;BRAILLE PATTERN DOTS-24;So;0;ON;;;;;N;;;;;
+280B;BRAILLE PATTERN DOTS-124;So;0;ON;;;;;N;;;;;
+280C;BRAILLE PATTERN DOTS-34;So;0;ON;;;;;N;;;;;
+280D;BRAILLE PATTERN DOTS-134;So;0;ON;;;;;N;;;;;
+280E;BRAILLE PATTERN DOTS-234;So;0;ON;;;;;N;;;;;
+280F;BRAILLE PATTERN DOTS-1234;So;0;ON;;;;;N;;;;;
+2810;BRAILLE PATTERN DOTS-5;So;0;ON;;;;;N;;;;;
+2811;BRAILLE PATTERN DOTS-15;So;0;ON;;;;;N;;;;;
+2812;BRAILLE PATTERN DOTS-25;So;0;ON;;;;;N;;;;;
+2813;BRAILLE PATTERN DOTS-125;So;0;ON;;;;;N;;;;;
+2814;BRAILLE PATTERN DOTS-35;So;0;ON;;;;;N;;;;;
+2815;BRAILLE PATTERN DOTS-135;So;0;ON;;;;;N;;;;;
+2816;BRAILLE PATTERN DOTS-235;So;0;ON;;;;;N;;;;;
+2817;BRAILLE PATTERN DOTS-1235;So;0;ON;;;;;N;;;;;
+2818;BRAILLE PATTERN DOTS-45;So;0;ON;;;;;N;;;;;
+2819;BRAILLE PATTERN DOTS-145;So;0;ON;;;;;N;;;;;
+281A;BRAILLE PATTERN DOTS-245;So;0;ON;;;;;N;;;;;
+281B;BRAILLE PATTERN DOTS-1245;So;0;ON;;;;;N;;;;;
+281C;BRAILLE PATTERN DOTS-345;So;0;ON;;;;;N;;;;;
+281D;BRAILLE PATTERN DOTS-1345;So;0;ON;;;;;N;;;;;
+281E;BRAILLE PATTERN DOTS-2345;So;0;ON;;;;;N;;;;;
+281F;BRAILLE PATTERN DOTS-12345;So;0;ON;;;;;N;;;;;
+2820;BRAILLE PATTERN DOTS-6;So;0;ON;;;;;N;;;;;
+2821;BRAILLE PATTERN DOTS-16;So;0;ON;;;;;N;;;;;
+2822;BRAILLE PATTERN DOTS-26;So;0;ON;;;;;N;;;;;
+2823;BRAILLE PATTERN DOTS-126;So;0;ON;;;;;N;;;;;
+2824;BRAILLE PATTERN DOTS-36;So;0;ON;;;;;N;;;;;
+2825;BRAILLE PATTERN DOTS-136;So;0;ON;;;;;N;;;;;
+2826;BRAILLE PATTERN DOTS-236;So;0;ON;;;;;N;;;;;
+2827;BRAILLE PATTERN DOTS-1236;So;0;ON;;;;;N;;;;;
+2828;BRAILLE PATTERN DOTS-46;So;0;ON;;;;;N;;;;;
+2829;BRAILLE PATTERN DOTS-146;So;0;ON;;;;;N;;;;;
+282A;BRAILLE PATTERN DOTS-246;So;0;ON;;;;;N;;;;;
+282B;BRAILLE PATTERN DOTS-1246;So;0;ON;;;;;N;;;;;
+282C;BRAILLE PATTERN DOTS-346;So;0;ON;;;;;N;;;;;
+282D;BRAILLE PATTERN DOTS-1346;So;0;ON;;;;;N;;;;;
+282E;BRAILLE PATTERN DOTS-2346;So;0;ON;;;;;N;;;;;
+282F;BRAILLE PATTERN DOTS-12346;So;0;ON;;;;;N;;;;;
+2830;BRAILLE PATTERN DOTS-56;So;0;ON;;;;;N;;;;;
+2831;BRAILLE PATTERN DOTS-156;So;0;ON;;;;;N;;;;;
+2832;BRAILLE PATTERN DOTS-256;So;0;ON;;;;;N;;;;;
+2833;BRAILLE PATTERN DOTS-1256;So;0;ON;;;;;N;;;;;
+2834;BRAILLE PATTERN DOTS-356;So;0;ON;;;;;N;;;;;
+2835;BRAILLE PATTERN DOTS-1356;So;0;ON;;;;;N;;;;;
+2836;BRAILLE PATTERN DOTS-2356;So;0;ON;;;;;N;;;;;
+2837;BRAILLE PATTERN DOTS-12356;So;0;ON;;;;;N;;;;;
+2838;BRAILLE PATTERN DOTS-456;So;0;ON;;;;;N;;;;;
+2839;BRAILLE PATTERN DOTS-1456;So;0;ON;;;;;N;;;;;
+283A;BRAILLE PATTERN DOTS-2456;So;0;ON;;;;;N;;;;;
+283B;BRAILLE PATTERN DOTS-12456;So;0;ON;;;;;N;;;;;
+283C;BRAILLE PATTERN DOTS-3456;So;0;ON;;;;;N;;;;;
+283D;BRAILLE PATTERN DOTS-13456;So;0;ON;;;;;N;;;;;
+283E;BRAILLE PATTERN DOTS-23456;So;0;ON;;;;;N;;;;;
+283F;BRAILLE PATTERN DOTS-123456;So;0;ON;;;;;N;;;;;
+2840;BRAILLE PATTERN DOTS-7;So;0;ON;;;;;N;;;;;
+2841;BRAILLE PATTERN DOTS-17;So;0;ON;;;;;N;;;;;
+2842;BRAILLE PATTERN DOTS-27;So;0;ON;;;;;N;;;;;
+2843;BRAILLE PATTERN DOTS-127;So;0;ON;;;;;N;;;;;
+2844;BRAILLE PATTERN DOTS-37;So;0;ON;;;;;N;;;;;
+2845;BRAILLE PATTERN DOTS-137;So;0;ON;;;;;N;;;;;
+2846;BRAILLE PATTERN DOTS-237;So;0;ON;;;;;N;;;;;
+2847;BRAILLE PATTERN DOTS-1237;So;0;ON;;;;;N;;;;;
+2848;BRAILLE PATTERN DOTS-47;So;0;ON;;;;;N;;;;;
+2849;BRAILLE PATTERN DOTS-147;So;0;ON;;;;;N;;;;;
+284A;BRAILLE PATTERN DOTS-247;So;0;ON;;;;;N;;;;;
+284B;BRAILLE PATTERN DOTS-1247;So;0;ON;;;;;N;;;;;
+284C;BRAILLE PATTERN DOTS-347;So;0;ON;;;;;N;;;;;
+284D;BRAILLE PATTERN DOTS-1347;So;0;ON;;;;;N;;;;;
+284E;BRAILLE PATTERN DOTS-2347;So;0;ON;;;;;N;;;;;
+284F;BRAILLE PATTERN DOTS-12347;So;0;ON;;;;;N;;;;;
+2850;BRAILLE PATTERN DOTS-57;So;0;ON;;;;;N;;;;;
+2851;BRAILLE PATTERN DOTS-157;So;0;ON;;;;;N;;;;;
+2852;BRAILLE PATTERN DOTS-257;So;0;ON;;;;;N;;;;;
+2853;BRAILLE PATTERN DOTS-1257;So;0;ON;;;;;N;;;;;
+2854;BRAILLE PATTERN DOTS-357;So;0;ON;;;;;N;;;;;
+2855;BRAILLE PATTERN DOTS-1357;So;0;ON;;;;;N;;;;;
+2856;BRAILLE PATTERN DOTS-2357;So;0;ON;;;;;N;;;;;
+2857;BRAILLE PATTERN DOTS-12357;So;0;ON;;;;;N;;;;;
+2858;BRAILLE PATTERN DOTS-457;So;0;ON;;;;;N;;;;;
+2859;BRAILLE PATTERN DOTS-1457;So;0;ON;;;;;N;;;;;
+285A;BRAILLE PATTERN DOTS-2457;So;0;ON;;;;;N;;;;;
+285B;BRAILLE PATTERN DOTS-12457;So;0;ON;;;;;N;;;;;
+285C;BRAILLE PATTERN DOTS-3457;So;0;ON;;;;;N;;;;;
+285D;BRAILLE PATTERN DOTS-13457;So;0;ON;;;;;N;;;;;
+285E;BRAILLE PATTERN DOTS-23457;So;0;ON;;;;;N;;;;;
+285F;BRAILLE PATTERN DOTS-123457;So;0;ON;;;;;N;;;;;
+2860;BRAILLE PATTERN DOTS-67;So;0;ON;;;;;N;;;;;
+2861;BRAILLE PATTERN DOTS-167;So;0;ON;;;;;N;;;;;
+2862;BRAILLE PATTERN DOTS-267;So;0;ON;;;;;N;;;;;
+2863;BRAILLE PATTERN DOTS-1267;So;0;ON;;;;;N;;;;;
+2864;BRAILLE PATTERN DOTS-367;So;0;ON;;;;;N;;;;;
+2865;BRAILLE PATTERN DOTS-1367;So;0;ON;;;;;N;;;;;
+2866;BRAILLE PATTERN DOTS-2367;So;0;ON;;;;;N;;;;;
+2867;BRAILLE PATTERN DOTS-12367;So;0;ON;;;;;N;;;;;
+2868;BRAILLE PATTERN DOTS-467;So;0;ON;;;;;N;;;;;
+2869;BRAILLE PATTERN DOTS-1467;So;0;ON;;;;;N;;;;;
+286A;BRAILLE PATTERN DOTS-2467;So;0;ON;;;;;N;;;;;
+286B;BRAILLE PATTERN DOTS-12467;So;0;ON;;;;;N;;;;;
+286C;BRAILLE PATTERN DOTS-3467;So;0;ON;;;;;N;;;;;
+286D;BRAILLE PATTERN DOTS-13467;So;0;ON;;;;;N;;;;;
+286E;BRAILLE PATTERN DOTS-23467;So;0;ON;;;;;N;;;;;
+286F;BRAILLE PATTERN DOTS-123467;So;0;ON;;;;;N;;;;;
+2870;BRAILLE PATTERN DOTS-567;So;0;ON;;;;;N;;;;;
+2871;BRAILLE PATTERN DOTS-1567;So;0;ON;;;;;N;;;;;
+2872;BRAILLE PATTERN DOTS-2567;So;0;ON;;;;;N;;;;;
+2873;BRAILLE PATTERN DOTS-12567;So;0;ON;;;;;N;;;;;
+2874;BRAILLE PATTERN DOTS-3567;So;0;ON;;;;;N;;;;;
+2875;BRAILLE PATTERN DOTS-13567;So;0;ON;;;;;N;;;;;
+2876;BRAILLE PATTERN DOTS-23567;So;0;ON;;;;;N;;;;;
+2877;BRAILLE PATTERN DOTS-123567;So;0;ON;;;;;N;;;;;
+2878;BRAILLE PATTERN DOTS-4567;So;0;ON;;;;;N;;;;;
+2879;BRAILLE PATTERN DOTS-14567;So;0;ON;;;;;N;;;;;
+287A;BRAILLE PATTERN DOTS-24567;So;0;ON;;;;;N;;;;;
+287B;BRAILLE PATTERN DOTS-124567;So;0;ON;;;;;N;;;;;
+287C;BRAILLE PATTERN DOTS-34567;So;0;ON;;;;;N;;;;;
+287D;BRAILLE PATTERN DOTS-134567;So;0;ON;;;;;N;;;;;
+287E;BRAILLE PATTERN DOTS-234567;So;0;ON;;;;;N;;;;;
+287F;BRAILLE PATTERN DOTS-1234567;So;0;ON;;;;;N;;;;;
+2880;BRAILLE PATTERN DOTS-8;So;0;ON;;;;;N;;;;;
+2881;BRAILLE PATTERN DOTS-18;So;0;ON;;;;;N;;;;;
+2882;BRAILLE PATTERN DOTS-28;So;0;ON;;;;;N;;;;;
+2883;BRAILLE PATTERN DOTS-128;So;0;ON;;;;;N;;;;;
+2884;BRAILLE PATTERN DOTS-38;So;0;ON;;;;;N;;;;;
+2885;BRAILLE PATTERN DOTS-138;So;0;ON;;;;;N;;;;;
+2886;BRAILLE PATTERN DOTS-238;So;0;ON;;;;;N;;;;;
+2887;BRAILLE PATTERN DOTS-1238;So;0;ON;;;;;N;;;;;
+2888;BRAILLE PATTERN DOTS-48;So;0;ON;;;;;N;;;;;
+2889;BRAILLE PATTERN DOTS-148;So;0;ON;;;;;N;;;;;
+288A;BRAILLE PATTERN DOTS-248;So;0;ON;;;;;N;;;;;
+288B;BRAILLE PATTERN DOTS-1248;So;0;ON;;;;;N;;;;;
+288C;BRAILLE PATTERN DOTS-348;So;0;ON;;;;;N;;;;;
+288D;BRAILLE PATTERN DOTS-1348;So;0;ON;;;;;N;;;;;
+288E;BRAILLE PATTERN DOTS-2348;So;0;ON;;;;;N;;;;;
+288F;BRAILLE PATTERN DOTS-12348;So;0;ON;;;;;N;;;;;
+2890;BRAILLE PATTERN DOTS-58;So;0;ON;;;;;N;;;;;
+2891;BRAILLE PATTERN DOTS-158;So;0;ON;;;;;N;;;;;
+2892;BRAILLE PATTERN DOTS-258;So;0;ON;;;;;N;;;;;
+2893;BRAILLE PATTERN DOTS-1258;So;0;ON;;;;;N;;;;;
+2894;BRAILLE PATTERN DOTS-358;So;0;ON;;;;;N;;;;;
+2895;BRAILLE PATTERN DOTS-1358;So;0;ON;;;;;N;;;;;
+2896;BRAILLE PATTERN DOTS-2358;So;0;ON;;;;;N;;;;;
+2897;BRAILLE PATTERN DOTS-12358;So;0;ON;;;;;N;;;;;
+2898;BRAILLE PATTERN DOTS-458;So;0;ON;;;;;N;;;;;
+2899;BRAILLE PATTERN DOTS-1458;So;0;ON;;;;;N;;;;;
+289A;BRAILLE PATTERN DOTS-2458;So;0;ON;;;;;N;;;;;
+289B;BRAILLE PATTERN DOTS-12458;So;0;ON;;;;;N;;;;;
+289C;BRAILLE PATTERN DOTS-3458;So;0;ON;;;;;N;;;;;
+289D;BRAILLE PATTERN DOTS-13458;So;0;ON;;;;;N;;;;;
+289E;BRAILLE PATTERN DOTS-23458;So;0;ON;;;;;N;;;;;
+289F;BRAILLE PATTERN DOTS-123458;So;0;ON;;;;;N;;;;;
+28A0;BRAILLE PATTERN DOTS-68;So;0;ON;;;;;N;;;;;
+28A1;BRAILLE PATTERN DOTS-168;So;0;ON;;;;;N;;;;;
+28A2;BRAILLE PATTERN DOTS-268;So;0;ON;;;;;N;;;;;
+28A3;BRAILLE PATTERN DOTS-1268;So;0;ON;;;;;N;;;;;
+28A4;BRAILLE PATTERN DOTS-368;So;0;ON;;;;;N;;;;;
+28A5;BRAILLE PATTERN DOTS-1368;So;0;ON;;;;;N;;;;;
+28A6;BRAILLE PATTERN DOTS-2368;So;0;ON;;;;;N;;;;;
+28A7;BRAILLE PATTERN DOTS-12368;So;0;ON;;;;;N;;;;;
+28A8;BRAILLE PATTERN DOTS-468;So;0;ON;;;;;N;;;;;
+28A9;BRAILLE PATTERN DOTS-1468;So;0;ON;;;;;N;;;;;
+28AA;BRAILLE PATTERN DOTS-2468;So;0;ON;;;;;N;;;;;
+28AB;BRAILLE PATTERN DOTS-12468;So;0;ON;;;;;N;;;;;
+28AC;BRAILLE PATTERN DOTS-3468;So;0;ON;;;;;N;;;;;
+28AD;BRAILLE PATTERN DOTS-13468;So;0;ON;;;;;N;;;;;
+28AE;BRAILLE PATTERN DOTS-23468;So;0;ON;;;;;N;;;;;
+28AF;BRAILLE PATTERN DOTS-123468;So;0;ON;;;;;N;;;;;
+28B0;BRAILLE PATTERN DOTS-568;So;0;ON;;;;;N;;;;;
+28B1;BRAILLE PATTERN DOTS-1568;So;0;ON;;;;;N;;;;;
+28B2;BRAILLE PATTERN DOTS-2568;So;0;ON;;;;;N;;;;;
+28B3;BRAILLE PATTERN DOTS-12568;So;0;ON;;;;;N;;;;;
+28B4;BRAILLE PATTERN DOTS-3568;So;0;ON;;;;;N;;;;;
+28B5;BRAILLE PATTERN DOTS-13568;So;0;ON;;;;;N;;;;;
+28B6;BRAILLE PATTERN DOTS-23568;So;0;ON;;;;;N;;;;;
+28B7;BRAILLE PATTERN DOTS-123568;So;0;ON;;;;;N;;;;;
+28B8;BRAILLE PATTERN DOTS-4568;So;0;ON;;;;;N;;;;;
+28B9;BRAILLE PATTERN DOTS-14568;So;0;ON;;;;;N;;;;;
+28BA;BRAILLE PATTERN DOTS-24568;So;0;ON;;;;;N;;;;;
+28BB;BRAILLE PATTERN DOTS-124568;So;0;ON;;;;;N;;;;;
+28BC;BRAILLE PATTERN DOTS-34568;So;0;ON;;;;;N;;;;;
+28BD;BRAILLE PATTERN DOTS-134568;So;0;ON;;;;;N;;;;;
+28BE;BRAILLE PATTERN DOTS-234568;So;0;ON;;;;;N;;;;;
+28BF;BRAILLE PATTERN DOTS-1234568;So;0;ON;;;;;N;;;;;
+28C0;BRAILLE PATTERN DOTS-78;So;0;ON;;;;;N;;;;;
+28C1;BRAILLE PATTERN DOTS-178;So;0;ON;;;;;N;;;;;
+28C2;BRAILLE PATTERN DOTS-278;So;0;ON;;;;;N;;;;;
+28C3;BRAILLE PATTERN DOTS-1278;So;0;ON;;;;;N;;;;;
+28C4;BRAILLE PATTERN DOTS-378;So;0;ON;;;;;N;;;;;
+28C5;BRAILLE PATTERN DOTS-1378;So;0;ON;;;;;N;;;;;
+28C6;BRAILLE PATTERN DOTS-2378;So;0;ON;;;;;N;;;;;
+28C7;BRAILLE PATTERN DOTS-12378;So;0;ON;;;;;N;;;;;
+28C8;BRAILLE PATTERN DOTS-478;So;0;ON;;;;;N;;;;;
+28C9;BRAILLE PATTERN DOTS-1478;So;0;ON;;;;;N;;;;;
+28CA;BRAILLE PATTERN DOTS-2478;So;0;ON;;;;;N;;;;;
+28CB;BRAILLE PATTERN DOTS-12478;So;0;ON;;;;;N;;;;;
+28CC;BRAILLE PATTERN DOTS-3478;So;0;ON;;;;;N;;;;;
+28CD;BRAILLE PATTERN DOTS-13478;So;0;ON;;;;;N;;;;;
+28CE;BRAILLE PATTERN DOTS-23478;So;0;ON;;;;;N;;;;;
+28CF;BRAILLE PATTERN DOTS-123478;So;0;ON;;;;;N;;;;;
+28D0;BRAILLE PATTERN DOTS-578;So;0;ON;;;;;N;;;;;
+28D1;BRAILLE PATTERN DOTS-1578;So;0;ON;;;;;N;;;;;
+28D2;BRAILLE PATTERN DOTS-2578;So;0;ON;;;;;N;;;;;
+28D3;BRAILLE PATTERN DOTS-12578;So;0;ON;;;;;N;;;;;
+28D4;BRAILLE PATTERN DOTS-3578;So;0;ON;;;;;N;;;;;
+28D5;BRAILLE PATTERN DOTS-13578;So;0;ON;;;;;N;;;;;
+28D6;BRAILLE PATTERN DOTS-23578;So;0;ON;;;;;N;;;;;
+28D7;BRAILLE PATTERN DOTS-123578;So;0;ON;;;;;N;;;;;
+28D8;BRAILLE PATTERN DOTS-4578;So;0;ON;;;;;N;;;;;
+28D9;BRAILLE PATTERN DOTS-14578;So;0;ON;;;;;N;;;;;
+28DA;BRAILLE PATTERN DOTS-24578;So;0;ON;;;;;N;;;;;
+28DB;BRAILLE PATTERN DOTS-124578;So;0;ON;;;;;N;;;;;
+28DC;BRAILLE PATTERN DOTS-34578;So;0;ON;;;;;N;;;;;
+28DD;BRAILLE PATTERN DOTS-134578;So;0;ON;;;;;N;;;;;
+28DE;BRAILLE PATTERN DOTS-234578;So;0;ON;;;;;N;;;;;
+28DF;BRAILLE PATTERN DOTS-1234578;So;0;ON;;;;;N;;;;;
+28E0;BRAILLE PATTERN DOTS-678;So;0;ON;;;;;N;;;;;
+28E1;BRAILLE PATTERN DOTS-1678;So;0;ON;;;;;N;;;;;
+28E2;BRAILLE PATTERN DOTS-2678;So;0;ON;;;;;N;;;;;
+28E3;BRAILLE PATTERN DOTS-12678;So;0;ON;;;;;N;;;;;
+28E4;BRAILLE PATTERN DOTS-3678;So;0;ON;;;;;N;;;;;
+28E5;BRAILLE PATTERN DOTS-13678;So;0;ON;;;;;N;;;;;
+28E6;BRAILLE PATTERN DOTS-23678;So;0;ON;;;;;N;;;;;
+28E7;BRAILLE PATTERN DOTS-123678;So;0;ON;;;;;N;;;;;
+28E8;BRAILLE PATTERN DOTS-4678;So;0;ON;;;;;N;;;;;
+28E9;BRAILLE PATTERN DOTS-14678;So;0;ON;;;;;N;;;;;
+28EA;BRAILLE PATTERN DOTS-24678;So;0;ON;;;;;N;;;;;
+28EB;BRAILLE PATTERN DOTS-124678;So;0;ON;;;;;N;;;;;
+28EC;BRAILLE PATTERN DOTS-34678;So;0;ON;;;;;N;;;;;
+28ED;BRAILLE PATTERN DOTS-134678;So;0;ON;;;;;N;;;;;
+28EE;BRAILLE PATTERN DOTS-234678;So;0;ON;;;;;N;;;;;
+28EF;BRAILLE PATTERN DOTS-1234678;So;0;ON;;;;;N;;;;;
+28F0;BRAILLE PATTERN DOTS-5678;So;0;ON;;;;;N;;;;;
+28F1;BRAILLE PATTERN DOTS-15678;So;0;ON;;;;;N;;;;;
+28F2;BRAILLE PATTERN DOTS-25678;So;0;ON;;;;;N;;;;;
+28F3;BRAILLE PATTERN DOTS-125678;So;0;ON;;;;;N;;;;;
+28F4;BRAILLE PATTERN DOTS-35678;So;0;ON;;;;;N;;;;;
+28F5;BRAILLE PATTERN DOTS-135678;So;0;ON;;;;;N;;;;;
+28F6;BRAILLE PATTERN DOTS-235678;So;0;ON;;;;;N;;;;;
+28F7;BRAILLE PATTERN DOTS-1235678;So;0;ON;;;;;N;;;;;
+28F8;BRAILLE PATTERN DOTS-45678;So;0;ON;;;;;N;;;;;
+28F9;BRAILLE PATTERN DOTS-145678;So;0;ON;;;;;N;;;;;
+28FA;BRAILLE PATTERN DOTS-245678;So;0;ON;;;;;N;;;;;
+28FB;BRAILLE PATTERN DOTS-1245678;So;0;ON;;;;;N;;;;;
+28FC;BRAILLE PATTERN DOTS-345678;So;0;ON;;;;;N;;;;;
+28FD;BRAILLE PATTERN DOTS-1345678;So;0;ON;;;;;N;;;;;
+28FE;BRAILLE PATTERN DOTS-2345678;So;0;ON;;;;;N;;;;;
+28FF;BRAILLE PATTERN DOTS-12345678;So;0;ON;;;;;N;;;;;
+2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;;
+2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;;
+2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;;
+2E83;CJK RADICAL SECOND TWO;So;0;ON;;;;;N;;;;;
+2E84;CJK RADICAL SECOND THREE;So;0;ON;;;;;N;;;;;
+2E85;CJK RADICAL PERSON;So;0;ON;;;;;N;;;;;
+2E86;CJK RADICAL BOX;So;0;ON;;;;;N;;;;;
+2E87;CJK RADICAL TABLE;So;0;ON;;;;;N;;;;;
+2E88;CJK RADICAL KNIFE ONE;So;0;ON;;;;;N;;;;;
+2E89;CJK RADICAL KNIFE TWO;So;0;ON;;;;;N;;;;;
+2E8A;CJK RADICAL DIVINATION;So;0;ON;;;;;N;;;;;
+2E8B;CJK RADICAL SEAL;So;0;ON;;;;;N;;;;;
+2E8C;CJK RADICAL SMALL ONE;So;0;ON;;;;;N;;;;;
+2E8D;CJK RADICAL SMALL TWO;So;0;ON;;;;;N;;;;;
+2E8E;CJK RADICAL LAME ONE;So;0;ON;;;;;N;;;;;
+2E8F;CJK RADICAL LAME TWO;So;0;ON;;;;;N;;;;;
+2E90;CJK RADICAL LAME THREE;So;0;ON;;;;;N;;;;;
+2E91;CJK RADICAL LAME FOUR;So;0;ON;;;;;N;;;;;
+2E92;CJK RADICAL SNAKE;So;0;ON;;;;;N;;;;;
+2E93;CJK RADICAL THREAD;So;0;ON;;;;;N;;;;;
+2E94;CJK RADICAL SNOUT ONE;So;0;ON;;;;;N;;;;;
+2E95;CJK RADICAL SNOUT TWO;So;0;ON;;;;;N;;;;;
+2E96;CJK RADICAL HEART ONE;So;0;ON;;;;;N;;;;;
+2E97;CJK RADICAL HEART TWO;So;0;ON;;;;;N;;;;;
+2E98;CJK RADICAL HAND;So;0;ON;;;;;N;;;;;
+2E99;CJK RADICAL RAP;So;0;ON;;;;;N;;;;;
+2E9B;CJK RADICAL CHOKE;So;0;ON;;;;;N;;;;;
+2E9C;CJK RADICAL SUN;So;0;ON;;;;;N;;;;;
+2E9D;CJK RADICAL MOON;So;0;ON;;;;;N;;;;;
+2E9E;CJK RADICAL DEATH;So;0;ON;;;;;N;;;;;
+2E9F;CJK RADICAL MOTHER;So;0;ON;<compat> 6BCD;;;;N;;;;;
+2EA0;CJK RADICAL CIVILIAN;So;0;ON;;;;;N;;;;;
+2EA1;CJK RADICAL WATER ONE;So;0;ON;;;;;N;;;;;
+2EA2;CJK RADICAL WATER TWO;So;0;ON;;;;;N;;;;;
+2EA3;CJK RADICAL FIRE;So;0;ON;;;;;N;;;;;
+2EA4;CJK RADICAL PAW ONE;So;0;ON;;;;;N;;;;;
+2EA5;CJK RADICAL PAW TWO;So;0;ON;;;;;N;;;;;
+2EA6;CJK RADICAL SIMPLIFIED HALF TREE TRUNK;So;0;ON;;;;;N;;;;;
+2EA7;CJK RADICAL COW;So;0;ON;;;;;N;;;;;
+2EA8;CJK RADICAL DOG;So;0;ON;;;;;N;;;;;
+2EA9;CJK RADICAL JADE;So;0;ON;;;;;N;;;;;
+2EAA;CJK RADICAL BOLT OF CLOTH;So;0;ON;;;;;N;;;;;
+2EAB;CJK RADICAL EYE;So;0;ON;;;;;N;;;;;
+2EAC;CJK RADICAL SPIRIT ONE;So;0;ON;;;;;N;;;;;
+2EAD;CJK RADICAL SPIRIT TWO;So;0;ON;;;;;N;;;;;
+2EAE;CJK RADICAL BAMBOO;So;0;ON;;;;;N;;;;;
+2EAF;CJK RADICAL SILK;So;0;ON;;;;;N;;;;;
+2EB0;CJK RADICAL C-SIMPLIFIED SILK;So;0;ON;;;;;N;;;;;
+2EB1;CJK RADICAL NET ONE;So;0;ON;;;;;N;;;;;
+2EB2;CJK RADICAL NET TWO;So;0;ON;;;;;N;;;;;
+2EB3;CJK RADICAL NET THREE;So;0;ON;;;;;N;;;;;
+2EB4;CJK RADICAL NET FOUR;So;0;ON;;;;;N;;;;;
+2EB5;CJK RADICAL MESH;So;0;ON;;;;;N;;;;;
+2EB6;CJK RADICAL SHEEP;So;0;ON;;;;;N;;;;;
+2EB7;CJK RADICAL RAM;So;0;ON;;;;;N;;;;;
+2EB8;CJK RADICAL EWE;So;0;ON;;;;;N;;;;;
+2EB9;CJK RADICAL OLD;So;0;ON;;;;;N;;;;;
+2EBA;CJK RADICAL BRUSH ONE;So;0;ON;;;;;N;;;;;
+2EBB;CJK RADICAL BRUSH TWO;So;0;ON;;;;;N;;;;;
+2EBC;CJK RADICAL MEAT;So;0;ON;;;;;N;;;;;
+2EBD;CJK RADICAL MORTAR;So;0;ON;;;;;N;;;;;
+2EBE;CJK RADICAL GRASS ONE;So;0;ON;;;;;N;;;;;
+2EBF;CJK RADICAL GRASS TWO;So;0;ON;;;;;N;;;;;
+2EC0;CJK RADICAL GRASS THREE;So;0;ON;;;;;N;;;;;
+2EC1;CJK RADICAL TIGER;So;0;ON;;;;;N;;;;;
+2EC2;CJK RADICAL CLOTHES;So;0;ON;;;;;N;;;;;
+2EC3;CJK RADICAL WEST ONE;So;0;ON;;;;;N;;;;;
+2EC4;CJK RADICAL WEST TWO;So;0;ON;;;;;N;;;;;
+2EC5;CJK RADICAL C-SIMPLIFIED SEE;So;0;ON;;;;;N;;;;;
+2EC6;CJK RADICAL SIMPLIFIED HORN;So;0;ON;;;;;N;;;;;
+2EC7;CJK RADICAL HORN;So;0;ON;;;;;N;;;;;
+2EC8;CJK RADICAL C-SIMPLIFIED SPEECH;So;0;ON;;;;;N;;;;;
+2EC9;CJK RADICAL C-SIMPLIFIED SHELL;So;0;ON;;;;;N;;;;;
+2ECA;CJK RADICAL FOOT;So;0;ON;;;;;N;;;;;
+2ECB;CJK RADICAL C-SIMPLIFIED CART;So;0;ON;;;;;N;;;;;
+2ECC;CJK RADICAL SIMPLIFIED WALK;So;0;ON;;;;;N;;;;;
+2ECD;CJK RADICAL WALK ONE;So;0;ON;;;;;N;;;;;
+2ECE;CJK RADICAL WALK TWO;So;0;ON;;;;;N;;;;;
+2ECF;CJK RADICAL CITY;So;0;ON;;;;;N;;;;;
+2ED0;CJK RADICAL C-SIMPLIFIED GOLD;So;0;ON;;;;;N;;;;;
+2ED1;CJK RADICAL LONG ONE;So;0;ON;;;;;N;;;;;
+2ED2;CJK RADICAL LONG TWO;So;0;ON;;;;;N;;;;;
+2ED3;CJK RADICAL C-SIMPLIFIED LONG;So;0;ON;;;;;N;;;;;
+2ED4;CJK RADICAL C-SIMPLIFIED GATE;So;0;ON;;;;;N;;;;;
+2ED5;CJK RADICAL MOUND ONE;So;0;ON;;;;;N;;;;;
+2ED6;CJK RADICAL MOUND TWO;So;0;ON;;;;;N;;;;;
+2ED7;CJK RADICAL RAIN;So;0;ON;;;;;N;;;;;
+2ED8;CJK RADICAL BLUE;So;0;ON;;;;;N;;;;;
+2ED9;CJK RADICAL C-SIMPLIFIED TANNED LEATHER;So;0;ON;;;;;N;;;;;
+2EDA;CJK RADICAL C-SIMPLIFIED LEAF;So;0;ON;;;;;N;;;;;
+2EDB;CJK RADICAL C-SIMPLIFIED WIND;So;0;ON;;;;;N;;;;;
+2EDC;CJK RADICAL C-SIMPLIFIED FLY;So;0;ON;;;;;N;;;;;
+2EDD;CJK RADICAL EAT ONE;So;0;ON;;;;;N;;;;;
+2EDE;CJK RADICAL EAT TWO;So;0;ON;;;;;N;;;;;
+2EDF;CJK RADICAL EAT THREE;So;0;ON;;;;;N;;;;;
+2EE0;CJK RADICAL C-SIMPLIFIED EAT;So;0;ON;;;;;N;;;;;
+2EE1;CJK RADICAL HEAD;So;0;ON;;;;;N;;;;;
+2EE2;CJK RADICAL C-SIMPLIFIED HORSE;So;0;ON;;;;;N;;;;;
+2EE3;CJK RADICAL BONE;So;0;ON;;;;;N;;;;;
+2EE4;CJK RADICAL GHOST;So;0;ON;;;;;N;;;;;
+2EE5;CJK RADICAL C-SIMPLIFIED FISH;So;0;ON;;;;;N;;;;;
+2EE6;CJK RADICAL C-SIMPLIFIED BIRD;So;0;ON;;;;;N;;;;;
+2EE7;CJK RADICAL C-SIMPLIFIED SALT;So;0;ON;;;;;N;;;;;
+2EE8;CJK RADICAL SIMPLIFIED WHEAT;So;0;ON;;;;;N;;;;;
+2EE9;CJK RADICAL SIMPLIFIED YELLOW;So;0;ON;;;;;N;;;;;
+2EEA;CJK RADICAL C-SIMPLIFIED FROG;So;0;ON;;;;;N;;;;;
+2EEB;CJK RADICAL J-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;;
+2EEC;CJK RADICAL C-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;;
+2EED;CJK RADICAL J-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;;
+2EEE;CJK RADICAL C-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;;
+2EEF;CJK RADICAL J-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;;
+2EF0;CJK RADICAL C-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;;
+2EF1;CJK RADICAL TURTLE;So;0;ON;;;;;N;;;;;
+2EF2;CJK RADICAL J-SIMPLIFIED TURTLE;So;0;ON;;;;;N;;;;;
+2EF3;CJK RADICAL C-SIMPLIFIED TURTLE;So;0;ON;<compat> 9F9F;;;;N;;;;;
+2F00;KANGXI RADICAL ONE;So;0;ON;<compat> 4E00;;;;N;;;;;
+2F01;KANGXI RADICAL LINE;So;0;ON;<compat> 4E28;;;;N;;;;;
+2F02;KANGXI RADICAL DOT;So;0;ON;<compat> 4E36;;;;N;;;;;
+2F03;KANGXI RADICAL SLASH;So;0;ON;<compat> 4E3F;;;;N;;;;;
+2F04;KANGXI RADICAL SECOND;So;0;ON;<compat> 4E59;;;;N;;;;;
+2F05;KANGXI RADICAL HOOK;So;0;ON;<compat> 4E85;;;;N;;;;;
+2F06;KANGXI RADICAL TWO;So;0;ON;<compat> 4E8C;;;;N;;;;;
+2F07;KANGXI RADICAL LID;So;0;ON;<compat> 4EA0;;;;N;;;;;
+2F08;KANGXI RADICAL MAN;So;0;ON;<compat> 4EBA;;;;N;;;;;
+2F09;KANGXI RADICAL LEGS;So;0;ON;<compat> 513F;;;;N;;;;;
+2F0A;KANGXI RADICAL ENTER;So;0;ON;<compat> 5165;;;;N;;;;;
+2F0B;KANGXI RADICAL EIGHT;So;0;ON;<compat> 516B;;;;N;;;;;
+2F0C;KANGXI RADICAL DOWN BOX;So;0;ON;<compat> 5182;;;;N;;;;;
+2F0D;KANGXI RADICAL COVER;So;0;ON;<compat> 5196;;;;N;;;;;
+2F0E;KANGXI RADICAL ICE;So;0;ON;<compat> 51AB;;;;N;;;;;
+2F0F;KANGXI RADICAL TABLE;So;0;ON;<compat> 51E0;;;;N;;;;;
+2F10;KANGXI RADICAL OPEN BOX;So;0;ON;<compat> 51F5;;;;N;;;;;
+2F11;KANGXI RADICAL KNIFE;So;0;ON;<compat> 5200;;;;N;;;;;
+2F12;KANGXI RADICAL POWER;So;0;ON;<compat> 529B;;;;N;;;;;
+2F13;KANGXI RADICAL WRAP;So;0;ON;<compat> 52F9;;;;N;;;;;
+2F14;KANGXI RADICAL SPOON;So;0;ON;<compat> 5315;;;;N;;;;;
+2F15;KANGXI RADICAL RIGHT OPEN BOX;So;0;ON;<compat> 531A;;;;N;;;;;
+2F16;KANGXI RADICAL HIDING ENCLOSURE;So;0;ON;<compat> 5338;;;;N;;;;;
+2F17;KANGXI RADICAL TEN;So;0;ON;<compat> 5341;;;;N;;;;;
+2F18;KANGXI RADICAL DIVINATION;So;0;ON;<compat> 535C;;;;N;;;;;
+2F19;KANGXI RADICAL SEAL;So;0;ON;<compat> 5369;;;;N;;;;;
+2F1A;KANGXI RADICAL CLIFF;So;0;ON;<compat> 5382;;;;N;;;;;
+2F1B;KANGXI RADICAL PRIVATE;So;0;ON;<compat> 53B6;;;;N;;;;;
+2F1C;KANGXI RADICAL AGAIN;So;0;ON;<compat> 53C8;;;;N;;;;;
+2F1D;KANGXI RADICAL MOUTH;So;0;ON;<compat> 53E3;;;;N;;;;;
+2F1E;KANGXI RADICAL ENCLOSURE;So;0;ON;<compat> 56D7;;;;N;;;;;
+2F1F;KANGXI RADICAL EARTH;So;0;ON;<compat> 571F;;;;N;;;;;
+2F20;KANGXI RADICAL SCHOLAR;So;0;ON;<compat> 58EB;;;;N;;;;;
+2F21;KANGXI RADICAL GO;So;0;ON;<compat> 5902;;;;N;;;;;
+2F22;KANGXI RADICAL GO SLOWLY;So;0;ON;<compat> 590A;;;;N;;;;;
+2F23;KANGXI RADICAL EVENING;So;0;ON;<compat> 5915;;;;N;;;;;
+2F24;KANGXI RADICAL BIG;So;0;ON;<compat> 5927;;;;N;;;;;
+2F25;KANGXI RADICAL WOMAN;So;0;ON;<compat> 5973;;;;N;;;;;
+2F26;KANGXI RADICAL CHILD;So;0;ON;<compat> 5B50;;;;N;;;;;
+2F27;KANGXI RADICAL ROOF;So;0;ON;<compat> 5B80;;;;N;;;;;
+2F28;KANGXI RADICAL INCH;So;0;ON;<compat> 5BF8;;;;N;;;;;
+2F29;KANGXI RADICAL SMALL;So;0;ON;<compat> 5C0F;;;;N;;;;;
+2F2A;KANGXI RADICAL LAME;So;0;ON;<compat> 5C22;;;;N;;;;;
+2F2B;KANGXI RADICAL CORPSE;So;0;ON;<compat> 5C38;;;;N;;;;;
+2F2C;KANGXI RADICAL SPROUT;So;0;ON;<compat> 5C6E;;;;N;;;;;
+2F2D;KANGXI RADICAL MOUNTAIN;So;0;ON;<compat> 5C71;;;;N;;;;;
+2F2E;KANGXI RADICAL RIVER;So;0;ON;<compat> 5DDB;;;;N;;;;;
+2F2F;KANGXI RADICAL WORK;So;0;ON;<compat> 5DE5;;;;N;;;;;
+2F30;KANGXI RADICAL ONESELF;So;0;ON;<compat> 5DF1;;;;N;;;;;
+2F31;KANGXI RADICAL TURBAN;So;0;ON;<compat> 5DFE;;;;N;;;;;
+2F32;KANGXI RADICAL DRY;So;0;ON;<compat> 5E72;;;;N;;;;;
+2F33;KANGXI RADICAL SHORT THREAD;So;0;ON;<compat> 5E7A;;;;N;;;;;
+2F34;KANGXI RADICAL DOTTED CLIFF;So;0;ON;<compat> 5E7F;;;;N;;;;;
+2F35;KANGXI RADICAL LONG STRIDE;So;0;ON;<compat> 5EF4;;;;N;;;;;
+2F36;KANGXI RADICAL TWO HANDS;So;0;ON;<compat> 5EFE;;;;N;;;;;
+2F37;KANGXI RADICAL SHOOT;So;0;ON;<compat> 5F0B;;;;N;;;;;
+2F38;KANGXI RADICAL BOW;So;0;ON;<compat> 5F13;;;;N;;;;;
+2F39;KANGXI RADICAL SNOUT;So;0;ON;<compat> 5F50;;;;N;;;;;
+2F3A;KANGXI RADICAL BRISTLE;So;0;ON;<compat> 5F61;;;;N;;;;;
+2F3B;KANGXI RADICAL STEP;So;0;ON;<compat> 5F73;;;;N;;;;;
+2F3C;KANGXI RADICAL HEART;So;0;ON;<compat> 5FC3;;;;N;;;;;
+2F3D;KANGXI RADICAL HALBERD;So;0;ON;<compat> 6208;;;;N;;;;;
+2F3E;KANGXI RADICAL DOOR;So;0;ON;<compat> 6236;;;;N;;;;;
+2F3F;KANGXI RADICAL HAND;So;0;ON;<compat> 624B;;;;N;;;;;
+2F40;KANGXI RADICAL BRANCH;So;0;ON;<compat> 652F;;;;N;;;;;
+2F41;KANGXI RADICAL RAP;So;0;ON;<compat> 6534;;;;N;;;;;
+2F42;KANGXI RADICAL SCRIPT;So;0;ON;<compat> 6587;;;;N;;;;;
+2F43;KANGXI RADICAL DIPPER;So;0;ON;<compat> 6597;;;;N;;;;;
+2F44;KANGXI RADICAL AXE;So;0;ON;<compat> 65A4;;;;N;;;;;
+2F45;KANGXI RADICAL SQUARE;So;0;ON;<compat> 65B9;;;;N;;;;;
+2F46;KANGXI RADICAL NOT;So;0;ON;<compat> 65E0;;;;N;;;;;
+2F47;KANGXI RADICAL SUN;So;0;ON;<compat> 65E5;;;;N;;;;;
+2F48;KANGXI RADICAL SAY;So;0;ON;<compat> 66F0;;;;N;;;;;
+2F49;KANGXI RADICAL MOON;So;0;ON;<compat> 6708;;;;N;;;;;
+2F4A;KANGXI RADICAL TREE;So;0;ON;<compat> 6728;;;;N;;;;;
+2F4B;KANGXI RADICAL LACK;So;0;ON;<compat> 6B20;;;;N;;;;;
+2F4C;KANGXI RADICAL STOP;So;0;ON;<compat> 6B62;;;;N;;;;;
+2F4D;KANGXI RADICAL DEATH;So;0;ON;<compat> 6B79;;;;N;;;;;
+2F4E;KANGXI RADICAL WEAPON;So;0;ON;<compat> 6BB3;;;;N;;;;;
+2F4F;KANGXI RADICAL DO NOT;So;0;ON;<compat> 6BCB;;;;N;;;;;
+2F50;KANGXI RADICAL COMPARE;So;0;ON;<compat> 6BD4;;;;N;;;;;
+2F51;KANGXI RADICAL FUR;So;0;ON;<compat> 6BDB;;;;N;;;;;
+2F52;KANGXI RADICAL CLAN;So;0;ON;<compat> 6C0F;;;;N;;;;;
+2F53;KANGXI RADICAL STEAM;So;0;ON;<compat> 6C14;;;;N;;;;;
+2F54;KANGXI RADICAL WATER;So;0;ON;<compat> 6C34;;;;N;;;;;
+2F55;KANGXI RADICAL FIRE;So;0;ON;<compat> 706B;;;;N;;;;;
+2F56;KANGXI RADICAL CLAW;So;0;ON;<compat> 722A;;;;N;;;;;
+2F57;KANGXI RADICAL FATHER;So;0;ON;<compat> 7236;;;;N;;;;;
+2F58;KANGXI RADICAL DOUBLE X;So;0;ON;<compat> 723B;;;;N;;;;;
+2F59;KANGXI RADICAL HALF TREE TRUNK;So;0;ON;<compat> 723F;;;;N;;;;;
+2F5A;KANGXI RADICAL SLICE;So;0;ON;<compat> 7247;;;;N;;;;;
+2F5B;KANGXI RADICAL FANG;So;0;ON;<compat> 7259;;;;N;;;;;
+2F5C;KANGXI RADICAL COW;So;0;ON;<compat> 725B;;;;N;;;;;
+2F5D;KANGXI RADICAL DOG;So;0;ON;<compat> 72AC;;;;N;;;;;
+2F5E;KANGXI RADICAL PROFOUND;So;0;ON;<compat> 7384;;;;N;;;;;
+2F5F;KANGXI RADICAL JADE;So;0;ON;<compat> 7389;;;;N;;;;;
+2F60;KANGXI RADICAL MELON;So;0;ON;<compat> 74DC;;;;N;;;;;
+2F61;KANGXI RADICAL TILE;So;0;ON;<compat> 74E6;;;;N;;;;;
+2F62;KANGXI RADICAL SWEET;So;0;ON;<compat> 7518;;;;N;;;;;
+2F63;KANGXI RADICAL LIFE;So;0;ON;<compat> 751F;;;;N;;;;;
+2F64;KANGXI RADICAL USE;So;0;ON;<compat> 7528;;;;N;;;;;
+2F65;KANGXI RADICAL FIELD;So;0;ON;<compat> 7530;;;;N;;;;;
+2F66;KANGXI RADICAL BOLT OF CLOTH;So;0;ON;<compat> 758B;;;;N;;;;;
+2F67;KANGXI RADICAL SICKNESS;So;0;ON;<compat> 7592;;;;N;;;;;
+2F68;KANGXI RADICAL DOTTED TENT;So;0;ON;<compat> 7676;;;;N;;;;;
+2F69;KANGXI RADICAL WHITE;So;0;ON;<compat> 767D;;;;N;;;;;
+2F6A;KANGXI RADICAL SKIN;So;0;ON;<compat> 76AE;;;;N;;;;;
+2F6B;KANGXI RADICAL DISH;So;0;ON;<compat> 76BF;;;;N;;;;;
+2F6C;KANGXI RADICAL EYE;So;0;ON;<compat> 76EE;;;;N;;;;;
+2F6D;KANGXI RADICAL SPEAR;So;0;ON;<compat> 77DB;;;;N;;;;;
+2F6E;KANGXI RADICAL ARROW;So;0;ON;<compat> 77E2;;;;N;;;;;
+2F6F;KANGXI RADICAL STONE;So;0;ON;<compat> 77F3;;;;N;;;;;
+2F70;KANGXI RADICAL SPIRIT;So;0;ON;<compat> 793A;;;;N;;;;;
+2F71;KANGXI RADICAL TRACK;So;0;ON;<compat> 79B8;;;;N;;;;;
+2F72;KANGXI RADICAL GRAIN;So;0;ON;<compat> 79BE;;;;N;;;;;
+2F73;KANGXI RADICAL CAVE;So;0;ON;<compat> 7A74;;;;N;;;;;
+2F74;KANGXI RADICAL STAND;So;0;ON;<compat> 7ACB;;;;N;;;;;
+2F75;KANGXI RADICAL BAMBOO;So;0;ON;<compat> 7AF9;;;;N;;;;;
+2F76;KANGXI RADICAL RICE;So;0;ON;<compat> 7C73;;;;N;;;;;
+2F77;KANGXI RADICAL SILK;So;0;ON;<compat> 7CF8;;;;N;;;;;
+2F78;KANGXI RADICAL JAR;So;0;ON;<compat> 7F36;;;;N;;;;;
+2F79;KANGXI RADICAL NET;So;0;ON;<compat> 7F51;;;;N;;;;;
+2F7A;KANGXI RADICAL SHEEP;So;0;ON;<compat> 7F8A;;;;N;;;;;
+2F7B;KANGXI RADICAL FEATHER;So;0;ON;<compat> 7FBD;;;;N;;;;;
+2F7C;KANGXI RADICAL OLD;So;0;ON;<compat> 8001;;;;N;;;;;
+2F7D;KANGXI RADICAL AND;So;0;ON;<compat> 800C;;;;N;;;;;
+2F7E;KANGXI RADICAL PLOW;So;0;ON;<compat> 8012;;;;N;;;;;
+2F7F;KANGXI RADICAL EAR;So;0;ON;<compat> 8033;;;;N;;;;;
+2F80;KANGXI RADICAL BRUSH;So;0;ON;<compat> 807F;;;;N;;;;;
+2F81;KANGXI RADICAL MEAT;So;0;ON;<compat> 8089;;;;N;;;;;
+2F82;KANGXI RADICAL MINISTER;So;0;ON;<compat> 81E3;;;;N;;;;;
+2F83;KANGXI RADICAL SELF;So;0;ON;<compat> 81EA;;;;N;;;;;
+2F84;KANGXI RADICAL ARRIVE;So;0;ON;<compat> 81F3;;;;N;;;;;
+2F85;KANGXI RADICAL MORTAR;So;0;ON;<compat> 81FC;;;;N;;;;;
+2F86;KANGXI RADICAL TONGUE;So;0;ON;<compat> 820C;;;;N;;;;;
+2F87;KANGXI RADICAL OPPOSE;So;0;ON;<compat> 821B;;;;N;;;;;
+2F88;KANGXI RADICAL BOAT;So;0;ON;<compat> 821F;;;;N;;;;;
+2F89;KANGXI RADICAL STOPPING;So;0;ON;<compat> 826E;;;;N;;;;;
+2F8A;KANGXI RADICAL COLOR;So;0;ON;<compat> 8272;;;;N;;;;;
+2F8B;KANGXI RADICAL GRASS;So;0;ON;<compat> 8278;;;;N;;;;;
+2F8C;KANGXI RADICAL TIGER;So;0;ON;<compat> 864D;;;;N;;;;;
+2F8D;KANGXI RADICAL INSECT;So;0;ON;<compat> 866B;;;;N;;;;;
+2F8E;KANGXI RADICAL BLOOD;So;0;ON;<compat> 8840;;;;N;;;;;
+2F8F;KANGXI RADICAL WALK ENCLOSURE;So;0;ON;<compat> 884C;;;;N;;;;;
+2F90;KANGXI RADICAL CLOTHES;So;0;ON;<compat> 8863;;;;N;;;;;
+2F91;KANGXI RADICAL WEST;So;0;ON;<compat> 897E;;;;N;;;;;
+2F92;KANGXI RADICAL SEE;So;0;ON;<compat> 898B;;;;N;;;;;
+2F93;KANGXI RADICAL HORN;So;0;ON;<compat> 89D2;;;;N;;;;;
+2F94;KANGXI RADICAL SPEECH;So;0;ON;<compat> 8A00;;;;N;;;;;
+2F95;KANGXI RADICAL VALLEY;So;0;ON;<compat> 8C37;;;;N;;;;;
+2F96;KANGXI RADICAL BEAN;So;0;ON;<compat> 8C46;;;;N;;;;;
+2F97;KANGXI RADICAL PIG;So;0;ON;<compat> 8C55;;;;N;;;;;
+2F98;KANGXI RADICAL BADGER;So;0;ON;<compat> 8C78;;;;N;;;;;
+2F99;KANGXI RADICAL SHELL;So;0;ON;<compat> 8C9D;;;;N;;;;;
+2F9A;KANGXI RADICAL RED;So;0;ON;<compat> 8D64;;;;N;;;;;
+2F9B;KANGXI RADICAL RUN;So;0;ON;<compat> 8D70;;;;N;;;;;
+2F9C;KANGXI RADICAL FOOT;So;0;ON;<compat> 8DB3;;;;N;;;;;
+2F9D;KANGXI RADICAL BODY;So;0;ON;<compat> 8EAB;;;;N;;;;;
+2F9E;KANGXI RADICAL CART;So;0;ON;<compat> 8ECA;;;;N;;;;;
+2F9F;KANGXI RADICAL BITTER;So;0;ON;<compat> 8F9B;;;;N;;;;;
+2FA0;KANGXI RADICAL MORNING;So;0;ON;<compat> 8FB0;;;;N;;;;;
+2FA1;KANGXI RADICAL WALK;So;0;ON;<compat> 8FB5;;;;N;;;;;
+2FA2;KANGXI RADICAL CITY;So;0;ON;<compat> 9091;;;;N;;;;;
+2FA3;KANGXI RADICAL WINE;So;0;ON;<compat> 9149;;;;N;;;;;
+2FA4;KANGXI RADICAL DISTINGUISH;So;0;ON;<compat> 91C6;;;;N;;;;;
+2FA5;KANGXI RADICAL VILLAGE;So;0;ON;<compat> 91CC;;;;N;;;;;
+2FA6;KANGXI RADICAL GOLD;So;0;ON;<compat> 91D1;;;;N;;;;;
+2FA7;KANGXI RADICAL LONG;So;0;ON;<compat> 9577;;;;N;;;;;
+2FA8;KANGXI RADICAL GATE;So;0;ON;<compat> 9580;;;;N;;;;;
+2FA9;KANGXI RADICAL MOUND;So;0;ON;<compat> 961C;;;;N;;;;;
+2FAA;KANGXI RADICAL SLAVE;So;0;ON;<compat> 96B6;;;;N;;;;;
+2FAB;KANGXI RADICAL SHORT TAILED BIRD;So;0;ON;<compat> 96B9;;;;N;;;;;
+2FAC;KANGXI RADICAL RAIN;So;0;ON;<compat> 96E8;;;;N;;;;;
+2FAD;KANGXI RADICAL BLUE;So;0;ON;<compat> 9751;;;;N;;;;;
+2FAE;KANGXI RADICAL WRONG;So;0;ON;<compat> 975E;;;;N;;;;;
+2FAF;KANGXI RADICAL FACE;So;0;ON;<compat> 9762;;;;N;;;;;
+2FB0;KANGXI RADICAL LEATHER;So;0;ON;<compat> 9769;;;;N;;;;;
+2FB1;KANGXI RADICAL TANNED LEATHER;So;0;ON;<compat> 97CB;;;;N;;;;;
+2FB2;KANGXI RADICAL LEEK;So;0;ON;<compat> 97ED;;;;N;;;;;
+2FB3;KANGXI RADICAL SOUND;So;0;ON;<compat> 97F3;;;;N;;;;;
+2FB4;KANGXI RADICAL LEAF;So;0;ON;<compat> 9801;;;;N;;;;;
+2FB5;KANGXI RADICAL WIND;So;0;ON;<compat> 98A8;;;;N;;;;;
+2FB6;KANGXI RADICAL FLY;So;0;ON;<compat> 98DB;;;;N;;;;;
+2FB7;KANGXI RADICAL EAT;So;0;ON;<compat> 98DF;;;;N;;;;;
+2FB8;KANGXI RADICAL HEAD;So;0;ON;<compat> 9996;;;;N;;;;;
+2FB9;KANGXI RADICAL FRAGRANT;So;0;ON;<compat> 9999;;;;N;;;;;
+2FBA;KANGXI RADICAL HORSE;So;0;ON;<compat> 99AC;;;;N;;;;;
+2FBB;KANGXI RADICAL BONE;So;0;ON;<compat> 9AA8;;;;N;;;;;
+2FBC;KANGXI RADICAL TALL;So;0;ON;<compat> 9AD8;;;;N;;;;;
+2FBD;KANGXI RADICAL HAIR;So;0;ON;<compat> 9ADF;;;;N;;;;;
+2FBE;KANGXI RADICAL FIGHT;So;0;ON;<compat> 9B25;;;;N;;;;;
+2FBF;KANGXI RADICAL SACRIFICIAL WINE;So;0;ON;<compat> 9B2F;;;;N;;;;;
+2FC0;KANGXI RADICAL CAULDRON;So;0;ON;<compat> 9B32;;;;N;;;;;
+2FC1;KANGXI RADICAL GHOST;So;0;ON;<compat> 9B3C;;;;N;;;;;
+2FC2;KANGXI RADICAL FISH;So;0;ON;<compat> 9B5A;;;;N;;;;;
+2FC3;KANGXI RADICAL BIRD;So;0;ON;<compat> 9CE5;;;;N;;;;;
+2FC4;KANGXI RADICAL SALT;So;0;ON;<compat> 9E75;;;;N;;;;;
+2FC5;KANGXI RADICAL DEER;So;0;ON;<compat> 9E7F;;;;N;;;;;
+2FC6;KANGXI RADICAL WHEAT;So;0;ON;<compat> 9EA5;;;;N;;;;;
+2FC7;KANGXI RADICAL HEMP;So;0;ON;<compat> 9EBB;;;;N;;;;;
+2FC8;KANGXI RADICAL YELLOW;So;0;ON;<compat> 9EC3;;;;N;;;;;
+2FC9;KANGXI RADICAL MILLET;So;0;ON;<compat> 9ECD;;;;N;;;;;
+2FCA;KANGXI RADICAL BLACK;So;0;ON;<compat> 9ED1;;;;N;;;;;
+2FCB;KANGXI RADICAL EMBROIDERY;So;0;ON;<compat> 9EF9;;;;N;;;;;
+2FCC;KANGXI RADICAL FROG;So;0;ON;<compat> 9EFD;;;;N;;;;;
+2FCD;KANGXI RADICAL TRIPOD;So;0;ON;<compat> 9F0E;;;;N;;;;;
+2FCE;KANGXI RADICAL DRUM;So;0;ON;<compat> 9F13;;;;N;;;;;
+2FCF;KANGXI RADICAL RAT;So;0;ON;<compat> 9F20;;;;N;;;;;
+2FD0;KANGXI RADICAL NOSE;So;0;ON;<compat> 9F3B;;;;N;;;;;
+2FD1;KANGXI RADICAL EVEN;So;0;ON;<compat> 9F4A;;;;N;;;;;
+2FD2;KANGXI RADICAL TOOTH;So;0;ON;<compat> 9F52;;;;N;;;;;
+2FD3;KANGXI RADICAL DRAGON;So;0;ON;<compat> 9F8D;;;;N;;;;;
+2FD4;KANGXI RADICAL TURTLE;So;0;ON;<compat> 9F9C;;;;N;;;;;
+2FD5;KANGXI RADICAL FLUTE;So;0;ON;<compat> 9FA0;;;;N;;;;;
+2FF0;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT;So;0;ON;;;;;N;;;;;
+2FF1;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO BELOW;So;0;ON;;;;;N;;;;;
+2FF2;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO MIDDLE AND RIGHT;So;0;ON;;;;;N;;;;;
+2FF3;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO MIDDLE AND BELOW;So;0;ON;;;;;N;;;;;
+2FF4;IDEOGRAPHIC DESCRIPTION CHARACTER FULL SURROUND;So;0;ON;;;;;N;;;;;
+2FF5;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM ABOVE;So;0;ON;;;;;N;;;;;
+2FF6;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM BELOW;So;0;ON;;;;;N;;;;;
+2FF7;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LEFT;So;0;ON;;;;;N;;;;;
+2FF8;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER LEFT;So;0;ON;;;;;N;;;;;
+2FF9;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER RIGHT;So;0;ON;;;;;N;;;;;
+2FFA;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER LEFT;So;0;ON;;;;;N;;;;;
+2FFB;IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID;So;0;ON;;;;;N;;;;;
+3000;IDEOGRAPHIC SPACE;Zs;0;WS;<wide> 0020;;;;N;;;;;
+3001;IDEOGRAPHIC COMMA;Po;0;ON;;;;;N;;;;;
+3002;IDEOGRAPHIC FULL STOP;Po;0;ON;;;;;N;IDEOGRAPHIC PERIOD;;;;
+3003;DITTO MARK;Po;0;ON;;;;;N;;;;;
+3004;JAPANESE INDUSTRIAL STANDARD SYMBOL;So;0;ON;;;;;N;;;;;
+3005;IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;;
+3006;IDEOGRAPHIC CLOSING MARK;Lo;0;L;;;;;N;;;;;
+3007;IDEOGRAPHIC NUMBER ZERO;Nl;0;L;;;;0;N;;;;;
+3008;LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING ANGLE BRACKET;;;;
+3009;RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING ANGLE BRACKET;;;;
+300A;LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING DOUBLE ANGLE BRACKET;;;;
+300B;RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING DOUBLE ANGLE BRACKET;;;;
+300C;LEFT CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING CORNER BRACKET;;;;
+300D;RIGHT CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING CORNER BRACKET;;;;
+300E;LEFT WHITE CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE CORNER BRACKET;;;;
+300F;RIGHT WHITE CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE CORNER BRACKET;;;;
+3010;LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING BLACK LENTICULAR BRACKET;;;;
+3011;RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING BLACK LENTICULAR BRACKET;;;;
+3012;POSTAL MARK;So;0;ON;;;;;N;;;;;
+3013;GETA MARK;So;0;ON;;;;;N;;;;;
+3014;LEFT TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING TORTOISE SHELL BRACKET;;;;
+3015;RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING TORTOISE SHELL BRACKET;;;;
+3016;LEFT WHITE LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE LENTICULAR BRACKET;;;;
+3017;RIGHT WHITE LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE LENTICULAR BRACKET;;;;
+3018;LEFT WHITE TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE TORTOISE SHELL BRACKET;;;;
+3019;RIGHT WHITE TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE TORTOISE SHELL BRACKET;;;;
+301A;LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE SQUARE BRACKET;;;;
+301B;RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE SQUARE BRACKET;;;;
+301C;WAVE DASH;Pd;0;ON;;;;;N;;;;;
+301D;REVERSED DOUBLE PRIME QUOTATION MARK;Ps;0;ON;;;;;N;;;;;
+301E;DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;;
+301F;LOW DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;;
+3020;POSTAL MARK FACE;So;0;ON;;;;;N;;;;;
+3021;HANGZHOU NUMERAL ONE;Nl;0;L;;;;1;N;;;;;
+3022;HANGZHOU NUMERAL TWO;Nl;0;L;;;;2;N;;;;;
+3023;HANGZHOU NUMERAL THREE;Nl;0;L;;;;3;N;;;;;
+3024;HANGZHOU NUMERAL FOUR;Nl;0;L;;;;4;N;;;;;
+3025;HANGZHOU NUMERAL FIVE;Nl;0;L;;;;5;N;;;;;
+3026;HANGZHOU NUMERAL SIX;Nl;0;L;;;;6;N;;;;;
+3027;HANGZHOU NUMERAL SEVEN;Nl;0;L;;;;7;N;;;;;
+3028;HANGZHOU NUMERAL EIGHT;Nl;0;L;;;;8;N;;;;;
+3029;HANGZHOU NUMERAL NINE;Nl;0;L;;;;9;N;;;;;
+302A;IDEOGRAPHIC LEVEL TONE MARK;Mn;218;NSM;;;;;N;;;;;
+302B;IDEOGRAPHIC RISING TONE MARK;Mn;228;NSM;;;;;N;;;;;
+302C;IDEOGRAPHIC DEPARTING TONE MARK;Mn;232;NSM;;;;;N;;;;;
+302D;IDEOGRAPHIC ENTERING TONE MARK;Mn;222;NSM;;;;;N;;;;;
+302E;HANGUL SINGLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;;
+302F;HANGUL DOUBLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;;
+3030;WAVY DASH;Pd;0;ON;;;;;N;;;;;
+3031;VERTICAL KANA REPEAT MARK;Lm;0;L;;;;;N;;;;;
+3032;VERTICAL KANA REPEAT WITH VOICED SOUND MARK;Lm;0;L;;;;;N;;;;;
+3033;VERTICAL KANA REPEAT MARK UPPER HALF;Lm;0;L;;;;;N;;;;;
+3034;VERTICAL KANA REPEAT WITH VOICED SOUND MARK UPPER HALF;Lm;0;L;;;;;N;;;;;
+3035;VERTICAL KANA REPEAT MARK LOWER HALF;Lm;0;L;;;;;N;;;;;
+3036;CIRCLED POSTAL MARK;So;0;ON;<compat> 3012;;;;N;;;;;
+3037;IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL;So;0;ON;;;;;N;;;;;
+3038;HANGZHOU NUMERAL TEN;Nl;0;L;<compat> 5341;;;10;N;;;;;
+3039;HANGZHOU NUMERAL TWENTY;Nl;0;L;<compat> 5344;;;20;N;;;;;
+303A;HANGZHOU NUMERAL THIRTY;Nl;0;L;<compat> 5345;;;30;N;;;;;
+303E;IDEOGRAPHIC VARIATION INDICATOR;So;0;ON;;;;;N;;;;;
+303F;IDEOGRAPHIC HALF FILL SPACE;So;0;ON;;;;;N;;;;;
+3041;HIRAGANA LETTER SMALL A;Lo;0;L;;;;;N;;;;;
+3042;HIRAGANA LETTER A;Lo;0;L;;;;;N;;;;;
+3043;HIRAGANA LETTER SMALL I;Lo;0;L;;;;;N;;;;;
+3044;HIRAGANA LETTER I;Lo;0;L;;;;;N;;;;;
+3045;HIRAGANA LETTER SMALL U;Lo;0;L;;;;;N;;;;;
+3046;HIRAGANA LETTER U;Lo;0;L;;;;;N;;;;;
+3047;HIRAGANA LETTER SMALL E;Lo;0;L;;;;;N;;;;;
+3048;HIRAGANA LETTER E;Lo;0;L;;;;;N;;;;;
+3049;HIRAGANA LETTER SMALL O;Lo;0;L;;;;;N;;;;;
+304A;HIRAGANA LETTER O;Lo;0;L;;;;;N;;;;;
+304B;HIRAGANA LETTER KA;Lo;0;L;;;;;N;;;;;
+304C;HIRAGANA LETTER GA;Lo;0;L;304B 3099;;;;N;;;;;
+304D;HIRAGANA LETTER KI;Lo;0;L;;;;;N;;;;;
+304E;HIRAGANA LETTER GI;Lo;0;L;304D 3099;;;;N;;;;;
+304F;HIRAGANA LETTER KU;Lo;0;L;;;;;N;;;;;
+3050;HIRAGANA LETTER GU;Lo;0;L;304F 3099;;;;N;;;;;
+3051;HIRAGANA LETTER KE;Lo;0;L;;;;;N;;;;;
+3052;HIRAGANA LETTER GE;Lo;0;L;3051 3099;;;;N;;;;;
+3053;HIRAGANA LETTER KO;Lo;0;L;;;;;N;;;;;
+3054;HIRAGANA LETTER GO;Lo;0;L;3053 3099;;;;N;;;;;
+3055;HIRAGANA LETTER SA;Lo;0;L;;;;;N;;;;;
+3056;HIRAGANA LETTER ZA;Lo;0;L;3055 3099;;;;N;;;;;
+3057;HIRAGANA LETTER SI;Lo;0;L;;;;;N;;;;;
+3058;HIRAGANA LETTER ZI;Lo;0;L;3057 3099;;;;N;;;;;
+3059;HIRAGANA LETTER SU;Lo;0;L;;;;;N;;;;;
+305A;HIRAGANA LETTER ZU;Lo;0;L;3059 3099;;;;N;;;;;
+305B;HIRAGANA LETTER SE;Lo;0;L;;;;;N;;;;;
+305C;HIRAGANA LETTER ZE;Lo;0;L;305B 3099;;;;N;;;;;
+305D;HIRAGANA LETTER SO;Lo;0;L;;;;;N;;;;;
+305E;HIRAGANA LETTER ZO;Lo;0;L;305D 3099;;;;N;;;;;
+305F;HIRAGANA LETTER TA;Lo;0;L;;;;;N;;;;;
+3060;HIRAGANA LETTER DA;Lo;0;L;305F 3099;;;;N;;;;;
+3061;HIRAGANA LETTER TI;Lo;0;L;;;;;N;;;;;
+3062;HIRAGANA LETTER DI;Lo;0;L;3061 3099;;;;N;;;;;
+3063;HIRAGANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;;
+3064;HIRAGANA LETTER TU;Lo;0;L;;;;;N;;;;;
+3065;HIRAGANA LETTER DU;Lo;0;L;3064 3099;;;;N;;;;;
+3066;HIRAGANA LETTER TE;Lo;0;L;;;;;N;;;;;
+3067;HIRAGANA LETTER DE;Lo;0;L;3066 3099;;;;N;;;;;
+3068;HIRAGANA LETTER TO;Lo;0;L;;;;;N;;;;;
+3069;HIRAGANA LETTER DO;Lo;0;L;3068 3099;;;;N;;;;;
+306A;HIRAGANA LETTER NA;Lo;0;L;;;;;N;;;;;
+306B;HIRAGANA LETTER NI;Lo;0;L;;;;;N;;;;;
+306C;HIRAGANA LETTER NU;Lo;0;L;;;;;N;;;;;
+306D;HIRAGANA LETTER NE;Lo;0;L;;;;;N;;;;;
+306E;HIRAGANA LETTER NO;Lo;0;L;;;;;N;;;;;
+306F;HIRAGANA LETTER HA;Lo;0;L;;;;;N;;;;;
+3070;HIRAGANA LETTER BA;Lo;0;L;306F 3099;;;;N;;;;;
+3071;HIRAGANA LETTER PA;Lo;0;L;306F 309A;;;;N;;;;;
+3072;HIRAGANA LETTER HI;Lo;0;L;;;;;N;;;;;
+3073;HIRAGANA LETTER BI;Lo;0;L;3072 3099;;;;N;;;;;
+3074;HIRAGANA LETTER PI;Lo;0;L;3072 309A;;;;N;;;;;
+3075;HIRAGANA LETTER HU;Lo;0;L;;;;;N;;;;;
+3076;HIRAGANA LETTER BU;Lo;0;L;3075 3099;;;;N;;;;;
+3077;HIRAGANA LETTER PU;Lo;0;L;3075 309A;;;;N;;;;;
+3078;HIRAGANA LETTER HE;Lo;0;L;;;;;N;;;;;
+3079;HIRAGANA LETTER BE;Lo;0;L;3078 3099;;;;N;;;;;
+307A;HIRAGANA LETTER PE;Lo;0;L;3078 309A;;;;N;;;;;
+307B;HIRAGANA LETTER HO;Lo;0;L;;;;;N;;;;;
+307C;HIRAGANA LETTER BO;Lo;0;L;307B 3099;;;;N;;;;;
+307D;HIRAGANA LETTER PO;Lo;0;L;307B 309A;;;;N;;;;;
+307E;HIRAGANA LETTER MA;Lo;0;L;;;;;N;;;;;
+307F;HIRAGANA LETTER MI;Lo;0;L;;;;;N;;;;;
+3080;HIRAGANA LETTER MU;Lo;0;L;;;;;N;;;;;
+3081;HIRAGANA LETTER ME;Lo;0;L;;;;;N;;;;;
+3082;HIRAGANA LETTER MO;Lo;0;L;;;;;N;;;;;
+3083;HIRAGANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;;
+3084;HIRAGANA LETTER YA;Lo;0;L;;;;;N;;;;;
+3085;HIRAGANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;;
+3086;HIRAGANA LETTER YU;Lo;0;L;;;;;N;;;;;
+3087;HIRAGANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;;
+3088;HIRAGANA LETTER YO;Lo;0;L;;;;;N;;;;;
+3089;HIRAGANA LETTER RA;Lo;0;L;;;;;N;;;;;
+308A;HIRAGANA LETTER RI;Lo;0;L;;;;;N;;;;;
+308B;HIRAGANA LETTER RU;Lo;0;L;;;;;N;;;;;
+308C;HIRAGANA LETTER RE;Lo;0;L;;;;;N;;;;;
+308D;HIRAGANA LETTER RO;Lo;0;L;;;;;N;;;;;
+308E;HIRAGANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;;
+308F;HIRAGANA LETTER WA;Lo;0;L;;;;;N;;;;;
+3090;HIRAGANA LETTER WI;Lo;0;L;;;;;N;;;;;
+3091;HIRAGANA LETTER WE;Lo;0;L;;;;;N;;;;;
+3092;HIRAGANA LETTER WO;Lo;0;L;;;;;N;;;;;
+3093;HIRAGANA LETTER N;Lo;0;L;;;;;N;;;;;
+3094;HIRAGANA LETTER VU;Lo;0;L;3046 3099;;;;N;;;;;
+3099;COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA VOICED SOUND MARK;;;;
+309A;COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;;;;
+309B;KATAKANA-HIRAGANA VOICED SOUND MARK;Sk;0;ON;<compat> 0020 3099;;;;N;;;;;
+309C;KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Sk;0;ON;<compat> 0020 309A;;;;N;;;;;
+309D;HIRAGANA ITERATION MARK;Lm;0;L;;;;;N;;;;;
+309E;HIRAGANA VOICED ITERATION MARK;Lm;0;L;309D 3099;;;;N;;;;;
+30A1;KATAKANA LETTER SMALL A;Lo;0;L;;;;;N;;;;;
+30A2;KATAKANA LETTER A;Lo;0;L;;;;;N;;;;;
+30A3;KATAKANA LETTER SMALL I;Lo;0;L;;;;;N;;;;;
+30A4;KATAKANA LETTER I;Lo;0;L;;;;;N;;;;;
+30A5;KATAKANA LETTER SMALL U;Lo;0;L;;;;;N;;;;;
+30A6;KATAKANA LETTER U;Lo;0;L;;;;;N;;;;;
+30A7;KATAKANA LETTER SMALL E;Lo;0;L;;;;;N;;;;;
+30A8;KATAKANA LETTER E;Lo;0;L;;;;;N;;;;;
+30A9;KATAKANA LETTER SMALL O;Lo;0;L;;;;;N;;;;;
+30AA;KATAKANA LETTER O;Lo;0;L;;;;;N;;;;;
+30AB;KATAKANA LETTER KA;Lo;0;L;;;;;N;;;;;
+30AC;KATAKANA LETTER GA;Lo;0;L;30AB 3099;;;;N;;;;;
+30AD;KATAKANA LETTER KI;Lo;0;L;;;;;N;;;;;
+30AE;KATAKANA LETTER GI;Lo;0;L;30AD 3099;;;;N;;;;;
+30AF;KATAKANA LETTER KU;Lo;0;L;;;;;N;;;;;
+30B0;KATAKANA LETTER GU;Lo;0;L;30AF 3099;;;;N;;;;;
+30B1;KATAKANA LETTER KE;Lo;0;L;;;;;N;;;;;
+30B2;KATAKANA LETTER GE;Lo;0;L;30B1 3099;;;;N;;;;;
+30B3;KATAKANA LETTER KO;Lo;0;L;;;;;N;;;;;
+30B4;KATAKANA LETTER GO;Lo;0;L;30B3 3099;;;;N;;;;;
+30B5;KATAKANA LETTER SA;Lo;0;L;;;;;N;;;;;
+30B6;KATAKANA LETTER ZA;Lo;0;L;30B5 3099;;;;N;;;;;
+30B7;KATAKANA LETTER SI;Lo;0;L;;;;;N;;;;;
+30B8;KATAKANA LETTER ZI;Lo;0;L;30B7 3099;;;;N;;;;;
+30B9;KATAKANA LETTER SU;Lo;0;L;;;;;N;;;;;
+30BA;KATAKANA LETTER ZU;Lo;0;L;30B9 3099;;;;N;;;;;
+30BB;KATAKANA LETTER SE;Lo;0;L;;;;;N;;;;;
+30BC;KATAKANA LETTER ZE;Lo;0;L;30BB 3099;;;;N;;;;;
+30BD;KATAKANA LETTER SO;Lo;0;L;;;;;N;;;;;
+30BE;KATAKANA LETTER ZO;Lo;0;L;30BD 3099;;;;N;;;;;
+30BF;KATAKANA LETTER TA;Lo;0;L;;;;;N;;;;;
+30C0;KATAKANA LETTER DA;Lo;0;L;30BF 3099;;;;N;;;;;
+30C1;KATAKANA LETTER TI;Lo;0;L;;;;;N;;;;;
+30C2;KATAKANA LETTER DI;Lo;0;L;30C1 3099;;;;N;;;;;
+30C3;KATAKANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;;
+30C4;KATAKANA LETTER TU;Lo;0;L;;;;;N;;;;;
+30C5;KATAKANA LETTER DU;Lo;0;L;30C4 3099;;;;N;;;;;
+30C6;KATAKANA LETTER TE;Lo;0;L;;;;;N;;;;;
+30C7;KATAKANA LETTER DE;Lo;0;L;30C6 3099;;;;N;;;;;
+30C8;KATAKANA LETTER TO;Lo;0;L;;;;;N;;;;;
+30C9;KATAKANA LETTER DO;Lo;0;L;30C8 3099;;;;N;;;;;
+30CA;KATAKANA LETTER NA;Lo;0;L;;;;;N;;;;;
+30CB;KATAKANA LETTER NI;Lo;0;L;;;;;N;;;;;
+30CC;KATAKANA LETTER NU;Lo;0;L;;;;;N;;;;;
+30CD;KATAKANA LETTER NE;Lo;0;L;;;;;N;;;;;
+30CE;KATAKANA LETTER NO;Lo;0;L;;;;;N;;;;;
+30CF;KATAKANA LETTER HA;Lo;0;L;;;;;N;;;;;
+30D0;KATAKANA LETTER BA;Lo;0;L;30CF 3099;;;;N;;;;;
+30D1;KATAKANA LETTER PA;Lo;0;L;30CF 309A;;;;N;;;;;
+30D2;KATAKANA LETTER HI;Lo;0;L;;;;;N;;;;;
+30D3;KATAKANA LETTER BI;Lo;0;L;30D2 3099;;;;N;;;;;
+30D4;KATAKANA LETTER PI;Lo;0;L;30D2 309A;;;;N;;;;;
+30D5;KATAKANA LETTER HU;Lo;0;L;;;;;N;;;;;
+30D6;KATAKANA LETTER BU;Lo;0;L;30D5 3099;;;;N;;;;;
+30D7;KATAKANA LETTER PU;Lo;0;L;30D5 309A;;;;N;;;;;
+30D8;KATAKANA LETTER HE;Lo;0;L;;;;;N;;;;;
+30D9;KATAKANA LETTER BE;Lo;0;L;30D8 3099;;;;N;;;;;
+30DA;KATAKANA LETTER PE;Lo;0;L;30D8 309A;;;;N;;;;;
+30DB;KATAKANA LETTER HO;Lo;0;L;;;;;N;;;;;
+30DC;KATAKANA LETTER BO;Lo;0;L;30DB 3099;;;;N;;;;;
+30DD;KATAKANA LETTER PO;Lo;0;L;30DB 309A;;;;N;;;;;
+30DE;KATAKANA LETTER MA;Lo;0;L;;;;;N;;;;;
+30DF;KATAKANA LETTER MI;Lo;0;L;;;;;N;;;;;
+30E0;KATAKANA LETTER MU;Lo;0;L;;;;;N;;;;;
+30E1;KATAKANA LETTER ME;Lo;0;L;;;;;N;;;;;
+30E2;KATAKANA LETTER MO;Lo;0;L;;;;;N;;;;;
+30E3;KATAKANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;;
+30E4;KATAKANA LETTER YA;Lo;0;L;;;;;N;;;;;
+30E5;KATAKANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;;
+30E6;KATAKANA LETTER YU;Lo;0;L;;;;;N;;;;;
+30E7;KATAKANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;;
+30E8;KATAKANA LETTER YO;Lo;0;L;;;;;N;;;;;
+30E9;KATAKANA LETTER RA;Lo;0;L;;;;;N;;;;;
+30EA;KATAKANA LETTER RI;Lo;0;L;;;;;N;;;;;
+30EB;KATAKANA LETTER RU;Lo;0;L;;;;;N;;;;;
+30EC;KATAKANA LETTER RE;Lo;0;L;;;;;N;;;;;
+30ED;KATAKANA LETTER RO;Lo;0;L;;;;;N;;;;;
+30EE;KATAKANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;;
+30EF;KATAKANA LETTER WA;Lo;0;L;;;;;N;;;;;
+30F0;KATAKANA LETTER WI;Lo;0;L;;;;;N;;;;;
+30F1;KATAKANA LETTER WE;Lo;0;L;;;;;N;;;;;
+30F2;KATAKANA LETTER WO;Lo;0;L;;;;;N;;;;;
+30F3;KATAKANA LETTER N;Lo;0;L;;;;;N;;;;;
+30F4;KATAKANA LETTER VU;Lo;0;L;30A6 3099;;;;N;;;;;
+30F5;KATAKANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;;
+30F6;KATAKANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;;
+30F7;KATAKANA LETTER VA;Lo;0;L;30EF 3099;;;;N;;;;;
+30F8;KATAKANA LETTER VI;Lo;0;L;30F0 3099;;;;N;;;;;
+30F9;KATAKANA LETTER VE;Lo;0;L;30F1 3099;;;;N;;;;;
+30FA;KATAKANA LETTER VO;Lo;0;L;30F2 3099;;;;N;;;;;
+30FB;KATAKANA MIDDLE DOT;Pc;0;ON;;;;;N;;;;;
+30FC;KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;;;;;N;;;;;
+30FD;KATAKANA ITERATION MARK;Lm;0;L;;;;;N;;;;;
+30FE;KATAKANA VOICED ITERATION MARK;Lm;0;L;30FD 3099;;;;N;;;;;
+3105;BOPOMOFO LETTER B;Lo;0;L;;;;;N;;;;;
+3106;BOPOMOFO LETTER P;Lo;0;L;;;;;N;;;;;
+3107;BOPOMOFO LETTER M;Lo;0;L;;;;;N;;;;;
+3108;BOPOMOFO LETTER F;Lo;0;L;;;;;N;;;;;
+3109;BOPOMOFO LETTER D;Lo;0;L;;;;;N;;;;;
+310A;BOPOMOFO LETTER T;Lo;0;L;;;;;N;;;;;
+310B;BOPOMOFO LETTER N;Lo;0;L;;;;;N;;;;;
+310C;BOPOMOFO LETTER L;Lo;0;L;;;;;N;;;;;
+310D;BOPOMOFO LETTER G;Lo;0;L;;;;;N;;;;;
+310E;BOPOMOFO LETTER K;Lo;0;L;;;;;N;;;;;
+310F;BOPOMOFO LETTER H;Lo;0;L;;;;;N;;;;;
+3110;BOPOMOFO LETTER J;Lo;0;L;;;;;N;;;;;
+3111;BOPOMOFO LETTER Q;Lo;0;L;;;;;N;;;;;
+3112;BOPOMOFO LETTER X;Lo;0;L;;;;;N;;;;;
+3113;BOPOMOFO LETTER ZH;Lo;0;L;;;;;N;;;;;
+3114;BOPOMOFO LETTER CH;Lo;0;L;;;;;N;;;;;
+3115;BOPOMOFO LETTER SH;Lo;0;L;;;;;N;;;;;
+3116;BOPOMOFO LETTER R;Lo;0;L;;;;;N;;;;;
+3117;BOPOMOFO LETTER Z;Lo;0;L;;;;;N;;;;;
+3118;BOPOMOFO LETTER C;Lo;0;L;;;;;N;;;;;
+3119;BOPOMOFO LETTER S;Lo;0;L;;;;;N;;;;;
+311A;BOPOMOFO LETTER A;Lo;0;L;;;;;N;;;;;
+311B;BOPOMOFO LETTER O;Lo;0;L;;;;;N;;;;;
+311C;BOPOMOFO LETTER E;Lo;0;L;;;;;N;;;;;
+311D;BOPOMOFO LETTER EH;Lo;0;L;;;;;N;;;;;
+311E;BOPOMOFO LETTER AI;Lo;0;L;;;;;N;;;;;
+311F;BOPOMOFO LETTER EI;Lo;0;L;;;;;N;;;;;
+3120;BOPOMOFO LETTER AU;Lo;0;L;;;;;N;;;;;
+3121;BOPOMOFO LETTER OU;Lo;0;L;;;;;N;;;;;
+3122;BOPOMOFO LETTER AN;Lo;0;L;;;;;N;;;;;
+3123;BOPOMOFO LETTER EN;Lo;0;L;;;;;N;;;;;
+3124;BOPOMOFO LETTER ANG;Lo;0;L;;;;;N;;;;;
+3125;BOPOMOFO LETTER ENG;Lo;0;L;;;;;N;;;;;
+3126;BOPOMOFO LETTER ER;Lo;0;L;;;;;N;;;;;
+3127;BOPOMOFO LETTER I;Lo;0;L;;;;;N;;;;;
+3128;BOPOMOFO LETTER U;Lo;0;L;;;;;N;;;;;
+3129;BOPOMOFO LETTER IU;Lo;0;L;;;;;N;;;;;
+312A;BOPOMOFO LETTER V;Lo;0;L;;;;;N;;;;;
+312B;BOPOMOFO LETTER NG;Lo;0;L;;;;;N;;;;;
+312C;BOPOMOFO LETTER GN;Lo;0;L;;;;;N;;;;;
+3131;HANGUL LETTER KIYEOK;Lo;0;L;<compat> 1100;;;;N;HANGUL LETTER GIYEOG;;;;
+3132;HANGUL LETTER SSANGKIYEOK;Lo;0;L;<compat> 1101;;;;N;HANGUL LETTER SSANG GIYEOG;;;;
+3133;HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<compat> 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;;
+3134;HANGUL LETTER NIEUN;Lo;0;L;<compat> 1102;;;;N;;;;;
+3135;HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<compat> 11AC;;;;N;HANGUL LETTER NIEUN JIEUJ;;;;
+3136;HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<compat> 11AD;;;;N;HANGUL LETTER NIEUN HIEUH;;;;
+3137;HANGUL LETTER TIKEUT;Lo;0;L;<compat> 1103;;;;N;HANGUL LETTER DIGEUD;;;;
+3138;HANGUL LETTER SSANGTIKEUT;Lo;0;L;<compat> 1104;;;;N;HANGUL LETTER SSANG DIGEUD;;;;
+3139;HANGUL LETTER RIEUL;Lo;0;L;<compat> 1105;;;;N;HANGUL LETTER LIEUL;;;;
+313A;HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<compat> 11B0;;;;N;HANGUL LETTER LIEUL GIYEOG;;;;
+313B;HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<compat> 11B1;;;;N;HANGUL LETTER LIEUL MIEUM;;;;
+313C;HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<compat> 11B2;;;;N;HANGUL LETTER LIEUL BIEUB;;;;
+313D;HANGUL LETTER RIEUL-SIOS;Lo;0;L;<compat> 11B3;;;;N;HANGUL LETTER LIEUL SIOS;;;;
+313E;HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<compat> 11B4;;;;N;HANGUL LETTER LIEUL TIEUT;;;;
+313F;HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<compat> 11B5;;;;N;HANGUL LETTER LIEUL PIEUP;;;;
+3140;HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<compat> 111A;;;;N;HANGUL LETTER LIEUL HIEUH;;;;
+3141;HANGUL LETTER MIEUM;Lo;0;L;<compat> 1106;;;;N;;;;;
+3142;HANGUL LETTER PIEUP;Lo;0;L;<compat> 1107;;;;N;HANGUL LETTER BIEUB;;;;
+3143;HANGUL LETTER SSANGPIEUP;Lo;0;L;<compat> 1108;;;;N;HANGUL LETTER SSANG BIEUB;;;;
+3144;HANGUL LETTER PIEUP-SIOS;Lo;0;L;<compat> 1121;;;;N;HANGUL LETTER BIEUB SIOS;;;;
+3145;HANGUL LETTER SIOS;Lo;0;L;<compat> 1109;;;;N;;;;;
+3146;HANGUL LETTER SSANGSIOS;Lo;0;L;<compat> 110A;;;;N;HANGUL LETTER SSANG SIOS;;;;
+3147;HANGUL LETTER IEUNG;Lo;0;L;<compat> 110B;;;;N;;;;;
+3148;HANGUL LETTER CIEUC;Lo;0;L;<compat> 110C;;;;N;HANGUL LETTER JIEUJ;;;;
+3149;HANGUL LETTER SSANGCIEUC;Lo;0;L;<compat> 110D;;;;N;HANGUL LETTER SSANG JIEUJ;;;;
+314A;HANGUL LETTER CHIEUCH;Lo;0;L;<compat> 110E;;;;N;HANGUL LETTER CIEUC;;;;
+314B;HANGUL LETTER KHIEUKH;Lo;0;L;<compat> 110F;;;;N;HANGUL LETTER KIYEOK;;;;
+314C;HANGUL LETTER THIEUTH;Lo;0;L;<compat> 1110;;;;N;HANGUL LETTER TIEUT;;;;
+314D;HANGUL LETTER PHIEUPH;Lo;0;L;<compat> 1111;;;;N;HANGUL LETTER PIEUP;;;;
+314E;HANGUL LETTER HIEUH;Lo;0;L;<compat> 1112;;;;N;;;;;
+314F;HANGUL LETTER A;Lo;0;L;<compat> 1161;;;;N;;;;;
+3150;HANGUL LETTER AE;Lo;0;L;<compat> 1162;;;;N;;;;;
+3151;HANGUL LETTER YA;Lo;0;L;<compat> 1163;;;;N;;;;;
+3152;HANGUL LETTER YAE;Lo;0;L;<compat> 1164;;;;N;;;;;
+3153;HANGUL LETTER EO;Lo;0;L;<compat> 1165;;;;N;;;;;
+3154;HANGUL LETTER E;Lo;0;L;<compat> 1166;;;;N;;;;;
+3155;HANGUL LETTER YEO;Lo;0;L;<compat> 1167;;;;N;;;;;
+3156;HANGUL LETTER YE;Lo;0;L;<compat> 1168;;;;N;;;;;
+3157;HANGUL LETTER O;Lo;0;L;<compat> 1169;;;;N;;;;;
+3158;HANGUL LETTER WA;Lo;0;L;<compat> 116A;;;;N;;;;;
+3159;HANGUL LETTER WAE;Lo;0;L;<compat> 116B;;;;N;;;;;
+315A;HANGUL LETTER OE;Lo;0;L;<compat> 116C;;;;N;;;;;
+315B;HANGUL LETTER YO;Lo;0;L;<compat> 116D;;;;N;;;;;
+315C;HANGUL LETTER U;Lo;0;L;<compat> 116E;;;;N;;;;;
+315D;HANGUL LETTER WEO;Lo;0;L;<compat> 116F;;;;N;;;;;
+315E;HANGUL LETTER WE;Lo;0;L;<compat> 1170;;;;N;;;;;
+315F;HANGUL LETTER WI;Lo;0;L;<compat> 1171;;;;N;;;;;
+3160;HANGUL LETTER YU;Lo;0;L;<compat> 1172;;;;N;;;;;
+3161;HANGUL LETTER EU;Lo;0;L;<compat> 1173;;;;N;;;;;
+3162;HANGUL LETTER YI;Lo;0;L;<compat> 1174;;;;N;;;;;
+3163;HANGUL LETTER I;Lo;0;L;<compat> 1175;;;;N;;;;;
+3164;HANGUL FILLER;Lo;0;L;<compat> 1160;;;;N;HANGUL CAE OM;;;;
+3165;HANGUL LETTER SSANGNIEUN;Lo;0;L;<compat> 1114;;;;N;HANGUL LETTER SSANG NIEUN;;;;
+3166;HANGUL LETTER NIEUN-TIKEUT;Lo;0;L;<compat> 1115;;;;N;HANGUL LETTER NIEUN DIGEUD;;;;
+3167;HANGUL LETTER NIEUN-SIOS;Lo;0;L;<compat> 11C7;;;;N;HANGUL LETTER NIEUN SIOS;;;;
+3168;HANGUL LETTER NIEUN-PANSIOS;Lo;0;L;<compat> 11C8;;;;N;HANGUL LETTER NIEUN BAN CHI EUM;;;;
+3169;HANGUL LETTER RIEUL-KIYEOK-SIOS;Lo;0;L;<compat> 11CC;;;;N;HANGUL LETTER LIEUL GIYEOG SIOS;;;;
+316A;HANGUL LETTER RIEUL-TIKEUT;Lo;0;L;<compat> 11CE;;;;N;HANGUL LETTER LIEUL DIGEUD;;;;
+316B;HANGUL LETTER RIEUL-PIEUP-SIOS;Lo;0;L;<compat> 11D3;;;;N;HANGUL LETTER LIEUL BIEUB SIOS;;;;
+316C;HANGUL LETTER RIEUL-PANSIOS;Lo;0;L;<compat> 11D7;;;;N;HANGUL LETTER LIEUL BAN CHI EUM;;;;
+316D;HANGUL LETTER RIEUL-YEORINHIEUH;Lo;0;L;<compat> 11D9;;;;N;HANGUL LETTER LIEUL YEOLIN HIEUH;;;;
+316E;HANGUL LETTER MIEUM-PIEUP;Lo;0;L;<compat> 111C;;;;N;HANGUL LETTER MIEUM BIEUB;;;;
+316F;HANGUL LETTER MIEUM-SIOS;Lo;0;L;<compat> 11DD;;;;N;HANGUL LETTER MIEUM SIOS;;;;
+3170;HANGUL LETTER MIEUM-PANSIOS;Lo;0;L;<compat> 11DF;;;;N;HANGUL LETTER BIEUB BAN CHI EUM;;;;
+3171;HANGUL LETTER KAPYEOUNMIEUM;Lo;0;L;<compat> 111D;;;;N;HANGUL LETTER MIEUM SUN GYEONG EUM;;;;
+3172;HANGUL LETTER PIEUP-KIYEOK;Lo;0;L;<compat> 111E;;;;N;HANGUL LETTER BIEUB GIYEOG;;;;
+3173;HANGUL LETTER PIEUP-TIKEUT;Lo;0;L;<compat> 1120;;;;N;HANGUL LETTER BIEUB DIGEUD;;;;
+3174;HANGUL LETTER PIEUP-SIOS-KIYEOK;Lo;0;L;<compat> 1122;;;;N;HANGUL LETTER BIEUB SIOS GIYEOG;;;;
+3175;HANGUL LETTER PIEUP-SIOS-TIKEUT;Lo;0;L;<compat> 1123;;;;N;HANGUL LETTER BIEUB SIOS DIGEUD;;;;
+3176;HANGUL LETTER PIEUP-CIEUC;Lo;0;L;<compat> 1127;;;;N;HANGUL LETTER BIEUB JIEUJ;;;;
+3177;HANGUL LETTER PIEUP-THIEUTH;Lo;0;L;<compat> 1129;;;;N;HANGUL LETTER BIEUB TIEUT;;;;
+3178;HANGUL LETTER KAPYEOUNPIEUP;Lo;0;L;<compat> 112B;;;;N;HANGUL LETTER BIEUB SUN GYEONG EUM;;;;
+3179;HANGUL LETTER KAPYEOUNSSANGPIEUP;Lo;0;L;<compat> 112C;;;;N;HANGUL LETTER SSANG BIEUB SUN GYEONG EUM;;;;
+317A;HANGUL LETTER SIOS-KIYEOK;Lo;0;L;<compat> 112D;;;;N;HANGUL LETTER SIOS GIYEOG;;;;
+317B;HANGUL LETTER SIOS-NIEUN;Lo;0;L;<compat> 112E;;;;N;HANGUL LETTER SIOS NIEUN;;;;
+317C;HANGUL LETTER SIOS-TIKEUT;Lo;0;L;<compat> 112F;;;;N;HANGUL LETTER SIOS DIGEUD;;;;
+317D;HANGUL LETTER SIOS-PIEUP;Lo;0;L;<compat> 1132;;;;N;HANGUL LETTER SIOS BIEUB;;;;
+317E;HANGUL LETTER SIOS-CIEUC;Lo;0;L;<compat> 1136;;;;N;HANGUL LETTER SIOS JIEUJ;;;;
+317F;HANGUL LETTER PANSIOS;Lo;0;L;<compat> 1140;;;;N;HANGUL LETTER BAN CHI EUM;;;;
+3180;HANGUL LETTER SSANGIEUNG;Lo;0;L;<compat> 1147;;;;N;HANGUL LETTER SSANG IEUNG;;;;
+3181;HANGUL LETTER YESIEUNG;Lo;0;L;<compat> 114C;;;;N;HANGUL LETTER NGIEUNG;;;;
+3182;HANGUL LETTER YESIEUNG-SIOS;Lo;0;L;<compat> 11F1;;;;N;HANGUL LETTER NGIEUNG SIOS;;;;
+3183;HANGUL LETTER YESIEUNG-PANSIOS;Lo;0;L;<compat> 11F2;;;;N;HANGUL LETTER NGIEUNG BAN CHI EUM;;;;
+3184;HANGUL LETTER KAPYEOUNPHIEUPH;Lo;0;L;<compat> 1157;;;;N;HANGUL LETTER PIEUP SUN GYEONG EUM;;;;
+3185;HANGUL LETTER SSANGHIEUH;Lo;0;L;<compat> 1158;;;;N;HANGUL LETTER SSANG HIEUH;;;;
+3186;HANGUL LETTER YEORINHIEUH;Lo;0;L;<compat> 1159;;;;N;HANGUL LETTER YEOLIN HIEUH;;;;
+3187;HANGUL LETTER YO-YA;Lo;0;L;<compat> 1184;;;;N;HANGUL LETTER YOYA;;;;
+3188;HANGUL LETTER YO-YAE;Lo;0;L;<compat> 1185;;;;N;HANGUL LETTER YOYAE;;;;
+3189;HANGUL LETTER YO-I;Lo;0;L;<compat> 1188;;;;N;HANGUL LETTER YOI;;;;
+318A;HANGUL LETTER YU-YEO;Lo;0;L;<compat> 1191;;;;N;HANGUL LETTER YUYEO;;;;
+318B;HANGUL LETTER YU-YE;Lo;0;L;<compat> 1192;;;;N;HANGUL LETTER YUYE;;;;
+318C;HANGUL LETTER YU-I;Lo;0;L;<compat> 1194;;;;N;HANGUL LETTER YUI;;;;
+318D;HANGUL LETTER ARAEA;Lo;0;L;<compat> 119E;;;;N;HANGUL LETTER ALAE A;;;;
+318E;HANGUL LETTER ARAEAE;Lo;0;L;<compat> 11A1;;;;N;HANGUL LETTER ALAE AE;;;;
+3190;IDEOGRAPHIC ANNOTATION LINKING MARK;So;0;L;;;;;N;KANBUN TATETEN;Kanbun Tateten;;;
+3191;IDEOGRAPHIC ANNOTATION REVERSE MARK;So;0;L;;;;;N;KAERITEN RE;Kaeriten;;;
+3192;IDEOGRAPHIC ANNOTATION ONE MARK;No;0;L;<super> 4E00;;;1;N;KAERITEN ITI;Kaeriten;;;
+3193;IDEOGRAPHIC ANNOTATION TWO MARK;No;0;L;<super> 4E8C;;;2;N;KAERITEN NI;Kaeriten;;;
+3194;IDEOGRAPHIC ANNOTATION THREE MARK;No;0;L;<super> 4E09;;;3;N;KAERITEN SAN;Kaeriten;;;
+3195;IDEOGRAPHIC ANNOTATION FOUR MARK;No;0;L;<super> 56DB;;;4;N;KAERITEN SI;Kaeriten;;;
+3196;IDEOGRAPHIC ANNOTATION TOP MARK;So;0;L;<super> 4E0A;;;;N;KAERITEN ZYOU;Kaeriten;;;
+3197;IDEOGRAPHIC ANNOTATION MIDDLE MARK;So;0;L;<super> 4E2D;;;;N;KAERITEN TYUU;Kaeriten;;;
+3198;IDEOGRAPHIC ANNOTATION BOTTOM MARK;So;0;L;<super> 4E0B;;;;N;KAERITEN GE;Kaeriten;;;
+3199;IDEOGRAPHIC ANNOTATION FIRST MARK;So;0;L;<super> 7532;;;;N;KAERITEN KOU;Kaeriten;;;
+319A;IDEOGRAPHIC ANNOTATION SECOND MARK;So;0;L;<super> 4E59;;;;N;KAERITEN OTU;Kaeriten;;;
+319B;IDEOGRAPHIC ANNOTATION THIRD MARK;So;0;L;<super> 4E19;;;;N;KAERITEN HEI;Kaeriten;;;
+319C;IDEOGRAPHIC ANNOTATION FOURTH MARK;So;0;L;<super> 4E01;;;;N;KAERITEN TEI;Kaeriten;;;
+319D;IDEOGRAPHIC ANNOTATION HEAVEN MARK;So;0;L;<super> 5929;;;;N;KAERITEN TEN;Kaeriten;;;
+319E;IDEOGRAPHIC ANNOTATION EARTH MARK;So;0;L;<super> 5730;;;;N;KAERITEN TI;Kaeriten;;;
+319F;IDEOGRAPHIC ANNOTATION MAN MARK;So;0;L;<super> 4EBA;;;;N;KAERITEN ZIN;Kaeriten;;;
+31A0;BOPOMOFO LETTER BU;Lo;0;L;;;;;N;;;;;
+31A1;BOPOMOFO LETTER ZI;Lo;0;L;;;;;N;;;;;
+31A2;BOPOMOFO LETTER JI;Lo;0;L;;;;;N;;;;;
+31A3;BOPOMOFO LETTER GU;Lo;0;L;;;;;N;;;;;
+31A4;BOPOMOFO LETTER EE;Lo;0;L;;;;;N;;;;;
+31A5;BOPOMOFO LETTER ENN;Lo;0;L;;;;;N;;;;;
+31A6;BOPOMOFO LETTER OO;Lo;0;L;;;;;N;;;;;
+31A7;BOPOMOFO LETTER ONN;Lo;0;L;;;;;N;;;;;
+31A8;BOPOMOFO LETTER IR;Lo;0;L;;;;;N;;;;;
+31A9;BOPOMOFO LETTER ANN;Lo;0;L;;;;;N;;;;;
+31AA;BOPOMOFO LETTER INN;Lo;0;L;;;;;N;;;;;
+31AB;BOPOMOFO LETTER UNN;Lo;0;L;;;;;N;;;;;
+31AC;BOPOMOFO LETTER IM;Lo;0;L;;;;;N;;;;;
+31AD;BOPOMOFO LETTER NGG;Lo;0;L;;;;;N;;;;;
+31AE;BOPOMOFO LETTER AINN;Lo;0;L;;;;;N;;;;;
+31AF;BOPOMOFO LETTER AUNN;Lo;0;L;;;;;N;;;;;
+31B0;BOPOMOFO LETTER AM;Lo;0;L;;;;;N;;;;;
+31B1;BOPOMOFO LETTER OM;Lo;0;L;;;;;N;;;;;
+31B2;BOPOMOFO LETTER ONG;Lo;0;L;;;;;N;;;;;
+31B3;BOPOMOFO LETTER INNN;Lo;0;L;;;;;N;;;;;
+31B4;BOPOMOFO FINAL LETTER P;Lo;0;L;;;;;N;;;;;
+31B5;BOPOMOFO FINAL LETTER T;Lo;0;L;;;;;N;;;;;
+31B6;BOPOMOFO FINAL LETTER K;Lo;0;L;;;;;N;;;;;
+31B7;BOPOMOFO FINAL LETTER H;Lo;0;L;;;;;N;;;;;
+3200;PARENTHESIZED HANGUL KIYEOK;So;0;L;<compat> 0028 1100 0029;;;;N;PARENTHESIZED HANGUL GIYEOG;;;;
+3201;PARENTHESIZED HANGUL NIEUN;So;0;L;<compat> 0028 1102 0029;;;;N;;;;;
+3202;PARENTHESIZED HANGUL TIKEUT;So;0;L;<compat> 0028 1103 0029;;;;N;PARENTHESIZED HANGUL DIGEUD;;;;
+3203;PARENTHESIZED HANGUL RIEUL;So;0;L;<compat> 0028 1105 0029;;;;N;PARENTHESIZED HANGUL LIEUL;;;;
+3204;PARENTHESIZED HANGUL MIEUM;So;0;L;<compat> 0028 1106 0029;;;;N;;;;;
+3205;PARENTHESIZED HANGUL PIEUP;So;0;L;<compat> 0028 1107 0029;;;;N;PARENTHESIZED HANGUL BIEUB;;;;
+3206;PARENTHESIZED HANGUL SIOS;So;0;L;<compat> 0028 1109 0029;;;;N;;;;;
+3207;PARENTHESIZED HANGUL IEUNG;So;0;L;<compat> 0028 110B 0029;;;;N;;;;;
+3208;PARENTHESIZED HANGUL CIEUC;So;0;L;<compat> 0028 110C 0029;;;;N;PARENTHESIZED HANGUL JIEUJ;;;;
+3209;PARENTHESIZED HANGUL CHIEUCH;So;0;L;<compat> 0028 110E 0029;;;;N;PARENTHESIZED HANGUL CIEUC;;;;
+320A;PARENTHESIZED HANGUL KHIEUKH;So;0;L;<compat> 0028 110F 0029;;;;N;PARENTHESIZED HANGUL KIYEOK;;;;
+320B;PARENTHESIZED HANGUL THIEUTH;So;0;L;<compat> 0028 1110 0029;;;;N;PARENTHESIZED HANGUL TIEUT;;;;
+320C;PARENTHESIZED HANGUL PHIEUPH;So;0;L;<compat> 0028 1111 0029;;;;N;PARENTHESIZED HANGUL PIEUP;;;;
+320D;PARENTHESIZED HANGUL HIEUH;So;0;L;<compat> 0028 1112 0029;;;;N;;;;;
+320E;PARENTHESIZED HANGUL KIYEOK A;So;0;L;<compat> 0028 1100 1161 0029;;;;N;PARENTHESIZED HANGUL GA;;;;
+320F;PARENTHESIZED HANGUL NIEUN A;So;0;L;<compat> 0028 1102 1161 0029;;;;N;PARENTHESIZED HANGUL NA;;;;
+3210;PARENTHESIZED HANGUL TIKEUT A;So;0;L;<compat> 0028 1103 1161 0029;;;;N;PARENTHESIZED HANGUL DA;;;;
+3211;PARENTHESIZED HANGUL RIEUL A;So;0;L;<compat> 0028 1105 1161 0029;;;;N;PARENTHESIZED HANGUL LA;;;;
+3212;PARENTHESIZED HANGUL MIEUM A;So;0;L;<compat> 0028 1106 1161 0029;;;;N;PARENTHESIZED HANGUL MA;;;;
+3213;PARENTHESIZED HANGUL PIEUP A;So;0;L;<compat> 0028 1107 1161 0029;;;;N;PARENTHESIZED HANGUL BA;;;;
+3214;PARENTHESIZED HANGUL SIOS A;So;0;L;<compat> 0028 1109 1161 0029;;;;N;PARENTHESIZED HANGUL SA;;;;
+3215;PARENTHESIZED HANGUL IEUNG A;So;0;L;<compat> 0028 110B 1161 0029;;;;N;PARENTHESIZED HANGUL A;;;;
+3216;PARENTHESIZED HANGUL CIEUC A;So;0;L;<compat> 0028 110C 1161 0029;;;;N;PARENTHESIZED HANGUL JA;;;;
+3217;PARENTHESIZED HANGUL CHIEUCH A;So;0;L;<compat> 0028 110E 1161 0029;;;;N;PARENTHESIZED HANGUL CA;;;;
+3218;PARENTHESIZED HANGUL KHIEUKH A;So;0;L;<compat> 0028 110F 1161 0029;;;;N;PARENTHESIZED HANGUL KA;;;;
+3219;PARENTHESIZED HANGUL THIEUTH A;So;0;L;<compat> 0028 1110 1161 0029;;;;N;PARENTHESIZED HANGUL TA;;;;
+321A;PARENTHESIZED HANGUL PHIEUPH A;So;0;L;<compat> 0028 1111 1161 0029;;;;N;PARENTHESIZED HANGUL PA;;;;
+321B;PARENTHESIZED HANGUL HIEUH A;So;0;L;<compat> 0028 1112 1161 0029;;;;N;PARENTHESIZED HANGUL HA;;;;
+321C;PARENTHESIZED HANGUL CIEUC U;So;0;L;<compat> 0028 110C 116E 0029;;;;N;PARENTHESIZED HANGUL JU;;;;
+3220;PARENTHESIZED IDEOGRAPH ONE;No;0;L;<compat> 0028 4E00 0029;;;1;N;;;;;
+3221;PARENTHESIZED IDEOGRAPH TWO;No;0;L;<compat> 0028 4E8C 0029;;;2;N;;;;;
+3222;PARENTHESIZED IDEOGRAPH THREE;No;0;L;<compat> 0028 4E09 0029;;;3;N;;;;;
+3223;PARENTHESIZED IDEOGRAPH FOUR;No;0;L;<compat> 0028 56DB 0029;;;4;N;;;;;
+3224;PARENTHESIZED IDEOGRAPH FIVE;No;0;L;<compat> 0028 4E94 0029;;;5;N;;;;;
+3225;PARENTHESIZED IDEOGRAPH SIX;No;0;L;<compat> 0028 516D 0029;;;6;N;;;;;
+3226;PARENTHESIZED IDEOGRAPH SEVEN;No;0;L;<compat> 0028 4E03 0029;;;7;N;;;;;
+3227;PARENTHESIZED IDEOGRAPH EIGHT;No;0;L;<compat> 0028 516B 0029;;;8;N;;;;;
+3228;PARENTHESIZED IDEOGRAPH NINE;No;0;L;<compat> 0028 4E5D 0029;;;9;N;;;;;
+3229;PARENTHESIZED IDEOGRAPH TEN;No;0;L;<compat> 0028 5341 0029;;;10;N;;;;;
+322A;PARENTHESIZED IDEOGRAPH MOON;So;0;L;<compat> 0028 6708 0029;;;;N;;;;;
+322B;PARENTHESIZED IDEOGRAPH FIRE;So;0;L;<compat> 0028 706B 0029;;;;N;;;;;
+322C;PARENTHESIZED IDEOGRAPH WATER;So;0;L;<compat> 0028 6C34 0029;;;;N;;;;;
+322D;PARENTHESIZED IDEOGRAPH WOOD;So;0;L;<compat> 0028 6728 0029;;;;N;;;;;
+322E;PARENTHESIZED IDEOGRAPH METAL;So;0;L;<compat> 0028 91D1 0029;;;;N;;;;;
+322F;PARENTHESIZED IDEOGRAPH EARTH;So;0;L;<compat> 0028 571F 0029;;;;N;;;;;
+3230;PARENTHESIZED IDEOGRAPH SUN;So;0;L;<compat> 0028 65E5 0029;;;;N;;;;;
+3231;PARENTHESIZED IDEOGRAPH STOCK;So;0;L;<compat> 0028 682A 0029;;;;N;;;;;
+3232;PARENTHESIZED IDEOGRAPH HAVE;So;0;L;<compat> 0028 6709 0029;;;;N;;;;;
+3233;PARENTHESIZED IDEOGRAPH SOCIETY;So;0;L;<compat> 0028 793E 0029;;;;N;;;;;
+3234;PARENTHESIZED IDEOGRAPH NAME;So;0;L;<compat> 0028 540D 0029;;;;N;;;;;
+3235;PARENTHESIZED IDEOGRAPH SPECIAL;So;0;L;<compat> 0028 7279 0029;;;;N;;;;;
+3236;PARENTHESIZED IDEOGRAPH FINANCIAL;So;0;L;<compat> 0028 8CA1 0029;;;;N;;;;;
+3237;PARENTHESIZED IDEOGRAPH CONGRATULATION;So;0;L;<compat> 0028 795D 0029;;;;N;;;;;
+3238;PARENTHESIZED IDEOGRAPH LABOR;So;0;L;<compat> 0028 52B4 0029;;;;N;;;;;
+3239;PARENTHESIZED IDEOGRAPH REPRESENT;So;0;L;<compat> 0028 4EE3 0029;;;;N;;;;;
+323A;PARENTHESIZED IDEOGRAPH CALL;So;0;L;<compat> 0028 547C 0029;;;;N;;;;;
+323B;PARENTHESIZED IDEOGRAPH STUDY;So;0;L;<compat> 0028 5B66 0029;;;;N;;;;;
+323C;PARENTHESIZED IDEOGRAPH SUPERVISE;So;0;L;<compat> 0028 76E3 0029;;;;N;;;;;
+323D;PARENTHESIZED IDEOGRAPH ENTERPRISE;So;0;L;<compat> 0028 4F01 0029;;;;N;;;;;
+323E;PARENTHESIZED IDEOGRAPH RESOURCE;So;0;L;<compat> 0028 8CC7 0029;;;;N;;;;;
+323F;PARENTHESIZED IDEOGRAPH ALLIANCE;So;0;L;<compat> 0028 5354 0029;;;;N;;;;;
+3240;PARENTHESIZED IDEOGRAPH FESTIVAL;So;0;L;<compat> 0028 796D 0029;;;;N;;;;;
+3241;PARENTHESIZED IDEOGRAPH REST;So;0;L;<compat> 0028 4F11 0029;;;;N;;;;;
+3242;PARENTHESIZED IDEOGRAPH SELF;So;0;L;<compat> 0028 81EA 0029;;;;N;;;;;
+3243;PARENTHESIZED IDEOGRAPH REACH;So;0;L;<compat> 0028 81F3 0029;;;;N;;;;;
+3260;CIRCLED HANGUL KIYEOK;So;0;L;<circle> 1100;;;;N;CIRCLED HANGUL GIYEOG;;;;
+3261;CIRCLED HANGUL NIEUN;So;0;L;<circle> 1102;;;;N;;;;;
+3262;CIRCLED HANGUL TIKEUT;So;0;L;<circle> 1103;;;;N;CIRCLED HANGUL DIGEUD;;;;
+3263;CIRCLED HANGUL RIEUL;So;0;L;<circle> 1105;;;;N;CIRCLED HANGUL LIEUL;;;;
+3264;CIRCLED HANGUL MIEUM;So;0;L;<circle> 1106;;;;N;;;;;
+3265;CIRCLED HANGUL PIEUP;So;0;L;<circle> 1107;;;;N;CIRCLED HANGUL BIEUB;;;;
+3266;CIRCLED HANGUL SIOS;So;0;L;<circle> 1109;;;;N;;;;;
+3267;CIRCLED HANGUL IEUNG;So;0;L;<circle> 110B;;;;N;;;;;
+3268;CIRCLED HANGUL CIEUC;So;0;L;<circle> 110C;;;;N;CIRCLED HANGUL JIEUJ;;;;
+3269;CIRCLED HANGUL CHIEUCH;So;0;L;<circle> 110E;;;;N;CIRCLED HANGUL CIEUC;;;;
+326A;CIRCLED HANGUL KHIEUKH;So;0;L;<circle> 110F;;;;N;CIRCLED HANGUL KIYEOK;;;;
+326B;CIRCLED HANGUL THIEUTH;So;0;L;<circle> 1110;;;;N;CIRCLED HANGUL TIEUT;;;;
+326C;CIRCLED HANGUL PHIEUPH;So;0;L;<circle> 1111;;;;N;CIRCLED HANGUL PIEUP;;;;
+326D;CIRCLED HANGUL HIEUH;So;0;L;<circle> 1112;;;;N;;;;;
+326E;CIRCLED HANGUL KIYEOK A;So;0;L;<circle> 1100 1161;;;;N;CIRCLED HANGUL GA;;;;
+326F;CIRCLED HANGUL NIEUN A;So;0;L;<circle> 1102 1161;;;;N;CIRCLED HANGUL NA;;;;
+3270;CIRCLED HANGUL TIKEUT A;So;0;L;<circle> 1103 1161;;;;N;CIRCLED HANGUL DA;;;;
+3271;CIRCLED HANGUL RIEUL A;So;0;L;<circle> 1105 1161;;;;N;CIRCLED HANGUL LA;;;;
+3272;CIRCLED HANGUL MIEUM A;So;0;L;<circle> 1106 1161;;;;N;CIRCLED HANGUL MA;;;;
+3273;CIRCLED HANGUL PIEUP A;So;0;L;<circle> 1107 1161;;;;N;CIRCLED HANGUL BA;;;;
+3274;CIRCLED HANGUL SIOS A;So;0;L;<circle> 1109 1161;;;;N;CIRCLED HANGUL SA;;;;
+3275;CIRCLED HANGUL IEUNG A;So;0;L;<circle> 110B 1161;;;;N;CIRCLED HANGUL A;;;;
+3276;CIRCLED HANGUL CIEUC A;So;0;L;<circle> 110C 1161;;;;N;CIRCLED HANGUL JA;;;;
+3277;CIRCLED HANGUL CHIEUCH A;So;0;L;<circle> 110E 1161;;;;N;CIRCLED HANGUL CA;;;;
+3278;CIRCLED HANGUL KHIEUKH A;So;0;L;<circle> 110F 1161;;;;N;CIRCLED HANGUL KA;;;;
+3279;CIRCLED HANGUL THIEUTH A;So;0;L;<circle> 1110 1161;;;;N;CIRCLED HANGUL TA;;;;
+327A;CIRCLED HANGUL PHIEUPH A;So;0;L;<circle> 1111 1161;;;;N;CIRCLED HANGUL PA;;;;
+327B;CIRCLED HANGUL HIEUH A;So;0;L;<circle> 1112 1161;;;;N;CIRCLED HANGUL HA;;;;
+327F;KOREAN STANDARD SYMBOL;So;0;L;;;;;N;;;;;
+3280;CIRCLED IDEOGRAPH ONE;No;0;L;<circle> 4E00;;;1;N;;;;;
+3281;CIRCLED IDEOGRAPH TWO;No;0;L;<circle> 4E8C;;;2;N;;;;;
+3282;CIRCLED IDEOGRAPH THREE;No;0;L;<circle> 4E09;;;3;N;;;;;
+3283;CIRCLED IDEOGRAPH FOUR;No;0;L;<circle> 56DB;;;4;N;;;;;
+3284;CIRCLED IDEOGRAPH FIVE;No;0;L;<circle> 4E94;;;5;N;;;;;
+3285;CIRCLED IDEOGRAPH SIX;No;0;L;<circle> 516D;;;6;N;;;;;
+3286;CIRCLED IDEOGRAPH SEVEN;No;0;L;<circle> 4E03;;;7;N;;;;;
+3287;CIRCLED IDEOGRAPH EIGHT;No;0;L;<circle> 516B;;;8;N;;;;;
+3288;CIRCLED IDEOGRAPH NINE;No;0;L;<circle> 4E5D;;;9;N;;;;;
+3289;CIRCLED IDEOGRAPH TEN;No;0;L;<circle> 5341;;;10;N;;;;;
+328A;CIRCLED IDEOGRAPH MOON;So;0;L;<circle> 6708;;;;N;;;;;
+328B;CIRCLED IDEOGRAPH FIRE;So;0;L;<circle> 706B;;;;N;;;;;
+328C;CIRCLED IDEOGRAPH WATER;So;0;L;<circle> 6C34;;;;N;;;;;
+328D;CIRCLED IDEOGRAPH WOOD;So;0;L;<circle> 6728;;;;N;;;;;
+328E;CIRCLED IDEOGRAPH METAL;So;0;L;<circle> 91D1;;;;N;;;;;
+328F;CIRCLED IDEOGRAPH EARTH;So;0;L;<circle> 571F;;;;N;;;;;
+3290;CIRCLED IDEOGRAPH SUN;So;0;L;<circle> 65E5;;;;N;;;;;
+3291;CIRCLED IDEOGRAPH STOCK;So;0;L;<circle> 682A;;;;N;;;;;
+3292;CIRCLED IDEOGRAPH HAVE;So;0;L;<circle> 6709;;;;N;;;;;
+3293;CIRCLED IDEOGRAPH SOCIETY;So;0;L;<circle> 793E;;;;N;;;;;
+3294;CIRCLED IDEOGRAPH NAME;So;0;L;<circle> 540D;;;;N;;;;;
+3295;CIRCLED IDEOGRAPH SPECIAL;So;0;L;<circle> 7279;;;;N;;;;;
+3296;CIRCLED IDEOGRAPH FINANCIAL;So;0;L;<circle> 8CA1;;;;N;;;;;
+3297;CIRCLED IDEOGRAPH CONGRATULATION;So;0;L;<circle> 795D;;;;N;;;;;
+3298;CIRCLED IDEOGRAPH LABOR;So;0;L;<circle> 52B4;;;;N;;;;;
+3299;CIRCLED IDEOGRAPH SECRET;So;0;L;<circle> 79D8;;;;N;;;;;
+329A;CIRCLED IDEOGRAPH MALE;So;0;L;<circle> 7537;;;;N;;;;;
+329B;CIRCLED IDEOGRAPH FEMALE;So;0;L;<circle> 5973;;;;N;;;;;
+329C;CIRCLED IDEOGRAPH SUITABLE;So;0;L;<circle> 9069;;;;N;;;;;
+329D;CIRCLED IDEOGRAPH EXCELLENT;So;0;L;<circle> 512A;;;;N;;;;;
+329E;CIRCLED IDEOGRAPH PRINT;So;0;L;<circle> 5370;;;;N;;;;;
+329F;CIRCLED IDEOGRAPH ATTENTION;So;0;L;<circle> 6CE8;;;;N;;;;;
+32A0;CIRCLED IDEOGRAPH ITEM;So;0;L;<circle> 9805;;;;N;;;;;
+32A1;CIRCLED IDEOGRAPH REST;So;0;L;<circle> 4F11;;;;N;;;;;
+32A2;CIRCLED IDEOGRAPH COPY;So;0;L;<circle> 5199;;;;N;;;;;
+32A3;CIRCLED IDEOGRAPH CORRECT;So;0;L;<circle> 6B63;;;;N;;;;;
+32A4;CIRCLED IDEOGRAPH HIGH;So;0;L;<circle> 4E0A;;;;N;;;;;
+32A5;CIRCLED IDEOGRAPH CENTRE;So;0;L;<circle> 4E2D;;;;N;CIRCLED IDEOGRAPH CENTER;;;;
+32A6;CIRCLED IDEOGRAPH LOW;So;0;L;<circle> 4E0B;;;;N;;;;;
+32A7;CIRCLED IDEOGRAPH LEFT;So;0;L;<circle> 5DE6;;;;N;;;;;
+32A8;CIRCLED IDEOGRAPH RIGHT;So;0;L;<circle> 53F3;;;;N;;;;;
+32A9;CIRCLED IDEOGRAPH MEDICINE;So;0;L;<circle> 533B;;;;N;;;;;
+32AA;CIRCLED IDEOGRAPH RELIGION;So;0;L;<circle> 5B97;;;;N;;;;;
+32AB;CIRCLED IDEOGRAPH STUDY;So;0;L;<circle> 5B66;;;;N;;;;;
+32AC;CIRCLED IDEOGRAPH SUPERVISE;So;0;L;<circle> 76E3;;;;N;;;;;
+32AD;CIRCLED IDEOGRAPH ENTERPRISE;So;0;L;<circle> 4F01;;;;N;;;;;
+32AE;CIRCLED IDEOGRAPH RESOURCE;So;0;L;<circle> 8CC7;;;;N;;;;;
+32AF;CIRCLED IDEOGRAPH ALLIANCE;So;0;L;<circle> 5354;;;;N;;;;;
+32B0;CIRCLED IDEOGRAPH NIGHT;So;0;L;<circle> 591C;;;;N;;;;;
+32C0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY;So;0;L;<compat> 0031 6708;;;;N;;;;;
+32C1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY;So;0;L;<compat> 0032 6708;;;;N;;;;;
+32C2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH;So;0;L;<compat> 0033 6708;;;;N;;;;;
+32C3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL;So;0;L;<compat> 0034 6708;;;;N;;;;;
+32C4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY;So;0;L;<compat> 0035 6708;;;;N;;;;;
+32C5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE;So;0;L;<compat> 0036 6708;;;;N;;;;;
+32C6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY;So;0;L;<compat> 0037 6708;;;;N;;;;;
+32C7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST;So;0;L;<compat> 0038 6708;;;;N;;;;;
+32C8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER;So;0;L;<compat> 0039 6708;;;;N;;;;;
+32C9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER;So;0;L;<compat> 0031 0030 6708;;;;N;;;;;
+32CA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER;So;0;L;<compat> 0031 0031 6708;;;;N;;;;;
+32CB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER;So;0;L;<compat> 0031 0032 6708;;;;N;;;;;
+32D0;CIRCLED KATAKANA A;So;0;L;<circle> 30A2;;;;N;;;;;
+32D1;CIRCLED KATAKANA I;So;0;L;<circle> 30A4;;;;N;;;;;
+32D2;CIRCLED KATAKANA U;So;0;L;<circle> 30A6;;;;N;;;;;
+32D3;CIRCLED KATAKANA E;So;0;L;<circle> 30A8;;;;N;;;;;
+32D4;CIRCLED KATAKANA O;So;0;L;<circle> 30AA;;;;N;;;;;
+32D5;CIRCLED KATAKANA KA;So;0;L;<circle> 30AB;;;;N;;;;;
+32D6;CIRCLED KATAKANA KI;So;0;L;<circle> 30AD;;;;N;;;;;
+32D7;CIRCLED KATAKANA KU;So;0;L;<circle> 30AF;;;;N;;;;;
+32D8;CIRCLED KATAKANA KE;So;0;L;<circle> 30B1;;;;N;;;;;
+32D9;CIRCLED KATAKANA KO;So;0;L;<circle> 30B3;;;;N;;;;;
+32DA;CIRCLED KATAKANA SA;So;0;L;<circle> 30B5;;;;N;;;;;
+32DB;CIRCLED KATAKANA SI;So;0;L;<circle> 30B7;;;;N;;;;;
+32DC;CIRCLED KATAKANA SU;So;0;L;<circle> 30B9;;;;N;;;;;
+32DD;CIRCLED KATAKANA SE;So;0;L;<circle> 30BB;;;;N;;;;;
+32DE;CIRCLED KATAKANA SO;So;0;L;<circle> 30BD;;;;N;;;;;
+32DF;CIRCLED KATAKANA TA;So;0;L;<circle> 30BF;;;;N;;;;;
+32E0;CIRCLED KATAKANA TI;So;0;L;<circle> 30C1;;;;N;;;;;
+32E1;CIRCLED KATAKANA TU;So;0;L;<circle> 30C4;;;;N;;;;;
+32E2;CIRCLED KATAKANA TE;So;0;L;<circle> 30C6;;;;N;;;;;
+32E3;CIRCLED KATAKANA TO;So;0;L;<circle> 30C8;;;;N;;;;;
+32E4;CIRCLED KATAKANA NA;So;0;L;<circle> 30CA;;;;N;;;;;
+32E5;CIRCLED KATAKANA NI;So;0;L;<circle> 30CB;;;;N;;;;;
+32E6;CIRCLED KATAKANA NU;So;0;L;<circle> 30CC;;;;N;;;;;
+32E7;CIRCLED KATAKANA NE;So;0;L;<circle> 30CD;;;;N;;;;;
+32E8;CIRCLED KATAKANA NO;So;0;L;<circle> 30CE;;;;N;;;;;
+32E9;CIRCLED KATAKANA HA;So;0;L;<circle> 30CF;;;;N;;;;;
+32EA;CIRCLED KATAKANA HI;So;0;L;<circle> 30D2;;;;N;;;;;
+32EB;CIRCLED KATAKANA HU;So;0;L;<circle> 30D5;;;;N;;;;;
+32EC;CIRCLED KATAKANA HE;So;0;L;<circle> 30D8;;;;N;;;;;
+32ED;CIRCLED KATAKANA HO;So;0;L;<circle> 30DB;;;;N;;;;;
+32EE;CIRCLED KATAKANA MA;So;0;L;<circle> 30DE;;;;N;;;;;
+32EF;CIRCLED KATAKANA MI;So;0;L;<circle> 30DF;;;;N;;;;;
+32F0;CIRCLED KATAKANA MU;So;0;L;<circle> 30E0;;;;N;;;;;
+32F1;CIRCLED KATAKANA ME;So;0;L;<circle> 30E1;;;;N;;;;;
+32F2;CIRCLED KATAKANA MO;So;0;L;<circle> 30E2;;;;N;;;;;
+32F3;CIRCLED KATAKANA YA;So;0;L;<circle> 30E4;;;;N;;;;;
+32F4;CIRCLED KATAKANA YU;So;0;L;<circle> 30E6;;;;N;;;;;
+32F5;CIRCLED KATAKANA YO;So;0;L;<circle> 30E8;;;;N;;;;;
+32F6;CIRCLED KATAKANA RA;So;0;L;<circle> 30E9;;;;N;;;;;
+32F7;CIRCLED KATAKANA RI;So;0;L;<circle> 30EA;;;;N;;;;;
+32F8;CIRCLED KATAKANA RU;So;0;L;<circle> 30EB;;;;N;;;;;
+32F9;CIRCLED KATAKANA RE;So;0;L;<circle> 30EC;;;;N;;;;;
+32FA;CIRCLED KATAKANA RO;So;0;L;<circle> 30ED;;;;N;;;;;
+32FB;CIRCLED KATAKANA WA;So;0;L;<circle> 30EF;;;;N;;;;;
+32FC;CIRCLED KATAKANA WI;So;0;L;<circle> 30F0;;;;N;;;;;
+32FD;CIRCLED KATAKANA WE;So;0;L;<circle> 30F1;;;;N;;;;;
+32FE;CIRCLED KATAKANA WO;So;0;L;<circle> 30F2;;;;N;;;;;
+3300;SQUARE APAATO;So;0;L;<square> 30A2 30D1 30FC 30C8;;;;N;SQUARED APAATO;;;;
+3301;SQUARE ARUHUA;So;0;L;<square> 30A2 30EB 30D5 30A1;;;;N;SQUARED ARUHUA;;;;
+3302;SQUARE ANPEA;So;0;L;<square> 30A2 30F3 30DA 30A2;;;;N;SQUARED ANPEA;;;;
+3303;SQUARE AARU;So;0;L;<square> 30A2 30FC 30EB;;;;N;SQUARED AARU;;;;
+3304;SQUARE ININGU;So;0;L;<square> 30A4 30CB 30F3 30B0;;;;N;SQUARED ININGU;;;;
+3305;SQUARE INTI;So;0;L;<square> 30A4 30F3 30C1;;;;N;SQUARED INTI;;;;
+3306;SQUARE UON;So;0;L;<square> 30A6 30A9 30F3;;;;N;SQUARED UON;;;;
+3307;SQUARE ESUKUUDO;So;0;L;<square> 30A8 30B9 30AF 30FC 30C9;;;;N;SQUARED ESUKUUDO;;;;
+3308;SQUARE EEKAA;So;0;L;<square> 30A8 30FC 30AB 30FC;;;;N;SQUARED EEKAA;;;;
+3309;SQUARE ONSU;So;0;L;<square> 30AA 30F3 30B9;;;;N;SQUARED ONSU;;;;
+330A;SQUARE OOMU;So;0;L;<square> 30AA 30FC 30E0;;;;N;SQUARED OOMU;;;;
+330B;SQUARE KAIRI;So;0;L;<square> 30AB 30A4 30EA;;;;N;SQUARED KAIRI;;;;
+330C;SQUARE KARATTO;So;0;L;<square> 30AB 30E9 30C3 30C8;;;;N;SQUARED KARATTO;;;;
+330D;SQUARE KARORII;So;0;L;<square> 30AB 30ED 30EA 30FC;;;;N;SQUARED KARORII;;;;
+330E;SQUARE GARON;So;0;L;<square> 30AC 30ED 30F3;;;;N;SQUARED GARON;;;;
+330F;SQUARE GANMA;So;0;L;<square> 30AC 30F3 30DE;;;;N;SQUARED GANMA;;;;
+3310;SQUARE GIGA;So;0;L;<square> 30AE 30AC;;;;N;SQUARED GIGA;;;;
+3311;SQUARE GINII;So;0;L;<square> 30AE 30CB 30FC;;;;N;SQUARED GINII;;;;
+3312;SQUARE KYURII;So;0;L;<square> 30AD 30E5 30EA 30FC;;;;N;SQUARED KYURII;;;;
+3313;SQUARE GIRUDAA;So;0;L;<square> 30AE 30EB 30C0 30FC;;;;N;SQUARED GIRUDAA;;;;
+3314;SQUARE KIRO;So;0;L;<square> 30AD 30ED;;;;N;SQUARED KIRO;;;;
+3315;SQUARE KIROGURAMU;So;0;L;<square> 30AD 30ED 30B0 30E9 30E0;;;;N;SQUARED KIROGURAMU;;;;
+3316;SQUARE KIROMEETORU;So;0;L;<square> 30AD 30ED 30E1 30FC 30C8 30EB;;;;N;SQUARED KIROMEETORU;;;;
+3317;SQUARE KIROWATTO;So;0;L;<square> 30AD 30ED 30EF 30C3 30C8;;;;N;SQUARED KIROWATTO;;;;
+3318;SQUARE GURAMU;So;0;L;<square> 30B0 30E9 30E0;;;;N;SQUARED GURAMU;;;;
+3319;SQUARE GURAMUTON;So;0;L;<square> 30B0 30E9 30E0 30C8 30F3;;;;N;SQUARED GURAMUTON;;;;
+331A;SQUARE KURUZEIRO;So;0;L;<square> 30AF 30EB 30BC 30A4 30ED;;;;N;SQUARED KURUZEIRO;;;;
+331B;SQUARE KUROONE;So;0;L;<square> 30AF 30ED 30FC 30CD;;;;N;SQUARED KUROONE;;;;
+331C;SQUARE KEESU;So;0;L;<square> 30B1 30FC 30B9;;;;N;SQUARED KEESU;;;;
+331D;SQUARE KORUNA;So;0;L;<square> 30B3 30EB 30CA;;;;N;SQUARED KORUNA;;;;
+331E;SQUARE KOOPO;So;0;L;<square> 30B3 30FC 30DD;;;;N;SQUARED KOOPO;;;;
+331F;SQUARE SAIKURU;So;0;L;<square> 30B5 30A4 30AF 30EB;;;;N;SQUARED SAIKURU;;;;
+3320;SQUARE SANTIIMU;So;0;L;<square> 30B5 30F3 30C1 30FC 30E0;;;;N;SQUARED SANTIIMU;;;;
+3321;SQUARE SIRINGU;So;0;L;<square> 30B7 30EA 30F3 30B0;;;;N;SQUARED SIRINGU;;;;
+3322;SQUARE SENTI;So;0;L;<square> 30BB 30F3 30C1;;;;N;SQUARED SENTI;;;;
+3323;SQUARE SENTO;So;0;L;<square> 30BB 30F3 30C8;;;;N;SQUARED SENTO;;;;
+3324;SQUARE DAASU;So;0;L;<square> 30C0 30FC 30B9;;;;N;SQUARED DAASU;;;;
+3325;SQUARE DESI;So;0;L;<square> 30C7 30B7;;;;N;SQUARED DESI;;;;
+3326;SQUARE DORU;So;0;L;<square> 30C9 30EB;;;;N;SQUARED DORU;;;;
+3327;SQUARE TON;So;0;L;<square> 30C8 30F3;;;;N;SQUARED TON;;;;
+3328;SQUARE NANO;So;0;L;<square> 30CA 30CE;;;;N;SQUARED NANO;;;;
+3329;SQUARE NOTTO;So;0;L;<square> 30CE 30C3 30C8;;;;N;SQUARED NOTTO;;;;
+332A;SQUARE HAITU;So;0;L;<square> 30CF 30A4 30C4;;;;N;SQUARED HAITU;;;;
+332B;SQUARE PAASENTO;So;0;L;<square> 30D1 30FC 30BB 30F3 30C8;;;;N;SQUARED PAASENTO;;;;
+332C;SQUARE PAATU;So;0;L;<square> 30D1 30FC 30C4;;;;N;SQUARED PAATU;;;;
+332D;SQUARE BAARERU;So;0;L;<square> 30D0 30FC 30EC 30EB;;;;N;SQUARED BAARERU;;;;
+332E;SQUARE PIASUTORU;So;0;L;<square> 30D4 30A2 30B9 30C8 30EB;;;;N;SQUARED PIASUTORU;;;;
+332F;SQUARE PIKURU;So;0;L;<square> 30D4 30AF 30EB;;;;N;SQUARED PIKURU;;;;
+3330;SQUARE PIKO;So;0;L;<square> 30D4 30B3;;;;N;SQUARED PIKO;;;;
+3331;SQUARE BIRU;So;0;L;<square> 30D3 30EB;;;;N;SQUARED BIRU;;;;
+3332;SQUARE HUARADDO;So;0;L;<square> 30D5 30A1 30E9 30C3 30C9;;;;N;SQUARED HUARADDO;;;;
+3333;SQUARE HUIITO;So;0;L;<square> 30D5 30A3 30FC 30C8;;;;N;SQUARED HUIITO;;;;
+3334;SQUARE BUSSYERU;So;0;L;<square> 30D6 30C3 30B7 30A7 30EB;;;;N;SQUARED BUSSYERU;;;;
+3335;SQUARE HURAN;So;0;L;<square> 30D5 30E9 30F3;;;;N;SQUARED HURAN;;;;
+3336;SQUARE HEKUTAARU;So;0;L;<square> 30D8 30AF 30BF 30FC 30EB;;;;N;SQUARED HEKUTAARU;;;;
+3337;SQUARE PESO;So;0;L;<square> 30DA 30BD;;;;N;SQUARED PESO;;;;
+3338;SQUARE PENIHI;So;0;L;<square> 30DA 30CB 30D2;;;;N;SQUARED PENIHI;;;;
+3339;SQUARE HERUTU;So;0;L;<square> 30D8 30EB 30C4;;;;N;SQUARED HERUTU;;;;
+333A;SQUARE PENSU;So;0;L;<square> 30DA 30F3 30B9;;;;N;SQUARED PENSU;;;;
+333B;SQUARE PEEZI;So;0;L;<square> 30DA 30FC 30B8;;;;N;SQUARED PEEZI;;;;
+333C;SQUARE BEETA;So;0;L;<square> 30D9 30FC 30BF;;;;N;SQUARED BEETA;;;;
+333D;SQUARE POINTO;So;0;L;<square> 30DD 30A4 30F3 30C8;;;;N;SQUARED POINTO;;;;
+333E;SQUARE BORUTO;So;0;L;<square> 30DC 30EB 30C8;;;;N;SQUARED BORUTO;;;;
+333F;SQUARE HON;So;0;L;<square> 30DB 30F3;;;;N;SQUARED HON;;;;
+3340;SQUARE PONDO;So;0;L;<square> 30DD 30F3 30C9;;;;N;SQUARED PONDO;;;;
+3341;SQUARE HOORU;So;0;L;<square> 30DB 30FC 30EB;;;;N;SQUARED HOORU;;;;
+3342;SQUARE HOON;So;0;L;<square> 30DB 30FC 30F3;;;;N;SQUARED HOON;;;;
+3343;SQUARE MAIKURO;So;0;L;<square> 30DE 30A4 30AF 30ED;;;;N;SQUARED MAIKURO;;;;
+3344;SQUARE MAIRU;So;0;L;<square> 30DE 30A4 30EB;;;;N;SQUARED MAIRU;;;;
+3345;SQUARE MAHHA;So;0;L;<square> 30DE 30C3 30CF;;;;N;SQUARED MAHHA;;;;
+3346;SQUARE MARUKU;So;0;L;<square> 30DE 30EB 30AF;;;;N;SQUARED MARUKU;;;;
+3347;SQUARE MANSYON;So;0;L;<square> 30DE 30F3 30B7 30E7 30F3;;;;N;SQUARED MANSYON;;;;
+3348;SQUARE MIKURON;So;0;L;<square> 30DF 30AF 30ED 30F3;;;;N;SQUARED MIKURON;;;;
+3349;SQUARE MIRI;So;0;L;<square> 30DF 30EA;;;;N;SQUARED MIRI;;;;
+334A;SQUARE MIRIBAARU;So;0;L;<square> 30DF 30EA 30D0 30FC 30EB;;;;N;SQUARED MIRIBAARU;;;;
+334B;SQUARE MEGA;So;0;L;<square> 30E1 30AC;;;;N;SQUARED MEGA;;;;
+334C;SQUARE MEGATON;So;0;L;<square> 30E1 30AC 30C8 30F3;;;;N;SQUARED MEGATON;;;;
+334D;SQUARE MEETORU;So;0;L;<square> 30E1 30FC 30C8 30EB;;;;N;SQUARED MEETORU;;;;
+334E;SQUARE YAADO;So;0;L;<square> 30E4 30FC 30C9;;;;N;SQUARED YAADO;;;;
+334F;SQUARE YAARU;So;0;L;<square> 30E4 30FC 30EB;;;;N;SQUARED YAARU;;;;
+3350;SQUARE YUAN;So;0;L;<square> 30E6 30A2 30F3;;;;N;SQUARED YUAN;;;;
+3351;SQUARE RITTORU;So;0;L;<square> 30EA 30C3 30C8 30EB;;;;N;SQUARED RITTORU;;;;
+3352;SQUARE RIRA;So;0;L;<square> 30EA 30E9;;;;N;SQUARED RIRA;;;;
+3353;SQUARE RUPII;So;0;L;<square> 30EB 30D4 30FC;;;;N;SQUARED RUPII;;;;
+3354;SQUARE RUUBURU;So;0;L;<square> 30EB 30FC 30D6 30EB;;;;N;SQUARED RUUBURU;;;;
+3355;SQUARE REMU;So;0;L;<square> 30EC 30E0;;;;N;SQUARED REMU;;;;
+3356;SQUARE RENTOGEN;So;0;L;<square> 30EC 30F3 30C8 30B2 30F3;;;;N;SQUARED RENTOGEN;;;;
+3357;SQUARE WATTO;So;0;L;<square> 30EF 30C3 30C8;;;;N;SQUARED WATTO;;;;
+3358;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO;So;0;L;<compat> 0030 70B9;;;;N;;;;;
+3359;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE;So;0;L;<compat> 0031 70B9;;;;N;;;;;
+335A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO;So;0;L;<compat> 0032 70B9;;;;N;;;;;
+335B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE;So;0;L;<compat> 0033 70B9;;;;N;;;;;
+335C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR;So;0;L;<compat> 0034 70B9;;;;N;;;;;
+335D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE;So;0;L;<compat> 0035 70B9;;;;N;;;;;
+335E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX;So;0;L;<compat> 0036 70B9;;;;N;;;;;
+335F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN;So;0;L;<compat> 0037 70B9;;;;N;;;;;
+3360;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT;So;0;L;<compat> 0038 70B9;;;;N;;;;;
+3361;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE;So;0;L;<compat> 0039 70B9;;;;N;;;;;
+3362;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN;So;0;L;<compat> 0031 0030 70B9;;;;N;;;;;
+3363;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN;So;0;L;<compat> 0031 0031 70B9;;;;N;;;;;
+3364;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE;So;0;L;<compat> 0031 0032 70B9;;;;N;;;;;
+3365;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN;So;0;L;<compat> 0031 0033 70B9;;;;N;;;;;
+3366;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN;So;0;L;<compat> 0031 0034 70B9;;;;N;;;;;
+3367;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN;So;0;L;<compat> 0031 0035 70B9;;;;N;;;;;
+3368;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN;So;0;L;<compat> 0031 0036 70B9;;;;N;;;;;
+3369;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN;So;0;L;<compat> 0031 0037 70B9;;;;N;;;;;
+336A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN;So;0;L;<compat> 0031 0038 70B9;;;;N;;;;;
+336B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN;So;0;L;<compat> 0031 0039 70B9;;;;N;;;;;
+336C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY;So;0;L;<compat> 0032 0030 70B9;;;;N;;;;;
+336D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE;So;0;L;<compat> 0032 0031 70B9;;;;N;;;;;
+336E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO;So;0;L;<compat> 0032 0032 70B9;;;;N;;;;;
+336F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE;So;0;L;<compat> 0032 0033 70B9;;;;N;;;;;
+3370;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR;So;0;L;<compat> 0032 0034 70B9;;;;N;;;;;
+3371;SQUARE HPA;So;0;L;<square> 0068 0050 0061;;;;N;;;;;
+3372;SQUARE DA;So;0;L;<square> 0064 0061;;;;N;;;;;
+3373;SQUARE AU;So;0;L;<square> 0041 0055;;;;N;;;;;
+3374;SQUARE BAR;So;0;L;<square> 0062 0061 0072;;;;N;;;;;
+3375;SQUARE OV;So;0;L;<square> 006F 0056;;;;N;;;;;
+3376;SQUARE PC;So;0;L;<square> 0070 0063;;;;N;;;;;
+337B;SQUARE ERA NAME HEISEI;So;0;L;<square> 5E73 6210;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME HEISEI;;;;
+337C;SQUARE ERA NAME SYOUWA;So;0;L;<square> 662D 548C;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME SYOUWA;;;;
+337D;SQUARE ERA NAME TAISYOU;So;0;L;<square> 5927 6B63;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME TAISYOU;;;;
+337E;SQUARE ERA NAME MEIZI;So;0;L;<square> 660E 6CBB;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME MEIZI;;;;
+337F;SQUARE CORPORATION;So;0;L;<square> 682A 5F0F 4F1A 793E;;;;N;SQUARED FOUR IDEOGRAPHS CORPORATION;;;;
+3380;SQUARE PA AMPS;So;0;L;<square> 0070 0041;;;;N;SQUARED PA AMPS;;;;
+3381;SQUARE NA;So;0;L;<square> 006E 0041;;;;N;SQUARED NA;;;;
+3382;SQUARE MU A;So;0;L;<square> 03BC 0041;;;;N;SQUARED MU A;;;;
+3383;SQUARE MA;So;0;L;<square> 006D 0041;;;;N;SQUARED MA;;;;
+3384;SQUARE KA;So;0;L;<square> 006B 0041;;;;N;SQUARED KA;;;;
+3385;SQUARE KB;So;0;L;<square> 004B 0042;;;;N;SQUARED KB;;;;
+3386;SQUARE MB;So;0;L;<square> 004D 0042;;;;N;SQUARED MB;;;;
+3387;SQUARE GB;So;0;L;<square> 0047 0042;;;;N;SQUARED GB;;;;
+3388;SQUARE CAL;So;0;L;<square> 0063 0061 006C;;;;N;SQUARED CAL;;;;
+3389;SQUARE KCAL;So;0;L;<square> 006B 0063 0061 006C;;;;N;SQUARED KCAL;;;;
+338A;SQUARE PF;So;0;L;<square> 0070 0046;;;;N;SQUARED PF;;;;
+338B;SQUARE NF;So;0;L;<square> 006E 0046;;;;N;SQUARED NF;;;;
+338C;SQUARE MU F;So;0;L;<square> 03BC 0046;;;;N;SQUARED MU F;;;;
+338D;SQUARE MU G;So;0;L;<square> 03BC 0067;;;;N;SQUARED MU G;;;;
+338E;SQUARE MG;So;0;L;<square> 006D 0067;;;;N;SQUARED MG;;;;
+338F;SQUARE KG;So;0;L;<square> 006B 0067;;;;N;SQUARED KG;;;;
+3390;SQUARE HZ;So;0;L;<square> 0048 007A;;;;N;SQUARED HZ;;;;
+3391;SQUARE KHZ;So;0;L;<square> 006B 0048 007A;;;;N;SQUARED KHZ;;;;
+3392;SQUARE MHZ;So;0;L;<square> 004D 0048 007A;;;;N;SQUARED MHZ;;;;
+3393;SQUARE GHZ;So;0;L;<square> 0047 0048 007A;;;;N;SQUARED GHZ;;;;
+3394;SQUARE THZ;So;0;L;<square> 0054 0048 007A;;;;N;SQUARED THZ;;;;
+3395;SQUARE MU L;So;0;L;<square> 03BC 2113;;;;N;SQUARED MU L;;;;
+3396;SQUARE ML;So;0;L;<square> 006D 2113;;;;N;SQUARED ML;;;;
+3397;SQUARE DL;So;0;L;<square> 0064 2113;;;;N;SQUARED DL;;;;
+3398;SQUARE KL;So;0;L;<square> 006B 2113;;;;N;SQUARED KL;;;;
+3399;SQUARE FM;So;0;L;<square> 0066 006D;;;;N;SQUARED FM;;;;
+339A;SQUARE NM;So;0;L;<square> 006E 006D;;;;N;SQUARED NM;;;;
+339B;SQUARE MU M;So;0;L;<square> 03BC 006D;;;;N;SQUARED MU M;;;;
+339C;SQUARE MM;So;0;L;<square> 006D 006D;;;;N;SQUARED MM;;;;
+339D;SQUARE CM;So;0;L;<square> 0063 006D;;;;N;SQUARED CM;;;;
+339E;SQUARE KM;So;0;L;<square> 006B 006D;;;;N;SQUARED KM;;;;
+339F;SQUARE MM SQUARED;So;0;L;<square> 006D 006D 00B2;;;;N;SQUARED MM SQUARED;;;;
+33A0;SQUARE CM SQUARED;So;0;L;<square> 0063 006D 00B2;;;;N;SQUARED CM SQUARED;;;;
+33A1;SQUARE M SQUARED;So;0;L;<square> 006D 00B2;;;;N;SQUARED M SQUARED;;;;
+33A2;SQUARE KM SQUARED;So;0;L;<square> 006B 006D 00B2;;;;N;SQUARED KM SQUARED;;;;
+33A3;SQUARE MM CUBED;So;0;L;<square> 006D 006D 00B3;;;;N;SQUARED MM CUBED;;;;
+33A4;SQUARE CM CUBED;So;0;L;<square> 0063 006D 00B3;;;;N;SQUARED CM CUBED;;;;
+33A5;SQUARE M CUBED;So;0;L;<square> 006D 00B3;;;;N;SQUARED M CUBED;;;;
+33A6;SQUARE KM CUBED;So;0;L;<square> 006B 006D 00B3;;;;N;SQUARED KM CUBED;;;;
+33A7;SQUARE M OVER S;So;0;L;<square> 006D 2215 0073;;;;N;SQUARED M OVER S;;;;
+33A8;SQUARE M OVER S SQUARED;So;0;L;<square> 006D 2215 0073 00B2;;;;N;SQUARED M OVER S SQUARED;;;;
+33A9;SQUARE PA;So;0;L;<square> 0050 0061;;;;N;SQUARED PA;;;;
+33AA;SQUARE KPA;So;0;L;<square> 006B 0050 0061;;;;N;SQUARED KPA;;;;
+33AB;SQUARE MPA;So;0;L;<square> 004D 0050 0061;;;;N;SQUARED MPA;;;;
+33AC;SQUARE GPA;So;0;L;<square> 0047 0050 0061;;;;N;SQUARED GPA;;;;
+33AD;SQUARE RAD;So;0;L;<square> 0072 0061 0064;;;;N;SQUARED RAD;;;;
+33AE;SQUARE RAD OVER S;So;0;L;<square> 0072 0061 0064 2215 0073;;;;N;SQUARED RAD OVER S;;;;
+33AF;SQUARE RAD OVER S SQUARED;So;0;L;<square> 0072 0061 0064 2215 0073 00B2;;;;N;SQUARED RAD OVER S SQUARED;;;;
+33B0;SQUARE PS;So;0;L;<square> 0070 0073;;;;N;SQUARED PS;;;;
+33B1;SQUARE NS;So;0;L;<square> 006E 0073;;;;N;SQUARED NS;;;;
+33B2;SQUARE MU S;So;0;L;<square> 03BC 0073;;;;N;SQUARED MU S;;;;
+33B3;SQUARE MS;So;0;L;<square> 006D 0073;;;;N;SQUARED MS;;;;
+33B4;SQUARE PV;So;0;L;<square> 0070 0056;;;;N;SQUARED PV;;;;
+33B5;SQUARE NV;So;0;L;<square> 006E 0056;;;;N;SQUARED NV;;;;
+33B6;SQUARE MU V;So;0;L;<square> 03BC 0056;;;;N;SQUARED MU V;;;;
+33B7;SQUARE MV;So;0;L;<square> 006D 0056;;;;N;SQUARED MV;;;;
+33B8;SQUARE KV;So;0;L;<square> 006B 0056;;;;N;SQUARED KV;;;;
+33B9;SQUARE MV MEGA;So;0;L;<square> 004D 0056;;;;N;SQUARED MV MEGA;;;;
+33BA;SQUARE PW;So;0;L;<square> 0070 0057;;;;N;SQUARED PW;;;;
+33BB;SQUARE NW;So;0;L;<square> 006E 0057;;;;N;SQUARED NW;;;;
+33BC;SQUARE MU W;So;0;L;<square> 03BC 0057;;;;N;SQUARED MU W;;;;
+33BD;SQUARE MW;So;0;L;<square> 006D 0057;;;;N;SQUARED MW;;;;
+33BE;SQUARE KW;So;0;L;<square> 006B 0057;;;;N;SQUARED KW;;;;
+33BF;SQUARE MW MEGA;So;0;L;<square> 004D 0057;;;;N;SQUARED MW MEGA;;;;
+33C0;SQUARE K OHM;So;0;L;<square> 006B 03A9;;;;N;SQUARED K OHM;;;;
+33C1;SQUARE M OHM;So;0;L;<square> 004D 03A9;;;;N;SQUARED M OHM;;;;
+33C2;SQUARE AM;So;0;L;<square> 0061 002E 006D 002E;;;;N;SQUARED AM;;;;
+33C3;SQUARE BQ;So;0;L;<square> 0042 0071;;;;N;SQUARED BQ;;;;
+33C4;SQUARE CC;So;0;L;<square> 0063 0063;;;;N;SQUARED CC;;;;
+33C5;SQUARE CD;So;0;L;<square> 0063 0064;;;;N;SQUARED CD;;;;
+33C6;SQUARE C OVER KG;So;0;L;<square> 0043 2215 006B 0067;;;;N;SQUARED C OVER KG;;;;
+33C7;SQUARE CO;So;0;L;<square> 0043 006F 002E;;;;N;SQUARED CO;;;;
+33C8;SQUARE DB;So;0;L;<square> 0064 0042;;;;N;SQUARED DB;;;;
+33C9;SQUARE GY;So;0;L;<square> 0047 0079;;;;N;SQUARED GY;;;;
+33CA;SQUARE HA;So;0;L;<square> 0068 0061;;;;N;SQUARED HA;;;;
+33CB;SQUARE HP;So;0;L;<square> 0048 0050;;;;N;SQUARED HP;;;;
+33CC;SQUARE IN;So;0;L;<square> 0069 006E;;;;N;SQUARED IN;;;;
+33CD;SQUARE KK;So;0;L;<square> 004B 004B;;;;N;SQUARED KK;;;;
+33CE;SQUARE KM CAPITAL;So;0;L;<square> 004B 004D;;;;N;SQUARED KM CAPITAL;;;;
+33CF;SQUARE KT;So;0;L;<square> 006B 0074;;;;N;SQUARED KT;;;;
+33D0;SQUARE LM;So;0;L;<square> 006C 006D;;;;N;SQUARED LM;;;;
+33D1;SQUARE LN;So;0;L;<square> 006C 006E;;;;N;SQUARED LN;;;;
+33D2;SQUARE LOG;So;0;L;<square> 006C 006F 0067;;;;N;SQUARED LOG;;;;
+33D3;SQUARE LX;So;0;L;<square> 006C 0078;;;;N;SQUARED LX;;;;
+33D4;SQUARE MB SMALL;So;0;L;<square> 006D 0062;;;;N;SQUARED MB SMALL;;;;
+33D5;SQUARE MIL;So;0;L;<square> 006D 0069 006C;;;;N;SQUARED MIL;;;;
+33D6;SQUARE MOL;So;0;L;<square> 006D 006F 006C;;;;N;SQUARED MOL;;;;
+33D7;SQUARE PH;So;0;L;<square> 0050 0048;;;;N;SQUARED PH;;;;
+33D8;SQUARE PM;So;0;L;<square> 0070 002E 006D 002E;;;;N;SQUARED PM;;;;
+33D9;SQUARE PPM;So;0;L;<square> 0050 0050 004D;;;;N;SQUARED PPM;;;;
+33DA;SQUARE PR;So;0;L;<square> 0050 0052;;;;N;SQUARED PR;;;;
+33DB;SQUARE SR;So;0;L;<square> 0073 0072;;;;N;SQUARED SR;;;;
+33DC;SQUARE SV;So;0;L;<square> 0053 0076;;;;N;SQUARED SV;;;;
+33DD;SQUARE WB;So;0;L;<square> 0057 0062;;;;N;SQUARED WB;;;;
+33E0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE;So;0;L;<compat> 0031 65E5;;;;N;;;;;
+33E1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO;So;0;L;<compat> 0032 65E5;;;;N;;;;;
+33E2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE;So;0;L;<compat> 0033 65E5;;;;N;;;;;
+33E3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR;So;0;L;<compat> 0034 65E5;;;;N;;;;;
+33E4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE;So;0;L;<compat> 0035 65E5;;;;N;;;;;
+33E5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX;So;0;L;<compat> 0036 65E5;;;;N;;;;;
+33E6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN;So;0;L;<compat> 0037 65E5;;;;N;;;;;
+33E7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT;So;0;L;<compat> 0038 65E5;;;;N;;;;;
+33E8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE;So;0;L;<compat> 0039 65E5;;;;N;;;;;
+33E9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN;So;0;L;<compat> 0031 0030 65E5;;;;N;;;;;
+33EA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN;So;0;L;<compat> 0031 0031 65E5;;;;N;;;;;
+33EB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE;So;0;L;<compat> 0031 0032 65E5;;;;N;;;;;
+33EC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN;So;0;L;<compat> 0031 0033 65E5;;;;N;;;;;
+33ED;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN;So;0;L;<compat> 0031 0034 65E5;;;;N;;;;;
+33EE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN;So;0;L;<compat> 0031 0035 65E5;;;;N;;;;;
+33EF;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN;So;0;L;<compat> 0031 0036 65E5;;;;N;;;;;
+33F0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN;So;0;L;<compat> 0031 0037 65E5;;;;N;;;;;
+33F1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN;So;0;L;<compat> 0031 0038 65E5;;;;N;;;;;
+33F2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN;So;0;L;<compat> 0031 0039 65E5;;;;N;;;;;
+33F3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY;So;0;L;<compat> 0032 0030 65E5;;;;N;;;;;
+33F4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE;So;0;L;<compat> 0032 0031 65E5;;;;N;;;;;
+33F5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO;So;0;L;<compat> 0032 0032 65E5;;;;N;;;;;
+33F6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE;So;0;L;<compat> 0032 0033 65E5;;;;N;;;;;
+33F7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR;So;0;L;<compat> 0032 0034 65E5;;;;N;;;;;
+33F8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE;So;0;L;<compat> 0032 0035 65E5;;;;N;;;;;
+33F9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX;So;0;L;<compat> 0032 0036 65E5;;;;N;;;;;
+33FA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN;So;0;L;<compat> 0032 0037 65E5;;;;N;;;;;
+33FB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT;So;0;L;<compat> 0032 0038 65E5;;;;N;;;;;
+33FC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE;So;0;L;<compat> 0032 0039 65E5;;;;N;;;;;
+33FD;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY;So;0;L;<compat> 0033 0030 65E5;;;;N;;;;;
+33FE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE;So;0;L;<compat> 0033 0031 65E5;;;;N;;;;;
+3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;
+4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;;
+4E00;<CJK Ideograph, First>;Lo;0;L;;;;;N;;;;;
+9FA5;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;;
+A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;;
+A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;;
+A003;YI SYLLABLE IP;Lo;0;L;;;;;N;;;;;
+A004;YI SYLLABLE IET;Lo;0;L;;;;;N;;;;;
+A005;YI SYLLABLE IEX;Lo;0;L;;;;;N;;;;;
+A006;YI SYLLABLE IE;Lo;0;L;;;;;N;;;;;
+A007;YI SYLLABLE IEP;Lo;0;L;;;;;N;;;;;
+A008;YI SYLLABLE AT;Lo;0;L;;;;;N;;;;;
+A009;YI SYLLABLE AX;Lo;0;L;;;;;N;;;;;
+A00A;YI SYLLABLE A;Lo;0;L;;;;;N;;;;;
+A00B;YI SYLLABLE AP;Lo;0;L;;;;;N;;;;;
+A00C;YI SYLLABLE UOX;Lo;0;L;;;;;N;;;;;
+A00D;YI SYLLABLE UO;Lo;0;L;;;;;N;;;;;
+A00E;YI SYLLABLE UOP;Lo;0;L;;;;;N;;;;;
+A00F;YI SYLLABLE OT;Lo;0;L;;;;;N;;;;;
+A010;YI SYLLABLE OX;Lo;0;L;;;;;N;;;;;
+A011;YI SYLLABLE O;Lo;0;L;;;;;N;;;;;
+A012;YI SYLLABLE OP;Lo;0;L;;;;;N;;;;;
+A013;YI SYLLABLE EX;Lo;0;L;;;;;N;;;;;
+A014;YI SYLLABLE E;Lo;0;L;;;;;N;;;;;
+A015;YI SYLLABLE WU;Lo;0;L;;;;;N;;;;;
+A016;YI SYLLABLE BIT;Lo;0;L;;;;;N;;;;;
+A017;YI SYLLABLE BIX;Lo;0;L;;;;;N;;;;;
+A018;YI SYLLABLE BI;Lo;0;L;;;;;N;;;;;
+A019;YI SYLLABLE BIP;Lo;0;L;;;;;N;;;;;
+A01A;YI SYLLABLE BIET;Lo;0;L;;;;;N;;;;;
+A01B;YI SYLLABLE BIEX;Lo;0;L;;;;;N;;;;;
+A01C;YI SYLLABLE BIE;Lo;0;L;;;;;N;;;;;
+A01D;YI SYLLABLE BIEP;Lo;0;L;;;;;N;;;;;
+A01E;YI SYLLABLE BAT;Lo;0;L;;;;;N;;;;;
+A01F;YI SYLLABLE BAX;Lo;0;L;;;;;N;;;;;
+A020;YI SYLLABLE BA;Lo;0;L;;;;;N;;;;;
+A021;YI SYLLABLE BAP;Lo;0;L;;;;;N;;;;;
+A022;YI SYLLABLE BUOX;Lo;0;L;;;;;N;;;;;
+A023;YI SYLLABLE BUO;Lo;0;L;;;;;N;;;;;
+A024;YI SYLLABLE BUOP;Lo;0;L;;;;;N;;;;;
+A025;YI SYLLABLE BOT;Lo;0;L;;;;;N;;;;;
+A026;YI SYLLABLE BOX;Lo;0;L;;;;;N;;;;;
+A027;YI SYLLABLE BO;Lo;0;L;;;;;N;;;;;
+A028;YI SYLLABLE BOP;Lo;0;L;;;;;N;;;;;
+A029;YI SYLLABLE BEX;Lo;0;L;;;;;N;;;;;
+A02A;YI SYLLABLE BE;Lo;0;L;;;;;N;;;;;
+A02B;YI SYLLABLE BEP;Lo;0;L;;;;;N;;;;;
+A02C;YI SYLLABLE BUT;Lo;0;L;;;;;N;;;;;
+A02D;YI SYLLABLE BUX;Lo;0;L;;;;;N;;;;;
+A02E;YI SYLLABLE BU;Lo;0;L;;;;;N;;;;;
+A02F;YI SYLLABLE BUP;Lo;0;L;;;;;N;;;;;
+A030;YI SYLLABLE BURX;Lo;0;L;;;;;N;;;;;
+A031;YI SYLLABLE BUR;Lo;0;L;;;;;N;;;;;
+A032;YI SYLLABLE BYT;Lo;0;L;;;;;N;;;;;
+A033;YI SYLLABLE BYX;Lo;0;L;;;;;N;;;;;
+A034;YI SYLLABLE BY;Lo;0;L;;;;;N;;;;;
+A035;YI SYLLABLE BYP;Lo;0;L;;;;;N;;;;;
+A036;YI SYLLABLE BYRX;Lo;0;L;;;;;N;;;;;
+A037;YI SYLLABLE BYR;Lo;0;L;;;;;N;;;;;
+A038;YI SYLLABLE PIT;Lo;0;L;;;;;N;;;;;
+A039;YI SYLLABLE PIX;Lo;0;L;;;;;N;;;;;
+A03A;YI SYLLABLE PI;Lo;0;L;;;;;N;;;;;
+A03B;YI SYLLABLE PIP;Lo;0;L;;;;;N;;;;;
+A03C;YI SYLLABLE PIEX;Lo;0;L;;;;;N;;;;;
+A03D;YI SYLLABLE PIE;Lo;0;L;;;;;N;;;;;
+A03E;YI SYLLABLE PIEP;Lo;0;L;;;;;N;;;;;
+A03F;YI SYLLABLE PAT;Lo;0;L;;;;;N;;;;;
+A040;YI SYLLABLE PAX;Lo;0;L;;;;;N;;;;;
+A041;YI SYLLABLE PA;Lo;0;L;;;;;N;;;;;
+A042;YI SYLLABLE PAP;Lo;0;L;;;;;N;;;;;
+A043;YI SYLLABLE PUOX;Lo;0;L;;;;;N;;;;;
+A044;YI SYLLABLE PUO;Lo;0;L;;;;;N;;;;;
+A045;YI SYLLABLE PUOP;Lo;0;L;;;;;N;;;;;
+A046;YI SYLLABLE POT;Lo;0;L;;;;;N;;;;;
+A047;YI SYLLABLE POX;Lo;0;L;;;;;N;;;;;
+A048;YI SYLLABLE PO;Lo;0;L;;;;;N;;;;;
+A049;YI SYLLABLE POP;Lo;0;L;;;;;N;;;;;
+A04A;YI SYLLABLE PUT;Lo;0;L;;;;;N;;;;;
+A04B;YI SYLLABLE PUX;Lo;0;L;;;;;N;;;;;
+A04C;YI SYLLABLE PU;Lo;0;L;;;;;N;;;;;
+A04D;YI SYLLABLE PUP;Lo;0;L;;;;;N;;;;;
+A04E;YI SYLLABLE PURX;Lo;0;L;;;;;N;;;;;
+A04F;YI SYLLABLE PUR;Lo;0;L;;;;;N;;;;;
+A050;YI SYLLABLE PYT;Lo;0;L;;;;;N;;;;;
+A051;YI SYLLABLE PYX;Lo;0;L;;;;;N;;;;;
+A052;YI SYLLABLE PY;Lo;0;L;;;;;N;;;;;
+A053;YI SYLLABLE PYP;Lo;0;L;;;;;N;;;;;
+A054;YI SYLLABLE PYRX;Lo;0;L;;;;;N;;;;;
+A055;YI SYLLABLE PYR;Lo;0;L;;;;;N;;;;;
+A056;YI SYLLABLE BBIT;Lo;0;L;;;;;N;;;;;
+A057;YI SYLLABLE BBIX;Lo;0;L;;;;;N;;;;;
+A058;YI SYLLABLE BBI;Lo;0;L;;;;;N;;;;;
+A059;YI SYLLABLE BBIP;Lo;0;L;;;;;N;;;;;
+A05A;YI SYLLABLE BBIET;Lo;0;L;;;;;N;;;;;
+A05B;YI SYLLABLE BBIEX;Lo;0;L;;;;;N;;;;;
+A05C;YI SYLLABLE BBIE;Lo;0;L;;;;;N;;;;;
+A05D;YI SYLLABLE BBIEP;Lo;0;L;;;;;N;;;;;
+A05E;YI SYLLABLE BBAT;Lo;0;L;;;;;N;;;;;
+A05F;YI SYLLABLE BBAX;Lo;0;L;;;;;N;;;;;
+A060;YI SYLLABLE BBA;Lo;0;L;;;;;N;;;;;
+A061;YI SYLLABLE BBAP;Lo;0;L;;;;;N;;;;;
+A062;YI SYLLABLE BBUOX;Lo;0;L;;;;;N;;;;;
+A063;YI SYLLABLE BBUO;Lo;0;L;;;;;N;;;;;
+A064;YI SYLLABLE BBUOP;Lo;0;L;;;;;N;;;;;
+A065;YI SYLLABLE BBOT;Lo;0;L;;;;;N;;;;;
+A066;YI SYLLABLE BBOX;Lo;0;L;;;;;N;;;;;
+A067;YI SYLLABLE BBO;Lo;0;L;;;;;N;;;;;
+A068;YI SYLLABLE BBOP;Lo;0;L;;;;;N;;;;;
+A069;YI SYLLABLE BBEX;Lo;0;L;;;;;N;;;;;
+A06A;YI SYLLABLE BBE;Lo;0;L;;;;;N;;;;;
+A06B;YI SYLLABLE BBEP;Lo;0;L;;;;;N;;;;;
+A06C;YI SYLLABLE BBUT;Lo;0;L;;;;;N;;;;;
+A06D;YI SYLLABLE BBUX;Lo;0;L;;;;;N;;;;;
+A06E;YI SYLLABLE BBU;Lo;0;L;;;;;N;;;;;
+A06F;YI SYLLABLE BBUP;Lo;0;L;;;;;N;;;;;
+A070;YI SYLLABLE BBURX;Lo;0;L;;;;;N;;;;;
+A071;YI SYLLABLE BBUR;Lo;0;L;;;;;N;;;;;
+A072;YI SYLLABLE BBYT;Lo;0;L;;;;;N;;;;;
+A073;YI SYLLABLE BBYX;Lo;0;L;;;;;N;;;;;
+A074;YI SYLLABLE BBY;Lo;0;L;;;;;N;;;;;
+A075;YI SYLLABLE BBYP;Lo;0;L;;;;;N;;;;;
+A076;YI SYLLABLE NBIT;Lo;0;L;;;;;N;;;;;
+A077;YI SYLLABLE NBIX;Lo;0;L;;;;;N;;;;;
+A078;YI SYLLABLE NBI;Lo;0;L;;;;;N;;;;;
+A079;YI SYLLABLE NBIP;Lo;0;L;;;;;N;;;;;
+A07A;YI SYLLABLE NBIEX;Lo;0;L;;;;;N;;;;;
+A07B;YI SYLLABLE NBIE;Lo;0;L;;;;;N;;;;;
+A07C;YI SYLLABLE NBIEP;Lo;0;L;;;;;N;;;;;
+A07D;YI SYLLABLE NBAT;Lo;0;L;;;;;N;;;;;
+A07E;YI SYLLABLE NBAX;Lo;0;L;;;;;N;;;;;
+A07F;YI SYLLABLE NBA;Lo;0;L;;;;;N;;;;;
+A080;YI SYLLABLE NBAP;Lo;0;L;;;;;N;;;;;
+A081;YI SYLLABLE NBOT;Lo;0;L;;;;;N;;;;;
+A082;YI SYLLABLE NBOX;Lo;0;L;;;;;N;;;;;
+A083;YI SYLLABLE NBO;Lo;0;L;;;;;N;;;;;
+A084;YI SYLLABLE NBOP;Lo;0;L;;;;;N;;;;;
+A085;YI SYLLABLE NBUT;Lo;0;L;;;;;N;;;;;
+A086;YI SYLLABLE NBUX;Lo;0;L;;;;;N;;;;;
+A087;YI SYLLABLE NBU;Lo;0;L;;;;;N;;;;;
+A088;YI SYLLABLE NBUP;Lo;0;L;;;;;N;;;;;
+A089;YI SYLLABLE NBURX;Lo;0;L;;;;;N;;;;;
+A08A;YI SYLLABLE NBUR;Lo;0;L;;;;;N;;;;;
+A08B;YI SYLLABLE NBYT;Lo;0;L;;;;;N;;;;;
+A08C;YI SYLLABLE NBYX;Lo;0;L;;;;;N;;;;;
+A08D;YI SYLLABLE NBY;Lo;0;L;;;;;N;;;;;
+A08E;YI SYLLABLE NBYP;Lo;0;L;;;;;N;;;;;
+A08F;YI SYLLABLE NBYRX;Lo;0;L;;;;;N;;;;;
+A090;YI SYLLABLE NBYR;Lo;0;L;;;;;N;;;;;
+A091;YI SYLLABLE HMIT;Lo;0;L;;;;;N;;;;;
+A092;YI SYLLABLE HMIX;Lo;0;L;;;;;N;;;;;
+A093;YI SYLLABLE HMI;Lo;0;L;;;;;N;;;;;
+A094;YI SYLLABLE HMIP;Lo;0;L;;;;;N;;;;;
+A095;YI SYLLABLE HMIEX;Lo;0;L;;;;;N;;;;;
+A096;YI SYLLABLE HMIE;Lo;0;L;;;;;N;;;;;
+A097;YI SYLLABLE HMIEP;Lo;0;L;;;;;N;;;;;
+A098;YI SYLLABLE HMAT;Lo;0;L;;;;;N;;;;;
+A099;YI SYLLABLE HMAX;Lo;0;L;;;;;N;;;;;
+A09A;YI SYLLABLE HMA;Lo;0;L;;;;;N;;;;;
+A09B;YI SYLLABLE HMAP;Lo;0;L;;;;;N;;;;;
+A09C;YI SYLLABLE HMUOX;Lo;0;L;;;;;N;;;;;
+A09D;YI SYLLABLE HMUO;Lo;0;L;;;;;N;;;;;
+A09E;YI SYLLABLE HMUOP;Lo;0;L;;;;;N;;;;;
+A09F;YI SYLLABLE HMOT;Lo;0;L;;;;;N;;;;;
+A0A0;YI SYLLABLE HMOX;Lo;0;L;;;;;N;;;;;
+A0A1;YI SYLLABLE HMO;Lo;0;L;;;;;N;;;;;
+A0A2;YI SYLLABLE HMOP;Lo;0;L;;;;;N;;;;;
+A0A3;YI SYLLABLE HMUT;Lo;0;L;;;;;N;;;;;
+A0A4;YI SYLLABLE HMUX;Lo;0;L;;;;;N;;;;;
+A0A5;YI SYLLABLE HMU;Lo;0;L;;;;;N;;;;;
+A0A6;YI SYLLABLE HMUP;Lo;0;L;;;;;N;;;;;
+A0A7;YI SYLLABLE HMURX;Lo;0;L;;;;;N;;;;;
+A0A8;YI SYLLABLE HMUR;Lo;0;L;;;;;N;;;;;
+A0A9;YI SYLLABLE HMYX;Lo;0;L;;;;;N;;;;;
+A0AA;YI SYLLABLE HMY;Lo;0;L;;;;;N;;;;;
+A0AB;YI SYLLABLE HMYP;Lo;0;L;;;;;N;;;;;
+A0AC;YI SYLLABLE HMYRX;Lo;0;L;;;;;N;;;;;
+A0AD;YI SYLLABLE HMYR;Lo;0;L;;;;;N;;;;;
+A0AE;YI SYLLABLE MIT;Lo;0;L;;;;;N;;;;;
+A0AF;YI SYLLABLE MIX;Lo;0;L;;;;;N;;;;;
+A0B0;YI SYLLABLE MI;Lo;0;L;;;;;N;;;;;
+A0B1;YI SYLLABLE MIP;Lo;0;L;;;;;N;;;;;
+A0B2;YI SYLLABLE MIEX;Lo;0;L;;;;;N;;;;;
+A0B3;YI SYLLABLE MIE;Lo;0;L;;;;;N;;;;;
+A0B4;YI SYLLABLE MIEP;Lo;0;L;;;;;N;;;;;
+A0B5;YI SYLLABLE MAT;Lo;0;L;;;;;N;;;;;
+A0B6;YI SYLLABLE MAX;Lo;0;L;;;;;N;;;;;
+A0B7;YI SYLLABLE MA;Lo;0;L;;;;;N;;;;;
+A0B8;YI SYLLABLE MAP;Lo;0;L;;;;;N;;;;;
+A0B9;YI SYLLABLE MUOT;Lo;0;L;;;;;N;;;;;
+A0BA;YI SYLLABLE MUOX;Lo;0;L;;;;;N;;;;;
+A0BB;YI SYLLABLE MUO;Lo;0;L;;;;;N;;;;;
+A0BC;YI SYLLABLE MUOP;Lo;0;L;;;;;N;;;;;
+A0BD;YI SYLLABLE MOT;Lo;0;L;;;;;N;;;;;
+A0BE;YI SYLLABLE MOX;Lo;0;L;;;;;N;;;;;
+A0BF;YI SYLLABLE MO;Lo;0;L;;;;;N;;;;;
+A0C0;YI SYLLABLE MOP;Lo;0;L;;;;;N;;;;;
+A0C1;YI SYLLABLE MEX;Lo;0;L;;;;;N;;;;;
+A0C2;YI SYLLABLE ME;Lo;0;L;;;;;N;;;;;
+A0C3;YI SYLLABLE MUT;Lo;0;L;;;;;N;;;;;
+A0C4;YI SYLLABLE MUX;Lo;0;L;;;;;N;;;;;
+A0C5;YI SYLLABLE MU;Lo;0;L;;;;;N;;;;;
+A0C6;YI SYLLABLE MUP;Lo;0;L;;;;;N;;;;;
+A0C7;YI SYLLABLE MURX;Lo;0;L;;;;;N;;;;;
+A0C8;YI SYLLABLE MUR;Lo;0;L;;;;;N;;;;;
+A0C9;YI SYLLABLE MYT;Lo;0;L;;;;;N;;;;;
+A0CA;YI SYLLABLE MYX;Lo;0;L;;;;;N;;;;;
+A0CB;YI SYLLABLE MY;Lo;0;L;;;;;N;;;;;
+A0CC;YI SYLLABLE MYP;Lo;0;L;;;;;N;;;;;
+A0CD;YI SYLLABLE FIT;Lo;0;L;;;;;N;;;;;
+A0CE;YI SYLLABLE FIX;Lo;0;L;;;;;N;;;;;
+A0CF;YI SYLLABLE FI;Lo;0;L;;;;;N;;;;;
+A0D0;YI SYLLABLE FIP;Lo;0;L;;;;;N;;;;;
+A0D1;YI SYLLABLE FAT;Lo;0;L;;;;;N;;;;;
+A0D2;YI SYLLABLE FAX;Lo;0;L;;;;;N;;;;;
+A0D3;YI SYLLABLE FA;Lo;0;L;;;;;N;;;;;
+A0D4;YI SYLLABLE FAP;Lo;0;L;;;;;N;;;;;
+A0D5;YI SYLLABLE FOX;Lo;0;L;;;;;N;;;;;
+A0D6;YI SYLLABLE FO;Lo;0;L;;;;;N;;;;;
+A0D7;YI SYLLABLE FOP;Lo;0;L;;;;;N;;;;;
+A0D8;YI SYLLABLE FUT;Lo;0;L;;;;;N;;;;;
+A0D9;YI SYLLABLE FUX;Lo;0;L;;;;;N;;;;;
+A0DA;YI SYLLABLE FU;Lo;0;L;;;;;N;;;;;
+A0DB;YI SYLLABLE FUP;Lo;0;L;;;;;N;;;;;
+A0DC;YI SYLLABLE FURX;Lo;0;L;;;;;N;;;;;
+A0DD;YI SYLLABLE FUR;Lo;0;L;;;;;N;;;;;
+A0DE;YI SYLLABLE FYT;Lo;0;L;;;;;N;;;;;
+A0DF;YI SYLLABLE FYX;Lo;0;L;;;;;N;;;;;
+A0E0;YI SYLLABLE FY;Lo;0;L;;;;;N;;;;;
+A0E1;YI SYLLABLE FYP;Lo;0;L;;;;;N;;;;;
+A0E2;YI SYLLABLE VIT;Lo;0;L;;;;;N;;;;;
+A0E3;YI SYLLABLE VIX;Lo;0;L;;;;;N;;;;;
+A0E4;YI SYLLABLE VI;Lo;0;L;;;;;N;;;;;
+A0E5;YI SYLLABLE VIP;Lo;0;L;;;;;N;;;;;
+A0E6;YI SYLLABLE VIET;Lo;0;L;;;;;N;;;;;
+A0E7;YI SYLLABLE VIEX;Lo;0;L;;;;;N;;;;;
+A0E8;YI SYLLABLE VIE;Lo;0;L;;;;;N;;;;;
+A0E9;YI SYLLABLE VIEP;Lo;0;L;;;;;N;;;;;
+A0EA;YI SYLLABLE VAT;Lo;0;L;;;;;N;;;;;
+A0EB;YI SYLLABLE VAX;Lo;0;L;;;;;N;;;;;
+A0EC;YI SYLLABLE VA;Lo;0;L;;;;;N;;;;;
+A0ED;YI SYLLABLE VAP;Lo;0;L;;;;;N;;;;;
+A0EE;YI SYLLABLE VOT;Lo;0;L;;;;;N;;;;;
+A0EF;YI SYLLABLE VOX;Lo;0;L;;;;;N;;;;;
+A0F0;YI SYLLABLE VO;Lo;0;L;;;;;N;;;;;
+A0F1;YI SYLLABLE VOP;Lo;0;L;;;;;N;;;;;
+A0F2;YI SYLLABLE VEX;Lo;0;L;;;;;N;;;;;
+A0F3;YI SYLLABLE VEP;Lo;0;L;;;;;N;;;;;
+A0F4;YI SYLLABLE VUT;Lo;0;L;;;;;N;;;;;
+A0F5;YI SYLLABLE VUX;Lo;0;L;;;;;N;;;;;
+A0F6;YI SYLLABLE VU;Lo;0;L;;;;;N;;;;;
+A0F7;YI SYLLABLE VUP;Lo;0;L;;;;;N;;;;;
+A0F8;YI SYLLABLE VURX;Lo;0;L;;;;;N;;;;;
+A0F9;YI SYLLABLE VUR;Lo;0;L;;;;;N;;;;;
+A0FA;YI SYLLABLE VYT;Lo;0;L;;;;;N;;;;;
+A0FB;YI SYLLABLE VYX;Lo;0;L;;;;;N;;;;;
+A0FC;YI SYLLABLE VY;Lo;0;L;;;;;N;;;;;
+A0FD;YI SYLLABLE VYP;Lo;0;L;;;;;N;;;;;
+A0FE;YI SYLLABLE VYRX;Lo;0;L;;;;;N;;;;;
+A0FF;YI SYLLABLE VYR;Lo;0;L;;;;;N;;;;;
+A100;YI SYLLABLE DIT;Lo;0;L;;;;;N;;;;;
+A101;YI SYLLABLE DIX;Lo;0;L;;;;;N;;;;;
+A102;YI SYLLABLE DI;Lo;0;L;;;;;N;;;;;
+A103;YI SYLLABLE DIP;Lo;0;L;;;;;N;;;;;
+A104;YI SYLLABLE DIEX;Lo;0;L;;;;;N;;;;;
+A105;YI SYLLABLE DIE;Lo;0;L;;;;;N;;;;;
+A106;YI SYLLABLE DIEP;Lo;0;L;;;;;N;;;;;
+A107;YI SYLLABLE DAT;Lo;0;L;;;;;N;;;;;
+A108;YI SYLLABLE DAX;Lo;0;L;;;;;N;;;;;
+A109;YI SYLLABLE DA;Lo;0;L;;;;;N;;;;;
+A10A;YI SYLLABLE DAP;Lo;0;L;;;;;N;;;;;
+A10B;YI SYLLABLE DUOX;Lo;0;L;;;;;N;;;;;
+A10C;YI SYLLABLE DUO;Lo;0;L;;;;;N;;;;;
+A10D;YI SYLLABLE DOT;Lo;0;L;;;;;N;;;;;
+A10E;YI SYLLABLE DOX;Lo;0;L;;;;;N;;;;;
+A10F;YI SYLLABLE DO;Lo;0;L;;;;;N;;;;;
+A110;YI SYLLABLE DOP;Lo;0;L;;;;;N;;;;;
+A111;YI SYLLABLE DEX;Lo;0;L;;;;;N;;;;;
+A112;YI SYLLABLE DE;Lo;0;L;;;;;N;;;;;
+A113;YI SYLLABLE DEP;Lo;0;L;;;;;N;;;;;
+A114;YI SYLLABLE DUT;Lo;0;L;;;;;N;;;;;
+A115;YI SYLLABLE DUX;Lo;0;L;;;;;N;;;;;
+A116;YI SYLLABLE DU;Lo;0;L;;;;;N;;;;;
+A117;YI SYLLABLE DUP;Lo;0;L;;;;;N;;;;;
+A118;YI SYLLABLE DURX;Lo;0;L;;;;;N;;;;;
+A119;YI SYLLABLE DUR;Lo;0;L;;;;;N;;;;;
+A11A;YI SYLLABLE TIT;Lo;0;L;;;;;N;;;;;
+A11B;YI SYLLABLE TIX;Lo;0;L;;;;;N;;;;;
+A11C;YI SYLLABLE TI;Lo;0;L;;;;;N;;;;;
+A11D;YI SYLLABLE TIP;Lo;0;L;;;;;N;;;;;
+A11E;YI SYLLABLE TIEX;Lo;0;L;;;;;N;;;;;
+A11F;YI SYLLABLE TIE;Lo;0;L;;;;;N;;;;;
+A120;YI SYLLABLE TIEP;Lo;0;L;;;;;N;;;;;
+A121;YI SYLLABLE TAT;Lo;0;L;;;;;N;;;;;
+A122;YI SYLLABLE TAX;Lo;0;L;;;;;N;;;;;
+A123;YI SYLLABLE TA;Lo;0;L;;;;;N;;;;;
+A124;YI SYLLABLE TAP;Lo;0;L;;;;;N;;;;;
+A125;YI SYLLABLE TUOT;Lo;0;L;;;;;N;;;;;
+A126;YI SYLLABLE TUOX;Lo;0;L;;;;;N;;;;;
+A127;YI SYLLABLE TUO;Lo;0;L;;;;;N;;;;;
+A128;YI SYLLABLE TUOP;Lo;0;L;;;;;N;;;;;
+A129;YI SYLLABLE TOT;Lo;0;L;;;;;N;;;;;
+A12A;YI SYLLABLE TOX;Lo;0;L;;;;;N;;;;;
+A12B;YI SYLLABLE TO;Lo;0;L;;;;;N;;;;;
+A12C;YI SYLLABLE TOP;Lo;0;L;;;;;N;;;;;
+A12D;YI SYLLABLE TEX;Lo;0;L;;;;;N;;;;;
+A12E;YI SYLLABLE TE;Lo;0;L;;;;;N;;;;;
+A12F;YI SYLLABLE TEP;Lo;0;L;;;;;N;;;;;
+A130;YI SYLLABLE TUT;Lo;0;L;;;;;N;;;;;
+A131;YI SYLLABLE TUX;Lo;0;L;;;;;N;;;;;
+A132;YI SYLLABLE TU;Lo;0;L;;;;;N;;;;;
+A133;YI SYLLABLE TUP;Lo;0;L;;;;;N;;;;;
+A134;YI SYLLABLE TURX;Lo;0;L;;;;;N;;;;;
+A135;YI SYLLABLE TUR;Lo;0;L;;;;;N;;;;;
+A136;YI SYLLABLE DDIT;Lo;0;L;;;;;N;;;;;
+A137;YI SYLLABLE DDIX;Lo;0;L;;;;;N;;;;;
+A138;YI SYLLABLE DDI;Lo;0;L;;;;;N;;;;;
+A139;YI SYLLABLE DDIP;Lo;0;L;;;;;N;;;;;
+A13A;YI SYLLABLE DDIEX;Lo;0;L;;;;;N;;;;;
+A13B;YI SYLLABLE DDIE;Lo;0;L;;;;;N;;;;;
+A13C;YI SYLLABLE DDIEP;Lo;0;L;;;;;N;;;;;
+A13D;YI SYLLABLE DDAT;Lo;0;L;;;;;N;;;;;
+A13E;YI SYLLABLE DDAX;Lo;0;L;;;;;N;;;;;
+A13F;YI SYLLABLE DDA;Lo;0;L;;;;;N;;;;;
+A140;YI SYLLABLE DDAP;Lo;0;L;;;;;N;;;;;
+A141;YI SYLLABLE DDUOX;Lo;0;L;;;;;N;;;;;
+A142;YI SYLLABLE DDUO;Lo;0;L;;;;;N;;;;;
+A143;YI SYLLABLE DDUOP;Lo;0;L;;;;;N;;;;;
+A144;YI SYLLABLE DDOT;Lo;0;L;;;;;N;;;;;
+A145;YI SYLLABLE DDOX;Lo;0;L;;;;;N;;;;;
+A146;YI SYLLABLE DDO;Lo;0;L;;;;;N;;;;;
+A147;YI SYLLABLE DDOP;Lo;0;L;;;;;N;;;;;
+A148;YI SYLLABLE DDEX;Lo;0;L;;;;;N;;;;;
+A149;YI SYLLABLE DDE;Lo;0;L;;;;;N;;;;;
+A14A;YI SYLLABLE DDEP;Lo;0;L;;;;;N;;;;;
+A14B;YI SYLLABLE DDUT;Lo;0;L;;;;;N;;;;;
+A14C;YI SYLLABLE DDUX;Lo;0;L;;;;;N;;;;;
+A14D;YI SYLLABLE DDU;Lo;0;L;;;;;N;;;;;
+A14E;YI SYLLABLE DDUP;Lo;0;L;;;;;N;;;;;
+A14F;YI SYLLABLE DDURX;Lo;0;L;;;;;N;;;;;
+A150;YI SYLLABLE DDUR;Lo;0;L;;;;;N;;;;;
+A151;YI SYLLABLE NDIT;Lo;0;L;;;;;N;;;;;
+A152;YI SYLLABLE NDIX;Lo;0;L;;;;;N;;;;;
+A153;YI SYLLABLE NDI;Lo;0;L;;;;;N;;;;;
+A154;YI SYLLABLE NDIP;Lo;0;L;;;;;N;;;;;
+A155;YI SYLLABLE NDIEX;Lo;0;L;;;;;N;;;;;
+A156;YI SYLLABLE NDIE;Lo;0;L;;;;;N;;;;;
+A157;YI SYLLABLE NDAT;Lo;0;L;;;;;N;;;;;
+A158;YI SYLLABLE NDAX;Lo;0;L;;;;;N;;;;;
+A159;YI SYLLABLE NDA;Lo;0;L;;;;;N;;;;;
+A15A;YI SYLLABLE NDAP;Lo;0;L;;;;;N;;;;;
+A15B;YI SYLLABLE NDOT;Lo;0;L;;;;;N;;;;;
+A15C;YI SYLLABLE NDOX;Lo;0;L;;;;;N;;;;;
+A15D;YI SYLLABLE NDO;Lo;0;L;;;;;N;;;;;
+A15E;YI SYLLABLE NDOP;Lo;0;L;;;;;N;;;;;
+A15F;YI SYLLABLE NDEX;Lo;0;L;;;;;N;;;;;
+A160;YI SYLLABLE NDE;Lo;0;L;;;;;N;;;;;
+A161;YI SYLLABLE NDEP;Lo;0;L;;;;;N;;;;;
+A162;YI SYLLABLE NDUT;Lo;0;L;;;;;N;;;;;
+A163;YI SYLLABLE NDUX;Lo;0;L;;;;;N;;;;;
+A164;YI SYLLABLE NDU;Lo;0;L;;;;;N;;;;;
+A165;YI SYLLABLE NDUP;Lo;0;L;;;;;N;;;;;
+A166;YI SYLLABLE NDURX;Lo;0;L;;;;;N;;;;;
+A167;YI SYLLABLE NDUR;Lo;0;L;;;;;N;;;;;
+A168;YI SYLLABLE HNIT;Lo;0;L;;;;;N;;;;;
+A169;YI SYLLABLE HNIX;Lo;0;L;;;;;N;;;;;
+A16A;YI SYLLABLE HNI;Lo;0;L;;;;;N;;;;;
+A16B;YI SYLLABLE HNIP;Lo;0;L;;;;;N;;;;;
+A16C;YI SYLLABLE HNIET;Lo;0;L;;;;;N;;;;;
+A16D;YI SYLLABLE HNIEX;Lo;0;L;;;;;N;;;;;
+A16E;YI SYLLABLE HNIE;Lo;0;L;;;;;N;;;;;
+A16F;YI SYLLABLE HNIEP;Lo;0;L;;;;;N;;;;;
+A170;YI SYLLABLE HNAT;Lo;0;L;;;;;N;;;;;
+A171;YI SYLLABLE HNAX;Lo;0;L;;;;;N;;;;;
+A172;YI SYLLABLE HNA;Lo;0;L;;;;;N;;;;;
+A173;YI SYLLABLE HNAP;Lo;0;L;;;;;N;;;;;
+A174;YI SYLLABLE HNUOX;Lo;0;L;;;;;N;;;;;
+A175;YI SYLLABLE HNUO;Lo;0;L;;;;;N;;;;;
+A176;YI SYLLABLE HNOT;Lo;0;L;;;;;N;;;;;
+A177;YI SYLLABLE HNOX;Lo;0;L;;;;;N;;;;;
+A178;YI SYLLABLE HNOP;Lo;0;L;;;;;N;;;;;
+A179;YI SYLLABLE HNEX;Lo;0;L;;;;;N;;;;;
+A17A;YI SYLLABLE HNE;Lo;0;L;;;;;N;;;;;
+A17B;YI SYLLABLE HNEP;Lo;0;L;;;;;N;;;;;
+A17C;YI SYLLABLE HNUT;Lo;0;L;;;;;N;;;;;
+A17D;YI SYLLABLE NIT;Lo;0;L;;;;;N;;;;;
+A17E;YI SYLLABLE NIX;Lo;0;L;;;;;N;;;;;
+A17F;YI SYLLABLE NI;Lo;0;L;;;;;N;;;;;
+A180;YI SYLLABLE NIP;Lo;0;L;;;;;N;;;;;
+A181;YI SYLLABLE NIEX;Lo;0;L;;;;;N;;;;;
+A182;YI SYLLABLE NIE;Lo;0;L;;;;;N;;;;;
+A183;YI SYLLABLE NIEP;Lo;0;L;;;;;N;;;;;
+A184;YI SYLLABLE NAX;Lo;0;L;;;;;N;;;;;
+A185;YI SYLLABLE NA;Lo;0;L;;;;;N;;;;;
+A186;YI SYLLABLE NAP;Lo;0;L;;;;;N;;;;;
+A187;YI SYLLABLE NUOX;Lo;0;L;;;;;N;;;;;
+A188;YI SYLLABLE NUO;Lo;0;L;;;;;N;;;;;
+A189;YI SYLLABLE NUOP;Lo;0;L;;;;;N;;;;;
+A18A;YI SYLLABLE NOT;Lo;0;L;;;;;N;;;;;
+A18B;YI SYLLABLE NOX;Lo;0;L;;;;;N;;;;;
+A18C;YI SYLLABLE NO;Lo;0;L;;;;;N;;;;;
+A18D;YI SYLLABLE NOP;Lo;0;L;;;;;N;;;;;
+A18E;YI SYLLABLE NEX;Lo;0;L;;;;;N;;;;;
+A18F;YI SYLLABLE NE;Lo;0;L;;;;;N;;;;;
+A190;YI SYLLABLE NEP;Lo;0;L;;;;;N;;;;;
+A191;YI SYLLABLE NUT;Lo;0;L;;;;;N;;;;;
+A192;YI SYLLABLE NUX;Lo;0;L;;;;;N;;;;;
+A193;YI SYLLABLE NU;Lo;0;L;;;;;N;;;;;
+A194;YI SYLLABLE NUP;Lo;0;L;;;;;N;;;;;
+A195;YI SYLLABLE NURX;Lo;0;L;;;;;N;;;;;
+A196;YI SYLLABLE NUR;Lo;0;L;;;;;N;;;;;
+A197;YI SYLLABLE HLIT;Lo;0;L;;;;;N;;;;;
+A198;YI SYLLABLE HLIX;Lo;0;L;;;;;N;;;;;
+A199;YI SYLLABLE HLI;Lo;0;L;;;;;N;;;;;
+A19A;YI SYLLABLE HLIP;Lo;0;L;;;;;N;;;;;
+A19B;YI SYLLABLE HLIEX;Lo;0;L;;;;;N;;;;;
+A19C;YI SYLLABLE HLIE;Lo;0;L;;;;;N;;;;;
+A19D;YI SYLLABLE HLIEP;Lo;0;L;;;;;N;;;;;
+A19E;YI SYLLABLE HLAT;Lo;0;L;;;;;N;;;;;
+A19F;YI SYLLABLE HLAX;Lo;0;L;;;;;N;;;;;
+A1A0;YI SYLLABLE HLA;Lo;0;L;;;;;N;;;;;
+A1A1;YI SYLLABLE HLAP;Lo;0;L;;;;;N;;;;;
+A1A2;YI SYLLABLE HLUOX;Lo;0;L;;;;;N;;;;;
+A1A3;YI SYLLABLE HLUO;Lo;0;L;;;;;N;;;;;
+A1A4;YI SYLLABLE HLUOP;Lo;0;L;;;;;N;;;;;
+A1A5;YI SYLLABLE HLOX;Lo;0;L;;;;;N;;;;;
+A1A6;YI SYLLABLE HLO;Lo;0;L;;;;;N;;;;;
+A1A7;YI SYLLABLE HLOP;Lo;0;L;;;;;N;;;;;
+A1A8;YI SYLLABLE HLEX;Lo;0;L;;;;;N;;;;;
+A1A9;YI SYLLABLE HLE;Lo;0;L;;;;;N;;;;;
+A1AA;YI SYLLABLE HLEP;Lo;0;L;;;;;N;;;;;
+A1AB;YI SYLLABLE HLUT;Lo;0;L;;;;;N;;;;;
+A1AC;YI SYLLABLE HLUX;Lo;0;L;;;;;N;;;;;
+A1AD;YI SYLLABLE HLU;Lo;0;L;;;;;N;;;;;
+A1AE;YI SYLLABLE HLUP;Lo;0;L;;;;;N;;;;;
+A1AF;YI SYLLABLE HLURX;Lo;0;L;;;;;N;;;;;
+A1B0;YI SYLLABLE HLUR;Lo;0;L;;;;;N;;;;;
+A1B1;YI SYLLABLE HLYT;Lo;0;L;;;;;N;;;;;
+A1B2;YI SYLLABLE HLYX;Lo;0;L;;;;;N;;;;;
+A1B3;YI SYLLABLE HLY;Lo;0;L;;;;;N;;;;;
+A1B4;YI SYLLABLE HLYP;Lo;0;L;;;;;N;;;;;
+A1B5;YI SYLLABLE HLYRX;Lo;0;L;;;;;N;;;;;
+A1B6;YI SYLLABLE HLYR;Lo;0;L;;;;;N;;;;;
+A1B7;YI SYLLABLE LIT;Lo;0;L;;;;;N;;;;;
+A1B8;YI SYLLABLE LIX;Lo;0;L;;;;;N;;;;;
+A1B9;YI SYLLABLE LI;Lo;0;L;;;;;N;;;;;
+A1BA;YI SYLLABLE LIP;Lo;0;L;;;;;N;;;;;
+A1BB;YI SYLLABLE LIET;Lo;0;L;;;;;N;;;;;
+A1BC;YI SYLLABLE LIEX;Lo;0;L;;;;;N;;;;;
+A1BD;YI SYLLABLE LIE;Lo;0;L;;;;;N;;;;;
+A1BE;YI SYLLABLE LIEP;Lo;0;L;;;;;N;;;;;
+A1BF;YI SYLLABLE LAT;Lo;0;L;;;;;N;;;;;
+A1C0;YI SYLLABLE LAX;Lo;0;L;;;;;N;;;;;
+A1C1;YI SYLLABLE LA;Lo;0;L;;;;;N;;;;;
+A1C2;YI SYLLABLE LAP;Lo;0;L;;;;;N;;;;;
+A1C3;YI SYLLABLE LUOT;Lo;0;L;;;;;N;;;;;
+A1C4;YI SYLLABLE LUOX;Lo;0;L;;;;;N;;;;;
+A1C5;YI SYLLABLE LUO;Lo;0;L;;;;;N;;;;;
+A1C6;YI SYLLABLE LUOP;Lo;0;L;;;;;N;;;;;
+A1C7;YI SYLLABLE LOT;Lo;0;L;;;;;N;;;;;
+A1C8;YI SYLLABLE LOX;Lo;0;L;;;;;N;;;;;
+A1C9;YI SYLLABLE LO;Lo;0;L;;;;;N;;;;;
+A1CA;YI SYLLABLE LOP;Lo;0;L;;;;;N;;;;;
+A1CB;YI SYLLABLE LEX;Lo;0;L;;;;;N;;;;;
+A1CC;YI SYLLABLE LE;Lo;0;L;;;;;N;;;;;
+A1CD;YI SYLLABLE LEP;Lo;0;L;;;;;N;;;;;
+A1CE;YI SYLLABLE LUT;Lo;0;L;;;;;N;;;;;
+A1CF;YI SYLLABLE LUX;Lo;0;L;;;;;N;;;;;
+A1D0;YI SYLLABLE LU;Lo;0;L;;;;;N;;;;;
+A1D1;YI SYLLABLE LUP;Lo;0;L;;;;;N;;;;;
+A1D2;YI SYLLABLE LURX;Lo;0;L;;;;;N;;;;;
+A1D3;YI SYLLABLE LUR;Lo;0;L;;;;;N;;;;;
+A1D4;YI SYLLABLE LYT;Lo;0;L;;;;;N;;;;;
+A1D5;YI SYLLABLE LYX;Lo;0;L;;;;;N;;;;;
+A1D6;YI SYLLABLE LY;Lo;0;L;;;;;N;;;;;
+A1D7;YI SYLLABLE LYP;Lo;0;L;;;;;N;;;;;
+A1D8;YI SYLLABLE LYRX;Lo;0;L;;;;;N;;;;;
+A1D9;YI SYLLABLE LYR;Lo;0;L;;;;;N;;;;;
+A1DA;YI SYLLABLE GIT;Lo;0;L;;;;;N;;;;;
+A1DB;YI SYLLABLE GIX;Lo;0;L;;;;;N;;;;;
+A1DC;YI SYLLABLE GI;Lo;0;L;;;;;N;;;;;
+A1DD;YI SYLLABLE GIP;Lo;0;L;;;;;N;;;;;
+A1DE;YI SYLLABLE GIET;Lo;0;L;;;;;N;;;;;
+A1DF;YI SYLLABLE GIEX;Lo;0;L;;;;;N;;;;;
+A1E0;YI SYLLABLE GIE;Lo;0;L;;;;;N;;;;;
+A1E1;YI SYLLABLE GIEP;Lo;0;L;;;;;N;;;;;
+A1E2;YI SYLLABLE GAT;Lo;0;L;;;;;N;;;;;
+A1E3;YI SYLLABLE GAX;Lo;0;L;;;;;N;;;;;
+A1E4;YI SYLLABLE GA;Lo;0;L;;;;;N;;;;;
+A1E5;YI SYLLABLE GAP;Lo;0;L;;;;;N;;;;;
+A1E6;YI SYLLABLE GUOT;Lo;0;L;;;;;N;;;;;
+A1E7;YI SYLLABLE GUOX;Lo;0;L;;;;;N;;;;;
+A1E8;YI SYLLABLE GUO;Lo;0;L;;;;;N;;;;;
+A1E9;YI SYLLABLE GUOP;Lo;0;L;;;;;N;;;;;
+A1EA;YI SYLLABLE GOT;Lo;0;L;;;;;N;;;;;
+A1EB;YI SYLLABLE GOX;Lo;0;L;;;;;N;;;;;
+A1EC;YI SYLLABLE GO;Lo;0;L;;;;;N;;;;;
+A1ED;YI SYLLABLE GOP;Lo;0;L;;;;;N;;;;;
+A1EE;YI SYLLABLE GET;Lo;0;L;;;;;N;;;;;
+A1EF;YI SYLLABLE GEX;Lo;0;L;;;;;N;;;;;
+A1F0;YI SYLLABLE GE;Lo;0;L;;;;;N;;;;;
+A1F1;YI SYLLABLE GEP;Lo;0;L;;;;;N;;;;;
+A1F2;YI SYLLABLE GUT;Lo;0;L;;;;;N;;;;;
+A1F3;YI SYLLABLE GUX;Lo;0;L;;;;;N;;;;;
+A1F4;YI SYLLABLE GU;Lo;0;L;;;;;N;;;;;
+A1F5;YI SYLLABLE GUP;Lo;0;L;;;;;N;;;;;
+A1F6;YI SYLLABLE GURX;Lo;0;L;;;;;N;;;;;
+A1F7;YI SYLLABLE GUR;Lo;0;L;;;;;N;;;;;
+A1F8;YI SYLLABLE KIT;Lo;0;L;;;;;N;;;;;
+A1F9;YI SYLLABLE KIX;Lo;0;L;;;;;N;;;;;
+A1FA;YI SYLLABLE KI;Lo;0;L;;;;;N;;;;;
+A1FB;YI SYLLABLE KIP;Lo;0;L;;;;;N;;;;;
+A1FC;YI SYLLABLE KIEX;Lo;0;L;;;;;N;;;;;
+A1FD;YI SYLLABLE KIE;Lo;0;L;;;;;N;;;;;
+A1FE;YI SYLLABLE KIEP;Lo;0;L;;;;;N;;;;;
+A1FF;YI SYLLABLE KAT;Lo;0;L;;;;;N;;;;;
+A200;YI SYLLABLE KAX;Lo;0;L;;;;;N;;;;;
+A201;YI SYLLABLE KA;Lo;0;L;;;;;N;;;;;
+A202;YI SYLLABLE KAP;Lo;0;L;;;;;N;;;;;
+A203;YI SYLLABLE KUOX;Lo;0;L;;;;;N;;;;;
+A204;YI SYLLABLE KUO;Lo;0;L;;;;;N;;;;;
+A205;YI SYLLABLE KUOP;Lo;0;L;;;;;N;;;;;
+A206;YI SYLLABLE KOT;Lo;0;L;;;;;N;;;;;
+A207;YI SYLLABLE KOX;Lo;0;L;;;;;N;;;;;
+A208;YI SYLLABLE KO;Lo;0;L;;;;;N;;;;;
+A209;YI SYLLABLE KOP;Lo;0;L;;;;;N;;;;;
+A20A;YI SYLLABLE KET;Lo;0;L;;;;;N;;;;;
+A20B;YI SYLLABLE KEX;Lo;0;L;;;;;N;;;;;
+A20C;YI SYLLABLE KE;Lo;0;L;;;;;N;;;;;
+A20D;YI SYLLABLE KEP;Lo;0;L;;;;;N;;;;;
+A20E;YI SYLLABLE KUT;Lo;0;L;;;;;N;;;;;
+A20F;YI SYLLABLE KUX;Lo;0;L;;;;;N;;;;;
+A210;YI SYLLABLE KU;Lo;0;L;;;;;N;;;;;
+A211;YI SYLLABLE KUP;Lo;0;L;;;;;N;;;;;
+A212;YI SYLLABLE KURX;Lo;0;L;;;;;N;;;;;
+A213;YI SYLLABLE KUR;Lo;0;L;;;;;N;;;;;
+A214;YI SYLLABLE GGIT;Lo;0;L;;;;;N;;;;;
+A215;YI SYLLABLE GGIX;Lo;0;L;;;;;N;;;;;
+A216;YI SYLLABLE GGI;Lo;0;L;;;;;N;;;;;
+A217;YI SYLLABLE GGIEX;Lo;0;L;;;;;N;;;;;
+A218;YI SYLLABLE GGIE;Lo;0;L;;;;;N;;;;;
+A219;YI SYLLABLE GGIEP;Lo;0;L;;;;;N;;;;;
+A21A;YI SYLLABLE GGAT;Lo;0;L;;;;;N;;;;;
+A21B;YI SYLLABLE GGAX;Lo;0;L;;;;;N;;;;;
+A21C;YI SYLLABLE GGA;Lo;0;L;;;;;N;;;;;
+A21D;YI SYLLABLE GGAP;Lo;0;L;;;;;N;;;;;
+A21E;YI SYLLABLE GGUOT;Lo;0;L;;;;;N;;;;;
+A21F;YI SYLLABLE GGUOX;Lo;0;L;;;;;N;;;;;
+A220;YI SYLLABLE GGUO;Lo;0;L;;;;;N;;;;;
+A221;YI SYLLABLE GGUOP;Lo;0;L;;;;;N;;;;;
+A222;YI SYLLABLE GGOT;Lo;0;L;;;;;N;;;;;
+A223;YI SYLLABLE GGOX;Lo;0;L;;;;;N;;;;;
+A224;YI SYLLABLE GGO;Lo;0;L;;;;;N;;;;;
+A225;YI SYLLABLE GGOP;Lo;0;L;;;;;N;;;;;
+A226;YI SYLLABLE GGET;Lo;0;L;;;;;N;;;;;
+A227;YI SYLLABLE GGEX;Lo;0;L;;;;;N;;;;;
+A228;YI SYLLABLE GGE;Lo;0;L;;;;;N;;;;;
+A229;YI SYLLABLE GGEP;Lo;0;L;;;;;N;;;;;
+A22A;YI SYLLABLE GGUT;Lo;0;L;;;;;N;;;;;
+A22B;YI SYLLABLE GGUX;Lo;0;L;;;;;N;;;;;
+A22C;YI SYLLABLE GGU;Lo;0;L;;;;;N;;;;;
+A22D;YI SYLLABLE GGUP;Lo;0;L;;;;;N;;;;;
+A22E;YI SYLLABLE GGURX;Lo;0;L;;;;;N;;;;;
+A22F;YI SYLLABLE GGUR;Lo;0;L;;;;;N;;;;;
+A230;YI SYLLABLE MGIEX;Lo;0;L;;;;;N;;;;;
+A231;YI SYLLABLE MGIE;Lo;0;L;;;;;N;;;;;
+A232;YI SYLLABLE MGAT;Lo;0;L;;;;;N;;;;;
+A233;YI SYLLABLE MGAX;Lo;0;L;;;;;N;;;;;
+A234;YI SYLLABLE MGA;Lo;0;L;;;;;N;;;;;
+A235;YI SYLLABLE MGAP;Lo;0;L;;;;;N;;;;;
+A236;YI SYLLABLE MGUOX;Lo;0;L;;;;;N;;;;;
+A237;YI SYLLABLE MGUO;Lo;0;L;;;;;N;;;;;
+A238;YI SYLLABLE MGUOP;Lo;0;L;;;;;N;;;;;
+A239;YI SYLLABLE MGOT;Lo;0;L;;;;;N;;;;;
+A23A;YI SYLLABLE MGOX;Lo;0;L;;;;;N;;;;;
+A23B;YI SYLLABLE MGO;Lo;0;L;;;;;N;;;;;
+A23C;YI SYLLABLE MGOP;Lo;0;L;;;;;N;;;;;
+A23D;YI SYLLABLE MGEX;Lo;0;L;;;;;N;;;;;
+A23E;YI SYLLABLE MGE;Lo;0;L;;;;;N;;;;;
+A23F;YI SYLLABLE MGEP;Lo;0;L;;;;;N;;;;;
+A240;YI SYLLABLE MGUT;Lo;0;L;;;;;N;;;;;
+A241;YI SYLLABLE MGUX;Lo;0;L;;;;;N;;;;;
+A242;YI SYLLABLE MGU;Lo;0;L;;;;;N;;;;;
+A243;YI SYLLABLE MGUP;Lo;0;L;;;;;N;;;;;
+A244;YI SYLLABLE MGURX;Lo;0;L;;;;;N;;;;;
+A245;YI SYLLABLE MGUR;Lo;0;L;;;;;N;;;;;
+A246;YI SYLLABLE HXIT;Lo;0;L;;;;;N;;;;;
+A247;YI SYLLABLE HXIX;Lo;0;L;;;;;N;;;;;
+A248;YI SYLLABLE HXI;Lo;0;L;;;;;N;;;;;
+A249;YI SYLLABLE HXIP;Lo;0;L;;;;;N;;;;;
+A24A;YI SYLLABLE HXIET;Lo;0;L;;;;;N;;;;;
+A24B;YI SYLLABLE HXIEX;Lo;0;L;;;;;N;;;;;
+A24C;YI SYLLABLE HXIE;Lo;0;L;;;;;N;;;;;
+A24D;YI SYLLABLE HXIEP;Lo;0;L;;;;;N;;;;;
+A24E;YI SYLLABLE HXAT;Lo;0;L;;;;;N;;;;;
+A24F;YI SYLLABLE HXAX;Lo;0;L;;;;;N;;;;;
+A250;YI SYLLABLE HXA;Lo;0;L;;;;;N;;;;;
+A251;YI SYLLABLE HXAP;Lo;0;L;;;;;N;;;;;
+A252;YI SYLLABLE HXUOT;Lo;0;L;;;;;N;;;;;
+A253;YI SYLLABLE HXUOX;Lo;0;L;;;;;N;;;;;
+A254;YI SYLLABLE HXUO;Lo;0;L;;;;;N;;;;;
+A255;YI SYLLABLE HXUOP;Lo;0;L;;;;;N;;;;;
+A256;YI SYLLABLE HXOT;Lo;0;L;;;;;N;;;;;
+A257;YI SYLLABLE HXOX;Lo;0;L;;;;;N;;;;;
+A258;YI SYLLABLE HXO;Lo;0;L;;;;;N;;;;;
+A259;YI SYLLABLE HXOP;Lo;0;L;;;;;N;;;;;
+A25A;YI SYLLABLE HXEX;Lo;0;L;;;;;N;;;;;
+A25B;YI SYLLABLE HXE;Lo;0;L;;;;;N;;;;;
+A25C;YI SYLLABLE HXEP;Lo;0;L;;;;;N;;;;;
+A25D;YI SYLLABLE NGIEX;Lo;0;L;;;;;N;;;;;
+A25E;YI SYLLABLE NGIE;Lo;0;L;;;;;N;;;;;
+A25F;YI SYLLABLE NGIEP;Lo;0;L;;;;;N;;;;;
+A260;YI SYLLABLE NGAT;Lo;0;L;;;;;N;;;;;
+A261;YI SYLLABLE NGAX;Lo;0;L;;;;;N;;;;;
+A262;YI SYLLABLE NGA;Lo;0;L;;;;;N;;;;;
+A263;YI SYLLABLE NGAP;Lo;0;L;;;;;N;;;;;
+A264;YI SYLLABLE NGUOT;Lo;0;L;;;;;N;;;;;
+A265;YI SYLLABLE NGUOX;Lo;0;L;;;;;N;;;;;
+A266;YI SYLLABLE NGUO;Lo;0;L;;;;;N;;;;;
+A267;YI SYLLABLE NGOT;Lo;0;L;;;;;N;;;;;
+A268;YI SYLLABLE NGOX;Lo;0;L;;;;;N;;;;;
+A269;YI SYLLABLE NGO;Lo;0;L;;;;;N;;;;;
+A26A;YI SYLLABLE NGOP;Lo;0;L;;;;;N;;;;;
+A26B;YI SYLLABLE NGEX;Lo;0;L;;;;;N;;;;;
+A26C;YI SYLLABLE NGE;Lo;0;L;;;;;N;;;;;
+A26D;YI SYLLABLE NGEP;Lo;0;L;;;;;N;;;;;
+A26E;YI SYLLABLE HIT;Lo;0;L;;;;;N;;;;;
+A26F;YI SYLLABLE HIEX;Lo;0;L;;;;;N;;;;;
+A270;YI SYLLABLE HIE;Lo;0;L;;;;;N;;;;;
+A271;YI SYLLABLE HAT;Lo;0;L;;;;;N;;;;;
+A272;YI SYLLABLE HAX;Lo;0;L;;;;;N;;;;;
+A273;YI SYLLABLE HA;Lo;0;L;;;;;N;;;;;
+A274;YI SYLLABLE HAP;Lo;0;L;;;;;N;;;;;
+A275;YI SYLLABLE HUOT;Lo;0;L;;;;;N;;;;;
+A276;YI SYLLABLE HUOX;Lo;0;L;;;;;N;;;;;
+A277;YI SYLLABLE HUO;Lo;0;L;;;;;N;;;;;
+A278;YI SYLLABLE HUOP;Lo;0;L;;;;;N;;;;;
+A279;YI SYLLABLE HOT;Lo;0;L;;;;;N;;;;;
+A27A;YI SYLLABLE HOX;Lo;0;L;;;;;N;;;;;
+A27B;YI SYLLABLE HO;Lo;0;L;;;;;N;;;;;
+A27C;YI SYLLABLE HOP;Lo;0;L;;;;;N;;;;;
+A27D;YI SYLLABLE HEX;Lo;0;L;;;;;N;;;;;
+A27E;YI SYLLABLE HE;Lo;0;L;;;;;N;;;;;
+A27F;YI SYLLABLE HEP;Lo;0;L;;;;;N;;;;;
+A280;YI SYLLABLE WAT;Lo;0;L;;;;;N;;;;;
+A281;YI SYLLABLE WAX;Lo;0;L;;;;;N;;;;;
+A282;YI SYLLABLE WA;Lo;0;L;;;;;N;;;;;
+A283;YI SYLLABLE WAP;Lo;0;L;;;;;N;;;;;
+A284;YI SYLLABLE WUOX;Lo;0;L;;;;;N;;;;;
+A285;YI SYLLABLE WUO;Lo;0;L;;;;;N;;;;;
+A286;YI SYLLABLE WUOP;Lo;0;L;;;;;N;;;;;
+A287;YI SYLLABLE WOX;Lo;0;L;;;;;N;;;;;
+A288;YI SYLLABLE WO;Lo;0;L;;;;;N;;;;;
+A289;YI SYLLABLE WOP;Lo;0;L;;;;;N;;;;;
+A28A;YI SYLLABLE WEX;Lo;0;L;;;;;N;;;;;
+A28B;YI SYLLABLE WE;Lo;0;L;;;;;N;;;;;
+A28C;YI SYLLABLE WEP;Lo;0;L;;;;;N;;;;;
+A28D;YI SYLLABLE ZIT;Lo;0;L;;;;;N;;;;;
+A28E;YI SYLLABLE ZIX;Lo;0;L;;;;;N;;;;;
+A28F;YI SYLLABLE ZI;Lo;0;L;;;;;N;;;;;
+A290;YI SYLLABLE ZIP;Lo;0;L;;;;;N;;;;;
+A291;YI SYLLABLE ZIEX;Lo;0;L;;;;;N;;;;;
+A292;YI SYLLABLE ZIE;Lo;0;L;;;;;N;;;;;
+A293;YI SYLLABLE ZIEP;Lo;0;L;;;;;N;;;;;
+A294;YI SYLLABLE ZAT;Lo;0;L;;;;;N;;;;;
+A295;YI SYLLABLE ZAX;Lo;0;L;;;;;N;;;;;
+A296;YI SYLLABLE ZA;Lo;0;L;;;;;N;;;;;
+A297;YI SYLLABLE ZAP;Lo;0;L;;;;;N;;;;;
+A298;YI SYLLABLE ZUOX;Lo;0;L;;;;;N;;;;;
+A299;YI SYLLABLE ZUO;Lo;0;L;;;;;N;;;;;
+A29A;YI SYLLABLE ZUOP;Lo;0;L;;;;;N;;;;;
+A29B;YI SYLLABLE ZOT;Lo;0;L;;;;;N;;;;;
+A29C;YI SYLLABLE ZOX;Lo;0;L;;;;;N;;;;;
+A29D;YI SYLLABLE ZO;Lo;0;L;;;;;N;;;;;
+A29E;YI SYLLABLE ZOP;Lo;0;L;;;;;N;;;;;
+A29F;YI SYLLABLE ZEX;Lo;0;L;;;;;N;;;;;
+A2A0;YI SYLLABLE ZE;Lo;0;L;;;;;N;;;;;
+A2A1;YI SYLLABLE ZEP;Lo;0;L;;;;;N;;;;;
+A2A2;YI SYLLABLE ZUT;Lo;0;L;;;;;N;;;;;
+A2A3;YI SYLLABLE ZUX;Lo;0;L;;;;;N;;;;;
+A2A4;YI SYLLABLE ZU;Lo;0;L;;;;;N;;;;;
+A2A5;YI SYLLABLE ZUP;Lo;0;L;;;;;N;;;;;
+A2A6;YI SYLLABLE ZURX;Lo;0;L;;;;;N;;;;;
+A2A7;YI SYLLABLE ZUR;Lo;0;L;;;;;N;;;;;
+A2A8;YI SYLLABLE ZYT;Lo;0;L;;;;;N;;;;;
+A2A9;YI SYLLABLE ZYX;Lo;0;L;;;;;N;;;;;
+A2AA;YI SYLLABLE ZY;Lo;0;L;;;;;N;;;;;
+A2AB;YI SYLLABLE ZYP;Lo;0;L;;;;;N;;;;;
+A2AC;YI SYLLABLE ZYRX;Lo;0;L;;;;;N;;;;;
+A2AD;YI SYLLABLE ZYR;Lo;0;L;;;;;N;;;;;
+A2AE;YI SYLLABLE CIT;Lo;0;L;;;;;N;;;;;
+A2AF;YI SYLLABLE CIX;Lo;0;L;;;;;N;;;;;
+A2B0;YI SYLLABLE CI;Lo;0;L;;;;;N;;;;;
+A2B1;YI SYLLABLE CIP;Lo;0;L;;;;;N;;;;;
+A2B2;YI SYLLABLE CIET;Lo;0;L;;;;;N;;;;;
+A2B3;YI SYLLABLE CIEX;Lo;0;L;;;;;N;;;;;
+A2B4;YI SYLLABLE CIE;Lo;0;L;;;;;N;;;;;
+A2B5;YI SYLLABLE CIEP;Lo;0;L;;;;;N;;;;;
+A2B6;YI SYLLABLE CAT;Lo;0;L;;;;;N;;;;;
+A2B7;YI SYLLABLE CAX;Lo;0;L;;;;;N;;;;;
+A2B8;YI SYLLABLE CA;Lo;0;L;;;;;N;;;;;
+A2B9;YI SYLLABLE CAP;Lo;0;L;;;;;N;;;;;
+A2BA;YI SYLLABLE CUOX;Lo;0;L;;;;;N;;;;;
+A2BB;YI SYLLABLE CUO;Lo;0;L;;;;;N;;;;;
+A2BC;YI SYLLABLE CUOP;Lo;0;L;;;;;N;;;;;
+A2BD;YI SYLLABLE COT;Lo;0;L;;;;;N;;;;;
+A2BE;YI SYLLABLE COX;Lo;0;L;;;;;N;;;;;
+A2BF;YI SYLLABLE CO;Lo;0;L;;;;;N;;;;;
+A2C0;YI SYLLABLE COP;Lo;0;L;;;;;N;;;;;
+A2C1;YI SYLLABLE CEX;Lo;0;L;;;;;N;;;;;
+A2C2;YI SYLLABLE CE;Lo;0;L;;;;;N;;;;;
+A2C3;YI SYLLABLE CEP;Lo;0;L;;;;;N;;;;;
+A2C4;YI SYLLABLE CUT;Lo;0;L;;;;;N;;;;;
+A2C5;YI SYLLABLE CUX;Lo;0;L;;;;;N;;;;;
+A2C6;YI SYLLABLE CU;Lo;0;L;;;;;N;;;;;
+A2C7;YI SYLLABLE CUP;Lo;0;L;;;;;N;;;;;
+A2C8;YI SYLLABLE CURX;Lo;0;L;;;;;N;;;;;
+A2C9;YI SYLLABLE CUR;Lo;0;L;;;;;N;;;;;
+A2CA;YI SYLLABLE CYT;Lo;0;L;;;;;N;;;;;
+A2CB;YI SYLLABLE CYX;Lo;0;L;;;;;N;;;;;
+A2CC;YI SYLLABLE CY;Lo;0;L;;;;;N;;;;;
+A2CD;YI SYLLABLE CYP;Lo;0;L;;;;;N;;;;;
+A2CE;YI SYLLABLE CYRX;Lo;0;L;;;;;N;;;;;
+A2CF;YI SYLLABLE CYR;Lo;0;L;;;;;N;;;;;
+A2D0;YI SYLLABLE ZZIT;Lo;0;L;;;;;N;;;;;
+A2D1;YI SYLLABLE ZZIX;Lo;0;L;;;;;N;;;;;
+A2D2;YI SYLLABLE ZZI;Lo;0;L;;;;;N;;;;;
+A2D3;YI SYLLABLE ZZIP;Lo;0;L;;;;;N;;;;;
+A2D4;YI SYLLABLE ZZIET;Lo;0;L;;;;;N;;;;;
+A2D5;YI SYLLABLE ZZIEX;Lo;0;L;;;;;N;;;;;
+A2D6;YI SYLLABLE ZZIE;Lo;0;L;;;;;N;;;;;
+A2D7;YI SYLLABLE ZZIEP;Lo;0;L;;;;;N;;;;;
+A2D8;YI SYLLABLE ZZAT;Lo;0;L;;;;;N;;;;;
+A2D9;YI SYLLABLE ZZAX;Lo;0;L;;;;;N;;;;;
+A2DA;YI SYLLABLE ZZA;Lo;0;L;;;;;N;;;;;
+A2DB;YI SYLLABLE ZZAP;Lo;0;L;;;;;N;;;;;
+A2DC;YI SYLLABLE ZZOX;Lo;0;L;;;;;N;;;;;
+A2DD;YI SYLLABLE ZZO;Lo;0;L;;;;;N;;;;;
+A2DE;YI SYLLABLE ZZOP;Lo;0;L;;;;;N;;;;;
+A2DF;YI SYLLABLE ZZEX;Lo;0;L;;;;;N;;;;;
+A2E0;YI SYLLABLE ZZE;Lo;0;L;;;;;N;;;;;
+A2E1;YI SYLLABLE ZZEP;Lo;0;L;;;;;N;;;;;
+A2E2;YI SYLLABLE ZZUX;Lo;0;L;;;;;N;;;;;
+A2E3;YI SYLLABLE ZZU;Lo;0;L;;;;;N;;;;;
+A2E4;YI SYLLABLE ZZUP;Lo;0;L;;;;;N;;;;;
+A2E5;YI SYLLABLE ZZURX;Lo;0;L;;;;;N;;;;;
+A2E6;YI SYLLABLE ZZUR;Lo;0;L;;;;;N;;;;;
+A2E7;YI SYLLABLE ZZYT;Lo;0;L;;;;;N;;;;;
+A2E8;YI SYLLABLE ZZYX;Lo;0;L;;;;;N;;;;;
+A2E9;YI SYLLABLE ZZY;Lo;0;L;;;;;N;;;;;
+A2EA;YI SYLLABLE ZZYP;Lo;0;L;;;;;N;;;;;
+A2EB;YI SYLLABLE ZZYRX;Lo;0;L;;;;;N;;;;;
+A2EC;YI SYLLABLE ZZYR;Lo;0;L;;;;;N;;;;;
+A2ED;YI SYLLABLE NZIT;Lo;0;L;;;;;N;;;;;
+A2EE;YI SYLLABLE NZIX;Lo;0;L;;;;;N;;;;;
+A2EF;YI SYLLABLE NZI;Lo;0;L;;;;;N;;;;;
+A2F0;YI SYLLABLE NZIP;Lo;0;L;;;;;N;;;;;
+A2F1;YI SYLLABLE NZIEX;Lo;0;L;;;;;N;;;;;
+A2F2;YI SYLLABLE NZIE;Lo;0;L;;;;;N;;;;;
+A2F3;YI SYLLABLE NZIEP;Lo;0;L;;;;;N;;;;;
+A2F4;YI SYLLABLE NZAT;Lo;0;L;;;;;N;;;;;
+A2F5;YI SYLLABLE NZAX;Lo;0;L;;;;;N;;;;;
+A2F6;YI SYLLABLE NZA;Lo;0;L;;;;;N;;;;;
+A2F7;YI SYLLABLE NZAP;Lo;0;L;;;;;N;;;;;
+A2F8;YI SYLLABLE NZUOX;Lo;0;L;;;;;N;;;;;
+A2F9;YI SYLLABLE NZUO;Lo;0;L;;;;;N;;;;;
+A2FA;YI SYLLABLE NZOX;Lo;0;L;;;;;N;;;;;
+A2FB;YI SYLLABLE NZOP;Lo;0;L;;;;;N;;;;;
+A2FC;YI SYLLABLE NZEX;Lo;0;L;;;;;N;;;;;
+A2FD;YI SYLLABLE NZE;Lo;0;L;;;;;N;;;;;
+A2FE;YI SYLLABLE NZUX;Lo;0;L;;;;;N;;;;;
+A2FF;YI SYLLABLE NZU;Lo;0;L;;;;;N;;;;;
+A300;YI SYLLABLE NZUP;Lo;0;L;;;;;N;;;;;
+A301;YI SYLLABLE NZURX;Lo;0;L;;;;;N;;;;;
+A302;YI SYLLABLE NZUR;Lo;0;L;;;;;N;;;;;
+A303;YI SYLLABLE NZYT;Lo;0;L;;;;;N;;;;;
+A304;YI SYLLABLE NZYX;Lo;0;L;;;;;N;;;;;
+A305;YI SYLLABLE NZY;Lo;0;L;;;;;N;;;;;
+A306;YI SYLLABLE NZYP;Lo;0;L;;;;;N;;;;;
+A307;YI SYLLABLE NZYRX;Lo;0;L;;;;;N;;;;;
+A308;YI SYLLABLE NZYR;Lo;0;L;;;;;N;;;;;
+A309;YI SYLLABLE SIT;Lo;0;L;;;;;N;;;;;
+A30A;YI SYLLABLE SIX;Lo;0;L;;;;;N;;;;;
+A30B;YI SYLLABLE SI;Lo;0;L;;;;;N;;;;;
+A30C;YI SYLLABLE SIP;Lo;0;L;;;;;N;;;;;
+A30D;YI SYLLABLE SIEX;Lo;0;L;;;;;N;;;;;
+A30E;YI SYLLABLE SIE;Lo;0;L;;;;;N;;;;;
+A30F;YI SYLLABLE SIEP;Lo;0;L;;;;;N;;;;;
+A310;YI SYLLABLE SAT;Lo;0;L;;;;;N;;;;;
+A311;YI SYLLABLE SAX;Lo;0;L;;;;;N;;;;;
+A312;YI SYLLABLE SA;Lo;0;L;;;;;N;;;;;
+A313;YI SYLLABLE SAP;Lo;0;L;;;;;N;;;;;
+A314;YI SYLLABLE SUOX;Lo;0;L;;;;;N;;;;;
+A315;YI SYLLABLE SUO;Lo;0;L;;;;;N;;;;;
+A316;YI SYLLABLE SUOP;Lo;0;L;;;;;N;;;;;
+A317;YI SYLLABLE SOT;Lo;0;L;;;;;N;;;;;
+A318;YI SYLLABLE SOX;Lo;0;L;;;;;N;;;;;
+A319;YI SYLLABLE SO;Lo;0;L;;;;;N;;;;;
+A31A;YI SYLLABLE SOP;Lo;0;L;;;;;N;;;;;
+A31B;YI SYLLABLE SEX;Lo;0;L;;;;;N;;;;;
+A31C;YI SYLLABLE SE;Lo;0;L;;;;;N;;;;;
+A31D;YI SYLLABLE SEP;Lo;0;L;;;;;N;;;;;
+A31E;YI SYLLABLE SUT;Lo;0;L;;;;;N;;;;;
+A31F;YI SYLLABLE SUX;Lo;0;L;;;;;N;;;;;
+A320;YI SYLLABLE SU;Lo;0;L;;;;;N;;;;;
+A321;YI SYLLABLE SUP;Lo;0;L;;;;;N;;;;;
+A322;YI SYLLABLE SURX;Lo;0;L;;;;;N;;;;;
+A323;YI SYLLABLE SUR;Lo;0;L;;;;;N;;;;;
+A324;YI SYLLABLE SYT;Lo;0;L;;;;;N;;;;;
+A325;YI SYLLABLE SYX;Lo;0;L;;;;;N;;;;;
+A326;YI SYLLABLE SY;Lo;0;L;;;;;N;;;;;
+A327;YI SYLLABLE SYP;Lo;0;L;;;;;N;;;;;
+A328;YI SYLLABLE SYRX;Lo;0;L;;;;;N;;;;;
+A329;YI SYLLABLE SYR;Lo;0;L;;;;;N;;;;;
+A32A;YI SYLLABLE SSIT;Lo;0;L;;;;;N;;;;;
+A32B;YI SYLLABLE SSIX;Lo;0;L;;;;;N;;;;;
+A32C;YI SYLLABLE SSI;Lo;0;L;;;;;N;;;;;
+A32D;YI SYLLABLE SSIP;Lo;0;L;;;;;N;;;;;
+A32E;YI SYLLABLE SSIEX;Lo;0;L;;;;;N;;;;;
+A32F;YI SYLLABLE SSIE;Lo;0;L;;;;;N;;;;;
+A330;YI SYLLABLE SSIEP;Lo;0;L;;;;;N;;;;;
+A331;YI SYLLABLE SSAT;Lo;0;L;;;;;N;;;;;
+A332;YI SYLLABLE SSAX;Lo;0;L;;;;;N;;;;;
+A333;YI SYLLABLE SSA;Lo;0;L;;;;;N;;;;;
+A334;YI SYLLABLE SSAP;Lo;0;L;;;;;N;;;;;
+A335;YI SYLLABLE SSOT;Lo;0;L;;;;;N;;;;;
+A336;YI SYLLABLE SSOX;Lo;0;L;;;;;N;;;;;
+A337;YI SYLLABLE SSO;Lo;0;L;;;;;N;;;;;
+A338;YI SYLLABLE SSOP;Lo;0;L;;;;;N;;;;;
+A339;YI SYLLABLE SSEX;Lo;0;L;;;;;N;;;;;
+A33A;YI SYLLABLE SSE;Lo;0;L;;;;;N;;;;;
+A33B;YI SYLLABLE SSEP;Lo;0;L;;;;;N;;;;;
+A33C;YI SYLLABLE SSUT;Lo;0;L;;;;;N;;;;;
+A33D;YI SYLLABLE SSUX;Lo;0;L;;;;;N;;;;;
+A33E;YI SYLLABLE SSU;Lo;0;L;;;;;N;;;;;
+A33F;YI SYLLABLE SSUP;Lo;0;L;;;;;N;;;;;
+A340;YI SYLLABLE SSYT;Lo;0;L;;;;;N;;;;;
+A341;YI SYLLABLE SSYX;Lo;0;L;;;;;N;;;;;
+A342;YI SYLLABLE SSY;Lo;0;L;;;;;N;;;;;
+A343;YI SYLLABLE SSYP;Lo;0;L;;;;;N;;;;;
+A344;YI SYLLABLE SSYRX;Lo;0;L;;;;;N;;;;;
+A345;YI SYLLABLE SSYR;Lo;0;L;;;;;N;;;;;
+A346;YI SYLLABLE ZHAT;Lo;0;L;;;;;N;;;;;
+A347;YI SYLLABLE ZHAX;Lo;0;L;;;;;N;;;;;
+A348;YI SYLLABLE ZHA;Lo;0;L;;;;;N;;;;;
+A349;YI SYLLABLE ZHAP;Lo;0;L;;;;;N;;;;;
+A34A;YI SYLLABLE ZHUOX;Lo;0;L;;;;;N;;;;;
+A34B;YI SYLLABLE ZHUO;Lo;0;L;;;;;N;;;;;
+A34C;YI SYLLABLE ZHUOP;Lo;0;L;;;;;N;;;;;
+A34D;YI SYLLABLE ZHOT;Lo;0;L;;;;;N;;;;;
+A34E;YI SYLLABLE ZHOX;Lo;0;L;;;;;N;;;;;
+A34F;YI SYLLABLE ZHO;Lo;0;L;;;;;N;;;;;
+A350;YI SYLLABLE ZHOP;Lo;0;L;;;;;N;;;;;
+A351;YI SYLLABLE ZHET;Lo;0;L;;;;;N;;;;;
+A352;YI SYLLABLE ZHEX;Lo;0;L;;;;;N;;;;;
+A353;YI SYLLABLE ZHE;Lo;0;L;;;;;N;;;;;
+A354;YI SYLLABLE ZHEP;Lo;0;L;;;;;N;;;;;
+A355;YI SYLLABLE ZHUT;Lo;0;L;;;;;N;;;;;
+A356;YI SYLLABLE ZHUX;Lo;0;L;;;;;N;;;;;
+A357;YI SYLLABLE ZHU;Lo;0;L;;;;;N;;;;;
+A358;YI SYLLABLE ZHUP;Lo;0;L;;;;;N;;;;;
+A359;YI SYLLABLE ZHURX;Lo;0;L;;;;;N;;;;;
+A35A;YI SYLLABLE ZHUR;Lo;0;L;;;;;N;;;;;
+A35B;YI SYLLABLE ZHYT;Lo;0;L;;;;;N;;;;;
+A35C;YI SYLLABLE ZHYX;Lo;0;L;;;;;N;;;;;
+A35D;YI SYLLABLE ZHY;Lo;0;L;;;;;N;;;;;
+A35E;YI SYLLABLE ZHYP;Lo;0;L;;;;;N;;;;;
+A35F;YI SYLLABLE ZHYRX;Lo;0;L;;;;;N;;;;;
+A360;YI SYLLABLE ZHYR;Lo;0;L;;;;;N;;;;;
+A361;YI SYLLABLE CHAT;Lo;0;L;;;;;N;;;;;
+A362;YI SYLLABLE CHAX;Lo;0;L;;;;;N;;;;;
+A363;YI SYLLABLE CHA;Lo;0;L;;;;;N;;;;;
+A364;YI SYLLABLE CHAP;Lo;0;L;;;;;N;;;;;
+A365;YI SYLLABLE CHUOT;Lo;0;L;;;;;N;;;;;
+A366;YI SYLLABLE CHUOX;Lo;0;L;;;;;N;;;;;
+A367;YI SYLLABLE CHUO;Lo;0;L;;;;;N;;;;;
+A368;YI SYLLABLE CHUOP;Lo;0;L;;;;;N;;;;;
+A369;YI SYLLABLE CHOT;Lo;0;L;;;;;N;;;;;
+A36A;YI SYLLABLE CHOX;Lo;0;L;;;;;N;;;;;
+A36B;YI SYLLABLE CHO;Lo;0;L;;;;;N;;;;;
+A36C;YI SYLLABLE CHOP;Lo;0;L;;;;;N;;;;;
+A36D;YI SYLLABLE CHET;Lo;0;L;;;;;N;;;;;
+A36E;YI SYLLABLE CHEX;Lo;0;L;;;;;N;;;;;
+A36F;YI SYLLABLE CHE;Lo;0;L;;;;;N;;;;;
+A370;YI SYLLABLE CHEP;Lo;0;L;;;;;N;;;;;
+A371;YI SYLLABLE CHUX;Lo;0;L;;;;;N;;;;;
+A372;YI SYLLABLE CHU;Lo;0;L;;;;;N;;;;;
+A373;YI SYLLABLE CHUP;Lo;0;L;;;;;N;;;;;
+A374;YI SYLLABLE CHURX;Lo;0;L;;;;;N;;;;;
+A375;YI SYLLABLE CHUR;Lo;0;L;;;;;N;;;;;
+A376;YI SYLLABLE CHYT;Lo;0;L;;;;;N;;;;;
+A377;YI SYLLABLE CHYX;Lo;0;L;;;;;N;;;;;
+A378;YI SYLLABLE CHY;Lo;0;L;;;;;N;;;;;
+A379;YI SYLLABLE CHYP;Lo;0;L;;;;;N;;;;;
+A37A;YI SYLLABLE CHYRX;Lo;0;L;;;;;N;;;;;
+A37B;YI SYLLABLE CHYR;Lo;0;L;;;;;N;;;;;
+A37C;YI SYLLABLE RRAX;Lo;0;L;;;;;N;;;;;
+A37D;YI SYLLABLE RRA;Lo;0;L;;;;;N;;;;;
+A37E;YI SYLLABLE RRUOX;Lo;0;L;;;;;N;;;;;
+A37F;YI SYLLABLE RRUO;Lo;0;L;;;;;N;;;;;
+A380;YI SYLLABLE RROT;Lo;0;L;;;;;N;;;;;
+A381;YI SYLLABLE RROX;Lo;0;L;;;;;N;;;;;
+A382;YI SYLLABLE RRO;Lo;0;L;;;;;N;;;;;
+A383;YI SYLLABLE RROP;Lo;0;L;;;;;N;;;;;
+A384;YI SYLLABLE RRET;Lo;0;L;;;;;N;;;;;
+A385;YI SYLLABLE RREX;Lo;0;L;;;;;N;;;;;
+A386;YI SYLLABLE RRE;Lo;0;L;;;;;N;;;;;
+A387;YI SYLLABLE RREP;Lo;0;L;;;;;N;;;;;
+A388;YI SYLLABLE RRUT;Lo;0;L;;;;;N;;;;;
+A389;YI SYLLABLE RRUX;Lo;0;L;;;;;N;;;;;
+A38A;YI SYLLABLE RRU;Lo;0;L;;;;;N;;;;;
+A38B;YI SYLLABLE RRUP;Lo;0;L;;;;;N;;;;;
+A38C;YI SYLLABLE RRURX;Lo;0;L;;;;;N;;;;;
+A38D;YI SYLLABLE RRUR;Lo;0;L;;;;;N;;;;;
+A38E;YI SYLLABLE RRYT;Lo;0;L;;;;;N;;;;;
+A38F;YI SYLLABLE RRYX;Lo;0;L;;;;;N;;;;;
+A390;YI SYLLABLE RRY;Lo;0;L;;;;;N;;;;;
+A391;YI SYLLABLE RRYP;Lo;0;L;;;;;N;;;;;
+A392;YI SYLLABLE RRYRX;Lo;0;L;;;;;N;;;;;
+A393;YI SYLLABLE RRYR;Lo;0;L;;;;;N;;;;;
+A394;YI SYLLABLE NRAT;Lo;0;L;;;;;N;;;;;
+A395;YI SYLLABLE NRAX;Lo;0;L;;;;;N;;;;;
+A396;YI SYLLABLE NRA;Lo;0;L;;;;;N;;;;;
+A397;YI SYLLABLE NRAP;Lo;0;L;;;;;N;;;;;
+A398;YI SYLLABLE NROX;Lo;0;L;;;;;N;;;;;
+A399;YI SYLLABLE NRO;Lo;0;L;;;;;N;;;;;
+A39A;YI SYLLABLE NROP;Lo;0;L;;;;;N;;;;;
+A39B;YI SYLLABLE NRET;Lo;0;L;;;;;N;;;;;
+A39C;YI SYLLABLE NREX;Lo;0;L;;;;;N;;;;;
+A39D;YI SYLLABLE NRE;Lo;0;L;;;;;N;;;;;
+A39E;YI SYLLABLE NREP;Lo;0;L;;;;;N;;;;;
+A39F;YI SYLLABLE NRUT;Lo;0;L;;;;;N;;;;;
+A3A0;YI SYLLABLE NRUX;Lo;0;L;;;;;N;;;;;
+A3A1;YI SYLLABLE NRU;Lo;0;L;;;;;N;;;;;
+A3A2;YI SYLLABLE NRUP;Lo;0;L;;;;;N;;;;;
+A3A3;YI SYLLABLE NRURX;Lo;0;L;;;;;N;;;;;
+A3A4;YI SYLLABLE NRUR;Lo;0;L;;;;;N;;;;;
+A3A5;YI SYLLABLE NRYT;Lo;0;L;;;;;N;;;;;
+A3A6;YI SYLLABLE NRYX;Lo;0;L;;;;;N;;;;;
+A3A7;YI SYLLABLE NRY;Lo;0;L;;;;;N;;;;;
+A3A8;YI SYLLABLE NRYP;Lo;0;L;;;;;N;;;;;
+A3A9;YI SYLLABLE NRYRX;Lo;0;L;;;;;N;;;;;
+A3AA;YI SYLLABLE NRYR;Lo;0;L;;;;;N;;;;;
+A3AB;YI SYLLABLE SHAT;Lo;0;L;;;;;N;;;;;
+A3AC;YI SYLLABLE SHAX;Lo;0;L;;;;;N;;;;;
+A3AD;YI SYLLABLE SHA;Lo;0;L;;;;;N;;;;;
+A3AE;YI SYLLABLE SHAP;Lo;0;L;;;;;N;;;;;
+A3AF;YI SYLLABLE SHUOX;Lo;0;L;;;;;N;;;;;
+A3B0;YI SYLLABLE SHUO;Lo;0;L;;;;;N;;;;;
+A3B1;YI SYLLABLE SHUOP;Lo;0;L;;;;;N;;;;;
+A3B2;YI SYLLABLE SHOT;Lo;0;L;;;;;N;;;;;
+A3B3;YI SYLLABLE SHOX;Lo;0;L;;;;;N;;;;;
+A3B4;YI SYLLABLE SHO;Lo;0;L;;;;;N;;;;;
+A3B5;YI SYLLABLE SHOP;Lo;0;L;;;;;N;;;;;
+A3B6;YI SYLLABLE SHET;Lo;0;L;;;;;N;;;;;
+A3B7;YI SYLLABLE SHEX;Lo;0;L;;;;;N;;;;;
+A3B8;YI SYLLABLE SHE;Lo;0;L;;;;;N;;;;;
+A3B9;YI SYLLABLE SHEP;Lo;0;L;;;;;N;;;;;
+A3BA;YI SYLLABLE SHUT;Lo;0;L;;;;;N;;;;;
+A3BB;YI SYLLABLE SHUX;Lo;0;L;;;;;N;;;;;
+A3BC;YI SYLLABLE SHU;Lo;0;L;;;;;N;;;;;
+A3BD;YI SYLLABLE SHUP;Lo;0;L;;;;;N;;;;;
+A3BE;YI SYLLABLE SHURX;Lo;0;L;;;;;N;;;;;
+A3BF;YI SYLLABLE SHUR;Lo;0;L;;;;;N;;;;;
+A3C0;YI SYLLABLE SHYT;Lo;0;L;;;;;N;;;;;
+A3C1;YI SYLLABLE SHYX;Lo;0;L;;;;;N;;;;;
+A3C2;YI SYLLABLE SHY;Lo;0;L;;;;;N;;;;;
+A3C3;YI SYLLABLE SHYP;Lo;0;L;;;;;N;;;;;
+A3C4;YI SYLLABLE SHYRX;Lo;0;L;;;;;N;;;;;
+A3C5;YI SYLLABLE SHYR;Lo;0;L;;;;;N;;;;;
+A3C6;YI SYLLABLE RAT;Lo;0;L;;;;;N;;;;;
+A3C7;YI SYLLABLE RAX;Lo;0;L;;;;;N;;;;;
+A3C8;YI SYLLABLE RA;Lo;0;L;;;;;N;;;;;
+A3C9;YI SYLLABLE RAP;Lo;0;L;;;;;N;;;;;
+A3CA;YI SYLLABLE RUOX;Lo;0;L;;;;;N;;;;;
+A3CB;YI SYLLABLE RUO;Lo;0;L;;;;;N;;;;;
+A3CC;YI SYLLABLE RUOP;Lo;0;L;;;;;N;;;;;
+A3CD;YI SYLLABLE ROT;Lo;0;L;;;;;N;;;;;
+A3CE;YI SYLLABLE ROX;Lo;0;L;;;;;N;;;;;
+A3CF;YI SYLLABLE RO;Lo;0;L;;;;;N;;;;;
+A3D0;YI SYLLABLE ROP;Lo;0;L;;;;;N;;;;;
+A3D1;YI SYLLABLE REX;Lo;0;L;;;;;N;;;;;
+A3D2;YI SYLLABLE RE;Lo;0;L;;;;;N;;;;;
+A3D3;YI SYLLABLE REP;Lo;0;L;;;;;N;;;;;
+A3D4;YI SYLLABLE RUT;Lo;0;L;;;;;N;;;;;
+A3D5;YI SYLLABLE RUX;Lo;0;L;;;;;N;;;;;
+A3D6;YI SYLLABLE RU;Lo;0;L;;;;;N;;;;;
+A3D7;YI SYLLABLE RUP;Lo;0;L;;;;;N;;;;;
+A3D8;YI SYLLABLE RURX;Lo;0;L;;;;;N;;;;;
+A3D9;YI SYLLABLE RUR;Lo;0;L;;;;;N;;;;;
+A3DA;YI SYLLABLE RYT;Lo;0;L;;;;;N;;;;;
+A3DB;YI SYLLABLE RYX;Lo;0;L;;;;;N;;;;;
+A3DC;YI SYLLABLE RY;Lo;0;L;;;;;N;;;;;
+A3DD;YI SYLLABLE RYP;Lo;0;L;;;;;N;;;;;
+A3DE;YI SYLLABLE RYRX;Lo;0;L;;;;;N;;;;;
+A3DF;YI SYLLABLE RYR;Lo;0;L;;;;;N;;;;;
+A3E0;YI SYLLABLE JIT;Lo;0;L;;;;;N;;;;;
+A3E1;YI SYLLABLE JIX;Lo;0;L;;;;;N;;;;;
+A3E2;YI SYLLABLE JI;Lo;0;L;;;;;N;;;;;
+A3E3;YI SYLLABLE JIP;Lo;0;L;;;;;N;;;;;
+A3E4;YI SYLLABLE JIET;Lo;0;L;;;;;N;;;;;
+A3E5;YI SYLLABLE JIEX;Lo;0;L;;;;;N;;;;;
+A3E6;YI SYLLABLE JIE;Lo;0;L;;;;;N;;;;;
+A3E7;YI SYLLABLE JIEP;Lo;0;L;;;;;N;;;;;
+A3E8;YI SYLLABLE JUOT;Lo;0;L;;;;;N;;;;;
+A3E9;YI SYLLABLE JUOX;Lo;0;L;;;;;N;;;;;
+A3EA;YI SYLLABLE JUO;Lo;0;L;;;;;N;;;;;
+A3EB;YI SYLLABLE JUOP;Lo;0;L;;;;;N;;;;;
+A3EC;YI SYLLABLE JOT;Lo;0;L;;;;;N;;;;;
+A3ED;YI SYLLABLE JOX;Lo;0;L;;;;;N;;;;;
+A3EE;YI SYLLABLE JO;Lo;0;L;;;;;N;;;;;
+A3EF;YI SYLLABLE JOP;Lo;0;L;;;;;N;;;;;
+A3F0;YI SYLLABLE JUT;Lo;0;L;;;;;N;;;;;
+A3F1;YI SYLLABLE JUX;Lo;0;L;;;;;N;;;;;
+A3F2;YI SYLLABLE JU;Lo;0;L;;;;;N;;;;;
+A3F3;YI SYLLABLE JUP;Lo;0;L;;;;;N;;;;;
+A3F4;YI SYLLABLE JURX;Lo;0;L;;;;;N;;;;;
+A3F5;YI SYLLABLE JUR;Lo;0;L;;;;;N;;;;;
+A3F6;YI SYLLABLE JYT;Lo;0;L;;;;;N;;;;;
+A3F7;YI SYLLABLE JYX;Lo;0;L;;;;;N;;;;;
+A3F8;YI SYLLABLE JY;Lo;0;L;;;;;N;;;;;
+A3F9;YI SYLLABLE JYP;Lo;0;L;;;;;N;;;;;
+A3FA;YI SYLLABLE JYRX;Lo;0;L;;;;;N;;;;;
+A3FB;YI SYLLABLE JYR;Lo;0;L;;;;;N;;;;;
+A3FC;YI SYLLABLE QIT;Lo;0;L;;;;;N;;;;;
+A3FD;YI SYLLABLE QIX;Lo;0;L;;;;;N;;;;;
+A3FE;YI SYLLABLE QI;Lo;0;L;;;;;N;;;;;
+A3FF;YI SYLLABLE QIP;Lo;0;L;;;;;N;;;;;
+A400;YI SYLLABLE QIET;Lo;0;L;;;;;N;;;;;
+A401;YI SYLLABLE QIEX;Lo;0;L;;;;;N;;;;;
+A402;YI SYLLABLE QIE;Lo;0;L;;;;;N;;;;;
+A403;YI SYLLABLE QIEP;Lo;0;L;;;;;N;;;;;
+A404;YI SYLLABLE QUOT;Lo;0;L;;;;;N;;;;;
+A405;YI SYLLABLE QUOX;Lo;0;L;;;;;N;;;;;
+A406;YI SYLLABLE QUO;Lo;0;L;;;;;N;;;;;
+A407;YI SYLLABLE QUOP;Lo;0;L;;;;;N;;;;;
+A408;YI SYLLABLE QOT;Lo;0;L;;;;;N;;;;;
+A409;YI SYLLABLE QOX;Lo;0;L;;;;;N;;;;;
+A40A;YI SYLLABLE QO;Lo;0;L;;;;;N;;;;;
+A40B;YI SYLLABLE QOP;Lo;0;L;;;;;N;;;;;
+A40C;YI SYLLABLE QUT;Lo;0;L;;;;;N;;;;;
+A40D;YI SYLLABLE QUX;Lo;0;L;;;;;N;;;;;
+A40E;YI SYLLABLE QU;Lo;0;L;;;;;N;;;;;
+A40F;YI SYLLABLE QUP;Lo;0;L;;;;;N;;;;;
+A410;YI SYLLABLE QURX;Lo;0;L;;;;;N;;;;;
+A411;YI SYLLABLE QUR;Lo;0;L;;;;;N;;;;;
+A412;YI SYLLABLE QYT;Lo;0;L;;;;;N;;;;;
+A413;YI SYLLABLE QYX;Lo;0;L;;;;;N;;;;;
+A414;YI SYLLABLE QY;Lo;0;L;;;;;N;;;;;
+A415;YI SYLLABLE QYP;Lo;0;L;;;;;N;;;;;
+A416;YI SYLLABLE QYRX;Lo;0;L;;;;;N;;;;;
+A417;YI SYLLABLE QYR;Lo;0;L;;;;;N;;;;;
+A418;YI SYLLABLE JJIT;Lo;0;L;;;;;N;;;;;
+A419;YI SYLLABLE JJIX;Lo;0;L;;;;;N;;;;;
+A41A;YI SYLLABLE JJI;Lo;0;L;;;;;N;;;;;
+A41B;YI SYLLABLE JJIP;Lo;0;L;;;;;N;;;;;
+A41C;YI SYLLABLE JJIET;Lo;0;L;;;;;N;;;;;
+A41D;YI SYLLABLE JJIEX;Lo;0;L;;;;;N;;;;;
+A41E;YI SYLLABLE JJIE;Lo;0;L;;;;;N;;;;;
+A41F;YI SYLLABLE JJIEP;Lo;0;L;;;;;N;;;;;
+A420;YI SYLLABLE JJUOX;Lo;0;L;;;;;N;;;;;
+A421;YI SYLLABLE JJUO;Lo;0;L;;;;;N;;;;;
+A422;YI SYLLABLE JJUOP;Lo;0;L;;;;;N;;;;;
+A423;YI SYLLABLE JJOT;Lo;0;L;;;;;N;;;;;
+A424;YI SYLLABLE JJOX;Lo;0;L;;;;;N;;;;;
+A425;YI SYLLABLE JJO;Lo;0;L;;;;;N;;;;;
+A426;YI SYLLABLE JJOP;Lo;0;L;;;;;N;;;;;
+A427;YI SYLLABLE JJUT;Lo;0;L;;;;;N;;;;;
+A428;YI SYLLABLE JJUX;Lo;0;L;;;;;N;;;;;
+A429;YI SYLLABLE JJU;Lo;0;L;;;;;N;;;;;
+A42A;YI SYLLABLE JJUP;Lo;0;L;;;;;N;;;;;
+A42B;YI SYLLABLE JJURX;Lo;0;L;;;;;N;;;;;
+A42C;YI SYLLABLE JJUR;Lo;0;L;;;;;N;;;;;
+A42D;YI SYLLABLE JJYT;Lo;0;L;;;;;N;;;;;
+A42E;YI SYLLABLE JJYX;Lo;0;L;;;;;N;;;;;
+A42F;YI SYLLABLE JJY;Lo;0;L;;;;;N;;;;;
+A430;YI SYLLABLE JJYP;Lo;0;L;;;;;N;;;;;
+A431;YI SYLLABLE NJIT;Lo;0;L;;;;;N;;;;;
+A432;YI SYLLABLE NJIX;Lo;0;L;;;;;N;;;;;
+A433;YI SYLLABLE NJI;Lo;0;L;;;;;N;;;;;
+A434;YI SYLLABLE NJIP;Lo;0;L;;;;;N;;;;;
+A435;YI SYLLABLE NJIET;Lo;0;L;;;;;N;;;;;
+A436;YI SYLLABLE NJIEX;Lo;0;L;;;;;N;;;;;
+A437;YI SYLLABLE NJIE;Lo;0;L;;;;;N;;;;;
+A438;YI SYLLABLE NJIEP;Lo;0;L;;;;;N;;;;;
+A439;YI SYLLABLE NJUOX;Lo;0;L;;;;;N;;;;;
+A43A;YI SYLLABLE NJUO;Lo;0;L;;;;;N;;;;;
+A43B;YI SYLLABLE NJOT;Lo;0;L;;;;;N;;;;;
+A43C;YI SYLLABLE NJOX;Lo;0;L;;;;;N;;;;;
+A43D;YI SYLLABLE NJO;Lo;0;L;;;;;N;;;;;
+A43E;YI SYLLABLE NJOP;Lo;0;L;;;;;N;;;;;
+A43F;YI SYLLABLE NJUX;Lo;0;L;;;;;N;;;;;
+A440;YI SYLLABLE NJU;Lo;0;L;;;;;N;;;;;
+A441;YI SYLLABLE NJUP;Lo;0;L;;;;;N;;;;;
+A442;YI SYLLABLE NJURX;Lo;0;L;;;;;N;;;;;
+A443;YI SYLLABLE NJUR;Lo;0;L;;;;;N;;;;;
+A444;YI SYLLABLE NJYT;Lo;0;L;;;;;N;;;;;
+A445;YI SYLLABLE NJYX;Lo;0;L;;;;;N;;;;;
+A446;YI SYLLABLE NJY;Lo;0;L;;;;;N;;;;;
+A447;YI SYLLABLE NJYP;Lo;0;L;;;;;N;;;;;
+A448;YI SYLLABLE NJYRX;Lo;0;L;;;;;N;;;;;
+A449;YI SYLLABLE NJYR;Lo;0;L;;;;;N;;;;;
+A44A;YI SYLLABLE NYIT;Lo;0;L;;;;;N;;;;;
+A44B;YI SYLLABLE NYIX;Lo;0;L;;;;;N;;;;;
+A44C;YI SYLLABLE NYI;Lo;0;L;;;;;N;;;;;
+A44D;YI SYLLABLE NYIP;Lo;0;L;;;;;N;;;;;
+A44E;YI SYLLABLE NYIET;Lo;0;L;;;;;N;;;;;
+A44F;YI SYLLABLE NYIEX;Lo;0;L;;;;;N;;;;;
+A450;YI SYLLABLE NYIE;Lo;0;L;;;;;N;;;;;
+A451;YI SYLLABLE NYIEP;Lo;0;L;;;;;N;;;;;
+A452;YI SYLLABLE NYUOX;Lo;0;L;;;;;N;;;;;
+A453;YI SYLLABLE NYUO;Lo;0;L;;;;;N;;;;;
+A454;YI SYLLABLE NYUOP;Lo;0;L;;;;;N;;;;;
+A455;YI SYLLABLE NYOT;Lo;0;L;;;;;N;;;;;
+A456;YI SYLLABLE NYOX;Lo;0;L;;;;;N;;;;;
+A457;YI SYLLABLE NYO;Lo;0;L;;;;;N;;;;;
+A458;YI SYLLABLE NYOP;Lo;0;L;;;;;N;;;;;
+A459;YI SYLLABLE NYUT;Lo;0;L;;;;;N;;;;;
+A45A;YI SYLLABLE NYUX;Lo;0;L;;;;;N;;;;;
+A45B;YI SYLLABLE NYU;Lo;0;L;;;;;N;;;;;
+A45C;YI SYLLABLE NYUP;Lo;0;L;;;;;N;;;;;
+A45D;YI SYLLABLE XIT;Lo;0;L;;;;;N;;;;;
+A45E;YI SYLLABLE XIX;Lo;0;L;;;;;N;;;;;
+A45F;YI SYLLABLE XI;Lo;0;L;;;;;N;;;;;
+A460;YI SYLLABLE XIP;Lo;0;L;;;;;N;;;;;
+A461;YI SYLLABLE XIET;Lo;0;L;;;;;N;;;;;
+A462;YI SYLLABLE XIEX;Lo;0;L;;;;;N;;;;;
+A463;YI SYLLABLE XIE;Lo;0;L;;;;;N;;;;;
+A464;YI SYLLABLE XIEP;Lo;0;L;;;;;N;;;;;
+A465;YI SYLLABLE XUOX;Lo;0;L;;;;;N;;;;;
+A466;YI SYLLABLE XUO;Lo;0;L;;;;;N;;;;;
+A467;YI SYLLABLE XOT;Lo;0;L;;;;;N;;;;;
+A468;YI SYLLABLE XOX;Lo;0;L;;;;;N;;;;;
+A469;YI SYLLABLE XO;Lo;0;L;;;;;N;;;;;
+A46A;YI SYLLABLE XOP;Lo;0;L;;;;;N;;;;;
+A46B;YI SYLLABLE XYT;Lo;0;L;;;;;N;;;;;
+A46C;YI SYLLABLE XYX;Lo;0;L;;;;;N;;;;;
+A46D;YI SYLLABLE XY;Lo;0;L;;;;;N;;;;;
+A46E;YI SYLLABLE XYP;Lo;0;L;;;;;N;;;;;
+A46F;YI SYLLABLE XYRX;Lo;0;L;;;;;N;;;;;
+A470;YI SYLLABLE XYR;Lo;0;L;;;;;N;;;;;
+A471;YI SYLLABLE YIT;Lo;0;L;;;;;N;;;;;
+A472;YI SYLLABLE YIX;Lo;0;L;;;;;N;;;;;
+A473;YI SYLLABLE YI;Lo;0;L;;;;;N;;;;;
+A474;YI SYLLABLE YIP;Lo;0;L;;;;;N;;;;;
+A475;YI SYLLABLE YIET;Lo;0;L;;;;;N;;;;;
+A476;YI SYLLABLE YIEX;Lo;0;L;;;;;N;;;;;
+A477;YI SYLLABLE YIE;Lo;0;L;;;;;N;;;;;
+A478;YI SYLLABLE YIEP;Lo;0;L;;;;;N;;;;;
+A479;YI SYLLABLE YUOT;Lo;0;L;;;;;N;;;;;
+A47A;YI SYLLABLE YUOX;Lo;0;L;;;;;N;;;;;
+A47B;YI SYLLABLE YUO;Lo;0;L;;;;;N;;;;;
+A47C;YI SYLLABLE YUOP;Lo;0;L;;;;;N;;;;;
+A47D;YI SYLLABLE YOT;Lo;0;L;;;;;N;;;;;
+A47E;YI SYLLABLE YOX;Lo;0;L;;;;;N;;;;;
+A47F;YI SYLLABLE YO;Lo;0;L;;;;;N;;;;;
+A480;YI SYLLABLE YOP;Lo;0;L;;;;;N;;;;;
+A481;YI SYLLABLE YUT;Lo;0;L;;;;;N;;;;;
+A482;YI SYLLABLE YUX;Lo;0;L;;;;;N;;;;;
+A483;YI SYLLABLE YU;Lo;0;L;;;;;N;;;;;
+A484;YI SYLLABLE YUP;Lo;0;L;;;;;N;;;;;
+A485;YI SYLLABLE YURX;Lo;0;L;;;;;N;;;;;
+A486;YI SYLLABLE YUR;Lo;0;L;;;;;N;;;;;
+A487;YI SYLLABLE YYT;Lo;0;L;;;;;N;;;;;
+A488;YI SYLLABLE YYX;Lo;0;L;;;;;N;;;;;
+A489;YI SYLLABLE YY;Lo;0;L;;;;;N;;;;;
+A48A;YI SYLLABLE YYP;Lo;0;L;;;;;N;;;;;
+A48B;YI SYLLABLE YYRX;Lo;0;L;;;;;N;;;;;
+A48C;YI SYLLABLE YYR;Lo;0;L;;;;;N;;;;;
+A490;YI RADICAL QOT;So;0;ON;;;;;N;;;;;
+A491;YI RADICAL LI;So;0;ON;;;;;N;;;;;
+A492;YI RADICAL KIT;So;0;ON;;;;;N;;;;;
+A493;YI RADICAL NYIP;So;0;ON;;;;;N;;;;;
+A494;YI RADICAL CYP;So;0;ON;;;;;N;;;;;
+A495;YI RADICAL SSI;So;0;ON;;;;;N;;;;;
+A496;YI RADICAL GGOP;So;0;ON;;;;;N;;;;;
+A497;YI RADICAL GEP;So;0;ON;;;;;N;;;;;
+A498;YI RADICAL MI;So;0;ON;;;;;N;;;;;
+A499;YI RADICAL HXIT;So;0;ON;;;;;N;;;;;
+A49A;YI RADICAL LYR;So;0;ON;;;;;N;;;;;
+A49B;YI RADICAL BBUT;So;0;ON;;;;;N;;;;;
+A49C;YI RADICAL MOP;So;0;ON;;;;;N;;;;;
+A49D;YI RADICAL YO;So;0;ON;;;;;N;;;;;
+A49E;YI RADICAL PUT;So;0;ON;;;;;N;;;;;
+A49F;YI RADICAL HXUO;So;0;ON;;;;;N;;;;;
+A4A0;YI RADICAL TAT;So;0;ON;;;;;N;;;;;
+A4A1;YI RADICAL GA;So;0;ON;;;;;N;;;;;
+A4A4;YI RADICAL DDUR;So;0;ON;;;;;N;;;;;
+A4A5;YI RADICAL BUR;So;0;ON;;;;;N;;;;;
+A4A6;YI RADICAL GGUO;So;0;ON;;;;;N;;;;;
+A4A7;YI RADICAL NYOP;So;0;ON;;;;;N;;;;;
+A4A8;YI RADICAL TU;So;0;ON;;;;;N;;;;;
+A4A9;YI RADICAL OP;So;0;ON;;;;;N;;;;;
+A4AA;YI RADICAL JJUT;So;0;ON;;;;;N;;;;;
+A4AB;YI RADICAL ZOT;So;0;ON;;;;;N;;;;;
+A4AC;YI RADICAL PYT;So;0;ON;;;;;N;;;;;
+A4AD;YI RADICAL HMO;So;0;ON;;;;;N;;;;;
+A4AE;YI RADICAL YIT;So;0;ON;;;;;N;;;;;
+A4AF;YI RADICAL VUR;So;0;ON;;;;;N;;;;;
+A4B0;YI RADICAL SHY;So;0;ON;;;;;N;;;;;
+A4B1;YI RADICAL VEP;So;0;ON;;;;;N;;;;;
+A4B2;YI RADICAL ZA;So;0;ON;;;;;N;;;;;
+A4B3;YI RADICAL JO;So;0;ON;;;;;N;;;;;
+A4B5;YI RADICAL JJY;So;0;ON;;;;;N;;;;;
+A4B6;YI RADICAL GOT;So;0;ON;;;;;N;;;;;
+A4B7;YI RADICAL JJIE;So;0;ON;;;;;N;;;;;
+A4B8;YI RADICAL WO;So;0;ON;;;;;N;;;;;
+A4B9;YI RADICAL DU;So;0;ON;;;;;N;;;;;
+A4BA;YI RADICAL SHUR;So;0;ON;;;;;N;;;;;
+A4BB;YI RADICAL LIE;So;0;ON;;;;;N;;;;;
+A4BC;YI RADICAL CY;So;0;ON;;;;;N;;;;;
+A4BD;YI RADICAL CUOP;So;0;ON;;;;;N;;;;;
+A4BE;YI RADICAL CIP;So;0;ON;;;;;N;;;;;
+A4BF;YI RADICAL HXOP;So;0;ON;;;;;N;;;;;
+A4C0;YI RADICAL SHAT;So;0;ON;;;;;N;;;;;
+A4C2;YI RADICAL SHOP;So;0;ON;;;;;N;;;;;
+A4C3;YI RADICAL CHE;So;0;ON;;;;;N;;;;;
+A4C4;YI RADICAL ZZIET;So;0;ON;;;;;N;;;;;
+A4C6;YI RADICAL KE;So;0;ON;;;;;N;;;;;
+AC00;<Hangul Syllable, First>;Lo;0;L;;;;;N;;;;;
+D7A3;<Hangul Syllable, Last>;Lo;0;L;;;;;N;;;;;
+D800;<Non Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DB7F;<Non Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+DB80;<Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DBFF;<Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+DC00;<Low Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DFFF;<Low Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+E000;<Private Use, First>;Co;0;L;;;;;N;;;;;
+F8FF;<Private Use, Last>;Co;0;L;;;;;N;;;;;
+F900;CJK COMPATIBILITY IDEOGRAPH-F900;Lo;0;L;8C48;;;;N;;;;;
+F901;CJK COMPATIBILITY IDEOGRAPH-F901;Lo;0;L;66F4;;;;N;;;;;
+F902;CJK COMPATIBILITY IDEOGRAPH-F902;Lo;0;L;8ECA;;;;N;;;;;
+F903;CJK COMPATIBILITY IDEOGRAPH-F903;Lo;0;L;8CC8;;;;N;;;;;
+F904;CJK COMPATIBILITY IDEOGRAPH-F904;Lo;0;L;6ED1;;;;N;;;;;
+F905;CJK COMPATIBILITY IDEOGRAPH-F905;Lo;0;L;4E32;;;;N;;;;;
+F906;CJK COMPATIBILITY IDEOGRAPH-F906;Lo;0;L;53E5;;;;N;;;;;
+F907;CJK COMPATIBILITY IDEOGRAPH-F907;Lo;0;L;9F9C;;;;N;;;;;
+F908;CJK COMPATIBILITY IDEOGRAPH-F908;Lo;0;L;9F9C;;;;N;;;;;
+F909;CJK COMPATIBILITY IDEOGRAPH-F909;Lo;0;L;5951;;;;N;;;;;
+F90A;CJK COMPATIBILITY IDEOGRAPH-F90A;Lo;0;L;91D1;;;;N;;;;;
+F90B;CJK COMPATIBILITY IDEOGRAPH-F90B;Lo;0;L;5587;;;;N;;;;;
+F90C;CJK COMPATIBILITY IDEOGRAPH-F90C;Lo;0;L;5948;;;;N;;;;;
+F90D;CJK COMPATIBILITY IDEOGRAPH-F90D;Lo;0;L;61F6;;;;N;;;;;
+F90E;CJK COMPATIBILITY IDEOGRAPH-F90E;Lo;0;L;7669;;;;N;;;;;
+F90F;CJK COMPATIBILITY IDEOGRAPH-F90F;Lo;0;L;7F85;;;;N;;;;;
+F910;CJK COMPATIBILITY IDEOGRAPH-F910;Lo;0;L;863F;;;;N;;;;;
+F911;CJK COMPATIBILITY IDEOGRAPH-F911;Lo;0;L;87BA;;;;N;;;;;
+F912;CJK COMPATIBILITY IDEOGRAPH-F912;Lo;0;L;88F8;;;;N;;;;;
+F913;CJK COMPATIBILITY IDEOGRAPH-F913;Lo;0;L;908F;;;;N;;;;;
+F914;CJK COMPATIBILITY IDEOGRAPH-F914;Lo;0;L;6A02;;;;N;;;;;
+F915;CJK COMPATIBILITY IDEOGRAPH-F915;Lo;0;L;6D1B;;;;N;;;;;
+F916;CJK COMPATIBILITY IDEOGRAPH-F916;Lo;0;L;70D9;;;;N;;;;;
+F917;CJK COMPATIBILITY IDEOGRAPH-F917;Lo;0;L;73DE;;;;N;;;;;
+F918;CJK COMPATIBILITY IDEOGRAPH-F918;Lo;0;L;843D;;;;N;;;;;
+F919;CJK COMPATIBILITY IDEOGRAPH-F919;Lo;0;L;916A;;;;N;;;;;
+F91A;CJK COMPATIBILITY IDEOGRAPH-F91A;Lo;0;L;99F1;;;;N;;;;;
+F91B;CJK COMPATIBILITY IDEOGRAPH-F91B;Lo;0;L;4E82;;;;N;;;;;
+F91C;CJK COMPATIBILITY IDEOGRAPH-F91C;Lo;0;L;5375;;;;N;;;;;
+F91D;CJK COMPATIBILITY IDEOGRAPH-F91D;Lo;0;L;6B04;;;;N;;;;;
+F91E;CJK COMPATIBILITY IDEOGRAPH-F91E;Lo;0;L;721B;;;;N;;;;;
+F91F;CJK COMPATIBILITY IDEOGRAPH-F91F;Lo;0;L;862D;;;;N;;;;;
+F920;CJK COMPATIBILITY IDEOGRAPH-F920;Lo;0;L;9E1E;;;;N;;;;;
+F921;CJK COMPATIBILITY IDEOGRAPH-F921;Lo;0;L;5D50;;;;N;;;;;
+F922;CJK COMPATIBILITY IDEOGRAPH-F922;Lo;0;L;6FEB;;;;N;;;;;
+F923;CJK COMPATIBILITY IDEOGRAPH-F923;Lo;0;L;85CD;;;;N;;;;;
+F924;CJK COMPATIBILITY IDEOGRAPH-F924;Lo;0;L;8964;;;;N;;;;;
+F925;CJK COMPATIBILITY IDEOGRAPH-F925;Lo;0;L;62C9;;;;N;;;;;
+F926;CJK COMPATIBILITY IDEOGRAPH-F926;Lo;0;L;81D8;;;;N;;;;;
+F927;CJK COMPATIBILITY IDEOGRAPH-F927;Lo;0;L;881F;;;;N;;;;;
+F928;CJK COMPATIBILITY IDEOGRAPH-F928;Lo;0;L;5ECA;;;;N;;;;;
+F929;CJK COMPATIBILITY IDEOGRAPH-F929;Lo;0;L;6717;;;;N;;;;;
+F92A;CJK COMPATIBILITY IDEOGRAPH-F92A;Lo;0;L;6D6A;;;;N;;;;;
+F92B;CJK COMPATIBILITY IDEOGRAPH-F92B;Lo;0;L;72FC;;;;N;;;;;
+F92C;CJK COMPATIBILITY IDEOGRAPH-F92C;Lo;0;L;90CE;;;;N;;;;;
+F92D;CJK COMPATIBILITY IDEOGRAPH-F92D;Lo;0;L;4F86;;;;N;;;;;
+F92E;CJK COMPATIBILITY IDEOGRAPH-F92E;Lo;0;L;51B7;;;;N;;;;;
+F92F;CJK COMPATIBILITY IDEOGRAPH-F92F;Lo;0;L;52DE;;;;N;;;;;
+F930;CJK COMPATIBILITY IDEOGRAPH-F930;Lo;0;L;64C4;;;;N;;;;;
+F931;CJK COMPATIBILITY IDEOGRAPH-F931;Lo;0;L;6AD3;;;;N;;;;;
+F932;CJK COMPATIBILITY IDEOGRAPH-F932;Lo;0;L;7210;;;;N;;;;;
+F933;CJK COMPATIBILITY IDEOGRAPH-F933;Lo;0;L;76E7;;;;N;;;;;
+F934;CJK COMPATIBILITY IDEOGRAPH-F934;Lo;0;L;8001;;;;N;;;;;
+F935;CJK COMPATIBILITY IDEOGRAPH-F935;Lo;0;L;8606;;;;N;;;;;
+F936;CJK COMPATIBILITY IDEOGRAPH-F936;Lo;0;L;865C;;;;N;;;;;
+F937;CJK COMPATIBILITY IDEOGRAPH-F937;Lo;0;L;8DEF;;;;N;;;;;
+F938;CJK COMPATIBILITY IDEOGRAPH-F938;Lo;0;L;9732;;;;N;;;;;
+F939;CJK COMPATIBILITY IDEOGRAPH-F939;Lo;0;L;9B6F;;;;N;;;;;
+F93A;CJK COMPATIBILITY IDEOGRAPH-F93A;Lo;0;L;9DFA;;;;N;;;;;
+F93B;CJK COMPATIBILITY IDEOGRAPH-F93B;Lo;0;L;788C;;;;N;;;;;
+F93C;CJK COMPATIBILITY IDEOGRAPH-F93C;Lo;0;L;797F;;;;N;;;;;
+F93D;CJK COMPATIBILITY IDEOGRAPH-F93D;Lo;0;L;7DA0;;;;N;;;;;
+F93E;CJK COMPATIBILITY IDEOGRAPH-F93E;Lo;0;L;83C9;;;;N;;;;;
+F93F;CJK COMPATIBILITY IDEOGRAPH-F93F;Lo;0;L;9304;;;;N;;;;;
+F940;CJK COMPATIBILITY IDEOGRAPH-F940;Lo;0;L;9E7F;;;;N;;;;;
+F941;CJK COMPATIBILITY IDEOGRAPH-F941;Lo;0;L;8AD6;;;;N;;;;;
+F942;CJK COMPATIBILITY IDEOGRAPH-F942;Lo;0;L;58DF;;;;N;;;;;
+F943;CJK COMPATIBILITY IDEOGRAPH-F943;Lo;0;L;5F04;;;;N;;;;;
+F944;CJK COMPATIBILITY IDEOGRAPH-F944;Lo;0;L;7C60;;;;N;;;;;
+F945;CJK COMPATIBILITY IDEOGRAPH-F945;Lo;0;L;807E;;;;N;;;;;
+F946;CJK COMPATIBILITY IDEOGRAPH-F946;Lo;0;L;7262;;;;N;;;;;
+F947;CJK COMPATIBILITY IDEOGRAPH-F947;Lo;0;L;78CA;;;;N;;;;;
+F948;CJK COMPATIBILITY IDEOGRAPH-F948;Lo;0;L;8CC2;;;;N;;;;;
+F949;CJK COMPATIBILITY IDEOGRAPH-F949;Lo;0;L;96F7;;;;N;;;;;
+F94A;CJK COMPATIBILITY IDEOGRAPH-F94A;Lo;0;L;58D8;;;;N;;;;;
+F94B;CJK COMPATIBILITY IDEOGRAPH-F94B;Lo;0;L;5C62;;;;N;;;;;
+F94C;CJK COMPATIBILITY IDEOGRAPH-F94C;Lo;0;L;6A13;;;;N;;;;;
+F94D;CJK COMPATIBILITY IDEOGRAPH-F94D;Lo;0;L;6DDA;;;;N;;;;;
+F94E;CJK COMPATIBILITY IDEOGRAPH-F94E;Lo;0;L;6F0F;;;;N;;;;;
+F94F;CJK COMPATIBILITY IDEOGRAPH-F94F;Lo;0;L;7D2F;;;;N;;;;;
+F950;CJK COMPATIBILITY IDEOGRAPH-F950;Lo;0;L;7E37;;;;N;;;;;
+F951;CJK COMPATIBILITY IDEOGRAPH-F951;Lo;0;L;96FB;;;;N;;;;;
+F952;CJK COMPATIBILITY IDEOGRAPH-F952;Lo;0;L;52D2;;;;N;;;;;
+F953;CJK COMPATIBILITY IDEOGRAPH-F953;Lo;0;L;808B;;;;N;;;;;
+F954;CJK COMPATIBILITY IDEOGRAPH-F954;Lo;0;L;51DC;;;;N;;;;;
+F955;CJK COMPATIBILITY IDEOGRAPH-F955;Lo;0;L;51CC;;;;N;;;;;
+F956;CJK COMPATIBILITY IDEOGRAPH-F956;Lo;0;L;7A1C;;;;N;;;;;
+F957;CJK COMPATIBILITY IDEOGRAPH-F957;Lo;0;L;7DBE;;;;N;;;;;
+F958;CJK COMPATIBILITY IDEOGRAPH-F958;Lo;0;L;83F1;;;;N;;;;;
+F959;CJK COMPATIBILITY IDEOGRAPH-F959;Lo;0;L;9675;;;;N;;;;;
+F95A;CJK COMPATIBILITY IDEOGRAPH-F95A;Lo;0;L;8B80;;;;N;;;;;
+F95B;CJK COMPATIBILITY IDEOGRAPH-F95B;Lo;0;L;62CF;;;;N;;;;;
+F95C;CJK COMPATIBILITY IDEOGRAPH-F95C;Lo;0;L;6A02;;;;N;;;;;
+F95D;CJK COMPATIBILITY IDEOGRAPH-F95D;Lo;0;L;8AFE;;;;N;;;;;
+F95E;CJK COMPATIBILITY IDEOGRAPH-F95E;Lo;0;L;4E39;;;;N;;;;;
+F95F;CJK COMPATIBILITY IDEOGRAPH-F95F;Lo;0;L;5BE7;;;;N;;;;;
+F960;CJK COMPATIBILITY IDEOGRAPH-F960;Lo;0;L;6012;;;;N;;;;;
+F961;CJK COMPATIBILITY IDEOGRAPH-F961;Lo;0;L;7387;;;;N;;;;;
+F962;CJK COMPATIBILITY IDEOGRAPH-F962;Lo;0;L;7570;;;;N;;;;;
+F963;CJK COMPATIBILITY IDEOGRAPH-F963;Lo;0;L;5317;;;;N;;;;;
+F964;CJK COMPATIBILITY IDEOGRAPH-F964;Lo;0;L;78FB;;;;N;;;;;
+F965;CJK COMPATIBILITY IDEOGRAPH-F965;Lo;0;L;4FBF;;;;N;;;;;
+F966;CJK COMPATIBILITY IDEOGRAPH-F966;Lo;0;L;5FA9;;;;N;;;;;
+F967;CJK COMPATIBILITY IDEOGRAPH-F967;Lo;0;L;4E0D;;;;N;;;;;
+F968;CJK COMPATIBILITY IDEOGRAPH-F968;Lo;0;L;6CCC;;;;N;;;;;
+F969;CJK COMPATIBILITY IDEOGRAPH-F969;Lo;0;L;6578;;;;N;;;;;
+F96A;CJK COMPATIBILITY IDEOGRAPH-F96A;Lo;0;L;7D22;;;;N;;;;;
+F96B;CJK COMPATIBILITY IDEOGRAPH-F96B;Lo;0;L;53C3;;;;N;;;;;
+F96C;CJK COMPATIBILITY IDEOGRAPH-F96C;Lo;0;L;585E;;;;N;;;;;
+F96D;CJK COMPATIBILITY IDEOGRAPH-F96D;Lo;0;L;7701;;;;N;;;;;
+F96E;CJK COMPATIBILITY IDEOGRAPH-F96E;Lo;0;L;8449;;;;N;;;;;
+F96F;CJK COMPATIBILITY IDEOGRAPH-F96F;Lo;0;L;8AAA;;;;N;;;;;
+F970;CJK COMPATIBILITY IDEOGRAPH-F970;Lo;0;L;6BBA;;;;N;;;;;
+F971;CJK COMPATIBILITY IDEOGRAPH-F971;Lo;0;L;8FB0;;;;N;;;;;
+F972;CJK COMPATIBILITY IDEOGRAPH-F972;Lo;0;L;6C88;;;;N;;;;;
+F973;CJK COMPATIBILITY IDEOGRAPH-F973;Lo;0;L;62FE;;;;N;;;;;
+F974;CJK COMPATIBILITY IDEOGRAPH-F974;Lo;0;L;82E5;;;;N;;;;;
+F975;CJK COMPATIBILITY IDEOGRAPH-F975;Lo;0;L;63A0;;;;N;;;;;
+F976;CJK COMPATIBILITY IDEOGRAPH-F976;Lo;0;L;7565;;;;N;;;;;
+F977;CJK COMPATIBILITY IDEOGRAPH-F977;Lo;0;L;4EAE;;;;N;;;;;
+F978;CJK COMPATIBILITY IDEOGRAPH-F978;Lo;0;L;5169;;;;N;;;;;
+F979;CJK COMPATIBILITY IDEOGRAPH-F979;Lo;0;L;51C9;;;;N;;;;;
+F97A;CJK COMPATIBILITY IDEOGRAPH-F97A;Lo;0;L;6881;;;;N;;;;;
+F97B;CJK COMPATIBILITY IDEOGRAPH-F97B;Lo;0;L;7CE7;;;;N;;;;;
+F97C;CJK COMPATIBILITY IDEOGRAPH-F97C;Lo;0;L;826F;;;;N;;;;;
+F97D;CJK COMPATIBILITY IDEOGRAPH-F97D;Lo;0;L;8AD2;;;;N;;;;;
+F97E;CJK COMPATIBILITY IDEOGRAPH-F97E;Lo;0;L;91CF;;;;N;;;;;
+F97F;CJK COMPATIBILITY IDEOGRAPH-F97F;Lo;0;L;52F5;;;;N;;;;;
+F980;CJK COMPATIBILITY IDEOGRAPH-F980;Lo;0;L;5442;;;;N;;;;;
+F981;CJK COMPATIBILITY IDEOGRAPH-F981;Lo;0;L;5973;;;;N;;;;;
+F982;CJK COMPATIBILITY IDEOGRAPH-F982;Lo;0;L;5EEC;;;;N;;;;;
+F983;CJK COMPATIBILITY IDEOGRAPH-F983;Lo;0;L;65C5;;;;N;;;;;
+F984;CJK COMPATIBILITY IDEOGRAPH-F984;Lo;0;L;6FFE;;;;N;;;;;
+F985;CJK COMPATIBILITY IDEOGRAPH-F985;Lo;0;L;792A;;;;N;;;;;
+F986;CJK COMPATIBILITY IDEOGRAPH-F986;Lo;0;L;95AD;;;;N;;;;;
+F987;CJK COMPATIBILITY IDEOGRAPH-F987;Lo;0;L;9A6A;;;;N;;;;;
+F988;CJK COMPATIBILITY IDEOGRAPH-F988;Lo;0;L;9E97;;;;N;;;;;
+F989;CJK COMPATIBILITY IDEOGRAPH-F989;Lo;0;L;9ECE;;;;N;;;;;
+F98A;CJK COMPATIBILITY IDEOGRAPH-F98A;Lo;0;L;529B;;;;N;;;;;
+F98B;CJK COMPATIBILITY IDEOGRAPH-F98B;Lo;0;L;66C6;;;;N;;;;;
+F98C;CJK COMPATIBILITY IDEOGRAPH-F98C;Lo;0;L;6B77;;;;N;;;;;
+F98D;CJK COMPATIBILITY IDEOGRAPH-F98D;Lo;0;L;8F62;;;;N;;;;;
+F98E;CJK COMPATIBILITY IDEOGRAPH-F98E;Lo;0;L;5E74;;;;N;;;;;
+F98F;CJK COMPATIBILITY IDEOGRAPH-F98F;Lo;0;L;6190;;;;N;;;;;
+F990;CJK COMPATIBILITY IDEOGRAPH-F990;Lo;0;L;6200;;;;N;;;;;
+F991;CJK COMPATIBILITY IDEOGRAPH-F991;Lo;0;L;649A;;;;N;;;;;
+F992;CJK COMPATIBILITY IDEOGRAPH-F992;Lo;0;L;6F23;;;;N;;;;;
+F993;CJK COMPATIBILITY IDEOGRAPH-F993;Lo;0;L;7149;;;;N;;;;;
+F994;CJK COMPATIBILITY IDEOGRAPH-F994;Lo;0;L;7489;;;;N;;;;;
+F995;CJK COMPATIBILITY IDEOGRAPH-F995;Lo;0;L;79CA;;;;N;;;;;
+F996;CJK COMPATIBILITY IDEOGRAPH-F996;Lo;0;L;7DF4;;;;N;;;;;
+F997;CJK COMPATIBILITY IDEOGRAPH-F997;Lo;0;L;806F;;;;N;;;;;
+F998;CJK COMPATIBILITY IDEOGRAPH-F998;Lo;0;L;8F26;;;;N;;;;;
+F999;CJK COMPATIBILITY IDEOGRAPH-F999;Lo;0;L;84EE;;;;N;;;;;
+F99A;CJK COMPATIBILITY IDEOGRAPH-F99A;Lo;0;L;9023;;;;N;;;;;
+F99B;CJK COMPATIBILITY IDEOGRAPH-F99B;Lo;0;L;934A;;;;N;;;;;
+F99C;CJK COMPATIBILITY IDEOGRAPH-F99C;Lo;0;L;5217;;;;N;;;;;
+F99D;CJK COMPATIBILITY IDEOGRAPH-F99D;Lo;0;L;52A3;;;;N;;;;;
+F99E;CJK COMPATIBILITY IDEOGRAPH-F99E;Lo;0;L;54BD;;;;N;;;;;
+F99F;CJK COMPATIBILITY IDEOGRAPH-F99F;Lo;0;L;70C8;;;;N;;;;;
+F9A0;CJK COMPATIBILITY IDEOGRAPH-F9A0;Lo;0;L;88C2;;;;N;;;;;
+F9A1;CJK COMPATIBILITY IDEOGRAPH-F9A1;Lo;0;L;8AAA;;;;N;;;;;
+F9A2;CJK COMPATIBILITY IDEOGRAPH-F9A2;Lo;0;L;5EC9;;;;N;;;;;
+F9A3;CJK COMPATIBILITY IDEOGRAPH-F9A3;Lo;0;L;5FF5;;;;N;;;;;
+F9A4;CJK COMPATIBILITY IDEOGRAPH-F9A4;Lo;0;L;637B;;;;N;;;;;
+F9A5;CJK COMPATIBILITY IDEOGRAPH-F9A5;Lo;0;L;6BAE;;;;N;;;;;
+F9A6;CJK COMPATIBILITY IDEOGRAPH-F9A6;Lo;0;L;7C3E;;;;N;;;;;
+F9A7;CJK COMPATIBILITY IDEOGRAPH-F9A7;Lo;0;L;7375;;;;N;;;;;
+F9A8;CJK COMPATIBILITY IDEOGRAPH-F9A8;Lo;0;L;4EE4;;;;N;;;;;
+F9A9;CJK COMPATIBILITY IDEOGRAPH-F9A9;Lo;0;L;56F9;;;;N;;;;;
+F9AA;CJK COMPATIBILITY IDEOGRAPH-F9AA;Lo;0;L;5BE7;;;;N;;;;;
+F9AB;CJK COMPATIBILITY IDEOGRAPH-F9AB;Lo;0;L;5DBA;;;;N;;;;;
+F9AC;CJK COMPATIBILITY IDEOGRAPH-F9AC;Lo;0;L;601C;;;;N;;;;;
+F9AD;CJK COMPATIBILITY IDEOGRAPH-F9AD;Lo;0;L;73B2;;;;N;;;;;
+F9AE;CJK COMPATIBILITY IDEOGRAPH-F9AE;Lo;0;L;7469;;;;N;;;;;
+F9AF;CJK COMPATIBILITY IDEOGRAPH-F9AF;Lo;0;L;7F9A;;;;N;;;;;
+F9B0;CJK COMPATIBILITY IDEOGRAPH-F9B0;Lo;0;L;8046;;;;N;;;;;
+F9B1;CJK COMPATIBILITY IDEOGRAPH-F9B1;Lo;0;L;9234;;;;N;;;;;
+F9B2;CJK COMPATIBILITY IDEOGRAPH-F9B2;Lo;0;L;96F6;;;;N;;;;;
+F9B3;CJK COMPATIBILITY IDEOGRAPH-F9B3;Lo;0;L;9748;;;;N;;;;;
+F9B4;CJK COMPATIBILITY IDEOGRAPH-F9B4;Lo;0;L;9818;;;;N;;;;;
+F9B5;CJK COMPATIBILITY IDEOGRAPH-F9B5;Lo;0;L;4F8B;;;;N;;;;;
+F9B6;CJK COMPATIBILITY IDEOGRAPH-F9B6;Lo;0;L;79AE;;;;N;;;;;
+F9B7;CJK COMPATIBILITY IDEOGRAPH-F9B7;Lo;0;L;91B4;;;;N;;;;;
+F9B8;CJK COMPATIBILITY IDEOGRAPH-F9B8;Lo;0;L;96B8;;;;N;;;;;
+F9B9;CJK COMPATIBILITY IDEOGRAPH-F9B9;Lo;0;L;60E1;;;;N;;;;;
+F9BA;CJK COMPATIBILITY IDEOGRAPH-F9BA;Lo;0;L;4E86;;;;N;;;;;
+F9BB;CJK COMPATIBILITY IDEOGRAPH-F9BB;Lo;0;L;50DA;;;;N;;;;;
+F9BC;CJK COMPATIBILITY IDEOGRAPH-F9BC;Lo;0;L;5BEE;;;;N;;;;;
+F9BD;CJK COMPATIBILITY IDEOGRAPH-F9BD;Lo;0;L;5C3F;;;;N;;;;;
+F9BE;CJK COMPATIBILITY IDEOGRAPH-F9BE;Lo;0;L;6599;;;;N;;;;;
+F9BF;CJK COMPATIBILITY IDEOGRAPH-F9BF;Lo;0;L;6A02;;;;N;;;;;
+F9C0;CJK COMPATIBILITY IDEOGRAPH-F9C0;Lo;0;L;71CE;;;;N;;;;;
+F9C1;CJK COMPATIBILITY IDEOGRAPH-F9C1;Lo;0;L;7642;;;;N;;;;;
+F9C2;CJK COMPATIBILITY IDEOGRAPH-F9C2;Lo;0;L;84FC;;;;N;;;;;
+F9C3;CJK COMPATIBILITY IDEOGRAPH-F9C3;Lo;0;L;907C;;;;N;;;;;
+F9C4;CJK COMPATIBILITY IDEOGRAPH-F9C4;Lo;0;L;9F8D;;;;N;;;;;
+F9C5;CJK COMPATIBILITY IDEOGRAPH-F9C5;Lo;0;L;6688;;;;N;;;;;
+F9C6;CJK COMPATIBILITY IDEOGRAPH-F9C6;Lo;0;L;962E;;;;N;;;;;
+F9C7;CJK COMPATIBILITY IDEOGRAPH-F9C7;Lo;0;L;5289;;;;N;;;;;
+F9C8;CJK COMPATIBILITY IDEOGRAPH-F9C8;Lo;0;L;677B;;;;N;;;;;
+F9C9;CJK COMPATIBILITY IDEOGRAPH-F9C9;Lo;0;L;67F3;;;;N;;;;;
+F9CA;CJK COMPATIBILITY IDEOGRAPH-F9CA;Lo;0;L;6D41;;;;N;;;;;
+F9CB;CJK COMPATIBILITY IDEOGRAPH-F9CB;Lo;0;L;6E9C;;;;N;;;;;
+F9CC;CJK COMPATIBILITY IDEOGRAPH-F9CC;Lo;0;L;7409;;;;N;;;;;
+F9CD;CJK COMPATIBILITY IDEOGRAPH-F9CD;Lo;0;L;7559;;;;N;;;;;
+F9CE;CJK COMPATIBILITY IDEOGRAPH-F9CE;Lo;0;L;786B;;;;N;;;;;
+F9CF;CJK COMPATIBILITY IDEOGRAPH-F9CF;Lo;0;L;7D10;;;;N;;;;;
+F9D0;CJK COMPATIBILITY IDEOGRAPH-F9D0;Lo;0;L;985E;;;;N;;;;;
+F9D1;CJK COMPATIBILITY IDEOGRAPH-F9D1;Lo;0;L;516D;;;;N;;;;;
+F9D2;CJK COMPATIBILITY IDEOGRAPH-F9D2;Lo;0;L;622E;;;;N;;;;;
+F9D3;CJK COMPATIBILITY IDEOGRAPH-F9D3;Lo;0;L;9678;;;;N;;;;;
+F9D4;CJK COMPATIBILITY IDEOGRAPH-F9D4;Lo;0;L;502B;;;;N;;;;;
+F9D5;CJK COMPATIBILITY IDEOGRAPH-F9D5;Lo;0;L;5D19;;;;N;;;;;
+F9D6;CJK COMPATIBILITY IDEOGRAPH-F9D6;Lo;0;L;6DEA;;;;N;;;;;
+F9D7;CJK COMPATIBILITY IDEOGRAPH-F9D7;Lo;0;L;8F2A;;;;N;;;;;
+F9D8;CJK COMPATIBILITY IDEOGRAPH-F9D8;Lo;0;L;5F8B;;;;N;;;;;
+F9D9;CJK COMPATIBILITY IDEOGRAPH-F9D9;Lo;0;L;6144;;;;N;;;;;
+F9DA;CJK COMPATIBILITY IDEOGRAPH-F9DA;Lo;0;L;6817;;;;N;;;;;
+F9DB;CJK COMPATIBILITY IDEOGRAPH-F9DB;Lo;0;L;7387;;;;N;;;;;
+F9DC;CJK COMPATIBILITY IDEOGRAPH-F9DC;Lo;0;L;9686;;;;N;;;;;
+F9DD;CJK COMPATIBILITY IDEOGRAPH-F9DD;Lo;0;L;5229;;;;N;;;;;
+F9DE;CJK COMPATIBILITY IDEOGRAPH-F9DE;Lo;0;L;540F;;;;N;;;;;
+F9DF;CJK COMPATIBILITY IDEOGRAPH-F9DF;Lo;0;L;5C65;;;;N;;;;;
+F9E0;CJK COMPATIBILITY IDEOGRAPH-F9E0;Lo;0;L;6613;;;;N;;;;;
+F9E1;CJK COMPATIBILITY IDEOGRAPH-F9E1;Lo;0;L;674E;;;;N;;;;;
+F9E2;CJK COMPATIBILITY IDEOGRAPH-F9E2;Lo;0;L;68A8;;;;N;;;;;
+F9E3;CJK COMPATIBILITY IDEOGRAPH-F9E3;Lo;0;L;6CE5;;;;N;;;;;
+F9E4;CJK COMPATIBILITY IDEOGRAPH-F9E4;Lo;0;L;7406;;;;N;;;;;
+F9E5;CJK COMPATIBILITY IDEOGRAPH-F9E5;Lo;0;L;75E2;;;;N;;;;;
+F9E6;CJK COMPATIBILITY IDEOGRAPH-F9E6;Lo;0;L;7F79;;;;N;;;;;
+F9E7;CJK COMPATIBILITY IDEOGRAPH-F9E7;Lo;0;L;88CF;;;;N;;;;;
+F9E8;CJK COMPATIBILITY IDEOGRAPH-F9E8;Lo;0;L;88E1;;;;N;;;;;
+F9E9;CJK COMPATIBILITY IDEOGRAPH-F9E9;Lo;0;L;91CC;;;;N;;;;;
+F9EA;CJK COMPATIBILITY IDEOGRAPH-F9EA;Lo;0;L;96E2;;;;N;;;;;
+F9EB;CJK COMPATIBILITY IDEOGRAPH-F9EB;Lo;0;L;533F;;;;N;;;;;
+F9EC;CJK COMPATIBILITY IDEOGRAPH-F9EC;Lo;0;L;6EBA;;;;N;;;;;
+F9ED;CJK COMPATIBILITY IDEOGRAPH-F9ED;Lo;0;L;541D;;;;N;;;;;
+F9EE;CJK COMPATIBILITY IDEOGRAPH-F9EE;Lo;0;L;71D0;;;;N;;;;;
+F9EF;CJK COMPATIBILITY IDEOGRAPH-F9EF;Lo;0;L;7498;;;;N;;;;;
+F9F0;CJK COMPATIBILITY IDEOGRAPH-F9F0;Lo;0;L;85FA;;;;N;;;;;
+F9F1;CJK COMPATIBILITY IDEOGRAPH-F9F1;Lo;0;L;96A3;;;;N;;;;;
+F9F2;CJK COMPATIBILITY IDEOGRAPH-F9F2;Lo;0;L;9C57;;;;N;;;;;
+F9F3;CJK COMPATIBILITY IDEOGRAPH-F9F3;Lo;0;L;9E9F;;;;N;;;;;
+F9F4;CJK COMPATIBILITY IDEOGRAPH-F9F4;Lo;0;L;6797;;;;N;;;;;
+F9F5;CJK COMPATIBILITY IDEOGRAPH-F9F5;Lo;0;L;6DCB;;;;N;;;;;
+F9F6;CJK COMPATIBILITY IDEOGRAPH-F9F6;Lo;0;L;81E8;;;;N;;;;;
+F9F7;CJK COMPATIBILITY IDEOGRAPH-F9F7;Lo;0;L;7ACB;;;;N;;;;;
+F9F8;CJK COMPATIBILITY IDEOGRAPH-F9F8;Lo;0;L;7B20;;;;N;;;;;
+F9F9;CJK COMPATIBILITY IDEOGRAPH-F9F9;Lo;0;L;7C92;;;;N;;;;;
+F9FA;CJK COMPATIBILITY IDEOGRAPH-F9FA;Lo;0;L;72C0;;;;N;;;;;
+F9FB;CJK COMPATIBILITY IDEOGRAPH-F9FB;Lo;0;L;7099;;;;N;;;;;
+F9FC;CJK COMPATIBILITY IDEOGRAPH-F9FC;Lo;0;L;8B58;;;;N;;;;;
+F9FD;CJK COMPATIBILITY IDEOGRAPH-F9FD;Lo;0;L;4EC0;;;;N;;;;;
+F9FE;CJK COMPATIBILITY IDEOGRAPH-F9FE;Lo;0;L;8336;;;;N;;;;;
+F9FF;CJK COMPATIBILITY IDEOGRAPH-F9FF;Lo;0;L;523A;;;;N;;;;;
+FA00;CJK COMPATIBILITY IDEOGRAPH-FA00;Lo;0;L;5207;;;;N;;;;;
+FA01;CJK COMPATIBILITY IDEOGRAPH-FA01;Lo;0;L;5EA6;;;;N;;;;;
+FA02;CJK COMPATIBILITY IDEOGRAPH-FA02;Lo;0;L;62D3;;;;N;;;;;
+FA03;CJK COMPATIBILITY IDEOGRAPH-FA03;Lo;0;L;7CD6;;;;N;;;;;
+FA04;CJK COMPATIBILITY IDEOGRAPH-FA04;Lo;0;L;5B85;;;;N;;;;;
+FA05;CJK COMPATIBILITY IDEOGRAPH-FA05;Lo;0;L;6D1E;;;;N;;;;;
+FA06;CJK COMPATIBILITY IDEOGRAPH-FA06;Lo;0;L;66B4;;;;N;;;;;
+FA07;CJK COMPATIBILITY IDEOGRAPH-FA07;Lo;0;L;8F3B;;;;N;;;;;
+FA08;CJK COMPATIBILITY IDEOGRAPH-FA08;Lo;0;L;884C;;;;N;;;;;
+FA09;CJK COMPATIBILITY IDEOGRAPH-FA09;Lo;0;L;964D;;;;N;;;;;
+FA0A;CJK COMPATIBILITY IDEOGRAPH-FA0A;Lo;0;L;898B;;;;N;;;;;
+FA0B;CJK COMPATIBILITY IDEOGRAPH-FA0B;Lo;0;L;5ED3;;;;N;;;;;
+FA0C;CJK COMPATIBILITY IDEOGRAPH-FA0C;Lo;0;L;5140;;;;N;;;;;
+FA0D;CJK COMPATIBILITY IDEOGRAPH-FA0D;Lo;0;L;55C0;;;;N;;;;;
+FA0E;CJK COMPATIBILITY IDEOGRAPH-FA0E;Lo;0;L;;;;;N;;;;;
+FA0F;CJK COMPATIBILITY IDEOGRAPH-FA0F;Lo;0;L;;;;;N;;;;;
+FA10;CJK COMPATIBILITY IDEOGRAPH-FA10;Lo;0;L;585A;;;;N;;;;;
+FA11;CJK COMPATIBILITY IDEOGRAPH-FA11;Lo;0;L;;;;;N;;;;;
+FA12;CJK COMPATIBILITY IDEOGRAPH-FA12;Lo;0;L;6674;;;;N;;;;;
+FA13;CJK COMPATIBILITY IDEOGRAPH-FA13;Lo;0;L;;;;;N;;;;;
+FA14;CJK COMPATIBILITY IDEOGRAPH-FA14;Lo;0;L;;;;;N;;;;;
+FA15;CJK COMPATIBILITY IDEOGRAPH-FA15;Lo;0;L;51DE;;;;N;;;;;
+FA16;CJK COMPATIBILITY IDEOGRAPH-FA16;Lo;0;L;732A;;;;N;;;;;
+FA17;CJK COMPATIBILITY IDEOGRAPH-FA17;Lo;0;L;76CA;;;;N;;;;;
+FA18;CJK COMPATIBILITY IDEOGRAPH-FA18;Lo;0;L;793C;;;;N;;;;;
+FA19;CJK COMPATIBILITY IDEOGRAPH-FA19;Lo;0;L;795E;;;;N;;;;;
+FA1A;CJK COMPATIBILITY IDEOGRAPH-FA1A;Lo;0;L;7965;;;;N;;;;;
+FA1B;CJK COMPATIBILITY IDEOGRAPH-FA1B;Lo;0;L;798F;;;;N;;;;;
+FA1C;CJK COMPATIBILITY IDEOGRAPH-FA1C;Lo;0;L;9756;;;;N;;;;;
+FA1D;CJK COMPATIBILITY IDEOGRAPH-FA1D;Lo;0;L;7CBE;;;;N;;;;;
+FA1E;CJK COMPATIBILITY IDEOGRAPH-FA1E;Lo;0;L;7FBD;;;;N;;;;;
+FA1F;CJK COMPATIBILITY IDEOGRAPH-FA1F;Lo;0;L;;;;;N;;*;;;
+FA20;CJK COMPATIBILITY IDEOGRAPH-FA20;Lo;0;L;8612;;;;N;;;;;
+FA21;CJK COMPATIBILITY IDEOGRAPH-FA21;Lo;0;L;;;;;N;;;;;
+FA22;CJK COMPATIBILITY IDEOGRAPH-FA22;Lo;0;L;8AF8;;;;N;;;;;
+FA23;CJK COMPATIBILITY IDEOGRAPH-FA23;Lo;0;L;;;;;N;;*;;;
+FA24;CJK COMPATIBILITY IDEOGRAPH-FA24;Lo;0;L;;;;;N;;;;;
+FA25;CJK COMPATIBILITY IDEOGRAPH-FA25;Lo;0;L;9038;;;;N;;;;;
+FA26;CJK COMPATIBILITY IDEOGRAPH-FA26;Lo;0;L;90FD;;;;N;;;;;
+FA27;CJK COMPATIBILITY IDEOGRAPH-FA27;Lo;0;L;;;;;N;;;;;
+FA28;CJK COMPATIBILITY IDEOGRAPH-FA28;Lo;0;L;;;;;N;;;;;
+FA29;CJK COMPATIBILITY IDEOGRAPH-FA29;Lo;0;L;;;;;N;;;;;
+FA2A;CJK COMPATIBILITY IDEOGRAPH-FA2A;Lo;0;L;98EF;;;;N;;;;;
+FA2B;CJK COMPATIBILITY IDEOGRAPH-FA2B;Lo;0;L;98FC;;;;N;;;;;
+FA2C;CJK COMPATIBILITY IDEOGRAPH-FA2C;Lo;0;L;9928;;;;N;;;;;
+FA2D;CJK COMPATIBILITY IDEOGRAPH-FA2D;Lo;0;L;9DB4;;;;N;;;;;
+FB00;LATIN SMALL LIGATURE FF;Ll;0;L;<compat> 0066 0066;;;;N;;;;;
+FB01;LATIN SMALL LIGATURE FI;Ll;0;L;<compat> 0066 0069;;;;N;;;;;
+FB02;LATIN SMALL LIGATURE FL;Ll;0;L;<compat> 0066 006C;;;;N;;;;;
+FB03;LATIN SMALL LIGATURE FFI;Ll;0;L;<compat> 0066 0066 0069;;;;N;;;;;
+FB04;LATIN SMALL LIGATURE FFL;Ll;0;L;<compat> 0066 0066 006C;;;;N;;;;;
+FB05;LATIN SMALL LIGATURE LONG S T;Ll;0;L;<compat> 017F 0074;;;;N;;;;;
+FB06;LATIN SMALL LIGATURE ST;Ll;0;L;<compat> 0073 0074;;;;N;;;;;
+FB13;ARMENIAN SMALL LIGATURE MEN NOW;Ll;0;L;<compat> 0574 0576;;;;N;;;;;
+FB14;ARMENIAN SMALL LIGATURE MEN ECH;Ll;0;L;<compat> 0574 0565;;;;N;;;;;
+FB15;ARMENIAN SMALL LIGATURE MEN INI;Ll;0;L;<compat> 0574 056B;;;;N;;;;;
+FB16;ARMENIAN SMALL LIGATURE VEW NOW;Ll;0;L;<compat> 057E 0576;;;;N;;;;;
+FB17;ARMENIAN SMALL LIGATURE MEN XEH;Ll;0;L;<compat> 0574 056D;;;;N;;;;;
+FB1D;HEBREW LETTER YOD WITH HIRIQ;Lo;0;R;05D9 05B4;;;;N;;;;;
+FB1E;HEBREW POINT JUDEO-SPANISH VARIKA;Mn;26;NSM;;;;;N;HEBREW POINT VARIKA;;;;
+FB1F;HEBREW LIGATURE YIDDISH YOD YOD PATAH;Lo;0;R;05F2 05B7;;;;N;;;;;
+FB20;HEBREW LETTER ALTERNATIVE AYIN;Lo;0;R;<font> 05E2;;;;N;;;;;
+FB21;HEBREW LETTER WIDE ALEF;Lo;0;R;<font> 05D0;;;;N;;;;;
+FB22;HEBREW LETTER WIDE DALET;Lo;0;R;<font> 05D3;;;;N;;;;;
+FB23;HEBREW LETTER WIDE HE;Lo;0;R;<font> 05D4;;;;N;;;;;
+FB24;HEBREW LETTER WIDE KAF;Lo;0;R;<font> 05DB;;;;N;;;;;
+FB25;HEBREW LETTER WIDE LAMED;Lo;0;R;<font> 05DC;;;;N;;;;;
+FB26;HEBREW LETTER WIDE FINAL MEM;Lo;0;R;<font> 05DD;;;;N;;;;;
+FB27;HEBREW LETTER WIDE RESH;Lo;0;R;<font> 05E8;;;;N;;;;;
+FB28;HEBREW LETTER WIDE TAV;Lo;0;R;<font> 05EA;;;;N;;;;;
+FB29;HEBREW LETTER ALTERNATIVE PLUS SIGN;Sm;0;ET;<font> 002B;;;;N;;;;;
+FB2A;HEBREW LETTER SHIN WITH SHIN DOT;Lo;0;R;05E9 05C1;;;;N;;;;;
+FB2B;HEBREW LETTER SHIN WITH SIN DOT;Lo;0;R;05E9 05C2;;;;N;;;;;
+FB2C;HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT;Lo;0;R;FB49 05C1;;;;N;;;;;
+FB2D;HEBREW LETTER SHIN WITH DAGESH AND SIN DOT;Lo;0;R;FB49 05C2;;;;N;;;;;
+FB2E;HEBREW LETTER ALEF WITH PATAH;Lo;0;R;05D0 05B7;;;;N;;;;;
+FB2F;HEBREW LETTER ALEF WITH QAMATS;Lo;0;R;05D0 05B8;;;;N;;;;;
+FB30;HEBREW LETTER ALEF WITH MAPIQ;Lo;0;R;05D0 05BC;;;;N;;;;;
+FB31;HEBREW LETTER BET WITH DAGESH;Lo;0;R;05D1 05BC;;;;N;;;;;
+FB32;HEBREW LETTER GIMEL WITH DAGESH;Lo;0;R;05D2 05BC;;;;N;;;;;
+FB33;HEBREW LETTER DALET WITH DAGESH;Lo;0;R;05D3 05BC;;;;N;;;;;
+FB34;HEBREW LETTER HE WITH MAPIQ;Lo;0;R;05D4 05BC;;;;N;;;;;
+FB35;HEBREW LETTER VAV WITH DAGESH;Lo;0;R;05D5 05BC;;;;N;;;;;
+FB36;HEBREW LETTER ZAYIN WITH DAGESH;Lo;0;R;05D6 05BC;;;;N;;;;;
+FB38;HEBREW LETTER TET WITH DAGESH;Lo;0;R;05D8 05BC;;;;N;;;;;
+FB39;HEBREW LETTER YOD WITH DAGESH;Lo;0;R;05D9 05BC;;;;N;;;;;
+FB3A;HEBREW LETTER FINAL KAF WITH DAGESH;Lo;0;R;05DA 05BC;;;;N;;;;;
+FB3B;HEBREW LETTER KAF WITH DAGESH;Lo;0;R;05DB 05BC;;;;N;;;;;
+FB3C;HEBREW LETTER LAMED WITH DAGESH;Lo;0;R;05DC 05BC;;;;N;;;;;
+FB3E;HEBREW LETTER MEM WITH DAGESH;Lo;0;R;05DE 05BC;;;;N;;;;;
+FB40;HEBREW LETTER NUN WITH DAGESH;Lo;0;R;05E0 05BC;;;;N;;;;;
+FB41;HEBREW LETTER SAMEKH WITH DAGESH;Lo;0;R;05E1 05BC;;;;N;;;;;
+FB43;HEBREW LETTER FINAL PE WITH DAGESH;Lo;0;R;05E3 05BC;;;;N;;;;;
+FB44;HEBREW LETTER PE WITH DAGESH;Lo;0;R;05E4 05BC;;;;N;;;;;
+FB46;HEBREW LETTER TSADI WITH DAGESH;Lo;0;R;05E6 05BC;;;;N;;;;;
+FB47;HEBREW LETTER QOF WITH DAGESH;Lo;0;R;05E7 05BC;;;;N;;;;;
+FB48;HEBREW LETTER RESH WITH DAGESH;Lo;0;R;05E8 05BC;;;;N;;;;;
+FB49;HEBREW LETTER SHIN WITH DAGESH;Lo;0;R;05E9 05BC;;;;N;;;;;
+FB4A;HEBREW LETTER TAV WITH DAGESH;Lo;0;R;05EA 05BC;;;;N;;;;;
+FB4B;HEBREW LETTER VAV WITH HOLAM;Lo;0;R;05D5 05B9;;;;N;;;;;
+FB4C;HEBREW LETTER BET WITH RAFE;Lo;0;R;05D1 05BF;;;;N;;;;;
+FB4D;HEBREW LETTER KAF WITH RAFE;Lo;0;R;05DB 05BF;;;;N;;;;;
+FB4E;HEBREW LETTER PE WITH RAFE;Lo;0;R;05E4 05BF;;;;N;;;;;
+FB4F;HEBREW LIGATURE ALEF LAMED;Lo;0;R;<compat> 05D0 05DC;;;;N;;;;;
+FB50;ARABIC LETTER ALEF WASLA ISOLATED FORM;Lo;0;AL;<isolated> 0671;;;;N;;;;;
+FB51;ARABIC LETTER ALEF WASLA FINAL FORM;Lo;0;AL;<final> 0671;;;;N;;;;;
+FB52;ARABIC LETTER BEEH ISOLATED FORM;Lo;0;AL;<isolated> 067B;;;;N;;;;;
+FB53;ARABIC LETTER BEEH FINAL FORM;Lo;0;AL;<final> 067B;;;;N;;;;;
+FB54;ARABIC LETTER BEEH INITIAL FORM;Lo;0;AL;<initial> 067B;;;;N;;;;;
+FB55;ARABIC LETTER BEEH MEDIAL FORM;Lo;0;AL;<medial> 067B;;;;N;;;;;
+FB56;ARABIC LETTER PEH ISOLATED FORM;Lo;0;AL;<isolated> 067E;;;;N;;;;;
+FB57;ARABIC LETTER PEH FINAL FORM;Lo;0;AL;<final> 067E;;;;N;;;;;
+FB58;ARABIC LETTER PEH INITIAL FORM;Lo;0;AL;<initial> 067E;;;;N;;;;;
+FB59;ARABIC LETTER PEH MEDIAL FORM;Lo;0;AL;<medial> 067E;;;;N;;;;;
+FB5A;ARABIC LETTER BEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0680;;;;N;;;;;
+FB5B;ARABIC LETTER BEHEH FINAL FORM;Lo;0;AL;<final> 0680;;;;N;;;;;
+FB5C;ARABIC LETTER BEHEH INITIAL FORM;Lo;0;AL;<initial> 0680;;;;N;;;;;
+FB5D;ARABIC LETTER BEHEH MEDIAL FORM;Lo;0;AL;<medial> 0680;;;;N;;;;;
+FB5E;ARABIC LETTER TTEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067A;;;;N;;;;;
+FB5F;ARABIC LETTER TTEHEH FINAL FORM;Lo;0;AL;<final> 067A;;;;N;;;;;
+FB60;ARABIC LETTER TTEHEH INITIAL FORM;Lo;0;AL;<initial> 067A;;;;N;;;;;
+FB61;ARABIC LETTER TTEHEH MEDIAL FORM;Lo;0;AL;<medial> 067A;;;;N;;;;;
+FB62;ARABIC LETTER TEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067F;;;;N;;;;;
+FB63;ARABIC LETTER TEHEH FINAL FORM;Lo;0;AL;<final> 067F;;;;N;;;;;
+FB64;ARABIC LETTER TEHEH INITIAL FORM;Lo;0;AL;<initial> 067F;;;;N;;;;;
+FB65;ARABIC LETTER TEHEH MEDIAL FORM;Lo;0;AL;<medial> 067F;;;;N;;;;;
+FB66;ARABIC LETTER TTEH ISOLATED FORM;Lo;0;AL;<isolated> 0679;;;;N;;;;;
+FB67;ARABIC LETTER TTEH FINAL FORM;Lo;0;AL;<final> 0679;;;;N;;;;;
+FB68;ARABIC LETTER TTEH INITIAL FORM;Lo;0;AL;<initial> 0679;;;;N;;;;;
+FB69;ARABIC LETTER TTEH MEDIAL FORM;Lo;0;AL;<medial> 0679;;;;N;;;;;
+FB6A;ARABIC LETTER VEH ISOLATED FORM;Lo;0;AL;<isolated> 06A4;;;;N;;;;;
+FB6B;ARABIC LETTER VEH FINAL FORM;Lo;0;AL;<final> 06A4;;;;N;;;;;
+FB6C;ARABIC LETTER VEH INITIAL FORM;Lo;0;AL;<initial> 06A4;;;;N;;;;;
+FB6D;ARABIC LETTER VEH MEDIAL FORM;Lo;0;AL;<medial> 06A4;;;;N;;;;;
+FB6E;ARABIC LETTER PEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A6;;;;N;;;;;
+FB6F;ARABIC LETTER PEHEH FINAL FORM;Lo;0;AL;<final> 06A6;;;;N;;;;;
+FB70;ARABIC LETTER PEHEH INITIAL FORM;Lo;0;AL;<initial> 06A6;;;;N;;;;;
+FB71;ARABIC LETTER PEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A6;;;;N;;;;;
+FB72;ARABIC LETTER DYEH ISOLATED FORM;Lo;0;AL;<isolated> 0684;;;;N;;;;;
+FB73;ARABIC LETTER DYEH FINAL FORM;Lo;0;AL;<final> 0684;;;;N;;;;;
+FB74;ARABIC LETTER DYEH INITIAL FORM;Lo;0;AL;<initial> 0684;;;;N;;;;;
+FB75;ARABIC LETTER DYEH MEDIAL FORM;Lo;0;AL;<medial> 0684;;;;N;;;;;
+FB76;ARABIC LETTER NYEH ISOLATED FORM;Lo;0;AL;<isolated> 0683;;;;N;;;;;
+FB77;ARABIC LETTER NYEH FINAL FORM;Lo;0;AL;<final> 0683;;;;N;;;;;
+FB78;ARABIC LETTER NYEH INITIAL FORM;Lo;0;AL;<initial> 0683;;;;N;;;;;
+FB79;ARABIC LETTER NYEH MEDIAL FORM;Lo;0;AL;<medial> 0683;;;;N;;;;;
+FB7A;ARABIC LETTER TCHEH ISOLATED FORM;Lo;0;AL;<isolated> 0686;;;;N;;;;;
+FB7B;ARABIC LETTER TCHEH FINAL FORM;Lo;0;AL;<final> 0686;;;;N;;;;;
+FB7C;ARABIC LETTER TCHEH INITIAL FORM;Lo;0;AL;<initial> 0686;;;;N;;;;;
+FB7D;ARABIC LETTER TCHEH MEDIAL FORM;Lo;0;AL;<medial> 0686;;;;N;;;;;
+FB7E;ARABIC LETTER TCHEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0687;;;;N;;;;;
+FB7F;ARABIC LETTER TCHEHEH FINAL FORM;Lo;0;AL;<final> 0687;;;;N;;;;;
+FB80;ARABIC LETTER TCHEHEH INITIAL FORM;Lo;0;AL;<initial> 0687;;;;N;;;;;
+FB81;ARABIC LETTER TCHEHEH MEDIAL FORM;Lo;0;AL;<medial> 0687;;;;N;;;;;
+FB82;ARABIC LETTER DDAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068D;;;;N;;;;;
+FB83;ARABIC LETTER DDAHAL FINAL FORM;Lo;0;AL;<final> 068D;;;;N;;;;;
+FB84;ARABIC LETTER DAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068C;;;;N;;;;;
+FB85;ARABIC LETTER DAHAL FINAL FORM;Lo;0;AL;<final> 068C;;;;N;;;;;
+FB86;ARABIC LETTER DUL ISOLATED FORM;Lo;0;AL;<isolated> 068E;;;;N;;;;;
+FB87;ARABIC LETTER DUL FINAL FORM;Lo;0;AL;<final> 068E;;;;N;;;;;
+FB88;ARABIC LETTER DDAL ISOLATED FORM;Lo;0;AL;<isolated> 0688;;;;N;;;;;
+FB89;ARABIC LETTER DDAL FINAL FORM;Lo;0;AL;<final> 0688;;;;N;;;;;
+FB8A;ARABIC LETTER JEH ISOLATED FORM;Lo;0;AL;<isolated> 0698;;;;N;;;;;
+FB8B;ARABIC LETTER JEH FINAL FORM;Lo;0;AL;<final> 0698;;;;N;;;;;
+FB8C;ARABIC LETTER RREH ISOLATED FORM;Lo;0;AL;<isolated> 0691;;;;N;;;;;
+FB8D;ARABIC LETTER RREH FINAL FORM;Lo;0;AL;<final> 0691;;;;N;;;;;
+FB8E;ARABIC LETTER KEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A9;;;;N;;;;;
+FB8F;ARABIC LETTER KEHEH FINAL FORM;Lo;0;AL;<final> 06A9;;;;N;;;;;
+FB90;ARABIC LETTER KEHEH INITIAL FORM;Lo;0;AL;<initial> 06A9;;;;N;;;;;
+FB91;ARABIC LETTER KEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A9;;;;N;;;;;
+FB92;ARABIC LETTER GAF ISOLATED FORM;Lo;0;AL;<isolated> 06AF;;;;N;;;;;
+FB93;ARABIC LETTER GAF FINAL FORM;Lo;0;AL;<final> 06AF;;;;N;;;;;
+FB94;ARABIC LETTER GAF INITIAL FORM;Lo;0;AL;<initial> 06AF;;;;N;;;;;
+FB95;ARABIC LETTER GAF MEDIAL FORM;Lo;0;AL;<medial> 06AF;;;;N;;;;;
+FB96;ARABIC LETTER GUEH ISOLATED FORM;Lo;0;AL;<isolated> 06B3;;;;N;;;;;
+FB97;ARABIC LETTER GUEH FINAL FORM;Lo;0;AL;<final> 06B3;;;;N;;;;;
+FB98;ARABIC LETTER GUEH INITIAL FORM;Lo;0;AL;<initial> 06B3;;;;N;;;;;
+FB99;ARABIC LETTER GUEH MEDIAL FORM;Lo;0;AL;<medial> 06B3;;;;N;;;;;
+FB9A;ARABIC LETTER NGOEH ISOLATED FORM;Lo;0;AL;<isolated> 06B1;;;;N;;;;;
+FB9B;ARABIC LETTER NGOEH FINAL FORM;Lo;0;AL;<final> 06B1;;;;N;;;;;
+FB9C;ARABIC LETTER NGOEH INITIAL FORM;Lo;0;AL;<initial> 06B1;;;;N;;;;;
+FB9D;ARABIC LETTER NGOEH MEDIAL FORM;Lo;0;AL;<medial> 06B1;;;;N;;;;;
+FB9E;ARABIC LETTER NOON GHUNNA ISOLATED FORM;Lo;0;AL;<isolated> 06BA;;;;N;;;;;
+FB9F;ARABIC LETTER NOON GHUNNA FINAL FORM;Lo;0;AL;<final> 06BA;;;;N;;;;;
+FBA0;ARABIC LETTER RNOON ISOLATED FORM;Lo;0;AL;<isolated> 06BB;;;;N;;;;;
+FBA1;ARABIC LETTER RNOON FINAL FORM;Lo;0;AL;<final> 06BB;;;;N;;;;;
+FBA2;ARABIC LETTER RNOON INITIAL FORM;Lo;0;AL;<initial> 06BB;;;;N;;;;;
+FBA3;ARABIC LETTER RNOON MEDIAL FORM;Lo;0;AL;<medial> 06BB;;;;N;;;;;
+FBA4;ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06C0;;;;N;;;;;
+FBA5;ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM;Lo;0;AL;<final> 06C0;;;;N;;;;;
+FBA6;ARABIC LETTER HEH GOAL ISOLATED FORM;Lo;0;AL;<isolated> 06C1;;;;N;;;;;
+FBA7;ARABIC LETTER HEH GOAL FINAL FORM;Lo;0;AL;<final> 06C1;;;;N;;;;;
+FBA8;ARABIC LETTER HEH GOAL INITIAL FORM;Lo;0;AL;<initial> 06C1;;;;N;;;;;
+FBA9;ARABIC LETTER HEH GOAL MEDIAL FORM;Lo;0;AL;<medial> 06C1;;;;N;;;;;
+FBAA;ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM;Lo;0;AL;<isolated> 06BE;;;;N;;;;;
+FBAB;ARABIC LETTER HEH DOACHASHMEE FINAL FORM;Lo;0;AL;<final> 06BE;;;;N;;;;;
+FBAC;ARABIC LETTER HEH DOACHASHMEE INITIAL FORM;Lo;0;AL;<initial> 06BE;;;;N;;;;;
+FBAD;ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM;Lo;0;AL;<medial> 06BE;;;;N;;;;;
+FBAE;ARABIC LETTER YEH BARREE ISOLATED FORM;Lo;0;AL;<isolated> 06D2;;;;N;;;;;
+FBAF;ARABIC LETTER YEH BARREE FINAL FORM;Lo;0;AL;<final> 06D2;;;;N;;;;;
+FBB0;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06D3;;;;N;;;;;
+FBB1;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 06D3;;;;N;;;;;
+FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL;<isolated> 06AD;;;;N;;;;;
+FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL;<final> 06AD;;;;N;;;;;
+FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL;<initial> 06AD;;;;N;;;;;
+FBD6;ARABIC LETTER NG MEDIAL FORM;Lo;0;AL;<medial> 06AD;;;;N;;;;;
+FBD7;ARABIC LETTER U ISOLATED FORM;Lo;0;AL;<isolated> 06C7;;;;N;;;;;
+FBD8;ARABIC LETTER U FINAL FORM;Lo;0;AL;<final> 06C7;;;;N;;;;;
+FBD9;ARABIC LETTER OE ISOLATED FORM;Lo;0;AL;<isolated> 06C6;;;;N;;;;;
+FBDA;ARABIC LETTER OE FINAL FORM;Lo;0;AL;<final> 06C6;;;;N;;;;;
+FBDB;ARABIC LETTER YU ISOLATED FORM;Lo;0;AL;<isolated> 06C8;;;;N;;;;;
+FBDC;ARABIC LETTER YU FINAL FORM;Lo;0;AL;<final> 06C8;;;;N;;;;;
+FBDD;ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0677;;;;N;;;;;
+FBDE;ARABIC LETTER VE ISOLATED FORM;Lo;0;AL;<isolated> 06CB;;;;N;;;;;
+FBDF;ARABIC LETTER VE FINAL FORM;Lo;0;AL;<final> 06CB;;;;N;;;;;
+FBE0;ARABIC LETTER KIRGHIZ OE ISOLATED FORM;Lo;0;AL;<isolated> 06C5;;;;N;;;;;
+FBE1;ARABIC LETTER KIRGHIZ OE FINAL FORM;Lo;0;AL;<final> 06C5;;;;N;;;;;
+FBE2;ARABIC LETTER KIRGHIZ YU ISOLATED FORM;Lo;0;AL;<isolated> 06C9;;;;N;;;;;
+FBE3;ARABIC LETTER KIRGHIZ YU FINAL FORM;Lo;0;AL;<final> 06C9;;;;N;;;;;
+FBE4;ARABIC LETTER E ISOLATED FORM;Lo;0;AL;<isolated> 06D0;;;;N;;;;;
+FBE5;ARABIC LETTER E FINAL FORM;Lo;0;AL;<final> 06D0;;;;N;;;;;
+FBE6;ARABIC LETTER E INITIAL FORM;Lo;0;AL;<initial> 06D0;;;;N;;;;;
+FBE7;ARABIC LETTER E MEDIAL FORM;Lo;0;AL;<medial> 06D0;;;;N;;;;;
+FBE8;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0649;;;;N;;;;;
+FBE9;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM;Lo;0;AL;<medial> 0649;;;;N;;;;;
+FBEA;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0626 0627;;;;N;;;;;
+FBEB;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM;Lo;0;AL;<final> 0626 0627;;;;N;;;;;
+FBEC;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D5;;;;N;;;;;
+FBED;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM;Lo;0;AL;<final> 0626 06D5;;;;N;;;;;
+FBEE;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM;Lo;0;AL;<isolated> 0626 0648;;;;N;;;;;
+FBEF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM;Lo;0;AL;<final> 0626 0648;;;;N;;;;;
+FBF0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C7;;;;N;;;;;
+FBF1;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM;Lo;0;AL;<final> 0626 06C7;;;;N;;;;;
+FBF2;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C6;;;;N;;;;;
+FBF3;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM;Lo;0;AL;<final> 0626 06C6;;;;N;;;;;
+FBF4;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C8;;;;N;;;;;
+FBF5;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM;Lo;0;AL;<final> 0626 06C8;;;;N;;;;;
+FBF6;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D0;;;;N;;;;;
+FBF7;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM;Lo;0;AL;<final> 0626 06D0;;;;N;;;;;
+FBF8;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM;Lo;0;AL;<initial> 0626 06D0;;;;N;;;;;
+FBF9;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;;
+FBFA;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;;
+FBFB;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0626 0649;;;;N;;;;;
+FBFC;ARABIC LETTER FARSI YEH ISOLATED FORM;Lo;0;AL;<isolated> 06CC;;;;N;;;;;
+FBFD;ARABIC LETTER FARSI YEH FINAL FORM;Lo;0;AL;<final> 06CC;;;;N;;;;;
+FBFE;ARABIC LETTER FARSI YEH INITIAL FORM;Lo;0;AL;<initial> 06CC;;;;N;;;;;
+FBFF;ARABIC LETTER FARSI YEH MEDIAL FORM;Lo;0;AL;<medial> 06CC;;;;N;;;;;
+FC00;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 062C;;;;N;;;;;
+FC01;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0626 062D;;;;N;;;;;
+FC02;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 0645;;;;N;;;;;
+FC03;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;;
+FC04;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0626 064A;;;;N;;;;;
+FC05;ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 062C;;;;N;;;;;
+FC06;ARABIC LIGATURE BEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062D;;;;N;;;;;
+FC07;ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062E;;;;N;;;;;
+FC08;ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 0645;;;;N;;;;;
+FC09;ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0628 0649;;;;N;;;;;
+FC0A;ARABIC LIGATURE BEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0628 064A;;;;N;;;;;
+FC0B;ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 062C;;;;N;;;;;
+FC0C;ARABIC LIGATURE TEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062D;;;;N;;;;;
+FC0D;ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062E;;;;N;;;;;
+FC0E;ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 0645;;;;N;;;;;
+FC0F;ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062A 0649;;;;N;;;;;
+FC10;ARABIC LIGATURE TEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062A 064A;;;;N;;;;;
+FC11;ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 062C;;;;N;;;;;
+FC12;ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 0645;;;;N;;;;;
+FC13;ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062B 0649;;;;N;;;;;
+FC14;ARABIC LIGATURE THEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062B 064A;;;;N;;;;;
+FC15;ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062C 062D;;;;N;;;;;
+FC16;ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C 0645;;;;N;;;;;
+FC17;ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 062C;;;;N;;;;;
+FC18;ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 0645;;;;N;;;;;
+FC19;ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 062C;;;;N;;;;;
+FC1A;ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062E 062D;;;;N;;;;;
+FC1B;ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 0645;;;;N;;;;;
+FC1C;ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 062C;;;;N;;;;;
+FC1D;ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062D;;;;N;;;;;
+FC1E;ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062E;;;;N;;;;;
+FC1F;ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 0645;;;;N;;;;;
+FC20;ARABIC LIGATURE SAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0635 062D;;;;N;;;;;
+FC21;ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0645;;;;N;;;;;
+FC22;ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 062C;;;;N;;;;;
+FC23;ARABIC LIGATURE DAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062D;;;;N;;;;;
+FC24;ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062E;;;;N;;;;;
+FC25;ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 0645;;;;N;;;;;
+FC26;ARABIC LIGATURE TAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0637 062D;;;;N;;;;;
+FC27;ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0637 0645;;;;N;;;;;
+FC28;ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0638 0645;;;;N;;;;;
+FC29;ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 062C;;;;N;;;;;
+FC2A;ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 0645;;;;N;;;;;
+FC2B;ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 062C;;;;N;;;;;
+FC2C;ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 0645;;;;N;;;;;
+FC2D;ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 062C;;;;N;;;;;
+FC2E;ARABIC LIGATURE FEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062D;;;;N;;;;;
+FC2F;ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062E;;;;N;;;;;
+FC30;ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 0645;;;;N;;;;;
+FC31;ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0641 0649;;;;N;;;;;
+FC32;ARABIC LIGATURE FEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0641 064A;;;;N;;;;;
+FC33;ARABIC LIGATURE QAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0642 062D;;;;N;;;;;
+FC34;ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0642 0645;;;;N;;;;;
+FC35;ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0642 0649;;;;N;;;;;
+FC36;ARABIC LIGATURE QAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0642 064A;;;;N;;;;;
+FC37;ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0643 0627;;;;N;;;;;
+FC38;ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 062C;;;;N;;;;;
+FC39;ARABIC LIGATURE KAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062D;;;;N;;;;;
+FC3A;ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062E;;;;N;;;;;
+FC3B;ARABIC LIGATURE KAF WITH LAM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0644;;;;N;;;;;
+FC3C;ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0645;;;;N;;;;;
+FC3D;ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0643 0649;;;;N;;;;;
+FC3E;ARABIC LIGATURE KAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0643 064A;;;;N;;;;;
+FC3F;ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 062C;;;;N;;;;;
+FC40;ARABIC LIGATURE LAM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062D;;;;N;;;;;
+FC41;ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062E;;;;N;;;;;
+FC42;ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 0645;;;;N;;;;;
+FC43;ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0644 0649;;;;N;;;;;
+FC44;ARABIC LIGATURE LAM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0644 064A;;;;N;;;;;
+FC45;ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 062C;;;;N;;;;;
+FC46;ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D;;;;N;;;;;
+FC47;ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062E;;;;N;;;;;
+FC48;ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 0645;;;;N;;;;;
+FC49;ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0645 0649;;;;N;;;;;
+FC4A;ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0645 064A;;;;N;;;;;
+FC4B;ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 062C;;;;N;;;;;
+FC4C;ARABIC LIGATURE NOON WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062D;;;;N;;;;;
+FC4D;ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062E;;;;N;;;;;
+FC4E;ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 0645;;;;N;;;;;
+FC4F;ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0646 0649;;;;N;;;;;
+FC50;ARABIC LIGATURE NOON WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0646 064A;;;;N;;;;;
+FC51;ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 062C;;;;N;;;;;
+FC52;ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 0645;;;;N;;;;;
+FC53;ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0647 0649;;;;N;;;;;
+FC54;ARABIC LIGATURE HEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0647 064A;;;;N;;;;;
+FC55;ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 062C;;;;N;;;;;
+FC56;ARABIC LIGATURE YEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062D;;;;N;;;;;
+FC57;ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062E;;;;N;;;;;
+FC58;ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 0645;;;;N;;;;;
+FC59;ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 064A 0649;;;;N;;;;;
+FC5A;ARABIC LIGATURE YEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A 064A;;;;N;;;;;
+FC5B;ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0630 0670;;;;N;;;;;
+FC5C;ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0631 0670;;;;N;;;;;
+FC5D;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0649 0670;;;;N;;;;;
+FC5E;ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C 0651;;;;N;;;;;
+FC5F;ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D 0651;;;;N;;;;;
+FC60;ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E 0651;;;;N;;;;;
+FC61;ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F 0651;;;;N;;;;;
+FC62;ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650 0651;;;;N;;;;;
+FC63;ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651 0670;;;;N;;;;;
+FC64;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM;Lo;0;AL;<final> 0626 0631;;;;N;;;;;
+FC65;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0626 0632;;;;N;;;;;
+FC66;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM;Lo;0;AL;<final> 0626 0645;;;;N;;;;;
+FC67;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM;Lo;0;AL;<final> 0626 0646;;;;N;;;;;
+FC68;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;;
+FC69;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM;Lo;0;AL;<final> 0626 064A;;;;N;;;;;
+FC6A;ARABIC LIGATURE BEH WITH REH FINAL FORM;Lo;0;AL;<final> 0628 0631;;;;N;;;;;
+FC6B;ARABIC LIGATURE BEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0628 0632;;;;N;;;;;
+FC6C;ARABIC LIGATURE BEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0628 0645;;;;N;;;;;
+FC6D;ARABIC LIGATURE BEH WITH NOON FINAL FORM;Lo;0;AL;<final> 0628 0646;;;;N;;;;;
+FC6E;ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0628 0649;;;;N;;;;;
+FC6F;ARABIC LIGATURE BEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 064A;;;;N;;;;;
+FC70;ARABIC LIGATURE TEH WITH REH FINAL FORM;Lo;0;AL;<final> 062A 0631;;;;N;;;;;
+FC71;ARABIC LIGATURE TEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062A 0632;;;;N;;;;;
+FC72;ARABIC LIGATURE TEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062A 0645;;;;N;;;;;
+FC73;ARABIC LIGATURE TEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062A 0646;;;;N;;;;;
+FC74;ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0649;;;;N;;;;;
+FC75;ARABIC LIGATURE TEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 064A;;;;N;;;;;
+FC76;ARABIC LIGATURE THEH WITH REH FINAL FORM;Lo;0;AL;<final> 062B 0631;;;;N;;;;;
+FC77;ARABIC LIGATURE THEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062B 0632;;;;N;;;;;
+FC78;ARABIC LIGATURE THEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062B 0645;;;;N;;;;;
+FC79;ARABIC LIGATURE THEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062B 0646;;;;N;;;;;
+FC7A;ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062B 0649;;;;N;;;;;
+FC7B;ARABIC LIGATURE THEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062B 064A;;;;N;;;;;
+FC7C;ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0641 0649;;;;N;;;;;
+FC7D;ARABIC LIGATURE FEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 064A;;;;N;;;;;
+FC7E;ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0642 0649;;;;N;;;;;
+FC7F;ARABIC LIGATURE QAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 064A;;;;N;;;;;
+FC80;ARABIC LIGATURE KAF WITH ALEF FINAL FORM;Lo;0;AL;<final> 0643 0627;;;;N;;;;;
+FC81;ARABIC LIGATURE KAF WITH LAM FINAL FORM;Lo;0;AL;<final> 0643 0644;;;;N;;;;;
+FC82;ARABIC LIGATURE KAF WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645;;;;N;;;;;
+FC83;ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0643 0649;;;;N;;;;;
+FC84;ARABIC LIGATURE KAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 064A;;;;N;;;;;
+FC85;ARABIC LIGATURE LAM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 0645;;;;N;;;;;
+FC86;ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 0649;;;;N;;;;;
+FC87;ARABIC LIGATURE LAM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 064A;;;;N;;;;;
+FC88;ARABIC LIGATURE MEEM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0645 0627;;;;N;;;;;
+FC89;ARABIC LIGATURE MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0645 0645;;;;N;;;;;
+FC8A;ARABIC LIGATURE NOON WITH REH FINAL FORM;Lo;0;AL;<final> 0646 0631;;;;N;;;;;
+FC8B;ARABIC LIGATURE NOON WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0646 0632;;;;N;;;;;
+FC8C;ARABIC LIGATURE NOON WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 0645;;;;N;;;;;
+FC8D;ARABIC LIGATURE NOON WITH NOON FINAL FORM;Lo;0;AL;<final> 0646 0646;;;;N;;;;;
+FC8E;ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0649;;;;N;;;;;
+FC8F;ARABIC LIGATURE NOON WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 064A;;;;N;;;;;
+FC90;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM;Lo;0;AL;<final> 0649 0670;;;;N;;;;;
+FC91;ARABIC LIGATURE YEH WITH REH FINAL FORM;Lo;0;AL;<final> 064A 0631;;;;N;;;;;
+FC92;ARABIC LIGATURE YEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 064A 0632;;;;N;;;;;
+FC93;ARABIC LIGATURE YEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645;;;;N;;;;;
+FC94;ARABIC LIGATURE YEH WITH NOON FINAL FORM;Lo;0;AL;<final> 064A 0646;;;;N;;;;;
+FC95;ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 064A 0649;;;;N;;;;;
+FC96;ARABIC LIGATURE YEH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 064A;;;;N;;;;;
+FC97;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0626 062C;;;;N;;;;;
+FC98;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0626 062D;;;;N;;;;;
+FC99;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0626 062E;;;;N;;;;;
+FC9A;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0626 0645;;;;N;;;;;
+FC9B;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0626 0647;;;;N;;;;;
+FC9C;ARABIC LIGATURE BEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0628 062C;;;;N;;;;;
+FC9D;ARABIC LIGATURE BEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0628 062D;;;;N;;;;;
+FC9E;ARABIC LIGATURE BEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0628 062E;;;;N;;;;;
+FC9F;ARABIC LIGATURE BEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0628 0645;;;;N;;;;;
+FCA0;ARABIC LIGATURE BEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0628 0647;;;;N;;;;;
+FCA1;ARABIC LIGATURE TEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C;;;;N;;;;;
+FCA2;ARABIC LIGATURE TEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 062D;;;;N;;;;;
+FCA3;ARABIC LIGATURE TEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 062E;;;;N;;;;;
+FCA4;ARABIC LIGATURE TEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645;;;;N;;;;;
+FCA5;ARABIC LIGATURE TEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 062A 0647;;;;N;;;;;
+FCA6;ARABIC LIGATURE THEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062B 0645;;;;N;;;;;
+FCA7;ARABIC LIGATURE JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 062D;;;;N;;;;;
+FCA8;ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062C 0645;;;;N;;;;;
+FCA9;ARABIC LIGATURE HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062D 062C;;;;N;;;;;
+FCAA;ARABIC LIGATURE HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062D 0645;;;;N;;;;;
+FCAB;ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062E 062C;;;;N;;;;;
+FCAC;ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062E 0645;;;;N;;;;;
+FCAD;ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062C;;;;N;;;;;
+FCAE;ARABIC LIGATURE SEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062D;;;;N;;;;;
+FCAF;ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0633 062E;;;;N;;;;;
+FCB0;ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645;;;;N;;;;;
+FCB1;ARABIC LIGATURE SAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D;;;;N;;;;;
+FCB2;ARABIC LIGATURE SAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0635 062E;;;;N;;;;;
+FCB3;ARABIC LIGATURE SAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645;;;;N;;;;;
+FCB4;ARABIC LIGATURE DAD WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062C;;;;N;;;;;
+FCB5;ARABIC LIGATURE DAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0636 062D;;;;N;;;;;
+FCB6;ARABIC LIGATURE DAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0636 062E;;;;N;;;;;
+FCB7;ARABIC LIGATURE DAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 0645;;;;N;;;;;
+FCB8;ARABIC LIGATURE TAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 062D;;;;N;;;;;
+FCB9;ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0638 0645;;;;N;;;;;
+FCBA;ARABIC LIGATURE AIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C;;;;N;;;;;
+FCBB;ARABIC LIGATURE AIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645;;;;N;;;;;
+FCBC;ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 063A 062C;;;;N;;;;;
+FCBD;ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 063A 0645;;;;N;;;;;
+FCBE;ARABIC LIGATURE FEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062C;;;;N;;;;;
+FCBF;ARABIC LIGATURE FEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0641 062D;;;;N;;;;;
+FCC0;ARABIC LIGATURE FEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0641 062E;;;;N;;;;;
+FCC1;ARABIC LIGATURE FEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 0645;;;;N;;;;;
+FCC2;ARABIC LIGATURE QAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 062D;;;;N;;;;;
+FCC3;ARABIC LIGATURE QAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0642 0645;;;;N;;;;;
+FCC4;ARABIC LIGATURE KAF WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0643 062C;;;;N;;;;;
+FCC5;ARABIC LIGATURE KAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0643 062D;;;;N;;;;;
+FCC6;ARABIC LIGATURE KAF WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0643 062E;;;;N;;;;;
+FCC7;ARABIC LIGATURE KAF WITH LAM INITIAL FORM;Lo;0;AL;<initial> 0643 0644;;;;N;;;;;
+FCC8;ARABIC LIGATURE KAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645;;;;N;;;;;
+FCC9;ARABIC LIGATURE LAM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C;;;;N;;;;;
+FCCA;ARABIC LIGATURE LAM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 062D;;;;N;;;;;
+FCCB;ARABIC LIGATURE LAM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0644 062E;;;;N;;;;;
+FCCC;ARABIC LIGATURE LAM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 0645;;;;N;;;;;
+FCCD;ARABIC LIGATURE LAM WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0644 0647;;;;N;;;;;
+FCCE;ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C;;;;N;;;;;
+FCCF;ARABIC LIGATURE MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062D;;;;N;;;;;
+FCD0;ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062E;;;;N;;;;;
+FCD1;ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 0645;;;;N;;;;;
+FCD2;ARABIC LIGATURE NOON WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C;;;;N;;;;;
+FCD3;ARABIC LIGATURE NOON WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062D;;;;N;;;;;
+FCD4;ARABIC LIGATURE NOON WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0646 062E;;;;N;;;;;
+FCD5;ARABIC LIGATURE NOON WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 0645;;;;N;;;;;
+FCD6;ARABIC LIGATURE NOON WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0646 0647;;;;N;;;;;
+FCD7;ARABIC LIGATURE HEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 062C;;;;N;;;;;
+FCD8;ARABIC LIGATURE HEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645;;;;N;;;;;
+FCD9;ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM;Lo;0;AL;<initial> 0647 0670;;;;N;;;;;
+FCDA;ARABIC LIGATURE YEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 064A 062C;;;;N;;;;;
+FCDB;ARABIC LIGATURE YEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 064A 062D;;;;N;;;;;
+FCDC;ARABIC LIGATURE YEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 064A 062E;;;;N;;;;;
+FCDD;ARABIC LIGATURE YEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645;;;;N;;;;;
+FCDE;ARABIC LIGATURE YEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 064A 0647;;;;N;;;;;
+FCDF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0626 0645;;;;N;;;;;
+FCE0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0626 0647;;;;N;;;;;
+FCE1;ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0628 0645;;;;N;;;;;
+FCE2;ARABIC LIGATURE BEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0628 0647;;;;N;;;;;
+FCE3;ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062A 0645;;;;N;;;;;
+FCE4;ARABIC LIGATURE TEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062A 0647;;;;N;;;;;
+FCE5;ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062B 0645;;;;N;;;;;
+FCE6;ARABIC LIGATURE THEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062B 0647;;;;N;;;;;
+FCE7;ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 0645;;;;N;;;;;
+FCE8;ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0633 0647;;;;N;;;;;
+FCE9;ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 0645;;;;N;;;;;
+FCEA;ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0634 0647;;;;N;;;;;
+FCEB;ARABIC LIGATURE KAF WITH LAM MEDIAL FORM;Lo;0;AL;<medial> 0643 0644;;;;N;;;;;
+FCEC;ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0643 0645;;;;N;;;;;
+FCED;ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0644 0645;;;;N;;;;;
+FCEE;ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0646 0645;;;;N;;;;;
+FCEF;ARABIC LIGATURE NOON WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0646 0647;;;;N;;;;;
+FCF0;ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 064A 0645;;;;N;;;;;
+FCF1;ARABIC LIGATURE YEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 064A 0647;;;;N;;;;;
+FCF2;ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E 0651;;;;N;;;;;
+FCF3;ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F 0651;;;;N;;;;;
+FCF4;ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650 0651;;;;N;;;;;
+FCF5;ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0637 0649;;;;N;;;;;
+FCF6;ARABIC LIGATURE TAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0637 064A;;;;N;;;;;
+FCF7;ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0639 0649;;;;N;;;;;
+FCF8;ARABIC LIGATURE AIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0639 064A;;;;N;;;;;
+FCF9;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 063A 0649;;;;N;;;;;
+FCFA;ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 063A 064A;;;;N;;;;;
+FCFB;ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0633 0649;;;;N;;;;;
+FCFC;ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0633 064A;;;;N;;;;;
+FCFD;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0634 0649;;;;N;;;;;
+FCFE;ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0634 064A;;;;N;;;;;
+FCFF;ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062D 0649;;;;N;;;;;
+FD00;ARABIC LIGATURE HAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062D 064A;;;;N;;;;;
+FD01;ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062C 0649;;;;N;;;;;
+FD02;ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062C 064A;;;;N;;;;;
+FD03;ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062E 0649;;;;N;;;;;
+FD04;ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062E 064A;;;;N;;;;;
+FD05;ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0649;;;;N;;;;;
+FD06;ARABIC LIGATURE SAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0635 064A;;;;N;;;;;
+FD07;ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0636 0649;;;;N;;;;;
+FD08;ARABIC LIGATURE DAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0636 064A;;;;N;;;;;
+FD09;ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 062C;;;;N;;;;;
+FD0A;ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062D;;;;N;;;;;
+FD0B;ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062E;;;;N;;;;;
+FD0C;ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 0645;;;;N;;;;;
+FD0D;ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0634 0631;;;;N;;;;;
+FD0E;ARABIC LIGATURE SEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0633 0631;;;;N;;;;;
+FD0F;ARABIC LIGATURE SAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0635 0631;;;;N;;;;;
+FD10;ARABIC LIGATURE DAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0636 0631;;;;N;;;;;
+FD11;ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0637 0649;;;;N;;;;;
+FD12;ARABIC LIGATURE TAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 064A;;;;N;;;;;
+FD13;ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0649;;;;N;;;;;
+FD14;ARABIC LIGATURE AIN WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 064A;;;;N;;;;;
+FD15;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0649;;;;N;;;;;
+FD16;ARABIC LIGATURE GHAIN WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 064A;;;;N;;;;;
+FD17;ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 0649;;;;N;;;;;
+FD18;ARABIC LIGATURE SEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 064A;;;;N;;;;;
+FD19;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0634 0649;;;;N;;;;;
+FD1A;ARABIC LIGATURE SHEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 064A;;;;N;;;;;
+FD1B;ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0649;;;;N;;;;;
+FD1C;ARABIC LIGATURE HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 064A;;;;N;;;;;
+FD1D;ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0649;;;;N;;;;;
+FD1E;ARABIC LIGATURE JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 064A;;;;N;;;;;
+FD1F;ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062E 0649;;;;N;;;;;
+FD20;ARABIC LIGATURE KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062E 064A;;;;N;;;;;
+FD21;ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0635 0649;;;;N;;;;;
+FD22;ARABIC LIGATURE SAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 064A;;;;N;;;;;
+FD23;ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 0649;;;;N;;;;;
+FD24;ARABIC LIGATURE DAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 064A;;;;N;;;;;
+FD25;ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM;Lo;0;AL;<final> 0634 062C;;;;N;;;;;
+FD26;ARABIC LIGATURE SHEEN WITH HAH FINAL FORM;Lo;0;AL;<final> 0634 062D;;;;N;;;;;
+FD27;ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 062E;;;;N;;;;;
+FD28;ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645;;;;N;;;;;
+FD29;ARABIC LIGATURE SHEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0634 0631;;;;N;;;;;
+FD2A;ARABIC LIGATURE SEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0633 0631;;;;N;;;;;
+FD2B;ARABIC LIGATURE SAD WITH REH FINAL FORM;Lo;0;AL;<final> 0635 0631;;;;N;;;;;
+FD2C;ARABIC LIGATURE DAD WITH REH FINAL FORM;Lo;0;AL;<final> 0636 0631;;;;N;;;;;
+FD2D;ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062C;;;;N;;;;;
+FD2E;ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0634 062D;;;;N;;;;;
+FD2F;ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 062E;;;;N;;;;;
+FD30;ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645;;;;N;;;;;
+FD31;ARABIC LIGATURE SEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0633 0647;;;;N;;;;;
+FD32;ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0634 0647;;;;N;;;;;
+FD33;ARABIC LIGATURE TAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645;;;;N;;;;;
+FD34;ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 062C;;;;N;;;;;
+FD35;ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062D;;;;N;;;;;
+FD36;ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062E;;;;N;;;;;
+FD37;ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 062C;;;;N;;;;;
+FD38;ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062D;;;;N;;;;;
+FD39;ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062E;;;;N;;;;;
+FD3A;ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0637 0645;;;;N;;;;;
+FD3B;ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0638 0645;;;;N;;;;;
+FD3C;ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM;Lo;0;AL;<final> 0627 064B;;;;N;;;;;
+FD3D;ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0627 064B;;;;N;;;;;
+FD3E;ORNATE LEFT PARENTHESIS;Ps;0;ON;;;;;N;;;;;
+FD3F;ORNATE RIGHT PARENTHESIS;Pe;0;ON;;;;;N;;;;;
+FD50;ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C 0645;;;;N;;;;;
+FD51;ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM;Lo;0;AL;<final> 062A 062D 062C;;;;N;;;;;
+FD52;ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 062C;;;;N;;;;;
+FD53;ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 0645;;;;N;;;;;
+FD54;ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062E 0645;;;;N;;;;;
+FD55;ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062C;;;;N;;;;;
+FD56;ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062D;;;;N;;;;;
+FD57;ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062E;;;;N;;;;;
+FD58;ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 062C 0645 062D;;;;N;;;;;
+FD59;ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 0645 062D;;;;N;;;;;
+FD5A;ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 0645 064A;;;;N;;;;;
+FD5B;ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0645 0649;;;;N;;;;;
+FD5C;ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062D 062C;;;;N;;;;;
+FD5D;ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062C 062D;;;;N;;;;;
+FD5E;ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062C 0649;;;;N;;;;;
+FD5F;ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0633 0645 062D;;;;N;;;;;
+FD60;ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062D;;;;N;;;;;
+FD61;ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062C;;;;N;;;;;
+FD62;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0633 0645 0645;;;;N;;;;;
+FD63;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 0645;;;;N;;;;;
+FD64;ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM;Lo;0;AL;<final> 0635 062D 062D;;;;N;;;;;
+FD65;ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D 062D;;;;N;;;;;
+FD66;ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0635 0645 0645;;;;N;;;;;
+FD67;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 062D 0645;;;;N;;;;;
+FD68;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062D 0645;;;;N;;;;;
+FD69;ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062C 064A;;;;N;;;;;
+FD6A;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 0645 062E;;;;N;;;;;
+FD6B;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 0645 062E;;;;N;;;;;
+FD6C;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645 0645;;;;N;;;;;
+FD6D;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645 0645;;;;N;;;;;
+FD6E;ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 062D 0649;;;;N;;;;;
+FD6F;ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0636 062E 0645;;;;N;;;;;
+FD70;ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062E 0645;;;;N;;;;;
+FD71;ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0637 0645 062D;;;;N;;;;;
+FD72;ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 0645 062D;;;;N;;;;;
+FD73;ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645 0645;;;;N;;;;;
+FD74;ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 0645 064A;;;;N;;;;;
+FD75;ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 062C 0645;;;;N;;;;;
+FD76;ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 0645 0645;;;;N;;;;;
+FD77;ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645 0645;;;;N;;;;;
+FD78;ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0645 0649;;;;N;;;;;
+FD79;ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 063A 0645 0645;;;;N;;;;;
+FD7A;ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 0645 064A;;;;N;;;;;
+FD7B;ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0645 0649;;;;N;;;;;
+FD7C;ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0641 062E 0645;;;;N;;;;;
+FD7D;ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062E 0645;;;;N;;;;;
+FD7E;ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0642 0645 062D;;;;N;;;;;
+FD7F;ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0642 0645 0645;;;;N;;;;;
+FD80;ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062D 0645;;;;N;;;;;
+FD81;ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062D 064A;;;;N;;;;;
+FD82;ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 062D 0649;;;;N;;;;;
+FD83;ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 062C;;;;N;;;;;
+FD84;ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 062C;;;;N;;;;;
+FD85;ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062E 0645;;;;N;;;;;
+FD86;ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062E 0645;;;;N;;;;;
+FD87;ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0644 0645 062D;;;;N;;;;;
+FD88;ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 0645 062D;;;;N;;;;;
+FD89;ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 062C;;;;N;;;;;
+FD8A;ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 0645;;;;N;;;;;
+FD8B;ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062D 064A;;;;N;;;;;
+FD8C;ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062D;;;;N;;;;;
+FD8D;ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C 0645;;;;N;;;;;
+FD8E;ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 062C;;;;N;;;;;
+FD8F;ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 0645;;;;N;;;;;
+FD92;ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062E;;;;N;;;;;
+FD93;ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 062C;;;;N;;;;;
+FD94;ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 0645;;;;N;;;;;
+FD95;ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062D 0645;;;;N;;;;;
+FD96;ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062D 0649;;;;N;;;;;
+FD97;ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 062C 0645;;;;N;;;;;
+FD98;ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C 0645;;;;N;;;;;
+FD99;ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062C 0649;;;;N;;;;;
+FD9A;ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 0645 064A;;;;N;;;;;
+FD9B;ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0645 0649;;;;N;;;;;
+FD9C;ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645 0645;;;;N;;;;;
+FD9D;ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645 0645;;;;N;;;;;
+FD9E;ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062E 064A;;;;N;;;;;
+FD9F;ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062C 064A;;;;N;;;;;
+FDA0;ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062C 0649;;;;N;;;;;
+FDA1;ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062E 064A;;;;N;;;;;
+FDA2;ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062E 0649;;;;N;;;;;
+FDA3;ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 0645 064A;;;;N;;;;;
+FDA4;ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0645 0649;;;;N;;;;;
+FDA5;ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 0645 064A;;;;N;;;;;
+FDA6;ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 062D 0649;;;;N;;;;;
+FDA7;ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0645 0649;;;;N;;;;;
+FDA8;ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062E 0649;;;;N;;;;;
+FDA9;ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 062D 064A;;;;N;;;;;
+FDAA;ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062D 064A;;;;N;;;;;
+FDAB;ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 062D 064A;;;;N;;;;;
+FDAC;ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062C 064A;;;;N;;;;;
+FDAD;ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 0645 064A;;;;N;;;;;
+FDAE;ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062D 064A;;;;N;;;;;
+FDAF;ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062C 064A;;;;N;;;;;
+FDB0;ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 0645 064A;;;;N;;;;;
+FDB1;ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 0645 064A;;;;N;;;;;
+FDB2;ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 0645 064A;;;;N;;;;;
+FDB3;ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062D 064A;;;;N;;;;;
+FDB4;ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 0645 062D;;;;N;;;;;
+FDB5;ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062D 0645;;;;N;;;;;
+FDB6;ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 0645 064A;;;;N;;;;;
+FDB7;ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 0645 064A;;;;N;;;;;
+FDB8;ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062C 062D;;;;N;;;;;
+FDB9;ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062E 064A;;;;N;;;;;
+FDBA;ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 0645;;;;N;;;;;
+FDBB;ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645 0645;;;;N;;;;;
+FDBC;ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 0645;;;;N;;;;;
+FDBD;ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0646 062C 062D;;;;N;;;;;
+FDBE;ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 062D 064A;;;;N;;;;;
+FDBF;ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 062C 064A;;;;N;;;;;
+FDC0;ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062C 064A;;;;N;;;;;
+FDC1;ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 0645 064A;;;;N;;;;;
+FDC2;ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062D 064A;;;;N;;;;;
+FDC3;ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645 0645;;;;N;;;;;
+FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C 0645;;;;N;;;;;
+FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645 0645;;;;N;;;;;
+FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 062E 064A;;;;N;;;;;
+FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062C 064A;;;;N;;;;;
+FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 06D2;;;;N;;;;;
+FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0642 0644 06D2;;;;N;;;;;
+FDF2;ARABIC LIGATURE ALLAH ISOLATED FORM;Lo;0;AL;<isolated> 0627 0644 0644 0647;;;;N;;;;;
+FDF3;ARABIC LIGATURE AKBAR ISOLATED FORM;Lo;0;AL;<isolated> 0627 0643 0628 0631;;;;N;;;;;
+FDF4;ARABIC LIGATURE MOHAMMAD ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D 0645 062F;;;;N;;;;;
+FDF5;ARABIC LIGATURE SALAM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0639 0645;;;;N;;;;;
+FDF6;ARABIC LIGATURE RASOUL ISOLATED FORM;Lo;0;AL;<isolated> 0631 0633 0648 0644;;;;N;;;;;
+FDF7;ARABIC LIGATURE ALAYHE ISOLATED FORM;Lo;0;AL;<isolated> 0639 0644 064A 0647;;;;N;;;;;
+FDF8;ARABIC LIGATURE WASALLAM ISOLATED FORM;Lo;0;AL;<isolated> 0648 0633 0644 0645;;;;N;;;;;
+FDF9;ARABIC LIGATURE SALLA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0649;;;;N;;;;;
+FDFA;ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM;Lo;0;AL;<isolated> 0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645;;;;N;ARABIC LETTER SALLALLAHOU ALAYHE WASALLAM;;;;
+FDFB;ARABIC LIGATURE JALLAJALALOUHOU;Lo;0;AL;<isolated> 062C 0644 0020 062C 0644 0627 0644 0647;;;;N;ARABIC LETTER JALLAJALALOUHOU;;;;
+FE20;COMBINING LIGATURE LEFT HALF;Mn;230;NSM;;;;;N;;;;;
+FE21;COMBINING LIGATURE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
+FE22;COMBINING DOUBLE TILDE LEFT HALF;Mn;230;NSM;;;;;N;;;;;
+FE23;COMBINING DOUBLE TILDE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
+FE30;PRESENTATION FORM FOR VERTICAL TWO DOT LEADER;Po;0;ON;<vertical> 2025;;;;N;GLYPH FOR VERTICAL TWO DOT LEADER;;;;
+FE31;PRESENTATION FORM FOR VERTICAL EM DASH;Pd;0;ON;<vertical> 2014;;;;N;GLYPH FOR VERTICAL EM DASH;;;;
+FE32;PRESENTATION FORM FOR VERTICAL EN DASH;Pd;0;ON;<vertical> 2013;;;;N;GLYPH FOR VERTICAL EN DASH;;;;
+FE33;PRESENTATION FORM FOR VERTICAL LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING UNDERSCORE;;;;
+FE34;PRESENTATION FORM FOR VERTICAL WAVY LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING WAVY UNDERSCORE;;;;
+FE35;PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS;Ps;0;ON;<vertical> 0028;;;;N;GLYPH FOR VERTICAL OPENING PARENTHESIS;;;;
+FE36;PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS;Pe;0;ON;<vertical> 0029;;;;N;GLYPH FOR VERTICAL CLOSING PARENTHESIS;;;;
+FE37;PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET;Ps;0;ON;<vertical> 007B;;;;N;GLYPH FOR VERTICAL OPENING CURLY BRACKET;;;;
+FE38;PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET;Pe;0;ON;<vertical> 007D;;;;N;GLYPH FOR VERTICAL CLOSING CURLY BRACKET;;;;
+FE39;PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<vertical> 3014;;;;N;GLYPH FOR VERTICAL OPENING TORTOISE SHELL BRACKET;;;;
+FE3A;PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<vertical> 3015;;;;N;GLYPH FOR VERTICAL CLOSING TORTOISE SHELL BRACKET;;;;
+FE3B;PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;<vertical> 3010;;;;N;GLYPH FOR VERTICAL OPENING BLACK LENTICULAR BRACKET;;;;
+FE3C;PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;<vertical> 3011;;;;N;GLYPH FOR VERTICAL CLOSING BLACK LENTICULAR BRACKET;;;;
+FE3D;PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;<vertical> 300A;;;;N;GLYPH FOR VERTICAL OPENING DOUBLE ANGLE BRACKET;;;;
+FE3E;PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;<vertical> 300B;;;;N;GLYPH FOR VERTICAL CLOSING DOUBLE ANGLE BRACKET;;;;
+FE3F;PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET;Ps;0;ON;<vertical> 3008;;;;N;GLYPH FOR VERTICAL OPENING ANGLE BRACKET;;;;
+FE40;PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET;Pe;0;ON;<vertical> 3009;;;;N;GLYPH FOR VERTICAL CLOSING ANGLE BRACKET;;;;
+FE41;PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET;Ps;0;ON;<vertical> 300C;;;;N;GLYPH FOR VERTICAL OPENING CORNER BRACKET;;;;
+FE42;PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET;Pe;0;ON;<vertical> 300D;;;;N;GLYPH FOR VERTICAL CLOSING CORNER BRACKET;;;;
+FE43;PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET;Ps;0;ON;<vertical> 300E;;;;N;GLYPH FOR VERTICAL OPENING WHITE CORNER BRACKET;;;;
+FE44;PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET;Pe;0;ON;<vertical> 300F;;;;N;GLYPH FOR VERTICAL CLOSING WHITE CORNER BRACKET;;;;
+FE49;DASHED OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DASHED OVERSCORE;;;;
+FE4A;CENTRELINE OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING CENTERLINE OVERSCORE;;;;
+FE4B;WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING WAVY OVERSCORE;;;;
+FE4C;DOUBLE WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DOUBLE WAVY OVERSCORE;;;;
+FE4D;DASHED LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING DASHED UNDERSCORE;;;;
+FE4E;CENTRELINE LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING CENTERLINE UNDERSCORE;;;;
+FE4F;WAVY LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING WAVY UNDERSCORE;;;;
+FE50;SMALL COMMA;Po;0;CS;<small> 002C;;;;N;;;;;
+FE51;SMALL IDEOGRAPHIC COMMA;Po;0;ON;<small> 3001;;;;N;;;;;
+FE52;SMALL FULL STOP;Po;0;CS;<small> 002E;;;;N;SMALL PERIOD;;;;
+FE54;SMALL SEMICOLON;Po;0;ON;<small> 003B;;;;N;;;;;
+FE55;SMALL COLON;Po;0;CS;<small> 003A;;;;N;;;;;
+FE56;SMALL QUESTION MARK;Po;0;ON;<small> 003F;;;;N;;;;;
+FE57;SMALL EXCLAMATION MARK;Po;0;ON;<small> 0021;;;;N;;;;;
+FE58;SMALL EM DASH;Pd;0;ON;<small> 2014;;;;N;;;;;
+FE59;SMALL LEFT PARENTHESIS;Ps;0;ON;<small> 0028;;;;N;SMALL OPENING PARENTHESIS;;;;
+FE5A;SMALL RIGHT PARENTHESIS;Pe;0;ON;<small> 0029;;;;N;SMALL CLOSING PARENTHESIS;;;;
+FE5B;SMALL LEFT CURLY BRACKET;Ps;0;ON;<small> 007B;;;;N;SMALL OPENING CURLY BRACKET;;;;
+FE5C;SMALL RIGHT CURLY BRACKET;Pe;0;ON;<small> 007D;;;;N;SMALL CLOSING CURLY BRACKET;;;;
+FE5D;SMALL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<small> 3014;;;;N;SMALL OPENING TORTOISE SHELL BRACKET;;;;
+FE5E;SMALL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<small> 3015;;;;N;SMALL CLOSING TORTOISE SHELL BRACKET;;;;
+FE5F;SMALL NUMBER SIGN;Po;0;ET;<small> 0023;;;;N;;;;;
+FE60;SMALL AMPERSAND;Po;0;ON;<small> 0026;;;;N;;;;;
+FE61;SMALL ASTERISK;Po;0;ON;<small> 002A;;;;N;;;;;
+FE62;SMALL PLUS SIGN;Sm;0;ET;<small> 002B;;;;N;;;;;
+FE63;SMALL HYPHEN-MINUS;Pd;0;ET;<small> 002D;;;;N;;;;;
+FE64;SMALL LESS-THAN SIGN;Sm;0;ON;<small> 003C;;;;N;;;;;
+FE65;SMALL GREATER-THAN SIGN;Sm;0;ON;<small> 003E;;;;N;;;;;
+FE66;SMALL EQUALS SIGN;Sm;0;ON;<small> 003D;;;;N;;;;;
+FE68;SMALL REVERSE SOLIDUS;Po;0;ON;<small> 005C;;;;N;SMALL BACKSLASH;;;;
+FE69;SMALL DOLLAR SIGN;Sc;0;ET;<small> 0024;;;;N;;;;;
+FE6A;SMALL PERCENT SIGN;Po;0;ET;<small> 0025;;;;N;;;;;
+FE6B;SMALL COMMERCIAL AT;Po;0;ON;<small> 0040;;;;N;;;;;
+FE70;ARABIC FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064B;;;;N;ARABIC SPACING FATHATAN;;;;
+FE71;ARABIC TATWEEL WITH FATHATAN ABOVE;Lo;0;AL;<medial> 0640 064B;;;;N;ARABIC FATHATAN ON TATWEEL;;;;
+FE72;ARABIC DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C;;;;N;ARABIC SPACING DAMMATAN;;;;
+FE74;ARABIC KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D;;;;N;ARABIC SPACING KASRATAN;;;;
+FE76;ARABIC FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E;;;;N;ARABIC SPACING FATHAH;;;;
+FE77;ARABIC FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E;;;;N;ARABIC FATHAH ON TATWEEL;;;;
+FE78;ARABIC DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F;;;;N;ARABIC SPACING DAMMAH;;;;
+FE79;ARABIC DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F;;;;N;ARABIC DAMMAH ON TATWEEL;;;;
+FE7A;ARABIC KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650;;;;N;ARABIC SPACING KASRAH;;;;
+FE7B;ARABIC KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650;;;;N;ARABIC KASRAH ON TATWEEL;;;;
+FE7C;ARABIC SHADDA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651;;;;N;ARABIC SPACING SHADDAH;;;;
+FE7D;ARABIC SHADDA MEDIAL FORM;Lo;0;AL;<medial> 0640 0651;;;;N;ARABIC SHADDAH ON TATWEEL;;;;
+FE7E;ARABIC SUKUN ISOLATED FORM;Lo;0;AL;<isolated> 0020 0652;;;;N;ARABIC SPACING SUKUN;;;;
+FE7F;ARABIC SUKUN MEDIAL FORM;Lo;0;AL;<medial> 0640 0652;;;;N;ARABIC SUKUN ON TATWEEL;;;;
+FE80;ARABIC LETTER HAMZA ISOLATED FORM;Lo;0;AL;<isolated> 0621;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH;;;;
+FE81;ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON ALEF;;;;
+FE82;ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON ALEF;;;;
+FE83;ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON ALEF;;;;
+FE84;ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON ALEF;;;;
+FE85;ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0624;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON WAW;;;;
+FE86;ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0624;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON WAW;;;;
+FE87;ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER ALEF;;;;
+FE88;ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER ALEF;;;;
+FE89;ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0626;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON YA;;;;
+FE8A;ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0626;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON YA;;;;
+FE8B;ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM;Lo;0;AL;<initial> 0626;;;;N;GLYPH FOR INITIAL ARABIC HAMZAH ON YA;;;;
+FE8C;ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM;Lo;0;AL;<medial> 0626;;;;N;GLYPH FOR MEDIAL ARABIC HAMZAH ON YA;;;;
+FE8D;ARABIC LETTER ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0627;;;;N;GLYPH FOR ISOLATE ARABIC ALEF;;;;
+FE8E;ARABIC LETTER ALEF FINAL FORM;Lo;0;AL;<final> 0627;;;;N;GLYPH FOR FINAL ARABIC ALEF;;;;
+FE8F;ARABIC LETTER BEH ISOLATED FORM;Lo;0;AL;<isolated> 0628;;;;N;GLYPH FOR ISOLATE ARABIC BAA;;;;
+FE90;ARABIC LETTER BEH FINAL FORM;Lo;0;AL;<final> 0628;;;;N;GLYPH FOR FINAL ARABIC BAA;;;;
+FE91;ARABIC LETTER BEH INITIAL FORM;Lo;0;AL;<initial> 0628;;;;N;GLYPH FOR INITIAL ARABIC BAA;;;;
+FE92;ARABIC LETTER BEH MEDIAL FORM;Lo;0;AL;<medial> 0628;;;;N;GLYPH FOR MEDIAL ARABIC BAA;;;;
+FE93;ARABIC LETTER TEH MARBUTA ISOLATED FORM;Lo;0;AL;<isolated> 0629;;;;N;GLYPH FOR ISOLATE ARABIC TAA MARBUTAH;;;;
+FE94;ARABIC LETTER TEH MARBUTA FINAL FORM;Lo;0;AL;<final> 0629;;;;N;GLYPH FOR FINAL ARABIC TAA MARBUTAH;;;;
+FE95;ARABIC LETTER TEH ISOLATED FORM;Lo;0;AL;<isolated> 062A;;;;N;GLYPH FOR ISOLATE ARABIC TAA;;;;
+FE96;ARABIC LETTER TEH FINAL FORM;Lo;0;AL;<final> 062A;;;;N;GLYPH FOR FINAL ARABIC TAA;;;;
+FE97;ARABIC LETTER TEH INITIAL FORM;Lo;0;AL;<initial> 062A;;;;N;GLYPH FOR INITIAL ARABIC TAA;;;;
+FE98;ARABIC LETTER TEH MEDIAL FORM;Lo;0;AL;<medial> 062A;;;;N;GLYPH FOR MEDIAL ARABIC TAA;;;;
+FE99;ARABIC LETTER THEH ISOLATED FORM;Lo;0;AL;<isolated> 062B;;;;N;GLYPH FOR ISOLATE ARABIC THAA;;;;
+FE9A;ARABIC LETTER THEH FINAL FORM;Lo;0;AL;<final> 062B;;;;N;GLYPH FOR FINAL ARABIC THAA;;;;
+FE9B;ARABIC LETTER THEH INITIAL FORM;Lo;0;AL;<initial> 062B;;;;N;GLYPH FOR INITIAL ARABIC THAA;;;;
+FE9C;ARABIC LETTER THEH MEDIAL FORM;Lo;0;AL;<medial> 062B;;;;N;GLYPH FOR MEDIAL ARABIC THAA;;;;
+FE9D;ARABIC LETTER JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C;;;;N;GLYPH FOR ISOLATE ARABIC JEEM;;;;
+FE9E;ARABIC LETTER JEEM FINAL FORM;Lo;0;AL;<final> 062C;;;;N;GLYPH FOR FINAL ARABIC JEEM;;;;
+FE9F;ARABIC LETTER JEEM INITIAL FORM;Lo;0;AL;<initial> 062C;;;;N;GLYPH FOR INITIAL ARABIC JEEM;;;;
+FEA0;ARABIC LETTER JEEM MEDIAL FORM;Lo;0;AL;<medial> 062C;;;;N;GLYPH FOR MEDIAL ARABIC JEEM;;;;
+FEA1;ARABIC LETTER HAH ISOLATED FORM;Lo;0;AL;<isolated> 062D;;;;N;GLYPH FOR ISOLATE ARABIC HAA;;;;
+FEA2;ARABIC LETTER HAH FINAL FORM;Lo;0;AL;<final> 062D;;;;N;GLYPH FOR FINAL ARABIC HAA;;;;
+FEA3;ARABIC LETTER HAH INITIAL FORM;Lo;0;AL;<initial> 062D;;;;N;GLYPH FOR INITIAL ARABIC HAA;;;;
+FEA4;ARABIC LETTER HAH MEDIAL FORM;Lo;0;AL;<medial> 062D;;;;N;GLYPH FOR MEDIAL ARABIC HAA;;;;
+FEA5;ARABIC LETTER KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062E;;;;N;GLYPH FOR ISOLATE ARABIC KHAA;;;;
+FEA6;ARABIC LETTER KHAH FINAL FORM;Lo;0;AL;<final> 062E;;;;N;GLYPH FOR FINAL ARABIC KHAA;;;;
+FEA7;ARABIC LETTER KHAH INITIAL FORM;Lo;0;AL;<initial> 062E;;;;N;GLYPH FOR INITIAL ARABIC KHAA;;;;
+FEA8;ARABIC LETTER KHAH MEDIAL FORM;Lo;0;AL;<medial> 062E;;;;N;GLYPH FOR MEDIAL ARABIC KHAA;;;;
+FEA9;ARABIC LETTER DAL ISOLATED FORM;Lo;0;AL;<isolated> 062F;;;;N;GLYPH FOR ISOLATE ARABIC DAL;;;;
+FEAA;ARABIC LETTER DAL FINAL FORM;Lo;0;AL;<final> 062F;;;;N;GLYPH FOR FINAL ARABIC DAL;;;;
+FEAB;ARABIC LETTER THAL ISOLATED FORM;Lo;0;AL;<isolated> 0630;;;;N;GLYPH FOR ISOLATE ARABIC THAL;;;;
+FEAC;ARABIC LETTER THAL FINAL FORM;Lo;0;AL;<final> 0630;;;;N;GLYPH FOR FINAL ARABIC THAL;;;;
+FEAD;ARABIC LETTER REH ISOLATED FORM;Lo;0;AL;<isolated> 0631;;;;N;GLYPH FOR ISOLATE ARABIC RA;;;;
+FEAE;ARABIC LETTER REH FINAL FORM;Lo;0;AL;<final> 0631;;;;N;GLYPH FOR FINAL ARABIC RA;;;;
+FEAF;ARABIC LETTER ZAIN ISOLATED FORM;Lo;0;AL;<isolated> 0632;;;;N;GLYPH FOR ISOLATE ARABIC ZAIN;;;;
+FEB0;ARABIC LETTER ZAIN FINAL FORM;Lo;0;AL;<final> 0632;;;;N;GLYPH FOR FINAL ARABIC ZAIN;;;;
+FEB1;ARABIC LETTER SEEN ISOLATED FORM;Lo;0;AL;<isolated> 0633;;;;N;GLYPH FOR ISOLATE ARABIC SEEN;;;;
+FEB2;ARABIC LETTER SEEN FINAL FORM;Lo;0;AL;<final> 0633;;;;N;GLYPH FOR FINAL ARABIC SEEN;;;;
+FEB3;ARABIC LETTER SEEN INITIAL FORM;Lo;0;AL;<initial> 0633;;;;N;GLYPH FOR INITIAL ARABIC SEEN;;;;
+FEB4;ARABIC LETTER SEEN MEDIAL FORM;Lo;0;AL;<medial> 0633;;;;N;GLYPH FOR MEDIAL ARABIC SEEN;;;;
+FEB5;ARABIC LETTER SHEEN ISOLATED FORM;Lo;0;AL;<isolated> 0634;;;;N;GLYPH FOR ISOLATE ARABIC SHEEN;;;;
+FEB6;ARABIC LETTER SHEEN FINAL FORM;Lo;0;AL;<final> 0634;;;;N;GLYPH FOR FINAL ARABIC SHEEN;;;;
+FEB7;ARABIC LETTER SHEEN INITIAL FORM;Lo;0;AL;<initial> 0634;;;;N;GLYPH FOR INITIAL ARABIC SHEEN;;;;
+FEB8;ARABIC LETTER SHEEN MEDIAL FORM;Lo;0;AL;<medial> 0634;;;;N;GLYPH FOR MEDIAL ARABIC SHEEN;;;;
+FEB9;ARABIC LETTER SAD ISOLATED FORM;Lo;0;AL;<isolated> 0635;;;;N;GLYPH FOR ISOLATE ARABIC SAD;;;;
+FEBA;ARABIC LETTER SAD FINAL FORM;Lo;0;AL;<final> 0635;;;;N;GLYPH FOR FINAL ARABIC SAD;;;;
+FEBB;ARABIC LETTER SAD INITIAL FORM;Lo;0;AL;<initial> 0635;;;;N;GLYPH FOR INITIAL ARABIC SAD;;;;
+FEBC;ARABIC LETTER SAD MEDIAL FORM;Lo;0;AL;<medial> 0635;;;;N;GLYPH FOR MEDIAL ARABIC SAD;;;;
+FEBD;ARABIC LETTER DAD ISOLATED FORM;Lo;0;AL;<isolated> 0636;;;;N;GLYPH FOR ISOLATE ARABIC DAD;;;;
+FEBE;ARABIC LETTER DAD FINAL FORM;Lo;0;AL;<final> 0636;;;;N;GLYPH FOR FINAL ARABIC DAD;;;;
+FEBF;ARABIC LETTER DAD INITIAL FORM;Lo;0;AL;<initial> 0636;;;;N;GLYPH FOR INITIAL ARABIC DAD;;;;
+FEC0;ARABIC LETTER DAD MEDIAL FORM;Lo;0;AL;<medial> 0636;;;;N;GLYPH FOR MEDIAL ARABIC DAD;;;;
+FEC1;ARABIC LETTER TAH ISOLATED FORM;Lo;0;AL;<isolated> 0637;;;;N;GLYPH FOR ISOLATE ARABIC TAH;;;;
+FEC2;ARABIC LETTER TAH FINAL FORM;Lo;0;AL;<final> 0637;;;;N;GLYPH FOR FINAL ARABIC TAH;;;;
+FEC3;ARABIC LETTER TAH INITIAL FORM;Lo;0;AL;<initial> 0637;;;;N;GLYPH FOR INITIAL ARABIC TAH;;;;
+FEC4;ARABIC LETTER TAH MEDIAL FORM;Lo;0;AL;<medial> 0637;;;;N;GLYPH FOR MEDIAL ARABIC TAH;;;;
+FEC5;ARABIC LETTER ZAH ISOLATED FORM;Lo;0;AL;<isolated> 0638;;;;N;GLYPH FOR ISOLATE ARABIC DHAH;;;;
+FEC6;ARABIC LETTER ZAH FINAL FORM;Lo;0;AL;<final> 0638;;;;N;GLYPH FOR FINAL ARABIC DHAH;;;;
+FEC7;ARABIC LETTER ZAH INITIAL FORM;Lo;0;AL;<initial> 0638;;;;N;GLYPH FOR INITIAL ARABIC DHAH;;;;
+FEC8;ARABIC LETTER ZAH MEDIAL FORM;Lo;0;AL;<medial> 0638;;;;N;GLYPH FOR MEDIAL ARABIC DHAH;;;;
+FEC9;ARABIC LETTER AIN ISOLATED FORM;Lo;0;AL;<isolated> 0639;;;;N;GLYPH FOR ISOLATE ARABIC AIN;;;;
+FECA;ARABIC LETTER AIN FINAL FORM;Lo;0;AL;<final> 0639;;;;N;GLYPH FOR FINAL ARABIC AIN;;;;
+FECB;ARABIC LETTER AIN INITIAL FORM;Lo;0;AL;<initial> 0639;;;;N;GLYPH FOR INITIAL ARABIC AIN;;;;
+FECC;ARABIC LETTER AIN MEDIAL FORM;Lo;0;AL;<medial> 0639;;;;N;GLYPH FOR MEDIAL ARABIC AIN;;;;
+FECD;ARABIC LETTER GHAIN ISOLATED FORM;Lo;0;AL;<isolated> 063A;;;;N;GLYPH FOR ISOLATE ARABIC GHAIN;;;;
+FECE;ARABIC LETTER GHAIN FINAL FORM;Lo;0;AL;<final> 063A;;;;N;GLYPH FOR FINAL ARABIC GHAIN;;;;
+FECF;ARABIC LETTER GHAIN INITIAL FORM;Lo;0;AL;<initial> 063A;;;;N;GLYPH FOR INITIAL ARABIC GHAIN;;;;
+FED0;ARABIC LETTER GHAIN MEDIAL FORM;Lo;0;AL;<medial> 063A;;;;N;GLYPH FOR MEDIAL ARABIC GHAIN;;;;
+FED1;ARABIC LETTER FEH ISOLATED FORM;Lo;0;AL;<isolated> 0641;;;;N;GLYPH FOR ISOLATE ARABIC FA;;;;
+FED2;ARABIC LETTER FEH FINAL FORM;Lo;0;AL;<final> 0641;;;;N;GLYPH FOR FINAL ARABIC FA;;;;
+FED3;ARABIC LETTER FEH INITIAL FORM;Lo;0;AL;<initial> 0641;;;;N;GLYPH FOR INITIAL ARABIC FA;;;;
+FED4;ARABIC LETTER FEH MEDIAL FORM;Lo;0;AL;<medial> 0641;;;;N;GLYPH FOR MEDIAL ARABIC FA;;;;
+FED5;ARABIC LETTER QAF ISOLATED FORM;Lo;0;AL;<isolated> 0642;;;;N;GLYPH FOR ISOLATE ARABIC QAF;;;;
+FED6;ARABIC LETTER QAF FINAL FORM;Lo;0;AL;<final> 0642;;;;N;GLYPH FOR FINAL ARABIC QAF;;;;
+FED7;ARABIC LETTER QAF INITIAL FORM;Lo;0;AL;<initial> 0642;;;;N;GLYPH FOR INITIAL ARABIC QAF;;;;
+FED8;ARABIC LETTER QAF MEDIAL FORM;Lo;0;AL;<medial> 0642;;;;N;GLYPH FOR MEDIAL ARABIC QAF;;;;
+FED9;ARABIC LETTER KAF ISOLATED FORM;Lo;0;AL;<isolated> 0643;;;;N;GLYPH FOR ISOLATE ARABIC CAF;;;;
+FEDA;ARABIC LETTER KAF FINAL FORM;Lo;0;AL;<final> 0643;;;;N;GLYPH FOR FINAL ARABIC CAF;;;;
+FEDB;ARABIC LETTER KAF INITIAL FORM;Lo;0;AL;<initial> 0643;;;;N;GLYPH FOR INITIAL ARABIC CAF;;;;
+FEDC;ARABIC LETTER KAF MEDIAL FORM;Lo;0;AL;<medial> 0643;;;;N;GLYPH FOR MEDIAL ARABIC CAF;;;;
+FEDD;ARABIC LETTER LAM ISOLATED FORM;Lo;0;AL;<isolated> 0644;;;;N;GLYPH FOR ISOLATE ARABIC LAM;;;;
+FEDE;ARABIC LETTER LAM FINAL FORM;Lo;0;AL;<final> 0644;;;;N;GLYPH FOR FINAL ARABIC LAM;;;;
+FEDF;ARABIC LETTER LAM INITIAL FORM;Lo;0;AL;<initial> 0644;;;;N;GLYPH FOR INITIAL ARABIC LAM;;;;
+FEE0;ARABIC LETTER LAM MEDIAL FORM;Lo;0;AL;<medial> 0644;;;;N;GLYPH FOR MEDIAL ARABIC LAM;;;;
+FEE1;ARABIC LETTER MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645;;;;N;GLYPH FOR ISOLATE ARABIC MEEM;;;;
+FEE2;ARABIC LETTER MEEM FINAL FORM;Lo;0;AL;<final> 0645;;;;N;GLYPH FOR FINAL ARABIC MEEM;;;;
+FEE3;ARABIC LETTER MEEM INITIAL FORM;Lo;0;AL;<initial> 0645;;;;N;GLYPH FOR INITIAL ARABIC MEEM;;;;
+FEE4;ARABIC LETTER MEEM MEDIAL FORM;Lo;0;AL;<medial> 0645;;;;N;GLYPH FOR MEDIAL ARABIC MEEM;;;;
+FEE5;ARABIC LETTER NOON ISOLATED FORM;Lo;0;AL;<isolated> 0646;;;;N;GLYPH FOR ISOLATE ARABIC NOON;;;;
+FEE6;ARABIC LETTER NOON FINAL FORM;Lo;0;AL;<final> 0646;;;;N;GLYPH FOR FINAL ARABIC NOON;;;;
+FEE7;ARABIC LETTER NOON INITIAL FORM;Lo;0;AL;<initial> 0646;;;;N;GLYPH FOR INITIAL ARABIC NOON;;;;
+FEE8;ARABIC LETTER NOON MEDIAL FORM;Lo;0;AL;<medial> 0646;;;;N;GLYPH FOR MEDIAL ARABIC NOON;;;;
+FEE9;ARABIC LETTER HEH ISOLATED FORM;Lo;0;AL;<isolated> 0647;;;;N;GLYPH FOR ISOLATE ARABIC HA;;;;
+FEEA;ARABIC LETTER HEH FINAL FORM;Lo;0;AL;<final> 0647;;;;N;GLYPH FOR FINAL ARABIC HA;;;;
+FEEB;ARABIC LETTER HEH INITIAL FORM;Lo;0;AL;<initial> 0647;;;;N;GLYPH FOR INITIAL ARABIC HA;;;;
+FEEC;ARABIC LETTER HEH MEDIAL FORM;Lo;0;AL;<medial> 0647;;;;N;GLYPH FOR MEDIAL ARABIC HA;;;;
+FEED;ARABIC LETTER WAW ISOLATED FORM;Lo;0;AL;<isolated> 0648;;;;N;GLYPH FOR ISOLATE ARABIC WAW;;;;
+FEEE;ARABIC LETTER WAW FINAL FORM;Lo;0;AL;<final> 0648;;;;N;GLYPH FOR FINAL ARABIC WAW;;;;
+FEEF;ARABIC LETTER ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0649;;;;N;GLYPH FOR ISOLATE ARABIC ALEF MAQSURAH;;;;
+FEF0;ARABIC LETTER ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0649;;;;N;GLYPH FOR FINAL ARABIC ALEF MAQSURAH;;;;
+FEF1;ARABIC LETTER YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A;;;;N;GLYPH FOR ISOLATE ARABIC YA;;;;
+FEF2;ARABIC LETTER YEH FINAL FORM;Lo;0;AL;<final> 064A;;;;N;GLYPH FOR FINAL ARABIC YA;;;;
+FEF3;ARABIC LETTER YEH INITIAL FORM;Lo;0;AL;<initial> 064A;;;;N;GLYPH FOR INITIAL ARABIC YA;;;;
+FEF4;ARABIC LETTER YEH MEDIAL FORM;Lo;0;AL;<medial> 064A;;;;N;GLYPH FOR MEDIAL ARABIC YA;;;;
+FEF5;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON LIGATURE LAM ALEF;;;;
+FEF6;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON LIGATURE LAM ALEF;;;;
+FEF7;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON LIGATURE LAM ALEF;;;;
+FEF8;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON LIGATURE LAM ALEF;;;;
+FEF9;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0644 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;;
+FEFA;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0644 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;;
+FEFB;ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0644 0627;;;;N;GLYPH FOR ISOLATE ARABIC LIGATURE LAM ALEF;;;;
+FEFC;ARABIC LIGATURE LAM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0644 0627;;;;N;GLYPH FOR FINAL ARABIC LIGATURE LAM ALEF;;;;
+FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;;
+FF01;FULLWIDTH EXCLAMATION MARK;Po;0;ON;<wide> 0021;;;;N;;;;;
+FF02;FULLWIDTH QUOTATION MARK;Po;0;ON;<wide> 0022;;;;N;;;;;
+FF03;FULLWIDTH NUMBER SIGN;Po;0;ET;<wide> 0023;;;;N;;;;;
+FF04;FULLWIDTH DOLLAR SIGN;Sc;0;ET;<wide> 0024;;;;N;;;;;
+FF05;FULLWIDTH PERCENT SIGN;Po;0;ET;<wide> 0025;;;;N;;;;;
+FF06;FULLWIDTH AMPERSAND;Po;0;ON;<wide> 0026;;;;N;;;;;
+FF07;FULLWIDTH APOSTROPHE;Po;0;ON;<wide> 0027;;;;N;;;;;
+FF08;FULLWIDTH LEFT PARENTHESIS;Ps;0;ON;<wide> 0028;;;;N;FULLWIDTH OPENING PARENTHESIS;;;;
+FF09;FULLWIDTH RIGHT PARENTHESIS;Pe;0;ON;<wide> 0029;;;;N;FULLWIDTH CLOSING PARENTHESIS;;;;
+FF0A;FULLWIDTH ASTERISK;Po;0;ON;<wide> 002A;;;;N;;;;;
+FF0B;FULLWIDTH PLUS SIGN;Sm;0;ET;<wide> 002B;;;;N;;;;;
+FF0C;FULLWIDTH COMMA;Po;0;CS;<wide> 002C;;;;N;;;;;
+FF0D;FULLWIDTH HYPHEN-MINUS;Pd;0;ET;<wide> 002D;;;;N;;;;;
+FF0E;FULLWIDTH FULL STOP;Po;0;CS;<wide> 002E;;;;N;FULLWIDTH PERIOD;;;;
+FF0F;FULLWIDTH SOLIDUS;Po;0;ES;<wide> 002F;;;;N;FULLWIDTH SLASH;;;;
+FF10;FULLWIDTH DIGIT ZERO;Nd;0;EN;<wide> 0030;0;0;0;N;;;;;
+FF11;FULLWIDTH DIGIT ONE;Nd;0;EN;<wide> 0031;1;1;1;N;;;;;
+FF12;FULLWIDTH DIGIT TWO;Nd;0;EN;<wide> 0032;2;2;2;N;;;;;
+FF13;FULLWIDTH DIGIT THREE;Nd;0;EN;<wide> 0033;3;3;3;N;;;;;
+FF14;FULLWIDTH DIGIT FOUR;Nd;0;EN;<wide> 0034;4;4;4;N;;;;;
+FF15;FULLWIDTH DIGIT FIVE;Nd;0;EN;<wide> 0035;5;5;5;N;;;;;
+FF16;FULLWIDTH DIGIT SIX;Nd;0;EN;<wide> 0036;6;6;6;N;;;;;
+FF17;FULLWIDTH DIGIT SEVEN;Nd;0;EN;<wide> 0037;7;7;7;N;;;;;
+FF18;FULLWIDTH DIGIT EIGHT;Nd;0;EN;<wide> 0038;8;8;8;N;;;;;
+FF19;FULLWIDTH DIGIT NINE;Nd;0;EN;<wide> 0039;9;9;9;N;;;;;
+FF1A;FULLWIDTH COLON;Po;0;CS;<wide> 003A;;;;N;;;;;
+FF1B;FULLWIDTH SEMICOLON;Po;0;ON;<wide> 003B;;;;N;;;;;
+FF1C;FULLWIDTH LESS-THAN SIGN;Sm;0;ON;<wide> 003C;;;;N;;;;;
+FF1D;FULLWIDTH EQUALS SIGN;Sm;0;ON;<wide> 003D;;;;N;;;;;
+FF1E;FULLWIDTH GREATER-THAN SIGN;Sm;0;ON;<wide> 003E;;;;N;;;;;
+FF1F;FULLWIDTH QUESTION MARK;Po;0;ON;<wide> 003F;;;;N;;;;;
+FF20;FULLWIDTH COMMERCIAL AT;Po;0;ON;<wide> 0040;;;;N;;;;;
+FF21;FULLWIDTH LATIN CAPITAL LETTER A;Lu;0;L;<wide> 0041;;;;N;;;;FF41;
+FF22;FULLWIDTH LATIN CAPITAL LETTER B;Lu;0;L;<wide> 0042;;;;N;;;;FF42;
+FF23;FULLWIDTH LATIN CAPITAL LETTER C;Lu;0;L;<wide> 0043;;;;N;;;;FF43;
+FF24;FULLWIDTH LATIN CAPITAL LETTER D;Lu;0;L;<wide> 0044;;;;N;;;;FF44;
+FF25;FULLWIDTH LATIN CAPITAL LETTER E;Lu;0;L;<wide> 0045;;;;N;;;;FF45;
+FF26;FULLWIDTH LATIN CAPITAL LETTER F;Lu;0;L;<wide> 0046;;;;N;;;;FF46;
+FF27;FULLWIDTH LATIN CAPITAL LETTER G;Lu;0;L;<wide> 0047;;;;N;;;;FF47;
+FF28;FULLWIDTH LATIN CAPITAL LETTER H;Lu;0;L;<wide> 0048;;;;N;;;;FF48;
+FF29;FULLWIDTH LATIN CAPITAL LETTER I;Lu;0;L;<wide> 0049;;;;N;;;;FF49;
+FF2A;FULLWIDTH LATIN CAPITAL LETTER J;Lu;0;L;<wide> 004A;;;;N;;;;FF4A;
+FF2B;FULLWIDTH LATIN CAPITAL LETTER K;Lu;0;L;<wide> 004B;;;;N;;;;FF4B;
+FF2C;FULLWIDTH LATIN CAPITAL LETTER L;Lu;0;L;<wide> 004C;;;;N;;;;FF4C;
+FF2D;FULLWIDTH LATIN CAPITAL LETTER M;Lu;0;L;<wide> 004D;;;;N;;;;FF4D;
+FF2E;FULLWIDTH LATIN CAPITAL LETTER N;Lu;0;L;<wide> 004E;;;;N;;;;FF4E;
+FF2F;FULLWIDTH LATIN CAPITAL LETTER O;Lu;0;L;<wide> 004F;;;;N;;;;FF4F;
+FF30;FULLWIDTH LATIN CAPITAL LETTER P;Lu;0;L;<wide> 0050;;;;N;;;;FF50;
+FF31;FULLWIDTH LATIN CAPITAL LETTER Q;Lu;0;L;<wide> 0051;;;;N;;;;FF51;
+FF32;FULLWIDTH LATIN CAPITAL LETTER R;Lu;0;L;<wide> 0052;;;;N;;;;FF52;
+FF33;FULLWIDTH LATIN CAPITAL LETTER S;Lu;0;L;<wide> 0053;;;;N;;;;FF53;
+FF34;FULLWIDTH LATIN CAPITAL LETTER T;Lu;0;L;<wide> 0054;;;;N;;;;FF54;
+FF35;FULLWIDTH LATIN CAPITAL LETTER U;Lu;0;L;<wide> 0055;;;;N;;;;FF55;
+FF36;FULLWIDTH LATIN CAPITAL LETTER V;Lu;0;L;<wide> 0056;;;;N;;;;FF56;
+FF37;FULLWIDTH LATIN CAPITAL LETTER W;Lu;0;L;<wide> 0057;;;;N;;;;FF57;
+FF38;FULLWIDTH LATIN CAPITAL LETTER X;Lu;0;L;<wide> 0058;;;;N;;;;FF58;
+FF39;FULLWIDTH LATIN CAPITAL LETTER Y;Lu;0;L;<wide> 0059;;;;N;;;;FF59;
+FF3A;FULLWIDTH LATIN CAPITAL LETTER Z;Lu;0;L;<wide> 005A;;;;N;;;;FF5A;
+FF3B;FULLWIDTH LEFT SQUARE BRACKET;Ps;0;ON;<wide> 005B;;;;N;FULLWIDTH OPENING SQUARE BRACKET;;;;
+FF3C;FULLWIDTH REVERSE SOLIDUS;Po;0;ON;<wide> 005C;;;;N;FULLWIDTH BACKSLASH;;;;
+FF3D;FULLWIDTH RIGHT SQUARE BRACKET;Pe;0;ON;<wide> 005D;;;;N;FULLWIDTH CLOSING SQUARE BRACKET;;;;
+FF3E;FULLWIDTH CIRCUMFLEX ACCENT;Sk;0;ON;<wide> 005E;;;;N;FULLWIDTH SPACING CIRCUMFLEX;;;;
+FF3F;FULLWIDTH LOW LINE;Pc;0;ON;<wide> 005F;;;;N;FULLWIDTH SPACING UNDERSCORE;;;;
+FF40;FULLWIDTH GRAVE ACCENT;Sk;0;ON;<wide> 0060;;;;N;FULLWIDTH SPACING GRAVE;;;;
+FF41;FULLWIDTH LATIN SMALL LETTER A;Ll;0;L;<wide> 0061;;;;N;;;FF21;;FF21
+FF42;FULLWIDTH LATIN SMALL LETTER B;Ll;0;L;<wide> 0062;;;;N;;;FF22;;FF22
+FF43;FULLWIDTH LATIN SMALL LETTER C;Ll;0;L;<wide> 0063;;;;N;;;FF23;;FF23
+FF44;FULLWIDTH LATIN SMALL LETTER D;Ll;0;L;<wide> 0064;;;;N;;;FF24;;FF24
+FF45;FULLWIDTH LATIN SMALL LETTER E;Ll;0;L;<wide> 0065;;;;N;;;FF25;;FF25
+FF46;FULLWIDTH LATIN SMALL LETTER F;Ll;0;L;<wide> 0066;;;;N;;;FF26;;FF26
+FF47;FULLWIDTH LATIN SMALL LETTER G;Ll;0;L;<wide> 0067;;;;N;;;FF27;;FF27
+FF48;FULLWIDTH LATIN SMALL LETTER H;Ll;0;L;<wide> 0068;;;;N;;;FF28;;FF28
+FF49;FULLWIDTH LATIN SMALL LETTER I;Ll;0;L;<wide> 0069;;;;N;;;FF29;;FF29
+FF4A;FULLWIDTH LATIN SMALL LETTER J;Ll;0;L;<wide> 006A;;;;N;;;FF2A;;FF2A
+FF4B;FULLWIDTH LATIN SMALL LETTER K;Ll;0;L;<wide> 006B;;;;N;;;FF2B;;FF2B
+FF4C;FULLWIDTH LATIN SMALL LETTER L;Ll;0;L;<wide> 006C;;;;N;;;FF2C;;FF2C
+FF4D;FULLWIDTH LATIN SMALL LETTER M;Ll;0;L;<wide> 006D;;;;N;;;FF2D;;FF2D
+FF4E;FULLWIDTH LATIN SMALL LETTER N;Ll;0;L;<wide> 006E;;;;N;;;FF2E;;FF2E
+FF4F;FULLWIDTH LATIN SMALL LETTER O;Ll;0;L;<wide> 006F;;;;N;;;FF2F;;FF2F
+FF50;FULLWIDTH LATIN SMALL LETTER P;Ll;0;L;<wide> 0070;;;;N;;;FF30;;FF30
+FF51;FULLWIDTH LATIN SMALL LETTER Q;Ll;0;L;<wide> 0071;;;;N;;;FF31;;FF31
+FF52;FULLWIDTH LATIN SMALL LETTER R;Ll;0;L;<wide> 0072;;;;N;;;FF32;;FF32
+FF53;FULLWIDTH LATIN SMALL LETTER S;Ll;0;L;<wide> 0073;;;;N;;;FF33;;FF33
+FF54;FULLWIDTH LATIN SMALL LETTER T;Ll;0;L;<wide> 0074;;;;N;;;FF34;;FF34
+FF55;FULLWIDTH LATIN SMALL LETTER U;Ll;0;L;<wide> 0075;;;;N;;;FF35;;FF35
+FF56;FULLWIDTH LATIN SMALL LETTER V;Ll;0;L;<wide> 0076;;;;N;;;FF36;;FF36
+FF57;FULLWIDTH LATIN SMALL LETTER W;Ll;0;L;<wide> 0077;;;;N;;;FF37;;FF37
+FF58;FULLWIDTH LATIN SMALL LETTER X;Ll;0;L;<wide> 0078;;;;N;;;FF38;;FF38
+FF59;FULLWIDTH LATIN SMALL LETTER Y;Ll;0;L;<wide> 0079;;;;N;;;FF39;;FF39
+FF5A;FULLWIDTH LATIN SMALL LETTER Z;Ll;0;L;<wide> 007A;;;;N;;;FF3A;;FF3A
+FF5B;FULLWIDTH LEFT CURLY BRACKET;Ps;0;ON;<wide> 007B;;;;N;FULLWIDTH OPENING CURLY BRACKET;;;;
+FF5C;FULLWIDTH VERTICAL LINE;Sm;0;ON;<wide> 007C;;;;N;FULLWIDTH VERTICAL BAR;;;;
+FF5D;FULLWIDTH RIGHT CURLY BRACKET;Pe;0;ON;<wide> 007D;;;;N;FULLWIDTH CLOSING CURLY BRACKET;;;;
+FF5E;FULLWIDTH TILDE;Sm;0;ON;<wide> 007E;;;;N;FULLWIDTH SPACING TILDE;;;;
+FF61;HALFWIDTH IDEOGRAPHIC FULL STOP;Po;0;ON;<narrow> 3002;;;;N;HALFWIDTH IDEOGRAPHIC PERIOD;;;;
+FF62;HALFWIDTH LEFT CORNER BRACKET;Ps;0;ON;<narrow> 300C;;;;N;HALFWIDTH OPENING CORNER BRACKET;;;;
+FF63;HALFWIDTH RIGHT CORNER BRACKET;Pe;0;ON;<narrow> 300D;;;;N;HALFWIDTH CLOSING CORNER BRACKET;;;;
+FF64;HALFWIDTH IDEOGRAPHIC COMMA;Po;0;ON;<narrow> 3001;;;;N;;;;;
+FF65;HALFWIDTH KATAKANA MIDDLE DOT;Pc;0;ON;<narrow> 30FB;;;;N;;;;;
+FF66;HALFWIDTH KATAKANA LETTER WO;Lo;0;L;<narrow> 30F2;;;;N;;;;;
+FF67;HALFWIDTH KATAKANA LETTER SMALL A;Lo;0;L;<narrow> 30A1;;;;N;;;;;
+FF68;HALFWIDTH KATAKANA LETTER SMALL I;Lo;0;L;<narrow> 30A3;;;;N;;;;;
+FF69;HALFWIDTH KATAKANA LETTER SMALL U;Lo;0;L;<narrow> 30A5;;;;N;;;;;
+FF6A;HALFWIDTH KATAKANA LETTER SMALL E;Lo;0;L;<narrow> 30A7;;;;N;;;;;
+FF6B;HALFWIDTH KATAKANA LETTER SMALL O;Lo;0;L;<narrow> 30A9;;;;N;;;;;
+FF6C;HALFWIDTH KATAKANA LETTER SMALL YA;Lo;0;L;<narrow> 30E3;;;;N;;;;;
+FF6D;HALFWIDTH KATAKANA LETTER SMALL YU;Lo;0;L;<narrow> 30E5;;;;N;;;;;
+FF6E;HALFWIDTH KATAKANA LETTER SMALL YO;Lo;0;L;<narrow> 30E7;;;;N;;;;;
+FF6F;HALFWIDTH KATAKANA LETTER SMALL TU;Lo;0;L;<narrow> 30C3;;;;N;;;;;
+FF70;HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;<narrow> 30FC;;;;N;;;;;
+FF71;HALFWIDTH KATAKANA LETTER A;Lo;0;L;<narrow> 30A2;;;;N;;;;;
+FF72;HALFWIDTH KATAKANA LETTER I;Lo;0;L;<narrow> 30A4;;;;N;;;;;
+FF73;HALFWIDTH KATAKANA LETTER U;Lo;0;L;<narrow> 30A6;;;;N;;;;;
+FF74;HALFWIDTH KATAKANA LETTER E;Lo;0;L;<narrow> 30A8;;;;N;;;;;
+FF75;HALFWIDTH KATAKANA LETTER O;Lo;0;L;<narrow> 30AA;;;;N;;;;;
+FF76;HALFWIDTH KATAKANA LETTER KA;Lo;0;L;<narrow> 30AB;;;;N;;;;;
+FF77;HALFWIDTH KATAKANA LETTER KI;Lo;0;L;<narrow> 30AD;;;;N;;;;;
+FF78;HALFWIDTH KATAKANA LETTER KU;Lo;0;L;<narrow> 30AF;;;;N;;;;;
+FF79;HALFWIDTH KATAKANA LETTER KE;Lo;0;L;<narrow> 30B1;;;;N;;;;;
+FF7A;HALFWIDTH KATAKANA LETTER KO;Lo;0;L;<narrow> 30B3;;;;N;;;;;
+FF7B;HALFWIDTH KATAKANA LETTER SA;Lo;0;L;<narrow> 30B5;;;;N;;;;;
+FF7C;HALFWIDTH KATAKANA LETTER SI;Lo;0;L;<narrow> 30B7;;;;N;;;;;
+FF7D;HALFWIDTH KATAKANA LETTER SU;Lo;0;L;<narrow> 30B9;;;;N;;;;;
+FF7E;HALFWIDTH KATAKANA LETTER SE;Lo;0;L;<narrow> 30BB;;;;N;;;;;
+FF7F;HALFWIDTH KATAKANA LETTER SO;Lo;0;L;<narrow> 30BD;;;;N;;;;;
+FF80;HALFWIDTH KATAKANA LETTER TA;Lo;0;L;<narrow> 30BF;;;;N;;;;;
+FF81;HALFWIDTH KATAKANA LETTER TI;Lo;0;L;<narrow> 30C1;;;;N;;;;;
+FF82;HALFWIDTH KATAKANA LETTER TU;Lo;0;L;<narrow> 30C4;;;;N;;;;;
+FF83;HALFWIDTH KATAKANA LETTER TE;Lo;0;L;<narrow> 30C6;;;;N;;;;;
+FF84;HALFWIDTH KATAKANA LETTER TO;Lo;0;L;<narrow> 30C8;;;;N;;;;;
+FF85;HALFWIDTH KATAKANA LETTER NA;Lo;0;L;<narrow> 30CA;;;;N;;;;;
+FF86;HALFWIDTH KATAKANA LETTER NI;Lo;0;L;<narrow> 30CB;;;;N;;;;;
+FF87;HALFWIDTH KATAKANA LETTER NU;Lo;0;L;<narrow> 30CC;;;;N;;;;;
+FF88;HALFWIDTH KATAKANA LETTER NE;Lo;0;L;<narrow> 30CD;;;;N;;;;;
+FF89;HALFWIDTH KATAKANA LETTER NO;Lo;0;L;<narrow> 30CE;;;;N;;;;;
+FF8A;HALFWIDTH KATAKANA LETTER HA;Lo;0;L;<narrow> 30CF;;;;N;;;;;
+FF8B;HALFWIDTH KATAKANA LETTER HI;Lo;0;L;<narrow> 30D2;;;;N;;;;;
+FF8C;HALFWIDTH KATAKANA LETTER HU;Lo;0;L;<narrow> 30D5;;;;N;;;;;
+FF8D;HALFWIDTH KATAKANA LETTER HE;Lo;0;L;<narrow> 30D8;;;;N;;;;;
+FF8E;HALFWIDTH KATAKANA LETTER HO;Lo;0;L;<narrow> 30DB;;;;N;;;;;
+FF8F;HALFWIDTH KATAKANA LETTER MA;Lo;0;L;<narrow> 30DE;;;;N;;;;;
+FF90;HALFWIDTH KATAKANA LETTER MI;Lo;0;L;<narrow> 30DF;;;;N;;;;;
+FF91;HALFWIDTH KATAKANA LETTER MU;Lo;0;L;<narrow> 30E0;;;;N;;;;;
+FF92;HALFWIDTH KATAKANA LETTER ME;Lo;0;L;<narrow> 30E1;;;;N;;;;;
+FF93;HALFWIDTH KATAKANA LETTER MO;Lo;0;L;<narrow> 30E2;;;;N;;;;;
+FF94;HALFWIDTH KATAKANA LETTER YA;Lo;0;L;<narrow> 30E4;;;;N;;;;;
+FF95;HALFWIDTH KATAKANA LETTER YU;Lo;0;L;<narrow> 30E6;;;;N;;;;;
+FF96;HALFWIDTH KATAKANA LETTER YO;Lo;0;L;<narrow> 30E8;;;;N;;;;;
+FF97;HALFWIDTH KATAKANA LETTER RA;Lo;0;L;<narrow> 30E9;;;;N;;;;;
+FF98;HALFWIDTH KATAKANA LETTER RI;Lo;0;L;<narrow> 30EA;;;;N;;;;;
+FF99;HALFWIDTH KATAKANA LETTER RU;Lo;0;L;<narrow> 30EB;;;;N;;;;;
+FF9A;HALFWIDTH KATAKANA LETTER RE;Lo;0;L;<narrow> 30EC;;;;N;;;;;
+FF9B;HALFWIDTH KATAKANA LETTER RO;Lo;0;L;<narrow> 30ED;;;;N;;;;;
+FF9C;HALFWIDTH KATAKANA LETTER WA;Lo;0;L;<narrow> 30EF;;;;N;;;;;
+FF9D;HALFWIDTH KATAKANA LETTER N;Lo;0;L;<narrow> 30F3;;;;N;;;;;
+FF9E;HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L;<narrow> 3099;;;;N;;halfwidth katakana-hiragana voiced sound mark;;;
+FF9F;HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L;<narrow> 309A;;;;N;;halfwidth katakana-hiragana semi-voiced sound mark;;;
+FFA0;HALFWIDTH HANGUL FILLER;Lo;0;L;<narrow> 3164;;;;N;HALFWIDTH HANGUL CAE OM;;;;
+FFA1;HALFWIDTH HANGUL LETTER KIYEOK;Lo;0;L;<narrow> 3131;;;;N;HALFWIDTH HANGUL LETTER GIYEOG;;;;
+FFA2;HALFWIDTH HANGUL LETTER SSANGKIYEOK;Lo;0;L;<narrow> 3132;;;;N;HALFWIDTH HANGUL LETTER SSANG GIYEOG;;;;
+FFA3;HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<narrow> 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;;
+FFA4;HALFWIDTH HANGUL LETTER NIEUN;Lo;0;L;<narrow> 3134;;;;N;;;;;
+FFA5;HALFWIDTH HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<narrow> 3135;;;;N;HALFWIDTH HANGUL LETTER NIEUN JIEUJ;;;;
+FFA6;HALFWIDTH HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<narrow> 3136;;;;N;HALFWIDTH HANGUL LETTER NIEUN HIEUH;;;;
+FFA7;HALFWIDTH HANGUL LETTER TIKEUT;Lo;0;L;<narrow> 3137;;;;N;HALFWIDTH HANGUL LETTER DIGEUD;;;;
+FFA8;HALFWIDTH HANGUL LETTER SSANGTIKEUT;Lo;0;L;<narrow> 3138;;;;N;HALFWIDTH HANGUL LETTER SSANG DIGEUD;;;;
+FFA9;HALFWIDTH HANGUL LETTER RIEUL;Lo;0;L;<narrow> 3139;;;;N;HALFWIDTH HANGUL LETTER LIEUL;;;;
+FFAA;HALFWIDTH HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<narrow> 313A;;;;N;HALFWIDTH HANGUL LETTER LIEUL GIYEOG;;;;
+FFAB;HALFWIDTH HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<narrow> 313B;;;;N;HALFWIDTH HANGUL LETTER LIEUL MIEUM;;;;
+FFAC;HALFWIDTH HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<narrow> 313C;;;;N;HALFWIDTH HANGUL LETTER LIEUL BIEUB;;;;
+FFAD;HALFWIDTH HANGUL LETTER RIEUL-SIOS;Lo;0;L;<narrow> 313D;;;;N;HALFWIDTH HANGUL LETTER LIEUL SIOS;;;;
+FFAE;HALFWIDTH HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<narrow> 313E;;;;N;HALFWIDTH HANGUL LETTER LIEUL TIEUT;;;;
+FFAF;HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<narrow> 313F;;;;N;HALFWIDTH HANGUL LETTER LIEUL PIEUP;;;;
+FFB0;HALFWIDTH HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<narrow> 3140;;;;N;HALFWIDTH HANGUL LETTER LIEUL HIEUH;;;;
+FFB1;HALFWIDTH HANGUL LETTER MIEUM;Lo;0;L;<narrow> 3141;;;;N;;;;;
+FFB2;HALFWIDTH HANGUL LETTER PIEUP;Lo;0;L;<narrow> 3142;;;;N;HALFWIDTH HANGUL LETTER BIEUB;;;;
+FFB3;HALFWIDTH HANGUL LETTER SSANGPIEUP;Lo;0;L;<narrow> 3143;;;;N;HALFWIDTH HANGUL LETTER SSANG BIEUB;;;;
+FFB4;HALFWIDTH HANGUL LETTER PIEUP-SIOS;Lo;0;L;<narrow> 3144;;;;N;HALFWIDTH HANGUL LETTER BIEUB SIOS;;;;
+FFB5;HALFWIDTH HANGUL LETTER SIOS;Lo;0;L;<narrow> 3145;;;;N;;;;;
+FFB6;HALFWIDTH HANGUL LETTER SSANGSIOS;Lo;0;L;<narrow> 3146;;;;N;HALFWIDTH HANGUL LETTER SSANG SIOS;;;;
+FFB7;HALFWIDTH HANGUL LETTER IEUNG;Lo;0;L;<narrow> 3147;;;;N;;;;;
+FFB8;HALFWIDTH HANGUL LETTER CIEUC;Lo;0;L;<narrow> 3148;;;;N;HALFWIDTH HANGUL LETTER JIEUJ;;;;
+FFB9;HALFWIDTH HANGUL LETTER SSANGCIEUC;Lo;0;L;<narrow> 3149;;;;N;HALFWIDTH HANGUL LETTER SSANG JIEUJ;;;;
+FFBA;HALFWIDTH HANGUL LETTER CHIEUCH;Lo;0;L;<narrow> 314A;;;;N;HALFWIDTH HANGUL LETTER CIEUC;;;;
+FFBB;HALFWIDTH HANGUL LETTER KHIEUKH;Lo;0;L;<narrow> 314B;;;;N;HALFWIDTH HANGUL LETTER KIYEOK;;;;
+FFBC;HALFWIDTH HANGUL LETTER THIEUTH;Lo;0;L;<narrow> 314C;;;;N;HALFWIDTH HANGUL LETTER TIEUT;;;;
+FFBD;HALFWIDTH HANGUL LETTER PHIEUPH;Lo;0;L;<narrow> 314D;;;;N;HALFWIDTH HANGUL LETTER PIEUP;;;;
+FFBE;HALFWIDTH HANGUL LETTER HIEUH;Lo;0;L;<narrow> 314E;;;;N;;;;;
+FFC2;HALFWIDTH HANGUL LETTER A;Lo;0;L;<narrow> 314F;;;;N;;;;;
+FFC3;HALFWIDTH HANGUL LETTER AE;Lo;0;L;<narrow> 3150;;;;N;;;;;
+FFC4;HALFWIDTH HANGUL LETTER YA;Lo;0;L;<narrow> 3151;;;;N;;;;;
+FFC5;HALFWIDTH HANGUL LETTER YAE;Lo;0;L;<narrow> 3152;;;;N;;;;;
+FFC6;HALFWIDTH HANGUL LETTER EO;Lo;0;L;<narrow> 3153;;;;N;;;;;
+FFC7;HALFWIDTH HANGUL LETTER E;Lo;0;L;<narrow> 3154;;;;N;;;;;
+FFCA;HALFWIDTH HANGUL LETTER YEO;Lo;0;L;<narrow> 3155;;;;N;;;;;
+FFCB;HALFWIDTH HANGUL LETTER YE;Lo;0;L;<narrow> 3156;;;;N;;;;;
+FFCC;HALFWIDTH HANGUL LETTER O;Lo;0;L;<narrow> 3157;;;;N;;;;;
+FFCD;HALFWIDTH HANGUL LETTER WA;Lo;0;L;<narrow> 3158;;;;N;;;;;
+FFCE;HALFWIDTH HANGUL LETTER WAE;Lo;0;L;<narrow> 3159;;;;N;;;;;
+FFCF;HALFWIDTH HANGUL LETTER OE;Lo;0;L;<narrow> 315A;;;;N;;;;;
+FFD2;HALFWIDTH HANGUL LETTER YO;Lo;0;L;<narrow> 315B;;;;N;;;;;
+FFD3;HALFWIDTH HANGUL LETTER U;Lo;0;L;<narrow> 315C;;;;N;;;;;
+FFD4;HALFWIDTH HANGUL LETTER WEO;Lo;0;L;<narrow> 315D;;;;N;;;;;
+FFD5;HALFWIDTH HANGUL LETTER WE;Lo;0;L;<narrow> 315E;;;;N;;;;;
+FFD6;HALFWIDTH HANGUL LETTER WI;Lo;0;L;<narrow> 315F;;;;N;;;;;
+FFD7;HALFWIDTH HANGUL LETTER YU;Lo;0;L;<narrow> 3160;;;;N;;;;;
+FFDA;HALFWIDTH HANGUL LETTER EU;Lo;0;L;<narrow> 3161;;;;N;;;;;
+FFDB;HALFWIDTH HANGUL LETTER YI;Lo;0;L;<narrow> 3162;;;;N;;;;;
+FFDC;HALFWIDTH HANGUL LETTER I;Lo;0;L;<narrow> 3163;;;;N;;;;;
+FFE0;FULLWIDTH CENT SIGN;Sc;0;ET;<wide> 00A2;;;;N;;;;;
+FFE1;FULLWIDTH POUND SIGN;Sc;0;ET;<wide> 00A3;;;;N;;;;;
+FFE2;FULLWIDTH NOT SIGN;Sm;0;ON;<wide> 00AC;;;;N;;;;;
+FFE3;FULLWIDTH MACRON;Sk;0;ON;<wide> 00AF;;;;N;FULLWIDTH SPACING MACRON;*;;;
+FFE4;FULLWIDTH BROKEN BAR;So;0;ON;<wide> 00A6;;;;N;FULLWIDTH BROKEN VERTICAL BAR;;;;
+FFE5;FULLWIDTH YEN SIGN;Sc;0;ET;<wide> 00A5;;;;N;;;;;
+FFE6;FULLWIDTH WON SIGN;Sc;0;ET;<wide> 20A9;;;;N;;;;;
+FFE8;HALFWIDTH FORMS LIGHT VERTICAL;So;0;ON;<narrow> 2502;;;;N;;;;;
+FFE9;HALFWIDTH LEFTWARDS ARROW;Sm;0;ON;<narrow> 2190;;;;N;;;;;
+FFEA;HALFWIDTH UPWARDS ARROW;Sm;0;ON;<narrow> 2191;;;;N;;;;;
+FFEB;HALFWIDTH RIGHTWARDS ARROW;Sm;0;ON;<narrow> 2192;;;;N;;;;;
+FFEC;HALFWIDTH DOWNWARDS ARROW;Sm;0;ON;<narrow> 2193;;;;N;;;;;
+FFED;HALFWIDTH BLACK SQUARE;So;0;ON;<narrow> 25A0;;;;N;;;;;
+FFEE;HALFWIDTH WHITE CIRCLE;So;0;ON;<narrow> 25CB;;;;N;;;;;
+FFF9;INTERLINEAR ANNOTATION ANCHOR;Cf;0;BN;;;;;N;;;;;
+FFFA;INTERLINEAR ANNOTATION SEPARATOR;Cf;0;BN;;;;;N;;;;;
+FFFB;INTERLINEAR ANNOTATION TERMINATOR;Cf;0;BN;;;;;N;;;;;
+FFFC;OBJECT REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
+FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
+10300;OLD ITALIC LETTER A;Lo;0;L;;;;;N;;;;;
+10301;OLD ITALIC LETTER BE;Lo;0;L;;;;;N;;;;;
+10302;OLD ITALIC LETTER KE;Lo;0;L;;;;;N;;;;;
+10303;OLD ITALIC LETTER DE;Lo;0;L;;;;;N;;;;;
+10304;OLD ITALIC LETTER E;Lo;0;L;;;;;N;;;;;
+10305;OLD ITALIC LETTER VE;Lo;0;L;;;;;N;;;;;
+10306;OLD ITALIC LETTER ZE;Lo;0;L;;;;;N;;;;;
+10307;OLD ITALIC LETTER HE;Lo;0;L;;;;;N;;;;;
+10308;OLD ITALIC LETTER THE;Lo;0;L;;;;;N;;;;;
+10309;OLD ITALIC LETTER I;Lo;0;L;;;;;N;;;;;
+1030A;OLD ITALIC LETTER KA;Lo;0;L;;;;;N;;;;;
+1030B;OLD ITALIC LETTER EL;Lo;0;L;;;;;N;;;;;
+1030C;OLD ITALIC LETTER EM;Lo;0;L;;;;;N;;;;;
+1030D;OLD ITALIC LETTER EN;Lo;0;L;;;;;N;;;;;
+1030E;OLD ITALIC LETTER ESH;Lo;0;L;;;;;N;;;;;
+1030F;OLD ITALIC LETTER O;Lo;0;L;;;;;N;;Faliscan;;;
+10310;OLD ITALIC LETTER PE;Lo;0;L;;;;;N;;;;;
+10311;OLD ITALIC LETTER SHE;Lo;0;L;;;;;N;;;;;
+10312;OLD ITALIC LETTER KU;Lo;0;L;;;;;N;;;;;
+10313;OLD ITALIC LETTER ER;Lo;0;L;;;;;N;;;;;
+10314;OLD ITALIC LETTER ES;Lo;0;L;;;;;N;;;;;
+10315;OLD ITALIC LETTER TE;Lo;0;L;;;;;N;;;;;
+10316;OLD ITALIC LETTER U;Lo;0;L;;;;;N;;;;;
+10317;OLD ITALIC LETTER EKS;Lo;0;L;;;;;N;;Faliscan;;;
+10318;OLD ITALIC LETTER PHE;Lo;0;L;;;;;N;;;;;
+10319;OLD ITALIC LETTER KHE;Lo;0;L;;;;;N;;;;;
+1031A;OLD ITALIC LETTER EF;Lo;0;L;;;;;N;;;;;
+1031B;OLD ITALIC LETTER ERS;Lo;0;L;;;;;N;;Umbrian;;;
+1031C;OLD ITALIC LETTER CHE;Lo;0;L;;;;;N;;Umbrian;;;
+1031D;OLD ITALIC LETTER II;Lo;0;L;;;;;N;;Oscan;;;
+1031E;OLD ITALIC LETTER UU;Lo;0;L;;;;;N;;Oscan;;;
+10320;OLD ITALIC NUMERAL ONE;No;0;L;;;;1;N;;;;;
+10321;OLD ITALIC NUMERAL FIVE;No;0;L;;;;5;N;;;;;
+10322;OLD ITALIC NUMERAL TEN;No;0;L;;;;10;N;;;;;
+10323;OLD ITALIC NUMERAL FIFTY;No;0;L;;;;50;N;;;;;
+10330;GOTHIC LETTER AHSA;Lo;0;L;;;;;N;;;;;
+10331;GOTHIC LETTER BAIRKAN;Lo;0;L;;;;;N;;;;;
+10332;GOTHIC LETTER GIBA;Lo;0;L;;;;;N;;;;;
+10333;GOTHIC LETTER DAGS;Lo;0;L;;;;;N;;;;;
+10334;GOTHIC LETTER AIHVUS;Lo;0;L;;;;;N;;;;;
+10335;GOTHIC LETTER QAIRTHRA;Lo;0;L;;;;;N;;;;;
+10336;GOTHIC LETTER IUJA;Lo;0;L;;;;;N;;;;;
+10337;GOTHIC LETTER HAGL;Lo;0;L;;;;;N;;;;;
+10338;GOTHIC LETTER THIUTH;Lo;0;L;;;;;N;;;;;
+10339;GOTHIC LETTER EIS;Lo;0;L;;;;;N;;;;;
+1033A;GOTHIC LETTER KUSMA;Lo;0;L;;;;;N;;;;;
+1033B;GOTHIC LETTER LAGUS;Lo;0;L;;;;;N;;;;;
+1033C;GOTHIC LETTER MANNA;Lo;0;L;;;;;N;;;;;
+1033D;GOTHIC LETTER NAUTHS;Lo;0;L;;;;;N;;;;;
+1033E;GOTHIC LETTER JER;Lo;0;L;;;;;N;;;;;
+1033F;GOTHIC LETTER URUS;Lo;0;L;;;;;N;;;;;
+10340;GOTHIC LETTER PAIRTHRA;Lo;0;L;;;;;N;;;;;
+10341;GOTHIC LETTER NINETY;Lo;0;L;;;;;N;;;;;
+10342;GOTHIC LETTER RAIDA;Lo;0;L;;;;;N;;;;;
+10343;GOTHIC LETTER SAUIL;Lo;0;L;;;;;N;;;;;
+10344;GOTHIC LETTER TEIWS;Lo;0;L;;;;;N;;;;;
+10345;GOTHIC LETTER WINJA;Lo;0;L;;;;;N;;;;;
+10346;GOTHIC LETTER FAIHU;Lo;0;L;;;;;N;;;;;
+10347;GOTHIC LETTER IGGWS;Lo;0;L;;;;;N;;;;;
+10348;GOTHIC LETTER HWAIR;Lo;0;L;;;;;N;;;;;
+10349;GOTHIC LETTER OTHAL;Lo;0;L;;;;;N;;;;;
+1034A;GOTHIC LETTER NINE HUNDRED;Nl;0;L;;;;;N;;;;;
+10400;DESERET CAPITAL LETTER LONG I;Lu;0;L;;;;;N;;;;10428;
+10401;DESERET CAPITAL LETTER LONG E;Lu;0;L;;;;;N;;;;10429;
+10402;DESERET CAPITAL LETTER LONG A;Lu;0;L;;;;;N;;;;1042A;
+10403;DESERET CAPITAL LETTER LONG AH;Lu;0;L;;;;;N;;;;1042B;
+10404;DESERET CAPITAL LETTER LONG O;Lu;0;L;;;;;N;;;;1042C;
+10405;DESERET CAPITAL LETTER LONG OO;Lu;0;L;;;;;N;;;;1042D;
+10406;DESERET CAPITAL LETTER SHORT I;Lu;0;L;;;;;N;;;;1042E;
+10407;DESERET CAPITAL LETTER SHORT E;Lu;0;L;;;;;N;;;;1042F;
+10408;DESERET CAPITAL LETTER SHORT A;Lu;0;L;;;;;N;;;;10430;
+10409;DESERET CAPITAL LETTER SHORT AH;Lu;0;L;;;;;N;;;;10431;
+1040A;DESERET CAPITAL LETTER SHORT O;Lu;0;L;;;;;N;;;;10432;
+1040B;DESERET CAPITAL LETTER SHORT OO;Lu;0;L;;;;;N;;;;10433;
+1040C;DESERET CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;10434;
+1040D;DESERET CAPITAL LETTER OW;Lu;0;L;;;;;N;;;;10435;
+1040E;DESERET CAPITAL LETTER WU;Lu;0;L;;;;;N;;;;10436;
+1040F;DESERET CAPITAL LETTER YEE;Lu;0;L;;;;;N;;;;10437;
+10410;DESERET CAPITAL LETTER H;Lu;0;L;;;;;N;;;;10438;
+10411;DESERET CAPITAL LETTER PEE;Lu;0;L;;;;;N;;;;10439;
+10412;DESERET CAPITAL LETTER BEE;Lu;0;L;;;;;N;;;;1043A;
+10413;DESERET CAPITAL LETTER TEE;Lu;0;L;;;;;N;;;;1043B;
+10414;DESERET CAPITAL LETTER DEE;Lu;0;L;;;;;N;;;;1043C;
+10415;DESERET CAPITAL LETTER CHEE;Lu;0;L;;;;;N;;;;1043D;
+10416;DESERET CAPITAL LETTER JEE;Lu;0;L;;;;;N;;;;1043E;
+10417;DESERET CAPITAL LETTER KAY;Lu;0;L;;;;;N;;;;1043F;
+10418;DESERET CAPITAL LETTER GAY;Lu;0;L;;;;;N;;;;10440;
+10419;DESERET CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;10441;
+1041A;DESERET CAPITAL LETTER VEE;Lu;0;L;;;;;N;;;;10442;
+1041B;DESERET CAPITAL LETTER ETH;Lu;0;L;;;;;N;;;;10443;
+1041C;DESERET CAPITAL LETTER THEE;Lu;0;L;;;;;N;;;;10444;
+1041D;DESERET CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;10445;
+1041E;DESERET CAPITAL LETTER ZEE;Lu;0;L;;;;;N;;;;10446;
+1041F;DESERET CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;10447;
+10420;DESERET CAPITAL LETTER ZHEE;Lu;0;L;;;;;N;;;;10448;
+10421;DESERET CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;10449;
+10422;DESERET CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;1044A;
+10423;DESERET CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;1044B;
+10424;DESERET CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;1044C;
+10425;DESERET CAPITAL LETTER ENG;Lu;0;L;;;;;N;;;;1044D;
+10428;DESERET SMALL LETTER LONG I;Ll;0;L;;;;;N;;;10400;;10400
+10429;DESERET SMALL LETTER LONG E;Ll;0;L;;;;;N;;;10401;;10401
+1042A;DESERET SMALL LETTER LONG A;Ll;0;L;;;;;N;;;10402;;10402
+1042B;DESERET SMALL LETTER LONG AH;Ll;0;L;;;;;N;;;10403;;10403
+1042C;DESERET SMALL LETTER LONG O;Ll;0;L;;;;;N;;;10404;;10404
+1042D;DESERET SMALL LETTER LONG OO;Ll;0;L;;;;;N;;;10405;;10405
+1042E;DESERET SMALL LETTER SHORT I;Ll;0;L;;;;;N;;;10406;;10406
+1042F;DESERET SMALL LETTER SHORT E;Ll;0;L;;;;;N;;;10407;;10407
+10430;DESERET SMALL LETTER SHORT A;Ll;0;L;;;;;N;;;10408;;10408
+10431;DESERET SMALL LETTER SHORT AH;Ll;0;L;;;;;N;;;10409;;10409
+10432;DESERET SMALL LETTER SHORT O;Ll;0;L;;;;;N;;;1040A;;1040A
+10433;DESERET SMALL LETTER SHORT OO;Ll;0;L;;;;;N;;;1040B;;1040B
+10434;DESERET SMALL LETTER AY;Ll;0;L;;;;;N;;;1040C;;1040C
+10435;DESERET SMALL LETTER OW;Ll;0;L;;;;;N;;;1040D;;1040D
+10436;DESERET SMALL LETTER WU;Ll;0;L;;;;;N;;;1040E;;1040E
+10437;DESERET SMALL LETTER YEE;Ll;0;L;;;;;N;;;1040F;;1040F
+10438;DESERET SMALL LETTER H;Ll;0;L;;;;;N;;;10410;;10410
+10439;DESERET SMALL LETTER PEE;Ll;0;L;;;;;N;;;10411;;10411
+1043A;DESERET SMALL LETTER BEE;Ll;0;L;;;;;N;;;10412;;10412
+1043B;DESERET SMALL LETTER TEE;Ll;0;L;;;;;N;;;10413;;10413
+1043C;DESERET SMALL LETTER DEE;Ll;0;L;;;;;N;;;10414;;10414
+1043D;DESERET SMALL LETTER CHEE;Ll;0;L;;;;;N;;;10415;;10415
+1043E;DESERET SMALL LETTER JEE;Ll;0;L;;;;;N;;;10416;;10416
+1043F;DESERET SMALL LETTER KAY;Ll;0;L;;;;;N;;;10417;;10417
+10440;DESERET SMALL LETTER GAY;Ll;0;L;;;;;N;;;10418;;10418
+10441;DESERET SMALL LETTER EF;Ll;0;L;;;;;N;;;10419;;10419
+10442;DESERET SMALL LETTER VEE;Ll;0;L;;;;;N;;;1041A;;1041A
+10443;DESERET SMALL LETTER ETH;Ll;0;L;;;;;N;;;1041B;;1041B
+10444;DESERET SMALL LETTER THEE;Ll;0;L;;;;;N;;;1041C;;1041C
+10445;DESERET SMALL LETTER ES;Ll;0;L;;;;;N;;;1041D;;1041D
+10446;DESERET SMALL LETTER ZEE;Ll;0;L;;;;;N;;;1041E;;1041E
+10447;DESERET SMALL LETTER ESH;Ll;0;L;;;;;N;;;1041F;;1041F
+10448;DESERET SMALL LETTER ZHEE;Ll;0;L;;;;;N;;;10420;;10420
+10449;DESERET SMALL LETTER ER;Ll;0;L;;;;;N;;;10421;;10421
+1044A;DESERET SMALL LETTER EL;Ll;0;L;;;;;N;;;10422;;10422
+1044B;DESERET SMALL LETTER EM;Ll;0;L;;;;;N;;;10423;;10423
+1044C;DESERET SMALL LETTER EN;Ll;0;L;;;;;N;;;10424;;10424
+1044D;DESERET SMALL LETTER ENG;Ll;0;L;;;;;N;;;10425;;10425
+1D000;BYZANTINE MUSICAL SYMBOL PSILI;So;0;L;;;;;N;;;;;
+1D001;BYZANTINE MUSICAL SYMBOL DASEIA;So;0;L;;;;;N;;;;;
+1D002;BYZANTINE MUSICAL SYMBOL PERISPOMENI;So;0;L;;;;;N;;;;;
+1D003;BYZANTINE MUSICAL SYMBOL OXEIA EKFONITIKON;So;0;L;;;;;N;;;;;
+1D004;BYZANTINE MUSICAL SYMBOL OXEIA DIPLI;So;0;L;;;;;N;;;;;
+1D005;BYZANTINE MUSICAL SYMBOL VAREIA EKFONITIKON;So;0;L;;;;;N;;;;;
+1D006;BYZANTINE MUSICAL SYMBOL VAREIA DIPLI;So;0;L;;;;;N;;;;;
+1D007;BYZANTINE MUSICAL SYMBOL KATHISTI;So;0;L;;;;;N;;;;;
+1D008;BYZANTINE MUSICAL SYMBOL SYRMATIKI;So;0;L;;;;;N;;;;;
+1D009;BYZANTINE MUSICAL SYMBOL PARAKLITIKI;So;0;L;;;;;N;;;;;
+1D00A;BYZANTINE MUSICAL SYMBOL YPOKRISIS;So;0;L;;;;;N;;;;;
+1D00B;BYZANTINE MUSICAL SYMBOL YPOKRISIS DIPLI;So;0;L;;;;;N;;;;;
+1D00C;BYZANTINE MUSICAL SYMBOL KREMASTI;So;0;L;;;;;N;;;;;
+1D00D;BYZANTINE MUSICAL SYMBOL APESO EKFONITIKON;So;0;L;;;;;N;;;;;
+1D00E;BYZANTINE MUSICAL SYMBOL EXO EKFONITIKON;So;0;L;;;;;N;;;;;
+1D00F;BYZANTINE MUSICAL SYMBOL TELEIA;So;0;L;;;;;N;;;;;
+1D010;BYZANTINE MUSICAL SYMBOL KENTIMATA;So;0;L;;;;;N;;;;;
+1D011;BYZANTINE MUSICAL SYMBOL APOSTROFOS;So;0;L;;;;;N;;;;;
+1D012;BYZANTINE MUSICAL SYMBOL APOSTROFOS DIPLI;So;0;L;;;;;N;;;;;
+1D013;BYZANTINE MUSICAL SYMBOL SYNEVMA;So;0;L;;;;;N;;;;;
+1D014;BYZANTINE MUSICAL SYMBOL THITA;So;0;L;;;;;N;;;;;
+1D015;BYZANTINE MUSICAL SYMBOL OLIGON ARCHAION;So;0;L;;;;;N;;;;;
+1D016;BYZANTINE MUSICAL SYMBOL GORGON ARCHAION;So;0;L;;;;;N;;;;;
+1D017;BYZANTINE MUSICAL SYMBOL PSILON;So;0;L;;;;;N;;;;;
+1D018;BYZANTINE MUSICAL SYMBOL CHAMILON;So;0;L;;;;;N;;;;;
+1D019;BYZANTINE MUSICAL SYMBOL VATHY;So;0;L;;;;;N;;;;;
+1D01A;BYZANTINE MUSICAL SYMBOL ISON ARCHAION;So;0;L;;;;;N;;;;;
+1D01B;BYZANTINE MUSICAL SYMBOL KENTIMA ARCHAION;So;0;L;;;;;N;;;;;
+1D01C;BYZANTINE MUSICAL SYMBOL KENTIMATA ARCHAION;So;0;L;;;;;N;;;;;
+1D01D;BYZANTINE MUSICAL SYMBOL SAXIMATA;So;0;L;;;;;N;;;;;
+1D01E;BYZANTINE MUSICAL SYMBOL PARICHON;So;0;L;;;;;N;;;;;
+1D01F;BYZANTINE MUSICAL SYMBOL STAVROS APODEXIA;So;0;L;;;;;N;;;;;
+1D020;BYZANTINE MUSICAL SYMBOL OXEIAI ARCHAION;So;0;L;;;;;N;;;;;
+1D021;BYZANTINE MUSICAL SYMBOL VAREIAI ARCHAION;So;0;L;;;;;N;;;;;
+1D022;BYZANTINE MUSICAL SYMBOL APODERMA ARCHAION;So;0;L;;;;;N;;;;;
+1D023;BYZANTINE MUSICAL SYMBOL APOTHEMA;So;0;L;;;;;N;;;;;
+1D024;BYZANTINE MUSICAL SYMBOL KLASMA;So;0;L;;;;;N;;;;;
+1D025;BYZANTINE MUSICAL SYMBOL REVMA;So;0;L;;;;;N;;;;;
+1D026;BYZANTINE MUSICAL SYMBOL PIASMA ARCHAION;So;0;L;;;;;N;;;;;
+1D027;BYZANTINE MUSICAL SYMBOL TINAGMA;So;0;L;;;;;N;;;;;
+1D028;BYZANTINE MUSICAL SYMBOL ANATRICHISMA;So;0;L;;;;;N;;;;;
+1D029;BYZANTINE MUSICAL SYMBOL SEISMA;So;0;L;;;;;N;;;;;
+1D02A;BYZANTINE MUSICAL SYMBOL SYNAGMA ARCHAION;So;0;L;;;;;N;;;;;
+1D02B;BYZANTINE MUSICAL SYMBOL SYNAGMA META STAVROU;So;0;L;;;;;N;;;;;
+1D02C;BYZANTINE MUSICAL SYMBOL OYRANISMA ARCHAION;So;0;L;;;;;N;;;;;
+1D02D;BYZANTINE MUSICAL SYMBOL THEMA;So;0;L;;;;;N;;;;;
+1D02E;BYZANTINE MUSICAL SYMBOL LEMOI;So;0;L;;;;;N;;;;;
+1D02F;BYZANTINE MUSICAL SYMBOL DYO;So;0;L;;;;;N;;;;;
+1D030;BYZANTINE MUSICAL SYMBOL TRIA;So;0;L;;;;;N;;;;;
+1D031;BYZANTINE MUSICAL SYMBOL TESSERA;So;0;L;;;;;N;;;;;
+1D032;BYZANTINE MUSICAL SYMBOL KRATIMATA;So;0;L;;;;;N;;;;;
+1D033;BYZANTINE MUSICAL SYMBOL APESO EXO NEO;So;0;L;;;;;N;;;;;
+1D034;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION;So;0;L;;;;;N;;;;;
+1D035;BYZANTINE MUSICAL SYMBOL IMIFTHORA;So;0;L;;;;;N;;;;;
+1D036;BYZANTINE MUSICAL SYMBOL TROMIKON ARCHAION;So;0;L;;;;;N;;;;;
+1D037;BYZANTINE MUSICAL SYMBOL KATAVA TROMIKON;So;0;L;;;;;N;;;;;
+1D038;BYZANTINE MUSICAL SYMBOL PELASTON;So;0;L;;;;;N;;;;;
+1D039;BYZANTINE MUSICAL SYMBOL PSIFISTON;So;0;L;;;;;N;;;;;
+1D03A;BYZANTINE MUSICAL SYMBOL KONTEVMA;So;0;L;;;;;N;;;;;
+1D03B;BYZANTINE MUSICAL SYMBOL CHOREVMA ARCHAION;So;0;L;;;;;N;;;;;
+1D03C;BYZANTINE MUSICAL SYMBOL RAPISMA;So;0;L;;;;;N;;;;;
+1D03D;BYZANTINE MUSICAL SYMBOL PARAKALESMA ARCHAION;So;0;L;;;;;N;;;;;
+1D03E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI ARCHAION;So;0;L;;;;;N;;;;;
+1D03F;BYZANTINE MUSICAL SYMBOL ICHADIN;So;0;L;;;;;N;;;;;
+1D040;BYZANTINE MUSICAL SYMBOL NANA;So;0;L;;;;;N;;;;;
+1D041;BYZANTINE MUSICAL SYMBOL PETASMA;So;0;L;;;;;N;;;;;
+1D042;BYZANTINE MUSICAL SYMBOL KONTEVMA ALLO;So;0;L;;;;;N;;;;;
+1D043;BYZANTINE MUSICAL SYMBOL TROMIKON ALLO;So;0;L;;;;;N;;;;;
+1D044;BYZANTINE MUSICAL SYMBOL STRAGGISMATA;So;0;L;;;;;N;;;;;
+1D045;BYZANTINE MUSICAL SYMBOL GRONTHISMATA;So;0;L;;;;;N;;;;;
+1D046;BYZANTINE MUSICAL SYMBOL ISON NEO;So;0;L;;;;;N;;;;;
+1D047;BYZANTINE MUSICAL SYMBOL OLIGON NEO;So;0;L;;;;;N;;;;;
+1D048;BYZANTINE MUSICAL SYMBOL OXEIA NEO;So;0;L;;;;;N;;;;;
+1D049;BYZANTINE MUSICAL SYMBOL PETASTI;So;0;L;;;;;N;;;;;
+1D04A;BYZANTINE MUSICAL SYMBOL KOUFISMA;So;0;L;;;;;N;;;;;
+1D04B;BYZANTINE MUSICAL SYMBOL PETASTOKOUFISMA;So;0;L;;;;;N;;;;;
+1D04C;BYZANTINE MUSICAL SYMBOL KRATIMOKOUFISMA;So;0;L;;;;;N;;;;;
+1D04D;BYZANTINE MUSICAL SYMBOL PELASTON NEO;So;0;L;;;;;N;;;;;
+1D04E;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO ANO;So;0;L;;;;;N;;;;;
+1D04F;BYZANTINE MUSICAL SYMBOL KENTIMA NEO ANO;So;0;L;;;;;N;;;;;
+1D050;BYZANTINE MUSICAL SYMBOL YPSILI;So;0;L;;;;;N;;;;;
+1D051;BYZANTINE MUSICAL SYMBOL APOSTROFOS NEO;So;0;L;;;;;N;;;;;
+1D052;BYZANTINE MUSICAL SYMBOL APOSTROFOI SYNDESMOS NEO;So;0;L;;;;;N;;;;;
+1D053;BYZANTINE MUSICAL SYMBOL YPORROI;So;0;L;;;;;N;;;;;
+1D054;BYZANTINE MUSICAL SYMBOL KRATIMOYPORROON;So;0;L;;;;;N;;;;;
+1D055;BYZANTINE MUSICAL SYMBOL ELAFRON;So;0;L;;;;;N;;;;;
+1D056;BYZANTINE MUSICAL SYMBOL CHAMILI;So;0;L;;;;;N;;;;;
+1D057;BYZANTINE MUSICAL SYMBOL MIKRON ISON;So;0;L;;;;;N;;;;;
+1D058;BYZANTINE MUSICAL SYMBOL VAREIA NEO;So;0;L;;;;;N;;;;;
+1D059;BYZANTINE MUSICAL SYMBOL PIASMA NEO;So;0;L;;;;;N;;;;;
+1D05A;BYZANTINE MUSICAL SYMBOL PSIFISTON NEO;So;0;L;;;;;N;;;;;
+1D05B;BYZANTINE MUSICAL SYMBOL OMALON;So;0;L;;;;;N;;;;;
+1D05C;BYZANTINE MUSICAL SYMBOL ANTIKENOMA;So;0;L;;;;;N;;;;;
+1D05D;BYZANTINE MUSICAL SYMBOL LYGISMA;So;0;L;;;;;N;;;;;
+1D05E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI NEO;So;0;L;;;;;N;;;;;
+1D05F;BYZANTINE MUSICAL SYMBOL PARAKALESMA NEO;So;0;L;;;;;N;;;;;
+1D060;BYZANTINE MUSICAL SYMBOL ETERON PARAKALESMA;So;0;L;;;;;N;;;;;
+1D061;BYZANTINE MUSICAL SYMBOL KYLISMA;So;0;L;;;;;N;;;;;
+1D062;BYZANTINE MUSICAL SYMBOL ANTIKENOKYLISMA;So;0;L;;;;;N;;;;;
+1D063;BYZANTINE MUSICAL SYMBOL TROMIKON NEO;So;0;L;;;;;N;;;;;
+1D064;BYZANTINE MUSICAL SYMBOL EKSTREPTON;So;0;L;;;;;N;;;;;
+1D065;BYZANTINE MUSICAL SYMBOL SYNAGMA NEO;So;0;L;;;;;N;;;;;
+1D066;BYZANTINE MUSICAL SYMBOL SYRMA;So;0;L;;;;;N;;;;;
+1D067;BYZANTINE MUSICAL SYMBOL CHOREVMA NEO;So;0;L;;;;;N;;;;;
+1D068;BYZANTINE MUSICAL SYMBOL EPEGERMA;So;0;L;;;;;N;;;;;
+1D069;BYZANTINE MUSICAL SYMBOL SEISMA NEO;So;0;L;;;;;N;;;;;
+1D06A;BYZANTINE MUSICAL SYMBOL XIRON KLASMA;So;0;L;;;;;N;;;;;
+1D06B;BYZANTINE MUSICAL SYMBOL TROMIKOPSIFISTON;So;0;L;;;;;N;;;;;
+1D06C;BYZANTINE MUSICAL SYMBOL PSIFISTOLYGISMA;So;0;L;;;;;N;;;;;
+1D06D;BYZANTINE MUSICAL SYMBOL TROMIKOLYGISMA;So;0;L;;;;;N;;;;;
+1D06E;BYZANTINE MUSICAL SYMBOL TROMIKOPARAKALESMA;So;0;L;;;;;N;;;;;
+1D06F;BYZANTINE MUSICAL SYMBOL PSIFISTOPARAKALESMA;So;0;L;;;;;N;;;;;
+1D070;BYZANTINE MUSICAL SYMBOL TROMIKOSYNAGMA;So;0;L;;;;;N;;;;;
+1D071;BYZANTINE MUSICAL SYMBOL PSIFISTOSYNAGMA;So;0;L;;;;;N;;;;;
+1D072;BYZANTINE MUSICAL SYMBOL GORGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D073;BYZANTINE MUSICAL SYMBOL ARGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D074;BYZANTINE MUSICAL SYMBOL ETERON ARGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D075;BYZANTINE MUSICAL SYMBOL OYRANISMA NEO;So;0;L;;;;;N;;;;;
+1D076;BYZANTINE MUSICAL SYMBOL THEMATISMOS ESO;So;0;L;;;;;N;;;;;
+1D077;BYZANTINE MUSICAL SYMBOL THEMATISMOS EXO;So;0;L;;;;;N;;;;;
+1D078;BYZANTINE MUSICAL SYMBOL THEMA APLOUN;So;0;L;;;;;N;;;;;
+1D079;BYZANTINE MUSICAL SYMBOL THES KAI APOTHES;So;0;L;;;;;N;;;;;
+1D07A;BYZANTINE MUSICAL SYMBOL KATAVASMA;So;0;L;;;;;N;;;;;
+1D07B;BYZANTINE MUSICAL SYMBOL ENDOFONON;So;0;L;;;;;N;;;;;
+1D07C;BYZANTINE MUSICAL SYMBOL YFEN KATO;So;0;L;;;;;N;;;;;
+1D07D;BYZANTINE MUSICAL SYMBOL YFEN ANO;So;0;L;;;;;N;;;;;
+1D07E;BYZANTINE MUSICAL SYMBOL STAVROS;So;0;L;;;;;N;;;;;
+1D07F;BYZANTINE MUSICAL SYMBOL KLASMA ANO;So;0;L;;;;;N;;;;;
+1D080;BYZANTINE MUSICAL SYMBOL DIPLI ARCHAION;So;0;L;;;;;N;;;;;
+1D081;BYZANTINE MUSICAL SYMBOL KRATIMA ARCHAION;So;0;L;;;;;N;;;;;
+1D082;BYZANTINE MUSICAL SYMBOL KRATIMA ALLO;So;0;L;;;;;N;;;;;
+1D083;BYZANTINE MUSICAL SYMBOL KRATIMA NEO;So;0;L;;;;;N;;;;;
+1D084;BYZANTINE MUSICAL SYMBOL APODERMA NEO;So;0;L;;;;;N;;;;;
+1D085;BYZANTINE MUSICAL SYMBOL APLI;So;0;L;;;;;N;;;;;
+1D086;BYZANTINE MUSICAL SYMBOL DIPLI;So;0;L;;;;;N;;;;;
+1D087;BYZANTINE MUSICAL SYMBOL TRIPLI;So;0;L;;;;;N;;;;;
+1D088;BYZANTINE MUSICAL SYMBOL TETRAPLI;So;0;L;;;;;N;;;;;
+1D089;BYZANTINE MUSICAL SYMBOL KORONIS;So;0;L;;;;;N;;;;;
+1D08A;BYZANTINE MUSICAL SYMBOL LEIMMA ENOS CHRONOU;So;0;L;;;;;N;;;;;
+1D08B;BYZANTINE MUSICAL SYMBOL LEIMMA DYO CHRONON;So;0;L;;;;;N;;;;;
+1D08C;BYZANTINE MUSICAL SYMBOL LEIMMA TRION CHRONON;So;0;L;;;;;N;;;;;
+1D08D;BYZANTINE MUSICAL SYMBOL LEIMMA TESSARON CHRONON;So;0;L;;;;;N;;;;;
+1D08E;BYZANTINE MUSICAL SYMBOL LEIMMA IMISEOS CHRONOU;So;0;L;;;;;N;;;;;
+1D08F;BYZANTINE MUSICAL SYMBOL GORGON NEO ANO;So;0;L;;;;;N;;;;;
+1D090;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON ARISTERA;So;0;L;;;;;N;;;;;
+1D091;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;;
+1D092;BYZANTINE MUSICAL SYMBOL DIGORGON;So;0;L;;;;;N;;;;;
+1D093;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA KATO;So;0;L;;;;;N;;;;;
+1D094;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA ANO;So;0;L;;;;;N;;;;;
+1D095;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;;
+1D096;BYZANTINE MUSICAL SYMBOL TRIGORGON;So;0;L;;;;;N;;;;;
+1D097;BYZANTINE MUSICAL SYMBOL ARGON;So;0;L;;;;;N;;;;;
+1D098;BYZANTINE MUSICAL SYMBOL IMIDIARGON;So;0;L;;;;;N;;;;;
+1D099;BYZANTINE MUSICAL SYMBOL DIARGON;So;0;L;;;;;N;;;;;
+1D09A;BYZANTINE MUSICAL SYMBOL AGOGI POLI ARGI;So;0;L;;;;;N;;;;;
+1D09B;BYZANTINE MUSICAL SYMBOL AGOGI ARGOTERI;So;0;L;;;;;N;;;;;
+1D09C;BYZANTINE MUSICAL SYMBOL AGOGI ARGI;So;0;L;;;;;N;;;;;
+1D09D;BYZANTINE MUSICAL SYMBOL AGOGI METRIA;So;0;L;;;;;N;;;;;
+1D09E;BYZANTINE MUSICAL SYMBOL AGOGI MESI;So;0;L;;;;;N;;;;;
+1D09F;BYZANTINE MUSICAL SYMBOL AGOGI GORGI;So;0;L;;;;;N;;;;;
+1D0A0;BYZANTINE MUSICAL SYMBOL AGOGI GORGOTERI;So;0;L;;;;;N;;;;;
+1D0A1;BYZANTINE MUSICAL SYMBOL AGOGI POLI GORGI;So;0;L;;;;;N;;;;;
+1D0A2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A3;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI PROTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A4;BYZANTINE MUSICAL SYMBOL MARTYRIA DEYTEROS ICHOS;So;0;L;;;;;N;;;;;
+1D0A5;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI DEYTEROS ICHOS;So;0;L;;;;;N;;;;;
+1D0A6;BYZANTINE MUSICAL SYMBOL MARTYRIA TRITOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A7;BYZANTINE MUSICAL SYMBOL MARTYRIA TRIFONIAS;So;0;L;;;;;N;;;;;
+1D0A8;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A9;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS LEGETOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AA;BYZANTINE MUSICAL SYMBOL MARTYRIA LEGETOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AB;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AC;BYZANTINE MUSICAL SYMBOL ISAKIA TELOUS ICHIMATOS;So;0;L;;;;;N;;;;;
+1D0AD;BYZANTINE MUSICAL SYMBOL APOSTROFOI TELOUS ICHIMATOS;So;0;L;;;;;N;;;;;
+1D0AE;BYZANTINE MUSICAL SYMBOL FANEROSIS TETRAFONIAS;So;0;L;;;;;N;;;;;
+1D0AF;BYZANTINE MUSICAL SYMBOL FANEROSIS MONOFONIAS;So;0;L;;;;;N;;;;;
+1D0B0;BYZANTINE MUSICAL SYMBOL FANEROSIS DIFONIAS;So;0;L;;;;;N;;;;;
+1D0B1;BYZANTINE MUSICAL SYMBOL MARTYRIA VARYS ICHOS;So;0;L;;;;;N;;;;;
+1D0B2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOVARYS ICHOS;So;0;L;;;;;N;;;;;
+1D0B3;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS TETARTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0B4;BYZANTINE MUSICAL SYMBOL GORTHMIKON N APLOUN;So;0;L;;;;;N;;;;;
+1D0B5;BYZANTINE MUSICAL SYMBOL GORTHMIKON N DIPLOUN;So;0;L;;;;;N;;;;;
+1D0B6;BYZANTINE MUSICAL SYMBOL ENARXIS KAI FTHORA VOU;So;0;L;;;;;N;;;;;
+1D0B7;BYZANTINE MUSICAL SYMBOL IMIFONON;So;0;L;;;;;N;;;;;
+1D0B8;BYZANTINE MUSICAL SYMBOL IMIFTHORON;So;0;L;;;;;N;;;;;
+1D0B9;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION DEYTEROU ICHOU;So;0;L;;;;;N;;;;;
+1D0BA;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI PA;So;0;L;;;;;N;;;;;
+1D0BB;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NANA;So;0;L;;;;;N;;;;;
+1D0BC;BYZANTINE MUSICAL SYMBOL FTHORA NAOS ICHOS;So;0;L;;;;;N;;;;;
+1D0BD;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI DI;So;0;L;;;;;N;;;;;
+1D0BE;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON DIATONON DI;So;0;L;;;;;N;;;;;
+1D0BF;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI KE;So;0;L;;;;;N;;;;;
+1D0C0;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI ZO;So;0;L;;;;;N;;;;;
+1D0C1;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI KATO;So;0;L;;;;;N;;;;;
+1D0C2;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI ANO;So;0;L;;;;;N;;;;;
+1D0C3;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA DIFONIAS;So;0;L;;;;;N;;;;;
+1D0C4;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA MONOFONIAS;So;0;L;;;;;N;;;;;
+1D0C5;BYZANTINE MUSICAL SYMBOL FHTORA SKLIRON CHROMA VASIS;So;0;L;;;;;N;;;;;
+1D0C6;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA SYNAFI;So;0;L;;;;;N;;;;;
+1D0C7;BYZANTINE MUSICAL SYMBOL FTHORA NENANO;So;0;L;;;;;N;;;;;
+1D0C8;BYZANTINE MUSICAL SYMBOL CHROA ZYGOS;So;0;L;;;;;N;;;;;
+1D0C9;BYZANTINE MUSICAL SYMBOL CHROA KLITON;So;0;L;;;;;N;;;;;
+1D0CA;BYZANTINE MUSICAL SYMBOL CHROA SPATHI;So;0;L;;;;;N;;;;;
+1D0CB;BYZANTINE MUSICAL SYMBOL FTHORA I YFESIS TETARTIMORION;So;0;L;;;;;N;;;;;
+1D0CC;BYZANTINE MUSICAL SYMBOL FTHORA ENARMONIOS ANTIFONIA;So;0;L;;;;;N;;;;;
+1D0CD;BYZANTINE MUSICAL SYMBOL YFESIS TRITIMORION;So;0;L;;;;;N;;;;;
+1D0CE;BYZANTINE MUSICAL SYMBOL DIESIS TRITIMORION;So;0;L;;;;;N;;;;;
+1D0CF;BYZANTINE MUSICAL SYMBOL DIESIS TETARTIMORION;So;0;L;;;;;N;;;;;
+1D0D0;BYZANTINE MUSICAL SYMBOL DIESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D1;BYZANTINE MUSICAL SYMBOL DIESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;;
+1D0D2;BYZANTINE MUSICAL SYMBOL DIESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;;
+1D0D3;BYZANTINE MUSICAL SYMBOL DIESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D4;BYZANTINE MUSICAL SYMBOL YFESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D5;BYZANTINE MUSICAL SYMBOL YFESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;;
+1D0D6;BYZANTINE MUSICAL SYMBOL YFESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;;
+1D0D7;BYZANTINE MUSICAL SYMBOL YFESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D8;BYZANTINE MUSICAL SYMBOL GENIKI DIESIS;So;0;L;;;;;N;;;;;
+1D0D9;BYZANTINE MUSICAL SYMBOL GENIKI YFESIS;So;0;L;;;;;N;;;;;
+1D0DA;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MIKRI;So;0;L;;;;;N;;;;;
+1D0DB;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MEGALI;So;0;L;;;;;N;;;;;
+1D0DC;BYZANTINE MUSICAL SYMBOL DIASTOLI DIPLI;So;0;L;;;;;N;;;;;
+1D0DD;BYZANTINE MUSICAL SYMBOL DIASTOLI THESEOS;So;0;L;;;;;N;;;;;
+1D0DE;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS;So;0;L;;;;;N;;;;;
+1D0DF;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS DISIMOU;So;0;L;;;;;N;;;;;
+1D0E0;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TRISIMOU;So;0;L;;;;;N;;;;;
+1D0E1;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TETRASIMOU;So;0;L;;;;;N;;;;;
+1D0E2;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS;So;0;L;;;;;N;;;;;
+1D0E3;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS DISIMOU;So;0;L;;;;;N;;;;;
+1D0E4;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TRISIMOU;So;0;L;;;;;N;;;;;
+1D0E5;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TETRASIMOU;So;0;L;;;;;N;;;;;
+1D0E6;BYZANTINE MUSICAL SYMBOL DIGRAMMA GG;So;0;L;;;;;N;;;;;
+1D0E7;BYZANTINE MUSICAL SYMBOL DIFTOGGOS OU;So;0;L;;;;;N;;;;;
+1D0E8;BYZANTINE MUSICAL SYMBOL STIGMA;So;0;L;;;;;N;;;;;
+1D0E9;BYZANTINE MUSICAL SYMBOL ARKTIKO PA;So;0;L;;;;;N;;;;;
+1D0EA;BYZANTINE MUSICAL SYMBOL ARKTIKO VOU;So;0;L;;;;;N;;;;;
+1D0EB;BYZANTINE MUSICAL SYMBOL ARKTIKO GA;So;0;L;;;;;N;;;;;
+1D0EC;BYZANTINE MUSICAL SYMBOL ARKTIKO DI;So;0;L;;;;;N;;;;;
+1D0ED;BYZANTINE MUSICAL SYMBOL ARKTIKO KE;So;0;L;;;;;N;;;;;
+1D0EE;BYZANTINE MUSICAL SYMBOL ARKTIKO ZO;So;0;L;;;;;N;;;;;
+1D0EF;BYZANTINE MUSICAL SYMBOL ARKTIKO NI;So;0;L;;;;;N;;;;;
+1D0F0;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO MESO;So;0;L;;;;;N;;;;;
+1D0F1;BYZANTINE MUSICAL SYMBOL KENTIMA NEO MESO;So;0;L;;;;;N;;;;;
+1D0F2;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO KATO;So;0;L;;;;;N;;;;;
+1D0F3;BYZANTINE MUSICAL SYMBOL KENTIMA NEO KATO;So;0;L;;;;;N;;;;;
+1D0F4;BYZANTINE MUSICAL SYMBOL KLASMA KATO;So;0;L;;;;;N;;;;;
+1D0F5;BYZANTINE MUSICAL SYMBOL GORGON NEO KATO;So;0;L;;;;;N;;;;;
+1D100;MUSICAL SYMBOL SINGLE BARLINE;So;0;L;;;;;N;;;;;
+1D101;MUSICAL SYMBOL DOUBLE BARLINE;So;0;L;;;;;N;;;;;
+1D102;MUSICAL SYMBOL FINAL BARLINE;So;0;L;;;;;N;;;;;
+1D103;MUSICAL SYMBOL REVERSE FINAL BARLINE;So;0;L;;;;;N;;;;;
+1D104;MUSICAL SYMBOL DASHED BARLINE;So;0;L;;;;;N;;;;;
+1D105;MUSICAL SYMBOL SHORT BARLINE;So;0;L;;;;;N;;;;;
+1D106;MUSICAL SYMBOL LEFT REPEAT SIGN;So;0;L;;;;;N;;;;;
+1D107;MUSICAL SYMBOL RIGHT REPEAT SIGN;So;0;L;;;;;N;;;;;
+1D108;MUSICAL SYMBOL REPEAT DOTS;So;0;L;;;;;N;;;;;
+1D109;MUSICAL SYMBOL DAL SEGNO;So;0;L;;;;;N;;;;;
+1D10A;MUSICAL SYMBOL DA CAPO;So;0;L;;;;;N;;;;;
+1D10B;MUSICAL SYMBOL SEGNO;So;0;L;;;;;N;;;;;
+1D10C;MUSICAL SYMBOL CODA;So;0;L;;;;;N;;;;;
+1D10D;MUSICAL SYMBOL REPEATED FIGURE-1;So;0;L;;;;;N;;;;;
+1D10E;MUSICAL SYMBOL REPEATED FIGURE-2;So;0;L;;;;;N;;;;;
+1D10F;MUSICAL SYMBOL REPEATED FIGURE-3;So;0;L;;;;;N;;;;;
+1D110;MUSICAL SYMBOL FERMATA;So;0;L;;;;;N;;;;;
+1D111;MUSICAL SYMBOL FERMATA BELOW;So;0;L;;;;;N;;;;;
+1D112;MUSICAL SYMBOL BREATH MARK;So;0;L;;;;;N;;;;;
+1D113;MUSICAL SYMBOL CAESURA;So;0;L;;;;;N;;;;;
+1D114;MUSICAL SYMBOL BRACE;So;0;L;;;;;N;;;;;
+1D115;MUSICAL SYMBOL BRACKET;So;0;L;;;;;N;;;;;
+1D116;MUSICAL SYMBOL ONE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D117;MUSICAL SYMBOL TWO-LINE STAFF;So;0;L;;;;;N;;;;;
+1D118;MUSICAL SYMBOL THREE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D119;MUSICAL SYMBOL FOUR-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11A;MUSICAL SYMBOL FIVE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11B;MUSICAL SYMBOL SIX-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11C;MUSICAL SYMBOL SIX-STRING FRETBOARD;So;0;L;;;;;N;;;;;
+1D11D;MUSICAL SYMBOL FOUR-STRING FRETBOARD;So;0;L;;;;;N;;;;;
+1D11E;MUSICAL SYMBOL G CLEF;So;0;L;;;;;N;;;;;
+1D11F;MUSICAL SYMBOL G CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D120;MUSICAL SYMBOL G CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D121;MUSICAL SYMBOL C CLEF;So;0;L;;;;;N;;;;;
+1D122;MUSICAL SYMBOL F CLEF;So;0;L;;;;;N;;;;;
+1D123;MUSICAL SYMBOL F CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D124;MUSICAL SYMBOL F CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D125;MUSICAL SYMBOL DRUM CLEF-1;So;0;L;;;;;N;;;;;
+1D126;MUSICAL SYMBOL DRUM CLEF-2;So;0;L;;;;;N;;;;;
+1D12A;MUSICAL SYMBOL DOUBLE SHARP;So;0;L;;;;;N;;;;;
+1D12B;MUSICAL SYMBOL DOUBLE FLAT;So;0;L;;;;;N;;;;;
+1D12C;MUSICAL SYMBOL FLAT UP;So;0;L;;;;;N;;;;;
+1D12D;MUSICAL SYMBOL FLAT DOWN;So;0;L;;;;;N;;;;;
+1D12E;MUSICAL SYMBOL NATURAL UP;So;0;L;;;;;N;;;;;
+1D12F;MUSICAL SYMBOL NATURAL DOWN;So;0;L;;;;;N;;;;;
+1D130;MUSICAL SYMBOL SHARP UP;So;0;L;;;;;N;;;;;
+1D131;MUSICAL SYMBOL SHARP DOWN;So;0;L;;;;;N;;;;;
+1D132;MUSICAL SYMBOL QUARTER TONE SHARP;So;0;L;;;;;N;;;;;
+1D133;MUSICAL SYMBOL QUARTER TONE FLAT;So;0;L;;;;;N;;;;;
+1D134;MUSICAL SYMBOL COMMON TIME;So;0;L;;;;;N;;;;;
+1D135;MUSICAL SYMBOL CUT TIME;So;0;L;;;;;N;;;;;
+1D136;MUSICAL SYMBOL OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D137;MUSICAL SYMBOL OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D138;MUSICAL SYMBOL QUINDICESIMA ALTA;So;0;L;;;;;N;;;;;
+1D139;MUSICAL SYMBOL QUINDICESIMA BASSA;So;0;L;;;;;N;;;;;
+1D13A;MUSICAL SYMBOL MULTI REST;So;0;L;;;;;N;;;;;
+1D13B;MUSICAL SYMBOL WHOLE REST;So;0;L;;;;;N;;;;;
+1D13C;MUSICAL SYMBOL HALF REST;So;0;L;;;;;N;;;;;
+1D13D;MUSICAL SYMBOL QUARTER REST;So;0;L;;;;;N;;;;;
+1D13E;MUSICAL SYMBOL EIGHTH REST;So;0;L;;;;;N;;;;;
+1D13F;MUSICAL SYMBOL SIXTEENTH REST;So;0;L;;;;;N;;;;;
+1D140;MUSICAL SYMBOL THIRTY-SECOND REST;So;0;L;;;;;N;;;;;
+1D141;MUSICAL SYMBOL SIXTY-FOURTH REST;So;0;L;;;;;N;;;;;
+1D142;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH REST;So;0;L;;;;;N;;;;;
+1D143;MUSICAL SYMBOL X NOTEHEAD;So;0;L;;;;;N;;;;;
+1D144;MUSICAL SYMBOL PLUS NOTEHEAD;So;0;L;;;;;N;;;;;
+1D145;MUSICAL SYMBOL CIRCLE X NOTEHEAD;So;0;L;;;;;N;;;;;
+1D146;MUSICAL SYMBOL SQUARE NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D147;MUSICAL SYMBOL SQUARE NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D148;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP WHITE;So;0;L;;;;;N;;;;;
+1D149;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP BLACK;So;0;L;;;;;N;;;;;
+1D14A;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT WHITE;So;0;L;;;;;N;;;;;
+1D14B;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT BLACK;So;0;L;;;;;N;;;;;
+1D14C;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT WHITE;So;0;L;;;;;N;;;;;
+1D14D;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT BLACK;So;0;L;;;;;N;;;;;
+1D14E;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;;
+1D14F;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;;
+1D150;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT WHITE;So;0;L;;;;;N;;;;;
+1D151;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT BLACK;So;0;L;;;;;N;;;;;
+1D152;MUSICAL SYMBOL MOON NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D153;MUSICAL SYMBOL MOON NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D154;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;;
+1D155;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;;
+1D156;MUSICAL SYMBOL PARENTHESIS NOTEHEAD;So;0;L;;;;;N;;;;;
+1D157;MUSICAL SYMBOL VOID NOTEHEAD;So;0;L;;;;;N;;;;;
+1D158;MUSICAL SYMBOL NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D159;MUSICAL SYMBOL NULL NOTEHEAD;So;0;L;;;;;N;;;;;
+1D15A;MUSICAL SYMBOL CLUSTER NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D15B;MUSICAL SYMBOL CLUSTER NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D15C;MUSICAL SYMBOL BREVE;So;0;L;;;;;N;;;;;
+1D15D;MUSICAL SYMBOL WHOLE NOTE;So;0;L;;;;;N;;;;;
+1D15E;MUSICAL SYMBOL HALF NOTE;So;0;L;1D157 1D165;;;;N;;;;;
+1D15F;MUSICAL SYMBOL QUARTER NOTE;So;0;L;1D158 1D165;;;;N;;;;;
+1D160;MUSICAL SYMBOL EIGHTH NOTE;So;0;L;1D15F 1D16E;;;;N;;;;;
+1D161;MUSICAL SYMBOL SIXTEENTH NOTE;So;0;L;1D15F 1D16F;;;;N;;;;;
+1D162;MUSICAL SYMBOL THIRTY-SECOND NOTE;So;0;L;1D15F 1D170;;;;N;;;;;
+1D163;MUSICAL SYMBOL SIXTY-FOURTH NOTE;So;0;L;1D15F 1D171;;;;N;;;;;
+1D164;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE;So;0;L;1D15F 1D172;;;;N;;;;;
+1D165;MUSICAL SYMBOL COMBINING STEM;Mc;216;L;;;;;N;;;;;
+1D166;MUSICAL SYMBOL COMBINING SPRECHGESANG STEM;Mc;216;L;;;;;N;;;;;
+1D167;MUSICAL SYMBOL COMBINING TREMOLO-1;Mn;1;NSM;;;;;N;;;;;
+1D168;MUSICAL SYMBOL COMBINING TREMOLO-2;Mn;1;NSM;;;;;N;;;;;
+1D169;MUSICAL SYMBOL COMBINING TREMOLO-3;Mn;1;NSM;;;;;N;;;;;
+1D16A;MUSICAL SYMBOL FINGERED TREMOLO-1;So;0;L;;;;;N;;;;;
+1D16B;MUSICAL SYMBOL FINGERED TREMOLO-2;So;0;L;;;;;N;;;;;
+1D16C;MUSICAL SYMBOL FINGERED TREMOLO-3;So;0;L;;;;;N;;;;;
+1D16D;MUSICAL SYMBOL COMBINING AUGMENTATION DOT;Mc;226;L;;;;;N;;;;;
+1D16E;MUSICAL SYMBOL COMBINING FLAG-1;Mc;216;L;;;;;N;;;;;
+1D16F;MUSICAL SYMBOL COMBINING FLAG-2;Mc;216;L;;;;;N;;;;;
+1D170;MUSICAL SYMBOL COMBINING FLAG-3;Mc;216;L;;;;;N;;;;;
+1D171;MUSICAL SYMBOL COMBINING FLAG-4;Mc;216;L;;;;;N;;;;;
+1D172;MUSICAL SYMBOL COMBINING FLAG-5;Mc;216;L;;;;;N;;;;;
+1D173;MUSICAL SYMBOL BEGIN BEAM;Cf;0;BN;;;;;N;;;;;
+1D174;MUSICAL SYMBOL END BEAM;Cf;0;BN;;;;;N;;;;;
+1D175;MUSICAL SYMBOL BEGIN TIE;Cf;0;BN;;;;;N;;;;;
+1D176;MUSICAL SYMBOL END TIE;Cf;0;BN;;;;;N;;;;;
+1D177;MUSICAL SYMBOL BEGIN SLUR;Cf;0;BN;;;;;N;;;;;
+1D178;MUSICAL SYMBOL END SLUR;Cf;0;BN;;;;;N;;;;;
+1D179;MUSICAL SYMBOL BEGIN PHRASE;Cf;0;BN;;;;;N;;;;;
+1D17A;MUSICAL SYMBOL END PHRASE;Cf;0;BN;;;;;N;;;;;
+1D17B;MUSICAL SYMBOL COMBINING ACCENT;Mn;220;NSM;;;;;N;;;;;
+1D17C;MUSICAL SYMBOL COMBINING STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D17D;MUSICAL SYMBOL COMBINING TENUTO;Mn;220;NSM;;;;;N;;;;;
+1D17E;MUSICAL SYMBOL COMBINING STACCATISSIMO;Mn;220;NSM;;;;;N;;;;;
+1D17F;MUSICAL SYMBOL COMBINING MARCATO;Mn;220;NSM;;;;;N;;;;;
+1D180;MUSICAL SYMBOL COMBINING MARCATO-STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D181;MUSICAL SYMBOL COMBINING ACCENT-STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D182;MUSICAL SYMBOL COMBINING LOURE;Mn;220;NSM;;;;;N;;;;;
+1D183;MUSICAL SYMBOL ARPEGGIATO UP;So;0;L;;;;;N;;;;;
+1D184;MUSICAL SYMBOL ARPEGGIATO DOWN;So;0;L;;;;;N;;;;;
+1D185;MUSICAL SYMBOL COMBINING DOIT;Mn;230;NSM;;;;;N;;;;;
+1D186;MUSICAL SYMBOL COMBINING RIP;Mn;230;NSM;;;;;N;;;;;
+1D187;MUSICAL SYMBOL COMBINING FLIP;Mn;230;NSM;;;;;N;;;;;
+1D188;MUSICAL SYMBOL COMBINING SMEAR;Mn;230;NSM;;;;;N;;;;;
+1D189;MUSICAL SYMBOL COMBINING BEND;Mn;230;NSM;;;;;N;;;;;
+1D18A;MUSICAL SYMBOL COMBINING DOUBLE TONGUE;Mn;220;NSM;;;;;N;;;;;
+1D18B;MUSICAL SYMBOL COMBINING TRIPLE TONGUE;Mn;220;NSM;;;;;N;;;;;
+1D18C;MUSICAL SYMBOL RINFORZANDO;So;0;L;;;;;N;;;;;
+1D18D;MUSICAL SYMBOL SUBITO;So;0;L;;;;;N;;;;;
+1D18E;MUSICAL SYMBOL Z;So;0;L;;;;;N;;;;;
+1D18F;MUSICAL SYMBOL PIANO;So;0;L;;;;;N;;;;;
+1D190;MUSICAL SYMBOL MEZZO;So;0;L;;;;;N;;;;;
+1D191;MUSICAL SYMBOL FORTE;So;0;L;;;;;N;;;;;
+1D192;MUSICAL SYMBOL CRESCENDO;So;0;L;;;;;N;;;;;
+1D193;MUSICAL SYMBOL DECRESCENDO;So;0;L;;;;;N;;;;;
+1D194;MUSICAL SYMBOL GRACE NOTE SLASH;So;0;L;;;;;N;;;;;
+1D195;MUSICAL SYMBOL GRACE NOTE NO SLASH;So;0;L;;;;;N;;;;;
+1D196;MUSICAL SYMBOL TR;So;0;L;;;;;N;;;;;
+1D197;MUSICAL SYMBOL TURN;So;0;L;;;;;N;;;;;
+1D198;MUSICAL SYMBOL INVERTED TURN;So;0;L;;;;;N;;;;;
+1D199;MUSICAL SYMBOL TURN SLASH;So;0;L;;;;;N;;;;;
+1D19A;MUSICAL SYMBOL TURN UP;So;0;L;;;;;N;;;;;
+1D19B;MUSICAL SYMBOL ORNAMENT STROKE-1;So;0;L;;;;;N;;;;;
+1D19C;MUSICAL SYMBOL ORNAMENT STROKE-2;So;0;L;;;;;N;;;;;
+1D19D;MUSICAL SYMBOL ORNAMENT STROKE-3;So;0;L;;;;;N;;;;;
+1D19E;MUSICAL SYMBOL ORNAMENT STROKE-4;So;0;L;;;;;N;;;;;
+1D19F;MUSICAL SYMBOL ORNAMENT STROKE-5;So;0;L;;;;;N;;;;;
+1D1A0;MUSICAL SYMBOL ORNAMENT STROKE-6;So;0;L;;;;;N;;;;;
+1D1A1;MUSICAL SYMBOL ORNAMENT STROKE-7;So;0;L;;;;;N;;;;;
+1D1A2;MUSICAL SYMBOL ORNAMENT STROKE-8;So;0;L;;;;;N;;;;;
+1D1A3;MUSICAL SYMBOL ORNAMENT STROKE-9;So;0;L;;;;;N;;;;;
+1D1A4;MUSICAL SYMBOL ORNAMENT STROKE-10;So;0;L;;;;;N;;;;;
+1D1A5;MUSICAL SYMBOL ORNAMENT STROKE-11;So;0;L;;;;;N;;;;;
+1D1A6;MUSICAL SYMBOL HAUPTSTIMME;So;0;L;;;;;N;;;;;
+1D1A7;MUSICAL SYMBOL NEBENSTIMME;So;0;L;;;;;N;;;;;
+1D1A8;MUSICAL SYMBOL END OF STIMME;So;0;L;;;;;N;;;;;
+1D1A9;MUSICAL SYMBOL DEGREE SLASH;So;0;L;;;;;N;;;;;
+1D1AA;MUSICAL SYMBOL COMBINING DOWN BOW;Mn;230;NSM;;;;;N;;;;;
+1D1AB;MUSICAL SYMBOL COMBINING UP BOW;Mn;230;NSM;;;;;N;;;;;
+1D1AC;MUSICAL SYMBOL COMBINING HARMONIC;Mn;230;NSM;;;;;N;;;;;
+1D1AD;MUSICAL SYMBOL COMBINING SNAP PIZZICATO;Mn;230;NSM;;;;;N;;;;;
+1D1AE;MUSICAL SYMBOL PEDAL MARK;So;0;L;;;;;N;;;;;
+1D1AF;MUSICAL SYMBOL PEDAL UP MARK;So;0;L;;;;;N;;;;;
+1D1B0;MUSICAL SYMBOL HALF PEDAL MARK;So;0;L;;;;;N;;;;;
+1D1B1;MUSICAL SYMBOL GLISSANDO UP;So;0;L;;;;;N;;;;;
+1D1B2;MUSICAL SYMBOL GLISSANDO DOWN;So;0;L;;;;;N;;;;;
+1D1B3;MUSICAL SYMBOL WITH FINGERNAILS;So;0;L;;;;;N;;;;;
+1D1B4;MUSICAL SYMBOL DAMP;So;0;L;;;;;N;;;;;
+1D1B5;MUSICAL SYMBOL DAMP ALL;So;0;L;;;;;N;;;;;
+1D1B6;MUSICAL SYMBOL MAXIMA;So;0;L;;;;;N;;;;;
+1D1B7;MUSICAL SYMBOL LONGA;So;0;L;;;;;N;;;;;
+1D1B8;MUSICAL SYMBOL BREVIS;So;0;L;;;;;N;;;;;
+1D1B9;MUSICAL SYMBOL SEMIBREVIS WHITE;So;0;L;;;;;N;;;;;
+1D1BA;MUSICAL SYMBOL SEMIBREVIS BLACK;So;0;L;;;;;N;;;;;
+1D1BB;MUSICAL SYMBOL MINIMA;So;0;L;1D1B9 1D165;;;;N;;;;;
+1D1BC;MUSICAL SYMBOL MINIMA BLACK;So;0;L;1D1BA 1D165;;;;N;;;;;
+1D1BD;MUSICAL SYMBOL SEMIMINIMA WHITE;So;0;L;1D1BB 1D16E;;;;N;;;;;
+1D1BE;MUSICAL SYMBOL SEMIMINIMA BLACK;So;0;L;1D1BC 1D16E;;;;N;;;;;
+1D1BF;MUSICAL SYMBOL FUSA WHITE;So;0;L;1D1BB 1D16F;;;;N;;;;;
+1D1C0;MUSICAL SYMBOL FUSA BLACK;So;0;L;1D1BC 1D16F;;;;N;;;;;
+1D1C1;MUSICAL SYMBOL LONGA PERFECTA REST;So;0;L;;;;;N;;;;;
+1D1C2;MUSICAL SYMBOL LONGA IMPERFECTA REST;So;0;L;;;;;N;;;;;
+1D1C3;MUSICAL SYMBOL BREVIS REST;So;0;L;;;;;N;;;;;
+1D1C4;MUSICAL SYMBOL SEMIBREVIS REST;So;0;L;;;;;N;;;;;
+1D1C5;MUSICAL SYMBOL MINIMA REST;So;0;L;;;;;N;;;;;
+1D1C6;MUSICAL SYMBOL SEMIMINIMA REST;So;0;L;;;;;N;;;;;
+1D1C7;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;;
+1D1C8;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;;
+1D1C9;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;;
+1D1CA;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;;
+1D1CB;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;;
+1D1CC;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;;
+1D1CD;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-2;So;0;L;;;;;N;;;;;
+1D1CE;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-3;So;0;L;;;;;N;;;;;
+1D1CF;MUSICAL SYMBOL CROIX;So;0;L;;;;;N;;;;;
+1D1D0;MUSICAL SYMBOL GREGORIAN C CLEF;So;0;L;;;;;N;;;;;
+1D1D1;MUSICAL SYMBOL GREGORIAN F CLEF;So;0;L;;;;;N;;;;;
+1D1D2;MUSICAL SYMBOL SQUARE B;So;0;L;;;;;N;;;;;
+1D1D3;MUSICAL SYMBOL VIRGA;So;0;L;;;;;N;;;;;
+1D1D4;MUSICAL SYMBOL PODATUS;So;0;L;;;;;N;;;;;
+1D1D5;MUSICAL SYMBOL CLIVIS;So;0;L;;;;;N;;;;;
+1D1D6;MUSICAL SYMBOL SCANDICUS;So;0;L;;;;;N;;;;;
+1D1D7;MUSICAL SYMBOL CLIMACUS;So;0;L;;;;;N;;;;;
+1D1D8;MUSICAL SYMBOL TORCULUS;So;0;L;;;;;N;;;;;
+1D1D9;MUSICAL SYMBOL PORRECTUS;So;0;L;;;;;N;;;;;
+1D1DA;MUSICAL SYMBOL PORRECTUS FLEXUS;So;0;L;;;;;N;;;;;
+1D1DB;MUSICAL SYMBOL SCANDICUS FLEXUS;So;0;L;;;;;N;;;;;
+1D1DC;MUSICAL SYMBOL TORCULUS RESUPINUS;So;0;L;;;;;N;;;;;
+1D1DD;MUSICAL SYMBOL PES SUBPUNCTIS;So;0;L;;;;;N;;;;;
+1D400;MATHEMATICAL BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D401;MATHEMATICAL BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D402;MATHEMATICAL BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D403;MATHEMATICAL BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D404;MATHEMATICAL BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D405;MATHEMATICAL BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D406;MATHEMATICAL BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D407;MATHEMATICAL BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D408;MATHEMATICAL BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D409;MATHEMATICAL BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D40A;MATHEMATICAL BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D40B;MATHEMATICAL BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D40C;MATHEMATICAL BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D40D;MATHEMATICAL BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D40E;MATHEMATICAL BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D40F;MATHEMATICAL BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D410;MATHEMATICAL BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D411;MATHEMATICAL BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D412;MATHEMATICAL BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D413;MATHEMATICAL BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D414;MATHEMATICAL BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D415;MATHEMATICAL BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D416;MATHEMATICAL BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D417;MATHEMATICAL BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D418;MATHEMATICAL BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D419;MATHEMATICAL BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D41A;MATHEMATICAL BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D41B;MATHEMATICAL BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D41C;MATHEMATICAL BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D41D;MATHEMATICAL BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D41E;MATHEMATICAL BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D41F;MATHEMATICAL BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D420;MATHEMATICAL BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D421;MATHEMATICAL BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D422;MATHEMATICAL BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D423;MATHEMATICAL BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D424;MATHEMATICAL BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D425;MATHEMATICAL BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D426;MATHEMATICAL BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D427;MATHEMATICAL BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D428;MATHEMATICAL BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D429;MATHEMATICAL BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D42A;MATHEMATICAL BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D42B;MATHEMATICAL BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D42C;MATHEMATICAL BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D42D;MATHEMATICAL BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D42E;MATHEMATICAL BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D42F;MATHEMATICAL BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D430;MATHEMATICAL BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D431;MATHEMATICAL BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D432;MATHEMATICAL BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D433;MATHEMATICAL BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D434;MATHEMATICAL ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D435;MATHEMATICAL ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D436;MATHEMATICAL ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D437;MATHEMATICAL ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D438;MATHEMATICAL ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D439;MATHEMATICAL ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D43A;MATHEMATICAL ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D43B;MATHEMATICAL ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D43C;MATHEMATICAL ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D43D;MATHEMATICAL ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D43E;MATHEMATICAL ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D43F;MATHEMATICAL ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D440;MATHEMATICAL ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D441;MATHEMATICAL ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D442;MATHEMATICAL ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D443;MATHEMATICAL ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D444;MATHEMATICAL ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D445;MATHEMATICAL ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D446;MATHEMATICAL ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D447;MATHEMATICAL ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D448;MATHEMATICAL ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D449;MATHEMATICAL ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D44A;MATHEMATICAL ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D44B;MATHEMATICAL ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D44C;MATHEMATICAL ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D44D;MATHEMATICAL ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D44E;MATHEMATICAL ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D44F;MATHEMATICAL ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D450;MATHEMATICAL ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D451;MATHEMATICAL ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D452;MATHEMATICAL ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D453;MATHEMATICAL ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D454;MATHEMATICAL ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D456;MATHEMATICAL ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D457;MATHEMATICAL ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D458;MATHEMATICAL ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D459;MATHEMATICAL ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D45A;MATHEMATICAL ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D45B;MATHEMATICAL ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D45C;MATHEMATICAL ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D45D;MATHEMATICAL ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D45E;MATHEMATICAL ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D45F;MATHEMATICAL ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D460;MATHEMATICAL ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D461;MATHEMATICAL ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D462;MATHEMATICAL ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D463;MATHEMATICAL ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D464;MATHEMATICAL ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D465;MATHEMATICAL ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D466;MATHEMATICAL ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D467;MATHEMATICAL ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D468;MATHEMATICAL BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D469;MATHEMATICAL BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D46A;MATHEMATICAL BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D46B;MATHEMATICAL BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D46C;MATHEMATICAL BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D46D;MATHEMATICAL BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D46E;MATHEMATICAL BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D46F;MATHEMATICAL BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D470;MATHEMATICAL BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D471;MATHEMATICAL BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D472;MATHEMATICAL BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D473;MATHEMATICAL BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D474;MATHEMATICAL BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D475;MATHEMATICAL BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D476;MATHEMATICAL BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D477;MATHEMATICAL BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D478;MATHEMATICAL BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D479;MATHEMATICAL BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D47A;MATHEMATICAL BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D47B;MATHEMATICAL BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D47C;MATHEMATICAL BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D47D;MATHEMATICAL BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D47E;MATHEMATICAL BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D47F;MATHEMATICAL BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D480;MATHEMATICAL BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D481;MATHEMATICAL BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D482;MATHEMATICAL BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D483;MATHEMATICAL BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D484;MATHEMATICAL BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D485;MATHEMATICAL BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D486;MATHEMATICAL BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D487;MATHEMATICAL BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D488;MATHEMATICAL BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D489;MATHEMATICAL BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D48A;MATHEMATICAL BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D48B;MATHEMATICAL BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D48C;MATHEMATICAL BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D48D;MATHEMATICAL BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D48E;MATHEMATICAL BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D48F;MATHEMATICAL BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D490;MATHEMATICAL BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D491;MATHEMATICAL BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D492;MATHEMATICAL BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D493;MATHEMATICAL BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D494;MATHEMATICAL BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D495;MATHEMATICAL BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D496;MATHEMATICAL BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D497;MATHEMATICAL BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D498;MATHEMATICAL BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D499;MATHEMATICAL BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D49A;MATHEMATICAL BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D49B;MATHEMATICAL BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D49C;MATHEMATICAL SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D49E;MATHEMATICAL SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D49F;MATHEMATICAL SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D4A2;MATHEMATICAL SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D4A5;MATHEMATICAL SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D4A6;MATHEMATICAL SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D4A9;MATHEMATICAL SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D4AA;MATHEMATICAL SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D4AB;MATHEMATICAL SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D4AC;MATHEMATICAL SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D4AE;MATHEMATICAL SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D4AF;MATHEMATICAL SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D4B0;MATHEMATICAL SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D4B1;MATHEMATICAL SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D4B2;MATHEMATICAL SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D4B3;MATHEMATICAL SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D4B4;MATHEMATICAL SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D4B5;MATHEMATICAL SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D4B6;MATHEMATICAL SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D4B7;MATHEMATICAL SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D4B8;MATHEMATICAL SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D4B9;MATHEMATICAL SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D4BB;MATHEMATICAL SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D4BD;MATHEMATICAL SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D4BE;MATHEMATICAL SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D4BF;MATHEMATICAL SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D4C0;MATHEMATICAL SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D4C2;MATHEMATICAL SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D4C3;MATHEMATICAL SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D4C5;MATHEMATICAL SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D4C6;MATHEMATICAL SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D4C7;MATHEMATICAL SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D4C8;MATHEMATICAL SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D4C9;MATHEMATICAL SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D4CA;MATHEMATICAL SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D4CB;MATHEMATICAL SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D4CC;MATHEMATICAL SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D4CD;MATHEMATICAL SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D4CE;MATHEMATICAL SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D4CF;MATHEMATICAL SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D4D0;MATHEMATICAL BOLD SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D4D1;MATHEMATICAL BOLD SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D4D2;MATHEMATICAL BOLD SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D4D3;MATHEMATICAL BOLD SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D4D4;MATHEMATICAL BOLD SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D4D5;MATHEMATICAL BOLD SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D4D6;MATHEMATICAL BOLD SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D4D7;MATHEMATICAL BOLD SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D4D8;MATHEMATICAL BOLD SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D4D9;MATHEMATICAL BOLD SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D4DA;MATHEMATICAL BOLD SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D4DB;MATHEMATICAL BOLD SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D4DC;MATHEMATICAL BOLD SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D4DD;MATHEMATICAL BOLD SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D4DE;MATHEMATICAL BOLD SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D4DF;MATHEMATICAL BOLD SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D4E0;MATHEMATICAL BOLD SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D4E1;MATHEMATICAL BOLD SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D4E2;MATHEMATICAL BOLD SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D4E3;MATHEMATICAL BOLD SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D4E4;MATHEMATICAL BOLD SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D4E5;MATHEMATICAL BOLD SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D4E6;MATHEMATICAL BOLD SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D4E7;MATHEMATICAL BOLD SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D4E8;MATHEMATICAL BOLD SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D4E9;MATHEMATICAL BOLD SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D4EA;MATHEMATICAL BOLD SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D4EB;MATHEMATICAL BOLD SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D4EC;MATHEMATICAL BOLD SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D4ED;MATHEMATICAL BOLD SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D4EE;MATHEMATICAL BOLD SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D4EF;MATHEMATICAL BOLD SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D4F0;MATHEMATICAL BOLD SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D4F1;MATHEMATICAL BOLD SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D4F2;MATHEMATICAL BOLD SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D4F3;MATHEMATICAL BOLD SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D4F4;MATHEMATICAL BOLD SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D4F5;MATHEMATICAL BOLD SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D4F6;MATHEMATICAL BOLD SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D4F7;MATHEMATICAL BOLD SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D4F8;MATHEMATICAL BOLD SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D4F9;MATHEMATICAL BOLD SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D4FA;MATHEMATICAL BOLD SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D4FB;MATHEMATICAL BOLD SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D4FC;MATHEMATICAL BOLD SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D4FD;MATHEMATICAL BOLD SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D4FE;MATHEMATICAL BOLD SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D4FF;MATHEMATICAL BOLD SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D500;MATHEMATICAL BOLD SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D501;MATHEMATICAL BOLD SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D502;MATHEMATICAL BOLD SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D503;MATHEMATICAL BOLD SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D504;MATHEMATICAL FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D505;MATHEMATICAL FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D507;MATHEMATICAL FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D508;MATHEMATICAL FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D509;MATHEMATICAL FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D50A;MATHEMATICAL FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D50D;MATHEMATICAL FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D50E;MATHEMATICAL FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D50F;MATHEMATICAL FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D510;MATHEMATICAL FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D511;MATHEMATICAL FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D512;MATHEMATICAL FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D513;MATHEMATICAL FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D514;MATHEMATICAL FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D516;MATHEMATICAL FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D517;MATHEMATICAL FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D518;MATHEMATICAL FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D519;MATHEMATICAL FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D51A;MATHEMATICAL FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D51B;MATHEMATICAL FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D51C;MATHEMATICAL FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D51E;MATHEMATICAL FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D51F;MATHEMATICAL FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D520;MATHEMATICAL FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D521;MATHEMATICAL FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D522;MATHEMATICAL FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D523;MATHEMATICAL FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D524;MATHEMATICAL FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D525;MATHEMATICAL FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D526;MATHEMATICAL FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D527;MATHEMATICAL FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D528;MATHEMATICAL FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D529;MATHEMATICAL FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D52A;MATHEMATICAL FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D52B;MATHEMATICAL FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D52C;MATHEMATICAL FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D52D;MATHEMATICAL FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D52E;MATHEMATICAL FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D52F;MATHEMATICAL FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D530;MATHEMATICAL FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D531;MATHEMATICAL FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D532;MATHEMATICAL FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D533;MATHEMATICAL FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D534;MATHEMATICAL FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D535;MATHEMATICAL FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D536;MATHEMATICAL FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D537;MATHEMATICAL FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D538;MATHEMATICAL DOUBLE-STRUCK CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D539;MATHEMATICAL DOUBLE-STRUCK CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D53B;MATHEMATICAL DOUBLE-STRUCK CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D53C;MATHEMATICAL DOUBLE-STRUCK CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D53D;MATHEMATICAL DOUBLE-STRUCK CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D53E;MATHEMATICAL DOUBLE-STRUCK CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D540;MATHEMATICAL DOUBLE-STRUCK CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D541;MATHEMATICAL DOUBLE-STRUCK CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D542;MATHEMATICAL DOUBLE-STRUCK CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D543;MATHEMATICAL DOUBLE-STRUCK CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D544;MATHEMATICAL DOUBLE-STRUCK CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D546;MATHEMATICAL DOUBLE-STRUCK CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D54A;MATHEMATICAL DOUBLE-STRUCK CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D54B;MATHEMATICAL DOUBLE-STRUCK CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D54C;MATHEMATICAL DOUBLE-STRUCK CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D54D;MATHEMATICAL DOUBLE-STRUCK CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D54E;MATHEMATICAL DOUBLE-STRUCK CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D54F;MATHEMATICAL DOUBLE-STRUCK CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D550;MATHEMATICAL DOUBLE-STRUCK CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D552;MATHEMATICAL DOUBLE-STRUCK SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D553;MATHEMATICAL DOUBLE-STRUCK SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D554;MATHEMATICAL DOUBLE-STRUCK SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D555;MATHEMATICAL DOUBLE-STRUCK SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D556;MATHEMATICAL DOUBLE-STRUCK SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D557;MATHEMATICAL DOUBLE-STRUCK SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D558;MATHEMATICAL DOUBLE-STRUCK SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D559;MATHEMATICAL DOUBLE-STRUCK SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D55A;MATHEMATICAL DOUBLE-STRUCK SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D55B;MATHEMATICAL DOUBLE-STRUCK SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D55C;MATHEMATICAL DOUBLE-STRUCK SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D55D;MATHEMATICAL DOUBLE-STRUCK SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D55E;MATHEMATICAL DOUBLE-STRUCK SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D55F;MATHEMATICAL DOUBLE-STRUCK SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D560;MATHEMATICAL DOUBLE-STRUCK SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D561;MATHEMATICAL DOUBLE-STRUCK SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D562;MATHEMATICAL DOUBLE-STRUCK SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D563;MATHEMATICAL DOUBLE-STRUCK SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D564;MATHEMATICAL DOUBLE-STRUCK SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D565;MATHEMATICAL DOUBLE-STRUCK SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D566;MATHEMATICAL DOUBLE-STRUCK SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D567;MATHEMATICAL DOUBLE-STRUCK SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D568;MATHEMATICAL DOUBLE-STRUCK SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D569;MATHEMATICAL DOUBLE-STRUCK SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D56A;MATHEMATICAL DOUBLE-STRUCK SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D56B;MATHEMATICAL DOUBLE-STRUCK SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D56C;MATHEMATICAL BOLD FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D56D;MATHEMATICAL BOLD FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D56E;MATHEMATICAL BOLD FRAKTUR CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D56F;MATHEMATICAL BOLD FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D570;MATHEMATICAL BOLD FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D571;MATHEMATICAL BOLD FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D572;MATHEMATICAL BOLD FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D573;MATHEMATICAL BOLD FRAKTUR CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D574;MATHEMATICAL BOLD FRAKTUR CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D575;MATHEMATICAL BOLD FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D576;MATHEMATICAL BOLD FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D577;MATHEMATICAL BOLD FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D578;MATHEMATICAL BOLD FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D579;MATHEMATICAL BOLD FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D57A;MATHEMATICAL BOLD FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D57B;MATHEMATICAL BOLD FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D57C;MATHEMATICAL BOLD FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D57D;MATHEMATICAL BOLD FRAKTUR CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D57E;MATHEMATICAL BOLD FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D57F;MATHEMATICAL BOLD FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D580;MATHEMATICAL BOLD FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D581;MATHEMATICAL BOLD FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D582;MATHEMATICAL BOLD FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D583;MATHEMATICAL BOLD FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D584;MATHEMATICAL BOLD FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D585;MATHEMATICAL BOLD FRAKTUR CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D586;MATHEMATICAL BOLD FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D587;MATHEMATICAL BOLD FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D588;MATHEMATICAL BOLD FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D589;MATHEMATICAL BOLD FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D58A;MATHEMATICAL BOLD FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D58B;MATHEMATICAL BOLD FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D58C;MATHEMATICAL BOLD FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D58D;MATHEMATICAL BOLD FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D58E;MATHEMATICAL BOLD FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D58F;MATHEMATICAL BOLD FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D590;MATHEMATICAL BOLD FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D591;MATHEMATICAL BOLD FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D592;MATHEMATICAL BOLD FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D593;MATHEMATICAL BOLD FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D594;MATHEMATICAL BOLD FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D595;MATHEMATICAL BOLD FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D596;MATHEMATICAL BOLD FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D597;MATHEMATICAL BOLD FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D598;MATHEMATICAL BOLD FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D599;MATHEMATICAL BOLD FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D59A;MATHEMATICAL BOLD FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D59B;MATHEMATICAL BOLD FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D59C;MATHEMATICAL BOLD FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D59D;MATHEMATICAL BOLD FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D59E;MATHEMATICAL BOLD FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D59F;MATHEMATICAL BOLD FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D5A0;MATHEMATICAL SANS-SERIF CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D5A1;MATHEMATICAL SANS-SERIF CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D5A2;MATHEMATICAL SANS-SERIF CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D5A3;MATHEMATICAL SANS-SERIF CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D5A4;MATHEMATICAL SANS-SERIF CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D5A5;MATHEMATICAL SANS-SERIF CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D5A6;MATHEMATICAL SANS-SERIF CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D5A7;MATHEMATICAL SANS-SERIF CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D5A8;MATHEMATICAL SANS-SERIF CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D5A9;MATHEMATICAL SANS-SERIF CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D5AA;MATHEMATICAL SANS-SERIF CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D5AB;MATHEMATICAL SANS-SERIF CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D5AC;MATHEMATICAL SANS-SERIF CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D5AD;MATHEMATICAL SANS-SERIF CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D5AE;MATHEMATICAL SANS-SERIF CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D5AF;MATHEMATICAL SANS-SERIF CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D5B0;MATHEMATICAL SANS-SERIF CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D5B1;MATHEMATICAL SANS-SERIF CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D5B2;MATHEMATICAL SANS-SERIF CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D5B3;MATHEMATICAL SANS-SERIF CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D5B4;MATHEMATICAL SANS-SERIF CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D5B5;MATHEMATICAL SANS-SERIF CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D5B6;MATHEMATICAL SANS-SERIF CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D5B7;MATHEMATICAL SANS-SERIF CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D5B8;MATHEMATICAL SANS-SERIF CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D5B9;MATHEMATICAL SANS-SERIF CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D5BA;MATHEMATICAL SANS-SERIF SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D5BB;MATHEMATICAL SANS-SERIF SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D5BC;MATHEMATICAL SANS-SERIF SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D5BD;MATHEMATICAL SANS-SERIF SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D5BE;MATHEMATICAL SANS-SERIF SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D5BF;MATHEMATICAL SANS-SERIF SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D5C0;MATHEMATICAL SANS-SERIF SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D5C1;MATHEMATICAL SANS-SERIF SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D5C2;MATHEMATICAL SANS-SERIF SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D5C3;MATHEMATICAL SANS-SERIF SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D5C4;MATHEMATICAL SANS-SERIF SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D5C5;MATHEMATICAL SANS-SERIF SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D5C6;MATHEMATICAL SANS-SERIF SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D5C7;MATHEMATICAL SANS-SERIF SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D5C8;MATHEMATICAL SANS-SERIF SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D5C9;MATHEMATICAL SANS-SERIF SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D5CA;MATHEMATICAL SANS-SERIF SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D5CB;MATHEMATICAL SANS-SERIF SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D5CC;MATHEMATICAL SANS-SERIF SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D5CD;MATHEMATICAL SANS-SERIF SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D5CE;MATHEMATICAL SANS-SERIF SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D5CF;MATHEMATICAL SANS-SERIF SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D5D0;MATHEMATICAL SANS-SERIF SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D5D1;MATHEMATICAL SANS-SERIF SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D5D2;MATHEMATICAL SANS-SERIF SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D5D3;MATHEMATICAL SANS-SERIF SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D5D4;MATHEMATICAL SANS-SERIF BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D5D5;MATHEMATICAL SANS-SERIF BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D5D6;MATHEMATICAL SANS-SERIF BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D5D7;MATHEMATICAL SANS-SERIF BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D5D8;MATHEMATICAL SANS-SERIF BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D5D9;MATHEMATICAL SANS-SERIF BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D5DA;MATHEMATICAL SANS-SERIF BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D5DB;MATHEMATICAL SANS-SERIF BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D5DC;MATHEMATICAL SANS-SERIF BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D5DD;MATHEMATICAL SANS-SERIF BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D5DE;MATHEMATICAL SANS-SERIF BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D5DF;MATHEMATICAL SANS-SERIF BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D5E0;MATHEMATICAL SANS-SERIF BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D5E1;MATHEMATICAL SANS-SERIF BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D5E2;MATHEMATICAL SANS-SERIF BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D5E3;MATHEMATICAL SANS-SERIF BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D5E4;MATHEMATICAL SANS-SERIF BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D5E5;MATHEMATICAL SANS-SERIF BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D5E6;MATHEMATICAL SANS-SERIF BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D5E7;MATHEMATICAL SANS-SERIF BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D5E8;MATHEMATICAL SANS-SERIF BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D5E9;MATHEMATICAL SANS-SERIF BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D5EA;MATHEMATICAL SANS-SERIF BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D5EB;MATHEMATICAL SANS-SERIF BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D5EC;MATHEMATICAL SANS-SERIF BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D5ED;MATHEMATICAL SANS-SERIF BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D5EE;MATHEMATICAL SANS-SERIF BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D5EF;MATHEMATICAL SANS-SERIF BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D5F0;MATHEMATICAL SANS-SERIF BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D5F1;MATHEMATICAL SANS-SERIF BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D5F2;MATHEMATICAL SANS-SERIF BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D5F3;MATHEMATICAL SANS-SERIF BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D5F4;MATHEMATICAL SANS-SERIF BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D5F5;MATHEMATICAL SANS-SERIF BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D5F6;MATHEMATICAL SANS-SERIF BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D5F7;MATHEMATICAL SANS-SERIF BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D5F8;MATHEMATICAL SANS-SERIF BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D5F9;MATHEMATICAL SANS-SERIF BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D5FA;MATHEMATICAL SANS-SERIF BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D5FB;MATHEMATICAL SANS-SERIF BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D5FC;MATHEMATICAL SANS-SERIF BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D5FD;MATHEMATICAL SANS-SERIF BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D5FE;MATHEMATICAL SANS-SERIF BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D5FF;MATHEMATICAL SANS-SERIF BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D600;MATHEMATICAL SANS-SERIF BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D601;MATHEMATICAL SANS-SERIF BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D602;MATHEMATICAL SANS-SERIF BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D603;MATHEMATICAL SANS-SERIF BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D604;MATHEMATICAL SANS-SERIF BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D605;MATHEMATICAL SANS-SERIF BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D606;MATHEMATICAL SANS-SERIF BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D607;MATHEMATICAL SANS-SERIF BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D608;MATHEMATICAL SANS-SERIF ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D609;MATHEMATICAL SANS-SERIF ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D60A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D60B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D60C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D60D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D60E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D60F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D610;MATHEMATICAL SANS-SERIF ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D611;MATHEMATICAL SANS-SERIF ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D612;MATHEMATICAL SANS-SERIF ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D613;MATHEMATICAL SANS-SERIF ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D614;MATHEMATICAL SANS-SERIF ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D615;MATHEMATICAL SANS-SERIF ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D616;MATHEMATICAL SANS-SERIF ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D617;MATHEMATICAL SANS-SERIF ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D618;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D619;MATHEMATICAL SANS-SERIF ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D61A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D61B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D61C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D61D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D61E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D61F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D620;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D621;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D622;MATHEMATICAL SANS-SERIF ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D623;MATHEMATICAL SANS-SERIF ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D624;MATHEMATICAL SANS-SERIF ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D625;MATHEMATICAL SANS-SERIF ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D626;MATHEMATICAL SANS-SERIF ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D627;MATHEMATICAL SANS-SERIF ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D628;MATHEMATICAL SANS-SERIF ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D629;MATHEMATICAL SANS-SERIF ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D62A;MATHEMATICAL SANS-SERIF ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D62B;MATHEMATICAL SANS-SERIF ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D62C;MATHEMATICAL SANS-SERIF ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D62D;MATHEMATICAL SANS-SERIF ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D62E;MATHEMATICAL SANS-SERIF ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D62F;MATHEMATICAL SANS-SERIF ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D630;MATHEMATICAL SANS-SERIF ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D631;MATHEMATICAL SANS-SERIF ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D632;MATHEMATICAL SANS-SERIF ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D633;MATHEMATICAL SANS-SERIF ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D634;MATHEMATICAL SANS-SERIF ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D635;MATHEMATICAL SANS-SERIF ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D636;MATHEMATICAL SANS-SERIF ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D637;MATHEMATICAL SANS-SERIF ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D638;MATHEMATICAL SANS-SERIF ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D639;MATHEMATICAL SANS-SERIF ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D63A;MATHEMATICAL SANS-SERIF ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D63B;MATHEMATICAL SANS-SERIF ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D63C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D63D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D63E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D63F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D640;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D641;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D642;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D643;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D644;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D645;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D646;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D647;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D648;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D649;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D64A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D64B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D64C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D64D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D64E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D64F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D650;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D651;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D652;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D653;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D654;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D655;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D656;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D657;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D658;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D659;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D65A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D65B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D65C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D65D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D65E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D65F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D660;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D661;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D662;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D663;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D664;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D665;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D666;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D667;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D668;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D669;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D66A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D66B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D66C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D66D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D66E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D66F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D670;MATHEMATICAL MONOSPACE CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D671;MATHEMATICAL MONOSPACE CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D672;MATHEMATICAL MONOSPACE CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D673;MATHEMATICAL MONOSPACE CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D674;MATHEMATICAL MONOSPACE CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D675;MATHEMATICAL MONOSPACE CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D676;MATHEMATICAL MONOSPACE CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D677;MATHEMATICAL MONOSPACE CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D678;MATHEMATICAL MONOSPACE CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D679;MATHEMATICAL MONOSPACE CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D67A;MATHEMATICAL MONOSPACE CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D67B;MATHEMATICAL MONOSPACE CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D67C;MATHEMATICAL MONOSPACE CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D67D;MATHEMATICAL MONOSPACE CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D67E;MATHEMATICAL MONOSPACE CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D67F;MATHEMATICAL MONOSPACE CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D680;MATHEMATICAL MONOSPACE CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D681;MATHEMATICAL MONOSPACE CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D682;MATHEMATICAL MONOSPACE CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D683;MATHEMATICAL MONOSPACE CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D684;MATHEMATICAL MONOSPACE CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D685;MATHEMATICAL MONOSPACE CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D686;MATHEMATICAL MONOSPACE CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D687;MATHEMATICAL MONOSPACE CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D688;MATHEMATICAL MONOSPACE CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D689;MATHEMATICAL MONOSPACE CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D68A;MATHEMATICAL MONOSPACE SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D68B;MATHEMATICAL MONOSPACE SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D68C;MATHEMATICAL MONOSPACE SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D68D;MATHEMATICAL MONOSPACE SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D68E;MATHEMATICAL MONOSPACE SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D68F;MATHEMATICAL MONOSPACE SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D690;MATHEMATICAL MONOSPACE SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D691;MATHEMATICAL MONOSPACE SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D692;MATHEMATICAL MONOSPACE SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D693;MATHEMATICAL MONOSPACE SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D694;MATHEMATICAL MONOSPACE SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D695;MATHEMATICAL MONOSPACE SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D696;MATHEMATICAL MONOSPACE SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D697;MATHEMATICAL MONOSPACE SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D698;MATHEMATICAL MONOSPACE SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D699;MATHEMATICAL MONOSPACE SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D69A;MATHEMATICAL MONOSPACE SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D69B;MATHEMATICAL MONOSPACE SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D69C;MATHEMATICAL MONOSPACE SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D69D;MATHEMATICAL MONOSPACE SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D69E;MATHEMATICAL MONOSPACE SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D69F;MATHEMATICAL MONOSPACE SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D6A0;MATHEMATICAL MONOSPACE SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D6A1;MATHEMATICAL MONOSPACE SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D6A2;MATHEMATICAL MONOSPACE SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D6A3;MATHEMATICAL MONOSPACE SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D6A8;MATHEMATICAL BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D6A9;MATHEMATICAL BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D6AA;MATHEMATICAL BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D6AB;MATHEMATICAL BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D6AC;MATHEMATICAL BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D6AD;MATHEMATICAL BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D6AE;MATHEMATICAL BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D6AF;MATHEMATICAL BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D6B0;MATHEMATICAL BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D6B1;MATHEMATICAL BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D6B2;MATHEMATICAL BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D6B3;MATHEMATICAL BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D6B4;MATHEMATICAL BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D6B5;MATHEMATICAL BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D6B6;MATHEMATICAL BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D6B7;MATHEMATICAL BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D6B8;MATHEMATICAL BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D6B9;MATHEMATICAL BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D6BA;MATHEMATICAL BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D6BB;MATHEMATICAL BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D6BC;MATHEMATICAL BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D6BD;MATHEMATICAL BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D6BE;MATHEMATICAL BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D6BF;MATHEMATICAL BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D6C0;MATHEMATICAL BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D6C1;MATHEMATICAL BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D6C2;MATHEMATICAL BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D6C3;MATHEMATICAL BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D6C4;MATHEMATICAL BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D6C5;MATHEMATICAL BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D6C6;MATHEMATICAL BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D6C7;MATHEMATICAL BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D6C8;MATHEMATICAL BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D6C9;MATHEMATICAL BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D6CA;MATHEMATICAL BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D6CB;MATHEMATICAL BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D6CC;MATHEMATICAL BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D6CD;MATHEMATICAL BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D6CE;MATHEMATICAL BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D6CF;MATHEMATICAL BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D6D0;MATHEMATICAL BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D6D1;MATHEMATICAL BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D6D2;MATHEMATICAL BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D6D3;MATHEMATICAL BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D6D4;MATHEMATICAL BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D6D5;MATHEMATICAL BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D6D6;MATHEMATICAL BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D6D7;MATHEMATICAL BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D6D8;MATHEMATICAL BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D6D9;MATHEMATICAL BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D6DA;MATHEMATICAL BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D6DB;MATHEMATICAL BOLD PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D6DC;MATHEMATICAL BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D6DD;MATHEMATICAL BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D6DE;MATHEMATICAL BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D6DF;MATHEMATICAL BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D6E0;MATHEMATICAL BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D6E1;MATHEMATICAL BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D6E2;MATHEMATICAL ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D6E3;MATHEMATICAL ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D6E4;MATHEMATICAL ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D6E5;MATHEMATICAL ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D6E6;MATHEMATICAL ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D6E7;MATHEMATICAL ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D6E8;MATHEMATICAL ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D6E9;MATHEMATICAL ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D6EA;MATHEMATICAL ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D6EB;MATHEMATICAL ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D6EC;MATHEMATICAL ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D6ED;MATHEMATICAL ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D6EE;MATHEMATICAL ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D6EF;MATHEMATICAL ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D6F0;MATHEMATICAL ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D6F1;MATHEMATICAL ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D6F2;MATHEMATICAL ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D6F3;MATHEMATICAL ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D6F4;MATHEMATICAL ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D6F5;MATHEMATICAL ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D6F6;MATHEMATICAL ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D6F7;MATHEMATICAL ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D6F8;MATHEMATICAL ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D6F9;MATHEMATICAL ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D6FA;MATHEMATICAL ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D6FB;MATHEMATICAL ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D6FC;MATHEMATICAL ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D6FD;MATHEMATICAL ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D6FE;MATHEMATICAL ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D6FF;MATHEMATICAL ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D700;MATHEMATICAL ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D701;MATHEMATICAL ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D702;MATHEMATICAL ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D703;MATHEMATICAL ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D704;MATHEMATICAL ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D705;MATHEMATICAL ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D706;MATHEMATICAL ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D707;MATHEMATICAL ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D708;MATHEMATICAL ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D709;MATHEMATICAL ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D70A;MATHEMATICAL ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D70B;MATHEMATICAL ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D70C;MATHEMATICAL ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D70D;MATHEMATICAL ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D70E;MATHEMATICAL ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D70F;MATHEMATICAL ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D710;MATHEMATICAL ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D711;MATHEMATICAL ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D712;MATHEMATICAL ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D713;MATHEMATICAL ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D714;MATHEMATICAL ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D715;MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D716;MATHEMATICAL ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D717;MATHEMATICAL ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D718;MATHEMATICAL ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D719;MATHEMATICAL ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D71A;MATHEMATICAL ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D71B;MATHEMATICAL ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D71C;MATHEMATICAL BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D71D;MATHEMATICAL BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D71E;MATHEMATICAL BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D71F;MATHEMATICAL BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D720;MATHEMATICAL BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D721;MATHEMATICAL BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D722;MATHEMATICAL BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D723;MATHEMATICAL BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D724;MATHEMATICAL BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D725;MATHEMATICAL BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D726;MATHEMATICAL BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D727;MATHEMATICAL BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D728;MATHEMATICAL BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D729;MATHEMATICAL BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D72A;MATHEMATICAL BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D72B;MATHEMATICAL BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D72C;MATHEMATICAL BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D72D;MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D72E;MATHEMATICAL BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D72F;MATHEMATICAL BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D730;MATHEMATICAL BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D731;MATHEMATICAL BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D732;MATHEMATICAL BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D733;MATHEMATICAL BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D734;MATHEMATICAL BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D735;MATHEMATICAL BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D736;MATHEMATICAL BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D737;MATHEMATICAL BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D738;MATHEMATICAL BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D739;MATHEMATICAL BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D73A;MATHEMATICAL BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D73B;MATHEMATICAL BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D73C;MATHEMATICAL BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D73D;MATHEMATICAL BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D73E;MATHEMATICAL BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D73F;MATHEMATICAL BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D740;MATHEMATICAL BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D741;MATHEMATICAL BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D742;MATHEMATICAL BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D743;MATHEMATICAL BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D744;MATHEMATICAL BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D745;MATHEMATICAL BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D746;MATHEMATICAL BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D747;MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D748;MATHEMATICAL BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D749;MATHEMATICAL BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D74A;MATHEMATICAL BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D74B;MATHEMATICAL BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D74C;MATHEMATICAL BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D74D;MATHEMATICAL BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D74E;MATHEMATICAL BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D74F;MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D750;MATHEMATICAL BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D751;MATHEMATICAL BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D752;MATHEMATICAL BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D753;MATHEMATICAL BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D754;MATHEMATICAL BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D755;MATHEMATICAL BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D756;MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D757;MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D758;MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D759;MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D75A;MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D75B;MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D75C;MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D75D;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D75E;MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D75F;MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D760;MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D761;MATHEMATICAL SANS-SERIF BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D762;MATHEMATICAL SANS-SERIF BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D763;MATHEMATICAL SANS-SERIF BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D764;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D765;MATHEMATICAL SANS-SERIF BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D766;MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D767;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D768;MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D769;MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D76A;MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D76B;MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D76C;MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D76D;MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D76E;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D76F;MATHEMATICAL SANS-SERIF BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D770;MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D771;MATHEMATICAL SANS-SERIF BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D772;MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D773;MATHEMATICAL SANS-SERIF BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D774;MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D775;MATHEMATICAL SANS-SERIF BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D776;MATHEMATICAL SANS-SERIF BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D777;MATHEMATICAL SANS-SERIF BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D778;MATHEMATICAL SANS-SERIF BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D779;MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D77A;MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D77B;MATHEMATICAL SANS-SERIF BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D77C;MATHEMATICAL SANS-SERIF BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D77D;MATHEMATICAL SANS-SERIF BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D77E;MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D77F;MATHEMATICAL SANS-SERIF BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D780;MATHEMATICAL SANS-SERIF BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D781;MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D782;MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D783;MATHEMATICAL SANS-SERIF BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D784;MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D785;MATHEMATICAL SANS-SERIF BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D786;MATHEMATICAL SANS-SERIF BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D787;MATHEMATICAL SANS-SERIF BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D788;MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D789;MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D78A;MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D78B;MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D78C;MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D78D;MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D78E;MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D78F;MATHEMATICAL SANS-SERIF BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D790;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D791;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D792;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D793;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D794;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D795;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D796;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D797;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D798;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D799;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D79A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D79B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D79C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D79D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D79E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D79F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D7A0;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D7A1;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D7A2;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D7A3;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D7A4;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D7A5;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D7A6;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D7A7;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D7A8;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D7A9;MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D7AA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D7AB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D7AC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D7AD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D7AE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D7AF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D7B0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D7B1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D7B2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D7B3;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D7B4;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D7B5;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D7B6;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D7B7;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D7B8;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D7B9;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D7BA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D7BB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D7BC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D7BD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D7BE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D7BF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D7C0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D7C1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D7C2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D7C3;MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D7C4;MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D7C5;MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D7C6;MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D7C7;MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D7C8;MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D7C9;MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D7CE;MATHEMATICAL BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7CF;MATHEMATICAL BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7D0;MATHEMATICAL BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7D1;MATHEMATICAL BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7D2;MATHEMATICAL BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7D3;MATHEMATICAL BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7D4;MATHEMATICAL BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7D5;MATHEMATICAL BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7D6;MATHEMATICAL BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7D7;MATHEMATICAL BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7D8;MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7D9;MATHEMATICAL DOUBLE-STRUCK DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7DA;MATHEMATICAL DOUBLE-STRUCK DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7DB;MATHEMATICAL DOUBLE-STRUCK DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7DC;MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7DD;MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7DE;MATHEMATICAL DOUBLE-STRUCK DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7DF;MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7E0;MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7E1;MATHEMATICAL DOUBLE-STRUCK DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7E2;MATHEMATICAL SANS-SERIF DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7E3;MATHEMATICAL SANS-SERIF DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7E4;MATHEMATICAL SANS-SERIF DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7E5;MATHEMATICAL SANS-SERIF DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7E6;MATHEMATICAL SANS-SERIF DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7E7;MATHEMATICAL SANS-SERIF DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7E8;MATHEMATICAL SANS-SERIF DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7E9;MATHEMATICAL SANS-SERIF DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7EA;MATHEMATICAL SANS-SERIF DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7EB;MATHEMATICAL SANS-SERIF DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7EC;MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7ED;MATHEMATICAL SANS-SERIF BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7EE;MATHEMATICAL SANS-SERIF BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7EF;MATHEMATICAL SANS-SERIF BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7F0;MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7F1;MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7F2;MATHEMATICAL SANS-SERIF BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7F3;MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7F4;MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7F5;MATHEMATICAL SANS-SERIF BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7F6;MATHEMATICAL MONOSPACE DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7F7;MATHEMATICAL MONOSPACE DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7F8;MATHEMATICAL MONOSPACE DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7F9;MATHEMATICAL MONOSPACE DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7FA;MATHEMATICAL MONOSPACE DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7FB;MATHEMATICAL MONOSPACE DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7FC;MATHEMATICAL MONOSPACE DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7FD;MATHEMATICAL MONOSPACE DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7FE;MATHEMATICAL MONOSPACE DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7FF;MATHEMATICAL MONOSPACE DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
+2A6D6;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
+2F800;CJK COMPATIBILITY IDEOGRAPH-2F800;Lo;0;L;4E3D;;;;N;;;;;
+2F801;CJK COMPATIBILITY IDEOGRAPH-2F801;Lo;0;L;4E38;;;;N;;;;;
+2F802;CJK COMPATIBILITY IDEOGRAPH-2F802;Lo;0;L;4E41;;;;N;;;;;
+2F803;CJK COMPATIBILITY IDEOGRAPH-2F803;Lo;0;L;20122;;;;N;;;;;
+2F804;CJK COMPATIBILITY IDEOGRAPH-2F804;Lo;0;L;4F60;;;;N;;;;;
+2F805;CJK COMPATIBILITY IDEOGRAPH-2F805;Lo;0;L;4FAE;;;;N;;;;;
+2F806;CJK COMPATIBILITY IDEOGRAPH-2F806;Lo;0;L;4FBB;;;;N;;;;;
+2F807;CJK COMPATIBILITY IDEOGRAPH-2F807;Lo;0;L;5002;;;;N;;;;;
+2F808;CJK COMPATIBILITY IDEOGRAPH-2F808;Lo;0;L;507A;;;;N;;;;;
+2F809;CJK COMPATIBILITY IDEOGRAPH-2F809;Lo;0;L;5099;;;;N;;;;;
+2F80A;CJK COMPATIBILITY IDEOGRAPH-2F80A;Lo;0;L;50E7;;;;N;;;;;
+2F80B;CJK COMPATIBILITY IDEOGRAPH-2F80B;Lo;0;L;50CF;;;;N;;;;;
+2F80C;CJK COMPATIBILITY IDEOGRAPH-2F80C;Lo;0;L;349E;;;;N;;;;;
+2F80D;CJK COMPATIBILITY IDEOGRAPH-2F80D;Lo;0;L;2063A;;;;N;;;;;
+2F80E;CJK COMPATIBILITY IDEOGRAPH-2F80E;Lo;0;L;514D;;;;N;;;;;
+2F80F;CJK COMPATIBILITY IDEOGRAPH-2F80F;Lo;0;L;5154;;;;N;;;;;
+2F810;CJK COMPATIBILITY IDEOGRAPH-2F810;Lo;0;L;5164;;;;N;;;;;
+2F811;CJK COMPATIBILITY IDEOGRAPH-2F811;Lo;0;L;5177;;;;N;;;;;
+2F812;CJK COMPATIBILITY IDEOGRAPH-2F812;Lo;0;L;2051C;;;;N;;;;;
+2F813;CJK COMPATIBILITY IDEOGRAPH-2F813;Lo;0;L;34B9;;;;N;;;;;
+2F814;CJK COMPATIBILITY IDEOGRAPH-2F814;Lo;0;L;5167;;;;N;;;;;
+2F815;CJK COMPATIBILITY IDEOGRAPH-2F815;Lo;0;L;518D;;;;N;;;;;
+2F816;CJK COMPATIBILITY IDEOGRAPH-2F816;Lo;0;L;2054B;;;;N;;;;;
+2F817;CJK COMPATIBILITY IDEOGRAPH-2F817;Lo;0;L;5197;;;;N;;;;;
+2F818;CJK COMPATIBILITY IDEOGRAPH-2F818;Lo;0;L;51A4;;;;N;;;;;
+2F819;CJK COMPATIBILITY IDEOGRAPH-2F819;Lo;0;L;4ECC;;;;N;;;;;
+2F81A;CJK COMPATIBILITY IDEOGRAPH-2F81A;Lo;0;L;51AC;;;;N;;;;;
+2F81B;CJK COMPATIBILITY IDEOGRAPH-2F81B;Lo;0;L;51B5;;;;N;;;;;
+2F81C;CJK COMPATIBILITY IDEOGRAPH-2F81C;Lo;0;L;291DF;;;;N;;;;;
+2F81D;CJK COMPATIBILITY IDEOGRAPH-2F81D;Lo;0;L;51F5;;;;N;;;;;
+2F81E;CJK COMPATIBILITY IDEOGRAPH-2F81E;Lo;0;L;5203;;;;N;;;;;
+2F81F;CJK COMPATIBILITY IDEOGRAPH-2F81F;Lo;0;L;34DF;;;;N;;;;;
+2F820;CJK COMPATIBILITY IDEOGRAPH-2F820;Lo;0;L;523B;;;;N;;;;;
+2F821;CJK COMPATIBILITY IDEOGRAPH-2F821;Lo;0;L;5246;;;;N;;;;;
+2F822;CJK COMPATIBILITY IDEOGRAPH-2F822;Lo;0;L;5272;;;;N;;;;;
+2F823;CJK COMPATIBILITY IDEOGRAPH-2F823;Lo;0;L;5277;;;;N;;;;;
+2F824;CJK COMPATIBILITY IDEOGRAPH-2F824;Lo;0;L;3515;;;;N;;;;;
+2F825;CJK COMPATIBILITY IDEOGRAPH-2F825;Lo;0;L;52C7;;;;N;;;;;
+2F826;CJK COMPATIBILITY IDEOGRAPH-2F826;Lo;0;L;52C9;;;;N;;;;;
+2F827;CJK COMPATIBILITY IDEOGRAPH-2F827;Lo;0;L;52E4;;;;N;;;;;
+2F828;CJK COMPATIBILITY IDEOGRAPH-2F828;Lo;0;L;52FA;;;;N;;;;;
+2F829;CJK COMPATIBILITY IDEOGRAPH-2F829;Lo;0;L;5305;;;;N;;;;;
+2F82A;CJK COMPATIBILITY IDEOGRAPH-2F82A;Lo;0;L;5306;;;;N;;;;;
+2F82B;CJK COMPATIBILITY IDEOGRAPH-2F82B;Lo;0;L;5317;;;;N;;;;;
+2F82C;CJK COMPATIBILITY IDEOGRAPH-2F82C;Lo;0;L;5349;;;;N;;;;;
+2F82D;CJK COMPATIBILITY IDEOGRAPH-2F82D;Lo;0;L;5351;;;;N;;;;;
+2F82E;CJK COMPATIBILITY IDEOGRAPH-2F82E;Lo;0;L;535A;;;;N;;;;;
+2F82F;CJK COMPATIBILITY IDEOGRAPH-2F82F;Lo;0;L;5373;;;;N;;;;;
+2F830;CJK COMPATIBILITY IDEOGRAPH-2F830;Lo;0;L;537D;;;;N;;;;;
+2F831;CJK COMPATIBILITY IDEOGRAPH-2F831;Lo;0;L;537F;;;;N;;;;;
+2F832;CJK COMPATIBILITY IDEOGRAPH-2F832;Lo;0;L;537F;;;;N;;;;;
+2F833;CJK COMPATIBILITY IDEOGRAPH-2F833;Lo;0;L;537F;;;;N;;;;;
+2F834;CJK COMPATIBILITY IDEOGRAPH-2F834;Lo;0;L;20A2C;;;;N;;;;;
+2F835;CJK COMPATIBILITY IDEOGRAPH-2F835;Lo;0;L;7070;;;;N;;;;;
+2F836;CJK COMPATIBILITY IDEOGRAPH-2F836;Lo;0;L;53CA;;;;N;;;;;
+2F837;CJK COMPATIBILITY IDEOGRAPH-2F837;Lo;0;L;53DF;;;;N;;;;;
+2F838;CJK COMPATIBILITY IDEOGRAPH-2F838;Lo;0;L;20B63;;;;N;;;;;
+2F839;CJK COMPATIBILITY IDEOGRAPH-2F839;Lo;0;L;53EB;;;;N;;;;;
+2F83A;CJK COMPATIBILITY IDEOGRAPH-2F83A;Lo;0;L;53F1;;;;N;;;;;
+2F83B;CJK COMPATIBILITY IDEOGRAPH-2F83B;Lo;0;L;5406;;;;N;;;;;
+2F83C;CJK COMPATIBILITY IDEOGRAPH-2F83C;Lo;0;L;549E;;;;N;;;;;
+2F83D;CJK COMPATIBILITY IDEOGRAPH-2F83D;Lo;0;L;5438;;;;N;;;;;
+2F83E;CJK COMPATIBILITY IDEOGRAPH-2F83E;Lo;0;L;5448;;;;N;;;;;
+2F83F;CJK COMPATIBILITY IDEOGRAPH-2F83F;Lo;0;L;5468;;;;N;;;;;
+2F840;CJK COMPATIBILITY IDEOGRAPH-2F840;Lo;0;L;54A2;;;;N;;;;;
+2F841;CJK COMPATIBILITY IDEOGRAPH-2F841;Lo;0;L;54F6;;;;N;;;;;
+2F842;CJK COMPATIBILITY IDEOGRAPH-2F842;Lo;0;L;5510;;;;N;;;;;
+2F843;CJK COMPATIBILITY IDEOGRAPH-2F843;Lo;0;L;5553;;;;N;;;;;
+2F844;CJK COMPATIBILITY IDEOGRAPH-2F844;Lo;0;L;5563;;;;N;;;;;
+2F845;CJK COMPATIBILITY IDEOGRAPH-2F845;Lo;0;L;5584;;;;N;;;;;
+2F846;CJK COMPATIBILITY IDEOGRAPH-2F846;Lo;0;L;5584;;;;N;;;;;
+2F847;CJK COMPATIBILITY IDEOGRAPH-2F847;Lo;0;L;5599;;;;N;;;;;
+2F848;CJK COMPATIBILITY IDEOGRAPH-2F848;Lo;0;L;55AB;;;;N;;;;;
+2F849;CJK COMPATIBILITY IDEOGRAPH-2F849;Lo;0;L;55B3;;;;N;;;;;
+2F84A;CJK COMPATIBILITY IDEOGRAPH-2F84A;Lo;0;L;55C2;;;;N;;;;;
+2F84B;CJK COMPATIBILITY IDEOGRAPH-2F84B;Lo;0;L;5716;;;;N;;;;;
+2F84C;CJK COMPATIBILITY IDEOGRAPH-2F84C;Lo;0;L;5606;;;;N;;;;;
+2F84D;CJK COMPATIBILITY IDEOGRAPH-2F84D;Lo;0;L;5717;;;;N;;;;;
+2F84E;CJK COMPATIBILITY IDEOGRAPH-2F84E;Lo;0;L;5651;;;;N;;;;;
+2F84F;CJK COMPATIBILITY IDEOGRAPH-2F84F;Lo;0;L;5674;;;;N;;;;;
+2F850;CJK COMPATIBILITY IDEOGRAPH-2F850;Lo;0;L;5207;;;;N;;;;;
+2F851;CJK COMPATIBILITY IDEOGRAPH-2F851;Lo;0;L;58EE;;;;N;;;;;
+2F852;CJK COMPATIBILITY IDEOGRAPH-2F852;Lo;0;L;57CE;;;;N;;;;;
+2F853;CJK COMPATIBILITY IDEOGRAPH-2F853;Lo;0;L;57F4;;;;N;;;;;
+2F854;CJK COMPATIBILITY IDEOGRAPH-2F854;Lo;0;L;580D;;;;N;;;;;
+2F855;CJK COMPATIBILITY IDEOGRAPH-2F855;Lo;0;L;578B;;;;N;;;;;
+2F856;CJK COMPATIBILITY IDEOGRAPH-2F856;Lo;0;L;5832;;;;N;;;;;
+2F857;CJK COMPATIBILITY IDEOGRAPH-2F857;Lo;0;L;5831;;;;N;;;;;
+2F858;CJK COMPATIBILITY IDEOGRAPH-2F858;Lo;0;L;58AC;;;;N;;;;;
+2F859;CJK COMPATIBILITY IDEOGRAPH-2F859;Lo;0;L;214E4;;;;N;;;;;
+2F85A;CJK COMPATIBILITY IDEOGRAPH-2F85A;Lo;0;L;58F2;;;;N;;;;;
+2F85B;CJK COMPATIBILITY IDEOGRAPH-2F85B;Lo;0;L;58F7;;;;N;;;;;
+2F85C;CJK COMPATIBILITY IDEOGRAPH-2F85C;Lo;0;L;5906;;;;N;;;;;
+2F85D;CJK COMPATIBILITY IDEOGRAPH-2F85D;Lo;0;L;591A;;;;N;;;;;
+2F85E;CJK COMPATIBILITY IDEOGRAPH-2F85E;Lo;0;L;5922;;;;N;;;;;
+2F85F;CJK COMPATIBILITY IDEOGRAPH-2F85F;Lo;0;L;5962;;;;N;;;;;
+2F860;CJK COMPATIBILITY IDEOGRAPH-2F860;Lo;0;L;216A8;;;;N;;;;;
+2F861;CJK COMPATIBILITY IDEOGRAPH-2F861;Lo;0;L;216EA;;;;N;;;;;
+2F862;CJK COMPATIBILITY IDEOGRAPH-2F862;Lo;0;L;59EC;;;;N;;;;;
+2F863;CJK COMPATIBILITY IDEOGRAPH-2F863;Lo;0;L;5A1B;;;;N;;;;;
+2F864;CJK COMPATIBILITY IDEOGRAPH-2F864;Lo;0;L;5A27;;;;N;;;;;
+2F865;CJK COMPATIBILITY IDEOGRAPH-2F865;Lo;0;L;59D8;;;;N;;;;;
+2F866;CJK COMPATIBILITY IDEOGRAPH-2F866;Lo;0;L;5A66;;;;N;;;;;
+2F867;CJK COMPATIBILITY IDEOGRAPH-2F867;Lo;0;L;36EE;;;;N;;;;;
+2F868;CJK COMPATIBILITY IDEOGRAPH-2F868;Lo;0;L;2136A;;;;N;;;;;
+2F869;CJK COMPATIBILITY IDEOGRAPH-2F869;Lo;0;L;5B08;;;;N;;;;;
+2F86A;CJK COMPATIBILITY IDEOGRAPH-2F86A;Lo;0;L;5B3E;;;;N;;;;;
+2F86B;CJK COMPATIBILITY IDEOGRAPH-2F86B;Lo;0;L;5B3E;;;;N;;;;;
+2F86C;CJK COMPATIBILITY IDEOGRAPH-2F86C;Lo;0;L;219C8;;;;N;;;;;
+2F86D;CJK COMPATIBILITY IDEOGRAPH-2F86D;Lo;0;L;5BC3;;;;N;;;;;
+2F86E;CJK COMPATIBILITY IDEOGRAPH-2F86E;Lo;0;L;5BD8;;;;N;;;;;
+2F86F;CJK COMPATIBILITY IDEOGRAPH-2F86F;Lo;0;L;5BE7;;;;N;;;;;
+2F870;CJK COMPATIBILITY IDEOGRAPH-2F870;Lo;0;L;5BF3;;;;N;;;;;
+2F871;CJK COMPATIBILITY IDEOGRAPH-2F871;Lo;0;L;21B18;;;;N;;;;;
+2F872;CJK COMPATIBILITY IDEOGRAPH-2F872;Lo;0;L;5BFF;;;;N;;;;;
+2F873;CJK COMPATIBILITY IDEOGRAPH-2F873;Lo;0;L;5C06;;;;N;;;;;
+2F874;CJK COMPATIBILITY IDEOGRAPH-2F874;Lo;0;L;5F33;;;;N;;;;;
+2F875;CJK COMPATIBILITY IDEOGRAPH-2F875;Lo;0;L;5C22;;;;N;;;;;
+2F876;CJK COMPATIBILITY IDEOGRAPH-2F876;Lo;0;L;3781;;;;N;;;;;
+2F877;CJK COMPATIBILITY IDEOGRAPH-2F877;Lo;0;L;5C60;;;;N;;;;;
+2F878;CJK COMPATIBILITY IDEOGRAPH-2F878;Lo;0;L;5C6E;;;;N;;;;;
+2F879;CJK COMPATIBILITY IDEOGRAPH-2F879;Lo;0;L;5CC0;;;;N;;;;;
+2F87A;CJK COMPATIBILITY IDEOGRAPH-2F87A;Lo;0;L;5C8D;;;;N;;;;;
+2F87B;CJK COMPATIBILITY IDEOGRAPH-2F87B;Lo;0;L;21DE4;;;;N;;;;;
+2F87C;CJK COMPATIBILITY IDEOGRAPH-2F87C;Lo;0;L;5D43;;;;N;;;;;
+2F87D;CJK COMPATIBILITY IDEOGRAPH-2F87D;Lo;0;L;21DE6;;;;N;;;;;
+2F87E;CJK COMPATIBILITY IDEOGRAPH-2F87E;Lo;0;L;5D6E;;;;N;;;;;
+2F87F;CJK COMPATIBILITY IDEOGRAPH-2F87F;Lo;0;L;5D6B;;;;N;;;;;
+2F880;CJK COMPATIBILITY IDEOGRAPH-2F880;Lo;0;L;5D7C;;;;N;;;;;
+2F881;CJK COMPATIBILITY IDEOGRAPH-2F881;Lo;0;L;5DE1;;;;N;;;;;
+2F882;CJK COMPATIBILITY IDEOGRAPH-2F882;Lo;0;L;5DE2;;;;N;;;;;
+2F883;CJK COMPATIBILITY IDEOGRAPH-2F883;Lo;0;L;382F;;;;N;;;;;
+2F884;CJK COMPATIBILITY IDEOGRAPH-2F884;Lo;0;L;5DFD;;;;N;;;;;
+2F885;CJK COMPATIBILITY IDEOGRAPH-2F885;Lo;0;L;5E28;;;;N;;;;;
+2F886;CJK COMPATIBILITY IDEOGRAPH-2F886;Lo;0;L;5E3D;;;;N;;;;;
+2F887;CJK COMPATIBILITY IDEOGRAPH-2F887;Lo;0;L;5E69;;;;N;;;;;
+2F888;CJK COMPATIBILITY IDEOGRAPH-2F888;Lo;0;L;3862;;;;N;;;;;
+2F889;CJK COMPATIBILITY IDEOGRAPH-2F889;Lo;0;L;22183;;;;N;;;;;
+2F88A;CJK COMPATIBILITY IDEOGRAPH-2F88A;Lo;0;L;387C;;;;N;;;;;
+2F88B;CJK COMPATIBILITY IDEOGRAPH-2F88B;Lo;0;L;5EB0;;;;N;;;;;
+2F88C;CJK COMPATIBILITY IDEOGRAPH-2F88C;Lo;0;L;5EB3;;;;N;;;;;
+2F88D;CJK COMPATIBILITY IDEOGRAPH-2F88D;Lo;0;L;5EB6;;;;N;;;;;
+2F88E;CJK COMPATIBILITY IDEOGRAPH-2F88E;Lo;0;L;5ECA;;;;N;;;;;
+2F88F;CJK COMPATIBILITY IDEOGRAPH-2F88F;Lo;0;L;2A392;;;;N;;;;;
+2F890;CJK COMPATIBILITY IDEOGRAPH-2F890;Lo;0;L;5EFE;;;;N;;;;;
+2F891;CJK COMPATIBILITY IDEOGRAPH-2F891;Lo;0;L;22331;;;;N;;;;;
+2F892;CJK COMPATIBILITY IDEOGRAPH-2F892;Lo;0;L;22331;;;;N;;;;;
+2F893;CJK COMPATIBILITY IDEOGRAPH-2F893;Lo;0;L;8201;;;;N;;;;;
+2F894;CJK COMPATIBILITY IDEOGRAPH-2F894;Lo;0;L;5F22;;;;N;;;;;
+2F895;CJK COMPATIBILITY IDEOGRAPH-2F895;Lo;0;L;5F22;;;;N;;;;;
+2F896;CJK COMPATIBILITY IDEOGRAPH-2F896;Lo;0;L;38C7;;;;N;;;;;
+2F897;CJK COMPATIBILITY IDEOGRAPH-2F897;Lo;0;L;232B8;;;;N;;;;;
+2F898;CJK COMPATIBILITY IDEOGRAPH-2F898;Lo;0;L;261DA;;;;N;;;;;
+2F899;CJK COMPATIBILITY IDEOGRAPH-2F899;Lo;0;L;5F62;;;;N;;;;;
+2F89A;CJK COMPATIBILITY IDEOGRAPH-2F89A;Lo;0;L;5F6B;;;;N;;;;;
+2F89B;CJK COMPATIBILITY IDEOGRAPH-2F89B;Lo;0;L;38E3;;;;N;;;;;
+2F89C;CJK COMPATIBILITY IDEOGRAPH-2F89C;Lo;0;L;5F9A;;;;N;;;;;
+2F89D;CJK COMPATIBILITY IDEOGRAPH-2F89D;Lo;0;L;5FCD;;;;N;;;;;
+2F89E;CJK COMPATIBILITY IDEOGRAPH-2F89E;Lo;0;L;5FD7;;;;N;;;;;
+2F89F;CJK COMPATIBILITY IDEOGRAPH-2F89F;Lo;0;L;5FF9;;;;N;;;;;
+2F8A0;CJK COMPATIBILITY IDEOGRAPH-2F8A0;Lo;0;L;6081;;;;N;;;;;
+2F8A1;CJK COMPATIBILITY IDEOGRAPH-2F8A1;Lo;0;L;393A;;;;N;;;;;
+2F8A2;CJK COMPATIBILITY IDEOGRAPH-2F8A2;Lo;0;L;391C;;;;N;;;;;
+2F8A3;CJK COMPATIBILITY IDEOGRAPH-2F8A3;Lo;0;L;6094;;;;N;;;;;
+2F8A4;CJK COMPATIBILITY IDEOGRAPH-2F8A4;Lo;0;L;226D4;;;;N;;;;;
+2F8A5;CJK COMPATIBILITY IDEOGRAPH-2F8A5;Lo;0;L;60C7;;;;N;;;;;
+2F8A6;CJK COMPATIBILITY IDEOGRAPH-2F8A6;Lo;0;L;6148;;;;N;;;;;
+2F8A7;CJK COMPATIBILITY IDEOGRAPH-2F8A7;Lo;0;L;614C;;;;N;;;;;
+2F8A8;CJK COMPATIBILITY IDEOGRAPH-2F8A8;Lo;0;L;614E;;;;N;;;;;
+2F8A9;CJK COMPATIBILITY IDEOGRAPH-2F8A9;Lo;0;L;614C;;;;N;;;;;
+2F8AA;CJK COMPATIBILITY IDEOGRAPH-2F8AA;Lo;0;L;617A;;;;N;;;;;
+2F8AB;CJK COMPATIBILITY IDEOGRAPH-2F8AB;Lo;0;L;618E;;;;N;;;;;
+2F8AC;CJK COMPATIBILITY IDEOGRAPH-2F8AC;Lo;0;L;61B2;;;;N;;;;;
+2F8AD;CJK COMPATIBILITY IDEOGRAPH-2F8AD;Lo;0;L;61A4;;;;N;;;;;
+2F8AE;CJK COMPATIBILITY IDEOGRAPH-2F8AE;Lo;0;L;61AF;;;;N;;;;;
+2F8AF;CJK COMPATIBILITY IDEOGRAPH-2F8AF;Lo;0;L;61DE;;;;N;;;;;
+2F8B0;CJK COMPATIBILITY IDEOGRAPH-2F8B0;Lo;0;L;61F2;;;;N;;;;;
+2F8B1;CJK COMPATIBILITY IDEOGRAPH-2F8B1;Lo;0;L;61F6;;;;N;;;;;
+2F8B2;CJK COMPATIBILITY IDEOGRAPH-2F8B2;Lo;0;L;6210;;;;N;;;;;
+2F8B3;CJK COMPATIBILITY IDEOGRAPH-2F8B3;Lo;0;L;621B;;;;N;;;;;
+2F8B4;CJK COMPATIBILITY IDEOGRAPH-2F8B4;Lo;0;L;625D;;;;N;;;;;
+2F8B5;CJK COMPATIBILITY IDEOGRAPH-2F8B5;Lo;0;L;62B1;;;;N;;;;;
+2F8B6;CJK COMPATIBILITY IDEOGRAPH-2F8B6;Lo;0;L;62D4;;;;N;;;;;
+2F8B7;CJK COMPATIBILITY IDEOGRAPH-2F8B7;Lo;0;L;6350;;;;N;;;;;
+2F8B8;CJK COMPATIBILITY IDEOGRAPH-2F8B8;Lo;0;L;22B0C;;;;N;;;;;
+2F8B9;CJK COMPATIBILITY IDEOGRAPH-2F8B9;Lo;0;L;633D;;;;N;;;;;
+2F8BA;CJK COMPATIBILITY IDEOGRAPH-2F8BA;Lo;0;L;62FC;;;;N;;;;;
+2F8BB;CJK COMPATIBILITY IDEOGRAPH-2F8BB;Lo;0;L;6368;;;;N;;;;;
+2F8BC;CJK COMPATIBILITY IDEOGRAPH-2F8BC;Lo;0;L;6383;;;;N;;;;;
+2F8BD;CJK COMPATIBILITY IDEOGRAPH-2F8BD;Lo;0;L;63E4;;;;N;;;;;
+2F8BE;CJK COMPATIBILITY IDEOGRAPH-2F8BE;Lo;0;L;22BF1;;;;N;;;;;
+2F8BF;CJK COMPATIBILITY IDEOGRAPH-2F8BF;Lo;0;L;6422;;;;N;;;;;
+2F8C0;CJK COMPATIBILITY IDEOGRAPH-2F8C0;Lo;0;L;63C5;;;;N;;;;;
+2F8C1;CJK COMPATIBILITY IDEOGRAPH-2F8C1;Lo;0;L;63A9;;;;N;;;;;
+2F8C2;CJK COMPATIBILITY IDEOGRAPH-2F8C2;Lo;0;L;3A2E;;;;N;;;;;
+2F8C3;CJK COMPATIBILITY IDEOGRAPH-2F8C3;Lo;0;L;6469;;;;N;;;;;
+2F8C4;CJK COMPATIBILITY IDEOGRAPH-2F8C4;Lo;0;L;647E;;;;N;;;;;
+2F8C5;CJK COMPATIBILITY IDEOGRAPH-2F8C5;Lo;0;L;649D;;;;N;;;;;
+2F8C6;CJK COMPATIBILITY IDEOGRAPH-2F8C6;Lo;0;L;6477;;;;N;;;;;
+2F8C7;CJK COMPATIBILITY IDEOGRAPH-2F8C7;Lo;0;L;3A6C;;;;N;;;;;
+2F8C8;CJK COMPATIBILITY IDEOGRAPH-2F8C8;Lo;0;L;654F;;;;N;;;;;
+2F8C9;CJK COMPATIBILITY IDEOGRAPH-2F8C9;Lo;0;L;656C;;;;N;;;;;
+2F8CA;CJK COMPATIBILITY IDEOGRAPH-2F8CA;Lo;0;L;2300A;;;;N;;;;;
+2F8CB;CJK COMPATIBILITY IDEOGRAPH-2F8CB;Lo;0;L;65E3;;;;N;;;;;
+2F8CC;CJK COMPATIBILITY IDEOGRAPH-2F8CC;Lo;0;L;66F8;;;;N;;;;;
+2F8CD;CJK COMPATIBILITY IDEOGRAPH-2F8CD;Lo;0;L;6649;;;;N;;;;;
+2F8CE;CJK COMPATIBILITY IDEOGRAPH-2F8CE;Lo;0;L;3B19;;;;N;;;;;
+2F8CF;CJK COMPATIBILITY IDEOGRAPH-2F8CF;Lo;0;L;6691;;;;N;;;;;
+2F8D0;CJK COMPATIBILITY IDEOGRAPH-2F8D0;Lo;0;L;3B08;;;;N;;;;;
+2F8D1;CJK COMPATIBILITY IDEOGRAPH-2F8D1;Lo;0;L;3AE4;;;;N;;;;;
+2F8D2;CJK COMPATIBILITY IDEOGRAPH-2F8D2;Lo;0;L;5192;;;;N;;;;;
+2F8D3;CJK COMPATIBILITY IDEOGRAPH-2F8D3;Lo;0;L;5195;;;;N;;;;;
+2F8D4;CJK COMPATIBILITY IDEOGRAPH-2F8D4;Lo;0;L;6700;;;;N;;;;;
+2F8D5;CJK COMPATIBILITY IDEOGRAPH-2F8D5;Lo;0;L;669C;;;;N;;;;;
+2F8D6;CJK COMPATIBILITY IDEOGRAPH-2F8D6;Lo;0;L;80AD;;;;N;;;;;
+2F8D7;CJK COMPATIBILITY IDEOGRAPH-2F8D7;Lo;0;L;43D9;;;;N;;;;;
+2F8D8;CJK COMPATIBILITY IDEOGRAPH-2F8D8;Lo;0;L;6717;;;;N;;;;;
+2F8D9;CJK COMPATIBILITY IDEOGRAPH-2F8D9;Lo;0;L;671B;;;;N;;;;;
+2F8DA;CJK COMPATIBILITY IDEOGRAPH-2F8DA;Lo;0;L;6721;;;;N;;;;;
+2F8DB;CJK COMPATIBILITY IDEOGRAPH-2F8DB;Lo;0;L;675E;;;;N;;;;;
+2F8DC;CJK COMPATIBILITY IDEOGRAPH-2F8DC;Lo;0;L;6753;;;;N;;;;;
+2F8DD;CJK COMPATIBILITY IDEOGRAPH-2F8DD;Lo;0;L;233C3;;;;N;;;;;
+2F8DE;CJK COMPATIBILITY IDEOGRAPH-2F8DE;Lo;0;L;3B49;;;;N;;;;;
+2F8DF;CJK COMPATIBILITY IDEOGRAPH-2F8DF;Lo;0;L;67FA;;;;N;;;;;
+2F8E0;CJK COMPATIBILITY IDEOGRAPH-2F8E0;Lo;0;L;6785;;;;N;;;;;
+2F8E1;CJK COMPATIBILITY IDEOGRAPH-2F8E1;Lo;0;L;6852;;;;N;;;;;
+2F8E2;CJK COMPATIBILITY IDEOGRAPH-2F8E2;Lo;0;L;6885;;;;N;;;;;
+2F8E3;CJK COMPATIBILITY IDEOGRAPH-2F8E3;Lo;0;L;2346D;;;;N;;;;;
+2F8E4;CJK COMPATIBILITY IDEOGRAPH-2F8E4;Lo;0;L;688E;;;;N;;;;;
+2F8E5;CJK COMPATIBILITY IDEOGRAPH-2F8E5;Lo;0;L;681F;;;;N;;;;;
+2F8E6;CJK COMPATIBILITY IDEOGRAPH-2F8E6;Lo;0;L;6914;;;;N;;;;;
+2F8E7;CJK COMPATIBILITY IDEOGRAPH-2F8E7;Lo;0;L;3B9D;;;;N;;;;;
+2F8E8;CJK COMPATIBILITY IDEOGRAPH-2F8E8;Lo;0;L;6942;;;;N;;;;;
+2F8E9;CJK COMPATIBILITY IDEOGRAPH-2F8E9;Lo;0;L;69A3;;;;N;;;;;
+2F8EA;CJK COMPATIBILITY IDEOGRAPH-2F8EA;Lo;0;L;69EA;;;;N;;;;;
+2F8EB;CJK COMPATIBILITY IDEOGRAPH-2F8EB;Lo;0;L;6AA8;;;;N;;;;;
+2F8EC;CJK COMPATIBILITY IDEOGRAPH-2F8EC;Lo;0;L;236A3;;;;N;;;;;
+2F8ED;CJK COMPATIBILITY IDEOGRAPH-2F8ED;Lo;0;L;6ADB;;;;N;;;;;
+2F8EE;CJK COMPATIBILITY IDEOGRAPH-2F8EE;Lo;0;L;3C18;;;;N;;;;;
+2F8EF;CJK COMPATIBILITY IDEOGRAPH-2F8EF;Lo;0;L;6B21;;;;N;;;;;
+2F8F0;CJK COMPATIBILITY IDEOGRAPH-2F8F0;Lo;0;L;238A7;;;;N;;;;;
+2F8F1;CJK COMPATIBILITY IDEOGRAPH-2F8F1;Lo;0;L;6B54;;;;N;;;;;
+2F8F2;CJK COMPATIBILITY IDEOGRAPH-2F8F2;Lo;0;L;3C4E;;;;N;;;;;
+2F8F3;CJK COMPATIBILITY IDEOGRAPH-2F8F3;Lo;0;L;6B72;;;;N;;;;;
+2F8F4;CJK COMPATIBILITY IDEOGRAPH-2F8F4;Lo;0;L;6B9F;;;;N;;;;;
+2F8F5;CJK COMPATIBILITY IDEOGRAPH-2F8F5;Lo;0;L;6BBA;;;;N;;;;;
+2F8F6;CJK COMPATIBILITY IDEOGRAPH-2F8F6;Lo;0;L;6BBB;;;;N;;;;;
+2F8F7;CJK COMPATIBILITY IDEOGRAPH-2F8F7;Lo;0;L;23A8D;;;;N;;;;;
+2F8F8;CJK COMPATIBILITY IDEOGRAPH-2F8F8;Lo;0;L;21D0B;;;;N;;;;;
+2F8F9;CJK COMPATIBILITY IDEOGRAPH-2F8F9;Lo;0;L;23AFA;;;;N;;;;;
+2F8FA;CJK COMPATIBILITY IDEOGRAPH-2F8FA;Lo;0;L;6C4E;;;;N;;;;;
+2F8FB;CJK COMPATIBILITY IDEOGRAPH-2F8FB;Lo;0;L;23CBC;;;;N;;;;;
+2F8FC;CJK COMPATIBILITY IDEOGRAPH-2F8FC;Lo;0;L;6CBF;;;;N;;;;;
+2F8FD;CJK COMPATIBILITY IDEOGRAPH-2F8FD;Lo;0;L;6CCD;;;;N;;;;;
+2F8FE;CJK COMPATIBILITY IDEOGRAPH-2F8FE;Lo;0;L;6C67;;;;N;;;;;
+2F8FF;CJK COMPATIBILITY IDEOGRAPH-2F8FF;Lo;0;L;6D16;;;;N;;;;;
+2F900;CJK COMPATIBILITY IDEOGRAPH-2F900;Lo;0;L;6D3E;;;;N;;;;;
+2F901;CJK COMPATIBILITY IDEOGRAPH-2F901;Lo;0;L;6D77;;;;N;;;;;
+2F902;CJK COMPATIBILITY IDEOGRAPH-2F902;Lo;0;L;6D41;;;;N;;;;;
+2F903;CJK COMPATIBILITY IDEOGRAPH-2F903;Lo;0;L;6D69;;;;N;;;;;
+2F904;CJK COMPATIBILITY IDEOGRAPH-2F904;Lo;0;L;6D78;;;;N;;;;;
+2F905;CJK COMPATIBILITY IDEOGRAPH-2F905;Lo;0;L;6D85;;;;N;;;;;
+2F906;CJK COMPATIBILITY IDEOGRAPH-2F906;Lo;0;L;23D1E;;;;N;;;;;
+2F907;CJK COMPATIBILITY IDEOGRAPH-2F907;Lo;0;L;6D34;;;;N;;;;;
+2F908;CJK COMPATIBILITY IDEOGRAPH-2F908;Lo;0;L;6E2F;;;;N;;;;;
+2F909;CJK COMPATIBILITY IDEOGRAPH-2F909;Lo;0;L;6E6E;;;;N;;;;;
+2F90A;CJK COMPATIBILITY IDEOGRAPH-2F90A;Lo;0;L;3D33;;;;N;;;;;
+2F90B;CJK COMPATIBILITY IDEOGRAPH-2F90B;Lo;0;L;6ECB;;;;N;;;;;
+2F90C;CJK COMPATIBILITY IDEOGRAPH-2F90C;Lo;0;L;6EC7;;;;N;;;;;
+2F90D;CJK COMPATIBILITY IDEOGRAPH-2F90D;Lo;0;L;23ED1;;;;N;;;;;
+2F90E;CJK COMPATIBILITY IDEOGRAPH-2F90E;Lo;0;L;6DF9;;;;N;;;;;
+2F90F;CJK COMPATIBILITY IDEOGRAPH-2F90F;Lo;0;L;6F6E;;;;N;;;;;
+2F910;CJK COMPATIBILITY IDEOGRAPH-2F910;Lo;0;L;23F5E;;;;N;;;;;
+2F911;CJK COMPATIBILITY IDEOGRAPH-2F911;Lo;0;L;23F8E;;;;N;;;;;
+2F912;CJK COMPATIBILITY IDEOGRAPH-2F912;Lo;0;L;6FC6;;;;N;;;;;
+2F913;CJK COMPATIBILITY IDEOGRAPH-2F913;Lo;0;L;7039;;;;N;;;;;
+2F914;CJK COMPATIBILITY IDEOGRAPH-2F914;Lo;0;L;701E;;;;N;;;;;
+2F915;CJK COMPATIBILITY IDEOGRAPH-2F915;Lo;0;L;701B;;;;N;;;;;
+2F916;CJK COMPATIBILITY IDEOGRAPH-2F916;Lo;0;L;3D96;;;;N;;;;;
+2F917;CJK COMPATIBILITY IDEOGRAPH-2F917;Lo;0;L;704A;;;;N;;;;;
+2F918;CJK COMPATIBILITY IDEOGRAPH-2F918;Lo;0;L;707D;;;;N;;;;;
+2F919;CJK COMPATIBILITY IDEOGRAPH-2F919;Lo;0;L;7077;;;;N;;;;;
+2F91A;CJK COMPATIBILITY IDEOGRAPH-2F91A;Lo;0;L;70AD;;;;N;;;;;
+2F91B;CJK COMPATIBILITY IDEOGRAPH-2F91B;Lo;0;L;20525;;;;N;;;;;
+2F91C;CJK COMPATIBILITY IDEOGRAPH-2F91C;Lo;0;L;7145;;;;N;;;;;
+2F91D;CJK COMPATIBILITY IDEOGRAPH-2F91D;Lo;0;L;24263;;;;N;;;;;
+2F91E;CJK COMPATIBILITY IDEOGRAPH-2F91E;Lo;0;L;719C;;;;N;;;;;
+2F91F;CJK COMPATIBILITY IDEOGRAPH-2F91F;Lo;0;L;43AB;;;;N;;;;;
+2F920;CJK COMPATIBILITY IDEOGRAPH-2F920;Lo;0;L;7228;;;;N;;;;;
+2F921;CJK COMPATIBILITY IDEOGRAPH-2F921;Lo;0;L;7235;;;;N;;;;;
+2F922;CJK COMPATIBILITY IDEOGRAPH-2F922;Lo;0;L;7250;;;;N;;;;;
+2F923;CJK COMPATIBILITY IDEOGRAPH-2F923;Lo;0;L;24608;;;;N;;;;;
+2F924;CJK COMPATIBILITY IDEOGRAPH-2F924;Lo;0;L;7280;;;;N;;;;;
+2F925;CJK COMPATIBILITY IDEOGRAPH-2F925;Lo;0;L;7295;;;;N;;;;;
+2F926;CJK COMPATIBILITY IDEOGRAPH-2F926;Lo;0;L;24735;;;;N;;;;;
+2F927;CJK COMPATIBILITY IDEOGRAPH-2F927;Lo;0;L;24814;;;;N;;;;;
+2F928;CJK COMPATIBILITY IDEOGRAPH-2F928;Lo;0;L;737A;;;;N;;;;;
+2F929;CJK COMPATIBILITY IDEOGRAPH-2F929;Lo;0;L;738B;;;;N;;;;;
+2F92A;CJK COMPATIBILITY IDEOGRAPH-2F92A;Lo;0;L;3EAC;;;;N;;;;;
+2F92B;CJK COMPATIBILITY IDEOGRAPH-2F92B;Lo;0;L;73A5;;;;N;;;;;
+2F92C;CJK COMPATIBILITY IDEOGRAPH-2F92C;Lo;0;L;3EB8;;;;N;;;;;
+2F92D;CJK COMPATIBILITY IDEOGRAPH-2F92D;Lo;0;L;3EB8;;;;N;;;;;
+2F92E;CJK COMPATIBILITY IDEOGRAPH-2F92E;Lo;0;L;7447;;;;N;;;;;
+2F92F;CJK COMPATIBILITY IDEOGRAPH-2F92F;Lo;0;L;745C;;;;N;;;;;
+2F930;CJK COMPATIBILITY IDEOGRAPH-2F930;Lo;0;L;7471;;;;N;;;;;
+2F931;CJK COMPATIBILITY IDEOGRAPH-2F931;Lo;0;L;7485;;;;N;;;;;
+2F932;CJK COMPATIBILITY IDEOGRAPH-2F932;Lo;0;L;74CA;;;;N;;;;;
+2F933;CJK COMPATIBILITY IDEOGRAPH-2F933;Lo;0;L;3F1B;;;;N;;;;;
+2F934;CJK COMPATIBILITY IDEOGRAPH-2F934;Lo;0;L;7524;;;;N;;;;;
+2F935;CJK COMPATIBILITY IDEOGRAPH-2F935;Lo;0;L;24C36;;;;N;;;;;
+2F936;CJK COMPATIBILITY IDEOGRAPH-2F936;Lo;0;L;753E;;;;N;;;;;
+2F937;CJK COMPATIBILITY IDEOGRAPH-2F937;Lo;0;L;24C92;;;;N;;;;;
+2F938;CJK COMPATIBILITY IDEOGRAPH-2F938;Lo;0;L;7570;;;;N;;;;;
+2F939;CJK COMPATIBILITY IDEOGRAPH-2F939;Lo;0;L;2219F;;;;N;;;;;
+2F93A;CJK COMPATIBILITY IDEOGRAPH-2F93A;Lo;0;L;7610;;;;N;;;;;
+2F93B;CJK COMPATIBILITY IDEOGRAPH-2F93B;Lo;0;L;24FA1;;;;N;;;;;
+2F93C;CJK COMPATIBILITY IDEOGRAPH-2F93C;Lo;0;L;24FB8;;;;N;;;;;
+2F93D;CJK COMPATIBILITY IDEOGRAPH-2F93D;Lo;0;L;25044;;;;N;;;;;
+2F93E;CJK COMPATIBILITY IDEOGRAPH-2F93E;Lo;0;L;3FFC;;;;N;;;;;
+2F93F;CJK COMPATIBILITY IDEOGRAPH-2F93F;Lo;0;L;4008;;;;N;;;;;
+2F940;CJK COMPATIBILITY IDEOGRAPH-2F940;Lo;0;L;76F4;;;;N;;;;;
+2F941;CJK COMPATIBILITY IDEOGRAPH-2F941;Lo;0;L;250F3;;;;N;;;;;
+2F942;CJK COMPATIBILITY IDEOGRAPH-2F942;Lo;0;L;250F2;;;;N;;;;;
+2F943;CJK COMPATIBILITY IDEOGRAPH-2F943;Lo;0;L;25119;;;;N;;;;;
+2F944;CJK COMPATIBILITY IDEOGRAPH-2F944;Lo;0;L;25133;;;;N;;;;;
+2F945;CJK COMPATIBILITY IDEOGRAPH-2F945;Lo;0;L;771E;;;;N;;;;;
+2F946;CJK COMPATIBILITY IDEOGRAPH-2F946;Lo;0;L;771F;;;;N;;;;;
+2F947;CJK COMPATIBILITY IDEOGRAPH-2F947;Lo;0;L;771F;;;;N;;;;;
+2F948;CJK COMPATIBILITY IDEOGRAPH-2F948;Lo;0;L;774A;;;;N;;;;;
+2F949;CJK COMPATIBILITY IDEOGRAPH-2F949;Lo;0;L;4039;;;;N;;;;;
+2F94A;CJK COMPATIBILITY IDEOGRAPH-2F94A;Lo;0;L;778B;;;;N;;;;;
+2F94B;CJK COMPATIBILITY IDEOGRAPH-2F94B;Lo;0;L;4046;;;;N;;;;;
+2F94C;CJK COMPATIBILITY IDEOGRAPH-2F94C;Lo;0;L;4096;;;;N;;;;;
+2F94D;CJK COMPATIBILITY IDEOGRAPH-2F94D;Lo;0;L;2541D;;;;N;;;;;
+2F94E;CJK COMPATIBILITY IDEOGRAPH-2F94E;Lo;0;L;784E;;;;N;;;;;
+2F94F;CJK COMPATIBILITY IDEOGRAPH-2F94F;Lo;0;L;788C;;;;N;;;;;
+2F950;CJK COMPATIBILITY IDEOGRAPH-2F950;Lo;0;L;78CC;;;;N;;;;;
+2F951;CJK COMPATIBILITY IDEOGRAPH-2F951;Lo;0;L;40E3;;;;N;;;;;
+2F952;CJK COMPATIBILITY IDEOGRAPH-2F952;Lo;0;L;25626;;;;N;;;;;
+2F953;CJK COMPATIBILITY IDEOGRAPH-2F953;Lo;0;L;7956;;;;N;;;;;
+2F954;CJK COMPATIBILITY IDEOGRAPH-2F954;Lo;0;L;2569A;;;;N;;;;;
+2F955;CJK COMPATIBILITY IDEOGRAPH-2F955;Lo;0;L;256C5;;;;N;;;;;
+2F956;CJK COMPATIBILITY IDEOGRAPH-2F956;Lo;0;L;798F;;;;N;;;;;
+2F957;CJK COMPATIBILITY IDEOGRAPH-2F957;Lo;0;L;79EB;;;;N;;;;;
+2F958;CJK COMPATIBILITY IDEOGRAPH-2F958;Lo;0;L;412F;;;;N;;;;;
+2F959;CJK COMPATIBILITY IDEOGRAPH-2F959;Lo;0;L;7A40;;;;N;;;;;
+2F95A;CJK COMPATIBILITY IDEOGRAPH-2F95A;Lo;0;L;7A4A;;;;N;;;;;
+2F95B;CJK COMPATIBILITY IDEOGRAPH-2F95B;Lo;0;L;7A4F;;;;N;;;;;
+2F95C;CJK COMPATIBILITY IDEOGRAPH-2F95C;Lo;0;L;2597C;;;;N;;;;;
+2F95D;CJK COMPATIBILITY IDEOGRAPH-2F95D;Lo;0;L;25AA7;;;;N;;;;;
+2F95E;CJK COMPATIBILITY IDEOGRAPH-2F95E;Lo;0;L;25AA7;;;;N;;;;;
+2F95F;CJK COMPATIBILITY IDEOGRAPH-2F95F;Lo;0;L;7AAE;;;;N;;;;;
+2F960;CJK COMPATIBILITY IDEOGRAPH-2F960;Lo;0;L;4202;;;;N;;;;;
+2F961;CJK COMPATIBILITY IDEOGRAPH-2F961;Lo;0;L;25BAB;;;;N;;;;;
+2F962;CJK COMPATIBILITY IDEOGRAPH-2F962;Lo;0;L;7BC6;;;;N;;;;;
+2F963;CJK COMPATIBILITY IDEOGRAPH-2F963;Lo;0;L;7BC9;;;;N;;;;;
+2F964;CJK COMPATIBILITY IDEOGRAPH-2F964;Lo;0;L;4227;;;;N;;;;;
+2F965;CJK COMPATIBILITY IDEOGRAPH-2F965;Lo;0;L;25C80;;;;N;;;;;
+2F966;CJK COMPATIBILITY IDEOGRAPH-2F966;Lo;0;L;7CD2;;;;N;;;;;
+2F967;CJK COMPATIBILITY IDEOGRAPH-2F967;Lo;0;L;42A0;;;;N;;;;;
+2F968;CJK COMPATIBILITY IDEOGRAPH-2F968;Lo;0;L;7CE8;;;;N;;;;;
+2F969;CJK COMPATIBILITY IDEOGRAPH-2F969;Lo;0;L;7CE3;;;;N;;;;;
+2F96A;CJK COMPATIBILITY IDEOGRAPH-2F96A;Lo;0;L;7D00;;;;N;;;;;
+2F96B;CJK COMPATIBILITY IDEOGRAPH-2F96B;Lo;0;L;25F86;;;;N;;;;;
+2F96C;CJK COMPATIBILITY IDEOGRAPH-2F96C;Lo;0;L;7D63;;;;N;;;;;
+2F96D;CJK COMPATIBILITY IDEOGRAPH-2F96D;Lo;0;L;4301;;;;N;;;;;
+2F96E;CJK COMPATIBILITY IDEOGRAPH-2F96E;Lo;0;L;7DC7;;;;N;;;;;
+2F96F;CJK COMPATIBILITY IDEOGRAPH-2F96F;Lo;0;L;7E02;;;;N;;;;;
+2F970;CJK COMPATIBILITY IDEOGRAPH-2F970;Lo;0;L;7E45;;;;N;;;;;
+2F971;CJK COMPATIBILITY IDEOGRAPH-2F971;Lo;0;L;4334;;;;N;;;;;
+2F972;CJK COMPATIBILITY IDEOGRAPH-2F972;Lo;0;L;26228;;;;N;;;;;
+2F973;CJK COMPATIBILITY IDEOGRAPH-2F973;Lo;0;L;26247;;;;N;;;;;
+2F974;CJK COMPATIBILITY IDEOGRAPH-2F974;Lo;0;L;4359;;;;N;;;;;
+2F975;CJK COMPATIBILITY IDEOGRAPH-2F975;Lo;0;L;262D9;;;;N;;;;;
+2F976;CJK COMPATIBILITY IDEOGRAPH-2F976;Lo;0;L;7F7A;;;;N;;;;;
+2F977;CJK COMPATIBILITY IDEOGRAPH-2F977;Lo;0;L;2633E;;;;N;;;;;
+2F978;CJK COMPATIBILITY IDEOGRAPH-2F978;Lo;0;L;7F95;;;;N;;;;;
+2F979;CJK COMPATIBILITY IDEOGRAPH-2F979;Lo;0;L;7FFA;;;;N;;;;;
+2F97A;CJK COMPATIBILITY IDEOGRAPH-2F97A;Lo;0;L;8005;;;;N;;;;;
+2F97B;CJK COMPATIBILITY IDEOGRAPH-2F97B;Lo;0;L;264DA;;;;N;;;;;
+2F97C;CJK COMPATIBILITY IDEOGRAPH-2F97C;Lo;0;L;26523;;;;N;;;;;
+2F97D;CJK COMPATIBILITY IDEOGRAPH-2F97D;Lo;0;L;8060;;;;N;;;;;
+2F97E;CJK COMPATIBILITY IDEOGRAPH-2F97E;Lo;0;L;265A8;;;;N;;;;;
+2F97F;CJK COMPATIBILITY IDEOGRAPH-2F97F;Lo;0;L;8070;;;;N;;;;;
+2F980;CJK COMPATIBILITY IDEOGRAPH-2F980;Lo;0;L;2335F;;;;N;;;;;
+2F981;CJK COMPATIBILITY IDEOGRAPH-2F981;Lo;0;L;43D5;;;;N;;;;;
+2F982;CJK COMPATIBILITY IDEOGRAPH-2F982;Lo;0;L;80B2;;;;N;;;;;
+2F983;CJK COMPATIBILITY IDEOGRAPH-2F983;Lo;0;L;8103;;;;N;;;;;
+2F984;CJK COMPATIBILITY IDEOGRAPH-2F984;Lo;0;L;440B;;;;N;;;;;
+2F985;CJK COMPATIBILITY IDEOGRAPH-2F985;Lo;0;L;813E;;;;N;;;;;
+2F986;CJK COMPATIBILITY IDEOGRAPH-2F986;Lo;0;L;5AB5;;;;N;;;;;
+2F987;CJK COMPATIBILITY IDEOGRAPH-2F987;Lo;0;L;267A7;;;;N;;;;;
+2F988;CJK COMPATIBILITY IDEOGRAPH-2F988;Lo;0;L;267B5;;;;N;;;;;
+2F989;CJK COMPATIBILITY IDEOGRAPH-2F989;Lo;0;L;23393;;;;N;;;;;
+2F98A;CJK COMPATIBILITY IDEOGRAPH-2F98A;Lo;0;L;2339C;;;;N;;;;;
+2F98B;CJK COMPATIBILITY IDEOGRAPH-2F98B;Lo;0;L;8201;;;;N;;;;;
+2F98C;CJK COMPATIBILITY IDEOGRAPH-2F98C;Lo;0;L;8204;;;;N;;;;;
+2F98D;CJK COMPATIBILITY IDEOGRAPH-2F98D;Lo;0;L;8F9E;;;;N;;;;;
+2F98E;CJK COMPATIBILITY IDEOGRAPH-2F98E;Lo;0;L;446B;;;;N;;;;;
+2F98F;CJK COMPATIBILITY IDEOGRAPH-2F98F;Lo;0;L;8291;;;;N;;;;;
+2F990;CJK COMPATIBILITY IDEOGRAPH-2F990;Lo;0;L;828B;;;;N;;;;;
+2F991;CJK COMPATIBILITY IDEOGRAPH-2F991;Lo;0;L;829D;;;;N;;;;;
+2F992;CJK COMPATIBILITY IDEOGRAPH-2F992;Lo;0;L;52B3;;;;N;;;;;
+2F993;CJK COMPATIBILITY IDEOGRAPH-2F993;Lo;0;L;82B1;;;;N;;;;;
+2F994;CJK COMPATIBILITY IDEOGRAPH-2F994;Lo;0;L;82B3;;;;N;;;;;
+2F995;CJK COMPATIBILITY IDEOGRAPH-2F995;Lo;0;L;82BD;;;;N;;;;;
+2F996;CJK COMPATIBILITY IDEOGRAPH-2F996;Lo;0;L;82E6;;;;N;;;;;
+2F997;CJK COMPATIBILITY IDEOGRAPH-2F997;Lo;0;L;26B3C;;;;N;;;;;
+2F998;CJK COMPATIBILITY IDEOGRAPH-2F998;Lo;0;L;82E5;;;;N;;;;;
+2F999;CJK COMPATIBILITY IDEOGRAPH-2F999;Lo;0;L;831D;;;;N;;;;;
+2F99A;CJK COMPATIBILITY IDEOGRAPH-2F99A;Lo;0;L;8363;;;;N;;;;;
+2F99B;CJK COMPATIBILITY IDEOGRAPH-2F99B;Lo;0;L;83AD;;;;N;;;;;
+2F99C;CJK COMPATIBILITY IDEOGRAPH-2F99C;Lo;0;L;8323;;;;N;;;;;
+2F99D;CJK COMPATIBILITY IDEOGRAPH-2F99D;Lo;0;L;83BD;;;;N;;;;;
+2F99E;CJK COMPATIBILITY IDEOGRAPH-2F99E;Lo;0;L;83E7;;;;N;;;;;
+2F99F;CJK COMPATIBILITY IDEOGRAPH-2F99F;Lo;0;L;8457;;;;N;;;;;
+2F9A0;CJK COMPATIBILITY IDEOGRAPH-2F9A0;Lo;0;L;8353;;;;N;;;;;
+2F9A1;CJK COMPATIBILITY IDEOGRAPH-2F9A1;Lo;0;L;83CA;;;;N;;;;;
+2F9A2;CJK COMPATIBILITY IDEOGRAPH-2F9A2;Lo;0;L;83CC;;;;N;;;;;
+2F9A3;CJK COMPATIBILITY IDEOGRAPH-2F9A3;Lo;0;L;83DC;;;;N;;;;;
+2F9A4;CJK COMPATIBILITY IDEOGRAPH-2F9A4;Lo;0;L;26C36;;;;N;;;;;
+2F9A5;CJK COMPATIBILITY IDEOGRAPH-2F9A5;Lo;0;L;26D6B;;;;N;;;;;
+2F9A6;CJK COMPATIBILITY IDEOGRAPH-2F9A6;Lo;0;L;26CD5;;;;N;;;;;
+2F9A7;CJK COMPATIBILITY IDEOGRAPH-2F9A7;Lo;0;L;452B;;;;N;;;;;
+2F9A8;CJK COMPATIBILITY IDEOGRAPH-2F9A8;Lo;0;L;84F1;;;;N;;;;;
+2F9A9;CJK COMPATIBILITY IDEOGRAPH-2F9A9;Lo;0;L;84F3;;;;N;;;;;
+2F9AA;CJK COMPATIBILITY IDEOGRAPH-2F9AA;Lo;0;L;8516;;;;N;;;;;
+2F9AB;CJK COMPATIBILITY IDEOGRAPH-2F9AB;Lo;0;L;273CA;;;;N;;;;;
+2F9AC;CJK COMPATIBILITY IDEOGRAPH-2F9AC;Lo;0;L;8564;;;;N;;;;;
+2F9AD;CJK COMPATIBILITY IDEOGRAPH-2F9AD;Lo;0;L;26F2C;;;;N;;;;;
+2F9AE;CJK COMPATIBILITY IDEOGRAPH-2F9AE;Lo;0;L;455D;;;;N;;;;;
+2F9AF;CJK COMPATIBILITY IDEOGRAPH-2F9AF;Lo;0;L;4561;;;;N;;;;;
+2F9B0;CJK COMPATIBILITY IDEOGRAPH-2F9B0;Lo;0;L;26FB1;;;;N;;;;;
+2F9B1;CJK COMPATIBILITY IDEOGRAPH-2F9B1;Lo;0;L;270D2;;;;N;;;;;
+2F9B2;CJK COMPATIBILITY IDEOGRAPH-2F9B2;Lo;0;L;456B;;;;N;;;;;
+2F9B3;CJK COMPATIBILITY IDEOGRAPH-2F9B3;Lo;0;L;8650;;;;N;;;;;
+2F9B4;CJK COMPATIBILITY IDEOGRAPH-2F9B4;Lo;0;L;865C;;;;N;;;;;
+2F9B5;CJK COMPATIBILITY IDEOGRAPH-2F9B5;Lo;0;L;8667;;;;N;;;;;
+2F9B6;CJK COMPATIBILITY IDEOGRAPH-2F9B6;Lo;0;L;8669;;;;N;;;;;
+2F9B7;CJK COMPATIBILITY IDEOGRAPH-2F9B7;Lo;0;L;86A9;;;;N;;;;;
+2F9B8;CJK COMPATIBILITY IDEOGRAPH-2F9B8;Lo;0;L;8688;;;;N;;;;;
+2F9B9;CJK COMPATIBILITY IDEOGRAPH-2F9B9;Lo;0;L;870E;;;;N;;;;;
+2F9BA;CJK COMPATIBILITY IDEOGRAPH-2F9BA;Lo;0;L;86E2;;;;N;;;;;
+2F9BB;CJK COMPATIBILITY IDEOGRAPH-2F9BB;Lo;0;L;8779;;;;N;;;;;
+2F9BC;CJK COMPATIBILITY IDEOGRAPH-2F9BC;Lo;0;L;8728;;;;N;;;;;
+2F9BD;CJK COMPATIBILITY IDEOGRAPH-2F9BD;Lo;0;L;876B;;;;N;;;;;
+2F9BE;CJK COMPATIBILITY IDEOGRAPH-2F9BE;Lo;0;L;8786;;;;N;;;;;
+2F9BF;CJK COMPATIBILITY IDEOGRAPH-2F9BF;Lo;0;L;4D57;;;;N;;;;;
+2F9C0;CJK COMPATIBILITY IDEOGRAPH-2F9C0;Lo;0;L;87E1;;;;N;;;;;
+2F9C1;CJK COMPATIBILITY IDEOGRAPH-2F9C1;Lo;0;L;8801;;;;N;;;;;
+2F9C2;CJK COMPATIBILITY IDEOGRAPH-2F9C2;Lo;0;L;45F9;;;;N;;;;;
+2F9C3;CJK COMPATIBILITY IDEOGRAPH-2F9C3;Lo;0;L;8860;;;;N;;;;;
+2F9C4;CJK COMPATIBILITY IDEOGRAPH-2F9C4;Lo;0;L;8863;;;;N;;;;;
+2F9C5;CJK COMPATIBILITY IDEOGRAPH-2F9C5;Lo;0;L;27667;;;;N;;;;;
+2F9C6;CJK COMPATIBILITY IDEOGRAPH-2F9C6;Lo;0;L;88D7;;;;N;;;;;
+2F9C7;CJK COMPATIBILITY IDEOGRAPH-2F9C7;Lo;0;L;88DE;;;;N;;;;;
+2F9C8;CJK COMPATIBILITY IDEOGRAPH-2F9C8;Lo;0;L;4635;;;;N;;;;;
+2F9C9;CJK COMPATIBILITY IDEOGRAPH-2F9C9;Lo;0;L;88FA;;;;N;;;;;
+2F9CA;CJK COMPATIBILITY IDEOGRAPH-2F9CA;Lo;0;L;34BB;;;;N;;;;;
+2F9CB;CJK COMPATIBILITY IDEOGRAPH-2F9CB;Lo;0;L;278AE;;;;N;;;;;
+2F9CC;CJK COMPATIBILITY IDEOGRAPH-2F9CC;Lo;0;L;27966;;;;N;;;;;
+2F9CD;CJK COMPATIBILITY IDEOGRAPH-2F9CD;Lo;0;L;46BE;;;;N;;;;;
+2F9CE;CJK COMPATIBILITY IDEOGRAPH-2F9CE;Lo;0;L;46C7;;;;N;;;;;
+2F9CF;CJK COMPATIBILITY IDEOGRAPH-2F9CF;Lo;0;L;8AA0;;;;N;;;;;
+2F9D0;CJK COMPATIBILITY IDEOGRAPH-2F9D0;Lo;0;L;8AED;;;;N;;;;;
+2F9D1;CJK COMPATIBILITY IDEOGRAPH-2F9D1;Lo;0;L;8B8A;;;;N;;;;;
+2F9D2;CJK COMPATIBILITY IDEOGRAPH-2F9D2;Lo;0;L;8C55;;;;N;;;;;
+2F9D3;CJK COMPATIBILITY IDEOGRAPH-2F9D3;Lo;0;L;27CA8;;;;N;;;;;
+2F9D4;CJK COMPATIBILITY IDEOGRAPH-2F9D4;Lo;0;L;8CAB;;;;N;;;;;
+2F9D5;CJK COMPATIBILITY IDEOGRAPH-2F9D5;Lo;0;L;8CC1;;;;N;;;;;
+2F9D6;CJK COMPATIBILITY IDEOGRAPH-2F9D6;Lo;0;L;8D1B;;;;N;;;;;
+2F9D7;CJK COMPATIBILITY IDEOGRAPH-2F9D7;Lo;0;L;8D77;;;;N;;;;;
+2F9D8;CJK COMPATIBILITY IDEOGRAPH-2F9D8;Lo;0;L;27F2F;;;;N;;;;;
+2F9D9;CJK COMPATIBILITY IDEOGRAPH-2F9D9;Lo;0;L;20804;;;;N;;;;;
+2F9DA;CJK COMPATIBILITY IDEOGRAPH-2F9DA;Lo;0;L;8DCB;;;;N;;;;;
+2F9DB;CJK COMPATIBILITY IDEOGRAPH-2F9DB;Lo;0;L;8DBC;;;;N;;;;;
+2F9DC;CJK COMPATIBILITY IDEOGRAPH-2F9DC;Lo;0;L;8DF0;;;;N;;;;;
+2F9DD;CJK COMPATIBILITY IDEOGRAPH-2F9DD;Lo;0;L;208DE;;;;N;;;;;
+2F9DE;CJK COMPATIBILITY IDEOGRAPH-2F9DE;Lo;0;L;8ED4;;;;N;;;;;
+2F9DF;CJK COMPATIBILITY IDEOGRAPH-2F9DF;Lo;0;L;8F38;;;;N;;;;;
+2F9E0;CJK COMPATIBILITY IDEOGRAPH-2F9E0;Lo;0;L;285D2;;;;N;;;;;
+2F9E1;CJK COMPATIBILITY IDEOGRAPH-2F9E1;Lo;0;L;285ED;;;;N;;;;;
+2F9E2;CJK COMPATIBILITY IDEOGRAPH-2F9E2;Lo;0;L;9094;;;;N;;;;;
+2F9E3;CJK COMPATIBILITY IDEOGRAPH-2F9E3;Lo;0;L;90F1;;;;N;;;;;
+2F9E4;CJK COMPATIBILITY IDEOGRAPH-2F9E4;Lo;0;L;9111;;;;N;;;;;
+2F9E5;CJK COMPATIBILITY IDEOGRAPH-2F9E5;Lo;0;L;2872E;;;;N;;;;;
+2F9E6;CJK COMPATIBILITY IDEOGRAPH-2F9E6;Lo;0;L;911B;;;;N;;;;;
+2F9E7;CJK COMPATIBILITY IDEOGRAPH-2F9E7;Lo;0;L;9238;;;;N;;;;;
+2F9E8;CJK COMPATIBILITY IDEOGRAPH-2F9E8;Lo;0;L;92D7;;;;N;;;;;
+2F9E9;CJK COMPATIBILITY IDEOGRAPH-2F9E9;Lo;0;L;92D8;;;;N;;;;;
+2F9EA;CJK COMPATIBILITY IDEOGRAPH-2F9EA;Lo;0;L;927C;;;;N;;;;;
+2F9EB;CJK COMPATIBILITY IDEOGRAPH-2F9EB;Lo;0;L;93F9;;;;N;;;;;
+2F9EC;CJK COMPATIBILITY IDEOGRAPH-2F9EC;Lo;0;L;9415;;;;N;;;;;
+2F9ED;CJK COMPATIBILITY IDEOGRAPH-2F9ED;Lo;0;L;28BFA;;;;N;;;;;
+2F9EE;CJK COMPATIBILITY IDEOGRAPH-2F9EE;Lo;0;L;958B;;;;N;;;;;
+2F9EF;CJK COMPATIBILITY IDEOGRAPH-2F9EF;Lo;0;L;4995;;;;N;;;;;
+2F9F0;CJK COMPATIBILITY IDEOGRAPH-2F9F0;Lo;0;L;95B7;;;;N;;;;;
+2F9F1;CJK COMPATIBILITY IDEOGRAPH-2F9F1;Lo;0;L;28D77;;;;N;;;;;
+2F9F2;CJK COMPATIBILITY IDEOGRAPH-2F9F2;Lo;0;L;49E6;;;;N;;;;;
+2F9F3;CJK COMPATIBILITY IDEOGRAPH-2F9F3;Lo;0;L;96C3;;;;N;;;;;
+2F9F4;CJK COMPATIBILITY IDEOGRAPH-2F9F4;Lo;0;L;5DB2;;;;N;;;;;
+2F9F5;CJK COMPATIBILITY IDEOGRAPH-2F9F5;Lo;0;L;9723;;;;N;;;;;
+2F9F6;CJK COMPATIBILITY IDEOGRAPH-2F9F6;Lo;0;L;29145;;;;N;;;;;
+2F9F7;CJK COMPATIBILITY IDEOGRAPH-2F9F7;Lo;0;L;2921A;;;;N;;;;;
+2F9F8;CJK COMPATIBILITY IDEOGRAPH-2F9F8;Lo;0;L;4A6E;;;;N;;;;;
+2F9F9;CJK COMPATIBILITY IDEOGRAPH-2F9F9;Lo;0;L;4A76;;;;N;;;;;
+2F9FA;CJK COMPATIBILITY IDEOGRAPH-2F9FA;Lo;0;L;97E0;;;;N;;;;;
+2F9FB;CJK COMPATIBILITY IDEOGRAPH-2F9FB;Lo;0;L;2940A;;;;N;;;;;
+2F9FC;CJK COMPATIBILITY IDEOGRAPH-2F9FC;Lo;0;L;4AB2;;;;N;;;;;
+2F9FD;CJK COMPATIBILITY IDEOGRAPH-2F9FD;Lo;0;L;29496;;;;N;;;;;
+2F9FE;CJK COMPATIBILITY IDEOGRAPH-2F9FE;Lo;0;L;980B;;;;N;;;;;
+2F9FF;CJK COMPATIBILITY IDEOGRAPH-2F9FF;Lo;0;L;980B;;;;N;;;;;
+2FA00;CJK COMPATIBILITY IDEOGRAPH-2FA00;Lo;0;L;9829;;;;N;;;;;
+2FA01;CJK COMPATIBILITY IDEOGRAPH-2FA01;Lo;0;L;295B6;;;;N;;;;;
+2FA02;CJK COMPATIBILITY IDEOGRAPH-2FA02;Lo;0;L;98E2;;;;N;;;;;
+2FA03;CJK COMPATIBILITY IDEOGRAPH-2FA03;Lo;0;L;4B33;;;;N;;;;;
+2FA04;CJK COMPATIBILITY IDEOGRAPH-2FA04;Lo;0;L;9929;;;;N;;;;;
+2FA05;CJK COMPATIBILITY IDEOGRAPH-2FA05;Lo;0;L;99A7;;;;N;;;;;
+2FA06;CJK COMPATIBILITY IDEOGRAPH-2FA06;Lo;0;L;99C2;;;;N;;;;;
+2FA07;CJK COMPATIBILITY IDEOGRAPH-2FA07;Lo;0;L;99FE;;;;N;;;;;
+2FA08;CJK COMPATIBILITY IDEOGRAPH-2FA08;Lo;0;L;4BCE;;;;N;;;;;
+2FA09;CJK COMPATIBILITY IDEOGRAPH-2FA09;Lo;0;L;29B30;;;;N;;;;;
+2FA0A;CJK COMPATIBILITY IDEOGRAPH-2FA0A;Lo;0;L;9B12;;;;N;;;;;
+2FA0B;CJK COMPATIBILITY IDEOGRAPH-2FA0B;Lo;0;L;9C40;;;;N;;;;;
+2FA0C;CJK COMPATIBILITY IDEOGRAPH-2FA0C;Lo;0;L;9CFD;;;;N;;;;;
+2FA0D;CJK COMPATIBILITY IDEOGRAPH-2FA0D;Lo;0;L;4CCE;;;;N;;;;;
+2FA0E;CJK COMPATIBILITY IDEOGRAPH-2FA0E;Lo;0;L;4CED;;;;N;;;;;
+2FA0F;CJK COMPATIBILITY IDEOGRAPH-2FA0F;Lo;0;L;9D67;;;;N;;;;;
+2FA10;CJK COMPATIBILITY IDEOGRAPH-2FA10;Lo;0;L;2A0CE;;;;N;;;;;
+2FA11;CJK COMPATIBILITY IDEOGRAPH-2FA11;Lo;0;L;4CF8;;;;N;;;;;
+2FA12;CJK COMPATIBILITY IDEOGRAPH-2FA12;Lo;0;L;2A105;;;;N;;;;;
+2FA13;CJK COMPATIBILITY IDEOGRAPH-2FA13;Lo;0;L;2A20E;;;;N;;;;;
+2FA14;CJK COMPATIBILITY IDEOGRAPH-2FA14;Lo;0;L;2A291;;;;N;;;;;
+2FA15;CJK COMPATIBILITY IDEOGRAPH-2FA15;Lo;0;L;9EBB;;;;N;;;;;
+2FA16;CJK COMPATIBILITY IDEOGRAPH-2FA16;Lo;0;L;4D56;;;;N;;;;;
+2FA17;CJK COMPATIBILITY IDEOGRAPH-2FA17;Lo;0;L;9EF9;;;;N;;;;;
+2FA18;CJK COMPATIBILITY IDEOGRAPH-2FA18;Lo;0;L;9EFE;;;;N;;;;;
+2FA19;CJK COMPATIBILITY IDEOGRAPH-2FA19;Lo;0;L;9F05;;;;N;;;;;
+2FA1A;CJK COMPATIBILITY IDEOGRAPH-2FA1A;Lo;0;L;9F0F;;;;N;;;;;
+2FA1B;CJK COMPATIBILITY IDEOGRAPH-2FA1B;Lo;0;L;9F16;;;;N;;;;;
+2FA1C;CJK COMPATIBILITY IDEOGRAPH-2FA1C;Lo;0;L;9F3B;;;;N;;;;;
+2FA1D;CJK COMPATIBILITY IDEOGRAPH-2FA1D;Lo;0;L;2A600;;;;N;;;;;
+E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;;
+E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;;
+E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;;
+E0022;TAG QUOTATION MARK;Cf;0;BN;;;;;N;;;;;
+E0023;TAG NUMBER SIGN;Cf;0;BN;;;;;N;;;;;
+E0024;TAG DOLLAR SIGN;Cf;0;BN;;;;;N;;;;;
+E0025;TAG PERCENT SIGN;Cf;0;BN;;;;;N;;;;;
+E0026;TAG AMPERSAND;Cf;0;BN;;;;;N;;;;;
+E0027;TAG APOSTROPHE;Cf;0;BN;;;;;N;;;;;
+E0028;TAG LEFT PARENTHESIS;Cf;0;BN;;;;;N;;;;;
+E0029;TAG RIGHT PARENTHESIS;Cf;0;BN;;;;;N;;;;;
+E002A;TAG ASTERISK;Cf;0;BN;;;;;N;;;;;
+E002B;TAG PLUS SIGN;Cf;0;BN;;;;;N;;;;;
+E002C;TAG COMMA;Cf;0;BN;;;;;N;;;;;
+E002D;TAG HYPHEN-MINUS;Cf;0;BN;;;;;N;;;;;
+E002E;TAG FULL STOP;Cf;0;BN;;;;;N;;;;;
+E002F;TAG SOLIDUS;Cf;0;BN;;;;;N;;;;;
+E0030;TAG DIGIT ZERO;Cf;0;BN;;;;;N;;;;;
+E0031;TAG DIGIT ONE;Cf;0;BN;;;;;N;;;;;
+E0032;TAG DIGIT TWO;Cf;0;BN;;;;;N;;;;;
+E0033;TAG DIGIT THREE;Cf;0;BN;;;;;N;;;;;
+E0034;TAG DIGIT FOUR;Cf;0;BN;;;;;N;;;;;
+E0035;TAG DIGIT FIVE;Cf;0;BN;;;;;N;;;;;
+E0036;TAG DIGIT SIX;Cf;0;BN;;;;;N;;;;;
+E0037;TAG DIGIT SEVEN;Cf;0;BN;;;;;N;;;;;
+E0038;TAG DIGIT EIGHT;Cf;0;BN;;;;;N;;;;;
+E0039;TAG DIGIT NINE;Cf;0;BN;;;;;N;;;;;
+E003A;TAG COLON;Cf;0;BN;;;;;N;;;;;
+E003B;TAG SEMICOLON;Cf;0;BN;;;;;N;;;;;
+E003C;TAG LESS-THAN SIGN;Cf;0;BN;;;;;N;;;;;
+E003D;TAG EQUALS SIGN;Cf;0;BN;;;;;N;;;;;
+E003E;TAG GREATER-THAN SIGN;Cf;0;BN;;;;;N;;;;;
+E003F;TAG QUESTION MARK;Cf;0;BN;;;;;N;;;;;
+E0040;TAG COMMERCIAL AT;Cf;0;BN;;;;;N;;;;;
+E0041;TAG LATIN CAPITAL LETTER A;Cf;0;BN;;;;;N;;;;;
+E0042;TAG LATIN CAPITAL LETTER B;Cf;0;BN;;;;;N;;;;;
+E0043;TAG LATIN CAPITAL LETTER C;Cf;0;BN;;;;;N;;;;;
+E0044;TAG LATIN CAPITAL LETTER D;Cf;0;BN;;;;;N;;;;;
+E0045;TAG LATIN CAPITAL LETTER E;Cf;0;BN;;;;;N;;;;;
+E0046;TAG LATIN CAPITAL LETTER F;Cf;0;BN;;;;;N;;;;;
+E0047;TAG LATIN CAPITAL LETTER G;Cf;0;BN;;;;;N;;;;;
+E0048;TAG LATIN CAPITAL LETTER H;Cf;0;BN;;;;;N;;;;;
+E0049;TAG LATIN CAPITAL LETTER I;Cf;0;BN;;;;;N;;;;;
+E004A;TAG LATIN CAPITAL LETTER J;Cf;0;BN;;;;;N;;;;;
+E004B;TAG LATIN CAPITAL LETTER K;Cf;0;BN;;;;;N;;;;;
+E004C;TAG LATIN CAPITAL LETTER L;Cf;0;BN;;;;;N;;;;;
+E004D;TAG LATIN CAPITAL LETTER M;Cf;0;BN;;;;;N;;;;;
+E004E;TAG LATIN CAPITAL LETTER N;Cf;0;BN;;;;;N;;;;;
+E004F;TAG LATIN CAPITAL LETTER O;Cf;0;BN;;;;;N;;;;;
+E0050;TAG LATIN CAPITAL LETTER P;Cf;0;BN;;;;;N;;;;;
+E0051;TAG LATIN CAPITAL LETTER Q;Cf;0;BN;;;;;N;;;;;
+E0052;TAG LATIN CAPITAL LETTER R;Cf;0;BN;;;;;N;;;;;
+E0053;TAG LATIN CAPITAL LETTER S;Cf;0;BN;;;;;N;;;;;
+E0054;TAG LATIN CAPITAL LETTER T;Cf;0;BN;;;;;N;;;;;
+E0055;TAG LATIN CAPITAL LETTER U;Cf;0;BN;;;;;N;;;;;
+E0056;TAG LATIN CAPITAL LETTER V;Cf;0;BN;;;;;N;;;;;
+E0057;TAG LATIN CAPITAL LETTER W;Cf;0;BN;;;;;N;;;;;
+E0058;TAG LATIN CAPITAL LETTER X;Cf;0;BN;;;;;N;;;;;
+E0059;TAG LATIN CAPITAL LETTER Y;Cf;0;BN;;;;;N;;;;;
+E005A;TAG LATIN CAPITAL LETTER Z;Cf;0;BN;;;;;N;;;;;
+E005B;TAG LEFT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;;
+E005C;TAG REVERSE SOLIDUS;Cf;0;BN;;;;;N;;;;;
+E005D;TAG RIGHT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;;
+E005E;TAG CIRCUMFLEX ACCENT;Cf;0;BN;;;;;N;;;;;
+E005F;TAG LOW LINE;Cf;0;BN;;;;;N;;;;;
+E0060;TAG GRAVE ACCENT;Cf;0;BN;;;;;N;;;;;
+E0061;TAG LATIN SMALL LETTER A;Cf;0;BN;;;;;N;;;;;
+E0062;TAG LATIN SMALL LETTER B;Cf;0;BN;;;;;N;;;;;
+E0063;TAG LATIN SMALL LETTER C;Cf;0;BN;;;;;N;;;;;
+E0064;TAG LATIN SMALL LETTER D;Cf;0;BN;;;;;N;;;;;
+E0065;TAG LATIN SMALL LETTER E;Cf;0;BN;;;;;N;;;;;
+E0066;TAG LATIN SMALL LETTER F;Cf;0;BN;;;;;N;;;;;
+E0067;TAG LATIN SMALL LETTER G;Cf;0;BN;;;;;N;;;;;
+E0068;TAG LATIN SMALL LETTER H;Cf;0;BN;;;;;N;;;;;
+E0069;TAG LATIN SMALL LETTER I;Cf;0;BN;;;;;N;;;;;
+E006A;TAG LATIN SMALL LETTER J;Cf;0;BN;;;;;N;;;;;
+E006B;TAG LATIN SMALL LETTER K;Cf;0;BN;;;;;N;;;;;
+E006C;TAG LATIN SMALL LETTER L;Cf;0;BN;;;;;N;;;;;
+E006D;TAG LATIN SMALL LETTER M;Cf;0;BN;;;;;N;;;;;
+E006E;TAG LATIN SMALL LETTER N;Cf;0;BN;;;;;N;;;;;
+E006F;TAG LATIN SMALL LETTER O;Cf;0;BN;;;;;N;;;;;
+E0070;TAG LATIN SMALL LETTER P;Cf;0;BN;;;;;N;;;;;
+E0071;TAG LATIN SMALL LETTER Q;Cf;0;BN;;;;;N;;;;;
+E0072;TAG LATIN SMALL LETTER R;Cf;0;BN;;;;;N;;;;;
+E0073;TAG LATIN SMALL LETTER S;Cf;0;BN;;;;;N;;;;;
+E0074;TAG LATIN SMALL LETTER T;Cf;0;BN;;;;;N;;;;;
+E0075;TAG LATIN SMALL LETTER U;Cf;0;BN;;;;;N;;;;;
+E0076;TAG LATIN SMALL LETTER V;Cf;0;BN;;;;;N;;;;;
+E0077;TAG LATIN SMALL LETTER W;Cf;0;BN;;;;;N;;;;;
+E0078;TAG LATIN SMALL LETTER X;Cf;0;BN;;;;;N;;;;;
+E0079;TAG LATIN SMALL LETTER Y;Cf;0;BN;;;;;N;;;;;
+E007A;TAG LATIN SMALL LETTER Z;Cf;0;BN;;;;;N;;;;;
+E007B;TAG LEFT CURLY BRACKET;Cf;0;BN;;;;;N;;;;;
+E007C;TAG VERTICAL LINE;Cf;0;BN;;;;;N;;;;;
+E007D;TAG RIGHT CURLY BRACKET;Cf;0;BN;;;;;N;;;;;
+E007E;TAG TILDE;Cf;0;BN;;;;;N;;;;;
+E007F;CANCEL TAG;Cf;0;BN;;;;;N;;;;;
+F0000;<Plane 15 Private Use, First>;Co;0;L;;;;;N;;;;;
+FFFFD;<Plane 15 Private Use, Last>;Co;0;L;;;;;N;;;;;
+100000;<Plane 16 Private Use, First>;Co;0;L;;;;;N;;;;;
+10FFFD;<Plane 16 Private Use, Last>;Co;0;L;;;;;N;;;;;
+
+
diff --git a/src/pal/src/locale/unicode.cpp b/src/pal/src/locale/unicode.cpp
new file mode 100644
index 0000000000..3c119744b0
--- /dev/null
+++ b/src/pal/src/locale/unicode.cpp
@@ -0,0 +1,1026 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+unicode.cpp
+
+Abstract:
+
+Implementation of all functions related to Unicode support
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+
+#include "pal/palinternal.h"
+#include "pal/unicode_data.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/utf8.h"
+#include "pal/locale.h"
+#include "pal/cruntime.h"
+#include "pal/stackstring.hpp"
+
+#if !(HAVE_PTHREAD_RWLOCK_T || HAVE_COREFOUNDATION)
+#error Either pthread rwlocks or Core Foundation are required for Unicode support
+#endif /* !(HAVE_PTHREAD_RWLOCK_T || HAVE_COREFOUNDATION) */
+
+#include <pthread.h>
+#include <locale.h>
+#ifndef __APPLE__
+#include <libintl.h>
+#endif // __APPLE__
+#include <errno.h>
+#if HAVE_COREFOUNDATION
+#include <corefoundation/corefoundation.h>
+#endif // HAVE_COREFOUNDATION
+
+#include <debugmacrosext.h>
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(UNICODE);
+
+#if HAVE_COREFOUNDATION
+
+static CP_MAPPING CP_TO_NATIVE_TABLE[] = {
+ { 65001, kCFStringEncodingUTF8, 4, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { 1252, kCFStringEncodingWindowsLatin1, 1, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { 1251, kCFStringEncodingWindowsCyrillic, 1, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { 1253, kCFStringEncodingWindowsGreek, 1, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { 1254, kCFStringEncodingWindowsLatin5, 1, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { 1258, kCFStringEncodingWindowsVietnamese, 1, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { 932, kCFStringEncodingDOSJapanese, 2, { 129, 159, 224, 252, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { 949, kCFStringEncodingDOSKorean, 2, { 129, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { 950, kCFStringEncodingDOSChineseTrad, 2, { 129, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+};
+
+#else // HAVE_COREFOUNDATION
+
+static const CP_MAPPING CP_TO_NATIVE_TABLE[] = {
+ { 65001, "utf8", 4, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+};
+
+#endif // HAVE_COREFOUNDATION
+
+// We hardcode the system's default codepage to be UTF-8.
+// There are several reasons for this:
+// - On OSX, HFS+ file names are encoded as UTF-8.
+// - On OSX, When writing strings to the console, the Terminal.app will interpret them as UTF-8.
+// - We want Ansi marshalling to mean marshal to UTF-8 on Mac and Linux
+static const UINT PAL_ACP = 65001;
+
+#if !HAVE_COREFOUNDATION
+/*++
+Function:
+UnicodeDataComp
+This is the comparison function used by the bsearch function to search
+for unicode characters in the UnicodeData array.
+
+Parameter:
+pnKey
+The unicode character value to search for.
+elem
+A pointer to a UnicodeDataRec.
+
+Return value:
+<0 if pnKey < elem->nUnicodeValue
+0 if pnKey == elem->nUnicodeValue
+>0 if pnKey > elem->nUnicodeValue
+--*/
+static int UnicodeDataComp(const void *pnKey, const void *elem)
+{
+ WCHAR uValue = ((UnicodeDataRec*)elem)->nUnicodeValue;
+ WORD rangeValue = ((UnicodeDataRec*)elem)->rangeValue;
+
+ if (*((INT*)pnKey) < uValue)
+ {
+ return -1;
+ }
+ else
+ {
+ if (*((INT*)pnKey) > (uValue + rangeValue))
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+}
+
+/*++
+Function:
+GetUnicodeData
+This function is used to get information about a Unicode character.
+
+Parameters:
+nUnicodeValue
+The numeric value of the Unicode character to get information about.
+pDataRec
+The UnicodeDataRec to fill in with the data for the Unicode character.
+
+Return value:
+TRUE if the Unicode character was found.
+
+--*/
+BOOL GetUnicodeData(INT nUnicodeValue, UnicodeDataRec *pDataRec)
+{
+ BOOL bRet;
+ if (nUnicodeValue <= UNICODE_DATA_DIRECT_ACCESS)
+ {
+ *pDataRec = UnicodeData[nUnicodeValue];
+ bRet = TRUE;
+ }
+ else
+ {
+ UnicodeDataRec *dataRec;
+ INT nNumOfChars = UNICODE_DATA_SIZE;
+ dataRec = (UnicodeDataRec *) bsearch(&nUnicodeValue, UnicodeData, nNumOfChars,
+ sizeof(UnicodeDataRec), UnicodeDataComp);
+ if (dataRec == NULL)
+ {
+ bRet = FALSE;
+ }
+ else
+ {
+ bRet = TRUE;
+ *pDataRec = *dataRec;
+ }
+ }
+ return bRet;
+}
+#endif /* !HAVE_COREFOUNDATION */
+
+/*++
+Function:
+CODEPAGEGetData
+
+ IN UINT CodePage - The code page the caller
+ is attempting to retrieve data on.
+
+ Returns a pointer to structure, NULL otherwise.
+--*/
+const CP_MAPPING *
+CODEPAGEGetData( IN UINT CodePage )
+{
+ UINT nSize = sizeof( CP_TO_NATIVE_TABLE ) / sizeof( CP_TO_NATIVE_TABLE[ 0 ] );
+ UINT nIndex = 0;
+
+ if ( CP_ACP == CodePage )
+ {
+ CodePage = PAL_ACP;
+ }
+
+ /* checking if the CodePage is ACP and returning true if so */
+ while (nIndex < nSize)
+ {
+ if ( ( CP_TO_NATIVE_TABLE[ nIndex ] ).nCodePage == CodePage )
+ {
+ return &(CP_TO_NATIVE_TABLE[ nIndex ]);
+ }
+ nIndex++;
+ }
+ return NULL;
+}
+
+#if HAVE_COREFOUNDATION
+/*++
+Function :
+
+CODEPAGECPToCFStringEncoding - Gets the CFStringEncoding for
+the given codepage.
+
+Returns the CFStringEncoding for the given codepage.
+--*/
+CFStringEncoding CODEPAGECPToCFStringEncoding(UINT codepage)
+{
+ const CP_MAPPING *cp_mapping = CODEPAGEGetData(codepage);
+ if (cp_mapping == NULL)
+ {
+ return kCFStringEncodingInvalidId;
+ }
+ else
+ {
+ return cp_mapping->nCFEncoding;
+ }
+}
+#endif // HAVE_COREFOUNDATION
+
+/*++
+Function:
+CharNextA
+
+Parameters
+
+lpsz
+[in] Pointer to a character in a null-terminated string.
+
+Return Values
+
+A pointer to the next character in the string, or to the terminating null character if at the end of the string, indicates success.
+
+If lpsz points to the terminating null character, the return value is equal to lpsz.
+
+See MSDN doc.
+--*/
+LPSTR
+PALAPI
+CharNextA(
+ IN LPCSTR lpsz)
+{
+ LPSTR pRet;
+ PERF_ENTRY(CharNextA);
+ ENTRY("CharNextA (lpsz=%p (%s))\n", lpsz?lpsz:NULL, lpsz?lpsz:NULL);
+
+ pRet = CharNextExA(GetACP(), lpsz, 0);
+
+ LOGEXIT ("CharNextA returns LPSTR %p\n", pRet);
+ PERF_EXIT(CharNextA);
+ return pRet;
+}
+
+
+/*++
+Function:
+CharNextExA
+
+See MSDN doc.
+--*/
+LPSTR
+PALAPI
+CharNextExA(
+ IN WORD CodePage,
+ IN LPCSTR lpCurrentChar,
+ IN DWORD dwFlags)
+{
+ LPSTR pRet = (LPSTR) lpCurrentChar;
+
+ PERF_ENTRY(CharNextExA);
+ ENTRY("CharNextExA (CodePage=%hu, lpCurrentChar=%p (%s), dwFlags=%#x)\n",
+ CodePage, lpCurrentChar?lpCurrentChar:"NULL", lpCurrentChar?lpCurrentChar:"NULL", dwFlags);
+
+ if ((lpCurrentChar != NULL) && (*lpCurrentChar != 0))
+ {
+ pRet += (*(lpCurrentChar+1) != 0) &&
+ IsDBCSLeadByteEx(CodePage, *lpCurrentChar) ? 2 : 1;
+ }
+
+ LOGEXIT("CharNextExA returns LPSTR:%p (%s)\n", pRet, pRet);
+ PERF_EXIT(CharNextExA);
+ return pRet;
+}
+
+
+
+/*++
+Function:
+AreFileApisANSI
+
+The AreFileApisANSI function determines whether the file I/O functions
+are using the ANSI or OEM character set code page. This function is
+useful for 8-bit console input and output operations.
+
+Return Values
+
+If the set of file I/O functions is using the ANSI code page, the return value is nonzero.
+
+If the set of file I/O functions is using the OEM code page, the return value is zero.
+
+In the ROTOR version we always return true since there is no concept
+of OEM code pages.
+
+--*/
+BOOL
+PALAPI
+AreFileApisANSI(
+ VOID)
+{
+ PERF_ENTRY(AreFileApisANSI);
+ ENTRY("AreFileApisANSI ()\n");
+
+ LOGEXIT("AreFileApisANSI returns BOOL TRUE\n");
+ PERF_EXIT(AreFileApisANSI);
+ return TRUE;
+}
+
+
+/*++
+Function:
+GetConsoleCP
+
+See MSDN doc.
+--*/
+UINT
+PALAPI
+GetConsoleCP(
+ VOID)
+{
+ UINT nRet = 0;
+ PERF_ENTRY(GetConsoleCP);
+ ENTRY("GetConsoleCP()\n");
+
+ nRet = GetACP();
+
+ LOGEXIT("GetConsoleCP returns UINT %d\n", nRet );
+ PERF_EXIT(GetConsoleCP);
+ return nRet;
+}
+
+/*++
+Function:
+GetConsoleOutputCP
+
+See MSDN doc.
+--*/
+UINT
+PALAPI
+GetConsoleOutputCP(
+ VOID)
+{
+ UINT nRet = 0;
+ PERF_ENTRY(GetConsoleOutputCP);
+ ENTRY("GetConsoleOutputCP()\n");
+ nRet = GetACP();
+ LOGEXIT("GetConsoleOutputCP returns UINT %d \n", nRet );
+ PERF_EXIT(GetConsoleOutputCP);
+ return nRet;
+}
+
+
+/*++
+Function:
+IsValidCodePage
+
+See MSDN doc.
+
+Notes :
+"pseudo code pages", like CP_ACP, aren't considered 'valid' in this context.
+CP_UTF7 and CP_UTF8, however, *are* considered valid code pages, even though
+MSDN fails to mention them in the IsValidCodePage entry.
+Note : CP_UTF7 support isn't required for Rotor
+--*/
+BOOL
+PALAPI
+IsValidCodePage(
+ IN UINT CodePage)
+{
+ BOOL retval = FALSE;
+
+ PERF_ENTRY(IsValidCodePage);
+ ENTRY("IsValidCodePage(%d)\n", CodePage );
+
+ switch(CodePage)
+ {
+ case CP_ACP : /* fall through */
+ case CP_OEMCP : /* fall through */
+ case CP_MACCP : /* fall through */
+ case CP_THREAD_ACP:
+ /* 'pseudo code pages' : not valid */
+ retval = FALSE;
+ break;
+ case CP_UTF7:
+ /* valid in Win32, but not supported in Rotor */
+ retval = FALSE;
+ break;
+ case CP_UTF8:
+ /* valid, but not part of CODEPAGEGetData's tables */
+ retval = TRUE;
+ break;
+ default:
+ retval = (NULL != CODEPAGEGetData( CodePage ));
+ break;
+ }
+
+ LOGEXIT("IsValidCodePage returns BOOL %d\n",retval);
+ PERF_EXIT(IsValidCodePage);
+ return retval;
+}
+
+#if ENABLE_DOWNLEVEL_FOR_NLS
+/*++
+Function:
+GetStringTypeEx
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetStringTypeExW(
+ IN LCID Locale,
+ IN DWORD dwInfoType,
+ IN LPCWSTR lpSrcStr,
+ IN int cchSrc,
+ OUT LPWORD lpCharType)
+{
+
+
+ int i = 0;
+#if !HAVE_COREFOUNDATION
+ UnicodeDataRec unicodeDataRec;
+#endif /* !HAVE_COREFOUNDATION */
+ BOOL bRet = TRUE;
+ wchar_t wcstr ;
+ PERF_ENTRY(GetStringTypeExW);
+ ENTRY("GetStringTypeExW(Locale=%#x, dwInfoType=%#x, lpSrcStr=%p (%S), "
+ "cchSrc=%d, lpCharType=%p)\n",
+ Locale, dwInfoType, lpSrcStr?lpSrcStr:W16_NULLSTRING, lpSrcStr?lpSrcStr:W16_NULLSTRING, cchSrc, lpCharType);
+
+ if((Locale != LOCALE_USER_DEFAULT)||(dwInfoType != CT_CTYPE1)
+ || (cchSrc != 1) || (lpSrcStr == (LPCWSTR)lpCharType))
+ {
+ ASSERT("One of the input parameters is invalid\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ bRet = FALSE;
+ goto GetStringTypeExExit;
+ }
+
+
+ /*
+ * get length if needed...
+ */
+ if(cchSrc == -1)
+ {
+ cchSrc = PAL_wcslen(lpSrcStr);
+ }
+
+ /*
+ * Loop through each character of the source string and update
+ * lpCharType accordingly.
+ */
+ for(i = 0; i < cchSrc; i++)
+ {
+ wcstr = lpSrcStr[i];
+#if HAVE_COREFOUNDATION
+ lpCharType[i] = 0;
+ if (PAL_iswlower(wcstr))
+ {
+ lpCharType[i] |= C1_LOWER;
+ }
+ if (PAL_iswupper(wcstr))
+ {
+ lpCharType[i] |= C1_UPPER;
+ }
+ if (PAL_iswalpha(wcstr))
+ {
+ lpCharType[i] |= C1_ALPHA;
+ }
+ if (PAL_iswdigit(wcstr))
+ {
+ lpCharType[i] |= C1_DIGIT;
+ }
+ if (PAL_iswspace(wcstr))
+ {
+ lpCharType[i] |= C1_SPACE;
+ }
+ if (PAL_iswblank(wcstr))
+ {
+ lpCharType[i] |= C1_BLANK;
+ }
+ if (PAL_iswcntrl(wcstr))
+ {
+ lpCharType[i] |= C1_CNTRL;
+ }
+ if (PAL_iswpunct(wcstr))
+ {
+ lpCharType[i] |= C1_PUNCT;
+ }
+#else /* HAVE_COREFOUNDATION */
+ /*
+ * Get the unicode data record for that character.
+ */
+ if(GetUnicodeData(wcstr, &unicodeDataRec))
+ {
+ lpCharType[i] = unicodeDataRec.C1_TYPE_FLAGS;
+ }
+ else
+ {
+ lpCharType[i] = 0;
+ }
+#endif /* HAVE_COREFOUNDATION */
+ }
+
+ GetStringTypeExExit:
+ LOGEXIT("GetStringTypeEx returns BOOL %d\n", bRet);
+ PERF_EXIT(GetStringTypeExW);
+ return bRet;
+}
+#endif // ENABLE_DOWNLEVEL_FOR_NLS
+
+/*++
+Function:
+GetCPInfo
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetCPInfo(
+ IN UINT CodePage,
+ OUT LPCPINFO lpCPInfo)
+{
+ const CP_MAPPING * lpStruct = NULL;
+ BOOL bRet = FALSE;
+
+ PERF_ENTRY(GetCPInfo);
+ ENTRY("GetCPInfo(CodePage=%hu, lpCPInfo=%p)\n", CodePage, lpCPInfo);
+
+ /*check if the input code page is valid*/
+ if( CP_ACP != CodePage && !IsValidCodePage( CodePage ) )
+ {
+ /* error, invalid argument */
+ ERROR("CodePage(%d) parameter is invalid\n",CodePage);
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto done;
+ }
+
+ /*check if the lpCPInfo parameter is valid. */
+ if( !lpCPInfo )
+ {
+ /* error, invalid argument */
+ ERROR("lpCPInfo cannot be NULL\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto done;
+ }
+
+ if ( NULL != ( lpStruct = CODEPAGEGetData( CodePage ) ) )
+ {
+ lpCPInfo->MaxCharSize = lpStruct->nMaxByteSize;;
+ memcpy( lpCPInfo->LeadByte, lpStruct->LeadByte , MAX_LEADBYTES );
+
+ /* Don't need to be set, according to the spec. */
+ memset( lpCPInfo->DefaultChar, '?', MAX_DEFAULTCHAR );
+
+ bRet = TRUE;
+ }
+
+done:
+ LOGEXIT("GetCPInfo returns BOOL %d \n",bRet);
+ PERF_EXIT(GetCPInfo);
+ return bRet;
+}
+
+
+/*++
+Function:
+GetACP
+
+See MSDN doc.
+--*/
+UINT
+PALAPI
+GetACP(VOID)
+{
+ PERF_ENTRY(GetACP);
+ ENTRY("GetACP(VOID)\n");
+
+ LOGEXIT("GetACP returning UINT %d\n", PAL_ACP );
+ PERF_EXIT(GetACP);
+
+ return PAL_ACP;
+}
+
+
+/*++
+Function:
+IsDBCSLeadByteEx
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+IsDBCSLeadByteEx(
+ IN UINT CodePage,
+ IN BYTE TestChar)
+{
+ CPINFO cpinfo;
+ SIZE_T i;
+ BOOL bRet = FALSE;
+
+ PERF_ENTRY(IsDBCSLeadByteEx);
+ ENTRY("IsDBCSLeadByteEx(CodePage=%#x, TestChar=%d)\n", CodePage, TestChar);
+
+ /* Get the lead byte info with respect to the given codepage*/
+ if( !GetCPInfo( CodePage, &cpinfo ) )
+ {
+ ERROR("Error CodePage(%#x) parameter is invalid\n", CodePage );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto done;
+ }
+
+ for( i=0; i < sizeof(cpinfo.LeadByte)/sizeof(cpinfo.LeadByte[0]); i += 2 )
+ {
+ if( 0 == cpinfo.LeadByte[ i ] )
+ {
+ goto done;
+ }
+
+ /*check if the given char is in one of the lead byte ranges*/
+ if( cpinfo.LeadByte[i] <= TestChar && TestChar<= cpinfo.LeadByte[i+1] )
+ {
+ bRet = TRUE;
+ goto done;
+ }
+ }
+done:
+ LOGEXIT("IsDBCSLeadByteEx returns BOOL %d\n",bRet);
+ PERF_EXIT(IsDBCSLeadByteEx);
+ return bRet;
+}
+
+/*++
+Function:
+IsDBCSLeadByte
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+IsDBCSLeadByte(
+ IN BYTE TestChar)
+{
+ // UNIXTODO: Implement this!
+ ERROR("Needs Implementation!!!");
+ return FALSE;
+}
+
+/*++
+Function:
+MultiByteToWideChar
+
+See MSDN doc.
+
+--*/
+int
+PALAPI
+MultiByteToWideChar(
+ IN UINT CodePage,
+ IN DWORD dwFlags,
+ IN LPCSTR lpMultiByteStr,
+ IN int cbMultiByte,
+ OUT LPWSTR lpWideCharStr,
+ IN int cchWideChar)
+{
+ INT retval =0;
+#if HAVE_COREFOUNDATION
+ CFStringRef cfString = NULL;
+ CFStringEncoding cfEncoding;
+ int bytesToConvert;
+#endif /* HAVE_COREFOUNDATION */
+
+ PERF_ENTRY(MultiByteToWideChar);
+ ENTRY("MultiByteToWideChar(CodePage=%u, dwFlags=%#x, lpMultiByteStr=%p (%s),"
+ " cbMultiByte=%d, lpWideCharStr=%p, cchWideChar=%d)\n",
+ CodePage, dwFlags, lpMultiByteStr?lpMultiByteStr:"NULL", lpMultiByteStr?lpMultiByteStr:"NULL",
+ cbMultiByte, lpWideCharStr, cchWideChar);
+
+ if (dwFlags & ~(MB_ERR_INVALID_CHARS | MB_PRECOMPOSED))
+ {
+ ASSERT("Error dwFlags(0x%x) parameter is invalid\n", dwFlags);
+ SetLastError(ERROR_INVALID_FLAGS);
+ goto EXIT;
+ }
+
+ if ( (cbMultiByte == 0) || (cchWideChar < 0) ||
+ (lpMultiByteStr == NULL) ||
+ ((cchWideChar != 0) &&
+ ((lpWideCharStr == NULL) ||
+ (lpMultiByteStr == (LPSTR)lpWideCharStr))) )
+ {
+ ERROR("Error lpMultiByteStr parameters are invalid\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto EXIT;
+ }
+
+ // Use UTF8ToUnicode on all systems, since it replaces
+ // invalid characters and Core Foundation doesn't do that.
+ if (CodePage == CP_UTF8 || (CodePage == CP_ACP && GetACP() == CP_UTF8))
+ {
+ if (cbMultiByte <= -1)
+ {
+ cbMultiByte = strlen(lpMultiByteStr) + 1;
+ }
+
+ retval = UTF8ToUnicode(lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar, dwFlags);
+ goto EXIT;
+ }
+
+#if !HAVE_COREFOUNDATION
+ ERROR( "This code page is not in the system.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto EXIT;
+#else /* !HAVE_COREFOUNDATION */
+ bytesToConvert = cbMultiByte;
+ if (bytesToConvert == -1)
+ {
+ /* Plus one for the trailing '\0', which will end up
+ * in the CFString. */
+ bytesToConvert = strlen(lpMultiByteStr) + 1;
+ }
+
+ cfEncoding = CODEPAGECPToCFStringEncoding(CodePage);
+ if (cfEncoding == kCFStringEncodingInvalidId)
+ {
+ ERROR( "This code page is not in the system.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto EXIT;
+ }
+
+ cfString = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)lpMultiByteStr,
+ bytesToConvert, cfEncoding, TRUE);
+ if (cfString == NULL)
+ {
+ ERROR( "Failed to convert the string to the specified encoding.\n" );
+ SetLastError( ERROR_NO_UNICODE_TRANSLATION );
+ goto EXIT;
+ }
+
+ if (cchWideChar != 0)
+ {
+ /* Do the conversion. */
+ CFIndex length = CFStringGetLength(cfString);
+ if (length > cchWideChar)
+ {
+ ERROR("Error insufficient buffer\n");
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ retval = 0;
+ goto ReleaseString;
+ }
+ CFStringGetCharacters(cfString, CFRangeMake(0, length),
+ (UniChar*)lpWideCharStr);
+ retval = length;
+ }
+ else
+ {
+ /* Just return the number of wide characters needed. */
+ retval = CFStringGetLength(cfString);
+ }
+
+ReleaseString:
+ if (cfString != NULL)
+ {
+ CFRelease(cfString);
+ }
+#endif /* !HAVE_COREFOUNDATION */
+
+EXIT:
+
+ LOGEXIT("MultiByteToWideChar returns %d.\n",retval);
+ PERF_EXIT(MultiByteToWideChar);
+ return retval;
+}
+
+
+/*++
+Function:
+WideCharToMultiByte
+
+See MSDN doc.
+
+--*/
+int
+PALAPI
+WideCharToMultiByte(
+ IN UINT CodePage,
+ IN DWORD dwFlags,
+ IN LPCWSTR lpWideCharStr,
+ IN int cchWideChar,
+ OUT LPSTR lpMultiByteStr,
+ IN int cbMultiByte,
+ IN LPCSTR lpDefaultChar,
+ OUT LPBOOL lpUsedDefaultChar)
+{
+ INT retval =0;
+ char defaultChar = '?';
+ BOOL usedDefaultChar = FALSE;
+#if HAVE_COREFOUNDATION
+ CFStringRef cfString = NULL;
+ CFStringEncoding cfEncoding;
+ int charsToConvert;
+ CFIndex charsConverted;
+ CFIndex bytesConverted;
+#endif /* !HAVE_COREFOUNDATION */
+
+ PERF_ENTRY(WideCharToMultiByte);
+ ENTRY("WideCharToMultiByte(CodePage=%u, dwFlags=%#x, lpWideCharStr=%p (%S), "
+ "cchWideChar=%d, lpMultiByteStr=%p, cbMultiByte=%d, "
+ "lpDefaultChar=%p, lpUsedDefaultChar=%p)\n",
+ CodePage, dwFlags, lpWideCharStr?lpWideCharStr:W16_NULLSTRING, lpWideCharStr?lpWideCharStr:W16_NULLSTRING,
+ cchWideChar, lpMultiByteStr, cbMultiByte,
+ lpDefaultChar, lpUsedDefaultChar);
+
+ if (dwFlags & ~WC_NO_BEST_FIT_CHARS)
+ {
+ ERROR("dwFlags %d invalid\n", dwFlags);
+ SetLastError(ERROR_INVALID_FLAGS);
+ goto EXIT;
+ }
+
+ // No special action is needed for WC_NO_BEST_FIT_CHARS. The default
+ // behavior of this API on Unix is not to find the best fit for a unicode
+ // character that does not map directly into a code point in the given
+ // code page. The best fit functionality is not available in wctomb on Unix
+ // and is better left unimplemented for security reasons anyway.
+
+ if ((cchWideChar < -1) || (cbMultiByte < 0) ||
+ (lpWideCharStr == NULL) ||
+ ((cbMultiByte != 0) &&
+ ((lpMultiByteStr == NULL) ||
+ (lpWideCharStr == (LPWSTR)lpMultiByteStr))) )
+ {
+ ERROR("Error lpWideCharStr parameters are invalid\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto EXIT;
+ }
+
+ if (lpDefaultChar != NULL)
+ {
+ defaultChar = *lpDefaultChar;
+ }
+
+ // Use UnicodeToUTF8 on all systems because we use
+ // UTF8ToUnicode in MultiByteToWideChar() on all systems.
+ if (CodePage == CP_UTF8 || (CodePage == CP_ACP && GetACP() == CP_UTF8))
+ {
+ if (cchWideChar == -1)
+ {
+ cchWideChar = PAL_wcslen(lpWideCharStr) + 1;
+ }
+ retval = UnicodeToUTF8(lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte);
+ goto EXIT;
+ }
+
+#if HAVE_COREFOUNDATION
+ charsToConvert = cchWideChar;
+ if (charsToConvert == -1)
+ {
+ LPCWSTR ptr = lpWideCharStr;
+
+ charsToConvert = 0;
+ while(*ptr++ != 0)
+ {
+ charsToConvert++;
+ }
+ charsToConvert++; /* For the terminating '\0' */
+ }
+
+ cfEncoding = CODEPAGECPToCFStringEncoding(CodePage);
+ if (cfEncoding == kCFStringEncodingInvalidId)
+ {
+ ERROR( "This code page is not in the system.\n" );
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto EXIT;
+ }
+
+ cfString = CFStringCreateWithCharacters(kCFAllocatorDefault,
+ (const UniChar*)lpWideCharStr, charsToConvert);
+ if (cfString == NULL)
+ {
+ ERROR("CFString creation failed.\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto EXIT;
+ }
+
+ if (cbMultiByte == 0)
+ {
+ lpMultiByteStr = NULL;
+ }
+ charsConverted = CFStringGetBytes(cfString,
+ CFRangeMake(0, charsToConvert),
+ cfEncoding, '?', TRUE, (UInt8*)lpMultiByteStr,
+ cbMultiByte, &bytesConverted);
+ if (charsConverted != charsToConvert)
+ {
+ if (lpMultiByteStr != NULL)
+ {
+ // CFStringGetBytes can fail due to an insufficient buffer or for
+ // other reasons. We need to check if we're out of buffer space.
+ charsConverted = CFStringGetBytes(cfString,
+ CFRangeMake(0, charsToConvert),
+ cfEncoding, '?', TRUE, NULL,
+ 0, &bytesConverted);
+ if (cbMultiByte < bytesConverted)
+ {
+ ERROR("Insufficient buffer for CFStringGetBytes.\n");
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ goto ReleaseString;
+ }
+ }
+ ERROR("Not all characters were converted.\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto ReleaseString;
+ }
+ retval = bytesConverted;
+
+ReleaseString:
+ if (cfString != NULL)
+ {
+ CFRelease(cfString);
+ }
+#else /*HAVE_COREFOUNDATION */
+ ERROR( "This code page is not in the system.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto EXIT;
+#endif /* HAVE_COREFOUNDATION */
+
+EXIT:
+
+ if ( lpUsedDefaultChar != NULL )
+ {
+ *lpUsedDefaultChar = usedDefaultChar;
+ }
+
+ /* Flag the cases when WC_NO_BEST_FIT_CHARS was not specified
+ * but we found characters that had to be replaced with default
+ * characters. Note that Windows would have attempted to find
+ * best fit characters under these conditions and that could pose
+ * a security risk.
+ */
+ _ASSERT_MSG((dwFlags & WC_NO_BEST_FIT_CHARS) || !usedDefaultChar,
+ "WideCharToMultiByte found a string which doesn't round trip: (%p)%S "
+ "and WC_NO_BEST_FIT_CHARS was not specified\n",
+ lpWideCharStr, lpWideCharStr);
+
+ LOGEXIT("WideCharToMultiByte returns INT %d\n", retval);
+ PERF_EXIT(WideCharToMultiByte);
+ return retval;
+}
+
+extern char * g_szCoreCLRPath;
+
+/*++
+Function :
+
+PAL_BindResources - bind the resource domain to the path where the coreclr resides
+
+Returns TRUE if it succeeded, FALSE if it failed due to OOM
+--*/
+BOOL
+PALAPI
+PAL_BindResources(IN LPCSTR lpDomain)
+{
+#ifndef __APPLE__
+ _ASSERTE(g_szCoreCLRPath != NULL);
+ char * coreCLRDirectoryPath;
+ PathCharString coreCLRDirectoryPathPS;
+ int len = strlen(g_szCoreCLRPath);
+ coreCLRDirectoryPath = coreCLRDirectoryPathPS.OpenStringBuffer(len);
+ if (NULL == coreCLRDirectoryPath)
+ {
+ return FALSE;
+ }
+ DWORD size = FILEGetDirectoryFromFullPathA(g_szCoreCLRPath, len, coreCLRDirectoryPath);
+ coreCLRDirectoryPathPS.CloseBuffer(size);
+
+ LPCSTR boundPath = bindtextdomain(lpDomain, coreCLRDirectoryPath);
+
+ return boundPath != NULL;
+#else // __APPLE__
+ // UNIXTODO: Implement for OSX if necessary
+ return TRUE;
+#endif // __APPLE__
+}
+
+/*++
+Function :
+
+PAL_GetResourceString - get localized string for a specified resource.
+The string that is passed in should be the English string, since it
+will be returned if an appropriately localized version is not found.
+
+Returns number of characters retrieved, 0 if it failed.
+--*/
+int
+PALAPI
+PAL_GetResourceString(
+ IN LPCSTR lpDomain,
+ IN LPCSTR lpResourceStr,
+ OUT LPWSTR lpWideCharStr,
+ IN int cchWideChar
+ )
+{
+#ifndef __APPLE__
+ // NOTE: dgettext returns the key if it fails to locate the appropriate
+ // resource. In our case, that will be the English string.
+ LPCSTR resourceString = dgettext(lpDomain, lpResourceStr);
+#else // __APPLE__
+ // UNIXTODO: Implement for OSX using the native localization API
+
+ // This is a temporary solution until we add the real native resource support.
+ LPCSTR resourceString = lpResourceStr;
+#endif // __APPLE__
+
+ int length = strlen(resourceString);
+ return UTF8ToUnicode(lpResourceStr, length + 1, lpWideCharStr, cchWideChar, 0);
+}
diff --git a/src/pal/src/locale/unicode_data.cpp b/src/pal/src/locale/unicode_data.cpp
new file mode 100644
index 0000000000..f9ba166f11
--- /dev/null
+++ b/src/pal/src/locale/unicode_data.cpp
@@ -0,0 +1,1852 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ locale/unicode_data.c
+
+Abstract:
+
+ Data definitions.
+
+
+
+--*/
+
+#include "pal/unicode_data.h"
+
+#if !HAVE_COREFOUNDATION
+
+CONST UnicodeDataRec UnicodeData[] = {
+
+
+{ 0x0000, 0x0020, 0x0000, 0 },
+{ 0x0001, 0x0020, 0x0000, 0 },
+{ 0x0002, 0x0020, 0x0000, 0 },
+{ 0x0003, 0x0020, 0x0000, 0 },
+{ 0x0004, 0x0020, 0x0000, 0 },
+{ 0x0005, 0x0020, 0x0000, 0 },
+{ 0x0006, 0x0020, 0x0000, 0 },
+{ 0x0007, 0x0020, 0x0000, 0 },
+{ 0x0008, 0x0020, 0x0000, 0 },
+{ 0x0009, 0x0068, 0x0000, 0 },
+{ 0x000a, 0x0028, 0x0000, 0 },
+{ 0x000b, 0x0028, 0x0000, 0 },
+{ 0x000c, 0x0028, 0x0000, 0 },
+{ 0x000d, 0x0028, 0x0000, 0 },
+{ 0x000e, 0x0020, 0x0000, 0 },
+{ 0x000f, 0x0020, 0x0000, 0 },
+{ 0x0010, 0x0020, 0x0000, 0 },
+{ 0x0011, 0x0020, 0x0000, 0 },
+{ 0x0012, 0x0020, 0x0000, 0 },
+{ 0x0013, 0x0020, 0x0000, 0 },
+{ 0x0014, 0x0020, 0x0000, 0 },
+{ 0x0015, 0x0020, 0x0000, 0 },
+{ 0x0016, 0x0020, 0x0000, 0 },
+{ 0x0017, 0x0020, 0x0000, 0 },
+{ 0x0018, 0x0020, 0x0000, 0 },
+{ 0x0019, 0x0020, 0x0000, 0 },
+{ 0x001a, 0x0020, 0x0000, 0 },
+{ 0x001b, 0x0020, 0x0000, 0 },
+{ 0x001c, 0x0020, 0x0000, 0 },
+{ 0x001d, 0x0020, 0x0000, 0 },
+{ 0x001e, 0x0020, 0x0000, 0 },
+{ 0x001f, 0x0020, 0x0000, 0 },
+{ 0x0020, 0x0048, 0x0000, 0 },
+{ 0x0021, 0x0010, 0x0000, 0 },
+{ 0x0022, 0x0010, 0x0000, 0 },
+{ 0x0023, 0x0010, 0x0000, 0 },
+{ 0x0024, 0x0010, 0x0000, 0 },
+{ 0x0025, 0x0010, 0x0000, 0 },
+{ 0x0026, 0x0010, 0x0000, 0 },
+{ 0x0027, 0x0010, 0x0000, 0 },
+{ 0x0028, 0x0010, 0x0000, 0 },
+{ 0x0029, 0x0010, 0x0000, 0 },
+{ 0x002a, 0x0010, 0x0000, 0 },
+{ 0x002b, 0x0010, 0x0000, 0 },
+{ 0x002c, 0x0010, 0x0000, 0 },
+{ 0x002d, 0x0010, 0x0000, 0 },
+{ 0x002e, 0x0010, 0x0000, 0 },
+{ 0x002f, 0x0010, 0x0000, 0 },
+{ 0x0030, 0x0084, 0x0000, 0 },
+{ 0x0031, 0x0084, 0x0000, 0 },
+{ 0x0032, 0x0084, 0x0000, 0 },
+{ 0x0033, 0x0084, 0x0000, 0 },
+{ 0x0034, 0x0084, 0x0000, 0 },
+{ 0x0035, 0x0084, 0x0000, 0 },
+{ 0x0036, 0x0084, 0x0000, 0 },
+{ 0x0037, 0x0084, 0x0000, 0 },
+{ 0x0038, 0x0084, 0x0000, 0 },
+{ 0x0039, 0x0084, 0x0000, 0 },
+{ 0x003a, 0x0010, 0x0000, 0 },
+{ 0x003b, 0x0010, 0x0000, 0 },
+{ 0x003c, 0x0010, 0x0000, 0 },
+{ 0x003d, 0x0010, 0x0000, 0 },
+{ 0x003e, 0x0010, 0x0000, 0 },
+{ 0x003f, 0x0010, 0x0000, 0 },
+{ 0x0040, 0x0010, 0x0000, 0 },
+{ 0x0041, 0x0181, 0x0061, 0 },
+{ 0x0042, 0x0181, 0x0062, 0 },
+{ 0x0043, 0x0181, 0x0063, 0 },
+{ 0x0044, 0x0181, 0x0064, 0 },
+{ 0x0045, 0x0181, 0x0065, 0 },
+{ 0x0046, 0x0181, 0x0066, 0 },
+{ 0x0047, 0x0101, 0x0067, 0 },
+{ 0x0048, 0x0101, 0x0068, 0 },
+{ 0x0049, 0x0101, 0x0069, 0 },
+{ 0x004a, 0x0101, 0x006a, 0 },
+{ 0x004b, 0x0101, 0x006b, 0 },
+{ 0x004c, 0x0101, 0x006c, 0 },
+{ 0x004d, 0x0101, 0x006d, 0 },
+{ 0x004e, 0x0101, 0x006e, 0 },
+{ 0x004f, 0x0101, 0x006f, 0 },
+{ 0x0050, 0x0101, 0x0070, 0 },
+{ 0x0051, 0x0101, 0x0071, 0 },
+{ 0x0052, 0x0101, 0x0072, 0 },
+{ 0x0053, 0x0101, 0x0073, 0 },
+{ 0x0054, 0x0101, 0x0074, 0 },
+{ 0x0055, 0x0101, 0x0075, 0 },
+{ 0x0056, 0x0101, 0x0076, 0 },
+{ 0x0057, 0x0101, 0x0077, 0 },
+{ 0x0058, 0x0101, 0x0078, 0 },
+{ 0x0059, 0x0101, 0x0079, 0 },
+{ 0x005a, 0x0101, 0x007a, 0 },
+{ 0x005b, 0x0010, 0x0000, 0 },
+{ 0x005c, 0x0010, 0x0000, 0 },
+{ 0x005d, 0x0010, 0x0000, 0 },
+{ 0x005e, 0x0010, 0x0000, 0 },
+{ 0x005f, 0x0010, 0x0000, 0 },
+{ 0x0060, 0x0010, 0x0000, 0 },
+{ 0x0061, 0x0182, 0x0041, 0 },
+{ 0x0062, 0x0182, 0x0042, 0 },
+{ 0x0063, 0x0182, 0x0043, 0 },
+{ 0x0064, 0x0182, 0x0044, 0 },
+{ 0x0065, 0x0182, 0x0045, 0 },
+{ 0x0066, 0x0182, 0x0046, 0 },
+{ 0x0067, 0x0102, 0x0047, 0 },
+{ 0x0068, 0x0102, 0x0048, 0 },
+{ 0x0069, 0x0102, 0x0049, 0 },
+{ 0x006a, 0x0102, 0x004a, 0 },
+{ 0x006b, 0x0102, 0x004b, 0 },
+{ 0x006c, 0x0102, 0x004c, 0 },
+{ 0x006d, 0x0102, 0x004d, 0 },
+{ 0x006e, 0x0102, 0x004e, 0 },
+{ 0x006f, 0x0102, 0x004f, 0 },
+{ 0x0070, 0x0102, 0x0050, 0 },
+{ 0x0071, 0x0102, 0x0051, 0 },
+{ 0x0072, 0x0102, 0x0052, 0 },
+{ 0x0073, 0x0102, 0x0053, 0 },
+{ 0x0074, 0x0102, 0x0054, 0 },
+{ 0x0075, 0x0102, 0x0055, 0 },
+{ 0x0076, 0x0102, 0x0056, 0 },
+{ 0x0077, 0x0102, 0x0057, 0 },
+{ 0x0078, 0x0102, 0x0058, 0 },
+{ 0x0079, 0x0102, 0x0059, 0 },
+{ 0x007a, 0x0102, 0x005a, 0 },
+{ 0x007b, 0x0010, 0x0000, 0 },
+{ 0x007c, 0x0010, 0x0000, 0 },
+{ 0x007d, 0x0010, 0x0000, 0 },
+{ 0x007e, 0x0010, 0x0000, 0 },
+{ 0x007f, 0x0020, 0x0000, 0 },
+{ 0x0080, 0x0020, 0x0000, 0 },
+{ 0x0081, 0x0020, 0x0000, 0 },
+{ 0x0082, 0x0020, 0x0000, 0 },
+{ 0x0083, 0x0020, 0x0000, 0 },
+{ 0x0084, 0x0020, 0x0000, 0 },
+{ 0x0085, 0x0020, 0x0000, 0 },
+{ 0x0086, 0x0020, 0x0000, 0 },
+{ 0x0087, 0x0020, 0x0000, 0 },
+{ 0x0088, 0x0020, 0x0000, 0 },
+{ 0x0089, 0x0020, 0x0000, 0 },
+{ 0x008a, 0x0020, 0x0000, 0 },
+{ 0x008b, 0x0020, 0x0000, 0 },
+{ 0x008c, 0x0020, 0x0000, 0 },
+{ 0x008d, 0x0020, 0x0000, 0 },
+{ 0x008e, 0x0020, 0x0000, 0 },
+{ 0x008f, 0x0020, 0x0000, 0 },
+{ 0x0090, 0x0020, 0x0000, 0 },
+{ 0x0091, 0x0020, 0x0000, 0 },
+{ 0x0092, 0x0020, 0x0000, 0 },
+{ 0x0093, 0x0020, 0x0000, 0 },
+{ 0x0094, 0x0020, 0x0000, 0 },
+{ 0x0095, 0x0020, 0x0000, 0 },
+{ 0x0096, 0x0020, 0x0000, 0 },
+{ 0x0097, 0x0020, 0x0000, 0 },
+{ 0x0098, 0x0020, 0x0000, 0 },
+{ 0x0099, 0x0020, 0x0000, 0 },
+{ 0x009a, 0x0020, 0x0000, 0 },
+{ 0x009b, 0x0020, 0x0000, 0 },
+{ 0x009c, 0x0020, 0x0000, 0 },
+{ 0x009d, 0x0020, 0x0000, 0 },
+{ 0x009e, 0x0020, 0x0000, 0 },
+{ 0x009f, 0x0020, 0x0000, 0 },
+{ 0x00a0, 0x0048, 0x0000, 0 },
+{ 0x00a1, 0x0010, 0x0000, 0 },
+{ 0x00a2, 0x0010, 0x0000, 0 },
+{ 0x00a3, 0x0010, 0x0000, 0 },
+{ 0x00a4, 0x0010, 0x0000, 0 },
+{ 0x00a5, 0x0010, 0x0000, 0 },
+{ 0x00a6, 0x0010, 0x0000, 0 },
+{ 0x00a7, 0x0010, 0x0000, 0 },
+{ 0x00a8, 0x0010, 0x0000, 0 },
+{ 0x00a9, 0x0010, 0x0000, 0 },
+{ 0x00aa, 0x0010, 0x0000, 0 },
+{ 0x00ab, 0x0010, 0x0000, 0 },
+{ 0x00ac, 0x0010, 0x0000, 0 },
+{ 0x00ad, 0x0010, 0x0000, 0 },
+{ 0x00ae, 0x0010, 0x0000, 0 },
+{ 0x00af, 0x0010, 0x0000, 0 },
+{ 0x00b0, 0x0010, 0x0000, 0 },
+{ 0x00b1, 0x0010, 0x0000, 0 },
+{ 0x00b2, 0x0014, 0x0000, 0 },
+{ 0x00b3, 0x0014, 0x0000, 0 },
+{ 0x00b4, 0x0010, 0x0000, 0 },
+{ 0x00b5, 0x0010, 0x0000, 0 },
+{ 0x00b6, 0x0010, 0x0000, 0 },
+{ 0x00b7, 0x0010, 0x0000, 0 },
+{ 0x00b8, 0x0010, 0x0000, 0 },
+{ 0x00b9, 0x0014, 0x0000, 0 },
+{ 0x00ba, 0x0010, 0x0000, 0 },
+{ 0x00bb, 0x0010, 0x0000, 0 },
+{ 0x00bc, 0x0010, 0x0000, 0 },
+{ 0x00bd, 0x0010, 0x0000, 0 },
+{ 0x00be, 0x0010, 0x0000, 0 },
+{ 0x00bf, 0x0010, 0x0000, 0 },
+{ 0x00c0, 0x0101, 0x0000, 0 },
+{ 0x00c1, 0x0101, 0x0000, 0 },
+{ 0x00c2, 0x0101, 0x0000, 0 },
+{ 0x00c3, 0x0101, 0x0000, 0 },
+{ 0x00c4, 0x0101, 0x0000, 0 },
+{ 0x00c5, 0x0101, 0x0000, 0 },
+{ 0x00c6, 0x0101, 0x0000, 0 },
+{ 0x00c7, 0x0101, 0x0000, 0 },
+{ 0x00c8, 0x0101, 0x0000, 0 },
+{ 0x00c9, 0x0101, 0x0000, 0 },
+{ 0x00ca, 0x0101, 0x0000, 0 },
+{ 0x00cb, 0x0101, 0x0000, 0 },
+{ 0x00cc, 0x0101, 0x0000, 0 },
+{ 0x00cd, 0x0101, 0x0000, 0 },
+{ 0x00ce, 0x0101, 0x0000, 0 },
+{ 0x00cf, 0x0101, 0x0000, 0 },
+{ 0x00d0, 0x0101, 0x0000, 0 },
+{ 0x00d1, 0x0101, 0x0000, 0 },
+{ 0x00d2, 0x0101, 0x0000, 0 },
+{ 0x00d3, 0x0101, 0x0000, 0 },
+{ 0x00d4, 0x0101, 0x0000, 0 },
+{ 0x00d5, 0x0101, 0x0000, 0 },
+{ 0x00d6, 0x0101, 0x0000, 0 },
+{ 0x00d7, 0x0010, 0x0000, 0 },
+{ 0x00d8, 0x0101, 0x0000, 0 },
+{ 0x00d9, 0x0101, 0x0000, 0 },
+{ 0x00da, 0x0101, 0x0000, 0 },
+{ 0x00db, 0x0101, 0x0000, 0 },
+{ 0x00dc, 0x0101, 0x0000, 0 },
+{ 0x00dd, 0x0101, 0x0000, 0 },
+{ 0x00de, 0x0101, 0x0000, 0 },
+{ 0x00df, 0x0102, 0x0000, 0 },
+{ 0x00e0, 0x0102, 0x0000, 0 },
+{ 0x00e1, 0x0102, 0x0000, 0 },
+{ 0x00e2, 0x0102, 0x0000, 0 },
+{ 0x00e3, 0x0102, 0x0000, 0 },
+{ 0x00e4, 0x0102, 0x0000, 0 },
+{ 0x00e5, 0x0102, 0x0000, 0 },
+{ 0x00e6, 0x0102, 0x0000, 0 },
+{ 0x00e7, 0x0102, 0x0000, 0 },
+{ 0x00e8, 0x0102, 0x0000, 0 },
+{ 0x00e9, 0x0102, 0x0000, 0 },
+{ 0x00ea, 0x0102, 0x0000, 0 },
+{ 0x00eb, 0x0102, 0x0000, 0 },
+{ 0x00ec, 0x0102, 0x0000, 0 },
+{ 0x00ed, 0x0102, 0x0000, 0 },
+{ 0x00ee, 0x0102, 0x0000, 0 },
+{ 0x00ef, 0x0102, 0x0000, 0 },
+{ 0x00f0, 0x0102, 0x0000, 0 },
+{ 0x00f1, 0x0102, 0x0000, 0 },
+{ 0x00f2, 0x0102, 0x0000, 0 },
+{ 0x00f3, 0x0102, 0x0000, 0 },
+{ 0x00f4, 0x0102, 0x0000, 0 },
+{ 0x00f5, 0x0102, 0x0000, 0 },
+{ 0x00f6, 0x0102, 0x0000, 0 },
+{ 0x00f7, 0x0010, 0x0000, 0 },
+{ 0x00f8, 0x0102, 0x0000, 0 },
+{ 0x00f9, 0x0102, 0x0000, 0 },
+{ 0x00fa, 0x0102, 0x0000, 0 },
+{ 0x00fb, 0x0102, 0x0000, 0 },
+{ 0x00fc, 0x0102, 0x0000, 0 },
+{ 0x00fd, 0x0102, 0x0000, 0 },
+{ 0x00fe, 0x0102, 0x0000, 0 },
+{ 0x00ff, 0x0102, 0x0000, 0 },
+{ 0x0100, 0x0101, 0x0100, 0 },
+{ 0x0101, 0x0102, 0x0101, 0 },
+{ 0x0102, 0x0101, 0x0102, 0 },
+{ 0x0103, 0x0102, 0x0103, 0 },
+{ 0x0104, 0x0101, 0x0104, 0 },
+{ 0x0105, 0x0102, 0x0105, 0 },
+{ 0x0106, 0x0101, 0x0106, 0 },
+{ 0x0107, 0x0102, 0x0107, 0 },
+{ 0x0108, 0x0101, 0x0108, 0 },
+{ 0x0109, 0x0102, 0x0109, 0 },
+{ 0x010a, 0x0101, 0x010a, 0 },
+{ 0x010b, 0x0102, 0x010b, 0 },
+{ 0x010c, 0x0101, 0x010c, 0 },
+{ 0x010d, 0x0102, 0x010d, 0 },
+{ 0x010e, 0x0101, 0x010e, 0 },
+{ 0x010f, 0x0102, 0x010f, 0 },
+{ 0x0110, 0x0101, 0x0110, 0 },
+{ 0x0111, 0x0102, 0x0111, 0 },
+{ 0x0112, 0x0101, 0x0112, 0 },
+{ 0x0113, 0x0102, 0x0113, 0 },
+{ 0x0114, 0x0101, 0x0114, 0 },
+{ 0x0115, 0x0102, 0x0115, 0 },
+{ 0x0116, 0x0101, 0x0116, 0 },
+{ 0x0117, 0x0102, 0x0117, 0 },
+{ 0x0118, 0x0101, 0x0118, 0 },
+{ 0x0119, 0x0102, 0x0119, 0 },
+{ 0x011a, 0x0101, 0x011a, 0 },
+{ 0x011b, 0x0102, 0x011b, 0 },
+{ 0x011c, 0x0101, 0x011c, 0 },
+{ 0x011d, 0x0102, 0x011d, 0 },
+{ 0x011e, 0x0101, 0x011e, 0 },
+{ 0x011f, 0x0102, 0x011f, 0 },
+{ 0x0120, 0x0101, 0x0120, 0 },
+{ 0x0121, 0x0102, 0x0121, 0 },
+{ 0x0122, 0x0101, 0x0122, 0 },
+{ 0x0123, 0x0102, 0x0123, 0 },
+{ 0x0124, 0x0101, 0x0124, 0 },
+{ 0x0125, 0x0102, 0x0125, 0 },
+{ 0x0126, 0x0101, 0x0126, 0 },
+{ 0x0127, 0x0102, 0x0127, 0 },
+{ 0x0128, 0x0101, 0x0128, 0 },
+{ 0x0129, 0x0102, 0x0129, 0 },
+{ 0x012a, 0x0101, 0x012a, 0 },
+{ 0x012b, 0x0102, 0x012b, 0 },
+{ 0x012c, 0x0101, 0x012c, 0 },
+{ 0x012d, 0x0102, 0x012d, 0 },
+{ 0x012e, 0x0101, 0x012e, 0 },
+{ 0x012f, 0x0102, 0x012f, 0 },
+{ 0x0130, 0x0101, 0x0130, 0 },
+{ 0x0131, 0x0102, 0x0131, 0 },
+{ 0x0132, 0x0101, 0x0132, 0 },
+{ 0x0133, 0x0102, 0x0133, 0 },
+{ 0x0134, 0x0101, 0x0134, 0 },
+{ 0x0135, 0x0102, 0x0135, 0 },
+{ 0x0136, 0x0101, 0x0136, 0 },
+{ 0x0137, 0x0102, 0x0137, 0 },
+{ 0x0138, 0x0102, 0x0138, 0 },
+{ 0x0139, 0x0101, 0x0139, 0 },
+{ 0x013a, 0x0102, 0x013a, 0 },
+{ 0x013b, 0x0101, 0x013b, 0 },
+{ 0x013c, 0x0102, 0x013c, 0 },
+{ 0x013d, 0x0101, 0x013d, 0 },
+{ 0x013e, 0x0102, 0x013e, 0 },
+{ 0x013f, 0x0101, 0x013f, 0 },
+{ 0x0140, 0x0102, 0x0140, 0 },
+{ 0x0141, 0x0101, 0x0141, 0 },
+{ 0x0142, 0x0102, 0x0142, 0 },
+{ 0x0143, 0x0101, 0x0143, 0 },
+{ 0x0144, 0x0102, 0x0144, 0 },
+{ 0x0145, 0x0101, 0x0145, 0 },
+{ 0x0146, 0x0102, 0x0146, 0 },
+{ 0x0147, 0x0101, 0x0147, 0 },
+{ 0x0148, 0x0102, 0x0148, 0 },
+{ 0x0149, 0x0102, 0x0149, 0 },
+{ 0x014a, 0x0101, 0x014a, 0 },
+{ 0x014b, 0x0102, 0x014b, 0 },
+{ 0x014c, 0x0101, 0x014c, 0 },
+{ 0x014d, 0x0102, 0x014d, 0 },
+{ 0x014e, 0x0101, 0x014e, 0 },
+{ 0x014f, 0x0102, 0x014f, 0 },
+{ 0x0150, 0x0101, 0x0150, 0 },
+{ 0x0151, 0x0102, 0x0151, 0 },
+{ 0x0152, 0x0101, 0x0152, 0 },
+{ 0x0153, 0x0102, 0x0153, 0 },
+{ 0x0154, 0x0101, 0x0154, 0 },
+{ 0x0155, 0x0102, 0x0155, 0 },
+{ 0x0156, 0x0101, 0x0156, 0 },
+{ 0x0157, 0x0102, 0x0157, 0 },
+{ 0x0158, 0x0101, 0x0158, 0 },
+{ 0x0159, 0x0102, 0x0159, 0 },
+{ 0x015a, 0x0101, 0x015a, 0 },
+{ 0x015b, 0x0102, 0x015b, 0 },
+{ 0x015c, 0x0101, 0x015c, 0 },
+{ 0x015d, 0x0102, 0x015d, 0 },
+{ 0x015e, 0x0101, 0x015e, 0 },
+{ 0x015f, 0x0102, 0x015f, 0 },
+{ 0x0160, 0x0101, 0x0160, 0 },
+{ 0x0161, 0x0102, 0x0161, 0 },
+{ 0x0162, 0x0101, 0x0162, 0 },
+{ 0x0163, 0x0102, 0x0163, 0 },
+{ 0x0164, 0x0101, 0x0164, 0 },
+{ 0x0165, 0x0102, 0x0165, 0 },
+{ 0x0166, 0x0101, 0x0166, 0 },
+{ 0x0167, 0x0102, 0x0167, 0 },
+{ 0x0168, 0x0101, 0x0168, 0 },
+{ 0x0169, 0x0102, 0x0169, 0 },
+{ 0x016a, 0x0101, 0x016a, 0 },
+{ 0x016b, 0x0102, 0x016b, 0 },
+{ 0x016c, 0x0101, 0x016c, 0 },
+{ 0x016d, 0x0102, 0x016d, 0 },
+{ 0x016e, 0x0101, 0x016e, 0 },
+{ 0x016f, 0x0102, 0x016f, 0 },
+{ 0x0170, 0x0101, 0x0170, 0 },
+{ 0x0171, 0x0102, 0x0171, 0 },
+{ 0x0172, 0x0101, 0x0172, 0 },
+{ 0x0173, 0x0102, 0x0173, 0 },
+{ 0x0174, 0x0101, 0x0174, 0 },
+{ 0x0175, 0x0102, 0x0175, 0 },
+{ 0x0176, 0x0101, 0x0176, 0 },
+{ 0x0177, 0x0102, 0x0177, 0 },
+{ 0x0178, 0x0101, 0x0178, 0 },
+{ 0x0179, 0x0101, 0x0179, 0 },
+{ 0x017a, 0x0102, 0x017a, 0 },
+{ 0x017b, 0x0101, 0x017b, 0 },
+{ 0x017c, 0x0102, 0x017c, 0 },
+{ 0x017d, 0x0101, 0x017d, 0 },
+{ 0x017e, 0x0102, 0x017e, 0 },
+{ 0x017f, 0x0102, 0x017f, 0 },
+{ 0x0180, 0x0102, 0x0180, 0 },
+{ 0x0181, 0x0101, 0x0181, 0 },
+{ 0x0182, 0x0101, 0x0182, 0 },
+{ 0x0183, 0x0102, 0x0183, 0 },
+{ 0x0184, 0x0101, 0x0184, 0 },
+{ 0x0185, 0x0102, 0x0185, 0 },
+{ 0x0186, 0x0101, 0x0186, 0 },
+{ 0x0187, 0x0101, 0x0187, 0 },
+{ 0x0188, 0x0102, 0x0188, 0 },
+{ 0x0189, 0x0101, 0x0189, 0 },
+{ 0x018a, 0x0101, 0x018a, 0 },
+{ 0x018b, 0x0101, 0x018b, 0 },
+{ 0x018c, 0x0102, 0x018c, 0 },
+{ 0x018d, 0x0102, 0x018d, 0 },
+{ 0x018e, 0x0101, 0x018e, 0 },
+{ 0x018f, 0x0101, 0x018f, 0 },
+{ 0x0190, 0x0101, 0x0190, 0 },
+{ 0x0191, 0x0101, 0x0191, 0 },
+{ 0x0192, 0x0112, 0x0192, 0 },
+{ 0x0193, 0x0101, 0x0193, 0 },
+{ 0x0194, 0x0101, 0x0194, 0 },
+{ 0x0195, 0x0102, 0x0195, 0 },
+{ 0x0196, 0x0101, 0x0196, 0 },
+{ 0x0197, 0x0101, 0x0197, 0 },
+{ 0x0198, 0x0101, 0x0198, 0 },
+{ 0x0199, 0x0102, 0x0199, 0 },
+{ 0x019a, 0x0102, 0x019a, 0 },
+{ 0x019b, 0x0102, 0x019b, 0 },
+{ 0x019c, 0x0101, 0x019c, 0 },
+{ 0x019d, 0x0101, 0x019d, 0 },
+{ 0x019e, 0x0102, 0x019e, 0 },
+{ 0x019f, 0x0101, 0x019f, 0 },
+{ 0x01a0, 0x0101, 0x01a0, 0 },
+{ 0x01a1, 0x0102, 0x01a1, 0 },
+{ 0x01a2, 0x0101, 0x01a2, 0 },
+{ 0x01a3, 0x0102, 0x01a3, 0 },
+{ 0x01a4, 0x0101, 0x01a4, 0 },
+{ 0x01a5, 0x0102, 0x01a5, 0 },
+{ 0x01a6, 0x0100, 0x0000, 0 },
+{ 0x01a7, 0x0101, 0x01a7, 0 },
+{ 0x01a8, 0x0102, 0x01a8, 0 },
+{ 0x01a9, 0x0101, 0x01a9, 0 },
+{ 0x01aa, 0x0100, 0x0000, 0 },
+{ 0x01ab, 0x0102, 0x01ab, 0 },
+{ 0x01ac, 0x0101, 0x01ac, 0 },
+{ 0x01ad, 0x0102, 0x01ad, 0 },
+{ 0x01ae, 0x0101, 0x01ae, 0 },
+{ 0x01af, 0x0101, 0x01af, 0 },
+{ 0x01b0, 0x0102, 0x01b0, 0 },
+{ 0x01b1, 0x0101, 0x01b1, 0 },
+{ 0x01b2, 0x0101, 0x01b2, 0 },
+{ 0x01b3, 0x0101, 0x01b3, 0 },
+{ 0x01b4, 0x0102, 0x01b4, 0 },
+{ 0x01b5, 0x0101, 0x01b5, 0 },
+{ 0x01b6, 0x0102, 0x01b6, 0 },
+{ 0x01b7, 0x0101, 0x01b7, 0 },
+{ 0x01b8, 0x0101, 0x01b8, 0 },
+{ 0x01b9, 0x0102, 0x01b9, 0 },
+{ 0x01ba, 0x0102, 0x01ba, 0 },
+{ 0x01bb, 0x0100, 0x0000, 0 },
+{ 0x01bc, 0x0101, 0x01bc, 0 },
+{ 0x01bd, 0x0102, 0x01bd, 0 },
+{ 0x01be, 0x0100, 0x0000, 5 },
+{ 0x01c4, 0x0101, 0x01c4, 0 },
+{ 0x01c5, 0x0103, 0x01c5, 0 },
+{ 0x01c6, 0x0102, 0x01c6, 0 },
+{ 0x01c7, 0x0101, 0x01c7, 0 },
+{ 0x01c8, 0x0103, 0x01c8, 0 },
+{ 0x01c9, 0x0102, 0x01c9, 0 },
+{ 0x01ca, 0x0101, 0x01ca, 0 },
+{ 0x01cb, 0x0103, 0x01cb, 0 },
+{ 0x01cc, 0x0102, 0x01cc, 0 },
+{ 0x01cd, 0x0101, 0x01cd, 0 },
+{ 0x01ce, 0x0102, 0x01ce, 0 },
+{ 0x01cf, 0x0101, 0x01cf, 0 },
+{ 0x01d0, 0x0102, 0x01d0, 0 },
+{ 0x01d1, 0x0101, 0x01d1, 0 },
+{ 0x01d2, 0x0102, 0x01d2, 0 },
+{ 0x01d3, 0x0101, 0x01d3, 0 },
+{ 0x01d4, 0x0102, 0x01d4, 0 },
+{ 0x01d5, 0x0101, 0x01d5, 0 },
+{ 0x01d6, 0x0102, 0x01d6, 0 },
+{ 0x01d7, 0x0101, 0x01d7, 0 },
+{ 0x01d8, 0x0102, 0x01d8, 0 },
+{ 0x01d9, 0x0101, 0x01d9, 0 },
+{ 0x01da, 0x0102, 0x01da, 0 },
+{ 0x01db, 0x0101, 0x01db, 0 },
+{ 0x01dc, 0x0102, 0x01dc, 0 },
+{ 0x01dd, 0x0102, 0x01dd, 0 },
+{ 0x01de, 0x0101, 0x01de, 0 },
+{ 0x01df, 0x0102, 0x01df, 0 },
+{ 0x01e0, 0x0101, 0x01e0, 0 },
+{ 0x01e1, 0x0102, 0x01e1, 0 },
+{ 0x01e2, 0x0101, 0x01e2, 0 },
+{ 0x01e3, 0x0102, 0x01e3, 0 },
+{ 0x01e4, 0x0101, 0x01e4, 0 },
+{ 0x01e5, 0x0102, 0x01e5, 0 },
+{ 0x01e6, 0x0101, 0x01e6, 0 },
+{ 0x01e7, 0x0102, 0x01e7, 0 },
+{ 0x01e8, 0x0101, 0x01e8, 0 },
+{ 0x01e9, 0x0102, 0x01e9, 0 },
+{ 0x01ea, 0x0101, 0x01ea, 0 },
+{ 0x01eb, 0x0102, 0x01eb, 0 },
+{ 0x01ec, 0x0101, 0x01ec, 0 },
+{ 0x01ed, 0x0102, 0x01ed, 0 },
+{ 0x01ee, 0x0101, 0x01ee, 0 },
+{ 0x01ef, 0x0102, 0x01ef, 0 },
+{ 0x01f0, 0x0102, 0x01f0, 0 },
+{ 0x01f1, 0x0101, 0x01f1, 0 },
+{ 0x01f2, 0x0103, 0x01f2, 0 },
+{ 0x01f3, 0x0102, 0x01f3, 0 },
+{ 0x01f4, 0x0101, 0x01f4, 0 },
+{ 0x01f5, 0x0102, 0x01f5, 0 },
+{ 0x01fa, 0x0101, 0x01fa, 0 },
+{ 0x01fb, 0x0102, 0x01fb, 0 },
+{ 0x01fc, 0x0101, 0x01fc, 0 },
+{ 0x01fd, 0x0102, 0x01fd, 0 },
+{ 0x01fe, 0x0101, 0x01fe, 0 },
+{ 0x01ff, 0x0102, 0x01ff, 0 },
+{ 0x0200, 0x0101, 0x0200, 0 },
+{ 0x0201, 0x0102, 0x0201, 0 },
+{ 0x0202, 0x0101, 0x0202, 0 },
+{ 0x0203, 0x0102, 0x0203, 0 },
+{ 0x0204, 0x0101, 0x0204, 0 },
+{ 0x0205, 0x0102, 0x0205, 0 },
+{ 0x0206, 0x0101, 0x0206, 0 },
+{ 0x0207, 0x0102, 0x0207, 0 },
+{ 0x0208, 0x0101, 0x0208, 0 },
+{ 0x0209, 0x0102, 0x0209, 0 },
+{ 0x020a, 0x0101, 0x020a, 0 },
+{ 0x020b, 0x0102, 0x020b, 0 },
+{ 0x020c, 0x0101, 0x020c, 0 },
+{ 0x020d, 0x0102, 0x020d, 0 },
+{ 0x020e, 0x0101, 0x020e, 0 },
+{ 0x020f, 0x0102, 0x020f, 0 },
+{ 0x0210, 0x0101, 0x0210, 0 },
+{ 0x0211, 0x0102, 0x0211, 0 },
+{ 0x0212, 0x0101, 0x0212, 0 },
+{ 0x0213, 0x0102, 0x0213, 0 },
+{ 0x0214, 0x0101, 0x0214, 0 },
+{ 0x0215, 0x0102, 0x0215, 0 },
+{ 0x0216, 0x0101, 0x0216, 0 },
+{ 0x0217, 0x0102, 0x0217, 0 },
+{ 0x0250, 0x0102, 0x0250, 0 },
+{ 0x0251, 0x0102, 0x0251, 0 },
+{ 0x0252, 0x0102, 0x0252, 0 },
+{ 0x0253, 0x0102, 0x0253, 0 },
+{ 0x0254, 0x0102, 0x0254, 0 },
+{ 0x0255, 0x0102, 0x0255, 0 },
+{ 0x0256, 0x0102, 0x0256, 0 },
+{ 0x0257, 0x0102, 0x0257, 0 },
+{ 0x0258, 0x0102, 0x0258, 0 },
+{ 0x0259, 0x0102, 0x0259, 0 },
+{ 0x025a, 0x0102, 0x025a, 0 },
+{ 0x025b, 0x0102, 0x025b, 0 },
+{ 0x025c, 0x0102, 0x025c, 0 },
+{ 0x025d, 0x0102, 0x025d, 0 },
+{ 0x025e, 0x0102, 0x025e, 0 },
+{ 0x025f, 0x0102, 0x025f, 0 },
+{ 0x0260, 0x0102, 0x0260, 0 },
+{ 0x0261, 0x0102, 0x0261, 0 },
+{ 0x0262, 0x0100, 0x0000, 0 },
+{ 0x0263, 0x0102, 0x0263, 0 },
+{ 0x0264, 0x0102, 0x0264, 0 },
+{ 0x0265, 0x0102, 0x0265, 0 },
+{ 0x0266, 0x0102, 0x0266, 0 },
+{ 0x0267, 0x0102, 0x0267, 0 },
+{ 0x0268, 0x0102, 0x0268, 0 },
+{ 0x0269, 0x0102, 0x0269, 0 },
+{ 0x026a, 0x0100, 0x0000, 0 },
+{ 0x026b, 0x0102, 0x026b, 0 },
+{ 0x026c, 0x0102, 0x026c, 0 },
+{ 0x026d, 0x0102, 0x026d, 0 },
+{ 0x026e, 0x0102, 0x026e, 0 },
+{ 0x026f, 0x0102, 0x026f, 0 },
+{ 0x0270, 0x0102, 0x0270, 0 },
+{ 0x0271, 0x0102, 0x0271, 0 },
+{ 0x0272, 0x0102, 0x0272, 0 },
+{ 0x0273, 0x0102, 0x0273, 0 },
+{ 0x0274, 0x0100, 0x0000, 0 },
+{ 0x0275, 0x0102, 0x0275, 0 },
+{ 0x0276, 0x0100, 0x0000, 0 },
+{ 0x0277, 0x0102, 0x0277, 0 },
+{ 0x0278, 0x0102, 0x0278, 0 },
+{ 0x0279, 0x0102, 0x0279, 0 },
+{ 0x027a, 0x0102, 0x027a, 0 },
+{ 0x027b, 0x0102, 0x027b, 0 },
+{ 0x027c, 0x0102, 0x027c, 0 },
+{ 0x027d, 0x0102, 0x027d, 0 },
+{ 0x027e, 0x0102, 0x027e, 0 },
+{ 0x027f, 0x0102, 0x027f, 0 },
+{ 0x0280, 0x0100, 0x0000, 1 },
+{ 0x0282, 0x0102, 0x0282, 0 },
+{ 0x0283, 0x0102, 0x0283, 0 },
+{ 0x0284, 0x0102, 0x0284, 0 },
+{ 0x0285, 0x0102, 0x0285, 0 },
+{ 0x0286, 0x0102, 0x0286, 0 },
+{ 0x0287, 0x0102, 0x0287, 0 },
+{ 0x0288, 0x0102, 0x0288, 0 },
+{ 0x0289, 0x0102, 0x0289, 0 },
+{ 0x028a, 0x0102, 0x028a, 0 },
+{ 0x028b, 0x0102, 0x028b, 0 },
+{ 0x028c, 0x0102, 0x028c, 0 },
+{ 0x028d, 0x0102, 0x028d, 0 },
+{ 0x028e, 0x0102, 0x028e, 0 },
+{ 0x028f, 0x0100, 0x0000, 0 },
+{ 0x0290, 0x0102, 0x0290, 0 },
+{ 0x0291, 0x0102, 0x0291, 0 },
+{ 0x0292, 0x0102, 0x0292, 0 },
+{ 0x0293, 0x0102, 0x0293, 0 },
+{ 0x0294, 0x0100, 0x0000, 5 },
+{ 0x029a, 0x0102, 0x029a, 0 },
+{ 0x029b, 0x0100, 0x0000, 1 },
+{ 0x029d, 0x0102, 0x029d, 0 },
+{ 0x029e, 0x0102, 0x029e, 0 },
+{ 0x029f, 0x0100, 0x0000, 0 },
+{ 0x02a0, 0x0102, 0x02a0, 0 },
+{ 0x02a1, 0x0100, 0x0000, 1 },
+{ 0x02a3, 0x0102, 0x02a3, 0 },
+{ 0x02a4, 0x0102, 0x02a4, 0 },
+{ 0x02a5, 0x0102, 0x02a5, 0 },
+{ 0x02a6, 0x0102, 0x02a6, 0 },
+{ 0x02a7, 0x0102, 0x02a7, 0 },
+{ 0x02a8, 0x0102, 0x02a8, 0 },
+{ 0x02b0, 0x0010, 0x0000, 134 },
+{ 0x0386, 0x0101, 0x0386, 0 },
+{ 0x0387, 0x0010, 0x0000, 0 },
+{ 0x0388, 0x0101, 0x0388, 0 },
+{ 0x0389, 0x0101, 0x0389, 0 },
+{ 0x038a, 0x0101, 0x038a, 0 },
+{ 0x038c, 0x0101, 0x038c, 0 },
+{ 0x038e, 0x0101, 0x038e, 0 },
+{ 0x038f, 0x0101, 0x038f, 0 },
+{ 0x0390, 0x0102, 0x0390, 0 },
+{ 0x0391, 0x0101, 0x0391, 0 },
+{ 0x0392, 0x0101, 0x0392, 0 },
+{ 0x0393, 0x0101, 0x0393, 0 },
+{ 0x0394, 0x0101, 0x0394, 0 },
+{ 0x0395, 0x0101, 0x0395, 0 },
+{ 0x0396, 0x0101, 0x0396, 0 },
+{ 0x0397, 0x0101, 0x0397, 0 },
+{ 0x0398, 0x0101, 0x0398, 0 },
+{ 0x0399, 0x0101, 0x0399, 0 },
+{ 0x039a, 0x0101, 0x039a, 0 },
+{ 0x039b, 0x0101, 0x039b, 0 },
+{ 0x039c, 0x0101, 0x039c, 0 },
+{ 0x039d, 0x0101, 0x039d, 0 },
+{ 0x039e, 0x0101, 0x039e, 0 },
+{ 0x039f, 0x0101, 0x039f, 0 },
+{ 0x03a0, 0x0101, 0x03a0, 0 },
+{ 0x03a1, 0x0101, 0x03a1, 0 },
+{ 0x03a3, 0x0101, 0x03a3, 0 },
+{ 0x03a4, 0x0101, 0x03a4, 0 },
+{ 0x03a5, 0x0101, 0x03a5, 0 },
+{ 0x03a6, 0x0101, 0x03a6, 0 },
+{ 0x03a7, 0x0101, 0x03a7, 0 },
+{ 0x03a8, 0x0101, 0x03a8, 0 },
+{ 0x03a9, 0x0101, 0x03a9, 0 },
+{ 0x03aa, 0x0101, 0x03aa, 0 },
+{ 0x03ab, 0x0101, 0x03ab, 0 },
+{ 0x03ac, 0x0102, 0x03ac, 0 },
+{ 0x03ad, 0x0102, 0x03ad, 0 },
+{ 0x03ae, 0x0102, 0x03ae, 0 },
+{ 0x03af, 0x0102, 0x03af, 0 },
+{ 0x03b0, 0x0102, 0x03b0, 0 },
+{ 0x03b1, 0x0102, 0x03b1, 0 },
+{ 0x03b2, 0x0102, 0x03b2, 0 },
+{ 0x03b3, 0x0102, 0x03b3, 0 },
+{ 0x03b4, 0x0102, 0x03b4, 0 },
+{ 0x03b5, 0x0102, 0x03b5, 0 },
+{ 0x03b6, 0x0102, 0x03b6, 0 },
+{ 0x03b7, 0x0102, 0x03b7, 0 },
+{ 0x03b8, 0x0102, 0x03b8, 0 },
+{ 0x03b9, 0x0102, 0x03b9, 0 },
+{ 0x03ba, 0x0102, 0x03ba, 0 },
+{ 0x03bb, 0x0102, 0x03bb, 0 },
+{ 0x03bc, 0x0102, 0x03bc, 0 },
+{ 0x03bd, 0x0102, 0x03bd, 0 },
+{ 0x03be, 0x0102, 0x03be, 0 },
+{ 0x03bf, 0x0102, 0x03bf, 0 },
+{ 0x03c0, 0x0102, 0x03c0, 0 },
+{ 0x03c1, 0x0102, 0x03c1, 0 },
+{ 0x03c2, 0x0102, 0x03c2, 0 },
+{ 0x03c3, 0x0102, 0x03c3, 0 },
+{ 0x03c4, 0x0102, 0x03c4, 0 },
+{ 0x03c5, 0x0102, 0x03c5, 0 },
+{ 0x03c6, 0x0102, 0x03c6, 0 },
+{ 0x03c7, 0x0102, 0x03c7, 0 },
+{ 0x03c8, 0x0102, 0x03c8, 0 },
+{ 0x03c9, 0x0102, 0x03c9, 0 },
+{ 0x03ca, 0x0102, 0x03ca, 0 },
+{ 0x03cb, 0x0102, 0x03cb, 0 },
+{ 0x03cc, 0x0102, 0x03cc, 0 },
+{ 0x03cd, 0x0102, 0x03cd, 0 },
+{ 0x03ce, 0x0102, 0x03ce, 0 },
+{ 0x03d0, 0x0102, 0x03d0, 0 },
+{ 0x03d1, 0x0102, 0x03d1, 0 },
+{ 0x03d2, 0x0101, 0x03d2, 0 },
+{ 0x03d3, 0x0101, 0x03d3, 0 },
+{ 0x03d4, 0x0101, 0x03d4, 0 },
+{ 0x03d5, 0x0102, 0x03d5, 0 },
+{ 0x03d6, 0x0102, 0x03d6, 0 },
+{ 0x03da, 0x0101, 0x03da, 0 },
+{ 0x03dc, 0x0101, 0x03dc, 0 },
+{ 0x03de, 0x0101, 0x03de, 0 },
+{ 0x03e0, 0x0101, 0x03e0, 0 },
+{ 0x03e2, 0x0101, 0x03e2, 0 },
+{ 0x03e3, 0x0102, 0x03e3, 0 },
+{ 0x03e4, 0x0101, 0x03e4, 0 },
+{ 0x03e5, 0x0102, 0x03e5, 0 },
+{ 0x03e6, 0x0101, 0x03e6, 0 },
+{ 0x03e7, 0x0102, 0x03e7, 0 },
+{ 0x03e8, 0x0101, 0x03e8, 0 },
+{ 0x03e9, 0x0102, 0x03e9, 0 },
+{ 0x03ea, 0x0101, 0x03ea, 0 },
+{ 0x03eb, 0x0102, 0x03eb, 0 },
+{ 0x03ec, 0x0101, 0x03ec, 0 },
+{ 0x03ed, 0x0102, 0x03ed, 0 },
+{ 0x03ee, 0x0101, 0x03ee, 0 },
+{ 0x03ef, 0x0102, 0x03ef, 0 },
+{ 0x03f0, 0x0102, 0x03f0, 0 },
+{ 0x03f1, 0x0102, 0x03f1, 0 },
+{ 0x03f2, 0x0102, 0x03f2, 0 },
+{ 0x03f3, 0x0102, 0x03f3, 0 },
+{ 0x0401, 0x0101, 0x0401, 0 },
+{ 0x0402, 0x0101, 0x0402, 0 },
+{ 0x0403, 0x0101, 0x0403, 0 },
+{ 0x0404, 0x0101, 0x0404, 0 },
+{ 0x0405, 0x0101, 0x0405, 0 },
+{ 0x0406, 0x0101, 0x0406, 0 },
+{ 0x0407, 0x0101, 0x0407, 0 },
+{ 0x0408, 0x0101, 0x0408, 0 },
+{ 0x0409, 0x0101, 0x0409, 0 },
+{ 0x040a, 0x0101, 0x040a, 0 },
+{ 0x040b, 0x0101, 0x040b, 0 },
+{ 0x040c, 0x0101, 0x040c, 0 },
+{ 0x040e, 0x0101, 0x040e, 0 },
+{ 0x040f, 0x0101, 0x040f, 0 },
+{ 0x0410, 0x0101, 0x0410, 0 },
+{ 0x0411, 0x0101, 0x0411, 0 },
+{ 0x0412, 0x0101, 0x0412, 0 },
+{ 0x0413, 0x0101, 0x0413, 0 },
+{ 0x0414, 0x0101, 0x0414, 0 },
+{ 0x0415, 0x0101, 0x0415, 0 },
+{ 0x0416, 0x0101, 0x0416, 0 },
+{ 0x0417, 0x0101, 0x0417, 0 },
+{ 0x0418, 0x0101, 0x0418, 0 },
+{ 0x0419, 0x0101, 0x0419, 0 },
+{ 0x041a, 0x0101, 0x041a, 0 },
+{ 0x041b, 0x0101, 0x041b, 0 },
+{ 0x041c, 0x0101, 0x041c, 0 },
+{ 0x041d, 0x0101, 0x041d, 0 },
+{ 0x041e, 0x0101, 0x041e, 0 },
+{ 0x041f, 0x0101, 0x041f, 0 },
+{ 0x0420, 0x0101, 0x0420, 0 },
+{ 0x0421, 0x0101, 0x0421, 0 },
+{ 0x0422, 0x0101, 0x0422, 0 },
+{ 0x0423, 0x0101, 0x0423, 0 },
+{ 0x0424, 0x0101, 0x0424, 0 },
+{ 0x0425, 0x0101, 0x0425, 0 },
+{ 0x0426, 0x0101, 0x0426, 0 },
+{ 0x0427, 0x0101, 0x0427, 0 },
+{ 0x0428, 0x0101, 0x0428, 0 },
+{ 0x0429, 0x0101, 0x0429, 0 },
+{ 0x042a, 0x0101, 0x042a, 0 },
+{ 0x042b, 0x0101, 0x042b, 0 },
+{ 0x042c, 0x0101, 0x042c, 0 },
+{ 0x042d, 0x0101, 0x042d, 0 },
+{ 0x042e, 0x0101, 0x042e, 0 },
+{ 0x042f, 0x0101, 0x042f, 0 },
+{ 0x0430, 0x0102, 0x0430, 0 },
+{ 0x0431, 0x0102, 0x0431, 0 },
+{ 0x0432, 0x0102, 0x0432, 0 },
+{ 0x0433, 0x0102, 0x0433, 0 },
+{ 0x0434, 0x0102, 0x0434, 0 },
+{ 0x0435, 0x0102, 0x0435, 0 },
+{ 0x0436, 0x0102, 0x0436, 0 },
+{ 0x0437, 0x0102, 0x0437, 0 },
+{ 0x0438, 0x0102, 0x0438, 0 },
+{ 0x0439, 0x0102, 0x0439, 0 },
+{ 0x043a, 0x0102, 0x043a, 0 },
+{ 0x043b, 0x0102, 0x043b, 0 },
+{ 0x043c, 0x0102, 0x043c, 0 },
+{ 0x043d, 0x0102, 0x043d, 0 },
+{ 0x043e, 0x0102, 0x043e, 0 },
+{ 0x043f, 0x0102, 0x043f, 0 },
+{ 0x0440, 0x0102, 0x0440, 0 },
+{ 0x0441, 0x0102, 0x0441, 0 },
+{ 0x0442, 0x0102, 0x0442, 0 },
+{ 0x0443, 0x0102, 0x0443, 0 },
+{ 0x0444, 0x0102, 0x0444, 0 },
+{ 0x0445, 0x0102, 0x0445, 0 },
+{ 0x0446, 0x0102, 0x0446, 0 },
+{ 0x0447, 0x0102, 0x0447, 0 },
+{ 0x0448, 0x0102, 0x0448, 0 },
+{ 0x0449, 0x0102, 0x0449, 0 },
+{ 0x044a, 0x0102, 0x044a, 0 },
+{ 0x044b, 0x0102, 0x044b, 0 },
+{ 0x044c, 0x0102, 0x044c, 0 },
+{ 0x044d, 0x0102, 0x044d, 0 },
+{ 0x044e, 0x0102, 0x044e, 0 },
+{ 0x044f, 0x0102, 0x044f, 0 },
+{ 0x0451, 0x0102, 0x0451, 0 },
+{ 0x0452, 0x0102, 0x0452, 0 },
+{ 0x0453, 0x0102, 0x0453, 0 },
+{ 0x0454, 0x0102, 0x0454, 0 },
+{ 0x0455, 0x0102, 0x0455, 0 },
+{ 0x0456, 0x0102, 0x0456, 0 },
+{ 0x0457, 0x0102, 0x0457, 0 },
+{ 0x0458, 0x0102, 0x0458, 0 },
+{ 0x0459, 0x0102, 0x0459, 0 },
+{ 0x045a, 0x0102, 0x045a, 0 },
+{ 0x045b, 0x0102, 0x045b, 0 },
+{ 0x045c, 0x0102, 0x045c, 0 },
+{ 0x045e, 0x0102, 0x045e, 0 },
+{ 0x045f, 0x0102, 0x045f, 0 },
+{ 0x0460, 0x0101, 0x0460, 0 },
+{ 0x0461, 0x0102, 0x0461, 0 },
+{ 0x0462, 0x0101, 0x0462, 0 },
+{ 0x0463, 0x0102, 0x0463, 0 },
+{ 0x0464, 0x0101, 0x0464, 0 },
+{ 0x0465, 0x0102, 0x0465, 0 },
+{ 0x0466, 0x0101, 0x0466, 0 },
+{ 0x0467, 0x0102, 0x0467, 0 },
+{ 0x0468, 0x0101, 0x0468, 0 },
+{ 0x0469, 0x0102, 0x0469, 0 },
+{ 0x046a, 0x0101, 0x046a, 0 },
+{ 0x046b, 0x0102, 0x046b, 0 },
+{ 0x046c, 0x0101, 0x046c, 0 },
+{ 0x046d, 0x0102, 0x046d, 0 },
+{ 0x046e, 0x0101, 0x046e, 0 },
+{ 0x046f, 0x0102, 0x046f, 0 },
+{ 0x0470, 0x0101, 0x0470, 0 },
+{ 0x0471, 0x0102, 0x0471, 0 },
+{ 0x0472, 0x0101, 0x0472, 0 },
+{ 0x0473, 0x0102, 0x0473, 0 },
+{ 0x0474, 0x0101, 0x0474, 0 },
+{ 0x0475, 0x0102, 0x0475, 0 },
+{ 0x0476, 0x0101, 0x0476, 0 },
+{ 0x0477, 0x0102, 0x0477, 0 },
+{ 0x0478, 0x0101, 0x0478, 0 },
+{ 0x0479, 0x0102, 0x0479, 0 },
+{ 0x047a, 0x0101, 0x047a, 0 },
+{ 0x047b, 0x0102, 0x047b, 0 },
+{ 0x047c, 0x0101, 0x047c, 0 },
+{ 0x047d, 0x0102, 0x047d, 0 },
+{ 0x047e, 0x0101, 0x047e, 0 },
+{ 0x047f, 0x0102, 0x047f, 0 },
+{ 0x0480, 0x0101, 0x0480, 0 },
+{ 0x0481, 0x0102, 0x0481, 0 },
+{ 0x0482, 0x0010, 0x0000, 4 },
+{ 0x0490, 0x0101, 0x0490, 0 },
+{ 0x0491, 0x0102, 0x0491, 0 },
+{ 0x0492, 0x0101, 0x0492, 0 },
+{ 0x0493, 0x0102, 0x0493, 0 },
+{ 0x0494, 0x0101, 0x0494, 0 },
+{ 0x0495, 0x0102, 0x0495, 0 },
+{ 0x0496, 0x0101, 0x0496, 0 },
+{ 0x0497, 0x0102, 0x0497, 0 },
+{ 0x0498, 0x0101, 0x0498, 0 },
+{ 0x0499, 0x0102, 0x0499, 0 },
+{ 0x049a, 0x0101, 0x049a, 0 },
+{ 0x049b, 0x0102, 0x049b, 0 },
+{ 0x049c, 0x0101, 0x049c, 0 },
+{ 0x049d, 0x0102, 0x049d, 0 },
+{ 0x049e, 0x0101, 0x049e, 0 },
+{ 0x049f, 0x0102, 0x049f, 0 },
+{ 0x04a0, 0x0101, 0x04a0, 0 },
+{ 0x04a1, 0x0102, 0x04a1, 0 },
+{ 0x04a2, 0x0101, 0x04a2, 0 },
+{ 0x04a3, 0x0102, 0x04a3, 0 },
+{ 0x04a4, 0x0101, 0x04a4, 0 },
+{ 0x04a5, 0x0102, 0x04a5, 0 },
+{ 0x04a6, 0x0101, 0x04a6, 0 },
+{ 0x04a7, 0x0102, 0x04a7, 0 },
+{ 0x04a8, 0x0101, 0x04a8, 0 },
+{ 0x04a9, 0x0102, 0x04a9, 0 },
+{ 0x04aa, 0x0101, 0x04aa, 0 },
+{ 0x04ab, 0x0102, 0x04ab, 0 },
+{ 0x04ac, 0x0101, 0x04ac, 0 },
+{ 0x04ad, 0x0102, 0x04ad, 0 },
+{ 0x04ae, 0x0101, 0x04ae, 0 },
+{ 0x04af, 0x0102, 0x04af, 0 },
+{ 0x04b0, 0x0101, 0x04b0, 0 },
+{ 0x04b1, 0x0102, 0x04b1, 0 },
+{ 0x04b2, 0x0101, 0x04b2, 0 },
+{ 0x04b3, 0x0102, 0x04b3, 0 },
+{ 0x04b4, 0x0101, 0x04b4, 0 },
+{ 0x04b5, 0x0102, 0x04b5, 0 },
+{ 0x04b6, 0x0101, 0x04b6, 0 },
+{ 0x04b7, 0x0102, 0x04b7, 0 },
+{ 0x04b8, 0x0101, 0x04b8, 0 },
+{ 0x04b9, 0x0102, 0x04b9, 0 },
+{ 0x04ba, 0x0101, 0x04ba, 0 },
+{ 0x04bb, 0x0102, 0x04bb, 0 },
+{ 0x04bc, 0x0101, 0x04bc, 0 },
+{ 0x04bd, 0x0102, 0x04bd, 0 },
+{ 0x04be, 0x0101, 0x04be, 0 },
+{ 0x04bf, 0x0102, 0x04bf, 0 },
+{ 0x04c0, 0x0100, 0x0000, 0 },
+{ 0x04c1, 0x0101, 0x04c1, 0 },
+{ 0x04c2, 0x0102, 0x04c2, 0 },
+{ 0x04c3, 0x0101, 0x04c3, 0 },
+{ 0x04c4, 0x0102, 0x04c4, 0 },
+{ 0x04c7, 0x0101, 0x04c7, 0 },
+{ 0x04c8, 0x0102, 0x04c8, 0 },
+{ 0x04cb, 0x0101, 0x04cb, 0 },
+{ 0x04cc, 0x0102, 0x04cc, 0 },
+{ 0x04d0, 0x0101, 0x04d0, 0 },
+{ 0x04d1, 0x0102, 0x04d1, 0 },
+{ 0x04d2, 0x0101, 0x04d2, 0 },
+{ 0x04d3, 0x0102, 0x04d3, 0 },
+{ 0x04d4, 0x0101, 0x04d4, 0 },
+{ 0x04d5, 0x0102, 0x04d5, 0 },
+{ 0x04d6, 0x0101, 0x04d6, 0 },
+{ 0x04d7, 0x0102, 0x04d7, 0 },
+{ 0x04d8, 0x0101, 0x04d8, 0 },
+{ 0x04d9, 0x0102, 0x04d9, 0 },
+{ 0x04da, 0x0101, 0x04da, 0 },
+{ 0x04db, 0x0102, 0x04db, 0 },
+{ 0x04dc, 0x0101, 0x04dc, 0 },
+{ 0x04dd, 0x0102, 0x04dd, 0 },
+{ 0x04de, 0x0101, 0x04de, 0 },
+{ 0x04df, 0x0102, 0x04df, 0 },
+{ 0x04e0, 0x0101, 0x04e0, 0 },
+{ 0x04e1, 0x0102, 0x04e1, 0 },
+{ 0x04e2, 0x0101, 0x04e2, 0 },
+{ 0x04e3, 0x0102, 0x04e3, 0 },
+{ 0x04e4, 0x0101, 0x04e4, 0 },
+{ 0x04e5, 0x0102, 0x04e5, 0 },
+{ 0x04e6, 0x0101, 0x04e6, 0 },
+{ 0x04e7, 0x0102, 0x04e7, 0 },
+{ 0x04e8, 0x0101, 0x04e8, 0 },
+{ 0x04e9, 0x0102, 0x04e9, 0 },
+{ 0x04ea, 0x0101, 0x04ea, 0 },
+{ 0x04eb, 0x0102, 0x04eb, 0 },
+{ 0x04ee, 0x0101, 0x04ee, 0 },
+{ 0x04ef, 0x0102, 0x04ef, 0 },
+{ 0x04f0, 0x0101, 0x04f0, 0 },
+{ 0x04f1, 0x0102, 0x04f1, 0 },
+{ 0x04f2, 0x0101, 0x04f2, 0 },
+{ 0x04f3, 0x0102, 0x04f3, 0 },
+{ 0x04f4, 0x0101, 0x04f4, 0 },
+{ 0x04f5, 0x0102, 0x04f5, 0 },
+{ 0x04f8, 0x0101, 0x04f8, 0 },
+{ 0x04f9, 0x0102, 0x04f9, 0 },
+{ 0x0531, 0x0101, 0x0531, 0 },
+{ 0x0532, 0x0101, 0x0532, 0 },
+{ 0x0533, 0x0101, 0x0533, 0 },
+{ 0x0534, 0x0101, 0x0534, 0 },
+{ 0x0535, 0x0101, 0x0535, 0 },
+{ 0x0536, 0x0101, 0x0536, 0 },
+{ 0x0537, 0x0101, 0x0537, 0 },
+{ 0x0538, 0x0101, 0x0538, 0 },
+{ 0x0539, 0x0101, 0x0539, 0 },
+{ 0x053a, 0x0101, 0x053a, 0 },
+{ 0x053b, 0x0101, 0x053b, 0 },
+{ 0x053c, 0x0101, 0x053c, 0 },
+{ 0x053d, 0x0101, 0x053d, 0 },
+{ 0x053e, 0x0101, 0x053e, 0 },
+{ 0x053f, 0x0101, 0x053f, 0 },
+{ 0x0540, 0x0101, 0x0540, 0 },
+{ 0x0541, 0x0101, 0x0541, 0 },
+{ 0x0542, 0x0101, 0x0542, 0 },
+{ 0x0543, 0x0101, 0x0543, 0 },
+{ 0x0544, 0x0101, 0x0544, 0 },
+{ 0x0545, 0x0101, 0x0545, 0 },
+{ 0x0546, 0x0101, 0x0546, 0 },
+{ 0x0547, 0x0101, 0x0547, 0 },
+{ 0x0548, 0x0101, 0x0548, 0 },
+{ 0x0549, 0x0101, 0x0549, 0 },
+{ 0x054a, 0x0101, 0x054a, 0 },
+{ 0x054b, 0x0101, 0x054b, 0 },
+{ 0x054c, 0x0101, 0x054c, 0 },
+{ 0x054d, 0x0101, 0x054d, 0 },
+{ 0x054e, 0x0101, 0x054e, 0 },
+{ 0x054f, 0x0101, 0x054f, 0 },
+{ 0x0550, 0x0101, 0x0550, 0 },
+{ 0x0551, 0x0101, 0x0551, 0 },
+{ 0x0552, 0x0101, 0x0552, 0 },
+{ 0x0553, 0x0101, 0x0553, 0 },
+{ 0x0554, 0x0101, 0x0554, 0 },
+{ 0x0555, 0x0101, 0x0555, 0 },
+{ 0x0556, 0x0101, 0x0556, 0 },
+{ 0x0559, 0x0010, 0x0000, 6 },
+{ 0x0561, 0x0102, 0x0561, 0 },
+{ 0x0562, 0x0102, 0x0562, 0 },
+{ 0x0563, 0x0102, 0x0563, 0 },
+{ 0x0564, 0x0102, 0x0564, 0 },
+{ 0x0565, 0x0102, 0x0565, 0 },
+{ 0x0566, 0x0102, 0x0566, 0 },
+{ 0x0567, 0x0102, 0x0567, 0 },
+{ 0x0568, 0x0102, 0x0568, 0 },
+{ 0x0569, 0x0102, 0x0569, 0 },
+{ 0x056a, 0x0102, 0x056a, 0 },
+{ 0x056b, 0x0102, 0x056b, 0 },
+{ 0x056c, 0x0102, 0x056c, 0 },
+{ 0x056d, 0x0102, 0x056d, 0 },
+{ 0x056e, 0x0102, 0x056e, 0 },
+{ 0x056f, 0x0102, 0x056f, 0 },
+{ 0x0570, 0x0102, 0x0570, 0 },
+{ 0x0571, 0x0102, 0x0571, 0 },
+{ 0x0572, 0x0102, 0x0572, 0 },
+{ 0x0573, 0x0102, 0x0573, 0 },
+{ 0x0574, 0x0102, 0x0574, 0 },
+{ 0x0575, 0x0102, 0x0575, 0 },
+{ 0x0576, 0x0102, 0x0576, 0 },
+{ 0x0577, 0x0102, 0x0577, 0 },
+{ 0x0578, 0x0102, 0x0578, 0 },
+{ 0x0579, 0x0102, 0x0579, 0 },
+{ 0x057a, 0x0102, 0x057a, 0 },
+{ 0x057b, 0x0102, 0x057b, 0 },
+{ 0x057c, 0x0102, 0x057c, 0 },
+{ 0x057d, 0x0102, 0x057d, 0 },
+{ 0x057e, 0x0102, 0x057e, 0 },
+{ 0x057f, 0x0102, 0x057f, 0 },
+{ 0x0580, 0x0102, 0x0580, 0 },
+{ 0x0581, 0x0102, 0x0581, 0 },
+{ 0x0582, 0x0102, 0x0582, 0 },
+{ 0x0583, 0x0102, 0x0583, 0 },
+{ 0x0584, 0x0102, 0x0584, 0 },
+{ 0x0585, 0x0102, 0x0585, 0 },
+{ 0x0586, 0x0102, 0x0586, 0 },
+{ 0x0587, 0x0102, 0x0587, 0 },
+{ 0x0589, 0x0010, 0x0000, 51 },
+{ 0x05d0, 0x0100, 0x0000, 29 },
+{ 0x05f3, 0x0010, 0x0000, 4 },
+{ 0x0621, 0x0100, 0x0000, 25 },
+{ 0x0640, 0x0010, 0x0000, 0 },
+{ 0x0641, 0x0100, 0x0000, 9 },
+{ 0x064b, 0x0010, 0x0000, 7 },
+{ 0x0660, 0x0004, 0x0000, 9 },
+{ 0x066a, 0x0010, 0x0000, 4 },
+{ 0x0671, 0x0100, 0x0000, 94 },
+{ 0x06d4, 0x0010, 0x0000, 0 },
+{ 0x06d5, 0x0100, 0x0000, 0 },
+{ 0x06d6, 0x0010, 0x0000, 23 },
+{ 0x06f0, 0x0004, 0x0000, 9 },
+{ 0x0901, 0x0010, 0x0000, 2 },
+{ 0x0905, 0x0100, 0x0000, 52 },
+{ 0x093c, 0x0010, 0x0000, 22 },
+{ 0x0958, 0x0100, 0x0000, 9 },
+{ 0x0962, 0x0010, 0x0000, 3 },
+{ 0x0966, 0x0004, 0x0000, 9 },
+{ 0x0970, 0x0010, 0x0000, 3 },
+{ 0x0985, 0x0100, 0x0000, 43 },
+{ 0x09bc, 0x0010, 0x0000, 13 },
+{ 0x09dc, 0x0100, 0x0000, 4 },
+{ 0x09e2, 0x0010, 0x0000, 1 },
+{ 0x09e6, 0x0004, 0x0000, 9 },
+{ 0x09f0, 0x0100, 0x0000, 1 },
+{ 0x09f2, 0x0010, 0x0000, 9 },
+{ 0x0a05, 0x0100, 0x0000, 42 },
+{ 0x0a3c, 0x0010, 0x0000, 10 },
+{ 0x0a59, 0x0100, 0x0000, 4 },
+{ 0x0a66, 0x0004, 0x0000, 9 },
+{ 0x0a70, 0x0010, 0x0000, 7 },
+{ 0x0a85, 0x0100, 0x0000, 46 },
+{ 0x0abc, 0x0010, 0x0000, 16 },
+{ 0x0ae0, 0x0100, 0x0000, 0 },
+{ 0x0ae6, 0x0004, 0x0000, 9 },
+{ 0x0b01, 0x0010, 0x0000, 2 },
+{ 0x0b05, 0x0100, 0x0000, 44 },
+{ 0x0b3c, 0x0010, 0x0000, 14 },
+{ 0x0b5c, 0x0100, 0x0000, 4 },
+{ 0x0b66, 0x0004, 0x0000, 9 },
+{ 0x0b70, 0x0010, 0x0000, 2 },
+{ 0x0b85, 0x0100, 0x0000, 33 },
+{ 0x0bbe, 0x0010, 0x0000, 12 },
+{ 0x0be7, 0x0004, 0x0000, 11 },
+{ 0x0c01, 0x0010, 0x0000, 2 },
+{ 0x0c05, 0x0100, 0x0000, 48 },
+{ 0x0c3e, 0x0010, 0x0000, 15 },
+{ 0x0c60, 0x0100, 0x0000, 1 },
+{ 0x0c66, 0x0004, 0x0000, 9 },
+{ 0x0c82, 0x0010, 0x0000, 1 },
+{ 0x0c85, 0x0100, 0x0000, 48 },
+{ 0x0cbe, 0x0010, 0x0000, 15 },
+{ 0x0cde, 0x0100, 0x0000, 2 },
+{ 0x0ce6, 0x0004, 0x0000, 9 },
+{ 0x0d02, 0x0010, 0x0000, 1 },
+{ 0x0d05, 0x0100, 0x0000, 49 },
+{ 0x0d3e, 0x0010, 0x0000, 13 },
+{ 0x0d60, 0x0100, 0x0000, 1 },
+{ 0x0d66, 0x0004, 0x0000, 9 },
+{ 0x0e01, 0x0100, 0x0000, 57 },
+{ 0x0e3f, 0x0010, 0x0000, 0 },
+{ 0x0e40, 0x0100, 0x0000, 14 },
+{ 0x0e4f, 0x0010, 0x0000, 0 },
+{ 0x0e50, 0x0004, 0x0000, 9 },
+{ 0x0e5a, 0x0010, 0x0000, 1 },
+{ 0x0e81, 0x0100, 0x0000, 26 },
+{ 0x0eaf, 0x0010, 0x0000, 25 },
+{ 0x0ed0, 0x0004, 0x0000, 9 },
+{ 0x0edc, 0x0100, 0x0000, 1 },
+{ 0x0f00, 0x0010, 0x0000, 31 },
+{ 0x0f20, 0x0004, 0x0000, 19 },
+{ 0x0f34, 0x0010, 0x0000, 11 },
+{ 0x0f40, 0x0100, 0x0000, 40 },
+{ 0x0f71, 0x0010, 0x0000, 26 },
+{ 0x0f90, 0x0100, 0x0000, 35 },
+{ 0x10a0, 0x0101, 0x10a0, 0 },
+{ 0x10a1, 0x0101, 0x10a1, 0 },
+{ 0x10a2, 0x0101, 0x10a2, 0 },
+{ 0x10a3, 0x0101, 0x10a3, 0 },
+{ 0x10a4, 0x0101, 0x10a4, 0 },
+{ 0x10a5, 0x0101, 0x10a5, 0 },
+{ 0x10a6, 0x0101, 0x10a6, 0 },
+{ 0x10a7, 0x0101, 0x10a7, 0 },
+{ 0x10a8, 0x0101, 0x10a8, 0 },
+{ 0x10a9, 0x0101, 0x10a9, 0 },
+{ 0x10aa, 0x0101, 0x10aa, 0 },
+{ 0x10ab, 0x0101, 0x10ab, 0 },
+{ 0x10ac, 0x0101, 0x10ac, 0 },
+{ 0x10ad, 0x0101, 0x10ad, 0 },
+{ 0x10ae, 0x0101, 0x10ae, 0 },
+{ 0x10af, 0x0101, 0x10af, 0 },
+{ 0x10b0, 0x0101, 0x10b0, 0 },
+{ 0x10b1, 0x0101, 0x10b1, 0 },
+{ 0x10b2, 0x0101, 0x10b2, 0 },
+{ 0x10b3, 0x0101, 0x10b3, 0 },
+{ 0x10b4, 0x0101, 0x10b4, 0 },
+{ 0x10b5, 0x0101, 0x10b5, 0 },
+{ 0x10b6, 0x0101, 0x10b6, 0 },
+{ 0x10b7, 0x0101, 0x10b7, 0 },
+{ 0x10b8, 0x0101, 0x10b8, 0 },
+{ 0x10b9, 0x0101, 0x10b9, 0 },
+{ 0x10ba, 0x0101, 0x10ba, 0 },
+{ 0x10bb, 0x0101, 0x10bb, 0 },
+{ 0x10bc, 0x0101, 0x10bc, 0 },
+{ 0x10bd, 0x0101, 0x10bd, 0 },
+{ 0x10be, 0x0101, 0x10be, 0 },
+{ 0x10bf, 0x0101, 0x10bf, 0 },
+{ 0x10c0, 0x0101, 0x10c0, 0 },
+{ 0x10c1, 0x0101, 0x10c1, 0 },
+{ 0x10c2, 0x0101, 0x10c2, 0 },
+{ 0x10c3, 0x0101, 0x10c3, 0 },
+{ 0x10c4, 0x0101, 0x10c4, 0 },
+{ 0x10c5, 0x0101, 0x10c5, 0 },
+{ 0x10d0, 0x0102, 0x10d0, 0 },
+{ 0x10d1, 0x0102, 0x10d1, 0 },
+{ 0x10d2, 0x0102, 0x10d2, 0 },
+{ 0x10d3, 0x0102, 0x10d3, 0 },
+{ 0x10d4, 0x0102, 0x10d4, 0 },
+{ 0x10d5, 0x0102, 0x10d5, 0 },
+{ 0x10d6, 0x0102, 0x10d6, 0 },
+{ 0x10d7, 0x0102, 0x10d7, 0 },
+{ 0x10d8, 0x0102, 0x10d8, 0 },
+{ 0x10d9, 0x0102, 0x10d9, 0 },
+{ 0x10da, 0x0102, 0x10da, 0 },
+{ 0x10db, 0x0102, 0x10db, 0 },
+{ 0x10dc, 0x0102, 0x10dc, 0 },
+{ 0x10dd, 0x0102, 0x10dd, 0 },
+{ 0x10de, 0x0102, 0x10de, 0 },
+{ 0x10df, 0x0102, 0x10df, 0 },
+{ 0x10e0, 0x0102, 0x10e0, 0 },
+{ 0x10e1, 0x0102, 0x10e1, 0 },
+{ 0x10e2, 0x0102, 0x10e2, 0 },
+{ 0x10e3, 0x0102, 0x10e3, 0 },
+{ 0x10e4, 0x0102, 0x10e4, 0 },
+{ 0x10e5, 0x0102, 0x10e5, 0 },
+{ 0x10e6, 0x0102, 0x10e6, 0 },
+{ 0x10e7, 0x0102, 0x10e7, 0 },
+{ 0x10e8, 0x0102, 0x10e8, 0 },
+{ 0x10e9, 0x0102, 0x10e9, 0 },
+{ 0x10ea, 0x0102, 0x10ea, 0 },
+{ 0x10eb, 0x0102, 0x10eb, 0 },
+{ 0x10ec, 0x0102, 0x10ec, 0 },
+{ 0x10ed, 0x0102, 0x10ed, 0 },
+{ 0x10ee, 0x0102, 0x10ee, 0 },
+{ 0x10ef, 0x0102, 0x10ef, 0 },
+{ 0x10f0, 0x0102, 0x10f0, 0 },
+{ 0x10f1, 0x0102, 0x10f1, 0 },
+{ 0x10f2, 0x0102, 0x10f2, 0 },
+{ 0x10f3, 0x0102, 0x10f3, 0 },
+{ 0x10f4, 0x0102, 0x10f4, 0 },
+{ 0x10f5, 0x0102, 0x10f5, 0 },
+{ 0x10f6, 0x0102, 0x10f6, 0 },
+{ 0x10fb, 0x0010, 0x0000, 0 },
+{ 0x1100, 0x0100, 0x0000, 239 },
+{ 0x1e00, 0x0101, 0x1e00, 0 },
+{ 0x1e01, 0x0102, 0x1e01, 0 },
+{ 0x1e02, 0x0101, 0x1e02, 0 },
+{ 0x1e03, 0x0102, 0x1e03, 0 },
+{ 0x1e04, 0x0101, 0x1e04, 0 },
+{ 0x1e05, 0x0102, 0x1e05, 0 },
+{ 0x1e06, 0x0101, 0x1e06, 0 },
+{ 0x1e07, 0x0102, 0x1e07, 0 },
+{ 0x1e08, 0x0101, 0x1e08, 0 },
+{ 0x1e09, 0x0102, 0x1e09, 0 },
+{ 0x1e0a, 0x0101, 0x1e0a, 0 },
+{ 0x1e0b, 0x0102, 0x1e0b, 0 },
+{ 0x1e0c, 0x0101, 0x1e0c, 0 },
+{ 0x1e0d, 0x0102, 0x1e0d, 0 },
+{ 0x1e0e, 0x0101, 0x1e0e, 0 },
+{ 0x1e0f, 0x0102, 0x1e0f, 0 },
+{ 0x1e10, 0x0101, 0x1e10, 0 },
+{ 0x1e11, 0x0102, 0x1e11, 0 },
+{ 0x1e12, 0x0101, 0x1e12, 0 },
+{ 0x1e13, 0x0102, 0x1e13, 0 },
+{ 0x1e14, 0x0101, 0x1e14, 0 },
+{ 0x1e15, 0x0102, 0x1e15, 0 },
+{ 0x1e16, 0x0101, 0x1e16, 0 },
+{ 0x1e17, 0x0102, 0x1e17, 0 },
+{ 0x1e18, 0x0101, 0x1e18, 0 },
+{ 0x1e19, 0x0102, 0x1e19, 0 },
+{ 0x1e1a, 0x0101, 0x1e1a, 0 },
+{ 0x1e1b, 0x0102, 0x1e1b, 0 },
+{ 0x1e1c, 0x0101, 0x1e1c, 0 },
+{ 0x1e1d, 0x0102, 0x1e1d, 0 },
+{ 0x1e1e, 0x0101, 0x1e1e, 0 },
+{ 0x1e1f, 0x0102, 0x1e1f, 0 },
+{ 0x1e20, 0x0101, 0x1e20, 0 },
+{ 0x1e21, 0x0102, 0x1e21, 0 },
+{ 0x1e22, 0x0101, 0x1e22, 0 },
+{ 0x1e23, 0x0102, 0x1e23, 0 },
+{ 0x1e24, 0x0101, 0x1e24, 0 },
+{ 0x1e25, 0x0102, 0x1e25, 0 },
+{ 0x1e26, 0x0101, 0x1e26, 0 },
+{ 0x1e27, 0x0102, 0x1e27, 0 },
+{ 0x1e28, 0x0101, 0x1e28, 0 },
+{ 0x1e29, 0x0102, 0x1e29, 0 },
+{ 0x1e2a, 0x0101, 0x1e2a, 0 },
+{ 0x1e2b, 0x0102, 0x1e2b, 0 },
+{ 0x1e2c, 0x0101, 0x1e2c, 0 },
+{ 0x1e2d, 0x0102, 0x1e2d, 0 },
+{ 0x1e2e, 0x0101, 0x1e2e, 0 },
+{ 0x1e2f, 0x0102, 0x1e2f, 0 },
+{ 0x1e30, 0x0101, 0x1e30, 0 },
+{ 0x1e31, 0x0102, 0x1e31, 0 },
+{ 0x1e32, 0x0101, 0x1e32, 0 },
+{ 0x1e33, 0x0102, 0x1e33, 0 },
+{ 0x1e34, 0x0101, 0x1e34, 0 },
+{ 0x1e35, 0x0102, 0x1e35, 0 },
+{ 0x1e36, 0x0101, 0x1e36, 0 },
+{ 0x1e37, 0x0102, 0x1e37, 0 },
+{ 0x1e38, 0x0101, 0x1e38, 0 },
+{ 0x1e39, 0x0102, 0x1e39, 0 },
+{ 0x1e3a, 0x0101, 0x1e3a, 0 },
+{ 0x1e3b, 0x0102, 0x1e3b, 0 },
+{ 0x1e3c, 0x0101, 0x1e3c, 0 },
+{ 0x1e3d, 0x0102, 0x1e3d, 0 },
+{ 0x1e3e, 0x0101, 0x1e3e, 0 },
+{ 0x1e3f, 0x0102, 0x1e3f, 0 },
+{ 0x1e40, 0x0101, 0x1e40, 0 },
+{ 0x1e41, 0x0102, 0x1e41, 0 },
+{ 0x1e42, 0x0101, 0x1e42, 0 },
+{ 0x1e43, 0x0102, 0x1e43, 0 },
+{ 0x1e44, 0x0101, 0x1e44, 0 },
+{ 0x1e45, 0x0102, 0x1e45, 0 },
+{ 0x1e46, 0x0101, 0x1e46, 0 },
+{ 0x1e47, 0x0102, 0x1e47, 0 },
+{ 0x1e48, 0x0101, 0x1e48, 0 },
+{ 0x1e49, 0x0102, 0x1e49, 0 },
+{ 0x1e4a, 0x0101, 0x1e4a, 0 },
+{ 0x1e4b, 0x0102, 0x1e4b, 0 },
+{ 0x1e4c, 0x0101, 0x1e4c, 0 },
+{ 0x1e4d, 0x0102, 0x1e4d, 0 },
+{ 0x1e4e, 0x0101, 0x1e4e, 0 },
+{ 0x1e4f, 0x0102, 0x1e4f, 0 },
+{ 0x1e50, 0x0101, 0x1e50, 0 },
+{ 0x1e51, 0x0102, 0x1e51, 0 },
+{ 0x1e52, 0x0101, 0x1e52, 0 },
+{ 0x1e53, 0x0102, 0x1e53, 0 },
+{ 0x1e54, 0x0101, 0x1e54, 0 },
+{ 0x1e55, 0x0102, 0x1e55, 0 },
+{ 0x1e56, 0x0101, 0x1e56, 0 },
+{ 0x1e57, 0x0102, 0x1e57, 0 },
+{ 0x1e58, 0x0101, 0x1e58, 0 },
+{ 0x1e59, 0x0102, 0x1e59, 0 },
+{ 0x1e5a, 0x0101, 0x1e5a, 0 },
+{ 0x1e5b, 0x0102, 0x1e5b, 0 },
+{ 0x1e5c, 0x0101, 0x1e5c, 0 },
+{ 0x1e5d, 0x0102, 0x1e5d, 0 },
+{ 0x1e5e, 0x0101, 0x1e5e, 0 },
+{ 0x1e5f, 0x0102, 0x1e5f, 0 },
+{ 0x1e60, 0x0101, 0x1e60, 0 },
+{ 0x1e61, 0x0102, 0x1e61, 0 },
+{ 0x1e62, 0x0101, 0x1e62, 0 },
+{ 0x1e63, 0x0102, 0x1e63, 0 },
+{ 0x1e64, 0x0101, 0x1e64, 0 },
+{ 0x1e65, 0x0102, 0x1e65, 0 },
+{ 0x1e66, 0x0101, 0x1e66, 0 },
+{ 0x1e67, 0x0102, 0x1e67, 0 },
+{ 0x1e68, 0x0101, 0x1e68, 0 },
+{ 0x1e69, 0x0102, 0x1e69, 0 },
+{ 0x1e6a, 0x0101, 0x1e6a, 0 },
+{ 0x1e6b, 0x0102, 0x1e6b, 0 },
+{ 0x1e6c, 0x0101, 0x1e6c, 0 },
+{ 0x1e6d, 0x0102, 0x1e6d, 0 },
+{ 0x1e6e, 0x0101, 0x1e6e, 0 },
+{ 0x1e6f, 0x0102, 0x1e6f, 0 },
+{ 0x1e70, 0x0101, 0x1e70, 0 },
+{ 0x1e71, 0x0102, 0x1e71, 0 },
+{ 0x1e72, 0x0101, 0x1e72, 0 },
+{ 0x1e73, 0x0102, 0x1e73, 0 },
+{ 0x1e74, 0x0101, 0x1e74, 0 },
+{ 0x1e75, 0x0102, 0x1e75, 0 },
+{ 0x1e76, 0x0101, 0x1e76, 0 },
+{ 0x1e77, 0x0102, 0x1e77, 0 },
+{ 0x1e78, 0x0101, 0x1e78, 0 },
+{ 0x1e79, 0x0102, 0x1e79, 0 },
+{ 0x1e7a, 0x0101, 0x1e7a, 0 },
+{ 0x1e7b, 0x0102, 0x1e7b, 0 },
+{ 0x1e7c, 0x0101, 0x1e7c, 0 },
+{ 0x1e7d, 0x0102, 0x1e7d, 0 },
+{ 0x1e7e, 0x0101, 0x1e7e, 0 },
+{ 0x1e7f, 0x0102, 0x1e7f, 0 },
+{ 0x1e80, 0x0101, 0x1e80, 0 },
+{ 0x1e81, 0x0102, 0x1e81, 0 },
+{ 0x1e82, 0x0101, 0x1e82, 0 },
+{ 0x1e83, 0x0102, 0x1e83, 0 },
+{ 0x1e84, 0x0101, 0x1e84, 0 },
+{ 0x1e85, 0x0102, 0x1e85, 0 },
+{ 0x1e86, 0x0101, 0x1e86, 0 },
+{ 0x1e87, 0x0102, 0x1e87, 0 },
+{ 0x1e88, 0x0101, 0x1e88, 0 },
+{ 0x1e89, 0x0102, 0x1e89, 0 },
+{ 0x1e8a, 0x0101, 0x1e8a, 0 },
+{ 0x1e8b, 0x0102, 0x1e8b, 0 },
+{ 0x1e8c, 0x0101, 0x1e8c, 0 },
+{ 0x1e8d, 0x0102, 0x1e8d, 0 },
+{ 0x1e8e, 0x0101, 0x1e8e, 0 },
+{ 0x1e8f, 0x0102, 0x1e8f, 0 },
+{ 0x1e90, 0x0101, 0x1e90, 0 },
+{ 0x1e91, 0x0102, 0x1e91, 0 },
+{ 0x1e92, 0x0101, 0x1e92, 0 },
+{ 0x1e93, 0x0102, 0x1e93, 0 },
+{ 0x1e94, 0x0101, 0x1e94, 0 },
+{ 0x1e95, 0x0102, 0x1e95, 0 },
+{ 0x1e96, 0x0102, 0x1e96, 0 },
+{ 0x1e97, 0x0102, 0x1e97, 0 },
+{ 0x1e98, 0x0102, 0x1e98, 0 },
+{ 0x1e99, 0x0102, 0x1e99, 0 },
+{ 0x1e9a, 0x0102, 0x1e9a, 0 },
+{ 0x1e9b, 0x0102, 0x1e9b, 0 },
+{ 0x1ea0, 0x0101, 0x1ea0, 0 },
+{ 0x1ea1, 0x0102, 0x1ea1, 0 },
+{ 0x1ea2, 0x0101, 0x1ea2, 0 },
+{ 0x1ea3, 0x0102, 0x1ea3, 0 },
+{ 0x1ea4, 0x0101, 0x1ea4, 0 },
+{ 0x1ea5, 0x0102, 0x1ea5, 0 },
+{ 0x1ea6, 0x0101, 0x1ea6, 0 },
+{ 0x1ea7, 0x0102, 0x1ea7, 0 },
+{ 0x1ea8, 0x0101, 0x1ea8, 0 },
+{ 0x1ea9, 0x0102, 0x1ea9, 0 },
+{ 0x1eaa, 0x0101, 0x1eaa, 0 },
+{ 0x1eab, 0x0102, 0x1eab, 0 },
+{ 0x1eac, 0x0101, 0x1eac, 0 },
+{ 0x1ead, 0x0102, 0x1ead, 0 },
+{ 0x1eae, 0x0101, 0x1eae, 0 },
+{ 0x1eaf, 0x0102, 0x1eaf, 0 },
+{ 0x1eb0, 0x0101, 0x1eb0, 0 },
+{ 0x1eb1, 0x0102, 0x1eb1, 0 },
+{ 0x1eb2, 0x0101, 0x1eb2, 0 },
+{ 0x1eb3, 0x0102, 0x1eb3, 0 },
+{ 0x1eb4, 0x0101, 0x1eb4, 0 },
+{ 0x1eb5, 0x0102, 0x1eb5, 0 },
+{ 0x1eb6, 0x0101, 0x1eb6, 0 },
+{ 0x1eb7, 0x0102, 0x1eb7, 0 },
+{ 0x1eb8, 0x0101, 0x1eb8, 0 },
+{ 0x1eb9, 0x0102, 0x1eb9, 0 },
+{ 0x1eba, 0x0101, 0x1eba, 0 },
+{ 0x1ebb, 0x0102, 0x1ebb, 0 },
+{ 0x1ebc, 0x0101, 0x1ebc, 0 },
+{ 0x1ebd, 0x0102, 0x1ebd, 0 },
+{ 0x1ebe, 0x0101, 0x1ebe, 0 },
+{ 0x1ebf, 0x0102, 0x1ebf, 0 },
+{ 0x1ec0, 0x0101, 0x1ec0, 0 },
+{ 0x1ec1, 0x0102, 0x1ec1, 0 },
+{ 0x1ec2, 0x0101, 0x1ec2, 0 },
+{ 0x1ec3, 0x0102, 0x1ec3, 0 },
+{ 0x1ec4, 0x0101, 0x1ec4, 0 },
+{ 0x1ec5, 0x0102, 0x1ec5, 0 },
+{ 0x1ec6, 0x0101, 0x1ec6, 0 },
+{ 0x1ec7, 0x0102, 0x1ec7, 0 },
+{ 0x1ec8, 0x0101, 0x1ec8, 0 },
+{ 0x1ec9, 0x0102, 0x1ec9, 0 },
+{ 0x1eca, 0x0101, 0x1eca, 0 },
+{ 0x1ecb, 0x0102, 0x1ecb, 0 },
+{ 0x1ecc, 0x0101, 0x1ecc, 0 },
+{ 0x1ecd, 0x0102, 0x1ecd, 0 },
+{ 0x1ece, 0x0101, 0x1ece, 0 },
+{ 0x1ecf, 0x0102, 0x1ecf, 0 },
+{ 0x1ed0, 0x0101, 0x1ed0, 0 },
+{ 0x1ed1, 0x0102, 0x1ed1, 0 },
+{ 0x1ed2, 0x0101, 0x1ed2, 0 },
+{ 0x1ed3, 0x0102, 0x1ed3, 0 },
+{ 0x1ed4, 0x0101, 0x1ed4, 0 },
+{ 0x1ed5, 0x0102, 0x1ed5, 0 },
+{ 0x1ed6, 0x0101, 0x1ed6, 0 },
+{ 0x1ed7, 0x0102, 0x1ed7, 0 },
+{ 0x1ed8, 0x0101, 0x1ed8, 0 },
+{ 0x1ed9, 0x0102, 0x1ed9, 0 },
+{ 0x1eda, 0x0101, 0x1eda, 0 },
+{ 0x1edb, 0x0102, 0x1edb, 0 },
+{ 0x1edc, 0x0101, 0x1edc, 0 },
+{ 0x1edd, 0x0102, 0x1edd, 0 },
+{ 0x1ede, 0x0101, 0x1ede, 0 },
+{ 0x1edf, 0x0102, 0x1edf, 0 },
+{ 0x1ee0, 0x0101, 0x1ee0, 0 },
+{ 0x1ee1, 0x0102, 0x1ee1, 0 },
+{ 0x1ee2, 0x0101, 0x1ee2, 0 },
+{ 0x1ee3, 0x0102, 0x1ee3, 0 },
+{ 0x1ee4, 0x0101, 0x1ee4, 0 },
+{ 0x1ee5, 0x0102, 0x1ee5, 0 },
+{ 0x1ee6, 0x0101, 0x1ee6, 0 },
+{ 0x1ee7, 0x0102, 0x1ee7, 0 },
+{ 0x1ee8, 0x0101, 0x1ee8, 0 },
+{ 0x1ee9, 0x0102, 0x1ee9, 0 },
+{ 0x1eea, 0x0101, 0x1eea, 0 },
+{ 0x1eeb, 0x0102, 0x1eeb, 0 },
+{ 0x1eec, 0x0101, 0x1eec, 0 },
+{ 0x1eed, 0x0102, 0x1eed, 0 },
+{ 0x1eee, 0x0101, 0x1eee, 0 },
+{ 0x1eef, 0x0102, 0x1eef, 0 },
+{ 0x1ef0, 0x0101, 0x1ef0, 0 },
+{ 0x1ef1, 0x0102, 0x1ef1, 0 },
+{ 0x1ef2, 0x0101, 0x1ef2, 0 },
+{ 0x1ef3, 0x0102, 0x1ef3, 0 },
+{ 0x1ef4, 0x0101, 0x1ef4, 0 },
+{ 0x1ef5, 0x0102, 0x1ef5, 0 },
+{ 0x1ef6, 0x0101, 0x1ef6, 0 },
+{ 0x1ef7, 0x0102, 0x1ef7, 0 },
+{ 0x1ef8, 0x0101, 0x1ef8, 0 },
+{ 0x1ef9, 0x0102, 0x1ef9, 0 },
+{ 0x1f00, 0x0102, 0x1f00, 0 },
+{ 0x1f01, 0x0102, 0x1f01, 0 },
+{ 0x1f02, 0x0102, 0x1f02, 0 },
+{ 0x1f03, 0x0102, 0x1f03, 0 },
+{ 0x1f04, 0x0102, 0x1f04, 0 },
+{ 0x1f05, 0x0102, 0x1f05, 0 },
+{ 0x1f06, 0x0102, 0x1f06, 0 },
+{ 0x1f07, 0x0102, 0x1f07, 0 },
+{ 0x1f08, 0x0101, 0x1f08, 0 },
+{ 0x1f09, 0x0101, 0x1f09, 0 },
+{ 0x1f0a, 0x0101, 0x1f0a, 0 },
+{ 0x1f0b, 0x0101, 0x1f0b, 0 },
+{ 0x1f0c, 0x0101, 0x1f0c, 0 },
+{ 0x1f0d, 0x0101, 0x1f0d, 0 },
+{ 0x1f0e, 0x0101, 0x1f0e, 0 },
+{ 0x1f0f, 0x0101, 0x1f0f, 0 },
+{ 0x1f10, 0x0102, 0x1f10, 0 },
+{ 0x1f11, 0x0102, 0x1f11, 0 },
+{ 0x1f12, 0x0102, 0x1f12, 0 },
+{ 0x1f13, 0x0102, 0x1f13, 0 },
+{ 0x1f14, 0x0102, 0x1f14, 0 },
+{ 0x1f15, 0x0102, 0x1f15, 0 },
+{ 0x1f18, 0x0101, 0x1f18, 0 },
+{ 0x1f19, 0x0101, 0x1f19, 0 },
+{ 0x1f1a, 0x0101, 0x1f1a, 0 },
+{ 0x1f1b, 0x0101, 0x1f1b, 0 },
+{ 0x1f1c, 0x0101, 0x1f1c, 0 },
+{ 0x1f1d, 0x0101, 0x1f1d, 0 },
+{ 0x1f20, 0x0102, 0x1f20, 0 },
+{ 0x1f21, 0x0102, 0x1f21, 0 },
+{ 0x1f22, 0x0102, 0x1f22, 0 },
+{ 0x1f23, 0x0102, 0x1f23, 0 },
+{ 0x1f24, 0x0102, 0x1f24, 0 },
+{ 0x1f25, 0x0102, 0x1f25, 0 },
+{ 0x1f26, 0x0102, 0x1f26, 0 },
+{ 0x1f27, 0x0102, 0x1f27, 0 },
+{ 0x1f28, 0x0101, 0x1f28, 0 },
+{ 0x1f29, 0x0101, 0x1f29, 0 },
+{ 0x1f2a, 0x0101, 0x1f2a, 0 },
+{ 0x1f2b, 0x0101, 0x1f2b, 0 },
+{ 0x1f2c, 0x0101, 0x1f2c, 0 },
+{ 0x1f2d, 0x0101, 0x1f2d, 0 },
+{ 0x1f2e, 0x0101, 0x1f2e, 0 },
+{ 0x1f2f, 0x0101, 0x1f2f, 0 },
+{ 0x1f30, 0x0102, 0x1f30, 0 },
+{ 0x1f31, 0x0102, 0x1f31, 0 },
+{ 0x1f32, 0x0102, 0x1f32, 0 },
+{ 0x1f33, 0x0102, 0x1f33, 0 },
+{ 0x1f34, 0x0102, 0x1f34, 0 },
+{ 0x1f35, 0x0102, 0x1f35, 0 },
+{ 0x1f36, 0x0102, 0x1f36, 0 },
+{ 0x1f37, 0x0102, 0x1f37, 0 },
+{ 0x1f38, 0x0101, 0x1f38, 0 },
+{ 0x1f39, 0x0101, 0x1f39, 0 },
+{ 0x1f3a, 0x0101, 0x1f3a, 0 },
+{ 0x1f3b, 0x0101, 0x1f3b, 0 },
+{ 0x1f3c, 0x0101, 0x1f3c, 0 },
+{ 0x1f3d, 0x0101, 0x1f3d, 0 },
+{ 0x1f3e, 0x0101, 0x1f3e, 0 },
+{ 0x1f3f, 0x0101, 0x1f3f, 0 },
+{ 0x1f40, 0x0102, 0x1f40, 0 },
+{ 0x1f41, 0x0102, 0x1f41, 0 },
+{ 0x1f42, 0x0102, 0x1f42, 0 },
+{ 0x1f43, 0x0102, 0x1f43, 0 },
+{ 0x1f44, 0x0102, 0x1f44, 0 },
+{ 0x1f45, 0x0102, 0x1f45, 0 },
+{ 0x1f48, 0x0101, 0x1f48, 0 },
+{ 0x1f49, 0x0101, 0x1f49, 0 },
+{ 0x1f4a, 0x0101, 0x1f4a, 0 },
+{ 0x1f4b, 0x0101, 0x1f4b, 0 },
+{ 0x1f4c, 0x0101, 0x1f4c, 0 },
+{ 0x1f4d, 0x0101, 0x1f4d, 0 },
+{ 0x1f50, 0x0102, 0x1f50, 0 },
+{ 0x1f51, 0x0102, 0x1f51, 0 },
+{ 0x1f52, 0x0102, 0x1f52, 0 },
+{ 0x1f53, 0x0102, 0x1f53, 0 },
+{ 0x1f54, 0x0102, 0x1f54, 0 },
+{ 0x1f55, 0x0102, 0x1f55, 0 },
+{ 0x1f56, 0x0102, 0x1f56, 0 },
+{ 0x1f57, 0x0102, 0x1f57, 0 },
+{ 0x1f59, 0x0101, 0x1f59, 0 },
+{ 0x1f5b, 0x0101, 0x1f5b, 0 },
+{ 0x1f5d, 0x0101, 0x1f5d, 0 },
+{ 0x1f5f, 0x0101, 0x1f5f, 0 },
+{ 0x1f60, 0x0102, 0x1f60, 0 },
+{ 0x1f61, 0x0102, 0x1f61, 0 },
+{ 0x1f62, 0x0102, 0x1f62, 0 },
+{ 0x1f63, 0x0102, 0x1f63, 0 },
+{ 0x1f64, 0x0102, 0x1f64, 0 },
+{ 0x1f65, 0x0102, 0x1f65, 0 },
+{ 0x1f66, 0x0102, 0x1f66, 0 },
+{ 0x1f67, 0x0102, 0x1f67, 0 },
+{ 0x1f68, 0x0101, 0x1f68, 0 },
+{ 0x1f69, 0x0101, 0x1f69, 0 },
+{ 0x1f6a, 0x0101, 0x1f6a, 0 },
+{ 0x1f6b, 0x0101, 0x1f6b, 0 },
+{ 0x1f6c, 0x0101, 0x1f6c, 0 },
+{ 0x1f6d, 0x0101, 0x1f6d, 0 },
+{ 0x1f6e, 0x0101, 0x1f6e, 0 },
+{ 0x1f6f, 0x0101, 0x1f6f, 0 },
+{ 0x1f70, 0x0102, 0x1f70, 0 },
+{ 0x1f71, 0x0102, 0x1f71, 0 },
+{ 0x1f72, 0x0102, 0x1f72, 0 },
+{ 0x1f73, 0x0102, 0x1f73, 0 },
+{ 0x1f74, 0x0102, 0x1f74, 0 },
+{ 0x1f75, 0x0102, 0x1f75, 0 },
+{ 0x1f76, 0x0102, 0x1f76, 0 },
+{ 0x1f77, 0x0102, 0x1f77, 0 },
+{ 0x1f78, 0x0102, 0x1f78, 0 },
+{ 0x1f79, 0x0102, 0x1f79, 0 },
+{ 0x1f7a, 0x0102, 0x1f7a, 0 },
+{ 0x1f7b, 0x0102, 0x1f7b, 0 },
+{ 0x1f7c, 0x0102, 0x1f7c, 0 },
+{ 0x1f7d, 0x0102, 0x1f7d, 0 },
+{ 0x1f80, 0x0102, 0x1f80, 0 },
+{ 0x1f81, 0x0102, 0x1f81, 0 },
+{ 0x1f82, 0x0102, 0x1f82, 0 },
+{ 0x1f83, 0x0102, 0x1f83, 0 },
+{ 0x1f84, 0x0102, 0x1f84, 0 },
+{ 0x1f85, 0x0102, 0x1f85, 0 },
+{ 0x1f86, 0x0102, 0x1f86, 0 },
+{ 0x1f87, 0x0102, 0x1f87, 0 },
+{ 0x1f88, 0x0101, 0x1f88, 0 },
+{ 0x1f89, 0x0101, 0x1f89, 0 },
+{ 0x1f8a, 0x0101, 0x1f8a, 0 },
+{ 0x1f8b, 0x0101, 0x1f8b, 0 },
+{ 0x1f8c, 0x0101, 0x1f8c, 0 },
+{ 0x1f8d, 0x0101, 0x1f8d, 0 },
+{ 0x1f8e, 0x0101, 0x1f8e, 0 },
+{ 0x1f8f, 0x0101, 0x1f8f, 0 },
+{ 0x1f90, 0x0102, 0x1f90, 0 },
+{ 0x1f91, 0x0102, 0x1f91, 0 },
+{ 0x1f92, 0x0102, 0x1f92, 0 },
+{ 0x1f93, 0x0102, 0x1f93, 0 },
+{ 0x1f94, 0x0102, 0x1f94, 0 },
+{ 0x1f95, 0x0102, 0x1f95, 0 },
+{ 0x1f96, 0x0102, 0x1f96, 0 },
+{ 0x1f97, 0x0102, 0x1f97, 0 },
+{ 0x1f98, 0x0101, 0x1f98, 0 },
+{ 0x1f99, 0x0101, 0x1f99, 0 },
+{ 0x1f9a, 0x0101, 0x1f9a, 0 },
+{ 0x1f9b, 0x0101, 0x1f9b, 0 },
+{ 0x1f9c, 0x0101, 0x1f9c, 0 },
+{ 0x1f9d, 0x0101, 0x1f9d, 0 },
+{ 0x1f9e, 0x0101, 0x1f9e, 0 },
+{ 0x1f9f, 0x0101, 0x1f9f, 0 },
+{ 0x1fa0, 0x0102, 0x1fa0, 0 },
+{ 0x1fa1, 0x0102, 0x1fa1, 0 },
+{ 0x1fa2, 0x0102, 0x1fa2, 0 },
+{ 0x1fa3, 0x0102, 0x1fa3, 0 },
+{ 0x1fa4, 0x0102, 0x1fa4, 0 },
+{ 0x1fa5, 0x0102, 0x1fa5, 0 },
+{ 0x1fa6, 0x0102, 0x1fa6, 0 },
+{ 0x1fa7, 0x0102, 0x1fa7, 0 },
+{ 0x1fa8, 0x0101, 0x1fa8, 0 },
+{ 0x1fa9, 0x0101, 0x1fa9, 0 },
+{ 0x1faa, 0x0101, 0x1faa, 0 },
+{ 0x1fab, 0x0101, 0x1fab, 0 },
+{ 0x1fac, 0x0101, 0x1fac, 0 },
+{ 0x1fad, 0x0101, 0x1fad, 0 },
+{ 0x1fae, 0x0101, 0x1fae, 0 },
+{ 0x1faf, 0x0101, 0x1faf, 0 },
+{ 0x1fb0, 0x0102, 0x1fb0, 0 },
+{ 0x1fb1, 0x0102, 0x1fb1, 0 },
+{ 0x1fb2, 0x0102, 0x1fb2, 0 },
+{ 0x1fb3, 0x0102, 0x1fb3, 0 },
+{ 0x1fb4, 0x0102, 0x1fb4, 0 },
+{ 0x1fb6, 0x0102, 0x1fb6, 0 },
+{ 0x1fb7, 0x0102, 0x1fb7, 0 },
+{ 0x1fb8, 0x0101, 0x1fb8, 0 },
+{ 0x1fb9, 0x0101, 0x1fb9, 0 },
+{ 0x1fba, 0x0101, 0x1fba, 0 },
+{ 0x1fbb, 0x0101, 0x1fbb, 0 },
+{ 0x1fbc, 0x0101, 0x1fbc, 0 },
+{ 0x1fbd, 0x0010, 0x0000, 4 },
+{ 0x1fc2, 0x0102, 0x1fc2, 0 },
+{ 0x1fc3, 0x0102, 0x1fc3, 0 },
+{ 0x1fc4, 0x0102, 0x1fc4, 0 },
+{ 0x1fc6, 0x0102, 0x1fc6, 0 },
+{ 0x1fc7, 0x0102, 0x1fc7, 0 },
+{ 0x1fc8, 0x0101, 0x1fc8, 0 },
+{ 0x1fc9, 0x0101, 0x1fc9, 0 },
+{ 0x1fca, 0x0101, 0x1fca, 0 },
+{ 0x1fcb, 0x0101, 0x1fcb, 0 },
+{ 0x1fcc, 0x0101, 0x1fcc, 0 },
+{ 0x1fcd, 0x0010, 0x0000, 2 },
+{ 0x1fd0, 0x0102, 0x1fd0, 0 },
+{ 0x1fd1, 0x0102, 0x1fd1, 0 },
+{ 0x1fd2, 0x0102, 0x1fd2, 0 },
+{ 0x1fd3, 0x0102, 0x1fd3, 0 },
+{ 0x1fd6, 0x0102, 0x1fd6, 0 },
+{ 0x1fd7, 0x0102, 0x1fd7, 0 },
+{ 0x1fd8, 0x0101, 0x1fd8, 0 },
+{ 0x1fd9, 0x0101, 0x1fd9, 0 },
+{ 0x1fda, 0x0101, 0x1fda, 0 },
+{ 0x1fdb, 0x0101, 0x1fdb, 0 },
+{ 0x1fdd, 0x0010, 0x0000, 2 },
+{ 0x1fe0, 0x0102, 0x1fe0, 0 },
+{ 0x1fe1, 0x0102, 0x1fe1, 0 },
+{ 0x1fe2, 0x0102, 0x1fe2, 0 },
+{ 0x1fe3, 0x0102, 0x1fe3, 0 },
+{ 0x1fe4, 0x0102, 0x1fe4, 0 },
+{ 0x1fe5, 0x0102, 0x1fe5, 0 },
+{ 0x1fe6, 0x0102, 0x1fe6, 0 },
+{ 0x1fe7, 0x0102, 0x1fe7, 0 },
+{ 0x1fe8, 0x0101, 0x1fe8, 0 },
+{ 0x1fe9, 0x0101, 0x1fe9, 0 },
+{ 0x1fea, 0x0101, 0x1fea, 0 },
+{ 0x1feb, 0x0101, 0x1feb, 0 },
+{ 0x1fec, 0x0101, 0x1fec, 0 },
+{ 0x1fed, 0x0010, 0x0000, 2 },
+{ 0x1ff2, 0x0102, 0x1ff2, 0 },
+{ 0x1ff3, 0x0102, 0x1ff3, 0 },
+{ 0x1ff4, 0x0102, 0x1ff4, 0 },
+{ 0x1ff6, 0x0102, 0x1ff6, 0 },
+{ 0x1ff7, 0x0102, 0x1ff7, 0 },
+{ 0x1ff8, 0x0101, 0x1ff8, 0 },
+{ 0x1ff9, 0x0101, 0x1ff9, 0 },
+{ 0x1ffa, 0x0101, 0x1ffa, 0 },
+{ 0x1ffb, 0x0101, 0x1ffb, 0 },
+{ 0x1ffc, 0x0101, 0x1ffc, 0 },
+{ 0x1ffd, 0x0010, 0x0000, 1 },
+{ 0x2000, 0x0018, 0x0000, 11 },
+{ 0x200c, 0x0030, 0x0000, 3 },
+{ 0x2010, 0x0010, 0x0000, 23 },
+{ 0x2028, 0x0030, 0x0000, 6 },
+{ 0x2030, 0x0010, 0x0000, 22 },
+{ 0x206a, 0x0030, 0x0000, 5 },
+{ 0x2070, 0x0004, 0x0000, 6 },
+{ 0x207a, 0x0010, 0x0000, 5 },
+{ 0x2080, 0x0004, 0x0000, 9 },
+{ 0x208a, 0x0010, 0x0000, 703 },
+{ 0x249c, 0x0112, 0x249c, 0 },
+{ 0x249d, 0x0112, 0x249d, 0 },
+{ 0x249e, 0x0112, 0x249e, 0 },
+{ 0x249f, 0x0112, 0x249f, 0 },
+{ 0x24a0, 0x0112, 0x24a0, 0 },
+{ 0x24a1, 0x0112, 0x24a1, 0 },
+{ 0x24a2, 0x0112, 0x24a2, 0 },
+{ 0x24a3, 0x0112, 0x24a3, 0 },
+{ 0x24a4, 0x0112, 0x24a4, 0 },
+{ 0x24a5, 0x0112, 0x24a5, 0 },
+{ 0x24a6, 0x0112, 0x24a6, 0 },
+{ 0x24a7, 0x0112, 0x24a7, 0 },
+{ 0x24a8, 0x0112, 0x24a8, 0 },
+{ 0x24a9, 0x0112, 0x24a9, 0 },
+{ 0x24aa, 0x0112, 0x24aa, 0 },
+{ 0x24ab, 0x0112, 0x24ab, 0 },
+{ 0x24ac, 0x0112, 0x24ac, 0 },
+{ 0x24ad, 0x0112, 0x24ad, 0 },
+{ 0x24ae, 0x0112, 0x24ae, 0 },
+{ 0x24af, 0x0112, 0x24af, 0 },
+{ 0x24b0, 0x0112, 0x24b0, 0 },
+{ 0x24b1, 0x0112, 0x24b1, 0 },
+{ 0x24b2, 0x0112, 0x24b2, 0 },
+{ 0x24b3, 0x0112, 0x24b3, 0 },
+{ 0x24b4, 0x0112, 0x24b4, 0 },
+{ 0x24b5, 0x0112, 0x24b5, 0 },
+{ 0x24b6, 0x0111, 0x24b6, 0 },
+{ 0x24b7, 0x0111, 0x24b7, 0 },
+{ 0x24b8, 0x0111, 0x24b8, 0 },
+{ 0x24b9, 0x0111, 0x24b9, 0 },
+{ 0x24ba, 0x0111, 0x24ba, 0 },
+{ 0x24bb, 0x0111, 0x24bb, 0 },
+{ 0x24bc, 0x0111, 0x24bc, 0 },
+{ 0x24bd, 0x0111, 0x24bd, 0 },
+{ 0x24be, 0x0111, 0x24be, 0 },
+{ 0x24bf, 0x0111, 0x24bf, 0 },
+{ 0x24c0, 0x0111, 0x24c0, 0 },
+{ 0x24c1, 0x0111, 0x24c1, 0 },
+{ 0x24c2, 0x0111, 0x24c2, 0 },
+{ 0x24c3, 0x0111, 0x24c3, 0 },
+{ 0x24c4, 0x0111, 0x24c4, 0 },
+{ 0x24c5, 0x0111, 0x24c5, 0 },
+{ 0x24c6, 0x0111, 0x24c6, 0 },
+{ 0x24c7, 0x0111, 0x24c7, 0 },
+{ 0x24c8, 0x0111, 0x24c8, 0 },
+{ 0x24c9, 0x0111, 0x24c9, 0 },
+{ 0x24ca, 0x0111, 0x24ca, 0 },
+{ 0x24cb, 0x0111, 0x24cb, 0 },
+{ 0x24cc, 0x0111, 0x24cc, 0 },
+{ 0x24cd, 0x0111, 0x24cd, 0 },
+{ 0x24ce, 0x0111, 0x24ce, 0 },
+{ 0x24cf, 0x0111, 0x24cf, 0 },
+{ 0x24d0, 0x0112, 0x24d0, 0 },
+{ 0x24d1, 0x0112, 0x24d1, 0 },
+{ 0x24d2, 0x0112, 0x24d2, 0 },
+{ 0x24d3, 0x0112, 0x24d3, 0 },
+{ 0x24d4, 0x0112, 0x24d4, 0 },
+{ 0x24d5, 0x0112, 0x24d5, 0 },
+{ 0x24d6, 0x0112, 0x24d6, 0 },
+{ 0x24d7, 0x0112, 0x24d7, 0 },
+{ 0x24d8, 0x0112, 0x24d8, 0 },
+{ 0x24d9, 0x0112, 0x24d9, 0 },
+{ 0x24da, 0x0112, 0x24da, 0 },
+{ 0x24db, 0x0112, 0x24db, 0 },
+{ 0x24dc, 0x0112, 0x24dc, 0 },
+{ 0x24dd, 0x0112, 0x24dd, 0 },
+{ 0x24de, 0x0112, 0x24de, 0 },
+{ 0x24df, 0x0112, 0x24df, 0 },
+{ 0x24e0, 0x0112, 0x24e0, 0 },
+{ 0x24e1, 0x0112, 0x24e1, 0 },
+{ 0x24e2, 0x0112, 0x24e2, 0 },
+{ 0x24e3, 0x0112, 0x24e3, 0 },
+{ 0x24e4, 0x0112, 0x24e4, 0 },
+{ 0x24e5, 0x0112, 0x24e5, 0 },
+{ 0x24e6, 0x0112, 0x24e6, 0 },
+{ 0x24e7, 0x0112, 0x24e7, 0 },
+{ 0x24e8, 0x0112, 0x24e8, 0 },
+{ 0x24e9, 0x0112, 0x24e9, 0 },
+{ 0x24ea, 0x0010, 0x0000, 496 },
+{ 0x3000, 0x0048, 0x0000, 0 },
+{ 0x3001, 0x0010, 0x0000, 3 },
+{ 0x3005, 0x0110, 0x0000, 0 },
+{ 0x3006, 0x0010, 0x0000, 0 },
+{ 0x3007, 0x0110, 0x0000, 0 },
+{ 0x3008, 0x0010, 0x0000, 48 },
+{ 0x3041, 0x0100, 0x0000, 83 },
+{ 0x3099, 0x0010, 0x0000, 1 },
+{ 0x309b, 0x0110, 0x0000, 3 },
+{ 0x30a1, 0x0100, 0x0000, 89 },
+{ 0x30fb, 0x0010, 0x0000, 0 },
+{ 0x30fc, 0x0110, 0x0000, 2 },
+{ 0x3105, 0x0100, 0x0000, 133 },
+{ 0x3190, 0x0010, 0x0000, 466 },
+{ 0x4e00, 0x0100, 0x0000, 32375 },
+{ 0xfb00, 0x0102, 0xfb00, 0 },
+{ 0xfb01, 0x0102, 0xfb01, 0 },
+{ 0xfb02, 0x0102, 0xfb02, 0 },
+{ 0xfb03, 0x0102, 0xfb03, 0 },
+{ 0xfb04, 0x0102, 0xfb04, 0 },
+{ 0xfb05, 0x0102, 0xfb05, 0 },
+{ 0xfb06, 0x0102, 0xfb06, 0 },
+{ 0xfb13, 0x0102, 0xfb13, 0 },
+{ 0xfb14, 0x0102, 0xfb14, 0 },
+{ 0xfb15, 0x0102, 0xfb15, 0 },
+{ 0xfb16, 0x0102, 0xfb16, 0 },
+{ 0xfb17, 0x0102, 0xfb17, 0 },
+{ 0xfb1e, 0x0010, 0x0000, 0 },
+{ 0xfb1f, 0x0100, 0x0000, 504 },
+{ 0xfd3e, 0x0010, 0x0000, 1 },
+{ 0xfd50, 0x0100, 0x0000, 129 },
+{ 0xfe20, 0x0010, 0x0000, 71 },
+{ 0xfe80, 0x0110, 0x0000, 124 },
+{ 0xfeff, 0x0048, 0x0000, 0 },
+{ 0xff01, 0x0010, 0x0000, 14 },
+{ 0xff10, 0x0084, 0x0000, 9 },
+{ 0xff1a, 0x0010, 0x0000, 6 },
+{ 0xff21, 0x0181, 0xff21, 0 },
+{ 0xff22, 0x0181, 0xff22, 0 },
+{ 0xff23, 0x0181, 0xff23, 0 },
+{ 0xff24, 0x0181, 0xff24, 0 },
+{ 0xff25, 0x0181, 0xff25, 0 },
+{ 0xff26, 0x0181, 0xff26, 0 },
+{ 0xff27, 0x0101, 0xff27, 0 },
+{ 0xff28, 0x0101, 0xff28, 0 },
+{ 0xff29, 0x0101, 0xff29, 0 },
+{ 0xff2a, 0x0101, 0xff2a, 0 },
+{ 0xff2b, 0x0101, 0xff2b, 0 },
+{ 0xff2c, 0x0101, 0xff2c, 0 },
+{ 0xff2d, 0x0101, 0xff2d, 0 },
+{ 0xff2e, 0x0101, 0xff2e, 0 },
+{ 0xff2f, 0x0101, 0xff2f, 0 },
+{ 0xff30, 0x0101, 0xff30, 0 },
+{ 0xff31, 0x0101, 0xff31, 0 },
+{ 0xff32, 0x0101, 0xff32, 0 },
+{ 0xff33, 0x0101, 0xff33, 0 },
+{ 0xff34, 0x0101, 0xff34, 0 },
+{ 0xff35, 0x0101, 0xff35, 0 },
+{ 0xff36, 0x0101, 0xff36, 0 },
+{ 0xff37, 0x0101, 0xff37, 0 },
+{ 0xff38, 0x0101, 0xff38, 0 },
+{ 0xff39, 0x0101, 0xff39, 0 },
+{ 0xff3a, 0x0101, 0xff3a, 0 },
+{ 0xff3b, 0x0010, 0x0000, 5 },
+{ 0xff41, 0x0182, 0xff41, 0 },
+{ 0xff42, 0x0182, 0xff42, 0 },
+{ 0xff43, 0x0182, 0xff43, 0 },
+{ 0xff44, 0x0182, 0xff44, 0 },
+{ 0xff45, 0x0182, 0xff45, 0 },
+{ 0xff46, 0x0182, 0xff46, 0 },
+{ 0xff47, 0x0102, 0xff47, 0 },
+{ 0xff48, 0x0102, 0xff48, 0 },
+{ 0xff49, 0x0102, 0xff49, 0 },
+{ 0xff4a, 0x0102, 0xff4a, 0 },
+{ 0xff4b, 0x0102, 0xff4b, 0 },
+{ 0xff4c, 0x0102, 0xff4c, 0 },
+{ 0xff4d, 0x0102, 0xff4d, 0 },
+{ 0xff4e, 0x0102, 0xff4e, 0 },
+{ 0xff4f, 0x0102, 0xff4f, 0 },
+{ 0xff50, 0x0102, 0xff50, 0 },
+{ 0xff51, 0x0102, 0xff51, 0 },
+{ 0xff52, 0x0102, 0xff52, 0 },
+{ 0xff53, 0x0102, 0xff53, 0 },
+{ 0xff54, 0x0102, 0xff54, 0 },
+{ 0xff55, 0x0102, 0xff55, 0 },
+{ 0xff56, 0x0102, 0xff56, 0 },
+{ 0xff57, 0x0102, 0xff57, 0 },
+{ 0xff58, 0x0102, 0xff58, 0 },
+{ 0xff59, 0x0102, 0xff59, 0 },
+{ 0xff5a, 0x0102, 0xff5a, 0 },
+{ 0xff5b, 0x0010, 0x0000, 8 },
+{ 0xff66, 0x0100, 0x0000, 9 },
+{ 0xff70, 0x0110, 0x0000, 0 },
+{ 0xff71, 0x0100, 0x0000, 44 },
+{ 0xff9e, 0x0110, 0x0000, 1 },
+{ 0xffa0, 0x0010, 0x0000, 0 },
+{ 0xffa1, 0x0100, 0x0000, 50 },
+{ 0xffe0, 0x0010, 0x0000, 13 },
+};
+
+CONST UINT UNICODE_DATA_SIZE = sizeof(UnicodeData)/sizeof(UnicodeDataRec);
+CONST UINT UNICODE_DATA_DIRECT_ACCESS = 256;
+
+#endif // !HAVE_COREFOUNDATION
diff --git a/src/pal/src/locale/utf8.cpp b/src/pal/src/locale/utf8.cpp
new file mode 100644
index 0000000000..87493a9673
--- /dev/null
+++ b/src/pal/src/locale/utf8.cpp
@@ -0,0 +1,2904 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ unicode/utf8.c
+
+Abstract:
+ Functions to encode and decode UTF-8 strings. This is a port of the C# version from mscorlib.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/utf8.h"
+
+#define FASTLOOP
+
+struct CharUnicodeInfo
+{
+ static const WCHAR HIGH_SURROGATE_START = 0xd800;
+ static const WCHAR HIGH_SURROGATE_END = 0xdbff;
+ static const WCHAR LOW_SURROGATE_START = 0xdc00;
+ static const WCHAR LOW_SURROGATE_END = 0xdfff;
+};
+
+struct Char
+{
+ // Test if the wide character is a high surrogate
+ static bool IsHighSurrogate(const WCHAR c)
+ {
+ return (c & 0xFC00) == CharUnicodeInfo::HIGH_SURROGATE_START;
+ }
+
+ // Test if the wide character is a low surrogate
+ static bool IsLowSurrogate(const WCHAR c)
+ {
+ return (c & 0xFC00) == CharUnicodeInfo::LOW_SURROGATE_START;
+ }
+
+ // Test if the wide character is a low surrogate
+ static bool IsSurrogate(const WCHAR c)
+ {
+ return (c & 0xF800) == CharUnicodeInfo::HIGH_SURROGATE_START;
+ }
+
+ // Test if the wide character is a high surrogate
+ static bool IsHighSurrogate(const WCHAR* s, int index)
+ {
+ return IsHighSurrogate(s[index]);
+ }
+
+ // Test if the wide character is a low surrogate
+ static bool IsLowSurrogate(const WCHAR* s, int index)
+ {
+ return IsLowSurrogate(s[index]);
+ }
+
+ // Test if the wide character is a low surrogate
+ static bool IsSurrogate(const WCHAR* s, int index)
+ {
+ return IsSurrogate(s[index]);
+ }
+};
+
+class ArgumentException
+{
+
+public:
+ ArgumentException(LPCSTR message)
+ {
+ }
+
+ ArgumentException(LPCSTR message, LPCSTR argName)
+ {
+ }
+};
+
+class ArgumentNullException : public ArgumentException
+{
+public:
+ ArgumentNullException(LPCSTR argName)
+ : ArgumentException("Argument is NULL", argName)
+ {
+
+ }
+};
+
+class ArgumentOutOfRangeException : public ArgumentException
+{
+public:
+ ArgumentOutOfRangeException(LPCSTR argName, LPCSTR message)
+ : ArgumentException(message, argName)
+ {
+
+ }
+};
+
+class InsufficientBufferException : public ArgumentException
+{
+public:
+ InsufficientBufferException(LPCSTR message, LPCSTR argName)
+ : ArgumentException(message, argName)
+ {
+
+ }
+};
+
+class Contract
+{
+public:
+ static void Assert(bool cond, LPCSTR str)
+ {
+ if (!cond)
+ {
+ throw ArgumentException(str);
+ }
+ }
+
+ static void EndContractBlock()
+ {
+ }
+};
+
+class DecoderFallbackException : public ArgumentException
+{
+ BYTE *bytesUnknown;
+ int index;
+
+public:
+ DecoderFallbackException(
+ LPCSTR message, BYTE bytesUnknown[], int index) : ArgumentException(message)
+ {
+ this->bytesUnknown = bytesUnknown;
+ this->index = index;
+ }
+
+ BYTE *BytesUnknown()
+ {
+ return (bytesUnknown);
+ }
+
+ int GetIndex()
+ {
+ return index;
+ }
+};
+
+class DecoderFallbackBuffer;
+
+class DecoderFallback
+{
+public:
+
+ // Fallback
+ //
+ // Return the appropriate unicode string alternative to the character that need to fall back.
+
+ virtual DecoderFallbackBuffer* CreateFallbackBuffer() = 0;
+
+ // Maximum number of characters that this instance of this fallback could return
+
+ virtual int GetMaxCharCount() = 0;
+};
+
+class DecoderReplacementFallback : public DecoderFallback
+{
+ // Our variables
+ WCHAR strDefault[2];
+ int strDefaultLength;
+
+public:
+ // Construction. Default replacement fallback uses no best fit and ? replacement string
+ DecoderReplacementFallback() : DecoderReplacementFallback(W("?"))
+ {
+ }
+
+ DecoderReplacementFallback(const WCHAR* replacement)
+ {
+ // Must not be null
+ if (replacement == nullptr)
+ throw ArgumentNullException("replacement");
+ Contract::EndContractBlock();
+
+ // Make sure it doesn't have bad surrogate pairs
+ bool bFoundHigh = false;
+ int replacementLength = PAL_wcslen((const WCHAR *)replacement);
+ for (int i = 0; i < replacementLength; i++)
+ {
+ // Found a surrogate?
+ if (Char::IsSurrogate(replacement, i))
+ {
+ // High or Low?
+ if (Char::IsHighSurrogate(replacement, i))
+ {
+ // if already had a high one, stop
+ if (bFoundHigh)
+ break; // break & throw at the bFoundHIgh below
+ bFoundHigh = true;
+ }
+ else
+ {
+ // Low, did we have a high?
+ if (!bFoundHigh)
+ {
+ // Didn't have one, make if fail when we stop
+ bFoundHigh = true;
+ break;
+ }
+
+ // Clear flag
+ bFoundHigh = false;
+ }
+ }
+ // If last was high we're in trouble (not surrogate so not low surrogate, so break)
+ else if (bFoundHigh)
+ break;
+ }
+ if (bFoundHigh)
+ throw ArgumentException("String 'replacement' contains invalid Unicode code points.", "replacement");
+
+ wcscpy_s(strDefault, sizeof(strDefault), replacement);
+ strDefaultLength = replacementLength;
+ }
+
+ WCHAR* GetDefaultString()
+ {
+ return strDefault;
+ }
+
+ virtual DecoderFallbackBuffer* CreateFallbackBuffer();
+
+ // Maximum number of characters that this instance of this fallback could return
+ virtual int GetMaxCharCount()
+ {
+ return strDefaultLength;
+ }
+};
+
+class DecoderFallbackBuffer
+{
+ friend class UTF8Encoding;
+ // Most implimentations will probably need an implimenation-specific constructor
+
+ // internal methods that cannot be overriden that let us do our fallback thing
+ // These wrap the internal methods so that we can check for people doing stuff that's incorrect
+
+public:
+ virtual bool Fallback(BYTE bytesUnknown[], int index, int size) = 0;
+
+ // Get next character
+ virtual WCHAR GetNextChar() = 0;
+
+ //Back up a character
+ virtual bool MovePrevious() = 0;
+
+ // How many chars left in this fallback?
+ virtual int GetRemaining() = 0;
+
+ // Clear the buffer
+ virtual void Reset()
+ {
+ while (GetNextChar() != (WCHAR)0);
+ }
+
+ // Internal items to help us figure out what we're doing as far as error messages, etc.
+ // These help us with our performance and messages internally
+protected:
+ BYTE* byteStart;
+ WCHAR* charEnd;
+
+ // Internal reset
+ void InternalReset()
+ {
+ byteStart = nullptr;
+ Reset();
+ }
+
+ // Set the above values
+ // This can't be part of the constructor because EncoderFallbacks would have to know how to impliment these.
+ void InternalInitialize(BYTE* byteStart, WCHAR* charEnd)
+ {
+ this->byteStart = byteStart;
+ this->charEnd = charEnd;
+ }
+
+ // Fallback the current byte by sticking it into the remaining char buffer.
+ // This can only be called by our encodings (other have to use the public fallback methods), so
+ // we can use our DecoderNLS here too (except we don't).
+ // Returns true if we are successful, false if we can't fallback the character (no buffer space)
+ // So caller needs to throw buffer space if return false.
+ // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
+ // array, and we might need the index, hence the byte*
+ // Don't touch ref chars unless we succeed
+ virtual bool InternalFallback(BYTE bytes[], BYTE* pBytes, WCHAR** chars, int size)
+ {
+
+ Contract::Assert(byteStart != nullptr, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
+
+ // See if there's a fallback character and we have an output buffer then copy our string.
+ if (this->Fallback(bytes, (int)(pBytes - byteStart - size), size))
+ {
+ // Copy the chars to our output
+ WCHAR ch;
+ WCHAR* charTemp = *chars;
+ bool bHighSurrogate = false;
+ while ((ch = GetNextChar()) != 0)
+ {
+ // Make sure no mixed up surrogates
+ if (Char::IsSurrogate(ch))
+ {
+ if (Char::IsHighSurrogate(ch))
+ {
+ // High Surrogate
+ if (bHighSurrogate)
+ throw ArgumentException("String 'chars' contains invalid Unicode code points.");
+ bHighSurrogate = true;
+ }
+ else
+ {
+ // Low surrogate
+ if (bHighSurrogate == false)
+ throw ArgumentException("String 'chars' contains invalid Unicode code points.");
+ bHighSurrogate = false;
+ }
+ }
+
+ if (charTemp >= charEnd)
+ {
+ // No buffer space
+ return false;
+ }
+
+ *(charTemp++) = ch;
+ }
+
+ // Need to make sure that bHighSurrogate isn't true
+ if (bHighSurrogate)
+ throw ArgumentException("String 'chars' contains invalid Unicode code points.");
+
+ // Now we aren't going to be false, so its OK to update chars
+ chars = &charTemp;
+ }
+
+ return true;
+ }
+
+ // This version just counts the fallback and doesn't actually copy anything.
+ virtual int InternalFallback(BYTE bytes[], BYTE* pBytes, int size)
+ // Right now this has both bytes[] and BYTE* bytes, since we might have extra bytes, hence the
+ // array, and we might need the index, hence the byte*
+ {
+
+ Contract::Assert(byteStart != nullptr, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
+
+ // See if there's a fallback character and we have an output buffer then copy our string.
+ if (this->Fallback(bytes, (int)(pBytes - byteStart - size), size))
+ {
+ int count = 0;
+
+ WCHAR ch;
+ bool bHighSurrogate = false;
+ while ((ch = GetNextChar()) != 0)
+ {
+ // Make sure no mixed up surrogates
+ if (Char::IsSurrogate(ch))
+ {
+ if (Char::IsHighSurrogate(ch))
+ {
+ // High Surrogate
+ if (bHighSurrogate)
+ throw ArgumentException("String 'chars' contains invalid Unicode code points.");
+ bHighSurrogate = true;
+ }
+ else
+ {
+ // Low surrogate
+ if (bHighSurrogate == false)
+ throw ArgumentException("String 'chars' contains invalid Unicode code points.");
+ bHighSurrogate = false;
+ }
+ }
+
+ count++;
+ }
+
+ // Need to make sure that bHighSurrogate isn't true
+ if (bHighSurrogate)
+ throw ArgumentException("String 'chars' contains invalid Unicode code points.");
+
+ return count;
+ }
+
+ // If no fallback return 0
+ return 0;
+ }
+
+ // private helper methods
+ void ThrowLastBytesRecursive(BYTE bytesUnknown[])
+ {
+ throw ArgumentException("Recursive fallback not allowed");
+ }
+};
+
+class DecoderReplacementFallbackBuffer : public DecoderFallbackBuffer
+{
+ // Store our default string
+ WCHAR strDefault[4];
+ int strDefaultLength;
+ int fallbackCount = -1;
+ int fallbackIndex = -1;
+
+public:
+ // Construction
+ DecoderReplacementFallbackBuffer(DecoderReplacementFallback* fallback)
+ {
+ // 2X in case we're a surrogate pair
+ wcscpy_s(strDefault, sizeof(strDefault), fallback->GetDefaultString());
+ wcscat_s(strDefault, sizeof(strDefault), fallback->GetDefaultString());
+ strDefaultLength = 2 * PAL_wcslen((const WCHAR *)fallback->GetDefaultString());
+
+ }
+
+ // Fallback Methods
+ virtual bool Fallback(BYTE bytesUnknown[], int index, int size)
+ {
+ // We expect no previous fallback in our buffer
+ // We can't call recursively but others might (note, we don't test on last char!!!)
+ if (fallbackCount >= 1)
+ {
+ ThrowLastBytesRecursive(bytesUnknown);
+ }
+
+ // Go ahead and get our fallback
+ if (strDefaultLength == 0)
+ return false;
+
+ fallbackCount = strDefaultLength;
+ fallbackIndex = -1;
+
+ return true;
+ }
+
+ virtual WCHAR GetNextChar()
+ {
+ // We want it to get < 0 because == 0 means that the current/last character is a fallback
+ // and we need to detect recursion. We could have a flag but we already have this counter.
+ fallbackCount--;
+ fallbackIndex++;
+
+ // Do we have anything left? 0 is now last fallback char, negative is nothing left
+ if (fallbackCount < 0)
+ return '\0';
+
+ // Need to get it out of the buffer.
+ // Make sure it didn't wrap from the fast count-- path
+ if (fallbackCount == INT_MAX)
+ {
+ fallbackCount = -1;
+ return '\0';
+ }
+
+ // Now make sure its in the expected range
+ Contract::Assert(fallbackIndex < strDefaultLength && fallbackIndex >= 0,
+ "Index exceeds buffer range");
+
+ return strDefault[fallbackIndex];
+ }
+
+ virtual bool MovePrevious()
+ {
+ // Back up one, only if we just processed the last character (or earlier)
+ if (fallbackCount >= -1 && fallbackIndex >= 0)
+ {
+ fallbackIndex--;
+ fallbackCount++;
+ return true;
+ }
+
+ // Return false 'cause we couldn't do it.
+ return false;
+ }
+
+ // How many characters left to output?
+ virtual int GetRemaining()
+ {
+ // Our count is 0 for 1 character left.
+ return (fallbackCount < 0) ? 0 : fallbackCount;
+ }
+
+ // Clear the buffer
+ virtual void Reset()
+ {
+ fallbackCount = -1;
+ fallbackIndex = -1;
+ byteStart = nullptr;
+ }
+
+ // This version just counts the fallback and doesn't actually copy anything.
+ virtual int InternalFallback(BYTE bytes[], BYTE* pBytes, int size)
+ // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
+ // array, and we might need the index, hence the byte*
+ {
+ // return our replacement string Length
+ return strDefaultLength;
+ }
+};
+
+class DecoderExceptionFallbackBuffer : public DecoderFallbackBuffer
+{
+public:
+ DecoderExceptionFallbackBuffer()
+ {
+ }
+
+ virtual bool Fallback(BYTE bytesUnknown[], int index, int size)
+ {
+ throw DecoderFallbackException(
+ "Unable to translate UTF-8 character to Unicode", bytesUnknown, index);
+ }
+
+ virtual WCHAR GetNextChar()
+ {
+ return 0;
+ }
+
+ virtual bool MovePrevious()
+ {
+ // Exception fallback doesn't have anywhere to back up to.
+ return false;
+ }
+
+ // Exceptions are always empty
+ virtual int GetRemaining()
+ {
+ return 0;
+ }
+
+};
+
+class DecoderExceptionFallback : public DecoderFallback
+{
+ // Construction
+public:
+ DecoderExceptionFallback()
+ {
+ }
+
+ virtual DecoderFallbackBuffer* CreateFallbackBuffer()
+ {
+ return new DecoderExceptionFallbackBuffer();
+ }
+
+ // Maximum number of characters that this instance of this fallback could return
+ virtual int GetMaxCharCount()
+ {
+ return 0;
+ }
+};
+
+DecoderFallbackBuffer* DecoderReplacementFallback::CreateFallbackBuffer()
+{
+ return new DecoderReplacementFallbackBuffer(this);
+}
+
+class EncoderFallbackException : public ArgumentException
+{
+ WCHAR charUnknown;
+ WCHAR charUnknownHigh;
+ WCHAR charUnknownLow;
+ int index;
+
+public:
+ EncoderFallbackException(
+ LPCSTR message, WCHAR charUnknown, int index) : ArgumentException(message)
+ {
+ this->charUnknown = charUnknown;
+ this->index = index;
+ }
+
+ EncoderFallbackException(
+ LPCSTR message, WCHAR charUnknownHigh, WCHAR charUnknownLow, int index) : ArgumentException(message)
+ {
+ if (!Char::IsHighSurrogate(charUnknownHigh))
+ {
+ throw ArgumentOutOfRangeException("charUnknownHigh",
+ "Argument out of range 0xD800..0xDBFF");
+ }
+ if (!Char::IsLowSurrogate(charUnknownLow))
+ {
+ throw ArgumentOutOfRangeException("charUnknownLow",
+ "Argument out of range 0xDC00..0xDFFF");
+ }
+ Contract::EndContractBlock();
+
+ this->charUnknownHigh = charUnknownHigh;
+ this->charUnknownLow = charUnknownLow;
+ this->index = index;
+ }
+
+ WCHAR GetCharUnknown()
+ {
+ return (charUnknown);
+ }
+
+ WCHAR GetCharUnknownHigh()
+ {
+ return (charUnknownHigh);
+ }
+
+ WCHAR GetCharUnknownLow()
+ {
+ return (charUnknownLow);
+ }
+
+ int GetIndex()
+ {
+ return index;
+ }
+
+ // Return true if the unknown character is a surrogate pair.
+ bool IsUnknownSurrogate()
+ {
+ return (charUnknownHigh != '\0');
+ }
+};
+
+class EncoderFallbackBuffer;
+
+class EncoderFallback
+{
+public:
+
+ // Fallback
+ //
+ // Return the appropriate unicode string alternative to the character that need to fall back.
+
+ virtual EncoderFallbackBuffer* CreateFallbackBuffer() = 0;
+
+ // Maximum number of characters that this instance of this fallback could return
+ virtual int GetMaxCharCount() = 0;
+};
+
+class EncoderReplacementFallback : public EncoderFallback
+{
+ // Our variables
+ WCHAR strDefault[2];
+ int strDefaultLength;
+
+public:
+ // Construction. Default replacement fallback uses no best fit and ? replacement string
+ EncoderReplacementFallback() : EncoderReplacementFallback(W("?"))
+ {
+ }
+
+ EncoderReplacementFallback(const WCHAR* replacement)
+ {
+ // Must not be null
+ if (replacement == nullptr)
+ throw ArgumentNullException("replacement");
+ Contract::EndContractBlock();
+
+ // Make sure it doesn't have bad surrogate pairs
+ bool bFoundHigh = false;
+ int replacementLength = PAL_wcslen((const WCHAR *)replacement);
+ for (int i = 0; i < replacementLength; i++)
+ {
+ // Found a surrogate?
+ if (Char::IsSurrogate(replacement, i))
+ {
+ // High or Low?
+ if (Char::IsHighSurrogate(replacement, i))
+ {
+ // if already had a high one, stop
+ if (bFoundHigh)
+ break; // break & throw at the bFoundHIgh below
+ bFoundHigh = true;
+ }
+ else
+ {
+ // Low, did we have a high?
+ if (!bFoundHigh)
+ {
+ // Didn't have one, make if fail when we stop
+ bFoundHigh = true;
+ break;
+ }
+
+ // Clear flag
+ bFoundHigh = false;
+ }
+ }
+ // If last was high we're in trouble (not surrogate so not low surrogate, so break)
+ else if (bFoundHigh)
+ break;
+ }
+ if (bFoundHigh)
+ throw ArgumentException("String 'replacement' contains invalid Unicode code points.", "replacement");
+
+ wcscpy_s(strDefault, sizeof(strDefault), replacement);
+ strDefaultLength = replacementLength;
+ }
+
+ WCHAR* GetDefaultString()
+ {
+ return strDefault;
+ }
+
+ virtual EncoderFallbackBuffer* CreateFallbackBuffer();
+
+ // Maximum number of characters that this instance of this fallback could return
+ virtual int GetMaxCharCount()
+ {
+ return strDefaultLength;
+ }
+};
+
+class EncoderFallbackBuffer
+{
+ friend class UTF8Encoding;
+ // Most implementations will probably need an implemenation-specific constructor
+
+ // Public methods that cannot be overriden that let us do our fallback thing
+ // These wrap the internal methods so that we can check for people doing stuff that is incorrect
+
+public:
+ virtual bool Fallback(WCHAR charUnknown, int index) = 0;
+
+ virtual bool Fallback(WCHAR charUnknownHigh, WCHAR charUnknownLow, int index) = 0;
+
+ // Get next character
+ virtual WCHAR GetNextChar() = 0;
+
+ // Back up a character
+ virtual bool MovePrevious() = 0;
+
+ // How many chars left in this fallback?
+ virtual int GetRemaining() = 0;
+
+ // Not sure if this should be public or not.
+ // Clear the buffer
+ virtual void Reset()
+ {
+ while (GetNextChar() != (WCHAR)0);
+ }
+
+ // Internal items to help us figure out what we're doing as far as error messages, etc.
+ // These help us with our performance and messages internally
+protected:
+ WCHAR* charStart;
+ WCHAR* charEnd;
+ bool setEncoder;
+ bool bUsedEncoder;
+ bool bFallingBack = false;
+ int iRecursionCount = 0;
+ static const int iMaxRecursion = 250;
+
+ // Internal Reset
+ // For example, what if someone fails a conversion and wants to reset one of our fallback buffers?
+ void InternalReset()
+ {
+ charStart = nullptr;
+ bFallingBack = false;
+ iRecursionCount = 0;
+ Reset();
+ }
+
+ // Set the above values
+ // This can't be part of the constructor because EncoderFallbacks would have to know how to impliment these.
+ void InternalInitialize(WCHAR* charStart, WCHAR* charEnd, bool setEncoder)
+ {
+ this->charStart = charStart;
+ this->charEnd = charEnd;
+ this->setEncoder = setEncoder;
+ this->bUsedEncoder = false;
+ this->bFallingBack = false;
+ this->iRecursionCount = 0;
+ }
+
+ WCHAR InternalGetNextChar()
+ {
+ WCHAR ch = GetNextChar();
+ bFallingBack = (ch != 0);
+ if (ch == 0) iRecursionCount = 0;
+ return ch;
+ }
+
+ // Fallback the current character using the remaining buffer and encoder if necessary
+ // This can only be called by our encodings (other have to use the public fallback methods), so
+ // we can use our EncoderNLS here too.
+ // setEncoder is true if we're calling from a GetBytes method, false if we're calling from a GetByteCount
+ //
+ // Note that this could also change the contents of this->encoder, which is the same
+ // object that the caller is using, so the caller could mess up the encoder for us
+ // if they aren't careful.
+ virtual bool InternalFallback(WCHAR ch, WCHAR** chars)
+ {
+ // Shouldn't have null charStart
+ Contract::Assert(charStart != nullptr,
+ "[EncoderFallback.InternalFallbackBuffer]Fallback buffer is not initialized");
+
+ // Get our index, remember chars was preincremented to point at next char, so have to -1
+ int index = (int)(*chars - charStart) - 1;
+
+ // See if it was a high surrogate
+ if (Char::IsHighSurrogate(ch))
+ {
+ // See if there's a low surrogate to go with it
+ if (*chars >= this->charEnd)
+ {
+ // Nothing left in input buffer
+ // No input, return 0
+ }
+ else
+ {
+ // Might have a low surrogate
+ WCHAR cNext = **chars;
+ if (Char::IsLowSurrogate(cNext))
+ {
+ // If already falling back then fail
+ if (bFallingBack && iRecursionCount++ > iMaxRecursion)
+ ThrowLastCharRecursive(ch, cNext);
+
+ // Next is a surrogate, add it as surrogate pair, and increment chars
+ (*chars)++;
+ bFallingBack = Fallback(ch, cNext, index);
+ return bFallingBack;
+ }
+
+ // Next isn't a low surrogate, just fallback the high surrogate
+ }
+ }
+
+ // If already falling back then fail
+ if (bFallingBack && iRecursionCount++ > iMaxRecursion)
+ ThrowLastCharRecursive((int)ch);
+
+ // Fall back our char
+ bFallingBack = Fallback(ch, index);
+
+ return bFallingBack;
+ }
+
+ // private helper methods
+ void ThrowLastCharRecursive(WCHAR highSurrogate, WCHAR lowSurrogate)
+ {
+ // Throw it, using our complete character
+ throw ArgumentException("Recursive fallback not allowed", "chars");
+ }
+
+ void ThrowLastCharRecursive(int utf32Char)
+ {
+ throw ArgumentException("Recursive fallback not allowed", "chars");
+ }
+
+};
+
+class EncoderReplacementFallbackBuffer : public EncoderFallbackBuffer
+{
+ // Store our default string
+ WCHAR strDefault[4];
+ int strDefaultLength;
+ int fallbackCount = -1;
+ int fallbackIndex = -1;
+public:
+ // Construction
+ EncoderReplacementFallbackBuffer(EncoderReplacementFallback* fallback)
+ {
+ // 2X in case we're a surrogate pair
+ wcscpy_s(strDefault, sizeof(strDefault), fallback->GetDefaultString());
+ wcscat_s(strDefault, sizeof(strDefault), fallback->GetDefaultString());
+ strDefaultLength = 2 * PAL_wcslen((const WCHAR *)fallback->GetDefaultString());
+
+ }
+
+ // Fallback Methods
+ virtual bool Fallback(WCHAR charUnknown, int index)
+ {
+ // If we had a buffer already we're being recursive, throw, it's probably at the suspect
+ // character in our array.
+ if (fallbackCount >= 1)
+ {
+ // If we're recursive we may still have something in our buffer that makes this a surrogate
+ if (Char::IsHighSurrogate(charUnknown) && fallbackCount >= 0 &&
+ Char::IsLowSurrogate(strDefault[fallbackIndex + 1]))
+ ThrowLastCharRecursive(charUnknown, strDefault[fallbackIndex + 1]);
+
+ // Nope, just one character
+ ThrowLastCharRecursive((int)charUnknown);
+ }
+
+ // Go ahead and get our fallback
+ // Divide by 2 because we aren't a surrogate pair
+ fallbackCount = strDefaultLength / 2;
+ fallbackIndex = -1;
+
+ return fallbackCount != 0;
+ }
+
+ virtual bool Fallback(WCHAR charUnknownHigh, WCHAR charUnknownLow, int index)
+ {
+ // Double check input surrogate pair
+ if (!Char::IsHighSurrogate(charUnknownHigh))
+ throw ArgumentOutOfRangeException("charUnknownHigh",
+ "Argument out of range 0xD800..0xDBFF");
+
+ if (!Char::IsLowSurrogate(charUnknownLow))
+ throw ArgumentOutOfRangeException("charUnknownLow",
+ "Argument out of range 0xDC00..0xDFFF");
+ Contract::EndContractBlock();
+
+ // If we had a buffer already we're being recursive, throw, it's probably at the suspect
+ // character in our array.
+ if (fallbackCount >= 1)
+ ThrowLastCharRecursive(charUnknownHigh, charUnknownLow);
+
+ // Go ahead and get our fallback
+ fallbackCount = strDefaultLength;
+ fallbackIndex = -1;
+
+ return fallbackCount != 0;
+ }
+
+ virtual WCHAR GetNextChar()
+ {
+ // We want it to get < 0 because == 0 means that the current/last character is a fallback
+ // and we need to detect recursion. We could have a flag but we already have this counter.
+ fallbackCount--;
+ fallbackIndex++;
+
+ // Do we have anything left? 0 is now last fallback char, negative is nothing left
+ if (fallbackCount < 0)
+ return '\0';
+
+ // Need to get it out of the buffer.
+ // Make sure it didn't wrap from the fast count-- path
+ if (fallbackCount == INT_MAX)
+ {
+ fallbackCount = -1;
+ return '\0';
+ }
+
+ // Now make sure its in the expected range
+ Contract::Assert(fallbackIndex < strDefaultLength && fallbackIndex >= 0,
+ "Index exceeds buffer range");
+
+ return strDefault[fallbackIndex];
+ }
+
+ virtual bool MovePrevious()
+ {
+ // Back up one, only if we just processed the last character (or earlier)
+ if (fallbackCount >= -1 && fallbackIndex >= 0)
+ {
+ fallbackIndex--;
+ fallbackCount++;
+ return true;
+ }
+
+ // Return false 'cause we couldn't do it.
+ return false;
+ }
+
+ // How many characters left to output?
+ virtual int GetRemaining()
+ {
+ // Our count is 0 for 1 character left.
+ return (fallbackCount < 0) ? 0 : fallbackCount;
+ }
+
+ // Clear the buffer
+ virtual void Reset()
+ {
+ fallbackCount = -1;
+ fallbackIndex = 0;
+ charStart = nullptr;
+ bFallingBack = false;
+ }
+};
+
+class EncoderExceptionFallbackBuffer : public EncoderFallbackBuffer
+{
+public:
+ EncoderExceptionFallbackBuffer()
+ {
+ }
+
+ virtual bool Fallback(WCHAR charUnknown, int index)
+ {
+ // Fall back our char
+ throw EncoderFallbackException("Unable to translate Unicode character to UTF-8", charUnknown, index);
+ }
+
+ virtual bool Fallback(WCHAR charUnknownHigh, WCHAR charUnknownLow, int index)
+ {
+ if (!Char::IsHighSurrogate(charUnknownHigh))
+ {
+ throw ArgumentOutOfRangeException("charUnknownHigh",
+ "Argument out of range 0xD800..0xDBFF");
+ }
+ if (!Char::IsLowSurrogate(charUnknownLow))
+ {
+ throw ArgumentOutOfRangeException("charUnknownLow",
+ "Argument out of range 0xDC00..0xDFFF");
+ }
+ Contract::EndContractBlock();
+
+ //int iTemp = Char::ConvertToUtf32(charUnknownHigh, charUnknownLow);
+
+ // Fall back our char
+ throw EncoderFallbackException(
+ "Unable to translate Unicode character to UTF-8", charUnknownHigh, charUnknownLow, index);
+ }
+
+ virtual WCHAR GetNextChar()
+ {
+ return 0;
+ }
+
+ virtual bool MovePrevious()
+ {
+ // Exception fallback doesn't have anywhere to back up to.
+ return false;
+ }
+
+ // Exceptions are always empty
+ virtual int GetRemaining()
+ {
+ return 0;
+ }
+};
+
+class EncoderExceptionFallback : public EncoderFallback
+{
+ // Construction
+public:
+ EncoderExceptionFallback()
+ {
+ }
+
+ virtual EncoderFallbackBuffer* CreateFallbackBuffer()
+ {
+ return new EncoderExceptionFallbackBuffer();
+ }
+
+ // Maximum number of characters that this instance of this fallback could return
+ virtual int GetMaxCharCount()
+ {
+ return 0;
+ }
+};
+
+EncoderFallbackBuffer* EncoderReplacementFallback::CreateFallbackBuffer()
+{
+ return new EncoderReplacementFallbackBuffer(this);
+}
+
+class UTF8Encoding
+{
+ EncoderFallback* encoderFallback;
+ // Instances of the two possible fallbacks. The constructor parameter
+ // determines which one to use.
+ EncoderReplacementFallback encoderReplacementFallback;
+ EncoderExceptionFallback encoderExceptionFallback;
+
+ DecoderFallback* decoderFallback;
+ // Instances of the two possible fallbacks. The constructor parameter
+ // determines which one to use.
+ DecoderReplacementFallback decoderReplacementFallback;
+ DecoderExceptionFallback decoderExceptionFallback;
+
+ bool InRange(WCHAR c, WCHAR begin, WCHAR end)
+ {
+ return begin <= c && c <= end;
+ }
+
+ size_t PtrDiff(void* ptr1, void* ptr2)
+ {
+ return (BYTE*)ptr2 - (BYTE*)ptr1;
+ }
+
+ void ThrowBytesOverflow()
+ {
+ // Special message to include fallback type in case fallback's GetMaxCharCount is broken
+ // This happens if user has implimented an encoder fallback with a broken GetMaxCharCount
+ throw InsufficientBufferException("The output byte buffer is too small to contain the encoded data", "bytes");
+ }
+
+ void ThrowBytesOverflow(bool nothingEncoded)
+ {
+ // Special message to include fallback type in case fallback's GetMaxCharCount is broken
+ // This happens if user has implimented an encoder fallback with a broken GetMaxCharCount
+ if (nothingEncoded){
+ ThrowBytesOverflow();
+ }
+ }
+
+ void ThrowCharsOverflow()
+ {
+ // Special message to include fallback type in case fallback's GetMaxCharCount is broken
+ // This happens if user has implimented a decoder fallback with a broken GetMaxCharCount
+ throw InsufficientBufferException("The output char buffer is too small to contain the encoded data", "chars");
+ }
+
+ void ThrowCharsOverflow(bool nothingEncoded)
+ {
+ // Special message to include fallback type in case fallback's GetMaxCharCount is broken
+ // This happens if user has implimented an decoder fallback with a broken GetMaxCharCount
+ if (nothingEncoded){
+ ThrowCharsOverflow();
+ }
+ }
+
+ int FallbackInvalidByteSequence(BYTE* pSrc, int ch, DecoderFallbackBuffer *fallback)
+ {
+ // Get our byte[]
+ BYTE *bytesUnknown;
+ int size = GetBytesUnknown(pSrc, ch, &bytesUnknown);
+
+ // Do the actual fallback
+ int count = fallback->InternalFallback(bytesUnknown, pSrc, size);
+
+ // # of fallback chars expected.
+ // Note that we only get here for "long" sequences, and have already unreserved
+ // the count that we prereserved for the input bytes
+ return count;
+ }
+
+ int GetBytesUnknown(BYTE* pSrc, int ch, BYTE **bytesUnknown)
+ {
+ int size;
+ BYTE bytes[3];
+
+ // See if it was a plain char
+ // (have to check >= 0 because we have all sorts of wierd bit flags)
+ if (ch < 0x100 && ch >= 0)
+ {
+ pSrc--;
+ bytes[0] = (BYTE)ch;
+ size = 1;
+ }
+ // See if its an unfinished 2 byte sequence
+ else if ((ch & (SupplimentarySeq | ThreeByteSeq)) == 0)
+ {
+ pSrc--;
+ bytes[0] = (BYTE)((ch & 0x1F) | 0xc0);
+ size = 1;
+ }
+ // So now we're either 2nd byte of 3 or 4 byte sequence or
+ // we hit a non-trail byte or we ran out of space for 3rd byte of 4 byte sequence
+ // 1st check if its a 4 byte sequence
+ else if ((ch & SupplimentarySeq) != 0)
+ {
+ // 3rd byte of 4 byte sequence?
+ if ((ch & (FinalByte >> 6)) != 0)
+ {
+ // 3rd byte of 4 byte sequence
+ pSrc -= 3;
+ bytes[0] = (BYTE)(((ch >> 12) & 0x07) | 0xF0);
+ bytes[1] = (BYTE)(((ch >> 6) & 0x3F) | 0x80);
+ bytes[2] = (BYTE)(((ch)& 0x3F) | 0x80);
+ size = 3;
+ }
+ else if ((ch & (FinalByte >> 12)) != 0)
+ {
+ // 2nd byte of a 4 byte sequence
+ pSrc -= 2;
+ bytes[0] = (BYTE)(((ch >> 6) & 0x07) | 0xF0);
+ bytes[1] = (BYTE)(((ch)& 0x3F) | 0x80);
+ size = 2;
+ }
+ else
+ {
+ // 4th byte of a 4 byte sequence
+ pSrc--;
+ bytes[0] = (BYTE)(((ch)& 0x07) | 0xF0);
+ size = 1;
+ }
+ }
+ else
+ {
+ // 2nd byte of 3 byte sequence?
+ if ((ch & (FinalByte >> 6)) != 0)
+ {
+ // So its 2nd byte of a 3 byte sequence
+ pSrc -= 2;
+ bytes[0] = (BYTE)(((ch >> 6) & 0x0F) | 0xE0);
+ bytes[1] = (BYTE)(((ch)& 0x3F) | 0x80);
+ size = 2;
+ }
+ else
+ {
+ // 1st byte of a 3 byte sequence
+ pSrc--;
+ bytes[0] = (BYTE)(((ch)& 0x0F) | 0xE0);
+ size = 1;
+ }
+ }
+
+ *bytesUnknown = bytes;
+ return size;
+ }
+
+public:
+
+ UTF8Encoding(bool isThrowException)
+ : encoderReplacementFallback(W("\xFFFD"))
+ {
+ if (isThrowException)
+ {
+ encoderFallback = &encoderExceptionFallback;
+ decoderFallback = &decoderExceptionFallback;
+ }
+ else
+ {
+ encoderFallback = &encoderReplacementFallback;
+ decoderFallback = &decoderReplacementFallback;
+ }
+ }
+
+ // These are bitmasks used to maintain the state in the decoder. They occupy the higher bits
+ // while the actual character is being built in the lower bits. They are shifted together
+ // with the actual bits of the character.
+
+ // bits 30 & 31 are used for pending bits fixup
+ const int FinalByte = 1 << 29;
+ const int SupplimentarySeq = 1 << 28;
+ const int ThreeByteSeq = 1 << 27;
+
+ int GetCharCount(BYTE* bytes, int count)
+ {
+ Contract::Assert(bytes != nullptr, "[UTF8Encoding.GetCharCount]bytes!=nullptr");
+ Contract::Assert(count >= 0, "[UTF8Encoding.GetCharCount]count >=0");
+
+ // Initialize stuff
+ BYTE *pSrc = bytes;
+ BYTE *pEnd = pSrc + count;
+
+ // Start by assuming we have as many as count, charCount always includes the adjustment
+ // for the character being decoded
+ int charCount = count;
+ int ch = 0;
+ DecoderFallbackBuffer *fallback = nullptr;
+
+ for (;;)
+ {
+ // SLOWLOOP: does all range checks, handles all special cases, but it is slow
+ if (pSrc >= pEnd) {
+ break;
+ }
+
+ // read next byte. The JIT optimization seems to be getting confused when
+ // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
+ int cha = *pSrc;
+
+ if (ch == 0) {
+ // no pending bits
+ goto ReadChar;
+ }
+
+ pSrc++;
+
+ // we are expecting to see trailing bytes like 10vvvvvv
+ if ((cha & 0xC0) != 0x80) {
+ // This can be a valid starting byte for another UTF8 byte sequence, so let's put
+ // the current byte back, and try to see if this is a valid byte for another UTF8 byte sequence
+ pSrc--;
+ charCount += (ch >> 30);
+ goto InvalidByteSequence;
+ }
+
+ // fold in the new byte
+ ch = (ch << 6) | (cha & 0x3F);
+
+ if ((ch & FinalByte) == 0) {
+ Contract::Assert((ch & (SupplimentarySeq | ThreeByteSeq)) != 0,
+ "[UTF8Encoding.GetChars]Invariant volation");
+
+ if ((ch & SupplimentarySeq) != 0) {
+ if ((ch & (FinalByte >> 6)) != 0) {
+ // this is 3rd byte (of 4 byte supplimentary) - nothing to do
+ continue;
+ }
+
+ // 2nd byte, check for non-shortest form of supplimentary char and the valid
+ // supplimentary characters in range 0x010000 - 0x10FFFF at the same time
+ if (!InRange(ch & 0x1F0, 0x10, 0x100)) {
+ goto InvalidByteSequence;
+ }
+ }
+ else {
+ // Must be 2nd byte of a 3-byte sequence
+ // check for non-shortest form of 3 byte seq
+ if ((ch & (0x1F << 5)) == 0 || // non-shortest form
+ (ch & (0xF800 >> 6)) == (0xD800 >> 6)) // illegal individually encoded surrogate
+ {
+ goto InvalidByteSequence;
+ }
+ }
+ continue;
+ }
+
+ // ready to punch
+
+ // adjust for surrogates in non-shortest form
+ if ((ch & (SupplimentarySeq | 0x1F0000)) == SupplimentarySeq) {
+ charCount--;
+ }
+ goto EncodeChar;
+
+ InvalidByteSequence:
+ // this code fragment should be close to the gotos referencing it
+ // Have to do fallback for invalid bytes
+ if (fallback == nullptr)
+ {
+ fallback = decoderFallback->CreateFallbackBuffer();
+ fallback->InternalInitialize(bytes, nullptr);
+ }
+ charCount += FallbackInvalidByteSequence(pSrc, ch, fallback);
+
+ ch = 0;
+ continue;
+
+ ReadChar:
+ ch = *pSrc;
+ pSrc++;
+
+ ProcessChar:
+ if (ch > 0x7F) {
+ // If its > 0x7F, its start of a new multi-byte sequence
+
+ // Long sequence, so unreserve our char.
+ charCount--;
+
+ // bit 6 has to be non-zero for start of multibyte chars.
+ if ((ch & 0x40) == 0) {
+ // Unexpected trail byte
+ goto InvalidByteSequence;
+ }
+
+ // start a new long code
+ if ((ch & 0x20) != 0) {
+ if ((ch & 0x10) != 0) {
+ // 4 byte encoding - supplimentary character (2 surrogates)
+
+ ch &= 0x0F;
+
+ // check that bit 4 is zero and the valid supplimentary character
+ // range 0x000000 - 0x10FFFF at the same time
+ if (ch > 0x04) {
+ ch |= 0xf0;
+ goto InvalidByteSequence;
+ }
+
+ // Add bit flags so that when we check new characters & rotate we'll be flagged correctly.
+ // Final byte flag, count fix if we don't make final byte & supplimentary sequence flag.
+ ch |= (FinalByte >> 3 * 6) | // Final byte is 3 more bytes from now
+ (1 << 30) | // If it dies on next byte we'll need an extra char
+ (3 << (30 - 2 * 6)) | // If it dies on last byte we'll need to subtract a char
+ (SupplimentarySeq) | (SupplimentarySeq >> 6) |
+ (SupplimentarySeq >> 2 * 6) | (SupplimentarySeq >> 3 * 6);
+
+ // Our character count will be 2 characters for these 4 bytes, so subtract another char
+ charCount--;
+ }
+ else {
+ // 3 byte encoding
+ // Add bit flags so that when we check new characters & rotate we'll be flagged correctly.
+ ch = (ch & 0x0F) | ((FinalByte >> 2 * 6) | (1 << 30) |
+ (ThreeByteSeq) | (ThreeByteSeq >> 6) | (ThreeByteSeq >> 2 * 6));
+
+ // We'll expect 1 character for these 3 bytes, so subtract another char.
+ charCount--;
+ }
+ }
+ else {
+ // 2 byte encoding
+
+ ch &= 0x1F;
+
+ // check for non-shortest form
+ if (ch <= 1) {
+ ch |= 0xc0;
+ goto InvalidByteSequence;
+ }
+
+ // Add bit flags so we'll be flagged correctly
+ ch |= (FinalByte >> 6);
+ }
+ continue;
+ }
+
+ EncodeChar:
+
+#ifdef FASTLOOP
+ int availableBytes = PtrDiff(pEnd, pSrc);
+
+ // don't fall into the fast decoding loop if we don't have enough bytes
+ if (availableBytes <= 13) {
+ // try to get over the remainder of the ascii characters fast though
+ BYTE* pLocalEnd = pEnd; // hint to get pLocalEnd enregistered
+ while (pSrc < pLocalEnd) {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F)
+ goto ProcessChar;
+ }
+ // we are done
+ ch = 0;
+ break;
+ }
+
+ // To compute the upper bound, assume that all characters are ASCII characters at this point,
+ // the boundary will be decreased for every non-ASCII character we encounter
+ // Also, we need 7 chars reserve for the unrolled ansi decoding loop and for decoding of multibyte sequences
+ BYTE *pStop = pSrc + availableBytes - 7;
+
+ while (pSrc < pStop) {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F) {
+ goto LongCode;
+ }
+
+ // get pSrc 2-byte aligned
+ if (((int)pSrc & 0x1) != 0) {
+ ch = *pSrc;
+ pSrc++;
+ if (ch > 0x7F) {
+ goto LongCode;
+ }
+ }
+
+ // get pSrc 4-byte aligned
+ if (((int)pSrc & 0x2) != 0) {
+ ch = *(USHORT*)pSrc;
+ if ((ch & 0x8080) != 0) {
+ goto LongCodeWithMask16;
+ }
+ pSrc += 2;
+ }
+
+
+ // Run 8 + 8 characters at a time!
+ while (pSrc < pStop) {
+ ch = *(int*)pSrc;
+ int chb = *(int*)(pSrc + 4);
+ if (((ch | chb) & (int)0x80808080) != 0) {
+ goto LongCodeWithMask32;
+ }
+ pSrc += 8;
+
+ // This is a really small loop - unroll it
+ if (pSrc >= pStop)
+ break;
+
+ ch = *(int*)pSrc;
+ chb = *(int*)(pSrc + 4);
+ if (((ch | chb) & (int)0x80808080) != 0) {
+ goto LongCodeWithMask32;
+ }
+ pSrc += 8;
+ }
+ break;
+
+#if BIGENDIAN
+ LongCodeWithMask32 :
+ // be careful about the sign extension
+ ch = (int)(((uint)ch) >> 16);
+ LongCodeWithMask16:
+ ch = (int)(((uint)ch) >> 8);
+#else // BIGENDIAN
+ LongCodeWithMask32:
+ LongCodeWithMask16:
+ ch &= 0xFF;
+#endif // BIGENDIAN
+ pSrc++;
+ if (ch <= 0x7F) {
+ continue;
+ }
+
+ LongCode:
+ int chc = *pSrc;
+ pSrc++;
+
+ if (
+ // bit 6 has to be zero
+ (ch & 0x40) == 0 ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (chc & 0xC0) != 0x80)
+ {
+ goto BadLongCode;
+ }
+
+ chc &= 0x3F;
+
+ // start a new long code
+ if ((ch & 0x20) != 0) {
+
+ // fold the first two bytes together
+ chc |= (ch & 0x0F) << 6;
+
+ if ((ch & 0x10) != 0) {
+ // 4 byte encoding - surrogate
+ ch = *pSrc;
+ if (
+ // check that bit 4 is zero, the non-shortest form of surrogate
+ // and the valid surrogate range 0x000000 - 0x10FFFF at the same time
+ !InRange(chc >> 4, 0x01, 0x10) ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (ch & 0xC0) != 0x80)
+ {
+ goto BadLongCode;
+ }
+
+ chc = (chc << 6) | (ch & 0x3F);
+
+ ch = *(pSrc + 1);
+ // we are expecting to see trailing bytes like 10vvvvvv
+ if ((ch & 0xC0) != 0x80) {
+ goto BadLongCode;
+ }
+ pSrc += 2;
+
+ // extra byte
+ charCount--;
+ }
+ else {
+ // 3 byte encoding
+ ch = *pSrc;
+ if (
+ // check for non-shortest form of 3 byte seq
+ (chc & (0x1F << 5)) == 0 ||
+ // Can't have surrogates here.
+ (chc & (0xF800 >> 6)) == (0xD800 >> 6) ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (ch & 0xC0) != 0x80)
+ {
+ goto BadLongCode;
+ }
+ pSrc++;
+
+ // extra byte
+ charCount--;
+ }
+ }
+ else {
+ // 2 byte encoding
+
+ // check for non-shortest form
+ if ((ch & 0x1E) == 0) {
+ goto BadLongCode;
+ }
+ }
+
+ // extra byte
+ charCount--;
+ }
+#endif // FASTLOOP
+
+ // no pending bits at this point
+ ch = 0;
+ continue;
+
+ BadLongCode:
+ pSrc -= 2;
+ ch = 0;
+ continue;
+ }
+
+ // May have a problem if we have to flush
+ if (ch != 0)
+ {
+ // We were already adjusting for these, so need to unadjust
+ charCount += (ch >> 30);
+ // Have to do fallback for invalid bytes
+ if (fallback == nullptr)
+ {
+ fallback = decoderFallback->CreateFallbackBuffer();
+ fallback->InternalInitialize(bytes, nullptr);
+ }
+ charCount += FallbackInvalidByteSequence(pSrc, ch, fallback);
+ }
+
+ // Shouldn't have anything in fallback buffer for GetCharCount
+ // (don't have to check m_throwOnOverflow for count)
+ Contract::Assert(fallback == nullptr || fallback->GetRemaining() == 0,
+ "[UTF8Encoding.GetCharCount]Expected empty fallback buffer at end");
+
+ return charCount;
+
+ }
+
+ int GetChars(BYTE* bytes, int byteCount, WCHAR* chars, int charCount)
+ {
+ Contract::Assert(chars != nullptr, "[UTF8Encoding.GetChars]chars!=nullptr");
+ Contract::Assert(byteCount >= 0, "[UTF8Encoding.GetChars]byteCount >=0");
+ Contract::Assert(charCount >= 0, "[UTF8Encoding.GetChars]charCount >=0");
+ Contract::Assert(bytes != nullptr, "[UTF8Encoding.GetChars]bytes!=nullptr");
+
+ BYTE *pSrc = bytes;
+ WCHAR *pTarget = chars;
+
+ BYTE *pEnd = pSrc + byteCount;
+ WCHAR *pAllocatedBufferEnd = pTarget + charCount;
+
+ int ch = 0;
+
+ DecoderFallbackBuffer *fallback = nullptr;
+
+ for (;;)
+ {
+ // SLOWLOOP: does all range checks, handles all special cases, but it is slow
+
+ if (pSrc >= pEnd) {
+ break;
+ }
+
+ // read next byte. The JIT optimization seems to be getting confused when
+ // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
+ int cha = *pSrc;
+
+ if (ch == 0) {
+ // no pending bits
+ goto ReadChar;
+ }
+
+ pSrc++;
+
+ // we are expecting to see trailing bytes like 10vvvvvv
+ if ((cha & 0xC0) != 0x80) {
+ // This can be a valid starting byte for another UTF8 byte sequence, so let's put
+ // the current byte back, and try to see if this is a valid byte for another UTF8 byte sequence
+ pSrc--;
+ goto InvalidByteSequence;
+ }
+
+ // fold in the new byte
+ ch = (ch << 6) | (cha & 0x3F);
+
+ if ((ch & FinalByte) == 0) {
+ // Not at last byte yet
+ Contract::Assert((ch & (SupplimentarySeq | ThreeByteSeq)) != 0,
+ "[UTF8Encoding.GetChars]Invariant volation");
+
+ if ((ch & SupplimentarySeq) != 0) {
+ // Its a 4-byte supplimentary sequence
+ if ((ch & (FinalByte >> 6)) != 0) {
+ // this is 3rd byte of 4 byte sequence - nothing to do
+ continue;
+ }
+
+ // 2nd byte of 4 bytes
+ // check for non-shortest form of surrogate and the valid surrogate
+ // range 0x000000 - 0x10FFFF at the same time
+ if (!InRange(ch & 0x1F0, 0x10, 0x100)) {
+ goto InvalidByteSequence;
+ }
+ }
+ else {
+ // Must be 2nd byte of a 3-byte sequence
+ // check for non-shortest form of 3 byte seq
+ if ((ch & (0x1F << 5)) == 0 || // non-shortest form
+ (ch & (0xF800 >> 6)) == (0xD800 >> 6)) // illegal individually encoded surrogate
+ {
+ goto InvalidByteSequence;
+ }
+ }
+ continue;
+ }
+
+ // ready to punch
+
+ // surrogate in shortest form?
+ // Might be possible to get rid of this? Already did non-shortest check for 4-byte sequence when reading 2nd byte?
+ if ((ch & (SupplimentarySeq | 0x1F0000)) > SupplimentarySeq) {
+ // let the range check for the second char throw the exception
+ if (pTarget < pAllocatedBufferEnd) {
+ *pTarget = (WCHAR)(((ch >> 10) & 0x7FF) +
+ (SHORT)((CharUnicodeInfo::HIGH_SURROGATE_START - (0x10000 >> 10))));
+ pTarget++;
+
+ ch = (ch & 0x3FF) +
+ (int)(CharUnicodeInfo::LOW_SURROGATE_START);
+ }
+ }
+
+ goto EncodeChar;
+
+ InvalidByteSequence:
+ // this code fragment should be close to the gotos referencing it
+ // Have to do fallback for invalid bytes
+ if (fallback == nullptr)
+ {
+ fallback = decoderFallback->CreateFallbackBuffer();
+ fallback->InternalInitialize(bytes, pAllocatedBufferEnd);
+ }
+ // This'll back us up the appropriate # of bytes if we didn't get anywhere
+ if (!FallbackInvalidByteSequence(pSrc, ch, fallback))
+ {
+ // Ran out of buffer space
+ // Need to throw an exception?
+ Contract::Assert(pSrc >= bytes || pTarget == chars,
+ "[UTF8Encoding.GetChars]Expected to throw or remain in byte buffer after fallback");
+ fallback->InternalReset();
+ ThrowCharsOverflow(pTarget == chars);
+ ch = 0;
+ break;
+ }
+ Contract::Assert(pSrc >= bytes,
+ "[UTF8Encoding.GetChars]Expected invalid byte sequence to have remained within the byte array");
+ ch = 0;
+ continue;
+
+ ReadChar:
+ ch = *pSrc;
+ pSrc++;
+
+ ProcessChar:
+ if (ch > 0x7F) {
+ // If its > 0x7F, its start of a new multi-byte sequence
+
+ // bit 6 has to be non-zero
+ if ((ch & 0x40) == 0) {
+ goto InvalidByteSequence;
+ }
+
+ // start a new long code
+ if ((ch & 0x20) != 0) {
+ if ((ch & 0x10) != 0) {
+ // 4 byte encoding - supplimentary character (2 surrogates)
+
+ ch &= 0x0F;
+
+ // check that bit 4 is zero and the valid supplimentary character
+ // range 0x000000 - 0x10FFFF at the same time
+ if (ch > 0x04) {
+ ch |= 0xf0;
+ goto InvalidByteSequence;
+ }
+
+ ch |= (FinalByte >> 3 * 6) | (1 << 30) | (3 << (30 - 2 * 6)) |
+ (SupplimentarySeq) | (SupplimentarySeq >> 6) |
+ (SupplimentarySeq >> 2 * 6) | (SupplimentarySeq >> 3 * 6);
+ }
+ else {
+ // 3 byte encoding
+ ch = (ch & 0x0F) | ((FinalByte >> 2 * 6) | (1 << 30) |
+ (ThreeByteSeq) | (ThreeByteSeq >> 6) | (ThreeByteSeq >> 2 * 6));
+ }
+ }
+ else {
+ // 2 byte encoding
+
+ ch &= 0x1F;
+
+ // check for non-shortest form
+ if (ch <= 1) {
+ ch |= 0xc0;
+ goto InvalidByteSequence;
+ }
+
+ ch |= (FinalByte >> 6);
+ }
+ continue;
+ }
+
+ EncodeChar:
+ // write the pending character
+ if (pTarget >= pAllocatedBufferEnd)
+ {
+ // Fix chars so we make sure to throw if we didn't output anything
+ ch &= 0x1fffff;
+ if (ch > 0x7f)
+ {
+ if (ch > 0x7ff)
+ {
+ if (ch >= CharUnicodeInfo::LOW_SURROGATE_START &&
+ ch <= CharUnicodeInfo::LOW_SURROGATE_END)
+ {
+ pSrc--; // It was 4 bytes
+ pTarget--; // 1 was stored already, but we can't remember 1/2, so back up
+ }
+ else if (ch > 0xffff)
+ {
+ pSrc--; // It was 4 bytes, nothing was stored
+ }
+ pSrc--; // It was at least 3 bytes
+ }
+ pSrc--; // It was at least 2 bytes
+ }
+ pSrc--;
+
+ // Throw that we don't have enough room (pSrc could be < chars if we had started to process
+ // a 4 byte sequence alredy)
+ Contract::Assert(pSrc >= bytes || pTarget == chars,
+ "[UTF8Encoding.GetChars]Expected pSrc to be within input buffer or throw due to no output]");
+ ThrowCharsOverflow(pTarget == chars);
+
+ // Don't store ch in decoder, we already backed up to its start
+ ch = 0;
+
+ // Didn't throw, just use this buffer size.
+ break;
+ }
+ *pTarget = (WCHAR)ch;
+ pTarget++;
+
+#ifdef FASTLOOP
+ int availableChars = PtrDiff(pAllocatedBufferEnd, pTarget);
+ int availableBytes = PtrDiff(pEnd, pSrc);
+
+ // don't fall into the fast decoding loop if we don't have enough bytes
+ // Test for availableChars is done because pStop would be <= pTarget.
+ if (availableBytes <= 13) {
+ // we may need as many as 1 character per byte
+ if (availableChars < availableBytes) {
+ // not enough output room. no pending bits at this point
+ ch = 0;
+ continue;
+ }
+
+ // try to get over the remainder of the ascii characters fast though
+ BYTE* pLocalEnd = pEnd; // hint to get pLocalEnd enregistered
+ while (pSrc < pLocalEnd) {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F)
+ goto ProcessChar;
+
+ *pTarget = (char)ch;
+ pTarget++;
+ }
+ // we are done
+ ch = 0;
+ break;
+ }
+
+ // we may need as many as 1 character per byte, so reduce the byte count if necessary.
+ // If availableChars is too small, pStop will be before pTarget and we won't do fast loop.
+ if (availableChars < availableBytes) {
+ availableBytes = availableChars;
+ }
+
+ // To compute the upper bound, assume that all characters are ASCII characters at this point,
+ // the boundary will be decreased for every non-ASCII character we encounter
+ // Also, we need 7 chars reserve for the unrolled ansi decoding loop and for decoding of multibyte sequences
+ WCHAR *pStop = pTarget + availableBytes - 7;
+
+ while (pTarget < pStop) {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F) {
+ goto LongCode;
+ }
+ *pTarget = (WCHAR)ch;
+ pTarget++;
+
+ // get pSrc to be 2-byte aligned
+ if ((((int)pSrc) & 0x1) != 0) {
+ ch = *pSrc;
+ pSrc++;
+ if (ch > 0x7F) {
+ goto LongCode;
+ }
+ *pTarget = (char)ch;
+ pTarget++;
+ }
+
+ // get pSrc to be 4-byte aligned
+ if ((((int)pSrc) & 0x2) != 0) {
+ ch = *(USHORT*)pSrc;
+ if ((ch & 0x8080) != 0) {
+ goto LongCodeWithMask16;
+ }
+
+ // Unfortunately, this is endianess sensitive
+#if BIGENDIAN
+ *pTarget = (WCHAR)((ch >> 8) & 0x7F);
+ pSrc += 2;
+ *(pTarget + 1) = (WCHAR)(ch & 0x7F);
+ pTarget += 2;
+#else // BIGENDIAN
+ *pTarget = (WCHAR)(ch & 0x7F);
+ pSrc += 2;
+ *(pTarget + 1) = (WCHAR)((ch >> 8) & 0x7F);
+ pTarget += 2;
+#endif // BIGENDIAN
+ }
+
+ // Run 8 characters at a time!
+ while (pTarget < pStop) {
+ ch = *(int*)pSrc;
+ int chb = *(int*)(pSrc + 4);
+ if (((ch | chb) & (int)0x80808080) != 0) {
+ goto LongCodeWithMask32;
+ }
+
+ // Unfortunately, this is endianess sensitive
+#if BIGENDIAN
+ *pTarget = (WCHAR)((ch >> 24) & 0x7F);
+ *(pTarget + 1) = (WCHAR)((ch >> 16) & 0x7F);
+ *(pTarget + 2) = (WCHAR)((ch >> 8) & 0x7F);
+ *(pTarget + 3) = (WCHAR)(ch & 0x7F);
+ pSrc += 8;
+ *(pTarget + 4) = (WCHAR)((chb >> 24) & 0x7F);
+ *(pTarget + 5) = (WCHAR)((chb >> 16) & 0x7F);
+ *(pTarget + 6) = (WCHAR)((chb >> 8) & 0x7F);
+ *(pTarget + 7) = (WCHAR)(chb & 0x7F);
+ pTarget += 8;
+#else // BIGENDIAN
+ *pTarget = (WCHAR)(ch & 0x7F);
+ *(pTarget + 1) = (WCHAR)((ch >> 8) & 0x7F);
+ *(pTarget + 2) = (WCHAR)((ch >> 16) & 0x7F);
+ *(pTarget + 3) = (WCHAR)((ch >> 24) & 0x7F);
+ pSrc += 8;
+ *(pTarget + 4) = (WCHAR)(chb & 0x7F);
+ *(pTarget + 5) = (WCHAR)((chb >> 8) & 0x7F);
+ *(pTarget + 6) = (WCHAR)((chb >> 16) & 0x7F);
+ *(pTarget + 7) = (WCHAR)((chb >> 24) & 0x7F);
+ pTarget += 8;
+#endif // BIGENDIAN
+ }
+ break;
+
+#if BIGENDIAN
+ LongCodeWithMask32 :
+ // be careful about the sign extension
+ ch = (int)(((uint)ch) >> 16);
+ LongCodeWithMask16:
+ ch = (int)(((uint)ch) >> 8);
+#else // BIGENDIAN
+ LongCodeWithMask32:
+ LongCodeWithMask16:
+ ch &= 0xFF;
+#endif // BIGENDIAN
+ pSrc++;
+ if (ch <= 0x7F) {
+ *pTarget = (WCHAR)ch;
+ pTarget++;
+ continue;
+ }
+
+ LongCode:
+ int chc = *pSrc;
+ pSrc++;
+
+ if (
+ // bit 6 has to be zero
+ (ch & 0x40) == 0 ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (chc & 0xC0) != 0x80)
+ {
+ goto BadLongCode;
+ }
+
+ chc &= 0x3F;
+
+ // start a new long code
+ if ((ch & 0x20) != 0) {
+
+ // fold the first two bytes together
+ chc |= (ch & 0x0F) << 6;
+
+ if ((ch & 0x10) != 0) {
+ // 4 byte encoding - surrogate
+ ch = *pSrc;
+ if (
+ // check that bit 4 is zero, the non-shortest form of surrogate
+ // and the valid surrogate range 0x000000 - 0x10FFFF at the same time
+ !InRange(chc >> 4, 0x01, 0x10) ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (ch & 0xC0) != 0x80)
+ {
+ goto BadLongCode;
+ }
+
+ chc = (chc << 6) | (ch & 0x3F);
+
+ ch = *(pSrc + 1);
+ // we are expecting to see trailing bytes like 10vvvvvv
+ if ((ch & 0xC0) != 0x80) {
+ goto BadLongCode;
+ }
+ pSrc += 2;
+
+ ch = (chc << 6) | (ch & 0x3F);
+
+ *pTarget = (char)(((ch >> 10) & 0x7FF) +
+ (SHORT)(CharUnicodeInfo::HIGH_SURROGATE_START - (0x10000 >> 10)));
+ pTarget++;
+
+ ch = (ch & 0x3FF) +
+ (SHORT)(CharUnicodeInfo::LOW_SURROGATE_START);
+
+ // extra byte, we're already planning 2 chars for 2 of these bytes,
+ // but the big loop is testing the target against pStop, so we need
+ // to subtract 2 more or we risk overrunning the input. Subtract
+ // one here and one below.
+ pStop--;
+ }
+ else {
+ // 3 byte encoding
+ ch = *pSrc;
+ if (
+ // check for non-shortest form of 3 byte seq
+ (chc & (0x1F << 5)) == 0 ||
+ // Can't have surrogates here.
+ (chc & (0xF800 >> 6)) == (0xD800 >> 6) ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (ch & 0xC0) != 0x80)
+ {
+ goto BadLongCode;
+ }
+ pSrc++;
+
+ ch = (chc << 6) | (ch & 0x3F);
+
+ // extra byte, we're only expecting 1 char for each of these 3 bytes,
+ // but the loop is testing the target (not source) against pStop, so
+ // we need to subtract 2 more or we risk overrunning the input.
+ // Subtract 1 here and one more below
+ pStop--;
+ }
+ }
+ else {
+ // 2 byte encoding
+
+ ch &= 0x1F;
+
+ // check for non-shortest form
+ if (ch <= 1) {
+ goto BadLongCode;
+ }
+ ch = (ch << 6) | chc;
+ }
+
+ *pTarget = (WCHAR)ch;
+ pTarget++;
+
+ // extra byte, we're only expecting 1 char for each of these 2 bytes,
+ // but the loop is testing the target (not source) against pStop.
+ // subtract an extra count from pStop so that we don't overrun the input.
+ pStop--;
+ }
+#endif // FASTLOOP
+
+ Contract::Assert(pTarget <= pAllocatedBufferEnd, "[UTF8Encoding.GetChars]pTarget <= pAllocatedBufferEnd");
+
+ // no pending bits at this point
+ ch = 0;
+ continue;
+
+ BadLongCode:
+ pSrc -= 2;
+ ch = 0;
+ continue;
+ }
+
+ if (ch != 0)
+ {
+ // Have to do fallback for invalid bytes
+ if (fallback == nullptr)
+ {
+ fallback = decoderFallback->CreateFallbackBuffer();
+ fallback->InternalInitialize(bytes, pAllocatedBufferEnd);
+ }
+
+ // This'll back us up the appropriate # of bytes if we didn't get anywhere
+ if (!FallbackInvalidByteSequence(pSrc, ch, fallback))
+ {
+ Contract::Assert(pSrc >= bytes || pTarget == chars,
+ "[UTF8Encoding.GetChars]Expected to throw or remain in byte buffer while flushing");
+
+ // Ran out of buffer space
+ // Need to throw an exception?
+ fallback->InternalReset();
+ ThrowCharsOverflow(pTarget == chars);
+ }
+ Contract::Assert(pSrc >= bytes,
+ "[UTF8Encoding.GetChars]Expected flushing invalid byte sequence to have remained within the byte array");
+ ch = 0;
+ }
+
+ // Shouldn't have anything in fallback buffer for GetChars
+ // (don't have to check m_throwOnOverflow for chars)
+ Contract::Assert(fallback == nullptr || fallback->GetRemaining() == 0,
+ "[UTF8Encoding.GetChars]Expected empty fallback buffer at end");
+
+ return PtrDiff(pTarget, chars);
+ }
+
+ int GetBytes(WCHAR* chars, int charCount, BYTE* bytes, int byteCount)
+ {
+ Contract::Assert(chars != nullptr, "[UTF8Encoding.GetBytes]chars!=nullptr");
+ Contract::Assert(byteCount >= 0, "[UTF8Encoding.GetBytes]byteCount >=0");
+ Contract::Assert(charCount >= 0, "[UTF8Encoding.GetBytes]charCount >=0");
+ Contract::Assert(bytes != nullptr, "[UTF8Encoding.GetBytes]bytes!=nullptr");
+
+ // For fallback we may need a fallback buffer.
+ // We wait to initialize it though in case we don't have any broken input unicode
+ EncoderFallbackBuffer* fallbackBuffer = nullptr;
+ WCHAR *pSrc = chars;
+ BYTE *pTarget = bytes;
+
+ WCHAR *pEnd = pSrc + charCount;
+ BYTE *pAllocatedBufferEnd = pTarget + byteCount;
+
+ int ch = 0;
+
+ // assume that JIT will enregister pSrc, pTarget and ch
+
+ for (;;) {
+ // SLOWLOOP: does all range checks, handles all special cases, but it is slow
+
+ if (pSrc >= pEnd) {
+
+ if (ch == 0) {
+ // Check if there's anything left to get out of the fallback buffer
+ ch = fallbackBuffer != nullptr ? fallbackBuffer->InternalGetNextChar() : 0;
+ if (ch > 0) {
+ goto ProcessChar;
+ }
+ }
+ else {
+ // Case of leftover surrogates in the fallback buffer
+ if (fallbackBuffer != nullptr && fallbackBuffer->bFallingBack) {
+ Contract::Assert(ch >= 0xD800 && ch <= 0xDBFF,
+ "[UTF8Encoding.GetBytes]expected high surrogate"); //, not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
+
+ int cha = ch;
+
+ ch = fallbackBuffer->InternalGetNextChar();
+
+ if (InRange(ch, CharUnicodeInfo::LOW_SURROGATE_START, CharUnicodeInfo::LOW_SURROGATE_END)) {
+ ch = ch + (cha << 10) + (0x10000 - CharUnicodeInfo::LOW_SURROGATE_START - (CharUnicodeInfo::HIGH_SURROGATE_START << 10));
+ goto EncodeChar;
+ }
+ else if (ch > 0){
+ goto ProcessChar;
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ // attempt to encode the partial surrogate (will fail or ignore)
+ if (ch > 0)
+ goto EncodeChar;
+
+ // We're done
+ break;
+ }
+
+ if (ch > 0) {
+ // We have a high surrogate left over from a previous loop.
+ Contract::Assert(ch >= 0xD800 && ch <= 0xDBFF,
+ "[UTF8Encoding.GetBytes]expected high surrogate");//, not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
+
+ // use separate helper variables for local contexts so that the jit optimizations
+ // won't get confused about the variable lifetimes
+ int cha = *pSrc;
+
+ // In previous byte, we encountered a high surrogate, so we are expecting a low surrogate here.
+ // if (IsLowSurrogate(cha)) {
+ if (InRange(cha, CharUnicodeInfo::LOW_SURROGATE_START, CharUnicodeInfo::LOW_SURROGATE_END)) {
+ ch = cha + (ch << 10) +
+ (0x10000
+ - CharUnicodeInfo::LOW_SURROGATE_START
+ - (CharUnicodeInfo::HIGH_SURROGATE_START << 10));
+
+ pSrc++;
+ }
+ // else ch is still high surrogate and encoding will fail
+
+ // attempt to encode the surrogate or partial surrogate
+ goto EncodeChar;
+ }
+
+ // If we've used a fallback, then we have to check for it
+ if (fallbackBuffer != nullptr)
+ {
+ ch = fallbackBuffer->InternalGetNextChar();
+ if (ch > 0) goto ProcessChar;
+ }
+
+ // read next char. The JIT optimization seems to be getting confused when
+ // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
+ ch = *pSrc;
+ pSrc++;
+
+ ProcessChar:
+ if (InRange(ch, CharUnicodeInfo::HIGH_SURROGATE_START, CharUnicodeInfo::HIGH_SURROGATE_END)) {
+ continue;
+ }
+ // either good char or partial surrogate
+
+ EncodeChar:
+ // throw exception on partial surrogate if necessary
+ if (InRange(ch, CharUnicodeInfo::HIGH_SURROGATE_START, CharUnicodeInfo::LOW_SURROGATE_END))
+ {
+ // Lone surrogates aren't allowed, we have to do fallback for them
+ // Have to make a fallback buffer if we don't have one
+ if (fallbackBuffer == nullptr)
+ {
+ // wait on fallbacks if we can
+ // For fallback we may need a fallback buffer
+ fallbackBuffer = encoderFallback->CreateFallbackBuffer();
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer->InternalInitialize(chars, pEnd, true);
+ }
+
+ // Do our fallback. Actually we already know its a mixed up surrogate,
+ // so the ref pSrc isn't gonna do anything.
+ fallbackBuffer->InternalFallback((WCHAR)ch, &pSrc);
+
+ // Ignore it if we don't throw
+ ch = 0;
+ continue;
+ }
+
+ // Count bytes needed
+ int bytesNeeded = 1;
+ if (ch > 0x7F) {
+ if (ch > 0x7FF) {
+ if (ch > 0xFFFF) {
+ bytesNeeded++; // 4 bytes (surrogate pair)
+ }
+ bytesNeeded++; // 3 bytes (800-FFFF)
+ }
+ bytesNeeded++; // 2 bytes (80-7FF)
+ }
+
+ if (pTarget > pAllocatedBufferEnd - bytesNeeded) {
+ // Left over surrogate from last time will cause pSrc == chars, so we'll throw
+ if (fallbackBuffer != nullptr && fallbackBuffer->bFallingBack)
+ {
+ fallbackBuffer->MovePrevious(); // Didn't use this fallback char
+ if (ch > 0xFFFF)
+ fallbackBuffer->MovePrevious(); // Was surrogate, didn't use 2nd part either
+ }
+ else
+ {
+ pSrc--; // Didn't use this char
+ if (ch > 0xFFFF)
+ pSrc--; // Was surrogate, didn't use 2nd part either
+ }
+ Contract::Assert(pSrc >= chars || pTarget == bytes,
+ "[UTF8Encoding.GetBytes]Expected pSrc to be within buffer or to throw with insufficient room.");
+ ThrowBytesOverflow(pTarget == bytes); // Throw if we must
+ ch = 0; // Nothing left over (we backed up to start of pair if supplimentary)
+ break;
+ }
+
+ if (ch <= 0x7F) {
+ *pTarget = (BYTE)ch;
+ }
+ else {
+ // use separate helper variables for local contexts so that the jit optimizations
+ // won't get confused about the variable lifetimes
+ int chb;
+ if (ch <= 0x7FF) {
+ // 2 BYTE encoding
+ chb = (BYTE)(0xC0 | (ch >> 6));
+ }
+ else
+ {
+ if (ch <= 0xFFFF) {
+ chb = (BYTE)(0xE0 | (ch >> 12));
+ }
+ else
+ {
+ *pTarget = (BYTE)(0xF0 | (ch >> 18));
+ pTarget++;
+
+ chb = 0x80 | ((ch >> 12) & 0x3F);
+ }
+ *pTarget = (BYTE)chb;
+ pTarget++;
+
+ chb = 0x80 | ((ch >> 6) & 0x3F);
+ }
+ *pTarget = (BYTE)chb;
+ pTarget++;
+
+ *pTarget = (BYTE)0x80 | (ch & 0x3F);
+ }
+ pTarget++;
+
+
+#ifdef FASTLOOP
+ // If still have fallback don't do fast loop
+ if (fallbackBuffer != nullptr && (ch = fallbackBuffer->InternalGetNextChar()) != 0)
+ goto ProcessChar;
+
+ int availableChars = PtrDiff(pEnd, pSrc);
+ int availableBytes = PtrDiff(pAllocatedBufferEnd, pTarget);
+
+ // don't fall into the fast decoding loop if we don't have enough characters
+ // Note that if we don't have enough bytes, pStop will prevent us from entering the fast loop.
+ if (availableChars <= 13) {
+ // we are hoping for 1 BYTE per char
+ if (availableBytes < availableChars) {
+ // not enough output room. no pending bits at this point
+ ch = 0;
+ continue;
+ }
+
+ // try to get over the remainder of the ascii characters fast though
+ WCHAR* pLocalEnd = pEnd; // hint to get pLocalEnd enregistered
+ while (pSrc < pLocalEnd) {
+ ch = *pSrc;
+ pSrc++;
+
+ // Not ASCII, need more than 1 BYTE per char
+ if (ch > 0x7F)
+ goto ProcessChar;
+
+ *pTarget = (BYTE)ch;
+ pTarget++;
+ }
+ // we are done, let ch be 0 to clear encoder
+ ch = 0;
+ break;
+ }
+
+ // we need at least 1 BYTE per character, but Convert might allow us to convert
+ // only part of the input, so try as much as we can. Reduce charCount if necessary
+ if (availableBytes < availableChars)
+ {
+ availableChars = availableBytes;
+ }
+
+ // FASTLOOP:
+ // - optimistic range checks
+ // - fallbacks to the slow loop for all special cases, exception throwing, etc.
+
+ // To compute the upper bound, assume that all characters are ASCII characters at this point,
+ // the boundary will be decreased for every non-ASCII character we encounter
+ // Also, we need 5 chars reserve for the unrolled ansi decoding loop and for decoding of surrogates
+ // If there aren't enough bytes for the output, then pStop will be <= pSrc and will bypass the loop.
+ WCHAR *pStop = pSrc + availableChars - 5;
+
+ while (pSrc < pStop) {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F) {
+ goto LongCode;
+ }
+ *pTarget = (BYTE)ch;
+ pTarget++;
+
+ // get pSrc aligned
+ if (((size_t)pSrc & 0x2) != 0) {
+ ch = *pSrc;
+ pSrc++;
+ if (ch > 0x7F) {
+ goto LongCode;
+ }
+ *pTarget = (BYTE)ch;
+ pTarget++;
+ }
+
+ // Run 4 characters at a time!
+ while (pSrc < pStop) {
+ ch = *(int*)pSrc;
+ int chc = *(int*)(pSrc + 2);
+ if (((ch | chc) & (int)0xFF80FF80) != 0) {
+ goto LongCodeWithMask;
+ }
+
+ // Unfortunately, this is endianess sensitive
+#if BIGENDIAN
+ *pTarget = (BYTE)(ch >> 16);
+ *(pTarget + 1) = (BYTE)ch;
+ pSrc += 4;
+ *(pTarget + 2) = (BYTE)(chc >> 16);
+ *(pTarget + 3) = (BYTE)chc;
+ pTarget += 4;
+#else // BIGENDIAN
+ *pTarget = (BYTE)ch;
+ *(pTarget + 1) = (BYTE)(ch >> 16);
+ pSrc += 4;
+ *(pTarget + 2) = (BYTE)chc;
+ *(pTarget + 3) = (BYTE)(chc >> 16);
+ pTarget += 4;
+#endif // BIGENDIAN
+ }
+ continue;
+
+ LongCodeWithMask:
+#if BIGENDIAN
+ // be careful about the sign extension
+ ch = (int)(((uint)ch) >> 16);
+#else // BIGENDIAN
+ ch = (WCHAR)ch;
+#endif // BIGENDIAN
+ pSrc++;
+
+ if (ch > 0x7F) {
+ goto LongCode;
+ }
+ *pTarget = (BYTE)ch;
+ pTarget++;
+ continue;
+
+ LongCode:
+ // use separate helper variables for slow and fast loop so that the jit optimizations
+ // won't get confused about the variable lifetimes
+ int chd;
+ if (ch <= 0x7FF) {
+ // 2 BYTE encoding
+ chd = 0xC0 | (ch >> 6);
+ }
+ else {
+ if (!InRange(ch, CharUnicodeInfo::HIGH_SURROGATE_START, CharUnicodeInfo::LOW_SURROGATE_END)) {
+ // 3 BYTE encoding
+ chd = 0xE0 | (ch >> 12);
+ }
+ else
+ {
+ // 4 BYTE encoding - high surrogate + low surrogate
+ if (ch > CharUnicodeInfo::HIGH_SURROGATE_END) {
+ // low without high -> bad, try again in slow loop
+ pSrc -= 1;
+ break;
+ }
+
+ chd = *pSrc;
+ pSrc++;
+
+ // if (!IsLowSurrogate(chd)) {
+ if (!InRange(chd, CharUnicodeInfo::LOW_SURROGATE_START, CharUnicodeInfo::LOW_SURROGATE_END)) {
+ // high not followed by low -> bad, try again in slow loop
+ pSrc -= 2;
+ break;
+ }
+
+ ch = chd + (ch << 10) +
+ (0x10000
+ - CharUnicodeInfo::LOW_SURROGATE_START
+ - (CharUnicodeInfo::HIGH_SURROGATE_START << 10));
+
+ *pTarget = (BYTE)(0xF0 | (ch >> 18));
+ // pStop - this BYTE is compensated by the second surrogate character
+ // 2 input chars require 4 output bytes. 2 have been anticipated already
+ // and 2 more will be accounted for by the 2 pStop-- calls below.
+ pTarget++;
+
+ chd = 0x80 | ((ch >> 12) & 0x3F);
+ }
+ *pTarget = (BYTE)chd;
+ pStop--; // 3 BYTE sequence for 1 char, so need pStop-- and the one below too.
+ pTarget++;
+
+ chd = 0x80 | ((ch >> 6) & 0x3F);
+ }
+ *pTarget = (BYTE)chd;
+ pStop--; // 2 BYTE sequence for 1 char so need pStop--.
+ pTarget++;
+
+ *pTarget = (BYTE)(0x80 | (ch & 0x3F));
+ // pStop - this BYTE is already included
+ pTarget++;
+ }
+
+ Contract::Assert(pTarget <= pAllocatedBufferEnd, "[UTF8Encoding.GetBytes]pTarget <= pAllocatedBufferEnd");
+
+#endif // FASTLOOP
+
+ // no pending char at this point
+ ch = 0;
+ }
+
+ return (int)(pTarget - bytes);
+ }
+
+ int GetByteCount(WCHAR *chars, int count)
+ {
+ // For fallback we may need a fallback buffer.
+ // We wait to initialize it though in case we don't have any broken input unicode
+ EncoderFallbackBuffer* fallbackBuffer = nullptr;
+ WCHAR *pSrc = chars;
+ WCHAR *pEnd = pSrc + count;
+
+ // Start by assuming we have as many as count
+ int byteCount = count;
+
+ int ch = 0;
+
+ for (;;) {
+ // SLOWLOOP: does all range checks, handles all special cases, but it is slow
+ if (pSrc >= pEnd) {
+
+ if (ch == 0) {
+ // Unroll any fallback that happens at the end
+ ch = fallbackBuffer != nullptr ? fallbackBuffer->InternalGetNextChar() : 0;
+ if (ch > 0) {
+ byteCount++;
+ goto ProcessChar;
+ }
+ }
+ else {
+ // Case of surrogates in the fallback.
+ if (fallbackBuffer != nullptr && fallbackBuffer->bFallingBack) {
+ Contract::Assert(ch >= 0xD800 && ch <= 0xDBFF,
+ "[UTF8Encoding.GetBytes]expected high surrogate");// , not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
+
+ ch = fallbackBuffer->InternalGetNextChar();
+ byteCount++;
+
+ if (InRange(ch, CharUnicodeInfo::LOW_SURROGATE_START, CharUnicodeInfo::LOW_SURROGATE_END)) {
+ ch = 0xfffd;
+ byteCount++;
+ goto EncodeChar;
+ }
+ else if (ch > 0){
+ goto ProcessChar;
+ }
+ else {
+ byteCount--; // ignore last one.
+ break;
+ }
+ }
+ }
+
+ if (ch <= 0) {
+ break;
+ }
+
+ // attempt to encode the partial surrogate (will fallback or ignore it), it'll also subtract 1.
+ byteCount++;
+ goto EncodeChar;
+ }
+
+ if (ch > 0) {
+ Contract::Assert(ch >= 0xD800 && ch <= 0xDBFF,
+ "[UTF8Encoding.GetBytes]expected high surrogate"); // , not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
+
+ // use separate helper variables for local contexts so that the jit optimizations
+ // won't get confused about the variable lifetimes
+ int cha = *pSrc;
+
+ // count the pending surrogate
+ byteCount++;
+
+ // In previous byte, we encountered a high surrogate, so we are expecting a low surrogate here.
+ // if (IsLowSurrogate(cha)) {
+ if (InRange(cha, CharUnicodeInfo::LOW_SURROGATE_START, CharUnicodeInfo::LOW_SURROGATE_END)) {
+ // Don't need a real # because we're just counting, anything > 0x7ff ('cept surrogate) will do.
+ ch = 0xfffd;
+ // ch = cha + (ch << 10) +
+ // (0x10000
+ // - CharUnicodeInfo::LOW_SURROGATE_START
+ // - (CharUnicodeInfo::HIGH_SURROGATE_START << 10) );
+
+ // Use this next char
+ pSrc++;
+ }
+ // else ch is still high surrogate and encoding will fail (so don't add count)
+
+ // attempt to encode the surrogate or partial surrogate
+ goto EncodeChar;
+ }
+
+ // If we've used a fallback, then we have to check for it
+ if (fallbackBuffer != nullptr)
+ {
+ ch = fallbackBuffer->InternalGetNextChar();
+ if (ch > 0)
+ {
+ // We have an extra byte we weren't expecting.
+ byteCount++;
+ goto ProcessChar;
+ }
+ }
+
+ // read next char. The JIT optimization seems to be getting confused when
+ // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
+ ch = *pSrc;
+ pSrc++;
+
+ ProcessChar:
+ if (InRange(ch, CharUnicodeInfo::HIGH_SURROGATE_START, CharUnicodeInfo::HIGH_SURROGATE_END)) {
+ // we will count this surrogate next time around
+ byteCount--;
+ continue;
+ }
+ // either good char or partial surrogate
+
+ EncodeChar:
+ // throw exception on partial surrogate if necessary
+ if (InRange(ch, CharUnicodeInfo::HIGH_SURROGATE_START, CharUnicodeInfo::LOW_SURROGATE_END))
+ {
+ // Lone surrogates aren't allowed
+ // Have to make a fallback buffer if we don't have one
+ if (fallbackBuffer == nullptr)
+ {
+ // wait on fallbacks if we can
+ // For fallback we may need a fallback buffer
+ fallbackBuffer = encoderFallback->CreateFallbackBuffer();
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer->InternalInitialize(chars, chars + count, false);
+ }
+
+ // Do our fallback. Actually we already know its a mixed up surrogate,
+ // so the ref pSrc isn't gonna do anything.
+ fallbackBuffer->InternalFallback((WCHAR)ch, &pSrc);
+
+ // Ignore it if we don't throw (we had preallocated this ch)
+ byteCount--;
+ ch = 0;
+ continue;
+ }
+
+ // Count them
+ if (ch > 0x7F) {
+ if (ch > 0x7FF) {
+ // the extra surrogate byte was compensated by the second surrogate character
+ // (2 surrogates make 4 bytes. We've already counted 2 bytes, 1 per char)
+ byteCount++;
+ }
+ byteCount++;
+ }
+
+#if WIN64
+ // check for overflow
+ if (byteCount < 0) {
+ break;
+ }
+#endif
+
+#ifdef FASTLOOP
+ // If still have fallback don't do fast loop
+ if (fallbackBuffer != nullptr && (ch = fallbackBuffer->InternalGetNextChar()) != 0)
+ {
+ // We're reserving 1 byte for each char by default
+ byteCount++;
+ goto ProcessChar;
+ }
+
+ int availableChars = PtrDiff(pEnd, pSrc);
+
+ // don't fall into the fast decoding loop if we don't have enough characters
+ if (availableChars <= 13) {
+ // try to get over the remainder of the ascii characters fast though
+ WCHAR* pLocalEnd = pEnd; // hint to get pLocalEnd enregistered
+ while (pSrc < pLocalEnd) {
+ ch = *pSrc;
+ pSrc++;
+ if (ch > 0x7F)
+ goto ProcessChar;
+ }
+
+ // we are done
+ break;
+ }
+
+#if WIN64
+ // make sure that we won't get a silent overflow inside the fast loop
+ // (Fall out to slow loop if we have this many characters)
+ availableChars &= 0x0FFFFFFF;
+#endif
+
+ // To compute the upper bound, assume that all characters are ASCII characters at this point,
+ // the boundary will be decreased for every non-ASCII character we encounter
+ // Also, we need 3 + 4 chars reserve for the unrolled ansi decoding loop and for decoding of surrogates
+ WCHAR *pStop = pSrc + availableChars - (3 + 4);
+
+ while (pSrc < pStop) {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F) // Not ASCII
+ {
+ if (ch > 0x7FF) // Not 2 Byte
+ {
+ if ((ch & 0xF800) == 0xD800) // See if its a Surrogate
+ goto LongCode;
+ byteCount++;
+ }
+ byteCount++;
+ }
+
+ // get pSrc aligned
+ if (((int)pSrc & 0x2) != 0) {
+ ch = *pSrc;
+ pSrc++;
+ if (ch > 0x7F) // Not ASCII
+ {
+ if (ch > 0x7FF) // Not 2 Byte
+ {
+ if ((ch & 0xF800) == 0xD800) // See if its a Surrogate
+ goto LongCode;
+ byteCount++;
+ }
+ byteCount++;
+ }
+ }
+
+ // Run 2 * 4 characters at a time!
+ while (pSrc < pStop) {
+ ch = *(int*)pSrc;
+ int chc = *(int*)(pSrc + 2);
+ if (((ch | chc) & (int)0xFF80FF80) != 0) // See if not ASCII
+ {
+ if (((ch | chc) & (int)0xF800F800) != 0) // See if not 2 Byte
+ {
+ goto LongCodeWithMask;
+ }
+
+
+ if ((ch & (int)0xFF800000) != 0) // Actually 0x07800780 is all we care about (4 bits)
+ byteCount++;
+ if ((ch & (int)0xFF80) != 0)
+ byteCount++;
+ if ((chc & (int)0xFF800000) != 0)
+ byteCount++;
+ if ((chc & (int)0xFF80) != 0)
+ byteCount++;
+ }
+ pSrc += 4;
+
+ ch = *(int*)pSrc;
+ chc = *(int*)(pSrc + 2);
+ if (((ch | chc) & (int)0xFF80FF80) != 0) // See if not ASCII
+ {
+ if (((ch | chc) & (int)0xF800F800) != 0) // See if not 2 Byte
+ {
+ goto LongCodeWithMask;
+ }
+
+ if ((ch & (int)0xFF800000) != 0)
+ byteCount++;
+ if ((ch & (int)0xFF80) != 0)
+ byteCount++;
+ if ((chc & (int)0xFF800000) != 0)
+ byteCount++;
+ if ((chc & (int)0xFF80) != 0)
+ byteCount++;
+ }
+ pSrc += 4;
+ }
+ break;
+
+ LongCodeWithMask:
+#if BIGENDIAN
+ // be careful about the sign extension
+ ch = (int)(((uint)ch) >> 16);
+#else // BIGENDIAN
+ ch = (WCHAR)ch;
+#endif // BIGENDIAN
+ pSrc++;
+
+ if (ch <= 0x7F) {
+ continue;
+ }
+
+ LongCode:
+ // use separate helper variables for slow and fast loop so that the jit optimizations
+ // won't get confused about the variable lifetimes
+ if (ch > 0x7FF) {
+ if (InRange(ch, CharUnicodeInfo::HIGH_SURROGATE_START, CharUnicodeInfo::LOW_SURROGATE_END)) {
+ // 4 byte encoding - high surrogate + low surrogate
+
+ int chd = *pSrc;
+ if (
+ ch > CharUnicodeInfo::HIGH_SURROGATE_END ||
+ !InRange(chd, CharUnicodeInfo::LOW_SURROGATE_START, CharUnicodeInfo::LOW_SURROGATE_END))
+ {
+ // Back up and drop out to slow loop to figure out error
+ pSrc--;
+ break;
+ }
+ pSrc++;
+
+ // byteCount - this byte is compensated by the second surrogate character
+ }
+ byteCount++;
+ }
+ byteCount++;
+
+ // byteCount - the last byte is already included
+ }
+#endif // FASTLOOP
+
+ // no pending char at this point
+ ch = 0;
+ }
+
+#if WIN64
+ // check for overflow
+ if (byteCount < 0) {
+ throw ArgumentException("Conversion buffer overflow.");
+ }
+#endif
+
+ Contract::Assert(fallbackBuffer == nullptr || fallbackBuffer->GetRemaining() == 0,
+ "[UTF8Encoding.GetByteCount]Expected Empty fallback buffer");
+
+ return byteCount;
+ }
+
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+//
+// UTF8ToUnicode
+//
+// Maps a UTF-8 character string to its wide character string counterpart.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int UTF8ToUnicode(
+ LPCSTR lpSrcStr,
+ int cchSrc,
+ LPWSTR lpDestStr,
+ int cchDest,
+ DWORD dwFlags
+ )
+{
+ int ret;
+ UTF8Encoding enc(dwFlags & MB_ERR_INVALID_CHARS);
+ try {
+ ret = enc.GetCharCount((BYTE*)lpSrcStr, cchSrc);
+ if (cchDest){
+ if (ret > cchDest){
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ ret = 0;
+ }
+ enc.GetChars((BYTE*)lpSrcStr, cchSrc, (WCHAR*)lpDestStr, ret);
+ }
+ }
+ catch (const InsufficientBufferException& e){
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return 0;
+ }
+ catch (const DecoderFallbackException& e){
+ SetLastError(ERROR_NO_UNICODE_TRANSLATION);
+ return 0;
+ }
+ catch (const ArgumentException& e){
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ return ret;
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// UnicodeToUTF8
+//
+// Maps a Unicode character string to its UTF-8 string counterpart.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int UnicodeToUTF8(
+ LPCWSTR lpSrcStr,
+ int cchSrc,
+ LPSTR lpDestStr,
+ int cchDest)
+{
+ int ret;
+ UTF8Encoding enc(false);
+ try{
+ ret = enc.GetByteCount((WCHAR*)lpSrcStr, cchSrc);
+ if (cchDest){
+ if (ret > cchDest){
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ ret = 0;
+ }
+ enc.GetBytes((WCHAR*)lpSrcStr, cchSrc, (BYTE*)lpDestStr, ret);
+ }
+ }
+ catch (const InsufficientBufferException& e){
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return 0;
+ }
+ catch (const EncoderFallbackException& e){
+ SetLastError(ERROR_NO_UNICODE_TRANSLATION);
+ return 0;
+ }
+ catch (const ArgumentException& e){
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ return ret;
+}
diff --git a/src/pal/src/map/common.cpp b/src/pal/src/map/common.cpp
new file mode 100644
index 0000000000..a030c3b384
--- /dev/null
+++ b/src/pal/src/map/common.cpp
@@ -0,0 +1,66 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ common.c
+
+Abstract:
+
+ Implementation of the common mapping functions.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+
+#include "common.h"
+
+#include <sys/mman.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(VIRTUAL);
+
+/*****
+ *
+ * W32toUnixAccessControl( DWORD ) - Maps Win32 to Unix memory access controls .
+ *
+ */
+INT W32toUnixAccessControl( IN DWORD flProtect )
+{
+ INT MemAccessControl = 0;
+
+ switch ( flProtect & 0xff )
+ {
+ case PAGE_READONLY :
+ MemAccessControl = PROT_READ;
+ break;
+ case PAGE_READWRITE :
+ MemAccessControl = PROT_READ | PROT_WRITE;
+ break;
+ case PAGE_EXECUTE_READWRITE:
+ MemAccessControl = PROT_EXEC | PROT_READ | PROT_WRITE;
+ break;
+ case PAGE_EXECUTE :
+ MemAccessControl = PROT_EXEC;
+ break;
+ case PAGE_EXECUTE_READ :
+ MemAccessControl = PROT_EXEC | PROT_READ;
+ break;
+ case PAGE_NOACCESS :
+ MemAccessControl = PROT_NONE;
+ break;
+
+ default:
+ MemAccessControl = 0;
+ ERROR( "Incorrect or no protection flags specified.\n" );
+ break;
+ }
+ return MemAccessControl;
+}
diff --git a/src/pal/src/map/common.h b/src/pal/src/map/common.h
new file mode 100644
index 0000000000..68d8fc6ae2
--- /dev/null
+++ b/src/pal/src/map/common.h
@@ -0,0 +1,43 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/common.h
+
+Abstract:
+ Header file for common helper functions in the map module.
+
+
+
+--*/
+
+#ifndef __COMMON_H_
+#define __COMMON_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*****
+ *
+ * W32toUnixAccessControl( DWORD ) - Maps Win32 to Unix memory access controls .
+ *
+ */
+INT W32toUnixAccessControl( IN DWORD flProtect );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __COMMON_H_ */
+
+
+
+
diff --git a/src/pal/src/map/map.cpp b/src/pal/src/map/map.cpp
new file mode 100644
index 0000000000..f3ec47b846
--- /dev/null
+++ b/src/pal/src/map/map.cpp
@@ -0,0 +1,2749 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ map.cpp
+
+Abstract:
+
+ Implementation of file mapping API.
+
+
+
+--*/
+
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/init.h"
+#include "pal/critsect.h"
+#include "pal/virtual.h"
+#include "pal/environ.h"
+#include "common.h"
+#include "pal/map.hpp"
+#include "pal/thread.hpp"
+#include "pal/file.hpp"
+#include "pal/malloc.hpp"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "rt/ntimage.h"
+#include <pal_endian.h>
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(VIRTUAL);
+
+//
+// The mapping critical section guards access to the list
+// of currently mapped views. If a thread needs to access
+// both this critical section and the data for an object
+// it must acquire the object data first. That is, a thread
+// cannot acquire any other locks after taking hold of
+// this critical section.
+//
+
+CRITICAL_SECTION mapping_critsec;
+LIST_ENTRY MappedViewList;
+
+#ifndef CORECLR
+static PAL_ERROR MAPCreateTempFile(CPalThread *, PINT, PSZ);
+#endif // !CORECLR
+static PAL_ERROR MAPGrowLocalFile(INT, UINT);
+static PMAPPED_VIEW_LIST MAPGetViewForAddress( LPCVOID );
+static PAL_ERROR MAPDesiredAccessAllowed( DWORD, DWORD, DWORD );
+
+static INT MAPProtectionToFileOpenFlags( DWORD );
+static BOOL MAPIsRequestPermissible( DWORD, CFileProcessLocalData * );
+static BOOL MAPContainsInvalidFlags( DWORD );
+static DWORD MAPConvertProtectToAccess( DWORD );
+static INT MAPFileMapToMmapFlags( DWORD );
+static DWORD MAPMmapProtToAccessFlags( int prot );
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+static NativeMapHolder * NewNativeMapHolder(CPalThread *pThread, LPVOID address, SIZE_T size,
+ SIZE_T offset, long init_ref_count);
+static LONG NativeMapHolderAddRef(NativeMapHolder * thisPMH);
+static LONG NativeMapHolderRelease(CPalThread *pThread, NativeMapHolder * thisPMH);
+static PMAPPED_VIEW_LIST FindSharedMappingReplacement(CPalThread *pThread, dev_t deviceNum, ino_t inodeNum,
+ SIZE_T size, SIZE_T offset);
+#endif
+
+static PAL_ERROR
+MAPRecordMapping(
+ IPalObject *pMappingObject,
+ void *pPEBaseAddress,
+ void *addr,
+ size_t len,
+ int prot
+ );
+
+static PAL_ERROR
+MAPmmapAndRecord(
+ IPalObject *pMappingObject,
+ void *pPEBaseAddress,
+ void *addr,
+ size_t len,
+ int prot,
+ int flags,
+ int fd,
+ off_t offset,
+ LPVOID *ppvBaseAddress
+ );
+
+#if !HAVE_MMAP_DEV_ZERO
+/* We need MAP_ANON. However on some platforms like HP-UX, it is defined as MAP_ANONYMOUS */
+#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
+#define MAP_ANON MAP_ANONYMOUS
+#endif
+#endif
+
+void
+FileMappingCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup,
+ bool fShutdown,
+ bool fCleanupSharedState
+ );
+
+PAL_ERROR
+FileMappingInitializationRoutine(
+ CPalThread *pThread,
+ CObjectType *pObjectType,
+ void *pImmutableData,
+ void *pSharedData,
+ void *pProcessLocalData
+ );
+
+CObjectType CorUnix::otFileMapping(
+ otiFileMapping,
+ FileMappingCleanupRoutine,
+ FileMappingInitializationRoutine,
+ sizeof(CFileMappingImmutableData),
+ sizeof(CFileMappingProcessLocalData),
+ 0,
+ PAGE_READWRITE | PAGE_READONLY | PAGE_WRITECOPY,
+ CObjectType::SecuritySupported,
+ CObjectType::SecurityInfoNotPersisted,
+ CObjectType::UnnamedObject,
+ CObjectType::LocalDuplicationOnly,
+ CObjectType::UnwaitableObject,
+ CObjectType::SignalingNotApplicable,
+ CObjectType::ThreadReleaseNotApplicable,
+ CObjectType::OwnershipNotApplicable
+ );
+
+CAllowedObjectTypes aotFileMapping(otiFileMapping);
+
+void
+FileMappingCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup,
+ bool fShutdown,
+ bool fCleanupSharedState
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CFileMappingImmutableData *pImmutableData = NULL;
+ CFileMappingProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ bool fDataChanged = FALSE;
+
+ if (TRUE == fCleanupSharedState)
+ {
+ //
+ // If we created a temporary file to back this mapping we need
+ // to unlink it now
+ //
+
+ palError = pObjectToCleanup->GetImmutableData(
+ reinterpret_cast<void**>(&pImmutableData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to obtain immutable data for object to be reclaimed");
+ return;
+ }
+
+ if (pImmutableData->bPALCreatedTempFile)
+ {
+ unlink(pImmutableData->szFileName);
+ }
+ }
+
+ if (FALSE == fShutdown)
+ {
+ //
+ // We only need to close the object's descriptor if we're not
+ // shutting down
+ //
+
+ palError = pObjectToCleanup->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to obtain process local data for object to be reclaimed");
+ return;
+ }
+
+ if (-1 != pLocalData->UnixFd)
+ {
+ close(pLocalData->UnixFd);
+ pLocalData->UnixFd = -1;
+ fDataChanged = TRUE;
+ }
+
+ pLocalDataLock->ReleaseLock(pThread, fDataChanged);
+ }
+
+ //
+ // Why don't we need to deal with any views that may have been created
+ // from this mapping? If the process is shutting down then there's nothing
+ // that we need to take care of, as the OS will remove the underlying
+ // mappings when the process goes away. If we're not shutting down then
+ // there's no way for a view to exist against this mapping, since each
+ // view holds a reference against the mapping object.
+ //
+}
+
+PAL_ERROR
+FileMappingInitializationRoutine(
+ CPalThread *pThread,
+ CObjectType *pObjectType,
+ void *pvImmutableData,
+ void *pvSharedData,
+ void *pvProcessLocalData
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ CFileMappingImmutableData *pImmutableData =
+ reinterpret_cast<CFileMappingImmutableData *>(pvImmutableData);
+ CFileMappingProcessLocalData *pProcessLocalData =
+ reinterpret_cast<CFileMappingProcessLocalData *>(pvProcessLocalData);
+
+ pProcessLocalData->UnixFd = InternalOpen(
+ pImmutableData->szFileName,
+ MAPProtectionToFileOpenFlags(pImmutableData->flProtect)
+ );
+
+ if (-1 == pProcessLocalData->UnixFd)
+ {
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitFileMappingInitializationRoutine;
+ }
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ struct stat st;
+
+ if (0 == fstat(pProcessLocalData->UnixFd, &st))
+ {
+ pProcessLocalData->MappedFileDevNum = st.st_dev;
+ pProcessLocalData->MappedFileInodeNum = st.st_ino;
+ }
+ else
+ {
+ ERROR("Couldn't get inode info for fd=%d to be stored in mapping object\n", pProcessLocalData->UnixFd);
+ }
+#endif
+
+ExitFileMappingInitializationRoutine:
+
+ return palError;
+}
+
+/*++
+Function:
+ CreateFileMappingA
+
+Note:
+ File mapping are used to do inter-process communication.
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+CreateFileMappingA(
+ IN HANDLE hFile,
+ IN LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
+ IN DWORD flProtect,
+ IN DWORD dwMaximumSizeHigh,
+ IN DWORD dwMaximumSizeLow,
+ IN LPCSTR lpName)
+{
+ HANDLE hFileMapping = NULL;
+ CPalThread *pThread = NULL;
+ PAL_ERROR palError = NO_ERROR;
+
+ PERF_ENTRY(CreateFileMappingA);
+ ENTRY("CreateFileMappingA(hFile=%p, lpAttributes=%p, flProtect=%#x, "
+ "dwMaxSizeH=%d, dwMaxSizeL=%d, lpName=%p (%s))\n",
+ hFile, lpFileMappingAttributes, flProtect,
+ dwMaximumSizeHigh, dwMaximumSizeLow,
+ lpName?lpName:"NULL",
+ lpName?lpName:"NULL");
+
+ pThread = InternalGetCurrentThread();
+
+ if (lpName != nullptr)
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ }
+ else
+ {
+ palError = InternalCreateFileMapping(
+ pThread,
+ hFile,
+ lpFileMappingAttributes,
+ flProtect,
+ dwMaximumSizeHigh,
+ dwMaximumSizeLow,
+ NULL,
+ &hFileMapping
+ );
+ }
+
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pThread->SetLastError(palError);
+
+ LOGEXIT( "CreateFileMappingA returns HANDLE %p. \n", hFileMapping );
+ PERF_EXIT(CreateFileMappingA);
+ return hFileMapping;
+}
+
+/*++
+Function:
+ CreateFileMappingW
+
+Note:
+ File mapping are used to do inter-process communication.
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+CreateFileMappingW(
+ IN HANDLE hFile,
+ IN LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
+ IN DWORD flProtect,
+ IN DWORD dwMaximumSizeHigh,
+ IN DWORD dwMaximumSizeLow,
+ IN LPCWSTR lpName)
+{
+ HANDLE hFileMapping = NULL;
+ CPalThread *pThread = NULL;
+ PAL_ERROR palError = NO_ERROR;
+
+ PERF_ENTRY(CreateFileMappingW);
+ ENTRY("CreateFileMappingW(hFile=%p, lpAttributes=%p, flProtect=%#x, "
+ "dwMaxSizeH=%u, dwMaxSizeL=%u, lpName=%p (%S))\n",
+ hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh,
+ dwMaximumSizeLow, lpName?lpName:W16_NULLSTRING, lpName?lpName:W16_NULLSTRING);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalCreateFileMapping(
+ pThread,
+ hFile,
+ lpFileMappingAttributes,
+ flProtect,
+ dwMaximumSizeHigh,
+ dwMaximumSizeLow,
+ lpName,
+ &hFileMapping
+ );
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pThread->SetLastError(palError);
+
+ LOGEXIT( "CreateFileMappingW returning %p .\n", hFileMapping );
+ PERF_EXIT(CreateFileMappingW);
+ return hFileMapping;
+}
+
+PAL_ERROR
+CorUnix::InternalCreateFileMapping(
+ CPalThread *pThread,
+ HANDLE hFile,
+ LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
+ DWORD flProtect,
+ DWORD dwMaximumSizeHigh,
+ DWORD dwMaximumSizeLow,
+ LPCWSTR lpName,
+ HANDLE *phMapping
+ )
+{
+ CObjectAttributes objectAttributes(lpName, lpFileMappingAttributes);
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pMapping = NULL;
+ IPalObject *pRegisteredMapping = NULL;
+ CFileMappingProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ CFileMappingImmutableData *pImmutableData = NULL;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pFileLocalData = NULL;
+ IDataLock *pFileLocalDataLock = NULL;
+
+ struct stat UnixFileInformation;
+ INT UnixFd = -1;
+ BOOL bPALCreatedTempFile = FALSE;
+ UINT nFileSize = 0;
+
+ //
+ // Validate parameters
+ //
+
+ if (lpName != nullptr)
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (0 != dwMaximumSizeHigh)
+ {
+ ASSERT("dwMaximumSizeHigh is always 0.\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (PAGE_READWRITE != flProtect
+ && PAGE_READONLY != flProtect
+ && PAGE_WRITECOPY != flProtect)
+ {
+ ASSERT( "invalid flProtect %#x, acceptable values are PAGE_READONLY "
+ "(%#x), PAGE_READWRITE (%#x) and PAGE_WRITECOPY (%#x).\n",
+ flProtect, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY );
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (hFile == INVALID_HANDLE_VALUE && 0 == dwMaximumSizeLow)
+ {
+ ERROR( "If hFile is INVALID_HANDLE_VALUE, then you must specify a size.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (hFile != INVALID_HANDLE_VALUE && NULL != lpName)
+ {
+ ASSERT( "If hFile is not -1, then lpName must be NULL.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otFileMapping,
+ &objectAttributes,
+ &pMapping
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalCreateFileMapping;
+ }
+
+ palError = pMapping->GetImmutableData(reinterpret_cast<void**>(&pImmutableData));
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (hFile == INVALID_HANDLE_VALUE
+#ifndef CORECLR
+ && NULL == lpName
+#endif // !CORECLR
+ )
+ {
+ //
+ // Note: this path is what prevents us supporting the
+ // duplication of file mapping objects across processes, since
+ // there is no backing file that the other process can open. We can
+ // avoid this restriction by always using a temp backing file for
+ // anonymous mappings.
+ //
+
+ /* Anonymous mapped files. */
+ if (strcpy_s(pImmutableData->szFileName, sizeof(pImmutableData->szFileName), "/dev/zero") != SAFECRT_SUCCESS)
+ {
+ ERROR( "strcpy_s failed!\n" );
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitInternalCreateFileMapping;
+ }
+
+#if HAVE_MMAP_DEV_ZERO
+
+ UnixFd = InternalOpen(pImmutableData->szFileName, O_RDWR);
+ if ( -1 == UnixFd )
+ {
+ ERROR( "Unable to open the file.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitInternalCreateFileMapping;
+ }
+
+#else //!HAVE_MMAP_DEV_ZERO
+
+ UnixFd = -1; /* will pass MAP_ANON to mmap() instead */
+
+#endif //!HAVE_MMAP_DEV_ZERO
+
+ }
+ else
+ {
+ if ( hFile != INVALID_HANDLE_VALUE )
+ {
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to obtain file data.\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pFileLocalDataLock,
+ reinterpret_cast<void**>(&pFileLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalCreateFileMapping;
+ }
+
+ /* We need to check to ensure flProtect jives with
+ the permission on the file handle */
+ if (!MAPIsRequestPermissible(flProtect, pFileLocalData))
+ {
+ ERROR("File handle does not have the correct "
+ "permissions to create mapping\n" );
+ palError = ERROR_ACCESS_DENIED;
+ if (NULL != pFileLocalDataLock)
+ {
+ pFileLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+ goto ExitInternalCreateFileMapping;
+ }
+
+ //
+ // TODO: technically, the file mapping object should hold
+ // a reference to the passed in file object. This implementation
+ // only keeps the underlying native file structure (i.e., what
+ // the duplicated descriptors point to) open. There may be a risk
+ // here pertaining to the file lock information that the PAL must
+ // maintain (e.g,. if the passed in handle is closed immediately
+ // after the file mapping is opened then the lock information will
+ // be released, since we're not doing anything to keep it alive
+ // here).
+ //
+ // Having a direct reference to the underlying file object adds
+ // some complication, especially in cross-process cases. We may
+ // want to consider adding a reference to the PAL's file lock
+ // information, though...
+ //
+
+ UnixFd = dup(pFileLocalData->unix_fd);
+ if (-1 == UnixFd)
+ {
+ ERROR( "Unable to duplicate the Unix file descriptor!\n" );
+ palError = ERROR_INTERNAL_ERROR;
+ if (NULL != pFileLocalDataLock)
+ {
+ pFileLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (strcpy_s(pImmutableData->szFileName, sizeof(pImmutableData->szFileName), pFileLocalData->unix_filename) != SAFECRT_SUCCESS)
+ {
+ ERROR( "strcpy_s failed!\n" );
+ palError = ERROR_INTERNAL_ERROR;
+ if (NULL != pFileLocalDataLock)
+ {
+ pFileLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (NULL != pFileLocalDataLock)
+ {
+ pFileLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+ }
+ else
+ {
+#ifndef CORECLR
+ TRACE( "INVALID_HANDLE_VALUE was the hFile, time to try to create a "
+ "temporary file" );
+
+ /* Create a temporary file on the filesystem in order to be
+ shared across processes. */
+ palError = MAPCreateTempFile(pThread, &UnixFd, pImmutableData->szFileName);
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to create the temporary file.\n");
+ goto ExitInternalCreateFileMapping;
+ }
+ bPALCreatedTempFile = TRUE;
+#else // !CORECLR
+ ASSERT("should not get here\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitInternalCreateFileMapping;
+#endif // !CORECLR
+ }
+
+ if (-1 == fstat(UnixFd, &UnixFileInformation))
+ {
+ ASSERT("fstat() failed for this reason %s.\n", strerror(errno));
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if ( 0 == UnixFileInformation.st_size &&
+ 0 == dwMaximumSizeHigh && 0 == dwMaximumSizeLow )
+ {
+ ERROR( "The file cannot be a zero length file.\n" );
+ palError = ERROR_FILE_INVALID;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if ( INVALID_HANDLE_VALUE != hFile &&
+ dwMaximumSizeLow > (DWORD) UnixFileInformation.st_size &&
+ ( PAGE_READONLY == flProtect || PAGE_WRITECOPY == flProtect ) )
+ {
+ /* In this situation, Windows returns an error, because the
+ permissions requested do not allow growing the file */
+ ERROR( "The file cannot be grown do to the map's permissions.\n" );
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if ( (DWORD) UnixFileInformation.st_size < dwMaximumSizeLow )
+ {
+ TRACE( "Growing the size of file on disk to match requested size.\n" );
+
+ /* Need to grow the file on disk to match size. */
+ palError = MAPGrowLocalFile(UnixFd, dwMaximumSizeLow);
+ if (NO_ERROR != palError)
+ {
+ ERROR( "Unable to grow the file on disk.\n" );
+ goto ExitInternalCreateFileMapping;
+ }
+ }
+ }
+
+ nFileSize = ( 0 == dwMaximumSizeLow && 0 == dwMaximumSizeHigh ) ?
+ UnixFileInformation.st_size : dwMaximumSizeLow;
+
+ pImmutableData->MaxSize = nFileSize;
+ pImmutableData->flProtect = flProtect;
+ pImmutableData->bPALCreatedTempFile = bPALCreatedTempFile;
+ pImmutableData->dwDesiredAccessWhenOpened = MAPConvertProtectToAccess(flProtect);
+
+
+ //
+ // The local data isn't grabbed / modified until here so that we don't
+ // need to worry ourselves with locking issues with the passed in
+ // file handle -- all operations concerning the file handle are completed
+ // before we deal with the lock for the new object.
+ //
+
+ palError = pMapping->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalCreateFileMapping;
+ }
+
+ pLocalData->UnixFd = UnixFd;
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ if (-1 == UnixFd)
+ {
+ pLocalData->MappedFileDevNum = (dev_t)-1; /* there is no standard NO_DEV */
+ pLocalData->MappedFileInodeNum = NO_INO;
+ }
+ else
+ {
+ struct stat st;
+
+ if (0 == fstat(UnixFd, &st))
+ {
+ pLocalData->MappedFileDevNum = st.st_dev;
+ pLocalData->MappedFileInodeNum = st.st_ino;
+ }
+ else
+ {
+ ERROR("Couldn't get inode info for fd=%d to be stored in mapping object\n", UnixFd);
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitInternalCreateFileMapping;
+ }
+ }
+#endif
+
+ pLocalDataLock->ReleaseLock(pThread, TRUE);
+ pLocalDataLock = NULL;
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pMapping,
+ &aotFileMapping,
+ flProtect, // TODO: is flProtect really an access right?
+ phMapping,
+ &pRegisteredMapping
+ );
+
+ //
+ // pMapping is invalidated by the call to RegisterObject, so NULL it
+ // out here to ensure that we don't try to release a reference on
+ // it down the line. This also ensures that we won't attempt to release
+ // any data associated with the mapping object here, as if any cleanup is
+ // necessary due to a failure in RegisterObject (which includes another
+ // object by the same name already existing) the cleanup will take place
+ // when that routine releases the reference to pMapping.
+ //
+
+ pMapping = NULL;
+
+ExitInternalCreateFileMapping:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(
+ pThread,
+ TRUE
+ );
+ }
+
+ if (NULL != pMapping)
+ {
+ pMapping->ReleaseReference(pThread);
+
+ if (bPALCreatedTempFile)
+ {
+ unlink(pImmutableData->szFileName);
+ }
+
+ if (-1 != UnixFd)
+ {
+ close(UnixFd);
+ }
+ }
+
+ if (NULL != pRegisteredMapping)
+ {
+ pRegisteredMapping->ReleaseReference(pThread);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ OpenFileMappingA
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+OpenFileMappingA(
+ IN DWORD dwDesiredAccess,
+ IN BOOL bInheritHandle,
+ IN LPCSTR lpName)
+{
+ HANDLE hFileMapping = NULL;
+ CPalThread *pThread = NULL;
+ PAL_ERROR palError = NO_ERROR;
+
+ PERF_ENTRY(OpenFileMappingA);
+ ENTRY("OpenFileMappingA(dwDesiredAccess=%u, bInheritHandle=%d, lpName=%p (%s)\n",
+ dwDesiredAccess, bInheritHandle, lpName?lpName:"NULL", lpName?lpName:"NULL");
+
+ pThread = InternalGetCurrentThread();
+
+ if (lpName == nullptr)
+ {
+ ERROR("name is NULL\n");
+ palError = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ }
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+ LOGEXIT( "OpenFileMappingA returning %p\n", hFileMapping );
+ PERF_EXIT(OpenFileMappingA);
+ return hFileMapping;
+}
+
+
+/*++
+Function:
+ OpenFileMappingW
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+OpenFileMappingW(
+ IN DWORD dwDesiredAccess,
+ IN BOOL bInheritHandle,
+ IN LPCWSTR lpName)
+{
+ HANDLE hFileMapping = NULL;
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread = NULL;
+
+ PERF_ENTRY(OpenFileMappingW);
+ ENTRY("OpenFileMappingW(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%S)\n",
+ dwDesiredAccess, bInheritHandle, lpName?lpName:W16_NULLSTRING, lpName?lpName:W16_NULLSTRING);
+
+ pThread = InternalGetCurrentThread();
+
+ /* validate parameters */
+ if (lpName == nullptr)
+ {
+ ERROR("name is NULL\n");
+ palError = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ }
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+ LOGEXIT("OpenFileMappingW returning %p.\n", hFileMapping);
+ PERF_EXIT(OpenFileMappingW);
+ return hFileMapping;
+}
+
+PAL_ERROR
+CorUnix::InternalOpenFileMapping(
+ CPalThread *pThread,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ LPCWSTR lpName,
+ HANDLE *phMapping
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileMapping = NULL;
+ CPalString sObjectName(lpName);
+
+ if ( MAPContainsInvalidFlags( dwDesiredAccess ) )
+ {
+ ASSERT( "dwDesiredAccess can be one or more of FILE_MAP_READ, "
+ "FILE_MAP_WRITE, FILE_MAP_COPY or FILE_MAP_ALL_ACCESS.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalOpenFileMapping;
+ }
+
+ palError = g_pObjectManager->LocateObject(
+ pThread,
+ &sObjectName,
+ &aotFileMapping,
+ &pFileMapping
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalOpenFileMapping;
+ }
+
+ palError = g_pObjectManager->ObtainHandleForObject(
+ pThread,
+ pFileMapping,
+ dwDesiredAccess,
+ bInheritHandle,
+ NULL,
+ phMapping
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalOpenFileMapping;
+ }
+
+ExitInternalOpenFileMapping:
+
+ if (NULL != pFileMapping)
+ {
+ pFileMapping->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ MapViewOfFile
+
+ Limitations: 1) Currently file mappings are supported only at file
+ offset 0.
+ 2) Some platforms (specifically HP-UX) do not support
+ multiple simultaneous shared mapping of the same file
+ region in the same process. On these platforms, in case
+ we are asked for a new view completely contained in an
+ existing one, we return an address within the existing
+ mapping. In case the new requested view is overlapping
+ with the existing one, but not contained in it, the
+ mapping is impossible, and MapViewOfFile will fail.
+ Since currently the mappings are supported only at file
+ offset 0, MapViewOfFile will succeed if the new view
+ is equal or smaller of the existing one, and the address
+ returned will be the same address of the existing
+ mapping.
+ Since the underlying mapping is always the same, all
+ the shared views of the same file region will share the
+ same protection, i.e. they will have the largest
+ protection requested. If any mapping asked for a
+ read-write access, all the read-only mappings of the
+ same region will silently get a read-write access to
+ it.
+
+See MSDN doc.
+--*/
+LPVOID
+PALAPI
+MapViewOfFile(
+ IN HANDLE hFileMappingObject,
+ IN DWORD dwDesiredAccess,
+ IN DWORD dwFileOffsetHigh,
+ IN DWORD dwFileOffsetLow,
+ IN SIZE_T dwNumberOfBytesToMap)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread = NULL;
+ LPVOID pvMappedBaseAddress = NULL;
+
+ PERF_ENTRY(MapViewOfFile);
+ ENTRY("MapViewOfFile(hFileMapping=%p, dwDesiredAccess=%u, "
+ "dwFileOffsetH=%u, dwFileOffsetL=%u, dwNumberOfBytes=%u)\n",
+ hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
+ dwFileOffsetLow, dwNumberOfBytesToMap);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalMapViewOfFile(
+ pThread,
+ hFileMappingObject,
+ dwDesiredAccess,
+ dwFileOffsetHigh,
+ dwFileOffsetLow,
+ dwNumberOfBytesToMap,
+ &pvMappedBaseAddress
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT( "MapViewOfFile returning %p.\n", pvMappedBaseAddress );
+ PERF_EXIT(MapViewOfFile);
+ return pvMappedBaseAddress;
+}
+
+LPVOID
+PALAPI
+MapViewOfFileEx(
+ IN HANDLE hFileMappingObject,
+ IN DWORD dwDesiredAccess,
+ IN DWORD dwFileOffsetHigh,
+ IN DWORD dwFileOffsetLow,
+ IN SIZE_T dwNumberOfBytesToMap,
+ IN LPVOID lpBaseAddress)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread = NULL;
+ LPVOID pvMappedBaseAddress = NULL;
+
+ PERF_ENTRY(MapViewOfFileEx);
+ ENTRY("MapViewOfFileEx(hFileMapping=%p, dwDesiredAccess=%u, "
+ "dwFileOffsetH=%u, dwFileOffsetL=%u, dwNumberOfBytes=%u, lpBaseAddress=%p)\n",
+ hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
+ dwFileOffsetLow, dwNumberOfBytesToMap, lpBaseAddress);
+
+ pThread = InternalGetCurrentThread();
+
+ if (lpBaseAddress == NULL)
+ {
+ palError = InternalMapViewOfFile(
+ pThread,
+ hFileMappingObject,
+ dwDesiredAccess,
+ dwFileOffsetHigh,
+ dwFileOffsetLow,
+ dwNumberOfBytesToMap,
+ &pvMappedBaseAddress
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+ }
+ else
+ {
+ // TODO: Figure out if we can support mapping at a specific address on Linux.
+ pThread->SetLastError(ERROR_INVALID_PARAMETER);
+ }
+
+ LOGEXIT( "MapViewOfFileEx returning %p.\n", pvMappedBaseAddress );
+ PERF_EXIT(MapViewOfFileEx);
+ return pvMappedBaseAddress;
+}
+
+/*++
+Function:
+ FlushViewOfFile
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+FlushViewOfFile(
+ IN LPVOID lpBaseAddress,
+ IN SIZE_T dwNumberOfBytesToFlush)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread = NULL;
+ PMAPPED_VIEW_LIST pView = NULL;
+ BOOL fResult = TRUE;
+
+ PERF_ENTRY(FlushViewOfFile);
+ ENTRY("FlushViewOfFile(lpBaseAddress=%p, dwNumberOfBytesToFlush=%u)\n",
+ lpBaseAddress, dwNumberOfBytesToFlush);
+
+ pThread = InternalGetCurrentThread();
+
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+
+ pView = MAPGetViewForAddress(lpBaseAddress);
+ if (NULL == pView)
+ {
+ ERROR("lpBaseAddress has to be the address returned by MapViewOfFile[Ex]");
+ palError = ERROR_INVALID_HANDLE;
+ goto Exit;
+ }
+
+ if (dwNumberOfBytesToFlush == 0)
+ {
+ dwNumberOfBytesToFlush = pView->NumberOfBytesToMap;
+ }
+
+ // <ROTORTODO>we should only use MS_SYNC if the file has been opened
+ // with FILE_FLAG_WRITE_THROUGH
+ if (msync(lpBaseAddress, dwNumberOfBytesToFlush, MS_SYNC) == -1)
+ {
+ if (errno == EINVAL)
+ {
+ WARN("msync failed; %s\n", strerror(errno));
+ palError = ERROR_INVALID_PARAMETER;
+ }
+ else if (errno == EIO)
+ {
+ WARN("msync failed; %s\n", strerror(errno));
+ palError = ERROR_WRITE_FAULT;
+ }
+ else
+ {
+ ERROR("msync failed; %s\n", strerror(errno));
+ palError = ERROR_INTERNAL_ERROR;
+ }
+ }
+
+Exit:
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+ if (NO_ERROR != palError)
+ {
+ fResult = FALSE;
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("FlushViewOfFile returning %d.\n", fResult);
+ PERF_EXIT(FlushViewOfFile);
+ return fResult;
+}
+
+
+/*++
+Function:
+ UnmapViewOfFile
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+UnmapViewOfFile(
+ IN LPCVOID lpBaseAddress)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+
+ PERF_ENTRY(UnmapViewOfFile);
+ ENTRY("UnmapViewOfFile(lpBaseAddress=%p)\n", lpBaseAddress);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalUnmapViewOfFile(pThread, lpBaseAddress);
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT( "UnmapViewOfFile returning %s.\n", (NO_ERROR == palError) ? "TRUE" : "FALSE" );
+ PERF_EXIT(UnmapViewOfFile);
+ return (NO_ERROR == palError);
+}
+
+PAL_ERROR
+CorUnix::InternalMapViewOfFile(
+ CPalThread *pThread,
+ HANDLE hFileMappingObject,
+ DWORD dwDesiredAccess,
+ DWORD dwFileOffsetHigh,
+ DWORD dwFileOffsetLow,
+ SIZE_T dwNumberOfBytesToMap,
+ LPVOID *ppvBaseAddress
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pMappingObject = NULL;
+ CFileMappingImmutableData *pImmutableData = NULL;
+ CFileMappingProcessLocalData *pProcessLocalData = NULL;
+ IDataLock *pProcessLocalDataLock = NULL;
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ PMAPPED_VIEW_LIST pReusedMapping = NULL;
+#endif
+ LPVOID pvBaseAddress = NULL;
+
+ /* Sanity checks */
+ if ( MAPContainsInvalidFlags( dwDesiredAccess ) )
+ {
+ ASSERT( "dwDesiredAccess can be one of FILE_MAP_WRITE, FILE_MAP_READ,"
+ " FILE_MAP_COPY or FILE_MAP_ALL_ACCESS.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalMapViewOfFileExit;
+ }
+
+ if ( 0 != dwFileOffsetHigh || 0 != dwFileOffsetLow )
+ {
+ ASSERT( "dwFileOffsetHigh and dwFileOffsetLow are always 0.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalMapViewOfFileExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFileMappingObject,
+ &aotFileMapping,
+ dwDesiredAccess,
+ &pMappingObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR( "Unable to reference handle %p.\n",hFileMappingObject );
+ goto InternalMapViewOfFileExit;
+ }
+
+ palError = pMappingObject->GetImmutableData(
+ reinterpret_cast<void**>(&pImmutableData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR( "Unable to obtain object immutable data");
+ goto InternalMapViewOfFileExit;
+ }
+
+ palError = pMappingObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pProcessLocalDataLock,
+ reinterpret_cast<void**>(&pProcessLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR( "Unable to obtain object process local data");
+ goto InternalMapViewOfFileExit;
+ }
+
+ /* If dwNumberOfBytesToMap is 0, we need to map the entire file.
+ * mmap doesn't do the same thing as Windows in that case, though,
+ * so we use the file size instead. */
+ if (0 == dwNumberOfBytesToMap)
+ {
+ dwNumberOfBytesToMap = pImmutableData->MaxSize;
+ }
+
+ palError = MAPDesiredAccessAllowed(
+ pImmutableData->flProtect,
+ dwDesiredAccess,
+ pImmutableData->dwDesiredAccessWhenOpened
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalMapViewOfFileExit;
+ }
+
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+
+ if (FILE_MAP_COPY == dwDesiredAccess)
+ {
+ int flags = MAP_PRIVATE;
+
+#if !HAVE_MMAP_DEV_ZERO
+ if (pProcessLocalData->UnixFd == -1)
+ {
+ flags |= MAP_ANON;
+ }
+#endif
+ pvBaseAddress = mmap(
+ NULL,
+ dwNumberOfBytesToMap,
+ PROT_READ|PROT_WRITE,
+ flags,
+ pProcessLocalData->UnixFd,
+ 0
+ );
+ }
+ else
+ {
+ INT prot = MAPFileMapToMmapFlags(dwDesiredAccess);
+ if (prot != -1)
+ {
+ int flags = MAP_SHARED;
+
+#if !HAVE_MMAP_DEV_ZERO
+ if (pProcessLocalData->UnixFd == -1)
+ {
+ flags |= MAP_ANON;
+ }
+#endif
+
+ pvBaseAddress = mmap(
+ NULL,
+ dwNumberOfBytesToMap,
+ prot,
+ flags,
+ pProcessLocalData->UnixFd,
+ 0
+ );
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ if ((MAP_FAILED == pvBaseAddress) && (ENOMEM == errno))
+ {
+ /* Search in list of MAPPED_MEMORY_INFO for a shared mapping
+ with the same inode number
+ */
+ TRACE("Mmap() failed with errno=ENOMEM probably for multiple mapping "
+ "limitation. Searching for a replacement among existing mappings\n");
+
+ pReusedMapping = FindSharedMappingReplacement(
+ pThread,
+ pProcessLocalData->MappedFileDevNum,
+ pProcessLocalData->MappedFileInodeNum,
+ dwNumberOfBytesToMap,
+ 0
+ );
+
+ if (pReusedMapping)
+ {
+ int ret;
+
+ TRACE("Mapping @ %p {sz=%d offs=%d} fully "
+ "contains the requested one {sz=%d offs=%d}: reusing it\n",
+ pReusedMapping->pNMHolder->address,
+ (int)pReusedMapping->pNMHolder->size,
+ (int)pReusedMapping->pNMHolder->offset,
+ dwNumberOfBytesToMap, 0);
+
+ /* Let's check the mapping's current protection */
+ ret = mprotect(pReusedMapping->pNMHolder->address,
+ pReusedMapping->pNMHolder->size,
+ prot | PROT_CHECK);
+ if (0 != ret)
+ {
+ /* We need to raise the protection to the desired
+ one. That will give write access to any read-only
+ mapping sharing this native mapping, but there is
+ no way around this problem on systems that do not
+ allow more than one mapping per file region, per
+ process */
+ TRACE("Raising protections on mapping @ %p to 0x%x\n",
+ pReusedMapping->pNMHolder->address, prot);
+ ret = mprotect(pReusedMapping->pNMHolder->address,
+ pReusedMapping->pNMHolder->size,
+ prot);
+ }
+
+ if (ret != 0)
+ {
+ ERROR( "Failed setting protections on reused mapping\n");
+
+ NativeMapHolderRelease(pThread, pReusedMapping->pNMHolder);
+ free(pReusedMapping);
+ pReusedMapping = NULL;
+ }
+ }
+ }
+#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ }
+ else
+ {
+ ASSERT( "MapFileMapToMmapFlags failed!\n" );
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalMapViewOfFileLeaveCriticalSection;
+ }
+ }
+
+ if (MAP_FAILED == pvBaseAddress
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ && (pReusedMapping == NULL)
+#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ )
+ {
+ ERROR( "mmap failed with code %s.\n", strerror( errno ) );
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto InternalMapViewOfFileLeaveCriticalSection;
+
+ }
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ if (pReusedMapping != NULL)
+ {
+ //
+ // Add a reference to the file mapping object the reused mapping
+ // points to (note that it may be different than the object this
+ // call was actually made against) and add the view to the global
+ // list. All other initialization took place in
+ // FindSharedMappingReplacement
+ //
+
+ pvBaseAddress = pReusedMapping->lpAddress;
+ pReusedMapping->pFileMapping->AddReference();
+ InsertTailList(&MappedViewList, &pReusedMapping->Link);
+ }
+ else
+#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ {
+ //
+ // Allocate and fill out a new view structure, and add it to
+ // the global list.
+ //
+
+ PMAPPED_VIEW_LIST pNewView = (PMAPPED_VIEW_LIST)InternalMalloc(sizeof(*pNewView));
+ if (NULL != pNewView)
+ {
+ pNewView->lpAddress = pvBaseAddress;
+ pNewView->NumberOfBytesToMap = dwNumberOfBytesToMap;
+ pNewView->dwDesiredAccess = dwDesiredAccess;
+ pNewView->pFileMapping = pMappingObject;
+ pNewView->pFileMapping->AddReference();
+ pNewView->lpPEBaseAddress = 0;
+ InsertTailList(&MappedViewList, &pNewView->Link);
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ pNewView->MappedFileDevNum = pProcessLocalData->MappedFileDevNum;
+ pNewView->MappedFileInodeNum = pProcessLocalData->MappedFileInodeNum;
+
+ pNewView->pNMHolder = NewNativeMapHolder(
+ pThread,
+ pvBaseAddress,
+ dwNumberOfBytesToMap,
+ 0,
+ 1
+ );
+
+ if (NULL == pNewView->pNMHolder)
+ {
+ pNewView->pFileMapping->ReleaseReference(pThread);
+ RemoveEntryList(&pNewView->Link);
+ free(pNewView);
+ palError = ERROR_INTERNAL_ERROR;
+ }
+#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ }
+ else
+ {
+ palError = ERROR_INTERNAL_ERROR;
+ }
+
+ if (NO_ERROR != palError)
+ {
+ if (-1 == munmap(pvBaseAddress, dwNumberOfBytesToMap))
+ {
+ ERROR("Unable to unmap the file. Expect trouble.\n");
+ goto InternalMapViewOfFileLeaveCriticalSection;
+ }
+ }
+ }
+
+ if (NO_ERROR == palError)
+ {
+ TRACE( "Added %p to the list.\n", pvBaseAddress );
+ *ppvBaseAddress = pvBaseAddress;
+ }
+
+InternalMapViewOfFileLeaveCriticalSection:
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+InternalMapViewOfFileExit:
+
+ if (NULL != pProcessLocalDataLock)
+ {
+ pProcessLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pMappingObject)
+ {
+ pMappingObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+
+PAL_ERROR
+CorUnix::InternalUnmapViewOfFile(
+ CPalThread *pThread,
+ LPCVOID lpBaseAddress
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ PMAPPED_VIEW_LIST pView = NULL;
+ IPalObject *pMappingObject = NULL;
+
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+
+ pView = MAPGetViewForAddress(lpBaseAddress);
+ if (NULL == pView)
+ {
+ ERROR("lpBaseAddress has to be the address returned by MapViewOfFile[Ex]");
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalUnmapViewOfFileExit;
+ }
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ NativeMapHolderRelease(pThread, pView->pNMHolder);
+ pView->pNMHolder = NULL;
+#else
+ if (-1 == munmap((LPVOID)lpBaseAddress, pView->NumberOfBytesToMap))
+ {
+ ASSERT( "Unable to unmap the memory. Error=%s.\n",
+ strerror( errno ) );
+ palError = ERROR_INTERNAL_ERROR;
+
+ //
+ // Even if the unmap fails we want to continue removing the
+ // info for this view
+ //
+ }
+#endif
+
+ RemoveEntryList(&pView->Link);
+ pMappingObject = pView->pFileMapping;
+ free(pView);
+
+InternalUnmapViewOfFileExit:
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+ //
+ // We can't dereference the file mapping object until after
+ // we've released the mapping critical section, since it may
+ // start going down its cleanup path and we don't want to make
+ // any assumptions as to what locks that might grab...
+ //
+
+ if (NULL != pMappingObject)
+ {
+ pMappingObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function :
+ MAPInitialize
+
+ Initialize the critical sections.
+
+Return value:
+ TRUE if initialization succeeded
+ FALSE otherwise
+--*/
+BOOL
+MAPInitialize( void )
+{
+ TRACE( "Initialising the critical section.\n" );
+
+ InternalInitializeCriticalSection(&mapping_critsec);
+
+ InitializeListHead(&MappedViewList);
+
+ return TRUE;
+}
+
+/*++
+Function :
+ MAPCleanup
+
+ Deletes the critical sections. And all other necessary cleanup.
+
+Note:
+ This function is called after the handle manager is stopped. So
+ there shouldn't be any call that will cause an access to the handle
+ manager.
+
+--*/
+void MAPCleanup( void )
+{
+ TRACE( "Deleting the critical section.\n" );
+ InternalDeleteCriticalSection(&mapping_critsec);
+}
+
+/*++
+Function :
+ MAPGetViewForAddress
+
+ Returns the mapped view (if any) that is based at the passed in address.
+
+ Callers to this function must hold mapping_critsec
+--*/
+static PMAPPED_VIEW_LIST MAPGetViewForAddress( LPCVOID lpAddress )
+{
+ if ( NULL == lpAddress )
+ {
+ ERROR( "lpAddress cannot be NULL\n" );
+ return NULL;
+ }
+
+ for(LIST_ENTRY *pLink = MappedViewList.Flink;
+ pLink != &MappedViewList;
+ pLink = pLink->Flink)
+ {
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+ if (pView->lpAddress == lpAddress)
+ {
+ return pView;
+ }
+ }
+
+ WARN( "No match found.\n" );
+
+ return NULL;
+}
+
+/*++
+Function :
+
+ MAPDesiredAccessAllowed
+
+ Determines if desired access is allowed based on the protection state.
+
+ if dwDesiredAccess conflicts with flProtect then the error is
+ ERROR_INVALID_PARAMETER, if the dwDesiredAccess conflicts with
+ dwDesiredAccessWhenOpened, then the error code is ERROR_ACCESS_DENIED
+--*/
+static PAL_ERROR MAPDesiredAccessAllowed( DWORD flProtect,
+ DWORD dwUserDesiredAccess,
+ DWORD dwDesiredAccessWhenOpened )
+{
+ TRACE( "flProtect=%d, dwUserDesiredAccess=%d, dwDesiredAccessWhenOpened=%d\n",
+ flProtect, dwUserDesiredAccess, dwDesiredAccessWhenOpened );
+
+ /* check flProtect parameters*/
+ if ( FILE_MAP_READ!= dwUserDesiredAccess && PAGE_READONLY == flProtect )
+ {
+ ERROR( "map object is read-only, can't map a view with write access\n");
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if ( FILE_MAP_WRITE == dwUserDesiredAccess && PAGE_READWRITE != flProtect )
+ {
+ ERROR( "map object not open read-write, can't map a view with write "
+ "access.\n" );
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if ( FILE_MAP_COPY == dwUserDesiredAccess && PAGE_WRITECOPY != flProtect )
+ {
+ ERROR( "map object not open for copy-on-write, can't map copy-on-write "
+ "view.\n" );
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ /* Check to see we don't confict with the desired access we
+ opened the mapping object with. */
+ if ( ( dwUserDesiredAccess == FILE_MAP_READ ) &&
+ !( ( dwDesiredAccessWhenOpened == FILE_MAP_READ ) ||
+ ( dwDesiredAccessWhenOpened == FILE_MAP_ALL_ACCESS ) ) )
+ {
+ ERROR( "dwDesiredAccess conflict : read access requested, object not "
+ "opened with read access.\n" );
+ return ERROR_ACCESS_DENIED;
+ }
+ if ( ( dwUserDesiredAccess & FILE_MAP_WRITE ) &&
+ !( ( dwDesiredAccessWhenOpened == FILE_MAP_WRITE ) ||
+ ( dwDesiredAccessWhenOpened == FILE_MAP_ALL_ACCESS ) ) )
+ {
+ ERROR( "dwDesiredAccess conflict : write access requested, object not "
+ "opened with write access.\n" );
+ return ERROR_ACCESS_DENIED;
+ }
+ if ( ( dwUserDesiredAccess == FILE_MAP_COPY ) &&
+ !( dwDesiredAccessWhenOpened == FILE_MAP_COPY ) )
+ {
+ ERROR( "dwDesiredAccess conflict : copy-on-write access requested, "
+ "object not opened with copy-on-write access.\n" );
+ return ERROR_ACCESS_DENIED;
+ }
+
+ return NO_ERROR;
+}
+
+/*++
+Function :
+ MAPConvertProtectToAccess
+
+ Converts the PAGE_READONLY type flags to FILE_MAP_READ flags.
+
+--*/
+static DWORD MAPConvertProtectToAccess( DWORD flProtect )
+{
+ if ( PAGE_READONLY == flProtect )
+ {
+ return FILE_MAP_READ;
+ }
+ if ( PAGE_READWRITE == flProtect )
+ {
+ return FILE_MAP_ALL_ACCESS;
+ }
+ if ( PAGE_WRITECOPY == flProtect )
+ {
+ return FILE_MAP_COPY;
+ }
+
+ ASSERT( "Unknown flag for flProtect. This line "
+ "should not have been executed.\n " );
+ return (DWORD) -1;
+}
+
+/*++
+Function :
+ MAPConvertAccessToProtect
+
+ Converts the FILE_MAP_READ type flags to PAGE_READONLY flags.
+ Currently, this function only deals with the access flags recognized as valid
+ by MAPContainsInvalidFlags().
+
+--*/
+static DWORD MAPConvertAccessToProtect(DWORD flAccess)
+{
+ if (flAccess == FILE_MAP_ALL_ACCESS)
+ {
+ return PAGE_READWRITE;
+ }
+ else if ((flAccess == FILE_MAP_COPY) || (flAccess == FILE_MAP_WRITE))
+ {
+ return PAGE_WRITECOPY;
+ }
+ else if (flAccess == FILE_MAP_READ)
+ {
+ return PAGE_READONLY;
+ }
+ else if (flAccess == 0)
+ {
+ return PAGE_NOACCESS;
+ }
+
+ ASSERT("Unknown flag for flAccess.\n");
+ return (DWORD) -1;
+}
+
+/*++
+Function :
+ MAPFileMapToMmapFlags
+
+ Converts the mapping flags to unix protection flags.
+--*/
+static INT MAPFileMapToMmapFlags( DWORD flags )
+{
+ if ( FILE_MAP_READ == flags )
+ {
+ TRACE( "FILE_MAP_READ\n" );
+ return PROT_READ;
+ }
+ else if ( FILE_MAP_WRITE == flags )
+ {
+ TRACE( "FILE_MAP_WRITE\n" );
+ /* The limitation of x86 archetecture
+ means you cant have writable but not readable
+ page. In Windows maps of FILE_MAP_WRITE can still be
+ read from. */
+ return PROT_WRITE | PROT_READ;
+ }
+ else if ( (FILE_MAP_READ|FILE_MAP_WRITE) == flags )
+ {
+ TRACE( "FILE_MAP_READ|FILE_MAP_WRITE\n" );
+ return PROT_READ | PROT_WRITE;
+ }
+ else if( FILE_MAP_COPY == flags)
+ {
+ TRACE( "FILE_MAP_COPY\n");
+ return PROT_READ | PROT_WRITE;
+ }
+
+ ASSERT( "Unknown flag. This line should not have been executed.\n" );
+ return -1;
+}
+
+/*++
+Function :
+ MAPMmapProtToAccessFlags
+
+ Converts unix protection flags to file access flags.
+ We ignore PROT_EXEC.
+--*/
+static DWORD MAPMmapProtToAccessFlags( int prot )
+{
+ DWORD flAccess = 0; // default: no access
+
+ if (PROT_NONE == prot)
+ {
+ flAccess = 0;
+ }
+ else if ( ((PROT_READ | PROT_WRITE) & prot) == (PROT_READ | PROT_WRITE) )
+ {
+ flAccess = FILE_MAP_ALL_ACCESS;
+ }
+ else if ( (PROT_WRITE & prot) == PROT_WRITE )
+ {
+ flAccess = FILE_MAP_WRITE;
+ }
+ else if ( (PROT_READ & prot) == PROT_READ )
+ {
+ flAccess = FILE_MAP_READ;
+ }
+ else
+ {
+ ASSERT( "Unknown Unix protection flag\n" );
+ }
+
+ return flAccess;
+}
+
+/*++
+Function :
+
+ MAPGrowLocalFile
+
+ Grows the file on disk to match the specified size.
+
+--*/
+static PAL_ERROR MAPGrowLocalFile( INT UnixFD, UINT NewSize )
+{
+ PAL_ERROR palError = NO_ERROR;
+ INT TruncateRetVal = -1;
+ struct stat FileInfo;
+ TRACE( "Entered MapGrowLocalFile (UnixFD=%d,NewSize%d)\n", UnixFD, NewSize );
+
+ //
+ // TODO: can we add configure flags to model the behavior of ftruncate
+ // among our various target platforms? How much would that actually gain
+ // us?
+ //
+
+ /* ftruncate is a standard function, but the behavior of enlarging files is
+ non-standard. So I will try to enlarge a file, and if that fails try the
+ less efficent way.*/
+ TruncateRetVal = ftruncate( UnixFD, NewSize );
+ fstat( UnixFD, &FileInfo );
+
+ if ( TruncateRetVal != 0 || FileInfo.st_size != (int) NewSize )
+ {
+ INT OrigSize;
+ CONST UINT BUFFER_SIZE = 128;
+ BYTE buf[BUFFER_SIZE];
+ UINT x = 0;
+ UINT CurrentPosition = 0;
+
+ TRACE( "Trying the less efficent way.\n" );
+
+ CurrentPosition = lseek( UnixFD, 0, SEEK_CUR );
+ OrigSize = lseek( UnixFD, 0, SEEK_END );
+ if ( OrigSize == -1 )
+ {
+ ERROR( "Unable to locate the EOF marker. Reason=%s\n",
+ strerror( errno ) );
+ palError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+
+ if (NewSize <= (UINT) OrigSize)
+ {
+ return TRUE;
+ }
+
+ memset( buf, 0, BUFFER_SIZE );
+
+ for ( x = 0; x < NewSize - OrigSize - BUFFER_SIZE; x += BUFFER_SIZE )
+ {
+ if ( write( UnixFD, (LPVOID)buf, BUFFER_SIZE ) == -1 )
+ {
+ ERROR( "Unable to grow the file. Reason=%s\n", strerror( errno ) );
+ if((errno == ENOSPC) || (errno == EDQUOT))
+ {
+ palError = ERROR_DISK_FULL;
+ }
+ else
+ {
+ palError = ERROR_INTERNAL_ERROR;
+ }
+ goto done;
+ }
+ }
+ /* Catch any left overs. */
+ if ( x != NewSize )
+ {
+ if ( write( UnixFD, (LPVOID)buf, NewSize - OrigSize - x) == -1 )
+ {
+ ERROR( "Unable to grow the file. Reason=%s\n", strerror( errno ) );
+ if((errno == ENOSPC) || (errno == EDQUOT))
+ {
+ palError = ERROR_DISK_FULL;
+ }
+ else
+ {
+ palError = ERROR_INTERNAL_ERROR;
+ }
+ goto done;
+ }
+ }
+
+ /* restore the file pointer position */
+ lseek( UnixFD, CurrentPosition, SEEK_SET );
+ }
+
+done:
+ return palError;
+}
+
+/*++
+Function :
+ MAPContainsInvalidFlags
+
+ Checks that only valid flags are in the parameter.
+
+--*/
+static BOOL MAPContainsInvalidFlags( DWORD flags )
+{
+
+ if ( (flags == FILE_MAP_READ) ||
+ (flags == FILE_MAP_WRITE) ||
+ (flags == FILE_MAP_ALL_ACCESS) ||
+ (flags == FILE_MAP_COPY) )
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+/*++
+Function :
+ MAPProtectionToFileOpenFlags
+
+ Converts the PAGE_* flags to the O_* flags.
+
+ Returns the file open flags.
+--*/
+static INT MAPProtectionToFileOpenFlags( DWORD flProtect )
+{
+ INT retVal = 0;
+ switch(flProtect)
+ {
+ case PAGE_READONLY:
+ retVal = O_RDONLY;
+ break;
+ case PAGE_READWRITE:
+ retVal = O_RDWR;
+ break;
+ case PAGE_WRITECOPY:
+ retVal = O_RDONLY;
+ break;
+ default:
+ ASSERT("unexpected flProtect value %#x\n", flProtect);
+ retVal = 0;
+ break;
+ }
+ return retVal;
+}
+
+/*++
+Function :
+
+ MAPIsRequestPermissible
+
+ DWORD flProtect - The requested file mapping protection .
+ file * pFileStruct - The file structure containing all the information.
+
+--*/
+static BOOL MAPIsRequestPermissible( DWORD flProtect, CFileProcessLocalData * pFileLocalData )
+{
+ if ( ( (flProtect == PAGE_READONLY || flProtect == PAGE_WRITECOPY) &&
+ (pFileLocalData->open_flags_deviceaccessonly == TRUE ||
+ pFileLocalData->open_flags & O_WRONLY) )
+ )
+ {
+ /*
+ * PAGE_READONLY or PAGE_WRITECOPY access to a file must at least be
+ * readable. Contrary to what MSDN says, PAGE_WRITECOPY
+ * only needs to be readable.
+ */
+ return FALSE;
+ }
+ else if ( flProtect == PAGE_READWRITE && !(pFileLocalData->open_flags & O_RDWR) )
+ {
+ /*
+ * PAGE_READWRITE access to a file needs to be readable and writable
+ */
+ return FALSE;
+ }
+ else
+ {
+ /* Action is permissible */
+ return TRUE;
+ }
+}
+
+// returns TRUE if we have information about the specified address
+BOOL MAPGetRegionInfo(LPVOID lpAddress,
+ PMEMORY_BASIC_INFORMATION lpBuffer)
+{
+ BOOL fFound = FALSE;
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+
+ for(LIST_ENTRY *pLink = MappedViewList.Flink;
+ pLink != &MappedViewList;
+ pLink = pLink->Flink)
+ {
+ UINT MappedSize;
+ VOID * real_map_addr;
+ SIZE_T real_map_sz;
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ real_map_addr = pView->pNMHolder->address;
+ real_map_sz = pView->pNMHolder->size;
+#else
+ real_map_addr = pView->lpAddress;
+ real_map_sz = pView->NumberOfBytesToMap;
+#endif
+
+ MappedSize = ((real_map_sz-1) & ~VIRTUAL_PAGE_MASK) + VIRTUAL_PAGE_SIZE;
+ if ( real_map_addr <= lpAddress &&
+ (VOID *)((UINT_PTR)real_map_addr+MappedSize) > lpAddress )
+ {
+ if (lpBuffer)
+ {
+ SIZE_T regionSize = MappedSize + (UINT_PTR) real_map_addr -
+ ((UINT_PTR) lpAddress & ~VIRTUAL_PAGE_MASK);
+
+ lpBuffer->BaseAddress = lpAddress;
+ lpBuffer->AllocationProtect = 0;
+ lpBuffer->RegionSize = regionSize;
+ lpBuffer->State = MEM_COMMIT;
+ lpBuffer->Protect = MAPConvertAccessToProtect(pView->dwDesiredAccess);
+ lpBuffer->Type = MEM_MAPPED;
+ }
+
+ fFound = TRUE;
+ break;
+ }
+ }
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+ return fFound;
+}
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+
+//
+// Callers of FindSharedMappingReplacement must hold mapping_critsec
+//
+
+static PMAPPED_VIEW_LIST FindSharedMappingReplacement(
+ CPalThread *pThread,
+ dev_t deviceNum,
+ ino_t inodeNum,
+ SIZE_T size,
+ SIZE_T offset)
+{
+ PMAPPED_VIEW_LIST pNewView = NULL;
+
+ if (size == 0)
+ {
+ ERROR("Mapping size cannot be NULL\n");
+ return NULL;
+ }
+
+ for (LIST_ENTRY *pLink = MappedViewList.Flink;
+ pLink != &MappedViewList;
+ pLink = pLink->Flink)
+ {
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+ if (pView->MappedFileDevNum != deviceNum
+ || pView->MappedFileInodeNum != inodeNum
+ || pView->dwDesiredAccess == FILE_MAP_COPY)
+ {
+ continue;
+ }
+
+ //
+ // This is a shared mapping for the same indoe / device. Now, check
+ // to see if it overlaps with the range for the new view
+ //
+
+ SIZE_T real_map_offs = pView->pNMHolder->offset;
+ SIZE_T real_map_sz = pView->pNMHolder->size;
+
+ if (real_map_offs <= offset
+ && real_map_offs+real_map_sz >= offset)
+ {
+ //
+ // The views overlap. Even if this view is not reusable for the
+ // new once the search is over, as on
+ // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS systems there
+ // cannot be shared mappings of two overlapping regions of the
+ // same file, in the same process. Therefore, whether this view
+ // is reusable or not we cannot mmap the requested region of
+ // the specified file.
+ //
+
+ if (real_map_offs+real_map_sz >= offset+size)
+ {
+ /* The new desired mapping is fully contained in the
+ one just found: we can reuse this one */
+
+ pNewView = (PMAPPED_VIEW_LIST)InternalMalloc(sizeof(MAPPED_VIEW_LIST));
+ if (pNewView)
+ {
+ memcpy(pNewView, pView, sizeof(*pNewView));
+ NativeMapHolderAddRef(pNewView->pNMHolder);
+ pNewView->lpAddress = (VOID*)((CHAR*)pNewView->pNMHolder->address +
+ offset - pNewView->pNMHolder->offset);
+ pNewView->NumberOfBytesToMap = size;
+ }
+ else
+ {
+ ERROR("No memory for new MAPPED_VIEW_LIST node\n");
+ }
+ }
+
+ break;
+ }
+ }
+
+ TRACE ("FindSharedMappingReplacement returning %p\n", pNewView);
+ return pNewView;
+}
+
+static NativeMapHolder * NewNativeMapHolder(CPalThread *pThread, LPVOID address, SIZE_T size,
+ SIZE_T offset, long init_ref_count)
+{
+ NativeMapHolder * pThisMapHolder;
+
+ if (init_ref_count < 0)
+ {
+ ASSERT("Negative initial reference count for new map holder\n");
+ return NULL;
+ }
+
+ pThisMapHolder =
+ (NativeMapHolder *)InternalMalloc(sizeof(NativeMapHolder));
+
+ if (pThisMapHolder)
+ {
+ pThisMapHolder->ref_count = init_ref_count;
+ pThisMapHolder->address = address;
+ pThisMapHolder->size = size;
+ pThisMapHolder->offset = offset;
+ }
+
+ return pThisMapHolder;
+}
+
+static LONG NativeMapHolderAddRef(NativeMapHolder * thisNMH)
+{
+ LONG ret = InterlockedIncrement(&thisNMH->ref_count);
+ return ret;
+}
+
+static LONG NativeMapHolderRelease(CPalThread *pThread, NativeMapHolder * thisNMH)
+{
+ LONG ret = InterlockedDecrement(&thisNMH->ref_count);
+ if (ret == 0)
+ {
+ if (-1 == munmap(thisNMH->address, thisNMH->size))
+ {
+ ASSERT( "Unable to unmap memory. Error=%s.\n",
+ strerror( errno ) );
+ }
+ else
+ {
+ TRACE( "Successfully unmapped %p (size=%lu)\n",
+ thisNMH->address, (unsigned long)thisNMH->size);
+ }
+ free (thisNMH);
+ }
+ else if (ret < 0)
+ {
+ ASSERT( "Negative reference count for map holder %p"
+ " {address=%p, size=%lu}\n", thisNMH->address,
+ (unsigned long)thisNMH->size);
+ }
+
+ return ret;
+}
+
+#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+
+// Record a mapping in the MappedViewList list.
+// This call assumes the mapping_critsec has already been taken.
+static PAL_ERROR
+MAPRecordMapping(
+ IPalObject *pMappingObject,
+ void *pPEBaseAddress,
+ void *addr,
+ size_t len,
+ int prot
+ )
+{
+ if (pPEBaseAddress == NULL)
+ {
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ PAL_ERROR palError = NO_ERROR;
+ PMAPPED_VIEW_LIST pNewView;
+ pNewView = (PMAPPED_VIEW_LIST)InternalMalloc(sizeof(*pNewView));
+ if (NULL != pNewView)
+ {
+ pNewView->lpAddress = addr;
+ pNewView->NumberOfBytesToMap = len;
+ pNewView->dwDesiredAccess = MAPMmapProtToAccessFlags(prot);
+ pMappingObject->AddReference();
+ pNewView->pFileMapping = pMappingObject;
+ pNewView->lpPEBaseAddress = pPEBaseAddress;
+ InsertTailList(&MappedViewList, &pNewView->Link);
+
+ TRACE_(LOADER)("Added address %p, size 0x%x, to the mapped file list.\n", addr, len);
+ }
+ else
+ {
+ palError = ERROR_INTERNAL_ERROR;
+ }
+
+ return palError;
+}
+
+// Do the actual mmap() call, and record the mapping in the MappedViewList list.
+// This call assumes the mapping_critsec has already been taken.
+static PAL_ERROR
+MAPmmapAndRecord(
+ IPalObject *pMappingObject,
+ void *pPEBaseAddress,
+ void *addr,
+ size_t len,
+ int prot,
+ int flags,
+ int fd,
+ off_t offset,
+ LPVOID *ppvBaseAddress
+ )
+{
+ _ASSERTE(pPEBaseAddress != NULL);
+
+ PAL_ERROR palError = NO_ERROR;
+ LPVOID pvBaseAddress = NULL;
+
+ pvBaseAddress = mmap(addr, len, prot, flags, fd, offset);
+ if (MAP_FAILED == pvBaseAddress)
+ {
+ ERROR_(LOADER)( "mmap failed with code %d: %s.\n", errno, strerror( errno ) );
+ palError = FILEGetLastErrorFromErrno();
+ }
+ else
+ {
+ palError = MAPRecordMapping(pMappingObject, pPEBaseAddress, pvBaseAddress, len, prot);
+ if (NO_ERROR != palError)
+ {
+ if (-1 == munmap(pvBaseAddress, len))
+ {
+ ERROR_(LOADER)("Unable to unmap the file. Expect trouble.\n");
+ }
+ }
+ else
+ {
+ *ppvBaseAddress = pvBaseAddress;
+ }
+ }
+
+ return palError;
+}
+
+/*++
+ MAPMapPEFile -
+
+ Map a PE format file into memory like Windows LoadLibrary() would do.
+ Doesn't apply base relocations if the function is relocated.
+
+Parameters:
+ IN hFile - file to map
+
+Return value:
+ non-NULL - the base address of the mapped image
+ NULL - error, with last error set.
+--*/
+
+void * MAPMapPEFile(HANDLE hFile)
+{
+ PAL_ERROR palError = 0;
+ IPalObject *pFileObject = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ CPalThread *pThread = InternalGetCurrentThread();
+ void * loadedBase = NULL;
+ IMAGE_DOS_HEADER * loadedHeader = NULL;
+ void * retval;
+#if _DEBUG
+ bool forceRelocs = false;
+ char* envVar;
+#endif
+
+ ENTRY("MAPMapPEFile (hFile=%p)\n", hFile);
+
+ //Step 0: Verify values, find internal pal data structures.
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR_(LOADER)( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto done;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "ReferenceObjectByHandle failed\n" );
+ goto done;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "GetProcessLocalData failed\n" );
+ goto done;
+ }
+
+ int fd;
+ fd = pLocalData->unix_fd;
+ //Step 1: Read the PE headers and reserve enough space for the whole image somewhere.
+ IMAGE_DOS_HEADER dosHeader;
+ IMAGE_NT_HEADERS ntHeader;
+ errno = 0;
+ if (0 != lseek(fd, 0, SEEK_SET))
+ {
+ palError = FILEGetLastErrorFromErrno();
+ ERROR_(LOADER)( "lseek failed\n" );
+ goto done;
+ }
+ if (sizeof(dosHeader) != read(fd, &dosHeader, sizeof(dosHeader)))
+ {
+ palError = FILEGetLastErrorFromErrno();
+ ERROR_(LOADER)( "reading dos header failed\n" );
+ goto done;
+ }
+ if (dosHeader.e_lfanew != lseek(fd, dosHeader.e_lfanew, SEEK_SET))
+ {
+ palError = FILEGetLastErrorFromErrno();
+ goto done;
+ }
+ if (sizeof(ntHeader) != read(fd, &ntHeader, sizeof(ntHeader)))
+ {
+ palError = FILEGetLastErrorFromErrno();
+ goto done;
+ }
+
+ if ((VAL16(IMAGE_DOS_SIGNATURE) != VAL16(dosHeader.e_magic))
+ || (VAL32(IMAGE_NT_SIGNATURE) != VAL32(ntHeader.Signature))
+ || (VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC) != VAL16(ntHeader.OptionalHeader.Magic) ) )
+ {
+ ERROR_(LOADER)( "Magic number mismatch\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ //this code requires that the file alignment be the same as the page alignment
+ if (ntHeader.OptionalHeader.FileAlignment < VIRTUAL_PAGE_SIZE)
+ {
+ ERROR_(LOADER)( "Optional header file alignment is bad\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ //This doesn't read the entire NT header (the optional header technically has a variable length. But I
+ //don't need more directories.
+
+ //I now know how big the file is. Reserve enough address space for the whole thing. Try to get the
+ //preferred base. Create the intial mapping as "no access". We'll use that for the guard pages in the
+ //"holes" between sections.
+ SIZE_T preferredBase, virtualSize;
+ preferredBase = ntHeader.OptionalHeader.ImageBase;
+ virtualSize = ntHeader.OptionalHeader.SizeOfImage;
+
+ // Validate the image header
+ if ( (preferredBase == 0)
+ || (virtualSize == 0)
+ || (preferredBase + virtualSize < preferredBase) // Does the image overflow?
+ )
+ {
+ ERROR_(LOADER)( "image is corrupt\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+#if _DEBUG
+ envVar = EnvironGetenv("PAL_ForceRelocs");
+ if (envVar)
+ {
+ if (strlen(envVar) > 0)
+ {
+ forceRelocs = true;
+ TRACE_(LOADER)("Forcing rebase of image\n");
+ }
+
+ free(envVar);
+ }
+
+ void * pForceRelocBase;
+ pForceRelocBase = NULL;
+ if (forceRelocs)
+ {
+ //if we're forcing relocs, create an anonymous mapping at the preferred base. Only create the
+ //mapping if we can create it at the specified address.
+ pForceRelocBase = mmap( (void*)preferredBase, VIRTUAL_PAGE_SIZE, PROT_NONE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0 );
+ if (pForceRelocBase == MAP_FAILED)
+ {
+ TRACE_(LOADER)("Attempt to take preferred base of %p to force relocation failed\n", (void*)preferredBase);
+ forceRelocs = false;
+ }
+ else if ((void*)preferredBase != pForceRelocBase)
+ {
+ TRACE_(LOADER)("Attempt to take preferred base of %p to force relocation failed; actually got %p\n", (void*)preferredBase, pForceRelocBase);
+ }
+ }
+#endif // _DEBUG
+
+ // The first mmap mapping covers the entire file but just reserves space. Subsequent mappings cover
+ // individual parts of the file, and actually map pages in. Note that according to the mmap() man page, "A
+ // successful mmap deletes any previous mapping in the allocated address range." Also, "If a MAP_FIXED
+ // request is successful, the mapping established by mmap() replaces any previous mappings for the process' pages
+ // in the range from addr to addr + len." Thus, we will record a series of mappings here, one for the header
+ // and each of the sections, as well as all the space between them that we give PROT_NONE protections.
+
+ // We're going to start adding mappings to the mapping list, so take the critical section
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+
+#if !defined(_AMD64_)
+ loadedBase = mmap((void*)preferredBase, virtualSize, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
+#else // defined(_AMD64_)
+ // First try to reserve virtual memory using ExecutableAllcator. This allows all PE images to be
+ // near each other and close to the coreclr library which also allows the runtime to generate
+ // more efficient code (by avoiding usage of jump stubs).
+ loadedBase = ReserveMemoryFromExecutableAllocator(pThread, virtualSize);
+ if (loadedBase == NULL)
+ {
+ // MAC64 requires we pass MAP_SHARED (or MAP_PRIVATE) flags - otherwise, the call is failed.
+ // Refer to mmap documentation at http://www.manpagez.com/man/2/mmap/ for details.
+ loadedBase = mmap((void*)preferredBase, virtualSize, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
+ }
+#endif // !defined(_AMD64_)
+
+ if (MAP_FAILED == loadedBase)
+ {
+ ERROR_(LOADER)( "mmap failed with code %d: %s.\n", errno, strerror( errno ) );
+ palError = FILEGetLastErrorFromErrno();
+ loadedBase = NULL; // clear it so we don't try to use it during clean-up
+ goto doneReleaseMappingCriticalSection;
+ }
+
+ // All subsequent mappings of the PE file will be in the range [loadedBase, loadedBase + virtualSize)
+
+#if _DEBUG
+ if (forceRelocs)
+ {
+ _ASSERTE(((SIZE_T)loadedBase) != preferredBase);
+ munmap(pForceRelocBase, VIRTUAL_PAGE_SIZE); // now that we've forced relocation, let the original address mapping go
+ }
+ if (((SIZE_T)loadedBase) != preferredBase)
+ {
+ TRACE_(LOADER)("Image rebased from preferredBase of %p to loadedBase of %p\n", preferredBase, loadedBase);
+ }
+ else
+ {
+ TRACE_(LOADER)("Image loaded at preferred base %p\n", loadedBase);
+ }
+#endif // _DEBUG
+
+ //we have now reserved memory (potentially we got rebased). Walk the PE sections and map each part
+ //separately.
+
+ size_t headerSize;
+ headerSize = VIRTUAL_PAGE_SIZE; // if there are lots of sections, this could be wrong
+
+ //first, map the PE header to the first page in the image. Get pointers to the section headers
+ palError = MAPmmapAndRecord(pFileObject, loadedBase,
+ loadedBase, headerSize, PROT_READ, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, 0,
+ (void**)&loadedHeader);
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "mmap of PE header failed\n" );
+ goto doneReleaseMappingCriticalSection;
+ }
+
+ TRACE_(LOADER)("PE header loaded @ %p\n", loadedHeader);
+ _ASSERTE(loadedHeader == loadedBase); // we already preallocated the space, and we used MAP_FIXED, so we should have gotten this address
+ IMAGE_SECTION_HEADER * firstSection;
+ firstSection = (IMAGE_SECTION_HEADER*)(((char *)loadedHeader)
+ + loadedHeader->e_lfanew
+ + offsetof(IMAGE_NT_HEADERS, OptionalHeader)
+ + VAL16(ntHeader.FileHeader.SizeOfOptionalHeader));
+ unsigned numSections;
+ numSections = ntHeader.FileHeader.NumberOfSections;
+
+ // Validation
+ char* sectionHeaderEnd;
+ sectionHeaderEnd = (char*)firstSection + numSections * sizeof(IMAGE_SECTION_HEADER);
+ if ( ((void*)firstSection < loadedBase)
+ || ((char*)firstSection > sectionHeaderEnd)
+ || (sectionHeaderEnd > (char*)loadedBase + virtualSize)
+ )
+ {
+ ERROR_(LOADER)( "image is corrupt\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto doneReleaseMappingCriticalSection;
+ }
+
+ void* prevSectionBase;
+ prevSectionBase = loadedBase; // the first "section" for our purposes is the header
+ size_t prevSectionSizeInMemory;
+ prevSectionSizeInMemory = headerSize;
+ for (unsigned i = 0; i < numSections; ++i)
+ {
+ //for each section, map the section of the file to the correct virtual offset. Gather the
+ //protection bits from the PE file and convert them to the correct mmap PROT_* flags.
+ void * sectionData;
+ int prot = 0;
+ IMAGE_SECTION_HEADER &currentHeader = firstSection[i];
+
+ void* sectionBase = (char*)loadedBase + currentHeader.VirtualAddress;
+
+ // Validate the section header
+ if ( (sectionBase < loadedBase) // Did computing the section base overflow?
+ || ((char*)sectionBase + currentHeader.SizeOfRawData < (char*)sectionBase) // Does the section overflow?
+ || ((char*)sectionBase + currentHeader.SizeOfRawData > (char*)loadedBase + virtualSize) // Does the section extend past the end of the image as the header stated?
+ || ((char*)prevSectionBase + prevSectionSizeInMemory > sectionBase) // Does this section overlap the previous one?
+ )
+ {
+ ERROR_(LOADER)( "section %d is corrupt\n", i );
+ palError = ERROR_INVALID_PARAMETER;
+ goto doneReleaseMappingCriticalSection;
+ }
+ if (currentHeader.Misc.VirtualSize > currentHeader.SizeOfRawData)
+ {
+ ERROR_(LOADER)( "no support for zero-padded sections, section %d\n", i );
+ palError = ERROR_INVALID_PARAMETER;
+ goto doneReleaseMappingCriticalSection;
+ }
+
+ // Is there space between the previous section and this one? If so, add a PROT_NONE mapping to cover it.
+ if ((char*)prevSectionBase + prevSectionSizeInMemory < sectionBase)
+ {
+ char* gapBase = (char*)prevSectionBase + prevSectionSizeInMemory;
+ palError = MAPRecordMapping(pFileObject,
+ loadedBase,
+ (void*)gapBase,
+ (char*)sectionBase - gapBase,
+ PROT_NONE);
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "recording gap section before section %d failed\n", i );
+ goto doneReleaseMappingCriticalSection;
+ }
+ }
+
+ //Don't discard these sections. We need them to verify PE files
+ //if (currentHeader.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
+ // continue;
+ if (currentHeader.Characteristics & IMAGE_SCN_MEM_EXECUTE)
+ prot |= PROT_EXEC;
+ if (currentHeader.Characteristics & IMAGE_SCN_MEM_READ)
+ prot |= PROT_READ;
+ if (currentHeader.Characteristics & IMAGE_SCN_MEM_WRITE)
+ prot |= PROT_WRITE;
+
+ palError = MAPmmapAndRecord(pFileObject, loadedBase,
+ sectionBase,
+ currentHeader.SizeOfRawData,
+ prot,
+ MAP_FILE|MAP_PRIVATE|MAP_FIXED,
+ fd,
+ currentHeader.PointerToRawData,
+ &sectionData);
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "mmap of section %d failed\n", i );
+ goto doneReleaseMappingCriticalSection;
+ }
+
+#if _DEBUG
+ {
+ // Ensure null termination of section name (which is allowed to not be null terminated if exactly 8 characters long)
+ char sectionName[9];
+ sectionName[8] = '\0';
+ memcpy(sectionName, currentHeader.Name, sizeof(currentHeader.Name));
+ TRACE_(LOADER)("Section %d '%s' (header @ %p) loaded @ %p (RVA: 0x%x, SizeOfRawData: 0x%x, PointerToRawData: 0x%x)\n",
+ i, sectionName, &currentHeader, sectionData, currentHeader.VirtualAddress, currentHeader.SizeOfRawData, currentHeader.PointerToRawData);
+ }
+#endif // _DEBUG
+
+ prevSectionBase = sectionBase;
+ prevSectionSizeInMemory = (currentHeader.SizeOfRawData + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK; // round up to page boundary
+ }
+
+ // Is there space after the last section and before the end of the mapped image? If so, add a PROT_NONE mapping to cover it.
+ char* imageEnd;
+ imageEnd = (char*)loadedBase + virtualSize; // actually, points just after the mapped end
+ if ((char*)prevSectionBase + prevSectionSizeInMemory < imageEnd)
+ {
+ char* gapBase = (char*)prevSectionBase + prevSectionSizeInMemory;
+ palError = MAPRecordMapping(pFileObject,
+ loadedBase,
+ (void*)gapBase,
+ imageEnd - gapBase,
+ PROT_NONE);
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "recording end of image gap section failed\n" );
+ goto doneReleaseMappingCriticalSection;
+ }
+ }
+
+ palError = ERROR_SUCCESS;
+
+doneReleaseMappingCriticalSection:
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+done:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ if (palError == ERROR_SUCCESS)
+ {
+ retval = loadedBase;
+ LOGEXIT("MAPMapPEFile returns %p\n", retval);
+ }
+ else
+ {
+ retval = NULL;
+ LOGEXIT("MAPMapPEFile error: %d\n", palError);
+
+ // If we had an error, and had mapped anything, we need to unmap it
+ if (loadedBase != NULL)
+ {
+ MAPUnmapPEFile(loadedBase);
+ }
+ }
+ return retval;
+}
+
+/*++
+Function :
+ MAPUnmapPEFile - unmap a PE file, and remove it from the recorded list of PE files mapped
+
+ returns TRUE if successful, FALSE otherwise
+--*/
+BOOL MAPUnmapPEFile(LPCVOID lpAddress)
+{
+ TRACE_(LOADER)("MAPUnmapPEFile(lpAddress=%p)\n", lpAddress);
+
+ if ( NULL == lpAddress )
+ {
+ ERROR_(LOADER)( "lpAddress cannot be NULL\n" );
+ return FALSE;
+ }
+
+ BOOL retval = TRUE;
+ CPalThread * pThread = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+ PLIST_ENTRY pLink, pLinkNext, pLinkLocal = NULL;
+ unsigned nPESections = 0;
+
+ // Look through the entire MappedViewList for all mappings associated with the
+ // PE file with base address 'lpAddress'. We want to unmap all the memory
+ // and then release the file mapping object. Unfortunately, based on the comment
+ // in CorUnix::InternalUnmapViewOfFile(), we can't release the file mapping object
+ // while within the mapping critical section. So, we unlink all the elements from the
+ // main list while in the critical section, and then run through this local list
+ // doing the real work after releasing the main list critical section. The order
+ // of the unmapping doesn't matter, so we don't fully set all the list link pointers,
+ // only a minimal set.
+
+ for(pLink = MappedViewList.Flink;
+ pLink != &MappedViewList;
+ pLink = pLinkNext)
+ {
+ pLinkNext = pLink->Flink;
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+ if (pView->lpPEBaseAddress == lpAddress) // this entry is associated with the PE file
+ {
+ ++nPESections; // for debugging, check that we see at least one
+
+ RemoveEntryList(&pView->Link);
+ pView->Link.Flink = pLinkLocal; // the local list is singly-linked, NULL terminated
+ pLinkLocal = &pView->Link;
+ }
+ }
+
+#if _DEBUG
+ if (nPESections == 0)
+ {
+ ERROR_(LOADER)( "MAPUnmapPEFile called to unmap a file that was not in the PE file mapping list\n" );
+ }
+#endif // _DEBUG
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+ // Now, outside the critical section, do the actual unmapping work
+
+ for(pLink = pLinkLocal;
+ pLink != NULL;
+ pLink = pLinkNext)
+ {
+ pLinkNext = pLink->Flink;
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+ // remove pView mapping from the list
+ if (-1 == munmap(pView->lpAddress, pView->NumberOfBytesToMap))
+ {
+ // Emit an error message in a trace, but continue trying to do the rest
+ ERROR_(LOADER)("Unable to unmap the file. Expect trouble.\n");
+ retval = FALSE;
+ }
+
+ IPalObject* pFileObject = pView->pFileMapping;
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+ free(pView); // this leaves pLink dangling
+ }
+
+ TRACE_(LOADER)("MAPUnmapPEFile returning %d\n", retval);
+ return retval;
+}
diff --git a/src/pal/src/map/virtual.cpp b/src/pal/src/map/virtual.cpp
new file mode 100644
index 0000000000..4b5209642c
--- /dev/null
+++ b/src/pal/src/map/virtual.cpp
@@ -0,0 +1,2064 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ virtual.cpp
+
+Abstract:
+
+ Implementation of virtual memory management functions.
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/cs.hpp"
+#include "pal/malloc.hpp"
+#include "pal/file.hpp"
+#include "pal/seh.hpp"
+#include "pal/dbgmsg.h"
+#include "pal/virtual.h"
+#include "pal/map.h"
+#include "pal/init.h"
+#include "common.h"
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#if HAVE_VM_ALLOCATE
+#include <mach/vm_map.h>
+#include <mach/mach_init.h>
+#endif // HAVE_VM_ALLOCATE
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(VIRTUAL);
+
+CRITICAL_SECTION virtual_critsec;
+
+// The first node in our list of allocated blocks.
+static PCMI pVirtualMemory;
+
+/* We need MAP_ANON. However on some platforms like HP-UX, it is defined as MAP_ANONYMOUS */
+#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
+#define MAP_ANON MAP_ANONYMOUS
+#endif
+
+/*++
+Function:
+ ReserveVirtualMemory()
+
+ Helper function that is used by Virtual* APIs and ExecutableMemoryAllocator
+ to reserve virtual memory from the OS.
+
+--*/
+static LPVOID ReserveVirtualMemory(
+ IN CPalThread *pthrCurrent, /* Currently executing thread */
+ IN LPVOID lpAddress, /* Region to reserve or commit */
+ IN SIZE_T dwSize); /* Size of Region */
+
+
+// A memory allocator that allocates memory from a pre-reserved region
+// of virtual memory that is located near the CoreCLR library.
+static ExecutableMemoryAllocator g_executableMemoryAllocator;
+
+//
+//
+// Virtual Memory Logging
+//
+// We maintain a lightweight in-memory circular buffer recording virtual
+// memory operations so that we can better diagnose failures and crashes
+// caused by one of these operations mishandling memory in some way.
+//
+//
+namespace VirtualMemoryLogging
+{
+ // Specifies the operation being logged
+ enum class VirtualOperation
+ {
+ Allocate = 0x10,
+ Reserve = 0x20,
+ Commit = 0x30,
+ Decommit = 0x40,
+ Release = 0x50,
+ };
+
+ // Indicates that the attempted operation has failed
+ const DWORD FailedOperationMarker = 0x80000000;
+
+ // An entry in the in-memory log
+ struct LogRecord
+ {
+ LONG RecordId;
+ DWORD Operation;
+ LPVOID CurrentThread;
+ LPVOID RequestedAddress;
+ LPVOID ReturnedAddress;
+ SIZE_T Size;
+ DWORD AllocationType;
+ DWORD Protect;
+ };
+
+ // Maximum number of records in the in-memory log
+ const LONG MaxRecords = 128;
+
+ // Buffer used to store the logged data
+ volatile LogRecord logRecords[MaxRecords];
+
+ // Current record number. Use (recordNumber % MaxRecords) to determine
+ // the current position in the circular buffer.
+ volatile LONG recordNumber = 0;
+
+ // Record an entry in the in-memory log
+ void LogVaOperation(
+ IN VirtualOperation operation,
+ IN LPVOID requestedAddress,
+ IN SIZE_T size,
+ IN DWORD flAllocationType,
+ IN DWORD flProtect,
+ IN LPVOID returnedAddress,
+ IN BOOL result)
+ {
+ LONG i = InterlockedIncrement(&recordNumber) - 1;
+ LogRecord* curRec = (LogRecord*)&logRecords[i % MaxRecords];
+
+ curRec->RecordId = i;
+ curRec->CurrentThread = (LPVOID)pthread_self();
+ curRec->RequestedAddress = requestedAddress;
+ curRec->ReturnedAddress = returnedAddress;
+ curRec->Size = size;
+ curRec->AllocationType = flAllocationType;
+ curRec->Protect = flProtect;
+ curRec->Operation = static_cast<DWORD>(operation) | (result ? 0 : FailedOperationMarker);
+ }
+}
+
+/*++
+Function:
+ VIRTUALInitialize()
+
+ Initializes this section's critical section.
+
+Return value:
+ TRUE if initialization succeeded
+ FALSE otherwise.
+
+--*/
+extern "C"
+BOOL
+VIRTUALInitialize(bool initializeExecutableMemoryAllocator)
+{
+ TRACE("Initializing the Virtual Critical Sections. \n");
+
+ InternalInitializeCriticalSection(&virtual_critsec);
+
+ pVirtualMemory = NULL;
+
+ if (initializeExecutableMemoryAllocator)
+ {
+ g_executableMemoryAllocator.Initialize();
+ }
+
+ return TRUE;
+}
+
+/***
+ *
+ * VIRTUALCleanup()
+ * Deletes this section's critical section.
+ *
+ */
+extern "C"
+void VIRTUALCleanup()
+{
+ PCMI pEntry;
+ PCMI pTempEntry;
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+
+ InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
+
+ // Clean up the allocated memory.
+ pEntry = pVirtualMemory;
+ while ( pEntry )
+ {
+ WARN( "The memory at %d was not freed through a call to VirtualFree.\n",
+ pEntry->startBoundary );
+ free(pEntry->pAllocState);
+ free(pEntry->pProtectionState );
+ pTempEntry = pEntry;
+ pEntry = pEntry->pNext;
+ free(pTempEntry );
+ }
+ pVirtualMemory = NULL;
+
+ InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec);
+
+ TRACE( "Deleting the Virtual Critical Sections. \n" );
+ DeleteCriticalSection( &virtual_critsec );
+}
+
+/***
+ *
+ * VIRTUALContainsInvalidProtectionFlags()
+ * Returns TRUE if an invalid flag is specified. FALSE otherwise.
+ */
+static BOOL VIRTUALContainsInvalidProtectionFlags( IN DWORD flProtect )
+{
+ if ( ( flProtect & ~( PAGE_NOACCESS | PAGE_READONLY |
+ PAGE_READWRITE | PAGE_EXECUTE | PAGE_EXECUTE_READ |
+ PAGE_EXECUTE_READWRITE ) ) != 0 )
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+/****
+ *
+ * VIRTUALIsPageCommitted
+ *
+ * SIZE_T nBitToRetrieve - Which page to check.
+ *
+ * Returns TRUE if committed, FALSE otherwise.
+ *
+ */
+static BOOL VIRTUALIsPageCommitted( SIZE_T nBitToRetrieve, CONST PCMI pInformation )
+{
+ SIZE_T nByteOffset = 0;
+ UINT nBitOffset = 0;
+ UINT byteMask = 0;
+
+ if ( !pInformation )
+ {
+ ERROR( "pInformation was NULL!\n" );
+ return FALSE;
+ }
+
+ nByteOffset = nBitToRetrieve / CHAR_BIT;
+ nBitOffset = nBitToRetrieve % CHAR_BIT;
+
+ byteMask = 1 << nBitOffset;
+
+ if ( pInformation->pAllocState[ nByteOffset ] & byteMask )
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/*********
+ *
+ * VIRTUALGetAllocationType
+ *
+ * IN SIZE_T Index - The page within the range to retrieve
+ * the state for.
+ *
+ * IN pInformation - The virtual memory object.
+ *
+ */
+static INT VIRTUALGetAllocationType( SIZE_T Index, CONST PCMI pInformation )
+{
+ if ( VIRTUALIsPageCommitted( Index, pInformation ) )
+ {
+ return MEM_COMMIT;
+ }
+ else
+ {
+ return MEM_RESERVE;
+ }
+}
+
+/****
+ *
+ * VIRTUALSetPageBits
+ *
+ * IN UINT nStatus - Bit set / reset [0: reset, any other value: set].
+ * IN SIZE_T nStartingBit - The bit to set.
+ *
+ * IN SIZE_T nNumberOfBits - The range of bits to set.
+ * IN BYTE* pBitArray - A pointer the array to be manipulated.
+ *
+ * Returns TRUE on success, FALSE otherwise.
+ * Turn on/off memory status bits.
+ *
+ */
+static BOOL VIRTUALSetPageBits ( UINT nStatus, SIZE_T nStartingBit,
+ SIZE_T nNumberOfBits, BYTE * pBitArray )
+{
+ /* byte masks for optimized modification of partial bytes (changing less
+ than 8 bits in a single byte). note that bits are treated in little
+ endian order : value 1 is bit 0; value 128 is bit 7. in the binary
+ representations below, bit 0 is on the right */
+
+ /* start masks : for modifying bits >= n while preserving bits < n.
+ example : if nStartignBit%8 is 3, then bits 0, 1, 2 remain unchanged
+ while bits 3..7 are changed; startmasks[3] can be used for this. */
+ static const BYTE startmasks[8] = {
+ 0xff, /* start at 0 : 1111 1111 */
+ 0xfe, /* start at 1 : 1111 1110 */
+ 0xfc, /* start at 2 : 1111 1100 */
+ 0xf8, /* start at 3 : 1111 1000 */
+ 0xf0, /* start at 4 : 1111 0000 */
+ 0xe0, /* start at 5 : 1110 0000 */
+ 0xc0, /* start at 6 : 1100 0000 */
+ 0x80 /* start at 7 : 1000 0000 */
+ };
+
+ /* end masks : for modifying bits <= n while preserving bits > n.
+ example : if the last bit to change is 5, then bits 6 & 7 stay unchanged
+ while bits 1..5 are changed; endmasks[5] can be used for this. */
+ static const BYTE endmasks[8] = {
+ 0x01, /* end at 0 : 0000 0001 */
+ 0x03, /* end at 1 : 0000 0011 */
+ 0x07, /* end at 2 : 0000 0111 */
+ 0x0f, /* end at 3 : 0000 1111 */
+ 0x1f, /* end at 4 : 0001 1111 */
+ 0x3f, /* end at 5 : 0011 1111 */
+ 0x7f, /* end at 6 : 0111 1111 */
+ 0xff /* end at 7 : 1111 1111 */
+ };
+ /* last example : if only the middle of a byte must be changed, both start
+ and end masks can be combined (bitwise AND) to obtain the correct mask.
+ if we want to change bits 2 to 4 :
+ startmasks[2] : 0xfc 1111 1100 (change 2,3,4,5,6,7)
+ endmasks[4]: 0x1f 0001 1111 (change 0,1,2,3,4)
+ bitwise AND : 0x1c 0001 1100 (change 2,3,4)
+ */
+
+ BYTE byte_mask;
+ SIZE_T nLastBit;
+ SIZE_T nFirstByte;
+ SIZE_T nLastByte;
+ SIZE_T nFullBytes;
+
+ TRACE( "VIRTUALSetPageBits( nStatus = %d, nStartingBit = %d, "
+ "nNumberOfBits = %d, pBitArray = 0x%p )\n",
+ nStatus, nStartingBit, nNumberOfBits, pBitArray );
+
+ if ( 0 == nNumberOfBits )
+ {
+ ERROR( "nNumberOfBits was 0!\n" );
+ return FALSE;
+ }
+
+ nLastBit = nStartingBit+nNumberOfBits-1;
+ nFirstByte = nStartingBit / 8;
+ nLastByte = nLastBit / 8;
+
+ /* handle partial first byte (if any) */
+ if(0 != (nStartingBit % 8))
+ {
+ byte_mask = startmasks[nStartingBit % 8];
+
+ /* if 1st byte is the only changing byte, combine endmask to preserve
+ trailing bits (see 3rd example above) */
+ if( nLastByte == nFirstByte)
+ {
+ byte_mask &= endmasks[nLastBit % 8];
+ }
+
+ /* byte_mask contains 1 for bits to change, 0 for bits to leave alone */
+ if(0 == nStatus)
+ {
+ /* bits to change must be set to 0 : invert byte_mask (giving 0 for
+ bits to change), use bitwise AND */
+ pBitArray[nFirstByte] &= ~byte_mask;
+ }
+ else
+ {
+ /* bits to change must be set to 1 : use bitwise OR */
+ pBitArray[nFirstByte] |= byte_mask;
+ }
+
+ /* stop right away if only 1 byte is being modified */
+ if(nLastByte == nFirstByte)
+ {
+ return TRUE;
+ }
+
+ /* we're done with the 1st byte; skip over it */
+ nFirstByte++;
+ }
+
+ /* number of bytes to change, excluding the last byte (handled separately)*/
+ nFullBytes = nLastByte - nFirstByte;
+
+ if(0 != nFullBytes)
+ {
+ // Turn off/on dirty bits
+ memset( &(pBitArray[nFirstByte]), (0 == nStatus) ? 0 : 0xFF, nFullBytes );
+ }
+
+ /* handle last (possibly partial) byte */
+ byte_mask = endmasks[nLastBit % 8];
+
+ /* byte_mask contains 1 for bits to change, 0 for bits to leave alone */
+ if(0 == nStatus)
+ {
+ /* bits to change must be set to 0 : invert byte_mask (giving 0 for
+ bits to change), use bitwise AND */
+ pBitArray[nLastByte] &= ~byte_mask;
+ }
+ else
+ {
+ /* bits to change must be set to 1 : use bitwise OR */
+ pBitArray[nLastByte] |= byte_mask;
+ }
+
+ return TRUE;
+}
+
+/****
+ *
+ * VIRTUALSetAllocState
+ *
+ * IN UINT nAction - Which action to perform.
+ * IN SIZE_T nStartingBit - The bit to set.
+ *
+ * IN SIZE_T nNumberOfBits - The range of bits to set.
+ * IN PCMI pStateArray - A pointer the array to be manipulated.
+ *
+ * Returns TRUE on success, FALSE otherwise.
+ * Turn bit on to indicate committed, turn bit off to indicate reserved.
+ *
+ */
+static BOOL VIRTUALSetAllocState( UINT nAction, SIZE_T nStartingBit,
+ SIZE_T nNumberOfBits, CONST PCMI pInformation )
+{
+ TRACE( "VIRTUALSetAllocState( nAction = %d, nStartingBit = %d, "
+ "nNumberOfBits = %d, pStateArray = 0x%p )\n",
+ nAction, nStartingBit, nNumberOfBits, pInformation );
+
+ if ( !pInformation )
+ {
+ ERROR( "pInformation was invalid!\n" );
+ return FALSE;
+ }
+
+ return VIRTUALSetPageBits((MEM_COMMIT == nAction) ? 1 : 0, nStartingBit,
+ nNumberOfBits, pInformation->pAllocState);
+}
+
+/****
+ *
+ * VIRTUALFindRegionInformation( )
+ *
+ * IN UINT_PTR address - The address to look for.
+ *
+ * Returns the PCMI if found, NULL otherwise.
+ */
+static PCMI VIRTUALFindRegionInformation( IN UINT_PTR address )
+{
+ PCMI pEntry = NULL;
+
+ TRACE( "VIRTUALFindRegionInformation( %#x )\n", address );
+
+ pEntry = pVirtualMemory;
+
+ while( pEntry )
+ {
+ if ( pEntry->startBoundary > address )
+ {
+ /* Gone past the possible location in the list. */
+ pEntry = NULL;
+ break;
+ }
+ if ( pEntry->startBoundary + pEntry->memSize > address )
+ {
+ break;
+ }
+
+ pEntry = pEntry->pNext;
+ }
+ return pEntry;
+}
+
+/*++
+Function :
+
+ VIRTUALReleaseMemory
+
+ Removes a PCMI entry from the list.
+
+ Returns true on success. FALSE otherwise.
+--*/
+static BOOL VIRTUALReleaseMemory( PCMI pMemoryToBeReleased )
+{
+ BOOL bRetVal = TRUE;
+
+ if ( !pMemoryToBeReleased )
+ {
+ ASSERT( "Invalid pointer.\n" );
+ return FALSE;
+ }
+
+ if ( pMemoryToBeReleased == pVirtualMemory )
+ {
+ /* This is either the first entry, or the only entry. */
+ pVirtualMemory = pMemoryToBeReleased->pNext;
+ if ( pMemoryToBeReleased->pNext )
+ {
+ pMemoryToBeReleased->pNext->pPrevious = NULL;
+ }
+ }
+ else /* Could be anywhere in the list. */
+ {
+ /* Delete the entry from the linked list. */
+ if ( pMemoryToBeReleased->pPrevious )
+ {
+ pMemoryToBeReleased->pPrevious->pNext = pMemoryToBeReleased->pNext;
+ }
+
+ if ( pMemoryToBeReleased->pNext )
+ {
+ pMemoryToBeReleased->pNext->pPrevious = pMemoryToBeReleased->pPrevious;
+ }
+ }
+
+ free( pMemoryToBeReleased->pAllocState );
+ pMemoryToBeReleased->pAllocState = NULL;
+
+ free( pMemoryToBeReleased->pProtectionState );
+ pMemoryToBeReleased->pProtectionState = NULL;
+
+ free( pMemoryToBeReleased );
+ pMemoryToBeReleased = NULL;
+
+ return bRetVal;
+}
+
+/****
+ * VIRTUALConvertWinFlags() -
+ * Converts win32 protection flags to
+ * internal VIRTUAL flags.
+ *
+ */
+static BYTE VIRTUALConvertWinFlags( IN DWORD flProtect )
+{
+ BYTE MemAccessControl = 0;
+
+ switch ( flProtect & 0xff )
+ {
+ case PAGE_NOACCESS :
+ MemAccessControl = VIRTUAL_NOACCESS;
+ break;
+ case PAGE_READONLY :
+ MemAccessControl = VIRTUAL_READONLY;
+ break;
+ case PAGE_READWRITE :
+ MemAccessControl = VIRTUAL_READWRITE;
+ break;
+ case PAGE_EXECUTE :
+ MemAccessControl = VIRTUAL_EXECUTE;
+ break;
+ case PAGE_EXECUTE_READ :
+ MemAccessControl = VIRTUAL_EXECUTE_READ;
+ break;
+ case PAGE_EXECUTE_READWRITE:
+ MemAccessControl = VIRTUAL_EXECUTE_READWRITE;
+ break;
+
+ default :
+ MemAccessControl = 0;
+ ERROR( "Incorrect or no protection flags specified.\n" );
+ break;
+ }
+ return MemAccessControl;
+}
+
+/****
+ * VIRTUALConvertVirtualFlags() -
+ * Converts internal virtual protection
+ * flags to their win32 counterparts.
+ */
+static DWORD VIRTUALConvertVirtualFlags( IN BYTE VirtualProtect )
+{
+ DWORD MemAccessControl = 0;
+
+ if ( VirtualProtect == VIRTUAL_READONLY )
+ {
+ MemAccessControl = PAGE_READONLY;
+ }
+ else if ( VirtualProtect == VIRTUAL_READWRITE )
+ {
+ MemAccessControl = PAGE_READWRITE;
+ }
+ else if ( VirtualProtect == VIRTUAL_EXECUTE_READWRITE )
+ {
+ MemAccessControl = PAGE_EXECUTE_READWRITE;
+ }
+ else if ( VirtualProtect == VIRTUAL_EXECUTE_READ )
+ {
+ MemAccessControl = PAGE_EXECUTE_READ;
+ }
+ else if ( VirtualProtect == VIRTUAL_EXECUTE )
+ {
+ MemAccessControl = PAGE_EXECUTE;
+ }
+ else if ( VirtualProtect == VIRTUAL_NOACCESS )
+ {
+ MemAccessControl = PAGE_NOACCESS;
+ }
+
+ else
+ {
+ MemAccessControl = 0;
+ ERROR( "Incorrect or no protection flags specified.\n" );
+ }
+ return MemAccessControl;
+}
+
+/***
+ * Displays the linked list.
+ *
+ */
+#if defined _DEBUG
+static void VIRTUALDisplayList( void )
+{
+ if (!DBG_ENABLED(DLI_TRACE, defdbgchan))
+ return;
+
+ PCMI p;
+ SIZE_T count;
+ SIZE_T index;
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+
+ InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
+
+ p = pVirtualMemory;
+ count = 0;
+ while ( p ) {
+
+ DBGOUT( "Entry %d : \n", count );
+ DBGOUT( "\t startBoundary %#x \n", p->startBoundary );
+ DBGOUT( "\t memSize %d \n", p->memSize );
+
+ DBGOUT( "\t pAllocState " );
+ for ( index = 0; index < p->memSize / VIRTUAL_PAGE_SIZE; index++)
+ {
+ DBGOUT( "[%d] ", VIRTUALGetAllocationType( index, p ) );
+ }
+ DBGOUT( "\t pProtectionState " );
+ for ( index = 0; index < p->memSize / VIRTUAL_PAGE_SIZE; index++ )
+ {
+ DBGOUT( "[%d] ", (UINT)p->pProtectionState[ index ] );
+ }
+ DBGOUT( "\n" );
+ DBGOUT( "\t accessProtection %d \n", p->accessProtection );
+ DBGOUT( "\t allocationType %d \n", p->allocationType );
+ DBGOUT( "\t pNext %p \n", p->pNext );
+ DBGOUT( "\t pLast %p \n", p->pPrevious );
+
+ count++;
+ p = p->pNext;
+ }
+
+ InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec);
+}
+#endif
+
+#ifdef DEBUG
+void VerifyRightEntry(PCMI pEntry)
+{
+ volatile PCMI pRight = pEntry->pNext;
+ SIZE_T endAddress;
+ if (pRight != nullptr)
+ {
+ endAddress = ((SIZE_T)pEntry->startBoundary) + pEntry->memSize;
+ _ASSERTE(endAddress <= (SIZE_T)pRight->startBoundary);
+ }
+}
+
+void VerifyLeftEntry(PCMI pEntry)
+{
+ volatile PCMI pLeft = pEntry->pPrevious;
+ SIZE_T endAddress;
+ if (pLeft != NULL)
+ {
+ endAddress = ((SIZE_T)pLeft->startBoundary) + pLeft->memSize;
+ _ASSERTE(endAddress <= (SIZE_T)pEntry->startBoundary);
+ }
+}
+#endif // DEBUG
+
+/****
+ * VIRTUALStoreAllocationInfo()
+ *
+ * Stores the allocation information in the linked list.
+ * NOTE: The caller must own the critical section.
+ */
+static BOOL VIRTUALStoreAllocationInfo(
+ IN UINT_PTR startBoundary, /* Start of the region. */
+ IN SIZE_T memSize, /* Size of the region. */
+ IN DWORD flAllocationType, /* Allocation Types. */
+ IN DWORD flProtection ) /* Protections flags on the memory. */
+{
+ PCMI pNewEntry = nullptr;
+ PCMI pMemInfo = nullptr;
+ SIZE_T nBufferSize = 0;
+
+ if ((memSize & VIRTUAL_PAGE_MASK) != 0)
+ {
+ ERROR("The memory size was not a multiple of the page size. \n");
+ return FALSE;
+ }
+
+ if (!(pNewEntry = (PCMI)InternalMalloc(sizeof(*pNewEntry))))
+ {
+ ERROR( "Unable to allocate memory for the structure.\n");
+ return FALSE;
+ }
+
+ pNewEntry->startBoundary = startBoundary;
+ pNewEntry->memSize = memSize;
+ pNewEntry->allocationType = flAllocationType;
+ pNewEntry->accessProtection = flProtection;
+
+ nBufferSize = memSize / VIRTUAL_PAGE_SIZE / CHAR_BIT;
+ if ((memSize / VIRTUAL_PAGE_SIZE) % CHAR_BIT != 0)
+ {
+ nBufferSize++;
+ }
+
+ pNewEntry->pAllocState = (BYTE*)InternalMalloc(nBufferSize);
+ pNewEntry->pProtectionState = (BYTE*)InternalMalloc((memSize / VIRTUAL_PAGE_SIZE));
+
+ if (pNewEntry->pAllocState && pNewEntry->pProtectionState)
+ {
+ /* Set the intial allocation state, and initial allocation protection. */
+ VIRTUALSetAllocState(MEM_RESERVE, 0, nBufferSize * CHAR_BIT, pNewEntry);
+ memset(pNewEntry->pProtectionState,
+ VIRTUALConvertWinFlags(flProtection),
+ memSize / VIRTUAL_PAGE_SIZE);
+ }
+ else
+ {
+ ERROR( "Unable to allocate memory for the structure.\n");
+
+ if (pNewEntry->pProtectionState) free(pNewEntry->pProtectionState);
+ pNewEntry->pProtectionState = nullptr;
+
+ if (pNewEntry->pAllocState) free(pNewEntry->pAllocState);
+ pNewEntry->pAllocState = nullptr;
+
+ free(pNewEntry);
+ pNewEntry = nullptr;
+
+ return FALSE;
+ }
+
+ pMemInfo = pVirtualMemory;
+
+ if (pMemInfo && pMemInfo->startBoundary < startBoundary)
+ {
+ /* Look for the correct insert point */
+ TRACE("Looking for the correct insert location.\n");
+ while (pMemInfo->pNext && (pMemInfo->pNext->startBoundary < startBoundary))
+ {
+ pMemInfo = pMemInfo->pNext;
+ }
+
+ pNewEntry->pNext = pMemInfo->pNext;
+ pNewEntry->pPrevious = pMemInfo;
+
+ if (pNewEntry->pNext)
+ {
+ pNewEntry->pNext->pPrevious = pNewEntry;
+ }
+
+ pMemInfo->pNext = pNewEntry;
+ }
+ else
+ {
+ /* This is the first entry in the list. */
+ pNewEntry->pNext = pMemInfo;
+ pNewEntry->pPrevious = nullptr;
+
+ if (pNewEntry->pNext)
+ {
+ pNewEntry->pNext->pPrevious = pNewEntry;
+ }
+
+ pVirtualMemory = pNewEntry ;
+ }
+
+#ifdef DEBUG
+ VerifyRightEntry(pNewEntry);
+ VerifyLeftEntry(pNewEntry);
+#endif // DEBUG
+
+ return TRUE;
+}
+
+/******
+ *
+ * VIRTUALReserveMemory() - Helper function that actually reserves the memory.
+ *
+ * NOTE: I call SetLastError in here, because many different error states
+ * exists, and that would be very complicated to work around.
+ *
+ */
+static LPVOID VIRTUALReserveMemory(
+ IN CPalThread *pthrCurrent, /* Currently executing thread */
+ IN LPVOID lpAddress, /* Region to reserve or commit */
+ IN SIZE_T dwSize, /* Size of Region */
+ IN DWORD flAllocationType, /* Type of allocation */
+ IN DWORD flProtect) /* Type of access protection */
+{
+ LPVOID pRetVal = NULL;
+ UINT_PTR StartBoundary;
+ SIZE_T MemSize;
+
+ TRACE( "Reserving the memory now..\n");
+
+ // First, figure out where we're trying to reserve the memory and
+ // how much we need. On most systems, requests to mmap must be
+ // page-aligned and at multiples of the page size.
+ StartBoundary = (UINT_PTR)lpAddress & ~BOUNDARY_64K;
+ /* Add the sizes, and round down to the nearest page boundary. */
+ MemSize = ( ((UINT_PTR)lpAddress + dwSize + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK ) -
+ StartBoundary;
+
+ InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
+
+ // If this is a request for special executable (JIT'ed) memory then, first of all,
+ // try to get memory from the executable memory allocator to satisfy the request.
+ if (((flAllocationType & MEM_RESERVE_EXECUTABLE) != 0) && (lpAddress == NULL))
+ {
+ pRetVal = g_executableMemoryAllocator.AllocateMemory(MemSize);
+ }
+
+ if (pRetVal == NULL)
+ {
+ // Try to reserve memory from the OS
+ pRetVal = ReserveVirtualMemory(pthrCurrent, (LPVOID)StartBoundary, MemSize);
+ }
+
+ if (pRetVal != NULL)
+ {
+ if ( !lpAddress )
+ {
+ /* Compute the real values instead of the null values. */
+ StartBoundary = (UINT_PTR)pRetVal & ~VIRTUAL_PAGE_MASK;
+ MemSize = ( ((UINT_PTR)pRetVal + dwSize + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK ) -
+ StartBoundary;
+ }
+
+ if ( !VIRTUALStoreAllocationInfo( StartBoundary, MemSize,
+ flAllocationType, flProtect ) )
+ {
+ ASSERT( "Unable to store the structure in the list.\n");
+ pthrCurrent->SetLastError( ERROR_INTERNAL_ERROR );
+ munmap( pRetVal, MemSize );
+ pRetVal = NULL;
+ }
+ }
+
+ LogVaOperation(
+ VirtualMemoryLogging::VirtualOperation::Reserve,
+ lpAddress,
+ dwSize,
+ flAllocationType,
+ flProtect,
+ pRetVal,
+ pRetVal != NULL);
+
+ InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec);
+ return pRetVal;
+}
+
+/******
+ *
+ * ReserveVirtualMemory() - Helper function that is used by Virtual* APIs
+ * and ExecutableMemoryAllocator to reserve virtual memory from the OS.
+ *
+ */
+static LPVOID ReserveVirtualMemory(
+ IN CPalThread *pthrCurrent, /* Currently executing thread */
+ IN LPVOID lpAddress, /* Region to reserve or commit */
+ IN SIZE_T dwSize) /* Size of Region */
+{
+ UINT_PTR StartBoundary = (UINT_PTR)lpAddress;
+ SIZE_T MemSize = dwSize;
+
+ TRACE( "Reserving the memory now.\n");
+
+ // Most platforms will only commit memory if it is dirtied,
+ // so this should not consume too much swap space.
+ int mmapFlags = 0;
+
+#if HAVE_VM_ALLOCATE
+ // Allocate with vm_allocate first, then map at the fixed address.
+ int result = vm_allocate(mach_task_self(),
+ &StartBoundary,
+ MemSize,
+ ((LPVOID) StartBoundary != nullptr) ? FALSE : TRUE);
+
+ if (result != KERN_SUCCESS)
+ {
+ ERROR("vm_allocate failed to allocated the requested region!\n");
+ pthrCurrent->SetLastError(ERROR_INVALID_ADDRESS);
+ return nullptr;
+ }
+
+ mmapFlags |= MAP_FIXED;
+#endif // HAVE_VM_ALLOCATE
+
+ mmapFlags |= MAP_ANON | MAP_PRIVATE;
+
+ LPVOID pRetVal = mmap((LPVOID) StartBoundary,
+ MemSize,
+ PROT_NONE,
+ mmapFlags,
+ -1 /* fd */,
+ 0 /* offset */);
+
+ if (pRetVal == MAP_FAILED)
+ {
+ ERROR( "Failed due to insufficient memory.\n" );
+
+#if HAVE_VM_ALLOCATE
+ vm_deallocate(mach_task_self(), StartBoundary, MemSize);
+#endif // HAVE_VM_ALLOCATE
+
+ pthrCurrent->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return nullptr;
+ }
+
+ /* Check to see if the region is what we asked for. */
+ if (lpAddress != nullptr && StartBoundary != (UINT_PTR)pRetVal)
+ {
+ ERROR("We did not get the region we asked for from mmap!\n");
+ pthrCurrent->SetLastError(ERROR_INVALID_ADDRESS);
+ munmap(pRetVal, MemSize);
+ return nullptr;
+ }
+
+#if MMAP_ANON_IGNORES_PROTECTION
+ if (mprotect(pRetVal, MemSize, PROT_NONE) != 0)
+ {
+ ERROR("mprotect failed to protect the region!\n");
+ pthrCurrent->SetLastError(ERROR_INVALID_ADDRESS);
+ munmap(pRetVal, MemSize);
+ return nullptr;
+ }
+#endif // MMAP_ANON_IGNORES_PROTECTION
+
+ return pRetVal;
+}
+
+/******
+ *
+ * VIRTUALCommitMemory() - Helper function that actually commits the memory.
+ *
+ * NOTE: I call SetLastError in here, because many different error states
+ * exists, and that would be very complicated to work around.
+ *
+ */
+static LPVOID
+VIRTUALCommitMemory(
+ IN CPalThread *pthrCurrent, /* Currently executing thread */
+ IN LPVOID lpAddress, /* Region to reserve or commit */
+ IN SIZE_T dwSize, /* Size of Region */
+ IN DWORD flAllocationType, /* Type of allocation */
+ IN DWORD flProtect) /* Type of access protection */
+{
+ UINT_PTR StartBoundary = 0;
+ SIZE_T MemSize = 0;
+ PCMI pInformation = 0;
+ LPVOID pRetVal = NULL;
+ BOOL IsLocallyReserved = FALSE;
+ SIZE_T totalPages;
+ INT allocationType, curAllocationType;
+ INT protectionState, curProtectionState;
+ SIZE_T initialRunStart;
+ SIZE_T runStart;
+ SIZE_T runLength;
+ SIZE_T index;
+ INT nProtect;
+ INT vProtect;
+
+ if ( lpAddress )
+ {
+ StartBoundary = (UINT_PTR)lpAddress & ~VIRTUAL_PAGE_MASK;
+ /* Add the sizes, and round down to the nearest page boundary. */
+ MemSize = ( ((UINT_PTR)lpAddress + dwSize + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK ) -
+ StartBoundary;
+ }
+ else
+ {
+ MemSize = ( dwSize + VIRTUAL_PAGE_MASK ) & ~VIRTUAL_PAGE_MASK;
+ }
+
+ /* See if we have already reserved this memory. */
+ pInformation = VIRTUALFindRegionInformation( StartBoundary );
+
+ if ( !pInformation )
+ {
+ /* According to the new MSDN docs, if MEM_COMMIT is specified,
+ and the memory is not reserved, you reserve and then commit.
+ */
+ LPVOID pReservedMemory =
+ VIRTUALReserveMemory( pthrCurrent, lpAddress, dwSize,
+ flAllocationType, flProtect );
+
+ TRACE( "Reserve and commit the memory!\n " );
+
+ if ( pReservedMemory )
+ {
+ /* Re-align the addresses and try again to find the memory. */
+ StartBoundary = (UINT_PTR)pReservedMemory & ~VIRTUAL_PAGE_MASK;
+ MemSize = ( ((UINT_PTR)pReservedMemory + dwSize + VIRTUAL_PAGE_MASK)
+ & ~VIRTUAL_PAGE_MASK ) - StartBoundary;
+
+ pInformation = VIRTUALFindRegionInformation( StartBoundary );
+
+ if ( !pInformation )
+ {
+ ASSERT( "Unable to locate the region information.\n" );
+ pthrCurrent->SetLastError( ERROR_INTERNAL_ERROR );
+ pRetVal = NULL;
+ goto done;
+ }
+ IsLocallyReserved = TRUE;
+ }
+ else
+ {
+ ERROR( "Unable to reserve the memory.\n" );
+ /* Don't set last error here, it will already be set. */
+ pRetVal = NULL;
+ goto done;
+ }
+ }
+
+ TRACE( "Committing the memory now..\n");
+
+ // Pages that aren't already committed need to be committed. Pages that
+ // are committed don't need to be committed, but they might need to have
+ // their permissions changed.
+ // To get this right, we find runs of pages with similar states and
+ // permissions. If a run is not committed, we commit it and then set
+ // its permissions. If a run is committed but has different permissions
+ // from what we're trying to set, we set its permissions. Finally,
+ // if a run is already committed and has the right permissions,
+ // we don't need to do anything to it.
+
+ totalPages = MemSize / VIRTUAL_PAGE_SIZE;
+ runStart = (StartBoundary - pInformation->startBoundary) /
+ VIRTUAL_PAGE_SIZE; // Page index
+ initialRunStart = runStart;
+ allocationType = VIRTUALGetAllocationType(runStart, pInformation);
+ protectionState = pInformation->pProtectionState[runStart];
+ curAllocationType = allocationType;
+ curProtectionState = protectionState;
+ runLength = 1;
+ nProtect = W32toUnixAccessControl(flProtect);
+ vProtect = VIRTUALConvertWinFlags(flProtect);
+
+ if (totalPages > pInformation->memSize / VIRTUAL_PAGE_SIZE - runStart)
+ {
+ ERROR("Trying to commit beyond the end of the region!\n");
+ goto error;
+ }
+
+ while(runStart < initialRunStart + totalPages)
+ {
+ // Find the next run of pages
+ for(index = runStart + 1; index < initialRunStart + totalPages;
+ index++)
+ {
+ curAllocationType = VIRTUALGetAllocationType(index, pInformation);
+ curProtectionState = pInformation->pProtectionState[index];
+ if (curAllocationType != allocationType ||
+ curProtectionState != protectionState)
+ {
+ break;
+ }
+ runLength++;
+ }
+
+ StartBoundary = pInformation->startBoundary + runStart * VIRTUAL_PAGE_SIZE;
+ MemSize = runLength * VIRTUAL_PAGE_SIZE;
+
+ if (allocationType != MEM_COMMIT)
+ {
+ // Commit the pages
+ if (mprotect((void *) StartBoundary, MemSize, PROT_WRITE | PROT_READ) != 0)
+ {
+ ERROR("mprotect() failed! Error(%d)=%s\n", errno, strerror(errno));
+ goto error;
+ }
+
+ VIRTUALSetAllocState(MEM_COMMIT, runStart, runLength, pInformation);
+
+ if (nProtect == (PROT_WRITE | PROT_READ))
+ {
+ // Handle this case specially so we don't bother
+ // mprotect'ing the region.
+ memset(pInformation->pProtectionState + runStart,
+ vProtect, runLength);
+ }
+
+ protectionState = VIRTUAL_READWRITE;
+ }
+
+ if (protectionState != vProtect)
+ {
+ // Change permissions.
+ if (mprotect((void *) StartBoundary, MemSize, nProtect) != -1)
+ {
+ memset(pInformation->pProtectionState + runStart,
+ vProtect, runLength);
+ }
+ else
+ {
+ ERROR("mprotect() failed! Error(%d)=%s\n",
+ errno, strerror(errno));
+ goto error;
+ }
+ }
+
+ runStart = index;
+ runLength = 1;
+ allocationType = curAllocationType;
+ protectionState = curProtectionState;
+ }
+
+ pRetVal = (void *) (pInformation->startBoundary + initialRunStart * VIRTUAL_PAGE_SIZE);
+ goto done;
+
+error:
+ if ( flAllocationType & MEM_RESERVE || IsLocallyReserved )
+ {
+ munmap( pRetVal, MemSize );
+ if ( VIRTUALReleaseMemory( pInformation ) == FALSE )
+ {
+ ASSERT( "Unable to remove the PCMI entry from the list.\n" );
+ pthrCurrent->SetLastError( ERROR_INTERNAL_ERROR );
+ pRetVal = NULL;
+ goto done;
+ }
+ pInformation = NULL;
+ pRetVal = NULL;
+ }
+
+done:
+
+ LogVaOperation(
+ VirtualMemoryLogging::VirtualOperation::Commit,
+ lpAddress,
+ dwSize,
+ flAllocationType,
+ flProtect,
+ pRetVal,
+ pRetVal != NULL);
+
+ return pRetVal;
+}
+
+/*++
+Function:
+ VirtualAlloc
+
+Note:
+ MEM_TOP_DOWN, MEM_PHYSICAL, MEM_WRITE_WATCH are not supported.
+ Unsupported flags are ignored.
+
+ Page size on i386 is set to 4k.
+
+See MSDN doc.
+--*/
+LPVOID
+PALAPI
+VirtualAlloc(
+ IN LPVOID lpAddress, /* Region to reserve or commit */
+ IN SIZE_T dwSize, /* Size of Region */
+ IN DWORD flAllocationType, /* Type of allocation */
+ IN DWORD flProtect) /* Type of access protection */
+{
+ LPVOID pRetVal = NULL;
+ CPalThread *pthrCurrent;
+
+ PERF_ENTRY(VirtualAlloc);
+ ENTRY("VirtualAlloc(lpAddress=%p, dwSize=%u, flAllocationType=%#x, \
+ flProtect=%#x)\n", lpAddress, dwSize, flAllocationType, flProtect);
+
+ pthrCurrent = InternalGetCurrentThread();
+
+ if ( ( flAllocationType & MEM_WRITE_WATCH ) != 0 )
+ {
+ pthrCurrent->SetLastError( ERROR_INVALID_PARAMETER );
+ goto done;
+ }
+
+ /* Test for un-supported flags. */
+ if ( ( flAllocationType & ~( MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN | MEM_RESERVE_EXECUTABLE ) ) != 0 )
+ {
+ ASSERT( "flAllocationType can be one, or any combination of MEM_COMMIT, \
+ MEM_RESERVE, MEM_TOP_DOWN, or MEM_RESERVE_EXECUTABLE.\n" );
+ pthrCurrent->SetLastError( ERROR_INVALID_PARAMETER );
+ goto done;
+ }
+ if ( VIRTUALContainsInvalidProtectionFlags( flProtect ) )
+ {
+ ASSERT( "flProtect can be one of PAGE_READONLY, PAGE_READWRITE, or \
+ PAGE_EXECUTE_READWRITE || PAGE_NOACCESS. \n" );
+
+ pthrCurrent->SetLastError( ERROR_INVALID_PARAMETER );
+ goto done;
+ }
+ if ( flAllocationType & MEM_TOP_DOWN )
+ {
+ WARN( "Ignoring the allocation flag MEM_TOP_DOWN.\n" );
+ }
+
+ LogVaOperation(
+ VirtualMemoryLogging::VirtualOperation::Allocate,
+ lpAddress,
+ dwSize,
+ flAllocationType,
+ flProtect,
+ NULL,
+ TRUE);
+
+ if ( flAllocationType & MEM_RESERVE )
+ {
+ InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
+ pRetVal = VIRTUALReserveMemory( pthrCurrent, lpAddress, dwSize, flAllocationType, flProtect );
+ InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec);
+
+ if ( !pRetVal )
+ {
+ /* Error messages are already displayed, just leave. */
+ goto done;
+ }
+ }
+
+ if ( flAllocationType & MEM_COMMIT )
+ {
+ InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
+ if ( pRetVal != NULL )
+ {
+ /* We are reserving and committing. */
+ pRetVal = VIRTUALCommitMemory( pthrCurrent, pRetVal, dwSize,
+ flAllocationType, flProtect );
+ }
+ else
+ {
+ /* Just a commit. */
+ pRetVal = VIRTUALCommitMemory( pthrCurrent, lpAddress, dwSize,
+ flAllocationType, flProtect );
+ }
+ InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec);
+ }
+
+done:
+#if defined _DEBUG
+ VIRTUALDisplayList();
+#endif
+ LOGEXIT("VirtualAlloc returning %p\n ", pRetVal );
+ PERF_EXIT(VirtualAlloc);
+ return pRetVal;
+}
+
+
+/*++
+Function:
+ VirtualFree
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+VirtualFree(
+ IN LPVOID lpAddress, /* Address of region. */
+ IN SIZE_T dwSize, /* Size of region. */
+ IN DWORD dwFreeType ) /* Operation type. */
+{
+ BOOL bRetVal = TRUE;
+ CPalThread *pthrCurrent;
+
+ PERF_ENTRY(VirtualFree);
+ ENTRY("VirtualFree(lpAddress=%p, dwSize=%u, dwFreeType=%#x)\n",
+ lpAddress, dwSize, dwFreeType);
+
+ pthrCurrent = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
+
+ /* Sanity Checks. */
+ if ( !lpAddress )
+ {
+ ERROR( "lpAddress cannot be NULL. You must specify the base address of\
+ regions to be de-committed. \n" );
+ pthrCurrent->SetLastError( ERROR_INVALID_ADDRESS );
+ bRetVal = FALSE;
+ goto VirtualFreeExit;
+ }
+
+ if ( !( dwFreeType & MEM_RELEASE ) && !(dwFreeType & MEM_DECOMMIT ) )
+ {
+ ERROR( "dwFreeType must contain one of the following: \
+ MEM_RELEASE or MEM_DECOMMIT\n" );
+ pthrCurrent->SetLastError( ERROR_INVALID_PARAMETER );
+ bRetVal = FALSE;
+ goto VirtualFreeExit;
+ }
+ /* You cannot release and decommit in one call.*/
+ if ( dwFreeType & MEM_RELEASE && dwFreeType & MEM_DECOMMIT )
+ {
+ ERROR( "MEM_RELEASE cannot be combined with MEM_DECOMMIT.\n" );
+ bRetVal = FALSE;
+ goto VirtualFreeExit;
+ }
+
+ if ( dwFreeType & MEM_DECOMMIT )
+ {
+ UINT_PTR StartBoundary = 0;
+ SIZE_T MemSize = 0;
+
+ if ( dwSize == 0 )
+ {
+ ERROR( "dwSize cannot be 0. \n" );
+ pthrCurrent->SetLastError( ERROR_INVALID_PARAMETER );
+ bRetVal = FALSE;
+ goto VirtualFreeExit;
+ }
+ /*
+ * A two byte range straddling 2 pages caues both pages to be either
+ * released or decommitted. So round the dwSize up to the next page
+ * boundary and round the lpAddress down to the next page boundary.
+ */
+ MemSize = (((UINT_PTR)(dwSize) + ((UINT_PTR)(lpAddress) & VIRTUAL_PAGE_MASK)
+ + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK);
+
+ StartBoundary = (UINT_PTR)lpAddress & ~VIRTUAL_PAGE_MASK;
+
+ PCMI pUnCommittedMem;
+ pUnCommittedMem = VIRTUALFindRegionInformation( StartBoundary );
+ if (!pUnCommittedMem)
+ {
+ ASSERT( "Unable to locate the region information.\n" );
+ pthrCurrent->SetLastError( ERROR_INTERNAL_ERROR );
+ bRetVal = FALSE;
+ goto VirtualFreeExit;
+ }
+
+ TRACE( "Un-committing the following page(s) %d to %d.\n",
+ StartBoundary, MemSize );
+
+ // Explicitly calling mmap instead of mprotect here makes it
+ // that much more clear to the operating system that we no
+ // longer need these pages.
+ if ( mmap( (LPVOID)StartBoundary, MemSize, PROT_NONE,
+ MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0 ) != MAP_FAILED )
+ {
+#if (MMAP_ANON_IGNORES_PROTECTION)
+ if (mprotect((LPVOID) StartBoundary, MemSize, PROT_NONE) != 0)
+ {
+ ASSERT("mprotect failed to protect the region!\n");
+ pthrCurrent->SetLastError(ERROR_INTERNAL_ERROR);
+ munmap((LPVOID) StartBoundary, MemSize);
+ bRetVal = FALSE;
+ goto VirtualFreeExit;
+ }
+#endif // MMAP_ANON_IGNORES_PROTECTION
+
+ SIZE_T index = 0;
+ SIZE_T nNumOfPagesToChange = 0;
+
+ /* We can now commit this memory by calling VirtualAlloc().*/
+ index = (StartBoundary - pUnCommittedMem->startBoundary) / VIRTUAL_PAGE_SIZE;
+
+ nNumOfPagesToChange = MemSize / VIRTUAL_PAGE_SIZE;
+ VIRTUALSetAllocState( MEM_RESERVE, index,
+ nNumOfPagesToChange, pUnCommittedMem );
+
+ goto VirtualFreeExit;
+ }
+ else
+ {
+ ASSERT( "mmap() returned an abnormal value.\n" );
+ bRetVal = FALSE;
+ pthrCurrent->SetLastError( ERROR_INTERNAL_ERROR );
+ goto VirtualFreeExit;
+ }
+ }
+
+ if ( dwFreeType & MEM_RELEASE )
+ {
+ PCMI pMemoryToBeReleased =
+ VIRTUALFindRegionInformation( (UINT_PTR)lpAddress );
+
+ if ( !pMemoryToBeReleased )
+ {
+ ERROR( "lpAddress must be the base address returned by VirtualAlloc.\n" );
+ pthrCurrent->SetLastError( ERROR_INVALID_ADDRESS );
+ bRetVal = FALSE;
+ goto VirtualFreeExit;
+ }
+ if ( dwSize != 0 )
+ {
+ ERROR( "dwSize must be 0 if you are releasing the memory.\n" );
+ pthrCurrent->SetLastError( ERROR_INVALID_PARAMETER );
+ bRetVal = FALSE;
+ goto VirtualFreeExit;
+ }
+
+ TRACE( "Releasing the following memory %d to %d.\n",
+ pMemoryToBeReleased->startBoundary, pMemoryToBeReleased->memSize );
+
+ if ( munmap( (LPVOID)pMemoryToBeReleased->startBoundary,
+ pMemoryToBeReleased->memSize ) == 0 )
+ {
+ if ( VIRTUALReleaseMemory( pMemoryToBeReleased ) == FALSE )
+ {
+ ASSERT( "Unable to remove the PCMI entry from the list.\n" );
+ pthrCurrent->SetLastError( ERROR_INTERNAL_ERROR );
+ bRetVal = FALSE;
+ goto VirtualFreeExit;
+ }
+ pMemoryToBeReleased = NULL;
+ }
+ else
+ {
+ ASSERT( "Unable to unmap the memory, munmap() returned an abnormal value.\n" );
+ pthrCurrent->SetLastError( ERROR_INTERNAL_ERROR );
+ bRetVal = FALSE;
+ goto VirtualFreeExit;
+ }
+ }
+
+VirtualFreeExit:
+
+ LogVaOperation(
+ (dwFreeType & MEM_DECOMMIT) ? VirtualMemoryLogging::VirtualOperation::Decommit
+ : VirtualMemoryLogging::VirtualOperation::Release,
+ lpAddress,
+ dwSize,
+ dwFreeType,
+ 0,
+ NULL,
+ bRetVal);
+
+ InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec);
+ LOGEXIT( "VirtualFree returning %s.\n", bRetVal == TRUE ? "TRUE" : "FALSE" );
+ PERF_EXIT(VirtualFree);
+ return bRetVal;
+}
+
+
+/*++
+Function:
+ VirtualProtect
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+VirtualProtect(
+ IN LPVOID lpAddress,
+ IN SIZE_T dwSize,
+ IN DWORD flNewProtect,
+ OUT PDWORD lpflOldProtect)
+{
+ BOOL bRetVal = FALSE;
+ PCMI pEntry = NULL;
+ SIZE_T MemSize = 0;
+ UINT_PTR StartBoundary = 0;
+ SIZE_T Index = 0;
+ SIZE_T NumberOfPagesToChange = 0;
+ SIZE_T OffSet = 0;
+ CPalThread * pthrCurrent;
+
+ PERF_ENTRY(VirtualProtect);
+ ENTRY("VirtualProtect(lpAddress=%p, dwSize=%u, flNewProtect=%#x, "
+ "flOldProtect=%p)\n",
+ lpAddress, dwSize, flNewProtect, lpflOldProtect);
+
+ pthrCurrent = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
+
+ StartBoundary = (UINT_PTR)lpAddress & ~VIRTUAL_PAGE_MASK;
+ MemSize = (((UINT_PTR)(dwSize) + ((UINT_PTR)(lpAddress) & VIRTUAL_PAGE_MASK)
+ + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK);
+
+ if ( VIRTUALContainsInvalidProtectionFlags( flNewProtect ) )
+ {
+ ASSERT( "flProtect can be one of PAGE_NOACCESS, PAGE_READONLY, "
+ "PAGE_READWRITE, PAGE_EXECUTE, PAGE_EXECUTE_READ "
+ ", or PAGE_EXECUTE_READWRITE. \n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto ExitVirtualProtect;
+ }
+
+ if ( !lpflOldProtect)
+ {
+ ERROR( "lpflOldProtect was invalid.\n" );
+ SetLastError( ERROR_NOACCESS );
+ goto ExitVirtualProtect;
+ }
+
+ pEntry = VIRTUALFindRegionInformation( StartBoundary );
+ if ( NULL != pEntry )
+ {
+ /* See if the pages are committed. */
+ Index = OffSet = StartBoundary - pEntry->startBoundary == 0 ?
+ 0 : ( StartBoundary - pEntry->startBoundary ) / VIRTUAL_PAGE_SIZE;
+ NumberOfPagesToChange = MemSize / VIRTUAL_PAGE_SIZE;
+
+ TRACE( "Number of pages to check %d, starting page %d \n", NumberOfPagesToChange, Index );
+
+ for ( ; Index < NumberOfPagesToChange; Index++ )
+ {
+ if ( !VIRTUALIsPageCommitted( Index, pEntry ) )
+ {
+ ERROR( "You can only change the protection attributes"
+ " on committed memory.\n" )
+ SetLastError( ERROR_INVALID_ADDRESS );
+ goto ExitVirtualProtect;
+ }
+ }
+ }
+
+ if ( 0 == mprotect( (LPVOID)StartBoundary, MemSize,
+ W32toUnixAccessControl( flNewProtect ) ) )
+ {
+ /* Reset the access protection. */
+ TRACE( "Number of pages to change %d, starting page %d \n",
+ NumberOfPagesToChange, OffSet );
+ /*
+ * Set the old protection flags. We only use the first flag, so
+ * if there were several regions with each with different flags only the
+ * first region's protection flag will be returned.
+ */
+ if ( pEntry )
+ {
+ *lpflOldProtect =
+ VIRTUALConvertVirtualFlags( pEntry->pProtectionState[ OffSet ] );
+
+ memset( pEntry->pProtectionState + OffSet,
+ VIRTUALConvertWinFlags( flNewProtect ),
+ NumberOfPagesToChange );
+ }
+ else
+ {
+ *lpflOldProtect = PAGE_EXECUTE_READWRITE;
+ }
+ bRetVal = TRUE;
+ }
+ else
+ {
+ ERROR( "%s\n", strerror( errno ) );
+ if ( errno == EINVAL )
+ {
+ SetLastError( ERROR_INVALID_ADDRESS );
+ }
+ else if ( errno == EACCES )
+ {
+ SetLastError( ERROR_INVALID_ACCESS );
+ }
+ }
+ExitVirtualProtect:
+ InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec);
+
+#if defined _DEBUG
+ VIRTUALDisplayList();
+#endif
+ LOGEXIT( "VirtualProtect returning %s.\n", bRetVal == TRUE ? "TRUE" : "FALSE" );
+ PERF_EXIT(VirtualProtect);
+ return bRetVal;
+}
+
+#if HAVE_VM_ALLOCATE
+//---------------------------------------------------------------------------------------
+//
+// Convert a vm_prot_t flag on the Mach kernel to the corresponding memory protection on Windows.
+//
+// Arguments:
+// protection - Mach protection to be converted
+//
+// Return Value:
+// Return the corresponding memory protection on Windows (e.g. PAGE_READ_WRITE, etc.)
+//
+
+static DWORD VirtualMapMachProtectToWinProtect(vm_prot_t protection)
+{
+ if (protection & VM_PROT_READ)
+ {
+ if (protection & VM_PROT_WRITE)
+ {
+ if (protection & VM_PROT_EXECUTE)
+ {
+ return PAGE_EXECUTE_READWRITE;
+ }
+ else
+ {
+ return PAGE_READWRITE;
+ }
+ }
+ else
+ {
+ if (protection & VM_PROT_EXECUTE)
+ {
+ return PAGE_EXECUTE_READ;
+ }
+ else
+ {
+ return PAGE_READONLY;
+ }
+ }
+ }
+ else
+ {
+ if (protection & VM_PROT_WRITE)
+ {
+ if (protection & VM_PROT_EXECUTE)
+ {
+ return PAGE_EXECUTE_WRITECOPY;
+ }
+ else
+ {
+ return PAGE_WRITECOPY;
+ }
+ }
+ else
+ {
+ if (protection & VM_PROT_EXECUTE)
+ {
+ return PAGE_EXECUTE;
+ }
+ else
+ {
+ return PAGE_NOACCESS;
+ }
+ }
+ }
+}
+
+static void VM_ALLOCATE_VirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer)
+{
+ kern_return_t MachRet;
+ vm_address_t vm_address;
+ vm_size_t vm_size;
+ vm_region_flavor_t vm_flavor;
+ mach_msg_type_number_t infoCnt;
+ mach_port_t object_name;
+#ifdef BIT64
+ vm_region_basic_info_data_64_t info;
+ infoCnt = VM_REGION_BASIC_INFO_COUNT_64;
+ vm_flavor = VM_REGION_BASIC_INFO_64;
+#else
+ vm_region_basic_info_data_t info;
+ infoCnt = VM_REGION_BASIC_INFO_COUNT;
+ vm_flavor = VM_REGION_BASIC_INFO;
+#endif
+
+ vm_address = (vm_address_t)lpAddress;
+#ifdef BIT64
+ MachRet = vm_region_64(
+#else
+ MachRet = vm_region(
+#endif
+ mach_task_self(),
+ &vm_address,
+ &vm_size,
+ vm_flavor,
+ (vm_region_info_t)&info,
+ &infoCnt,
+ &object_name);
+ if (MachRet != KERN_SUCCESS) {
+ return;
+ }
+
+ if (vm_address > (vm_address_t)lpAddress) {
+ /* lpAddress was pointing into a free region */
+ lpBuffer->State = MEM_FREE;
+ return;
+ }
+
+ lpBuffer->BaseAddress = (PVOID)vm_address;
+
+ // We don't actually have any information on the Mach kernel which maps to AllocationProtect.
+ lpBuffer->AllocationProtect = VM_PROT_NONE;
+
+ lpBuffer->RegionSize = (SIZE_T)vm_size;
+
+ if (info.reserved)
+ {
+ lpBuffer->State = MEM_RESERVE;
+ }
+ else
+ {
+ lpBuffer->State = MEM_COMMIT;
+ }
+
+ lpBuffer->Protect = VirtualMapMachProtectToWinProtect(info.protection);
+
+ /* Note that if a mapped region and a private region are adjacent, this
+ will return MEM_PRIVATE but the region size will span
+ both the mapped and private regions. */
+ if (!info.shared)
+ {
+ lpBuffer->Type = MEM_PRIVATE;
+ }
+ else
+ {
+ // What should this be? It's either MEM_MAPPED or MEM_IMAGE, but without an image list,
+ // we can't determine which one it is.
+ lpBuffer->Type = MEM_MAPPED;
+ }
+}
+#endif // HAVE_VM_ALLOCATE
+
+/*++
+Function:
+ VirtualQuery
+
+See MSDN doc.
+--*/
+SIZE_T
+PALAPI
+VirtualQuery(
+ IN LPCVOID lpAddress,
+ OUT PMEMORY_BASIC_INFORMATION lpBuffer,
+ IN SIZE_T dwLength)
+{
+ PCMI pEntry = NULL;
+ UINT_PTR StartBoundary = 0;
+ CPalThread * pthrCurrent;
+
+ PERF_ENTRY(VirtualQuery);
+ ENTRY("VirtualQuery(lpAddress=%p, lpBuffer=%p, dwLength=%u)\n",
+ lpAddress, lpBuffer, dwLength);
+
+ pthrCurrent = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pthrCurrent, &virtual_critsec);
+
+ if ( !lpBuffer)
+ {
+ ERROR( "lpBuffer has to be a valid pointer.\n" );
+ pthrCurrent->SetLastError( ERROR_NOACCESS );
+ goto ExitVirtualQuery;
+ }
+ if ( dwLength < sizeof( *lpBuffer ) )
+ {
+ ERROR( "dwLength cannot be smaller then the size of *lpBuffer.\n" );
+ pthrCurrent->SetLastError( ERROR_BAD_LENGTH );
+ goto ExitVirtualQuery;
+ }
+
+ StartBoundary = (UINT_PTR)lpAddress & ~VIRTUAL_PAGE_MASK;
+
+#if MMAP_IGNORES_HINT
+ // Make sure we have memory to map before we try to query it.
+ VIRTUALGetBackingFile(pthrCurrent);
+
+ // If we're suballocating, claim that any memory that isn't in our
+ // suballocated block is already allocated. This keeps callers from
+ // using these results to try to allocate those blocks and failing.
+ if (StartBoundary < (UINT_PTR) gBackingBaseAddress ||
+ StartBoundary >= (UINT_PTR) gBackingBaseAddress + BACKING_FILE_SIZE)
+ {
+ if (StartBoundary < (UINT_PTR) gBackingBaseAddress)
+ {
+ lpBuffer->RegionSize = (UINT_PTR) gBackingBaseAddress - StartBoundary;
+ }
+ else
+ {
+ lpBuffer->RegionSize = -StartBoundary;
+ }
+ lpBuffer->BaseAddress = (void *) StartBoundary;
+ lpBuffer->State = MEM_COMMIT;
+ lpBuffer->Type = MEM_MAPPED;
+ lpBuffer->AllocationProtect = 0;
+ lpBuffer->Protect = 0;
+ goto ExitVirtualQuery;
+ }
+#endif // MMAP_IGNORES_HINT
+
+ /* Find the entry. */
+ pEntry = VIRTUALFindRegionInformation( StartBoundary );
+
+ if ( !pEntry )
+ {
+ /* Can't find a match, or no list present. */
+ /* Next, looking for this region in file maps */
+ if (!MAPGetRegionInfo((LPVOID)StartBoundary, lpBuffer))
+ {
+ // When all else fails, call vm_region() if it's available.
+
+ // Initialize the State to be MEM_FREE, in which case AllocationBase, AllocationProtect,
+ // Protect, and Type are all undefined.
+ lpBuffer->BaseAddress = (LPVOID)StartBoundary;
+ lpBuffer->RegionSize = 0;
+ lpBuffer->State = MEM_FREE;
+#if HAVE_VM_ALLOCATE
+ VM_ALLOCATE_VirtualQuery(lpAddress, lpBuffer);
+#endif
+ }
+ }
+ else
+ {
+ /* Starting page. */
+ SIZE_T Index = ( StartBoundary - pEntry->startBoundary ) / VIRTUAL_PAGE_SIZE;
+
+ /* Attributes to check for. */
+ BYTE AccessProtection = pEntry->pProtectionState[ Index ];
+ INT AllocationType = VIRTUALGetAllocationType( Index, pEntry );
+ SIZE_T RegionSize = 0;
+
+ TRACE( "Index = %d, Number of Pages = %d. \n",
+ Index, pEntry->memSize / VIRTUAL_PAGE_SIZE );
+
+ while ( Index < pEntry->memSize / VIRTUAL_PAGE_SIZE &&
+ VIRTUALGetAllocationType( Index, pEntry ) == AllocationType &&
+ pEntry->pProtectionState[ Index ] == AccessProtection )
+ {
+ RegionSize += VIRTUAL_PAGE_SIZE;
+ Index++;
+ }
+
+ TRACE( "RegionSize = %d.\n", RegionSize );
+
+ /* Fill the structure.*/
+ lpBuffer->AllocationProtect = pEntry->accessProtection;
+ lpBuffer->BaseAddress = (LPVOID)StartBoundary;
+
+ lpBuffer->Protect = AllocationType == MEM_COMMIT ?
+ VIRTUALConvertVirtualFlags( AccessProtection ) : 0;
+
+ lpBuffer->RegionSize = RegionSize;
+ lpBuffer->State =
+ ( AllocationType == MEM_COMMIT ? MEM_COMMIT : MEM_RESERVE );
+ WARN( "Ignoring lpBuffer->Type. \n" );
+ }
+
+ExitVirtualQuery:
+
+ InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec);
+
+ LOGEXIT( "VirtualQuery returning %d.\n", sizeof( *lpBuffer ) );
+ PERF_EXIT(VirtualQuery);
+ return sizeof( *lpBuffer );
+}
+
+/*++
+Function:
+ GetWriteWatch
+
+See MSDN doc.
+--*/
+UINT
+PALAPI
+GetWriteWatch(
+ IN DWORD dwFlags,
+ IN PVOID lpBaseAddress,
+ IN SIZE_T dwRegionSize,
+ OUT PVOID *lpAddresses,
+ IN OUT PULONG_PTR lpdwCount,
+ OUT PULONG lpdwGranularity
+)
+{
+ // TODO: implement this method
+ *lpAddresses = NULL;
+ *lpdwCount = 0;
+ // Until it is implemented, return non-zero value as an indicator of failure
+ return 1;
+}
+
+/*++
+Function:
+ ResetWriteWatch
+
+See MSDN doc.
+--*/
+UINT
+PALAPI
+ResetWriteWatch(
+ IN LPVOID lpBaseAddress,
+ IN SIZE_T dwRegionSize
+)
+{
+ // TODO: implement this method
+ // Until it is implemented, return non-zero value as an indicator of failure
+ return 1;
+}
+
+/*++
+Function :
+ ReserveMemoryFromExecutableAllocator
+
+ This function is used to reserve a region of virual memory (not commited)
+ that is located close to the coreclr library. The memory comes from the virtual
+ address range that is managed by ExecutableMemoryAllocator.
+--*/
+void* ReserveMemoryFromExecutableAllocator(CPalThread* pThread, SIZE_T allocationSize)
+{
+ InternalEnterCriticalSection(pThread, &virtual_critsec);
+ void* mem = g_executableMemoryAllocator.AllocateMemory(allocationSize);
+ InternalLeaveCriticalSection(pThread, &virtual_critsec);
+
+ return mem;
+}
+
+/*++
+Function:
+ ExecutableMemoryAllocator::Initialize()
+
+ This function initializes the allocator. It should be called early during process startup
+ (when process address space is pretty much empty) in order to have a chance to reserve
+ sufficient amount of memory that is close to the coreclr library.
+
+--*/
+void ExecutableMemoryAllocator::Initialize()
+{
+ m_startAddress = NULL;
+ m_nextFreeAddress = NULL;
+ m_totalSizeOfReservedMemory = 0;
+ m_remainingReservedMemory = 0;
+
+ // Enable the executable memory allocator on 64-bit platforms only
+ // because 32-bit platforms have limited amount of virtual address space.
+#ifdef BIT64
+ TryReserveInitialMemory();
+#endif // BIT64
+
+}
+
+/*++
+Function:
+ ExecutableMemoryAllocator::TryReserveInitialMemory()
+
+ This function is called during PAL initialization. It opportunistically tries to reserve
+ a large chunk of virtual memory that can be later used to store JIT'ed code.\
+
+--*/
+void ExecutableMemoryAllocator::TryReserveInitialMemory()
+{
+ CPalThread* pthrCurrent = InternalGetCurrentThread();
+ int32_t sizeOfAllocation = MaxExecutableMemorySize;
+ int32_t startAddressIncrement;
+ UINT_PTR startAddress;
+ UINT_PTR coreclrLoadAddress;
+ const int32_t MemoryProbingIncrement = 128 * 1024 * 1024;
+
+ // Try to find and reserve an available region of virtual memory that is located
+ // within 2GB range (defined by the MaxExecutableMemorySize constant) from the
+ // location of the coreclr library.
+ // Potentially, as a possible future improvement, we can get precise information
+ // about available memory ranges by parsing data from '/proc/self/maps'.
+ // But since this code is called early during process startup, the user address space
+ // is pretty much empty so the simple algorithm that is implemented below is sufficient
+ // for this purpose.
+
+ // First of all, we need to determine the current address of libcoreclr. Please note that depending on
+ // the OS implementation, the library is usually loaded either at the end or at the start of the user
+ // address space. If the library is loaded at low addresses then try to reserve memory above libcoreclr
+ // (thus avoiding reserving memory below 4GB; besides some operating systems do not allow that).
+ // If libcoreclr is loaded at high addresses then try to reserve memory below its location.
+ coreclrLoadAddress = (UINT_PTR)PAL_GetSymbolModuleBase((void*)VirtualAlloc);
+ if ((coreclrLoadAddress < 0xFFFFFFFF) || ((coreclrLoadAddress - MaxExecutableMemorySize) < 0xFFFFFFFF))
+ {
+ // Try to allocate above the location of libcoreclr
+ startAddress = coreclrLoadAddress + CoreClrLibrarySize;
+ startAddressIncrement = MemoryProbingIncrement;
+ }
+ else
+ {
+ // Try to allocate below the location of libcoreclr
+ startAddress = coreclrLoadAddress - MaxExecutableMemorySize;
+ startAddressIncrement = 0;
+ }
+
+ // Do actual memory reservation.
+ do
+ {
+ m_startAddress = ReserveVirtualMemory(pthrCurrent, (void*)startAddress, sizeOfAllocation);
+ if (m_startAddress != NULL)
+ {
+ // Memory has been successfully reserved.
+ m_totalSizeOfReservedMemory = sizeOfAllocation;
+
+ // Randomize the location at which we start allocating from the reserved memory range.
+ int32_t randomOffset = GenerateRandomStartOffset();
+ m_nextFreeAddress = (void*)(((UINT_PTR)m_startAddress) + randomOffset);
+ m_remainingReservedMemory = sizeOfAllocation - randomOffset;
+ break;
+ }
+
+ // Try to allocate a smaller region
+ sizeOfAllocation -= MemoryProbingIncrement;
+ startAddress += startAddressIncrement;
+
+ } while (sizeOfAllocation >= MemoryProbingIncrement);
+}
+
+/*++
+Function:
+ ExecutableMemoryAllocator::AllocateMemory
+
+ This function attempts to allocate the requested amount of memory from its reserved virtual
+ address space. The function will return NULL if the allocation request cannot
+ be satisfied by the memory that is currently available in the allocator.
+
+ Note: This function MUST be called with the virtual_critsec lock held.
+
+--*/
+void* ExecutableMemoryAllocator::AllocateMemory(SIZE_T allocationSize)
+{
+ void* allocatedMemory = NULL;
+
+ // Allocation size must be in multiples of the virtual page size.
+ _ASSERTE((allocationSize & VIRTUAL_PAGE_MASK) == 0);
+
+ // The code below assumes that the caller owns the virtual_critsec lock.
+ // So the calculations are not done in thread-safe manner.
+ if ((allocationSize > 0) && (allocationSize <= m_remainingReservedMemory))
+ {
+ allocatedMemory = m_nextFreeAddress;
+ m_nextFreeAddress = (void*)(((UINT_PTR)m_nextFreeAddress) + allocationSize);
+ m_remainingReservedMemory -= allocationSize;
+
+ }
+
+ return allocatedMemory;
+}
+
+/*++
+Function:
+ ExecutableMemoryAllocator::GenerateRandomStartOffset()
+
+ This function returns a random offset (in multiples of the virtual page size)
+ at which the allocator should start allocating memory from its reserved memory range.
+
+--*/
+int32_t ExecutableMemoryAllocator::GenerateRandomStartOffset()
+{
+ int32_t pageCount;
+ const int32_t MaxStartPageOffset = 64;
+
+ // This code is similar to what coreclr runtime does on Windows.
+ // It generates a random number of pages to skip between 0...MaxStartPageOffset.
+ srandom(time(NULL));
+ pageCount = (int32_t)(MaxStartPageOffset * (int64_t)random() / RAND_MAX);
+
+ return pageCount * VIRTUAL_PAGE_SIZE;
+}
diff --git a/src/pal/src/memory/heap.cpp b/src/pal/src/memory/heap.cpp
new file mode 100644
index 0000000000..5757da83b7
--- /dev/null
+++ b/src/pal/src/memory/heap.cpp
@@ -0,0 +1,389 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ heap.c
+
+Abstract:
+
+ Implementation of heap memory management functions.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/handlemgr.hpp"
+#include "pal/corunix.hpp"
+#include <errno.h>
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(MEM);
+
+// In safemath.h, Template SafeInt uses macro _ASSERTE, which need to use variable
+// defdbgchan defined by SET_DEFAULT_DEBUG_CHANNEL. Therefore, the include statement
+// should be placed after the SET_DEFAULT_DEBUG_CHANNEL(MEM)
+#include <safemath.h>
+
+#ifndef __APPLE__
+#define DUMMY_HEAP 0x01020304
+#endif // __APPLE__
+
+/*++
+Function:
+ RtlMoveMemory
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+RtlMoveMemory(
+ IN PVOID Destination,
+ IN CONST VOID *Source,
+ IN SIZE_T Length)
+{
+ PERF_ENTRY(RtlMoveMemory);
+ ENTRY("RtlMoveMemory(Destination:%p, Source:%p, Length:%d)\n",
+ Destination, Source, Length);
+
+ memmove(Destination, Source, Length);
+
+ LOGEXIT("RtlMoveMemory returning\n");
+ PERF_EXIT(RtlMoveMemory);
+}
+
+/*++
+Function:
+ RtlZeroMemory
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+RtlZeroMemory(
+ PVOID Destination,
+ SIZE_T Length
+)
+{
+ PERF_ENTRY(RtlZeroMemory);
+ ENTRY("RtlZeroMemory(Destination:%p, Length:%x)\n", Destination, Length);
+
+ memset(Destination, 0, Length);
+
+ LOGEXIT("RtlZeroMemory returning.\n");
+ PERF_EXIT(RtlZeroMemory);
+}
+
+/*++
+Function:
+ HeapCreate
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+HeapCreate(
+ IN DWORD flOptions,
+ IN SIZE_T dwInitialSize,
+ IN SIZE_T dwMaximumSize)
+{
+ HANDLE ret = INVALID_HANDLE_VALUE;
+ PERF_ENTRY(HeapCreate);
+ ENTRY("HeapCreate(flOptions=%#x, dwInitialSize=%u, dwMaximumSize=%u)\n",
+ flOptions, dwInitialSize, dwMaximumSize);
+#ifdef __APPLE__
+ if ((flOptions & 0x40005) != 0)
+ {
+ ERROR("Invalid flOptions\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ else if (flOptions != 0)
+ {
+ ERROR("No support for flOptions\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ else if (dwMaximumSize)
+ {
+ ERROR("Zone implementation does not support a max size\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ else
+ {
+ ret = (HANDLE)malloc_create_zone(dwInitialSize, 0 /* flags */);
+ }
+
+#else // __APPLE__
+ ret = (HANDLE)DUMMY_HEAP;
+#endif // __APPLE__
+
+ LOGEXIT("HeapCreate returning HANDLE %p\n", ret);
+ PERF_EXIT(HeapCreate);
+ return ret;
+}
+
+
+/*++
+Function:
+ GetProcessHeap
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+GetProcessHeap(
+ VOID)
+{
+ HANDLE ret;
+
+ PERF_ENTRY(GetProcessHeap);
+ ENTRY("GetProcessHeap()\n");
+
+#ifdef __APPLE__
+#if HEAP_HANDLES_ARE_REAL_HANDLES
+#error
+#else
+ malloc_zone_t *pZone = malloc_default_zone();
+ ret = (HANDLE)pZone;
+#endif // HEAP_HANDLES_ARE_REAL_HANDLES
+#else
+ ret = (HANDLE) DUMMY_HEAP;
+#endif
+
+ LOGEXIT("GetProcessHeap returning HANDLE %p\n", ret);
+ PERF_EXIT(GetProcessHeap);
+ return ret;
+}
+
+/*++
+Function:
+ HeapAlloc
+
+Abstract
+ Implemented as wrapper over malloc
+
+See MSDN doc.
+--*/
+LPVOID
+PALAPI
+HeapAlloc(
+ IN HANDLE hHeap,
+ IN DWORD dwFlags,
+ IN SIZE_T numberOfBytes)
+{
+ BYTE *pMem;
+
+ PERF_ENTRY(HeapAlloc);
+ ENTRY("HeapAlloc (hHeap=%p, dwFlags=%#x, numberOfBytes=%u)\n",
+ hHeap, dwFlags, numberOfBytes);
+
+#ifdef __APPLE__
+ if (hHeap == NULL)
+#else // __APPLE__
+ if (hHeap != (HANDLE) DUMMY_HEAP)
+#endif // __APPLE__ else
+ {
+ ERROR("Invalid heap handle\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ LOGEXIT("HeapAlloc returning NULL\n");
+ PERF_EXIT(HeapAlloc);
+ return NULL;
+ }
+
+ if ((dwFlags != 0) && (dwFlags != HEAP_ZERO_MEMORY))
+ {
+ ASSERT("Invalid parameter dwFlags=%#x\n", dwFlags);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ LOGEXIT("HeapAlloc returning NULL\n");
+ PERF_EXIT(HeapAlloc);
+ return NULL;
+ }
+
+#ifdef __APPLE__
+ // This is patterned off of InternalMalloc in malloc.cpp.
+ {
+ pMem = (BYTE *)malloc_zone_malloc((malloc_zone_t *)hHeap, numberOfBytes);
+ }
+#else // __APPLE__
+ pMem = (BYTE *) PAL_malloc(numberOfBytes);
+#endif // __APPLE__ else
+
+ if (pMem == NULL)
+ {
+ ERROR("Not enough memory\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ LOGEXIT("HeapAlloc returning NULL\n");
+ PERF_EXIT(HeapAlloc);
+ return NULL;
+ }
+
+ /* If the HEAP_ZERO_MEMORY flag is set initialize to zero */
+ if (dwFlags == HEAP_ZERO_MEMORY)
+ {
+ memset(pMem, 0, numberOfBytes);
+ }
+
+ LOGEXIT("HeapAlloc returning LPVOID %p\n", pMem);
+ PERF_EXIT(HeapAlloc);
+ return (pMem);
+}
+
+
+/*++
+Function:
+ HeapFree
+
+Abstract
+ Implemented as wrapper over free
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+HeapFree(
+ IN HANDLE hHeap,
+ IN DWORD dwFlags,
+ IN LPVOID lpMem)
+{
+ BOOL bRetVal = FALSE;
+
+ PERF_ENTRY(HeapFree);
+ ENTRY("HeapFree (hHeap=%p, dwFlags = %#x, lpMem=%p)\n",
+ hHeap, dwFlags, lpMem);
+
+#ifdef __APPLE__
+ if (hHeap == NULL)
+#else // __APPLE__
+ if (hHeap != (HANDLE) DUMMY_HEAP)
+#endif // __APPLE__ else
+ {
+ ERROR("Invalid heap handle\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ if (dwFlags != 0)
+ {
+ ASSERT("Invalid parameter dwFlags=%#x\n", dwFlags);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ if (!lpMem)
+ {
+ bRetVal = TRUE;
+ goto done;
+ }
+
+ bRetVal = TRUE;
+#ifdef __APPLE__
+ {
+ malloc_zone_free((malloc_zone_t *)hHeap, lpMem);
+ }
+#else // __APPLE__
+ PAL_free (lpMem);
+#endif // __APPLE__ else
+
+done:
+ LOGEXIT( "HeapFree returning BOOL %d\n", bRetVal );
+ PERF_EXIT(HeapFree);
+ return bRetVal;
+}
+
+
+/*++
+Function:
+ HeapReAlloc
+
+Abstract
+ Implemented as wrapper over realloc
+
+See MSDN doc.
+--*/
+LPVOID
+PALAPI
+HeapReAlloc(
+ IN HANDLE hHeap,
+ IN DWORD dwFlags,
+ IN LPVOID lpmem,
+ IN SIZE_T numberOfBytes)
+{
+ BYTE *pMem = NULL;
+
+ PERF_ENTRY(HeapReAlloc);
+ ENTRY("HeapReAlloc (hHeap=%p, dwFlags=%#x, lpmem=%p, numberOfBytes=%u)\n",
+ hHeap, dwFlags, lpmem, numberOfBytes);
+
+#ifdef __APPLE__
+ if (hHeap == NULL)
+#else // __APPLE__
+ if (hHeap != (HANDLE)DUMMY_HEAP)
+#endif // __APPLE__ else
+ {
+ ASSERT("Invalid heap handle\n");
+ SetLastError(ERROR_INVALID_HANDLE);
+ goto done;
+ }
+
+ if ((dwFlags != 0))
+ {
+ ASSERT("Invalid parameter dwFlags=%#x\n", dwFlags);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ if (lpmem == NULL)
+ {
+ WARN("NULL memory pointer to realloc. Do not do anything.\n");
+ /* set LastError back to zero. this appears to be an undocumented
+ behavior in Windows, in doesn't cost much to match it */
+ SetLastError(0);
+ goto done;
+ }
+
+ if(numberOfBytes == 0)
+ {
+ // PAL's realloc behaves like free for a requested size of zero bytes. Force a nonzero size to get a valid pointer.
+ numberOfBytes = 1;
+ }
+
+#ifdef __APPLE__
+ // This is patterned off of InternalRealloc in malloc.cpp.
+ {
+ pMem = (BYTE *) malloc_zone_realloc((malloc_zone_t *)hHeap, lpmem, numberOfBytes);
+ }
+#else // __APPLE__
+ pMem = (BYTE *) PAL_realloc(lpmem, numberOfBytes);
+#endif // __APPLE__ else
+
+ if (pMem == NULL)
+ {
+ ERROR("Not enough memory\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+
+done:
+ LOGEXIT("HeapReAlloc returns LPVOID %p\n", pMem);
+ PERF_EXIT(HeapReAlloc);
+ return pMem;
+}
+
+BOOL
+PALAPI
+HeapSetInformation(
+ IN OPTIONAL HANDLE HeapHandle,
+ IN HEAP_INFORMATION_CLASS HeapInformationClass,
+ IN PVOID HeapInformation,
+ IN SIZE_T HeapInformationLength)
+{
+ return TRUE;
+}
diff --git a/src/pal/src/memory/local.cpp b/src/pal/src/memory/local.cpp
new file mode 100644
index 0000000000..3a0f40f8c4
--- /dev/null
+++ b/src/pal/src/memory/local.cpp
@@ -0,0 +1,141 @@
+// 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.
+
+/*++
+
+
+
+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);
+
+static
+int
+AllocFlagsToHeapAllocFlags (IN UINT AllocFlags,
+ OUT PUINT pHeapallocFlags)
+{
+ int success = 1;
+ UINT newFlags = 0, flags = AllocFlags;
+ if (flags & LMEM_ZEROINIT) {
+ newFlags |= HEAP_ZERO_MEMORY;
+ flags &= ~LMEM_ZEROINIT;
+ }
+ if (flags != 0) {
+ ASSERT("Invalid parameter AllocFlags=0x%x\n", AllocFlags);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ success = 0;
+ }
+ if (success) {
+ *pHeapallocFlags = newFlags;
+ }
+ return success;
+}
+
+
+
+/*++
+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 (!AllocFlagsToHeapAllocFlags (uFlags, &uFlags)) {
+ goto done;
+ }
+
+ lpRetVal = HeapAlloc( GetProcessHeap(), uFlags, uBytes );
+
+done:
+ LOGEXIT( "LocalAlloc returning %p.\n", lpRetVal );
+ PERF_EXIT(LocalAlloc);
+ return (HLOCAL) lpRetVal;
+}
+
+/*++
+Function:
+LocalReAlloc
+
+See MSDN doc.
+--*/
+HLOCAL
+PALAPI
+LocalReAlloc(
+ IN HLOCAL hMem,
+ IN SIZE_T uBytes,
+ IN UINT uFlags)
+{
+ LPVOID lpRetVal = NULL;
+ PERF_ENTRY(LocalReAlloc);
+ ENTRY("LocalReAlloc (hMem=%p, uBytes=%u, uFlags=%#x)\n", hMem, uBytes, uFlags);
+
+ if (uFlags != LMEM_MOVEABLE) {
+ // Currently valid iff uFlags is LMEM_MOVEABLE
+ ASSERT("Invalid parameter uFlags=0x%x\n", uFlags);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+ uFlags = 0;
+
+ lpRetVal = HeapReAlloc(GetProcessHeap(), uFlags, hMem, uBytes);
+
+done:
+ LOGEXIT("LocalReAlloc returning %p.\n", lpRetVal);
+ PERF_EXIT(LocalReAlloc);
+ 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);
+
+ if ( hMem )
+ {
+ bRetVal = HeapFree( GetProcessHeap(), 0, hMem );
+ }
+ else
+ {
+ 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/dbgmsg.cpp b/src/pal/src/misc/dbgmsg.cpp
new file mode 100644
index 0000000000..488e61494e
--- /dev/null
+++ b/src/pal/src/misc/dbgmsg.cpp
@@ -0,0 +1,968 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ misc/dbgmsg.cpp
+
+Abstract:
+ Implementation of Debug Message utilies. Relay channel information,
+ output functions, etc.
+
+
+
+--*/
+
+/* PAL headers */
+
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+#include "pal/file.hpp"
+
+#include "config.h"
+#include "pal/dbgmsg.h"
+#include "pal/cruntime.h"
+#include "pal/critsect.h"
+#include "pal/file.h"
+#include "pal/environ.h"
+
+/* standard headers */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h> /* for pthread_self */
+#include <errno.h>
+#include <dirent.h>
+#include <dlfcn.h>
+
+/* <stdarg.h> needs to be included after "palinternal.h" to avoid name
+ collision for va_start and va_end */
+#include <stdarg.h>
+
+using namespace CorUnix;
+
+/* append mode file I/O is safer */
+#define _PAL_APPEND_DBG_OUTPUT_
+
+#if defined(_PAL_APPEND_DBG_OUTPUT_)
+static const char FOPEN_FLAGS[] = "at";
+#else
+static const char FOPEN_FLAGS[] = "wt";
+#endif
+
+/* number of ENTRY nesting levels to indicate with a '.' */
+#define MAX_NESTING 50
+
+/* size of output buffer (arbitrary) */
+#define DBG_BUFFER_SIZE 20000
+
+/* global and static variables */
+
+LPCWSTR W16_NULLSTRING = (LPCWSTR) "N\0U\0L\0L\0\0";
+
+DWORD dbg_channel_flags[DCI_LAST];
+BOOL g_Dbg_asserts_enabled;
+
+/* we must use stdio functions directly rather that rely on PAL functions for
+ output, because those functions do tracing and we need to avoid recursion */
+FILE *output_file = NULL;
+
+/* master switch for debug channel enablement, to be modified by debugger */
+Volatile<BOOL> dbg_master_switch = TRUE;
+
+
+static const char *dbg_channel_names[]=
+{
+ "PAL",
+ "LOADER",
+ "HANDLE",
+ "SHMEM",
+ "PROCESS",
+ "THREAD",
+ "EXCEPT",
+ "CRT",
+ "UNICODE",
+ "ARCH",
+ "SYNC",
+ "FILE",
+ "VIRTUAL",
+ "MEM",
+ "SOCKET",
+ "DEBUG",
+ "LOCALE",
+ "MISC",
+ "MUTEX",
+ "CRITSEC",
+ "POLL",
+ "CRYPT",
+ "SHFOLDER"
+#ifdef FEATURE_PAL_SXS
+ , "SXS"
+#endif // FEATURE_PAL_SXS
+};
+
+static const char *dbg_level_names[]=
+{
+ "ENTRY",
+ "TRACE",
+ "WARN",
+ "ERROR",
+ "ASSERT",
+ "EXIT"
+};
+
+static const char ENV_FILE[]="PAL_API_TRACING";
+static const char ENV_CHANNELS[]="PAL_DBG_CHANNELS";
+static const char ENV_ASSERTS[]="PAL_DISABLE_ASSERTS";
+static const char ENV_ENTRY_LEVELS[]="PAL_API_LEVELS";
+
+/* per-thread storage for ENTRY tracing level */
+static pthread_key_t entry_level_key;
+
+/* entry level limitation */
+static int max_entry_level;
+
+/* character to use for ENTRY indentation */
+static const char INDENT_CHAR = '.';
+
+static BOOL DBG_get_indent(DBG_LEVEL_ID level, const char *format,
+ char *indent_string);
+
+static CRITICAL_SECTION fprintf_crit_section;
+
+/* Function definitions */
+
+/*++
+Function :
+ DBG_init_channels
+
+ Parse environment variables PAL_DBG_CHANNELS and PAL_API_TRACING for debug
+ channel settings; initialize static variables.
+
+ (no parameters, no return value)
+--*/
+BOOL DBG_init_channels(void)
+{
+ INT i;
+ LPSTR env_string;
+ LPSTR env_workstring;
+ LPSTR env_pcache;
+ LPSTR entry_ptr;
+ LPSTR level_ptr;
+ CHAR plus_or_minus;
+ DWORD flag_mask = 0;
+ int ret;
+
+ InternalInitializeCriticalSection(&fprintf_crit_section);
+
+ /* output only asserts by default [only affects no-vararg-support case; if
+ we have varargs, these flags aren't even checked for ASSERTs] */
+ for(i=0;i<DCI_LAST;i++)
+ dbg_channel_flags[i]=1<<DLI_ASSERT;
+
+ /* parse PAL_DBG_CHANNELS environment variable */
+
+ env_string = EnvironGetenv(ENV_CHANNELS);
+ env_pcache = env_workstring = env_string;
+
+ while(env_workstring)
+ {
+ entry_ptr=env_workstring;
+
+ /* find beginning of next entry */
+ while((*entry_ptr != '\0') &&(*entry_ptr != '+') && (*entry_ptr != '-'))
+ {
+ entry_ptr++;
+ }
+
+ /* break if end of string is reached */
+ if(*entry_ptr == '\0')
+ {
+ break;
+ }
+
+ plus_or_minus=*entry_ptr++;
+
+ /* find end of entry; if strchr returns NULL, we have reached the end
+ of the string and we will leave the loop at the end of this pass. */
+ env_workstring=strchr(entry_ptr,':');
+
+ /* NULL-terminate entry, make env_string point to rest of string */
+ if(env_workstring)
+ {
+ *env_workstring++='\0';
+ }
+
+ /* find period that separates channel name from level name */
+ level_ptr=strchr(entry_ptr,'.');
+
+ /* an entry with no period is illegal : ignore it */
+ if(!level_ptr)
+ {
+ continue;
+ }
+ /* NULL-terminate channel name, make level_ptr point to the level name */
+ *level_ptr++='\0';
+
+ /* build the flag mask based on requested level */
+
+ /* if "all" level is specified, we want to open/close all levels at
+ once, so mask is either all ones or all zeroes */
+ if(!strcmp(level_ptr,"all"))
+ {
+ if(plus_or_minus=='+')
+ {
+ flag_mask=0xFFFF; /* OR this to open all levels */
+ }
+ else
+ {
+ flag_mask=0; /* AND this to close all levels*/
+ }
+ }
+ else
+ {
+ for(i=0;i<DLI_LAST;i++)
+ {
+ if(!strcmp(level_ptr,dbg_level_names[i]))
+ {
+ if(plus_or_minus=='+')
+ {
+ flag_mask=1<<i; /* OR this to open the level */
+ }
+ else
+ {
+ flag_mask=~(1<<i); /* AND this to close the level */
+ }
+ break;
+ }
+ }
+ /* didn't find a matching level : skip it. */
+ if(i==DLI_LAST)
+ {
+ continue;
+ }
+ }
+
+ /* Set EXIT and ENTRY channels to be identical */
+ if(!(flag_mask & (1<<DLI_ENTRY)))
+ {
+ flag_mask = flag_mask & (~(1<<DLI_EXIT));
+ }
+ else
+ {
+ flag_mask = flag_mask | (1<<DLI_EXIT);
+ }
+
+ /* apply the flag mask to the specified channel */
+
+ /* if "all" channel is specified, apply mask to all channels */
+ if(!strcmp(entry_ptr,"all"))
+ {
+ if(plus_or_minus=='+')
+ {
+ for(i=0;i<DCI_LAST;i++)
+ {
+ dbg_channel_flags[i] |= flag_mask; /* OR to open levels*/
+ }
+ }
+ else
+ {
+ for(i=0;i<DCI_LAST;i++)
+ {
+ dbg_channel_flags[i] &= flag_mask; /* AND to close levels */
+ }
+ }
+ }
+ else
+ {
+ for(i=0;i<DCI_LAST;i++)
+ {
+ if(!strcmp(entry_ptr,dbg_channel_names[i]))
+ {
+ if(plus_or_minus=='+')
+ {
+ dbg_channel_flags[i] |= flag_mask;
+ }
+ else
+ {
+ dbg_channel_flags[i] &= flag_mask;
+ }
+
+ break;
+ }
+ }
+ /* ignore the entry if the channel name is unknown */
+ }
+ /* done processing this entry; on to the next. */
+ }
+ PAL_free(env_pcache);
+
+ /* select output file */
+ env_string = EnvironGetenv(ENV_FILE);
+ if(env_string && *env_string!='\0')
+ {
+ if(!strcmp(env_string, "stderr"))
+ {
+ output_file = stderr;
+ }
+ else if(!strcmp(env_string, "stdout"))
+ {
+ output_file = stdout;
+ }
+ else
+ {
+ output_file = fopen(env_string,FOPEN_FLAGS);
+
+ /* if file can't be opened, default to stderr */
+ if(!output_file)
+ {
+ output_file = stderr;
+ fprintf(stderr, "Can't open %s for writing : debug messages "
+ "will go to stderr. Check your PAL_API_TRACING "
+ "variable!\n", env_string);
+ }
+ }
+ }
+ else
+ {
+ output_file = stderr; /* output to stderr by default */
+ }
+
+ if(env_string)
+ {
+ PAL_free(env_string);
+ }
+
+ /* see if we need to disable assertions */
+ env_string = EnvironGetenv(ENV_ASSERTS);
+ if(env_string && 0 == strcmp(env_string,"1"))
+ {
+ g_Dbg_asserts_enabled = FALSE;
+ }
+ else
+ {
+ g_Dbg_asserts_enabled = TRUE;
+ }
+
+ if(env_string)
+ {
+ PAL_free(env_string);
+ }
+
+ /* select ENTRY level limitation */
+ env_string = EnvironGetenv(ENV_ENTRY_LEVELS);
+ if(env_string)
+ {
+ max_entry_level = atoi(env_string);
+ PAL_free(env_string);
+ }
+ else
+ {
+ max_entry_level = 1;
+ }
+
+ /* if necessary, allocate TLS key for entry nesting level */
+ if(0 != max_entry_level)
+ {
+ if ((ret = pthread_key_create(&entry_level_key,NULL)) != 0)
+ {
+ fprintf(stderr, "ERROR : pthread_key_create() failed error:%d (%s)\n",
+ ret, strerror(ret));
+ DeleteCriticalSection(&fprintf_crit_section);;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*++
+Function :
+ DBG_close_channels
+
+ Stop outputting debug messages by closing the associated file.
+
+ (no parameters, no return value)
+--*/
+void DBG_close_channels()
+{
+ if(output_file && output_file != stderr && output_file != stdout)
+ {
+ if (fclose(output_file) != 0)
+ {
+ fprintf(stderr, "ERROR : fclose() failed errno:%d (%s)\n",
+ errno, strerror(errno));
+ }
+ }
+
+ output_file = NULL;
+
+ DeleteCriticalSection(&fprintf_crit_section);
+
+ /* if necessary, release TLS key for entry nesting level */
+ if(0 != max_entry_level)
+ {
+ int retval;
+
+ retval = pthread_key_delete(entry_level_key);
+ if(0 != retval)
+ {
+ fprintf(stderr, "ERROR : pthread_key_delete() returned %d! (%s)\n",
+ retval, strerror(retval));
+ }
+ }
+}
+
+
+#ifdef FEATURE_PAL_SXS
+static const void *DBG_get_module_id()
+{
+ static const void *s_module_id = NULL;
+ if (s_module_id == NULL)
+ {
+ Dl_info dl_info;
+ if (dladdr((void *) DBG_get_module_id, &dl_info) == 0 || dl_info.dli_sname == NULL)
+ {
+ s_module_id = (void *) -1;
+ }
+ else
+ {
+ s_module_id = dl_info.dli_fbase;
+ }
+ }
+ return s_module_id;
+}
+
+#define MODULE_ID DBG_get_module_id,
+#define MODULE_FORMAT "-%p"
+#else
+#define MODULE_ID
+#define MODULE_FORMAT
+#endif // FEATURE_PAL_SXS
+
+
+/*++
+Function :
+ DBG_printf_gcc
+
+ Internal function for debug channels; don't use.
+ This function outputs a complete debug message, including the function name.
+
+Parameters :
+ DBG_CHANNEL_ID channel : debug channel to use
+ DBG_LEVEL_ID level : debug message level
+ BOOL bHeader : whether or not to output message header (thread id, etc)
+ LPSTR function : current function
+ LPSTR file : current file
+ INT line : line number
+ LPSTR format, ... : standard printf parameter list.
+
+Return Value :
+ always 1.
+
+Notes :
+ This version is for gnu compilers that support variable-argument macros
+ and the __FUNCTION__ pseudo-macro.
+
+--*/
+int DBG_printf_gcc(DBG_CHANNEL_ID channel, DBG_LEVEL_ID level, BOOL bHeader,
+ LPCSTR function, LPCSTR file, INT line, LPCSTR format, ...)
+{
+ CHAR *buffer = (CHAR*)alloca(DBG_BUFFER_SIZE);
+ CHAR indent[MAX_NESTING+1];
+ LPSTR buffer_ptr;
+ INT output_size;
+ va_list args;
+ void *thread_id;
+ int old_errno = 0;
+ CPalThread *pthrCurrent = InternalGetCurrentThread();
+
+ old_errno = errno;
+
+ if(!DBG_get_indent(level, format, indent))
+ {
+ return 1;
+ }
+
+ thread_id = (void *)THREADSilentGetCurrentThreadId();
+
+ if(bHeader)
+ {
+ /* Print file instead of function name for ENTRY messages, because those
+ already include the function name */
+ /* also print file name for ASSERTs, to match Win32 behavior */
+ if( DLI_ENTRY == level || DLI_ASSERT == level || DLI_EXIT == level)
+ {
+ output_size=snprintf(buffer, DBG_BUFFER_SIZE,
+ "{%p" MODULE_FORMAT "} %-5s [%-7s] at %s.%d: ",
+ thread_id, MODULE_ID
+ dbg_level_names[level], dbg_channel_names[channel], file, line);
+ }
+ else
+ {
+ output_size=snprintf(buffer, DBG_BUFFER_SIZE,
+ "{%p" MODULE_FORMAT "} %-5s [%-7s] at %s.%d: ",
+ thread_id, MODULE_ID
+ dbg_level_names[level], dbg_channel_names[channel], function, line);
+ }
+
+ if(output_size + 1 > DBG_BUFFER_SIZE)
+ {
+ fprintf(stderr, "ERROR : buffer overflow in DBG_printf_gcc");
+ return 1;
+ }
+
+ buffer_ptr=buffer+output_size;
+ }
+ else
+ {
+ buffer_ptr = buffer;
+ output_size = 0;
+ }
+
+ va_start(args, format);
+
+ output_size+=Silent_PAL_vsnprintf(buffer_ptr, DBG_BUFFER_SIZE-output_size,
+ format, args);
+ va_end(args);
+
+ if( output_size > DBG_BUFFER_SIZE )
+ {
+ fprintf(stderr, "ERROR : buffer overflow in DBG_printf_gcc");
+ }
+
+ /* Use a Critical section before calling printf code to
+ avoid holding a libc lock while another thread is calling
+ SuspendThread on this one. */
+
+ InternalEnterCriticalSection(pthrCurrent, &fprintf_crit_section);
+ fprintf( output_file, "%s%s", indent, buffer );
+ InternalLeaveCriticalSection(pthrCurrent, &fprintf_crit_section);
+
+ /* flush the output to file */
+ if ( fflush(output_file) != 0 )
+ {
+ fprintf(stderr, "ERROR : fflush() failed errno:%d (%s)\n",
+ errno, strerror(errno));
+ }
+
+ // Some systems support displaying a GUI dialog. We attempt this only for asserts.
+ if ( level == DLI_ASSERT )
+ PAL_DisplayDialog("PAL ASSERT", buffer);
+
+ if ( old_errno != errno )
+ {
+ fprintf( stderr,"ERROR: errno changed by DBG_printf_gcc\n" );
+ errno = old_errno;
+ }
+
+ return 1;
+}
+
+/*++
+Function :
+ DBG_printf_c99
+
+ Internal function for debug channels; don't use.
+ This function outputs a complete debug message, without function name.
+
+Parameters :
+ DBG_CHANNEL_ID channel : debug channel to use
+ DBG_LEVEL_ID level : debug message level
+ BOOL bHeader : whether or not to output message header (thread id, etc)
+ LPSTR file : current file
+ INT line : line number
+ LPSTR format, ... : standard printf parameter list.
+
+Return Value :
+ always 1.
+
+Notes :
+ This version is for compilers that support the C99 flavor of
+ variable-argument macros but not the gnu flavor, and do not support the
+ __FUNCTION__ pseudo-macro.
+
+--*/
+int DBG_printf_c99(DBG_CHANNEL_ID channel, DBG_LEVEL_ID level, BOOL bHeader,
+ LPSTR file, INT line, LPSTR format, ...)
+{
+ CHAR *buffer = (CHAR*)alloca(DBG_BUFFER_SIZE);
+ CHAR indent[MAX_NESTING+1];
+ LPSTR buffer_ptr;
+ INT output_size;
+ va_list args;
+ static INT call_count=0;
+ void *thread_id;
+ int old_errno = 0;
+ CPalThread *pthrCurrent = InternalGetCurrentThread();
+
+ old_errno = errno;
+
+ if(!DBG_get_indent(level, format, indent))
+ {
+ return 1;
+ }
+
+ thread_id= (void *)THREADSilentGetCurrentThreadId();
+
+ if(bHeader)
+ {
+ output_size=snprintf(buffer, DBG_BUFFER_SIZE,
+ "{%p" MODULE_FORMAT "} %-5s [%-7s] at %s.%d: ", thread_id, MODULE_ID
+ dbg_level_names[level], dbg_channel_names[channel],
+ file, line);
+
+ if(output_size + 1 > DBG_BUFFER_SIZE)
+ {
+ fprintf(stderr, "ERROR : buffer overflow in DBG_printf_gcc");
+ return 1;
+ }
+
+ buffer_ptr=buffer+output_size;
+ }
+ else
+ {
+ output_size = 0;
+ buffer_ptr = buffer;
+ }
+
+ va_start(args, format);
+ output_size+=Silent_PAL_vsnprintf(buffer_ptr, DBG_BUFFER_SIZE-output_size,
+ format, args);
+ va_end(args);
+
+ if(output_size>DBG_BUFFER_SIZE)
+ fprintf(stderr, "ERROR : buffer overflow in DBG_printf_c99");
+
+ /* Use a Critical section before calling printf code to
+ avoid holding a libc lock while another thread is calling
+ SuspendThread on this one. */
+
+ InternalEnterCriticalSection(pthrCurrent, &fprintf_crit_section);
+ fprintf( output_file, "%s", buffer );
+ InternalLeaveCriticalSection(pthrCurrent, &fprintf_crit_section);
+
+ /* flush the output to file every once in a while */
+ call_count++;
+ if(call_count>5)
+ {
+ call_count=0;
+ if ( fflush(output_file) != 0 )
+ {
+ fprintf(stderr, "ERROR : fflush() failed errno:%d (%s)\n",
+ errno, strerror(errno));
+ }
+ }
+
+ if ( old_errno != errno )
+ {
+ fprintf( stderr, "ERROR: DBG_printf_c99 changed the errno.\n" );
+ errno = old_errno;
+ }
+
+ return 1;
+}
+
+/*++
+Function :
+ DBG_get_indent
+
+ generate an indentation string to be used for message output
+
+Parameters :
+ DBG_LEVEL_ID level : level of message (DLI_ENTRY, etc)
+ const char *format : printf format string of message
+ char *indent_string : destination for indentation string
+
+Return value :
+ TRUE if output can proceed, FALSE otherwise
+
+Notes:
+As a side-effect, this function updates the ENTRY nesting level for the current
+thread : it decrements it if 'format' contains the string 'return', increments
+it otherwise (but only if 'level' is DLI_ENTRY). The function will return
+FALSE if the current nesting level is beyond our treshold (max_nesting_level);
+it always returns TRUE for other message levels
+--*/
+static BOOL DBG_get_indent(DBG_LEVEL_ID level, const char *format,
+ char *indent_string)
+{
+ int ret;
+
+ /* determine whether to output an ENTRY line */
+ if(DLI_ENTRY == level||DLI_EXIT == level)
+ {
+ if(0 != max_entry_level)
+ {
+ INT_PTR nesting;
+
+ /* Determine if this is an entry or an
+ exit */
+ if(DLI_EXIT == level)
+ {
+ nesting = (INT_PTR) pthread_getspecific(entry_level_key);
+ /* avoid going negative */
+ if(nesting != 0)
+ {
+ nesting--;
+ if ((ret = pthread_setspecific(entry_level_key,
+ (LPVOID)nesting)) != 0)
+ {
+ fprintf(stderr, "ERROR : pthread_setspecific() failed "
+ "error:%d (%s)\n", ret, strerror(ret));
+ }
+ }
+ }
+ else
+ {
+ nesting = (INT_PTR) pthread_getspecific(entry_level_key);
+
+ if ((ret = pthread_setspecific(entry_level_key,
+ (LPVOID)(nesting+1))) != 0)
+ {
+ fprintf(stderr, "ERROR : pthread_setspecific() failed "
+ "error:%d (%s)\n", ret, strerror(ret));
+ }
+ }
+
+ /* see if we're past the level treshold */
+ if(nesting >= max_entry_level)
+ {
+ return FALSE;
+ }
+
+ /* generate indentation string */
+ if(MAX_NESTING < nesting)
+ {
+ nesting = MAX_NESTING;
+ }
+ memset(indent_string,INDENT_CHAR ,nesting);
+ indent_string[nesting] = '\0';
+ }
+ else
+ {
+ indent_string[0] = '\0';
+ }
+ }
+ else
+ {
+ indent_string[0] = '\0';
+ }
+ return TRUE;
+}
+
+/*++
+Function :
+ DBG_change_entrylevel
+
+ retrieve current ENTRY nesting level and [optionnally] modify it
+
+Parameters :
+ int new_level : value to which the nesting level must be set, or -1
+
+Return value :
+ nesting level at the time the function was called
+
+Notes:
+if new_level is -1, the nesting level will not be modified
+--*/
+int DBG_change_entrylevel(int new_level)
+{
+ int old_level;
+ int ret;
+
+ if(0 == max_entry_level)
+ {
+ return 0;
+ }
+ old_level = PtrToInt(pthread_getspecific(entry_level_key));
+ if(-1 != new_level)
+ {
+ if ((ret = pthread_setspecific(entry_level_key,(LPVOID)(IntToPtr(new_level)))) != 0)
+ {
+ fprintf(stderr, "ERROR : pthread_setspecific() failed "
+ "error:%d (%s)\n", ret, strerror(ret));
+ }
+ }
+ return old_level;
+}
+
+#if _DEBUG && defined(__APPLE__)
+/*++
+Function:
+ DBG_ShouldCheckStackAlignment
+
+ Wires up stack alignment checks (debug builds only)
+--*/
+static const char * PAL_CHECK_ALIGNMENT_MODE = "PAL_CheckAlignmentMode";
+enum CheckAlignmentMode
+{
+ // special value to indicate we've not initialized yet
+ CheckAlignment_Uninitialized = -1,
+
+ CheckAlignment_Off = 0,
+ CheckAlignment_On = 1,
+
+ CheckAlignment_Default = CheckAlignment_On
+};
+
+bool DBG_ShouldCheckStackAlignment()
+{
+ static CheckAlignmentMode caMode = CheckAlignment_Uninitialized;
+
+ if (caMode == CheckAlignment_Uninitialized)
+ {
+ char* checkAlignmentSettings;
+ bool shouldFreeCheckAlignmentSettings = false;
+ if (palEnvironment == nullptr)
+ {
+ // This function might be called before the PAL environment is initialized.
+ // In this case, use the system getenv instead.
+ checkAlignmentSettings = ::getenv(PAL_CHECK_ALIGNMENT_MODE);
+ }
+ else
+ {
+ checkAlignmentSettings = EnvironGetenv(PAL_CHECK_ALIGNMENT_MODE);
+ shouldFreeCheckAlignmentSettings = true;
+ }
+
+ caMode = checkAlignmentSettings ?
+ (CheckAlignmentMode)atoi(checkAlignmentSettings) : CheckAlignment_Default;
+
+ if (checkAlignmentSettings && shouldFreeCheckAlignmentSettings)
+ {
+ free(checkAlignmentSettings);
+ }
+ }
+
+ return caMode == CheckAlignment_On;
+}
+#endif // _DEBUG && __APPLE__
+
+#ifdef __APPLE__
+#include "CoreFoundation/CFUserNotification.h"
+#include "CoreFoundation/CFString.h"
+#include "Security/AuthSession.h"
+
+static const char * PAL_DISPLAY_DIALOG = "PAL_DisplayDialog";
+enum DisplayDialogMode
+{
+ DisplayDialog_Uninitialized = -1,
+
+ DisplayDialog_Suppress = 0,
+ DisplayDialog_Show = 1,
+
+ DisplayDialog_Default = DisplayDialog_Suppress,
+};
+
+/*++
+Function :
+ PAL_DisplayDialog
+
+ Display a simple modal dialog with an alert icon and a single OK button. Caller supplies the title of the
+ dialog and the main text. The dialog is displayed only if the PAL_DisplayDialog environment
+ variable is set to the value "1" and the session has access to the display.
+
+--*/
+void PAL_DisplayDialog(const char *szTitle, const char *szText)
+{
+ static DisplayDialogMode dispDialog = DisplayDialog_Uninitialized;
+
+ if (dispDialog == DisplayDialog_Uninitialized)
+ {
+ char* displayDialog = EnvironGetenv(PAL_DISPLAY_DIALOG);
+ if (displayDialog)
+ {
+ int i = atoi(displayDialog);
+ free(displayDialog);
+
+ switch (i)
+ {
+ case 0:
+ dispDialog = DisplayDialog_Suppress;
+ break;
+
+ case 1:
+ dispDialog = DisplayDialog_Show;
+ break;
+
+ default:
+ // Asserting here would just be re-entrant. :/
+ dispDialog = DisplayDialog_Default;
+ break;
+ }
+ }
+ else
+ dispDialog = DisplayDialog_Default;
+
+ if (dispDialog == DisplayDialog_Show)
+ {
+ // We may not be allowed to show.
+ OSStatus osstatus;
+ SecuritySessionId secSession;
+ SessionAttributeBits secSessionInfo;
+
+ osstatus = SessionGetInfo(callerSecuritySession, &secSession, &secSessionInfo);
+ if (noErr != osstatus || (secSessionInfo & sessionHasGraphicAccess) == 0)
+ dispDialog = DisplayDialog_Suppress;
+ }
+ }
+
+ if (dispDialog == DisplayDialog_Suppress)
+ return;
+
+ CFStringRef cfsTitle = CFStringCreateWithCString(kCFAllocatorDefault,
+ szTitle,
+ kCFStringEncodingUTF8);
+ if (cfsTitle != NULL)
+ {
+ CFStringRef cfsText = CFStringCreateWithCString(kCFAllocatorDefault,
+ szText,
+ kCFStringEncodingUTF8);
+ if (cfsText != NULL)
+ {
+ CFOptionFlags response;
+ CFUserNotificationDisplayAlert(0, // Never time-out, wait for user to hit 'OK'
+ 0, // No flags
+ NULL, // Default icon
+ NULL, // Default sound
+ NULL, // No-localization support for text
+ cfsTitle, // Title for dialog
+ cfsText, // The actual alert text
+ NULL, // Default default button title ('OK')
+ NULL, // No alternate button
+ NULL, // No third button
+ &response); // User's response (discarded)
+ CFRelease(cfsText);
+ }
+ CFRelease(cfsTitle);
+ }
+}
+
+/*++
+Function :
+ PAL_DisplayDialogFormatted
+
+ As above but takes a printf-style format string and insertion values to form the main text.
+
+--*/
+void PAL_DisplayDialogFormatted(const char *szTitle, const char *szTextFormat, ...)
+{
+ va_list args;
+
+ va_start(args, szTextFormat);
+
+ const int cchBuffer = 4096;
+ char *szBuffer = (char*)alloca(cchBuffer);
+ PAL__vsnprintf(szBuffer, cchBuffer, szTextFormat, args);
+ PAL_DisplayDialog(szTitle, szBuffer);
+
+ va_end(args);
+}
+#endif // __APPLE__
diff --git a/src/pal/src/misc/environ.cpp b/src/pal/src/misc/environ.cpp
new file mode 100644
index 0000000000..fed7b69f38
--- /dev/null
+++ b/src/pal/src/misc/environ.cpp
@@ -0,0 +1,1096 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ environ.cpp
+
+Abstract:
+
+ Implementation of functions manipulating environment variables.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/critsect.h"
+#include "pal/dbgmsg.h"
+#include "pal/environ.h"
+#include "pal/malloc.hpp"
+
+#if HAVE_CRT_EXTERNS_H
+#include <crt_externs.h>
+#endif
+
+#include <stdlib.h>
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+char **palEnvironment = nullptr;
+int palEnvironmentCount = 0;
+int palEnvironmentCapacity = 0;
+
+CRITICAL_SECTION gcsEnvironment;
+
+/*++
+Function:
+ GetEnvironmentVariableA
+
+The GetEnvironmentVariable function retrieves the value of the
+specified variable from the environment block of the calling
+process. The value is in the form of a null-terminated string of
+characters.
+
+Parameters
+
+lpName
+ [in] Pointer to a null-terminated string that specifies the environment variable.
+lpBuffer
+ [out] Pointer to a buffer to receive the value of the specified environment variable.
+nSize
+ [in] Specifies the size, in TCHARs, of the buffer pointed to by the lpBuffer parameter.
+
+Return Values
+
+If the function succeeds, the return value is the number of TCHARs
+stored into the buffer pointed to by lpBuffer, not including the
+terminating null character.
+
+If the specified environment variable name was not found in the
+environment block for the current process, the return value is zero.
+
+If the buffer pointed to by lpBuffer is not large enough, the return
+value is the buffer size, in TCHARs, required to hold the value string
+and its terminating null character.
+
+--*/
+DWORD
+PALAPI
+GetEnvironmentVariableA(
+ IN LPCSTR lpName,
+ OUT LPSTR lpBuffer,
+ IN DWORD nSize)
+{
+ char *value;
+ DWORD dwRet = 0;
+
+ PERF_ENTRY(GetEnvironmentVariableA);
+ ENTRY("GetEnvironmentVariableA(lpName=%p (%s), lpBuffer=%p, nSize=%u)\n",
+ lpName ? lpName : "NULL",
+ lpName ? lpName : "NULL", lpBuffer, nSize);
+
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+
+ if (lpName == nullptr)
+ {
+ ERROR("lpName is null\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ if (lpName[0] == 0)
+ {
+ TRACE("lpName is an empty string\n", lpName);
+ SetLastError(ERROR_ENVVAR_NOT_FOUND);
+ goto done;
+ }
+
+ if (strchr(lpName, '=') != nullptr)
+ {
+ // GetEnvironmentVariable doesn't permit '=' in variable names.
+ value = nullptr;
+ }
+ else
+ {
+ // Enter the environment critical section so that we can safely get
+ // the environment variable value without EnvironGetenv making an
+ // intermediate copy. We will just copy the string to the output
+ // buffer anyway, so just stay in the critical section until then.
+ InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
+
+ value = EnvironGetenv(lpName, /* copyValue */ FALSE);
+
+ if (value != nullptr)
+ {
+ DWORD valueLength = strlen(value);
+ if (valueLength < nSize)
+ {
+ strcpy_s(lpBuffer, nSize, value);
+ dwRet = valueLength;
+ }
+ else
+ {
+ dwRet = valueLength + 1;
+ }
+
+ SetLastError(ERROR_SUCCESS);
+ }
+
+ InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
+ }
+
+ if (value == nullptr)
+ {
+ TRACE("%s is not found\n", lpName);
+ SetLastError(ERROR_ENVVAR_NOT_FOUND);
+ }
+
+done:
+
+ LOGEXIT("GetEnvironmentVariableA returns DWORD 0x%x\n", dwRet);
+ PERF_EXIT(GetEnvironmentVariableA);
+ return dwRet;
+}
+
+/*++
+Function:
+ GetEnvironmentVariableW
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+GetEnvironmentVariableW(
+ IN LPCWSTR lpName,
+ OUT LPWSTR lpBuffer,
+ IN DWORD nSize)
+{
+ CHAR *inBuff = nullptr;
+ CHAR *outBuff = nullptr;
+ INT inBuffSize;
+ DWORD size = 0;
+
+ PERF_ENTRY(GetEnvironmentVariableW);
+ ENTRY("GetEnvironmentVariableW(lpName=%p (%S), lpBuffer=%p, nSize=%u)\n",
+ lpName ? lpName : W16_NULLSTRING,
+ lpName ? lpName : W16_NULLSTRING, lpBuffer, nSize);
+
+ inBuffSize = WideCharToMultiByte(CP_ACP, 0, lpName, -1,
+ inBuff, 0, nullptr, nullptr);
+ if (0 == inBuffSize)
+ {
+ ERROR("lpName has to be a valid parameter\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ inBuff = (CHAR *)PAL_malloc(inBuffSize);
+ if (inBuff == nullptr)
+ {
+ ERROR("malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+
+ if (nSize)
+ {
+ outBuff = (CHAR *)PAL_malloc(nSize*2);
+ if (outBuff == nullptr)
+ {
+ ERROR("malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ }
+
+ if (0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1, inBuff,
+ inBuffSize, nullptr, nullptr))
+ {
+ ASSERT("WideCharToMultiByte failed!\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ size = GetEnvironmentVariableA(inBuff, outBuff, nSize);
+ if (size > nSize)
+ {
+ TRACE("Insufficient buffer\n");
+ }
+ else if (size == 0)
+ {
+ // handle error in GetEnvironmentVariableA
+ }
+ else
+ {
+ size = MultiByteToWideChar(CP_ACP, 0, outBuff, -1, lpBuffer, nSize);
+ if (0 != size)
+ {
+ // -1 for the null.
+ size--;
+ }
+ else
+ {
+ ASSERT("MultiByteToWideChar failed!\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ size = 0;
+ *lpBuffer = '\0';
+ }
+ }
+
+done:
+ PAL_free(outBuff);
+ PAL_free(inBuff);
+
+ LOGEXIT("GetEnvironmentVariableW returns DWORD 0x%x\n", size);
+ PERF_EXIT(GetEnvironmentVariableW);
+
+ return size;
+}
+
+/*++
+Function:
+ SetEnvironmentVariableW
+
+The SetEnvironmentVariable function sets the value of an environment
+variable for the current process.
+
+Parameters
+
+lpName
+ [in] Pointer to a null-terminated string that specifies the
+ environment variable whose value is being set. The operating
+ system creates the environment variable if it does not exist
+ and lpValue is not null.
+lpValue
+ [in] Pointer to a null-terminated string containing the new
+ value of the specified environment variable. If this parameter
+ is null, the variable is deleted from the current process's
+ environment.
+
+Return Values
+
+If the function succeeds, the return value is nonzero.
+
+If the function fails, the return value is zero. To get extended error
+information, call GetLastError.
+
+Remarks
+
+This function has no effect on the system environment variables or the
+environment variables of other processes.
+
+--*/
+BOOL
+PALAPI
+SetEnvironmentVariableW(
+ IN LPCWSTR lpName,
+ IN LPCWSTR lpValue)
+{
+ PCHAR name = nullptr;
+ PCHAR value = nullptr;
+ INT nameSize = 0;
+ INT valueSize = 0;
+ BOOL bRet = FALSE;
+
+ PERF_ENTRY(SetEnvironmentVariableW);
+ ENTRY("SetEnvironmentVariableW(lpName=%p (%S), lpValue=%p (%S))\n",
+ lpName?lpName:W16_NULLSTRING,
+ lpName?lpName:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING);
+
+ if ((nameSize = WideCharToMultiByte(CP_ACP, 0, lpName, -1, name, 0,
+ nullptr, nullptr)) == 0)
+ {
+ ERROR("WideCharToMultiByte failed\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ name = (PCHAR)PAL_malloc(sizeof(CHAR)* nameSize);
+ if (name == nullptr)
+ {
+ ERROR("malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+
+ if (0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1,
+ name, nameSize, nullptr, nullptr))
+ {
+ ASSERT("WideCharToMultiByte returned 0\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ if (lpValue != nullptr)
+ {
+ if ((valueSize = WideCharToMultiByte(CP_ACP, 0, lpValue, -1, value,
+ 0, nullptr, nullptr)) == 0)
+ {
+ ERROR("WideCharToMultiByte failed\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ value = (PCHAR)PAL_malloc(sizeof(CHAR)*valueSize);
+
+ if (value == nullptr)
+ {
+ ERROR("malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+
+ if (0 == WideCharToMultiByte(CP_ACP, 0, lpValue, -1,
+ value, valueSize, nullptr, nullptr))
+ {
+ ASSERT("WideCharToMultiByte failed\n");
+ SetLastError( ERROR_INTERNAL_ERROR );
+ goto done;
+ }
+ }
+
+ bRet = SetEnvironmentVariableA(name, value);
+done:
+ PAL_free(value);
+ PAL_free(name);
+
+ LOGEXIT("SetEnvironmentVariableW returning BOOL %d\n", bRet);
+ PERF_EXIT(SetEnvironmentVariableW);
+ return bRet;
+}
+
+/*++
+Function:
+ GetEnvironmentStringsW
+
+The GetEnvironmentStrings function retrieves the environment block for
+the current process.
+
+Parameters
+
+This function has no parameters.
+
+Return Values
+
+The return value is a pointer to an environment block for the current process.
+
+Remarks
+
+The GetEnvironmentStrings function returns a pointer to the
+environment block of the calling process. This should be treated as a
+read-only block; do not modify it directly. Instead, use the
+GetEnvironmentVariable and SetEnvironmentVariable functions to
+retrieve or change the environment variables within this block. When
+the block is no longer needed, it should be freed by calling
+FreeEnvironmentStrings.
+
+--*/
+LPWSTR
+PALAPI
+GetEnvironmentStringsW(
+ VOID)
+{
+ WCHAR *wenviron = nullptr, *tempEnviron;
+ int i, len, envNum;
+
+ PERF_ENTRY(GetEnvironmentStringsW);
+ ENTRY("GetEnvironmentStringsW()\n");
+
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
+
+ envNum = 0;
+ len = 0;
+
+ /* get total length of the bytes that we need to allocate */
+ for (i = 0; palEnvironment[i] != 0; i++)
+ {
+ len = MultiByteToWideChar(CP_ACP, 0, palEnvironment[i], -1, wenviron, 0);
+ envNum += len;
+ }
+
+ wenviron = (WCHAR *)PAL_malloc(sizeof(WCHAR)* (envNum + 1));
+ if (wenviron == nullptr)
+ {
+ ERROR("malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto EXIT;
+ }
+
+ len = 0;
+ tempEnviron = wenviron;
+ for (i = 0; palEnvironment[i] != 0; i++)
+ {
+ len = MultiByteToWideChar(CP_ACP, 0, palEnvironment[i], -1, tempEnviron, envNum);
+ tempEnviron += len;
+ envNum -= len;
+ }
+
+ *tempEnviron = 0; /* Put an extra null at the end */
+
+ EXIT:
+ InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
+
+ LOGEXIT("GetEnvironmentStringsW returning %p\n", wenviron);
+ PERF_EXIT(GetEnvironmentStringsW);
+ return wenviron;
+}
+
+/*++
+Function:
+ GetEnvironmentStringsA
+
+See GetEnvironmentStringsW.
+
+--*/
+LPSTR
+PALAPI
+GetEnvironmentStringsA(
+ VOID)
+{
+ char *environ = nullptr, *tempEnviron;
+ int i, len, envNum;
+
+ PERF_ENTRY(GetEnvironmentStringsA);
+ ENTRY("GetEnvironmentStringsA()\n");
+
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
+
+ envNum = 0;
+ len = 0;
+
+ /* get total length of the bytes that we need to allocate */
+ for (i = 0; palEnvironment[i] != 0; i++)
+ {
+ len = strlen(palEnvironment[i]) + 1;
+ envNum += len;
+ }
+
+ environ = (char *)PAL_malloc(envNum + 1);
+ if (environ == nullptr)
+ {
+ ERROR("malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto EXIT;
+ }
+
+ len = 0;
+ tempEnviron = environ;
+ for (i = 0; palEnvironment[i] != 0; i++)
+ {
+ len = strlen(palEnvironment[i]) + 1;
+ memcpy(tempEnviron, palEnvironment[i], len);
+ tempEnviron += len;
+ envNum -= len;
+ }
+
+ *tempEnviron = 0; /* Put an extra null at the end */
+
+ EXIT:
+ InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
+
+ LOGEXIT("GetEnvironmentStringsA returning %p\n", environ);
+ PERF_EXIT(GetEnvironmentStringsA);
+ return environ;
+}
+
+/*++
+Function:
+ FreeEnvironmentStringsW
+
+The FreeEnvironmentStrings function frees a block of environment strings.
+
+Parameters
+
+lpszEnvironmentBlock [in] Pointer to a block of environment strings. The pointer to
+ the block must be obtained by calling the
+ GetEnvironmentStrings function.
+
+Return Values
+
+If the function succeeds, the return value is nonzero. If the
+function fails, the return value is zero. To get extended error
+information, call GetLastError.
+
+Remarks
+
+When GetEnvironmentStrings is called, it allocates memory for a block
+of environment strings. When the block is no longer needed, it should
+be freed by calling FreeEnvironmentStrings.
+
+--*/
+BOOL
+PALAPI
+FreeEnvironmentStringsW(
+ IN LPWSTR lpValue)
+{
+ PERF_ENTRY(FreeEnvironmentStringsW);
+ ENTRY("FreeEnvironmentStringsW(lpValue=%p (%S))\n", lpValue ? lpValue : W16_NULLSTRING, lpValue ? lpValue : W16_NULLSTRING);
+
+ if (lpValue != nullptr)
+ {
+ PAL_free(lpValue);
+ }
+
+ LOGEXIT("FreeEnvironmentStringW returning BOOL TRUE\n");
+ PERF_EXIT(FreeEnvironmentStringsW);
+ return TRUE;
+}
+
+/*++
+Function:
+ FreeEnvironmentStringsA
+
+See FreeEnvironmentStringsW.
+
+--*/
+BOOL
+PALAPI
+FreeEnvironmentStringsA(
+ IN LPSTR lpValue)
+{
+ PERF_ENTRY(FreeEnvironmentStringsA);
+ ENTRY("FreeEnvironmentStringsA(lpValue=%p (%s))\n", lpValue ? lpValue : "NULL", lpValue ? lpValue : "NULL");
+
+ if (lpValue != nullptr)
+ {
+ PAL_free(lpValue);
+ }
+
+ LOGEXIT("FreeEnvironmentStringA returning BOOL TRUE\n");
+ PERF_EXIT(FreeEnvironmentStringsA);
+ return TRUE;
+}
+
+/*++
+Function:
+ SetEnvironmentVariableA
+
+The SetEnvironmentVariable function sets the value of an environment
+variable for the current process.
+
+Parameters
+
+lpName
+ [in] Pointer to a null-terminated string that specifies the
+ environment variable whose value is being set. The operating
+ system creates the environment variable if it does not exist
+ and lpValue is not null.
+lpValue
+ [in] Pointer to a null-terminated string containing the new
+ value of the specified environment variable. If this parameter
+ is null, the variable is deleted from the current process's
+ environment.
+
+Return Values
+
+If the function succeeds, the return value is nonzero.
+
+If the function fails, the return value is zero. To get extended error
+information, call GetLastError.
+
+Remarks
+
+This function has no effect on the system environment variables or the
+environment variables of other processes.
+
+--*/
+BOOL
+PALAPI
+SetEnvironmentVariableA(
+ IN LPCSTR lpName,
+ IN LPCSTR lpValue)
+{
+
+ BOOL bRet = FALSE;
+ int nResult =0;
+ PERF_ENTRY(SetEnvironmentVariableA);
+ ENTRY("SetEnvironmentVariableA(lpName=%p (%s), lpValue=%p (%s))\n",
+ lpName ? lpName : "NULL", lpName ? lpName : "NULL",
+ lpValue ? lpValue : "NULL", lpValue ? lpValue : "NULL");
+
+ // exit if the input variable name is null
+ if ((lpName == nullptr) || (lpName[0] == 0))
+ {
+ ERROR("lpName is null\n");
+ goto done;
+ }
+
+ /* check if the input value is null and if so
+ * check if the input name is valid and delete
+ * the variable name from process environment */
+ if (lpValue == nullptr)
+ {
+ // We tell EnvironGetenv not to bother with making a copy of the
+ // value since we're not going to use it for anything interesting
+ // apart from checking whether it's null.
+ if ((lpValue = EnvironGetenv(lpName, /* copyValue */ FALSE)) == nullptr)
+ {
+ ERROR("Couldn't find environment variable (%s)\n", lpName);
+ SetLastError(ERROR_ENVVAR_NOT_FOUND);
+ goto done;
+ }
+
+ EnvironUnsetenv(lpName);
+ }
+ else
+ {
+ // All the conditions are met. Set the variable.
+ int iLen = strlen(lpName) + strlen(lpValue) + 2;
+ LPSTR string = (LPSTR) PAL_malloc(iLen);
+ if (string == nullptr)
+ {
+ bRet = FALSE;
+ ERROR("Unable to allocate memory\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+
+ sprintf_s(string, iLen, "%s=%s", lpName, lpValue);
+ nResult = EnvironPutenv(string, FALSE) ? 0 : -1;
+
+ PAL_free(string);
+ string = nullptr;
+
+ // If EnvironPutenv returns FALSE, it almost certainly failed to allocate memory.
+ if (nResult == -1)
+ {
+ bRet = FALSE;
+ ERROR("Unable to allocate memory\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto done;
+ }
+ }
+
+ bRet = TRUE;
+
+done:
+ LOGEXIT("SetEnvironmentVariableA returning BOOL %d\n", bRet);
+ PERF_EXIT(SetEnvironmentVariableA);
+ return bRet;
+}
+
+/*++
+Function:
+ ResizeEnvironment
+
+Resizes the PAL environment buffer.
+
+Parameters
+
+ newSize
+ [in] New size of palEnvironment
+
+Return Values
+
+ TRUE on success, FALSE otherwise
+
+--*/
+BOOL ResizeEnvironment(int newSize)
+{
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
+
+ BOOL ret = FALSE;
+ if (newSize >= palEnvironmentCount)
+ {
+ // If palEnvironment is null, realloc acts like malloc.
+ char **newEnvironment = (char**)realloc(palEnvironment, newSize * sizeof(char *));
+ if (newEnvironment != nullptr)
+ {
+ // realloc succeeded, so set palEnvironment to what it returned.
+ palEnvironment = newEnvironment;
+ palEnvironmentCapacity = newSize;
+ ret = TRUE;
+ }
+ }
+ else
+ {
+ ASSERT("ResizeEnvironment: newSize < current palEnvironmentCount!\n");
+ }
+
+ InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
+ return ret;
+}
+
+/*++
+Function:
+ EnvironUnsetenv
+
+Remove the environment variable with the given name from the PAL version
+of the environment if it exists.
+
+Parameters
+
+ name
+ [in] Name of variable to unset.
+
+--*/
+void EnvironUnsetenv(const char *name)
+{
+ int nameLength = strlen(name);
+
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
+
+ for (int i = 0; palEnvironment[i] != nullptr; ++i)
+ {
+ const char *equalsSignPosition = strchr(palEnvironment[i], '=');
+ if (equalsSignPosition == nullptr)
+ {
+ equalsSignPosition = palEnvironment[i] + strlen(palEnvironment[i]);
+ }
+
+ // Check whether the name of this variable has the same length as the one
+ // we're looking for before proceeding to compare them.
+ if (equalsSignPosition - palEnvironment[i] == nameLength)
+ {
+ if (memcmp(name, palEnvironment[i], nameLength) == 0)
+ {
+ // Free the string we're removing.
+ free(palEnvironment[i]);
+
+ // Move the last environment variable pointer here.
+ palEnvironment[i] = palEnvironment[palEnvironmentCount - 1];
+ palEnvironment[palEnvironmentCount - 1] = nullptr;
+
+ palEnvironmentCount--;
+ }
+ }
+ }
+
+ InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
+}
+
+/*++
+Function:
+ EnvironPutenv
+
+Add the environment variable string provided to the PAL version
+of the environment.
+
+Parameters
+
+ entry
+ [in] The variable string to add. Should be in the format
+ "name=value", where value might be empty (see below).
+ deleteIfEmpty
+ [in] If this is TRUE, "name=" will unset the 'name' variable.
+
+Return Values
+
+ TRUE on success, FALSE otherwise
+
+--*/
+BOOL EnvironPutenv(const char* entry, BOOL deleteIfEmpty)
+{
+ BOOL result = FALSE;
+
+ bool fOwningCS = false;
+
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+
+ const char *equalsSignPosition = strchr(entry, '=');
+ if (equalsSignPosition == entry || equalsSignPosition == nullptr)
+ {
+ // "=foo" and "foo" have no meaning
+ return FALSE;
+ }
+
+ char* copy = strdup(entry);
+ if (copy == nullptr)
+ {
+ return FALSE;
+ }
+
+ int nameLength = equalsSignPosition - entry;
+
+ if (equalsSignPosition[1] == '\0' && deleteIfEmpty)
+ {
+ // "foo=" removes foo from the environment in _putenv() on Windows.
+ // The same string can result from a call to SetEnvironmentVariable()
+ // with the empty string as the value, but in that case we want to
+ // set the variable's value to "". deleteIfEmpty will be FALSE in
+ // that case.
+
+ // Change '=' to '\0'
+ copy[nameLength] = '\0';
+
+ EnvironUnsetenv(copy);
+ free(copy);
+
+ result = TRUE;
+ }
+ else
+ {
+ // See if we are replacing an item or adding one.
+
+ InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
+ fOwningCS = true;
+
+ int i;
+ for (i = 0; palEnvironment[i] != nullptr; i++)
+ {
+ const char *existingEquals = strchr(palEnvironment[i], '=');
+ if (existingEquals == nullptr)
+ {
+ // The PAL screens out malformed strings, but the strings which
+ // came from the system during initialization might not have the
+ // equals sign. We treat the entire string as a name in that case.
+ existingEquals = palEnvironment[i] + strlen(palEnvironment[i]);
+ }
+
+ if (existingEquals - palEnvironment[i] == nameLength)
+ {
+ if (memcmp(entry, palEnvironment[i], nameLength) == 0)
+ {
+ free(palEnvironment[i]);
+ palEnvironment[i] = copy;
+
+ result = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (palEnvironment[i] == nullptr)
+ {
+ _ASSERTE(i < palEnvironmentCapacity);
+ if (i == (palEnvironmentCapacity - 1))
+ {
+ // We found the first null, but it's the last element in our environment
+ // block. We need more space in our environment, so let's double its size.
+ int resizeRet = ResizeEnvironment(palEnvironmentCapacity * 2);
+ if (resizeRet != TRUE)
+ {
+ free(copy);
+ goto done;
+ }
+ }
+
+ _ASSERTE(copy != nullptr);
+ palEnvironment[i] = copy;
+ palEnvironment[i + 1] = nullptr;
+ palEnvironmentCount++;
+
+ result = TRUE;
+ }
+ }
+done:
+
+ if (fOwningCS)
+ {
+ InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
+ }
+
+ return result;
+}
+
+/*++
+Function:
+ EnvironGetenv
+
+Get the value of environment variable with the given name.
+
+Parameters
+
+ name
+ [in] The name of the environment variable to get.
+ copyValue
+ [in] If this is TRUE, the function will make a copy of the
+ value and return a pointer to that. Otherwise, it will
+ return a pointer to the value in the PAL environment
+ directly. Calling this function with copyValue set to
+ FALSE is therefore unsafe without taking special pre-
+ cautions since the pointer may point to garbage later.
+
+Return Value
+
+ A pointer to the value of the environment variable if it exists,
+ or nullptr otherwise.
+
+--*/
+char* EnvironGetenv(const char* name, BOOL copyValue)
+{
+ char *retValue = nullptr;
+
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
+
+ int nameLength = strlen(name);
+ for (int i = 0; palEnvironment[i] != nullptr; ++i)
+ {
+ if (strlen(palEnvironment[i]) < nameLength)
+ {
+ continue;
+ }
+
+ if (memcmp(palEnvironment[i], name, nameLength) == 0)
+ {
+ char *equalsSignPosition = palEnvironment[i] + nameLength;
+
+ // If this is one of the variables which has no equals sign, we
+ // treat the whole thing as name, so the value is an empty string.
+ if (*equalsSignPosition == '\0')
+ {
+ retValue = (char *)"";
+ break;
+ }
+ else if (*equalsSignPosition == '=')
+ {
+ retValue = equalsSignPosition + 1;
+ break;
+ }
+ }
+ }
+
+ if ((retValue != nullptr) && copyValue)
+ {
+ retValue = strdup(retValue);
+ }
+
+ InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
+ return retValue;
+}
+
+/*++
+Function:
+ EnvironGetSystemEnvironment
+
+Get a pointer to the array of pointers representing the process's
+environment.
+
+See 'man environ' for details.
+
+Return Value
+
+ A pointer to the environment.
+
+--*/
+char** EnvironGetSystemEnvironment()
+{
+ char** sysEnviron;
+
+#if HAVE__NSGETENVIRON
+ sysEnviron = *(_NSGetEnviron());
+#else // HAVE__NSGETENVIRON
+ extern char **environ;
+ sysEnviron = environ;
+#endif // HAVE__NSGETENVIRON
+
+ return sysEnviron;
+}
+
+/*++
+Function:
+ EnvironInitialize
+
+Initialization function called from PAL_Initialize.
+
+Note: This is called before debug channels are initialized, so it
+ cannot use debug tracing calls.
+--*/
+BOOL
+EnvironInitialize(void)
+{
+ BOOL ret = FALSE;
+
+ InternalInitializeCriticalSection(&gcsEnvironment);
+
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
+
+ char** sourceEnviron = EnvironGetSystemEnvironment();
+
+ int variableCount = 0;
+ while (sourceEnviron[variableCount] != nullptr)
+ variableCount++;
+
+ palEnvironmentCount = 0;
+
+ // We need to decide how much space to allocate. Since we need enough
+ // space for all of the 'n' current environment variables, but we don't
+ // know how many more there will be, we will initially make room for
+ // '2n' variables. If even more are added, we will resize again.
+ // If there are no variables, we will still make room for 1 entry to
+ // store a nullptr there.
+ int initialSize = (variableCount == 0) ? 1 : variableCount * 2;
+
+ ret = ResizeEnvironment(initialSize);
+ if (ret == TRUE)
+ {
+ _ASSERTE(palEnvironment != nullptr);
+ for (int i = 0; i < variableCount; ++i)
+ {
+ palEnvironment[i] = strdup(sourceEnviron[i]);
+ palEnvironmentCount++;
+ }
+
+ // Set the entry after the last variable to null to indicate the end.
+ palEnvironment[variableCount] = nullptr;
+ }
+
+ InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
+ return ret;
+}
+
+/*++
+
+Function : _putenv.
+
+See MSDN for more details.
+
+Note: The BSD implementation can cause
+ memory leaks. See man pages for more details.
+--*/
+int
+__cdecl
+_putenv( const char * envstring )
+{
+ int ret = -1;
+
+ PERF_ENTRY(_putenv);
+ ENTRY( "_putenv( %p (%s) )\n", envstring ? envstring : "NULL", envstring ? envstring : "NULL") ;
+
+ if (envstring != nullptr)
+ {
+ ret = EnvironPutenv(envstring, TRUE) ? 0 : -1;
+ }
+ else
+ {
+ ERROR( "_putenv() called with NULL envstring!\n");
+ }
+
+ LOGEXIT( "_putenv returning %d\n", ret);
+ PERF_EXIT(_putenv);
+ return ret;
+}
+
+/*++
+
+Function : PAL_getenv
+
+See MSDN for more details.
+--*/
+char * __cdecl PAL_getenv(const char *varname)
+{
+ char *retval;
+
+ PERF_ENTRY(getenv);
+ ENTRY("getenv (%p (%s))\n", varname ? varname : "NULL", varname ? varname : "NULL");
+
+ if (strcmp(varname, "") == 0)
+ {
+ ERROR("getenv called with a empty variable name\n");
+ LOGEXIT("getenv returning NULL\n");
+ PERF_EXIT(getenv);
+ return(NULL);
+ }
+
+ retval = EnvironGetenv(varname);
+
+ LOGEXIT("getenv returning %p\n", retval);
+ PERF_EXIT(getenv);
+ return(retval);
+}
diff --git a/src/pal/src/misc/error.cpp b/src/pal/src/misc/error.cpp
new file mode 100644
index 0000000000..cb483885d5
--- /dev/null
+++ b/src/pal/src/misc/error.cpp
@@ -0,0 +1,126 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ error.c
+
+Abstract:
+
+ Implementation of Error management functions.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "pal/dbgmsg.h"
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+/*++
+Function:
+ SetErrorMode
+
+The SetErrorMode function controls whether the system will handle the
+specified types of serious errors, or whether the process will handle
+them.
+
+Parameters
+
+uMode
+ [in] Specifies the process error mode. This parameter can be one or more of the following values.
+
+ Value Action
+ 0 Use the system default, which is to display all error dialog boxes.
+ SEM_FAILCRITICALERRORS The system does not display the critical-error-handler message box. Instead,
+ the system sends the error to the calling process.
+ SEM_NOOPENFILEERRORBOX The system does not display a message box when it fails to find a file. Instead,
+ the error is returned to the calling process.
+
+Return Values
+
+The return value is the previous state of the error-mode bit flags.
+
+--*/
+UINT
+PALAPI
+SetErrorMode(
+ IN UINT uMode)
+{
+ PERF_ENTRY(SetErrorMode);
+ ENTRY("SetErrorMode (uMode=%#x)\n", uMode);
+
+ LOGEXIT("SetErrorMode returns UINT 0\n");
+ PERF_EXIT(SetErrorMode);
+ return 0;
+}
+
+
+/*++
+Function:
+ GetLastError
+
+GetLastError
+
+The GetLastError function retrieves the calling thread's last-error
+code value. The last-error code is maintained on a per-thread
+basis. Multiple threads do not overwrite each other's last-error code.
+
+Parameters
+
+This function has no parameters.
+
+Return Values
+
+The return value is the calling thread's last-error code
+value. Functions set this value by calling the SetLastError
+function. The Return Value section of each reference page notes the
+conditions under which the function sets the last-error code.
+
+--*/
+DWORD
+PALAPI
+GetLastError(
+ VOID)
+{
+ return CPalThread::GetLastError();
+}
+
+
+
+/*++
+Function:
+ SetLastError
+
+SetLastError
+
+The SetLastError function sets the last-error code for the calling thread.
+
+Parameters
+
+dwErrCode
+ [in] Specifies the last-error code for the thread.
+
+Return Values
+
+This function does not return a value.
+
+--*/
+VOID
+PALAPI
+SetLastError(
+ IN DWORD dwErrCode)
+{
+ CPalThread::SetLastError(dwErrCode);
+}
+
diff --git a/src/pal/src/misc/errorstrings.cpp b/src/pal/src/misc/errorstrings.cpp
new file mode 100644
index 0000000000..22443114ee
--- /dev/null
+++ b/src/pal/src/misc/errorstrings.cpp
@@ -0,0 +1,172 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ errorstrings.cpp
+
+Abstract:
+
+ Conversion of PAL error code to string
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "errorstrings.h"
+
+struct ErrorString
+{
+ DWORD code;
+ LPCWSTR const string;
+};
+
+ErrorString palErrorStrings[] =
+{
+ { ERROR_SUCCESS, W("The operation completed successfully.\n") },
+ { ERROR_INVALID_FUNCTION, W("Incorrect function.\n") },
+ { ERROR_FILE_NOT_FOUND, W("The system cannot find the file specified.\n") },
+ { ERROR_PATH_NOT_FOUND, W("The system cannot find the path specified.\n") },
+ { ERROR_TOO_MANY_OPEN_FILES, W("The system cannot open the file.\n") },
+ { ERROR_ACCESS_DENIED, W("Access is denied.\n") },
+ { ERROR_INVALID_HANDLE, W("The handle is invalid.\n") },
+ { ERROR_NOT_ENOUGH_MEMORY, W("Not enough storage is available to process this command.\n") },
+ { ERROR_BAD_ENVIRONMENT, W("The environment is incorrect.\n") },
+ { ERROR_BAD_FORMAT, W("An attempt was made to load a program with an incorrect format.\n") },
+ { ERROR_INVALID_ACCESS, W("The access code is invalid.\n") },
+ { ERROR_INVALID_DATA, W("The data is invalid.\n") },
+ { ERROR_OUTOFMEMORY, W("Not enough storage is available to complete this operation.\n") },
+ { ERROR_INVALID_DRIVE, W("The system cannot find the drive specified.\n") },
+ { ERROR_NO_MORE_FILES, W("There are no more files.\n") },
+ { ERROR_WRITE_PROTECT, W("The media is write protected.\n") },
+ { ERROR_NOT_READY, W("The device is not ready.\n") },
+ { ERROR_BAD_COMMAND, W("The device does not recognize the command.\n") },
+ { ERROR_BAD_LENGTH, W("The program issued a command but the command length is incorrect.\n") },
+ { ERROR_WRITE_FAULT, W("The system cannot write to the specified device.\n") },
+ { ERROR_READ_FAULT, W("The system cannot read from the specified device.\n") },
+ { ERROR_GEN_FAILURE, W("A device attached to the system is not functioning.\n") },
+ { ERROR_SHARING_VIOLATION, W("The process cannot access the file because it is being used by another process.\n") },
+ { ERROR_LOCK_VIOLATION, W("The process cannot access the file because another process has locked a portion of the file.\n") },
+ { ERROR_SHARING_BUFFER_EXCEEDED, W("Too many files opened for sharing.\n") },
+ { ERROR_HANDLE_EOF, W("Reached the end of the file.\n") },
+ { ERROR_HANDLE_DISK_FULL, W("The disk is full.\n") },
+ { ERROR_NOT_SUPPORTED, W("The request is not supported.\n") },
+ { ERROR_DUP_NAME, W("A duplicate name exists on the network.\n") },
+ { ERROR_BAD_NETPATH, W("The network path was not found.\n") },
+ { ERROR_DEV_NOT_EXIST, W("The specified network resource or device is no longer available.\n") },
+ { ERROR_BAD_NET_NAME, W("The network name cannot be found.\n") },
+ { ERROR_FILE_EXISTS, W("The file exists.\n") },
+ { ERROR_CANNOT_MAKE, W("The directory or file cannot be created.\n") },
+ { ERROR_INVALID_PARAMETER, W("The parameter is incorrect.\n") },
+ { ERROR_NET_WRITE_FAULT, W("A write fault occurred on the network.\n") },
+ { ERROR_DRIVE_LOCKED, W("The disk is in use or locked by another process.\n") },
+ { ERROR_BROKEN_PIPE, W("The pipe has been ended.\n") },
+ { ERROR_OPEN_FAILED, W("The system cannot open the device or file specified.\n") },
+ { ERROR_BUFFER_OVERFLOW, W("The file name is too long.\n") },
+ { ERROR_DISK_FULL, W("There is not enough space on the disk.\n") },
+ { ERROR_CALL_NOT_IMPLEMENTED, W("This function is not supported on this system.\n") },
+ { ERROR_SEM_TIMEOUT, W("The semaphore timeout period has expired.\n") },
+ { ERROR_INSUFFICIENT_BUFFER, W("The data area passed to a system call is too small.\n") },
+ { ERROR_INVALID_NAME, W("The filename, directory name, or volume label syntax is incorrect.\n") },
+ { ERROR_MOD_NOT_FOUND, W("The specified module could not be found.\n") },
+ { ERROR_PROC_NOT_FOUND, W("The specified procedure could not be found.\n") },
+ { ERROR_WAIT_NO_CHILDREN, W("There are no child processes to wait for.\n") },
+ { ERROR_NEGATIVE_SEEK, W("An attempt was made to move the file pointer before the beginning of the file.\n") },
+ { ERROR_SEEK_ON_DEVICE, W("The file pointer cannot be set on the specified device or file.\n") },
+ { ERROR_DIR_NOT_EMPTY, W("The directory is not empty.\n") },
+ { ERROR_SIGNAL_REFUSED, W("The recipient process has refused the signal.\n") },
+ { ERROR_NOT_LOCKED, W("The segment is already unlocked.\n") },
+ { ERROR_BAD_PATHNAME, W("The specified path is invalid.\n") },
+ { ERROR_BUSY, W("The requested resource is in use.\n") },
+ { ERROR_INVALID_ORDINAL, W("The operating system cannot run %1.\n") },
+ { ERROR_ALREADY_EXISTS, W("Cannot create a file when that file already exists.\n") },
+ { ERROR_INVALID_EXE_SIGNATURE, W("Cannot run %1 in Win32 mode.\n") },
+ { ERROR_EXE_MARKED_INVALID, W("The operating system cannot run %1.\n") },
+ { ERROR_BAD_EXE_FORMAT, W("%1 is not a valid Win32 application.\n") },
+ { ERROR_ENVVAR_NOT_FOUND, W("The system could not find the environment option that was entered.\n") },
+ { ERROR_FILENAME_EXCED_RANGE, W("The filename or extension is too long.\n") },
+ { ERROR_PIPE_BUSY, W("All pipe instances are busy.\n") },
+ { ERROR_NO_DATA, W("The pipe is being closed\n")},
+ { ERROR_MORE_DATA, W("More data is available.\n") },
+ { ERROR_NO_MORE_ITEMS, W("No more data is available.\n") },
+ { ERROR_DIRECTORY, W("The directory name is invalid.\n") },
+ { ERROR_NOT_OWNER, W("Attempt to release mutex not owned by caller.\n") },
+ { ERROR_PARTIAL_COPY, W("Only part of a ReadProcessMemory or WriteProcessMemory request was completed.\n") },
+ { ERROR_INVALID_ADDRESS, W("Attempt to access invalid address.\n") },
+ { ERROR_ARITHMETIC_OVERFLOW, W("Arithmetic result exceeded 32 bits.\n") },
+ { ERROR_OPERATION_ABORTED, W("The I/O operation has been aborted because of either a thread exit or an application request.\n") },
+ { ERROR_IO_INCOMPLETE, W("Overlapped I/O event is not in a signaled state.\n") },
+ { ERROR_IO_PENDING, W("Overlapped I/O operation is in progress.\n") },
+ { ERROR_NOACCESS, W("Invalid access to memory location.\n") },
+ { ERROR_STACK_OVERFLOW, W("Recursion too deep; the stack overflowed.\n") },
+ { ERROR_INVALID_FLAGS, W("Invalid flags.\n") },
+ { ERROR_UNRECOGNIZED_VOLUME, W("The volume does not contain a recognized file system.\nPlease make sure that all required file system drivers are loaded and that the volume is not corrupted.\n") },
+ { ERROR_FILE_INVALID, W("The volume for a file has been externally altered so that the opened file is no longer valid.\n") },
+ { ERROR_PROCESS_ABORTED, W("The process terminated unexpectedly.\n") },
+ { ERROR_NO_UNICODE_TRANSLATION, W("No mapping for the Unicode character exists in the target multi-byte code page.\n") },
+ { ERROR_DLL_INIT_FAILED, W("A dynamic link library (DLL) initialization routine failed.\n") },
+ { ERROR_IO_DEVICE, W("The request could not be performed because of an I/O device error.\n") },
+ { ERROR_DISK_OPERATION_FAILED, W("While accessing the hard disk, a disk operation failed even after retries.\n") },
+ { ERROR_POSSIBLE_DEADLOCK, W("A potential deadlock condition has been detected.\n") },
+ { ERROR_TOO_MANY_LINKS, W("An attempt was made to create more links on a file than the file system supports.\n") },
+ { ERROR_INVALID_DLL, W("One of the library files needed to run this application is damaged.\n") },
+ { ERROR_DLL_NOT_FOUND, W("One of the library files needed to run this application cannot be found.\n") },
+ { ERROR_NOT_FOUND, W("Element not found.\n") },
+ { ERROR_CANCELLED, W("The operation was canceled by the user.\n") },
+ { ERROR_NOT_AUTHENTICATED, W("The operation being requested was not performed because the user has not been authenticated.\n") },
+ { ERROR_INTERNAL_ERROR, W("An internal error occurred.\n") },
+ { ERROR_FILE_CORRUPT, W("The file or directory is corrupted and unreadable.\n") },
+ { ERROR_DISK_CORRUPT, W("The disk structure is corrupted and unreadable.\n") },
+ { ERROR_WRONG_TARGET_NAME, W("Logon Failure: The target account name is incorrect.\n") },
+ { ERROR_NO_SYSTEM_RESOURCES, W("Insufficient system resources exist to complete the requested service.\n") },
+ { ERROR_COMMITMENT_LIMIT, W("The paging file is too small for this operation to complete.\n") },
+ { ERROR_TIMEOUT, W("This operation returned because the timeout period expired.\n") },
+ { ERROR_EVENTLOG_FILE_CORRUPT, W("The event log file is corrupted.\n") },
+ { ERROR_LOG_FILE_FULL, W("The event log file is full.\n") },
+ { ERROR_UNSUPPORTED_TYPE, W("Data of this type is not supported.\n") },
+ { RPC_S_INVALID_VERS_OPTION, W("The version option is invalid.\n") },
+ { ERROR_RESOURCE_DATA_NOT_FOUND, W("The specified image file did not contain a resource section.\n") },
+ { ERROR_RESOURCE_LANG_NOT_FOUND, W("The specified resource language ID cannot be found in the image file.\n") },
+ { ERROR_TAG_NOT_PRESENT, W("A required tag is not present.\n") }
+};
+
+int CompareErrorStrings(const void *a, const void *b)
+{
+ DWORD codeA = ((ErrorString *)a)->code;
+ DWORD codeB = ((ErrorString *)b)->code;
+
+ if (codeA < codeB)
+ {
+ return -1;
+ }
+ else if (codeA == codeB)
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+LPCWSTR GetPalErrorString(DWORD code)
+{
+ // Search the sorted set of resources for the ID we're interested in.
+ ErrorString searchEntry = {code, NULL};
+ ErrorString *stringEntry = (ErrorString *)bsearch(
+ &searchEntry,
+ palErrorStrings,
+ sizeof(palErrorStrings) / sizeof(palErrorStrings[0]),
+ sizeof(ErrorString),
+ CompareErrorStrings);
+
+ return (stringEntry != NULL) ? stringEntry->string : NULL;
+}
diff --git a/src/pal/src/misc/errorstrings.h b/src/pal/src/misc/errorstrings.h
new file mode 100644
index 0000000000..d24f025e32
--- /dev/null
+++ b/src/pal/src/misc/errorstrings.h
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef _ERRORSTRINGS_H_
+#define _ERRORSTRINGS_H_
+
+LPCWSTR GetPalErrorString(DWORD code);
+
+#endif // _ERRORSTRINGS_H_
diff --git a/src/pal/src/misc/eventlog.cpp b/src/pal/src/misc/eventlog.cpp
new file mode 100644
index 0000000000..9eb67ffb1c
--- /dev/null
+++ b/src/pal/src/misc/eventlog.cpp
@@ -0,0 +1,423 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ eventlog.cpp
+
+Abstract:
+
+ (Rudimentary) implementation of Event Log.
+
+ Defaults to BSD syslog. On Mac OS X, uses the superior asl.
+
+ Caviats:
+ * Neither are real handles that you can lock. If that is necessary, the
+ PAL handle functionality can be used.
+ * Neither are kept in a table so that if you ask for the same event source
+ twice, you get the same handle.
+ * The resource file is not consulted, so we just print out the replacement
+ strings. Fortunately, for the CLR, there's the resources just have the single
+ replacement string.
+
+Revision History:
+
+ 5/21/09 -- initial
+
+
+
+--*/
+
+#include "pal/malloc.hpp"
+#include "pal/dbgmsg.h"
+
+#ifdef __APPLE__
+#define USE_ASL
+#endif // __APPLE__
+
+#ifdef USE_ASL
+#include <asl.h>
+#else // USE_ASL
+#include <syslog.h>
+#endif // USE_ASL else
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+HANDLE
+PALAPI
+RegisterEventSourceA (
+ IN OPTIONAL LPCSTR lpUNCServerName,
+ IN LPCSTR lpSourceName
+ )
+{
+ HANDLE hRet = INVALID_HANDLE_VALUE;
+
+ PERF_ENTRY(RegisterEventSourceA);
+ ENTRY("RegisterEventSourceA(lpUNCServerName=%p (%s), lpSourceName=%p (%s))\n",
+ lpUNCServerName, lpUNCServerName?lpUNCServerName:"NULL",
+ lpSourceName, lpSourceName?lpSourceName:"NULL");
+
+ if (NULL != lpUNCServerName)
+ {
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return hRet;
+ }
+
+ if (NULL == lpSourceName)
+ {
+ ERROR("lpSourceName has to be a valid parameter\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return hRet;
+ }
+
+#ifdef USE_ASL
+ // In asl parlance, the EventSource handle is an aslclient; it's not
+ // guaranteed to be the same as a different call to this with the same
+ // source name.
+ aslclient asl = asl_open(lpSourceName, NULL /* facility */, 0 /* opts */);
+ hRet = (HANDLE)asl;
+#else // USE_ASL
+ // In syslog parlance, the EventSource handle is just a string name
+ // representing the source.
+ size_t sizeSyslogHandle = strlen(lpSourceName) + 1;
+ char *syslogHandle = (char *)PAL_malloc(sizeSyslogHandle);
+ if (syslogHandle)
+ {
+ strcpy_s(syslogHandle, sizeSyslogHandle, lpSourceName);
+ hRet = (HANDLE)syslogHandle;
+ }
+#endif // USE_ASL else
+
+ if (INVALID_HANDLE_VALUE == hRet)
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+
+ LOGEXIT("RegisterEventSourceA returns %p\n", hRet);
+ PERF_EXIT(RegisterEventSourceA);
+ return hRet;
+}
+
+HANDLE
+PALAPI
+RegisterEventSourceW (
+ IN OPTIONAL LPCWSTR lpUNCServerName,
+ IN LPCWSTR lpSourceName
+ )
+{
+ HANDLE hRet = INVALID_HANDLE_VALUE;
+ int size;
+ CHAR *inBuff = NULL;
+
+ PERF_ENTRY(RegisterEventSourceW);
+ ENTRY("RegisterEventSourceW(lpUNCServerName=%p (%S), lpSourceName=%p (%S))\n",
+ lpUNCServerName, lpUNCServerName?lpUNCServerName:W16_NULLSTRING,
+ lpSourceName, lpSourceName?lpSourceName:W16_NULLSTRING);
+
+ if (NULL != lpUNCServerName)
+ {
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return hRet;
+ }
+
+ size = WideCharToMultiByte(CP_ACP, 0, lpSourceName, -1, NULL, 0, NULL, NULL);
+
+ if (0 == size)
+ {
+ ERROR("lpSourceName has to be a valid parameter\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return hRet;
+ }
+ inBuff = (CHAR *)PAL_malloc(size);
+ if (NULL == inBuff)
+ {
+ ERROR("malloc failed\n");
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return hRet;
+ }
+
+ if (0 == WideCharToMultiByte(CP_ACP, 0, lpSourceName, -1, inBuff, size, NULL, NULL))
+ {
+ ASSERT( "WideCharToMultiByte failed!\n" );
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ hRet = RegisterEventSourceA(NULL, inBuff);
+
+done:
+ PAL_free(inBuff);
+
+ LOGEXIT("RegisterEventSourceW returns %p\n", hRet);
+ PERF_EXIT(RegisterEventSourceW);
+ return hRet;
+}
+
+BOOL
+PALAPI
+DeregisterEventSource (
+ IN HANDLE hEventLog
+ )
+{
+ BOOL bRet = FALSE;
+
+ PERF_ENTRY(DeregisterEventSource)
+ ENTRY("DeregisterEventSource(hEventLog=%p)\n", hEventLog);
+
+ if (INVALID_HANDLE_VALUE == hEventLog ||
+ NULL == hEventLog)
+ {
+ SetLastError(ERROR_INVALID_HANDLE);
+ goto done;
+ }
+
+#ifdef USE_ASL
+ asl_close((aslclient)hEventLog);
+#else // USE_ASL
+ PAL_free(hEventLog);
+#endif // USE_ASL else
+
+ bRet = TRUE;
+
+done:
+
+ LOGEXIT("DeregisterEventSource returns BOOL %d\n", bRet);
+ PERF_EXIT(DeregisterEventSource);
+ return bRet;
+}
+
+BOOL
+PALAPI
+ReportEventA (
+ IN HANDLE hEventLog,
+ IN WORD wType,
+ IN WORD wCategory,
+ IN DWORD dwEventID,
+ IN OPTIONAL PSID lpUserSid,
+ IN WORD wNumStrings,
+ IN DWORD dwDataSize,
+ IN OPTIONAL LPCSTR *lpStrings,
+ IN OPTIONAL LPVOID lpRawData
+ )
+{
+ BOOL bRet = FALSE;
+
+ PERF_ENTRY(ReportEventA);
+ ENTRY("ReportEventA(hEventLog=%p, wType=0x%hx, wCategory=0x%hx, dwEventID=0x%x, "
+ "lpUserSid=%p, wNumStrings=%hu, dwDataSize=%u, lpStrings=%p, lpRawData=%p)\n",
+ hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize,
+ lpStrings, lpRawData);
+
+ if (INVALID_HANDLE_VALUE == hEventLog)
+ {
+ ERROR("hEventLog has to be a valid parameter\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return bRet;
+ }
+
+ if (wNumStrings > 0 && NULL == lpStrings)
+ {
+ ERROR("lpStrings has to be a valid parameter if wNumStrings is non-zero\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return bRet;
+ }
+
+ if (NULL != lpUserSid || 0 != dwDataSize || 1 != wNumStrings)
+ {
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return bRet;
+ }
+
+#ifdef USE_ASL
+ int level;
+ switch (wType)
+ {
+ case EVENTLOG_SUCCESS:
+ case EVENTLOG_AUDIT_SUCCESS:
+ level = ASL_LEVEL_NOTICE;
+ break;
+
+ case EVENTLOG_INFORMATION_TYPE:
+ level = ASL_LEVEL_INFO;
+ break;
+
+ case EVENTLOG_ERROR_TYPE:
+ case EVENTLOG_AUDIT_FAILURE:
+ level = ASL_LEVEL_ERR;
+ break;
+
+ case EVENTLOG_WARNING_TYPE:
+ level = ASL_LEVEL_WARNING;
+ break;
+
+ default:
+ ERROR("Unknown RecordEvent type.\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return bRet;
+ }
+
+ aslmsg msg;
+ msg = asl_new(ASL_TYPE_MSG);
+ int aslRet;
+
+ if (msg)
+ {
+ char szNumber[11];
+
+ sprintf_s(szNumber, sizeof(szNumber) / sizeof(*szNumber), "%hu", wCategory);
+ aslRet = asl_set(msg, "Category", szNumber);
+ if (aslRet != 0)
+ WARN("Could not set Category %s on aslmsg (%p)", szNumber, msg);
+ sprintf_s(szNumber, sizeof(szNumber) / sizeof(*szNumber), "%u", dwEventID);
+ aslRet = asl_set(msg, "EventID", szNumber);
+ if (aslRet != 0)
+ WARN("Could not set EventID %s on aslmsg (%p)", szNumber, msg);
+
+ aslRet = asl_log((aslclient)hEventLog, msg, level, "%s", lpStrings[0]);
+
+ asl_free(msg);
+ }
+ else
+ {
+ // Yikes, fall back to worse syslog behavior due to low mem or asl issue.
+ aslRet = asl_log((aslclient)hEventLog, NULL, level, "[%hx:%x] %s", wCategory, dwEventID, lpStrings[0]);
+ }
+
+ if (aslRet != 0)
+ SetLastError(ERROR_INTERNAL_ERROR);
+ else
+ bRet = TRUE;
+#else // USE_ASL
+ int priority;
+ switch (wType)
+ {
+ case EVENTLOG_SUCCESS:
+ case EVENTLOG_AUDIT_SUCCESS:
+ case EVENTLOG_INFORMATION_TYPE:
+ priority = LOG_INFO;
+ break;
+
+ case EVENTLOG_ERROR_TYPE:
+ case EVENTLOG_AUDIT_FAILURE:
+ priority = LOG_ERR;
+ break;
+
+ case EVENTLOG_WARNING_TYPE:
+ priority = LOG_WARNING;
+ break;
+
+ default:
+ ERROR("Unknown RecordEvent type.\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return bRet;
+ }
+
+ openlog((char *)hEventLog, LOG_CONS | LOG_PID, LOG_USER);
+
+ syslog(priority, "[%hx:%x] %s", wCategory, dwEventID, lpStrings[0]);
+
+ closelog();
+
+ bRet = TRUE;
+#endif // USE_ASL else
+
+ LOGEXIT("ReportEventA returns BOOL %d\n", bRet);
+ PERF_EXIT(ReportEventA);
+ return bRet;
+}
+
+BOOL
+PALAPI
+ReportEventW (
+ IN HANDLE hEventLog,
+ IN WORD wType,
+ IN WORD wCategory,
+ IN DWORD dwEventID,
+ IN OPTIONAL PSID lpUserSid,
+ IN WORD wNumStrings,
+ IN DWORD dwDataSize,
+ IN OPTIONAL LPCWSTR *lpStrings,
+ IN OPTIONAL LPVOID lpRawData
+ )
+{
+ BOOL bRet = FALSE;
+ LPCSTR *lpMBStrings = NULL;
+
+ PERF_ENTRY(ReportEventW);
+ ENTRY("ReportEventW(hEventLog=%p, wType=0x%hx, wCategory=0x%hx, dwEventID=0x%x, "
+ "lpUserSid=%p, wNumStrings=%hu, dwDataSize=%u, lpStrings=%p, lpRawData=%p)\n",
+ hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize,
+ lpStrings, lpRawData);
+
+ if (wNumStrings > 0 && NULL == lpStrings)
+ {
+ ERROR("lpStrings has to be a valid parameter if wNumStrings is non-zero\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return bRet;
+ }
+
+ if (wNumStrings > 0)
+ {
+ lpMBStrings = (LPCSTR *)PAL_malloc(wNumStrings * sizeof(CHAR *));
+ if (!lpMBStrings)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return bRet;
+ }
+
+ for (WORD iString = 0; iString < wNumStrings; iString++)
+ {
+ int size;
+ bool fConverted;
+ CHAR *sz;
+
+ size = WideCharToMultiByte(CP_ACP, 0, lpStrings[iString], -1, NULL, 0, NULL, NULL);
+ if (0 == size)
+ {
+ ERROR("lpStrings[%d] has to be a valid parameter\n", iString);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ wNumStrings = iString; // so that free only frees earlier converted lpStrings.
+ goto done;
+ }
+
+ sz = (LPSTR)PAL_malloc(size);
+ if (!sz)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ wNumStrings = iString; // so that free only frees earlier converted lpStrings.
+ goto done;
+ }
+ fConverted = (0 != WideCharToMultiByte(CP_ACP, 0, lpStrings[iString], -1,
+ sz, size, NULL, NULL));
+ lpMBStrings[iString] = sz; // no const-cast needed.
+ if (!fConverted)
+ {
+ ASSERT("WideCharToMultiByte failed!\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ wNumStrings = iString + 1; // so that free only frees earlier converted lpStrings.
+ goto done;
+ }
+ }
+ }
+
+ bRet = ReportEventA(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings,
+ dwDataSize, lpMBStrings, lpRawData);
+
+done:
+
+ if (wNumStrings > 0)
+ {
+ for (WORD iString = 0; iString < wNumStrings; iString++)
+ PAL_free((PVOID)lpMBStrings[iString]);
+ PAL_free(lpMBStrings);
+ }
+
+ LOGEXIT("ReportEventW returns BOOL %d\n", bRet);
+ PERF_EXIT(ReportEventW);
+ return bRet;
+}
diff --git a/src/pal/src/misc/fmtmessage.cpp b/src/pal/src/misc/fmtmessage.cpp
new file mode 100644
index 0000000000..46e0af6e0d
--- /dev/null
+++ b/src/pal/src/misc/fmtmessage.cpp
@@ -0,0 +1,701 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ fmtmessage.c
+
+Abstract:
+
+ Implementation of FormatMessage function.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/unicode_data.h"
+#include "pal/critsect.h"
+#include "pal/module.h"
+#include "pal/misc.h"
+
+#include "pal/printfcpp.hpp"
+
+#include "errorstrings.h"
+
+#include <stdarg.h>
+#if NEED_DLCOMPAT
+#include "dlcompat.h"
+#else // NEED_DLCOMPAT
+#include <dlfcn.h>
+#endif // NEED_DLCOMPAT
+#include <errno.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+/* Defines */
+
+#define MAX_ERROR_STRING_LENGTH 32
+
+/*++
+Function:
+
+ FMTMSG_GetMessageString
+
+Returns the message as a wide string.
+--*/
+static LPWSTR FMTMSG_GetMessageString( DWORD dwErrCode )
+{
+ TRACE("Entered FMTMSG_GetMessageString\n");
+
+ LPCWSTR lpErrorString = GetPalErrorString(dwErrCode);
+ int allocChars;
+
+ if (lpErrorString != NULL)
+ {
+ allocChars = PAL_wcslen(lpErrorString) + 1;
+ }
+ else
+ {
+ allocChars = MAX_ERROR_STRING_LENGTH + 1;
+ }
+
+ LPWSTR lpRetVal = (LPWSTR)LocalAlloc(LMEM_FIXED, allocChars * sizeof(WCHAR));
+
+ if (lpRetVal)
+ {
+ if (lpErrorString != NULL)
+ {
+ PAL_wcscpy(lpRetVal, lpErrorString);
+ }
+ else
+ {
+ swprintf_s(lpRetVal, MAX_ERROR_STRING_LENGTH, W("Error %u"), dwErrCode);
+ }
+ }
+ else
+ {
+ ERROR("Unable to allocate memory.\n");
+ }
+
+ return lpRetVal;
+}
+
+/*++
+
+Function :
+
+ FMTMSG__watoi
+
+ Converts a wide string repersentation of an integer number
+ into a interger number.
+
+ Returns a integer number, or 0 on failure. 0 is not a valid number
+ for FormatMessage inserts.
+
+--*/
+static INT FMTMSG__watoi( LPWSTR str )
+{
+ CONST UINT MAX_NUMBER_LENGTH = 3;
+ CHAR buf[ MAX_NUMBER_LENGTH ];
+ INT nRetVal = 0;
+
+ nRetVal = WideCharToMultiByte( CP_ACP, 0, str, -1, buf,
+ MAX_NUMBER_LENGTH, NULL, 0 );
+
+ if ( nRetVal != 0 )
+ {
+ return atoi( buf );
+ }
+ else
+ {
+ ERROR( "Unable to convert the string to a number.\n" );
+ return 0;
+ }
+}
+
+/* Adds the character to the working string. */
+#define _ADD_TO_STRING( c ) \
+{\
+ TRACE( "Adding %c to the string.\n", (CHAR)c );\
+ *lpWorkingString = c;\
+ lpWorkingString++;\
+ nCount++;\
+}
+
+/* Grows the buffer. */
+#define _GROW_BUFFER() \
+{\
+ if ( bIsLocalAlloced ) \
+ { \
+ LPWSTR lpTemp = NULL; \
+ UINT NumOfBytes = 0; \
+ nSize *= 2; \
+ NumOfBytes = nSize * sizeof( WCHAR ); \
+ lpTemp = static_cast<WCHAR *>( LocalAlloc( LMEM_FIXED, NumOfBytes ) ); \
+ TRACE( "Growing the buffer.\n" );\
+ \
+ if ( !lpTemp ) \
+ { \
+ ERROR( "Out of buffer\n" ); \
+ SetLastError( ERROR_NOT_ENOUGH_MEMORY ); \
+ nCount = 0; \
+ lpWorkingString = NULL; \
+ goto exit; \
+ } \
+ \
+ *lpWorkingString = '\0';\
+ PAL_wcscpy( lpTemp, lpReturnString );\
+ LocalFree( lpReturnString ); \
+ lpWorkingString = lpReturnString = lpTemp; \
+ lpWorkingString += nCount; \
+ } \
+ else \
+ { \
+ WARN( "Out of buffer.\n" ); \
+ SetLastError( ERROR_INSUFFICIENT_BUFFER ); \
+ nCount = 0; \
+ lpWorkingString = NULL; \
+ goto exit; \
+ } \
+}
+/* Adds a character to the working string. This is a safer version
+of _ADD_TO_STRING, as we will resize the buffer if necessary. */
+#define _CHECKED_ADD_TO_STRING( c ) \
+{\
+ if ( nCount+1 == nSize ) \
+ {\
+ _GROW_BUFFER();\
+ } \
+ _ADD_TO_STRING( c );\
+}
+
+
+/*++
+Function :
+
+ FMTMSG_ProcessPrintf
+
+ Processes the printf formatters based on the format.
+
+ Returns the LPWSTR string, or NULL on failure.
+*/
+
+static LPWSTR FMTMSG_ProcessPrintf( wchar_t c ,
+ LPWSTR lpPrintfString,
+ LPWSTR lpInsertString)
+{
+ LPWSTR lpBuffer = NULL;
+ LPWSTR lpBuffer2 = NULL;
+ LPWSTR lpFormat = NULL;
+#if _DEBUG
+ // small size for _DEBUG to exercise buffer reallocation logic
+ int tmpSize = 4;
+#else
+ int tmpSize = 64;
+#endif
+ UINT nFormatLength = 0;
+ int nBufferLength = 0;
+
+ TRACE( "FMTMSG_ProcessPrintf( %C, %S, %S )\n", c,
+ lpPrintfString, lpInsertString );
+
+ switch ( c )
+ {
+ case 'e' :
+ /* Fall through */
+ case 'E' :
+ /* Fall through */
+ case 'f' :
+ /* Fall through */
+ case 'g' :
+ /* Fall through */
+ case 'G' :
+ ERROR( "%%%c is not supported by FormatMessage.\n", c );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return NULL;
+ }
+
+ nFormatLength = PAL_wcslen( lpPrintfString ) + 2; /* Need to count % AND NULL */
+ lpFormat = (LPWSTR)PAL_malloc( nFormatLength * sizeof( WCHAR ) );
+ if ( !lpFormat )
+ {
+ ERROR( "Unable to allocate memory.\n" );
+ SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+ return NULL;
+ }
+ /* Create the format string. */
+ memset( lpFormat, 0, nFormatLength * sizeof(WCHAR) );
+ *lpFormat = '%';
+
+ PAL_wcscat( lpFormat, lpPrintfString );
+
+ lpBuffer = (LPWSTR) PAL_malloc(tmpSize*sizeof(WCHAR));
+
+ /* try until the buffer is big enough */
+ while (TRUE)
+ {
+ if (!lpBuffer)
+ {
+ ERROR("Unable to allocate memory\n");
+ SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+ PAL_free(lpFormat);
+ return NULL;
+ }
+ nBufferLength = _snwprintf_s( lpBuffer, tmpSize, tmpSize,
+ lpFormat, lpInsertString);
+
+ if ((nBufferLength >= 0) && (nBufferLength != tmpSize))
+ {
+ break; /* succeeded */
+ }
+ else
+ {
+ tmpSize *= 2;
+ lpBuffer2 = static_cast<WCHAR *>(
+ PAL_realloc(lpBuffer, tmpSize*sizeof(WCHAR)));
+ if (lpBuffer2 == NULL)
+ PAL_free(lpBuffer);
+ lpBuffer = lpBuffer2;
+ }
+ }
+
+ PAL_free( lpFormat );
+ lpFormat = NULL;
+
+ return lpBuffer;
+}
+
+/*++
+Function:
+ FormatMessageW
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+FormatMessageW(
+ IN DWORD dwFlags,
+ IN LPCVOID lpSource,
+ IN DWORD dwMessageId,
+ IN DWORD dwLanguageId,
+ OUT LPWSTR lpBuffer,
+ IN DWORD nSize,
+ IN va_list *Arguments)
+{
+ BOOL bIgnoreInserts = FALSE;
+ BOOL bIsVaList = TRUE;
+ BOOL bIsLocalAlloced = FALSE;
+ LPWSTR lpSourceString = NULL;
+ UINT nCount = 0;
+ LPWSTR lpReturnString = NULL;
+ LPWSTR lpWorkingString = NULL;
+
+
+ PERF_ENTRY(FormatMessageW);
+ ENTRY( "FormatMessageW(dwFlags=%#x, lpSource=%p, dwMessageId=%#x, "
+ "dwLanguageId=%#x, lpBuffer=%p, nSize=%u, va_list=%p)\n",
+ dwFlags, lpSource, dwMessageId, dwLanguageId, lpBuffer, nSize,
+ Arguments);
+
+ /* Sanity checks. */
+ if ( dwFlags & FORMAT_MESSAGE_FROM_STRING && !lpSource )
+ {
+ /* This behavior is different then in Windows.
+ Windows would just crash.*/
+ ERROR( "lpSource cannot be NULL.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto exit;
+ }
+
+ if ( !(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER ) && !lpBuffer )
+ {
+ /* This behavior is different then in Windows.
+ Windows would just crash.*/
+ ERROR( "lpBuffer cannot be NULL, if "
+ " FORMAT_MESSAGE_ALLOCATE_BUFFER is not specified.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto exit;
+ }
+
+ if ( ( dwFlags & FORMAT_MESSAGE_FROM_STRING ) &&
+ ( dwFlags & FORMAT_MESSAGE_FROM_SYSTEM ) )
+ {
+ ERROR( "These flags cannot co-exist. You can either "
+ "specify FORMAT_MESSAGE_FROM_STRING, or "
+ "FORMAT_MESSAGE_FROM_SYSTEM.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto exit;
+ }
+
+ if ( !( dwFlags & FORMAT_MESSAGE_FROM_STRING ) &&
+ ( dwLanguageId != 0
+#if ENABLE_DOWNLEVEL_FOR_NLS
+ && dwLanguageId != MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT )
+#endif
+ ) )
+ {
+ ERROR( "Invalid language indentifier.\n" );
+ SetLastError( ERROR_RESOURCE_LANG_NOT_FOUND );
+ goto exit;
+ }
+
+ /* Parameter processing. */
+ if ( dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER )
+ {
+ TRACE( "Allocated %d TCHARs. Don't forget to call LocalFree to "
+ "free the memory when done.\n", nSize );
+ bIsLocalAlloced = TRUE;
+ }
+
+ if ( dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS )
+ {
+ bIgnoreInserts = TRUE;
+ }
+
+ if ( dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY )
+ {
+ if ( !Arguments && !bIgnoreInserts )
+ {
+ ERROR( "The va_list cannot be NULL.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto exit;
+ }
+ else
+ {
+ bIsVaList = FALSE;
+ }
+ }
+
+ if ( dwFlags & FORMAT_MESSAGE_FROM_STRING )
+ {
+ lpSourceString = (LPWSTR)lpSource;
+ }
+ else if ( dwFlags & FORMAT_MESSAGE_FROM_SYSTEM )
+ {
+ if ((dwMessageId & 0xFFFF0000) == 0x80070000)
+ {
+ // This message has been produced by HRESULT_FROM_WIN32. Undo its work.
+ dwMessageId &= 0xFFFF;
+ }
+
+ lpWorkingString = lpReturnString =
+ FMTMSG_GetMessageString( dwMessageId );
+
+ if ( !lpWorkingString )
+ {
+ ERROR( "Unable to find the message %d.\n", dwMessageId );
+ SetLastError( ERROR_INTERNAL_ERROR );
+ nCount = 0;
+ goto exit;
+ }
+
+ nCount = PAL_wcslen( lpWorkingString );
+
+ if ( !bIsLocalAlloced && nCount > nSize )
+ {
+ ERROR( "Insufficient buffer.\n" );
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ lpWorkingString = NULL;
+ nCount = 0;
+ goto exit;
+ }
+ if ( !lpWorkingString )
+ {
+ ERROR( "Invalid error indentifier.\n" );
+ SetLastError( ERROR_INVALID_ADDRESS );
+ }
+ goto exit;
+ }
+ else
+ {
+ ERROR( "Unknown flag.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ goto exit;
+ }
+
+ if ( nSize == 0 && bIsLocalAlloced )
+ {
+ nSize = 1;
+ }
+
+ lpWorkingString = static_cast<WCHAR *>(
+ LocalAlloc( LMEM_FIXED, nSize * sizeof( WCHAR ) ) );
+ if ( !lpWorkingString )
+ {
+ ERROR( "Unable to allocate memory for the working string.\n" );
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ goto exit;
+ }
+
+
+ /* Process the string. */
+ lpReturnString = lpWorkingString;
+ while ( *lpSourceString )
+ {
+ if ( *lpSourceString == '%' && !bIgnoreInserts )
+ {
+ lpSourceString++;
+ /* Escape sequences. */
+ if ( *lpSourceString == '0' )
+ {
+ /* Terminates a message without a newline character. */
+ *lpWorkingString = '\0';
+ goto exit;
+ }
+ else if ( PAL_iswdigit( *lpSourceString ) )
+ {
+ /* Get the insert number. */
+ WCHAR Number[] = { '\0', '\0', '\0' };
+ SIZE_T Index = 0;
+
+ Number[ 0 ] = *lpSourceString;
+ lpSourceString++;
+
+ if ( PAL_iswdigit( *lpSourceString ) )
+ {
+ Number[ 1 ] = *lpSourceString;
+ lpSourceString++;
+ if ( PAL_iswdigit( *lpSourceString ) )
+ {
+ ERROR( "Invalid insert indentifier.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ lpWorkingString = NULL;
+ nCount = 0;
+ goto exit;
+ }
+ }
+ Index = FMTMSG__watoi( Number );
+ if ( Index == 0 )
+ {
+ ERROR( "Invalid insert indentifier.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ lpWorkingString = NULL;
+ nCount = 0;
+ goto exit;
+ }
+ if ( *lpSourceString == '!' )
+ {
+ LPWSTR lpInsertString = NULL;
+ LPWSTR lpPrintfString = NULL;
+ LPWSTR lpStartOfFormattedString = NULL;
+ UINT nPrintfLength = 0;
+ LPWSTR lpFormattedString = NULL;
+ UINT nFormattedLength = 0;
+
+ if ( !bIsVaList )
+ {
+ lpInsertString = ((LPWSTR*)Arguments)[ Index - 1 ];
+ }
+ else
+ {
+ va_list TheArgs;
+
+ va_copy(TheArgs, *Arguments);
+ UINT i = 0;
+ for ( ; i < Index; i++ )
+ {
+ lpInsertString = va_arg( TheArgs, LPWSTR );
+ }
+ }
+
+ /* Calculate the length, and extract the printf string.*/
+ lpSourceString++;
+ {
+ LPWSTR p = PAL_wcschr( lpSourceString, '!' );
+
+ if ( NULL == p )
+ {
+ nPrintfLength = 0;
+ }
+ else
+ {
+ nPrintfLength = p - lpSourceString;
+ }
+ }
+
+ lpPrintfString =
+ (LPWSTR)PAL_malloc( ( nPrintfLength + 1 ) * sizeof( WCHAR ) );
+
+ if ( !lpPrintfString )
+ {
+ ERROR( "Unable to allocate memory.\n" );
+ SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+ lpWorkingString = NULL;
+ nCount = 0;
+ goto exit;
+ }
+
+ PAL_wcsncpy( lpPrintfString, lpSourceString, nPrintfLength );
+ *( lpPrintfString + nPrintfLength ) = '\0';
+
+ lpStartOfFormattedString = lpFormattedString =
+ FMTMSG_ProcessPrintf( *lpPrintfString,
+ lpPrintfString,
+ lpInsertString);
+
+ if ( !lpFormattedString )
+ {
+ ERROR( "Unable to process the format string.\n" );
+ /* Function will set the error code. */
+ PAL_free( lpPrintfString );
+ lpWorkingString = NULL;
+ goto exit;
+ }
+
+
+ nFormattedLength = PAL_wcslen( lpFormattedString );
+
+ /* Append the processed printf string into the working string */
+ while ( *lpFormattedString )
+ {
+ _CHECKED_ADD_TO_STRING( *lpFormattedString );
+ lpFormattedString++;
+ }
+
+ lpSourceString += nPrintfLength + 1;
+ PAL_free( lpPrintfString );
+ PAL_free( lpStartOfFormattedString );
+ lpPrintfString = lpFormattedString = NULL;
+ }
+ else
+ {
+ /* The printf format string defaults to 's'.*/
+ LPWSTR lpInsert = NULL;
+
+ if ( !bIsVaList )
+ {
+ lpInsert = ((LPWSTR*)Arguments)[Index - 1];
+ }
+ else
+ {
+ va_list TheArgs;
+ va_copy(TheArgs, *Arguments);
+ UINT i = 0;
+ for ( ; i < Index; i++ )
+ {
+ lpInsert = va_arg( TheArgs, LPWSTR );
+ }
+ }
+
+ while ( *lpInsert )
+ {
+ _CHECKED_ADD_TO_STRING( *lpInsert );
+ lpInsert++;
+ }
+ }
+ }
+ /* Format specifiers. */
+ else if ( *lpSourceString == '%' )
+ {
+ _CHECKED_ADD_TO_STRING( '%' );
+ lpSourceString++;
+ }
+ else if ( *lpSourceString == 'n' )
+ {
+ /* Hard line break. */
+ _CHECKED_ADD_TO_STRING( '\n' );
+ lpSourceString++;
+ }
+ else if ( *lpSourceString == '.' )
+ {
+ _CHECKED_ADD_TO_STRING( '.' );
+ lpSourceString++;
+ }
+ else if ( *lpSourceString == '!' )
+ {
+ _CHECKED_ADD_TO_STRING( '!' );
+ lpSourceString++;
+ }
+ else if ( !*lpSourceString )
+ {
+ ERROR( "Invalid parameter.\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ lpWorkingString = NULL;
+ nCount = 0;
+ goto exit;
+ }
+ else /* Append the character. */
+ {
+ _CHECKED_ADD_TO_STRING( *lpSourceString );
+ lpSourceString++;
+ }
+ }/* END if ( *lpSourceString == '%' ) */
+ else
+ {
+ /* In Windows if FormatMessage is called with ignore inserts,
+ then FormatMessage strips %1!s! down to %1, since string is the
+ default. */
+ if ( bIgnoreInserts && *lpSourceString == '!' &&
+ *( lpSourceString + 1 ) == 's' )
+ {
+ LPWSTR lpLastBang = PAL_wcschr( lpSourceString + 1, '!' );
+
+ if ( lpLastBang && ( 2 == lpLastBang - lpSourceString ) )
+ {
+ lpSourceString = lpLastBang + 1;
+ }
+ else
+ {
+ ERROR( "Mal-formed string\n" );
+ SetLastError( ERROR_INVALID_PARAMETER );
+ lpWorkingString = NULL;
+ nCount = 0;
+ goto exit;
+ }
+ }
+ else
+ {
+ /* Append to the string. */
+ _CHECKED_ADD_TO_STRING( *lpSourceString );
+ lpSourceString++;
+ }
+ }
+ }
+
+ /* Terminate the message. */
+ _CHECKED_ADD_TO_STRING( '\0' );
+ /* NULL does not count. */
+ nCount--;
+
+exit: /* Function clean-up and exit. */
+ if ( lpWorkingString )
+ {
+ if ( bIsLocalAlloced )
+ {
+ TRACE( "Assigning the buffer to the pointer.\n" );
+ // when FORMAT_MESSAGE_ALLOCATE_BUFFER is specified, nSize
+ // does not specify the size of lpBuffer, rather it specifies
+ // the minimum size of the string
+ // as such we have to blindly assume that lpBuffer has enough space to
+ // store PVOID
+ // might cause a prefast warning, but there is no good way to suppress it yet
+ _ASSERTE(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER);
+ *((LPVOID*)lpBuffer) = (LPVOID)lpReturnString;
+ }
+ else /* Only delete lpReturnString if the caller has their own buffer.*/
+ {
+ TRACE( "Copying the string into the buffer.\n" );
+ PAL_wcsncpy( lpBuffer, lpReturnString, nCount + 1 );
+ LocalFree( lpReturnString );
+ }
+ }
+ else /* Error, something occurred. */
+ {
+ if ( lpReturnString )
+ {
+ LocalFree( lpReturnString );
+ }
+ }
+ LOGEXIT( "FormatMessageW returns %d.\n", nCount );
+ PERF_EXIT(FormatMessageW);
+ return nCount;
+}
diff --git a/src/pal/src/misc/identity.cpp b/src/pal/src/misc/identity.cpp
new file mode 100644
index 0000000000..e983b1265c
--- /dev/null
+++ b/src/pal/src/misc/identity.cpp
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+misc/identity.cpp
+
+Abstract:
+
+Implementation of GetComputerNameW and GetUserNameW functions.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/misc.h"
+#include "pal/thread.hpp"
+#include "pal/identity.hpp"
+#include "pal/malloc.hpp"
+
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#if HAVE_SYSCONF && defined(_SC_GETPW_R_SIZE_MAX)
+#include <limits.h> // for INT_MAX
+#endif
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+#if HAVE_GETPWUID_R
+
+#define DEFAULT_PASSWORD_BUFFER_SIZE 1024
+static DWORD dwInitialPasswdBufferSize = DEFAULT_PASSWORD_BUFFER_SIZE;
+
+#else // HAVE_GETPWUID_R
+
+static CRITICAL_SECTION identity_critsec;
+
+#endif // HAVE_GETPWUID_R
+
+
+/*++
+Function:
+ IdentityInitialize
+
+Intitialization function called from PAL_Initialize.
+Initializes the critical section for the case when thread-safe
+getpwuid_r is not available.
+
+--*/
+BOOL
+IdentityInitialize(void)
+{
+#if HAVE_GETPWUID_R
+
+#if HAVE_SYSCONF && defined(_SC_GETPW_R_SIZE_MAX)
+ long lBufferSize = 0;
+ lBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX);
+
+ if ((long)INT_MAX < lBufferSize)
+ {
+ ERROR("sysconf(_SC_GETPW_R_SIZE_MAX) returns %ld which is > INT_MAX (%u)\n",
+ lBufferSize, INT_MAX);
+ return FALSE;
+ }
+
+ if (0 >= (int)(lBufferSize))
+ {
+ WARN("sysconf(_SC_GETPW_R_SIZE_MAX) returns %ld, using %u as the buffer size instead\n",
+ lBufferSize, dwInitialPasswdBufferSize);
+ }
+ else
+ {
+ TRACE("sysconf(_SC_GETPW_R_SIZE_MAX) returns %ld\n", lBufferSize);
+ dwInitialPasswdBufferSize = (DWORD)lBufferSize;
+ }
+
+#endif // HAVE_SYSCONF && _SC_GETPW_R_SIZE_MAX
+
+#else // HAVE_GETPWUID_R
+
+ InternalInitializeCriticalSection(&identity_critsec);
+
+#endif // HAVE_GETPWUID_R
+
+ return TRUE;
+}
+
+
+/*++
+Function:
+ IdentityCleanup
+
+Termination function called from PAL_Terminate.
+Deletes the critical section for the case when thread-safe
+getpwuid_r is not available.
+
+--*/
+void
+IdentityCleanup(void)
+{
+#if !HAVE_GETPWUID_R
+ InternalDeleteCriticalSection(&identity_critsec);
+#endif
+}
+
+/*++
+Function:
+ GetUserNameW
+
+Uses getpwuid_r to get the user name and if it's not available uses
+getpwuid (with the safety of a critical section). See MSDN for functional spec.
+
+--*/
+PALIMPORT
+BOOL
+PALAPI
+GetUserNameW(
+ OUT LPWSTR lpBuffer, // address of name buffer
+ IN OUT LPDWORD nSize ) // address of size of name buffer
+{
+ BOOL fRet = FALSE;
+ struct passwd *pPasswd = NULL;
+ char *szUserName = NULL;
+ DWORD cwchLen = 0;
+ int iEuid = -1;
+ int iRet = -1;
+ CPalThread *pPalThread = InternalGetCurrentThread();
+
+#if HAVE_GETPWUID_R
+
+ char *pchBuffer = NULL;
+ DWORD dwBufLen = 0;
+ struct passwd sPasswd;
+
+#endif // HAVE_GETPWUID_R
+
+ PERF_ENTRY(GetUserNameW);
+ ENTRY("GetUserNameW(lpBuffer = %p, nSize = %p (%d)\n",
+ lpBuffer, nSize, nSize?*nSize:0);
+
+ iEuid = geteuid();
+
+ if (NULL == lpBuffer || NULL == nSize)
+ {
+ ERROR("lpBuffer == NULL or nSize == NULL");
+ pPalThread->SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+#if HAVE_GETPWUID_R
+
+ dwBufLen = dwInitialPasswdBufferSize;
+
+ while (NULL == pPasswd)
+ {
+ pchBuffer = (char*) PAL_malloc(sizeof(pchBuffer[0]) * dwBufLen);
+ if (NULL == pchBuffer)
+ {
+ pPalThread->SetLastError(ERROR_OUTOFMEMORY);
+ goto done;
+ }
+
+ iRet = InternalGetpwuid_r(pPalThread, iEuid, &sPasswd, pchBuffer, dwBufLen, &pPasswd);
+ if (0 != iRet)
+ {
+ WARN("getpwuid_r(%d) returns %d for a buffer size of %d, error string is %s\n",
+ iEuid, iRet, dwBufLen, strerror(iRet));
+
+ if (ERANGE == iRet) // need a bigger buffer
+ {
+ PAL_free(pchBuffer);
+ pchBuffer = NULL;
+ pPasswd = NULL;
+ dwBufLen *= 2; // double the buffer
+ continue; // try again
+ }
+
+ pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ // Unfortunately, HPUX returns success and result = NULL even when the buffer size is small
+ // (instead of returning ERANGE error). But, since we are using either
+ // sysconf(_SC_GETPW_R_SIZE_MAX) or the HP recommended value of 1024, buffer should always
+ // be big enough for getpwuid_r.
+
+ if (NULL == pPasswd || NULL == pPasswd->pw_name)
+ {
+ // No matching entry found! something failed somewhere.
+ ERROR("getpwuid_r(%d) returned %p with name NULL!\n", iEuid, pPasswd);
+ pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+ }
+
+ szUserName = pPasswd->pw_name;
+
+#else // HAVE_GETPWUID_R
+
+ InternalEnterCriticalSection(pPalThread, &identity_critsec);
+ pPasswd = getpwuid(iEuid);
+
+ if ((NULL == pPasswd) || (NULL == pPasswd->pw_name))
+ {
+ InternalLeaveCriticalSection(pPalThread, &identity_critsec);
+ ERROR("getpwuid(%d) returned %p with name NULL! error (%d) is %s\n",
+ iEuid, pPasswd, errno, strerror(errno));
+ pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ // make a copy so that we can modify it
+ szUserName = strdup(pPasswd->pw_name);
+ if (NULL == szUserName)
+ {
+ InternalLeaveCriticalSection(pPalThread, &identity_critsec);
+ pPalThread->SetLastError(ERROR_OUTOFMEMORY);
+ goto done;
+ }
+
+ InternalLeaveCriticalSection(pPalThread, &identity_critsec);
+#endif // HAVE_GETPWUID_R
+
+ // truncate the user name if it exceeds the maximum allowed limit
+ if (strlen(szUserName) > UNLEN)
+ {
+ szUserName[UNLEN] = '\0';
+ }
+
+ // Copy from pPasswd->pw_name
+ cwchLen = MultiByteToWideChar(CP_ACP, 0, szUserName, -1, lpBuffer, *nSize);
+ if (0 == cwchLen)
+ {
+ ERROR ("MultiByteToWideChar failed with error %d when trying to convert the username "
+ "%s to wide char\n", pPalThread->GetLastError(), szUserName);
+ if (ERROR_INSUFFICIENT_BUFFER == pPalThread->GetLastError())
+ {
+ // Find the required size (including NULL)
+ cwchLen = MultiByteToWideChar(CP_ACP, 0, szUserName, -1, NULL, 0);
+ if (0 == cwchLen)
+ {
+ ERROR ("MultiByteToWideChar failed with error %d when trying to find the size of "
+ "%s in wide chars\n", pPalThread->GetLastError(), szUserName);
+ pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ else
+ {
+ // Update the required size
+ *nSize = cwchLen;
+ pPalThread->SetLastError(ERROR_MORE_DATA);
+ }
+ }
+ goto done;
+ }
+
+ *nSize = cwchLen;
+ fRet = TRUE;
+
+done:
+#if HAVE_GETPWUID_R
+ if (NULL != pchBuffer)
+ {
+ PAL_free(pchBuffer);
+ }
+#else // HAVE_GETPWUID_R
+ if (NULL != szUserName)
+ {
+ PAL_free(szUserName);
+ }
+#endif // HAVE_GETPWUID_R
+
+ LOGEXIT("GetUserNameW returning BOOL %d\n", fRet);
+ PERF_EXIT(GetUserNameW);
+ return fRet;
+}
+
+#ifndef MAXHOSTNAMELEN
+// AIX doesn't have MAXHOSTNAMELEN, it recommends using 256
+#define MAXHOSTNAMELEN 256
+#endif // MAXHOSTNAMELEN
+
+/*++
+Function:
+ GetComputerNameW
+
+Uses gethostname to get the computer name. See MSDN for functional spec.
+
+--*/
+PALIMPORT
+BOOL
+PALAPI
+GetComputerNameW(
+ OUT LPWSTR lpBuffer, // address of name buffer
+ IN OUT LPDWORD nSize) // address of size of name buffer
+{
+ BOOL fRet = FALSE;
+ char szHostName[MAXHOSTNAMELEN+1];
+ char *pchDot = NULL;
+ DWORD cwchLen = 0;
+ CPalThread *pPalThread = InternalGetCurrentThread();
+
+ PERF_ENTRY(GetComputerNameW);
+ ENTRY("GetComputerNameW(lpBuffer = %p, nSize = %p (%d)\n",
+ lpBuffer, nSize, nSize?*nSize:0);
+
+ if (NULL == lpBuffer || NULL == nSize)
+ {
+ ERROR("lpBuffer == NULL or nSize == NULL");
+ pPalThread->SetLastError(ERROR_INVALID_PARAMETER);
+ goto done;
+ }
+
+ if (0 != gethostname(szHostName, sizeof(szHostName)/sizeof(szHostName[0])))
+ {
+ ERROR("gethostname failed with error (%d) %s\n", errno, strerror(errno));
+ pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
+ goto done;
+ }
+
+ // Null terminate the string
+ szHostName[sizeof(szHostName)/sizeof(szHostName[0])-1] = '\0';
+
+ // some OSes return the hostname with the domain name included.
+ // We want to return only the host part of the name (see the spec for
+ // more details
+ pchDot = strchr(szHostName, '.');
+ if (NULL != pchDot)
+ {
+ *pchDot = '\0'; // remove the domain name info
+ }
+
+ // copy the hostname (including NULL character)
+ cwchLen = MultiByteToWideChar(CP_ACP, 0, szHostName, -1, lpBuffer, *nSize);
+ if (0 == cwchLen)
+ {
+ ERROR ("MultiByteToWideChar failed with error %d when trying to convert the hostname "
+ "%s to wide char\n", pPalThread->GetLastError(), szHostName);
+ if (ERROR_INSUFFICIENT_BUFFER == pPalThread->GetLastError())
+ {
+ // Find the required size (including NULL)
+ cwchLen = MultiByteToWideChar(CP_ACP, 0, szHostName, -1, NULL, 0);
+ if (0 == cwchLen)
+ {
+ ERROR ("MultiByteToWideChar failed with error %d when trying to find the size of "
+ "%s in wide chars\n", pPalThread->GetLastError(), szHostName);
+ pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
+ }
+ else
+ {
+ // Update the required size
+ *nSize = cwchLen - 1; // don't include the NULL
+ pPalThread->SetLastError(ERROR_BUFFER_OVERFLOW);
+ }
+ }
+ goto done;
+ }
+
+ *nSize = cwchLen - 1; // don't include the NULL
+ fRet = TRUE;
+
+done:
+ LOGEXIT("GetComputerNameW returning BOOL %d\n", fRet);
+ PERF_EXIT(GetComputerNameW);
+ return fRet;
+}
+
+#if HAVE_GETPWUID_R
+/*++
+Function:
+ InternalGetpwuid_r
+
+Suspension safe wrapper for getpwuid_r
+--*/
+int
+CorUnix::InternalGetpwuid_r(
+ CPalThread *pPalThread,
+ uid_t uid,
+ struct passwd *pPasswd,
+ char *pchBuffer,
+ size_t nBufSize,
+ struct passwd **ppResult
+ )
+{
+ int iError = 0;
+
+ iError = getpwuid_r(uid, pPasswd, pchBuffer, nBufSize, ppResult);
+
+#if GETPWUID_R_SETS_ERRNO
+ if (0 != iError)
+ {
+ iError = errno; // some systems (AIX) sets errno instead of returning error
+ }
+#endif // GETPWUID_R_SETS_ERRNO
+
+ return iError;
+}
+#endif // HAVE_GETPWUID_R
diff --git a/src/pal/src/misc/miscpalapi.cpp b/src/pal/src/misc/miscpalapi.cpp
new file mode 100644
index 0000000000..0e1234401e
--- /dev/null
+++ b/src/pal/src/misc/miscpalapi.cpp
@@ -0,0 +1,381 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ miscpalapi.c
+
+Abstract:
+
+ Implementation misc PAL APIs
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/process.h"
+#include "pal/module.h"
+#include "pal/malloc.hpp"
+#include "pal/stackstring.hpp"
+
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <pthread.h>
+#include <dlfcn.h>
+
+#if HAVE_BSD_UUID_H
+#include <uuid.h>
+#elif HAVE_LIBUUID_H
+#include <uuid/uuid.h>
+#endif
+
+#include <pal_endian.h>
+
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif // __APPLE__
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+static const char RANDOM_DEVICE_NAME[] ="/dev/random";
+static const char URANDOM_DEVICE_NAME[]="/dev/urandom";
+
+/*++
+
+Function :
+
+ PAL_GetPALDirectoryW
+
+ Returns the fully qualified path name
+ where the PALL DLL was loaded from.
+
+ On failure it returns FALSE and sets the
+ proper LastError code.
+
+--*/
+BOOL
+PAL_GetPALDirectoryW(PathWCharString& lpDirectoryName)
+{
+ LPCWSTR lpFullPathAndName = NULL;
+ LPCWSTR lpEndPoint = NULL;
+ BOOL bRet = FALSE;
+
+ PERF_ENTRY(PAL_GetPALDirectoryW);
+
+ MODSTRUCT *module = LOADGetPalLibrary();
+ if (!module)
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto EXIT;
+ }
+ lpFullPathAndName = module->lib_name;
+ if (lpFullPathAndName == NULL)
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto EXIT;
+ }
+ lpEndPoint = PAL_wcsrchr( lpFullPathAndName, '/' );
+ if ( lpEndPoint )
+ {
+ /* The path that we return is required to have
+ the trailing slash on the end.*/
+ lpEndPoint++;
+
+
+ if(!lpDirectoryName.Set(lpFullPathAndName,lpEndPoint - lpFullPathAndName))
+ {
+ ASSERT( "The buffer was not large enough.\n" );
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ goto EXIT;
+ }
+
+ bRet = TRUE;
+ }
+ else
+ {
+ ASSERT( "Unable to determine the path.\n" );
+ /* Error path, should not be executed. */
+ SetLastError( ERROR_INTERNAL_ERROR );
+ }
+
+EXIT:
+ PERF_EXIT(PAL_GetPALDirectoryW);
+ return bRet;
+}
+
+BOOL
+PAL_GetPALDirectoryA(PathCharString& lpDirectoryName)
+{
+ BOOL bRet;
+ PathWCharString directory;
+
+ PERF_ENTRY(PAL_GetPALDirectoryA);
+
+ bRet = PAL_GetPALDirectoryW(directory);
+
+ if (bRet)
+ {
+
+ int length = WideCharToMultiByte(CP_ACP, 0, directory.GetString(), -1, NULL, 0, NULL, 0);
+ LPSTR DirectoryName = lpDirectoryName.OpenStringBuffer(length);
+ if (NULL == DirectoryName)
+ {
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ bRet = FALSE;
+ }
+
+ length = WideCharToMultiByte(CP_ACP, 0, directory.GetString(), -1, DirectoryName, length, NULL, 0);
+
+ if (0 == length)
+ {
+ bRet = FALSE;
+ length++;
+ }
+
+ lpDirectoryName.CloseBuffer(length - 1);
+ }
+
+ PERF_EXIT(PAL_GetPALDirectoryA);
+ return bRet;
+}
+
+/*++
+
+Function :
+
+ PAL_GetPALDirectoryW
+
+ Returns the fully qualified path name
+ where the PALL DLL was loaded from.
+
+ On failure it returns FALSE and sets the
+ proper LastError code.
+
+See rotor_pal.doc for more details.
+
+--*/
+PALIMPORT
+BOOL
+PALAPI
+PAL_GetPALDirectoryW( OUT LPWSTR lpDirectoryName, IN OUT UINT* cchDirectoryName )
+{
+ PathWCharString directory;
+ BOOL bRet;
+ PERF_ENTRY(PAL_GetPALDirectoryW);
+ ENTRY( "PAL_GetPALDirectoryW( %p, %d )\n", lpDirectoryName, *cchDirectoryName );
+
+ bRet = PAL_GetPALDirectoryW(directory);
+
+ if (bRet) {
+
+ if (directory.GetCount() > *cchDirectoryName)
+ {
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ bRet = FALSE;
+ }
+ else
+ {
+ PAL_wcscpy(lpDirectoryName, directory.GetString());
+ }
+
+ *cchDirectoryName = directory.GetCount();
+ }
+
+ LOGEXIT( "PAL_GetPALDirectoryW returns BOOL %d.\n", bRet);
+ PERF_EXIT(PAL_GetPALDirectoryW);
+ return bRet;
+
+}
+
+PALIMPORT
+BOOL
+PALAPI
+PAL_GetPALDirectoryA(
+ OUT LPSTR lpDirectoryName,
+ IN UINT* cchDirectoryName)
+{
+ BOOL bRet;
+ PathCharString directory;
+
+ PERF_ENTRY(PAL_GetPALDirectoryA);
+ ENTRY( "PAL_GetPALDirectoryA( %p, %d )\n", lpDirectoryName, *cchDirectoryName );
+
+ bRet = PAL_GetPALDirectoryA(directory);
+
+ if (bRet)
+ {
+ if (directory.GetCount() > *cchDirectoryName)
+ {
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ bRet = FALSE;
+ *cchDirectoryName = directory.GetCount();
+ }
+ else if (strcpy_s(lpDirectoryName, directory.GetCount(), directory.GetString()) == SAFECRT_SUCCESS)
+ {
+ }
+ else
+ {
+ bRet = FALSE;
+ }
+ }
+
+ LOGEXIT( "PAL_GetPALDirectoryA returns BOOL %d.\n", bRet);
+ PERF_EXIT(PAL_GetPALDirectoryA);
+ return bRet;
+}
+
+BOOL
+PALAPI
+PAL_Random(
+ IN BOOL bStrong,
+ IN OUT LPVOID lpBuffer,
+ IN DWORD dwLength)
+{
+ int rand_des = -1;
+ BOOL bRet = FALSE;
+ DWORD i;
+ char buf;
+ long num = 0;
+ static BOOL sMissingDevRandom;
+ static BOOL sMissingDevURandom;
+ static BOOL sInitializedMRand;
+
+ PERF_ENTRY(PAL_Random);
+ ENTRY("PAL_Random(bStrong=%d, lpBuffer=%p, dwLength=%d)\n",
+ bStrong, lpBuffer, dwLength);
+
+ i = 0;
+
+ if (bStrong == TRUE && i < dwLength && !sMissingDevRandom)
+ {
+ // request non-blocking access to avoid hangs if the /dev/random is exhausted
+ // or just simply broken
+ if ((rand_des = PAL__open(RANDOM_DEVICE_NAME, O_RDONLY | O_NONBLOCK)) == -1)
+ {
+ if (errno == ENOENT)
+ {
+ sMissingDevRandom = TRUE;
+ }
+ else
+ {
+ ASSERT("PAL__open() failed, errno:%d (%s)\n", errno, strerror(errno));
+ }
+
+ // Back off and try /dev/urandom.
+ }
+ else
+ {
+ for( ; i < dwLength; i++)
+ {
+ if (read(rand_des, &buf, 1) < 1)
+ {
+ // the /dev/random pool has been exhausted. Fall back
+ // to /dev/urandom for the remainder of the buffer.
+ break;
+ }
+
+ *(((BYTE*)lpBuffer) + i) ^= buf;
+ }
+
+ close(rand_des);
+ }
+ }
+
+ if (i < dwLength && !sMissingDevURandom)
+ {
+ if ((rand_des = PAL__open(URANDOM_DEVICE_NAME, O_RDONLY)) == -1)
+ {
+ if (errno == ENOENT)
+ {
+ sMissingDevURandom = TRUE;
+ }
+ else
+ {
+ ASSERT("PAL__open() failed, errno:%d (%s)\n", errno, strerror(errno));
+ }
+
+ // Back off and try mrand48.
+ }
+ else
+ {
+ for( ; i < dwLength; i++)
+ {
+ if (read(rand_des, &buf, 1) < 1)
+ {
+ // Fall back to srand48 for the remainder of the buffer.
+ break;
+ }
+
+ *(((BYTE*)lpBuffer) + i) ^= buf;
+ }
+
+ close(rand_des);
+ }
+ }
+
+ if (!sInitializedMRand)
+ {
+ srand48(time(NULL));
+ sInitializedMRand = TRUE;
+ }
+
+ // always xor srand48 over the whole buffer to get some randomness
+ // in case /dev/random is not really random
+
+ for(i = 0; i < dwLength; i++)
+ {
+ if (i % sizeof(long) == 0) {
+ num = mrand48();
+ }
+
+ *(((BYTE*)lpBuffer) + i) ^= num;
+ num >>= 8;
+ }
+
+ bRet = TRUE;
+
+ LOGEXIT("PAL_Random returns %d\n", bRet);
+ PERF_EXIT(PAL_Random);
+ return bRet;
+}
+
+HRESULT
+PALAPI
+CoCreateGuid(OUT GUID * pguid)
+{
+#if HAVE_BSD_UUID_H
+ uuid_t uuid;
+ uint32_t status;
+ uuid_create(&uuid, &status);
+ if (status != uuid_s_ok)
+ {
+ ASSERT("Unexpected uuid_create failure (status=%u)\n", status);
+ PROCAbort();
+ }
+
+ // Encode the uuid with little endian.
+ uuid_enc_le(pguid, &uuid);
+#elif HAVE_LIBUUID_H
+ uuid_generate_random(*(uuid_t*)pguid);
+
+ // Change the byte order of the Data1, 2 and 3, since the uuid_generate_random
+ // generates them with big endian while GUIDS need to have them in little endian.
+ pguid->Data1 = SWAP32(pguid->Data1);
+ pguid->Data2 = SWAP16(pguid->Data2);
+ pguid->Data3 = SWAP16(pguid->Data3);
+#else
+ #error Don't know how to generate UUID on this platform
+#endif
+ return 0;
+}
diff --git a/src/pal/src/misc/msgbox.cpp b/src/pal/src/misc/msgbox.cpp
new file mode 100644
index 0000000000..b3041b1422
--- /dev/null
+++ b/src/pal/src/misc/msgbox.cpp
@@ -0,0 +1,416 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ msgbox.c
+
+Abstract:
+
+ Implementation of Message Box.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/critsect.h"
+#include "pal/dbgmsg.h"
+#include "pal/misc.h"
+
+#include <syslog.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+CRITICAL_SECTION msgbox_critsec;
+
+
+/*++
+Function :
+ MsgBoxInitialize
+
+ Initialize the critical sections.
+
+Return value:
+ TRUE if initialize succeeded
+ FALSE otherwise
+
+--*/
+BOOL
+MsgBoxInitialize( void )
+{
+ TRACE( "Initialising the critical section.\n" );
+ InternalInitializeCriticalSection(&msgbox_critsec);
+
+ return TRUE;
+}
+
+/*++
+Function :
+ MsgBoxCleanup
+
+ Deletes the critical sections.
+
+--*/
+void MsgBoxCleanup( void )
+{
+ TRACE( "Deleting the critical section.\n" );
+ DeleteCriticalSection( &msgbox_critsec );
+}
+
+
+
+#ifdef __APPLE__
+#include "CoreFoundation/CFUserNotification.h"
+#include "CoreFoundation/CFString.h"
+#include "Security/AuthSession.h"
+#endif // __APPLE__
+
+
+/*++
+Function:
+ MessageBoxW
+
+This is a small subset of MessageBox that simply logs a message to the
+system logging facility and returns. A typical log entry will look
+like:
+
+May 23 15:48:10 rice example1: MessageBox: Caption: Error Text
+
+Note:
+ hWnd should always be NULL.
+
+See MSDN doc.
+--*/
+int
+PALAPI
+MessageBoxW(
+ IN LPVOID hWnd,
+ IN LPCWSTR lpText,
+ IN LPCWSTR lpCaption,
+ IN UINT uType)
+{
+ CHAR *text = NULL;
+ CHAR *caption = NULL;
+ INT len = 0;
+ INT rc = 0;
+
+ PERF_ENTRY(MessageBoxW);
+ ENTRY( "MessageBoxW (hWnd=%p, lpText=%p (%S), lpCaption=%p (%S), uType=%#x)\n",
+ hWnd, lpText?lpText:W16_NULLSTRING, lpText?lpText:W16_NULLSTRING,
+ lpCaption?lpCaption:W16_NULLSTRING,
+ lpCaption?lpCaption:W16_NULLSTRING, uType );
+
+ if (hWnd != NULL)
+ {
+ ASSERT("hWnd != NULL");
+ }
+
+ if(lpText)
+ {
+ len = WideCharToMultiByte(CP_ACP, 0, lpText, -1, NULL, 0, NULL, NULL);
+ if(len)
+ {
+ text = (LPSTR)PAL_malloc(len);
+ if(!text)
+ {
+ ERROR("malloc() failed!\n");
+ SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+ goto error;
+ }
+ if( !WideCharToMultiByte( CP_ACP, 0, lpText, -1, text, len,
+ NULL, NULL))
+ {
+ ASSERT("WideCharToMultiByte failure\n");
+ SetLastError( ERROR_INTERNAL_ERROR );
+ goto error;
+ }
+ }
+ else
+ {
+ ASSERT("WideCharToMultiByte failure\n");
+ SetLastError( ERROR_INTERNAL_ERROR );
+ goto error;
+ }
+ }
+ else
+ {
+ WARN("No message text\n");
+
+ if (NULL == (text = PAL__strdup("(no message text)")))
+ {
+ ASSERT("strdup() failed\n");
+ SetLastError( ERROR_INTERNAL_ERROR );
+ goto error;
+ }
+ }
+ if (lpCaption)
+ {
+ len = WideCharToMultiByte( CP_ACP, 0, lpCaption, -1, NULL, 0,
+ NULL, NULL);
+ if(len)
+ {
+ caption = (CHAR*)PAL_malloc(len);
+ if(!caption)
+ {
+ ERROR("malloc() failed!\n");
+ SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+ goto error;
+ }
+ if( !WideCharToMultiByte( CP_ACP, 0, lpCaption, -1, caption, len,
+ NULL, NULL))
+ {
+ ASSERT("WideCharToMultiByte failure\n");
+ SetLastError( ERROR_INTERNAL_ERROR );
+ goto error;
+ }
+ }
+ else
+ {
+ ASSERT("WideCharToMultiByte failure\n");
+ SetLastError( ERROR_INTERNAL_ERROR );
+ goto error;
+ }
+ }
+ else
+ {
+ if (NULL == (caption = PAL__strdup("Error")))
+ {
+ ERROR("strdup() failed\n");
+ SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+ goto error;
+ }
+ }
+
+ rc = MessageBoxA(hWnd, text, caption, uType);
+
+error:
+ PAL_free(caption);
+ PAL_free(text);
+
+
+ LOGEXIT("MessageBoxW returns %d\n", rc);
+ PERF_EXIT(MessageBoxW);
+ return rc;
+}
+
+
+/*++
+Function:
+ MessageBoxA
+
+This is a small subset of MessageBox that simply logs a message to the
+system logging facility and returns. A typical log entry will look
+like:
+
+May 23 15:48:10 rice example1: MessageBox: Caption: Error Text
+
+Note:
+ hWnd should always be NULL.
+
+See MSDN doc.
+--*/
+int
+PALAPI
+MessageBoxA(
+ IN LPVOID hWnd,
+ IN LPCSTR lpText,
+ IN LPCSTR lpCaption,
+ IN UINT uType)
+{
+ INT rc = 0;
+
+ PERF_ENTRY(MessageBoxA);
+ ENTRY( "MessageBoxA (hWnd=%p, lpText=%p (%s), lpCaption=%p (%s), uType=%#x)\n",
+ hWnd, lpText?lpText:"NULL", lpText?lpText:"NULL",
+ lpCaption?lpCaption:"NULL",
+ lpCaption?lpCaption:"NULL", uType );
+
+ if (hWnd != NULL)
+ {
+ ASSERT("hWnd != NULL");
+ }
+
+ if (lpText == NULL)
+ {
+ WARN("No message text\n");
+
+ lpText = "(no message text)";
+ }
+
+ if (lpCaption == NULL)
+ {
+ lpCaption = "Error";
+ }
+
+ if (uType & MB_DEFMASK)
+ {
+ WARN("No support for alternate default buttons.\n");
+ }
+
+ /* set default status based on the type of button */
+ switch(uType & MB_TYPEMASK)
+ {
+ case MB_OK:
+ rc = IDOK;
+ break;
+
+ case MB_ABORTRETRYIGNORE:
+ rc = IDABORT;
+ break;
+
+ case MB_YESNO:
+ rc = IDNO;
+ break;
+
+ case MB_OKCANCEL :
+ rc = IDCANCEL;
+ break;
+
+ case MB_RETRYCANCEL :
+ rc = IDCANCEL;
+ break;
+
+ default:
+ ASSERT("Bad uType");
+ rc = IDOK;
+ break;
+ }
+
+ PALCEnterCriticalSection( &msgbox_critsec);
+
+#ifdef __APPLE__
+ OSStatus osstatus;
+
+ SecuritySessionId secSession;
+ SessionAttributeBits secSessionInfo;
+
+ osstatus = SessionGetInfo(callerSecuritySession, &secSession, &secSessionInfo);
+ if (noErr == osstatus && (secSessionInfo & sessionHasGraphicAccess) != 0)
+ {
+ CFStringRef cfsTitle = CFStringCreateWithCString(kCFAllocatorDefault, lpCaption, kCFStringEncodingUTF8);
+ CFStringRef cfsText = CFStringCreateWithCString(kCFAllocatorDefault, lpText, kCFStringEncodingUTF8);
+ CFStringRef cfsButton1 = NULL;
+ CFStringRef cfsButton2 = NULL;
+ CFStringRef cfsButton3 = NULL;
+ CFOptionFlags alertFlags = 0;
+ CFOptionFlags response;
+
+ switch (uType & MB_TYPEMASK)
+ {
+ case MB_OK:
+ // Nothing needed; since if all the buttons are null, a stock "OK" is used.
+ break;
+
+ case MB_ABORTRETRYIGNORE:
+ // Localization? Would be needed if this were used outside of debugging.
+ cfsButton1 = CFSTR("Abort");
+ cfsButton2 = CFSTR("Retry");
+ cfsButton3 = CFSTR("Ignore");
+ alertFlags = kCFUserNotificationCautionAlertLevel;
+ break;
+
+ case MB_YESNO:
+ cfsButton1 = CFSTR("Yes");
+ cfsButton2 = CFSTR("No");
+ break;
+
+ case MB_OKCANCEL:
+ cfsButton1 = CFSTR("OK");
+ cfsButton2 = CFSTR("Cancel");
+ break;
+
+ case MB_RETRYCANCEL:
+ cfsButton1 = CFSTR("Retry");
+ cfsButton2 = CFSTR("Cancel");
+ break;
+ }
+
+ CFUserNotificationDisplayAlert(0 /* no time out */, alertFlags, NULL /* iconURL */,
+ NULL /* soundURL */, NULL /* localizationURL */, cfsTitle, cfsText, cfsButton1,
+ cfsButton2, cfsButton3, &response);
+
+ switch (uType & MB_TYPEMASK)
+ {
+ case MB_OK:
+ break;
+
+ case MB_ABORTRETRYIGNORE:
+ switch (response)
+ {
+ case kCFUserNotificationDefaultResponse:
+ rc = IDABORT;
+ break;
+ case kCFUserNotificationAlternateResponse:
+ rc = IDRETRY;
+ break;
+ case kCFUserNotificationOtherResponse:
+ rc = IDIGNORE;
+ break;
+ }
+ break;
+
+ case MB_YESNO:
+ switch (response)
+ {
+ case kCFUserNotificationDefaultResponse:
+ rc = IDYES;
+ break;
+ case kCFUserNotificationAlternateResponse:
+ rc = IDNO;
+ break;
+ }
+ break;
+
+ case MB_OKCANCEL:
+ switch (response)
+ {
+ case kCFUserNotificationDefaultResponse:
+ rc = IDOK;
+ break;
+ case kCFUserNotificationAlternateResponse:
+ rc = IDCANCEL;
+ break;
+ }
+ break;
+
+ case MB_RETRYCANCEL:
+ switch (response)
+ {
+ case kCFUserNotificationDefaultResponse:
+ rc = IDRETRY;
+ break;
+ case kCFUserNotificationAlternateResponse:
+ rc = IDCANCEL;
+ break;
+ }
+ break;
+ }
+ }
+ else
+ {
+ // We're not in a login session, e.g., running via ssh, and so bringing
+ // up a message box would be bad form.
+ fprintf ( stderr, "MessageBox: %s: %s", lpCaption, lpText );
+ syslog(LOG_USER|LOG_ERR, "MessageBox: %s: %s", lpCaption, lpText);
+ }
+#else // __APPLE__
+ fprintf ( stderr, "MessageBox: %s: %s", lpCaption, lpText );
+ syslog(LOG_USER|LOG_ERR, "MessageBox: %s: %s", lpCaption, lpText);
+
+ // Some systems support displaying a GUI dialog. (This will suspend the current thread until they hit the
+ // 'OK' button and allow a debugger to be attached).
+ PAL_DisplayDialog(lpCaption, lpText);
+#endif // __APPLE__ else
+
+ PALCLeaveCriticalSection( &msgbox_critsec);
+
+ LOGEXIT("MessageBoxA returns %d\n", rc);
+ PERF_EXIT(MessageBoxA);
+ return rc;
+}
diff --git a/src/pal/src/misc/perftrace.cpp b/src/pal/src/misc/perftrace.cpp
new file mode 100644
index 0000000000..d4fba3367a
--- /dev/null
+++ b/src/pal/src/misc/perftrace.cpp
@@ -0,0 +1,1522 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ misc/perftrace.c
+
+Abstract:
+ Implementation of PAL Performance trace utilities.
+
+
+
+--*/
+
+/* PAL headers */
+
+
+
+#ifdef PAL_PERF
+
+#ifndef PLATFORM_UNIX
+/* PAL Headers */
+#include "perftrace.h"
+
+/* Standard Headers */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#define snprintf _snprintf
+#define MiscGetenv getenv
+#define pthread_getspecific TlsGetValue
+#define THREADSilentGetCurrentThreadId GetCurrentThreadId
+#define getpid GetCurrentProcessId
+#define PAL_fgets fgets // on Windows, we want fgets.
+#define PAL_fwrite fwrite // on Windows, we want fwrite.
+#define PAL_fseek fseek // on Windows, we want fseek.
+
+#else
+/* PAL Headers */
+#include "pal/palinternal.h"
+#include "pal/perftrace.h"
+#include "pal/dbgmsg.h"
+#include "pal/cruntime.h"
+#include "pal/misc.h"
+
+/* Standard headers */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h> /* for pthread_self */
+#include <dirent.h>
+#include <unistd.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+#endif //End of PLATFORM_UNIX
+
+
+#define PAL_PERF_MAX_LOGLINE 0x400 /* 1K */
+#define PAL_PERF_MAX_INPUT 0x1000 /* 4k for single line of input file */
+#define PAL_PERF_MAX_FUNCTION_NAME 128 /* any one want a function name longer than 127 bytes? */
+#define PAL_PERF_PROFILE_BUFFER_SIZE 0x400000 /* 4M */
+#define PAL_PERF_BUFFER_FULL (PAL_PERF_PROFILE_BUFFER_SIZE - PAL_PERF_MAX_LOGLINE ) /* (Buffer size - 1K) */
+
+typedef struct _pal_perf_api_info
+{
+ ULONGLONG entries; /* number of PERF_ENTRY calls for an API function */
+ ULONGLONG counter; /* number of PERF_EXIT calls for an API function */
+ ULONGLONG min_duration; /* Minimum duration in CPU clock ticks in an API function */
+ ULONGLONG max_duration; /* Maximum duration in CPU clock ticks in an API function */
+ ULONGLONG sum_duration; /* Sum of duration*/
+ double sum_of_square_duration; /* Sum of square of durations */
+ DWORD *histograms; /* An array to store the histogram of an API execution cpu ticks. */
+} pal_perf_api_info;
+
+
+typedef struct _pal_perf_thread_info
+{
+ DWORD threadId;
+ pal_perf_api_info * api_table;
+ char * pal_write_buf;
+ DWORD buf_offset;
+ BOOL profile_enabled;
+ ULONGLONG start_ticks;
+ ULONGLONG total_duration;
+} pal_perf_thread_info;
+
+typedef struct _pal_thread_list_node
+{
+ pal_perf_thread_info * thread_info;
+ struct _pal_thread_list_node * next;
+
+} pal_thread_list_node;
+
+typedef struct _pal_perf_program_info
+{
+ char command_line[PAL_PERF_MAX_LOGLINE];
+ char exe_path[PAL_PERF_MAX_LOGLINE];
+ char hostname[PAL_PERF_MAX_FUNCTION_NAME];
+ double cpu_clock_frequency;
+ ULONGLONG start_ticks;
+ ULONGLONG elapsed_time; /* Duration in CPU clock ticks of the program */
+ ULONGLONG total_duration; /* Total CPU clock ticks of all the threads */
+ ULONGLONG pal_duration; /* Total CPU clock ticks spent inside PAL */
+
+#ifndef PLATFORM_UNIX
+ DWORD process_id;
+#else
+ pid_t process_id;
+#endif
+ char start_time[32]; /* must be at least 26 characters */
+} pal_perf_program_info;
+
+#ifndef PLATFORM_UNIX
+typedef FILE PERF_FILE;
+#define PERF_FILEFN(x) x
+#else
+typedef PAL_FILE PERF_FILE;
+#define PERF_FILEFN(x) PAL_ ## x
+#endif
+
+static ULONGLONG PERFGetTicks();
+static double PERFComputeStandardDeviation(pal_perf_api_info *api);
+static void PERFPrintProgramHeaderInfo(PERF_FILE * hFile, BOOL completedExecution);
+static BOOL PERFInitProgramInfo(LPWSTR command_line, LPWSTR exe_path);
+static BOOL PERFReadSetting( );
+static void PERFLogFileName(PathCharString * destFileString, const char *fileName, const char *suffix, int max_length);
+static void PERFlushAllLogs();
+static int PERFWriteCounters(pal_perf_api_info * table);
+static BOOL PERFFlushLog(pal_perf_thread_info * local_buffer, BOOL output_header);
+static void PERFUpdateApiInfo(pal_perf_api_info *api, ULONGLONG duration);
+static char * PERFIsValidPath( const char * path );
+static char * PERFIsValidFile( const char * path, const char * file);
+
+typedef char PAL_API_NAME[PAL_PERF_MAX_FUNCTION_NAME];
+
+static PAL_API_NAME API_list[PAL_API_NUMBER] ;
+static pal_perf_program_info program_info;
+
+#ifndef PLATFORM_UNIX
+static DWORD PERF_tlsTableKey=0 ;
+#else
+static pthread_key_t PERF_tlsTableKey=0 ;
+#endif
+
+static pal_thread_list_node * process_pal_thread_list=NULL;
+static BOOL pal_profile_on=FALSE;
+static BOOL pal_perf_enabled=FALSE;
+static char * pal_function_map=NULL;
+static char * perf_default_path=NULL;
+static char * traced_apis_file=NULL;
+static char * enabledapis_path=NULL;
+static char * profile_log_path=NULL;
+static char * profile_summary_log_name=NULL;
+static char * profile_time_log_name=NULL;
+static BOOL summary_only=FALSE;
+static BOOL nested_tracing=FALSE;
+static BOOL calibrate=FALSE;
+
+/* If report_only_called_apis is TRUE,
+ those PAL APIs with no function entry or exit
+ will not be shown in the PAL perf summary file. */
+static BOOL report_only_called_apis=FALSE;
+
+/* If the wait_for_startup is TRUE, process profiling
+ will not start until the application
+ has called PAL_EnableProcessProfile(). */
+static BOOL wait_for_startup=FALSE;
+
+/* The size of a PAL API execution CPU ticks histogram, i.e.,
+ Number of categories of frequency distrubution of PAL API
+ execution CPU ticks.*/
+static DWORD pal_perf_histogram_size = 0;
+
+/* The step size in CPU ticks of each category of the
+ PAL API execution CPU ticks histogram.*/
+static DWORD pal_perf_histogram_step = 100;
+
+static const char PAL_PERF_TRACING[]="PAL_PERF_TRACING";
+static const char PAL_DEFAULT_PATH[]="PAL_PERF_DEFAULT_PATH";
+static const char PAL_PERF_TRACEDAPIS_PATH[]="PAL_PERF_TRACEDAPIS_FILE";
+static const char PAL_PERF_LOG_PATH[]="PAL_PERF_LOG_PATH";
+static const char PAL_PERF_SUMMARY_LOG_NAME[]="PAL_PERF_SUMMARY_LOG_NAME";
+static const char PAL_PERF_TIME_LOG_NAME[]="PAL_PERF_TIME_LOG_NAME";
+static const char PAL_PERF_ENABLED_APIS_PATH[]="PAL_PERF_ENABLEDAPIS_FILE";
+static const char PAL_SUMMARY_FLAG[]="PAL_PERF_SUMMARY_ONLY";
+static const char PAL_PERF_NESTED_TRACING[]="PAL_PERF_NESTED_TRACING";
+static const char PAL_PERF_CALIBRATE[]="PAL_PERF_CALIBRATE";
+static const char PAL_PERF_REPORT_ONLY_CALLED_APIS[]="PAL_PERF_REPORT_ONLY_CALLED_APIS";
+static const char PAL_PERF_WAIT_FOR_STARTUP[]="PAL_PERF_WAIT_FOR_STARTUP";
+static const char PAL_PERF_HISTOGRAM_SIZE[]="PAL_PERF_HISTOGRAM_SIZE";
+static const char PAL_PERF_HISTOGRAM_STEP[]="PAL_PERF_HISTOGRAM_STEP";
+static const char traced_apis_filename[]="PerfTracedAPIs.txt";
+static const char perf_enabled_filename[]="AllPerfEnabledAPIs.txt";
+#ifndef PLATFORM_UNIX
+static const char PATH_SEPARATOR[] = "\\";
+#else
+static const char PATH_SEPARATOR[] = "/";
+#endif
+
+
+
+#ifndef PLATFORM_UNIX
+#define LLFORMAT "%I64u"
+#else
+#define LLFORMAT "%llu"
+#endif
+
+static
+ULONGLONG
+PERFGetTicks(){
+#ifdef _X86_ // for BSD and Windows.
+ unsigned long a, d;
+ #ifdef _MSC_VER
+ __asm{
+ rdtsc
+ mov a, eax
+ mov d, edx
+ }
+ #else
+ #undef volatile
+ asm volatile("rdtsc":"=a" (a), "=d" (d));
+ #define volatile DoNotUseVolatileKeyword
+ #endif
+ return ((ULONGLONG)((unsigned int)(d)) << 32) | (unsigned int)(a);
+#else
+#ifdef __sparc__
+ return (ULONGLONG)gethrtime();
+#else
+ return 0; // on non-BSD and non-Windows, we'll return 0 for now.
+#endif // __sparc__
+#endif // _X86_
+}
+
+static
+double
+PERFComputeStandardDeviation(pal_perf_api_info *api)
+{
+ double n;
+ double sum_of_variance;
+ if (api->counter <= 1)
+ return 0.0;
+ n = (double) api->counter;
+ // Calculates standard deviation based on the entire population given as arguments.
+ // Same as stdevp in Excel.
+ sum_of_variance = (n*api->sum_of_square_duration) - (api->sum_duration*api->sum_duration);
+ if (sum_of_variance <= 0.0)
+ return 0.0;
+ return sqrt(sum_of_variance/(n*n));
+}
+
+
+static
+void
+PERFPrintProgramHeaderInfo(PERF_FILE * hFile, BOOL completedExecution)
+{
+ ULONGLONG etime = 0;
+ ULONGLONG ttime = 0;
+ ULONGLONG ptime = 0;
+ if (completedExecution) {
+ etime = program_info.elapsed_time;
+ ttime = program_info.total_duration;
+ ptime = program_info.pal_duration;
+ }
+ PERF_FILEFN(fprintf)(hFile,"#LOG\tversion=1.00\n");
+
+ PERF_FILEFN(fprintf)(hFile, "#MACHINE\thostname=%s\tcpu_clock_frequency=%g\n", program_info.hostname,
+ program_info.cpu_clock_frequency);
+ PERF_FILEFN(fprintf)(hFile, "#PROCESS\tprocess_id=%d\ttotal_latency=" LLFORMAT "\tthread_times=" LLFORMAT "\tpal_time=" LLFORMAT "\texe_path=%s\tcommand_line=%s\tstart_time=%s",
+ program_info.process_id, etime, ttime, ptime,
+ program_info.exe_path,program_info.command_line,program_info.start_time);
+}
+
+static
+BOOL
+PERFInitProgramInfo(LPWSTR command_line, LPWSTR exe_path)
+{
+ ULONGLONG start_tick;
+#ifndef PLATFORM_UNIX
+ time_t tv;
+#else
+ struct timeval tv;
+#endif
+
+ if (WideCharToMultiByte(CP_ACP, 0, command_line, -1,
+ program_info.command_line, PAL_PERF_MAX_LOGLINE-1, NULL, NULL) == 0)
+ return FALSE;
+ if (WideCharToMultiByte(CP_ACP, 0, exe_path, -1,
+ program_info.exe_path, PAL_PERF_MAX_LOGLINE-1, NULL, NULL) == 0)
+ return FALSE;
+
+ gethostname(program_info.hostname, PAL_PERF_MAX_FUNCTION_NAME);
+ program_info.process_id = getpid();
+
+#ifndef PLATFORM_UNIX
+ time( &tv );
+ strcpy(program_info.start_time, ctime( &tv ));
+#else
+ gettimeofday(&tv, NULL);
+ ctime_r(&tv.tv_sec, program_info.start_time);
+#endif
+
+ // estimate the cpu clock cycles
+ start_tick = PERFGetTicks();
+ if (start_tick != 0)
+ {
+#ifndef PLATFORM_UNIX
+ Sleep(1000); //Sleep on Windows takes milliseconds as argument
+#else
+ sleep(1);
+#endif
+ program_info.cpu_clock_frequency = (double) (PERFGetTicks() - start_tick);
+ }
+ else
+ {
+ program_info.cpu_clock_frequency = 0.0;
+ }
+
+ program_info.start_ticks = 0;
+ program_info.elapsed_time = 0;
+ program_info.total_duration = 0;
+ program_info.pal_duration = 0;
+
+ return TRUE;
+}
+
+static
+void
+PERFCalibrationFunction()
+{
+ PERF_ENTRY(CalibrationFunction);
+ PERF_EXIT(CalibrationFunction);
+}
+
+void
+PERFCalibrate(const char* msg)
+{
+ ULONGLONG start_tick, cal_ticks;
+ int i=0;
+ int cal_length=100000;
+
+ if (calibrate) {
+ start_tick = PERFGetTicks();
+ for(i=0; i<cal_length; i++)
+ {
+ PERFCalibrationFunction();
+ }
+ cal_ticks = PERFGetTicks() - start_tick;
+ printf("%s: %g\n", msg, (double)(cal_ticks/cal_length));
+ }
+}
+
+BOOL
+PERFInitialize(LPWSTR command_line, LPWSTR exe_path)
+{
+ BOOL bRead;
+ BOOL ret = TRUE;
+
+ // Check if PAL Perf should be disabled
+ char *pal_perf_tracing_env = MiscGetenv(PAL_PERF_TRACING);
+ if ( pal_perf_tracing_env == NULL || strlen(pal_perf_tracing_env) == 0)
+ {
+ pal_perf_enabled = FALSE;
+ return TRUE;
+ }
+ else
+ {
+ pal_perf_enabled = TRUE;
+ }
+ if (!PERFInitProgramInfo(command_line, exe_path))
+ return FALSE;
+
+ pal_profile_on = FALSE; // turn it off until we setup everything.
+ // allocate the TLS index for structures
+#ifndef PLATFORM_UNIX
+ if( ( PERF_tlsTableKey = TlsAlloc() ) == -1 )
+ ret = FALSE;
+#else
+ if( pthread_key_create(&PERF_tlsTableKey , NULL) != 0 )
+ ret = FALSE;
+#endif
+
+ if( ret == TRUE )
+ {
+ pal_function_map = (char*)PAL_malloc(PAL_API_NUMBER);
+ if(pal_function_map != NULL)
+ {
+ bRead = PERFReadSetting( ); // we don't quit even we failed to read the file.
+ ret = TRUE;
+ }
+ /* free the index in TLS */
+ else
+ {
+
+#ifndef PLATFORM_UNIX
+ TlsFree(PERF_tlsTableKey );
+#else
+ pthread_key_delete(PERF_tlsTableKey );
+#endif
+ ret = FALSE;
+ }
+ }
+
+ PERFCalibrate("Overhead when profiling is disabled process-wide");
+
+ return ret;
+}
+
+
+void PERFTerminate( )
+{
+ static LONG pal_perf_terminated = FALSE;
+
+ if (!pal_perf_enabled || wait_for_startup)
+ return;
+
+ // make sure PERFTerminate is called only once
+ if (InterlockedCompareExchange(&pal_perf_terminated, TRUE, FALSE))
+ return;
+
+ PERFlushAllLogs();
+#ifndef PLATFORM_UNIX
+ TlsFree(PERF_tlsTableKey );
+#else
+ pthread_key_delete(PERF_tlsTableKey );
+#endif
+ PAL_free(pal_function_map);
+}
+
+
+BOOL PERFAllocThreadInfo( )
+{
+ pal_perf_api_info * apiTable = NULL;
+ pal_thread_list_node * node = NULL;
+ pal_perf_thread_info * local_info = NULL;
+ char * log_buf = NULL;
+ int i;
+ BOOL ret = TRUE;
+
+ if (!pal_perf_enabled)
+ return TRUE;
+
+ /* The memory allocated per thread for PAL perf tracing is never freed until PAL_Terminate
+ is called in the current implementation. If the test program keeps creating new threads,
+ memory resources could be exhausted. If this ever becomes a problem, the memory allocated
+ per thread should be freed when a thread exits. */
+
+ node = ( pal_thread_list_node * )PAL_malloc(sizeof(pal_thread_list_node));
+ if(node == NULL)
+ {
+ ret = FALSE;
+ goto PERFAllocThreadInfoExit;
+ }
+
+ local_info = (pal_perf_thread_info *)PAL_malloc(sizeof(pal_perf_thread_info));
+ if (local_info == NULL)
+ {
+ ret = FALSE;
+ goto PERFAllocThreadInfoExit;
+ }
+
+ apiTable = (pal_perf_api_info *)PAL_malloc( PAL_API_NUMBER * sizeof(pal_perf_api_info));
+ if (apiTable == NULL)
+ {
+ ret = FALSE;
+ goto PERFAllocThreadInfoExit;
+ }
+
+ node->thread_info = local_info;
+ local_info->api_table=apiTable;
+ local_info->threadId = THREADSilentGetCurrentThreadId();
+
+ for (i = 0; i < PAL_API_NUMBER; i++)
+ {
+ apiTable[i].entries = 0;
+ apiTable[i].counter = 0;
+ apiTable[i].min_duration = _UI64_MAX;
+ apiTable[i].max_duration = 0;
+ apiTable[i].sum_duration = 0;
+ apiTable[i].sum_of_square_duration = 0.0;
+ if (pal_perf_histogram_size > 0)
+ {
+ apiTable[i].histograms = (DWORD *)PAL_malloc(pal_perf_histogram_size*sizeof(DWORD));
+ if (apiTable[i].histograms == NULL)
+ {
+ ret = FALSE;
+ goto PERFAllocThreadInfoExit;
+ }
+ memset(apiTable[i].histograms, 0, pal_perf_histogram_size*sizeof(DWORD));
+ }
+ else
+ {
+ apiTable[i].histograms = NULL;
+ }
+ }
+
+ log_buf = (char * )PAL_malloc( PAL_PERF_PROFILE_BUFFER_SIZE );
+
+ if(log_buf == NULL)
+ {
+ ret = FALSE;
+ goto PERFAllocThreadInfoExit;
+ }
+
+ local_info->pal_write_buf=log_buf;
+ local_info->buf_offset = 0;
+ local_info->profile_enabled = FALSE;
+ local_info->total_duration = 0;
+ local_info->start_ticks = 0;
+ memset(log_buf, 0, PAL_PERF_PROFILE_BUFFER_SIZE);
+
+#ifndef PLATFORM_UNIX
+ if ( TlsSetValue(PERF_tlsTableKey, local_info) == 0)
+ ret = FALSE;
+#else
+ if (pthread_setspecific(PERF_tlsTableKey, local_info) != 0)
+ ret = FALSE;
+#endif
+
+PERFAllocThreadInfoExit:
+ if (ret == TRUE)
+ {
+ node->next = process_pal_thread_list;
+ process_pal_thread_list = node;
+ PERFFlushLog(local_info, TRUE);
+ }
+ else
+ {
+ if (node != NULL)
+ {
+ PAL_free(node);
+ }
+ if (local_info != NULL)
+ {
+ PAL_free(local_info);
+ }
+ if (apiTable != NULL)
+ {
+ for (i = 0; i < PAL_API_NUMBER; i++)
+ {
+ if (apiTable[i].histograms != NULL)
+ {
+ PAL_free(apiTable[i].histograms);
+ }
+ }
+ PAL_free(apiTable);
+ }
+ if (log_buf != NULL)
+ {
+ PAL_free(log_buf);
+ }
+ }
+ return ret;
+}
+
+static
+void
+PERFUpdateProgramInfo(pal_perf_thread_info* local_info)
+{
+ int i;
+
+ if (!local_info) return;
+
+ // add the elapsed time to the program's total
+ if (local_info->total_duration == 0)
+ {
+ // this thread did not go through PERFDisableThreadProfile code
+ // so compute the total elapsed time for the thread here
+ local_info->total_duration = PERFGetTicks() - local_info->start_ticks;
+ }
+ program_info.total_duration += local_info->total_duration;
+
+ // Add up all the time spent in PAL
+ if (local_info->api_table) {
+ for(i=0; i<PAL_API_NUMBER; i++) {
+ program_info.pal_duration += local_info->api_table[i].sum_duration;
+ }
+ }
+}
+
+
+static
+void
+PERFlushAllLogs( )
+{
+ pal_thread_list_node * current, * node;
+ pal_perf_api_info * table1, *table0;
+ int i;
+ node = process_pal_thread_list;
+ if(node == NULL || node->thread_info == NULL || node->thread_info->api_table == NULL ) // should not come here
+ {
+ return ;
+ }
+ process_pal_thread_list = process_pal_thread_list->next;
+ table0 = node->thread_info->api_table;
+
+ PERFUpdateProgramInfo(node->thread_info);
+
+ while(process_pal_thread_list)
+ {
+ current=process_pal_thread_list;
+ process_pal_thread_list = process_pal_thread_list->next;
+ if (current->thread_info)
+ {
+ if (current->thread_info->api_table)
+ {
+ table1 = current->thread_info->api_table;
+ for(i=0;i<PAL_API_NUMBER;i++)
+ {
+ DWORD j;
+ if (table1[i].counter == 0)
+ {
+ continue;
+ }
+ for (j = 0; j < pal_perf_histogram_size; j++)
+ {
+ table0[i].histograms[j] += table1[i].histograms[j];
+ }
+ table0[i].entries += table1[i].entries;
+ table0[i].counter += table1[i].counter;
+ if (table0[i].min_duration > table1[i].min_duration)
+ table0[i].min_duration = table1[i].min_duration;
+ if (table0[i].max_duration < table1[i].max_duration)
+ table0[i].max_duration = table1[i].max_duration;
+ table0[i].sum_duration += table1[i].sum_duration;
+ table0[i].sum_of_square_duration += table1[i].sum_of_square_duration;
+ }
+ PERFUpdateProgramInfo(current->thread_info);
+ if (table1->histograms != NULL)
+ {
+ PAL_free(table1->histograms);
+ }
+ PAL_free(table1);
+ }
+ PERFFlushLog(current->thread_info, FALSE);
+ PAL_free(current->thread_info->pal_write_buf);
+ PAL_free(current->thread_info);
+ }
+ PAL_free(current);
+ }
+ PERFWriteCounters(table0);
+ if (table0->histograms != NULL)
+ {
+ PAL_free(table0->histograms);
+ }
+ PAL_free(table0);
+ PERFFlushLog(node->thread_info, FALSE);
+ PAL_free(node->thread_info->pal_write_buf);
+ PAL_free(node->thread_info);
+ PAL_free(node);
+}
+
+static
+void
+PERFLogFileName(PathCharString& destFileString, const char *fileName, const char *suffix)
+{
+ const char *dir_path;
+ CPalThread* pThread = InternalGetCurrentThread();
+ dir_path = (profile_log_path == NULL) ? "." : profile_log_path;
+
+ destFileString.Append(dir_path, strlen(dir_path));
+ destFileString.Append(PATH_SEPARATOR, strlen(PATH_SEPARATOR));
+ if (fileName != NULL)
+ {
+ destFileString.Append(fileName, strlen(fileName));
+ }
+ else
+ {
+ char buffer[33];
+ char* process_id = itoa(program_info.process_id, buffer, 10);
+ destFileString.Append(process_id, strlen(process_id));
+ destFileString.Append("_", 1);
+
+ char* current_thread = itoa(THREADSilentGetCurrentThreadId(),buffer, 10);
+ destFileString.Append(current_thread, strlen( current_thread));
+ destFileString.Append(suffix, strlen(suffix));
+ }
+
+}
+
+static
+int
+PERFWriteCounters( pal_perf_api_info * table )
+{
+ PathCharString fileName;
+ pal_perf_api_info * off;
+ PERF_FILE * hFile;
+ int i;
+
+ off = table;
+
+ PERFLogFileName(fileName, profile_summary_log_name, "_perf_summary.log");
+ hFile = PERF_FILEFN(fopen)(fileName, "a+");
+ if(hFile != NULL)
+ {
+ PERFPrintProgramHeaderInfo(hFile, TRUE);
+ PERF_FILEFN(fprintf)(hFile,"#api_name\tapi_id\tperf_entries\tperf_exits\tsum_of_latency\tmin_latency\tmax_latency\tstd_dev_latency\tsum_of_square_latency\n");
+ for(i=0;i<PAL_API_NUMBER;i++)
+ {
+ double dev;
+ ULONGLONG min_duration;
+
+ min_duration = (off->min_duration == _UI64_MAX) ? 0 : off->min_duration;
+ if (off->counter >= 1)
+ {
+ dev = PERFComputeStandardDeviation(off);
+ }
+ else
+ {
+ dev = 0.0;
+ }
+
+ if (off->counter > 0 || !report_only_called_apis)
+ {
+ PERF_FILEFN(fprintf)(hFile,"%s\t%d\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t%g\t%g\n",
+ API_list[i], i, off->entries, off->counter,off->sum_duration,
+ min_duration, off->max_duration, dev, off->sum_of_square_duration);
+ }
+
+ off++;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ PERF_FILEFN(fclose)(hFile);
+
+ if (pal_perf_histogram_size > 0)
+ {
+ off = table;
+ PERFLogFileName(fileName, profile_summary_log_name, "_perf_summary.hist");
+ hFile = PERF_FILEFN(fopen)(fileName, "a+");
+
+ if (hFile != NULL)
+ {
+ DWORD j;
+ PERF_FILEFN(fprintf)(hFile,"#api_name\tapi_id");
+ for (j = 0; j < pal_perf_histogram_size; j++)
+ {
+ PERF_FILEFN(fprintf)(hFile, "\t%d", j*pal_perf_histogram_step);
+ }
+ PERF_FILEFN(fprintf)(hFile, "\n");
+
+ for(i = 0; i < PAL_API_NUMBER; i++)
+ {
+ if (off->counter > 0)
+ {
+ PERF_FILEFN(fprintf)(hFile,"%s\t%d", API_list[i], i);
+
+ for (j = 0; j < pal_perf_histogram_size; j++)
+ {
+ PERF_FILEFN(fprintf)(hFile, "\t%d", off->histograms[j]);
+ }
+
+ PERF_FILEFN(fprintf)(hFile, "\n");
+ }
+
+ off++;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ PERF_FILEFN(fclose)(hFile);
+ }
+
+ return 0;
+}
+
+static
+BOOL
+PERFReadSetting( )
+{
+ // this function is not safe right now.
+ //more code is required to deal with corrupted input file.
+ BOOL ret;
+ unsigned int index;
+ char line[PAL_PERF_MAX_INPUT];
+ char * ptr;
+ char function_name[PAL_PERF_MAX_FUNCTION_NAME]; //no function can be longer than 127 bytes.
+
+ char * file_name_buf;
+ PathCharString file_name_bufPS;
+ char * input_file_name;
+ char * summary_flag_env;
+ char * nested_tracing_env;
+ char * calibrate_env;
+ char * report_only_called_apis_env;
+ char * wait_for_startup_env;
+ char * pal_perf_histogram_size_env;
+ char * pal_perf_histogram_step_env;
+
+#ifdef PLATFORM_UNIX
+ PAL_FILE * hFile;
+#else
+ FILE * hFile;
+#endif
+
+ if((pal_function_map == NULL) || (PAL_API_NUMBER < 0) )
+ {
+ // should not be here.
+ }
+
+ /* do some env setting here */
+ summary_flag_env = MiscGetenv(PAL_SUMMARY_FLAG);
+ if (summary_flag_env == NULL || strlen(summary_flag_env) == 0)
+ {
+ summary_only = FALSE;
+ }
+ else
+ {
+ summary_only = TRUE;
+ }
+ nested_tracing_env = MiscGetenv(PAL_PERF_NESTED_TRACING);
+ if (nested_tracing_env == NULL || strlen(nested_tracing_env) == 0)
+ {
+ nested_tracing = FALSE;
+ }
+ else
+ {
+ nested_tracing = TRUE;
+ }
+
+ calibrate_env = MiscGetenv(PAL_PERF_CALIBRATE);
+ if (calibrate_env == NULL || strlen(calibrate_env) == 0)
+ {
+ calibrate = FALSE;
+ }
+ else
+ {
+ calibrate = TRUE;
+ }
+
+ report_only_called_apis_env = MiscGetenv(PAL_PERF_REPORT_ONLY_CALLED_APIS);
+ if (report_only_called_apis_env == NULL || strlen(report_only_called_apis_env) == 0)
+ {
+ report_only_called_apis = FALSE;
+ }
+ else
+ {
+ report_only_called_apis = TRUE;
+ }
+
+ wait_for_startup_env = MiscGetenv(PAL_PERF_WAIT_FOR_STARTUP);
+ if (wait_for_startup_env == NULL || strlen(wait_for_startup_env) == 0)
+ {
+ wait_for_startup = FALSE;
+ }
+ else
+ {
+ wait_for_startup = TRUE;
+ }
+
+ pal_perf_histogram_size_env = MiscGetenv(PAL_PERF_HISTOGRAM_SIZE);
+ if (pal_perf_histogram_size_env != NULL && strlen(pal_perf_histogram_size_env) > 0)
+ {
+ long value;
+ char *endptr;
+ value = strtol(pal_perf_histogram_size_env, &endptr, 10);
+ if (value > 0)
+ {
+ pal_perf_histogram_size = (DWORD) value;
+ }
+ }
+
+ pal_perf_histogram_step_env = MiscGetenv(PAL_PERF_HISTOGRAM_STEP);
+ if (pal_perf_histogram_step_env != NULL && strlen(pal_perf_histogram_step_env) > 0)
+ {
+ long value;
+ char *endptr;
+ value = strtol(pal_perf_histogram_step_env, &endptr, 10);
+ if (value > 0)
+ {
+ pal_perf_histogram_step = (DWORD) value;
+ }
+ }
+
+ traced_apis_file = PERFIsValidFile("", MiscGetenv(PAL_PERF_TRACEDAPIS_PATH));
+ enabledapis_path = PERFIsValidFile("", MiscGetenv(PAL_PERF_ENABLED_APIS_PATH));
+ profile_log_path = PERFIsValidPath(MiscGetenv(PAL_PERF_LOG_PATH));
+ perf_default_path = PERFIsValidPath( MiscGetenv(PAL_DEFAULT_PATH));
+ profile_summary_log_name = MiscGetenv(PAL_PERF_SUMMARY_LOG_NAME);
+ if (profile_summary_log_name != NULL && strlen(profile_summary_log_name) == 0)
+ profile_summary_log_name = NULL;
+ profile_time_log_name = MiscGetenv(PAL_PERF_TIME_LOG_NAME);
+ if (profile_time_log_name != NULL && strlen(profile_time_log_name) == 0)
+ profile_time_log_name = NULL;
+
+ if( traced_apis_file == NULL)
+ {
+ if(perf_default_path==NULL)
+ {
+ ret=FALSE;
+ input_file_name = NULL;
+ }
+ else
+ {
+ if( PERFIsValidFile(perf_default_path,traced_apis_filename))
+ {
+ int length = strlen(perf_default_path) + strlen(PATH_SEPARATOR) + strlen(traced_apis_filename);
+ file_name_buf = file_name_bufPS.OpenStringBuffer(length);
+ if ((strcpy_s(file_name_buf, file_name_bufPS.GetSizeOf(), perf_default_path) != SAFECRT_SUCCESS) ||
+ (strcat_s(file_name_buf, file_name_bufPS.GetSizeOf(), PATH_SEPARATOR) != SAFECRT_SUCCESS) ||
+ (strcat_s(file_name_buf, file_name_bufPS.GetSizeOf(), traced_apis_filename) != SAFECRT_SUCCESS))
+ {
+ file_name_bufPS.CloseBuffer(0);
+ ret = FALSE;
+ input_file_name = NULL;
+ }
+ else
+ {
+ file_name_bufPS.CloseBuffer(length);
+ input_file_name = file_name_buf;
+ }
+ }
+ else
+ {
+ ret = FALSE;
+ input_file_name=NULL;
+ }
+ }
+ }
+ else
+ {
+ input_file_name=traced_apis_file;
+ }
+
+ if(input_file_name)
+ {
+#ifdef PLATFORM_UNIX
+ hFile = PAL_fopen(input_file_name, "r+");
+#else
+ hFile = fopen(input_file_name, "r+");
+#endif
+ if ( hFile == NULL )
+ {
+ memset(pal_function_map, 1, PAL_API_NUMBER);
+ ret = FALSE;
+ }
+ else
+ {
+ memset(pal_function_map, 0, PAL_API_NUMBER);
+
+ PAL_fseek(hFile, 0L, SEEK_SET);
+
+ /* Read a line of data from file: */
+ while ( PAL_fgets(line, PAL_PERF_MAX_INPUT, hFile) != NULL )
+ {
+ if(strlen(line)==0)
+ continue;
+ ptr = strchr( line, '#');
+ if( ptr )
+ continue;
+ sscanf_s(line, "%s %u", function_name,&index);
+
+ if( index >= PAL_API_NUMBER)
+ {
+ // some code here to deal with incorrect index.
+ // use function name to cover it.
+ }
+ else if(pal_function_map[index]==1)
+ {
+ // some code here to deal with conflict index.
+ // use function name to cover it.
+ }
+ else
+ {
+ pal_function_map[index]=1;
+ }
+
+ }
+
+#ifdef PLATFORM_UNIX
+ PAL_fclose(hFile);
+#else
+ fclose(hFile);
+#endif
+ ret = TRUE;
+ }
+ }
+ else
+ {
+ memset(pal_function_map, 1, PAL_API_NUMBER);
+ ret = FALSE;
+ }
+
+ if( enabledapis_path == NULL)
+ {
+ if(perf_default_path==NULL)
+ {
+ input_file_name = NULL;
+ }
+ else
+ {
+ if( PERFIsValidFile(perf_default_path,perf_enabled_filename))
+ {
+ if ((strcpy_s(file_name_buf, sizeof(file_name_buf), perf_default_path) != SAFECRT_SUCCESS) ||
+ (strcat_s(file_name_buf, sizeof(file_name_buf), PATH_SEPARATOR) != SAFECRT_SUCCESS) ||
+ (strcat_s(file_name_buf, sizeof(file_name_buf), perf_enabled_filename) != SAFECRT_SUCCESS))
+ {
+ ret = FALSE;
+ input_file_name = NULL;
+ }
+ else
+ {
+ input_file_name = file_name_buf;
+ }
+ }
+ else
+ {
+ input_file_name=NULL;
+ }
+ }
+ }
+ else
+ {
+ input_file_name=enabledapis_path;
+ }
+
+ if(input_file_name == NULL)
+ {
+ return ret;
+ }
+
+#ifdef PLATFORM_UNIX
+ hFile = PAL_fopen(input_file_name, "r+");
+#else
+ hFile = fopen(input_file_name, "r+");
+#endif
+
+ if ( hFile != NULL )
+ {
+ PAL_fseek(hFile, 0L, SEEK_SET);
+
+ /* Read a line of data from file: */
+ while (PAL_fgets(line, PAL_PERF_MAX_INPUT, hFile) != NULL)
+ {
+ if(strlen(line)==0)
+ continue;
+ ptr = strchr( line, '#');
+ if( ptr )
+ continue;
+ sscanf_s(line, "%s %u", function_name,&index);
+
+ if( index >= PAL_API_NUMBER)
+ {
+ // some code here to deal with incorrect index.
+ // use function name to cover it.
+ continue;
+ }
+
+ if (strcpy_s(API_list[index], sizeof(API_list[index]), function_name) != SAFECRT_SUCCESS)
+ {
+ ret = FALSE;
+ break;
+ }
+ }
+
+#ifdef PLATFORM_UNIX
+ PAL_fclose(hFile);
+#else
+ fclose(hFile);
+#endif
+ }
+
+ return ret;
+
+}
+
+
+static
+BOOL
+PERFFlushLog(pal_perf_thread_info * local_info, BOOL output_header)
+{
+ BOOL ret = FALSE;
+ PathCharString fileName;
+ int nWrittenBytes = 0;
+ PERF_FILE * hFile;
+
+ if (summary_only)
+ return TRUE;
+
+ PERFLogFileName(fileName, profile_time_log_name, "_perf_time.log");
+
+ hFile = PERF_FILEFN(fopen)(fileName, "a+");
+
+ if(hFile)
+ {
+ if (output_header)
+ {
+ PERFPrintProgramHeaderInfo(hFile, FALSE);
+ }
+ if (local_info->buf_offset > 0)
+ {
+ nWrittenBytes = PERF_FILEFN(fwrite)(local_info->pal_write_buf, local_info->buf_offset, 1, hFile);
+ if (nWrittenBytes < 1)
+ {
+ ERROR("fwrite() failed with errno == %d\n", errno);
+ return ret;
+ }
+ local_info->buf_offset = 0;
+ }
+ PERF_FILEFN(fclose)(hFile);
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+void
+PERFLogFunctionEntry(unsigned int pal_api_id, ULONGLONG *pal_perf_start_tick )
+{
+ pal_perf_thread_info * local_info=NULL;
+ pal_perf_api_info * table;
+ char * write_buf;
+ __int32 buf_off;
+ short bufused = 0;
+
+
+#ifndef PLATFORM_UNIX
+ DWORD tv;
+ DWORD last_error;
+ last_error = GetLastError();
+#else
+ struct timeval tv;
+#endif
+
+
+ if(!pal_perf_enabled || pal_function_map==NULL || !pal_profile_on ) // haven't initialize, just quit.
+ return;
+
+ if( pal_function_map[pal_api_id] )
+ {
+ local_info= (pal_perf_thread_info * )pthread_getspecific(PERF_tlsTableKey);
+
+ if (local_info==NULL )
+ {
+ return;
+ }
+ if ( !local_info->profile_enabled ) /* prevent recursion. */
+ {
+ return;
+ }
+ // turn on this flag before call any other functions
+ local_info->profile_enabled = FALSE;
+ table = local_info->api_table;
+ table[pal_api_id].entries++;
+
+ if(!summary_only)
+ {
+ write_buf = (local_info->pal_write_buf);
+ if(local_info->buf_offset >= PAL_PERF_BUFFER_FULL)
+ {
+ PERFFlushLog(local_info, FALSE);
+ }
+
+#ifndef PLATFORM_UNIX
+ tv = GetTickCount();
+#else
+ gettimeofday(&tv, NULL);
+#endif
+
+ buf_off = local_info->buf_offset;
+
+#ifndef PLATFORM_UNIX
+ bufused = snprintf(&write_buf[buf_off], PAL_PERF_MAX_LOGLINE, "----> %d %lu entry.\n", pal_api_id, tv );
+#else
+ bufused = snprintf(&write_buf[buf_off], PAL_PERF_MAX_LOGLINE, "----> %d %lu %06u entry.\n", pal_api_id, tv.tv_sec, tv.tv_usec );
+#endif
+ local_info->buf_offset += bufused;
+ }
+ if(nested_tracing)
+ local_info->profile_enabled = TRUE;
+ *pal_perf_start_tick = PERFGetTicks();
+ }
+#ifndef PLATFORM_UNIX
+ SetLastError( last_error );
+#endif
+ return;
+}
+
+static
+void
+PERFUpdateApiInfo(pal_perf_api_info *api, ULONGLONG duration)
+{
+ DWORD iBucket;
+
+ api->counter++;
+ if (api->min_duration > duration)
+ api->min_duration = duration;
+ if (api->max_duration < duration)
+ api->max_duration = duration;
+ api->sum_duration += duration;
+ api->sum_of_square_duration += (double) duration * (double)duration;
+
+ if (pal_perf_histogram_size > 0)
+ {
+ iBucket = (DWORD)(duration / pal_perf_histogram_step);
+ if (iBucket >= pal_perf_histogram_size)
+ {
+ iBucket = pal_perf_histogram_size - 1;
+ }
+ api->histograms[iBucket]++;
+ }
+
+}
+
+void
+PERFLogFunctionExit(unsigned int pal_api_id, ULONGLONG *pal_perf_start_tick )
+{
+
+ pal_perf_thread_info * local_info;
+ char * buf;
+ short bufused = 0;
+ DWORD off;
+ ULONGLONG duration = 0;
+#ifndef PLATFORM_UNIX
+ DWORD timev;
+ DWORD last_error;
+ last_error = GetLastError();
+#else
+ struct timeval timev;
+
+#endif
+
+ if(!pal_perf_enabled || (pal_function_map == NULL) || !pal_profile_on ) // haven't initiallize yet, just quit.
+ return;
+
+ if (*pal_perf_start_tick != 0)
+ {
+ duration = PERFGetTicks() - *pal_perf_start_tick;
+ }
+ else
+ {
+ return; // pal_perf_start_tick == 0 indicates that we exited PERFLogFunctionEntry before getting the ticks.
+ }
+
+ if( pal_function_map[pal_api_id] )
+ {
+ local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey);
+
+ if (NULL == local_info ){
+ return;
+ }
+ PERFUpdateApiInfo(&local_info->api_table[pal_api_id], duration);
+ *pal_perf_start_tick = 0;
+
+ if(summary_only)
+ {
+ local_info->profile_enabled = TRUE;
+#ifndef PLATFORM_UNIX
+ SetLastError( last_error );
+#endif
+ return;
+ }
+
+#ifndef PLATFORM_UNIX
+ timev = GetTickCount();
+#else
+ gettimeofday(&timev, NULL);
+#endif
+
+ buf = local_info->pal_write_buf;
+ if(local_info->buf_offset >= PAL_PERF_BUFFER_FULL)
+ {
+ PERFFlushLog(local_info, FALSE);
+ }
+ off = local_info->buf_offset;
+
+#ifndef PLATFORM_UNIX
+ bufused = snprintf(&buf[off], PAL_PERF_MAX_LOGLINE, "<---- %d %lu exit. \n", pal_api_id, timev);
+#else
+ bufused = snprintf(&buf[off], PAL_PERF_MAX_LOGLINE, "<---- %d %lu %06u exit. \n", pal_api_id, timev.tv_sec, timev.tv_usec );
+#endif
+ local_info->buf_offset += bufused;
+ local_info->profile_enabled = TRUE;
+ }
+#ifndef PLATFORM_UNIX
+ SetLastError( last_error );
+#endif
+ return;
+}
+
+void
+PERFNoLatencyProfileEntry(unsigned int pal_api_id )
+{
+ pal_perf_thread_info * local_info=NULL;
+ pal_perf_api_info * table;
+#ifndef PLATFORM_UNIX
+ DWORD last_error;
+ last_error = GetLastError();
+#endif
+
+ if(!pal_perf_enabled || pal_function_map==NULL || !pal_profile_on ) // haven't initialize, just quit.
+ return;
+ if( pal_function_map[pal_api_id] )
+ {
+ local_info= (pal_perf_thread_info * )pthread_getspecific(PERF_tlsTableKey);
+ if (local_info==NULL )
+ {
+#ifndef PLATFORM_UNIX
+ SetLastError( last_error );
+#endif
+ return;
+ }
+ else{
+ table = local_info->api_table;
+ table[pal_api_id].entries++;
+ }
+ }
+#ifndef PLATFORM_UNIX
+ SetLastError( last_error );
+#endif
+ return;
+}
+
+
+void
+PERFEnableThreadProfile(BOOL isInternal)
+{
+ pal_perf_thread_info * local_info;
+#ifndef PLATFORM_UNIX
+ DWORD last_error;
+ last_error = GetLastError();
+#endif
+ if (!pal_perf_enabled)
+ return;
+ if (NULL != (local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey)))
+ {
+ if (!isInternal || nested_tracing) {
+ local_info->profile_enabled = TRUE;
+ local_info->start_ticks = PERFGetTicks();
+ }
+ }
+#ifndef PLATFORM_UNIX
+ SetLastError( last_error );
+#endif
+}
+
+
+void
+PERFDisableThreadProfile(BOOL isInternal)
+{
+ pal_perf_thread_info * local_info;
+#ifndef PLATFORM_UNIX
+ DWORD last_error;
+ last_error = GetLastError();
+#endif
+ if (!pal_perf_enabled)
+ return;
+ if (NULL != (local_info = (pal_perf_thread_info*)pthread_getspecific(PERF_tlsTableKey)))
+ {
+ if (!isInternal || nested_tracing) {
+ local_info->profile_enabled = FALSE;
+ local_info->total_duration = PERFGetTicks() - local_info->start_ticks;
+ }
+ }
+#ifndef PLATFORM_UNIX
+ SetLastError( last_error );
+#endif
+}
+
+
+void
+PERFEnableProcessProfile( )
+{
+ if (!pal_perf_enabled || wait_for_startup)
+ return;
+ pal_profile_on = TRUE;
+ PERFCalibrate("Overhead when profiling is disabled temporarily for a thread");
+ // record the cpu clock ticks at the beginning of the profiling.
+ program_info.start_ticks = PERFGetTicks();
+}
+
+
+void
+PERFDisableProcessProfile( )
+{
+ if (!pal_perf_enabled)
+ return;
+ pal_profile_on = FALSE;
+ // compute the total program duration in cpu clock ticks.
+ if (program_info.start_ticks != 0)
+ {
+ program_info.elapsed_time += (PERFGetTicks() - program_info.start_ticks);
+ program_info.start_ticks = 0;
+ }
+}
+
+BOOL
+PERFIsProcessProfileEnabled( )
+{
+ return pal_profile_on;
+}
+
+static
+char *
+PERFIsValidPath( const char * path )
+{
+#ifndef PLATFORM_UNIX
+ DWORD result;
+#else
+ DIR * dir;
+#endif
+
+ if(( path==NULL) || (strlen(path)==0))
+ return NULL;
+
+#ifndef PLATFORM_UNIX
+ result = GetFileAttributesA( path );
+ if ((result != INVALID_FILE_ATTRIBUTES) && (result & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ return ((char *) path );
+ }
+#else
+ dir = opendir(path);
+ if( dir!=NULL)
+ {
+ closedir(dir);
+ return ((char *)path);
+ }
+#endif
+ return NULL;
+}
+
+static
+char *
+PERFIsValidFile( const char * path, const char * file)
+{
+ FILE * hFile;
+ char * temp;
+ PathCharString tempPS;
+
+ if(file==NULL || strlen(file)==0)
+ return NULL;
+
+ if ( strcmp(path, "") )
+ {
+ int length = strlen(path) + strlen(PATH_SEPARATOR) + strlen(file);
+ temp = tempPS.OpenStringBuffer(length);
+ if ((strcpy_s(temp, sizeof(temp), path) != SAFECRT_SUCCESS) ||
+ (strcat_s(temp, sizeof(temp), PATH_SEPARATOR) != SAFECRT_SUCCESS) ||
+ (strcat_s(temp, sizeof(temp), file) != SAFECRT_SUCCESS))
+ {
+ tempPS.CloseBuffer(0);
+ return NULL;
+ }
+
+ tempPS.CloseBuffer(length);
+ hFile = fopen(temp, "r");
+ }
+ else
+ {
+ hFile = fopen(file, "r");
+ }
+
+ if(hFile)
+ {
+ fclose(hFile);
+ return ((char *) file);
+ }
+ else
+ return NULL;
+
+}
+
+PALIMPORT
+VOID
+PALAPI
+PAL_EnableProcessProfile(VOID)
+{
+ wait_for_startup = FALSE;
+ pal_profile_on = TRUE;
+ PERFEnableProcessProfile();
+}
+
+PALIMPORT
+VOID
+PALAPI
+PAL_DisableProcessProfile(VOID)
+{
+ pal_profile_on = FALSE;
+ PERFDisableProcessProfile();
+}
+
+PALIMPORT
+BOOL
+PALAPI
+PAL_IsProcessProfileEnabled(VOID)
+{
+ return PERFIsProcessProfileEnabled();
+}
+
+PALIMPORT
+INT64
+PALAPI
+PAL_GetCpuTickCount(VOID)
+{
+ return PERFGetTicks();
+}
+
+#ifndef PLATFORM_UNIX
+#undef snprintf
+#undef MiscGetenv
+#undef pthread_key_t
+#undef pthread_getspecific
+#endif /* ifndef PLATFORM_UNIX definitions */
+
+#endif /* PAL_PERF */
+
+
+
+
diff --git a/src/pal/src/misc/strutil.cpp b/src/pal/src/misc/strutil.cpp
new file mode 100644
index 0000000000..f00b4fde8e
--- /dev/null
+++ b/src/pal/src/misc/strutil.cpp
@@ -0,0 +1,96 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ strutil.cpp
+
+Abstract:
+ Various string-related utility functions
+
+
+
+--*/
+
+#include "pal/corunix.hpp"
+#include "pal/thread.hpp"
+#include "pal/malloc.hpp"
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(PAL);
+
+using namespace CorUnix;
+
+/*++
+Function:
+ CPalString::CopyString
+
+ Copies a CPalString into a new (empty) instance, allocating buffer space
+ as necessary
+
+Parameters:
+ psSource -- the string to copy from
+--*/
+
+PAL_ERROR
+CPalString::CopyString(
+ CPalString *psSource
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ _ASSERTE(NULL != psSource);
+ _ASSERTE(NULL == m_pwsz);
+ _ASSERTE(0 == m_dwStringLength);
+ _ASSERTE(0 == m_dwMaxLength);
+
+ if (0 != psSource->GetStringLength())
+ {
+ _ASSERTE(psSource->GetMaxLength() > psSource->GetStringLength());
+
+ WCHAR *pwsz = reinterpret_cast<WCHAR*>(
+ InternalMalloc(psSource->GetMaxLength() * sizeof(WCHAR))
+ );
+
+ if (NULL != pwsz)
+ {
+ _ASSERTE(NULL != psSource->GetString());
+
+ CopyMemory(
+ pwsz,
+ psSource->GetString(),
+ psSource->GetMaxLength() * sizeof(WCHAR)
+ );
+
+ m_pwsz = pwsz;
+ m_dwStringLength = psSource->GetStringLength();
+ m_dwMaxLength = psSource->GetMaxLength();
+ }
+ else
+ {
+ palError = ERROR_OUTOFMEMORY;
+ }
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ CPalString::FreeBuffer
+
+ Frees the contained string buffer
+
+--*/
+
+void
+CPalString::FreeBuffer()
+{
+ _ASSERTE(NULL != m_pwsz);
+ free(const_cast<WCHAR*>(m_pwsz));
+}
diff --git a/src/pal/src/misc/sysinfo.cpp b/src/pal/src/misc/sysinfo.cpp
new file mode 100644
index 0000000000..515ccf1cdb
--- /dev/null
+++ b/src/pal/src/misc/sysinfo.cpp
@@ -0,0 +1,363 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ sysinfo.c
+
+Abstract:
+
+ Implements GetSystemInfo.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+
+#include <sched.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#if HAVE_SYSCTL
+#include <sys/sysctl.h>
+#elif !HAVE_SYSCONF
+#error Either sysctl or sysconf is required for GetSystemInfo.
+#endif
+
+#include <sys/param.h>
+
+#if HAVE_SYS_VMPARAM_H
+#include <sys/vmparam.h>
+#endif // HAVE_SYS_VMPARAM_H
+
+#if HAVE_MACH_VM_TYPES_H
+#include <mach/vm_types.h>
+#endif // HAVE_MACH_VM_TYPES_H
+
+#if HAVE_MACH_VM_PARAM_H
+#include <mach/vm_param.h>
+#endif // HAVE_MACH_VM_PARAM_H
+
+#if HAVE_MACHINE_VMPARAM_H
+#include <machine/vmparam.h>
+#endif // HAVE_MACHINE_VMPARAM_H
+
+#if defined(_TARGET_MAC64)
+#include <mach/vm_statistics.h>
+#include <mach/mach_types.h>
+#include <mach/mach_init.h>
+#include <mach/mach_host.h>
+#endif // defined(_TARGET_MAC64)
+
+// On some platforms sys/user.h ends up defining _DEBUG; if so
+// remove the definition before including the header and put
+// back our definition afterwards
+#if USER_H_DEFINES_DEBUG
+#define OLD_DEBUG _DEBUG
+#undef _DEBUG
+#endif
+#include <sys/user.h>
+#if USER_H_DEFINES_DEBUG
+#undef _DEBUG
+#define _DEBUG OLD_DEBUG
+#undef OLD_DEBUG
+#endif
+
+#include "pal/dbgmsg.h"
+
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+#if defined(_HPUX_) && ( defined (_IA64_) || defined (__hppa__) )
+#include <sys/pstat.h>
+#include <sys/vmparam.h>
+#endif
+
+#ifndef __APPLE__
+#if HAVE_SYSCONF && HAVE__SC_AVPHYS_PAGES
+#define SYSCONF_PAGES _SC_AVPHYS_PAGES
+#elif HAVE_SYSCONF && HAVE__SC_PHYS_PAGES
+#define SYSCONF_PAGES _SC_PHYS_PAGES
+#else
+#error Dont know how to get page-size on this architecture!
+#endif
+#endif // __APPLE__
+
+
+/*++
+Function:
+ GetSystemInfo
+
+GetSystemInfo
+
+The GetSystemInfo function returns information about the current system.
+
+Parameters
+
+lpSystemInfo
+ [out] Pointer to a SYSTEM_INFO structure that receives the information.
+
+Return Values
+
+This function does not return a value.
+
+Note:
+ fields returned by this function are:
+ dwNumberOfProcessors
+ dwPageSize
+Others are set to zero.
+
+--*/
+VOID
+PALAPI
+GetSystemInfo(
+ OUT LPSYSTEM_INFO lpSystemInfo)
+{
+ int nrcpus = 0;
+ long pagesize;
+
+ PERF_ENTRY(GetSystemInfo);
+ ENTRY("GetSystemInfo (lpSystemInfo=%p)\n", lpSystemInfo);
+
+ pagesize = getpagesize();
+
+ lpSystemInfo->wProcessorArchitecture_PAL_Undefined = 0;
+ lpSystemInfo->wReserved_PAL_Undefined = 0;
+ lpSystemInfo->dwPageSize = pagesize;
+ lpSystemInfo->dwActiveProcessorMask_PAL_Undefined = 0;
+
+#if HAVE_SYSCONF
+#if defined(_HPUX_) && ( defined (_IA64_) || defined (__hppa__) )
+ struct pst_dynamic psd;
+ if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) != -1) {
+ nrcpus = psd.psd_proc_cnt;
+ }
+ else {
+ ASSERT("pstat_getdynamic failed (%d)\n", errno);
+ }
+
+#else // !__hppa__
+ nrcpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if (nrcpus < 1)
+ {
+ ASSERT("sysconf failed for _SC_NPROCESSORS_ONLN (%d)\n", errno);
+ }
+#endif // __hppa__
+#elif HAVE_SYSCTL
+ int rc;
+ size_t sz;
+ int mib[2];
+
+ sz = sizeof(nrcpus);
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ rc = sysctl(mib, 2, &nrcpus, &sz, NULL, 0);
+ if (rc != 0)
+ {
+ ASSERT("sysctl failed for HW_NCPU (%d)\n", errno);
+ }
+#endif // HAVE_SYSCONF
+
+ TRACE("dwNumberOfProcessors=%d\n", nrcpus);
+ lpSystemInfo->dwNumberOfProcessors = nrcpus;
+
+#ifdef VM_MAXUSER_ADDRESS
+ lpSystemInfo->lpMaximumApplicationAddress = (PVOID) VM_MAXUSER_ADDRESS;
+#elif defined(__sun__) || defined(_AIX) || defined(__hppa__) || ( defined (_IA64_) && defined (_HPUX_) ) || defined(__linux__)
+ lpSystemInfo->lpMaximumApplicationAddress = (PVOID) -1;
+#elif defined(USERLIMIT)
+ lpSystemInfo->lpMaximumApplicationAddress = (PVOID) USERLIMIT;
+#elif defined(_WIN64)
+#if defined(USRSTACK64)
+ lpSystemInfo->lpMaximumApplicationAddress = (PVOID) USRSTACK64;
+#else // !USRSTACK64
+#error How come USRSTACK64 is not defined for 64bit?
+#endif // USRSTACK64
+#elif defined(USRSTACK)
+ lpSystemInfo->lpMaximumApplicationAddress = (PVOID) USRSTACK;
+#else
+#error The maximum application address is not known on this platform.
+#endif
+
+ lpSystemInfo->lpMinimumApplicationAddress = (PVOID) pagesize;
+
+ lpSystemInfo->dwProcessorType_PAL_Undefined = 0;
+
+ lpSystemInfo->dwAllocationGranularity = pagesize;
+
+ lpSystemInfo->wProcessorLevel_PAL_Undefined = 0;
+ lpSystemInfo->wProcessorRevision_PAL_Undefined = 0;
+
+ LOGEXIT("GetSystemInfo returns VOID\n");
+ PERF_EXIT(GetSystemInfo);
+}
+
+/*++
+Function:
+ GlobalMemoryStatusEx
+
+GlobalMemoryStatusEx
+
+Retrieves information about the system's current usage of both physical and virtual memory.
+
+Return Values
+
+This function returns a BOOL to indicate its success status.
+
+--*/
+BOOL
+PALAPI
+GlobalMemoryStatusEx(
+ IN OUT LPMEMORYSTATUSEX lpBuffer)
+{
+
+ PERF_ENTRY(GlobalMemoryStatusEx);
+ ENTRY("GlobalMemoryStatusEx (lpBuffer=%p)\n", lpBuffer);
+
+ lpBuffer->dwMemoryLoad = 0;
+ lpBuffer->ullTotalPhys = 0;
+ lpBuffer->ullAvailPhys = 0;
+ lpBuffer->ullTotalPageFile = 0;
+ lpBuffer->ullAvailPageFile = 0;
+ lpBuffer->ullTotalVirtual = 0;
+ lpBuffer->ullAvailVirtual = 0;
+ lpBuffer->ullAvailExtendedVirtual = 0;
+
+ BOOL fRetVal = FALSE;
+
+ // Get the physical memory size
+#if HAVE_SYSCONF && HAVE__SC_PHYS_PAGES
+ int64_t physical_memory;
+
+ // Get the Physical memory size
+ physical_memory = sysconf( _SC_PHYS_PAGES ) * sysconf( _SC_PAGE_SIZE );
+ lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory;
+ fRetVal = TRUE;
+#elif HAVE_SYSCTL
+ int mib[2];
+ int64_t physical_memory;
+ size_t length;
+
+ // Get the Physical memory size
+ mib[0] = CTL_HW;
+ mib[1] = HW_MEMSIZE;
+ length = sizeof(INT64);
+ int rc = sysctl(mib, 2, &physical_memory, &length, NULL, 0);
+ if (rc != 0)
+ {
+ ASSERT("sysctl failed for HW_MEMSIZE (%d)\n", errno);
+ }
+ else
+ {
+ lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory;
+ fRetVal = TRUE;
+ }
+#elif // HAVE_SYSINFO
+ // TODO: implement getting memory details via sysinfo. On Linux, it provides swap file details that
+ // we can use to fill in the xxxPageFile members.
+
+#endif // HAVE_SYSCONF
+
+ // Get the physical memory in use - from it, we can get the physical memory available.
+ // We do this only when we have the total physical memory available.
+ if (lpBuffer->ullTotalPhys > 0)
+ {
+#ifndef __APPLE__
+ lpBuffer->ullAvailPhys = sysconf(SYSCONF_PAGES) * sysconf(_SC_PAGE_SIZE);
+ INT64 used_memory = lpBuffer->ullTotalPhys - lpBuffer->ullAvailPhys;
+ lpBuffer->dwMemoryLoad = (DWORD)((used_memory * 100) / lpBuffer->ullTotalPhys);
+#else
+ vm_size_t page_size;
+ mach_port_t mach_port;
+ mach_msg_type_number_t count;
+ vm_statistics_data_t vm_stats;
+ mach_port = mach_host_self();
+ count = sizeof(vm_stats) / sizeof(natural_t);
+ if (KERN_SUCCESS == host_page_size(mach_port, &page_size))
+ {
+ if (KERN_SUCCESS == host_statistics(mach_port, HOST_VM_INFO, (host_info_t)&vm_stats, &count))
+ {
+ lpBuffer->ullAvailPhys = (int64_t)vm_stats.free_count * (int64_t)page_size;
+ INT64 used_memory = ((INT64)vm_stats.active_count + (INT64)vm_stats.inactive_count + (INT64)vm_stats.wire_count) * (INT64)page_size;
+ lpBuffer->dwMemoryLoad = (DWORD)((used_memory * 100) / lpBuffer->ullTotalPhys);
+ }
+ }
+ mach_port_deallocate(mach_task_self(), mach_port);
+#endif // __APPLE__
+ }
+
+ // There is no API to get the total virtual address space size on
+ // Unix, so we use a constant value representing 128TB, which is
+ // the approximate size of total user virtual address space on
+ // the currently supported Unix systems.
+ static const UINT64 _128TB = (1ull << 47);
+ lpBuffer->ullTotalVirtual = _128TB;
+ lpBuffer->ullAvailVirtual = lpBuffer->ullAvailPhys;
+
+ LOGEXIT("GlobalMemoryStatusEx returns %d\n", fRetVal);
+ PERF_EXIT(GlobalMemoryStatusEx);
+
+ return fRetVal;
+}
+
+PALIMPORT
+DWORD
+PALAPI
+GetCurrentProcessorNumber()
+{
+#if HAVE_SCHED_GETCPU
+ return sched_getcpu();
+#else //HAVE_SCHED_GETCPU
+ return -1;
+#endif //HAVE_SCHED_GETCPU
+}
+
+BOOL
+PALAPI
+PAL_HasGetCurrentProcessorNumber()
+{
+ return HAVE_SCHED_GETCPU;
+}
+
+DWORD
+PALAPI
+PAL_GetLogicalCpuCountFromOS()
+{
+ DWORD numLogicalCores = 0;
+
+#if HAVE_SYSCONF
+ numLogicalCores = sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+
+ return numLogicalCores;
+}
+
+size_t
+PALAPI
+PAL_GetLogicalProcessorCacheSizeFromOS()
+{
+ size_t cacheSize = 0;
+
+#ifdef _SC_LEVEL1_DCACHE_SIZE
+ cacheSize = max(cacheSize, sysconf(_SC_LEVEL1_DCACHE_SIZE));
+#endif
+#ifdef _SC_LEVEL2_CACHE_SIZE
+ cacheSize = max(cacheSize, sysconf(_SC_LEVEL2_CACHE_SIZE));
+#endif
+#ifdef _SC_LEVEL3_CACHE_SIZE
+ cacheSize = max(cacheSize, sysconf(_SC_LEVEL3_CACHE_SIZE));
+#endif
+#ifdef _SC_LEVEL4_CACHE_SIZE
+ cacheSize = max(cacheSize, sysconf(_SC_LEVEL4_CACHE_SIZE));
+#endif
+
+ return cacheSize;
+}
diff --git a/src/pal/src/misc/time.cpp b/src/pal/src/misc/time.cpp
new file mode 100644
index 0000000000..918f92a90f
--- /dev/null
+++ b/src/pal/src/misc/time.cpp
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ time.c
+
+Abstract:
+
+ Implementation of time related WIN API functions.
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/misc.h"
+
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <string.h>
+#include <sched.h>
+
+#if HAVE_MACH_ABSOLUTE_TIME
+#include <mach/mach_time.h>
+static mach_timebase_info_data_t s_TimebaseInfo;
+#endif
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+/*++
+Function :
+TIMEInitialize
+
+Initialize all Time-related stuff related
+
+(no parameters)
+
+Return value :
+TRUE if Time support initialization succeeded
+FALSE otherwise
+--*/
+BOOL TIMEInitialize(void)
+{
+#if HAVE_MACH_ABSOLUTE_TIME
+ kern_return_t machRet;
+ if ((machRet = mach_timebase_info(&s_TimebaseInfo)) != KERN_SUCCESS)
+ {
+ ASSERT("mach_timebase_info() failed: %s\n", mach_error_string(machRet));
+ return FALSE;
+ }
+#endif
+
+ return TRUE;
+}
+
+
+/*++
+Function:
+ GetSystemTime
+
+The GetSystemTime function retrieves the current system date and
+time. The system time is expressed in Coordinated Universal Time
+(UTC).
+
+Parameters
+
+lpSystemTime
+ [out] Pointer to a SYSTEMTIME structure to receive the current system date and time.
+
+Return Values
+
+This function does not return a value.
+
+--*/
+VOID
+PALAPI
+GetSystemTime(
+ OUT LPSYSTEMTIME lpSystemTime)
+{
+ time_t tt;
+#if HAVE_GMTIME_R
+ struct tm ut;
+#endif /* HAVE_GMTIME_R */
+ struct tm *utPtr;
+ struct timeval timeval;
+ int timeofday_retval;
+
+ PERF_ENTRY(GetSystemTime);
+ ENTRY("GetSystemTime (lpSystemTime=%p)\n", lpSystemTime);
+
+ tt = time(NULL);
+
+ /* We can't get millisecond resolution from time(), so we get it from
+ gettimeofday() */
+ timeofday_retval = gettimeofday(&timeval,NULL);
+
+#if HAVE_GMTIME_R
+ utPtr = &ut;
+ if (gmtime_r(&tt, utPtr) == NULL)
+#else /* HAVE_GMTIME_R */
+ if ((utPtr = gmtime(&tt)) == NULL)
+#endif /* HAVE_GMTIME_R */
+ {
+ ASSERT("gmtime() failed; errno is %d (%s)\n", errno, strerror(errno));
+ goto EXIT;
+ }
+
+ lpSystemTime->wYear = 1900 + utPtr->tm_year;
+ lpSystemTime->wMonth = utPtr->tm_mon + 1;
+ lpSystemTime->wDayOfWeek = utPtr->tm_wday;
+ lpSystemTime->wDay = utPtr->tm_mday;
+ lpSystemTime->wHour = utPtr->tm_hour;
+ lpSystemTime->wMinute = utPtr->tm_min;
+ lpSystemTime->wSecond = utPtr->tm_sec;
+
+ if(-1 == timeofday_retval)
+ {
+ ASSERT("gettimeofday() failed; errno is %d (%s)\n",
+ errno, strerror(errno));
+ lpSystemTime->wMilliseconds = 0;
+ }
+ else
+ {
+ int old_seconds;
+ int new_seconds;
+
+ lpSystemTime->wMilliseconds = timeval.tv_usec/tccMillieSecondsToMicroSeconds;
+
+ old_seconds = utPtr->tm_sec;
+ new_seconds = timeval.tv_sec%60;
+
+ /* just in case we reached the next second in the interval between
+ time() and gettimeofday() */
+ if( old_seconds!=new_seconds )
+ {
+ TRACE("crossed seconds boundary; setting milliseconds to 999\n");
+ lpSystemTime->wMilliseconds = 999;
+ }
+ }
+EXIT:
+ LOGEXIT("GetSystemTime returns void\n");
+ PERF_EXIT(GetSystemTime);
+}
+
+/*++
+Function:
+ GetTickCount
+
+The GetTickCount function retrieves the number of milliseconds that
+have elapsed since the system was started. It is limited to the
+resolution of the system timer. To obtain the system timer resolution,
+use the GetSystemTimeAdjustment function.
+
+Parameters
+
+This function has no parameters.
+
+Return Values
+
+The return value is the number of milliseconds that have elapsed since
+the system was started.
+
+In the ROTOR implementation the return value is the elapsed time since
+the start of the epoch.
+
+--*/
+DWORD
+PALAPI
+GetTickCount(
+ VOID)
+{
+ DWORD retval = 0;
+ PERF_ENTRY(GetTickCount);
+ ENTRY("GetTickCount ()\n");
+
+ // Get the 64-bit count from GetTickCount64 and truncate the results.
+ retval = (DWORD) GetTickCount64();
+
+ LOGEXIT("GetTickCount returns DWORD %u\n", retval);
+ PERF_EXIT(GetTickCount);
+ return retval;
+}
+
+BOOL
+PALAPI
+QueryPerformanceCounter(
+ OUT LARGE_INTEGER *lpPerformanceCount
+ )
+{
+ BOOL retval = TRUE;
+
+ PERF_ENTRY(QueryPerformanceCounter);
+ ENTRY("QueryPerformanceCounter()\n");
+ do
+#if HAVE_CLOCK_MONOTONIC
+ {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ {
+ ASSERT("clock_gettime(CLOCK_MONOTONIC) failed; errno is %d (%s)\n", errno, strerror(errno));
+ retval = FALSE;
+ break;
+ }
+ lpPerformanceCount->QuadPart =
+ (LONGLONG)ts.tv_sec * (LONGLONG)tccSecondsToNanoSeconds + (LONGLONG)ts.tv_nsec;
+ }
+#elif HAVE_MACH_ABSOLUTE_TIME
+ {
+ lpPerformanceCount->QuadPart = (LONGLONG)mach_absolute_time();
+ }
+#elif HAVE_GETHRTIME
+ {
+ lpPerformanceCount->QuadPart = (LONGLONG)gethrtime();
+ }
+#elif HAVE_READ_REAL_TIME
+ {
+ timebasestruct_t tb;
+ read_real_time(&tb, TIMEBASE_SZ);
+ if (time_base_to_time(&tb, TIMEBASE_SZ) != 0)
+ {
+ ASSERT("time_base_to_time() failed; errno is %d (%s)\n", errno, strerror(errno));
+ retval = FALSE;
+ break;
+ }
+ lpPerformanceCount->QuadPart =
+ (LONGLONG)tb.tb_high * (LONGLONG)tccSecondsToNanoSeconds + (LONGLONG)tb.tb_low;
+ }
+#else
+ {
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) == -1)
+ {
+ ASSERT("gettimeofday() failed; errno is %d (%s)\n", errno, strerror(errno));
+ retval = FALSE;
+ break;
+ }
+ lpPerformanceCount->QuadPart =
+ (LONGLONG)tv.tv_sec * (LONGLONG)tccSecondsToMicroSeconds + (LONGLONG)tv.tv_usec;
+ }
+#endif // HAVE_CLOCK_MONOTONIC
+ while (false);
+
+ LOGEXIT("QueryPerformanceCounter\n");
+ PERF_EXIT(QueryPerformanceCounter);
+ return retval;
+}
+
+BOOL
+PALAPI
+QueryPerformanceFrequency(
+ OUT LARGE_INTEGER *lpFrequency
+ )
+{
+ BOOL retval = TRUE;
+ PERF_ENTRY(QueryPerformanceFrequency);
+ ENTRY("QueryPerformanceFrequency()\n");
+#if HAVE_GETHRTIME || HAVE_READ_REAL_TIME || HAVE_CLOCK_MONOTONIC
+ lpFrequency->QuadPart = (LONGLONG)tccSecondsToNanoSeconds;
+#elif HAVE_MACH_ABSOLUTE_TIME
+ // use denom == 0 to indicate that s_TimebaseInfo is uninitialised.
+ if (s_TimebaseInfo.denom == 0)
+ {
+ ASSERT("s_TimebaseInfo is uninitialized.\n");
+ retval = FALSE;
+ }
+ else
+ {
+ lpFrequency->QuadPart = (LONGLONG)tccSecondsToNanoSeconds * ((LONGLONG)s_TimebaseInfo.denom / (LONGLONG)s_TimebaseInfo.numer);
+ }
+#else
+ lpFrequency->QuadPart = (LONGLONG)tccSecondsToMicroSeconds;
+#endif // HAVE_GETHRTIME || HAVE_READ_REAL_TIME || HAVE_CLOCK_MONOTONIC
+ LOGEXIT("QueryPerformanceFrequency\n");
+ PERF_EXIT(QueryPerformanceFrequency);
+ return retval;
+}
+
+/*++
+Function:
+ QueryThreadCycleTime
+
+Puts the execution time (in nanoseconds) for the thread pointed to by ThreadHandle, into the unsigned long
+pointed to by CycleTime. ThreadHandle must refer to the current thread. Returns TRUE on success, FALSE on
+failure.
+--*/
+
+BOOL
+PALAPI
+QueryThreadCycleTime(
+ IN HANDLE ThreadHandle,
+ OUT PULONG64 CycleTime
+ )
+{
+
+ ULONG64 calcTime;
+ FILETIME kernelTime, userTime;
+ BOOL retval = TRUE;
+
+ if(!GetThreadTimesInternal(ThreadHandle, &kernelTime, &userTime))
+ {
+ ASSERT("Could not get cycle time for current thread");
+ retval = FALSE;
+ goto EXIT;
+ }
+
+ calcTime = ((ULONG64)kernelTime.dwHighDateTime << 32);
+ calcTime += (ULONG64)kernelTime.dwLowDateTime;
+ calcTime += ((ULONG64)userTime.dwHighDateTime << 32);
+ calcTime += (ULONG64)userTime.dwLowDateTime;
+ *CycleTime = calcTime;
+
+EXIT:
+ return retval;
+}
+
+/*++
+Function:
+ GetTickCount64
+
+Returns a 64-bit tick count with a millisecond resolution. It tries its best
+to return monotonically increasing counts and avoid being affected by changes
+to the system clock (either due to drift or due to explicit changes to system
+time).
+--*/
+PALAPI
+ULONGLONG
+GetTickCount64()
+{
+ ULONGLONG retval = 0;
+
+#if HAVE_CLOCK_MONOTONIC_COARSE || HAVE_CLOCK_MONOTONIC
+ {
+ clockid_t clockType =
+#if HAVE_CLOCK_MONOTONIC_COARSE
+ CLOCK_MONOTONIC_COARSE; // good enough resolution, fastest speed
+#else
+ CLOCK_MONOTONIC;
+#endif
+ struct timespec ts;
+ if (clock_gettime(clockType, &ts) != 0)
+ {
+ ASSERT("clock_gettime(CLOCK_MONOTONIC*) failed; errno is %d (%s)\n", errno, strerror(errno));
+ goto EXIT;
+ }
+ retval = (ts.tv_sec * tccSecondsToMillieSeconds)+(ts.tv_nsec / tccMillieSecondsToNanoSeconds);
+ }
+#elif HAVE_MACH_ABSOLUTE_TIME
+ {
+ // use denom == 0 to indicate that s_TimebaseInfo is uninitialised.
+ if (s_TimebaseInfo.denom == 0)
+ {
+ ASSERT("s_TimebaseInfo is uninitialized.\n");
+ goto EXIT;
+ }
+ retval = (mach_absolute_time() * s_TimebaseInfo.numer / s_TimebaseInfo.denom) / tccMillieSecondsToNanoSeconds;
+ }
+#elif HAVE_GETHRTIME
+ {
+ retval = (ULONGLONG)(gethrtime() / tccMillieSecondsToNanoSeconds);
+ }
+#elif HAVE_READ_REAL_TIME
+ {
+ timebasestruct_t tb;
+ read_real_time(&tb, TIMEBASE_SZ);
+ if (time_base_to_time(&tb, TIMEBASE_SZ) != 0)
+ {
+ ASSERT("time_base_to_time() failed; errno is %d (%s)\n", errno, strerror(errno));
+ goto EXIT;
+ }
+ retval = (tb.tb_high * tccSecondsToMillieSeconds)+(tb.tb_low / tccMillieSecondsToNanoSeconds);
+ }
+#else
+ {
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) == -1)
+ {
+ ASSERT("gettimeofday() failed; errno is %d (%s)\n", errno, strerror(errno));
+ goto EXIT;
+ }
+ retval = (tv.tv_sec * tccSecondsToMillieSeconds) + (tv.tv_usec / tccMillieSecondsToMicroSeconds);
+ }
+#endif // HAVE_CLOCK_MONOTONIC
+EXIT:
+ return retval;
+}
+
diff --git a/src/pal/src/misc/tracepointprovider.cpp b/src/pal/src/misc/tracepointprovider.cpp
new file mode 100644
index 0000000000..8d20266688
--- /dev/null
+++ b/src/pal/src/misc/tracepointprovider.cpp
@@ -0,0 +1,109 @@
+// 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.
+
+/*++
+
+Module Name:
+
+ tracepointprovider.cpp
+
+Abstract:
+
+ Trace point provider support
+
+Revision History:
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+#include "pal/process.h"
+#include "pal/module.h"
+#include "pal/malloc.hpp"
+#include "pal/stackstring.hpp"
+
+#include <errno.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <dlfcn.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+/*++
+
+Initialization logic for LTTng tracepoint providers.
+
+--*/
+#if defined(__linux__)
+
+static const char tpLibName[] = "libcoreclrtraceptprovider.so";
+
+
+/*++
+
+NOTE: PAL_InitializeTracing MUST NOT depend on anything in the PAL itself
+as it is called prior to PAL initialization.
+
+Constructor priority is set to 200, which allows for constructors to
+guarantee that they run before or after this constructor by setting
+their priority appropriately.
+
+Priority values must be greater than 100. The lower the value,
+the higher the priority.
+
+--*/
+__attribute__((__unused__))
+__attribute__((constructor (200)))
+static void
+PAL_InitializeTracing(void)
+{
+ // Get the path to the currently executing shared object (libcoreclr.so).
+ Dl_info info;
+ int succeeded = dladdr((void *)PAL_InitializeTracing, &info);
+ if(!succeeded)
+ {
+ return;
+ }
+
+ // Copy the path and modify the shared object name to be the tracepoint provider.
+ PathCharString tpProvPath;
+ int pathLen = strlen(info.dli_fname);
+
+ // Find the length of the full path without the shared object name, including the trailing slash.
+ int lastTrailingSlashLen = -1;
+ for(int i=pathLen-1; i>=0; i--)
+ {
+ if(info.dli_fname[i] == '/')
+ {
+ lastTrailingSlashLen = i+1;
+ break;
+ }
+ }
+
+ // Make sure we found the last trailing slash.
+ if(lastTrailingSlashLen == -1)
+ {
+ return;
+ }
+
+ SIZE_T tpLibNameLen = strlen(tpLibName);
+
+ if( !tpProvPath.Reserve(tpLibNameLen + lastTrailingSlashLen) ||
+ // Copy the path without the shared object name.
+ !tpProvPath.Append(info.dli_fname, lastTrailingSlashLen) ||
+ // Append the shared object name for the tracepoint provider.
+ !tpProvPath.Append(tpLibName, tpLibNameLen))
+ {
+ return;
+ }
+
+
+
+ // Load the tracepoint provider.
+ // It's OK if this fails - that just means that tracing dependencies aren't available.
+ dlopen(tpProvPath, RTLD_NOW | RTLD_GLOBAL);
+}
+
+#endif
diff --git a/src/pal/src/misc/utils.cpp b/src/pal/src/misc/utils.cpp
new file mode 100644
index 0000000000..1e333d19ac
--- /dev/null
+++ b/src/pal/src/misc/utils.cpp
@@ -0,0 +1,320 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ misc/utils.c
+
+Abstract:
+
+ Miscellaneous helper functions for the PAL, which don't fit anywhere else
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#if HAVE_VM_ALLOCATE
+#include <mach/message.h>
+#endif //HAVE_VM_ALLOCATE
+
+#include "pal/utils.h"
+#include "pal/dbgmsg.h"
+#include "pal/file.h"
+
+#include <errno.h>
+#include <string.h>
+
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+// In safemath.h, Template SafeInt uses macro _ASSERTE, which need to use variable
+// defdbgchan defined by SET_DEFAULT_DEBUG_CHANNEL. Therefore, the include statement
+// should be placed after the SET_DEFAULT_DEBUG_CHANNEL(MISC)
+#include <safemath.h>
+
+/*++
+Function:
+ UTIL_inverse_wcspbrk
+
+ Opposite of wcspbrk : searches a string for the first character NOT in the
+ given set
+
+Parameters :
+ LPWSTR lpwstr : string to search
+ LPCWSTR charset : list of characters to search for
+
+Return value :
+ pointer to first character of lpwstr that isn't in the set
+ NULL if all characters are in the set
+--*/
+LPWSTR UTIL_inverse_wcspbrk(LPWSTR lpwstr, LPCWSTR charset)
+{
+ while(*lpwstr)
+ {
+ if(NULL == PAL_wcschr(charset,*lpwstr))
+ {
+ return lpwstr;
+ }
+ lpwstr++;
+ }
+ return NULL;
+}
+
+
+/*++
+Function :
+ UTIL_IsReadOnlyBitsSet
+
+ Takes a struct stat *
+ Returns true if the file is read only,
+--*/
+BOOL UTIL_IsReadOnlyBitsSet( struct stat * stat_data )
+{
+ BOOL bRetVal = FALSE;
+
+ /* Check for read permissions. */
+ if ( stat_data->st_uid == geteuid() )
+ {
+ /* The process owner is the file owner as well. */
+ if ( ( stat_data->st_mode & S_IRUSR ) && !( stat_data->st_mode & S_IWUSR ) )
+ {
+ bRetVal = TRUE;
+ }
+ }
+ else if ( stat_data->st_gid == getegid() )
+ {
+ /* The process's owner is in the same group as the file's owner. */
+ if ( ( stat_data->st_mode & S_IRGRP ) && !( stat_data->st_mode & S_IWGRP ) )
+ {
+ bRetVal = TRUE;
+ }
+ }
+ else
+ {
+ /* Check the other bits to see who can access the file. */
+ if ( ( stat_data->st_mode & S_IROTH ) && !( stat_data->st_mode & S_IWOTH ) )
+ {
+ bRetVal = TRUE;
+ }
+ }
+
+ return bRetVal;
+}
+
+/*++
+Function :
+ UTIL_IsExecuteBitsSet
+
+ Takes a struct stat *
+ Returns true if the file is executable,
+--*/
+BOOL UTIL_IsExecuteBitsSet( struct stat * stat_data )
+{
+ BOOL bRetVal = FALSE;
+
+ if ( (stat_data->st_mode & S_IFMT) == S_IFDIR )
+ {
+ return FALSE;
+ }
+
+ /* Check for read permissions. */
+ if ( stat_data->st_uid == geteuid() )
+ {
+ /* The process owner is the file owner as well. */
+ if ( ( stat_data->st_mode & S_IXUSR ) )
+ {
+ bRetVal = TRUE;
+ }
+ }
+ else if ( stat_data->st_gid == getegid() )
+ {
+ /* The process's owner is in the same group as the file's owner. */
+ if ( ( stat_data->st_mode & S_IXGRP ) )
+ {
+ bRetVal = TRUE;
+ }
+ }
+ else
+ {
+ /* Check the other bits to see who can access the file. */
+ if ( ( stat_data->st_mode & S_IXOTH ) )
+ {
+ bRetVal = TRUE;
+ }
+ }
+
+ return bRetVal;
+}
+
+/*++
+Function :
+ UTIL_WCToMB_Alloc
+
+ Converts a wide string to a multibyte string, allocating the required buffer
+
+Parameters :
+ LPCWSTR lpWideCharStr : string to convert
+ int cchWideChar : number of wide characters to convert
+ (-1 to convert a complete null-termnated string)
+
+Return Value :
+ newly allocated buffer containing the converted string. Conversion is
+ performed using CP_ACP. Buffer is allocated with malloc(), release it
+ with free().
+ In case if failure, LastError will be set.
+--*/
+LPSTR UTIL_WCToMB_Alloc(LPCWSTR lpWideCharStr, int cchWideChar)
+{
+ int length;
+ LPSTR lpMultiByteStr;
+
+ /* get required buffer length */
+ length = WideCharToMultiByte(CP_ACP, 0, lpWideCharStr, cchWideChar,
+ NULL, 0, NULL, NULL);
+ if(0 == length)
+ {
+ ERROR("WCToMB error; GetLastError returns %#x", GetLastError());
+ return NULL;
+ }
+
+ /* allocate required buffer */
+ lpMultiByteStr = (LPSTR)PAL_malloc(length);
+ if(NULL == lpMultiByteStr)
+ {
+ ERROR("malloc() failed! errno is %d (%s)\n", errno,strerror(errno));
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+ }
+
+ /* convert into allocated buffer */
+ length = WideCharToMultiByte(CP_ACP, 0, lpWideCharStr, cchWideChar,
+ lpMultiByteStr, length, NULL, NULL);
+ if(0 == length)
+ {
+ ASSERT("WCToMB error; GetLastError returns %#x\n", GetLastError());
+ PAL_free(lpMultiByteStr);
+ return NULL;
+ }
+ return lpMultiByteStr;
+}
+
+/*++
+Function :
+ UTIL_MBToWC_Alloc
+
+ Converts a multibyte string to a wide string, allocating the required buffer
+
+Parameters :
+ LPCSTR lpMultiByteStr : string to convert
+ int cbMultiByte : number of bytes to convert
+ (-1 to convert a complete null-termnated string)
+
+Return Value :
+ newly allocated buffer containing the converted string. Conversion is
+ performed using CP_ACP. Buffer is allocated with malloc(), release it
+ with free().
+ In case if failure, LastError will be set.
+--*/
+LPWSTR UTIL_MBToWC_Alloc(LPCSTR lpMultiByteStr, int cbMultiByte)
+{
+ int length;
+ LPWSTR lpWideCharStr;
+
+ /* get required buffer length */
+ length = MultiByteToWideChar(CP_ACP, 0, lpMultiByteStr, cbMultiByte,
+ NULL, 0);
+ if(0 == length)
+ {
+ ERROR("MBToWC error; GetLastError returns %#x", GetLastError());
+ return NULL;
+ }
+
+ /* allocate required buffer */
+ size_t fullsize;
+ if (!ClrSafeInt<size_t>::multiply(length,sizeof(WCHAR),fullsize))
+ {
+ ERROR("integer overflow! length = %d , sizeof(WCHAR) = (%d)\n", length,sizeof(WCHAR) );
+ SetLastError(ERROR_ARITHMETIC_OVERFLOW);
+ return NULL;
+ }
+
+ lpWideCharStr = (LPWSTR)PAL_malloc(fullsize);
+ if(NULL == lpWideCharStr)
+ {
+ ERROR("malloc() failed! errno is %d (%s)\n", errno,strerror(errno));
+ SetLastError(FILEGetLastErrorFromErrno());
+ return NULL;
+ }
+
+ /* convert into allocated buffer */
+ length = MultiByteToWideChar(CP_ACP, 0, lpMultiByteStr, cbMultiByte,
+ lpWideCharStr, length);
+ if(0 >= length)
+ {
+ ASSERT("MCToMB error; GetLastError returns %#x\n", GetLastError());
+ PAL_free(lpWideCharStr);
+ return NULL;
+ }
+ return lpWideCharStr;
+}
+
+#if HAVE_VM_ALLOCATE
+/*++
+Function:
+ UTIL_MachErrorToPalError
+
+ Maps a Mach kern_return_t to a Win32 error code.
+--*/
+DWORD UTIL_MachErrorToPalError(kern_return_t MachReturn)
+{
+ switch (MachReturn)
+ {
+ case KERN_SUCCESS:
+ return ERROR_SUCCESS;
+
+ case KERN_NO_ACCESS:
+ case KERN_INVALID_CAPABILITY:
+ return ERROR_ACCESS_DENIED;
+
+ case KERN_TERMINATED:
+ return ERROR_INVALID_HANDLE;
+
+ case KERN_INVALID_ADDRESS:
+ return ERROR_INVALID_ADDRESS;
+
+ case KERN_NO_SPACE:
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ case KERN_INVALID_ARGUMENT:
+ return ERROR_INVALID_PARAMETER;
+
+ default:
+ ASSERT("Unknown kern_return_t value %d - reporting ERROR_INTERNAL_ERROR\n", MachReturn);
+ return ERROR_INTERNAL_ERROR;
+ }
+}
+
+/*++
+Function:
+ UTIL_SetLastErrorFromMach
+
+ Sets Win32 LastError according to the argument Mach kern_return_t value,
+ provided it indicates an error. If the argument indicates success, does
+ not modify LastError.
+--*/
+void UTIL_SetLastErrorFromMach(kern_return_t MachReturn)
+{
+ DWORD palError = UTIL_MachErrorToPalError(MachReturn);
+ if (palError != ERROR_SUCCESS)
+ {
+ SetLastError(palError);
+ }
+}
+#endif //HAVE_VM_ALLOCATE
+
diff --git a/src/pal/src/misc/version.cpp b/src/pal/src/misc/version.cpp
new file mode 100644
index 0000000000..7a9f90a320
--- /dev/null
+++ b/src/pal/src/misc/version.cpp
@@ -0,0 +1,119 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ version.c
+
+Abstract:
+
+ Implementation of functions for getting platform.OS versions.
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(MISC);
+
+/*++
+Function:
+ GetVersionExA
+
+
+
+GetVersionEx
+
+The GetVersionEx function obtains extended information about the
+version of the operating system that is currently running.
+
+Parameters
+
+lpVersionInfo
+ [in/out] Pointer to an OSVERSIONINFO data structure that the
+ function fills with operating system version information.
+
+ Before calling the GetVersionEx function, set the
+ dwOSVersionInfoSize member of the OSVERSIONINFO data structure
+ to sizeof(OSVERSIONINFO).
+
+Return Values
+
+If the function succeeds, the return value is a nonzero value.
+
+If the function fails, the return value is zero. To get extended error
+information, call GetLastError. The function fails if you specify an
+invalid value for the dwOSVersionInfoSize member of the OSVERSIONINFO
+structure.
+
+--*/
+BOOL
+PALAPI
+GetVersionExA(
+ IN OUT LPOSVERSIONINFOA lpVersionInformation)
+{
+ BOOL bRet = TRUE;
+ PERF_ENTRY(GetVersionExA);
+ ENTRY("GetVersionExA (lpVersionInformation=%p)\n", lpVersionInformation);
+
+ if (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA))
+ {
+ lpVersionInformation->dwMajorVersion = 5; /* same as WIN2000 */
+ lpVersionInformation->dwMinorVersion = 0; /* same as WIN2000 */
+ lpVersionInformation->dwBuildNumber = 0;
+ lpVersionInformation->dwPlatformId = VER_PLATFORM_UNIX;
+ lpVersionInformation->szCSDVersion[0] = '\0'; /* no service pack */
+ }
+ else
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ bRet = FALSE;
+ }
+ LOGEXIT("GetVersionExA returning BOOL %d\n", bRet);
+ PERF_EXIT(GetVersionExA);
+ return bRet;
+}
+
+
+/*++
+Function:
+ GetVersionExW
+
+See GetVersionExA
+--*/
+BOOL
+PALAPI
+GetVersionExW(
+ IN OUT LPOSVERSIONINFOW lpVersionInformation)
+{
+ BOOL bRet = TRUE;
+
+ PERF_ENTRY(GetVersionExW);
+ ENTRY("GetVersionExW (lpVersionInformation=%p)\n", lpVersionInformation);
+
+ if (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOW))
+ {
+ lpVersionInformation->dwMajorVersion = 5; /* same as WIN2000 */
+ lpVersionInformation->dwMinorVersion = 0; /* same as WIN2000 */
+ lpVersionInformation->dwBuildNumber = 0;
+ lpVersionInformation->dwPlatformId = VER_PLATFORM_UNIX;
+ lpVersionInformation->szCSDVersion[0] = '\0'; /* no service pack */
+ }
+ else
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ bRet = FALSE;
+ }
+ LOGEXIT("GetVersionExW returning BOOL %d\n", bRet);
+ PERF_EXIT(GetVersionExW);
+ return bRet;
+}
diff --git a/src/pal/src/objmgr/palobjbase.cpp b/src/pal/src/objmgr/palobjbase.cpp
new file mode 100644
index 0000000000..27842f6d97
--- /dev/null
+++ b/src/pal/src/objmgr/palobjbase.cpp
@@ -0,0 +1,359 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ palobjbase.cpp
+
+Abstract:
+ PAL object base class
+
+
+
+--*/
+
+#include "palobjbase.hpp"
+#include "pal/malloc.hpp"
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(PAL);
+
+using namespace CorUnix;
+
+CObjectType* CObjectType::s_rgotIdMapping[ObjectTypeIdCount];
+
+/*++
+Function:
+ CPalObjectBase::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
+CPalObjectBase::Initialize(
+ CPalThread *pthr,
+ CObjectAttributes *poa
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != poa);
+
+ ENTRY("CPalObjectBase::Initialize"
+ "(this = %p, pthr = %p, poa = %p)\n",
+ this,
+ pthr,
+ poa
+ );
+
+ if (0 != m_pot->GetImmutableDataSize())
+ {
+ m_pvImmutableData = InternalMalloc(m_pot->GetImmutableDataSize());
+ if (NULL != m_pvImmutableData)
+ {
+ ZeroMemory(m_pvImmutableData, m_pot->GetImmutableDataSize());
+ }
+ else
+ {
+ ERROR("Unable to allocate immutable data\n");
+ palError = ERROR_OUTOFMEMORY;
+ goto IntializeExit;
+ }
+ }
+
+ if (0 != m_pot->GetProcessLocalDataSize())
+ {
+ palError = m_sdlLocalData.Initialize();
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to initialize local data lock!\n");
+ goto IntializeExit;
+ }
+
+ m_pvLocalData = InternalMalloc(m_pot->GetProcessLocalDataSize());
+ if (NULL != m_pvLocalData)
+ {
+ ZeroMemory(m_pvLocalData, m_pot->GetProcessLocalDataSize());
+ }
+ else
+ {
+ ERROR("Unable to allocate local data\n");
+ palError = ERROR_OUTOFMEMORY;
+ goto IntializeExit;
+ }
+ }
+
+ if (0 != poa->sObjectName.GetStringLength())
+ {
+ palError = m_oa.sObjectName.CopyString(&poa->sObjectName);
+ }
+
+IntializeExit:
+
+ LOGEXIT("CPalObjectBase::Initialize returns %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CPalObjectBase::GetObjectType
+
+ Returns the type of the object
+--*/
+
+CObjectType *
+CPalObjectBase::GetObjectType(
+ VOID
+ )
+{
+ ENTRY("CPalObjectBase::GetObjectType(this = %p)\n", this);
+ LOGEXIT("CPalObjectBase::GetObjectType returns %p\n", m_pot);
+
+ return m_pot;
+}
+
+/*++
+Function:
+ CPalObjectBase::GetObjectAttributes
+
+ Returns the attributes of the object
+--*/
+
+CObjectAttributes *
+CPalObjectBase::GetObjectAttributes(
+ VOID
+ )
+{
+ ENTRY("CPalObjectBase::GetObjectAttributes(this = %p)\n", this);
+ LOGEXIT("CPalObjectBase::GetObjectAttributes returns %p\n", &m_oa);
+
+ return &m_oa;
+}
+
+/*++
+Function:
+ CPalObjectBase::GetImmutableData
+
+ Provides the caller access to the object's immutable data (if any)
+
+Parameters:
+ ppvImmutableData -- on success, receives a pointer to the object's
+ immutable data
+--*/
+
+PAL_ERROR
+CPalObjectBase::GetImmutableData(
+ void **ppvImmutableData // OUT
+ )
+{
+ _ASSERTE(NULL != ppvImmutableData);
+
+ ENTRY("CPalObjectBase::GetImmutableData"
+ "(this = %p, ppvImmutableData = %p)\n",
+ this,
+ ppvImmutableData
+ );
+
+ _ASSERTE(0 < m_pot->GetImmutableDataSize());
+
+ *ppvImmutableData = m_pvImmutableData;
+
+ LOGEXIT("CPalObjectBase::GetImmutableData returns %d\n", NO_ERROR);
+
+ return NO_ERROR;
+}
+
+/*++
+Function:
+ CPalObjectBase::GetProcessLocalData
+
+ Provides the caller access to the object's local data (if any)
+
+Parameters:
+ pthr -- thread data for calling thread
+ eLockRequest -- specifies if the caller desires a read lock or a
+ write lock on the data (currently ignored)
+ ppDataLock -- on success, receives a pointer to the data lock instance
+ for the local data
+ ppvProcssLocalData -- on success, receives a pointer to the local data
+--*/
+
+PAL_ERROR
+CPalObjectBase::GetProcessLocalData(
+ CPalThread *pthr,
+ LockType eLockRequest,
+ IDataLock **ppDataLock, // OUT
+ void **ppvProcessLocalData // OUT
+ )
+{
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(ReadLock == eLockRequest || WriteLock == eLockRequest);
+ _ASSERTE(NULL != ppDataLock);
+ _ASSERTE(NULL != ppvProcessLocalData);
+
+ ENTRY("CPalObjectBase::GetProcessLocalData"
+ "(this = %p, pthr = %p, eLockRequest = %d, ppDataLock = %p,"
+ " ppvProcessLocalData = %p)\n",
+ this,
+ pthr,
+ eLockRequest,
+ ppDataLock,
+ ppvProcessLocalData
+ );
+
+ _ASSERTE(0 < m_pot->GetProcessLocalDataSize());
+
+ m_sdlLocalData.AcquireLock(pthr, ppDataLock);
+ *ppvProcessLocalData = m_pvLocalData;
+
+ LOGEXIT("CPalObjectBase::GetProcessLocalData returns %d\n", NO_ERROR);
+
+ return NO_ERROR;
+}
+
+/*++
+Function:
+ CPalObjectBase::AddReference
+
+ Increments the object's reference count. The updated count is returned
+ for diagnostic purposes only
+--*/
+
+DWORD
+CPalObjectBase::AddReference(
+ void
+ )
+{
+ LONG lRefCount;
+
+ ENTRY("CPalObjectBase::AddReference(this = %p)\n", this);
+
+ _ASSERTE(m_lRefCount > 0);
+ lRefCount = InterlockedIncrement(&m_lRefCount);
+
+ LOGEXIT("CPalObjectBase::AddReference returns %d\n", lRefCount);
+
+ return lRefCount;
+}
+
+/*++
+Function:
+ CPalObjectBase::ReleaseReference
+
+ Decrements the object's reference count. The updated count is returned
+ for diagnostic purposes only
+
+Parameters:
+ pthr -- thread data for calling thread
+--*/
+
+DWORD
+CPalObjectBase::ReleaseReference(
+ CPalThread *pthr
+ )
+{
+ LONG lRefCount;
+
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("CPalObjectBase::ReleaseReference"
+ "(this = %p, pthr = %p)\n",
+ this,
+ pthr
+ );
+
+ AcquireObjectDestructionLock(pthr);
+
+ _ASSERTE(m_lRefCount > 0);
+
+ //
+ // Even though object destruction takes place under a lock
+ // we still need to use an interlocked decrement, as AddRef
+ // operates lock free
+ //
+
+ lRefCount = InterlockedDecrement(&m_lRefCount);
+
+ if (0 == lRefCount)
+ {
+ bool fCleanupSharedState = ReleaseObjectDestructionLock(pthr, TRUE);
+
+ //
+ // We need to do two things with the calling thread data here:
+ // 1) store it in m_pthrCleanup so it is available to the destructors
+ // 2) Add a reference to it before starting any cleanup, and release
+ // that reference afterwords.
+ //
+ // Step 2 is necessary when we're cleaning up the thread object that
+ // represents the calling thread -- it ensures that the thread data
+ // is available throughout the entire cleanup process.
+ //
+
+ m_pthrCleanup = pthr;
+ pthr->AddThreadReference();
+
+ if (NULL != m_pot->GetObjectCleanupRoutine())
+ {
+ (*m_pot->GetObjectCleanupRoutine())(
+ pthr,
+ static_cast<IPalObject*>(this),
+ FALSE,
+ fCleanupSharedState
+ );
+ }
+
+ InternalDelete(this);
+
+ pthr->ReleaseThreadReference();
+ }
+ else
+ {
+ ReleaseObjectDestructionLock(pthr, FALSE);
+ }
+
+ LOGEXIT("CPalObjectBase::ReleaseReference returns %d\n", lRefCount);
+
+ return lRefCount;
+}
+
+/*++
+Function:
+ CPalObjectBase::~CPalObjectBase
+
+ Object destructor
+--*/
+
+CPalObjectBase::~CPalObjectBase()
+{
+ ENTRY("CPalObjectBase::~CPalObjectBase(this = %p)\n", this);
+
+ if (NULL != m_pvImmutableData)
+ {
+ free(m_pvImmutableData);
+ }
+
+ if (NULL != m_pvLocalData)
+ {
+ free(m_pvLocalData);
+ }
+
+ if (NULL != m_oa.sObjectName.GetString())
+ {
+ m_oa.sObjectName.FreeBuffer();
+ }
+
+ LOGEXIT("CPalObjectBase::~CPalObjectBase\n");
+}
+
diff --git a/src/pal/src/objmgr/palobjbase.hpp b/src/pal/src/objmgr/palobjbase.hpp
new file mode 100644
index 0000000000..4a7f4ac3ed
--- /dev/null
+++ b/src/pal/src/objmgr/palobjbase.hpp
@@ -0,0 +1,191 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ palobjbase.hpp
+
+Abstract:
+ PAL object base class
+
+
+
+--*/
+
+#ifndef _PALOBJBASE_HPP_
+#define _PALOBJBASE_HPP_
+
+#include "pal/corunix.hpp"
+#include "pal/cs.hpp"
+#include "pal/thread.hpp"
+
+namespace CorUnix
+{
+ class CSimpleDataLock : IDataLock
+ {
+ private:
+
+ CRITICAL_SECTION m_cs;
+ bool m_fInitialized;
+
+ public:
+
+ CSimpleDataLock()
+ :
+ m_fInitialized(FALSE)
+ {
+ };
+
+ virtual ~CSimpleDataLock()
+ {
+ if (m_fInitialized)
+ {
+ InternalDeleteCriticalSection(&m_cs);
+ }
+ };
+
+ PAL_ERROR
+ Initialize(
+ void
+ )
+ {
+ PAL_ERROR palError = NO_ERROR;
+
+ InternalInitializeCriticalSection(&m_cs);
+ m_fInitialized = TRUE;
+
+ return palError;
+ };
+
+ void
+ AcquireLock(
+ CPalThread *pthr,
+ IDataLock **pDataLock
+ )
+ {
+ InternalEnterCriticalSection(pthr, &m_cs);
+ *pDataLock = static_cast<IDataLock*>(this);
+ };
+
+ virtual
+ void
+ ReleaseLock(
+ CPalThread *pthr,
+ bool fDataChanged
+ )
+ {
+ InternalLeaveCriticalSection(pthr, &m_cs);
+ };
+
+ };
+
+ class CPalObjectBase : public IPalObject
+ {
+ template <class T> friend void InternalDelete(T *p);
+
+ protected:
+
+ LONG m_lRefCount;
+
+ VOID *m_pvImmutableData;
+ VOID *m_pvLocalData;
+
+ CObjectType *m_pot;
+ CObjectAttributes m_oa;
+
+ CSimpleDataLock m_sdlLocalData;
+
+ //
+ // The thread that initiated object cleanup
+ //
+
+ CPalThread *m_pthrCleanup;
+
+ virtual ~CPalObjectBase();
+
+ virtual
+ void
+ AcquireObjectDestructionLock(
+ CPalThread *pthr
+ ) = 0;
+
+ virtual
+ bool
+ ReleaseObjectDestructionLock(
+ CPalThread *pthr,
+ bool fDestructionPending
+ ) = 0;
+
+ public:
+
+ CPalObjectBase(
+ CObjectType *pot
+ )
+ :
+ m_lRefCount(1),
+ m_pvImmutableData(NULL),
+ m_pvLocalData(NULL),
+ m_pot(pot),
+ m_pthrCleanup(NULL)
+ {
+ };
+
+ virtual
+ PAL_ERROR
+ Initialize(
+ CPalThread *pthr,
+ CObjectAttributes *poa
+ );
+
+ //
+ // IPalObject routines
+ //
+
+ virtual
+ CObjectType *
+ GetObjectType(
+ VOID
+ );
+
+ virtual
+ CObjectAttributes *
+ GetObjectAttributes(
+ VOID
+ );
+
+ virtual
+ PAL_ERROR
+ GetImmutableData(
+ void **ppvImmutableData // OUT
+ );
+
+ virtual
+ PAL_ERROR
+ GetProcessLocalData(
+ CPalThread *pthr, // IN, OPTIONAL
+ LockType eLockRequest,
+ IDataLock **ppDataLock, // OUT
+ void **ppvProcessLocalData // OUT
+ );
+
+ virtual
+ DWORD
+ AddReference(
+ void
+ );
+
+ virtual
+ DWORD
+ ReleaseReference(
+ CPalThread *pthr
+ );
+ };
+}
+
+#endif // _PALOBJBASE_HPP_
+
diff --git a/src/pal/src/objmgr/shmobject.cpp b/src/pal/src/objmgr/shmobject.cpp
new file mode 100644
index 0000000000..1435d5d734
--- /dev/null
+++ b/src/pal/src/objmgr/shmobject.cpp
@@ -0,0 +1,1471 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ shmobject.hpp
+
+Abstract:
+ Shared memory based object
+
+
+
+--*/
+
+#include "shmobject.hpp"
+#include "pal/malloc.hpp"
+#include "pal/cs.hpp"
+#include "pal/dbgmsg.h"
+
+#include <stddef.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(PAL);
+
+using namespace CorUnix;
+
+/*++
+Function:
+ CSharedMemoryObject::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
+CSharedMemoryObject::Initialize(
+ CPalThread *pthr,
+ CObjectAttributes *poa
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ SHMObjData *psmod = NULL;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != poa);
+
+ ENTRY("CSharedMemoryObject::Initialize"
+ "(this = %p, pthr = %p, poa = %p)\n",
+ this,
+ pthr,
+ poa
+ );
+
+ palError = CPalObjectBase::Initialize(pthr, poa);
+ if (NO_ERROR != palError)
+ {
+ goto InitializeExit;
+ }
+
+ //
+ // If this is a named object it needs to go into the shared domain;
+ // otherwise it remains local
+ //
+
+ if (0 != m_oa.sObjectName.GetStringLength())
+ {
+ m_ObjectDomain = SharedObject;
+
+ palError = AllocateSharedDataItems(&m_shmod, &psmod);
+ if (NO_ERROR != palError || NULL == psmod)
+ {
+ goto InitializeExit;
+ }
+ }
+
+ if (0 != m_pot->GetSharedDataSize())
+ {
+ if (SharedObject == m_ObjectDomain)
+ {
+ //
+ // Map the shared data into our address space
+ //
+ if (NULL == psmod)
+ {
+ ASSERT("psmod should not be NULL");
+ palError = ERROR_INTERNAL_ERROR;
+ goto InitializeExit;
+ }
+
+ m_pvSharedData = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjSharedData);
+ if (NULL == m_pvSharedData)
+ {
+ ASSERT("Unable to map shared data area\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto InitializeExit;
+ }
+ }
+ else
+ {
+ //
+ // Initialize the local shared data lock.
+ //
+
+ palError = m_sdlSharedData.Initialize();
+ if (NO_ERROR != palError)
+ {
+ ERROR("Failure initializing m_sdlSharedData\n");
+ goto InitializeExit;
+ }
+
+ //
+ // Allocate local memory to hold the shared data
+ //
+
+ m_pvSharedData = InternalMalloc(m_pot->GetSharedDataSize());
+ if (NULL == m_pvSharedData)
+ {
+ ERROR("Failure allocating m_pvSharedData (local copy)\n");
+ palError = ERROR_OUTOFMEMORY;
+ goto InitializeExit;
+ }
+ }
+
+ ZeroMemory(m_pvSharedData, m_pot->GetSharedDataSize());
+ }
+
+
+InitializeExit:
+
+ LOGEXIT("CSharedMemoryObject::Initalize returns %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::InitializeFromExistingSharedData
+
+ Performs possibly-failing initialization for a newly-constructed
+ object that is to represent an existing object (i.e., importing
+ a shared object into this process)
+
+ The shared memory lock must be held when calling this method
+
+Parameters:
+ pthr -- thread data for calling thread
+ poa -- the object attributes for the object
+--*/
+
+PAL_ERROR
+CSharedMemoryObject::InitializeFromExistingSharedData(
+ CPalThread *pthr,
+ CObjectAttributes *poa
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ SHMObjData *psmod = NULL;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != poa);
+
+ ENTRY("CSharedMemoryObject::InitializeFromExistingSharedData"
+ "(this = %p, pthr = %p, poa = %p)\n",
+ this,
+ pthr,
+ poa
+ );
+
+ //
+ // This object is obviously shared...
+ //
+
+ m_ObjectDomain = SharedObject;
+
+ _ASSERTE(SHMNULL != m_shmod);
+
+ psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, m_shmod);
+ if (NULL == psmod)
+ {
+ ASSERT("Unable to map shared object data\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto InitializeFromExistingSharedDataExit;
+ }
+
+ //
+ // When we're being called on the duplicate handle path the passed
+ // in object attributes likely won't have an object name in it.
+ // If there is an object name in the shared data place that in the
+ // object attributs so that the constructed object has a local copy
+ // of the name
+ //
+
+ if (0 == poa->sObjectName.GetStringLength()
+ && 0 != psmod->dwNameLength)
+ {
+ WCHAR *wsz;
+
+ wsz = SHMPTR_TO_TYPED_PTR(WCHAR, psmod->shmObjName);
+ if (NULL != wsz)
+ {
+ poa->sObjectName.SetStringWithLength(wsz, psmod->dwNameLength);
+ }
+ else
+ {
+ ASSERT("Unable to map object name\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto InitializeFromExistingSharedDataExit;
+ }
+ }
+#if _DEBUG
+ else if (0 != psmod->dwNameLength)
+ {
+ WCHAR *wsz;
+
+ //
+ // Verify that the names are consistent
+ //
+
+ wsz = SHMPTR_TO_TYPED_PTR(WCHAR, psmod->shmObjName);
+ _ASSERTE(NULL != wsz);
+ _ASSERTE(0 == PAL_wcscmp(wsz, poa->sObjectName.GetString()));
+ }
+#endif // debug
+
+ palError = CPalObjectBase::Initialize(pthr, poa);
+ if (NO_ERROR != palError)
+ {
+ goto InitializeFromExistingSharedDataExit;
+ }
+
+ if (SHMNULL != psmod->shmObjImmutableData)
+ {
+ VOID *pv = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjImmutableData);
+ if (NULL != pv)
+ {
+ memcpy(m_pvImmutableData, pv, m_pot->GetImmutableDataSize());
+ }
+ else
+ {
+ ASSERT("Unable to map object immutable data\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto InitializeFromExistingSharedDataExit;
+ }
+ }
+
+ if (SHMNULL != psmod->shmObjSharedData)
+ {
+ m_pvSharedData = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjSharedData);
+ if (NULL == m_pvSharedData)
+ {
+ ASSERT("Unable to map object shared data\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto InitializeFromExistingSharedDataExit;
+ }
+ }
+
+ if (NULL != m_pot->GetObjectInitRoutine())
+ {
+ palError = (*m_pot->GetObjectInitRoutine())(
+ pthr,
+ m_pot,
+ m_pvImmutableData,
+ m_pvSharedData,
+ m_pvLocalData
+ );
+ }
+
+InitializeFromExistingSharedDataExit:
+
+ LOGEXIT("CSharedMemoryObject::InitalizeFromExistingSharedData returns %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::AllocatedSharedDataItems
+
+ Allocates and initialiazes the shared memory structures necessary to make an
+ object available to other processes
+
+Parameters:
+ pshmObjData -- on success, receives the shared memory pointer for the
+ shared memory object data
+ ppsmod -- on success, receives the locally-mapped pointer for the shared
+ memory object data
+--*/
+
+PAL_ERROR
+CSharedMemoryObject::AllocateSharedDataItems(
+ SHMPTR *pshmObjData,
+ SHMObjData **ppsmod
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ SHMPTR shmod = SHMNULL;
+ SHMObjData *psmod = NULL;
+
+ _ASSERTE(NULL != pshmObjData);
+ _ASSERTE(NULL != ppsmod);
+
+ ENTRY("CSharedMemoryObject::AllocateSharedDataItems"
+ "(this = %p, pshmObjData = %p, ppsmod = %p)\n",
+ this,
+ pshmObjData,
+ ppsmod
+ );
+
+ //
+ // We're about to make a number of shared memory allocations,
+ // so grab the lock for the entirety of the routine.
+ //
+
+ SHMLock();
+
+ shmod = SHMalloc(sizeof(SHMObjData));
+ if (SHMNULL == shmod)
+ {
+ ERROR("Unable to allocate m_shmod for new object\n");
+ palError = ERROR_OUTOFMEMORY;
+ goto AllocateSharedDataItemsExit;
+ }
+
+ psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, shmod);
+ _ASSERTE(NULL != psmod);
+
+ ZeroMemory(psmod, sizeof(*psmod));
+
+ psmod->eTypeId = m_pot->GetId();
+ psmod->lProcessRefCount = 1;
+
+ if (0 != m_oa.sObjectName.GetStringLength())
+ {
+ psmod->dwNameLength = m_oa.sObjectName.GetStringLength();
+ psmod->shmObjName = SHMWStrDup(m_oa.sObjectName.GetString());
+ if (SHMNULL == psmod->shmObjName)
+ {
+ ERROR("Unable to allocate psmod->shmObjName for new object\n");
+ palError = ERROR_OUTOFMEMORY;
+ goto AllocateSharedDataItemsExit;
+ }
+ }
+
+ if (0 != m_pot->GetImmutableDataSize())
+ {
+ //
+ // The shared copy of the object's immutable data will be initialized
+ // by CSharedMemoryObjectManager::RegisterObject or PromoteSharedData
+ //
+
+ psmod->shmObjImmutableData = SHMalloc(m_pot->GetImmutableDataSize());
+ if (SHMNULL == psmod->shmObjImmutableData)
+ {
+ ERROR("Unable to allocate psmod->shmObjImmutableData for new object\n");
+ palError = ERROR_OUTOFMEMORY;
+ goto AllocateSharedDataItemsExit;
+ }
+ }
+
+ if (0 != m_pot->GetSharedDataSize())
+ {
+ psmod->shmObjSharedData = SHMalloc(m_pot->GetSharedDataSize());
+ if (SHMNULL == psmod->shmObjSharedData)
+ {
+ ERROR("Unable to allocate psmod->shmObjSharedData for new object\n");
+ palError = ERROR_OUTOFMEMORY;
+ goto AllocateSharedDataItemsExit;
+ }
+ }
+
+ *pshmObjData = shmod;
+ *ppsmod = psmod;
+
+AllocateSharedDataItemsExit:
+
+ if (NO_ERROR != palError && SHMNULL != shmod)
+ {
+ FreeSharedDataAreas(shmod);
+ }
+
+ SHMRelease();
+
+ LOGEXIT("CSharedMemoryObject::AllocateSharedDataItems returns %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::FreeSharedDataItems
+
+ Frees the shared memory structures referenced by the provided shared
+ memory pointer
+
+Parameters:
+ shmObjData -- shared memory pointer to the structures to free
+--*/
+
+// static
+void
+CSharedMemoryObject::FreeSharedDataAreas(
+ SHMPTR shmObjData
+ )
+{
+ SHMObjData *psmod;
+
+ _ASSERTE(SHMNULL != shmObjData);
+
+ ENTRY("CSharedMemoryObject::FreeSharedDataAreas"
+ "(shmObjData = %p)\n",
+ shmObjData
+ );
+
+ SHMLock();
+
+ psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, shmObjData);
+ _ASSERTE(NULL != psmod);
+
+ if (SHMNULL != psmod->shmObjImmutableData)
+ {
+ SHMfree(psmod->shmObjImmutableData);
+ }
+
+ if (SHMNULL != psmod->shmObjSharedData)
+ {
+ SHMfree(psmod->shmObjSharedData);
+ }
+
+ if (SHMNULL != psmod->shmObjName)
+ {
+ SHMfree(psmod->shmObjName);
+ }
+
+ SHMfree(shmObjData);
+
+ SHMRelease();
+
+ LOGEXIT("CSharedMemoryObject::FreeSharedDataAreas\n");
+}
+
+/*++
+Function:
+ CSharedMemoryObject::PromoteShjaredData
+
+ Copies the object's state into the passed-in shared data structures
+
+Parameters:
+ shmObjData -- shared memory pointer for the shared memory object data
+ psmod -- locally-mapped pointer for the shared memory object data
+--*/
+
+void
+CSharedMemoryObject::PromoteSharedData(
+ SHMPTR shmObjData,
+ SHMObjData *psmod
+ )
+{
+ _ASSERTE(SHMNULL != shmObjData);
+ _ASSERTE(NULL != psmod);
+
+ ENTRY("CSharedMemoryObject::PromoteSharedData"
+ "(this = %p, shmObjData = %p, psmod = %p)\n",
+ this,
+ shmObjData,
+ psmod);
+
+ //
+ // psmod has been zero-inited, so we don't need to worry about
+ // shmPrevObj, shmNextObj, fAddedToList, shmObjName, dwNameLength,
+ // or pvSynchData
+ //
+
+ psmod->lProcessRefCount = 1;
+ psmod->eTypeId = m_pot->GetId();
+
+ if (0 != m_pot->GetImmutableDataSize())
+ {
+ void *pvImmutableData;
+
+ pvImmutableData = SHMPTR_TO_TYPED_PTR(void, psmod->shmObjImmutableData);
+ _ASSERTE(NULL != pvImmutableData);
+
+ CopyMemory(
+ pvImmutableData,
+ m_pvImmutableData,
+ m_pot->GetImmutableDataSize()
+ );
+ }
+
+ if (0 != m_pot->GetSharedDataSize())
+ {
+ void *pvSharedData;
+
+ pvSharedData = SHMPTR_TO_TYPED_PTR(void, psmod->shmObjSharedData);
+ _ASSERTE(NULL != pvSharedData);
+
+ CopyMemory(
+ pvSharedData,
+ m_pvSharedData,
+ m_pot->GetSharedDataSize()
+ );
+
+ free(m_pvSharedData);
+ m_pvSharedData = pvSharedData;
+ }
+
+ m_shmod = shmObjData;
+
+ LOGEXIT("CSharedMemoryObject::PromoteSharedData\n");
+}
+
+/*++
+Function:
+ CSharedMemoryObject::EnsureObjectIsShared
+
+ If this object is not yet in the shared domain allocate the necessary
+ shared memory structures for it and copy the object's data into those
+ structures
+
+Parameters:
+ pthr -- thread data for the calling thread
+--*/
+
+PAL_ERROR
+CSharedMemoryObject::EnsureObjectIsShared(
+ CPalThread *pthr
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IDataLock *pDataLock = NULL;
+ SHMPTR shmObjData;
+ SHMObjData *psmod;
+
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("CSharedMemoryObject::EnsureObjectIsShared"
+ "(this = %p, pthr = %p)\n",
+ this,
+ pthr
+ );
+
+ //
+ // Grab the shared memory lock and check if the object is already
+ // shared
+ //
+
+ SHMLock();
+
+ if (SharedObject == m_ObjectDomain)
+ {
+ goto EnsureObjectIsSharedExit;
+ }
+
+ //
+ // Grab the local shared data lock, if necessary
+ //
+
+ if (0 != m_pot->GetSharedDataSize())
+ {
+ m_sdlSharedData.AcquireLock(pthr, &pDataLock);
+ }
+
+ //
+ // Allocate the necessary shared data areas
+ //
+
+ palError = AllocateSharedDataItems(&shmObjData, &psmod);
+ if (NO_ERROR != palError)
+ {
+ goto EnsureObjectIsSharedExit;
+ }
+
+ //
+ // Promote the object's data and set the domain to shared
+ //
+
+ PromoteSharedData(shmObjData, psmod);
+ m_ObjectDomain = SharedObject;
+
+EnsureObjectIsSharedExit:
+
+ if (NULL != pDataLock)
+ {
+ pDataLock->ReleaseLock(pthr, TRUE);
+ }
+
+ SHMRelease();
+
+ LOGEXIT("CSharedMemoryObject::EnsureObjectIsShared returns %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::CleanupForProcessShutdown
+
+ Cleanup routine called by the object manager when shutting down
+
+Parameters:
+ pthr -- thread data for the calling thread
+--*/
+
+void
+CSharedMemoryObject::CleanupForProcessShutdown(
+ CPalThread *pthr
+ )
+{
+ bool fCleanupSharedState;
+
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("CSharedMemoryObject::CleanupForProcessShutdown"
+ "(this = %p, pthr = %p)\n",
+ this,
+ pthr
+ );
+
+ fCleanupSharedState = DereferenceSharedData();
+
+ if (NULL != m_pot->GetObjectCleanupRoutine())
+ {
+ (*m_pot->GetObjectCleanupRoutine())(
+ pthr,
+ static_cast<IPalObject*>(this),
+ TRUE,
+ fCleanupSharedState
+ );
+ }
+
+ //
+ // We need to do two things with the calling thread data here:
+ // 1) store it in m_pthrCleanup so it is available to the destructors
+ // 2) Add a reference to it before starting any cleanup, and release
+ // that reference afterwords.
+ //
+ // Step 2 is necessary when we're cleaning up the thread object that
+ // represents the calling thread -- it ensures that the thread data
+ // is available throughout the entire cleanup process.
+ //
+
+ m_pthrCleanup = pthr;
+ pthr->AddThreadReference();
+
+ InternalDelete(this);
+
+ pthr->ReleaseThreadReference();
+
+ LOGEXIT("CSharedMemoryObject::CleanupForProcessShutdown\n");
+}
+
+/*++
+Function:
+ CSharedMemoryObject::AcquiteObjectDestructionLock
+
+ Acquires the lock that must be held when decrementing the object's
+ reference count (and, if the count drops to 0, while removing the
+ object from the object manager's lists).
+
+Parameters:
+ pthr -- thread data for the calling thread
+--*/
+
+void
+CSharedMemoryObject::AcquireObjectDestructionLock(
+ CPalThread *pthr
+ )
+{
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("CSharedMemoryObject::AcquireObjectDestructionLock"
+ "(this = %p, pthr = $p)\n",
+ this,
+ pthr
+ );
+
+ InternalEnterCriticalSection(pthr, m_pcsObjListLock);
+
+ LOGEXIT("CSharedMemoryObject::AcquireObjectDestructionLock\n");
+}
+
+/*++
+Function:
+ CSharedMemoryObject::ReleaseObjectDestructionLock
+
+ Releases the lock acquired by AcquireObjectDestructionLock
+
+Parameters:
+ pthr -- thread data for the calling thread
+ fDestructionPending -- if TRUE, the reference count for this
+ object has dropped to 0; the object will be destroyed after
+ this routine returns
+--*/
+
+bool
+CSharedMemoryObject::ReleaseObjectDestructionLock(
+ CPalThread *pthr,
+ bool fDestructionPending
+ )
+{
+ bool fCleanupSharedState = FALSE;
+
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("CSharedMemoryObject::ReleaseObjectDestructionLock"
+ "(this = %p, pthr = %p, fDestructionPending = %d\n",
+ this,
+ pthr,
+ fDestructionPending
+ );
+
+ if (fDestructionPending)
+ {
+ RemoveEntryList(&m_le);
+ fCleanupSharedState = DereferenceSharedData();
+ }
+
+ InternalLeaveCriticalSection(pthr, m_pcsObjListLock);
+
+ LOGEXIT("CSharedMemoryObject::ReleaseObjectDestructionLock returns %d\n",
+ fCleanupSharedState
+ );
+
+ return fCleanupSharedState;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::DereferenceSharedData
+
+ Called to decrement the global refcount (i.e., the count of
+ the number of processes that have reference to the object) when
+ the local reference to the object is being destroyed.
+
+Return value:
+ Returns TRUE if this process needs to clean up the object's shared
+ data (i.e., the global refcount has dropped to 0, or the object
+ is in the local domain)
+--*/
+
+bool
+CSharedMemoryObject::DereferenceSharedData()
+{
+ LONG fSharedDataAlreadDereferenced;
+
+ ENTRY("CSharedMemoryObject::DereferenceSharedData(this = %p)\n", this);
+
+ fSharedDataAlreadDereferenced = InterlockedExchange(
+ &m_fSharedDataDereferenced,
+ TRUE
+ );
+
+ if (!fSharedDataAlreadDereferenced)
+ {
+ if (SHMNULL != m_shmod)
+ {
+ SHMObjData *psmod;
+
+ SHMLock();
+
+ psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, m_shmod);
+ _ASSERTE(NULL != psmod);
+
+ psmod->lProcessRefCount -= 1;
+ if (0 == psmod->lProcessRefCount)
+ {
+ //
+ // No other process is using this object, so remove
+ // it from the shared memory named object list (if it
+ // had been added to it). The final cleanup will happen
+ // in the object's destructor
+ //
+
+ m_fDeleteSharedData = TRUE;
+
+ if (psmod->fAddedToList)
+ {
+ //
+ // This object better have a name...
+ //
+
+ _ASSERTE(0 != psmod->dwNameLength);
+
+ if (SHMNULL != psmod->shmPrevObj)
+ {
+ SHMObjData *psmodPrevious = SHMPTR_TO_TYPED_PTR(SHMObjData, psmod->shmPrevObj);
+ _ASSERTE(NULL != psmodPrevious);
+
+ psmodPrevious->shmNextObj = psmod->shmNextObj;
+ }
+ else
+ {
+ //
+ // This object is the head of the shared memory named object
+ // list -- reset that pointer now
+ //
+
+ if (!SHMSetInfo(SIID_NAMED_OBJECTS, psmod->shmNextObj))
+ {
+ ASSERT("Failed to set shared named object list head");
+ }
+ }
+
+ if (SHMNULL != psmod->shmNextObj)
+ {
+ SHMObjData *psmodNext = SHMPTR_TO_TYPED_PTR(SHMObjData, psmod->shmNextObj);
+ _ASSERTE(NULL != psmodNext);
+
+ psmodNext->shmPrevObj = psmod->shmPrevObj;
+ }
+ }
+#if _DEBUG
+ else
+ {
+ _ASSERTE(SHMNULL == psmod->shmPrevObj);
+ _ASSERTE(SHMNULL == psmod->shmNextObj);
+ }
+#endif
+ }
+
+ SHMRelease();
+ }
+ else if (ProcessLocalObject == m_ObjectDomain)
+ {
+ //
+ // If the object is local the shared data needs to be
+ // deleted by definition
+ //
+
+ m_fDeleteSharedData = TRUE;
+ }
+ }
+ else
+ {
+ ASSERT("Multiple calls to DereferenceSharedData\n");
+ }
+
+ LOGEXIT("CSharedMemoryObject::DereferenceSharedData returns %d\n",
+ m_fDeleteSharedData
+ );
+
+ return m_fDeleteSharedData;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::~CSharedMemoryObject
+
+ Destructor; should only be called from ReleaseReference
+--*/
+
+CSharedMemoryObject::~CSharedMemoryObject()
+{
+ ENTRY("CSharedMemoryObject::~CSharedMemoryObject(this = %p)\n", this);
+
+ if (!m_fSharedDataDereferenced)
+ {
+ ASSERT("DereferenceSharedData not called before object destructor -- delete called directly?\n");
+ DereferenceSharedData();
+ }
+
+ if (NULL != m_pvSharedData && ProcessLocalObject == m_ObjectDomain)
+ {
+ free(m_pvSharedData);
+ }
+ else if (SHMNULL != m_shmod && m_fDeleteSharedData)
+ {
+ FreeSharedDataAreas(m_shmod);
+ }
+
+ LOGEXIT("CSharedMemoryObject::~CSharedMemoryObject\n");
+}
+
+//
+// C++ standard, 18.1.5 - offsetof requires a POD (plain old data) struct or
+// union. Since offsetof is a macro, gcc doesn't actually check for improper
+// use of offsetof, it keys off of the -> from NULL (which is also invalid for
+// non-POD types by 18.1.5)
+//
+// As we have numerous examples of this behavior in our codebase,
+// making an offsetof which doesn't use 0.
+//
+// PAL_safe_offsetof is a version of offsetof that protects against an
+// overridden operator&
+//
+
+#define PAL_safe_offsetof(s,m) ((size_t)((ptrdiff_t)&(char&)(((s *)64)->m))-64)
+
+/*++
+Function:
+ CSharedMemoryObject::GetObjectFromListLink
+
+ Given a list link returns the object that contains it. Since m_le is
+ protected the caller cannot perform this computation directly
+
+Parameters:
+ ple -- the list entry to obtain the object for
+--*/
+
+// static
+CSharedMemoryObject*
+CSharedMemoryObject::GetObjectFromListLink(PLIST_ENTRY ple)
+{
+ CSharedMemoryObject *pshmo;
+
+ _ASSERTE(NULL != ple);
+
+ ENTRY("CSharedMemoryObject::GetObjectFromListLink(ple = %p)\n", ple);
+
+ //
+ // Ideally we'd use CONTAINING_RECORD here, but it uses offsetof (see above
+ // comment
+ //
+
+ pshmo = reinterpret_cast<CSharedMemoryObject*>(
+ reinterpret_cast<size_t>(ple) - PAL_safe_offsetof(CSharedMemoryObject, m_le)
+ );
+
+ _ASSERTE(ple == &pshmo->m_le);
+
+ LOGEXIT("CSharedMemoryObject::GetObjectFromListLink returns %p\n", pshmo);
+
+ return pshmo;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::GetSharedData
+
+ Provides the caller access to the object's shared data (if any)
+
+Parameters:
+ pthr -- thread data for calling thread
+ eLockRequest -- specifies if the caller desires a read lock or a
+ write lock on the data (currently ignored)
+ ppDataLock -- on success, receives a pointer to the data lock instance
+ for the shared data
+ ppvProcssSharedData -- on success, receives a pointer to the shared data
+--*/
+
+PAL_ERROR
+CSharedMemoryObject::GetSharedData(
+ CPalThread *pthr,
+ LockType eLockRequest,
+ IDataLock **ppDataLock, // OUT
+ void **ppvSharedData // OUT
+ )
+{
+ IDataLock *pDataLock;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(ReadLock == eLockRequest || WriteLock == eLockRequest);
+ _ASSERTE(NULL != ppDataLock);
+ _ASSERTE(NULL != ppvSharedData);
+
+ ENTRY("CSharedMemoryObject::GetSharedData"
+ "(this = %p, pthr = %p, eLockRequest = %d, ppDataLock = %p,"
+ " ppvSharedData = %p)\n",
+ this,
+ pthr,
+ eLockRequest,
+ ppDataLock,
+ ppvSharedData
+ );
+
+ _ASSERTE(0 < m_pot->GetSharedDataSize());
+
+ if (ProcessLocalObject == m_ObjectDomain)
+ {
+ //
+ // We need to grab the local shared data lock and re-check
+ // the object's domain, as there's a chance the object might
+ // have been promoted after we made the above check but before
+ // we grabbed the lock
+ //
+
+ m_sdlSharedData.AcquireLock(pthr, &pDataLock);
+
+ if (SharedObject == m_ObjectDomain)
+ {
+ pDataLock->ReleaseLock(pthr, FALSE);
+ m_ssmlSharedData.AcquireLock(pthr, &pDataLock);
+ }
+ }
+ else
+ {
+ //
+ // A shared object can never transition back to local,
+ // so there's no need to recheck the domain on this path
+ //
+
+ m_ssmlSharedData.AcquireLock(pthr, &pDataLock);
+ }
+
+ *ppDataLock = pDataLock;
+ *ppvSharedData = m_pvSharedData;
+
+ LOGEXIT("CSharedMemoryObject::GetSharedData returns %d\n", NO_ERROR);
+
+ return NO_ERROR;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::GetSynchStateController
+
+ Obtain a synchronization state controller for this object. Should
+ never be called.
+
+Parameters:
+ pthr -- thread data for calling thread
+ ppStateController -- on success, receives a pointer to the state controller
+ instance
+--*/
+
+PAL_ERROR
+CSharedMemoryObject::GetSynchStateController(
+ CPalThread *pthr,
+ ISynchStateController **ppStateController // OUT
+ )
+{
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != ppStateController);
+
+ //
+ // This is not a waitable object!
+ //
+
+ ASSERT("Attempt to obtain a synch state controller on a non-waitable object\n");
+ return ERROR_INVALID_HANDLE;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::GetSynchWaitController
+
+ Obtain a synchronization wait controller for this object. Should
+ never be called.
+
+Parameters:
+ pthr -- thread data for calling thread
+ ppWaitController -- on success, receives a pointer to the wait controller
+ instance
+--*/
+
+PAL_ERROR
+CSharedMemoryObject::GetSynchWaitController(
+ CPalThread *pthr,
+ ISynchWaitController **ppWaitController // OUT
+ )
+{
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != ppWaitController);
+
+ //
+ // This is not a waitable object!!!
+ //
+
+ ASSERT("Attempt to obtain a synch wait controller on a non-waitable object\n");
+ return ERROR_INVALID_HANDLE;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::GetObjectDomain
+
+ Returns the object's domain (local or shared)
+
+--*/
+
+ObjectDomain
+CSharedMemoryObject::GetObjectDomain(
+ void
+ )
+{
+ TRACE("CSharedMemoryObject::GetObjectDomain(this = %p)\n", this);
+ LOGEXIT("CSharedMemoryObject::GetObjectDomain returns %d\n", m_ObjectDomain);
+
+ return m_ObjectDomain;
+}
+
+/*++
+Function:
+ CSharedMemoryObject::GetObjectSynchData
+
+ Obtain the synchronization data for this object. Should
+ never be called.
+
+Parameters:
+ ppvSynchData -- on success, receives a pointer to the object's synch data
+--*/
+
+PAL_ERROR
+CSharedMemoryObject::GetObjectSynchData(
+ VOID **ppvSynchData // OUT
+ )
+{
+ _ASSERTE(NULL != ppvSynchData);
+
+ //
+ // This is not a waitable object!!!
+ //
+
+ 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::EnsureObjectIsShared
+
+ If this object is not yet in the shared domain allocate the necessary
+ shared memory structures for it and copy the object's data into those
+ structures
+
+Parameters:
+ pthr -- thread data for the calling thread
+--*/
+
+PAL_ERROR
+CSharedMemoryWaitableObject::EnsureObjectIsShared(
+ CPalThread *pthr
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IDataLock *pDataLock = NULL;
+ SHMPTR shmObjData = SHMNULL;
+ SHMObjData *psmod;
+ VOID *pvSharedSynchData;
+
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("CSharedMemoryWaitableObject::EnsureObjectIsShared"
+ "(this = %p, pthr = %p)\n",
+ this,
+ pthr
+ );
+
+ //
+ // First, grab the process synchronization lock and check
+ // if the object is already shared
+ //
+
+ g_pSynchronizationManager->AcquireProcessLock(pthr);
+
+ if (SharedObject == m_ObjectDomain)
+ {
+ goto EnsureObjectIsSharedExitNoSHMLockRelease;
+ }
+
+ //
+ // Grab the necessary locks
+ //
+
+ SHMLock();
+
+ if (0 != m_pot->GetSharedDataSize())
+ {
+ m_sdlSharedData.AcquireLock(pthr, &pDataLock);
+ }
+
+ //
+ // Allocate the necessary shared data areas
+ //
+
+ palError = AllocateSharedDataItems(&shmObjData, &psmod);
+ if (NO_ERROR != palError)
+ {
+ goto EnsureObjectIsSharedExit;
+ }
+
+ //
+ // Promote the object's synchronization data
+ //
+
+ palError = g_pSynchronizationManager->PromoteObjectSynchData(
+ pthr,
+ m_pvSynchData,
+ &pvSharedSynchData
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto EnsureObjectIsSharedExit;
+ }
+
+ m_pvSynchData = pvSharedSynchData;
+ psmod->pvSynchData = pvSharedSynchData;
+
+ //
+ // Promote the object's data and set the domain to shared
+ //
+
+ PromoteSharedData(shmObjData, psmod);
+ m_ObjectDomain = SharedObject;
+
+EnsureObjectIsSharedExit:
+
+ if (NULL != pDataLock)
+ {
+ pDataLock->ReleaseLock(pthr, TRUE);
+ }
+
+ SHMRelease();
+
+EnsureObjectIsSharedExitNoSHMLockRelease:
+
+ g_pSynchronizationManager->ReleaseProcessLock(pthr);
+
+ if (NO_ERROR != palError && SHMNULL != shmObjData)
+ {
+ //
+ // Since shmObjdData is local to this function there's no
+ // need to continue to hold the promotion locks when
+ // freeing the allocated data on error
+ //
+
+ FreeSharedDataAreas(shmObjData);
+ }
+
+ LOGEXIT("CSharedMemoryWaitableObject::EnsureObjectIsShared 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
new file mode 100644
index 0000000000..addfda52cc
--- /dev/null
+++ b/src/pal/src/objmgr/shmobject.hpp
@@ -0,0 +1,391 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ shmobject.hpp
+
+Abstract:
+ Shared memory based object
+
+
+
+--*/
+
+#ifndef _PAL_SHMOBJECT_HPP
+#define _PAL_SHMOBJECT_HPP
+
+#include "palobjbase.hpp"
+#include "pal/shm.hpp"
+
+extern "C"
+{
+#include "pal/list.h"
+}
+
+namespace CorUnix
+{
+ class CSimpleSharedMemoryLock : public IDataLock
+ {
+ public:
+
+ void
+ AcquireLock(
+ CPalThread *pthr,
+ IDataLock **ppDataLock
+ )
+ {
+ SHMLock();
+ *ppDataLock = static_cast<IDataLock*>(this);
+ };
+
+ virtual
+ void
+ ReleaseLock(
+ CPalThread *pthr,
+ bool fDataChanged
+ )
+ {
+ SHMRelease();
+ };
+ };
+
+ typedef struct _SHMObjData
+ {
+ SHMPTR shmPrevObj;
+ SHMPTR shmNextObj;
+ BOOL fAddedToList;
+
+ SHMPTR shmObjName;
+ SHMPTR shmObjImmutableData;
+ SHMPTR shmObjSharedData;
+
+ LONG lProcessRefCount;
+ DWORD dwNameLength;
+
+ PalObjectTypeId eTypeId;
+
+ PVOID pvSynchData;
+ } SHMObjData;
+
+ class CSharedMemoryObject : public CPalObjectBase
+ {
+ template <class T> friend void InternalDelete(T *p);
+
+ protected:
+
+ //
+ // Entry on the process's named or anonymous object list
+ //
+
+ LIST_ENTRY m_le;
+
+ //
+ // The lock that guards access to that list
+ //
+
+ CRITICAL_SECTION *m_pcsObjListLock;
+
+ //
+ // The SHMObjData for this object, protected by the
+ // shared memory lock.
+ //
+
+ SHMPTR m_shmod;
+
+ //
+ // The shared data (i.e., m_shmObjData->shmObjSharedData)
+ // for this object, mapped into this process. This will be
+ // NULL if m_pot->dwSharedDataSize is 0. Access to this data
+ // is controlled by m_ssmlSharedData when m_ObjectDomain is
+ // SharedObject, and m_sdlSharedData when it is ProcessLocalObject.
+ //
+
+ VOID *m_pvSharedData;
+
+ CSimpleSharedMemoryLock m_ssmlSharedData;
+ CSimpleDataLock m_sdlSharedData;
+
+ //
+ // Is this object process local or shared?
+ //
+
+ ObjectDomain m_ObjectDomain;
+
+ //
+ // m_fSharedDataDereferenced will be TRUE if DereferenceSharedData
+ // has already been called. (N.B. -- this is a LONG instead of a bool
+ // because it is passed to InterlockedExchange). If the shared data blob
+ // should be freed in the object's destructor (i.e., SHMfree should be
+ // called on the appropriate SHMPTRs) DereferenceSharedData will
+ // set m_fDeleteSharedData to TRUE.
+ //
+
+ LONG m_fSharedDataDereferenced;
+ LONG m_fDeleteSharedData;
+
+ PAL_ERROR
+ AllocateSharedDataItems(
+ SHMPTR *pshmObjData,
+ SHMObjData **ppsmod
+ );
+
+ static
+ void
+ FreeSharedDataAreas(
+ SHMPTR shmObjData
+ );
+
+ void
+ PromoteSharedData(
+ SHMPTR shmObjData,
+ SHMObjData *psmod
+ );
+
+ bool
+ DereferenceSharedData();
+
+ virtual
+ void
+ AcquireObjectDestructionLock(
+ CPalThread *pthr
+ );
+
+ virtual
+ bool
+ ReleaseObjectDestructionLock(
+ CPalThread *pthr,
+ bool fDestructionPending
+ );
+
+ virtual ~CSharedMemoryObject();
+
+ public:
+
+ //
+ // Constructor used for new object
+ //
+
+ CSharedMemoryObject(
+ CObjectType *pot,
+ CRITICAL_SECTION *pcsObjListLock
+ )
+ :
+ CPalObjectBase(pot),
+ m_pcsObjListLock(pcsObjListLock),
+ m_shmod(SHMNULL),
+ m_pvSharedData(NULL),
+ m_ObjectDomain(ProcessLocalObject),
+ m_fSharedDataDereferenced(FALSE),
+ m_fDeleteSharedData(FALSE)
+ {
+ InitializeListHead(&m_le);
+ };
+
+ //
+ // Constructor used to import a shared object into this process. The
+ // shared memory lock must be held when calling this contstructor
+ //
+
+ CSharedMemoryObject(
+ CObjectType *pot,
+ CRITICAL_SECTION *pcsObjListLock,
+ SHMPTR shmSharedObjectData,
+ SHMObjData *psmod,
+ bool fAddRefSharedData
+ )
+ :
+ CPalObjectBase(pot),
+ m_pcsObjListLock(pcsObjListLock),
+ m_shmod(shmSharedObjectData),
+ m_pvSharedData(NULL),
+ m_ObjectDomain(SharedObject),
+ m_fSharedDataDereferenced(FALSE),
+ m_fDeleteSharedData(FALSE)
+ {
+ InitializeListHead(&m_le);
+ if (fAddRefSharedData)
+ {
+ psmod->lProcessRefCount += 1;
+ }
+ };
+
+ virtual
+ PAL_ERROR
+ Initialize(
+ CPalThread *pthr,
+ CObjectAttributes *poa
+ );
+
+ virtual
+ PAL_ERROR
+ InitializeFromExistingSharedData(
+ CPalThread *pthr,
+ CObjectAttributes *poa
+ );
+
+ virtual
+ PAL_ERROR
+ EnsureObjectIsShared(
+ CPalThread *pthr
+ );
+
+ void
+ CleanupForProcessShutdown(
+ CPalThread *pthr
+ );
+
+ SHMPTR
+ GetShmObjData(
+ void
+ )
+ {
+ return m_shmod;
+ };
+
+ PLIST_ENTRY
+ GetObjectListLink(
+ void
+ )
+ {
+ return &m_le;
+ }
+
+ //
+ // Clients of this object -- in particular, CSharedMemoryObjectManager
+ // -- can't use CONTAINING_RECORD directly, since they don't have
+ // access to m_Link.
+ //
+
+ static
+ CSharedMemoryObject*
+ GetObjectFromListLink(PLIST_ENTRY pLink);
+
+ //
+ // IPalObject routines
+ //
+
+ virtual
+ PAL_ERROR
+ GetSharedData(
+ CPalThread *pthr,
+ LockType eLockRequest,
+ IDataLock **ppDataLock,
+ void **ppvSharedData
+ );
+
+ virtual
+ PAL_ERROR
+ GetSynchStateController(
+ CPalThread *pthr,
+ ISynchStateController **ppStateController
+ );
+
+ virtual
+ PAL_ERROR
+ GetSynchWaitController(
+ CPalThread *pthr,
+ ISynchWaitController **ppWaitController
+ );
+
+ virtual
+ ObjectDomain
+ GetObjectDomain(
+ void
+ );
+
+ virtual
+ PAL_ERROR
+ GetObjectSynchData(
+ VOID **ppvSynchData
+ );
+
+ };
+
+ class CSharedMemoryWaitableObject : public CSharedMemoryObject
+ {
+ template <class T> friend void InternalDelete(T *p);
+
+ protected:
+
+ VOID *m_pvSynchData;
+
+ virtual ~CSharedMemoryWaitableObject();
+
+ public:
+
+ CSharedMemoryWaitableObject(
+ CObjectType *pot,
+ CRITICAL_SECTION *pcsObjListLock
+ )
+ :
+ CSharedMemoryObject(pot, pcsObjListLock),
+ m_pvSynchData(NULL)
+ {
+ };
+
+ //
+ // Constructor used to import a shared object into this process. The
+ // shared memory lock must be held when calling this contstructor
+ //
+
+ CSharedMemoryWaitableObject(
+ CObjectType *pot,
+ CRITICAL_SECTION *pcsObjListLock,
+ SHMPTR shmSharedObjectData,
+ SHMObjData *psmod,
+ bool fAddRefSharedData
+ )
+ :
+ CSharedMemoryObject(pot, pcsObjListLock, shmSharedObjectData, psmod, fAddRefSharedData),
+ m_pvSynchData(psmod->pvSynchData)
+ {
+ };
+
+ virtual
+ PAL_ERROR
+ Initialize(
+ CPalThread *pthr,
+ CObjectAttributes *poa
+ );
+
+ virtual
+ PAL_ERROR
+ EnsureObjectIsShared(
+ CPalThread *pthr
+ );
+
+ //
+ // 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
new file mode 100644
index 0000000000..42754216f1
--- /dev/null
+++ b/src/pal/src/objmgr/shmobjectmanager.cpp
@@ -0,0 +1,1567 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ shmobjectmgr.cpp
+
+Abstract:
+ Shared memory based object manager
+
+
+
+--*/
+
+#include "shmobjectmanager.hpp"
+#include "shmobject.hpp"
+#include "pal/cs.hpp"
+#include "pal/thread.hpp"
+#include "pal/procobj.hpp"
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(PAL);
+
+#include "pal/corunix.inl"
+
+using namespace CorUnix;
+
+IPalObjectManager * CorUnix::g_pObjectManager;
+
+static
+PAL_ERROR
+CheckObjectTypeAndRights(
+ IPalObject *pobj,
+ CAllowedObjectTypes *paot,
+ DWORD dwRightsGranted,
+ DWORD dwRightsRequired
+ );
+
+/*++
+Function:
+ CSharedMemoryObjectManager::Initialize
+
+ Performs (possibly failing) startup tasks for the object manager
+
+Parameters:
+ None
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::Initialize(
+ void
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ ENTRY("CSharedMemoryObjectManager::Initialize (this=%p)\n", this);
+
+ InitializeListHead(&m_leNamedObjects);
+ InitializeListHead(&m_leAnonymousObjects);
+
+ InternalInitializeCriticalSection(&m_csListLock);
+ m_fListLockInitialized = TRUE;
+
+ palError = m_HandleManager.Initialize();
+
+ LOGEXIT("CSharedMemoryObjectManager::Initialize returns %d", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::Shutdown
+
+ Cleans up the object manager. This routine will call cleanup routines
+ for all objects referenced by this process. After this routine is called
+ no attempt should be made to access an IPalObject.
+
+Parameters:
+ pthr -- thread data for calling thread
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::Shutdown(
+ CPalThread *pthr
+ )
+{
+ PLIST_ENTRY ple;
+ CSharedMemoryObject *pshmobj;
+
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("CSharedMemoryObjectManager::Shutdown (this=%p, pthr=%p)\n",
+ this,
+ pthr
+ );
+
+ InternalEnterCriticalSection(pthr, &m_csListLock);
+ SHMLock();
+
+ while (!IsListEmpty(&m_leAnonymousObjects))
+ {
+ ple = RemoveTailList(&m_leAnonymousObjects);
+ pshmobj = CSharedMemoryObject::GetObjectFromListLink(ple);
+ pshmobj->CleanupForProcessShutdown(pthr);
+ }
+
+ while (!IsListEmpty(&m_leNamedObjects))
+ {
+ ple = RemoveTailList(&m_leNamedObjects);
+ pshmobj = CSharedMemoryObject::GetObjectFromListLink(ple);
+ pshmobj->CleanupForProcessShutdown(pthr);
+ }
+
+ SHMRelease();
+ InternalLeaveCriticalSection(pthr, &m_csListLock);
+
+ LOGEXIT("CSharedMemoryObjectManager::Shutdown returns %d\n", NO_ERROR);
+
+ return NO_ERROR;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::AllocateObject
+
+ Allocates a new object instance of the specified type.
+
+Parameters:
+ pthr -- thread data for calling thread
+ pot -- type of object to allocate
+ poa -- attributes (name and SD) of object to allocate
+ ppobjNew -- on success, receives a reference to the new object
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::AllocateObject(
+ CPalThread *pthr,
+ CObjectType *pot,
+ CObjectAttributes *poa,
+ IPalObject **ppobjNew // OUT
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CSharedMemoryObject *pshmobj = NULL;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != pot);
+ _ASSERTE(NULL != poa);
+ _ASSERTE(NULL != ppobjNew);
+
+ ENTRY("CSharedMemoryObjectManager::AllocateObject "
+ "(this=%p, pthr=%p, pot=%p, poa=%p, ppobjNew=%p)\n",
+ this,
+ pthr,
+ pot,
+ poa,
+ ppobjNew
+ );
+
+ if (CObjectType::WaitableObject == pot->GetSynchronizationSupport())
+ {
+ pshmobj = InternalNew<CSharedMemoryWaitableObject>(pot, &m_csListLock);
+ }
+ else
+ {
+ pshmobj = InternalNew<CSharedMemoryObject>(pot, &m_csListLock);
+ }
+
+ if (NULL != pshmobj)
+ {
+ palError = pshmobj->Initialize(pthr, poa);
+ if (NO_ERROR == palError)
+ {
+ *ppobjNew = static_cast<IPalObject*>(pshmobj);
+ }
+ }
+ else
+ {
+ ERROR("Unable to allocate pshmobj\n");
+ palError = ERROR_OUTOFMEMORY;
+ }
+
+ LOGEXIT("CSharedMemoryObjectManager::AllocateObject returns %d\n", palError);
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::RegisterObject
+
+ Registers a newly-allocated object instance. If the object to be registered
+ has a name, and a previously registered object has the same name the new
+ object will not be registered.
+
+Distinguished return values:
+ ERROR_ALREADY_EXISTS -- an object of a compatible type was already registered
+ with the specified name
+ ERROR_INVALID_HANDLE -- an object of an incompatible type was already
+ registered with the specified name
+
+Parameters:
+ pthr -- thread data for calling thread
+ pobjToRegister -- the object instance to register. This routine will always
+ call ReleaseReference on this instance
+ paot -- object types that are compatible with the new object instance
+ dwRightsRequested -- requested access rights for the returned handle (ignored)
+ pHandle -- on success, receives a handle to the registered object
+ ppobjRegistered -- on success, receives a reference to the registered object
+ instance.
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::RegisterObject(
+ CPalThread *pthr,
+ IPalObject *pobjToRegister,
+ CAllowedObjectTypes *paot,
+ DWORD dwRightsRequested,
+ HANDLE *pHandle, // OUT
+ IPalObject **ppobjRegistered // OUT
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CSharedMemoryObject *pshmobj = static_cast<CSharedMemoryObject*>(pobjToRegister);
+ SHMObjData *psmodNew = NULL;
+ CObjectAttributes *poa;
+ CObjectType *potObj;
+ IPalObject *pobjExisting;
+ BOOL fInherit = FALSE;
+ BOOL fShared = FALSE;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != pobjToRegister);
+ _ASSERTE(NULL != paot);
+ _ASSERTE(NULL != pHandle);
+ _ASSERTE(NULL != ppobjRegistered);
+
+ ENTRY("CSharedMemoryObjectManager::RegisterObject "
+ "(this=%p, pthr=%p, pobjToRegister=%p, paot=%p, "
+ "dwRightsRequested=%d, pHandle=%p, ppobjRegistered=%p)\n",
+ this,
+ pthr,
+ pobjToRegister,
+ paot,
+ dwRightsRequested,
+ pHandle,
+ ppobjRegistered
+ );
+
+ poa = pobjToRegister->GetObjectAttributes();
+ _ASSERTE(NULL != poa);
+
+ if (NULL != poa->pSecurityAttributes)
+ {
+ fInherit = poa->pSecurityAttributes->bInheritHandle;
+ }
+
+ potObj = pobjToRegister->GetObjectType();
+ fShared = (SharedObject == pshmobj->GetObjectDomain());
+
+ InternalEnterCriticalSection(pthr, &m_csListLock);
+
+ if (fShared)
+ {
+ //
+ // We only need to acquire the shared memory lock if this
+ // object is actually shared.
+ //
+
+ SHMLock();
+ }
+
+ if (0 != poa->sObjectName.GetStringLength())
+ {
+ SHMPTR shmObjectListHead = SHMNULL;
+
+ //
+ // The object must be shared
+ //
+
+ _ASSERTE(fShared);
+
+ //
+ // Check if an object by this name alredy exists
+ //
+
+ palError = LocateObject(
+ pthr,
+ &poa->sObjectName,
+ paot,
+ &pobjExisting
+ );
+
+ if (NO_ERROR == palError)
+ {
+ //
+ // Obtain a new handle to the existing object
+ //
+
+ palError = ObtainHandleForObject(
+ pthr,
+ pobjExisting,
+ dwRightsRequested,
+ fInherit,
+ NULL,
+ pHandle
+ );
+
+ if (NO_ERROR == palError)
+ {
+ //
+ // Transfer object reference to out param
+ //
+
+ *ppobjRegistered = pobjExisting;
+ palError = ERROR_ALREADY_EXISTS;
+ }
+ else
+ {
+ pobjExisting->ReleaseReference(pthr);
+ }
+
+ goto RegisterObjectExit;
+ }
+ else if (ERROR_INVALID_NAME != palError)
+ {
+ //
+ // Something different than an object not found error
+ // occurred. This is most likely due to a type conflict.
+ //
+
+ goto RegisterObjectExit;
+ }
+
+ //
+ // Insert the object on the named object lists
+ //
+
+ InsertTailList(&m_leNamedObjects, pshmobj->GetObjectListLink());
+
+ psmodNew = SHMPTR_TO_TYPED_PTR(SHMObjData, pshmobj->GetShmObjData());
+ if (NULL == psmodNew)
+ {
+ ASSERT("Failure to map shared object data\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto RegisterObjectExit;
+ }
+
+ shmObjectListHead = SHMGetInfo(SIID_NAMED_OBJECTS);
+ if (SHMNULL != shmObjectListHead)
+ {
+ SHMObjData *psmodListHead;
+
+ psmodListHead = SHMPTR_TO_TYPED_PTR(SHMObjData, shmObjectListHead);
+ if (NULL != psmodListHead)
+ {
+ psmodNew->shmNextObj = shmObjectListHead;
+ psmodListHead->shmPrevObj = pshmobj->GetShmObjData();
+ }
+ else
+ {
+ ASSERT("Failure to map shared object data\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto RegisterObjectExit;
+ }
+ }
+
+ psmodNew->fAddedToList = TRUE;
+
+ if (!SHMSetInfo(SIID_NAMED_OBJECTS, pshmobj->GetShmObjData()))
+ {
+ ASSERT("Failed to set shared named object list head\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto RegisterObjectExit;
+ }
+ }
+ else
+ {
+ //
+ // Place the object on the anonymous object list
+ //
+
+ InsertTailList(&m_leAnonymousObjects, pshmobj->GetObjectListLink());
+ }
+
+ //
+ // Hoist the object's immutable data (if any) into shared memory if
+ // the object is shared
+ //
+
+ if (fShared && 0 != potObj->GetImmutableDataSize())
+ {
+ VOID *pvImmutableData;
+ SHMObjData *psmod;
+
+ palError = pobjToRegister->GetImmutableData(&pvImmutableData);
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Failure to obtain object immutable data\n");
+ goto RegisterObjectExit;
+ }
+
+ psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, pshmobj->GetShmObjData());
+ if (NULL != psmod)
+ {
+ VOID *pvSharedImmutableData =
+ SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjImmutableData);
+
+ if (NULL != pvSharedImmutableData)
+ {
+ CopyMemory(
+ pvSharedImmutableData,
+ pvImmutableData,
+ potObj->GetImmutableDataSize()
+ );
+ }
+ else
+ {
+ ASSERT("Failure to map psmod->shmObjImmutableData\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto RegisterObjectExit;
+ }
+ }
+ else
+ {
+ ASSERT("Failure to map pshmobj->GetShmObjData()\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto RegisterObjectExit;
+ }
+ }
+
+ //
+ // Obtain a handle for the new object
+ //
+
+ palError = ObtainHandleForObject(
+ pthr,
+ pobjToRegister,
+ dwRightsRequested,
+ fInherit,
+ NULL,
+ pHandle
+ );
+
+ if (NO_ERROR == palError)
+ {
+ //
+ // Transfer pobjToRegister reference to out param
+ //
+
+ *ppobjRegistered = pobjToRegister;
+ pobjToRegister = NULL;
+ }
+
+RegisterObjectExit:
+
+ if (fShared)
+ {
+ SHMRelease();
+ }
+
+ InternalLeaveCriticalSection(pthr, &m_csListLock);
+
+ if (NULL != pobjToRegister)
+ {
+ pobjToRegister->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("CSharedMemoryObjectManager::RegisterObject return %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::LocateObject
+
+ Search for a previously registered object with a give name and type
+
+Distinguished return values:
+ ERROR_INVALID_NAME -- no object with the specified name was previously
+ registered
+ ERROR_INVALID_HANDLE -- an object with the specified name was previously
+ registered, but its type is not compatible
+
+Parameters:
+ pthr -- thread data for calling thread
+ psObjectToLocate -- the name of the object to locate
+ paot -- acceptable types for the object
+ ppobj -- on success, receives a reference to the object instance
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::LocateObject(
+ CPalThread *pthr,
+ CPalString *psObjectToLocate,
+ CAllowedObjectTypes *paot,
+ IPalObject **ppobj // OUT
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjExisting = NULL;
+ SHMPTR shmSharedObjectData = SHMNULL;
+ SHMPTR shmObjectListEntry = SHMNULL;
+ SHMObjData *psmod = NULL;
+ LPWSTR pwsz = NULL;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != psObjectToLocate);
+ _ASSERTE(NULL != psObjectToLocate->GetString());
+ _ASSERTE(PAL_wcslen(psObjectToLocate->GetString()) == psObjectToLocate->GetStringLength());
+ _ASSERTE(NULL != ppobj);
+
+ ENTRY("CSharedMemoryObjectManager::LocateObject "
+ "(this=%p, pthr=%p, psObjectToLocate=%p, paot=%p, "
+ "ppobj=%p)\n",
+ this,
+ pthr,
+ psObjectToLocate,
+ paot,
+ ppobj
+ );
+
+ TRACE("Searching for object name %S\n", psObjectToLocate->GetString());
+
+ InternalEnterCriticalSection(pthr, &m_csListLock);
+
+ //
+ // Search the local named object list for this object
+ //
+
+ for (PLIST_ENTRY ple = m_leNamedObjects.Flink;
+ ple != &m_leNamedObjects;
+ ple = ple->Flink)
+ {
+ CObjectAttributes *poa;
+ CSharedMemoryObject *pshmobj =
+ CSharedMemoryObject::GetObjectFromListLink(ple);
+
+ poa = pshmobj->GetObjectAttributes();
+ _ASSERTE(NULL != poa);
+
+ if (poa->sObjectName.GetStringLength() != psObjectToLocate->GetStringLength())
+ {
+ continue;
+ }
+
+ if (0 != PAL_wcscmp(poa->sObjectName.GetString(), psObjectToLocate->GetString()))
+ {
+ continue;
+ }
+
+ //
+ // This object has the name we're looking for
+ //
+
+ pobjExisting = static_cast<IPalObject*>(pshmobj);
+ break;
+ }
+
+ if (NULL != pobjExisting)
+ {
+ //
+ // Validate the located object's type
+ //
+
+ if (paot->IsTypeAllowed(
+ pobjExisting->GetObjectType()->GetId()
+ ))
+ {
+ TRACE("Local object exists with compatible type\n");
+
+ //
+ // Add a reference to the found object
+ //
+
+ pobjExisting->AddReference();
+ *ppobj = pobjExisting;
+ }
+ else
+ {
+ TRACE("Local object exists w/ incompatible type\n");
+ palError = ERROR_INVALID_HANDLE;
+ }
+
+ goto LocateObjectExit;
+ }
+
+ //
+ // Search the shared memory named object list for a matching object
+ //
+
+ SHMLock();
+
+ shmObjectListEntry = SHMGetInfo(SIID_NAMED_OBJECTS);
+ while (SHMNULL != shmObjectListEntry)
+ {
+ psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, shmObjectListEntry);
+ if (NULL != psmod)
+ {
+ if (psmod->dwNameLength == psObjectToLocate->GetStringLength())
+ {
+ pwsz = SHMPTR_TO_TYPED_PTR(WCHAR, psmod->shmObjName);
+ if (NULL != pwsz)
+ {
+ if (0 == PAL_wcscmp(pwsz, psObjectToLocate->GetString()))
+ {
+ //
+ // This is the object we were looking for.
+ //
+
+ shmSharedObjectData = shmObjectListEntry;
+ break;
+ }
+ }
+ else
+ {
+ ASSERT("Unable to map psmod->shmObjName\n");
+ break;
+ }
+ }
+
+ shmObjectListEntry = psmod->shmNextObj;
+ }
+ else
+ {
+ ASSERT("Unable to map shmObjectListEntry\n");
+ break;
+ }
+ }
+
+ if (SHMNULL != shmSharedObjectData)
+ {
+ CSharedMemoryObject *pshmobj = NULL;
+ CObjectAttributes oa(pwsz, NULL);
+
+ //
+ // Check if the type is allowed
+ //
+
+ if (!paot->IsTypeAllowed(psmod->eTypeId))
+ {
+ TRACE("Remote object exists w/ incompatible type\n");
+ palError = ERROR_INVALID_HANDLE;
+ goto LocateObjectExitSHMRelease;
+ }
+
+ //
+ // Get the local instance of the CObjectType
+ //
+
+ CObjectType *pot = CObjectType::GetObjectTypeById(psmod->eTypeId);
+ if (NULL == pot)
+ {
+ ASSERT("Invalid object type ID in shared memory info\n");
+ goto LocateObjectExitSHMRelease;
+ }
+
+ TRACE("Remote object exists compatible type -- importing\n");
+
+ //
+ // Create the local state for the shared object
+ //
+
+ palError = ImportSharedObjectIntoProcess(
+ pthr,
+ pot,
+ &oa,
+ shmSharedObjectData,
+ psmod,
+ TRUE,
+ &pshmobj
+ );
+
+ if (NO_ERROR == palError)
+ {
+ *ppobj = static_cast<IPalObject*>(pshmobj);
+ }
+ else
+ {
+ ERROR("Failure initializing object from shared data\n");
+ goto LocateObjectExitSHMRelease;
+ }
+
+ }
+ else
+ {
+ //
+ // The object was not found
+ //
+
+ palError = ERROR_INVALID_NAME;
+ }
+
+LocateObjectExitSHMRelease:
+
+ SHMRelease();
+
+LocateObjectExit:
+
+ InternalLeaveCriticalSection(pthr, &m_csListLock);
+
+ LOGEXIT("CSharedMemoryObjectManager::LocateObject returns %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::ObtainHandleForObject
+
+ Allocated a new handle for an object
+
+Parameters:
+ pthr -- thread data for calling thread
+ pobj -- the object to allocate a handle for
+ dwRightsRequired -- the access rights to grant the handle; currently ignored
+ fInheritHandle -- true if the handle is inheritable; ignored for all but file
+ objects that represent pipes
+ pProcessForHandle -- the process the handle is to be used from; currently
+ must be NULL
+ pNewHandle -- on success, receives the newly allocated handle
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::ObtainHandleForObject(
+ CPalThread *pthr,
+ IPalObject *pobj,
+ DWORD dwRightsRequested,
+ bool fInheritHandle,
+ IPalProcess *pProcessForHandle, // IN, OPTIONAL
+ HANDLE *pNewHandle // OUT
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != pobj);
+ _ASSERTE(NULL != pNewHandle);
+
+ ENTRY("CSharedMemoryObjectManager::ObtainHandleForObject "
+ "(this=%p, pthr=%p, pobj=%p, dwRightsRequested=%d, "
+ "fInheritHandle=%p, pProcessForHandle=%p, pNewHandle=%p)\n",
+ this,
+ pthr,
+ pobj,
+ dwRightsRequested,
+ fInheritHandle,
+ pProcessForHandle,
+ pNewHandle
+ );
+
+ if (NULL != pProcessForHandle)
+ {
+ //
+ // Not yet supported
+ //
+
+ ASSERT("Caller to ObtainHandleForObject provided a process\n");
+ return ERROR_CALL_NOT_IMPLEMENTED;
+ }
+
+ palError = m_HandleManager.AllocateHandle(
+ pthr,
+ pobj,
+ dwRightsRequested,
+ fInheritHandle,
+ pNewHandle
+ );
+
+ LOGEXIT("CSharedMemoryObjectManager::ObtainHandleForObject return %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::RevokeHandle
+
+ Removes a handle from the process's handle table, which in turn releases
+ the handle's reference on the object instance it refers to
+
+Parameters:
+ pthr -- thread data for calling thread
+ hHandleToRevoke -- the handle to revoke
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::RevokeHandle(
+ CPalThread *pthr,
+ HANDLE hHandleToRevoke
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("CSharedMemoryObjectManager::RevokeHandle "
+ "(this=%p, pthr=%p, hHandleToRevoke=%p)\n",
+ this,
+ pthr,
+ hHandleToRevoke
+ );
+
+ palError = m_HandleManager.FreeHandle(pthr, hHandleToRevoke);
+
+ LOGEXIT("CSharedMemoryObjectManager::RevokeHandle returns %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::ReferenceObjectByHandle
+
+ Returns a referenced object instance that a handle refers to
+
+Parameters:
+ pthr -- thread data for calling thread
+ hHandleToReference -- the handle to reference
+ paot -- acceptable types for the underlying object
+ dwRightsRequired -- the access rights that the handle must have been
+ granted; currently ignored
+ ppobj -- on success, receives a reference to the object instance
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::ReferenceObjectByHandle(
+ CPalThread *pthr,
+ HANDLE hHandleToReference,
+ CAllowedObjectTypes *paot,
+ DWORD dwRightsRequired,
+ IPalObject **ppobj // OUT
+ )
+{
+ PAL_ERROR palError;
+ DWORD dwRightsGranted;
+ IPalObject *pobj;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != paot);
+ _ASSERTE(NULL != ppobj);
+
+ ENTRY("CSharedMemoryObjectManager::ReferenceObjectByHandle "
+ "(this=%p, pthr=%p, hHandleToReference=%p, paot=%p, "
+ "dwRightsRequired=%d, ppobj=%p)\n",
+ this,
+ pthr,
+ hHandleToReference,
+ paot,
+ dwRightsRequired,
+ ppobj
+ );
+
+ palError = m_HandleManager.GetObjectFromHandle(
+ pthr,
+ hHandleToReference,
+ &dwRightsGranted,
+ &pobj
+ );
+
+ if (NO_ERROR == palError)
+ {
+ palError = CheckObjectTypeAndRights(
+ pobj,
+ paot,
+ dwRightsGranted,
+ dwRightsRequired
+ );
+
+ if (NO_ERROR == palError)
+ {
+ //
+ // Transfer object reference to out parameter
+ //
+
+ *ppobj = pobj;
+ }
+ else
+ {
+ pobj->ReleaseReference(pthr);
+ }
+ }
+
+ LOGEXIT("CSharedMemoryObjectManager::ReferenceObjectByHandle returns %d\n",
+ palError
+ );
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::ReferenceObjectByHandleArray
+
+ Returns the referenced object instances that an array of handles
+ refer to.
+
+Parameters:
+ pthr -- thread data for calling thread
+ rgHandlesToReference -- the array of handles to reference
+ dwHandleCount -- the number of handles in the arrayu
+ paot -- acceptable types for the underlying objects
+ dwRightsRequired -- the access rights that the handles must have been
+ granted; currently ignored
+ rgpobjs -- on success, receives references to the object instances; will
+ be empty on failures
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::ReferenceMultipleObjectsByHandleArray(
+ CPalThread *pthr,
+ HANDLE rghHandlesToReference[],
+ DWORD dwHandleCount,
+ CAllowedObjectTypes *paot,
+ DWORD dwRightsRequired,
+ IPalObject *rgpobjs[] // OUT (caller allocated)
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobj = NULL;
+ DWORD dwRightsGranted;
+ DWORD dw;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != rghHandlesToReference);
+ _ASSERTE(0 < dwHandleCount);
+ _ASSERTE(NULL != paot);
+ _ASSERTE(NULL != rgpobjs);
+
+ ENTRY("CSharedMemoryObjectManager::ReferenceMultipleObjectsByHandleArray "
+ "(this=%p, pthr=%p, rghHandlesToReference=%p, dwHandleCount=%d, "
+ "pAllowedTyped=%d, dwRightsRequired=%d, rgpobjs=%p)\n",
+ this,
+ pthr,
+ rghHandlesToReference,
+ dwHandleCount,
+ paot,
+ dwRightsRequired,
+ rgpobjs
+ );
+
+ m_HandleManager.Lock(pthr);
+
+ for (dw = 0; dw < dwHandleCount; dw += 1)
+ {
+ palError = m_HandleManager.GetObjectFromHandle(
+ pthr,
+ rghHandlesToReference[dw],
+ &dwRightsGranted,
+ &pobj
+ );
+
+ if (NO_ERROR == palError)
+ {
+ palError = CheckObjectTypeAndRights(
+ pobj,
+ paot,
+ dwRightsGranted,
+ dwRightsRequired
+ );
+
+ if (NO_ERROR == palError)
+ {
+ //
+ // Transfer reference to out array
+ //
+
+ rgpobjs[dw] = pobj;
+ pobj = NULL;
+ }
+ }
+
+ if (NO_ERROR != palError)
+ {
+ break;
+ }
+ }
+
+ //
+ // The handle manager lock must be released before releasing
+ // any object references, as ReleaseReference will acquire
+ // the object manager list lock (which needs to be acquired before
+ // the handle manager lock)
+ //
+
+ m_HandleManager.Unlock(pthr);
+
+ if (NO_ERROR != palError)
+ {
+ //
+ // dw's current value is the failing index, so we want
+ // to free from dw - 1.
+ //
+
+ while (dw > 0)
+ {
+ rgpobjs[--dw]->ReleaseReference(pthr);
+ }
+
+ if (NULL != pobj)
+ {
+ pobj->ReleaseReference(pthr);
+ }
+ }
+
+ LOGEXIT("CSharedMemoryObjectManager::ReferenceMultipleObjectsByHandleArray"
+ " returns %d\n",
+ palError
+ );
+
+ return palError;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::ReferenceObjectByForeignHandle
+
+ Returns a referenced object instance that a handle belongin to
+ another process refers to; currently unimplemented
+
+Parameters:
+ pthr -- thread data for calling thread
+ hForeignHandle -- the handle to reference
+ pForeignProcess -- the process that hForeignHandle belongs to
+ paot -- acceptable types for the underlying object
+ dwRightsRequired -- the access rights that the handle must have been
+ granted; currently ignored
+ ppobj -- on success, receives a reference to the object instance
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::ReferenceObjectByForeignHandle(
+ CPalThread *pthr,
+ HANDLE hForeignHandle,
+ IPalProcess *pForeignProcess,
+ CAllowedObjectTypes *paot,
+ DWORD dwRightsRequired,
+ IPalObject **ppobj // OUT
+ )
+{
+ //
+ // Not implemented for basic shared memory object manager --
+ // requires an IPC channel. (For the shared memory object manager
+ // PAL_LocalHandleToRemote and PAL_RemoteHandleToLocal must still
+ // be used...)
+ //
+
+ ASSERT("ReferenceObjectByForeignHandle not yet supported\n");
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::ImportSharedObjectIntoProcess
+
+ Takes an object's shared memory data and from it creates the
+ necessary in-process structures for the object
+
+Parameters:
+ pthr -- thread data for calling thread
+ pot -- the object's type
+ poa -- attributes for the object
+ shmSharedObjectData -- the shared memory pointer for the object's shared
+ data
+ psmod -- the shared memory data for the object, mapped into this process's
+ address space
+ fAddRefSharedData -- if TRUE, we need to add to the shared data reference
+ count
+ ppshmobj -- on success, receives a pointer to the newly created local
+ object instance
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::ImportSharedObjectIntoProcess(
+ CPalThread *pthr,
+ CObjectType *pot,
+ CObjectAttributes *poa,
+ SHMPTR shmSharedObjectData,
+ SHMObjData *psmod,
+ bool fAddRefSharedData,
+ CSharedMemoryObject **ppshmobj
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CSharedMemoryObject *pshmobj;
+ PLIST_ENTRY pleObjectList;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != pot);
+ _ASSERTE(NULL != poa);
+ _ASSERTE(SHMNULL != shmSharedObjectData);
+ _ASSERTE(NULL != psmod);
+ _ASSERTE(NULL != ppshmobj);
+
+ ENTRY("CSharedMemoryObjectManager::ImportSharedObjectIntoProcess(pthr=%p, "
+ "pot=%p, poa=%p, shmSharedObjectData=%p, psmod=%p, fAddRefSharedData=%d, "
+ "ppshmobj=%p)\n",
+ pthr,
+ pot,
+ poa,
+ shmSharedObjectData,
+ psmod,
+ fAddRefSharedData,
+ ppshmobj
+ );
+
+ if (CObjectType::WaitableObject == pot->GetSynchronizationSupport())
+ {
+ pshmobj = InternalNew<CSharedMemoryWaitableObject>(pot,
+ &m_csListLock,
+ shmSharedObjectData,
+ psmod,
+ fAddRefSharedData);
+ }
+ else
+ {
+ pshmobj = InternalNew<CSharedMemoryObject>(pot,
+ &m_csListLock,
+ shmSharedObjectData,
+ psmod,
+ fAddRefSharedData);
+ }
+
+ if (NULL != pshmobj)
+ {
+ palError = pshmobj->InitializeFromExistingSharedData(pthr, poa);
+ if (NO_ERROR == palError)
+ {
+ if (0 != psmod->dwNameLength)
+ {
+ pleObjectList = &m_leNamedObjects;
+ }
+ else
+ {
+ pleObjectList = &m_leAnonymousObjects;
+ }
+
+ InsertTailList(pleObjectList, pshmobj->GetObjectListLink());
+ }
+ else
+ {
+ goto ImportSharedObjectIntoProcessExit;
+ }
+ }
+ else
+ {
+ ERROR("Unable to alllocate new object\n");
+ palError = ERROR_OUTOFMEMORY;
+ goto ImportSharedObjectIntoProcessExit;
+ }
+
+ *ppshmobj = pshmobj;
+
+ImportSharedObjectIntoProcessExit:
+
+ LOGEXIT("CSharedMemoryObjectManager::ImportSharedObjectIntoProcess returns %d\n", palError);
+
+ return palError;
+}
+
+static PalObjectTypeId RemotableObjectTypes[] =
+ {otiManualResetEvent, otiAutoResetEvent, otiMutex, otiProcess};
+
+static CAllowedObjectTypes aotRemotable(
+ RemotableObjectTypes,
+ sizeof(RemotableObjectTypes) / sizeof(RemotableObjectTypes[0])
+ );
+
+/*++
+Function:
+ PAL_LocalHandleToRemote
+
+ Returns a "remote handle" that may be passed to another process.
+
+Parameters:
+ hLocal -- the handle to generate a "remote handle" for
+--*/
+
+PALIMPORT
+RHANDLE
+PALAPI
+PAL_LocalHandleToRemote(IN HANDLE hLocal)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthr;
+ IPalObject *pobj = NULL;
+ CSharedMemoryObject *pshmobj;
+ SHMObjData *psmod = NULL;
+ RHANDLE hRemote = reinterpret_cast<RHANDLE>(INVALID_HANDLE_VALUE);
+
+ PERF_ENTRY(PAL_LocalHandleToRemote);
+ ENTRY("PAL_LocalHandleToRemote( hLocal=0x%lx )\n", hLocal);
+
+ pthr = InternalGetCurrentThread();
+
+ if (!HandleIsSpecial(hLocal))
+ {
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pthr,
+ hLocal,
+ &aotRemotable,
+ 0,
+ &pobj
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto PAL_LocalHandleToRemoteExitNoLockRelease;
+ }
+ }
+ else if (hPseudoCurrentProcess == hLocal)
+ {
+ pobj = g_pobjProcess;
+ pobj->AddReference();
+ }
+ else
+ {
+ ASSERT("Invalid special handle type passed to PAL_LocalHandleToRemote\n");
+ palError = ERROR_INVALID_HANDLE;
+ goto PAL_LocalHandleToRemoteExitNoLockRelease;
+ }
+
+ pshmobj = static_cast<CSharedMemoryObject*>(pobj);
+
+ //
+ // Make sure that the object is shared
+ //
+
+ palError = pshmobj->EnsureObjectIsShared(pthr);
+ if (NO_ERROR != palError)
+ {
+ ERROR("Failure %d promoting object\n", palError);
+ goto PAL_LocalHandleToRemoteExitNoLockRelease;
+ }
+
+ SHMLock();
+
+ psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, pshmobj->GetShmObjData());
+ if (NULL != psmod)
+ {
+ //
+ // Bump up the process ref count by 1. The receiving process will not
+ // increase the ref count when it converts the remote handle to
+ // local.
+ //
+
+ psmod->lProcessRefCount += 1;
+
+ //
+ // The remote handle is simply the SHMPTR for the SHMObjData
+ //
+
+ hRemote = reinterpret_cast<RHANDLE>(pshmobj->GetShmObjData());
+ }
+ else
+ {
+ ASSERT("Unable to map shared object data\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto PAL_LocalHandleToRemoteExit;
+ }
+
+PAL_LocalHandleToRemoteExit:
+
+ SHMRelease();
+
+PAL_LocalHandleToRemoteExitNoLockRelease:
+
+ if (NULL != pobj)
+ {
+ pobj->ReleaseReference(pthr);
+ }
+
+ if (NO_ERROR != palError)
+ {
+ pthr->SetLastError(palError);
+ }
+
+ LOGEXIT("PAL_LocalHandleToRemote returns RHANDLE 0x%lx\n", hRemote);
+ PERF_EXIT(PAL_LocalHandleToRemote);
+ return hRemote;
+}
+
+/*++
+Function:
+ CSharedMemoryObjectManager::ConvertRemoteHandleToLocal
+
+ Given a "remote handle" creates a local handle that refers
+ to the desired object. (Unlike PAL_RemoteHandleToLocal this method
+ needs to access internal object manager state, so it's a member function.)
+
+Parameters:
+ pthr -- thread data for calling thread
+ rhRemote -- the remote handle
+ phLocal -- on success, receives the local handle
+--*/
+
+PAL_ERROR
+CSharedMemoryObjectManager::ConvertRemoteHandleToLocal(
+ CPalThread *pthr,
+ RHANDLE rhRemote,
+ HANDLE *phLocal
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ SHMObjData *psmod;
+ CSharedMemoryObject *pshmobj = NULL;
+ PLIST_ENTRY pleObjectList;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != phLocal);
+
+ ENTRY("CSharedMemoryObjectManager::ConvertRemoteHandleToLocal "
+ "(this=%p, pthr=%p, rhRemote=%p, phLocal=%p)\n",
+ this,
+ pthr,
+ rhRemote,
+ phLocal
+ );
+
+ if (rhRemote == NULL || rhRemote == INVALID_HANDLE_VALUE)
+ {
+ palError = ERROR_INVALID_HANDLE;
+ goto ConvertRemoteHandleToLocalExitNoLockRelease;
+ }
+
+ InternalEnterCriticalSection(pthr, &m_csListLock);
+ SHMLock();
+
+ //
+ // The remote handle is really a shared memory pointer to the
+ // SHMObjData for the object.
+ //
+
+ psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, reinterpret_cast<SHMPTR>(rhRemote));
+ if (NULL == psmod)
+ {
+ ERROR("Invalid remote handle\n");
+ palError = ERROR_INVALID_HANDLE;
+ goto ConvertRemoteHandleToLocalExit;
+ }
+
+ //
+ // Check to see if a local reference for this object already
+ // exists
+ //
+
+ if (0 != psmod->dwNameLength)
+ {
+ pleObjectList = &m_leNamedObjects;
+ }
+ else
+ {
+ pleObjectList = &m_leAnonymousObjects;
+ }
+
+ for (PLIST_ENTRY ple = pleObjectList->Flink;
+ ple != pleObjectList;
+ ple = ple->Flink)
+ {
+ pshmobj = CSharedMemoryObject::GetObjectFromListLink(ple);
+
+ if (SharedObject == pshmobj->GetObjectDomain()
+ && reinterpret_cast<SHMPTR>(rhRemote) == pshmobj->GetShmObjData())
+ {
+ TRACE("Object for remote handle already present in this process\n");
+
+ //
+ // PAL_LocalHandleToRemote bumped up the process refcount on the
+ // object. Since this process already had a reference to the object
+ // we need to decrement that reference now...
+ //
+
+ psmod->lProcessRefCount -= 1;
+ _ASSERTE(0 < psmod->lProcessRefCount);
+
+ //
+ // We also need to add a reference to the object (since ReleaseReference
+ // gets called below)
+ //
+
+ pshmobj->AddReference();
+
+ break;
+ }
+
+ pshmobj = NULL;
+ }
+
+ if (NULL == pshmobj)
+ {
+ CObjectType *pot;
+ CObjectAttributes oa;
+
+ //
+ // Get the local instance of the CObjectType
+ //
+
+ pot = CObjectType::GetObjectTypeById(psmod->eTypeId);
+ if (NULL == pot)
+ {
+ ASSERT("Invalid object type ID in shared memory info\n");
+ goto ConvertRemoteHandleToLocalExit;
+ }
+
+ //
+ // Create the local state for the shared object
+ //
+
+ palError = ImportSharedObjectIntoProcess(
+ pthr,
+ pot,
+ &oa,
+ reinterpret_cast<SHMPTR>(rhRemote),
+ psmod,
+ FALSE,
+ &pshmobj
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ConvertRemoteHandleToLocalExit;
+ }
+ }
+
+ //
+ // Finally, allocate a local handle for the object
+ //
+
+ palError = ObtainHandleForObject(
+ pthr,
+ pshmobj,
+ 0,
+ FALSE,
+ NULL,
+ phLocal
+ );
+
+ConvertRemoteHandleToLocalExit:
+
+ SHMRelease();
+ InternalLeaveCriticalSection(pthr, &m_csListLock);
+
+ConvertRemoteHandleToLocalExitNoLockRelease:
+
+ if (NULL != pshmobj)
+ {
+ pshmobj->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("CSharedMemoryObjectManager::ConvertRemoteHandleToLocal returns %d\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ PAL_RemoteHandleToLocal
+
+ Given a "remote handle", return a local handle that refers to the
+ specified process. Calls
+ SharedMemoryObjectManager::ConvertRemoteHandleToLocal to do the actual
+ work
+
+Parameters:
+ rhRemote -- the "remote handle" to convert to a local handle
+--*/
+
+PALIMPORT
+HANDLE
+PALAPI
+PAL_RemoteHandleToLocal(IN RHANDLE rhRemote)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthr;
+ HANDLE hLocal = INVALID_HANDLE_VALUE;
+
+ PERF_ENTRY(PAL_RemoteHandleToLocal);
+ ENTRY("PAL_RemoteHandleToLocal( hRemote=0x%lx )\n", rhRemote);
+
+ pthr = InternalGetCurrentThread();
+
+ palError = static_cast<CSharedMemoryObjectManager*>(g_pObjectManager)->ConvertRemoteHandleToLocal(
+ pthr,
+ rhRemote,
+ &hLocal
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pthr->SetLastError(palError);
+ }
+
+ LOGEXIT("PAL_RemoteHandleToLocal returns HANDLE 0x%lx\n", hLocal);
+ PERF_EXIT(PAL_RemoteHandleToLocal);
+ return hLocal;
+}
+
+/*++
+Function:
+ CheckObjectTypeAndRights
+
+ Helper routine that determines if:
+ 1) An object instance is of a specified type
+ 2) A set of granted access rights satisfies the required access rights
+ (currently ignored)
+
+Parameters:
+ pobj -- the object instance whose type is to be checked
+ paot -- the acceptable type for the object instance
+ dwRightsGranted -- the granted access rights (ignored)
+ dwRightsRequired -- the required access rights (ignored)
+--*/
+
+static
+PAL_ERROR
+CheckObjectTypeAndRights(
+ IPalObject *pobj,
+ CAllowedObjectTypes *paot,
+ DWORD dwRightsGranted,
+ DWORD dwRightsRequired
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ _ASSERTE(NULL != pobj);
+ _ASSERTE(NULL != paot);
+
+ ENTRY("CheckObjectTypeAndRights (pobj=%p, paot=%p, "
+ "dwRightsGranted=%d, dwRightsRequired=%d)\n",
+ pobj,
+ paot,
+ dwRightsGranted,
+ dwRightsRequired
+ );
+
+ if (paot->IsTypeAllowed(pobj->GetObjectType()->GetId()))
+ {
+#ifdef ENFORCE_OBJECT_ACCESS_RIGHTS
+
+ //
+ // This is where the access right check would occur if Win32 object
+ // security were supported.
+ //
+
+ if ((dwRightsRequired & dwRightsGranted) != dwRightsRequired)
+ {
+ palError = ERROR_ACCESS_DENIED;
+ }
+#endif
+ }
+ else
+ {
+ palError = ERROR_INVALID_HANDLE;
+ }
+
+ LOGEXIT("CheckObjectTypeAndRights returns %d\n", palError);
+
+ return palError;
+}
+
+
diff --git a/src/pal/src/objmgr/shmobjectmanager.hpp b/src/pal/src/objmgr/shmobjectmanager.hpp
new file mode 100644
index 0000000000..fbde872eeb
--- /dev/null
+++ b/src/pal/src/objmgr/shmobjectmanager.hpp
@@ -0,0 +1,167 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ shmobjectmanager.hpp
+
+Abstract:
+ Shared memory based object manager
+
+
+
+--*/
+
+#ifndef _PAL_SHMOBJECTMANAGER_HPP_
+#define _PAL_SHMOBJECTMANAGER_HPP_
+
+#include "pal/corunix.hpp"
+#include "pal/handlemgr.hpp"
+#include "pal/list.h"
+#include "shmobject.hpp"
+
+namespace CorUnix
+{
+ class CSharedMemoryObjectManager : public IPalObjectManager
+ {
+ protected:
+
+ CRITICAL_SECTION m_csListLock;
+ bool m_fListLockInitialized;
+ LIST_ENTRY m_leNamedObjects;
+ LIST_ENTRY m_leAnonymousObjects;
+
+ CSimpleHandleManager m_HandleManager;
+
+ PAL_ERROR
+ ImportSharedObjectIntoProcess(
+ CPalThread *pthr,
+ CObjectType *pot,
+ CObjectAttributes *poa,
+ SHMPTR shmSharedObjectData,
+ SHMObjData *psmod,
+ bool fAddRefSharedData,
+ CSharedMemoryObject **ppshmobj
+ );
+
+ public:
+
+ CSharedMemoryObjectManager()
+ :
+ m_fListLockInitialized(FALSE)
+ {
+ };
+
+ virtual ~CSharedMemoryObjectManager()
+ {
+ };
+
+ PAL_ERROR
+ Initialize(
+ void
+ );
+
+ PAL_ERROR
+ Shutdown(
+ CPalThread *pthr
+ );
+
+ PAL_ERROR
+ ConvertRemoteHandleToLocal(
+ CPalThread *pthr,
+ RHANDLE rhRemote,
+ HANDLE *phLocal
+ );
+
+ //
+ // IPalObjectManager routines
+ //
+
+ virtual
+ PAL_ERROR
+ AllocateObject(
+ CPalThread *pthr,
+ CObjectType *pot,
+ CObjectAttributes *poa,
+ IPalObject **ppobjNew
+ );
+
+ virtual
+ PAL_ERROR
+ RegisterObject(
+ CPalThread *pthr,
+ IPalObject *pobjToRegister,
+ CAllowedObjectTypes *paot,
+ DWORD dwRightsRequested,
+ HANDLE *pHandle,
+ IPalObject **ppobjRegistered
+ );
+
+ virtual
+ PAL_ERROR
+ LocateObject(
+ CPalThread *pthr,
+ CPalString *psObjectToLocate,
+ CAllowedObjectTypes *paot,
+ IPalObject **ppobj
+ );
+
+ virtual
+ PAL_ERROR
+ ObtainHandleForObject(
+ CPalThread *pthr,
+ IPalObject *pobj,
+ DWORD dwRightsRequested,
+ bool fInheritHandle,
+ IPalProcess *pProcessForHandle, // IN, OPTIONAL
+ HANDLE *pNewHandle
+ );
+
+ virtual
+ PAL_ERROR
+ RevokeHandle(
+ CPalThread *pthr,
+ HANDLE hHandleToRevoke
+ );
+
+ virtual
+ PAL_ERROR
+ ReferenceObjectByHandle(
+ CPalThread *pthr,
+ HANDLE hHandleToReference,
+ CAllowedObjectTypes *paot,
+ DWORD dwRightsRequired,
+ IPalObject **ppobj
+ );
+
+ virtual
+ PAL_ERROR
+ ReferenceMultipleObjectsByHandleArray(
+ CPalThread *pthr,
+ HANDLE rghHandlesToReference[],
+ DWORD dwHandleCount,
+ CAllowedObjectTypes *paot,
+ DWORD dwRightsRequired,
+ IPalObject *rgpobjs[]
+ );
+
+ virtual
+ PAL_ERROR
+ ReferenceObjectByForeignHandle(
+ CPalThread *pthr,
+ HANDLE hForeignHandle,
+ IPalProcess *pForeignProcess,
+ CAllowedObjectTypes *paot,
+ DWORD dwRightsRequired,
+ IPalObject **ppobj
+ );
+ };
+}
+
+#endif // _PAL_SHMOBJECTMANAGER_HPP_
+
diff --git a/src/pal/src/poll/fakepoll.cpp b/src/pal/src/poll/fakepoll.cpp
new file mode 100644
index 0000000000..ac531413ae
--- /dev/null
+++ b/src/pal/src/poll/fakepoll.cpp
@@ -0,0 +1,133 @@
+// 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.
+
+// fakepoll.h
+// poll using select
+// Warning: a call to this poll() takes about 4K of stack space.
+
+// Greg Parker gparker@cs.stanford.edu December 2000
+// This code is in the public domain and may be copied or modified without
+// permission.
+
+// Located at <http://www.sealiesoftware.com/fakepoll.h>.
+
+
+
+#include "pal/palinternal.h"
+#include "pal/fakepoll.h"
+#include "pal/dbgmsg.h"
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+SET_DEFAULT_DEBUG_CHANNEL(POLL);
+
+int poll(struct pollfd *pollSet, int pollCount, int pollTimeout)
+{
+ struct timeval tv;
+ struct timeval *tvp;
+ fd_set readFDs, writeFDs, exceptFDs;
+ fd_set *readp, *writep, *exceptp;
+ struct pollfd *pollEnd, *p;
+ int selected;
+ int result;
+ int maxFD;
+
+ if (!pollSet) {
+ pollEnd = NULL;
+ readp = NULL;
+ writep = NULL;
+ exceptp = NULL;
+ maxFD = 0;
+ }
+ else {
+ pollEnd = pollSet + pollCount;
+ readp = &readFDs;
+ writep = &writeFDs;
+ exceptp = &exceptFDs;
+
+ FD_ZERO(readp);
+ FD_ZERO(writep);
+ FD_ZERO(exceptp);
+
+ // Find the biggest fd in the poll set
+ maxFD = 0;
+ for (p = pollSet; p < pollEnd; p++) {
+ if (p->fd > maxFD) maxFD = p->fd;
+ }
+
+ if (maxFD >= FD_SETSIZE) {
+ // At least one fd is too big
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Transcribe flags from the poll set to the fd sets
+ for (p = pollSet; p < pollEnd; p++) {
+ if (p->fd < 0) {
+ // Negative fd checks nothing and always reports zero
+ } else {
+ if (p->events & POLLIN) FD_SET(p->fd, readp);
+ if (p->events & POLLOUT) FD_SET(p->fd, writep);
+ if (p->events != 0) FD_SET(p->fd, exceptp);
+ // POLLERR is never set coming in; poll() always reports errors.
+ // But don't report if we're not listening to anything at all.
+ }
+ }
+ }
+
+ // poll timeout is in milliseconds. Convert to struct timeval.
+ // poll timeout == -1 : wait forever : select timeout of NULL
+ // poll timeout == 0 : return immediately : select timeout of zero
+ if (pollTimeout >= 0) {
+ tv.tv_sec = pollTimeout / 1000;
+ tv.tv_usec = (pollTimeout % 1000) * 1000;
+ tvp = &tv;
+ } else {
+ tvp = NULL;
+ }
+
+ selected = select(maxFD+1, readp, writep, exceptp, tvp);
+
+ if (selected < 0) {
+ // Error during select
+ result = -1;
+ }
+ else if (selected > 0) {
+ // Select found something
+ // Transcribe result from fd sets to poll set.
+ // Also count the number of selected fds. poll returns the
+ // number of ready fds; select returns the number of bits set.
+ int polled = 0;
+ for (p = pollSet; p < pollEnd; p++) {
+ p->revents = 0;
+ if (p->fd > -1) {
+ // Check p->events before setting p->revents. If we
+ // have multiple pollfds with the same fd, we want to
+ // set the appropriate revents value for each pollfd.
+ if (FD_ISSET(p->fd, readp) && (p->events & POLLIN))
+ p->revents |= POLLIN;
+ if (FD_ISSET(p->fd, writep) && (p->events & POLLOUT))
+ p->revents |= POLLOUT;
+ if (FD_ISSET(p->fd, exceptp) && (p->events != 0))
+ p->revents |= POLLERR;
+ if (p->revents) polled++;
+ }
+ }
+ result = polled;
+ }
+ else {
+ // selected == 0, select timed out before anything happened
+ // Clear all result bits and return zero.
+ for (p = pollSet; p < pollEnd; p++) {
+ p->revents = 0;
+ }
+ result = 0;
+ }
+
+ return result;
+}
diff --git a/src/pal/src/safecrt/cruntime.h b/src/pal/src/safecrt/cruntime.h
new file mode 100644
index 0000000000..cdad474e53
--- /dev/null
+++ b/src/pal/src/safecrt/cruntime.h
@@ -0,0 +1,98 @@
+// 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.
+
+/***
+*cruntime.h - definitions specific to the target operating system and hardware
+*
+
+*
+*Purpose:
+* This header file contains widely used definitions specific to the
+* host operating system and hardware. It is included by every C source
+* and most every other header file.
+*
+* [Internal]
+*
+****/
+
+#if _MSC_VER > 1000
+#pragma once
+#endif /* _MSC_VER > 1000 */
+
+#ifndef _INC_CRUNTIME
+#define _INC_CRUNTIME
+
+#ifndef _CRTBLD
+/*
+ * This is an internal C runtime header file. It is used when building
+ * the C runtimes only. It is not to be used as a public header file.
+ */
+#error ERROR: Use of C runtime library internal header file.
+#endif /* _CRTBLD */
+
+#if defined (_SYSCRT) && defined (_WIN64)
+#define _USE_OLD_STDCPP 1
+#endif /* defined (_SYSCRT) && defined (_WIN64) */
+
+#if !defined (UNALIGNED)
+#if defined (_M_IA64) || defined (_M_AMD64)
+#define UNALIGNED __unaligned
+#else /* defined (_M_IA64) || defined (_M_AMD64) */
+#define UNALIGNED
+#endif /* defined (_M_IA64) || defined (_M_AMD64) */
+#endif /* !defined (UNALIGNED) */
+
+#ifdef _M_IX86
+/*
+ * 386/486
+ */
+#define REG1 register
+#define REG2 register
+#define REG3 register
+#define REG4
+#define REG5
+#define REG6
+#define REG7
+#define REG8
+#define REG9
+
+#elif defined (_M_IA64) || defined (_M_AMD64)
+/*
+ * IA64
+ */
+#define REG1 register
+#define REG2 register
+#define REG3 register
+#define REG4 register
+#define REG5 register
+#define REG6 register
+#define REG7 register
+#define REG8 register
+#define REG9 register
+
+#else /* defined (_M_IA64) || defined (_M_AMD64) */
+
+#pragma message ("Machine register set not defined")
+
+/*
+ * Unknown machine
+ */
+
+#define REG1
+#define REG2
+#define REG3
+#define REG4
+#define REG5
+#define REG6
+#define REG7
+#define REG8
+#define REG9
+
+#endif /* defined (_M_IA64) || defined (_M_AMD64) */
+
+/*
+ * Are the macro definitions below still needed in this file?
+ */
+
+#endif /* _INC_CRUNTIME */
diff --git a/src/pal/src/safecrt/input.inl b/src/pal/src/safecrt/input.inl
new file mode 100644
index 0000000000..eaad174ff5
--- /dev/null
+++ b/src/pal/src/safecrt/input.inl
@@ -0,0 +1,1314 @@
+// 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.
+
+/***
+*input.c - C formatted input, used by scanf, etc.
+*
+
+*
+*Purpose:
+* defines _input() to do formatted input; called from scanf(),
+* etc. functions. This module defines _cscanf() instead when
+* CPRFLAG is defined. The file cscanf.c defines that symbol
+* and then includes this file in order to implement _cscanf().
+*
+*Note:
+* this file is included in safecrt.lib build directly, plese refer
+* to safecrt_[w]input_s.c
+*
+*******************************************************************************/
+
+
+#define ALLOW_RANGE /* enable "%[a-z]"-style scansets */
+
+
+/* temporary work-around for compiler without 64-bit support */
+
+#ifndef _INTEGRAL_MAX_BITS
+#define _INTEGRAL_MAX_BITS 64
+#endif /* _INTEGRAL_MAX_BITS */
+
+// typedef __int64_t __int64;
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define UNALIGNED
+
+#define _BEGIN_SECURE_CRT_DEPRECATION_DISABLE
+#define _END_SECURE_CRT_DEPRECATION_DISABLE
+
+#define _CVTBUFSIZE (309+40) /* # of digits in max. dp value + slop */
+
+//#include <cruntime.h>
+//#include <stdio.h>
+//#include <ctype.h>
+//#include <cvt.h>
+//#include <conio.h>
+//#include <stdarg.h>
+//#include <string.h>
+//#include <internal.h>
+//#include <fltintrn.h>
+//#include <malloc.h>
+//#include <locale.h>
+//#include <mtdll.h>
+//#include <stdlib.h>
+//#include <setlocal.h>
+//#include <dbgint.h>
+
+//#ifndef _INC_INTERNAL_SAFECRT
+//#include <internal_securecrt.h>
+//#endif /* _INC_INTERNAL_SAFECRT */
+
+//#ifdef _MBCS
+//#undef _MBCS
+//#endif /* _MBCS */
+//#include <tchar.h>
+
+#define _MBTOWC(x,y,z) _minimal_chartowchar( x, y )
+
+#define _istspace(x) isspace((unsigned char)x)
+
+#define _malloc_crt PAL_malloc
+#define _realloc_crt PAL_realloc
+#define _free_crt PAL_free
+
+#define _FASSIGN(flag, argument, number, dec_point, locale) _safecrt_fassign((flag), (argument), (number))
+#define _WFASSIGN(flag, argument, number, dec_point, locale) _safecrt_wfassign((flag), (argument), (number))
+
+#if defined (UNICODE)
+#define ALLOC_TABLE 1
+#else /* defined (UNICODE) */
+#define ALLOC_TABLE 0
+#endif /* defined (UNICODE) */
+
+#define HEXTODEC(chr) _hextodec(chr)
+
+#define LEFT_BRACKET ('[' | ('a' - 'A')) /* 'lowercase' version */
+
+static int __cdecl _hextodec(_TCHAR);
+#ifdef CPRFLAG
+
+#define INC() (++charcount, _inc())
+#define UN_INC(chr) (--charcount, _un_inc(chr))
+#define EAT_WHITE() _whiteout(&charcount)
+
+static int __cdecl _inc(void);
+static void __cdecl _un_inc(int);
+static int __cdecl _whiteout(int *);
+
+#else /* CPRFLAG */
+
+#define INC() (++charcount, _inc(stream))
+#define UN_INC(chr) (--charcount, _un_inc(chr, stream))
+#define EAT_WHITE() _whiteout(&charcount, stream)
+
+static int __cdecl _inc(miniFILE *);
+static void __cdecl _un_inc(int, miniFILE *);
+static int __cdecl _whiteout(int *, miniFILE *);
+
+#endif /* CPRFLAG */
+
+#ifndef _UNICODE
+#define _ISDIGIT(chr) isdigit((unsigned char)chr)
+#define _ISXDIGIT(chr) isxdigit((unsigned char)chr)
+#else /* _UNICODE */
+#define _ISDIGIT(chr) ( !(chr & 0xff00) && isdigit( ((chr) & 0x00ff) ) )
+#define _ISXDIGIT(chr) ( !(chr & 0xff00) && isxdigit( ((chr) & 0x00ff) ) )
+#endif /* _UNICODE */
+
+#define MUL10(x) ( (((x)<<2) + (x))<<1 )
+
+
+#define LONGLONG_IS_INT64 1 /* 1 means long long is same as int64
+ 0 means long long is same as long */
+
+/***
+* int __check_float_string(size_t,size_t *, _TCHAR**, _TCHAR*, int*)
+*
+* Purpose:
+* Check if there is enough space insert onemore character in the given
+* block, if not then allocate more memory.
+*
+* Return:
+* FALSE if more memory needed and the reallocation failed.
+*
+*******************************************************************************/
+
+static int __check_float_string(size_t nFloatStrUsed,
+ size_t *pnFloatStrSz,
+ _TCHAR **pFloatStr,
+ _TCHAR *floatstring,
+ int *pmalloc_FloatStrFlag)
+{
+ void *tmpPointer;
+ _ASSERTE(nFloatStrUsed<=(*pnFloatStrSz));
+ if (nFloatStrUsed==(*pnFloatStrSz))
+ {
+ size_t newSize;
+
+ // Will (*pnFloatStrSz) * 2 * sizeof(_TCHAR) overflow?
+ if ( *pnFloatStrSz > (SIZE_T_MAX / 2 / sizeof(_TCHAR)))
+ {
+ return FALSE;
+ }
+
+ newSize = *pnFloatStrSz * 2 * sizeof(_TCHAR);
+
+ if ((*pFloatStr)==floatstring)
+ {
+ if (((*pFloatStr)=(_TCHAR *)_malloc_crt(newSize))==NULL)
+ {
+ return FALSE;
+ }
+
+ (*pmalloc_FloatStrFlag)=1;
+
+ memcpy((*pFloatStr),floatstring,(*pnFloatStrSz)*sizeof(_TCHAR));
+ (*pnFloatStrSz)*=2;
+ }
+ else
+ {
+ if ((tmpPointer=(_TCHAR *)_realloc_crt((*pFloatStr), newSize))==NULL)
+ {
+ return FALSE;
+ }
+ (*pFloatStr)=(_TCHAR *)(tmpPointer);
+ (*pnFloatStrSz)*=2;
+ }
+ }
+ return TRUE;
+}
+
+
+#define ASCII 32 /* # of bytes needed to hold 256 bits */
+
+#define SCAN_SHORT 0 /* also for FLOAT */
+#define SCAN_LONG 1 /* also for DOUBLE */
+#define SCAN_L_DOUBLE 2 /* only for LONG DOUBLE */
+
+#define SCAN_NEAR 0
+#define SCAN_FAR 1
+
+#ifndef _UNICODE
+#define TABLESIZE ASCII
+#else /* _UNICODE */
+#define TABLESIZE (ASCII * 256)
+#endif /* _UNICODE */
+
+
+/***
+*int _input(stream, format, arglist), static int input(format, arglist)
+*
+*Purpose:
+* get input items (data items or literal matches) from the input stream
+* and assign them if appropriate to the items thru the arglist. this
+* function is intended for internal library use only, not for the user
+*
+* The _input entry point is for the normal scanf() functions
+* The input entry point is used when compiling for _cscanf() [CPRFLAF
+* defined] and is a static function called only by _cscanf() -- reads from
+* console.
+*
+* This code also defines _input_s, which works differently for %c, %s & %[.
+* For these, _input_s first picks up the next argument from the variable
+* argument list & uses it as the maximum size of the character array pointed
+* to by the next argument in the list.
+*
+*Entry:
+* FILE *stream - file to read from
+* char *format - format string to determine the data to read
+* arglist - list of pointer to data items
+*
+*Exit:
+* returns number of items assigned and fills in data items
+* returns EOF if error or EOF found on stream before 1st data item matched
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+ #define _INTRN_LOCALE_CONV( x ) localeconv()
+
+#ifndef _UNICODE
+ int __cdecl __tinput_s (miniFILE* stream, const _TUCHAR* format, va_list arglist)
+#else
+ int __cdecl __twinput_s (miniFILE* stream, const _TUCHAR* format, va_list arglist)
+#endif /* _UNICODE */
+{
+ _TCHAR floatstring[_CVTBUFSIZE + 1];
+ _TCHAR *pFloatStr=floatstring;
+ size_t nFloatStrUsed=0;
+ size_t nFloatStrSz=sizeof(floatstring)/sizeof(floatstring[0]);
+ int malloc_FloatStrFlag=0;
+
+ unsigned long number; /* temp hold-value */
+#if ALLOC_TABLE
+ char *table = NULL; /* which chars allowed for %[] */
+ int malloc_flag = 0; /* is "table" allocated on the heap? */
+#else /* ALLOC_TABLE */
+ char AsciiTable[TABLESIZE];
+ char *table = AsciiTable;
+#endif /* ALLOC_TABLE */
+
+#if _INTEGRAL_MAX_BITS >= 64
+ uint64_t num64 = 0LL; /* temp for 64-bit integers */
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+ void *pointer=NULL; /* points to user data receptacle */
+ void *start; /* indicate non-empty string */
+
+
+#ifndef _UNICODE
+ wchar_t wctemp=L'\0';
+#endif /* _UNICODE */
+ _TUCHAR *scanptr; /* for building "table" data */
+ int ch = 0;
+ int charcount; /* total number of chars read */
+ int comchr; /* holds designator type */
+ int count; /* return value. # of assignments */
+
+ int started; /* indicate good number */
+ int width; /* width of field */
+ int widthset; /* user has specified width */
+#ifdef _SECURE_SCANF
+ size_t array_width = 0;
+ size_t original_array_width = 0;
+ int enomem = 0;
+ int format_error = FALSE;
+#endif /* _SECURE_SCANF */
+
+/* Neither coerceshort nor farone are need for the 386 */
+
+
+ char done_flag; /* general purpose loop monitor */
+ char longone; /* 0 = SHORT, 1 = LONG, 2 = L_DOUBLE */
+#if _INTEGRAL_MAX_BITS >= 64
+ int integer64; /* 1 for 64-bit integer, 0 otherwise */
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+ signed char widechar; /* -1 = char, 0 = ????, 1 = wchar_t */
+ char reject; /* %[^ABC] instead of %[ABC] */
+ char negative; /* flag for '-' detected */
+ char suppress; /* don't assign anything */
+ char match; /* flag: !0 if any fields matched */
+ va_list arglistsave; /* save arglist value */
+
+ char fl_wchar_arg; /* flags wide char/string argument */
+
+ _TCHAR decimal;
+
+
+ _TUCHAR rngch;
+ _TUCHAR last;
+ _TUCHAR prevchar;
+ _TCHAR tch;
+
+ _VALIDATE_RETURN( (format != NULL), EINVAL, EOF);
+
+#ifndef CPRFLAG
+ _VALIDATE_RETURN( (stream != NULL), EINVAL, EOF);
+#endif /* CPRFLAG */
+
+ /*
+ count = # fields assigned
+ charcount = # chars read
+ match = flag indicating if any fields were matched
+
+ [Note that we need both count and match. For example, a field
+ may match a format but have assignments suppressed. In this case,
+ match will get set, but 'count' will still equal 0. We need to
+ distinguish 'match vs no-match' when terminating due to EOF.]
+ */
+
+ count = charcount = match = 0;
+
+ while (*format) {
+
+ if (_istspace((_TUCHAR)*format)) {
+
+ UN_INC(EAT_WHITE()); /* put first non-space char back */
+
+ do {
+ tch = *++format;
+ } while (_istspace((_TUCHAR)tch));
+
+ continue;
+
+ }
+
+ if (_T('%') == *format) {
+
+ number = 0;
+ prevchar = 0;
+ width = widthset = started = 0;
+#ifdef _SECURE_SCANF
+ original_array_width = array_width = 0;
+ enomem = 0;
+#endif /* _SECURE_SCANF */
+ fl_wchar_arg = done_flag = suppress = negative = reject = 0;
+ widechar = 0;
+
+ longone = 1;
+
+#if _INTEGRAL_MAX_BITS >= 64
+ integer64 = 0;
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+
+ while (!done_flag) {
+
+ comchr = *++format;
+ if (_ISDIGIT((_TUCHAR)comchr)) {
+ ++widthset;
+ width = MUL10(width) + (comchr - _T('0'));
+ } else
+ switch (comchr) {
+ case _T('F') :
+ case _T('N') : /* no way to push NEAR in large model */
+ break; /* NEAR is default in small model */
+ case _T('h') :
+ /* set longone to 0 */
+ --longone;
+ --widechar; /* set widechar = -1 */
+ break;
+
+#if _INTEGRAL_MAX_BITS >= 64
+ case _T('I'):
+ if ( (*(format + 1) == _T('6')) &&
+ (*(format + 2) == _T('4')) )
+ {
+ format += 2;
+ ++integer64;
+ num64 = 0;
+ break;
+ }
+ else if ( (*(format + 1) == _T('3')) &&
+ (*(format + 2) == _T('2')) )
+ {
+ format += 2;
+ break;
+ }
+ else if ( (*(format + 1) == _T('d')) ||
+ (*(format + 1) == _T('i')) ||
+ (*(format + 1) == _T('o')) ||
+ (*(format + 1) == _T('x')) ||
+ (*(format + 1) == _T('X')) )
+ {
+ if (sizeof(void*) == sizeof(__int64))
+ {
+ ++integer64;
+ num64 = 0;
+ }
+ break;
+ }
+ if (sizeof(void*) == sizeof(__int64))
+ {
+ ++integer64;
+ num64 = 0;
+ }
+ goto DEFAULT_LABEL;
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+
+ case _T('L') :
+ /* ++longone; */
+ ++longone;
+ break;
+
+ case _T('q'):
+ ++integer64;
+ num64 = 0;
+ break;
+
+ case _T('l') :
+ if (*(format + 1) == _T('l'))
+ {
+ ++format;
+#ifdef LONGLONG_IS_INT64
+ ++integer64;
+ num64 = 0;
+ break;
+#else /* LONGLONG_IS_INT64 */
+ ++longone;
+ /* NOBREAK */
+#endif /* LONGLONG_IS_INT64 */
+ }
+ else
+ {
+ ++longone;
+ /* NOBREAK */
+ }
+ case _T('w') :
+ ++widechar; /* set widechar = 1 */
+ break;
+
+ case _T('*') :
+ ++suppress;
+ break;
+
+ default:
+DEFAULT_LABEL:
+ ++done_flag;
+ break;
+ }
+ }
+
+ if (!suppress) {
+ va_copy(arglistsave, arglist);
+ pointer = va_arg(arglist,void *);
+ } else {
+ pointer = NULL; // doesn't matter what value we use here - we're only using it as a flag
+ }
+
+ done_flag = 0;
+
+ if (!widechar) { /* use case if not explicitly specified */
+ if ((*format == _T('S')) || (*format == _T('C')))
+#ifdef _UNICODE
+ --widechar;
+ else
+ ++widechar;
+#else /* _UNICODE */
+ ++widechar;
+ else
+ --widechar;
+#endif /* _UNICODE */
+ }
+
+ /* switch to lowercase to allow %E,%G, and to
+ keep the switch table small */
+
+ comchr = *format | (_T('a') - _T('A'));
+
+ if (_T('n') != comchr)
+ {
+ if (_T('c') != comchr && LEFT_BRACKET != comchr)
+ ch = EAT_WHITE();
+ else
+ ch = INC();
+ }
+
+ if (_T('n') != comchr)
+ {
+ if (_TEOF == ch)
+ goto error_return;
+ }
+
+ if (!widthset || width) {
+
+#ifdef _SECURE_SCANF
+ if(!suppress && (comchr == _T('c') || comchr == _T('s') || comchr == LEFT_BRACKET)) {
+
+ va_copy(arglist, arglistsave);
+
+ /* Reinitialize pointer to point to the array to which we write the input */
+ pointer = va_arg(arglist, void*);
+
+ va_copy(arglistsave, arglist);
+
+ /* Get the next argument - size of the array in characters */
+#ifdef _WIN64
+ original_array_width = array_width = (size_t)(va_arg(arglist, unsigned int));
+#else /* _WIN64 */
+ original_array_width = array_width = va_arg(arglist, size_t);
+#endif /* _WIN64 */
+
+ if(array_width < 1) {
+ if (widechar > 0)
+ *(wchar_t UNALIGNED *)pointer = L'\0';
+ else
+ *(char *)pointer = '\0';
+
+ errno = ENOMEM;
+
+ goto error_return;
+ }
+ }
+#endif /* _SECURE_SCANF */
+ switch(comchr) {
+
+ case _T('c'):
+ /* case _T('C'): */
+ if (!widthset) {
+ ++widthset;
+ ++width;
+ }
+ if (widechar > 0)
+ fl_wchar_arg++;
+ goto scanit;
+
+
+ case _T('s'):
+ /* case _T('S'): */
+ if(widechar > 0)
+ fl_wchar_arg++;
+ goto scanit;
+
+
+ case LEFT_BRACKET : /* scanset */
+ if (widechar>0)
+ fl_wchar_arg++;
+ scanptr = (_TUCHAR *)(++format);
+
+ if (_T('^') == *scanptr) {
+ ++scanptr;
+ --reject; /* set reject to 255 */
+ }
+
+ /* Allocate "table" on first %[] spec */
+#if ALLOC_TABLE
+ if (table == NULL) {
+ table = (char*)_malloc_crt(TABLESIZE);
+ if ( table == NULL)
+ goto error_return;
+ malloc_flag = 1;
+ }
+#endif /* ALLOC_TABLE */
+ memset(table, 0, TABLESIZE);
+
+
+ if (LEFT_BRACKET == comchr)
+ if (_T(']') == *scanptr) {
+ prevchar = _T(']');
+ ++scanptr;
+
+ table[ _T(']') >> 3] = 1 << (_T(']') & 7);
+
+ }
+
+ while (_T(']') != *scanptr) {
+
+ rngch = *scanptr++;
+
+ if (_T('-') != rngch ||
+ !prevchar || /* first char */
+ _T(']') == *scanptr) /* last char */
+
+ table[(prevchar = rngch) >> 3] |= 1 << (rngch & 7);
+
+ else { /* handle a-z type set */
+
+ rngch = *scanptr++; /* get end of range */
+
+ if (prevchar < rngch) /* %[a-z] */
+ last = rngch;
+ else { /* %[z-a] */
+ last = prevchar;
+ prevchar = rngch;
+ }
+ for (rngch = prevchar; rngch <= last; ++rngch)
+ table[rngch >> 3] |= 1 << (rngch & 7);
+
+ prevchar = 0;
+
+ }
+ }
+
+
+ if (!*scanptr)
+ goto error_return; /* trunc'd format string */
+
+ /* scanset completed. Now read string */
+
+ if (LEFT_BRACKET == comchr)
+ format = scanptr;
+
+scanit:
+ start = pointer;
+
+ /*
+ * execute the format directive. that is, scan input
+ * characters until the directive is fulfilled, eof
+ * is reached, or a non-matching character is
+ * encountered.
+ *
+ * it is important not to get the next character
+ * unless that character needs to be tested! other-
+ * wise, reads from line-buffered devices (e.g.,
+ * scanf()) would require an extra, spurious, newline
+ * if the first newline completes the current format
+ * directive.
+ */
+ UN_INC(ch);
+
+#ifdef _SECURE_SCANF
+ /* One element is needed for '\0' for %s & %[ */
+ if(comchr != _T('c')) {
+ --array_width;
+ }
+#endif /* _SECURE_SCANF */
+ while ( !widthset || width-- ) {
+
+ ch = INC();
+ if (
+#ifndef CPRFLAG
+ (_TEOF != ch) &&
+#endif /* CPRFLAG */
+ // char conditions
+ ( ( comchr == _T('c')) ||
+ // string conditions !isspace()
+ ( ( comchr == _T('s') &&
+ (!(ch >= _T('\t') && ch <= _T('\r')) &&
+ ch != _T(' ')))) ||
+ // BRACKET conditions
+ ( (comchr == LEFT_BRACKET) &&
+ ((table[ch >> 3] ^ reject) & (1 << (ch & 7)))
+ )
+ )
+ )
+ {
+ if (!suppress) {
+#ifdef _SECURE_SCANF
+ if(!array_width) {
+ /* We have exhausted the user's buffer */
+
+ enomem = 1;
+ break;
+ }
+#endif /* _SECURE_SCANF */
+#ifndef _UNICODE
+ if (fl_wchar_arg) {
+ wctemp = L'?';
+ char temp[2];
+ temp[0] = (char) ch;
+#if 0 // we are not supporting multibyte input strings
+ if (isleadbyte((unsigned char)ch))
+ {
+ temp[1] = (char) INC();
+ }
+#endif /* 0 */
+ _MBTOWC(&wctemp, temp, MB_CUR_MAX);
+ *(wchar_t UNALIGNED *)pointer = wctemp;
+ /* just copy L'?' if mbtowc fails, errno is set by mbtowc */
+ pointer = (wchar_t *)pointer + 1;
+#ifdef _SECURE_SCANF
+ --array_width;
+#endif /* _SECURE_SCANF */
+ } else
+#else /* _UNICODE */
+ if (fl_wchar_arg) {
+ *(wchar_t UNALIGNED *)pointer = ch;
+ pointer = (wchar_t *)pointer + 1;
+#ifdef _SECURE_SCANF
+ --array_width;
+#endif /* _SECURE_SCANF */
+ } else
+#endif /* _UNICODE */
+ {
+#ifndef _UNICODE
+ *(char *)pointer = (char)ch;
+ pointer = (char *)pointer + 1;
+#ifdef _SECURE_SCANF
+ --array_width;
+#endif /* _SECURE_SCANF */
+#else /* _UNICODE */
+ int temp = 0;
+#ifndef _SECURE_SCANF
+ /* convert wide to multibyte */
+ if (_ERRCHECK_EINVAL_ERANGE(wctomb_s(&temp, (char *)pointer, MB_LEN_MAX, ch)) == 0)
+ {
+ /* do nothing if wctomb fails, errno will be set to EILSEQ */
+ pointer = (char *)pointer + temp;
+ }
+#else /* _SECURE_SCANF */
+ /* convert wide to multibyte */
+ if (array_width >= ((size_t)MB_CUR_MAX))
+ {
+_BEGIN_SECURE_CRT_DEPRECATION_DISABLE
+ temp = wctomb((char *)pointer, ch);
+_END_SECURE_CRT_DEPRECATION_DISABLE
+ }
+ else
+ {
+ char tmpbuf[MB_LEN_MAX];
+_BEGIN_SECURE_CRT_DEPRECATION_DISABLE
+ temp = wctomb(tmpbuf, ch);
+_END_SECURE_CRT_DEPRECATION_DISABLE
+ if (temp > 0 && ((size_t)temp) > array_width)
+ {
+ /* We have exhausted the user's buffer */
+ enomem = 1;
+ break;
+ }
+ memcpy(pointer, tmpbuf, temp);
+ }
+ if (temp > 0)
+ {
+ /* do nothing if wctomb fails, errno will be set to EILSEQ */
+ pointer = (char *)pointer + temp;
+ array_width -= temp;
+ }
+#endif /* _SECURE_SCANF */
+#endif /* _UNICODE */
+ }
+ } /* suppress */
+ else {
+ /* just indicate a match */
+ start = (_TCHAR *)start + 1;
+ }
+ }
+ else {
+ UN_INC(ch);
+ break;
+ }
+ }
+
+ /* make sure something has been matched and, if
+ assignment is not suppressed, null-terminate
+ output string if comchr != c */
+
+#ifdef _SECURE_SCANF
+ if(enomem) {
+ errno = ENOMEM;
+ /* In case of error, blank out the input buffer */
+ if (fl_wchar_arg)
+ {
+ _RESET_STRING(((wchar_t UNALIGNED *)start), original_array_width);
+ }
+ else
+ {
+ _RESET_STRING(((char *)start), original_array_width);
+ }
+
+ goto error_return;
+ }
+#endif /* _SECURE_SCANF */
+
+ if (start != pointer) {
+ if (!suppress) {
+ ++count;
+ if ('c' != comchr) /* null-terminate strings */
+ {
+ if (fl_wchar_arg)
+ {
+ *(wchar_t UNALIGNED *)pointer = L'\0';
+#ifdef _SECURE_SCANF
+ _FILL_STRING(((wchar_t UNALIGNED *)start), original_array_width,
+ ((wchar_t UNALIGNED *)pointer - (wchar_t UNALIGNED *)start + 1))
+#endif /* _SECURE_SCANF */
+ }
+ else
+ {
+ *(char *)pointer = '\0';
+#ifdef _SECURE_SCANF
+ _FILL_STRING(((char *)start), original_array_width,
+ ((char *)pointer - (char *)start + 1))
+#endif /* _SECURE_SCANF */
+ }
+ }
+ }
+ else
+ {
+ // supress set, do nothing
+ }
+ }
+ else
+ goto error_return;
+
+ break;
+
+ case _T('i') : /* could be d, o, or x */
+
+ comchr = _T('d'); /* use as default */
+
+ case _T('x'):
+
+ if (_T('-') == ch) {
+ ++negative;
+
+ goto x_incwidth;
+
+ } else if (_T('+') == ch) {
+x_incwidth:
+ if (!--width && widthset)
+ ++done_flag;
+ else
+ ch = INC();
+ }
+
+ if (_T('0') == ch) {
+
+ if (_T('x') == (_TCHAR)(ch = INC()) || _T('X') == (_TCHAR)ch) {
+ ch = INC();
+ if (widthset) {
+ width -= 2;
+ if (width < 1)
+ ++done_flag;
+ }
+ comchr = _T('x');
+ } else {
+ ++started;
+ if (_T('x') != comchr) {
+ if (widthset && !--width)
+ ++done_flag;
+ comchr = _T('o');
+ }
+ else {
+ /* scanning a hex number that starts */
+ /* with a 0. push back the character */
+ /* currently in ch and restore the 0 */
+ UN_INC(ch);
+ ch = _T('0');
+ }
+ }
+ }
+ goto getnum;
+
+ /* NOTREACHED */
+
+ case _T('p') :
+ /* force %hp to be treated as %p */
+ longone = 1;
+#ifdef _WIN64
+ /* force %p to be 64 bit in WIN64 */
+ ++integer64;
+ num64 = 0;
+#endif /* _WIN64 */
+ case _T('o') :
+ case _T('u') :
+ case _T('d') :
+
+ if (_T('-') == ch) {
+ ++negative;
+
+ goto d_incwidth;
+
+ } else if (_T('+') == ch) {
+d_incwidth:
+ if (!--width && widthset)
+ ++done_flag;
+ else
+ ch = INC();
+ }
+
+getnum:
+#if _INTEGRAL_MAX_BITS >= 64
+ if ( integer64 ) {
+
+ while (!done_flag) {
+
+ if (_T('x') == comchr || _T('p') == comchr)
+
+ if (_ISXDIGIT(ch)) {
+ num64 <<= 4;
+ ch = _hextodec(ch);
+ }
+ else
+ ++done_flag;
+
+ else if (_ISDIGIT(ch))
+
+ if (_T('o') == comchr)
+ if (_T('8') > ch)
+ num64 <<= 3;
+ else {
+ ++done_flag;
+ }
+ else /* _T('d') == comchr */
+ num64 = MUL10(num64);
+
+ else
+ ++done_flag;
+
+ if (!done_flag) {
+ ++started;
+ num64 += ch - _T('0');
+
+ if (widthset && !--width)
+ ++done_flag;
+ else
+ ch = INC();
+ } else
+ UN_INC(ch);
+
+ } /* end of WHILE loop */
+
+ if (negative)
+ num64 = (uint64_t )(-(__int64)num64);
+ }
+ else {
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+ while (!done_flag) {
+
+ if (_T('x') == comchr || _T('p') == comchr)
+
+ if (_ISXDIGIT(ch)) {
+ number = (number << 4);
+ ch = _hextodec(ch);
+ }
+ else
+ ++done_flag;
+
+ else if (_ISDIGIT(ch))
+
+ if (_T('o') == comchr)
+ if (_T('8') > ch)
+ number = (number << 3);
+ else {
+ ++done_flag;
+ }
+ else /* _T('d') == comchr */
+ number = MUL10(number);
+
+ else
+ ++done_flag;
+
+ if (!done_flag) {
+ ++started;
+ number += ch - _T('0');
+
+ if (widthset && !--width)
+ ++done_flag;
+ else
+ ch = INC();
+ } else
+ UN_INC(ch);
+
+ } /* end of WHILE loop */
+
+ if (negative)
+ number = (unsigned long)(-(long)number);
+#if _INTEGRAL_MAX_BITS >= 64
+ }
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+ if (_T('F')==comchr) /* expected ':' in long pointer */
+ started = 0;
+
+ if (started)
+ if (!suppress) {
+
+ ++count;
+assign_num:
+#if _INTEGRAL_MAX_BITS >= 64
+ if ( integer64 )
+ *(__int64 UNALIGNED *)pointer = ( uint64_t )num64;
+ else
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+ if (longone)
+ *(int UNALIGNED *)pointer = (unsigned int)number;
+ else
+ *(short UNALIGNED *)pointer = (unsigned short)number;
+
+ } else /*NULL*/;
+ else
+ goto error_return;
+
+ break;
+
+ case _T('n') : /* char count, don't inc return value */
+ number = charcount;
+ if(!suppress)
+ goto assign_num; /* found in number code above */
+ break;
+
+
+ case _T('e') :
+ /* case _T('E') : */
+ case _T('f') :
+ case _T('g') : /* scan a float */
+ /* case _T('G') : */
+ nFloatStrUsed=0;
+
+ if (_T('-') == ch) {
+ pFloatStr[nFloatStrUsed++] = _T('-');
+ goto f_incwidth;
+
+ } else if (_T('+') == ch) {
+f_incwidth:
+ --width;
+ ch = INC();
+ }
+
+ if (!widthset) /* must watch width */
+ width = -1;
+
+
+ /* now get integral part */
+
+ while (_ISDIGIT(ch) && width--) {
+ ++started;
+ pFloatStr[nFloatStrUsed++] = (char)ch;
+ if (__check_float_string(nFloatStrUsed,
+ &nFloatStrSz,
+ &pFloatStr,
+ floatstring,
+ &malloc_FloatStrFlag
+ )==FALSE) {
+ goto error_return;
+ }
+ ch = INC();
+ }
+
+#ifdef _UNICODE
+ /* convert decimal point to wide-char */
+ /* if mbtowc fails (should never happen), we use L'.' */
+ decimal = L'.';
+ _MBTOWC(&decimal, _INTRN_LOCALE_CONV(_loc_update)->decimal_point, MB_CUR_MAX);
+#else /* _UNICODE */
+
+ decimal=*((_INTRN_LOCALE_CONV(_loc_update))->decimal_point);
+#endif /* _UNICODE */
+
+ /* now check for decimal */
+ if (decimal == (char)ch && width--) {
+ ch = INC();
+ pFloatStr[nFloatStrUsed++] = decimal;
+ if (__check_float_string(nFloatStrUsed,
+ &nFloatStrSz,
+ &pFloatStr,
+ floatstring,
+ &malloc_FloatStrFlag
+ )==FALSE) {
+ goto error_return;
+ }
+
+ while (_ISDIGIT(ch) && width--) {
+ ++started;
+ pFloatStr[nFloatStrUsed++] = (_TCHAR)ch;
+ if (__check_float_string(nFloatStrUsed,
+ &nFloatStrSz,
+ &pFloatStr,
+ floatstring,
+ &malloc_FloatStrFlag
+ )==FALSE) {
+ goto error_return;
+ }
+ ch = INC();
+ }
+ }
+
+ /* now check for exponent */
+
+ if (started && (_T('e') == ch || _T('E') == ch) && width--) {
+ pFloatStr[nFloatStrUsed++] = _T('e');
+ if (__check_float_string(nFloatStrUsed,
+ &nFloatStrSz,
+ &pFloatStr,
+ floatstring,
+ &malloc_FloatStrFlag
+ )==FALSE) {
+ goto error_return;
+ }
+
+ if (_T('-') == (ch = INC())) {
+
+ pFloatStr[nFloatStrUsed++] = _T('-');
+ if (__check_float_string(nFloatStrUsed,
+ &nFloatStrSz,
+ &pFloatStr,
+ floatstring,
+ &malloc_FloatStrFlag
+ )==FALSE) {
+ goto error_return;
+ }
+ goto f_incwidth2;
+
+ } else if (_T('+') == ch) {
+f_incwidth2:
+ if (!width--)
+ ++width;
+ else
+ ch = INC();
+ }
+
+
+ while (_ISDIGIT(ch) && width--) {
+ ++started;
+ pFloatStr[nFloatStrUsed++] = (_TCHAR)ch;
+ if (__check_float_string(nFloatStrUsed,
+ &nFloatStrSz,
+ &pFloatStr,
+ floatstring,
+ &malloc_FloatStrFlag
+ )==FALSE) {
+ goto error_return;
+ }
+ ch = INC();
+ }
+
+ }
+
+ UN_INC(ch);
+
+ if (started)
+ if (!suppress) {
+ ++count;
+ pFloatStr[nFloatStrUsed]= _T('\0');
+#ifdef _UNICODE
+ _WFASSIGN( longone-1, pointer, pFloatStr, (char)decimal, _loc_update.GetLocaleT());
+#else /* _UNICODE */
+ _FASSIGN( longone-1, pointer, pFloatStr, (char)decimal, _loc_update.GetLocaleT());
+#endif /* _UNICODE */
+ } else /*NULL */;
+ else
+ goto error_return;
+
+ break;
+
+
+ default: /* either found '%' or something else */
+
+ if ((int)*format != (int)ch) {
+ UN_INC(ch);
+#ifdef _SECURE_SCANF
+ /* error_return ASSERT's if format_error is true */
+ format_error = TRUE;
+#endif /* _SECURE_SCANF */
+ goto error_return;
+ }
+ else
+ match--; /* % found, compensate for inc below */
+
+ if (!suppress)
+ va_copy(arglist, arglistsave);
+
+ } /* SWITCH */
+
+ match++; /* matched a format field - set flag */
+
+ } /* WHILE (width) */
+
+ else { /* zero-width field in format string */
+ UN_INC(ch); /* check for input error */
+ goto error_return;
+ }
+
+ ++format; /* skip to next char */
+
+ } else /* ('%' != *format) */
+ {
+
+ if ((int)*format++ != (int)(ch = INC()))
+ {
+ UN_INC(ch);
+ goto error_return;
+ }
+#if 0 // we are not supporting multibyte input strings
+#ifndef _UNICODE
+ if (isleadbyte((unsigned char)ch))
+ {
+ int ch2;
+ if ((int)*format++ != (ch2=INC()))
+ {
+ UN_INC(ch2);
+ UN_INC(ch);
+ goto error_return;
+ }
+
+ --charcount; /* only count as one character read */
+ }
+#endif /* _UNICODE */
+#endif
+ }
+
+#ifndef CPRFLAG
+ if ( (_TEOF == ch) && ((*format != _T('%')) || (*(format + 1) != _T('n'))) )
+ break;
+#endif /* CPRFLAG */
+
+ } /* WHILE (*format) */
+
+error_return:
+#if ALLOC_TABLE
+ if (malloc_flag == 1)
+ {
+ _free_crt(table);
+ }
+#endif /* ALLOC_TABLE */
+ if (malloc_FloatStrFlag == 1)
+ {
+ _free_crt(pFloatStr);
+ }
+
+#ifndef CPRFLAG
+ if (_TEOF == ch)
+ /* If any fields were matched or assigned, return count */
+ return ( (count || match) ? count : EOF);
+ else
+#endif /* CPRFLAG */
+#ifdef _SECURE_SCANF
+ if(format_error == TRUE) {
+ _VALIDATE_RETURN( ("Invalid Input Format" && 0), EINVAL, count);
+ }
+#endif /* _SECURE_SCANF */
+ return count;
+
+}
+
+/* _hextodec() returns a value of 0-15 and expects a char 0-9, a-f, A-F */
+/* _inc() is the one place where we put the actual getc code. */
+/* _whiteout() returns the first non-blank character, as defined by isspace() */
+
+static int __cdecl _hextodec ( _TCHAR chr)
+{
+ return _ISDIGIT(chr) ? chr : (chr & ~(_T('a') - _T('A'))) - _T('A') + 10 + _T('0');
+}
+
+#ifdef CPRFLAG
+
+static int __cdecl _inc(void)
+{
+ return (_gettche_nolock());
+}
+
+static void __cdecl _un_inc(int chr)
+{
+ if (_TEOF != chr) {
+ _ungettch_nolock(chr);
+ }
+}
+
+static int __cdecl _whiteout(REG1 int* counter)
+{
+ REG2 int ch;
+
+ do
+ {
+ ++*counter;
+ ch = _inc();
+
+ if (ch == _TEOF)
+ {
+ break;
+ }
+ }
+ while(_istspace((_TUCHAR)ch));
+ return ch;
+}
+
+#else /* CPRFLAG */
+
+static int __cdecl _inc(miniFILE* fileptr)
+{
+ return (_gettc_nolock(fileptr));
+}
+
+static void __cdecl _un_inc(int chr, miniFILE* fileptr)
+{
+ if (_TEOF != chr) {
+ _ungettc_nolock(chr,fileptr);
+ }
+}
+
+static int __cdecl _whiteout(int* counter, miniFILE* fileptr)
+{
+ int ch;
+
+ do
+ {
+ ++*counter;
+ ch = _inc(fileptr);
+
+ if (ch == _TEOF)
+ {
+ break;
+ }
+ }
+ while(_istspace((_TUCHAR)ch));
+ return ch;
+}
+
+#endif /* CPRFLAG */
diff --git a/src/pal/src/safecrt/internal.h b/src/pal/src/safecrt/internal.h
new file mode 100644
index 0000000000..f4220c2b68
--- /dev/null
+++ b/src/pal/src/safecrt/internal.h
@@ -0,0 +1,1097 @@
+// 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.
+
+/***
+*internal.h - contains declarations of internal routines and variables
+*
+
+*
+*Purpose:
+* Declares routines and variables used internally by the C run-time.
+*
+* [Internal]
+*
+****/
+
+#if _MSC_VER > 1000
+#pragma once
+#endif /* _MSC_VER > 1000 */
+
+#ifndef _INC_INTERNAL
+#define _INC_INTERNAL
+
+#include <crtdefs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <cruntime.h>
+#include <limits.h>
+
+/*
+ * Conditionally include windows.h to pick up the definition of
+ * CRITICAL_SECTION.
+ */
+#include <windows.h>
+
+#ifdef _MSC_VER
+#pragma pack(push,_CRT_PACKING)
+#endif /* _MSC_VER */
+
+/* Define function types used in several startup sources */
+
+typedef void (__cdecl *_PVFV)(void);
+typedef int (__cdecl *_PIFV)(void);
+typedef void (__cdecl *_PVFI)(int);
+
+#if _MSC_VER >= 1400 && defined(_M_CEE)
+typedef const void* (__clrcall *_PVFVM)(void);
+typedef int (__clrcall *_PIFVM)(void);
+typedef void (__clrcall *_CPVFV)(void);
+#endif /* _MSC_VER >= 1400 && defined(_M_CEE) */
+
+#if defined (_M_CEE_PURE) || (defined (_DLL) && defined (_M_IX86))
+/* Retained for compatibility with VC++ 5.0 and earlier versions */
+_CRTIMP int * __cdecl __p__commode(void);
+#endif /* defined (_M_CEE_PURE) || (defined (_DLL) && defined (_M_IX86)) */
+#if defined (SPECIAL_CRTEXE) && defined (_DLL)
+ extern int _commode;
+#else /* defined (SPECIAL_CRTEXE) && defined (_DLL) */
+#ifndef _M_CEE_PURE
+_CRTIMP extern int _commode;
+#else /* _M_CEE_PURE */
+#define _commode (*__p___commode())
+#endif /* _M_CEE_PURE */
+#endif /* defined (SPECIAL_CRTEXE) && defined (_DLL) */
+
+#define __IOINFO_TM_ANSI 0 /* Regular Text */
+#define __IOINFO_TM_UTF8 1 /* UTF8 Encoded */
+#define __IOINFO_TM_UTF16LE 2 /* UTF16 Little Endian Encoded */
+
+/*
+ * Control structure for lowio file handles
+ */
+typedef struct {
+ intptr_t osfhnd; /* underlying OS file HANDLE */
+ char osfile; /* attributes of file (e.g., open in text mode?) */
+ char pipech; /* one char buffer for handles opened on pipes */
+ int lockinitflag;
+ CRITICAL_SECTION lock;
+#ifndef _SAFECRT_IMPL
+ /* Not used in the safecrt downlevel. We do not define them, so we cannot use them accidentally */
+ char textmode : 7; /* __IOINFO_TM_ANSI or __IOINFO_TM_UTF8 or __IOINFO_TM_UTF16LE */
+ char unicode : 1; /* Was the file opened as unicode? */
+ char pipech2[2]; /* 2 more peak ahead chars for UNICODE mode */
+#endif /* _SAFECRT_IMPL */
+ } ioinfo;
+
+/*
+ * Definition of IOINFO_L2E, the log base 2 of the number of elements in each
+ * array of ioinfo structs.
+ */
+#define IOINFO_L2E 5
+
+/*
+ * Definition of IOINFO_ARRAY_ELTS, the number of elements in ioinfo array
+ */
+#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
+
+/*
+ * Definition of IOINFO_ARRAYS, maximum number of supported ioinfo arrays.
+ */
+#define IOINFO_ARRAYS 64
+
+#define _NHANDLE_ (IOINFO_ARRAYS * IOINFO_ARRAY_ELTS)
+
+#define _TZ_STRINGS_SIZE 64
+
+/*
+ * Access macros for getting at an ioinfo struct and its fields from a
+ * file handle
+ */
+#define _pioinfo(i) ( __pioinfo[(i) >> IOINFO_L2E] + ((i) & (IOINFO_ARRAY_ELTS - \
+ 1)) )
+#define _osfhnd(i) ( _pioinfo(i)->osfhnd )
+
+#define _osfile(i) ( _pioinfo(i)->osfile )
+
+#define _pipech(i) ( _pioinfo(i)->pipech )
+
+#define _pipech2(i) ( _pioinfo(i)->pipech2 )
+
+#define _textmode(i) ( _pioinfo(i)->textmode )
+
+#define _tm_unicode(i) ( _pioinfo(i)->unicode )
+
+/*
+ * Safer versions of the above macros. Currently, only _osfile_safe is
+ * used.
+ */
+#define _pioinfo_safe(i) ( (((i) != -1) && ((i) != -2)) ? _pioinfo(i) : &__badioinfo )
+
+#define _osfhnd_safe(i) ( _pioinfo_safe(i)->osfhnd )
+
+#define _osfile_safe(i) ( _pioinfo_safe(i)->osfile )
+
+#define _pipech_safe(i) ( _pioinfo_safe(i)->pipech )
+
+#define _pipech2_safe(i) ( _pioinfo_safe(i)->pipech2 )
+
+#ifdef _SAFECRT_IMPL
+/* safecrt does not have support for textmode, so we always return __IOINFO_TM_ANSI */
+#define _textmode_safe(i) __IOINFO_TM_ANSI
+#define _tm_unicode_safe(i) 0
+#else /* _SAFECRT_IMPL */
+#define _textmode_safe(i) ( _pioinfo_safe(i)->textmode )
+#define _tm_unicode_safe(i) ( _pioinfo_safe(i)->unicode )
+#endif /* _SAFECRT_IMPL */
+
+#ifndef _M_CEE_PURE
+#ifdef _SAFECRT_IMPL
+/* We need to get this from the downlevel DLL, even when we build safecrt.lib */
+extern __declspec(dllimport) ioinfo __badioinfo;
+extern __declspec(dllimport) ioinfo * __pioinfo[];
+#else /* _SAFECRT_IMPL */
+/*
+ * Special, static ioinfo structure used only for more graceful handling
+ * of a C file handle value of -1 (results from common errors at the stdio
+ * level).
+ */
+extern _CRTIMP ioinfo __badioinfo;
+
+/*
+ * Array of arrays of control structures for lowio files.
+ */
+extern _CRTIMP ioinfo * __pioinfo[];
+#endif /* _SAFECRT_IMPL */
+#endif /* _M_CEE_PURE */
+
+/*
+ * Current number of allocated ioinfo structures (_NHANDLE_ is the upper
+ * limit).
+ */
+extern int _nhandle;
+
+int __cdecl _alloc_osfhnd(void);
+int __cdecl _free_osfhnd(int);
+int __cdecl _set_osfhnd(int, intptr_t);
+
+/*
+ fileno for stdout, stdin & stderr when there is no console
+*/
+#define _NO_CONSOLE_FILENO (intptr_t)-2
+
+
+extern const char __dnames[];
+extern const char __mnames[];
+
+extern int _days[];
+extern int _lpdays[];
+
+extern __time32_t __cdecl __loctotime32_t(int, int, int, int, int, int, int);
+extern __time64_t __cdecl __loctotime64_t(int, int, int, int, int, int, int);
+
+#ifdef _TM_DEFINED
+extern int __cdecl _isindst(__in struct tm * _Time);
+#endif /* _TM_DEFINED */
+
+extern void __cdecl __tzset(void);
+
+extern int __cdecl _validdrive(unsigned);
+
+/*
+ * If we are only interested in years between 1901 and 2099, we could use this:
+ *
+ * #define IS_LEAP_YEAR(y) (y % 4 == 0)
+ */
+
+#define IS_LEAP_YEAR(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
+
+/*
+ * get the buffer used by gmtime
+ */
+struct tm * __cdecl __getgmtimebuf ();
+
+/*
+ * This variable is in the C start-up; the length must be kept synchronized
+ * It is used by the *cenvarg.c modules
+ */
+
+extern char _acfinfo[]; /* "_C_FILE_INFO=" */
+
+#define CFI_LENGTH 12 /* "_C_FILE_INFO" is 12 bytes long */
+
+
+/*
+ * stdio internals
+ */
+#ifndef _FILE_DEFINED
+struct _iobuf {
+ char *_ptr;
+ int _cnt;
+ char *_base;
+ int _flag;
+ int _file;
+ int _charbuf;
+ int _bufsiz;
+ char *_tmpfname;
+ };
+typedef struct _iobuf FILE;
+#define _FILE_DEFINED
+#endif /* _FILE_DEFINED */
+
+#if !defined (_FILEX_DEFINED) && defined (_WINDOWS_)
+
+/*
+ * Variation of FILE type used for the dynamically allocated portion of
+ * __piob[]. For single thread, _FILEX is the same as FILE. For multithread
+ * models, _FILEX has two fields: the FILE struct and the CRITICAL_SECTION
+ * struct used to serialize access to the FILE.
+ */
+
+typedef struct {
+ FILE f;
+ CRITICAL_SECTION lock;
+ } _FILEX;
+
+
+#define _FILEX_DEFINED
+#endif /* !defined (_FILEX_DEFINED) && defined (_WINDOWS_) */
+
+/*
+ * Number of entries supported in the array pointed to by __piob[]. That is,
+ * the number of stdio-level files which may be open simultaneously. This
+ * is normally set to _NSTREAM_ by the stdio initialization code.
+ */
+extern int _nstream;
+
+/*
+ * Pointer to the array of pointers to FILE/_FILEX structures that are used
+ * to manage stdio-level files.
+ */
+extern void **__piob;
+
+FILE * __cdecl _getstream(void);
+FILE * __cdecl _openfile(__in_z const char * _Filename, __in_z const char * _Mode, __in int _ShFlag, __out FILE * _File);
+FILE * __cdecl _wopenfile(__in_z const wchar_t * _Filename, __in_z const wchar_t * _Mode, __in int _ShFlag, __out FILE * _File);
+void __cdecl _getbuf(__out FILE * _File);
+int __cdecl _filwbuf (__inout FILE * _File);
+int __cdecl _flswbuf(__in int _Ch, __inout FILE * _File);
+void __cdecl _freebuf(__inout FILE * _File);
+int __cdecl _stbuf(__inout FILE * _File);
+void __cdecl _ftbuf(int _Flag, __inout FILE * _File);
+
+#ifdef _SAFECRT_IMPL
+
+int __cdecl _output(__inout FILE * _File, __in_z __format_string const char *_Format, va_list _ArgList);
+int __cdecl _woutput(__inout FILE * _File, __in_z __format_string const wchar_t *_Format, va_list _ArgList);
+int __cdecl _output_s(__inout FILE * _File, __in_z __format_string const char *_Format, va_list _ArgList);
+int __cdecl _output_p(__inout FILE * _File, __in_z __format_string const char *_Format, va_list _ArgList);
+int __cdecl _woutput_s(__inout FILE * _File, __in_z __format_string const wchar_t *_Format, va_list _ArgList);
+int __cdecl _woutput_p(__inout FILE * _File, __in_z __format_string const wchar_t *_Format, va_list _ArgList);
+typedef int (*OUTPUTFN)(FILE *, const char *, va_list);
+typedef int (*WOUTPUTFN)(FILE *, const wchar_t *, va_list);
+
+#else /* _SAFECRT_IMPL */
+
+int __cdecl _output_l(__inout FILE * _File, __in_z __format_string const char *_Format, __in_opt _locale_t _Locale, va_list _ArgList);
+int __cdecl _woutput_l(__inout FILE * _File, __in_z __format_string const wchar_t *_Format, __in_opt _locale_t _Locale, va_list _ArgList);
+int __cdecl _output_s_l(__inout FILE * _File, __in_z __format_string const char *_Format, __in_opt _locale_t _Locale, va_list _ArgList);
+int __cdecl _output_p_l(__inout FILE * _File, __in_z __format_string const char *_Format, __in_opt _locale_t _Locale, va_list _ArgList);
+int __cdecl _woutput_s_l(__inout FILE * _File, __in_z __format_string const wchar_t *_Format, __in_opt _locale_t _Locale, va_list _ArgList);
+int __cdecl _woutput_p_l(__inout FILE * _File, __in_z __format_string const wchar_t *_Format, __in_opt _locale_t _Locale, va_list _ArgList);
+typedef int (*OUTPUTFN)(__inout FILE * _File, const char *, _locale_t, va_list);
+typedef int (*WOUTPUTFN)(__inout FILE * _File, const wchar_t *, _locale_t, va_list);
+
+#endif /* _SAFECRT_IMPL */
+
+#ifdef _SAFECRT_IMPL
+
+int __cdecl _input(__in FILE * _File, __in_z __format_string const unsigned char * _Format, va_list _ArgList);
+int __cdecl _winput(__in FILE * _File, __in_z __format_string const wchar_t * _Format, va_list _ArgList);
+int __cdecl _input_s(__in FILE * _File, __in_z __format_string const unsigned char * _Format, va_list _ArgList);
+int __cdecl _winput_s(__in FILE * _File, __in_z __format_string const wchar_t * _Format, va_list _ArgList);
+typedef int (*INPUTFN)(FILE *, const unsigned char *, va_list);
+typedef int (*WINPUTFN)(FILE *, const wchar_t *, va_list);
+
+#else /* _SAFECRT_IMPL */
+
+int __cdecl _input_l(__inout FILE * _File, __in_z __format_string const unsigned char *, __in_opt _locale_t _Locale, va_list _ArgList);
+int __cdecl _winput_l(__inout FILE * _File, __in_z __format_string const wchar_t *, __in_opt _locale_t _Locale, va_list _ArgList);
+int __cdecl _input_s_l(__inout FILE * _File, __in_z __format_string const unsigned char *, __in_opt _locale_t _Locale, va_list _ArgList);
+int __cdecl _winput_s_l(__inout FILE * _File, __in_z __format_string const wchar_t *, __in_opt _locale_t _Locale, va_list _ArgList);
+typedef int (*INPUTFN)(FILE *, const unsigned char *, _locale_t, va_list);
+typedef int (*WINPUTFN)(FILE *, const wchar_t *, _locale_t, va_list);
+
+#ifdef _UNICODE
+#define TINPUTFN WINPUTFN
+#else /* _UNICODE */
+#define TINPUTFN INPUTFN
+#endif /* _UNICODE */
+
+#endif /* _SAFECRT_IMPL */
+
+int __cdecl _flush(__inout FILE * _File);
+void __cdecl _endstdio(void);
+
+errno_t __cdecl _sopen_helper(__in_z const char * _Filename,
+ __in int _OFlag, __in int _ShFlag, __in int _PMode,
+ __out int * _PFileHandle, int _BSecure);
+errno_t __cdecl _wsopen_helper(__in_z const wchar_t * _Filename,
+ __in int _OFlag, __in int _ShFlag, __in int _PMode,
+ __out int * _PFileHandle, int _BSecure);
+
+#ifndef CRTDLL
+extern int _cflush;
+#endif /* CRTDLL */
+
+extern unsigned int _tempoff;
+
+extern unsigned int _old_pfxlen;
+
+extern int _umaskval; /* the umask value */
+
+extern char _pipech[]; /* pipe lookahead */
+
+extern char _exitflag; /* callable termination flag */
+
+extern int _C_Termination_Done; /* termination done flag */
+
+char * __cdecl _getpath(__in_z const char * _Src, __out_ecount_z(_SizeInChars) char * _Dst, __in size_t _SizeInChars);
+wchar_t * __cdecl _wgetpath(__in_z const wchar_t * _Src, __out_ecount_z(_SizeInWords) wchar_t * _Dst, __in size_t _SizeInWords);
+
+extern int _dowildcard; /* flag to enable argv[] wildcard expansion */
+
+#ifndef _PNH_DEFINED
+typedef int (__cdecl * _PNH)( size_t );
+#define _PNH_DEFINED
+#endif /* _PNH_DEFINED */
+
+#if _MSC_VER >= 1400 && defined(_M_CEE)
+#ifndef __MPNH_DEFINED
+typedef int (__clrcall * __MPNH)( size_t );
+#define __MPNH_DEFINED
+#endif /* __MPNH_DEFINED */
+#endif /* _MSC_VER >= 1400 && defined(_M_CEE) */
+
+
+/* calls the currently installed new handler */
+int __cdecl _callnewh(__in size_t _Size);
+
+extern int _newmode; /* malloc new() handler mode */
+
+/* pointer to initial environment block that is passed to [w]main */
+#ifndef _M_CEE_PURE
+extern _CRTIMP wchar_t **__winitenv;
+extern _CRTIMP char **__initenv;
+#endif /* _M_CEE_PURE */
+
+/* _calloca helper */
+#define _calloca(count, size) ((count<=0 || size<=0 || ((((size_t)_HEAP_MAXREQ) / ((size_t)count)) < ((size_t)size)))? NULL : _malloca(count * size))
+
+/* startup set values */
+extern char *_aenvptr; /* environment ptr */
+extern wchar_t *_wenvptr; /* wide environment ptr */
+
+/* command line */
+
+#if defined (_DLL)
+_CRTIMP char ** __cdecl __p__acmdln(void);
+_CRTIMP wchar_t ** __cdecl __p__wcmdln(void);
+#endif /* defined (_DLL) */
+#ifndef _M_CEE_PURE
+_CRTIMP extern char *_acmdln;
+_CRTIMP extern wchar_t *_wcmdln;
+#else /* _M_CEE_PURE */
+#define _acmdln (*__p__acmdln())
+#define _wcmdln (*__p__wcmdln())
+#endif /* _M_CEE_PURE */
+
+/*
+ * prototypes for internal startup functions
+ */
+int __cdecl _cwild(void); /* wild.c */
+int __cdecl _wcwild(void); /* wwild.c */
+int __cdecl _mtinit(void); /* tidtable.c */
+void __cdecl _mtterm(void); /* tidtable.c */
+int __cdecl _mtinitlocks(void); /* mlock.c */
+void __cdecl _mtdeletelocks(void); /* mlock.c */
+int __cdecl _mtinitlocknum(int); /* mlock.c */
+
+/* Wrapper for InitializeCriticalSection API, with default spin count */
+int __cdecl __crtInitCritSecAndSpinCount(PCRITICAL_SECTION, DWORD);
+#define _CRT_SPINCOUNT 4000
+
+/*
+ * C source build only!!!!
+ *
+ * more prototypes for internal startup functions
+ */
+void __cdecl _amsg_exit(int); /* crt0.c */
+void __cdecl __crtExitProcess(int); /* crt0dat.c */
+void __cdecl __crtCorExitProcess(int); /* crt0dat.c */
+void __cdecl __crtdll_callstaticterminators(void); /* crt0dat.c */
+
+/*
+_cinit now allows the caller to suppress floating point precision init
+This allows the DLLs that use the CRT to not initialise FP precision,
+allowing the EXE's setting to persist even when a DLL is loaded
+*/
+int __cdecl _cinit(int /* initFloatingPrecision */); /* crt0dat.c */
+void __cdecl __doinits(void); /* astart.asm */
+void __cdecl __doterms(void); /* astart.asm */
+void __cdecl __dopreterms(void); /* astart.asm */
+void __cdecl _FF_MSGBANNER(void);
+void __cdecl _fpmath(int /*initPrecision*/);
+void __cdecl _fpclear(void);
+void __cdecl _fptrap(void); /* crt0fp.c */
+int __cdecl _heap_init(int);
+void __cdecl _heap_term(void);
+void __cdecl _heap_abort(void);
+void __cdecl __initconin(void); /* initcon.c */
+void __cdecl __initconout(void); /* initcon.c */
+int __cdecl _ioinit(void); /* crt0.c, crtlib.c */
+void __cdecl _ioterm(void); /* crt0.c, crtlib.c */
+char * __cdecl _GET_RTERRMSG(int);
+void __cdecl _NMSG_WRITE(int);
+int __CRTDECL _setargv(void); /* setargv.c, stdargv.c */
+int __CRTDECL __setargv(void); /* stdargv.c */
+int __CRTDECL _wsetargv(void); /* wsetargv.c, wstdargv.c */
+int __CRTDECL __wsetargv(void); /* wstdargv.c */
+int __cdecl _setenvp(void); /* stdenvp.c */
+int __cdecl _wsetenvp(void); /* wstdenvp.c */
+void __cdecl __setmbctable(unsigned int); /* mbctype.c */
+
+#ifdef MRTDLL
+_MRTIMP int __cdecl _onexit_process(_CPVFV);
+_MRTIMP int __cdecl _onexit_app_domain(_CPVFV);
+#endif /* MRTDLL */
+
+#ifdef _MBCS
+int __cdecl __initmbctable(void); /* mbctype.c */
+#endif /* _MBCS */
+
+#ifndef _MANAGED_MAIN
+int __CRTDECL main(__in int _Argc, __in_ecount_z(_Argc) char ** _Argv, __in_z char ** _Env);
+int __CRTDECL wmain(__in int _Argc, __in_ecount_z(_Argc) wchar_t ** _Argv, __in_z wchar_t ** _Env);
+#endif /* _MANAGED_MAIN */
+
+/* helper functions for wide/multibyte environment conversion */
+int __cdecl __mbtow_environ (void);
+int __cdecl __wtomb_environ (void);
+
+/* These two functions take a char ** for the environment option
+ At some point during their execution, they take ownership of the
+ memory block passed in using option. At this point, they
+ NULL out the incoming char * / wchar_t * to ensure there is no
+ double-free
+*/
+int __cdecl __crtsetenv (__deref_inout_opt char ** _POption, __in const int _Primary);
+int __cdecl __crtwsetenv (__deref_inout_opt wchar_t ** _POption, __in const int _Primary);
+
+#ifndef _M_CEE_PURE
+_CRTIMP extern void (__cdecl * _aexit_rtn)(int);
+#endif /* _M_CEE_PURE */
+
+#if defined (_DLL) || defined (CRTDLL)
+
+#ifndef _STARTUP_INFO_DEFINED
+typedef struct
+{
+ int newmode;
+} _startupinfo;
+#define _STARTUP_INFO_DEFINED
+#endif /* _STARTUP_INFO_DEFINED */
+
+_CRTIMP int __cdecl __getmainargs(__out int * _Argc, __deref_out_ecount(*_Argc) char *** _Argv,
+ __deref_out_opt char *** _Env, __in int _DoWildCard,
+ __in _startupinfo * _StartInfo);
+
+_CRTIMP int __cdecl __wgetmainargs(__out int * _Argc, __deref_out_ecount(*_Argc)wchar_t *** _Argv,
+ __deref_out_opt wchar_t *** _Env, __in int _DoWildCard,
+ __in _startupinfo * _StartInfo);
+
+#endif /* defined (_DLL) || defined (CRTDLL) */
+
+/*
+ * Prototype, variables and constants which determine how error messages are
+ * written out.
+ */
+#define _UNKNOWN_APP 0
+#define _CONSOLE_APP 1
+#define _GUI_APP 2
+
+extern int __app_type;
+
+#if !defined (_M_CEE_PURE)
+
+extern Volatile<void*> __native_startup_lock;
+
+#define __NO_REASON UINT_MAX
+extern Volatile<unsigned int> __native_dllmain_reason;
+extern Volatile<unsigned int> __native_vcclrit_reason;
+
+#if defined (__cplusplus)
+
+#pragma warning(push)
+#pragma warning(disable: 4483)
+#if _MSC_FULL_VER >= 140050415
+#define _NATIVE_STARTUP_NAMESPACE __identifier("<CrtImplementationDetails>")
+#else /* _MSC_FULL_VER >= 140050415 */
+#define _NATIVE_STARTUP_NAMESPACE __CrtImplementationDetails
+#endif /* _MSC_FULL_VER >= 140050415 */
+
+namespace _NATIVE_STARTUP_NAMESPACE
+{
+ class NativeDll
+ {
+ private:
+ static const unsigned int ProcessDetach = 0;
+ static const unsigned int ProcessAttach = 1;
+ static const unsigned int ThreadAttach = 2;
+ static const unsigned int ThreadDetach = 3;
+ static const unsigned int ProcessVerifier = 4;
+
+ public:
+
+ inline static bool IsInDllMain()
+ {
+ return (__native_dllmain_reason != __NO_REASON);
+ }
+
+ inline static bool IsInProcessAttach()
+ {
+ return (__native_dllmain_reason == ProcessAttach);
+ }
+
+ inline static bool IsInProcessDetach()
+ {
+ return (__native_dllmain_reason == ProcessDetach);
+ }
+
+ inline static bool IsInVcclrit()
+ {
+ return (__native_vcclrit_reason != __NO_REASON);
+ }
+
+ inline static bool IsSafeForManagedCode()
+ {
+ if (!IsInDllMain())
+ {
+ return true;
+ }
+
+ if (IsInVcclrit())
+ {
+ return true;
+ }
+
+ return !IsInProcessAttach() && !IsInProcessDetach();
+ }
+ };
+}
+#pragma warning(pop)
+
+#endif /* defined (__cplusplus) */
+
+#endif /* !defined (_M_CEE_PURE) */
+
+extern int __error_mode;
+
+_CRTIMP void __cdecl __set_app_type(int);
+#if defined (CRTDLL) && !defined (_SYSCRT)
+/*
+ * All these function pointer are used for creating global state of CRT
+ * functions. Either all of them will be set or all of them will be NULL
+ */
+typedef void (__cdecl *_set_app_type_function)(int);
+typedef int (__cdecl *_get_app_type_function)();
+extern _set_app_type_function __set_app_type_server;
+extern _get_app_type_function __get_app_type_server;
+#endif /* defined (CRTDLL) && !defined (_SYSCRT) */
+
+/*
+ * C source build only!!!!
+ *
+ * map Win32 errors into Xenix errno values -- for modules written in C
+ */
+_CRTIMP void __cdecl _dosmaperr(unsigned long);
+extern int __cdecl _get_errno_from_oserr(unsigned long);
+
+/*
+ * internal routines used by the exec/spawn functions
+ */
+
+extern intptr_t __cdecl _dospawn(__in int _Mode, __in_z_opt const char * _Name, __inout_z char * _Cmd, __in_z_opt char * _Env);
+extern intptr_t __cdecl _wdospawn(__in int _Mode, __in_z_opt const wchar_t * _Name, __inout_z wchar_t * _Cmd, __in_z_opt wchar_t * _Env);
+extern int __cdecl _cenvarg(__in_z const char * const * _Argv, __in_z_opt const char * const * _Env,
+ __deref_out_opt char ** _ArgBlk, __deref_out_opt char ** _EnvBlk, __in_z const char *_Name);
+extern int __cdecl _wcenvarg(__in_z const wchar_t * const * _Argv, __in_z_opt const wchar_t * const * _Env,
+ __deref_out_opt wchar_t ** _ArgBlk, __deref_out_opt wchar_t ** _EnvBlk, __in_z const wchar_t * _Name);
+#ifndef _M_IX86
+extern char ** _capture_argv(__in va_list *, __in_z const char * _FirstArg, __out_ecount_z(_MaxCount) char ** _Static_argv, __in size_t _MaxCount);
+extern wchar_t ** _wcapture_argv(__in va_list *, __in_z const wchar_t * _FirstArg, __out_ecount_z(_MaxCount) wchar_t ** _Static_argv, __in size_t _MaxCount);
+#endif /* _M_IX86 */
+
+/*
+ * internal routine used by the abort
+ */
+
+extern _PHNDLR __cdecl __get_sigabrt(void);
+
+/*
+ * Type from ntdef.h
+ */
+
+typedef LONG NTSTATUS;
+
+/*
+ * Exception code used in _invalid_parameter
+ */
+
+#ifndef STATUS_INVALID_PARAMETER
+#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
+#endif /* STATUS_INVALID_PARAMETER */
+
+/*
+ * Exception code used for abort and _CALL_REPORTFAULT
+ */
+
+#ifndef STATUS_FATAL_APP_EXIT
+#define STATUS_FATAL_APP_EXIT ((NTSTATUS)0x40000015L)
+#endif /* STATUS_FATAL_APP_EXIT */
+
+/*
+ * Validate functions
+ */
+#include <crtdbg.h> /* _ASSERTE */
+#include <errno.h>
+
+#define __STR2WSTR(str) L##str
+
+#define _STR2WSTR(str) __STR2WSTR(str)
+
+#define __FILEW__ _STR2WSTR(__FILE__)
+#define __FUNCTIONW__ _STR2WSTR(__FUNCTION__)
+
+/* We completely fill the buffer only in debug (see _SECURECRT__FILL_STRING
+ * and _SECURECRT__FILL_BYTE macros).
+ */
+#if !defined (_SECURECRT_FILL_BUFFER)
+#ifdef _DEBUG
+#define _SECURECRT_FILL_BUFFER 1
+#else /* _DEBUG */
+#define _SECURECRT_FILL_BUFFER 0
+#endif /* _DEBUG */
+#endif /* !defined (_SECURECRT_FILL_BUFFER) */
+
+#ifndef _SAFECRT_IMPL
+/* _invalid_parameter is already defined in safecrt.h and safecrt.lib */
+#if !defined (_NATIVE_WCHAR_T_DEFINED) && defined (_M_CEE_PURE)
+extern "C++"
+#endif /* !defined (_NATIVE_WCHAR_T_DEFINED) && defined (_M_CEE_PURE) */
+_CRTIMP
+#endif /* _SAFECRT_IMPL */
+void __cdecl _invalid_parameter(__in_z_opt const wchar_t *, __in_z_opt const wchar_t *, __in_z_opt const wchar_t *, unsigned int, uintptr_t);
+
+#if !defined (_NATIVE_WCHAR_T_DEFINED) && defined (_M_CEE_PURE)
+extern "C++"
+#endif /* !defined (_NATIVE_WCHAR_T_DEFINED) && defined (_M_CEE_PURE) */
+_CRTIMP
+void __cdecl _invoke_watson(__in_z_opt const wchar_t *, __in_z_opt const wchar_t *, __in_z_opt const wchar_t *, unsigned int, uintptr_t);
+
+#ifndef _DEBUG
+#if !defined (_NATIVE_WCHAR_T_DEFINED) && defined (_M_CEE_PURE)
+extern "C++"
+#endif /* !defined (_NATIVE_WCHAR_T_DEFINED) && defined (_M_CEE_PURE) */
+_CRTIMP
+void __cdecl _invalid_parameter_noinfo(void);
+#endif /* _DEBUG */
+
+/* Invoke Watson if _ExpressionError is not 0; otherwise simply return _EspressionError */
+__forceinline
+void _invoke_watson_if_error(
+ errno_t _ExpressionError,
+ const wchar_t *_Expression,
+ const wchar_t *_Function,
+ const wchar_t *_File,
+ unsigned int _Line,
+ uintptr_t _Reserved
+ )
+{
+ if (_ExpressionError == 0)
+ {
+ return;
+ }
+ _invoke_watson(_Expression, _Function, _File, _Line, _Reserved);
+}
+
+/* Invoke Watson if _ExpressionError is not 0 and equal to _ErrorValue1 or _ErrorValue2; otherwise simply return _EspressionError */
+__forceinline
+errno_t _invoke_watson_if_oneof(
+ errno_t _ExpressionError,
+ errno_t _ErrorValue1,
+ errno_t _ErrorValue2,
+ const wchar_t *_Expression,
+ const wchar_t *_Function,
+ const wchar_t *_File,
+ unsigned int _Line,
+ uintptr_t _Reserved
+ )
+{
+ if (_ExpressionError == 0 || (_ExpressionError != _ErrorValue1 && _ExpressionError != _ErrorValue2))
+ {
+ return _ExpressionError;
+ }
+ _invoke_watson(_Expression, _Function, _File, _Line, _Reserved);
+ return _ExpressionError;
+}
+
+/*
+ * Assert in debug builds.
+ * set errno and return
+ *
+ */
+#ifdef _DEBUG
+#define _CALL_INVALID_PARAMETER_FUNC(funcname, expr) funcname(expr, __FUNCTIONW__, __FILEW__, __LINE__, 0)
+#define _INVOKE_WATSON_IF_ERROR(expr) _invoke_watson_if_error((expr), __STR2WSTR(#expr), __FUNCTIONW__, __FILEW__, __LINE__, 0)
+#define _INVOKE_WATSON_IF_ONEOF(expr, errvalue1, errvalue2) _invoke_watson_if_oneof(expr, (errvalue1), (errvalue2), __STR2WSTR(#expr), __FUNCTIONW__, __FILEW__, __LINE__, 0)
+#else /* _DEBUG */
+#define _CALL_INVALID_PARAMETER_FUNC(funcname, expr) funcname(NULL, NULL, NULL, 0, 0)
+#define _INVOKE_WATSON_IF_ERROR(expr) _invoke_watson_if_error(expr, NULL, NULL, NULL, 0, 0)
+#define _INVOKE_WATSON_IF_ONEOF(expr, errvalue1, errvalue2) _invoke_watson_if_oneof((expr), (errvalue1), (errvalue2), NULL, NULL, NULL, 0, 0)
+#endif /* _DEBUG */
+
+#define _INVALID_PARAMETER(expr) _CALL_INVALID_PARAMETER_FUNC(_invalid_parameter, expr)
+
+#define _VALIDATE_RETURN_VOID( expr, errorcode ) \
+ { \
+ int _Expr_val=!!(expr); \
+ _ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) ); \
+ if ( !( _Expr_val ) ) \
+ { \
+ errno = errorcode; \
+ _INVALID_PARAMETER(_CRT_WIDE(#expr)); \
+ return; \
+ } \
+ }
+
+/*
+ * Assert in debug builds.
+ * set errno and return value
+ */
+
+#ifndef _VALIDATE_RETURN
+#define _VALIDATE_RETURN( expr, errorcode, retexpr ) \
+ { \
+ int _Expr_val=!!(expr); \
+ _ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) ); \
+ if ( !( _Expr_val ) ) \
+ { \
+ errno = errorcode; \
+ _INVALID_PARAMETER(_CRT_WIDE(#expr) ); \
+ return ( retexpr ); \
+ } \
+ }
+#endif /* _VALIDATE_RETURN */
+
+#ifndef _VALIDATE_RETURN_NOEXC
+#define _VALIDATE_RETURN_NOEXC( expr, errorcode, retexpr ) \
+ { \
+ if ( !(expr) ) \
+ { \
+ errno = errorcode; \
+ return ( retexpr ); \
+ } \
+ }
+#endif /* _VALIDATE_RETURN_NOEXC */
+
+/*
+ * Assert in debug builds.
+ * set errno and set retval for later usage
+ */
+
+#define _VALIDATE_SETRET( expr, errorcode, retval, retexpr ) \
+ { \
+ int _Expr_val=!!(expr); \
+ _ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) ); \
+ if ( !( _Expr_val ) ) \
+ { \
+ errno = errorcode; \
+ _INVALID_PARAMETER(_CRT_WIDE(#expr)); \
+ retval=( retexpr ); \
+ } \
+ }
+
+#define _CHECK_FH_RETURN( handle, errorcode, retexpr ) \
+ { \
+ if(handle == _NO_CONSOLE_FILENO) \
+ { \
+ errno = errorcode; \
+ return ( retexpr ); \
+ } \
+ }
+
+/*
+ We use _VALIDATE_STREAM_ANSI_RETURN to ensure that ANSI file operations(
+ fprintf etc) aren't called on files opened as UNICODE. We do this check
+ only if it's an actual FILE pointer & not a string
+*/
+
+#define _VALIDATE_STREAM_ANSI_RETURN( stream, errorcode, retexpr ) \
+ { \
+ FILE *_Stream=stream; \
+ _VALIDATE_RETURN(( (_Stream->_flag & _IOSTRG) || \
+ ( (_textmode_safe(_fileno(_Stream)) == __IOINFO_TM_ANSI) && \
+ !_tm_unicode_safe(_fileno(_Stream)))), \
+ errorcode, retexpr) \
+ }
+
+/*
+ We use _VALIDATE_STREAM_ANSI_SETRET to ensure that ANSI file operations(
+ fprintf etc) aren't called on files opened as UNICODE. We do this check
+ only if it's an actual FILE pointer & not a string. It doesn't actually return
+ immediately
+*/
+
+#define _VALIDATE_STREAM_ANSI_SETRET( stream, errorcode, retval, retexpr) \
+ { \
+ FILE *_Stream=stream; \
+ _VALIDATE_SETRET(( (_Stream->_flag & _IOSTRG) || \
+ ( (_textmode_safe(_fileno(_Stream)) == __IOINFO_TM_ANSI) && \
+ !_tm_unicode_safe(_fileno(_Stream)))), \
+ errorcode, retval, retexpr) \
+ }
+
+/*
+ * Assert in debug builds.
+ * Return value (do not set errno)
+ */
+
+#define _VALIDATE_RETURN_NOERRNO( expr, retexpr ) \
+ { \
+ int _Expr_val=!!(expr); \
+ _ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) ); \
+ if ( !( _Expr_val ) ) \
+ { \
+ _INVALID_PARAMETER(_CRT_WIDE(#expr)); \
+ return ( retexpr ); \
+ } \
+ }
+
+/*
+ * Assert in debug builds.
+ * set errno and return errorcode
+ */
+
+#define _VALIDATE_RETURN_ERRCODE( expr, errorcode ) \
+ { \
+ int _Expr_val=!!(expr); \
+ _ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) ); \
+ if ( !( _Expr_val ) ) \
+ { \
+ errno = errorcode; \
+ _INVALID_PARAMETER(_CRT_WIDE(#expr)); \
+ return ( errorcode ); \
+ } \
+ }
+
+#define _VALIDATE_RETURN_ERRCODE_NOEXC( expr, errorcode ) \
+ { \
+ if (!(expr)) \
+ { \
+ errno = errorcode; \
+ return ( errorcode ); \
+ } \
+ }
+
+#define _VALIDATE_CLEAR_OSSERR_RETURN( expr, errorcode, retexpr ) \
+ { \
+ int _Expr_val=!!(expr); \
+ _ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) ); \
+ if ( !( _Expr_val ) ) \
+ { \
+ _doserrno = 0L; \
+ errno = errorcode; \
+ _INVALID_PARAMETER(_CRT_WIDE(#expr) ); \
+ return ( retexpr ); \
+ } \
+ }
+
+#define _CHECK_FH_CLEAR_OSSERR_RETURN( handle, errorcode, retexpr ) \
+ { \
+ if(handle == _NO_CONSOLE_FILENO) \
+ { \
+ _doserrno = 0L; \
+ errno = errorcode; \
+ return ( retexpr ); \
+ } \
+ }
+
+#define _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE( expr, errorcode ) \
+ { \
+ int _Expr_val=!!(expr); \
+ _ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) ); \
+ if ( !( _Expr_val ) ) \
+ { \
+ _doserrno = 0L; \
+ errno = errorcode; \
+ _INVALID_PARAMETER(_CRT_WIDE(#expr)); \
+ return ( errorcode ); \
+ } \
+ }
+
+#define _CHECK_FH_CLEAR_OSSERR_RETURN_ERRCODE( handle, retexpr ) \
+ { \
+ if(handle == _NO_CONSOLE_FILENO) \
+ { \
+ _doserrno = 0L; \
+ return ( retexpr ); \
+ } \
+ }
+
+#ifdef _DEBUG
+extern size_t __crtDebugFillThreshold;
+#endif /* _DEBUG */
+
+#if !defined (_SECURECRT_FILL_BUFFER_THRESHOLD)
+#ifdef _DEBUG
+#define _SECURECRT_FILL_BUFFER_THRESHOLD __crtDebugFillThreshold
+#else /* _DEBUG */
+#define _SECURECRT_FILL_BUFFER_THRESHOLD ((size_t)0)
+#endif /* _DEBUG */
+#endif /* !defined (_SECURECRT_FILL_BUFFER_THRESHOLD) */
+
+#if _SECURECRT_FILL_BUFFER
+#define _SECURECRT__FILL_STRING(_String, _Size, _Offset) \
+ if ((_Size) != ((size_t)-1) && (_Size) != INT_MAX && \
+ ((size_t)(_Offset)) < (_Size)) \
+ { \
+ memset((_String) + (_Offset), \
+ _SECURECRT_FILL_BUFFER_PATTERN, \
+ (_SECURECRT_FILL_BUFFER_THRESHOLD < ((size_t)((_Size) - (_Offset))) ? \
+ _SECURECRT_FILL_BUFFER_THRESHOLD : \
+ ((_Size) - (_Offset))) * sizeof(*(_String))); \
+ }
+#else /* _SECURECRT_FILL_BUFFER */
+#define _SECURECRT__FILL_STRING(_String, _Size, _Offset)
+#endif /* _SECURECRT_FILL_BUFFER */
+
+#if _SECURECRT_FILL_BUFFER
+#define _SECURECRT__FILL_BYTE(_Position) \
+ if (_SECURECRT_FILL_BUFFER_THRESHOLD > 0) \
+ { \
+ (_Position) = _SECURECRT_FILL_BUFFER_PATTERN; \
+ }
+#else /* _SECURECRT_FILL_BUFFER */
+#define _SECURECRT__FILL_BYTE(_Position)
+#endif /* _SECURECRT_FILL_BUFFER */
+
+#ifdef __cplusplus
+#define _REDIRECT_TO_L_VERSION_FUNC_PROLOGUE extern "C"
+#else /* __cplusplus */
+#define _REDIRECT_TO_L_VERSION_FUNC_PROLOGUE
+#endif /* __cplusplus */
+
+/* helper macros to redirect an mbs function to the corresponding _l version */
+#define _REDIRECT_TO_L_VERSION_1(_ReturnType, _FunctionName, _Type1) \
+ _REDIRECT_TO_L_VERSION_FUNC_PROLOGUE \
+ _ReturnType __cdecl _FunctionName(_Type1 _Arg1) \
+ { \
+ return _FunctionName##_l(_Arg1, NULL); \
+ }
+
+#define _REDIRECT_TO_L_VERSION_2(_ReturnType, _FunctionName, _Type1, _Type2) \
+ _REDIRECT_TO_L_VERSION_FUNC_PROLOGUE \
+ _ReturnType __cdecl _FunctionName(_Type1 _Arg1, _Type2 _Arg2) \
+ { \
+ return _FunctionName##_l(_Arg1, _Arg2, NULL); \
+ }
+
+#define _REDIRECT_TO_L_VERSION_3(_ReturnType, _FunctionName, _Type1, _Type2, _Type3) \
+ _REDIRECT_TO_L_VERSION_FUNC_PROLOGUE \
+ _ReturnType __cdecl _FunctionName(_Type1 _Arg1, _Type2 _Arg2, _Type3 _Arg3) \
+ { \
+ return _FunctionName##_l(_Arg1, _Arg2, _Arg3, NULL); \
+ }
+
+#define _REDIRECT_TO_L_VERSION_4(_ReturnType, _FunctionName, _Type1, _Type2, _Type3, _Type4) \
+ _REDIRECT_TO_L_VERSION_FUNC_PROLOGUE \
+ _ReturnType __cdecl _FunctionName(_Type1 _Arg1, _Type2 _Arg2, _Type3 _Arg3, _Type4 _Arg4) \
+ { \
+ return _FunctionName##_l(_Arg1, _Arg2, _Arg3, _Arg4, NULL); \
+ }
+
+#define _REDIRECT_TO_L_VERSION_5(_ReturnType, _FunctionName, _Type1, _Type2, _Type3, _Type4, _Type5) \
+ _REDIRECT_TO_L_VERSION_FUNC_PROLOGUE \
+ _ReturnType __cdecl _FunctionName(_Type1 _Arg1, _Type2 _Arg2, _Type3 _Arg3, _Type4 _Arg4, _Type5 _Arg5) \
+ { \
+ return _FunctionName##_l(_Arg1, _Arg2, _Arg3, _Arg4, _Arg5, NULL); \
+ }
+
+#define _REDIRECT_TO_L_VERSION_6(_ReturnType, _FunctionName, _Type1, _Type2, _Type3, _Type4, _Type5, _Type6) \
+ _REDIRECT_TO_L_VERSION_FUNC_PROLOGUE \
+ _ReturnType __cdecl _FunctionName(_Type1 _Arg1, _Type2 _Arg2, _Type3 _Arg3, _Type4 _Arg4, _Type5 _Arg5, _Type6 _Arg6) \
+ { \
+ return _FunctionName##_l(_Arg1, _Arg2, _Arg3, _Arg4, _Arg5, _Arg6, NULL); \
+ }
+
+/* internal helper functions for encoding and decoding pointers */
+void __cdecl _init_pointers();
+_CRTIMP void * __cdecl _encode_pointer(void *);
+_CRTIMP void * __cdecl _encoded_null();
+_CRTIMP void * __cdecl _decode_pointer(void *);
+
+/* internal helper function for communicating with the debugger */
+BOOL DebuggerKnownHandle();
+
+/* Macros to simplify the use of Secure CRT in the CRT itself.
+ * We should use [_BEGIN/_END]_SECURE_CRT_DEPRECATION_DISABLE sparingly.
+ */
+#define _BEGIN_SECURE_CRT_DEPRECATION_DISABLE \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4996))
+
+#define _END_SECURE_CRT_DEPRECATION_DISABLE \
+ __pragma(warning(pop))
+
+#define _ERRCHECK(e) \
+ _INVOKE_WATSON_IF_ERROR(e)
+
+#define _ERRCHECK_EINVAL(e) \
+ _INVOKE_WATSON_IF_ONEOF(e, EINVAL, EINVAL)
+
+#define _ERRCHECK_EINVAL_ERANGE(e) \
+ _INVOKE_WATSON_IF_ONEOF(e, EINVAL, ERANGE)
+
+#define _ERRCHECK_SPRINTF(_PrintfCall) \
+ { \
+ errno_t _SaveErrno = errno; \
+ errno = 0; \
+ if ( ( _PrintfCall ) < 0) \
+ { \
+ _ERRCHECK_EINVAL_ERANGE(errno); \
+ } \
+ errno = _SaveErrno; \
+ }
+
+/* internal helper function to access environment variable in read-only mode */
+const wchar_t * __cdecl _wgetenv_helper_nolock(const wchar_t *);
+const char * __cdecl _getenv_helper_nolock(const char *);
+
+/* internal helper routines used to query a PE image header. */
+BOOL __cdecl _ValidateImageBase(PBYTE pImageBase);
+PIMAGE_SECTION_HEADER __cdecl _FindPESection(PBYTE pImageBase, DWORD_PTR rva);
+BOOL __cdecl _IsNonwritableInCurrentImage(PBYTE pTarget);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#endif /* _INC_INTERNAL */
diff --git a/src/pal/src/safecrt/internal_securecrt.h b/src/pal/src/safecrt/internal_securecrt.h
new file mode 100644
index 0000000000..c38bc4613b
--- /dev/null
+++ b/src/pal/src/safecrt/internal_securecrt.h
@@ -0,0 +1,292 @@
+// 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.
+
+/***
+*internal_securecrt.h - contains declarations of internal routines and variables for securecrt
+*
+
+*
+*Purpose:
+* Declares routines and variables used internally in the SecureCRT implementation.
+* In this include file we define the macros needed to implement the secure functions
+* inlined in the *.inl files like tcscpy_s.inl, etc.
+* Note that this file is used for the CRT implementation, while internal_safecrt is used
+* to build the downlevel library safecrt.lib.
+*
+* [Internal]
+*
+****/
+
+#pragma once
+
+#ifndef _INC_INTERNAL_SECURECRT
+#define _INC_INTERNAL_SECURECRT
+
+/* more VS specific goodness */
+#define __out_ecount_z( x )
+#define __out_ecount( x )
+#define __in_opt
+#define __in_z_opt
+#define __out_ecount_z_opt( x )
+#define __in_z
+#define __in
+
+/*
+ * The original SafeCRT implemention allows runtine control over buffer checking.
+ * For now we'll key this off the debug flag.
+ */
+#ifdef _DEBUG
+ #define _CrtGetCheckCount() ((int)1)
+#else
+ #define _CrtGetCheckCount() ((int)0)
+#endif
+
+/* Assert message and Invalid parameter */
+#ifdef _DEBUG
+ #define _ASSERT_EXPR( val, exp ) \
+ { \
+ if ( ( val ) == 0 ) \
+ { \
+ if ( sMBUSafeCRTAssertFunc != NULL ) \
+ { \
+ ( *sMBUSafeCRTAssertFunc )( #exp, "SafeCRT assert failed", __FILE__, __LINE__ ); \
+ } \
+ } \
+ }
+ #define _INVALID_PARAMETER( exp ) _ASSERT_EXPR( 0, exp )
+ #define _ASSERTE( exp ) _ASSERT_EXPR( exp, exp )
+#else
+ #define _ASSERT_EXPR( val, expr )
+ #define _INVALID_PARAMETER( exp )
+ #define _ASSERTE( exp )
+#endif
+
+/* _TRUNCATE */
+#if !defined (_TRUNCATE)
+#define _TRUNCATE ((size_t)-1)
+#endif /* !defined (_TRUNCATE) */
+
+/* #include <internal.h> */
+
+#define _VALIDATE_RETURN_VOID( expr, errorcode ) \
+ { \
+ int _Expr_val=!!(expr); \
+ _ASSERT_EXPR( ( _Expr_val ), #expr ); \
+ if ( !( _Expr_val ) ) \
+ { \
+ errno = errorcode; \
+ _INVALID_PARAMETER(#expr); \
+ return; \
+ } \
+ }
+
+/*
+ * Assert in debug builds.
+ * set errno and return value
+ */
+
+#ifndef _VALIDATE_RETURN
+#define _VALIDATE_RETURN( expr, errorcode, retexpr ) \
+ { \
+ int _Expr_val=!!(expr); \
+ _ASSERT_EXPR( ( _Expr_val ), #expr ); \
+ if ( !( _Expr_val ) ) \
+ { \
+ errno = errorcode; \
+ _INVALID_PARAMETER(#expr ); \
+ return ( retexpr ); \
+ } \
+ }
+#endif /* _VALIDATE_RETURN */
+
+#ifndef _VALIDATE_RETURN_NOEXC
+#define _VALIDATE_RETURN_NOEXC( expr, errorcode, retexpr ) \
+ { \
+ if ( !(expr) ) \
+ { \
+ errno = errorcode; \
+ return ( retexpr ); \
+ } \
+ }
+#endif /* _VALIDATE_RETURN_NOEXC */
+
+/*
+ * Assert in debug builds.
+ * set errno and return errorcode
+ */
+
+#define _VALIDATE_RETURN_ERRCODE( expr, errorcode ) \
+ { \
+ int _Expr_val=!!(expr); \
+ _ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) ); \
+ if ( !( _Expr_val ) ) \
+ { \
+ errno = errorcode; \
+ _INVALID_PARAMETER(_CRT_WIDE(#expr)); \
+ return ( errorcode ); \
+ } \
+ }
+
+/* We completely fill the buffer only in debug (see _SECURECRT__FILL_STRING
+ * and _SECURECRT__FILL_BYTE macros).
+ */
+#if !defined (_SECURECRT_FILL_BUFFER)
+#ifdef _DEBUG
+#define _SECURECRT_FILL_BUFFER 1
+#else /* _DEBUG */
+#define _SECURECRT_FILL_BUFFER 0
+#endif /* _DEBUG */
+#endif /* !defined (_SECURECRT_FILL_BUFFER) */
+
+/* _SECURECRT_FILL_BUFFER_PATTERN is the same as _bNoMansLandFill */
+#define _SECURECRT_FILL_BUFFER_PATTERN 0xFD
+
+#if !defined (_SECURECRT_FILL_BUFFER_THRESHOLD)
+#ifdef _DEBUG
+#define _SECURECRT_FILL_BUFFER_THRESHOLD ((size_t)8)
+#else /* _DEBUG */
+#define _SECURECRT_FILL_BUFFER_THRESHOLD ((size_t)0)
+#endif /* _DEBUG */
+#endif /* !defined (_SECURECRT_FILL_BUFFER_THRESHOLD) */
+
+#if _SECURECRT_FILL_BUFFER
+#define _SECURECRT__FILL_STRING(_String, _Size, _Offset) \
+ if ((_Size) != ((size_t)-1) && (_Size) != INT_MAX && \
+ ((size_t)(_Offset)) < (_Size)) \
+ { \
+ memset((_String) + (_Offset), \
+ _SECURECRT_FILL_BUFFER_PATTERN, \
+ (_SECURECRT_FILL_BUFFER_THRESHOLD < ((size_t)((_Size) - (_Offset))) ? \
+ _SECURECRT_FILL_BUFFER_THRESHOLD : \
+ ((_Size) - (_Offset))) * sizeof(*(_String))); \
+ }
+#else /* _SECURECRT_FILL_BUFFER */
+#define _SECURECRT__FILL_STRING(_String, _Size, _Offset)
+#endif /* _SECURECRT_FILL_BUFFER */
+
+#if _SECURECRT_FILL_BUFFER
+#define _SECURECRT__FILL_BYTE(_Position) \
+ if (_SECURECRT_FILL_BUFFER_THRESHOLD > 0) \
+ { \
+ (_Position) = _SECURECRT_FILL_BUFFER_PATTERN; \
+ }
+#else /* _SECURECRT_FILL_BUFFER */
+#define _SECURECRT__FILL_BYTE(_Position)
+#endif /* _SECURECRT_FILL_BUFFER */
+
+/* string resetting */
+#define _FILL_STRING _SECURECRT__FILL_STRING
+
+#define _FILL_BYTE _SECURECRT__FILL_BYTE
+
+#define _RESET_STRING(_String, _Size) \
+ { \
+ *(_String) = 0; \
+ _FILL_STRING((_String), (_Size), 1); \
+ }
+
+/* validations */
+#define _VALIDATE_STRING_ERROR(_String, _Size, _Ret) \
+ _VALIDATE_RETURN((_String) != NULL && (_Size) > 0, EINVAL, (_Ret))
+
+#define _VALIDATE_STRING(_String, _Size) \
+ _VALIDATE_STRING_ERROR((_String), (_Size), EINVAL)
+
+#define _VALIDATE_POINTER_ERROR_RETURN(_Pointer, _ErrorCode, _Ret) \
+ _VALIDATE_RETURN((_Pointer) != NULL, (_ErrorCode), (_Ret))
+
+#define _VALIDATE_POINTER_ERROR(_Pointer, _Ret) \
+ _VALIDATE_POINTER_ERROR_RETURN((_Pointer), EINVAL, (_Ret))
+
+#define _VALIDATE_POINTER(_Pointer) \
+ _VALIDATE_POINTER_ERROR((_Pointer), EINVAL)
+
+#define _VALIDATE_CONDITION_ERROR_RETURN(_Condition, _ErrorCode, _Ret) \
+ _VALIDATE_RETURN((_Condition), (_ErrorCode), (_Ret))
+
+#define _VALIDATE_CONDITION_ERROR(_Condition, _Ret) \
+ _VALIDATE_CONDITION_ERROR_RETURN((_Condition), EINVAL, (_Ret))
+
+#define _VALIDATE_POINTER_RESET_STRING_ERROR(_Pointer, _String, _Size, _Ret) \
+ if ((_Pointer) == NULL) \
+ { \
+ _RESET_STRING((_String), (_Size)); \
+ _VALIDATE_POINTER_ERROR_RETURN((_Pointer), EINVAL, (_Ret)) \
+ }
+
+#define _VALIDATE_POINTER_RESET_STRING(_Pointer, _String, _Size) \
+ _VALIDATE_POINTER_RESET_STRING_ERROR((_Pointer), (_String), (_Size), EINVAL)
+
+#define _RETURN_BUFFER_TOO_SMALL_ERROR(_String, _Size, _Ret) \
+ _VALIDATE_RETURN(("Buffer is too small" && 0), ERANGE, _Ret)
+
+#define _RETURN_BUFFER_TOO_SMALL(_String, _Size) \
+ _RETURN_BUFFER_TOO_SMALL_ERROR((_String), (_Size), ERANGE)
+
+#define _RETURN_DEST_NOT_NULL_TERMINATED(_String, _Size) \
+ _VALIDATE_RETURN(("String is not null terminated" && 0), EINVAL, EINVAL)
+
+#define _RETURN_EINVAL \
+ _VALIDATE_RETURN(("Invalid parameter" && 0), EINVAL, EINVAL)
+
+#define _RETURN_ERROR(_Msg, _Ret) \
+ _VALIDATE_RETURN(((_Msg), 0), EINVAL, _Ret)
+
+/* returns without calling _invalid_parameter */
+#define _RETURN_NO_ERROR \
+ return 0
+
+/* Note that _RETURN_TRUNCATE does not set errno */
+#define _RETURN_TRUNCATE \
+ return STRUNCATE
+
+#define _SET_MBCS_ERROR \
+ (errno = EILSEQ)
+
+#define _RETURN_MBCS_ERROR \
+ return _SET_MBCS_ERROR
+
+/* locale dependent */
+#define _LOCALE_ARG \
+ _LocInfo
+
+#define _LOCALE_ARG_DECL \
+ _locale_t _LOCALE_ARG
+
+#define _LOCALE_UPDATE \
+ _LocaleUpdate _LocUpdate(_LOCALE_ARG)
+
+#define _ISMBBLEAD(_Character) \
+ _ismbblead_l((_Character), _LocUpdate.GetLocaleT())
+
+#define _MBSDEC(_String, _Current) \
+ _mbsdec((_String), (_Current))
+
+#define _ISMBBLEADPREFIX(_Result, _StringStart, _BytePtr) \
+ { \
+ unsigned char *_Tmp_VAR, *_StringStart_VAR, *_BytePtr_VAR; \
+ \
+ _StringStart_VAR = (_StringStart); \
+ _BytePtr_VAR = (_BytePtr); \
+ _Tmp_VAR = _BytePtr_VAR; \
+ while ((_Tmp_VAR >= _StringStart_VAR) && _ISMBBLEAD(*_Tmp_VAR)) \
+ { \
+ _Tmp_VAR--; \
+ } \
+ (_Result) = ((_BytePtr_VAR - _Tmp_VAR) & 1) != 0; \
+ }
+
+#define _LOCALE_SHORTCUT_TEST \
+ _LocUpdate.GetLocaleT()->mbcinfo->ismbcodepage == 0
+
+#define _USE_LOCALE_ARG 1
+
+/* misc */
+#define _ASSIGN_IF_NOT_NULL(_Pointer, _Value) \
+ if ((_Pointer) != NULL) \
+ { \
+ *(_Pointer) = (_Value); \
+ }
+
+#endif /* _INC_INTERNAL_SECURECRT */
diff --git a/src/pal/src/safecrt/makepath_s.c b/src/pal/src/safecrt/makepath_s.c
new file mode 100644
index 0000000000..4342685b9c
--- /dev/null
+++ b/src/pal/src/safecrt/makepath_s.c
@@ -0,0 +1,31 @@
+// 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.
+
+/***
+*makepath_s.c - create path name from components
+*
+
+*
+*Purpose:
+* To provide support for creation of full path names from components
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME _makepath_s
+#define _CHAR char
+#define _DEST _Dst
+#define _SIZE _SizeInBytes
+#define _T(_Character) _Character
+
+#define _MBS_SUPPORT 0
+
+#include "tmakepath_s.inl"
diff --git a/src/pal/src/safecrt/mbusafecrt.c b/src/pal/src/safecrt/mbusafecrt.c
new file mode 100644
index 0000000000..ca853d9269
--- /dev/null
+++ b/src/pal/src/safecrt/mbusafecrt.c
@@ -0,0 +1,254 @@
+// 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.
+
+/***
+* mbusafecrt.c - implementaion of support functions and data for MBUSafeCRT
+*
+
+*
+* Purpose:
+* This file contains the implementation of support functions and
+* data for MBUSafeCRT declared in mbusafecrt.h and mbusafecrt_internal.h.
+****/
+
+#include "pal/palinternal.h"
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "mbusafecrt_internal.h"
+
+/* global data */
+tSafeCRT_AssertFuncPtr sMBUSafeCRTAssertFunc = NULL;
+
+/***
+* MBUSafeCRTSetAssertFunc - Set the function called when an assert fails.
+****/
+
+void MBUSafeCRTSetAssertFunc( tSafeCRT_AssertFuncPtr inAssertFuncPtr )
+{
+ /* set it */
+ sMBUSafeCRTAssertFunc = inAssertFuncPtr;
+}
+
+/***
+* _putc_nolock - putc for the miniFILE stream.
+****/
+
+int _putc_nolock( char inChar, miniFILE* inStream )
+{
+ int returnValue = EOF;
+
+ inStream->_cnt -= sizeof( char );
+
+ if ( ( inStream->_cnt ) >= 0 )
+ {
+ *( inStream->_ptr ) = inChar;
+ inStream->_ptr += sizeof( char );
+ returnValue = ( int )inChar;
+ }
+
+ return returnValue;
+}
+
+/***
+* _putwc_nolock - putwc for the miniFILE stream.
+****/
+
+int _putwc_nolock( wchar_t inChar, miniFILE* inStream )
+{
+ int returnValue = WEOF;
+
+ inStream->_cnt -= sizeof( wchar_t );
+
+ if ( ( inStream->_cnt ) >= 0 )
+ {
+ *( ( wchar_t* )( inStream->_ptr ) ) = inChar;
+ inStream->_ptr += sizeof( wchar_t );
+ returnValue = ( int )inChar;
+ }
+
+ return returnValue;
+}
+
+/***
+* _getc_nolock - getc for the miniFILE stream.
+****/
+
+int _getc_nolock( miniFILE* inStream )
+{
+ int returnValue = EOF;
+
+ if ( ( inStream->_cnt ) >= ( int )( sizeof( char ) ) )
+ {
+ inStream->_cnt -= sizeof( char );
+ returnValue = ( int )( *( inStream->_ptr ) );
+ inStream->_ptr += sizeof( char );
+ }
+
+ return returnValue;
+}
+
+/***
+* _getwc_nolock - getc for the miniFILE stream.
+****/
+
+int _getwc_nolock( miniFILE* inStream )
+{
+ int returnValue = EOF;
+
+ if ( ( inStream->_cnt ) >= ( int )( sizeof( wchar_t ) ) )
+ {
+ inStream->_cnt -= sizeof( wchar_t );
+ returnValue = ( int )( *( ( wchar_t* )( inStream->_ptr ) ) );
+ inStream->_ptr += sizeof( wchar_t );
+ }
+
+ return returnValue;
+}
+
+/***
+* _ungetc_nolock - ungetc for the miniFILE stream.
+****/
+
+int _ungetc_nolock( char inChar, miniFILE* inStream )
+{
+ int returnValue = EOF;
+
+ if ( ( size_t )( ( inStream->_ptr ) - ( inStream->_base ) ) >= ( sizeof( char ) ) )
+ {
+ inStream->_cnt += sizeof( char );
+ inStream->_ptr -= sizeof( char );
+ return ( int )inChar;
+ }
+
+ return returnValue;
+}
+
+/***
+* _ungetwc_nolock - ungetwc for the miniFILE stream.
+****/
+
+int _ungetwc_nolock( wchar_t inChar, miniFILE* inStream )
+{
+ int returnValue = WEOF;
+
+ if ( ( size_t )( ( inStream->_ptr ) - ( inStream->_base ) ) >= ( sizeof( wchar_t ) ) )
+ {
+ inStream->_cnt += sizeof( wchar_t );
+ inStream->_ptr -= sizeof( wchar_t );
+ returnValue = ( unsigned short )inChar;
+ }
+
+ return returnValue;
+}
+
+
+/***
+* _safecrt_cfltcvt - convert a float to an ascii string.
+* Uses sprintf - this usage is OK.
+****/
+
+/* routine used for floating-point output */
+#define FORMATSIZE 30
+
+#define _snprintf snprintf
+
+// taken from output.inl
+#define FL_ALTERNATE 0x00080 /* alternate form requested */
+
+errno_t _safecrt_cfltcvt(double *arg, char *buffer, size_t sizeInBytes, int type, int precision, int flags)
+{
+ char format[FORMATSIZE];
+ size_t formatlen = 0;
+ int retvalue;
+
+ if (flags & 1)
+ {
+ type -= 'a' - 'A';
+ }
+ formatlen = 0;
+ format[formatlen++] = '%';
+ if (flags & FL_ALTERNATE)
+ {
+ format[formatlen++] = '#';
+ }
+ format[formatlen++] = '.';
+ _itoa_s(precision, format + formatlen, FORMATSIZE - formatlen, 10);
+ formatlen = strlen(format);
+ format[formatlen++] = (char)type;
+ format[formatlen] = 0;
+
+ buffer[sizeInBytes - 1] = 0;
+ retvalue = _snprintf(buffer, sizeInBytes, format, *arg);
+ if (buffer[sizeInBytes - 1] != 0 || retvalue <= 0)
+ {
+ buffer[0] = 0;
+ return EINVAL;
+ }
+ return 0;
+}
+
+
+/***
+* _safecrt_fassign - convert a string into a float or double.
+****/
+
+void _safecrt_fassign(int flag, void* argument, char* number )
+{
+ if ( flag != 0 ) // double
+ {
+ double dblValue = 0.0;
+ (void)sscanf( number, "%lf", &dblValue );
+ *( ( double* )argument ) = dblValue;
+ }
+ else // float
+ {
+ float fltValue = 0.0;
+ (void)sscanf( number, "%f", &fltValue );
+ *( ( float* )argument ) = fltValue;
+ }
+}
+
+
+/***
+* _safecrt_wfassign - convert a wchar_t string into a float or double.
+****/
+
+void _safecrt_wfassign(int flag, void* argument, wchar_t* number )
+{
+ // We cannot use system functions for this - they
+ // assume that wchar_t is four bytes, while we assume
+ // two. So, we need to convert to a regular char string
+ // without using any system functions. To do this,
+ // we'll assume that the numbers are in the 0-9 range and
+ // do a simple conversion.
+
+ char* numberAsChars = ( char* )number;
+ int position = 0;
+
+ // do the convert
+ while ( number[ position ] != 0 )
+ {
+ numberAsChars[ position ] = ( char )( number[ position ] & 0x00FF );
+ position++;
+ }
+ numberAsChars[ position ] = ( char )( number[ position ] & 0x00FF );
+
+ // call the normal char version
+ _safecrt_fassign( flag, argument, numberAsChars );
+}
+
+
+/***
+* _minimal_chartowchar - do a simple char to wchar conversion.
+****/
+
+int _minimal_chartowchar( wchar_t* outWChar, const char* inChar )
+{
+ *outWChar = ( wchar_t )( ( unsigned short )( ( unsigned char )( *inChar ) ) );
+ return 1;
+}
+
+
diff --git a/src/pal/src/safecrt/mbusafecrt_internal.h b/src/pal/src/safecrt/mbusafecrt_internal.h
new file mode 100644
index 0000000000..9a3aa1ca90
--- /dev/null
+++ b/src/pal/src/safecrt/mbusafecrt_internal.h
@@ -0,0 +1,88 @@
+// 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.
+
+/***
+* mbusafecrt_internal.h - internal declarations for SafeCRT functions
+*
+
+*
+* Purpose:
+* This file contains the internal declarations SafeCRT
+* functions ported to MacOS. These are the safe versions of
+* functions standard functions banned by SWI
+****/
+
+/* shields! */
+
+#ifndef MBUSAFECRT_INTERNAL_H
+#define MBUSAFECRT_INTERNAL_H
+
+#include "pal_char16.h"
+#include "pal_mstypes.h"
+
+typedef __builtin_va_list va_list;
+
+// The ifdef below are to accommodate Unix build
+// that complains about them being declared in stdarg.h already.
+#ifndef va_start
+#define va_start __builtin_va_start
+#endif
+#ifndef va_end
+#define va_end __builtin_va_end
+#endif
+
+#include "mbusafecrt.h"
+
+#ifdef EOF
+#undef EOF
+#endif
+#define EOF -1
+
+#ifdef WEOF
+#undef WEOF
+#endif
+#define WEOF -1
+
+#define CASSERT(p) extern int sanity_check_dummy[1+((!(p))*(-2))];
+
+extern tSafeCRT_AssertFuncPtr sMBUSafeCRTAssertFunc;
+
+typedef struct miniFILE_struct
+{
+ char* _ptr;
+ int _cnt;
+ char* _base;
+ int _flag;
+} miniFILE;
+
+#define _IOSTRG 1
+#define _IOWRT 2
+#define _IOREAD 4
+#define _IOMYBUF 8
+
+int _putc_nolock( char inChar, miniFILE* inStream );
+int _putwc_nolock( wchar_t inChar, miniFILE* inStream );
+int _getc_nolock( miniFILE* inStream );
+int _getwc_nolock( miniFILE* inStream );
+int _ungetc_nolock( char inChar, miniFILE* inStream );
+int _ungetwc_nolock( wchar_t inChar, miniFILE* inStream );
+
+errno_t _safecrt_cfltcvt(double *arg, char *buffer, size_t sizeInBytes, int type, int precision, int flags);
+
+void _safecrt_fassign(int flag, void* argument, char * number );
+void _safecrt_wfassign(int flag, void* argument, wchar_t * number );
+
+int _minimal_chartowchar( wchar_t* outWChar, const char* inChar );
+
+int _output_s( miniFILE* outfile, const char* _Format, va_list _ArgList);
+int _woutput_s( miniFILE* outfile, const wchar_t* _Format, va_list _ArgList);
+int _output( miniFILE *outfile, const char* _Format, va_list _ArgList);
+
+int _soutput_s( char *_Dst, size_t _Size, const char *_Format, va_list _ArgList );
+int _swoutput_s( wchar_t *_Dst, size_t _Size, const wchar_t *_Format, va_list _ArgList );
+
+int __tinput_s( miniFILE* inFile, const unsigned char * inFormat, va_list inArgList );
+int __twinput_s( miniFILE* inFile, const wchar_t * inFormat, va_list inArgList );
+
+#endif /* MBUSAFECRT_INTERNAL_H */
diff --git a/src/pal/src/safecrt/memcpy_s.c b/src/pal/src/safecrt/memcpy_s.c
new file mode 100644
index 0000000000..27aeb79665
--- /dev/null
+++ b/src/pal/src/safecrt/memcpy_s.c
@@ -0,0 +1,82 @@
+// 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.
+
+/***
+*memcpy_s.c - contains memcpy_s routine
+*
+
+*
+*Purpose:
+* memcpy_s() copies a source memory buffer to a destination buffer.
+* Overlapping buffers are not treated specially, so propagation may occur.
+*
+*Revision History:
+* 10-07-03 AC Module created.
+* 03-10-04 AC Return ERANGE when buffer is too small
+* 01-14-05 AC Prefast (espx) fixes
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include "internal_securecrt.h"
+#include "mbusafecrt_internal.h"
+
+/***
+*memcpy_s - Copy source buffer to destination buffer
+*
+*Purpose:
+* memcpy_s() copies a source memory buffer to a destination memory buffer.
+* This routine does NOT recognize overlapping buffers, and thus can lead
+* to propagation.
+*
+* For cases where propagation must be avoided, memmove_s() must be used.
+*
+*Entry:
+* void *dst = pointer to destination buffer
+* size_t sizeInBytes = size in bytes of the destination buffer
+* const void *src = pointer to source buffer
+* size_t count = number of bytes to copy
+*
+*Exit:
+* Returns 0 if everything is ok, else return the error code.
+*
+*Exceptions:
+* Input parameters are validated. Refer to the validation section of the function.
+* On error, the error code is returned and the destination buffer is zeroed.
+*
+*******************************************************************************/
+
+errno_t __cdecl memcpy_s(
+ void * dst,
+ size_t sizeInBytes,
+ const void * src,
+ size_t count
+)
+{
+ if (count == 0)
+ {
+ /* nothing to do */
+ return 0;
+ }
+
+ /* validation section */
+ _VALIDATE_RETURN_ERRCODE(dst != NULL, EINVAL);
+ if (src == NULL || sizeInBytes < count)
+ {
+ /* zeroes the destination buffer */
+ memset(dst, 0, sizeInBytes);
+
+ _VALIDATE_RETURN_ERRCODE(src != NULL, EINVAL);
+ _VALIDATE_RETURN_ERRCODE(sizeInBytes >= count, ERANGE);
+ /* useless, but prefast is confused */
+ return EINVAL;
+ }
+
+ UINT_PTR x = (UINT_PTR)dst, y = (UINT_PTR)src;
+ assert((x + count <= y) || (y + count <= x));
+ memcpy(dst, src, count);
+ return 0;
+}
diff --git a/src/pal/src/safecrt/memmove_s.c b/src/pal/src/safecrt/memmove_s.c
new file mode 100644
index 0000000000..a0ae5f7ea6
--- /dev/null
+++ b/src/pal/src/safecrt/memmove_s.c
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/***
+*memmove_s.c - contains memmove_s routine
+*
+
+*
+*Purpose:
+* memmove_s() copies a source memory buffer to a destination buffer.
+* Overlapping buffers are treated specially, to avoid propagation.
+*
+*Revision History:
+* 10-07-03 AC Module created.
+* 03-10-04 AC Return ERANGE when buffer is too small
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include "internal_securecrt.h"
+#include "mbusafecrt_internal.h"
+
+/***
+*memmove - Copy source buffer to destination buffer
+*
+*Purpose:
+* memmove() copies a source memory buffer to a destination memory buffer.
+* This routine recognize overlapping buffers to avoid propagation.
+*
+* For cases where propagation is not a problem, memcpy_s() can be used.
+*
+*Entry:
+* void *dst = pointer to destination buffer
+* size_t sizeInBytes = size in bytes of the destination buffer
+* const void *src = pointer to source buffer
+* size_t count = number of bytes to copy
+*
+*Exit:
+* Returns 0 if everything is ok, else return the error code.
+*
+*Exceptions:
+* Input parameters are validated. Refer to the validation section of the function.
+* On error, the error code is returned. Nothing is written to the destination buffer.
+*
+*******************************************************************************/
+
+errno_t __cdecl memmove_s(
+ void * dst,
+ size_t sizeInBytes,
+ const void * src,
+ size_t count
+)
+{
+ if (count == 0)
+ {
+ /* nothing to do */
+ return 0;
+ }
+
+ /* validation section */
+ _VALIDATE_RETURN_ERRCODE(dst != NULL, EINVAL);
+ _VALIDATE_RETURN_ERRCODE(src != NULL, EINVAL);
+ _VALIDATE_RETURN_ERRCODE(sizeInBytes >= count, ERANGE);
+
+ memmove(dst, src, count);
+ return 0;
+}
diff --git a/src/pal/src/safecrt/output.inl b/src/pal/src/safecrt/output.inl
new file mode 100644
index 0000000000..ae0692efc5
--- /dev/null
+++ b/src/pal/src/safecrt/output.inl
@@ -0,0 +1,1624 @@
+// 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.
+
+/***
+*output.c - printf style output to a FILE
+*
+
+*
+*Purpose:
+* This file contains the code that does all the work for the
+* printf family of functions. It should not be called directly, only
+* by the *printf functions. We don't make any assumtions about the
+* sizes of ints, longs, shorts, or long doubles, but if types do overlap,
+* we also try to be efficient. We do assume that pointers are the same
+* size as either ints or longs.
+* If CPRFLAG is defined, defines _cprintf instead.
+* **** DOESN'T CURRENTLY DO MTHREAD LOCKING ****
+*
+*Note:
+* this file is included in safecrt.lib build directly, plese refer
+* to safecrt_[w]output_s.c
+*
+*******************************************************************************/
+
+
+//typedef __int64_t __int64;
+
+
+#define FORMAT_VALIDATIONS
+
+typedef double _CRT_DOUBLE;
+
+//typedef int* intptr_t;
+
+/*
+Buffer size required to be passed to _gcvt, fcvt and other fp conversion routines
+*/
+#define _CVTBUFSIZE (309+40) /* # of digits in max. dp value + slop */
+
+/* temporary work-around for compiler without 64-bit support */
+#ifndef _INTEGRAL_MAX_BITS
+#define _INTEGRAL_MAX_BITS 64
+#endif /* _INTEGRAL_MAX_BITS */
+
+//#include <mtdll.h>
+//#include <cruntime.h>
+//#include <limits.h>
+//#include <string.h>
+//#include <stddef.h>
+//#include <crtdefs.h>
+//#include <stdio.h>
+//#include <stdarg.h>
+//#include <cvt.h>
+//#include <conio.h>
+//#include <internal.h>
+//#include <fltintrn.h>
+//#include <stdlib.h>
+//#include <ctype.h>
+//#include <dbgint.h>
+//#include <setlocal.h>
+
+#define _MBTOWC(x,y,z) _minimal_chartowchar( x, y )
+
+#ifndef _WCTOMB_S
+#define _WCTOMB_S wctomb_s
+#endif /* _WCTOMB_S */
+
+#undef _malloc_crt
+#define _malloc_crt malloc
+
+#undef _free_crt
+#define _free_crt free
+
+/* Wrapper for _output_s so that we do not expose FILE in the _output_s signature.
+ * Always ensure null-termination. Returns the number of written chars, not including the terminating null.
+ * Returns -1 if something went wrong during the formatting (in _output_s), e.g. mbcs conversions.
+ * Returns -2 if the string has been truncated.
+ * _output_s calls _invalid_parameter (and returns -1, possibly) if the format string is malformed.
+ */
+#ifndef _UNICODE
+int __cdecl _soutput_s(char *_Dst, size_t _Size, const char *_Format, va_list _ArgList)
+#else /* _UNICODE */
+int __cdecl _swoutput_s(wchar_t *_Dst, size_t _Size, const wchar_t *_Format, va_list _ArgList)
+#endif /* _UNICODE */
+{
+ miniFILE stream;
+ miniFILE *outfile = &stream;
+ int written = -1;
+
+ /* validation section */
+#ifndef _UNICODE
+ if(_Size==SIZE_MAX)
+ {
+ /* user is attempting to make us unbounded, but we don't fit that much */
+ outfile->_cnt = INT_MAX;
+ }
+ else
+ {
+ _VALIDATE_RETURN(_Size <= INT_MAX, EINVAL, -1);
+ outfile->_cnt = (int)_Size;
+ }
+ outfile->_ptr = outfile->_base = _Dst;
+#else /* _UNICODE */
+ if(_Size==SIZE_MAX)
+ {
+ /* user is attempting to make us unbounded, but we don't fit that much */
+ outfile->_cnt = INT_MAX;
+ }
+ else if(_Size>(INT_MAX/sizeof(wchar_t)))
+ {
+ /* we can't represent the amount of output the user asked for */
+ _VALIDATE_RETURN( 0 /* FALSE */, EINVAL, -1 );
+ }
+ else
+ {
+ outfile->_cnt = (int)(_Size*sizeof(wchar_t));
+ }
+ outfile->_ptr = outfile->_base = (char*)_Dst;
+#endif /* _UNICODE */
+ outfile->_flag = _IOWRT | _IOSTRG;
+
+#ifndef _UNICODE
+ written = _output_s(outfile, _Format, _ArgList);
+#else /* _UNICODE */
+ written = _woutput_s(outfile, _Format, _ArgList);
+#endif /* _UNICODE */
+ _Dst[_Size - 1] = 0;
+ if (written < 0)
+ {
+ if (outfile->_cnt < 0)
+ {
+ /* the buffer was too small; we return -2 to indicate truncation */
+ return -2;
+ }
+ /* otherwise, something else failed: we reset the string and we return */
+ if (_Dst != NULL && _Size > 0)
+ {
+ *_Dst = 0;
+ }
+ return written;
+ }
+
+#ifndef _UNICODE
+ if ((_putc_nolock('\0', outfile) != EOF))
+#else /* _UNICODE */
+ if ((_putc_nolock('\0', outfile) != EOF) && (_putc_nolock('\0', outfile) != EOF))
+#endif /* _UNICODE */
+ {
+ return written;
+ }
+ /* the last putc failed, so it means there is not enough space in the buffer */
+ return -2;
+}
+
+
+#ifndef _CFLTCVT
+#define _CFLTCVT _cfltcvt
+#endif /* _CFLTCVT */
+
+#ifndef _CLDCVT
+#define _CLDCVT _cldcvt
+#endif /* _CLDCVT */
+
+#ifdef _MBCS
+#undef _MBCS
+#endif /* _MBCS */
+//#include <tchar.h>
+
+/* this macro defines a function which is private and as fast as possible: */
+/* for example, in C 6.0, it might be static _fastcall <type> near. */
+#define LOCAL(x) static x __cdecl
+
+/* int/long/short/pointer sizes */
+
+/* the following should be set depending on the sizes of various types */
+#if __LP64__
+ #define LONG_IS_INT 0
+ CASSERT(sizeof(long) > sizeof(int));
+#else
+ #define LONG_IS_INT 1 /* 1 means long is same size as int */
+ CASSERT(sizeof(long) == sizeof(int));
+#endif
+
+#define SHORT_IS_INT 0 /* 1 means short is same size as int */
+#define LONGLONG_IS_INT64 1 /* 1 means long long is same as int64 */
+#if defined (_WIN64)
+ #define PTR_IS_INT 0 /* 1 means ptr is same size as int */
+ CASSERT(sizeof(void *) != sizeof(int));
+ #if __LP64__
+ #define PTR_IS_LONG 1 /* 1 means ptr is same size as long */
+ CASSERT(sizeof(void *) == sizeof(long));
+ #else
+ #define PTR_IS_LONG 0 /* 1 means ptr is same size as long */
+ CASSERT(sizeof(void *) != sizeof(long));
+ #endif
+ #define PTR_IS_INT64 1 /* 1 means ptr is same size as int64 */
+ CASSERT(sizeof(void *) == sizeof(int64_t));
+#else /* defined (_WIN64) */
+ #define PTR_IS_INT 1 /* 1 means ptr is same size as int */
+ CASSERT(sizeof(void *) == sizeof(int));
+ #define PTR_IS_LONG 1 /* 1 means ptr is same size as long */
+ CASSERT(sizeof(void *) == sizeof(long));
+ #define PTR_IS_INT64 0 /* 1 means ptr is same size as int64 */
+ CASSERT(sizeof(void *) != sizeof(int64_t));
+#endif /* defined (_WIN64) */
+
+/* CONSTANTS */
+
+/* size of conversion buffer (ANSI-specified minimum is 509) */
+
+#define BUFFERSIZE 512
+#define MAXPRECISION BUFFERSIZE
+
+#if BUFFERSIZE < _CVTBUFSIZE + 6
+/*
+ * Buffer needs to be big enough for default minimum precision
+ * when converting floating point needs bigger buffer, and malloc
+ * fails
+ */
+#error Conversion buffer too small for max double.
+#endif /* BUFFERSIZE < _CVTBUFSIZE + 6 */
+
+/* flag definitions */
+#define FL_SIGN 0x00001 /* put plus or minus in front */
+#define FL_SIGNSP 0x00002 /* put space or minus in front */
+#define FL_LEFT 0x00004 /* left justify */
+#define FL_LEADZERO 0x00008 /* pad with leading zeros */
+#define FL_LONG 0x00010 /* long value given */
+#define FL_SHORT 0x00020 /* short value given */
+#define FL_SIGNED 0x00040 /* signed data given */
+#define FL_ALTERNATE 0x00080 /* alternate form requested */
+#define FL_NEGATIVE 0x00100 /* value is negative */
+#define FL_FORCEOCTAL 0x00200 /* force leading '0' for octals */
+#define FL_LONGDOUBLE 0x00400 /* long double value given */
+#define FL_WIDECHAR 0x00800 /* wide characters */
+#define FL_LONGLONG 0x01000 /* long long value given */
+#define FL_I64 0x08000 /* __int64 value given */
+
+/* state definitions */
+enum STATE {
+ ST_NORMAL, /* normal state; outputting literal chars */
+ ST_PERCENT, /* just read '%' */
+ ST_FLAG, /* just read flag character */
+ ST_WIDTH, /* just read width specifier */
+ ST_DOT, /* just read '.' */
+ ST_PRECIS, /* just read precision specifier */
+ ST_SIZE, /* just read size specifier */
+ ST_TYPE /* just read type specifier */
+#ifdef FORMAT_VALIDATIONS
+ ,ST_INVALID /* Invalid format */
+#endif /* FORMAT_VALIDATIONS */
+
+};
+
+#ifdef FORMAT_VALIDATIONS
+#define NUMSTATES (ST_INVALID + 1)
+#else /* FORMAT_VALIDATIONS */
+#define NUMSTATES (ST_TYPE + 1)
+#endif /* FORMAT_VALIDATIONS */
+
+/* character type values */
+enum CHARTYPE {
+ CH_OTHER, /* character with no special meaning */
+ CH_PERCENT, /* '%' */
+ CH_DOT, /* '.' */
+ CH_STAR, /* '*' */
+ CH_ZERO, /* '0' */
+ CH_DIGIT, /* '1'..'9' */
+ CH_FLAG, /* ' ', '+', '-', '#' */
+ CH_SIZE, /* 'h', 'l', 'L', 'N', 'F', 'w' */
+ CH_TYPE /* type specifying character */
+};
+
+/* static data (read only, since we are re-entrant) */
+//#if defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS)
+//extern const char __nullstring[]; /* string to print on null ptr */
+//extern const wchar_t __wnullstring[]; /* string to print on null ptr */
+//#else /* defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS) */
+static const char __nullstring[] = "(null)"; /* string to print on null ptr */
+static const wchar_t __wnullstring[] = {'(', 'n', 'u', 'l', 'l', ')', '\0'};/* string to print on null ptr */
+//#endif /* defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS) */
+
+/* The state table. This table is actually two tables combined into one. */
+/* The lower nybble of each byte gives the character class of any */
+/* character; while the uper nybble of the byte gives the next state */
+/* to enter. See the macros below the table for details. */
+/* */
+/* The table is generated by maketabc.c -- use this program to make */
+/* changes. */
+
+#ifndef FORMAT_VALIDATIONS
+
+//#if defined (_UNICODE) || defined (CPRFLAG)
+//extern const char __lookuptable[];
+//#else /* defined (_UNICODE) || defined (CPRFLAG) */
+extern const char __lookuptable[] = {
+ /* ' ' */ 0x06,
+ /* '!' */ 0x00,
+ /* '"' */ 0x00,
+ /* '#' */ 0x06,
+ /* '$' */ 0x00,
+ /* '%' */ 0x01,
+ /* '&' */ 0x00,
+ /* ''' */ 0x00,
+ /* '(' */ 0x10,
+ /* ')' */ 0x00,
+ /* '*' */ 0x03,
+ /* '+' */ 0x06,
+ /* ',' */ 0x00,
+ /* '-' */ 0x06,
+ /* '.' */ 0x02,
+ /* '/' */ 0x10,
+ /* '0' */ 0x04,
+ /* '1' */ 0x45,
+ /* '2' */ 0x45,
+ /* '3' */ 0x45,
+ /* '4' */ 0x05,
+ /* '5' */ 0x05,
+ /* '6' */ 0x05,
+ /* '7' */ 0x05,
+ /* '8' */ 0x05,
+ /* '9' */ 0x35,
+ /* ':' */ 0x30,
+ /* ';' */ 0x00,
+ /* '<' */ 0x50,
+ /* '=' */ 0x00,
+ /* '>' */ 0x00,
+ /* '?' */ 0x00,
+ /* '@' */ 0x00,
+ /* 'A' */ 0x20, // Disable %A format
+ /* 'B' */ 0x20,
+ /* 'C' */ 0x38,
+ /* 'D' */ 0x50,
+ /* 'E' */ 0x58,
+ /* 'F' */ 0x07,
+ /* 'G' */ 0x08,
+ /* 'H' */ 0x00,
+ /* 'I' */ 0x37,
+ /* 'J' */ 0x30,
+ /* 'K' */ 0x30,
+ /* 'L' */ 0x57,
+ /* 'M' */ 0x50,
+ /* 'N' */ 0x07,
+ /* 'O' */ 0x00,
+ /* 'P' */ 0x00,
+ /* 'Q' */ 0x20,
+ /* 'R' */ 0x20,
+ /* 'S' */ 0x08,
+ /* 'T' */ 0x00,
+ /* 'U' */ 0x00,
+ /* 'V' */ 0x00,
+ /* 'W' */ 0x00,
+ /* 'X' */ 0x08,
+ /* 'Y' */ 0x60,
+ /* 'Z' */ 0x68,
+ /* '[' */ 0x60,
+ /* '\' */ 0x60,
+ /* ']' */ 0x60,
+ /* '^' */ 0x60,
+ /* '_' */ 0x00,
+ /* '`' */ 0x00,
+ /* 'a' */ 0x70, // Disable %a format
+ /* 'b' */ 0x70,
+ /* 'c' */ 0x78,
+ /* 'd' */ 0x78,
+ /* 'e' */ 0x78,
+ /* 'f' */ 0x78,
+ /* 'g' */ 0x08,
+ /* 'h' */ 0x07,
+ /* 'i' */ 0x08,
+ /* 'j' */ 0x00,
+ /* 'k' */ 0x00,
+ /* 'l' */ 0x07,
+ /* 'm' */ 0x00,
+ /* 'n' */ 0x00, // Disable %n format
+ /* 'o' */ 0x08,
+ /* 'p' */ 0x08,
+ /* 'q' */ 0x00,
+ /* 'r' */ 0x00,
+ /* 's' */ 0x08,
+ /* 't' */ 0x00,
+ /* 'u' */ 0x08,
+ /* 'v' */ 0x00,
+ /* 'w' */ 0x07,
+ /* 'x' */ 0x08
+};
+
+//#endif /* defined (_UNICODE) || defined (CPRFLAG) */
+
+#else /* FORMAT_VALIDATIONS */
+
+//#if defined (_UNICODE) || defined (CPRFLAG)
+//extern const unsigned char __lookuptable_s[];
+//#else /* defined (_UNICODE) || defined (CPRFLAG) */
+static const unsigned char __lookuptable_s[] = {
+ /* ' ' */ 0x06,
+ /* '!' */ 0x80,
+ /* '"' */ 0x80,
+ /* '#' */ 0x86,
+ /* '$' */ 0x80,
+ /* '%' */ 0x81,
+ /* '&' */ 0x80,
+ /* ''' */ 0x00,
+ /* '(' */ 0x00,
+ /* ')' */ 0x10,
+ /* '*' */ 0x03,
+ /* '+' */ 0x86,
+ /* ',' */ 0x80,
+ /* '-' */ 0x86,
+ /* '.' */ 0x82,
+ /* '/' */ 0x80,
+ /* '0' */ 0x14,
+ /* '1' */ 0x05,
+ /* '2' */ 0x05,
+ /* '3' */ 0x45,
+ /* '4' */ 0x45,
+ /* '5' */ 0x45,
+ /* '6' */ 0x85,
+ /* '7' */ 0x85,
+ /* '8' */ 0x85,
+ /* '9' */ 0x05,
+ /* ':' */ 0x00,
+ /* ';' */ 0x00,
+ /* '<' */ 0x30,
+ /* '=' */ 0x30,
+ /* '>' */ 0x80,
+ /* '?' */ 0x50,
+ /* '@' */ 0x80,
+ /* 'A' */ 0x80, // Disable %A format
+ /* 'B' */ 0x00,
+ /* 'C' */ 0x08,
+ /* 'D' */ 0x00,
+ /* 'E' */ 0x28,
+ /* 'F' */ 0x27,
+ /* 'G' */ 0x38,
+ /* 'H' */ 0x50,
+ /* 'I' */ 0x57,
+ /* 'J' */ 0x80,
+ /* 'K' */ 0x00,
+ /* 'L' */ 0x07,
+ /* 'M' */ 0x00,
+ /* 'N' */ 0x37,
+ /* 'O' */ 0x30,
+ /* 'P' */ 0x30,
+ /* 'Q' */ 0x50,
+ /* 'R' */ 0x50,
+ /* 'S' */ 0x88,
+ /* 'T' */ 0x00,
+ /* 'U' */ 0x00,
+ /* 'V' */ 0x00,
+ /* 'W' */ 0x20,
+ /* 'X' */ 0x28,
+ /* 'Y' */ 0x80,
+ /* 'Z' */ 0x88,
+ /* '[' */ 0x80,
+ /* '\' */ 0x80,
+ /* ']' */ 0x00,
+ /* '^' */ 0x00,
+ /* '_' */ 0x00,
+ /* '`' */ 0x60,
+ /* 'a' */ 0x60, // Disable %a format
+ /* 'b' */ 0x60,
+ /* 'c' */ 0x68,
+ /* 'd' */ 0x68,
+ /* 'e' */ 0x68,
+ /* 'f' */ 0x08,
+ /* 'g' */ 0x08,
+ /* 'h' */ 0x07,
+ /* 'i' */ 0x78,
+ /* 'j' */ 0x70,
+ /* 'k' */ 0x70,
+ /* 'l' */ 0x77,
+ /* 'm' */ 0x70,
+ /* 'n' */ 0x70,
+ /* 'o' */ 0x08,
+ /* 'p' */ 0x08,
+ /* 'q' */ 0x00,
+ /* 'r' */ 0x00,
+ /* 's' */ 0x08,
+ /* 't' */ 0x00,
+ /* 'u' */ 0x08,
+ /* 'v' */ 0x00,
+ /* 'w' */ 0x07,
+ /* 'x' */ 0x08
+};
+//#endif /* defined (_UNICODE) || defined (CPRFLAG) */
+
+#endif /* FORMAT_VALIDATIONS */
+
+#define FIND_CHAR_CLASS(lookuptbl, c) \
+ ((c) < _T(' ') || (c) > _T('x') ? \
+ CH_OTHER \
+ : \
+ (enum CHARTYPE)(lookuptbl[(c)-_T(' ')] & 0xF))
+
+#define FIND_NEXT_STATE(lookuptbl, class, state) \
+ (enum STATE)(lookuptbl[(class) * NUMSTATES + (state)] >> 4)
+
+/*
+ * Note: CPRFLAG and _UNICODE cases are currently mutually exclusive.
+ */
+
+/* prototypes */
+
+#ifdef CPRFLAG
+
+#define WRITE_CHAR(ch, pnw) write_char(ch, pnw)
+#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, pnw)
+#define WRITE_STRING(s, len, pnw) write_string(s, len, pnw)
+
+LOCAL(void) write_char(_TCHAR ch, int *pnumwritten);
+LOCAL(void) write_multi_char(_TCHAR ch, int num, int *pnumwritten);
+LOCAL(void) write_string(const _TCHAR *string, int len, int *numwritten);
+
+#else /* CPRFLAG */
+
+#define WRITE_CHAR(ch, pnw) write_char(ch, stream, pnw)
+#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, stream, pnw)
+#define WRITE_STRING(s, len, pnw) write_string(s, len, stream, pnw)
+
+LOCAL(void) write_char(_TCHAR ch, miniFILE *f, int *pnumwritten);
+LOCAL(void) write_multi_char(_TCHAR ch, int num, miniFILE *f, int *pnumwritten);
+LOCAL(void) write_string(const _TCHAR *string, int len, miniFILE *f, int *numwritten);
+
+#endif /* CPRFLAG */
+
+#define get_int_arg(list) va_arg(*list, int)
+#define get_long_arg(list) va_arg(*list, long)
+#define get_long_long_arg(list) va_arg(*list, long long)
+#define get_int64_arg(list) va_arg(*list, __int64)
+#define get_crtdouble_arg(list) va_arg(*list, _CRT_DOUBLE)
+#define get_ptr_arg(list) va_arg(*list, void *)
+
+#ifdef CPRFLAG
+LOCAL(int) output(const _TCHAR *, _locale_t , va_list);
+_CRTIMP int __cdecl _vtcprintf_l (const _TCHAR *, _locale_t, va_list);
+_CRTIMP int __cdecl _vtcprintf_s_l (const _TCHAR *, _locale_t, va_list);
+_CRTIMP int __cdecl _vtcprintf_p_l (const _TCHAR *, _locale_t, va_list);
+
+
+/***
+*int _cprintf(format, arglist) - write formatted output directly to console
+*
+*Purpose:
+* Writes formatted data like printf, but uses console I/O functions.
+*
+*Entry:
+* char *format - format string to determine data formats
+* arglist - list of POINTERS to where to put data
+*
+*Exit:
+* returns number of characters written
+*
+*Exceptions:
+*
+*******************************************************************************/
+#ifndef FORMAT_VALIDATIONS
+_CRTIMP int __cdecl _tcprintf_l (
+ const _TCHAR * format,
+ _locale_t plocinfo,
+ ...
+ )
+#else /* FORMAT_VALIDATIONS */
+_CRTIMP int __cdecl _tcprintf_s_l (
+ const _TCHAR * format,
+ _locale_t plocinfo,
+ ...
+ )
+#endif /* FORMAT_VALIDATIONS */
+
+{
+ int ret;
+ va_list arglist;
+ va_start(arglist, plocinfo);
+
+#ifndef FORMAT_VALIDATIONS
+ ret = _vtcprintf_l(format, plocinfo, arglist);
+#else /* FORMAT_VALIDATIONS */
+ ret = _vtcprintf_s_l(format, plocinfo, arglist);
+
+#endif /* FORMAT_VALIDATIONS */
+
+ va_end(arglist);
+
+ return ret;
+}
+
+#ifndef FORMAT_VALIDATIONS
+_CRTIMP int __cdecl _tcprintf (
+ const _TCHAR * format,
+ ...
+ )
+#else /* FORMAT_VALIDATIONS */
+_CRTIMP int __cdecl _tcprintf_s (
+ const _TCHAR * format,
+ ...
+ )
+#endif /* FORMAT_VALIDATIONS */
+
+{
+ int ret;
+ va_list arglist;
+
+ va_start(arglist, format);
+
+#ifndef FORMAT_VALIDATIONS
+ ret = _vtcprintf_l(format, NULL, arglist);
+#else /* FORMAT_VALIDATIONS */
+ ret = _vtcprintf_s_l(format, NULL, arglist);
+
+#endif /* FORMAT_VALIDATIONS */
+
+ va_end(arglist);
+
+ return ret;
+}
+
+#endif /* CPRFLAG */
+
+
+/***
+*int _output(stream, format, argptr), static int output(format, argptr)
+*
+*Purpose:
+* Output performs printf style output onto a stream. It is called by
+* printf/fprintf/sprintf/vprintf/vfprintf/vsprintf to so the dirty
+* work. In multi-thread situations, _output assumes that the given
+* stream is already locked.
+*
+* Algorithm:
+* The format string is parsed by using a finite state automaton
+* based on the current state and the current character read from
+* the format string. Thus, looping is on a per-character basis,
+* not a per conversion specifier basis. Once the format specififying
+* character is read, output is performed.
+*
+*Entry:
+* FILE *stream - stream for output
+* char *format - printf style format string
+* va_list argptr - pointer to list of subsidiary arguments
+*
+*Exit:
+* Returns the number of characters written, or -1 if an output error
+* occurs.
+*ifdef _UNICODE
+* The wide-character flavour returns the number of wide-characters written.
+*endif
+*
+*Exceptions:
+*
+*******************************************************************************/
+#ifdef CPRFLAG
+#ifndef FORMAT_VALIDATIONS
+_CRTIMP int __cdecl _vtcprintf (
+ const _TCHAR *format,
+ va_list argptr
+ )
+{
+ return _vtcprintf_l(format, NULL, argptr);
+}
+
+#else /* FORMAT_VALIDATIONS */
+_CRTIMP int __cdecl _vtcprintf_s (
+ const _TCHAR *format,
+ va_list argptr
+ )
+{
+ return _vtcprintf_s_l(format, NULL, argptr);
+}
+
+#endif /* FORMAT_VALIDATIONS */
+#endif /* CPRFLAG */
+
+#ifdef CPRFLAG
+#ifndef FORMAT_VALIDATIONS
+_CRTIMP int __cdecl _vtcprintf_l (
+#else /* FORMAT_VALIDATIONS */
+_CRTIMP int __cdecl _vtcprintf_s_l (
+#endif /* FORMAT_VALIDATIONS */
+#else /* CPRFLAG */
+
+#ifdef _UNICODE
+#ifndef FORMAT_VALIDATIONS
+int __cdecl _woutput (
+ miniFILE *stream,
+#else /* FORMAT_VALIDATIONS */
+int __cdecl _woutput_s (
+ miniFILE *stream,
+#endif /* FORMAT_VALIDATIONS */
+#else /* _UNICODE */
+#ifndef FORMAT_VALIDATIONS
+int __cdecl _output (
+ miniFILE *stream,
+#else /* FORMAT_VALIDATIONS */
+ int __cdecl _output_s (
+ miniFILE *stream,
+
+#endif /* FORMAT_VALIDATIONS */
+#endif /* _UNICODE */
+
+#endif /* CPRFLAG */
+ const _TCHAR *format,
+ va_list argptr
+ )
+{
+ int hexadd=0; /* offset to add to number to get 'a'..'f' */
+ TCHAR ch; /* character just read */
+ int flags=0; /* flag word -- see #defines above for flag values */
+ enum STATE state; /* current state */
+ enum CHARTYPE chclass; /* class of current character */
+ int radix; /* current conversion radix */
+ int charsout; /* characters currently written so far, -1 = IO error */
+ int fldwidth = 0; /* selected field width -- 0 means default */
+ int precision = 0; /* selected precision -- -1 means default */
+ TCHAR prefix[2]; /* numeric prefix -- up to two characters */
+ int prefixlen=0; /* length of prefix -- 0 means no prefix */
+ int capexp = 0; /* non-zero = 'E' exponent signifient, zero = 'e' */
+ int no_output=0; /* non-zero = prodcue no output for this specifier */
+ union {
+ const char *sz; /* pointer text to be printed, not zero terminated */
+ const wchar_t *wz;
+ } text;
+
+ int textlen; /* length of the text in bytes/wchars to be printed.
+ textlen is in multibyte or wide chars if _UNICODE */
+ union {
+ char sz[BUFFERSIZE];
+#ifdef _UNICODE
+ wchar_t wz[BUFFERSIZE];
+#endif /* _UNICODE */
+ } buffer;
+ wchar_t wchar; /* temp wchar_t */
+ int buffersize; /* size of text.sz (used only for the call to _cfltcvt) */
+ int bufferiswide=0; /* non-zero = buffer contains wide chars already */
+
+#ifndef CPRFLAG
+ _VALIDATE_RETURN( (stream != NULL), EINVAL, -1);
+#endif /* CPRFLAG */
+ _VALIDATE_RETURN( (format != NULL), EINVAL, -1);
+
+ charsout = 0; /* no characters written yet */
+ textlen = 0; /* no text yet */
+ state = ST_NORMAL; /* starting state */
+ buffersize = 0;
+
+ /* main loop -- loop while format character exist and no I/O errors */
+ while ((ch = *format++) != _T('\0') && charsout >= 0) {
+#ifndef FORMAT_VALIDATIONS
+ chclass = FIND_CHAR_CLASS(__lookuptable, ch); /* find character class */
+ state = FIND_NEXT_STATE(__lookuptable, chclass, state); /* find next state */
+#else /* FORMAT_VALIDATIONS */
+ chclass = FIND_CHAR_CLASS(__lookuptable_s, ch); /* find character class */
+ state = FIND_NEXT_STATE(__lookuptable_s, chclass, state); /* find next state */
+
+ _VALIDATE_RETURN((state != ST_INVALID), EINVAL, -1);
+
+#endif /* FORMAT_VALIDATIONS */
+
+ /* execute code for each state */
+ switch (state) {
+
+ case ST_NORMAL:
+
+ NORMAL_STATE:
+
+ /* normal state -- just write character */
+#ifdef _UNICODE
+ bufferiswide = 1;
+#else /* _UNICODE */
+ bufferiswide = 0;
+#endif /* _UNICODE */
+ WRITE_CHAR(ch, &charsout);
+ break;
+
+ case ST_PERCENT:
+ /* set default value of conversion parameters */
+ prefixlen = fldwidth = no_output = capexp = 0;
+ flags = 0;
+ precision = -1;
+ bufferiswide = 0; /* default */
+ break;
+
+ case ST_FLAG:
+ /* set flag based on which flag character */
+ switch (ch) {
+ case _T('-'):
+ flags |= FL_LEFT; /* '-' => left justify */
+ break;
+ case _T('+'):
+ flags |= FL_SIGN; /* '+' => force sign indicator */
+ break;
+ case _T(' '):
+ flags |= FL_SIGNSP; /* ' ' => force sign or space */
+ break;
+ case _T('#'):
+ flags |= FL_ALTERNATE; /* '#' => alternate form */
+ break;
+ case _T('0'):
+ flags |= FL_LEADZERO; /* '0' => pad with leading zeros */
+ break;
+ }
+ break;
+
+ case ST_WIDTH:
+ /* update width value */
+ if (ch == _T('*')) {
+ /* get width from arg list */
+ fldwidth = get_int_arg(&argptr);
+ if (fldwidth < 0) {
+ /* ANSI says neg fld width means '-' flag and pos width */
+ flags |= FL_LEFT;
+ fldwidth = -fldwidth;
+ }
+ }
+ else {
+ /* add digit to current field width */
+ fldwidth = fldwidth * 10 + (ch - _T('0'));
+ }
+ break;
+
+ case ST_DOT:
+ /* zero the precision, since dot with no number means 0
+ not default, according to ANSI */
+ precision = 0;
+ break;
+
+ case ST_PRECIS:
+ /* update precison value */
+ if (ch == _T('*')) {
+ /* get precision from arg list */
+ precision = get_int_arg(&argptr);
+ if (precision < 0)
+ precision = -1; /* neg precision means default */
+ }
+ else {
+ /* add digit to current precision */
+ precision = precision * 10 + (ch - _T('0'));
+ }
+ break;
+
+ case ST_SIZE:
+ /* just read a size specifier, set the flags based on it */
+ switch (ch) {
+ case _T('l'):
+ /*
+ * In order to handle the ll case, we depart from the
+ * simple deterministic state machine.
+ */
+ if (*format == _T('l'))
+ {
+ ++format;
+ flags |= FL_LONGLONG; /* 'll' => long long */
+ }
+ else
+ {
+ flags |= FL_LONG; /* 'l' => long int or wchar_t */
+ }
+ break;
+
+ case _T('I'):
+ /*
+ * In order to handle the I, I32, and I64 size modifiers, we
+ * depart from the simple deterministic state machine. The
+ * code below scans for characters following the 'I',
+ * and defaults to 64 bit on WIN64 and 32 bit on WIN32
+ */
+#if PTR_IS_INT64
+ flags |= FL_I64; /* 'I' => __int64 on WIN64 systems */
+#endif /* PTR_IS_INT64 */
+ if ( (*format == _T('6')) && (*(format + 1) == _T('4')) )
+ {
+ format += 2;
+ flags |= FL_I64; /* I64 => __int64 */
+ }
+ else if ( (*format == _T('3')) && (*(format + 1) == _T('2')) )
+ {
+ format += 2;
+ flags &= ~FL_I64; /* I32 => __int32 */
+ }
+ else if ( (*format == _T('d')) ||
+ (*format == _T('i')) ||
+ (*format == _T('o')) ||
+ (*format == _T('u')) ||
+ (*format == _T('x')) ||
+ (*format == _T('X')) )
+ {
+ /*
+ * Nothing further needed. %Id (et al) is
+ * handled just like %d, except that it defaults to 64 bits
+ * on WIN64. Fall through to the next iteration.
+ */
+ }
+ else {
+ state = ST_NORMAL;
+ goto NORMAL_STATE;
+ }
+ break;
+
+ case _T('h'):
+ flags |= FL_SHORT; /* 'h' => short int or char */
+ break;
+
+ case _T('w'):
+ flags |= FL_WIDECHAR; /* 'w' => wide character */
+ break;
+
+ }
+ break;
+
+ case ST_TYPE:
+ /* we have finally read the actual type character, so we */
+ /* now format and "print" the output. We use a big switch */
+ /* statement that sets 'text' to point to the text that should */
+ /* be printed, and 'textlen' to the length of this text. */
+ /* Common code later on takes care of justifying it and */
+ /* other miscellaneous chores. Note that cases share code, */
+ /* in particular, all integer formatting is done in one place. */
+ /* Look at those funky goto statements! */
+
+ switch (ch) {
+
+ case _T('C'): /* ISO wide character */
+ if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
+#ifdef _UNICODE
+ flags |= FL_SHORT;
+#else /* _UNICODE */
+ flags |= FL_WIDECHAR; /* ISO std. */
+#endif /* _UNICODE */
+ /* fall into 'c' case */
+
+ case _T('c'): {
+ /* print a single character specified by int argument */
+#ifdef _UNICODE
+ bufferiswide = 1;
+ wchar = (wchar_t) get_int_arg(&argptr);
+ if (flags & FL_SHORT) {
+ /* format multibyte character */
+ /* this is an extension of ANSI */
+ char tempchar[2];
+ {
+ tempchar[0] = (char)(wchar & 0x00ff);
+ tempchar[1] = '\0';
+ }
+
+ if (_MBTOWC(buffer.wz,tempchar, MB_CUR_MAX) < 0)
+ {
+ /* ignore if conversion was unsuccessful */
+ no_output = 1;
+ }
+ } else {
+ buffer.wz[0] = wchar;
+ }
+ text.wz = buffer.wz;
+ textlen = 1; /* print just a single character */
+#else /* _UNICODE */
+ if (flags & (FL_LONG|FL_WIDECHAR)) {
+ wchar = (wchar_t) get_int_arg(&argptr);
+ no_output = 1;
+ } else {
+ /* format multibyte character */
+ /* this is an extension of ANSI */
+ unsigned short temp;
+ wchar = (wchar_t)get_int_arg(&argptr);
+ temp = (unsigned short)wchar;
+ {
+ buffer.sz[0] = (char) temp;
+ textlen = 1;
+ }
+ }
+ text.sz = buffer.sz;
+#endif /* _UNICODE */
+ }
+ break;
+
+ case _T('Z'): {
+ /* print a Counted String */
+ struct _count_string {
+ short Length;
+ short MaximumLength;
+ char *Buffer;
+ } *pstr;
+
+ pstr = (struct _count_string *)get_ptr_arg(&argptr);
+ if (pstr == NULL || pstr->Buffer == NULL) {
+ /* null ptr passed, use special string */
+ text.sz = __nullstring;
+ textlen = (int)strlen(text.sz);
+ } else {
+ if (flags & FL_WIDECHAR) {
+ text.wz = (wchar_t *)pstr->Buffer;
+ textlen = pstr->Length / (int)sizeof(wchar_t);
+ bufferiswide = 1;
+ } else {
+ bufferiswide = 0;
+ text.sz = pstr->Buffer;
+ textlen = pstr->Length;
+ }
+ }
+ }
+ break;
+
+ case _T('S'): /* ISO wide character string */
+#ifndef _UNICODE
+ if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
+ flags |= FL_WIDECHAR;
+#else /* _UNICODE */
+ if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
+ flags |= FL_SHORT;
+#endif /* _UNICODE */
+
+ case _T('s'): {
+ /* print a string -- */
+ /* ANSI rules on how much of string to print: */
+ /* all if precision is default, */
+ /* min(precision, length) if precision given. */
+ /* prints '(null)' if a null string is passed */
+
+ int i;
+ const char *p; /* temps */
+ const wchar_t *pwch;
+
+ /* At this point it is tempting to use strlen(), but */
+ /* if a precision is specified, we're not allowed to */
+ /* scan past there, because there might be no null */
+ /* at all. Thus, we must do our own scan. */
+
+ i = (precision == -1) ? INT_MAX : precision;
+ text.sz = (char *)get_ptr_arg(&argptr);
+
+ /* scan for null upto i characters */
+#ifdef _UNICODE
+ if (flags & FL_SHORT) {
+ if (text.sz == NULL) /* NULL passed, use special string */
+ text.sz = __nullstring;
+ p = text.sz;
+ for (textlen=0; textlen<i && *p; textlen++) {
+ ++p;
+ }
+ /* textlen now contains length in multibyte chars */
+ } else {
+ if (text.wz == NULL) /* NULL passed, use special string */
+ text.wz = __wnullstring;
+ bufferiswide = 1;
+ pwch = text.wz;
+ while (i-- && *pwch)
+ ++pwch;
+ textlen = (int)(pwch - text.wz); /* in wchar_ts */
+ /* textlen now contains length in wide chars */
+ }
+#else /* _UNICODE */
+ if (flags & (FL_LONG|FL_WIDECHAR)) {
+ if (text.wz == NULL) /* NULL passed, use special string */
+ text.wz = __wnullstring;
+ bufferiswide = 1;
+ pwch = text.wz;
+ while ( i-- && *pwch )
+ ++pwch;
+ textlen = (int)(pwch - text.wz);
+ /* textlen now contains length in wide chars */
+ } else {
+ if (text.sz == NULL) /* NULL passed, use special string */
+ text.sz = __nullstring;
+ p = text.sz;
+ while (i-- && *p)
+ ++p;
+ textlen = (int)(p - text.sz); /* length of the string */
+ }
+
+#endif /* _UNICODE */
+ }
+ break;
+
+
+ case _T('n'): {
+ /* write count of characters seen so far into */
+ /* short/int/long thru ptr read from args */
+
+ void *p; /* temp */
+
+ p = get_ptr_arg(&argptr);
+
+ /* %n is disabled */
+ _VALIDATE_RETURN(("'n' format specifier disabled" && 0), EINVAL, -1);
+ break;
+
+ /* store chars out into short/long/int depending on flags */
+#if !LONG_IS_INT
+ if (flags & FL_LONG)
+ *(long *)p = charsout;
+ else
+#endif /* !LONG_IS_INT */
+
+#if !SHORT_IS_INT
+ if (flags & FL_SHORT)
+ *(short *)p = (short) charsout;
+ else
+#endif /* !SHORT_IS_INT */
+ *(int *)p = charsout;
+
+ no_output = 1; /* force no output */
+ }
+ break;
+
+ case _T('E'):
+ case _T('G'):
+ case _T('A'):
+ capexp = 1; /* capitalize exponent */
+ ch += _T('a') - _T('A'); /* convert format char to lower */
+ /* DROP THROUGH */
+ case _T('e'):
+ case _T('f'):
+ case _T('g'):
+ case _T('a'): {
+ /* floating point conversion -- we call cfltcvt routines */
+ /* to do the work for us. */
+ flags |= FL_SIGNED; /* floating point is signed conversion */
+ text.sz = buffer.sz; /* put result in buffer */
+ buffersize = BUFFERSIZE;
+
+ /* compute the precision value */
+ if (precision < 0)
+ precision = 6; /* default precision: 6 */
+ else if (precision == 0 && ch == _T('g'))
+ precision = 1; /* ANSI specified */
+ else if (precision > MAXPRECISION)
+ precision = MAXPRECISION;
+
+ if (precision > BUFFERSIZE - _CVTBUFSIZE) {
+ /* cap precision further */
+ precision = BUFFERSIZE - _CVTBUFSIZE;
+ }
+
+ /* for safecrt, we pass along the FL_ALTERNATE flag to _safecrt_cfltcvt */
+ if (flags & FL_ALTERNATE)
+ {
+ capexp |= FL_ALTERNATE;
+ }
+
+ _CRT_DOUBLE tmp;
+ tmp=va_arg(argptr, _CRT_DOUBLE);
+ /* Note: assumes ch is in ASCII range */
+ /* In safecrt, we provide a special version of _cfltcvt which internally calls printf (see safecrt_output_s.c) */
+ _CFLTCVT(&tmp, buffer.sz, buffersize, (char)ch, precision, capexp);
+
+ /* check if result was negative, save '-' for later */
+ /* and point to positive part (this is for '0' padding) */
+ if (*text.sz == '-') {
+ flags |= FL_NEGATIVE;
+ ++text.sz;
+ }
+
+ textlen = (int)strlen(text.sz); /* compute length of text */
+ }
+ break;
+
+ case _T('d'):
+ case _T('i'):
+ /* signed decimal output */
+ flags |= FL_SIGNED;
+ radix = 10;
+ goto COMMON_INT;
+
+ case _T('u'):
+ radix = 10;
+ goto COMMON_INT;
+
+ case _T('p'):
+ /* write a pointer -- this is like an integer or long */
+ /* except we force precision to pad with zeros and */
+ /* output in big hex. */
+
+ precision = 2 * sizeof(void *); /* number of hex digits needed */
+#if PTR_IS_INT64
+ flags |= FL_I64; /* assume we're converting an int64 */
+#elif !PTR_IS_INT
+ flags |= FL_LONG; /* assume we're converting a long */
+#endif /* !PTR_IS_INT */
+ /* DROP THROUGH to hex formatting */
+
+ case _T('X'):
+ /* unsigned upper hex output */
+ hexadd = _T('A') - _T('9') - 1; /* set hexadd for uppercase hex */
+ goto COMMON_HEX;
+
+ case _T('x'):
+ /* unsigned lower hex output */
+ hexadd = _T('a') - _T('9') - 1; /* set hexadd for lowercase hex */
+ /* DROP THROUGH TO COMMON_HEX */
+
+ COMMON_HEX:
+ radix = 16;
+ if (flags & FL_ALTERNATE) {
+ /* alternate form means '0x' prefix */
+ prefix[0] = _T('0');
+ prefix[1] = (TCHAR)(_T('x') - _T('a') + _T('9') + 1 + hexadd); /* 'x' or 'X' */
+ prefixlen = 2;
+ }
+ goto COMMON_INT;
+
+ case _T('o'):
+ /* unsigned octal output */
+ radix = 8;
+ if (flags & FL_ALTERNATE) {
+ /* alternate form means force a leading 0 */
+ flags |= FL_FORCEOCTAL;
+ }
+ /* DROP THROUGH to COMMON_INT */
+
+ COMMON_INT: {
+ /* This is the general integer formatting routine. */
+ /* Basically, we get an argument, make it positive */
+ /* if necessary, and convert it according to the */
+ /* correct radix, setting text and textlen */
+ /* appropriately. */
+
+#if _INTEGRAL_MAX_BITS >= 64
+ uint64_t number; /* number to convert */
+ int digit; /* ascii value of digit */
+ __int64 l; /* temp long value */
+#else /* _INTEGRAL_MAX_BITS >= 64 */
+ unsigned long number; /* number to convert */
+ int digit; /* ascii value of digit */
+ long l; /* temp long value */
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+
+ /* 1. read argument into l, sign extend as needed */
+#if _INTEGRAL_MAX_BITS >= 64
+ if (flags & FL_I64)
+ l = get_int64_arg(&argptr);
+ else
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+
+ if (flags & FL_LONGLONG)
+ l = get_long_long_arg(&argptr);
+
+ else
+
+#if !LONG_IS_INT
+ if (flags & FL_LONG)
+ l = get_long_arg(&argptr);
+ else
+#endif /* !LONG_IS_INT */
+
+#if !SHORT_IS_INT
+ if (flags & FL_SHORT) {
+ if (flags & FL_SIGNED)
+ l = (short) get_int_arg(&argptr); /* sign extend */
+ else
+ l = (unsigned short) get_int_arg(&argptr); /* zero-extend*/
+
+ } else
+#endif /* !SHORT_IS_INT */
+ {
+ if (flags & FL_SIGNED)
+ l = get_int_arg(&argptr); /* sign extend */
+ else
+ l = (unsigned int) get_int_arg(&argptr); /* zero-extend*/
+ }
+
+ /* 2. check for negative; copy into number */
+ if ( (flags & FL_SIGNED) && l < 0) {
+ number = -l;
+ flags |= FL_NEGATIVE; /* remember negative sign */
+ } else {
+ number = l;
+ }
+
+#if _INTEGRAL_MAX_BITS >= 64
+ if ( (flags & FL_I64) == 0 && (flags & FL_LONGLONG) == 0 ) {
+ /*
+ * Unless printing a full 64-bit value, insure values
+ * here are not in cananical longword format to prevent
+ * the sign extended upper 32-bits from being printed.
+ */
+ number &= 0xffffffff;
+ }
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+
+ /* 3. check precision value for default; non-default */
+ /* turns off 0 flag, according to ANSI. */
+ if (precision < 0)
+ precision = 1; /* default precision */
+ else {
+ flags &= ~FL_LEADZERO;
+ if (precision > MAXPRECISION)
+ precision = MAXPRECISION;
+ }
+
+ /* 4. Check if data is 0; if so, turn off hex prefix */
+ if (number == 0)
+ prefixlen = 0;
+
+ /* 5. Convert data to ASCII -- note if precision is zero */
+ /* and number is zero, we get no digits at all. */
+
+ char *sz;
+ sz = &buffer.sz[BUFFERSIZE-1]; /* last digit at end of buffer */
+
+ while (precision-- > 0 || number != 0) {
+ digit = (int)(number % radix) + '0';
+ number /= radix; /* reduce number */
+ if (digit > '9') {
+ /* a hex digit, make it a letter */
+ digit += hexadd;
+ }
+ *sz-- = (char)digit; /* store the digit */
+ }
+
+ textlen = (int)((char *)&buffer.sz[BUFFERSIZE-1] - sz); /* compute length of number */
+ ++sz; /* text points to first digit now */
+
+
+ /* 6. Force a leading zero if FORCEOCTAL flag set */
+ if ((flags & FL_FORCEOCTAL) && (textlen == 0 || sz[0] != '0')) {
+ *--sz = '0';
+ ++textlen; /* add a zero */
+ }
+
+ text.sz = sz;
+ }
+ break;
+ }
+
+
+ /* At this point, we have done the specific conversion, and */
+ /* 'text' points to text to print; 'textlen' is length. Now we */
+ /* justify it, put on prefixes, leading zeros, and then */
+ /* print it. */
+
+ if (!no_output) {
+ int padding; /* amount of padding, negative means zero */
+
+ if (flags & FL_SIGNED) {
+ if (flags & FL_NEGATIVE) {
+ /* prefix is a '-' */
+ prefix[0] = _T('-');
+ prefixlen = 1;
+ }
+ else if (flags & FL_SIGN) {
+ /* prefix is '+' */
+ prefix[0] = _T('+');
+ prefixlen = 1;
+ }
+ else if (flags & FL_SIGNSP) {
+ /* prefix is ' ' */
+ prefix[0] = _T(' ');
+ prefixlen = 1;
+ }
+ }
+
+ /* calculate amount of padding -- might be negative, */
+ /* but this will just mean zero */
+ padding = fldwidth - textlen - prefixlen;
+
+ /* put out the padding, prefix, and text, in the correct order */
+
+ if (!(flags & (FL_LEFT | FL_LEADZERO))) {
+ /* pad on left with blanks */
+ WRITE_MULTI_CHAR(_T(' '), padding, &charsout);
+ }
+
+ /* write prefix */
+ WRITE_STRING(prefix, prefixlen, &charsout);
+
+ if ((flags & FL_LEADZERO) && !(flags & FL_LEFT)) {
+ /* write leading zeros */
+ WRITE_MULTI_CHAR(_T('0'), padding, &charsout);
+ }
+
+ /* write text */
+#ifndef _UNICODE
+ if (bufferiswide && (textlen > 0)) {
+ charsout = -1;
+ } else {
+ WRITE_STRING(text.sz, textlen, &charsout);
+ }
+#else /* _UNICODE */
+ if (!bufferiswide && textlen > 0) {
+ const char *p;
+ int retval = 0;
+ int count;
+
+ p = text.sz;
+ count = textlen;
+ while (count-- > 0) {
+ retval = _MBTOWC(&wchar, p, MB_CUR_MAX);
+ if (retval <= 0) {
+ charsout = -1;
+ break;
+ }
+ WRITE_CHAR(wchar, &charsout);
+ p += retval;
+ }
+ } else {
+ WRITE_STRING(text.wz, textlen, &charsout);
+ }
+#endif /* _UNICODE */
+
+ if (charsout >= 0 && (flags & FL_LEFT)) {
+ /* pad on right with blanks */
+ WRITE_MULTI_CHAR(_T(' '), padding, &charsout);
+ }
+
+ /* we're done! */
+ }
+ break;
+ case ST_INVALID:
+ _VALIDATE_RETURN(0 /* FALSE */, EINVAL, -1);
+ break;
+ }
+ }
+
+#ifdef FORMAT_VALIDATIONS
+ /* The format string shouldn't be incomplete - i.e. when we are finished
+ with the format string, the last thing we should have encountered
+ should have been a regular char to be output or a type specifier. Else
+ the format string was incomplete */
+ _VALIDATE_RETURN(((state == ST_NORMAL) || (state == ST_TYPE)), EINVAL, -1);
+#endif /* FORMAT_VALIDATIONS */
+
+ return charsout; /* return value = number of characters written */
+}
+
+/*
+ * Future Optimizations for swprintf:
+ * - Don't free the memory used for converting the buffer to wide chars.
+ * Use realloc if the memory is not sufficient. Free it at the end.
+ */
+
+/***
+*void write_char(char ch, int *pnumwritten)
+*ifdef _UNICODE
+*void write_char(wchar_t ch, FILE *f, int *pnumwritten)
+*endif
+*void write_char(char ch, FILE *f, int *pnumwritten)
+*
+*Purpose:
+* Writes a single character to the given file/console. If no error occurs,
+* then *pnumwritten is incremented; otherwise, *pnumwritten is set
+* to -1.
+*
+*Entry:
+* _TCHAR ch - character to write
+* FILE *f - file to write to
+* int *pnumwritten - pointer to integer to update with total chars written
+*
+*Exit:
+* No return value.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+#ifdef CPRFLAG
+
+LOCAL(void) write_char (
+ _TCHAR ch,
+ int *pnumwritten
+ )
+{
+#ifdef _UNICODE
+ if (_putwch_nolock(ch) == WEOF)
+#else /* _UNICODE */
+ if (_putch_nolock(ch) == EOF)
+#endif /* _UNICODE */
+ *pnumwritten = -1;
+ else
+ ++(*pnumwritten);
+}
+
+#else /* CPRFLAG */
+
+LOCAL(void) write_char (
+ _TCHAR ch,
+ miniFILE *f,
+ int *pnumwritten
+ )
+{
+ if ( (f->_flag & _IOSTRG) && f->_base == NULL)
+ {
+ ++(*pnumwritten);
+ return;
+ }
+#ifdef _UNICODE
+ if (_putwc_nolock(ch, f) == WEOF)
+#else /* _UNICODE */
+ if (_putc_nolock(ch, f) == EOF)
+#endif /* _UNICODE */
+ *pnumwritten = -1;
+ else
+ ++(*pnumwritten);
+}
+
+#endif /* CPRFLAG */
+
+/***
+*void write_multi_char(char ch, int num, int *pnumwritten)
+*ifdef _UNICODE
+*void write_multi_char(wchar_t ch, int num, FILE *f, int *pnumwritten)
+*endif
+*void write_multi_char(char ch, int num, FILE *f, int *pnumwritten)
+*
+*Purpose:
+* Writes num copies of a character to the given file/console. If no error occurs,
+* then *pnumwritten is incremented by num; otherwise, *pnumwritten is set
+* to -1. If num is negative, it is treated as zero.
+*
+*Entry:
+* _TCHAR ch - character to write
+* int num - number of times to write the characters
+* FILE *f - file to write to
+* int *pnumwritten - pointer to integer to update with total chars written
+*
+*Exit:
+* No return value.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+#ifdef CPRFLAG
+LOCAL(void) write_multi_char (
+ _TCHAR ch,
+ int num,
+ int *pnumwritten
+ )
+{
+ while (num-- > 0) {
+ write_char(ch, pnumwritten);
+ if (*pnumwritten == -1)
+ break;
+ }
+}
+
+#else /* CPRFLAG */
+
+LOCAL(void) write_multi_char (
+ _TCHAR ch,
+ int num,
+ miniFILE *f,
+ int *pnumwritten
+ )
+{
+ while (num-- > 0) {
+ write_char(ch, f, pnumwritten);
+ if (*pnumwritten == -1)
+ break;
+ }
+}
+
+#endif /* CPRFLAG */
+
+/***
+*void write_string(const char *string, int len, int *pnumwritten)
+*void write_string(const char *string, int len, FILE *f, int *pnumwritten)
+*ifdef _UNICODE
+*void write_string(const wchar_t *string, int len, FILE *f, int *pnumwritten)
+*endif
+*
+*Purpose:
+* Writes a string of the given length to the given file. If no error occurs,
+* then *pnumwritten is incremented by len; otherwise, *pnumwritten is set
+* to -1. If len is negative, it is treated as zero.
+*
+*Entry:
+* _TCHAR *string - string to write (NOT null-terminated)
+* int len - length of string
+* FILE *f - file to write to
+* int *pnumwritten - pointer to integer to update with total chars written
+*
+*Exit:
+* No return value.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+#ifdef CPRFLAG
+
+LOCAL(void) write_string (
+ const _TCHAR *string,
+ int len,
+ int *pnumwritten
+ )
+{
+ while (len-- > 0) {
+ write_char(*string++, pnumwritten);
+ if (*pnumwritten == -1)
+ {
+ if (errno == EILSEQ)
+ write_char(_T('?'), pnumwritten);
+ else
+ break;
+ }
+ }
+}
+
+#else /* CPRFLAG */
+
+LOCAL(void) write_string (
+ const _TCHAR *string,
+ int len,
+ miniFILE *f,
+ int *pnumwritten
+ )
+{
+ if ( (f->_flag & _IOSTRG) && f->_base == NULL)
+ {
+ (*pnumwritten) += len;
+ return;
+ }
+ while (len-- > 0) {
+ write_char(*string++, f, pnumwritten);
+ if (*pnumwritten == -1)
+ {
+ if (errno == EILSEQ)
+ write_char(_T('?'), f, pnumwritten);
+ else
+ break;
+ }
+ }
+}
+#endif /* CPRFLAG */
diff --git a/src/pal/src/safecrt/safecrt_input_s.c b/src/pal/src/safecrt/safecrt_input_s.c
new file mode 100644
index 0000000000..6ba607c669
--- /dev/null
+++ b/src/pal/src/safecrt/safecrt_input_s.c
@@ -0,0 +1,46 @@
+// 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.
+
+/***
+*safecrt_input_s.c - implementation of the _input family for safecrt.lib
+*
+
+*
+*Purpose:
+* This file contains the implementation of the _input family for safecrt.lib.
+*
+*Revision History:
+* 07/19/04 AC Created
+*
+****/
+
+#define _SAFECRT_IMPL
+#define _SECURE_SCANF
+
+#include "pal/palinternal.h"
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <inttypes.h>
+
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _TCHAR CRT_TCHAR
+#define TCHAR CRTTCHAR
+
+typedef char _TCHAR;
+typedef char TCHAR;
+typedef unsigned char _TUCHAR;
+#define _T(x) x
+#define _TEOF EOF
+
+#define _gettc_nolock(x) _getc_nolock(x)
+#define _ungettc_nolock(x,y) _ungetc_nolock(x,y)
+
+#include "input.inl"
diff --git a/src/pal/src/safecrt/safecrt_output_l.c b/src/pal/src/safecrt/safecrt_output_l.c
new file mode 100644
index 0000000000..d6844f4f8b
--- /dev/null
+++ b/src/pal/src/safecrt/safecrt_output_l.c
@@ -0,0 +1,1467 @@
+// 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.
+
+/***
+*safecrt_output_l.c - implementation of the _output family for safercrt.lib
+*
+
+*
+*Purpose:
+* This file contains the implementation of the _output family for safercrt.lib.
+*
+*Revision History:
+* 07-08-04 SJ Stub module created.
+* 07-13-04 AC Added support for floating-point types.
+* 07-29-04 AC Added macros for a safecrt version of mctowc and wctomb, which target ntdll.dll or msvcrt.dll
+* based on the _NTSUBSET_ #define
+* 09-24-04 MSL Prefix disallow NULL deref
+*
+****/
+
+#define _SAFECRT_IMPL
+
+#define __STDC_LIMIT_MACROS
+#include "pal/palinternal.h"
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _CFLTCVT _safecrt_cfltcvt
+
+//typedef __int64_t __int64;
+typedef double _CRT_DOUBLE;
+typedef char _TCHAR;
+typedef char TCHAR;
+#define _T(x) x
+/*
+Buffer size required to be passed to _gcvt, fcvt and other fp conversion routines
+*/
+#define _CVTBUFSIZE (309+40) /* # of digits in max. dp value + slop */
+
+//------------------------------------------------------------------------------
+// This code was taken from the 'ouput.c' file located in Visual Studio 8 (i.e. 2005)
+// in the '\Microsoft Visual Studio 8\VC\crt\src' directory. It was moved into
+// this file to support only the '_output' function used by _vscprintf() in vsprintf.c
+// UNUSED / NON-RELEVANT PORTIONS OF THE CODE HAVE BEEN REMOVED - do not try and
+// use it to generate any other safecrt 'output' functions
+//
+// Noteable modifications
+// - changed FILE to miniFILE (defined in mbusafecrt_internal.h)
+// - removed _soutput_s - it was unused in this case and conflicted with output.inl
+// - changed #define SHORT_IS_INT to true varargs promotes shorts to ints in GCC
+// - removed definition of __lookuptable_s when using FORMAT_VALIDATIONS, we don't use them
+
+// 7/03/07 - Created by Stephen Shaw (steshaw)
+//------------------------------------------------------------------------------
+
+
+/* temporary work-around for compiler without 64-bit support */
+#ifndef _INTEGRAL_MAX_BITS
+#define _INTEGRAL_MAX_BITS 64
+#endif /* _INTEGRAL_MAX_BITS */
+
+#include <limits.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#define _MBTOWC(x,y,z) _minimal_chartowchar( x, y )
+
+#undef _malloc_crt
+#define _malloc_crt malloc
+
+#undef _free_crt
+#define _free_crt free
+
+// SNIP -srs 7/3/07
+
+#ifndef _CFLTCVT
+#define _CFLTCVT _cfltcvt
+#endif /* _CFLTCVT */
+
+#ifndef _CLDCVT
+#define _CLDCVT _cldcvt
+#endif /* _CLDCVT */
+
+#ifdef _MBCS
+#undef _MBCS
+#endif /* _MBCS */
+//#include <tchar.h>
+
+/* this macro defines a function which is private and as fast as possible: */
+/* for example, in C 6.0, it might be static _fastcall <type> near. */
+#define LOCAL(x) static x __cdecl
+
+/* int/long/short/pointer sizes */
+
+/* the following should be set depending on the sizes of various types */
+#if __LP64__
+ #define LONG_IS_INT 0
+ CASSERT(sizeof(long) > sizeof(int));
+#else
+ #define LONG_IS_INT 1 /* 1 means long is same size as int */
+ CASSERT(sizeof(long) == sizeof(int));
+#endif
+
+// GCC: short is not int, but GCC promotes va_arg to int
+#define SHORT_IS_INT 1
+
+#define LONGLONG_IS_INT64 1 /* 1 means long long is same as int64 */
+ CASSERT(sizeof(long long) == sizeof(int64_t));
+
+#if defined (_WIN64)
+ #define PTR_IS_INT 0 /* 1 means ptr is same size as int */
+ CASSERT(sizeof(void *) != sizeof(int));
+ #if __LP64__
+ #define PTR_IS_LONG 1 /* 1 means ptr is same size as long */
+ CASSERT(sizeof(void *) == sizeof(long));
+ #else
+ #define PTR_IS_LONG 0 /* 1 means ptr is same size as long */
+ CASSERT(sizeof(void *) != sizeof(long));
+ #endif
+ #define PTR_IS_INT64 1 /* 1 means ptr is same size as int64 */
+ CASSERT(sizeof(void *) == sizeof(int64_t));
+#else /* defined (_WIN64) */
+ #define PTR_IS_INT 1 /* 1 means ptr is same size as int */
+ CASSERT(sizeof(void *) == sizeof(int));
+ #define PTR_IS_LONG 1 /* 1 means ptr is same size as long */
+ CASSERT(sizeof(void *) == sizeof(long));
+ #define PTR_IS_INT64 0 /* 1 means ptr is same size as int64 */
+ CASSERT(sizeof(void *) != sizeof(int64_t));
+#endif /* defined (_WIN64) */
+
+/* CONSTANTS */
+
+/* size of conversion buffer (ANSI-specified minimum is 509) */
+
+#define BUFFERSIZE 512
+#define MAXPRECISION BUFFERSIZE
+
+#if BUFFERSIZE < _CVTBUFSIZE + 6
+/*
+ * Buffer needs to be big enough for default minimum precision
+ * when converting floating point needs bigger buffer, and malloc
+ * fails
+ */
+#error Conversion buffer too small for max double.
+#endif /* BUFFERSIZE < _CVTBUFSIZE + 6 */
+
+/* flag definitions */
+#define FL_SIGN 0x00001 /* put plus or minus in front */
+#define FL_SIGNSP 0x00002 /* put space or minus in front */
+#define FL_LEFT 0x00004 /* left justify */
+#define FL_LEADZERO 0x00008 /* pad with leading zeros */
+#define FL_LONG 0x00010 /* long value given */
+#define FL_SHORT 0x00020 /* short value given */
+#define FL_SIGNED 0x00040 /* signed data given */
+#define FL_ALTERNATE 0x00080 /* alternate form requested */
+#define FL_NEGATIVE 0x00100 /* value is negative */
+#define FL_FORCEOCTAL 0x00200 /* force leading '0' for octals */
+#define FL_LONGDOUBLE 0x00400 /* long double value given */
+#define FL_WIDECHAR 0x00800 /* wide characters */
+#define FL_LONGLONG 0x01000 /* long long value given */
+#define FL_I64 0x08000 /* __int64 value given */
+
+/* state definitions */
+enum STATE {
+ ST_NORMAL, /* normal state; outputting literal chars */
+ ST_PERCENT, /* just read '%' */
+ ST_FLAG, /* just read flag character */
+ ST_WIDTH, /* just read width specifier */
+ ST_DOT, /* just read '.' */
+ ST_PRECIS, /* just read precision specifier */
+ ST_SIZE, /* just read size specifier */
+ ST_TYPE /* just read type specifier */
+#ifdef FORMAT_VALIDATIONS
+ ,ST_INVALID /* Invalid format */
+#endif /* FORMAT_VALIDATIONS */
+
+};
+
+#ifdef FORMAT_VALIDATIONS
+#define NUMSTATES (ST_INVALID + 1)
+#else /* FORMAT_VALIDATIONS */
+#define NUMSTATES (ST_TYPE + 1)
+#endif /* FORMAT_VALIDATIONS */
+
+/* character type values */
+enum CHARTYPE {
+ CH_OTHER, /* character with no special meaning */
+ CH_PERCENT, /* '%' */
+ CH_DOT, /* '.' */
+ CH_STAR, /* '*' */
+ CH_ZERO, /* '0' */
+ CH_DIGIT, /* '1'..'9' */
+ CH_FLAG, /* ' ', '+', '-', '#' */
+ CH_SIZE, /* 'h', 'l', 'L', 'N', 'F', 'w' */
+ CH_TYPE /* type specifying character */
+};
+
+/* static data (read only, since we are re-entrant) */
+#if defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS)
+extern const char __nullstring[]; /* string to print on null ptr */
+extern const wchar_t __wnullstring[]; /* string to print on null ptr */
+#else /* defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS) */
+static const char __nullstring[] = "(null)"; /* string to print on null ptr */
+static const wchar_t __wnullstring[] = { '(', 'n', 'u', 'l', 'l', ')', '\0' };/* string to print on null ptr */
+#endif /* defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS) */
+
+/* The state table. This table is actually two tables combined into one. */
+/* The lower nybble of each byte gives the character class of any */
+/* character; while the uper nybble of the byte gives the next state */
+/* to enter. See the macros below the table for details. */
+/* */
+/* The table is generated by maketabc.c -- use this program to make */
+/* changes. */
+
+#ifndef FORMAT_VALIDATIONS
+#if defined (_UNICODE) || defined (CPRFLAG)
+extern const char __lookuptable[];
+#else /* defined (_UNICODE) || defined (CPRFLAG) */
+//extern const char __lookuptable[] = {
+const char __lookuptable[] = {
+ /* ' ' */ 0x06,
+ /* '!' */ 0x00,
+ /* '"' */ 0x00,
+ /* '#' */ 0x06,
+ /* '$' */ 0x00,
+ /* '%' */ 0x01,
+ /* '&' */ 0x00,
+ /* ''' */ 0x00,
+ /* '(' */ 0x10,
+ /* ')' */ 0x00,
+ /* '*' */ 0x03,
+ /* '+' */ 0x06,
+ /* ',' */ 0x00,
+ /* '-' */ 0x06,
+ /* '.' */ 0x02,
+ /* '/' */ 0x10,
+ /* '0' */ 0x04,
+ /* '1' */ 0x45,
+ /* '2' */ 0x45,
+ /* '3' */ 0x45,
+ /* '4' */ 0x05,
+ /* '5' */ 0x05,
+ /* '6' */ 0x05,
+ /* '7' */ 0x05,
+ /* '8' */ 0x05,
+ /* '9' */ 0x35,
+ /* ':' */ 0x30,
+ /* ';' */ 0x00,
+ /* '<' */ 0x50,
+ /* '=' */ 0x00,
+ /* '>' */ 0x00,
+ /* '?' */ 0x00,
+ /* '@' */ 0x00,
+ /* 'A' */ 0x20, // Disable %A format
+ /* 'B' */ 0x20,
+ /* 'C' */ 0x38,
+ /* 'D' */ 0x50,
+ /* 'E' */ 0x58,
+ /* 'F' */ 0x07,
+ /* 'G' */ 0x08,
+ /* 'H' */ 0x00,
+ /* 'I' */ 0x37,
+ /* 'J' */ 0x30,
+ /* 'K' */ 0x30,
+ /* 'L' */ 0x57,
+ /* 'M' */ 0x50,
+ /* 'N' */ 0x07,
+ /* 'O' */ 0x00,
+ /* 'P' */ 0x00,
+ /* 'Q' */ 0x20,
+ /* 'R' */ 0x20,
+ /* 'S' */ 0x08,
+ /* 'T' */ 0x00,
+ /* 'U' */ 0x00,
+ /* 'V' */ 0x00,
+ /* 'W' */ 0x00,
+ /* 'X' */ 0x08,
+ /* 'Y' */ 0x60,
+ /* 'Z' */ 0x68,
+ /* '[' */ 0x60,
+ /* '\' */ 0x60,
+ /* ']' */ 0x60,
+ /* '^' */ 0x60,
+ /* '_' */ 0x00,
+ /* '`' */ 0x00,
+ /* 'a' */ 0x70, // Disable %a format
+ /* 'b' */ 0x70,
+ /* 'c' */ 0x78,
+ /* 'd' */ 0x78,
+ /* 'e' */ 0x78,
+ /* 'f' */ 0x78,
+ /* 'g' */ 0x08,
+ /* 'h' */ 0x07,
+ /* 'i' */ 0x08,
+ /* 'j' */ 0x00,
+ /* 'k' */ 0x00,
+ /* 'l' */ 0x07,
+ /* 'm' */ 0x00,
+ /* 'n' */ 0x00, // Disable %n format
+ /* 'o' */ 0x08,
+ /* 'p' */ 0x08,
+ /* 'q' */ 0x00,
+ /* 'r' */ 0x00,
+ /* 's' */ 0x08,
+ /* 't' */ 0x00,
+ /* 'u' */ 0x08,
+ /* 'v' */ 0x00,
+ /* 'w' */ 0x07,
+ /* 'x' */ 0x08
+};
+
+#endif /* defined (_UNICODE) || defined (CPRFLAG) */
+
+#else /* FORMAT_VALIDATIONS */
+// SNIP -srs 7/3/07
+#error code has been removed
+#endif /* FORMAT_VALIDATIONS */
+
+#define FIND_CHAR_CLASS(lookuptbl, c) \
+ ((c) < _T(' ') || (c) > _T('x') ? \
+ CH_OTHER \
+ : \
+ (enum CHARTYPE)(lookuptbl[(c)-_T(' ')] & 0xF))
+
+#define FIND_NEXT_STATE(lookuptbl, class, state) \
+ (enum STATE)(lookuptbl[(class) * NUMSTATES + (state)] >> 4)
+
+/*
+ * Note: CPRFLAG and _UNICODE cases are currently mutually exclusive.
+ */
+
+/* prototypes */
+
+#ifdef CPRFLAG
+
+#define WRITE_CHAR(ch, pnw) write_char(ch, pnw)
+#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, pnw)
+#define WRITE_STRING(s, len, pnw) write_string(s, len, pnw)
+#define WRITE_WSTRING(s, len, pnw) write_wstring(s, len, pnw)
+
+LOCAL(void) write_char(_TCHAR ch, int *pnumwritten);
+LOCAL(void) write_multi_char(_TCHAR ch, int num, int *pnumwritten);
+LOCAL(void) write_string(const _TCHAR *string, int len, int *numwritten);
+LOCAL(void) write_wstring(const wchar_t *string, int len, int *numwritten);
+
+#else /* CPRFLAG */
+
+#define WRITE_CHAR(ch, pnw) write_char(ch, stream, pnw)
+#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, stream, pnw)
+#define WRITE_STRING(s, len, pnw) write_string(s, len, stream, pnw)
+#define WRITE_WSTRING(s, len, pnw) write_wstring(s, len, stream, pnw)
+
+LOCAL(void) write_char(_TCHAR ch, miniFILE *f, int *pnumwritten);
+LOCAL(void) write_multi_char(_TCHAR ch, int num, miniFILE *f, int *pnumwritten);
+LOCAL(void) write_string(const _TCHAR *string, int len, miniFILE *f, int *numwritten);
+//LOCAL(void) write_wstring(const wchar_t *string, int len, miniFILE *f, int *numwritten);
+
+#endif /* CPRFLAG */
+
+#define get_short_arg(list) va_arg(*list, int) // GCC promotes va_arg shorts into int values
+#define get_int_arg(list) va_arg(*list, int)
+#define get_long_arg(list) va_arg(*list, long)
+#define get_long_long_arg(list) va_arg(*list, long long)
+#define get_int64_arg(list) va_arg(*list, __int64)
+#define get_crtdouble_arg(list) va_arg(*list, _CRT_DOUBLE)
+#define get_ptr_arg(list) va_arg(*list, void *)
+
+#ifdef CPRFLAG
+LOCAL(int) output(const _TCHAR *, _locale_t , va_list);
+_CRTIMP int __cdecl _vtcprintf_l (const _TCHAR *, _locale_t, va_list);
+_CRTIMP int __cdecl _vtcprintf_s_l (const _TCHAR *, _locale_t, va_list);
+_CRTIMP int __cdecl _vtcprintf_p_l (const _TCHAR *, _locale_t, va_list);
+
+
+/***
+*int _cprintf(format, arglist) - write formatted output directly to console
+*
+*Purpose:
+* Writes formatted data like printf, but uses console I/O functions.
+*
+*Entry:
+* char *format - format string to determine data formats
+* arglist - list of POINTERS to where to put data
+*
+*Exit:
+* returns number of characters written
+*
+*Exceptions:
+*
+*******************************************************************************/
+#ifndef FORMAT_VALIDATIONS
+_CRTIMP int __cdecl _tcprintf_l (
+ const _TCHAR * format,
+ _locale_t plocinfo,
+ ...
+ )
+#else /* FORMAT_VALIDATIONS */
+_CRTIMP int __cdecl _tcprintf_s_l (
+ const _TCHAR * format,
+ _locale_t plocinfo,
+ ...
+ )
+#endif /* FORMAT_VALIDATIONS */
+
+{
+ int ret;
+ va_list arglist;
+ va_start(arglist, plocinfo);
+
+#ifndef FORMAT_VALIDATIONS
+ ret = _vtcprintf_l(format, plocinfo, arglist);
+#else /* FORMAT_VALIDATIONS */
+ ret = _vtcprintf_s_l(format, plocinfo, arglist);
+
+#endif /* FORMAT_VALIDATIONS */
+
+ va_end(arglist);
+
+ return ret;
+}
+
+#ifndef FORMAT_VALIDATIONS
+_CRTIMP int __cdecl _tcprintf (
+ const _TCHAR * format,
+ ...
+ )
+#else /* FORMAT_VALIDATIONS */
+_CRTIMP int __cdecl _tcprintf_s (
+ const _TCHAR * format,
+ ...
+ )
+#endif /* FORMAT_VALIDATIONS */
+
+{
+ int ret;
+ va_list arglist;
+
+ va_start(arglist, format);
+
+#ifndef FORMAT_VALIDATIONS
+ ret = _vtcprintf_l(format, NULL, arglist);
+#else /* FORMAT_VALIDATIONS */
+ ret = _vtcprintf_s_l(format, NULL, arglist);
+
+#endif /* FORMAT_VALIDATIONS */
+
+ va_end(arglist);
+
+ return ret;
+}
+
+#endif /* CPRFLAG */
+
+
+/***
+*int _output(stream, format, argptr), static int output(format, argptr)
+*
+*Purpose:
+* Output performs printf style output onto a stream. It is called by
+* printf/fprintf/sprintf/vprintf/vfprintf/vsprintf to so the dirty
+* work. In multi-thread situations, _output assumes that the given
+* stream is already locked.
+*
+* Algorithm:
+* The format string is parsed by using a finite state automaton
+* based on the current state and the current character read from
+* the format string. Thus, looping is on a per-character basis,
+* not a per conversion specifier basis. Once the format specififying
+* character is read, output is performed.
+*
+*Entry:
+* FILE *stream - stream for output
+* char *format - printf style format string
+* va_list argptr - pointer to list of subsidiary arguments
+*
+*Exit:
+* Returns the number of characters written, or -1 if an output error
+* occurs.
+*ifdef _UNICODE
+* The wide-character flavour returns the number of wide-characters written.
+*endif
+*
+*Exceptions:
+*
+*******************************************************************************/
+#ifdef CPRFLAG
+#ifndef FORMAT_VALIDATIONS
+_CRTIMP int __cdecl _vtcprintf (
+ const _TCHAR *format,
+ va_list argptr
+ )
+{
+ return _vtcprintf_l(format, NULL, argptr);
+}
+
+#else /* FORMAT_VALIDATIONS */
+_CRTIMP int __cdecl _vtcprintf_s (
+ const _TCHAR *format,
+ va_list argptr
+ )
+{
+ return _vtcprintf_s_l(format, NULL, argptr);
+}
+
+#endif /* FORMAT_VALIDATIONS */
+#endif /* CPRFLAG */
+
+#ifdef CPRFLAG
+#ifndef FORMAT_VALIDATIONS
+_CRTIMP int __cdecl _vtcprintf_l (
+#else /* FORMAT_VALIDATIONS */
+_CRTIMP int __cdecl _vtcprintf_s_l (
+#endif /* FORMAT_VALIDATIONS */
+#else /* CPRFLAG */
+
+#ifdef _UNICODE
+#ifndef FORMAT_VALIDATIONS
+int __cdecl _woutput (
+ miniFILE *stream,
+#else /* FORMAT_VALIDATIONS */
+int __cdecl _woutput_s (
+ miniFILE *stream,
+#endif /* FORMAT_VALIDATIONS */
+#else /* _UNICODE */
+#ifndef FORMAT_VALIDATIONS
+int __cdecl _output (
+ miniFILE *stream,
+#else /* FORMAT_VALIDATIONS */
+ int __cdecl _output_s (
+ miniFILE *stream,
+
+#endif /* FORMAT_VALIDATIONS */
+#endif /* _UNICODE */
+
+#endif /* CPRFLAG */
+ const _TCHAR *format,
+ va_list argptr
+ )
+{
+ int hexadd=0; /* offset to add to number to get 'a'..'f' */
+ TCHAR ch; /* character just read */
+ int flags=0; /* flag word -- see #defines above for flag values */
+ enum STATE state; /* current state */
+ enum CHARTYPE chclass; /* class of current character */
+ int radix; /* current conversion radix */
+ int charsout; /* characters currently written so far, -1 = IO error */
+ int fldwidth = 0; /* selected field width -- 0 means default */
+ int precision = 0; /* selected precision -- -1 means default */
+ TCHAR prefix[2]; /* numeric prefix -- up to two characters */
+ int prefixlen=0; /* length of prefix -- 0 means no prefix */
+ int capexp = 0; /* non-zero = 'E' exponent signifient, zero = 'e' */
+ int no_output=0; /* non-zero = prodcue no output for this specifier */
+ union {
+ const char *sz; /* pointer text to be printed, not zero terminated */
+ const wchar_t *wz;
+ } text;
+
+ int textlen; /* length of the text in bytes/wchars to be printed.
+ textlen is in multibyte or wide chars if _UNICODE */
+ union {
+ char sz[BUFFERSIZE];
+#ifdef _UNICODE
+ wchar_t wz[BUFFERSIZE];
+#endif /* _UNICODE */
+ } buffer;
+ wchar_t wchar; /* temp wchar_t */
+ int buffersize; /* size of text.sz (used only for the call to _cfltcvt) */
+ int bufferiswide=0; /* non-zero = buffer contains wide chars already */
+
+#ifndef CPRFLAG
+ _VALIDATE_RETURN( (stream != NULL), EINVAL, -1);
+#endif /* CPRFLAG */
+ _VALIDATE_RETURN( (format != NULL), EINVAL, -1);
+
+ charsout = 0; /* no characters written yet */
+ textlen = 0; /* no text yet */
+ state = ST_NORMAL; /* starting state */
+ buffersize = 0;
+
+ /* main loop -- loop while format character exist and no I/O errors */
+ while ((ch = *format++) != _T('\0') && charsout >= 0) {
+#ifndef FORMAT_VALIDATIONS
+ chclass = FIND_CHAR_CLASS(__lookuptable, ch); /* find character class */
+ state = FIND_NEXT_STATE(__lookuptable, chclass, state); /* find next state */
+#else /* FORMAT_VALIDATIONS */
+ chclass = FIND_CHAR_CLASS(__lookuptable_s, ch); /* find character class */
+ state = FIND_NEXT_STATE(__lookuptable_s, chclass, state); /* find next state */
+
+ _VALIDATE_RETURN((state != ST_INVALID), EINVAL, -1);
+
+#endif /* FORMAT_VALIDATIONS */
+
+ /* execute code for each state */
+ switch (state) {
+
+ case ST_NORMAL:
+
+ NORMAL_STATE:
+
+ /* normal state -- just write character */
+#ifdef _UNICODE
+ bufferiswide = 1;
+#else /* _UNICODE */
+ bufferiswide = 0;
+#endif /* _UNICODE */
+ WRITE_CHAR(ch, &charsout);
+ break;
+
+ case ST_PERCENT:
+ /* set default value of conversion parameters */
+ prefixlen = fldwidth = no_output = capexp = 0;
+ flags = 0;
+ precision = -1;
+ bufferiswide = 0; /* default */
+ break;
+
+ case ST_FLAG:
+ /* set flag based on which flag character */
+ switch (ch) {
+ case _T('-'):
+ flags |= FL_LEFT; /* '-' => left justify */
+ break;
+ case _T('+'):
+ flags |= FL_SIGN; /* '+' => force sign indicator */
+ break;
+ case _T(' '):
+ flags |= FL_SIGNSP; /* ' ' => force sign or space */
+ break;
+ case _T('#'):
+ flags |= FL_ALTERNATE; /* '#' => alternate form */
+ break;
+ case _T('0'):
+ flags |= FL_LEADZERO; /* '0' => pad with leading zeros */
+ break;
+ }
+ break;
+
+ case ST_WIDTH:
+ /* update width value */
+ if (ch == _T('*')) {
+ /* get width from arg list */
+ fldwidth = get_int_arg(&argptr);
+ if (fldwidth < 0) {
+ /* ANSI says neg fld width means '-' flag and pos width */
+ flags |= FL_LEFT;
+ fldwidth = -fldwidth;
+ }
+ }
+ else {
+ /* add digit to current field width */
+ fldwidth = fldwidth * 10 + (ch - _T('0'));
+ }
+ break;
+
+ case ST_DOT:
+ /* zero the precision, since dot with no number means 0
+ not default, according to ANSI */
+ precision = 0;
+ break;
+
+ case ST_PRECIS:
+ /* update precison value */
+ if (ch == _T('*')) {
+ /* get precision from arg list */
+ precision = get_int_arg(&argptr);
+ if (precision < 0)
+ precision = -1; /* neg precision means default */
+ }
+ else {
+ /* add digit to current precision */
+ precision = precision * 10 + (ch - _T('0'));
+ }
+ break;
+
+ case ST_SIZE:
+ /* just read a size specifier, set the flags based on it */
+ switch (ch) {
+ case _T('l'):
+ /*
+ * In order to handle the ll case, we depart from the
+ * simple deterministic state machine.
+ */
+ if (*format == _T('l'))
+ {
+ ++format;
+ flags |= FL_LONGLONG; /* 'll' => long long */
+ }
+ else
+ {
+ flags |= FL_LONG; /* 'l' => long int or wchar_t */
+ }
+ break;
+
+ case _T('I'):
+ /*
+ * In order to handle the I, I32, and I64 size modifiers, we
+ * depart from the simple deterministic state machine. The
+ * code below scans for characters following the 'I',
+ * and defaults to 64 bit on WIN64 and 32 bit on WIN32
+ */
+#if PTR_IS_INT64
+ flags |= FL_I64; /* 'I' => __int64 on WIN64 systems */
+#endif /* PTR_IS_INT64 */
+ if ( (*format == _T('6')) && (*(format + 1) == _T('4')) )
+ {
+ format += 2;
+ flags |= FL_I64; /* I64 => __int64 */
+ }
+ else if ( (*format == _T('3')) && (*(format + 1) == _T('2')) )
+ {
+ format += 2;
+ flags &= ~FL_I64; /* I32 => __int32 */
+ }
+ else if ( (*format == _T('d')) ||
+ (*format == _T('i')) ||
+ (*format == _T('o')) ||
+ (*format == _T('u')) ||
+ (*format == _T('x')) ||
+ (*format == _T('X')) )
+ {
+ /*
+ * Nothing further needed. %Id (et al) is
+ * handled just like %d, except that it defaults to 64 bits
+ * on WIN64. Fall through to the next iteration.
+ */
+ }
+ else {
+ state = ST_NORMAL;
+ goto NORMAL_STATE;
+ }
+ break;
+
+ case _T('h'):
+ flags |= FL_SHORT; /* 'h' => short int or char */
+ break;
+
+ case _T('w'):
+ flags |= FL_WIDECHAR; /* 'w' => wide character */
+ break;
+
+ }
+ break;
+
+ case ST_TYPE:
+ /* we have finally read the actual type character, so we */
+ /* now format and "print" the output. We use a big switch */
+ /* statement that sets 'text' to point to the text that should */
+ /* be printed, and 'textlen' to the length of this text. */
+ /* Common code later on takes care of justifying it and */
+ /* other miscellaneous chores. Note that cases share code, */
+ /* in particular, all integer formatting is done in one place. */
+ /* Look at those funky goto statements! */
+
+ switch (ch) {
+
+ case _T('C'): /* ISO wide character */
+ if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
+#ifdef _UNICODE
+ flags |= FL_SHORT;
+#else /* _UNICODE */
+ flags |= FL_WIDECHAR; /* ISO std. */
+#endif /* _UNICODE */
+ /* fall into 'c' case */
+
+ case _T('c'): {
+ /* print a single character specified by int argument */
+#ifdef _UNICODE
+ bufferiswide = 1;
+ wchar = (wchar_t) get_int_arg(&argptr);
+ if (flags & FL_SHORT) {
+ /* format multibyte character */
+ /* this is an extension of ANSI */
+ char tempchar[2];
+ {
+ tempchar[0] = (char)(wchar & 0x00ff);
+ tempchar[1] = '\0';
+ }
+
+ if (_MBTOWC(buffer.wz,tempchar, MB_CUR_MAX) < 0)
+ {
+ /* ignore if conversion was unsuccessful */
+ no_output = 1;
+ }
+ } else {
+ buffer.wz[0] = wchar;
+ }
+ text.wz = buffer.wz;
+ textlen = 1; /* print just a single character */
+#else /* _UNICODE */
+ if (flags & (FL_LONG|FL_WIDECHAR)) {
+ wchar = (wchar_t) get_short_arg(&argptr);
+ no_output = 1;
+ } else {
+ /* format multibyte character */
+ /* this is an extension of ANSI */
+ unsigned short temp;
+ wchar = (wchar_t)get_int_arg(&argptr);
+ temp = (unsigned short)wchar;
+ {
+ buffer.sz[0] = (char) temp;
+ textlen = 1;
+ }
+ }
+ text.sz = buffer.sz;
+#endif /* _UNICODE */
+ }
+ break;
+
+ case _T('Z'): {
+ /* print a Counted String */
+ struct _count_string {
+ short Length;
+ short MaximumLength;
+ char *Buffer;
+ } *pstr;
+
+ pstr = (struct _count_string *)get_ptr_arg(&argptr);
+ if (pstr == NULL || pstr->Buffer == NULL) {
+ /* null ptr passed, use special string */
+ text.sz = __nullstring;
+ textlen = (int)strlen(text.sz);
+ } else {
+ if (flags & FL_WIDECHAR) {
+ text.wz = (wchar_t *)pstr->Buffer;
+ textlen = pstr->Length / (int)sizeof(wchar_t);
+ bufferiswide = 1;
+ } else {
+ bufferiswide = 0;
+ text.sz = pstr->Buffer;
+ textlen = pstr->Length;
+ }
+ }
+ }
+ break;
+
+ case _T('S'): /* ISO wide character string */
+#ifndef _UNICODE
+ if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
+ flags |= FL_WIDECHAR;
+#else /* _UNICODE */
+ if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
+ flags |= FL_SHORT;
+#endif /* _UNICODE */
+
+ case _T('s'): {
+ /* print a string -- */
+ /* ANSI rules on how much of string to print: */
+ /* all if precision is default, */
+ /* min(precision, length) if precision given. */
+ /* prints '(null)' if a null string is passed */
+
+ int i;
+ const char *p; /* temps */
+ const wchar_t *pwch;
+
+ /* At this point it is tempting to use strlen(), but */
+ /* if a precision is specified, we're not allowed to */
+ /* scan past there, because there might be no null */
+ /* at all. Thus, we must do our own scan. */
+
+ i = (precision == -1) ? INT_MAX : precision;
+ text.sz = (char *)get_ptr_arg(&argptr);
+
+ /* scan for null upto i characters */
+#ifdef _UNICODE
+ if (flags & FL_SHORT) {
+ if (text.sz == NULL) /* NULL passed, use special string */
+ text.sz = __nullstring;
+ p = text.sz;
+ for (textlen=0; textlen<i && *p; textlen++) {
+ ++p;
+ }
+ /* textlen now contains length in multibyte chars */
+ } else {
+ if (text.wz == NULL) /* NULL passed, use special string */
+ text.wz = __wnullstring;
+ bufferiswide = 1;
+ pwch = text.wz;
+ while (i-- && *pwch)
+ ++pwch;
+ textlen = (int)(pwch - text.wz); /* in wchar_ts */
+ /* textlen now contains length in wide chars */
+ }
+#else /* _UNICODE */
+ if (flags & (FL_LONG|FL_WIDECHAR)) {
+ if (text.wz == NULL) /* NULL passed, use special string */
+ text.wz = __wnullstring;
+ bufferiswide = 1;
+ pwch = text.wz;
+ while ( i-- && *pwch )
+ ++pwch;
+ textlen = (int)(pwch - text.wz);
+ /* textlen now contains length in wide chars */
+ } else {
+ if (text.sz == NULL) /* NULL passed, use special string */
+ text.sz = __nullstring;
+ p = text.sz;
+ while (i-- && *p)
+ ++p;
+ textlen = (int)(p - text.sz); /* length of the string */
+ }
+
+#endif /* _UNICODE */
+ }
+ break;
+
+
+ case _T('n'): {
+ /* write count of characters seen so far into */
+ /* short/int/long thru ptr read from args */
+
+ void *p; /* temp */
+
+ p = get_ptr_arg(&argptr);
+
+ /* %n is disabled */
+ _VALIDATE_RETURN(("'n' format specifier disabled" && 0), EINVAL, -1);
+ break;
+
+ /* store chars out into short/long/int depending on flags */
+#if !LONG_IS_INT
+ if (flags & FL_LONG)
+ *(long *)p = charsout;
+ else
+#endif /* !LONG_IS_INT */
+
+#if !SHORT_IS_INT
+ if (flags & FL_SHORT)
+ *(short *)p = (short) charsout;
+ else
+#endif /* !SHORT_IS_INT */
+ *(int *)p = charsout;
+
+ no_output = 1; /* force no output */
+ }
+ break;
+
+ case _T('E'):
+ case _T('G'):
+ case _T('A'):
+ capexp = 1; /* capitalize exponent */
+ ch += _T('a') - _T('A'); /* convert format char to lower */
+ /* DROP THROUGH */
+ case _T('e'):
+ case _T('f'):
+ case _T('g'):
+ case _T('a'): {
+ /* floating point conversion -- we call cfltcvt routines */
+ /* to do the work for us. */
+ flags |= FL_SIGNED; /* floating point is signed conversion */
+ text.sz = buffer.sz; /* put result in buffer */
+ buffersize = BUFFERSIZE;
+
+ /* compute the precision value */
+ if (precision < 0)
+ precision = 6; /* default precision: 6 */
+ else if (precision == 0 && ch == _T('g'))
+ precision = 1; /* ANSI specified */
+ else if (precision > MAXPRECISION)
+ precision = MAXPRECISION;
+
+ if (precision > BUFFERSIZE - _CVTBUFSIZE) {
+ precision = BUFFERSIZE - _CVTBUFSIZE;
+ }
+
+ /* for safecrt, we pass along the FL_ALTERNATE flag to _safecrt_cfltcvt */
+ if (flags & FL_ALTERNATE)
+ {
+ capexp |= FL_ALTERNATE;
+ }
+
+ _CRT_DOUBLE tmp;
+ tmp=va_arg(argptr, _CRT_DOUBLE);
+ /* Note: assumes ch is in ASCII range */
+ /* In safecrt, we provide a special version of _cfltcvt which internally calls printf (see safecrt_output_s.c) */
+ _CFLTCVT(&tmp, buffer.sz, buffersize, (char)ch, precision, capexp);
+
+ /* check if result was negative, save '-' for later */
+ /* and point to positive part (this is for '0' padding) */
+ if (*text.sz == '-') {
+ flags |= FL_NEGATIVE;
+ ++text.sz;
+ }
+
+ textlen = (int)strlen(text.sz); /* compute length of text */
+ }
+ break;
+
+ case _T('d'):
+ case _T('i'):
+ /* signed decimal output */
+ flags |= FL_SIGNED;
+ radix = 10;
+ goto COMMON_INT;
+
+ case _T('u'):
+ radix = 10;
+ goto COMMON_INT;
+
+ case _T('p'):
+ /* write a pointer -- this is like an integer or long */
+ /* except we force precision to pad with zeros and */
+ /* output in big hex. */
+
+ precision = 2 * sizeof(void *); /* number of hex digits needed */
+#if PTR_IS_INT64
+ flags |= FL_I64; /* assume we're converting an int64 */
+#elif !PTR_IS_INT
+ flags |= FL_LONG; /* assume we're converting a long */
+#endif /* !PTR_IS_INT */
+ /* DROP THROUGH to hex formatting */
+
+ case _T('X'):
+ /* unsigned upper hex output */
+ hexadd = _T('A') - _T('9') - 1; /* set hexadd for uppercase hex */
+ goto COMMON_HEX;
+
+ case _T('x'):
+ /* unsigned lower hex output */
+ hexadd = _T('a') - _T('9') - 1; /* set hexadd for lowercase hex */
+ /* DROP THROUGH TO COMMON_HEX */
+
+ COMMON_HEX:
+ radix = 16;
+ if (flags & FL_ALTERNATE) {
+ /* alternate form means '0x' prefix */
+ prefix[0] = _T('0');
+ prefix[1] = (TCHAR)(_T('x') - _T('a') + _T('9') + 1 + hexadd); /* 'x' or 'X' */
+ prefixlen = 2;
+ }
+ goto COMMON_INT;
+
+ case _T('o'):
+ /* unsigned octal output */
+ radix = 8;
+ if (flags & FL_ALTERNATE) {
+ /* alternate form means force a leading 0 */
+ flags |= FL_FORCEOCTAL;
+ }
+ /* DROP THROUGH to COMMON_INT */
+
+ COMMON_INT: {
+ /* This is the general integer formatting routine. */
+ /* Basically, we get an argument, make it positive */
+ /* if necessary, and convert it according to the */
+ /* correct radix, setting text and textlen */
+ /* appropriately. */
+
+#if _INTEGRAL_MAX_BITS >= 64
+// unsigned __int64 number; /* number to convert */
+ uint64_t number; /* number to convert */
+ int digit; /* ascii value of digit */
+ __int64 l; /* temp long value */
+#else /* _INTEGRAL_MAX_BITS >= 64 */
+ unsigned long number; /* number to convert */
+ int digit; /* ascii value of digit */
+ long l; /* temp long value */
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+
+ /* 1. read argument into l, sign extend as needed */
+#if _INTEGRAL_MAX_BITS >= 64
+ if (flags & FL_I64)
+ l = get_int64_arg(&argptr);
+ else
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+
+ if (flags & FL_LONGLONG)
+ l = get_long_long_arg(&argptr);
+ else
+
+#if !LONG_IS_INT
+ if (flags & FL_LONG)
+ l = get_long_arg(&argptr);
+ else
+#endif /* !LONG_IS_INT */
+
+#if !SHORT_IS_INT
+ if (flags & FL_SHORT) {
+ if (flags & FL_SIGNED)
+ l = (short) get_int_arg(&argptr); /* sign extend */
+ else
+ l = (unsigned short) get_int_arg(&argptr); /* zero-extend*/
+
+ } else
+#endif /* !SHORT_IS_INT */
+ {
+ if (flags & FL_SIGNED)
+ l = get_int_arg(&argptr); /* sign extend */
+ else
+ l = (unsigned int) get_int_arg(&argptr); /* zero-extend*/
+
+ }
+
+ /* 2. check for negative; copy into number */
+ if ( (flags & FL_SIGNED) && l < 0) {
+ number = -l;
+ flags |= FL_NEGATIVE; /* remember negative sign */
+ } else {
+ number = l;
+ }
+
+#if _INTEGRAL_MAX_BITS >= 64
+ if ( (flags & FL_I64) == 0 && (flags & FL_LONGLONG) == 0 ) {
+ /*
+ * Unless printing a full 64-bit value, insure values
+ * here are not in cananical longword format to prevent
+ * the sign extended upper 32-bits from being printed.
+ */
+ number &= 0xffffffff;
+ }
+#endif /* _INTEGRAL_MAX_BITS >= 64 */
+
+ /* 3. check precision value for default; non-default */
+ /* turns off 0 flag, according to ANSI. */
+ if (precision < 0)
+ precision = 1; /* default precision */
+ else {
+ flags &= ~FL_LEADZERO;
+ if (precision > MAXPRECISION)
+ precision = MAXPRECISION;
+ }
+
+ /* 4. Check if data is 0; if so, turn off hex prefix */
+ if (number == 0)
+ prefixlen = 0;
+
+ /* 5. Convert data to ASCII -- note if precision is zero */
+ /* and number is zero, we get no digits at all. */
+
+ char *sz;
+ sz = &buffer.sz[BUFFERSIZE-1]; /* last digit at end of buffer */
+
+ while (precision-- > 0 || number != 0) {
+ digit = (int)(number % radix) + '0';
+ number /= radix; /* reduce number */
+ if (digit > '9') {
+ /* a hex digit, make it a letter */
+ digit += hexadd;
+ }
+ *sz-- = (char)digit; /* store the digit */
+ }
+
+ textlen = (int)((char *)&buffer.sz[BUFFERSIZE-1] - sz); /* compute length of number */
+ ++sz; /* text points to first digit now */
+
+
+ /* 6. Force a leading zero if FORCEOCTAL flag set */
+ if ((flags & FL_FORCEOCTAL) && (textlen == 0 || sz[0] != '0')) {
+ *--sz = '0';
+ ++textlen; /* add a zero */
+ }
+
+ text.sz = sz;
+ }
+ break;
+ }
+
+
+ /* At this point, we have done the specific conversion, and */
+ /* 'text' points to text to print; 'textlen' is length. Now we */
+ /* justify it, put on prefixes, leading zeros, and then */
+ /* print it. */
+
+ if (!no_output) {
+ int padding; /* amount of padding, negative means zero */
+
+ if (flags & FL_SIGNED) {
+ if (flags & FL_NEGATIVE) {
+ /* prefix is a '-' */
+ prefix[0] = _T('-');
+ prefixlen = 1;
+ }
+ else if (flags & FL_SIGN) {
+ /* prefix is '+' */
+ prefix[0] = _T('+');
+ prefixlen = 1;
+ }
+ else if (flags & FL_SIGNSP) {
+ /* prefix is ' ' */
+ prefix[0] = _T(' ');
+ prefixlen = 1;
+ }
+ }
+
+ /* calculate amount of padding -- might be negative, */
+ /* but this will just mean zero */
+ padding = fldwidth - textlen - prefixlen;
+
+ /* put out the padding, prefix, and text, in the correct order */
+
+ if (!(flags & (FL_LEFT | FL_LEADZERO))) {
+ /* pad on left with blanks */
+ WRITE_MULTI_CHAR(_T(' '), padding, &charsout);
+ }
+
+ /* write prefix */
+ WRITE_STRING(prefix, prefixlen, &charsout);
+
+ if ((flags & FL_LEADZERO) && !(flags & FL_LEFT)) {
+ /* write leading zeros */
+ WRITE_MULTI_CHAR(_T('0'), padding, &charsout);
+ }
+
+ /* write text */
+#ifndef _UNICODE
+ if (bufferiswide && (textlen > 0)) {
+ charsout = -1;
+ } else {
+ WRITE_STRING(text.sz, textlen, &charsout);
+ }
+#else /* _UNICODE */
+ if (!bufferiswide && textlen > 0) {
+ char *p;
+ int retval = 0
+ int count;
+
+ p = text.sz;
+ count = textlen;
+ while (count-- > 0) {
+ retval = _MBTOWC(&wchar, p, MB_CUR_MAX);
+ if (retval <= 0) {
+ charsout = -1;
+ break;
+ }
+ WRITE_CHAR(wchar, &charsout);
+ p += retval;
+ }
+ } else {
+ WRITE_STRING(text.wz, textlen, &charsout);
+ }
+#endif /* _UNICODE */
+
+ if (charsout >= 0 && (flags & FL_LEFT)) {
+ /* pad on right with blanks */
+ WRITE_MULTI_CHAR(_T(' '), padding, &charsout);
+ }
+
+ /* we're done! */
+ }
+ break;
+ }
+ }
+
+#ifdef FORMAT_VALIDATIONS
+ /* The format string shouldn't be incomplete - i.e. when we are finished
+ with the format string, the last thing we should have encountered
+ should have been a regular char to be output or a type specifier. Else
+ the format string was incomplete */
+ _VALIDATE_RETURN(((state == ST_NORMAL) || (state == ST_TYPE)), EINVAL, -1);
+#endif /* FORMAT_VALIDATIONS */
+
+ return charsout; /* return value = number of characters written */
+}
+
+/*
+ * Future Optimizations for swprintf:
+ * - Don't free the memory used for converting the buffer to wide chars.
+ * Use realloc if the memory is not sufficient. Free it at the end.
+ */
+
+/***
+*void write_char(char ch, int *pnumwritten)
+*ifdef _UNICODE
+*void write_char(wchar_t ch, FILE *f, int *pnumwritten)
+*endif
+*void write_char(char ch, FILE *f, int *pnumwritten)
+*
+*Purpose:
+* Writes a single character to the given file/console. If no error occurs,
+* then *pnumwritten is incremented; otherwise, *pnumwritten is set
+* to -1.
+*
+*Entry:
+* _TCHAR ch - character to write
+* FILE *f - file to write to
+* int *pnumwritten - pointer to integer to update with total chars written
+*
+*Exit:
+* No return value.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+#ifdef CPRFLAG
+
+LOCAL(void) write_char (
+ _TCHAR ch,
+ int *pnumwritten
+ )
+{
+#ifdef _UNICODE
+ if (_putwch_nolock(ch) == WEOF)
+#else /* _UNICODE */
+ if (_putch_nolock(ch) == EOF)
+#endif /* _UNICODE */
+ *pnumwritten = -1;
+ else
+ ++(*pnumwritten);
+}
+
+#else /* CPRFLAG */
+
+LOCAL(void) write_char (
+ _TCHAR ch,
+ miniFILE *f,
+ int *pnumwritten
+ )
+{
+ if ( (f->_flag & _IOSTRG) && f->_base == NULL)
+ {
+ ++(*pnumwritten);
+ return;
+ }
+#ifdef _UNICODE
+ if (_putwc_nolock(ch, f) == WEOF)
+#else /* _UNICODE */
+ if (_putc_nolock(ch, f) == EOF)
+#endif /* _UNICODE */
+ *pnumwritten = -1;
+ else
+ ++(*pnumwritten);
+}
+
+#endif /* CPRFLAG */
+
+/***
+*void write_multi_char(char ch, int num, int *pnumwritten)
+*ifdef _UNICODE
+*void write_multi_char(wchar_t ch, int num, FILE *f, int *pnumwritten)
+*endif
+*void write_multi_char(char ch, int num, FILE *f, int *pnumwritten)
+*
+*Purpose:
+* Writes num copies of a character to the given file/console. If no error occurs,
+* then *pnumwritten is incremented by num; otherwise, *pnumwritten is set
+* to -1. If num is negative, it is treated as zero.
+*
+*Entry:
+* _TCHAR ch - character to write
+* int num - number of times to write the characters
+* FILE *f - file to write to
+* int *pnumwritten - pointer to integer to update with total chars written
+*
+*Exit:
+* No return value.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+#ifdef CPRFLAG
+LOCAL(void) write_multi_char (
+ _TCHAR ch,
+ int num,
+ int *pnumwritten
+ )
+{
+ while (num-- > 0) {
+ write_char(ch, pnumwritten);
+ if (*pnumwritten == -1)
+ break;
+ }
+}
+
+#else /* CPRFLAG */
+
+LOCAL(void) write_multi_char (
+ _TCHAR ch,
+ int num,
+ miniFILE *f,
+ int *pnumwritten
+ )
+{
+ while (num-- > 0) {
+ write_char(ch, f, pnumwritten);
+ if (*pnumwritten == -1)
+ break;
+ }
+}
+
+#endif /* CPRFLAG */
+
+/***
+*void write_string(const char *string, int len, int *pnumwritten)
+*void write_string(const char *string, int len, FILE *f, int *pnumwritten)
+*ifdef _UNICODE
+*void write_string(const wchar_t *string, int len, FILE *f, int *pnumwritten)
+*endif
+*void write_wstring(const wchar_t *string, int len, int *pnumwritten)
+*void write_wstring(const wchar_t *string, int len, FILE *f, int *pnumwritten)
+*
+*Purpose:
+* Writes a string of the given length to the given file. If no error occurs,
+* then *pnumwritten is incremented by len; otherwise, *pnumwritten is set
+* to -1. If len is negative, it is treated as zero.
+*
+*Entry:
+* _TCHAR *string - string to write (NOT null-terminated)
+* int len - length of string
+* FILE *f - file to write to
+* int *pnumwritten - pointer to integer to update with total chars written
+*
+*Exit:
+* No return value.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+#ifdef CPRFLAG
+
+LOCAL(void) write_string (
+ const _TCHAR *string,
+ int len,
+ int *pnumwritten
+ )
+{
+ while (len-- > 0) {
+ write_char(*string++, pnumwritten);
+ if (*pnumwritten == -1)
+ {
+ if (errno == EILSEQ)
+ write_char(_T('?'), pnumwritten);
+ else
+ break;
+ }
+ }
+}
+
+#else /* CPRFLAG */
+
+LOCAL(void) write_string (
+ const _TCHAR *string,
+ int len,
+ miniFILE *f,
+ int *pnumwritten
+ )
+{
+ if ( (f->_flag & _IOSTRG) && f->_base == NULL)
+ {
+ (*pnumwritten) += len;
+ return;
+ }
+ while (len-- > 0) {
+ write_char(*string++, f, pnumwritten);
+ if (*pnumwritten == -1)
+ {
+ if (errno == EILSEQ)
+ write_char(_T('?'), f, pnumwritten);
+ else
+ break;
+ }
+ }
+}
+#endif /* CPRFLAG */
diff --git a/src/pal/src/safecrt/safecrt_output_s.c b/src/pal/src/safecrt/safecrt_output_s.c
new file mode 100644
index 0000000000..c3e7f91404
--- /dev/null
+++ b/src/pal/src/safecrt/safecrt_output_s.c
@@ -0,0 +1,46 @@
+// 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.
+
+/***
+*safecrt_output_s.c - implementation of the _output family for safercrt.lib
+*
+
+*
+*Purpose:
+* This file contains the implementation of the _output family for safercrt.lib.
+*
+*Revision History:
+* 07-08-04 SJ Stub module created.
+* 07-13-04 AC Added support for floating-point types.
+* 07-29-04 AC Added macros for a safecrt version of mctowc and wctomb, which target ntdll.dll or msvcrt.dll
+* based on the _NTSUBSET_ #define
+* 09-24-04 MSL Prefix disallow NULL deref
+*
+****/
+
+#define _SAFECRT_IMPL
+
+#define __STDC_LIMIT_MACROS
+#include "pal/palinternal.h"
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define FORMAT_VALIDATIONS
+#define _CFLTCVT _safecrt_cfltcvt
+
+#define _TCHAR CRT_TCHAR
+#define TCHAR CRTTCHAR
+
+typedef char _TCHAR;
+typedef char TCHAR;
+#define _T(x) x
+
+#include "output.inl"
diff --git a/src/pal/src/safecrt/safecrt_winput_s.c b/src/pal/src/safecrt/safecrt_winput_s.c
new file mode 100644
index 0000000000..17a621781b
--- /dev/null
+++ b/src/pal/src/safecrt/safecrt_winput_s.c
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/***
+*safecrt_winput_s.c - implementation of the _winput family for safecrt.lib
+*
+
+*
+*Purpose:
+* This file contains the implementation of the _winput family for safecrt.lib.
+*
+*Revision History:
+* 07/19/04 AC Created
+*
+****/
+
+
+#ifndef _UNICODE /* CRT flag */
+#define _UNICODE 1
+#endif
+
+#ifndef UNICODE /* NT flag */
+#define UNICODE 1
+#endif
+
+#define _SAFECRT_IMPL
+#define _SECURE_SCANF
+
+#include "pal/palinternal.h"
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _TCHAR CRT_TCHAR
+#define TCHAR CRTTCHAR
+
+typedef wchar_t _TCHAR;
+typedef wchar_t TCHAR;
+typedef wchar_t _TUCHAR;
+#define _T(x) x
+#define _TEOF WEOF
+
+#define _gettc_nolock(x) _getwc_nolock(x)
+#define _ungettc_nolock(x,y) _ungetwc_nolock(x,y)
+
+#include "input.inl"
+
diff --git a/src/pal/src/safecrt/safecrt_woutput_s.c b/src/pal/src/safecrt/safecrt_woutput_s.c
new file mode 100644
index 0000000000..52fe9400d5
--- /dev/null
+++ b/src/pal/src/safecrt/safecrt_woutput_s.c
@@ -0,0 +1,59 @@
+// 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.
+
+/***
+*safecrt_woutput_s.c - implementation of the _woutput family for safercrt.lib
+*
+
+*
+*Purpose:
+* This file contains the implementation of the _woutput family for safercrt.lib.
+*
+*Revision History:
+* 07-08-04 SJ Stub module created.
+* 07-13-04 AC Added support for floating-point types.
+* 07-29-04 AC Added macros for a safecrt version of mctowc and wctomb, which target ntdll.dll or msvcrt.dll
+* based on the _NTSUBSET_ #define
+*
+****/
+
+#define _SAFECRT_IMPL
+
+#define __STDC_LIMIT_MACROS
+
+#include "pal/palinternal.h"
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#ifndef _UNICODE /* CRT flag */
+#define _UNICODE 1
+#endif
+
+#ifndef UNICODE /* NT flag */
+#define UNICODE 1
+#endif
+
+#define FORMAT_VALIDATIONS
+#if defined(_NTSUBSET_)
+#define _MBTOWC _safecrt_mbtowc
+#endif
+#define _WCTOMB_S _safecrt_wctomb_s
+#define _CFLTCVT _safecrt_cfltcvt
+#define _CLDCVT _safecrt_cldcvt
+
+#define _TCHAR CRT_TCHAR
+#define TCHAR CRTTCHAR
+
+typedef wchar_t _TCHAR;
+typedef wchar_t TCHAR;
+#define _T(x) L##x
+
+#include "output.inl"
diff --git a/src/pal/src/safecrt/snprintf.c b/src/pal/src/safecrt/snprintf.c
new file mode 100644
index 0000000000..c892d1a9b6
--- /dev/null
+++ b/src/pal/src/safecrt/snprintf.c
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/***
+*snprintf.c - "Count" version of sprintf
+*
+
+*
+*Purpose:
+* The _snprintf() flavor takes a count argument that is
+* the max number of bytes that should be written to the
+* user's buffer.
+*
+*******************************************************************************/
+
+#define _COUNT_ 1
+#include "sprintf.c"
diff --git a/src/pal/src/safecrt/splitpath_s.c b/src/pal/src/safecrt/splitpath_s.c
new file mode 100644
index 0000000000..cb8a364550
--- /dev/null
+++ b/src/pal/src/safecrt/splitpath_s.c
@@ -0,0 +1,31 @@
+// 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.
+
+/***
+*splitpath_s.c - break down path name into components
+*
+
+*
+*Purpose:
+* To provide support for accessing the individual components of an
+* arbitrary path name
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME _splitpath_s
+#define _CHAR char
+#define _TCSNCPY_S strncpy_s
+#define _T(_Character) _Character
+
+#define _MBS_SUPPORT 0
+
+#include "tsplitpath_s.inl"
diff --git a/src/pal/src/safecrt/sprintf.c b/src/pal/src/safecrt/sprintf.c
new file mode 100644
index 0000000000..5454179f8d
--- /dev/null
+++ b/src/pal/src/safecrt/sprintf.c
@@ -0,0 +1,98 @@
+// 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.
+
+/***
+*sprintf.c - print formatted to string
+*
+
+*
+*Purpose:
+* defines sprintf() and _snprintf() - print formatted data to string
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+
+/***
+*ifndef _COUNT_
+*int sprintf(string, format, ...) - print formatted data to string
+*else
+*int _snprintf(string, cnt, format, ...) - print formatted data to string
+*endif
+*
+*Purpose:
+* Prints formatted data to the using the format string to
+* format data and getting as many arguments as called for
+* Sets up a FILE so file i/o operations can be used, make
+* string look like a huge buffer to it, but _flsbuf will
+* refuse to flush it if it fills up. Appends '\0' to make
+* it a true string. _output does the real work here
+*
+* Allocate the 'fake' _iob[] entry statically instead of on
+* the stack so that other routines can assume that _iob[]
+* entries are in are in DGROUP and, thus, are near.
+*
+*ifdef _COUNT_
+* The _snprintf() flavor takes a count argument that is
+* the max number of bytes that should be written to the
+* user's buffer.
+*endif
+*
+* Multi-thread: (1) Since there is no stream, this routine must
+* never try to get the stream lock (i.e., there is no stream
+* lock either). (2) Also, since there is only one statically
+* allocated 'fake' iob, we must lock/unlock to prevent collisions.
+*
+*Entry:
+* char *string - pointer to place to put output
+*ifdef _COUNT_
+* size_t count - max number of bytes to put in buffer
+*endif
+* char *format - format string to control data format/number
+* of arguments followed by list of arguments, number and type
+* controlled by format string
+*
+*Exit:
+* returns number of characters printed
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+int sprintf_s (
+ char *string,
+ size_t sizeInBytes,
+ const char *format,
+ ...
+ )
+{
+ int ret;
+ va_list arglist;
+ va_start(arglist, format);
+ ret = _vsprintf_s(string, sizeInBytes, format, arglist);
+ va_end(arglist);
+ return ret;
+}
+
+int _snprintf_s (
+ char *string,
+ size_t sizeInBytes,
+ size_t count,
+ const char *format,
+ ...
+ )
+{
+ int ret;
+ va_list arglist;
+ va_start(arglist, format);
+ ret = _vsnprintf_s(string, sizeInBytes, count, format, arglist);
+ va_end(arglist);
+ return ret;
+}
diff --git a/src/pal/src/safecrt/sscanf.c b/src/pal/src/safecrt/sscanf.c
new file mode 100644
index 0000000000..94b5148875
--- /dev/null
+++ b/src/pal/src/safecrt/sscanf.c
@@ -0,0 +1,249 @@
+// 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.
+
+/***
+*sscanf.c - read formatted data from string
+*
+
+*
+*Purpose:
+* defines scanf() - reads formatted data from string
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+typedef int (*INPUTFN)(miniFILE *, const unsigned char*, va_list);
+typedef int (*WINPUTFN)(miniFILE *, const unsigned short*, va_list);
+
+
+/***
+*static int v[nw]scan_fn([w]inputfn, string, [count], format, ...)
+*
+*Purpose:
+* this is a helper function which is called by the other functions
+* in this file - sscanf/swscanf/snscanf etc. It calls either _(w)input or
+* _(w)input_s depending on the first parameter.
+*
+*******************************************************************************/
+
+static int __cdecl vscan_fn (
+ INPUTFN inputfn,
+ const char *string,
+ const char *format,
+ va_list arglist
+ )
+{
+ miniFILE str;
+ miniFILE *infile = &str;
+ int retval;
+ size_t count = strlen(string);
+
+ _VALIDATE_RETURN( (string != NULL), EINVAL, EOF);
+ _VALIDATE_RETURN( (format != NULL), EINVAL, EOF);
+
+ infile->_flag = _IOREAD|_IOSTRG|_IOMYBUF;
+ infile->_ptr = infile->_base = (char *) string;
+
+ if(count>(INT_MAX/sizeof(char)))
+ {
+ /* old-style functions allow any large value to mean unbounded */
+ infile->_cnt = INT_MAX;
+ }
+ else
+ {
+ infile->_cnt = (int)count*sizeof(char);
+ }
+
+ retval = (inputfn(infile, ( const unsigned char* )format, arglist));
+
+ return(retval);
+}
+
+static int __cdecl vnscan_fn (
+ INPUTFN inputfn,
+ const char *string,
+ size_t count,
+ const char *format,
+ va_list arglist
+ )
+{
+ miniFILE str;
+ miniFILE *infile = &str;
+ int retval;
+ size_t length = strlen(string);
+
+ _VALIDATE_RETURN( (string != NULL), EINVAL, EOF);
+ _VALIDATE_RETURN( (format != NULL), EINVAL, EOF);
+
+ infile->_flag = _IOREAD|_IOSTRG|_IOMYBUF;
+ infile->_ptr = infile->_base = (char *) string;
+
+ if ( count > length )
+ {
+ count = length;
+ }
+
+ if(count>(INT_MAX/sizeof(char)))
+ {
+ /* old-style functions allow any large value to mean unbounded */
+ infile->_cnt = INT_MAX;
+ }
+ else
+ {
+ infile->_cnt = (int)count*sizeof(char);
+ }
+
+ retval = (inputfn(infile, ( const unsigned char* )format, arglist));
+
+ return(retval);
+}
+
+static int __cdecl vwscan_fn (
+ WINPUTFN inputfn,
+ const wchar_t *string,
+ const wchar_t *format,
+ va_list arglist
+ )
+{
+ miniFILE str;
+ miniFILE *infile = &str;
+ int retval;
+ size_t count = wcsnlen(string, INT_MAX);
+
+ _VALIDATE_RETURN( (string != NULL), EINVAL, EOF);
+ _VALIDATE_RETURN( (format != NULL), EINVAL, EOF);
+
+ infile->_flag = _IOREAD|_IOSTRG|_IOMYBUF;
+ infile->_ptr = infile->_base = (char *) string;
+
+ if(count>(INT_MAX/sizeof(wchar_t)))
+ {
+ /* old-style functions allow any large value to mean unbounded */
+ infile->_cnt = INT_MAX;
+ }
+ else
+ {
+ infile->_cnt = (int)count*sizeof(wchar_t);
+ }
+
+ retval = (inputfn(infile, format, arglist));
+
+ return(retval);
+}
+
+static int __cdecl vnwscan_fn (
+ WINPUTFN inputfn,
+ const wchar_t *string,
+ size_t count,
+ const wchar_t *format,
+ va_list arglist
+ )
+{
+ miniFILE str;
+ miniFILE *infile = &str;
+ int retval;
+ size_t length = wcsnlen(string, INT_MAX);
+
+ _VALIDATE_RETURN( (string != NULL), EINVAL, EOF);
+ _VALIDATE_RETURN( (format != NULL), EINVAL, EOF);
+
+ infile->_flag = _IOREAD|_IOSTRG|_IOMYBUF;
+ infile->_ptr = infile->_base = (char *) string;
+
+ if ( count > length )
+ {
+ count = length;
+ }
+
+ if(count>(INT_MAX/sizeof(wchar_t)))
+ {
+ /* old-style functions allow any large value to mean unbounded */
+ infile->_cnt = INT_MAX;
+ }
+ else
+ {
+ infile->_cnt = (int)count*sizeof(wchar_t);
+ }
+
+ retval = (inputfn(infile, format, arglist));
+
+ return(retval);
+}
+
+
+/***
+*int sscanf_s(string, format, ...)
+* Same as sscanf above except that it calls _input_s to do the real work.
+*
+*int snscanf_s(string, size, format, ...)
+* Same as snscanf above except that it calls _input_s to do the real work.
+*
+* _input_s has a size check for array parameters.
+*
+*******************************************************************************/
+
+int __cdecl sscanf_s (
+ const char *string,
+ const char *format,
+ ...
+ )
+{
+ int ret;
+ va_list arglist;
+ va_start(arglist, format);
+ ret = vscan_fn(__tinput_s, string, format, arglist);
+ va_end(arglist);
+ return ret;
+}
+
+int __cdecl _snscanf_s (
+ const char *string,
+ size_t count,
+ const char *format,
+ ...
+ )
+{
+ int ret;
+ va_list arglist;
+ va_start(arglist, format);
+ ret = vnscan_fn(__tinput_s, string, count, format, arglist);
+ va_end(arglist);
+ return ret;
+}
+
+int __cdecl swscanf_s (
+ const wchar_t *string,
+ const wchar_t *format,
+ ...
+ )
+{
+ int ret;
+ va_list arglist;
+ va_start(arglist, format);
+ ret = vwscan_fn(__twinput_s, string, format, arglist);
+ va_end(arglist);
+ return ret;
+}
+
+int __cdecl _snwscanf_s (
+ const wchar_t *string,
+ size_t count,
+ const wchar_t *format,
+ ...
+ )
+{
+ int ret;
+ va_list arglist;
+ va_start(arglist, format);
+ ret = vnwscan_fn(__twinput_s, string, count, format, arglist);
+ va_end(arglist);
+ return ret;
+}
+
diff --git a/src/pal/src/safecrt/strcat_s.c b/src/pal/src/safecrt/strcat_s.c
new file mode 100644
index 0000000000..4dc2332006
--- /dev/null
+++ b/src/pal/src/safecrt/strcat_s.c
@@ -0,0 +1,33 @@
+// 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.
+
+/***
+*strcat_s.c - contains strcat_s()
+*
+
+*
+*Purpose:
+* strcat_s() concatenates (appends) a copy of the source string to the
+* end of the destination string.
+*
+*******************************************************************************/
+
+#define _SECURECRT_FILL_BUFFER 1
+#define _SECURECRT_FILL_BUFFER_THRESHOLD ((size_t)8)
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME strcat_s
+#define _CHAR char
+#define _DEST _Dst
+#define _SIZE _SizeInBytes
+#define _SRC _Src
+
+#include "tcscat_s.inl"
diff --git a/src/pal/src/safecrt/strcpy_s.c b/src/pal/src/safecrt/strcpy_s.c
new file mode 100644
index 0000000000..821dbe85f6
--- /dev/null
+++ b/src/pal/src/safecrt/strcpy_s.c
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/***
+*strcpy_s.c - contains strcpy_s()
+*
+
+*
+*Purpose:
+* strcpy_s() copies one string onto another.
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME strcpy_s
+#define _CHAR char
+#define _DEST _Dst
+#define _SIZE _SizeInBytes
+#define _SRC _Src
+
+#include "tcscpy_s.inl"
diff --git a/src/pal/src/safecrt/strlen_s.c b/src/pal/src/safecrt/strlen_s.c
new file mode 100644
index 0000000000..34c1308d5c
--- /dev/null
+++ b/src/pal/src/safecrt/strlen_s.c
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/***
+*strlen_s.c - contains strnlen() routine
+*
+
+*
+*Purpose:
+* strnlen returns the length of a null-terminated string,
+* not including the null byte itself, up to the specified max size
+*
+*******************************************************************************/
+
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+/***
+*strnlen - return the length of a null-terminated string
+*
+*Purpose:
+* Finds the length in bytes of the given string, not including
+* the final null character. Only the first maxsize characters
+* are inspected: if the null character is not found, maxsize is
+* returned.
+*
+*Entry:
+* const char * str - string whose length is to be computed
+* size_t maxsize
+*
+*Exit:
+* Length of the string "str", exclusive of the final null byte, or
+* maxsize if the null character is not found.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+size_t __cdecl strnlen(const char *str, size_t maxsize)
+{
+ size_t n;
+
+ /* Note that we do not check if str == NULL, because we do not
+ * return errno_t...
+ */
+
+ for (n = 0; n < maxsize && *str; n++, str++)
+ ;
+
+ return n;
+}
+
diff --git a/src/pal/src/safecrt/strncat_s.c b/src/pal/src/safecrt/strncat_s.c
new file mode 100644
index 0000000000..ef8c6cfc7f
--- /dev/null
+++ b/src/pal/src/safecrt/strncat_s.c
@@ -0,0 +1,31 @@
+// 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.
+
+/***
+*strncat_s.c - append n chars of string to new string
+*
+
+*
+*Purpose:
+* defines strncat_s() - appends n characters of string onto
+* end of other string
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME strncat_s
+#define _CHAR char
+#define _DEST _Dst
+#define _SIZE _SizeInBytes
+#define _SRC _Src
+#define _COUNT _Count
+
+#include "tcsncat_s.inl"
diff --git a/src/pal/src/safecrt/strncpy_s.c b/src/pal/src/safecrt/strncpy_s.c
new file mode 100644
index 0000000000..f819ebb6bb
--- /dev/null
+++ b/src/pal/src/safecrt/strncpy_s.c
@@ -0,0 +1,30 @@
+// 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.
+
+/***
+*strncpy_s.c - copy at most n characters of string
+*
+
+*
+*Purpose:
+* defines strncpy_s() - copy at most n characters of string
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME strncpy_s
+#define _CHAR char
+#define _DEST _Dst
+#define _SIZE _SizeInBytes
+#define _SRC _Src
+#define _COUNT _Count
+
+#include "tcsncpy_s.inl"
diff --git a/src/pal/src/safecrt/strtok_s.c b/src/pal/src/safecrt/strtok_s.c
new file mode 100644
index 0000000000..6f1c80633f
--- /dev/null
+++ b/src/pal/src/safecrt/strtok_s.c
@@ -0,0 +1,27 @@
+// 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.
+
+/***
+*strtok_s.c - tokenize a string with given delimiters
+*
+
+*
+*Purpose:
+* defines strtok_s() - breaks string into series of token
+* via repeated calls.
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME strtok_s
+#define _CHAR char
+
+#include "tcstok_s.inl"
diff --git a/src/pal/src/safecrt/swprintf.c b/src/pal/src/safecrt/swprintf.c
new file mode 100644
index 0000000000..75004eafe2
--- /dev/null
+++ b/src/pal/src/safecrt/swprintf.c
@@ -0,0 +1,120 @@
+// 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.
+
+/***
+*swprintf.c - print formatted to string
+*
+*Purpose:
+* defines _swprintf(), _swprintf_c and _snwprintf() - print formatted data
+* to string
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+/***
+*ifndef _COUNT_
+*int _swprintf(string, format, ...) - print formatted data to string
+*else
+*ifndef _SWPRINTFS_ERROR_RETURN_FIX
+*int _snwprintf(string, cnt, format, ...) - print formatted data to string
+*else
+*int _swprintf_c(string, cnt, format, ...) - print formatted data to string
+*endif
+*endif
+*
+*Purpose:
+* Prints formatted data to the using the format string to
+* format data and getting as many arguments as called for
+* Sets up a FILE so file i/o operations can be used, make
+* string look like a huge buffer to it, but _flsbuf will
+* refuse to flush it if it fills up. Appends '\0' to make
+* it a true string. _output does the real work here
+*
+* Allocate the 'fake' _iob[] entry statically instead of on
+* the stack so that other routines can assume that _iob[]
+* entries are in are in DGROUP and, thus, are near.
+*
+* We alias swprintf to _swprintf
+*
+*ifdef _COUNT_
+*ifndef _SWPRINTFS_ERROR_RETURN_FIX
+* The _snwprintf() flavor takes a count argument that is
+* the max number of wide characters that should be written to the
+* user's buffer.
+* We don't expose this function directly in the headers.
+*else
+* The _swprintf_c() flavor does the same thing as the _snwprintf
+* above, but, it also fixes a issue in the return value in the case
+* when there isn't enough space to write the null terminator
+* We don't fix this issue in _snwprintf because of backward
+* compatibility. In new code, however, _snwprintf is #defined to
+* _swprintf_c so users get the fix.
+*
+*endif
+*
+* Multi-thread: (1) Since there is no stream, this routine must
+* never try to get the stream lock (i.e., there is no stream
+* lock either). (2) Also, since there is only one statically
+* allocated 'fake' iob, we must lock/unlock to prevent collisions.
+*
+*Entry:
+* wchar_t *string - pointer to place to put output
+*ifdef _COUNT_
+* size_t count - max number of wide characters to put in buffer
+*endif
+* wchar_t *format - format string to control data format/number
+* of arguments followed by list of arguments, number and type
+* controlled by format string
+*
+*Exit:
+* returns number of wide characters printed
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+int __cdecl swprintf_s (
+ wchar_t *string,
+ size_t sizeInWords,
+ const wchar_t *format,
+ ...
+ )
+{
+ int ret;
+ va_list arglist;
+
+ va_start(arglist, format);
+
+ ret = _vswprintf_s(string, sizeInWords, format, arglist);
+
+ va_end(arglist);
+
+ return ret;
+}
+
+int __cdecl _snwprintf_s (
+ wchar_t *string,
+ size_t sizeInWords,
+ size_t count,
+ const wchar_t *format,
+ ...
+ )
+{
+ int ret;
+ va_list arglist;
+
+ va_start(arglist, format);
+
+ ret = _vsnwprintf_s(string, sizeInWords, count, format, arglist);
+
+ va_end(arglist);
+
+ return ret;
+}
diff --git a/src/pal/src/safecrt/tcscat_s.inl b/src/pal/src/safecrt/tcscat_s.inl
new file mode 100644
index 0000000000..b6fefdc0ae
--- /dev/null
+++ b/src/pal/src/safecrt/tcscat_s.inl
@@ -0,0 +1,51 @@
+// 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.
+
+/***
+*tcscat_s.inl - general implementation of _tcscpy_s
+*
+
+*
+*Purpose:
+* This file contains the general algorithm for strcat_s and its variants.
+*
+****/
+
+_FUNC_PROLOGUE
+errno_t __cdecl _FUNC_NAME(_CHAR *_DEST, size_t _SIZE, const _CHAR *_SRC)
+{
+ _CHAR *p;
+ size_t available;
+
+ /* validation section */
+ _VALIDATE_STRING(_DEST, _SIZE);
+ _VALIDATE_POINTER_RESET_STRING(_SRC, _DEST, _SIZE);
+
+ p = _DEST;
+ available = _SIZE;
+ while (available > 0 && *p != 0)
+ {
+ p++;
+ available--;
+ }
+
+ if (available == 0)
+ {
+ _RESET_STRING(_DEST, _SIZE);
+ _RETURN_DEST_NOT_NULL_TERMINATED(_DEST, _SIZE);
+ }
+
+ while ((*p++ = *_SRC++) != 0 && --available > 0)
+ {
+ }
+
+ if (available == 0)
+ {
+ _RESET_STRING(_DEST, _SIZE);
+ _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE);
+ }
+ _FILL_STRING(_DEST, _SIZE, _SIZE - available + 1);
+ _RETURN_NO_ERROR;
+}
+
diff --git a/src/pal/src/safecrt/tcscpy_s.inl b/src/pal/src/safecrt/tcscpy_s.inl
new file mode 100644
index 0000000000..b1192d8ee7
--- /dev/null
+++ b/src/pal/src/safecrt/tcscpy_s.inl
@@ -0,0 +1,39 @@
+// 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.
+
+/***
+*tcscpy_s.inl - general implementation of _tcscpy_s
+*
+
+*
+*Purpose:
+* This file contains the general algorithm for strcpy_s and its variants.
+*
+****/
+
+_FUNC_PROLOGUE
+errno_t __cdecl _FUNC_NAME(_CHAR *_DEST, size_t _SIZE, const _CHAR *_SRC)
+{
+ _CHAR *p;
+ size_t available;
+
+ /* validation section */
+ _VALIDATE_STRING(_DEST, _SIZE);
+ _VALIDATE_POINTER_RESET_STRING(_SRC, _DEST, _SIZE);
+
+ p = _DEST;
+ available = _SIZE;
+ while ((*p++ = *_SRC++) != 0 && --available > 0)
+ {
+ }
+
+ if (available == 0)
+ {
+ _RESET_STRING(_DEST, _SIZE);
+ _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE);
+ }
+ _FILL_STRING(_DEST, _SIZE, _SIZE - available + 1);
+ _RETURN_NO_ERROR;
+}
+
diff --git a/src/pal/src/safecrt/tcsncat_s.inl b/src/pal/src/safecrt/tcsncat_s.inl
new file mode 100644
index 0000000000..6de501767d
--- /dev/null
+++ b/src/pal/src/safecrt/tcsncat_s.inl
@@ -0,0 +1,81 @@
+// 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.
+
+/***
+*tcsncat_s.inl - general implementation of _tcscpy_s
+*
+
+*
+*Purpose:
+* This file contains the general algorithm for strncat_s and its variants.
+*
+****/
+
+_FUNC_PROLOGUE
+errno_t __cdecl _FUNC_NAME(_CHAR *_DEST, size_t _SIZE, const _CHAR *_SRC, size_t _COUNT)
+{
+ _CHAR *p;
+ size_t available;
+
+ if (_COUNT == 0 && _DEST == NULL && _SIZE == 0)
+ {
+ /* this case is allowed; nothing to do */
+ _RETURN_NO_ERROR;
+ }
+
+ /* validation section */
+ _VALIDATE_STRING(_DEST, _SIZE);
+ if (_COUNT != 0)
+ {
+ _VALIDATE_POINTER_RESET_STRING(_SRC, _DEST, _SIZE);
+ }
+
+ p = _DEST;
+ available = _SIZE;
+ while (available > 0 && *p != 0)
+ {
+ p++;
+ available--;
+ }
+
+ if (available == 0)
+ {
+ _RESET_STRING(_DEST, _SIZE);
+ _RETURN_DEST_NOT_NULL_TERMINATED(_DEST, _SIZE);
+ }
+
+ if (_COUNT == _TRUNCATE)
+ {
+ while ((*p++ = *_SRC++) != 0 && --available > 0)
+ {
+ }
+ }
+ else
+ {
+ _ASSERT_EXPR((!_CrtGetCheckCount() || _COUNT < available), "Buffer is too small");
+
+ while (_COUNT > 0 && (*p++ = *_SRC++) != 0 && --available > 0)
+ {
+ _COUNT--;
+ }
+ if (_COUNT == 0)
+ {
+ *p = 0;
+ }
+ }
+
+ if (available == 0)
+ {
+ if (_COUNT == _TRUNCATE)
+ {
+ _DEST[_SIZE - 1] = 0;
+ _RETURN_TRUNCATE;
+ }
+ _RESET_STRING(_DEST, _SIZE);
+ _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE);
+ }
+ _FILL_STRING(_DEST, _SIZE, _SIZE - available + 1);
+ _RETURN_NO_ERROR;
+}
+
diff --git a/src/pal/src/safecrt/tcsncpy_s.inl b/src/pal/src/safecrt/tcsncpy_s.inl
new file mode 100644
index 0000000000..54a79a03e7
--- /dev/null
+++ b/src/pal/src/safecrt/tcsncpy_s.inl
@@ -0,0 +1,71 @@
+// 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.
+
+/***
+*tcsncpy_s.inl - general implementation of _tcsncpy_s
+*
+
+*
+*Purpose:
+* This file contains the general algorithm for strncpy_s and its variants.
+*
+****/
+
+_FUNC_PROLOGUE
+errno_t __cdecl _FUNC_NAME(_CHAR *_DEST, size_t _SIZE, const _CHAR *_SRC, size_t _COUNT)
+{
+ _CHAR *p;
+ size_t available;
+
+ if (_COUNT == 0 && _DEST == NULL && _SIZE == 0)
+ {
+ /* this case is allowed; nothing to do */
+ _RETURN_NO_ERROR;
+ }
+
+ /* validation section */
+ _VALIDATE_STRING(_DEST, _SIZE);
+ if (_COUNT == 0)
+ {
+ /* notice that the source string pointer can be NULL in this case */
+ _RESET_STRING(_DEST, _SIZE);
+ _RETURN_NO_ERROR;
+ }
+ _VALIDATE_POINTER_RESET_STRING(_SRC, _DEST, _SIZE);
+
+ p = _DEST;
+ available = _SIZE;
+ if (_COUNT == _TRUNCATE)
+ {
+ while ((*p++ = *_SRC++) != 0 && --available > 0)
+ {
+ }
+ }
+ else
+ {
+ _ASSERT_EXPR((!_CrtGetCheckCount() || _COUNT < _SIZE), "Buffer is too small");
+
+ while ((*p++ = *_SRC++) != 0 && --available > 0 && --_COUNT > 0)
+ {
+ }
+ if (_COUNT == 0)
+ {
+ *p = 0;
+ }
+ }
+
+ if (available == 0)
+ {
+ if (_COUNT == _TRUNCATE)
+ {
+ _DEST[_SIZE - 1] = 0;
+ _RETURN_TRUNCATE;
+ }
+ _RESET_STRING(_DEST, _SIZE);
+ _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE);
+ }
+ _FILL_STRING(_DEST, _SIZE, _SIZE - available + 1);
+ _RETURN_NO_ERROR;
+}
+
diff --git a/src/pal/src/safecrt/tcstok_s.inl b/src/pal/src/safecrt/tcstok_s.inl
new file mode 100644
index 0000000000..29ca5c6858
--- /dev/null
+++ b/src/pal/src/safecrt/tcstok_s.inl
@@ -0,0 +1,71 @@
+// 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.
+
+/***
+*tcstok_s.inl - general implementation of _tcstok_s
+*
+
+*
+*Purpose:
+* This file contains the general algorithm for strtok_s and its variants.
+*
+****/
+
+_FUNC_PROLOGUE
+_CHAR * __cdecl _FUNC_NAME(_CHAR *_String, const _CHAR *_Control, _CHAR **_Context)
+{
+ _CHAR *token;
+ const _CHAR *ctl;
+
+ /* validation section */
+ _VALIDATE_POINTER_ERROR_RETURN(_Context, EINVAL, NULL);
+ _VALIDATE_POINTER_ERROR_RETURN(_Control, EINVAL, NULL);
+ _VALIDATE_CONDITION_ERROR_RETURN(_String != NULL || *_Context != NULL, EINVAL, NULL);
+
+ /* If string==NULL, continue with previous string */
+ if (!_String)
+ {
+ _String = *_Context;
+ }
+
+ /* Find beginning of token (skip over leading delimiters). Note that
+ * there is no token iff this loop sets string to point to the terminal null. */
+ for ( ; *_String != 0 ; _String++)
+ {
+ for (ctl = _Control; *ctl != 0 && *ctl != *_String; ctl++)
+ ;
+ if (*ctl == 0)
+ {
+ break;
+ }
+ }
+
+ token = _String;
+
+ /* Find the end of the token. If it is not the end of the string,
+ * put a null there. */
+ for ( ; *_String != 0 ; _String++)
+ {
+ for (ctl = _Control; *ctl != 0 && *ctl != *_String; ctl++)
+ ;
+ if (*ctl != 0)
+ {
+ *_String++ = 0;
+ break;
+ }
+ }
+
+ /* Update the context */
+ *_Context = _String;
+
+ /* Determine if a token has been found. */
+ if (token == _String)
+ {
+ return NULL;
+ }
+ else
+ {
+ return token;
+ }
+}
diff --git a/src/pal/src/safecrt/tmakepath_s.inl b/src/pal/src/safecrt/tmakepath_s.inl
new file mode 100644
index 0000000000..34c4842c61
--- /dev/null
+++ b/src/pal/src/safecrt/tmakepath_s.inl
@@ -0,0 +1,116 @@
+// 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.
+
+/***
+*tmakepath_s.inl - general implementation of _tmakepath_s
+*
+
+*
+*Purpose:
+* This file contains the general algorithm for _makepath_s and its variants.
+*
+*******************************************************************************/
+
+_FUNC_PROLOGUE
+errno_t __cdecl _FUNC_NAME(__out_ecount_z(_SIZE) _CHAR *_DEST, __in_opt size_t _SIZE, __in_z_opt const _CHAR *_Drive, __in_z_opt const _CHAR *_Dir, __in_z_opt const _CHAR *_Filename, __in_z_opt const _CHAR *_Ext)
+{
+ size_t written;
+ const _CHAR *p;
+ _CHAR *d;
+
+ /* validation section */
+ _VALIDATE_STRING(_DEST, _SIZE);
+
+ /* copy drive */
+ written = 0;
+ d = _DEST;
+ if (_Drive != NULL && *_Drive != 0)
+ {
+ written += 2;
+ if(written >= _SIZE)
+ {
+ goto error_return;
+ }
+ *d++ = *_Drive;
+ *d++ = _T(':');
+ }
+
+ /* copy dir */
+ p = _Dir;
+ if (p != NULL && *p != 0)
+ {
+ do {
+ if(++written >= _SIZE)
+ {
+ goto error_return;
+ }
+ *d++ = *p++;
+ } while (*p != 0);
+
+#if _MBS_SUPPORT
+ p = _MBSDEC(_Dir, p);
+#else /* _MBS_SUPPORT */
+ p = p - 1;
+#endif /* _MBS_SUPPORT */
+ if (*p != _T('/') && *p != _T('\\'))
+ {
+ if(++written >= _SIZE)
+ {
+ goto error_return;
+ }
+ *d++ = _T('\\');
+ }
+ }
+
+ /* copy fname */
+ p = _Filename;
+ if (p != NULL)
+ {
+ while (*p != 0)
+ {
+ if(++written >= _SIZE)
+ {
+ goto error_return;
+ }
+ *d++ = *p++;
+ }
+ }
+
+ /* copy extension; check to see if a '.' needs to be inserted */
+ p = _Ext;
+ if (p != NULL)
+ {
+ if (*p != 0 && *p != _T('.'))
+ {
+ if(++written >= _SIZE)
+ {
+ goto error_return;
+ }
+ *d++ = _T('.');
+ }
+ while (*p != 0)
+ {
+ if(++written >= _SIZE)
+ {
+ goto error_return;
+ }
+ *d++ = *p++;
+ }
+ }
+
+ if(++written > _SIZE)
+ {
+ goto error_return;
+ }
+ *d = 0;
+ _FILL_STRING(_DEST, _SIZE, written);
+ _RETURN_NO_ERROR;
+
+error_return:
+ _RESET_STRING(_DEST, _SIZE);
+ _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE);
+
+ /* should never happen, but compiler can't tell */
+ return EINVAL;
+}
diff --git a/src/pal/src/safecrt/tsplitpath_s.inl b/src/pal/src/safecrt/tsplitpath_s.inl
new file mode 100644
index 0000000000..3a14fa5900
--- /dev/null
+++ b/src/pal/src/safecrt/tsplitpath_s.inl
@@ -0,0 +1,280 @@
+// 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.
+
+/***
+*tsplitpath_s.inl - general implementation of _tsplitpath_s
+*
+
+*
+*Purpose:
+* This file contains the general algorithm for _splitpath_s and its variants.
+*
+*******************************************************************************/
+
+_FUNC_PROLOGUE
+errno_t __cdecl _FUNC_NAME(
+ __in_z const _CHAR *_Path,
+ __out_ecount_z_opt(_DriveSize) _CHAR *_Drive, __in size_t _DriveSize,
+ __out_ecount_z_opt(_DirSize) _CHAR *_Dir, __in size_t _DirSize,
+ __out_ecount_z_opt(_FilenameSize) _CHAR *_Filename, __in size_t _FilenameSize,
+ __out_ecount_z_opt(_ExtSize) _CHAR *_Ext, __in size_t _ExtSize
+)
+{
+ const _CHAR *tmp;
+ const _CHAR *last_slash;
+ const _CHAR *dot;
+ int drive_set = 0;
+ size_t length = 0;
+ int bEinval = 0;
+
+ /* validation section */
+ if (_Path == NULL)
+ {
+ goto error_einval;
+ }
+ if ((_Drive == NULL && _DriveSize != 0) || (_Drive != NULL && _DriveSize == 0))
+ {
+ goto error_einval;
+ }
+ if ((_Dir == NULL && _DirSize != 0) || (_Dir != NULL && _DirSize == 0))
+ {
+ goto error_einval;
+ }
+ if ((_Filename == NULL && _FilenameSize != 0) || (_Filename != NULL && _FilenameSize == 0))
+ {
+ goto error_einval;
+ }
+ if ((_Ext == NULL && _ExtSize != 0) || (_Ext != NULL && _ExtSize == 0))
+ {
+ goto error_einval;
+ }
+
+ /* check if _Path begins with the longpath prefix */
+ if (_Path[0] == _T('\\') && _Path[1] == _T('\\') && _Path[2] == _T('?') && _Path[3] == _T('\\'))
+ {
+ _Path += 4;
+ }
+
+ /* extract drive letter and ':', if any */
+ if (!drive_set)
+ {
+// The CorUnix PAL is never built on Windows and thus, the code below
+// for the drive check is not required.
+#if 0
+ size_t skip = _MAX_DRIVE - 2;
+ tmp = _Path;
+ while (skip > 0 && *tmp != 0)
+ {
+ skip--;
+ tmp++;
+ }
+ if (*tmp == _T(':'))
+ {
+ if (_Drive != NULL)
+ {
+ if (_DriveSize < _MAX_DRIVE)
+ {
+ goto error_erange;
+ }
+ _TCSNCPY_S(_Drive, _DriveSize, _Path, _MAX_DRIVE - 1);
+ }
+ _Path = tmp + 1;
+ }
+ else
+#endif
+ {
+ if (_Drive != NULL)
+ {
+ _RESET_STRING(_Drive, _DriveSize);
+ }
+ }
+ }
+
+ /* extract path string, if any. _Path now points to the first character
+ * of the path, if any, or the filename or extension, if no path was
+ * specified. Scan ahead for the last occurence, if any, of a '/' or
+ * '\' path separator character. If none is found, there is no path.
+ * We will also note the last '.' character found, if any, to aid in
+ * handling the extension.
+ */
+ last_slash = NULL;
+ dot = NULL;
+ tmp = _Path;
+ for (; *tmp != 0; ++tmp)
+ {
+#if _MBS_SUPPORT
+#pragma warning(push)
+#pragma warning(disable:4127)
+ if (_ISMBBLEAD(*tmp))
+#pragma warning(pop)
+ {
+ tmp++;
+ }
+ else
+#endif /* _MBS_SUPPORT */
+ {
+ if (*tmp == _T('/') || *tmp == _T('\\'))
+ {
+ /* point to one beyond for later copy */
+ last_slash = tmp + 1;
+ }
+ else if (*tmp == _T('.'))
+ {
+ dot = tmp;
+ }
+ }
+ }
+
+ if (last_slash != NULL)
+ {
+ /* found a path - copy up through last_slash or max characters
+ * allowed, whichever is smaller
+ */
+ if (_Dir != NULL) {
+ length = (size_t)(last_slash - _Path);
+ if (_DirSize <= length)
+ {
+ goto error_erange;
+ }
+ _TCSNCPY_S(_Dir, _DirSize, _Path, length);
+
+ // Normalize the path seperator
+ int iIndex;
+ for(iIndex = 0; iIndex < length; iIndex++)
+ {
+ if (_Dir[iIndex] == _T('\\'))
+ {
+ _Dir[iIndex] = _T('/');
+ }
+ }
+ }
+ _Path = last_slash;
+ }
+ else
+ {
+ /* there is no path */
+ if (_Dir != NULL)
+ {
+ _RESET_STRING(_Dir, _DirSize);
+ }
+ }
+
+ /* extract file name and extension, if any. Path now points to the
+ * first character of the file name, if any, or the extension if no
+ * file name was given. Dot points to the '.' beginning the extension,
+ * if any.
+ */
+ if (dot != NULL && (dot >= _Path))
+ {
+ /* found the marker for an extension - copy the file name up to the '.' */
+ if (_Filename)
+ {
+ length = (size_t)(dot - _Path);
+ if (length == 0)
+ {
+ // At this time, dot will be equal to _Path if string is something like "/."
+ // since _path was set to last_slash, which in turn, was set to "tmp +1"
+ // where "tmp" is the location where "/" was found. See code above for
+ // clarification.
+ //
+ // For such cases, return the "." in filename buffer.
+ //
+ // Thus, if the length is zero, we know its a string like "/." and thus, we
+ // set length to 1 to get the "." in filename buffer.
+ length = 1;
+ }
+
+ if (_FilenameSize <= length)
+ {
+ goto error_erange;
+ }
+ _TCSNCPY_S(_Filename, _FilenameSize, _Path, length);
+ }
+
+ /* now we can get the extension - remember that tmp still points
+ * to the terminating NULL character of path.
+ */
+ if (_Ext)
+ {
+ // At this time, _Path is pointing to the character after the last slash found.
+ // (See comments and code above for clarification).
+ //
+ // Returns extension as empty string for strings like "/.".
+ if (dot > _Path)
+ {
+ length = (size_t)(tmp - dot);
+ if (_ExtSize <= length)
+ {
+ goto error_erange;
+ }
+
+ /* Since dot pointed to the ".", make sure we actually have an extension
+ like ".cmd" and not just ".", OR
+
+ Confirm that its a string like "/.." - for this, return the
+ second "." in the extension part.
+
+ However, for strings like "/myfile.", return empty string
+ in extension buffer.
+ */
+ int fIsDir = (*(dot-1) == _T('.'))?1:0;
+ if (length > 1 || (length == 1 && fIsDir == 1))
+ _TCSNCPY_S(_Ext, _ExtSize, dot, length);
+ else
+ _RESET_STRING(_Ext, _ExtSize);
+ }
+ else
+ _RESET_STRING(_Ext, _ExtSize);
+ }
+ }
+ else
+ {
+ /* found no extension, give empty extension and copy rest of
+ * string into fname.
+ */
+ if (_Filename)
+ {
+ length = (size_t)(tmp - _Path);
+ if (_FilenameSize <= length)
+ {
+ goto error_erange;
+ }
+ _TCSNCPY_S(_Filename, _FilenameSize, _Path, length);
+ }
+ if (_Ext)
+ {
+ _RESET_STRING(_Ext, _ExtSize);
+ }
+ }
+
+ _RETURN_NO_ERROR;
+
+error_einval:
+ bEinval = 1;
+
+error_erange:
+ if (_Drive != NULL && _DriveSize > 0)
+ {
+ _RESET_STRING(_Drive, _DriveSize);
+ }
+ if (_Dir != NULL && _DirSize > 0)
+ {
+ _RESET_STRING(_Dir, _DirSize);
+ }
+ if (_Filename != NULL && _FilenameSize > 0)
+ {
+ _RESET_STRING(_Filename, _FilenameSize);
+ }
+ if (_Ext != NULL && _ExtSize > 0)
+ {
+ _RESET_STRING(_Ext, _ExtSize);
+ }
+
+ _VALIDATE_POINTER(_Path);
+ if (bEinval)
+ {
+ _RETURN_EINVAL;
+ }
+ return (errno = ERANGE);
+}
diff --git a/src/pal/src/safecrt/vsprintf.c b/src/pal/src/safecrt/vsprintf.c
new file mode 100644
index 0000000000..4f2bd9fdeb
--- /dev/null
+++ b/src/pal/src/safecrt/vsprintf.c
@@ -0,0 +1,268 @@
+// 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.
+
+/***
+*vsprintf.c - print formatted data into a string from var arg list
+*
+
+*
+*Purpose:
+* defines vsprintf(), _vsnprintf() and _vsnprintf_s() - print formatted output to
+* a string, get the data from an argument ptr instead of explicit
+* arguments.
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+typedef int (*OUTPUTFN)(miniFILE *, const char *, va_list);
+
+static int _vsnprintf_helper( OUTPUTFN outfn, char *string, size_t count, const char *format, va_list ap );
+static int _vscprintf_helper ( OUTPUTFN outfn, const char *format, va_list ap);
+
+/***
+*ifndef _COUNT_
+*int vsprintf(string, format, ap) - print formatted data to string from arg ptr
+*else
+*int _vsnprintf(string, cnt, format, ap) - print formatted data to string from arg ptr
+*endif
+*
+*Purpose:
+* Prints formatted data, but to a string and gets data from an argument
+* pointer.
+* Sets up a FILE so file i/o operations can be used, make string look
+* like a huge buffer to it, but _flsbuf will refuse to flush it if it
+* fills up. Appends '\0' to make it a true string.
+*
+* Allocate the 'fake' _iob[] entryit statically instead of on
+* the stack so that other routines can assume that _iob[] entries are in
+* are in DGROUP and, thus, are near.
+*
+*ifdef _COUNT_
+* The _vsnprintf() flavor takes a count argument that is
+* the max number of bytes that should be written to the
+* user's buffer.
+*endif
+*
+* Multi-thread: (1) Since there is no stream, this routine must never try
+* to get the stream lock (i.e., there is no stream lock either). (2)
+* Also, since there is only one staticly allocated 'fake' iob, we must
+* lock/unlock to prevent collisions.
+*
+*Entry:
+* char *string - place to put destination string
+*ifdef _COUNT_
+* size_t count - max number of bytes to put in buffer
+*endif
+* char *format - format string, describes format of data
+* va_list ap - varargs argument pointer
+*
+*Exit:
+* returns number of characters in string
+* returns -2 if the string has been truncated (only in _vsnprintf_helper)
+* returns -1 in other error cases
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+int __cdecl _vsnprintf_helper (
+ OUTPUTFN outfn,
+ char *string,
+ size_t count,
+ const char *format,
+ va_list ap
+ )
+{
+ miniFILE str;
+ miniFILE *outfile = &str;
+ int retval;
+
+ _VALIDATE_RETURN( (format != NULL), EINVAL, -1);
+
+ _VALIDATE_RETURN( (count == 0) || (string != NULL), EINVAL, -1 );
+
+ if(count>INT_MAX)
+ {
+ /* old-style functions allow any large value to mean unbounded */
+ outfile->_cnt = INT_MAX;
+ }
+ else
+ {
+ outfile->_cnt = (int)count;
+ }
+
+ outfile->_flag = _IOWRT|_IOSTRG;
+ outfile->_ptr = outfile->_base = string;
+
+ retval = outfn(outfile, format, ap );
+
+ if ( string==NULL)
+ return(retval);
+
+ if((retval >= 0) && (_putc_nolock('\0',outfile) != EOF))
+ return(retval);
+
+ string[count - 1] = 0;
+
+ if (outfile->_cnt < 0)
+ {
+ /* the buffer was too small; we return -2 to indicate truncation */
+ return -2;
+ }
+ return -1;
+}
+
+int __cdecl _vsprintf_s (
+ char *string,
+ size_t sizeInBytes,
+ const char *format,
+ va_list ap
+ )
+{
+ int retvalue = -1;
+
+ /* validation section */
+ _VALIDATE_RETURN(format != NULL, EINVAL, -1);
+ _VALIDATE_RETURN(string != NULL && sizeInBytes > 0, EINVAL, -1);
+
+ retvalue = _vsnprintf_helper(_output_s, string, sizeInBytes, format, ap);
+ if (retvalue < 0)
+ {
+ string[0] = 0;
+ _SECURECRT__FILL_STRING(string, sizeInBytes, 1);
+ }
+ if (retvalue == -2)
+ {
+ _VALIDATE_RETURN(("Buffer too small" && 0), ERANGE, -1);
+ }
+ if (retvalue >= 0)
+ {
+ _SECURECRT__FILL_STRING(string, sizeInBytes, retvalue + 1);
+ }
+
+ return retvalue;
+}
+
+int __cdecl _vsnprintf_s (
+ char *string,
+ size_t sizeInBytes,
+ size_t count,
+ const char *format,
+ va_list ap
+ )
+{
+ int retvalue = -1;
+ errno_t save_errno = 0;
+
+ /* validation section */
+ _VALIDATE_RETURN(format != NULL, EINVAL, -1);
+ if (count == 0 && string == NULL && sizeInBytes == 0)
+ {
+ /* this case is allowed; nothing to do */
+ return 0;
+ }
+ _VALIDATE_RETURN(string != NULL && sizeInBytes > 0, EINVAL, -1);
+
+ if (sizeInBytes > count)
+ {
+ save_errno = errno;
+ retvalue = _vsnprintf_helper(_output_s, string, count + 1, format, ap);
+ if (retvalue == -2)
+ {
+ /* the string has been truncated, return -1 */
+ _SECURECRT__FILL_STRING(string, sizeInBytes, count + 1);
+ if (errno == ERANGE)
+ {
+ errno = save_errno;
+ }
+ return -1;
+ }
+ }
+ else /* sizeInBytes <= count */
+ {
+ save_errno = errno;
+ retvalue = _vsnprintf_helper(_output_s, string, sizeInBytes, format, ap);
+ string[sizeInBytes - 1] = 0;
+ /* we allow truncation if count == _TRUNCATE */
+ if (retvalue == -2 && count == _TRUNCATE)
+ {
+ if (errno == ERANGE)
+ {
+ errno = save_errno;
+ }
+ return -1;
+ }
+ }
+
+ if (retvalue < 0)
+ {
+ string[0] = 0;
+ _SECURECRT__FILL_STRING(string, sizeInBytes, 1);
+ if (retvalue == -2)
+ {
+ _VALIDATE_RETURN(("Buffer too small" && 0), ERANGE, -1);
+ }
+ return -1;
+ }
+
+ _SECURECRT__FILL_STRING(string, sizeInBytes, retvalue + 1);
+
+ return (retvalue < 0 ? -1 : retvalue);
+}
+
+/***
+* _vscprintf() - counts the number of character needed to print the formatted
+* data
+*
+*Purpose:
+* Counts the number of characters in the fotmatted data.
+*
+*Entry:
+* char *format - format string, describes format of data
+* va_list ap - varargs argument pointer
+*
+*Exit:
+* returns number of characters needed to print formatted data.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+#ifndef _COUNT_
+
+int __cdecl _vscprintf_helper (
+ OUTPUTFN outfn,
+ const char *format,
+ va_list ap
+ )
+{
+ miniFILE str;
+ miniFILE *outfile = &str;
+ int retval;
+
+ _VALIDATE_RETURN( (format != NULL), EINVAL, -1);
+
+ outfile->_cnt = INT_MAX; //MAXSTR;
+ outfile->_flag = _IOWRT|_IOSTRG;
+ outfile->_ptr = outfile->_base = NULL;
+
+ retval = outfn(outfile, format, ap);
+ return(retval);
+}
+
+int __cdecl _vscprintf (
+ const char *format,
+ va_list ap
+ )
+{
+ return _vscprintf_helper(_output, format, ap);
+}
+
+#endif /* _COUNT_ */
diff --git a/src/pal/src/safecrt/vswprint.c b/src/pal/src/safecrt/vswprint.c
new file mode 100644
index 0000000000..77c79b8752
--- /dev/null
+++ b/src/pal/src/safecrt/vswprint.c
@@ -0,0 +1,282 @@
+// 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.
+
+/***
+*vswprint.c - print formatted data into a string from var arg list
+*
+*Purpose:
+* defines vswprintf(), _vswprintf_c and _vsnwprintf() - print formatted output to
+* a string, get the data from an argument ptr instead of explicit
+* arguments.
+*
+*******************************************************************************/
+
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+typedef int (*WOUTPUTFN)(miniFILE *, const wchar_t *, va_list);
+
+static int _vswprintf_helper( WOUTPUTFN outfn, wchar_t *string, size_t count, const wchar_t *format, va_list ap );
+static int _vscwprintf_helper (WOUTPUTFN outfn, const wchar_t *format, va_list ap );
+
+/***
+*ifndef _COUNT_
+*int _vswprintf(string, format, ap) - print formatted data to string from arg ptr
+*else
+*ifndef _SWPRINTFS_ERROR_RETURN_FIX
+*int _vsnwprintf(string, cnt, format, ap) - print formatted data to string from arg ptr
+*else
+*int _vswprintf_c(string, cnt, format, ...) - print formatted data to string
+*endif
+*endif
+*
+*Purpose:
+* Prints formatted data, but to a string and gets data from an argument
+* pointer.
+* Sets up a FILE so file i/o operations can be used, make string look
+* like a huge buffer to it, but _flsbuf will refuse to flush it if it
+* fills up. Appends '\0' to make it a true string.
+*
+* Allocate the 'fake' _iob[] entryit statically instead of on
+* the stack so that other routines can assume that _iob[] entries are in
+* are in DGROUP and, thus, are near.
+*
+*ifdef _COUNT_
+*ifndef _SWPRINTFS_ERROR_RETURN_FIX
+* The _vsnwprintf() flavor takes a count argument that is
+* the max number of bytes that should be written to the
+* user's buffer.
+* We don't expose this function directly in the headers.
+*else
+* The _vswprintf_c() flavor does the same thing as the _snwprintf
+* above, but, it also fixes an issue in the return value in the case
+* when there isn't enough space to write the null terminator
+* We don't fix this issue in _vsnwprintf because of backward
+* compatibility. In new code, however, _vsnwprintf is #defined to
+* _vswprintf_c so users get the fix.
+*
+*endif
+*
+* Multi-thread: (1) Since there is no stream, this routine must never try
+* to get the stream lock (i.e., there is no stream lock either). (2)
+* Also, since there is only one statically allocated 'fake' iob, we must
+* lock/unlock to prevent collisions.
+*
+*Entry:
+* wchar_t *string - place to put destination string
+*ifdef _COUNT_
+* size_t count - max number of bytes to put in buffer
+*endif
+* wchar_t *format - format string, describes format of data
+* va_list ap - varargs argument pointer
+*
+*Exit:
+* returns number of wide characters in string
+* returns -2 if the string has been truncated (only in _vsnprintf_helper)
+* returns -1 in other error cases
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+int __cdecl _vswprintf_helper (
+ WOUTPUTFN woutfn,
+ wchar_t *string,
+ size_t count,
+ const wchar_t *format,
+ va_list ap
+ )
+{
+ miniFILE str;
+ miniFILE *outfile = &str;
+ int retval;
+
+ _VALIDATE_RETURN( (format != NULL), EINVAL, -1);
+
+ _VALIDATE_RETURN( (count == 0) || (string != NULL), EINVAL, -1 );
+
+ outfile->_flag = _IOWRT|_IOSTRG;
+ outfile->_ptr = outfile->_base = (char *) string;
+
+ if(count>(INT_MAX/sizeof(wchar_t)))
+ {
+ /* old-style functions allow any large value to mean unbounded */
+ outfile->_cnt = INT_MAX;
+ }
+ else
+ {
+ outfile->_cnt = (int)(count*sizeof(wchar_t));
+ }
+
+ retval = woutfn(outfile, format, ap );
+
+ if(string==NULL)
+ {
+ return retval;
+ }
+
+ if((retval >= 0) && (_putc_nolock('\0',outfile) != EOF) && (_putc_nolock('\0',outfile) != EOF))
+ return(retval);
+
+ string[count - 1] = 0;
+ if (outfile->_cnt < 0)
+ {
+ /* the buffer was too small; we return -2 to indicate truncation */
+ return -2;
+ }
+ return -1;
+}
+
+int __cdecl _vswprintf_s (
+ wchar_t *string,
+ size_t sizeInWords,
+ const wchar_t *format,
+ va_list ap
+ )
+{
+ int retvalue = -1;
+
+ /* validation section */
+ _VALIDATE_RETURN(format != NULL, EINVAL, -1);
+ _VALIDATE_RETURN(string != NULL && sizeInWords > 0, EINVAL, -1);
+
+ retvalue = _vswprintf_helper(_woutput_s, string, sizeInWords, format, ap);
+ if (retvalue < 0)
+ {
+ string[0] = 0;
+ _SECURECRT__FILL_STRING(string, sizeInWords, 1);
+ }
+ if (retvalue == -2)
+ {
+ _VALIDATE_RETURN(("Buffer too small" && 0), ERANGE, -1);
+ }
+ if (retvalue >= 0)
+ {
+ _SECURECRT__FILL_STRING(string, sizeInWords, retvalue + 1);
+ }
+
+ return retvalue;
+}
+
+int __cdecl _vsnwprintf_s (
+ wchar_t *string,
+ size_t sizeInWords,
+ size_t count,
+ const wchar_t *format,
+ va_list ap
+ )
+{
+ int retvalue = -1;
+ errno_t save_errno = 0;
+
+ /* validation section */
+ _VALIDATE_RETURN(format != NULL, EINVAL, -1);
+ if (count == 0 && string == NULL && sizeInWords == 0)
+ {
+ /* this case is allowed; nothing to do */
+ return 0;
+ }
+ _VALIDATE_RETURN(string != NULL && sizeInWords > 0, EINVAL, -1);
+
+ if (sizeInWords > count)
+ {
+ save_errno = errno;
+ retvalue = _vswprintf_helper(_woutput_s, string, count + 1, format, ap);
+ if (retvalue == -2)
+ {
+ /* the string has been truncated, return -1 */
+ _SECURECRT__FILL_STRING(string, sizeInWords, count + 1);
+ if (errno == ERANGE)
+ {
+ errno = save_errno;
+ }
+ return -1;
+ }
+ }
+ else /* sizeInWords <= count */
+ {
+ save_errno = errno;
+ retvalue = _vswprintf_helper(_woutput_s, string, sizeInWords, format, ap);
+ string[sizeInWords - 1] = 0;
+ /* we allow truncation if count == _TRUNCATE */
+ if (retvalue == -2 && count == _TRUNCATE)
+ {
+ if (errno == ERANGE)
+ {
+ errno = save_errno;
+ }
+ return -1;
+ }
+ }
+
+ if (retvalue < 0)
+ {
+ string[0] = 0;
+ _SECURECRT__FILL_STRING(string, sizeInWords, 1);
+ if (retvalue == -2)
+ {
+ _VALIDATE_RETURN(("Buffer too small" && 0), ERANGE, -1);
+ }
+ return -1;
+ }
+
+ _SECURECRT__FILL_STRING(string, sizeInWords, retvalue + 1);
+
+ return (retvalue < 0 ? -1 : retvalue);
+}
+
+/***
+* _vscwprintf() - counts the number of character needed to print the formatted
+* data
+*
+*Purpose:
+* Counts the number of characters in the fotmatted data.
+*
+*Entry:
+* wchar_t *format - format string, describes format of data
+* va_list ap - varargs argument pointer
+*
+*Exit:
+* returns number of characters needed to print formatted data.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+#ifndef _COUNT_
+
+int __cdecl _vscwprintf_helper (
+ WOUTPUTFN woutfn,
+ const wchar_t *format,
+ va_list ap
+ )
+{
+ miniFILE str;
+ miniFILE *outfile = &str;
+ int retval;
+
+ _VALIDATE_RETURN( (format != NULL), EINVAL, -1);
+
+ outfile->_cnt = INT_MAX; //MAXSTR;
+ outfile->_flag = _IOWRT|_IOSTRG;
+ outfile->_ptr = outfile->_base = NULL;
+
+ retval = woutfn(outfile, format, ap);
+ return(retval);
+}
+
+int __cdecl _vscwprintf (
+ const wchar_t *format,
+ va_list ap
+ )
+{
+ return _vscwprintf_helper(_woutput_s, format, ap);
+}
+
+#endif /* _COUNT_ */
diff --git a/src/pal/src/safecrt/wcscat_s.c b/src/pal/src/safecrt/wcscat_s.c
new file mode 100644
index 0000000000..06179888ff
--- /dev/null
+++ b/src/pal/src/safecrt/wcscat_s.c
@@ -0,0 +1,36 @@
+// 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.
+
+/***
+*wcscat_s.c - contains wcscat_s()
+*
+
+*
+*Purpose:
+* wcscat_s() appends one wchar_t string onto another.
+*
+* wcscat() concatenates (appends) a copy of the source string to the
+* end of the destination string.
+* Strings are wide-character strings.
+*
+*******************************************************************************/
+
+#define _SECURECRT_FILL_BUFFER 1
+#define _SECURECRT_FILL_BUFFER_THRESHOLD ((size_t)8)
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME wcscat_s
+#define _CHAR wchar_t
+#define _DEST _Dst
+#define _SIZE _SizeInBytes
+#define _SRC _Src
+
+#include "tcscat_s.inl"
diff --git a/src/pal/src/safecrt/wcscpy_s.c b/src/pal/src/safecrt/wcscpy_s.c
new file mode 100644
index 0000000000..4c60a81489
--- /dev/null
+++ b/src/pal/src/safecrt/wcscpy_s.c
@@ -0,0 +1,33 @@
+// 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.
+
+/***
+*strcpy_s.c - contains wcscpy_s()
+*
+
+*
+*Purpose:
+* wcscpy_s() copies one string onto another.
+*
+*******************************************************************************/
+
+#define _SECURECRT_FILL_BUFFER 1
+#define _SECURECRT_FILL_BUFFER_THRESHOLD ((size_t)8)
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME wcscpy_s
+#define _CHAR wchar_t
+#define _DEST _Dst
+#define _SIZE _SizeInWords
+#define _SRC _Src
+
+#include "tcscpy_s.inl"
+
diff --git a/src/pal/src/safecrt/wcslen_s.c b/src/pal/src/safecrt/wcslen_s.c
new file mode 100644
index 0000000000..4fd5371035
--- /dev/null
+++ b/src/pal/src/safecrt/wcslen_s.c
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/***
+*wcslen_s.c - contains wcsnlen() routine
+*
+
+*
+*Purpose:
+* wcslen returns the length of a null-terminated wide-character string,
+* not including the null wchar_t itself.
+*
+*******************************************************************************/
+
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+/***
+*wcsnlen - return the length of a null-terminated wide-character string
+*
+*Purpose:
+* Finds the length in bytes of the given string, not including
+* the final null character. Only the first maxsize characters
+* are inspected: if the null character is not found, maxsize is
+* returned.
+*
+*Entry:
+* const wchar_t * wcs - string whose length is to be computed
+* size_t maxsize
+*
+*Exit:
+* Length of the string "wcs", exclusive of the final null byte, or
+* maxsize if the null character is not found.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+size_t __cdecl wcsnlen(const wchar_t *wcs, size_t maxsize)
+{
+ size_t n;
+
+ /* Note that we do not check if s == NULL, because we do not
+ * return errno_t...
+ */
+
+ for (n = 0; n < maxsize && *wcs; n++, wcs++)
+ ;
+
+ return n;
+}
+
diff --git a/src/pal/src/safecrt/wcsncat_s.c b/src/pal/src/safecrt/wcsncat_s.c
new file mode 100644
index 0000000000..1ff39d55f3
--- /dev/null
+++ b/src/pal/src/safecrt/wcsncat_s.c
@@ -0,0 +1,35 @@
+// 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.
+
+/***
+*wcsncat_s.c - append n chars of string to new string
+*
+
+*
+*Purpose:
+* defines wcsncat_s() - appends n characters of string onto
+* end of other string
+*
+*******************************************************************************/
+
+#define _SECURECRT_FILL_BUFFER 1
+#define _SECURECRT_FILL_BUFFER_THRESHOLD ((size_t)8)
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME wcsncat_s
+#define _CHAR wchar_t
+#define _DEST _Dst
+#define _SIZE _SizeInWords
+#define _SRC _Src
+#define _COUNT _Count
+
+#include "tcsncat_s.inl"
+
diff --git a/src/pal/src/safecrt/wcsncpy_s.c b/src/pal/src/safecrt/wcsncpy_s.c
new file mode 100644
index 0000000000..7902ded43a
--- /dev/null
+++ b/src/pal/src/safecrt/wcsncpy_s.c
@@ -0,0 +1,34 @@
+// 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.
+
+/***
+*wcsncpy_s.c - copy at most n characters of wide-character string
+*
+
+*
+*Purpose:
+* defines wcsncpy_s() - copy at most n characters of wchar_t string
+*
+*******************************************************************************/
+
+#define _SECURECRT_FILL_BUFFER 1
+#define _SECURECRT_FILL_BUFFER_THRESHOLD ((size_t)8)
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME wcsncpy_s
+#define _CHAR wchar_t
+#define _DEST _Dst
+#define _SIZE _SizeInWords
+#define _SRC _Src
+#define _COUNT _Count
+
+#include "tcsncpy_s.inl"
+
diff --git a/src/pal/src/safecrt/wcstok_s.c b/src/pal/src/safecrt/wcstok_s.c
new file mode 100644
index 0000000000..c99b30c773
--- /dev/null
+++ b/src/pal/src/safecrt/wcstok_s.c
@@ -0,0 +1,27 @@
+// 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.
+
+/***
+*wcstok_s.c - tokenize a wide-character string with given delimiters
+*
+
+*
+*Purpose:
+* defines wcstok_s() - breaks wide-character string into series of token
+* via repeated calls.
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME wcstok_s
+#define _CHAR wchar_t
+
+#include "tcstok_s.inl"
diff --git a/src/pal/src/safecrt/wmakepath_s.c b/src/pal/src/safecrt/wmakepath_s.c
new file mode 100644
index 0000000000..35ab7d386e
--- /dev/null
+++ b/src/pal/src/safecrt/wmakepath_s.c
@@ -0,0 +1,30 @@
+// 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.
+
+/***
+*wmakepath_s.c - create path name from components
+*
+
+*
+*Purpose:
+* To provide support for creation of full path names from components
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME _wmakepath_s
+#define _CHAR wchar_t
+#define _DEST _Dst
+#define _SIZE _SizeInWords
+#define _T(_Character) L##_Character
+#define _MBS_SUPPORT 0
+
+#include "tmakepath_s.inl"
diff --git a/src/pal/src/safecrt/wsplitpath_s.c b/src/pal/src/safecrt/wsplitpath_s.c
new file mode 100644
index 0000000000..c7fb107803
--- /dev/null
+++ b/src/pal/src/safecrt/wsplitpath_s.c
@@ -0,0 +1,30 @@
+// 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.
+
+/***
+*wsplitpath_s.c - break down path name into components
+*
+
+*
+*Purpose:
+* To provide support for accessing the individual components of an
+* arbitrary path name
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _FUNC_PROLOGUE
+#define _FUNC_NAME _wsplitpath_s
+#define _CHAR wchar_t
+#define _TCSNCPY_S wcsncpy_s
+#define _T(_Character) L##_Character
+#define _MBS_SUPPORT 0
+
+#include "tsplitpath_s.inl"
diff --git a/src/pal/src/safecrt/xtoa_s.c b/src/pal/src/safecrt/xtoa_s.c
new file mode 100644
index 0000000000..42cc5786d1
--- /dev/null
+++ b/src/pal/src/safecrt/xtoa_s.c
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/***
+*strtok_s.c - tokenize a string with given delimiters
+*
+
+*
+*Purpose:
+* defines strtok_s() - breaks string into series of token
+* via repeated calls.
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+//#define __int64 long long
+
+#define _SECURE_ITOA
+
+#define TCHAR char
+#define _T(x) x
+#include "xtox_s.inl"
diff --git a/src/pal/src/safecrt/xtow_s.c b/src/pal/src/safecrt/xtow_s.c
new file mode 100644
index 0000000000..7a02424c85
--- /dev/null
+++ b/src/pal/src/safecrt/xtow_s.c
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+/***
+*strtok_s.c - tokenize a string with given delimiters
+*
+
+*
+*Purpose:
+* defines strtok_s() - breaks string into series of token
+* via repeated calls.
+*
+*******************************************************************************/
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "internal_securecrt.h"
+
+#include "mbusafecrt_internal.h"
+
+#define _SECURE_ITOA
+
+#define _UNICODE
+#define TCHAR wchar_t
+#define _T(x) L##x
+#include "xtox_s.inl"
diff --git a/src/pal/src/safecrt/xtox_s.inl b/src/pal/src/safecrt/xtox_s.inl
new file mode 100644
index 0000000000..e07d87adf5
--- /dev/null
+++ b/src/pal/src/safecrt/xtox_s.inl
@@ -0,0 +1,450 @@
+// 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.
+
+/***
+*xtoa.c - convert integers/longs to ASCII string
+*
+
+*
+*Purpose:
+* The module has code to convert integers/longs to ASCII strings. See
+*
+*******************************************************************************/
+
+#ifdef _UNICODE
+#define xtox_s xtow_s
+#define _itox_s _itow_s
+#define _ltox_s _ltow_s
+#define _ultox_s _ultow_s
+#define x64tox_s x64tow_s
+#define _i64tox_s _i64tow_s
+#define _ui64tox_s _ui64tow_s
+#define xtox xtow
+#define _itox _itow
+#define _ltox _ltow
+#define _ultox _ultow
+#define x64tox x64tow
+#define _i64tox _i64tow
+#define _ui64tox _ui64tow
+#else /* _UNICODE */
+#define xtox_s xtoa_s
+#define _itox_s _itoa_s
+#define _ltox_s _ltoa_s
+#define _ultox_s _ultoa_s
+#define x64tox_s x64toa_s
+#define _i64tox_s _i64toa_s
+#define _ui64tox_s _ui64toa_s
+#define xtox xtoa
+#define _itox _itoa
+#define _ltox _ltoa
+#define _ultox _ultoa
+#define x64tox x64toa
+#define _i64tox _i64toa
+#define _ui64tox _ui64toa
+#endif /* _UNICODE */
+
+/***
+*char *_itoa_s, *_ltoa_s, *_ultoa_s(val, buf, sizeInTChars, radix) - convert binary int to ASCII
+* string
+*
+*Purpose:
+* Converts an int to a character string.
+*
+*Entry:
+* val - number to be converted (int, long or unsigned long)
+* char *buf - ptr to buffer to place result
+* size_t sizeInTChars - size of the destination buffer
+* int radix - base to convert into
+*
+*Exit:
+* Fills in space pointed to by buf with string result.
+* Returns the errno_t: err != 0 means that something went wrong, and
+* an empty string (buf[0] = 0) is returned.
+*
+*Exceptions:
+* Input parameters and buffer length are validated.
+* Refer to the validation section of the function.
+*
+*******************************************************************************/
+
+/* helper routine that does the main job. */
+#ifdef _SECURE_ITOA
+static errno_t __stdcall xtox_s
+ (
+ unsigned long val,
+ TCHAR *buf,
+ size_t sizeInTChars,
+ unsigned radix,
+ int is_neg
+ )
+#else /* _SECURE_ITOA */
+static void __stdcall xtox
+ (
+ unsigned long val,
+ TCHAR *buf,
+ unsigned radix,
+ int is_neg
+ )
+#endif /* _SECURE_ITOA */
+{
+ TCHAR *p; /* pointer to traverse string */
+ TCHAR *firstdig; /* pointer to first digit */
+ TCHAR temp; /* temp char */
+ unsigned digval; /* value of digit */
+#ifdef _SECURE_ITOA
+ size_t length; /* current length of the string */
+
+ /* validation section */
+ _VALIDATE_RETURN_ERRCODE(buf != NULL, EINVAL);
+ _VALIDATE_RETURN_ERRCODE(sizeInTChars > 0, EINVAL);
+ _RESET_STRING(buf, sizeInTChars);
+ _VALIDATE_RETURN_ERRCODE(sizeInTChars > (size_t)(is_neg ? 2 : 1), ERANGE);
+ _VALIDATE_RETURN_ERRCODE(2 <= radix && radix <= 36, EINVAL);
+ length = 0;
+
+#endif /* _SECURE_ITOA */
+ p = buf;
+
+ if (is_neg) {
+ /* negative, so output '-' and negate */
+ *p++ = _T('-');
+#ifdef _SECURE_ITOA
+ length++;
+#endif /* _SECURE_ITOA */
+ val = (unsigned long)(-(long)val);
+ }
+
+ firstdig = p; /* save pointer to first digit */
+
+ do {
+ digval = (unsigned) (val % radix);
+ val /= radix; /* get next digit */
+
+ /* convert to ascii and store */
+ if (digval > 9)
+ *p++ = (TCHAR) (digval - 10 + _T('a')); /* a letter */
+ else
+ *p++ = (TCHAR) (digval + _T('0')); /* a digit */
+#ifndef _SECURE_ITOA
+ } while (val > 0);
+#else /* _SECURE_ITOA */
+ length++;
+ } while (val > 0 && length < sizeInTChars);
+
+ /* Check for buffer overrun */
+ if (length >= sizeInTChars)
+ {
+ buf[0] = '\0';
+ _VALIDATE_RETURN_ERRCODE(length < sizeInTChars, ERANGE);
+ }
+#endif /* _SECURE_ITOA */
+ /* We now have the digit of the number in the buffer, but in reverse
+ order. Thus we reverse them now. */
+
+ *p-- = _T('\0'); /* terminate string; p points to last digit */
+
+ do {
+ temp = *p;
+ *p = *firstdig;
+ *firstdig = temp; /* swap *p and *firstdig */
+ --p;
+ ++firstdig; /* advance to next two digits */
+ } while (firstdig < p); /* repeat until halfway */
+#ifdef _SECURE_ITOA
+ return 0;
+#endif /* _SECURE_ITOA */
+}
+
+/* Actual functions just call conversion helper with neg flag set correctly,
+ and return pointer to buffer. */
+
+#ifdef _SECURE_ITOA
+errno_t __cdecl _itox_s (
+ int val,
+ TCHAR *buf,
+ size_t sizeInTChars,
+ int radix
+ )
+{
+ errno_t e = 0;
+
+ if (radix == 10 && val < 0)
+ e = xtox_s((unsigned long)val, buf, sizeInTChars, radix, 1);
+ else
+ e = xtox_s((unsigned long)(unsigned int)val, buf, sizeInTChars, radix, 0);
+
+ return e;
+}
+
+errno_t __cdecl _ltox_s (
+ long val,
+ TCHAR *buf,
+ size_t sizeInTChars,
+ int radix
+ )
+{
+ return xtox_s((unsigned long)val, buf, sizeInTChars, radix, (radix == 10 && val < 0));
+}
+
+errno_t __cdecl _ultox_s (
+ unsigned long val,
+ TCHAR *buf,
+ size_t sizeInTChars,
+ int radix
+ )
+{
+ return xtox_s(val, buf, sizeInTChars, radix, 0);
+}
+
+#else /* _SECURE_ITOA */
+
+/***
+*char *_itoa, *_ltoa, *_ultoa(val, buf, radix) - convert binary int to ASCII
+* string
+*
+*Purpose:
+* Converts an int to a character string.
+*
+*Entry:
+* val - number to be converted (int, long or unsigned long)
+* int radix - base to convert into
+* char *buf - ptr to buffer to place result
+*
+*Exit:
+* fills in space pointed to by buf with string result
+* returns a pointer to this buffer
+*
+*Exceptions:
+* Input parameters are validated. The buffer is assumed to be big enough to
+* contain the string. Refer to the validation section of the function.
+*
+*******************************************************************************/
+
+/* Actual functions just call conversion helper with neg flag set correctly,
+ and return pointer to buffer. */
+
+TCHAR * __cdecl _itox (
+ int val,
+ TCHAR *buf,
+ int radix
+ )
+{
+ if (radix == 10 && val < 0)
+ xtox((unsigned long)val, buf, radix, 1);
+ else
+ xtox((unsigned long)(unsigned int)val, buf, radix, 0);
+ return buf;
+}
+
+TCHAR * __cdecl _ltox (
+ long val,
+ TCHAR *buf,
+ int radix
+ )
+{
+ xtox((unsigned long)val, buf, radix, (radix == 10 && val < 0));
+ return buf;
+}
+
+TCHAR * __cdecl _ultox (
+ unsigned long val,
+ TCHAR *buf,
+ int radix
+ )
+{
+ xtox(val, buf, radix, 0);
+ return buf;
+}
+
+#endif /* _SECURE_ITOA */
+
+#ifndef _NO_INT64
+
+/***
+*char *_i64toa_s(val, buf, sizeInTChars, radix) - convert binary int to ASCII
+* string
+*
+*Purpose:
+* Converts an int64 to a character string.
+*
+*Entry:
+* val - number to be converted
+* char *buf - ptr to buffer to place result
+* size_t sizeInTChars - size of the destination buffer
+* int radix - base to convert into
+*
+*Exit:
+* Fills in space pointed to by buf with string result.
+* Returns the errno_t: err != 0 means that something went wrong, and
+* an empty string (buf[0] = 0) is returned.
+*
+*Exceptions:
+* Input parameters and buffer length are validated.
+* Refer to the validation section of the function.
+*
+*******************************************************************************/
+
+#ifdef _SECURE_ITOA
+static errno_t __fastcall x64tox_s
+ (/* stdcall is faster and smaller... Might as well use it for the helper. */
+ unsigned __int64 val,
+ TCHAR *buf,
+ size_t sizeInTChars,
+ unsigned radix,
+ int is_neg
+ )
+#else /* _SECURE_ITOA */
+static void __fastcall x64tox
+ (/* stdcall is faster and smaller... Might as well use it for the helper. */
+ unsigned __int64 val,
+ TCHAR *buf,
+ unsigned radix,
+ int is_neg
+ )
+#endif /* _SECURE_ITOA */
+{
+ TCHAR *p; /* pointer to traverse string */
+ TCHAR *firstdig; /* pointer to first digit */
+ TCHAR temp; /* temp char */
+ unsigned digval; /* value of digit */
+#ifdef _SECURE_ITOA
+ size_t length; /* current length of the string */
+
+ /* validation section */
+ _VALIDATE_RETURN_ERRCODE(buf != NULL, EINVAL);
+ _VALIDATE_RETURN_ERRCODE(sizeInTChars > 0, EINVAL);
+ _RESET_STRING(buf, sizeInTChars);
+ _VALIDATE_RETURN_ERRCODE(sizeInTChars > (size_t)(is_neg ? 2 : 1), ERANGE);
+ _VALIDATE_RETURN_ERRCODE(2 <= radix && radix <= 36, EINVAL);
+ length = 0;
+#endif /* _SECURE_ITOA */
+ p = buf;
+
+ if ( is_neg )
+ {
+ *p++ = _T('-'); /* negative, so output '-' and negate */
+#ifdef _SECURE_ITOA
+ length++;
+#endif /* _SECURE_ITOA */
+ val = (unsigned __int64)(-(__int64)val);
+ }
+
+ firstdig = p; /* save pointer to first digit */
+
+ do {
+ digval = (unsigned) (val % radix);
+ val /= radix; /* get next digit */
+
+ /* convert to ascii and store */
+ if (digval > 9)
+ *p++ = (TCHAR) (digval - 10 + _T('a')); /* a letter */
+ else
+ *p++ = (TCHAR) (digval + _T('0')); /* a digit */
+
+#ifndef _SECURE_ITOA
+ } while (val > 0);
+#else /* _SECURE_ITOA */
+ length++;
+ } while (val > 0 && length < sizeInTChars);
+
+ /* Check for buffer overrun */
+ if (length >= sizeInTChars)
+ {
+ buf[0] = '\0';
+ _VALIDATE_RETURN_ERRCODE(length < sizeInTChars, ERANGE);
+ }
+#endif /* _SECURE_ITOA */
+ /* We now have the digit of the number in the buffer, but in reverse
+ order. Thus we reverse them now. */
+
+ *p-- = _T('\0'); /* terminate string; p points to last digit */
+
+ do {
+ temp = *p;
+ *p = *firstdig;
+ *firstdig = temp; /* swap *p and *firstdig */
+ --p;
+ ++firstdig; /* advance to next two digits */
+ } while (firstdig < p); /* repeat until halfway */
+
+#ifdef _SECURE_ITOA
+ return 0;
+#endif /* _SECURE_ITOA */
+}
+
+#ifdef _SECURE_ITOA
+
+/* Actual functions just call conversion helper with neg flag set correctly,
+ and return pointer to buffer. */
+
+errno_t __cdecl _i64tox_s (
+ long long val,
+ TCHAR *buf,
+ size_t sizeInTChars,
+ int radix
+ )
+{
+ return x64tox_s((unsigned __int64)val, buf, sizeInTChars, radix, (radix == 10 && val < 0));
+}
+
+errno_t __cdecl _ui64tox_s (
+ unsigned long long val,
+ TCHAR *buf,
+ size_t sizeInTChars,
+ int radix
+ )
+{
+ return x64tox_s(val, buf, sizeInTChars, radix, 0);
+}
+
+#else /* _SECURE_ITOA */
+
+/***
+*char *_i64toa(val, buf, radix) - convert binary int to ASCII
+* string
+*
+*Purpose:
+* Converts an int64 to a character string.
+*
+*Entry:
+* val - number to be converted
+* int radix - base to convert into
+* char *buf - ptr to buffer to place result
+*
+*Exit:
+* fills in space pointed to by buf with string result
+* returns a pointer to this buffer
+*
+*Exceptions:
+* Input parameters are validated. The buffer is assumed to be big enough to
+* contain the string. Refer to the validation section of the function.
+*
+*******************************************************************************/
+
+/* Actual functions just call conversion helper with neg flag set correctly,
+ and return pointer to buffer. */
+
+TCHAR * __cdecl _i64tox (
+ __int64 val,
+ TCHAR *buf,
+ int radix
+ )
+{
+ x64tox((unsigned __int64)val, buf, radix, (radix == 10 && val < 0));
+ return buf;
+}
+
+TCHAR * __cdecl _ui64tox (
+ unsigned __int64 val,
+ TCHAR *buf,
+ int radix
+ )
+{
+ x64tox(val, buf, radix, 0);
+ return buf;
+}
+
+#endif /* _SECURE_ITOA */
+
+#endif /* _NO_INT64 */
diff --git a/src/pal/src/sharedmemory/sharedmemory.cpp b/src/pal/src/sharedmemory/sharedmemory.cpp
new file mode 100644
index 0000000000..7f25cae49e
--- /dev/null
+++ b/src/pal/src/sharedmemory/sharedmemory.cpp
@@ -0,0 +1,1136 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "pal/sharedmemory.h"
+
+#include "pal/dbgmsg.h"
+#include "pal/file.hpp"
+#include "pal/malloc.hpp"
+#include "pal/thread.hpp"
+#include "pal/virtual.h"
+
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(SHMEM);
+
+#include "pal/sharedmemory.inl"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// AutoFreeBuffer
+
+AutoFreeBuffer::AutoFreeBuffer(void *buffer) : m_buffer(buffer), m_cancel(false)
+{
+}
+
+AutoFreeBuffer::~AutoFreeBuffer()
+{
+ if (!m_cancel && m_buffer != nullptr)
+ {
+ free(m_buffer);
+ }
+}
+
+void AutoFreeBuffer::Cancel()
+{
+ m_cancel = true;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SharedMemoryException
+
+SharedMemoryException::SharedMemoryException(DWORD errorCode) : m_errorCode(errorCode)
+{
+}
+
+DWORD SharedMemoryException::GetErrorCode() const
+{
+ return m_errorCode;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SharedMemoryHelpers
+
+const mode_t SharedMemoryHelpers::PermissionsMask_AllUsers_ReadWrite =
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+const mode_t SharedMemoryHelpers::PermissionsMask_AllUsers_ReadWriteExecute =
+ PermissionsMask_AllUsers_ReadWrite | (S_IXUSR | S_IXGRP | S_IXOTH);
+const UINT32 SharedMemoryHelpers::InvalidProcessId = static_cast<UINT32>(-1);
+const SIZE_T SharedMemoryHelpers::InvalidThreadId = static_cast<SIZE_T>(-1);
+const UINT64 SharedMemoryHelpers::InvalidSharedThreadId = static_cast<UINT64>(-1);
+
+void *SharedMemoryHelpers::Alloc(SIZE_T byteCount)
+{
+ void *buffer = InternalMalloc(byteCount);
+ if (buffer == nullptr)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::OutOfMemory));
+ }
+ return buffer;
+}
+
+SIZE_T SharedMemoryHelpers::AlignDown(SIZE_T value, SIZE_T alignment)
+{
+ _ASSERTE((alignment & (alignment - 1)) == 0); // must be a power of 2
+ return value & ~(alignment - 1);
+}
+
+SIZE_T SharedMemoryHelpers::AlignUp(SIZE_T value, SIZE_T alignment)
+{
+ _ASSERTE((alignment & (alignment - 1)) == 0); // must be a power of 2
+ return AlignDown(value + (alignment - 1), alignment);
+}
+
+bool SharedMemoryHelpers::EnsureDirectoryExists(const char *path, bool isGlobalLockAcquired, bool createIfNotExist)
+{
+ _ASSERTE(path != nullptr);
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+
+ // Check if the path already exists
+ struct stat statInfo;
+ int statResult = stat(path, &statInfo);
+ if (statResult != 0 && errno == ENOENT)
+ {
+ if (!createIfNotExist)
+ {
+ return false;
+ }
+
+ // The path does not exist, create the directory. The permissions mask passed to mkdir() is filtered by the process'
+ // permissions umask, so mkdir() may not set all of the requested permissions. We need to use chmod() to set the proper
+ // permissions. That creates a race when there is no global lock acquired when creating the directory. Another user's
+ // process may create the directory and this user's process may try to use it before the other process sets the full
+ // permissions. In that case, create a temporary directory first, set the permissions, and rename it to the actual
+ // directory name.
+
+ if (isGlobalLockAcquired)
+ {
+ if (mkdir(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ if (chmod(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0)
+ {
+ rmdir(path);
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ return true;
+ }
+
+ char tempPath[] = SHARED_MEMORY_UNIQUE_TEMP_NAME_TEMPLATE;
+ if (mkdtemp(tempPath) == nullptr)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ if (chmod(tempPath, PermissionsMask_AllUsers_ReadWriteExecute) != 0)
+ {
+ rmdir(tempPath);
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ if (rename(tempPath, path) == 0)
+ {
+ return true;
+ }
+
+ // Another process may have beaten us to it. Delete the temp directory and continue to check the requested directory to
+ // see if it meets our needs.
+ rmdir(tempPath);
+ statResult = stat(path, &statInfo);
+ }
+
+ // If the path exists, check that it's a directory
+ if (statResult != 0 || !(statInfo.st_mode & S_IFDIR))
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+
+ // Check the directory's permissions and try to update them
+ if ((statInfo.st_mode & PermissionsMask_AllUsers_ReadWriteExecute) == PermissionsMask_AllUsers_ReadWriteExecute)
+ {
+ return true;
+ }
+ if (!createIfNotExist || chmod(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ return true;
+}
+
+int SharedMemoryHelpers::Open(LPCSTR path, int flags, mode_t mode)
+{
+ int openErrorCode;
+ do
+ {
+ int fileDescriptor = InternalOpen(path, flags, mode);
+ if (fileDescriptor != -1)
+ {
+ return fileDescriptor;
+ }
+ openErrorCode = errno;
+ } while (openErrorCode == EINTR);
+
+ switch (openErrorCode)
+ {
+ case ENOENT:
+ _ASSERTE(!(flags & O_CREAT));
+ errno = openErrorCode;
+ return -1;
+
+ case ENAMETOOLONG:
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::NameTooLong));
+
+ case EMFILE:
+ case ENFILE:
+ case ENOMEM:
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::OutOfMemory));
+
+ default:
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+}
+
+int SharedMemoryHelpers::OpenDirectory(LPCSTR path)
+{
+ _ASSERTE(path != nullptr);
+ _ASSERTE(path[0] != '\0');
+
+ int fileDescriptor = Open(path, O_RDONLY);
+ _ASSERTE(fileDescriptor != -1 || errno == ENOENT);
+ return fileDescriptor;
+}
+
+int SharedMemoryHelpers::CreateOrOpenFile(LPCSTR path, bool createIfNotExist, bool *createdRef)
+{
+ _ASSERTE(path != nullptr);
+ _ASSERTE(path[0] != '\0');
+
+ // Try to open the file
+ int openFlags = O_RDWR;
+ int fileDescriptor = Open(path, openFlags);
+ if (fileDescriptor != -1)
+ {
+ if (createdRef != nullptr)
+ {
+ *createdRef = false;
+ }
+ return fileDescriptor;
+ }
+ _ASSERTE(errno == ENOENT);
+ if (!createIfNotExist)
+ {
+ if (createdRef != nullptr)
+ {
+ *createdRef = false;
+ }
+ return -1;
+ }
+
+ // File does not exist, create the file
+ openFlags |= O_CREAT | O_EXCL;
+ fileDescriptor = Open(path, openFlags, PermissionsMask_AllUsers_ReadWrite);
+ _ASSERTE(fileDescriptor != -1);
+
+ // The permissions mask passed to open() is filtered by the process' permissions umask, so open() may not set all of
+ // the requested permissions. Use chmod() to set the proper permissions.
+ if (chmod(path, PermissionsMask_AllUsers_ReadWrite) != 0)
+ {
+ CloseFile(fileDescriptor);
+ unlink(path);
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+
+ if (createdRef != nullptr)
+ {
+ *createdRef = true;
+ }
+ return fileDescriptor;
+}
+
+void SharedMemoryHelpers::CloseFile(int fileDescriptor)
+{
+ _ASSERTE(fileDescriptor != -1);
+
+ int closeResult;
+ do
+ {
+ closeResult = close(fileDescriptor);
+ } while (closeResult != 0 && errno == EINTR);
+}
+
+SIZE_T SharedMemoryHelpers::GetFileSize(int fileDescriptor)
+{
+ _ASSERTE(fileDescriptor != -1);
+
+ off_t endOffset = lseek(fileDescriptor, 0, SEEK_END);
+ if (endOffset == static_cast<off_t>(-1) ||
+ lseek(fileDescriptor, 0, SEEK_SET) == static_cast<off_t>(-1))
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ return endOffset;
+}
+
+void SharedMemoryHelpers::SetFileSize(int fileDescriptor, SIZE_T byteCount)
+{
+ _ASSERTE(fileDescriptor != -1);
+ _ASSERTE(static_cast<off_t>(byteCount) == byteCount);
+
+ while (true)
+ {
+ int ftruncateResult = ftruncate(fileDescriptor, static_cast<off_t>(byteCount));
+ if (ftruncateResult == 0)
+ {
+ break;
+ }
+ if (errno != EINTR)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ }
+}
+
+void *SharedMemoryHelpers::MemoryMapFile(int fileDescriptor, SIZE_T byteCount)
+{
+ _ASSERTE(fileDescriptor != -1);
+ _ASSERTE(byteCount > sizeof(SharedMemorySharedDataHeader));
+ _ASSERTE(AlignDown(byteCount, VIRTUAL_PAGE_SIZE) == byteCount);
+
+ void *sharedMemoryBuffer = mmap(nullptr, byteCount, PROT_READ | PROT_WRITE, MAP_SHARED, fileDescriptor, 0);
+ if (sharedMemoryBuffer != MAP_FAILED)
+ {
+ return sharedMemoryBuffer;
+ }
+ switch (errno)
+ {
+ case ENFILE:
+ case ENOMEM:
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::OutOfMemory));
+
+ default:
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+}
+
+bool SharedMemoryHelpers::TryAcquireFileLock(int fileDescriptor, int operation)
+{
+ // A file lock is acquired once per file descriptor, so the caller will need to synchronize threads of this process
+
+ _ASSERTE(fileDescriptor != -1);
+ _ASSERTE(!(operation & LOCK_UN));
+
+ while (true)
+ {
+ if (flock(fileDescriptor, operation) == 0)
+ {
+ return true;
+ }
+
+ int flockError = errno;
+ switch (flockError)
+ {
+ case EWOULDBLOCK:
+ return false;
+
+ case EINTR:
+ continue;
+
+ default:
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::OutOfMemory));
+ }
+ }
+}
+
+void SharedMemoryHelpers::ReleaseFileLock(int fileDescriptor)
+{
+ _ASSERTE(fileDescriptor != -1);
+
+ int flockResult;
+ do
+ {
+ flockResult = flock(fileDescriptor, LOCK_UN);
+ } while (flockResult != 0 && errno == EINTR);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SharedMemoryId
+
+SharedMemoryId::SharedMemoryId() : m_name(nullptr)
+{
+}
+
+SharedMemoryId::SharedMemoryId(LPCSTR name, SIZE_T nameCharCount, bool isSessionScope)
+ : m_name(name), m_nameCharCount(nameCharCount), m_isSessionScope(isSessionScope)
+{
+ _ASSERTE(name != nullptr);
+ _ASSERTE(nameCharCount != 0);
+ _ASSERTE(nameCharCount <= SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT);
+ _ASSERTE(strlen(name) == nameCharCount);
+}
+
+SharedMemoryId::SharedMemoryId(LPCSTR name)
+{
+ _ASSERTE(name != nullptr);
+
+ // Look for "Global\" and "Local\" prefixes in the name, and determine the session ID
+ if (strncmp(name, "Global\\", 7) == 0)
+ {
+ m_isSessionScope = false;
+ name += _countof("Global\\") - 1;
+ }
+ else
+ {
+ if (strncmp(name, "Local\\", 6) == 0)
+ {
+ name += _countof("Local\\") - 1;
+ }
+ m_isSessionScope = true;
+ }
+ m_name = name;
+
+ m_nameCharCount = strlen(name);
+ if (m_nameCharCount == 0)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::NameEmpty));
+ }
+ if (m_nameCharCount > SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::NameTooLong));
+ }
+
+ // Look for invalid characters '\' and '/' in the name
+ for (SIZE_T i = 0; i < m_nameCharCount; ++i)
+ {
+ char c = name[i];
+ if (c == '\\' || c == '/')
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::NameInvalid));
+ }
+ }
+}
+
+LPCSTR SharedMemoryId::GetName() const
+{
+ _ASSERTE(m_name != nullptr);
+ return m_name;
+}
+
+SIZE_T SharedMemoryId::GetNameCharCount() const
+{
+ _ASSERTE(m_name != nullptr);
+ return m_nameCharCount;
+}
+
+bool SharedMemoryId::IsSessionScope() const
+{
+ _ASSERTE(m_name != nullptr);
+ return m_isSessionScope;
+}
+
+bool SharedMemoryId::Equals(SharedMemoryId *other) const
+{
+ return
+ GetNameCharCount() == other->GetNameCharCount() &&
+ IsSessionScope() == other->IsSessionScope() &&
+ strcmp(GetName(), other->GetName()) == 0;
+}
+
+SIZE_T SharedMemoryId::AppendSessionDirectoryName(
+ char (&path)[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1],
+ SIZE_T pathCharCount) const
+{
+ if (IsSessionScope())
+ {
+ pathCharCount = SharedMemoryHelpers::CopyString(path, pathCharCount, SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX);
+ pathCharCount = SharedMemoryHelpers::AppendUInt32String(path, pathCharCount, GetCurrentSessionId());
+ }
+ else
+ {
+ pathCharCount = SharedMemoryHelpers::CopyString(path, pathCharCount, SHARED_MEMORY_GLOBAL_DIRECTORY_NAME);
+ }
+
+ _ASSERTE(pathCharCount <= SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT);
+ return pathCharCount;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SharedMemorySharedDataHeader
+
+SIZE_T SharedMemorySharedDataHeader::DetermineTotalByteCount(SIZE_T dataByteCount)
+{
+ return SharedMemoryHelpers::AlignUp(sizeof(SharedMemorySharedDataHeader) + dataByteCount, VIRTUAL_PAGE_SIZE);
+}
+
+SharedMemorySharedDataHeader::SharedMemorySharedDataHeader(SharedMemoryType type, UINT8 version)
+ : m_type(type), m_version(version)
+{
+}
+
+SharedMemoryType SharedMemorySharedDataHeader::GetType() const
+{
+ return m_type;
+}
+
+UINT8 SharedMemorySharedDataHeader::GetVersion() const
+{
+ return m_version;
+}
+
+void *SharedMemorySharedDataHeader::GetData()
+{
+ return this + 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SharedMemoryProcessDataHeader
+
+SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen(
+ LPCSTR name,
+ SharedMemorySharedDataHeader requiredSharedDataHeader,
+ SIZE_T sharedDataByteCount,
+ bool createIfNotExist,
+ bool *createdRef)
+{
+ _ASSERTE(name != nullptr);
+ _ASSERTE(sharedDataByteCount != 0);
+ _ASSERTE(!createIfNotExist || createdRef != nullptr);
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(!SharedMemoryManager::IsCreationDeletionFileLockAcquired());
+
+ if (createdRef != nullptr)
+ {
+ *createdRef = false;
+ }
+
+ SharedMemoryId id(name);
+
+ struct AutoCleanup
+ {
+ bool m_acquiredCreationDeletionFileLock;
+ char *m_filePath;
+ SIZE_T m_sessionDirectoryPathCharCount;
+ bool m_createdFile;
+ int m_fileDescriptor;
+ bool m_acquiredFileLock;
+ void *m_mappedBuffer;
+ SIZE_T m_mappedBufferByteCount;
+ bool m_cancel;
+
+ AutoCleanup()
+ : m_acquiredCreationDeletionFileLock(false),
+ m_filePath(nullptr),
+ m_sessionDirectoryPathCharCount(0),
+ m_createdFile(false),
+ m_fileDescriptor(-1),
+ m_acquiredFileLock(false),
+ m_mappedBuffer(nullptr),
+ m_mappedBufferByteCount(0),
+ m_cancel(false)
+ {
+ }
+
+ ~AutoCleanup()
+ {
+ if (m_cancel)
+ {
+ return;
+ }
+
+ if (m_mappedBuffer != nullptr)
+ {
+ _ASSERTE(m_mappedBufferByteCount != 0);
+ munmap(m_mappedBuffer, m_mappedBufferByteCount);
+ }
+
+ if (m_acquiredFileLock)
+ {
+ _ASSERTE(m_fileDescriptor != -1);
+ SharedMemoryHelpers::ReleaseFileLock(m_fileDescriptor);
+ }
+
+ if (m_fileDescriptor != -1)
+ {
+ SharedMemoryHelpers::CloseFile(m_fileDescriptor);
+ }
+
+ if (m_createdFile)
+ {
+ _ASSERTE(m_filePath != nullptr);
+ unlink(m_filePath);
+ }
+
+ if (m_sessionDirectoryPathCharCount != 0)
+ {
+ _ASSERTE(m_filePath != nullptr);
+ m_filePath[m_sessionDirectoryPathCharCount] = '\0';
+ rmdir(m_filePath);
+ }
+
+ if (m_acquiredCreationDeletionFileLock)
+ {
+ SharedMemoryManager::ReleaseCreationDeletionFileLock();
+ }
+ }
+ } autoCleanup;
+
+ SharedMemoryProcessDataHeader *processDataHeader = SharedMemoryManager::FindProcessDataHeader(&id);
+ if (processDataHeader != nullptr)
+ {
+ _ASSERTE(
+ processDataHeader->GetSharedDataTotalByteCount() ==
+ SharedMemorySharedDataHeader::DetermineTotalByteCount(sharedDataByteCount));
+ processDataHeader->IncRefCount();
+ return processDataHeader;
+ }
+
+ SharedMemoryManager::AcquireCreationDeletionFileLock();
+ autoCleanup.m_acquiredCreationDeletionFileLock = true;
+
+ // Create the session directory
+ char filePath[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1];
+ SIZE_T filePathCharCount = SharedMemoryHelpers::CopyString(filePath, 0, SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH);
+ filePath[filePathCharCount++] = '/';
+ filePathCharCount = id.AppendSessionDirectoryName(filePath, filePathCharCount);
+ if (!SharedMemoryHelpers::EnsureDirectoryExists(filePath, true /* isGlobalLockAcquired */, createIfNotExist))
+ {
+ _ASSERTE(!createIfNotExist);
+ return nullptr;
+ }
+ autoCleanup.m_filePath = filePath;
+ autoCleanup.m_sessionDirectoryPathCharCount = filePathCharCount;
+
+ // Create or open the shared memory file
+ filePath[filePathCharCount++] = '/';
+ filePathCharCount = SharedMemoryHelpers::CopyString(filePath, filePathCharCount, id.GetName(), id.GetNameCharCount());
+ bool createdFile;
+ int fileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(filePath, createIfNotExist, &createdFile);
+ if (fileDescriptor == -1)
+ {
+ _ASSERTE(!createIfNotExist);
+ return nullptr;
+ }
+ autoCleanup.m_createdFile = createdFile;
+ autoCleanup.m_fileDescriptor = fileDescriptor;
+
+ bool clearContents = false;
+ if (!createdFile)
+ {
+ // A shared file lock on the shared memory file would be held by any process that has opened the same file. Try to take
+ // an exclusive lock on the file. Successfully acquiring an exclusive lock indicates that no process has a reference to
+ // the shared memory file, and this process can reinitialize its contents.
+ if (SharedMemoryHelpers::TryAcquireFileLock(fileDescriptor, LOCK_EX | LOCK_NB))
+ {
+ // The shared memory file is not being used, flag it as created so that its contents will be reinitialized
+ SharedMemoryHelpers::ReleaseFileLock(fileDescriptor);
+ autoCleanup.m_createdFile = true;
+ if (!createIfNotExist)
+ {
+ return nullptr;
+ }
+ createdFile = true;
+ clearContents = true;
+ }
+ }
+
+ // Set or validate the file length
+ SIZE_T sharedDataTotalByteCount = SharedMemorySharedDataHeader::DetermineTotalByteCount(sharedDataByteCount);
+ if (createdFile)
+ {
+ SharedMemoryHelpers::SetFileSize(fileDescriptor, sharedDataTotalByteCount);
+ }
+ else if (SharedMemoryHelpers::GetFileSize(fileDescriptor) != sharedDataTotalByteCount)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::HeaderMismatch));
+ }
+
+ // Acquire and hold a shared file lock on the shared memory file as long as it is open, to indicate that this process is
+ // using the file. An exclusive file lock is attempted above to detect whether the file contents are valid, for the case
+ // where a process crashes or is killed after the file is created. Since we already hold the creation/deletion locks, a
+ // non-blocking file lock should succeed.
+ if (!SharedMemoryHelpers::TryAcquireFileLock(fileDescriptor, LOCK_SH | LOCK_NB))
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ autoCleanup.m_acquiredFileLock = true;
+
+ // Map the file into memory, and initialize or validate the header
+ void *mappedBuffer = SharedMemoryHelpers::MemoryMapFile(fileDescriptor, sharedDataTotalByteCount);
+ autoCleanup.m_mappedBuffer = mappedBuffer;
+ autoCleanup.m_mappedBufferByteCount = sharedDataTotalByteCount;
+ SharedMemorySharedDataHeader *sharedDataHeader;
+ if (createdFile)
+ {
+ if (clearContents)
+ {
+ memset(mappedBuffer, 0, sharedDataTotalByteCount);
+ }
+ sharedDataHeader = new(mappedBuffer) SharedMemorySharedDataHeader(requiredSharedDataHeader);
+ }
+ else
+ {
+ sharedDataHeader = reinterpret_cast<SharedMemorySharedDataHeader *>(mappedBuffer);
+ if (sharedDataHeader->GetType() != requiredSharedDataHeader.GetType() ||
+ sharedDataHeader->GetVersion() != requiredSharedDataHeader.GetVersion())
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::HeaderMismatch));
+ }
+ }
+
+ // When *createdRef is true, the creation/deletion file lock will remain locked upon returning for the caller to initialize
+ // the shared data. The caller must release the file lock afterwards.
+ if (!createdFile)
+ {
+ autoCleanup.m_acquiredCreationDeletionFileLock = false;
+ SharedMemoryManager::ReleaseCreationDeletionFileLock();
+ }
+
+ processDataHeader = SharedMemoryProcessDataHeader::New(&id, fileDescriptor, sharedDataHeader, sharedDataTotalByteCount);
+
+ autoCleanup.m_cancel = true;
+ if (createdFile)
+ {
+ _ASSERTE(createIfNotExist);
+ _ASSERTE(createdRef != nullptr);
+ *createdRef = true;
+ }
+ return processDataHeader;
+}
+
+SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::PalObject_GetProcessDataHeader(CorUnix::IPalObject *object)
+{
+ _ASSERTE(object != nullptr);
+ _ASSERTE(object->GetObjectType()->GetId() == otiNamedMutex);
+ _ASSERTE(object->GetObjectType()->GetImmutableDataSize() == sizeof(SharedMemoryProcessDataHeader *));
+
+ void *immutableDataBuffer;
+ PAL_ERROR errorCode = object->GetImmutableData(&immutableDataBuffer);
+ _ASSERTE(errorCode == NO_ERROR);
+ _ASSERTE(immutableDataBuffer != nullptr);
+ return *reinterpret_cast<SharedMemoryProcessDataHeader **>(immutableDataBuffer);
+}
+
+void SharedMemoryProcessDataHeader::PalObject_SetProcessDataHeader(
+ CorUnix::IPalObject *object,
+ SharedMemoryProcessDataHeader *processDataHeader)
+{
+ _ASSERTE(object != nullptr);
+ _ASSERTE(object->GetObjectType()->GetId() == otiNamedMutex);
+ _ASSERTE(object->GetObjectType()->GetImmutableDataSize() == sizeof(SharedMemoryProcessDataHeader *));
+ _ASSERTE(processDataHeader != nullptr);
+
+ void *immutableDataBuffer;
+ PAL_ERROR errorCode = object->GetImmutableData(&immutableDataBuffer);
+ _ASSERTE(errorCode == NO_ERROR);
+ _ASSERTE(immutableDataBuffer != nullptr);
+ *reinterpret_cast<SharedMemoryProcessDataHeader **>(immutableDataBuffer) = processDataHeader;
+}
+
+void SharedMemoryProcessDataHeader::PalObject_Close(
+ CPalThread *thread,
+ IPalObject *object,
+ bool isShuttingDown,
+ bool cleanUpPalSharedState)
+{
+ // This function's signature matches OBJECTCLEANUPROUTINE
+ _ASSERTE(thread != nullptr);
+ _ASSERTE(object != nullptr);
+ _ASSERTE(object->GetObjectType()->GetId() == otiNamedMutex);
+ _ASSERTE(object->GetObjectType()->GetImmutableDataSize() == sizeof(SharedMemoryProcessDataHeader *));
+
+ SharedMemoryProcessDataHeader *processDataHeader = PalObject_GetProcessDataHeader(object);
+ if (processDataHeader == nullptr)
+ {
+ // The object was created, but an error must have occurred before the process data was initialized
+ return;
+ }
+
+ SharedMemoryManager::AcquireCreationDeletionProcessLock();
+ processDataHeader->DecRefCount();
+ SharedMemoryManager::ReleaseCreationDeletionProcessLock();
+}
+
+SharedMemoryProcessDataHeader::SharedMemoryProcessDataHeader(
+ SharedMemoryId *id,
+ int fileDescriptor,
+ SharedMemorySharedDataHeader *sharedDataHeader,
+ SIZE_T sharedDataTotalByteCount)
+ :
+ m_refCount(1),
+ m_data(nullptr),
+ m_fileDescriptor(fileDescriptor),
+ m_sharedDataHeader(sharedDataHeader),
+ m_sharedDataTotalByteCount(sharedDataTotalByteCount),
+ m_nextInProcessDataHeaderList(nullptr)
+{
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(id != nullptr);
+ _ASSERTE(fileDescriptor != -1);
+ _ASSERTE(sharedDataHeader != nullptr);
+ _ASSERTE(sharedDataTotalByteCount > sizeof(SharedMemorySharedDataHeader));
+ _ASSERTE(SharedMemoryHelpers::AlignDown(sharedDataTotalByteCount, VIRTUAL_PAGE_SIZE) == sharedDataTotalByteCount);
+
+ // Copy the name and initialize the ID
+ char *nameCopy = reinterpret_cast<char *>(this + 1);
+ SIZE_T nameByteCount = id->GetNameCharCount() + 1;
+ memcpy_s(nameCopy, nameByteCount, id->GetName(), nameByteCount);
+ m_id = SharedMemoryId(nameCopy, id->GetNameCharCount(), id->IsSessionScope());
+
+ SharedMemoryManager::AddProcessDataHeader(this);
+}
+
+SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::New(
+ SharedMemoryId *id,
+ int fileDescriptor,
+ SharedMemorySharedDataHeader *sharedDataHeader,
+ SIZE_T sharedDataTotalByteCount)
+{
+ _ASSERTE(id != nullptr);
+
+ // Allocate space for the header and a copy of the name
+ SIZE_T nameByteCount = id->GetNameCharCount() + 1;
+ SIZE_T totalByteCount = sizeof(SharedMemoryProcessDataHeader) + nameByteCount;
+ void *buffer = SharedMemoryHelpers::Alloc(totalByteCount);
+ AutoFreeBuffer autoFreeBuffer(buffer);
+ SharedMemoryProcessDataHeader *processDataHeader =
+ new(buffer) SharedMemoryProcessDataHeader(id, fileDescriptor, sharedDataHeader, sharedDataTotalByteCount);
+ autoFreeBuffer.Cancel();
+ return processDataHeader;
+}
+
+SharedMemoryProcessDataHeader::~SharedMemoryProcessDataHeader()
+{
+ _ASSERTE(m_refCount == 0);
+ Close();
+}
+
+void SharedMemoryProcessDataHeader::Close()
+{
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(!SharedMemoryManager::IsCreationDeletionFileLockAcquired());
+
+ // If the ref count is nonzero, we are shutting down the process abruptly without having closed some shared memory objects.
+ // There could still be threads running with active references to the shared memory object. So when the ref count is
+ // nonzero, don't clean up any object or global process-local state.
+ if (m_refCount == 0)
+ {
+ SharedMemoryManager::RemoveProcessDataHeader(this);
+ }
+
+ struct AutoReleaseCreationDeletionFileLock
+ {
+ bool m_acquired;
+
+ AutoReleaseCreationDeletionFileLock() : m_acquired(false)
+ {
+ }
+
+ ~AutoReleaseCreationDeletionFileLock()
+ {
+ if (m_acquired)
+ {
+ SharedMemoryManager::ReleaseCreationDeletionFileLock();
+ }
+ }
+ } autoReleaseCreationDeletionFileLock;
+
+ // A shared file lock on the shared memory file would be held by any process that has opened the same file. Try to take
+ // an exclusive lock on the file. Successfully acquiring an exclusive lock indicates that no process has a reference to
+ // the shared memory file, and this process can delete the file. File locks on the shared memory file are only ever acquired
+ // or released while holding the creation/deletion locks, so holding the creation/deletion locks while trying an exclusive
+ // lock on the shared memory file guarantees that another process cannot start using the shared memory file after this
+ // process has decided to delete the file.
+ bool releaseSharedData = false;
+ try
+ {
+ SharedMemoryManager::AcquireCreationDeletionFileLock();
+ autoReleaseCreationDeletionFileLock.m_acquired = true;
+
+ SharedMemoryHelpers::ReleaseFileLock(m_fileDescriptor);
+ if (SharedMemoryHelpers::TryAcquireFileLock(m_fileDescriptor, LOCK_EX | LOCK_NB))
+ {
+ SharedMemoryHelpers::ReleaseFileLock(m_fileDescriptor);
+ releaseSharedData = true;
+ }
+ }
+ catch (SharedMemoryException)
+ {
+ // Ignore the error, just don't release shared data
+ }
+
+ if (m_data != nullptr)
+ {
+ m_data->Close(m_refCount != 0 /* isAbruptShutdown */, releaseSharedData);
+ }
+
+ if (m_refCount == 0)
+ {
+ if (m_data != nullptr)
+ {
+ InternalDelete(m_data);
+ }
+
+ if (releaseSharedData)
+ {
+ m_sharedDataHeader->~SharedMemorySharedDataHeader();
+ }
+
+ munmap(m_sharedDataHeader, m_sharedDataTotalByteCount);
+ SharedMemoryHelpers::CloseFile(m_fileDescriptor);
+ }
+
+ if (!releaseSharedData)
+ {
+ return;
+ }
+
+ // Delete the shared memory file, and the session directory if it's not empty
+ char path[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1];
+ SIZE_T sessionDirectoryPathCharCount = SharedMemoryHelpers::CopyString(path, 0, SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH);
+ path[sessionDirectoryPathCharCount++] = '/';
+ sessionDirectoryPathCharCount = m_id.AppendSessionDirectoryName(path, sessionDirectoryPathCharCount);
+ path[sessionDirectoryPathCharCount++] = '/';
+ SharedMemoryHelpers::CopyString(path, sessionDirectoryPathCharCount, m_id.GetName(), m_id.GetNameCharCount());
+ unlink(path);
+ path[sessionDirectoryPathCharCount] = '\0';
+ rmdir(path);
+}
+
+SharedMemoryId *SharedMemoryProcessDataHeader::GetId()
+{
+ return &m_id;
+}
+
+SharedMemoryProcessDataBase *SharedMemoryProcessDataHeader::GetData() const
+{
+ return m_data;
+}
+
+void SharedMemoryProcessDataHeader::SetData(SharedMemoryProcessDataBase *data)
+{
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(m_data == nullptr);
+ _ASSERTE(data != nullptr);
+
+ m_data = data;
+}
+
+SharedMemorySharedDataHeader *SharedMemoryProcessDataHeader::GetSharedDataHeader() const
+{
+ return m_sharedDataHeader;
+}
+
+SIZE_T SharedMemoryProcessDataHeader::GetSharedDataTotalByteCount() const
+{
+ return m_sharedDataTotalByteCount;
+}
+
+SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::GetNextInProcessDataHeaderList() const
+{
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ return m_nextInProcessDataHeaderList;
+}
+
+void SharedMemoryProcessDataHeader::SetNextInProcessDataHeaderList(SharedMemoryProcessDataHeader *next)
+{
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ m_nextInProcessDataHeaderList = next;
+}
+
+void SharedMemoryProcessDataHeader::IncRefCount()
+{
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(m_refCount != 0);
+
+ ++m_refCount;
+}
+
+void SharedMemoryProcessDataHeader::DecRefCount()
+{
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(m_refCount != 0);
+
+ if (--m_refCount == 0)
+ {
+ InternalDelete(this);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SharedMemoryManager
+
+CRITICAL_SECTION SharedMemoryManager::s_creationDeletionProcessLock;
+int SharedMemoryManager::s_creationDeletionLockFileDescriptor = -1;
+
+SharedMemoryProcessDataHeader *SharedMemoryManager::s_processDataHeaderListHead = nullptr;
+
+#ifdef _DEBUG
+SIZE_T SharedMemoryManager::s_creationDeletionProcessLockOwnerThreadId = SharedMemoryHelpers::InvalidThreadId;
+SIZE_T SharedMemoryManager::s_creationDeletionFileLockOwnerThreadId = SharedMemoryHelpers::InvalidThreadId;
+#endif // _DEBUG
+
+void SharedMemoryManager::StaticInitialize()
+{
+ InitializeCriticalSection(&s_creationDeletionProcessLock);
+}
+
+void SharedMemoryManager::StaticClose()
+{
+ // This function could very well be running during abrupt shutdown, and there could still be user threads running.
+ // Synchronize the deletion, and don't remove or delete items in the linked list.
+ AcquireCreationDeletionProcessLock();
+ for (SharedMemoryProcessDataHeader *current = s_processDataHeaderListHead;
+ current != nullptr;
+ current = current->GetNextInProcessDataHeaderList())
+ {
+ current->Close();
+ }
+ ReleaseCreationDeletionProcessLock();
+
+ // This function could very well be running during abrupt shutdown, and there could still be user threads running. Don't
+ // delete the creation/deletion process lock, the process is shutting down anyway.
+}
+
+void SharedMemoryManager::AcquireCreationDeletionProcessLock()
+{
+ _ASSERTE(!IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(!IsCreationDeletionFileLockAcquired());
+
+ EnterCriticalSection(&s_creationDeletionProcessLock);
+#ifdef _DEBUG
+ s_creationDeletionProcessLockOwnerThreadId = THREADSilentGetCurrentThreadId();
+#endif // _DEBUG
+}
+
+void SharedMemoryManager::ReleaseCreationDeletionProcessLock()
+{
+ _ASSERTE(IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(!IsCreationDeletionFileLockAcquired());
+
+#ifdef _DEBUG
+ s_creationDeletionProcessLockOwnerThreadId = SharedMemoryHelpers::InvalidThreadId;
+#endif // _DEBUG
+ LeaveCriticalSection(&s_creationDeletionProcessLock);
+}
+
+void SharedMemoryManager::AcquireCreationDeletionFileLock()
+{
+ _ASSERTE(IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(!IsCreationDeletionFileLockAcquired());
+
+ if (s_creationDeletionLockFileDescriptor == -1)
+ {
+ if (!SharedMemoryHelpers::EnsureDirectoryExists(
+ SHARED_MEMORY_TEMP_DIRECTORY_PATH,
+ false /* isGlobalLockAcquired */,
+ false /* createIfNotExist */))
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ SharedMemoryHelpers::EnsureDirectoryExists(
+ SHARED_MEMORY_RUNTIME_TEMP_DIRECTORY_PATH,
+ false /* isGlobalLockAcquired */);
+ SharedMemoryHelpers::EnsureDirectoryExists(
+ SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH,
+ false /* isGlobalLockAcquired */);
+ s_creationDeletionLockFileDescriptor = SharedMemoryHelpers::OpenDirectory(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_PATH);
+ if (s_creationDeletionLockFileDescriptor == -1)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ }
+
+ bool acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(s_creationDeletionLockFileDescriptor, LOCK_EX);
+ _ASSERTE(acquiredFileLock);
+#ifdef _DEBUG
+ s_creationDeletionFileLockOwnerThreadId = THREADSilentGetCurrentThreadId();
+#endif // _DEBUG
+}
+
+void SharedMemoryManager::ReleaseCreationDeletionFileLock()
+{
+ _ASSERTE(IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(IsCreationDeletionFileLockAcquired());
+ _ASSERTE(s_creationDeletionLockFileDescriptor != -1);
+
+#ifdef _DEBUG
+ s_creationDeletionFileLockOwnerThreadId = SharedMemoryHelpers::InvalidThreadId;
+#endif // _DEBUG
+ SharedMemoryHelpers::ReleaseFileLock(s_creationDeletionLockFileDescriptor);
+}
+
+#ifdef _DEBUG
+bool SharedMemoryManager::IsCreationDeletionProcessLockAcquired()
+{
+ return s_creationDeletionProcessLockOwnerThreadId == THREADSilentGetCurrentThreadId();
+}
+
+bool SharedMemoryManager::IsCreationDeletionFileLockAcquired()
+{
+ return s_creationDeletionFileLockOwnerThreadId == THREADSilentGetCurrentThreadId();
+}
+#endif // _DEBUG
+
+void SharedMemoryManager::AddProcessDataHeader(SharedMemoryProcessDataHeader *processDataHeader)
+{
+ _ASSERTE(processDataHeader != nullptr);
+ _ASSERTE(IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(processDataHeader->GetNextInProcessDataHeaderList() == nullptr);
+ _ASSERTE(FindProcessDataHeader(processDataHeader->GetId()) == nullptr);
+
+ processDataHeader->SetNextInProcessDataHeaderList(s_processDataHeaderListHead);
+ s_processDataHeaderListHead = processDataHeader;
+}
+
+void SharedMemoryManager::RemoveProcessDataHeader(SharedMemoryProcessDataHeader *processDataHeader)
+{
+ _ASSERTE(processDataHeader != nullptr);
+ _ASSERTE(IsCreationDeletionProcessLockAcquired());
+
+ if (s_processDataHeaderListHead == processDataHeader)
+ {
+ s_processDataHeaderListHead = processDataHeader->GetNextInProcessDataHeaderList();
+ processDataHeader->SetNextInProcessDataHeaderList(nullptr);
+ return;
+ }
+ for (SharedMemoryProcessDataHeader
+ *previous = s_processDataHeaderListHead,
+ *current = previous->GetNextInProcessDataHeaderList();
+ current != nullptr;
+ previous = current, current = current->GetNextInProcessDataHeaderList())
+ {
+ if (current == processDataHeader)
+ {
+ previous->SetNextInProcessDataHeaderList(current->GetNextInProcessDataHeaderList());
+ current->SetNextInProcessDataHeaderList(nullptr);
+ return;
+ }
+ }
+ _ASSERTE(false);
+}
+
+SharedMemoryProcessDataHeader *SharedMemoryManager::FindProcessDataHeader(SharedMemoryId *id)
+{
+ _ASSERTE(IsCreationDeletionProcessLockAcquired());
+
+ // TODO: Use a hash table
+ for (SharedMemoryProcessDataHeader *current = s_processDataHeaderListHead;
+ current != nullptr;
+ current = current->GetNextInProcessDataHeaderList())
+ {
+ if (current->GetId()->Equals(id))
+ {
+ return current;
+ }
+ }
+ return nullptr;
+}
diff --git a/src/pal/src/shmemory/shmemory.cpp b/src/pal/src/shmemory/shmemory.cpp
new file mode 100644
index 0000000000..42e06be834
--- /dev/null
+++ b/src/pal/src/shmemory/shmemory.cpp
@@ -0,0 +1,1734 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ shmemory/shmemory.c
+
+Abstract:
+
+ Implementation of shared memory infrastructure for IPC
+
+Issues :
+
+ Interprocess synchronization
+
+
+There doesn't seem to be ANY synchronization mechanism that will work
+inter-process AND be pthread-safe. FreeBSD's pthread implementation has no
+support for inter-process synchronization (PTHREAD_PROCESS_SHARED);
+"traditionnal" inter-process syncronization functions, on the other hand, are
+not pthread-aware, and thus will block entire processes instead of only the
+calling thread.
+
+From suggestions and information obtained on the freebsd-hackers mailing list,
+I have come up with 2 possible strategies to ensure serialized access to our
+shared memory region
+
+Note that the estimates of relative efficiency are wild guesses; my assumptions
+are that blocking entire processes is least efficient, busy wait somewhat
+better, and anything that does neither is preferable. However, the overhead of
+complex solutions is likely to have an important impact on performance
+
+Option 1 : very simple; possibly less efficient. in 2 words : "busy wait"
+Basically,
+
+while(InterlockedCompareExchange(spinlock_in_shared_memory, 1, 0)
+ sched_yield();
+
+In other words, if a value is 0, set it to 1; otherwise, try again until we
+succeed. use shed_yield to give the system a chance to schedule other threads
+while we wait. (once a thread succeeds at this, it does its work, then sets
+the value back to 0)
+One inconvenient : threads will not unblock in the order they are blocked;
+once a thread releases the mutex, whichever waiting thread is scheduled next
+will be unblocked. This is what is called the "thundering herd" problem, and in
+extreme cases, can lead to starvation
+Update : we'll set the spinlock to our PID instead of 1, that way we can find
+out if the lock is held by a dead process.
+
+Option 2 : possibly more efficient, much more complex, borders on
+"over-engineered". I'll explain it in stages, in the same way I deduced it.
+
+Option 2.1 : probably less efficient, reasonably simple. stop at step 2)
+
+1) The minimal, original idea was to use SysV semaphores for synchronization.
+This didn't work, because semaphores block the entire process, which can easily
+lead to deadlocks (thread 1 takes sem, thread 2 tries to take sem, blocks
+process, thread 1 is blocked and never releases sem)
+
+2) (this is option 2.1) Protect the use of the semaphores in critical sections.
+Enter the critical section before taking the semaphore, leave the section after
+releasing the semaphore. This ensures that 2 threads of the same process will
+never try to acquire the semaphore at the same time, which avoids deadlocks.
+However, the entire process still blocks if another process has the semaphore.
+Here, unblocking order should match blocking order (assuming the semaphores work
+properly); therefore, no risk of starvation.
+
+3) This is where it gets complicated. To avoid blocking whole processes, we
+can't use semaphores. One suggestion I got was to use multi-ended FIFOs, here's
+how it would work.
+
+-as in option 1, use InterlockedCompareExchange on a value in shared memory.
+-if this was not succesful (someone else has locked the shared memory), then :
+ -open a special FIFO for reading; try to read 1 byte. This will block until
+ someone writes to it, and *should* only block the current thread. (note :
+ more than one thread/process can open the same FIFO and block on read(),
+ in this case, only one gets woken up when someone writes to it.
+ *which* one is, again, not predictable; this may lead to starvation)
+ -once we are unblocked, we have the lock.
+-once we have the lock (either from Interlocked...() or from read()),
+ we can do our work
+-once the work is done, we open the FIFO for writing. this will fail if no one
+ is listening.
+-if no one is listening, release the lock by setting the shared memory value
+ back to 0
+-if someone is listening, write 1 byte to the FIFO to wake someone, then close
+ the FIFO. the value in shared memory will remain nonzero until a thread tries
+ to wake the next one and sees no one is listening.
+
+problem with this option : it is possible for a thread to call Interlocked...()
+BETWEEN the failed "open for write" attempt and the subsequent restoration of
+the SHM value back to zero. In this case, that thread will go to sleep and will
+not wake up until *another* thread asks for the lock, takes it and releases it.
+
+so to fix that, we come to step
+
+4) Instead of using InterlockedCompareExchange, use a SysV semaphore :
+-when taking the lock :
+ -take the semaphore
+ -try to take the lock (check if value is zero, change it to 1 if it is)
+ -if we fail : open FIFO for reading, release the semaphore, read() and block
+ -if we succeed : release the semaphore
+-when releasing the lock :
+ -take the semaphore
+ -open FIFO for write
+ -if we succeed, release semaphore, then write value
+ -if we fail, reset SHM value to 0, then release semaphore.
+
+Yes, using a SysV semaphore will block the whole process, but for a very short
+time (unlike option 2.1)
+problem with this : again, we get deadlocks if 2 threads from a single process
+try to take the semaphore. So like in option 2.1, we ave to wrap the semaphore
+usage in a critical section. (complex enough yet?)
+
+so the locking sequence becomes EnterCriticalSection - take semaphore - try to
+ lock - open FIFO - release semaphore - LeaveCriticalSection - read
+and the unlocking sequence becomes EnterCS - take sem - open FIFO - release
+ sem - LeaveCS - write
+
+Once again, the unblocking order probably won't match the blocking order.
+This could be fixed by using multiple FIFOs : waiting thread open their own
+personal FIFO, write the ID of their FIFO to another FIFO. The thread that wants
+to release the lock reads ID from that FIFO, determines which FIFO to open for
+writing and writes a byte to it. This way, whoever wrote its ID to the FIFO
+first will be first to awake. How's that for complexity?
+
+So to summarize, the options are
+1 - busy wait
+2.1 - semaphores + critical sections (whole process blocks)
+2 - semaphores + critical sections + FIFOs (minimal process blocking)
+2.2 - option 2 with multiple FIFOs (minimal process blocking, order preserved)
+
+Considering the overhead involved in options 2 & 2.2, it is our guess that
+option 1 may in fact be more efficient, and this is how we'll implement it for
+the moment. Note that other platforms may not present the same difficulties
+(i.e. other pthread implementations may support inter-process mutexes), and may
+be able to use a simpler, more efficient approach.
+
+B] Reliability.
+It is important for the shared memory implementation to be as foolproof as
+possible. Since more than one process will be able to modify the shared data,
+it becomes possible for one unstable process to destabilize the others. The
+simplest example is a process that dies while modifying shared memory : if
+it doesn't release its lock, we're in trouble. (this case will be taken care
+of by using PIDs in the spinlock; this we we can check if the locking process
+is still alive).
+
+
+
+--*/
+
+#include "config.h"
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/shmemory.h"
+#include "pal/critsect.h"
+#include "pal/shmemory.h"
+#include "pal/init.h"
+#include "pal/process.h"
+#include "pal/misc.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <sched.h>
+#include <pthread.h>
+
+#if HAVE_YIELD_SYSCALL
+#include <sys/syscall.h>
+#endif /* HAVE_YIELD_SYSCALL */
+
+SET_DEFAULT_DEBUG_CHANNEL(SHMEM);
+
+/* Macro-definitions **********************************************************/
+
+/* rounds 'val' up to be divisible by 'r'. 'r' must be a power of two. */
+#ifndef roundup
+#define roundup(val, r) ( ((val)+(r)-1) & ~( (r)-1 ) )
+#endif
+
+#define SEGMENT_NAME_SUFFIX_LENGTH 10
+
+#if defined(_DEBUG) && defined(_HPUX_)
+#define TRACK_SHMLOCK_OWNERSHIP
+#endif // _DEBUG && _HPUX_
+
+/*
+SHMPTR structure :
+High byte is SHM segment number
+Low bytes are offset in the segment
+ */
+#define SHMPTR_SEGMENT(shmptr) \
+ (((shmptr)>>24)&0xFF)
+
+#define SHMPTR_OFFSET(shmptr) \
+ ((shmptr)&0x00FFFFFF)
+
+#define MAKE_SHMPTR(segment,offset) \
+ ((SHMPTR)((((segment)&0xFF)<<24)|((offset)&0x00FFFFFF)))
+
+/*#define MAX_SEGMENTS 256*//*definition is now in shmemory.h*/
+
+/* Use MAP_NOSYNC to improve performance if it's available */
+#if defined(MAP_NOSYNC)
+#define MAPFLAGS MAP_NOSYNC|MAP_SHARED
+#else
+#define MAPFLAGS MAP_SHARED
+#endif
+
+
+/* Type definitions ***********************************************************/
+
+enum SHM_POOL_SIZES
+{
+ SPS_16 = 0, /* 16 bytes */
+ SPS_32, /* 32 bytes */
+ SPS_64, /* 64 bytes */
+ SPS_MAXPATHx2, /* 520 bytes, for long Unicode paths */
+
+ SPS_LAST
+};
+/* Block size associated to each SPS identifier */
+static const int block_sizes[SPS_LAST] = {16,32,64,roundup((MAX_LONGPATH+1)*2, sizeof(INT64))};
+
+/*
+SHM_POOL_INFO
+Description of a shared memory pool for a specific block size.
+
+Note on pool structure :
+first_free identifies the first available SHMPTR in the block. Free blocks are
+arranged in a linked list, each free block indicating the location of the next
+one. To walk the list, do something like this :
+SHMPTR *shmptr_ptr=(SHMPTR *)SHMPTR_TO_PTR(pool->first_free)
+while(shm_ptr)
+{
+ SHMPTR next = *shmptr_ptr;
+ shmptr_ptr = (SHMPTR *)SHMPTR_TO_PTR(next)
+}
+ */
+typedef struct
+{
+ int item_size; /* size of 1 block, in bytes */
+ int num_items; /* total number of blocks in the pool */
+ int free_items; /* number of unused items in the pool */
+ SHMPTR first_free; /* location of first available block in the pool */
+}SHM_POOL_INFO;
+
+/*
+SHM_SEGMENT_HEADER
+Description of a single shared memory segment
+
+Notes on segment names :
+next_semgent contains the string generated by mkstemp() when a new segment is
+generated. This allows processes to map segment files created by other
+processes. To get the file name of a segment file, concatenate
+"segment_name_prefix" and "next_segment".
+
+Notes on pool segments :
+Each segment is divided into one pool for each defined block size (SPS_*).
+These pools are linked with pools in other segment to form one large pool for
+each block size, so that SHMAlloc() doesn't have to search each segment to find
+an available block.
+the first_ and last_pool_blocks indicate the first and last block in a single
+segment for each block size. This allows SHMFree() to determine the size of a
+block by comparing its value with these boundaries. (note that within each
+segment, each pool is composed of a single contiguous block of memory)
+*/
+typedef struct
+{
+ Volatile<SHMPTR> first_pool_blocks[SPS_LAST];
+ Volatile<SHMPTR> last_pool_blocks[SPS_LAST];
+} SHM_SEGMENT_HEADER;
+
+/*
+SHM_FIRST_HEADER
+Global information about the shared memory system
+In addition to the standard SHM_SEGGMENT_HEADER, the first segment contains some
+information required to properly use the shared memory system.
+
+The spinlock is used to ensure that only one process accesses shared memory at
+the same time. A process can only take the spinlock if its contents is 0, and
+it takes the spinlock by placing its PID in it. (this allows a process to catch
+the special case where it tries to take a spinlock it already owns.
+
+The first_* members will contain the location of the first element in the
+various linked lists of shared information
+ */
+
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+
+#define SHMLOCK_OWNERSHIP_HISTORY_ARRAY_SIZE 5
+
+#define CHECK_CANARIES(header) \
+ _ASSERTE(HeadSignature == header->dwHeadCanaries[0]); \
+ _ASSERTE(HeadSignature == header->dwHeadCanaries[1]); \
+ _ASSERTE(TailSignature == header->dwTailCanaries[0]); \
+ _ASSERTE(TailSignature == header->dwTailCanaries[1])
+
+typedef struct _pid_and_tid
+{
+ Volatile<pid_t> pid;
+ Volatile<pthread_t> tid;
+} pid_and_tid;
+
+const DWORD HeadSignature = 0x48454144;
+const DWORD TailSignature = 0x5441494C;
+
+#endif // TRACK_SHMLOCK_OWNERSHIP
+
+typedef struct
+{
+ SHM_SEGMENT_HEADER header;
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+ Volatile<DWORD> dwHeadCanaries[2];
+#endif // TRACK_SHMLOCK_OWNERSHIP
+ Volatile<pid_t> spinlock;
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+ Volatile<DWORD> dwTailCanaries[2];
+ pid_and_tid pidtidCurrentOwner;
+ pid_and_tid pidtidOwners[SHMLOCK_OWNERSHIP_HISTORY_ARRAY_SIZE];
+ Volatile<ULONG> ulOwnersIdx;
+#endif // TRACK_SHMLOCK_OWNERSHIP
+ SHM_POOL_INFO pools[SPS_LAST]; /* information about each memory pool */
+ Volatile<SHMPTR> shm_info[SIID_LAST]; /* basic blocks of shared information.*/
+} SHM_FIRST_HEADER;
+
+
+/* Static variables ***********************************************************/
+
+/* Critical section to ensure that only one thread at a time accesses shared
+memory. Rationale :
+-Using a spinlock means that processes must busy-wait for the lock to be
+ available. The critical section ensures taht only one thread will busy-wait,
+ while the rest are put to sleep.
+-Since the spinlock only contains a PID, it isn't possible to make a difference
+ between threads of the same process. This could be resolved by using 2
+ spinlocks, but this would introduce more busy-wait.
+*/
+static CRITICAL_SECTION shm_critsec;
+
+/* number of segments the current process knows about */
+int shm_numsegments;
+
+/* array containing the base address of each segment */
+Volatile<LPVOID> shm_segment_bases[MAX_SEGMENTS];
+
+/* number of locks the process currently holds (SHMLock calls without matching
+SHMRelease). Because we take the critical section while inside a
+SHMLock/SHMRelease pair, this is actually the number of locks held by a single
+thread. */
+static Volatile<LONG> lock_count;
+
+/* thread ID of thread holding the SHM lock. used for debugging purposes :
+ SHMGet/SetInfo will verify that the calling thread holds the lock */
+static Volatile<HANDLE> locking_thread;
+
+/* Constants ******************************************************************/
+
+/* size of a single segment : 256KB */
+static const int segment_size = 0x40000;
+
+/* Static function prototypes *************************************************/
+
+static SHMPTR SHMInitPool(SHMPTR first, int block_size, int pool_size,
+ SHM_POOL_INFO *pool);
+static SHMPTR SHMLinkPool(SHMPTR first, int block_size, int num_blocks);
+static BOOL SHMMapUnknownSegments(void);
+static BOOL SHMAddSegment(void);
+
+
+#define init_waste()
+#define log_waste(x,y)
+#define save_waste()
+
+/* Public function implementations ********************************************/
+
+/*++
+SHMInitialize
+
+Hook this process into the PAL shared memory system; initialize the shared
+memory if no other process has done it.
+
+--*/
+BOOL SHMInitialize(void)
+{
+ InternalInitializeCriticalSection(&shm_critsec);
+
+ init_waste();
+
+ int size;
+ SHM_FIRST_HEADER *header;
+ SHMPTR pool_start;
+ SHMPTR pool_end;
+ enum SHM_POOL_SIZES sps;
+
+ TRACE("Now initializing global shared memory system\n");
+
+ // Not really shared in CoreCLR; we don't try to talk to other CoreCLRs.
+ shm_segment_bases[0] = mmap(NULL, segment_size,PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, 0);
+ if(shm_segment_bases[0] == MAP_FAILED)
+ {
+ ERROR("mmap() failed; error is %d (%s)\n", errno, strerror(errno));
+ return FALSE;
+ }
+ TRACE("Mapped first SHM segment at %p\n",shm_segment_bases[0].Load());
+
+ /* Initialize first segment's header */
+ header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
+
+ InterlockedExchange((LONG *)&header->spinlock, 0);
+
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+ header->dwHeadCanaries[0] = HeadSignature;
+ header->dwHeadCanaries[1] = HeadSignature;
+ header->dwTailCanaries[0] = TailSignature;
+ header->dwTailCanaries[1] = TailSignature;
+
+ // Check spinlock size
+ _ASSERTE(sizeof(DWORD) == sizeof(header->spinlock));
+ // Check spinlock alignment
+ _ASSERTE(0 == ((DWORD_PTR)&header->spinlock % (DWORD_PTR)sizeof(void *)));
+#endif // TRACK_SHMLOCK_OWNERSHIP
+
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+ header->pidtidCurrentOwner.pid = 0;
+ header->pidtidCurrentOwner.tid = 0;
+ memset((void *)header->pidtidOwners, 0, sizeof(header->pidtidOwners));
+ header->ulOwnersIdx = 0;
+#endif // TRACK_SHMLOCK_OWNERSHIP
+
+ /* SHM information array starts with NULLs */
+ memset((void *)header->shm_info, 0, SIID_LAST*sizeof(SHMPTR));
+
+ /* Initialize memory pools */
+
+ /* first pool starts right after header */
+ pool_start = roundup(sizeof(SHM_FIRST_HEADER), sizeof(INT64));
+
+ /* Same size for each pool, ensuring alignment is correct */
+ size = ((segment_size-pool_start)/SPS_LAST) & ~(sizeof(INT64)-1);
+
+ for (sps = static_cast<SHM_POOL_SIZES>(0); sps < SPS_LAST;
+ sps = static_cast<SHM_POOL_SIZES>(sps + 1))
+ {
+ pool_end = SHMInitPool(pool_start, block_sizes[sps], size,
+ (SHM_POOL_INFO *)&header->pools[sps]);
+
+ if(pool_end ==0)
+ {
+ ERROR("SHMInitPool failed.\n");
+ munmap(shm_segment_bases[0],segment_size);
+ return FALSE;
+ }
+ /* save first and last element of each pool for this segment */
+ header->header.first_pool_blocks[sps] = pool_start;
+ header->header.last_pool_blocks[sps] = pool_end;
+
+ /* next pool starts immediately after this one */
+ pool_start +=size;
+ }
+
+ TRACE("Global shared memory initialization complete.\n");
+
+ shm_numsegments = 1;
+ lock_count = 0;
+ locking_thread = 0;
+
+ /* hook into all SHM segments */
+ if(!SHMMapUnknownSegments())
+ {
+ ERROR("Error while mapping segments!\n");
+ SHMCleanup();
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*++
+SHMCleanup
+
+Release all shared memory resources held; remove ourselves from the list of
+registered processes, and remove all shared memory files if no process remains
+
+Note that this function does not use thread suspension wrapper for unlink and free
+because all thread objects are deleted before this function is called
+in PALCommonCleanup.
+
+--*/
+void SHMCleanup(void)
+{
+ SHM_FIRST_HEADER *header;
+ pid_t my_pid;
+
+ TRACE("Starting shared memory cleanup\n");
+
+ SHMLock();
+ SHMRelease();
+
+ /* We should not be holding the spinlock at this point. If we are, release
+ the spinlock. by setting it to 0 */
+ my_pid = gPID;
+ header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
+
+ _ASSERT_MSG(header->spinlock != my_pid,
+ "SHMCleanup called while the current process still owns the lock "
+ "[owner thread=%u, current thread: %u]\n",
+ locking_thread.Load(), THREADSilentGetCurrentThreadId());
+
+ /* Now for the interprocess stuff. */
+ DeleteCriticalSection(&shm_critsec);
+
+
+ /* Unmap memory segments */
+ while(shm_numsegments)
+ {
+ shm_numsegments--;
+ if ( -1 == munmap( shm_segment_bases[ shm_numsegments ],
+ segment_size ) )
+ {
+ ASSERT( "munmap() failed; errno is %d (%s).\n",
+ errno, strerror( errno ) );
+ }
+ }
+
+ save_waste();
+ TRACE("SHMCleanup complete!\n");
+}
+
+/*++
+SHMalloc
+
+Allocate a block of memory of the specified size
+
+Parameters :
+ size_t size : size of block required
+
+Return value :
+ A SHMPTR identifying the new block, or 0 on failure. Use SHMPTR_TO_PTR to
+ convert a SHMPTR into a useable pointer (but remember to lock the shared
+ memory first!)
+
+Notes :
+ SHMalloc will fail if the requested size is larger than a certain maximum.
+ At the moment, the maximum is 520 bytes (MAX_LONGPATH*2).
+--*/
+SHMPTR SHMalloc(size_t size)
+{
+ enum SHM_POOL_SIZES sps;
+ SHMPTR first_free;
+ SHMPTR next_free;
+ SHM_FIRST_HEADER *header;
+ SHMPTR *shmptr_ptr;
+
+ TRACE("SHMalloc() called; requested size is %u\n", size);
+
+ if(0 == size)
+ {
+ WARN("Got a request for a 0-byte block! returning 0\n");
+ return 0;
+ }
+
+ /* Find the first block size >= requested size */
+ for (sps = static_cast<SHM_POOL_SIZES>(0); sps < SPS_LAST;
+ sps = static_cast<SHM_POOL_SIZES>(sps + 1))
+ {
+ if (size <= static_cast<size_t>(block_sizes[sps]))
+ {
+ break;
+ }
+ }
+
+ /* If no block size is found, requested size was too large. */
+ if( SPS_LAST == sps )
+ {
+ ASSERT("Got request for shared memory block of %u bytes; maximum block "
+ "size is %d.\n", size, block_sizes[SPS_LAST-1]);
+ return 0;
+ }
+
+ TRACE("Best block size is %d (%d bytes wasted)\n",
+ block_sizes[sps], block_sizes[sps]-size );
+
+ log_waste(sps, block_sizes[sps]-size);
+
+ SHMLock();
+ header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
+
+ /* If there are no free items of the specified size left, it's time to
+ allocate a new shared memory segment.*/
+ if(header->pools[sps].free_items == 0)
+ {
+ TRACE("No blocks of %d bytes left; allocating new segment.\n",
+ block_sizes[sps]);
+ if(!SHMAddSegment())
+ {
+ ERROR("Unable to allocate new shared memory segment!\n");
+ SHMRelease();
+ return 0;
+ }
+ }
+
+ /* Remove the first free block from the pool */
+ first_free = header->pools[sps].first_free;
+ shmptr_ptr = static_cast<SHMPTR*>(SHMPTR_TO_PTR(first_free));
+
+ if( 0 == first_free )
+ {
+ ASSERT("First free block in %d-byte pool (%08x) was invalid!\n",
+ block_sizes[sps], first_free);
+ SHMRelease();
+ return 0;
+ }
+
+ /* the block "first_free" is the head of a linked list of free blocks;
+ take the next link in the list and set it as new head of list. */
+ next_free = *shmptr_ptr;
+ header->pools[sps].first_free = next_free;
+ header->pools[sps].free_items--;
+
+ /* make sure we're still in a sane state */
+ if(( 0 == header->pools[sps].free_items && 0 != next_free) ||
+ ( 0 != header->pools[sps].free_items && 0 == next_free))
+ {
+ ASSERT("free block count is %d, but next free block is %#x\n",
+ header->pools[sps].free_items, next_free);
+ /* assume all remaining blocks in the pool are corrupt */
+ header->pools[sps].first_free = 0;
+ header->pools[sps].free_items = 0;
+ }
+ else if (0 != next_free && 0 == SHMPTR_TO_PTR(next_free) )
+ {
+ ASSERT("Next free block (%#x) in %d-byte pool is invalid!\n",
+ next_free, block_sizes[sps]);
+ /* assume all remaining blocks in the pool are corrupt */
+ header->pools[sps].first_free = 0;
+ header->pools[sps].free_items = 0;
+ }
+
+ SHMRelease();
+
+ TRACE("Allocation successful; %d blocks of %d bytes left. Returning %08x\n",
+ header->pools[sps].free_items, block_sizes[sps], first_free);
+ return first_free;
+}
+
+/*++
+SHMfree
+
+Release a block of shared memory and put it back in the shared memory pool
+
+Parameters :
+ SHMPTR shmptr : identifier of block to release
+
+(no return value)
+--*/
+void SHMfree(SHMPTR shmptr)
+{
+ int segment;
+ int offset;
+ SHM_SEGMENT_HEADER *header;
+ SHM_FIRST_HEADER *first_header;
+ enum SHM_POOL_SIZES sps;
+ SHMPTR *shmptr_ptr;
+
+ if(0 == shmptr)
+ {
+ WARN("can't SHMfree() a NULL SHMPTR!\n");
+ return;
+ }
+ SHMLock();
+
+ TRACE("Releasing SHMPTR 0x%08x\n", shmptr);
+
+ shmptr_ptr = static_cast<SHMPTR*>(SHMPTR_TO_PTR(shmptr));
+
+ if(!shmptr_ptr)
+ {
+ ASSERT("Tried to free an invalid shared memory pointer 0x%08x\n", shmptr);
+ SHMRelease();
+ return;
+ }
+
+ /* note : SHMPTR_TO_PTR has already validated the segment/offset pair */
+ segment = SHMPTR_SEGMENT(shmptr);
+ header = (SHM_SEGMENT_HEADER *)shm_segment_bases[segment].Load();
+
+ /* Find out the size of this block. Each segment tells where are its first
+ and last blocks for each block size, so we simply need to check in which
+ interval the block fits */
+ for (sps = static_cast<SHM_POOL_SIZES>(0); sps < SPS_LAST;
+ sps = static_cast<SHM_POOL_SIZES>(sps + 1))
+ {
+ if(header->first_pool_blocks[sps]<=shmptr &&
+ header->last_pool_blocks[sps]>=shmptr)
+ {
+ break;
+ }
+ }
+
+ /* If we didn't find an interval, then the block doesn't really belong in
+ this segment (shouldn't happen, the offset check in SHMPTR_TO_PTR should
+ have caught this.) */
+ if(sps == SPS_LAST)
+ {
+ ASSERT("Shared memory pointer 0x%08x is out of bounds!\n", shmptr);
+ SHMRelease();
+ return;
+ }
+
+ TRACE("SHMPTR 0x%08x is a %d-byte block located in segment %d\n",
+ shmptr, block_sizes[sps], segment);
+
+ /* Determine the offset of this block (in bytes) relative to the first
+ block of the same size in this segment */
+ offset = shmptr - header->first_pool_blocks[sps];
+
+ /* Make sure that the offset is a multiple of the block size; otherwise,
+ this isn't a real SHMPTR */
+ if( 0 != ( offset % block_sizes[sps] ) )
+ {
+ ASSERT("Shared memory pointer 0x%08x is misaligned!\n", shmptr);
+ SHMRelease();
+ return;
+ }
+
+ /* Put the SHMPTR back in its pool. */
+ first_header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
+
+ /* first_free is the head of a linked list of free SHMPTRs. All we need to
+ do is make shmptr point to first_free, and set shmptr as the new head
+ of the list. */
+ *shmptr_ptr = first_header->pools[sps].first_free;
+ first_header->pools[sps].first_free = shmptr;
+ first_header->pools[sps].free_items++;
+
+ TRACE("SHMPTR 0x%08x released; there are now %d blocks of %d bytes "
+ "available\n", shmptr, first_header->pools[sps].free_items,
+ block_sizes[sps]);
+ SHMRelease();
+}
+
+/*++
+SHMLock
+
+Restrict shared memory access to the current thread of the current process
+
+(no parameters)
+
+Return value :
+ New lock count
+
+Notes :
+see comments at the declaration of shm_critsec for rationale of critical
+section usage
+--*/
+int SHMLock(void)
+{
+ /* Hold the critical section until the lock is released */
+ PALCEnterCriticalSection(&shm_critsec);
+
+ _ASSERTE((0 == lock_count && 0 == locking_thread) ||
+ (0 < lock_count && (HANDLE)pthread_self() == locking_thread));
+
+ if(lock_count == 0)
+ {
+ SHM_FIRST_HEADER *header;
+ pid_t my_pid, tmp_pid;
+ int spincount = 1;
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+ ULONG ulIdx;
+#endif // TRACK_SHMLOCK_OWNERSHIP
+
+ TRACE("First-level SHM lock : taking spinlock\n");
+
+ header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
+
+ // Store the id of the current thread as the (only) one that is
+ // trying to grab the spinlock from the current process
+ locking_thread = (HANDLE)pthread_self();
+
+ my_pid = gPID;
+
+ while(TRUE)
+ {
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+ _ASSERTE(0 != my_pid);
+ _ASSERTE(getpid() == my_pid);
+ _ASSERTE(my_pid != header->spinlock);
+ CHECK_CANARIES(header);
+#endif // TRACK_SHMLOCK_OWNERSHIP
+
+ //
+ // Try to grab the spinlock
+ //
+ tmp_pid = InterlockedCompareExchange((LONG *) &header->spinlock, my_pid,0);
+
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+ CHECK_CANARIES(header);
+#endif // TRACK_SHMLOCK_OWNERSHIP
+
+#ifdef _HPUX_
+ //
+ // TODO: workaround for VSW # 381564
+ //
+ if (0 == tmp_pid && my_pid != header->spinlock)
+ {
+ ERROR("InterlockedCompareExchange returned the Comperand but "
+ "failed to store the Exchange value to the Destination: "
+ "looping again [my_pid=%u header->spinlock=%u tmp_pid=%u "
+ "spincount=%d locking_thread=%u]\n", (DWORD)my_pid,
+ (DWORD)header->spinlock, (DWORD)tmp_pid, (int)spincount,
+ (HANDLE)locking_thread);
+
+ // Keep looping
+ tmp_pid = 42;
+ }
+#endif // _HPUX_
+
+ if (0 == tmp_pid)
+ {
+ // Spinlock acquired: break out of the loop
+ break;
+ }
+
+ /* Check if lock holder is alive. If it isn't, we can reset the
+ spinlock and try to take it again. If it is, we have to wait.
+ We use "spincount" to do this check only every 8th time through
+ the loop, for performance reasons.*/
+ if( (0 == (spincount&0x7)) &&
+ (-1 == kill(tmp_pid,0)) &&
+ (errno == ESRCH))
+ {
+ TRACE("SHM spinlock owner (%08x) is dead; releasing its lock\n",
+ tmp_pid);
+
+ InterlockedCompareExchange((LONG *) &header->spinlock, 0, tmp_pid);
+ }
+ else
+ {
+ /* another process is holding the lock... we want to yield and
+ give the holder a chance to release the lock
+ The function sched_yield() only yields to a thread in the
+ current process; this doesn't help us much, anddoens't help
+ at all if there's only 1 thread. There doesn't seem to be
+ any clean way to force a yield to another process, but the
+ FreeBSD syscall "yield" does the job. We alternate between
+ both methods to give other threads of this process a chance
+ to run while we wait.
+ */
+#if HAVE_YIELD_SYSCALL
+ if(spincount&1)
+ {
+#endif /* HAVE_YIELD_SYSCALL */
+ sched_yield();
+#if HAVE_YIELD_SYSCALL
+ }
+ else
+ {
+ /* use the syscall first, since we know we'l need to yield
+ to another process eventually - the lock can't be held
+ by the current process, thanks to the critical section */
+ syscall(SYS_yield, 0);
+ }
+#endif /* HAVE_YIELD_SYSCALL */
+ }
+
+ // Increment spincount
+ spincount++;
+ }
+
+ _ASSERT_MSG(my_pid == header->spinlock,
+ "\n(my_pid = %u) != (header->spinlock = %u)\n"
+ "tmp_pid = %u\n"
+ "spincount = %d\n"
+ "locking_thread = %u\n",
+ (DWORD)my_pid, (DWORD)header->spinlock,
+ (DWORD)tmp_pid,
+ (int)spincount,
+ (HANDLE)locking_thread);
+
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+ _ASSERTE(0 == header->pidtidCurrentOwner.pid);
+ _ASSERTE(0 == header->pidtidCurrentOwner.tid);
+
+ header->pidtidCurrentOwner.pid = my_pid;
+ header->pidtidCurrentOwner.tid = locking_thread;
+
+ ulIdx = header->ulOwnersIdx % (sizeof(header->pidtidOwners) / sizeof(header->pidtidOwners[0]));
+
+ header->pidtidOwners[ulIdx].pid = my_pid;
+ header->pidtidOwners[ulIdx].tid = locking_thread;
+
+ header->ulOwnersIdx += 1;
+#endif // TRACK_SHMLOCK_OWNERSHIP
+
+ }
+
+ lock_count++;
+ TRACE("SHM lock level is now %d\n", lock_count.Load());
+ return lock_count;
+}
+
+/*++
+SHMRelease
+
+Release a lock on shared memory taken with SHMLock.
+
+(no parameters)
+
+Return value :
+ New lock count
+
+--*/
+int SHMRelease(void)
+{
+ /* prevent a thread from releasing another thread's lock */
+ PALCEnterCriticalSection(&shm_critsec);
+
+ if(lock_count==0)
+ {
+ ASSERT("SHMRelease called without matching SHMLock!\n");
+ PALCLeaveCriticalSection(&shm_critsec);
+ return 0;
+ }
+
+ lock_count--;
+
+ _ASSERTE(lock_count >= 0);
+
+ /* If lock count is 0, this call matches the first Lock call; it's time to
+ set the spinlock back to 0. */
+ if(lock_count == 0)
+ {
+ SHM_FIRST_HEADER *header;
+ pid_t my_pid, tmp_pid;
+
+ TRACE("Releasing first-level SHM lock : resetting spinlock\n");
+
+ my_pid = gPID;
+
+ header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
+
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+ CHECK_CANARIES(header);
+ _ASSERTE(0 != my_pid);
+ _ASSERTE(getpid() == my_pid);
+ _ASSERTE(my_pid == header->spinlock);
+ _ASSERTE(header->pidtidCurrentOwner.pid == my_pid);
+ _ASSERTE(pthread_self() == header->pidtidCurrentOwner.tid);
+ _ASSERTE((pthread_t)locking_thread == header->pidtidCurrentOwner.tid);
+
+ header->pidtidCurrentOwner.pid = 0;
+ header->pidtidCurrentOwner.tid = 0;
+#endif // TRACK_SHMLOCK_OWNERSHIP
+
+
+#ifdef _HPUX_
+ //
+ // TODO: workaround for VSW # 381564
+ //
+ do
+#endif // _HPUX_
+ {
+ /* Make sure we don't touch the spinlock if we don't own it. We're
+ supposed to own it if we get here, but just in case... */
+ tmp_pid = InterlockedCompareExchange((LONG *) &header->spinlock, 0, my_pid);
+
+ if (tmp_pid != my_pid)
+ {
+ ASSERT("Process 0x%08x tried to release spinlock owned by process "
+ "0x%08x! \n", my_pid, tmp_pid);
+ PALCLeaveCriticalSection(&shm_critsec);
+ return 0;
+ }
+ }
+#ifdef _HPUX_
+ //
+ // TODO: workaround for VSW # 381564
+ //
+ while (my_pid == header->spinlock);
+#endif // _HPUX_
+
+ /* indicate no thread (in this process) holds the SHM lock */
+ locking_thread = 0;
+
+#ifdef TRACK_SHMLOCK_OWNERSHIP
+ CHECK_CANARIES(header);
+#endif // TRACK_SHMLOCK_OWNERSHIP
+ }
+
+ TRACE("SHM lock level is now %d\n", lock_count.Load());
+
+ /* This matches the EnterCriticalSection from SHMRelease */
+ PALCLeaveCriticalSection(&shm_critsec);
+
+ /* This matches the EnterCriticalSection from SHMLock */
+ PALCLeaveCriticalSection(&shm_critsec);
+
+ return lock_count;
+}
+
+/*++
+SHMPtrToPtr
+
+Convert a SHMPTR value to a valid pointer within the address space of the
+current process
+
+Parameters :
+ SHMPTR shmptr : SHMPTR value to convert into a pointer
+
+Return value :
+ Address corresponding to the given SHMPTR, valid for the current process
+
+Notes :
+(see notes for SHMPTR_SEGMENT macro for details on SHMPTR structure)
+
+It is possible for the segment index to be greater than the known total number
+of segments (shm_numsegments); this means that the SHMPTR points to a memory
+block in a shared memory segment this process doesn't know about. In this case,
+we must obtain an address for that new segment and add it to our array
+(see SHMMapUnknownSegments for details)
+
+In the simplest case (no need to map new segments), there is no need to hold
+the lock, since we don't access any information that can change
+--*/
+LPVOID SHMPtrToPtr(SHMPTR shmptr)
+{
+ void *retval;
+ int segment;
+ int offset;
+
+ TRACE("Converting SHMPTR 0x%08x to a valid pointer...\n", shmptr);
+ if(!shmptr)
+ {
+ WARN("Got SHMPTR \"0\"; returning NULL pointer\n");
+ return NULL;
+ }
+
+ segment = SHMPTR_SEGMENT(shmptr);
+
+ /* If segment isn't known, it may have been added by another process. We
+ need to map all new segments into our address space. */
+ if(segment>= shm_numsegments)
+ {
+ TRACE("SHMPTR is in segment %d, we know only %d. We must now map all "
+ "unknowns.\n", segment, shm_numsegments);
+ SHMMapUnknownSegments();
+
+ /* if segment is still unknown, then it doesn't exist */
+ if(segment>=shm_numsegments)
+ {
+ ASSERT("Segment %d still unknown; returning NULL\n", segment);
+ return NULL;
+ }
+ TRACE("Segment %d found; continuing\n", segment);
+ }
+
+ /* Make sure the offset doesn't point outside the segment */
+ offset = SHMPTR_OFFSET(shmptr);
+ if(offset>=segment_size)
+ {
+ ASSERT("Offset %d is larger than segment size (%d)! returning NULL\n",
+ offset, segment_size);
+ return NULL;
+
+ }
+
+ /* Make sure the offset doesn't point in the segment's header */
+ if(segment == 0)
+ {
+ if (static_cast<size_t>(offset) < roundup(sizeof(SHM_FIRST_HEADER), sizeof(INT64)))
+ {
+ ASSERT("Offset %d is in segment header! returning NULL\n", offset);
+ return NULL;
+ }
+ }
+ else
+ {
+ if (static_cast<size_t>(offset) < sizeof(SHM_SEGMENT_HEADER))
+ {
+ ASSERT("Offset %d is in segment header! returning NULL\n", offset);
+ return NULL;
+ }
+ }
+
+ retval = shm_segment_bases[segment];
+ retval = static_cast<BYTE*>(retval) + offset;
+
+ TRACE("SHMPTR %#x is at offset %d in segment %d; maps to address %p\n",
+ shmptr, offset, segment, retval);
+ return retval;
+}
+
+
+/*++
+Function :
+ SHMGetInfo
+
+ Retrieve some information from shared memory
+
+Parameters :
+ SHM_INFO_ID element : identifier of element to retrieve
+
+Return value :
+ Value of specified element
+
+Notes :
+ The SHM lock should be held while manipulating shared memory
+--*/
+SHMPTR SHMGetInfo(SHM_INFO_ID element)
+{
+ SHM_FIRST_HEADER *header = NULL;
+ SHMPTR retval = 0;
+
+ if(element < 0 || element >= SIID_LAST)
+ {
+ ASSERT("Invalid SHM info element %d\n", element);
+ return 0;
+ }
+
+ /* verify that this thread holds the SHM lock. No race condition: if the
+ current thread is here, it can't be in SHMLock or SHMUnlock */
+ if( (HANDLE)pthread_self() != locking_thread )
+ {
+ ASSERT("SHMGetInfo called while thread does not hold the SHM lock!\n");
+ }
+
+ header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
+
+ retval = header->shm_info[element];
+
+ TRACE("SHM info element %d is %08x\n", element, retval );
+ return retval;
+}
+
+
+/*++
+Function :
+ SHMSetInfo
+
+ Place some information into shared memory
+
+Parameters :
+ SHM_INFO_ID element : identifier of element to save
+ SHMPTR value : new value of element
+
+Return value :
+ TRUE if successfull, FALSE otherwise.
+
+Notes :
+ The SHM lock should be held while manipulating shared memory
+--*/
+BOOL SHMSetInfo(SHM_INFO_ID element, SHMPTR value)
+{
+ SHM_FIRST_HEADER *header;
+
+ if(element < 0 || element >= SIID_LAST)
+ {
+ ASSERT("Invalid SHM info element %d\n", element);
+ return FALSE;
+ }
+
+ /* verify that this thread holds the SHM lock. No race condition: if the
+ current thread is here, it can't be in SHMLock or SHMUnlock */
+ if( (HANDLE)pthread_self() != locking_thread )
+ {
+ ASSERT("SHMGetInfo called while thread does not hold the SHM lock!\n");
+ }
+
+ header = (SHM_FIRST_HEADER*)shm_segment_bases[0].Load();
+
+ TRACE("Setting SHM info element %d to %08x; used to be %08x\n",
+ element, value, header->shm_info[element].Load() );
+
+ header->shm_info[element] = value;
+
+ return TRUE;
+}
+
+
+/* Static function implementations ********************************************/
+
+/*++
+SHMInitPool
+
+Perform one-time initialization for a shared memory pool.
+
+Parameters :
+ SHMPTR first : SHMPTR of first memory block in the pool
+ int block_size : size (in bytes) of a memory block in this pool
+ int pool_size : total size (in bytes) of this pool
+ SHM_POOL_INFO *pool : pointer to initialize with information about the pool
+
+Return value :
+ SHMPTR of last memory block in the pool
+
+Notes :
+This function is used to initialize the memory pools of the first SHM segment.
+In addition to creating a linked list of SHMPTRs, it initializes the given
+SHM_POOL_INFO based on the given information.
+--*/
+static SHMPTR SHMInitPool(SHMPTR first, int block_size, int pool_size,
+ SHM_POOL_INFO *pool)
+{
+ int num_blocks;
+ SHMPTR last;
+
+ TRACE("Initializing SHM pool for %d-byte blocks\n", block_size);
+
+ /* Number of memory blocks of size "block_size" that can fit in "pool_size"
+ bytes (rounded down) */
+ num_blocks = pool_size/block_size;
+
+ /* Create the initial linked list of free blocks */
+ last = SHMLinkPool(first, block_size, num_blocks);
+ if( 0 == last )
+ {
+ ERROR("Failed to create linked list of free blocks!\n");
+ return 0;
+ }
+
+ /* Initialize SHM_POOL_INFO */
+ pool->first_free = first;
+ pool->free_items = num_blocks;
+ pool->item_size = block_size;
+ pool->num_items = num_blocks;
+
+ TRACE("New SHM pool extends from SHMPTR 0x%08x to 0x%08x\n", first, last);
+ return last;
+}
+
+/*++
+SHMLinkPool
+
+Joins contiguous blocks of memory into a linked list..
+
+Parameters :
+ SHMPTR first : First SHMPTR in the memory pool; first link in the list
+ int block_size : size (in bytes) of the memory blocks
+ int num_blocks : number of contiguous blocks to link
+
+Return value :
+ SHMPTR of last memory block in the pool
+
+Notes :
+The linked list is created by saving the value of the next SHMPTR in the list
+in the memory location corresponding to the previous SHMPTR :
+*(SHMPTR *)SHMPTR_TO_PTR(previous) = previous + block_size
+--*/
+static SHMPTR SHMLinkPool(SHMPTR first, int block_size, int num_blocks)
+{
+ LPBYTE item_ptr;
+ SHMPTR *shmptr_ptr;
+ SHMPTR next_shmptr;
+ int i;
+
+ TRACE("Linking %d blocks of %d bytes, starting at 0x%08x\n",
+ num_blocks, block_size, first);
+
+ item_ptr = static_cast<LPBYTE>(
+ static_cast<LPBYTE>(shm_segment_bases[SHMPTR_SEGMENT(first)].Load()) +
+ (SHMPTR_OFFSET(first)));
+ next_shmptr = first/*+block_size*/;
+
+ /* Link blocks together */
+ for(i=0; i<num_blocks; i++)
+ {
+ next_shmptr += block_size;
+
+ /* item_ptr is char * (so we can increment with +=blocksize), we cast
+ it to a SHMPTR * and set its content to the next SHMPTR in the list*/
+ shmptr_ptr = (SHMPTR *)item_ptr;
+ *shmptr_ptr = next_shmptr;
+
+ item_ptr+=block_size;
+ }
+ /* Last SHMPTR in the list must point to NULL */
+ item_ptr-=block_size;
+ shmptr_ptr = (SHMPTR *)item_ptr;
+ *shmptr_ptr = 0;
+
+ /* Return SHMPTR of last element in the list */
+ next_shmptr -= block_size;
+
+ TRACE("New linked pool goes from 0x%08x to 0x%08x\n", first, next_shmptr);
+ return next_shmptr;
+}
+
+/*++
+SHMMapUnknownSegments
+
+Map into this process all SHM segments not yet mapped
+
+(no parameters)
+
+Return value :
+ TRUE on success, FALSE in case of error
+--*/
+static BOOL SHMMapUnknownSegments(void)
+{
+ return TRUE;
+}
+
+/*++
+SHMAddSegment
+
+Create a new SHM segment, map it into this process, initialize it, then link it
+to the other SHM segments
+
+(no parameters)
+
+Return value :
+ TRUE on success, FALSE in case of error
+
+Notes :
+ This function assumes the SHM lock is held.
+--*/
+static BOOL SHMAddSegment(void)
+{
+ LPVOID segment_base;
+ SHM_SEGMENT_HEADER *header;
+ SHM_FIRST_HEADER *first_header;
+ SHMPTR first_shmptr;
+ SHMPTR *shmptr_ptr;
+ int sps;
+ int used_size;
+ int new_size;
+ int current_pool_size;
+ int used_pool_size;
+ int new_pool_size;
+ int num_new_items;
+
+ /* Map all segments this process doesn't yet know about, so we link the new
+ segment at the right place */
+ if(!SHMMapUnknownSegments())
+ {
+ ERROR("SHMMapUnknownSegments failed!\n");
+ return FALSE;
+ }
+
+ /* Avoid overflowing */
+ if(shm_numsegments == MAX_SEGMENTS)
+ {
+ ERROR("Can't map more segments : maximum number (%d) reached!\n",
+ MAX_SEGMENTS);
+ return FALSE;
+ }
+
+ TRACE("Creating SHM segment #%d\n", shm_numsegments);
+
+ segment_base = mmap(NULL, segment_size, PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE,-1, 0);
+
+ if(segment_base == MAP_FAILED)
+ {
+ ERROR("mmap() failed! error is %d (%s)\n", errno, strerror(errno));
+ return FALSE;
+ }
+
+ shm_segment_bases[shm_numsegments] = segment_base;
+
+ /* Save name (well, suffix) of new segment in the header of the old last
+ segment, so that other processes know where it is. */
+ header = (SHM_SEGMENT_HEADER *)shm_segment_bases[shm_numsegments-1].Load();
+
+ /* Indicate that the new segment is the last one */
+ header = (SHM_SEGMENT_HEADER *)segment_base;
+
+ /* We're now ready to update our memory pools */
+
+ first_header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
+
+ /* Calculate total amount of used memory (in bytes) */
+ used_size = 0;
+ for(sps = 0; sps<SPS_LAST;sps++)
+ {
+ /* Add total size of this pool */
+ used_size += first_header->pools[sps].num_items*block_sizes[sps];
+
+ /* Remove unused size of this pool */
+ used_size -= first_header->pools[sps].free_items*block_sizes[sps];
+ }
+
+ /* Determine how to divide the new segment between the pools for the
+ different block sizes, then update the pool inforamtion accordingly
+ Allocation strategy :
+ 1) Calculate the proportion of used memory used by each pool
+ 2) Allocate this proportion of the new segment to each pool
+ */
+
+ /* Add the new segment to the total amount of SHM memory */
+ new_size = segment_size-roundup(sizeof(SHM_SEGMENT_HEADER), sizeof(INT64));
+
+ /* Calculate value of first SHMPTR in the new segment : segment is
+ shm_numsegments (not yet incremented); offset is the first byte after
+ the segment header */
+ first_shmptr = MAKE_SHMPTR(shm_numsegments,roundup(sizeof(SHM_SEGMENT_HEADER), sizeof(INT64)));
+
+ TRACE("Updating SHM pool information; Total memory used is %d bytes; "
+ "we are adding %d bytes\n", used_size, new_size);
+
+ /* We want to allocate at least 1 block of each size (to avoid adding
+ special cases everywhere). We remove the required space for these blocks
+ from the size used in the calculations, then add 1 to each block count */
+ for(sps=0;sps<SPS_LAST;sps++)
+ new_size -= block_sizes[sps];
+
+ /* Loop through all block sizes */
+ for(sps=0; sps<SPS_LAST; sps++)
+ {
+ TRACE("Now processing block size \"%d\"...\n", block_sizes[sps]);
+ /* amount of memory currently reserved for this block size */
+ current_pool_size = first_header->pools[sps].num_items*block_sizes[sps];
+
+ /* how much of that is actually used? */
+ used_pool_size = current_pool_size -
+ first_header->pools[sps].free_items*block_sizes[sps];
+
+ DBGOUT("%d bytes of %d bytes used (%d%%)\n", used_pool_size,
+ current_pool_size, (used_pool_size*100)/current_pool_size);
+
+ /* amount of memory we want to add to the pool for this block size :
+ amount used by this pool/total amount used * new segment's size */
+ new_pool_size = (((LONGLONG)used_pool_size)*new_size)/used_size;
+
+ DBGOUT("Allocating %d bytes of %d to %d-byte pool\n",
+ new_pool_size, new_size, block_sizes[sps]);
+
+ /* determine the number of blocks that can fit in the chosen amount */
+ num_new_items = new_pool_size/block_sizes[sps];
+
+ /* make sure we allocate at least 1 block of each size */
+ num_new_items +=1;
+
+ DBGOUT("Adding %d new blocks\n", num_new_items);
+
+ /* Save the first and last block of the current block size in the new
+ segment; join all blocks in between in a linked list */
+ header->first_pool_blocks[sps] = first_shmptr;
+ header->last_pool_blocks[sps] = SHMLinkPool(first_shmptr,
+ block_sizes[sps],
+ num_new_items);
+
+ /* Link the last block in the new linked list to the first block of the
+ old global linked list. We don't use SHMPTR_TO_PTR because the pool
+ data isn't updated yet */
+ shmptr_ptr = reinterpret_cast<SHMPTR*>(
+ static_cast<LPBYTE>(shm_segment_bases[SHMPTR_SEGMENT(header->last_pool_blocks[sps])].Load()) +
+ SHMPTR_OFFSET(header->last_pool_blocks[sps]));
+
+ *shmptr_ptr = first_header->pools[sps].first_free;
+
+ /* Save the first block of the new linked list as the new beginning of
+ the global linked list; the global list now contains all new blocks
+ AND all blocks that were already free */
+ first_header->pools[sps].first_free = header->first_pool_blocks[sps];
+
+ /* Update block counts to include new blocks */
+ first_header->pools[sps].free_items+=num_new_items;
+ first_header->pools[sps].num_items+=num_new_items;
+
+ DBGOUT("There are now %d %d-byte blocks, %d are free\n",
+ first_header->pools[sps].num_items, block_sizes[sps],
+ first_header->pools[sps].free_items);
+
+ /* Update first_shmptr to first byte after the new pool */
+ first_shmptr+=num_new_items*block_sizes[sps];
+ }
+ shm_numsegments++;
+
+ return TRUE;
+}
+
+/*++
+SHMStrDup
+
+Duplicates the string in shared memory.
+
+Returns the new address as SHMPTR on success.
+Returns (SHMPTR)NULL on failure.
+--*/
+SHMPTR SHMStrDup( LPCSTR string )
+{
+ UINT length = 0;
+ SHMPTR retVal = 0;
+
+ if ( string )
+ {
+ length = strlen( string );
+
+ retVal = SHMalloc( ++length );
+
+ if ( retVal != 0 )
+ {
+ LPVOID ptr = SHMPTR_TO_PTR( retVal );
+ _ASSERT_MSG(ptr != NULL, "SHMPTR_TO_PTR returned NULL.\n");
+ if (ptr != NULL)
+ {
+ memcpy( ptr, string, length );
+ }
+ else
+ {
+ // This code should never be reached. If a valid pointer
+ // is passed to SHMPTR_TO_PTR and NULL is returned, then
+ // there's a problem in either the macro, or the underlying
+ // call to SHMPtrToPtr. In case the impossible happens,
+ // though, free the memory and return NULL rather than
+ // returning uninitialized memory.
+ SHMfree( retVal );
+ retVal = NULL;
+ }
+ }
+ }
+ return retVal;
+}
+
+/*++
+SHMWStrDup
+
+Duplicates the wide string in shared memory.
+
+Returns the new address as SHMPTR on success.
+Returns (SHMPTR)NULL on failure.
+--*/
+SHMPTR SHMWStrDup( LPCWSTR string )
+{
+ UINT length = 0;
+ SHMPTR retVal = 0;
+
+ if ( string )
+ {
+ length = ( PAL_wcslen( string ) + 1 ) * sizeof( WCHAR );
+
+ retVal = SHMalloc( length );
+
+ if ( retVal != 0 )
+ {
+ LPVOID ptr = SHMPTR_TO_PTR(retVal);
+ _ASSERT_MSG(ptr != NULL, "SHMPTR_TO_PTR returned NULL.\n");
+ if (ptr != NULL)
+ {
+ memcpy( ptr, string, length );
+ }
+ else
+ {
+ // This code should never be reached. If a valid pointer
+ // is passed to SHMPTR_TO_PTR and NULL is returned, then
+ // there's a problem in either the macro, or the underlying
+ // call to SHMPtrToPtr. In case the impossible happens,
+ // though, free the memory and return NULL rather than
+ // returning uninitialized memory.
+ SHMfree( retVal );
+ retVal = NULL;
+ }
+ }
+ }
+ return retVal;
+}
+
+
+
+/*++
+SHMFindNamedObjectByName
+
+Searches for an object whose name matches the name and ID passed in.
+
+Returns a SHMPTR to its location in shared memory. If no object
+matches the name, the function returns NULL and sets pbNameExists to FALSE.
+If an object matches the name but is of a different type, the function
+returns NULL and sets pbNameExists to TRUE.
+
+--*/
+SHMPTR SHMFindNamedObjectByName( LPCWSTR lpName, SHM_NAMED_OBJECTS_ID oid,
+ BOOL *pbNameExists )
+{
+ PSHM_NAMED_OBJECTS pNamedObject = NULL;
+ SHMPTR shmNamedObject = 0;
+ LPWSTR object_name = NULL;
+
+ if(oid==SHM_NAMED_LAST)
+ {
+ ASSERT("Invalid named object type.\n");
+ return 0;
+ }
+
+ if (pbNameExists == NULL)
+ {
+ ASSERT("pbNameExists must be non-NULL.\n");
+ }
+
+ SHMLock();
+
+ *pbNameExists = FALSE;
+ shmNamedObject = SHMGetInfo( SIID_NAMED_OBJECTS );
+
+ TRACE( "Entering SHMFindNamedObjectByName looking for %S .\n",
+ lpName?lpName:W16_NULLSTRING );
+
+ while ( shmNamedObject )
+ {
+ pNamedObject = (PSHM_NAMED_OBJECTS)SHMPTR_TO_PTR( shmNamedObject );
+ if(NULL == pNamedObject)
+ {
+ ASSERT("Got invalid SHMPTR value; list of named objects is "
+ "corrupted.\n");
+ break;
+ }
+
+ if ( pNamedObject->ShmObjectName )
+ {
+ object_name = (LPWSTR)SHMPTR_TO_PTR( pNamedObject->ShmObjectName );
+ }
+
+ if ( object_name &&
+ PAL_wcscmp( lpName, object_name ) == 0 )
+ {
+ if(oid == pNamedObject->ObjectType)
+ {
+ TRACE( "Returning the kernel object %p.\n", pNamedObject );
+ }
+ else
+ {
+ shmNamedObject = 0;
+ *pbNameExists = TRUE;
+ }
+ goto Exit;
+ }
+ shmNamedObject = pNamedObject->ShmNext;
+ }
+
+ shmNamedObject = 0;
+ TRACE( "No matching kernel object was found.\n" );
+
+Exit:
+ SHMRelease();
+ return shmNamedObject;
+
+}
+
+/*++
+SHMRemoveNamedObject
+
+Removes the specified named object from the list
+
+No return.
+
+note : the caller is reponsible for releasing all associated memory
+--*/
+void SHMRemoveNamedObject( SHMPTR shmNamedObject )
+{
+ PSHM_NAMED_OBJECTS pshmLast = 0;
+ PSHM_NAMED_OBJECTS pshmCurrent = 0;
+
+ TRACE( "Entered SHMDeleteNamedObject shmNamedObject = %d\n", shmNamedObject );
+ SHMLock();
+
+ pshmCurrent =
+ (PSHM_NAMED_OBJECTS)SHMPTR_TO_PTR( SHMGetInfo( SIID_NAMED_OBJECTS ) );
+ pshmLast = pshmCurrent;
+
+ while ( pshmCurrent )
+ {
+ if ( pshmCurrent->ShmSelf == shmNamedObject )
+ {
+ TRACE( "Patching the list.\n" );
+
+ /* Patch the list, and delete the object. */
+ if ( pshmLast->ShmSelf == pshmCurrent->ShmSelf )
+ {
+ /* Either the first element or no elements left. */
+ SHMSetInfo( SIID_NAMED_OBJECTS, pshmCurrent->ShmNext );
+ }
+ else if ( (PSHM_NAMED_OBJECTS)SHMPTR_TO_PTR( pshmCurrent->ShmNext ) )
+ {
+ pshmLast->ShmNext = pshmCurrent->ShmNext;
+ }
+ else
+ {
+ /* Only one left. */
+ pshmLast->ShmNext = 0;
+ }
+
+ break;
+ }
+ else
+ {
+ pshmLast = pshmCurrent;
+ pshmCurrent = (PSHM_NAMED_OBJECTS)SHMPTR_TO_PTR( pshmCurrent->ShmNext );
+ }
+ }
+
+ SHMRelease();
+ return;
+}
+
+/*++ SHMAddNamedObject
+
+Adds the specified named object to the list.
+
+No return.
+--*/
+void SHMAddNamedObject( SHMPTR shmNewNamedObject )
+{
+ PSHM_NAMED_OBJECTS pshmNew = 0;
+
+ pshmNew = (PSHM_NAMED_OBJECTS)SHMPTR_TO_PTR( shmNewNamedObject );
+
+ if ( pshmNew == NULL )
+ {
+ ASSERT( "pshmNew should not be NULL\n" );
+ }
+
+ SHMLock();
+
+ pshmNew->ShmNext = SHMGetInfo( SIID_NAMED_OBJECTS );
+
+ if ( !SHMSetInfo( SIID_NAMED_OBJECTS, shmNewNamedObject ) )
+ {
+ ASSERT( "Unable to add the mapping object to shared memory.\n" );
+ }
+
+ SHMRelease();
+ return;
+}
diff --git a/src/pal/src/sync/cs.cpp b/src/pal/src/sync/cs.cpp
new file mode 100644
index 0000000000..530e467301
--- /dev/null
+++ b/src/pal/src/sync/cs.cpp
@@ -0,0 +1,1603 @@
+// 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.
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// File:
+// cs.cpp
+//
+// Purpose:
+// Implementation of critical sections
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "pal/thread.hpp"
+#include "pal/cs.hpp"
+#include "pal/malloc.hpp"
+#include "pal/list.h"
+#include "pal/dbgmsg.h"
+#include "pal/init.h"
+#include "pal/process.h"
+
+#include <sched.h>
+#include <pthread.h>
+
+using namespace CorUnix;
+
+//
+// Uncomment the following line to turn CS behavior from
+// unfair to fair lock
+//
+// #define PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+
+//
+// Uncomment the following line to enable simple mutex based CSs
+// Note: when MUTEX_BASED_CSS is defined, PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+// has no effect
+//
+// #define MUTEX_BASED_CSS
+
+//
+// Important notes on critical sections layout/semantics on Unix
+//
+// 1) The PAL_CRITICAL_SECTION structure below must match the size of the
+// CRITICAL_SECTION defined in pal.h. Besides the "windows part"
+// of both the structures must be identical.
+// 2) Both PAL_CRITICAL_SECTION and CRITICAL_SECTION currently do not match
+// the size of the Windows' CRITICAL_SECTION.
+// - From unmanaged code point of view, one should never make assumptions
+// on the size and layout of the CRITICAL_SECTION structure, and anyway
+// on Unix PAL's CRITICAL_SECTION extends the Windows one, so that some
+// assumptions may still work.
+// - From managed code point of view, one could try to interop directly
+// to unmanaged critical sections APIs (though that would be quite
+// meaningless). In order to do that, she would need to define a copy
+// of the CRITICAL_SECTION structure in his/her code, and that may lead
+// to access random data beyond the structure limit, if that managed
+// code is compiled on Unix.
+// In case such scenario should be supported, the current implementation
+// will have to be modified in a way to go back to the original Windows
+// CRITICAL_SECTION layout. That would require to dynamically allocate
+// the native data and use LockSemaphore as a pointer to it. The current
+// solution intentionally avoids that since an effort has been made to
+// make CSs objects completely independent from any other PAL subsystem,
+// so that they can be used during initialization and shutdown.
+// In case the "dynamically allocate native data" solution should be
+// implemented, CSs would acquire a dependency on memory allocation and
+// thread suspension subsystems, since the first contention on a specific
+// CS would trigger the native data allocation.
+// 3) The semantics of the LockCount field has not been kept compatible with
+// the Windows implementation.
+// Both on Windows and Unix the lower bit of LockCount indicates
+// whether or not the CS is locked (for both fair and unfair lock
+// solution), the second bit indicates whether or not currently there is a
+// waiter that has been awakened and that is trying to acquire the CS
+// (only unfair lock solution, unused in the fair one); starting from the
+// third bit, LockCount represents the number of waiter threads currently
+// waiting on the CS.
+// Windows, anyway, implements this semantics in negative logic, so that
+// an unlocked CS is represented by a LockCount == -1 (i.e. 0xFFFFFFFF,
+// all the bits set), while on Unix an unlocked CS has LockCount == 0.
+// Windows needs to use negative logic to support legacy code bad enough
+// to directly access CS's fields making the assumption that
+// LockCount == -1 means CS unlocked. Unix will not support that, and
+// it uses positive logic.
+// 4) The CRITICAL_SECTION_DEBUG_INFO layout on Unix is intentionally not
+// compatible with the Windows layout.
+// 5) For legacy code dependencies issues similar to those just described for
+// the LockCount field, Windows CS code maintains a per-process list of
+// debug info for all the CSs, both on debug and free/retail builds. On
+// Unix such a list is maintained only on debug builds, and no debug
+// info structure is allocated on free/retail builds
+//
+
+SET_DEFAULT_DEBUG_CHANNEL(CRITSEC);
+
+#ifdef TRACE_CS_LOGIC
+#define CS_TRACE TRACE
+#else
+#ifdef __GNUC__
+#define CS_TRACE(args...)
+#else
+#define CS_TRACE(...)
+#endif
+#endif // TRACE_CS_LOGIC
+
+//
+// Note: PALCS_LOCK_WAITER_INC must be 2 * PALCS_LOCK_AWAKENED_WAITER
+//
+#define PALCS_LOCK_INIT 0
+#define PALCS_LOCK_BIT 1
+#define PALCS_LOCK_AWAKENED_WAITER 2
+#define PALCS_LOCK_WAITER_INC 4
+
+#define PALCS_GETLBIT(val) ((int)(0!=(PALCS_LOCK_BIT&val)))
+#define PALCS_GETAWBIT(val) ((int)(0!=(PALCS_LOCK_AWAKENED_WAITER&val)))
+#define PALCS_GETWCOUNT(val) (val/PALCS_LOCK_WAITER_INC)
+
+enum PalCsInitState
+{
+ PalCsNotInitialized, // Critical section not initialized (InitializedCriticalSection
+ // has not yet been called, or DeleteCriticalsection has been
+ // called).
+ PalCsUserInitialized, // Critical section initialized from the user point of view,
+ // i.e. InitializedCriticalSection has been called.
+ PalCsFullyInitializing, // A thread found the CS locked, this is the first contention on
+ // this CS, and the thread is initializing the CS's native data.
+ PalCsFullyInitialized // Internal CS's native data has been fully initialized.
+};
+
+enum PalCsWaiterReturnState
+{
+ PalCsReturnWaiterAwakened,
+ PalCsWaiterDidntWait
+};
+
+struct _PAL_CRITICAL_SECTION; // fwd declaration
+
+typedef struct _CRITICAL_SECTION_DEBUG_INFO
+{
+ LIST_ENTRY Link;
+ struct _PAL_CRITICAL_SECTION * pOwnerCS;
+ Volatile<ULONG> lAcquireCount;
+ Volatile<ULONG> lEnterCount;
+ Volatile<LONG> lContentionCount;
+} CRITICAL_SECTION_DEBUG_INFO, *PCRITICAL_SECTION_DEBUG_INFO;
+
+typedef struct _PAL_CRITICAL_SECTION_NATIVE_DATA
+{
+ pthread_mutex_t mutex;
+ pthread_cond_t condition;
+ int iPredicate;
+} PAL_CRITICAL_SECTION_NATIVE_DATA, *PPAL_CRITICAL_SECTION_NATIVE_DATA;
+
+typedef struct _PAL_CRITICAL_SECTION {
+ // Windows part
+ PCRITICAL_SECTION_DEBUG_INFO DebugInfo;
+ Volatile<LONG> LockCount;
+ LONG RecursionCount;
+ SIZE_T OwningThread;
+ HANDLE LockSemaphore;
+ ULONG_PTR SpinCount;
+ // Private Unix part
+ BOOL fInternal;
+ Volatile<PalCsInitState> cisInitState;
+ PAL_CRITICAL_SECTION_NATIVE_DATA csndNativeData;
+} PAL_CRITICAL_SECTION, *PPAL_CRITICAL_SECTION, *LPPAL_CRITICAL_SECTION;
+
+#ifdef _DEBUG
+namespace CorUnix
+{
+ PAL_CRITICAL_SECTION g_csPALCSsListLock;
+ LIST_ENTRY g_PALCSList = { &g_PALCSList, &g_PALCSList};
+}
+#endif // _DEBUG
+
+#define ObtainCurrentThreadId(thread) ObtainCurrentThreadIdImpl(thread, __func__)
+static SIZE_T ObtainCurrentThreadIdImpl(CPalThread *pCurrentThread, const char *callingFuncName)
+{
+ SIZE_T threadId;
+ if(pCurrentThread)
+ {
+ threadId = pCurrentThread->GetThreadId();
+ _ASSERTE(threadId == THREADSilentGetCurrentThreadId());
+ }
+ else
+ {
+ threadId = THREADSilentGetCurrentThreadId();
+ CS_TRACE("Early %s, no pthread data, getting TID internally\n", callingFuncName);
+ }
+ _ASSERTE(0 != threadId);
+
+ return threadId;
+}
+
+
+/*++
+Function:
+ InitializeCriticalSection
+
+See MSDN doc.
+--*/
+void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+ PERF_ENTRY(InitializeCriticalSection);
+ ENTRY("InitializeCriticalSection(lpCriticalSection=%p)\n",
+ lpCriticalSection);
+
+ InternalInitializeCriticalSectionAndSpinCount(lpCriticalSection,
+ 0, false);
+
+ LOGEXIT("InitializeCriticalSection returns void\n");
+ PERF_EXIT(InitializeCriticalSection);
+}
+
+/*++
+Function:
+ InitializeCriticalSectionEx - Flags is ignored.
+
+See MSDN doc.
+--*/
+BOOL InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD Flags)
+{
+ PERF_ENTRY(InitializeCriticalSection);
+ ENTRY("InitializeCriticalSectionEx(lpCriticalSection=%p, dwSpinCount=%d, Flags=%d)\n",
+ lpCriticalSection, dwSpinCount, Flags);
+
+ InternalInitializeCriticalSectionAndSpinCount(lpCriticalSection, dwSpinCount, false);
+
+ LOGEXIT("InitializeCriticalSectionEx returns TRUE\n");
+ PERF_EXIT(InitializeCriticalSection);
+ return true;
+}
+
+/*++
+Function:
+ InitializeCriticalSectionAndSpinCount
+
+See MSDN doc.
+--*/
+BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection,
+ DWORD dwSpinCount)
+{
+ BOOL bRet = TRUE;
+ PERF_ENTRY(InitializeCriticalSectionAndSpinCount);
+ ENTRY("InitializeCriticalSectionAndSpinCount(lpCriticalSection=%p, "
+ "dwSpinCount=%u)\n", lpCriticalSection, dwSpinCount);
+
+ InternalInitializeCriticalSectionAndSpinCount(lpCriticalSection,
+ dwSpinCount, false);
+
+ LOGEXIT("InitializeCriticalSectionAndSpinCount returns BOOL %d\n",
+ bRet);
+ PERF_EXIT(InitializeCriticalSectionAndSpinCount);
+ return bRet;
+}
+
+/*++
+Function:
+ DeleteCriticalSection
+
+See MSDN doc.
+--*/
+void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+ PERF_ENTRY(DeleteCriticalSection);
+ ENTRY("DeleteCriticalSection(lpCriticalSection=%p)\n", lpCriticalSection);
+
+ InternalDeleteCriticalSection(lpCriticalSection);
+
+ LOGEXIT("DeleteCriticalSection returns void\n");
+ PERF_EXIT(DeleteCriticalSection);
+}
+
+/*++
+Function:
+ EnterCriticalSection
+
+See MSDN doc.
+--*/
+void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+ PERF_ENTRY(EnterCriticalSection);
+ ENTRY("EnterCriticalSection(lpCriticalSection=%p)\n", lpCriticalSection);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ InternalEnterCriticalSection(pThread, lpCriticalSection);
+
+ LOGEXIT("EnterCriticalSection returns void\n");
+ PERF_EXIT(EnterCriticalSection);
+}
+
+/*++
+Function:
+ TryEnterCriticalSection
+
+See MSDN doc.
+--*/
+BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+ PERF_ENTRY(TryEnterCriticalSection);
+ ENTRY("TryEnterCriticalSection(lpCriticalSection=%p)\n", lpCriticalSection);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ bool fRet = InternalTryEnterCriticalSection(pThread,
+ lpCriticalSection);
+
+ LOGEXIT("TryEnterCriticalSection returns bool %d\n", (int)fRet);
+ PERF_EXIT(TryEnterCriticalSection);
+
+ return (BOOL)fRet;
+}
+
+/*++
+Function:
+ LeaveCriticalSection
+
+See MSDN doc.
+--*/
+VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+ PERF_ENTRY(LeaveCriticalSection);
+ ENTRY("LeaveCriticalSection(lpCriticalSection=%p)\n", lpCriticalSection);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ InternalLeaveCriticalSection(pThread, lpCriticalSection);
+
+ LOGEXIT("LeaveCriticalSection returns void\n");
+ PERF_EXIT(LeaveCriticalSection);
+}
+
+/*++
+Function:
+ InternalInitializeCriticalSection
+
+Initializes a critical section. It assumes the CS is an internal one,
+i.e. thread entering it will be marked unsafe for suspension
+--*/
+VOID InternalInitializeCriticalSection(CRITICAL_SECTION *pcs)
+{
+ InternalInitializeCriticalSectionAndSpinCount(pcs, 0, true);
+}
+
+/*++
+Function:
+ InternalDeleteCriticalSection
+
+Deletes a critical section
+--*/
+VOID InternalDeleteCriticalSection(
+ PCRITICAL_SECTION pCriticalSection)
+{
+ PAL_CRITICAL_SECTION * pPalCriticalSection =
+ reinterpret_cast<PAL_CRITICAL_SECTION*>(pCriticalSection);
+
+ _ASSERT_MSG(PalCsUserInitialized == pPalCriticalSection->cisInitState ||
+ PalCsFullyInitialized == pPalCriticalSection->cisInitState,
+ "CS %p is not initialized", pPalCriticalSection);
+
+#ifdef _DEBUG
+ CPalThread * pThread =
+ (PALIsThreadDataInitialized() ? GetCurrentPalThread() : NULL);
+
+ if (0 != pPalCriticalSection->LockCount)
+ {
+ SIZE_T tid;
+ tid = ObtainCurrentThreadId(pThread);
+ int iWaiterCount = (int)PALCS_GETWCOUNT(pPalCriticalSection->LockCount);
+
+ if (0 != (PALCS_LOCK_BIT & pPalCriticalSection->LockCount))
+ {
+ // CS is locked
+ if (tid != pPalCriticalSection->OwningThread)
+ {
+ // not owner
+ ASSERT("Thread tid=%u deleting a CS owned by thread tid=%u\n",
+ tid, pPalCriticalSection->OwningThread);
+ }
+ else
+ {
+ // owner
+ if (0 != iWaiterCount)
+ {
+ ERROR("Thread tid=%u is deleting a CS with %d threads waiting on it\n",
+ tid, iWaiterCount);
+ }
+ else
+ {
+ WARN("Thread tid=%u is deleting a critical section it still owns\n",
+ tid);
+ }
+ }
+ }
+ else
+ {
+ // CS is not locked
+ if (0 != iWaiterCount)
+ {
+ ERROR("Deleting a CS with %d threads waiting on it\n",
+ iWaiterCount);
+ }
+ else
+ {
+ ERROR("Thread tid=%u is deleting a critical section currently not "
+ "owned, but with one waiter awakened\n", tid);
+ }
+ }
+ }
+
+ if (NULL != pPalCriticalSection->DebugInfo)
+ {
+ if (pPalCriticalSection != &CorUnix::g_csPALCSsListLock)
+ {
+ InternalEnterCriticalSection(pThread,
+ reinterpret_cast<CRITICAL_SECTION*>(&g_csPALCSsListLock));
+ RemoveEntryList(&pPalCriticalSection->DebugInfo->Link);
+ InternalLeaveCriticalSection(pThread,
+ reinterpret_cast<CRITICAL_SECTION*>(&g_csPALCSsListLock));
+ }
+ else
+ {
+ RemoveEntryList(&pPalCriticalSection->DebugInfo->Link);
+ }
+
+#ifdef PAL_TRACK_CRITICAL_SECTIONS_DATA
+ LONG lVal, lNewVal;
+ Volatile<LONG> * plDest;
+
+ // Update delete count
+ InterlockedIncrement(pPalCriticalSection->fInternal ?
+ &g_lPALCSInternalDeleteCount : &g_lPALCSDeleteCount);
+
+ // Update acquire count
+ plDest = pPalCriticalSection->fInternal ?
+ &g_lPALCSInternalAcquireCount : &g_lPALCSAcquireCount;
+ do {
+ lVal = *plDest;
+ lNewVal = lVal + pPalCriticalSection->DebugInfo->lAcquireCount;
+ lNewVal = InterlockedCompareExchange(plDest, lNewVal, lVal);
+ } while (lVal != lNewVal);
+
+ // Update enter count
+ plDest = pPalCriticalSection->fInternal ?
+ &g_lPALCSInternalEnterCount : &g_lPALCSEnterCount;
+ do {
+ lVal = *plDest;
+ lNewVal = lVal + pPalCriticalSection->DebugInfo->lEnterCount;
+ lNewVal = InterlockedCompareExchange(plDest, lNewVal, lVal);
+ } while (lVal != lNewVal);
+
+ // Update contention count
+ plDest = pPalCriticalSection->fInternal ?
+ &g_lPALCSInternalContentionCount : &g_lPALCSContentionCount;
+ do {
+ lVal = *plDest;
+ lNewVal = lVal + pPalCriticalSection->DebugInfo->lContentionCount;
+ lNewVal = InterlockedCompareExchange(plDest, lNewVal, lVal);
+ } while (lVal != lNewVal);
+
+#endif // PAL_TRACK_CRITICAL_SECTIONS_DATA
+
+ InternalDelete(pPalCriticalSection->DebugInfo);
+ pPalCriticalSection->DebugInfo = NULL;
+ }
+#endif // _DEBUG
+
+ if (PalCsFullyInitialized == pPalCriticalSection->cisInitState)
+ {
+ int iRet;
+
+ // destroy condition
+ iRet = pthread_cond_destroy(&pPalCriticalSection->csndNativeData.condition);
+ _ASSERT_MSG(0 == iRet, "Failed destroying condition in CS @ %p "
+ "[err=%d]\n", pPalCriticalSection, iRet);
+
+ // destroy mutex
+ iRet = pthread_mutex_destroy(&pPalCriticalSection->csndNativeData.mutex);
+ _ASSERT_MSG(0 == iRet, "Failed destroying mutex in CS @ %p "
+ "[err=%d]\n", pPalCriticalSection, iRet);
+ }
+
+ // Reset critical section state
+ pPalCriticalSection->cisInitState = PalCsNotInitialized;
+}
+
+// The following PALCEnterCriticalSection and PALCLeaveCriticalSection
+// functions are intended to provide CorUnix's InternalEnterCriticalSection
+// and InternalLeaveCriticalSection functionalities to legacy C code,
+// which has no knowledge of CPalThread, classes and namespaces.
+
+/*++
+Function:
+ PALCEnterCriticalSection
+
+Provides CorUnix's InternalEnterCriticalSection functionality to legacy C code,
+which has no knowledge of CPalThread, classes and namespaces.
+--*/
+VOID PALCEnterCriticalSection(CRITICAL_SECTION * pcs)
+{
+ CPalThread * pThread =
+ (PALIsThreadDataInitialized() ? GetCurrentPalThread() : NULL);
+ CorUnix::InternalEnterCriticalSection(pThread, pcs);
+}
+
+/*++
+Function:
+ PALCLeaveCriticalSection
+
+Provides CorUnix's InternalLeaveCriticalSection functionality to legacy C code,
+which has no knowledge of CPalThread, classes and namespaces.
+--*/
+VOID PALCLeaveCriticalSection(CRITICAL_SECTION * pcs)
+{
+ CPalThread * pThread =
+ (PALIsThreadDataInitialized() ? GetCurrentPalThread() : NULL);
+ CorUnix::InternalLeaveCriticalSection(pThread, pcs);
+}
+
+namespace CorUnix
+{
+ static PalCsWaiterReturnState PALCS_WaitOnCS(
+ PAL_CRITICAL_SECTION * pPalCriticalSection,
+ LONG lInc);
+ static PAL_ERROR PALCS_DoActualWait(PAL_CRITICAL_SECTION * pPalCriticalSection);
+ static PAL_ERROR PALCS_WakeUpWaiter(PAL_CRITICAL_SECTION * pPalCriticalSection);
+ static bool PALCS_FullyInitialize(PAL_CRITICAL_SECTION * pPalCriticalSection);
+
+#ifdef _DEBUG
+ enum CSSubSysInitState
+ {
+ CSSubSysNotInitialzed,
+ CSSubSysInitializing,
+ CSSubSysInitialized
+ };
+ static Volatile<CSSubSysInitState> csssInitState = CSSubSysNotInitialzed;
+
+#ifdef PAL_TRACK_CRITICAL_SECTIONS_DATA
+ static Volatile<LONG> g_lPALCSInitializeCount = 0;
+ static Volatile<LONG> g_lPALCSDeleteCount = 0;
+ static Volatile<LONG> g_lPALCSAcquireCount = 0;
+ static Volatile<LONG> g_lPALCSEnterCount = 0;
+ static Volatile<LONG> g_lPALCSContentionCount = 0;
+ static Volatile<LONG> g_lPALCSInternalInitializeCount = 0;
+ static Volatile<LONG> g_lPALCSInternalDeleteCount = 0;
+ static Volatile<LONG> g_lPALCSInternalAcquireCount = 0;
+ static Volatile<LONG> g_lPALCSInternalEnterCount = 0;
+ static Volatile<LONG> g_lPALCSInternalContentionCount = 0;
+#endif // PAL_TRACK_CRITICAL_SECTIONS_DATA
+#endif // _DEBUG
+
+
+ /*++
+ Function:
+ CorUnix::CriticalSectionSubSysInitialize
+
+ Initializes CS subsystem
+ --*/
+ void CriticalSectionSubSysInitialize()
+ {
+ static_assert(sizeof(CRITICAL_SECTION) >= sizeof(PAL_CRITICAL_SECTION),
+ "PAL fatal internal error: sizeof(CRITICAL_SECTION) is "
+ "smaller than sizeof(PAL_CRITICAL_SECTION)");
+
+#ifdef _DEBUG
+ LONG lRet = InterlockedCompareExchange((LONG *)&csssInitState,
+ (LONG)CSSubSysInitializing,
+ (LONG)CSSubSysNotInitialzed);
+ if ((LONG)CSSubSysNotInitialzed == lRet)
+ {
+ InitializeListHead(&g_PALCSList);
+
+ InternalInitializeCriticalSectionAndSpinCount(
+ reinterpret_cast<CRITICAL_SECTION*>(&g_csPALCSsListLock),
+ 0, true);
+ InterlockedExchange((LONG *)&csssInitState,
+ (LONG)CSSubSysInitialized);
+ }
+ else
+ {
+ while (csssInitState != CSSubSysInitialized)
+ {
+ sched_yield();
+ }
+ }
+#endif // _DEBUG
+ }
+
+ /*++
+ Function:
+ CorUnix::InternalInitializeCriticalSectionAndSpinCount
+
+ Initializes a CS with the given spin count. If 'fInternal' is true
+ the CS will be treatead as an internal one for its whole lifetime,
+ i.e. any thread that will enter it will be marked as unsafe for
+ suspension as long as it holds the CS
+ --*/
+ void InternalInitializeCriticalSectionAndSpinCount(
+ PCRITICAL_SECTION pCriticalSection,
+ DWORD dwSpinCount,
+ bool fInternal)
+ {
+ PAL_CRITICAL_SECTION * pPalCriticalSection =
+ reinterpret_cast<PAL_CRITICAL_SECTION*>(pCriticalSection);
+
+#ifndef PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+ // Make sure bits are defined in a usable way
+ _ASSERTE(PALCS_LOCK_AWAKENED_WAITER * 2 == PALCS_LOCK_WAITER_INC);
+#endif // !PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+
+ // Make sure structure sizes are compatible
+ _ASSERTE(sizeof(CRITICAL_SECTION) >= sizeof(PAL_CRITICAL_SECTION));
+
+#ifdef _DEBUG
+ if (sizeof(CRITICAL_SECTION) > sizeof(PAL_CRITICAL_SECTION))
+ {
+ WARN("PAL_CS_NATIVE_DATA_SIZE appears to be defined to a value (%d) "
+ "larger than needed on this platform (%d).\n",
+ sizeof(CRITICAL_SECTION), sizeof(PAL_CRITICAL_SECTION));
+ }
+#endif // _DEBUG
+
+ // Init CS data
+ pPalCriticalSection->DebugInfo = NULL;
+ pPalCriticalSection->LockCount = 0;
+ pPalCriticalSection->RecursionCount = 0;
+ pPalCriticalSection->SpinCount = dwSpinCount;
+ pPalCriticalSection->OwningThread = NULL;
+ pPalCriticalSection->LockSemaphore = NULL;
+ pPalCriticalSection->fInternal = fInternal;
+
+#ifdef _DEBUG
+ CPalThread * pThread =
+ (PALIsThreadDataInitialized() ? GetCurrentPalThread() : NULL);
+
+ pPalCriticalSection->DebugInfo = InternalNew<CRITICAL_SECTION_DEBUG_INFO>();
+ _ASSERT_MSG(NULL != pPalCriticalSection->DebugInfo,
+ "Failed to allocate debug info for new CS\n");
+
+ // Init debug info data
+ pPalCriticalSection->DebugInfo->lAcquireCount = 0;
+ pPalCriticalSection->DebugInfo->lEnterCount = 0;
+ pPalCriticalSection->DebugInfo->lContentionCount = 0;
+ pPalCriticalSection->DebugInfo->pOwnerCS = pPalCriticalSection;
+
+ // Insert debug info struct in global list
+ if (pPalCriticalSection != &g_csPALCSsListLock)
+ {
+ InternalEnterCriticalSection(pThread,
+ reinterpret_cast<CRITICAL_SECTION*>(&g_csPALCSsListLock));
+ InsertTailList(&g_PALCSList, &pPalCriticalSection->DebugInfo->Link);
+ InternalLeaveCriticalSection(pThread,
+ reinterpret_cast<CRITICAL_SECTION*>(&g_csPALCSsListLock));
+ }
+ else
+ {
+ InsertTailList(&g_PALCSList, &pPalCriticalSection->DebugInfo->Link);
+ }
+
+#ifdef PAL_TRACK_CRITICAL_SECTIONS_DATA
+ InterlockedIncrement(fInternal ?
+ &g_lPALCSInternalInitializeCount : &g_lPALCSInitializeCount);
+#endif // PAL_TRACK_CRITICAL_SECTIONS_DATA
+#endif // _DEBUG
+
+ // Set initializazion state
+ pPalCriticalSection->cisInitState = PalCsUserInitialized;
+
+#ifdef MUTEX_BASED_CSS
+ bool fInit;
+ do
+ {
+ fInit = PALCS_FullyInitialize(pPalCriticalSection);
+ _ASSERTE(fInit);
+ } while (!fInit && 0 == sched_yield());
+
+ if (fInit)
+ {
+ // Set initializazion state
+ pPalCriticalSection->cisInitState = PalCsFullyInitialized;
+ }
+#endif // MUTEX_BASED_CSS
+ }
+
+#ifndef MUTEX_BASED_CSS
+ /*++
+ Function:
+ CorUnix::InternalEnterCriticalSection
+
+ Enters a CS, causing the thread to block if the CS is owned by
+ another thread
+ --*/
+ void InternalEnterCriticalSection(
+ CPalThread * pThread,
+ PCRITICAL_SECTION pCriticalSection)
+ {
+ PAL_CRITICAL_SECTION * pPalCriticalSection =
+ reinterpret_cast<PAL_CRITICAL_SECTION*>(pCriticalSection);
+
+ LONG lSpinCount;
+ LONG lVal, lNewVal;
+ LONG lBitsToChange, lWaitInc;
+ PalCsWaiterReturnState cwrs;
+ SIZE_T threadId;
+
+ _ASSERTE(PalCsNotInitialized != pPalCriticalSection->cisInitState);
+
+ threadId = ObtainCurrentThreadId(pThread);
+
+
+ // Check if the current thread already owns the CS
+ //
+ // Note: there is no need for this double check to be atomic. In fact
+ // if the first check fails, the second doesn't count (and it's not
+ // even executed). If the first one succeeds and the second one
+ // doesn't, it doesn't matter if LockCount has already changed by the
+ // time OwningThread is tested. Instead, if the first one succeeded,
+ // and the second also succeeds, LockCount cannot have changed in the
+ // meanwhile, since this is the owning thread and only the owning
+ // thread can change the lock bit when the CS is owned.
+ if ((pPalCriticalSection->LockCount & PALCS_LOCK_BIT) &&
+ (pPalCriticalSection->OwningThread == threadId))
+ {
+ pPalCriticalSection->RecursionCount += 1;
+#ifdef _DEBUG
+ if (NULL != pPalCriticalSection->DebugInfo)
+ {
+ pPalCriticalSection->DebugInfo->lEnterCount += 1;
+ }
+#endif // _DEBUG
+ goto IECS_exit;
+ }
+
+ // Set bits to change and waiter increment for an incoming thread
+ lBitsToChange = PALCS_LOCK_BIT;
+ lWaitInc = PALCS_LOCK_WAITER_INC;
+ lSpinCount = pPalCriticalSection->SpinCount;
+
+ while (TRUE)
+ {
+ // Either this is an incoming thread, and therefore lBitsToChange
+ // is just PALCS_LOCK_BIT, or this is an awakened waiter
+ _ASSERTE(PALCS_LOCK_BIT == lBitsToChange ||
+ (PALCS_LOCK_BIT | PALCS_LOCK_AWAKENED_WAITER) == lBitsToChange);
+
+ // Make sure the waiter increment is in a valid range
+ _ASSERTE(PALCS_LOCK_WAITER_INC == lWaitInc ||
+ PALCS_LOCK_AWAKENED_WAITER == lWaitInc);
+
+ do {
+ lVal = pPalCriticalSection->LockCount;
+
+ while (0 == (lVal & PALCS_LOCK_BIT))
+ {
+ // CS is not locked: try lo lock it
+
+ // Make sure that whether we are an incoming thread
+ // or the PALCS_LOCK_AWAKENED_WAITER bit is set
+ _ASSERTE((PALCS_LOCK_BIT == lBitsToChange) ||
+ (PALCS_LOCK_AWAKENED_WAITER & lVal));
+
+ lNewVal = lVal ^ lBitsToChange;
+
+ // Make sure we are actually trying to lock
+ _ASSERTE(lNewVal & PALCS_LOCK_BIT);
+
+ CS_TRACE("[ECS %p] Switching from {%d, %d, %d} to "
+ "{%d, %d, %d} ==>\n", pPalCriticalSection,
+ PALCS_GETWCOUNT(lVal), PALCS_GETAWBIT(lVal), PALCS_GETLBIT(lVal),
+ PALCS_GETWCOUNT(lNewVal), PALCS_GETAWBIT(lNewVal), PALCS_GETLBIT(lNewVal));
+
+ // Try to switch the value
+ lNewVal = InterlockedCompareExchange (&pPalCriticalSection->LockCount,
+ lNewVal, lVal);
+
+ CS_TRACE("[ECS %p] ==> %s LockCount={%d, %d, %d} "
+ "lVal={%d, %d, %d}\n", pPalCriticalSection,
+ (lNewVal == lVal) ? "OK" : "NO",
+ PALCS_GETWCOUNT(pPalCriticalSection->LockCount),
+ PALCS_GETAWBIT(pPalCriticalSection->LockCount),
+ PALCS_GETLBIT(pPalCriticalSection->LockCount),
+ PALCS_GETWCOUNT(lVal), PALCS_GETAWBIT(lVal), PALCS_GETLBIT(lVal));
+
+ if (lNewVal == lVal)
+ {
+ // CS successfully acquired
+ goto IECS_set_ownership;
+ }
+
+ // Acquisition failed, some thread raced with us;
+ // update value for next loop
+ lVal = lNewVal;
+ }
+
+ if (0 < lSpinCount)
+ {
+ sched_yield();
+ }
+ } while (0 <= --lSpinCount);
+
+ cwrs = PALCS_WaitOnCS(pPalCriticalSection, lWaitInc);
+
+ if (PalCsReturnWaiterAwakened == cwrs)
+ {
+#ifdef PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+ //
+ // Fair Critical Sections
+ //
+ // In the fair lock case, when a waiter wakes up the CS
+ // must be locked (i.e. ownership passed on to the waiter)
+ _ASSERTE(0 != (PALCS_LOCK_BIT & pPalCriticalSection->LockCount));
+
+ // CS successfully acquired
+ goto IECS_set_ownership;
+
+#else // PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+ //
+ // Unfair Critical Sections
+ //
+ _ASSERTE(PALCS_LOCK_AWAKENED_WAITER & pPalCriticalSection->LockCount);
+
+ lBitsToChange = PALCS_LOCK_BIT | PALCS_LOCK_AWAKENED_WAITER;
+ lWaitInc = PALCS_LOCK_AWAKENED_WAITER;
+#endif // PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+ }
+ }
+
+ IECS_set_ownership:
+ // Critical section acquired: set ownership data
+ pPalCriticalSection->OwningThread = threadId;
+ pPalCriticalSection->RecursionCount = 1;
+#ifdef _DEBUG
+ if (NULL != pPalCriticalSection->DebugInfo)
+ {
+ pPalCriticalSection->DebugInfo->lAcquireCount += 1;
+ pPalCriticalSection->DebugInfo->lEnterCount += 1;
+ }
+#endif // _DEBUG
+
+ IECS_exit:
+ return;
+ }
+
+ /*++
+ Function:
+ CorUnix::InternalLeaveCriticalSection
+
+ Leaves a currently owned CS
+ --*/
+ void InternalLeaveCriticalSection(CPalThread * pThread,
+ PCRITICAL_SECTION pCriticalSection)
+ {
+ PAL_CRITICAL_SECTION * pPalCriticalSection =
+ reinterpret_cast<PAL_CRITICAL_SECTION*>(pCriticalSection);
+ LONG lVal, lNewVal;
+
+#ifdef _DEBUG
+ SIZE_T threadId;
+
+ _ASSERTE(PalCsNotInitialized != pPalCriticalSection->cisInitState);
+
+ threadId = ObtainCurrentThreadId(pThread);
+ _ASSERTE(threadId == pPalCriticalSection->OwningThread);
+#endif // _DEBUG
+
+ _ASSERT_MSG(PALCS_LOCK_BIT & pPalCriticalSection->LockCount,
+ "Trying to release an unlocked CS\n");
+ _ASSERT_MSG(0 < pPalCriticalSection->RecursionCount,
+ "Trying to release an unlocked CS\n");
+
+ if (--pPalCriticalSection->RecursionCount > 0)
+ {
+ // Recursion was > 1, still owning the CS
+ goto ILCS_cs_exit;
+ }
+
+ // Reset CS ownership
+ pPalCriticalSection->OwningThread = NULL;
+
+ // Load the current LockCount value
+ lVal = pPalCriticalSection->LockCount;
+
+ while (true)
+ {
+ _ASSERT_MSG(0 != (PALCS_LOCK_BIT & lVal),
+ "Trying to release an unlocked CS\n");
+
+ // NB: In the fair lock case (PALCS_TRANSFER_OWNERSHIP_ON_RELEASE) the
+ // PALCS_LOCK_AWAKENED_WAITER bit is not used
+ if ( (PALCS_LOCK_BIT == lVal)
+#ifndef PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+ || (PALCS_LOCK_AWAKENED_WAITER & lVal)
+#endif // !PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+ )
+ {
+ // Whether there are no waiters (PALCS_LOCK_BIT == lVal)
+ // or a waiter has already been awakened, therefore we
+ // just need to reset the lock bit and return
+ lNewVal = lVal & ~PALCS_LOCK_BIT;
+ CS_TRACE("[LCS-UN %p] Switching from {%d, %d, %d} to "
+ "{%d, %d, %d} ==>\n", pPalCriticalSection,
+ PALCS_GETWCOUNT(lVal), PALCS_GETAWBIT(lVal), PALCS_GETLBIT(lVal),
+ PALCS_GETWCOUNT(lNewVal), PALCS_GETAWBIT(lNewVal), PALCS_GETLBIT(lNewVal));
+
+ lNewVal = InterlockedCompareExchange(&pPalCriticalSection->LockCount,
+ lNewVal, lVal);
+
+ CS_TRACE("[LCS-UN %p] ==> %s\n", pPalCriticalSection,
+ (lNewVal == lVal) ? "OK" : "NO");
+
+ if (lNewVal == lVal)
+ {
+ goto ILCS_cs_exit;
+ }
+ }
+ else
+ {
+ // There is at least one waiter, we need to wake it up
+
+#ifdef PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+ // Fair lock case: passing ownership on to the first waiter.
+ // Here we need only to decrement the waiters count. CS will
+ // remain locked and ownership will be passed to the waiter,
+ // which will take care of setting ownership data as soon as
+ // it wakes up
+ lNewVal = lVal - PALCS_LOCK_WAITER_INC;
+#else // PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+ // Unfair lock case: we need to atomically decrement the waiters
+ // count (we are about ot wake up one of them), set the
+ // "waiter awakened" bit and to reset the "CS locked" bit.
+ // Note that, since we know that at this time PALCS_LOCK_BIT
+ // is set and PALCS_LOCK_AWAKENED_WAITER is not set, none of
+ // the addenda will affect bits other than its target bit(s),
+ // i.e. PALCS_LOCK_BIT will not affect PALCS_LOCK_AWAKENED_WAITER,
+ // PALCS_LOCK_AWAKENED_WAITER will not affect the actual
+ // count of waiters, and the latter will not change the two
+ // former ones
+ lNewVal = lVal - PALCS_LOCK_WAITER_INC +
+ PALCS_LOCK_AWAKENED_WAITER - PALCS_LOCK_BIT;
+#endif // PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+ CS_TRACE("[LCS-CN %p] Switching from {%d, %d, %d} to {%d, %d, %d} ==>\n",
+ pPalCriticalSection,
+ PALCS_GETWCOUNT(lVal), PALCS_GETAWBIT(lVal), PALCS_GETLBIT(lVal),
+ PALCS_GETWCOUNT(lNewVal), PALCS_GETAWBIT(lNewVal), PALCS_GETLBIT(lNewVal));
+
+ lNewVal = InterlockedCompareExchange(&pPalCriticalSection->LockCount,
+ lNewVal, lVal);
+
+ CS_TRACE("[LCS-CN %p] ==> %s\n", pPalCriticalSection,
+ (lNewVal == lVal) ? "OK" : "NO");
+
+ if (lNewVal == lVal)
+ {
+ // Wake up the waiter
+ PALCS_WakeUpWaiter (pPalCriticalSection);
+
+#ifdef PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+ // In the fair lock case, we need to yield here to defeat
+ // the inherently unfair nature of the condition/predicate
+ // construct
+ sched_yield();
+#endif // PALCS_TRANSFER_OWNERSHIP_ON_RELEASE
+
+ goto ILCS_cs_exit;
+ }
+ }
+
+ // CS unlock failed due to race with another thread trying to
+ // register as waiter on it. We need to keep on looping. We
+ // intentionally do not yield here in order to reserve higher
+ // priority for the releasing thread.
+ //
+ // At this point lNewVal contains the latest LockCount value
+ // retrieved by one of the two InterlockedCompareExchange above;
+ // we can use this value as expected LockCount for the next loop,
+ // without the need to fetch it again.
+ lVal = lNewVal;
+ }
+
+ ILCS_cs_exit:
+ return;
+ }
+
+ /*++
+ Function:
+ CorUnix::InternalTryEnterCriticalSection
+
+ Tries to acquire a CS. It returns true on success, false if the CS is
+ locked by another thread
+ --*/
+ bool InternalTryEnterCriticalSection(
+ CPalThread * pThread,
+ PCRITICAL_SECTION pCriticalSection)
+ {
+ PAL_CRITICAL_SECTION * pPalCriticalSection =
+ reinterpret_cast<PAL_CRITICAL_SECTION*>(pCriticalSection);
+
+ LONG lNewVal;
+ SIZE_T threadId;
+ bool fRet = true;
+
+ _ASSERTE(PalCsNotInitialized != pPalCriticalSection->cisInitState);
+
+ threadId = ObtainCurrentThreadId(pThread);
+
+ lNewVal = InterlockedCompareExchange (&pPalCriticalSection->LockCount,
+ (LONG)PALCS_LOCK_BIT,
+ (LONG)PALCS_LOCK_INIT);
+ if (lNewVal == PALCS_LOCK_INIT)
+ {
+ // CS successfully acquired: setting ownership data
+ pPalCriticalSection->OwningThread = threadId;
+ pPalCriticalSection->RecursionCount = 1;
+#ifdef _DEBUG
+ if (NULL != pPalCriticalSection->DebugInfo)
+ {
+ pPalCriticalSection->DebugInfo->lAcquireCount += 1;
+ pPalCriticalSection->DebugInfo->lEnterCount += 1;
+ }
+#endif // _DEBUG
+
+ goto ITECS_exit;
+ }
+
+ // check if the current thread already owns the criticalSection
+ if ((lNewVal & PALCS_LOCK_BIT) &&
+ (pPalCriticalSection->OwningThread == threadId))
+ {
+ pPalCriticalSection->RecursionCount += 1;
+#ifdef _DEBUG
+ if (NULL != pPalCriticalSection->DebugInfo)
+ {
+ pPalCriticalSection->DebugInfo->lEnterCount += 1;
+ }
+#endif // _DEBUG
+
+ goto ITECS_exit;
+ }
+
+ // Failed to acquire the CS
+ fRet = false;
+
+ ITECS_exit:
+ return fRet;
+ }
+#endif // MUTEX_BASED_CSS
+
+ /*++
+ Function:
+ CorUnix::PALCS_FullyInitialize
+
+ Fully initializes a CS previously initialied true InitializeCriticalSection.
+ This method is called at the first contention on the target CS
+ --*/
+ bool PALCS_FullyInitialize(PAL_CRITICAL_SECTION * pPalCriticalSection)
+ {
+ LONG lVal, lNewVal;
+ bool fRet = true;
+
+ lVal = pPalCriticalSection->cisInitState;
+ if (PalCsFullyInitialized == lVal)
+ {
+ goto PCDI_exit;
+ }
+ if (PalCsUserInitialized == lVal)
+ {
+ int iRet;
+ lNewVal = (LONG)PalCsFullyInitializing;
+ lNewVal = InterlockedCompareExchange(
+ (LONG *)&pPalCriticalSection->cisInitState, lNewVal, lVal);
+ if (lNewVal != lVal)
+ {
+ if (PalCsFullyInitialized == lNewVal)
+ {
+ // Another thread did initialize this CS: we can
+ // safely return 'true'
+ goto PCDI_exit;
+ }
+
+ // Another thread is still initializing this CS: yield and
+ // spin by returning 'false'
+ sched_yield();
+ fRet = false;
+ goto PCDI_exit;
+ }
+
+ //
+ // Actual native initialization
+ //
+ // Mutex
+ iRet = pthread_mutex_init(&pPalCriticalSection->csndNativeData.mutex, NULL);
+ if (0 != iRet)
+ {
+ ASSERT("Failed initializing mutex in CS @ %p [err=%d]\n",
+ pPalCriticalSection, iRet);
+ pPalCriticalSection->cisInitState = PalCsUserInitialized;
+ fRet = false;
+ goto PCDI_exit;
+ }
+#ifndef MUTEX_BASED_CSS
+ // Condition
+ iRet = pthread_cond_init(&pPalCriticalSection->csndNativeData.condition, NULL);
+ if (0 != iRet)
+ {
+ ASSERT("Failed initializing condition in CS @ %p [err=%d]\n",
+ pPalCriticalSection, iRet);
+ pthread_mutex_destroy(&pPalCriticalSection->csndNativeData.mutex);
+ pPalCriticalSection->cisInitState = PalCsUserInitialized;
+ fRet = false;
+ goto PCDI_exit;
+ }
+ // Predicate
+ pPalCriticalSection->csndNativeData.iPredicate = 0;
+#endif
+
+ pPalCriticalSection->cisInitState = PalCsFullyInitialized;
+ }
+ else if (PalCsFullyInitializing == lVal)
+ {
+ // Another thread is still initializing this CS: yield and
+ // spin by returning 'false'
+ sched_yield();
+ fRet = false;
+ goto PCDI_exit;
+ }
+ else
+ {
+ ASSERT("CS %p is not initialized", pPalCriticalSection);
+ fRet = false;
+ goto PCDI_exit;
+ }
+
+ PCDI_exit:
+ return fRet;
+ }
+
+
+ /*++
+ Function:
+ CorUnix::PALCS_WaitOnCS
+
+ Waits on a CS owned by anothr thread. It returns PalCsReturnWaiterAwakened
+ if the thread actually waited on the CS and it has been awakened on CS
+ release. It returns PalCsWaiterDidntWait if another thread is currently
+ fully-initializing the CS and therefore the current thread couldn't wait
+ on it
+ --*/
+ PalCsWaiterReturnState PALCS_WaitOnCS(PAL_CRITICAL_SECTION * pPalCriticalSection,
+ LONG lInc)
+ {
+ DWORD lVal, lNewVal;
+ PAL_ERROR palErr = NO_ERROR;
+
+ if (PalCsFullyInitialized != pPalCriticalSection->cisInitState)
+ {
+ // First contention, the CS native wait support need to be
+ // initialized at this time
+ if (!PALCS_FullyInitialize(pPalCriticalSection))
+ {
+ // The current thread failed the full initialization of the CS,
+ // whether because another thread is race-initializing it, or
+ // there are no enough memory/resources at this time, or
+ // InitializeCriticalSection has never been called. By
+ // returning we will cause the thread to spin on CS trying
+ // again until the CS is initialized
+ return PalCsWaiterDidntWait;
+ }
+ }
+
+ // Make sure we have a valid waiter increment
+ _ASSERTE(PALCS_LOCK_WAITER_INC == lInc ||
+ PALCS_LOCK_AWAKENED_WAITER == lInc);
+
+ do {
+ lVal = pPalCriticalSection->LockCount;
+
+ // Make sure the waiter increment is compatible with the
+ // awakened waiter bit value
+ _ASSERTE(PALCS_LOCK_WAITER_INC == lInc ||
+ PALCS_LOCK_AWAKENED_WAITER & lVal);
+
+ if (0 == (lVal & PALCS_LOCK_BIT))
+ {
+ // the CS is no longer locked, let's bail out
+ return PalCsWaiterDidntWait;
+ }
+
+ lNewVal = lVal + lInc;
+
+ // Make sure that this thread was whether an incoming one or it
+ // was an awakened waiter and, in this case, we are now going to
+ // turn off the awakened waiter bit
+ _ASSERT_MSG(PALCS_LOCK_WAITER_INC == lInc ||
+ 0 == (PALCS_LOCK_AWAKENED_WAITER & lNewVal));
+
+ CS_TRACE("[WCS %p] Switching from {%d, %d, %d} to "
+ "{%d, %d, %d} ==> ", pPalCriticalSection,
+ PALCS_GETWCOUNT(lVal), PALCS_GETAWBIT(lVal), PALCS_GETLBIT(lVal),
+ PALCS_GETWCOUNT(lNewVal), PALCS_GETAWBIT(lNewVal), PALCS_GETLBIT(lNewVal));
+
+ lNewVal = InterlockedCompareExchange (&pPalCriticalSection->LockCount,
+ lNewVal, lVal);
+
+ CS_TRACE("[WCS %p] ==> %s\n", pPalCriticalSection,
+ (lNewVal == lVal) ? "OK" : "NO");
+
+ } while (lNewVal != lVal);
+
+#ifdef _DEBUG
+ if (NULL != pPalCriticalSection->DebugInfo)
+ {
+ pPalCriticalSection->DebugInfo->lContentionCount += 1;
+ }
+#endif // _DEBUG
+
+ // Do the actual native wait
+ palErr = PALCS_DoActualWait(pPalCriticalSection);
+ _ASSERT_MSG(NO_ERROR == palErr, "Native CS wait failed\n");
+
+ return PalCsReturnWaiterAwakened;
+ }
+
+ /*++
+ Function:
+ CorUnix::PALCS_DoActualWait
+
+ Performs the actual native wait on the CS
+ --*/
+ PAL_ERROR PALCS_DoActualWait(PAL_CRITICAL_SECTION * pPalCriticalSection)
+ {
+ int iRet;
+ PAL_ERROR palErr = NO_ERROR;
+
+ CS_TRACE("Trying to go to sleep [CS=%p]\n", pPalCriticalSection);
+
+ // Lock the mutex
+ iRet = pthread_mutex_lock(&pPalCriticalSection->csndNativeData.mutex);
+ if (0 != iRet)
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ goto PCDAW_exit;
+ }
+
+ CS_TRACE("Actually Going to sleep [CS=%p]\n", pPalCriticalSection);
+
+ while (0 == pPalCriticalSection->csndNativeData.iPredicate)
+ {
+ // Wait on the condition
+ iRet = pthread_cond_wait(&pPalCriticalSection->csndNativeData.condition,
+ &pPalCriticalSection->csndNativeData.mutex);
+
+ CS_TRACE("Got a signal on condition [pred=%d]!\n",
+ pPalCriticalSection->csndNativeData.iPredicate);
+ if (0 != iRet)
+ {
+ // Failed: unlock the mutex and bail out
+ ASSERT("Failed waiting on condition in CS %p [err=%d]\n",
+ pPalCriticalSection, iRet);
+ pthread_mutex_unlock(&pPalCriticalSection->csndNativeData.mutex);
+ palErr = ERROR_INTERNAL_ERROR;
+ goto PCDAW_exit;
+ }
+ }
+
+ // Reset the predicate
+ pPalCriticalSection->csndNativeData.iPredicate = 0;
+
+ // Unlock the mutex
+ iRet = pthread_mutex_unlock(&pPalCriticalSection->csndNativeData.mutex);
+ if (0 != iRet)
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ goto PCDAW_exit;
+ }
+
+ PCDAW_exit:
+
+ CS_TRACE("Just woken up [CS=%p]\n", pPalCriticalSection);
+
+ return palErr;
+ }
+
+ /*++
+ Function:
+ CorUnix::PALCS_WakeUpWaiter
+
+ Wakes up the first thread waiting on the CS
+ --*/
+ PAL_ERROR PALCS_WakeUpWaiter(PAL_CRITICAL_SECTION * pPalCriticalSection)
+ {
+ int iRet;
+ PAL_ERROR palErr = NO_ERROR;
+
+ _ASSERT_MSG(PalCsFullyInitialized == pPalCriticalSection->cisInitState,
+ "Trying to wake up a waiter on CS not fully initialized\n");
+
+ // Lock the mutex
+ iRet = pthread_mutex_lock(&pPalCriticalSection->csndNativeData.mutex);
+ if (0 != iRet)
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ goto PCWUW_exit;
+ }
+
+ // Set the predicate
+ pPalCriticalSection->csndNativeData.iPredicate = 1;
+
+ CS_TRACE("Signaling condition/predicate [pred=%d]!\n",
+ pPalCriticalSection->csndNativeData.iPredicate);
+
+ // Signal the condition
+ iRet = pthread_cond_signal(&pPalCriticalSection->csndNativeData.condition);
+ if (0 != iRet)
+ {
+ // Failed: set palErr, but continue in order to unlock
+ // the mutex anyway
+ ASSERT("Failed setting condition in CS %p [ret=%d]\n",
+ pPalCriticalSection, iRet);
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+
+ // Unlock the mutex
+ iRet = pthread_mutex_unlock(&pPalCriticalSection->csndNativeData.mutex);
+ if (0 != iRet)
+ {
+ palErr = ERROR_INTERNAL_ERROR;
+ goto PCWUW_exit;
+ }
+
+ PCWUW_exit:
+ return palErr;
+ }
+
+#ifdef _DEBUG
+ /*++
+ Function:
+ CorUnix::PALCS_ReportStatisticalData
+
+ Report creation/acquisition/contention statistical data for the all the
+ CSs so far existed and no longer existing in the current process
+ --*/
+ void PALCS_ReportStatisticalData()
+ {
+#ifdef PAL_TRACK_CRITICAL_SECTIONS_DATA
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ if (NULL == pThread) DebugBreak();
+
+ // Take the lock for the global list of CS debug infos
+ InternalEnterCriticalSection(pThread, (CRITICAL_SECTION*)&g_csPALCSsListLock);
+
+ LONG lPALCSInitializeCount = g_lPALCSInitializeCount;
+ LONG lPALCSDeleteCount = g_lPALCSDeleteCount;
+ LONG lPALCSAcquireCount = g_lPALCSAcquireCount;
+ LONG lPALCSEnterCount = g_lPALCSEnterCount;
+ LONG lPALCSContentionCount = g_lPALCSContentionCount;
+ LONG lPALCSInternalInitializeCount = g_lPALCSInternalInitializeCount;
+ LONG lPALCSInternalDeleteCount = g_lPALCSInternalDeleteCount;
+ LONG lPALCSInternalAcquireCount = g_lPALCSInternalAcquireCount;
+ LONG lPALCSInternalEnterCount = g_lPALCSInternalEnterCount;
+ LONG lPALCSInternalContentionCount = g_lPALCSInternalContentionCount;
+
+ PLIST_ENTRY pItem = g_PALCSList.Flink;
+ while (&g_PALCSList != pItem)
+ {
+ PCRITICAL_SECTION_DEBUG_INFO pDebugInfo =
+ (PCRITICAL_SECTION_DEBUG_INFO)pItem;
+
+ if (pDebugInfo->pOwnerCS->fInternal)
+ {
+ lPALCSInternalAcquireCount += pDebugInfo->lAcquireCount;
+ lPALCSInternalEnterCount += pDebugInfo->lEnterCount;
+ lPALCSInternalContentionCount += pDebugInfo->lContentionCount;
+ }
+ else
+ {
+ lPALCSAcquireCount += pDebugInfo->lAcquireCount;
+ lPALCSEnterCount += pDebugInfo->lEnterCount;
+ lPALCSContentionCount += pDebugInfo->lContentionCount;
+ }
+
+ pItem = pItem->Flink;
+ }
+
+ // Release the lock for the global list of CS debug infos
+ InternalLeaveCriticalSection(pThread, (CRITICAL_SECTION*)&g_csPALCSsListLock);
+
+ TRACE("Critical Sections Statistical Data:\n");
+ TRACE("{\n");
+ TRACE(" Client code CSs:\n");
+ TRACE(" {\n");
+ TRACE(" Initialize Count: %d\n", lPALCSInitializeCount);
+ TRACE(" Delete Count: %d\n", lPALCSDeleteCount);
+ TRACE(" Acquire Count: %d\n", lPALCSAcquireCount);
+ TRACE(" Enter Count: %d\n", lPALCSEnterCount);
+ TRACE(" Contention Count: %d\n", lPALCSContentionCount);
+ TRACE(" }\n");
+ TRACE(" Internal PAL CSs:\n");
+ TRACE(" {\n");
+ TRACE(" Initialize Count: %d\n", lPALCSInternalInitializeCount);
+ TRACE(" Delete Count: %d\n", lPALCSInternalDeleteCount);
+ TRACE(" Acquire Count: %d\n", lPALCSInternalAcquireCount);
+ TRACE(" Enter Count: %d\n", lPALCSInternalEnterCount);
+ TRACE(" Contention Count: %d\n", lPALCSInternalContentionCount);
+ TRACE(" }\n");
+ TRACE("}\n");
+#endif // PAL_TRACK_CRITICAL_SECTIONS_DATA
+ }
+
+ /*++
+ Function:
+ CorUnix::PALCS_DumpCSList
+
+ Dumps the list of all the CS currently existing in this process.
+ --*/
+ void PALCS_DumpCSList()
+ {
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ // Take the lock for the global list of CS debug infos
+ InternalEnterCriticalSection(pThread, (CRITICAL_SECTION*)&g_csPALCSsListLock);
+
+ PLIST_ENTRY pItem = g_PALCSList.Flink;
+ while (&g_PALCSList != pItem)
+ {
+ PCRITICAL_SECTION_DEBUG_INFO pDebugInfo =
+ (PCRITICAL_SECTION_DEBUG_INFO)pItem;
+ PPAL_CRITICAL_SECTION pCS = pDebugInfo->pOwnerCS;
+
+ printf("CS @ %p \n"
+ "{\tDebugInfo = %p -> \n",
+ pCS, pDebugInfo);
+
+ printf("\t{\n\t\t[Link]\n\t\tpOwnerCS = %p\n"
+ "\t\tAcquireCount \t= %d\n"
+ "\t\tEnterCount \t= %d\n"
+ "\t\tContentionCount = %d\n",
+ pDebugInfo->pOwnerCS, pDebugInfo->lAcquireCount.Load(),
+ pDebugInfo->lEnterCount.Load(), pDebugInfo->lContentionCount.Load());
+ printf("\t}\n");
+
+ printf("\tLockCount \t= %#x\n"
+ "\tRecursionCount \t= %d\n"
+ "\tOwningThread \t= %p\n"
+ "\tLockSemaphore \t= %p\n"
+ "\tSpinCount \t= %u\n"
+ "\tfInternal \t= %d\n"
+ "\teInitState \t= %u\n"
+ "\tpNativeData \t= %p ->\n",
+ pCS->LockCount.Load(), pCS->RecursionCount, (void *)pCS->OwningThread,
+ pCS->LockSemaphore, (unsigned)pCS->SpinCount, (int)pCS->fInternal,
+ pCS->cisInitState.Load(), &pCS->csndNativeData);
+
+ printf("\t{\n\t\t[mutex]\n\t\t[condition]\n"
+ "\t\tPredicate \t= %d\n"
+ "\t}\n}\n",pCS->csndNativeData.iPredicate);
+
+ printf("}\n");
+
+ pItem = pItem->Flink;
+ }
+
+ // Release the lock for the global list of CS debug infos
+ InternalLeaveCriticalSection(pThread, (CRITICAL_SECTION*)&g_csPALCSsListLock);
+ }
+#endif // _DEBUG
+
+
+#if defined(MUTEX_BASED_CSS) || defined(_DEBUG)
+ /*++
+ Function:
+ CorUnix::InternalEnterCriticalSection
+
+ Enters a CS, causing the thread to block if the CS is owned by
+ another thread
+ --*/
+#ifdef MUTEX_BASED_CSS
+ void InternalEnterCriticalSection(
+ CPalThread * pThread,
+ PCRITICAL_SECTION pCriticalSection)
+#else // MUTEX_BASED_CSS
+ void MTX_InternalEnterCriticalSection(
+ CPalThread * pThread,
+ PCRITICAL_SECTION pCriticalSection)
+#endif // MUTEX_BASED_CSS
+
+ {
+ PAL_CRITICAL_SECTION * pPalCriticalSection =
+ reinterpret_cast<PAL_CRITICAL_SECTION*>(pCriticalSection);
+ int iRet;
+ SIZE_T threadId;
+
+ _ASSERTE(PalCsNotInitialized != pPalCriticalSection->cisInitState);
+
+ threadId = ObtainCurrentThreadId(pThread);
+
+ /* check if the current thread already owns the criticalSection */
+ if (pPalCriticalSection->OwningThread == threadId)
+ {
+ _ASSERTE(0 < pPalCriticalSection->RecursionCount);
+ pPalCriticalSection->RecursionCount += 1;
+ return;
+ }
+
+ iRet = pthread_mutex_lock(&pPalCriticalSection->csndNativeData.mutex);
+ _ASSERTE(0 == iRet);
+
+ pPalCriticalSection->OwningThread = threadId;
+ pPalCriticalSection->RecursionCount = 1;
+ }
+
+
+ /*++
+ Function:
+ CorUnix::InternalLeaveCriticalSection
+
+ Leaves a currently owned CS
+ --*/
+#ifdef MUTEX_BASED_CSS
+ void InternalLeaveCriticalSection(
+ CPalThread * pThread,
+ PCRITICAL_SECTION pCriticalSection)
+#else // MUTEX_BASED_CSS
+ void MTX_InternalLeaveCriticalSection(
+ CPalThread * pThread,
+ PCRITICAL_SECTION pCriticalSection)
+#endif // MUTEX_BASED_CSS
+ {
+ PAL_CRITICAL_SECTION * pPalCriticalSection =
+ reinterpret_cast<PAL_CRITICAL_SECTION*>(pCriticalSection);
+ int iRet;
+#ifdef _DEBUG
+ SIZE_T threadId;
+
+ _ASSERTE(PalCsNotInitialized != pPalCriticalSection->cisInitState);
+
+ threadId = ObtainCurrentThreadId(pThread);
+ _ASSERTE(threadId == pPalCriticalSection->OwningThread);
+
+ if (0 >= pPalCriticalSection->RecursionCount)
+ DebugBreak();
+
+ _ASSERTE(0 < pPalCriticalSection->RecursionCount);
+#endif // _DEBUG
+
+ if (0 < --pPalCriticalSection->RecursionCount)
+ return;
+
+ pPalCriticalSection->OwningThread = 0;
+
+ iRet = pthread_mutex_unlock(&pPalCriticalSection->csndNativeData.mutex);
+ _ASSERTE(0 == iRet);
+ }
+
+ /*++
+ Function:
+ CorUnix::InternalTryEnterCriticalSection
+
+ Tries to acquire a CS. It returns true on success, false if the CS is
+ locked by another thread
+ --*/
+#ifdef MUTEX_BASED_CSS
+ bool InternalTryEnterCriticalSection(
+ CPalThread * pThread,
+ PCRITICAL_SECTION pCriticalSection)
+#else // MUTEX_BASED_CSS
+ bool MTX_InternalTryEnterCriticalSection(
+ CPalThread * pThread,
+ PCRITICAL_SECTION pCriticalSection)
+#endif // MUTEX_BASED_CSS
+ {
+ PAL_CRITICAL_SECTION * pPalCriticalSection =
+ reinterpret_cast<PAL_CRITICAL_SECTION*>(pCriticalSection);
+ bool fRet;
+ SIZE_T threadId;
+
+ _ASSERTE(PalCsNotInitialized != pPalCriticalSection->cisInitState);
+
+ threadId = ObtainCurrentThreadId(pThread);
+
+ /* check if the current thread already owns the criticalSection */
+ if (pPalCriticalSection->OwningThread == threadId)
+ {
+ pPalCriticalSection->RecursionCount += 1;
+ fRet = true;
+ goto ITECS_exit;
+ }
+
+ fRet = (0 == pthread_mutex_trylock(&pPalCriticalSection->csndNativeData.mutex));
+
+ if (fRet)
+ {
+ pPalCriticalSection->OwningThread = threadId;
+ pPalCriticalSection->RecursionCount = 1;
+ }
+
+ ITECS_exit:
+ return fRet;
+ }
+#endif // MUTEX_BASED_CSS || _DEBUG
+}
diff --git a/src/pal/src/synchmgr/synchcontrollers.cpp b/src/pal/src/synchmgr/synchcontrollers.cpp
new file mode 100644
index 0000000000..f7df5ea364
--- /dev/null
+++ b/src/pal/src/synchmgr/synchcontrollers.cpp
@@ -0,0 +1,1976 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ synchcontrollers.cpp
+
+Abstract:
+ Implementation of Synchronization Controllers and related objects
+
+
+
+--*/
+
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(SYNC); // some headers have code with asserts, so do this first
+
+#include "synchmanager.hpp"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sched.h>
+#include <errno.h>
+#include <limits.h>
+
+namespace CorUnix
+{
+#ifdef SYNCH_STATISTICS
+ LONG g_rglStatWaitCount[ObjectTypeIdCount] = { 0 };
+ LONG g_rglStatContentionCount[ObjectTypeIdCount] = { 0 };
+#endif // SYNCH_STATISTICS
+ ////////////////////////////
+ // //
+ // CSynchControllerBase //
+ // //
+ ////////////////////////////
+
+ /*++
+ Method:
+ CSynchControllerBase::Init
+
+ Initializes a generic controller
+ --*/
+ PAL_ERROR CSynchControllerBase::Init(
+ CPalThread * pthrCurrent,
+ ControllerType ctCtrlrType,
+ ObjectDomain odObjectDomain,
+ CObjectType *potObjectType,
+ CSynchData * psdSynchData,
+ WaitDomain wdWaitDomain)
+ {
+ VALIDATEOBJECT(psdSynchData);
+
+ _ASSERTE(InternalGetCurrentThread() == pthrCurrent);
+
+ // Initialize internal controller data
+ m_pthrOwner = pthrCurrent;
+ m_ctCtrlrType = ctCtrlrType;
+ m_odObjectDomain = odObjectDomain;
+ m_potObjectType = potObjectType;
+ m_psdSynchData = psdSynchData;
+ m_wdWaitDomain = wdWaitDomain;
+
+ // Add reference to target synch data
+ m_psdSynchData->AddRef();
+
+ // Acquire lock implied by the controller
+ CPalSynchronizationManager::AcquireLocalSynchLock(m_pthrOwner);
+ if (LocalWait != m_wdWaitDomain)
+ {
+ CPalSynchronizationManager::AcquireSharedSynchLock(m_pthrOwner);
+ }
+
+ return NO_ERROR;
+ }
+
+ /*++
+ Method:
+ CSynchControllerBase::Release
+
+ Releases a generic controller a return it to the appropriate cache
+ --*/
+ void CSynchControllerBase::Release()
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+#ifdef _DEBUG
+ ThreadWaitInfo * ptwiWaitInfo =
+ CPalSynchronizationManager::GetThreadWaitInfo(m_pthrOwner);
+#endif // _DEBUG
+
+ CPalSynchronizationManager * pSynchManager =
+ CPalSynchronizationManager::GetInstance();
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERTE(ptwiWaitInfo->pthrOwner == m_pthrOwner);
+
+ // Release reference to target synch data
+ m_psdSynchData->Release(m_pthrOwner);
+
+ // Release lock implied by the controller
+ if (LocalWait != m_wdWaitDomain)
+ {
+ CPalSynchronizationManager::ReleaseSharedSynchLock(m_pthrOwner);
+ }
+ CPalSynchronizationManager::ReleaseLocalSynchLock(m_pthrOwner);
+
+ // Return controller to the appropriate cache
+ if (WaitController == m_ctCtrlrType)
+ {
+ // The cast here must be static_cast and not reinterpet_cast.
+ // In fact in general static_cast<CSynchWaitController*>(this) is
+ // equal to this-sizeof(void*), given that CSynchWaitController
+ // has a virtual table, while CSynchControllerBase doesn't.
+ pSynchManager->CacheAddWaitCtrlr(m_pthrOwner,
+ static_cast<CSynchWaitController*>(this));
+ }
+ else
+ {
+ // The cast here must be static_cast and not reinterpet_cast
+ pSynchManager->CacheAddStateCtrlr(m_pthrOwner,
+ static_cast<CSynchStateController*>(this));
+ }
+ }
+
+ ////////////////////////////
+ // //
+ // CSynchWaitController //
+ // //
+ ////////////////////////////
+
+ /*++
+ Method:
+ CSynchWaitController::CanThreadWaitWithoutBlocking
+
+ Returns whether or not the thread owning this controller can
+ wait on the target object without blocking (i.e. the objet is
+ signaled)
+ --*/
+ PAL_ERROR CSynchWaitController::CanThreadWaitWithoutBlocking(
+ bool * pfCanWaitWithoutBlocking,
+ bool * pfAbandoned)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ bool fRetVal = false;
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+ _ASSERTE(NULL != pfCanWaitWithoutBlocking);
+ _ASSERTE(NULL != pfAbandoned);
+
+ fRetVal = m_psdSynchData->CanWaiterWaitWithoutBlocking(m_pthrOwner, pfAbandoned);
+
+ if(!fRetVal && otiProcess == m_psdSynchData->GetObjectTypeId())
+ {
+ // Note: if the target object is a process, here we need to check
+ // whether or not it has already exited. In fact, since currently
+ // we do not monitor a process status as long as there is no
+ // thread waiting on it, in general if the process already exited
+ // the process object is likely not to be signaled yet, therefore
+ // the above CanWaiterWaitWithoutBlocking call probably returned
+ // false, and, without the check below, that would cause the
+ // current thread to eventually go to sleep for a short time
+ // (until the worker thread notifies that the waited process has
+ // indeed exited), while it would not be necessary.
+ // As side effect that would cause a WaitForSingleObject with zero
+ // timeout to always return WAIT_TIMEOUT, even though the target
+ // process already exited. WaitForSingleObject with zero timeout
+ // is a common way to probe whether or not a process has already
+ // exited, and it is supposed to return WAIT_OBJECT_0 if the
+ // process exited, and WAIT_TIMEOUT if it is still active.
+ // In order to support this feature we need to check at this time
+ // whether or not the process has already exited.
+
+ CProcProcessLocalData * pProcLocalData = GetProcessLocalData();
+ DWORD dwExitCode = 0;
+ bool fIsActualExitCode = false;
+
+ _ASSERT_MSG(NULL != pProcLocalData,
+ "Process synch data pointer is missing\n");
+
+ if (NULL != pProcLocalData &&
+ CPalSynchronizationManager::HasProcessExited(pProcLocalData->dwProcessId,
+ &dwExitCode,
+ &fIsActualExitCode))
+ {
+ TRACE("Process pid=%u exited with %s exitcode=%u\n",
+ pProcLocalData->dwProcessId,
+ fIsActualExitCode ? "actual" : "guessed",
+ dwExitCode);
+
+ // Store the exit code in the process local data
+ if (fIsActualExitCode)
+ {
+ pProcLocalData->dwExitCode = dwExitCode;
+ }
+
+ // Set process status to PS_DONE
+ pProcLocalData->ps = PS_DONE;
+
+ // Set signal count
+ m_psdSynchData->SetSignalCount(1);
+
+ // Releasing all local waiters
+ // (see comments in DoMonitorProcesses)
+ m_psdSynchData->ReleaseAllLocalWaiters(m_pthrOwner);
+
+ fRetVal = true;
+ }
+ }
+
+ *pfCanWaitWithoutBlocking = fRetVal;
+ return NO_ERROR;
+ }
+
+ /*++
+ Method:
+ CSynchWaitController::ReleaseWaitingThreadWithoutBlocking
+
+ Performs all the steps needed to be done by the controller's owner
+ thread in order to wait on the target object without blocking
+ (e.g. modifying the object signal count accordingly with its
+ thread release semantics)
+ This method should be called only after having received positive
+ response from CanThreadWaitWithoutBlocking called on the same
+ controller.
+ --*/
+ PAL_ERROR CSynchWaitController::ReleaseWaitingThreadWithoutBlocking()
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ PAL_ERROR palErr = NO_ERROR;
+
+ _ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
+
+ palErr = m_psdSynchData->ReleaseWaiterWithoutBlocking(m_pthrOwner, m_pthrOwner);
+
+#ifdef SYNCH_STATISTICS
+ if (NO_ERROR == palErr)
+ {
+ m_psdSynchData->IncrementStatWaitCount();
+ }
+#endif
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CSynchWaitController::RegisterWaitingThread
+
+ Registers the controller's owner thread for waiting on the target
+ object
+ --*/
+ PAL_ERROR CSynchWaitController::RegisterWaitingThread(
+ WaitType wtWaitType,
+ DWORD dwIndex,
+ bool fAlertable)
+ {
+ VALIDATEOBJECT(m_psdSynchData);
+
+ PAL_ERROR palErr = NO_ERROR;
+ WaitingThreadsListNode * pwtlnNewNode = NULL;
+ SharedID shridNewNode = NULLSharedID;
+ 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 && (NULLSharedID != 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 = NULLSharedID;
+ 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);
+ ptwiWaitInfo->lSharedObjCount += 1;
+ }
+ else
+ {
+ m_psdSynchData->WaiterEnqueue(pwtlnNewNode);
+ }
+
+ // 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) && (NULLSharedID != 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 && (NULLSharedID == 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());
+
+ _ASSERT_MSG(otiMutex != m_otiObjectTypeId || m_lSignalCount <= 1,
+ "Mutex with invalid singal count\n");
+
+ 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 = NULLSharedID, shridNextItem = NULLSharedID;
+ 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 = NULLSharedID, shridNextItem = NULLSharedID;
+ 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)
+ {
+ 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");
+
+ 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;
+ }
+
+ 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)
+ {
+ VALIDATEOBJECT(this);
+
+ _ASSERT_MSG(SharedObject == GetObjectDomain(),
+ "Trying to enqueue a WaitingThreadsListNode as shared "
+ "on a local object\n");
+
+ 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 = NULLSharedID;
+ if (NULL == pwtlnCurrLast)
+ {
+ _ASSERT_MSG(NULLSharedID == m_ptrWTLHead.shrid,
+ "Corrupted waiting list on shared CSynchData at "
+ "{shrid=%p, p=%p}\n", m_shridThis, this);
+
+ pwtlnNewNode->ptrPrev.shrid = NULLSharedID;
+ m_ptrWTLHead.shrid = shridNewNode;
+ m_ptrWTLTail.shrid = shridNewNode;
+ }
+ else
+ {
+ VALIDATEOBJECT(pwtlnCurrLast);
+
+ pwtlnNewNode->ptrPrev.shrid = shridCurrLast;
+ pwtlnCurrLast->ptrNext.shrid = shridNewNode;
+ m_ptrWTLTail.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 0000000000..473918cb68
--- /dev/null
+++ b/src/pal/src/synchmgr/synchmanager.cpp
@@ -0,0 +1,4556 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ synchmanager.cpp
+
+Abstract:
+ Implementation of Synchronization Manager and related objects
+
+
+
+--*/
+
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(SYNC); // some headers have code with asserts, so do this first
+
+#include "synchmanager.hpp"
+#include "pal/file.hpp"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sched.h>
+#include <signal.h>
+#include <errno.h>
+#if HAVE_POLL
+#include <poll.h>
+#else
+#include "pal/fakepoll.h"
+#endif // HAVE_POLL
+
+// We use the synchronization manager's worker thread to handle
+// process termination requests. It does so by calling the
+// registered handler function.
+PTERMINATION_REQUEST_HANDLER g_terminationRequestHandler = NULL;
+
+// Set the handler for process termination requests.
+VOID PALAPI PAL_SetTerminationRequestHandler(
+ IN PTERMINATION_REQUEST_HANDLER terminationHandler)
+{
+ g_terminationRequestHandler = terminationHandler;
+}
+
+namespace CorUnix
+{
+ /////////////////////////////////
+ // //
+ // WaitingThreadsListNode //
+ // //
+ /////////////////////////////////
+#ifdef SYNCH_OBJECT_VALIDATION
+ _WaitingThreadsListNode::_WaitingThreadsListNode()
+ {
+ ValidateEmptyObject();
+ dwDebugHeadSignature = HeadSignature;
+ dwDebugTailSignature = TailSignature;
+ }
+ _WaitingThreadsListNode::~_WaitingThreadsListNode()
+ {
+ ValidateObject();
+ InvalidateObject();
+ }
+ void _WaitingThreadsListNode::ValidateObject()
+ {
+ TRACE("Verifying WaitingThreadsListNode @ %p\n", this);
+ _ASSERT_MSG(HeadSignature == dwDebugHeadSignature,
+ "WaitingThreadsListNode header signature corruption [p=%p]",
+ this);
+ _ASSERT_MSG(TailSignature == dwDebugTailSignature,
+ "WaitingThreadsListNode trailer signature corruption [p=%p]",
+ this);
+ }
+ void _WaitingThreadsListNode::ValidateEmptyObject()
+ {
+ _ASSERT_MSG(HeadSignature != dwDebugHeadSignature,
+ "WaitingThreadsListNode header previously signed [p=%p]",
+ this);
+ _ASSERT_MSG(TailSignature != dwDebugTailSignature,
+ "WaitingThreadsListNode trailer previously signed [p=%p]",
+ this);
+ }
+ void _WaitingThreadsListNode::InvalidateObject()
+ {
+ TRACE("Invalidating WaitingThreadsListNode @ %p\n", this);
+ dwDebugHeadSignature = EmptySignature;
+ dwDebugTailSignature = EmptySignature;
+ }
+#endif // SYNCH_OBJECT_VALIDATION
+
+ //////////////////////////////
+ // //
+ // CPalSynchMgrController //
+ // //
+ //////////////////////////////
+
+ /*++
+ Method:
+ CPalSynchMgrController::CreatePalSynchronizationManager
+
+ Creates the Synchronization Manager. It must be called once per process.
+ --*/
+ IPalSynchronizationManager * CPalSynchMgrController::CreatePalSynchronizationManager()
+ {
+ return CPalSynchronizationManager::CreatePalSynchronizationManager();
+ };
+
+ /*++
+ Method:
+ CPalSynchMgrController::StartWorker
+
+ Starts the Synchronization Manager's Worker Thread
+ --*/
+ PAL_ERROR CPalSynchMgrController::StartWorker(
+ CPalThread * pthrCurrent)
+ {
+ return CPalSynchronizationManager::StartWorker(pthrCurrent);
+ }
+
+ /*++
+ Method:
+ CPalSynchMgrController::PrepareForShutdown
+
+ This method performs the part of Synchronization Manager's shutdown that
+ needs to be carried out when core PAL subsystems are still active
+ --*/
+ PAL_ERROR CPalSynchMgrController::PrepareForShutdown()
+ {
+ return CPalSynchronizationManager::PrepareForShutdown();
+ }
+
+ //////////////////////////////////
+ // //
+ // CPalSynchronizationManager //
+ // //
+ //////////////////////////////////
+
+ IPalSynchronizationManager * g_pSynchronizationManager = NULL;
+
+ CPalSynchronizationManager * CPalSynchronizationManager::s_pObjSynchMgr = NULL;
+ Volatile<LONG> CPalSynchronizationManager::s_lInitStatus = SynchMgrStatusIdle;
+ CRITICAL_SECTION CPalSynchronizationManager::s_csSynchProcessLock;
+ CRITICAL_SECTION CPalSynchronizationManager::s_csMonitoredProcessesLock;
+
+ CPalSynchronizationManager::CPalSynchronizationManager()
+ : m_dwWorkerThreadTid(0),
+ m_pipoThread(NULL),
+ m_pthrWorker(NULL),
+ m_iProcessPipeRead(-1),
+ m_iProcessPipeWrite(-1),
+ m_pmplnMonitoredProcesses(NULL),
+ m_lMonitoredProcessesCount(0),
+ m_pmplnExitedNodes(NULL),
+ m_cacheWaitCtrlrs(CtrlrsCacheMaxSize),
+ m_cacheStateCtrlrs(CtrlrsCacheMaxSize),
+ m_cacheSynchData(SynchDataCacheMaxSize),
+ m_cacheSHRSynchData(SynchDataCacheMaxSize),
+ m_cacheWTListNodes(WTListNodeCacheMaxSize),
+ m_cacheSHRWTListNodes(WTListNodeCacheMaxSize),
+ m_cacheThreadApcInfoNodes(ApcInfoNodeCacheMaxSize),
+ 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 fRaceAlerted = false;
+ 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)
+ {
+ // If fIsSleep is true we are being called by Sleep/SleepEx
+ // and we need to switch the wait state to TWS_WAITING or
+ // TWS_ALERTABLE (according to fAlertable)
+
+ if (fAlertable)
+ {
+ // If we are in alertable mode we need to grab the lock to
+ // make sure that no APC is queued right before the
+ // InterlockedCompareExchange.
+ // If there are APCs queued at this time, no native wakeup
+ // will be posted, so we need to skip the native wait
+
+ // Lock
+ AcquireLocalSynchLock(pthrCurrent);
+ AcquireSharedSynchLock(pthrCurrent);
+
+ if (AreAPCsPending(pthrCurrent))
+ {
+ // APCs have been queued when the thread wait status was
+ // still TWS_ACTIVE, therefore the queueing thread will not
+ // post any native wakeup: we need to skip the actual
+ // native wait
+ fRaceAlerted = true;
+ }
+ }
+
+ if (!fRaceAlerted)
+ {
+ // Setting the thread in wait state
+ dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE : TWS_WAITING);
+
+ TRACE("Switching my wait state [%p] from TWS_ACTIVE to %u [current *pdwWaitState=%u]\n",
+ pdwWaitState, dwWaitState, *pdwWaitState);
+
+ dwWaitState = InterlockedCompareExchange((LONG *)pdwWaitState,
+ dwWaitState,
+ TWS_ACTIVE);
+
+ if ((DWORD)TWS_ACTIVE != dwWaitState)
+ {
+ if (fAlertable)
+ {
+ // Unlock
+ ReleaseSharedSynchLock(pthrCurrent);
+ ReleaseLocalSynchLock(pthrCurrent);
+ }
+
+ 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 %u\n", dwWaitState);
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+
+ goto BT_exit;
+ }
+ }
+
+ if (fAlertable)
+ {
+ // Unlock
+ ReleaseSharedSynchLock(pthrCurrent);
+ ReleaseLocalSynchLock(pthrCurrent);
+ }
+ }
+
+ if (fRaceAlerted)
+ {
+ twrWakeupReason = Alerted;
+ }
+ else
+ {
+ 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);
+ 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);
+ }
+
+ // Abandon owned named mutexes
+ while (true)
+ {
+ NamedMutexProcessData *processData = pSynchInfo->RemoveFirstOwnedNamedMutex();
+ if (processData == nullptr)
+ {
+ break;
+ }
+ processData->Abandon();
+ }
+
+ 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);
+ DiscardAllPendingAPCs(pthrCurrent, pthrTarget);
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::GetSynchWaitControllersForObjects
+
+ Returns an array of wait controllers, one for each of the objects
+ in rgObjects
+ --*/
+ PAL_ERROR CPalSynchronizationManager::GetSynchWaitControllersForObjects(
+ CPalThread *pthrCurrent,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ ISynchWaitController * rgControllers[])
+ {
+ return GetSynchControllersForObjects(pthrCurrent,
+ rgObjects,
+ dwObjectCount,
+ (void **)rgControllers,
+ CSynchControllerBase::WaitController);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::GetSynchStateControllersForObjects
+
+ Returns an array of state controllers, one for each of the objects
+ in rgObjects
+ --*/
+ PAL_ERROR CPalSynchronizationManager::GetSynchStateControllersForObjects(
+ CPalThread *pthrCurrent,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ ISynchStateController *rgControllers[])
+ {
+ return GetSynchControllersForObjects(pthrCurrent,
+ rgObjects,
+ dwObjectCount,
+ (void **)rgControllers,
+ CSynchControllerBase::StateController);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::GetSynchControllersForObjects
+
+ Internal common implementation for GetSynchWaitControllersForObjects and
+ GetSynchStateControllersForObjects
+ --*/
+ PAL_ERROR CPalSynchronizationManager::GetSynchControllersForObjects(
+ CPalThread *pthrCurrent,
+ IPalObject *rgObjects[],
+ DWORD dwObjectCount,
+ void ** ppvControllers,
+ CSynchControllerBase::ControllerType ctCtrlrType)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ unsigned int uIdx, uCount = 0, uSharedObjectCount = 0;
+ WaitDomain wdWaitDomain = LocalWait;
+ CObjectType * potObjectType = NULL;
+ unsigned int uErrCleanupIdxFirstNotInitializedCtrlr = 0;
+ unsigned int uErrCleanupIdxLastCtrlr = 0;
+ bool fLocalSynchLock = false;
+
+ union
+ {
+ CSynchWaitController * pWaitCtrlrs[MAXIMUM_WAIT_OBJECTS];
+ CSynchStateController * pStateCtrlrs[MAXIMUM_WAIT_OBJECTS];
+ } Ctrlrs;
+
+ if ((dwObjectCount <= 0) || (dwObjectCount > MAXIMUM_WAIT_OBJECTS))
+ {
+ palErr = ERROR_INVALID_PARAMETER;
+ goto GSCFO_exit;
+ }
+
+ if (CSynchControllerBase::WaitController == ctCtrlrType)
+ {
+ uCount = (unsigned int)m_cacheWaitCtrlrs.Get(pthrCurrent,
+ dwObjectCount,
+ Ctrlrs.pWaitCtrlrs);
+ }
+ else
+ {
+ uCount = (unsigned int)m_cacheStateCtrlrs.Get(pthrCurrent,
+ dwObjectCount,
+ Ctrlrs.pStateCtrlrs);
+ }
+
+ if (uCount < dwObjectCount)
+ {
+ // We got less controllers (uCount) than we asked for (dwObjectCount),
+ // probably because of low memory.
+ // None of these controllers is initialized, so they must be all
+ // returned directly to the cache
+ uErrCleanupIdxLastCtrlr = uCount;
+
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ goto GSCFO_error_cleanup;
+ }
+
+ //
+ // We need to acquire the local synch lock before evaluating object domains
+ //
+ AcquireLocalSynchLock(pthrCurrent);
+ fLocalSynchLock = true;
+
+ for (uIdx=0; uIdx<dwObjectCount; uIdx++)
+ {
+ if (SharedObject == rgObjects[uIdx]->GetObjectDomain())
+ {
+ ++uSharedObjectCount;
+ }
+
+ if (uSharedObjectCount > 0 && uSharedObjectCount <= uIdx)
+ {
+ wdWaitDomain = MixedWait;
+ break;
+ }
+ }
+
+ if (dwObjectCount == uSharedObjectCount)
+ {
+ wdWaitDomain = SharedWait;
+ }
+
+ for (uIdx=0;uIdx<dwObjectCount;uIdx++)
+ {
+ void * pvSData;
+ CSynchData * psdSynchData;
+ ObjectDomain odObjectDomain = rgObjects[uIdx]->GetObjectDomain();
+
+ palErr = rgObjects[uIdx]->GetObjectSynchData((void **)&pvSData);
+ if (NO_ERROR != palErr)
+ {
+ break;
+ }
+
+ psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(
+ CSynchData, reinterpret_cast<SharedID>(pvSData)) :
+ static_cast<CSynchData *>(pvSData);
+
+ VALIDATEOBJECT(psdSynchData);
+
+ potObjectType = rgObjects[uIdx]->GetObjectType();
+
+ if (CSynchControllerBase::WaitController == ctCtrlrType)
+ {
+ Ctrlrs.pWaitCtrlrs[uIdx]->Init(pthrCurrent,
+ ctCtrlrType,
+ odObjectDomain,
+ potObjectType,
+ psdSynchData,
+ wdWaitDomain);
+ }
+ else
+ {
+ Ctrlrs.pStateCtrlrs[uIdx]->Init(pthrCurrent,
+ ctCtrlrType,
+ odObjectDomain,
+ potObjectType,
+ psdSynchData,
+ wdWaitDomain);
+ }
+
+ if (CSynchControllerBase::WaitController == ctCtrlrType &&
+ otiProcess == potObjectType->GetId())
+ {
+ CProcProcessLocalData * pProcLocData;
+ IDataLock * pDataLock;
+
+ palErr = rgObjects[uIdx]->GetProcessLocalData(
+ pthrCurrent,
+ ReadLock,
+ &pDataLock,
+ (void **)&pProcLocData);
+
+ if (NO_ERROR != palErr)
+ {
+ // In case of failure here, bail out of the loop, but
+ // keep track (by incrementing the counter 'uIdx') of the
+ // fact that this controller has already being initialized
+ // and therefore need to be Release'd rather than just
+ // returned to the cache
+ uIdx++;
+ break;
+ }
+
+ Ctrlrs.pWaitCtrlrs[uIdx]->SetProcessData(rgObjects[uIdx], pProcLocData);
+ pDataLock->ReleaseLock(pthrCurrent, false);
+ }
+ }
+ if (NO_ERROR != palErr)
+ {
+ // An error occurred while initializing the (uIdx+1)-th controller,
+ // i.e. the one at index uIdx; therefore the first uIdx controllers
+ // must be Release'd, while the remaining uCount-uIdx must be returned
+ // directly to the cache.
+ uErrCleanupIdxFirstNotInitializedCtrlr = uIdx;
+ uErrCleanupIdxLastCtrlr = dwObjectCount;
+
+ goto GSCFO_error_cleanup;
+ }
+
+ // Succeeded
+ if (CSynchControllerBase::WaitController == ctCtrlrType)
+ {
+ for (uIdx=0;uIdx<dwObjectCount;uIdx++)
+ {
+ // The multiple cast is NEEDED, though currently it does not
+ // change the value ot the pointer. Anyway, if in the future
+ // a virtual method should be added to the base class
+ // CSynchControllerBase, both derived classes would have two
+ // virtual tables, therefore a static cast from, for instance,
+ // a CSynchWaitController* to a ISynchWaitController* would
+ // return the given pointer incremented by the size of a
+ // generic pointer on the specific platform
+ ppvControllers[uIdx] = reinterpret_cast<void *>(
+ static_cast<ISynchWaitController *>(Ctrlrs.pWaitCtrlrs[uIdx]));
+ }
+ }
+ else
+ {
+ for (uIdx=0;uIdx<dwObjectCount;uIdx++)
+ {
+ // See comment above
+ ppvControllers[uIdx] = reinterpret_cast<void *>(
+ static_cast<ISynchStateController *>(Ctrlrs.pStateCtrlrs[uIdx]));
+ }
+ }
+
+ // Succeeded: skip error cleanup
+ goto GSCFO_exit;
+
+ GSCFO_error_cleanup:
+ if (CSynchControllerBase::WaitController == ctCtrlrType)
+ {
+ // Release already initialized wait controllers
+ for (uIdx=0; uIdx<uErrCleanupIdxFirstNotInitializedCtrlr; uIdx++)
+ {
+ Ctrlrs.pWaitCtrlrs[uIdx]->Release();
+ }
+
+ // Return to the cache not yet initialized wait controllers
+ for (uIdx=uErrCleanupIdxFirstNotInitializedCtrlr; uIdx<uErrCleanupIdxLastCtrlr; uIdx++)
+ {
+ m_cacheWaitCtrlrs.Add(pthrCurrent, Ctrlrs.pWaitCtrlrs[uIdx]);
+ }
+ }
+ else
+ {
+ // Release already initialized state controllers
+ for (uIdx=0; uIdx<uErrCleanupIdxFirstNotInitializedCtrlr; uIdx++)
+ {
+ Ctrlrs.pStateCtrlrs[uIdx]->Release();
+ }
+
+ // Return to the cache not yet initialized state controllers
+ for (uIdx=uErrCleanupIdxFirstNotInitializedCtrlr; uIdx<uErrCleanupIdxLastCtrlr; uIdx++)
+ {
+ m_cacheStateCtrlrs.Add(pthrCurrent, Ctrlrs.pStateCtrlrs[uIdx]);
+ }
+ }
+
+ GSCFO_exit:
+ if (fLocalSynchLock)
+ {
+ ReleaseLocalSynchLock(pthrCurrent);
+ }
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::AllocateObjectSynchData
+
+ Returns a new SynchData for an object of given type and domain
+ --*/
+ PAL_ERROR CPalSynchronizationManager::AllocateObjectSynchData(
+ CObjectType *potObjectType,
+ ObjectDomain odObjectDomain,
+ VOID **ppvSynchData)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ CSynchData * psdSynchData = NULL;
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+
+ if (SharedObject == odObjectDomain)
+ {
+ SharedID shridSynchData = m_cacheSHRSynchData.Get(pthrCurrent);
+ if (NULLSharedID == shridSynchData)
+ {
+ ERROR("Unable to allocate shared memory\n");
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ psdSynchData = SharedIDToTypePointer(CSynchData, shridSynchData);
+
+ VALIDATEOBJECT(psdSynchData);
+
+ _ASSERT_MSG(NULL != psdSynchData, "Bad shared memory pointer\n");
+
+ // Initialize waiting list pointers
+ psdSynchData->SetWTLHeadShrPtr(NULLSharedID);
+ psdSynchData->SetWTLTailShrPtr(NULLSharedID);
+
+ // Store shared pointer to this object
+ psdSynchData->SetSharedThis(shridSynchData);
+
+ *ppvSynchData = reinterpret_cast<void *>(shridSynchData);
+ }
+ else
+ {
+ psdSynchData = m_cacheSynchData.Get(pthrCurrent);
+ if (NULL == psdSynchData)
+ {
+ ERROR("Unable to allocate memory\n");
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ // Initialize waiting list pointers
+ psdSynchData->SetWTLHeadPtr(NULL);
+ psdSynchData->SetWTLTailPtr(NULL);
+
+ // Set shared this pointer to NULL
+ psdSynchData->SetSharedThis(NULLSharedID);
+
+ *ppvSynchData = static_cast<void *>(psdSynchData);
+ }
+
+ // Initialize object domain and object type;
+ psdSynchData->SetObjectDomain(odObjectDomain);
+ psdSynchData->SetObjectType(potObjectType);
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::FreeObjectSynchData
+
+ Called to return a no longer used SynchData to the Synchronization Manager.
+ The SynchData may actually survive this call, since it is a ref-counted
+ object and at FreeObjectSynchData time it may still be used from within
+ the Synchronization Manager itself (e.g. the worker thread).
+ --*/
+ void CPalSynchronizationManager::FreeObjectSynchData(
+ CObjectType *potObjectType,
+ ObjectDomain odObjectDomain,
+ VOID *pvSynchData)
+ {
+ CSynchData * psdSynchData;
+ CPalThread * pthrCurrent = InternalGetCurrentThread();
+
+ if (odObjectDomain == SharedObject)
+ {
+ psdSynchData = SharedIDToTypePointer(CSynchData,
+ reinterpret_cast<SharedID>(pvSynchData));
+
+ if (NULL == psdSynchData)
+ {
+ ASSERT("Bad shared memory pointer\n");
+ return;
+ }
+ }
+ else
+ {
+ psdSynchData = static_cast<CSynchData *>(pvSynchData);
+ }
+
+ psdSynchData->Release(pthrCurrent);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::CreateSynchStateController
+
+ Creates a state controller for the given object
+ --*/
+ PAL_ERROR CPalSynchronizationManager::CreateSynchStateController(
+ CPalThread *pthrCurrent,
+ CObjectType *potObjectType,
+ VOID *pvSynchData,
+ ObjectDomain odObjectDomain,
+ ISynchStateController **ppStateController)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ CSynchStateController * pCtrlr = NULL;
+ WaitDomain wdWaitDomain = (SharedObject == odObjectDomain) ? SharedWait : LocalWait;
+ CSynchData * psdSynchData;
+
+ psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(CSynchData, reinterpret_cast<SharedID>(pvSynchData))
+ : static_cast<CSynchData *>(pvSynchData);
+
+ VALIDATEOBJECT(psdSynchData);
+
+ pCtrlr = m_cacheStateCtrlrs.Get(pthrCurrent);
+ if (NULL == pCtrlr)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ pCtrlr->Init(pthrCurrent,
+ CSynchControllerBase::StateController,
+ odObjectDomain,
+ potObjectType,
+ psdSynchData,
+ wdWaitDomain);
+
+ // Succeeded
+ *ppStateController = (ISynchStateController *)pCtrlr;
+
+ if (NO_ERROR != palErr)
+ {
+ m_cacheStateCtrlrs.Add(pthrCurrent, pCtrlr);
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::CreateSynchWaitController
+
+ Creates a wait controller for the given object
+ --*/
+ PAL_ERROR CPalSynchronizationManager::CreateSynchWaitController(
+ CPalThread *pthrCurrent,
+ CObjectType *potObjectType,
+ VOID *pvSynchData,
+ ObjectDomain odObjectDomain,
+ ISynchWaitController **ppWaitController)
+ {
+ CSynchWaitController * pCtrlr = NULL;
+ WaitDomain wdWaitDomain = (SharedObject == odObjectDomain) ? SharedWait : LocalWait;
+ CSynchData * psdSynchData;
+
+ psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(
+ CSynchData, reinterpret_cast<SharedID>(pvSynchData)) :
+ static_cast<CSynchData *>(pvSynchData);
+
+ VALIDATEOBJECT(psdSynchData);
+
+ pCtrlr = m_cacheWaitCtrlrs.Get(pthrCurrent);
+ if (NULL == pCtrlr)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ pCtrlr->Init(pthrCurrent,
+ CSynchControllerBase::WaitController,
+ odObjectDomain,
+ potObjectType,
+ psdSynchData,
+ wdWaitDomain);
+
+ // Succeeded
+ *ppWaitController = (ISynchWaitController *)pCtrlr;
+
+ return NO_ERROR;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::QueueUserAPC
+
+ Internal implementation of QueueUserAPC
+ --*/
+ PAL_ERROR CPalSynchronizationManager::QueueUserAPC(CPalThread * pthrCurrent,
+ CPalThread * pthrTarget,
+ PAPCFUNC pfnAPC,
+ ULONG_PTR uptrData)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ ThreadApcInfoNode * ptainNode = NULL;
+ DWORD dwWaitState;
+ DWORD * pdwWaitState;
+ ThreadWaitInfo * pTargetTWInfo = GetThreadWaitInfo(pthrTarget);
+ bool fLocalSynchLock = false;
+ bool fSharedSynchLock = false;
+ bool fThreadLock = false;
+
+ ptainNode = m_cacheThreadApcInfoNodes.Get(pthrCurrent);
+ if (NULL == ptainNode)
+ {
+ ERROR("No memory for new APCs linked list entry\n");
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ goto QUAPC_exit;
+ }
+
+ ptainNode->pfnAPC = pfnAPC;
+ ptainNode->pAPCData = uptrData;
+ ptainNode->pNext = NULL;
+
+ AcquireLocalSynchLock(pthrCurrent);
+ fLocalSynchLock = true;
+
+ if (LocalWait != pTargetTWInfo->wdWaitDomain)
+ {
+ AcquireSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = true;
+ }
+
+ pthrTarget->Lock(pthrCurrent);
+ fThreadLock = true;
+
+ if (TS_DONE == pthrTarget->synchronizationInfo.GetThreadState())
+ {
+ ERROR("Thread %#x has terminated; can't queue an APC on it\n",
+ pthrTarget->GetThreadId());
+ palErr = ERROR_INVALID_PARAMETER;
+ goto QUAPC_exit;
+ }
+ pdwWaitState = SharedIDToTypePointer(DWORD,
+ pthrTarget->synchronizationInfo.m_shridWaitAwakened);
+ if (TWS_EARLYDEATH == VolatileLoad(pdwWaitState))
+ {
+ ERROR("Thread %#x is about to be suspended for process shutdwon, "
+ "can't queue an APC on it\n", pthrTarget->GetThreadId());
+ palErr = ERROR_INVALID_PARAMETER;
+ goto QUAPC_exit;
+ }
+
+ if (NULL == pthrTarget->apcInfo.m_ptainTail)
+ {
+ _ASSERT_MSG(NULL == pthrTarget->apcInfo.m_ptainHead, "Corrupted APC list\n");
+
+ pthrTarget->apcInfo.m_ptainHead = ptainNode;
+ pthrTarget->apcInfo.m_ptainTail = ptainNode;
+ }
+ else
+ {
+ pthrTarget->apcInfo.m_ptainTail->pNext = ptainNode;
+ pthrTarget->apcInfo.m_ptainTail = ptainNode;
+ }
+
+ // Set ptainNode to NULL so it won't be readded to the cache
+ ptainNode = NULL;
+
+ TRACE("APC %p with parameter %p added to APC queue\n", pfnAPC, uptrData);
+
+ dwWaitState = InterlockedCompareExchange((LONG *)pdwWaitState,
+ (LONG)TWS_ACTIVE,
+ (LONG)TWS_ALERTABLE);
+
+ // Release thread lock
+ pthrTarget->Unlock(pthrCurrent);
+ fThreadLock = false;
+
+ if (TWS_ALERTABLE == dwWaitState)
+ {
+ // Unregister the wait
+ UnRegisterWait(pthrCurrent, pTargetTWInfo, fSharedSynchLock);
+
+ // Wake up target thread
+ palErr = WakeUpLocalThread(
+ pthrCurrent,
+ pthrTarget,
+ Alerted,
+ 0);
+
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Failed to wakeup local thread %#x for dispatching APCs [err=%u]\n",
+ pthrTarget->GetThreadId(), palErr);
+ }
+ }
+
+ QUAPC_exit:
+ if (fThreadLock)
+ {
+ pthrTarget->Unlock(pthrCurrent);
+ }
+
+ if (fSharedSynchLock)
+ {
+ ReleaseSharedSynchLock(pthrCurrent);
+ }
+
+ if (fLocalSynchLock)
+ {
+ ReleaseLocalSynchLock(pthrCurrent);
+ }
+
+ if (ptainNode)
+ {
+ m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode);
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::SendTerminationRequestToWorkerThread
+
+ Send a request to the worker thread to initiate process termination.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::SendTerminationRequestToWorkerThread()
+ {
+ PAL_ERROR palErr = GetInstance()->WakeUpLocalWorkerThread(SynchWorkerCmdTerminationRequest);
+ if (palErr != NO_ERROR)
+ {
+ ERROR("Failed to wake up worker thread [errno=%d {%s%}]\n",
+ errno, strerror(errno));
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::AreAPCsPending
+
+ Returns 'true' if there are APCs currently pending for the target
+ thread (normally the current one)
+ --*/
+ bool CPalSynchronizationManager::AreAPCsPending(
+ CPalThread * pthrTarget)
+ {
+ // No need to lock here
+ return (NULL != pthrTarget->apcInfo.m_ptainHead);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::DispatchPendingAPCs
+
+ Executes any pending APC for the current thread
+ --*/
+ PAL_ERROR CPalSynchronizationManager::DispatchPendingAPCs(
+ CPalThread * pthrCurrent)
+ {
+ ThreadApcInfoNode * ptainNode, * ptainLocalHead;
+ int iAPCsCalled = 0;
+
+ while (TRUE)
+ {
+ // Lock
+ pthrCurrent->Lock(pthrCurrent);
+ ptainLocalHead = pthrCurrent->apcInfo.m_ptainHead;
+ if (ptainLocalHead)
+ {
+ pthrCurrent->apcInfo.m_ptainHead = NULL;
+ pthrCurrent->apcInfo.m_ptainTail = NULL;
+ }
+
+ // Unlock
+ pthrCurrent->Unlock(pthrCurrent);
+
+ if (NULL == ptainLocalHead)
+ {
+ break;
+ }
+
+ while (ptainLocalHead)
+ {
+ ptainNode = ptainLocalHead;
+ ptainLocalHead = ptainNode->pNext;
+
+#if _ENABLE_DEBUG_MESSAGES_
+ // reset ENTRY nesting level back to zero while
+ // inside the callback ...
+ int iOldLevel = DBG_change_entrylevel(0);
+#endif /* _ENABLE_DEBUG_MESSAGES_ */
+
+ TRACE("Calling APC %p with parameter %#x\n",
+ ptainNode->pfnAPC, ptainNode->pfnAPC);
+
+ // Actual APC call
+ ptainNode->pfnAPC(ptainNode->pAPCData);
+
+#if _ENABLE_DEBUG_MESSAGES_
+ // ... and set nesting level back to what it was
+ DBG_change_entrylevel(iOldLevel);
+#endif /* _ENABLE_DEBUG_MESSAGES_ */
+
+ iAPCsCalled++;
+ m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode);
+ }
+ }
+
+ return (iAPCsCalled > 0) ? NO_ERROR : ERROR_NOT_FOUND;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::DiscardAllPendingAPCs
+
+ Discards any pending APC for the target pthrTarget thread
+ --*/
+ void CPalSynchronizationManager::DiscardAllPendingAPCs(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget)
+ {
+ ThreadApcInfoNode * ptainNode, * ptainLocalHead;
+
+ // Lock
+ pthrTarget->Lock(pthrCurrent);
+ ptainLocalHead = pthrTarget->apcInfo.m_ptainHead;
+ if (ptainLocalHead)
+ {
+ pthrTarget->apcInfo.m_ptainHead = NULL;
+ pthrTarget->apcInfo.m_ptainTail = NULL;
+ }
+
+ // Unlock
+ pthrTarget->Unlock(pthrCurrent);
+
+ while (ptainLocalHead)
+ {
+ ptainNode = ptainLocalHead;
+ ptainLocalHead = ptainNode->pNext;
+
+ m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode);
+ }
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::CreatePalSynchronizationManager
+
+ Creates the Synchronization Manager.
+ Private method, it is called only by CPalSynchMgrController.
+ --*/
+ IPalSynchronizationManager * CPalSynchronizationManager::CreatePalSynchronizationManager()
+ {
+ if (s_pObjSynchMgr != NULL)
+ {
+ ASSERT("Multiple PAL Synchronization manager initializations\n");
+ return NULL;
+ }
+
+ Initialize();
+ return static_cast<IPalSynchronizationManager *>(s_pObjSynchMgr);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::Initialize
+
+ Internal Synchronization Manager initialization
+ --*/
+ PAL_ERROR CPalSynchronizationManager::Initialize()
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ LONG lInit;
+ CPalSynchronizationManager * pSynchManager = NULL;
+
+ lInit = InterlockedCompareExchange(&s_lInitStatus,
+ (LONG)SynchMgrStatusInitializing,
+ (LONG)SynchMgrStatusIdle);
+
+ if ((LONG)SynchMgrStatusIdle != lInit)
+ {
+ ASSERT("Synchronization Manager already being initialized");
+ palErr = ERROR_INTERNAL_ERROR;
+ goto I_exit;
+ }
+
+ InternalInitializeCriticalSection(&s_csSynchProcessLock);
+ InternalInitializeCriticalSection(&s_csMonitoredProcessesLock);
+
+ pSynchManager = InternalNew<CPalSynchronizationManager>();
+ if (NULL == pSynchManager)
+ {
+ ERROR("Failed to allocate memory for Synchronization Manager");
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ goto I_exit;
+ }
+
+ if (!pSynchManager->CreateProcessPipe())
+ {
+ ERROR("Unable to create process pipe \n");
+ palErr = ERROR_OPEN_FAILED;
+ goto I_exit;
+ }
+
+ s_pObjSynchMgr = pSynchManager;
+
+ // Initialization was successful
+ g_pSynchronizationManager =
+ static_cast<IPalSynchronizationManager *>(pSynchManager);
+ s_lInitStatus = (LONG)SynchMgrStatusRunning;
+
+ I_exit:
+ if (NO_ERROR != palErr)
+ {
+ s_lInitStatus = (LONG)SynchMgrStatusError;
+ if (NULL != pSynchManager)
+ {
+ pSynchManager->ShutdownProcessPipe();
+ }
+
+ s_pObjSynchMgr = NULL;
+ g_pSynchronizationManager = NULL;
+ InternalDelete(pSynchManager);
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::StartWorker
+
+ Starts the Synchronization Manager's Worker Thread.
+ Private method, it is called only by CPalSynchMgrController.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::StartWorker(
+ CPalThread * pthrCurrent)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ CPalSynchronizationManager * pSynchManager = GetInstance();
+
+ if ((NULL == pSynchManager) || ((LONG)SynchMgrStatusRunning != s_lInitStatus))
+ {
+ ERROR("Trying to to create worker thread in invalid state\n");
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ HANDLE hWorkerThread = NULL;
+ palErr = InternalCreateThread(pthrCurrent,
+ NULL,
+ 0,
+ &WorkerThread,
+ (PVOID)pSynchManager,
+ 0,
+ PalWorkerThread,
+ &pSynchManager->m_dwWorkerThreadTid,
+ &hWorkerThread);
+
+ if (NO_ERROR == palErr)
+ {
+ palErr = InternalGetThreadDataFromHandle(pthrCurrent,
+ hWorkerThread,
+ 0,
+ &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);
+ 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;
+ }
+
+ // Entry point routine for the thread that initiates process termination.
+ DWORD TerminationRequestHandlingRoutine(LPVOID pArg)
+ {
+ // Call the termination request handler if one is registered.
+ if (g_terminationRequestHandler != NULL)
+ {
+ g_terminationRequestHandler();
+ }
+
+ return 0;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::WorkerThread
+
+ Synchronization Manager's Worker Thread
+ --*/
+ DWORD PALAPI CPalSynchronizationManager::WorkerThread(LPVOID pArg)
+ {
+ PAL_ERROR palErr;
+ bool fShuttingDown = false;
+ bool fWorkerIsDone = false;
+ int iPollTimeout = INFTIM;
+ SynchWorkerCmd swcCmd;
+ ThreadWakeupReason twrWakeUpReason;
+ SharedID shridMarshaledData;
+ DWORD dwData;
+ CPalSynchronizationManager * pSynchManager =
+ reinterpret_cast<CPalSynchronizationManager*>(pArg);
+ CPalThread * pthrWorker = InternalGetCurrentThread();
+
+ while (!fWorkerIsDone)
+ {
+ LONG lProcessCount;
+
+ palErr = pSynchManager->ReadCmdFromProcessPipe(iPollTimeout,
+ &swcCmd,
+ &shridMarshaledData,
+ &dwData);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Received error %x from ReadCmdFromProcessPipe()\n",
+ palErr);
+ continue;
+ }
+ switch (swcCmd)
+ {
+ case SynchWorkerCmdTerminationRequest:
+ // This worker thread is being asked to initiate process termination
+
+ HANDLE hTerminationRequestHandlingThread;
+ palErr = InternalCreateThread(pthrWorker,
+ NULL,
+ 0,
+ &TerminationRequestHandlingRoutine,
+ NULL,
+ 0,
+ PalWorkerThread,
+ NULL,
+ &hTerminationRequestHandlingThread);
+
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Unable to create worker thread\n");
+ }
+
+ if (hTerminationRequestHandlingThread != NULL)
+ {
+ CloseHandle(hTerminationRequestHandlingThread);
+ }
+
+ break;
+ 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 ||
+ SynchWorkerCmdTerminationRequest == swcWorkerCmd,
+ "Unknown worker command code %u\n", swcWorkerCmd);
+
+ TRACE("Got cmd %u from process pipe\n", swcWorkerCmd);
+ }
+
+ if (SynchWorkerCmdRemoteSignal == swcWorkerCmd ||
+ SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd)
+ {
+ SharedID shridMarshaledId = NULLSharedID;
+
+ TRACE("Received %s cmd\n",
+ (swcWorkerCmd == SynchWorkerCmdRemoteSignal) ?
+ "REMOTE SIGNAL" : "DELEGATED OBJECT SIGNALING" );
+
+ iRet = ReadBytesFromProcessPipe(WorkerCmdCompletionTimeout,
+ (BYTE *)&shridMarshaledId,
+ sizeof(shridMarshaledId));
+ if (sizeof(shridMarshaledId) != iRet)
+ {
+ ERROR("Unable to read marshaled Shared ID from the "
+ "process pipe [pipe=%d ret=%d errno=%d (%s)]\n",
+ m_iProcessPipeRead, iRet, errno, strerror(errno));
+
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ TRACE("Received marshaled shrid=%p\n", (VOID *)shridMarshaledId);
+
+ *pshridMarshaledData = shridMarshaledId;
+ }
+
+ if (SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd)
+ {
+ DWORD dwData;
+
+ iRet = ReadBytesFromProcessPipe(WorkerCmdCompletionTimeout,
+ (BYTE *)&dwData,
+ sizeof(dwData));
+ if (sizeof(dwData) != iRet)
+ {
+ ERROR("Unable to read signal count from the "
+ "process pipe [pipe=%d ret=%d errno=%d (%s)]\n",
+ m_iProcessPipeRead, iRet, errno, strerror(errno));
+
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ TRACE("Received signal count %u\n", dwData);
+
+ *pdwData = dwData;
+ }
+
+ *pswcWorkerCmd = swcWorkerCmd;
+ return NO_ERROR;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ReadBytesFromProcessPipe
+
+ Reads the specified number of bytes from the process pipe. If there is
+ no data to be read on the pipe, it blocks until there is data available
+ or the timeout expires.
+ --*/
+ int CPalSynchronizationManager::ReadBytesFromProcessPipe(
+ int iTimeout,
+ BYTE * pRecvBuf,
+ LONG iBytes)
+ {
+#if !HAVE_KQUEUE
+ struct pollfd Poll;
+#endif // !HAVE_KQUEUE
+ int iRet = -1;
+ int iConsecutiveEintrs = 0;
+ LONG iBytesRead = 0;
+ BYTE * pPos = pRecvBuf;
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ struct kevent keChanges;
+ struct timespec ts, *pts;
+ int iNChanges;
+#endif // HAVE_KQUEUE
+
+ _ASSERTE(0 <= iBytes);
+
+ do
+ {
+ while (TRUE)
+ {
+ int iErrno = 0;
+#if HAVE_KQUEUE
+#if HAVE_BROKEN_FIFO_KEVENT
+#if HAVE_BROKEN_FIFO_SELECT
+#error Found no way to wait on a FIFO.
+#endif
+
+ timeval *ptv;
+ timeval tv;
+
+ if (INFTIM == iTimeout)
+ {
+ ptv = NULL;
+ }
+ else
+ {
+ tv.tv_usec = (iTimeout % tccSecondsToMillieSeconds) *
+ tccMillieSecondsToMicroSeconds;
+ tv.tv_sec = iTimeout / tccSecondsToMillieSeconds;
+ ptv = &tv;
+ }
+
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(m_iProcessPipeRead, &readfds);
+ iRet = select(m_iProcessPipeRead + 1, &readfds, NULL, NULL, ptv);
+
+#else // HAVE_BROKEN_FIFO_KEVENT
+
+ // Note: FreeBSD needs to use kqueue/kevent support here, since on this
+ // platform the EOF notification on FIFOs is not surfaced through poll,
+ // and process pipe shutdown relies on this feature.
+ // If a thread is polling a FIFO or a pipe for POLLIN, when the last
+ // write descriptor for that pipe is closed, poll() is supposed to
+ // return with a POLLIN event but no data to be read on the FIFO/pipe,
+ // which means EOF.
+ // On FreeBSD such feature works for pipes but it doesn't for FIFOs.
+ // Using kevent the EOF is instead surfaced correctly.
+
+ if (iBytes > m_keProcessPipeEvent.data)
+ {
+ if (INFTIM == iTimeout)
+ {
+ pts = NULL;
+ }
+ else
+ {
+ ts.tv_nsec = (iTimeout % tccSecondsToMillieSeconds) *
+ tccMillieSecondsToNanoSeconds;
+ ts.tv_sec = iTimeout / tccSecondsToMillieSeconds;
+ pts = &ts;
+ }
+
+ if (0 != (EV_EOF & m_keProcessPipeEvent.flags))
+ {
+ TRACE("Refreshing kevent settings\n");
+ EV_SET(&keChanges, m_iProcessPipeRead, EVFILT_READ,
+ EV_ADD | EV_CLEAR, 0, 0, 0);
+ iNChanges = 1;
+ }
+ else
+ {
+ iNChanges = 0;
+ }
+
+ iRet = kevent(m_iKQueue, &keChanges, iNChanges,
+ &m_keProcessPipeEvent, 1, pts);
+
+ if (0 < iRet)
+ {
+ _ASSERTE(1 == iRet);
+ _ASSERTE(EVFILT_READ == m_keProcessPipeEvent.filter);
+
+ if (EV_ERROR & m_keProcessPipeEvent.flags)
+ {
+ ERROR("EV_ERROR from kevent [ident=%d filter=%d flags=%x]\n", m_keProcessPipeEvent.ident, m_keProcessPipeEvent.filter, m_keProcessPipeEvent.flags);
+ iRet = -1;
+ iErrno = m_keProcessPipeEvent.data;
+ m_keProcessPipeEvent.data = 0;
+ }
+ }
+ else if (0 > iRet)
+ {
+ iErrno = errno;
+ }
+
+ TRACE("Woken up from kevent() with ret=%d flags=%#x data=%d "
+ "[iTimeout=%d]\n", iRet, m_keProcessPipeEvent.flags,
+ m_keProcessPipeEvent.data, iTimeout);
+ }
+ else
+ {
+ // There is enough data already available in the buffer, just use that.
+ iRet = 1;
+ }
+
+#endif // HAVE_BROKEN_FIFO_KEVENT
+#else // HAVE_KQUEUE
+
+ Poll.fd = m_iProcessPipeRead;
+ Poll.events = POLLIN;
+ Poll.revents = 0;
+
+ iRet = poll(&Poll, 1, iTimeout);
+
+ TRACE("Woken up from poll() with ret=%d [iTimeout=%d]\n",
+ iRet, iTimeout);
+
+ if (1 == iRet &&
+ ((POLLERR | POLLHUP | POLLNVAL) & Poll.revents))
+ {
+ // During PAL shutdown the pipe gets closed and Poll.revents is set to POLLHUP
+ // (note: no other flags are set). We will also receive an EOF on from the read call.
+ // Please see the comment for SynchWorkerCmdShutdown in CPalSynchronizationManager::WorkerThread.
+ if (!PALIsShuttingDown() || (Poll.revents != POLLHUP))
+ {
+ ERROR("Unexpected revents=%x while polling pipe %d\n",
+ Poll.revents, Poll.fd);
+ iErrno = EINVAL;
+ iRet = -1;
+ }
+ }
+ else if (0 > iRet)
+ {
+ iErrno = errno;
+ }
+
+#endif // HAVE_KQUEUE
+
+ if (0 == iRet || 1 == iRet)
+ {
+ // 0 == wait timed out
+ // 1 == FIFO has data available
+ break;
+ }
+ else
+ {
+ if (1 < iRet)
+ {
+ // Unexpected iRet > 1
+ ASSERT("Unexpected return code %d from blocking poll/kevent call\n",
+ iRet);
+ goto RBFPP_exit;
+ }
+
+ if (EINTR != iErrno)
+ {
+ // Unexpected error
+ ASSERT("Unexpected error from blocking poll/kevent call: %d (%s)\n",
+ iErrno, strerror(iErrno));
+ goto RBFPP_exit;
+ }
+
+ iConsecutiveEintrs++;
+ TRACE("poll() failed with EINTR; re-polling\n");
+
+ if (iConsecutiveEintrs >= MaxWorkerConsecutiveEintrs)
+ {
+ if (iTimeout != INFTIM)
+ {
+ WARN("Receiving too many EINTRs; converting one of them "
+ "to a timeout");
+ iRet = 0;
+ break;
+ }
+ else if (0 == (iConsecutiveEintrs % MaxWorkerConsecutiveEintrs))
+ {
+ WARN("Receiving too many EINTRs [%d so far]",
+ iConsecutiveEintrs);
+ }
+ }
+ }
+ }
+
+ if (0 == iRet)
+ {
+ // Time out
+ break;
+ }
+ else
+ {
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ if (0 != (EV_EOF & m_keProcessPipeEvent.flags) && 0 == m_keProcessPipeEvent.data)
+ {
+ // EOF
+ TRACE("Received an EOF on process pipe via kevent\n");
+ goto RBFPP_exit;
+ }
+#endif // HAVE_KQUEUE
+
+ iRet = read(m_iProcessPipeRead, pPos, iBytes - iBytesRead);
+
+ if (0 == iRet)
+ {
+ // Poll returned 1 and read returned zero: this is an EOF,
+ // i.e. no other process has the pipe still open for write
+ TRACE("Received an EOF on process pipe via poll\n");
+ goto RBFPP_exit;
+ }
+ else if (0 > iRet)
+ {
+ ERROR("Unable to read %d bytes from the the process pipe "
+ "[pipe=%d ret=%d errno=%d (%s)]\n", iBytes - iBytesRead,
+ m_iProcessPipeRead, iRet, errno, strerror(errno));
+ goto RBFPP_exit;
+ }
+
+ TRACE("Read %d bytes from process pipe\n", iRet);
+
+ iBytesRead += iRet;
+ pPos += iRet;
+
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ // Update available data count
+ m_keProcessPipeEvent.data -= iRet;
+ _ASSERTE(0 <= m_keProcessPipeEvent.data);
+#endif // HAVE_KQUEUE
+ }
+ } while(iBytesRead < iBytes);
+
+ RBFPP_exit:
+ return (iRet < 0) ? iRet : iBytesRead;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::WakeUpLocalThread
+
+ Wakes up a local thead currently sleeping for a wait or a sleep
+ --*/
+ PAL_ERROR CPalSynchronizationManager::WakeUpLocalThread(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget,
+ ThreadWakeupReason twrWakeupReason,
+ DWORD dwObjectIndex)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ ThreadNativeWaitData * ptnwdNativeWaitData =
+ pthrTarget->synchronizationInfo.GetNativeData();
+
+ TRACE("Waking up a local thread [WakeUpReason=%u ObjectIndex=%u "
+ "ptnwdNativeWaitData=%p]\n", twrWakeupReason, dwObjectIndex,
+ ptnwdNativeWaitData);
+
+ // Set wakeup reason and signaled object index
+ ptnwdNativeWaitData->twrWakeupReason = twrWakeupReason;
+ ptnwdNativeWaitData->dwObjectIndex = dwObjectIndex;
+
+#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ if (0 < GetLocalSynchLockCount(pthrCurrent))
+ {
+ // Defer the actual thread signaling to right after
+ // releasing the synch lock(s), so that signaling
+ // can happen from a thread-suspension safe area
+ palErr = DeferThreadConditionSignaling(pthrCurrent, pthrTarget);
+ }
+ else
+ {
+ // Signal the target thread's condition
+ palErr = SignalThreadCondition(ptnwdNativeWaitData);
+ }
+#else // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ // Signal the target thread's condition
+ palErr = SignalThreadCondition(ptnwdNativeWaitData);
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+
+ return palErr;
+ }
+
+#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ /*++
+ Method:
+ CPalSynchronizationManager::DeferThreadConditionSignaling
+
+ Defers thread signaling to the final release of synchronization
+ lock(s), so that condition signaling can happen when the signaling
+ thread is marked as safe for thread suspension.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::DeferThreadConditionSignaling(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ LONG lCount = pthrCurrent->synchronizationInfo.m_lPendingSignalingCount;
+
+ _ASSERTE(pthrTarget != pthrCurrent);
+
+ if (CThreadSynchronizationInfo::PendingSignalingsArraySize > lCount)
+ {
+ // If there is available room, add the target thread object to
+ // the array of pending thread signalings.
+ pthrCurrent->synchronizationInfo.m_rgpthrPendingSignalings[lCount] = pthrTarget;
+ }
+ else
+ {
+ // If the array is full, add the target thread object at the end
+ // of the overflow list
+ DeferredSignalingListNode * pdsln =
+ InternalNew<DeferredSignalingListNode>();
+
+ if (pdsln)
+ {
+ pdsln->pthrTarget = pthrTarget;
+
+ // Add the note to the end of the list.
+ // Note: no need to synchronize the access to this list since
+ // it is meant to be accessed only by the owner thread.
+ InsertTailList(&pthrCurrent->synchronizationInfo.m_lePendingSignalingsOverflowList,
+ &pdsln->Link);
+ }
+ else
+ {
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ if (NO_ERROR == palErr)
+ {
+ // Increment the count of pending signalings
+ pthrCurrent->synchronizationInfo.m_lPendingSignalingCount += 1;
+
+ // Add a reference to the target CPalThread object; this is
+ // needed since deferring signaling after releasing the synch
+ // locks implies accessing the target thread object without
+ // holding the local synch lock. In rare circumstances, the
+ // target thread may have already exited while deferred signaling
+ // takes place, therefore invalidating the thread object. The
+ // reference added here ensures that the thread object is still
+ // good, even if the target thread has exited.
+ pthrTarget->AddThreadReference();
+ }
+
+ return palErr;
+ }
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+
+ /*++
+ Method:
+ CPalSynchronizationManager::SignalThreadCondition
+
+ Performs the actual condition signaling in to wake up the target thread
+ --*/
+ PAL_ERROR CPalSynchronizationManager::SignalThreadCondition(
+ ThreadNativeWaitData * ptnwdNativeWaitData)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ int iRet;
+
+ // Lock the mutex
+ iRet = pthread_mutex_lock(&ptnwdNativeWaitData->mutex);
+ if (0 != iRet)
+ {
+ ERROR("Cannot lock mutex [err=%d]\n", iRet);
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ // Set the predicate
+ ptnwdNativeWaitData->iPred = TRUE;
+
+ // Signal the condition
+ iRet = pthread_cond_signal(&ptnwdNativeWaitData->cond);
+ if (0 != iRet)
+ {
+ ERROR("Failed to signal condition: pthread_cond_signal "
+ "returned %d [errno=%d (%s)]\n", iRet, errno,
+ strerror(errno));
+ palErr = ERROR_INTERNAL_ERROR;
+ // Continue in order to unlock the mutex anyway
+ }
+
+ // Unlock the mutex
+ iRet = pthread_mutex_unlock(&ptnwdNativeWaitData->mutex);
+ if (0 != iRet)
+ {
+ ERROR("Cannot unlock mutex [err=%d]\n", iRet);
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ReadBytesFromProcessPipe
+
+ Wakes up a remote thead currently sleeping for a wait or a sleep
+ by sending the appropriate cmd to the remote process' worker
+ thread, which will take care to convert this command into a
+ WakeUpLocalThread in the remote process
+ --*/
+ PAL_ERROR CPalSynchronizationManager::WakeUpRemoteThread(
+ SharedID shridWLNode)
+ {
+ const int MsgSize = sizeof(BYTE) + sizeof(SharedID);
+ PAL_ERROR palErr = NO_ERROR;
+ BYTE rgSendBuf[MsgSize];
+ BYTE * pbySrc, * pbyDst = rgSendBuf;
+ WaitingThreadsListNode * pWLNode = SharedIDToTypePointer(WaitingThreadsListNode, shridWLNode);
+
+ _ASSERT_MSG(gPID != pWLNode->dwProcessId, "WakeUpRemoteThread called on local thread\n");
+ _ASSERT_MSG(NULLSharedID != shridWLNode, "NULL shared identifier\n");
+ _ASSERT_MSG(NULL != pWLNode, "Bad shared wait list node identifier (%p)\n", (VOID*)shridWLNode);
+ _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(NULLSharedID != 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) ||
+ (SynchWorkerCmdTerminationRequest == swcWorkerCmd),
+ "WakeUpLocalWorkerThread supports only SynchWorkerCmdNop, SynchWorkerCmdShutdown, and SynchWorkerCmdTerminationRequest."
+ "[received cmd=%d]\n", swcWorkerCmd);
+
+ BYTE byCmd = (BYTE)(swcWorkerCmd & 0xFF);
+
+ TRACE("Waking up Synch Worker Thread for %u [byCmd=%u]\n",
+ swcWorkerCmd, (unsigned int)byCmd);
+
+ // As long as we use pipes and we keep the message size
+ // within PIPE_BUF, there's no need to lock here, since the
+ // write is guaranteed not to be interleaved with/into other
+ // writes of PIPE_BUF bytes or less.
+ _ASSERT_MSG(sizeof(BYTE) <= PIPE_BUF, "Message too long\n");
+
+ int iRetryCount = 0;
+ ssize_t sszWritten;
+ do
+ {
+ sszWritten = write(m_iProcessPipeWrite, &byCmd, sizeof(BYTE));
+ } while (-1 == sszWritten &&
+ EAGAIN == errno &&
+ ++iRetryCount < MaxConsecutiveEagains &&
+ 0 == sched_yield());
+
+ if (sszWritten != sizeof(BYTE))
+ {
+ ERROR("Unable to write to the process pipe to wake up the "
+ "worker thread [errno=%d (%s)]\n", errno, strerror(errno));
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::GetThreadWaitInfo
+
+ Returns a pointer to the WaitInfo structure for the passed CPalThread object
+ --*/
+ ThreadWaitInfo * CPalSynchronizationManager::GetThreadWaitInfo(
+ CPalThread * pthrCurrent)
+ {
+ return &pthrCurrent->synchronizationInfo.m_twiWaitInfo;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::UnRegisterWait
+
+ Unregister the wait described by ptwiWaitInfo that in general involves
+ a thread other than the current one (most of the times the deregistration
+ is performed by the signaling thread)
+
+ Note: this method must be called while holding the local process
+ synchronization lock.
+ --*/
+ void CPalSynchronizationManager::UnRegisterWait(
+ CPalThread * pthrCurrent,
+ ThreadWaitInfo * ptwiWaitInfo,
+ bool fHaveSharedLock)
+ {
+ int i = 0;
+ CSynchData * psdSynchData = NULL;
+ bool fSharedSynchLock = false;
+
+ if (!fHaveSharedLock && LocalWait != ptwiWaitInfo->wdWaitDomain)
+ {
+ AcquireSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = true;
+ }
+
+ TRACE("Unregistering wait for thread=%u [ObjCount=%d WaitType=%u WaitDomain=%u]\n",
+ ptwiWaitInfo->pthrOwner->GetThreadId(),
+ ptwiWaitInfo->lObjCount, ptwiWaitInfo->wtWaitType,
+ ptwiWaitInfo->wdWaitDomain);
+
+ for (i=0; i < ptwiWaitInfo->lObjCount; i++)
+ {
+ WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i];
+
+ VALIDATEOBJECT(pwtlnItem);
+
+ if (pwtlnItem->dwFlags & WTLN_FLAG_OWNER_OBJECT_IS_SHARED)
+ {
+ // Shared object
+ WaitingThreadsListNode * pwtlnItemNext, * pwtlnItemPrev;
+
+ psdSynchData = SharedIDToTypePointer(CSynchData,
+ pwtlnItem->ptrOwnerObjSynchData.shrid);
+
+ VALIDATEOBJECT(psdSynchData);
+
+ pwtlnItemNext = SharedIDToTypePointer(WaitingThreadsListNode,
+ pwtlnItem->ptrNext.shrid);
+ pwtlnItemPrev = SharedIDToTypePointer(WaitingThreadsListNode,
+ pwtlnItem->ptrPrev.shrid);
+ if (pwtlnItemPrev)
+ {
+ VALIDATEOBJECT(pwtlnItemPrev);
+ pwtlnItemPrev->ptrNext.shrid = pwtlnItem->ptrNext.shrid;
+ }
+ else
+ {
+ psdSynchData->SetWTLHeadShrPtr(pwtlnItem->ptrNext.shrid);
+ }
+
+ if (pwtlnItemNext)
+ {
+ VALIDATEOBJECT(pwtlnItemNext);
+ pwtlnItemNext->ptrPrev.shrid = pwtlnItem->ptrPrev.shrid;
+ }
+ else
+ {
+ psdSynchData->SetWTLTailShrPtr(pwtlnItem->ptrPrev.shrid);
+ }
+
+ m_cacheSHRWTListNodes.Add(pthrCurrent, pwtlnItem->shridSHRThis);
+ }
+ else
+ {
+ // Local object
+ psdSynchData = pwtlnItem->ptrOwnerObjSynchData.ptr;
+
+ VALIDATEOBJECT(psdSynchData);
+
+ if (pwtlnItem->ptrPrev.ptr)
+ {
+ VALIDATEOBJECT(pwtlnItem);
+ pwtlnItem->ptrPrev.ptr->ptrNext.ptr = pwtlnItem->ptrNext.ptr;
+ }
+ else
+ {
+ psdSynchData->SetWTLHeadPtr(pwtlnItem->ptrNext.ptr);
+ }
+
+ if (pwtlnItem->ptrNext.ptr)
+ {
+ VALIDATEOBJECT(pwtlnItem);
+ pwtlnItem->ptrNext.ptr->ptrPrev.ptr = pwtlnItem->ptrPrev.ptr;
+ }
+ else
+ {
+ psdSynchData->SetWTLTailPtr(pwtlnItem->ptrPrev.ptr);
+ }
+
+ m_cacheWTListNodes.Add(pthrCurrent, pwtlnItem);
+ }
+
+ // Release the node's refcount on the synch data, and decerement
+ // waiting thread count
+ psdSynchData->DecrementWaitingThreadCount();
+ psdSynchData->Release(pthrCurrent);
+ }
+
+ // Reset wait data in ThreadWaitInfo structure: it is enough
+ // to reset lObjCount, lSharedObjCount and wdWaitDomain.
+ ptwiWaitInfo->lObjCount = 0;
+ ptwiWaitInfo->lSharedObjCount = 0;
+ ptwiWaitInfo->wdWaitDomain = LocalWait;
+
+ // Done
+ if (fSharedSynchLock)
+ {
+ ReleaseSharedSynchLock(pthrCurrent);
+ }
+
+ return;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll
+
+ Unsignals all the objects involved in a wait all, except the target
+ one (i.e. psdTgtObjectSynchData)
+
+ Note: this method must be called while holding the synchronization locks
+ appropriate to all the objects involved in the wait-all. If any
+ of the objects is shared, the caller must own both local and
+ shared synch locks; if no shared object is involved in the wait,
+ only the local synch lock is needed.
+ --*/
+ void CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget,
+ WaitingThreadsListNode * pwtlnNode,
+ CSynchData * psdTgtObjectSynchData)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ CSynchData * psdSynchDataItem = NULL;
+
+#ifdef _DEBUG
+ bool bOriginatingNodeFound = false;
+#endif
+
+ VALIDATEOBJECT(psdTgtObjectSynchData);
+ VALIDATEOBJECT(pwtlnNode);
+
+ _ASSERT_MSG(0 != (WTLN_FLAG_WAIT_ALL & pwtlnNode->dwFlags),
+ "UnsignalRestOfLocalAwakeningWaitAll() called on a normal (non wait all) wait");
+
+ _ASSERT_MSG(gPID == pwtlnNode->dwProcessId,
+ "UnsignalRestOfLocalAwakeningWaitAll() called on a wait all with remote awakening");
+
+ ThreadWaitInfo *ptwiWaitInfo = pwtlnNode->ptwiWaitInfo;
+
+ int iObjCount = ptwiWaitInfo->lObjCount;
+ for (int i = 0; i < iObjCount; i++)
+ {
+ WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i];
+
+ VALIDATEOBJECT(pwtlnItem);
+
+ if (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnItem->dwFlags))
+ {
+ psdSynchDataItem = SharedIDToTypePointer(CSynchData, pwtlnItem->ptrOwnerObjSynchData.shrid);
+ }
+ else
+ {
+ psdSynchDataItem = pwtlnItem->ptrOwnerObjSynchData.ptr;
+ }
+
+ VALIDATEOBJECT(psdSynchDataItem);
+
+ // Skip originating node
+ if (psdTgtObjectSynchData == psdSynchDataItem)
+ {
+#ifdef _DEBUG
+ bOriginatingNodeFound = true;
+#endif
+ continue;
+ }
+
+ palErr = psdSynchDataItem->ReleaseWaiterWithoutBlocking(pthrCurrent, pthrTarget);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("ReleaseWaiterWithoutBlocking failed on SynchData @ %p [palErr = %u]\n", psdSynchDataItem, palErr);
+ }
+ }
+
+ _ASSERT_MSG(bOriginatingNodeFound, "Couldn't find originating node while unsignaling rest of the wait all\n");
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::MarkWaitForDelegatedObjectSignalingInProgress
+
+ Marks all the thread waiting list nodes involved in the the current wait-all
+ for "delegated object signaling in progress", so that this wait cannot be
+ involved in another delegated object signaling that may happen while the
+ current object singaling is being tranfered to the target process (while
+ transfering it, synchronization locks are released in this process and later
+ grabbed again in the target process; in this time window another thread
+ could signal another object part of the same wait-all. In this case no
+ signal delegation must take place.
+
+ Note: this method must be called while holding the synchronization locks
+ appropriate to the target object described by pwtlnNode (i.e. the
+ local process synch lock if the target object is local, both local
+ and shared one if the object is shared).
+ --*/
+ void CPalSynchronizationManager::MarkWaitForDelegatedObjectSignalingInProgress(
+ CPalThread * pthrCurrent,
+ WaitingThreadsListNode * pwtlnNode)
+ {
+ bool fSharedSynchLock = false;
+ bool fTargetObjectIsShared = (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNode->dwFlags));
+
+ VALIDATEOBJECT(pwtlnNode);
+
+ _ASSERT_MSG(gPID == pwtlnNode->dwProcessId,
+ "MarkWaitForDelegatedObjectSignalingInProgress() called from the wrong process");
+
+ ThreadWaitInfo *ptwiWaitInfo = pwtlnNode->ptwiWaitInfo;
+
+ if (!fSharedSynchLock && !fTargetObjectIsShared &&
+ LocalWait != ptwiWaitInfo->wdWaitDomain)
+ {
+ AcquireSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = true;
+ }
+
+ _ASSERT_MSG(MultipleObjectsWaitAll == ptwiWaitInfo->wtWaitType,
+ "MarkWaitForDelegatedObjectSignalingInProgress() called on a normal (non wait-all) wait");
+
+ // Unmark all nodes other than the target one
+ int iTgtCount = ptwiWaitInfo->lObjCount;
+ for (int i = 0; i < iTgtCount; i++)
+ {
+ VALIDATEOBJECT(ptwiWaitInfo->rgpWTLNodes[i]);
+ ptwiWaitInfo->rgpWTLNodes[i]->dwFlags &= ~WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;
+ }
+
+ // Mark the target node
+ pwtlnNode->dwFlags |= WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;
+
+ // Done
+ if (fSharedSynchLock)
+ {
+ ReleaseSharedSynchLock(pthrCurrent);
+ }
+
+ return;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::UnmarkTWListForDelegatedObjectSignalingInProgress
+
+ Resets the "delegated object signaling in progress" flags in all the
+ nodes of the thread waitin list for the target waitable objects (represented
+ by its SynchData)
+
+ Note: this method must be called while holding the appropriate
+ synchronization locks (the local process synch lock if the target
+ object is local, both local and shared one if the object is shared).
+ --*/
+ void CPalSynchronizationManager::UnmarkTWListForDelegatedObjectSignalingInProgress(
+ CSynchData * pTgtObjectSynchData)
+ {
+ bool fSharedObject = (SharedObject == pTgtObjectSynchData->GetObjectDomain());
+ WaitingThreadsListNode * pwtlnNode;
+
+ VALIDATEOBJECT(pTgtObjectSynchData);
+
+ pwtlnNode = fSharedObject ? SharedIDToTypePointer(WaitingThreadsListNode, pTgtObjectSynchData->GetWTLHeadShmPtr())
+ : pTgtObjectSynchData->GetWTLHeadPtr();
+
+ while (pwtlnNode)
+ {
+ VALIDATEOBJECT(pwtlnNode);
+
+ pwtlnNode->dwFlags &= ~WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;
+ pwtlnNode = fSharedObject ? SharedIDToTypePointer(WaitingThreadsListNode, pwtlnNode->ptrNext.shrid)
+ : pwtlnNode->ptrNext.ptr;
+ }
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::RegisterProcessForMonitoring
+
+ Registers the process object represented by the passed psdSynchData and
+ pProcLocalData. The worker thread will monitor the actual process and,
+ upon process termination, it will set the exit code in pProcLocalData,
+ and it will signal the process object, by signaling its psdSynchData.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::RegisterProcessForMonitoring(
+ CPalThread * pthrCurrent,
+ CSynchData *psdSynchData,
+ IPalObject *pProcessObject,
+ CProcProcessLocalData * pProcLocalData)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ MonitoredProcessesListNode * pmpln;
+ bool fWakeUpWorker = false;
+ bool fMonitoredProcessesLock = false;
+
+ VALIDATEOBJECT(psdSynchData);
+
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+
+ fMonitoredProcessesLock = true;
+
+ pmpln = m_pmplnMonitoredProcesses;
+ while (pmpln)
+ {
+ if (psdSynchData == pmpln->psdSynchData)
+ {
+ _ASSERT_MSG(pmpln->dwPid == pProcLocalData->dwProcessId, "Invalid node in Monitored Processes List\n");
+ break;
+ }
+
+ pmpln = pmpln->pNext;
+ }
+
+ if (pmpln)
+ {
+ pmpln->lRefCount++;
+ }
+ else
+ {
+ pmpln = InternalNew<MonitoredProcessesListNode>();
+ if (NULL == pmpln)
+ {
+ ERROR("No memory to allocate MonitoredProcessesListNode structure\n");
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ goto RPFM_exit;
+ }
+
+ pmpln->lRefCount = 1;
+ pmpln->dwPid = pProcLocalData->dwProcessId;
+ pmpln->dwExitCode = 0;
+ pmpln->pProcessObject = pProcessObject;
+ pmpln->pProcessObject->AddReference();
+ pmpln->pProcLocalData = pProcLocalData;
+
+ // Acquire SynchData and AddRef it
+ pmpln->psdSynchData = psdSynchData;
+ psdSynchData->AddRef();
+
+ pmpln->pNext = m_pmplnMonitoredProcesses;
+ m_pmplnMonitoredProcesses = pmpln;
+ m_lMonitoredProcessesCount++;
+
+ fWakeUpWorker = true;
+ }
+
+ // Unlock
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = false;
+
+ if (fWakeUpWorker)
+ {
+ CPalSynchronizationManager * pSynchManager = GetInstance();
+
+ palErr = pSynchManager->WakeUpLocalWorkerThread(SynchWorkerCmdNop);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Failed waking up worker thread for process "
+ "monitoring registration [errno=%d {%s%}]\n",
+ errno, strerror(errno));
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+ }
+
+ RPFM_exit:
+ if (fMonitoredProcessesLock)
+ {
+ InternalLeaveCriticalSection(pthrCurrent,
+ &s_csMonitoredProcessesLock);
+ }
+
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::UnRegisterProcessForMonitoring
+
+ Unregisters a process object currently monitored by the worker thread
+ (typically called if the wait timed out before the process exited, or
+ if the wait was a normal (i.e. non wait-all) wait that involved othter
+ objects, and another object has been signaled).
+ --*/
+ PAL_ERROR CPalSynchronizationManager::UnRegisterProcessForMonitoring(
+ CPalThread * pthrCurrent,
+ CSynchData *psdSynchData,
+ DWORD dwPid)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ MonitoredProcessesListNode * pmpln, * pmplnPrev = NULL;
+
+ VALIDATEOBJECT(psdSynchData);
+
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+
+ pmpln = m_pmplnMonitoredProcesses;
+ while (pmpln)
+ {
+ if (psdSynchData == pmpln->psdSynchData)
+ {
+ _ASSERT_MSG(dwPid == pmpln->dwPid, "Invalid node in Monitored Processes List\n");
+ break;
+ }
+
+ pmplnPrev = pmpln;
+ pmpln = pmpln->pNext;
+ }
+
+ if (pmpln)
+ {
+ if (0 == --pmpln->lRefCount)
+ {
+ if (NULL != pmplnPrev)
+ {
+ pmplnPrev->pNext = pmpln->pNext;
+ }
+ else
+ {
+ m_pmplnMonitoredProcesses = pmpln->pNext;
+ }
+
+ m_lMonitoredProcessesCount--;
+ pmpln->pProcessObject->ReleaseReference(pthrCurrent);
+ pmpln->psdSynchData->Release(pthrCurrent);
+ InternalDelete(pmpln);
+ }
+ }
+ else
+ {
+ palErr = ERROR_NOT_FOUND;
+ }
+
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ return palErr;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ThreadPrepareForShutdown
+
+ Used to hijack thread execution from known spots within the
+ Synchronization Manager in case a PAL shutdown is initiated
+ or the thread is being terminated by another thread.
+ --*/
+ void CPalSynchronizationManager::ThreadPrepareForShutdown()
+ {
+ TRACE("The Synchronization Manager hijacked the current thread "
+ "for process shutdown or thread termination\n");
+ while (true)
+ {
+ poll(NULL, 0, INFTIM);
+ sched_yield();
+ }
+
+ ASSERT("This code should never be executed\n");
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::DoMonitorProcesses
+
+ This method is called by the worker thread to execute one step of
+ monitoring for all the process currently registered for monitoring
+ --*/
+ LONG CPalSynchronizationManager::DoMonitorProcesses(
+ CPalThread * pthrCurrent)
+ {
+ MonitoredProcessesListNode * pNode, * pPrev = NULL, * pNext;
+ LONG lInitialNodeCount;
+ LONG lRemovingCount = 0;
+ bool fLocalSynchLock = false;
+ bool fSharedSynchLock = false;
+ bool fMonitoredProcessesLock = false;
+
+ // Note: we first need to grab the monitored processes lock to walk
+ // the list of monitored processes, and then, if there is any
+ // which exited, to grab the synchronization lock(s) to signal
+ // the process object. Anyway we cannot grab the synchronization
+ // lock(s) while holding the monitored processes lock; that
+ // would cause deadlock, since RegisterProcessForMonitoring and
+ // UnRegisterProcessForMonitoring call stacks grab the locks
+ // in the opposite order. Grabbing the synch lock(s) first (and
+ // therefore all the times) would cause unacceptable contention
+ // (process monitoring is done in polling mode).
+ // Therefore we need to remove list nodes for processes that
+ // exited copying them to the exited array, while holding only
+ // the monitored processes lock, and then to signal them from that
+ // array holding synch lock(s) and monitored processes lock,
+ // acquired in this order. Holding again the monitored processes
+ // lock is needed in order to support object promotion.
+
+ // Grab the monitored processes lock
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = true;
+
+ lInitialNodeCount = m_lMonitoredProcessesCount;
+
+ pNode = m_pmplnMonitoredProcesses;
+ while (pNode)
+ {
+ pNext = pNode->pNext;
+
+ if (HasProcessExited(pNode->dwPid,
+ &pNode->dwExitCode,
+ &pNode->fIsActualExitCode))
+ {
+ TRACE("Process %u exited with return code %u\n",
+ pNode->dwPid,
+ pNode->fIsActualExitCode ? "actual" : "guessed",
+ pNode->dwExitCode);
+
+ if (NULL != pPrev)
+ {
+ pPrev->pNext = pNext;
+ }
+ else
+ {
+ m_pmplnMonitoredProcesses = pNext;
+ }
+
+ m_lMonitoredProcessesCount--;
+
+ // Insert in the list of nodes for exited processes
+ pNode->pNext = m_pmplnExitedNodes;
+ m_pmplnExitedNodes = pNode;
+ lRemovingCount++;
+ }
+ else
+ {
+ pPrev = pNode;
+ }
+
+ // Go to the next
+ pNode = pNext;
+ }
+
+ // Release the monitored processes lock
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = false;
+
+ if (lRemovingCount > 0)
+ {
+ // First grab the local synch lock
+ AcquireLocalSynchLock(pthrCurrent);
+ fLocalSynchLock = true;
+
+ // Acquire the monitored processes lock
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = true;
+
+ if (!fSharedSynchLock)
+ {
+ bool fSharedSynchLockIsNeeded = false;
+
+ // See if the shared lock is needed
+ pNode = m_pmplnExitedNodes;
+ while (pNode)
+ {
+ if (SharedObject == pNode->psdSynchData->GetObjectDomain())
+ {
+ fSharedSynchLockIsNeeded = true;
+ break;
+ }
+
+ pNode = pNode->pNext;
+ }
+
+ if (fSharedSynchLockIsNeeded)
+ {
+ // Release the monitored processes lock
+ InternalLeaveCriticalSection(pthrCurrent,
+ &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = false;
+
+ // Acquire the shared synch lock
+ AcquireSharedSynchLock(pthrCurrent);
+ fSharedSynchLock = true;
+
+ // Acquire again the monitored processes lock
+ InternalEnterCriticalSection(pthrCurrent,
+ &s_csMonitoredProcessesLock);
+ fMonitoredProcessesLock = true;
+ }
+ }
+
+ // Start from the beginning of the exited processes list
+ pNode = m_pmplnExitedNodes;
+
+ // Invalidate the list
+ m_pmplnExitedNodes = NULL;
+
+ while (pNode)
+ {
+ pNext = pNode->pNext;
+
+ TRACE("Process pid=%u exited with exitcode=%u\n",
+ pNode->dwPid, pNode->dwExitCode);
+
+ // Store the exit code in the process local data
+ if (pNode->fIsActualExitCode)
+ {
+ pNode->pProcLocalData->dwExitCode = pNode->dwExitCode;
+ }
+
+ // Set process status to PS_DONE
+ pNode->pProcLocalData->ps = PS_DONE;
+
+ // Set signal count
+ pNode->psdSynchData->SetSignalCount(1);
+
+ // Releasing all local waiters
+ //
+ // We just called directly in CSynchData::SetSignalCount(), so
+ // we need to take care of waking up waiting threads according
+ // to the Process object semantics (i.e. every thread must be
+ // awakend). Anyway if a process object is shared among two or
+ // more processes and threads from different processes are
+ // waiting on it, the object will be registered for monitoring
+ // in each of the processes. As result its signal count will
+ // be set to one more times (which is not a problem, given the
+ // process object semantics) and each worker thread will wake
+ // up waiting threads. Therefore we need to make sure that each
+ // worker wakes up only threads in its own process: we do that
+ // by calling ReleaseAllLocalWaiters
+ pNode->psdSynchData->ReleaseAllLocalWaiters(pthrCurrent);
+
+ // We are done with pProcLocalData, so we can release the process object
+ pNode->pProcessObject->ReleaseReference(pthrCurrent);
+
+ // Release the reference to the SynchData
+ pNode->psdSynchData->Release(pthrCurrent);
+
+ // Delete the node
+ InternalDelete(pNode);
+
+ // Go to the next
+ pNode = pNext;
+ }
+ }
+
+ if (fMonitoredProcessesLock)
+ {
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ }
+
+ if (fSharedSynchLock)
+ {
+ ReleaseSharedSynchLock(pthrCurrent);
+ }
+
+ if (fLocalSynchLock)
+ {
+ ReleaseLocalSynchLock(pthrCurrent);
+ }
+
+ return (lInitialNodeCount - lRemovingCount);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::DiscardMonitoredProcesses
+
+ This method is called at shutdown time to discard all the registration
+ for the processes currently monitored by the worker thread.
+ This method must be called at shutdown time, otherwise some shared memory
+ may be leaked at process shutdown.
+ --*/
+ void CPalSynchronizationManager::DiscardMonitoredProcesses(
+ CPalThread * pthrCurrent)
+ {
+ MonitoredProcessesListNode * pNode;
+
+ // Grab the monitored processes lock
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+
+ while (m_pmplnMonitoredProcesses)
+ {
+ pNode = m_pmplnMonitoredProcesses;
+ m_pmplnMonitoredProcesses = pNode->pNext;
+ pNode->pProcessObject->ReleaseReference(pthrCurrent);
+ pNode->psdSynchData->Release(pthrCurrent);
+ InternalDelete(pNode);
+ }
+
+ // Release the monitored processes lock
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::CreateProcessPipe
+
+ Creates the process pipe for the current process
+ --*/
+ bool CPalSynchronizationManager::CreateProcessPipe()
+ {
+ bool fRet = true;
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ int iKq = -1;
+#endif // HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+
+#ifndef CORECLR
+ int iPipeRd = -1, iPipeWr = -1;
+ char szPipeFilename[MAX_PATH];
+
+ /* Create the blocking pipe */
+ if (!GetProcessPipeName(szPipeFilename, MAX_PATH, gPID))
+ {
+ ERROR("couldn't get process pipe's name\n");
+ szPipeFilename[0] = 0;
+ fRet = false;
+ goto CPP_exit;
+ }
+
+ /* create the pipe, with full access to the owner only */
+ if (mkfifo(szPipeFilename, S_IRWXU) == -1)
+ {
+ if (errno == EEXIST)
+ {
+ /* Some how no one deleted the pipe, perhaps it was left behind
+ from a crash?? Delete the pipe and try again. */
+ if (-1 == unlink(szPipeFilename))
+ {
+ ERROR( "Unable to delete the process pipe that was left behind.\n" );
+ fRet = false;
+ goto CPP_exit;
+ }
+ else
+ {
+ if (mkfifo(szPipeFilename, S_IRWXU) == -1)
+ {
+ ERROR( "Still unable to create the process pipe...giving up!\n" );
+ fRet = false;
+ goto CPP_exit;
+ }
+ }
+ }
+ else
+ {
+ ERROR( "Unable to create the process pipe.\n" );
+ fRet = false;
+ goto CPP_exit;
+ }
+ }
+
+ iPipeRd = InternalOpen(szPipeFilename, O_RDONLY | O_NONBLOCK);
+ if (iPipeRd == -1)
+ {
+ ERROR("Unable to open the process pipe for read\n");
+ fRet = false;
+ goto CPP_exit;
+ }
+
+ iPipeWr = InternalOpen(szPipeFilename, O_WRONLY | O_NONBLOCK);
+ if (iPipeWr == -1)
+ {
+ ERROR("Unable to open the process pipe for write\n");
+ fRet = false;
+ goto CPP_exit;
+ }
+#else // !CORECLR
+ int rgiPipe[] = { -1, -1 };
+ if (pipe(rgiPipe) == -1)
+ {
+ ERROR("Unable to create the process pipe\n");
+ fRet = false;
+ goto CPP_exit;
+ }
+#endif // !CORECLR
+
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ iKq = kqueue();
+ if (-1 == iKq)
+ {
+ ERROR("Failed to create kqueue associated to process pipe\n");
+ fRet = false;
+ goto CPP_exit;
+ }
+#endif // HAVE_KQUEUE
+
+ CPP_exit:
+ if (fRet)
+ {
+ // Succeeded
+#ifndef CORECLR
+ m_iProcessPipeRead = iPipeRd;
+ m_iProcessPipeWrite = iPipeWr;
+#else // !CORECLR
+ m_iProcessPipeRead = rgiPipe[0];
+ m_iProcessPipeWrite = rgiPipe[1];
+#endif // !CORECLR
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ m_iKQueue = iKq;
+#endif // HAVE_KQUEUE
+ }
+ else
+ {
+#ifndef CORECLR
+ // Failed
+ if (0 != szPipeFilename[0])
+ {
+ unlink(szPipeFilename);
+ }
+ if (-1 != iPipeRd)
+ {
+ close(iPipeRd);
+ }
+ if (-1 != iPipeWr)
+ {
+ close(iPipeWr);
+ }
+#else // !CORECLR
+ if (-1 != rgiPipe[0])
+ {
+ close(rgiPipe[0]);
+ close(rgiPipe[1]);
+ }
+#endif // !CORECLR
+#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
+ if (-1 != iKq)
+ {
+ close(iKq);
+ }
+#endif // HAVE_KQUEUE
+ }
+
+ return fRet;
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ShutdownProcessPipe
+
+ Shuts down the process pipe and removes the fifo so that other processes
+ can no longer open it. It also closes the local write end of the pipe (see
+ comment below). From this moment on the worker thread will process any
+ possible data already received in the pipe (but not yet consumed) and any
+ data written by processes that still have a opened write end of this pipe;
+ it will wait (with timeout) until the last remote process which has a write
+ end opened closes it, and then it will yield to process shutdown.
+ --*/
+ PAL_ERROR CPalSynchronizationManager::ShutdownProcessPipe()
+ {
+ PAL_ERROR palErr = NO_ERROR;
+#ifndef CORECLR
+ char szPipeFilename[MAX_PATH];
+
+ if (GetProcessPipeName(szPipeFilename, MAX_PATH, gPID))
+ {
+ if (unlink(szPipeFilename) == -1)
+ {
+ ERROR("Unable to unlink the pipe file name errno=%d (%s)\n",
+ errno, strerror(errno));
+ palErr = ERROR_INTERNAL_ERROR;
+ // go on anyway
+ }
+ }
+ else
+ {
+ ERROR("Couldn't get the process pipe's name\n");
+ palErr = ERROR_INTERNAL_ERROR;
+ // go on anyway
+ }
+#endif // CORECLR
+
+ if (-1 != m_iProcessPipeWrite)
+ {
+ // Closing the write end of the process pipe. When the last process
+ // that still has a open write-fd on this pipe will close it, the
+ // worker thread will receive an EOF; the worker thread will wait
+ // for this EOF before shutting down, so to ensure to process any
+ // possible data already written to the pipe by other processes
+ // when the shutdown has been initiated in the current process.
+ // Note: no need here to worry about platforms where close(pipe)
+ // blocks on outstanding syscalls, since we are the only one using
+ // this fd.
+ TRACE("Closing the write end of process pipe\n");
+ if (close(m_iProcessPipeWrite) == -1)
+ {
+ ERROR("Unable to close the write end of process pipe\n");
+ palErr = ERROR_INTERNAL_ERROR;
+ }
+
+ m_iProcessPipeWrite = -1;
+ }
+
+ return palErr;
+ }
+
+#ifndef CORECLR
+ /*++
+ Method:
+ CPalSynchronizationManager::GetProcessPipeName
+
+ Returns the process pipe name for the target process (identified by its PID)
+ --*/
+ bool CPalSynchronizationManager::GetProcessPipeName(
+ LPSTR pDest,
+ int iDestSize,
+ DWORD dwPid)
+ {
+ CHAR config_dir[MAX_PATH];
+ int needed_size;
+
+ _ASSERT_MSG(NULL != pDest, "Destination pointer is NULL!\n");
+ _ASSERT_MSG(0 < iDestSize,"Invalid buffer size %d\n", iDestSize);
+
+ if (!PALGetPalConfigDir(config_dir, MAX_PATH))
+ {
+ ASSERT("Unable to determine the PAL config directory.\n");
+ pDest[0] = '\0';
+ return false;
+ }
+ needed_size = snprintf(pDest, iDestSize, "%s/%s-%u", config_dir,
+ PROCESS_PIPE_NAME_PREFIX, dwPid);
+ pDest[iDestSize-1] = 0;
+ if(needed_size >= iDestSize)
+ {
+ ERROR("threadpipe name needs %d characters, buffer only has room for "
+ "%d\n", needed_size, iDestSize+1);
+ return false;
+ }
+ return true;
+ }
+#endif // !CORECLR
+
+ /*++
+ Method:
+ CPalSynchronizationManager::AcquireProcessLock
+
+ Acquires the local Process Lock (which currently is the same as the
+ the local Process Synch Lock)
+ --*/
+ void CPalSynchronizationManager::AcquireProcessLock(CPalThread * pthrCurrent)
+ {
+ AcquireLocalSynchLock(pthrCurrent);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::ReleaseProcessLock
+
+ Releases the local Process Lock (which currently is the same as the
+ the local Process Synch Lock)
+ --*/
+ void CPalSynchronizationManager::ReleaseProcessLock(CPalThread * pthrCurrent)
+ {
+ ReleaseLocalSynchLock(pthrCurrent);
+ }
+
+ /*++
+ Method:
+ CPalSynchronizationManager::PromoteObjectSynchData
+
+ Promotes an object's synchdata from local to shared
+ --*/
+ PAL_ERROR CPalSynchronizationManager::PromoteObjectSynchData(
+ CPalThread *pthrCurrent,
+ VOID *pvLocalSynchData,
+ VOID **ppvSharedSynchData)
+ {
+ PAL_ERROR palError = NO_ERROR;
+ CSynchData *psdLocal = reinterpret_cast<CSynchData *>(pvLocalSynchData);
+ CSynchData *psdShared = NULL;
+ SharedID shridSynchData = NULLSharedID;
+ 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 (NULLSharedID == shridSynchData)
+ {
+ ERROR("Unable to allocate shared memory\n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto POSD_exit;
+ }
+
+ psdShared = SharedIDToTypePointer(CSynchData, shridSynchData);
+ _ASSERTE(NULL != psdShared);
+
+ //
+ // Allocate shared memory WaitingThreadListNodes if there are
+ // any threads currently waiting on this object
+ //
+
+ ulcWaitingThreads = psdLocal->GetWaitingThreadCount();
+ if (0 < ulcWaitingThreads)
+ {
+ int i;
+
+ rgshridWTLNodes = InternalNewArray<SharedID>(ulcWaitingThreads);
+ if (NULL == rgshridWTLNodes)
+ {
+ palError = ERROR_OUTOFMEMORY;
+ goto POSD_exit;
+ }
+
+ i = m_cacheSHRWTListNodes.Get(
+ pthrCurrent,
+ ulcWaitingThreads,
+ rgshridWTLNodes
+ );
+
+ if (static_cast<ULONG>(i) != ulcWaitingThreads)
+ {
+ for (i -= 1; i >= 0; i -= 1)
+ {
+ m_cacheSHRWTListNodes.Add(pthrCurrent, rgshridWTLNodes[i]);
+ }
+
+ palError = ERROR_OUTOFMEMORY;
+ goto POSD_exit;
+ }
+ }
+
+ //
+ // If the synch data is for a process object we need to grab
+ // the monitored process list lock here
+ //
+
+ pot = psdLocal->GetObjectType();
+ _ASSERTE(NULL != pot);
+
+ if (otiProcess == pot->GetId())
+ {
+ InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ }
+
+ //
+ // Copy pertinent CSynchData info to the shared memory version (and
+ // initialize other members)
+ //
+
+ psdShared->SetSharedThis(shridSynchData);
+ psdShared->SetObjectDomain(SharedObject);
+ psdShared->SetObjectType(psdLocal->GetObjectType());
+ psdShared->SetSignalCount(psdLocal->GetSignalCount());
+
+#ifdef SYNCH_STATISTICS
+ psdShared->SetStatContentionCount(psdLocal->GetStatContentionCount());
+ psdShared->SetStatWaitCount(psdLocal->GetStatWaitCount());
+#endif
+
+ //
+ // Rebuild the waiting thread list, and update the wait domain
+ // for the waiting threads
+ //
+
+ psdShared->SetWTLHeadShrPtr(NULLSharedID);
+ psdShared->SetWTLTailShrPtr(NULLSharedID);
+
+ 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]);
+ psdShared->AddRef();
+
+ _ASSERTE(pwtlnOld = pwtlnOld->ptwiWaitInfo->rgpWTLNodes[pwtlnOld->dwObjIndex]);
+ pwtlnNew->ptwiWaitInfo->rgpWTLNodes[pwtlnNew->dwObjIndex] = pwtlnNew;
+
+ pwtlnNew->ptwiWaitInfo->lSharedObjCount += 1;
+ if (pwtlnNew->ptwiWaitInfo->lSharedObjCount
+ == pwtlnNew->ptwiWaitInfo->lObjCount)
+ {
+ pwtlnNew->ptwiWaitInfo->wdWaitDomain = SharedWait;
+ }
+ else
+ {
+ _ASSERTE(pwtlnNew->ptwiWaitInfo->lSharedObjCount
+ < pwtlnNew->ptwiWaitInfo->lObjCount);
+
+ pwtlnNew->ptwiWaitInfo->wdWaitDomain = MixedWait;
+ }
+ }
+
+ _ASSERTE(psdShared->GetWaitingThreadCount() == ulcWaitingThreads);
+ }
+
+ //
+ // If the object tracks ownership and has a current owner update
+ // the OwnedObjectsListNode to point to the shared memory synch
+ // data
+ //
+
+ if (CObjectType::OwnershipTracked == pot->GetOwnershipSemantics())
+ {
+ OwnedObjectsListNode *pooln;
+
+ pooln = psdLocal->GetOwnershipListNode();
+ if (NULL != pooln)
+ {
+ pooln->pPalObjSynchData = psdShared;
+ psdShared->SetOwnershipListNode(pooln);
+ psdShared->AddRef();
+
+ //
+ // Copy over other ownership info.
+ //
+
+ psdShared->SetOwner(psdLocal->GetOwnerThread());
+ psdShared->SetOwnershipCount(psdLocal->GetOwnershipCount());
+ _ASSERTE(!psdShared->IsAbandoned());
+ }
+ else
+ {
+ _ASSERTE(0 == psdLocal->GetOwnershipCount());
+ _ASSERTE(0 == psdShared->GetOwnershipCount());
+ psdShared->SetAbandoned(psdLocal->IsAbandoned());
+ }
+ }
+
+ //
+ // If the synch data is for a process object update the monitored
+ // process list nodes to point to the shared memory object data,
+ // and release the monitored process list lock
+ //
+
+ if (otiProcess == pot->GetId())
+ {
+ MonitoredProcessesListNode *pmpn;
+
+ pmpn = m_pmplnMonitoredProcesses;
+ while (NULL != pmpn)
+ {
+ if (psdLocal == pmpn->psdSynchData)
+ {
+ pmpn->psdSynchData = psdShared;
+ psdShared->AddRef();
+ }
+
+ pmpn = pmpn->pNext;
+ }
+
+ pmpn = m_pmplnExitedNodes;
+ while (NULL != pmpn)
+ {
+ if (psdLocal == pmpn->psdSynchData)
+ {
+ pmpn->psdSynchData = psdShared;
+ psdShared->AddRef();
+ }
+
+ pmpn = pmpn->pNext;
+ }
+
+ InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
+ }
+
+ *ppvSharedSynchData = reinterpret_cast<VOID*>(shridSynchData);
+
+ //
+ // Free the local memory items to caches
+ //
+
+ if (0 < ulcWaitingThreads)
+ {
+ WaitingThreadsListNode *pwtln;
+
+ pwtln = psdLocal->GetWTLHeadPtr();
+ while (NULL != pwtln)
+ {
+ WaitingThreadsListNode *pwtlnTemp;
+
+ pwtlnTemp = pwtln;
+ pwtln = pwtln->ptrNext.ptr;
+ m_cacheWTListNodes.Add(pthrCurrent, pwtlnTemp);
+ }
+ }
+
+ m_cacheSynchData.Add(pthrCurrent, psdLocal);
+
+ POSD_exit:
+
+ if (NULL != rgshridWTLNodes)
+ {
+ InternalDeleteArray(rgshridWTLNodes);
+ }
+
+ return palError;
+ }
+
+
+ /////////////////////////////
+ // //
+ // _ThreadNativeWaitData //
+ // //
+ /////////////////////////////
+
+ _ThreadNativeWaitData::~_ThreadNativeWaitData()
+ {
+ if (fInitialized)
+ {
+ fInitialized = false;
+ pthread_cond_destroy(&cond);
+ pthread_mutex_destroy(&mutex);
+ }
+ }
+
+
+ //////////////////////////////////
+ // //
+ // CThreadSynchronizationInfo //
+ // //
+ //////////////////////////////////
+
+ CThreadSynchronizationInfo::CThreadSynchronizationInfo() :
+ m_tsThreadState(TS_IDLE),
+ m_shridWaitAwakened(NULLSharedID),
+ m_lLocalSynchLockCount(0),
+ m_lSharedSynchLockCount(0),
+ m_ownedNamedMutexListHead(nullptr)
+ {
+ InitializeListHead(&m_leOwnedObjsList);
+ InitializeCriticalSection(&m_ownedNamedMutexListLock);
+
+#ifdef SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ m_lPendingSignalingCount = 0;
+ InitializeListHead(&m_lePendingSignalingsOverflowList);
+#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
+ }
+
+ CThreadSynchronizationInfo::~CThreadSynchronizationInfo()
+ {
+ DeleteCriticalSection(&m_ownedNamedMutexListLock);
+ if (NULLSharedID != m_shridWaitAwakened)
+ {
+ RawSharedObjectFree(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;
+ m_shridWaitAwakened = RawSharedObjectAlloc(sizeof(DWORD),
+ DefaultSharedPool);
+ if (NULLSharedID == m_shridWaitAwakened)
+ {
+ ERROR("Fail allocating thread wait status shared object\n");
+ palErr = ERROR_NOT_ENOUGH_MEMORY;
+ goto IPrC_exit;
+ }
+
+ pdwWaitState = SharedIDToTypePointer(DWORD,
+ m_shridWaitAwakened);
+
+ _ASSERT_MSG(NULL != pdwWaitState,
+ "Unable to map shared wait state: bad shared ID [shrid=%p]\n", (VOID*)m_shridWaitAwakened);
+
+ VolatileStore<DWORD>(pdwWaitState, TWS_ACTIVE);
+ m_tsThreadState = TS_STARTING;
+
+ 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, 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, NULL);
+ if (0 != iRet)
+ {
+ ERROR("Failed creating thread synchronization condition "
+ "[error=%d (%s)]\n", iRet, strerror(iRet));
+ if (EAGAIN == iRet && MaxUnavailableResourceRetries >= ++iEagains)
+ {
+ poll(NULL, 0, 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 (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;
+ }
+
+ void CThreadSynchronizationInfo::AddOwnedNamedMutex(NamedMutexProcessData *processData)
+ {
+ _ASSERTE(processData != nullptr);
+ _ASSERTE(processData->GetNextInThreadOwnedNamedMutexList() == nullptr);
+
+ EnterCriticalSection(&m_ownedNamedMutexListLock);
+ processData->SetNextInThreadOwnedNamedMutexList(m_ownedNamedMutexListHead);
+ m_ownedNamedMutexListHead = processData;
+ LeaveCriticalSection(&m_ownedNamedMutexListLock);
+ }
+
+ void CThreadSynchronizationInfo::RemoveOwnedNamedMutex(NamedMutexProcessData *processData)
+ {
+ _ASSERTE(processData != nullptr);
+
+ EnterCriticalSection(&m_ownedNamedMutexListLock);
+ if (m_ownedNamedMutexListHead == processData)
+ {
+ m_ownedNamedMutexListHead = processData->GetNextInThreadOwnedNamedMutexList();
+ processData->SetNextInThreadOwnedNamedMutexList(nullptr);
+ }
+ else
+ {
+ bool found = false;
+ for (NamedMutexProcessData
+ *previous = m_ownedNamedMutexListHead,
+ *current = previous->GetNextInThreadOwnedNamedMutexList();
+ current != nullptr;
+ previous = current, current = current->GetNextInThreadOwnedNamedMutexList())
+ {
+ if (current == processData)
+ {
+ found = true;
+ previous->SetNextInThreadOwnedNamedMutexList(current->GetNextInThreadOwnedNamedMutexList());
+ current->SetNextInThreadOwnedNamedMutexList(nullptr);
+ break;
+ }
+ }
+ _ASSERTE(found);
+ }
+ LeaveCriticalSection(&m_ownedNamedMutexListLock);
+ }
+
+ NamedMutexProcessData *CThreadSynchronizationInfo::RemoveFirstOwnedNamedMutex()
+ {
+ EnterCriticalSection(&m_ownedNamedMutexListLock);
+ NamedMutexProcessData *processData = m_ownedNamedMutexListHead;
+ if (processData != nullptr)
+ {
+ m_ownedNamedMutexListHead = processData->GetNextInThreadOwnedNamedMutexList();
+ processData->SetNextInThreadOwnedNamedMutexList(nullptr);
+ }
+ LeaveCriticalSection(&m_ownedNamedMutexListLock);
+ return processData;
+ }
+
+ bool CThreadSynchronizationInfo::OwnsNamedMutex(NamedMutexProcessData *processData)
+ {
+ EnterCriticalSection(&m_ownedNamedMutexListLock);
+ bool found = false;
+ for (NamedMutexProcessData *current = m_ownedNamedMutexListHead;
+ current != nullptr;
+ current = current->GetNextInThreadOwnedNamedMutexList())
+ {
+ if (current == processData)
+ {
+ found = true;
+ break;
+ }
+ }
+ LeaveCriticalSection(&m_ownedNamedMutexListLock);
+ return found;
+ }
+
+#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 = 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)
+ {
+ PAL_ERROR palErr = NO_ERROR;
+ int iRet;
+
+#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
+ #error "Don't know how to get hi-res current time on this platform"
+#endif // HAVE_WORKING_CLOCK_GETTIME, HAVE_WORKING_GETTIMEOFDAY
+
+ 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 0000000000..fdef82e936
--- /dev/null
+++ b/src/pal/src/synchmgr/synchmanager.hpp
@@ -0,0 +1,1022 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ synchmanager.hpp
+
+Abstract:
+ Private header file for synchronization manager and
+ controllers implementation
+
+
+
+--*/
+#ifndef _SYNCHMANAGER_HPP_
+#define _SYNCHMANAGER_HPP_
+
+#include "pal/synchobjects.hpp"
+#include "pal/synchcache.hpp"
+#include "pal/cs.hpp"
+#include "pal/corunix.hpp"
+#include "pal/thread.hpp"
+#include "pal/procobj.hpp"
+#include "pal/init.h"
+#include "pal/process.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#if HAVE_KQUEUE
+#include <sys/event.h>
+#endif // HAVE_KQUEUE
+#include "pal/dbgmsg.h"
+
+#ifdef _DEBUG
+// #define SYNCH_OBJECT_VALIDATION
+// #define SYNCH_STATISTICS
+#endif
+
+#ifdef SYNCH_OBJECT_VALIDATION
+#define VALIDATEOBJECT(obj) ((obj)->ValidateObject())
+#else
+#define VALIDATEOBJECT(obj)
+#endif
+
+namespace CorUnix
+{
+ const DWORD WTLN_FLAG_OWNER_OBJECT_IS_SHARED = 1<<0;
+ const DWORD WTLN_FLAG_WAIT_ALL = 1<<1;
+ const DWORD WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS = 1<<2;
+
+#ifdef SYNCH_OBJECT_VALIDATION
+ const DWORD HeadSignature = 0x48454144;
+ const DWORD TailSignature = 0x5441494C;
+ const DWORD EmptySignature = 0xBAADF00D;
+#endif
+
+ enum THREAD_WAIT_STATE
+ {
+ TWS_ACTIVE,
+ TWS_WAITING,
+ TWS_ALERTABLE,
+ TWS_EARLYDEATH,
+ };
+
+ enum WaitCompletionState
+ {
+ WaitIsNotSatisfied,
+ WaitIsSatisfied,
+ WaitMayBeSatisfied
+ };
+
+ typedef union _SynchDataGenrPtr
+ {
+ SharedID shrid;
+ CSynchData * ptr;
+ } SynchDataGenrPtr;
+
+ typedef union _WTLNodeGenrPtr
+ {
+ SharedID shrid;
+ struct _WaitingThreadsListNode * ptr;
+ } WTLNodeGenrPtr;
+
+ typedef struct _WaitingThreadsListNode
+ {
+#ifdef SYNCH_OBJECT_VALIDATION
+ DWORD dwDebugHeadSignature;
+#endif
+ WTLNodeGenrPtr ptrNext;
+ WTLNodeGenrPtr ptrPrev;
+ SharedID shridSHRThis;
+
+ // Data
+ DWORD dwThreadId;
+ DWORD dwProcessId;
+ DWORD dwObjIndex;
+ DWORD dwFlags;
+
+ // Pointers to related objects
+ SharedID shridWaitingState;
+ SynchDataGenrPtr ptrOwnerObjSynchData;
+ struct _ThreadWaitInfo * ptwiWaitInfo; // valid only in the
+ // target process
+#ifdef SYNCH_OBJECT_VALIDATION
+ _WaitingThreadsListNode();
+ ~_WaitingThreadsListNode();
+ void ValidateObject(void);
+ void ValidateEmptyObject(void);
+ void InvalidateObject(void);
+
+ DWORD dwDebugTailSignature;
+#endif
+ } WaitingThreadsListNode;
+
+ typedef struct _DeferredSignalingListNode
+ {
+ LIST_ENTRY Link;
+ CPalThread * pthrTarget;
+ } DeferredSignalingListNode;
+
+ typedef struct _OwnedObjectsListNode
+ {
+ LIST_ENTRY Link;
+ CSynchData * pPalObjSynchData;
+ } OwnedObjectsListNode;
+
+ typedef struct _ThreadApcInfoNode
+ {
+ struct _ThreadApcInfoNode * pNext;
+ PAPCFUNC pfnAPC;
+ ULONG_PTR pAPCData;
+ } ThreadApcInfoNode;
+
+ 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(NULLSharedID), 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);
+ void SharedWaiterEnqueue(SharedID shridNewNode);
+
+ // 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);
+
+ virtual void ReleaseController(void);
+
+ CProcProcessLocalData * GetProcessLocalData(void);
+
+ void SetProcessData(IPalObject* pProcessObject, CProcProcessLocalData * pProcLocalData);
+ };
+
+ class CSynchStateController : public CSynchControllerBase,
+ public ISynchStateController
+ {
+ public:
+ // NB: For perforformance purposes this class is supposed
+ // to have no constructor
+ virtual ~CSynchStateController() = default;
+
+ //
+ // ISynchStateController methods
+ //
+ virtual PAL_ERROR GetSignalCount(LONG *plSignalCount);
+ virtual PAL_ERROR SetSignalCount(LONG lNewCount);
+ virtual PAL_ERROR IncrementSignalCount(LONG lAmountToIncrement);
+ virtual PAL_ERROR DecrementSignalCount(LONG lAmountToDecrement);
+ virtual PAL_ERROR SetOwner(CPalThread *pNewOwningThread);
+ virtual PAL_ERROR DecrementOwnershipCount(void);
+ virtual void ReleaseController(void);
+ };
+
+ class CPalSynchronizationManager : public IPalSynchronizationManager
+ {
+ friend class CPalSynchMgrController;
+ template <class T> friend T *CorUnix::InternalNew();
+
+ public:
+ // types
+ typedef CSynchCache<CSynchWaitController> CSynchWaitControllerCache;
+ typedef CSynchCache<CSynchStateController> CSynchStateControllerCache;
+ typedef CSynchCache<CSynchData> CSynchDataCache;
+ typedef CSHRSynchCache<CSynchData> CSHRSynchDataCache;
+ typedef CSynchCache<WaitingThreadsListNode> CWaitingThreadsListNodeCache;
+ typedef CSHRSynchCache<WaitingThreadsListNode> CSHRWaitingThreadsListNodeCache;
+ typedef CSynchCache<ThreadApcInfoNode> CThreadApcInfoNodeCache;
+ typedef CSynchCache<OwnedObjectsListNode> COwnedObjectsListNodeCache;
+
+ private:
+ // types
+ enum InitStatus
+ {
+ SynchMgrStatusIdle,
+ SynchMgrStatusInitializing,
+ SynchMgrStatusRunning,
+ SynchMgrStatusShuttingDown,
+ SynchMgrStatusReadyForProcessShutDown,
+ SynchMgrStatusError
+ };
+ enum SynchWorkerCmd
+ {
+ SynchWorkerCmdNop,
+ SynchWorkerCmdRemoteSignal,
+ SynchWorkerCmdDelegatedObjectSignaling,
+ SynchWorkerCmdShutdown,
+ SynchWorkerCmdTerminationRequest,
+ SynchWorkerCmdLast
+ };
+
+ typedef struct _MonitoredProcessesListNode
+ {
+ struct _MonitoredProcessesListNode * pNext;
+ LONG lRefCount;
+ CSynchData * psdSynchData;
+ DWORD dwPid;
+ DWORD dwExitCode;
+ bool fIsActualExitCode;
+
+ // Object that owns pProcLocalData. This is stored, with a reference, to
+ // ensure that pProcLocalData is not deleted.
+ IPalObject *pProcessObject;
+ CProcProcessLocalData * pProcLocalData;
+ } MonitoredProcessesListNode;
+
+ // constants
+ static const int CtrlrsCacheMaxSize = 256;
+ static const int SynchDataCacheMaxSize = 256;
+ static const int WTListNodeCacheMaxSize = 256;
+ static const int ApcInfoNodeCacheMaxSize = 32;
+ static const int OwnedObjectsListCacheMaxSize = 16;
+ static const int MaxWorkerConsecutiveEintrs = 128;
+ static const int MaxConsecutiveEagains = 128;
+ static const int WorkerThreadProcMonitoringTimeout = 250; // ms
+ static const int WorkerThreadShuttingDownTimeout = 1000; // ms
+ static const int WorkerCmdCompletionTimeout = 250; // ms
+ static const DWORD SecondNativeWaitTimeout = INFINITE;
+ static const DWORD WorkerThreadTerminationTimeout = 2000; // ms
+
+ // static members
+ static CPalSynchronizationManager * s_pObjSynchMgr;
+ static Volatile<LONG> s_lInitStatus;
+ static CRITICAL_SECTION s_csSynchProcessLock;
+ static CRITICAL_SECTION s_csMonitoredProcessesLock;
+
+ // members
+ DWORD m_dwWorkerThreadTid;
+ IPalObject * m_pipoThread;
+ CPalThread * m_pthrWorker;
+ int m_iProcessPipeRead;
+ int m_iProcessPipeWrite;
+#if HAVE_KQUEUE
+ int m_iKQueue;
+ struct kevent m_keProcessPipeEvent;
+#endif // HAVE_KQUEUE
+
+ MonitoredProcessesListNode * m_pmplnMonitoredProcesses;
+ LONG m_lMonitoredProcessesCount;
+ MonitoredProcessesListNode * m_pmplnExitedNodes;
+
+ // caches
+ CSynchWaitControllerCache m_cacheWaitCtrlrs;
+ CSynchStateControllerCache m_cacheStateCtrlrs;
+ CSynchDataCache m_cacheSynchData;
+ CSHRSynchDataCache m_cacheSHRSynchData;
+ CWaitingThreadsListNodeCache m_cacheWTListNodes;
+ CSHRWaitingThreadsListNodeCache m_cacheSHRWTListNodes;
+ CThreadApcInfoNodeCache m_cacheThreadApcInfoNodes;
+ 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);
+ }
+
+ ThreadApcInfoNode * CacheGetApcInfoNodes(CPalThread * pthrCurrent)
+ {
+ return m_cacheThreadApcInfoNodes.Get(pthrCurrent);
+ }
+ void CacheAddApcInfoNodes(
+ CPalThread * pthrCurrent,
+ ThreadApcInfoNode * pNode)
+ {
+ m_cacheThreadApcInfoNodes.Add(pthrCurrent, pNode);
+ }
+
+ 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 PAL_ERROR QueueUserAPC(
+ CPalThread * pthrCurrent,
+ CPalThread *pthrTarget,
+ PAPCFUNC pfnAPC,
+ ULONG_PTR uptrData);
+
+ virtual PAL_ERROR SendTerminationRequestToWorkerThread();
+
+ virtual bool AreAPCsPending(CPalThread * pthrTarget);
+
+ virtual PAL_ERROR DispatchPendingAPCs(CPalThread * pthrCurrent);
+
+ 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);
+
+ void DiscardAllPendingAPCs(
+ CPalThread * pthrCurrent,
+ CPalThread * pthrTarget);
+
+ 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);
+ };
+}
+
+#endif // _SYNCHMANAGER_HPP_
diff --git a/src/pal/src/synchmgr/wait.cpp b/src/pal/src/synchmgr/wait.cpp
new file mode 100644
index 0000000000..9c4fe3a9ca
--- /dev/null
+++ b/src/pal/src/synchmgr/wait.cpp
@@ -0,0 +1,711 @@
+// 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.
+
+/*++
+
+
+
+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/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[] =
+ {
+ otiAutoResetEvent,
+ otiManualResetEvent,
+ otiMutex,
+ otiNamedMutex,
+ otiSemaphore,
+ 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;
+}
+
+
+/*++
+Function:
+ WaitForSingleObjectEx
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+WaitForSingleObjectEx(IN HANDLE hHandle,
+ IN DWORD dwMilliseconds,
+ IN BOOL bAlertable)
+{
+ DWORD dwRet;
+
+ PERF_ENTRY(WaitForSingleObjectEx);
+ ENTRY("WaitForSingleObjectEx(hHandle=%p, dwMilliseconds=%u, bAlertable=%s)\n",
+ hHandle, dwMilliseconds, bAlertable ? "TRUE" : "FALSE");
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE,
+ dwMilliseconds, bAlertable);
+
+ LOGEXIT("WaitForSingleObjectEx returns DWORD %u\n", dwRet);
+ PERF_EXIT(WaitForSingleObjectEx);
+ return dwRet;
+}
+
+
+/*++
+Function:
+ WaitForMultipleObjects
+
+See MSDN doc.
+
+--*/
+DWORD
+PALAPI
+WaitForMultipleObjects(IN DWORD nCount,
+ IN CONST HANDLE *lpHandles,
+ IN BOOL bWaitAll,
+ IN DWORD dwMilliseconds)
+{
+ DWORD dwRet;
+
+ PERF_ENTRY(WaitForMultipleObjects);
+ ENTRY("WaitForMultipleObjects(nCount=%d, lpHandles=%p,"
+ " bWaitAll=%d, dwMilliseconds=%u)\n",
+ nCount, lpHandles, bWaitAll, dwMilliseconds);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ dwRet = InternalWaitForMultipleObjectsEx(pThread, nCount, lpHandles,
+ bWaitAll, dwMilliseconds, FALSE);
+
+ LOGEXIT("WaitForMultipleObjects returns DWORD %u\n", dwRet);
+ PERF_EXIT(WaitForMultipleObjects);
+ return dwRet;
+}
+
+/*++
+Function:
+ WaitForMultipleObjectsEx
+
+See MSDN doc for info about this function.
+--*/
+DWORD
+PALAPI
+WaitForMultipleObjectsEx(IN DWORD nCount,
+ IN CONST HANDLE *lpHandles,
+ IN BOOL bWaitAll,
+ IN DWORD dwMilliseconds,
+ IN BOOL bAlertable)
+{
+ DWORD dwRet;
+
+ PERF_ENTRY(WaitForMultipleObjectsEx);
+ ENTRY("WaitForMultipleObjectsEx(nCount=%d, lpHandles=%p,"
+ " bWaitAll=%d, dwMilliseconds=%u, bAlertable=d)\n",
+ nCount, lpHandles, bWaitAll, dwMilliseconds, bAlertable);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ dwRet = InternalWaitForMultipleObjectsEx(pThread, nCount, lpHandles, bWaitAll,
+ dwMilliseconds, bAlertable);
+
+ LOGEXIT("WaitForMultipleObjectsEx returns DWORD %u\n", dwRet);
+ PERF_EXIT(WaitForMultipleObjectsEx);
+ return dwRet;
+}
+
+/*++
+Function:
+ Sleep
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+Sleep(IN DWORD dwMilliseconds)
+{
+ PERF_ENTRY(Sleep);
+ ENTRY("Sleep(dwMilliseconds=%u)\n", dwMilliseconds);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ DWORD internalSleepRet = InternalSleepEx(pThread, dwMilliseconds, FALSE);
+
+ if (internalSleepRet != 0)
+ {
+ ERROR("Sleep(dwMilliseconds=%u) failed [error=%u]\n", dwMilliseconds, internalSleepRet);
+ pThread->SetLastError(internalSleepRet);
+ }
+
+ LOGEXIT("Sleep returns VOID\n");
+ PERF_EXIT(Sleep);
+}
+
+
+/*++
+Function:
+ SleepEx
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+SleepEx(IN DWORD dwMilliseconds,
+ IN BOOL bAlertable)
+{
+ DWORD dwRet;
+
+ PERF_ENTRY(SleepEx);
+ ENTRY("SleepEx(dwMilliseconds=%u, bAlertable=%d)\n", dwMilliseconds, bAlertable);
+
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ dwRet = InternalSleepEx(pThread, dwMilliseconds, bAlertable);
+
+ LOGEXIT("SleepEx returns DWORD %u\n", dwRet);
+ PERF_EXIT(SleepEx);
+
+ return dwRet;
+}
+
+/*++
+Function:
+ QueueUserAPC
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+QueueUserAPC(
+ PAPCFUNC pfnAPC,
+ HANDLE hThread,
+ ULONG_PTR dwData)
+{
+ CPalThread * pCurrentThread = NULL;
+ CPalThread * pTargetThread = NULL;
+ IPalObject * pTargetThreadObject = NULL;
+ PAL_ERROR palErr;
+ DWORD dwRet;
+
+ PERF_ENTRY(QueueUserAPC);
+ ENTRY("QueueUserAPC(pfnAPC=%p, hThread=%p, dwData=%#x)\n",
+ pfnAPC, hThread, dwData);
+
+ /* NOTE: Windows does not check the validity of pfnAPC, even if it is
+ NULL. It just does an access violation later on when the APC call
+ is attempted */
+
+ pCurrentThread = InternalGetCurrentThread();
+
+ palErr = InternalGetThreadDataFromHandle(
+ pCurrentThread,
+ hThread,
+ 0, // THREAD_SET_CONTEXT
+ &pTargetThread,
+ &pTargetThreadObject
+ );
+
+ if (NO_ERROR != palErr)
+ {
+ ERROR("Unable to obtain thread data for handle %p (error %x)!\n",
+ hThread, palErr);
+ goto QueueUserAPC_exit;
+ }
+
+
+ palErr = g_pSynchronizationManager->QueueUserAPC(pCurrentThread, pTargetThread,
+ pfnAPC, dwData);
+
+QueueUserAPC_exit:
+ if (pTargetThreadObject)
+ {
+ pTargetThreadObject->ReleaseReference(pCurrentThread);
+ }
+
+ dwRet = (NO_ERROR == palErr) ? 1 : 0;
+
+ LOGEXIT("QueueUserAPC returns DWORD %d\n", dwRet);
+ PERF_EXIT(QueueUserAPC);
+ return dwRet;
+}
+
+DWORD CorUnix::InternalWaitForMultipleObjectsEx(
+ CPalThread * pThread,
+ DWORD nCount,
+ CONST HANDLE *lpHandles,
+ BOOL bWaitAll,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ DWORD dwRet = WAIT_FAILED;
+ PAL_ERROR palErr = NO_ERROR;
+ int i, iSignaledObjCount, iSignaledObjIndex = -1;
+ bool fWAll = (bool)bWaitAll, fNeedToBlock = false;
+ bool fAbandoned = false;
+ WaitType wtWaitType;
+
+ IPalObject * pIPalObjStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL };
+ ISynchWaitController * pISyncStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL };
+ IPalObject ** ppIPalObjs = pIPalObjStackArray;
+ ISynchWaitController ** ppISyncWaitCtrlrs = pISyncStackArray;
+
+ if ((nCount == 0) || (nCount > MAXIMUM_WAIT_OBJECTS))
+ {
+ ppIPalObjs = NULL; // make delete at the end safe
+ ppISyncWaitCtrlrs = NULL; // make delete at the end safe
+ ERROR("Invalid object count=%d [range: 1 to %d]\n",
+ nCount, MAXIMUM_WAIT_OBJECTS)
+ pThread->SetLastError(ERROR_INVALID_PARAMETER);
+ goto WFMOExIntExit;
+ }
+ else if (nCount == 1)
+ {
+ fWAll = false; // makes no difference when nCount is 1
+ wtWaitType = SingleObject;
+ }
+ else
+ {
+ wtWaitType = fWAll ? MultipleObjectsWaitAll : MultipleObjectsWaitOne;
+ if (nCount > MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE)
+ {
+ ppIPalObjs = InternalNewArray<IPalObject*>(nCount);
+ ppISyncWaitCtrlrs = InternalNewArray<ISynchWaitController*>(nCount);
+ if ((NULL == ppIPalObjs) || (NULL == ppISyncWaitCtrlrs))
+ {
+ ERROR("Out of memory allocating internal structures\n");
+ pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto WFMOExIntExit;
+ }
+ }
+ }
+
+ palErr = g_pObjectManager->ReferenceMultipleObjectsByHandleArray(pThread,
+ (VOID **)lpHandles,
+ nCount,
+ &sg_aotWaitObject,
+ SYNCHRONIZE,
+ 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)
+ {
+ // Check for any cross-process sync objects. "Wait for any" and "wait for all" operations are not supported on
+ // cross-process sync objects in the PAL.
+ for (DWORD i = 0; i < nCount; ++i)
+ {
+ if (ppIPalObjs[i]->GetObjectType()->GetId() == otiNamedMutex)
+ {
+ 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;
+ }
+ }
+ }
+ else if (ppIPalObjs[0]->GetObjectType()->GetId() == otiNamedMutex)
+ {
+ SharedMemoryProcessDataHeader *processDataHeader =
+ SharedMemoryProcessDataHeader::PalObject_GetProcessDataHeader(ppIPalObjs[0]);
+ _ASSERTE(processDataHeader != nullptr);
+ try
+ {
+ MutexTryAcquireLockResult tryAcquireLockResult =
+ static_cast<NamedMutexProcessData *>(processDataHeader->GetData())->TryAcquireLock(dwMilliseconds);
+ switch (tryAcquireLockResult)
+ {
+ case MutexTryAcquireLockResult::AcquiredLock:
+ dwRet = WAIT_OBJECT_0;
+ break;
+
+ case MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned:
+ dwRet = WAIT_ABANDONED_0;
+ break;
+
+ case MutexTryAcquireLockResult::TimedOut:
+ dwRet = WAIT_TIMEOUT;
+ break;
+
+ default:
+ _ASSERTE(false);
+ break;
+ }
+ }
+ catch (SharedMemoryException ex)
+ {
+ pThread->SetLastError(ex.GetErrorCode());
+ }
+ 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)
+ {
+ // First check for pending APC. We need to do that while holding the global
+ // synch lock implicitely grabbed by GetSynchWaitControllersForObjects
+ if (g_pSynchronizationManager->AreAPCsPending(pThread))
+ {
+ // If there is any pending APC we need to release the
+ // implicit global synch lock before calling into it
+ for (i = 0; (i < (int)nCount) && (NULL != ppISyncWaitCtrlrs[i]); i++)
+ {
+ ppISyncWaitCtrlrs[i]->ReleaseController();
+ ppISyncWaitCtrlrs[i] = NULL;
+ }
+ palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread);
+ if (NO_ERROR == palErr)
+ {
+ dwRet = WAIT_IO_COMPLETION;
+ }
+ else
+ {
+ ASSERT("Awakened for APC, but no APC is pending\n");
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ dwRet = WAIT_FAILED;
+ }
+ goto WFMOExIntCleanup;
+ }
+ }
+
+ iSignaledObjCount = 0;
+ iSignaledObjIndex = -1;
+ for (i=0;i<(int)nCount;i++)
+ {
+ bool fValue;
+ palErr = ppISyncWaitCtrlrs[i]->CanThreadWaitWithoutBlocking(&fValue, &fAbandoned);
+ 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 (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));
+ 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 Alerted:
+ _ASSERT_MSG(bAlertable,
+ "Awakened for APC from a non-alertable wait\n");
+
+ dwRet = WAIT_IO_COMPLETION;
+ palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread);
+
+ _ASSERT_MSG(NO_ERROR == palErr,
+ "Awakened for APC, but no APC is pending\n");
+ break;
+ case WaitFailed:
+ default:
+ ERROR("Thread %p awakened with some failure\n", pThread);
+ dwRet = WAIT_FAILED;
+ break;
+ }
+ }
+
+ if (!fWAll && ((WAIT_OBJECT_0 == dwRet) || (WAIT_ABANDONED_0 == dwRet)))
+ {
+ _ASSERT_MSG(0 <= iSignaledObjIndex,
+ "Failed to identify signaled/abandoned object\n");
+ _ASSERT_MSG(iSignaledObjIndex >= 0 && nCount > static_cast<DWORD>(iSignaledObjIndex),
+ "SignaledObjIndex object out of range "
+ "[index=%d obj_count=%u\n",
+ iSignaledObjCount, nCount);
+
+ if (iSignaledObjIndex < 0)
+ {
+ pThread->SetLastError(ERROR_INTERNAL_ERROR);
+ dwRet = WAIT_FAILED;
+ goto WFMOExIntCleanup;
+ }
+ dwRet += iSignaledObjIndex;
+ }
+
+WFMOExIntCleanup:
+ for (i = 0; i < (int)nCount; i++)
+ {
+ ppIPalObjs[i]->ReleaseReference(pThread);
+ ppIPalObjs[i] = NULL;
+ }
+
+WFMOExIntExit:
+ if (nCount > MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE)
+ {
+ InternalDeleteArray(ppIPalObjs);
+ InternalDeleteArray(ppISyncWaitCtrlrs);
+ }
+
+ return dwRet;
+}
+
+DWORD CorUnix::InternalSleepEx (
+ CPalThread * pThread,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ PAL_ERROR palErr = NO_ERROR;
+ DWORD dwRet = WAIT_FAILED;
+ int iSignaledObjIndex;
+
+ TRACE("Sleeping %u ms [bAlertable=%d]", dwMilliseconds, (int)bAlertable);
+
+ if (bAlertable)
+ {
+ // In this case do not use AreAPCsPending. In fact, since we are
+ // not holding the synch lock(s) an APC posting may race with
+ // AreAPCsPending.
+ palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread);
+ if (NO_ERROR == palErr)
+ {
+ return WAIT_IO_COMPLETION;
+ }
+ }
+
+ if (dwMilliseconds > 0)
+ {
+ ThreadWakeupReason twrWakeupReason;
+ palErr = g_pSynchronizationManager->BlockThread(pThread,
+ dwMilliseconds,
+ (TRUE == bAlertable),
+ true,
+ &twrWakeupReason,
+ (DWORD *)&iSignaledObjIndex);
+ if (NO_ERROR != palErr)
+ {
+ ERROR("IPalSynchronizationManager::BlockThread failed for thread "
+ "pThread=%p [error=%u]\n", pThread, palErr);
+ return dwRet;
+ }
+
+ switch (twrWakeupReason)
+ {
+ case WaitSucceeded:
+ case WaitTimeout:
+ dwRet = 0;
+ break;
+ case Alerted:
+ _ASSERT_MSG(bAlertable, "Awakened for APC from a non-alertable wait\n");
+
+ dwRet = WAIT_IO_COMPLETION;
+ palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread);
+ _ASSERT_MSG(NO_ERROR == palErr, "Awakened for APC, but no APC is pending\n");
+
+ break;
+ case MutexAbondoned:
+ ASSERT("Thread %p awakened with reason=MutexAbondoned from a SleepEx\n", pThread);
+ break;
+ case WaitFailed:
+ default:
+ ERROR("Thread %p awakened with some failure\n", pThread);
+ break;
+ }
+ }
+ else
+ {
+ dwRet = 0;
+ }
+
+ TRACE("Done sleeping %u ms [bAlertable=%d]", dwMilliseconds, (int)bAlertable);
+ return dwRet;
+}
+
diff --git a/src/pal/src/synchobj/event.cpp b/src/pal/src/synchobj/event.cpp
new file mode 100644
index 0000000000..54addad51c
--- /dev/null
+++ b/src/pal/src/synchobj/event.cpp
@@ -0,0 +1,589 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ event.cpp
+
+Abstract:
+
+ Implementation of event synchronization object as described in
+ the WIN32 API
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/event.hpp"
+#include "pal/thread.hpp"
+#include "pal/dbgmsg.h"
+
+using namespace CorUnix;
+
+/* ------------------- Definitions ------------------------------*/
+SET_DEFAULT_DEBUG_CHANNEL(SYNC);
+
+CObjectType CorUnix::otManualResetEvent(
+ otiManualResetEvent,
+ NULL, // No cleanup routine
+ NULL, // No initialization routine
+ 0, // No immutable data
+ 0, // No process local data
+ 0, // No shared data
+ EVENT_ALL_ACCESS, // Currently ignored (no Win32 security)
+ CObjectType::SecuritySupported,
+ CObjectType::SecurityInfoNotPersisted,
+ CObjectType::UnnamedObject,
+ CObjectType::LocalDuplicationOnly,
+ CObjectType::WaitableObject,
+ CObjectType::ObjectCanBeUnsignaled,
+ CObjectType::ThreadReleaseHasNoSideEffects,
+ CObjectType::NoOwner
+ );
+
+CObjectType CorUnix::otAutoResetEvent(
+ otiAutoResetEvent,
+ NULL, // No cleanup routine
+ NULL, // No initialization routine
+ 0, // No immutable data
+ 0, // No process local data
+ 0, // No shared data
+ EVENT_ALL_ACCESS, // Currently ignored (no Win32 security)
+ CObjectType::SecuritySupported,
+ CObjectType::SecurityInfoNotPersisted,
+ CObjectType::UnnamedObject,
+ CObjectType::LocalDuplicationOnly,
+ CObjectType::WaitableObject,
+ CObjectType::ObjectCanBeUnsignaled,
+ CObjectType::ThreadReleaseAltersSignalCount,
+ CObjectType::NoOwner
+ );
+
+PalObjectTypeId rgEventIds[] = {otiManualResetEvent, otiAutoResetEvent};
+CAllowedObjectTypes aotEvent(rgEventIds, sizeof(rgEventIds)/sizeof(rgEventIds[0]));
+
+/*++
+Function:
+ CreateEventA
+
+Note:
+ lpEventAttributes currentely ignored:
+ -- Win32 object security not supported
+ -- handles to event objects are not inheritable
+
+Parameters:
+ See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+CreateEventA(
+ IN LPSECURITY_ATTRIBUTES lpEventAttributes,
+ IN BOOL bManualReset,
+ IN BOOL bInitialState,
+ IN LPCSTR lpName)
+{
+ HANDLE hEvent = NULL;
+ CPalThread *pthr = NULL;
+ PAL_ERROR palError;
+
+ PERF_ENTRY(CreateEventA);
+ ENTRY("CreateEventA(lpEventAttr=%p, bManualReset=%d, bInitialState=%d, lpName=%p (%s)\n",
+ lpEventAttributes, bManualReset, bInitialState, lpName, lpName?lpName:"NULL");
+
+ pthr = InternalGetCurrentThread();
+
+ if (lpName != nullptr)
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ }
+ else
+ {
+ palError = InternalCreateEvent(
+ pthr,
+ lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ NULL,
+ &hEvent
+ );
+ }
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pthr->SetLastError(palError);
+
+ LOGEXIT("CreateEventA returns HANDLE %p\n", hEvent);
+ PERF_EXIT(CreateEventA);
+ return hEvent;
+}
+
+
+/*++
+Function:
+ CreateEventW
+
+Note:
+ lpEventAttributes currentely ignored:
+ -- Win32 object security not supported
+ -- handles to event objects are not inheritable
+
+Parameters:
+ See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+CreateEventW(
+ IN LPSECURITY_ATTRIBUTES lpEventAttributes,
+ IN BOOL bManualReset,
+ IN BOOL bInitialState,
+ IN LPCWSTR lpName)
+{
+ HANDLE hEvent = NULL;
+ PAL_ERROR palError;
+ CPalThread *pthr = NULL;
+
+ PERF_ENTRY(CreateEventW);
+ ENTRY("CreateEventW(lpEventAttr=%p, bManualReset=%d, "
+ "bInitialState=%d, lpName=%p (%S)\n", lpEventAttributes, bManualReset,
+ bInitialState, lpName, lpName?lpName:W16_NULLSTRING);
+
+ pthr = InternalGetCurrentThread();
+
+ palError = InternalCreateEvent(
+ pthr,
+ lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName,
+ &hEvent
+ );
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pthr->SetLastError(palError);
+
+ LOGEXIT("CreateEventW returns HANDLE %p\n", hEvent);
+ PERF_EXIT(CreateEventW);
+ return hEvent;
+}
+
+/*++
+Function:
+ InternalCreateEvent
+
+Note:
+ lpEventAttributes currentely ignored:
+ -- Win32 object security not supported
+ -- handles to event objects are not inheritable
+
+Parameters:
+ pthr -- thread data for calling thread
+ phEvent -- on success, receives the allocated event handle
+
+ See MSDN docs on CreateEvent for all other parameters
+--*/
+
+PAL_ERROR
+CorUnix::InternalCreateEvent(
+ CPalThread *pthr,
+ LPSECURITY_ATTRIBUTES lpEventAttributes,
+ BOOL bManualReset,
+ BOOL bInitialState,
+ LPCWSTR lpName,
+ HANDLE *phEvent
+ )
+{
+ CObjectAttributes oa(lpName, lpEventAttributes);
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjEvent = NULL;
+ IPalObject *pobjRegisteredEvent = NULL;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != phEvent);
+
+ ENTRY("InternalCreateEvent(pthr=%p, lpEventAttributes=%p, bManualReset=%i, "
+ "bInitialState=%i, lpName=%p, phEvent=%p)\n",
+ pthr,
+ lpEventAttributes,
+ bManualReset,
+ bInitialState,
+ lpName,
+ phEvent
+ );
+
+ if (lpName != nullptr)
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ goto InternalCreateEventExit;
+ }
+
+ palError = g_pObjectManager->AllocateObject(
+ pthr,
+ bManualReset ? &otManualResetEvent : &otAutoResetEvent,
+ &oa,
+ &pobjEvent
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateEventExit;
+ }
+
+ if (bInitialState)
+ {
+ ISynchStateController *pssc;
+
+ palError = pobjEvent->GetSynchStateController(
+ pthr,
+ &pssc
+ );
+
+ if (NO_ERROR == palError)
+ {
+ palError = pssc->SetSignalCount(1);
+ pssc->ReleaseController();
+ }
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to set new event state (%d)\n", palError);
+ goto InternalCreateEventExit;
+ }
+ }
+
+ palError = g_pObjectManager->RegisterObject(
+ pthr,
+ pobjEvent,
+ &aotEvent,
+ EVENT_ALL_ACCESS, // Currently ignored (no Win32 security)
+ phEvent,
+ &pobjRegisteredEvent
+ );
+
+ //
+ // pobjEvent is invalidated by the call to RegisterObject, so NULL it
+ // out here to ensure that we don't try to release a reference on
+ // it down the line.
+ //
+
+ pobjEvent = NULL;
+
+InternalCreateEventExit:
+
+ if (NULL != pobjEvent)
+ {
+ pobjEvent->ReleaseReference(pthr);
+ }
+
+ if (NULL != pobjRegisteredEvent)
+ {
+ pobjRegisteredEvent->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("InternalCreateEvent returns %i\n", palError);
+
+ return palError;
+}
+
+
+/*++
+Function:
+ SetEvent
+
+See MSDN doc.
+--*/
+
+BOOL
+PALAPI
+SetEvent(
+ IN HANDLE hEvent)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthr = NULL;
+
+ PERF_ENTRY(SetEvent);
+ ENTRY("SetEvent(hEvent=%p)\n", hEvent);
+
+ pthr = InternalGetCurrentThread();
+
+ palError = InternalSetEvent(pthr, hEvent, TRUE);
+
+ if (NO_ERROR != palError)
+ {
+ pthr->SetLastError(palError);
+ }
+
+ LOGEXIT("SetEvent returns BOOL %d\n", (NO_ERROR == palError));
+ PERF_EXIT(SetEvent);
+ return (NO_ERROR == palError);
+}
+
+
+/*++
+Function:
+ ResetEvent
+
+See MSDN doc.
+--*/
+
+BOOL
+PALAPI
+ResetEvent(
+ IN HANDLE hEvent)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthr = NULL;
+
+ PERF_ENTRY(ResetEvent);
+ ENTRY("ResetEvent(hEvent=%p)\n", hEvent);
+
+ pthr = InternalGetCurrentThread();
+
+ palError = InternalSetEvent(pthr, hEvent, FALSE);
+
+ if (NO_ERROR != palError)
+ {
+ pthr->SetLastError(palError);
+ }
+
+ LOGEXIT("ResetEvent returns BOOL %d\n", (NO_ERROR == palError));
+ PERF_EXIT(ResetEvent);
+ return (NO_ERROR == palError);
+}
+
+/*++
+Function:
+ InternalCreateEvent
+
+Parameters:
+ pthr -- thread data for calling thread
+ hEvent -- handle to the event to set
+ fSetEvent -- if TRUE, set the event; if FALSE, reset it
+--*/
+
+PAL_ERROR
+CorUnix::InternalSetEvent(
+ CPalThread *pthr,
+ HANDLE hEvent,
+ BOOL fSetEvent
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjEvent = NULL;
+ ISynchStateController *pssc = NULL;
+
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("InternalSetEvent(pthr=%p, hEvent=%p, fSetEvent=%i\n",
+ pthr,
+ hEvent,
+ fSetEvent
+ );
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pthr,
+ hEvent,
+ &aotEvent,
+ 0, // Should be EVENT_MODIFY_STATE; currently ignored (no Win32 security)
+ &pobjEvent
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to obtain object for handle %p (error %d)!\n", hEvent, palError);
+ goto InternalSetEventExit;
+ }
+
+ palError = pobjEvent->GetSynchStateController(
+ pthr,
+ &pssc
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Error %d obtaining synch state controller\n", palError);
+ goto InternalSetEventExit;
+ }
+
+ palError = pssc->SetSignalCount(fSetEvent ? 1 : 0);
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Error %d setting event state\n", palError);
+ goto InternalSetEventExit;
+ }
+
+InternalSetEventExit:
+
+ if (NULL != pssc)
+ {
+ pssc->ReleaseController();
+ }
+
+ if (NULL != pobjEvent)
+ {
+ pobjEvent->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("InternalSetEvent returns %d\n", palError);
+
+ return palError;
+}
+
+// TODO: Implementation of OpenEventA() doesn't exist, do we need it? More generally, do we need the A versions at all?
+
+/*++
+Function:
+ OpenEventW
+
+Note:
+ dwDesiredAccess is currently ignored (no Win32 object security support)
+ bInheritHandle is currently ignored (handles to events are not inheritable)
+
+Parameters:
+ See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+OpenEventW(
+ IN DWORD dwDesiredAccess,
+ IN BOOL bInheritHandle,
+ IN LPCWSTR lpName)
+{
+ HANDLE hEvent = NULL;
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthr = NULL;
+
+ PERF_ENTRY(OpenEventW);
+ ENTRY("OpenEventW(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%S))\n",
+ dwDesiredAccess, bInheritHandle, lpName, lpName?lpName:W16_NULLSTRING);
+
+ pthr = InternalGetCurrentThread();
+
+ /* validate parameters */
+ if (lpName == nullptr)
+ {
+ ERROR("name is NULL\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto OpenEventWExit;
+ }
+ else
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ }
+
+OpenEventWExit:
+
+ if (NO_ERROR != palError)
+ {
+ pthr->SetLastError(palError);
+ }
+
+ LOGEXIT("OpenEventW returns HANDLE %p\n", hEvent);
+ PERF_EXIT(OpenEventW);
+
+ return hEvent;
+}
+
+/*++
+Function:
+ InternalOpenEvent
+
+Note:
+ dwDesiredAccess is currently ignored (no Win32 object security support)
+ bInheritHandle is currently ignored (handles to events are not inheritable)
+
+Parameters:
+ pthr -- thread data for calling thread
+ phEvent -- on success, receives the allocated event handle
+
+ See MSDN docs on OpenEvent for all other parameters.
+--*/
+
+PAL_ERROR
+CorUnix::InternalOpenEvent(
+ CPalThread *pthr,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ LPCWSTR lpName,
+ HANDLE *phEvent
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjEvent = NULL;
+ CPalString sObjectName(lpName);
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != lpName);
+ _ASSERTE(NULL != phEvent);
+
+ ENTRY("InternalOpenEvent(pthr=%p, dwDesiredAccess=%#x, bInheritHandle=%d, "
+ "lpName=%p, phEvent=%p)\n",
+ pthr,
+ dwDesiredAccess,
+ bInheritHandle,
+ lpName,
+ phEvent
+ );
+
+ palError = g_pObjectManager->LocateObject(
+ pthr,
+ &sObjectName,
+ &aotEvent,
+ &pobjEvent
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalOpenEventExit;
+ }
+
+ palError = g_pObjectManager->ObtainHandleForObject(
+ pthr,
+ pobjEvent,
+ dwDesiredAccess,
+ bInheritHandle,
+ NULL,
+ phEvent
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalOpenEventExit;
+ }
+
+InternalOpenEventExit:
+
+ if (NULL != pobjEvent)
+ {
+ pobjEvent->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("InternalOpenEvent returns %d\n", palError);
+
+ return palError;
+}
+
diff --git a/src/pal/src/synchobj/mutex.cpp b/src/pal/src/synchobj/mutex.cpp
new file mode 100644
index 0000000000..d929eaa472
--- /dev/null
+++ b/src/pal/src/synchobj/mutex.cpp
@@ -0,0 +1,1584 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ mutex.ccpp
+
+Abstract:
+
+ Implementation of mutex synchroniztion object as described in
+ the WIN32 API
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(SYNC); // some headers have code with asserts, so do this first
+
+#include "pal/mutex.hpp"
+#include "pal/file.hpp"
+#include "pal/thread.hpp"
+
+#include "../synchmgr/synchmanager.hpp"
+
+#include <sys/file.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "pal/sharedmemory.inl"
+
+using namespace CorUnix;
+
+/* ------------------- Definitions ------------------------------*/
+
+CObjectType CorUnix::otMutex(
+ otiMutex,
+ NULL, // No cleanup routine
+ NULL, // No initialization routine
+ 0, // No immutable data
+ 0, // No process local data
+ 0, // No shared data
+ 0, // Should be MUTEX_ALL_ACCESS; currently ignored (no Win32 security)
+ CObjectType::SecuritySupported,
+ CObjectType::SecurityInfoNotPersisted,
+ CObjectType::UnnamedObject,
+ CObjectType::LocalDuplicationOnly,
+ CObjectType::WaitableObject,
+ CObjectType::ObjectCanBeUnsignaled,
+ CObjectType::ThreadReleaseAltersSignalCount,
+ CObjectType::OwnershipTracked
+ );
+
+static CAllowedObjectTypes aotMutex(otiMutex);
+
+CObjectType CorUnix::otNamedMutex(
+ otiNamedMutex,
+ &SharedMemoryProcessDataHeader::PalObject_Close, // Cleanup routine
+ NULL, // No initialization routine
+ sizeof(SharedMemoryProcessDataHeader *), // Immutable data
+ 0, // No process local data
+ 0, // No shared data
+ 0, // Should be MUTEX_ALL_ACCESS; currently ignored (no Win32 security)
+ CObjectType::SecuritySupported,
+ CObjectType::SecurityInfoNotPersisted,
+ CObjectType::UnnamedObject, // PAL's naming infrastructure is not used
+ CObjectType::LocalDuplicationOnly,
+ CObjectType::UnwaitableObject, // PAL's waiting infrastructure is not used
+ CObjectType::SignalingNotApplicable, // PAL's signaling infrastructure is not used
+ CObjectType::ThreadReleaseNotApplicable, // PAL's signaling infrastructure is not used
+ CObjectType::OwnershipNotApplicable // PAL's ownership infrastructure is not used
+ );
+
+static CAllowedObjectTypes aotNamedMutex(otiNamedMutex);
+
+static PalObjectTypeId anyMutexTypeIds[] = {otiMutex, otiNamedMutex};
+static CAllowedObjectTypes aotAnyMutex(anyMutexTypeIds, _countof(anyMutexTypeIds));
+
+/*++
+Function:
+ CreateMutexA
+
+Note:
+ lpMutexAttributes currentely ignored:
+ -- Win32 object security not supported
+ -- handles to mutex objects are not inheritable
+
+Parameters:
+ See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+CreateMutexA(
+ IN LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ IN BOOL bInitialOwner,
+ IN LPCSTR lpName)
+{
+ HANDLE hMutex = NULL;
+ CPalThread *pthr = NULL;
+ PAL_ERROR palError;
+
+ PERF_ENTRY(CreateMutexA);
+ ENTRY("CreateMutexA(lpMutexAttr=%p, bInitialOwner=%d, lpName=%p (%s)\n",
+ lpMutexAttributes, bInitialOwner, lpName, lpName?lpName:"NULL");
+
+ pthr = InternalGetCurrentThread();
+
+ palError = InternalCreateMutex(
+ pthr,
+ lpMutexAttributes,
+ bInitialOwner,
+ lpName,
+ &hMutex
+ );
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pthr->SetLastError(palError);
+
+ LOGEXIT("CreateMutexA returns HANDLE %p\n", hMutex);
+ PERF_EXIT(CreateMutexA);
+ return hMutex;
+}
+
+
+/*++
+Function:
+ CreateMutexW
+
+Note:
+ lpMutexAttributes currentely ignored:
+ -- Win32 object security not supported
+ -- handles to mutex objects are not inheritable
+
+Parameters:
+ See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+CreateMutexW(
+ IN LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ IN BOOL bInitialOwner,
+ IN LPCWSTR lpName)
+{
+ HANDLE hMutex = NULL;
+ PAL_ERROR palError;
+ CPalThread *pthr = NULL;
+ char utf8Name[SHARED_MEMORY_MAX_NAME_CHAR_COUNT + 1];
+
+ PERF_ENTRY(CreateMutexW);
+ ENTRY("CreateMutexW(lpMutexAttr=%p, bInitialOwner=%d, lpName=%p (%S)\n",
+ lpMutexAttributes, bInitialOwner, lpName, lpName?lpName:W16_NULLSTRING);
+
+ pthr = InternalGetCurrentThread();
+
+ if (lpName != nullptr)
+ {
+ int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, _countof(utf8Name), nullptr, nullptr);
+ if (bytesWritten == 0)
+ {
+ DWORD errorCode = GetLastError();
+ if (errorCode == ERROR_INSUFFICIENT_BUFFER)
+ {
+ palError = static_cast<DWORD>(SharedMemoryError::NameTooLong);
+ }
+ else
+ {
+ ASSERT("WideCharToMultiByte failed (%u)\n", errorCode);
+ palError = errorCode;
+ }
+ goto CreateMutexWExit;
+ }
+ }
+
+ palError = InternalCreateMutex(
+ pthr,
+ lpMutexAttributes,
+ bInitialOwner,
+ lpName == nullptr ? nullptr : utf8Name,
+ &hMutex
+ );
+
+CreateMutexWExit:
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pthr->SetLastError(palError);
+
+ LOGEXIT("CreateMutexW returns HANDLE %p\n", hMutex);
+ PERF_EXIT(CreateMutexW);
+ return hMutex;
+}
+
+/*++
+Function:
+ InternalCreateMutex
+
+Note:
+ lpMutexAttributes currentely ignored:
+ -- Win32 object security not supported
+ -- handles to mutex objects are not inheritable
+
+Parameters:
+ pthr -- thread data for calling thread
+ phEvent -- on success, receives the allocated mutex handle
+
+ See MSDN docs on CreateMutex for all other parameters
+--*/
+
+PAL_ERROR
+CorUnix::InternalCreateMutex(
+ CPalThread *pthr,
+ LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCSTR lpName,
+ HANDLE *phMutex
+ )
+{
+ CObjectAttributes oa(nullptr, lpMutexAttributes);
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjMutex = NULL;
+ IPalObject *pobjRegisteredMutex = NULL;
+ ISynchStateController *pssc = NULL;
+ HANDLE hMutex = nullptr;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != phMutex);
+
+ ENTRY("InternalCreateMutex(pthr=%p, lpMutexAttributes=%p, bInitialOwner=%d"
+ ", lpName=%p, phMutex=%p)\n",
+ pthr,
+ lpMutexAttributes,
+ bInitialOwner,
+ lpName,
+ phMutex
+ );
+
+ if (lpName != nullptr && lpName[0] == '\0')
+ {
+ // Empty name is treated as a request for an unnamed process-local mutex
+ lpName = nullptr;
+ }
+
+ CObjectType *ot = lpName == nullptr ? &otMutex : &otNamedMutex;
+ CAllowedObjectTypes *aot = lpName == nullptr ? &aotMutex : &aotNamedMutex;
+
+ palError = g_pObjectManager->AllocateObject(
+ pthr,
+ ot,
+ &oa,
+ &pobjMutex
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateMutexExit;
+ }
+
+ if (lpName == nullptr)
+ {
+ palError = pobjMutex->GetSynchStateController(
+ pthr,
+ &pssc
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to create state controller (%d)\n", palError);
+ goto InternalCreateMutexExit;
+ }
+
+ if (bInitialOwner)
+ {
+ palError = pssc->SetOwner(pthr);
+ }
+ else
+ {
+ palError = pssc->SetSignalCount(1);
+ }
+
+ pssc->ReleaseController();
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to set initial mutex state (%d)\n", palError);
+ goto InternalCreateMutexExit;
+ }
+ }
+
+ palError = g_pObjectManager->RegisterObject(
+ pthr,
+ pobjMutex,
+ aot,
+ 0, // should be MUTEX_ALL_ACCESS -- currently ignored (no Win32 security)
+ &hMutex,
+ &pobjRegisteredMutex
+ );
+
+ if (palError != NO_ERROR)
+ {
+ _ASSERTE(palError != ERROR_ALREADY_EXISTS); // PAL's naming infrastructure is not used for named mutexes
+ _ASSERTE(pobjRegisteredMutex == nullptr);
+ _ASSERTE(hMutex == nullptr);
+ goto InternalCreateMutexExit;
+ }
+
+ // Now that the object has been registered successfully, it would have a reference associated with the handle, so release
+ // the initial reference. Any errors from now on need to revoke the handle.
+ _ASSERTE(pobjRegisteredMutex == pobjMutex);
+ _ASSERTE(hMutex != nullptr);
+ pobjMutex->ReleaseReference(pthr);
+ pobjRegisteredMutex = nullptr;
+
+ if (lpName != nullptr)
+ {
+ SharedMemoryProcessDataHeader *processDataHeader;
+ bool created = false;
+ try
+ {
+ processDataHeader = NamedMutexProcessData::CreateOrOpen(lpName, !!bInitialOwner, &created);
+ }
+ catch (SharedMemoryException ex)
+ {
+ palError = ex.GetErrorCode();
+ goto InternalCreateMutexExit;
+ }
+ SharedMemoryProcessDataHeader::PalObject_SetProcessDataHeader(pobjMutex, processDataHeader);
+
+ if (!created)
+ {
+ // Indicate to the caller that an existing mutex was opened, and hence the caller will not have initial ownership
+ // of the mutex if requested through bInitialOwner
+ palError = ERROR_ALREADY_EXISTS;
+ }
+ }
+
+ *phMutex = hMutex;
+ hMutex = nullptr;
+ pobjMutex = nullptr;
+
+InternalCreateMutexExit:
+
+ _ASSERTE(pobjRegisteredMutex == nullptr);
+ if (hMutex != nullptr)
+ {
+ g_pObjectManager->RevokeHandle(pthr, hMutex);
+ }
+ else if (NULL != pobjMutex)
+ {
+ pobjMutex->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("InternalCreateMutex returns %i\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ ReleaseMutex
+
+Parameters:
+ See MSDN doc.
+--*/
+
+BOOL
+PALAPI
+ReleaseMutex( IN HANDLE hMutex )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthr = NULL;
+
+ PERF_ENTRY(ReleaseMutex);
+ ENTRY("ReleaseMutex(hMutex=%p)\n", hMutex);
+
+ pthr = InternalGetCurrentThread();
+
+ palError = InternalReleaseMutex(pthr, hMutex);
+
+ if (NO_ERROR != palError)
+ {
+ pthr->SetLastError(palError);
+ }
+
+ LOGEXIT("ReleaseMutex returns BOOL %d\n", (NO_ERROR == palError));
+ PERF_EXIT(ReleaseMutex);
+ return (NO_ERROR == palError);
+}
+
+/*++
+Function:
+ InternalReleaseMutex
+
+Parameters:
+ pthr -- thread data for calling thread
+
+ See MSDN docs on ReleaseMutex for all other parameters
+--*/
+
+PAL_ERROR
+CorUnix::InternalReleaseMutex(
+ CPalThread *pthr,
+ HANDLE hMutex
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjMutex = NULL;
+ ISynchStateController *pssc = NULL;
+ PalObjectTypeId objectTypeId;
+
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("InternalReleaseMutex(pthr=%p, hMutex=%p)\n",
+ pthr,
+ hMutex
+ );
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pthr,
+ hMutex,
+ &aotAnyMutex,
+ 0, // should be MUTEX_MODIFY_STATE -- current ignored (no Win32 security)
+ &pobjMutex
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to obtain object for handle %p (error %d)!\n", hMutex, palError);
+ goto InternalReleaseMutexExit;
+ }
+
+ objectTypeId = pobjMutex->GetObjectType()->GetId();
+ if (objectTypeId == otiMutex)
+ {
+ palError = pobjMutex->GetSynchStateController(
+ pthr,
+ &pssc
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Error %d obtaining synch state controller\n", palError);
+ goto InternalReleaseMutexExit;
+ }
+
+ palError = pssc->DecrementOwnershipCount();
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Error %d decrementing mutex ownership count\n", palError);
+ goto InternalReleaseMutexExit;
+ }
+ }
+ else
+ {
+ _ASSERTE(objectTypeId == otiNamedMutex);
+
+ SharedMemoryProcessDataHeader *processDataHeader =
+ SharedMemoryProcessDataHeader::PalObject_GetProcessDataHeader(pobjMutex);
+ _ASSERTE(processDataHeader != nullptr);
+ try
+ {
+ static_cast<NamedMutexProcessData *>(processDataHeader->GetData())->ReleaseLock();
+ }
+ catch (SharedMemoryException ex)
+ {
+ palError = ex.GetErrorCode();
+ goto InternalReleaseMutexExit;
+ }
+ }
+
+InternalReleaseMutexExit:
+
+ if (NULL != pssc)
+ {
+ pssc->ReleaseController();
+ }
+
+ if (NULL != pobjMutex)
+ {
+ pobjMutex->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("InternalReleaseMutex returns %i\n", palError);
+
+ return palError;
+}
+
+/*++
+Function:
+ OpenMutexA
+
+Note:
+ dwDesiredAccess is currently ignored (no Win32 object security support)
+ bInheritHandle is currently ignored (handles to mutexes are not inheritable)
+
+See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+OpenMutexA (
+ IN DWORD dwDesiredAccess,
+ IN BOOL bInheritHandle,
+ IN LPCSTR lpName)
+{
+ HANDLE hMutex = NULL;
+ CPalThread *pthr = NULL;
+ PAL_ERROR palError;
+
+ PERF_ENTRY(OpenMutexA);
+ ENTRY("OpenMutexA(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%s))\n",
+ dwDesiredAccess, bInheritHandle, lpName, lpName?lpName:"NULL");
+
+ pthr = InternalGetCurrentThread();
+
+ /* validate parameters */
+ if (lpName == nullptr)
+ {
+ ERROR("name is NULL\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto OpenMutexAExit;
+ }
+
+ palError = InternalOpenMutex(pthr, dwDesiredAccess, bInheritHandle, lpName, &hMutex);
+
+OpenMutexAExit:
+ if (NO_ERROR != palError)
+ {
+ pthr->SetLastError(palError);
+ }
+
+ LOGEXIT("OpenMutexA returns HANDLE %p\n", hMutex);
+ PERF_EXIT(OpenMutexA);
+ return hMutex;
+}
+
+/*++
+Function:
+ OpenMutexW
+
+Note:
+ dwDesiredAccess is currently ignored (no Win32 object security support)
+ bInheritHandle is currently ignored (handles to mutexes are not inheritable)
+
+See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+OpenMutexW(
+ IN DWORD dwDesiredAccess,
+ IN BOOL bInheritHandle,
+ IN LPCWSTR lpName)
+{
+ HANDLE hMutex = NULL;
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthr = NULL;
+ char utf8Name[SHARED_MEMORY_MAX_NAME_CHAR_COUNT + 1];
+
+ PERF_ENTRY(OpenMutexW);
+ ENTRY("OpenMutexW(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%S))\n",
+ dwDesiredAccess, bInheritHandle, lpName, lpName?lpName:W16_NULLSTRING);
+
+ pthr = InternalGetCurrentThread();
+
+ /* validate parameters */
+ if (lpName == nullptr)
+ {
+ ERROR("name is NULL\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto OpenMutexWExit;
+ }
+
+ {
+ int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, _countof(utf8Name), nullptr, nullptr);
+ if (bytesWritten == 0)
+ {
+ DWORD errorCode = GetLastError();
+ if (errorCode == ERROR_INSUFFICIENT_BUFFER)
+ {
+ palError = static_cast<DWORD>(SharedMemoryError::NameTooLong);
+ }
+ else
+ {
+ ASSERT("WideCharToMultiByte failed (%u)\n", errorCode);
+ palError = errorCode;
+ }
+ goto OpenMutexWExit;
+ }
+ }
+
+ palError = InternalOpenMutex(pthr, dwDesiredAccess, bInheritHandle, lpName == nullptr ? nullptr : utf8Name, &hMutex);
+
+OpenMutexWExit:
+ if (NO_ERROR != palError)
+ {
+ pthr->SetLastError(palError);
+ }
+
+ LOGEXIT("OpenMutexW returns HANDLE %p\n", hMutex);
+ PERF_EXIT(OpenMutexW);
+
+ return hMutex;
+}
+
+/*++
+Function:
+ InternalOpenMutex
+
+Note:
+ dwDesiredAccess is currently ignored (no Win32 object security support)
+ bInheritHandle is currently ignored (handles to mutexes are not inheritable)
+
+Parameters:
+ pthr -- thread data for calling thread
+ phEvent -- on success, receives the allocated mutex handle
+
+ See MSDN docs on OpenMutex for all other parameters.
+--*/
+
+PAL_ERROR
+CorUnix::InternalOpenMutex(
+ CPalThread *pthr,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ LPCSTR lpName,
+ HANDLE *phMutex
+ )
+{
+ CObjectAttributes oa;
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjMutex = NULL;
+ IPalObject *pobjRegisteredMutex = NULL;
+ HANDLE hMutex = nullptr;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != lpName);
+ _ASSERTE(NULL != phMutex);
+
+ ENTRY("InternalOpenMutex(pthr=%p, dwDesiredAcces=%d, bInheritHandle=%d, "
+ "lpName=%p, phMutex=%p)\n",
+ pthr,
+ dwDesiredAccess,
+ bInheritHandle,
+ lpName,
+ phMutex
+ );
+
+ palError = g_pObjectManager->AllocateObject(
+ pthr,
+ &otNamedMutex,
+ &oa,
+ &pobjMutex
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalOpenMutexExit;
+ }
+
+ palError = g_pObjectManager->RegisterObject(
+ pthr,
+ pobjMutex,
+ &aotNamedMutex,
+ dwDesiredAccess,
+ &hMutex,
+ &pobjRegisteredMutex
+ );
+
+ if (palError != NO_ERROR)
+ {
+ _ASSERTE(palError != ERROR_ALREADY_EXISTS); // PAL's naming infrastructure is not used for named mutexes
+ _ASSERTE(pobjRegisteredMutex == nullptr);
+ _ASSERTE(hMutex == nullptr);
+ goto InternalOpenMutexExit;
+ }
+
+ // Now that the object has been registered successfully, it would have a reference associated with the handle, so release
+ // the initial reference. Any errors from now on need to revoke the handle.
+ _ASSERTE(pobjRegisteredMutex == pobjMutex);
+ _ASSERTE(hMutex != nullptr);
+ pobjMutex->ReleaseReference(pthr);
+ pobjRegisteredMutex = nullptr;
+
+ {
+ SharedMemoryProcessDataHeader *processDataHeader;
+ try
+ {
+ processDataHeader = NamedMutexProcessData::Open(lpName);
+ }
+ catch (SharedMemoryException ex)
+ {
+ palError = ex.GetErrorCode();
+ goto InternalOpenMutexExit;
+ }
+ if (processDataHeader == nullptr)
+ {
+ palError = ERROR_FILE_NOT_FOUND;
+ goto InternalOpenMutexExit;
+ }
+ SharedMemoryProcessDataHeader::PalObject_SetProcessDataHeader(pobjMutex, processDataHeader);
+ }
+
+ *phMutex = hMutex;
+ hMutex = nullptr;
+ pobjMutex = nullptr;
+
+InternalOpenMutexExit:
+
+ _ASSERTE(pobjRegisteredMutex == nullptr);
+ if (hMutex != nullptr)
+ {
+ g_pObjectManager->RevokeHandle(pthr, hMutex);
+ }
+ else if (NULL != pobjMutex)
+ {
+ pobjMutex->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("InternalCreateMutex returns %i\n", palError);
+
+ return palError;
+}
+
+
+/* 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)
+{
+ *lock = 0;
+}
+
+DWORD SPINLOCKTryAcquire (LONG * lock)
+{
+ return InterlockedCompareExchange(lock, 1, 0);
+ // only returns 0 or 1.
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// MutexHelpers
+
+#if NAMED_MUTEX_USE_PTHREAD_MUTEX
+void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t *mutex)
+{
+ _ASSERTE(mutex != nullptr);
+
+ struct AutoCleanup
+ {
+ pthread_mutexattr_t *m_mutexAttributes;
+
+ AutoCleanup() : m_mutexAttributes(nullptr)
+ {
+ }
+
+ ~AutoCleanup()
+ {
+ if (m_mutexAttributes != nullptr)
+ {
+ int error = pthread_mutexattr_destroy(m_mutexAttributes);
+ _ASSERTE(error == 0);
+ }
+ }
+ } autoCleanup;
+
+ pthread_mutexattr_t mutexAttributes;
+ int error = pthread_mutexattr_init(&mutexAttributes);
+ if (error != 0)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::OutOfMemory));
+ }
+ autoCleanup.m_mutexAttributes = &mutexAttributes;
+
+ error = pthread_mutexattr_setpshared(&mutexAttributes, PTHREAD_PROCESS_SHARED);
+ _ASSERTE(error == 0);
+
+ error = pthread_mutexattr_setrobust(&mutexAttributes, PTHREAD_MUTEX_ROBUST);
+ _ASSERTE(error == 0);
+
+ error = pthread_mutexattr_settype(&mutexAttributes, PTHREAD_MUTEX_RECURSIVE);
+ _ASSERTE(error == 0);
+
+ error = pthread_mutex_init(mutex, &mutexAttributes);
+ if (error != 0)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(error == EPERM ? SharedMemoryError::IO : SharedMemoryError::OutOfMemory));
+ }
+}
+
+void MutexHelpers::DestroyMutex(pthread_mutex_t *mutex)
+{
+ _ASSERTE(mutex != nullptr);
+
+ int error = pthread_mutex_destroy(mutex);
+ _ASSERTE(error == 0 || error == EBUSY); // the error will be EBUSY if the mutex is locked
+}
+
+MutexTryAcquireLockResult MutexHelpers::TryAcquireLock(pthread_mutex_t *mutex, DWORD timeoutMilliseconds)
+{
+ _ASSERTE(mutex != nullptr);
+
+ int lockResult;
+ switch (timeoutMilliseconds)
+ {
+ case static_cast<DWORD>(-1):
+ lockResult = pthread_mutex_lock(mutex);
+ break;
+
+ case 0:
+ lockResult = pthread_mutex_trylock(mutex);
+ break;
+
+ default:
+ {
+ struct timespec timeoutTime;
+ PAL_ERROR palError = CPalSynchronizationManager::GetAbsoluteTimeout(timeoutMilliseconds, &timeoutTime);
+ _ASSERTE(palError == NO_ERROR);
+ lockResult = pthread_mutex_timedlock(mutex, &timeoutTime);
+ break;
+ }
+ }
+
+ switch (lockResult)
+ {
+ case 0:
+ return MutexTryAcquireLockResult::AcquiredLock;
+
+ case EBUSY:
+ _ASSERTE(timeoutMilliseconds == 0);
+ return MutexTryAcquireLockResult::TimedOut;
+
+ case ETIMEDOUT:
+ _ASSERTE(timeoutMilliseconds != static_cast<DWORD>(-1));
+ _ASSERTE(timeoutMilliseconds != 0);
+ return MutexTryAcquireLockResult::TimedOut;
+
+ case EOWNERDEAD:
+ {
+ int setConsistentResult = pthread_mutex_consistent(mutex);
+ _ASSERTE(setConsistentResult == 0);
+ return MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned;
+ }
+
+ case EAGAIN:
+ throw SharedMemoryException(static_cast<DWORD>(NamedMutexError::MaximumRecursiveLocksReached));
+
+ default:
+ throw SharedMemoryException(static_cast<DWORD>(NamedMutexError::Unknown));
+ }
+}
+
+void MutexHelpers::ReleaseLock(pthread_mutex_t *mutex)
+{
+ _ASSERTE(mutex != nullptr);
+
+ int unlockResult = pthread_mutex_unlock(mutex);
+ _ASSERTE(unlockResult == 0);
+}
+#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// NamedMutexSharedData
+
+NamedMutexSharedData::NamedMutexSharedData()
+ :
+#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ m_timedWaiterCount(0),
+#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ m_lockOwnerProcessId(SharedMemoryHelpers::InvalidProcessId),
+ m_lockOwnerThreadId(SharedMemoryHelpers::InvalidSharedThreadId),
+ m_isAbandoned(false)
+{
+#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ static_assert_no_msg(sizeof(m_timedWaiterCount) == sizeof(LONG)); // for interlocked operations
+#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
+
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionFileLockAcquired());
+
+#if NAMED_MUTEX_USE_PTHREAD_MUTEX
+ MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(&m_lock);
+#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
+}
+
+NamedMutexSharedData::~NamedMutexSharedData()
+{
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionFileLockAcquired());
+
+#if NAMED_MUTEX_USE_PTHREAD_MUTEX
+ MutexHelpers::DestroyMutex(&m_lock);
+#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
+}
+
+#if NAMED_MUTEX_USE_PTHREAD_MUTEX
+pthread_mutex_t *NamedMutexSharedData::GetLock()
+{
+ return &m_lock;
+}
+#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+bool NamedMutexSharedData::HasAnyTimedWaiters() const
+{
+ return
+ InterlockedCompareExchange(
+ const_cast<LONG *>(reinterpret_cast<const LONG *>(&m_timedWaiterCount)),
+ -1 /* Exchange */,
+ -1 /* Comparand */) != 0;
+}
+
+void NamedMutexSharedData::IncTimedWaiterCount()
+{
+ ULONG newValue = InterlockedIncrement(reinterpret_cast<LONG *>(&m_timedWaiterCount));
+ if (newValue == 0)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::OutOfMemory));
+ }
+}
+
+void NamedMutexSharedData::DecTimedWaiterCount()
+{
+ ULONG newValue = InterlockedDecrement(reinterpret_cast<LONG *>(&m_timedWaiterCount));
+ _ASSERTE(newValue + 1 != 0);
+}
+#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
+
+bool NamedMutexSharedData::IsAbandoned() const
+{
+ _ASSERTE(IsLockOwnedByCurrentThread());
+ return m_isAbandoned;
+}
+
+void NamedMutexSharedData::SetIsAbandoned(bool isAbandoned)
+{
+ _ASSERTE(IsLockOwnedByCurrentThread());
+ _ASSERTE(m_isAbandoned != isAbandoned);
+
+ m_isAbandoned = isAbandoned;
+}
+
+bool NamedMutexSharedData::IsLockOwnedByAnyThread() const
+{
+ return
+ m_lockOwnerProcessId != SharedMemoryHelpers::InvalidProcessId ||
+ m_lockOwnerThreadId != SharedMemoryHelpers::InvalidSharedThreadId;
+}
+
+bool NamedMutexSharedData::IsLockOwnedByCurrentThread() const
+{
+ return m_lockOwnerProcessId == GetCurrentProcessId() && m_lockOwnerThreadId == THREADSilentGetCurrentThreadId();
+}
+
+void NamedMutexSharedData::SetLockOwnerToCurrentThread()
+{
+ m_lockOwnerProcessId = GetCurrentProcessId();
+ _ASSERTE(m_lockOwnerProcessId != SharedMemoryHelpers::InvalidProcessId);
+ m_lockOwnerThreadId = THREADSilentGetCurrentThreadId();
+ _ASSERTE(m_lockOwnerThreadId != SharedMemoryHelpers::InvalidSharedThreadId);
+}
+
+void NamedMutexSharedData::ClearLockOwner()
+{
+ _ASSERTE(IsLockOwnedByCurrentThread());
+
+ m_lockOwnerProcessId = SharedMemoryHelpers::InvalidProcessId;
+ m_lockOwnerThreadId = SharedMemoryHelpers::InvalidSharedThreadId;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// NamedMutexProcessData
+
+// This value should only be incremented if a non-backward-compatible change to the sync system is made. A process would fail to
+// open a mutex created with a different sync system version.
+const UINT8 NamedMutexProcessData::SyncSystemVersion = 1;
+
+const DWORD NamedMutexProcessData::PollLoopMaximumSleepMilliseconds = 100;
+
+SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(LPCSTR name, bool acquireLockIfCreated, bool *createdRef)
+{
+ return CreateOrOpen(name, true /* createIfNotExist */, acquireLockIfCreated, createdRef);
+}
+
+SharedMemoryProcessDataHeader *NamedMutexProcessData::Open(LPCSTR name)
+{
+ return CreateOrOpen(name, false /* createIfNotExist */, false /* acquireLockIfCreated */, nullptr /* createdRef */);
+}
+
+SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
+ LPCSTR name,
+ bool createIfNotExist,
+ bool acquireLockIfCreated,
+ bool *createdRef)
+{
+ _ASSERTE(name != nullptr);
+ _ASSERTE(createIfNotExist || !acquireLockIfCreated);
+
+ struct AutoCleanup
+ {
+ bool m_acquiredCreationDeletionProcessLock;
+ bool m_acquiredCreationDeletionFileLock;
+ SharedMemoryProcessDataHeader *m_processDataHeader;
+ #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ char *m_lockFilePath;
+ SIZE_T m_sessionDirectoryPathCharCount;
+ bool m_createdLockFile;
+ int m_lockFileDescriptor;
+ #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ bool m_cancel;
+
+ AutoCleanup()
+ : m_acquiredCreationDeletionProcessLock(false),
+ m_acquiredCreationDeletionFileLock(false),
+ m_processDataHeader(nullptr),
+ #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ m_lockFilePath(nullptr),
+ m_sessionDirectoryPathCharCount(0),
+ m_createdLockFile(false),
+ m_lockFileDescriptor(-1),
+ #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ m_cancel(false)
+ {
+ }
+
+ ~AutoCleanup()
+ {
+ #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ if (!m_cancel)
+ {
+ if (m_lockFileDescriptor != -1)
+ {
+ SharedMemoryHelpers::CloseFile(m_lockFileDescriptor);
+ }
+
+ if (m_createdLockFile)
+ {
+ _ASSERTE(m_lockFilePath != nullptr);
+ unlink(m_lockFilePath);
+ }
+
+ if (m_sessionDirectoryPathCharCount != 0)
+ {
+ _ASSERTE(m_lockFilePath != nullptr);
+ m_lockFilePath[m_sessionDirectoryPathCharCount] = '\0';
+ rmdir(m_lockFilePath);
+ }
+ }
+ #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+
+ if (m_acquiredCreationDeletionFileLock)
+ {
+ SharedMemoryManager::ReleaseCreationDeletionFileLock();
+ }
+
+ if (!m_cancel && m_processDataHeader != nullptr)
+ {
+ _ASSERTE(m_acquiredCreationDeletionProcessLock);
+ m_processDataHeader->DecRefCount();
+ }
+
+ if (m_acquiredCreationDeletionProcessLock)
+ {
+ SharedMemoryManager::ReleaseCreationDeletionProcessLock();
+ }
+ }
+ } autoCleanup;
+
+ SharedMemoryManager::AcquireCreationDeletionProcessLock();
+ autoCleanup.m_acquiredCreationDeletionProcessLock = true;
+
+ // Create or open the shared memory
+ bool created;
+ SharedMemoryProcessDataHeader *processDataHeader =
+ SharedMemoryProcessDataHeader::CreateOrOpen(
+ name,
+ SharedMemorySharedDataHeader(SharedMemoryType::Mutex, SyncSystemVersion),
+ sizeof(NamedMutexSharedData),
+ createIfNotExist,
+ &created);
+ if (createdRef != nullptr)
+ {
+ *createdRef = created;
+ }
+ if (created)
+ {
+ // If the shared memory file was created, the creation/deletion file lock would have been acquired so that we can
+ // initialize the shared data
+ autoCleanup.m_acquiredCreationDeletionFileLock = true;
+ }
+ if (processDataHeader == nullptr)
+ {
+ _ASSERTE(!createIfNotExist);
+ return nullptr;
+ }
+ autoCleanup.m_processDataHeader = processDataHeader;
+
+ if (created)
+ {
+ // Initialize the shared data
+ new(processDataHeader->GetSharedDataHeader()->GetData()) NamedMutexSharedData;
+ }
+
+ if (processDataHeader->GetData() == nullptr)
+ {
+ #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ // Create the lock files directory
+ char lockFilePath[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1];
+ SIZE_T lockFilePathCharCount =
+ SharedMemoryHelpers::CopyString(lockFilePath, 0, SHARED_MEMORY_LOCK_FILES_DIRECTORY_PATH);
+ if (created)
+ {
+ SharedMemoryHelpers::EnsureDirectoryExists(lockFilePath, true /* isGlobalLockAcquired */);
+ }
+
+ // Create the session directory
+ lockFilePath[lockFilePathCharCount++] = '/';
+ SharedMemoryId *id = processDataHeader->GetId();
+ lockFilePathCharCount = id->AppendSessionDirectoryName(lockFilePath, lockFilePathCharCount);
+ if (created)
+ {
+ SharedMemoryHelpers::EnsureDirectoryExists(lockFilePath, true /* isGlobalLockAcquired */);
+ autoCleanup.m_lockFilePath = lockFilePath;
+ autoCleanup.m_sessionDirectoryPathCharCount = lockFilePathCharCount;
+ }
+
+ // Create or open the lock file
+ lockFilePath[lockFilePathCharCount++] = '/';
+ lockFilePathCharCount =
+ SharedMemoryHelpers::CopyString(lockFilePath, lockFilePathCharCount, id->GetName(), id->GetNameCharCount());
+ int lockFileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(lockFilePath, created);
+ if (lockFileDescriptor == -1)
+ {
+ _ASSERTE(!created);
+ if (createIfNotExist)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
+ }
+ return nullptr;
+ }
+ autoCleanup.m_createdLockFile = created;
+ autoCleanup.m_lockFileDescriptor = lockFileDescriptor;
+ #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+
+ // Create the process data
+ void *processDataBuffer = SharedMemoryHelpers::Alloc(sizeof(NamedMutexProcessData));
+ AutoFreeBuffer autoFreeProcessDataBuffer(processDataBuffer);
+ NamedMutexProcessData *processData =
+ new(processDataBuffer)
+ NamedMutexProcessData(
+ processDataHeader
+ #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ ,
+ lockFileDescriptor
+ #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ );
+ autoFreeProcessDataBuffer.Cancel();
+ processDataHeader->SetData(processData);
+
+ // If the mutex was created and if requested, acquire the lock initially while holding the creation/deletion locks
+ if (created && acquireLockIfCreated)
+ {
+ MutexTryAcquireLockResult tryAcquireLockResult = processData->TryAcquireLock(0);
+ _ASSERTE(tryAcquireLockResult == MutexTryAcquireLockResult::AcquiredLock);
+ }
+ }
+
+ autoCleanup.m_cancel = true;
+ return processDataHeader;
+}
+
+NamedMutexProcessData::NamedMutexProcessData(
+ SharedMemoryProcessDataHeader *processDataHeader
+#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ ,
+ int sharedLockFileDescriptor
+#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+)
+ :
+ m_processDataHeader(processDataHeader),
+ m_lockCount(0),
+#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ m_sharedLockFileDescriptor(sharedLockFileDescriptor),
+#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ m_lockOwnerThread(nullptr),
+ m_nextInThreadOwnedNamedMutexList(nullptr)
+{
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(processDataHeader != nullptr);
+
+#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ _ASSERTE(sharedLockFileDescriptor != -1);
+
+ m_processLockHandle = CreateMutex(nullptr /* lpMutexAttributes */, false /* bInitialOwner */, nullptr /* lpName */);
+ if (m_processLockHandle == nullptr)
+ {
+ throw SharedMemoryException(GetLastError());
+ }
+#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+}
+
+void NamedMutexProcessData::Close(bool isAbruptShutdown, bool releaseSharedData)
+{
+ _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
+ _ASSERTE(!releaseSharedData || SharedMemoryManager::IsCreationDeletionFileLockAcquired());
+
+ // If the process is shutting down abruptly without having closed some mutexes, there could still be threads running with
+ // active references to the mutex. So when shutting down abruptly, don't clean up any object or global process-local state.
+ if (!isAbruptShutdown)
+ {
+ CPalThread *lockOwnerThread = m_lockOwnerThread;
+ if (lockOwnerThread != nullptr)
+ {
+ // The mutex was not released before it was closed. If the lock is owned by the current thread, abandon the mutex.
+ // In both cases, clean up the owner thread's list of owned mutexes.
+ lockOwnerThread->synchronizationInfo.RemoveOwnedNamedMutex(this);
+ if (lockOwnerThread == GetCurrentPalThread())
+ {
+ Abandon();
+ }
+ else
+ {
+ m_lockOwnerThread = nullptr;
+ }
+ }
+
+ if (releaseSharedData)
+ {
+ GetSharedData()->~NamedMutexSharedData();
+ }
+ }
+
+#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ if (!isAbruptShutdown)
+ {
+ CloseHandle(m_processLockHandle);
+ SharedMemoryHelpers::CloseFile(m_sharedLockFileDescriptor);
+ }
+
+ if (!releaseSharedData)
+ {
+ return;
+ }
+
+ // Delete the lock file, and the session directory if it's not empty
+ char path[SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1];
+ SIZE_T sessionDirectoryPathCharCount = SharedMemoryHelpers::CopyString(path, 0, SHARED_MEMORY_LOCK_FILES_DIRECTORY_PATH);
+ path[sessionDirectoryPathCharCount++] = '/';
+ SharedMemoryId *id = m_processDataHeader->GetId();
+ sessionDirectoryPathCharCount = id->AppendSessionDirectoryName(path, sessionDirectoryPathCharCount);
+ path[sessionDirectoryPathCharCount++] = '/';
+ SharedMemoryHelpers::CopyString(path, sessionDirectoryPathCharCount, id->GetName(), id->GetNameCharCount());
+ unlink(path);
+ path[sessionDirectoryPathCharCount] = '\0';
+ rmdir(path);
+#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+}
+
+NamedMutexSharedData *NamedMutexProcessData::GetSharedData() const
+{
+ return reinterpret_cast<NamedMutexSharedData *>(m_processDataHeader->GetSharedDataHeader()->GetData());
+}
+
+void NamedMutexProcessData::SetLockOwnerThread(CorUnix::CPalThread *lockOwnerThread)
+{
+ _ASSERTE(lockOwnerThread == nullptr || lockOwnerThread == GetCurrentPalThread());
+ _ASSERTE(GetSharedData()->IsLockOwnedByCurrentThread());
+
+ m_lockOwnerThread = lockOwnerThread;
+}
+
+NamedMutexProcessData *NamedMutexProcessData::GetNextInThreadOwnedNamedMutexList() const
+{
+ return m_nextInThreadOwnedNamedMutexList;
+}
+
+void NamedMutexProcessData::SetNextInThreadOwnedNamedMutexList(NamedMutexProcessData *next)
+{
+ m_nextInThreadOwnedNamedMutexList = next;
+}
+
+MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMilliseconds)
+{
+ NamedMutexSharedData *sharedData = GetSharedData();
+
+#if NAMED_MUTEX_USE_PTHREAD_MUTEX
+ MutexTryAcquireLockResult result = MutexHelpers::TryAcquireLock(sharedData->GetLock(), timeoutMilliseconds);
+ if (result == MutexTryAcquireLockResult::TimedOut)
+ {
+ return result;
+ }
+
+ // Check if a recursive lock was just taken. The recursion level is tracked manually so that the lock owner can be cleared
+ // at the appropriate time, see ReleaseLock().
+ if (m_lockCount != 0)
+ {
+ _ASSERTE(sharedData->IsLockOwnedByCurrentThread()); // otherwise, this thread would not have acquired the lock
+ _ASSERTE(GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this));
+
+ if (m_lockCount + 1 < m_lockCount)
+ {
+ MutexHelpers::ReleaseLock(sharedData->GetLock());
+ throw SharedMemoryException(static_cast<DWORD>(NamedMutexError::MaximumRecursiveLocksReached));
+ }
+ ++m_lockCount;
+
+ // The lock is released upon acquiring a recursive lock from the thread that already owns the lock
+ MutexHelpers::ReleaseLock(sharedData->GetLock());
+
+ _ASSERTE(result != MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned);
+ _ASSERTE(!sharedData->IsAbandoned());
+ return result;
+ }
+
+ // The non-recursive case is handled below (skip the #else and see below that)
+#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ // If a timeout is specified, determine the start time
+ DWORD startTime = 0;
+ if (timeoutMilliseconds != static_cast<DWORD>(-1) && timeoutMilliseconds != 0)
+ {
+ startTime = GetTickCount();
+ }
+
+ // Acquire the process lock. A file lock can only be acquired once per file descriptor, so to synchronize the threads of
+ // this process, the process lock is used.
+ while (true)
+ {
+ DWORD waitResult = WaitForSingleObject(m_processLockHandle, timeoutMilliseconds);
+ switch (waitResult)
+ {
+ case WAIT_OBJECT_0:
+ case WAIT_ABANDONED: // abandoned state for the process lock is irrelevant, the shared lock will also have been abandoned
+ break;
+
+ case WAIT_TIMEOUT:
+ return MutexTryAcquireLockResult::TimedOut;
+
+ case WAIT_IO_COMPLETION:
+ continue;
+
+ case WAIT_FAILED:
+ throw SharedMemoryException(GetLastError());
+
+ default:
+ _ASSERTE(false);
+ break;
+ }
+ break;
+ }
+
+ struct AutoReleaseProcessLock
+ {
+ HANDLE m_processLockHandle;
+ bool m_cancel;
+
+ AutoReleaseProcessLock(HANDLE processLockHandle) : m_processLockHandle(processLockHandle), m_cancel(false)
+ {
+ }
+
+ ~AutoReleaseProcessLock()
+ {
+ if (!m_cancel)
+ {
+ ReleaseMutex(m_processLockHandle);
+ }
+ }
+ } autoReleaseProcessLock(m_processLockHandle);
+
+ // Check if it's a recursive lock attempt
+ if (m_lockCount != 0)
+ {
+ _ASSERTE(sharedData->IsLockOwnedByCurrentThread()); // otherwise, this thread would not have acquired the process lock
+ _ASSERTE(GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this));
+
+ if (m_lockCount + 1 < m_lockCount)
+ {
+ throw SharedMemoryException(static_cast<DWORD>(NamedMutexError::MaximumRecursiveLocksReached));
+ }
+ ++m_lockCount;
+
+ // The process lock is released upon acquiring a recursive lock from the thread that already owns the lock
+ return MutexTryAcquireLockResult::AcquiredLock;
+ }
+
+ switch (timeoutMilliseconds)
+ {
+ case static_cast<DWORD>(-1):
+ {
+ // The file lock API does not have a timeout on the wait, so timed waiters will poll the file lock in a loop,
+ // sleeping for a short duration in-between. Due to the polling nature of a timed wait, timed waiters will almost
+ // never acquire the file lock as long as there are also untimed waiters. So, in order to make the file lock
+ // acquisition reasonable, when there are timed waiters, have untimed waiters also use polling.
+ bool acquiredFileLock = false;
+ while (sharedData->HasAnyTimedWaiters())
+ {
+ if (SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
+ {
+ acquiredFileLock = true;
+ break;
+ }
+ Sleep(PollLoopMaximumSleepMilliseconds);
+ }
+ if (acquiredFileLock)
+ {
+ break;
+ }
+
+ acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX);
+ _ASSERTE(acquiredFileLock);
+ break;
+ }
+
+ case 0:
+ if (!SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
+ {
+ return MutexTryAcquireLockResult::TimedOut;
+ }
+ break;
+
+ default:
+ {
+ // Try to acquire the file lock without waiting
+ if (SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
+ {
+ break;
+ }
+
+ // The file lock API does not have a timeout on the wait, so timed waiters need to poll the file lock in a loop,
+ // sleeping for a short duration in-between. Due to the polling nature of a timed wait, timed waiters will almost
+ // never acquire the file lock as long as there are also untimed waiters. So, in order to make the file lock
+ // acquisition reasonable, record that there is a timed waiter, to have untimed waiters also use polling.
+ sharedData->IncTimedWaiterCount();
+ struct AutoDecTimedWaiterCount
+ {
+ NamedMutexSharedData *m_sharedData;
+
+ AutoDecTimedWaiterCount(NamedMutexSharedData *sharedData) : m_sharedData(sharedData)
+ {
+ }
+
+ ~AutoDecTimedWaiterCount()
+ {
+ m_sharedData->DecTimedWaiterCount();
+ }
+ } autoDecTimedWaiterCount(sharedData);
+
+ // Poll for the file lock
+ do
+ {
+ DWORD elapsedMilliseconds = GetTickCount() - startTime;
+ if (elapsedMilliseconds >= timeoutMilliseconds)
+ {
+ return MutexTryAcquireLockResult::TimedOut;
+ }
+
+ DWORD remainingMilliseconds = timeoutMilliseconds - elapsedMilliseconds;
+ DWORD sleepMilliseconds =
+ remainingMilliseconds < PollLoopMaximumSleepMilliseconds
+ ? remainingMilliseconds
+ : PollLoopMaximumSleepMilliseconds;
+ Sleep(sleepMilliseconds);
+ } while (!SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB));
+ break;
+ }
+ }
+
+ // There cannot be any exceptions after this
+ autoReleaseProcessLock.m_cancel = true;
+
+ // After acquiring the file lock, if we find that a lock owner is already designated, the process that previously owned the
+ // lock must have terminated while holding the lock.
+ MutexTryAcquireLockResult result =
+ sharedData->IsLockOwnedByAnyThread()
+ ? MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned
+ : MutexTryAcquireLockResult::AcquiredLock;
+#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
+
+ sharedData->SetLockOwnerToCurrentThread();
+ m_lockCount = 1;
+ CPalThread *currentThread = GetCurrentPalThread();
+ SetLockOwnerThread(currentThread);
+ currentThread->synchronizationInfo.AddOwnedNamedMutex(this);
+
+ if (sharedData->IsAbandoned())
+ {
+ // The thread that previously owned the lock did not release it before exiting
+ sharedData->SetIsAbandoned(false);
+ result = MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned;
+ }
+ return result;
+}
+
+void NamedMutexProcessData::ReleaseLock()
+{
+ if (!GetSharedData()->IsLockOwnedByCurrentThread())
+ {
+ throw SharedMemoryException(static_cast<DWORD>(NamedMutexError::ThreadHasNotAcquiredMutex));
+ }
+
+ _ASSERTE(GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this));
+
+ _ASSERTE(m_lockCount != 0);
+ --m_lockCount;
+ if (m_lockCount != 0)
+ {
+ return;
+ }
+
+ GetCurrentPalThread()->synchronizationInfo.RemoveOwnedNamedMutex(this);
+ SetLockOwnerThread(nullptr);
+ ActuallyReleaseLock();
+}
+
+void NamedMutexProcessData::Abandon()
+{
+ NamedMutexSharedData *sharedData = GetSharedData();
+ _ASSERTE(sharedData->IsLockOwnedByCurrentThread());
+ _ASSERTE(m_lockCount != 0);
+
+ sharedData->SetIsAbandoned(true);
+ m_lockCount = 0;
+ SetLockOwnerThread(nullptr);
+ ActuallyReleaseLock();
+}
+
+void NamedMutexProcessData::ActuallyReleaseLock()
+{
+ NamedMutexSharedData *sharedData = GetSharedData();
+ _ASSERTE(sharedData->IsLockOwnedByCurrentThread());
+ _ASSERTE(!GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this));
+ _ASSERTE(m_lockCount == 0);
+
+ sharedData->ClearLockOwner();
+
+#if NAMED_MUTEX_USE_PTHREAD_MUTEX
+ MutexHelpers::ReleaseLock(sharedData->GetLock());
+#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX
+ SharedMemoryHelpers::ReleaseFileLock(m_sharedLockFileDescriptor);
+ ReleaseMutex(m_processLockHandle);
+#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
+}
diff --git a/src/pal/src/synchobj/semaphore.cpp b/src/pal/src/synchobj/semaphore.cpp
new file mode 100644
index 0000000000..b2240184c5
--- /dev/null
+++ b/src/pal/src/synchobj/semaphore.cpp
@@ -0,0 +1,680 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ semaphore.cpp
+
+Abstract:
+
+ Implementation of the sempahore synchroniztion object as described in
+ the WIN32 API
+
+Revision History:
+
+
+
+--*/
+
+#include "pal/semaphore.hpp"
+#include "pal/thread.hpp"
+#include "pal/dbgmsg.h"
+
+using namespace CorUnix;
+
+/* ------------------- Definitions ------------------------------*/
+SET_DEFAULT_DEBUG_CHANNEL(SYNC);
+
+CObjectType CorUnix::otSemaphore(
+ otiSemaphore,
+ NULL, // No cleanup routine
+ NULL, // No initialization routine
+ sizeof(SemaphoreImmutableData),
+ 0, // No process local data
+ 0, // No shared data
+ 0, // Should be SEMAPHORE_ALL_ACCESS; currently ignored (no Win32 security)
+ CObjectType::SecuritySupported,
+ CObjectType::SecurityInfoNotPersisted,
+ CObjectType::UnnamedObject,
+ CObjectType::LocalDuplicationOnly,
+ CObjectType::WaitableObject,
+ CObjectType::ObjectCanBeUnsignaled,
+ CObjectType::ThreadReleaseAltersSignalCount,
+ CObjectType::NoOwner
+ );
+
+CAllowedObjectTypes aotSempahore(otiSemaphore);
+
+/*++
+Function:
+CreateSemaphoreExA
+
+Note:
+lpSemaphoreAttributes currently ignored:
+-- Win32 object security not supported
+-- handles to semaphore objects are not inheritable
+
+Parameters:
+See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+CreateSemaphoreExA(
+ IN LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
+ IN LONG lInitialCount,
+ IN LONG lMaximumCount,
+ IN LPCSTR lpName,
+ IN /*_Reserved_*/ DWORD dwFlags,
+ IN DWORD dwDesiredAccess)
+{
+ // dwFlags is reserved and unused, and dwDesiredAccess is currently
+ // only ever used as SEMAPHORE_ALL_ACCESS. The other parameters
+ // all map to CreateSemaphoreA.
+ _ASSERTE(SEMAPHORE_ALL_ACCESS == dwDesiredAccess);
+
+ return CreateSemaphoreA(
+ lpSemaphoreAttributes,
+ lInitialCount,
+ lMaximumCount,
+ lpName);
+}
+
+/*++
+Function:
+ CreateSemaphoreA
+
+Note:
+ lpSemaphoreAttributes currently ignored:
+ -- Win32 object security not supported
+ -- handles to semaphore objects are not inheritable
+
+Parameters:
+ See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+CreateSemaphoreA(
+ IN LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
+ IN LONG lInitialCount,
+ IN LONG lMaximumCount,
+ IN LPCSTR lpName)
+{
+ HANDLE hSemaphore = NULL;
+ CPalThread *pthr = NULL;
+ PAL_ERROR palError;
+
+ PERF_ENTRY(CreateSemaphoreA);
+ ENTRY("CreateSemaphoreA(lpSemaphoreAttributes=%p, lInitialCount=%d, "
+ "lMaximumCount=%d, lpName=%p (%s))\n",
+ lpSemaphoreAttributes, lInitialCount, lMaximumCount, lpName, lpName?lpName:"NULL");
+
+ pthr = InternalGetCurrentThread();
+
+ if (lpName != nullptr)
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ }
+ else
+ {
+ palError = InternalCreateSemaphore(
+ pthr,
+ lpSemaphoreAttributes,
+ lInitialCount,
+ lMaximumCount,
+ NULL,
+ &hSemaphore
+ );
+ }
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pthr->SetLastError(palError);
+
+ LOGEXIT("CreateSemaphoreA returns HANDLE %p\n", hSemaphore);
+ PERF_EXIT(CreateSemaphoreA);
+ return hSemaphore;
+}
+
+/*++
+Function:
+CreateSemaphoreExW
+
+Note:
+lpSemaphoreAttributes currentely ignored:
+-- Win32 object security not supported
+-- handles to semaphore objects are not inheritable
+
+Parameters:
+See MSDN doc.
+--*/
+
+PALIMPORT
+HANDLE
+PALAPI
+CreateSemaphoreExW(
+ IN LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
+ IN LONG lInitialCount,
+ IN LONG lMaximumCount,
+ IN LPCWSTR lpName,
+ IN /*_Reserved_*/ DWORD dwFlags,
+ IN DWORD dwDesiredAccess)
+{
+ // dwFlags is reserved and unused, and dwDesiredAccess is currently
+ // only ever used as SEMAPHORE_ALL_ACCESS. The other parameters
+ // all map to CreateSemaphoreW.
+ _ASSERTE(SEMAPHORE_ALL_ACCESS == dwDesiredAccess);
+
+ return CreateSemaphoreW(
+ lpSemaphoreAttributes,
+ lInitialCount,
+ lMaximumCount,
+ lpName);
+}
+
+/*++
+Function:
+ CreateSemaphoreW
+
+Note:
+ lpSemaphoreAttributes currentely ignored:
+ -- Win32 object security not supported
+ -- handles to semaphore objects are not inheritable
+
+Parameters:
+ See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+CreateSemaphoreW(
+ IN LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
+ IN LONG lInitialCount,
+ IN LONG lMaximumCount,
+ IN LPCWSTR lpName)
+{
+ HANDLE hSemaphore = NULL;
+ PAL_ERROR palError;
+ CPalThread *pthr = NULL;
+
+ PERF_ENTRY(CreateSemaphoreW);
+ ENTRY("CreateSemaphoreW(lpSemaphoreAttributes=%p, lInitialCount=%d, "
+ "lMaximumCount=%d, lpName=%p (%S))\n",
+ lpSemaphoreAttributes, lInitialCount, lMaximumCount,
+ lpName, lpName?lpName:W16_NULLSTRING);
+
+ pthr = InternalGetCurrentThread();
+
+ palError = InternalCreateSemaphore(
+ pthr,
+ lpSemaphoreAttributes,
+ lInitialCount,
+ lMaximumCount,
+ lpName,
+ &hSemaphore
+ );
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pthr->SetLastError(palError);
+
+ LOGEXIT("CreateSemaphoreW returns HANDLE %p\n", hSemaphore);
+ PERF_EXIT(CreateSemaphoreW);
+ return hSemaphore;
+}
+
+/*++
+Function:
+ InternalCreateSemaphore
+
+Note:
+ lpSemaphoreAttributes currentely ignored:
+ -- Win32 object security not supported
+ -- handles to semaphore objects are not inheritable
+
+Parameters
+ pthr -- thread data for calling thread
+ phEvent -- on success, receives the allocated semaphore handle
+
+ See MSDN docs on CreateSemaphore for all other parameters.
+--*/
+
+PAL_ERROR
+CorUnix::InternalCreateSemaphore(
+ CPalThread *pthr,
+ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
+ LONG lInitialCount,
+ LONG lMaximumCount,
+ LPCWSTR lpName,
+ HANDLE *phSemaphore
+ )
+{
+ CObjectAttributes oa(lpName, lpSemaphoreAttributes);
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjSemaphore = NULL;
+ IPalObject *pobjRegisteredSemaphore = NULL;
+ SemaphoreImmutableData *pSemaphoreData;
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != phSemaphore);
+
+ ENTRY("InternalCreateSemaphore(pthr=%p, lpSemaphoreAttributes=%p, "
+ "lInitialCount=%d, lMaximumCount=%d, lpName=%p, phSemaphore=%p)\n",
+ pthr,
+ lpSemaphoreAttributes,
+ lInitialCount,
+ lMaximumCount,
+ lpName,
+ phSemaphore
+ );
+
+ if (lpName != nullptr)
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ goto InternalCreateSemaphoreExit;
+ }
+
+ if (lMaximumCount <= 0)
+ {
+ ERROR("lMaximumCount is invalid (%d)\n", lMaximumCount);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreateSemaphoreExit;
+ }
+
+ if ((lInitialCount < 0) || (lInitialCount > lMaximumCount))
+ {
+ ERROR("lInitialCount is invalid (%d)\n", lInitialCount);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalCreateSemaphoreExit;
+ }
+
+ palError = g_pObjectManager->AllocateObject(
+ pthr,
+ &otSemaphore,
+ &oa,
+ &pobjSemaphore
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateSemaphoreExit;
+ }
+
+ palError = pobjSemaphore->GetImmutableData(reinterpret_cast<void**>(&pSemaphoreData));
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Error %d obtaining object data\n", palError);
+ goto InternalCreateSemaphoreExit;
+ }
+
+ pSemaphoreData->lMaximumCount = lMaximumCount;
+
+ if (0 != lInitialCount)
+ {
+ ISynchStateController *pssc;
+
+ palError = pobjSemaphore->GetSynchStateController(
+ pthr,
+ &pssc
+ );
+
+ if (NO_ERROR == palError)
+ {
+ palError = pssc->SetSignalCount(lInitialCount);
+ pssc->ReleaseController();
+ }
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to set new semaphore state (%d)\n", palError);
+ goto InternalCreateSemaphoreExit;
+ }
+ }
+
+ palError = g_pObjectManager->RegisterObject(
+ pthr,
+ pobjSemaphore,
+ &aotSempahore,
+ 0, // Should be SEMAPHORE_ALL_ACCESS; currently ignored (no Win32 security)
+ phSemaphore,
+ &pobjRegisteredSemaphore
+ );
+
+ //
+ // pobjSemaphore is invalidated by the call to RegisterObject, so NULL it
+ // out here to ensure that we don't try to release a reference on
+ // it down the line.
+ //
+
+ pobjSemaphore = NULL;
+
+InternalCreateSemaphoreExit:
+
+ if (NULL != pobjSemaphore)
+ {
+ pobjSemaphore->ReleaseReference(pthr);
+ }
+
+ if (NULL != pobjRegisteredSemaphore)
+ {
+ pobjRegisteredSemaphore->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("InternalCreateSemaphore returns %d\n", palError);
+
+ return palError;
+}
+
+
+/*++
+Function:
+ ReleaseSemaphore
+
+Parameters:
+ See MSDN doc.
+--*/
+
+BOOL
+PALAPI
+ReleaseSemaphore(
+ IN HANDLE hSemaphore,
+ IN LONG lReleaseCount,
+ OUT LPLONG lpPreviousCount)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthr = NULL;
+
+ PERF_ENTRY(ReleaseSemaphore);
+ ENTRY("ReleaseSemaphore(hSemaphore=%p, lReleaseCount=%d, "
+ "lpPreviousCount=%p)\n",
+ hSemaphore, lReleaseCount, lpPreviousCount);
+
+ pthr = InternalGetCurrentThread();
+
+ palError = InternalReleaseSemaphore(
+ pthr,
+ hSemaphore,
+ lReleaseCount,
+ lpPreviousCount
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pthr->SetLastError(palError);
+ }
+
+ LOGEXIT ("ReleaseSemaphore returns BOOL %d\n", (NO_ERROR == palError));
+ PERF_EXIT(ReleaseSemaphore);
+ return (NO_ERROR == palError);
+}
+
+/*++
+Function:
+ InternalReleaseSemaphore
+
+Parameters:
+ pthr -- thread data for calling thread
+
+ See MSDN docs on ReleaseSemaphore for all other parameters
+--*/
+
+PAL_ERROR
+CorUnix::InternalReleaseSemaphore(
+ CPalThread *pthr,
+ HANDLE hSemaphore,
+ LONG lReleaseCount,
+ LPLONG lpPreviousCount
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjSemaphore = NULL;
+ ISynchStateController *pssc = NULL;
+ SemaphoreImmutableData *pSemaphoreData;
+ LONG lOldCount;
+
+ _ASSERTE(NULL != pthr);
+
+ ENTRY("InternalReleaseSempahore(pthr=%p, hSemaphore=%p, lReleaseCount=%d, "
+ "lpPreviousCount=%p)\n",
+ pthr,
+ hSemaphore,
+ lReleaseCount,
+ lpPreviousCount
+ );
+
+ if (0 >= lReleaseCount)
+ {
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalReleaseSemaphoreExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pthr,
+ hSemaphore,
+ &aotSempahore,
+ 0, // Should be SEMAPHORE_MODIFY_STATE; currently ignored (no Win32 security)
+ &pobjSemaphore
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to obtain object for handle %p (error %d)!\n", hSemaphore, palError);
+ goto InternalReleaseSemaphoreExit;
+ }
+
+ palError = pobjSemaphore->GetImmutableData(reinterpret_cast<void**>(&pSemaphoreData));
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Error %d obtaining object data\n", palError);
+ goto InternalReleaseSemaphoreExit;
+ }
+
+ palError = pobjSemaphore->GetSynchStateController(
+ pthr,
+ &pssc
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Error %d obtaining synch state controller\n", palError);
+ goto InternalReleaseSemaphoreExit;
+ }
+
+ palError = pssc->GetSignalCount(&lOldCount);
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Error %d obtaining current signal count\n", palError);
+ goto InternalReleaseSemaphoreExit;
+ }
+
+ _ASSERTE(lOldCount <= pSemaphoreData->lMaximumCount);
+ if (lReleaseCount > pSemaphoreData->lMaximumCount - lOldCount)
+ {
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalReleaseSemaphoreExit;
+ }
+
+ palError = pssc->IncrementSignalCount(lReleaseCount);
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Error %d incrementing signal count\n", palError);
+ goto InternalReleaseSemaphoreExit;
+ }
+
+ if (NULL != lpPreviousCount)
+ {
+ *lpPreviousCount = lOldCount;
+ }
+
+InternalReleaseSemaphoreExit:
+
+ if (NULL != pssc)
+ {
+ pssc->ReleaseController();
+ }
+
+ if (NULL != pobjSemaphore)
+ {
+ pobjSemaphore->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("InternalReleaseSemaphore returns %d\n", palError);
+
+ return palError;
+}
+
+// TODO: Implementation of OpenSemaphoreA() doesn't exist, do we need it? More generally, do we need the A versions at all?
+
+/*++
+Function:
+ OpenSemaphoreW
+
+Note:
+ dwDesiredAccess is currently ignored (no Win32 object security support)
+ bInheritHandle is currently ignored (handles to semaphore are not inheritable)
+
+Parameters:
+ See MSDN doc.
+--*/
+
+HANDLE
+PALAPI
+OpenSemaphoreW(
+ IN DWORD dwDesiredAccess,
+ IN BOOL bInheritHandle,
+ IN LPCWSTR lpName)
+{
+ HANDLE hSemaphore = NULL;
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthr = NULL;
+
+ PERF_ENTRY(OpenSemaphoreW);
+ ENTRY("OpenSemaphoreW(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%S))\n",
+ dwDesiredAccess, bInheritHandle, lpName, lpName?lpName:W16_NULLSTRING);
+
+ pthr = InternalGetCurrentThread();
+
+ /* validate parameters */
+ if (lpName == nullptr)
+ {
+ ERROR("lpName is NULL\n");
+ palError = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ }
+
+ if (NO_ERROR != palError)
+ {
+ pthr->SetLastError(palError);
+ }
+
+ LOGEXIT("OpenSemaphoreW returns HANDLE %p\n", hSemaphore);
+ PERF_EXIT(OpenSemaphoreW);
+
+ return hSemaphore;
+}
+
+/*++
+Function:
+ InternalOpenSemaphore
+
+Note:
+ dwDesiredAccess is currently ignored (no Win32 object security support)
+ bInheritHandle is currently ignored (handles to semaphores are not inheritable)
+
+Parameters:
+ pthr -- thread data for calling thread
+ phEvent -- on success, receives the allocated semaphore handle
+
+ See MSDN docs on OpenSemaphore for all other parameters.
+--*/
+
+PAL_ERROR
+CorUnix::InternalOpenSemaphore(
+ CPalThread *pthr,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ LPCWSTR lpName,
+ HANDLE *phSemaphore
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjSemaphore = NULL;
+ CPalString sObjectName(lpName);
+
+ _ASSERTE(NULL != pthr);
+ _ASSERTE(NULL != lpName);
+ _ASSERTE(NULL != phSemaphore);
+
+ ENTRY("InternalOpenSemaphore(pthr=%p, dwDesiredAccess=%d, bInheritHandle=%d, "
+ "lpName=%p, phSemaphore=%p)\n",
+ pthr,
+ dwDesiredAccess,
+ bInheritHandle,
+ phSemaphore
+ );
+
+ palError = g_pObjectManager->LocateObject(
+ pthr,
+ &sObjectName,
+ &aotSempahore,
+ &pobjSemaphore
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalOpenSemaphoreExit;
+ }
+
+ palError = g_pObjectManager->ObtainHandleForObject(
+ pthr,
+ pobjSemaphore,
+ dwDesiredAccess,
+ bInheritHandle,
+ NULL,
+ phSemaphore
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalOpenSemaphoreExit;
+ }
+
+InternalOpenSemaphoreExit:
+
+ if (NULL != pobjSemaphore)
+ {
+ pobjSemaphore->ReleaseReference(pthr);
+ }
+
+ LOGEXIT("InternalOpenSemaphore returns %d\n", palError);
+
+ return palError;
+}
+
+
diff --git a/src/pal/src/thread/context.cpp b/src/pal/src/thread/context.cpp
new file mode 100644
index 0000000000..f832015710
--- /dev/null
+++ b/src/pal/src/thread/context.cpp
@@ -0,0 +1,1360 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ context.c
+
+Abstract:
+
+ Implementation of GetThreadContext/SetThreadContext/DebugBreak.
+ There are a lot of architecture specifics here.
+
+
+
+--*/
+
+#include "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do this first
+
+#include "pal/palinternal.h"
+#include "pal/context.h"
+#include "pal/debug.h"
+#include "pal/thread.hpp"
+
+#include <sys/ptrace.h>
+#include <errno.h>
+#include <unistd.h>
+
+extern PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode;
+
+// in context2.S
+extern void CONTEXT_CaptureContext(LPCONTEXT lpContext);
+
+#ifdef _X86_
+#define CONTEXT_ALL_FLOATING (CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS)
+#elif defined(_AMD64_)
+#define CONTEXT_ALL_FLOATING CONTEXT_FLOATING_POINT
+#elif defined(_ARM_)
+#define CONTEXT_ALL_FLOATING CONTEXT_FLOATING_POINT
+#elif defined(_ARM64_)
+#define CONTEXT_ALL_FLOATING CONTEXT_FLOATING_POINT
+#else
+#error Unexpected architecture.
+#endif
+
+#if !HAVE_MACH_EXCEPTIONS
+
+#ifndef __GLIBC__
+typedef int __ptrace_request;
+#endif
+
+#if HAVE_MACHINE_REG_H
+#include <machine/reg.h>
+#endif // HAVE_MACHINE_REG_H
+#if HAVE_MACHINE_NPX_H
+#include <machine/npx.h>
+#endif // HAVE_MACHINE_NPX_H
+
+#if HAVE_PT_REGS
+#include <asm/ptrace.h>
+#endif // HAVE_PT_REGS
+
+#ifdef _AMD64_
+#define ASSIGN_CONTROL_REGS \
+ ASSIGN_REG(Rbp) \
+ ASSIGN_REG(Rip) \
+ ASSIGN_REG(SegCs) \
+ ASSIGN_REG(EFlags) \
+ ASSIGN_REG(Rsp) \
+
+#define ASSIGN_INTEGER_REGS \
+ ASSIGN_REG(Rdi) \
+ ASSIGN_REG(Rsi) \
+ ASSIGN_REG(Rbx) \
+ ASSIGN_REG(Rdx) \
+ ASSIGN_REG(Rcx) \
+ ASSIGN_REG(Rax) \
+ ASSIGN_REG(R8) \
+ ASSIGN_REG(R9) \
+ ASSIGN_REG(R10) \
+ ASSIGN_REG(R11) \
+ ASSIGN_REG(R12) \
+ ASSIGN_REG(R13) \
+ ASSIGN_REG(R14) \
+ ASSIGN_REG(R15) \
+
+#elif defined(_X86_)
+#define ASSIGN_CONTROL_REGS \
+ ASSIGN_REG(Ebp) \
+ ASSIGN_REG(Eip) \
+ ASSIGN_REG(SegCs) \
+ ASSIGN_REG(EFlags) \
+ ASSIGN_REG(Esp) \
+ ASSIGN_REG(SegSs) \
+
+#define ASSIGN_INTEGER_REGS \
+ ASSIGN_REG(Edi) \
+ ASSIGN_REG(Esi) \
+ ASSIGN_REG(Ebx) \
+ ASSIGN_REG(Edx) \
+ ASSIGN_REG(Ecx) \
+ ASSIGN_REG(Eax) \
+
+#elif defined(_ARM_)
+#define ASSIGN_CONTROL_REGS \
+ ASSIGN_REG(Sp) \
+ ASSIGN_REG(Lr) \
+ ASSIGN_REG(Pc) \
+ ASSIGN_REG(Cpsr) \
+
+#define ASSIGN_INTEGER_REGS \
+ ASSIGN_REG(R0) \
+ ASSIGN_REG(R1) \
+ ASSIGN_REG(R2) \
+ ASSIGN_REG(R3) \
+ ASSIGN_REG(R4) \
+ ASSIGN_REG(R5) \
+ ASSIGN_REG(R6) \
+ ASSIGN_REG(R7) \
+ ASSIGN_REG(R8) \
+ ASSIGN_REG(R9) \
+ ASSIGN_REG(R10) \
+ ASSIGN_REG(R11) \
+ ASSIGN_REG(R12)
+#elif defined(_ARM64_)
+#define ASSIGN_CONTROL_REGS \
+ ASSIGN_REG(Sp) \
+ ASSIGN_REG(Lr) \
+ ASSIGN_REG(Pc)
+
+#define ASSIGN_INTEGER_REGS \
+ ASSIGN_REG(X0) \
+ ASSIGN_REG(X1) \
+ ASSIGN_REG(X2) \
+ ASSIGN_REG(X3) \
+ ASSIGN_REG(X4) \
+ ASSIGN_REG(X5) \
+ ASSIGN_REG(X6) \
+ ASSIGN_REG(X7) \
+ ASSIGN_REG(X8) \
+ ASSIGN_REG(X9) \
+ ASSIGN_REG(X10) \
+ ASSIGN_REG(X11) \
+ ASSIGN_REG(X12) \
+ ASSIGN_REG(X13) \
+ ASSIGN_REG(X14) \
+ ASSIGN_REG(X15) \
+ ASSIGN_REG(X16) \
+ ASSIGN_REG(X17) \
+ ASSIGN_REG(X18) \
+ ASSIGN_REG(X19) \
+ ASSIGN_REG(X20) \
+ ASSIGN_REG(X21) \
+ ASSIGN_REG(X22) \
+ ASSIGN_REG(X23) \
+ ASSIGN_REG(X24) \
+ ASSIGN_REG(X25) \
+ ASSIGN_REG(X26) \
+ ASSIGN_REG(X27) \
+ ASSIGN_REG(X28)
+
+#else
+#error Don't know how to assign registers on this architecture
+#endif
+
+#define ASSIGN_ALL_REGS \
+ ASSIGN_CONTROL_REGS \
+ ASSIGN_INTEGER_REGS \
+
+/*++
+Function:
+ CONTEXT_GetRegisters
+
+Abstract
+ retrieve the machine registers value of the indicated process.
+
+Parameter
+ processId: process ID
+ lpContext: context structure in which the machine registers value will be returned.
+Return
+ returns TRUE if it succeeds, FALSE otherwise
+--*/
+BOOL CONTEXT_GetRegisters(DWORD processId, LPCONTEXT lpContext)
+{
+#if HAVE_BSD_REGS_T
+ int regFd = -1;
+#endif // HAVE_BSD_REGS_T
+ BOOL bRet = FALSE;
+
+ if (processId == GetCurrentProcessId())
+ {
+ CONTEXT_CaptureContext(lpContext);
+ }
+ else
+ {
+ ucontext_t registers;
+#if HAVE_PT_REGS
+ struct pt_regs ptrace_registers;
+ if (ptrace((__ptrace_request)PT_GETREGS, processId, (caddr_t) &ptrace_registers, 0) == -1)
+#elif HAVE_BSD_REGS_T
+ struct reg ptrace_registers;
+ if (PAL_PTRACE(PT_GETREGS, processId, &ptrace_registers, 0) == -1)
+#endif
+ {
+ ASSERT("Failed ptrace(PT_GETREGS, processId:%d) errno:%d (%s)\n",
+ processId, errno, strerror(errno));
+ }
+
+#if HAVE_PT_REGS
+#define ASSIGN_REG(reg) MCREG_##reg(registers.uc_mcontext) = PTREG_##reg(ptrace_registers);
+#elif HAVE_BSD_REGS_T
+#define ASSIGN_REG(reg) MCREG_##reg(registers.uc_mcontext) = BSDREG_##reg(ptrace_registers);
+#else
+#define ASSIGN_REG(reg)
+ ASSERT("Don't know how to get the context of another process on this platform!");
+ return bRet;
+#endif
+ ASSIGN_ALL_REGS
+#undef ASSIGN_REG
+
+ CONTEXTFromNativeContext(&registers, lpContext, lpContext->ContextFlags);
+ }
+
+ bRet = TRUE;
+#if HAVE_BSD_REGS_T
+ if (regFd != -1)
+ {
+ close(regFd);
+ }
+#endif // HAVE_BSD_REGS_T
+ return bRet;
+}
+
+/*++
+Function:
+ GetThreadContext
+
+See MSDN doc.
+--*/
+BOOL
+CONTEXT_GetThreadContext(
+ DWORD dwProcessId,
+ pthread_t self,
+ LPCONTEXT lpContext)
+{
+ BOOL ret = FALSE;
+
+ if (lpContext == NULL)
+ {
+ ERROR("Invalid lpContext parameter value\n");
+ SetLastError(ERROR_NOACCESS);
+ goto EXIT;
+ }
+
+ /* How to consider the case when self is different from the current
+ thread of its owner process. Machine registers values could be retreived
+ by a ptrace(pid, ...) call or from the "/proc/%pid/reg" file content.
+ Unfortunately, these two methods only depend on process ID, not on
+ thread ID. */
+
+ if (dwProcessId == GetCurrentProcessId())
+ {
+ if (self != pthread_self())
+ {
+ DWORD flags;
+ // There aren't any APIs for this. We can potentially get the
+ // context of another thread by using per-thread signals, but
+ // on FreeBSD signal handlers that are called as a result
+ // of signals raised via pthread_kill don't get a valid
+ // sigcontext or ucontext_t. But we need this to return TRUE
+ // to avoid an assertion in the CLR in code that manages to
+ // cope reasonably well without a valid thread context.
+ // Given that, we'll zero out our structure and return TRUE.
+ ERROR("GetThreadContext on a thread other than the current "
+ "thread is returning TRUE\n");
+ flags = lpContext->ContextFlags;
+ memset(lpContext, 0, sizeof(*lpContext));
+ lpContext->ContextFlags = flags;
+ ret = TRUE;
+ goto EXIT;
+ }
+
+ }
+
+ if (lpContext->ContextFlags &
+ (CONTEXT_CONTROL | CONTEXT_INTEGER))
+ {
+ if (CONTEXT_GetRegisters(dwProcessId, lpContext) == FALSE)
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto EXIT;
+ }
+ }
+
+ ret = TRUE;
+
+EXIT:
+ return ret;
+}
+
+/*++
+Function:
+ SetThreadContext
+
+See MSDN doc.
+--*/
+BOOL
+CONTEXT_SetThreadContext(
+ DWORD dwProcessId,
+ pthread_t self,
+ CONST CONTEXT *lpContext)
+{
+ BOOL ret = FALSE;
+
+#if HAVE_PT_REGS
+ struct pt_regs ptrace_registers;
+#elif HAVE_BSD_REGS_T
+ struct reg ptrace_registers;
+#endif
+
+ if (lpContext == NULL)
+ {
+ ERROR("Invalid lpContext parameter value\n");
+ SetLastError(ERROR_NOACCESS);
+ goto EXIT;
+ }
+
+ /* How to consider the case when self is different from the current
+ thread of its owner process. Machine registers values could be retreived
+ by a ptrace(pid, ...) call or from the "/proc/%pid/reg" file content.
+ Unfortunately, these two methods only depend on process ID, not on
+ thread ID. */
+
+ if (dwProcessId == GetCurrentProcessId())
+ {
+#ifdef FEATURE_PAL_SXS
+ // Need to implement SetThreadContext(current thread) for the IX architecture; look at common_signal_handler.
+ _ASSERT(FALSE);
+#endif // FEATURE_PAL_SXS
+ ASSERT("SetThreadContext should be called for cross-process only.\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto EXIT;
+ }
+
+ if (lpContext->ContextFlags &
+ (CONTEXT_CONTROL | CONTEXT_INTEGER))
+ {
+#if HAVE_PT_REGS
+ if (ptrace((__ptrace_request)PT_GETREGS, dwProcessId, (caddr_t)&ptrace_registers, 0) == -1)
+#elif HAVE_BSD_REGS_T
+ if (PAL_PTRACE(PT_GETREGS, dwProcessId, &ptrace_registers, 0) == -1)
+#endif
+ {
+ ASSERT("Failed ptrace(PT_GETREGS, processId:%d) errno:%d (%s)\n",
+ dwProcessId, errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto EXIT;
+ }
+
+#if HAVE_PT_REGS
+#define ASSIGN_REG(reg) PTREG_##reg(ptrace_registers) = lpContext->reg;
+#elif HAVE_BSD_REGS_T
+#define ASSIGN_REG(reg) BSDREG_##reg(ptrace_registers) = lpContext->reg;
+#else
+#define ASSIGN_REG(reg)
+ ASSERT("Don't know how to set the context of another process on this platform!");
+ return FALSE;
+#endif
+ if (lpContext->ContextFlags & CONTEXT_CONTROL)
+ {
+ ASSIGN_CONTROL_REGS
+ }
+ if (lpContext->ContextFlags & CONTEXT_INTEGER)
+ {
+ ASSIGN_INTEGER_REGS
+ }
+#undef ASSIGN_REG
+
+#if HAVE_PT_REGS
+ if (ptrace((__ptrace_request)PT_SETREGS, dwProcessId, (caddr_t)&ptrace_registers, 0) == -1)
+#elif HAVE_BSD_REGS_T
+ if (PAL_PTRACE(PT_SETREGS, dwProcessId, &ptrace_registers, 0) == -1)
+#endif
+ {
+ ASSERT("Failed ptrace(PT_SETREGS, processId:%d) errno:%d (%s)\n",
+ dwProcessId, errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto EXIT;
+ }
+ }
+
+ ret = TRUE;
+ EXIT:
+ return ret;
+}
+
+/*++
+Function :
+ CONTEXTToNativeContext
+
+ Converts a CONTEXT record to a native context.
+
+Parameters :
+ CONST CONTEXT *lpContext : CONTEXT to convert
+ native_context_t *native : native context to fill in
+
+Return value :
+ None
+
+--*/
+void CONTEXTToNativeContext(CONST CONTEXT *lpContext, native_context_t *native)
+{
+#define ASSIGN_REG(reg) MCREG_##reg(native->uc_mcontext) = lpContext->reg;
+ if ((lpContext->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
+ {
+ ASSIGN_CONTROL_REGS
+ }
+
+ if ((lpContext->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
+ {
+ ASSIGN_INTEGER_REGS
+ }
+#undef ASSIGN_REG
+
+#if HAVE_GREGSET_T || HAVE_GREGSET_T
+#if HAVE_GREGSET_T
+ if (native->uc_mcontext.fpregs == nullptr)
+#elif HAVE___GREGSET_T
+ if (native->uc_mcontext.__fpregs == nullptr)
+#endif
+ {
+ // If the pointer to the floating point state in the native context
+ // is not valid, we can't copy floating point registers regardless of
+ // whether CONTEXT_FLOATING_POINT is set in the CONTEXT's flags.
+ return;
+ }
+#endif
+
+ if ((lpContext->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT)
+ {
+#ifdef _AMD64_
+ FPREG_ControlWord(native) = lpContext->FltSave.ControlWord;
+ FPREG_StatusWord(native) = lpContext->FltSave.StatusWord;
+ FPREG_TagWord(native) = lpContext->FltSave.TagWord;
+ FPREG_ErrorOffset(native) = lpContext->FltSave.ErrorOffset;
+ FPREG_ErrorSelector(native) = lpContext->FltSave.ErrorSelector;
+ FPREG_DataOffset(native) = lpContext->FltSave.DataOffset;
+ FPREG_DataSelector(native) = lpContext->FltSave.DataSelector;
+ FPREG_MxCsr(native) = lpContext->FltSave.MxCsr;
+ FPREG_MxCsr_Mask(native) = lpContext->FltSave.MxCsr_Mask;
+
+ for (int i = 0; i < 8; i++)
+ {
+ FPREG_St(native, i) = lpContext->FltSave.FloatRegisters[i];
+ }
+
+ for (int i = 0; i < 16; i++)
+ {
+ FPREG_Xmm(native, i) = lpContext->FltSave.XmmRegisters[i];
+ }
+#endif
+ }
+
+ // TODO: Enable for all Unix systems
+#if defined(_AMD64_) && defined(__linux__)
+ if ((lpContext->ContextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE)
+ {
+ _ASSERTE(FPREG_HasExtendedState(native));
+ memcpy_s(FPREG_Xstate_Ymmh(native), sizeof(M128A) * 16, lpContext->VectorRegister, sizeof(M128A) * 16);
+ }
+#endif // _AMD64_
+}
+
+/*++
+Function :
+ CONTEXTFromNativeContext
+
+ Converts a native context to a CONTEXT record.
+
+Parameters :
+ const native_context_t *native : native context to convert
+ LPCONTEXT lpContext : CONTEXT to fill in
+ ULONG contextFlags : flags that determine which registers are valid in
+ native and which ones to set in lpContext
+
+Return value :
+ None
+
+--*/
+void CONTEXTFromNativeContext(const native_context_t *native, LPCONTEXT lpContext,
+ ULONG contextFlags)
+{
+ lpContext->ContextFlags = contextFlags;
+
+#define ASSIGN_REG(reg) lpContext->reg = MCREG_##reg(native->uc_mcontext);
+ if ((contextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
+ {
+ ASSIGN_CONTROL_REGS
+#ifdef _ARM_
+ // WinContext assumes that the least bit of Pc is always 1 (denoting thumb)
+ // although the pc value retrived from native context might not have set the least bit.
+ // This becomes especially problematic if the context is on the JIT_WRITEBARRIER.
+ lpContext->Pc |= 0x1;
+#endif
+ }
+
+ if ((contextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
+ {
+ ASSIGN_INTEGER_REGS
+ }
+#undef ASSIGN_REG
+
+#if HAVE_GREGSET_T || HAVE___GREGSET_T
+#if HAVE_GREGSET_T
+ if (native->uc_mcontext.fpregs == nullptr)
+#elif HAVE___GREGSET_T
+ if (native->uc_mcontext.__fpregs == nullptr)
+#endif
+ {
+ // Reset the CONTEXT_FLOATING_POINT bit(s) and the CONTEXT_XSTATE bit(s) so it's
+ // clear that the floating point and extended state data in the CONTEXT is not
+ // valid. Since these flags are defined as the architecture bit(s) OR'd with one
+ // or more other bits, we first get the bits that are unique to each by resetting
+ // the architecture bits. We determine what those are by inverting the union of
+ // CONTEXT_CONTROL and CONTEXT_INTEGER, both of which should also have the
+ // architecture bit(s) set.
+ const ULONG floatingPointFlags = CONTEXT_FLOATING_POINT & ~(CONTEXT_CONTROL & CONTEXT_INTEGER);
+ const ULONG xstateFlags = CONTEXT_XSTATE & ~(CONTEXT_CONTROL & CONTEXT_INTEGER);
+
+ lpContext->ContextFlags &= ~(floatingPointFlags | xstateFlags);
+
+ // Bail out regardless of whether the caller wanted CONTEXT_FLOATING_POINT or CONTEXT_XSTATE
+ return;
+ }
+#endif
+
+ if ((contextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT)
+ {
+#ifdef _AMD64_
+ lpContext->FltSave.ControlWord = FPREG_ControlWord(native);
+ lpContext->FltSave.StatusWord = FPREG_StatusWord(native);
+ lpContext->FltSave.TagWord = FPREG_TagWord(native);
+ lpContext->FltSave.ErrorOffset = FPREG_ErrorOffset(native);
+ lpContext->FltSave.ErrorSelector = FPREG_ErrorSelector(native);
+ lpContext->FltSave.DataOffset = FPREG_DataOffset(native);
+ lpContext->FltSave.DataSelector = FPREG_DataSelector(native);
+ lpContext->FltSave.MxCsr = FPREG_MxCsr(native);
+ lpContext->FltSave.MxCsr_Mask = FPREG_MxCsr_Mask(native);
+
+ for (int i = 0; i < 8; i++)
+ {
+ lpContext->FltSave.FloatRegisters[i] = FPREG_St(native, i);
+ }
+
+ for (int i = 0; i < 16; i++)
+ {
+ lpContext->FltSave.XmmRegisters[i] = FPREG_Xmm(native, i);
+ }
+#endif
+ }
+
+ // TODO: Enable for all Unix systems
+#if defined(_AMD64_) && defined(__linux__)
+ if ((contextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE)
+ {
+ if (FPREG_HasExtendedState(native))
+ {
+ memcpy_s(lpContext->VectorRegister, sizeof(M128A) * 16, FPREG_Xstate_Ymmh(native), sizeof(M128A) * 16);
+ }
+ else
+ {
+ // Reset the CONTEXT_XSTATE bit(s) so it's clear that the extended state data in
+ // the CONTEXT is not valid.
+ const ULONG xstateFlags = CONTEXT_XSTATE & ~(CONTEXT_CONTROL & CONTEXT_INTEGER);
+ lpContext->ContextFlags &= ~xstateFlags;
+ }
+ }
+#endif // _AMD64_
+}
+
+/*++
+Function :
+ GetNativeContextPC
+
+ Returns the program counter from the native context.
+
+Parameters :
+ const native_context_t *native : native context
+
+Return value :
+ The program counter from the native context.
+
+--*/
+LPVOID GetNativeContextPC(const native_context_t *context)
+{
+#ifdef _AMD64_
+ return (LPVOID)MCREG_Rip(context->uc_mcontext);
+#elif defined(_X86_)
+ return (LPVOID) MCREG_Eip(context->uc_mcontext);
+#elif defined(_ARM_)
+ return (LPVOID) MCREG_Pc(context->uc_mcontext);
+#elif defined(_ARM64_)
+ return (LPVOID) MCREG_Pc(context->uc_mcontext);
+#else
+# error implement me for this architecture
+#endif
+}
+
+/*++
+Function :
+ CONTEXTGetExceptionCodeForSignal
+
+ Translates signal and context information to a Win32 exception code.
+
+Parameters :
+ const siginfo_t *siginfo : signal information from a signal handler
+ const native_context_t *context : context information
+
+Return value :
+ The Win32 exception code that corresponds to the signal and context
+ information.
+
+--*/
+#ifdef ILL_ILLOPC
+// If si_code values are available for all signals, use those.
+DWORD CONTEXTGetExceptionCodeForSignal(const siginfo_t *siginfo,
+ const native_context_t *context)
+{
+ // IMPORTANT NOTE: This function must not call any signal unsafe functions
+ // since it is called from signal handlers.
+ // That includes ASSERT and TRACE macros.
+
+ switch (siginfo->si_signo)
+ {
+ case SIGILL:
+ switch (siginfo->si_code)
+ {
+ case ILL_ILLOPC: // Illegal opcode
+ case ILL_ILLOPN: // Illegal operand
+ case ILL_ILLADR: // Illegal addressing mode
+ case ILL_ILLTRP: // Illegal trap
+ case ILL_COPROC: // Co-processor error
+ return EXCEPTION_ILLEGAL_INSTRUCTION;
+ case ILL_PRVOPC: // Privileged opcode
+ case ILL_PRVREG: // Privileged register
+ return EXCEPTION_PRIV_INSTRUCTION;
+ case ILL_BADSTK: // Internal stack error
+ return EXCEPTION_STACK_OVERFLOW;
+ default:
+ break;
+ }
+ break;
+ case SIGFPE:
+ switch (siginfo->si_code)
+ {
+ case FPE_INTDIV:
+ return EXCEPTION_INT_DIVIDE_BY_ZERO;
+ case FPE_INTOVF:
+ return EXCEPTION_INT_OVERFLOW;
+ case FPE_FLTDIV:
+ return EXCEPTION_FLT_DIVIDE_BY_ZERO;
+ case FPE_FLTOVF:
+ return EXCEPTION_FLT_OVERFLOW;
+ case FPE_FLTUND:
+ return EXCEPTION_FLT_UNDERFLOW;
+ case FPE_FLTRES:
+ return EXCEPTION_FLT_INEXACT_RESULT;
+ case FPE_FLTINV:
+ return EXCEPTION_FLT_INVALID_OPERATION;
+ case FPE_FLTSUB:
+ return EXCEPTION_FLT_INVALID_OPERATION;
+ default:
+ break;
+ }
+ break;
+ case SIGSEGV:
+ switch (siginfo->si_code)
+ {
+ case SI_USER: // User-generated signal, sometimes sent
+ // for SIGSEGV under normal circumstances
+ case SEGV_MAPERR: // Address not mapped to object
+ case SEGV_ACCERR: // Invalid permissions for mapped object
+ return EXCEPTION_ACCESS_VIOLATION;
+
+#ifdef SI_KERNEL
+ case SI_KERNEL:
+ {
+ // Identify privileged instructions that are not identified as such by the system
+ if (g_getGcMarkerExceptionCode != nullptr)
+ {
+ DWORD exceptionCode = g_getGcMarkerExceptionCode(GetNativeContextPC(context));
+ if (exceptionCode != 0)
+ {
+ return exceptionCode;
+ }
+ }
+ return EXCEPTION_ACCESS_VIOLATION;
+ }
+#endif
+ default:
+ break;
+ }
+ break;
+ case SIGBUS:
+ switch (siginfo->si_code)
+ {
+ case BUS_ADRALN: // Invalid address alignment
+ return EXCEPTION_DATATYPE_MISALIGNMENT;
+ case BUS_ADRERR: // Non-existent physical address
+ return EXCEPTION_ACCESS_VIOLATION;
+ case BUS_OBJERR: // Object-specific hardware error
+ default:
+ break;
+ }
+ case SIGTRAP:
+ switch (siginfo->si_code)
+ {
+#ifdef SI_KERNEL
+ case SI_KERNEL:
+#endif
+ case SI_USER:
+ case TRAP_BRKPT: // Process breakpoint
+ return EXCEPTION_BREAKPOINT;
+ case TRAP_TRACE: // Process trace trap
+ return EXCEPTION_SINGLE_STEP;
+ default:
+ // Got unknown SIGTRAP signal with code siginfo->si_code;
+ return EXCEPTION_ILLEGAL_INSTRUCTION;
+ }
+ default:
+ break;
+ }
+
+ // Got unknown signal number siginfo->si_signo with code siginfo->si_code;
+ return EXCEPTION_ILLEGAL_INSTRUCTION;
+}
+#else // ILL_ILLOPC
+DWORD CONTEXTGetExceptionCodeForSignal(const siginfo_t *siginfo,
+ const native_context_t *context)
+{
+ // IMPORTANT NOTE: This function must not call any signal unsafe functions
+ // since it is called from signal handlers.
+ // That includes ASSERT and TRACE macros.
+
+ int trap;
+
+ if (siginfo->si_signo == SIGFPE)
+ {
+ // Floating point exceptions are mapped by their si_code.
+ switch (siginfo->si_code)
+ {
+ case FPE_INTDIV :
+ return EXCEPTION_INT_DIVIDE_BY_ZERO;
+ case FPE_INTOVF :
+ return EXCEPTION_INT_OVERFLOW;
+ case FPE_FLTDIV :
+ return EXCEPTION_FLT_DIVIDE_BY_ZERO;
+ case FPE_FLTOVF :
+ return EXCEPTION_FLT_OVERFLOW;
+ case FPE_FLTUND :
+ return EXCEPTION_FLT_UNDERFLOW;
+ case FPE_FLTRES :
+ return EXCEPTION_FLT_INEXACT_RESULT;
+ case FPE_FLTINV :
+ return EXCEPTION_FLT_INVALID_OPERATION;
+ case FPE_FLTSUB :/* subscript out of range */
+ return EXCEPTION_FLT_INVALID_OPERATION;
+ default:
+ // Got unknown signal code siginfo->si_code;
+ return 0;
+ }
+ }
+
+ trap = context->uc_mcontext.mc_trapno;
+ switch (trap)
+ {
+ case T_PRIVINFLT : /* privileged instruction */
+ return EXCEPTION_PRIV_INSTRUCTION;
+ case T_BPTFLT : /* breakpoint instruction */
+ return EXCEPTION_BREAKPOINT;
+ case T_ARITHTRAP : /* arithmetic trap */
+ return 0; /* let the caller pick an exception code */
+#ifdef T_ASTFLT
+ case T_ASTFLT : /* system forced exception : ^C, ^\. SIGINT signal
+ handler shouldn't be calling this function, since
+ it doesn't need an exception code */
+ // Trap code T_ASTFLT received, shouldn't get here;
+ return 0;
+#endif // T_ASTFLT
+ case T_PROTFLT : /* protection fault */
+ return EXCEPTION_ACCESS_VIOLATION;
+ case T_TRCTRAP : /* debug exception (sic) */
+ return EXCEPTION_SINGLE_STEP;
+ case T_PAGEFLT : /* page fault */
+ return EXCEPTION_ACCESS_VIOLATION;
+ case T_ALIGNFLT : /* alignment fault */
+ return EXCEPTION_DATATYPE_MISALIGNMENT;
+ case T_DIVIDE :
+ return EXCEPTION_INT_DIVIDE_BY_ZERO;
+ case T_NMI : /* non-maskable trap */
+ return EXCEPTION_ILLEGAL_INSTRUCTION;
+ case T_OFLOW :
+ return EXCEPTION_INT_OVERFLOW;
+ case T_BOUND : /* bound instruction fault */
+ return EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
+ case T_DNA : /* device not available fault */
+ return EXCEPTION_ILLEGAL_INSTRUCTION;
+ case T_DOUBLEFLT : /* double fault */
+ return EXCEPTION_ILLEGAL_INSTRUCTION;
+ case T_FPOPFLT : /* fp coprocessor operand fetch fault */
+ return EXCEPTION_FLT_INVALID_OPERATION;
+ case T_TSSFLT : /* invalid tss fault */
+ return EXCEPTION_ILLEGAL_INSTRUCTION;
+ case T_SEGNPFLT : /* segment not present fault */
+ return EXCEPTION_ACCESS_VIOLATION;
+ case T_STKFLT : /* stack fault */
+ return EXCEPTION_STACK_OVERFLOW;
+ case T_MCHK : /* machine check trap */
+ return EXCEPTION_ILLEGAL_INSTRUCTION;
+ case T_RESERVED : /* reserved (unknown) */
+ return EXCEPTION_ILLEGAL_INSTRUCTION;
+ default:
+ // Got unknown trap code trap;
+ break;
+ }
+ return EXCEPTION_ILLEGAL_INSTRUCTION;
+}
+#endif // ILL_ILLOPC
+
+#else // !HAVE_MACH_EXCEPTIONS
+
+#include <mach/message.h>
+#include <mach/thread_act.h>
+#include "../exception/machexception.h"
+
+/*++
+Function:
+ CONTEXT_GetThreadContextFromPort
+
+ Helper for GetThreadContext that uses a mach_port
+--*/
+kern_return_t
+CONTEXT_GetThreadContextFromPort(
+ mach_port_t Port,
+ LPCONTEXT lpContext)
+{
+ // Extract the CONTEXT from the Mach thread.
+
+ kern_return_t MachRet = KERN_SUCCESS;
+ mach_msg_type_number_t StateCount;
+ thread_state_flavor_t StateFlavor;
+
+ if (lpContext->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS))
+ {
+#ifdef _X86_
+ x86_thread_state32_t State;
+ StateFlavor = x86_THREAD_STATE32;
+#elif defined(_AMD64_)
+ x86_thread_state64_t State;
+ StateFlavor = x86_THREAD_STATE64;
+#else
+#error Unexpected architecture.
+#endif
+ StateCount = sizeof(State) / sizeof(natural_t);
+ MachRet = thread_get_state(Port, StateFlavor, (thread_state_t)&State, &StateCount);
+ if (MachRet != KERN_SUCCESS)
+ {
+ ASSERT("thread_get_state(THREAD_STATE) failed: %d\n", MachRet);
+ goto exit;
+ }
+
+ CONTEXT_GetThreadContextFromThreadState(StateFlavor, (thread_state_t)&State, lpContext);
+ }
+
+ if (lpContext->ContextFlags & CONTEXT_ALL_FLOATING) {
+#ifdef _X86_
+ x86_float_state32_t State;
+ StateFlavor = x86_FLOAT_STATE32;
+#elif defined(_AMD64_)
+ x86_float_state64_t State;
+ StateFlavor = x86_FLOAT_STATE64;
+#else
+#error Unexpected architecture.
+#endif
+ StateCount = sizeof(State) / sizeof(natural_t);
+ MachRet = thread_get_state(Port, StateFlavor, (thread_state_t)&State, &StateCount);
+ if (MachRet != KERN_SUCCESS)
+ {
+ ASSERT("thread_get_state(FLOAT_STATE) failed: %d\n", MachRet);
+ goto exit;
+ }
+
+ CONTEXT_GetThreadContextFromThreadState(StateFlavor, (thread_state_t)&State, lpContext);
+ }
+
+exit:
+ return MachRet;
+}
+
+/*++
+Function:
+ CONTEXT_GetThreadContextFromThreadState
+
+--*/
+void
+CONTEXT_GetThreadContextFromThreadState(
+ thread_state_flavor_t threadStateFlavor,
+ thread_state_t threadState,
+ LPCONTEXT lpContext)
+{
+ switch (threadStateFlavor)
+ {
+#ifdef _X86_
+ case x86_THREAD_STATE32:
+ if (lpContext->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS))
+ {
+ x86_thread_state32_t *pState = (x86_thread_state32_t *)threadState;
+
+ lpContext->Eax = pState->eax;
+ lpContext->Ebx = pState->ebx;
+ lpContext->Ecx = pState->ecx;
+ lpContext->Edx = pState->edx;
+ lpContext->Edi = pState->edi;
+ lpContext->Esi = pState->esi;
+ lpContext->Ebp = pState->ebp;
+ lpContext->Esp = pState->esp;
+ lpContext->SegSs = pState->ss;
+ lpContext->EFlags = pState->eflags;
+ lpContext->Eip = pState->eip;
+ lpContext->SegCs = pState->cs;
+ lpContext->SegDs_PAL_Undefined = pState->ds;
+ lpContext->SegEs_PAL_Undefined = pState->es;
+ lpContext->SegFs_PAL_Undefined = pState->fs;
+ lpContext->SegGs_PAL_Undefined = pState->gs;
+ }
+ break;
+
+ case x86_FLOAT_STATE32:
+ {
+ x86_float_state32_t *pState = (x86_float_state32_t *)threadState;
+
+ if (lpContext->ContextFlags & CONTEXT_FLOATING_POINT)
+ {
+ lpContext->FloatSave.ControlWord = *(DWORD*)&pState->fpu_fcw;
+ lpContext->FloatSave.StatusWord = *(DWORD*)&pState->fpu_fsw;
+ lpContext->FloatSave.TagWord = pState->fpu_ftw;
+ lpContext->FloatSave.ErrorOffset = pState->fpu_ip;
+ lpContext->FloatSave.ErrorSelector = pState->fpu_cs;
+ lpContext->FloatSave.DataOffset = pState->fpu_dp;
+ lpContext->FloatSave.DataSelector = pState->fpu_ds;
+ lpContext->FloatSave.Cr0NpxState = pState->fpu_mxcsr;
+
+ // Windows stores the floating point registers in a packed layout (each 10-byte register end to end
+ // for a total of 80 bytes). But Mach returns each register in an 16-bit structure (presumably for
+ // alignment purposes). So we can't just memcpy the registers over in a single block, we need to copy
+ // them individually.
+ for (int i = 0; i < 8; i++)
+ memcpy(&lpContext->FloatSave.RegisterArea[i * 10], (&pState->fpu_stmm0)[i].mmst_reg, 10);
+ }
+
+ if (lpContext->ContextFlags & CONTEXT_EXTENDED_REGISTERS)
+ {
+ // The only extended register information that Mach will tell us about are the xmm register values.
+ // Both Windows and Mach store the registers in a packed layout (each of the 8 registers is 16 bytes)
+ // so we can simply memcpy them across.
+ memcpy(lpContext->ExtendedRegisters + CONTEXT_EXREG_XMM_OFFSET, &pState->fpu_xmm0, 8 * 16);
+ }
+ }
+ break;
+
+#elif defined(_AMD64_)
+ case x86_THREAD_STATE64:
+ if (lpContext->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS))
+ {
+ x86_thread_state64_t *pState = (x86_thread_state64_t *)threadState;
+
+ lpContext->Rax = pState->__rax;
+ lpContext->Rbx = pState->__rbx;
+ lpContext->Rcx = pState->__rcx;
+ lpContext->Rdx = pState->__rdx;
+ lpContext->Rdi = pState->__rdi;
+ lpContext->Rsi = pState->__rsi;
+ lpContext->Rbp = pState->__rbp;
+ lpContext->Rsp = pState->__rsp;
+ lpContext->R8 = pState->__r8;
+ lpContext->R9 = pState->__r9;
+ lpContext->R10 = pState->__r10;
+ lpContext->R11 = pState->__r11;
+ lpContext->R12 = pState->__r12;
+ lpContext->R13 = pState->__r13;
+ lpContext->R14 = pState->__r14;
+ lpContext->R15 = pState->__r15;
+ lpContext->EFlags = pState->__rflags;
+ lpContext->Rip = pState->__rip;
+ lpContext->SegCs = pState->__cs;
+ // RtlRestoreContext uses the actual ss instead of this one
+ // to build the iret frame so just set it zero.
+ lpContext->SegSs = 0;
+ lpContext->SegDs = 0;
+ lpContext->SegEs = 0;
+ lpContext->SegFs = pState->__fs;
+ lpContext->SegGs = pState->__gs;
+ }
+ break;
+
+ case x86_FLOAT_STATE64:
+ if (lpContext->ContextFlags & CONTEXT_FLOATING_POINT)
+ {
+ x86_float_state64_t *pState = (x86_float_state64_t *)threadState;
+
+ lpContext->FltSave.ControlWord = *(DWORD*)&pState->__fpu_fcw;
+ lpContext->FltSave.StatusWord = *(DWORD*)&pState->__fpu_fsw;
+ lpContext->FltSave.TagWord = pState->__fpu_ftw;
+ lpContext->FltSave.ErrorOffset = pState->__fpu_ip;
+ lpContext->FltSave.ErrorSelector = pState->__fpu_cs;
+ lpContext->FltSave.DataOffset = pState->__fpu_dp;
+ lpContext->FltSave.DataSelector = pState->__fpu_ds;
+ lpContext->FltSave.MxCsr = pState->__fpu_mxcsr;
+ lpContext->FltSave.MxCsr_Mask = pState->__fpu_mxcsrmask; // note: we don't save the mask for x86
+
+ // Windows stores the floating point registers in a packed layout (each 10-byte register end to end
+ // for a total of 80 bytes). But Mach returns each register in an 16-bit structure (presumably for
+ // alignment purposes). So we can't just memcpy the registers over in a single block, we need to copy
+ // them individually.
+ for (int i = 0; i < 8; i++)
+ memcpy(&lpContext->FltSave.FloatRegisters[i], (&pState->__fpu_stmm0)[i].__mmst_reg, 10);
+
+ // AMD64's FLOATING_POINT includes the xmm registers.
+ memcpy(&lpContext->Xmm0, &pState->__fpu_xmm0, 8 * 16);
+ }
+ break;
+#else
+#error Unexpected architecture.
+#endif
+ case x86_THREAD_STATE:
+ {
+ x86_thread_state_t *pState = (x86_thread_state_t *)threadState;
+ CONTEXT_GetThreadContextFromThreadState((thread_state_flavor_t)pState->tsh.flavor, (thread_state_t)&pState->uts, lpContext);
+ }
+ break;
+
+ case x86_FLOAT_STATE:
+ {
+ x86_float_state_t *pState = (x86_float_state_t *)threadState;
+ CONTEXT_GetThreadContextFromThreadState((thread_state_flavor_t)pState->fsh.flavor, (thread_state_t)&pState->ufs, lpContext);
+ }
+ break;
+
+ default:
+ ASSERT("Invalid thread state flavor %d\n", threadStateFlavor);
+ break;
+ }
+}
+
+/*++
+Function:
+ GetThreadContext
+
+See MSDN doc.
+--*/
+BOOL
+CONTEXT_GetThreadContext(
+ DWORD dwProcessId,
+ pthread_t self,
+ LPCONTEXT lpContext)
+{
+ BOOL ret = FALSE;
+
+ if (lpContext == NULL)
+ {
+ ERROR("Invalid lpContext parameter value\n");
+ SetLastError(ERROR_NOACCESS);
+ goto EXIT;
+ }
+
+ if (GetCurrentProcessId() == dwProcessId)
+ {
+ if (self != pthread_self())
+ {
+ // the target thread is in the current process, but isn't
+ // the current one: extract the CONTEXT from the Mach thread.
+ mach_port_t mptPort;
+ mptPort = pthread_mach_thread_np(self);
+
+ ret = (CONTEXT_GetThreadContextFromPort(mptPort, lpContext) == KERN_SUCCESS);
+ }
+ else
+ {
+ CONTEXT_CaptureContext(lpContext);
+ ret = TRUE;
+ }
+ }
+ else
+ {
+ ASSERT("Cross-process GetThreadContext() is not supported on this platform\n");
+ SetLastError(ERROR_NOACCESS);
+ }
+
+EXIT:
+ return ret;
+}
+
+/*++
+Function:
+ SetThreadContextOnPort
+
+ Helper for CONTEXT_SetThreadContext
+--*/
+kern_return_t
+CONTEXT_SetThreadContextOnPort(
+ mach_port_t Port,
+ IN CONST CONTEXT *lpContext)
+{
+ kern_return_t MachRet = KERN_SUCCESS;
+ mach_msg_type_number_t StateCount;
+ thread_state_flavor_t StateFlavor;
+
+ if (lpContext->ContextFlags & (CONTEXT_CONTROL|CONTEXT_INTEGER))
+ {
+#ifdef _X86_
+ x86_thread_state32_t State;
+ StateFlavor = x86_THREAD_STATE32;
+
+ State.eax = lpContext->Eax;
+ State.ebx = lpContext->Ebx;
+ State.ecx = lpContext->Ecx;
+ State.edx = lpContext->Edx;
+ State.edi = lpContext->Edi;
+ State.esi = lpContext->Esi;
+ State.ebp = lpContext->Ebp;
+ State.esp = lpContext->Esp;
+ State.ss = lpContext->SegSs;
+ State.eflags = lpContext->EFlags;
+ State.eip = lpContext->Eip;
+ State.cs = lpContext->SegCs;
+ State.ds = lpContext->SegDs_PAL_Undefined;
+ State.es = lpContext->SegEs_PAL_Undefined;
+ State.fs = lpContext->SegFs_PAL_Undefined;
+ State.gs = lpContext->SegGs_PAL_Undefined;
+#elif defined(_AMD64_)
+ x86_thread_state64_t State;
+ StateFlavor = x86_THREAD_STATE64;
+
+ State.__rax = lpContext->Rax;
+ State.__rbx = lpContext->Rbx;
+ State.__rcx = lpContext->Rcx;
+ State.__rdx = lpContext->Rdx;
+ State.__rdi = lpContext->Rdi;
+ State.__rsi = lpContext->Rsi;
+ State.__rbp = lpContext->Rbp;
+ State.__rsp = lpContext->Rsp;
+ State.__r8 = lpContext->R8;
+ State.__r9 = lpContext->R9;
+ State.__r10 = lpContext->R10;
+ State.__r11 = lpContext->R11;
+ State.__r12 = lpContext->R12;
+ State.__r13 = lpContext->R13;
+ State.__r14 = lpContext->R14;
+ State.__r15 = lpContext->R15;
+// State.ss = lpContext->SegSs;
+ State.__rflags = lpContext->EFlags;
+ State.__rip = lpContext->Rip;
+ State.__cs = lpContext->SegCs;
+// State.ds = lpContext->SegDs_PAL_Undefined;
+// State.es = lpContext->SegEs_PAL_Undefined;
+ State.__fs = lpContext->SegFs;
+ State.__gs = lpContext->SegGs;
+#else
+#error Unexpected architecture.
+#endif
+
+ StateCount = sizeof(State) / sizeof(natural_t);
+
+ MachRet = thread_set_state(Port,
+ StateFlavor,
+ (thread_state_t)&State,
+ StateCount);
+ if (MachRet != KERN_SUCCESS)
+ {
+ ASSERT("thread_set_state(THREAD_STATE) failed: %d\n", MachRet);
+ goto EXIT;
+ }
+ }
+
+ if (lpContext->ContextFlags & CONTEXT_ALL_FLOATING)
+ {
+
+#ifdef _X86_
+ x86_float_state32_t State;
+ StateFlavor = x86_FLOAT_STATE32;
+#elif defined(_AMD64_)
+ x86_float_state64_t State;
+ StateFlavor = x86_FLOAT_STATE64;
+#else
+#error Unexpected architecture.
+#endif
+
+ StateCount = sizeof(State) / sizeof(natural_t);
+
+ // If we're setting only one of the floating point or extended registers (of which Mach supports only
+ // the xmm values) then we don't have values for the other set. This is a problem since Mach only
+ // supports setting both groups as a single unit. So in this case we'll need to fetch the current
+ // values first.
+ if ((lpContext->ContextFlags & CONTEXT_ALL_FLOATING) !=
+ CONTEXT_ALL_FLOATING)
+ {
+ mach_msg_type_number_t StateCountGet = StateCount;
+ MachRet = thread_get_state(Port,
+ StateFlavor,
+ (thread_state_t)&State,
+ &StateCountGet);
+ if (MachRet != KERN_SUCCESS)
+ {
+ ASSERT("thread_get_state(FLOAT_STATE) failed: %d\n", MachRet);
+ goto EXIT;
+ }
+ _ASSERTE(StateCountGet == StateCount);
+ }
+
+ if (lpContext->ContextFlags & CONTEXT_FLOATING_POINT)
+ {
+#ifdef _X86_
+ *(DWORD*)&State.fpu_fcw = lpContext->FloatSave.ControlWord;
+ *(DWORD*)&State.fpu_fsw = lpContext->FloatSave.StatusWord;
+ State.fpu_ftw = lpContext->FloatSave.TagWord;
+ State.fpu_ip = lpContext->FloatSave.ErrorOffset;
+ State.fpu_cs = lpContext->FloatSave.ErrorSelector;
+ State.fpu_dp = lpContext->FloatSave.DataOffset;
+ State.fpu_ds = lpContext->FloatSave.DataSelector;
+ State.fpu_mxcsr = lpContext->FloatSave.Cr0NpxState;
+
+ // Windows stores the floating point registers in a packed layout (each 10-byte register end to
+ // end for a total of 80 bytes). But Mach returns each register in an 16-bit structure (presumably
+ // for alignment purposes). So we can't just memcpy the registers over in a single block, we need
+ // to copy them individually.
+ for (int i = 0; i < 8; i++)
+ memcpy((&State.fpu_stmm0)[i].mmst_reg, &lpContext->FloatSave.RegisterArea[i * 10], 10);
+#elif defined(_AMD64_)
+ *(DWORD*)&State.__fpu_fcw = lpContext->FltSave.ControlWord;
+ *(DWORD*)&State.__fpu_fsw = lpContext->FltSave.StatusWord;
+ State.__fpu_ftw = lpContext->FltSave.TagWord;
+ State.__fpu_ip = lpContext->FltSave.ErrorOffset;
+ State.__fpu_cs = lpContext->FltSave.ErrorSelector;
+ State.__fpu_dp = lpContext->FltSave.DataOffset;
+ State.__fpu_ds = lpContext->FltSave.DataSelector;
+ State.__fpu_mxcsr = lpContext->FltSave.MxCsr;
+ State.__fpu_mxcsrmask = lpContext->FltSave.MxCsr_Mask; // note: we don't save the mask for x86
+
+ // Windows stores the floating point registers in a packed layout (each 10-byte register end to
+ // end for a total of 80 bytes). But Mach returns each register in an 16-bit structure (presumably
+ // for alignment purposes). So we can't just memcpy the registers over in a single block, we need
+ // to copy them individually.
+ for (int i = 0; i < 8; i++)
+ memcpy((&State.__fpu_stmm0)[i].__mmst_reg, &lpContext->FltSave.FloatRegisters[i], 10);
+
+ memcpy(&State.__fpu_xmm0, &lpContext->Xmm0, 8 * 16);
+#else
+#error Unexpected architecture.
+#endif
+ }
+
+#ifdef _X86_
+ if (lpContext->ContextFlags & CONTEXT_EXTENDED_REGISTERS)
+ {
+ // The only extended register information that Mach will tell us about are the xmm register
+ // values. Both Windows and Mach store the registers in a packed layout (each of the 8 registers
+ // is 16 bytes) so we can simply memcpy them across.
+ memcpy(&State.fpu_xmm0, lpContext->ExtendedRegisters + CONTEXT_EXREG_XMM_OFFSET, 8 * 16);
+ }
+#endif // _X86_
+
+ MachRet = thread_set_state(Port,
+ StateFlavor,
+ (thread_state_t)&State,
+ StateCount);
+ if (MachRet != KERN_SUCCESS)
+ {
+ ASSERT("thread_set_state(FLOAT_STATE) failed: %d\n", MachRet);
+ goto EXIT;
+ }
+ }
+
+EXIT:
+ return MachRet;
+}
+
+/*++
+Function:
+ SetThreadContext
+
+See MSDN doc.
+--*/
+BOOL
+CONTEXT_SetThreadContext(
+ DWORD dwProcessId,
+ pthread_t self,
+ CONST CONTEXT *lpContext)
+{
+ BOOL ret = FALSE;
+
+ if (lpContext == NULL)
+ {
+ ERROR("Invalid lpContext parameter value\n");
+ SetLastError(ERROR_NOACCESS);
+ goto EXIT;
+ }
+
+ if (dwProcessId != GetCurrentProcessId())
+ {
+ // GetThreadContext() of a thread in another process
+ ASSERT("Cross-process GetThreadContext() is not supported\n");
+ SetLastError(ERROR_NOACCESS);
+ goto EXIT;
+ }
+
+ if (self != pthread_self())
+ {
+ // hThread is in the current process, but isn't the current
+ // thread. Extract the CONTEXT from the Mach thread.
+
+ mach_port_t mptPort;
+
+ mptPort = pthread_mach_thread_np(self);
+
+ ret = (CONTEXT_SetThreadContextOnPort(mptPort, lpContext) == KERN_SUCCESS);
+ }
+ else
+ {
+ MachSetThreadContext(const_cast<CONTEXT *>(lpContext));
+ ASSERT("MachSetThreadContext should never return\n");
+ }
+
+EXIT:
+ return ret;
+}
+
+#endif // !HAVE_MACH_EXCEPTIONS
+
+/*++
+Function:
+ DBG_FlushInstructionCache: processor-specific portion of
+ FlushInstructionCache
+
+See MSDN doc.
+--*/
+BOOL
+DBG_FlushInstructionCache(
+ IN LPCVOID lpBaseAddress,
+ IN SIZE_T dwSize)
+{
+ // Intrinsic should do the right thing across all platforms
+ __builtin___clear_cache((char *)lpBaseAddress, (char *)((INT_PTR)lpBaseAddress + dwSize));
+
+ return TRUE;
+}
diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp
new file mode 100644
index 0000000000..315145dc03
--- /dev/null
+++ b/src/pal/src/thread/process.cpp
@@ -0,0 +1,4615 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ process.cpp
+
+Abstract:
+
+ Implementation of process object and functions related to processes.
+
+
+
+--*/
+
+#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/dbgmsg.h"
+#include "pal/utils.h"
+#include "pal/environ.h"
+#include "pal/virtual.h"
+#include "pal/stackstring.hpp"
+
+#include <errno.h>
+#if HAVE_POLL
+#include <poll.h>
+#else
+#include "pal/fakepoll.h"
+#endif // HAVE_POLL
+
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <debugmacrosext.h>
+#include <semaphore.h>
+#include <stdint.h>
+
+#ifdef __APPLE__
+#include <sys/sysctl.h>
+#endif
+
+#ifdef __NetBSD__
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <kvm.h>
+#endif
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(PROCESS);
+
+CObjectType CorUnix::otProcess(
+ otiProcess,
+ NULL,
+ NULL,
+ 0,
+ sizeof(CProcProcessLocalData),
+ 0,
+ PROCESS_ALL_ACCESS,
+ CObjectType::SecuritySupported,
+ CObjectType::SecurityInfoNotPersisted,
+ CObjectType::UnnamedObject,
+ CObjectType::CrossProcessDuplicationAllowed,
+ CObjectType::WaitableObject,
+ CObjectType::SingleTransitionObject,
+ CObjectType::ThreadReleaseHasNoSideEffects,
+ CObjectType::NoOwner
+ );
+
+static
+DWORD
+PALAPI
+StartupHelperThread(
+ LPVOID p);
+
+static
+BOOL
+GetProcessIdDisambiguationKey(
+ IN DWORD processId,
+ OUT UINT64 *disambiguationKey);
+
+//
+// Helper memory page used by the FlushProcessWriteBuffers
+//
+static int s_helperPage[VIRTUAL_PAGE_SIZE / sizeof(int)] __attribute__((aligned(VIRTUAL_PAGE_SIZE)));
+
+//
+// Mutex to make the FlushProcessWriteBuffersMutex thread safe
+//
+pthread_mutex_t flushProcessWriteBuffersMutex;
+
+CAllowedObjectTypes aotProcess(otiProcess);
+
+//
+// The representative IPalObject for this process
+//
+IPalObject* CorUnix::g_pobjProcess;
+
+//
+// Critical section that protects process data (e.g., the
+// list of active threads)/
+//
+CRITICAL_SECTION g_csProcess;
+
+//
+// List and count of active threads
+//
+CPalThread* CorUnix::pGThreadList;
+DWORD g_dwThreadCount;
+
+//
+// The command line and app name for the process
+//
+LPWSTR g_lpwstrCmdLine = NULL;
+LPWSTR g_lpwstrAppDir = NULL;
+
+// Thread ID of thread that has started the ExitProcess process
+Volatile<LONG> terminator = 0;
+
+// Process and session ID of this process.
+DWORD gPID = (DWORD) -1;
+DWORD gSID = (DWORD) -1;
+
+// The lowest common supported semaphore length, including null character
+// NetBSD-7.99.25: 15 characters
+// MacOSX 10.11: 31 -- Core 1.0 RC2 compatibility
+#if defined(__NetBSD__)
+#define CLR_SEM_MAX_NAMELEN 15
+#else
+#define CLR_SEM_MAX_NAMELEN (NAME_MAX - 4)
+#endif
+
+// Function to call during PAL/process shutdown/abort
+Volatile<PSHUTDOWN_CALLBACK> g_shutdownCallback = nullptr;
+
+//
+// Key used for associating CPalThread's with the underlying pthread
+// (through pthread_setspecific)
+//
+pthread_key_t CorUnix::thObjKey;
+
+#define PROCESS_PELOADER_FILENAME "clix"
+
+static WCHAR W16_WHITESPACE[]= {0x0020, 0x0009, 0x000D, 0};
+static WCHAR W16_WHITESPACE_DQUOTE[]= {0x0020, 0x0009, 0x000D, '"', 0};
+
+enum FILETYPE
+{
+ FILE_ERROR,/*ERROR*/
+ FILE_PE, /*PE/COFF file*/
+ FILE_UNIX, /*Unix Executable*/
+ FILE_DIR /*Directory*/
+};
+
+PAL_ERROR
+PROCGetProcessStatus(
+ CPalThread *pThread,
+ HANDLE hProcess,
+ PROCESS_STATE *pps,
+ DWORD *pdwExitCode
+ );
+
+static BOOL getFileName(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
+ PathCharString& lpFileName);
+static char ** buildArgv(LPCWSTR lpCommandLine, PathCharString& lpAppPath,
+ UINT *pnArg, BOOL prependLoader);
+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:
+ CreateProcessA
+
+Note:
+ Only Standard handles need to be inherited.
+ Security attributes parameters are not used.
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+CreateProcessA(
+ IN LPCSTR lpApplicationName,
+ IN LPSTR lpCommandLine,
+ IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ IN BOOL bInheritHandles,
+ IN DWORD dwCreationFlags,
+ IN LPVOID lpEnvironment,
+ IN LPCSTR lpCurrentDirectory,
+ IN LPSTARTUPINFOA lpStartupInfo,
+ OUT LPPROCESS_INFORMATION lpProcessInformation)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread;
+ STARTUPINFOW StartupInfoW;
+ LPWSTR CommandLineW = NULL;
+ LPWSTR ApplicationNameW = NULL;
+ LPWSTR CurrentDirectoryW = NULL;
+
+ int n;
+
+ PERF_ENTRY(CreateProcessA);
+ ENTRY("CreateProcessA(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:"NULL",
+ lpApplicationName?lpApplicationName:"NULL",
+ lpCommandLine?lpCommandLine:"NULL",
+ lpCommandLine?lpCommandLine:"NULL",
+ lpProcessAttributes, lpThreadAttributes, bInheritHandles,
+ dwCreationFlags, lpEnvironment,
+ lpCurrentDirectory?lpCurrentDirectory:"NULL",
+ lpCurrentDirectory?lpCurrentDirectory:"NULL",
+ lpStartupInfo, lpProcessInformation);
+
+ pThread = InternalGetCurrentThread();
+
+ if(lpStartupInfo == NULL)
+ {
+ ASSERT("lpStartupInfo is NULL!\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* convert parameters to Unicode */
+
+ if(lpApplicationName)
+ {
+ n = MultiByteToWideChar(CP_ACP, 0, lpApplicationName, -1, NULL, 0);
+ if(0 == n)
+ {
+ ASSERT("MultiByteToWideChar failed!\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+ ApplicationNameW = (LPWSTR)InternalMalloc(sizeof(WCHAR)*n);
+ if(!ApplicationNameW)
+ {
+ ERROR("malloc() failed!\n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ MultiByteToWideChar(CP_ACP, 0, lpApplicationName, -1, ApplicationNameW,
+ n);
+ }
+
+ if(lpCommandLine)
+ {
+ n = MultiByteToWideChar(CP_ACP, 0, lpCommandLine, -1, NULL, 0);
+ if(0 == n)
+ {
+ ASSERT("MultiByteToWideChar failed!\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+ CommandLineW = (LPWSTR)InternalMalloc(sizeof(WCHAR)*n);
+ if(!CommandLineW)
+ {
+ ERROR("malloc() failed!\n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ MultiByteToWideChar(CP_ACP, 0, lpCommandLine, -1, CommandLineW, n);
+ }
+
+ if(lpCurrentDirectory)
+ {
+ n = MultiByteToWideChar(CP_ACP, 0, lpCurrentDirectory, -1, NULL, 0);
+ if(0 == n)
+ {
+ ASSERT("MultiByteToWideChar failed!\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+ CurrentDirectoryW = (LPWSTR)InternalMalloc(sizeof(WCHAR)*n);
+ if(!CurrentDirectoryW)
+ {
+ ERROR("malloc() failed!\n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ MultiByteToWideChar(CP_ACP, 0, lpCurrentDirectory, -1,
+ CurrentDirectoryW, n);
+ }
+
+ // lpEnvironment should remain ansi on the call to CreateProcessW
+
+ StartupInfoW.cb = sizeof StartupInfoW;
+ StartupInfoW.dwFlags = lpStartupInfo->dwFlags;
+ StartupInfoW.hStdError = lpStartupInfo->hStdError;
+ StartupInfoW.hStdInput = lpStartupInfo->hStdInput;
+ StartupInfoW.hStdOutput = lpStartupInfo->hStdOutput;
+ /* all other members are PAL_Undefined, we can ignore them */
+
+ palError = InternalCreateProcess(
+ pThread,
+ ApplicationNameW,
+ CommandLineW,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags,
+ lpEnvironment,
+ CurrentDirectoryW,
+ &StartupInfoW,
+ lpProcessInformation
+ );
+done:
+ free(ApplicationNameW);
+ free(CommandLineW);
+ free(CurrentDirectoryW);
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("CreateProcessA returns BOOL %d\n", NO_ERROR == palError);
+ PERF_EXIT(CreateProcessA);
+ return NO_ERROR == palError;
+}
+
+
+/*++
+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,
+ bInheritHandles,
+ 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,
+ 0,
+ &pobjFile
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Bad handle passed through CreateProcess\n");
+ goto PrepareStandardHandleExit;
+ }
+
+ palError = pobjFile->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to access file data\n");
+ goto PrepareStandardHandleExit;
+ }
+
+ //
+ // The passed in file needs to be inheritable
+ //
+
+ if (!pLocalData->inheritable)
+ {
+ ERROR("Non-inheritable handle passed through CreateProcess\n");
+ palError = ERROR_INVALID_HANDLE;
+ goto PrepareStandardHandleExit;
+ }
+
+ iError = fcntl(pLocalData->unix_fd, F_SETFD, 0);
+ if (-1 == iError)
+ {
+ ERROR("Unable to remove close-on-exec for file (errno %i)\n", errno);
+ palError = ERROR_INVALID_HANDLE;
+ goto PrepareStandardHandleExit;
+ }
+
+ *piFd = pLocalData->unix_fd;
+ pDataLock->ReleaseLock(pThread, FALSE);
+ pDataLock = NULL;
+
+ //
+ // Transfer pobjFile reference to out parameter
+ //
+
+ *ppobjFile = pobjFile;
+ pobjFile = NULL;
+
+PrepareStandardHandleExit:
+
+ if (NULL != pDataLock)
+ {
+ pDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pobjFile)
+ {
+ pobjFile->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+PAL_ERROR
+CorUnix::InternalCreateProcess(
+ CPalThread *pThread,
+ LPCWSTR lpApplicationName,
+ LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ 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_PE: /* PE/COFF file */
+ //Get the path name where the PAL DLL was loaded from
+ if ( PAL_GetPALDirectoryA( lpFileNamePS ))
+ {
+ if (lpFileNamePS.Append("/", 1) == FALSE ||
+ lpFileNamePS.Append( PROCESS_PELOADER_FILENAME, strlen(PROCESS_PELOADER_FILENAME)) == FALSE)
+ {
+ ERROR("Append failed!\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalCreateProcessExit;
+ }
+ }
+ else
+ {
+ ASSERT("PAL_GetPALDirectoryA failed to return the"
+ "pal installation directory \n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalCreateProcessExit;
+ }
+
+ break;
+
+ 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, iRet==1);
+
+ /* 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,
+ PROCESS_ALL_ACCESS,
+ &hProcess,
+ &pobjProcessRegistered
+ );
+
+ //
+ // pobjProcess is invalidated by the above call, so
+ // NULL it out here
+ //
+
+ pobjProcess = NULL;
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to register new process object\n");
+ goto InternalCreateProcessExit;
+ }
+
+ //
+ // Create a new "dummy" thread object
+ //
+
+ palError = InternalCreateDummyThread(
+ pThread,
+ lpThreadAttributes,
+ &pDummyThread,
+ &hDummyThread
+ );
+
+ if (dwCreationFlags & CREATE_SUSPENDED)
+ {
+ int pipe_descs[2];
+
+ if (-1 == pipe(pipe_descs))
+ {
+ ERROR("pipe() failed! error is %d (%s)\n", errno, strerror(errno));
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto InternalCreateProcessExit;
+ }
+
+ /* [0] is read end, [1] is write end */
+ pDummyThread->suspensionInfo.SetBlockingPipe(pipe_descs[1]);
+ parent_blocking_pipe = pipe_descs[1];
+ child_blocking_pipe = pipe_descs[0];
+ }
+
+ palError = pobjProcessRegistered->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pLocalDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to obtain local data for new process object\n");
+ goto InternalCreateProcessExit;
+ }
+
+
+ /* fork the new process */
+ processId = fork();
+
+ if (processId == -1)
+ {
+ ASSERT("Unable to create a new process with fork()\n");
+ if (-1 != child_blocking_pipe)
+ {
+ close(child_blocking_pipe);
+ close(parent_blocking_pipe);
+ }
+
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalCreateProcessExit;
+ }
+
+ /* From the time the child process begins running, to when it reaches execve,
+ the child process is not a real PAL process and does not own any PAL
+ resources, although it has access to the PAL resources of its parent process.
+ Thus, while the child process is in this window, it is dangerous for it to affect
+ its parent's PAL resources. As a consequence, no PAL code should be used
+ in this window; all code should make unix calls. Note the use of _exit
+ instead of exit to avoid calling PAL_Terminate and the lack of TRACE's and
+ ASSERT's. */
+
+ if (processId == 0) /* child process */
+ {
+ // At this point, the PAL should be considered uninitialized for this child process.
+
+ // Don't want to enter the init_critsec here since we're trying to avoid
+ // calling PAL functions. Furthermore, nothing should be changing
+ // the init_count in the child process at this point since this is the only
+ // thread executing.
+ init_count = 0;
+
+ sigset_t sm;
+
+ //
+ // Clear out the signal mask for the new process.
+ //
+
+ sigemptyset(&sm);
+ iRet = sigprocmask(SIG_SETMASK, &sm, NULL);
+ if (iRet != 0)
+ {
+ _exit(EXIT_FAILURE);
+ }
+
+ if (dwCreationFlags & CREATE_SUSPENDED)
+ {
+ BYTE resume_code = 0;
+ ssize_t read_ret;
+
+ /* close the write end of the pipe, the child doesn't need it */
+ close(parent_blocking_pipe);
+
+ read_again:
+ /* block until ResumeThread writes something to the pipe */
+ read_ret = read(child_blocking_pipe, &resume_code, sizeof(resume_code));
+ if (sizeof(resume_code) != read_ret)
+ {
+ if (read_ret == -1 && EINTR == errno)
+ {
+ goto read_again;
+ }
+ else
+ {
+ /* note : read might return 0 (and return EAGAIN) if the other
+ end of the pipe gets closed - for example because the parent
+ process dies (very) abruptly */
+ _exit(EXIT_FAILURE);
+ }
+ }
+ if (WAKEUPCODE != resume_code)
+ {
+ // resume_code should always equal WAKEUPCODE.
+ _exit(EXIT_FAILURE);
+ }
+
+ close(child_blocking_pipe);
+ }
+
+ /* Set the current directory */
+ if (lpCurrentDirectory)
+ {
+ SetCurrentDirectoryW(lpCurrentDirectory);
+ }
+
+ /* Set the standard handles to the incoming values */
+ if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES)
+ {
+ /* For each handle, we need to duplicate the incoming unix
+ fd to the corresponding standard one. The API that I use,
+ dup2, will copy the source to the destination, automatically
+ closing the existing destination, in an atomic way */
+ if (dup2(iFdIn, STDIN_FILENO) == -1)
+ {
+ // Didn't duplicate standard in.
+ _exit(EXIT_FAILURE);
+ }
+
+ if (dup2(iFdOut, STDOUT_FILENO) == -1)
+ {
+ // Didn't duplicate standard out.
+ _exit(EXIT_FAILURE);
+ }
+
+ if (dup2(iFdErr, STDERR_FILENO) == -1)
+ {
+ // Didn't duplicate standard error.
+ _exit(EXIT_FAILURE);
+ }
+
+ /* now close the original FDs, we don't need them anymore */
+ close(iFdIn);
+ close(iFdOut);
+ close(iFdErr);
+ }
+
+ /* execute the new process */
+
+ if (EnvironmentArray)
+ {
+ execve(lpFileNamePS, lppArgv, EnvironmentArray);
+ }
+ else
+ {
+ execve(lpFileNamePS, lppArgv, palEnvironment);
+ }
+
+ /* if we get here, it means the execve function call failed so just exit */
+ _exit(EXIT_FAILURE);
+ }
+
+ /* parent process */
+
+ /* close the read end of the pipe, the parent doesn't need it */
+ close(child_blocking_pipe);
+
+ /* Set the process ID */
+ pLocalData->dwProcessId = processId;
+ pLocalDataLock->ReleaseLock(pThread, TRUE);
+ pLocalDataLock = NULL;
+
+ //
+ // Release file handle info; we don't need them anymore. Note that
+ // this must happen after we've released the data locks, as
+ // otherwise a deadlock could result.
+ //
+
+ if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES)
+ {
+ pobjFileIn->ReleaseReference(pThread);
+ pobjFileIn = NULL;
+ pobjFileOut->ReleaseReference(pThread);
+ pobjFileOut = NULL;
+ pobjFileErr->ReleaseReference(pThread);
+ pobjFileErr = NULL;
+ }
+
+ /* fill PROCESS_INFORMATION strucutre */
+ lpProcessInformation->hProcess = hProcess;
+ lpProcessInformation->hThread = hDummyThread;
+ lpProcessInformation->dwProcessId = processId;
+ lpProcessInformation->dwThreadId_PAL_Undefined = 0;
+
+
+ TRACE("New process created: id=%#x\n", processId);
+
+InternalCreateProcessExit:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pSharedDataLock)
+ {
+ pSharedDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pobjProcess)
+ {
+ pobjProcess->ReleaseReference(pThread);
+ }
+
+ if (NULL != pobjProcessRegistered)
+ {
+ pobjProcessRegistered->ReleaseReference(pThread);
+ }
+
+ if (NO_ERROR != palError)
+ {
+ if (NULL != hProcess)
+ {
+ g_pObjectManager->RevokeHandle(pThread, hProcess);
+ }
+
+ if (NULL != hDummyThread)
+ {
+ g_pObjectManager->RevokeHandle(pThread, hDummyThread);
+ }
+ }
+
+ if (EnvironmentArray)
+ {
+ free(EnvironmentArray);
+ }
+
+ /* if we still have the file structures at this point, it means we
+ encountered an error sometime between when we acquired them and when we
+ fork()ed. We not only have to release them, we have to give them back
+ their close-on-exec flag */
+ if (NULL != pobjFileIn)
+ {
+ if(-1 == fcntl(iFdIn, F_SETFD, 1))
+ {
+ WARN("couldn't restore close-on-exec flag to stdin descriptor! "
+ "errno is %d (%s)\n", errno, strerror(errno));
+ }
+ pobjFileIn->ReleaseReference(pThread);
+ }
+
+ if (NULL != pobjFileOut)
+ {
+ if(-1 == fcntl(iFdOut, F_SETFD, 1))
+ {
+ WARN("couldn't restore close-on-exec flag to stdout descriptor! "
+ "errno is %d (%s)\n", errno, strerror(errno));
+ }
+ pobjFileOut->ReleaseReference(pThread);
+ }
+
+ if (NULL != pobjFileErr)
+ {
+ if(-1 == fcntl(iFdErr, F_SETFD, 1))
+ {
+ WARN("couldn't restore close-on-exec flag to stderr descriptor! "
+ "errno is %d (%s)\n", errno, strerror(errno));
+ }
+ pobjFileErr->ReleaseReference(pThread);
+ }
+
+ /* free allocated memory */
+ if (lppArgv)
+ {
+ free(*lppArgv);
+ free(lppArgv);
+ }
+
+ return palError;
+}
+
+
+/*++
+Function:
+ GetExitCodeProcess
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetExitCodeProcess(
+ IN HANDLE hProcess,
+ IN LPDWORD lpExitCode)
+{
+ CPalThread *pThread;
+ PAL_ERROR palError = NO_ERROR;
+ DWORD dwExitCode;
+ PROCESS_STATE ps;
+
+ PERF_ENTRY(GetExitCodeProcess);
+ ENTRY("GetExitCodeProcess(hProcess = %p, lpExitCode = %p)\n",
+ hProcess, lpExitCode);
+
+ pThread = InternalGetCurrentThread();
+
+ if(NULL == lpExitCode)
+ {
+ WARN("Got NULL lpExitCode\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ palError = PROCGetProcessStatus(
+ pThread,
+ hProcess,
+ &ps,
+ &dwExitCode
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Couldn't get process status information!\n");
+ goto done;
+ }
+
+ if( PS_DONE == ps )
+ {
+ *lpExitCode = dwExitCode;
+ }
+ else
+ {
+ *lpExitCode = STILL_ACTIVE;
+ }
+
+done:
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("GetExitCodeProcess returns BOOL %d\n", NO_ERROR == palError);
+ PERF_EXIT(GetExitCodeProcess);
+
+ return NO_ERROR == palError;
+}
+
+/*++
+Function:
+ ExitProcess
+
+See MSDN doc.
+--*/
+PAL_NORETURN
+VOID
+PALAPI
+ExitProcess(
+ IN UINT uExitCode)
+{
+ DWORD old_terminator;
+
+ PERF_ENTRY_ONLY(ExitProcess);
+ ENTRY("ExitProcess(uExitCode=0x%x)\n", uExitCode );
+
+ old_terminator = InterlockedCompareExchange(&terminator, GetCurrentThreadId(), 0);
+
+ if (GetCurrentThreadId() == old_terminator)
+ {
+ // This thread has already initiated termination. This can happen
+ // in two ways:
+ // 1) DllMain(DLL_PROCESS_DETACH) triggers a call to ExitProcess.
+ // 2) PAL_exit() is called after the last PALTerminate().
+ // If the PAL is still initialized, we go straight through to
+ // PROCEndProcess. If it isn't, we simply exit.
+ if (!PALIsInitialized())
+ {
+ exit(uExitCode);
+ ASSERT("exit has returned\n");
+ }
+ else
+ {
+ WARN("thread re-called ExitProcess\n");
+ PROCEndProcess(GetCurrentProcess(), uExitCode, FALSE);
+ }
+ }
+ else if (0 != 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? */
+ WARN("termination already started from another thread; blocking.\n");
+ poll(NULL, 0, INFTIM);
+ }
+
+ /* ExitProcess may be called even if PAL is not initialized.
+ Verify if process structure exist
+ */
+ if (PALInitLock() && PALIsInitialized())
+ {
+ PROCEndProcess(GetCurrentProcess(), uExitCode, FALSE);
+
+ /* Should not get here, because we terminate the current process */
+ ASSERT("PROCEndProcess has returned\n");
+ }
+ else
+ {
+ exit(uExitCode);
+
+ /* Should not get here, because we terminate the current process */
+ ASSERT("exit has returned\n");
+ }
+
+ /* this should never get executed */
+ ASSERT("ExitProcess should not return!\n");
+ for (;;);
+}
+
+/*++
+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:
+ 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");
+
+ // exit() runs atexit handlers possibly registered by foreign code.
+ // The right thing to do here is to leave the PAL. If our client
+ // registered our own PAL_Terminate with atexit(), the latter will
+ // explicitly re-enter us.
+ PAL_Leave(PAL_BoundaryBottom);
+
+ if (bTerminateUnconditionally)
+ {
+ // abort() has the semantics that
+ // (1) it doesn't run atexit handlers
+ // (2) can invoke CrashReporter or produce a coredump,
+ // which is appropriate for TerminateProcess calls
+ abort();
+ }
+ else
+ {
+ exit(uExitCode);
+ }
+
+ ASSERT(FALSE); // we shouldn't get here
+ }
+
+ return ret;
+}
+
+/*++
+Function:
+ PAL_SetShutdownCallback
+
+Abstract:
+ Sets a callback that is executed when the PAL is shut down because of
+ ExitProcess, TerminateProcess or PAL_Shutdown but not PAL_Terminate/Ex.
+
+ NOTE: Currently only one callback can be set at a time.
+--*/
+PALIMPORT
+VOID
+PALAPI
+PAL_SetShutdownCallback(
+ IN PSHUTDOWN_CALLBACK callback)
+{
+ _ASSERTE(g_shutdownCallback == nullptr);
+ g_shutdownCallback = callback;
+}
+
+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* RuntimeStartupSemaphoreName = "/clrst%08llx";
+static const char* RuntimeContinueSemaphoreName = "/clrco%08llx";
+#else
+static const char* RuntimeStartupSemaphoreName = "/clrst%08x%016llx";
+static const char* RuntimeContinueSemaphoreName = "/clrco%08x%016llx";
+#endif
+
+#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* PipeNameFormat = "/tmp/clr-debug-pipe-%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;
+
+ // 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;
+
+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)
+ {
+ char startupSemName[CLR_SEM_MAX_NAMELEN];
+ sprintf_s(startupSemName,
+ sizeof(startupSemName),
+ RuntimeStartupSemaphoreName,
+ HashSemaphoreName(m_processId,
+ m_processIdDisambiguationKey));
+
+ sem_close(m_startupSem);
+ sem_unlink(startupSemName);
+ }
+
+ if (m_continueSem != SEM_FAILED)
+ {
+ char continueSemName[CLR_SEM_MAX_NAMELEN];
+ sprintf_s(continueSemName,
+ sizeof(continueSemName),
+ RuntimeContinueSemaphoreName,
+ HashSemaphoreName(m_processId,
+ m_processIdDisambiguationKey));
+
+ sem_close(m_continueSem);
+ sem_unlink(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)
+ {
+ delete 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()
+ {
+ CPalThread *pThread = InternalGetCurrentThread();
+ char startupSemName[CLR_SEM_MAX_NAMELEN];
+ char continueSemName[CLR_SEM_MAX_NAMELEN];
+ PAL_ERROR pe = NO_ERROR;
+
+ // 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.
+ INDEBUG(BOOL disambiguationKeyRet = )
+ GetProcessIdDisambiguationKey(m_processId, &m_processIdDisambiguationKey);
+ _ASSERTE(disambiguationKeyRet == TRUE || m_processIdDisambiguationKey == 0);
+
+ sprintf_s(startupSemName,
+ sizeof(startupSemName),
+ RuntimeStartupSemaphoreName,
+ HashSemaphoreName(m_processId,
+ m_processIdDisambiguationKey));
+
+ sprintf_s(continueSemName,
+ sizeof(continueSemName),
+ RuntimeContinueSemaphoreName,
+ HashSemaphoreName(m_processId,
+ m_processIdDisambiguationKey));
+
+ TRACE("PAL_RuntimeStartupHelper.Register creating startup '%s' continue '%s'\n", startupSemName, 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(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(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,
+ &m_threadId,
+ &m_threadHandle);
+
+ if (NO_ERROR != pe)
+ {
+ TRACE("InternalCreateThread failed %d\n", pe);
+ Release();
+ goto exit;
+ }
+
+ 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, "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
+ if (sem_wait(m_startupSem) == 0)
+ {
+ pe = InvokeStartupCallback();
+ }
+ else
+ {
+ TRACE("sem_wait(startup) failed: errno is %d (%s)\n", errno, strerror(errno));
+ pe = GetSemError();
+ }
+ }
+
+ // Invoke the callback on errors
+ if (pe != NO_ERROR && !m_canceled)
+ {
+ SetLastError(pe);
+ m_callback(NULL, NULL, m_parameter);
+ }
+ }
+};
+
+static
+DWORD
+PALAPI
+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
+ 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 PPAL_STARTUP_CALLBACK pfnCallback,
+ IN PVOID parameter,
+ OUT PVOID *ppUnregisterToken)
+{
+ _ASSERTE(pfnCallback != NULL);
+ _ASSERTE(ppUnregisterToken != NULL);
+
+ PAL_RuntimeStartupHelper *helper = new PAL_RuntimeStartupHelper(dwProcessId, pfnCallback, parameter);
+
+ // Create the debuggee startup semaphore so the runtime (debuggee) knows to wait for
+ // a debugger connection.
+ PAL_ERROR pe = helper->Register();
+ 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;
+}
+
+/*++
+ PAL_NotifyRuntimeStarted
+
+ Signals the debugger waiting for runtime startup notification to continue and
+ waits until the debugger signals us to continue.
+
+Parameters:
+ None
+
+Return value:
+ TRUE - succeeded, FALSE - failed
+--*/
+BOOL
+PALAPI
+PAL_NotifyRuntimeStarted()
+{
+ char startupSemName[CLR_SEM_MAX_NAMELEN];
+ char continueSemName[CLR_SEM_MAX_NAMELEN];
+ sem_t *startupSem = SEM_FAILED;
+ sem_t *continueSem = SEM_FAILED;
+ BOOL result = TRUE;
+
+ UINT64 processIdDisambiguationKey = 0;
+ GetProcessIdDisambiguationKey(gPID, &processIdDisambiguationKey);
+
+ sprintf_s(startupSemName, sizeof(startupSemName), RuntimeStartupSemaphoreName, HashSemaphoreName(gPID, processIdDisambiguationKey));
+ sprintf_s(continueSemName, sizeof(continueSemName), RuntimeContinueSemaphoreName, HashSemaphoreName(gPID, processIdDisambiguationKey));
+
+ TRACE("PAL_NotifyRuntimeStarted opening continue '%s' startup '%s'\n", continueSemName, startupSemName);
+
+
+ // Open the debugger startup semaphore. If it doesn't exists, then we do nothing and
+ // the function is successful.
+ startupSem = sem_open(startupSemName, 0);
+ if (startupSem == SEM_FAILED)
+ {
+ TRACE("sem_open(%s) failed: %d (%s)\n", startupSemName, errno, strerror(errno));
+ goto exit;
+ }
+
+ continueSem = sem_open(continueSemName, 0);
+ if (continueSem == SEM_FAILED)
+ {
+ ASSERT("sem_open(%s) failed: %d (%s)\n", continueSemName, errno, strerror(errno));
+ result = FALSE;
+ goto exit;
+ }
+
+ // Wake up the debugger waiting for startup
+ if (sem_post(startupSem) != 0)
+ {
+ ASSERT("sem_post(startupSem) failed: errno is %d (%s)\n", errno, strerror(errno));
+ result = FALSE;
+ goto exit;
+ }
+
+ // Now wait until the debugger's runtime startup notification is finished
+ if (sem_wait(continueSem) != 0)
+ {
+ ASSERT("sem_wait(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno));
+ result = FALSE;
+ goto exit;
+ }
+
+exit:
+ if (startupSem != SEM_FAILED)
+ {
+ sem_close(startupSem);
+ }
+ if (continueSem != SEM_FAILED)
+ {
+ sem_close(continueSem);
+ }
+ return result;
+}
+
+/*++
+ 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__)
+
+ // 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)
+ {
+ timeval procStartTime = info.kp_proc.p_starttime;
+ long secondsSinceEpoch = procStartTime.tv_sec;
+
+ *disambiguationKey = secondsSinceEpoch;
+ return TRUE;
+ }
+ else
+ {
+ _ASSERTE(!"Failed to get start time of a process.");
+ return FALSE;
+ }
+
+#elif defined(__NetBSD__)
+
+ // On NetBSD, we return the process start time expressed in Unix time (the number of seconds
+ // since the start of the Unix epoch).
+ kvm_t *kd;
+ int cnt;
+ struct kinfo_proc2 *info;
+
+ kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, "kvm_open");
+ if (kd == nullptr)
+ {
+ _ASSERTE(!"Failed to get start time of a process.");
+ return FALSE;
+ }
+
+ info = kvm_getproc2(kd, KERN_PROC_PID, processId, sizeof(struct kinfo_proc2), &cnt);
+ if (info == nullptr || cnt < 1)
+ {
+ kvm_close(kd);
+ _ASSERTE(!"Failed to get start time of a process.");
+ return FALSE;
+ }
+
+ kvm_close(kd);
+
+ long secondsSinceEpoch = info->p_ustart_sec;
+ *disambiguationKey = secondsSinceEpoch;
+
+ return TRUE;
+
+#elif HAVE_PROCFS_STAT
+
+ // Here we read /proc/<pid>/stat file to get the start time for the process.
+ // We return this value (which is expressed in jiffies since boot time).
+
+ // Making something like: /proc/123/stat
+ char statFileName[64];
+
+ INDEBUG(int chars = )
+ snprintf(statFileName, sizeof(statFileName), "/proc/%d/stat", processId);
+ _ASSERTE(chars > 0 && chars <= sizeof(statFileName));
+
+ FILE *statFile = fopen(statFileName, "r");
+ if (statFile == nullptr)
+ {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+
+ char *line = nullptr;
+ size_t lineLen = 0;
+ if (getline(&line, &lineLen, statFile) == -1)
+ {
+ _ASSERTE(!"Failed to getline from the stat file for a process.");
+ 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 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(scanStartPosition,
+ "%*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %*lu %*lu %*ld %*ld %*ld %*ld %*ld %*ld %llu \n",
+ &starttime);
+
+ if (sscanfRet != 1)
+ {
+ _ASSERTE(!"Failed to parse stat file contents with sscanf.");
+ 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_GetTransportPipeName
+
+ Builds the transport pipe names from the process id.
+--*/
+void
+PALAPI
+PAL_GetTransportPipeName(char *name, DWORD id, const char *suffix)
+{
+ UINT64 disambiguationKey = 0;
+ BOOL ret = GetProcessIdDisambiguationKey(id, &disambiguationKey);
+
+ // 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);
+
+ int chars = _snprintf(name, MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, PipeNameFormat, id, disambiguationKey, suffix);
+ _ASSERTE(chars > 0 && chars < MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH);
+}
+
+/*++
+Function:
+ GetProcessTimes
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetProcessTimes(
+ IN HANDLE hProcess,
+ OUT LPFILETIME lpCreationTime,
+ OUT LPFILETIME lpExitTime,
+ OUT LPFILETIME lpKernelTime,
+ OUT LPFILETIME lpUserTime)
+{
+ BOOL retval = FALSE;
+ struct rusage resUsage;
+ __int64 calcTime;
+ const __int64 SECS_TO_NS = 1000000000; /* 10^9 */
+ const __int64 USECS_TO_NS = 1000; /* 10^3 */
+
+
+ PERF_ENTRY(GetProcessTimes);
+ ENTRY("GetProcessTimes(hProcess=%p, lpExitTime=%p, lpKernelTime=%p,"
+ "lpUserTime=%p)\n",
+ hProcess, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime );
+
+ /* Make sure hProcess is the current process, this is the only supported
+ case */
+ if(PROCGetProcessIDFromHandle(hProcess)!=GetCurrentProcessId())
+ {
+ ASSERT("GetProcessTimes() does not work on a process other than the "
+ "current process.\n");
+ SetLastError(ERROR_INVALID_HANDLE);
+ goto GetProcessTimesExit;
+ }
+
+ /* First, we need to actually retrieve the relevant statistics from the
+ OS */
+ if (getrusage (RUSAGE_SELF, &resUsage) == -1)
+ {
+ ASSERT("Unable to get resource usage information for the current "
+ "process\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto GetProcessTimesExit;
+ }
+
+ TRACE ("getrusage User: %ld sec,%ld microsec. Kernel: %ld sec,%ld"
+ " microsec\n",
+ resUsage.ru_utime.tv_sec, resUsage.ru_utime.tv_usec,
+ resUsage.ru_stime.tv_sec, resUsage.ru_stime.tv_usec);
+
+ if (lpUserTime)
+ {
+ /* Get the time of user mode execution, in 100s of nanoseconds */
+ calcTime = (__int64)resUsage.ru_utime.tv_sec * SECS_TO_NS;
+ calcTime += (__int64)resUsage.ru_utime.tv_usec * USECS_TO_NS;
+ calcTime /= 100; /* Produce the time in 100s of ns */
+ /* Assign the time into lpUserTime */
+ lpUserTime->dwLowDateTime = (DWORD)calcTime;
+ lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
+ }
+
+ if (lpKernelTime)
+ {
+ /* Get the time of kernel mode execution, in 100s of nanoseconds */
+ calcTime = (__int64)resUsage.ru_stime.tv_sec * SECS_TO_NS;
+ calcTime += (__int64)resUsage.ru_stime.tv_usec * USECS_TO_NS;
+ calcTime /= 100; /* Produce the time in 100s of ns */
+ /* Assign the time into lpUserTime */
+ lpKernelTime->dwLowDateTime = (DWORD)calcTime;
+ lpKernelTime->dwHighDateTime = (DWORD)(calcTime >> 32);
+ }
+
+ retval = TRUE;
+
+
+GetProcessTimesExit:
+ LOGEXIT("GetProcessTimes returns BOOL %d\n", retval);
+ PERF_EXIT(GetProcessTimes);
+ return (retval);
+}
+
+#define FILETIME_TO_ULONGLONG(f) \
+ (((ULONGLONG)(f).dwHighDateTime << 32) | ((ULONGLONG)(f).dwLowDateTime))
+
+/*++
+Function:
+ PAL_GetCPUBusyTime
+
+The main purpose of this function is to compute the overall CPU utilization
+for the CLR thread pool to regulate the number of I/O completion port
+worker threads.
+Since there is no consistent API on Unix to get the CPU utilization
+from a user process, getrusage and gettimeofday are used to
+compute the current process's CPU utilization instead.
+This function emulates the ThreadpoolMgr::GetCPUBusyTime_NT function in
+win32threadpool.cpp of the CLR.
+
+See MSDN doc for GetSystemTimes.
+--*/
+INT
+PALAPI
+PAL_GetCPUBusyTime(
+ IN OUT PAL_IOCP_CPU_INFORMATION *lpPrevCPUInfo)
+{
+ ULONGLONG nLastRecordedCurrentTime = 0;
+ ULONGLONG nLastRecordedUserTime = 0;
+ ULONGLONG nLastRecordedKernelTime = 0;
+ ULONGLONG nKernelTime = 0;
+ ULONGLONG nUserTime = 0;
+ ULONGLONG nCurrentTime = 0;
+ ULONGLONG nCpuBusyTime = 0;
+ ULONGLONG nCpuTotalTime = 0;
+ DWORD nReading = 0;
+ struct rusage resUsage;
+ struct timeval tv;
+ static DWORD dwNumberOfProcessors = 0;
+
+ if (dwNumberOfProcessors <= 0)
+ {
+ SYSTEM_INFO SystemInfo;
+ GetSystemInfo(&SystemInfo);
+ dwNumberOfProcessors = SystemInfo.dwNumberOfProcessors;
+ if (dwNumberOfProcessors <= 0)
+ {
+ return 0;
+ }
+ }
+
+ if (getrusage(RUSAGE_SELF, &resUsage) == -1)
+ {
+ ASSERT("getrusage() failed; errno is %d (%s)\n", errno, strerror(errno));
+ return 0;
+ }
+ else
+ {
+ nKernelTime = (ULONGLONG)resUsage.ru_stime.tv_sec*tccSecondsTo100NanoSeconds +
+ resUsage.ru_stime.tv_usec*tccMicroSecondsTo100NanoSeconds;
+ nUserTime = (ULONGLONG)resUsage.ru_utime.tv_sec*tccSecondsTo100NanoSeconds +
+ resUsage.ru_utime.tv_usec*tccMicroSecondsTo100NanoSeconds;
+ }
+
+ if (gettimeofday(&tv, NULL) == -1)
+ {
+ ASSERT("gettimeofday() failed; errno is %d (%s)\n", errno, strerror(errno));
+ return 0;
+ }
+ else
+ {
+ nCurrentTime = (ULONGLONG)tv.tv_sec*tccSecondsTo100NanoSeconds +
+ tv.tv_usec*tccMicroSecondsTo100NanoSeconds;
+ }
+
+ nLastRecordedCurrentTime = FILETIME_TO_ULONGLONG(lpPrevCPUInfo->LastRecordedTime.ftLastRecordedCurrentTime);
+ nLastRecordedUserTime = FILETIME_TO_ULONGLONG(lpPrevCPUInfo->ftLastRecordedUserTime);
+ nLastRecordedKernelTime = FILETIME_TO_ULONGLONG(lpPrevCPUInfo->ftLastRecordedKernelTime);
+
+ if (nCurrentTime > nLastRecordedCurrentTime)
+ {
+ nCpuTotalTime = (nCurrentTime - nLastRecordedCurrentTime);
+#if HAVE_THREAD_SELF || HAVE__LWP_SELF || HAVE_VM_READ
+ // For systems that run multiple threads of a process on multiple processors,
+ // the accumulated userTime and kernelTime of this process may exceed
+ // the elapsed time. In this case, the cpuTotalTime needs to be adjusted
+ // according to number of processors so that the cpu utilization
+ // will not be greater than 100.
+ nCpuTotalTime *= dwNumberOfProcessors;
+#endif // HAVE_THREAD_SELF || HAVE__LWP_SELF || HAVE_VM_READ
+ }
+
+ if (nUserTime >= nLastRecordedUserTime &&
+ nKernelTime >= nLastRecordedKernelTime)
+ {
+ nCpuBusyTime =
+ (nUserTime - nLastRecordedUserTime)+
+ (nKernelTime - nLastRecordedKernelTime);
+ }
+
+ if (nCpuTotalTime > 0 && nCpuBusyTime > 0)
+ {
+ nReading = (DWORD)((nCpuBusyTime*100)/nCpuTotalTime);
+ TRACE("PAL_GetCPUBusyTime: nCurrentTime=%lld, nKernelTime=%lld, nUserTime=%lld, nReading=%d\n",
+ nCurrentTime, nKernelTime, nUserTime, nReading);
+ }
+
+ if (nReading > 100)
+ {
+ ERROR("cpu utilization(%d) > 100\n", nReading);
+ }
+
+ lpPrevCPUInfo->LastRecordedTime.ftLastRecordedCurrentTime.dwLowDateTime = (DWORD)nCurrentTime;
+ lpPrevCPUInfo->LastRecordedTime.ftLastRecordedCurrentTime.dwHighDateTime = (DWORD)(nCurrentTime >> 32);
+
+ lpPrevCPUInfo->ftLastRecordedUserTime.dwLowDateTime = (DWORD)nUserTime;
+ lpPrevCPUInfo->ftLastRecordedUserTime.dwHighDateTime = (DWORD)(nUserTime >> 32);
+
+ lpPrevCPUInfo->ftLastRecordedKernelTime.dwLowDateTime = (DWORD)nKernelTime;
+ lpPrevCPUInfo->ftLastRecordedKernelTime.dwHighDateTime = (DWORD)(nKernelTime >> 32);
+
+ return (DWORD)nReading;
+}
+
+/*++
+Function:
+ GetCommandLineW
+
+See MSDN doc.
+--*/
+LPWSTR
+PALAPI
+GetCommandLineW(
+ VOID)
+{
+ PERF_ENTRY(GetCommandLineW);
+ ENTRY("GetCommandLineW()\n");
+
+ LPWSTR lpwstr = g_lpwstrCmdLine ? g_lpwstrCmdLine : (LPWSTR)W("");
+
+ LOGEXIT("GetCommandLineW returns LPWSTR %p (%S)\n",
+ g_lpwstrCmdLine,
+ lpwstr);
+ PERF_EXIT(GetCommandLineW);
+
+ return lpwstr;
+}
+
+/*++
+Function:
+ OpenProcess
+
+See MSDN doc.
+
+Notes :
+dwDesiredAccess is ignored (all supported operations will be allowed)
+bInheritHandle is ignored (no inheritance)
+--*/
+HANDLE
+PALAPI
+OpenProcess(
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ DWORD dwProcessId)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+ IPalObject *pobjProcess = NULL;
+ IPalObject *pobjProcessRegistered = NULL;
+ IDataLock *pDataLock;
+ CProcProcessLocalData *pLocalData;
+ CObjectAttributes oa;
+ HANDLE hProcess = NULL;
+
+ PERF_ENTRY(OpenProcess);
+ ENTRY("OpenProcess(dwDesiredAccess=0x%08x, bInheritHandle=%d, "
+ "dwProcessId = 0x%08x)\n",
+ dwDesiredAccess, bInheritHandle, dwProcessId );
+
+ pThread = InternalGetCurrentThread();
+
+ if (0 == dwProcessId)
+ {
+ palError = ERROR_INVALID_PARAMETER;
+ goto OpenProcessExit;
+ }
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otProcess,
+ &oa,
+ &pobjProcess
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto OpenProcessExit;
+ }
+
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto OpenProcessExit;
+ }
+
+ pLocalData->dwProcessId = dwProcessId;
+ pDataLock->ReleaseLock(pThread, TRUE);
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pobjProcess,
+ &aotProcess,
+ dwDesiredAccess,
+ &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;
+ }
+ else
+ {
+ CAllowedObjectTypes aotProcess(otiProcess);
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hProcess,
+ &aotProcess,
+ 0,
+ &pobjProcess);
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(ERROR_INVALID_HANDLE);
+ goto exit;
+ }
+ }
+
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData));
+
+ _ASSERTE(NO_ERROR == palError);
+
+ dwProcessId = pLocalData->dwProcessId;
+ listHead = pLocalData->pProcessModules;
+ count = pLocalData->cProcessModules;
+
+ // If the module list hasn't been created yet, create it now
+ if (listHead == NULL)
+ {
+ listHead = CreateProcessModules(dwProcessId, &count);
+ if (listHead == NULL)
+ {
+ pThread->SetLastError(ERROR_INVALID_PARAMETER);
+ goto exit;
+ }
+
+ if (pLocalData != NULL)
+ {
+ pLocalData->pProcessModules = listHead;
+ pLocalData->cProcessModules = count;
+ }
+ }
+
+exit:
+ if (NULL != pDataLock)
+ {
+ pDataLock->ReleaseLock(pThread, TRUE);
+ }
+ if (NULL != pobjProcess)
+ {
+ pobjProcess->ReleaseReference(pThread);
+ }
+
+ *lpCount = count;
+ return listHead;
+}
+
+/*++
+Function:
+ CreateProcessModules
+
+Abstract
+ Returns a process's module list
+
+Return
+ ProcessModules * list
+
+--*/
+ProcessModules *
+CreateProcessModules(
+ IN DWORD dwProcessId,
+ OUT LPDWORD lpCount)
+{
+ ProcessModules *listHead = NULL;
+ _ASSERTE(lpCount != NULL);
+
+#if defined(__APPLE__)
+
+ // For OS X, the "vmmap" command outputs something similar to the /proc/*/maps file so popen the
+ // command and read the relevant lines:
+ //
+ // ...
+ // ==== regions for process 347 (non-writable and writable regions are interleaved)
+ // REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
+ // __TEXT 000000010446d000-0000000104475000 [ 32K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun
+ // __DATA 0000000104475000-0000000104476000 [ 4K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun
+ // __LINKEDIT 0000000104476000-000000010447a000 [ 16K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun
+ // Kernel Alloc Once 000000010447a000-000000010447b000 [ 4K] rw-/rwx SM=PRV
+ // MALLOC (admin) 000000010447b000-000000010447c000 [ 4K] r--/rwx SM=ZER
+ // ...
+ // MALLOC (admin) 00000001044ab000-00000001044ac000 [ 4K] r--/rwx SM=PRV
+ // __TEXT 00000001044ac000-0000000104c84000 [ 8032K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __TEXT 0000000104c84000-0000000104c85000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __TEXT 0000000104c85000-000000010513b000 [ 4824K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __TEXT 000000010513b000-000000010513c000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __TEXT 000000010513c000-000000010516f000 [ 204K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __DATA 000000010516f000-00000001051ce000 [ 380K] rw-/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __DATA 00000001051ce000-00000001051fa000 [ 176K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // __LINKEDIT 00000001051fa000-0000000105bac000 [ 9928K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
+ // VM_ALLOCATE 0000000105bac000-0000000105bad000 [ 4K] r--/rw- SM=SHM
+ // MALLOC (admin) 0000000105bad000-0000000105bae000 [ 4K] r--/rwx SM=ZER
+ // MALLOC 0000000105bae000-0000000105baf000 [ 4K] rw-/rwx SM=ZER
+ char *line = NULL;
+ size_t lineLen = 0;
+ int count = 0;
+ ssize_t read;
+
+ char vmmapCommand[100];
+ int chars = snprintf(vmmapCommand, sizeof(vmmapCommand), "/usr/bin/vmmap -interleaved %d -wide", dwProcessId);
+ _ASSERTE(chars > 0 && chars <= sizeof(vmmapCommand));
+
+ FILE *vmmapFile = popen(vmmapCommand, "r");
+ if (vmmapFile == NULL)
+ {
+ goto exit;
+ }
+
+ // Reading maps file line by line
+ while ((read = getline(&line, &lineLen, vmmapFile)) != -1)
+ {
+ void *startAddress, *endAddress;
+ char moduleName[PATH_MAX];
+ int size;
+
+ if (sscanf(line, "__TEXT %p-%p [ %dK] %*[-/rwxsp] SM=%*[A-Z] %s\n", &startAddress, &endAddress, &size, moduleName) == 4)
+ {
+ 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
+ pclose(vmmapFile);
+exit:
+
+#elif HAVE_PROCFS_MAPS
+
+ // Here we read /proc/<pid>/maps file in order to parse it and figure out what it says
+ // about a library we are looking for. This file looks something like this:
+ //
+ // [address] [perms] [offset] [dev] [inode] [pathname] - HEADER is not preset in an actual file
+ //
+ // 35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so
+ // 35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so
+ // 35b1a20000-35b1a21000 rw-p 00020000 08:02 135522 /usr/lib64/ld-2.15.so
+ // 35b1a21000-35b1a22000 rw-p 00000000 00:00 0 [heap]
+ // 35b1c00000-35b1dac000 r-xp 00000000 08:02 135870 /usr/lib64/libc-2.15.so
+ // 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so
+ // 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so
+ // 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870 /usr/lib64/libc-2.15.so
+
+ // Making something like: /proc/123/maps
+ char mapFileName[100];
+ char *line = NULL;
+ size_t lineLen = 0;
+ int count = 0;
+ ssize_t read;
+
+ INDEBUG(int chars = )
+ snprintf(mapFileName, sizeof(mapFileName), "/proc/%d/maps", dwProcessId);
+ _ASSERTE(chars > 0 && chars <= 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(line, "%p-%p %*[-rwxsp] %p %x:%x %d %s\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, 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:
+ PROCNotifyProcessShutdown
+
+ Calls the abort handler to do any shutdown cleanup. Call be called
+ from the unhandled native exception handler.
+
+(no return value)
+--*/
+__attribute__((destructor))
+void PROCNotifyProcessShutdown()
+{
+ // Call back into the coreclr to clean up the debugger transport pipes
+ PSHUTDOWN_CALLBACK callback = InterlockedExchangePointer(&g_shutdownCallback, NULL);
+ if (callback != NULL)
+ {
+ callback();
+ }
+}
+
+/*++
+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()
+{
+ PROCNotifyProcessShutdown();
+ abort();
+}
+
+/*++
+Function:
+ InitializeFlushProcessWriteBuffers
+
+Abstract
+ This function initializes data structures needed for the FlushProcessWriteBuffers
+Return
+ TRUE if it succeeded, FALSE otherwise
+--*/
+BOOL InitializeFlushProcessWriteBuffers()
+{
+ // Verify that the s_helperPage is really aligned to the VIRTUAL_PAGE_SIZE
+ _ASSERTE((((SIZE_T)s_helperPage) & (VIRTUAL_PAGE_SIZE - 1)) == 0);
+
+ // Locking the page ensures that it stays in memory during the two mprotect
+ // calls in the FlushProcessWriteBuffers below. If the page was unmapped between
+ // those calls, they would not have the expected effect of generating IPI.
+ int status = mlock(s_helperPage, VIRTUAL_PAGE_SIZE);
+
+ if (status != 0)
+ {
+ return FALSE;
+ }
+
+ status = pthread_mutex_init(&flushProcessWriteBuffersMutex, NULL);
+ if (status != 0)
+ {
+ munlock(s_helperPage, VIRTUAL_PAGE_SIZE);
+ }
+
+ return status == 0;
+}
+
+#define FATAL_ASSERT(e, msg) \
+ do \
+ { \
+ if (!(e)) \
+ { \
+ fprintf(stderr, "FATAL ERROR: " msg); \
+ PROCAbort(); \
+ } \
+ } \
+ while(0)
+
+/*++
+Function:
+ FlushProcessWriteBuffers
+
+See MSDN doc.
+--*/
+VOID
+PALAPI
+FlushProcessWriteBuffers()
+{
+ int status = pthread_mutex_lock(&flushProcessWriteBuffersMutex);
+ FATAL_ASSERT(status == 0, "Failed to lock the flushProcessWriteBuffersMutex lock");
+
+ // Changing a helper memory page protection from read / write to no access
+ // causes the OS to issue IPI to flush TLBs on all processors. This also
+ // results in flushing the processor buffers.
+ status = mprotect(s_helperPage, VIRTUAL_PAGE_SIZE, PROT_READ | PROT_WRITE);
+ FATAL_ASSERT(status == 0, "Failed to change helper page protection to read / write");
+
+ // Ensure that the page is dirty before we change the protection so that
+ // we prevent the OS from skipping the global TLB flush.
+ InterlockedIncrement(s_helperPage);
+
+ status = mprotect(s_helperPage, VIRTUAL_PAGE_SIZE, PROT_NONE);
+ FATAL_ASSERT(status == 0, "Failed to change helper page protection to no access");
+
+ status = pthread_mutex_unlock(&flushProcessWriteBuffersMutex);
+ FATAL_ASSERT(status == 0, "Failed to unlock the flushProcessWriteBuffersMutex lock");
+}
+
+/*++
+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,
+ 0,
+ &pobjProcess
+ );
+
+ if (NO_ERROR == palError)
+ {
+ IDataLock *pDataLock;
+ CProcProcessLocalData *pLocalData;
+
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR == palError)
+ {
+ dwProcessId = pLocalData->dwProcessId;
+ pDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ pobjProcess->ReleaseReference(pThread);
+ }
+
+PROCGetProcessIDFromHandleExit:
+
+ return dwProcessId;
+}
+
+PAL_ERROR
+CorUnix::InitializeProcessData(
+ void
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ bool fLockInitialized = FALSE;
+
+ pGThreadList = NULL;
+ g_dwThreadCount = 0;
+
+ InternalInitializeCriticalSection(&g_csProcess);
+ fLockInitialized = TRUE;
+
+ if (NO_ERROR != palError)
+ {
+ if (fLockInitialized)
+ {
+ InternalDeleteCriticalSection(&g_csProcess);
+ }
+ }
+
+ return palError;
+}
+
+/*++
+Function
+ InitializeProcessCommandLine
+
+Abstract
+ Initializes (or re-initializes) the saved command line and exe path.
+
+Parameter
+ lpwstrCmdLine
+ lpwstrFullPath
+
+Return
+ PAL_ERROR
+
+Notes
+ This function takes ownership of lpwstrCmdLine, but not of lpwstrFullPath
+--*/
+
+PAL_ERROR
+CorUnix::InitializeProcessCommandLine(
+ LPWSTR lpwstrCmdLine,
+ LPWSTR lpwstrFullPath
+)
+{
+ PAL_ERROR palError = NO_ERROR;
+ LPWSTR initial_dir = NULL;
+
+ //
+ // Save the command line and initial directory
+ //
+
+ if (lpwstrFullPath)
+ {
+ LPWSTR lpwstr = PAL_wcsrchr(lpwstrFullPath, '/');
+ lpwstr[0] = '\0';
+ INT n = lstrlenW(lpwstrFullPath) + 1;
+
+ int iLen = n;
+ initial_dir = reinterpret_cast<LPWSTR>(InternalMalloc(iLen*sizeof(WCHAR)));
+ if (NULL == initial_dir)
+ {
+ ERROR("malloc() failed! (initial_dir) \n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto exit;
+ }
+
+ if (wcscpy_s(initial_dir, iLen, lpwstrFullPath) != SAFECRT_SUCCESS)
+ {
+ ERROR("wcscpy_s failed!\n");
+ free(initial_dir);
+ palError = ERROR_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ lpwstr[0] = '/';
+
+ free(g_lpwstrAppDir);
+ g_lpwstrAppDir = initial_dir;
+ }
+
+ free(g_lpwstrCmdLine);
+ g_lpwstrCmdLine = lpwstrCmdLine;
+
+exit:
+ return palError;
+}
+
+
+/*++
+Function:
+ CreateInitialProcessAndThreadObjects
+
+Abstract
+ Creates the IPalObjects that represent the current process
+ and the initial thread
+
+Parameter
+ pThread - the initial thread
+
+Return
+ PAL_ERROR
+--*/
+
+PAL_ERROR
+CorUnix::CreateInitialProcessAndThreadObjects(
+ CPalThread *pThread
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ HANDLE hThread;
+ IPalObject *pobjProcess = NULL;
+ IDataLock *pDataLock;
+ CProcProcessLocalData *pLocalData;
+ CObjectAttributes oa;
+ HANDLE hProcess;
+
+ //
+ // Create initial thread object
+ //
+
+ palError = CreateThreadObject(pThread, pThread, &hThread);
+ if (NO_ERROR != palError)
+ {
+ goto CreateInitialProcessAndThreadObjectsExit;
+ }
+
+ //
+ // This handle isn't needed
+ //
+
+ (void) g_pObjectManager->RevokeHandle(pThread, hThread);
+
+ //
+ // Create and initialize process object
+ //
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otProcess,
+ &oa,
+ &pobjProcess
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to allocate process object");
+ goto CreateInitialProcessAndThreadObjectsExit;
+ }
+
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to access local data");
+ goto CreateInitialProcessAndThreadObjectsExit;
+ }
+
+ pLocalData->dwProcessId = gPID;
+ pLocalData->ps = PS_RUNNING;
+ pDataLock->ReleaseLock(pThread, TRUE);
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pobjProcess,
+ &aotProcess,
+ PROCESS_ALL_ACCESS,
+ &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:
+ PROCGetNumberOfThreads
+
+Abstract
+ Return the number of threads in the thread list.
+
+Parameter
+ void
+
+Return
+ the number of threads.
+--*/
+INT
+CorUnix::PROCGetNumberOfThreads(
+ void)
+{
+ return g_dwThreadCount;
+}
+
+
+/*++
+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);
+}
+
+#if USE_SYSV_SEMAPHORES
+/*++
+Function:
+ PROCCleanupThreadSemIds
+
+Abstract
+ Cleanup SysV semaphore ids for all threads
+
+(no parameters, no return value)
+--*/
+VOID
+PROCCleanupThreadSemIds(void)
+{
+ //
+ // When using SysV semaphores, the semaphore ids used by PAL threads must be removed
+ // so they can be used again.
+ //
+
+ PROCProcessLock();
+
+ CPalThread *pTargetThread = pGThreadList;
+ while (NULL != pTargetThread)
+ {
+ pTargetThread->suspensionInfo.DestroySemaphoreIds();
+ pTargetThread = pTargetThread->GetNext();
+ }
+
+ PROCProcessUnlock();
+
+}
+#endif // USE_SYSV_SEMAPHORES
+
+/*++
+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())
+ {
+ PROCNotifyProcessShutdown();
+ PALCommonCleanup();
+ }
+}
+
+/*++
+Function:
+ PROCGetProcessStatus
+
+Abstract:
+ Retrieve process state information (state & exit code).
+
+Parameters:
+ DWORD process_id : PID of process to retrieve state for
+ PROCESS_STATE *state : state of process (starting, running, done)
+ DWORD *exit_code : exit code of process (from ExitProcess, etc.)
+
+Return value :
+ TRUE on success
+--*/
+PAL_ERROR
+PROCGetProcessStatus(
+ CPalThread *pThread,
+ HANDLE hProcess,
+ PROCESS_STATE *pps,
+ DWORD *pdwExitCode
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjProcess = NULL;
+ IDataLock *pDataLock;
+ CProcProcessLocalData *pLocalData;
+ pid_t wait_retval;
+ int status;
+
+ //
+ // First, check if we already know the status of this process. This will be
+ // the case if this function has already been called for the same process.
+ //
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hProcess,
+ &aotProcess,
+ 0,
+ &pobjProcess
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto PROCGetProcessStatusExit;
+ }
+
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (PS_DONE == pLocalData->ps)
+ {
+ TRACE("We already called waitpid() on process ID %#x; process has "
+ "terminated, exit code is %d\n",
+ pLocalData->dwProcessId, pLocalData->dwExitCode);
+
+ *pps = pLocalData->ps;
+ *pdwExitCode = pLocalData->dwExitCode;
+
+ pDataLock->ReleaseLock(pThread, FALSE);
+
+ goto PROCGetProcessStatusExit;
+ }
+
+ /* By using waitpid(), we can even retrieve the exit code of a non-PAL
+ process. However, note that waitpid() can only provide the low 8 bits
+ of the exit code. This is all that is required for the PAL spec. */
+ TRACE("Looking for status of process; trying wait()");
+
+ while(1)
+ {
+ /* try to get state of process, using non-blocking call */
+ wait_retval = waitpid(pLocalData->dwProcessId, &status, WNOHANG);
+
+ if ( wait_retval == (pid_t) pLocalData->dwProcessId )
+ {
+ /* success; get the exit code */
+ if ( WIFEXITED( status ) )
+ {
+ *pdwExitCode = WEXITSTATUS(status);
+ TRACE("Exit code was %d\n", *pdwExitCode);
+ }
+ else
+ {
+ WARN("process terminated without exiting; can't get exit "
+ "code. faking it.\n");
+ *pdwExitCode = EXIT_FAILURE;
+ }
+ *pps = PS_DONE;
+ }
+ else if (0 == wait_retval)
+ {
+ // The process is still running.
+ TRACE("Process %#x is still active.\n", pLocalData->dwProcessId);
+ *pps = PS_RUNNING;
+ *pdwExitCode = 0;
+ }
+ else if (-1 == wait_retval)
+ {
+ // This might happen if waitpid() had already been called, but
+ // this shouldn't happen - we call waitpid once, store the
+ // result, and use that afterwards.
+ // One 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");
+ continue;
+ }
+ else if (ECHILD == errno)
+ {
+ TRACE("waitpid() failed with ECHILD; calling kill instead");
+ if (kill(pLocalData->dwProcessId, 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");
+ *pdwExitCode = 0;
+ }
+ else
+ {
+ ERROR("kill(pid, 0) failed; errno is %d (%s)\n",
+ errno, strerror(errno));
+ *pdwExitCode = EXIT_FAILURE;
+ }
+ *pps = PS_DONE;
+ }
+ else
+ {
+ *pps = PS_RUNNING;
+ *pdwExitCode = 0;
+ }
+ }
+ else
+ {
+ // Ignoring unexpected waitpid errno and assuming that
+ // the process is still running
+ ERROR("waitpid(pid=%u) failed with unexpected errno=%d (%s)\n",
+ pLocalData->dwProcessId, errno, strerror(errno));
+ *pps = PS_RUNNING;
+ *pdwExitCode = 0;
+ }
+ }
+ else
+ {
+ ASSERT("waitpid returned unexpected value %d\n",wait_retval);
+ *pdwExitCode = EXIT_FAILURE;
+ *pps = PS_DONE;
+ }
+ // Break out of the loop in all cases except EINTR.
+ break;
+ }
+
+ // Save the exit code for future reference (waitpid will only work once).
+ if(PS_DONE == *pps)
+ {
+ pLocalData->ps = PS_DONE;
+ pLocalData->dwExitCode = *pdwExitCode;
+ }
+
+ TRACE( "State of process 0x%08x : %d (exit code %d)\n",
+ pLocalData->dwProcessId, *pps, *pdwExitCode );
+
+ pDataLock->ReleaseLock(pThread, TRUE);
+
+PROCGetProcessStatusExit:
+
+ if (NULL != pobjProcess)
+ {
+ pobjProcess->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+#ifdef _DEBUG
+void PROCDumpThreadList()
+{
+ CPalThread *pThread;
+
+ PROCProcessLock();
+
+ TRACE ("Threads:{\n");
+
+ pThread = pGThreadList;
+ while (NULL != pThread)
+ {
+ TRACE (" {pThr=0x%p tid=%#x lwpid=%#x state=%d finsusp=%d}\n",
+ pThread, (int)pThread->GetThreadId(), (int)pThread->GetLwpId(),
+ (int)pThread->synchronizationInfo.GetThreadState(),
+ (int)pThread->suspensionInfo.GetSuspendedForShutdown());
+
+ pThread = pThread->GetNext();
+ }
+ TRACE ("Threads:}\n");
+
+ PROCProcessUnlock();
+}
+#endif
+
+/* 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;
+}
+
+/*++
+Functions: VAL16 & VAL32
+ Byte swapping functions for reading in little endian format files
+--*/
+#ifdef BIGENDIAN
+
+static inline USHORT VAL16(USHORT x)
+{
+ return ( ((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8) );
+}
+static inline ULONG VAL32(DWORD x)
+{
+ return( ((x & 0xFF000000L) >> 24) |
+ ((x & 0x00FF0000L) >> 8) |
+ ((x & 0x0000FF00L) << 8) |
+ ((x & 0x000000FFL) << 24) );
+}
+#else // BIGENDIAN
+// For little-endian machines, do nothing
+static __inline USHORT VAL16(unsigned short x) { return x; }
+static __inline DWORD VAL32(DWORD x){ return x; }
+#endif // BIGENDIAN
+
+static const DWORD IMAGE_DOS_SIGNATURE = 0x5A4D;
+static const DWORD IMAGE_NT_SIGNATURE = 0x00004550;
+static const DWORD IMAGE_SIZEOF_NT_OPTIONAL32_HEADER = 224;
+static const DWORD IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
+static const DWORD IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14;
+
+typedef struct _IMAGE_DATA_DIRECTORY {
+ DWORD VirtualAddress;
+ DWORD Size;
+} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
+
+typedef struct _IMAGE_OPTIONAL_HEADER {
+ //
+ // Standard fields.
+ //
+
+ WORD Magic;
+ BYTE MajorLinkerVersion;
+ BYTE MinorLinkerVersion;
+ DWORD SizeOfCode;
+ DWORD SizeOfInitializedData;
+ DWORD SizeOfUninitializedData;
+ DWORD AddressOfEntryPoint;
+ DWORD BaseOfCode;
+ DWORD BaseOfData;
+
+ //
+ // NT additional fields.
+ //
+
+ DWORD ImageBase;
+ DWORD SectionAlignment;
+ DWORD FileAlignment;
+ WORD MajorOperatingSystemVersion;
+ WORD MinorOperatingSystemVersion;
+ WORD MajorImageVersion;
+ WORD MinorImageVersion;
+ WORD MajorSubsystemVersion;
+ WORD MinorSubsystemVersion;
+ DWORD Win32VersionValue;
+ DWORD SizeOfImage;
+ DWORD SizeOfHeaders;
+ DWORD CheckSum;
+ WORD Subsystem;
+ WORD DllCharacteristics;
+ DWORD SizeOfStackReserve;
+ DWORD SizeOfStackCommit;
+ DWORD SizeOfHeapReserve;
+ DWORD SizeOfHeapCommit;
+ DWORD LoaderFlags;
+ DWORD NumberOfRvaAndSizes;
+ IMAGE_DATA_DIRECTORY DataDirectory[16];
+} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
+
+typedef struct _IMAGE_FILE_HEADER {
+ WORD Machine;
+ WORD NumberOfSections;
+ DWORD TimeDateStamp;
+ DWORD PointerToSymbolTable;
+ DWORD NumberOfSymbols;
+ WORD SizeOfOptionalHeader;
+ WORD Characteristics;
+} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
+
+typedef struct _IMAGE_NT_HEADERS {
+ DWORD Signature;
+ IMAGE_FILE_HEADER FileHeader;
+ IMAGE_OPTIONAL_HEADER32 OptionalHeader;
+} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
+
+typedef struct _IMAGE_DOS_HEADER { /* DOS .EXE header*/
+ WORD e_magic; /* Magic number*/
+ WORD e_cblp; /* Bytes on last page of file*/
+ WORD e_cp; /* Pages in file*/
+ WORD e_crlc; /* Relocations*/
+ WORD e_cparhdr; /* Size of header in paragraphs*/
+ WORD e_minalloc; /* Minimum extra paragraphs needed*/
+ WORD e_maxalloc; /* Maximum extra paragraphs needed*/
+ WORD e_ss; /* Initial (relative) SS value*/
+ WORD e_sp; /* Initial SP value*/
+ WORD e_csum; /* Checksum*/
+ WORD e_ip; /* Initial IP value*/
+ WORD e_cs; /* Initial (relative) CS value*/
+ WORD e_lfarlc; /* File address of relocation table*/
+ WORD e_ovno; /* Overlay number*/
+ WORD e_res[4]; /* Reserved words*/
+ WORD e_oemid; /* OEM identifier (for e_oeminfo)*/
+ WORD e_oeminfo; /* OEM information; e_oemid specific*/
+ WORD e_res2[10]; /* Reserved words*/
+ LONG e_lfanew; /* File address of new exe header*/
+ } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
+
+
+/*++
+Function:
+ isManagedExecutable
+
+Determines if the passed in file is a managed executable
+
+--*/
+static
+int
+isManagedExecutable(LPCSTR lpFileName)
+{
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ DWORD cbRead;
+ IMAGE_DOS_HEADER dosheader;
+ IMAGE_NT_HEADERS32 NtHeaders;
+ BOOL ret = 0;
+
+ /* then check if it is a PE/COFF file */
+ if((hFile = CreateFileA(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL)) == INVALID_HANDLE_VALUE)
+ {
+ goto isManagedExecutableExit;
+ }
+
+ /* Open the file and read the IMAGE_DOS_HEADER structure */
+ if(!ReadFile(hFile, &dosheader, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL) || cbRead != sizeof(IMAGE_DOS_HEADER) )
+ goto isManagedExecutableExit;
+
+ /* check the DOS headers */
+ if ( (dosheader.e_magic != VAL16(IMAGE_DOS_SIGNATURE)) || (VAL32(dosheader.e_lfanew) <= 0) )
+ goto isManagedExecutableExit;
+
+ /* Advance the file pointer to File address of new exe header */
+ if( SetFilePointer(hFile, VAL32(dosheader.e_lfanew), NULL, FILE_BEGIN) == 0xffffffff)
+ goto isManagedExecutableExit;
+
+ if( !ReadFile(hFile, &NtHeaders , sizeof(IMAGE_NT_HEADERS32), &cbRead, NULL) || cbRead != sizeof(IMAGE_NT_HEADERS32) )
+ goto isManagedExecutableExit;
+
+ /* check the NT headers */
+ if ((NtHeaders.Signature != VAL32(IMAGE_NT_SIGNATURE)) ||
+ (NtHeaders.FileHeader.SizeOfOptionalHeader != VAL16(IMAGE_SIZEOF_NT_OPTIONAL32_HEADER)) ||
+ (NtHeaders.OptionalHeader.Magic != VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)))
+ goto isManagedExecutableExit;
+
+ /* Check that the virtual address of IMAGE_DIRECTORY_ENTRY_COMHEADER is non-null */
+ if ( NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress == 0 )
+ goto isManagedExecutableExit;
+
+ /* The file is a managed executable */
+ ret = 1;
+
+ isManagedExecutableExit:
+ /* Close the file handle if we opened it */
+ if ( hFile != INVALID_HANDLE_VALUE )
+ CloseHandle(hFile);
+
+ return ret;
+}
+
+/*++
+Function:
+ checkFileType
+
+Abstract:
+ Return the type of the file.
+
+Parameters:
+ IN lpFileName: file name
+
+Return:
+ FILE_DIR: Directory
+ FILE_UNIX: Unix executable file
+ FILE_PE: managed PE/COFF 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( isManagedExecutable(lpFileName) )
+ {
+ return FILE_PE;
+ }
+
+ /* 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
+ IN prependLoader: If True first argument should be the PE loader
+
+Return:
+ the number of arguments
+
+note: this doesn't yet match precisely the behavior of Windows, but should be
+sufficient.
+what's here:
+1) stripping nonquoted whitespace
+2) handling of quoted parameters and quoted "parts" of parameters, removal of
+ doublequotes (<aaaa"b bbb b"ccc> becomes <aaaab bbb bccc>)
+3) \" as an escaped doublequote, both within doublequoted sequences and out
+what's known missing :
+1) \\ as an escaped backslash, but only if the string of '\'
+ is followed by a " (escaped or not)
+2) "alternate" escape sequence : double-doublequote within a double-quoted
+ argument (<"aaa a""aa aaa">) expands to a single-doublequote(<aaa a"aa aaa>)
+note that there may be other special cases
+--*/
+static
+char **
+buildArgv(
+ LPCWSTR lpCommandLine,
+ PathCharString& lpAppPath,
+ UINT *pnArg,
+ BOOL prependLoader)
+{
+ 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 + strlen(PROCESS_PELOADER_FILENAME) + lpAppPath.GetCount() + 2);
+ lpAsciiCmdLine = (char *) InternalMalloc(iLength);
+
+ if (lpAsciiCmdLine == NULL)
+ {
+ ERROR("Unable to allocate memory\n");
+ return NULL;
+ }
+
+ pChar = lpAsciiCmdLine;
+
+ /* Prepend the PE loader, if it's required */
+ if (prependLoader)
+ {
+ if ((strcpy_s(lpAsciiCmdLine, iLength, PROCESS_PELOADER_FILENAME) != SAFECRT_SUCCESS) ||
+ (strcat_s(lpAsciiCmdLine, iLength, " ") != SAFECRT_SUCCESS))
+ {
+ ERROR("strcpy_s/strcat_s failed!\n");
+ return NULL;
+ }
+
+ pChar = lpAsciiCmdLine + strlen (lpAsciiCmdLine);
+
+ }
+ else
+ {
+ /* 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 0000000000..bd35b69fe2
--- /dev/null
+++ b/src/pal/src/thread/procprivate.hpp
@@ -0,0 +1,78 @@
+// 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.
+
+/*++
+
+
+
+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
new file mode 100644
index 0000000000..566ef855b4
--- /dev/null
+++ b/src/pal/src/thread/thread.cpp
@@ -0,0 +1,2871 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ thread.cpp
+
+Abstract:
+
+ Thread object and core APIs
+
+
+
+--*/
+
+#include "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do this first
+
+#include "pal/corunix.hpp"
+#include "pal/context.h"
+#include "pal/thread.hpp"
+#include "pal/mutex.hpp"
+#include "pal/handlemgr.hpp"
+#include "pal/cs.hpp"
+#include "pal/seh.hpp"
+
+#include "procprivate.hpp"
+#include "pal/process.h"
+#include "pal/module.h"
+#include "pal/environ.h"
+#include "pal/init.h"
+
+#if defined(__NetBSD__) && !HAVE_PTHREAD_GETCPUCLOCKID
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <kvm.h>
+#endif
+
+#include <signal.h>
+#include <pthread.h>
+#if HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#if HAVE_MACH_THREADS
+#include <mach/mach.h>
+#endif // HAVE_MACH_THREADS
+#if HAVE_POLL
+#include <poll.h>
+#else
+#include "pal/fakepoll.h"
+#endif // HAVE_POLL
+#include <limits.h>
+
+#if HAVE_SYS_LWP_H
+#include <sys/lwp.h>
+#endif
+#if HAVE_LWP_H
+#include <lwp.h>
+#endif
+// If we don't have sys/lwp.h but do expect to use _lwp_self, declare it to silence compiler warnings
+#if HAVE__LWP_SELF && !HAVE_SYS_LWP_H && !HAVE_LWP_H
+extern "C" int _lwp_self ();
+#endif
+
+using namespace CorUnix;
+
+
+/* ------------------- Definitions ------------------------------*/
+
+// The default stack size of a newly created thread (currently 256KB)
+// when the dwStackSize parameter of PAL_CreateThread()
+// is zero. This value can be set by setting the
+// environment variable PAL_THREAD_DEFAULT_STACK_SIZE
+// (the value should be in bytes and in hex).
+DWORD CPalThread::s_dwDefaultThreadStackSize = 256*1024;
+
+/* list of free CPalThread objects */
+static Volatile<CPalThread*> free_threads_list = NULL;
+
+/* lock to access list of free THREAD structures */
+/* NOTE: can't use a CRITICAL_SECTION here (see comment in FreeTHREAD) */
+int free_threads_spinlock = 0;
+
+/* lock to access iEndingThreads counter, condition variable to signal shutdown
+thread when any remaining threads have died, and count of exiting threads that
+can't be suspended. */
+pthread_mutex_t ptmEndThread;
+pthread_cond_t ptcEndThread;
+static int iEndingThreads = 0;
+
+// Activation function that gets called when an activation is injected into a thread.
+PAL_ActivationFunction g_activationFunction = NULL;
+// Function to check if an activation can be safely injected at a specified context
+PAL_SafeActivationCheckFunction g_safeActivationCheckFunction = NULL;
+
+void
+ThreadCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup,
+ bool fShutdown,
+ bool fCleanupSharedState
+ );
+
+PAL_ERROR
+ThreadInitializationRoutine(
+ CPalThread *pThread,
+ CObjectType *pObjectType,
+ void *pImmutableData,
+ void *pSharedData,
+ void *pProcessLocalData
+ );
+
+void
+IncrementEndingThreadCount(
+ void
+ );
+
+void
+DecrementEndingThreadCount(
+ void
+ );
+
+CObjectType CorUnix::otThread(
+ otiThread,
+ ThreadCleanupRoutine,
+ ThreadInitializationRoutine,
+ 0, //sizeof(CThreadImmutableData),
+ sizeof(CThreadProcessLocalData),
+ 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
+
+ Destructor for the thread-specific data representing the current PAL thread.
+ Called from pthread_exit. (pthread_exit is not called from the thread on which
+ main() was first invoked. This is not a problem, though, since when main()
+ returns, this results in an implicit call to exit().)
+
+ arg: the PAL thread
+*/
+static void InternalEndCurrentThreadWrapper(void *arg)
+{
+ CPalThread *pThread = (CPalThread *) arg;
+
+ // When pthread_exit calls us, it has already removed the PAL thread
+ // from TLS. Since InternalEndCurrentThread calls functions that assert
+ // that the current thread is known to this PAL, and that pThread
+ // actually is the current PAL thread, put it back in TLS temporarily.
+ pthread_setspecific(thObjKey, pThread);
+ (void)PAL_Enter(PAL_BoundaryTop);
+
+ /* Call entry point functions of every attached modules to
+ indicate the thread is exiting */
+ /* note : no need to enter a critical section for serialization, the loader
+ will lock its own critical section */
+ LOADCallDllMain(DLL_THREAD_DETACH, NULL);
+
+ // PAL_Leave will be called just before we release the thread reference
+ // in InternalEndCurrentThread.
+ InternalEndCurrentThread(pThread);
+ pthread_setspecific(thObjKey, NULL);
+}
+
+/*++
+Function:
+ TLSInitialize
+
+ Initialize the TLS subsystem
+--*/
+BOOL TLSInitialize()
+{
+ /* Create the pthread key for thread objects, which we use
+ for fast access to the current thread object. */
+ if (pthread_key_create(&thObjKey, InternalEndCurrentThreadWrapper))
+ {
+ ERROR("Couldn't create the thread object key\n");
+ return FALSE;
+ }
+
+ SPINLOCKInit(&free_threads_spinlock);
+
+ return TRUE;
+}
+
+/*++
+Function:
+ TLSCleanup
+
+ Shutdown the TLS subsystem
+--*/
+VOID TLSCleanup()
+{
+ SPINLOCKDestroy(&free_threads_spinlock);
+
+ pthread_key_delete(thObjKey);
+}
+
+/*++
+Function:
+ AllocTHREAD
+
+Abstract:
+ Allocate CPalThread instance
+
+Return:
+ The fresh thread structure, NULL otherwise
+--*/
+CPalThread* AllocTHREAD()
+{
+ CPalThread* pThread = NULL;
+
+ /* Get the lock */
+ SPINLOCKAcquire(&free_threads_spinlock, 0);
+
+ pThread = free_threads_list;
+ if (pThread != NULL)
+ {
+ free_threads_list = pThread->GetNext();
+ }
+
+ /* Release the lock */
+ SPINLOCKRelease(&free_threads_spinlock);
+
+ if (pThread == NULL)
+ {
+ pThread = InternalNew<CPalThread>();
+ }
+ else
+ {
+ pThread = new (pThread) CPalThread;
+ }
+
+ return pThread;
+}
+
+/*++
+Function:
+ FreeTHREAD
+
+Abstract:
+ Free THREAD structure
+
+--*/
+static void FreeTHREAD(CPalThread *pThread)
+{
+ //
+ // Run the destructors for this object
+ //
+
+ pThread->~CPalThread();
+
+#ifdef _DEBUG
+ // Fill value so we can find code re-using threads after they're dead. We
+ // check against pThread->dwGuard when getting the current thread's data.
+ memset((void*)pThread, 0xcc, sizeof(*pThread));
+#endif
+
+ // We SHOULD be doing the following, but it causes massive problems. See the
+ // comment below.
+ //pthread_setspecific(thObjKey, NULL); // Make sure any TLS entry is removed.
+
+ //
+ // Never actually free the THREAD structure to make the TLS lookaside cache work.
+ // THREAD* for terminated thread can be stuck in the lookaside cache code for an
+ // arbitrary amount of time. The unused THREAD* structures has to remain in a
+ // valid memory and thus can't be returned to the heap.
+ //
+ // TODO: is this really true? Why would the entry remain in the cache for
+ // an indefinite period of time after we've flushed it?
+ //
+
+ /* NOTE: can't use a CRITICAL_SECTION here: EnterCriticalSection(&cs,TRUE) and
+ LeaveCriticalSection(&cs,TRUE) need to access the thread private data
+ stored in the very THREAD structure that we just destroyed. Entering and
+ leaving the critical section with internal==FALSE leads to possible hangs
+ in the PROCSuspendOtherThreads logic, at shutdown time
+
+ Update: [TODO] PROCSuspendOtherThreads has been removed. Can this
+ code be changed?*/
+
+ /* Get the lock */
+ SPINLOCKAcquire(&free_threads_spinlock, 0);
+
+ pThread->SetNext(free_threads_list);
+ free_threads_list = pThread;
+
+ /* Release the lock */
+ SPINLOCKRelease(&free_threads_spinlock);
+}
+
+
+/*++
+Function:
+ THREADGetThreadProcessId
+
+returns the process owner ID of the indicated hThread
+--*/
+DWORD
+THREADGetThreadProcessId(
+ HANDLE hThread
+ // UNIXTODO Should take pThread parameter here (modify callers)
+ )
+{
+ CPalThread *pThread;
+ CPalThread *pTargetThread;
+ IPalObject *pobjThread = NULL;
+ PAL_ERROR palError = NO_ERROR;
+
+ DWORD dwProcessId = 0;
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalGetThreadDataFromHandle(
+ pThread,
+ hThread,
+ 0,
+ &pTargetThread,
+ &pobjThread
+ );
+
+ if (NO_ERROR != palError)
+ {
+ if (!pThread->IsDummy())
+ {
+ dwProcessId = GetCurrentProcessId();
+ }
+ else
+ {
+ ASSERT("Dummy thread passed to THREADGetProcessId\n");
+ }
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pThread);
+ }
+ }
+ else
+ {
+ ERROR("Couldn't retreive the hThread:%p pid owner !\n", hThread);
+ }
+
+
+ return dwProcessId;
+}
+
+/*++
+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;
+}
+
+
+
+/*++
+Function:
+ GetCurrentThread
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+PAL_GetCurrentThread(
+ VOID)
+{
+ PERF_ENTRY(GetCurrentThread);
+ ENTRY("GetCurrentThread()\n");
+
+ LOGEXIT("GetCurrentThread returns HANDLE %p\n", hPseudoCurrentThread);
+ PERF_EXIT(GetCurrentThread);
+
+ /* return a pseudo handle */
+ return (HANDLE) hPseudoCurrentThread;
+}
+
+/*++
+Function:
+ SwitchToThread
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+SwitchToThread(
+ VOID)
+{
+ BOOL ret;
+
+ PERF_ENTRY(SwitchToThread);
+ ENTRY("SwitchToThread(VOID)\n");
+
+ /* sched_yield yields to another thread in the current process. This implementation
+ won't work well for cross-process synchronization. */
+ ret = (sched_yield() == 0);
+
+ LOGEXIT("SwitchToThread returns BOOL %d\n", ret);
+ PERF_EXIT(SwitchToThread);
+
+ return ret;
+}
+
+/*++
+Function:
+ CreateThread
+
+Note:
+ lpThreadAttributes could be ignored.
+
+See MSDN doc.
+
+--*/
+HANDLE
+PALAPI
+CreateThread(
+ IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ IN DWORD dwStackSize,
+ IN LPTHREAD_START_ROUTINE lpStartAddress,
+ IN LPVOID lpParameter,
+ IN DWORD dwCreationFlags,
+ OUT LPDWORD lpThreadId)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+ HANDLE hNewThread = NULL;
+
+ PERF_ENTRY(CreateThread);
+ ENTRY("CreateThread(lpThreadAttr=%p, dwStackSize=%u, lpStartAddress=%p, "
+ "lpParameter=%p, dwFlags=%#x, lpThreadId=%#x)\n",
+ lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter,
+ dwCreationFlags, lpThreadId);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalCreateThread(
+ pThread,
+ lpThreadAttributes,
+ dwStackSize,
+ lpStartAddress,
+ lpParameter,
+ dwCreationFlags,
+ UserCreatedThread,
+ lpThreadId,
+ &hNewThread
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("CreateThread returns HANDLE %p\n", hNewThread);
+ PERF_EXIT(CreateThread);
+
+ return hNewThread;
+}
+
+PAL_ERROR
+CorUnix::InternalCreateThread(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ DWORD dwStackSize,
+ LPTHREAD_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter,
+ DWORD dwCreationFlags,
+ PalThreadType eThreadType,
+ LPDWORD lpThreadId,
+ 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;
+ size_t pthreadStackSize;
+#if PTHREAD_CREATE_MODIFIES_ERRNO
+ int storedErrno;
+#endif // PTHREAD_CREATE_MODIFIES_ERRNO
+ BOOL fHoldingProcessLock = FALSE;
+ int iError = 0;
+
+ if (0 != terminator)
+ {
+ //
+ // Since the PAL is in the middle of shutting down we don't want to
+ // create any new threads (since it's possible for that new thread
+ // to create another thread before the shutdown thread gets around
+ // to suspending it, and so on). We don't want to return an error
+ // here, though, as some programs (in particular, build) do not
+ // handle CreateThread errors properly -- instead, we just put
+ // the calling thread to sleep (unless it is the shutdown thread,
+ // which could occur if a DllMain PROCESS_DETACH handler tried to
+ // create a new thread for some odd reason).
+ //
+
+ ERROR("process is terminating, can't create new thread.\n");
+
+ if (pThread->GetThreadId() != static_cast<DWORD>(terminator))
+ {
+ while (true)
+ {
+ poll(NULL, 0, INFTIM);
+ sched_yield();
+ }
+ }
+ else
+ {
+ //
+ // This is the shutdown thread, so just return an error
+ //
+
+ palError = ERROR_PROCESS_ABORTED;
+ goto EXIT;
+ }
+ }
+
+ /* Validate parameters */
+
+ if (lpThreadAttributes != NULL)
+ {
+ ASSERT("lpThreadAttributes parameter must be NULL (%p)\n",
+ lpThreadAttributes);
+ palError = ERROR_INVALID_PARAMETER;
+ goto EXIT;
+ }
+
+ // 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;
+
+ /* adjust the stack size if necessary */
+ if (0 != pthread_attr_getstacksize(&pthreadAttr, &pthreadStackSize))
+ {
+ ERROR("couldn't set thread stack size\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+
+ TRACE("default pthread stack size is %d, caller requested %d (default is %d)\n",
+ pthreadStackSize, dwStackSize, CPalThread::s_dwDefaultThreadStackSize);
+
+ if (0 == dwStackSize)
+ {
+ dwStackSize = CPalThread::s_dwDefaultThreadStackSize;
+ }
+
+#ifdef PTHREAD_STACK_MIN
+ if (PTHREAD_STACK_MIN > pthreadStackSize)
+ {
+ WARN("default stack size is reported as %d, but PTHREAD_STACK_MIN is "
+ "%d\n", pthreadStackSize, PTHREAD_STACK_MIN);
+ }
+#endif
+
+ if (pthreadStackSize < dwStackSize)
+ {
+ TRACE("setting thread stack size to %d\n", dwStackSize);
+ if (0 != pthread_attr_setstacksize(&pthreadAttr, dwStackSize))
+ {
+ ERROR("couldn't set pthread stack size to %d\n", dwStackSize);
+ palError = ERROR_INTERNAL_ERROR;
+ goto EXIT;
+ }
+ }
+ else
+ {
+ TRACE("using the system default thread stack size of %d\n", pthreadStackSize);
+ }
+
+#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
+
+#ifdef FEATURE_PAL_SXS
+ _ASSERT_MSG(pNewThread->IsInPal(), "New threads we're about to spawn should always be in the PAL.\n");
+#endif // FEATURE_PAL_SXS
+ 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 != lpThreadId)
+ {
+ *lpThreadId = 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:
+ ExitThread
+
+See MSDN doc.
+--*/
+PAL_NORETURN
+VOID
+PALAPI
+ExitThread(
+ IN DWORD dwExitCode)
+{
+ CPalThread *pThread;
+
+ ENTRY("ExitThread(dwExitCode=%u)\n", dwExitCode);
+ PERF_ENTRY_ONLY(ExitThread);
+
+ pThread = InternalGetCurrentThread();
+
+ /* store the exit code */
+ pThread->SetExitCode(dwExitCode);
+
+ /* pthread_exit runs TLS destructors and cleanup routines,
+ possibly registered by foreign code. The right thing
+ to do here is to leave the PAL. Our own TLS destructor
+ re-enters us explicitly. */
+ PAL_Leave(PAL_BoundaryTop);
+
+ /* kill the thread (itself), resulting in a call to InternalEndCurrentThread */
+ pthread_exit(NULL);
+
+ ASSERT("pthread_exit should not return!\n");
+ for (;;);
+}
+
+/*++
+Function:
+ GetExitCodeThread
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetExitCodeThread(
+ IN HANDLE hThread,
+ IN LPDWORD lpExitCode)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthrCurrent = NULL;
+ CPalThread *pthrTarget = NULL;
+ IPalObject *pobjThread = NULL;
+ BOOL fExitCodeSet;
+
+ PERF_ENTRY(GetExitCodeThread);
+ ENTRY("GetExitCodeThread(hThread = %p, lpExitCode = %p)\n",
+ hThread, lpExitCode);
+
+ if (NULL == lpExitCode)
+ {
+ WARN("Got NULL lpExitCode\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ pthrCurrent = InternalGetCurrentThread();
+ palError = InternalGetThreadDataFromHandle(
+ pthrCurrent,
+ hThread,
+ 0,
+ &pthrTarget,
+ &pobjThread
+ );
+
+ pthrTarget->Lock(pthrCurrent);
+
+ fExitCodeSet = pthrTarget->GetExitCode(lpExitCode);
+ if (!fExitCodeSet)
+ {
+ if (TS_DONE == pthrTarget->synchronizationInfo.GetThreadState())
+ {
+#ifdef FEATURE_PAL_SXS
+ // The thread exited without ever calling ExitThread.
+ // It must have wandered in.
+ *lpExitCode = 0;
+#else // FEATURE_PAL_SXS
+ ASSERT("exit code not set but thread is dead\n");
+#endif // FEATURE_PAL_SXS
+ }
+ else
+ {
+ *lpExitCode = STILL_ACTIVE;
+ }
+ }
+
+ pthrTarget->Unlock(pthrCurrent);
+
+done:
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pthrCurrent);
+ }
+
+ LOGEXIT("GetExitCodeThread returns BOOL %d\n", NO_ERROR == palError);
+ PERF_EXIT(GetExitCodeThread);
+
+ return NO_ERROR == 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);
+ IncrementEndingThreadCount();
+ 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");
+ }
+
+#ifndef FEATURE_PAL_SXS
+ // If this is the last thread then delete the process' data,
+ // but don't exit because the application hosting the PAL
+ // might have its own threads.
+ if (PROCGetNumberOfThreads() == 1)
+ {
+ TRACE("Last thread is exiting\n");
+ DecrementEndingThreadCount();
+ TerminateCurrentProcessNoExit(FALSE);
+ }
+ else
+#endif // !FEATURE_PAL_SXS
+ {
+ /* Do this ONLY if we aren't the last thread -> otherwise
+ it gets done by TerminateProcess->
+ PROCCleanupProcess->PALShutdown->PAL_Terminate */
+
+ //
+ // 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);
+
+#ifdef FEATURE_PAL_SXS
+ // Ensure that EH is disabled on the current thread
+ SEHDisable(pThread);
+ PAL_Leave(PAL_BoundaryTop);
+#endif // FEATURE_PAL_SXS
+
+
+ //
+ // Now release our reference to the thread data. We cannot touch
+ // it after this point
+ //
+
+ pThread->ReleaseThreadReference();
+ DecrementEndingThreadCount();
+
+ }
+}
+
+/*++
+Function:
+ GetThreadPriority
+
+See MSDN doc.
+--*/
+int
+PALAPI
+GetThreadPriority(
+ IN HANDLE hThread)
+{
+ CPalThread *pThread;
+ PAL_ERROR palError;
+ int iPriority = THREAD_PRIORITY_ERROR_RETURN;
+
+ PERF_ENTRY(GetThreadPriority);
+ ENTRY("GetThreadPriority(hThread=%p)\n", hThread);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalGetThreadPriority(
+ pThread,
+ hThread,
+ &iPriority
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("GetThreadPriorityExit returns int %d\n", iPriority);
+ PERF_EXIT(GetThreadPriority);
+
+ return iPriority;
+}
+
+PAL_ERROR
+CorUnix::InternalGetThreadPriority(
+ CPalThread *pThread,
+ HANDLE hThread,
+ int *piPriority
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pTargetThread;
+ IPalObject *pobjThread = NULL;
+
+ palError = InternalGetThreadDataFromHandle(
+ pThread,
+ hThread,
+ 0, // THREAD_QUERY_INFORMATION
+ &pTargetThread,
+ &pobjThread
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalGetThreadPriorityExit;
+ }
+
+ pTargetThread->Lock(pThread);
+
+ *piPriority = pTargetThread->GetThreadPriority();
+
+ pTargetThread->Unlock(pThread);
+
+InternalGetThreadPriorityExit:
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+
+/*++
+Function:
+ SetThreadPriority
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+SetThreadPriority(
+ IN HANDLE hThread,
+ IN int nPriority)
+{
+ CPalThread *pThread;
+ PAL_ERROR palError = NO_ERROR;
+
+ PERF_ENTRY(SetThreadPriority);
+ ENTRY("SetThreadPriority(hThread=%p, nPriority=%#x)\n", hThread, nPriority);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalSetThreadPriority(
+ pThread,
+ hThread,
+ nPriority
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("SetThreadPriority returns BOOL %d\n", NO_ERROR == palError);
+ PERF_EXIT(SetThreadPriority);
+
+ return NO_ERROR == palError;
+}
+
+PAL_ERROR
+CorUnix::InternalSetThreadPriority(
+ CPalThread *pThread,
+ HANDLE hTargetThread,
+ int iNewPriority
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pTargetThread = NULL;
+ IPalObject *pobjThread = NULL;
+
+ int policy;
+ struct sched_param schedParam;
+ int max_priority;
+ int min_priority;
+ float posix_priority;
+
+
+ palError = InternalGetThreadDataFromHandle(
+ pThread,
+ hTargetThread,
+ 0, // THREAD_SET_INFORMATION
+ &pTargetThread,
+ &pobjThread
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalSetThreadPriorityExit;
+ }
+
+ pTargetThread->Lock(pThread);
+
+ /* validate the requested priority */
+ switch (iNewPriority)
+ {
+ case THREAD_PRIORITY_TIME_CRITICAL: /* fall through */
+ case THREAD_PRIORITY_IDLE:
+ break;
+
+ case THREAD_PRIORITY_HIGHEST: /* fall through */
+ case THREAD_PRIORITY_ABOVE_NORMAL: /* fall through */
+ case THREAD_PRIORITY_NORMAL: /* fall through */
+ case THREAD_PRIORITY_BELOW_NORMAL: /* fall through */
+ case THREAD_PRIORITY_LOWEST:
+#if PAL_IGNORE_NORMAL_THREAD_PRIORITY
+ /* We aren't going to set the thread priority. Just record what it is,
+ and exit */
+ pTargetThread->m_iThreadPriority = iNewPriority;
+ goto InternalSetThreadPriorityExit;
+#endif
+ break;
+
+ default:
+ ASSERT("Priority %d not supported\n", iNewPriority);
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalSetThreadPriorityExit;
+ }
+
+ /* check if the thread is still running */
+ if (TS_DONE == pTargetThread->synchronizationInfo.GetThreadState())
+ {
+ /* the thread has exited, set the priority in the thread structure
+ and exit */
+ pTargetThread->m_iThreadPriority = iNewPriority;
+ goto InternalSetThreadPriorityExit;
+ }
+
+ /* get the previous thread schedule parameters. We need to know the
+ scheduling policy to determine the priority range */
+ if (pthread_getschedparam(
+ pTargetThread->GetPThreadSelf(),
+ &policy,
+ &schedParam
+ ) != 0)
+ {
+ ASSERT("Unable to get current thread scheduling information\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalSetThreadPriorityExit;
+ }
+
+#if !HAVE_SCHED_OTHER_ASSIGNABLE
+ /* Defining thread priority for SCHED_OTHER is implementation defined.
+ Some platforms like NetBSD cannot reassign it as they are dynamic.
+ */
+ if (policy == SCHED_OTHER)
+ {
+ TRACE("Pthread priority levels for SCHED_OTHER cannot be reassigned on this platform\n");
+ goto InternalSetThreadPriorityExit;
+ }
+#endif
+
+#if HAVE_SCHED_GET_PRIORITY
+ max_priority = sched_get_priority_max(policy);
+ min_priority = sched_get_priority_min(policy);
+ if( -1 == max_priority || -1 == min_priority)
+ {
+ ASSERT("sched_get_priority_min/max failed; error is %d (%s)\n",
+ errno, strerror(errno));
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalSetThreadPriorityExit;
+ }
+#else
+ max_priority = PAL_THREAD_PRIORITY_MAX;
+ min_priority = PAL_THREAD_PRIORITY_MIN;
+#endif
+
+ TRACE("Pthread priorities for policy %d must be in the range %d to %d\n",
+ policy, min_priority, max_priority);
+
+ /* explanation for fancy maths below :
+ POSIX doesn't specify the range of thread priorities that can be used
+ with pthread_setschedparam. Instead, one must use sched_get_priority_min
+ and sched_get_priority_max to obtain the lower and upper bounds of this
+ range. Since the PAL also uses a range of values (from Idle [-15] to
+ Time Critical [+15]), we have to do a mapping from a known range to an
+ unknown (at compilation) range.
+ We do this by :
+ -substracting the minimal PAL priority from the desired priority. this
+ gives a value between 0 and the PAL priority range
+ -dividing this value by the PAL priority range. this allows us to
+ express the desired priority as a floating-point value between 0 and 1
+ -multiplying this value by the PTHREAD priority range. This gives a
+ value between 0 and the PTHREAD priority range
+ -adding the minimal PTHREAD priority range. This will give us a value
+ between the minimal and maximla pthread priority, which should be
+ equivalent to the original PAL value.
+
+ example : suppose a pthread range 100 to 200, and a desired priority
+ of 0 (halfway between PAL minimum and maximum)
+ 0 - (IDLE [-15]) = 15
+ 15 / (TIMECRITICAL[15] - IDLE[-15]) = 0.5
+ 0.5 * (pthreadmax[200]-pthreadmin[100]) = 50
+ 50 + pthreadmin[100] = 150 -> halfway between pthread min and max
+ */
+ posix_priority = (iNewPriority - THREAD_PRIORITY_IDLE);
+ posix_priority /= (THREAD_PRIORITY_TIME_CRITICAL - THREAD_PRIORITY_IDLE);
+ posix_priority *= (max_priority-min_priority);
+ posix_priority += min_priority;
+
+ schedParam.sched_priority = (int)posix_priority;
+
+ TRACE("PAL priority %d is mapped to pthread priority %d\n",
+ iNewPriority, schedParam.sched_priority);
+
+ /* Finally, set the new priority into place */
+ if (pthread_setschedparam(
+ pTargetThread->GetPThreadSelf(),
+ policy,
+ &schedParam
+ ) != 0)
+ {
+#if SET_SCHEDPARAM_NEEDS_PRIVS
+ if (EPERM == errno)
+ {
+ // UNIXTODO: Should log a warning to the event log
+ TRACE("Caller does not have OS privileges to call pthread_setschedparam\n");
+ pTargetThread->m_iThreadPriority = iNewPriority;
+ goto InternalSetThreadPriorityExit;
+ }
+#endif
+
+ ASSERT("Unable to set thread priority (errno %d)\n", errno);
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalSetThreadPriorityExit;
+ }
+
+ pTargetThread->m_iThreadPriority = iNewPriority;
+
+InternalSetThreadPriorityExit:
+
+ if (NULL != pTargetThread)
+ {
+ pTargetThread->Unlock(pThread);
+ }
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+BOOL
+CorUnix::GetThreadTimesInternal(
+ IN HANDLE hThread,
+ OUT LPFILETIME lpKernelTime,
+ OUT LPFILETIME lpUserTime)
+{
+ __int64 calcTime;
+ BOOL retval = FALSE;
+ const __int64 SECS_TO_NS = 1000000000; /* 10^9 */
+ const __int64 USECS_TO_NS = 1000; /* 10^3 */
+
+#if HAVE_MACH_THREADS
+ thread_basic_info resUsage;
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthrCurrent = NULL;
+ CPalThread *pthrTarget = NULL;
+ IPalObject *pobjThread = NULL;
+ mach_msg_type_number_t resUsage_count = THREAD_BASIC_INFO_COUNT;
+
+ pthrCurrent = InternalGetCurrentThread();
+ palError = InternalGetThreadDataFromHandle(
+ pthrCurrent,
+ hThread,
+ 0,
+ &pthrTarget,
+ &pobjThread
+ );
+
+ if (palError != NO_ERROR)
+ {
+ ASSERT("Unable to get thread data from handle %p"
+ "thread\n", hThread);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto SetTimesToZero;
+ }
+
+ pthrTarget->Lock(pthrCurrent);
+
+ mach_port_t mhThread;
+ mhThread = pthread_mach_thread_np(pthrTarget->GetPThreadSelf());
+
+ kern_return_t status;
+ status = thread_info(
+ mhThread,
+ THREAD_BASIC_INFO,
+ (thread_info_t)&resUsage,
+ &resUsage_count);
+
+ pthrTarget->Unlock(pthrCurrent);
+
+ if (status != KERN_SUCCESS)
+ {
+ ASSERT("Unable to get resource usage information for the current "
+ "thread\n");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto SetTimesToZero;
+ }
+
+ /* Get the time of user mode execution, in nanoseconds */
+ calcTime = (__int64)resUsage.user_time.seconds * SECS_TO_NS;
+ calcTime += (__int64)resUsage.user_time.microseconds * USECS_TO_NS;
+ /* Assign the time into lpUserTime */
+ lpUserTime->dwLowDateTime = (DWORD)calcTime;
+ lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
+
+ /* Get the time of kernel mode execution, in nanoseconds */
+ calcTime = (__int64)resUsage.system_time.seconds * SECS_TO_NS;
+ calcTime += (__int64)resUsage.system_time.microseconds * USECS_TO_NS;
+ /* Assign the time into lpKernelTime */
+ lpKernelTime->dwLowDateTime = (DWORD)calcTime;
+ lpKernelTime->dwHighDateTime = (DWORD)(calcTime >> 32);
+
+ retval = TRUE;
+
+ goto GetThreadTimesInternalExit;
+
+#elif defined(__NetBSD__) && !HAVE_PTHREAD_GETCPUCLOCKID /* Currently unimplemented */
+
+ PAL_ERROR palError;
+ CPalThread *pThread;
+ CPalThread *pTargetThread;
+ IPalObject *pobjThread = NULL;
+ kvm_t *kd;
+ int cnt, nlwps;
+ struct kinfo_lwp *klwp;
+ int i;
+ bool found = false;
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalGetThreadDataFromHandle(
+ pThread,
+ hThread,
+ 0, // THREAD_GET_CONTEXT
+ &pTargetThread,
+ &pobjThread
+ );
+ if (palError != NO_ERROR)
+ {
+ ASSERT("Unable to get thread data from handle %p"
+ "thread\n", hThread);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto SetTimesToZero;
+ }
+
+ kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
+ if (kd == NULL)
+ {
+ ASSERT("kvm_open(3) error");
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto SetTimesToZero;
+ }
+
+ pTargetThread->Lock(pThread);
+
+ klwp = kvm_getlwps(kd, getpid(), 0, sizeof(struct kinfo_lwp), &nlwps);
+ if (klwp == NULL || nlwps < 1)
+ {
+ kvm_close(kd);
+ ASSERT("Unable to get clock from %p thread\n", hThread);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ pTargetThread->Unlock(pThread);
+ goto SetTimesToZero;
+ }
+
+ for (i = 0; i < nlwps; i++)
+ {
+ if (klwp[i].l_lid == THREADSilentGetCurrentThreadId())
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ kvm_close(kd);
+ ASSERT("Unable to get clock from %p thread\n", hThread);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ pTargetThread->Unlock(pThread);
+ goto SetTimesToZero;
+ }
+
+ pTargetThread->Unlock(pThread);
+
+ kvm_close(kd);
+
+ calcTime = (__int64) klwp[i].l_rtime_sec * SECS_TO_NS;
+ calcTime += (__int64) klwp[i].l_rtime_usec * USECS_TO_NS;
+ lpUserTime->dwLowDateTime = (DWORD)calcTime;
+ lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
+
+ /* NetBSD as of (7.0) doesn't differentiate used time in user/kernel for lwp */
+ lpKernelTime->dwLowDateTime = 0;
+ lpKernelTime->dwHighDateTime = 0;
+
+ retval = TRUE;
+ goto GetThreadTimesInternalExit;
+
+#else //HAVE_MACH_THREADS
+
+ PAL_ERROR palError;
+ CPalThread *pThread;
+ CPalThread *pTargetThread;
+ IPalObject *pobjThread = NULL;
+ clockid_t cid;
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalGetThreadDataFromHandle(
+ pThread,
+ hThread,
+ 0, // THREAD_GET_CONTEXT
+ &pTargetThread,
+ &pobjThread
+ );
+ if (palError != NO_ERROR)
+ {
+ ASSERT("Unable to get thread data from handle %p"
+ "thread\n", hThread);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ goto SetTimesToZero;
+ }
+
+ pTargetThread->Lock(pThread);
+
+#if HAVE_PTHREAD_GETCPUCLOCKID
+ if (pthread_getcpuclockid(pTargetThread->GetPThreadSelf(), &cid) != 0)
+#endif
+ {
+ ASSERT("Unable to get clock from thread\n", hThread);
+ SetLastError(ERROR_INTERNAL_ERROR);
+ pTargetThread->Unlock(pThread);
+ goto SetTimesToZero;
+ }
+
+ struct timespec ts;
+ if (clock_gettime(cid, &ts) != 0)
+ {
+ ASSERT("clock_gettime() failed; errno is %d (%s)\n", errno, strerror(errno));
+ SetLastError(ERROR_INTERNAL_ERROR);
+ pTargetThread->Unlock(pThread);
+ goto SetTimesToZero;
+ }
+
+ pTargetThread->Unlock(pThread);
+
+ /* Calculate time in nanoseconds and assign to user time */
+ calcTime = (__int64) ts.tv_sec * SECS_TO_NS;
+ calcTime += (__int64) ts.tv_nsec;
+ lpUserTime->dwLowDateTime = (DWORD)calcTime;
+ lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
+
+ /* Set kernel time to zero, for now */
+ lpKernelTime->dwLowDateTime = 0;
+ lpKernelTime->dwHighDateTime = 0;
+
+ retval = TRUE;
+ goto GetThreadTimesInternalExit;
+
+#endif //HAVE_MACH_THREADS
+
+SetTimesToZero:
+
+ lpUserTime->dwLowDateTime = 0;
+ lpUserTime->dwHighDateTime = 0;
+ lpKernelTime->dwLowDateTime = 0;
+ lpKernelTime->dwHighDateTime = 0;
+ goto GetThreadTimesInternalExit;
+
+GetThreadTimesInternalExit:
+ return retval;
+}
+
+/*++
+Function:
+ GetThreadTimes
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+GetThreadTimes(
+ IN HANDLE hThread,
+ OUT LPFILETIME lpCreationTime,
+ OUT LPFILETIME lpExitTime,
+ OUT LPFILETIME lpKernelTime,
+ OUT LPFILETIME lpUserTime)
+{
+ PERF_ENTRY(GetThreadTimes);
+ ENTRY("GetThreadTimes(hThread=%p, lpExitTime=%p, lpKernelTime=%p,"
+ "lpUserTime=%p)\n",
+ hThread, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime );
+
+ FILETIME KernelTime, UserTime;
+
+ BOOL retval = GetThreadTimesInternal(hThread, &KernelTime, &UserTime);
+
+ /* Not sure if this still needs to be here */
+ /*
+ TRACE ("thread_info User: %ld sec,%ld microsec. Kernel: %ld sec,%ld"
+ " microsec\n",
+ resUsage.user_time.seconds, resUsage.user_time.microseconds,
+ resUsage.system_time.seconds, resUsage.system_time.microseconds);
+ */
+
+ __int64 calcTime;
+ if (lpUserTime)
+ {
+ /* Produce the time in 100s of ns */
+ calcTime = ((ULONG64)UserTime.dwHighDateTime << 32);
+ calcTime += (ULONG64)UserTime.dwLowDateTime;
+ calcTime /= 100;
+ lpUserTime->dwLowDateTime = (DWORD)calcTime;
+ lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
+ }
+ if (lpKernelTime)
+ {
+ /* Produce the time in 100s of ns */
+ calcTime = ((ULONG64)KernelTime.dwHighDateTime << 32);
+ calcTime += (ULONG64)KernelTime.dwLowDateTime;
+ calcTime /= 100;
+ lpKernelTime->dwLowDateTime = (DWORD)calcTime;
+ lpKernelTime->dwHighDateTime = (DWORD)(calcTime >> 32);
+ }
+ //Set CreationTime and Exit time to zero for now - maybe change this later?
+ if (lpCreationTime)
+ {
+ lpCreationTime->dwLowDateTime = 0;
+ lpCreationTime->dwHighDateTime = 0;
+ }
+
+ if (lpExitTime)
+ {
+ lpExitTime->dwLowDateTime = 0;
+ lpExitTime->dwHighDateTime = 0;
+ }
+
+ LOGEXIT("GetThreadTimes returns BOOL %d\n", retval);
+ PERF_EXIT(GetThreadTimes);
+ return (retval);
+}
+
+
+
+void *
+CPalThread::ThreadEntry(
+ void *pvParam
+ )
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+ PTHREAD_START_ROUTINE pfnStartRoutine;
+ LPVOID pvPar;
+ DWORD retValue;
+
+ pThread = reinterpret_cast<CPalThread*>(pvParam);
+
+ if (NULL == pThread)
+ {
+ ASSERT("THREAD pointer is NULL!\n");
+ goto fail;
+ }
+
+#if defined(FEATURE_PAL_SXS) && defined(_DEBUG)
+ // We cannot assert yet, as we haven't set in this thread into the TLS, and so __ASSERT_ENTER
+ // will fail if the assert fails and we'll crash.
+ //_ASSERT_MSG(pThread->m_fInPal == 1, "New threads should always be in the PAL upon ThreadEntry.\n");
+ if (g_Dbg_asserts_enabled && pThread->m_fInPal != 1)
+ DebugBreak();
+#endif // FEATURE_PAL_SXS && _DEBUG
+
+ pThread->m_threadId = THREADSilentGetCurrentThreadId();
+ pThread->m_pthreadSelf = pthread_self();
+#if HAVE_MACH_THREADS
+ pThread->m_machPortSelf = pthread_mach_thread_np(pThread->m_pthreadSelf);
+#endif
+#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;
+ }
+
+ //
+ // We need to run any APCs that have already been queued for
+ // this thread.
+ //
+
+ (void) g_pSynchronizationManager->DispatchPendingAPCs(pThread);
+ }
+ 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);
+ ExitThread(retValue);
+
+ /* Note: never get here */
+ ASSERT("ExitThread failed!\n");
+ for (;;);
+
+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;
+}
+
+
+#define PAL_THREAD_DEFAULT_STACK_SIZE "PAL_THREAD_DEFAULT_STACK_SIZE"
+
+PAL_ERROR
+CorUnix::InitializeGlobalThreadData(
+ void
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ char *pszStackSize = NULL;
+
+ //
+ // Read in the environment to see whether we need to change the default
+ // thread stack size.
+ //
+ pszStackSize = EnvironGetenv(PAL_THREAD_DEFAULT_STACK_SIZE);
+ if (NULL != pszStackSize)
+ {
+ // Environment variable exists
+ char *pszEnd;
+ DWORD dw = PAL_strtoul(pszStackSize, &pszEnd, 16); // treat it as hex
+ if ( (pszStackSize != pszEnd) && (0 != dw) )
+ {
+ CPalThread::s_dwDefaultThreadStackSize = dw;
+ }
+
+ free(pszStackSize);
+ }
+
+ return palError;
+}
+
+
+/*++
+Function:
+ CreateThreadData
+
+Abstract:
+ Create the CPalThread for the startup thread
+ or another external thread entering the PAL
+ for the first time
+
+Parameters:
+ ppThread - on success, receives the CPalThread
+
+Return:
+ PAL_ERROR
+--*/
+
+PAL_ERROR
+CorUnix::CreateThreadData(
+ CPalThread **ppThread
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread = NULL;
+
+ /* Create the thread object */
+ pThread = AllocTHREAD();
+
+ if (NULL == pThread)
+ {
+ palError = ERROR_OUTOFMEMORY;
+ goto CreateThreadDataExit;
+ }
+
+ palError = pThread->RunPreCreateInitializers();
+
+ if (NO_ERROR != palError)
+ {
+ goto CreateThreadDataExit;
+ }
+
+ pThread->SetLastError(0);
+
+ pThread->m_threadId = THREADSilentGetCurrentThreadId();
+ pThread->m_pthreadSelf = pthread_self();
+#if HAVE_MACH_THREADS
+ pThread->m_machPortSelf = pthread_mach_thread_np(pThread->m_pthreadSelf);
+#endif
+#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)
+ {
+ goto CreateThreadDataExit;
+ }
+
+ *ppThread = pThread;
+
+CreateThreadDataExit:
+
+ if (NO_ERROR != palError)
+ {
+ if (NULL != pThread)
+ {
+ pThread->ReleaseThreadReference();
+ }
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ CreateThreadData
+
+Abstract:
+ Creates the IPalObject for a thread, storing
+ the reference in the CPalThread
+
+Parameters:
+ pThread - the thread data for the creating thread
+ pNewThread - the thread data for the thread being initialized
+
+Return:
+ PAL_ERROR
+--*/
+
+PAL_ERROR
+CorUnix::CreateThreadObject(
+ CPalThread *pThread,
+ CPalThread *pNewThread,
+ HANDLE *phThread
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pobjThread = NULL;
+ IDataLock *pDataLock;
+ HANDLE hThread = NULL;
+ CThreadProcessLocalData *pLocalData = NULL;
+ CObjectAttributes oa;
+ BOOL fThreadDataStoredInObject = FALSE;
+ IPalObject *pobjRegisteredThread = NULL;
+
+ //
+ // Create the IPalObject for the thread
+ //
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otThread,
+ &oa,
+ &pobjThread
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto CreateThreadObjectExit;
+ }
+
+ //
+ // Store the CPalThread inside of the IPalObject
+ //
+
+ palError = pobjThread->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto CreateThreadObjectExit;
+ }
+
+ pLocalData->pThread = pNewThread;
+ pDataLock->ReleaseLock(pThread, TRUE);
+ fThreadDataStoredInObject = TRUE;
+
+ //
+ // Register the IPalObject (obtaining a handle)
+ //
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pobjThread,
+ &aotThread,
+ 0, //THREAD_ALL_ACCESS,
+ &hThread,
+ &pobjRegisteredThread
+ );
+
+ //
+ // pobjThread is invalidated by the call to RegisterObject, so NULL
+ // it out here to prevent it from being released
+ //
+
+ pobjThread = NULL;
+
+ if (NO_ERROR != palError)
+ {
+ goto CreateThreadObjectExit;
+ }
+
+ //
+ // Store the registered object inside of the thread object,
+ // adding a reference for the thread itself
+ //
+
+ pNewThread->m_pThreadObject = pobjRegisteredThread;
+ pNewThread->m_pThreadObject->AddReference();
+
+ *phThread = hThread;
+
+CreateThreadObjectExit:
+
+ if (NO_ERROR != palError)
+ {
+ if (NULL != hThread)
+ {
+ g_pObjectManager->RevokeHandle(pThread, hThread);
+ }
+
+ if (NULL != pNewThread->m_pThreadObject)
+ {
+ //
+ // Release the new thread's reference on the underlying thread
+ // object
+ //
+
+ pNewThread->m_pThreadObject->ReleaseReference(pThread);
+ }
+
+ if (!fThreadDataStoredInObject)
+ {
+ //
+ // The CPalThread for the new thread was never stored in
+ // an IPalObject instance, so we need to release the initial
+ // reference here. (If it has been stored it will get freed in
+ // the owning object's cleanup routine)
+ //
+
+ pNewThread->ReleaseThreadReference();
+ }
+ }
+
+ if (NULL != pobjThread)
+ {
+ pobjThread->ReleaseReference(pThread);
+ }
+
+ if (NULL != pobjRegisteredThread)
+ {
+ pobjRegisteredThread->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+PAL_ERROR
+CorUnix::InternalCreateDummyThread(
+ CPalThread *pThread,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ CPalThread **ppDummyThread,
+ HANDLE *phThread
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pDummyThread = NULL;
+ IPalObject *pobjThread = NULL;
+ IPalObject *pobjThreadRegistered = NULL;
+ IDataLock *pDataLock;
+ CThreadProcessLocalData *pLocalData;
+ CObjectAttributes oa(NULL, lpThreadAttributes);
+ bool fThreadDataStoredInObject = FALSE;
+
+ pDummyThread = AllocTHREAD();
+ if (NULL == pDummyThread)
+ {
+ palError = ERROR_OUTOFMEMORY;
+ goto InternalCreateDummyThreadExit;
+ }
+
+ pDummyThread->m_fIsDummy = TRUE;
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otThread,
+ &oa,
+ &pobjThread
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateDummyThreadExit;
+ }
+
+ palError = pobjThread->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalCreateDummyThreadExit;
+ }
+
+ pLocalData->pThread = pDummyThread;
+ pDataLock->ReleaseLock(pThread, TRUE);
+ fThreadDataStoredInObject = TRUE;
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pobjThread,
+ &aotThread,
+ 0, // THREAD_ALL_ACCESS
+ 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,
+ DWORD dwRightsRequired,
+ 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,
+ dwRightsRequired,
+ &pobj
+ );
+
+ if (NO_ERROR == palError)
+ {
+ palError = pobj->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLock,
+ reinterpret_cast<void**>(&pData)
+ );
+
+ if (NO_ERROR == palError)
+ {
+ *ppTargetThread = pData->pThread;
+ pLock->ReleaseLock(pThread, FALSE);
+
+ //
+ // Transfer object reference to out param
+ //
+
+ *ppobjThread = pobj;
+ }
+ else
+ {
+ pobj->ReleaseReference(pThread);
+ }
+ }
+ }
+
+ return palError;
+}
+
+PAL_ERROR
+CPalThread::RunPreCreateInitializers(
+ void
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ int iError;
+
+ //
+ // First, perform initialization of CPalThread private members
+ //
+
+ 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 = sehInfo.InitializePreCreate();
+ if (NO_ERROR != palError)
+ {
+ goto RunPreCreateInitializersExit;
+ }
+
+ palError = tlsInfo.InitializePreCreate();
+ if (NO_ERROR != palError)
+ {
+ goto RunPreCreateInitializersExit;
+ }
+
+ palError = apcInfo.InitializePreCreate();
+ if (NO_ERROR != palError)
+ {
+ goto RunPreCreateInitializersExit;
+ }
+
+ palError = crtInfo.InitializePreCreate();
+ if (NO_ERROR != palError)
+ {
+ goto RunPreCreateInitializersExit;
+ }
+
+RunPreCreateInitializersExit:
+
+ return palError;
+}
+
+CPalThread::~CPalThread()
+{
+ // @UNIXTODO: This is our last chance to unlink our Mach exception handler from the pseudo-chain we're trying
+ // to maintain. Unfortunately we don't have enough data or control to do this at all well (and we can't
+ // guarantee that another component hasn't chained to us, about which we can do nothing). If the kernel or
+ // another component forwards an exception notification to us for this thread things will go badly (we'll
+ // terminate the process when trying to look up this CPalThread in order to find forwarding information).
+ // On the flip side I don't believe we'll get here currently unless the thread has been terminated (in
+ // which case it's not an issue). If we start supporting unload or early disposal of CPalThread objects
+ // (say when we return from an outer reverse p/invoke) then we'll need to revisit this. But hopefully by
+ // then we'll have an alternative design for handling hardware exceptions.
+
+ if (m_fLockInitialized)
+ {
+ 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
+CPalThread::AddThreadReference(
+ void
+ )
+{
+ InterlockedIncrement(&m_lRefCount);
+}
+
+void
+CPalThread::ReleaseThreadReference(
+ void
+ )
+{
+ LONG lRefCount = InterlockedDecrement(&m_lRefCount);
+ _ASSERT_MSG(lRefCount >= 0, "Released a thread and ended with a negative refcount (%ld)\n", lRefCount);
+ if (0 == lRefCount)
+ {
+ FreeTHREAD(this);
+ }
+
+}
+
+PAL_ERROR
+CPalThread::RunPostCreateInitializers(
+ void
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ //
+ // Call the post-create initializers for embedded classes
+ //
+
+ 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 = sehInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
+ if (NO_ERROR != palError)
+ {
+ goto RunPostCreateInitializersExit;
+ }
+
+ palError = tlsInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
+ if (NO_ERROR != palError)
+ {
+ goto RunPostCreateInitializersExit;
+ }
+
+ palError = apcInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
+ if (NO_ERROR != palError)
+ {
+ goto RunPostCreateInitializersExit;
+ }
+
+ palError = crtInfo.InitializePostCreate(this, m_threadId, m_dwLwpId);
+ if (NO_ERROR != palError)
+ {
+ goto RunPostCreateInitializersExit;
+ }
+
+#ifdef FEATURE_PAL_SXS
+ _ASSERTE(m_fInPal);
+ palError = SEHEnable(this);
+ if (NO_ERROR != palError)
+ {
+ goto RunPostCreateInitializersExit;
+ }
+#endif // FEATURE_PAL_SXS
+
+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;
+}
+
+/* IncrementEndingThreadCount and DecrementEndingThreadCount are used
+to control a global counter that indicates if any threads are about to die.
+Once a thread's state is set to TS_DONE, it cannot be suspended. However,
+the dying thread can still access PAL resources, which is dangerous if the
+thread dies during PAL cleanup. To avoid this, the shutdown thread calls
+WaitForEndingThreads after suspending all other threads. WaitForEndingThreads
+uses a condition variable along with the global counter to wait for remaining
+PAL threads to die before proceeding with cleanup. As threads die, they
+decrement the counter and signal the condition variable. */
+
+void
+IncrementEndingThreadCount(
+ void
+ )
+{
+ int iError;
+
+ iError = pthread_mutex_lock(&ptmEndThread);
+ _ASSERT_MSG(iError == 0, "pthread_mutex_lock returned %d\n", iError);
+
+ iEndingThreads++;
+
+ iError = pthread_mutex_unlock(&ptmEndThread);
+ _ASSERT_MSG(iError == 0, "pthread_mutex_unlock returned %d\n", iError);
+}
+
+void
+DecrementEndingThreadCount(
+ void
+ )
+{
+ int iError;
+
+ iError = pthread_mutex_lock(&ptmEndThread);
+ _ASSERT_MSG(iError == 0, "pthread_mutex_lock returned %d\n", iError);
+
+ iEndingThreads--;
+ _ASSERTE(iEndingThreads >= 0);
+
+ if (iEndingThreads == 0)
+ {
+ iError = pthread_cond_signal(&ptcEndThread);
+ _ASSERT_MSG(iError == 0, "pthread_cond_signal returned %d\n", iError);
+ }
+
+ iError = pthread_mutex_unlock(&ptmEndThread);
+ _ASSERT_MSG(iError == 0, "pthread_mutex_unlock returned %d\n", iError);
+}
+
+void
+WaitForEndingThreads(
+ void
+ )
+{
+ int iError;
+
+ iError = pthread_mutex_lock(&ptmEndThread);
+ _ASSERT_MSG(iError == 0, "pthread_mutex_lock returned %d\n", iError);
+
+ while (iEndingThreads > 0)
+ {
+ iError = pthread_cond_wait(&ptcEndThread, &ptmEndThread);
+ _ASSERT_MSG(iError == 0, "pthread_cond_wait returned %d\n", iError);
+ }
+
+ iError = pthread_mutex_unlock(&ptmEndThread);
+ _ASSERT_MSG(iError == 0, "pthread_mutex_unlock returned %d\n", iError);
+}
+
+PAL_ERROR
+CorUnix::InitializeEndingThreadsData(
+ void
+ )
+{
+ PAL_ERROR palError = ERROR_INTERNAL_ERROR;
+ int iError;
+
+ iError = pthread_mutex_init(&ptmEndThread, NULL);
+ if (0 != iError)
+ {
+ goto InitializeEndingThreadsDataExit;
+ }
+
+ iError = pthread_cond_init(&ptcEndThread, NULL);
+ if (0 != iError)
+ {
+ //
+ // Don't bother checking the return value of pthread_mutex_destroy
+ // since PAL initialization will now fail.
+ //
+
+ pthread_mutex_destroy(&ptmEndThread);
+ goto InitializeEndingThreadsDataExit;
+ }
+
+ palError = NO_ERROR;
+
+InitializeEndingThreadsDataExit:
+
+ return palError;
+}
+
+void
+ThreadCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup,
+ bool fShutdown,
+ bool fCleanupSharedState
+ )
+{
+ CThreadProcessLocalData *pThreadData = NULL;
+ CPalThread *pThreadToCleanup = NULL;
+ IDataLock *pDataLock = NULL;
+ PAL_ERROR palError = NO_ERROR;
+
+ //
+ // Free the CPalThread data for the passed in thread
+ //
+
+ palError = pObjectToCleanup->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void**>(&pThreadData)
+ );
+
+ if (NO_ERROR == palError)
+ {
+ //
+ // Note that we may be cleaning up the data for the calling
+ // thread (i.e., pThread == pThreadToCleanup), so the release
+ // of the thread reference needs to be the last thing that
+ // we do (though in that case it's very likely that the person
+ // calling us will be holding an extra reference to allow
+ // for the thread data to be available while the rest of the
+ // object cleanup takes place).
+ //
+
+ pThreadToCleanup = pThreadData->pThread;
+ pThreadData->pThread = NULL;
+ pDataLock->ReleaseLock(pThread, TRUE);
+ pThreadToCleanup->ReleaseThreadReference();
+ }
+ else
+ {
+ ASSERT("Unable to obtain thread data");
+ }
+
+}
+
+PAL_ERROR
+ThreadInitializationRoutine(
+ CPalThread *pThread,
+ CObjectType *pObjectType,
+ void *pImmutableData,
+ void *pSharedData,
+ void *pProcessLocalData
+ )
+{
+ return NO_ERROR;
+}
+
+// Get base address of the current thread's stack
+void *
+CPalThread::GetStackBase()
+{
+ void* stackBase;
+#ifdef _TARGET_MAC64
+ // This is a Mac specific method
+ stackBase = pthread_get_stackaddr_np(pthread_self());
+#else
+ pthread_attr_t attr;
+ void* stackAddr;
+ size_t stackSize;
+ int status;
+
+ pthread_t thread = pthread_self();
+
+ status = pthread_attr_init(&attr);
+ _ASSERT_MSG(status == 0, "pthread_attr_init call failed");
+
+#if HAVE_PTHREAD_ATTR_GET_NP
+ status = pthread_attr_get_np(thread, &attr);
+#elif HAVE_PTHREAD_GETATTR_NP
+ status = pthread_getattr_np(thread, &attr);
+#else
+#error Dont know how to get thread attributes on this platform!
+#endif
+ _ASSERT_MSG(status == 0, "pthread_getattr_np call failed");
+
+ status = pthread_attr_getstack(&attr, &stackAddr, &stackSize);
+ _ASSERT_MSG(status == 0, "pthread_attr_getstack call failed");
+
+ status = pthread_attr_destroy(&attr);
+ _ASSERT_MSG(status == 0, "pthread_attr_destroy call failed");
+
+ stackBase = (void*)((size_t)stackAddr + stackSize);
+#endif
+
+ return stackBase;
+}
+
+// Get limit address of the current thread's stack
+void *
+CPalThread::GetStackLimit()
+{
+ void* stackLimit;
+#ifdef _TARGET_MAC64
+ // This is a Mac specific method
+ stackLimit = ((BYTE *)pthread_get_stackaddr_np(pthread_self()) -
+ pthread_get_stacksize_np(pthread_self()));
+#else
+ pthread_attr_t attr;
+ size_t stackSize;
+ int status;
+
+ pthread_t thread = pthread_self();
+
+ status = pthread_attr_init(&attr);
+ _ASSERT_MSG(status == 0, "pthread_attr_init call failed");
+
+#if HAVE_PTHREAD_ATTR_GET_NP
+ status = pthread_attr_get_np(thread, &attr);
+#elif HAVE_PTHREAD_GETATTR_NP
+ status = pthread_getattr_np(thread, &attr);
+#else
+#error Dont know how to get thread attributes on this platform!
+#endif
+ _ASSERT_MSG(status == 0, "pthread_getattr_np call failed");
+
+ status = pthread_attr_getstack(&attr, &stackLimit, &stackSize);
+ _ASSERT_MSG(status == 0, "pthread_attr_getstack call failed");
+
+ status = pthread_attr_destroy(&attr);
+ _ASSERT_MSG(status == 0, "pthread_attr_destroy call failed");
+#endif
+
+ return stackLimit;
+}
+
+// Get cached base address of this thread's stack
+// Can be called only for the current thread.
+void *
+CPalThread::GetCachedStackBase()
+{
+ _ASSERT_MSG(this == InternalGetCurrentThread(), "CPalThread::GetStackBase called from foreign thread");
+
+ if (m_stackBase == NULL)
+ {
+ m_stackBase = GetStackBase();
+ }
+
+ return m_stackBase;
+}
+
+// Get cached limit address of this thread's stack.
+// Can be called only for the current thread.
+void *
+CPalThread::GetCachedStackLimit()
+{
+ _ASSERT_MSG(this == InternalGetCurrentThread(), "CPalThread::GetCachedStackLimit called from foreign thread");
+
+ if (m_stackLimit == NULL)
+ {
+ m_stackLimit = GetStackLimit();
+ }
+
+ return m_stackLimit;
+}
+
+void *
+PALAPI
+PAL_GetStackBase()
+{
+ CPalThread* thread = InternalGetCurrentThread();
+ return thread->GetCachedStackBase();
+}
+
+void *
+PALAPI
+PAL_GetStackLimit()
+{
+ CPalThread* thread = InternalGetCurrentThread();
+ return thread->GetCachedStackLimit();
+}
+
+PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread);
+
+/*++
+Function:
+ PAL_SetActivationFunction
+
+ Register an activation function that gets called when an activation is injected
+ into a thread.
+
+Parameters:
+ pActivationFunction - activation function
+ pSafeActivationCheckFunction - function to check if an activation can be safely
+ injected at a specified context
+Return value:
+ None
+--*/
+PALIMPORT
+VOID
+PALAPI
+PAL_SetActivationFunction(
+ IN PAL_ActivationFunction pActivationFunction,
+ IN PAL_SafeActivationCheckFunction pSafeActivationCheckFunction)
+{
+ g_activationFunction = pActivationFunction;
+ g_safeActivationCheckFunction = pSafeActivationCheckFunction;
+}
+
+/*++
+Function:
+PAL_InjectActivation
+
+Interrupt the specified thread and have it call an activation function registered
+using the PAL_SetActivationFunction
+
+Parameters:
+hThread - handle of the target thread
+
+Return:
+TRUE if it succeeded, FALSE otherwise.
+--*/
+BOOL
+PALAPI
+PAL_InjectActivation(
+ IN HANDLE hThread)
+{
+ PERF_ENTRY(PAL_InjectActivation);
+ ENTRY("PAL_InjectActivation(hThread=%p)\n", hThread);
+
+ CPalThread *pCurrentThread;
+ CPalThread *pTargetThread;
+ IPalObject *pobjThread = NULL;
+
+ pCurrentThread = InternalGetCurrentThread();
+
+ PAL_ERROR palError = InternalGetThreadDataFromHandle(
+ pCurrentThread,
+ hThread,
+ 0,
+ &pTargetThread,
+ &pobjThread
+ );
+
+ if (palError == NO_ERROR)
+ {
+ palError = InjectActivationInternal(pTargetThread);
+ }
+
+ if (palError == NO_ERROR)
+ {
+ pCurrentThread->SetLastError(palError);
+ }
+
+ if (pobjThread != NULL)
+ {
+ pobjThread->ReleaseReference(pCurrentThread);
+ }
+
+ BOOL success = (palError == NO_ERROR);
+ LOGEXIT("PAL_InjectActivation returns:d\n", success);
+ PERF_EXIT(PAL_InjectActivation);
+
+ return success;
+}
+
+#if HAVE_MACH_EXCEPTIONS
+
+extern mach_port_t s_ExceptionPort;
+
+// Get handler details for a given type of exception. If successful the structure pointed at by pHandler is
+// filled in and true is returned. Otherwise false is returned.
+bool CorUnix::CThreadMachExceptionHandlers::GetHandler(exception_type_t eException, CorUnix::MachExceptionHandler *pHandler)
+{
+ exception_mask_t bmExceptionMask = (1 << eException);
+ int idxHandler = GetIndexOfHandler(bmExceptionMask);
+
+ // Did we find a handler?
+ if (idxHandler == -1)
+ return false;
+
+ // Found one, so initialize the output structure with the details.
+ pHandler->m_mask = m_masks[idxHandler];
+ pHandler->m_handler = m_handlers[idxHandler];
+ pHandler->m_behavior = m_behaviors[idxHandler];
+ pHandler->m_flavor = m_flavors[idxHandler];
+
+ return true;
+}
+
+// Look for a handler for the given exception within the given handler node. Return its index if successful or
+// -1 otherwise.
+int CorUnix::CThreadMachExceptionHandlers::GetIndexOfHandler(exception_mask_t bmExceptionMask)
+{
+ // Check all handler entries for one handling the exception mask.
+ for (mach_msg_type_number_t i = 0; i < m_nPorts; i++)
+ {
+ // Entry covers this exception type and the handler isn't null
+ if (m_masks[i] & bmExceptionMask && m_handlers[i] != MACH_PORT_NULL)
+ {
+ _ASSERTE(m_handlers[i] != s_ExceptionPort);
+
+ // One more check; has the target handler port become dead?
+ mach_port_type_t ePortType;
+ if (mach_port_type(mach_task_self(), m_handlers[i], &ePortType) == KERN_SUCCESS && !(ePortType & MACH_PORT_TYPE_DEAD_NAME))
+ {
+ // Got a matching entry.
+ return i;
+ }
+ }
+ }
+
+ // Didn't find a handler.
+ return -1;
+}
+
+#endif // HAVE_MACH_EXCEPTIONS
diff --git a/src/pal/src/thread/threadsusp.cpp b/src/pal/src/thread/threadsusp.cpp
new file mode 100644
index 0000000000..b31b88da59
--- /dev/null
+++ b/src/pal/src/thread/threadsusp.cpp
@@ -0,0 +1,1046 @@
+// 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.
+
+
+/*++
+
+
+
+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/seh.hpp"
+#include "pal/init.h"
+#include "pal/dbgmsg.h"
+
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <debugmacrosext.h>
+
+#if defined(_AIX)
+// AIX requires explicit definition of the union semun (see semctl man page)
+union semun
+{
+ int val;
+ struct semid_ds * buf;
+ unsigned short * array;
+};
+#endif
+
+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];
+ if (pipe(pipe_descs) == -1)
+ {
+ ERROR("pipe() failed! error is %d (%s)\n", errno, strerror(errno));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ // [0] is the read end of the pipe, and [1] is the write end.
+ pThread->suspensionInfo.SetBlockingPipe(pipe_descs[1]);
+ pThread->SetStartStatus(TRUE);
+
+ BYTE resume_code = 0;
+ ssize_t read_ret;
+
+ // Block until ResumeThread writes something to the pipe
+ while ((read_ret = read(pipe_descs[0], &resume_code, sizeof(resume_code))) != sizeof(resume_code))
+ {
+ if (read_ret != -1 || EINTR != errno)
+ {
+ // read might return 0 (with EAGAIN) if the other end of the pipe gets closed
+ palError = ERROR_INTERNAL_ERROR;
+ break;
+ }
+ }
+
+ if (palError == NO_ERROR && resume_code != WAKEUPCODE)
+ {
+ // If we did read successfully but the byte didn't match WAKEUPCODE, we treat it as a failure.
+ palError = ERROR_INTERNAL_ERROR;
+ }
+
+ if (palError == NO_ERROR)
+ {
+ AcquireSuspensionLock(pThread);
+ pThread->suspensionInfo.SetSelfSusp(FALSE);
+ ReleaseSuspensionLock(pThread);
+ }
+
+ // Close the pipes regardless of whether we were successful.
+ close(pipe_descs[0]);
+ close(pipe_descs[1]);
+
+ return palError;
+}
+
+/*++
+Function:
+
+ ResumeThread
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+ResumeThread(
+ IN HANDLE hThread
+ )
+{
+ PAL_ERROR palError;
+ CPalThread *pthrResumer;
+ DWORD dwSuspendCount = (DWORD)-1;
+
+ PERF_ENTRY(ResumeThread);
+ ENTRY("ResumeThread(hThread=%p)\n", hThread);
+
+ pthrResumer = InternalGetCurrentThread();
+ palError = InternalResumeThread(
+ pthrResumer,
+ hThread,
+ &dwSuspendCount
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pthrResumer->SetLastError(palError);
+ dwSuspendCount = (DWORD) -1;
+ }
+ else
+ {
+ _ASSERT_MSG(dwSuspendCount != static_cast<DWORD>(-1), "InternalResumeThread returned success but dwSuspendCount did not change.\n");
+ }
+
+ LOGEXIT("ResumeThread returns DWORD %u\n", dwSuspendCount);
+ PERF_EXIT(ResumeThread);
+ return dwSuspendCount;
+}
+
+/*++
+Function:
+ InternalResumeThread
+
+InternalResumeThread converts the handle of the target thread to a
+CPalThread, and passes both the resumer and target thread references
+to InternalResumeThreadFromData. A reference to the suspend count from
+the resumption attempt is passed back to the caller of this function.
+--*/
+PAL_ERROR
+CorUnix::InternalResumeThread(
+ CPalThread *pthrResumer,
+ HANDLE hTargetThread,
+ DWORD *pdwSuspendCount
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pthrTarget = NULL;
+ IPalObject *pobjThread = NULL;
+
+ palError = InternalGetThreadDataFromHandle(
+ pthrResumer,
+ hTargetThread,
+ 0, // THREAD_SUSPEND_RESUME
+ &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:
+ PostOnSuspendSemaphore
+
+PostOnSuspendSemaphore is a utility function for a thread
+to post on its POSIX or SysV suspension semaphore.
+--*/
+void
+CThreadSuspensionInfo::PostOnSuspendSemaphore()
+{
+#if USE_POSIX_SEMAPHORES
+ if (sem_post(&m_semSusp) == -1)
+ {
+ ASSERT("sem_post returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+#elif USE_SYSV_SEMAPHORES
+ if (semop(m_nSemsuspid, &m_sbSempost, 1) == -1)
+ {
+ ASSERT("semop - post returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+#elif USE_PTHREAD_CONDVARS
+ int status;
+
+ // The suspending thread may not have entered the wait yet, in which case the cond var
+ // signal below will be a no-op. To prevent the race condition we set m_fSuspended to
+ // TRUE first (which the suspender will take as an indication that no wait is required).
+ // But the setting of the flag and the signal must appear atomic to the suspender (as
+ // reading the flag and potentially waiting must appear to us) to avoid the race
+ // condition where the suspender reads the flag as FALSE, we set it and signal and the
+ // suspender then waits.
+
+ // Acquire the suspend mutex. Once we enter the critical section the suspender has
+ // either gotten there before us (and is waiting for our signal) or is yet to even
+ // check the flag (so we can set it here to stop them attempting a wait).
+ status = pthread_mutex_lock(&m_mutexSusp);
+ if (status != 0)
+ {
+ ASSERT("pthread_mutex_lock returned %d (%s)\n", status, strerror(status));
+ }
+
+ m_fSuspended = TRUE;
+
+ status = pthread_cond_signal(&m_condSusp);
+ if (status != 0)
+ {
+ ASSERT("pthread_cond_signal returned %d (%s)\n", status, strerror(status));
+ }
+
+ status = pthread_mutex_unlock(&m_mutexSusp);
+ if (status != 0)
+ {
+ ASSERT("pthread_mutex_unlock returned %d (%s)\n", status, strerror(status));
+ }
+#endif // USE_POSIX_SEMAPHORES
+}
+
+/*++
+Function:
+ WaitOnSuspendSemaphore
+
+WaitOnSuspendSemaphore is a utility function for a thread
+to wait on its POSIX or SysV suspension semaphore.
+--*/
+void
+CThreadSuspensionInfo::WaitOnSuspendSemaphore()
+{
+#if USE_POSIX_SEMAPHORES
+ while (sem_wait(&m_semSusp) == -1)
+ {
+ ASSERT("sem_wait returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+#elif USE_SYSV_SEMAPHORES
+ while (semop(m_nSemsuspid, &m_sbSemwait, 1) == -1)
+ {
+ ASSERT("semop wait returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+#elif USE_PTHREAD_CONDVARS
+ int status;
+
+ // By the time we wait the target thread may have already signalled its suspension (in
+ // which case m_fSuspended will be TRUE and we shouldn't wait on the cond var). But we
+ // must check the flag and potentially wait atomically to avoid the race where we read
+ // the flag and the target thread sets it and signals before we have a chance to wait.
+
+ status = pthread_mutex_lock(&m_mutexSusp);
+ if (status != 0)
+ {
+ ASSERT("pthread_mutex_lock returned %d (%s)\n", status, strerror(status));
+ }
+
+ // If the target has already acknowledged the suspend we shouldn't wait.
+ while (!m_fSuspended)
+ {
+ // We got here before the target could signal. Wait on them (which atomically releases
+ // the mutex during the wait).
+ status = pthread_cond_wait(&m_condSusp, &m_mutexSusp);
+ if (status != 0)
+ {
+ ASSERT("pthread_cond_wait returned %d (%s)\n", status, strerror(status));
+ }
+ }
+
+ status = pthread_mutex_unlock(&m_mutexSusp);
+ if (status != 0)
+ {
+ ASSERT("pthread_mutex_unlock returned %d (%s)\n", status, strerror(status));
+ }
+#endif // USE_POSIX_SEMAPHORES
+}
+
+/*++
+Function:
+ PostOnResumeSemaphore
+
+PostOnResumeSemaphore is a utility function for a thread
+to post on its POSIX or SysV resume semaphore.
+--*/
+void
+CThreadSuspensionInfo::PostOnResumeSemaphore()
+{
+#if USE_POSIX_SEMAPHORES
+ if (sem_post(&m_semResume) == -1)
+ {
+ ASSERT("sem_post returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+#elif USE_SYSV_SEMAPHORES
+ if (semop(m_nSemrespid, &m_sbSempost, 1) == -1)
+ {
+ ASSERT("semop - post returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+#elif USE_PTHREAD_CONDVARS
+ int status;
+
+ // The resuming thread may not have entered the wait yet, in which case the cond var
+ // signal below will be a no-op. To prevent the race condition we set m_fResumed to
+ // TRUE first (which the resumer will take as an indication that no wait is required).
+ // But the setting of the flag and the signal must appear atomic to the resumer (as
+ // reading the flag and potentially waiting must appear to us) to avoid the race
+ // condition where the resumer reads the flag as FALSE, we set it and signal and the
+ // resumer then waits.
+
+ // Acquire the resume mutex. Once we enter the critical section the resumer has
+ // either gotten there before us (and is waiting for our signal) or is yet to even
+ // check the flag (so we can set it here to stop them attempting a wait).
+ status = pthread_mutex_lock(&m_mutexResume);
+ if (status != 0)
+ {
+ ASSERT("pthread_mutex_lock returned %d (%s)\n", status, strerror(status));
+ }
+
+ m_fResumed = TRUE;
+
+ status = pthread_cond_signal(&m_condResume);
+ if (status != 0)
+ {
+ ASSERT("pthread_cond_signal returned %d (%s)\n", status, strerror(status));
+ }
+
+ status = pthread_mutex_unlock(&m_mutexResume);
+ if (status != 0)
+ {
+ ASSERT("pthread_mutex_unlock returned %d (%s)\n", status, strerror(status));
+ }
+#endif // USE_POSIX_SEMAPHORES
+}
+
+/*++
+Function:
+ WaitOnResumeSemaphore
+
+WaitOnResumeSemaphore is a utility function for a thread
+to wait on its POSIX or SysV resume semaphore.
+--*/
+void
+CThreadSuspensionInfo::WaitOnResumeSemaphore()
+{
+#if USE_POSIX_SEMAPHORES
+ while (sem_wait(&m_semResume) == -1)
+ {
+ ASSERT("sem_wait returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+#elif USE_SYSV_SEMAPHORES
+ while (semop(m_nSemrespid, &m_sbSemwait, 1) == -1)
+ {
+ ASSERT("semop wait returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
+ }
+#elif USE_PTHREAD_CONDVARS
+ int status;
+
+ // By the time we wait the target thread may have already signalled its resumption (in
+ // which case m_fResumed will be TRUE and we shouldn't wait on the cond var). But we
+ // must check the flag and potentially wait atomically to avoid the race where we read
+ // the flag and the target thread sets it and signals before we have a chance to wait.
+
+ status = pthread_mutex_lock(&m_mutexResume);
+ if (status != 0)
+ {
+ ASSERT("pthread_mutex_lock returned %d (%s)\n", status, strerror(status));
+ }
+
+ // If the target has already acknowledged the resume we shouldn't wait.
+ while (!m_fResumed)
+ {
+ // We got here before the target could signal. Wait on them (which atomically releases
+ // the mutex during the wait).
+ status = pthread_cond_wait(&m_condResume, &m_mutexResume);
+ if (status != 0)
+ {
+ ASSERT("pthread_cond_wait returned %d (%s)\n", status, strerror(status));
+ }
+ }
+
+ status = pthread_mutex_unlock(&m_mutexResume);
+ if (status != 0)
+ {
+ ASSERT("pthread_mutex_unlock returned %d (%s)\n", status, strerror(status));
+ }
+#endif // USE_POSIX_SEMAPHORES
+}
+
+/*++
+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/pal/src/thread/tls.cpp b/src/pal/src/thread/tls.cpp
new file mode 100644
index 0000000000..ef6d02f00a
--- /dev/null
+++ b/src/pal/src/thread/tls.cpp
@@ -0,0 +1,226 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ tls.cpp
+
+Abstract:
+
+ Implementation of Thread local storage functions.
+
+
+
+--*/
+
+#include "pal/thread.hpp"
+#include "procprivate.hpp"
+
+#include <pthread.h>
+
+#include "pal/dbgmsg.h"
+#include "pal/misc.h"
+#include "pal/virtual.h"
+#include "pal/process.h"
+#include "pal/init.h"
+#include "pal/malloc.hpp"
+#include "pal_endian.h"
+
+#include <stddef.h>
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(THREAD);
+
+// In safemath.h, Template SafeInt uses macro _ASSERTE, which need to use variable
+// defdbgchan defined by SET_DEFAULT_DEBUG_CHANNEL. Therefore, the include statement
+// should be placed after the SET_DEFAULT_DEBUG_CHANNEL(THREAD)
+#include <safemath.h>
+
+/* This tracks the slots that are used for TlsAlloc. Its size in bits
+ must be the same as TLS_SLOT_SIZE in pal/thread.h. Since this is
+ static, it is initialized to 0, which is what we want. */
+static unsigned __int64 sTlsSlotFields;
+
+/*++
+Function:
+ TlsAlloc
+
+See MSDN doc.
+--*/
+DWORD
+PALAPI
+TlsAlloc(
+ VOID)
+{
+ DWORD dwIndex;
+ unsigned int i;
+
+ PERF_ENTRY(TlsAlloc);
+ ENTRY("TlsAlloc()\n");
+
+ /* Yes, this could be ever so slightly improved. It's not
+ likely to be called enough to matter, though, so we won't
+ optimize here until or unless we need to. */
+
+ PROCProcessLock();
+
+ for(i = 0; i < sizeof(sTlsSlotFields) * 8; i++)
+ {
+ if ((sTlsSlotFields & ((unsigned __int64) 1 << i)) == 0)
+ {
+ sTlsSlotFields |= ((unsigned __int64) 1 << i);
+ break;
+ }
+ }
+ if (i == sizeof(sTlsSlotFields) * 8)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ dwIndex = TLS_OUT_OF_INDEXES;
+ }
+ else
+ {
+ dwIndex = i;
+ }
+
+ PROCProcessUnlock();
+
+ LOGEXIT("TlsAlloc returns DWORD %u\n", dwIndex);
+ PERF_EXIT(TlsAlloc);
+ return dwIndex;
+}
+
+
+/*++
+Function:
+ TlsGetValue
+
+See MSDN doc.
+--*/
+LPVOID
+PALAPI
+TlsGetValue(
+ IN DWORD dwTlsIndex)
+{
+ CPalThread *pThread;
+
+ PERF_ENTRY(TlsGetValue);
+ ENTRY("TlsGetValue()\n");
+
+ if (dwTlsIndex == (DWORD) -1 || dwTlsIndex >= TLS_SLOT_SIZE)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ pThread = InternalGetCurrentThread();
+
+ /* From MSDN : "The TlsGetValue function calls SetLastError to clear a
+ thread's last error when it succeeds." */
+ pThread->SetLastError(NO_ERROR);
+
+ LOGEXIT("TlsGetValue \n" );
+ PERF_EXIT(TlsGetValue);
+
+ return pThread->tlsInfo.tlsSlots[dwTlsIndex];
+}
+
+
+/*++
+Function:
+ TlsSetValue
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+TlsSetValue(
+ IN DWORD dwTlsIndex,
+ IN LPVOID lpTlsValue)
+{
+ CPalThread *pThread;
+ BOOL bRet = FALSE;
+ PERF_ENTRY(TlsSetValue);
+ ENTRY("TlsSetValue(dwTlsIndex=%u, lpTlsValue=%p)\n", dwTlsIndex, lpTlsValue);
+
+ if (dwTlsIndex == (DWORD) -1 || dwTlsIndex >= TLS_SLOT_SIZE)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto EXIT;
+ }
+
+ pThread = InternalGetCurrentThread();
+ pThread->tlsInfo.tlsSlots[dwTlsIndex] = lpTlsValue;
+ bRet = TRUE;
+
+EXIT:
+ LOGEXIT("TlsSetValue returns BOOL %d\n", bRet);
+ PERF_EXIT(TlsSetValue);
+ return bRet;
+}
+
+
+/*++
+Function:
+ TlsFree
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+TlsFree(
+ IN DWORD dwTlsIndex)
+{
+ CPalThread *pThread;
+
+ PERF_ENTRY(TlsFree);
+ ENTRY("TlsFree(dwTlsIndex=%u)\n", dwTlsIndex);
+
+
+ if (dwTlsIndex == (DWORD) -1 || dwTlsIndex >= TLS_SLOT_SIZE)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ LOGEXIT("TlsFree returns BOOL FALSE\n");
+ PERF_EXIT(TlsFree);
+ return FALSE;
+ }
+
+ PROCProcessLock();
+
+ /* Reset all threads' values to zero for this index. */
+ for(pThread = pGThreadList;
+ pThread != NULL; pThread = pThread->GetNext())
+ {
+ pThread->tlsInfo.tlsSlots[dwTlsIndex] = 0;
+ }
+ sTlsSlotFields &= ~((unsigned __int64) 1 << dwTlsIndex);
+
+ PROCProcessUnlock();
+
+ LOGEXIT("TlsFree returns BOOL TRUE\n");
+ PERF_EXIT(TlsFree);
+ return TRUE;
+}
+
+PAL_ERROR
+CThreadTLSInfo::InitializePostCreate(
+ CPalThread *pThread,
+ SIZE_T threadId,
+ DWORD dwLwpId
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ if (pthread_setspecific(thObjKey, reinterpret_cast<void*>(pThread)))
+ {
+ ASSERT("Unable to set the thread object key's value\n");
+ palError = ERROR_INTERNAL_ERROR;
+ }
+
+ return palError;
+}
+