static kern_return_t validate_symbols(KXLDKext *kext) { kern_return_t rval = KERN_FAILURE; KXLDSymtabIterator iter; KXLDSym *sym = NULL; u_int error = FALSE; char *demangled_name = NULL; size_t demangled_length = 0; /* Check for any unresolved symbols */ kxld_symtab_iterator_init(&iter, kxld_object_get_symtab(kext->kext), kxld_sym_is_unresolved, FALSE); while ((sym = kxld_symtab_iterator_get_next(&iter))) { if (!error) { error = TRUE; kxld_log(kKxldLogLinking, kKxldLogErr, "The following symbols are unresolved for this kext:"); } kxld_log(kKxldLogLinking, kKxldLogErr, "\t%s", kxld_demangle(sym->name, &demangled_name, &demangled_length)); } require_noerr_action(error, finish, rval=KERN_FAILURE); rval = KERN_SUCCESS; finish: if (demangled_name) kxld_free(demangled_name, demangled_length); return rval; }
kern_return_t kxld_relocator_init(KXLDRelocator *relocator, cpu_type_t cputype, cpu_subtype_t cpusubtype __unused, boolean_t swap) { kern_return_t rval = KERN_FAILURE; check(relocator); switch(cputype) { #if KXLD_USER_OR_I386 case CPU_TYPE_I386: relocator->reloc_has_pair = generic_reloc_has_pair; relocator->reloc_is_pair = generic_reloc_is_pair; relocator->reloc_has_got = generic_reloc_has_got; relocator->process_reloc = generic_process_reloc; relocator->is_32_bit = TRUE; break; #endif /* KXLD_USER_OR_I386 */ #if KXLD_USER_OR_PPC case CPU_TYPE_POWERPC: relocator->reloc_has_pair = ppc_reloc_has_pair; relocator->reloc_is_pair = ppc_reloc_is_pair; relocator->reloc_has_got = ppc_reloc_has_got; relocator->process_reloc = ppc_process_reloc; relocator->is_32_bit = TRUE; break; #endif /* KXLD_USER_OR_PPC */ #if KXLD_USER_OR_X86_64 case CPU_TYPE_X86_64: relocator->reloc_has_pair = x86_64_reloc_has_pair; relocator->reloc_is_pair = x86_64_reloc_is_pair; relocator->reloc_has_got = x86_64_reloc_has_got; relocator->process_reloc = x86_64_process_reloc; relocator->is_32_bit = FALSE; break; #endif /* KXLD_USER_OR_X86_64 */ #if KXLD_USER_OR_ARM case CPU_TYPE_ARM: relocator->reloc_has_pair = arm_reloc_has_pair; relocator->reloc_is_pair = arm_reloc_is_pair; relocator->reloc_has_got = arm_reloc_has_got; relocator->process_reloc = arm_process_reloc; relocator->is_32_bit = TRUE; break; #endif /* KXLD_USER_OR_ARM */ default: rval = KERN_FAILURE; kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogArchNotSupported, cputype); goto finish; } relocator->is_32_bit = kxld_is_32_bit(cputype); relocator->swap = swap; rval = KERN_SUCCESS; finish: return rval; }
static u_char * allocate_kext(KXLDContext *context, void *callback_data, kxld_addr_t *vmaddr_out, u_long *vmsize_out, u_char **linked_object_alloc_out) { KXLDAllocateFlags flags = 0; kxld_addr_t vmaddr = 0; u_long vmsize = 0; u_long header_size = 0; u_char * linked_object = NULL; *linked_object_alloc_out = NULL; kxld_kext_get_vmsize(context->kext, &header_size, &vmsize); vmaddr = context->allocate_callback(vmsize, &flags, callback_data); require_action(!(vmaddr & (kxld_get_effective_page_size()-1)), finish, kxld_log(kKxldLogLinking, kKxldLogErr, "Load address %p is not page-aligned.", (void *) (uintptr_t) vmaddr)); if (flags & kKxldAllocateWritable) { linked_object = (u_char *) (u_long) vmaddr; } else { linked_object = kxld_page_alloc_untracked(vmsize); require(linked_object, finish); *linked_object_alloc_out = linked_object; } kxld_kext_set_linked_object_size(context->kext, vmsize); /* Zero out the memory before we fill it. We fill this buffer in a * sparse fashion, and it's simpler to clear it now rather than * track and zero any pieces we didn't touch after we've written * all of the sections to memory. */ bzero(linked_object, vmsize); *vmaddr_out = vmaddr; *vmsize_out = vmsize; finish: return linked_object; }
kern_return_t kxld_link_file( KXLDContext * context, u_char * file, u_long size, const char * name, void * callback_data, KXLDDependency * dependencies, u_int ndependencies, u_char ** linked_object_out, kxld_addr_t * kmod_info_kern) { kern_return_t rval = KERN_FAILURE; kxld_addr_t vmaddr = 0; u_long vmsize = 0; u_char * linked_object = NULL; u_char * linked_object_alloc = NULL; kaslr_offsets_count = 0; kaslr_offsets_index = 0; kaslr_offsets = NULL; kxld_set_logging_callback_data(name, callback_data); kxld_log(kKxldLogLinking, kKxldLogBasic, "Linking kext %s", name); require_action(context, finish, rval=KERN_INVALID_ARGUMENT); require_action(dependencies, finish, rval=KERN_INVALID_ARGUMENT); require_action(ndependencies, finish, rval=KERN_INVALID_ARGUMENT); require_action(file, finish, rval=KERN_INVALID_ARGUMENT); require_action(size, finish, rval=KERN_INVALID_ARGUMENT); require_action(linked_object_out, finish, rval=KERN_INVALID_ARGUMENT); require_action(kmod_info_kern, finish, rval=KERN_INVALID_ARGUMENT); isSplitKext = FALSE; isOldInterface = TRUE; rval = init_context(context, ndependencies); require_noerr(rval, finish); rval = init_kext_objects(context, file, size, name, dependencies, ndependencies); require_noerr(rval, finish); linked_object = allocate_kext(context, callback_data, &vmaddr, &vmsize, &linked_object_alloc); require_action(linked_object, finish, rval=KERN_RESOURCE_SHORTAGE); rval = kxld_kext_relocate(context->kext, vmaddr, &context->vtables_by_name, &context->defined_symbols_by_name, &context->obsolete_symbols_by_name, &context->defined_cxx_symbols_by_value); require_noerr(rval, finish); rval = kxld_kext_export_linked_object(context->kext, (void *) linked_object, kmod_info_kern); require_noerr(rval, finish); *linked_object_out = linked_object; linked_object_alloc = NULL; rval = KERN_SUCCESS; finish: if (linked_object_alloc) { kxld_page_free_untracked(linked_object_alloc, vmsize); } clear_context(context); kxld_set_logging_callback_data(NULL, NULL); return rval; }
kern_return_t kxld_link_split_file( KXLDContext * context, splitKextLinkInfo *link_info, const char * name, void * callback_data, KXLDDependency * dependencies, u_int ndependencies, kxld_addr_t * kmod_info_kern) { kern_return_t rval = KERN_FAILURE; KXLDObject * kext_object = NULL; splitKextLinkInfo * my_link_info = NULL; isSplitKext = (link_info->vmaddr_TEXT_EXEC != 0); isOldInterface = FALSE; kxld_set_logging_callback_data(name, callback_data); kxld_log(kKxldLogLinking, kKxldLogBasic, "Linking kext %s", name); kaslr_offsets_count = 0; kaslr_offsets_index = 0; kaslr_offsets = NULL; require_action(context, finish, rval=KERN_INVALID_ARGUMENT); require_action(link_info, finish, rval=KERN_INVALID_ARGUMENT); require_action(dependencies, finish, rval=KERN_INVALID_ARGUMENT); require_action(ndependencies, finish, rval=KERN_INVALID_ARGUMENT); require_action(kmod_info_kern, finish, rval=KERN_INVALID_ARGUMENT); rval = init_context(context, ndependencies); require_noerr(rval, finish); rval = init_kext_objects(context, link_info->kextExecutable, link_info->kextSize, name, dependencies, ndependencies); require_noerr(rval, finish); kext_object = get_object_for_file(context, link_info->kextExecutable, link_info->kextSize, name); require_action(kext_object, finish, rval=KERN_FAILURE); // copy vmaddrs and fileoffsets for split segments into kext_object kxld_object_set_link_info(kext_object, link_info); my_link_info = kxld_object_get_link_info(kext_object); rval = allocate_split_kext(context, my_link_info); require_noerr(rval, finish); #if SPLIT_KEXTS_DEBUG kxld_log(kKxldLogLinking, kKxldLogErr, "Linking kext %s", name); kxld_show_split_info(link_info); #endif // SPLIT_KEXTS_DEBUG rval = kxld_kext_relocate(context->kext, (kxld_addr_t)my_link_info, &context->vtables_by_name, &context->defined_symbols_by_name, &context->obsolete_symbols_by_name, &context->defined_cxx_symbols_by_value); require_noerr(rval, finish); rval = kxld_kext_export_linked_object(context->kext, (void *) my_link_info, kmod_info_kern); require_noerr(rval, finish); // pass back info about linked kext link_info->kaslr_offsets_count = kaslr_offsets_count; link_info->kaslr_offsets = kaslr_offsets; link_info->linkedKext = my_link_info->linkedKext; link_info->linkedKextSize = my_link_info->linkedKextSize; if (kaslr_offsets_count != kaslr_offsets_index) { kxld_log(kKxldLogLinking, kKxldLogErr, "[ERROR] %s: KASLR pointers: count=%d, but only populated %d!", name, kaslr_offsets_count, kaslr_offsets_index); rval = KERN_FAILURE; goto finish; } // the values are now the responsibility of the caller kaslr_offsets_count = 0; kaslr_offsets_index = 0; kaslr_offsets = NULL; rval = KERN_SUCCESS; finish: clear_context(context); kxld_set_logging_callback_data(NULL, NULL); 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; }
static kern_return_t resolve_symbols(KXLDKext *kext, const KXLDDict *defined_symbols, const KXLDDict *obsolete_symbols) { kern_return_t rval = KERN_FAILURE; const KXLDSymtab *symtab = NULL; KXLDSymtabIterator iter; KXLDSym *sym = NULL; KXLDSym *defined_sym = NULL; const char *name = NULL; boolean_t tests_for_weak = FALSE; boolean_t error = FALSE; char *demangled_name = NULL; size_t demangled_length = 0; check(kext->kext); check(defined_symbols); check(obsolete_symbols); symtab = kxld_object_get_symtab(kext->kext); /* Check if the kext tests for weak symbols */ sym = kxld_symtab_get_symbol_by_name(symtab, KXLD_WEAK_TEST_SYMBOL); tests_for_weak = (sym != NULL); /* Check for duplicate symbols */ kxld_symtab_iterator_init(&iter, symtab, kxld_sym_is_exported, FALSE); while ((sym = kxld_symtab_iterator_get_next(&iter))) { defined_sym = kxld_dict_find(defined_symbols, sym->name); if (defined_sym) { /* Not a problem if the symbols have the same address */ if (defined_sym->link_addr == sym->link_addr) { continue; } if (!error) { error = TRUE; kxld_log(kKxldLogLinking, kKxldLogErr, "The following symbols were defined more than once:"); } kxld_log(kKxldLogLinking, kKxldLogErr, "\t%s: %p - %p", kxld_demangle(sym->name, &demangled_name, &demangled_length), (void *) (uintptr_t) sym->link_addr, (void *) (uintptr_t) defined_sym->link_addr); } } require_noerr_action(error, finish, rval=KERN_FAILURE); /* Resolve undefined and indirect symbols */ /* Iterate over all unresolved symbols */ kxld_symtab_iterator_init(&iter, symtab, kxld_sym_is_unresolved, FALSE); while ((sym = kxld_symtab_iterator_get_next(&iter))) { /* Common symbols are not supported */ if (kxld_sym_is_common(sym)) { if (!error) { error = TRUE; if (kxld_object_target_supports_common_symbols(kext->kext)) { kxld_log(kKxldLogLinking, kKxldLogErr, "The following common symbols were not resolved:"); } else { kxld_log(kKxldLogLinking, kKxldLogErr, "Common symbols are not supported in kernel extensions. " "Use -fno-common to build your kext. " "The following are common symbols:"); } } kxld_log(kKxldLogLinking, kKxldLogErr, "\t%s", kxld_demangle(sym->name, &demangled_name, &demangled_length)); } else { /* Find the address of the defined symbol */ if (kxld_sym_is_undefined(sym)) { name = sym->name; } else { name = sym->alias; } defined_sym = kxld_dict_find(defined_symbols, name); /* Resolve the symbol. If a definition cannot be found, then: * 1) Psuedokexts log a warning and proceed * 2) Actual kexts delay the error until validation in case vtable * patching replaces the undefined symbol. */ if (defined_sym) { rval = kxld_sym_resolve(sym, defined_sym->link_addr); require_noerr(rval, finish); if (obsolete_symbols && kxld_dict_find(obsolete_symbols, name)) { kxld_log(kKxldLogLinking, kKxldLogWarn, "This kext uses obsolete symbol %s.", kxld_demangle(name, &demangled_name, &demangled_length)); } } else if (kxld_sym_is_weak(sym)) { kxld_addr_t addr = 0; /* Make sure that the kext has referenced gOSKextUnresolved. */ require_action(tests_for_weak, finish, rval=KERN_FAILURE; kxld_log(kKxldLogLinking, kKxldLogErr, "This kext has weak references but does not test for " "them. Test for weak references with " "OSKextIsSymbolResolved().")); #if KERNEL /* Get the address of the default weak address. */ addr = (kxld_addr_t) &kext_weak_symbol_referenced; #else /* This is run during symbol generation only, so we only * need a filler value here. */ addr = 0xF00DD00D; #endif /* KERNEL */ rval = kxld_sym_resolve(sym, addr); require_noerr(rval, finish); } } } require_noerr_action(error, finish, rval=KERN_FAILURE); rval = KERN_SUCCESS; finish: if (demangled_name) kxld_free(demangled_name, demangled_length); return rval; }
static kern_return_t get_vtable_syms_from_smcp(KXLDKext *kext, const KXLDDict *defined_symbols, KXLDSym *super_metaclass_ptr_sym, KXLDSym **vtable_sym_out, KXLDSym **meta_vtable_sym_out) { kern_return_t rval = KERN_FAILURE; const KXLDSymtab *symtab = NULL; KXLDSym *vtable_sym = NULL; KXLDSym *meta_vtable_sym = NULL; char class_name[KXLD_MAX_NAME_LEN]; char vtable_name[KXLD_MAX_NAME_LEN]; char meta_vtable_name[KXLD_MAX_NAME_LEN]; char *demangled_name1 = NULL; char *demangled_name2 = NULL; size_t demangled_length1 = 0; size_t demangled_length2 = 0; check(kext); check(vtable_sym_out); check(meta_vtable_sym_out); require(!kxld_object_is_kernel(kext->kext), finish); symtab = kxld_object_get_symtab(kext->kext); /* Get the class name from the smc pointer */ rval = kxld_sym_get_class_name_from_super_metaclass_pointer( super_metaclass_ptr_sym, 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 symbol */ if (defined_symbols) { vtable_sym = kxld_dict_find(defined_symbols, vtable_name); } else { vtable_sym = kxld_symtab_get_locally_defined_symbol_by_name(symtab, vtable_name); } require_action(vtable_sym, finish, rval=KERN_FAILURE; kxld_log(kKxldLogPatching, kKxldLogErr, kKxldLogMissingVtable, vtable_name, class_name)); /* Get the meta vtable name from the class name */ rval = kxld_sym_get_meta_vtable_name_from_class_name(class_name, meta_vtable_name, sizeof(meta_vtable_name)); require_noerr(rval, finish); /* Get the meta vtable symbol */ if (defined_symbols) { meta_vtable_sym = kxld_dict_find(defined_symbols, meta_vtable_name); } else { meta_vtable_sym = kxld_symtab_get_locally_defined_symbol_by_name(symtab, meta_vtable_name); } if (!meta_vtable_sym) { if (kxld_object_target_supports_strict_patching(kext->kext)) { kxld_log(kKxldLogPatching, kKxldLogErr, kKxldLogMissingVtable, meta_vtable_name, class_name); rval = KERN_FAILURE; goto finish; } else { kxld_log(kKxldLogPatching, kKxldLogErr, "Warning: " kKxldLogMissingVtable, kxld_demangle(meta_vtable_name, &demangled_name1, &demangled_length1), kxld_demangle(class_name, &demangled_name2, &demangled_length2)); } } *vtable_sym_out = vtable_sym; *meta_vtable_sym_out = meta_vtable_sym; 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; }
static kern_return_t init_predicates(KXLDSym *sym, u_char n_type, u_short n_desc) { kern_return_t rval = KERN_FAILURE; check(sym); /* The type field is interpreted differently for normal symbols and stabs */ if (n_type & N_STAB) { sym->predicates.is_stab = 1; switch (n_type) { /* Labeled as NO_SECT in stab.h */ case N_GSYM: case N_FNAME: case N_RSYM: case N_SSYM: case N_LSYM: case N_BINCL: case N_PARAMS: case N_VERSION: case N_OLEVEL: case N_PSYM: case N_EINCL: case N_EXCL: case N_BCOMM: case N_LENG: case N_OPT: case N_OSO: sym->predicates.is_absolute = 1; break; /* Labeled as n_sect in stab.h */ case N_FUN: case N_STSYM: case N_LCSYM: case N_BNSYM: case N_SLINE: case N_ENSYM: case N_SO: case N_SOL: case N_ENTRY: case N_ECOMM: case N_ECOML: /* These are labeled as NO_SECT in stab.h, but they are actually * section-based on OS X. We must mark them as such so they get * relocated. */ case N_LBRAC: case N_RBRAC: sym->predicates.is_section = 1; break; default: rval = KERN_FAILURE; kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogMalformedMachO "Invalid N_STAB symbol type: %u.", n_type); goto finish; } /* Don't care about the C++ predicates for stabs */ } else { u_char type = n_type & N_TYPE; /* Set the type-independent fields */ if ((n_type & N_EXT) && !(n_type & N_PEXT)) { sym->predicates.is_external = 1; } if (n_desc & N_DESC_DISCARDED) { sym->predicates.is_obsolete = 1; } if (n_desc & N_WEAK_REF) { sym->predicates.is_weak = 1; } if (n_desc & N_ARM_THUMB_DEF) { sym->predicates.is_thumb = 1; } /* The first set of type fields are mutually exclusive, so they can be * set with a switch statement. */ switch (type) { case N_ABS: sym->predicates.is_absolute = 1; break; case N_SECT: sym->predicates.is_section = 1; break; case N_UNDF: if (sym->base_addr) { sym->predicates.is_common = 1; } else { sym->predicates.is_undefined = 1; } break; case N_INDR: sym->predicates.is_indirect = 1; break; default: rval = KERN_FAILURE; kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogMalformedMachO "Invalid symbol type: %u.", type); goto finish; } /* Set the C++-specific fields */ if ((0 == strncmp(CXX_PREFIX, sym->name, const_strlen(CXX_PREFIX)))) { sym->predicates.is_cxx = 1; if (0 == strncmp(sym->name, METACLASS_VTABLE_PREFIX, const_strlen(METACLASS_VTABLE_PREFIX))) { sym->predicates.is_meta_vtable = 1; } else if (0 == strncmp(sym->name, VTABLE_PREFIX, const_strlen(VTABLE_PREFIX))) { sym->predicates.is_class_vtable = 1; } else if (kxld_strstr(sym->name, RESERVED_TOKEN)) { sym->predicates.is_padslot = 1; } else if (kxld_strstr(sym->name, METACLASS_TOKEN)) { sym->predicates.is_metaclass = 1; } else if (kxld_strstr(sym->name, SUPER_METACLASS_POINTER_TOKEN)) { sym->predicates.is_super_metaclass_pointer = 1; } } else if (streq_safe(CXX_PURE_VIRTUAL, sym->name, sizeof(CXX_PURE_VIRTUAL))) { sym->predicates.is_cxx = 1; sym->predicates.is_pure_virtual = 1; } } rval = KERN_SUCCESS; finish: return rval; }
/******************************************************************************* * Patching vtables allows us to preserve binary compatibility across releases. *******************************************************************************/ kern_return_t kxld_vtable_patch(KXLDVTable *vtable, const KXLDVTable *super_vtable, KXLDObject *object) { kern_return_t rval = KERN_FAILURE; const KXLDSymtab *symtab = NULL; const KXLDSym *sym = NULL; KXLDVTableEntry *child_entry = NULL; KXLDVTableEntry *parent_entry = NULL; u_int symindex = 0; u_int i = 0; char *demangled_name1 = NULL; char *demangled_name2 = NULL; char *demangled_name3 = NULL; size_t demangled_length1 = 0; size_t demangled_length2 = 0; size_t demangled_length3 = 0; boolean_t failure = FALSE; check(vtable); check(super_vtable); symtab = kxld_object_get_symtab(object); require_action(!vtable->is_patched, finish, rval=KERN_SUCCESS); require_action(vtable->entries.nitems >= super_vtable->entries.nitems, finish, rval=KERN_FAILURE; kxld_log(kKxldLogPatching, kKxldLogErr, kKxldLogMalformedVTable, kxld_demangle(vtable->name, &demangled_name1, &demangled_length1))); for (i = 0; i < super_vtable->entries.nitems; ++i) { child_entry = kxld_array_get_item(&vtable->entries, i); parent_entry = kxld_array_get_item(&super_vtable->entries, i); /* The child entry can be NULL when a locally-defined, non-external * symbol is stripped. We wouldn't patch this entry anyway, so we * just skip it. */ if (!child_entry->unpatched.sym) continue; /* It's possible for the patched parent entry not to have a symbol * (e.g. when the definition is inlined). We can't patch this entry no * matter what, so we'll just skip it and die later if it's a problem * (which is not likely). */ if (!parent_entry->patched.name) continue; /* 1) If the symbol is defined locally, do not patch */ if (kxld_sym_is_defined_locally(child_entry->unpatched.sym)) continue; /* 2) If the child is a pure virtual function, do not patch. * In general, we want to proceed with patching when the symbol is * externally defined because pad slots fall into this category. * The pure virtual function symbol is special case, as the pure * virtual property itself overrides the parent's implementation. */ if (kxld_sym_is_pure_virtual(child_entry->unpatched.sym)) continue; /* 3) If the symbols are the same, do not patch */ if (streq(child_entry->unpatched.sym->name, parent_entry->patched.name)) { continue; } /* 4) If the parent vtable entry is a pad slot, and the child does not * match it, then the child was built against a newer version of the * libraries, so it is binary-incompatible. */ require_action(!kxld_sym_name_is_padslot(parent_entry->patched.name), finish, rval=KERN_FAILURE; kxld_log(kKxldLogPatching, kKxldLogErr, kKxldLogParentOutOfDate, kxld_demangle(super_vtable->name, &demangled_name1, &demangled_length1), kxld_demangle(vtable->name, &demangled_name2, &demangled_length2))); #if KXLD_USER_OR_STRICT_PATCHING /* 5) If we are doing strict patching, we prevent kexts from declaring * virtual functions and not implementing them. We can tell if a * virtual function is declared but not implemented because we resolve * symbols before patching; an unimplemented function will still be * undefined at this point. We then look at whether the symbol has * the same class prefix as the vtable. If it does, the symbol was * declared as part of the class and not inherited, which means we * should not patch it. */ if (kxld_object_target_supports_strict_patching(object) && !kxld_sym_is_defined(child_entry->unpatched.sym)) { char class_name[KXLD_MAX_NAME_LEN]; char function_prefix[KXLD_MAX_NAME_LEN]; u_long function_prefix_len = 0; rval = kxld_sym_get_class_name_from_vtable_name(vtable->name, class_name, sizeof(class_name)); require_noerr(rval, finish); function_prefix_len = kxld_sym_get_function_prefix_from_class_name(class_name, function_prefix, sizeof(function_prefix)); require(function_prefix_len, finish); if (!strncmp(child_entry->unpatched.sym->name, function_prefix, function_prefix_len)) { failure = TRUE; kxld_log(kKxldLogPatching, kKxldLogErr, "The %s is unpatchable because its class declares the " "method '%s' without providing an implementation.", kxld_demangle(vtable->name, &demangled_name1, &demangled_length1), kxld_demangle(child_entry->unpatched.sym->name, &demangled_name2, &demangled_length2)); continue; } } #endif /* KXLD_USER_OR_STRICT_PATCHING */ /* 6) The child symbol is unresolved and different from its parent, so * we need to patch it up. We do this by modifying the relocation * entry of the vtable entry to point to the symbol of the parent * vtable entry. If that symbol does not exist (i.e. we got the data * from a link state object's vtable representation), then we create a * new symbol in the symbol table and point the relocation entry to * that. */ sym = kxld_symtab_get_locally_defined_symbol_by_name(symtab, parent_entry->patched.name); if (!sym) { rval = kxld_object_add_symbol(object, parent_entry->patched.name, parent_entry->patched.addr, &sym); require_noerr(rval, finish); } require_action(sym, finish, rval=KERN_FAILURE); rval = kxld_symtab_get_sym_index(symtab, sym, &symindex); require_noerr(rval, finish); rval = kxld_reloc_update_symindex(child_entry->unpatched.reloc, symindex); require_noerr(rval, finish); kxld_log(kKxldLogPatching, kKxldLogDetail, "In vtable '%s', patching '%s' with '%s'.", kxld_demangle(vtable->name, &demangled_name1, &demangled_length1), kxld_demangle(child_entry->unpatched.sym->name, &demangled_name2, &demangled_length2), kxld_demangle(sym->name, &demangled_name3, &demangled_length3)); rval = kxld_object_patch_symbol(object, child_entry->unpatched.sym); require_noerr(rval, finish); child_entry->unpatched.sym = sym; /* * The C++ ABI requires that functions be aligned on a 2-byte boundary: * http://www.codesourcery.com/public/cxx-abi/abi.html#member-pointers * If the LSB of any virtual function's link address is 1, then the * compiler has violated that part of the ABI, and we're going to panic * in _ptmf2ptf() (in OSMetaClass.h). Better to panic here with some * context. */ assert(kxld_sym_is_pure_virtual(sym) || !(sym->link_addr & 1)); } require_action(!failure, finish, rval=KERN_FAILURE); /* Change the vtable representation from the unpatched layout to the * patched layout. */ for (i = 0; i < vtable->entries.nitems; ++i) { char *name; kxld_addr_t addr; child_entry = kxld_array_get_item(&vtable->entries, i); if (child_entry->unpatched.sym) { name = child_entry->unpatched.sym->name; addr = child_entry->unpatched.sym->link_addr; } else { name = NULL; addr = 0; } child_entry->patched.name = name; child_entry->patched.addr = addr; } vtable->is_patched = TRUE; rval = KERN_SUCCESS; finish: if (demangled_name1) kxld_free(demangled_name1, demangled_length1); if (demangled_name2) kxld_free(demangled_name2, demangled_length2); if (demangled_name3) kxld_free(demangled_name3, demangled_length3); return rval; }
kern_return_t kxld_splitinfolc_export_macho(const KXLDsplitinfolc *splitinfolc, splitKextLinkInfo *linked_object, u_long *header_offset, u_long header_size, u_long *data_offset, u_long size) { kern_return_t rval = KERN_FAILURE; struct linkedit_data_command *splitinfolc_hdr = NULL; u_char * buf; check(splitinfolc); check(linked_object); check(header_offset); check(data_offset); buf = (u_char *)(linked_object->linkedKext); require_action(sizeof(*splitinfolc_hdr) <= header_size - *header_offset, finish, rval=KERN_FAILURE); splitinfolc_hdr = (struct linkedit_data_command *)((void *)(buf + *header_offset)); *header_offset += sizeof(*splitinfolc_hdr); if (buf + *data_offset > buf + size) { kxld_log(kKxldLogLinking, kKxldLogErr, "\n OVERFLOW! linkedKext %p to %p (%lu) copy %p to %p (%u) <%s>", (void *) buf, (void *) (buf + size), size, (void *) (buf + *data_offset), (void *) (buf + *data_offset + splitinfolc->datasize), splitinfolc->datasize, __func__); goto finish; } // copy in the split info reloc data from kextExecutable. For example dataoff // in LC_SEGMENT_SPLIT_INFO load command points to the reloc data in the // __LINKEDIT segment. In this case 65768 into the kextExecutable file is // the split seg reloc info (for 920 bytes) // Load command 9 // cmd LC_SEGMENT_SPLIT_INFO // cmdsize 16 // dataoff 65768 // datasize 920 memcpy(buf + *data_offset, linked_object->kextExecutable + splitinfolc->dataoff, splitinfolc->datasize); #if SPLIT_KEXTS_DEBUG u_char *dataPtr = buf + *data_offset; kxld_log(kKxldLogLinking, kKxldLogErr, "\n\n linkedKext %p to %p (%lu) copy %p to %p (%u) <%s>", (void *) buf, (void *) (buf + size), size, (void *) (dataPtr), (void *) (dataPtr + splitinfolc->datasize), splitinfolc->datasize, __func__); if (*(dataPtr + 0) != 0x7F) { kxld_log(kKxldLogLinking, kKxldLogErr, "\n\n bad LC_SEGMENT_SPLIT_INFO: 0x%02X %02X %02X %02X %02X %02X %02X %02X at %p (buf %p + %lu) <%s>", *(dataPtr +0), *(dataPtr +1), *(dataPtr +2), *(dataPtr +3), *(dataPtr +4), *(dataPtr +5), *(dataPtr +6), *(dataPtr +7), (void *) dataPtr, (void *) buf, *data_offset, __func__); } #endif // update the load command header splitinfolc_hdr->cmd = LC_SEGMENT_SPLIT_INFO; splitinfolc_hdr->cmdsize = (uint32_t) sizeof(*splitinfolc_hdr); splitinfolc_hdr->dataoff = (uint32_t)(*data_offset); splitinfolc_hdr->datasize = splitinfolc->datasize; *data_offset += splitinfolc->datasize; rval = KERN_SUCCESS; finish: return rval; }