├── ArmRuntime ├── CMakeLists.txt ├── Collections.mm ├── NSLog.mm ├── StringFormat.mm ├── asl.mm ├── generateReplacements.py ├── memory.cpp ├── printf.h ├── printf.mm ├── scanf.cpp └── swiftStdlib.cpp ├── CMakeLists.txt ├── Converter ├── CMakeLists.txt ├── macho.cpp ├── macho.h ├── main.cpp ├── simenv.cpp └── simenv.h ├── Debugger ├── .lldbinit ├── commands.py └── debug.sh ├── HeaderParser ├── CMakeLists.txt └── declparse.cpp ├── LICENSE.txt ├── Runtime ├── CMakeLists.txt ├── cpu.cpp ├── cpu.h ├── exceptionHandlers.h ├── exceptionHandlers.mm ├── fmt │ ├── chrono.h │ ├── color.h │ ├── compile.h │ ├── core.h │ ├── format-inl.h │ ├── format.h │ ├── locale.h │ ├── os.h │ ├── ostream.h │ ├── posix.h │ ├── printf.h │ └── ranges.h ├── format.cc ├── gs.h ├── jmpWrappers.cpp ├── jmpWrappers.h ├── keychainTester.mm ├── kickoff.cpp ├── logging.h ├── logging.mm ├── objcWrappers.h ├── objcWrappers.mm ├── objcppWrappers.h ├── objcppWrappers.mm ├── os.cc ├── record.h ├── replacements.generated.h ├── repr.h ├── swiftDemangler.cpp ├── swiftDemangler.h ├── trampoliner.cpp ├── trampoliner.h ├── wrappers.cpp └── wrappers.h ├── SwiftTrampolineTests.app ├── Base.lproj │ ├── LaunchScreen.storyboardc │ │ ├── 01J-lp-oVM-view-Ze5-6b-2t3.nib │ │ ├── Info.plist │ │ └── UIViewController-01J-lp-oVM.nib │ └── Main.storyboardc │ │ ├── BYZ-38-t0r-view-8bC-Xf-vdC.nib │ │ ├── Info.plist │ │ └── UIViewController-BYZ-38-t0r.nib ├── Frameworks │ └── NativeSide.framework │ │ ├── Info.plist │ │ ├── NativeSide │ │ └── _CodeSignature │ │ └── CodeResources ├── GSWrapper ├── Info.plist ├── PkgInfo ├── SwiftTrampolineTests ├── SwiftTrampolineTests.dylib ├── _CodeSignature │ └── CodeResources ├── embedded.mobileprovision ├── gsinit.bin ├── libarmruntime.dylib ├── libc--.1.dylib └── libc--abi.1.dylib ├── TrampolineTests.app ├── Base.lproj │ ├── LaunchScreen.storyboardc │ │ ├── 01J-lp-oVM-view-Ze5-6b-2t3.nib │ │ │ ├── objects-13.0+.nib │ │ │ └── runtime.nib │ │ ├── Info.plist │ │ └── UIViewController-01J-lp-oVM.nib │ │ │ ├── objects-13.0+.nib │ │ │ └── runtime.nib │ └── Main.storyboardc │ │ ├── BYZ-38-t0r-view-8bC-Xf-vdC.nib │ │ ├── objects-13.0+.nib │ │ └── runtime.nib │ │ ├── Info.plist │ │ └── UIViewController-BYZ-38-t0r.nib │ │ ├── objects-13.0+.nib │ │ └── runtime.nib ├── Frameworks │ └── NativeSide.framework │ │ ├── Info.plist │ │ ├── NativeSide │ │ └── _CodeSignature │ │ └── CodeResources ├── GSWrapper ├── Info.plist ├── PkgInfo ├── TrampolineTests ├── TrampolineTests.dylib ├── _CodeSignature │ └── CodeResources ├── embedded.mobileprovision ├── gsinit.bin ├── libarmruntime.dylib ├── libc--.1.dylib └── libc--abi.1.dylib ├── buildSwiftTrampolineTests.sh ├── buildSwiftdb.py ├── buildTrampolineTests.sh ├── cleanFuncdb.py ├── convertAll.sh ├── convertOne.sh ├── dumpGsInit.py ├── dylibify.py ├── funcdb ├── funcdb2 ├── generateFuncdb.py ├── init.h ├── libarmruntime.dylib ├── libc--.1.dylib ├── libc--abi.1.dylib ├── libemuruntime.dylib ├── libunicorn.a ├── wrap.py └── wrapTemplate.mm /ArmRuntime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(ArmRuntime) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 6 | 7 | set(CMAKE_OSX_SYSROOT "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk") 8 | set(CMAKE_CXX_FLAGS "-Wno-deprecated-declarations -mios-simulator-version-min=10.0") 9 | set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0" CACHE STRING "Non-empty for iOS" FORCE) 10 | set(CMAKE_OSX_ARCHITECTURES "arm64") 11 | link_libraries("-framework Foundation" "-framework CoreFoundation") 12 | 13 | add_library(armruntime SHARED printf.mm scanf.cpp NSLog.mm StringFormat.mm asl.mm Collections.mm memory.cpp swiftStdlib.cpp) 14 | add_custom_command(TARGET armruntime POST_BUILD 15 | COMMAND ${CMAKE_COMMAND} -E copy $ ../) 16 | -------------------------------------------------------------------------------- /ArmRuntime/Collections.mm: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #import 4 | #import "printf.h" 5 | #include 6 | using namespace std; 7 | 8 | bool inheritsFrom(Class tcls, Class scls) { 9 | while(true) { 10 | if(tcls == scls) 11 | return true; 12 | auto parent = class_getSuperclass(tcls); 13 | if(parent == tcls) 14 | return false; 15 | tcls = parent; 16 | } 17 | } 18 | 19 | id NSMutableSet_initWithObjects_(NSMutableSet* set, id firstObj, va_list ap) { 20 | vector objects; 21 | if(firstObj != nil) { 22 | objects.push_back(firstObj); 23 | while(true) { 24 | id nobj = va_arg(ap, id); 25 | if(nobj == nil) 26 | break; 27 | objects.push_back(nobj); 28 | } 29 | } 30 | 31 | id* objarr = new id[objects.size()]; 32 | copy(objects.begin(), objects.end(), objarr); 33 | set = [set initWithObjects: objarr count: objects.size()]; 34 | delete[] objarr; 35 | return set; 36 | } 37 | 38 | id NSMutableArray_initWithObjects_(NSMutableArray* array, id firstObj, va_list ap) { 39 | vector objects; 40 | if(firstObj != nil) { 41 | objects.push_back(firstObj); 42 | while(true) { 43 | id nobj = va_arg(ap, id); 44 | if(nobj == nil) 45 | break; 46 | objects.push_back(nobj); 47 | } 48 | } 49 | 50 | id* objarr = new id[objects.size()]; 51 | copy(objects.begin(), objects.end(), objarr); 52 | array = [array initWithObjects: objarr count: objects.size()]; 53 | delete[] objarr; 54 | return array; 55 | } 56 | 57 | extern "C" { 58 | 59 | /// REPLACE arrayWithObjects: 60 | __attribute__((visibility("default"))) 61 | id replace_arrayWithObjects_(id self, SEL _cmd, id firstObj, ...) { 62 | va_list ap; 63 | va_start(ap, firstObj); 64 | vector objects; 65 | if(firstObj != nil) { 66 | objects.push_back(firstObj); 67 | while(true) { 68 | id nobj = va_arg(ap, id); 69 | if(nobj == nil) 70 | break; 71 | objects.push_back(nobj); 72 | } 73 | } 74 | va_end(ap); 75 | id* objarr = new id[objects.size()]; 76 | copy(objects.begin(), objects.end(), objarr); 77 | id ret = [self arrayWithObjects:objarr count:objects.size()]; 78 | delete[] objarr; 79 | return ret; 80 | } 81 | 82 | /// REPLACE setWithObjects: 83 | __attribute__((visibility("default"))) 84 | id replace_setWithObjects_(id self, SEL _cmd, id firstObj, ...) { 85 | va_list ap; 86 | va_start(ap, firstObj); 87 | vector objects; 88 | if(firstObj != nil) { 89 | objects.push_back(firstObj); 90 | while(true) { 91 | id nobj = va_arg(ap, id); 92 | if(nobj == nil) 93 | break; 94 | objects.push_back(nobj); 95 | } 96 | } 97 | va_end(ap); 98 | id* objarr = new id[objects.size()]; 99 | copy(objects.begin(), objects.end(), objarr); 100 | id ret = [self setWithObjects:objarr count:objects.size()]; 101 | delete[] objarr; 102 | return ret; 103 | } 104 | 105 | /// REPLACE dictionaryWithObjectsAndKeys: 106 | __attribute__((visibility("default"))) 107 | id replace_dictionaryWithObjectsAndKeys_(id self, SEL _cmd, id firstObj, ...) { 108 | va_list ap; 109 | va_start(ap, firstObj); 110 | vector objects, keys; 111 | if(firstObj != nil) { 112 | objects.push_back(firstObj); 113 | keys.push_back(va_arg(ap, id)); 114 | while(true) { 115 | id nobj = va_arg(ap, id); 116 | if(nobj == nil) 117 | break; 118 | objects.push_back(nobj); 119 | keys.push_back(va_arg(ap, id)); 120 | } 121 | } 122 | va_end(ap); 123 | id* objarr = new id[objects.size()]; 124 | copy(objects.begin(), objects.end(), objarr); 125 | id* keyarr = new id[keys.size()]; 126 | copy(keys.begin(), keys.end(), keyarr); 127 | id ret = [self dictionaryWithObjects: objarr forKeys: keyarr count: objects.size()]; 128 | delete[] objarr; 129 | delete[] keyarr; 130 | return ret; 131 | } 132 | 133 | /// REPLACE initWithObjectsAndKeys: 134 | __attribute__((visibility("default"))) 135 | id replace_initWithObjectsAndKeys_(id self, SEL _cmd, id firstObj, ...) { 136 | va_list ap; 137 | va_start(ap, firstObj); 138 | vector objects, keys; 139 | if(firstObj != nil) { 140 | objects.push_back(firstObj); 141 | keys.push_back(va_arg(ap, id)); 142 | while(true) { 143 | id nobj = va_arg(ap, id); 144 | if(nobj == nil) 145 | break; 146 | objects.push_back(nobj); 147 | keys.push_back(va_arg(ap, id)); 148 | } 149 | } 150 | va_end(ap); 151 | id* objarr = new id[objects.size()]; 152 | copy(objects.begin(), objects.end(), objarr); 153 | id* keyarr = new id[keys.size()]; 154 | copy(keys.begin(), keys.end(), keyarr); 155 | id ret = [self initWithObjects: objarr forKeys: keyarr count: objects.size()]; 156 | delete[] objarr; 157 | delete[] keyarr; 158 | return ret; 159 | } 160 | 161 | /// REPLACE initWithObjects: 162 | __attribute__((visibility("default"))) 163 | id replace_initWithObjects_(id self, SEL _cmd, id firstObj, ...) { 164 | Class cls = object_getClass(self); 165 | va_list ap; 166 | va_start(ap, firstObj); 167 | if(inheritsFrom(cls, [NSMutableSet class])) { 168 | id ret = NSMutableSet_initWithObjects_((NSMutableSet*) self, firstObj, ap); 169 | va_end(ap); 170 | return ret; 171 | } else if(inheritsFrom(cls, [NSMutableArray class])) { 172 | id ret = NSMutableArray_initWithObjects_((NSMutableArray*) self, firstObj, ap); 173 | va_end(ap); 174 | return ret; 175 | } else { 176 | exit(1); 177 | } 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /ArmRuntime/NSLog.mm: -------------------------------------------------------------------------------- 1 | #include 2 | #include "printf.h" 3 | 4 | void log(const char* format, ...); 5 | 6 | extern "C" { 7 | 8 | /// REPLACE NSLogv 9 | __attribute__((visibility("default"))) 10 | void replace_NSLogv(NSString* format, va_list args) { 11 | log("NSLogv! '%@'", format); 12 | vprintf_objc([format UTF8String], args); 13 | printf_objc("\n"); 14 | } 15 | 16 | /// REPLACE NSLog 17 | __attribute__((visibility("default"))) 18 | void replace_NSLog(NSString* format, ...) { 19 | log("NSLog! '%@'", format); 20 | va_list ap; 21 | va_start(ap, format); 22 | replace_NSLogv(format, ap); 23 | va_end(ap); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /ArmRuntime/StringFormat.mm: -------------------------------------------------------------------------------- 1 | #import 2 | #include "printf.h" 3 | 4 | #include 5 | using namespace std; 6 | 7 | void lambdaCaller(char character, void* arg) { 8 | auto lambda = (function*) arg; 9 | (*lambda)(character); 10 | } 11 | 12 | string formatString(const char* format, va_list argList) { 13 | string ret; 14 | function lambda = [&](auto c) { 15 | ret += c; 16 | }; 17 | fctprintfv(lambdaCaller, &lambda, true, format, argList); 18 | return ret; 19 | } 20 | 21 | static auto bdLogCore = (void(*)(const char*)) 0xDEADBEEFCAFEBAB0; 22 | 23 | void log(const char* format, ...) { 24 | va_list argList; 25 | va_start(argList, format); 26 | bdLogCore(formatString(format, argList).c_str()); 27 | va_end(argList); 28 | } 29 | 30 | extern "C" { 31 | 32 | /// REPLACE initWithFormat: 33 | __attribute__((visibility("default"))) 34 | NSString* replace_initWithFormat_(id self, SEL _cmd, NSString* format, ...) { 35 | //log("Got call to initWithFormat:! '%@'", format); 36 | va_list argList; 37 | va_start(argList, format); 38 | auto str = formatString([format UTF8String], argList); 39 | va_end(argList); 40 | return [self initWithUTF8String: str.c_str()]; 41 | } 42 | 43 | /// REPLACE initWithFormat:arguments: 44 | __attribute__((visibility("default"))) 45 | NSString* replace_initWithFormat_arguments_(id self, SEL _cmd, NSString* format, va_list argList) { 46 | //log("Got call to initWithFormat:arguments:! '%@'", format); 47 | auto str = formatString([format UTF8String], argList); 48 | return [self initWithUTF8String:str.c_str()]; 49 | } 50 | 51 | /// REPLACE deferredLocalizedIntentsStringWithFormat: 52 | __attribute__((visibility("default"))) 53 | NSString* replace_deferredLocalizedIntentsStringWithFormat_(id self, SEL _cmd, NSString* format, ...) { 54 | //log("Got call to deferredLocalizedIntentsStringWithFormat:! '%@'", format); 55 | va_list argList; 56 | va_start(argList, format); 57 | auto str = formatString([format UTF8String], argList); 58 | va_end(argList); 59 | return [NSString stringWithUTF8String:str.c_str()]; 60 | } 61 | 62 | /// REPLACE initWithFormat:locale: 63 | __attribute__((visibility("default"))) 64 | NSString* replace_initWithFormat_locale_(id self, SEL _cmd, NSString* format, id locale, ...) { 65 | //log("Got call to initWithFormat:locale:! '%@'", format); 66 | va_list argList; 67 | va_start(argList, locale); 68 | auto str = formatString([format UTF8String], argList); 69 | va_end(argList); 70 | return [self initWithUTF8String:str.c_str()]; 71 | } 72 | 73 | /// REPLACE initWithFormat:locale:arguments: 74 | __attribute__((visibility("default"))) 75 | NSString* replace_initWithFormat_locale_arguments_(id self, SEL _cmd, NSString* format, id locale, va_list argList) { 76 | //log("Got call to initWithFormat:locale:arguments:! '%@'", format); 77 | auto str = formatString([format UTF8String], argList); 78 | return [self initWithUTF8String:str.c_str()]; 79 | } 80 | 81 | /// REPLACE stringByAppendingFormat: 82 | __attribute__((visibility("default"))) 83 | NSString* replace_stringByAppendingFormat_(id self, SEL _cmd, NSString* format, ...) { 84 | //log("Got call to stringByAppendingFormat:! '%@'", format); 85 | va_list argList; 86 | va_start(argList, format); 87 | auto str = formatString([format UTF8String], argList); 88 | auto nstr = [[NSString stringWithUTF8String:str.c_str()] retain]; 89 | va_end(argList); 90 | auto ret = [self stringByAppendingString:nstr]; 91 | [nstr release]; 92 | return ret; 93 | } 94 | 95 | /// REPLACE appendFormat: 96 | __attribute__((visibility("default"))) 97 | id replace_appendFormat_(id self, SEL _cmd, NSString* format, ...) { 98 | //log("Got call to appendFormat:! '%@'", format); 99 | va_list argList; 100 | va_start(argList, format); 101 | auto str = formatString([format UTF8String], argList); 102 | //printf_objc("Formatted string to append: '%s' (%i bytes)\n", str, strlen(str)); 103 | auto nstr = [[[NSString alloc] initWithBytes:str.c_str() length:str.length() encoding:NSASCIIStringEncoding] retain]; 104 | va_end(argList); 105 | //printf_objc("NSString: '%@'\n", nstr); 106 | [self appendString:nstr]; 107 | [nstr release]; 108 | return self; 109 | } 110 | 111 | /// REPLACE stringWithFormat: 112 | __attribute__((visibility("default"))) 113 | NSString* replace_stringWithFormat_(id self, SEL _cmd, NSString* format, ...) { 114 | //log("Got call to stringWithFormat:! '%@'", format); 115 | va_list argList; 116 | va_start(argList, format); 117 | auto str = formatString([format UTF8String], argList); 118 | id nstr = [self stringWithUTF8String:str.c_str()]; 119 | va_end(argList); 120 | return nstr; 121 | } 122 | 123 | /// REPLACE CFStringCreateWithFormatAndArguments 124 | __attribute__((visibility("default"))) 125 | CFStringRef replace_CFStringCreateWithFormatAndArguments(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, va_list arguments) { 126 | auto flen = CFStringGetLength(format); 127 | auto buf = new char[flen + 1]; 128 | CFStringGetCString(format, buf, flen + 1, kCFStringEncodingUTF8); 129 | //log("Got call to CFStringCreateWithFormatAndArguments! '%s'", buf); 130 | auto str = formatString(buf, arguments); 131 | return CFStringCreateWithBytes(alloc, (const UInt8*) str.c_str(), str.length(), kCFStringEncodingUTF8, false); 132 | } 133 | 134 | /// REPLACE predicateWithFormat:arguments: 135 | __attribute__((visibility("default"))) 136 | NSPredicate* replace_predicateWithFormat_arguments_(id self, SEL _cmd, NSString* format, va_list args) { 137 | auto arr = [[NSMutableArray alloc] init]; 138 | auto ptr = [format UTF8String]; 139 | while(*ptr != 0) { 140 | char c = *ptr++; 141 | if(c == '%') { 142 | c = *ptr; 143 | switch(c) { 144 | case '%': 145 | ptr++; 146 | break; 147 | 148 | case 'K': 149 | case '@': 150 | ptr++; 151 | [arr addObject:va_arg(args, id)]; 152 | break; 153 | 154 | case 'c': 155 | ptr++; 156 | [arr addObject:[NSNumber numberWithChar: (char) va_arg(args, NSInteger)]]; 157 | break; 158 | 159 | case 'C': 160 | ptr++; 161 | [arr addObject:[NSNumber numberWithShort: (short) va_arg(args, NSInteger)]]; 162 | break; 163 | 164 | case 'l': 165 | ptr++; 166 | if(*ptr == 'l') { 167 | switch(*++ptr) { 168 | case 'd': 169 | [arr addObject:[NSNumber numberWithLongLong: va_arg(args, int64_t)]]; 170 | break; 171 | } 172 | } else 173 | switch(*ptr++) { 174 | case 'd': 175 | [arr addObject:[NSNumber numberWithInt: va_arg(args, int)]]; 176 | break; 177 | } 178 | break; 179 | 180 | case 'd': 181 | case 'D': 182 | case 'i': 183 | ptr++; 184 | [arr addObject:[NSNumber numberWithInt: va_arg(args, int)]]; 185 | break; 186 | 187 | case 'o': 188 | case 'O': 189 | case 'u': 190 | case 'U': 191 | case 'x': 192 | case 'X': 193 | ptr++; 194 | [arr addObject:[NSNumber numberWithUnsignedInt: va_arg(args, unsigned)]]; 195 | break; 196 | 197 | case 'e': 198 | case 'E': 199 | case 'f': 200 | case 'g': 201 | case 'G': 202 | ptr++; 203 | [arr addObject:[NSNumber numberWithDouble: va_arg(args, double)]]; 204 | break; 205 | 206 | case 'h': 207 | ptr++; 208 | if(*ptr != 0) { 209 | c = *ptr; 210 | if(c == 'i') 211 | [arr addObject:[NSNumber numberWithShort: (short) va_arg(args, NSInteger)]]; 212 | if(c == 'u') 213 | [arr addObject:[NSNumber numberWithUnsignedShort: (unsigned short) va_arg(args, NSInteger)]]; 214 | } 215 | break; 216 | 217 | case 'q': 218 | ptr++; 219 | if(*ptr != 0) { 220 | c = *ptr; 221 | if(c == 'i') 222 | [arr addObject:[NSNumber numberWithLongLong: va_arg(args, long long)]]; 223 | if(c == 'u' || c == 'x' || c == 'X') 224 | [arr addObject:[NSNumber numberWithUnsignedLongLong: va_arg(args, unsigned long long)]]; 225 | } 226 | break; 227 | } 228 | } else if(c == '\'') { 229 | while(*ptr != 0) 230 | if(*ptr++ == '\'') 231 | break; 232 | } else if(c == '"') { 233 | while(*ptr != 0) 234 | if(*ptr++ == '"') 235 | break; 236 | } 237 | } 238 | //printf_objc("predicateWithFormat '%@' args %i\n", format, [arr count]); 239 | return [NSPredicate predicateWithFormat: format argumentArray: arr]; 240 | } 241 | 242 | /// REPLACE predicateWithFormat: 243 | __attribute__((visibility("default"))) 244 | NSPredicate* replace_predicateWithFormat_(id self, SEL _cmd, NSString* format, ...) { 245 | va_list args; 246 | va_start(args, format); 247 | auto p = replace_predicateWithFormat_arguments_(self, _cmd, format, args); 248 | va_end(args); 249 | return p; 250 | } 251 | 252 | } 253 | -------------------------------------------------------------------------------- /ArmRuntime/asl.mm: -------------------------------------------------------------------------------- 1 | #include 2 | #include "printf.h" 3 | #include 4 | 5 | extern "C" { 6 | 7 | /// REPLACE asl_log 8 | __attribute__((visibility("default"))) 9 | int replace_asl_log(aslclient asl, aslmsg msg, int level, const char* format, ...) { 10 | printf_objc("Got call to asl_log\n"); 11 | va_list ap; 12 | va_start(ap, format); 13 | vprintf_objc(format, ap); 14 | va_end(ap); 15 | printf_objc("\n----asl_log done\n"); 16 | return 0; 17 | } 18 | 19 | /// REPLACE asl_vlog 20 | __attribute__((visibility("default"))) 21 | int replace_asl_vlog(aslclient asl, aslmsg msg, int level, const char* format, va_list ap) { 22 | printf_objc("Got call to asl_vlog\n"); 23 | vprintf_objc(format, ap); 24 | printf_objc("\n----asl_vlog done\n"); 25 | return 0; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ArmRuntime/generateReplacements.py: -------------------------------------------------------------------------------- 1 | import glob 2 | 3 | with file('../Runtime/replacements.generated.h', 'w') as fp: 4 | nfuncs = [] 5 | sels = [] 6 | for fn in glob.glob('*.cpp') + glob.glob('*.m') + glob.glob('*.mm') + glob.glob('*.h'): 7 | source = file(fn, 'r').read().split('\n') 8 | for line in source: 9 | line = line.strip() 10 | if not line.startswith('/// REPLACE'): 11 | continue 12 | args = line[11:].strip().split(' ') 13 | if len(args) == 1: 14 | if ':' not in args[0]: 15 | nfuncs.append(args[0]) 16 | else: 17 | sels.append((args[0], args[0].replace(':', '_'))) 18 | else: 19 | print 'Unhandled replacement:', args 20 | print >>fp, 'vector> armReplacements = {' 21 | for func in nfuncs: 22 | print >>fp, '{ "_%s", "replace_%s" }, ' % (func, func) 23 | print >>fp, '};' 24 | print >>fp, 'vector> armSelReplacements = {' 25 | for sel, func in sels: 26 | print >>fp, '{ "%s", "replace_%s" }, ' % (sel, func) 27 | print >>fp, '};' 28 | -------------------------------------------------------------------------------- /ArmRuntime/memory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" { 5 | 6 | /// REPLACE memcpy 7 | __attribute__((visibility("default"))) 8 | uint8_t* replace_memcpy(uint8_t* dest, const uint8_t* src, uint64_t n) { 9 | for(auto i = 0; i < n; ++i) 10 | dest[i] = src[i]; 11 | return dest; 12 | } 13 | 14 | /// REPLACE memset 15 | __attribute__((visibility("default"))) 16 | uint8_t* replace_memset(uint8_t* dest, int value, size_t n) { 17 | for(auto i = 0; i < n; ++i) 18 | dest[i] = (uint8_t) value; 19 | return dest; 20 | } 21 | 22 | /// REPLACE memmove 23 | __attribute__((visibility("default"))) 24 | uint8_t* replace_memmove(uint8_t* dest, const uint8_t* src, uint64_t n) { 25 | if(n == 0) 26 | return dest; 27 | if(dest + n < src || src + n < dest) 28 | replace_memcpy(dest, src, n); 29 | else if(n > 16384) { 30 | auto buf = (uint8_t*) malloc(n); 31 | replace_memcpy(buf, src, n); 32 | replace_memcpy(dest, buf, n); 33 | free(buf); 34 | } else { 35 | uint8_t buf[n]; 36 | replace_memcpy(buf, src, n); 37 | replace_memcpy(dest, buf, n); 38 | } 39 | return dest; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /ArmRuntime/printf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern "C" { 4 | int printf_objc(const char* format, ...); 5 | int vprintf_objc(const char* format, va_list va); 6 | int fctprintf(void (* out)(char character, void* arg), void* arg, bool isObjC, const char* format, ...); 7 | int fctprintfv(void (* out)(char character, void* arg), void* arg, bool isObjC, const char* format, va_list argList); 8 | } 9 | -------------------------------------------------------------------------------- /ArmRuntime/scanf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | ////// REPLACE scanf 6 | __attribute__((visibility("default"))) 7 | int replace_scanf(const char* format, ...) { 8 | 9 | } 10 | 11 | ////// REPLACE fscanf 12 | __attribute__((visibility("default"))) 13 | int replace_fscanf(FILE* stream, const char* format, ...) { 14 | 15 | } 16 | 17 | ////// REPLACE sscanf 18 | __attribute__((visibility("default"))) 19 | int replace_sscanf(const char* s, const char* format, ...) { 20 | 21 | } 22 | */ -------------------------------------------------------------------------------- /ArmRuntime/swiftStdlib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" { 4 | 5 | typedef struct { 6 | uint64_t major, minor, patch; 7 | } _SwiftNSOperatingSystemVersion; 8 | 9 | /// REPLACE _swift_stdlib_operatingSystemVersion 10 | __attribute__((visibility("default"))) 11 | _SwiftNSOperatingSystemVersion replace__swift_stdlib_operatingSystemVersion() { 12 | return {13, 3, 0}; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(GrinningSoul) 3 | 4 | add_subdirectory("${PROJECT_SOURCE_DIR}/Converter" "${PROJECT_SOURCE_DIR}/converter_output") 5 | add_subdirectory("${PROJECT_SOURCE_DIR}/HeaderParser" "${PROJECT_SOURCE_DIR}/headerparser_output") 6 | add_subdirectory("${PROJECT_SOURCE_DIR}/ArmRuntime" "${PROJECT_SOURCE_DIR}/armruntime_output") 7 | add_subdirectory("${PROJECT_SOURCE_DIR}/Runtime" "${PROJECT_SOURCE_DIR}/runtime_output") 8 | 9 | -------------------------------------------------------------------------------- /Converter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(Converter) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | set(Boost_USE_STATIC_LIBS ON) 6 | set(Boost_USE_MULTITHREADED ON) 7 | find_package(Boost 1.72.0 COMPONENTS filesystem) 8 | include_directories(${Boost_INCLUDE_DIRS}) 9 | 10 | include_directories(/usr/local/include) 11 | link_directories(/usr/local/lib) 12 | link_libraries(clang Boost::filesystem) 13 | 14 | add_executable(converter main.cpp simenv.cpp simenv.h macho.cpp macho.h) 15 | -------------------------------------------------------------------------------- /Converter/macho.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | class MachO { 10 | public: 11 | MachO(const char* fn); 12 | void bind(string* dylibs, uint8_t* data, uint32_t size); 13 | void rebase(uint8_t* data, uint32_t size); 14 | void rebaseAt(uint64_t address, int type); 15 | void doExport(uint8_t* data, uint32_t size); 16 | uint64_t* segment_offsets; 17 | uint64_t base_address, main; 18 | 19 | vector relocations, objcClasses; 20 | vector> imports; 21 | vector> exports; 22 | unordered_map>, bool, void*>> segments; // offset, vm size, file size, section addresses+sizes, writable, data 23 | }; 24 | -------------------------------------------------------------------------------- /Converter/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "simenv.h" 3 | #include "macho.h" 4 | 5 | uint32_t addString(string str, uint8_t** stringTable, uint32_t* stringTableSize) { 6 | auto data = str.c_str(); 7 | auto size = (int) str.length() + 1; 8 | for(auto i = 0; i < (int) (*stringTableSize) - size + 1; ++i) 9 | if(memcmp(*stringTable + i, data, size) == 0) 10 | return i; 11 | *stringTable = (uint8_t*) realloc(*stringTable, *stringTableSize + size); 12 | memcpy(*stringTable + *stringTableSize, data, size); 13 | auto off = *stringTableSize; 14 | *stringTableSize += size; 15 | return off; 16 | } 17 | 18 | int main(int argc, char** argv) { 19 | if(argc < 2) { 20 | printf("Usage: %s \n", argv[0]); 21 | return 1; 22 | } 23 | 24 | MachO macho { argv[1] }; 25 | printf("base:%llx\n", macho.base_address); 26 | printf("entrypoint:%llx\n", macho.main); 27 | for(auto addr : macho.objcClasses) 28 | printf("objcClass:%llx\n", addr); 29 | for(auto& [addr, dylib, name] : macho.imports) 30 | printf("import:%llx;%s;%s\n", addr, dylib.c_str(), name.c_str()); 31 | for(auto& [addr, name] : macho.exports) 32 | printf("export:%llx;%s\n", addr, name.c_str()); 33 | 34 | for(auto& [segname, tup] : macho.segments) { 35 | printf("segment:%s\n", segname.c_str()); 36 | auto& [offset, vmSize, fileSize, sectionAddresses, writable, data] = tup; 37 | for(auto& [sectname, stup] : sectionAddresses) { 38 | auto& [addr, size] = stup; 39 | printf("section:%s;%llx;%llx\n", sectname.c_str(), addr, size); 40 | } 41 | } 42 | 43 | return 0; 44 | } -------------------------------------------------------------------------------- /Converter/simenv.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Cody Brocious on 2/12/20. 3 | // 4 | 5 | #define SIMDEF 6 | #include "simenv.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | std::string exec(const char* cmd) { 16 | std::array buffer{}; 17 | std::string result; 18 | std::unique_ptr pipe(popen(cmd, "r"), pclose); 19 | if (!pipe) { 20 | throw std::runtime_error("popen() failed!"); 21 | } 22 | while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { 23 | result += buffer.data(); 24 | } 25 | return result; 26 | } 27 | 28 | SimEnv::SimEnv() { 29 | auto path = exec("xcodebuild -version -sdk iphonesimulator Path"); 30 | boost::algorithm::trim(path); 31 | SdkPath = strdup(path.c_str()); 32 | RuntimeRoot = "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot"; 33 | } 34 | -------------------------------------------------------------------------------- /Converter/simenv.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Cody Brocious on 2/12/20. 3 | // 4 | 5 | #ifndef CONVERTER_SIMENV_H 6 | #define CONVERTER_SIMENV_H 7 | 8 | #include 9 | 10 | std::string exec(const char* cmd); 11 | 12 | class SimEnv { 13 | public: 14 | const char *SdkPath, *RuntimeRoot; 15 | SimEnv(); 16 | }; 17 | 18 | #ifdef SIMDEF 19 | SimEnv* SimEnv = new class SimEnv(); 20 | #else 21 | extern SimEnv* SimEnv; 22 | #endif 23 | 24 | #endif //CONVERTER_SIMENV_H 25 | -------------------------------------------------------------------------------- /Debugger/.lldbinit: -------------------------------------------------------------------------------- 1 | command script import commands.py 2 | -------------------------------------------------------------------------------- /Debugger/commands.py: -------------------------------------------------------------------------------- 1 | import lldb 2 | 3 | def __lldb_init_module(debugger, internal_dict): 4 | for name in 'armdisassemble adisassemble adis adi'.split(' '): 5 | debugger.HandleCommand('command script add -f commands.armDis %s' % name) 6 | 7 | def armDis(debugger, command, result, internal_dict): 8 | debugger.HandleCommand('disassemble -A arm64 ' + command) 9 | -------------------------------------------------------------------------------- /Debugger/debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | lldb --local-lldbinit 4 | -------------------------------------------------------------------------------- /HeaderParser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(HeaderParse) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | set(Boost_USE_STATIC_LIBS ON) 6 | set(Boost_USE_MULTITHREADED ON) 7 | find_package(Boost 1.72.0 COMPONENTS filesystem) 8 | include_directories(${Boost_INCLUDE_DIRS}) 9 | 10 | include_directories(/usr/local/include) 11 | link_directories(/usr/local/lib) 12 | link_libraries(clang Boost::filesystem) 13 | 14 | add_executable(headerparse declparse.cpp) 15 | -------------------------------------------------------------------------------- /HeaderParser/declparse.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | namespace fs = ::boost::filesystem; 9 | 10 | ostream& operator<<(ostream& stream, const CXString& str) { 11 | stream << clang_getCString(str); 12 | clang_disposeString(str); 13 | return stream; 14 | } 15 | 16 | string* curRecordType = nullptr; 17 | 18 | string BuildType(CXType type, bool noRecord = false, bool inRecord = false) { 19 | auto cursor = clang_getTypeDeclaration(type); 20 | switch(type.kind) { 21 | case CXType_FunctionProto: { 22 | //auto fptr = (boost::format(" ../) 52 | -------------------------------------------------------------------------------- /Runtime/cpu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef USE_UNICORN 4 | #include 5 | #endif 6 | 7 | #include 8 | #include 9 | #include 10 | #include "gs.h" 11 | 12 | class MetaCpu; 13 | 14 | bool isArmCodePointer(uint64_t addr); 15 | 16 | struct stackcontext { 17 | uint64_t r15, r14, r13, r12, r11, r10, r9, r8, rbp, rsi, rdx, rcx, rbx, rax, rdi, target, retaddr; 18 | }; 19 | 20 | class Interpreter; 21 | class Cpu : public CpuInterface { 22 | public: 23 | Cpu(); 24 | ~Cpu(); 25 | void runFrom(uint64_t addr); 26 | CpuState* currentState(); 27 | void precompile(uint64_t addr); 28 | 29 | void trampoline(uint64_t addr); 30 | void dumpRegs(); 31 | void nativeToArm(stackcontext* context); 32 | std::stack callStack; 33 | 34 | #ifdef USE_UNICORN 35 | uc_engine* uc; 36 | CpuState unicornState; 37 | void pullUnicornState(); 38 | void pushUnicornState(); 39 | #else 40 | MetaCpu* metaCpu; 41 | #endif 42 | 43 | uint64_t lastPageChecked[CodeSourceCount]; 44 | bool isValidCodePointer(CodeSource source, uint64_t addr, CpuState* state) override; 45 | bool Svc(uint32_t svc, CpuState* state) override; 46 | uint64_t SR(uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2) override; 47 | void SR(uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2, uint64_t value) override; 48 | void Log(const std::string& message) override; 49 | void Error(const std::string& message) override; 50 | }; 51 | 52 | extern thread_local Cpu CpuInstance; 53 | -------------------------------------------------------------------------------- /Runtime/exceptionHandlers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void registerExceptionHandlers(); -------------------------------------------------------------------------------- /Runtime/exceptionHandlers.mm: -------------------------------------------------------------------------------- 1 | #include 2 | #import 3 | #include "exceptionHandlers.h" 4 | #include "gs.h" 5 | 6 | const char* asStr(NSString* str) { 7 | return [str UTF8String]; 8 | } 9 | 10 | void globalNSExceptionHandler(NSException* exc) { 11 | log("Unhandled NSException!\nName: {}\nReason: {}\nUserInfo: {}\nTop Symbol On Call Stack: {}", 12 | asStr([exc name]), asStr([exc reason]), 13 | [exc userInfo] != nil ? asStr([[exc userInfo] description]) : "nil", 14 | [exc callStackSymbols] != nil && [[exc callStackSymbols] count] != 0 ? asStr([[[exc callStackSymbols] firstObject] description]) : "nil"); 15 | BAILOUT(); 16 | } 17 | 18 | void registerExceptionHandlers() { 19 | NSSetUncaughtExceptionHandler(globalNSExceptionHandler); 20 | } 21 | -------------------------------------------------------------------------------- /Runtime/fmt/locale.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::locale support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_LOCALE_H_ 9 | #define FMT_LOCALE_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | 17 | namespace internal { 18 | template 19 | typename buffer_context::iterator vformat_to( 20 | const std::locale& loc, buffer& buf, 21 | basic_string_view format_str, 22 | basic_format_args>> args) { 23 | using range = buffer_range; 24 | return vformat_to>(buf, to_string_view(format_str), args, 25 | internal::locale_ref(loc)); 26 | } 27 | 28 | template 29 | std::basic_string vformat( 30 | const std::locale& loc, basic_string_view format_str, 31 | basic_format_args>> args) { 32 | basic_memory_buffer buffer; 33 | internal::vformat_to(loc, buffer, format_str, args); 34 | return fmt::to_string(buffer); 35 | } 36 | } // namespace internal 37 | 38 | template > 39 | inline std::basic_string vformat( 40 | const std::locale& loc, const S& format_str, 41 | basic_format_args>> args) { 42 | return internal::vformat(loc, to_string_view(format_str), args); 43 | } 44 | 45 | template > 46 | inline std::basic_string format(const std::locale& loc, 47 | const S& format_str, Args&&... args) { 48 | return internal::vformat( 49 | loc, to_string_view(format_str), 50 | internal::make_args_checked(format_str, args...)); 51 | } 52 | 53 | template ::value, char_t>> 56 | inline OutputIt vformat_to( 57 | OutputIt out, const std::locale& loc, const S& format_str, 58 | format_args_t, Char> args) { 59 | using range = internal::output_range; 60 | return vformat_to>( 61 | range(out), to_string_view(format_str), args, internal::locale_ref(loc)); 62 | } 63 | 64 | template ::value&& 66 | internal::is_string::value)> 67 | inline OutputIt format_to(OutputIt out, const std::locale& loc, 68 | const S& format_str, Args&&... args) { 69 | internal::check_format_string(format_str); 70 | using context = format_context_t>; 71 | format_arg_store as{args...}; 72 | return vformat_to(out, loc, to_string_view(format_str), 73 | basic_format_args(as)); 74 | } 75 | 76 | FMT_END_NAMESPACE 77 | 78 | #endif // FMT_LOCALE_H_ 79 | -------------------------------------------------------------------------------- /Runtime/fmt/os.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional OS-specific functionality 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OS_H_ 9 | #define FMT_OS_H_ 10 | 11 | #if defined(__MINGW32__) || defined(__CYGWIN__) 12 | // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. 13 | # undef __STRICT_ANSI__ 14 | #endif 15 | 16 | #include 17 | #include // for locale_t 18 | #include 19 | #include 20 | #include // for strtod_l 21 | 22 | #if defined __APPLE__ || defined(__FreeBSD__) 23 | # include // for LC_NUMERIC_MASK on OS X 24 | #endif 25 | 26 | #include "format.h" 27 | 28 | // UWP doesn't provide _pipe. 29 | #if FMT_HAS_INCLUDE("winapifamily.h") 30 | # include 31 | #endif 32 | #if FMT_HAS_INCLUDE("fcntl.h") && \ 33 | (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) 34 | # include // for O_RDONLY 35 | # define FMT_USE_FCNTL 1 36 | #else 37 | # define FMT_USE_FCNTL 0 38 | #endif 39 | 40 | #ifndef FMT_POSIX 41 | # if defined(_WIN32) && !defined(__MINGW32__) 42 | // Fix warnings about deprecated symbols. 43 | # define FMT_POSIX(call) _##call 44 | # else 45 | # define FMT_POSIX(call) call 46 | # endif 47 | #endif 48 | 49 | // Calls to system functions are wrapped in FMT_SYSTEM for testability. 50 | #ifdef FMT_SYSTEM 51 | # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) 52 | #else 53 | # define FMT_SYSTEM(call) call 54 | # ifdef _WIN32 55 | // Fix warnings about deprecated symbols. 56 | # define FMT_POSIX_CALL(call) ::_##call 57 | # else 58 | # define FMT_POSIX_CALL(call) ::call 59 | # endif 60 | #endif 61 | 62 | // Retries the expression while it evaluates to error_result and errno 63 | // equals to EINTR. 64 | #ifndef _WIN32 65 | # define FMT_RETRY_VAL(result, expression, error_result) \ 66 | do { \ 67 | (result) = (expression); \ 68 | } while ((result) == (error_result) && errno == EINTR) 69 | #else 70 | # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) 71 | #endif 72 | 73 | #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) 74 | 75 | FMT_BEGIN_NAMESPACE 76 | 77 | /** 78 | \rst 79 | A reference to a null-terminated string. It can be constructed from a C 80 | string or ``std::string``. 81 | 82 | You can use one of the following type aliases for common character types: 83 | 84 | +---------------+-----------------------------+ 85 | | Type | Definition | 86 | +===============+=============================+ 87 | | cstring_view | basic_cstring_view | 88 | +---------------+-----------------------------+ 89 | | wcstring_view | basic_cstring_view | 90 | +---------------+-----------------------------+ 91 | 92 | This class is most useful as a parameter type to allow passing 93 | different types of strings to a function, for example:: 94 | 95 | template 96 | std::string format(cstring_view format_str, const Args & ... args); 97 | 98 | format("{}", 42); 99 | format(std::string("{}"), 42); 100 | \endrst 101 | */ 102 | template class basic_cstring_view { 103 | private: 104 | const Char* data_; 105 | 106 | public: 107 | /** Constructs a string reference object from a C string. */ 108 | basic_cstring_view(const Char* s) : data_(s) {} 109 | 110 | /** 111 | \rst 112 | Constructs a string reference from an ``std::string`` object. 113 | \endrst 114 | */ 115 | basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} 116 | 117 | /** Returns the pointer to a C string. */ 118 | const Char* c_str() const { return data_; } 119 | }; 120 | 121 | using cstring_view = basic_cstring_view; 122 | using wcstring_view = basic_cstring_view; 123 | 124 | // An error code. 125 | class error_code { 126 | private: 127 | int value_; 128 | 129 | public: 130 | explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} 131 | 132 | int get() const FMT_NOEXCEPT { return value_; } 133 | }; 134 | 135 | #ifdef _WIN32 136 | namespace internal { 137 | // A converter from UTF-16 to UTF-8. 138 | // It is only provided for Windows since other systems support UTF-8 natively. 139 | class utf16_to_utf8 { 140 | private: 141 | memory_buffer buffer_; 142 | 143 | public: 144 | utf16_to_utf8() {} 145 | FMT_API explicit utf16_to_utf8(wstring_view s); 146 | operator string_view() const { return string_view(&buffer_[0], size()); } 147 | size_t size() const { return buffer_.size() - 1; } 148 | const char* c_str() const { return &buffer_[0]; } 149 | std::string str() const { return std::string(&buffer_[0], size()); } 150 | 151 | // Performs conversion returning a system error code instead of 152 | // throwing exception on conversion error. This method may still throw 153 | // in case of memory allocation error. 154 | FMT_API int convert(wstring_view s); 155 | }; 156 | 157 | FMT_API void format_windows_error(buffer& out, int error_code, 158 | string_view message) FMT_NOEXCEPT; 159 | } // namespace internal 160 | 161 | /** A Windows error. */ 162 | class windows_error : public system_error { 163 | private: 164 | FMT_API void init(int error_code, string_view format_str, format_args args); 165 | 166 | public: 167 | /** 168 | \rst 169 | Constructs a :class:`fmt::windows_error` object with the description 170 | of the form 171 | 172 | .. parsed-literal:: 173 | **: ** 174 | 175 | where ** is the formatted message and ** is the 176 | system message corresponding to the error code. 177 | *error_code* is a Windows error code as given by ``GetLastError``. 178 | If *error_code* is not a valid error code such as -1, the system message 179 | will look like "error -1". 180 | 181 | **Example**:: 182 | 183 | // This throws a windows_error with the description 184 | // cannot open file 'madeup': The system cannot find the file specified. 185 | // or similar (system message may vary). 186 | const char *filename = "madeup"; 187 | LPOFSTRUCT of = LPOFSTRUCT(); 188 | HFILE file = OpenFile(filename, &of, OF_READ); 189 | if (file == HFILE_ERROR) { 190 | throw fmt::windows_error(GetLastError(), 191 | "cannot open file '{}'", filename); 192 | } 193 | \endrst 194 | */ 195 | template 196 | windows_error(int error_code, string_view message, const Args&... args) { 197 | init(error_code, message, make_format_args(args...)); 198 | } 199 | }; 200 | 201 | // Reports a Windows error without throwing an exception. 202 | // Can be used to report errors from destructors. 203 | FMT_API void report_windows_error(int error_code, 204 | string_view message) FMT_NOEXCEPT; 205 | #endif // _WIN32 206 | 207 | // A buffered file. 208 | class buffered_file { 209 | private: 210 | FILE* file_; 211 | 212 | friend class file; 213 | 214 | explicit buffered_file(FILE* f) : file_(f) {} 215 | 216 | public: 217 | buffered_file(const buffered_file&) = delete; 218 | void operator=(const buffered_file&) = delete; 219 | 220 | // Constructs a buffered_file object which doesn't represent any file. 221 | buffered_file() FMT_NOEXCEPT : file_(nullptr) {} 222 | 223 | // Destroys the object closing the file it represents if any. 224 | FMT_API ~buffered_file() FMT_NOEXCEPT; 225 | 226 | public: 227 | buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { 228 | other.file_ = nullptr; 229 | } 230 | 231 | buffered_file& operator=(buffered_file&& other) { 232 | close(); 233 | file_ = other.file_; 234 | other.file_ = nullptr; 235 | return *this; 236 | } 237 | 238 | // Opens a file. 239 | FMT_API buffered_file(cstring_view filename, cstring_view mode); 240 | 241 | // Closes the file. 242 | FMT_API void close(); 243 | 244 | // Returns the pointer to a FILE object representing this file. 245 | FILE* get() const FMT_NOEXCEPT { return file_; } 246 | 247 | // We place parentheses around fileno to workaround a bug in some versions 248 | // of MinGW that define fileno as a macro. 249 | FMT_API int(fileno)() const; 250 | 251 | void vprint(string_view format_str, format_args args) { 252 | fmt::vprint(file_, format_str, args); 253 | } 254 | 255 | template 256 | inline void print(string_view format_str, const Args&... args) { 257 | vprint(format_str, make_format_args(args...)); 258 | } 259 | }; 260 | 261 | #if FMT_USE_FCNTL 262 | // A file. Closed file is represented by a file object with descriptor -1. 263 | // Methods that are not declared with FMT_NOEXCEPT may throw 264 | // fmt::system_error in case of failure. Note that some errors such as 265 | // closing the file multiple times will cause a crash on Windows rather 266 | // than an exception. You can get standard behavior by overriding the 267 | // invalid parameter handler with _set_invalid_parameter_handler. 268 | class file { 269 | private: 270 | int fd_; // File descriptor. 271 | 272 | // Constructs a file object with a given descriptor. 273 | explicit file(int fd) : fd_(fd) {} 274 | 275 | public: 276 | // Possible values for the oflag argument to the constructor. 277 | enum { 278 | RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. 279 | WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. 280 | RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. 281 | }; 282 | 283 | // Constructs a file object which doesn't represent any file. 284 | file() FMT_NOEXCEPT : fd_(-1) {} 285 | 286 | // Opens a file and constructs a file object representing this file. 287 | FMT_API file(cstring_view path, int oflag); 288 | 289 | public: 290 | file(const file&) = delete; 291 | void operator=(const file&) = delete; 292 | 293 | file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } 294 | 295 | file& operator=(file&& other) FMT_NOEXCEPT { 296 | close(); 297 | fd_ = other.fd_; 298 | other.fd_ = -1; 299 | return *this; 300 | } 301 | 302 | // Destroys the object closing the file it represents if any. 303 | FMT_API ~file() FMT_NOEXCEPT; 304 | 305 | // Returns the file descriptor. 306 | int descriptor() const FMT_NOEXCEPT { return fd_; } 307 | 308 | // Closes the file. 309 | FMT_API void close(); 310 | 311 | // Returns the file size. The size has signed type for consistency with 312 | // stat::st_size. 313 | FMT_API long long size() const; 314 | 315 | // Attempts to read count bytes from the file into the specified buffer. 316 | FMT_API std::size_t read(void* buffer, std::size_t count); 317 | 318 | // Attempts to write count bytes from the specified buffer to the file. 319 | FMT_API std::size_t write(const void* buffer, std::size_t count); 320 | 321 | // Duplicates a file descriptor with the dup function and returns 322 | // the duplicate as a file object. 323 | FMT_API static file dup(int fd); 324 | 325 | // Makes fd be the copy of this file descriptor, closing fd first if 326 | // necessary. 327 | FMT_API void dup2(int fd); 328 | 329 | // Makes fd be the copy of this file descriptor, closing fd first if 330 | // necessary. 331 | FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT; 332 | 333 | // Creates a pipe setting up read_end and write_end file objects for reading 334 | // and writing respectively. 335 | FMT_API static void pipe(file& read_end, file& write_end); 336 | 337 | // Creates a buffered_file object associated with this file and detaches 338 | // this file object from the file. 339 | FMT_API buffered_file fdopen(const char* mode); 340 | }; 341 | 342 | // Returns the memory page size. 343 | long getpagesize(); 344 | #endif // FMT_USE_FCNTL 345 | 346 | #ifdef FMT_LOCALE 347 | // A "C" numeric locale. 348 | class locale { 349 | private: 350 | # ifdef _WIN32 351 | using locale_t = _locale_t; 352 | 353 | static void freelocale(locale_t loc) { _free_locale(loc); } 354 | 355 | static double strtod_l(const char* nptr, char** endptr, _locale_t loc) { 356 | return _strtod_l(nptr, endptr, loc); 357 | } 358 | # endif 359 | 360 | locale_t locale_; 361 | 362 | public: 363 | using type = locale_t; 364 | locale(const locale&) = delete; 365 | void operator=(const locale&) = delete; 366 | 367 | locale() { 368 | # ifndef _WIN32 369 | locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr)); 370 | # else 371 | locale_ = _create_locale(LC_NUMERIC, "C"); 372 | # endif 373 | if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); 374 | } 375 | ~locale() { freelocale(locale_); } 376 | 377 | type get() const { return locale_; } 378 | 379 | // Converts string to floating-point number and advances str past the end 380 | // of the parsed input. 381 | double strtod(const char*& str) const { 382 | char* end = nullptr; 383 | double result = strtod_l(str, &end, locale_); 384 | str = end; 385 | return result; 386 | } 387 | }; 388 | using Locale FMT_DEPRECATED_ALIAS = locale; 389 | #endif // FMT_LOCALE 390 | FMT_END_NAMESPACE 391 | 392 | #endif // FMT_OS_H_ 393 | -------------------------------------------------------------------------------- /Runtime/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | 17 | template class basic_printf_parse_context; 18 | template class basic_printf_context; 19 | 20 | namespace internal { 21 | 22 | template class formatbuf : public std::basic_streambuf { 23 | private: 24 | using int_type = typename std::basic_streambuf::int_type; 25 | using traits_type = typename std::basic_streambuf::traits_type; 26 | 27 | buffer& buffer_; 28 | 29 | public: 30 | formatbuf(buffer& buf) : buffer_(buf) {} 31 | 32 | protected: 33 | // The put-area is actually always empty. This makes the implementation 34 | // simpler and has the advantage that the streambuf and the buffer are always 35 | // in sync and sputc never writes into uninitialized memory. The obvious 36 | // disadvantage is that each call to sputc always results in a (virtual) call 37 | // to overflow. There is no disadvantage here for sputn since this always 38 | // results in a call to xsputn. 39 | 40 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { 41 | if (!traits_type::eq_int_type(ch, traits_type::eof())) 42 | buffer_.push_back(static_cast(ch)); 43 | return ch; 44 | } 45 | 46 | std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { 47 | buffer_.append(s, s + count); 48 | return count; 49 | } 50 | }; 51 | 52 | template struct test_stream : std::basic_ostream { 53 | private: 54 | // Hide all operator<< from std::basic_ostream. 55 | void_t<> operator<<(null<>); 56 | void_t<> operator<<(const Char*); 57 | 58 | template ::value && 59 | !std::is_enum::value)> 60 | void_t<> operator<<(T); 61 | }; 62 | 63 | // Checks if T has a user-defined operator<< (e.g. not a member of 64 | // std::ostream). 65 | template class is_streamable { 66 | private: 67 | template 68 | static bool_constant&>() 69 | << std::declval()), 70 | void_t<>>::value> 71 | test(int); 72 | 73 | template static std::false_type test(...); 74 | 75 | using result = decltype(test(0)); 76 | 77 | public: 78 | static const bool value = result::value; 79 | }; 80 | 81 | // Write the content of buf to os. 82 | template 83 | void write(std::basic_ostream& os, buffer& buf) { 84 | const Char* buf_data = buf.data(); 85 | using unsigned_streamsize = std::make_unsigned::type; 86 | unsigned_streamsize size = buf.size(); 87 | unsigned_streamsize max_size = to_unsigned(max_value()); 88 | do { 89 | unsigned_streamsize n = size <= max_size ? size : max_size; 90 | os.write(buf_data, static_cast(n)); 91 | buf_data += n; 92 | size -= n; 93 | } while (size != 0); 94 | } 95 | 96 | template 97 | void format_value(buffer& buf, const T& value, 98 | locale_ref loc = locale_ref()) { 99 | formatbuf format_buf(buf); 100 | std::basic_ostream output(&format_buf); 101 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) 102 | if (loc) output.imbue(loc.get()); 103 | #endif 104 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 105 | output << value; 106 | buf.resize(buf.size()); 107 | } 108 | 109 | // Formats an object of type T that has an overloaded ostream operator<<. 110 | template 111 | struct fallback_formatter::value>> 112 | : private formatter, Char> { 113 | auto parse(basic_format_parse_context& ctx) -> decltype(ctx.begin()) { 114 | return formatter, Char>::parse(ctx); 115 | } 116 | template >::value)> 119 | auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) { 120 | return ctx.begin(); 121 | } 122 | 123 | template 124 | auto format(const T& value, basic_format_context& ctx) 125 | -> OutputIt { 126 | basic_memory_buffer buffer; 127 | format_value(buffer, value, ctx.locale()); 128 | basic_string_view str(buffer.data(), buffer.size()); 129 | return formatter, Char>::format(str, ctx); 130 | } 131 | template 132 | auto format(const T& value, basic_printf_context& ctx) 133 | -> OutputIt { 134 | basic_memory_buffer buffer; 135 | format_value(buffer, value, ctx.locale()); 136 | return std::copy(buffer.begin(), buffer.end(), ctx.out()); 137 | } 138 | }; 139 | } // namespace internal 140 | 141 | template 142 | void vprint(std::basic_ostream& os, basic_string_view format_str, 143 | basic_format_args>> args) { 144 | basic_memory_buffer buffer; 145 | internal::vformat_to(buffer, format_str, args); 146 | internal::write(os, buffer); 147 | } 148 | 149 | /** 150 | \rst 151 | Prints formatted data to the stream *os*. 152 | 153 | **Example**:: 154 | 155 | fmt::print(cerr, "Don't {}!", "panic"); 156 | \endrst 157 | */ 158 | template ::value, char_t>> 160 | void print(std::basic_ostream& os, const S& format_str, Args&&... args) { 161 | vprint(os, to_string_view(format_str), 162 | internal::make_args_checked(format_str, args...)); 163 | } 164 | FMT_END_NAMESPACE 165 | 166 | #endif // FMT_OSTREAM_H_ 167 | -------------------------------------------------------------------------------- /Runtime/fmt/posix.h: -------------------------------------------------------------------------------- 1 | #include "os.h" 2 | #warning "fmt/posix.h is deprecated; use fmt/os.h instead" -------------------------------------------------------------------------------- /Runtime/fmt/ranges.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - experimental range support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | // 8 | // Copyright (c) 2018 - present, Remotion (Igor Schulz) 9 | // All Rights Reserved 10 | // {fmt} support for ranges, containers and types tuple interface. 11 | 12 | #ifndef FMT_RANGES_H_ 13 | #define FMT_RANGES_H_ 14 | 15 | #include 16 | #include 17 | 18 | #include "format.h" 19 | 20 | // output only up to N items from the range. 21 | #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT 22 | # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 23 | #endif 24 | 25 | FMT_BEGIN_NAMESPACE 26 | 27 | template struct formatting_base { 28 | template 29 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 30 | return ctx.begin(); 31 | } 32 | }; 33 | 34 | template 35 | struct formatting_range : formatting_base { 36 | static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = 37 | FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the 38 | // range. 39 | Char prefix; 40 | Char delimiter; 41 | Char postfix; 42 | formatting_range() : prefix('{'), delimiter(','), postfix('}') {} 43 | static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; 44 | static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 45 | }; 46 | 47 | template 48 | struct formatting_tuple : formatting_base { 49 | Char prefix; 50 | Char delimiter; 51 | Char postfix; 52 | formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} 53 | static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; 54 | static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 55 | }; 56 | 57 | namespace internal { 58 | 59 | template 60 | OutputIterator copy(const RangeT& range, OutputIterator out) { 61 | for (auto it = range.begin(), end = range.end(); it != end; ++it) 62 | *out++ = *it; 63 | return out; 64 | } 65 | 66 | template 67 | OutputIterator copy(const char* str, OutputIterator out) { 68 | while (*str) *out++ = *str++; 69 | return out; 70 | } 71 | 72 | template 73 | OutputIterator copy(char ch, OutputIterator out) { 74 | *out++ = ch; 75 | return out; 76 | } 77 | 78 | /// Return true value if T has std::string interface, like std::string_view. 79 | template class is_like_std_string { 80 | template 81 | static auto check(U* p) 82 | -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); 83 | template static void check(...); 84 | 85 | public: 86 | static FMT_CONSTEXPR_DECL const bool value = 87 | is_string::value || !std::is_void(nullptr))>::value; 88 | }; 89 | 90 | template 91 | struct is_like_std_string> : std::true_type {}; 92 | 93 | template struct conditional_helper {}; 94 | 95 | template struct is_range_ : std::false_type {}; 96 | 97 | #if !FMT_MSC_VER || FMT_MSC_VER > 1800 98 | template 99 | struct is_range_< 100 | T, conditional_t().begin()), 102 | decltype(std::declval().end())>, 103 | void>> : std::true_type {}; 104 | #endif 105 | 106 | /// tuple_size and tuple_element check. 107 | template class is_tuple_like_ { 108 | template 109 | static auto check(U* p) -> decltype(std::tuple_size::value, int()); 110 | template static void check(...); 111 | 112 | public: 113 | static FMT_CONSTEXPR_DECL const bool value = 114 | !std::is_void(nullptr))>::value; 115 | }; 116 | 117 | // Check for integer_sequence 118 | #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 119 | template 120 | using integer_sequence = std::integer_sequence; 121 | template using index_sequence = std::index_sequence; 122 | template 123 | using make_index_sequence = std::make_index_sequence; 124 | #else 125 | template struct integer_sequence { 126 | using value_type = T; 127 | 128 | static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); } 129 | }; 130 | 131 | template 132 | using index_sequence = integer_sequence; 133 | 134 | template 135 | struct make_integer_sequence : make_integer_sequence {}; 136 | template 137 | struct make_integer_sequence : integer_sequence {}; 138 | 139 | template 140 | using make_index_sequence = make_integer_sequence; 141 | #endif 142 | 143 | template 144 | void for_each(index_sequence, Tuple&& tup, F&& f) FMT_NOEXCEPT { 145 | using std::get; 146 | // using free function get(T) now. 147 | const int _[] = {0, ((void)f(get(tup)), 0)...}; 148 | (void)_; // blocks warnings 149 | } 150 | 151 | template 152 | FMT_CONSTEXPR make_index_sequence::value> get_indexes( 153 | T const&) { 154 | return {}; 155 | } 156 | 157 | template void for_each(Tuple&& tup, F&& f) { 158 | const auto indexes = get_indexes(tup); 159 | for_each(indexes, std::forward(tup), std::forward(f)); 160 | } 161 | 162 | template ::type>::value)> 164 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { 165 | return add_space ? " {}" : "{}"; 166 | } 167 | 168 | template ::type>::value)> 170 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { 171 | return add_space ? " \"{}\"" : "\"{}\""; 172 | } 173 | 174 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { 175 | return add_space ? " \"{}\"" : "\"{}\""; 176 | } 177 | FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { 178 | return add_space ? L" \"{}\"" : L"\"{}\""; 179 | } 180 | 181 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { 182 | return add_space ? " '{}'" : "'{}'"; 183 | } 184 | FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { 185 | return add_space ? L" '{}'" : L"'{}'"; 186 | } 187 | 188 | } // namespace internal 189 | 190 | template struct is_tuple_like { 191 | static FMT_CONSTEXPR_DECL const bool value = 192 | internal::is_tuple_like_::value && !internal::is_range_::value; 193 | }; 194 | 195 | template 196 | struct formatter::value>> { 197 | private: 198 | // C++11 generic lambda for format() 199 | template struct format_each { 200 | template void operator()(const T& v) { 201 | if (i > 0) { 202 | if (formatting.add_prepostfix_space) { 203 | *out++ = ' '; 204 | } 205 | out = internal::copy(formatting.delimiter, out); 206 | } 207 | out = format_to(out, 208 | internal::format_str_quoted( 209 | (formatting.add_delimiter_spaces && i > 0), v), 210 | v); 211 | ++i; 212 | } 213 | 214 | formatting_tuple& formatting; 215 | std::size_t& i; 216 | typename std::add_lvalue_reference().out())>::type out; 218 | }; 219 | 220 | public: 221 | formatting_tuple formatting; 222 | 223 | template 224 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 225 | return formatting.parse(ctx); 226 | } 227 | 228 | template 229 | auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { 230 | auto out = ctx.out(); 231 | std::size_t i = 0; 232 | internal::copy(formatting.prefix, out); 233 | 234 | internal::for_each(values, format_each{formatting, i, out}); 235 | if (formatting.add_prepostfix_space) { 236 | *out++ = ' '; 237 | } 238 | internal::copy(formatting.postfix, out); 239 | 240 | return ctx.out(); 241 | } 242 | }; 243 | 244 | template struct is_range { 245 | static FMT_CONSTEXPR_DECL const bool value = 246 | internal::is_range_::value && 247 | !internal::is_like_std_string::value && 248 | !std::is_convertible>::value && 249 | !std::is_constructible, T>::value; 250 | }; 251 | 252 | template 253 | struct formatter::value>> { 255 | formatting_range formatting; 256 | 257 | template 258 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 259 | return formatting.parse(ctx); 260 | } 261 | 262 | template 263 | typename FormatContext::iterator format(const RangeT& values, 264 | FormatContext& ctx) { 265 | auto out = internal::copy(formatting.prefix, ctx.out()); 266 | std::size_t i = 0; 267 | for (auto it = values.begin(), end = values.end(); it != end; ++it) { 268 | if (i > 0) { 269 | if (formatting.add_prepostfix_space) *out++ = ' '; 270 | out = internal::copy(formatting.delimiter, out); 271 | } 272 | out = format_to(out, 273 | internal::format_str_quoted( 274 | (formatting.add_delimiter_spaces && i > 0), *it), 275 | *it); 276 | if (++i > formatting.range_length_limit) { 277 | out = format_to(out, " ... "); 278 | break; 279 | } 280 | } 281 | if (formatting.add_prepostfix_space) *out++ = ' '; 282 | return internal::copy(formatting.postfix, out); 283 | } 284 | }; 285 | 286 | template struct tuple_arg_join : internal::view { 287 | const std::tuple& tuple; 288 | basic_string_view sep; 289 | 290 | tuple_arg_join(const std::tuple& t, basic_string_view s) 291 | : tuple{t}, sep{s} {} 292 | }; 293 | 294 | template 295 | struct formatter, Char> { 296 | template 297 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 298 | return ctx.begin(); 299 | } 300 | 301 | template 302 | typename FormatContext::iterator format( 303 | const tuple_arg_join& value, FormatContext& ctx) { 304 | return format(value, ctx, internal::make_index_sequence{}); 305 | } 306 | 307 | private: 308 | template 309 | typename FormatContext::iterator format( 310 | const tuple_arg_join& value, FormatContext& ctx, 311 | internal::index_sequence) { 312 | return format_args(value, ctx, std::get(value.tuple)...); 313 | } 314 | 315 | template 316 | typename FormatContext::iterator format_args( 317 | const tuple_arg_join&, FormatContext& ctx) { 318 | // NOTE: for compilers that support C++17, this empty function instantiation 319 | // can be replaced with a constexpr branch in the variadic overload. 320 | return ctx.out(); 321 | } 322 | 323 | template 324 | typename FormatContext::iterator format_args( 325 | const tuple_arg_join& value, FormatContext& ctx, 326 | const Arg& arg, const Args&... args) { 327 | using base = formatter::type, Char>; 328 | auto out = ctx.out(); 329 | out = base{}.format(arg, ctx); 330 | if (sizeof...(Args) > 0) { 331 | out = std::copy(value.sep.begin(), value.sep.end(), out); 332 | ctx.advance_to(out); 333 | return format_args(value, ctx, args...); 334 | } 335 | return out; 336 | } 337 | }; 338 | 339 | /** 340 | \rst 341 | Returns an object that formats `tuple` with elements separated by `sep`. 342 | 343 | **Example**:: 344 | 345 | std::tuple t = {1, 'a'}; 346 | fmt::print("{}", fmt::join(t, ", ")); 347 | // Output: "1, a" 348 | \endrst 349 | */ 350 | template 351 | FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 352 | string_view sep) { 353 | return {tuple, sep}; 354 | } 355 | 356 | template 357 | FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 358 | wstring_view sep) { 359 | return {tuple, sep}; 360 | } 361 | 362 | /** 363 | \rst 364 | Returns an object that formats `initializer_list` with elements separated by 365 | `sep`. 366 | 367 | **Example**:: 368 | 369 | fmt::print("{}", fmt::join({1, 2, 3}, ", ")); 370 | // Output: "1, 2, 3" 371 | \endrst 372 | */ 373 | template 374 | arg_join>, char> join( 375 | std::initializer_list list, string_view sep) { 376 | return join(std::begin(list), std::end(list), sep); 377 | } 378 | 379 | template 380 | arg_join>, wchar_t> join( 381 | std::initializer_list list, wstring_view sep) { 382 | return join(std::begin(list), std::end(list), sep); 383 | } 384 | 385 | FMT_END_NAMESPACE 386 | 387 | #endif // FMT_RANGES_H_ 388 | -------------------------------------------------------------------------------- /Runtime/format.cc: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #include "fmt/format-inl.h" 9 | 10 | FMT_BEGIN_NAMESPACE 11 | namespace internal { 12 | 13 | template 14 | int format_float(char* buf, std::size_t size, const char* format, int precision, 15 | T value) { 16 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 17 | if (precision > 100000) 18 | throw std::runtime_error( 19 | "fuzz mode - avoid large allocation inside snprintf"); 20 | #endif 21 | // Suppress the warning about nonliteral format string. 22 | int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; 23 | return precision < 0 ? snprintf_ptr(buf, size, format, value) 24 | : snprintf_ptr(buf, size, format, precision, value); 25 | } 26 | struct sprintf_specs { 27 | int precision; 28 | char type; 29 | bool alt : 1; 30 | 31 | template 32 | constexpr sprintf_specs(basic_format_specs specs) 33 | : precision(specs.precision), type(specs.type), alt(specs.alt) {} 34 | 35 | constexpr bool has_precision() const { return precision >= 0; } 36 | }; 37 | 38 | // This is deprecated and is kept only to preserve ABI compatibility. 39 | template 40 | char* sprintf_format(Double value, internal::buffer& buf, 41 | sprintf_specs specs) { 42 | // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. 43 | FMT_ASSERT(buf.capacity() != 0, "empty buffer"); 44 | 45 | // Build format string. 46 | enum { max_format_size = 10 }; // longest format: %#-*.*Lg 47 | char format[max_format_size]; 48 | char* format_ptr = format; 49 | *format_ptr++ = '%'; 50 | if (specs.alt || !specs.type) *format_ptr++ = '#'; 51 | if (specs.precision >= 0) { 52 | *format_ptr++ = '.'; 53 | *format_ptr++ = '*'; 54 | } 55 | if (std::is_same::value) *format_ptr++ = 'L'; 56 | 57 | char type = specs.type; 58 | 59 | if (type == '%') 60 | type = 'f'; 61 | else if (type == 0 || type == 'n') 62 | type = 'g'; 63 | #if FMT_MSC_VER 64 | if (type == 'F') { 65 | // MSVC's printf doesn't support 'F'. 66 | type = 'f'; 67 | } 68 | #endif 69 | *format_ptr++ = type; 70 | *format_ptr = '\0'; 71 | 72 | // Format using snprintf. 73 | char* start = nullptr; 74 | char* decimal_point_pos = nullptr; 75 | for (;;) { 76 | std::size_t buffer_size = buf.capacity(); 77 | start = &buf[0]; 78 | int result = 79 | format_float(start, buffer_size, format, specs.precision, value); 80 | if (result >= 0) { 81 | unsigned n = internal::to_unsigned(result); 82 | if (n < buf.capacity()) { 83 | // Find the decimal point. 84 | auto p = buf.data(), end = p + n; 85 | if (*p == '+' || *p == '-') ++p; 86 | if (specs.type != 'a' && specs.type != 'A') { 87 | while (p < end && *p >= '0' && *p <= '9') ++p; 88 | if (p < end && *p != 'e' && *p != 'E') { 89 | decimal_point_pos = p; 90 | if (!specs.type) { 91 | // Keep only one trailing zero after the decimal point. 92 | ++p; 93 | if (*p == '0') ++p; 94 | while (p != end && *p >= '1' && *p <= '9') ++p; 95 | char* where = p; 96 | while (p != end && *p == '0') ++p; 97 | if (p == end || *p < '0' || *p > '9') { 98 | if (p != end) std::memmove(where, p, to_unsigned(end - p)); 99 | n -= static_cast(p - where); 100 | } 101 | } 102 | } 103 | } 104 | buf.resize(n); 105 | break; // The buffer is large enough - continue with formatting. 106 | } 107 | buf.reserve(n + 1); 108 | } else { 109 | // If result is negative we ask to increase the capacity by at least 1, 110 | // but as std::vector, the buffer grows exponentially. 111 | buf.reserve(buf.capacity() + 1); 112 | } 113 | } 114 | return decimal_point_pos; 115 | } 116 | } // namespace internal 117 | 118 | template FMT_API char* internal::sprintf_format(double, internal::buffer&, 119 | sprintf_specs); 120 | template FMT_API char* internal::sprintf_format(long double, 121 | internal::buffer&, 122 | sprintf_specs); 123 | 124 | template struct FMT_INSTANTIATION_DEF_API internal::basic_data; 125 | 126 | // Workaround a bug in MSVC2013 that prevents instantiation of format_float. 127 | int (*instantiate_format_float)(double, int, internal::float_specs, 128 | internal::buffer&) = 129 | internal::format_float; 130 | 131 | #ifndef FMT_STATIC_THOUSANDS_SEPARATOR 132 | template FMT_API internal::locale_ref::locale_ref(const std::locale& loc); 133 | template FMT_API std::locale internal::locale_ref::get() const; 134 | #endif 135 | 136 | // Explicit instantiations for char. 137 | 138 | template FMT_API std::string internal::grouping_impl(locale_ref); 139 | template FMT_API char internal::thousands_sep_impl(locale_ref); 140 | template FMT_API char internal::decimal_point_impl(locale_ref); 141 | 142 | template FMT_API void internal::buffer::append(const char*, const char*); 143 | 144 | template FMT_API void internal::arg_map::init( 145 | const basic_format_args& args); 146 | 147 | template FMT_API std::string internal::vformat( 148 | string_view, basic_format_args); 149 | 150 | template FMT_API format_context::iterator internal::vformat_to( 151 | internal::buffer&, string_view, basic_format_args); 152 | 153 | template FMT_API int internal::snprintf_float(double, int, 154 | internal::float_specs, 155 | internal::buffer&); 156 | template FMT_API int internal::snprintf_float(long double, int, 157 | internal::float_specs, 158 | internal::buffer&); 159 | template FMT_API int internal::format_float(double, int, internal::float_specs, 160 | internal::buffer&); 161 | template FMT_API int internal::format_float(long double, int, 162 | internal::float_specs, 163 | internal::buffer&); 164 | 165 | // Explicit instantiations for wchar_t. 166 | 167 | template FMT_API std::string internal::grouping_impl(locale_ref); 168 | template FMT_API wchar_t internal::thousands_sep_impl(locale_ref); 169 | template FMT_API wchar_t internal::decimal_point_impl(locale_ref); 170 | 171 | template FMT_API void internal::buffer::append(const wchar_t*, 172 | const wchar_t*); 173 | 174 | template FMT_API std::wstring internal::vformat( 175 | wstring_view, basic_format_args); 176 | FMT_END_NAMESPACE 177 | -------------------------------------------------------------------------------- /Runtime/gs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "cpu.h" 6 | #include "logging.h" 7 | 8 | #define PAGEBASE(addr) ((addr) & ~0xFFFULL) 9 | #define PAGEOFF(addr) ((addr) & 0xFFFULL) 10 | #define LARGEPAGEBASE(addr) ((addr) & ~0x3FFFULL) 11 | 12 | void initializeImages(); 13 | #define BAILOUT() bailout(__FILE__, __LINE__) 14 | __attribute__((noreturn)) void bailout(const char* fn, int line); 15 | 16 | bool isKeychainUsable(); 17 | 18 | struct BlockInternal { 19 | void* isa; 20 | int flags, reserved; 21 | uint64_t function; 22 | }; 23 | -------------------------------------------------------------------------------- /Runtime/jmpWrappers.cpp: -------------------------------------------------------------------------------- 1 | #include "gs.h" 2 | #include "jmpWrappers.h" 3 | 4 | /* 5 | * _JBLEN is the number of ints required to save the following: 6 | * r21-r29, sp, fp, lr == 12 registers, 8 bytes each. d8-d15 7 | * are another 8 registers, each 8 bytes long. (aapcs64 specifies 8 | * that only 64-bit versions of FP registers need to be saved). 9 | * Finally, two 8-byte fields for signal handling purposes. 10 | */ 11 | #define _JBLEN ((14 + 8 + 2) * 2) 12 | 13 | struct arm64_jmp_buf { 14 | uint64_t r21, r22, r23, r24, r25, r26, r27, r28, r29; 15 | uint64_t sp, fp, lr; 16 | double d8, d9, d10, d11, d12, d13, d14, d15; 17 | uint8_t signal_a, signal_b; 18 | }; 19 | 20 | void wrap_setjmp(CpuState* state) { 21 | log("Got call to setjmp"); 22 | auto buf = (arm64_jmp_buf*) state->X0; 23 | 24 | buf->r21 = state->X21; 25 | buf->r22 = state->X22; 26 | buf->r23 = state->X23; 27 | buf->r24 = state->X24; 28 | buf->r25 = state->X25; 29 | buf->r26 = state->X26; 30 | buf->r27 = state->X27; 31 | buf->r28 = state->X28; 32 | buf->r29 = state->X29; 33 | 34 | buf->sp = state->SP; 35 | buf->fp = state->X29; /// TODO: Why is this duplicated ... ? 36 | buf->lr = state->X30; 37 | 38 | buf->d8 = *(double*) &state->V8; 39 | buf->d9 = *(double*) &state->V9; 40 | buf->d10 = *(double*) &state->V10; 41 | buf->d11 = *(double*) &state->V11; 42 | buf->d12 = *(double*) &state->V12; 43 | buf->d13 = *(double*) &state->V13; 44 | buf->d14 = *(double*) &state->V14; 45 | buf->d15 = *(double*) &state->V15; 46 | 47 | buf->signal_a = buf->signal_b = 0; /// TODO: Figure this out 48 | 49 | state->X0 = 0; 50 | } 51 | 52 | void wrap_sigsetjmp(CpuState* state) { 53 | log("Got call to sigsetjmp"); 54 | auto buf = (arm64_jmp_buf*) state->X0; 55 | 56 | buf->r21 = state->X21; 57 | buf->r22 = state->X22; 58 | buf->r23 = state->X23; 59 | buf->r24 = state->X24; 60 | buf->r25 = state->X25; 61 | buf->r26 = state->X26; 62 | buf->r27 = state->X27; 63 | buf->r28 = state->X28; 64 | buf->r29 = state->X29; 65 | 66 | buf->sp = state->SP; 67 | buf->fp = state->X29; /// TODO: Why is this duplicated ... ? 68 | buf->lr = state->X30; 69 | 70 | buf->d8 = *(double*) &state->V8; 71 | buf->d9 = *(double*) &state->V9; 72 | buf->d10 = *(double*) &state->V10; 73 | buf->d11 = *(double*) &state->V11; 74 | buf->d12 = *(double*) &state->V12; 75 | buf->d13 = *(double*) &state->V13; 76 | buf->d14 = *(double*) &state->V14; 77 | buf->d15 = *(double*) &state->V15; 78 | 79 | buf->signal_a = buf->signal_b = 0; /// TODO: Figure this out 80 | 81 | state->X0 = 0; 82 | } 83 | void wrap_longjmp(CpuState* state) { 84 | log("Got call to longjmp"); 85 | auto buf = (arm64_jmp_buf*) state->X0; 86 | auto val = (int) state->X1; 87 | 88 | state->X21 = buf->r21; 89 | state->X22 = buf->r22; 90 | state->X23 = buf->r23; 91 | state->X24 = buf->r24; 92 | state->X25 = buf->r25; 93 | state->X26 = buf->r26; 94 | state->X27 = buf->r27; 95 | state->X28 = buf->r28; 96 | state->X29 = buf->r29; 97 | 98 | state->SP = buf->sp; 99 | state->X29 = buf->fp; /// TODO: Why is this duplicated ... ? 100 | state->PC = state->X30 = buf->lr; 101 | 102 | memset(&state->V8, 0, sizeof(double) * 8 * 2); 103 | *(double*) &state->V8 = buf->d8; 104 | *(double*) &state->V9 = buf->d9; 105 | *(double*) &state->V10 = buf->d10; 106 | *(double*) &state->V11 = buf->d11; 107 | *(double*) &state->V12 = buf->d12; 108 | *(double*) &state->V13 = buf->d13; 109 | *(double*) &state->V14 = buf->d14; 110 | *(double*) &state->V15 = buf->d15; 111 | 112 | state->X0 = (uint64_t) val; 113 | } 114 | void wrap_siglongjmp(CpuState* state) { 115 | log("Got call to siglongjmp"); 116 | auto buf = (arm64_jmp_buf*) state->X0; 117 | auto val = (int) state->X1; 118 | 119 | state->X21 = buf->r21; 120 | state->X22 = buf->r22; 121 | state->X23 = buf->r23; 122 | state->X24 = buf->r24; 123 | state->X25 = buf->r25; 124 | state->X26 = buf->r26; 125 | state->X27 = buf->r27; 126 | state->X28 = buf->r28; 127 | state->X29 = buf->r29; 128 | 129 | state->SP = buf->sp; 130 | state->X29 = buf->fp; /// TODO: Why is this duplicated ... ? 131 | state->PC = state->X30 = buf->lr; 132 | 133 | memset(&state->V8, 0, sizeof(double) * 8 * 2); 134 | *(double*) &state->V8 = buf->d8; 135 | *(double*) &state->V9 = buf->d9; 136 | *(double*) &state->V10 = buf->d10; 137 | *(double*) &state->V11 = buf->d11; 138 | *(double*) &state->V12 = buf->d12; 139 | *(double*) &state->V13 = buf->d13; 140 | *(double*) &state->V14 = buf->d14; 141 | *(double*) &state->V15 = buf->d15; 142 | 143 | state->X0 = (uint64_t) val; 144 | } 145 | -------------------------------------------------------------------------------- /Runtime/jmpWrappers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cpu.h" 4 | #include 5 | 6 | void wrap_setjmp(CpuState* state); 7 | void wrap_sigsetjmp(CpuState* state); 8 | void wrap_longjmp(CpuState* state); 9 | void wrap_siglongjmp(CpuState* state); 10 | -------------------------------------------------------------------------------- /Runtime/keychainTester.mm: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #include 4 | #include 5 | #include 6 | #include "gs.h" 7 | using namespace std; 8 | 9 | const char* canaryKey = "GS_KeychainCanaryUsername"; 10 | const char* canaryValue = "GS_KeychainCanaryPassword"; 11 | 12 | NSString* toNSS(const string& str) { 13 | return [NSString stringWithCString:str.c_str()]; 14 | } 15 | 16 | tuple getKeychainItem(const string& key) { 17 | auto query = @{ 18 | (id) kSecAttrAccount: (id) toNSS(key), 19 | (id) kSecClass: (id) kSecClassGenericPassword, 20 | (id) kSecAttrService: (id) @"GrinningSoul", 21 | (id) kSecAttrAccessible: (id) kSecAttrAccessibleWhenUnlocked, 22 | (id) kSecReturnRef: @YES 23 | }; 24 | CFTypeRef res; 25 | auto ret = SecItemCopyMatching((__bridge CFDictionaryRef) query, &res); 26 | if(ret != errSecSuccess) { 27 | log("SecItemCopyMatching failed with status code {}", ret); 28 | return {false, ""}; 29 | } 30 | 31 | auto data = (__bridge NSData*) res; 32 | log("SecItemCopyMatching succeeded."); 33 | return {true, ""}; 34 | } 35 | 36 | bool putKeychainItem(const string& key, const string& value) { 37 | auto query = @{ 38 | (id) kSecAttrAccount: (id) toNSS(key), 39 | (id) kSecValueData: (id) [NSData dataWithBytes: (const void*) value.c_str() length: value.length()], 40 | (id) kSecClass: (id) kSecClassGenericPassword, 41 | (id) kSecAttrService: (id) @"GrinningSoul", 42 | (id) kSecAttrAccessible: (id) kSecAttrAccessibleWhenUnlocked 43 | }; 44 | auto ret = SecItemAdd((__bridge CFDictionaryRef) query, nullptr); 45 | if(ret == errSecSuccess) { 46 | log("SecItemAdd succeeded!"); 47 | return true; 48 | } 49 | log("SecItemAdd failed with status code {}", ret); 50 | return false; 51 | } 52 | 53 | bool isCanaryValueInKeychain() { 54 | auto [success, value] = getKeychainItem(canaryKey); 55 | return success;// && value == canaryValue; 56 | } 57 | 58 | bool isKeychainUsable() { 59 | if(isCanaryValueInKeychain()) 60 | return true; 61 | if(!putKeychainItem(canaryKey, canaryValue)) 62 | return false; 63 | return isCanaryValueInKeychain(); 64 | } 65 | -------------------------------------------------------------------------------- /Runtime/logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "fmt/format.h" 6 | 7 | void _log(const std::string& str); 8 | 9 | #define log(fmtstr, ...) do { _log(fmt::format("T#{:x} " fmtstr, (uint64_t) pthread_self(), ##__VA_ARGS__)); } while(0) 10 | -------------------------------------------------------------------------------- /Runtime/logging.mm: -------------------------------------------------------------------------------- 1 | #include "logging.h" 2 | #import 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | void _log(const string& str) { 8 | os_log(OS_LOG_DEFAULT, "%{public}@", [NSString stringWithUTF8String:str.c_str()]); 9 | } 10 | -------------------------------------------------------------------------------- /Runtime/objcWrappers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void wrap_objc_msgSend(CpuState* state); 4 | void wrap_objc_msgSendSuper2(CpuState* state); 5 | void wrap_objc_retainAutoreleasedReturnValue(CpuState* state); 6 | void wrap_class_getInstanceMethod(CpuState* state); 7 | void wrap_class_addMethod(CpuState* state); 8 | void wrap_class_replaceMethod(CpuState* state); 9 | void wrap_method_getImplementation(CpuState* state); 10 | void wrap_method_setImplementation(CpuState* state); 11 | void wrap_imp_implementationWithBlock(CpuState* state); 12 | -------------------------------------------------------------------------------- /Runtime/objcWrappers.mm: -------------------------------------------------------------------------------- 1 | #include "cpu.h" 2 | #include 3 | #include "objcWrappers.h" 4 | #include "trampoliner.h" 5 | #include 6 | #import 7 | #include 8 | using namespace std; 9 | 10 | extern unordered_map armSelReplacementFunctions; 11 | 12 | Method findMethod(Class cls, SEL op) { 13 | while(true) { 14 | auto method = class_getInstanceMethod(cls, op); 15 | if(method != nullptr) 16 | return method; 17 | auto scls = class_getSuperclass(cls); 18 | if(scls == cls) 19 | break; 20 | cls = scls; 21 | } 22 | return nullptr; 23 | } 24 | 25 | bool forward(id object, SEL selector, CpuState* state) { 26 | auto cls = object_getClass(object); 27 | auto forwarder = findMethod(cls, @selector(forwardInvocation:)); 28 | if(forwarder == nullptr) return false; 29 | auto signaturer = findMethod(cls, @selector(methodSignatureForSelector:)); 30 | if(signaturer == nullptr) { 31 | //log("Weird, forwardInvocation: is supported but not methodSignatureForSelector:?"); 32 | return false; 33 | } 34 | auto sigObject = [object methodSignatureForSelector: selector]; 35 | if(sigObject == nil) { 36 | //log("Could not get signature for selector in forward"); 37 | return false; 38 | } 39 | auto argCount = (NSUInteger) [sigObject numberOfArguments]; 40 | if(argCount > 6) { 41 | log("Attempted to forward message with signature over 6 arguments!"); 42 | BAILOUT(); 43 | } 44 | 45 | auto invocation = [NSInvocation invocationWithMethodSignature:sigObject]; 46 | [invocation setSelector: selector]; 47 | [invocation setTarget: object]; 48 | auto argI = 2, intI = 2, floatI = 0; 49 | for(; argI < argCount; ++argI) { 50 | auto argType = [sigObject getArgumentTypeAtIndex: argI]; 51 | switch(argType[0]) { 52 | case 'f': 53 | case 'd': 54 | [invocation setArgument: &state->V[floatI++] atIndex: argI]; 55 | break; 56 | default: 57 | [invocation setArgument: &state->X[intI++] atIndex: argI]; 58 | break; 59 | } 60 | } 61 | //log("NSInvocation created; forwarding"); 62 | [object forwardInvocation: invocation]; 63 | //log("Done forwarding!"); 64 | 65 | return true; 66 | } 67 | 68 | __attribute__((noinline)) void trapError() { 69 | } 70 | 71 | void wrap_recordError(id self, SEL op, id error) { 72 | log("Got recordError:! [{} recordError:] -- {}", class_getName(object_getClass(self)), [[error description] UTF8String]); 73 | trapError(); 74 | } 75 | 76 | void wrap_objc_msgSend(CpuState* state) { 77 | //log("In msgSend wrapper!"); 78 | //CpuInstance.dumpRegs(); 79 | id self = (id) state->X0; 80 | auto op = (SEL) state->X1; 81 | if(self == nil) { 82 | auto selName = op != nullptr ? sel_getName(op) : nullptr; 83 | if(selName != nullptr) 84 | log("Message to nil! Selector {}", selName); 85 | else 86 | log("Message to nil! BAD SELECTOR"); 87 | memset(&state->V0, 0, sizeof(vector128_float) * 4); 88 | return; 89 | } 90 | //log("Object at 0x" << hex << state->X0); 91 | auto cls = object_getClass(self); 92 | //log("Message to object of type " << class_getName(cls)); 93 | //log("Selector " << sel_getName(op)); 94 | log("###ARM->ObjC Message: [{} {}] to object 0x{:x} from 0x{:x}", class_getName(cls), sel_getName(op), (uint64_t) self, state->X30); 95 | auto csel = @selector(class); 96 | if(class_respondsToSelector(cls, csel)) { 97 | ((void (*)(id, SEL)) objc_msgSend)(self, csel); // Ensure initialization! 98 | //log("Called initializer, just in case"); 99 | } 100 | // else 101 | // log("Class seems to not respond to selector ... ?"); 102 | if(sel_isEqual(op, @selector(setAssertsEnabled:))) { 103 | log("HACK: FORCING ASSERTS IN TWITTER"); 104 | state->X2 = 1; 105 | } else if(sel_isEqual(op, @selector(recordError:))) { 106 | wrap_recordError(self, op, (id) state->X2); 107 | state->X0 = 0; 108 | return; 109 | } else if(sel_isEqual(op, @selector(network_URLSessionTaskOperation:didReceiveURLResponse:))) { 110 | id operation = (id) state->X2; 111 | auto desc = [operation description]; 112 | log("GOT CALL TO network_URLSessionTaskOperation:didReceiveURLResponse: with operation description {}", desc != nil ? [desc UTF8String] : "(nil)"); 113 | id resp = (id) state->X3; 114 | desc = [resp description]; 115 | log("GOT CALL TO network_URLSessionTaskOperation:didReceiveURLResponse: with response description {}", desc != nil ? [desc UTF8String] : "(nil)"); 116 | } else if(sel_isEqual(op, @selector(network_URLSessionTaskOperation:didStartSessionTaskWithRequest:))) { 117 | id operation = (id) state->X2; 118 | auto desc = [operation description]; 119 | log("GOT CALL TO network_URLSessionTaskOperation:didStartSessionTaskWithRequest: with operation description {}", desc != nil ? [desc UTF8String] : "(nil)"); 120 | id resp = (id) state->X3; 121 | desc = [resp description]; 122 | log("GOT CALL TO network_URLSessionTaskOperation:didStartSessionTaskWithRequest: with request description {}", desc != nil ? [desc UTF8String] : "(nil)"); 123 | desc = [[resp allHTTPHeaderFields] description]; 124 | log("GOT CALL TO network_URLSessionTaskOperation:didStartSessionTaskWithRequest: with request header fields description {}", desc != nil ? [desc UTF8String] : "(nil)"); 125 | } 126 | auto iter = armSelReplacementFunctions.find(op); 127 | if(iter != armSelReplacementFunctions.end()) { 128 | //log("This is a message to a function we've replaced!"); 129 | state->PC = iter->second; 130 | return; 131 | } 132 | auto method = findMethod(cls, op); 133 | if(method == nullptr) { 134 | //log("Unsupported selector. Attempting to find forwarding target"); 135 | 136 | if(class_respondsToSelector(cls, @selector(forwardingTargetForSelector:))) { 137 | //log("forwardingTargetForSelector: supported; trying to get target"); 138 | id newTarget = [self forwardingTargetForSelector: op]; 139 | if(newTarget != nil && newTarget != self) { 140 | //log("Got new target; restarting process."); 141 | state->X0 = (uint64_t) newTarget; 142 | wrap_objc_msgSend(state); 143 | return; 144 | } 145 | } 146 | 147 | //log("Falling back to an attempt to slow forward"); 148 | 149 | if(!forward(self, op, state)) { 150 | log("Got message to unsupported selector and could not find forward!"); 151 | state->X0 = 0; 152 | } 153 | return; 154 | } 155 | auto impl = (uint64_t) method_getImplementation(method); 156 | if(CpuInstance.isValidCodePointer(CodeSource::Execution, impl, state)) { 157 | //log("This is actually a message to our own ARM code!"); 158 | state->PC = impl; 159 | return; 160 | } 161 | auto inv = TrampolinerInstance.getKnownInverse(impl); 162 | if(inv != 0) { 163 | //log("This is actually a message to our own ARM code (via trampoline 0x{:x} to 0x{:x})!", impl, inv); 164 | state->PC = inv; 165 | } else { 166 | auto encoding = method_getTypeEncoding(method); 167 | //log("Method encoding {}", encoding); 168 | auto tramp = (TrampolineTrampoline*) TrampolinerInstance.getANTrampoline(impl, (boost::format("[%1% %2%]") % class_getName(cls) % sel_getName(op)).str(), encoding); 169 | //log("Got trampoline; calling"); 170 | ((void(*)())(tramp->trampoline & ~ARM_TO_NATIVE))(); 171 | //log("Returned from trampoline (to {:x})", state->X30); 172 | } 173 | } 174 | 175 | void wrap_objc_msgSendSuper2(CpuState* state) { 176 | //log("In msgSendSuper2 wrapper!"); 177 | //CpuInstance.dumpRegs(); 178 | auto super = (objc_super*) state->X0; 179 | //log("Super at 0x{:x}", state->X0); 180 | id self = super->receiver; 181 | if(self == nil) { 182 | log("Message to nil!"); 183 | state->PC = state->X30; 184 | return; 185 | } 186 | auto op = (SEL) state->X1; 187 | //log("Message to (superclass of) object of type " << class_getName(object_getClass(self))); 188 | auto cls = super->super_class; 189 | //log("First superclass " << class_getName(cls)); 190 | ((void(*)(id, SEL)) objc_msgSend)(self, sel_registerName("class")); // Ensure initialization! 191 | //log("Called initializer, just in case"); 192 | cls = class_getSuperclass(cls); 193 | //log("Second superclass " << class_getName(cls)); 194 | //log("###ARM->ObjC Super2 Message: [{} {}] to object 0x{:x} from 0x{:x}", class_getName(cls), sel_getName(op), (uint64_t) self, state->X30); 195 | state->X0 = (uint64_t) self; // Method expects self in x0, not objc_super* 196 | auto iter = armSelReplacementFunctions.find(op); 197 | if(iter != armSelReplacementFunctions.end()) { 198 | //log("This is a message to a function we've replaced!"); 199 | state->PC = iter->second; 200 | return; 201 | } 202 | auto method = findMethod(cls, op); 203 | if(method == nullptr) { 204 | //log("Unsupported selector. Attempting to find forwarding target"); 205 | 206 | if(class_respondsToSelector(cls, @selector(forwardingTargetForSelector:))) { 207 | //log("forwardingTargetForSelector: supported; trying to get target"); 208 | id newTarget = [self forwardingTargetForSelector: op]; 209 | if(newTarget != nil && newTarget != self) { 210 | //log("Got new target; restarting process."); 211 | state->X0 = (uint64_t) newTarget; 212 | wrap_objc_msgSend(state); 213 | return; 214 | } 215 | } 216 | 217 | //log("Falling back to an attempt to slow forward"); 218 | 219 | if(!forward(self, op, state)) { 220 | log("Got message to unsupported selector and could not find forward!"); 221 | state->X0 = 0; 222 | } 223 | return; 224 | } 225 | auto impl = (uint64_t) method_getImplementation(method); 226 | auto inv = TrampolinerInstance.getKnownInverse(impl); 227 | if(inv != 0) { 228 | //log("This is actually a message to our own ARM code via inverse trampoline!"); 229 | state->PC = inv; 230 | } else if(CpuInstance.isValidCodePointer(CodeSource::Execution, impl, state)) { 231 | //log("This is actually a message to our own ARM code!"); 232 | state->PC = impl; 233 | } else { 234 | auto encoding = method_getTypeEncoding(method); 235 | //log("Method encoding {}", encoding); 236 | auto tramp = (TrampolineTrampoline*) TrampolinerInstance.getANTrampoline(impl, (boost::format("[%1% %2%]") % class_getName(cls) % sel_getName(op)).str(), encoding); 237 | //log("Got trampoline; calling"); 238 | ((void(*)())(tramp->trampoline & ~ARM_TO_NATIVE))(); 239 | //log("Returned from trampoline"); 240 | } 241 | } 242 | 243 | void wrap_class_getInstanceMethod(CpuState* state) { 244 | auto cls = (Class) state->X0; 245 | auto name = (SEL) state->X1; 246 | log("Got class_getInstanceMethod -- requesting [{} {}]", class_getName(cls), sel_getName(name)); 247 | state->X0 = (uint64_t) class_getInstanceMethod(cls, name); 248 | log("class_getInstanceMethod returning {:x}", state->X0); 249 | } 250 | 251 | void wrap_class_addMethod(CpuState* state) { 252 | auto cls = (Class) state->X0; 253 | auto name = (SEL) state->X1; 254 | auto imp = (IMP) state->X2; 255 | auto types = (const char*) state->X3; 256 | log("Got class_addMethod -- adding [{} {}] with encoding {}", class_getName(cls), sel_getName(name), types); 257 | state->X0 = (uint64_t) class_addMethod(cls, name, (IMP) TrampolinerInstance.getNATrampoline((uint64_t) imp, fmt::format("[{} {}]", class_getName(cls), sel_getName(name)), types), types); 258 | log("class_addMethod returning {}", state->X0); 259 | } 260 | 261 | void wrap_class_replaceMethod(CpuState* state) { 262 | auto cls = (Class) state->X0; 263 | auto name = (SEL) state->X1; 264 | auto mname = sel_getName(name); 265 | auto imp = (IMP) state->X2; 266 | auto types = (const char*) state->X3; 267 | log("Got class_replaceMethod -- replacing [{} {}] with encoding {}", class_getName(cls), mname == nullptr ? "" : mname, types == nullptr ? "" : types); 268 | if(mname == nullptr || types == nullptr) { 269 | log("Couldn't get selector name or encoding; dropping."); 270 | state->X0 = 0; 271 | return; 272 | } 273 | state->X0 = (uint64_t) class_replaceMethod(cls, name, (IMP) TrampolinerInstance.getNATrampoline((uint64_t) imp, fmt::format("[{} {}]", class_getName(cls), mname == nullptr ? "" : mname), types), types); 274 | } 275 | 276 | void wrap_method_getImplementation(CpuState* state) { 277 | auto method = (Method) state->X0; 278 | if(method == nullptr) { 279 | state->X0 = 0; 280 | log("method_getImplementation got null method"); 281 | return; 282 | } 283 | auto mname = method_getName(method); 284 | if(mname == nullptr) { 285 | log("Could not get selector for method to method_getImplementation??"); 286 | state->X0 = 0; 287 | return; 288 | } 289 | auto name = sel_getName(mname); 290 | if(name == nullptr) { 291 | log("Got method name but not selector name in method_getImplementation??"); 292 | state->X0 = 0; 293 | return; 294 | } 295 | log("Got method_getImplementation -- {}", name); 296 | auto types = method_getTypeEncoding(method); 297 | auto addr = (uint64_t) method_getImplementation(method); 298 | auto tramp = TrampolinerInstance.asTrampoline(addr); 299 | if(tramp != nullptr || CpuInstance.isValidCodePointer(CodeSource::Speculation, addr, state)) { 300 | if(tramp == nullptr || tramp->isNativeToArm()) { 301 | log("ARM method -- returning direct address"); 302 | state->X0 = tramp != nullptr ? tramp->target : addr; 303 | } else { 304 | log("Native method via trampoline -- returning trampoline"); 305 | state->X0 = addr; 306 | } 307 | } else { 308 | log("Native method -- returning trampoline"); 309 | state->X0 = TrampolinerInstance.getANTrampoline(addr, name, types); 310 | } 311 | } 312 | 313 | void wrap_method_setImplementation(CpuState* state) { 314 | auto method = (Method) state->X0; 315 | auto newImp = (IMP) state->X1; 316 | auto name = sel_getName(method_getName(method)); 317 | log("Got method_setImplementation -- replacing {}", name); 318 | auto types = method_getTypeEncoding(method); 319 | auto oldImp = method_setImplementation(method, (IMP) TrampolinerInstance.getNATrampoline((uint64_t) newImp, fmt::format("replaced!!!{}", name), types)); 320 | if(TrampolinerInstance.asTrampoline((uint64_t) oldImp) != nullptr) 321 | state->X0 = (uint64_t) oldImp; 322 | else 323 | state->X0 = (uint64_t) TrampolinerInstance.getANTrampoline((uint64_t) oldImp, fmt::format("replaced!!!{}", name), types); 324 | } 325 | 326 | extern "C" { 327 | id objc_retainBlock(id value); 328 | } 329 | 330 | void wrap_imp_implementationWithBlock(CpuState* state) { 331 | log("Got imp_implementationWithBlock; returning AARch64 trampoline"); 332 | auto blockAddr = (uint64_t) objc_retainBlock((id) state->X0); 333 | auto block = (BlockInternal*) blockAddr; 334 | auto funcAddr = block->function; 335 | auto inv = TrampolinerInstance.getKnownInverse(funcAddr); 336 | if(inv != 0) funcAddr = inv; 337 | 338 | auto code = new uint8_t[10 * 4 + 3]; 339 | while((uint64_t) code & 3) code++; 340 | 341 | auto insts = (uint32_t*) code; 342 | insts[0] = 0xAA0003e1; // mov X1, X0 343 | 344 | insts[1] = 0xd2800000 | ((uint32_t) (blockAddr & 0xFFFF) << 5); // movz X0, block bits 0-15 345 | insts[2] = 0xf2a00000 | ((uint32_t) ((blockAddr >> 16) & 0xFFFF) << 5); // movk X0, block bits 16-31, LSL 16 346 | insts[3] = 0xf2c00000 | ((uint32_t) ((blockAddr >> 32) & 0xFFFF) << 5); // movk X0, block bits 32-47, LSL 32 347 | insts[4] = 0xf2e00000 | ((uint32_t) ((blockAddr >> 48) & 0xFFFF) << 5); // movk X0, block bits 48-63, LSL 48 348 | 349 | insts[5] = 0xd2800009 | ((uint32_t) (funcAddr & 0xFFFF) << 5); // movz X9, func bits 0-15 350 | insts[6] = 0xf2a00009 | ((uint32_t) ((funcAddr >> 16) & 0xFFFF) << 5); // movk X9, func bits 16-31, LSL 16 351 | insts[7] = 0xf2c00009 | ((uint32_t) ((funcAddr >> 32) & 0xFFFF) << 5); // movk X9, func bits 32-47, LSL 32 352 | insts[8] = 0xf2e00009 | ((uint32_t) ((funcAddr >> 48) & 0xFFFF) << 5); // movk X9, func bits 48-63, LSL 48 353 | 354 | insts[9] = 0xd61f0120; // br X9 355 | 356 | state->X0 = (uint64_t) code; 357 | } 358 | -------------------------------------------------------------------------------- /Runtime/objcppWrappers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void wrap_SecItemAdd(CpuState* state); 6 | void wrap_SecItemCopyMatching(CpuState* state); 7 | -------------------------------------------------------------------------------- /Runtime/objcppWrappers.mm: -------------------------------------------------------------------------------- 1 | #import 2 | #include 3 | #include "gs.h" 4 | 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | void hack_printRecursiveDescription(id object) { 10 | log("Recursive description of object 0x{:x}", (uint64_t) object); 11 | log("Description: {}", [[object recursiveDescription] UTF8String]); 12 | BAILOUT(); 13 | } 14 | 15 | static inline unsigned char itoh(int i) { 16 | if (i > 9) return 'A' + (i - 10); 17 | return '0' + i; 18 | } 19 | 20 | NSString * NSDataToHex(NSData *data) { 21 | NSUInteger i, len; 22 | unsigned char *buf, *bytes; 23 | 24 | len = data.length; 25 | bytes = (unsigned char*)data.bytes; 26 | buf = new unsigned char[len*2]; 27 | 28 | for (i=0; i> 4) & 0xF); 30 | buf[i*2+1] = itoh(bytes[i] & 0xF); 31 | } 32 | 33 | return [[NSString alloc] initWithBytesNoCopy:buf 34 | length:len*2 35 | encoding:NSASCIIStringEncoding 36 | freeWhenDone:YES]; 37 | } 38 | 39 | void wrap_SecItemAdd(CpuState* state) { 40 | log("In wrapper for SecItemAdd!"); 41 | auto attributes = (CFDictionaryRef) state->X0; 42 | auto result = (CFTypeRef*) state->X1; 43 | auto count = CFDictionaryGetCount(attributes); 44 | log("Attributes contains {} kv pairs", count); 45 | auto keys = new void*[count]; 46 | auto values = new void*[count]; 47 | CFDictionaryGetKeysAndValues(attributes, (const void**) keys, (const void**) values); 48 | log("Got attributes"); 49 | for(auto i = 0; i < count; ++i) { 50 | auto klen = CFStringGetLength((CFStringRef) keys[i]); 51 | auto key = new char[klen + 1]; 52 | CFStringGetCString((CFStringRef) keys[i], key, klen + 1, kCFStringEncodingUTF8); 53 | log("\t {} -- {}: {}", i, key, [[(id) values[i] description] UTF8String]); 54 | delete[] key; 55 | } 56 | 57 | auto ret = SecItemAdd(attributes, result); 58 | log("SecItemAdd returned {}", ret); 59 | state->X0 = (uint64_t) ret; 60 | } 61 | 62 | void wrap_SecItemCopyMatching(CpuState* state) { 63 | log("In wrapper for SecItemCopyMatching!"); 64 | auto attributes = (CFDictionaryRef) state->X0; 65 | auto result = (CFTypeRef*) state->X1; 66 | auto count = CFDictionaryGetCount(attributes); 67 | log("Attributes contains {} kv pairs", count); 68 | auto keys = new void*[count]; 69 | auto values = new void*[count]; 70 | CFDictionaryGetKeysAndValues(attributes, (const void**) keys, (const void**) values); 71 | log("Got attributes"); 72 | for(auto i = 0; i < count; ++i) { 73 | auto klen = CFStringGetLength((CFStringRef) keys[i]); 74 | auto key = new char[klen + 1]; 75 | CFStringGetCString((CFStringRef) keys[i], key, klen + 1, kCFStringEncodingUTF8); 76 | log("\t {} -- {}: {}", i, key, [[(id) values[i] description] UTF8String]); 77 | delete[] key; 78 | } 79 | 80 | auto ret = SecItemCopyMatching(attributes, result); 81 | log("SecItemCopyMatching returned {}", ret); 82 | if(result != nullptr && *result != nullptr) { 83 | id obj = (__bridge id) *result; 84 | if([obj isKindOfClass:[NSData class]]) 85 | log("SecItemCopyMatching returned this NSData: {}", [NSDataToHex((__bridge NSData*) *result) UTF8String]); 86 | else 87 | log("SecItemCopyMatching returned this data: {}", [[obj description] UTF8String]); 88 | } 89 | state->X0 = (uint64_t) ret; 90 | } 91 | -------------------------------------------------------------------------------- /Runtime/os.cc: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional OS-specific functionality 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | // Disable bogus MSVC warnings. 9 | #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) 10 | # define _CRT_SECURE_NO_WARNINGS 11 | #endif 12 | 13 | #include "fmt/os.h" 14 | 15 | #include 16 | 17 | #if FMT_USE_FCNTL 18 | # include 19 | # include 20 | 21 | # ifndef _WIN32 22 | # include 23 | # else 24 | # ifndef WIN32_LEAN_AND_MEAN 25 | # define WIN32_LEAN_AND_MEAN 26 | # endif 27 | # include 28 | # include 29 | 30 | # define O_CREAT _O_CREAT 31 | # define O_TRUNC _O_TRUNC 32 | 33 | # ifndef S_IRUSR 34 | # define S_IRUSR _S_IREAD 35 | # endif 36 | 37 | # ifndef S_IWUSR 38 | # define S_IWUSR _S_IWRITE 39 | # endif 40 | 41 | # ifdef __MINGW32__ 42 | # define _SH_DENYNO 0x40 43 | # endif 44 | # endif // _WIN32 45 | #endif // FMT_USE_FCNTL 46 | 47 | #ifdef _WIN32 48 | # include 49 | #endif 50 | 51 | #ifdef fileno 52 | # undef fileno 53 | #endif 54 | 55 | namespace { 56 | #ifdef _WIN32 57 | // Return type of read and write functions. 58 | using RWResult = int; 59 | 60 | // On Windows the count argument to read and write is unsigned, so convert 61 | // it from size_t preventing integer overflow. 62 | inline unsigned convert_rwcount(std::size_t count) { 63 | return count <= UINT_MAX ? static_cast(count) : UINT_MAX; 64 | } 65 | #else 66 | // Return type of read and write functions. 67 | using RWResult = ssize_t; 68 | 69 | inline std::size_t convert_rwcount(std::size_t count) { return count; } 70 | #endif 71 | } // namespace 72 | 73 | FMT_BEGIN_NAMESPACE 74 | 75 | #ifdef _WIN32 76 | internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) { 77 | if (int error_code = convert(s)) { 78 | FMT_THROW(windows_error(error_code, 79 | "cannot convert string from UTF-16 to UTF-8")); 80 | } 81 | } 82 | 83 | int internal::utf16_to_utf8::convert(wstring_view s) { 84 | if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; 85 | int s_size = static_cast(s.size()); 86 | if (s_size == 0) { 87 | // WideCharToMultiByte does not support zero length, handle separately. 88 | buffer_.resize(1); 89 | buffer_[0] = 0; 90 | return 0; 91 | } 92 | 93 | int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0, 94 | nullptr, nullptr); 95 | if (length == 0) return GetLastError(); 96 | buffer_.resize(length + 1); 97 | length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0], 98 | length, nullptr, nullptr); 99 | if (length == 0) return GetLastError(); 100 | buffer_[length] = 0; 101 | return 0; 102 | } 103 | 104 | void windows_error::init(int err_code, string_view format_str, 105 | format_args args) { 106 | error_code_ = err_code; 107 | memory_buffer buffer; 108 | internal::format_windows_error(buffer, err_code, vformat(format_str, args)); 109 | std::runtime_error& base = *this; 110 | base = std::runtime_error(to_string(buffer)); 111 | } 112 | 113 | void internal::format_windows_error(internal::buffer& out, int error_code, 114 | string_view message) FMT_NOEXCEPT { 115 | FMT_TRY { 116 | wmemory_buffer buf; 117 | buf.resize(inline_buffer_size); 118 | for (;;) { 119 | wchar_t* system_message = &buf[0]; 120 | int result = FormatMessageW( 121 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, 122 | error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message, 123 | static_cast(buf.size()), nullptr); 124 | if (result != 0) { 125 | utf16_to_utf8 utf8_message; 126 | if (utf8_message.convert(system_message) == ERROR_SUCCESS) { 127 | internal::writer w(out); 128 | w.write(message); 129 | w.write(": "); 130 | w.write(utf8_message); 131 | return; 132 | } 133 | break; 134 | } 135 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 136 | break; // Can't get error message, report error code instead. 137 | buf.resize(buf.size() * 2); 138 | } 139 | } 140 | FMT_CATCH(...) {} 141 | format_error_code(out, error_code, message); 142 | } 143 | 144 | void report_windows_error(int error_code, 145 | fmt::string_view message) FMT_NOEXCEPT { 146 | report_error(internal::format_windows_error, error_code, message); 147 | } 148 | #endif // _WIN32 149 | 150 | buffered_file::~buffered_file() FMT_NOEXCEPT { 151 | if (file_ && FMT_SYSTEM(fclose(file_)) != 0) 152 | report_system_error(errno, "cannot close file"); 153 | } 154 | 155 | buffered_file::buffered_file(cstring_view filename, cstring_view mode) { 156 | FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 157 | nullptr); 158 | if (!file_) 159 | FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); 160 | } 161 | 162 | void buffered_file::close() { 163 | if (!file_) return; 164 | int result = FMT_SYSTEM(fclose(file_)); 165 | file_ = nullptr; 166 | if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); 167 | } 168 | 169 | // A macro used to prevent expansion of fileno on broken versions of MinGW. 170 | #define FMT_ARGS 171 | 172 | int buffered_file::fileno() const { 173 | int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); 174 | if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); 175 | return fd; 176 | } 177 | 178 | #if FMT_USE_FCNTL 179 | file::file(cstring_view path, int oflag) { 180 | int mode = S_IRUSR | S_IWUSR; 181 | # if defined(_WIN32) && !defined(__MINGW32__) 182 | fd_ = -1; 183 | FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); 184 | # else 185 | FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); 186 | # endif 187 | if (fd_ == -1) 188 | FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); 189 | } 190 | 191 | file::~file() FMT_NOEXCEPT { 192 | // Don't retry close in case of EINTR! 193 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 194 | if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) 195 | report_system_error(errno, "cannot close file"); 196 | } 197 | 198 | void file::close() { 199 | if (fd_ == -1) return; 200 | // Don't retry close in case of EINTR! 201 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 202 | int result = FMT_POSIX_CALL(close(fd_)); 203 | fd_ = -1; 204 | if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); 205 | } 206 | 207 | long long file::size() const { 208 | # ifdef _WIN32 209 | // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT 210 | // is less than 0x0500 as is the case with some default MinGW builds. 211 | // Both functions support large file sizes. 212 | DWORD size_upper = 0; 213 | HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); 214 | DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); 215 | if (size_lower == INVALID_FILE_SIZE) { 216 | DWORD error = GetLastError(); 217 | if (error != NO_ERROR) 218 | FMT_THROW(windows_error(GetLastError(), "cannot get file size")); 219 | } 220 | unsigned long long long_size = size_upper; 221 | return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; 222 | # else 223 | using Stat = struct stat; 224 | Stat file_stat = Stat(); 225 | if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) 226 | FMT_THROW(system_error(errno, "cannot get file attributes")); 227 | static_assert(sizeof(long long) >= sizeof(file_stat.st_size), 228 | "return type of file::size is not large enough"); 229 | return file_stat.st_size; 230 | # endif 231 | } 232 | 233 | std::size_t file::read(void* buffer, std::size_t count) { 234 | RWResult result = 0; 235 | FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); 236 | if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); 237 | return internal::to_unsigned(result); 238 | } 239 | 240 | std::size_t file::write(const void* buffer, std::size_t count) { 241 | RWResult result = 0; 242 | FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); 243 | if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); 244 | return internal::to_unsigned(result); 245 | } 246 | 247 | file file::dup(int fd) { 248 | // Don't retry as dup doesn't return EINTR. 249 | // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html 250 | int new_fd = FMT_POSIX_CALL(dup(fd)); 251 | if (new_fd == -1) 252 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); 253 | return file(new_fd); 254 | } 255 | 256 | void file::dup2(int fd) { 257 | int result = 0; 258 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 259 | if (result == -1) { 260 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}", 261 | fd_, fd)); 262 | } 263 | } 264 | 265 | void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT { 266 | int result = 0; 267 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 268 | if (result == -1) ec = error_code(errno); 269 | } 270 | 271 | void file::pipe(file& read_end, file& write_end) { 272 | // Close the descriptors first to make sure that assignments don't throw 273 | // and there are no leaks. 274 | read_end.close(); 275 | write_end.close(); 276 | int fds[2] = {}; 277 | # ifdef _WIN32 278 | // Make the default pipe capacity same as on Linux 2.6.11+. 279 | enum { DEFAULT_CAPACITY = 65536 }; 280 | int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); 281 | # else 282 | // Don't retry as the pipe function doesn't return EINTR. 283 | // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html 284 | int result = FMT_POSIX_CALL(pipe(fds)); 285 | # endif 286 | if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe")); 287 | // The following assignments don't throw because read_fd and write_fd 288 | // are closed. 289 | read_end = file(fds[0]); 290 | write_end = file(fds[1]); 291 | } 292 | 293 | buffered_file file::fdopen(const char* mode) { 294 | // Don't retry as fdopen doesn't return EINTR. 295 | FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); 296 | if (!f) 297 | FMT_THROW( 298 | system_error(errno, "cannot associate stream with file descriptor")); 299 | buffered_file bf(f); 300 | fd_ = -1; 301 | return bf; 302 | } 303 | 304 | long getpagesize() { 305 | # ifdef _WIN32 306 | SYSTEM_INFO si; 307 | GetSystemInfo(&si); 308 | return si.dwPageSize; 309 | # else 310 | long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); 311 | if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size")); 312 | return size; 313 | # endif 314 | } 315 | #endif // FMT_USE_FCNTL 316 | FMT_END_NAMESPACE 317 | -------------------------------------------------------------------------------- /Runtime/record.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "repr.h" 5 | 6 | #define EXTERNAL_INIT ~~external-init~~ 7 | #define IF_NOT_EXTERNAL_INIT(elem, x) BOOST_PP_IIF(BOOST_PP_IS_BEGIN_PARENS(elem), BOOST_PP_IDENTITY(x), BOOST_PP_EMPTY)() 8 | #define CONTAINS_EXTERNAL_INIT_OP(s, state, elem) BOOST_PP_OR(state, BOOST_PP_NOT(BOOST_PP_IS_BEGIN_PARENS(elem))) 9 | #define CONTAINS_EXTERNAL_INIT(seq) BOOST_PP_SEQ_FOLD_LEFT(CONTAINS_EXTERNAL_INIT_OP, 0, seq) 10 | 11 | #define AS_STRUCT(name, ...) (struct, name, BOOST_PP_TUPLE_TO_SEQ((IRepr, ##__VA_ARGS__))) 12 | #define AS_CLASS(name, ...) (class, name, BOOST_PP_TUPLE_TO_SEQ((IRepr, ##__VA_ARGS__))) 13 | 14 | #define MAKE_ONE_VARIABLE(r, data, elem) \ 15 | IF_NOT_EXTERNAL_INIT(elem, BOOST_PP_TUPLE_ELEM(0, elem) BOOST_PP_TUPLE_ELEM(1, elem);) 16 | #define ARG_ONE_VARIABLE(r, data, elem) \ 17 | IF_NOT_EXTERNAL_INIT(elem, (BOOST_PP_TUPLE_ELEM(0, elem) BOOST_PP_TUPLE_ELEM(1, elem))) 18 | #define INIT_ONE_VARIABLE(r, data, elem) \ 19 | IF_NOT_EXTERNAL_INIT(elem, (BOOST_PP_TUPLE_ELEM(1, elem)(std::move(BOOST_PP_TUPLE_ELEM(1, elem))))) 20 | #define INIT_EMPTY_ONE_VARIABLE(r, data, elem) \ 21 | IF_NOT_EXTERNAL_INIT(elem, (BOOST_PP_TUPLE_ELEM(1, elem)())) 22 | #define REPR_ONE_VARIABLE(r, data, elem) \ 23 | IF_NOT_EXTERNAL_INIT(elem, ret += (" " BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)) "=") + ::repr(BOOST_PP_TUPLE_ELEM(1, elem));) 24 | 25 | #define MAKE_PUBLIC(r, data, elem) (virtual public elem) 26 | 27 | #define _RECORD(baseType, ...) \ 28 | BOOST_PP_TUPLE_ELEM(0, baseType) BOOST_PP_TUPLE_ELEM(1, baseType) : BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(MAKE_PUBLIC, , BOOST_PP_TUPLE_ELEM(2, baseType))) { \ 29 | public: \ 30 | BOOST_PP_SEQ_FOR_EACH(MAKE_ONE_VARIABLE, , BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ 31 | inline BOOST_PP_TUPLE_ELEM(1, baseType)() : BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(INIT_EMPTY_ONE_VARIABLE, , BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))) { __init(); } \ 32 | explicit inline BOOST_PP_TUPLE_ELEM(1, baseType)(BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(ARG_ONE_VARIABLE, , BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)))) : BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(INIT_ONE_VARIABLE, , BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))) { __init(); } \ 33 | inline std::string repr() const override { \ 34 | std::string ret = "<" BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, baseType)); \ 35 | BOOST_PP_SEQ_FOR_EACH(REPR_ONE_VARIABLE, , BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ 36 | return ret + ">"; \ 37 | } \ 38 | void __init() BOOST_PP_IF(CONTAINS_EXTERNAL_INIT(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)), ;, {}) \ 39 | } 40 | 41 | #define RECORD(baseType, ...) \ 42 | _RECORD(BOOST_PP_IF(BOOST_PP_IS_BEGIN_PARENS(baseType), baseType, AS_STRUCT(baseType)), __VA_ARGS__) 43 | 44 | #define MAKE_ONE_SUBVARIABLE(r, data, i, elem) \ 45 | BOOST_PP_IF(BOOST_PP_AND(BOOST_PP_IS_BEGIN_PARENS(elem), BOOST_PP_NOT_EQUAL(i, 0)), \ 46 | BOOST_PP_TUPLE_ELEM(0, elem) BOOST_PP_TUPLE_ELEM(1, elem); \ 47 | , \ 48 | ) 49 | #define ARG_ONE_SUBVARIABLE(r, data, i, elem) \ 50 | BOOST_PP_IF(BOOST_PP_AND(BOOST_PP_IS_BEGIN_PARENS(elem), BOOST_PP_NOT_EQUAL(i, 0)), \ 51 | (BOOST_PP_TUPLE_ELEM(0, elem) BOOST_PP_TUPLE_ELEM(1, elem)) \ 52 | , \ 53 | ) 54 | #define INIT_ONE_SUBVARIABLE(r, data, i, elem) \ 55 | BOOST_PP_IF(BOOST_PP_AND(BOOST_PP_IS_BEGIN_PARENS(elem), BOOST_PP_NOT_EQUAL(i, 0)), \ 56 | (BOOST_PP_TUPLE_ELEM(1, elem)(std::move(BOOST_PP_TUPLE_ELEM(1, elem)))) \ 57 | , \ 58 | ) 59 | #define INIT_EMPTY_ONE_SUBVARIABLE(r, data, i, elem) \ 60 | BOOST_PP_IF(BOOST_PP_AND(BOOST_PP_IS_BEGIN_PARENS(elem), BOOST_PP_NOT_EQUAL(i, 0)), \ 61 | (BOOST_PP_TUPLE_ELEM(1, elem)()) \ 62 | , \ 63 | ) 64 | #define REPR_ONE_SUBVARIABLE(r, data, i, elem) \ 65 | BOOST_PP_IF(BOOST_PP_AND(BOOST_PP_IS_BEGIN_PARENS(elem), BOOST_PP_NOT_EQUAL(i, 0)), \ 66 | ret += (" " BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)) "=") + ::repr(BOOST_PP_TUPLE_ELEM(1, elem)); \ 67 | , \ 68 | ) 69 | 70 | #define MAKE_ONE_SUBRECORD(r, baseType, elem) \ 71 | BOOST_PP_TUPLE_ELEM(0, baseType) BOOST_PP_TUPLE_ELEM(1, baseType)::BOOST_PP_TUPLE_ELEM(0, elem) : public BOOST_PP_TUPLE_ELEM(1, baseType) { \ 72 | public: \ 73 | BOOST_PP_SEQ_FOR_EACH_I(MAKE_ONE_SUBVARIABLE, , BOOST_PP_TUPLE_TO_SEQ(elem)) \ 74 | inline BOOST_PP_TUPLE_ELEM(0, elem)() : BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(INIT_EMPTY_ONE_SUBVARIABLE, , BOOST_PP_TUPLE_TO_SEQ(elem))) { __init(); } \ 75 | explicit inline BOOST_PP_TUPLE_ELEM(0, elem)(BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(ARG_ONE_SUBVARIABLE, , BOOST_PP_TUPLE_TO_SEQ(elem)))) : BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(INIT_ONE_SUBVARIABLE, , BOOST_PP_TUPLE_TO_SEQ(elem))) { __init(); } \ 76 | inline std::string repr() const override { \ 77 | std::string ret = "<" BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, baseType)) "::" BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(0, elem)); \ 78 | BOOST_PP_SEQ_FOR_EACH_I(REPR_ONE_SUBVARIABLE, , BOOST_PP_TUPLE_TO_SEQ(elem)) \ 79 | return ret + ">"; \ 80 | } \ 81 | inline bool BOOST_PP_CAT(is, BOOST_PP_TUPLE_ELEM(0, elem))() override { return true; } \ 82 | inline BOOST_PP_TUPLE_ELEM(0, elem)* BOOST_PP_CAT(as, BOOST_PP_TUPLE_ELEM(0, elem))() override { return this; } \ 83 | void __init() BOOST_PP_IF(CONTAINS_EXTERNAL_INIT(BOOST_PP_TUPLE_TO_SEQ(BOOST_PP_TUPLE_POP_FRONT(elem))), ;, {}) \ 84 | }; 85 | #define PREDEFINE_ONE_SUBRECORD(r, baseType, elem) \ 86 | BOOST_PP_TUPLE_ELEM(0, baseType) BOOST_PP_TUPLE_ELEM(0, elem); \ 87 | inline virtual bool BOOST_PP_CAT(is, BOOST_PP_TUPLE_ELEM(0, elem))() { return false; } \ 88 | inline virtual BOOST_PP_TUPLE_ELEM(0, elem)* BOOST_PP_CAT(as, BOOST_PP_TUPLE_ELEM(0, elem))() { return nullptr; } 89 | 90 | #define _DISCRIMINATED_UNION(baseType, ...) \ 91 | BOOST_PP_TUPLE_ELEM(0, baseType) BOOST_PP_TUPLE_ELEM(1, baseType) : BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(MAKE_PUBLIC, , BOOST_PP_TUPLE_ELEM(2, baseType))) { \ 92 | public: \ 93 | BOOST_PP_SEQ_FOR_EACH(PREDEFINE_ONE_SUBRECORD, baseType, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ 94 | }; \ 95 | BOOST_PP_SEQ_FOR_EACH(MAKE_ONE_SUBRECORD, baseType, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ 96 | 97 | #define DISCRIMINATED_UNION(baseType, ...) \ 98 | _DISCRIMINATED_UNION(BOOST_PP_IF(BOOST_PP_IS_BEGIN_PARENS(baseType), baseType, AS_STRUCT(baseType)), __VA_ARGS__) \ 99 | struct __fake_struct_unlikely_to_ever_be_used____why_would_anyone_use_this 100 | -------------------------------------------------------------------------------- /Runtime/replacements.generated.h: -------------------------------------------------------------------------------- 1 | vector> armReplacements = { 2 | { "_memcpy", "replace_memcpy" }, 3 | { "_memset", "replace_memset" }, 4 | { "_memmove", "replace_memmove" }, 5 | { "__swift_stdlib_operatingSystemVersion", "replace__swift_stdlib_operatingSystemVersion" }, 6 | { "_asl_log", "replace_asl_log" }, 7 | { "_asl_vlog", "replace_asl_vlog" }, 8 | { "_CFStringCreateWithFormatAndArguments", "replace_CFStringCreateWithFormatAndArguments" }, 9 | { "_printf", "replace_printf" }, 10 | { "_sprintf", "replace_sprintf" }, 11 | { "___sprintf_chk", "replace___sprintf_chk" }, 12 | { "_snprintf", "replace_snprintf" }, 13 | { "___snprintf_chk", "replace___snprintf_chk" }, 14 | { "_vprintf", "replace_vprintf" }, 15 | { "_vsnprintf", "replace_vsnprintf" }, 16 | { "___vsnprintf_chk", "replace___vsnprintf_chk" }, 17 | { "_fprintf", "replace_fprintf" }, 18 | { "_vfprintf", "replace_vfprintf" }, 19 | { "_vasprintf", "replace_vasprintf" }, 20 | { "_NSLogv", "replace_NSLogv" }, 21 | { "_NSLog", "replace_NSLog" }, 22 | }; 23 | vector> armSelReplacements = { 24 | { "arrayWithObjects:", "replace_arrayWithObjects_" }, 25 | { "setWithObjects:", "replace_setWithObjects_" }, 26 | { "dictionaryWithObjectsAndKeys:", "replace_dictionaryWithObjectsAndKeys_" }, 27 | { "initWithObjectsAndKeys:", "replace_initWithObjectsAndKeys_" }, 28 | { "initWithObjects:", "replace_initWithObjects_" }, 29 | { "initWithFormat:", "replace_initWithFormat_" }, 30 | { "initWithFormat:arguments:", "replace_initWithFormat_arguments_" }, 31 | { "deferredLocalizedIntentsStringWithFormat:", "replace_deferredLocalizedIntentsStringWithFormat_" }, 32 | { "initWithFormat:locale:", "replace_initWithFormat_locale_" }, 33 | { "initWithFormat:locale:arguments:", "replace_initWithFormat_locale_arguments_" }, 34 | { "stringByAppendingFormat:", "replace_stringByAppendingFormat_" }, 35 | { "appendFormat:", "replace_appendFormat_" }, 36 | { "stringWithFormat:", "replace_stringWithFormat_" }, 37 | { "predicateWithFormat:arguments:", "replace_predicateWithFormat_arguments_" }, 38 | { "predicateWithFormat:", "replace_predicateWithFormat_" }, 39 | }; 40 | -------------------------------------------------------------------------------- /Runtime/repr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class IRepr { 11 | public: 12 | virtual std::string repr() const = 0; 13 | }; 14 | 15 | inline std::string repr(IRepr& reprable) { 16 | return reprable.repr(); 17 | } 18 | 19 | template>> 20 | inline std::string repr(std::shared_ptr& reprable) { 21 | return std::static_pointer_cast(reprable)->repr(); 22 | } 23 | 24 | template 25 | inline std::string repr(const T* pointer) { 26 | if constexpr(std::is_base_of>()) 27 | return dynamic_cast(pointer)->repr(); 28 | else 29 | return (boost::format("(%1% *) 0x%2$x") % boost::core::demangle(typeid(T).name()) % (uint64_t) pointer).str(); 30 | } 31 | 32 | template 33 | inline std::string repr(const std::vector& vec) { 34 | std::string ret = (boost::format("vector<%1%>{") % boost::core::demangle(typeid(T).name())).str(); 35 | for(auto i = 0; i < vec.size(); ++i) { 36 | if(i != 0) 37 | ret += ", "; 38 | ret += repr(vec[i]); 39 | } 40 | return ret + "}"; 41 | } 42 | 43 | inline std::string repr(const std::string& str) { 44 | std::ostringstream ss; 45 | ss << std::quoted(str); 46 | return ss.str(); 47 | } 48 | 49 | inline std::string repr(int i) { 50 | return (boost::format("%1%") % i).str(); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Runtime/swiftDemangler.cpp: -------------------------------------------------------------------------------- 1 | #include "swiftDemangler.h" 2 | 3 | #include 4 | #include "record.h" 5 | using namespace std; 6 | 7 | string swiftNameToObjCEncoding(string name) { 8 | return ""; 9 | } 10 | -------------------------------------------------------------------------------- /Runtime/swiftDemangler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | std::string swiftNameToObjCEncoding(std::string name); 6 | -------------------------------------------------------------------------------- /Runtime/trampoliner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | class BaseSignature; 11 | 12 | const uint64_t ARM_TO_NATIVE = 1ULL << 62ULL; 13 | const uint64_t NATIVE_TO_ARM = 2ULL << 62ULL; 14 | 15 | struct TrampolineTrampoline { 16 | uint64_t trampoline, target; 17 | BaseSignature* signature; 18 | uint64_t canary; 19 | 20 | inline bool isArmToNative() { return (trampoline & ARM_TO_NATIVE) == ARM_TO_NATIVE; } 21 | inline bool isNativeToArm() { return (trampoline & NATIVE_TO_ARM) == NATIVE_TO_ARM; } 22 | }; 23 | 24 | class Trampoliner { 25 | public: 26 | Trampoliner(); 27 | uint64_t getKnownTrampoline(uint64_t target); 28 | uint64_t getKnownInverse(uint64_t target); 29 | uint64_t getANTrampoline(uint64_t target, const string& name, const string& signature); 30 | uint64_t getNATrampoline(uint64_t target, const string& name, const string& signature); 31 | uint64_t getANTrampoline(uint64_t target, BaseSignature* signature); 32 | uint64_t getNATrampoline(uint64_t target, BaseSignature* signature); 33 | TrampolineTrampoline* allocateTrampolineTrampoline(uint64_t trampoline, uint64_t target, BaseSignature* signature); 34 | inline TrampolineTrampoline* asTrampoline(uint64_t addr) { 35 | if(addr < (uint64_t) trampolineBase || addr >= (uint64_t) trampolineBase + sizeof(TrampolineTrampoline) * trampolineMax) 36 | return nullptr; 37 | return (TrampolineTrampoline*) addr; 38 | } 39 | 40 | inline void checkCanaries() { 41 | if(canary1 != 0xDEADBEEFCAFEBABEULL) { 42 | log("Canary 1 corrupted!"); 43 | BAILOUT(); 44 | } 45 | if(canary2 != 0xDEADBEEFCAFEBABEULL) { 46 | log("Canary 2 corrupted!"); 47 | BAILOUT(); 48 | } 49 | for(auto i = 0; i < trampolineCounter; ++i) { 50 | auto t = &trampolineBase[i]; 51 | if(~t->canary != t->target) { 52 | log("Canary value in trampoline {} at 0x{:x} invalid! Expected 0x{:x} and got 0x{:x}", i, (uint64_t) t, ~t->target); 53 | BAILOUT(); 54 | } 55 | } 56 | } 57 | 58 | uint64_t canary1 = 0xDEADBEEFCAFEBABEULL; 59 | mutex mutex; 60 | uint64_t canary2 = 0xDEADBEEFCAFEBABEULL; 61 | unordered_map *instances, *inverses; 62 | unordered_map armToNative, nativeToArm; 63 | static const uint64_t trampolineMax = 1048576; 64 | TrampolineTrampoline* trampolineBase; 65 | int trampolineCounter = 0; 66 | }; 67 | 68 | extern Trampoliner TrampolinerInstance; 69 | -------------------------------------------------------------------------------- /Runtime/wrappers.cpp: -------------------------------------------------------------------------------- 1 | #include "wrappers.h" 2 | #include "objcWrappers.h" 3 | #include "objcppWrappers.h" 4 | #include "jmpWrappers.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | using namespace std; 12 | 13 | void wrap_sscanf(CpuState* state) { 14 | auto match = (const char*) state->X0; 15 | auto pattern = (const char*) state->X1; 16 | log("In wrapper for sscanf"); 17 | log("Matching against '{}'", match); 18 | log("With pattern '{}'", pattern); 19 | auto sp = (void**) state->SP; 20 | auto _0 = *sp++; 21 | auto _1 = *sp++; 22 | auto _2 = *sp++; 23 | auto _3 = *sp++; 24 | auto _4 = *sp++; 25 | auto _5 = *sp++; 26 | auto _6 = *sp++; 27 | state->X0 = sscanf(match, pattern, _0, _1, _2, _3, _4, _5, _6); 28 | } 29 | 30 | void wrap_open(CpuState* state) { 31 | // This only exists because open() is fucking stupid and overloaded. 32 | log("In wrapper for open"); 33 | auto pathname = (const char*) state->X0; 34 | auto flags = (int) state->X1; 35 | if(flags & O_CREAT) 36 | state->X0 = open(pathname, flags, (int) state->X2); 37 | else 38 | state->X0 = open(pathname, flags); 39 | } 40 | 41 | void wrap_openat(CpuState* state) { 42 | // This only exists because openat() is fucking stupid and overloaded. 43 | log("In wrapper for openat"); 44 | auto dirfd = (int) state->X0; 45 | auto pathname = (const char*) state->X1; 46 | auto flags = (int) state->X2; 47 | if(flags & O_CREAT) 48 | state->X0 = openat(dirfd, pathname, flags, (int) state->X3); 49 | else 50 | state->X0 = openat(dirfd, pathname, flags); 51 | } 52 | 53 | void wrap_open_dprotected_np(CpuState* state) { 54 | // This only exists because open_dprotected_np() is fucking stupid and overloaded. 55 | log("In wrapper for open_dprotected_np"); 56 | auto pathname = (const char*) state->X0; 57 | auto flags = (int) state->X1; 58 | auto _class = state->X2; 59 | auto dpflags = state->X3; 60 | if(flags & O_CREAT) 61 | state->X0 = open_dprotected_np(pathname, flags, _class, dpflags, (int) state->X4); 62 | else 63 | state->X0 = open_dprotected_np(pathname, flags, _class, dpflags); 64 | } 65 | 66 | void wrap_task_set_exception_ports(CpuState* state) { 67 | log("App attempted to set exception ports; silently ignoring."); 68 | state->X0 = KERN_SUCCESS; 69 | } 70 | 71 | void wrap_NSSetUncaughtExceptionHandler(CpuState* state) { 72 | log("App attempted to set NSException handler; silently ignoring."); 73 | } 74 | 75 | void wrap___ZSt13set_terminatePFvvE(CpuState* state) { 76 | log("App attempted to set C++ exception handler; silently ignoring."); 77 | state->X0 = 0; 78 | } 79 | 80 | void wrap_malloc(CpuState* state) { 81 | state->X0 = (uint64_t) malloc(state->X0); 82 | } 83 | 84 | void wrap_calloc(CpuState* state) { 85 | state->X0 = (uint64_t) calloc(state->X0, state->X1); 86 | } 87 | 88 | void wrap_free(CpuState* state) { 89 | free((void*) state->X0); 90 | } 91 | 92 | void wrap_strlen(CpuState* state) { 93 | state->X0 = strlen((const char*) state->X0); 94 | } 95 | 96 | void wrap_memcpy(CpuState* state) { 97 | state->X0 = (uint64_t) memcpy((void*) state->X0, (void*) state->X1, (size_t) state->X2); 98 | } 99 | 100 | void wrap_memcmp(CpuState* state) { 101 | state->X0 = (uint64_t) (int64_t) memcmp((void*) state->X0, (void*) state->X1, (size_t) state->X2); 102 | } 103 | 104 | void wrap_strcmp(CpuState* state) { 105 | state->X0 = (uint64_t) (int64_t) strcmp((const char*) state->X0, (const char*) state->X1); 106 | } 107 | 108 | void wrap_strncmp(CpuState* state) { 109 | state->X0 = (uint64_t) (int64_t) strncmp((const char*) state->X0, (const char*) state->X1, (size_t) state->X2); 110 | } 111 | 112 | void wrap_bzero(CpuState* state) { 113 | bzero((void*) state->X0, (size_t) state->X1); 114 | } 115 | 116 | void wrap_write(CpuState* state) { 117 | state->X0 = (uint64_t) write((int) state->X0, (const void*) state->X1, (size_t) state->X2); 118 | } 119 | 120 | void wrap___chkstk_darwin(CpuState* state) { 121 | log("Got call to ___chkstk_darwin"); 122 | } 123 | 124 | void wrap__tlv_bootstrap(CpuState* state) { 125 | log("Caught and ignored tlv_bootstrap"); 126 | } 127 | 128 | void wrap_fread(CpuState* state) { 129 | state->X0 = (uint64_t) fread((void*) state->X0, (size_t) state->X1, (size_t) state->X2, (FILE*) state->X3); 130 | } 131 | 132 | static inline unsigned char itoh(int i) { 133 | if (i > 9) return 'A' + (i - 10); 134 | return '0' + i; 135 | } 136 | 137 | string bufferToHex(const void* data, const size_t len) { 138 | string ret; 139 | for(auto i = 0; i < len; ++i) { 140 | ret += itoh((((uint8_t*) data)[i] >> 4) & 0xF); 141 | ret += itoh(((uint8_t*) data)[i] & 0xF); 142 | } 143 | return ret; 144 | } 145 | 146 | void wrap_CCHmac(CpuState* state) { 147 | auto algo = (CCHmacAlgorithm) state->X0; 148 | auto key = (const void*) state->X1; 149 | auto keyLength = (size_t) state->X2; 150 | auto data = (const void*) state->X3; 151 | auto dataLength = (size_t) state->X4; 152 | auto macOut = (void*) state->X5; 153 | log("Got call to CCHmac. Key {} Data {}", bufferToHex(key, keyLength), bufferToHex(data, dataLength)); 154 | CCHmac(algo, key, keyLength, data, dataLength, macOut); 155 | log("CCHmac finished. MAC {}", bufferToHex(macOut, 20)); 156 | } 157 | 158 | void wrap_fesetenv(CpuState* state) { 159 | log("Got call to fesetenv; dropping."); 160 | } 161 | 162 | void wrap___assert_rtn(CpuState* state) { 163 | CpuInstance.dumpRegs(); 164 | log("Failed assertion (assert_rtn): function {} in {} (line {}) -- {}", (const char*) state->X0, (const char*) state->X1, state->X2, (const char*) state->X3); 165 | BAILOUT(); 166 | } 167 | 168 | vector> allWrappers = { 169 | {"libobjc.dylib", "_objc_msgSend", wrap_objc_msgSend}, 170 | {"libobjc.A.dylib", "_objc_msgSend", wrap_objc_msgSend}, 171 | {"libobjc.dylib", "_objc_msgSendSuper2", wrap_objc_msgSendSuper2}, 172 | {"libobjc.A.dylib", "_objc_msgSendSuper2", wrap_objc_msgSendSuper2}, 173 | {"libobjc.dylib", "_class_getInstanceMethod", wrap_class_getInstanceMethod}, 174 | {"libobjc.A.dylib", "_class_getInstanceMethod", wrap_class_getInstanceMethod}, 175 | {"libobjc.dylib", "_class_addMethod", wrap_class_addMethod}, 176 | {"libobjc.A.dylib", "_class_addMethod", wrap_class_addMethod}, 177 | {"libobjc.dylib", "_class_replaceMethod", wrap_class_replaceMethod}, 178 | {"libobjc.A.dylib", "_class_replaceMethod", wrap_class_replaceMethod}, 179 | {"libobjc.dylib", "_method_getImplementation", wrap_method_getImplementation}, 180 | {"libobjc.A.dylib", "_method_getImplementation", wrap_method_getImplementation}, 181 | {"libobjc.dylib", "_method_setImplementation", wrap_method_setImplementation}, 182 | {"libobjc.A.dylib", "_method_setImplementation", wrap_method_setImplementation}, 183 | {"libobjc.dylib", "_imp_implementationWithBlock", wrap_imp_implementationWithBlock}, 184 | {"libobjc.A.dylib", "_imp_implementationWithBlock", wrap_imp_implementationWithBlock}, 185 | {"libSystem.B.dylib", "_sscanf", wrap_sscanf}, 186 | {"libSystem.B.dylib", "_free", wrap_free}, 187 | {"libSystem.B.dylib", "_malloc", wrap_malloc}, 188 | {"libSystem.B.dylib", "_calloc", wrap_calloc}, 189 | {"libSystem.B.dylib", "_strlen", wrap_strlen}, 190 | {"libSystem.B.dylib", "_memcpy", wrap_memcpy}, 191 | {"libSystem.B.dylib", "_memcmp", wrap_memcmp}, 192 | {"libSystem.B.dylib", "_strcmp", wrap_strcmp}, 193 | {"libSystem.B.dylib", "_strncmp", wrap_strncmp}, 194 | {"libSystem.B.dylib", "_open", wrap_open}, 195 | {"libSystem.B.dylib", "_openat", wrap_openat}, 196 | {"libSystem.B.dylib", "_bzero", wrap_bzero}, 197 | {"libSystem.B.dylib", "_open_dprotected_np", wrap_open_dprotected_np}, 198 | {"libSystem.B.dylib", "_task_set_exception_ports", wrap_task_set_exception_ports}, 199 | {"libSystem.B.dylib", "___chkstk_darwin", wrap___chkstk_darwin}, 200 | {"libSystem.B.dylib", "__tlv_bootstrap", wrap__tlv_bootstrap}, 201 | {"libSystem.B.dylib", "_fread", wrap_fread}, 202 | {"libSystem.B.dylib", "_write", wrap_write}, 203 | {"libSystem.B.dylib", "_setjmp", wrap_setjmp}, 204 | {"libSystem.B.dylib", "_sigsetjmp", wrap_sigsetjmp}, 205 | {"libSystem.B.dylib", "_longjmp", wrap_longjmp}, 206 | {"libSystem.B.dylib", "_siglongjmp", wrap_siglongjmp}, 207 | {"libSystem.B.dylib", "_CCHmac", wrap_CCHmac}, 208 | {"libSystem.B.dylib", "_fesetenv", wrap_fesetenv}, 209 | {"libSystem.B.dylib", "___assert_rtn", wrap___assert_rtn}, 210 | {"Foundation", "_NSSetUncaughtExceptionHandler", wrap_NSSetUncaughtExceptionHandler}, 211 | {"libc++.1.dylib", "__ZSt13set_terminatePFvvE", wrap___ZSt13set_terminatePFvvE}, 212 | {"Security", "_SecItemAdd", wrap_SecItemAdd}, 213 | {"Security", "_SecItemCopyMatching", wrap_SecItemCopyMatching}, 214 | }; 215 | -------------------------------------------------------------------------------- /Runtime/wrappers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gs.h" 4 | #include 5 | #include 6 | #include 7 | 8 | extern std::vector> allWrappers; 9 | -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/Info.plist -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/Base.lproj/Main.storyboardc/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/Base.lproj/Main.storyboardc/Info.plist -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/Frameworks/NativeSide.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/Frameworks/NativeSide.framework/Info.plist -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/Frameworks/NativeSide.framework/NativeSide: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/Frameworks/NativeSide.framework/NativeSide -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/Frameworks/NativeSide.framework/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Info.plist 8 | 9 | DsJI1vxgmWcSAHs12eVJt8yyRh0= 10 | 11 | 12 | files2 13 | 14 | rules 15 | 16 | ^.* 17 | 18 | ^.*\.lproj/ 19 | 20 | optional 21 | 22 | weight 23 | 1000 24 | 25 | ^.*\.lproj/locversion.plist$ 26 | 27 | omit 28 | 29 | weight 30 | 1100 31 | 32 | ^Base\.lproj/ 33 | 34 | weight 35 | 1010 36 | 37 | ^version.plist$ 38 | 39 | 40 | rules2 41 | 42 | .*\.dSYM($|/) 43 | 44 | weight 45 | 11 46 | 47 | ^(.*/)?\.DS_Store$ 48 | 49 | omit 50 | 51 | weight 52 | 2000 53 | 54 | ^.* 55 | 56 | ^.*\.lproj/ 57 | 58 | optional 59 | 60 | weight 61 | 1000 62 | 63 | ^.*\.lproj/locversion.plist$ 64 | 65 | omit 66 | 67 | weight 68 | 1100 69 | 70 | ^Base\.lproj/ 71 | 72 | weight 73 | 1010 74 | 75 | ^Info\.plist$ 76 | 77 | omit 78 | 79 | weight 80 | 20 81 | 82 | ^PkgInfo$ 83 | 84 | omit 85 | 86 | weight 87 | 20 88 | 89 | ^embedded\.provisionprofile$ 90 | 91 | weight 92 | 20 93 | 94 | ^version\.plist$ 95 | 96 | weight 97 | 20 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/GSWrapper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/GSWrapper -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 18G103 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleExecutable 10 | GSWrapper 11 | CFBundleIdentifier 12 | dev.daeken.SwiftTrampolineTests 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | SwiftTrampolineTests ARM 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSupportedPlatforms 22 | 23 | iPhoneOS 24 | 25 | CFBundleVersion 26 | 1 27 | DTCompiler 28 | com.apple.compilers.llvm.clang.1_0 29 | DTPlatformBuild 30 | 17B102 31 | DTPlatformName 32 | iphoneos 33 | DTPlatformVersion 34 | 13.2 35 | DTSDKBuild 36 | 17B102 37 | DTSDKName 38 | iphoneos13.2 39 | DTXcode 40 | 1130 41 | DTXcodeBuild 42 | 11C504 43 | LSRequiresIPhoneOS 44 | 45 | MinimumOSVersion 46 | 13.2 47 | UIApplicationSceneManifest 48 | 49 | UIApplicationSupportsMultipleScenes 50 | 51 | UISceneConfigurations 52 | 53 | UIWindowSceneSessionRoleApplication 54 | 55 | 56 | UISceneConfigurationName 57 | Default Configuration 58 | UISceneDelegateClassName 59 | SwiftTrampolineTests.SceneDelegate 60 | UISceneStoryboardFile 61 | Main 62 | 63 | 64 | 65 | 66 | UIDeviceFamily 67 | 68 | 1 69 | 2 70 | 71 | UILaunchStoryboardName 72 | LaunchScreen 73 | UIMainStoryboardFile 74 | Main 75 | UIRequiredDeviceCapabilities 76 | 77 | arm64 78 | 79 | UISupportedInterfaceOrientations 80 | 81 | UIInterfaceOrientationPortrait 82 | UIInterfaceOrientationLandscapeLeft 83 | UIInterfaceOrientationLandscapeRight 84 | 85 | UISupportedInterfaceOrientations~ipad 86 | 87 | UIInterfaceOrientationPortrait 88 | UIInterfaceOrientationPortraitUpsideDown 89 | UIInterfaceOrientationLandscapeLeft 90 | UIInterfaceOrientationLandscapeRight 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/PkgInfo: -------------------------------------------------------------------------------- 1 | APPL???? -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/SwiftTrampolineTests: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/SwiftTrampolineTests -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/SwiftTrampolineTests.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/SwiftTrampolineTests.dylib -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib 8 | 9 | ohbGzsQIUgIbcruywsxyqsgfnig= 10 | 11 | Base.lproj/LaunchScreen.storyboardc/Info.plist 12 | 13 | n2t8gsDpfE6XkhG31p7IQJRxTxU= 14 | 15 | Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib 16 | 17 | l0FgMdjxiHF9mmmA9N9D8K9bBd8= 18 | 19 | Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib 20 | 21 | GYDoMJZFs2zjHm6hbukzeEJxkX0= 22 | 23 | Base.lproj/Main.storyboardc/Info.plist 24 | 25 | MDrKFvFWroTb0+KEbQShBcoBvo4= 26 | 27 | Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib 28 | 29 | YDXl38Rkw9iqOkXl7EimnXdfduc= 30 | 31 | Frameworks/NativeSide.framework/Info.plist 32 | 33 | gC0vU+mqlQ7Nef1HBrFDLU6Jc4A= 34 | 35 | Frameworks/NativeSide.framework/NativeSide 36 | 37 | XBsVi+32c7CvZLuwlrzGtel3IAM= 38 | 39 | Frameworks/NativeSide.framework/_CodeSignature/CodeResources 40 | 41 | CzDBkJvRhMIkbkpVJtii5AodLGs= 42 | 43 | Info.plist 44 | 45 | oXs/L5eT61WX7xN2BZc/XnLSUV8= 46 | 47 | PkgInfo 48 | 49 | n57qDP4tZfLD1rCS43W0B4LQjzE= 50 | 51 | embedded.mobileprovision 52 | 53 | RnfxWxbyUevgrPvoUnAVHzGYtjg= 54 | 55 | 56 | files2 57 | 58 | Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib 59 | 60 | hash2 61 | 62 | EalA5hz1UPacNbsdWFz8+nbF0rygxm+XvyQDWDK41qM= 63 | 64 | 65 | Base.lproj/LaunchScreen.storyboardc/Info.plist 66 | 67 | hash2 68 | 69 | HyVdXMU7Ux4/KalAao30mpWOK/lEPT4gvYN09wf31cg= 70 | 71 | 72 | Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib 73 | 74 | hash2 75 | 76 | TgdaXzDS/hU1VtWi2xnFUK+yvtBCXqquMJcfgTiVnS8= 77 | 78 | 79 | Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib 80 | 81 | hash2 82 | 83 | YS9wv0SFc3d/cnFNdVu05JqAt8ImSWPmBH9jSvrPamA= 84 | 85 | 86 | Base.lproj/Main.storyboardc/Info.plist 87 | 88 | hash2 89 | 90 | PpvapAjR62rl6Ym4E6hkTgpKmBICxTaQXeUqcpHmmqQ= 91 | 92 | 93 | Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib 94 | 95 | hash2 96 | 97 | ThOWqfePsc+vH3CKrUXz0bmBvzMQ32uCqS7gZ3abhC0= 98 | 99 | 100 | Frameworks/NativeSide.framework/Info.plist 101 | 102 | hash2 103 | 104 | ksTJ4Ed5t8GnE5C74UHpBLkqQ8HuQrxaOh7L5wZdX6g= 105 | 106 | 107 | Frameworks/NativeSide.framework/NativeSide 108 | 109 | hash2 110 | 111 | H8yFgDD2g5FiCeps7NRWGj50g8IxlOXtGDR4qyUkexw= 112 | 113 | 114 | Frameworks/NativeSide.framework/_CodeSignature/CodeResources 115 | 116 | hash2 117 | 118 | a3sRHMwo/4a99QLVAT07q1zn/vqx74vDA3Ms1KX2ToU= 119 | 120 | 121 | embedded.mobileprovision 122 | 123 | hash2 124 | 125 | wSlPu0bMqza4Au44vQQQ3czk+Cz0Ah1sEXWpjT1yDws= 126 | 127 | 128 | 129 | rules 130 | 131 | ^.* 132 | 133 | ^.*\.lproj/ 134 | 135 | optional 136 | 137 | weight 138 | 1000 139 | 140 | ^.*\.lproj/locversion.plist$ 141 | 142 | omit 143 | 144 | weight 145 | 1100 146 | 147 | ^Base\.lproj/ 148 | 149 | weight 150 | 1010 151 | 152 | ^version.plist$ 153 | 154 | 155 | rules2 156 | 157 | .*\.dSYM($|/) 158 | 159 | weight 160 | 11 161 | 162 | ^(.*/)?\.DS_Store$ 163 | 164 | omit 165 | 166 | weight 167 | 2000 168 | 169 | ^.* 170 | 171 | ^.*\.lproj/ 172 | 173 | optional 174 | 175 | weight 176 | 1000 177 | 178 | ^.*\.lproj/locversion.plist$ 179 | 180 | omit 181 | 182 | weight 183 | 1100 184 | 185 | ^Base\.lproj/ 186 | 187 | weight 188 | 1010 189 | 190 | ^Info\.plist$ 191 | 192 | omit 193 | 194 | weight 195 | 20 196 | 197 | ^PkgInfo$ 198 | 199 | omit 200 | 201 | weight 202 | 20 203 | 204 | ^embedded\.provisionprofile$ 205 | 206 | weight 207 | 20 208 | 209 | ^version\.plist$ 210 | 211 | weight 212 | 20 213 | 214 | 215 | 216 | 217 | -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/embedded.mobileprovision: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/embedded.mobileprovision -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/gsinit.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/gsinit.bin -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/libarmruntime.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/libarmruntime.dylib -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/libc--.1.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/libc--.1.dylib -------------------------------------------------------------------------------- /SwiftTrampolineTests.app/libc--abi.1.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/SwiftTrampolineTests.app/libc--abi.1.dylib -------------------------------------------------------------------------------- /TrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib/objects-13.0+.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib/objects-13.0+.nib -------------------------------------------------------------------------------- /TrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib/runtime.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib/runtime.nib -------------------------------------------------------------------------------- /TrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/Info.plist -------------------------------------------------------------------------------- /TrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib/objects-13.0+.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib/objects-13.0+.nib -------------------------------------------------------------------------------- /TrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib/runtime.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib/runtime.nib -------------------------------------------------------------------------------- /TrampolineTests.app/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib/objects-13.0+.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib/objects-13.0+.nib -------------------------------------------------------------------------------- /TrampolineTests.app/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib/runtime.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib/runtime.nib -------------------------------------------------------------------------------- /TrampolineTests.app/Base.lproj/Main.storyboardc/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Base.lproj/Main.storyboardc/Info.plist -------------------------------------------------------------------------------- /TrampolineTests.app/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib/objects-13.0+.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib/objects-13.0+.nib -------------------------------------------------------------------------------- /TrampolineTests.app/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib/runtime.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib/runtime.nib -------------------------------------------------------------------------------- /TrampolineTests.app/Frameworks/NativeSide.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Frameworks/NativeSide.framework/Info.plist -------------------------------------------------------------------------------- /TrampolineTests.app/Frameworks/NativeSide.framework/NativeSide: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/Frameworks/NativeSide.framework/NativeSide -------------------------------------------------------------------------------- /TrampolineTests.app/Frameworks/NativeSide.framework/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Info.plist 8 | 9 | onOkwsG8qc0jg5uxmyR8xo6Y6KQ= 10 | 11 | 12 | files2 13 | 14 | rules 15 | 16 | ^.* 17 | 18 | ^.*\.lproj/ 19 | 20 | optional 21 | 22 | weight 23 | 1000 24 | 25 | ^.*\.lproj/locversion.plist$ 26 | 27 | omit 28 | 29 | weight 30 | 1100 31 | 32 | ^Base\.lproj/ 33 | 34 | weight 35 | 1010 36 | 37 | ^version.plist$ 38 | 39 | 40 | rules2 41 | 42 | .*\.dSYM($|/) 43 | 44 | weight 45 | 11 46 | 47 | ^(.*/)?\.DS_Store$ 48 | 49 | omit 50 | 51 | weight 52 | 2000 53 | 54 | ^.* 55 | 56 | ^.*\.lproj/ 57 | 58 | optional 59 | 60 | weight 61 | 1000 62 | 63 | ^.*\.lproj/locversion.plist$ 64 | 65 | omit 66 | 67 | weight 68 | 1100 69 | 70 | ^Base\.lproj/ 71 | 72 | weight 73 | 1010 74 | 75 | ^Info\.plist$ 76 | 77 | omit 78 | 79 | weight 80 | 20 81 | 82 | ^PkgInfo$ 83 | 84 | omit 85 | 86 | weight 87 | 20 88 | 89 | ^embedded\.provisionprofile$ 90 | 91 | weight 92 | 20 93 | 94 | ^version\.plist$ 95 | 96 | weight 97 | 20 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /TrampolineTests.app/GSWrapper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/GSWrapper -------------------------------------------------------------------------------- /TrampolineTests.app/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 18G103 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleExecutable 10 | GSWrapper 11 | CFBundleIdentifier 12 | dev.daeken.TrampolineTests 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | TrampolineTests ARM 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSupportedPlatforms 22 | 23 | iPhoneOS 24 | 25 | CFBundleVersion 26 | 1 27 | DTCompiler 28 | com.apple.compilers.llvm.clang.1_0 29 | DTPlatformBuild 30 | 17B102 31 | DTPlatformName 32 | iphoneos 33 | DTPlatformVersion 34 | 13.2 35 | DTSDKBuild 36 | 17B102 37 | DTSDKName 38 | iphoneos13.2 39 | DTXcode 40 | 1130 41 | DTXcodeBuild 42 | 11C504 43 | LSRequiresIPhoneOS 44 | 45 | MinimumOSVersion 46 | 12.1 47 | UIApplicationSceneManifest 48 | 49 | UIApplicationSupportsMultipleScenes 50 | 51 | UISceneConfigurations 52 | 53 | UIWindowSceneSessionRoleApplication 54 | 55 | 56 | UISceneConfigurationName 57 | Default Configuration 58 | UISceneDelegateClassName 59 | SceneDelegate 60 | UISceneStoryboardFile 61 | Main 62 | 63 | 64 | 65 | 66 | UIDeviceFamily 67 | 68 | 1 69 | 2 70 | 71 | UILaunchStoryboardName 72 | LaunchScreen 73 | UIMainStoryboardFile 74 | Main 75 | UIRequiredDeviceCapabilities 76 | 77 | arm64 78 | 79 | UISupportedInterfaceOrientations 80 | 81 | UIInterfaceOrientationPortrait 82 | UIInterfaceOrientationLandscapeLeft 83 | UIInterfaceOrientationLandscapeRight 84 | 85 | UISupportedInterfaceOrientations~ipad 86 | 87 | UIInterfaceOrientationPortrait 88 | UIInterfaceOrientationPortraitUpsideDown 89 | UIInterfaceOrientationLandscapeLeft 90 | UIInterfaceOrientationLandscapeRight 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /TrampolineTests.app/PkgInfo: -------------------------------------------------------------------------------- 1 | APPL???? -------------------------------------------------------------------------------- /TrampolineTests.app/TrampolineTests: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/TrampolineTests -------------------------------------------------------------------------------- /TrampolineTests.app/TrampolineTests.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/TrampolineTests.dylib -------------------------------------------------------------------------------- /TrampolineTests.app/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib/objects-13.0+.nib 8 | 9 | EDABvdmDS1ejZE8oP/2bMBPIqtQ= 10 | 11 | Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib/runtime.nib 12 | 13 | Bn5WjzUShaKp3UXLyWouwNatp8o= 14 | 15 | Base.lproj/LaunchScreen.storyboardc/Info.plist 16 | 17 | n2t8gsDpfE6XkhG31p7IQJRxTxU= 18 | 19 | Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib/objects-13.0+.nib 20 | 21 | i5tqP3+YyDpMuI67SkAMG65Htcs= 22 | 23 | Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib/runtime.nib 24 | 25 | FAIwThVKhAh2dIGgWI9uLEkkxRY= 26 | 27 | Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib/objects-13.0+.nib 28 | 29 | z6QlrbU2XfEC7Yeu+1MEuEfydQ8= 30 | 31 | Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib/runtime.nib 32 | 33 | LFu39N0HiDH04fqZULs7X5sLZNs= 34 | 35 | Base.lproj/Main.storyboardc/Info.plist 36 | 37 | MDrKFvFWroTb0+KEbQShBcoBvo4= 38 | 39 | Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib/objects-13.0+.nib 40 | 41 | Ym81DJ9ihYgYjrGUH7GLdRM6OYM= 42 | 43 | Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib/runtime.nib 44 | 45 | FDUJxie1phY3QQdKpYIYw2raQSo= 46 | 47 | Frameworks/NativeSide.framework/Info.plist 48 | 49 | IfmGeGyI22mkINieGtb7CjZvAR0= 50 | 51 | Frameworks/NativeSide.framework/NativeSide 52 | 53 | hdrdmi+6TcUvnnY6cxEGza7qmdg= 54 | 55 | Frameworks/NativeSide.framework/_CodeSignature/CodeResources 56 | 57 | hrIFBk1KGt7hpZyGkAof04nmrv8= 58 | 59 | Info.plist 60 | 61 | xPJ7mmbPPJHroZg4hhewxVsDyOY= 62 | 63 | PkgInfo 64 | 65 | n57qDP4tZfLD1rCS43W0B4LQjzE= 66 | 67 | embedded.mobileprovision 68 | 69 | TTyIEd9A5SZ+JL+1Bw4z4J4HQoY= 70 | 71 | 72 | files2 73 | 74 | Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib/objects-13.0+.nib 75 | 76 | hash2 77 | 78 | kRRA9c94+gNec2B6Y/oPd6sOace4IpfqmtY8C/bUQC0= 79 | 80 | 81 | Base.lproj/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib/runtime.nib 82 | 83 | hash2 84 | 85 | ST3HQaisX5VprV6YoleJSbOgP+cmZzdUCIx55g4//qI= 86 | 87 | 88 | Base.lproj/LaunchScreen.storyboardc/Info.plist 89 | 90 | hash2 91 | 92 | HyVdXMU7Ux4/KalAao30mpWOK/lEPT4gvYN09wf31cg= 93 | 94 | 95 | Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib/objects-13.0+.nib 96 | 97 | hash2 98 | 99 | tijskFbKbQ4lUZXbZsFGXiaBIbrR4Leic2n8DTTTR7g= 100 | 101 | 102 | Base.lproj/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib/runtime.nib 103 | 104 | hash2 105 | 106 | LVgmXcli5YMIZOklPFjwxZsQXc0ayOidwgrLWtewO9k= 107 | 108 | 109 | Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib/objects-13.0+.nib 110 | 111 | hash2 112 | 113 | Zd2kedsfD1Mjuep0p0hCFUe7dnQF+Lzi6I5BH2kCIik= 114 | 115 | 116 | Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib/runtime.nib 117 | 118 | hash2 119 | 120 | BvjNagqNTba28JFxVp1oITREaoCKD2G/wkbzfj/eLiA= 121 | 122 | 123 | Base.lproj/Main.storyboardc/Info.plist 124 | 125 | hash2 126 | 127 | PpvapAjR62rl6Ym4E6hkTgpKmBICxTaQXeUqcpHmmqQ= 128 | 129 | 130 | Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib/objects-13.0+.nib 131 | 132 | hash2 133 | 134 | B914uUizZP3cUJDP+tOpNauSM6vjD5CeBeZf1+sFxZk= 135 | 136 | 137 | Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib/runtime.nib 138 | 139 | hash2 140 | 141 | Myhv2Cz6Z1n03V3mMGtG2+FXe5h/cc7N3u7wL4lezII= 142 | 143 | 144 | Frameworks/NativeSide.framework/Info.plist 145 | 146 | hash2 147 | 148 | x74G7JjdbsQe1Y3WeNJVlRwLleZEd25J0o9Qm6dx4hw= 149 | 150 | 151 | Frameworks/NativeSide.framework/NativeSide 152 | 153 | hash2 154 | 155 | IHcvkln9muDshekR+SbLnBEM1ldO6/EpcAcrOcEwgWU= 156 | 157 | 158 | Frameworks/NativeSide.framework/_CodeSignature/CodeResources 159 | 160 | hash2 161 | 162 | 8IuLTn7/PZi6wvBeTGHqLB/Q7qqqLxUB4fi5fckYz2E= 163 | 164 | 165 | embedded.mobileprovision 166 | 167 | hash2 168 | 169 | pot1V2M9BnSLMC/VWgvQfFvWljQIflVenSIvOt6ayG0= 170 | 171 | 172 | 173 | rules 174 | 175 | ^.* 176 | 177 | ^.*\.lproj/ 178 | 179 | optional 180 | 181 | weight 182 | 1000 183 | 184 | ^.*\.lproj/locversion.plist$ 185 | 186 | omit 187 | 188 | weight 189 | 1100 190 | 191 | ^Base\.lproj/ 192 | 193 | weight 194 | 1010 195 | 196 | ^version.plist$ 197 | 198 | 199 | rules2 200 | 201 | .*\.dSYM($|/) 202 | 203 | weight 204 | 11 205 | 206 | ^(.*/)?\.DS_Store$ 207 | 208 | omit 209 | 210 | weight 211 | 2000 212 | 213 | ^.* 214 | 215 | ^.*\.lproj/ 216 | 217 | optional 218 | 219 | weight 220 | 1000 221 | 222 | ^.*\.lproj/locversion.plist$ 223 | 224 | omit 225 | 226 | weight 227 | 1100 228 | 229 | ^Base\.lproj/ 230 | 231 | weight 232 | 1010 233 | 234 | ^Info\.plist$ 235 | 236 | omit 237 | 238 | weight 239 | 20 240 | 241 | ^PkgInfo$ 242 | 243 | omit 244 | 245 | weight 246 | 20 247 | 248 | ^embedded\.provisionprofile$ 249 | 250 | weight 251 | 20 252 | 253 | ^version\.plist$ 254 | 255 | weight 256 | 20 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /TrampolineTests.app/embedded.mobileprovision: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/embedded.mobileprovision -------------------------------------------------------------------------------- /TrampolineTests.app/gsinit.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/gsinit.bin -------------------------------------------------------------------------------- /TrampolineTests.app/libarmruntime.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/libarmruntime.dylib -------------------------------------------------------------------------------- /TrampolineTests.app/libc--.1.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/libc--.1.dylib -------------------------------------------------------------------------------- /TrampolineTests.app/libc--abi.1.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/TrampolineTests.app/libc--abi.1.dylib -------------------------------------------------------------------------------- /buildSwiftTrampolineTests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #####pushd /Users/daeken/projects/SwiftTrampolineTests > /dev/null 4 | #####xcodebuild -configuration Debug -target SwiftTrampolineTests -arch x86_64 -sdk iphonesimulator13.2 > /dev/null 5 | ###### /Users/daeken/Library/Developer/Xcode/DerivedData/SwiftTrampolineTests-bokcuymepnhrwddxokpdxjgditxw/Build/Products/Debug-iphonesimulator/SwiftTrampolineTests.app 6 | #####xcodebuild -configuration Debug -target SwiftTrampolineTests -arch arm64 -sdk iphoneos13.2 > /dev/null 7 | ###### /Users/daeken/Library/Developer/Xcode/DerivedData/SwiftTrampolineTests-bokcuymepnhrwddxokpdxjgditxw/Build/Products/Debug-iphoneos/SwiftTrampolineTests.app 8 | ##### 9 | #####popd > /dev/null 10 | ##### 11 | #####rm -rf SwiftTrampolineTests.app 2>/dev/null 12 | #####cp -rf /Users/daeken/Library/Developer/Xcode/DerivedData/SwiftTrampolineTests-bokcuymepnhrwddxokpdxjgditxw/Build/Products/Debug-iphoneos/SwiftTrampolineTests.app SwiftTrampolineTestsArm.app 13 | #####rm -rf SwiftTrampolineTestsArm.app/Frameworks/NativeSide.framework 14 | #####cp -rf /Users/daeken/Library/Developer/Xcode/DerivedData/SwiftTrampolineTests-bokcuymepnhrwddxokpdxjgditxw/Build/Products/Debug-iphonesimulator/SwiftTrampolineTests.app/Frameworks/NativeSide.framework SwiftTrampolineTestsArm.app/Frameworks/ 15 | #####python wrap.py SwiftTrampolineTestsArm.app SwiftTrampolineTests.app 16 | #####rm -rf SwiftTrampolineTestsArm.app 2>/dev/null 17 | ##### 18 | #####xcrun simctl install 4AE7240F-67C1-4197-9639-235C2E6417A1 SwiftTrampolineTests.app 19 | ######xcrun simctl install 5FF1783B-C870-4688-8D3D-8B8CC6A818C0 SwiftTrampolineTests.app 20 | ##### 21 | rm /Users/daeken/emulog.out 22 | PID=`xcrun simctl launch 4AE7240F-67C1-4197-9639-235C2E6417A1 dev.daeken.SwiftTrampolineTests | cut -d ' ' -f 2` 23 | #PID=`xcrun simctl launch 5FF1783B-C870-4688-8D3D-8B8CC6A818C0 dev.daeken.SwiftTrampolineTests | cut -d ' ' -f 2` 24 | lsof -p $PID +r 1 &>/dev/null 25 | cat /Users/daeken/emulog.out | grep -E '~~~|Genuine segfault' | sed 's/^~~~//' | sed 's/^.*segfault.*$/SEGFAULT/' | grep --color -E '^|FAIL|SEGFAULT' 26 | PASSCOUNT=`cat /Users/daeken/emulog.out | fgrep '~~~Pass' | wc -l` 27 | TOTALCOUNT=`cat /Users/daeken/emulog.out | grep -E '~~~Pass|~~~FAIL' | wc -l` 28 | FAILCOUNT=`cat /Users/daeken/emulog.out | grep -E '~~~FAIL|Genuine segfault' | sed 's/^~~~//' | sed 's/^.*segfault.*$/SEGFAULT/' | wc -l` 29 | if [ "$FAILCOUNT" -ne "0" ]; then 30 | echo 31 | echo 'Failures:' 32 | cat /Users/daeken/emulog.out | grep -E '~~~FAIL|Genuine segfault' | sed 's/^~~~//' | sed 's/^.*segfault.*$/SEGFAULT/' | grep --color -E '^|FAIL|SEGFAULT'; 33 | fi 34 | echo `echo $PASSCOUNT | sed -e 's/^[[:space:]]*//'`/`echo $TOTALCOUNT | sed -e 's/^[[:space:]]*//'` passing -------------------------------------------------------------------------------- /buildSwiftdb.py: -------------------------------------------------------------------------------- 1 | import fnmatch, json, os, subprocess 2 | from multiprocessing import Pool 3 | import tqdm 4 | 5 | sdkPath = subprocess.check_output('xcodebuild -version -sdk iphonesimulator Path', shell=True).strip() 6 | 7 | def reframe(data): 8 | if isinstance(data, dict): 9 | if 'Str' in data: 10 | return data['Str'] 11 | elif 'IType' in data: 12 | if data['IType'] == 'trivia': 13 | return None 14 | val = {'!type' : data['IType']} 15 | if 'Prop' in data: 16 | val.update({k : reframe(v) for k, v in data['Prop'].items()}) 17 | return {k : v for k, v in val.items() if v is not None} 18 | elif 'List' in data: 19 | return [x for x in [reframe(x) for x in data['List']] if x is not None] 20 | else: 21 | print data 22 | assert False 23 | assert False 24 | 25 | unhandled = set() 26 | class Walker(object): 27 | def __init__(self): 28 | self.parents = [] 29 | 30 | @property 31 | def parent(self): 32 | return self.parents[-1] 33 | 34 | def walk(self, node): 35 | if not isinstance(node, dict): 36 | print node 37 | assert isinstance(node, dict) 38 | type = node['!type'] 39 | if not hasattr(self, type): 40 | if type not in unhandled: 41 | print 'Unhandled node type:', type 42 | print 'With members:', ', '.join(k for k in node if k != '!type') 43 | unhandled.add(type) 44 | return 45 | func = getattr(self, type) 46 | co = func.func_code 47 | hasStar = (co.co_flags & 4) == 4 48 | argCount = co.co_argcount - 1 49 | mandatory = argCount - len(func.func_defaults if func.func_defaults else []) 50 | optional = func.func_defaults 51 | argNames = co.co_varnames[1:argCount+1] 52 | args = [] 53 | for i, name in enumerate(argNames): 54 | if name in node: 55 | args.append(node[name]) 56 | elif i >= mandatory: 57 | args.append(optional[i - mandatory]) 58 | else: 59 | print 'Mandatory argument for %r missing: %r' % (type, name) 60 | assert False 61 | if hasStar and '__children__' in node: 62 | args += node['__children__'] 63 | return func(*args) 64 | 65 | def walkAll(self, lst): 66 | value = None 67 | for elem in lst: 68 | value = self.walk(elem) 69 | return value 70 | 71 | def SourceFileSyntax(self, statements): 72 | #print 'Got SourceFileSyntax!' 73 | self.walk(statements) 74 | 75 | def CodeBlockItemListSyntax(self, *statements): 76 | #print 'Got CodeBlockItemListSyntax' 77 | self.walkAll(statements) 78 | 79 | def CodeBlockItemSyntax(self, item): 80 | self.walk(item) 81 | 82 | def ImportDeclSyntax(self, path): 83 | print 'Import:', `path` 84 | 85 | def ExtensionDeclSyntax(self, extendedType, inheritanceClause, members, genericWhereClause): 86 | pass 87 | 88 | def parseSymbols(fn): 89 | args = ['SwiftHeaderParser/build/Debug/SwiftHeaderParser', fn] 90 | 91 | try: 92 | output = subprocess.check_output(args, stderr=subprocess.STDOUT) 93 | data = json.loads(output) 94 | except Exception, e: 95 | import traceback 96 | traceback.print_exc() 97 | return None, {} 98 | data = reframe(data) 99 | json.dump(data, file('/Users/daeken/projects/swiftmodules_json/%s.json' % fn.split('/')[-2].rsplit('.', 1)[0], 'w'), indent=2, sort_keys=True) 100 | walker = Walker() 101 | walker.walk(data) 102 | return None, {} 103 | 104 | allFns = [] 105 | for root, dirnames, filenames in os.walk(sdkPath): 106 | for filename in fnmatch.filter(filenames, 'x86_64.swiftinterface'): 107 | allFns.append(os.path.join(root, filename)) 108 | 109 | #pool = Pool(1) 110 | 111 | allSymsByFn = {} 112 | #for fn, symbols in tqdm.tqdm(pool.imap_unordered(parseSymbols, allFns), total=len(allFns)): 113 | for fn in allFns: #tqdm.tqdm(allFns): 114 | fn, symbols = parseSymbols(fn) 115 | 116 | for dfn, syms in symbols.items(): 117 | if dfn not in allSymsByFn: 118 | allSymsByFn[dfn] = {} 119 | allSymsByFn[dfn].update(syms) 120 | 121 | with file('swiftfuncdb', 'w') as fp: 122 | for fn, syms in allSymsByFn.items(): 123 | print >>fp, fn 124 | for name, encoding in sorted(syms.items(), key=lambda x: x[0]): 125 | print >>fp, '\t' + name, '=', encoding 126 | -------------------------------------------------------------------------------- /buildTrampolineTests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pushd /Users/daeken/projects/TrampolineTests > /dev/null 4 | xcodebuild -configuration Debug -target TrampolineTests -arch x86_64 -sdk iphonesimulator13.2 > /dev/null 5 | # /Users/daeken/projects/TrampolineTests/build/Debug-iphonesimulator/TrampolineTests.app 6 | xcodebuild -configuration Debug -target TrampolineTests -arch arm64 -sdk iphoneos13.2 > /dev/null 7 | # /Users/daeken/projects/TrampolineTests/build/Debug-iphoneos/TrampolineTests.app 8 | 9 | popd > /dev/null 10 | 11 | rm -rf TrampolineTests.app 2>/dev/null 12 | cp -rf /Users/daeken/projects/TrampolineTests/build/Debug-iphoneos/TrampolineTests.app TrampolineTestsArm.app 13 | rm -rf TrampolineTestsArm.app/Frameworks/NativeSide.framework 14 | cp -rf /Users/daeken/projects/TrampolineTests/build/Debug-iphonesimulator/TrampolineTests.app/Frameworks/NativeSide.framework TrampolineTestsArm.app/Frameworks/ 15 | python wrap.py TrampolineTestsArm.app TrampolineTests.app 16 | rm -rf TrampolineTestsArm.app 2>/dev/null 17 | 18 | xcrun simctl install 4AE7240F-67C1-4197-9639-235C2E6417A1 TrampolineTests.app 19 | rm /Users/daeken/emulog.out 20 | PID=`xcrun simctl launch 4AE7240F-67C1-4197-9639-235C2E6417A1 dev.daeken.TrampolineTests | cut -d ' ' -f 2` 21 | lsof -p $PID +r 1 &>/dev/null 22 | cat /Users/daeken/emulog.out | grep -E '~~~|Genuine segfault' | sed 's/^~~~//' | sed 's/^.*segfault.*$/SEGFAULT/' | grep --color -E '^|FAIL|SEGFAULT' 23 | PASSCOUNT=`cat /Users/daeken/emulog.out | fgrep '~~~Pass' | wc -l` 24 | TOTALCOUNT=`cat /Users/daeken/emulog.out | grep -E '~~~Pass|~~~FAIL' | wc -l` 25 | FAILCOUNT=`cat /Users/daeken/emulog.out | grep -E '~~~FAIL|Genuine segfault' | sed 's/^~~~//' | sed 's/^.*segfault.*$/SEGFAULT/' | wc -l` 26 | if [ "$FAILCOUNT" -ne "0" ]; then 27 | echo 28 | echo 'Failures:' 29 | cat /Users/daeken/emulog.out | grep -E '~~~FAIL|Genuine segfault' | sed 's/^~~~//' | sed 's/^.*segfault.*$/SEGFAULT/' | grep --color -E '^|FAIL|SEGFAULT'; 30 | fi 31 | echo `echo $PASSCOUNT | sed -e 's/^[[:space:]]*//'`/`echo $TOTALCOUNT | sed -e 's/^[[:space:]]*//'` passing -------------------------------------------------------------------------------- /cleanFuncdb.py: -------------------------------------------------------------------------------- 1 | allSyms = {} 2 | 3 | fn = None 4 | for line in file('funcdb', 'r').read().strip().split('\n'): 5 | if not line: 6 | continue 7 | if line[0] != '\t': 8 | fn = line 9 | continue 10 | sym, encoding = line[1:].split(' = ', 1) 11 | if not encoding.startswith('~f{'): 12 | continue 13 | encoding = encoding[3:-1] 14 | if sym not in allSyms or len(allSyms[sym][0]) > len(fn): 15 | allSyms[sym] = fn, encoding 16 | 17 | with file('funcdb2', 'w') as fp: 18 | for sym, (_, encoding) in sorted(allSyms.items(), key=lambda x: x[0]): 19 | print >>fp, '%s=%s' % (sym, encoding) 20 | -------------------------------------------------------------------------------- /convertAll.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir convertedApps 2>/dev/null 4 | 5 | for fn in ~/ios\ apps/*.app; do 6 | echo $fn; 7 | python wrap.py "$fn" "convertedApps/$(basename "$fn")"; 8 | #xcrun simctl install 24AFE712-AC84-4A97-A1E8-EBB7137E4367 "convertedApps/$(basename "$fn")"; 9 | xcrun simctl install D1A6DE6D-4268-4FC5-8BD2-6083F1E3BD33 "convertedApps/$(basename "$fn")"; 10 | #xcrun simctl install 5FF1783B-C870-4688-8D3D-8B8CC6A818C0 "convertedApps/$(basename "$fn")"; 11 | done 12 | 13 | -------------------------------------------------------------------------------- /convertOne.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Converting $1" 4 | python wrap.py "$1" "convertedApps/$(basename "$1")"; 5 | #xcrun simctl install 4AE7240F-67C1-4197-9639-235C2E6417A1 "convertedApps/$(basename "$1")"; 6 | xcrun simctl install D1A6DE6D-4268-4FC5-8BD2-6083F1E3BD33 "convertedApps/$(basename "$1")"; 7 | #xcrun simctl install 5FF1783B-C870-4688-8D3D-8B8CC6A818C0 "convertedApps/$(basename "$1")"; -------------------------------------------------------------------------------- /dumpGsInit.py: -------------------------------------------------------------------------------- 1 | import struct, sys 2 | 3 | """ 4 | auto fp = fopen((string(path) + "/gsinit.bin").c_str(), "rb"); 5 | auto read32 = [&]() -> uint32_t { uint32_t val; fread(&val, sizeof(uint32_t), 1, fp); return val; }; 6 | auto read64 = [&]() -> uint64_t { uint32_t val; fread(&val, sizeof(uint64_t), 1, fp); return val; }; 7 | auto readString = [&](auto len) -> const char* { 8 | auto fn = new char[len]; 9 | fread(fn, len, 1, fp); 10 | return (const char*) fn; 11 | }; 12 | init->fileCount = read32(); 13 | init->files = new TargetFile[init->fileCount]; 14 | for(auto i = 0; i < init->fileCount; ++i) { 15 | auto file = &init->files[i]; 16 | auto fnlen = read32(); 17 | auto clscount = read32(); 18 | auto impcount = read32(); 19 | auto expcount = read32(); 20 | file->main = read64(); 21 | file->fn = readString(fnlen); 22 | file->objcClasses = new uint64_t[clscount + 1]; 23 | file->objcClasses[clscount] = 0; 24 | for(auto j = 0; j < clscount; ++j) 25 | file->objcClasses[j] = read64(); 26 | file->imports = new Import[impcount + 1]; 27 | file->imports[impcount].addr = 0; 28 | for(auto j = 0; j < impcount; ++j) { 29 | auto imp = &file->imports[j]; 30 | imp->addr = read64(); 31 | auto dlen = read32(); 32 | auto nlen = read32(); 33 | imp->dylib = readString(dlen); 34 | imp->symbol = readString(nlen); 35 | } 36 | file->exports = new Export[expcount + 1]; 37 | file->exports[expcount].addr = 0; 38 | for(auto j = 0; j < expcount; ++j) { 39 | auto exp = &file->exports[j]; 40 | exp->addr = read64(); 41 | exp->symbol = readString(read32()); 42 | } 43 | } 44 | ginit = init;""" 45 | 46 | def main(fn): 47 | with file(fn, 'rb') as fp: 48 | read32 = lambda: struct.unpack('>fp, fn 65 | for name, encoding in sorted(syms.items(), key=lambda x: x[0]): 66 | print >>fp, '\t' + name, '=', encoding 67 | -------------------------------------------------------------------------------- /init.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Import { 6 | const char* dylib; 7 | const char* symbol; 8 | uint64_t addr; 9 | }; 10 | 11 | struct Export { 12 | const char* symbol; 13 | uint64_t addr; 14 | }; 15 | 16 | struct Segment { 17 | const char* section; 18 | const char* segment; 19 | uint64_t addr, size; 20 | }; 21 | 22 | struct TargetFile { 23 | const char* fn; 24 | uint64_t main; 25 | uint64_t* objcClasses; 26 | Import* imports; 27 | Export* exports; 28 | Segment* segments; 29 | }; 30 | 31 | struct Init { 32 | int fileCount; 33 | TargetFile* files; 34 | }; 35 | -------------------------------------------------------------------------------- /libarmruntime.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/libarmruntime.dylib -------------------------------------------------------------------------------- /libc--.1.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/libc--.1.dylib -------------------------------------------------------------------------------- /libc--abi.1.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/libc--abi.1.dylib -------------------------------------------------------------------------------- /libemuruntime.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/libemuruntime.dylib -------------------------------------------------------------------------------- /libunicorn.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daeken/GrinningSoul/d35ac197dade35d8a831ffb10fffd2ec93a791ea/libunicorn.a -------------------------------------------------------------------------------- /wrap.py: -------------------------------------------------------------------------------- 1 | import glob, os, shutil, struct, subprocess, sys, tempfile 2 | from bplist import BPListReader 3 | from plistlib import readPlist, writePlist, readPlistFromString, writePlistToString 4 | from dylibify import dylibify, dylibconv 5 | 6 | def main(iname, oname): 7 | iname, oname = iname.rstrip('/'), oname.rstrip('/') 8 | 9 | assert oname.endswith('.app') 10 | 11 | try: 12 | shutil.rmtree(oname) 13 | except: 14 | pass 15 | shutil.copytree(iname, oname) 16 | 17 | try: 18 | with file(iname + '/Info.plist', 'rb') as ipfp: 19 | reader = BPListReader(ipfp.read()) 20 | data = reader.parse() 21 | except: 22 | with file(iname + '/Info.plist', 'rb') as ipfp: 23 | data = readPlist(ipfp) 24 | 25 | origbin = binfile = data['CFBundleExecutable'] 26 | data['CFBundleExecutable'] = 'GSWrapper' 27 | data['CFBundleName'] += ' ARM' 28 | #data['CFBundleIdentifier'] += '.arm' 29 | writePlist(data, oname + '/Info.plist') 30 | 31 | swiftDylibs = [] 32 | for fn in glob.glob('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift/*.dylib'): 33 | fn = fn.rsplit('/', 1)[1] 34 | swiftDylibs.append(fn) 35 | 36 | filenames = [binfile, 'libc--.1.dylib', 'libc--abi.1.dylib', 'libarmruntime.dylib'] 37 | for fn in glob.glob(iname + '/Frameworks/*.dylib'): 38 | fn = fn.rsplit('/', 1)[1] 39 | if fn in swiftDylibs: 40 | continue 41 | filenames.append('Frameworks/' + fn) 42 | for fn in glob.glob(iname + '/Frameworks/*.framework'): 43 | name = fn.rsplit('/', 1)[1][:-10] 44 | filenames.append('Frameworks/' + name + '.framework/' + name) 45 | for fn in glob.glob(iname + '/Frameworks/' + name + '.framework/*.dylib'): 46 | print 'Found dylib in framework!', fn 47 | filenames.append(fn) 48 | allFiles = [] 49 | for binfile in filenames: 50 | if binfile == 'libc--.1.dylib' or binfile == 'libc--abi.1.dylib' or binfile == 'libarmruntime.dylib': 51 | ifile = binfile 52 | else: 53 | ifile = iname + '/' + binfile 54 | ofb = file(ifile, 'rb').read() 55 | if ofb[4:8] == '\x07\x00\x00\x01': 56 | print binfile, 'is already x86-64; hopefully this is a test binary!' 57 | continue 58 | if ofb.startswith('\xca\xfe\xba\xbe'): 59 | print 'FAT binary!' 60 | subprocess.check_call(['lipo', ifile, '-thin', 'arm64', '-output', 'thintemp.bin']) 61 | ifile = 'thintemp.bin' 62 | ofb = file('thintemp.bin', 'rb').read() 63 | with file('machtemp.bin', 'wb') as fp: 64 | fp.write(struct.pack('', 'com.apple.security.get-task-allow')) 188 | print xfn 189 | subprocess.check_call(['codesign', '--entitlements', xfn, '-f', '-s', '-', oname], stderr=devnull) 190 | 191 | if __name__=='__main__': 192 | main(*sys.argv[1:]) 193 | -------------------------------------------------------------------------------- /wrapTemplate.mm: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #import 8 | #include 9 | 10 | int main() { 11 | /*{ 12 | void* libdyld = dlopen("/usr/lib/system/libdyld.dylib", RTLD_NOW); 13 | if(libdyld == nullptr) { 14 | os_log(OS_LOG_DEFAULT, "Failed to get libdyld"); 15 | return 4; 16 | } 17 | auto icf = (uint32_t(*)()) dlsym(libdyld, "_dyld_image_count"); 18 | auto ic = icf(); 19 | if(ic == 0) { 20 | os_log(OS_LOG_DEFAULT, "No images ... ?"); 21 | return 5; 22 | } 23 | auto ginf = (const char*(*)(uint32_t)) dlsym(libdyld, "_dyld_get_image_name"); 24 | auto gihf = (uint8_t*(*)(uint32_t)) dlsym(libdyld, "_dyld_get_image_header"); 25 | for(auto i = 0; i < ic; ++i) { 26 | auto path = ginf(i); 27 | auto fn = strrchr(path, '/') + 1; 28 | if(strcmp(fn, "dyld_sim") != 0) continue; 29 | fflush(stdout); 30 | gihf(i)[0x7c9b0] = 1; // 12.1 31 | gihf(i)[0x89280] = 1; // 13.3 32 | break; 33 | } 34 | }*/ 35 | auto lib = dlopen("%PATH%/libemuruntime.dylib", RTLD_NOW); 36 | if(lib == nullptr) { 37 | os_log(OS_LOG_DEFAULT, "Could not load libemuruntime; ensure that GrinningSoul has not moved. %{public}@", [NSString stringWithUTF8String: dlerror()]); 38 | return 2; 39 | } 40 | auto kickoff = (void(*)(const char*)) dlsym(lib, "_Z7kickoffPKc"); 41 | if(kickoff == nullptr) { 42 | os_log(OS_LOG_DEFAULT, "Could not get kickoff symbol from libemuruntime. %{public}@", [NSString stringWithUTF8String: dlerror()]); 43 | return 3; 44 | } 45 | kickoff("%PATH%"); 46 | } 47 | --------------------------------------------------------------------------------