kern_return_t export_symbols(const KXLDObject *kext, KXLDDict *defined_symbols_by_name, KXLDDict *defined_cxx_symbols_by_value) { kern_return_t rval = KERN_FAILURE; KXLDSymtabIterator iter; KXLDSym *sym = NULL; (void) kxld_symtab_iterator_init(&iter, kxld_object_get_symtab(kext), kxld_sym_is_exported, FALSE); while ((sym = kxld_symtab_iterator_get_next(&iter))) { if (defined_symbols_by_name) { rval = kxld_dict_insert(defined_symbols_by_name, sym->name, sym); require_noerr(rval, finish); } if (kxld_sym_is_cxx(sym) && defined_cxx_symbols_by_value) { rval = kxld_dict_insert(defined_cxx_symbols_by_value, &sym->link_addr, sym); require_noerr(rval, finish); } } rval = KERN_SUCCESS; finish: return rval; }
static kern_return_t create_vtable_index(KXLDKext *kext) { kern_return_t rval = KERN_FAILURE; KXLDVTable *vtable = NULL; u_int i = 0; if (kext->vtable_index_created) { rval = KERN_SUCCESS; goto finish; } /* Map vtable names to the vtable structures */ rval = kxld_dict_init(&kext->vtable_index, kxld_dict_string_hash, kxld_dict_string_cmp, kext->vtables.nitems); require_noerr(rval, finish); for (i = 0; i < kext->vtables.nitems; ++i) { vtable = kxld_array_get_item(&kext->vtables, i); rval = kxld_dict_insert(&kext->vtable_index, vtable->name, vtable); require_noerr(rval, finish); } kext->vtable_index_created = TRUE; rval = KERN_SUCCESS; finish: return rval; }
kern_return_t kxld_kext_export_vtables(KXLDKext *kext, const KXLDDict *defined_cxx_symbols, const KXLDDict *defined_symbols, KXLDDict *vtables) { kern_return_t rval = KERN_FAILURE; KXLDVTable *vtable = NULL; u_int i = 0; check(kext); check(defined_symbols); check(defined_cxx_symbols); check(vtables); rval = create_vtables(kext, defined_cxx_symbols, defined_symbols); require_noerr(rval, finish); for (i = 0; i < kext->vtables.nitems; ++i) { vtable = kxld_array_get_item(&kext->vtables, i); rval = kxld_dict_insert(vtables, vtable->name, vtable); require_noerr(rval, finish); } rval = KERN_SUCCESS; finish: return rval; }
kern_return_t kxld_create_context(KXLDContext **_context, KXLDAllocateCallback allocate_callback, KXLDLoggingCallback logging_callback, KXLDFlags flags, cpu_type_t cputype, cpu_subtype_t cpusubtype, vm_size_t pagesize __KXLD_KERNEL_UNUSED) { kern_return_t rval = KERN_FAILURE; KXLDContext * context = NULL; KXLDArray * section_order = NULL; #if !KERNEL cpu_type_t * cputype_p = NULL; #endif check(_context); if (isOldInterface) { check(allocate_callback); } check(logging_callback); *_context = NULL; context = kxld_alloc(sizeof(*context)); require_action(context, finish, rval=KERN_RESOURCE_SHORTAGE); bzero(context, sizeof(*context)); context->flags = flags; context->allocate_callback = allocate_callback; context->cputype = cputype; context->cpusubtype = cpusubtype; #if !KERNEL if (pagesize) { kxld_set_cross_link_page_size(pagesize); } #endif /* !KERNEL */ kxld_set_logging_callback(logging_callback); context->kext = kxld_alloc(kxld_kext_sizeof()); require_action(context->kext, finish, rval=KERN_RESOURCE_SHORTAGE); bzero(context->kext, kxld_kext_sizeof()); /* Check if we already have an order array for this arch */ #if KXLD_USER_OR_OBJECT #if KERNEL context->section_order = s_section_order; #else /* In userspace, create the dictionary if it doesn't already exist */ if (!s_order_dict) { s_order_dict = kxld_alloc(sizeof(*s_order_dict)); require_action(s_order_dict, finish, rval=KERN_RESOURCE_SHORTAGE); bzero(s_order_dict, sizeof(*s_order_dict)); rval = kxld_dict_init(s_order_dict, kxld_dict_uint32_hash, kxld_dict_uint32_cmp, 0); require_noerr(rval, finish); } context->section_order = kxld_dict_find(s_order_dict, &cputype); #endif /* KERNEL */ /* Create an order array for this arch if needed */ if (!context->section_order) { section_order = kxld_alloc(sizeof(*section_order)); require_action(section_order, finish, rval=KERN_RESOURCE_SHORTAGE); bzero(section_order, sizeof(*section_order)); #if KERNEL s_section_order = section_order; #else /* In userspace, add the new array to the order dictionary */ cputype_p = kxld_alloc(sizeof(*cputype_p)); require_action(cputype_p, finish, rval=KERN_RESOURCE_SHORTAGE); *cputype_p = cputype; rval = kxld_dict_insert(s_order_dict, cputype_p, section_order); require_noerr(rval, finish); cputype_p = NULL; #endif /* KERNEL */ context->section_order = section_order; section_order = NULL; } #endif /* KXLD_USER_OR_OBJECT */ rval = KERN_SUCCESS; *_context = context; context = NULL; finish: if (context) kxld_destroy_context(context); if (section_order) kxld_free(section_order, sizeof(*section_order)); #if !KERNEL if (cputype_p) kxld_free(cputype_p, sizeof(*cputype_p)); #endif return rval; }
static kern_return_t patch_vtables(KXLDKext *kext, KXLDDict *patched_vtables, const KXLDDict *defined_symbols) { kern_return_t rval = KERN_FAILURE; KXLDSymtabIterator iter; const KXLDSymtab *symtab = NULL; const KXLDSym *metaclass = NULL; KXLDSym *super_metaclass_pointer = NULL; KXLDSym *final_sym = NULL; KXLDVTable *vtable = NULL; KXLDVTable *super_vtable = NULL; char class_name[KXLD_MAX_NAME_LEN]; char super_class_name[KXLD_MAX_NAME_LEN]; char vtable_name[KXLD_MAX_NAME_LEN]; char super_vtable_name[KXLD_MAX_NAME_LEN]; char final_sym_name[KXLD_MAX_NAME_LEN]; char *demangled_name1 = NULL; char *demangled_name2 = NULL; size_t demangled_length1 = 0;; size_t demangled_length2 = 0; size_t len = 0; u_int nvtables = 0; u_int npatched = 0; u_int nprogress = 0; boolean_t failure = FALSE; check(kext); check(patched_vtables); symtab = kxld_object_get_symtab(kext->kext); rval = create_vtable_index(kext); require_noerr(rval, finish); /* Find each super meta class pointer symbol */ kxld_symtab_iterator_init(&iter, symtab, kxld_sym_is_super_metaclass_pointer, FALSE); nvtables = kxld_symtab_iterator_get_num_remaining(&iter); while (npatched < nvtables) { npatched = 0; nprogress = 0; kxld_symtab_iterator_reset(&iter); while((super_metaclass_pointer = kxld_symtab_iterator_get_next(&iter))) { /* Get the class name from the smc pointer */ rval = kxld_sym_get_class_name_from_super_metaclass_pointer( super_metaclass_pointer, class_name, sizeof(class_name)); require_noerr(rval, finish); /* Get the vtable name from the class name */ rval = kxld_sym_get_vtable_name_from_class_name(class_name, vtable_name, sizeof(vtable_name)); require_noerr(rval, finish); /* Get the vtable and make sure it hasn't been patched */ vtable = kxld_dict_find(&kext->vtable_index, vtable_name); require_action(vtable, finish, rval=KERN_FAILURE; kxld_log(kKxldLogPatching, kKxldLogErr, kKxldLogMissingVtable, vtable_name, class_name)); if (!vtable->is_patched) { /* Find the SMCP's meta class symbol */ metaclass = get_metaclass_symbol_from_super_meta_class_pointer_symbol( kext, super_metaclass_pointer); require_action(metaclass, finish, rval=KERN_FAILURE); /* Get the super class name from the super metaclass */ rval = kxld_sym_get_class_name_from_metaclass(metaclass, super_class_name, sizeof(super_class_name)); require_noerr(rval, finish); /* Get the super vtable name from the class name */ rval = kxld_sym_get_vtable_name_from_class_name(super_class_name, super_vtable_name, sizeof(super_vtable_name)); require_noerr(rval, finish); /* Get the super vtable if it's been patched */ super_vtable = kxld_dict_find(patched_vtables, super_vtable_name); if (failure) { const KXLDVTable *unpatched_super_vtable; unpatched_super_vtable = kxld_dict_find(&kext->vtable_index, super_vtable_name); /* If the parent's vtable hasn't been patched, warn that * this vtable is unpatchable because of the parent. */ if (!super_vtable) { kxld_log(kKxldLogPatching, kKxldLogErr, "The %s was not patched because its parent, " "the %s, was not %s.", kxld_demangle(vtable_name, &demangled_name1, &demangled_length1), kxld_demangle(super_vtable_name, &demangled_name2, &demangled_length2), (unpatched_super_vtable) ? "patchable" : "found"); } continue; } if (!super_vtable) continue; /* Get the final symbol's name from the super vtable */ rval = kxld_sym_get_final_sym_name_from_class_name(super_class_name, final_sym_name, sizeof(final_sym_name)); require_noerr(rval, finish); /* Verify that the final symbol does not exist. First check * all the externally defined symbols, then check locally. */ final_sym = kxld_dict_find(defined_symbols, final_sym_name); if (!final_sym) { final_sym = kxld_symtab_get_locally_defined_symbol_by_name( symtab, final_sym_name); } if (final_sym) { kxld_log(kKxldLogPatching, kKxldLogErr, "Class '%s' is a subclass of final class '%s'.", kxld_demangle(class_name, &demangled_name1, &demangled_length1), kxld_demangle(super_class_name, &demangled_name2, &demangled_length2)); continue; } /* Patch the class's vtable */ rval = kxld_vtable_patch(vtable, super_vtable, kext->kext); if (rval) continue; /* Add the class's vtable to the set of patched vtables */ rval = kxld_dict_insert(patched_vtables, vtable->name, vtable); require_noerr(rval, finish); /* Get the meta vtable name from the class name */ rval = kxld_sym_get_meta_vtable_name_from_class_name(class_name, vtable_name, sizeof(vtable_name)); require_noerr(rval, finish); /* Get the meta vtable. Whether or not it should exist has already * been tested in create_vtables(), so if it doesn't exist and we're * still running, we can safely skip it. */ vtable = kxld_dict_find(&kext->vtable_index, vtable_name); if (!vtable) { ++nprogress; ++npatched; continue; } require_action(!vtable->is_patched, finish, rval=KERN_FAILURE); /* There is no way to look up a metaclass vtable at runtime, but * we know that every class's metaclass inherits directly from * OSMetaClass, so we just hardcode that vtable name here. */ len = strlcpy(super_vtable_name, kOSMetaClassVTableName, sizeof(super_vtable_name)); require_action(len == const_strlen(kOSMetaClassVTableName), finish, rval=KERN_FAILURE); /* Get the super meta vtable */ super_vtable = kxld_dict_find(patched_vtables, super_vtable_name); require_action(super_vtable && super_vtable->is_patched, finish, rval=KERN_FAILURE); /* Patch the meta class's vtable */ rval = kxld_vtable_patch(vtable, super_vtable, kext->kext); require_noerr(rval, finish); /* Add the MetaClass's vtable to the set of patched vtables */ rval = kxld_dict_insert(patched_vtables, vtable->name, vtable); require_noerr(rval, finish); ++nprogress; } ++npatched; } require_action(!failure, finish, rval=KERN_FAILURE); failure = (nprogress == 0); } rval = KERN_SUCCESS; finish: if (demangled_name1) kxld_free(demangled_name1, demangled_length1); if (demangled_name2) kxld_free(demangled_name2, demangled_length2); return rval; }
kern_return_t export_symbols_through_interface(const KXLDObject *kext, const KXLDObject *interface, KXLDDict *defined_symbols_by_name, KXLDDict *obsolete_symbols_by_name, KXLDDict *defined_cxx_symbols_by_value) { kern_return_t rval = KERN_FAILURE; KXLDSymtabIterator iter; const KXLDSymtab *kext_symtab = NULL; const KXLDSymtab *interface_symtab = NULL; KXLDSym *kext_sym = NULL; const KXLDSym *interface_sym = NULL; check(kext); check(interface); kext_symtab = kxld_object_get_symtab(kext); interface_symtab = kxld_object_get_symtab(interface); if (defined_symbols_by_name) { /* Add exported symbols */ (void) kxld_symtab_iterator_init(&iter, interface_symtab, kxld_sym_is_undefined, FALSE); while ((interface_sym = kxld_symtab_iterator_get_next(&iter))) { kext_sym = kxld_symtab_get_locally_defined_symbol_by_name(kext_symtab, interface_sym->name); if (!kext_sym) { kxld_log(kKxldLogLinking, kKxldLogWarn, "In interface %s of %s, couldn't find symbol %s\n", kxld_object_get_name(interface), kxld_object_get_name(kext), interface_sym->name); continue; } rval = kxld_dict_insert(defined_symbols_by_name, kext_sym->name, kext_sym); require_noerr(rval, finish); } /* Add indirect symbols */ (void) kxld_symtab_iterator_init(&iter, interface_symtab, kxld_sym_is_indirect, FALSE); while ((interface_sym = kxld_symtab_iterator_get_next(&iter))) { kext_sym = kxld_symtab_get_locally_defined_symbol_by_name(kext_symtab, interface_sym->alias); if (!kext_sym) { kxld_log(kKxldLogLinking, kKxldLogWarn, "In interface %s of %s, couldn't find indirect symbol %s (%s)\n", kxld_object_get_name(interface), kxld_object_get_name(kext), interface_sym->alias, interface_sym->name); continue; } rval = kxld_dict_insert(defined_symbols_by_name, interface_sym->name, kext_sym); require_noerr(rval, finish); } } /* Add obsolete symbols */ if (obsolete_symbols_by_name) { (void) kxld_symtab_iterator_init(&iter, interface_symtab, kxld_sym_is_obsolete, FALSE); while ((kext_sym = kxld_symtab_iterator_get_next(&iter))) { rval = kxld_dict_insert(obsolete_symbols_by_name, kext_sym->name, kext_sym); require_noerr(rval, finish); } } /* Add C++ symbols */ if (defined_cxx_symbols_by_value) { (void) kxld_symtab_iterator_init(&iter, kext_symtab, kxld_sym_is_cxx, FALSE); while ((kext_sym = kxld_symtab_iterator_get_next(&iter))) { rval = kxld_dict_insert(defined_cxx_symbols_by_value, &kext_sym->link_addr, kext_sym); require_noerr(rval, finish); } } rval = KERN_SUCCESS; finish: return rval; }