aboutsummaryrefslogtreecommitdiff
path: root/darwin-bootstrap/substituted-plist-loader.m
diff options
context:
space:
mode:
authorcomex2015-02-28 21:18:20 -0500
committercomex2015-02-28 21:18:20 -0500
commitbfce3bf03e7bc285bc5bbf3df561a6f4e9169899 (patch)
tree115e02d7a4d44ca6e0a13c4a02a335dffe9b54e2 /darwin-bootstrap/substituted-plist-loader.m
parentRename ios-bootstrap to darwin-bootstrap; cleanup posixspawn-hook and unrestr... (diff)
downloadsubstitute-bfce3bf03e7bc285bc5bbf3df561a6f4e9169899.tar.gz
Add substituted and fix things up to use it. Still untested.
Diffstat (limited to 'darwin-bootstrap/substituted-plist-loader.m')
-rw-r--r--darwin-bootstrap/substituted-plist-loader.m173
1 files changed, 173 insertions, 0 deletions
diff --git a/darwin-bootstrap/substituted-plist-loader.m b/darwin-bootstrap/substituted-plist-loader.m
new file mode 100644
index 0000000..0c8d66f
--- /dev/null
+++ b/darwin-bootstrap/substituted-plist-loader.m
@@ -0,0 +1,173 @@
+#import <Foundation/Foundation.h>
+#import <CoreFoundation/CoreFoundation.h>
+#include "substituted-messages.h"
+
+enum convert_filters_ret {
+ PROVISIONAL_PASS,
+ FAIL,
+ INVALID
+};
+
+static double id_to_double(id o) {
+ if ([o isKindOfClass:[NSString class]]) {
+ NSScanner *scan = [NSScanner scannerWithString:o];
+ double d;
+ if (![scan scanDouble:&d] || !scan.atEnd)
+ return NAN;
+ return d;
+ } else if ([o isKindOfClass:[NSNumber class]]) {
+ return [o doubleValue];
+ } else {
+ return NAN;
+ }
+}
+
+static enum convert_filters_ret convert_filters(NSDictionary *plist_dict,
+ const char *exec_name,
+ NSMutableData *test_out) {
+
+ NSDictionary *filter = [plist_dict objectForKey:@"Filter"];
+ if (!filter)
+ return PROVISIONAL_PASS;
+ if (![filter isKindOfClass:[NSDictionary class]])
+ return INVALID;
+
+ for (NSString *key in [filter allKeys]) {
+ if (!([key isEqualToString:@"CoreFoundationVersion"] ||
+ [key isEqualToString:@"Classes"] ||
+ [key isEqualToString:@"Bundles"] ||
+ [key isEqualToString:@"Executables"])) {
+ return INVALID;
+ }
+ }
+
+ /* First do the two we can test here. */
+
+ NSArray *cfv = [filter objectForKey:@"CoreFoundationVersion"];
+ if (cfv) {
+ if (![cfv isKindOfClass:[NSArray class]] ||
+ [cfv count] == 0 ||
+ [cfv count] > 2)
+ return INVALID;
+ double version = kCFCoreFoundationVersionNumber;
+ double minimum = id_to_double([cfv objectAtIndex:0]);
+ if (minimum != minimum)
+ return INVALID;
+ if (version < minimum)
+ return FAIL;
+ id supremum_o = [cfv objectAtIndex:1];
+ if (supremum_o) {
+ double supremum = id_to_double(supremum_o);
+ if (supremum != supremum)
+ return INVALID;
+ if (version >= supremum)
+ return FAIL;
+ }
+ }
+
+ NSArray *executables = [filter objectForKey:@"Executables"];
+ if (executables) {
+ NSString *exe = [NSString stringWithCString:exec_name
+ encoding:NSUTF8StringEncoding];
+ if (![executables isKindOfClass:[NSArray class]])
+ return INVALID;
+ for (NSString *name in executables) {
+ if (![name isKindOfClass:[NSString class]])
+ return INVALID;
+ if ([name isEqualToString:exe])
+ goto ok;
+ }
+ return FAIL;
+ ok:;
+ }
+
+ /* Convert the rest to substituted_bundle_list_ops. */
+
+ struct {
+ __unsafe_unretained NSString *key;
+ uint8_t opc;
+ } types[2] = {
+ {@"Classes", SUBSTITUTED_TEST_CLASS},
+ {@"Bundles", SUBSTITUTED_TEST_BUNDLE},
+ };
+
+ for (int i = 0; i < 2; i++) {
+ NSArray *things = [filter objectForKey:types[i].key];
+ if (things) {
+ if (![things isKindOfClass:[NSArray class]])
+ return INVALID;
+ for (NSString *name in things) {
+ if (![name isKindOfClass:[NSString class]])
+ return INVALID;
+ NSData *name_data = [name dataUsingEncoding:NSUTF8StringEncoding];
+ size_t len = [name_data length];
+ if (len > 65535)
+ return INVALID;
+ struct substituted_bundle_list_op op = {(uint16_t) len,
+ types[i].opc};
+ [test_out appendBytes:&op length:sizeof(op)];
+ [test_out appendData:name_data];
+ static char zero = '\0';
+ [test_out appendBytes:&zero length:1];
+ }
+ }
+ }
+
+ return PROVISIONAL_PASS;
+}
+
+static NSData *the_bundle_list;
+
+void get_bundle_list(const char *exec_name,
+ const void **bundle_list, size_t *bundle_list_size) {
+ /* TODO cache */
+ NSError *err;
+ NSString *base = @"/Library/Substitute/DynamicLibraries";
+ NSArray *list = [[NSFileManager defaultManager]
+ contentsOfDirectoryAtPath:base
+ error:&err];
+ if (!list)
+ return;
+
+ NSMutableData *out = [NSMutableData data];
+
+ for (NSString *dylib in list) {
+ if (![[dylib pathExtension] isEqualToString:@"dylib"])
+ continue;
+ NSString *plist = [[dylib stringByDeletingPathExtension]
+ stringByAppendingPathExtension:@"plist"];
+ NSString *full_plist = [base stringByAppendingPathComponent:plist];
+ NSDictionary *plist_dict = [NSDictionary dictionaryWithContentsOfFile:
+ full_plist];
+ if (!plist_dict) {
+ NSLog(@"missing, unreadable, or invalid plist '%@' for dylib '%@'; unlike Substrate, we require plists", full_plist, dylib);
+ continue;
+ }
+
+ NSMutableData *test = [NSMutableData data];
+ enum convert_filters_ret ret = convert_filters(plist_dict, exec_name, test);
+ if (ret == FAIL) {
+ continue;
+ } else if (ret == INVALID) {
+ NSLog(@"bad data in plist '%@' for dylib '%@'", full_plist, dylib);
+ continue;
+ }
+ NSString *dylib_path = [base stringByAppendingPathComponent:dylib];
+ NSData *dylib_path_data = [dylib_path dataUsingEncoding:NSUTF8StringEncoding];
+ size_t len = [dylib_path length];
+ if (len > 65535) {
+ NSLog(@"dylib '%@' somehow has an absurdly long path", dylib);
+ continue;
+ }
+ [out appendData:test];
+ struct substituted_bundle_list_op op = {(uint16_t) len,
+ SUBSTITUTED_USE_DYLIB};
+ [out appendBytes:&op length:sizeof(op)];
+ [out appendData:dylib_path_data];
+ static char zero = '\0';
+ [out appendBytes:&zero length:1];
+ }
+ the_bundle_list = out;
+ *bundle_list = [out bytes];
+ *bundle_list_size = [out length];
+}