From d00ab151f8989ae05b830e9f557ee3e6b267aa13 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 21 Oct 2019 09:32:30 -0700 Subject: [PATCH] Cleanup memory copy helpers (dotnet/coreclr#27307) - Merge RuntimeImports that has just a few FCalls into Buffer. - Delete assembly implementation of memcpy for ARM. The implementation was outdated and it was used only on Windows ARM and only for fraction of memcpy callsites. - Delete unnecessary PInvoke into SysStringLen. We have a managed equivalent on System.Runtime.InteropServices.Marshal. - Delete large amount of redundant code between SecureString.Windows.cs and SecureString.Unix.cs, and switch SecureString to use Spans - Fold null check into IsWin32Atom Commit migrated from https://github.com/dotnet/coreclr/commit/17cbbeeac4c6e455d6fd34be22f38ef1f1f49834 --- .../System.Private.CoreLib.csproj | 3 - .../src/System/Buffer.CoreCLR.cs | 33 +- .../Runtime/InteropServices/Marshal.CoreCLR.cs | 19 +- .../src/System/Runtime/RuntimeImports.cs | 41 - src/coreclr/src/pal/inc/rt/palrt.h | 39 - src/coreclr/src/vm/CMakeLists.txt | 2 - src/coreclr/src/vm/arm/cgencpu.h | 2 - src/coreclr/src/vm/arm/memcpy.S | 39 - src/coreclr/src/vm/arm/memcpy.asm | 284 ------ src/coreclr/src/vm/arm/memcpy_crt.asm | 1001 -------------------- src/coreclr/src/vm/comutilnative.cpp | 7 +- src/coreclr/src/vm/comutilnative.h | 17 +- src/coreclr/src/vm/ecalllist.h | 12 +- src/coreclr/src/vm/excep.cpp | 8 - src/coreclr/src/vm/jithelpers.cpp | 25 - .../Windows/Crypt32/Interop.CryptProtectMemory.cs | 4 +- .../Windows/OleAut32/Interop.SysAllocStringLen.cs | 3 - .../Windows/OleAut32/Interop.SysStringLen.cs | 19 - .../src/System.Private.CoreLib.Shared.projitems | 2 - .../System.Private.CoreLib/src/System/Buffer.cs | 21 +- .../src/System/IO/UnmanagedMemoryStream.cs | 13 +- .../src/System/Number.BigInteger.cs | 10 +- .../System/Runtime/InteropServices/Marshal.Unix.cs | 16 +- .../Runtime/InteropServices/Marshal.Windows.cs | 53 +- .../src/System/Runtime/InteropServices/Marshal.cs | 18 +- .../src/System/Security/SafeBSTRHandle.cs | 82 -- .../src/System/Security/SecureString.Unix.cs | 300 +----- .../src/System/Security/SecureString.Windows.cs | 271 +----- .../src/System/Security/SecureString.cs | 327 ++++++- .../src/System/SpanHelpers.Char.cs | 8 +- .../src/System/SpanHelpers.cs | 4 +- 31 files changed, 456 insertions(+), 2227 deletions(-) delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs delete mode 100644 src/coreclr/src/vm/arm/memcpy.S delete mode 100644 src/coreclr/src/vm/arm/memcpy.asm delete mode 100644 src/coreclr/src/vm/arm/memcpy_crt.asm delete mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/OleAut32/Interop.SysStringLen.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Security/SafeBSTRHandle.cs diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index ca3624d..d4e5265 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -415,9 +415,6 @@ - - - true diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs index b4878dc..7675da8 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs @@ -23,26 +23,31 @@ namespace System [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool IsPrimitiveTypeArray(Array array); - // This method has a slightly different behavior on arm and other platforms. - // On arm this method behaves like memcpy and does not handle overlapping buffers. - // While on other platforms it behaves like memmove and handles overlapping buffers. - // This behavioral difference is unfortunate but intentional because - // 1. This method is given access to other internal dlls and this close to release we do not want to change it. - // 2. It is difficult to get this right for arm and again due to release dates we would like to visit it later. -#if ARM + // Non-inlinable wrapper around the QCall that avoids polluting the fast path + // with P/Invoke prolog/epilog. + [MethodImpl(MethodImplOptions.NoInlining)] + internal static unsafe void _ZeroMemory(ref byte b, nuint byteLength) + { + fixed (byte* bytePointer = &b) + { + __ZeroMemory(bytePointer, byteLength); + } + } + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + private static extern unsafe void __ZeroMemory(void* b, nuint byteLength); + [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe void Memcpy(byte* dest, byte* src, int len); -#else // ARM - [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static extern void BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + private static extern unsafe void __Memmove(byte* dest, byte* src, nuint len); + internal static unsafe void Memcpy(byte* dest, byte* src, int len) { Debug.Assert(len >= 0, "Negative length in memcpy!"); Memmove(dest, src, (nuint)len); } -#endif // ARM - - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern unsafe void __Memmove(byte* dest, byte* src, nuint len); // Used by ilmarshalers.cpp internal static unsafe void Memcpy(byte* pDest, int destIndex, byte[] src, int srcIndex, int len) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs index aa11b8e..34dd7c9 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs @@ -275,7 +275,7 @@ namespace System.Runtime.InteropServices public static void FreeHGlobal(IntPtr hglobal) { - if (!IsWin32Atom(hglobal)) + if (!IsNullOrWin32Atom(hglobal)) { if (IntPtr.Zero != Interop.Kernel32.LocalFree(hglobal)) { @@ -419,7 +419,7 @@ namespace System.Runtime.InteropServices public static void FreeCoTaskMem(IntPtr ptr) { - if (!IsWin32Atom(ptr)) + if (!IsNullOrWin32Atom(ptr)) { Interop.Ole32.CoTaskMemFree(ptr); } @@ -448,7 +448,7 @@ namespace System.Runtime.InteropServices public static void FreeBSTR(IntPtr ptr) { - if (!IsWin32Atom(ptr)) + if (!IsNullOrWin32Atom(ptr)) { Interop.OleAut32.SysFreeString(ptr); } @@ -461,12 +461,6 @@ namespace System.Runtime.InteropServices return IntPtr.Zero; } - // Overflow checking - if (s.Length + 1 < s.Length) - { - throw new ArgumentOutOfRangeException(nameof(s)); - } - IntPtr bstr = Interop.OleAut32.SysAllocStringLen(s, s.Length); if (bstr == IntPtr.Zero) { @@ -478,7 +472,12 @@ namespace System.Runtime.InteropServices public static string PtrToStringBSTR(IntPtr ptr) { - return PtrToStringUni(ptr, (int)Interop.OleAut32.SysStringLen(ptr)); + if (ptr == IntPtr.Zero) + { + throw new ArgumentNullException(nameof(ptr)); + } + + return PtrToStringUni(ptr, (int)(SysStringByteLen(ptr) / sizeof(char))); } #if FEATURE_COMINTEROP diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs deleted file mode 100644 index 1574812..0000000 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types -#if BIT64 -using nuint = System.UInt64; -#else -using nuint = System.UInt32; -#endif - -namespace System.Runtime -{ - public class RuntimeImports - { - // Non-inlinable wrapper around the QCall that avoids poluting the fast path - // with P/Invoke prolog/epilog. - [MethodImpl(MethodImplOptions.NoInlining)] - internal static unsafe void RhZeroMemory(ref byte b, nuint byteLength) - { - fixed (byte* bytePointer = &b) - { - RhZeroMemory(bytePointer, byteLength); - } - } - - internal static unsafe void RhZeroMemory(IntPtr p, UIntPtr byteLength) - { - RhZeroMemory((void*)p, (nuint)byteLength); - } - - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern unsafe void RhZeroMemory(void* b, nuint byteLength); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void RhBulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount); - } -} diff --git a/src/coreclr/src/pal/inc/rt/palrt.h b/src/coreclr/src/pal/inc/rt/palrt.h index 54e5ad7..913a8c1 100644 --- a/src/coreclr/src/pal/inc/rt/palrt.h +++ b/src/coreclr/src/pal/inc/rt/palrt.h @@ -714,34 +714,8 @@ typedef unsigned int ALG_ID; /******************* NLS ****************************************/ -typedef -enum tagMIMECONTF { - MIMECONTF_MAILNEWS = 0x1, - MIMECONTF_BROWSER = 0x2, - MIMECONTF_MINIMAL = 0x4, - MIMECONTF_IMPORT = 0x8, - MIMECONTF_SAVABLE_MAILNEWS = 0x100, - MIMECONTF_SAVABLE_BROWSER = 0x200, - MIMECONTF_EXPORT = 0x400, - MIMECONTF_PRIVCONVERTER = 0x10000, - MIMECONTF_VALID = 0x20000, - MIMECONTF_VALID_NLS = 0x40000, - MIMECONTF_MIME_IE4 = 0x10000000, - MIMECONTF_MIME_LATEST = 0x20000000, - MIMECONTF_MIME_REGISTRY = 0x40000000 - } MIMECONTF; - #define LCMAP_LOWERCASE 0x00000100 #define LCMAP_UPPERCASE 0x00000200 -#define LCMAP_SORTKEY 0x00000400 -#define LCMAP_BYTEREV 0x00000800 - -#define LCMAP_HIRAGANA 0x00100000 -#define LCMAP_KATAKANA 0x00200000 -#define LCMAP_HALFWIDTH 0x00400000 -#define LCMAP_FULLWIDTH 0x00800000 - -#define LCMAP_LINGUISTIC_CASING 0x01000000 // 8 characters for language // 8 characters for region @@ -751,23 +725,10 @@ enum tagMIMECONTF { // 1 null termination #define LOCALE_NAME_MAX_LENGTH 85 -#define LOCALE_SCOUNTRY 0x00000006 -#define LOCALE_SENGCOUNTRY 0x00001002 - -#define LOCALE_SLANGUAGE 0x00000002 -#define LOCALE_SENGLANGUAGE 0x00001001 - -#define LOCALE_SDATE 0x0000001D -#define LOCALE_STIME 0x0000001E - #define CSTR_LESS_THAN 1 #define CSTR_EQUAL 2 #define CSTR_GREATER_THAN 3 -#define NORM_IGNORENONSPACE 0x00000002 - -#define WC_COMPOSITECHECK 0x00000000 // NOTE: diff from winnls.h - /******************* shlwapi ************************************/ // note: diff in NULL handing and calling convetion diff --git a/src/coreclr/src/vm/CMakeLists.txt b/src/coreclr/src/vm/CMakeLists.txt index ed72c94..cebf370 100644 --- a/src/coreclr/src/vm/CMakeLists.txt +++ b/src/coreclr/src/vm/CMakeLists.txt @@ -686,7 +686,6 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM) ${ARCH_SOURCES_DIR}/asmhelpers.asm ${ARCH_SOURCES_DIR}/CrtHelpers.asm ${ARCH_SOURCES_DIR}/ehhelpers.asm - ${ARCH_SOURCES_DIR}/memcpy.asm ${ARCH_SOURCES_DIR}/patchedcode.asm ${ARCH_SOURCES_DIR}/PInvokeStubs.asm ) @@ -740,7 +739,6 @@ else(WIN32) ${ARCH_SOURCES_DIR}/asmhelpers.S ${ARCH_SOURCES_DIR}/crthelpers.S ${ARCH_SOURCES_DIR}/ehhelpers.S - ${ARCH_SOURCES_DIR}/memcpy.S ${ARCH_SOURCES_DIR}/patchedcode.S ${ARCH_SOURCES_DIR}/pinvokestubs.S ) diff --git a/src/coreclr/src/vm/arm/cgencpu.h b/src/coreclr/src/vm/arm/cgencpu.h index 95c68cc..61ba2ab 100644 --- a/src/coreclr/src/vm/arm/cgencpu.h +++ b/src/coreclr/src/vm/arm/cgencpu.h @@ -1366,6 +1366,4 @@ inline size_t GetARMInstructionLength(PBYTE pInstr) return GetARMInstructionLength(*(WORD*)pInstr); } -EXTERN_C void FCallMemcpy(BYTE* dest, BYTE* src, int len); - #endif // __cgencpu_h__ diff --git a/src/coreclr/src/vm/arm/memcpy.S b/src/coreclr/src/vm/arm/memcpy.S deleted file mode 100644 index 0c2c26e..0000000 --- a/src/coreclr/src/vm/arm/memcpy.S +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#include "unixasmmacros.inc" -#include "asmconstants.h" - -.syntax unified -.thumb - -// -// void *memcpy(void *dst, const void *src, size_t length) -// -// Copy a block of memory in a forward direction. -// - - NESTED_ENTRY FCallMemcpy, _TEXT, NoHandler - cmp r2, #0 - - beq LOCAL_LABEL(GC_POLL) - - PROLOG_PUSH "{r7, lr}" - PROLOG_STACK_SAVE r7 - - ldrb r3, [r0] - ldrb r3, [r1] - - blx C_FUNC(memcpy) - - EPILOG_POP "{r7, pc}" - -LOCAL_LABEL(GC_POLL): - ldr r0, =g_TrapReturningThreads - ldr r0, [r0] - cmp r0, #0 - it ne - bne C_FUNC(FCallMemCpy_GCPoll) - bx lr - NESTED_END_MARKED FCallMemcpy, _TEXT diff --git a/src/coreclr/src/vm/arm/memcpy.asm b/src/coreclr/src/vm/arm/memcpy.asm deleted file mode 100644 index 9a0e7d3..0000000 --- a/src/coreclr/src/vm/arm/memcpy.asm +++ /dev/null @@ -1,284 +0,0 @@ -; Licensed to the .NET Foundation under one or more agreements. -; The .NET Foundation licenses this file to you under the MIT license. -; See the LICENSE file in the project root for more information. - -; - -; - -; This is the fast memcpy implementation for ARM stolen from the CRT (original location -; vctools\crt\crtw32\string\arm\memcpy.asm) and modified to be compatible with CLR. -; -; For reference, the unmodified crt version of memcpy is preserved as memcpy_crt.asm - -#include "ksarm.h" -#include "asmmacros.h" - - IMPORT FCallMemCpy_GCPoll - IMPORT g_TrapReturningThreads - - AREA |.text|,ALIGN=5,CODE,READONLY - -; -; void *memcpy(void *dst, const void *src, size_t length) -; -; Copy a block of memory in a forward direction. -; - - ALIGN 32 - LEAF_ENTRY FCallMemcpy - - pld [r1] ; preload the first cache line - cmp r2, #16 ; less than 16 bytes? - mov r3, r0 ; use r3 as our destination - bhs.W __FCallMemcpy_large ; go to the large copy case directly. ".W" indicates encoding using 32bits - -CpySmal tbb [pc, r2] ; branch to specialized bits for small copies -__SwitchTable1_Copy -CTable dcb (Copy0 - CTable) / 2 ; 0B - dcb (Copy1 - CTable) / 2 ; 1B - dcb (Copy2 - CTable) / 2 ; 2B - dcb (Copy3 - CTable) / 2 ; 3B - dcb (Copy4 - CTable) / 2 ; 4B - dcb (Copy5 - CTable) / 2 ; 5B - dcb (Copy6 - CTable) / 2 ; 6B - dcb (Copy7 - CTable) / 2 ; 7B - dcb (Copy8 - CTable) / 2 ; 8B - dcb (Copy9 - CTable) / 2 ; 9B - dcb (Copy10 - CTable) / 2 ; 10B - dcb (Copy11 - CTable) / 2 ; 11B - dcb (Copy12 - CTable) / 2 ; 12B - dcb (Copy13 - CTable) / 2 ; 13B - dcb (Copy14 - CTable) / 2 ; 14B - dcb (Copy15 - CTable) / 2 ; 15B -__SwitchTableEnd_Copy - -Copy1 ldrb r2, [r1] - strb r2, [r3] -Copy0 b GC_POLL - -Copy2 ldrh r2, [r1] - strh r2, [r3] - b GC_POLL - -Copy3 ldrh r2, [r1] - ldrb r1, [r1, #2] - strh r2, [r3] - strb r1, [r3, #2] - b GC_POLL - -Copy4 ldr r2, [r1] - str r2, [r3] - b GC_POLL - -Copy5 ldr r2, [r1] - ldrb r1, [r1, #4] - str r2, [r3] - strb r1, [r3, #4] - b GC_POLL - -Copy6 ldr r2, [r1] - ldrh r1, [r1, #4] - str r2, [r3] - strh r1, [r3, #4] - b GC_POLL - -Copy7 ldr r12, [r1] - ldrh r2, [r1, #4] - ldrb r1, [r1, #6] - str r12, [r3] - strh r2, [r3, #4] - strb r1, [r3, #6] - b GC_POLL - -Copy8 ldr r2, [r1] - ldr r1, [r1, #4] - str r2, [r3] - str r1, [r3, #4] - b GC_POLL - -Copy9 ldr r12, [r1] - ldr r2, [r1, #4] - ldrb r1, [r1, #8] - str r12, [r3] - str r2, [r3, #4] - strb r1, [r3, #8] - b GC_POLL - -Copy10 ldr r12, [r1] - ldr r2, [r1, #4] - ldrh r1, [r1, #8] - str r12, [r3] - str r2, [r3, #4] - strh r1, [r3, #8] - b GC_POLL - -Copy11 ldr r12, [r1] - ldr r2, [r1, #4] - str r12, [r3] - str r2, [r3, #4] - ldrh r2, [r1, #8] - ldrb r1, [r1, #10] - strh r2, [r3, #8] - strb r1, [r3, #10] - b GC_POLL - -Copy12 ldr r12, [r1] - ldr r2, [r1, #4] - ldr r1, [r1, #8] - str r12, [r3] - str r2, [r3, #4] - str r1, [r3, #8] - b GC_POLL - -Copy13 ldr r12, [r1] - ldr r2, [r1, #4] - str r12, [r3] - str r2, [r3, #4] - ldr r2, [r1, #8] - ldrb r1, [r1, #12] - str r2, [r3, #8] - strb r1, [r3, #12] - b GC_POLL - -Copy14 ldr r12, [r1] - ldr r2, [r1, #4] - str r12, [r3] - str r2, [r3, #4] - ldr r2, [r1, #8] - ldrh r1, [r1, #12] - str r2, [r3, #8] - strh r1, [r3, #12] - b GC_POLL - -Copy15 ldr r12, [r1] - ldr r2, [r1, #4] - str r12, [r3] - str r2, [r3, #4] - ldr r12, [r1, #8] - ldrh r2, [r1, #12] - ldrb r1, [r1, #14] - str r12, [r3, #8] - strh r2, [r3, #12] - strb r1, [r3, #14] -GC_POLL - ldr r0, =g_TrapReturningThreads - ldr r0, [r0] - cmp r0, #0 - bne FCallMemCpy_GCPoll - - bx lr - - LEAF_END FCallMemcpy - - -; -; __memcpy_forward_large_integer (internal calling convention) -; -; Copy large (>= 16 bytes) blocks of memory in a forward direction, -; using integer registers only. -; - - ALIGN 32 - NESTED_ENTRY __FCallMemcpy_large - - PROLOG_NOP lsls r12, r3, #31 ; C = bit 1, N = bit 0 - PROLOG_PUSH {r4-r9, r11, lr} - -; -; Align destination to a word boundary -; - - bpl %F1 - ldrb r4, [r1], #1 ; fetch byte - subs r2, r2, #1 ; decrement count - strb r4, [r3], #1 ; store byte - lsls r12, r3, #31 ; compute updated status -1 - bcc %F2 ; if already aligned, just skip ahead - ldrh r4, [r1], #2 ; fetch halfword - subs r2, r2, #2 ; decrement count - strh r4, [r3], #2 ; store halfword -2 - tst r1, #3 ; is the source now word-aligned? - bne %F20 ; if not, we have to use the slow path - -; -; Source is word-aligned; fast case -; - -10 - subs r2, r2, #32 ; take 32 off the top - blo %F13 ; if not enough, recover and do small copies - subs r2, r2, #32 ; take off another 32 - pld [r1, #32] ; pre-load one block ahead - blo %F12 ; skip the loop if that's all we have -11 - pld [r1, #64] ; prefetch ahead - subs r2, r2, #32 ; count the bytes for this block - ldm r1!, {r4-r9, r12, lr} ; load 32 bytes - stm r3!, {r4-r9, r12, lr} ; store 32 bytes - bhs %B11 ; keep going until we're done -12 - ldm r1!, {r4-r9, r12, lr} ; load 32 bytes - stm r3!, {r4-r9, r12, lr} ; store 32 bytes -13 - adds r2, r2, #(32 - 8) ; recover original count, and pre-decrement - blo %F15 ; if not enough remaining, skip this loop -14 - subs r2, r2, #8 ; decrement count - ldrd r4, r5, [r1], #8 ; fetch pair of words - strd r4, r5, [r3], #8 ; store pair of words - bhs %B14 ; loop while we still have data remaining -15 - adds r2, r2, #8 ; recover final count - - EPILOG_POP {r4-r9, r11, lr} - EPILOG_NOP bne CpySmal ; if some left, continue with small - EPILOG_BRANCH GC_POLL - -; -; Source is not word-aligned; slow case -; - -20 - subs r2, r2, #64 ; pre-decrement to simplify the loop - blo %23 ; skip over the loop if we don't have enough - pld [r1, #32] ; pre-load one block ahead -21 - pld [r1, #64] ; prefetch ahead - ldr r4, [r1, #0] ; load 32 bytes - ldr r5, [r1, #4] ; - ldr r6, [r1, #8] ; - ldr r7, [r1, #12] ; - ldr r8, [r1, #16] ; - ldr r9, [r1, #20] ; - ldr r12, [r1, #24] ; - ldr lr, [r1, #28] ; - adds r1, r1, #32 ; update pointer - subs r2, r2, #32 ; count the bytes for this block - stm r3!, {r4-r9, r12, lr} ; store 32 bytes - bhs %B21 ; keep going until we're done -23 - adds r2, r2, #(64 - 8) ; recover original count, and pre-decrement - blo %F25 ; if not enough remaining, skip this loop -24 - ldr r4, [r1] ; fetch pair of words - ldr r5, [r1, #4] ; - adds r1, r1, #8 ; update pointer - subs r2, r2, #8 ; decrement count - strd r4, r5, [r3], #8 ; store pair of words - bhs %B24 ; loop while we still have data remaining -25 - adds r2, r2, #8 ; recover final count - - EPILOG_POP {r4-r9, r11, lr} - EPILOG_NOP bne CpySmal ; if some left, continue with small - EPILOG_BRANCH GC_POLL - - EXPORT FCallMemcpy_End ; this is used to place the entire -FCallMemcpy_End ; implementation in av-exclusion list - - NESTED_END __FCallMemcpy_large - - END diff --git a/src/coreclr/src/vm/arm/memcpy_crt.asm b/src/coreclr/src/vm/arm/memcpy_crt.asm deleted file mode 100644 index 5e3a97e..0000000 --- a/src/coreclr/src/vm/arm/memcpy_crt.asm +++ /dev/null @@ -1,1001 +0,0 @@ -; Licensed to the .NET Foundation under one or more agreements. -; The .NET Foundation licenses this file to you under the MIT license. -; See the LICENSE file in the project root for more information. - -; - -; - -#include "ksarm.h" - -#if !defined PF_ARM_EXTERNAL_CACHE_AVAILABLE -#define PF_ARM_EXTERNAL_CACHE_AVAILABLE 0x1a -#endif - -#if !defined(_BOOTCRT_) - - DATAAREA - -__memcpy_forward_large_func dcd __memcpy_decide - EXPORT __memcpy_forward_large_func -__memcpy_reverse_large_func dcd __memcpy_decide - EXPORT __memcpy_reverse_large_func - -#endif - - AREA |.text|,ALIGN=5,CODE,READONLY - -; -; void *memcpy(void *dst, const void *src, size_t length) -; -; Copy a block of memory in a forward direction. -; - - ALIGN 32 - LEAF_ENTRY memcpy - - ALTERNATE_ENTRY __memcpy_forward_new - - pld [r1] ; preload the first cache line - cmp r2, #16 ; less than 16 bytes? - mov r3, r0 ; use r3 as our destination - bhs CpyLrge ; go to the small copy case directly - -CpySmal tbb [pc, r2] ; branch to specialized bits for small copies -__SwitchTable1_Copy -CTable dcb (Copy0 - CTable) / 2 ; 0B - dcb (Copy1 - CTable) / 2 ; 1B - dcb (Copy2 - CTable) / 2 ; 2B - dcb (Copy3 - CTable) / 2 ; 3B - dcb (Copy4 - CTable) / 2 ; 4B - dcb (Copy5 - CTable) / 2 ; 5B - dcb (Copy6 - CTable) / 2 ; 6B - dcb (Copy7 - CTable) / 2 ; 7B - dcb (Copy8 - CTable) / 2 ; 8B - dcb (Copy9 - CTable) / 2 ; 9B - dcb (Copy10 - CTable) / 2 ; 10B - dcb (Copy11 - CTable) / 2 ; 11B - dcb (Copy12 - CTable) / 2 ; 12B - dcb (Copy13 - CTable) / 2 ; 13B - dcb (Copy14 - CTable) / 2 ; 14B - dcb (Copy15 - CTable) / 2 ; 15B -__SwitchTableEnd_Copy - -Copy1 ldrb r2, [r1] - strb r2, [r3] -Copy0 bx lr - -Copy2 ldrh r2, [r1] - strh r2, [r3] - bx lr - -Copy3 ldrh r2, [r1] - ldrb r1, [r1, #2] - strh r2, [r3] - strb r1, [r3, #2] - bx lr - -Copy4 ldr r2, [r1] - str r2, [r3] - bx lr - -Copy5 ldr r2, [r1] - ldrb r1, [r1, #4] - str r2, [r3] - strb r1, [r3, #4] - bx lr - -Copy6 ldr r2, [r1] - ldrh r1, [r1, #4] - str r2, [r3] - strh r1, [r3, #4] - bx lr - -Copy7 ldr r12, [r1] - ldrh r2, [r1, #4] - ldrb r1, [r1, #6] - str r12, [r3] - strh r2, [r3, #4] - strb r1, [r3, #6] - bx lr - -Copy8 ldr r2, [r1] - ldr r1, [r1, #4] - str r2, [r3] - str r1, [r3, #4] - bx lr - -Copy9 ldr r12, [r1] - ldr r2, [r1, #4] - ldrb r1, [r1, #8] - str r12, [r3] - str r2, [r3, #4] - strb r1, [r3, #8] - bx lr - -Copy10 ldr r12, [r1] - ldr r2, [r1, #4] - ldrh r1, [r1, #8] - str r12, [r3] - str r2, [r3, #4] - strh r1, [r3, #8] - bx lr - -Copy11 ldr r12, [r1] - ldr r2, [r1, #4] - str r12, [r3] - str r2, [r3, #4] - ldrh r2, [r1, #8] - ldrb r1, [r1, #10] - strh r2, [r3, #8] - strb r1, [r3, #10] - bx lr - -Copy12 ldr r12, [r1] - ldr r2, [r1, #4] - ldr r1, [r1, #8] - str r12, [r3] - str r2, [r3, #4] - str r1, [r3, #8] - bx lr - -Copy13 ldr r12, [r1] - ldr r2, [r1, #4] - str r12, [r3] - str r2, [r3, #4] - ldr r2, [r1, #8] - ldrb r1, [r1, #12] - str r2, [r3, #8] - strb r1, [r3, #12] - bx lr - -Copy14 ldr r12, [r1] - ldr r2, [r1, #4] - str r12, [r3] - str r2, [r3, #4] - ldr r2, [r1, #8] - ldrh r1, [r1, #12] - str r2, [r3, #8] - strh r1, [r3, #12] - bx lr - -Copy15 ldr r12, [r1] - ldr r2, [r1, #4] - str r12, [r3] - str r2, [r3, #4] - ldr r12, [r1, #8] - ldrh r2, [r1, #12] - ldrb r1, [r1, #14] - str r12, [r3, #8] - strh r2, [r3, #12] - strb r1, [r3, #14] - bx lr - -CpyLrge - -#if defined(_BOOTCRT_) - - b __memcpy_forward_large_integer ; always use integer in boot code - -#else - - eor r12, r0, r1 ; see if src/dst are equally aligned - tst r12, #3 ; at least to a 4 byte boundary - bne __memcpy_forward_large_neon ; if not, always use NEON - mov32 r12, __memcpy_forward_large_func ; otherwise, load the large function pointer - ldr pc, [r12] ; and call it - -#endif - - LEAF_END memcpy - - -; -; __memcpy_forward_large_integer (internal calling convention) -; -; Copy large (>= 16 bytes) blocks of memory in a forward direction, -; using integer registers only. -; - - ALIGN 32 - NESTED_ENTRY __memcpy_forward_large_integer_wrapper - -__memcpy_forward_large_integer - - PROLOG_NOP lsls r12, r3, #31 ; C = bit 1, N = bit 0 - PROLOG_PUSH {r4-r9, r11, lr} - -; -; Align destination to a word boundary -; - - bpl %F1 - ldrb r4, [r1], #1 ; fetch byte - subs r2, r2, #1 ; decrement count - strb r4, [r3], #1 ; store byte - lsls r12, r3, #31 ; compute updated status -1 - bcc %F2 ; if already aligned, just skip ahead - ldrh r4, [r1], #2 ; fetch halfword - subs r2, r2, #2 ; decrement count - strh r4, [r3], #2 ; store halfword -2 - tst r1, #3 ; is the source now word-aligned? - bne %F20 ; if not, we have to use the slow path - -; -; Source is word-aligned; fast case -; - -10 - subs r2, r2, #32 ; take 32 off the top - blo %F13 ; if not enough, recover and do small copies - subs r2, r2, #32 ; take off another 32 - pld [r1, #32] ; pre-load one block ahead - blo %F12 ; skip the loop if that's all we have -11 - pld [r1, #64] ; prefetch ahead - subs r2, r2, #32 ; count the bytes for this block - ldm r1!, {r4-r9, r12, lr} ; load 32 bytes - stm r3!, {r4-r9, r12, lr} ; store 32 bytes - bhs %B11 ; keep going until we're done -12 - ldm r1!, {r4-r9, r12, lr} ; load 32 bytes - stm r3!, {r4-r9, r12, lr} ; store 32 bytes -13 - adds r2, r2, #(32 - 8) ; recover original count, and pre-decrement - blo %F15 ; if not enough remaining, skip this loop -14 - subs r2, r2, #8 ; decrement count - ldrd r4, r5, [r1], #8 ; fetch pair of words - strd r4, r5, [r3], #8 ; store pair of words - bhs %B14 ; loop while we still have data remaining -15 - adds r2, r2, #8 ; recover final count - - EPILOG_POP {r4-r9, r11, lr} - EPILOG_NOP bne CpySmal ; if some left, continue with small - EPILOG_RETURN ; else just return - -; -; Source is not word-aligned; slow case -; - -20 - subs r2, r2, #64 ; pre-decrement to simplify the loop - blo %23 ; skip over the loop if we don't have enough - pld [r1, #32] ; pre-load one block ahead -21 - pld [r1, #64] ; prefetch ahead - ldr r4, [r1, #0] ; load 32 bytes - ldr r5, [r1, #4] ; - ldr r6, [r1, #8] ; - ldr r7, [r1, #12] ; - ldr r8, [r1, #16] ; - ldr r9, [r1, #20] ; - ldr r12, [r1, #24] ; - ldr lr, [r1, #28] ; - adds r1, r1, #32 ; update pointer - subs r2, r2, #32 ; count the bytes for this block - stm r3!, {r4-r9, r12, lr} ; store 32 bytes - bhs %B21 ; keep going until we're done -23 - adds r2, r2, #(64 - 8) ; recover original count, and pre-decrement - blo %F25 ; if not enough remaining, skip this loop -24 - ldr r4, [r1] ; fetch pair of words - ldr r5, [r1, #4] ; - adds r1, r1, #8 ; update pointer - subs r2, r2, #8 ; decrement count - strd r4, r5, [r3], #8 ; store pair of words - bhs %B24 ; loop while we still have data remaining -25 - adds r2, r2, #8 ; recover final count - - EPILOG_POP {r4-r9, r11, lr} - EPILOG_NOP bne CpySmal ; if some left, continue with small - EPILOG_RETURN ; else just return - - NESTED_END __memcpy_forward_large_integer - - -; -; __memcpy_forward_large_neon (internal calling convention) -; -; Copy large (>= 16 bytes) blocks of memory in a forward direction, -; using NEON registers. -; - -#if !defined(_BOOTCRT_) - - ALIGN 32 - NESTED_ENTRY __memcpy_forward_large_neon_wrapper - -__memcpy_forward_large_neon - - PROLOG_PUSH {r4-r5, r11, lr} - - subs r2, r2, #32 ; pre-decrement to simplify the loop - blo %F13 ; skip over the loop if we don't have enough - subs r2, r2, #32 ; pre-decrement to simplify the loop - pld [r1, #32] ; pre-load one block ahead - blo %F12 ; skip over the loop if we don't have enough -11 - pld [r1, #64] ; prefetch ahead - subs r2, r2, #32 ; count the bytes for this block - vld1.8 {d0-d3}, [r1]! ; load 32 bytes - vst1.8 {d0-d3}, [r3]! ; store 32 bytes - bhs %B11 ; keep going until we're done -12 - vld1.8 {d0-d3}, [r1]! ; load 32 bytes - vst1.8 {d0-d3}, [r3]! ; store 32 bytes -13 - adds r2, r2, #(32 - 8) ; recover original count, and pre-decrement - blo %F15 ; if not enough remaining, skip this loop -14 - ldr r4, [r1] ; fetch pair of words - ldr r5, [r1, #4] ; - adds r1, r1, #8 ; update pointer - str r4, [r3] ; store pair of words - str r5, [r3, #4] ; - adds r3, r3, #8 - subs r2, r2, #8 ; decrement count - bhs %B14 ; loop while we still have data remaining -15 - adds r2, r2, #8 ; recover final count - - EPILOG_POP {r4-r5, r11, lr} - EPILOG_NOP bne CpySmal ; if some left, continue with small - EPILOG_RETURN ; else just return - - NESTED_END __memcpy_forward_large_neon - -#endif - - -; -; void *memmove(void *dst, const void *src, size_t length) -; -; Copy a block of memory in a forward or reverse direction, ensuring that -; overlapping source/destination regions are copied correctly. -; - - ALIGN 32 - LEAF_ENTRY memmove - - subs r3, r0, r1 ; compute dest - source - cmp r3, r2 ; compare against size - bhs memcpy ; if no overlap, we can just do memcpy - - ALTERNATE_ENTRY __memcpy_reverse_new - - cmp r2, #16 ; less than 16 bytes? - pld [r1] ; preload the first cache line - bhs MovLrge ; go to the small copy case directly - -MovSmal tbb [pc, r2] ; branch to specialized bits for small copies -__SwitchTable1_Move -MTable dcb (Move0 - MTable) / 2 ; 0B - dcb (Move1 - MTable) / 2 ; 1B - dcb (Move2 - MTable) / 2 ; 2B - dcb (Move3 - MTable) / 2 ; 3B - dcb (Move4 - MTable) / 2 ; 4B - dcb (Move5 - MTable) / 2 ; 5B - dcb (Move6 - MTable) / 2 ; 6B - dcb (Move7 - MTable) / 2 ; 7B - dcb (Move8 - MTable) / 2 ; 8B - dcb (Move9 - MTable) / 2 ; 9B - dcb (Move10 - MTable) / 2 ; 10B - dcb (Move11 - MTable) / 2 ; 11B - dcb (Move12 - MTable) / 2 ; 12B - dcb (Move13 - MTable) / 2 ; 13B - dcb (Move14 - MTable) / 2 ; 14B - dcb (Move15 - MTable) / 2 ; 15B -__SwitchTableEnd_Move - -Move1 ldrb r2, [r1] - strb r2, [r0] -Move0 bx lr - -Move2 ldrh r2, [r1] - strh r2, [r0] - bx lr - -Move3 ldrh r2, [r1] - ldrb r1, [r1, #2] - strh r2, [r0] - strb r1, [r0, #2] - bx lr - -Move4 ldr r2, [r1] - str r2, [r0] - bx lr - -Move5 ldr r2, [r1] - ldrb r1, [r1, #4] - str r2, [r0] - strb r1, [r0, #4] - bx lr - -Move6 ldr r2, [r1] - ldrh r1, [r1, #4] - str r2, [r0] - strh r1, [r0, #4] - bx lr - -Move7 ldr r3, [r1] - ldrh r2, [r1, #4] - ldrb r1, [r1, #6] - str r3, [r0] - strh r2, [r0, #4] - strb r1, [r0, #6] - bx lr - -Move8 ldr r2, [r1] - ldr r1, [r1, #4] - str r2, [r0] - str r1, [r0, #4] - bx lr - -Move9 ldr r3, [r1] - ldr r2, [r1, #4] - ldrb r1, [r1, #8] - str r3, [r0] - str r2, [r0, #4] - strb r1, [r0, #8] - bx lr - -Move10 ldr r3, [r1] - ldr r2, [r1, #4] - ldrh r1, [r1, #8] - str r3, [r0] - str r2, [r0, #4] - strh r1, [r0, #8] - bx lr - -Move11 ldr r12, [r1] - ldr r3, [r1, #4] - ldrh r2, [r1, #8] - ldrb r1, [r1, #10] - str r12, [r0] - str r3, [r0, #4] - strh r2, [r0, #8] - strb r1, [r0, #10] - bx lr - -Move12 ldr r12, [r1] - ldr r2, [r1, #4] - ldr r1, [r1, #8] - str r12, [r0] - str r2, [r0, #4] - str r1, [r0, #8] - bx lr - -Move13 ldr r12, [r1] - ldr r3, [r1, #4] - ldr r2, [r1, #8] - ldrb r1, [r1, #12] - str r12, [r0] - str r3, [r0, #4] - str r2, [r0, #8] - strb r1, [r0, #12] - bx lr - -Move14 ldr r12, [r1] - ldr r3, [r1, #4] - ldr r2, [r1, #8] - ldrh r1, [r1, #12] - str r12, [r0] - str r3, [r0, #4] - str r2, [r0, #8] - strh r1, [r0, #12] - bx lr - -Move15 ldrh r3, [r1, #12] - ldrb r2, [r1, #14] - strh r3, [r0, #12] - strb r2, [r0, #14] - ldr r3, [r1] - ldr r2, [r1, #4] - ldr r1, [r1, #8] - str r3, [r0] - str r2, [r0, #4] - str r1, [r0, #8] - bx lr - -MovLrge - -#if defined(_BOOTCRT_) - - b __memcpy_reverse_large_integer ; always use integer in boot code - -#else - - eor r12, r0, r1 ; see if src/dst are equally aligned - tst r12, #3 ; at least to a 4 byte boundary - bne __memcpy_reverse_large_neon ; if not, always use NEON - mov32 r12, __memcpy_reverse_large_func - ldr pc, [r12] - -#endif - - LEAF_END memmove - - -; -; __memcpy_reverse_large_integer (internal calling convention) -; -; Copy large (>= 16 bytes) block of memory in a reverse direction, -; using NEON registers. -; - - ALIGN 32 - NESTED_ENTRY __memcpy_reverse_large_integer_wrapper - -__memcpy_reverse_large_integer - - PROLOG_NOP adds r3, r0, r2 ; advance destination to end - PROLOG_NOP adds r1, r1, r2 ; advance source to end - PROLOG_NOP lsls r12, r3, #31 ; C = bit 1, N = bit 0 - PROLOG_NOP pld [r1, #-32] ; pre-load one block ahead - PROLOG_PUSH {r4-r9, r11, lr} - -; -; Align destination to a word boundary -; - - bpl %F1 - ldrb r4, [r1, #-1]! ; fetch byte - subs r2, r2, #1 ; decrement count - strb r4, [r3, #-1]! ; store byte - lsls r12, r3, #31 ; compute updated status -1 - bcc %F2 ; if already aligned, just skip ahead - ldrh r4, [r1, #-2]! ; fetch halfword - subs r2, r2, #2 ; decrement count - strh r4, [r3, #-2]! ; store halfword -2 - tst r1, #3 ; is the source now word-aligned? - bne %F20 ; if not, we have to use the slow path - -; -; Source is word-aligned; fast case -; - -10 - subs r2, r2, #32 ; pre-decrement to simplify the loop - blo %F13 ; skip over the loop if we don't have enough - subs r2, r2, #32 ; pre-decrement to simplify the loop - pld [r1, #-64] ; pre-load one block ahead - blo %F12 ; skip over the loop if we don't have enough -11 - pld [r1, #-96] ; prefetch ahead - subs r2, r2, #32 ; count the bytes for this block - ldmdb r1!, {r4-r9, r12, lr} ; load 32 bytes - stmdb r3!, {r4-r9, r12, lr} ; store 32 bytes - bhs %B11 ; keep going until we're done -12 - ldmdb r1!, {r4-r9, r12, lr} ; load 32 bytes - stmdb r3!, {r4-r9, r12, lr} ; store 32 bytes -13 - adds r2, r2, #(32 - 8) ; recover original count, and pre-decrement - blo %F15 ; if not enough remaining, skip this loop -14 - subs r2, r2, #8 ; decrement count - ldrd r4, r5, [r1, #-8]! ; fetch pair of words - strd r4, r5, [r3, #-8]! ; store pair of words - bhs %B14 ; loop while we still have data remaining -15 - adds r2, r2, #8 ; determine final count - subs r1, r1, r2 ; recover original source - - EPILOG_POP {r4-r9, r11, lr} - EPILOG_NOP bne MovSmal ; if some left, continue with small - EPILOG_RETURN ; else just return - - -; -; Source is not word-aligned; slow case -; - -20 - subs r2, r2, #64 ; pre-decrement to simplify the loop - blo %F23 ; skip over the loop if we don't have enough - pld [r1, #-64] ; pre-load one block ahead -21 - pld [r1, #-96] ; prefetch ahead - subs r2, r2, #32 ; count the bytes for this block - ldr r4, [r1, #-32]! ; load 32 bytes - ldr r5, [r1, #4] ; - ldr r6, [r1, #8] ; - ldr r7, [r1, #12] ; - ldr r8, [r1, #16] ; - ldr r9, [r1, #20] ; - ldr r12, [r1, #24] ; - ldr lr, [r1, #28] ; - stmdb r3!, {r4-r9, r12, lr} ; store 32 bytes - bhs %B21 ; keep going until we're done -23 - adds r2, r2, #(64 - 8) ; recover original count, and pre-decrement - blo %F25 ; if not enough remaining, skip this loop -24 - subs r2, r2, #8 ; decrement count - ldr r4, [r1, #-8]! ; fetch pair of words - ldr r5, [r1, #4] ; - strd r4, r5, [r3, #-8]! ; store pair of words - bhs %B24 ; loop while we still have data remaining -25 - adds r2, r2, #8 ; determine final count - subs r1, r1, r2 ; recover original source - - EPILOG_POP {r4-r9, r11, lr} - EPILOG_NOP bne MovSmal ; if some left, continue with small - EPILOG_RETURN ; else just return - - NESTED_END __memcpy_reverse_large_integer - - -; -; __memcpy_reverse_large_neon (internal calling convention) -; -; Copy large (>= 16 bytes) block of memory in a reverse direction, -; using NEON registers. -; - -#if !defined(_BOOTCRT_) - - ALIGN 32 - NESTED_ENTRY __memcpy_reverse_large_neon_wrapper - -__memcpy_reverse_large_neon - - PROLOG_NOP adds r3, r0, r2 ; advance destination to end - PROLOG_NOP adds r1, r1, r2 ; advance source to end - PROLOG_NOP lsls r12, r3, #31 ; C = bit 1, N = bit 0 - PROLOG_NOP pld [r1, #-32] ; pre-load one block ahead - PROLOG_PUSH {r4-r5, r11, lr} - -; -; Align destination to a word boundary -; - - bpl %F1 - ldrb r4, [r1, #-1]! ; fetch byte - subs r2, r2, #1 ; decrement count - strb r4, [r3, #-1]! ; store byte - lsls r12, r3, #31 ; compute updated status -1 - bcc %F2 ; if already aligned, just skip ahead - ldrh r4, [r1, #-2]! ; fetch halfword - subs r2, r2, #2 ; decrement count - strh r4, [r3, #-2]! ; store halfword -2 - -; -; Perform main copy -; - - subs r2, r2, #32 ; pre-decrement to simplify the loop - blo %F13 ; skip over the loop if we don't have enough - subs r2, r2, #32 ; pre-decrement to simplify the loop - pld [r1, #-64] ; pre-load one block ahead - blo %F12 ; skip over the loop if we don't have enough -11 - pld [r1, #-96] ; prefetch ahead - subs r1, r1, #32 - subs r3, r3, #32 - subs r2, r2, #32 ; count the bytes for this block - vld1.8 {d0-d3}, [r1] ; load 32 bytes - vst1.8 {d0-d3}, [r3] ; store 32 bytes - bhs %B11 ; keep going until we're done -12 - subs r1, r1, #32 - subs r3, r3, #32 - vld1.8 {d0-d3}, [r1] ; load 32 bytes - vst1.8 {d0-d3}, [r3] ; store 32 bytes -13 - adds r2, r2, #(32 - 8) ; recover original count, and pre-decrement - blo %F15 ; if not enough remaining, skip this loop -14 - ldr r4, [r1, #-8]! ; fetch pair of words - ldr r5, [r1, #4] ; fetch pair of words - subs r2, r2, #8 ; decrement count - str r4, [r3, #-8]! ; store pair of words - str r5, [r3, #4] - bhs %B14 ; loop while we still have data remaining -15 - adds r2, r2, #8 ; determine final count - subs r1, r1, r2 ; recover original source - - EPILOG_POP {r4-r5, r11, lr} - EPILOG_NOP bne MovSmal ; if some left, continue with small - EPILOG_RETURN ; else just return - - NESTED_END __memcpy_reverse_large_neon - -#endif - - -; -; __memcpy_decide (internal calling convention) -; -; Determine whether to use integer or NEON for future memcpy's. -; - -#if !defined(_BOOTCRT_) - - ALIGN 32 - NESTED_ENTRY __memcpy_decide_wrapper - -__memcpy_decide - - PROLOG_PUSH {r4-r5, r11, lr} - - ; - ; We want to use integer memcpy's on the A9, which has an external cache. - ; - ; First determine if we're in user or kernel mode. Reading CPSR - ; from user mode will either return the proper 5 mode bits, or all 0s. - ; Conveniently, user mode is 0x10, and there is no mode 0x00, so if - ; we read CPSR and the low 4 bits are 0, that's good enough. - ; - - mrs r4, cpsr ; get CPSR - ands r4, r4, #0xf ; isolate the low 4 bits of the mode - beq %F1 ; if 0, we're in user mode - - ; - ; If we are in kernel mode, read the MIDR directly. - ; - - CP_READ r4, CP15_MIDR ; read main ID register - ubfx r5, r4, #24, #8 ; get implementer - lsrs r4, r4, #4 ; shift off revision field - cmp r5, #0x41 ; is implementer == ARM? - bne %F3 ; if not, use NEON - bfc r4, #12, #20 ; clear upper bits - ldr r5, =0xc09 ; A9 signature - cmp r4, r5 ; is this an A9? - bne %F3 ; if not, use NEON - b %F2 ; otherwise, use integer - - ; - ; If we are in user mode, check the "external cache available" flag - ; -1 - ldr r4, =MM_SHARED_USER_DATA_VA + UsProcessorFeatures + PF_ARM_EXTERNAL_CACHE_AVAILABLE - ldrb r4, [r4] ; get external cache bit - cbz r4, %F3 ; if no external cache, do NEON - - ; - ; Register for integer functions - ; -2 - ldr r4, =__memcpy_forward_large_integer ; select integer functions - ldr r5, =__memcpy_forward_large_func ; - str r4, [r5] ; - ldr r4, =__memcpy_reverse_large_integer ; select integer functions - ldr r5, =__memcpy_reverse_large_func ; - str r4, [r5] ; - b %F4 - - ; - ; Register for NEON functions - ; -3 - ldr r4, =__memcpy_forward_large_neon ; select NEON functions - ldr r5, =__memcpy_forward_large_func ; - str r4, [r5] ; - ldr r4, =__memcpy_reverse_large_neon ; select NEON functions - ldr r5, =__memcpy_reverse_large_func ; - str r4, [r5] ; -4 - EPILOG_POP {r4-r5, r11, lr} ; restore saved registers - EPILOG_NOP ldr pc, [r12] ; jump to the appropriate target - - NESTED_END __memcpy_decide - -#endif - - -; -; void _memcpy_strict_align(void *dst, const void *src, size_t length) -; -; Copy a block of memory in a forward direction, only performing naturally-aligned -; accesses. -; - - ALIGN 32 - LEAF_ENTRY _memcpy_strict_align - -; -; Verify alignment between source and destination -; - - sub r3, r0, r1 ; get relative alignment of source and destination - cbz r2, CopyExit ; exit if 0 count - ands r3, r3, #3 ; check DWORD alignment - bne CopyMisalignedHalf ; misaligned - -; -; Source and destination are equally aligned: just align the -; destination and the source will end up aligned as well -; - - tst r0, #3 ; dword aligned at the dest? - beq WordAligned_0 ; if so, skip ahead - tst r0, #1 ; halfword aligned at the dest? - beq HalfAligned_0 ; if so, skip ahead - - subs r2, r2, #1 ; decrement count - ldrb r3, [r1], #1 ; fetch byte - strb r3, [r0], #1 ; store it - beq CopyExit ; stop if done - tst r0, #3 ; word aligned now? - beq WordAligned_0 ; if so, skip ahead - -HalfAligned_0 - cmp r2, #2 ; do we have at least 2 bytes left? - blo CopyFinalBytes ; if not, copy bytes - subs r2, r2, #2 ; decrement count - ldrh r3, [r1], #2 ; fetch halfword - strh r3, [r0], #2 ; store it - beq CopyExit ; stop if done - -WordAligned_0 - subs r2, r2, #4 ; at least 4 bytes remaining? - blt WordLoopEnd_0 ; if not, skip the main loop -WordLoop_0 - subs r2, r2, #4 ; decrement count - ldr r3, [r1], #4 ; fetch word - str r3, [r0], #4 ; store it - bge WordLoop_0 ; stop if done -WordLoopEnd_0 - adds r2, r2, #4 ; recover the extra 4 we subtracted - beq CopyExit ; stop if that's everything - -CopyFinalHalfwords - subs r2, r2, #2 ; at least 2 bytes remaining? - blt CopyFinalHalfwordsEnd ; if not, skip this -CopyFinalHalfwordsLoop - subs r2, r2, #2 ; decrement count - ldrh r3, [r1], #2 ; fetch halfword - strh r3, [r0], #2 ; store it - bge CopyFinalHalfwordsLoop ; loop until done -CopyFinalHalfwordsEnd - adds r2, r2, #2 ; recover the extra 2 we subtracted - beq CopyExit ; stop if that's everything - -CopyFinalBytes - subs r2, r2, #1 ; decrement count - ldrb r3, [r1], #1 ; fetch byte - strb r3, [r0], #1 ; store it - bne CopyFinalBytes ; loop until done -CopyExit - bx lr ; return - - -; -; Source and destination are misaligned by 2 bytes -; - -CopyMisalignedHalf - cmp r3, #2 ; misaligned by a halfword? - bne CopyMisalignedByte ; if not, skip - - tst r0, #3 ; dword aligned at the dest? - beq WordAligned_2 ; if so, skip ahead - tst r0, #1 ; halfword aligned at the dest? - beq HalfAligned_2 ; if so, skip ahead - - subs r2, r2, #1 ; decrement count - ldrb r3, [r1], #1 ; fetch byte - strb r3, [r0], #1 ; store it - beq CopyExit ; stop if done - tst r0, #3 ; word aligned now? - beq WordAligned_2 ; if so, skip ahead - -HalfAligned_2 - cmp r2, #2 ; do we have at least 2 bytes left? - blo CopyFinalBytes ; if not, copy bytes - subs r2, r2, #2 ; decrement count - ldrh r3, [r1], #2 ; fetch halfword - strh r3, [r0], #2 ; store it - beq CopyExit ; stop if done - -WordAligned_2 - subs r2, r2, #6 ; at least 6 bytes remaining? - blt WordLoopEnd_2 ; if so, skip the main loop - ldrh r12, [r1], #2 ; preload a halfword of source - subs r2, r2, #2 ; count these 2 bytes -WordLoop_2 - subs r2, r2, #4 ; decrement count - ldr r3, [r1], #4 ; fetch word - orr r12, r12, r3, lsl #16 ; copy low 16 bits to upper 16 of r12 - str r12, [r0], #4 ; store it - lsr r12, r3, #16 ; copy upper 16 bits to lower 16 of r12 - bge WordLoop_2 ; stop if done - strh r12, [r0], #2 ; store the extra halfword to the dest -WordLoopEnd_2 - adds r2, r2, #6 ; recover the extra 6 we subtracted - beq CopyExit ; stop if that's everything - b CopyFinalHalfwords ; otherwise, copy remainder - - -; -; Source and destination are misaligned by 1 byte -; - -CopyMisalignedByte - cmp r3, #1 ; misaligned by a byte? - bne CopyMisalignedByte3 ; if not, skip - - tst r0, #3 ; dword aligned at the dest? - beq WordAligned_1 ; if so, skip ahead -ByteAlign_1 - subs r2, r2, #1 ; decrement count - ldrb r3, [r1], #1 ; fetch byte - strb r3, [r0], #1 ; store it - beq CopyExit ; stop if done - tst r0, #3 ; word aligned now? - bne ByteAlign_1 ; if not, keep copying bytes - -WordAligned_1 - subs r2, r2, #5 ; at least 5 bytes remaining? - blt WordLoopEnd_1 ; if so, skip the main loop - ldrb r12, [r1], #1 ; preload a byte of source - subs r2, r2, #1 ; count this byte -WordLoop_1 - subs r2, r2, #4 ; decrement count - ldr r3, [r1], #4 ; fetch word - orr r12, r12, r3, lsl #8 ; copy low 24 bits to upper 24 of r12 - str r12, [r0], #4 ; store it - lsr r12, r3, #24 ; copy upper 8 bits to lower 8 of r12 - bge WordLoop_1 ; stop if done - strb r12, [r0], #1 ; store the extra byte to the dest -WordLoopEnd_1 - adds r2, r2, #5 ; recover the extra 5 we subtracted - beq CopyExit ; stop if that's everything - b CopyFinalBytes ; otherwise, copy remainder - - -; -; Source and destination are misaligned by 3 bytes -; - -CopyMisalignedByte3 - tst r0, #3 ; dword aligned at the dest? - beq WordAligned_3 ; if so, skip ahead -ByteAlign_3 - subs r2, r2, #1 ; decrement count - ldrb r3, [r1], #1 ; fetch byte - strb r3, [r0], #1 ; store it - beq CopyExit ; stop if done - tst r0, #3 ; word aligned now? - bne ByteAlign_3 ; if not, keep copying bytes - -WordAligned_3 - subs r2, r2, #7 ; at least 7 bytes remaining? - blt WordLoopEnd_3 ; if so, skip the main loop - ldrb r12, [r1], #1 ; preload a byte of source - ldrh r3, [r1], #2 ; preload a halfword of source - orr r12, r12, r3, lsl #8 ; OR in the halfword - subs r2, r2, #3 ; count these 3 bytes -WordLoop_3 - subs r2, r2, #4 ; decrement count - ldr r3, [r1], #4 ; fetch word - orr r12, r12, r3, lsl #24 ; copy low 8 bits to upper 8 of r12 - str r12, [r0], #4 ; store it - lsr r12, r3, #8 ; copy upper 24 bits to lower 24 of r12 - bge WordLoop_3 ; stop if done - strh r12, [r0], #2 ; store the extra halfword to the dest - lsr r12, r12, #16 ; down to the final byte - strb r12, [r0], #1 ; store the extra byte to the dest -WordLoopEnd_3 - adds r2, r2, #7 ; recover the extra 7 we subtracted - beq CopyExit ; stop if that's everything - b CopyFinalBytes ; otherwise, copy remainder - - LEAF_END _memcpy_strict_align - - END diff --git a/src/coreclr/src/vm/comutilnative.cpp b/src/coreclr/src/vm/comutilnative.cpp index f2c2811..76bfa61 100644 --- a/src/coreclr/src/vm/comutilnative.cpp +++ b/src/coreclr/src/vm/comutilnative.cpp @@ -685,7 +685,7 @@ void QCALLTYPE ExceptionNative::GetMessageFromNativeResources(ExceptionMessageKi END_QCALL; } -void QCALLTYPE MemoryNative::Clear(void *dst, size_t length) +void QCALLTYPE Buffer::Clear(void *dst, size_t length) { QCALL_CONTRACT; @@ -714,11 +714,12 @@ void QCALLTYPE MemoryNative::Clear(void *dst, size_t length) memset(dst, 0, length); } -FCIMPL3(VOID, MemoryNative::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount) +FCIMPL3(VOID, Buffer::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount) { FCALL_CONTRACT; - InlinedMemmoveGCRefsHelper(dst, src, byteCount); + if (dst != src && byteCount != 0) + InlinedMemmoveGCRefsHelper(dst, src, byteCount); FC_GC_POLL(); } diff --git a/src/coreclr/src/vm/comutilnative.h b/src/coreclr/src/vm/comutilnative.h index cc48e88..5b6d7a9 100644 --- a/src/coreclr/src/vm/comutilnative.h +++ b/src/coreclr/src/vm/comutilnative.h @@ -64,25 +64,18 @@ public: static FCDECL0(UINT32, GetExceptionCount); }; -class MemoryNative -{ -public: - static void QCALLTYPE Clear(void *dst, size_t length); - static FCDECL3(VOID, BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount); -}; - // // Buffer // -class Buffer { +class Buffer +{ public: - - // BlockCopy - // This method from one primitive array to another based - // upon an offset into each an a byte count. static FCDECL1(FC_BOOL_RET, IsPrimitiveTypeArray, ArrayBase *arrayUNSAFE); + static FCDECL3(VOID, BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount); + static void QCALLTYPE MemMove(void *dst, void *src, size_t length); + static void QCALLTYPE Clear(void *dst, size_t length); }; #define MIN_GC_MEMORYPRESSURE_THRESHOLD 100000 diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 8a5a1d2..21b0ff8 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -740,9 +740,8 @@ FCFuncEnd() FCFuncStart(gBufferFuncs) FCFuncElement("IsPrimitiveTypeArray", Buffer::IsPrimitiveTypeArray) -#ifdef _TARGET_ARM_ - FCFuncElement("Memcpy", FCallMemcpy) -#endif + QCFuncElement("__ZeroMemory", Buffer::Clear) + FCFuncElement("BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier) QCFuncElement("__Memmove", Buffer::MemMove) FCFuncEnd() @@ -1109,11 +1108,6 @@ FCFuncStart(gRuntimeClassFuncs) FCFuncEnd() #endif // ifdef FEATURE_COMINTEROP -FCFuncStart(gRuntimeImportsFuncs) - QCFuncElement("RhZeroMemory", MemoryNative::Clear) - FCFuncElement("RhBulkMoveWithWriteBarrier", MemoryNative::BulkMoveWithWriteBarrier) -FCFuncEnd() - FCFuncStart(gWeakReferenceFuncs) FCFuncElement("Create", WeakReferenceNative::Create) FCFuncElement("Finalize", WeakReferenceNative::Finalize) @@ -1175,7 +1169,6 @@ FCFuncStart(gPalOleAut32Funcs) QCFuncElement("SysAllocStringByteLen", SysAllocStringByteLen) QCFuncElement("SysAllocStringLen", SysAllocStringLen) QCFuncElement("SysFreeString", SysFreeString) - QCFuncElement("SysStringLen", SysStringLen) FCFuncEnd() #endif @@ -1299,7 +1292,6 @@ FCClassElement("RuntimeClass", "System.Runtime.InteropServices.WindowsRuntime", #endif // FEATURE_COMINTEROP FCClassElement("RuntimeFieldHandle", "System", gCOMFieldHandleNewFuncs) FCClassElement("RuntimeHelpers", "System.Runtime.CompilerServices", gRuntimeHelpers) -FCClassElement("RuntimeImports", "System.Runtime", gRuntimeImportsFuncs) FCClassElement("RuntimeMethodHandle", "System", gRuntimeMethodHandle) FCClassElement("RuntimeModule", "System.Reflection", gCOMModuleFuncs) FCClassElement("RuntimeType", "System", gSystem_RuntimeType) diff --git a/src/coreclr/src/vm/excep.cpp b/src/coreclr/src/vm/excep.cpp index d3fc6ea..dafc105 100644 --- a/src/coreclr/src/vm/excep.cpp +++ b/src/coreclr/src/vm/excep.cpp @@ -6653,10 +6653,6 @@ EXTERN_C void JIT_WriteBarrier_Debug(); EXTERN_C void JIT_WriteBarrier_Debug_End(); #endif -#ifdef _TARGET_ARM_ -EXTERN_C void FCallMemcpy_End(); -#endif - #ifdef VSD_STUB_CAN_THROW_AV //Return TRUE if pContext->Pc is in VirtualStub BOOL IsIPinVirtualStub(PCODE f_IP) @@ -6717,10 +6713,6 @@ bool IsIPInMarkedJitHelper(UINT_PTR uControlPc) CHECK_RANGE(JIT_WriteBarrier_Debug) #endif -#ifdef _TARGET_ARM_ - CHECK_RANGE(FCallMemcpy) -#endif - return false; } #endif // FEATURE_EH_FUNCLETS diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index 413b929..521afb7 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -5563,31 +5563,6 @@ EXTERN_C void JIT_PInvokeEnd(InlinedCallFrame* pFrame); //======================================================================== // -// JIT HELPERS IMPLEMENTED AS FCALLS -// -//======================================================================== - -#ifdef _TARGET_ARM_ -// This function is used from the FCallMemcpy for GC polling -EXTERN_C VOID FCallMemCpy_GCPoll() -{ - FC_INNER_PROLOG(FCallMemcpy); - - Thread *thread = GetThread(); - // CommonTripThread does this check, but doing this to avoid raising the frames - if (thread->CatchAtSafePointOpportunistic()) - { - HELPER_METHOD_FRAME_BEGIN_0(); - CommonTripThread(); - HELPER_METHOD_FRAME_END(); - } - - FC_INNER_EPILOG(); -} -#endif // _TARGET_ARM_ - -//======================================================================== -// // JIT HELPERS INITIALIZATION // //======================================================================== diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Crypt32/Interop.CryptProtectMemory.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Crypt32/Interop.CryptProtectMemory.cs index 84942d9..cce0190 100644 --- a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Crypt32/Interop.CryptProtectMemory.cs +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Crypt32/Interop.CryptProtectMemory.cs @@ -13,9 +13,9 @@ internal static partial class Interop internal const uint CRYPTPROTECTMEMORY_SAME_PROCESS = 0; [DllImport(Libraries.Crypt32, CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern bool CryptProtectMemory(SafeBSTRHandle pData, uint cbData, uint dwFlags); + internal static extern bool CryptProtectMemory(SafeBuffer pData, uint cbData, uint dwFlags); [DllImport(Libraries.Crypt32, CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern bool CryptUnprotectMemory(SafeBSTRHandle pData, uint cbData, uint dwFlags); + internal static extern bool CryptUnprotectMemory(SafeBuffer pData, uint cbData, uint dwFlags); } } diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/OleAut32/Interop.SysAllocStringLen.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/OleAut32/Interop.SysAllocStringLen.cs index a7c09b2..ac94599 100644 --- a/src/libraries/System.Private.CoreLib/src/Interop/Windows/OleAut32/Interop.SysAllocStringLen.cs +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/OleAut32/Interop.SysAllocStringLen.cs @@ -11,9 +11,6 @@ internal static partial class Interop internal static partial class OleAut32 { [DllImport(Libraries.OleAut32, CharSet = CharSet.Unicode)] - internal static extern SafeBSTRHandle SysAllocStringLen(IntPtr src, uint len); - - [DllImport(Libraries.OleAut32, CharSet = CharSet.Unicode)] internal static extern IntPtr SysAllocStringLen(string? src, int len); } } diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/OleAut32/Interop.SysStringLen.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/OleAut32/Interop.SysStringLen.cs deleted file mode 100644 index a80821b..0000000 --- a/src/libraries/System.Private.CoreLib/src/Interop/Windows/OleAut32/Interop.SysStringLen.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; -using System.Security; - -internal static partial class Interop -{ - internal static partial class OleAut32 - { - [DllImport(Libraries.OleAut32)] - internal static extern uint SysStringLen(SafeBSTRHandle bstr); - - [DllImport(Libraries.OleAut32)] - internal static extern uint SysStringLen(IntPtr bstr); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index f3056ec..caa3d1d 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1195,7 +1195,6 @@ - @@ -1210,7 +1209,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffer.cs b/src/libraries/System.Private.CoreLib/src/System/Buffer.cs index 78d8273..3320579d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffer.cs @@ -115,13 +115,6 @@ namespace System Unsafe.Add(ref array.GetRawArrayData(), index) = value; } - // This is currently used by System.IO.UnmanagedMemoryStream - internal static unsafe void ZeroMemory(byte* dest, long len) - { - Debug.Assert((ulong)(len) == (nuint)(len)); - ZeroMemory(dest, (nuint)(len)); - } - // This method has different signature for x64 and other platforms and is done for performance reasons. internal static unsafe void ZeroMemory(byte* dest, nuint len) { @@ -338,16 +331,10 @@ namespace System else { // Non-blittable memmove - - // Try to avoid calling RhBulkMoveWithWriteBarrier if we can get away - // with a no-op. - if (!Unsafe.AreSame(ref destination, ref source) && elementCount != 0) - { - RuntimeImports.RhBulkMoveWithWriteBarrier( - ref Unsafe.As(ref destination), - ref Unsafe.As(ref source), - elementCount * (nuint)Unsafe.SizeOf()); - } + BulkMoveWithWriteBarrier( + ref Unsafe.As(ref destination), + ref Unsafe.As(ref source), + elementCount * (nuint)Unsafe.SizeOf()); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStream.cs index 6484e34..de03abd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStream.cs @@ -8,6 +8,13 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + namespace System.IO { /* @@ -620,7 +627,7 @@ namespace System.IO { unsafe { - Buffer.ZeroMemory(_mem + len, value - len); + Buffer.ZeroMemory(_mem + len, (nuint)(value - len)); } } Interlocked.Exchange(ref _length, value); @@ -690,7 +697,7 @@ namespace System.IO // zero any memory in the middle. if (pos > len) { - Buffer.ZeroMemory(_mem + len, pos - len); + Buffer.ZeroMemory(_mem + len, (nuint)(pos - len)); } // set length after zeroing memory to avoid race condition of accessing unzeroed memory @@ -831,7 +838,7 @@ namespace System.IO { unsafe { - Buffer.ZeroMemory(_mem + len, pos - len); + Buffer.ZeroMemory(_mem + len, (nuint)(pos - len)); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs b/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs index 0d99bab..d4058c3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs @@ -806,7 +806,7 @@ namespace System Debug.Assert(unchecked((uint)(maxResultLength)) <= MaxBlockCount); // Zero out result internal blocks. - Buffer.ZeroMemory((byte*)(result.GetBlocksPointer()), maxResultLength * sizeof(uint)); + Buffer.ZeroMemory((byte*)result.GetBlocksPointer(), (uint)maxResultLength * sizeof(uint)); int smallIndex = 0; int resultStartIndex = 0; @@ -1122,7 +1122,7 @@ namespace System var result = new BigInteger(0); Multiply(ref this, ref value, ref result); - Buffer.Memcpy((byte*)(GetBlocksPointer()), (byte*)(result.GetBlocksPointer()), (result._length) * sizeof(uint)); + Buffer.Memcpy((byte*)GetBlocksPointer(), (byte*)result.GetBlocksPointer(), result._length * sizeof(uint)); _length = result._length; } @@ -1189,7 +1189,7 @@ namespace System public void SetValue(ref BigInteger rhs) { int rhsLength = rhs._length; - Buffer.Memcpy((byte*)(GetBlocksPointer()), (byte*)(rhs.GetBlocksPointer()), rhsLength * sizeof(uint)); + Buffer.Memcpy((byte*)GetBlocksPointer(), (byte*)rhs.GetBlocksPointer(), rhsLength * sizeof(uint)); _length = rhsLength; } @@ -1229,7 +1229,7 @@ namespace System _length += (int)(blocksToShift); // Zero the remaining low blocks - Buffer.ZeroMemory((byte*)(GetBlocksPointer()), blocksToShift * sizeof(uint)); + Buffer.ZeroMemory((byte*)GetBlocksPointer(), blocksToShift * sizeof(uint)); } else { @@ -1262,7 +1262,7 @@ namespace System _blocks[writeIndex - 1] = block << (int)(remainingBitsToShift); // Zero the remaining low blocks - Buffer.ZeroMemory((byte*)(GetBlocksPointer()), blocksToShift * sizeof(uint)); + Buffer.ZeroMemory((byte*)GetBlocksPointer(), blocksToShift * sizeof(uint)); // Check if the terminating block has no set bits if (_blocks[_length - 1] == 0) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs index c0e9bd6..63c7905 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs @@ -31,7 +31,7 @@ namespace System.Runtime.InteropServices private static int GetSystemMaxDBCSCharSize() => 3; - private static bool IsWin32Atom(IntPtr ptr) => false; + private static bool IsNullOrWin32Atom(IntPtr ptr) => ptr == IntPtr.Zero; internal static unsafe int StringToAnsiString(string s, byte* buffer, int bufferLength, bool bestFit = false, bool throwOnUnmappableChar = false) { @@ -48,5 +48,19 @@ namespace System.Runtime.InteropServices return convertedBytes; } + + // Returns number of bytes required to convert given string to Ansi string. The return value includes null terminator. + internal static unsafe int GetAnsiStringByteCount(ReadOnlySpan chars) + { + int byteLength = Encoding.UTF8.GetByteCount(chars); + return checked(byteLength + 1); + } + + // Converts given string to Ansi string. The destination buffer must be large enough to hold the converted value, including null terminator. + internal static unsafe void GetAnsiStringBytes(ReadOnlySpan chars, Span bytes) + { + int actualByteLength = Encoding.UTF8.GetBytes(chars, bytes); + bytes[actualByteLength] = 0; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Windows.cs index f4027d0..9889030 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Windows.cs @@ -41,10 +41,8 @@ namespace System.Runtime.InteropServices // Win32 has the concept of Atoms, where a pointer can either be a pointer // or an int. If it's less than 64K, this is guaranteed to NOT be a // pointer since the bottom 64K bytes are reserved in a process' page table. - // We should be careful about deallocating this stuff. Extracted to - // a function to avoid C# problems with lack of support for IntPtr. - // We have 2 of these methods for slightly different semantics for NULL. - private static bool IsWin32Atom(IntPtr ptr) + // We should be careful about deallocating this stuff. + private static bool IsNullOrWin32Atom(IntPtr ptr) { const long HIWORDMASK = unchecked((long)0xffffffffffff0000L); @@ -82,5 +80,52 @@ namespace System.Runtime.InteropServices buffer[nb] = 0; return nb; } + + // Returns number of bytes required to convert given string to Ansi string. The return value includes null terminator. + internal static unsafe int GetAnsiStringByteCount(ReadOnlySpan chars) + { + int byteLength; + + if (chars.Length == 0) + { + byteLength = 0; + } + else + { + fixed (char* pChars = chars) + { + byteLength = Interop.Kernel32.WideCharToMultiByte( + Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, pChars, chars.Length, null, 0, IntPtr.Zero, IntPtr.Zero); + if (byteLength <= 0) + throw new ArgumentException(); + } + } + + return checked(byteLength + 1); + } + + // Converts given string to Ansi string. The destination buffer must be large enough to hold the converted value, including null terminator. + internal static unsafe void GetAnsiStringBytes(ReadOnlySpan chars, Span bytes) + { + int byteLength; + + if (chars.Length == 0) + { + byteLength = 0; + } + else + { + fixed (char* pChars = chars) + fixed (byte* pBytes = bytes) + { + byteLength = Interop.Kernel32.WideCharToMultiByte( + Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, pChars, chars.Length, pBytes, bytes.Length, IntPtr.Zero, IntPtr.Zero); + if (byteLength <= 0) + throw new ArgumentException(); + } + } + + bytes[byteLength] = 0; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index ba6f9bf..4eaef53 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -40,7 +40,7 @@ namespace System.Runtime.InteropServices public static unsafe string? PtrToStringAnsi(IntPtr ptr) { - if (ptr == IntPtr.Zero || IsWin32Atom(ptr)) + if (IsNullOrWin32Atom(ptr)) { return null; } @@ -64,7 +64,7 @@ namespace System.Runtime.InteropServices public static unsafe string? PtrToStringUni(IntPtr ptr) { - if (ptr == IntPtr.Zero || IsWin32Atom(ptr)) + if (IsNullOrWin32Atom(ptr)) { return null; } @@ -88,7 +88,7 @@ namespace System.Runtime.InteropServices public static unsafe string? PtrToStringUTF8(IntPtr ptr) { - if (ptr == IntPtr.Zero || IsWin32Atom(ptr)) + if (IsNullOrWin32Atom(ptr)) { return null; } @@ -915,13 +915,13 @@ namespace System.Runtime.InteropServices public static IntPtr /* IDispatch */ GetIDispatchForObject(object o) => throw new PlatformNotSupportedException(); - public static void ZeroFreeBSTR(IntPtr s) + public static unsafe void ZeroFreeBSTR(IntPtr s) { if (s == IntPtr.Zero) { return; } - RuntimeImports.RhZeroMemory(s, (UIntPtr)SysStringByteLen(s)); + Buffer.ZeroMemory((byte*)s, SysStringByteLen(s)); FreeBSTR(s); } @@ -936,7 +936,7 @@ namespace System.Runtime.InteropServices { return; } - RuntimeImports.RhZeroMemory(s, (UIntPtr)(string.wcslen((char*)s) * 2)); + Buffer.ZeroMemory((byte*)s, (nuint)string.wcslen((char*)s) * sizeof(char)); FreeCoTaskMem(s); } @@ -946,7 +946,7 @@ namespace System.Runtime.InteropServices { return; } - RuntimeImports.RhZeroMemory(s, (UIntPtr)string.strlen((byte*)s)); + Buffer.ZeroMemory((byte*)s, (nuint)string.strlen((byte*)s)); FreeCoTaskMem(s); } @@ -956,7 +956,7 @@ namespace System.Runtime.InteropServices { return; } - RuntimeImports.RhZeroMemory(s, (UIntPtr)string.strlen((byte*)s)); + Buffer.ZeroMemory((byte*)s, (nuint)string.strlen((byte*)s)); FreeHGlobal(s); } @@ -966,7 +966,7 @@ namespace System.Runtime.InteropServices { return; } - RuntimeImports.RhZeroMemory(s, (UIntPtr)(string.wcslen((char*)s) * 2)); + Buffer.ZeroMemory((byte*)s, (nuint)string.wcslen((char*)s) * sizeof(char)); FreeHGlobal(s); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Security/SafeBSTRHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Security/SafeBSTRHandle.cs deleted file mode 100644 index ceaf903..0000000 --- a/src/libraries/System.Private.CoreLib/src/System/Security/SafeBSTRHandle.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.Security -{ - internal sealed class SafeBSTRHandle : SafeBuffer - { - internal SafeBSTRHandle() : base(true) { } - - internal static SafeBSTRHandle Allocate(uint lenInChars) - { - ulong lenInBytes = (ulong)lenInChars * sizeof(char); - SafeBSTRHandle bstr = Interop.OleAut32.SysAllocStringLen(IntPtr.Zero, lenInChars); - if (bstr.IsInvalid) // SysAllocStringLen returns a NULL ptr when there's insufficient memory - { - throw new OutOfMemoryException(); - } - bstr.Initialize(lenInBytes); - return bstr; - } - - protected override bool ReleaseHandle() - { - RuntimeImports.RhZeroMemory(handle, (UIntPtr)Marshal.SysStringByteLen(handle)); - Interop.OleAut32.SysFreeString(handle); - return true; - } - - internal unsafe void ClearBuffer() - { - byte* bufferPtr = null; - try - { - AcquirePointer(ref bufferPtr); - RuntimeImports.RhZeroMemory((IntPtr)bufferPtr, (UIntPtr)Marshal.SysStringByteLen((IntPtr)bufferPtr)); - } - finally - { - if (bufferPtr != null) - { - ReleasePointer(); - } - } - } - - internal unsafe uint Length => Interop.OleAut32.SysStringLen(this); - - internal static unsafe void Copy(SafeBSTRHandle source, SafeBSTRHandle target, uint bytesToCopy) - { - if (bytesToCopy == 0) - { - return; - } - - byte* sourcePtr = null, targetPtr = null; - try - { - source.AcquirePointer(ref sourcePtr); - target.AcquirePointer(ref targetPtr); - - Debug.Assert(source.ByteLength >= bytesToCopy, "Source buffer is too small."); - Buffer.MemoryCopy(sourcePtr, targetPtr, target.ByteLength, bytesToCopy); - } - finally - { - if (targetPtr != null) - { - target.ReleasePointer(); - } - if (sourcePtr != null) - { - source.ReleasePointer(); - } - } - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.Unix.cs index 82b3833..d3fbd9b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.Unix.cs @@ -20,307 +20,19 @@ namespace System.Security public sealed partial class SecureString { - private UnmanagedBuffer? _buffer; - - internal SecureString(SecureString str) - { - // Allocate enough space to store the provided string - EnsureCapacity(str._decryptedLength); - _decryptedLength = str._decryptedLength; - - // Copy the string into the newly allocated space - if (_decryptedLength > 0) - { - Debug.Assert(str._buffer != null && _buffer != null); ; - UnmanagedBuffer.Copy(str._buffer, _buffer, (ulong)(str._decryptedLength * sizeof(char))); - } - } - - private unsafe void InitializeSecureString(char* value, int length) - { - // Allocate enough space to store the provided string - EnsureCapacity(length); - _decryptedLength = length; - if (length == 0) - { - return; - } - - Debug.Assert(_buffer != null); - // Copy the string into the newly allocated space - byte* ptr = null; - try - { - _buffer.AcquirePointer(ref ptr); - Buffer.MemoryCopy(value, ptr, _buffer.ByteLength, (ulong)(length * sizeof(char))); - } - finally - { - if (ptr != null) - { - _buffer.ReleasePointer(); - } - } - } - - private void DisposeCore() - { - if (_buffer != null && !_buffer.IsInvalid) - { - _buffer.Dispose(); - _buffer = null; - } - } - - private void ClearCore() + private static int GetAlignedByteSize(int length) { - _decryptedLength = 0; - Debug.Assert(_buffer != null); - _buffer.Clear(); + return Math.Max(length, 1) * sizeof(char); } - private unsafe void AppendCharCore(char c) + private void ProtectMemory() { - // Make sure we have enough space for the new character, then write it at the end. - EnsureCapacity(_decryptedLength + 1); - Debug.Assert(_buffer != null); - _buffer.Write((ulong)(_decryptedLength * sizeof(char)), c); - _decryptedLength++; + _encrypted = true; } - private unsafe void InsertAtCore(int index, char c) + private void UnprotectMemory() { - // Make sure we have enough space for the new character, then shift all of the characters above it and insert it. - EnsureCapacity(_decryptedLength + 1); - byte* ptr = null; - Debug.Assert(_buffer != null); - try - { - _buffer.AcquirePointer(ref ptr); - ptr += index * sizeof(char); - long bytesToShift = (_decryptedLength - index) * sizeof(char); - Buffer.MemoryCopy(ptr, ptr + sizeof(char), bytesToShift, bytesToShift); - *((char*)ptr) = c; - ++_decryptedLength; - } - finally - { - if (ptr != null) - { - _buffer.ReleasePointer(); - } - } - } - - private unsafe void RemoveAtCore(int index) - { - // Shift down all values above the specified index, then null out the empty space at the end. - byte* ptr = null; - Debug.Assert(_buffer != null); - try - { - _buffer.AcquirePointer(ref ptr); - ptr += index * sizeof(char); - long bytesToShift = (_decryptedLength - index - 1) * sizeof(char); - Buffer.MemoryCopy(ptr + sizeof(char), ptr, bytesToShift, bytesToShift); - *((char*)(ptr + bytesToShift)) = (char)0; - --_decryptedLength; - } - finally - { - if (ptr != null) - { - _buffer.ReleasePointer(); - } - } - } - - private void SetAtCore(int index, char c) - { - // Overwrite the character at the specified index - Debug.Assert(_buffer != null); - _buffer.Write((ulong)(index * sizeof(char)), c); - } - - internal unsafe IntPtr MarshalToBSTRCore() - { - int length = _decryptedLength; - IntPtr ptr = IntPtr.Zero; - IntPtr result = IntPtr.Zero; - byte* bufferPtr = null; - Debug.Assert(_buffer != null); - - try - { - _buffer.AcquirePointer(ref bufferPtr); - int resultByteLength = (length + 1) * sizeof(char); - - ptr = Marshal.AllocBSTR(length); - - Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char)); - - result = ptr; - } - finally - { - // If we failed for any reason, free the new buffer - if (result == IntPtr.Zero && ptr != IntPtr.Zero) - { - RuntimeImports.RhZeroMemory(ptr, (UIntPtr)(length * sizeof(char))); - Marshal.FreeBSTR(ptr); - } - - if (bufferPtr != null) - { - _buffer.ReleasePointer(); - } - } - return result; - } - - internal unsafe IntPtr MarshalToStringCore(bool globalAlloc, bool unicode) - { - int length = _decryptedLength; - - byte* bufferPtr = null; - IntPtr stringPtr = IntPtr.Zero, result = IntPtr.Zero; - Debug.Assert(_buffer != null); - try - { - _buffer.AcquirePointer(ref bufferPtr); - if (unicode) - { - int resultLength = (length + 1) * sizeof(char); - stringPtr = globalAlloc ? Marshal.AllocHGlobal(resultLength) : Marshal.AllocCoTaskMem(resultLength); - Buffer.MemoryCopy( - source: bufferPtr, - destination: (byte*)stringPtr.ToPointer(), - destinationSizeInBytes: resultLength, - sourceBytesToCopy: length * sizeof(char)); - *(length + (char*)stringPtr) = '\0'; - } - else - { - int resultLength = Encoding.UTF8.GetByteCount((char*)bufferPtr, length) + 1; - stringPtr = globalAlloc ? Marshal.AllocHGlobal(resultLength) : Marshal.AllocCoTaskMem(resultLength); - int encodedLength = Encoding.UTF8.GetBytes((char*)bufferPtr, length, (byte*)stringPtr, resultLength); - Debug.Assert(encodedLength + 1 == resultLength, $"Expected encoded length to match result, got {encodedLength} != {resultLength}"); - *(resultLength - 1 + (byte*)stringPtr) = 0; - } - - result = stringPtr; - } - finally - { - // If there was a failure, such that result isn't initialized, - // release the string if we had one. - if (stringPtr != IntPtr.Zero && result == IntPtr.Zero) - { - RuntimeImports.RhZeroMemory(stringPtr, (UIntPtr)(length * sizeof(char))); - MarshalFree(stringPtr, globalAlloc); - } - - if (bufferPtr != null) - { - _buffer.ReleasePointer(); - } - } - - return result; - } - - // ----------------------------- - // ---- PAL layer ends here ---- - // ----------------------------- - - private void EnsureCapacity(int capacity) - { - // Make sure the requested capacity doesn't exceed SecureString's defined limit - if (capacity > MaxLength) - { - throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity); - } - - // If we already have enough space allocated, we're done - if (_buffer != null && (capacity * sizeof(char)) <= (int)_buffer.ByteLength) - { - return; - } - - // We need more space, so allocate a new buffer, copy all our data into it, - // and then swap the new for the old. - UnmanagedBuffer newBuffer = UnmanagedBuffer.Allocate(capacity * sizeof(char)); - if (_buffer != null) - { - UnmanagedBuffer.Copy(_buffer, newBuffer, _buffer.ByteLength); - _buffer.Dispose(); - } - _buffer = newBuffer; - } - - /// SafeBuffer for managing memory meant to be kept confidential. - private sealed class UnmanagedBuffer : SafeBuffer - { - internal UnmanagedBuffer() : base(true) { } - - internal static UnmanagedBuffer Allocate(int bytes) - { - Debug.Assert(bytes >= 0); - UnmanagedBuffer buffer = new UnmanagedBuffer(); - buffer.SetHandle(Marshal.AllocHGlobal(bytes)); - buffer.Initialize((ulong)bytes); - return buffer; - } - - internal unsafe void Clear() - { - byte* ptr = null; - try - { - AcquirePointer(ref ptr); - RuntimeImports.RhZeroMemory((IntPtr)ptr, (UIntPtr)ByteLength); - } - finally - { - if (ptr != null) - { - ReleasePointer(); - } - } - } - - internal static unsafe void Copy(UnmanagedBuffer source, UnmanagedBuffer destination, ulong bytesLength) - { - if (bytesLength == 0) - { - return; - } - - byte* srcPtr = null, dstPtr = null; - try - { - source.AcquirePointer(ref srcPtr); - destination.AcquirePointer(ref dstPtr); - Buffer.MemoryCopy(srcPtr, dstPtr, destination.ByteLength, bytesLength); - } - finally - { - if (dstPtr != null) - { - destination.ReleasePointer(); - } - if (srcPtr != null) - { - source.ReleasePointer(); - } - } - } - - protected override unsafe bool ReleaseHandle() - { - Marshal.FreeHGlobal(handle); - return true; - } + _encrypted = false; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.Windows.cs index abf9f16..9e226ec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.Windows.cs @@ -11,271 +11,12 @@ namespace System.Security { public sealed partial class SecureString { - internal SecureString(SecureString str) + private static int GetAlignedByteSize(int length) { - Debug.Assert(str != null, "Expected non-null SecureString"); - Debug.Assert(str._buffer != null, "Expected other SecureString's buffer to be non-null"); - Debug.Assert(str._encrypted, "Expected to be used only on encrypted SecureStrings"); + int byteSize = Math.Max(length, 1) * sizeof(char); - AllocateBuffer(str._buffer.Length); - Debug.Assert(_buffer != null); - SafeBSTRHandle.Copy(str._buffer, _buffer, str._buffer.Length * sizeof(char)); - - _decryptedLength = str._decryptedLength; - _encrypted = str._encrypted; - } - - private unsafe void InitializeSecureString(char* value, int length) - { - Debug.Assert(length >= 0, $"Expected non-negative length, got {length}"); - - AllocateBuffer((uint)length); - _decryptedLength = length; - - byte* bufferPtr = null; - Debug.Assert(_buffer != null); - try - { - _buffer.AcquirePointer(ref bufferPtr); - Buffer.MemoryCopy((byte*)value, bufferPtr, (long)_buffer.ByteLength, length * sizeof(char)); - } - finally - { - if (bufferPtr != null) - { - _buffer.ReleasePointer(); - } - } - - ProtectMemory(); - } - - private void AppendCharCore(char c) - { - UnprotectMemory(); - try - { - EnsureCapacity(_decryptedLength + 1); - Debug.Assert(_buffer != null); - _buffer.Write((uint)_decryptedLength * sizeof(char), c); - _decryptedLength++; - } - finally - { - ProtectMemory(); - } - } - - private void ClearCore() - { - _decryptedLength = 0; - Debug.Assert(_buffer != null); - _buffer.ClearBuffer(); - } - - private void DisposeCore() - { - if (_buffer != null) - { - _buffer.Dispose(); - _buffer = null; - } - } - - private unsafe void InsertAtCore(int index, char c) - { - byte* bufferPtr = null; - UnprotectMemory(); - Debug.Assert(_buffer != null); - try - { - EnsureCapacity(_decryptedLength + 1); - - _buffer.AcquirePointer(ref bufferPtr); - char* pBuffer = (char*)bufferPtr; - - for (int i = _decryptedLength; i > index; i--) - { - pBuffer[i] = pBuffer[i - 1]; - } - pBuffer[index] = c; - ++_decryptedLength; - } - finally - { - ProtectMemory(); - if (bufferPtr != null) - { - _buffer.ReleasePointer(); - } - } - } - - private unsafe void RemoveAtCore(int index) - { - byte* bufferPtr = null; - UnprotectMemory(); - Debug.Assert(_buffer != null); - try - { - _buffer.AcquirePointer(ref bufferPtr); - char* pBuffer = (char*)bufferPtr; - - for (int i = index; i < _decryptedLength - 1; i++) - { - pBuffer[i] = pBuffer[i + 1]; - } - pBuffer[--_decryptedLength] = (char)0; - } - finally - { - ProtectMemory(); - if (bufferPtr != null) - { - _buffer.ReleasePointer(); - } - } - } - - private void SetAtCore(int index, char c) - { - UnprotectMemory(); - try - { - Debug.Assert(_buffer != null); - _buffer.Write((uint)index * sizeof(char), c); - } - finally - { - ProtectMemory(); - } - } - - internal unsafe IntPtr MarshalToBSTRCore() - { - int length = _decryptedLength; - IntPtr ptr = IntPtr.Zero; - IntPtr result = IntPtr.Zero; - byte* bufferPtr = null; - - UnprotectMemory(); - Debug.Assert(_buffer != null); - try - { - _buffer.AcquirePointer(ref bufferPtr); - int resultByteLength = (length + 1) * sizeof(char); - - ptr = Marshal.AllocBSTR(length); - - Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char)); - - result = ptr; - } - finally - { - ProtectMemory(); - - // If we failed for any reason, free the new buffer - if (result == IntPtr.Zero && ptr != IntPtr.Zero) - { - RuntimeImports.RhZeroMemory(ptr, (UIntPtr)(length * sizeof(char))); - Marshal.FreeBSTR(ptr); - } - - if (bufferPtr != null) - { - _buffer.ReleasePointer(); - } - } - return result; - } - - internal unsafe IntPtr MarshalToStringCore(bool globalAlloc, bool unicode) - { - int length = _decryptedLength; - IntPtr ptr = IntPtr.Zero; - IntPtr result = IntPtr.Zero; - byte* bufferPtr = null; - - UnprotectMemory(); - Debug.Assert(_buffer != null); - try - { - _buffer.AcquirePointer(ref bufferPtr); - if (unicode) - { - int resultByteLength = (length + 1) * sizeof(char); - ptr = globalAlloc ? Marshal.AllocHGlobal(resultByteLength) : Marshal.AllocCoTaskMem(resultByteLength); - Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char)); - *(length + (char*)ptr) = '\0'; - } - else - { - uint defaultChar = '?'; - int resultByteLength = 1 + Interop.Kernel32.WideCharToMultiByte( - Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, (char*)bufferPtr, length, null, 0, (IntPtr)(&defaultChar), IntPtr.Zero); - ptr = globalAlloc ? Marshal.AllocHGlobal(resultByteLength) : Marshal.AllocCoTaskMem(resultByteLength); - Interop.Kernel32.WideCharToMultiByte( - Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, (char*)bufferPtr, length, (byte*)ptr, resultByteLength - 1, (IntPtr)(&defaultChar), IntPtr.Zero); - *(resultByteLength - 1 + (byte*)ptr) = 0; - } - result = ptr; - } - finally - { - ProtectMemory(); - - // If we failed for any reason, free the new buffer - if (result == IntPtr.Zero && ptr != IntPtr.Zero) - { - RuntimeImports.RhZeroMemory(ptr, (UIntPtr)(length * sizeof(char))); - MarshalFree(ptr, globalAlloc); - } - - if (bufferPtr != null) - { - _buffer.ReleasePointer(); - } - } - return result; - } - - // ----------------------------- - // ---- PAL layer ends here ---- - // ----------------------------- - - private const int BlockSize = (int)Interop.Crypt32.CRYPTPROTECTMEMORY_BLOCK_SIZE / sizeof(char); - private SafeBSTRHandle? _buffer; - private bool _encrypted; - - private void AllocateBuffer(uint size) - { - _buffer = SafeBSTRHandle.Allocate(GetAlignedSize(size)); - } - - private static uint GetAlignedSize(uint size) => - size == 0 || size % BlockSize != 0 ? - BlockSize + ((size / BlockSize) * BlockSize) : - size; - - private void EnsureCapacity(int capacity) - { - if (capacity > MaxLength) - { - throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity); - } - - Debug.Assert(_buffer != null); - if (((uint)capacity * sizeof(char)) <= _buffer.ByteLength) - { - return; - } - - SafeBSTRHandle oldBuffer = _buffer; - SafeBSTRHandle newBuffer = SafeBSTRHandle.Allocate(GetAlignedSize((uint)capacity)); - SafeBSTRHandle.Copy(oldBuffer, newBuffer, (uint)_decryptedLength * sizeof(char)); - _buffer = newBuffer; - oldBuffer.Dispose(); + const int blockSize = (int)Interop.Crypt32.CRYPTPROTECTMEMORY_BLOCK_SIZE; + return ((byteSize + (blockSize - 1)) / blockSize) * blockSize; } private void ProtectMemory() @@ -285,7 +26,7 @@ namespace System.Security if (_decryptedLength != 0 && !_encrypted && - !Interop.Crypt32.CryptProtectMemory(_buffer, _buffer.Length * sizeof(char), Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS)) + !Interop.Crypt32.CryptProtectMemory(_buffer, (uint)_buffer.ByteLength, Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } @@ -300,7 +41,7 @@ namespace System.Security if (_decryptedLength != 0 && _encrypted && - !Interop.Crypt32.CryptUnprotectMemory(_buffer, _buffer.Length * sizeof(char), Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS)) + !Interop.Crypt32.CryptUnprotectMemory(_buffer, (uint)_buffer.ByteLength, Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.cs b/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.cs index c9a1eba..35deaba 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Security/SecureString.cs @@ -12,12 +12,14 @@ namespace System.Security { private const int MaxLength = 65536; private readonly object _methodLock = new object(); - private bool _readOnly; + private UnmanagedBuffer? _buffer; private int _decryptedLength; + private bool _encrypted; + private bool _readOnly; - public unsafe SecureString() + public SecureString() { - InitializeSecureString(null, 0); + Initialize(ReadOnlySpan.Empty); } [CLSCompliant(false)] @@ -36,7 +38,38 @@ namespace System.Security throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Length); } - InitializeSecureString(value, length); + Initialize(new ReadOnlySpan(value, length)); + } + + private void Initialize(ReadOnlySpan value) + { + _buffer = UnmanagedBuffer.Allocate(GetAlignedByteSize(value.Length)); + _decryptedLength = value.Length; + + SafeBuffer? bufferToRelease = null; + try + { + Span span = AcquireSpan(ref bufferToRelease); + value.CopyTo(span); + } + finally + { + ProtectMemory(); + bufferToRelease?.DangerousRelease(); + } + } + + private SecureString(SecureString str) + { + Debug.Assert(str._buffer != null, "Expected other SecureString's buffer to be non-null"); + Debug.Assert(str._encrypted, "Expected to be used only on encrypted SecureStrings"); + + _buffer = UnmanagedBuffer.Allocate((int)str._buffer.ByteLength); + Debug.Assert(_buffer != null); + UnmanagedBuffer.Copy(str._buffer, _buffer, str._buffer.ByteLength); + + _decryptedLength = str._decryptedLength; + _encrypted = str._encrypted; } public int Length @@ -48,13 +81,52 @@ namespace System.Security } } + private void EnsureCapacity(int capacity) + { + if (capacity > MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity); + } + + Debug.Assert(_buffer != null); + if ((uint)capacity * sizeof(char) <= _buffer.ByteLength) + { + return; + } + + UnmanagedBuffer oldBuffer = _buffer; + UnmanagedBuffer newBuffer = UnmanagedBuffer.Allocate(GetAlignedByteSize(capacity)); + UnmanagedBuffer.Copy(oldBuffer, newBuffer, (uint)_decryptedLength * sizeof(char)); + _buffer = newBuffer; + oldBuffer.Dispose(); + } + public void AppendChar(char c) { lock (_methodLock) { EnsureNotDisposed(); EnsureNotReadOnly(); - AppendCharCore(c); + + Debug.Assert(_buffer != null); + + SafeBuffer? bufferToRelease = null; + + try + { + UnprotectMemory(); + + EnsureCapacity(_decryptedLength + 1); + + Span span = AcquireSpan(ref bufferToRelease); + span[_decryptedLength] = c; + _decryptedLength++; + } + finally + { + ProtectMemory(); + bufferToRelease?.DangerousRelease(); + } } } @@ -65,7 +137,21 @@ namespace System.Security { EnsureNotDisposed(); EnsureNotReadOnly(); - ClearCore(); + + Debug.Assert(_buffer != null); + + _decryptedLength = 0; + + SafeBuffer? bufferToRelease = null; + try + { + Span span = AcquireSpan(ref bufferToRelease); + span.Clear(); + } + finally + { + bufferToRelease?.DangerousRelease(); + } } } @@ -83,7 +169,11 @@ namespace System.Security { lock (_methodLock) { - DisposeCore(); + if (_buffer != null) + { + _buffer.Dispose(); + _buffer = null; + } } } @@ -99,7 +189,26 @@ namespace System.Security EnsureNotDisposed(); EnsureNotReadOnly(); - InsertAtCore(index, c); + Debug.Assert(_buffer != null); + + SafeBuffer? bufferToRelease = null; + + try + { + UnprotectMemory(); + + EnsureCapacity(_decryptedLength + 1); + + Span span = AcquireSpan(ref bufferToRelease); + span.Slice(index, _decryptedLength - index).CopyTo(span.Slice(index + 1)); + span[index] = c; + _decryptedLength++; + } + finally + { + ProtectMemory(); + bufferToRelease?.DangerousRelease(); + } } } @@ -119,15 +228,31 @@ namespace System.Security { lock (_methodLock) { - EnsureNotDisposed(); - EnsureNotReadOnly(); - if (index < 0 || index >= _decryptedLength) { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString); } - RemoveAtCore(index); + EnsureNotDisposed(); + EnsureNotReadOnly(); + + Debug.Assert(_buffer != null); + + SafeBuffer? bufferToRelease = null; + + try + { + UnprotectMemory(); + + Span span = AcquireSpan(ref bufferToRelease); + span.Slice(index + 1, _decryptedLength - (index + 1)).CopyTo(span.Slice(index)); + _decryptedLength--; + } + finally + { + ProtectMemory(); + bufferToRelease?.DangerousRelease(); + } } } @@ -139,15 +264,41 @@ namespace System.Security { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString); } - Debug.Assert(index <= int.MaxValue / sizeof(char)); EnsureNotDisposed(); EnsureNotReadOnly(); - SetAtCore(index, c); + Debug.Assert(_buffer != null); + + SafeBuffer? bufferToRelease = null; + + try + { + UnprotectMemory(); + + Span span = AcquireSpan(ref bufferToRelease); + span[index] = c; + } + finally + { + ProtectMemory(); + bufferToRelease?.DangerousRelease(); + } } } + private unsafe Span AcquireSpan(ref SafeBuffer? bufferToRelease) + { + SafeBuffer buffer = _buffer!; + + bool ignore = false; + buffer.DangerousAddRef(ref ignore); + + bufferToRelease = buffer; + + return new Span((byte*)buffer.DangerousGetHandle(), (int)(buffer.ByteLength / 2)); + } + private void EnsureNotReadOnly() { if (_readOnly) @@ -164,12 +315,41 @@ namespace System.Security } } - internal IntPtr MarshalToBSTR() + internal unsafe IntPtr MarshalToBSTR() { lock (_methodLock) { EnsureNotDisposed(); - return MarshalToBSTRCore(); + + UnprotectMemory(); + + SafeBuffer? bufferToRelease = null; + IntPtr ptr = IntPtr.Zero; + int length = 0; + try + { + Span span = AcquireSpan(ref bufferToRelease); + + length = _decryptedLength; + ptr = Marshal.AllocBSTR(length); + span.Slice(0, length).CopyTo(new Span((void*)ptr, length)); + + IntPtr result = ptr; + ptr = IntPtr.Zero; + return result; + } + finally + { + // If we failed for any reason, free the new buffer + if (ptr != IntPtr.Zero) + { + new Span((void*)ptr, length).Clear(); + Marshal.FreeBSTR(ptr); + } + + ProtectMemory(); + bufferToRelease?.DangerousRelease(); + } } } @@ -178,19 +358,122 @@ namespace System.Security lock (_methodLock) { EnsureNotDisposed(); - return MarshalToStringCore(globalAlloc, unicode); + + UnprotectMemory(); + + SafeBuffer? bufferToRelease = null; + IntPtr ptr = IntPtr.Zero; + int byteLength = 0; + try + { + Span span = AcquireSpan(ref bufferToRelease).Slice(0, _decryptedLength); + + if (unicode) + { + byteLength = (span.Length + 1) * sizeof(char); + } + else + { + byteLength = Marshal.GetAnsiStringByteCount(span); + } + + if (globalAlloc) + { + ptr = Marshal.AllocHGlobal(byteLength); + } + else + { + ptr = Marshal.AllocCoTaskMem(byteLength); + } + + if (unicode) + { + Span resultSpan = new Span((void*)ptr, byteLength / sizeof(char)); + span.CopyTo(resultSpan); + resultSpan[resultSpan.Length - 1] = '\0'; + } + else + { + Marshal.GetAnsiStringBytes(span, new Span((void*)ptr, byteLength)); + } + + IntPtr result = ptr; + ptr = IntPtr.Zero; + return result; + } + finally + { + // If we failed for any reason, free the new buffer + if (ptr != IntPtr.Zero) + { + new Span((void*)ptr, byteLength).Clear(); + + if (globalAlloc) + { + Marshal.FreeHGlobal(ptr); + } + else + { + Marshal.FreeCoTaskMem(ptr); + } + } + + ProtectMemory(); + bufferToRelease?.DangerousRelease(); + } } } - private static void MarshalFree(IntPtr ptr, bool globalAlloc) + /// SafeBuffer for managing memory meant to be kept confidential. + private sealed class UnmanagedBuffer : SafeBuffer { - if (globalAlloc) + // A local copy of byte length to be able to access it in ReleaseHandle without the risk of throwing exceptions + private int _byteLength; + + private UnmanagedBuffer() : base(true) { } + + public static UnmanagedBuffer Allocate(int byteLength) { - Marshal.FreeHGlobal(ptr); + Debug.Assert(byteLength >= 0); + UnmanagedBuffer buffer = new UnmanagedBuffer(); + buffer.SetHandle(Marshal.AllocHGlobal(byteLength)); + buffer.Initialize((ulong)byteLength); + buffer._byteLength = byteLength; + return buffer; } - else + + internal static unsafe void Copy(UnmanagedBuffer source, UnmanagedBuffer destination, ulong bytesLength) + { + if (bytesLength == 0) + { + return; + } + + byte* srcPtr = null, dstPtr = null; + try + { + source.AcquirePointer(ref srcPtr); + destination.AcquirePointer(ref dstPtr); + Buffer.MemoryCopy(srcPtr, dstPtr, destination.ByteLength, bytesLength); + } + finally + { + if (dstPtr != null) + { + destination.ReleasePointer(); + } + if (srcPtr != null) + { + source.ReleasePointer(); + } + } + } + + protected override unsafe bool ReleaseHandle() { - Marshal.FreeCoTaskMem(ptr); + new Span((void*)handle, _byteLength).Clear(); + Marshal.FreeHGlobal(handle); + return true; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs index 9f178f5..79b7347 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs @@ -315,7 +315,7 @@ namespace System else { // Find bitflag offset of first match and add to current offset - return (int)(offset + (BitOperations.TrailingZeroCount(matches) / sizeof(char))); + return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char))); } } @@ -341,7 +341,7 @@ namespace System // Find bitflag offset of first match and add to current offset, // flags are in bytes so divide for chars - return (int)(offset + (BitOperations.TrailingZeroCount(matches) / sizeof(char))); + return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char))); } while (lengthToExamine > 0); } @@ -365,7 +365,7 @@ namespace System { // Find bitflag offset of first match and add to current offset, // flags are in bytes so divide for chars - return (int)(offset + (BitOperations.TrailingZeroCount(matches) / sizeof(char))); + return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char))); } } @@ -404,7 +404,7 @@ namespace System // Find bitflag offset of first match and add to current offset, // flags are in bytes so divide for chars - return (int)(offset + (BitOperations.TrailingZeroCount(matches) / sizeof(char))); + return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char))); } while (lengthToExamine > 0); } diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.cs index 220f79e..d558758 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.cs @@ -24,7 +24,7 @@ namespace System return; #if AMD64 || ARM64 - // The exact matrix on when RhZeroMemory is faster than InitBlockUnaligned is very complex. The factors to consider include + // The exact matrix on when ZeroMemory is faster than InitBlockUnaligned is very complex. The factors to consider include // type of hardware and memory aligment. This threshold was chosen as a good balance accross different configurations. if (byteLength > 768) goto PInvoke; @@ -336,7 +336,7 @@ namespace System #endif PInvoke: - RuntimeImports.RhZeroMemory(ref b, byteLength); + Buffer._ZeroMemory(ref b, byteLength); } public static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength) -- 2.7.4