/* * Copyright © 2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @APPLE_LICENSE_HEADER_END@ */ #include "stdio.h" #include "stddef.h" #include "string.h" #include "mach-o/loader.h" #include "stuff/allocate.h" #include "stuff/bytesex.h" #include "stuff/symbol.h" #include "stuff/reloc.h" #include "ofile_print.h" extern char *oname; /* * Here we need structures that have the same memory layout and size as the * 32-bit Objective-C 2 meta data structures. * * The real structure definitions come from the objc4 project in the private * header file runtime/objc-runtime-new.h in that project. */ struct class_t { uint32_t isa; /* class_t * (32-bit pointer) */ uint32_t superclass; /* class_t * (32-bit pointer) */ uint32_t cache; /* Cache (32-bit pointer) */ uint32_t vtable; /* IMP * (32-bit pointer) */ uint32_t data; /* class_ro_t * (32-bit pointer) */ }; static void swap_class_t( struct class_t *c, enum byte_sex target_byte_sex) { c->isa = SWAP_INT(c->isa); c->superclass = SWAP_INT(c->superclass); c->cache = SWAP_INT(c->cache); c->vtable = SWAP_INT(c->vtable); c->data = SWAP_INT(c->data); } struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; uint32_t ivarLayout; /* const uint8_t * (32-bit pointer) */ uint32_t name; /* const char * (32-bit pointer) */ uint32_t baseMethods; /* const method_list_t * (32-bit pointer) */ uint32_t baseProtocols; /* const protocol_list_t * (32-bit pointer) */ uint32_t ivars; /* const ivar_list_t * (32-bit pointer) */ uint32_t weakIvarLayout; /* const uint8_t * (32-bit pointer) */ uint32_t baseProperties; /* const struct objc_property_list * (32-bit pointer) */ }; /* Values for class_ro_t->flags */ #define RO_META (1<<0) #define RO_ROOT (1<<1) #define RO_HAS_CXX_STRUCTORS (1<<2) static void swap_class_ro_t( struct class_ro_t *cro, enum byte_sex target_byte_sex) { cro->flags = SWAP_INT(cro->flags); cro->instanceStart = SWAP_INT(cro->instanceStart); cro->instanceSize = SWAP_INT(cro->instanceSize); cro->ivarLayout = SWAP_INT(cro->ivarLayout); cro->name = SWAP_INT(cro->name); cro->baseMethods = SWAP_INT(cro->baseMethods); cro->baseProtocols = SWAP_INT(cro->baseProtocols); cro->ivars = SWAP_INT(cro->ivars); cro->weakIvarLayout = SWAP_INT(cro->weakIvarLayout); cro->baseProperties = SWAP_INT(cro->baseProperties); } struct method_list_t { uint32_t entsize; uint32_t count; /* struct method_t first; These structures follow inline */ }; static void swap_method_list_t( struct method_list_t *ml, enum byte_sex target_byte_sex) { ml->entsize = SWAP_INT(ml->entsize); ml->count = SWAP_INT(ml->count); } struct method_t { uint32_t name; /* SEL (32-bit pointer) */ uint32_t types; /* const char * (32-bit pointer) */ uint32_t imp; /* IMP (32-bit pointer) */ }; static void swap_method_t( struct method_t *m, enum byte_sex target_byte_sex) { m->name = SWAP_INT(m->name); m->types = SWAP_INT(m->types); m->imp = SWAP_INT(m->imp); } struct ivar_list_t { uint32_t entsize; uint32_t count; /* struct ivar_t first; These structures follow inline */ }; static void swap_ivar_list_t( struct ivar_list_t *il, enum byte_sex target_byte_sex) { il->entsize = SWAP_INT(il->entsize); il->count = SWAP_INT(il->count); } struct ivar_t { uint32_t offset; /* uintptr_t * (32-bit pointer) */ uint32_t name; /* const char * (32-bit pointer) */ uint32_t type; /* const char * (32-bit pointer) */ uint32_t alignment; uint32_t size; }; static void swap_ivar_t( struct ivar_t *i, enum byte_sex target_byte_sex) { i->offset = SWAP_INT(i->offset); i->name = SWAP_INT(i->name); i->type = SWAP_INT(i->type); i->alignment = SWAP_INT(i->alignment); i->size = SWAP_INT(i->size); } struct protocol_list_t { uint32_t count; /* uintptr_t (a 32-bit value) */ /* struct protocol_t * list[0]; These pointers follow inline */ }; static void swap_protocol_list_t( struct protocol_list_t *pl, enum byte_sex target_byte_sex) { pl->count = SWAP_INT(pl->count); } struct protocol_t { uint32_t isa; /* id * (32-bit pointer) */ uint32_t name; /* const char * (32-bit pointer) */ uint32_t protocols; /* struct protocol_list_t * (32-bit pointer) */ uint32_t instanceMethods; /* method_list_t * (32-bit pointer) */ uint32_t classMethods; /* method_list_t * (32-bit pointer) */ uint32_t optionalInstanceMethods; /* method_list_t * (32-bit pointer) */ uint32_t optionalClassMethods; /* method_list_t * (32-bit pointer) */ uint32_t instanceProperties; /* struct objc_property_list * (32-bit pointer) */ }; static void swap_protocol_t( struct protocol_t *p, enum byte_sex target_byte_sex) { p->isa = SWAP_INT(p->isa); p->name = SWAP_INT(p->name); p->protocols = SWAP_INT(p->protocols); p->instanceMethods = SWAP_INT(p->instanceMethods); p->classMethods = SWAP_INT(p->classMethods); p->optionalInstanceMethods = SWAP_INT(p->optionalInstanceMethods); p->optionalClassMethods = SWAP_INT(p->optionalClassMethods); p->instanceProperties = SWAP_INT(p->instanceProperties); } struct objc_property_list { uint32_t entsize; uint32_t count; /* struct objc_property first; These structures follow inline */ }; static void swap_objc_property_list( struct objc_property_list *pl, enum byte_sex target_byte_sex) { pl->entsize = SWAP_INT(pl->entsize); pl->count = SWAP_INT(pl->count); } struct objc_property { uint32_t name; /* const char * (32-bit pointer) */ uint32_t attributes; /* const char * (32-bit pointer) */ }; static void swap_objc_property( struct objc_property *op, enum byte_sex target_byte_sex) { op->name = SWAP_INT(op->name); op->attributes = SWAP_INT(op->attributes); } struct category_t { uint32_t name; /* const char * (32-bit pointer) */ uint32_t cls; /* struct class_t * (32-bit pointer) */ uint32_t instanceMethods; /* struct method_list_t * (32-bit pointer) */ uint32_t classMethods; /* struct method_list_t * (32-bit pointer) */ uint32_t protocols; /* struct protocol_list_t * (32-bit pointer) */ uint32_t instanceProperties; /* struct objc_property_list * (32-bit pointer) */ } category_t; static void swap_category_t( struct category_t *c, enum byte_sex target_byte_sex) { c->name = SWAP_INT(c->name); c->cls = SWAP_INT(c->cls); c->instanceMethods = SWAP_INT(c->instanceMethods); c->classMethods = SWAP_INT(c->classMethods); c->protocols = SWAP_INT(c->protocols); c->instanceProperties = SWAP_INT(c->instanceProperties); } struct message_ref { uint32_t imp; /* IMP (32-bit pointer) */ uint32_t sel; /* SEL (32-bit pointer) */ }; static void swap_message_ref( struct message_ref *mr, enum byte_sex target_byte_sex) { mr->imp = SWAP_INT(mr->imp); mr->sel = SWAP_INT(mr->sel); } struct objc_image_info { uint32_t version; uint32_t flags; }; /* masks for objc_image_info.flags */ #define OBJC_IMAGE_IS_REPLACEMENT (1<<0) #define OBJC_IMAGE_SUPPORTS_GC (1<<1) static void swap_objc_image_info( struct objc_image_info *o, enum byte_sex target_byte_sex) { o->version = SWAP_INT(o->version); o->flags = SWAP_INT(o->flags); } struct info { enum bool swapped; enum byte_sex host_byte_sex; struct section_info_32 *sections; uint32_t nsections; cpu_type_t cputype; struct nlist *symbols; uint32_t nsymbols; char *strings; uint32_t strings_size; struct symbol *sorted_symbols; uint32_t nsorted_symbols; uint32_t database; struct relocation_info *ext_relocs; uint32_t next_relocs; struct relocation_info *loc_relocs; uint32_t nloc_relocs; enum bool verbose; }; struct section_info_32 { char segname[16]; char sectname[16]; char *contents; uint32_t addr; uint32_t size; uint32_t offset; struct relocation_info *relocs; uint32_t nrelocs; enum bool protected; enum bool zerofill; }; static void walk_pointer_list( char *listname, struct section_info_32 *s, struct info *info, void (*func)(uint32_t, struct info *)); static void print_class_t( uint32_t p, struct info *info); static void print_class_ro_t( uint32_t p, struct info *info, enum bool *is_meta_class); static void print_layout_map( uint32_t p, struct info *info); static void print_method_list_t( uint32_t p, struct info *info, char *indent); static void print_ivar_list_t( uint32_t p, struct info *info); static void print_protocol_list_t( uint32_t p, struct info *info); static void print_objc_property_list( uint32_t p, struct info *info); static void print_category_t( uint32_t p, struct info *info); static void print_message_refs( struct section_info_32 *s, struct info *info); static void print_image_info( struct section_info_32 *s, struct info *info); static void get_sections_32( struct load_command *load_commands, uint32_t ncmds, uint32_t sizeofcmds, enum byte_sex object_byte_sex, char *object_addr, uint32_t object_size, struct section_info_32 **sections, uint32_t *nsections, uint32_t *database); static struct section_info_32 *get_section_32( struct section_info_32 *sections, uint32_t nsections, char *segname, char *sectname); static void *get_pointer_32( uint32_t p, uint32_t *offset, uint32_t *left, struct section_info_32 **s, struct section_info_32 *sections, uint32_t nsections); static const char *get_symbol_32( uint32_t sect_offset, uint32_t database_offset, uint64_t value, struct relocation_info *relocs, uint32_t nrelocs, struct info *info); /* * Print the objc2 meta data in 32-bit Mach-O files. */ void print_objc2_32bit( cpu_type_t cputype, struct load_command *load_commands, uint32_t ncmds, uint32_t sizeofcmds, enum byte_sex object_byte_sex, char *object_addr, uint32_t object_size, struct nlist *symbols, uint32_t nsymbols, char *strings, uint32_t strings_size, struct symbol *sorted_symbols, uint32_t nsorted_symbols, struct relocation_info *ext_relocs, uint32_t next_relocs, struct relocation_info *loc_relocs, uint32_t nloc_relocs, enum bool verbose) { struct section_info_32 *s; struct info info; info.host_byte_sex = get_host_byte_sex(); info.swapped = info.host_byte_sex != object_byte_sex; info.cputype = cputype; info.symbols = symbols; info.nsymbols = nsymbols; info.strings = strings; info.strings_size = strings_size; info.sorted_symbols = sorted_symbols; info.nsorted_symbols = nsorted_symbols; info.ext_relocs = ext_relocs; info.next_relocs = next_relocs; info.loc_relocs = loc_relocs; info.nloc_relocs = nloc_relocs; info.verbose = verbose; get_sections_32(load_commands, ncmds, sizeofcmds, object_byte_sex, object_addr, object_size, &info.sections, &info.nsections, &info.database); s = get_section_32(info.sections, info.nsections, "__OBJC2", "__class_list"); if(s == NULL) s = get_section_32(info.sections, info.nsections, "__DATA", "__objc_classlist"); walk_pointer_list("class", s, &info, print_class_t); s = get_section_32(info.sections, info.nsections, "__OBJC2", "__class_refs"); if(s == NULL) s = get_section_32(info.sections, info.nsections, "__DATA", "__objc_classrefs"); walk_pointer_list("class refs", s, &info, NULL); s = get_section_32(info.sections, info.nsections, "__OBJC2", "__super_refs"); if(s == NULL) s = get_section_32(info.sections, info.nsections, "__DATA", "__objc_superrefs"); walk_pointer_list("super refs", s, &info, NULL); s = get_section_32(info.sections, info.nsections, "__OBJC2", "__category_list"); if(s == NULL) s = get_section_32(info.sections, info.nsections, "__DATA", "__objc_catlist"); walk_pointer_list("category", s, &info, print_category_t); s = get_section_32(info.sections, info.nsections, "__OBJC2", "__protocol_list"); if(s == NULL) s = get_section_32(info.sections, info.nsections, "__DATA", "__objc_protolist"); walk_pointer_list("protocol", s, &info, NULL); s = get_section_32(info.sections, info.nsections, "__OBJC2", "__message_refs"); if(s == NULL) s = get_section_32(info.sections, info.nsections, "__DATA", "__objc_msgrefs"); print_message_refs(s, &info); s = get_section_32(info.sections, info.nsections, "__OBJC", "__image_info"); if(s == NULL) s = get_section_32(info.sections, info.nsections, "__DATA", "__objc_imageinfo"); print_image_info(s, &info); } static void walk_pointer_list( char *listname, struct section_info_32 *s, struct info *info, void (*func)(uint32_t, struct info *)) { uint32_t i, size, left; uint32_t p; const char *name; if(s == NULL) return; printf("Contents of (%.16s,%.16s) section\n", s->segname, s->sectname); for(i = 0; i < s->size; i += sizeof(uint32_t)){ memset(&p, '\0', sizeof(uint32_t)); left = s->size - i; size = left < sizeof(uint32_t) ? left : sizeof(uint32_t); memcpy(&p, s->contents + i, size); if(i + sizeof(uint32_t) > s->size) printf("%s list pointer extends past end of (%.16s,%.16s) " "section\n", listname, s->segname, s->sectname); printf("%08x ", s->addr + i); if(info->swapped) p = SWAP_INT(p); printf("0x%x", p); name = get_symbol_32(i, s->addr - info->database, p, s->relocs, s->nrelocs, info); if(name != NULL) printf(" %s\n", name); else printf("\n"); if(func != NULL) func(p, info); } } static void print_class_t( uint32_t p, struct info *info) { struct class_t c; void *r; uint32_t offset, left; struct section_info_32 *s; const char *name; enum bool is_meta_class; is_meta_class = FALSE; r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&c, '\0', sizeof(struct class_t)); if(left < sizeof(struct class_t)){ memcpy(&c, r, left); printf(" (class_t entends past the end of the section)\n"); } else memcpy(&c, r, sizeof(struct class_t)); if(info->swapped) swap_class_t(&c, info->host_byte_sex); printf(" isa 0x%x", c.isa); name = get_symbol_32(offset + offsetof(struct class_t, isa), s->addr - info->database, c.isa, s->relocs, s->nrelocs, info); if(name != NULL) printf(" %s\n", name); else printf("\n"); printf(" superclass 0x%x", c.superclass); name = get_symbol_32(offset + offsetof(struct class_t, superclass), s->addr - info->database, c.superclass, s->relocs, s->nrelocs, info); if(name != NULL) printf(" %s\n", name); else printf("\n"); printf(" cache 0x%x", c.cache); name = get_symbol_32(offset + offsetof(struct class_t, cache), s->addr - info->database, c.cache, s->relocs, s->nrelocs, info); if(name != NULL) printf(" %s\n", name); else printf("\n"); printf(" vtable 0x%x", c.vtable); name = get_symbol_32(offset + offsetof(struct class_t, vtable), s->addr - info->database, c.vtable, s->relocs, s->nrelocs, info); if(name != NULL) printf(" %s\n", name); else printf("\n"); printf(" data 0x%x (struct class_ro_t *)\n", c.data); print_class_ro_t(c.data, info, &is_meta_class); if(! is_meta_class) { printf("Meta Class\n"); print_class_t(c.isa, info); } } static void print_class_ro_t( uint32_t p, struct info *info, enum bool *is_meta_class) { struct class_ro_t cro; void *r; uint32_t offset, left; struct section_info_32 *s; const char *name; r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&cro, '\0', sizeof(struct class_ro_t)); if(left < sizeof(struct class_ro_t)){ memcpy(&cro, r, left); printf(" (class_ro_t entends past the end of the section)\n"); } else memcpy(&cro, r, sizeof(struct class_ro_t)); if(info->swapped) swap_class_ro_t(&cro, info->host_byte_sex); printf(" flags 0x%x", cro.flags); if(cro.flags & RO_META) printf(" RO_META"); if(cro.flags & RO_ROOT) printf(" RO_ROOT"); if(cro.flags & RO_HAS_CXX_STRUCTORS) printf(" RO_HAS_CXX_STRUCTORS"); printf("\n"); printf(" instanceStart %u\n", cro.instanceStart); printf(" instanceSize %u\n", cro.instanceSize); printf(" ivarLayout 0x%x\n", cro.ivarLayout); print_layout_map(cro.ivarLayout, info); printf(" name 0x%x", cro.name); name = get_pointer_32(cro.name, NULL, &left, NULL, info->sections, info->nsections); if(name != NULL) printf(" %.*s\n", (int)left, name); else printf("\n"); printf(" baseMethods 0x%x (struct method_list_t *)\n", cro.baseMethods); if(cro.baseMethods != 0) print_method_list_t(cro.baseMethods, info, ""); printf(" baseProtocols 0x%x\n", cro.baseProtocols); if(cro.baseProtocols != 0) print_protocol_list_t(cro.baseProtocols, info); printf(" ivars 0x%x\n", cro.ivars); if(cro.ivars != 0) print_ivar_list_t(cro.ivars, info); printf(" weakIvarLayout 0x%x\n", cro.weakIvarLayout); print_layout_map(cro.weakIvarLayout, info); printf(" baseProperties 0x%x\n", cro.baseProperties); if(cro.baseProperties != 0) print_objc_property_list(cro.baseProperties, info); if (is_meta_class) *is_meta_class = (cro.flags & RO_META) ? TRUE : FALSE; } static void print_layout_map( uint32_t p, struct info *info) { uint32_t offset, left; struct section_info_32 *s; char *layout_map; if(p == 0) return; layout_map = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(layout_map != NULL){ printf(" layout map: "); do{ printf("0x%02x ", (*layout_map) & 0xff); left--; layout_map++; }while(*layout_map != '\0' && left != 0); printf("\n"); } } static void print_method_list_t( uint32_t p, struct info *info, char *indent) { struct method_list_t ml; struct method_t m; void *r; uint32_t offset, left, i; struct section_info_32 *s; const char *name; r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&ml, '\0', sizeof(struct method_list_t)); if(left < sizeof(struct method_list_t)){ memcpy(&ml, r, left); printf("%s (method_list_t entends past the end of the " "section)\n", indent); } else memcpy(&ml, r, sizeof(struct method_list_t)); if(info->swapped) swap_method_list_t(&ml, info->host_byte_sex); printf("%s\t\t entsize %u\n", indent, ml.entsize); printf("%s\t\t count %u\n", indent, ml.count); p += sizeof(struct method_list_t); offset += sizeof(struct method_list_t); for(i = 0; i < ml.count; i++){ r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&m, '\0', sizeof(struct method_t)); if(left < sizeof(struct method_t)){ memcpy(&ml, r, left); printf("%s (method_t entends past the end of the " "section)\n", indent); } else memcpy(&m, r, sizeof(struct method_t)); if(info->swapped) swap_method_t(&m, info->host_byte_sex); printf("%s\t\t name 0x%x", indent, m.name); name = get_pointer_32(m.name, NULL, &left, NULL, info->sections, info->nsections); if(name != NULL) printf(" %.*s\n", (int)left, name); else printf("\n"); printf("%s\t\t types 0x%x", indent, m.types); name = get_pointer_32(m.types, NULL, &left, NULL, info->sections, info->nsections); if(name != NULL) printf(" %.*s\n", (int)left, name); else printf("\n"); printf("%s\t\t imp 0x%x", indent, m.imp); name = get_symbol_32(offset + offsetof(struct method_t, imp), s->addr - info->database, m.imp, s->relocs, s->nrelocs, info); if(name != NULL) printf(" %s\n", name); else printf("\n"); p += sizeof(struct method_t); offset += sizeof(struct method_t); } } static void print_ivar_list_t( uint32_t p, struct info *info) { struct ivar_list_t il; struct ivar_t i; void *r; uint32_t offset, left, j; struct section_info_32 *s; const char *name; uint32_t *ivar_offset_p, ivar_offset; r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&il, '\0', sizeof(struct ivar_list_t)); if(left < sizeof(struct ivar_list_t)){ memcpy(&il, r, left); printf(" (ivar_list_t entends past the end of the section)\n"); } else memcpy(&il, r, sizeof(struct ivar_list_t)); if(info->swapped) swap_ivar_list_t(&il, info->host_byte_sex); printf(" entsize %u\n", il.entsize); printf(" count %u\n", il.count); p += sizeof(struct ivar_list_t); offset += sizeof(struct ivar_list_t); for(j = 0; j < il.count; j++){ r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&i, '\0', sizeof(struct ivar_t)); if(left < sizeof(struct ivar_t)){ memcpy(&i, r, left); printf(" (ivar_t entends past the end of the section)\n"); } else memcpy(&i, r, sizeof(struct ivar_t)); if(info->swapped) swap_ivar_t(&i, info->host_byte_sex); printf("\t\t\t offset 0x%x", i.offset); ivar_offset_p = get_pointer_32(i.offset, NULL, &left, NULL, info->sections, info->nsections); if(ivar_offset_p != NULL && left >= sizeof(*ivar_offset_p)){ memcpy(&ivar_offset, ivar_offset_p, sizeof(ivar_offset)); if(info->swapped) ivar_offset = SWAP_INT(ivar_offset); printf(" %u\n", ivar_offset); } else printf("\n"); printf("\t\t\t name 0x%x", i.name); name = get_pointer_32(i.name, NULL, &left, NULL, info->sections, info->nsections); if(name != NULL) printf(" %.*s\n", (int)left, name); else printf("\n"); printf("\t\t\t type 0x%x", i.type); name = get_pointer_32(i.type, NULL, &left, NULL, info->sections, info->nsections); if(name != NULL) printf(" %.*s\n", (int)left, name); else printf("\n"); printf("\t\t\talignment %u\n", i.alignment); printf("\t\t\t size %u\n", i.size); p += sizeof(struct ivar_t); offset += sizeof(struct ivar_t); } } static void print_protocol_list_t( uint32_t p, struct info *info) { struct protocol_list_t pl; uint32_t q; struct protocol_t pc; void *r; uint32_t offset, left, i; struct section_info_32 *s; const char *name; r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&pl, '\0', sizeof(struct protocol_list_t)); if(left < sizeof(struct protocol_list_t)){ memcpy(&pl, r, left); printf(" (protocol_list_t entends past the end of the " "section)\n"); } else memcpy(&pl, r, sizeof(struct protocol_list_t)); if(info->swapped) swap_protocol_list_t(&pl, info->host_byte_sex); printf(" count %u\n", pl.count); p += sizeof(struct protocol_list_t); offset += sizeof(struct protocol_list_t); for(i = 0; i < pl.count; i++){ r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; q = 0; if(left < sizeof(uint32_t)){ memcpy(&q, r, left); printf(" (protocol_t * entends past the end of the " "section)\n"); } else memcpy(&q, r, sizeof(uint32_t)); if(info->swapped) q = SWAP_INT(q); printf("\t\t list[%u] 0x%x (struct protocol_t *)\n", i, q); r = get_pointer_32(q, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&pc, '\0', sizeof(struct protocol_t)); if(left < sizeof(struct protocol_t)){ memcpy(&pc, r, left); printf(" (protocol_t entends past the end of the section)\n"); } else memcpy(&pc, r, sizeof(struct protocol_t)); if(info->swapped) swap_protocol_t(&pc, info->host_byte_sex); printf("\t\t\t isa 0x%x\n", pc.isa); printf("\t\t\t name 0x%x", pc.name); name = get_pointer_32(pc.name, NULL, &left, NULL, info->sections, info->nsections); if(name != NULL) printf(" %.*s\n", (int)left, name); else printf("\n"); printf("\t\t\tprotocols 0x%x\n", pc.protocols); printf("\t\t instanceMethods 0x%x (struct method_list_t *)\n", pc.instanceMethods); if(pc.instanceMethods != 0) print_method_list_t(pc.instanceMethods, info, "\t"); printf("\t\t classMethods 0x%x (struct method_list_t *)\n", pc.classMethods); if(pc.classMethods != 0) print_method_list_t(pc.classMethods, info, "\t"); printf("\t optionalInstanceMethods 0x%x\n", pc.optionalInstanceMethods); printf("\t optionalClassMethods 0x%x\n", pc.optionalClassMethods); printf("\t instanceProperties 0x%x\n", pc.instanceProperties); p += sizeof(uint32_t); offset += sizeof(uint32_t); } } static void print_objc_property_list( uint32_t p, struct info *info) { struct objc_property_list opl; struct objc_property op; void *r; uint32_t offset, left, j; struct section_info_32 *s; const char *name; r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&opl, '\0', sizeof(struct objc_property_list)); if(left < sizeof(struct objc_property_list)){ memcpy(&opl, r, left); printf(" (objc_property_list entends past the end of the " "section)\n"); } else memcpy(&opl, r, sizeof(struct objc_property_list)); if(info->swapped) swap_objc_property_list(&opl, info->host_byte_sex); printf(" entsize %u\n", opl.entsize); printf(" count %u\n", opl.count); p += sizeof(struct objc_property_list); offset += sizeof(struct objc_property_list); for(j = 0; j < opl.count; j++){ r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&op, '\0', sizeof(struct objc_property)); if(left < sizeof(struct objc_property)){ memcpy(&op, r, left); printf(" (objc_property entends past the end of the " "section)\n"); } else memcpy(&op, r, sizeof(struct objc_property)); if(info->swapped) swap_objc_property(&op, info->host_byte_sex); printf("\t\t\t name 0x%x", op.name); name = get_pointer_32(op.name, NULL, &left, NULL, info->sections, info->nsections); if(name != NULL) printf(" %.*s\n", (int)left, name); else printf("\n"); printf("\t\t\tattributes x%x", op.attributes); name = get_pointer_32(op.attributes, NULL, &left, NULL, info->sections, info->nsections); if(name != NULL) printf(" %.*s\n", (int)left, name); else printf("\n"); p += sizeof(struct objc_property); offset += sizeof(struct objc_property); } } static void print_category_t( uint32_t p, struct info *info) { struct category_t c; void *r; uint32_t offset, left; struct section_info_32 *s; const char *name; r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&c, '\0', sizeof(struct category_t)); if(left < sizeof(struct category_t)){ memcpy(&c, r, left); printf(" (category_t entends past the end of the section)\n"); } else memcpy(&c, r, sizeof(struct category_t)); if(info->swapped) swap_category_t(&c, info->host_byte_sex); printf(" name 0x%x", c.name); name = get_symbol_32(offset + offsetof(struct category_t, name), s->addr - info->database, c.name, s->relocs, s->nrelocs, info); if(name != NULL) printf(" %s\n", name); else printf("\n"); printf(" cls 0x%x\n", c.cls); if(c.cls != 0) print_class_t(c.cls, info); printf(" instanceMethods 0x%x\n", c.instanceMethods); if(c.instanceMethods != 0) print_method_list_t(c.instanceMethods, info, ""); printf(" classMethods 0x%x\n", c.classMethods); if(c.classMethods != 0) print_method_list_t(c.classMethods, info, ""); printf(" protocols 0x%x\n", c.protocols); if(c.protocols != 0) print_protocol_list_t(c.protocols, info); printf("instanceProperties 0x%x\n", c.instanceProperties); if(c.instanceProperties) print_objc_property_list(c.instanceProperties, info); } static void print_message_refs( struct section_info_32 *s, struct info *info) { uint32_t i, left, offset; uint32_t p; struct message_ref mr; const char *name; void *r; if(s == NULL) return; printf("Contents of (%.16s,%.16s) section\n", s->segname, s->sectname); offset = 0; for(i = 0; i < s->size; i += sizeof(struct message_ref)){ p = s->addr + i; r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&mr, '\0', sizeof(struct message_ref)); if(left < sizeof(struct message_ref)){ memcpy(&mr, r, left); printf(" (message_ref entends past the end of the section)\n"); } else memcpy(&mr, r, sizeof(struct message_ref)); if(info->swapped) swap_message_ref(&mr, info->host_byte_sex); printf(" imp 0x%x", mr.imp); name = get_symbol_32(offset + offsetof(struct message_ref, imp), s->addr - info->database, mr.imp, s->relocs, s->nrelocs, info); if(name != NULL) printf(" %s\n", name); else printf("\n"); printf(" sel 0x%x", mr.sel); name = get_pointer_32(mr.sel, NULL, &left, NULL, info->sections, info->nsections); if(name != NULL) printf(" %.*s\n", (int)left, name); else printf("\n"); offset += sizeof(struct message_ref); } } static void print_image_info( struct section_info_32 *s, struct info *info) { uint32_t left, offset; uint32_t p; struct objc_image_info o; void *r; if(s == NULL) return; printf("Contents of (%.16s,%.16s) section\n", s->segname, s->sectname); p = s->addr; r = get_pointer_32(p, &offset, &left, &s, info->sections, info->nsections); if(r == NULL) return; memset(&o, '\0', sizeof(struct objc_image_info)); if(left < sizeof(struct objc_image_info)){ memcpy(&o, r, left); printf(" (objc_image_info entends past the end of the section)\n"); } else memcpy(&o, r, sizeof(struct objc_image_info)); if(info->swapped) swap_objc_image_info(&o, info->host_byte_sex); printf(" version %u\n", o.version); printf(" flags 0x%x", o.flags); if(o.flags & OBJC_IMAGE_IS_REPLACEMENT) printf(" OBJC_IMAGE_IS_REPLACEMENT"); if(o.flags & OBJC_IMAGE_SUPPORTS_GC) printf(" OBJC_IMAGE_SUPPORTS_GC"); printf("\n"); } static void get_sections_32( struct load_command *load_commands, uint32_t ncmds, uint32_t sizeofcmds, enum byte_sex object_byte_sex, char *object_addr, uint32_t object_size, struct section_info_32 **sections, uint32_t *nsections, uint32_t *database) { enum byte_sex host_byte_sex; enum bool swapped, database_set, zerobased, encrypt_found; uint32_t i, j, left, size; struct load_command lcmd, *lc; char *p; struct segment_command sg; struct section s; struct encryption_info_command encrypt; host_byte_sex = get_host_byte_sex(); swapped = host_byte_sex != object_byte_sex; *sections = NULL; *nsections = 0; database_set = FALSE; *database = 0; zerobased = FALSE; encrypt_found = FALSE; lc = load_commands; for(i = 0 ; i < ncmds; i++){ memcpy((char *)&lcmd, (char *)lc, sizeof(struct load_command)); if(swapped) swap_load_command(&lcmd, host_byte_sex); if(lcmd.cmdsize % sizeof(int32_t) != 0) printf("load command %u size not a multiple of " "sizeof(int32_t)\n", i); if((char *)lc + lcmd.cmdsize > (char *)load_commands + sizeofcmds) printf("load command %u extends past end of load " "commands\n", i); left = sizeofcmds - ((char *)lc - (char *)load_commands); switch(lcmd.cmd){ case LC_SEGMENT: memset((char *)&sg, '\0', sizeof(struct segment_command)); size = left < sizeof(struct segment_command) ? left : sizeof(struct segment_command); memcpy((char *)&sg, (char *)lc, size); if(swapped) swap_segment_command(&sg, host_byte_sex); if((sg.initprot & VM_PROT_WRITE) == VM_PROT_WRITE && database_set == FALSE){ *database = sg.vmaddr; database_set = TRUE; } if((sg.initprot & VM_PROT_READ) == VM_PROT_READ && sg.vmaddr == 0) zerobased = TRUE; p = (char *)lc + sizeof(struct segment_command); for(j = 0 ; j < sg.nsects ; j++){ if(p + sizeof(struct section) > (char *)load_commands + sizeofcmds){ printf("section structure command extends past " "end of load commands\n"); } left = sizeofcmds - (p - (char *)load_commands); memset((char *)&s, '\0', sizeof(struct section)); size = left < sizeof(struct section) ? left : sizeof(struct section); memcpy((char *)&s, p, size); if(swapped) swap_section(&s, 1, host_byte_sex); *sections = reallocate(*sections, sizeof(struct section_info_32) * (*nsections + 1)); memcpy((*sections)[*nsections].segname, s.segname, 16); memcpy((*sections)[*nsections].sectname, s.sectname, 16); (*sections)[*nsections].addr = s.addr; (*sections)[*nsections].contents = object_addr + s.offset; (*sections)[*nsections].offset = s.offset; (*sections)[*nsections].zerofill = (s.flags & SECTION_TYPE) == S_ZEROFILL ? TRUE : FALSE; if(s.offset > object_size){ printf("section contents of: (%.16s,%.16s) is past " "end of file\n", s.segname, s.sectname); (*sections)[*nsections].size = 0; } else if(s.offset + s.size > object_size){ printf("part of section contents of: (%.16s,%.16s) " "is past end of file\n", s.segname, s.sectname); (*sections)[*nsections].size = object_size - s.offset; } else (*sections)[*nsections].size = s.size; if(s.reloff >= object_size){ printf("relocation entries offset for (%.16s,%.16s)" ": is past end of file\n", s.segname, s.sectname); (*sections)[*nsections].nrelocs = 0; } else{ (*sections)[*nsections].relocs = (struct relocation_info *)(object_addr + s.reloff); if(s.reloff + s.nreloc * sizeof(struct relocation_info) > object_size){ printf("relocation entries for section (%.16s," "%.16s) extends past end of file\n", s.segname, s.sectname); (*sections)[*nsections].nrelocs = (object_size - s.reloff) / sizeof(struct relocation_info); } else (*sections)[*nsections].nrelocs = s.nreloc; if(swapped) swap_relocation_info( (*sections)[*nsections].relocs, (*sections)[*nsections].nrelocs, host_byte_sex); } if(sg.flags & SG_PROTECTED_VERSION_1) (*sections)[*nsections].protected = TRUE; else (*sections)[*nsections].protected = FALSE; (*nsections)++; if(p + sizeof(struct section) > (char *)load_commands + sizeofcmds) break; p += size; } break; case LC_ENCRYPTION_INFO: memset((char *)&encrypt, '\0', sizeof(struct encryption_info_command)); size = left < sizeof(struct encryption_info_command) ? left : sizeof(struct encryption_info_command); memcpy((char *)&encrypt, (char *)lc, size); if(swapped) swap_encryption_command(&encrypt, host_byte_sex); encrypt_found = TRUE; break; } if(lcmd.cmdsize == 0){ printf("load command %u size zero (can't advance to other " "load commands)\n", i); break; } lc = (struct load_command *)((char *)lc + lcmd.cmdsize); if((char *)lc > (char *)load_commands + sizeofcmds) break; } if(zerobased == TRUE) *database = 0; if(encrypt_found == TRUE && encrypt.cryptid != 0){ for(i = 0; i < *nsections; i++){ if((*sections)[i].size > 0 && (*sections)[i].zerofill == FALSE){ if((*sections)[i].offset > encrypt.cryptoff + encrypt.cryptsize){ /* section starts past encryption area */ ; } else if((*sections)[i].offset + (*sections)[i].size < encrypt.cryptoff){ /* section ends before encryption area */ ; } else{ /* section has part in the encrypted area */ (*sections)[i].protected = TRUE; } } } } } static struct section_info_32 * get_section_32( struct section_info_32 *sections, uint32_t nsections, char *segname, char *sectname) { uint32_t i; for(i = 0; i < nsections; i++){ if(strncmp(sections[i].segname, segname, 16) == 0 && strncmp(sections[i].sectname, sectname, 16) == 0){ return(sections + i); } } return(NULL); } static void * get_pointer_32( uint32_t p, uint32_t *offset, uint32_t *left, struct section_info_32 **s, struct section_info_32 *sections, uint32_t nsections) { void *r; uint32_t addr; uint32_t i; addr = p; for(i = 0; i < nsections; i++){ if(addr >= sections[i].addr && addr < sections[i].addr + sections[i].size){ if(s != NULL) *s = sections + i; if(offset != NULL) *offset = addr - sections[i].addr; if(left != NULL) *left = sections[i].size - (addr - sections[i].addr); if(sections[i].protected == TRUE) r = "some string from a protected section"; else r = sections[i].contents + (addr - sections[i].addr); return(r); } } if(s != NULL) *s = NULL; if(offset != NULL) *offset = 0; if(left != NULL) *left = 0; return(NULL); } /* * get_symbol() returns the name of a symbol (or NULL). Based on the relocation * information at the specified section offset or the value. */ static const char * get_symbol_32( uint32_t sect_offset, uint32_t database_offset, uint64_t value, struct relocation_info *relocs, uint32_t nrelocs, struct info *info) { uint32_t i; unsigned int r_symbolnum; uint32_t n_strx; if(info->verbose == FALSE) return(NULL); for(i = 0; i < nrelocs; i++){ if((uint32_t)relocs[i].r_address == sect_offset){ r_symbolnum = relocs[i].r_symbolnum; if(relocs[i].r_extern){ if(r_symbolnum >= info->nsymbols) break; n_strx = info->symbols[r_symbolnum].n_un.n_strx; if(n_strx <= 0 || n_strx >= info->strings_size) break; return(info->strings + n_strx); } break; } if(reloc_has_pair(info->cputype, relocs[i].r_type) == TRUE) i++; } for(i = 0; i < info->next_relocs; i++){ if((uint32_t)info->ext_relocs[i].r_address == database_offset + sect_offset){ r_symbolnum = info->ext_relocs[i].r_symbolnum; if(info->ext_relocs[i].r_extern){ if(r_symbolnum >= info->nsymbols) break; n_strx = info->symbols[r_symbolnum].n_un.n_strx; if(n_strx <= 0 || n_strx >= info->strings_size) break; return(info->strings + n_strx); } break; } if(reloc_has_pair(info->cputype, info->ext_relocs[i].r_type) ==TRUE) i++; } return(guess_symbol(value, info->sorted_symbols, info->nsorted_symbols, info->verbose)); }