print_objc2_64bit.c [plain text]
#include "stdio.h"
#include "stdlib.h"
#include "stddef.h"
#include "string.h"
#include "mach-o/loader.h"
#include "mach-o/arm64/reloc.h"
#include "stuff/allocate.h"
#include "stuff/bytesex.h"
#include "stuff/symbol.h"
#include "stuff/reloc.h"
#include "dyld_bind_info.h"
#include "ofile_print.h"
#include <stdarg.h>
extern char *oname;
struct class_t {
uint64_t isa;
uint64_t superclass;
uint64_t cache;
uint64_t vtable;
uint64_t data;
};
static
void
swap_class_t(
struct class_t *c,
enum byte_sex target_byte_sex)
{
c->isa = SWAP_LONG_LONG(c->isa);
c->superclass = SWAP_LONG_LONG(c->superclass);
c->cache = SWAP_LONG_LONG(c->cache);
c->vtable = SWAP_LONG_LONG(c->vtable);
c->data = SWAP_LONG_LONG(c->data);
}
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
uint32_t reserved;
uint64_t ivarLayout;
uint64_t name;
uint64_t baseMethods;
uint64_t baseProtocols;
uint64_t ivars;
uint64_t weakIvarLayout;
uint64_t baseProperties;
};
#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->reserved = SWAP_INT(cro->reserved);
cro->ivarLayout = SWAP_LONG_LONG(cro->ivarLayout);
cro->name = SWAP_LONG_LONG(cro->name);
cro->baseMethods = SWAP_LONG_LONG(cro->baseMethods);
cro->baseProtocols = SWAP_LONG_LONG(cro->baseProtocols);
cro->ivars = SWAP_LONG_LONG(cro->ivars);
cro->weakIvarLayout = SWAP_LONG_LONG(cro->weakIvarLayout);
cro->baseProperties = SWAP_LONG_LONG(cro->baseProperties);
}
struct method_list_t {
uint32_t entsize;
uint32_t count;
};
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 {
uint64_t name;
uint64_t types;
uint64_t imp;
};
static
void
swap_method_t(
struct method_t *m,
enum byte_sex target_byte_sex)
{
m->name = SWAP_LONG_LONG(m->name);
m->types = SWAP_LONG_LONG(m->types);
m->imp = SWAP_LONG_LONG(m->imp);
}
struct ivar_list_t {
uint32_t entsize;
uint32_t count;
};
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 {
uint64_t offset;
uint64_t name;
uint64_t type;
uint32_t alignment;
uint32_t size;
};
static
void
swap_ivar_t(
struct ivar_t *i,
enum byte_sex target_byte_sex)
{
i->offset = SWAP_LONG_LONG(i->offset);
i->name = SWAP_LONG_LONG(i->name);
i->type = SWAP_LONG_LONG(i->type);
i->alignment = SWAP_INT(i->alignment);
i->size = SWAP_INT(i->size);
}
struct protocol_list_t {
uint64_t count;
};
static
void
swap_protocol_list_t(
struct protocol_list_t *pl,
enum byte_sex target_byte_sex)
{
pl->count = SWAP_LONG_LONG(pl->count);
}
struct protocol_t {
uint64_t isa;
uint64_t name;
uint64_t protocols;
uint64_t instanceMethods;
uint64_t classMethods;
uint64_t optionalInstanceMethods;
uint64_t optionalClassMethods;
uint64_t instanceProperties;
};
static
void
swap_protocol_t(
struct protocol_t *p,
enum byte_sex target_byte_sex)
{
p->isa = SWAP_LONG_LONG(p->isa);
p->name = SWAP_LONG_LONG(p->name);
p->protocols = SWAP_LONG_LONG(p->protocols);
p->instanceMethods = SWAP_LONG_LONG(p->instanceMethods);
p->classMethods = SWAP_LONG_LONG(p->classMethods);
p->optionalInstanceMethods = SWAP_LONG_LONG(p->optionalInstanceMethods);
p->optionalClassMethods = SWAP_LONG_LONG(p->optionalClassMethods);
p->instanceProperties = SWAP_LONG_LONG(p->instanceProperties);
}
struct objc_property_list {
uint32_t entsize;
uint32_t count;
};
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 {
uint64_t name;
uint64_t attributes;
};
static
void
swap_objc_property(
struct objc_property *op,
enum byte_sex target_byte_sex)
{
op->name = SWAP_LONG_LONG(op->name);
op->attributes = SWAP_LONG_LONG(op->attributes);
}
struct category_t {
uint64_t name;
uint64_t cls;
uint64_t instanceMethods;
uint64_t classMethods;
uint64_t protocols;
uint64_t instanceProperties;
};
static
void
swap_category_t(
struct category_t *c,
enum byte_sex target_byte_sex)
{
c->name = SWAP_LONG_LONG(c->name);
c->cls = SWAP_LONG_LONG(c->cls);
c->instanceMethods = SWAP_LONG_LONG(c->instanceMethods);
c->classMethods = SWAP_LONG_LONG(c->classMethods);
c->protocols = SWAP_LONG_LONG(c->protocols);
c->instanceProperties = SWAP_LONG_LONG(c->instanceProperties);
}
struct message_ref {
uint64_t imp;
uint64_t sel;
};
static
void
swap_message_ref(
struct message_ref *mr,
enum byte_sex target_byte_sex)
{
mr->imp = SWAP_LONG_LONG(mr->imp);
mr->sel = SWAP_LONG_LONG(mr->sel);
}
struct objc_image_info {
uint32_t version;
uint32_t 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 objc_string_object_64 {
uint64_t isa;
uint64_t characters;
uint32_t _length;
uint32_t _pad;
};
static
void
swap_string_object_64(
struct objc_string_object_64 *string_object,
enum byte_sex target_byte_sex)
{
string_object->isa = SWAP_LONG_LONG(string_object->isa);
string_object->characters = SWAP_LONG_LONG(string_object->characters);
string_object->_length = SWAP_INT(string_object->_length);
string_object->_pad = SWAP_INT(string_object->_pad);
}
struct cfstring_t {
uint64_t isa;
uint64_t flags;
uint64_t characters;
uint64_t length;
};
static
void
swap_cfstring_t(
struct cfstring_t *cfstring,
enum byte_sex target_byte_sex)
{
cfstring->isa = SWAP_LONG_LONG(cfstring->isa);
cfstring->flags = SWAP_LONG_LONG(cfstring->flags);
cfstring->characters = SWAP_LONG_LONG(cfstring->characters);
cfstring->length = SWAP_LONG_LONG(cfstring->length);
}
#define MAXINDENT 10
struct info {
char *object_addr;
uint64_t object_size;
enum bool swapped;
enum byte_sex host_byte_sex;
struct section_info_64 *sections;
uint32_t nsections;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
struct nlist_64 *symbols64;
uint32_t nsymbols;
char *strings;
uint32_t strings_size;
struct symbol *sorted_symbols;
uint32_t nsorted_symbols;
uint64_t textbase;
uint64_t database;
struct relocation_info *ext_relocs;
uint32_t next_relocs;
struct relocation_info *loc_relocs;
uint32_t nloc_relocs;
struct dyld_bind_info *dbi;
uint64_t ndbi;
enum chain_format_t chain_format;
enum bool verbose;
enum bool Vflag;
uint32_t indent_level;
uint32_t indent_widths[MAXINDENT];
};
struct section_info_64 {
char segname[16];
char sectname[16];
char *contents;
uint64_t addr;
uint64_t size;
uint32_t offset;
struct relocation_info *relocs;
uint32_t nrelocs;
enum bool cstring;
enum bool protected;
enum bool zerofill;
};
static void walk_pointer_list(
char *listname,
struct section_info_64 *s,
struct info *info,
void (*func)(uint64_t, struct info *));
static void print_class_t(
uint64_t p,
struct info *info);
static void print_class_ro_t(
uint64_t p,
struct info *info,
enum bool *is_meta_class);
static void print_layout_map(
uint64_t p,
struct info *info);
static void print_method_list_t(
uint64_t p,
struct info *info);
static void print_ivar_list_t(
uint64_t p,
struct info *info);
static void print_protocol_list_t(
uint64_t p,
struct info *info);
static void print_objc_property_list(
uint64_t p,
struct info *info);
static void print_category_t(
uint64_t p,
struct info *info);
static void print_protocol_t(
uint64_t p,
struct info *info);
static void print_message_refs(
struct section_info_64 *s,
struct info *info);
static void print_image_info(
struct section_info_64 *s,
struct info *info);
static void get_sections_64(
struct load_command *load_commands,
uint32_t ncmds,
uint32_t sizeofcmds,
enum byte_sex object_byte_sex,
char *object_addr,
uint64_t object_size,
struct section_info_64 **sections,
uint32_t *nsections,
uint64_t *textbase,
uint64_t *database);
static struct section_info_64 *get_section_64(
struct section_info_64 *sections,
uint32_t nsections,
char *segname,
char *sectname);
static void get_cstring_section_64(
struct load_command *load_commands,
uint32_t ncmds,
uint32_t sizeofcmds,
enum byte_sex object_byte_sex,
char *object_addr,
uint64_t object_size,
struct section_info_64 *cstring_section_ptr);
static void *get_pointer_64(
uint64_t p,
uint32_t *offset,
uint32_t *left,
struct section_info_64 **s,
struct section_info_64 *sections,
uint32_t nsections);
static const char *get_symbol_64(
uint64_t sect_offset,
uint64_t sect_addr,
uint64_t textbase,
uint64_t database,
uint64_t value,
struct relocation_info *relocs,
uint32_t nrelocs,
struct info *info,
uint64_t *n_value,
int64_t *addend);
static void print_field_value(
uint64_t offset,
uint64_t pointer,
enum bool print_data,
const char* type_name,
const char* suffix,
struct info *info,
struct section_info_64 *s,
uint64_t *out_n_value,
int64_t *out_addend);
static void print_field_label(
struct info *info,
const char* label,
...);
static void print_field_scalar(
struct info *info,
const char* label,
const char* fmt,...);
static void indent_push(
struct info *info,
uint32_t width);
static void indent_pop(
struct info *info);
void
print_objc2_64bit(
cpu_type_t cputype,
cpu_subtype_t cpusubtype,
struct load_command *load_commands,
uint32_t ncmds,
uint32_t sizeofcmds,
enum byte_sex object_byte_sex,
char *object_addr,
uint64_t object_size,
struct nlist_64 *symbols64,
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,
struct dyld_bind_info *dbi,
uint64_t ndbi,
enum chain_format_t chain_format,
enum bool verbose,
enum bool Vflag)
{
struct section_info_64 *s;
struct info info;
info.object_addr = object_addr;
info.object_size = object_size;
info.host_byte_sex = get_host_byte_sex();
info.swapped = info.host_byte_sex != object_byte_sex;
info.cputype = cputype;
info.cpusubtype = cpusubtype;
info.symbols64 = symbols64;
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.dbi = dbi;
info.ndbi = ndbi;
info.chain_format = chain_format;
info.verbose = verbose;
info.Vflag = Vflag;
get_sections_64(load_commands, ncmds, sizeofcmds, object_byte_sex,
object_addr, object_size, &info.sections,
&info.nsections, &info.textbase, &info.database);
s = get_section_64(info.sections, info.nsections,
"__OBJC2", "__class_list");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA", "__objc_classlist");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_CONST", "__objc_classlist");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_DIRTY", "__objc_classlist");
walk_pointer_list("class", s, &info, print_class_t);
s = get_section_64(info.sections, info.nsections,
"__OBJC2", "__class_refs");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA", "__objc_classrefs");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_CONST", "__objc_classrefs");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_DIRTY", "__objc_classrefs");
walk_pointer_list("class refs", s, &info, NULL);
s = get_section_64(info.sections, info.nsections,
"__OBJC2", "__super_refs");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA", "__objc_superrefs");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_CONST", "__objc_superrefs");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_DIRTY", "__objc_superrefs");
walk_pointer_list("super refs", s, &info, NULL);
s = get_section_64(info.sections, info.nsections,
"__OBJC2", "__category_list");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA", "__objc_catlist");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_CONST", "__objc_catlist");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_DIRTY", "__objc_catlist");
walk_pointer_list("category", s, &info, print_category_t);
s = get_section_64(info.sections, info.nsections,
"__OBJC2", "__protocol_list");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA", "__objc_protolist");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_CONST", "__objc_protolist");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_DIRTY", "__objc_protolist");
walk_pointer_list("protocol", s, &info, print_protocol_t);
s = get_section_64(info.sections, info.nsections,
"__OBJC2", "__message_refs");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA", "__objc_msgrefs");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_CONST", "__objc_msgrefs");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_DIRTY", "__objc_msgrefs");
print_message_refs(s, &info);
s = get_section_64(info.sections, info.nsections,
"__OBJC", "__image_info");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA", "__objc_imageinfo");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_CONST", "__objc_imageinfo");
if(s == NULL)
s = get_section_64(info.sections, info.nsections,
"__DATA_DIRTY", "__objc_imageinfo");
print_image_info(s, &info);
}
static
void
walk_pointer_list(
char *listname,
struct section_info_64 *s,
struct info *info,
void (*func)(uint64_t, struct info *))
{
uint64_t i, size, left;
uint64_t p, n_value;
int64_t addend;
if(s == NULL)
return;
info->indent_level = 0;
info->indent_widths[info->indent_level] = 0;
printf("Contents of (%.16s,%.16s) section\n", s->segname, s->sectname);
for(i = 0; i < s->size; i += sizeof(uint64_t))
{
left = s->size - i;
size = left < sizeof(uint64_t) ?
left : sizeof(uint64_t);
if(s->contents + i + size > info->object_addr + info->object_size)
return;
if(i + sizeof(uint64_t) > s->size)
printf("%s list pointer extends past end of (%s,%s) "
"section\n", listname, s->segname, s->sectname);
printf("%016llx ", s->addr + i);
memset(&p, '\0', sizeof(uint64_t));
memcpy(&p, s->contents + i, size);
if(info->swapped)
p = SWAP_LONG_LONG(p);
print_field_value(i, p, FALSE, NULL, "\n", info, s, &n_value, &addend);
if(func != NULL)
func(n_value + addend, info);
}
}
char *
get_objc2_64bit_cfstring_name(
uint64_t p,
struct load_command *load_commands,
uint32_t ncmds,
uint32_t sizeofcmds,
enum byte_sex object_byte_sex,
char *object_addr,
uint64_t object_size,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strings_size,
cpu_type_t cputype)
{
struct section_info_64 *sections, *s;
uint32_t nsections, left, offset;
uint64_t textbase, database, n_value, cfs_characters;
int64_t addend;
struct cfstring_t cfs;
char *name;
const char *symbol_name;
void *r;
struct info info;
memset(&info, '\0', sizeof(struct info));
info.symbols64 = symbols64;
info.nsymbols = nsymbols;
info.strings = strings;
info.strings_size = strings_size;
info.cputype = cputype;
info.verbose = TRUE;
get_sections_64(load_commands, ncmds, sizeofcmds, object_byte_sex,
object_addr, object_size, §ions, &nsections,
&textbase, &database);
r = get_pointer_64(p, &offset, &left, &s, sections, nsections);
if(r == NULL || left < sizeof(struct cfstring_t))
return(NULL);
memcpy(&cfs, r, sizeof(struct cfstring_t));
if(get_host_byte_sex() != object_byte_sex)
swap_cfstring_t(&cfs, get_host_byte_sex());
symbol_name = get_symbol_64(offset +
offsetof(struct cfstring_t, characters),
s->addr, textbase, database, p, s->relocs,
s->nrelocs, &info, &n_value, &addend);
if(symbol_name == NULL){
if(sections != NULL)
free(sections);
return(NULL);
}
cfs_characters = n_value + addend;
name = get_pointer_64(cfs_characters, NULL, &left, NULL,
sections, nsections);
if(sections != NULL)
free(sections);
return(name);
}
char *
get_objc2_64bit_class_name(
uint64_t p,
uint64_t address_of_p,
struct load_command *load_commands,
uint32_t ncmds,
uint32_t sizeofcmds,
enum byte_sex object_byte_sex,
char *object_addr,
uint64_t object_size,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strings_size,
cpu_type_t cputype)
{
struct section_info_64 *sections, *s;
uint32_t nsections, left, offset;
uint64_t textbase, database, n_value;
int64_t addend;
struct class_t c;
struct class_ro_t cro;
char *name, *class_name;
const char *symbol_name;
void *r;
struct info info;
memset(&info, '\0', sizeof(struct info));
info.symbols64 = symbols64;
info.nsymbols = nsymbols;
info.strings = strings;
info.strings_size = strings_size;
info.cputype = cputype;
info.verbose = TRUE;
get_sections_64(load_commands, ncmds, sizeofcmds, object_byte_sex,
object_addr, object_size, §ions, &nsections,
&textbase, &database);
if(p == 0){
r = get_pointer_64(address_of_p, &offset, &left, &s, sections,
nsections);
if(r == NULL || left < sizeof(uint64_t)){
if(sections != NULL)
free(sections);
return(NULL);
}
symbol_name = get_symbol_64(offset, s->addr, textbase, database,
address_of_p, s->relocs, s->nrelocs,
&info, &n_value, &addend);
if(symbol_name == NULL){
if(sections != NULL)
free(sections);
return(NULL);
}
class_name = rindex(symbol_name, '$');
if(class_name != NULL &&
class_name[1] == '_' && class_name[2] != '\0'){
if(sections != NULL)
free(sections);
return(class_name + 2);
}
else{
if(sections != NULL)
free(sections);
return(NULL);
}
}
r = get_pointer_64(p, NULL, &left, NULL, sections, nsections);
if(r == NULL || left < sizeof(struct class_t)){
if(sections != NULL)
free(sections);
return(NULL);
}
memcpy(&c, r, sizeof(struct class_t));
if(get_host_byte_sex() != object_byte_sex)
swap_class_t(&c, get_host_byte_sex());
if(c.data == 0){
if(sections != NULL)
free(sections);
return(NULL);
}
r = get_pointer_64(c.data, NULL, &left, NULL, sections, nsections);
if(r == NULL || left < sizeof(struct class_ro_t)){
if(sections != NULL)
free(sections);
return(NULL);
}
memcpy(&cro, r, sizeof(struct class_ro_t));
if(get_host_byte_sex() != object_byte_sex)
swap_class_ro_t(&cro, get_host_byte_sex());
if(cro.name == 0){
if(sections != NULL)
free(sections);
return(NULL);
}
name = get_pointer_64(cro.name, NULL, &left, NULL, sections, nsections);
if(sections != NULL)
free(sections);
return(name);
}
uint64_t
get_objc2_64bit_selref(
uint64_t address_of_p,
struct load_command *load_commands,
uint32_t ncmds,
uint32_t sizeofcmds,
enum byte_sex object_byte_sex,
char *object_addr,
uint64_t object_size,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
uint32_t strings_size,
cpu_type_t cputype)
{
struct section_info_64 *sections, *s;
uint32_t nsections, left, offset;
uint64_t textbase, database, n_value;
int64_t addend;
void *r;
const char *symbol_name;
struct info info;
memset(&info, '\0', sizeof(struct info));
info.symbols64 = symbols64;
info.nsymbols = nsymbols;
info.strings = strings;
info.strings_size = strings_size;
info.cputype = cputype;
info.verbose = TRUE;
get_sections_64(load_commands, ncmds, sizeofcmds, object_byte_sex,
object_addr, object_size, §ions, &nsections,
&textbase, &database);
r = get_pointer_64(address_of_p, &offset, &left, &s, sections,
nsections);
if(r == NULL || left < sizeof(uint64_t)){
if(sections != NULL)
free(sections);
return(0);
}
symbol_name = get_symbol_64(offset, s->addr, textbase, database,
address_of_p, s->relocs, s->nrelocs,
&info, &n_value, &addend);
if(symbol_name == NULL){
if(sections != NULL)
free(sections);
return(0);
}
return(n_value);
}
static
void
print_class_t(
uint64_t p,
struct info *info)
{
struct class_t c;
void *r;
uint32_t offset, left;
struct section_info_64 *s;
enum bool is_meta_class;
uint64_t n_value, isa_n_value;
int64_t addend, isa_addend;
static uint32_t recursive_depth;
is_meta_class = FALSE;
r = get_pointer_64(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);
indent_push(info, sizeof("superclass") - 1);
print_field_label(info, "isa");
print_field_value(offset + offsetof(struct class_t, isa), c.isa,
FALSE, NULL, "\n", info, s, &isa_n_value, &isa_addend);
print_field_label(info, "superclass");
print_field_value(offset + offsetof(struct class_t, superclass),
c.superclass, FALSE, NULL, "\n", info, s,
&n_value, &addend);
print_field_label(info, "cache");
print_field_value(offset + offsetof(struct class_t, cache),
c.cache, FALSE, NULL, "\n", info, s, &n_value, &addend);
print_field_label(info, "vtable");
print_field_value(offset + offsetof(struct class_t, vtable),
c.vtable, FALSE, NULL, "\n", info, s, &n_value, &addend);
print_field_label(info, "data");
print_field_value(offset + offsetof(struct class_t, data), c.data, FALSE,
"(struct class_ro_t *)", NULL, info, s, &n_value, &addend);
if((c.data + n_value) & 0x7)
printf(" Swift class");
printf("\n");
print_class_ro_t((n_value + addend) & ~0x7, info, &is_meta_class);
indent_pop(info);
if(is_meta_class == FALSE &&
isa_n_value + isa_addend != p &&
isa_n_value + isa_addend != 0 &&
recursive_depth < 100)
{
recursive_depth++;
printf("Meta Class\n");
print_class_t(isa_n_value + isa_addend, info);
recursive_depth--;
}
}
static
void
print_class_ro_t(
uint64_t p,
struct info *info,
enum bool *is_meta_class)
{
struct class_ro_t cro;
void *r;
uint32_t offset, left;
struct section_info_64 *s;
const char *name;
uint64_t n_value;
int64_t addend;
r = get_pointer_64(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);
indent_push(info, sizeof("weakIvarLayout") - 1);
print_field_scalar(info, "flags", "0x%x", cro.flags);
if(info->verbose){
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");
print_field_scalar(info, "instanceStart", "%u\n", cro.instanceStart);
print_field_scalar(info, "instanceSize", "%u\n", cro.instanceSize);
print_field_scalar(info, "reserved", "0x%x\n", cro.reserved);
print_field_label(info, "ivarLayout");
print_field_value(offset + offsetof(struct class_ro_t, ivarLayout),
cro.ivarLayout, FALSE, NULL, "\n", info, s,
&n_value, &addend);
print_layout_map(n_value + addend, info);
print_field_label(info, "name");
print_field_value(offset + offsetof(struct class_ro_t, name),
cro.name, FALSE, NULL, NULL, info, s, &n_value, &addend);
if (info->verbose) {
name = get_pointer_64(n_value + addend, NULL, &left, NULL,
info->sections, info->nsections);
if (name != NULL)
printf(" %.*s", (int)left, name);
}
printf("\n");
print_field_label(info, "baseMethods");
print_field_value(offset + offsetof(struct class_ro_t, baseMethods),
cro.baseMethods, FALSE, "(struct method_list_t *)", "\n",
info, s, &n_value, &addend);
if(n_value + addend != 0) {
print_method_list_t(n_value + addend, info);
}
print_field_label(info, "baseProtocols");
print_field_value(offset + offsetof(struct class_ro_t, baseProtocols),
cro.baseProtocols, FALSE, "(struct protocol_list_t *)",
"\n", info, s, &n_value, &addend);
if(n_value + addend != 0) {
print_protocol_list_t(n_value + addend, info);
}
print_field_label(info, "ivars");
print_field_value(offset + offsetof(struct class_ro_t, ivars),
cro.ivars, FALSE, "(struct ivar_list_t *)", "\n", info, s,
&n_value, &addend);
if(n_value + addend != 0) {
print_ivar_list_t(n_value + addend, info);
}
print_field_label(info, "weakIvarLayout");
print_field_value(offset + offsetof(struct class_ro_t, weakIvarLayout),
cro.weakIvarLayout, FALSE, NULL, "\n", info, s,
&n_value, &addend);
print_layout_map(n_value + addend, info);
print_field_label(info, "baseProperties");
print_field_value(offset + offsetof(struct class_ro_t, baseProperties),
cro.baseProperties, FALSE,
"(struct objc_property_list *)", "\n", info, s,
&n_value, &addend);
if(n_value + addend != 0) {
print_objc_property_list(n_value + addend, info);
}
if(is_meta_class)
*is_meta_class = (cro.flags & RO_META) ? TRUE : FALSE;
indent_pop(info);
}
static
void
print_layout_map(
uint64_t p,
struct info *info)
{
uint32_t offset, left;
struct section_info_64 *s;
char *layout_map;
if(p == 0)
return;
layout_map = get_pointer_64(p, &offset, &left, &s,
info->sections, info->nsections);
if(layout_map != NULL){
print_field_label(info, "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(
uint64_t p,
struct info *info)
{
struct method_list_t ml;
struct method_t m;
void *r;
uint32_t offset, left, i;
struct section_info_64 *s;
uint64_t n_value;
int64_t addend;
r = get_pointer_64(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);
print_field_scalar(info, "", "(method_list_t entends past the end "
"of the section)\n)");
}
else
memcpy(&ml, r, sizeof(struct method_list_t));
if(info->swapped)
swap_method_list_t(&ml, info->host_byte_sex);
indent_push(info, sizeof("entsize") - 1);
print_field_scalar(info, "entsize", "%u\n", ml.entsize);
print_field_scalar(info, "count", "%u\n", ml.count);
p += sizeof(struct method_list_t);
offset += sizeof(struct method_list_t);
for(i = 0; i < ml.count; i++){
r = get_pointer_64(p, &offset, &left, &s,
info->sections, info->nsections);
if(r == NULL)
break;
memset(&m, '\0', sizeof(struct method_t));
if(left < sizeof(struct method_t)){
memcpy(&m, r, left);
print_field_scalar(info, "", "(method_t entends past the end "
"of the section)\n)");
}
else
memcpy(&m, r, sizeof(struct method_t));
if(info->swapped)
swap_method_t(&m, info->host_byte_sex);
print_field_label(info, "name");
print_field_value(offset + offsetof(struct method_t, name),
m.name, TRUE, NULL, "\n", info, s, &n_value, &addend);
print_field_label(info, "types");
print_field_value(offset + offsetof(struct method_t, types),
m.types, TRUE, NULL, "\n", info, s, &n_value,&addend);
print_field_label(info, "imp");
print_field_value(offset + offsetof(struct method_t, imp),
m.imp, FALSE, NULL, "\n", info, s, &n_value, &addend);
p += sizeof(struct method_t);
offset += sizeof(struct method_t);
}
indent_pop(info);
}
static
void
print_ivar_list_t(
uint64_t p,
struct info *info)
{
struct ivar_list_t il;
struct ivar_t i;
void *r;
uint32_t offset, left, j;
struct section_info_64 *s;
uint64_t *ivar_offset_p, n_value;
uint32_t ivar_offset;
int64_t addend;
r = get_pointer_64(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);
indent_push(info, sizeof("alignment") - 1);
print_field_scalar(info, "entsize", "%u\n", il.entsize);
print_field_scalar(info, "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_64(p, &offset, &left, &s, info->sections,
info->nsections);
if(r == NULL)
break;
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);
print_field_label(info, "offset");
print_field_value(offset + offsetof(struct ivar_t, offset),
i.offset, FALSE, NULL, NULL, info, s,
&n_value, &addend);
if (info->verbose) {
ivar_offset_p = get_pointer_64(n_value + addend, NULL, &left, NULL,
info->sections, info->nsections);
if(ivar_offset_p != NULL && left >= sizeof(ivar_offset)){
memcpy(&ivar_offset, ivar_offset_p, sizeof(ivar_offset));
if(info->swapped)
ivar_offset = SWAP_INT(ivar_offset);
printf(" %u", ivar_offset);
}
}
printf("\n");
print_field_label(info, "name");
print_field_value(offset + offsetof(struct ivar_t, name),
i.name, TRUE, NULL, "\n", info, s, &n_value, &addend);
print_field_label(info, "type");
print_field_value(offset + offsetof(struct ivar_t, type),
i.type, TRUE, NULL, "\n", info, s, &n_value, &addend);
print_field_scalar(info, "alignment", "%u\n", i.alignment);
print_field_scalar(info, "size", "%u\n", i.size);
p += sizeof(struct ivar_t);
offset += sizeof(struct ivar_t);
}
indent_pop(info);
}
static
void
print_protocol_list_t(
uint64_t p,
struct info *info)
{
struct protocol_list_t pl;
uint64_t q, n_value;
int64_t addend;
void *r;
uint32_t offset, left, i;
struct section_info_64 *s;
static uint32_t recursive_depth;
r = get_pointer_64(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);
indent_push(info, sizeof("list[99]") - 1);
print_field_scalar(info, "count", "%llu\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_64(p, &offset, &left, &s, info->sections,
info->nsections);
if(r == NULL)
break;
q = 0;
if(left < sizeof(uint64_t)){
memcpy(&q, r, left);
printf(" (protocol_t * entends past the end of the "
"section)\n");
}
else
memcpy(&q, r, sizeof(uint64_t));
if(info->swapped)
q = SWAP_LONG_LONG(q);
print_field_label(info, "list[%u]", i);
print_field_value(offset, q, FALSE, "(struct protocol_t *)", "\n",
info, s, &n_value, &addend);
if (n_value + addend &&
recursive_depth < 100)
{
recursive_depth += 1;
print_protocol_t(n_value + addend, info);
recursive_depth -= 1;
}
p += sizeof(uint64_t);
offset += sizeof(uint64_t);
}
indent_pop(info);
}
static
void
print_objc_property_list(
uint64_t p,
struct info *info)
{
struct objc_property_list opl;
struct objc_property op;
void *r;
uint32_t offset, left, j;
struct section_info_64 *s;
uint64_t n_value;
int64_t addend;
r = get_pointer_64(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);
indent_push(info, sizeof("attributes") - 1);
print_field_scalar(info, "entsize", "%u\n", opl.entsize);
print_field_scalar(info, "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_64(p, &offset, &left, &s,
info->sections, info->nsections);
if(r == NULL)
break;
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);
print_field_label(info, "name");
print_field_value(offset + offsetof(struct objc_property, name),
op.name, TRUE, NULL, "\n", info, s,
&n_value, &addend);
print_field_label(info, "attributes");
print_field_value(offset + offsetof(struct objc_property, attributes),
op.attributes, TRUE, NULL, "\n", info, s,
&n_value, &addend);
p += sizeof(struct objc_property);
offset += sizeof(struct objc_property);
}
indent_pop(info);
}
static
void
print_category_t(
uint64_t p,
struct info *info)
{
struct category_t c;
void *r;
uint32_t offset, left;
struct section_info_64 *s;
uint64_t n_value;
int64_t addend;
r = get_pointer_64(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);
indent_push(info, sizeof("protocols") - 1);
print_field_label(info, "name");
print_field_value(offset + offsetof(struct category_t, name),
c.name, TRUE, NULL, "\n", info, s, &n_value, &addend);
print_field_label(info, "cls");
print_field_value(offset + offsetof(struct category_t, cls),
c.cls, FALSE, "(struct class_t *)", "\n", info, s,
&n_value, &addend);
if(n_value + addend != 0) {
print_class_t(n_value + addend, info);
}
print_field_label(info, "instanceMethods");
print_field_value(offset + offsetof(struct category_t, instanceMethods),
c.instanceMethods, FALSE, "(struct method_list_t *)",
"\n", info, s, &n_value, &addend);
if(n_value + addend != 0) {
print_method_list_t(n_value + addend, info);
}
print_field_label(info, "classMethods");
print_field_value(offset + offsetof(struct category_t, classMethods),
c.classMethods, FALSE, "(struct method_list_t *)",
"\n", info, s, &n_value, &addend);
if(n_value + addend != 0) {
print_method_list_t(n_value + addend, info);
}
print_field_label(info, "protocols");
print_field_value(offset + offsetof(struct category_t, protocols),
c.protocols, FALSE, "(struct protocol_list_t *)", "\n",
info, s, &n_value, &addend);
if(n_value + addend != 0) {
print_protocol_list_t(n_value + addend, info);
}
print_field_label(info, "instanceProperties");
print_field_value(offset + offsetof(struct category_t, instanceProperties),
c.instanceProperties, FALSE,
"(struct objc_property_list *)", "\n", info, s,
&n_value, &addend);
if(n_value + addend) {
print_objc_property_list(n_value + addend, info);
}
indent_pop(info);
}
void
print_protocol_t(uint64_t p,
struct info *info)
{
struct protocol_t pt;
void *r;
uint32_t offset, left;
struct section_info_64 *s;
uint64_t n_value;
int64_t addend;
r = get_pointer_64(p, &offset, &left, &s, info->sections, info->nsections);
if(r == NULL)
return;
memset(&pt, '\0', sizeof(struct protocol_t));
if(left < sizeof(struct protocol_t)){
memcpy(&pt, r, left);
printf(" (protocol_t entends past the end of the section)\n");
}
else
memcpy(&pt, r, sizeof(struct protocol_t));
if(info->swapped)
swap_protocol_t(&pt, info->host_byte_sex);
indent_push(info, sizeof("protocols") - 1);
print_field_label(info, "isa");
print_field_value(offset + offsetof(struct protocol_t, isa),
pt.isa, TRUE, NULL, "\n", info, s, &n_value, &addend);
print_field_label(info, "name");
print_field_value(offset + offsetof(struct protocol_t, name),
pt.name, TRUE, NULL, "\n", info, s, &n_value, &addend);
print_field_label(info, "protocols");
print_field_value(offset + offsetof(struct protocol_t, protocols),
pt.protocols, FALSE, "(struct protocol_list_t *)", "\n",
info, s, &n_value, &addend);
if(n_value + addend != 0) {
print_protocol_list_t(n_value + addend, info);
}
print_field_label(info, "instanceMethods");
print_field_value(offset + offsetof(struct protocol_t, instanceMethods),
pt.instanceMethods, FALSE, "(struct method_list_t *)",
"\n", info, s, &n_value, &addend);
if(n_value + addend != 0) {
print_method_list_t(n_value + addend, info);
}
print_field_label(info, "classMethods");
print_field_value(offset + offsetof(struct protocol_t, classMethods),
pt.classMethods, FALSE, "(struct method_list_t *)",
"\n", info, s, &n_value, &addend);
if(n_value + addend != 0) {
print_method_list_t(n_value + addend, info);
}
print_field_label(info, "optionalInstanceMethods");
print_field_value(offset + offsetof(struct protocol_t,
optionalInstanceMethods),
pt.optionalInstanceMethods, FALSE,
"(struct method_list_t *)", "\n", info, s,
&n_value, &addend);
if(n_value + addend != 0) {
print_method_list_t(n_value + addend, info);
}
print_field_label(info, "optionalClassMethods");
print_field_value(offset + offsetof(struct protocol_t,
optionalClassMethods),
pt.optionalClassMethods, FALSE,
"(struct method_list_t *)", "\n", info, s,
&n_value, &addend);
if(n_value + addend != 0) {
print_method_list_t(n_value + addend, info);
}
print_field_label(info, "instanceProperties");
print_field_value(offset + offsetof(struct protocol_t,
instanceProperties),
pt.instanceProperties, FALSE,
"(struct objc_property_list *)", "\n", info, s,
&n_value, &addend);
if(n_value + addend) {
print_objc_property_list(n_value + addend, info);
}
indent_pop(info);
}
static
void
print_message_refs(
struct section_info_64 *s,
struct info *info)
{
uint32_t i, left, offset;
uint64_t p, n_value;
int64_t addend;
struct message_ref mr;
void *r;
if(s == NULL)
return;
info->indent_level = 0;
info->indent_widths[info->indent_level] = 0;
printf("Contents of (%.16s,%.16s) section\n", s->segname, s->sectname);
indent_push(info, sizeof("imp") - 1);
offset = 0;
for(i = 0; i < s->size; i += sizeof(struct message_ref)){
p = s->addr + i;
r = get_pointer_64(p, &offset, &left, &s,
info->sections, info->nsections);
if(r == NULL)
break;
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);
print_field_label(info, "imp");
print_field_value(offset + offsetof(struct message_ref, imp),
mr.imp, FALSE, NULL, "\n", info, s, &n_value, &addend);
print_field_label(info, "sel");
print_field_value(offset + offsetof(struct message_ref, sel),
mr.sel, FALSE, NULL, "\n", info, s, &n_value, &addend);
offset += sizeof(struct message_ref);
}
indent_pop(info);
}
static
void
print_image_info(
struct section_info_64 *s,
struct info *info)
{
uint32_t left, offset, swift_version;
uint64_t p;
struct objc_image_info o;
void *r;
if(s == NULL)
return;
info->indent_level = 0;
info->indent_widths[info->indent_level] = 0;
printf("Contents of (%.16s,%.16s) section\n", s->segname, s->sectname);
p = s->addr;
r = get_pointer_64(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);
indent_push(info, sizeof("version") - 1);
print_field_scalar(info, "version", "%u\n", o.version);
print_field_scalar(info, "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");
swift_version = (o.flags >> 8) & 0xff;
if(swift_version != 0){
if(swift_version == 1)
printf(" Swift 1.0");
else if(swift_version == 2)
printf(" Swift 1.1");
else if(swift_version == 3)
printf(" Swift 2.0");
else if(swift_version == 4)
printf(" Swift 3.0");
else if(swift_version == 5)
printf(" Swift 4.0");
else if(swift_version == 6)
printf(" Swift 4.1/4.2");
else if(swift_version == 7)
printf(" Swift 5 or later");
else
printf(" unknown future Swift version (%d)", swift_version);
}
printf("\n");
indent_pop(info);
}
void
print_objc_string_object_section_64(
char *sectname,
struct load_command *load_commands,
uint32_t ncmds,
uint32_t sizeofcmds,
enum byte_sex object_byte_sex,
char *object_addr,
uint64_t object_size,
cpu_type_t cputype,
struct nlist_64 *symbols64,
uint32_t nsymbols,
char *strings,
const uint32_t strings_size,
struct symbol *sorted_symbols,
uint32_t nsorted_symbols,
enum bool verbose)
{
struct info info;
struct section_info_64 *o, cstring_section;
struct objc_string_object_64 *string_objects, *s, string_object;
uint64_t string_objects_addr, string_objects_size;
uint64_t size, left;
uint32_t left32;
char *p;
const char *name;
printf("Contents of (" SEG_OBJC ",%s) section\n", sectname);
info.object_addr = object_addr;
info.object_size = object_size;
info.host_byte_sex = get_host_byte_sex();
info.swapped = info.host_byte_sex != object_byte_sex;
info.cputype = cputype;
info.symbols64 = symbols64;
info.nsymbols = nsymbols;
info.strings = strings;
info.strings_size = strings_size;
info.sorted_symbols = sorted_symbols;
info.nsorted_symbols = nsorted_symbols;
info.verbose = verbose;
info.indent_level = 0;
info.indent_widths[info.indent_level] = 0;
get_sections_64(load_commands, ncmds, sizeofcmds, object_byte_sex,
object_addr, object_size, &info.sections,
&info.nsections, &info.textbase, &info.database);
o = get_section_64(info.sections, info.nsections, SEG_OBJC, sectname);
if(o == NULL)
return;
get_cstring_section_64(load_commands, ncmds, sizeofcmds,object_byte_sex,
object_addr, object_size, &cstring_section);
string_objects = (struct objc_string_object_64 *)o->contents;
string_objects_addr = o->addr;
string_objects_size = o->size;
for(s = string_objects;
(char *)s < (char *)string_objects + string_objects_size;
s++)
{
memset(&string_object, '\0', sizeof(struct objc_string_object_64));
left = string_objects_size - (s - string_objects);
size = left < sizeof(struct objc_string_object_64) ?
left : sizeof(struct objc_string_object_64);
memcpy(&string_object, s, size);
if(info.swapped)
swap_string_object_64(&string_object, info.host_byte_sex);
if((char *)s + sizeof(struct objc_string_object_64) >
(char *)s + string_objects_size) {
printf("String Object extends past end of %s section\n",
sectname);
}
indent_push(&info, sizeof("characters") - 1);
printf("String Object 0x%llx\n",
string_objects_addr + ((char *)s - (char *)string_objects));
print_field_scalar(&info, "isa", "0x%llx", string_object.isa);
name = get_symbol_64((uintptr_t)s - (uintptr_t)string_objects,
o->addr, info.textbase, info.database,
string_object.isa, o->relocs, o->nrelocs,
&info, NULL, NULL);
if(name != NULL)
printf(" %s\n", name);
else
printf("\n");
print_field_scalar(&info, "characters", "0x%llx",
string_object.characters);
if(verbose){
p = get_pointer_64(string_object.characters, NULL, &left32,
NULL, info.sections, info.nsections);
if(p != NULL)
printf(" %.*s", (int)left32, p);
}
printf("\n");
print_field_scalar(&info, "_length", "%u\n", string_object._length);
print_field_scalar(&info, "_pad", "%u\n", string_object._pad);
indent_pop(&info);
}
}
static
void
get_sections_64(
struct load_command *load_commands,
uint32_t ncmds,
uint32_t sizeofcmds,
enum byte_sex object_byte_sex,
char *object_addr,
uint64_t object_size,
struct section_info_64 **sections,
uint32_t *nsections,
uint64_t *textbase,
uint64_t *database)
{
enum byte_sex host_byte_sex;
enum bool swapped, textbase_set, database_set, encrypt_found,
encrypt64_found;
uint32_t i, j;
uint64_t left, size;
struct load_command lcmd, *lc;
char *p;
struct segment_command_64 sg64;
struct section_64 s64;
struct encryption_info_command encrypt;
struct encryption_info_command_64 encrypt64;
host_byte_sex = get_host_byte_sex();
swapped = host_byte_sex != object_byte_sex;
*sections = NULL;
*nsections = 0;
textbase_set = FALSE;
*textbase = 0;
database_set = FALSE;
*database = 0;
encrypt_found = FALSE;
encrypt64_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 - (uint32_t)((char *)lc - (char *)load_commands);
switch(lcmd.cmd){
case LC_SEGMENT_64:
memset((char *)&sg64, '\0', sizeof(struct segment_command_64));
size = left < sizeof(struct segment_command_64) ?
left : sizeof(struct segment_command_64);
memcpy((char *)&sg64, (char *)lc, size);
if(swapped)
swap_segment_command_64(&sg64, host_byte_sex);
if((sg64.initprot & VM_PROT_WRITE) == VM_PROT_WRITE &&
database_set == FALSE){
*database = sg64.vmaddr;
database_set = TRUE;
}
if((sg64.initprot & VM_PROT_READ) == VM_PROT_READ &&
textbase_set == FALSE){
*textbase = sg64.vmaddr;
textbase_set = TRUE;
}
p = (char *)lc + sizeof(struct segment_command_64);
for(j = 0 ; j < sg64.nsects ; j++){
if(p + sizeof(struct section_64) >
(char *)load_commands + sizeofcmds){
printf("section structure command extends past "
"end of load commands\n");
}
left = sizeofcmds - (uint32_t)(p - (char *)load_commands);
memset((char *)&s64, '\0', sizeof(struct section_64));
size = left < sizeof(struct section_64) ?
left : sizeof(struct section_64);
memcpy((char *)&s64, p, size);
if(swapped)
swap_section_64(&s64, 1, host_byte_sex);
*sections = reallocate(*sections,
sizeof(struct section_info_64) * (*nsections + 1));
memcpy((*sections)[*nsections].segname,
s64.segname, 16);
memcpy((*sections)[*nsections].sectname,
s64.sectname, 16);
(*sections)[*nsections].addr = s64.addr;
(*sections)[*nsections].contents = object_addr + s64.offset;
(*sections)[*nsections].offset = s64.offset;
(*sections)[*nsections].zerofill =
(s64.flags & SECTION_TYPE) == S_ZEROFILL ? TRUE : FALSE;
if(s64.offset > object_size){
printf("section contents of: (%.16s,%.16s) is past "
"end of file\n", s64.segname, s64.sectname);
(*sections)[*nsections].size = 0;
}
else if(s64.offset + s64.size > object_size){
printf("part of section contents of: (%.16s,%.16s) "
"is past end of file\n",
s64.segname, s64.sectname);
(*sections)[*nsections].size = object_size - s64.offset;
}
else
(*sections)[*nsections].size = s64.size;
if(s64.reloff >= object_size){
printf("relocation entries offset for (%.16s,%.16s)"
": is past end of file\n", s64.segname,
s64.sectname);
(*sections)[*nsections].nrelocs = 0;
}
else{
(*sections)[*nsections].relocs =
(struct relocation_info *)(object_addr +
s64.reloff);
if(s64.reloff +
s64.nreloc * sizeof(struct relocation_info) >
object_size){
printf("relocation entries for section (%.16s,"
"%.16s) extends past end of file\n",
s64.segname, s64.sectname);
(*sections)[*nsections].nrelocs =
(uint32_t)((object_size - s64.reloff) /
sizeof(struct relocation_info));
}
else
(*sections)[*nsections].nrelocs = s64.nreloc;
if(swapped)
swap_relocation_info(
(*sections)[*nsections].relocs,
(*sections)[*nsections].nrelocs,
host_byte_sex);
}
if(sg64.flags & SG_PROTECTED_VERSION_1)
(*sections)[*nsections].protected = TRUE;
else
(*sections)[*nsections].protected = FALSE;
if((s64.flags & SECTION_TYPE) == S_CSTRING_LITERALS)
(*sections)[*nsections].cstring = TRUE;
else
(*sections)[*nsections].cstring = FALSE;
(*nsections)++;
if(p + sizeof(struct section_64) >
(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;
case LC_ENCRYPTION_INFO_64:
memset((char *)&encrypt64, '\0',
sizeof(struct encryption_info_command_64));
size = left < sizeof(struct encryption_info_command_64) ?
left : sizeof(struct encryption_info_command_64);
memcpy((char *)&encrypt64, (char *)lc, size);
if(swapped)
swap_encryption_command_64(&encrypt64, host_byte_sex);
encrypt64_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(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){
;
}
else if((*sections)[i].offset + (*sections)[i].size <
encrypt.cryptoff){
;
}
else{
(*sections)[i].protected = TRUE;
}
}
}
}
if(encrypt64_found == TRUE && encrypt64.cryptid != 0){
for(i = 0; i < *nsections; i++){
if((*sections)[i].size > 0 && (*sections)[i].zerofill == FALSE){
if((*sections)[i].offset >
encrypt64.cryptoff + encrypt64.cryptsize){
;
}
else if((*sections)[i].offset + (*sections)[i].size <
encrypt64.cryptoff){
;
}
else{
(*sections)[i].protected = TRUE;
}
}
}
}
}
static
struct section_info_64 *
get_section_64(
struct section_info_64 *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_cstring_section_64(
struct load_command *load_commands,
uint32_t ncmds,
uint32_t sizeofcmds,
enum byte_sex object_byte_sex,
char *object_addr,
uint64_t object_size,
struct section_info_64 *cstring_section)
{
enum byte_sex host_byte_sex;
enum bool swapped;
uint32_t i, j;
uint64_t left, size;
struct load_command lcmd, *lc;
char *p;
struct segment_command_64 sg64;
struct section_64 s64;
host_byte_sex = get_host_byte_sex();
swapped = host_byte_sex != object_byte_sex;
memset(cstring_section, '\0', sizeof(struct section_info_64));
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 - (uint32_t)((char *)lc - (char *)load_commands);
switch(lcmd.cmd){
case LC_SEGMENT_64:
memset((char *)&sg64, '\0', sizeof(struct segment_command_64));
size = left < sizeof(struct segment_command_64) ?
left : sizeof(struct segment_command_64);
memcpy((char *)&sg64, (char *)lc, size);
if(swapped)
swap_segment_command_64(&sg64, host_byte_sex);
p = (char *)lc + sizeof(struct segment_command_64);
for(j = 0 ; j < sg64.nsects ; j++){
if(p + sizeof(struct section_64) >
(char *)load_commands + sizeofcmds){
printf("section structure command extends past "
"end of load commands\n");
}
left = sizeofcmds - (uint32_t)(p - (char *)load_commands);
memset((char *)&s64, '\0', sizeof(struct section_64));
size = left < sizeof(struct section_64) ?
left : sizeof(struct section_64);
memcpy((char *)&s64, p, size);
if(swapped)
swap_section_64(&s64, 1, host_byte_sex);
if(strcmp(s64.segname, SEG_TEXT) == 0 &&
strcmp(s64.sectname, "__cstring") == 0){
cstring_section->addr = s64.addr;
cstring_section->contents = object_addr + s64.offset;
if(s64.offset > object_size){
printf("section contents of: (%.16s,%.16s) is past "
"end of file\n", s64.segname, s64.sectname);
cstring_section->size = 0;
}
else if(s64.offset + s64.size > object_size){
printf("part of section contents of: (%.16s,%.16s) "
"is past end of file\n",
s64.segname, s64.sectname);
cstring_section->size = object_size - s64.offset;
}
else
cstring_section->size = s64.size;
if(sg64.flags & SG_PROTECTED_VERSION_1)
cstring_section->protected = TRUE;
else
cstring_section->protected = FALSE;
cstring_section->cstring = TRUE;
return;
}
if(p + sizeof(struct section) >
(char *)load_commands + sizeofcmds)
break;
p += size;
}
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;
}
}
static
void *
get_pointer_64(
uint64_t p,
uint32_t *offset,
uint32_t *left,
struct section_info_64 **s,
struct section_info_64 *sections,
uint32_t nsections)
{
void *r;
uint64_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 = (uint32_t)(addr - sections[i].addr);
if(left != NULL)
*left = (uint32_t)(sections[i].size - (addr-sections[i].addr));
if(sections[i].protected == TRUE && sections[i].cstring == 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);
}
static
const char *
get_symbol_64(
uint64_t sect_offset,
uint64_t sect_addr,
uint64_t textbase,
uint64_t database,
uint64_t value,
struct relocation_info *relocs,
uint32_t nrelocs,
struct info *info,
uint64_t *n_value,
int64_t *addend)
{
uint32_t i;
unsigned int r_symbolnum;
uint32_t n_strx;
const char *name;
enum bool has_auth;
if(n_value != NULL)
*n_value = 0;
if(addend != NULL)
*addend = value;
has_auth = FALSE;
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->symbols64[r_symbolnum].n_un.n_strx;
if(n_strx <= 0 || n_strx >= info->strings_size)
break;
if(info->cputype == CPU_TYPE_ARM64 &&
info->cpusubtype == CPU_SUBTYPE_ARM64E &&
relocs[i].r_type == ARM64_RELOC_AUTHENTICATED_POINTER){
if(addend != NULL){
*addend = 0xffffffffULL & value;
if((*addend & 0x80000000ULL) != 0)
*addend |= 0xffffffff00000000ULL;
}
}
if(n_value != NULL)
*n_value = info->symbols64[r_symbolnum].n_value;
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 + 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->symbols64[r_symbolnum].n_un.n_strx;
if(n_strx <= 0 || n_strx >= info->strings_size)
break;
if(n_value != NULL)
*n_value = info->symbols64[r_symbolnum].n_value;
return(info->strings + n_strx);
}
break;
}
if(reloc_has_pair(info->cputype, info->ext_relocs[i].r_type) ==TRUE)
i++;
}
name = get_dyld_bind_info_symbolname(sect_addr + sect_offset,
info->dbi, info->ndbi,
info->chain_format, addend);
if(name != NULL)
return(name);
value = get_chained_rebase_value(value, info->chain_format, &has_auth);
if(n_value != NULL)
*n_value = value;
if(addend != NULL)
*addend = 0;
if(value == 0)
return(NULL);
if(info->chain_format && has_auth == TRUE){
value += textbase;
}
return(guess_symbol(value, info->sorted_symbols, info->nsorted_symbols,
info->verbose));
}
static
void
print_field_scalar(
struct info *info,
const char* label,
const char* fmt,
...)
{
print_field_label(info, label);
if (fmt) {
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
}
static
void
print_field_label(
struct info *info,
const char* label,
...)
{
va_list ap;
int width = 0;
uint32_t label_indent;
uint32_t label_width;
label_indent = info->indent_level * 4;
#if 1
label_width = (info->indent_level < MAXINDENT ?
info->indent_widths[info->indent_level] : 0);
#else
uint32_t right = 0;
for (uint32_t i = 0; i < MAXINDENT; ++i) {
if (i > info->indent_level)
break;
uint32_t r = i * 4 + info->indent_widths[i];
if (r > right)
right = r;
}
label_width = right - label_indent;
#endif
va_start(ap, label);
if (label) {
width = vsnprintf(NULL, 0, label, ap);
}
va_end(ap);
width = width < label_width ? label_width - width : 0;
printf("%*s", label_indent, "");
if (label) {
va_start(ap, label);
vprintf(label, ap);
va_end(ap);
}
printf("%*s", width + 1, "");
}
static
void
print_field_value(
uint64_t offset,
uint64_t p,
enum bool print_data,
const char* type_name,
const char* suffix,
struct info *info,
struct section_info_64 *s,
uint64_t *out_n_value,
int64_t *out_addend)
{
uint64_t n_value;
int64_t addend;
const char* sym_name;
sym_name = get_symbol_64(offset, s->addr, info->textbase, info->database,
p, s->relocs, s->nrelocs, info, &n_value, &addend);
if (info->verbose) {
printf("0x%llx", n_value);
if (addend)
printf(" + 0x%llx", addend);
if (sym_name)
printf(" %s", sym_name);
}
else {
printf("0x%llx", p);
}
if (info->verbose && print_data) {
const char* ptr_data;
ptr_data = get_pointer_64(n_value + addend, NULL, NULL, NULL,
info->sections, info->nsections);
if (ptr_data)
printf(" %s", ptr_data);
}
#if 0
if (info->verbose && type_name && (n_value + addend > 0)) {
printf(" %s", type_name);
}
#endif
if (suffix)
printf("%s", suffix);
if (out_n_value)
*out_n_value = n_value;
if (out_addend)
*out_addend = addend;
}
void
indent_push(
struct info *info,
uint32_t width)
{
info->indent_level += 1;
if (info->indent_level < MAXINDENT)
info->indent_widths[info->indent_level] = width;
}
void
indent_pop(
struct info *info)
{
if (info->indent_level)
info->indent_level -= 1;
}