Skip to content

Instantly share code, notes, and snippets.

@DerekSelander
Last active June 1, 2024 22:16
Show Gist options
  • Save DerekSelander/d4fa05cb76f03e9eff5edcff82e449fa to your computer and use it in GitHub Desktop.
Save DerekSelander/d4fa05cb76f03e9eff5edcff82e449fa to your computer and use it in GitHub Desktop.
Exchange Mach-O platform types with min version
//
// 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