summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMorgan Brown <morganbr@users.noreply.github.com>2018-09-06 19:37:30 -0700
committerGitHub <noreply@github.com>2018-09-06 19:37:30 -0700
commit9e976d515099c7708cf26cef7520b628564f5acf (patch)
tree3a29d4011d871007e7dc24713f056aaf2af70e51
parent748b5a0a5a701b7f9803021313c620413e353fdc (diff)
downloadcoreclr-9e976d515099c7708cf26cef7520b628564f5acf.tar.gz
coreclr-9e976d515099c7708cf26cef7520b628564f5acf.tar.bz2
coreclr-9e976d515099c7708cf26cef7520b628564f5acf.zip
Enable IJW Native calling managed (#19750)
* Adds back code required to make IJW native->managed calls (if the runtime is already started) and includes a simple test.
-rw-r--r--src/dlls/mscorrc/mscorrc.rc1
-rw-r--r--src/dlls/mscorrc/resource.h1
-rw-r--r--src/vm/ceeload.cpp381
-rw-r--r--src/vm/ceeload.h9
-rw-r--r--src/vm/domainfile.cpp4
-rw-r--r--src/vm/peimage.cpp160
-rw-r--r--src/vm/peimage.h44
-rw-r--r--tests/src/Interop/CMakeLists.txt1
-rw-r--r--tests/src/Interop/IJW/FakeMscoree/mscoree.cpp4
-rw-r--r--tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/CMakeLists.txt10
-rw-r--r--tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/IjwNativeDll.cpp4
-rw-r--r--tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/CMakeLists.txt42
-rw-r--r--tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp25
-rw-r--r--tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.cs58
-rw-r--r--tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.csproj39
15 files changed, 773 insertions, 10 deletions
diff --git a/src/dlls/mscorrc/mscorrc.rc b/src/dlls/mscorrc/mscorrc.rc
index a298563cc6..713d7e0a66 100644
--- a/src/dlls/mscorrc/mscorrc.rc
+++ b/src/dlls/mscorrc/mscorrc.rc
@@ -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."
diff --git a/src/dlls/mscorrc/resource.h b/src/dlls/mscorrc/resource.h
index d58780f8f9..0a42dad28e 100644
--- a/src/dlls/mscorrc/resource.h
+++ b/src/dlls/mscorrc/resource.h
@@ -540,6 +540,7 @@
#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
diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp
index b3d2d59cb3..19c5167005 100644
--- a/src/vm/ceeload.cpp
+++ b/src/vm/ceeload.cpp
@@ -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 */
diff --git a/src/vm/ceeload.h b/src/vm/ceeload.h
index 73f1a8f145..d8a278e544 100644
--- a/src/vm/ceeload.h
+++ b/src/vm/ceeload.h
@@ -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:
diff --git a/src/vm/domainfile.cpp b/src/vm/domainfile.cpp
index 6b30ee7c3a..12d3f3c579 100644
--- a/src/vm/domainfile.cpp
+++ b/src/vm/domainfile.cpp
@@ -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()
diff --git a/src/vm/peimage.cpp b/src/vm/peimage.cpp
index 821dc3a8fc..6b066ff2f5 100644
--- a/src/vm/peimage.cpp
+++ b/src/vm/peimage.cpp
@@ -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
diff --git a/src/vm/peimage.h b/src/vm/peimage.h
index 22aed04b0e..f4e2924723 100644
--- a/src/vm/peimage.h
+++ b/src/vm/peimage.h
@@ -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;
diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt
index f2a480eda6..0107958aa5 100644
--- a/tests/src/Interop/CMakeLists.txt
+++ b/tests/src/Interop/CMakeLists.txt
@@ -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)
diff --git a/tests/src/Interop/IJW/FakeMscoree/mscoree.cpp b/tests/src/Interop/IJW/FakeMscoree/mscoree.cpp
index 415c8d5be0..49470d3865 100644
--- a/tests/src/Interop/IJW/FakeMscoree/mscoree.cpp
+++ b/tests/src/Interop/IJW/FakeMscoree/mscoree.cpp
@@ -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
diff --git a/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/CMakeLists.txt b/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/CMakeLists.txt
index e0b91278f5..612e6aa17f 100644
--- a/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/CMakeLists.txt
+++ b/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/CMakeLists.txt
@@ -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")
diff --git a/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/IjwNativeDll.cpp b/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/IjwNativeDll.cpp
index 6ac5601921..cb25b445a4 100644
--- a/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/IjwNativeDll.cpp
+++ b/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/IjwNativeDll.cpp
@@ -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
index 0000000000..c8b0edcb83
--- /dev/null
+++ b/tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/CMakeLists.txt
@@ -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
index 0000000000..9ba7e3ac0c
--- /dev/null
+++ b/tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp
@@ -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
index 0000000000..5f035d1cd7
--- /dev/null
+++ b/tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.cs
@@ -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
index 0000000000..3b92603041
--- /dev/null
+++ b/tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.csproj
@@ -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>