Last active
June 1, 2024 22:16
-
-
Save DerekSelander/d4fa05cb76f03e9eff5edcff82e449fa to your computer and use it in GitHub Desktop.
Exchange Mach-O platform types with min version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// main.m | |
// platform_swap @LOLgrep | |
// clang -o /tmp/platform_swap /path/to/platform_swap.m -framework Foundation | |
#import <Foundation/Foundation.h> | |
#import <mach-o/fat.h> | |
#import <mach-o/loader.h> | |
struct version { | |
int fix : 8; | |
int min : 8; | |
int max : 16; | |
}; | |
static const char *get_platform_str(int platform) { | |
switch (platform) { | |
case PLATFORM_MACOS: | |
return "macos"; | |
case PLATFORM_IOS: | |
return "ios"; | |
case PLATFORM_TVOS: | |
return "tvos"; | |
case PLATFORM_WATCHOS: | |
return "watchos"; | |
case PLATFORM_BRIDGEOS: | |
return "bridgeos"; | |
case PLATFORM_MACCATALYST: | |
return "maccatalyst"; | |
case PLATFORM_IOSSIMULATOR: | |
return "iossimulator"; | |
case PLATFORM_TVOSSIMULATOR: | |
return "tvossimulator"; | |
case PLATFORM_WATCHOSSIMULATOR: | |
return "watchossimulator"; | |
case PLATFORM_DRIVERKIT: | |
return "driverkit"; | |
case PLATFORM_ANY: | |
return "any"; | |
default: | |
return "unkown"; | |
} | |
return "unknown"; | |
} | |
int main(int argc, const char * argv[]) { | |
if (argc != 6 && argc != 2) { | |
printf("platform_swap platform_num major minor bugfix (i.e. convert MacOS M1 binary to iOS 10.3.1 -> platform_swap /tmp/afile 2 10 3 1\nSee PLATFORM_* in <mach-o/loader.h>\n"); | |
exit(1); | |
} | |
NSString *file = [NSString stringWithUTF8String:argv[1]]; | |
NSURL *fileURL = [NSURL fileURLWithPath:file]; | |
if (!fileURL) { | |
printf("Can't find file %s\n", argv[1]); | |
exit(1); | |
} | |
NSMutableData *data = [NSMutableData dataWithContentsOfFile:file]; | |
char *ptr = (void*)[data bytes]; | |
int32_t *magic = (int32_t*)ptr; | |
if (*magic != MH_MAGIC_64) { | |
if (*magic == FAT_CIGAM || *magic == FAT_MAGIC || *magic == FAT_MAGIC_64 || *magic == FAT_CIGAM_64) { | |
printf("fat file. Use \"lipo -thin <ARCH> -o /tmp/new_file %s\" first\n", argv[1]); | |
} | |
printf("Invalid header file\n"); | |
exit(1); | |
} | |
struct mach_header_64 *header = (void*)ptr; | |
struct load_command *cur = (void*)((uintptr_t)ptr + (uintptr_t)sizeof(struct mach_header_64)); | |
if (argc == 2) { | |
for (int i = 0; i < header->ncmds; i++) { | |
if (cur->cmd == LC_BUILD_VERSION) { | |
struct build_version_command* build = (void*)cur; | |
struct version *v = (void*)&build->minos; | |
struct version *sdk = (void*)&build->sdk; | |
printf("%s (%d) %d.%d.%d, built with sdk: %d.%d.%d\nplatform_swap %s %d %d %d %d\n", get_platform_str(build->platform), build->platform, v->max, v->min, v->fix, sdk->max, sdk->min, sdk->fix, argv[1], build->platform, v->max, v->min | |
, v->fix); | |
exit(0); | |
} | |
cur = (void*)(cur->cmdsize + (uintptr_t)cur); | |
} | |
printf("couldn't find LC_BUILD_VERSION\n"); | |
exit(1); | |
} | |
long platform = strtol(argv[2], NULL, 10); | |
long major = strtol(argv[3], NULL, 10); | |
long minor = strtol(argv[4], NULL, 10); | |
long bugfix = strtol(argv[5], NULL, 10); | |
for (int i = 0; i < header->ncmds; i++) { | |
if (cur->cmd == LC_BUILD_VERSION) { | |
struct build_version_command*build = (void*)cur; | |
NSRange platform_range = NSMakeRange((uintptr_t)&build->platform - (uintptr_t)ptr, sizeof(build->platform)); | |
int32_t new_platform = (int)platform; | |
[data replaceBytesInRange:platform_range withBytes:&new_platform]; | |
NSRange version_range = NSMakeRange((uintptr_t)&build->minos - (uintptr_t)ptr, sizeof(build->minos)); | |
struct version new_version = {(int)bugfix, (int)minor, (int)major}; | |
[data replaceBytesInRange:version_range withBytes:&new_version]; | |
const char *platform_name = get_platform_str(build->platform); | |
NSString *resolvedString = nil; | |
if (getenv("INPLACE")) { | |
resolvedString = [NSString stringWithFormat:@"%@", file]; | |
} else { | |
resolvedString = [NSString stringWithFormat:@"%@_%s", file, platform_name]; | |
} | |
if (!getenv("DRYRUN")) { | |
[data writeToFile:resolvedString atomically:YES]; | |
} | |
printf("writting to file: %s\n", resolvedString.UTF8String); | |
exit(0); | |
} | |
cur = (void*)(cur->cmdsize + (uintptr_t)cur); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment