Skip to content

Instantly share code, notes, and snippets.

@comex
Created June 27, 2010 18:50
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save comex/455086 to your computer and use it in GitHub Desktop.
Save comex/455086 to your computer and use it in GitHub Desktop.
// DySlim is complicated and requires writing 6GB to disk (if only temporarily).
// This lets you mount the dyld shared cache via FUSE; the resulting files are weird but readable by things like otool and strings.
//
// gcc -std=gnu99 -I/opt/local/include -L/opt/local/lib -D_FILE_OFFSET_BITS=64 -o dsc dsc.c -lfuse -framework CoreFoundation
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <stdbool.h>
#include <mach/shared_region.h> // struct shared_file_mapping_np
#include <mach-o/loader.h>
#include <errno.h>
#include <CoreFoundation/CoreFoundation.h>
#define FUSE_USE_VERSION 26
#include <fuse.h>
// from dyld
struct dyld_cache_header
{
char magic[16]; // e.g. "dyld_v0 ppc"
uint32_t mappingOffset; // file offset to first shared_file_mapping_np
uint32_t mappingCount; // number of shared_file_mapping_np 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
};
struct dyld_cache_image_info
{
uint64_t address;
uint64_t modTime;
uint64_t inode;
uint32_t pathFileOffset;
uint32_t pad;
};
struct dyld_cache_header ch;
int fd;
off_t cache_size;
static int dc_getattr(const char *path, struct stat *stbuf) {
memset(stbuf, 0, sizeof(struct stat));
int path_len = strlen(path);
bool ends_with_slash = path[path_len-1] == '/';
lseek(fd, ch.imagesOffset, SEEK_SET);
for(int i = 0; i < ch.imagesCount; i++) {
struct dyld_cache_image_info ii;
assert(pread(fd, &ii, sizeof(ii), ch.imagesOffset + i*sizeof(ii)) == sizeof(ii));
char stuff[256];
pread(fd, &stuff, sizeof(stuff), ii.pathFileOffset);
if(!strcmp(stuff, path)) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = cache_size + 20*1024*1024; // LINKEDIT extends beyond the end of the cache!?
stbuf->st_ino = ii.inode;
return 0;
}
if(!memcmp(stuff, path, path_len) && (ends_with_slash || stuff[path_len] == '/')) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
return 0;
}
}
return -ENOENT;
}
static int dc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi) {
int path_len = strlen(path);
bool ends_with_slash = path[path_len-1] == '/';
lseek(fd, ch.imagesOffset, SEEK_SET);
CFMutableSetRef heard_of = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
for(int i = 0; i < ch.imagesCount; i++) {
struct dyld_cache_image_info ii;
assert(pread(fd, &ii, sizeof(ii), ch.imagesOffset + i*sizeof(ii)) == sizeof(ii));
char stuff[256];
pread(fd, &stuff, sizeof(stuff), ii.pathFileOffset);
if(!memcmp(stuff, path, path_len) && (ends_with_slash || stuff[path_len] == '/')) {
char *ent = strdup(stuff + path_len + (ends_with_slash?0:1));
char *slash = strchr(ent, '/'); if(slash) *slash = 0;
CFStringRef ents = CFStringCreateWithCString(NULL, ent, kCFStringEncodingASCII);
if(!CFSetContainsValue(heard_of, ents)) {
CFSetAddValue(heard_of, ents);
filler(buf, ent, NULL, 0);
}
CFRelease(ents);
}
}
CFRelease(heard_of);
return 0;
}
static int dc_open(const char *path, struct fuse_file_info *fi) {
fprintf(stderr, "open\n");
lseek(fd, ch.imagesOffset, SEEK_SET);
for(int i = 0; i < ch.imagesCount; i++) {
struct dyld_cache_image_info ii;
assert(pread(fd, &ii, sizeof(ii), ch.imagesOffset + i*sizeof(ii)) == sizeof(ii));
char stuff[256];
pread(fd, &stuff, sizeof(stuff), ii.pathFileOffset);
if(!strcmp(stuff, path)) {
fi->fh = ii.address;
return 0;
}
}
return -ENOENT;
}
static int dc_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi) {
if(offset < 0x5000) {
// Beginning of file; either from the client assuming the mach-o will start at 0, or the initial LC_SEGMENT with fileoff=0.
// Assume this lies within a single shared_file_mapping_np
offset += fi->fh;
for(int i = 0; i < ch.mappingCount; i++) {
struct shared_file_mapping_np sfm;
assert(pread(fd, &sfm, sizeof(sfm), ch.mappingOffset + i*sizeof(sfm)) == sizeof(sfm));
if(offset >= sfm.sfm_address && (offset + size) < (sfm.sfm_address + sfm.sfm_size)) {
return pread(fd, buf, size, offset - sfm.sfm_address + sfm.sfm_file_offset);
}
}
} else {
return pread(fd, buf, size, offset);
}
return 0;
}
static struct fuse_operations dc_oper = {
.getattr = dc_getattr,
.readdir = dc_readdir,
.open = dc_open,
.read = dc_read,
};
int main(int argc, char **argv) {
assert(argc > 1);
fd = open(argv[1], O_RDONLY);
cache_size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
assert(read(fd, &ch, sizeof(ch)) == sizeof(ch));
printf("magic:'%.16s' mappingOffset:%u mappingCount:%u imagesOffset:%u imagesCount:%u dyldBaseAddress:%llu\n",
ch.magic, ch.mappingOffset, ch.mappingCount,
ch.imagesOffset, ch.imagesCount, ch.dyldBaseAddress);
return fuse_main(argc - 1, argv + 1, &dc_oper, NULL);
/*
lseek(fd, ch.imagesOffset, SEEK_SET);
int i;
for(i = 0; i < ch.imagesCount; i++) {
struct dyld_cache_image_info ii;
assert(read(fd, &ii, sizeof(ii)) == sizeof(ii));
printf("address:%llx modTime:%llu inode:%llu pathFileOffset:%x pad:%u\n", ii.address, ii.modTime, ii.inode, ii.pathFileOffset, ii.pad);
char stuff[128];
pread(fd, &stuff, sizeof(stuff), ii.pathFileOffset);
printf("name: %.128s\n", stuff);
}*/
//return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment