kxld_state.c   [plain text]


/*
 * Copyright (c) 2008 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 * 
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
#include <string.h>

#if !KERNEL
    #include <libkern/OSByteOrder.h>
#endif

#define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld"
#include <AssertMacros.h>

#include "kxld_array.h"
#include "kxld_dict.h"
#include "kxld_kext.h"
#include "kxld_state.h"
#include "kxld_sym.h"
#include "kxld_symtab.h"
#include "kxld_util.h"
#include "kxld_vtable.h"

#define LINK_STATE_MAGIC 0xF00DD00D
#define CIGAM_ETATS_KNIL 0x0DD00DF0

#define LINK_STATE_MAGIC_64 0xCAFEF00D
#define CIGAM_ETATS_KNIL_64 0x0DF0FECA

#define LINK_STATE_VERSION 1

static kern_return_t init_string_index(KXLDDict *strings, KXLDArray *tmps, 
    KXLDSymtabIterator *iter, const KXLDArray *vtables, u_int nsymentries, 
    u_long *strsize);
static kern_return_t add_string_to_index(KXLDDict *strings, const char *str, 
    KXLDArray *tmps, u_int *tmpi, u_long *stroff);
static kern_return_t create_link_state(u_char **_file, u_long *_filesize, 
    const KXLDKext *kext,  KXLDSymtabIterator *iter, const KXLDArray *vtables, 
    KXLDDict *strings, u_int nsyms, u_int nsymentries, u_long strsize);
static boolean_t state_is_32_bit(KXLDLinkStateHdr *state);

#if KXLD_USER_OR_ILP32
static kern_return_t get_symbols_32(KXLDState *state, KXLDDict *defined_symbols,
    KXLDDict *obsolete_symbols);
static kern_return_t copy_symbols_32(u_char *file, u_long *data_offset, 
    KXLDSymtabIterator *iter, const KXLDDict *strings);
static kern_return_t copy_vtables_32(u_char *file, u_long *header_offset, 
    u_long *data_offset, const KXLDArray *vtables, const KXLDDict *strings);
#endif /* KXLD_USER_OR_ILP32*/
#if KXLD_USER_OR_LP64
static kern_return_t get_symbols_64(KXLDState *state, KXLDDict *defined_symbols,
    KXLDDict *obsolete_symbols);
static kern_return_t copy_symbols_64(u_char *file, u_long *data_offset, 
    KXLDSymtabIterator *iter, const KXLDDict *strings);
static kern_return_t copy_vtables_64(u_char *file, u_long *header_offset, 
    u_long *data_offset, const KXLDArray *vtables, const KXLDDict *strings);
#endif /* KXLD_USER_OR_ILP64 */

#if !KERNEL
static boolean_t swap_link_state(u_char *state);
static void swap_link_state_32(u_char *state);
static void swap_link_state_64(u_char *state);
static boolean_t unswap_link_state(u_char *state);
static void unswap_link_state_32(u_char *state);
static void unswap_link_state_64(u_char *state);
static void swap_state_hdr(KXLDLinkStateHdr *state_hdr);
static void swap_vtable_hdr(KXLDVTableHdr *vtable_hdr);
static void swap_sym_entry_32(KXLDSymEntry32 *entry);
static void swap_sym_entry_64(KXLDSymEntry64 *entry);
#endif

/*******************************************************************************
*******************************************************************************/
kern_return_t
kxld_state_init_from_file(KXLDState *state, u_char *file, 
    KXLDArray *section_order __unused)
{
    kern_return_t rval = KERN_FAILURE;
    KXLDLinkStateHdr *hdr = (KXLDLinkStateHdr *) file;
#if KXLD_USER_OR_OBJECT
    KXLDSectionName *dstname = NULL;
    KXLDSectionName *srcname = NULL;
#endif
    KXLDVTableHdr *vhdr = NULL;
    KXLDVTable *vtable = NULL;
    u_int i = 0;

    check(state);
    check(file);

#if !KERNEL
    /* Swap the link state file to host byte order for as long this kxld_state
     * object owns the file.
     */
    state->swap = swap_link_state(file);
#endif
    require_action(hdr->magic == LINK_STATE_MAGIC || 
        hdr->magic == LINK_STATE_MAGIC_64,
        finish, rval=KERN_FAILURE);

    state->file = file;

#if KXLD_USER_OR_OBJECT
    if (section_order && !section_order->nitems && hdr->nsects) {
        rval = kxld_array_init(section_order, sizeof(*dstname), hdr->nsects);
        require_noerr(rval, finish);

        srcname = (KXLDSectionName *) (file + hdr->sectoff);
        for (i = 0; i < hdr->nsects; ++i, ++srcname) {
            dstname = kxld_array_get_item(section_order, i);
            memcpy(dstname, srcname, sizeof(*srcname));
        }
    }
#endif

    rval = kxld_array_init(&state->vtables, sizeof(*vtable), hdr->nvtables);
    require_noerr(rval, finish);
    
    vhdr = (KXLDVTableHdr *) (file + hdr->voff);
    for (i = 0; i < hdr->nvtables; ++i, ++vhdr) {
        vtable = kxld_array_get_item(&state->vtables, i);
        KXLD_3264_FUNC(kxld_is_32_bit(hdr->cputype), rval,
            kxld_vtable_init_from_link_state_32,
            kxld_vtable_init_from_link_state_64,
            vtable, file, vhdr);
        require_noerr(rval, finish);
    }

    rval = KERN_SUCCESS;

finish:
    return rval;
}

/*******************************************************************************
*******************************************************************************/
void 
kxld_state_clear(KXLDState *state)
{
    KXLDVTable *vtable = NULL;
    u_int i = 0;

    check(state);

#if !KERNEL
    /* We use kxld_state objects to wrap the link state files.  Whenever the
     * file is wrapped by a kxld_state object, the file is kept in host byte
     * order.  Once we are done, we must return it to target byte order.
     */
    if (state->swap) (void)unswap_link_state(state->file);
#endif

    state->file = NULL;
    state->swap = FALSE;
    for (i = 0; i < state->vtables.nitems; ++i) {
        vtable = kxld_array_get_item(&state->vtables, i);
        kxld_vtable_clear(vtable);
    }
    kxld_array_reset(&state->vtables);
}

/*******************************************************************************
*******************************************************************************/
void 
kxld_state_deinit(KXLDState *state)
{
    KXLDVTable *vtable = NULL;
    u_int i = 0;

    check(state);

#if !KERNEL
    if (state->file && state->swap) (void)unswap_link_state(state->file);
#endif
   
    for (i = 0; i < state->vtables.maxitems; ++i) {
        vtable = kxld_array_get_slot(&state->vtables, i);
        kxld_vtable_deinit(vtable);
    }
    kxld_array_deinit(&state->vtables);
    bzero(state, sizeof(*state));
}

/*******************************************************************************
*******************************************************************************/
u_int 
kxld_state_get_num_symbols(KXLDState *state)
{
    KXLDLinkStateHdr *hdr = (KXLDLinkStateHdr *) state->file;

    return hdr->nsyms;
}

/*******************************************************************************
*******************************************************************************/
kern_return_t 
kxld_state_get_symbols(KXLDState *state, KXLDDict *defined_symbols,
    KXLDDict *obsolete_symbols)
{
    KXLDLinkStateHdr * hdr = (KXLDLinkStateHdr *) state->file;
    kern_return_t rval = KERN_FAILURE;

    check(state);
    check(defined_symbols);
    check(obsolete_symbols);

    require_action(hdr->magic == LINK_STATE_MAGIC || 
        hdr->magic == LINK_STATE_MAGIC_64,
        finish, rval=KERN_FAILURE);

    KXLD_3264_FUNC(state_is_32_bit(hdr), rval,
        get_symbols_32, get_symbols_64,
        state, defined_symbols, obsolete_symbols);
    require_noerr(rval, finish);

    rval = KERN_SUCCESS;

finish:
    return rval;
}

#if KXLD_USER_OR_ILP32
/*******************************************************************************
*******************************************************************************/
static kern_return_t
get_symbols_32(KXLDState *state, KXLDDict *defined_symbols,
    KXLDDict *obsolete_symbols)
{
    kern_return_t rval = KERN_FAILURE;
    KXLDLinkStateHdr *hdr = (KXLDLinkStateHdr *) state->file;
    KXLDSymEntry32 *entry = NULL;
    const char *name = NULL;
    u_int i = 0;

    entry = (KXLDSymEntry32 *) (state->file + hdr->symoff);
    for (i = 0; i < hdr->nsyms; ++i, ++entry) {
        name = (const char *) (state->file + entry->nameoff);
        rval = kxld_dict_insert(defined_symbols, name, &entry->addr);
        require_noerr(rval, finish);

        if (entry->flags & KXLD_SYM_OBSOLETE) {
            rval = kxld_dict_insert(obsolete_symbols, name, &entry->addr);
            require_noerr(rval, finish);
        }
    }

    rval = KERN_SUCCESS;

finish:
    return rval;
}
#endif /* KXLD_USER_OR_ILP32 */

#if KXLD_USER_OR_LP64
/*******************************************************************************
*******************************************************************************/
static kern_return_t
get_symbols_64(KXLDState *state, KXLDDict *defined_symbols,
    KXLDDict *obsolete_symbols)
{
    kern_return_t rval = KERN_FAILURE;
    KXLDLinkStateHdr *hdr = (KXLDLinkStateHdr *) state->file;
    KXLDSymEntry64 *entry = NULL;
    const char *name = NULL;
    u_int i = 0;

    entry = (KXLDSymEntry64 *) (state->file + hdr->symoff);
    for (i = 0; i < hdr->nsyms; ++i, ++entry) {
        name = (const char *) (state->file + entry->nameoff);
        rval = kxld_dict_insert(defined_symbols, name, &entry->addr);
        require_noerr(rval, finish);

        if (entry->flags & KXLD_SYM_OBSOLETE) {
            rval = kxld_dict_insert(obsolete_symbols, name, &entry->addr);
            require_noerr(rval, finish);
        }
    }

    rval = KERN_SUCCESS;

finish:
    return rval;
}
#endif /* KXLD_USER_OR_LP64 */

/*******************************************************************************
*******************************************************************************/
u_int 
kxld_state_get_num_vtables(KXLDState *state)
{
    return state->vtables.nitems;
}

/*******************************************************************************
*******************************************************************************/
kern_return_t 
kxld_state_get_vtables(KXLDState *state, KXLDDict *patched_vtables)
{
    kern_return_t rval = KERN_FAILURE;
    KXLDVTable *vtable = NULL;
    u_int i = 0;

    check(state);
    check(patched_vtables);

    for (i = 0; i < state->vtables.nitems; ++i) {
        vtable = kxld_array_get_item(&state->vtables, i);
        rval = kxld_dict_insert(patched_vtables, vtable->name, vtable);
        require_noerr(rval, finish);
    }

    rval = KERN_SUCCESS;

finish:
    return rval;
}

/*******************************************************************************
*******************************************************************************/
void
kxld_state_get_cputype(const KXLDState *state, cpu_type_t *cputype, 
    cpu_subtype_t *cpusubtype)
{
    KXLDLinkStateHdr *hdr = (KXLDLinkStateHdr *) state->file;
    
    check(state);
    check(cputype);
    check(cpusubtype);

    *cputype = hdr->cputype;
    *cpusubtype = hdr->cpusubtype;
}

/*******************************************************************************
*******************************************************************************/
kern_return_t 
kxld_state_export_kext_to_file(KXLDKext *kext, u_char **file, u_long *filesize, 
    KXLDDict *strings, KXLDArray *tmps)
{
    kern_return_t rval = KERN_FAILURE;
    KXLDSymtabIterator iter;
    const KXLDSymtab *symtab = NULL;
    const KXLDArray *vtables = NULL;
    const KXLDVTable *vtable = NULL;
    u_int nsyms = 0;
    u_int nsymentries = 0;
    u_int i = 0;
    u_long strsize = 0;

    check(kext);
    check(file);
    check(tmps);

    bzero(&iter, sizeof(iter));

    /* Get the vtables and symbol tables from the kext */

    kxld_kext_get_vtables(kext, &vtables);
    symtab = kxld_kext_get_symtab(kext);
    require_action(symtab, finish, rval=KERN_FAILURE);

    /* Count the number of symentries we'll need in the linkstate */

    kxld_symtab_iterator_init(&iter, symtab, kxld_sym_is_exported, FALSE);

    nsyms = kxld_symtab_iterator_get_num_remaining(&iter);
    nsymentries = nsyms;
    for (i = 0; i < vtables->nitems; ++i) {
        vtable = kxld_array_get_item(vtables, i);
        nsymentries += vtable->entries.nitems;
    }

    /* Initialize the string index */

    rval = init_string_index(strings, tmps, &iter, vtables, nsymentries, 
        &strsize);
    require_noerr(rval, finish);

    /* Create the linkstate file */

    rval = create_link_state(file, filesize, kext, &iter, vtables, 
        strings, nsyms, nsymentries, strsize);
    require_noerr(rval, finish);

    /* Swap if necessary */

#if !KERNEL
    if (kxld_kext_target_needs_swap(kext)) unswap_link_state(*file);
#endif /* !KERNEL */
    
    rval = KERN_SUCCESS;

finish:
    return rval;
}

/*******************************************************************************
*******************************************************************************/
static kern_return_t
init_string_index(KXLDDict *strings, KXLDArray *tmps, KXLDSymtabIterator *iter,
    const KXLDArray *vtables, u_int nsymentries, u_long *_strsize)
{
    kern_return_t rval = KERN_SUCCESS;
    const KXLDSym *sym = NULL;
    const KXLDVTable *vtable = NULL;
    const KXLDVTableEntry *ventry = NULL;
    u_long strsize = 0;
    u_int tmpi = 0;
    u_int i = 0;
    u_int j = 0;

    check(strings);
    check(tmps);
    check(iter);
    check(vtables);
    check(_strsize);

    *_strsize = 0;

    /* Initialize the string dictionary and string offset array */
    
    rval = kxld_dict_init(strings, kxld_dict_string_hash, kxld_dict_string_cmp,
        nsymentries);
    require_noerr(rval, finish);

    rval = kxld_array_init(tmps, sizeof(u_long), nsymentries);
    require_noerr(rval, finish);

    /* Add all of the strings from the symbol table to the dictionary */

    kxld_symtab_iterator_reset(iter);
    while ((sym = kxld_symtab_iterator_get_next(iter))) {
        rval = add_string_to_index(strings, sym->name, tmps, &tmpi, &strsize);
        require_noerr(rval, finish);
    }

    /* Add all of the strings from the vtables entries to the dictionary */

    for (i = 0; i < vtables->nitems; ++i) {
        vtable = kxld_array_get_item(vtables, i);
        rval = add_string_to_index(strings, vtable->name, tmps, &tmpi, &strsize);
        require_noerr(rval, finish);

        for (j = 0; j < vtable->entries.nitems; ++j) {
            ventry = kxld_array_get_item(&vtable->entries, j);
            if (ventry->patched.name) {
                rval = add_string_to_index(strings, ventry->patched.name, tmps, 
                    &tmpi, &strsize);
                require_noerr(rval, finish);
            }
        }
    }

    *_strsize = strsize;
    rval = KERN_SUCCESS;

finish:
    return rval;
}

/*******************************************************************************
*******************************************************************************/
static kern_return_t
add_string_to_index(KXLDDict *strings, const char *str, KXLDArray *tmps,
    u_int *tmpi, u_long *stroff)
{
    kern_return_t rval = KERN_FAILURE;
    u_long *tmpp = NULL;

    if (!kxld_dict_find(strings, str)) {
        tmpp = kxld_array_get_item(tmps, (*tmpi)++);
        *tmpp = *stroff;
        
        rval = kxld_dict_insert(strings, str, tmpp);
        require_noerr(rval, finish);
    
        *stroff += strlen(str) + 1;
    }

    rval = KERN_SUCCESS;

finish:
    return rval;
}

/*******************************************************************************
*******************************************************************************/
static boolean_t
state_is_32_bit(KXLDLinkStateHdr *state)
{
    return kxld_is_32_bit(state->cputype);
}

/*******************************************************************************
*******************************************************************************/
static kern_return_t
create_link_state(u_char **_file, u_long *_filesize, const KXLDKext *kext,
    KXLDSymtabIterator *iter, const KXLDArray *vtables, KXLDDict *strings, 
    u_int nsyms, u_int nsymentries, u_long strsize)
{
    kern_return_t rval = KERN_SUCCESS;
    u_char *file = NULL;
    KXLDLinkStateHdr *hdr = NULL;
    KXLDDictIterator striter;
#if KXLD_USER_OR_OBJECT
    KXLDSectionName *dstsectname = NULL;
    KXLDSectionName *srcsectname = NULL;
    const KXLDArray *section_order = NULL;
    u_int i = 0;
#endif
    const char *name = NULL;
    char *dstname = NULL;
    u_long *stridx = 0;
    u_long hsize = 0;
    u_long dsize = 0;
    u_long filesize = 0;
    u_long hoff = 0;
    u_long doff = 0;
    u_long stroff = 0;

    check(_file);
    check(iter);
    check(vtables);
    check(strings);

    *_file = NULL;
    *_filesize = 0;

#if KXLD_USER_OR_OBJECT
    section_order = kxld_kext_get_section_order(kext);
#endif

    /* Calculate header and data size */

    hsize = sizeof(KXLDLinkStateHdr);
    hsize += vtables->nitems * sizeof(KXLDVTableHdr);
#if KXLD_USER_OR_OBJECT
    if (section_order) {
        hsize += section_order->nitems * sizeof(KXLDSectionName);
    }
#endif

    if (kxld_kext_is_32_bit(kext)) {
        dsize = nsymentries * sizeof(KXLDSymEntry32);
    } else {
        dsize = nsymentries * sizeof(KXLDSymEntry64);
    }

    filesize = hsize + dsize + strsize;

    hoff = 0;
    doff = hsize;
    stroff = hsize + dsize;

    /* Allocate the link state */

    file = kxld_alloc_pageable(filesize);
    require_action(file, finish, rval=KERN_RESOURCE_SHORTAGE);

    /* Initialize link state header */

    hdr = (KXLDLinkStateHdr *) file;
    hoff += sizeof(*hdr); 

    if (state_is_32_bit(hdr)) {
        hdr->magic = LINK_STATE_MAGIC;
    } else {
        hdr->magic = LINK_STATE_MAGIC_64;
    }
    hdr->version = LINK_STATE_VERSION;
    kxld_kext_get_cputype(kext, &hdr->cputype, &hdr->cpusubtype);
    hdr->nsects = 0;
    hdr->nvtables = vtables->nitems;
    hdr->nsyms = nsyms;

#if KXLD_USER_OR_OBJECT
    if (section_order) {
        hdr->nsects = section_order->nitems;
        hdr->sectoff = (uint32_t) hoff;

        dstsectname = (KXLDSectionName *) (file + hoff);
        hoff += section_order->nitems * sizeof(*dstsectname);

        for (i = 0; i < section_order->nitems; ++i, ++dstsectname) {
            srcsectname = kxld_array_get_item(section_order, i);
            memcpy(dstsectname, srcsectname, sizeof(*srcsectname));
        }
    }
#endif

    hdr->voff = (uint32_t) hoff;
    hdr->symoff = (uint32_t) doff;

    /* Copy strings */
    
    kxld_dict_iterator_init(&striter, strings);
    kxld_dict_iterator_get_next(&striter, (const void **) &name, (void **) &stridx);
    while (name) {
        *stridx += stroff;
        dstname = (char *) (file + *stridx);
        strlcpy(dstname, name, filesize - *stridx);
        kxld_dict_iterator_get_next(&striter, (const void **) &name, (void **) &stridx);
    }

    /* Copy symbols */

    KXLD_3264_FUNC(state_is_32_bit(hdr), rval,
        copy_symbols_32, copy_symbols_64,
        file, &doff, iter, strings);
    require_noerr(rval, finish);

    /* Copy vtables */

    KXLD_3264_FUNC(state_is_32_bit(hdr), rval,
        copy_vtables_32, copy_vtables_64,
        file, &hoff, &doff, vtables, strings);
    require_noerr(rval, finish);

    *_file = file;
    *_filesize = filesize;
    file = NULL;
    rval = KERN_SUCCESS;

finish:

    if (file) {
        kxld_page_free(file, filesize);
        file = NULL;
    }

    return rval;
}

#if KXLD_USER_OR_ILP32
/*******************************************************************************
*******************************************************************************/
static kern_return_t
copy_symbols_32(u_char *file, u_long *data_offset, KXLDSymtabIterator *iter, 
    const KXLDDict *strings)
{
    kern_return_t rval = KERN_FAILURE;
    KXLDSymEntry32 *symentry = NULL;
    const KXLDSym *sym = NULL;
    u_long *stridx = 0;

    kxld_symtab_iterator_reset(iter);
    while ((sym = kxld_symtab_iterator_get_next(iter))) {
        symentry = (KXLDSymEntry32 *) (file + *data_offset);
        stridx = kxld_dict_find(strings, sym->name);
        require_action(stridx, finish, rval=KERN_FAILURE);

        /* Initialize the symentry */

        symentry->nameoff = (uint32_t) *stridx;
        if (sym->predicates.is_thumb) {
            symentry->addr = (uint32_t) sym->link_addr | 1;
        } else {            
            symentry->addr = (uint32_t) sym->link_addr;
        }
        symentry->flags = 0;

        /* Set any flags */

        symentry->flags |= (kxld_sym_is_obsolete(sym)) ? KXLD_SYM_OBSOLETE : 0;

        *data_offset += sizeof(*symentry);
    }

    rval = KERN_SUCCESS;

finish:
    return rval;
}
#endif /* KXLD_USER_OR_ILP32 */

#if KXLD_USER_OR_LP64
/*******************************************************************************
*******************************************************************************/
static kern_return_t
copy_symbols_64(u_char *file, u_long *data_offset, KXLDSymtabIterator *iter, 
    const KXLDDict *strings)
{
    kern_return_t rval = KERN_FAILURE;
    KXLDSymEntry64 *symentry = NULL;
    const KXLDSym *sym = NULL;
    u_long *stridx = 0;

    kxld_symtab_iterator_reset(iter);
    while ((sym = kxld_symtab_iterator_get_next(iter))) {
        symentry = (KXLDSymEntry64 *) (file + *data_offset);
        stridx = kxld_dict_find(strings, sym->name);
        require_action(stridx, finish, rval=KERN_FAILURE);

        /* Initialize the symentry */

        symentry->nameoff = (uint32_t) *stridx;
        symentry->addr = (uint64_t) sym->link_addr;
        symentry->flags = 0;

        /* Set any flags */

        symentry->flags |= (kxld_sym_is_obsolete(sym)) ? KXLD_SYM_OBSOLETE : 0;

        *data_offset += sizeof(*symentry);
    }

    rval = KERN_SUCCESS;

finish:
    return rval;
}
#endif /* KXLD_USER_OR_LP64 */

#if KXLD_USER_OR_ILP32
/*******************************************************************************
*******************************************************************************/
static kern_return_t
copy_vtables_32(u_char *file, u_long *header_offset, u_long *data_offset,
    const KXLDArray *vtables, const KXLDDict *strings)
{
    kern_return_t rval = KERN_FAILURE;
    KXLDVTable *vtable = NULL;
    KXLDVTableHdr *vhdr = NULL;
    KXLDVTableEntry *ventry = NULL;
    KXLDSymEntry32 *symentry = NULL;
    u_long *stridx = 0;
    u_int i = 0;
    u_int j = 0;

    for (i = 0; i < vtables->nitems; ++i) {
        vtable = kxld_array_get_item(vtables, i);
        stridx = kxld_dict_find(strings, vtable->name);
        require_action(stridx, finish, rval=KERN_FAILURE);

        vhdr = (KXLDVTableHdr *) (file + *header_offset);
        vhdr->nameoff = (uint32_t) *stridx;
        vhdr->nentries = vtable->entries.nitems;
        vhdr->vtableoff = (uint32_t) (*data_offset);

        *header_offset += sizeof(*vhdr);

        for(j = 0; j < vtable->entries.nitems; ++j) {

            ventry = kxld_array_get_item(&vtable->entries, j);
            symentry = (KXLDSymEntry32 *) (file + *data_offset);
            
            if (ventry->patched.name) {
                stridx = kxld_dict_find(strings, ventry->patched.name);
                require_action(stridx, finish, rval=KERN_FAILURE);

                symentry->nameoff = (uint32_t) *stridx;
                symentry->addr = (uint32_t) ventry->patched.addr;
            } else {
                symentry->nameoff = 0;
                symentry->addr = 0;
            }

            *data_offset += sizeof(*symentry);
        }
    }

    rval = KERN_SUCCESS;

finish:
    return rval;
}
#endif /* KXLD_USER_OR_ILP32 */

#if KXLD_USER_OR_LP64
/*******************************************************************************
*******************************************************************************/
static kern_return_t
copy_vtables_64(u_char *file, u_long *header_offset, u_long *data_offset,
    const KXLDArray *vtables, const KXLDDict *strings)
{
    kern_return_t rval = KERN_FAILURE;
    KXLDVTable *vtable = NULL;
    KXLDVTableHdr *vhdr = NULL;
    KXLDVTableEntry *ventry = NULL;
    KXLDSymEntry64 *symentry = NULL;
    u_long *stridx = 0;
    u_int i = 0;
    u_int j = 0;

    for (i = 0; i < vtables->nitems; ++i) {
        vtable = kxld_array_get_item(vtables, i);
        stridx = kxld_dict_find(strings, vtable->name);
        require_action(stridx, finish, rval=KERN_FAILURE);

        vhdr = (KXLDVTableHdr *) (file + *header_offset);
        vhdr->nameoff = (uint32_t) *stridx;
        vhdr->nentries = vtable->entries.nitems;
        vhdr->vtableoff = (uint32_t) (*data_offset);

        *header_offset += sizeof(*vhdr);

        for(j = 0; j < vtable->entries.nitems; ++j) {

            ventry = kxld_array_get_item(&vtable->entries, j);
            symentry = (KXLDSymEntry64 *) (file + *data_offset);
            
            if (ventry->patched.name) {
                stridx = kxld_dict_find(strings, ventry->patched.name);
                require_action(stridx, finish, rval=KERN_FAILURE);

                symentry->nameoff = (uint32_t) *stridx;
                symentry->addr = (uint64_t) ventry->patched.addr;
            } else {
                symentry->nameoff = 0;
                symentry->addr = 0;
            }

            *data_offset += sizeof(*symentry);
        }
    }

    rval = KERN_SUCCESS;

finish:
    return rval;
}
#endif /* KXLD_USER_OR_LP64 */

#if !KERNEL
/*******************************************************************************
*******************************************************************************/
static boolean_t
swap_link_state(u_char *state)
{
    KXLDLinkStateHdr *state_hdr = (KXLDLinkStateHdr *) state;

    if (state_hdr->magic == CIGAM_ETATS_KNIL) {
        swap_link_state_32(state);
        return TRUE;
    } else if (state_hdr->magic == CIGAM_ETATS_KNIL_64) {
        swap_link_state_64(state);
        return TRUE;
    }

    return FALSE;
}

/*******************************************************************************
*******************************************************************************/
static void
swap_link_state_32(u_char *state)
{
    KXLDLinkStateHdr *state_hdr = NULL;
    KXLDVTableHdr *vtable_hdr = NULL;
    KXLDSymEntry32 *entry = NULL;
    u_int i = 0;
    u_int j = 0;
    
    state_hdr = (KXLDLinkStateHdr *) state;

    if (state_hdr->magic != CIGAM_ETATS_KNIL) return;

    /* Swap the header */
    swap_state_hdr(state_hdr);

    /* Swap the symbols */
    entry = (KXLDSymEntry32 *) (state + state_hdr->symoff);
    for (i = 0; i < state_hdr->nsyms; ++i, ++entry) {
        swap_sym_entry_32(entry);
    }

    /* Swap the vtable headers and entries */
    vtable_hdr = (KXLDVTableHdr *) (state + state_hdr->voff);
    for (i = 0; i < state_hdr->nvtables; ++i, ++vtable_hdr) {
        swap_vtable_hdr(vtable_hdr);

        entry = (KXLDSymEntry32 *) (state + vtable_hdr->vtableoff);
        for (j = 0; j < vtable_hdr->nentries; ++j, ++entry) {
            swap_sym_entry_32(entry);
        }
    }
}

/*******************************************************************************
*******************************************************************************/
static void
swap_link_state_64(u_char *state)
{
    KXLDLinkStateHdr *state_hdr = NULL;
    KXLDVTableHdr *vtable_hdr = NULL;
    KXLDSymEntry64 *entry = NULL;
    u_int i = 0;
    u_int j = 0;
    
    state_hdr = (KXLDLinkStateHdr *) state;

    if (state_hdr->magic != CIGAM_ETATS_KNIL_64) return;

    /* Swap the header */
    swap_state_hdr(state_hdr);

    /* Swap the symbols */
    entry = (KXLDSymEntry64 *) (state + state_hdr->symoff);
    for (i = 0; i < state_hdr->nsyms; ++i, ++entry) {
        swap_sym_entry_64(entry);
    }

    /* Swap the vtable headers and entries */
    vtable_hdr = (KXLDVTableHdr *) (state + state_hdr->voff);
    for (i = 0; i < state_hdr->nvtables; ++i, ++vtable_hdr) {
        swap_vtable_hdr(vtable_hdr);

        entry = (KXLDSymEntry64 *) (state + vtable_hdr->vtableoff);
        for (j = 0; j < vtable_hdr->nentries; ++j, ++entry) {
            swap_sym_entry_64(entry);
        }
    }
}

/*******************************************************************************
*******************************************************************************/
static boolean_t
unswap_link_state(u_char *state)
{
    KXLDLinkStateHdr *state_hdr = (KXLDLinkStateHdr *) state;

    if (state_hdr->magic == LINK_STATE_MAGIC) {
        unswap_link_state_32(state);
        return TRUE;
    } else if (state_hdr->magic == LINK_STATE_MAGIC_64) {
        unswap_link_state_64(state);
        return TRUE;
    }

    return FALSE;
}

/*******************************************************************************
*******************************************************************************/
static void
unswap_link_state_32(u_char *state)
{
    KXLDLinkStateHdr *state_hdr = NULL;
    KXLDVTableHdr *vtable_hdr = NULL;
    KXLDSymEntry32 *entry = NULL;
    u_int i = 0;
    u_int j = 0;
    
    state_hdr = (KXLDLinkStateHdr *) state;

    if (state_hdr->magic != LINK_STATE_MAGIC) return;

    /* Unswap the vtables and their headers */
    vtable_hdr = (KXLDVTableHdr *) (state + state_hdr->voff);
    for (i = 0; i < state_hdr->nvtables; ++i, ++vtable_hdr) {
        entry = (KXLDSymEntry32 *) (state + vtable_hdr->vtableoff);
        for (j = 0; j < vtable_hdr->nentries; ++j, ++entry) {
            swap_sym_entry_32(entry);
        }

        swap_vtable_hdr(vtable_hdr);
    }

    /* Unswap the symbols themselves */
    entry = (KXLDSymEntry32 *) (state + state_hdr->symoff);
    for (i = 0; i < state_hdr->nsyms; ++i, ++entry) {
        swap_sym_entry_32(entry);
    }

    /* Unswap the header */
    swap_state_hdr(state_hdr);
}

/*******************************************************************************
*******************************************************************************/
static void
unswap_link_state_64(u_char *state)
{
    KXLDLinkStateHdr *state_hdr = NULL;
    KXLDVTableHdr *vtable_hdr = NULL;
    KXLDSymEntry64 *entry = NULL;
    u_int i = 0;
    u_int j = 0;
    
    state_hdr = (KXLDLinkStateHdr *) state;

    if (state_hdr->magic != LINK_STATE_MAGIC_64) return;

    /* Unswap the vtables and their headers */
    vtable_hdr = (KXLDVTableHdr *) (state + state_hdr->voff);
    for (i = 0; i < state_hdr->nvtables; ++i, ++vtable_hdr) {
        entry = (KXLDSymEntry64 *) (state + vtable_hdr->vtableoff);
        for (j = 0; j < vtable_hdr->nentries; ++j, ++entry) {
            swap_sym_entry_64(entry);
        }

        swap_vtable_hdr(vtable_hdr);
    }

    /* Unswap the symbols themselves */
    entry = (KXLDSymEntry64 *) (state + state_hdr->symoff);
    for (i = 0; i < state_hdr->nsyms; ++i, ++entry) {
        swap_sym_entry_64(entry);
    }

    /* Unswap the header */
    swap_state_hdr(state_hdr);
}

/*******************************************************************************
*******************************************************************************/
static void
swap_state_hdr(KXLDLinkStateHdr *state_hdr)
{
    state_hdr->magic = OSSwapInt32(state_hdr->magic);
    state_hdr->version = OSSwapInt32(state_hdr->version);
    state_hdr->cputype = OSSwapInt32(state_hdr->cputype);
    state_hdr->cpusubtype = OSSwapInt32(state_hdr->cpusubtype);
    state_hdr->nsects = OSSwapInt32(state_hdr->nsects);
    state_hdr->sectoff = OSSwapInt32(state_hdr->sectoff);
    state_hdr->nvtables = OSSwapInt32(state_hdr->nvtables);
    state_hdr->voff = OSSwapInt32(state_hdr->voff);
    state_hdr->nsyms = OSSwapInt32(state_hdr->nsyms);
    state_hdr->symoff = OSSwapInt32(state_hdr->symoff);
}

/*******************************************************************************
*******************************************************************************/
static void
swap_vtable_hdr(KXLDVTableHdr *vtable_hdr)
{
    vtable_hdr->nameoff = OSSwapInt32(vtable_hdr->nameoff);
    vtable_hdr->vtableoff = OSSwapInt32(vtable_hdr->vtableoff);
    vtable_hdr->nentries = OSSwapInt32(vtable_hdr->nentries);
}

/*******************************************************************************
*******************************************************************************/
static void
swap_sym_entry_32(KXLDSymEntry32 *entry)
{
    entry->nameoff = OSSwapInt32(entry->nameoff);
    entry->addr = OSSwapInt32(entry->addr);
}

/*******************************************************************************
*******************************************************************************/
static void
swap_sym_entry_64(KXLDSymEntry64 *entry)
{
    entry->nameoff = OSSwapInt32(entry->nameoff);
    entry->addr = OSSwapInt64(entry->addr);
}
#endif /* !KERNEL */