mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 21:39:52 +00:00
Merge pull request #18506 from Halo-Michael/master
[iOS] Update JIT enable method from project UTM
This commit is contained in:
commit
1a66135480
316
ios/main.mm
316
ios/main.mm
@ -3,12 +3,16 @@
|
|||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import <dlfcn.h>
|
#import <dlfcn.h>
|
||||||
#import <mach/mach.h>
|
#import <mach/mach.h>
|
||||||
|
#import <mach-o/loader.h>
|
||||||
|
#import <mach-o/getsect.h>
|
||||||
#import <pthread.h>
|
#import <pthread.h>
|
||||||
|
#import <spawn.h>
|
||||||
#import <signal.h>
|
#import <signal.h>
|
||||||
#import <string>
|
#import <string>
|
||||||
#import <stdio.h>
|
#import <stdio.h>
|
||||||
#import <stdlib.h>
|
#import <stdlib.h>
|
||||||
#import <sys/syscall.h>
|
#import <sys/syscall.h>
|
||||||
|
#import <sys/utsname.h>
|
||||||
#import <AudioToolbox/AudioToolbox.h>
|
#import <AudioToolbox/AudioToolbox.h>
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
@ -22,84 +26,261 @@
|
|||||||
#include "Common/StringUtils.h"
|
#include "Common/StringUtils.h"
|
||||||
#include "Common/Profiler/Profiler.h"
|
#include "Common/Profiler/Profiler.h"
|
||||||
#include "Core/Config.h"
|
#include "Core/Config.h"
|
||||||
|
#include "Common/Log.h"
|
||||||
#include "UI/DarwinFileSystemServices.h"
|
#include "UI/DarwinFileSystemServices.h"
|
||||||
|
|
||||||
|
struct cs_blob_index {
|
||||||
|
uint32_t type;
|
||||||
|
uint32_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cs_superblob {
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t count;
|
||||||
|
struct cs_blob_index index[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cs_entitlements {
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t length;
|
||||||
|
char entitlements[];
|
||||||
|
};
|
||||||
|
|
||||||
static int (*csops)(pid_t pid, unsigned int ops, void * useraddr, size_t usersize);
|
static int (*csops)(pid_t pid, unsigned int ops, void * useraddr, size_t usersize);
|
||||||
static boolean_t (*exc_server)(mach_msg_header_t *, mach_msg_header_t *);
|
static boolean_t (*exc_server)(mach_msg_header_t *, mach_msg_header_t *);
|
||||||
static int (*ptrace)(int request, pid_t pid, caddr_t addr, int data);
|
static int (*ptrace)(int request, pid_t pid, caddr_t addr, int data);
|
||||||
|
|
||||||
#define CS_OPS_STATUS 0 /* return status */
|
#define CS_OPS_STATUS 0 /* return status */
|
||||||
|
#define CS_KILL 0x00000200 /* kill process if it becomes invalid */
|
||||||
#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */
|
#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */
|
||||||
#define PT_ATTACHEXC 14 /* attach to running process with signal exception */
|
#define PT_TRACE_ME 0 /* child declares it's being traced */
|
||||||
#define PT_DETACH 11 /* stop tracing a process */
|
#define PT_SIGEXC 12 /* signals as exceptions for current_proc */
|
||||||
#define ptrace(a, b, c, d) syscall(SYS_ptrace, a, b, c, d)
|
#define ptrace(a, b, c, d) syscall(SYS_ptrace, a, b, c, d)
|
||||||
|
|
||||||
bool get_debugged() {
|
static void *exception_handler(void *argument) {
|
||||||
int flags;
|
mach_port_t port = *(mach_port_t *)argument;
|
||||||
int rv = csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags));
|
|
||||||
if (rv==0 && flags&CS_DEBUGGED) return true;
|
|
||||||
|
|
||||||
pid_t pid = fork();
|
|
||||||
if (pid > 0) {
|
|
||||||
int st,rv,i=0;
|
|
||||||
do {
|
|
||||||
usleep(500);
|
|
||||||
rv = waitpid(pid, &st, 0);
|
|
||||||
} while (rv<0 && i++<10);
|
|
||||||
if (rv<0) fprintf(stderr, "Unable to wait for child?\n");
|
|
||||||
} else if (pid == 0) {
|
|
||||||
pid_t ppid = getppid();
|
|
||||||
int rv = ptrace(PT_ATTACHEXC, ppid, 0, 0);
|
|
||||||
if (rv) {
|
|
||||||
perror("Unable to attach to process");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
for (int i=0; i<100; i++) {
|
|
||||||
usleep(1000);
|
|
||||||
errno = 0;
|
|
||||||
rv = ptrace(PT_DETACH, ppid, 0, 0);
|
|
||||||
if (rv==0) break;
|
|
||||||
}
|
|
||||||
if (rv) {
|
|
||||||
perror("Unable to detach from process");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
exit(0);
|
|
||||||
} else {
|
|
||||||
perror("Unable to fork");
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags));
|
|
||||||
if (rv==0 && flags&CS_DEBUGGED) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
kern_return_t catch_exception_raise(mach_port_t exception_port,
|
|
||||||
mach_port_t thread,
|
|
||||||
mach_port_t task,
|
|
||||||
exception_type_t exception,
|
|
||||||
exception_data_t code,
|
|
||||||
mach_msg_type_number_t code_count) {
|
|
||||||
return KERN_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *exception_handler(void *argument) {
|
|
||||||
auto port = *reinterpret_cast<mach_port_t *>(argument);
|
|
||||||
mach_msg_server(exc_server, 2048, port, 0);
|
mach_msg_server(exc_server, 2048, port, 0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NSDictionary *parse_entitlements(const void *entitlements, size_t length) {
|
||||||
|
char *copy = (char *)malloc(length);
|
||||||
|
memcpy(copy, entitlements, length);
|
||||||
|
|
||||||
|
// strip out psychic paper entitlement hiding
|
||||||
|
if (@available(iOS 13.5, *)) {
|
||||||
|
} else {
|
||||||
|
static const char *needle = "<!---><!-->";
|
||||||
|
char *found = strnstr(copy, needle, length);
|
||||||
|
if (found) {
|
||||||
|
memset(found, ' ', strlen(needle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NSData *data = [NSData dataWithBytes:copy length:length];
|
||||||
|
free(copy);
|
||||||
|
|
||||||
|
return [NSPropertyListSerialization propertyListWithData:data
|
||||||
|
options:NSPropertyListImmutable
|
||||||
|
format:nil
|
||||||
|
error:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSDictionary *app_entitlements(void) {
|
||||||
|
// Inspired by codesign.c in Darwin sources for Security.framework
|
||||||
|
|
||||||
|
// Find our mach-o header
|
||||||
|
Dl_info dl_info;
|
||||||
|
if (dladdr((const void *)app_entitlements, &dl_info) == 0)
|
||||||
|
return nil;
|
||||||
|
if (dl_info.dli_fbase == NULL)
|
||||||
|
return nil;
|
||||||
|
char *base = (char *)dl_info.dli_fbase;
|
||||||
|
struct mach_header_64 *header = (struct mach_header_64 *)dl_info.dli_fbase;
|
||||||
|
if (header->magic != MH_MAGIC_64)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
// Simulator executables have fake entitlements in the code signature. The real entitlements can be found in an __entitlements section.
|
||||||
|
size_t entitlements_size;
|
||||||
|
uint8_t *entitlements_data = getsectiondata(header, "__TEXT", "__entitlements", &entitlements_size);
|
||||||
|
if (entitlements_data != NULL) {
|
||||||
|
NSData *data = [NSData dataWithBytesNoCopy:entitlements_data
|
||||||
|
length:entitlements_size
|
||||||
|
freeWhenDone:NO];
|
||||||
|
return [NSPropertyListSerialization propertyListWithData:data
|
||||||
|
options:NSPropertyListImmutable
|
||||||
|
format:nil
|
||||||
|
error:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the LC_CODE_SIGNATURE
|
||||||
|
struct load_command *lc = (struct load_command *) (base + sizeof(*header));
|
||||||
|
struct linkedit_data_command *cs_lc = NULL;
|
||||||
|
for (uint32_t i = 0; i < header->ncmds; i++) {
|
||||||
|
if (lc->cmd == LC_CODE_SIGNATURE) {
|
||||||
|
cs_lc = (struct linkedit_data_command *) lc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lc = (struct load_command *) ((char *) lc + lc->cmdsize);
|
||||||
|
}
|
||||||
|
if (cs_lc == NULL)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
// Read the code signature off disk, as it's apparently not loaded into memory
|
||||||
|
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:NSBundle.mainBundle.executableURL error:nil];
|
||||||
|
if (fileHandle == nil)
|
||||||
|
return nil;
|
||||||
|
[fileHandle seekToFileOffset:cs_lc->dataoff];
|
||||||
|
NSData *csData = [fileHandle readDataOfLength:cs_lc->datasize];
|
||||||
|
[fileHandle closeFile];
|
||||||
|
const struct cs_superblob *cs = (const struct cs_superblob *)csData.bytes;
|
||||||
|
if (ntohl(cs->magic) != 0xfade0cc0)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
// Find the entitlements in the code signature
|
||||||
|
for (uint32_t i = 0; i < ntohl(cs->count); i++) {
|
||||||
|
struct cs_entitlements *ents = (struct cs_entitlements *) ((char *) cs + ntohl(cs->index[i].offset));
|
||||||
|
if (ntohl(ents->magic) == 0xfade7171) {
|
||||||
|
return parse_entitlements(ents->entitlements, ntohl(ents->length) - offsetof(struct cs_entitlements, entitlements));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSDictionary *cached_app_entitlements(void) {
|
||||||
|
static NSDictionary *entitlements = nil;
|
||||||
|
if (!entitlements) {
|
||||||
|
entitlements = app_entitlements();
|
||||||
|
}
|
||||||
|
return entitlements;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jb_has_container(void) {
|
||||||
|
NSDictionary *entitlements = cached_app_entitlements();
|
||||||
|
return ![entitlements[@"com.apple.private.security.no-sandbox"] boolValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
extern const char *environ[];
|
||||||
|
|
||||||
|
static char *childArgv[] = {NULL, "debugme", NULL};
|
||||||
|
|
||||||
|
bool jb_spawn_ptrace_child(int argc, char **argv) {
|
||||||
|
int ret; pid_t pid;
|
||||||
|
|
||||||
|
if (argc > 1 && strcmp(argv[1], childArgv[1]) == 0) {
|
||||||
|
ret = ptrace(PT_TRACE_ME, 0, NULL, 0);
|
||||||
|
NSLog(@"child: ptrace(PT_TRACE_ME) %d", ret);
|
||||||
|
exit(ret);
|
||||||
|
}
|
||||||
|
if (jb_has_container()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
childArgv[0] = argv[0];
|
||||||
|
if ((ret = posix_spawnp(&pid, argv[0], NULL, NULL, (char *const *)childArgv, NULL)) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jb_has_jit_entitlement(void) {
|
||||||
|
NSDictionary *entitlements = cached_app_entitlements();
|
||||||
|
return [entitlements[@"dynamic-codesigning"] boolValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jb_has_cs_disabled(void) {
|
||||||
|
int flags;
|
||||||
|
return !csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags)) && (flags & ~CS_KILL) == flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _COMM_PAGE_START_ADDRESS (0x0000000FFFFFC000ULL) /* In TTBR0 */
|
||||||
|
#define _COMM_PAGE_APRR_SUPPORT (_COMM_PAGE_START_ADDRESS+0x10C)
|
||||||
|
|
||||||
|
static bool is_device_A12_or_newer(void) {
|
||||||
|
// devices without APRR are definitely < A12
|
||||||
|
char aprr_support = *(volatile char *)_COMM_PAGE_APRR_SUPPORT;
|
||||||
|
if (aprr_support == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// we still have A11 devices that support APRR
|
||||||
|
struct utsname systemInfo;
|
||||||
|
if (uname(&systemInfo) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// iPhone 8, 8 Plus, and iPhone X
|
||||||
|
if (strncmp("iPhone10,", systemInfo.machine, 9) == 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jb_has_cs_execseg_allow_unsigned(void) {
|
||||||
|
NSDictionary *entitlements = cached_app_entitlements();
|
||||||
|
if (@available(iOS 14.2, *)) {
|
||||||
|
if (@available(iOS 14.4, *)) {
|
||||||
|
return false; // iOS 14.4 broke it again
|
||||||
|
}
|
||||||
|
// technically we need to check the Code Directory and make sure
|
||||||
|
// CS_EXECSEG_ALLOW_UNSIGNED is set but we assume that it is properly
|
||||||
|
// signed, which should reflect the get-task-allow entitlement
|
||||||
|
return is_device_A12_or_newer() && [entitlements[@"get-task-allow"] boolValue];
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool jb_has_debugger_attached(void) {
|
||||||
|
int flags;
|
||||||
|
return !csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags)) && flags & CS_DEBUGGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jb_enable_ptrace_hack(void) {
|
||||||
|
bool debugged = jb_has_debugger_attached();
|
||||||
|
|
||||||
|
// Thanks to this comment: https://news.ycombinator.com/item?id=18431524
|
||||||
|
// We use this hack to allow mmap with PROT_EXEC (which usually requires the
|
||||||
|
// dynamic-codesigning entitlement) by tricking the process into thinking
|
||||||
|
// that Xcode is debugging it. We abuse the fact that JIT is needed to
|
||||||
|
// debug the process.
|
||||||
|
if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ptracing ourselves confuses the kernel and will cause bad things to
|
||||||
|
// happen to the system (hangs…) if an exception or signal occurs. Setup
|
||||||
|
// some "safety nets" so we can cause the process to exit in a somewhat sane
|
||||||
|
// state. We only need to do this if the debugger isn't attached. (It'll do
|
||||||
|
// this itself, and if we do it we'll interfere with its normal operation
|
||||||
|
// anyways.)
|
||||||
|
if (!debugged) {
|
||||||
|
// First, ensure that signals are delivered as Mach software exceptions…
|
||||||
|
ptrace(PT_SIGEXC, 0, NULL, 0);
|
||||||
|
|
||||||
|
// …then ensure that this exception goes through our exception handler.
|
||||||
|
// I think it's OK to just watch for EXC_SOFTWARE because the other
|
||||||
|
// exceptions (e.g. EXC_BAD_ACCESS, EXC_BAD_INSTRUCTION, and friends)
|
||||||
|
// will end up being delivered as signals anyways, and we can get them
|
||||||
|
// once they're resent as a software exception.
|
||||||
|
mach_port_t port = MACH_PORT_NULL;
|
||||||
|
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
|
||||||
|
mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
|
||||||
|
task_set_exception_ports(mach_task_self(), EXC_MASK_SOFTWARE, port, EXCEPTION_DEFAULT, THREAD_STATE_NONE);
|
||||||
|
pthread_t thread;
|
||||||
|
pthread_create(&thread, NULL, exception_handler, (void *)&port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
float g_safeInsetLeft = 0.0;
|
float g_safeInsetLeft = 0.0;
|
||||||
float g_safeInsetRight = 0.0;
|
float g_safeInsetRight = 0.0;
|
||||||
float g_safeInsetTop = 0.0;
|
float g_safeInsetTop = 0.0;
|
||||||
float g_safeInsetBottom = 0.0;
|
float g_safeInsetBottom = 0.0;
|
||||||
|
|
||||||
// We no longer need to judge if jit is usable or not by according to the ios version.
|
// We no longer need to judge if jit is usable or not by according to the ios version.
|
||||||
/*
|
static bool g_jitAvailable = true;
|
||||||
static bool g_jitAvailable = false;
|
//static int g_iosVersionMinor;
|
||||||
static int g_iosVersionMinor;
|
|
||||||
*/
|
|
||||||
static int g_iosVersionMajor;
|
static int g_iosVersionMajor;
|
||||||
static std::string version;
|
static std::string version;
|
||||||
|
|
||||||
@ -169,7 +350,7 @@ bool System_GetPropertyBool(SystemProperty prop) {
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
case SYSPROP_CAN_JIT:
|
case SYSPROP_CAN_JIT:
|
||||||
return get_debugged();
|
return g_jitAvailable;
|
||||||
#ifndef HTTPS_NOT_AVAILABLE
|
#ifndef HTTPS_NOT_AVAILABLE
|
||||||
case SYSPROP_SUPPORTS_HTTPS:
|
case SYSPROP_SUPPORTS_HTTPS:
|
||||||
return true;
|
return true;
|
||||||
@ -309,9 +490,22 @@ int main(int argc, char *argv[])
|
|||||||
g_iosVersionMajor = 14;
|
g_iosVersionMajor = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (jb_spawn_ptrace_child(argc, argv)) {
|
||||||
g_jitAvailable = get_debugged();
|
INFO_LOG(SYSTEM, "JIT: ptrace() child spawn trick\n");
|
||||||
|
} else if (jb_has_jit_entitlement()) {
|
||||||
|
INFO_LOG(SYSTEM, "JIT: found entitlement\n");
|
||||||
|
} else if (jb_has_cs_disabled()) {
|
||||||
|
INFO_LOG(SYSTEM, "JIT: CS_KILL disabled\n");
|
||||||
|
} else if (jb_has_cs_execseg_allow_unsigned()) {
|
||||||
|
INFO_LOG(SYSTEM, "JIT: CS_EXECSEG_ALLOW_UNSIGNED set\n");
|
||||||
|
} else if (jb_enable_ptrace_hack()) {
|
||||||
|
INFO_LOG(SYSTEM, "JIT: ptrace() hack supported\n");
|
||||||
|
} else {
|
||||||
|
INFO_LOG(SYSTEM, "JIT: ptrace() hack failed\n");
|
||||||
|
g_jitAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
if (g_iosVersionMajor > 14 || (g_iosVersionMajor == 14 && g_iosVersionMinor >= 4)) {
|
if (g_iosVersionMajor > 14 || (g_iosVersionMajor == 14 && g_iosVersionMinor >= 4)) {
|
||||||
g_jitAvailable = false;
|
g_jitAvailable = false;
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user