Merge pull request #9441 from skylersaleh/master
Apple M1 Support for MacOS
This commit is contained in:
commit
51671921c4
|
@ -0,0 +1,330 @@
|
||||||
|
#!/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) Generate universal .app packages combining the ARM and x64 packages
|
||||||
|
6) Use the lipo tool to combine the binary objects inside each of the
|
||||||
|
packages into universal binaries
|
||||||
|
7) Code sign the final universal binaries using the specified
|
||||||
|
codesign_identity
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import filecmp
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import multiprocessing
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# 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",
|
||||||
|
|
||||||
|
# Location for CMake to search for files (default is for homebrew)
|
||||||
|
"arm64_cmake_prefix": "/opt/homebrew",
|
||||||
|
"x86_64_cmake_prefix": "/usr/local",
|
||||||
|
|
||||||
|
# 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": "-",
|
||||||
|
# Entitlements 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",
|
||||||
|
|
||||||
|
# CMake Generator to use for building
|
||||||
|
"generator": "Unix Makefiles",
|
||||||
|
"build_type": "Release",
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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(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=conf["build_target"],
|
||||||
|
dest="build_target")
|
||||||
|
parser.add_argument(
|
||||||
|
"-G",
|
||||||
|
help="CMake Generator to use for creating project files",
|
||||||
|
default=conf["generator"],
|
||||||
|
dest="generator")
|
||||||
|
parser.add_argument(
|
||||||
|
"--build_type",
|
||||||
|
help="CMake build type [Debug, Release, RelWithDebInfo, MinSizeRel]",
|
||||||
|
default=conf["build_type"],
|
||||||
|
dest="build_type")
|
||||||
|
parser.add_argument(
|
||||||
|
"--dst_app",
|
||||||
|
help="Directory where universal binary will be stored",
|
||||||
|
default=conf["dst_app"])
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--entitlements",
|
||||||
|
help="Path to .entitlements file for code signing",
|
||||||
|
default=conf["entitlements"])
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--codesign",
|
||||||
|
help="Code signing identity to use to sign the applications",
|
||||||
|
default=conf["codesign_identity"],
|
||||||
|
dest="codesign_identity")
|
||||||
|
|
||||||
|
for arch in ARCHITECTURES:
|
||||||
|
parser.add_argument(
|
||||||
|
f"--{arch}_cmake_prefix",
|
||||||
|
help="Folder for cmake to search for packages",
|
||||||
|
default=conf[arch+"_cmake_prefix"],
|
||||||
|
dest=arch+"_cmake_prefix")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
f"--{arch}_qt5_path",
|
||||||
|
help=f"Install path for {arch} qt5 libraries",
|
||||||
|
default=conf[arch+"_qt5_path"])
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
f"--{arch}_mac_os_deployment_target",
|
||||||
|
help=f"Deployment architecture for {arch} slice",
|
||||||
|
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(f"WARNING: {path0} and {path1} cannot be lipo'd")
|
||||||
|
|
||||||
|
shutil.copy(path0, dst)
|
||||||
|
|
||||||
|
|
||||||
|
def recursive_merge_binaries(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
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check that all files present in the folder are of the same type and that
|
||||||
|
# links link to the same relative location
|
||||||
|
for newpath0 in glob.glob(src0+"/*"):
|
||||||
|
filename = os.path.basename(newpath0)
|
||||||
|
newpath1 = os.path.join(src1, filename)
|
||||||
|
if not os.path.exists(newpath1):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if os.path.islink(newpath0) and os.path.islink(newpath1):
|
||||||
|
if os.path.relpath(newpath0, src0) == os.path.relpath(newpath1, src1):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if os.path.isdir(newpath0) and os.path.isdir(newpath1):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# isfile() can be true for links so check that both are not links
|
||||||
|
# before checking if they are both files
|
||||||
|
if (not os.path.islink(newpath0)) and (not os.path.islink(newpath1)):
|
||||||
|
if os.path.isfile(newpath0) and os.path.isfile(newpath1):
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise Exception(f"{newpath0} and {newpath1} cannot be " +
|
||||||
|
"merged into a universal binary because they are of " +
|
||||||
|
"incompatible types. Perhaps the installed libraries" +
|
||||||
|
" are from different versions for each architecture")
|
||||||
|
|
||||||
|
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):
|
||||||
|
if os.path.isdir(newpath0):
|
||||||
|
shutil.copytree(newpath0, new_dst_path)
|
||||||
|
else:
|
||||||
|
shutil.copy(newpath0, new_dst_path)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
if os.path.isdir(newpath1):
|
||||||
|
os.mkdir(new_dst_path)
|
||||||
|
recursive_merge_binaries(newpath0, newpath1, new_dst_path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if filecmp.cmp(newpath0, newpath1):
|
||||||
|
shutil.copy(newpath0, new_dst_path)
|
||||||
|
else:
|
||||||
|
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(newpath1)
|
||||||
|
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)):
|
||||||
|
if os.path.isdir(newpath1):
|
||||||
|
shutil.copytree(newpath1, new_dst_path)
|
||||||
|
else:
|
||||||
|
shutil.copy(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))
|
||||||
|
|
||||||
|
# Configure and build single architecture builds for each architecture
|
||||||
|
for arch in ARCHITECTURES:
|
||||||
|
if not os.path.exists(arch):
|
||||||
|
os.mkdir(arch)
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["Qt5_DIR"] = config[arch+"_qt5_path"]
|
||||||
|
env["CMAKE_OSX_ARCHITECTURES"] = arch
|
||||||
|
env["CMAKE_PREFIX_PATH"] = config[arch+"_cmake_prefix"]
|
||||||
|
|
||||||
|
# Add the other architecture's prefix path to the ignore path so that
|
||||||
|
# CMake doesn't try to pick up the wrong architecture's libraries when
|
||||||
|
# cross compiling.
|
||||||
|
ignore_path = ""
|
||||||
|
for a in ARCHITECTURES:
|
||||||
|
if a != arch:
|
||||||
|
ignore_path = config[a+"_cmake_prefix"]
|
||||||
|
|
||||||
|
subprocess.check_call([
|
||||||
|
"cmake", "../../", "-G", config["generator"],
|
||||||
|
"-DCMAKE_BUILD_TYPE=" + config["build_type"],
|
||||||
|
'-DCMAKE_CXX_FLAGS="-DMACOS_UNIVERSAL_BUILD=1"',
|
||||||
|
'-DCMAKE_C_FLAGS="-DMACOS_UNIVERSAL_BUILD=1"',
|
||||||
|
# System name needs to be specified for CMake to use
|
||||||
|
# the specified CMAKE_SYSTEM_PROCESSOR
|
||||||
|
"-DCMAKE_SYSTEM_NAME=Darwin",
|
||||||
|
"-DCMAKE_PREFIX_PATH="+config[arch+"_cmake_prefix"],
|
||||||
|
"-DCMAKE_SYSTEM_PROCESSOR="+arch,
|
||||||
|
"-DCMAKE_IGNORE_PATH="+ignore_path,
|
||||||
|
"-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=env, cwd=arch)
|
||||||
|
|
||||||
|
threads = multiprocessing.cpu_count()
|
||||||
|
subprocess.check_call(["cmake", "--build", ".",
|
||||||
|
"--config", config["build_type"],
|
||||||
|
"--parallel", f"{threads}"], cwd=arch)
|
||||||
|
|
||||||
|
dst_app = config["dst_app"]
|
||||||
|
|
||||||
|
if os.path.exists(dst_app):
|
||||||
|
shutil.rmtree(dst_app)
|
||||||
|
|
||||||
|
# Create and codesign the universal binary/
|
||||||
|
os.mkdir(dst_app)
|
||||||
|
|
||||||
|
# Source binary trees to merge together
|
||||||
|
src_app0 = ARCHITECTURES[0]+"/Binaries/"
|
||||||
|
src_app1 = ARCHITECTURES[1]+"/Binaries/"
|
||||||
|
|
||||||
|
recursive_merge_binaries(src_app0, src_app1, dst_app)
|
||||||
|
for path in glob.glob(dst_app+"/*"):
|
||||||
|
if os.path.isdir(path) and os.path.splitext(path)[1] != ".app":
|
||||||
|
continue
|
||||||
|
|
||||||
|
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!")
|
|
@ -2,13 +2,17 @@
|
||||||
# General setup
|
# General setup
|
||||||
#
|
#
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
|
||||||
# Minimum OS X version.
|
# Minimum OS X version.
|
||||||
# This is inserted into the Info.plist as well.
|
# This is inserted into the Info.plist as well.
|
||||||
|
|
||||||
# MacOS prior to 10.12 did not fully support C++17, which is used to
|
# MacOS prior to 10.14 did not support aligned alloc which is used to implement
|
||||||
# handle configuration options
|
# std::unique_ptr in the arm64 C++ standard library. x86_64 builds can override
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.12.0" CACHE STRING "")
|
# 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")
|
set(CMAKE_USER_MAKE_RULES_OVERRIDE "CMake/FlagsOverride.cmake")
|
||||||
|
|
||||||
|
@ -68,8 +72,12 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
option(OSX_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF)
|
option(MACOS_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF)
|
||||||
option(SKIP_POSTPROCESS_BUNDLE "Skip postprocessing bundle for redistributability" 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()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
@ -290,7 +298,7 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
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
|
# 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
|
# increase the chance of not depending on a bunch of copies of them
|
||||||
# installed by MacPorts, Fink, Homebrew, etc, and ending up copying
|
# installed by MacPorts, Fink, Homebrew, etc, and ending up copying
|
||||||
|
@ -306,9 +314,10 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_STRICT_ALIASING NO)
|
set(CMAKE_XCODE_ATTRIBUTE_GCC_STRICT_ALIASING NO)
|
||||||
|
|
||||||
# Specify target CPUs.
|
# Specify target CPUs.
|
||||||
check_and_add_flag(HAVE_MSSSE3 -mssse3)
|
if(_ARCH_64 AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
|
||||||
check_and_add_flag(HAVE_ARCH_CORE2 -march=core2)
|
check_and_add_flag(HAVE_MSSSE3 -mssse3)
|
||||||
|
check_and_add_flag(HAVE_ARCH_CORE2 -march=core2)
|
||||||
|
endif()
|
||||||
# Linker flags.
|
# Linker flags.
|
||||||
# Drop unreachable code and data.
|
# Drop unreachable code and data.
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip,-dead_strip_dylibs")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip,-dead_strip_dylibs")
|
||||||
|
|
Binary file not shown.
|
@ -1 +1 @@
|
||||||
MoltenVK from https://github.com/KhronosGroup/MoltenVK, commit b9b78def172074872bfbb1015ccf75eeec554ae2
|
MoltenVK from https://vulkan.lunarg.com/sdk/home#mac, version 1.2.170.0
|
14
Readme.md
14
Readme.md
|
@ -65,6 +65,8 @@ missing packages yourself.
|
||||||
|
|
||||||
### macOS Build Steps:
|
### macOS Build Steps:
|
||||||
|
|
||||||
|
A binary supporting a single architecture can be built using the following steps:
|
||||||
|
|
||||||
1. `mkdir build`
|
1. `mkdir build`
|
||||||
2. `cd build`
|
2. `cd build`
|
||||||
3. `cmake ..`
|
3. `cmake ..`
|
||||||
|
@ -72,6 +74,18 @@ missing packages yourself.
|
||||||
|
|
||||||
An application bundle will be created in `./Binaries`.
|
An application bundle will be created in `./Binaries`.
|
||||||
|
|
||||||
|
A script is also provided to build universal binaries supporting both x64 and ARM in the same
|
||||||
|
application bundle using the following steps:
|
||||||
|
|
||||||
|
1. `mkdir build`
|
||||||
|
2. `cd build`
|
||||||
|
3. `python ../BuildMacOSUniversalBinary.py`
|
||||||
|
4. Universal binaries will be available in the `universal` folder
|
||||||
|
|
||||||
|
Doing this is more complex as it requires installation of library dependencies for both x64 and ARM (or universal library
|
||||||
|
equivalents) and may require specifying additional arguments to point to relevant library locations.
|
||||||
|
Execute BuildMacOSUniversalBinary.py --help for more details.
|
||||||
|
|
||||||
### Linux Global Build Steps:
|
### Linux Global Build Steps:
|
||||||
|
|
||||||
To install to your system.
|
To install to your system.
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <libkern/OSCacheControl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Arm64Gen
|
namespace Arm64Gen
|
||||||
{
|
{
|
||||||
|
@ -342,7 +345,7 @@ void ARM64XEmitter::FlushIcacheSection(u8* start, u8* end)
|
||||||
if (start == end)
|
if (start == end)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#if defined(IOS)
|
#if defined(IOS) || defined(__APPLE__)
|
||||||
// Header file says this is equivalent to: sys_icache_invalidate(start, end - start);
|
// Header file says this is equivalent to: sys_icache_invalidate(start, end - start);
|
||||||
sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
|
sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
|
||||||
#elif defined(WIN32)
|
#elif defined(WIN32)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||||
#ifndef __FreeBSD__
|
#ifndef __FreeBSD__
|
||||||
#include <asm/hwcap.h>
|
#include <asm/hwcap.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -71,7 +71,17 @@ void CPUInfo::Detect()
|
||||||
vendor = CPUVendor::ARM;
|
vendor = CPUVendor::ARM;
|
||||||
bFlushToZero = true;
|
bFlushToZero = true;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef __APPLE__
|
||||||
|
num_cores = std::thread::hardware_concurrency();
|
||||||
|
|
||||||
|
// M-series CPUs have all of these
|
||||||
|
bFP = true;
|
||||||
|
bASIMD = true;
|
||||||
|
bAES = true;
|
||||||
|
bSHA1 = true;
|
||||||
|
bSHA2 = true;
|
||||||
|
bCRC32 = true;
|
||||||
|
#elif defined(_WIN32)
|
||||||
num_cores = std::thread::hardware_concurrency();
|
num_cores = std::thread::hardware_concurrency();
|
||||||
|
|
||||||
// Windows does not provide any mechanism for querying the system registers on ARMv8, unlike Linux
|
// Windows does not provide any mechanism for querying the system registers on ARMv8, unlike Linux
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#else
|
#else
|
||||||
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
@ -38,9 +39,15 @@ void* AllocateExecutableMemory(size_t size)
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
#else
|
#else
|
||||||
void* ptr =
|
int map_flags = MAP_ANON | MAP_PRIVATE;
|
||||||
mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
|
#if defined(__APPLE__)
|
||||||
|
// This check is in place to prepare for x86_64 MAP_JIT support. While MAP_JIT did exist
|
||||||
|
// prior to 10.14, it had restrictions on the number of JIT allocations that were removed
|
||||||
|
// in 10.14.
|
||||||
|
if (__builtin_available(macOS 10.14, *))
|
||||||
|
map_flags |= MAP_JIT;
|
||||||
|
#endif
|
||||||
|
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, map_flags, -1, 0);
|
||||||
if (ptr == MAP_FAILED)
|
if (ptr == MAP_FAILED)
|
||||||
ptr = nullptr;
|
ptr = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
@ -50,6 +57,79 @@ void* AllocateExecutableMemory(size_t size)
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// The default state of these allocations in Dolphin is for them to be executable,
|
||||||
|
// but not writeable. So, functions that are updating these pages should wrap their
|
||||||
|
// writes like below:
|
||||||
|
|
||||||
|
// JITPageWriteEnableExecuteDisable();
|
||||||
|
// PrepareInstructionStreamForJIT();
|
||||||
|
// JITPageWriteDisableExecuteEnable();
|
||||||
|
|
||||||
|
// 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 (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()
|
||||||
|
{
|
||||||
|
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 (JITPageWriteNestCounter() == 0)
|
||||||
|
{
|
||||||
|
if (__builtin_available(macOS 11.0, *))
|
||||||
|
{
|
||||||
|
pthread_jit_write_protect_np(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void* AllocateMemoryPages(size_t size)
|
void* AllocateMemoryPages(size_t size)
|
||||||
{
|
{
|
||||||
|
@ -128,7 +208,10 @@ void WriteProtectMemory(void* ptr, size_t size, bool allowExecute)
|
||||||
DWORD oldValue;
|
DWORD oldValue;
|
||||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
|
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
|
||||||
PanicAlertFmt("WriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString());
|
PanicAlertFmt("WriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString());
|
||||||
#else
|
#elif !(defined(_M_ARM_64) && defined(__APPLE__))
|
||||||
|
// MacOS 11.2 on ARM does not allow for changing the access permissions of pages
|
||||||
|
// that were marked executable, instead it uses the protections offered by MAP_JIT
|
||||||
|
// for write protection.
|
||||||
if (mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ) != 0)
|
if (mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ) != 0)
|
||||||
PanicAlertFmt("WriteProtectMemory failed!\nmprotect: {}", LastStrerrorString());
|
PanicAlertFmt("WriteProtectMemory failed!\nmprotect: {}", LastStrerrorString());
|
||||||
#endif
|
#endif
|
||||||
|
@ -140,7 +223,10 @@ void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute)
|
||||||
DWORD oldValue;
|
DWORD oldValue;
|
||||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue))
|
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue))
|
||||||
PanicAlertFmt("UnWriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString());
|
PanicAlertFmt("UnWriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString());
|
||||||
#else
|
#elif !(defined(_M_ARM_64) && defined(__APPLE__))
|
||||||
|
// MacOS 11.2 on ARM does not allow for changing the access permissions of pages
|
||||||
|
// that were marked executable, instead it uses the protections offered by MAP_JIT
|
||||||
|
// for write protection.
|
||||||
if (mprotect(ptr, size,
|
if (mprotect(ptr, size,
|
||||||
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ) != 0)
|
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ) != 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,23 @@
|
||||||
namespace Common
|
namespace Common
|
||||||
{
|
{
|
||||||
void* AllocateExecutableMemory(size_t size);
|
void* AllocateExecutableMemory(size_t size);
|
||||||
|
|
||||||
|
// These two functions control the executable/writable state of the W^X memory
|
||||||
|
// allocations. More detailed documentation about them is in the .cpp file.
|
||||||
|
// In general where applicable the ScopedJITPageWriteAndNoExecute wrapper
|
||||||
|
// should be used to prevent bugs from not pairing up the calls properly.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
void JITPageWriteDisableExecuteEnable();
|
||||||
|
// RAII Wrapper around JITPageWrite*Execute*(). When this is in scope the thread can
|
||||||
|
// write to executable memory but not execute it.
|
||||||
|
struct ScopedJITPageWriteAndNoExecute
|
||||||
|
{
|
||||||
|
ScopedJITPageWriteAndNoExecute() { JITPageWriteEnableExecuteDisable(); }
|
||||||
|
~ScopedJITPageWriteAndNoExecute() { JITPageWriteDisableExecuteEnable(); }
|
||||||
|
};
|
||||||
void* AllocateMemoryPages(size_t size);
|
void* AllocateMemoryPages(size_t size);
|
||||||
void FreeMemoryPages(void* ptr, size_t size);
|
void FreeMemoryPages(void* ptr, size_t size);
|
||||||
void* AllocateAlignedMemory(size_t size, size_t alignment);
|
void* AllocateAlignedMemory(size_t size, size_t alignment);
|
||||||
|
|
|
@ -289,11 +289,17 @@ void DolphinAnalytics::MakeBaseBuilder()
|
||||||
s64 minor_version; // NSInteger minorVersion
|
s64 minor_version; // NSInteger minorVersion
|
||||||
s64 patch_version; // NSInteger patchVersion
|
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]
|
// NSOperatingSystemVersion version = [processInfo operatingSystemVersion]
|
||||||
OSVersion version = reinterpret_cast<OSVersion (*)(id, SEL)>(objc_msgSend_stret)(
|
OSVersion version = reinterpret_cast<OSVersion (*)(id, SEL)>(msgSend)(
|
||||||
processInfo, sel_getUid("operatingSystemVersion"));
|
processInfo, sel_getUid("operatingSystemVersion"));
|
||||||
|
#undef msgSend
|
||||||
builder.AddData("osx-ver-major", version.major_version);
|
builder.AddData("osx-ver-major", version.major_version);
|
||||||
builder.AddData("osx-ver-minor", version.minor_version);
|
builder.AddData("osx-ver-minor", version.minor_version);
|
||||||
builder.AddData("osx-ver-bugfix", version.patch_version);
|
builder.AddData("osx-ver-bugfix", version.patch_version);
|
||||||
|
|
|
@ -67,6 +67,12 @@ typedef x86_thread_state64_t SContext;
|
||||||
#define CTX_R14 __r14
|
#define CTX_R14 __r14
|
||||||
#define CTX_R15 __r15
|
#define CTX_R15 __r15
|
||||||
#define CTX_RIP __rip
|
#define CTX_RIP __rip
|
||||||
|
#elif _M_ARM_64
|
||||||
|
typedef arm_thread_state64_t SContext;
|
||||||
|
#define CTX_REG(x) __x[x]
|
||||||
|
#define CTX_LR __x[30]
|
||||||
|
#define CTX_SP __sp
|
||||||
|
#define CTX_PC __pc
|
||||||
#else
|
#else
|
||||||
#error No context definition for architecture
|
#error No context definition for architecture
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -25,6 +25,20 @@
|
||||||
#include <unistd.h> // Needed for _POSIX_VERSION
|
#include <unistd.h> // Needed for _POSIX_VERSION
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#ifdef _M_X86_64
|
||||||
|
#define THREAD_STATE64_COUNT x86_THREAD_STATE64_COUNT
|
||||||
|
#define THREAD_STATE64 x86_THREAD_STATE64
|
||||||
|
#define thread_state64_t x86_thread_state64_t
|
||||||
|
#elif defined(_M_ARM_64)
|
||||||
|
#define THREAD_STATE64_COUNT ARM_THREAD_STATE64_COUNT
|
||||||
|
#define THREAD_STATE64 ARM_THREAD_STATE64
|
||||||
|
#define thread_state64_t arm_thread_state64_t
|
||||||
|
#else
|
||||||
|
#error Unsupported architecture
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace EMM
|
namespace EMM
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -123,7 +137,7 @@ static void ExceptionThread(mach_port_t port)
|
||||||
int64_t code[2];
|
int64_t code[2];
|
||||||
int flavor;
|
int flavor;
|
||||||
mach_msg_type_number_t old_stateCnt;
|
mach_msg_type_number_t old_stateCnt;
|
||||||
natural_t old_state[x86_THREAD_STATE64_COUNT];
|
natural_t old_state[THREAD_STATE64_COUNT];
|
||||||
mach_msg_trailer_t trailer;
|
mach_msg_trailer_t trailer;
|
||||||
} msg_in;
|
} msg_in;
|
||||||
|
|
||||||
|
@ -134,7 +148,7 @@ static void ExceptionThread(mach_port_t port)
|
||||||
kern_return_t RetCode;
|
kern_return_t RetCode;
|
||||||
int flavor;
|
int flavor;
|
||||||
mach_msg_type_number_t new_stateCnt;
|
mach_msg_type_number_t new_stateCnt;
|
||||||
natural_t new_state[x86_THREAD_STATE64_COUNT];
|
natural_t new_state[THREAD_STATE64_COUNT];
|
||||||
} msg_out;
|
} msg_out;
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
memset(&msg_in, 0xee, sizeof(msg_in));
|
memset(&msg_in, 0xee, sizeof(msg_in));
|
||||||
|
@ -165,13 +179,13 @@ static void ExceptionThread(mach_port_t port)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg_in.flavor != x86_THREAD_STATE64)
|
if (msg_in.flavor != THREAD_STATE64)
|
||||||
{
|
{
|
||||||
PanicAlertFmt("unknown flavor {} (expected {})", msg_in.flavor, x86_THREAD_STATE64);
|
PanicAlertFmt("unknown flavor {} (expected {})", msg_in.flavor, THREAD_STATE64);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
x86_thread_state64_t* state = (x86_thread_state64_t*)msg_in.old_state;
|
thread_state64_t* state = (thread_state64_t*)msg_in.old_state;
|
||||||
|
|
||||||
bool ok = JitInterface::HandleFault((uintptr_t)msg_in.code[1], state);
|
bool ok = JitInterface::HandleFault((uintptr_t)msg_in.code[1], state);
|
||||||
|
|
||||||
|
@ -184,9 +198,9 @@ static void ExceptionThread(mach_port_t port)
|
||||||
if (ok)
|
if (ok)
|
||||||
{
|
{
|
||||||
msg_out.RetCode = KERN_SUCCESS;
|
msg_out.RetCode = KERN_SUCCESS;
|
||||||
msg_out.flavor = x86_THREAD_STATE64;
|
msg_out.flavor = THREAD_STATE64;
|
||||||
msg_out.new_stateCnt = x86_THREAD_STATE64_COUNT;
|
msg_out.new_stateCnt = THREAD_STATE64_COUNT;
|
||||||
memcpy(msg_out.new_state, msg_in.old_state, x86_THREAD_STATE64_COUNT * sizeof(natural_t));
|
memcpy(msg_out.new_state, msg_in.old_state, THREAD_STATE64_COUNT * sizeof(natural_t));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -218,7 +232,7 @@ void InstallExceptionHandler()
|
||||||
// Debuggers set the task port, so we grab the thread port.
|
// Debuggers set the task port, so we grab the thread port.
|
||||||
CheckKR("thread_set_exception_ports",
|
CheckKR("thread_set_exception_ports",
|
||||||
thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port,
|
thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port,
|
||||||
EXCEPTION_STATE | MACH_EXCEPTION_CODES, x86_THREAD_STATE64));
|
EXCEPTION_STATE | MACH_EXCEPTION_CODES, THREAD_STATE64));
|
||||||
// ...and get rid of our copy so that MACH_NOTIFY_NO_SENDERS works.
|
// ...and get rid of our copy so that MACH_NOTIFY_NO_SENDERS works.
|
||||||
CheckKR("mach_port_mod_refs",
|
CheckKR("mach_port_mod_refs",
|
||||||
mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1));
|
mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1));
|
||||||
|
|
|
@ -73,6 +73,8 @@ void JitArm64::Init()
|
||||||
|
|
||||||
bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx)
|
bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx)
|
||||||
{
|
{
|
||||||
|
// 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.
|
// We can't handle any fault from other threads.
|
||||||
if (!Core::IsCPUThread())
|
if (!Core::IsCPUThread())
|
||||||
{
|
{
|
||||||
|
@ -80,6 +82,7 @@ bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx)
|
||||||
DoBacktrace(access_address, ctx);
|
DoBacktrace(access_address, ctx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
|
@ -124,6 +127,7 @@ void JitArm64::ClearCache()
|
||||||
m_handler_to_loc.clear();
|
m_handler_to_loc.clear();
|
||||||
|
|
||||||
blocks.Clear();
|
blocks.Clear();
|
||||||
|
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
|
||||||
ClearCodeSpace();
|
ClearCodeSpace();
|
||||||
farcode.ClearCodeSpace();
|
farcode.ClearCodeSpace();
|
||||||
UpdateMemoryOptions();
|
UpdateMemoryOptions();
|
||||||
|
@ -596,6 +600,7 @@ void JitArm64::Jit(u32)
|
||||||
{
|
{
|
||||||
ClearCache();
|
ClearCache();
|
||||||
}
|
}
|
||||||
|
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
|
||||||
|
|
||||||
std::size_t block_size = m_code_buffer.size();
|
std::size_t block_size = m_code_buffer.size();
|
||||||
const u32 em_address = PowerPC::ppcState.pc;
|
const u32 em_address = PowerPC::ppcState.pc;
|
||||||
|
|
|
@ -59,11 +59,11 @@ void JitArm64BlockCache::WriteLinkBlock(Arm64Gen::ARM64XEmitter& emit,
|
||||||
|
|
||||||
void JitArm64BlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest)
|
void JitArm64BlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest)
|
||||||
{
|
{
|
||||||
|
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
|
||||||
u8* location = source.exitPtrs;
|
u8* location = source.exitPtrs;
|
||||||
ARM64XEmitter emit(location);
|
ARM64XEmitter emit(location);
|
||||||
|
|
||||||
WriteLinkBlock(emit, source, dest);
|
WriteLinkBlock(emit, source, dest);
|
||||||
|
|
||||||
emit.FlushIcache();
|
emit.FlushIcache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,9 +71,8 @@ void JitArm64BlockCache::WriteDestroyBlock(const JitBlock& block)
|
||||||
{
|
{
|
||||||
// Only clear the entry points as we might still be within this block.
|
// Only clear the entry points as we might still be within this block.
|
||||||
ARM64XEmitter emit(block.checkedEntry);
|
ARM64XEmitter emit(block.checkedEntry);
|
||||||
|
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
|
||||||
while (emit.GetWritableCodePtr() <= block.normalEntry)
|
while (emit.GetWritableCodePtr() <= block.normalEntry)
|
||||||
emit.BRK(0x123);
|
emit.BRK(0x123);
|
||||||
|
|
||||||
emit.FlushIcache();
|
emit.FlushIcache();
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,6 +289,7 @@ bool JitArm64::HandleFastmemFault(uintptr_t access_address, SContext* ctx)
|
||||||
if ((const u8*)ctx->CTX_PC - fault_location > fastmem_area_length)
|
if ((const u8*)ctx->CTX_PC - fault_location > fastmem_area_length)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
|
||||||
ARM64XEmitter emitter((u8*)fault_location);
|
ARM64XEmitter emitter((u8*)fault_location);
|
||||||
|
|
||||||
emitter.BL(slow_handler_iter->second.slowmem_code);
|
emitter.BL(slow_handler_iter->second.slowmem_code);
|
||||||
|
@ -300,6 +301,7 @@ bool JitArm64::HandleFastmemFault(uintptr_t access_address, SContext* ctx)
|
||||||
m_fault_to_handler.erase(slow_handler_iter);
|
m_fault_to_handler.erase(slow_handler_iter);
|
||||||
|
|
||||||
emitter.FlushIcache();
|
emitter.FlushIcache();
|
||||||
|
|
||||||
ctx->CTX_PC = reinterpret_cast<std::uintptr_t>(fault_location);
|
ctx->CTX_PC = reinterpret_cast<std::uintptr_t>(fault_location);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ using namespace Arm64Gen;
|
||||||
|
|
||||||
void JitArm64::GenerateAsm()
|
void JitArm64::GenerateAsm()
|
||||||
{
|
{
|
||||||
|
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
|
||||||
|
|
||||||
// This value is all of the callee saved registers that we are required to save.
|
// This value is all of the callee saved registers that we are required to save.
|
||||||
// According to the AACPS64 we need to save R19 ~ R30 and Q8 ~ Q15.
|
// According to the AACPS64 we need to save R19 ~ R30 and Q8 ~ Q15.
|
||||||
const u32 ALL_CALLEE_SAVED = 0x7FF80000;
|
const u32 ALL_CALLEE_SAVED = 0x7FF80000;
|
||||||
|
|
|
@ -71,9 +71,8 @@ CPUCoreBase* InitJitCore(PowerPC::CPUCore core)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
PanicAlertFmtT("The selected CPU emulation core ({0}) is not available. "
|
// Under this case the caller overrides the CPU core to the default and logs that
|
||||||
"Please select a different CPU emulation core in the settings.",
|
// it performed the override.
|
||||||
core);
|
|
||||||
g_jit = nullptr;
|
g_jit = nullptr;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -473,6 +473,8 @@ if(APPLE)
|
||||||
set_target_properties(dolphin-emu PROPERTIES
|
set_target_properties(dolphin-emu PROPERTIES
|
||||||
MACOSX_BUNDLE true
|
MACOSX_BUNDLE true
|
||||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
|
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
|
||||||
|
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/DolphinEmu.entitlements"
|
||||||
|
XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--deep --options=runtime"
|
||||||
OUTPUT_NAME Dolphin
|
OUTPUT_NAME Dolphin
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -516,6 +518,22 @@ if(APPLE)
|
||||||
POST_BUILD COMMAND
|
POST_BUILD COMMAND
|
||||||
${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/"
|
${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/"
|
||||||
$<TARGET_FILE:dolphin-emu>)
|
$<TARGET_FILE:dolphin-emu>)
|
||||||
|
|
||||||
|
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()
|
else()
|
||||||
install(TARGETS dolphin-emu RUNTIME DESTINATION ${bindir})
|
install(TARGETS dolphin-emu RUNTIME DESTINATION ${bindir})
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<!-- Needed for GameCube microphone emulation -->
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
|
<true/>
|
||||||
|
<!-- TODO: It is likely this requirement is coming from Qt, but should confirm -->
|
||||||
|
<key>com.apple.security.automation.apple-events</key>
|
||||||
|
<true/>
|
||||||
|
<!-- This is needed to use adhoc signed linked libraries -->
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -13,6 +13,8 @@ set(SOURCES
|
||||||
add_executable(MacUpdater ${SOURCES})
|
add_executable(MacUpdater ${SOURCES})
|
||||||
|
|
||||||
set(MacUpdater_NAME "Dolphin Updater")
|
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
|
set_target_properties(MacUpdater PROPERTIES
|
||||||
MACOSX_BUNDLE true
|
MACOSX_BUNDLE true
|
||||||
|
@ -53,8 +55,24 @@ foreach(sb ${STORYBOARDS})
|
||||||
|
|
||||||
add_custom_command(TARGET MacUpdater POST_BUILD
|
add_custom_command(TARGET MacUpdater POST_BUILD
|
||||||
COMMAND ${IBTOOL} --errors --warnings --notices --output-format human-readable-text
|
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}
|
${CMAKE_CURRENT_SOURCE_DIR}/${sb}
|
||||||
COMMENT "Compiling Storyboard ${sb}...")
|
COMMENT "Compiling Storyboard ${sb}...")
|
||||||
endforeach()
|
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()
|
||||||
|
|
|
@ -140,7 +140,11 @@ static std::string GetPlatformID()
|
||||||
#if defined _WIN32
|
#if defined _WIN32
|
||||||
return "win";
|
return "win";
|
||||||
#elif defined __APPLE__
|
#elif defined __APPLE__
|
||||||
|
#if defined(MACOS_UNIVERSAL_BUILD)
|
||||||
|
return "macos-universal";
|
||||||
|
#else
|
||||||
return "macos";
|
return "macos";
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
return "unknown";
|
return "unknown";
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -54,6 +54,7 @@ VertexLoaderARM64::VertexLoaderARM64(const TVtxDesc& vtx_desc, const VAT& vtx_at
|
||||||
: VertexLoaderBase(vtx_desc, vtx_att), m_float_emit(this)
|
: VertexLoaderBase(vtx_desc, vtx_att), m_float_emit(this)
|
||||||
{
|
{
|
||||||
AllocCodeSpace(4096);
|
AllocCodeSpace(4096);
|
||||||
|
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
|
||||||
ClearCodeSpace();
|
ClearCodeSpace();
|
||||||
GenerateVertexLoader();
|
GenerateVertexLoader();
|
||||||
WriteProtect();
|
WriteProtect();
|
||||||
|
|
Loading…
Reference in New Issue