diff options
-rw-r--r-- | src/vm/method.cpp | 117 | ||||
-rw-r--r-- | src/vm/method.hpp | 2 | ||||
-rw-r--r-- | tests/src/Interop/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/src/Interop/PInvoke/ExactSpelling/CMakeLists.txt | 14 | ||||
-rw-r--r-- | tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingNative.cpp | 179 | ||||
-rw-r--r-- | tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.cs | 110 | ||||
-rw-r--r-- | tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.csproj | 29 |
7 files changed, 401 insertions, 51 deletions
diff --git a/src/vm/method.cpp b/src/vm/method.cpp index a2440ff92a..b16e27d33e 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<LPVOID>(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<LPVOID>(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<LPVOID>(pFunc); } #endif // CROSSGEN_COMPILE diff --git a/src/vm/method.hpp b/src/vm/method.hpp index 002985f4b8..414d2a33f0 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 934ab7a483..c81008bd2b 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 0000000000..27850f4d3b --- /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 0000000000..a4dad9df67 --- /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 <stdio.h> +#include <xplatform.h> + +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 0000000000..7616e6d6d8 --- /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 0000000000..77a6dcc087 --- /dev/null +++ b/tests/src/Interop/PInvoke/ExactSpelling/ExactSpellingTest.csproj @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <AssemblyName>ExactSpellingTest</AssemblyName> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid> + <OutputType>Exe</OutputType> + <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> + </PropertyGroup> + <!-- Default configurations to help VS understand the configurations --> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'"></PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'"></PropertyGroup> + <ItemGroup> + <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> + <Visible>False</Visible> + </CodeAnalysisDependentAssemblyPaths> + </ItemGroup> + <ItemGroup> + <Compile Include="ExactSpellingTest.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="CMakeLists.txt" /> + </ItemGroup> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> +</Project> |