Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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;
}
Beispiel #4
0
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;
}
Beispiel #5
0
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;
}
Beispiel #6
0
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;
}
Beispiel #7
0
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;
}
Beispiel #8
0
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;
}
Beispiel #9
0
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;
}
Beispiel #10
0
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;
}
Beispiel #11
0
/*******************************************************************************
* 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;
}
Beispiel #12
0
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;
}