aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure4
-rw-r--r--darwin-bootstrap/bundle-loader.c10
-rw-r--r--darwin-bootstrap/posixspawn-hook.c137
-rw-r--r--darwin-bootstrap/substituted.m119
-rw-r--r--lib/cbit/htab.h54
-rw-r--r--lib/darwin/xxpc.h8
6 files changed, 252 insertions, 80 deletions
diff --git a/configure b/configure
index 7d12f35..5026f50 100755
--- a/configure
+++ b/configure
@@ -57,7 +57,7 @@ if settings.enable_werror:
settings[mach.name].cflags = ['-Werror'] + settings[mach.name].cflags
for i in ('cflags', 'ldflags'):
- settings.host[i] = ['-miphoneos-version-min=8.0'] + settings.host[i]
+ settings.host[i] = ['-miphoneos-version-min=8.0', '-O3'] + settings.host[i]
# todo make overridable?
cc_argv = c.cc.argv()
@@ -212,7 +212,7 @@ if settings.enable_ios_bootstrap:
ls = ['(out)/libsubstitute.dylib']
for ty, out, ins, objs, ldf, cf in [
- ('dylib', '(out)/posixspawn-hook.dylib', ['(src)/darwin-bootstrap/posixspawn-hook.c'], ls, [], []),
+ ('dylib', '(out)/posixspawn-hook.dylib', ['(src)/darwin-bootstrap/posixspawn-hook.c'], ls, ['-lbsm'], []),
('dylib', '(out)/bundle-loader.dylib', ['(src)/darwin-bootstrap/bundle-loader.c'], [], [], []),
('exec', '(out)/unrestrict', ['(src)/darwin-bootstrap/unrestrict.c'], ls, [], []),
('exec', '(out)/inject-into-launchd', ['(src)/darwin-bootstrap/inject-into-launchd.c'], ls, ['-framework', 'IOKit', '-framework', 'CoreFoundation'], []),
diff --git a/darwin-bootstrap/bundle-loader.c b/darwin-bootstrap/bundle-loader.c
index ed4dde4..7a90414 100644
--- a/darwin-bootstrap/bundle-loader.c
+++ b/darwin-bootstrap/bundle-loader.c
@@ -193,13 +193,6 @@ static void handle_xxpc_object(xxpc_object_t object, bool is_reply) {
free(desc);
}
-static void inform_sud_of_clean_exit() {
- xxpc_object_t message = xxpc_dictionary_create(NULL, NULL, 0);
- xxpc_dictionary_set_string(message, "type", "bye");
- xxpc_connection_send_message(substituted_conn, message);
- xxpc_release(message);
-}
-
/* this is DYLD_INSERT_LIBRARIES'd, not injected. */
__attribute__((constructor))
static void init() {
@@ -229,6 +222,7 @@ static void init() {
^(xxpc_object_t reply) {
handle_xxpc_object(reply, true);
});
+ xxpc_release(message);
/* Timing out *always* means a bug (or the user manually unloaded
* substituted). Therefore, a high timeout is actually a good thing,
@@ -272,8 +266,6 @@ static void init() {
}
xxpc_release(hello_reply);
- atexit(inform_sud_of_clean_exit);
-
return;
bad:
ib_log("giving up on loading bundles for this process...");
diff --git a/darwin-bootstrap/posixspawn-hook.c b/darwin-bootstrap/posixspawn-hook.c
index 7c339f1..b2a7336 100644
--- a/darwin-bootstrap/posixspawn-hook.c
+++ b/darwin-bootstrap/posixspawn-hook.c
@@ -20,6 +20,8 @@
#include "ib-log.h"
#include "substitute.h"
#include "substitute-internal.h"
+#include "darwin/xxpc.h"
+#include "cbit/htab.h"
#include <mach/mach.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
@@ -30,15 +32,35 @@
#include <malloc/malloc.h>
#include <errno.h>
#include <arpa/inet.h>
+#include <pthread.h>
#include <libkern/OSByteOrder.h>
+#define _pid_hash(pidp) (*(pidp))
+#define _pid_eq(pid1p, pid2p) (*(pid1p) == *(pid2p))
+#define _pid_null(pidp) (!*(pidp))
+DECL_STATIC_HTAB_KEY(pid_t, pid_t, _pid_hash, _pid_eq, _pid_null, 0);
+DECL_HTAB(pid_str, pid_t, char *);
+
extern char ***_NSGetEnviron(void);
-static __typeof__(posix_spawn) *old_posix_spawn, *old_posix_spawnp,
- hook_posix_spawn, hook_posix_spawnp;
+struct au_tid;
+extern void audit_token_to_au32(audit_token_t, uid_t *, uid_t *, gid_t *,
+ uid_t *, gid_t *, pid_t *, pid_t *,
+ struct au_tid *);
+
+static typeof(posix_spawn) *old_posix_spawn, *old_posix_spawnp,
+ hook_posix_spawn, hook_posix_spawnp;
+static typeof(wait4) *old_wait4, hook_wait4;
+static typeof(waitpid) *old_waitpid, hook_waitpid;
static int (*old_sandbox_check)(pid_t, const char *, int type, ...);
+static int (*old_xpc_pipe_try_receive)(mach_port_t, xxpc_object_t *,
+ mach_port_t *, void *, size_t, int);
-static bool is_launchd;
+static bool g_is_launchd;
+static xxpc_object_t g_argv0_to_fate;
+static HTAB_STORAGE(pid_str) g_pid_to_argv0 =
+ HTAB_STORAGE_INIT_STATIC(&g_pid_to_argv0, pid_str);
+static pthread_mutex_t g_dicts_lock = PTHREAD_MUTEX_INITIALIZER;
static bool advance(char **strp, const char *template) {
size_t len = strlen(template);
@@ -174,7 +196,7 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
path,
(flags & POSIX_SPAWN_SETEXEC) ? " (exec)" : "",
(flags & POSIX_SPAWN_START_SUSPENDED) ? " (suspend)" : "",
- is_launchd);
+ g_is_launchd);
for (char *const *ap = argv; *ap; ap++)
ib_log(" %s", *ap);
}
@@ -185,9 +207,11 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
static const char psh_dylib[] =
"/Library/Substitute/Helpers/posixspawn-hook.dylib";
+ const char *argv0 = argv[0] ?: "";
+
/* which dylib should we add, if any? */
const char *dylib_to_add;
- if (is_launchd) {
+ if (g_is_launchd) {
if (strcmp(path, "/usr/libexec/xpcproxy"))
goto skip;
dylib_to_add = psh_dylib;
@@ -218,7 +242,7 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
*/
if (!strcmp(path, "/Library/Substitute/Helpers/substituted") ||
!strcmp(path, "/usr/sbin/notifyd") ||
- !strcmp(xbasename(argv[0] ?: ""), "sshd"))
+ !strcmp(xbasename(argv0), "sshd"))
goto skip;
dylib_to_add = bl_dylib;
}
@@ -354,6 +378,11 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
pid_t pid = *pidp;
if (need_unrestrict)
spawn_unrestrict(pid, !was_suspended, false);
+
+ pthread_mutex_lock(&g_dicts_lock);
+ *htab_setp_pid_str(&g_pid_to_argv0.h, &pid, NULL) = strdup(argv0);
+ pthread_mutex_unlock(&g_dicts_lock);
+
goto cleanup;
crap:
ib_log("posixspawn-hook: weird error - OOM? skipping our stuff");
@@ -382,6 +411,95 @@ int hook_posix_spawnp(pid_t *restrict pid, const char *restrict path,
attrp, argv, envp);
}
+static void after_wait_generic(pid_t pid, int stat) {
+ if (pid == -1)
+ return;
+ pthread_mutex_lock(&g_dicts_lock);
+ struct htab_bucket_pid_str *bucket =
+ htab_getbucket_pid_str(&g_pid_to_argv0.h, &pid);
+ if (!bucket) {
+ /* probably spawned some other way / not a task */
+ if (IB_VERBOSE)
+ ib_log("reaped unknown pid %d", pid);
+ return;
+ }
+ char *argv0 = bucket->value;
+ xxpc_dictionary_set_int64(g_argv0_to_fate, argv0, stat);
+ free(argv0);
+ htab_removeat_pid_str(&g_pid_to_argv0.h, bucket);
+ pthread_mutex_unlock(&g_dicts_lock);
+}
+
+pid_t hook_wait4(pid_t pid, int *stat_loc, int options, struct rusage *rusage) {
+ pid_t ret = old_wait4(pid, stat_loc, options, rusage);
+ after_wait_generic(ret, *stat_loc);
+ return ret;
+}
+
+pid_t hook_waitpid(pid_t pid, int *stat_loc, int options) {
+ pid_t ret = old_waitpid(pid, stat_loc, options);
+ after_wait_generic(ret, *stat_loc);
+ return ret;
+}
+
+int hook_xpc_pipe_try_receive(mach_port_t port_set, xxpc_object_t *requestp,
+ mach_port_t *recvp, void *mig_demux,
+ size_t msg_size, int dunno_ignored) {
+ int res = old_xpc_pipe_try_receive(port_set, requestp, recvp, mig_demux,
+ msg_size, dunno_ignored);
+ if (res)
+ return res;
+ xxpc_object_t request = *requestp;
+ if (!request || /* just to be sure */
+ xxpc_get_type(request) != XXPC_TYPE_DICTIONARY)
+ return res;
+ /* is it for us? - usage of "in"/"out" is to satisfy the public vproc API */
+ xxpc_object_t in = xxpc_dictionary_get_value(request, "in");
+ if (!in || xxpc_get_type(in) != XXPC_TYPE_DICTIONARY)
+ return res;
+ const char *name = xxpc_dictionary_get_string(in,
+ "com.ex.substitute.hook-operation");
+ if (!name)
+ return res;
+ /* is it from someone untrustworthy? */
+ audit_token_t at;
+ xxpc_dictionary_get_audit_token(request, &at);
+ uid_t euid;
+ pid_t pid;
+ audit_token_to_au32(at, NULL, &euid, NULL, NULL, NULL, &pid, NULL, NULL);
+ if (euid != 0) {
+ ib_log("Attempt to perform hook-operation by pid %d with euid %d",
+ euid, pid);
+ return res;
+ }
+ xxpc_object_t reply = NULL;
+ if (!strcmp(name, "argv0-to-fate")) {
+ const char *argv0 = xxpc_dictionary_get_string(in, "argv0");
+ if (!argv0) {
+ ib_log("invalid hook-operation message");
+ return res;
+ }
+ reply = xxpc_dictionary_create_reply(request);
+ xxpc_object_t fate = xxpc_dictionary_get_value(g_argv0_to_fate, argv0);
+ if (fate)
+ xxpc_dictionary_set_value(reply, "out", fate);
+ } else {
+ ib_log("unknown hook-operation '%s'", name);
+ return res;
+ }
+ if (reply) {
+ int reply_res = xxpc_pipe_routine_reply(reply);
+ if (reply_res) {
+ ib_log("xxpc_pipe_routine_reply: %d", reply_res);
+ return res;
+ }
+ xxpc_release(reply);
+ }
+ xxpc_release(request);
+ *requestp = NULL;
+ return 0;
+}
+
int hook_sandbox_check(pid_t pid, const char *op, int type, ...) {
/* Can't easily determine the number of arguments, so just assume there's
* less than 5 pointers' worth. */
@@ -438,9 +556,10 @@ static void init() {
* (it also decreases the amount of library code necessary to load from
* disk...)
*/
+ g_argv0_to_fate = xxpc_dictionary_create(NULL, NULL, 0);
const char *image0 = _dyld_get_image_name(0);
- is_launchd = !!strstr(image0, "launchd");
+ g_is_launchd = !!strstr(image0, "launchd");
struct substitute_image *im = substitute_open_image(image0);
if (!im) {
ib_log("posixspawn-hook: substitute_open_image failed");
@@ -451,6 +570,10 @@ static void init() {
{"_posix_spawn", hook_posix_spawn, &old_posix_spawn},
{"_posix_spawnp", hook_posix_spawnp, &old_posix_spawnp},
{"_sandbox_check", hook_sandbox_check, &old_sandbox_check},
+ {"_waitpid", hook_waitpid, &old_waitpid},
+ {"_wait4", hook_wait4, &old_wait4},
+ {"_xpc_pipe_try_receive", hook_xpc_pipe_try_receive,
+ &old_xpc_pipe_try_receive},
};
int err = substitute_interpose_imports(im, hooks, sizeof(hooks)/sizeof(*hooks),
diff --git a/darwin-bootstrap/substituted.m b/darwin-bootstrap/substituted.m
index 2c3217b..f03670c 100644
--- a/darwin-bootstrap/substituted.m
+++ b/darwin-bootstrap/substituted.m
@@ -3,6 +3,9 @@
#include "darwin/xxpc.h"
#include "substitute.h"
+void *vproc_swap_complex(void *vp, int key, xxpc_object_t inval,
+ __strong xxpc_object_t *outval);
+
/* This is a daemon contacted by all processes which can load extensions. It
* currently does the work of reading the plists in
* /Library/Substitute/DynamicLibraries in order to avoid loading objc/CF
@@ -59,7 +62,7 @@ static xxpc_object_t nsstring_to_xpc(NSString *in) {
@interface PeerHandler : NSObject {
xxpc_object_t _connection;
NSString *_argv0;
- bool _is_springboard, _got_bye;
+ bool _is_springboard;
}
@end
@@ -197,20 +200,77 @@ enum convert_filters_ret {
return PROVISIONAL_PASS;
}
-- (bool)handleMessageHello:(xxpc_object_t)request {
+- (void)updateSpringBoardNeedsSafe:(const char *)argv0 then:(void (^)())then {
+ xxpc_object_t inn = xxpc_dictionary_create(NULL, NULL, 0);
+ xxpc_dictionary_set_string(inn, "com.ex.substiute.hook-operation",
+ "argv0-to-fate");
+ xxpc_dictionary_set_string(inn, "argv0", argv0);
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
+ ^{
+ xxpc_object_t out = NULL;
+ vproc_swap_complex(NULL, 99999, inn, &out);
+ int to_set = REALLY_SAFE;
+ if (!out) {
+ NSLog(@"couldn't talk to launchd :( - assume worst case scenario");
+ goto out;
+ }
+ if (xxpc_get_type(out) != XXPC_TYPE_INT64) {
+ NSLog(@"wrong type from launchd!?");
+ goto out;
+ }
+
+ int stat = (int) xxpc_int64_get_value(out);
+ bool crashed = WIFSIGNALED(stat) && WTERMSIG(stat) != SIGTERM;
+ if (crashed) {
+ if (g_springboard_needs_safe) {
+ NSLog(@"SpringBoard hung up more than once without without saying bye; using Really Safe Mode (no UI) next time :(");
+ to_set = REALLY_SAFE;
+ } else {
+ NSLog(@"SpringBoard hung up without saying bye; using safe mode next time.");
+ to_set = NEEDS_SAFE;
+ }
+ } else {
+ to_set = NO_SAFE;
+ }
+
+ out:
+ dispatch_async(dispatch_get_main_queue(), ^{
+ g_springboard_needs_safe = to_set;
+ then();
+ });
+ });
+}
+
+- (void)handleMessageHello:(NS_VALID_UNTIL_END_OF_SCOPE xxpc_object_t)request {
+ NSString *sb_exe =
+ @"/System/Library/CoreServices/SpringBoard.app/SpringBoard";
+
if (_argv0 != NULL)
- return false;
+ goto bad;
const char *argv0 = xxpc_dictionary_get_string(request, "argv0");
if (!argv0)
- return false;
+ goto bad;
_argv0 = [NSString stringWithCString:argv0
encoding:NSUTF8StringEncoding];
- NSString *sb_exe =
- @"/System/Library/CoreServices/SpringBoard.app/SpringBoard";
_is_springboard = [_argv0 isEqualToString:sb_exe];
+ if (_is_springboard)
+ [self updateSpringBoardNeedsSafe:argv0
+ then:^{[self handleMessageHelloRest:request];}];
+ else
+ [self handleMessageHelloRest:request];
+ return;
+
+bad:
+ [self handleBadMessage:request];
+}
+
+- (void)handleMessageHelloRest:(NS_VALID_UNTIL_END_OF_SCOPE
+ xxpc_object_t)request {
+
xxpc_object_t bundles = xxpc_array_create(NULL, 0);
NSError *err;
@@ -218,8 +278,6 @@ enum convert_filters_ret {
NSArray *list = [[NSFileManager defaultManager]
contentsOfDirectoryAtPath:base
error:&err];
- if (!list)
- return bundles;
for (NSString *dylib in list) {
if (![[dylib pathExtension] isEqualToString:@"dylib"])
@@ -255,44 +313,23 @@ enum convert_filters_ret {
xxpc_object_t reply = xxpc_dictionary_create_reply(request);
xxpc_dictionary_set_value(reply, "bundles", bundles);
xxpc_connection_send_message(_connection, reply);
- return true;
-}
-
-- (bool)handleMessageBye:(xxpc_object_t)request {
- _got_bye = true;
- return true;
}
-- (void)handleHangup {
- /* this could be false because hello hasn't been sent, but in that case it
- * hasn't loaded any substitute dylibs, so not our problem *whistle* */
- if (_is_springboard) {
- bool needs_safe = !_got_bye;
- if (needs_safe) {
- if (g_springboard_needs_safe) {
- NSLog(@"SpringBoard hung up more than once without without saying bye; using Really Safe Mode (no UI) next time :(");
- g_springboard_needs_safe = REALLY_SAFE;
- } else {
- NSLog(@"SpringBoard hung up without saying bye; using safe mode next time.");
- g_springboard_needs_safe = NEEDS_SAFE;
- }
- } else {
- g_springboard_needs_safe = NO_SAFE;
- }
- }
+- (void)handleBadMessage:(xxpc_object_t)request {
+ NSLog(@"bad message received: %@", request);
+ xxpc_connection_cancel(_connection);
}
-
-- (bool)handleMessage:(xxpc_object_t)request {
+- (void)handleMessage:(NS_VALID_UNTIL_END_OF_SCOPE xxpc_object_t)request {
const char *type = xxpc_dictionary_get_string(request, "type");
if (!type)
- return false;
+ goto bad;
if (!strcmp(type, "hello"))
return [self handleMessageHello:request];
- else if (!strcmp(type, "bye"))
- return [self handleMessageBye:request];
else
- return false;
+ goto bad;
+bad:
+ return [self handleBadMessage:request];
}
- (instancetype)initWithConnection:(xxpc_object_t)connection {
@@ -300,11 +337,10 @@ enum convert_filters_ret {
xxpc_connection_set_event_handler(connection, ^(xxpc_object_t event) {
xxpc_type_t ty = xxpc_get_type(event);
if (ty == XXPC_TYPE_DICTIONARY) {
- if (![self handleMessage:event])
- xxpc_connection_cancel(connection);
+ [self handleMessage:event];
} else if (ty == XXPC_TYPE_ERROR) {
if (event == XXPC_ERROR_CONNECTION_INVALID) {
- [self handleHangup];
+ /* [self handleHangup]; */
} else {
NSLog(@"XPC error from connection: %@", event);
xxpc_connection_cancel(connection);
@@ -322,7 +358,8 @@ int main() {
NSLog(@"hello from substituted");
install_deadlock_warning();
xxpc_connection_t listener = xxpc_connection_create_mach_service(
- "com.ex.substituted", NULL, XXPC_CONNECTION_MACH_SERVICE_LISTENER);
+ "com.ex.substituted", dispatch_get_main_queue(),
+ XXPC_CONNECTION_MACH_SERVICE_LISTENER);
if (!listener) {
NSLog(@"xxpc_connection_create_mach_service returned null");
exit(1);
diff --git a/lib/cbit/htab.h b/lib/cbit/htab.h
index ccb40b9..054b423 100644
--- a/lib/cbit/htab.h
+++ b/lib/cbit/htab.h
@@ -42,11 +42,11 @@ struct htab_internal {
void *__htab_key_lookup_##name(struct htab_internal *restrict hi, \
const key_ty *restrict key, \
size_t entry_size, \
- bool add, bool resize_if_necessary); \
+ bool add); \
func_decl \
- bool __htab_key_remove_##name(struct htab_internal *restrict hi, \
- const key_ty *restrict key, \
- size_t entry_size); \
+ void __htab_key_removeat_##name(struct htab_internal *restrict hi, \
+ void *op, \
+ size_t entry_size); \
func_decl \
void __htab_key_memset_##name(void *ptr, size_t size); \
func_decl \
@@ -81,8 +81,8 @@ struct htab_internal {
void *__htab_key_lookup_##name(struct htab_internal *restrict hi, \
const key_ty *restrict key, \
size_t entry_size, \
- bool add, bool resize_if_necessary) { \
- if (resize_if_necessary && \
+ bool add) { \
+ if (add && \
hi->capacity * 2 <= hi->length * 3) \
__htab_key_resize_##name(hi, hi->capacity * 2, entry_size); \
size_t capacity = hi->capacity; \
@@ -101,17 +101,14 @@ struct htab_internal {
} \
if (eq_func(bucket, key)) \
return bucket; \
- } while ((i = (i + 1) % capacity) != hash); \
+ } while (i = (i + 1) == capacity ? 0 : (i + 1), i != hash); \
return NULL; \
} \
\
/* slow but who cares */ \
- bool __htab_key_remove_##name(struct htab_internal *restrict hi, \
- const key_ty *restrict key, \
- size_t entry_size) { \
- void *op = __htab_key_lookup_##name(hi, key, entry_size, false, false); \
- if (!op) \
- return false; \
+ void __htab_key_removeat_##name(struct htab_internal *restrict hi, \
+ void *op, \
+ size_t entry_size) { \
key_ty *orig = op; \
key_ty *end = (void *) ((char *) hi->base + hi->capacity * entry_size); \
key_ty *cur = orig; \
@@ -126,9 +123,8 @@ struct htab_internal {
memmove(prev, cur, entry_size); \
prev = cur; \
} while (cur != orig); \
- memset(cur, 0, entry_size); \
+ memset(cur, nil_byte, entry_size); \
hi->length--; \
- return true; \
} \
void __htab_key_memset_##name(void *ptr, size_t size) { \
memset(ptr, (nil_byte), size); \
@@ -152,7 +148,7 @@ struct htab_internal {
if (!null_func(bucket)) { \
memcpy( \
__htab_key_lookup_##name(&temp, bucket, entry_size, \
- true, false), \
+ true), \
bucket, \
entry_size); \
} \
@@ -199,7 +195,7 @@ struct htab_internal {
bucket_ty *htab_getbucket_##name(htab_ty *restrict ht, \
const key_ty *restrict key) { \
return __htab_key_lookup_##key_name(&ht->hi, key, sizeof(bucket_ty), \
- false, true); \
+ false); \
} \
UNUSED_STATIC_INLINE \
value_ty *htab_getp_##name(const htab_ty *restrict ht, \
@@ -211,7 +207,7 @@ struct htab_internal {
bucket_ty *htab_setbucket_##name(htab_ty *restrict ht, \
const key_ty *restrict key) { \
return __htab_key_lookup_##key_name(&ht->hi, key, sizeof(bucket_ty), \
- true, true); \
+ true); \
} \
UNUSED_STATIC_INLINE \
value_ty *htab_setp_##name(const htab_ty *restrict ht, \
@@ -231,7 +227,16 @@ struct htab_internal {
} \
UNUSED_STATIC_INLINE \
bool htab_remove_##name(htab_ty *restrict ht, const key_ty *restrict key) { \
- return __htab_key_remove_##key_name(&ht->hi, key, sizeof(bucket_ty)); \
+ void *op = __htab_key_lookup_##key_name(&ht->hi, key, sizeof(bucket_ty), \
+ false); \
+ if (!op) \
+ return false; \
+ __htab_key_removeat_##key_name(&ht->hi, op, sizeof(bucket_ty)); \
+ return true; \
+ } \
+ UNUSED_STATIC_INLINE \
+ void htab_removeat_##name(htab_ty *restrict ht, bucket_ty *op) { \
+ __htab_key_removeat_##key_name(&ht->hi, op, sizeof(bucket_ty)); \
} \
UNUSED_STATIC_INLINE \
void __htab_memset_##name(void *ptr, size_t size) { \
@@ -263,12 +268,19 @@ struct htab_internal {
#define HTAB_STORAGE_INIT(hs, name) do { \
struct htab_##name *h = &(hs)->h; \
h->length = 0; \
- h->capacity = (sizeof((hs)->rest) / sizeof(__htab_key_ty_##name)) + 1; \
+ h->capacity = (sizeof((hs)->rest) / sizeof(struct htab_bucket_##name)) + 1; \
h->base = h->storage; \
__htab_memset_##name(h->base, \
- h->capacity * sizeof(__htab_key_ty_##name)); \
+ h->capacity * sizeof(struct htab_bucket_##name)); \
} while (0)
+/* only works if nil_byte is 0 */
+#define HTAB_STORAGE_INIT_STATIC(hs, name) \
+ {{0, \
+ (sizeof((hs)->rest) / sizeof(struct htab_bucket_##name)) + 1, \
+ (hs)->h.storage \
+ }}
+
#define HTAB_FOREACH(ht, key_var, val_var, name) \
LET(struct htab_##name *__htfe_ht = (ht)) \
for (size_t __htfe_bucket = 0; \
diff --git a/lib/darwin/xxpc.h b/lib/darwin/xxpc.h
index 5caed15..4b5e175 100644
--- a/lib/darwin/xxpc.h
+++ b/lib/darwin/xxpc.h
@@ -3,6 +3,7 @@
* on OS X. */
#include <dispatch/dispatch.h>
#include <os/object.h>
+#include <mach/message.h> /* for audit_token_t */
#if OS_OBJECT_USE_OBJC
#define DC_CAST (__bridge xxpc_object_t)
@@ -23,6 +24,7 @@ DEFINE_CONST(XXPC_TYPE_ERROR, _xpc_type_error);
DEFINE_CONST(XXPC_TYPE_DICTIONARY, _xpc_type_dictionary);
DEFINE_CONST(XXPC_TYPE_ARRAY, _xpc_type_array);
DEFINE_CONST(XXPC_TYPE_STRING, _xpc_type_string);
+DEFINE_CONST(XXPC_TYPE_INT64, _xpc_type_int64);
DEFINE_CONST(XXPC_ERROR_CONNECTION_INTERRUPTED,
_xpc_error_connection_interrupted);
DEFINE_CONST(XXPC_ERROR_CONNECTION_INVALID,
@@ -44,6 +46,7 @@ char *WRAP(xpc_copy_description, (xxpc_object_t));
xxpc_type_t WRAP(xpc_get_type, (xxpc_object_t));
xxpc_object_t WRAP(xpc_string_create, (const char *));
const char *WRAP(xpc_string_get_string_ptr, (xxpc_object_t));
+int64_t WRAP(xpc_int64_get_value, (xxpc_object_t));
void WRAP(xpc_array_append_value, (xxpc_object_t, xxpc_object_t));
xxpc_object_t WRAP(xpc_array_create, (const xxpc_object_t *, size_t));
size_t WRAP(xpc_array_get_count, (const xxpc_object_t));
@@ -61,7 +64,10 @@ const char *WRAP(xpc_dictionary_get_string, (xxpc_object_t, const char *));
xxpc_object_t WRAP(xpc_dictionary_get_value, (xxpc_object_t, const char *));
void WRAP(xpc_dictionary_set_bool, (xxpc_object_t, const char *, bool));
void WRAP(xpc_dictionary_set_string, (xxpc_object_t, const char *, const char *));
+void WRAP(xpc_dictionary_set_uint64, (xxpc_object_t, const char *, uint64_t));
+void WRAP(xpc_dictionary_set_int64, (xxpc_object_t, const char *, int64_t));
void WRAP(xpc_dictionary_set_value, (xxpc_object_t, const char *, xxpc_object_t));
+void WRAP(xpc_dictionary_get_audit_token, (xxpc_object_t, audit_token_t *));
xxpc_connection_t WRAP(xpc_connection_create_mach_service, (const char *,
dispatch_queue_t,
@@ -72,6 +78,8 @@ void WRAP(xpc_connection_send_message_with_reply,
(xxpc_connection_t, xxpc_object_t, dispatch_queue_t, xxpc_handler_t));
void WRAP(xpc_connection_send_message, (xxpc_connection_t, xxpc_object_t));
void WRAP(xpc_connection_cancel, (xxpc_connection_t));
+int WRAP(xpc_pipe_routine_reply, (xxpc_object_t));
+
#undef DEFINE_TYPE
#undef DEFINE_CONST