Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NativeAOT] Implement thunk page generation and mapping for iOS-like platforms #82317

Merged
merged 4 commits into from
Feb 25, 2023
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 @@ -158,6 +158,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<LinkerArg Include="-L/usr/local/lib -lgssapi_krb5" Condition="'$(_targetOS)' == 'freebsd'" />
<!-- FreeBSD's inotify is an installed package and not found in default libraries -->
<LinkerArg Include="-L/usr/local/lib -linotify" Condition="'$(_targetOS)' == 'freebsd'" />
<LinkerArg Include="-Wl,-segprot,__THUNKS,rx,rx" Condition="'$(_IsiOSLikePlatform)' == 'true'" />
jkotas marked this conversation as resolved.
Show resolved Hide resolved

<LinkerArg Include="@(NativeFramework->'-framework %(Identity)')" Condition="'$(_IsApplePlatform)' == 'true'" />
</ItemGroup>
Expand Down
8 changes: 7 additions & 1 deletion src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ project(Runtime)
# Include auto-generated files on include path
set(CMAKE_INCLUDE_CURRENT_DIR ON)

add_definitions(-DFEATURE_RX_THUNKS)
if (CLR_CMAKE_TARGET_APPLE AND NOT CLR_CMAKE_TARGET_OSX)
list(APPEND RUNTIME_SOURCES_ARCH_ASM
${ARCH_SOURCES_DIR}/ThunkPoolThunks.${ASM_SUFFIX}
)
else()
add_definitions(-DFEATURE_RX_THUNKS)
endif()

if (CLR_CMAKE_TARGET_WIN32)
if (CLR_CMAKE_HOST_ARCH_ARM OR CLR_CMAKE_HOST_ARCH_ARM64)
Expand Down
126 changes: 126 additions & 0 deletions src/coreclr/nativeaot/Runtime/amd64/ThunkPoolThunks.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

.intel_syntax noprefix
#include <unixasmmacros.inc>

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

#define THUNK_CODESIZE 0x10 // 3 instructions, 4 bytes each (and we also have 4 bytes of padding)
#define THUNK_DATASIZE 0x10 // 2 qwords

#define POINTER_SIZE 0x08

#define THUNKS_MAP_SIZE 0x8000

#define PAGE_SIZE 0x1000
#define PAGE_SIZE_LOG2 12

// THUNK_POOL_NUM_THUNKS_PER_PAGE = min(PAGE_SIZE / THUNK_CODESIZE, (PAGE_SIZE - POINTER_SIZE) / THUNK_DATASIZE)
#define THUNK_POOL_NUM_THUNKS_PER_PAGE 0xff

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Thunk Pages ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.macro THUNKS_PAGE_BLOCK
IN_PAGE_INDEX = 0
.rept THUNK_POOL_NUM_THUNKS_PER_PAGE

.p2align 4

// Set r10 to the address of the current thunk's data block.
lea r10, [rip + THUNKS_MAP_SIZE - 7]

// jump to the location pointed at by the last qword in the data page
jmp qword ptr[r10 + PAGE_SIZE - POINTER_SIZE - (THUNK_DATASIZE * IN_PAGE_INDEX)]

IN_PAGE_INDEX = IN_PAGE_INDEX + 1
.endr
.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
.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

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; General Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

//
// IntPtr RhpGetThunksBase()
//
LEAF_ENTRY RhpGetThunksBase
// Return the address of the first thunk pool to the caller (this is really the base address)
lea rax, [rip + C_FUNC(ThunkPool)]
ret
LEAF_END RhpGetThunksBase

//
// int RhpGetNumThunksPerBlock()
//
LEAF_ENTRY RhpGetNumThunksPerBlock
mov rax, THUNK_POOL_NUM_THUNKS_PER_PAGE
ret
LEAF_END RhpGetNumThunksPerBlock

//
// int RhpGetThunkSize()
//
LEAF_ENTRY RhpGetThunkSize
mov rax, THUNK_CODESIZE
ret
LEAF_END RhpGetThunkSize

//
// int RhpGetNumThunkBlocksPerMapping()
//
LEAF_ENTRY RhpGetNumThunkBlocksPerMapping
mov rax, (THUNKS_MAP_SIZE / PAGE_SIZE)
ret
LEAF_END RhpGetNumThunkBlocksPerMapping

//
// int RhpGetThunkBlockSize
//
LEAF_ENTRY RhpGetThunkBlockSize
mov rax, PAGE_SIZE
ret
LEAF_END RhpGetThunkBlockSize

//
// IntPtr RhpGetThunkDataBlockAddress(IntPtr thunkStubAddress)
//
LEAF_ENTRY RhpGetThunkDataBlockAddress
mov rax, rdi
mov rdi, PAGE_SIZE - 1
not rdi
and rax, rdi
add rax, THUNKS_MAP_SIZE
ret
LEAF_END RhpGetThunkDataBlockAddress

//
// IntPtr RhpGetThunkStubsBlockAddress(IntPtr thunkDataAddress)
//
LEAF_ENTRY RhpGetThunkStubsBlockAddress
mov rax, rdi
mov rdi, PAGE_SIZE - 1
not rdi
and rax, rdi
sub rax, THUNKS_MAP_SIZE
ret
LEAF_END RhpGetThunkStubsBlockAddress
133 changes: 133 additions & 0 deletions src/coreclr/nativeaot/Runtime/arm64/ThunkPoolThunks.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include <unixasmmacros.inc>

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

#define THUNK_CODESIZE 0x10 // 3 instructions, 4 bytes each (and we also have 4 bytes of padding)
#define THUNK_DATASIZE 0x10 // 2 qwords

#define POINTER_SIZE 0x08

#define THUNKS_MAP_SIZE 0x8000

#ifdef TARGET_APPLE
#define PAGE_SIZE 0x4000
#define PAGE_SIZE_LOG2 14
#else
#error Unsupported OS
#endif

// THUNK_POOL_NUM_THUNKS_PER_PAGE = min(PAGE_SIZE / THUNK_CODESIZE, (PAGE_SIZE - POINTER_SIZE) / THUNK_DATASIZE)
#define THUNK_POOL_NUM_THUNKS_PER_PAGE 0x3ff

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Thunk Pages ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.macro THUNKS_PAGE_BLOCK
IN_PAGE_INDEX = 0
.rept THUNK_POOL_NUM_THUNKS_PER_PAGE

// Set xip0 to the address of the current thunk's data block.
adr xip0, THUNKS_MAP_SIZE

// start : xip0 points to the current thunks first data cell in the data page
// set xip0 to beginning of data page : xip0 <- xip0 - (THUNK_DATASIZE * current thunk's index)
// fix offset to point to last QWROD in page : xip1 <- [xip0 + PAGE_SIZE - POINTER_SIZE]
// tailcall to the location pointed at by the last qword in the data page
ldr xip1, [xip0, #(PAGE_SIZE - POINTER_SIZE - (THUNK_DATASIZE * IN_PAGE_INDEX))]
br xip1

brk 0xf000 // Stubs need to be 16-byte aligned for CFG table. Filling padding with a
// deterministic brk instruction, instead of having it just filled with zeros.

IN_PAGE_INDEX = IN_PAGE_INDEX + 1
.endr
.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
.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

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; General Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

//
// IntPtr RhpGetThunksBase()
//
LEAF_ENTRY RhpGetThunksBase
// Return the address of the first thunk pool to the caller (this is really the base address)
adrp x0, C_FUNC(ThunkPool)@PAGE
add x0, x0, C_FUNC(ThunkPool)@PAGEOFF
ret
LEAF_END RhpGetThunksBase

//
// int RhpGetNumThunksPerBlock()
//
LEAF_ENTRY RhpGetNumThunksPerBlock
mov x0, THUNK_POOL_NUM_THUNKS_PER_PAGE
ret
LEAF_END RhpGetNumThunksPerBlock

//
// int RhpGetThunkSize()
//
LEAF_ENTRY RhpGetThunkSize
mov x0, THUNK_CODESIZE
ret
LEAF_END RhpGetThunkSize

//
// int RhpGetNumThunkBlocksPerMapping()
//
LEAF_ENTRY RhpGetNumThunkBlocksPerMapping
mov x0, (THUNKS_MAP_SIZE / PAGE_SIZE)
ret
LEAF_END RhpGetNumThunkBlocksPerMapping

//
// int RhpGetThunkBlockSize
//
LEAF_ENTRY RhpGetThunkBlockSize
mov x0, PAGE_SIZE
ret
LEAF_END RhpGetThunkBlockSize

//
// IntPtr RhpGetThunkDataBlockAddress(IntPtr thunkStubAddress)
//
LEAF_ENTRY RhpGetThunkDataBlockAddress
mov x12, PAGE_SIZE - 1
bic x0, x0, x12
mov x12, THUNKS_MAP_SIZE
add x0, x0, x12
ret
LEAF_END RhpGetThunkDataBlockAddress

//
// IntPtr RhpGetThunkStubsBlockAddress(IntPtr thunkDataAddress)
//
LEAF_ENTRY RhpGetThunkStubsBlockAddress
mov x12, PAGE_SIZE - 1
bic x0, x0, x12
mov x12, THUNKS_MAP_SIZE
sub x0, x0, x12
ret
LEAF_END RhpGetThunkStubsBlockAddress
60 changes: 59 additions & 1 deletion src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
#include <time.h>
#endif

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

using std::nullptr_t;

#define PalRaiseFailFastException RaiseFailFastException
Expand Down Expand Up @@ -488,14 +493,63 @@ 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;

int st = dladdr((const void*)hTemplateModule, &info);
if (st == 0)
{
return UInt32_FALSE;
}

f = open(info.dli_fname, O_RDONLY);
if (f < 0)
{
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)
{
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");
}

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

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

REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(void *pBaseAddress)
{
#ifdef TARGET_APPLE
int ret = munmap(pBaseAddress, thunks_section->size + thunks_data_section->size);
return ret == 0 ? UInt32_TRUE : UInt32_FALSE;
#else
PORTABILITY_ASSERT("UNIXTODO: Implement this function");
#endif
}
#endif // !USE_PORTABLE_HELPERS && !FEATURE_RX_THUNKS

Expand All @@ -506,7 +560,11 @@ REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalMarkThunksAsValidCallTargets(
int thunkBlockSize,
int thunkBlocksPerMapping)
{
return UInt32_TRUE;
int ret = mprotect(
(void*)((uintptr_t)virtualAddress + (thunkBlocksPerMapping * OS_PAGE_SIZE)),
thunkBlocksPerMapping * OS_PAGE_SIZE,
PROT_READ | PROT_WRITE);
return ret == 0 ? UInt32_TRUE : UInt32_FALSE;
}

REDHAWK_PALEXPORT void REDHAWK_PALAPI PalSleep(uint32_t milliseconds)
Expand Down
4 changes: 2 additions & 2 deletions src/tests/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -641,8 +641,8 @@
<PropertyGroup Condition="'$(TestBuildMode)' == 'nativeaot'">
<!-- NativeAOT compiled output is placed into a 'native' subdirectory: we need to tweak
rpath so that the test can load its native library dependencies if there's any -->
<IlcRPath Condition="'$(TargetOS)' != 'osx'">$ORIGIN/..</IlcRPath>
<IlcRPath Condition="'$(TargetOS)' == 'osx'">@executable_path/..</IlcRPath>
<IlcRPath Condition="'$(TargetOS)' == 'osx' or '$(TargetsAppleMobile)' == 'true'">@executable_path/..</IlcRPath>
<IlcRPath Condition="'$(IlcRPath)' == ''">$ORIGIN/..</IlcRPath>

<!-- Works around "Error: Native compilation can run on x64 and arm64 hosts only"
Microsoft.NETCore.Native.targets expect IlcHostArch to be set but it doesn't have to -->
Expand Down