Skip to content

Commit

Permalink
Add initial support for Apple Silicon (#40435)
Browse files Browse the repository at this point in the history
* Add CoreCLR compilation support for Apple Silicon
    * Use CMAKE_OSX_ARCH rework
    * Set clang -arch flag
    * Workaround uname arch reporting emulated arch

* Fix native code compilation issues
* Implement missing osx-arm64 functionality
* Prototype fix for write no execute issues
* Strip libunwind pointer authentication bits

* Review feedback
* Does not fix Arm64 ABI issues

Co-authored-by: Jan Vorlicek <janvorli@microsoft.com>
  • Loading branch information
sdmaclea and janvorli authored Oct 8, 2020
1 parent 2db7179 commit fd094a9
Show file tree
Hide file tree
Showing 73 changed files with 1,348 additions and 334 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
<!-- Used for launchSettings.json and runtime config files. -->
<AppDesignerFolder>Properties</AppDesignerFolder>
<!-- Determine what architecture we are building on. -->
<BuildArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant())</BuildArchitecture>
<BuildArchitecture Condition="'$(BuildArchitecture)' == ''">$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant())</BuildArchitecture>
</PropertyGroup>

<Import Project="$(RepositoryEngineeringDir)Analyzers.props" />
Expand Down
8 changes: 5 additions & 3 deletions eng/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ initDistroRid()
local isCrossBuild="$3"
local isPortableBuild="$4"

# Only pass ROOTFS_DIR if __DoCrossArchBuild is specified.
if (( isCrossBuild == 1 )); then
# Only pass ROOTFS_DIR if __DoCrossArchBuild is specified and the current platform is not OSX that doesn't use rootfs
if [[ $isCrossBuild == 1 && "$targetOs" != "OSX" ]]; then
passedRootfsDir=${ROOTFS_DIR}
fi
initDistroRidGlobal ${targetOs} ${buildArch} ${isPortableBuild} ${passedRootfsDir}
Expand All @@ -153,6 +153,8 @@ portableBuild=1

source $scriptroot/native/init-os-and-arch.sh

hostArch=$arch

# Check if an action is passed in
declare -a actions=("b" "build" "r" "restore" "rebuild" "testnobuild" "sign" "publish" "clean")
actInt=($(comm -12 <(printf '%s\n' "${actions[@]/#/-}" | sort) <(printf '%s\n' "${@/#--/-}" | sort)))
Expand Down Expand Up @@ -436,6 +438,6 @@ initDistroRid $os $arch $crossBuild $portableBuild
# URL-encode space (%20) to avoid quoting issues until the msbuild call in /eng/common/tools.sh.
# In *proj files (XML docs), URL-encoded string are rendered in their decoded form.
cmakeargs="${cmakeargs// /%20}"
arguments="$arguments /p:TargetArchitecture=$arch"
arguments="$arguments /p:TargetArchitecture=$arch /p:BuildArchitecture=$hostArch"
arguments="$arguments /p:CMakeArgs=\"$cmakeargs\" $extraargs"
"$scriptroot/common/build.sh" $arguments
34 changes: 24 additions & 10 deletions eng/native/build-commons.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ initTargetDistroRid()

local passedRootfsDir=""

# Only pass ROOTFS_DIR if cross is specified.
if [[ "$__CrossBuild" == 1 ]]; then
# Only pass ROOTFS_DIR if cross is specified and the target platform is not Darwin that doesn't use rootfs
if [[ "$__CrossBuild" == 1 && "$platform" != "Darwin" ]]; then
passedRootfsDir="$ROOTFS_DIR"
fi

Expand Down Expand Up @@ -68,15 +68,28 @@ check_prereqs()

build_native()
{
platformArch="$1"
cmakeDir="$2"
tryrunDir="$3"
intermediatesDir="$4"
message="$5"
targetOS="$1"
platformArch="$2"
cmakeDir="$3"
tryrunDir="$4"
intermediatesDir="$5"
cmakeArgs="$6"
message="$7"

# All set to commence the build
echo "Commencing build of \"$message\" for $__TargetOS.$__BuildArch.$__BuildType in $intermediatesDir"

if [[ "$targetOS" == OSX ]]; then
if [[ "$platformArch" == x64 ]]; then
cmakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" $cmakeArgs"
elif [[ "$platformArch" == arm64 ]]; then
cmakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"arm64\" $cmakeArgs"
else
echo "Error: Unknown OSX architecture $platformArch."
exit 1
fi
fi

if [[ "$__UseNinja" == 1 ]]; then
generator="ninja"
buildTool="$(command -v ninja || command -v ninja-build)"
Expand Down Expand Up @@ -134,8 +147,8 @@ EOF
fi

engNativeDir="$__RepoRootDir/eng/native"
__CMakeArgs="-DCLR_ENG_NATIVE_DIR=\"$engNativeDir\" $__CMakeArgs"
nextCommand="\"$engNativeDir/gen-buildsys.sh\" \"$cmakeDir\" \"$tryrunDir\" \"$intermediatesDir\" $platformArch $__Compiler \"$__CompilerMajorVersion\" \"$__CompilerMinorVersion\" $__BuildType \"$generator\" $scan_build $__CMakeArgs"
cmakeArgs="-DCLR_ENG_NATIVE_DIR=\"$engNativeDir\" $cmakeArgs"
nextCommand="\"$engNativeDir/gen-buildsys.sh\" \"$cmakeDir\" \"$tryrunDir\" \"$intermediatesDir\" $platformArch $__Compiler \"$__CompilerMajorVersion\" \"$__CompilerMinorVersion\" $__BuildType \"$generator\" $scan_build $cmakeArgs"
echo "Invoking $nextCommand"
eval $nextCommand

Expand Down Expand Up @@ -448,7 +461,8 @@ fi
if [[ "$__CrossBuild" == 1 ]]; then
CROSSCOMPILE=1
export CROSSCOMPILE
if [[ ! -n "$ROOTFS_DIR" ]]; then
# Darwin that doesn't use rootfs
if [[ ! -n "$ROOTFS_DIR" && "$platform" != "Darwin" ]]; then
ROOTFS_DIR="$__RepoRootDir/.tools/rootfs/$__BuildArch"
export ROOTFS_DIR
fi
Expand Down
12 changes: 11 additions & 1 deletion eng/native/configurecompiler.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ if (CLR_CMAKE_HOST_UNIX)
add_definitions(-DHOST_UNIX)

if(CLR_CMAKE_HOST_OSX)
add_definitions(-DHOST_OSX)
if(CLR_CMAKE_HOST_UNIX_AMD64)
message("Detected OSX x86_64")
elseif(CLR_CMAKE_HOST_UNIX_ARM64)
Expand Down Expand Up @@ -374,7 +375,16 @@ if (CLR_CMAKE_HOST_UNIX)

# Specify the minimum supported version of macOS
if(CLR_CMAKE_HOST_OSX)
set(MACOS_VERSION_MIN_FLAGS -mmacosx-version-min=10.13)
if(CLR_CMAKE_HOST_ARCH_ARM64)
# 'pthread_jit_write_protect_np' is only available on macOS 11.0 or newer
set(MACOS_VERSION_MIN_FLAGS -mmacosx-version-min=11.0)
add_compile_options(-arch arm64)
elseif(CLR_CMAKE_HOST_ARCH_AMD64)
set(MACOS_VERSION_MIN_FLAGS -mmacosx-version-min=10.13)
add_compile_options(-arch x86_64)
else()
clr_unknown_arch()
endif()
add_compile_options(${MACOS_VERSION_MIN_FLAGS})
add_linker_flag(${MACOS_VERSION_MIN_FLAGS})
endif(CLR_CMAKE_HOST_OSX)
Expand Down
4 changes: 2 additions & 2 deletions eng/native/configureplatform.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ endif(CLR_CMAKE_HOST_OS STREQUAL Linux)
if(CLR_CMAKE_HOST_OS STREQUAL Darwin)
set(CLR_CMAKE_HOST_UNIX 1)
set(CLR_CMAKE_HOST_OSX 1)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64)
if(CMAKE_OSX_ARCHITECTURES STREQUAL x86_64)
set(CLR_CMAKE_HOST_UNIX_AMD64 1)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL arm64)
elseif(CMAKE_OSX_ARCHITECTURES STREQUAL arm64)
set(CLR_CMAKE_HOST_UNIX_ARM64 1)
else()
clr_unknown_arch()
Expand Down
11 changes: 9 additions & 2 deletions eng/native/gen-buildsys.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ done

cmake_extra_defines=
if [[ "$CROSSCOMPILE" == "1" ]]; then
if ! [[ -n "$ROOTFS_DIR" ]]; then
platform="$(uname)"
# OSX doesn't use rootfs
if ! [[ -n "$ROOTFS_DIR" || "$platform" == "Darwin" ]]; then
echo "ROOTFS_DIR not set for crosscompile"
exit 1
fi
Expand All @@ -74,7 +76,12 @@ if [[ "$CROSSCOMPILE" == "1" ]]; then
if [[ -n "$tryrun_dir" ]]; then
cmake_extra_defines="$cmake_extra_defines -C $tryrun_dir/tryrun.cmake"
fi
cmake_extra_defines="$cmake_extra_defines -DCMAKE_TOOLCHAIN_FILE=$scriptroot/../common/cross/toolchain.cmake"

if [[ "$platform" == "Darwin" ]]; then
cmake_extra_defines="$cmake_extra_defines -DCMAKE_SYSTEM_NAME=Darwin"
else
cmake_extra_defines="$cmake_extra_defines -DCMAKE_TOOLCHAIN_FILE=$scriptroot/../common/cross/toolchain.cmake"
fi
fi

if [[ "$build_arch" == "armel" ]]; then
Expand Down
11 changes: 11 additions & 0 deletions eng/native/init-os-and-arch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ if [ "$os" = "SunOS" ]; then
os="Solaris"
fi
CPUName=$(isainfo -n)
elif [ "$os" = "OSX" ]; then
# On OSX universal binaries make uname -m unreliable. The uname -m response changes
# based on what hardware is being emulated.
# Use sysctl instead
if [ "$(sysctl -q -n hw.optional.arm64)" = "1" ]; then
CPUName=arm64
elif [ "$(sysctl -q -n hw.optional.x86_64)" = "1" ]; then
CPUName=x86_64
else
CPUName=$(uname -m)
fi
else
# For rest of the operating systems, use uname(1) to determine what the CPU is.
CPUName=$(uname -m)
Expand Down
8 changes: 2 additions & 6 deletions src/coreclr/build-runtime.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ build_cross_architecture_components()
export __CMakeBinDir CROSSCOMPILE

__CMakeArgs="-DCLR_CMAKE_TARGET_ARCH=$__BuildArch -DCLR_CROSS_COMPONENTS_BUILD=1 $__CMakeArgs"
build_native "$__CrossArch" "$__ProjectRoot" "$__ProjectRoot" "$intermediatesForBuild" "cross-architecture components"
build_native "$__TargetOS" "$__CrossArch" "$__ProjectRoot" "$__ProjectRoot" "$intermediatesForBuild" "$__CMakeArgs" "cross-architecture components"

CROSSCOMPILE=1
export CROSSCOMPILE
Expand Down Expand Up @@ -198,10 +198,6 @@ __BuildRuntime=1

source "$__ProjectRoot"/_build-commons.sh

if [[ "${__BuildArch}" != "${__HostArch}" ]]; then
__CrossBuild=1
fi

# Set dependent variables

# Set the remaining variables based upon the determined build configuration
Expand Down Expand Up @@ -261,7 +257,7 @@ fi
if [[ "$__SkipNative" == 1 ]]; then
echo "Skipping CoreCLR component build."
else
build_native "$__BuildArch" "$__ProjectRoot" "$__ProjectRoot" "$__IntermediatesDir" "CoreCLR component"
build_native "$__TargetOS" "$__BuildArch" "$__ProjectRoot" "$__ProjectRoot" "$__IntermediatesDir" "$__CMakeArgs" "CoreCLR component"

# Build cross-architecture components
if [[ "$__SkipCrossArchNative" != 1 ]]; then
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/crosscomponents.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ if (CLR_CMAKE_HOST_OS STREQUAL CLR_CMAKE_TARGET_OS)
endif(CLR_CMAKE_TARGET_UNIX)
endif()

if(NOT CLR_CMAKE_HOST_LINUX AND NOT FEATURE_CROSSBITNESS)
if(NOT CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_OSX AND NOT FEATURE_CROSSBITNESS)
list (APPEND CLR_CROSS_COMPONENTS_LIST
mscordaccore
mscordbi
Expand Down
20 changes: 20 additions & 0 deletions src/coreclr/src/debug/createdump/mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ typedef struct elf64_note {
Elf64_Word n_type; /* Content type */
} Elf64_Nhdr;

#if defined(TARGET_AMD64)
struct user_fpregs_struct
{
unsigned short int cwd;
Expand Down Expand Up @@ -113,6 +114,25 @@ struct user_regs_struct
unsigned long long int fs;
unsigned long long int gs;
};
#elif defined(TARGET_ARM64)
struct user_fpsimd_struct
{
uint64_t vregs[2*32];
uint32_t fpcr;
uint32_t fpsr;
};

struct user_regs_struct
{
uint64_t regs[31];
uint64_t sp;
uint64_t pc;
uint32_t pstate;
};
#else
#error Unexpected architecture
#endif


typedef pid_t __pid_t;

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/debug/createdump/memoryregion.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#if defined(__arm__) || defined(__aarch64__)
#if !defined(PAGE_SIZE) && (defined(__arm__) || defined(__aarch64__))
#define PAGE_SIZE sysconf(_SC_PAGESIZE)
#endif

Expand Down
33 changes: 33 additions & 0 deletions src/coreclr/src/debug/createdump/threadinfomac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ThreadInfo::Initialize()
m_ppid = 0;
m_tgid = 0;

#if defined(TARGET_AMD64)
x86_thread_state64_t state;
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
kern_return_t result = ::thread_get_state(Port(), x86_THREAD_STATE64, (thread_state_t)&state, &stateCount);
Expand Down Expand Up @@ -88,6 +89,38 @@ ThreadInfo::Initialize()

memcpy(m_fpRegisters.st_space, &fpstate.__fpu_stmm0, sizeof(m_fpRegisters.st_space));
memcpy(m_fpRegisters.xmm_space, &fpstate.__fpu_xmm0, sizeof(m_fpRegisters.xmm_space));
#elif defined(TARGET_ARM64)
arm_thread_state64_t state;
mach_msg_type_number_t stateCount = ARM_THREAD_STATE64_COUNT;
kern_return_t result = ::thread_get_state(Port(), ARM_THREAD_STATE64, (thread_state_t)&state, &stateCount);
if (result != KERN_SUCCESS)
{
fprintf(stderr, "thread_get_state(%x) FAILED %x %s\n", m_tid, result, mach_error_string(result));
return false;
}

memcpy(m_gpRegisters.regs, &state.__x, sizeof(state.__x));
m_gpRegisters.regs[29] = arm_thread_state64_get_fp(state);
m_gpRegisters.regs[30] = (uint64_t)arm_thread_state64_get_lr_fptr(state);

m_gpRegisters.sp = arm_thread_state64_get_sp(state);
m_gpRegisters.pc = (uint64_t)arm_thread_state64_get_pc_fptr(state);

arm_neon_state64_t fpstate;
stateCount = ARM_NEON_STATE64_COUNT;
result = ::thread_get_state(Port(), ARM_NEON_STATE64, (thread_state_t)&fpstate, &stateCount);
if (result != KERN_SUCCESS)
{
fprintf(stderr, "thread_get_state(%x) FAILED %x %s\n", m_tid, result, mach_error_string(result));
return false;
}

memcpy(m_fpRegisters.vregs, &fpstate.__v, sizeof(m_fpRegisters.vregs));
m_fpRegisters.fpsr = fpstate.__fpsr;
m_fpRegisters.fpcr = fpstate.__fpcr;
#else
#error Unexpected architecture
#endif

return true;
}
4 changes: 2 additions & 2 deletions src/coreclr/src/debug/ee/arm64/dbghelpers.S
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ NESTED_ENTRY FuncEvalHijack, _TEXT, UnhandledExceptionHandlerUnix
PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32
str x0, [sp, #16]
// FuncEvalHijackWorker returns the address we should jump to.
bl FuncEvalHijackWorker
bl C_FUNC(FuncEvalHijackWorker)

EPILOG_STACK_FREE 32
EPILOG_BRANCH_REG x0
Expand All @@ -33,7 +33,7 @@ NESTED_END FuncEvalHijack
NESTED_ENTRY ExceptionHijack, _TEXT, UnhandledExceptionHandlerUnix

// make the call
bl ExceptionHijackWorker
bl C_FUNC(ExceptionHijackWorker)

// effective NOP to terminate unwind
mov x3, x3
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/src/dlls/mscordac/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,11 @@ if(CLR_CMAKE_HOST_WIN32 AND CLR_CMAKE_TARGET_UNIX)
)
endif(CLR_CMAKE_HOST_WIN32 AND CLR_CMAKE_TARGET_UNIX)

if(CLR_CMAKE_HOST_OSX)
if(CLR_CMAKE_HOST_UNIX)
list(APPEND COREDAC_LIBRARIES
coreclrpal_dac
)
endif(CLR_CMAKE_HOST_OSX)
endif(CLR_CMAKE_HOST_UNIX)

target_link_libraries(mscordaccore PRIVATE ${COREDAC_LIBRARIES})

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/gc/unix/config.gc.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#cmakedefine01 HAVE_SWAPCTL
#cmakedefine01 HAVE_SYSCTLBYNAME
#cmakedefine01 HAVE_PTHREAD_CONDATTR_SETCLOCK
#cmakedefine01 HAVE_MACH_ABSOLUTE_TIME
#cmakedefine01 HAVE_CLOCK_GETTIME_NSEC_NP
#cmakedefine01 HAVE_SCHED_GETAFFINITY
#cmakedefine01 HAVE_SCHED_SETAFFINITY
#cmakedefine01 HAVE_PTHREAD_SETAFFINITY_NP
Expand Down
20 changes: 9 additions & 11 deletions src/coreclr/src/gc/unix/configure.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,15 @@ check_cxx_source_runs("
check_library_exists(pthread pthread_condattr_setclock "" HAVE_PTHREAD_CONDATTR_SETCLOCK)

check_cxx_source_runs("
#include <stdlib.h>
#include <mach/mach_time.h>
int main()
{
int ret;
mach_timebase_info_data_t timebaseInfo;
ret = mach_timebase_info(&timebaseInfo);
mach_absolute_time();
exit(ret);
}
" HAVE_MACH_ABSOLUTE_TIME)
#include <stdlib.h>
#include <time.h>
int main()
{
int ret;
ret = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
exit((ret == 0) ? 1 : 0);
}" HAVE_CLOCK_GETTIME_NSEC_NP)


check_library_exists(c sched_getaffinity "" HAVE_SCHED_GETAFFINITY)
Expand Down
Loading

0 comments on commit fd094a9

Please sign in to comment.