diff options
author | Jeremy Koritzinsky <jkoritzinsky@gmail.com> | 2019-03-29 09:49:55 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-03-29 09:49:55 -0700 |
commit | 6b889aba05b8e1ccc9cef793a2d1293e30598453 (patch) | |
tree | 14ff0a7b99564cf98dddfe67c4a3302f2d5c926e /src/vm/dllimport.cpp | |
parent | cda8f7bf0a826878fc6ca6b9d7bc6f6011626cc9 (diff) | |
download | coreclr-6b889aba05b8e1ccc9cef793a2d1293e30598453.tar.gz coreclr-6b889aba05b8e1ccc9cef793a2d1293e30598453.tar.bz2 coreclr-6b889aba05b8e1ccc9cef793a2d1293e30598453.zip |
Correctly marshal structure return values in member functions on Win-x64 and Win-x86 (#23145)
* In Windows-x64, if we have a native member function signature with a struct return type, we need to do a by-ref return.
* Implement support for marshalling structure return values via a return buffer argument correctly in instance signatures on AMD64-Windows.
* Change field initialization ordering to satisfy warning.
* Try to narrow down the conditions that trigger these changes to just COM methods.
* Don't bash the return type on AMD64 Windows. Only treat it as a byref return buffer.
* PR feedback.
* Enable returning structs from COM methods via a return buffer on x86 for structs <= 8 bytes.
* Add test for struct returns with ThisCall. Extend the "struct return buffer" fix to functions marked as unmanaged thiscall since they all must be instance methods
* Don't include the return-type-bashing switch on AMD64 platforms.
* Don't do the signature swapping/copy on non-instance functions with struct returns.
* Cast the return type of GetStubTargetCallingConv to the right calling convention enum type.
* If we're doing a thiscall, marshal the "this" parameter before the return buffer (if the return buffer exists) on all platforms.
* Remove temporary logging code I added in for debugging.
* Clean up class naming.
* Try using a vtable instead of a pointer-to-member-function.
* Remove delete of class with non-virtual destructor
Diffstat (limited to 'src/vm/dllimport.cpp')
-rw-r--r-- | src/vm/dllimport.cpp | 179 |
1 files changed, 99 insertions, 80 deletions
diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp index b200a1bccb..c83f05ce05 100644 --- a/src/vm/dllimport.cpp +++ b/src/vm/dllimport.cpp @@ -1737,7 +1737,7 @@ NDirectStubLinker::NDirectStubLinker( int iLCIDParamIdx, BOOL fTargetHasThis, BOOL fStubHasThis) - : ILStubLinker(pModule, signature, pTypeContext, pTargetMD, fTargetHasThis, fStubHasThis, !SF_IsCOMStub(dwStubFlags)), + : ILStubLinker(pModule, signature, pTypeContext, pTargetMD, fTargetHasThis, fStubHasThis, !SF_IsCOMStub(dwStubFlags), SF_IsReverseStub(dwStubFlags)), m_pCleanupFinallyBeginLabel(NULL), m_pCleanupFinallyEndLabel(NULL), m_pSkipExceptionCleanupLabel(NULL), @@ -1747,6 +1747,7 @@ NDirectStubLinker::NDirectStubLinker( m_fHasCleanupCode(FALSE), m_fHasExceptionCleanupCode(FALSE), m_fCleanupWorkListIsSetup(FALSE), + m_targetHasThis(fTargetHasThis), m_dwThreadLocalNum(-1), m_dwCleanupWorkListLocalNum(-1), m_dwRetValLocalNum(-1), @@ -3661,42 +3662,19 @@ static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig, if (SF_IsCOMStub(dwStubFlags)) { - if (marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS || - marshalType == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS || + // We don't support native methods that return VARIANTs, non-blittable structs, GUIDs, or DECIMALs directly. + if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT || + marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS || marshalType == MarshalInfo::MARSHAL_TYPE_GUID || marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL) { -#ifndef _TARGET_X86_ - // We cannot optimize marshalType to MARSHAL_TYPE_GENERIC_* because the JIT works with exact types - // and would refuse to compile the stub if it implicitly converted between scalars and value types (also see - // code:MarshalInfo.MarhalInfo where we do the optimization on x86). We want to throw only if the structure - // is too big to be returned in registers. - if (marshalType != MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS || - IsUnmanagedValueTypeReturnedByRef(returnInfo.GetNativeArgSize())) -#endif // _TARGET_X86_ + if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags)) { - if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags)) - { - // Note that this limitation is very likely not needed anymore and could be lifted if we care. - COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG); - } + COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG); } - - pss->MarshalReturn(&returnInfo, argOffset); } - else - { - // We don't support native methods that return VARIANTs directly. - if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT) - { - if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags)) - { - COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG); - } - } - pss->MarshalReturn(&returnInfo, argOffset); - } + pss->MarshalReturn(&returnInfo, argOffset); } else #endif // FEATURE_COMINTEROP @@ -3953,14 +3931,21 @@ static void CreateNDirectStubWorker(StubState* pss, // Normally we would like this to be false so that we use the correct signature // in the IL_STUB, (i.e if it returns a value class then the signature will use that) // When this bool is true we change the return type to void and explicitly add a - // return buffer argument as the first argument. - BOOL fMarshalReturnValueFirst = false; + // return buffer argument as the first argument so as to match the native calling convention correctly. + BOOL fMarshalReturnValueFirst = FALSE; + + BOOL fReverseWithReturnBufferArg = FALSE; // We can only change fMarshalReturnValueFirst to true when we are NOT doing HRESULT-swapping! - // + // When we are HRESULT-swapping, the managed return type is actually the type of the last parameter and not the return type. + // The native return type of an HRESULT-swapped function is an HRESULT, which never uses a return-buffer argument. + // Since the managed return type is actually the last parameter, we need to marshal it after the last parameter in the managed signature + // to make sure we match the native signature correctly (when marshalling parameters, we add them to the native stub signature). if (!SF_IsHRESULTSwapping(dwStubFlags)) { - + bool isInstanceMethod = fStubNeedsCOM || fThisCall; + // We cannot just use pSig.GetReturnType() here since it will return ELEMENT_TYPE_VALUETYPE for enums. + bool isReturnTypeValueType = msig.GetRetTypeHandleThrowing().GetVerifierCorElementType() == ELEMENT_TYPE_VALUETYPE; #if defined(_TARGET_X86_) || defined(_TARGET_ARM_) // JIT32 has problems in generating code for pinvoke ILStubs which do a return in return buffer. // Therefore instead we change the signature of calli to return void and make the return buffer as first @@ -3972,55 +3957,18 @@ static void CreateNDirectStubWorker(StubState* pss, #ifdef UNIX_X86_ABI // For functions with value type class, managed and unmanaged calling convention differ fMarshalReturnValueFirst = HasRetBuffArgUnmanagedFixup(&msig); -#else // UNIX_X86_ABI +#elif defined(_TARGET_ARM_) fMarshalReturnValueFirst = HasRetBuffArg(&msig); +#else + // On Windows-X86, the native signature might need a return buffer when the managed doesn't (specifically when the native signature is a member function). + fMarshalReturnValueFirst = HasRetBuffArg(&msig) || (isInstanceMethod && isReturnTypeValueType); #endif // UNIX_X86_ABI - +#elif defined(_TARGET_AMD64_) + fMarshalReturnValueFirst = isInstanceMethod && isReturnTypeValueType; #endif // defined(_TARGET_X86_) || defined(_TARGET_ARM_) - - } - - if (fMarshalReturnValueFirst) - { - marshalType = DoMarshalReturnValue(msig, - pParamTokenArray, - nlType, - nlFlags, - 0, - pss, - fThisCall, - argOffset, - dwStubFlags, - pMD, - nativeStackSize, - fStubNeedsCOM, - 0 - DEBUG_ARG(pSigDesc->m_pDebugName) - DEBUG_ARG(pSigDesc->m_pDebugClassName) - ); - - if (marshalType == MarshalInfo::MARSHAL_TYPE_DATE || - marshalType == MarshalInfo::MARSHAL_TYPE_CURRENCY || - marshalType == MarshalInfo::MARSHAL_TYPE_ARRAYWITHOFFSET || - marshalType == MarshalInfo::MARSHAL_TYPE_HANDLEREF || - marshalType == MarshalInfo::MARSHAL_TYPE_ARGITERATOR -#ifdef FEATURE_COMINTEROP - || marshalType == MarshalInfo::MARSHAL_TYPE_OLECOLOR -#endif // FEATURE_COMINTEROP - ) - { - // These are special non-blittable types returned by-ref in managed, - // but marshaled as primitive values returned by-value in unmanaged. - } - else - { - // This is an ordinary value type - see if it is returned by-ref. - MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable(); - if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize())) - { - nativeStackSize += sizeof(LPVOID); - } - } +#ifdef _WIN32 + fReverseWithReturnBufferArg = fMarshalReturnValueFirst && SF_IsReverseStub(dwStubFlags); +#endif } // @@ -4088,6 +4036,77 @@ static void CreateNDirectStubWorker(StubState* pss, // Marshal the parameters int argidx = 1; int nativeArgIndex = 0; + + // If we are generating a return buffer on a member function that is marked as thiscall (as opposed to being a COM method) + // then we need to marshal the this parameter first and the return buffer second. + // We don't need to do this for COM methods because the "this" is implied as argument 0 by the signature of the stub. + if (fThisCall && fMarshalReturnValueFirst) + { + msig.NextArg(); + + MarshalInfo &info = pParamMarshalInfo[argidx - 1]; + pss->MarshalArgument(&info, argOffset, GetStackOffsetFromStackSize(nativeStackSize, fThisCall)); + nativeStackSize += info.GetNativeArgSize(); + + fStubNeedsCOM |= info.MarshalerRequiresCOM(); + + // make sure that the first parameter is enregisterable + if (info.GetNativeArgSize() > sizeof(SLOT)) + COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL); + + argidx++; + } + + // If we're doing a native->managed call and are generating a return buffer, + // we need to move all of the actual arguments over one and have the return value be the first argument (after the this pointer if applicable). + if (fReverseWithReturnBufferArg) + { + ++argOffset; + } + + if (fMarshalReturnValueFirst) + { + marshalType = DoMarshalReturnValue(msig, + pParamTokenArray, + nlType, + nlFlags, + 0, + pss, + fThisCall, + argOffset, + dwStubFlags, + pMD, + nativeStackSize, + fStubNeedsCOM, + 0 + DEBUG_ARG(pSigDesc->m_pDebugName) + DEBUG_ARG(pSigDesc->m_pDebugClassName) + ); + + if (marshalType == MarshalInfo::MARSHAL_TYPE_DATE || + marshalType == MarshalInfo::MARSHAL_TYPE_CURRENCY || + marshalType == MarshalInfo::MARSHAL_TYPE_ARRAYWITHOFFSET || + marshalType == MarshalInfo::MARSHAL_TYPE_HANDLEREF || + marshalType == MarshalInfo::MARSHAL_TYPE_ARGITERATOR +#ifdef FEATURE_COMINTEROP + || marshalType == MarshalInfo::MARSHAL_TYPE_OLECOLOR +#endif // FEATURE_COMINTEROP + ) + { + // These are special non-blittable types returned by-ref in managed, + // but marshaled as primitive values returned by-value in unmanaged. + } + else + { + // This is an ordinary value type - see if it is returned by-ref. + MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable(); + if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize())) + { + nativeStackSize += sizeof(LPVOID); + } + } + } + while (argidx <= numArgs) { #ifdef FEATURE_COMINTEROP |