From f56dc8e76dc4bdf66b1f4376a9b8d0fc107c5926 Mon Sep 17 00:00:00 2001 From: Zeng Jiang Date: Sat, 20 Oct 2018 01:07:59 +0800 Subject: [PATCH] Add PInvoke/ExactSpelling tests (#19303) * Add PInvoke/ExactSpelling tests Refactor tests to fit with the rest of the Interop tests. Fix up test to cleanly run. Change CMakeLists.txt to match the rest of the tests. Include Interop.cmake in CMakeLists.txt Remove Service. * On x86 enable stdcall mangling irrespective of ExactSpelling and account for the charset suffix when ExactSpelling = false. Change variable name. Clean up the FindEntryPoint. The logic flow now matches CoreRT + CoreCLR specific features (ordinals and stdcall mangling). PR Feedback. Fix format specifier. Add back probing null check. Fix offset calculation for stdcall mangling. Probe the stdcall-mangled versions of the original entry-point names when ExactSpelling isn't set. Cleanup. --- src/vm/method.cpp | 117 ++++++++------ src/vm/method.hpp | 2 + tests/src/Interop/CMakeLists.txt | 1 + .../Interop/PInvoke/ExactSpelling/CMakeLists.txt | 14 ++ .../PInvoke/ExactSpelling/ExactSpellingNative.cpp | 179 +++++++++++++++++++++ .../PInvoke/ExactSpelling/ExactSpellingTest.cs | 110 +++++++++++++ .../PInvoke/ExactSpelling/ExactSpellingTest.csproj | 29 ++++ 7 files changed, 401 insertions(+), 51 deletions(-) create mode 100644 tests/src/Interop/PInvoke/ExactSpelling/CMakeLists.txt create mode 100644 tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingNative.cpp create mode 100644 tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.cs create mode 100644 tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.csproj diff --git a/src/vm/method.cpp b/src/vm/method.cpp index a2440ff..b16e27d 100644 --- a/src/vm/method.cpp +++ b/src/vm/method.cpp @@ -4948,6 +4948,51 @@ void NDirectMethodDesc::InterlockedSetNDirectFlags(WORD wFlags) } #ifndef CROSSGEN_COMPILE +FARPROC NDirectMethodDesc::FindEntryPointWithMangling(HINSTANCE hMod, PTR_CUTF8 entryPointName) const +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + FARPROC pFunc = GetProcAddress(hMod, entryPointName); + +#if defined(_TARGET_X86_) + + if (pFunc) + { + return pFunc; + } + + if (IsStdCall()) + { + DWORD probedEntrypointNameLength = (DWORD)(strlen(entryPointName) + 1); // 1 for null terminator + int dstbufsize = (int)(sizeof(char) * (probedEntrypointNameLength + 10)); // 10 for stdcall mangling + LPSTR szProbedEntrypointName = ((LPSTR)_alloca(dstbufsize + 1)); + szProbedEntrypointName[0] = '_'; + strcpy_s(szProbedEntrypointName + 1, dstbufsize, entryPointName); + szProbedEntrypointName[probedEntrypointNameLength] = '\0'; // Add an extra '\0'. + + UINT16 numParamBytesMangle = GetStackArgumentSize(); + + if (IsStdCallWithRetBuf()) + { + _ASSERTE(numParamBytesMangle >= sizeof(LPVOID)); + numParamBytesMangle -= (UINT16)sizeof(LPVOID); + } + + sprintf_s(szProbedEntrypointName + probedEntrypointNameLength, dstbufsize - probedEntrypointNameLength + 1, "@%lu", (ULONG)numParamBytesMangle); + pFunc = GetProcAddress(hMod, szProbedEntrypointName); + } + +#endif + + return pFunc; +} + //******************************************************************************* LPVOID NDirectMethodDesc::FindEntryPoint(HINSTANCE hMod) const { @@ -4959,84 +5004,54 @@ LPVOID NDirectMethodDesc::FindEntryPoint(HINSTANCE hMod) const } CONTRACTL_END; - char const * funcName = NULL; + char const * funcName = GetEntrypointName(); - FARPROC pFunc = NULL, pFuncW = NULL; + FARPROC pFunc = NULL; #ifndef FEATURE_PAL // Handle ordinals. - if (GetEntrypointName()[0] == '#') + if (funcName[0] == '#') { - long ordinal = atol(GetEntrypointName()+1); + long ordinal = atol(funcName + 1); return reinterpret_cast(GetProcAddress(hMod, (LPCSTR)(size_t)((UINT16)ordinal))); } #endif - // Just look for the unmangled name. If it is unicode fcn, we are going + // Just look for the user-provided name without charset suffixes. If it is unicode fcn, we are going // to need to check for the 'W' API because it takes precedence over the // unmangled one (on NT some APIs have unmangled ANSI exports). - pFunc = GetProcAddress(hMod, funcName = GetEntrypointName()); + pFunc = FindEntryPointWithMangling(hMod, funcName); if ((pFunc != NULL && IsNativeAnsi()) || IsNativeNoMangled()) { - return (LPVOID)pFunc; + return reinterpret_cast(pFunc); } + DWORD probedEntrypointNameLength = (DWORD)(strlen(funcName) + 1); // +1 for charset decorations + // Allocate space for a copy of the entry point name. - int dstbufsize = (int)(sizeof(char) * (strlen(GetEntrypointName()) + 1 + 20)); // +1 for the null terminator - // +20 for various decorations + int dstbufsize = (int)(sizeof(char) * (probedEntrypointNameLength + 1)); // +1 for the null terminator - // Allocate a single character before the start of this string to enable quickly - // prepending a '_' character if we look for a stdcall entrypoint later on. - LPSTR szAnsiEntrypointName = ((LPSTR)_alloca(dstbufsize + 1)) + 1; + LPSTR szProbedEntrypointName = ((LPSTR)_alloca(dstbufsize)); // Copy the name so we can mangle it. - strcpy_s(szAnsiEntrypointName,dstbufsize,GetEntrypointName()); - DWORD nbytes = (DWORD)(strlen(GetEntrypointName()) + 1); - szAnsiEntrypointName[nbytes] = '\0'; // Add an extra '\0'. + strcpy_s(szProbedEntrypointName, dstbufsize, funcName); + szProbedEntrypointName[probedEntrypointNameLength] = '\0'; // Add an extra '\0'. - - // If the program wants the ANSI api or if Unicode APIs are unavailable. - if (IsNativeAnsi()) + if(!IsNativeNoMangled()) { - szAnsiEntrypointName[nbytes-1] = 'A'; - pFunc = GetProcAddress(hMod, funcName = szAnsiEntrypointName); - } - else - { - szAnsiEntrypointName[nbytes-1] = 'W'; - pFuncW = GetProcAddress(hMod, szAnsiEntrypointName); + szProbedEntrypointName[probedEntrypointNameLength - 1] = IsNativeAnsi() ? 'A' : 'W'; - // This overrides the unmangled API. See the comment above. - if (pFuncW != NULL) - { - pFunc = pFuncW; - funcName = szAnsiEntrypointName; - } - } + FARPROC pProbedFunc = FindEntryPointWithMangling(hMod, szProbedEntrypointName); - if (!pFunc) - { - -#if defined(_TARGET_X86_) - /* try mangled names only for __stdcalls */ - if (!pFunc && IsStdCall()) + if(pProbedFunc != NULL) { - UINT16 numParamBytesMangle = GetStackArgumentSize(); - - if (IsStdCallWithRetBuf()) - { - _ASSERTE(numParamBytesMangle >= sizeof(LPVOID)); - numParamBytesMangle -= (UINT16)sizeof(LPVOID); - } - - szAnsiEntrypointName[-1] = '_'; - sprintf_s(szAnsiEntrypointName + nbytes - 1, dstbufsize - (nbytes - 1), "@%ld", (ULONG)numParamBytesMangle); - pFunc = GetProcAddress(hMod, funcName = szAnsiEntrypointName - 1); + pFunc = pProbedFunc; } -#endif // _TARGET_X86_ + + probedEntrypointNameLength++; } - return (LPVOID)pFunc; + return reinterpret_cast(pFunc); } #endif // CROSSGEN_COMPILE diff --git a/src/vm/method.hpp b/src/vm/method.hpp index 002985f..414d2a3 100644 --- a/src/vm/method.hpp +++ b/src/vm/method.hpp @@ -2837,6 +2837,8 @@ public: LPVOID FindEntryPoint(HINSTANCE hMod) const; private: + FARPROC FindEntryPointWithMangling(HINSTANCE mod, PTR_CUTF8 entryPointName) const; + #ifdef MDA_SUPPORTED Stub* GenerateStubForMDA(LPVOID pNativeTarget, Stub *pInnerStub); #endif // MDA_SUPPORTED diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt index 934ab7a..c81008b 100644 --- a/tests/src/Interop/CMakeLists.txt +++ b/tests/src/Interop/CMakeLists.txt @@ -13,6 +13,7 @@ SET(CLR_INTEROP_TEST_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) include_directories(common) add_subdirectory(PInvoke/Primitives/Int) +add_subdirectory(PInvoke/ExactSpelling) add_subdirectory(NativeCallable) add_subdirectory(PrimitiveMarshalling/Bool) add_subdirectory(PrimitiveMarshalling/UIntPtr) diff --git a/tests/src/Interop/PInvoke/ExactSpelling/CMakeLists.txt b/tests/src/Interop/PInvoke/ExactSpelling/CMakeLists.txt new file mode 100644 index 0000000..27850f4 --- /dev/null +++ b/tests/src/Interop/PInvoke/ExactSpelling/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required (VERSION 2.6) +project (ExactSpellingNative) +include_directories(${INC_PLATFORM_DIR}) +include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") +set(SOURCES ExactSpellingNative.cpp) + +# add the executable +add_library (ExactSpellingNative SHARED ${SOURCES}) +target_link_libraries(ExactSpellingNative ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS ExactSpellingNative DESTINATION bin) + + diff --git a/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingNative.cpp b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingNative.cpp new file mode 100644 index 0000000..a4dad9d --- /dev/null +++ b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingNative.cpp @@ -0,0 +1,179 @@ +// 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 +#include + +int intManaged = 1000; +int intNative = 2000; + +int intReturn = 3000; +int intReturnA = 4000; +int intReturnW = 5000; + +int intErrReturn = 6000; + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_Int_InOut(/*[In,Out]*/int intValue) +{ + //Check the input + if(intValue != intManaged) + { + printf("Error in Function Marshal_Int_InOut(Native Client)\n"); + + //Expected + printf("Expected:%u\n", intManaged); + + //Actual + printf("Actual:%u\n",intValue); + + //Return the error value instead if verification failed + return intErrReturn; + } + + //In-Place Change + intValue = intNative; + + //Return + return intReturn; +} + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_Int_InOutA(/*[In,Out]*/int intValue) +{ + //Check the input + if(intValue != intManaged) + { + printf("Error in Function Marshal_Int_InOutA(Native Client)\n"); + + //Expected + printf("Expected:%u\n", intManaged); + + //Actual + printf("Actual:%u\n",intValue); + + //Return the error value instead if verification failed + return intErrReturn; + } + + //In-Place Change + intValue = intNative; + + //Return + return intReturnA; +} + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_Int_InOutW(/*[In,Out]*/int intValue) +{ + //Check the input + if(intValue != intManaged) + { + printf("Error in Function Marshal_Int_InOutW(Native Client)\n"); + + //Expected + printf("Expected:%u\n", intManaged); + + //Actual + printf("Actual:%u\n",intValue); + + //Return the error value instead if verification failed + return intErrReturn; + } + + //In-Place Change + intValue = intNative; + + //Return + return intReturnW; +} + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE MarshalPointer_Int_InOut(/*[in,out]*/int *pintValue) +{ + //Check the input + if(*pintValue != intManaged) + { + printf("Error in Function MarshalPointer_Int_InOut(Native Client)\n"); + + //Expected + printf("Expected:%u\n", intManaged); + + //Actual + printf("Actual:%u\n",*pintValue); + + //Return the error value instead if verification failed + return intErrReturn; + } + + //In-Place Change + *pintValue = intNative; + + //Return + return intReturn; +} + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE MarshalPointer_Int_InOutA(/*[in,out]*/int *pintValue) +{ + //Check the input + if(*pintValue != intManaged) + { + printf("Error in Function MarshalPointer_Int_InOutA(Native Client)\n"); + + //Expected + printf("Expected:%u\n", intManaged); + + //Actual + printf("Actual:%u\n",*pintValue); + + //Return the error value instead if verification failed + return intErrReturn; + } + + //In-Place Change + *pintValue = intNative; + + //Return + return intReturnA; +} + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE MarshalPointer_Int_InOutW(/*[in,out]*/int *pintValue) +{ + //Check the input + if(*pintValue != intManaged) + { + printf("Error in Function MarshalPointer_Int_InOutW(Native Client)\n"); + + //Expected + printf("Expected:%u\n", intManaged); + + //Actual + printf("Actual:%u\n",*pintValue); + + //Return the error value instead if verification failed + return intErrReturn; + } + + //In-Place Change + *pintValue = intNative; + + //Return + return intReturnW; +} + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_Int_InOut2A(/*[In,Out]*/int intValue) +{ + return Marshal_Int_InOutA(intValue); +} + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_Int_InOut2W(/*[In,Out]*/int intValue) +{ + return Marshal_Int_InOutW(intValue); +} + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE MarshalPointer_Int_InOut2A(/*[in,out]*/int *pintValue) +{ + return MarshalPointer_Int_InOutA(pintValue); +} + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE MarshalPointer_Int_InOut2W(/*[in,out]*/int *pintValue) +{ + return MarshalPointer_Int_InOutW(pintValue); +} diff --git a/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.cs b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.cs new file mode 100644 index 0000000..7616e6d --- /dev/null +++ b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.cs @@ -0,0 +1,110 @@ +// 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; + +class ExactSpellingTest +{ + class Ansi + { + [DllImport("ExactSpellingNative", CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int Marshal_Int_InOut([In, Out] int intValue); + + [DllImport("ExactSpellingNative", CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int MarshalPointer_Int_InOut([In, Out] ref int intValue); + + [DllImport("ExactSpellingNative", CharSet = CharSet.Ansi, ExactSpelling = false)] + public static extern int Marshal_Int_InOut2([In, Out] int intValue); + + [DllImport("ExactSpellingNative", CharSet = CharSet.Ansi, ExactSpelling = false)] + public static extern int MarshalPointer_Int_InOut2([In, Out] ref int intValue); + } + + class Unicode + { + [DllImport("ExactSpellingNative", CharSet = CharSet.Unicode, ExactSpelling = true)] + public static extern int Marshal_Int_InOut([In, Out] int intValue); + + [DllImport("ExactSpellingNative", CharSet = CharSet.Unicode, ExactSpelling = true)] + public static extern int MarshalPointer_Int_InOut([In, Out] ref int intValue); + + [DllImport("ExactSpellingNative", CharSet = CharSet.Unicode, ExactSpelling = false)] + public static extern int Marshal_Int_InOut2([In, Out] int intValue); + + [DllImport("ExactSpellingNative", CharSet = CharSet.Unicode, ExactSpelling = false)] + public static extern int MarshalPointer_Int_InOut2([In, Out] ref int intValue); + } + + public static int Main(string[] args) + { + int failures = 0; + int intManaged = 1000; + int intNative = 2000; + int intReturn = 3000; + + Console.WriteLine("Method Unicode.Marshal_Int_InOut: ExactSpelling = true"); + int int1 = intManaged; + int intRet1 = Unicode.Marshal_Int_InOut(int1); + failures += Verify(intReturn, intManaged, intRet1, int1); + + Console.WriteLine("Method Unicode.MarshalPointer_Int_InOut: ExactSpelling = true"); + int int2 = intManaged; + int intRet2 = Unicode.MarshalPointer_Int_InOut(ref int2); + + failures += Verify(intReturn, intNative, intRet2, int2); + + Console.WriteLine("Method Ansi.Marshal_Int_InOut: ExactSpelling = true"); + int int3 = intManaged; + int intRet3 = Ansi.Marshal_Int_InOut(int3); + failures += Verify(intReturn, intManaged, intRet3, int3); + + Console.WriteLine("Method Ansi.MarshalPointer_Int_InOut: ExactSpelling = true"); + int int4 = intManaged; + int intRet4 = Ansi.MarshalPointer_Int_InOut(ref int4); + failures += Verify(intReturn, intNative, intRet4, int4); + + int intReturnAnsi = 4000; + int intReturnUnicode = 5000; + + Console.WriteLine("Method Unicode.Marshal_Int_InOut2: ExactSpelling = false"); + int int5 = intManaged; + int intRet5 = Unicode.Marshal_Int_InOut2(int5); + failures += Verify(intReturnUnicode, intManaged, intRet5, int5); + + Console.WriteLine("Method Unicode.MarshalPointer_Int_InOut2: ExactSpelling = false"); + int int6 = intManaged; + int intRet6 = Unicode.MarshalPointer_Int_InOut2(ref int6); + failures += Verify(intReturnUnicode, intNative, intRet6, int6); + + Console.WriteLine("Method Ansi.Marshal_Int_InOut2: ExactSpelling = false"); + int int7 = intManaged; + int intRet7 = Ansi.Marshal_Int_InOut2(int7); + failures += Verify(intReturnAnsi, intManaged, intRet7, int7); + + Console.WriteLine("Method Ansi.MarshalPointer_Int_InOut2: ExactSpelling = false"); + int int8 = intManaged; + int intRet8 = Ansi.MarshalPointer_Int_InOut2(ref int8); + failures += Verify(intReturnAnsi, intNative, intRet8, int8); + + return 100 + failures; + } + + private static int Verify(int expectedReturnValue, int expectedParameterValue, int actualReturnValue, int actualParameterValue) + { + int failures = 0; + if (expectedReturnValue != actualReturnValue) + { + failures++; + Console.WriteLine($"The return value is wrong. Expected {expectedReturnValue}, got {actualReturnValue}"); + } + if (expectedParameterValue != actualParameterValue) + { + failures++; + Console.WriteLine($"The parameter value is changed. Expected {expectedParameterValue}, got {actualParameterValue}"); + } + + return failures; + } +} diff --git a/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.csproj b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.csproj new file mode 100644 index 0000000..77a6dcc --- /dev/null +++ b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.csproj @@ -0,0 +1,29 @@ + + + + + Debug + AnyCPU + ExactSpellingTest + 2.0 + {F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + + + + + + + False + + + + + + + + + + -- 2.7.4