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
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,6 @@ The .NET Foundation licenses this file to you under the MIT license.
<NativeSystemLibrary Include="m" />
</ItemGroup>

<ItemGroup>
<ExtraLinkerArg Include="-segprot,__THUNKS,rx,rx" Condition="'$(_IsApplePlatform)' == 'true'" />
</ItemGroup>

<ItemGroup>
<LinkerArg Include="-gz=zlib" Condition="'$(CompressSymbols)' != 'false'" />
<LinkerArg Include="-fuse-ld=$(LinkerFlavor)" Condition="'$(LinkerFlavor)' != ''" />
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/nativeaot/Runtime/PalRedhawk.h
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ REDHAWK_PALIMPORT void REDHAWK_PALAPI PalHijack(HANDLE hThread, _In_opt_ void* p
REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalRegisterHijackCallback(_In_ PalHijackCallback callback);

REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalAllocateThunksFromTemplate(_In_ HANDLE hTemplateModule, uint32_t templateRva, size_t templateSize, _Outptr_result_bytebuffer_(templateSize) void** newThunksOut);
REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(_In_ void *pBaseAddress);
REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(_In_ void *pBaseAddress, size_t templateSize);

REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalMarkThunksAsValidCallTargets(
void *virtualAddress,
Expand Down
6 changes: 4 additions & 2 deletions src/coreclr/nativeaot/Runtime/ThunksMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ EXTERN_C NATIVEAOT_API void* __cdecl RhAllocateThunksMapping()
int thunkBlockSize = RhpGetThunkBlockSize();
int templateSize = thunkBlocksPerMapping * thunkBlockSize;

#ifndef TARGET_APPLE // Apple platforms cannot use the initial template
if (pThunksTemplateAddress == NULL)
{
// First, we use the thunks directly from the thunks template sections in the module until all
Expand All @@ -337,12 +338,13 @@ EXTERN_C NATIVEAOT_API void* __cdecl RhAllocateThunksMapping()
pThunkMap = pThunksTemplateAddress;
}
else
#endif
{
// We've already used the thunks template in the module for some previous thunks, and we
// cannot reuse it here. Now we need to create a new mapping of the thunks section in order to have
// more thunks

uint8_t* pModuleBase = (uint8_t*)PalGetModuleHandleFromPointer(pThunksTemplateAddress);
uint8_t* pModuleBase = (uint8_t*)PalGetModuleHandleFromPointer(RhpGetThunksBase());
int templateRva = (int)((uint8_t*)RhpGetThunksBase() - pModuleBase);

if (!PalAllocateThunksFromTemplate((HANDLE)pModuleBase, templateRva, templateSize, &pThunkMap))
Expand All @@ -357,7 +359,7 @@ EXTERN_C NATIVEAOT_API void* __cdecl RhAllocateThunksMapping()
thunkBlocksPerMapping))
{
if (pThunkMap != pThunksTemplateAddress)
PalFreeThunksFromTemplate(pThunkMap);
PalFreeThunksFromTemplate(pThunkMap, templateSize);

return NULL;
}
Expand Down
11 changes: 2 additions & 9 deletions src/coreclr/nativeaot/Runtime/amd64/ThunkPoolThunks.S
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,15 @@
.endm

#ifdef TARGET_APPLE
// Create two segments in the Mach-O file:
// __THUNKS with executable permissions
// __THUNKS_DATA with read/write permissions

.section __THUNKS,__thunks,regular,pure_instructions
// Thunk pool
.text
.p2align PAGE_SIZE_LOG2
PATCH_LABEL ThunkPool
.rept (THUNKS_MAP_SIZE / PAGE_SIZE)
.p2align PAGE_SIZE_LOG2
THUNKS_PAGE_BLOCK
.endr
.p2align PAGE_SIZE_LOG2
.section __THUNKS_DATA,__thunks,regular
.p2align PAGE_SIZE_LOG2
.space THUNKS_MAP_SIZE
.p2align PAGE_SIZE_LOG2
#else
#error Unsupported OS
#endif
Expand Down
11 changes: 2 additions & 9 deletions src/coreclr/nativeaot/Runtime/arm64/ThunkPoolThunks.S
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,15 @@
.endm

#ifdef TARGET_APPLE
// Create two segments in the Mach-O file:
// __THUNKS with executable permissions
// __THUNKS_DATA with read/write permissions

.section __THUNKS,__thunks,regular,pure_instructions
// Thunk pool
.text
.p2align PAGE_SIZE_LOG2
PATCH_LABEL ThunkPool
.rept (THUNKS_MAP_SIZE / PAGE_SIZE)
.p2align PAGE_SIZE_LOG2
THUNKS_PAGE_BLOCK
.endr
.p2align PAGE_SIZE_LOG2
.section __THUNKS_DATA,__thunks,regular
.p2align PAGE_SIZE_LOG2
.space THUNKS_MAP_SIZE
.p2align PAGE_SIZE_LOG2
#else
#error Unsupported OS
#endif
Expand Down
69 changes: 35 additions & 34 deletions src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@
#endif

#ifdef TARGET_APPLE
#include <minipal/getexepath.h>
#include <mach-o/getsect.h>
#include <mach/mach.h>
#endif

using std::nullptr_t;
Expand Down Expand Up @@ -516,59 +515,61 @@ extern "C" bool PalDetachThread(void* thread)

#if !defined(USE_PORTABLE_HELPERS) && !defined(FEATURE_RX_THUNKS)

#ifdef TARGET_APPLE
static const struct section_64 *thunks_section;
static const struct section_64 *thunks_data_section;
#endif

REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalAllocateThunksFromTemplate(HANDLE hTemplateModule, uint32_t templateRva, size_t templateSize, void** newThunksOut)
{
#ifdef TARGET_APPLE
int f;
Dl_info info;
vm_address_t addr, taddr;
vm_prot_t prot, max_prot;
kern_return_t ret;

int st = dladdr((const void*)hTemplateModule, &info);
if (st == 0)
// Allocate two contiguous ranges of memory: the first range will contain the trampolines
// and the second range will contain their data.
do
{
return UInt32_FALSE;
}
ret = vm_allocate(mach_task_self(), &addr, templateSize * 2, VM_FLAGS_ANYWHERE);
} while (ret == KERN_ABORTED);

f = open(info.dli_fname, O_RDONLY);
if (f < 0)
if (ret != KERN_SUCCESS)
{
return UInt32_FALSE;
}

// NOTE: We ignore templateRva since we would need to convert it to file offset
// and templateSize is useless too. Instead we read the sections from the
// executable and determine the size from them.
if (thunks_section == NULL)
do
{
ret = vm_remap(
mach_task_self(), &addr, templateSize, 0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
Copy link
Member

Choose a reason for hiding this comment

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

Just trying to understand the difference here, Mono seems to pass only FALSE in place of flags when calling vm_remap, why are we passing VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE instead (or vice versa should we use the same on Mono side as well)?

Copy link
Member Author

@filipnavara filipnavara Dec 13, 2023

Choose a reason for hiding this comment

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

We should use this on Mono side as well. Mono uses a loop with vm_allocate(2*size)+vm_deallocate(1*size)+vm_remap. There's a race condition between the vm_deallocate+vm_remap not ending up at the same address. If the race condition is hit then it starts again from the beginning. VM_FLAGS_OVERWRITE gets rid of this race condition by skipping the intermediate reallocation.

Copy link
Member

Choose a reason for hiding this comment

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

/cc: @lambdageek ^

Copy link
Member

@lambdageek lambdageek Dec 14, 2023

Choose a reason for hiding this comment

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

Yea seems like a good idea (I'm not intimately familiar with our infinite trampolines code, but it seems workable).

So instead of the loop we would do a single pass:

  • vm_allocate(2*size) returning addr
  • set tpage = addr+size
  • vm_remap(&tpage, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE)
  • if success, we're done; if failure, dump core

?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep

mach_task_self(), ((vm_address_t)hTemplateModule + templateRva), FALSE, &prot, &max_prot, VM_INHERIT_SHARE);
} while (ret == KERN_ABORTED);

if (ret != KERN_SUCCESS)
{
const struct mach_header_64 *hdr = (const struct mach_header_64 *)hTemplateModule;
thunks_section = getsectbynamefromheader_64(hdr, "__THUNKS", "__thunks");
thunks_data_section = getsectbynamefromheader_64(hdr, "__THUNKS_DATA", "__thunks");
do
{
ret = vm_deallocate(mach_task_self(), addr, templateSize * 2);
} while (ret == KERN_ABORTED);

return UInt32_FALSE;
}

*newThunksOut = mmap(
NULL,
thunks_section->size + thunks_data_section->size,
PROT_READ | PROT_EXEC,
MAP_PRIVATE,
f,
thunks_section->offset);
close(f);
*newThunksOut = (void*)addr;

return *newThunksOut == NULL ? UInt32_FALSE : UInt32_TRUE;
return UInt32_TRUE;
#else
PORTABILITY_ASSERT("UNIXTODO: Implement this function");
#endif
}

REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(void *pBaseAddress)
REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(void *pBaseAddress, size_t templateSize)
{
#ifdef TARGET_APPLE
int ret = munmap(pBaseAddress, thunks_section->size + thunks_data_section->size);
return ret == 0 ? UInt32_TRUE : UInt32_FALSE;
kern_return_t ret;

do
{
ret = vm_deallocate(mach_task_self(), (vm_address_t)pBaseAddress, templateSize * 2);
} while (ret == KERN_ABORTED);

return ret == KERN_SUCCESS ? UInt32_TRUE : UInt32_FALSE;
#else
PORTABILITY_ASSERT("UNIXTODO: Implement this function");
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalAllocateThunksFromTemplate(_In_
#endif
}

REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(_In_ void *pBaseAddress)
REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(_In_ void *pBaseAddress, size_t templateSize)
{
#ifdef XBOX_ONE
return TRUE;
Expand Down