Enable IJW Native calling managed (#19750)
authorMorgan Brown <morganbr@users.noreply.github.com>
Fri, 7 Sep 2018 02:37:30 +0000 (19:37 -0700)
committerGitHub <noreply@github.com>
Fri, 7 Sep 2018 02:37:30 +0000 (19:37 -0700)
* Adds back code required to make IJW native->managed calls (if the runtime is already started) and includes a simple test.

15 files changed:
src/dlls/mscorrc/mscorrc.rc
src/dlls/mscorrc/resource.h
src/vm/ceeload.cpp
src/vm/ceeload.h
src/vm/domainfile.cpp
src/vm/peimage.cpp
src/vm/peimage.h
tests/src/Interop/CMakeLists.txt
tests/src/Interop/IJW/FakeMscoree/mscoree.cpp
tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/CMakeLists.txt
tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/IjwNativeDll.cpp
tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp [new file with mode: 0644]
tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.cs [new file with mode: 0644]
tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.csproj [new file with mode: 0644]

index a298563..713d7e0 100644 (file)
@@ -1086,6 +1086,7 @@ BEGIN
         BFA_BAD_CA_HEADER                       "Malformed custom attribute header."
         BFA_BAD_STRING_TOKEN                    "Bad string token."
         BFA_BAD_STRING_TOKEN_RANGE              "No string associated with token."
+        BFA_FIXUP_WRONG_PLATFORM                "Image has a platform-specific fixup type that is not compatible with this platform."
         BFA_UNEXPECTED_GENERIC_TOKENTYPE        "Token specifying generic type must be either a typeref or typedef."
         BFA_MDARRAY_BADRANK                     "Array rank may not be zero."
         BFA_SDARRAY_BADRANK                     "Single-dimensional array rank must be one."
index d58780f..0a42dad 100644 (file)
 #define BFA_BAD_CA_HEADER                       0x2050
 #define BFA_BAD_STRING_TOKEN                    0x2052
 #define BFA_BAD_STRING_TOKEN_RANGE              0x2053
+#define BFA_FIXUP_WRONG_PLATFORM                0x2054
 #define BFA_UNEXPECTED_GENERIC_TOKENTYPE        0x2055
 #define BFA_MDARRAY_BADRANK                     0x2056
 #define BFA_SDARRAY_BADRANK                     0x2057
index b3d2d59..19c5167 100644 (file)
@@ -6390,8 +6390,7 @@ MethodDesc *Module::FindMethod(mdToken pMethod)
         CONTRACT_VIOLATION(ThrowsViolation);
         char szMethodName [MAX_CLASSNAME_LENGTH];
         CEEInfo::findNameOfToken(this, pMethod, szMethodName, COUNTOF (szMethodName));
-        // This used to be LF_IJW, but changed to LW_INTEROP to reclaim a bit in our log facilities
-        // IJW itself is not supported in coreclr so this code should never be run.
+        // This used to be IJW, but changed to LW_INTEROP to reclaim a bit in our log facilities
         LOG((LF_INTEROP, LL_INFO10, "Failed to find Method: %s for Vtable Fixup\n", szMethodName));
 #endif // _DEBUG
     }
@@ -6837,7 +6836,379 @@ void Module::NotifyDebuggerUnload(AppDomain *pDomain)
     g_pDebugInterface->UnloadModule(this, pDomain);
 }
 
+#if !defined(CROSSGEN_COMPILE)
+//=================================================================================
+mdToken GetTokenForVTableEntry(HINSTANCE hInst, BYTE **ppVTEntry)
+{
+    CONTRACTL{
+        NOTHROW;
+    } CONTRACTL_END;
+
+    mdToken tok =(mdToken)(UINT_PTR)*ppVTEntry;
+    _ASSERTE(TypeFromToken(tok) == mdtMethodDef || TypeFromToken(tok) == mdtMemberRef);
+    return tok;
+}
+
+//=================================================================================
+void SetTargetForVTableEntry(HINSTANCE hInst, BYTE **ppVTEntry, BYTE *pTarget)
+{
+    CONTRACTL{
+        THROWS;
+    } CONTRACTL_END;
+
+    DWORD oldProtect;
+    if (!ClrVirtualProtect(ppVTEntry, sizeof(BYTE*), PAGE_READWRITE, &oldProtect))
+    {
+        
+        // This is very bad.  We are not going to be able to update header.
+        _ASSERTE(!"SetTargetForVTableEntry(): VirtualProtect() changing IJW thunk vtable to R/W failed.\n");
+        ThrowLastError();
+    }
+
+    *ppVTEntry = pTarget;
+
+    DWORD ignore;
+    if (!ClrVirtualProtect(ppVTEntry, sizeof(BYTE*), oldProtect, &ignore))
+    {
+        // This is not so bad, we're already done the update, we just didn't return the thunk table to read only
+        _ASSERTE(!"SetTargetForVTableEntry(): VirtualProtect() changing IJW thunk vtable back to RO failed.\n");
+    }
+}
+
+//=================================================================================
+BYTE * GetTargetForVTableEntry(HINSTANCE hInst, BYTE **ppVTEntry)
+{
+    CONTRACTL{
+        NOTHROW;
+    } CONTRACTL_END;
+
+    return *ppVTEntry;
+}
+
+//======================================================================================
+// Fixup vtables stored in the header to contain pointers to method desc
+// prestubs rather than metadata method tokens.
+void Module::FixupVTables()
+{
+    CONTRACTL{
+        INSTANCE_CHECK;
+        STANDARD_VM_CHECK;
+    } CONTRACTL_END;
+
+
+    // If we've already fixed up, or this is not an IJW module, just return.
+    // NOTE: This relies on ILOnly files not having fixups. If this changes,
+    //       we need to change this conditional.
+    if (IsIJWFixedUp() || m_file->IsILOnly()) {
+        return;
+    }
+
+    HINSTANCE hInstThis = GetFile()->GetIJWBase();
+
+    // <REVISIT_TODO>@todo: workaround!</REVISIT_TODO>
+    // If we are compiling in-process, we don't want to fixup the vtables - as it
+    // will have side effects on the other copy of the module!
+    if (SystemDomain::GetCurrentDomain()->IsPassiveDomain()) {
+        return;
+    }
+
+#ifdef FEATURE_PREJIT
+    // We delayed filling in this value until the LoadLibrary occurred
+    if (HasTls() && HasNativeImage()) {
+        CORCOMPILE_EE_INFO_TABLE *pEEInfo = GetNativeImage()->GetNativeEEInfoTable();
+        pEEInfo->rvaStaticTlsIndex = GetTlsIndex();
+    }
+#endif
+    // Get vtable fixup data
+    COUNT_T cFixupRecords;
+    IMAGE_COR_VTABLEFIXUP *pFixupTable = m_file->GetVTableFixups(&cFixupRecords);
+
+    // No records then return
+    if (cFixupRecords == 0) {
+        return;
+    }
+
+    // Now, we need to take a lock to serialize fixup.
+    PEImage::IJWFixupData *pData = PEImage::GetIJWData(m_file->GetIJWBase());
+
+    // If it's already been fixed (in some other appdomain), record the fact and return
+    if (pData->IsFixedUp()) {
+        SetIsIJWFixedUp();
+        return;
+    }
+
+    //////////////////////////////////////////////////////
+    //
+    // This is done in three stages:
+    //  1. We enumerate the types we'll need to load
+    //  2. We load the types
+    //  3. We create and install the thunks
+    //
+
+    COUNT_T cVtableThunks = 0;
+    struct MethodLoadData
+    {
+        mdToken     token;
+        MethodDesc *pMD;
+    };
+    MethodLoadData *rgMethodsToLoad = NULL;
+    COUNT_T cMethodsToLoad = 0;
+
+    //
+    // Stage 1
+    //
+
+    // Each fixup entry describes a vtable, so iterate the vtables and sum their counts
+    {
+        DWORD iFixup;
+        for (iFixup = 0; iFixup < cFixupRecords; iFixup++)
+            cVtableThunks += pFixupTable[iFixup].Count;
+    }
+
+    Thread *pThread = GetThread();
+    StackingAllocator *pAlloc = &pThread->m_MarshalAlloc;
+    CheckPointHolder cph(pAlloc->GetCheckpoint());
+
+    // Allocate the working array of tokens.
+    cMethodsToLoad = cVtableThunks;
+
+    rgMethodsToLoad = new (pAlloc) MethodLoadData[cMethodsToLoad];
+    memset(rgMethodsToLoad, 0, cMethodsToLoad * sizeof(MethodLoadData));
+
+    // Now take the IJW module lock and get all the tokens
+    {
+        // Take the lock
+        CrstHolder lockHolder(pData->GetLock());
+
+        // If someone has beaten us, just return
+        if (pData->IsFixedUp())
+        {
+            SetIsIJWFixedUp();
+            return;
+        }
+
+        COUNT_T iCurMethod = 0;
+
+        if (cFixupRecords != 0)
+        {
+            for (COUNT_T iFixup = 0; iFixup < cFixupRecords; iFixup++)
+            {
+                // Vtables can be 32 or 64 bit.
+                if ((pFixupTable[iFixup].Type == (COR_VTABLE_PTRSIZED)) ||
+                    (pFixupTable[iFixup].Type == (COR_VTABLE_PTRSIZED | COR_VTABLE_FROM_UNMANAGED)) ||
+                    (pFixupTable[iFixup].Type == (COR_VTABLE_PTRSIZED | COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN)))
+                {
+                    const BYTE** pPointers = (const BYTE **)m_file->GetVTable(pFixupTable[iFixup].RVA);
+                    for (int iMethod = 0; iMethod < pFixupTable[iFixup].Count; iMethod++)
+                    {
+                        if (pData->IsMethodFixedUp(iFixup, iMethod))
+                            continue;
+                        mdToken mdTok = GetTokenForVTableEntry(hInstThis, (BYTE **)(pPointers + iMethod));
+                        CONSISTENCY_CHECK(mdTok != mdTokenNil);
+                        rgMethodsToLoad[iCurMethod++].token = mdTok;
+                    }
+                }
+            }
+        }
+
+    }
+
+    //
+    // Stage 2 - Load the types
+    //
+
+    {
+        for (COUNT_T iCurMethod = 0; iCurMethod < cMethodsToLoad; iCurMethod++)
+        {
+            mdToken curTok = rgMethodsToLoad[iCurMethod].token;
+            if (!GetMDImport()->IsValidToken(curTok))
+            {
+                _ASSERTE(!"Invalid token in v-table fix-up table");
+                ThrowHR(COR_E_BADIMAGEFORMAT);
+            }
+
+
+            // Find the method desc
+            MethodDesc *pMD;
+
+            {
+                CONTRACT_VIOLATION(LoadsTypeViolation);
+                pMD = FindMethodThrowing(curTok);
+            }
+
+            CONSISTENCY_CHECK(CheckPointer(pMD));
 
+            rgMethodsToLoad[iCurMethod].pMD = pMD;
+        }
+    }
+
+    //
+    // Stage 3 - Create the thunk data
+    //
+    {
+        // Take the lock
+        CrstHolder lockHolder(pData->GetLock());
+
+        // If someone has beaten us, just return
+        if (pData->IsFixedUp())
+        {
+            SetIsIJWFixedUp();
+            return;
+        }
+
+        // This phase assumes there is only one AppDomain and that thunks
+        // can all safely point directly to the method in the current AppDomain
+
+        AppDomain *pAppDomain = GetAppDomain();
+
+        // Used to index into rgMethodsToLoad
+        COUNT_T iCurMethod = 0;
+
+
+        // Each fixup entry describes a vtable (each slot contains a metadata token
+        // at this stage).
+        DWORD iFixup;
+        for (iFixup = 0; iFixup < cFixupRecords; iFixup++)
+            cVtableThunks += pFixupTable[iFixup].Count;
+
+        DWORD dwIndex = 0;
+        DWORD dwThunkIndex = 0;
+
+        // Now to fill in the thunk table.
+        for (iFixup = 0; iFixup < cFixupRecords; iFixup++)
+        {
+            // Tables may contain zero fixups, in which case the RVA is null, which triggers an assert
+            if (pFixupTable[iFixup].Count == 0)
+                continue;
+
+            const BYTE** pPointers = (const BYTE **)
+                m_file->GetVTable(pFixupTable[iFixup].RVA);
+
+            // Vtables can be 32 or 64 bit.
+            if (pFixupTable[iFixup].Type == COR_VTABLE_PTRSIZED)
+            {
+                for (int iMethod = 0; iMethod < pFixupTable[iFixup].Count; iMethod++)
+                {
+                    if (pData->IsMethodFixedUp(iFixup, iMethod))
+                        continue;
+
+                    mdToken mdTok = rgMethodsToLoad[iCurMethod].token;
+                    MethodDesc *pMD = rgMethodsToLoad[iCurMethod].pMD;
+                    iCurMethod++;
+
+#ifdef _DEBUG 
+                    if (pMD->IsNDirect())
+                    {
+                        LOG((LF_INTEROP, LL_INFO10, "[0x%lx] <-- PINV thunk for \"%s\" (target = 0x%lx)\n",
+                            (size_t)&(pPointers[iMethod]), pMD->m_pszDebugMethodName,
+                            (size_t)(((NDirectMethodDesc*)pMD)->GetNDirectTarget())));
+                    }
+#endif // _DEBUG
+
+                    CONSISTENCY_CHECK(dwThunkIndex < cVtableThunks);
+
+                    // Point the local vtable slot to the thunk we created
+                    SetTargetForVTableEntry(hInstThis, (BYTE **)&pPointers[iMethod], (BYTE *)pMD->GetMultiCallableAddrOfCode());
+
+                    pData->MarkMethodFixedUp(iFixup, iMethod);
+
+                    dwThunkIndex++;
+                }
+
+            }
+            else if (pFixupTable[iFixup].Type == (COR_VTABLE_PTRSIZED | COR_VTABLE_FROM_UNMANAGED) || 
+                    (pFixupTable[iFixup].Type == (COR_VTABLE_PTRSIZED | COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN)))
+            {
+                for (int iMethod = 0; iMethod < pFixupTable[iFixup].Count; iMethod++)
+                {
+                    if (pData->IsMethodFixedUp(iFixup, iMethod))
+                        continue;
+
+                    mdToken mdTok = rgMethodsToLoad[iCurMethod].token;
+                    MethodDesc *pMD = rgMethodsToLoad[iCurMethod].pMD;
+                    iCurMethod++;
+                    LOG((LF_INTEROP, LL_INFO10, "[0x%p] <-- VTable  thunk for \"%s\" (pMD = 0x%p)\n",
+                        (UINT_PTR)&(pPointers[iMethod]), pMD->m_pszDebugMethodName, pMD));
+
+                    UMEntryThunk *pUMEntryThunk = (UMEntryThunk*)(void*)(GetDllThunkHeap()->AllocAlignedMem(sizeof(UMEntryThunk), CODE_SIZE_ALIGN)); // UMEntryThunk contains code
+                    FillMemory(pUMEntryThunk, sizeof(*pUMEntryThunk), 0);
+
+                    UMThunkMarshInfo *pUMThunkMarshInfo = (UMThunkMarshInfo*)(void*)(GetThunkHeap()->AllocAlignedMem(sizeof(UMThunkMarshInfo), CODE_SIZE_ALIGN));
+                    FillMemory(pUMThunkMarshInfo, sizeof(*pUMThunkMarshInfo), 0);
+
+                    pUMThunkMarshInfo->LoadTimeInit(pMD);
+                    pUMEntryThunk->LoadTimeInit(NULL, NULL, pUMThunkMarshInfo, pMD, pAppDomain->GetId());
+                    SetTargetForVTableEntry(hInstThis, (BYTE **)&pPointers[iMethod], (BYTE *)pUMEntryThunk->GetCode());
+
+                    pData->MarkMethodFixedUp(iFixup, iMethod);
+                }
+            }
+            else if ((pFixupTable[iFixup].Type & COR_VTABLE_NOT_PTRSIZED) == COR_VTABLE_NOT_PTRSIZED)
+            {
+                // fixup type doesn't match the platform
+                THROW_BAD_FORMAT(BFA_FIXUP_WRONG_PLATFORM, this);
+            }
+            else
+            {
+                _ASSERTE(!"Unknown vtable fixup type");
+            }
+        }
+
+        // Indicate that this module has been fixed before releasing the lock
+        pData->SetIsFixedUp();  // On the data
+        SetIsIJWFixedUp();      // On the module
+    } // End of Stage 3
+}
+
+// Self-initializing accessor for m_pThunkHeap
+LoaderHeap *Module::GetDllThunkHeap()
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+    return PEImage::GetDllThunkHeap(GetFile()->GetIJWBase());
+
+}
+
+LoaderHeap *Module::GetThunkHeap()
+{
+    CONTRACT(LoaderHeap *)
+    {
+        INSTANCE_CHECK;
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+        INJECT_FAULT(COMPlusThrowOM());
+        POSTCONDITION(CheckPointer(RETVAL));
+    }
+    CONTRACT_END
+
+        if (!m_pThunkHeap)
+        {
+            size_t * pPrivatePCLBytes = NULL;
+            size_t * pGlobalPCLBytes = NULL;
+
+            COUNTER_ONLY(pPrivatePCLBytes = &(GetPerfCounters().m_Loading.cbLoaderHeapSize));
+
+            LoaderHeap *pNewHeap = new LoaderHeap(VIRTUAL_ALLOC_RESERVE_GRANULARITY, // DWORD dwReserveBlockSize
+                0,                                 // DWORD dwCommitBlockSize
+                pPrivatePCLBytes,
+                ThunkHeapStubManager::g_pManager->GetRangeList(),
+                TRUE);                             // BOOL fMakeExecutable
+
+            if (FastInterlockCompareExchangePointer(&m_pThunkHeap, pNewHeap, 0) != 0)
+            {
+                delete pNewHeap;
+            }
+        }
+
+    RETURN m_pThunkHeap;
+}
+#endif // !CROSSGEN_COMPILE
 
 #ifdef FEATURE_NATIVE_IMAGE_GENERATION
 
@@ -12492,7 +12863,11 @@ void Module::DeleteProfilingData()
 }
 #endif //FEATURE_PREJIT
 
-
+void Module::SetIsIJWFixedUp()
+{
+    LIMITED_METHOD_CONTRACT;
+    FastInterlockOr(&m_dwTransientFlags, IS_IJW_FIXED_UP);
+}
 
 #ifdef FEATURE_PREJIT
 /* static */
index 73f1a8f..d8a278e 100644 (file)
@@ -1740,6 +1740,7 @@ protected:
 
     void ApplyMetaData();
 
+    void FixupVTables();
 
     void FreeClassTables();
 
@@ -3156,6 +3157,14 @@ public:
     }
 #endif // FEATURE_PREJIT
 
+    // LoaderHeap for storing IJW thunks
+    PTR_LoaderHeap           m_pThunkHeap;
+
+    // Self-initializing accessor for IJW thunk heap
+    LoaderHeap              *GetThunkHeap();
+    // Self-initializing accessor for domain-independent IJW thunk heap
+    LoaderHeap              *GetDllThunkHeap();
+
     void            EnumRegularStaticGCRefs        (AppDomain* pAppDomain, promote_func* fn, ScanContext* sc);
 
 protected:    
index 6b30ee7..12d3f3c 100644 (file)
@@ -1075,6 +1075,10 @@ void DomainFile::VtableFixups()
 {
     WRAPPER_NO_CONTRACT;
 
+#if !defined(CROSSGEN_COMPILE)
+    if (!GetCurrentModule()->IsResource())
+        GetCurrentModule()->FixupVTables();
+#endif // !CROSSGEN_COMPILE
 }
 
 void DomainFile::FinishLoad()
index 821dc3a..6b066ff 100644 (file)
@@ -28,6 +28,8 @@
 
 CrstStatic  PEImage::s_hashLock;
 PtrHashMap *PEImage::s_Images = NULL;
+CrstStatic  PEImage::s_ijwHashLock;
+PtrHashMap *PEImage::s_ijwFixupDataHash;
 
 extern LocaleID g_lcid; // fusion path comparison lcid
 
@@ -54,6 +56,12 @@ void PEImage::Startup()
     LockOwner lock = { &s_hashLock, IsOwnerOfCrst };
     s_Images         = ::new PtrHashMap;
     s_Images->Init(CompareImage, FALSE, &lock);
+
+    s_ijwHashLock.Init(CrstIJWHash, CRST_REENTRANCY);
+    LockOwner ijwLock = { &s_ijwHashLock, IsOwnerOfCrst };
+    s_ijwFixupDataHash = ::new PtrHashMap;
+    s_ijwFixupDataHash->Init(CompareIJWDataBase, FALSE, &ijwLock);
+
     PEImageLayout::Startup();
 #ifdef FEATURE_USE_LCID
     g_lcid = MAKELCID(LOCALE_INVARIANT, SORT_DEFAULT);
@@ -196,6 +204,20 @@ PEImage::~PEImage()
 
 }
 
+/* static */
+BOOL PEImage::CompareIJWDataBase(UPTR base, UPTR mapping)
+{
+    CONTRACTL{
+        PRECONDITION(CheckStartup());
+        PRECONDITION(CheckPointer((BYTE *)(base << 1)));
+        PRECONDITION(CheckPointer((IJWFixupData *)mapping));
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    } CONTRACTL_END;
+
+    return ((BYTE *)(base << 1) == ((IJWFixupData*)mapping)->GetBase());
+}
 
     // Thread stress
 #if 0
@@ -686,7 +708,145 @@ void DECLSPEC_NORETURN PEImage::ThrowFormat(HRESULT hrError)
     EEFileLoadException::Throw(m_path, hrError);
 }
 
+#if !defined(CROSSGEN_COMPILE)
+
+//may outlive PEImage
+PEImage::IJWFixupData::IJWFixupData(void *pBase)
+    : m_lock(CrstIJWFixupData),
+    m_base(pBase), m_flags(0), m_DllThunkHeap(NULL), m_iNextFixup(0), m_iNextMethod(0)
+{
+    WRAPPER_NO_CONTRACT;
+}
+
+PEImage::IJWFixupData::~IJWFixupData()
+{
+    WRAPPER_NO_CONTRACT;
+    if (m_DllThunkHeap)
+        delete m_DllThunkHeap;
+}
+
+
+// Self-initializing accessor for m_DllThunkHeap
+LoaderHeap *PEImage::IJWFixupData::GetThunkHeap()
+{
+    CONTRACT(LoaderHeap *)
+    {
+        INSTANCE_CHECK;
+        THROWS;
+        GC_NOTRIGGER;
+        MODE_ANY;
+        INJECT_FAULT(COMPlusThrowOM());
+        POSTCONDITION(CheckPointer(RETVAL));
+    }
+    CONTRACT_END
+
+        if (!m_DllThunkHeap)
+        {
+            size_t * pPrivatePCLBytes = NULL;
+            size_t * pGlobalPCLBytes = NULL;
+
+            COUNTER_ONLY(pPrivatePCLBytes = &(GetPerfCounters().m_Loading.cbLoaderHeapSize));
+
+            LoaderHeap *pNewHeap = new LoaderHeap(VIRTUAL_ALLOC_RESERVE_GRANULARITY, // DWORD dwReserveBlockSize
+                0,                                 // DWORD dwCommitBlockSize
+                pPrivatePCLBytes,
+                ThunkHeapStubManager::g_pManager->GetRangeList(),
+                TRUE);                             // BOOL fMakeExecutable
+
+            if (FastInterlockCompareExchangePointer((PVOID*)&m_DllThunkHeap, (VOID*)pNewHeap, (VOID*)0) != 0)
+            {
+                delete pNewHeap;
+            }
+        }
+
+    RETURN m_DllThunkHeap;
+}
+
+void PEImage::IJWFixupData::MarkMethodFixedUp(COUNT_T iFixup, COUNT_T iMethod)
+{
+    LIMITED_METHOD_CONTRACT;
+    // supports only sequential fixup/method
+    _ASSERTE((iFixup == m_iNextFixup + 1 && iMethod == 0) ||                 //first method of the next fixup or
+        (iFixup == m_iNextFixup && iMethod == m_iNextMethod));     //the method that was next to fixup
+
+    m_iNextFixup = iFixup;
+    m_iNextMethod = iMethod + 1;
+}
+
+BOOL PEImage::IJWFixupData::IsMethodFixedUp(COUNT_T iFixup, COUNT_T iMethod)
+{
+    LIMITED_METHOD_CONTRACT;
+    if (iFixup < m_iNextFixup)
+        return TRUE;
+    if (iFixup > m_iNextFixup)
+        return FALSE;
+    if (iMethod < m_iNextMethod)
+        return TRUE;
+
+    return FALSE;
+}
+
+/*static */
+PTR_LoaderHeap PEImage::GetDllThunkHeap(void *pBase)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+    return GetIJWData(pBase)->GetThunkHeap();
+}
+
+/* static */
+PEImage::IJWFixupData *PEImage::GetIJWData(void *pBase)
+{
+    CONTRACTL{
+        THROWS;
+        GC_TRIGGERS;
+        MODE_ANY;
+        INJECT_FAULT(COMPlusThrowOM(););
+    } CONTRACTL_END
+
+    // Take the IJW hash lock
+    CrstHolder hashLockHolder(&s_ijwHashLock);
+
+    // Try to find the data
+    IJWFixupData *pData = (IJWFixupData *)s_ijwFixupDataHash->LookupValue((UPTR)pBase, pBase);
+
+    // No data, must create
+    if ((UPTR)pData == (UPTR)INVALIDENTRY)
+    {
+        pData = new IJWFixupData(pBase);
+        s_ijwFixupDataHash->InsertValue((UPTR)pBase, pData);
+    }
+
+    // Return the new data
+    return (pData);
+}
+
+/* static */
+void PEImage::UnloadIJWModule(void *pBase)
+{
+    CONTRACTL{
+        NOTHROW;
+        GC_TRIGGERS;
+        MODE_ANY;
+    } CONTRACTL_END
+
+    // Take the IJW hash lock
+    CrstHolder hashLockHolder(&s_ijwHashLock);
+
+    // Try to delete the hash entry
+    IJWFixupData *pData = (IJWFixupData *)s_ijwFixupDataHash->DeleteValue((UPTR)pBase, pBase);
+
+    // Now delete the data
+    if ((UPTR)pData != (UPTR)INVALIDENTRY)
+        delete pData;
+}
 
+#endif // !CROSSGEN_COMPILE
 
 
 
index 22aed04..f4e2924 100644 (file)
@@ -271,6 +271,7 @@ private:
     };
 
     static BOOL CompareImage(UPTR image1, UPTR image2);
+    static BOOL CompareIJWDataBase(UPTR base, UPTR mapping);
 
     void DECLSPEC_NORETURN ThrowFormat(HRESULT hr);
 
@@ -341,6 +342,49 @@ private:
     BOOL        m_bSignatureInfoCached;
     HRESULT   m_hrSignatureInfoStatus;
     DWORD        m_dwSignatureInfo;    
+
+    //@TODO:workaround: Remove this when we have one PEImage per mapped image,
+    //@TODO:workaround: and move the lock there
+    // This is for IJW thunk initialization, as it is no longer guaranteed
+    // that the initialization will occur under the loader lock.
+    static CrstStatic   s_ijwHashLock;
+    static PtrHashMap   *s_ijwFixupDataHash;
+
+public:
+        class IJWFixupData
+        {
+        private:
+            Crst            m_lock;
+            void           *m_base;
+            DWORD           m_flags;
+            PTR_LoaderHeap  m_DllThunkHeap;
+
+            // the fixup for the next iteration in FixupVTables
+            // we use it to make sure that we do not try to fix up the same entry twice
+            // if there was a pass that was aborted in the middle
+            COUNT_T         m_iNextFixup;
+            COUNT_T         m_iNextMethod;
+
+            enum {
+                e_FIXED_UP = 0x1
+            };
+
+        public:
+            IJWFixupData(void *pBase);
+            ~IJWFixupData();
+            void *GetBase() { LIMITED_METHOD_CONTRACT; return m_base; }
+            Crst *GetLock() { LIMITED_METHOD_CONTRACT; return &m_lock; }
+            BOOL IsFixedUp() { LIMITED_METHOD_CONTRACT; return m_flags & e_FIXED_UP; }
+            void SetIsFixedUp() { LIMITED_METHOD_CONTRACT; m_flags |= e_FIXED_UP; }
+            PTR_LoaderHeap  GetThunkHeap();
+            void MarkMethodFixedUp(COUNT_T iFixup, COUNT_T iMethod);
+            BOOL IsMethodFixedUp(COUNT_T iFixup, COUNT_T iMethod);
+        };
+
+        static IJWFixupData *GetIJWData(void *pBase);
+        static PTR_LoaderHeap GetDllThunkHeap(void *pBase);
+        static void UnloadIJWModule(void *pBase);
+
 private:
     DWORD m_dwPEKind;
     DWORD m_dwMachine;
index f2a480e..0107958 100644 (file)
@@ -38,5 +38,6 @@ if(WIN32)
     # IJW isn't supported on ARM64
     if(NOT CLR_CMAKE_PLATFORM_ARCH_ARM64)
         add_subdirectory(IJW/ManagedCallingNative/IjwNativeDll)
+        add_subdirectory(IJW/NativeCallingManaged/IjwNativeCallingManagedDll)
     endif()
 endif(WIN32)
index 415c8d5..49470d3 100644 (file)
@@ -1,3 +1,7 @@
+// 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 <windows.h>
 
 // Entrypoint jumped to by IJW dlls when their dllmain is called
index e0b9127..612e6aa 100644 (file)
@@ -5,17 +5,13 @@ set(SOURCES IjwNativeDll.cpp)
 
 if (WIN32)
   # 4365 - signed/unsigned mismatch
-  add_compile_options(-wd4365)
+  add_compile_options(/wd4365)
 
   # IJW
-  add_compile_options(-clr)
+  add_compile_options(/clr)
   
   # IJW requires the CRT as a dll, not linked in
-  if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL CHECKED)
-    add_compile_options(-MDd)
-  else()
-    add_compile_options(-MD)
-  endif()
+  add_compile_options(/MD$<$<OR:$<CONFIG:Debug>,$<CONFIG:Checked>>:d>)
 
   # CMake enables /RTC1 and /EHsc by default, but they're not compatible with /clr, so remove them
   if(CMAKE_CXX_FLAGS_DEBUG MATCHES "/RTC1")
index 6ac5601..cb25b44 100644 (file)
@@ -1,3 +1,7 @@
+// 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.
+
 #pragma unmanaged
 int NativeFunction()
 {
diff --git a/tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/CMakeLists.txt b/tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c8b0edc
--- /dev/null
@@ -0,0 +1,42 @@
+cmake_minimum_required (VERSION 2.6)
+project (IjwNativeCallingManagedDll)
+include_directories( ${INC_PLATFORM_DIR} )
+set(SOURCES IjwNativeCallingManagedDll.cpp)
+
+if (WIN32)
+  # 4365 - signed/unsigned mismatch
+  add_compile_options(/wd4365)
+
+  # IJW
+  add_compile_options(/clr)
+  
+  # IJW requires the CRT as a dll, not linked in
+  add_compile_options(/MD$<$<OR:$<CONFIG:Debug>,$<CONFIG:Checked>>:d>)
+
+  # CMake enables /RTC1 and /EHsc by default, but they're not compatible with /clr, so remove them
+  if(CMAKE_CXX_FLAGS_DEBUG MATCHES "/RTC1")
+    string(REPLACE "/RTC1" " " CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
+  endif()
+  
+  if(CMAKE_CXX_FLAGS MATCHES "/EHsc")
+    string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+  endif()
+
+  # IJW isn't compatible with CFG
+   if(CMAKE_CXX_FLAGS MATCHES "/guard:cf")
+    string(REPLACE "/guard:cf" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+   endif()
+
+     # IJW isn't compatible with GR-
+   if(CMAKE_CXX_FLAGS MATCHES "/GR-")
+    string(REPLACE "/GR-" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+   endif()
+
+endif()
+
+# add the shared library
+add_library (IjwNativeCallingManagedDll SHARED ${SOURCES})
+target_link_libraries(IjwNativeCallingManagedDll ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS IjwNativeCallingManagedDll DESTINATION bin)
diff --git a/tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp b/tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp
new file mode 100644 (file)
index 0000000..9ba7e3a
--- /dev/null
@@ -0,0 +1,25 @@
+// 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.
+
+#pragma managed
+int ManagedCallee()
+{
+    return 100;
+}
+
+#pragma unmanaged
+int NativeFunction()
+{
+    return ManagedCallee();
+}
+
+#pragma managed
+public ref class TestClass
+{
+public:
+    int ManagedEntryPoint()
+    {
+        return NativeFunction();
+    }
+};
diff --git a/tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.cs b/tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.cs
new file mode 100644 (file)
index 0000000..5f035d1
--- /dev/null
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+namespace NativeCallingManaged
+{
+    class NativeCallingManaged
+    {
+        static int Main(string[] args)
+        {
+            bool success = true;
+            // Load a fake mscoree.dll to avoid starting desktop
+            LoadLibraryEx(Path.Combine(Environment.CurrentDirectory, "mscoree.dll"), IntPtr.Zero, 0);
+
+            TestFramework.BeginScenario("Calling from managed to native IJW code");
+
+            // Building with a reference to the IJW dll is difficult, so load via reflection instead
+            TestFramework.BeginTestCase("Load IJW dll via reflection");
+            Assembly ijwNativeDll = Assembly.Load("IjwNativeCallingManagedDll");
+            TestFramework.EndTestCase();
+
+            TestFramework.BeginTestCase("Call native method returning int");
+            Type testType = ijwNativeDll.GetType("TestClass");
+            object testInstance = Activator.CreateInstance(testType);
+            MethodInfo testMethod = testType.GetMethod("ManagedEntryPoint");
+            int result = (int)testMethod.Invoke(testInstance, null);
+            if(result != 100)
+            {
+                TestFramework.LogError("IJW", "Incorrect result returned: " + result);
+                success = false;
+            }
+            TestFramework.EndTestCase();
+
+            TestFramework.BeginTestCase("Ensure .NET Framework was not loaded");
+            IntPtr clrHandle = GetModuleHandle("mscoreei.dll");
+            if (clrHandle != IntPtr.Zero)
+            {
+                TestFramework.LogError("IJW", ".NET Framework loaded by IJw module load");
+                success = false;
+            }
+            TestFramework.EndTestCase();
+
+            return success ? 100 : 99;
+        }
+
+        [DllImport("kernel32.dll")]
+        static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, int dwFlags);
+
+        [DllImport("kernel32.dll")]
+        static extern IntPtr GetModuleHandle(string lpModuleName);
+    }
+}
diff --git a/tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.csproj b/tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.csproj
new file mode 100644 (file)
index 0000000..3b92603
--- /dev/null
@@ -0,0 +1,39 @@
+<?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>NativeCallingManaged</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{8B76A001-5654-4F11-A80B-EF12644EAD3D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+
+    <!-- IJW is Windows-only -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+
+    <!-- IJW is not supported on ARM64 -->
+    <DisableProjectBuild Condition="'$(Platform)' == 'arm64'">true</DisableProjectBuild>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+  </PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="NativeCallingManaged.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="IjwNativeCallingManagedDll/CMakeLists.txt" />
+    <ProjectReference Include="../FakeMscoree/CMakeLists.txt" />
+    <ProjectReference Include="../../../Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>