From 948764d37bdbb89e84886e233dea47fba0cb1017 Mon Sep 17 00:00:00 2001 From: Skyler Saleh Date: Sat, 16 Jan 2021 20:31:48 -0800 Subject: [PATCH] Apple M1: Build, Analytics, and Memory Management Analytics: - Incorporated fix to allow the full set of analytics that was recommended by spotlightishere BuildMacOSUniversalBinary: - The x86_64 slice for a universal binary is now built for 10.12 - The universal binary build script now can be configured though command line options instead of modifying the script itself. - os.system calls were replaced with equivalent subprocess calls - Formatting was reworked to be more PEP 8 compliant - The script was refactored to make it more modular - The com.apple.security.cs.disable-library-validation entitlement was removed Memory Management: - Changed the JITPageWrite*Execute*() functions to incorporate support for nesting Other: - Fixed several small lint errors - Fixed doc and formatting mistakes - Several small refactors to make things clearer --- BuildMacOSUniveralBinary.py | 146 --------- BuildMacOSUniversalBinary.py | 278 ++++++++++++++++++ CMakeLists.txt | 18 +- Readme.md | 2 +- Source/Core/Common/Arm64Emitter.cpp | 2 +- Source/Core/Common/MemoryUtil.cpp | 69 ++++- Source/Core/Common/MemoryUtil.h | 4 +- Source/Core/Core/DolphinAnalytics.cpp | 17 +- Source/Core/Core/PowerPC/JitArm64/Jit.cpp | 5 +- Source/Core/DolphinQt/CMakeLists.txt | 16 + Source/Core/DolphinQt/DolphinEmu.entitlements | 9 +- Source/Core/MacUpdater/CMakeLists.txt | 20 +- 12 files changed, 406 insertions(+), 180 deletions(-) delete mode 100644 BuildMacOSUniveralBinary.py create mode 100755 BuildMacOSUniversalBinary.py diff --git a/BuildMacOSUniveralBinary.py b/BuildMacOSUniveralBinary.py deleted file mode 100644 index f204c45749..0000000000 --- a/BuildMacOSUniveralBinary.py +++ /dev/null @@ -1,146 +0,0 @@ -""" -The current tooling supported in CMake, Homebrew, and QT5 are insufficient for creating -MacOSX universal binaries automatically for applications like Dolphin which have more -complicated build requirements (like different libraries, build flags and source files -for each target architecture). - -So instead, this script manages the conifiguration and compilation of distinct builds -and project files for each target architecture and then merges the two binaries into -a single universal binary. - -Running this script will: -1) Generate Xcode project files for the ARM build (if project files don't already exist) -2) Generate Xcode project files for the x64 build (if project files don't already exist) -3) Build the ARM project for the selected build_target -4) Build the x64 project for the selected build_target -5) Generates universal .app packages combining the ARM and x64 packages -6) Utilizes the lipo tool to combine the binary objects inside each of the packages - into universal binaries -7) Code signs the final universal binaries using the specified codesign_identity -""" - -##BEGIN CONFIG## -#Location of destination universal binary -dst_app = "universal/" -#Build Target (dolphin-emu to just build the emulator and skip the tests) -build_target = "ALL_BUILD" - -#Locations to pkg config files for arm and x64 libraries -#The default values of these paths are taken from the default -#paths used for homebrew -arm_pkg_config_path='/opt/homebrew/lib/pkgconfig' -x64_pkg_config_path='/usr/local/lib/pkgconfig' - -#Locations to qt5 directories for arm and x64 libraries -#The default values of these paths are taken from the default -#paths used for homebrew -arm_qt5_path='/opt/homebrew/opt/qt5' -x64_qt5_path='/usr/local/opt/qt5' - -# Identity to use for code signing. "-" indicates that the app will not -# be cryptographically signed/notarized but will instead just use a -# SHA checksum to verify the integrity of the app. This doesn't -# protect against malicious actors, but it does protect against -# running corrupted binaries and allows for access to the extended -# permisions needed for ARM builds - -codesign_identity ='"-"' - -##END CONFIG## - -import glob -import sys -import os -import shutil -import filecmp - - -#Configure ARM project files if they don't exist -if not os.path.exists("arm"): - os.mkdir("arm"); - os.chdir("arm"); - - os.system('PKG_CONFIG_PATH="'+arm_pkg_config_path+'" Qt5_DIR="'+arm_qt5_path+'" CMAKE_OSX_ARCHITECTURES=arm64 arch -arm64 cmake ../../ -G Xcode'); - os.chdir(".."); - -#Configure x64 project files if they don't exist -if not os.path.exists("x64"): - os.mkdir("x64"); - os.chdir("x64"); - - os.system('PKG_CONFIG_PATH="'+x64_pkg_config_path+'" Qt5_DIR="'+x64_qt5_path+'" CMAKE_OSX_ARCHITECTURES=x86_64 arch -x86_64 cmake ../../ -G Xcode') - os.chdir(".."); - -#Build ARM and x64 projects -os.system('xcodebuild -project arm/dolphin-emu.xcodeproj -target "'+build_target+'" -configuration Release'); -os.system('xcodebuild -project x64/dolphin-emu.xcodeproj -target "'+build_target+'" -configuration Release'); - -#Merge ARM and x64 binaries into universal binaries - -#Source binaries to merge together -src_app0 = "arm/Binaries/release" -src_app1 = "x64/Binaries/release" - - -if os.path.exists(dst_app): shutil.rmtree(dst_app) -os.mkdir(dst_app); - -def lipo(path0,path1,dst): - cmd = 'lipo -create -output "'+dst + '" "' + path0 +'" "' + path1+'"' - print(cmd) - os.system(cmd) -def recursiveMergeBinaries(src0,src1,dst): - #loop over all files in src0 - for newpath0 in glob.glob(src0+"/*"): - filename = os.path.basename(newpath0); - newpath1 = os.path.join(src1,filename); - new_dst_path = os.path.join(dst,filename); - if not os.path.islink(newpath0): - if os.path.exists(newpath1): - if os.path.isdir(newpath1): - os.mkdir(new_dst_path); - #recurse into directories - recursiveMergeBinaries(newpath0,newpath1,new_dst_path) - else: - if filecmp.cmp(newpath0,newpath1): - #copy files that are the same - shutil.copy(newpath0,new_dst_path); - else: - #lipo together files that are different - lipo(newpath0,newpath1,new_dst_path) - else: - #copy files that don't exist in path1 - shutil.copy(newpath0,new_dst_path) - - #loop over files in src1 and copy missing things over to dst - for newpath1 in glob.glob(src1+"/*"): - filename = os.path.basename(newpath0); - newpath0 = os.path.join(src0,filename); - new_dst_path = os.path.join(dst,filename); - if not os.path.exists(newpath0) and not os.path.islink(newpath1): - shutil.copytree(newpath1,new_dst_path); - - #fix up symlinks for path0 - for newpath0 in glob.glob(src0+"/*"): - filename = os.path.basename(newpath0); - new_dst_path = os.path.join(dst,filename); - if os.path.islink(newpath0): - relative_path = os.path.relpath(os.path.realpath(newpath0),src0) - print(relative_path,new_dst_path) - os.symlink(relative_path,new_dst_path); - #fix up symlinks for path1 - for newpath1 in glob.glob(src1+"/*"): - filename = os.path.basename(newpath1); - new_dst_path = os.path.join(dst,filename); - newpath0 = os.path.join(src0,filename); - if os.path.islink(newpath1) and not os.path.exists(newpath0): - relative_path = os.path.relpath(os.path.realpath(newpath1),src1) - print(relative_path,new_dst_path) - os.symlink(relative_path,new_dst_path); - - return; - -#create univeral binary -recursiveMergeBinaries(src_app0,src_app1,dst_app); -#codesign -os.system("codesign --deep --force -s "+codesign_identity+" " +dst_app +"/*"); diff --git a/BuildMacOSUniversalBinary.py b/BuildMacOSUniversalBinary.py new file mode 100755 index 0000000000..6c64426f5d --- /dev/null +++ b/BuildMacOSUniversalBinary.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python3 +""" +The current tooling supported in CMake, Homebrew, and QT5 are insufficient for +creating macOS universal binaries automatically for applications like Dolphin +which have more complicated build requirements (like different libraries, build +flags and source files for each target architecture). + +So instead, this script manages the configuration and compilation of distinct +builds and project files for each target architecture and then merges the two +binaries into a single universal binary. + +Running this script will: +1) Generate Xcode project files for the ARM build (if project files don't + already exist) +2) Generate Xcode project files for the x64 build (if project files don't + already exist) +3) Build the ARM project for the selected build_target +4) Build the x64 project for the selected build_target +5) Generates universal .app packages combining the ARM and x64 packages +6) Utilizes the lipo tool to combine the binary objects inside each of the + packages into universal binaries +7) Code signs the final universal binaries using the specified + codesign_identity +""" + +import argparse +import copy +import filecmp +import glob +import json # Used to print config +import os +import shutil +import subprocess +import sys + +# #BEGIN CONFIG# # + +# The config variables listed below are the defaults, but they can be +# overridden by command line arguments see parse_args(), or run: +# BuildMacOSUniversalBinary.py --help +DEFAULT_CONFIG = { + + # Location of destination universal binary + "dst_app": "universal/", + # Build Target (dolphin-emu to just build the emulator and skip the tests) + "build_target": "ALL_BUILD", + + # Locations to pkg config files for arm and x64 libraries + # The default values of these paths are taken from the default + # paths used for homebrew + "arm64_pkg_config_path": '/opt/homebrew/lib/pkgconfig', + "x86_64_pkg_config_path": '/usr/local/lib/pkgconfig', + + # Locations to qt5 directories for arm and x64 libraries + # The default values of these paths are taken from the default + # paths used for homebrew + "arm64_qt5_path": '/opt/homebrew/opt/qt5', + "x86_64_qt5_path": '/usr/local/opt/qt5', + + # Identity to use for code signing. "-" indicates that the app will not + # be cryptographically signed/notarized but will instead just use a + # SHA checksum to verify the integrity of the app. This doesn't + # protect against malicious actors, but it does protect against + # running corrupted binaries and allows for access to the extended + # permisions needed for ARM builds + "codesign_identity": '-', + # Etitlements file to use for code signing + "entitlements": "../Source/Core/DolphinQt/DolphinEmu.entitlements", + + # Minimum macOS version for each architecture slice + "arm64_mac_os_deployment_target": "11.0.0", + "x86_64_mac_os_deployment_target": "10.12.0" + +} +# # END CONFIG # # + +# Architectures to build for. This is explicity left out of the command line +# config options for several reasons: +# 1) Adding new architectures will generally require more code changes +# 2) Single architecture builds should utilize the normal generated cmake +# project files rather than this wrapper script + +ARCHITECTURES = ["x86_64", "arm64"] + + +def parse_args(default_conf=DEFAULT_CONFIG): + """ + Parses the command line arguments into a config dictionary. + """ + + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + '--target', + help='Build target in generated project files', + default=default_conf["build_target"], + dest="build_target") + + parser.add_argument( + '--dst_app', + help='Directory where universal binary will be stored', + default=default_conf["dst_app"]) + + parser.add_argument( + '--entitlements', + help='Path to .entitlements file for code signing', + default=default_conf["entitlements"]) + + parser.add_argument( + '--codesign', + help='Code signing identity to use to sign the applications', + default=default_conf["codesign_identity"], + dest="codesign_identity") + + for arch in ARCHITECTURES: + parser.add_argument( + '--{}_pkg_config'.format(arch), + help="Folder containing .pc files for {} libraries".format(arch), + default=default_conf[arch+"_pkg_config_path"], + dest=arch+"_pkg_config_path") + + parser.add_argument( + '--{}_qt5_path'.format(arch), + help="Install path for {} qt5 libraries".format(arch), + default=default_conf[arch+"_qt5_path"]) + + parser.add_argument( + '--{}_mac_os_deployment_target'.format(arch), + help="Deployment architecture for {} slice".format(arch), + default=default_conf[arch+"_mac_os_deployment_target"]) + + return vars(parser.parse_args()) + + +def lipo(path0, path1, dst): + if subprocess.call(['lipo', '-create', '-output', dst, path0, path1]) != 0: + print("WARNING: {} and {} can not be lipo'd, keeping {}" + .format(path0, path1, path0)) + + shutil.copy(path0, dst) + + +def recursiveMergeBinaries(src0, src1, dst): + """ + Merges two build trees together for different architectures into a single + universal binary. + + The rules for merging are: + + 1) Files that exist in either src tree are copied into the dst tree + 2) Files that exist in both trees and are identical are copied over + unmodified + 3) Files that exist in both trees and are non-identical are lipo'd + 4) Symlinks are created in the destination tree to mirror the hierarchy in + the source trees + """ + + # loop over all files in src0 + for newpath0 in glob.glob(src0+"/*"): + filename = os.path.basename(newpath0) + newpath1 = os.path.join(src1, filename) + new_dst_path = os.path.join(dst, filename) + if os.path.islink(newpath0): + # symlinks will be fixed after files are resolved + continue + + if not os.path.exists(newpath1): + # copy files that don't exist in path1 + shutil.copy(newpath0, new_dst_path) + continue + + if os.path.isdir(newpath1): + os.mkdir(new_dst_path) + # recurse into directories + recursiveMergeBinaries(newpath0, newpath1, new_dst_path) + continue + + if filecmp.cmp(newpath0, newpath1): + # copy files that are the same + shutil.copy(newpath0, new_dst_path) + else: + # lipo together files that are different + lipo(newpath0, newpath1, new_dst_path) + + # loop over files in src1 and copy missing things over to dst + for newpath1 in glob.glob(src1+"/*"): + filename = os.path.basename(newpath0) + newpath0 = os.path.join(src0, filename) + new_dst_path = os.path.join(dst, filename) + if not os.path.exists(newpath0) and not os.path.islink(newpath1): + shutil.copytree(newpath1, new_dst_path) + + # fix up symlinks for path0 + for newpath0 in glob.glob(src0+"/*"): + filename = os.path.basename(newpath0) + new_dst_path = os.path.join(dst, filename) + if os.path.islink(newpath0): + relative_path = os.path.relpath(os.path.realpath(newpath0), src0) + os.symlink(relative_path, new_dst_path) + # fix up symlinks for path1 + for newpath1 in glob.glob(src1+"/*"): + filename = os.path.basename(newpath1) + new_dst_path = os.path.join(dst, filename) + newpath0 = os.path.join(src0, filename) + if os.path.islink(newpath1) and not os.path.exists(newpath0): + relative_path = os.path.relpath(os.path.realpath(newpath1), src1) + os.symlink(relative_path, new_dst_path) + + +def build(config): + """ + Builds the project with the parameters specified in config. + """ + + print("Building config:") + print(json.dumps(config, indent=4)) + + dst_app = config["dst_app"] + # Configure and build single architecture builds for each architecture + for arch in ARCHITECTURES: + # Create build directory for architecture + if not os.path.exists(arch): + os.mkdir(arch) + # Setup environment variables for build + envs = os.environ.copy() + envs['PKG_CONFIG_PATH'] = config[arch+"_pkg_config_path"] + envs['Qt5_DIR'] = config[arch+"_qt5_path"] + envs['CMAKE_OSX_ARCHITECTURES'] = arch + + subprocess.check_call([ + 'arch', '-'+arch, + 'cmake', '../../', '-G', 'Xcode', + '-DCMAKE_OSX_DEPLOYMENT_TARGET=' + + config[arch+"_mac_os_deployment_target"], + '-DMACOS_CODE_SIGNING_IDENTITY=' + + config['codesign_identity'], + '-DMACOS_CODE_SIGNING_IDENTITY_UPDATER=' + + config['codesign_identity'], + '-DMACOS_CODE_SIGNING="ON"' + ], + env=envs, cwd=arch) + + # Build project + subprocess.check_call(['xcodebuild', + '-project', 'dolphin-emu.xcodeproj', + '-target', config["build_target"], + '-configuration', 'Release'], cwd=arch) + + # Source binary trees to merge together + src_app0 = ARCHITECTURES[0]+"/Binaries/release" + src_app1 = ARCHITECTURES[1]+"/Binaries/release" + + if os.path.exists(dst_app): + shutil.rmtree(dst_app) + + os.mkdir(dst_app) + # create univeral binary + recursiveMergeBinaries(src_app0, src_app1, dst_app) + # codesign the universal binary + for path in glob.glob(dst_app+"/*"): + subprocess.check_call([ + 'codesign', + '-d', + '--force', + '-s', + config["codesign_identity"], + '--options', 'runtime', + '--entitlements', config["entitlements"], + '--deep', + '--verbose=2', + path]) + + +if __name__ == "__main__": + conf = parse_args() + build(conf) + print("Built Universal Binary successfully!") diff --git a/CMakeLists.txt b/CMakeLists.txt index 9931ad76ed..5aed1f4fa2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,12 @@ cmake_minimum_required(VERSION 3.10) # Minimum OS X version. # This is inserted into the Info.plist as well. -# MacOS prior to 10.14 did not fully support C++17, which is used to -# handle configuration options and aligned alloc +# MacOS prior to 10.14 did not support aligned alloc which is used to implement +# std::unique_ptr in the arm64 C++ standard library. x86_64 builds can override +# this to 10.12.0 using -DCMAKE_OSX_DEPLOYMENT_TARGET="10.12.0" without issue. +# This is done in the universal binary building script to build a binary that +# runs on 10.12 on x86_64 computers, while still containing an arm64 slice. + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14.0" CACHE STRING "") set(CMAKE_USER_MAKE_RULES_OVERRIDE "CMake/FlagsOverride.cmake") @@ -68,8 +72,12 @@ else() endif() if(APPLE) - option(OSX_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF) - option(SKIP_POSTPROCESS_BUNDLE "Skip postprocessing bundle for redistributability" OFF) + option(MACOS_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF) + option(SKIP_POSTPROCESS_BUNDLE "Skip postprocessing bundle for redistributability" OFF) + # Enable adhoc code signing by default (otherwise makefile builds on ARM will not work) + option(MACOS_CODE_SIGNING "Enable codesigning" ON) + set(MACOS_CODE_SIGNING_IDENTITY "-" CACHE STRING "The identity used for codesigning.") + set(MACOS_CODE_SIGNING_IDENTITY_UPDATER "-" CACHE STRING "The identity used for codesigning, for the updater.") endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux") @@ -290,7 +298,7 @@ else() endif() if(CMAKE_SYSTEM_NAME MATCHES "Darwin") - if(NOT OSX_USE_DEFAULT_SEARCH_PATH) + if(NOT MACOS_USE_DEFAULT_SEARCH_PATH) # Hack up the path to prioritize the path to built-in OS libraries to # increase the chance of not depending on a bunch of copies of them # installed by MacPorts, Fink, Homebrew, etc, and ending up copying diff --git a/Readme.md b/Readme.md index f383e7ac33..041bde5be9 100644 --- a/Readme.md +++ b/Readme.md @@ -73,7 +73,7 @@ application bundle using the following steps: 4. Universal binaries will be available in the `universal` folder Doing this requires installation of library dependencies for both x64 and ARM (or universal library -equilvalents) and may require modification of the config portion of the script to point to the +equivalents) and may require modification of the config portion of the script to point to the library locations A binary supporting a single architecture can be built as well using the following steps: diff --git a/Source/Core/Common/Arm64Emitter.cpp b/Source/Core/Common/Arm64Emitter.cpp index e5f1b5156d..4d6feb222f 100644 --- a/Source/Core/Common/Arm64Emitter.cpp +++ b/Source/Core/Common/Arm64Emitter.cpp @@ -345,7 +345,7 @@ void ARM64XEmitter::FlushIcacheSection(u8* start, u8* end) if (start == end) return; -#if defined(IOS) ||defined(__APPLE__) +#if defined(IOS) || defined(__APPLE__) // Header file says this is equivalent to: sys_icache_invalidate(start, end - start); sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); #elif defined(WIN32) diff --git a/Source/Core/Common/MemoryUtil.cpp b/Source/Core/Common/MemoryUtil.cpp index b91faeeb91..d1e9d25785 100644 --- a/Source/Core/Common/MemoryUtil.cpp +++ b/Source/Core/Common/MemoryUtil.cpp @@ -39,12 +39,11 @@ void* AllocateExecutableMemory(size_t size) #if defined(_WIN32) void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); #else - int map_flags =MAP_ANON | MAP_PRIVATE; + int map_flags = MAP_ANON | MAP_PRIVATE; #if defined(_M_ARM_64) && defined(__APPLE__) map_flags |= MAP_JIT; #endif - void* ptr = - mmap(nullptr, size, PROT_READ | PROT_WRITE |PROT_EXEC , map_flags, -1, 0); + void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, map_flags, -1, 0); if (ptr == MAP_FAILED) ptr = nullptr; #endif @@ -54,7 +53,19 @@ void* AllocateExecutableMemory(size_t size) return ptr; } -// Certain platforms (Mac OS X on ARM) enforce that a single thread can only have write or +// This function is used to provide a counter for the JITPageWrite*Execute* +// functions to enable nesting. The static variable is wrapped in a a function +// to allow those functions to be called inside of the constructor of a static +// variable portably. +// +// The variable is thread_local as the W^X mode is specific to each running thread. +static int& JITPageWriteNestCounter() +{ + static thread_local int nest_counter = 0; + return nest_counter; +} + +// Certain platforms (Mac OS on ARM) enforce that a single thread can only have write or // execute permissions to pages at any given point of time. The two below functions // are used to toggle between having write permissions or execute permissions. // @@ -66,20 +77,52 @@ void* AllocateExecutableMemory(size_t size) // PrepareInstructionStreamForJIT(); // JITPageWriteDisableExecuteEnable(); -//Allows a thread to write to executable memory, but not execute the data. -void JITPageWriteEnableExecuteDisable(){ +// These functions can be nested, in which case execution will only be enabled +// after the call to the JITPageWriteDisableExecuteEnable from the top most +// nesting level. Example: + +// [JIT page is in execute mode for the thread] +// JITPageWriteEnableExecuteDisable(); +// [JIT page is in write mode for the thread] +// JITPageWriteEnableExecuteDisable(); +// [JIT page is in write mode for the thread] +// JITPageWriteDisableExecuteEnable(); +// [JIT page is in write mode for the thread] +// JITPageWriteDisableExecuteEnable(); +// [JIT page is in execute mode for the thread] + +// Allows a thread to write to executable memory, but not execute the data. +void JITPageWriteEnableExecuteDisable() +{ #if defined(_M_ARM_64) && defined(__APPLE__) - if (__builtin_available(macOS 11.0, *)) { - pthread_jit_write_protect_np(0); + if (JITPageWriteNestCounter() == 0) + { + if (__builtin_available(macOS 11.0, *)) + { + pthread_jit_write_protect_np(0); + } } #endif - + JITPageWriteNestCounter()++; } -//Allows a thread to execute memory allocated for execution, but not write to it. -void JITPageWriteDisableExecuteEnable(){ +// Allows a thread to execute memory allocated for execution, but not write to it. +void JITPageWriteDisableExecuteEnable() +{ + JITPageWriteNestCounter()--; + + // Sanity check the NestCounter to identify underflow + // This can indicate the calls to JITPageWriteDisableExecuteEnable() + // are not matched with previous calls to JITPageWriteEnableExecuteDisable() + if (JITPageWriteNestCounter() < 0) + PanicAlertFmt("JITPageWriteNestCounter() underflowed"); + #if defined(_M_ARM_64) && defined(__APPLE__) - if (__builtin_available(macOS 11.0, *)) { - pthread_jit_write_protect_np(1); + if (JITPageWriteNestCounter() == 0) + { + if (__builtin_available(macOS 11.0, *)) + { + pthread_jit_write_protect_np(1); + } } #endif } diff --git a/Source/Core/Common/MemoryUtil.h b/Source/Core/Common/MemoryUtil.h index 0ee9128ac9..b64d5cd8de 100644 --- a/Source/Core/Common/MemoryUtil.h +++ b/Source/Core/Common/MemoryUtil.h @@ -10,9 +10,9 @@ namespace Common { void* AllocateExecutableMemory(size_t size); -//Allows a thread to write to executable memory, but not execute the data. +// Allows a thread to write to executable memory, but not execute the data. void JITPageWriteEnableExecuteDisable(); -//Allows a thread to execute memory allocated for execution, but not write to it. +// Allows a thread to execute memory allocated for execution, but not write to it. void JITPageWriteDisableExecuteEnable(); void* AllocateMemoryPages(size_t size); void FreeMemoryPages(void* ptr, size_t size); diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index c26689a919..11071d0678 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -277,8 +277,7 @@ void DolphinAnalytics::MakeBaseBuilder() builder.AddData("android-version", s_get_val_func("DEVICE_OS")); #elif defined(__APPLE__) builder.AddData("os-type", "osx"); -//objc_msgSend is only available on x86 -#ifndef _M_ARM_64 + // id processInfo = [NSProcessInfo processInfo] id processInfo = reinterpret_cast(objc_msgSend)( objc_getClass("NSProcessInfo"), sel_getUid("processInfo")); @@ -290,17 +289,21 @@ void DolphinAnalytics::MakeBaseBuilder() s64 minor_version; // NSInteger minorVersion s64 patch_version; // NSInteger patchVersion }; - + // Under arm64, we need to call objc_msgSend to recieve a struct. + // On x86_64, we need to explicitly call objc_msgSend_stret for a struct. +#if _M_ARM_64 +#define msgSend objc_msgSend +#else +#define msgSend objc_msgSend_stret +#endif // NSOperatingSystemVersion version = [processInfo operatingSystemVersion] - OSVersion version = reinterpret_cast(objc_msgSend_stret)( + OSVersion version = reinterpret_cast(msgSend)( processInfo, sel_getUid("operatingSystemVersion")); - +#undef msgSend builder.AddData("osx-ver-major", version.major_version); builder.AddData("osx-ver-minor", version.minor_version); builder.AddData("osx-ver-bugfix", version.patch_version); } -#endif - #elif defined(__linux__) builder.AddData("os-type", "linux"); #elif defined(__FreeBSD__) diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp index f217dba6a8..8e9ece7615 100644 --- a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp @@ -73,8 +73,8 @@ void JitArm64::Init() bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx) { - //Ifdef this since the exception handler runs on a separate thread on Mac OS X (ARM) -#if !defined(__APPLE__) && !defined(_M_ARM_64) + // Ifdef this since the exception handler runs on a separate thread on macOS (ARM) +#if !(defined(__APPLE__) && defined(_M_ARM_64)) // We can't handle any fault from other threads. if (!Core::IsCPUThread()) { @@ -631,7 +631,6 @@ void JitArm64::Jit(u32) DoJit(em_address, b, nextPC); blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block.m_physical_addresses); Common::JITPageWriteDisableExecuteEnable(); - } void JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index e930825c72..cdad88cb4c 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -518,6 +518,22 @@ if(APPLE) POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $) + + if(MACOS_CODE_SIGNING) + # Code sign make file builds + add_custom_command(TARGET dolphin-emu + POST_BUILD COMMAND + /usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY}" --deep --options runtime --entitlements ${CMAKE_SOURCE_DIR}/Source/Core/DolphinQt/DolphinEmu.entitlements "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Dolphin.app" || true) + + # Code sign builds for build systems that do have release/debug variants (Xcode) + add_custom_command(TARGET dolphin-emu + POST_BUILD COMMAND + /usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY}" --deep --options runtime --entitlements ${CMAKE_SOURCE_DIR}/Source/Core/DolphinQt/DolphinEmu.entitlements "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}/Dolphin.app" || true) + + add_custom_command(TARGET dolphin-emu + POST_BUILD COMMAND + /usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY}" --deep --options runtime --entitlements ${CMAKE_SOURCE_DIR}/Source/Core/DolphinQt/DolphinEmu.entitlements "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/Dolphin.app" || true) + endif() else() install(TARGETS dolphin-emu RUNTIME DESTINATION ${bindir}) endif() diff --git a/Source/Core/DolphinQt/DolphinEmu.entitlements b/Source/Core/DolphinQt/DolphinEmu.entitlements index 461123ca3b..50c57c58c1 100644 --- a/Source/Core/DolphinQt/DolphinEmu.entitlements +++ b/Source/Core/DolphinQt/DolphinEmu.entitlements @@ -4,7 +4,14 @@ com.apple.security.cs.allow-jit - com.apple.security.cs.disable-library-validation + + com.apple.security.device.audio-input + + com.apple.security.automation.apple-events + + + com.apple.security.cs.disable-library-validation + diff --git a/Source/Core/MacUpdater/CMakeLists.txt b/Source/Core/MacUpdater/CMakeLists.txt index 725917e030..65e7d99a92 100644 --- a/Source/Core/MacUpdater/CMakeLists.txt +++ b/Source/Core/MacUpdater/CMakeLists.txt @@ -13,6 +13,8 @@ set(SOURCES add_executable(MacUpdater ${SOURCES}) set(MacUpdater_NAME "Dolphin Updater") +set(MacUpdater_BIN_DIR ${CMAKE_BINARY_DIR}/Binaries) +set(MacUpdater_BUNDLE_PATH ${MacUpdater_BIN_DIR}/${MacUpdater_NAME}.app) set_target_properties(MacUpdater PROPERTIES MACOSX_BUNDLE true @@ -53,8 +55,24 @@ foreach(sb ${STORYBOARDS}) add_custom_command(TARGET MacUpdater POST_BUILD COMMAND ${IBTOOL} --errors --warnings --notices --output-format human-readable-text - --compile ${MacUpdater_BIN_DIR}/${MacUpdater_NAME}.app/Contents/Resources/${sb}c + --compile ${MacUpdater_BUNDLE_PATH}/Contents/Resources/${sb}c ${CMAKE_CURRENT_SOURCE_DIR}/${sb} COMMENT "Compiling Storyboard ${sb}...") endforeach() +if(MACOS_CODE_SIGNING) + if (MACOS_CODE_SIGNING_IDENTITY_UPDATER STREQUAL "") + set(MACOS_CODE_SIGNING_IDENTITY_UPDATER "${MACOS_CODE_SIGNING_IDENTITY}") + endif() + + # Make file build code sign + add_custom_command(TARGET MacUpdater POST_BUILD + COMMAND test ${MacUpdater_BUNDLE_PATH} || /usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY_UPDATER}" --deep --options runtime ${MacUpdater_BUNDLE_PATH}) + + # Xcode build code sign + add_custom_command(TARGET MacUpdater POST_BUILD + COMMAND test "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}/${MacUpdater_NAME}.app" || /usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY_UPDATER}" --deep --options runtime "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}/${MacUpdater_NAME}.app") + + add_custom_command(TARGET MacUpdater POST_BUILD + COMMAND test "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/${MacUpdater_NAME}.app" || /usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY_UPDATER}" --deep --options runtime "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/${MacUpdater_NAME}.app") +endif()