Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/coreclr/nativeaot/Runtime/PalRedhawk.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
// we have to (in which case these definitions will move to CommonTypes.h).
typedef int32_t HRESULT;

#define S_OK 0x0
#define E_FAIL 0x80004005
#define E_OUTOFMEMORY 0x8007000E

typedef WCHAR * LPWSTR;
typedef const WCHAR * LPCWSTR;
typedef char * LPSTR;
Expand Down
37 changes: 22 additions & 15 deletions src/coreclr/nativeaot/Runtime/ThunksMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,15 @@ FCIMPL0(int, RhpGetThunkBlockSize)
}
FCIMPLEND

EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
EXTERN_C HRESULT QCALLTYPE RhAllocateThunksMapping(void** ppThunksSection)
{
#ifdef WIN32

void * pNewMapping = PalVirtualAlloc(THUNKS_MAP_SIZE * 2, PAGE_READWRITE);
if (pNewMapping == NULL)
return NULL;
{
return E_OUTOFMEMORY;
}

void * pThunksSection = pNewMapping;
void * pDataSection = (uint8_t*)pNewMapping + THUNKS_MAP_SIZE;
Expand All @@ -120,7 +122,9 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
// changed anymore.
void * pNewMapping = PalVirtualAlloc(THUNKS_MAP_SIZE * 2, PAGE_EXECUTE_READ);
if (pNewMapping == NULL)
return NULL;
{
return E_OUTOFMEMORY;
}

void * pThunksSection = pNewMapping;
void * pDataSection = (uint8_t*)pNewMapping + THUNKS_MAP_SIZE;
Expand All @@ -129,7 +133,7 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
!PalVirtualProtect(pThunksSection, THUNKS_MAP_SIZE, PAGE_EXECUTE_READWRITE))
{
PalVirtualFree(pNewMapping, THUNKS_MAP_SIZE * 2);
return NULL;
return E_FAIL;
}

#if defined(HOST_APPLE) && defined(HOST_ARM64)
Expand Down Expand Up @@ -306,13 +310,14 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
if (!PalVirtualProtect(pThunksSection, THUNKS_MAP_SIZE, PAGE_EXECUTE_READ))
{
PalVirtualFree(pNewMapping, THUNKS_MAP_SIZE * 2);
return NULL;
return E_FAIL;
}
#endif

PalFlushInstructionCache(pThunksSection, THUNKS_MAP_SIZE);

return pThunksSection;
*ppThunksSection = pThunksSection;
return S_OK;
}

// FEATURE_RX_THUNKS
Expand All @@ -327,7 +332,7 @@ FCDECL0(int, RhpGetThunkBlockSize);
FCDECL1(void*, RhpGetThunkDataBlockAddress, void* addr);
FCDECL1(void*, RhpGetThunkStubsBlockAddress, void* addr);

EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
EXTERN_C HRESULT QCALLTYPE RhAllocateThunksMapping(void** ppThunksSection)
{
static int nextThunkDataMapping = 0;

Expand All @@ -342,7 +347,7 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()

if (nextThunkDataMapping == thunkDataMappingCount)
{
return NULL;
return E_FAIL;
}

if (g_pThunkStubData == NULL)
Expand All @@ -353,23 +358,24 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()

if (g_pThunkStubData == NULL)
{
return NULL;
return E_OUTOFMEMORY;
}
}

void* pThunkDataBlock = (int8_t*)g_pThunkStubData + nextThunkDataMapping * thunkDataMappingSize;

if (VirtualAlloc(pThunkDataBlock, thunkDataMappingSize, MEM_COMMIT, PAGE_READWRITE) == NULL)
{
return NULL;
return E_OUTOFMEMORY;
}

nextThunkDataMapping++;

void* pThunks = RhpGetThunkStubsBlockAddress(pThunkDataBlock);
ASSERT(RhpGetThunkDataBlockAddress(pThunks) == pThunkDataBlock);

return pThunks;
*ppThunksSection = pThunks;
return S_OK;
}

#else // FEATURE_FIXED_POOL_THUNKS
Expand All @@ -380,7 +386,7 @@ FCDECL0(int, RhpGetNumThunksPerBlock);
FCDECL0(int, RhpGetThunkSize);
FCDECL0(int, RhpGetThunkBlockSize);

EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
EXTERN_C HRESULT QCALLTYPE RhAllocateThunksMapping(void** ppThunksSection)
{
static void* pThunksTemplateAddress = NULL;

Expand Down Expand Up @@ -409,7 +415,7 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
int templateRva = (int)((uint8_t*)RhpGetThunksBase() - pModuleBase);

if (!PalAllocateThunksFromTemplate((HANDLE)pModuleBase, templateRva, templateSize, &pThunkMap))
return NULL;
return E_OUTOFMEMORY;
}

if (!PalMarkThunksAsValidCallTargets(
Expand All @@ -422,10 +428,11 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
if (pThunkMap != pThunksTemplateAddress)
PalFreeThunksFromTemplate(pThunkMap, templateSize);

return NULL;
return E_FAIL;
}

return pThunkMap;
*ppThunksSection = pThunkMap;
return S_OK;
}

#endif // FEATURE_RX_THUNKS
4 changes: 2 additions & 2 deletions src/coreclr/nativeaot/Runtime/portable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,9 +365,9 @@ FCIMPL2(Object *, RhpCheckedXchg, Object ** location, Object * value)
}
FCIMPLEND

FCIMPL0(void*, RhAllocateThunksMapping)
FCIMPL1(HRESULT, RhAllocateThunksMapping, void ** ppThunksSection)
{
return NULL;
return E_FAIL;
}
FCIMPLEND

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,18 +711,12 @@ public static string TryGetMethodDisplayStringFromIp(IntPtr ip)

public static object CreateThunksHeap(IntPtr commonStubAddress)
{
object? newHeap = ThunksHeap.CreateThunksHeap(commonStubAddress);
if (newHeap == null)
throw new OutOfMemoryException();
return newHeap;
return ThunksHeap.CreateThunksHeap(commonStubAddress);
}

public static IntPtr AllocateThunk(object thunksHeap)
{
IntPtr newThunk = ((ThunksHeap)thunksHeap).AllocateThunk();
if (newThunk == IntPtr.Zero)
throw new OutOfMemoryException();
return newThunk;
return ((ThunksHeap)thunksHeap).AllocateThunk();
}

public static void FreeThunk(object thunksHeap, IntPtr thunkAddress)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ internal static IntPtr RhHandleAllocDependent(object primary, object secondary)
internal static extern int RhpGetThunkBlockSize();

[LibraryImport(RuntimeLibrary, EntryPoint = "RhAllocateThunksMapping")]
internal static partial IntPtr RhAllocateThunksMapping();
internal static unsafe partial int RhAllocateThunksMapping(IntPtr* ppMapping);

//
// calls to runtime for type equality checks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,43 +87,26 @@ private unsafe ThunksHeap(IntPtr commonStubAddress)

_allocatedBlocks = new AllocatedBlock();

IntPtr thunkStubsBlock;
lock (this)
{
Comment on lines -91 to -92
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was scratching my head about this - we're in a constructor. Nobody else can have this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's obviously wrong, but also GetNewThunksBlock doesn't seem to be thread-safe, so perhaps the intention was to protect multiple simultaneous calls to that?

Copy link
Member

@filipnavara filipnavara May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are not addressing this, should we open a follow-up issue? (or did I miss some mechanism that is making the static variable access thread safe?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of doing more within a PR than what's needed and this is orthogonal. Filed #116089.

thunkStubsBlock = ThunkBlocks.GetNewThunksBlock();
}

if (thunkStubsBlock != IntPtr.Zero)
{
IntPtr thunkDataBlock = RuntimeImports.RhpGetThunkDataBlockAddress(thunkStubsBlock);
IntPtr thunkStubsBlock = ThunkBlocks.GetNewThunksBlock();
IntPtr thunkDataBlock = RuntimeImports.RhpGetThunkDataBlockAddress(thunkStubsBlock);

// Address of the first thunk data cell should be at the beginning of the thunks data block (page-aligned)
Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.PageSize) == 0);
// Address of the first thunk data cell should be at the beginning of the thunks data block (page-aligned)
Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.PageSize) == 0);

// Update the last pointer value in the thunks data section with the value of the common stub address
*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) = commonStubAddress;
Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) == commonStubAddress);
// Update the last pointer value in the thunks data section with the value of the common stub address
*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) = commonStubAddress;
Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) == commonStubAddress);

// Set the head and end of the linked list
_nextAvailableThunkPtr = thunkDataBlock;
_lastThunkPtr = _nextAvailableThunkPtr + Constants.ThunkDataSize * (Constants.NumThunksPerBlock - 1);
// Set the head and end of the linked list
_nextAvailableThunkPtr = thunkDataBlock;
_lastThunkPtr = _nextAvailableThunkPtr + Constants.ThunkDataSize * (Constants.NumThunksPerBlock - 1);

_allocatedBlocks._blockBaseAddress = thunkStubsBlock;
}
_allocatedBlocks._blockBaseAddress = thunkStubsBlock;
}

public static unsafe ThunksHeap? CreateThunksHeap(IntPtr commonStubAddress)
public static unsafe ThunksHeap CreateThunksHeap(IntPtr commonStubAddress)
{
try
{
ThunksHeap newHeap = new ThunksHeap(commonStubAddress);

if (newHeap._nextAvailableThunkPtr != IntPtr.Zero)
return newHeap;
}
catch (Exception) { }

return null;
return new ThunksHeap(commonStubAddress);
}

// TODO: Feature
Expand All @@ -134,47 +117,30 @@ private unsafe ThunksHeap(IntPtr commonStubAddress)
//
// Note: Expected to be called under lock
//
private unsafe bool ExpandHeap()
private unsafe void ExpandHeap()
{
AllocatedBlock newBlockInfo;

try
{
newBlockInfo = new AllocatedBlock();
}
catch (Exception)
{
return false;
}
AllocatedBlock newBlockInfo = new AllocatedBlock();

IntPtr thunkStubsBlock = ThunkBlocks.GetNewThunksBlock();
IntPtr thunkDataBlock = RuntimeImports.RhpGetThunkDataBlockAddress(thunkStubsBlock);

if (thunkStubsBlock != IntPtr.Zero)
{
IntPtr thunkDataBlock = RuntimeImports.RhpGetThunkDataBlockAddress(thunkStubsBlock);

// Address of the first thunk data cell should be at the beginning of the thunks data block (page-aligned)
Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.PageSize) == 0);

// Update the last pointer value in the thunks data section with the value of the common stub address
*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) = _commonStubAddress;
Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) == _commonStubAddress);

// Link the last entry in the old list to the first entry in the new list
*((IntPtr*)_lastThunkPtr) = thunkDataBlock;
// Address of the first thunk data cell should be at the beginning of the thunks data block (page-aligned)
Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.PageSize) == 0);

// Update the pointer to the last entry in the list
_lastThunkPtr = *((IntPtr*)_lastThunkPtr) + Constants.ThunkDataSize * (Constants.NumThunksPerBlock - 1);
// Update the last pointer value in the thunks data section with the value of the common stub address
*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) = _commonStubAddress;
Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) == _commonStubAddress);

newBlockInfo._blockBaseAddress = thunkStubsBlock;
newBlockInfo._nextBlock = _allocatedBlocks;
// Link the last entry in the old list to the first entry in the new list
*((IntPtr*)_lastThunkPtr) = thunkDataBlock;

_allocatedBlocks = newBlockInfo;
// Update the pointer to the last entry in the list
_lastThunkPtr = *((IntPtr*)_lastThunkPtr) + Constants.ThunkDataSize * (Constants.NumThunksPerBlock - 1);

return true;
}
newBlockInfo._blockBaseAddress = thunkStubsBlock;
newBlockInfo._nextBlock = _allocatedBlocks;

return false;
_allocatedBlocks = newBlockInfo;
}

public unsafe IntPtr AllocateThunk()
Expand All @@ -192,10 +158,7 @@ public unsafe IntPtr AllocateThunk()

if (nextNextAvailableThunkPtr == IntPtr.Zero)
{
if (!ExpandHeap())
{
return IntPtr.Zero;
}
ExpandHeap();

nextAvailableThunkPtr = _nextAvailableThunkPtr;
nextNextAvailableThunkPtr = *((IntPtr*)(nextAvailableThunkPtr));
Expand Down Expand Up @@ -347,19 +310,12 @@ public static unsafe IntPtr GetNewThunksBlock()
}
else
{
nextThunksBlock = RuntimeImports.RhAllocateThunksMapping();

if (nextThunksBlock == IntPtr.Zero)
{
// We either ran out of memory and can't do anymore mappings of the thunks templates sections,
// or we are using the managed runtime services fallback, which doesn't provide the
// file mapping feature (ex: older version of mrt100.dll, or no mrt100.dll at all).

// The only option is for the caller to attempt and recycle unused thunks to be able to
// find some free entries.

return IntPtr.Zero;
}
nextThunksBlock = IntPtr.Zero;
int result = RuntimeImports.RhAllocateThunksMapping(&nextThunksBlock);
if (result == HResults.E_OUTOFMEMORY)
throw new OutOfMemoryException();
else if (result != HResults.S_OK)
throw new PlatformNotSupportedException(SR.PlatformNotSupported_DynamicEntrypoint);

// Each mapping consists of multiple blocks of thunk stubs/data pairs. Keep track of those
// so that we do not create a new mapping until all blocks in the sections we just mapped are consumed
Expand Down
1 change: 1 addition & 0 deletions src/libraries/Common/src/System/HResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ internal static partial class HResults
internal const int E_HANDLE = unchecked((int)0x80070006);
internal const int E_INVALIDARG = unchecked((int)0x80070057);
internal const int E_NOTIMPL = unchecked((int)0x80004001);
internal const int E_OUTOFMEMORY = unchecked((int)0x8007000E);
internal const int E_POINTER = unchecked((int)0x80004003);
internal const int ERROR_MRM_MAP_NOT_FOUND = unchecked((int)0x80073B1F);
internal const int ERROR_TIMEOUT = unchecked((int)0x800705B4);
Expand Down
Loading
Loading