aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure2
-rw-r--r--lib/darwin/find-syms.c208
-rw-r--r--lib/substitute-internal.h2
-rw-r--r--vendor/dyld_cache_format.h104
4 files changed, 304 insertions, 12 deletions
diff --git a/configure b/configure
index 8c0fbf7..c86dda5 100755
--- a/configure
+++ b/configure
@@ -70,7 +70,7 @@ if 'armv7' in cc_argv or 'arm64' in cc_argv:
settings.extra_link_deps = ['(src)/ent.plist']
settings.host.debug_info = True
-settings.c_includes = ['(src)/lib', '(src)/substrate']
+settings.c_includes = ['(src)/lib', '(src)/substrate', '(src)/vendor']
emitter = settings.emitter
diff --git a/lib/darwin/find-syms.c b/lib/darwin/find-syms.c
index 6b07591..ce1d687 100644
--- a/lib/darwin/find-syms.c
+++ b/lib/darwin/find-syms.c
@@ -3,9 +3,13 @@
#include <stdbool.h>
#include <dlfcn.h>
#include <pthread.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <fcntl.h>
#include "substitute.h"
#include "substitute-internal.h"
+#include "dyld_cache_format.h"
extern const struct dyld_all_image_infos *_dyld_get_all_image_infos();
@@ -14,7 +18,7 @@ static pthread_once_t dyld_inspect_once = PTHREAD_ONCE_INIT;
static uintptr_t (*ImageLoaderMachO_getSlide)(void *);
static const struct mach_header *(*ImageLoaderMachO_machHeader)(void *);
-static void *sym_to_ptr(substitute_sym *sym, intptr_t slide) {
+static void *sym_to_ptr(const substitute_sym *sym, intptr_t slide) {
uintptr_t addr = sym->n_value;
addr += slide;
if (sym->n_desc & N_ARM_THUMB_DEF)
@@ -22,11 +26,182 @@ static void *sym_to_ptr(substitute_sym *sym, intptr_t slide) {
return (void *) addr;
}
+static const struct dyld_cache_header *_Atomic s_cur_shared_cache_hdr;
+static int s_cur_shared_cache_fd;
+static pthread_once_t s_open_cache_once = PTHREAD_ONCE_INIT;
+static struct dyld_cache_local_symbols_info s_cache_local_symbols_info;
+static struct dyld_cache_local_symbols_entry *s_cache_local_symbols_entries;
+
+static bool oscf_try_dir(const char *dir, const char *arch,
+ const struct dyld_cache_header *dch) {
+ char path[PATH_MAX];
+ if (snprintf(path, sizeof(path), "%s/%s%s", dir,
+ DYLD_SHARED_CACHE_BASE_NAME, arch) >= sizeof(path))
+ return false;
+ int fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return false;
+ struct dyld_cache_header this_dch;
+ if (read(fd, &this_dch, sizeof(this_dch)) != sizeof(this_dch))
+ goto fail;
+ if (memcmp(this_dch.uuid, dch->uuid, 16) ||
+ this_dch.localSymbolsSize != dch->localSymbolsSize /* just in case */)
+ goto fail;
+ s_cur_shared_cache_fd = fd;
+ struct dyld_cache_local_symbols_info *lsi = &s_cache_local_symbols_info;
+ if (pread(fd, lsi, sizeof(*lsi), dch->localSymbolsOffset) != sizeof(*lsi))
+ goto fail;
+ if (lsi->nlistOffset > dch->localSymbolsSize ||
+ lsi->nlistCount > (dch->localSymbolsSize - lsi->nlistOffset)
+ / sizeof(substitute_sym) ||
+ lsi->stringsOffset > dch->localSymbolsSize ||
+ lsi->stringsSize > dch->localSymbolsSize - lsi->stringsOffset) {
+ /* bad format */
+ goto fail;
+ }
+ uint32_t count = lsi->entriesCount;
+ if (count > 1000000)
+ goto fail;
+ struct dyld_cache_local_symbols_entry *lses;
+ size_t lses_size = count * sizeof(*lses);
+ if (!(lses = malloc(lses_size)))
+ goto fail;
+ if (pread(fd, lses, lses_size, dch->localSymbolsOffset + lsi->entriesOffset)
+ != lses_size) {
+ free(lses);
+ goto fail;
+ }
+
+ s_cache_local_symbols_entries = lses;
+ return true;
+
+fail:
+ close(fd);
+ return false;
+}
+
+static void open_shared_cache_file_once() {
+ s_cur_shared_cache_fd = -1;
+ const struct dyld_cache_header *dch = s_cur_shared_cache_hdr;
+ if (memcmp(dch->magic, "dyld_v1 ", 8))
+ return;
+ const char *archp = &dch->magic[8];
+ while (*archp == ' ')
+ archp++;
+ static char filename[32];
+ const char *env_dir = getenv("DYLD_SHARED_CACHE_DIR");
+ if (env_dir) {
+ if (oscf_try_dir(env_dir, archp, dch))
+ return;
+ }
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ oscf_try_dir(IPHONE_DYLD_SHARED_CACHE_DIR, archp, dch);
+#else
+ oscf_try_dir(MACOSX_DYLD_SHARED_CACHE_DIR, archp, dch);
+#endif
+}
+
+static bool ul_mmap(int fd, off_t offset, size_t size,
+ void *data_p, void **mapping_p, size_t *mapping_size_p) {
+ int pmask = getpagesize() - 1;
+ int page_off = offset & pmask;
+ off_t map_offset = offset & ~pmask;
+ size_t map_size = ((offset + size + pmask) & ~pmask) - map_offset;
+ void *mapping = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, map_offset);
+ if (mapping == MAP_FAILED)
+ return false;
+ *(void **) data_p = (char *) mapping + page_off;
+ *mapping_p = mapping;
+ *mapping_size_p = map_size;
+ return true;
+}
+
+static bool get_shared_cache_syms(const void *hdr,
+ const substitute_sym **syms_p,
+ const char **strs_p,
+ size_t *nsyms_p,
+ void **mapping_p,
+ size_t *mapping_size_p) {
+ pthread_once(&s_open_cache_once, open_shared_cache_file_once);
+ int fd = s_cur_shared_cache_fd;
+ if (fd == -1)
+ return false;
+ const struct dyld_cache_header *dch = s_cur_shared_cache_hdr;
+ const struct dyld_cache_local_symbols_info *lsi = &s_cache_local_symbols_info;
+ struct dyld_cache_local_symbols_entry lse;
+ for (uint32_t i = 0; i < lsi->entriesCount; i++) {
+ lse = s_cache_local_symbols_entries[i];
+ if (lse.dylibOffset == (uintptr_t) hdr - (uintptr_t) dch)
+ goto got_lse;
+ }
+ return false;
+got_lse:
+ /* map - we don't do this persistently to avoid wasting address space on
+ * iOS (my random OS X 10.10 blob pushes 55MB) */
+ if (lse.nlistStartIndex > lsi->nlistCount ||
+ lsi->nlistCount - lse.nlistStartIndex < lse.nlistCount)
+ return false;
+
+ char *ls_data;
+ if (!ul_mmap(fd, dch->localSymbolsOffset, dch->localSymbolsSize,
+ &ls_data, mapping_p, mapping_size_p))
+ return false;
+ const substitute_sym *syms = (void *) (ls_data + lsi->nlistOffset);
+ *syms_p = syms + lse.nlistStartIndex;
+ *strs_p = ls_data + lsi->stringsOffset;
+ *nsyms_p = lse.nlistCount;
+ return true;
+}
+
+
+static const struct dyld_cache_header *get_cur_shared_cache_hdr() {
+ const struct dyld_cache_header *dch = s_cur_shared_cache_hdr;
+ if (!dch) {
+ /* race is OK */
+ uint64_t start_address = 0;
+ if (syscall(294, &start_address)) /* shared_region_check_np */
+ dch = (void *) 1;
+ else
+ dch = (void *) (uintptr_t) start_address;
+ s_cur_shared_cache_hdr = dch;
+ }
+ return dch == (void *) 1 ? NULL : dch;
+}
+
+static bool addr_in_shared_cache(const void *addr) {
+ const struct dyld_cache_header *dch = get_cur_shared_cache_hdr();
+ if (!dch)
+ return false;
+
+ uint32_t mapping_count = dch->mappingCount;
+ const struct dyld_cache_mapping_info *mappings =
+ (void *) ((char *) dch + dch->mappingOffset);
+ intptr_t slide = (uintptr_t) dch - (uintptr_t) mappings[0].address;
+
+ for (uint32_t i = 0; i < mapping_count; i++) {
+ const struct dyld_cache_mapping_info *mapping = &mappings[i];
+ uintptr_t diff = (uintptr_t) addr -
+ ((uintptr_t) mapping->address + slide);
+ if (diff < mapping->size)
+ return true;
+ }
+ return false;
+}
+
static void find_syms_raw(const void *hdr, intptr_t *restrict slide,
const char **restrict names, void **restrict syms,
size_t nsyms) {
memset(syms, 0, sizeof(*syms) * nsyms);
+ void *mapping = NULL;
+ size_t mapping_size = 0;
+ const substitute_sym *cache_syms = NULL;
+ const char *cache_strs = NULL;
+ size_t ncache_syms = 0;
+ if (addr_in_shared_cache(hdr))
+ get_shared_cache_syms(hdr, &cache_syms, &cache_strs, &ncache_syms,
+ &mapping, &mapping_size);
+
/* note: no verification at all */
const mach_header_x *mh = hdr;
uint32_t ncmds = mh->ncmds;
@@ -64,18 +239,28 @@ ok: ;
ok2: ;
symtab = (void *) symtab + *slide;
strtab = (void *) strtab + *slide;
- /* This could be optimized for efficiency with a large number of names... */
- for (uint32_t i = 0; i < syc.nsyms; i++) {
- substitute_sym *sym = &symtab[i];
- uint32_t strx = sym->n_un.n_strx;
- const char *name = strx == 0 ? "" : strtab + strx;
- for (size_t j = 0; j < nsyms; j++) {
- if (!strcmp(name, names[j])) {
- syms[j] = sym_to_ptr(sym, *slide);
- break;
+
+ for (int type = 0; type <= 1; type++) {
+ const substitute_sym *this_symtab = type ? cache_syms : symtab;
+ const char *this_strtab = type ? cache_strs : strtab;
+ size_t this_nsyms = type ? ncache_syms : syc.nsyms;
+ /* This could be optimized for efficiency with a large number of
+ * names... */
+ for (uint32_t i = 0; i < syc.nsyms; i++) {
+ const substitute_sym *sym = &this_symtab[i];
+ uint32_t strx = sym->n_un.n_strx;
+ const char *name = strx == 0 ? "" : this_strtab + strx;
+ for (size_t j = 0; j < nsyms; j++) {
+ if (!strcmp(name, names[j])) {
+ syms[j] = sym_to_ptr(sym, *slide);
+ break;
+ }
}
}
}
+
+ if (mapping_size)
+ munmap(mapping, mapping_size);
}
/* This is a mess because the usual _dyld_image_count loop is not thread safe.
@@ -92,7 +277,8 @@ static void inspect_dyld() {
const struct dyld_all_image_infos *aii = _dyld_get_all_image_infos();
const void *dyld_hdr = aii->dyldImageLoadAddress;
- const char *names[2] = { "__ZNK16ImageLoaderMachO8getSlideEv", "__ZNK16ImageLoaderMachO10machHeaderEv" };
+ const char *names[2] = { "__ZNK16ImageLoaderMachO8getSlideEv",
+ "__ZNK16ImageLoaderMachO10machHeaderEv" };
void *syms[2];
intptr_t dyld_slide = -1;
find_syms_raw(dyld_hdr, &dyld_slide, names, syms, 2);
diff --git a/lib/substitute-internal.h b/lib/substitute-internal.h
index 675f6eb..c9b78f0 100644
--- a/lib/substitute-internal.h
+++ b/lib/substitute-internal.h
@@ -16,6 +16,7 @@
#include <mach-o/loader.h>
#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
+#include <mach-o/nlist.h>
#ifdef __LP64__
typedef struct mach_header_64 mach_header_x;
typedef struct segment_command_64 segment_command_x;
@@ -27,6 +28,7 @@ typedef struct segment_command segment_command_x;
typedef struct section section_x;
#define LC_SEGMENT_X LC_SEGMENT
#endif
+/* instead of 'nlist_x', use substitute_sym */
#endif
/* FORCE_* are for tests */
diff --git a/vendor/dyld_cache_format.h b/vendor/dyld_cache_format.h
new file mode 100644
index 0000000..18074d8
--- /dev/null
+++ b/vendor/dyld_cache_format.h
@@ -0,0 +1,104 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#ifndef __DYLD_CACHE_FORMAT__
+#define __DYLD_CACHE_FORMAT__
+
+#include <stdint.h>
+
+
+struct dyld_cache_header
+{
+ char magic[16]; // e.g. "dyld_v0 i386"
+ uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info
+ uint32_t mappingCount; // number of dyld_cache_mapping_info entries
+ uint32_t imagesOffset; // file offset to first dyld_cache_image_info
+ uint32_t imagesCount; // number of dyld_cache_image_info entries
+ uint64_t dyldBaseAddress; // base address of dyld when cache was built
+ uint64_t codeSignatureOffset; // file offset of code signature blob
+ uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file)
+ uint64_t slideInfoOffset; // file offset of kernel slid info
+ uint64_t slideInfoSize; // size of kernel slid info
+ uint64_t localSymbolsOffset; // file offset of where local symbols are stored
+ uint64_t localSymbolsSize; // size of local symbols information
+ uint8_t uuid[16]; // unique value for each shared cache file
+};
+
+struct dyld_cache_mapping_info {
+ uint64_t address;
+ uint64_t size;
+ uint64_t fileOffset;
+ uint32_t maxProt;
+ uint32_t initProt;
+};
+
+struct dyld_cache_image_info
+{
+ uint64_t address;
+ uint64_t modTime;
+ uint64_t inode;
+ uint32_t pathFileOffset;
+ uint32_t pad;
+};
+
+struct dyld_cache_slide_info
+{
+ uint32_t version; // currently 1
+ uint32_t toc_offset;
+ uint32_t toc_count;
+ uint32_t entries_offset;
+ uint32_t entries_count;
+ uint32_t entries_size; // currently 128
+ // uint16_t toc[toc_count];
+ // entrybitmap entries[entries_count];
+};
+
+
+struct dyld_cache_local_symbols_info
+{
+ uint32_t nlistOffset; // offset into this chunk of nlist entries
+ uint32_t nlistCount; // count of nlist entries
+ uint32_t stringsOffset; // offset into this chunk of string pool
+ uint32_t stringsSize; // byte count of string pool
+ uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry
+ uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array
+};
+
+struct dyld_cache_local_symbols_entry
+{
+ uint32_t dylibOffset; // offset in cache file of start of dylib
+ uint32_t nlistStartIndex; // start index of locals for this dylib
+ uint32_t nlistCount; // number of local symbols for this dylib
+};
+
+
+
+#define MACOSX_DYLD_SHARED_CACHE_DIR "/var/db/dyld/"
+#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/"
+#define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_"
+
+
+
+#endif // __DYLD_CACHE_FORMAT__
+
+