diff --git a/NXBoot.xcodeproj/project.pbxproj b/NXBoot.xcodeproj/project.pbxproj index a96c82d..f191dcd 100644 --- a/NXBoot.xcodeproj/project.pbxproj +++ b/NXBoot.xcodeproj/project.pbxproj @@ -45,6 +45,9 @@ A78865E520FAAF6A00ECA2B2 /* AssetsLegacy.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A78865E420FAAF6A00ECA2B2 /* AssetsLegacy.xcassets */; }; A78BBEC920EE30F4007C3514 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A78BBEC820EE30F4007C3514 /* IOKit.framework */; }; A78D523520F343EC0045FC4F /* AboutScreenViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A78D523420F343EC0045FC4F /* AboutScreenViewController.m */; }; + A795B6BC21015F970008820F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A718F2302100CD1C009BD181 /* main.m */; }; + A795B6BD21015FD30008820F /* libNXBootKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A718F21C2100CBB7009BD181 /* libNXBootKit.a */; }; + A795B6BE21015FD80008820F /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A718F2332100CDC1009BD181 /* IOKit.framework */; }; A7999ECA20F5761B00F659EB /* PackageIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = A7999EC920F5761B00F659EB /* PackageIcon.png */; }; A7AD2A1120F63A3500D1A400 /* libMobileGestalt.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A7AD2A1020F63A3500D1A400 /* libMobileGestalt.tbd */; }; A7B7E1FD20EE738300F39094 /* BootProfilesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7B7E1FC20EE738300F39094 /* BootProfilesViewController.m */; }; @@ -68,6 +71,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A795B6B321015F880008820F /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -107,6 +119,7 @@ A78BBEC820EE30F4007C3514 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; A78D523320F343EC0045FC4F /* AboutScreenViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AboutScreenViewController.h; sourceTree = ""; }; A78D523420F343EC0045FC4F /* AboutScreenViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AboutScreenViewController.m; sourceTree = ""; }; + A795B6B521015F880008820F /* nxboot */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nxboot; sourceTree = BUILT_PRODUCTS_DIR; }; A7999EC920F5761B00F659EB /* PackageIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = PackageIcon.png; sourceTree = ""; }; A7AD2A1020F63A3500D1A400 /* libMobileGestalt.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libMobileGestalt.tbd; path = usr/lib/libMobileGestalt.tbd; sourceTree = SDKROOT; }; A7B494EB20FAA4000006E028 /* NXBootLegacy.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NXBootLegacy.entitlements; sourceTree = ""; }; @@ -154,6 +167,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A795B6B221015F880008820F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A795B6BE21015FD80008820F /* IOKit.framework in Frameworks */, + A795B6BD21015FD30008820F /* libNXBootKit.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A7FDBE1120EE27DD00E59587 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -280,10 +302,10 @@ isa = PBXGroup; children = ( A7FDBE2D20EE2A1700E59587 /* Frameworks */, - A7FDBE1620EE27DD00E59587 /* NXBoot */, - A718F22F2100CD1C009BD181 /* NXBootCmd */, A73FDCE420FA9C5C007EBF4F /* NXBootKit */, + A7FDBE1620EE27DD00E59587 /* NXBoot */, A73FDD1720FA9FBC007EBF4F /* NXBootLegacy */, + A718F22F2100CD1C009BD181 /* NXBootCmd */, 422DE3698D5FA49548658B9F /* Pods */, A7FDBE1520EE27DD00E59587 /* Products */, ); @@ -296,6 +318,7 @@ A73FDCE320FA9C5C007EBF4F /* libNXBootKit.a */, A73FDD1520FA9F5A007EBF4F /* NXBootLegacy.app */, A718F21C2100CBB7009BD181 /* libNXBootKit.a */, + A795B6B521015F880008820F /* nxboot */, ); name = Products; sourceTree = ""; @@ -399,6 +422,23 @@ productReference = A73FDD1520FA9F5A007EBF4F /* NXBootLegacy.app */; productType = "com.apple.product-type.application"; }; + A795B6B421015F880008820F /* NXBootCmdMac */ = { + isa = PBXNativeTarget; + buildConfigurationList = A795B6BB21015F880008820F /* Build configuration list for PBXNativeTarget "NXBootCmdMac" */; + buildPhases = ( + A795B6B121015F880008820F /* Sources */, + A795B6B221015F880008820F /* Frameworks */, + A795B6B321015F880008820F /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NXBootCmdMac; + productName = NXBootCmd; + productReference = A795B6B521015F880008820F /* nxboot */; + productType = "com.apple.product-type.tool"; + }; A7FDBE1320EE27DD00E59587 /* NXBoot */ = { isa = PBXNativeTarget; buildConfigurationList = A7FDBE2A20EE27DE00E59587 /* Build configuration list for PBXNativeTarget "NXBoot" */; @@ -439,6 +479,9 @@ }; }; }; + A795B6B421015F880008820F = { + CreatedOnToolsVersion = 9.4.1; + }; A7FDBE1320EE27DD00E59587 = { CreatedOnToolsVersion = 9.4.1; SystemCapabilities = { @@ -466,6 +509,7 @@ A73FDCF920FA9F5A007EBF4F /* NXBootLegacy */, A73FDCE220FA9C5C007EBF4F /* NXBootKit */, A718F21B2100CBB7009BD181 /* NXBootKitMac */, + A795B6B421015F880008820F /* NXBootCmdMac */, ); }; /* End PBXProject section */ @@ -562,6 +606,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A795B6B121015F880008820F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A795B6BC21015F970008820F /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A7FDBE1020EE27DD00E59587 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -722,6 +774,42 @@ }; name = Release; }; + A795B6B921015F880008820F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "NXBOOT_VERSION=\\\"PLACEHOLDER\\\"", + "NXBOOT_BUILDNO=0", + "DEBUG=1", + "$(inherited)", + ); + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_NAME = nxboot; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = Debug; + }; + A795B6BA21015F880008820F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "NXBOOT_VERSION=\\\"PLACEHOLDER\\\"", + "NXBOOT_BUILDNO=0", + ); + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_NAME = nxboot; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = Release; + }; A7FDBE2820EE27DE00E59587 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -916,6 +1004,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + A795B6BB21015F880008820F /* Build configuration list for PBXNativeTarget "NXBootCmdMac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A795B6B921015F880008820F /* Debug */, + A795B6BA21015F880008820F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; A7FDBE0F20EE27DD00E59587 /* Build configuration list for PBXProject "NXBoot" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/NXBoot.xcodeproj/xcshareddata/xcschemes/NXBootCmdMac.xcscheme b/NXBoot.xcodeproj/xcshareddata/xcschemes/NXBootCmdMac.xcscheme new file mode 100644 index 0000000..5be1891 --- /dev/null +++ b/NXBoot.xcodeproj/xcshareddata/xcschemes/NXBootCmdMac.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NXBoot.xcodeproj/xcshareddata/xcschemes/NXBootKitAll.xcscheme b/NXBoot.xcodeproj/xcshareddata/xcschemes/NXBootKitAll.xcscheme new file mode 100644 index 0000000..189b2ad --- /dev/null +++ b/NXBoot.xcodeproj/xcshareddata/xcschemes/NXBootKitAll.xcscheme @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NXBootCmd/main.m b/NXBootCmd/main.m index 149f8e5..056e420 100644 --- a/NXBootCmd/main.m +++ b/NXBootCmd/main.m @@ -4,17 +4,28 @@ */ #import +#import +#import +#import #import "NXBootKit.h" #import "NXExec.h" #import "NXUSBDevice.h" #import "NXUSBDeviceEnumerator.h" +#define ESC_RED "\033[1;31m" +#define ESC_GREEN "\033[1;32m" +#define ESC "\033[0m" +#define ESC_LN ESC "\n" + +static volatile sig_atomic_t gTerm = 0; + @interface NXBoot : NSObject @property (strong, nonatomic) NSData *relocator; @property (strong, nonatomic) NSData *image; @property (strong, nonatomic) NXUSBDeviceEnumerator *usbEnum; @property (assign, nonatomic) BOOL daemon; // keep running after handling a device @property (assign, nonatomic) BOOL keepReading; +@property (assign, nonatomic) BOOL lastBootFailed; @end @implementation NXBoot @@ -29,16 +40,24 @@ - (void)start { - (void)usbDeviceEnumerator:(NXUSBDeviceEnumerator *)deviceEnum deviceConnected:(NXUSBDevice *)device { kern_return_t kr; NSString *err = nil; + struct NXExecDesc desc = NXExecAcquireDeviceInterface(device->_intf, &err); + if (desc.intf) { if (NXExecDesc(&desc, self.relocator, self.image, &err)) { + self.lastBootFailed = NO; if (self.keepReading) { - fprintf(stderr, "success: payload was run, will continue to read data to stdout...\n"); + fprintf(stderr, ESC_GREEN "success: payload was run, will continue to read from EP1 to stdout..." ESC_LN); UInt32 btransf; char rdbuf[0x1000]; - while (true) { + while (!gTerm) { btransf = sizeof(rdbuf); kr = NXCOMCall(desc.intf, ReadPipeTO, desc.readRef, rdbuf, &btransf, 1000, 1000); + if (kr == 0xE0004051) { + // bulk read error, expected + fprintf(stderr, ESC_GREEN "success: USB EP1 bulk stream was terminated, exiting" ESC_LN); + break; + } if (kr) { fprintf(stderr, "Failed to read after successful payload execution with code %08x.\n", kr); fprintf(stderr, "This is not an error if the RCM payload deliberately terminated the USB connection. Exiting.\n"); @@ -46,20 +65,32 @@ - (void)usbDeviceEnumerator:(NXUSBDeviceEnumerator *)deviceEnum deviceConnected: } fwrite(rdbuf, btransf, 1, stdout); } + if (gTerm) { + fprintf(stderr, ESC_GREEN "error: USB EP1 read operation was interrupted" ESC_LN); + } + } + else if (self.daemon) { + fprintf(stderr, ESC_GREEN "success: payload was run" ESC_LN); } else { - fprintf(stderr, "success: payload was run. fair winds!\n"); + fprintf(stderr, ESC_GREEN "success: payload was run. exiting, fair winds!" ESC_LN); } } else { - fprintf(stderr, "error: NXExec failed: %s\n", err.UTF8String); + self.lastBootFailed = YES; + fprintf(stderr, ESC_RED "error: NXExec failed: %s" ESC_LN, err.UTF8String); } NXExecReleaseDeviceInterface(&desc); } else { - fprintf(stderr, "error: could not acquire USB device handle: %s\n", err.UTF8String); + self.lastBootFailed = YES; + fprintf(stderr, ESC_RED "error: could not acquire USB device handle: %s" ESC_LN, err.UTF8String); } - if (self.keepReading || !self.daemon) { + + if (self.daemon) { + fprintf(stderr, "waiting for next connection...\n"); + } + else { CFRunLoopStop(CFRunLoopGetMain()); } } @@ -75,94 +106,150 @@ - (void)usbDeviceEnumerator:(NXUSBDeviceEnumerator *)deviceEnum deviceError:(NSS @end static void printHelp() { - fprintf(stderr, "usage: nxboot [-v] [-d|-r] \n"); - fprintf(stderr, " -v: enable debug logging\n"); - fprintf(stderr, " -d: daemon mode, don't stop after handling the first device\n"); - fprintf(stderr, " -r: read more data from payload (cannot be used with -d)\n\n"); - fprintf(stderr, "for updates visit https://mologie.github.io/nxboot/\n"); + fprintf(stderr, + "usage: nxboot [-v] [-d|-k] [-r ] \n" + " -v: enable verbose debug output\n" + " -d: daemon mode, don't stop after handling the first device\n" + " -k: read data from USB EP1 to stdout after payload execution (conflicts with -d)\n" + " -r: use a custom relocator (default: embedded Fusée/intermezzo)\n" + "\n" + "example for Coreboot/Linux: nxboot -r cbfs.bin coreboot.rom\n" + "for updates visit: https://mologie.github.io/nxboot/\n"); +} + +static void onSignal(int sig) { + gTerm = 1; + dispatch_async(dispatch_get_main_queue(), ^{ + NXLog(@"CMD: Got signal %d, stopping main run loop", sig); + CFRunLoopStop(CFRunLoopGetMain()); + }); } int main(int argc, char *argv[]) { @autoreleasepool { - NXBoot *cmdTool = [[NXBoot alloc] init]; - NSString *relocatorPath = nil; - NSString *imagePath = nil; - NXBootKitDebugEnabled = NO; + NXBoot *cmdTool = [[NXBoot alloc] init]; + fprintf(stderr, "nxboot %s build %d\n", NXBOOT_VERSION, NXBOOT_BUILDNO); for (int i = 1, pos = 0; i < argc; i++) { if (argv[i][0] == '-') { if (strlen(argv[i]) != 2) { - fprintf(stderr, "error: unknown argument -%s\n", argv[i]); + fprintf(stderr, ESC_RED "error: invalid argument %s" ESC_LN, argv[i]); + printHelp(); return 1; } switch (argv[i][1]) { - case 'h': - fprintf(stderr, "Copyright 2018 Oliver Kuckertz \n"); + case 'h': { + fprintf(stderr, "\n"); + fprintf(stderr, "NXBoot is a Fusée/ShofEL2 implementation for macOS and iOS.\n"); + fprintf(stderr, "It supports any Fusée payload and Coreboot/CBFS.\n"); + fprintf(stderr, "Copyright 2018 Oliver Kuckertz \n\n"); printHelp(); return 0; - case 'v': + } + case 'v': { NXBootKitDebugEnabled = YES; break; - case 'd': + } + case 'd': { if (cmdTool.keepReading) { - fprintf(stderr, "error: -d cannot be used with -r\n"); + fprintf(stderr, ESC_RED "error: -d cannot be used with -k" ESC_LN); + printHelp(); return 1; } cmdTool.daemon = YES; break; - case 'r': + } + case 'k': { if (cmdTool.daemon) { - fprintf(stderr, "error: -r cannot be used with -d\n"); + fprintf(stderr, ESC_RED "error: -d cannot be used with -k" ESC_LN); + printHelp(); return 1; } cmdTool.keepReading = YES; break; - default: - fprintf(stderr, "error: unknown argument -%s\n", argv[i]); + } + case 'r': { + i++; + if (i < argc) { + NSString *path = [NSString stringWithUTF8String:argv[i]]; + NXLog(@"CMD: Using relocator %@", path); + cmdTool.relocator = [NSData dataWithContentsOfFile:path]; + if (cmdTool.relocator == nil) { + fprintf(stderr, ESC_RED "error: failed to load relocator" ESC_LN); + return 1; + } + } + else { + fprintf(stderr, ESC_RED "error: -r requires an argument" ESC_LN); + printHelp(); + return 1; + } + } + default: { + fprintf(stderr, ESC_RED "error: unknown argument %s" ESC_LN, argv[i]); + printHelp(); return 1; + } } } else { switch (pos) { - case 0: - relocatorPath = [NSString stringWithUTF8String:argv[i]]; - break; - case 1: - imagePath = [NSString stringWithUTF8String:argv[i]]; + case 0: { + NSString *path = [NSString stringWithUTF8String:argv[i]]; + NXLog(@"CMD: Using payload %@", path); + cmdTool.image = [NSData dataWithContentsOfFile:path]; + if (cmdTool.image == nil) { + fprintf(stderr, ESC_RED "error: failed to load payload" ESC_LN); + return 1; + } break; - default: - fprintf(stderr, "error: too many positional arguments\n"); + } + default: { + fprintf(stderr, ESC_RED "error: too many positional arguments" ESC_LN); + printHelp(); return 1; + } } pos++; } } - if (!relocatorPath || !imagePath) { + if (!cmdTool.image) { + fprintf(stderr, ESC_RED "error: a payload path must be set" ESC_LN); printHelp(); return 1; } - NXLog(@"CMD: Using relocator %@ and image %@", relocatorPath, imagePath); - - cmdTool.relocator = [NSData dataWithContentsOfFile:relocatorPath]; - if (cmdTool.relocator == nil) { - fprintf(stderr, "error: failed to load relocator\n"); - return 1; - } - - cmdTool.image = [NSData dataWithContentsOfFile:imagePath]; - if (cmdTool.image == nil) { - fprintf(stderr, "error: failed to load payload\n"); - return 1; + if (!cmdTool.relocator) { + size_t n; + void *p = getsectiondata(&_mh_execute_header, "__TEXT", "__intermezzo", &n); + if (!p) { + fprintf(stderr, ESC_RED "error: getsectiondata failed, which means that your nxboot build is broken" ESC_LN); + } + cmdTool.relocator = [NSData dataWithBytesNoCopy:p length:n freeWhenDone:NO]; + NXLog(@"CMD: Using default relocator with size %lu bytes", (unsigned long)n); } [cmdTool start]; + fprintf(stderr, "waiting for Nintendo Switch in RCM mode...\n"); + + dispatch_async(dispatch_get_main_queue(), ^{ + struct sigaction sa = { + .sa_handler = onSignal, + .sa_flags = 0 + }; + sigaction(SIGINT, &sa, 0); + sigaction(SIGTERM, &sa, 0); + NXLog(@"CMD: Signal handler installed"); + }); + CFRunLoopRun(); - return 0; + NXLog(@"CMD: Exiting normally, last boot %@", cmdTool.lastBootFailed ? @"failed" : @"OK"); + + return cmdTool.lastBootFailed ? 1 : 0; } } diff --git a/build_cmdtool.sh b/build_cmdtool.sh index 06221ed..6a20d6e 100755 --- a/build_cmdtool.sh +++ b/build_cmdtool.sh @@ -8,14 +8,14 @@ RELEASEDIR_MACOS=$PROJDIR/DerivedData/NXBoot/Build/Products/Release RELEASEDIR_IOS=$PROJDIR/DerivedData/NXBoot/Build/Products/Release-iphoneos BINDIR=$PROJDIR/DerivedData/bin mkdir -p $BINDIR -xcodebuild -workspace NXBoot.xcworkspace -scheme NXBootKit -configuration Release build | xcpretty -xcodebuild -workspace NXBoot.xcworkspace -scheme NXBootKitMac -configuration Release build | xcpretty +xcodebuild -workspace NXBoot.xcworkspace -scheme NXBootKitAll -configuration Release build | xcpretty NXBOOT_CMD_CFLAGS="-DNXBOOT_VERSION=\"$version\" -DNXBOOT_BUILDNO=$buildno -I$PROJDIR/NXBootKit -std=gnu11 -fobjc-arc -fobjc-weak -fmodules -fvisibility=hidden -Wall -O2" NXBOOT_CMD_FRAMEWORKS="-framework CoreFoundation -framework Foundation -framework IOKit" -xcrun -sdk iphoneos clang NXBootCmd/main.m $NXBOOT_CMD_CFLAGS $NXBOOT_CMD_FRAMEWORKS "-L$RELEASEDIR_IOS" -lNXBootKit -arch armv7 -miphoneos-version-min=7.0 -o $BINDIR/nxboot.armv7 -xcrun -sdk iphoneos clang NXBootCmd/main.m $NXBOOT_CMD_CFLAGS $NXBOOT_CMD_FRAMEWORKS "-L$RELEASEDIR_IOS" -lNXBootKit -arch arm64 -miphoneos-version-min=7.0 -o $BINDIR/nxboot.arm64 +NXBOOT_CMD_LDFLAGS="-lNXBootKit -sectcreate __TEXT __intermezzo $PROJDIR/NXBoot/Payloads/intermezzo.bin" +xcrun -sdk iphoneos clang NXBootCmd/main.m $NXBOOT_CMD_CFLAGS $NXBOOT_CMD_FRAMEWORKS -L"$RELEASEDIR_IOS" $NXBOOT_CMD_LDFLAGS -arch armv7 -miphoneos-version-min=7.0 -o $BINDIR/nxboot.armv7 jtool --sign --inplace --ident com.mologie.NXBootCmd --ent NXBootCmd/NXBootCmd.entitlements $BINDIR/nxboot.armv7 +xcrun -sdk iphoneos clang NXBootCmd/main.m $NXBOOT_CMD_CFLAGS $NXBOOT_CMD_FRAMEWORKS -L"$RELEASEDIR_IOS" $NXBOOT_CMD_LDFLAGS -arch arm64 -miphoneos-version-min=7.0 -o $BINDIR/nxboot.arm64 jtool --sign --inplace --ident com.mologie.NXBootCmd --ent NXBootCmd/NXBootCmd.entitlements $BINDIR/nxboot.arm64 -clang NXBootCmd/main.m $NXBOOT_CMD_CFLAGS $NXBOOT_CMD_FRAMEWORKS "-L$RELEASEDIR_MACOS" -lNXBootKit -arch x86_64 -mmacosx-version-min=10.10 -o $BINDIR/nxboot.x86_64 +clang NXBootCmd/main.m $NXBOOT_CMD_CFLAGS $NXBOOT_CMD_FRAMEWORKS "-L$RELEASEDIR_MACOS" $NXBOOT_CMD_LDFLAGS -arch x86_64 -mmacosx-version-min=10.10 -o $BINDIR/nxboot.x86_64 lipo -create -output $BINDIR/nxboot $BINDIR/nxboot.* echo "Universal executable available at $BINDIR/nxboot"