]> git.saurik.com Git - apple/xnu.git/blobdiff - libkern/kxld/kxld_reloc.c
xnu-2782.1.97.tar.gz
[apple/xnu.git] / libkern / kxld / kxld_reloc.c
index c781d6dc28241d1721adb38fa082c6ae49e7e85e..41e899eac27dad3d8667d730ad499bff90dc177d 100644 (file)
  */
 #include <string.h>
 #include <mach/boolean.h>
-#include <mach/machine.h>
 #include <sys/types.h>
 
 #if KERNEL
     #include <libkern/libkern.h>
+    #include <mach/machine.h>
 #else
-    #include <libkern/OSByteOrder.h>
     #include <stdlib.h>
+    #include <libkern/OSByteOrder.h>
+
+    /* Get machine.h from the kernel source so we can support all platforms
+     * that the kernel supports. Otherwise we're at the mercy of the host.
+     */
+    #include "../../osfmk/mach/machine.h"
 #endif
 
 #define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld"
 #include <AssertMacros.h>
 
 #include "kxld_array.h"
+#include "kxld_demangle.h"
+#include "kxld_dict.h"
 #include "kxld_reloc.h"
 #include "kxld_sect.h"
 #include "kxld_seg.h"
 #include "kxld_sym.h"
 #include "kxld_symtab.h"
 #include "kxld_util.h"
+#include "kxld_vtable.h"
+
+#if KXLD_PIC_KEXTS
+/* This will try to pull in mach/machine.h, so it has to come after the
+ * explicit include above.
+ */
+#include <mach-o/loader.h>
+#endif
 
 /* include target-specific relocation prototypes */
 #include <mach-o/reloc.h>
-#if KXLD_USER_OR_PPC
-#include <mach-o/ppc/reloc.h>
-#endif
 #if KXLD_USER_OR_X86_64
 #include <mach-o/x86_64/reloc.h>
 #endif
 #if KXLD_USER_OR_ARM
 #include <mach-o/arm/reloc.h>
 #endif
+#if KXLD_USER_OR_ARM64
+#include <mach-o/arm64/reloc.h>
+#endif
 
 #define KXLD_TARGET_NONE        (u_int) 0x0
 #define KXLD_TARGET_VALUE       (u_int) 0x1
 #if KXLD_USER_OR_I386
 static boolean_t generic_reloc_has_pair(u_int _type) 
     __attribute__((const));
-static boolean_t generic_reloc_is_pair(u_int _type, u_int _prev_type)
+static u_int generic_reloc_get_pair_type(u_int _prev_type)
     __attribute__((const));
 static boolean_t generic_reloc_has_got(u_int _type)
     __attribute__((const));
-static kern_return_t generic_process_reloc(u_char *instruction, u_int length, 
-    u_int pcrel, kxld_addr_t base_pc, kxld_addr_t link_pc, kxld_addr_t link_disp,
-    u_int type, kxld_addr_t target, kxld_addr_t pair_target, boolean_t swap);
+static kern_return_t generic_process_reloc(const KXLDRelocator *relocator,
+    u_char *instruction, u_int length, u_int pcrel, kxld_addr_t base_pc, 
+    kxld_addr_t link_pc, kxld_addr_t link_disp, u_int type, kxld_addr_t target, 
+    kxld_addr_t pair_target, boolean_t swap);
 #endif /* KXLD_USER_OR_I386 */
 
-#if KXLD_USER_OR_PPC 
-static boolean_t ppc_reloc_has_pair(u_int _type) 
-    __attribute__((const));
-static boolean_t ppc_reloc_is_pair(u_int _type, u_int _prev_type) 
-    __attribute__((const));
-static boolean_t ppc_reloc_has_got(u_int _type)
-    __attribute__((const));
-static kern_return_t ppc_process_reloc(u_char *instruction, u_int length, 
-    u_int pcrel, kxld_addr_t base_pc, kxld_addr_t link_pc, kxld_addr_t link_disp,
-    u_int type, kxld_addr_t target, kxld_addr_t pair_target, boolean_t swap);
-#endif /* KXLD_USER_OR_PPC */
-
 #if KXLD_USER_OR_X86_64 
 static boolean_t x86_64_reloc_has_pair(u_int _type) 
     __attribute__((const));
-static boolean_t x86_64_reloc_is_pair(u_int _type, u_int _prev_type) 
+static u_int x86_64_reloc_get_pair_type(u_int _prev_type) 
     __attribute__((const));
 static boolean_t x86_64_reloc_has_got(u_int _type)
     __attribute__((const));
-static kern_return_t x86_64_process_reloc(u_char *instruction, u_int length, 
-    u_int pcrel, kxld_addr_t base_pc, kxld_addr_t link_pc, kxld_addr_t link_disp,
-    u_int type, kxld_addr_t target, kxld_addr_t pair_target, boolean_t swap);
+static kern_return_t x86_64_process_reloc(const KXLDRelocator *relocator, 
+    u_char *instruction, u_int length, u_int pcrel, kxld_addr_t base_pc, 
+    kxld_addr_t link_pc, kxld_addr_t link_disp, u_int type, kxld_addr_t target, 
+    kxld_addr_t pair_target, boolean_t swap);
 static kern_return_t calculate_displacement_x86_64(uint64_t target, 
     uint64_t adjustment, int32_t *instr32);
 #endif /* KXLD_USER_OR_X86_64 */
@@ -136,23 +141,37 @@ static kern_return_t calculate_displacement_x86_64(uint64_t target,
 #if KXLD_USER_OR_ARM
 static boolean_t arm_reloc_has_pair(u_int _type) 
     __attribute__((const));
-static boolean_t arm_reloc_is_pair(u_int _type, u_int _prev_type) 
+static u_int arm_reloc_get_pair_type(u_int _prev_type) 
     __attribute__((const));
 static boolean_t arm_reloc_has_got(u_int _type)
     __attribute__((const));
-static kern_return_t arm_process_reloc(u_char *instruction, u_int length, 
-    u_int pcrel, kxld_addr_t base_pc, kxld_addr_t link_pc, kxld_addr_t link_disp,
-    u_int type, kxld_addr_t target, kxld_addr_t pair_target, boolean_t swap);
+static kern_return_t arm_process_reloc(const KXLDRelocator *relocator, 
+    u_char *instruction, u_int length, u_int pcrel, kxld_addr_t base_pc, 
+    kxld_addr_t link_pc, kxld_addr_t link_disp, u_int type, kxld_addr_t target, 
+    kxld_addr_t pair_target, boolean_t swap);
 #endif /* KXLD_USER_OR_ARM */
 
+#if KXLD_USER_OR_ARM64
+static boolean_t arm64_reloc_has_pair(u_int _type) 
+    __attribute__((const));
+static u_int arm64_reloc_get_pair_type(u_int _prev_type) 
+    __attribute__((const));
+static boolean_t arm64_reloc_has_got(u_int _type)
+    __attribute__((const));
+static kern_return_t arm64_process_reloc(const KXLDRelocator *relocator, 
+    u_char *instruction, u_int length, u_int pcrel, kxld_addr_t base_pc, 
+    kxld_addr_t link_pc, kxld_addr_t link_disp, u_int type, kxld_addr_t target, 
+    kxld_addr_t pair_target, boolean_t swap);
+#endif /* KXLD_USER_OR_ARM64 */
+
 #if KXLD_USER_OR_ILP32
-static kxld_addr_t get_pointer_at_addr_32(u_char *data, u_long offset,
-    const KXLDRelocator *relocator __unused)
+static kxld_addr_t get_pointer_at_addr_32(const KXLDRelocator *relocator, 
+    const u_char *data, u_long offset)
     __attribute__((pure, nonnull));
 #endif /* KXLD_USER_OR_ILP32 */
 #if KXLD_USER_OR_LP64
-static kxld_addr_t get_pointer_at_addr_64(u_char *data, u_long offset,
-    const KXLDRelocator *relocator __unused)
+static kxld_addr_t get_pointer_at_addr_64(const KXLDRelocator *relocator, 
+    const u_char *data, u_long offset)
     __attribute__((pure, nonnull));
 #endif /* KXLD_USER_OR_LP64 */
 
@@ -160,59 +179,82 @@ static u_int count_relocatable_relocs(const KXLDRelocator *relocator,
     const struct relocation_info *relocs, u_int nrelocs)
     __attribute__((pure));
 
-static kern_return_t calculate_targets(kxld_addr_t *_target, 
-    kxld_addr_t *_pair_target, const KXLDReloc *reloc, 
-    const KXLDArray *sectarray, const KXLDSymtab *symtab);
+static kern_return_t calculate_targets(KXLDRelocator *relocator, 
+    kxld_addr_t *_target, kxld_addr_t *_pair_target, const KXLDReloc *reloc);
+
+static kxld_addr_t align_raw_function_address(const KXLDRelocator *relocator, 
+    kxld_addr_t value);
+
 static kern_return_t get_target_by_address_lookup(kxld_addr_t *target, 
     kxld_addr_t addr, const KXLDArray *sectarray);
 
+static kern_return_t check_for_direct_pure_virtual_call(
+    const KXLDRelocator *relocator, u_long offset);
+
+#if KXLD_PIC_KEXTS
+static u_long get_macho_data_size_for_array(const KXLDArray *relocs);
+
+static kern_return_t export_macho_for_array(const KXLDRelocator *relocator,
+    const KXLDArray *relocs, struct relocation_info **dstp);
+#endif /* KXLD_PIC_KEXTS */
+
 /*******************************************************************************
 *******************************************************************************/
 kern_return_t 
-kxld_relocator_init(KXLDRelocator *relocator, cpu_type_t cputype, 
+kxld_relocator_init(KXLDRelocator *relocator, u_char *file,
+    const KXLDSymtab *symtab, const KXLDArray *sectarray, 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_get_pair_type = generic_reloc_get_pair_type;
         relocator->reloc_has_got = generic_reloc_has_got;
         relocator->process_reloc = generic_process_reloc;
+        relocator->function_align = 0;
         relocator->is_32_bit = TRUE;
+        relocator->may_scatter = 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_get_pair_type = x86_64_reloc_get_pair_type;
         relocator->reloc_has_got = x86_64_reloc_has_got;
         relocator->process_reloc = x86_64_process_reloc;
+        relocator->function_align = 0;
         relocator->is_32_bit = FALSE;
+        relocator->may_scatter = 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_get_pair_type = arm_reloc_get_pair_type;
         relocator->reloc_has_got = arm_reloc_has_got;
         relocator->process_reloc = arm_process_reloc;
+        relocator->function_align = 1;
         relocator->is_32_bit = TRUE;
+        relocator->may_scatter = FALSE;
         break;
 #endif /* KXLD_USER_OR_ARM */
+#if KXLD_USER_OR_ARM64
+    case CPU_TYPE_ARM64:
+        relocator->reloc_has_pair = arm64_reloc_has_pair;
+        relocator->reloc_get_pair_type = arm64_reloc_get_pair_type;
+        relocator->reloc_has_got = arm64_reloc_has_got;
+        relocator->process_reloc = arm64_process_reloc;
+        relocator->function_align = 0;
+        relocator->is_32_bit = FALSE;
+        relocator->may_scatter = FALSE;
+        break;
+#endif /* KXLD_USER_OR_ARM64 */
+
     default:
         rval = KERN_FAILURE;
         kxld_log(kKxldLogLinking, kKxldLogErr,
@@ -220,6 +262,9 @@ kxld_relocator_init(KXLDRelocator *relocator, cpu_type_t cputype,
         goto finish;
     }
 
+    relocator->file = file;
+    relocator->symtab = symtab;
+    relocator->sectarray = sectarray;
     relocator->is_32_bit = kxld_is_32_bit(cputype);
     relocator->swap = swap;
 
@@ -238,8 +283,8 @@ kxld_reloc_create_macho(KXLDArray *relocarray, const KXLDRelocator *relocator,
     kern_return_t rval = KERN_FAILURE;
     KXLDReloc *reloc = NULL;
     u_int nrelocs = 0;
-    const struct relocation_info *src = NULL, *prev_src = NULL;
-    const struct scattered_relocation_info *scatsrc = NULL, *prev_scatsrc = NULL;
+    const struct relocation_info *src = NULL;
+    const struct scattered_relocation_info *scatsrc = NULL;
     u_int i = 0;
     u_int reloc_index = 0;
 
@@ -272,8 +317,8 @@ kxld_reloc_create_macho(KXLDArray *relocarray, const KXLDRelocator *relocator,
              * symbols.
              */
 
-            if (!(src->r_address & R_SCATTERED) && !(src->r_extern) && 
-                (R_ABS == src->r_symbolnum))
+            if (!(relocator->may_scatter && (src->r_address & R_SCATTERED)) &&
+                !(src->r_extern) && (R_ABS == src->r_symbolnum))
             {
                 continue;
             }
@@ -285,7 +330,7 @@ kxld_reloc_create_macho(KXLDArray *relocarray, const KXLDRelocator *relocator,
              *  Extern -> Symbolnum by Index
              */
             reloc = kxld_array_get_item(relocarray, reloc_index++);
-            if (src->r_address & R_SCATTERED) {
+            if (relocator->may_scatter && (src->r_address & R_SCATTERED)) {
                 reloc->address = scatsrc->r_address;
                 reloc->pcrel = scatsrc->r_pcrel;
                 reloc->length = scatsrc->r_length;
@@ -313,21 +358,21 @@ kxld_reloc_create_macho(KXLDArray *relocarray, const KXLDRelocator *relocator,
                 ++i;
                 require_action(i < nsrcs, finish, rval=KERN_FAILURE);
 
-                prev_src = src;
                 src = srcs + i;
-                prev_scatsrc = (const struct scattered_relocation_info *) prev_src;
                 scatsrc = (const struct scattered_relocation_info *) src;
                  
-                if (src->r_address & R_SCATTERED) {
-                    require_action(relocator->reloc_is_pair(
-                        scatsrc->r_type, reloc->reloc_type), 
+                if (relocator->may_scatter && (src->r_address & R_SCATTERED)) {
+                    require_action(relocator->reloc_get_pair_type(
+                        reloc->reloc_type) == scatsrc->r_type,
                         finish, rval=KERN_FAILURE);
+                    reloc->pair_address= scatsrc->r_address;
                     reloc->pair_target = scatsrc->r_value;
                     reloc->pair_target_type = KXLD_TARGET_LOOKUP;
                 } else {
-                    require_action(relocator->reloc_is_pair(src->r_type, 
-                        reloc->reloc_type), finish, rval=KERN_FAILURE);
-
+                    require_action(relocator->reloc_get_pair_type(
+                        reloc->reloc_type) == scatsrc->r_type,
+                        finish, rval=KERN_FAILURE);
+                    reloc->pair_address = scatsrc->r_address;
                     if (src->r_extern) {
                         reloc->pair_target = src->r_symbolnum;
                         reloc->pair_target_type = KXLD_TARGET_SYMBOLNUM;
@@ -365,7 +410,6 @@ count_relocatable_relocs(const KXLDRelocator *relocator,
 {
     u_int num_nonpair_relocs = 0;
     u_int i = 0;
-    u_int prev_type = 0;
     const struct relocation_info *reloc = NULL;
     const struct scattered_relocation_info *sreloc = NULL;
 
@@ -375,7 +419,6 @@ count_relocatable_relocs(const KXLDRelocator *relocator,
     /* Loop over all of the relocation entries */
 
     num_nonpair_relocs = 1;
-    prev_type = relocs->r_type;
     for (i = 1; i < nrelocs; ++i) {
         reloc = relocs + i;
 
@@ -386,18 +429,14 @@ count_relocatable_relocs(const KXLDRelocator *relocator,
             sreloc = (const struct scattered_relocation_info *) reloc;
 
             num_nonpair_relocs += 
-                (!relocator->reloc_is_pair(sreloc->r_type, prev_type));
-
-            prev_type = sreloc->r_type;
+                !relocator->reloc_has_pair(sreloc->r_type);
         } else {
             /* A normal relocation entry is relocatable if it is not a pair and
              * if it is not a section-based relocation for an absolute symbol.
              */
             num_nonpair_relocs += 
-                !(relocator->reloc_is_pair(reloc->r_type, prev_type)
+                !(relocator->reloc_has_pair(reloc->r_type)
                  || (0 == reloc->r_extern && R_ABS == reloc->r_symbolnum));
-
-            prev_type = reloc->r_type;
         }
 
     }
@@ -425,13 +464,13 @@ kxld_relocator_has_pair(const KXLDRelocator *relocator, u_int r_type)
 
 /*******************************************************************************
 *******************************************************************************/
-boolean_
-kxld_relocator_is_pair(const KXLDRelocator *relocator, u_int r_type, 
+u_in
+kxld_relocator_get_pair_type(const KXLDRelocator *relocator,
     u_int prev_r_type)
 {
     check(relocator);
 
-    return relocator->reloc_is_pair(r_type, prev_r_type);
+    return relocator->reloc_get_pair_type(prev_r_type);
 }
 
 /*******************************************************************************
@@ -447,25 +486,23 @@ kxld_relocator_has_got(const KXLDRelocator *relocator, u_int r_type)
 /*******************************************************************************
 *******************************************************************************/
 KXLDSym *
-kxld_reloc_get_symbol(const KXLDRelocator *relocator, const KXLDReloc *reloc, 
-    u_char *data, const KXLDSymtab *symtab)
+kxld_reloc_get_symbol(const KXLDRelocator *relocator, const KXLDReloc *reloc,
+    const u_char *data)
 {
     KXLDSym *sym = NULL;
     kxld_addr_t value = 0;
 
     check(reloc);
-    check(symtab);
 
     switch (reloc->target_type) {
     case KXLD_TARGET_SYMBOLNUM:
-        sym = kxld_symtab_get_symbol_by_index(symtab, reloc->target);
+        sym = kxld_symtab_get_symbol_by_index(relocator->symtab, reloc->target);
         break;
     case KXLD_TARGET_SECTNUM:
-        if (data) {
-            KXLD_3264_FUNC(relocator->is_32_bit, value,
-                get_pointer_at_addr_32, get_pointer_at_addr_64,
-                data, reloc->address, relocator);
-            sym = kxld_symtab_get_cxx_symbol_by_value(symtab, value);           
+        if (data) { 
+            value = kxld_relocator_get_pointer_at_addr(relocator, data, 
+                reloc->address);
+            sym = kxld_symtab_get_cxx_symbol_by_value(relocator->symtab, value);           
         }
         break;
     default:
@@ -521,26 +558,115 @@ finish:
     return reloc;
 }
 
+#if KXLD_PIC_KEXTS
+/*******************************************************************************
+*******************************************************************************/
+u_long
+kxld_reloc_get_macho_header_size()
+{
+    return sizeof(struct dysymtab_command);
+}
+
+/*******************************************************************************
+*******************************************************************************/
+u_long
+kxld_reloc_get_macho_data_size(const KXLDArray *locrelocs,
+    const KXLDArray *extrelocs)
+{
+    u_long    rval = 0;
+
+    rval += get_macho_data_size_for_array(locrelocs);
+    rval += get_macho_data_size_for_array(extrelocs);
+
+    return (rval);
+}
+
+/*******************************************************************************
+*******************************************************************************/
+kern_return_t
+kxld_reloc_export_macho(const KXLDRelocator *relocator,
+    const KXLDArray *locrelocs, const KXLDArray *extrelocs,
+    u_char *buf, u_long *header_offset, u_long header_size,
+    u_long *data_offset, u_long size)
+{
+    kern_return_t rval = KERN_FAILURE;
+    struct dysymtab_command *dysymtabhdr = NULL;
+    struct relocation_info *start = NULL;
+    struct relocation_info *dst = NULL;
+    u_long count = 0;
+    u_long data_size = 0;
+
+    check(locrelocs);
+    check(extrelocs);
+    check(buf);
+    check(header_offset);
+    check(data_offset);
+
+    require_action(sizeof(*dysymtabhdr) <= header_size - *header_offset, finish, rval=KERN_FAILURE);
+    dysymtabhdr = (struct dysymtab_command *) ((void *) (buf + *header_offset));
+    *header_offset += sizeof(*dysymtabhdr);
+
+    data_size = kxld_reloc_get_macho_data_size(locrelocs, extrelocs);
+    require_action((*data_offset + data_size) <= size, finish, rval=KERN_FAILURE);
+    
+    start = dst = (struct relocation_info *) ((void *) (buf + *data_offset));
+
+    rval = export_macho_for_array(relocator, locrelocs, &dst);
+    require_noerr(rval, finish);
+    
+    rval = export_macho_for_array(relocator, extrelocs, &dst);
+    require_noerr(rval, finish);
+
+    count = dst - start;
+
+    memset(dysymtabhdr, 0, sizeof(*dysymtabhdr));
+    dysymtabhdr->cmd = LC_DYSYMTAB;
+    dysymtabhdr->cmdsize = (uint32_t) sizeof(*dysymtabhdr);
+    dysymtabhdr->locreloff = (uint32_t) *data_offset;
+    dysymtabhdr->nlocrel = (uint32_t) count;
+    
+    *data_offset += count * sizeof(struct relocation_info);
+
+    rval = KERN_SUCCESS;
+finish:
+    return rval;
+}
+#endif /* KXLD_PIC_KEXTS */
+
+/*******************************************************************************
+*******************************************************************************/
+kxld_addr_t
+kxld_relocator_get_pointer_at_addr(const KXLDRelocator *relocator,
+    const u_char *data, u_long offset)
+{
+    kxld_addr_t value;
+
+    KXLD_3264_FUNC(relocator->is_32_bit, value,
+        get_pointer_at_addr_32, get_pointer_at_addr_64,
+        relocator, data, offset);
+
+    return value;
+}
+
 #if KXLD_USER_OR_ILP32
 /*******************************************************************************
 *******************************************************************************/
 static kxld_addr_t
-get_pointer_at_addr_32(u_char *data, u_long offset,
-    const KXLDRelocator *relocator __unused)
+get_pointer_at_addr_32(const KXLDRelocator *relocator, 
+    const u_char *data, u_long offset)
 {
     uint32_t addr = 0;
     
     check(relocator);
-    check(data);
 
-    addr = *(uint32_t *) (data + offset);
+    addr = *(const uint32_t *) ((void *) (data + offset));
 #if !KERNEL
     if (relocator->swap) {
         addr = OSSwapInt32(addr);
     }
 #endif
 
-    return (kxld_addr_t) addr;
+    return align_raw_function_address(relocator, addr);
 }
 #endif /* KXLD_USER_OR_ILP32 */
 
@@ -548,31 +674,54 @@ get_pointer_at_addr_32(u_char *data, u_long offset,
 /*******************************************************************************
 *******************************************************************************/
 static kxld_addr_t
-get_pointer_at_addr_64(u_char *data, u_long offset,
-    const KXLDRelocator *relocator __unused)
+get_pointer_at_addr_64(const KXLDRelocator *relocator, 
+    const u_char *data, u_long offset)
 {
     uint64_t addr = 0;
     
     check(relocator);
-    check(data);
 
-    addr = *(uint64_t *) (data + offset);
+    addr = *(const uint64_t *) ((void *) (data + offset));
 #if !KERNEL
     if (relocator->swap) {
         addr = OSSwapInt64(addr);
     }
 #endif
 
-    return (kxld_addr_t) addr;
+    return align_raw_function_address(relocator, addr);
 }
 #endif /* KXLD_USER_OR_LP64 */
 
+/*******************************************************************************
+*******************************************************************************/
+void 
+kxld_relocator_set_vtables(KXLDRelocator *relocator, const KXLDDict *vtables)
+{
+    relocator->vtables = vtables;
+}
+
+/*******************************************************************************
+* When we're inspecting the raw binary and not the symbol table, value may
+* hold a THUMB address (with bit 0 set to 1) but the index will have the real
+* address (bit 0 set to 0). So if bit 0 is set here, we clear it. This only
+* impacts ARM for now, but it's implemented as a generic function alignment
+* mask.
+*******************************************************************************/
+static kxld_addr_t
+align_raw_function_address(const KXLDRelocator *relocator, kxld_addr_t value)
+{
+    if (relocator->function_align) { 
+        value &= ~((1ULL << relocator->function_align) - 1); 
+    }
+
+    return value; 
+}
+
 /*******************************************************************************
 *******************************************************************************/
 kern_return_t 
-kxld_relocator_process_sect_reloc(const KXLDRelocator *relocator,
-    const KXLDReloc *reloc, const struct kxld_sect *sect,
-    const KXLDArray *sectarray, const struct kxld_symtab *symtab)
+kxld_relocator_process_sect_reloc(KXLDRelocator *relocator,
+    const KXLDReloc *reloc, const KXLDSect *sect)
 {
     kern_return_t rval = KERN_FAILURE;
     u_char *instruction = NULL;
@@ -585,8 +734,6 @@ kxld_relocator_process_sect_reloc(const KXLDRelocator *relocator,
     check(relocator);
     check(reloc);
     check(sect);
-    check(sectarray);
-    check(symtab);
 
     /* Find the instruction */
 
@@ -594,7 +741,7 @@ kxld_relocator_process_sect_reloc(const KXLDRelocator *relocator,
 
     /* Calculate the target */
 
-    rval = calculate_targets(&target, &pair_target, reloc, sectarray, symtab);
+    rval = calculate_targets(relocator, &target, &pair_target, reloc);
     require_noerr(rval, finish);
 
     base_pc = reloc->address;
@@ -603,13 +750,14 @@ kxld_relocator_process_sect_reloc(const KXLDRelocator *relocator,
 
     /* Relocate */
 
-    rval = relocator->process_reloc(instruction, reloc->length, reloc->pcrel,
-        base_pc, link_pc, link_disp, reloc->reloc_type, target, pair_target, 
-        relocator->swap);
+    rval = relocator->process_reloc(relocator, instruction, reloc->length, 
+        reloc->pcrel, base_pc, link_pc, link_disp, reloc->reloc_type, target, 
+        pair_target, relocator->swap);
     require_noerr(rval, finish);
     
     /* Return */
 
+    relocator->current_vtable = NULL;
     rval = KERN_SUCCESS;
 
 finish:
@@ -637,9 +785,8 @@ finish:
 /*******************************************************************************
 *******************************************************************************/
 kern_return_t 
-kxld_relocator_process_table_reloc(const KXLDRelocator *relocator,
-    const KXLDReloc *reloc, const KXLDSeg *seg, u_char *file, 
-    const struct kxld_array *sectarray, const struct kxld_symtab *symtab)
+kxld_relocator_process_table_reloc(KXLDRelocator *relocator,
+    const KXLDReloc *reloc, const KXLDSeg *seg, kxld_addr_t link_addr)
 {
     kern_return_t rval = KERN_FAILURE;
     u_char *instruction = NULL;
@@ -647,36 +794,34 @@ kxld_relocator_process_table_reloc(const KXLDRelocator *relocator,
     kxld_addr_t pair_target = 0;
     kxld_addr_t base_pc = 0;
     kxld_addr_t link_pc = 0;
-    kxld_addr_t link_disp = 0;
+    u_long offset = 0;
 
     check(relocator);
     check(reloc);
-    check(file);
-    check(sectarray);
-    check(symtab);
 
     /* Find the instruction */
 
-    instruction = file + seg->fileoff + reloc->address;
+    offset = (u_long)(seg->fileoff + (reloc->address - seg->base_addr));
+    instruction = relocator->file + offset;
 
     /* Calculate the target */
 
-    rval = calculate_targets(&target, &pair_target, reloc, sectarray, symtab);
+    rval = calculate_targets(relocator, &target, &pair_target, reloc);
     require_noerr(rval, finish);
 
     base_pc = reloc->address;
-    link_pc = base_pc + seg->link_addr;
-    link_disp = seg->link_addr - seg->base_addr;
+    link_pc = base_pc + link_addr;
 
     /* Relocate */
 
-    rval = relocator->process_reloc(instruction, reloc->length, reloc->pcrel,
-        base_pc, link_pc, link_disp, reloc->reloc_type, target, pair_target, 
-        relocator->swap);
+    rval = relocator->process_reloc(relocator, instruction, reloc->length, 
+        reloc->pcrel, base_pc, link_pc, link_addr, reloc->reloc_type, target,
+        pair_target, relocator->swap);
     require_noerr(rval, finish);
     
     /* Return */
 
+    relocator->current_vtable = NULL;
     rval = KERN_SUCCESS;
 
 finish:
@@ -686,19 +831,19 @@ finish:
 /*******************************************************************************
 *******************************************************************************/
 static kern_return_t
-calculate_targets(kxld_addr_t *_target, kxld_addr_t *_pair_target,
-    const KXLDReloc *reloc, const KXLDArray *sectarray, const KXLDSymtab *symtab)
+calculate_targets(KXLDRelocator *relocator, kxld_addr_t *_target, 
+    kxld_addr_t *_pair_target, const KXLDReloc *reloc)
 {
     kern_return_t rval = KERN_FAILURE;
     const KXLDSect *sect = NULL;
     const KXLDSym *sym = NULL;
     kxld_addr_t target = 0;
     kxld_addr_t pair_target = 0;
+    char *demangled_name = NULL;
+    size_t demangled_length = 0;
 
     check(_target);
     check(_pair_target);
-    check(sectarray);
-    check(symtab);
     *_target = 0;
     *_pair_target = 0;
 
@@ -711,12 +856,13 @@ calculate_targets(kxld_addr_t *_target, kxld_addr_t *_pair_target,
             reloc->pair_target_type == KXLD_TARGET_VALUE,
             finish, rval=KERN_FAILURE);
 
-        rval = get_target_by_address_lookup(&target, reloc->target, sectarray);
+        rval = get_target_by_address_lookup(&target, reloc->target, 
+            relocator->sectarray);
         require_noerr(rval, finish);
 
         if (reloc->pair_target_type == KXLD_TARGET_LOOKUP) {
             rval = get_target_by_address_lookup(&pair_target,
-                reloc->pair_target, sectarray);
+                reloc->pair_target, relocator->sectarray);
             require_noerr(rval, finish);
         } else if (reloc->pair_target_type == KXLD_TARGET_VALUE) {
             pair_target = reloc->pair_target;
@@ -728,7 +874,7 @@ calculate_targets(kxld_addr_t *_target, kxld_addr_t *_pair_target,
             finish, rval=KERN_FAILURE);
 
         /* Get the target's section by section number */
-        sect = kxld_array_get_item(sectarray, reloc->target);
+        sect = kxld_array_get_item(relocator->sectarray, reloc->target);
         require_action(sect, finish, rval=KERN_FAILURE);
 
         /* target is the change in the section's address */
@@ -751,10 +897,27 @@ calculate_targets(kxld_addr_t *_target, kxld_addr_t *_pair_target,
             rval=KERN_FAILURE);
 
         /* Get the target's symbol by symbol number */
-        sym = kxld_symtab_get_symbol_by_index(symtab, reloc->target);
+        sym = kxld_symtab_get_symbol_by_index(relocator->symtab, reloc->target);
         require_action(sym, finish, rval=KERN_FAILURE);
+
+        /* If this symbol is a padslot that has already been replaced, then the
+         * only way a relocation entry can still reference it is if there is a
+         * vtable that has not been patched.  The vtable patcher uses the
+         * MetaClass structure to find classes for patching, so an unpatched
+         * vtable means that there is an OSObject-dervied class that is missing
+         * its OSDeclare/OSDefine macros.
+         */
+        require_action(!kxld_sym_is_padslot(sym) || !kxld_sym_is_replaced(sym), 
+            finish, rval=KERN_FAILURE;
+            kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogRelocatingPatchedSym,
+                kxld_demangle(sym->name, &demangled_name, &demangled_length)));
+
         target = sym->link_addr;
 
+        if (kxld_sym_is_vtable(sym)) {
+            relocator->current_vtable = kxld_dict_find(relocator->vtables, sym->name);
+        }
+
         /* Some relocation types need the GOT entry address instead of the
          * symbol's actual address.  These types don't have pair relocation
          * entries, so we store the GOT entry address as the pair target.
@@ -762,7 +925,8 @@ calculate_targets(kxld_addr_t *_target, kxld_addr_t *_pair_target,
         if (reloc->pair_target_type == KXLD_TARGET_VALUE) {
             pair_target = reloc->pair_target;
         } else if (reloc->pair_target_type == KXLD_TARGET_SYMBOLNUM ) {
-            sym = kxld_symtab_get_symbol_by_index(symtab, reloc->pair_target);
+            sym = kxld_symtab_get_symbol_by_index(relocator->symtab, 
+                reloc->pair_target);
             require_action(sym, finish, rval=KERN_FAILURE);
             pair_target = sym->link_addr;
         } else if (reloc->pair_target_type == KXLD_TARGET_GOT) {
@@ -779,6 +943,7 @@ calculate_targets(kxld_addr_t *_target, kxld_addr_t *_pair_target,
     rval = KERN_SUCCESS;
 
 finish:
+    if (demangled_name) kxld_free(demangled_name, demangled_length);
     return rval;
 }
 
@@ -804,9 +969,10 @@ get_target_by_address_lookup(kxld_addr_t *target, kxld_addr_t addr,
         end = start + sect->size;
 
         if (start <= addr && addr < end) break;
+        
+        sect = NULL;
     }
-    require_action(i < sectarray->nitems, finish, 
-        rval=KERN_FAILURE);
+    require_action(sect, finish, rval=KERN_FAILURE);
 
     *target = sect->link_addr - sect->base_addr;
     rval = KERN_SUCCESS;
@@ -815,261 +981,226 @@ finish:
     return rval;
 }
 
-#if KXLD_USER_OR_I386 
 /*******************************************************************************
 *******************************************************************************/
-static boolean_t
-generic_reloc_has_pair(u_int _type)
+static kern_return_t
+check_for_direct_pure_virtual_call(const KXLDRelocator *relocator, u_long offset)
 {
-    enum reloc_type_generic type = _type;
+    kern_return_t rval = KERN_FAILURE;
+    const KXLDVTableEntry *entry = NULL;
+
+    if (relocator->current_vtable) {
+        entry = kxld_vtable_get_entry_for_offset(relocator->current_vtable, 
+            offset, relocator->is_32_bit);
+        require_action(!entry || !entry->patched.name ||
+            !kxld_sym_name_is_pure_virtual(entry->patched.name),
+            finish, rval=KERN_FAILURE;
+            kxld_log(kKxldLogLinking, kKxldLogErr, 
+                kKxldLogDirectPureVirtualCall));
+    }
 
-    return (type == GENERIC_RELOC_SECTDIFF || 
-        type == GENERIC_RELOC_LOCAL_SECTDIFF);
+    rval = KERN_SUCCESS;
+finish:
+    return rval;
 }
 
+#if KXLD_PIC_KEXTS
 /*******************************************************************************
 *******************************************************************************/
-static boolean_t 
-generic_reloc_is_pair(u_int _type, u_int _prev_type __unused)
+static u_long
+get_macho_data_size_for_array(const KXLDArray *relocs)
 {
-    enum reloc_type_generic type = _type;
+    const KXLDReloc *reloc = NULL;
+    u_int i = 0;
+    u_long size = 0;
 
-    return (type == GENERIC_RELOC_PAIR);
-}
+    check(relocs);
 
-/*******************************************************************************
-*******************************************************************************/
-static boolean_t generic_reloc_has_got(u_int _type __unused)
-{
-    return FALSE;
+    for (i = 0; i < relocs->nitems; ++i) {
+        reloc = kxld_array_get_item(relocs, i);
+        if (!reloc->pcrel) {
+            size += sizeof(struct relocation_info);
+            if(reloc->pair_target_type != KXLD_TARGET_NONE) {
+                size += sizeof(struct relocation_info);
+            }
+        }
+    }
+
+    return size;
 }
 
 /*******************************************************************************
 *******************************************************************************/
-static kern_return_t 
-generic_process_reloc(u_char *instruction, u_int length, u_int pcrel,
-    kxld_addr_t _base_pc, kxld_addr_t _link_pc, kxld_addr_t _link_disp __unused, 
-    u_int _type, kxld_addr_t _target, kxld_addr_t _pair_target, 
-    boolean_t swap __unused)
+static kern_return_t
+export_macho_for_array(const KXLDRelocator *relocator,
+    const KXLDArray *relocs, struct relocation_info **dstp)
 {
     kern_return_t rval = KERN_FAILURE;
-    uint32_t base_pc = (uint32_t) _base_pc;
-    uint32_t link_pc = (uint32_t) _link_pc;
-    uint32_t *instr_addr = NULL;
-    uint32_t instr_data = 0;
-    uint32_t target = (uint32_t) _target;
-    uint32_t pair_target = (uint32_t) _pair_target;
-    enum reloc_type_generic type = _type;
+    const KXLDReloc *reloc = NULL;
+    struct relocation_info *dst = NULL;
+    struct scattered_relocation_info *scatdst = NULL;
+    u_int i = 0;
 
-    check(instruction);
-    require_action(length == 2, finish, rval=KERN_FAILURE);
+    dst = *dstp;
 
-    if (pcrel) target = target + base_pc - link_pc;
+    for (i = 0; i < relocs->nitems; ++i) {
+        reloc = kxld_array_get_item(relocs, i);
+        scatdst = (struct scattered_relocation_info *) dst;
 
-    instr_addr = (uint32_t *)instruction;
-    instr_data = *instr_addr;
+        if (reloc->pcrel) {
+            continue;
+        }
 
-#if !KERNEL
-    if (swap) instr_data = OSSwapInt32(instr_data);
-#endif
+        switch (reloc->target_type) {
+        case KXLD_TARGET_LOOKUP:
+            scatdst->r_address = reloc->address;
+            scatdst->r_pcrel = reloc->pcrel;
+            scatdst->r_length = reloc->length;
+            scatdst->r_type = reloc->reloc_type;
+            scatdst->r_value = reloc->target;
+            scatdst->r_scattered = 1;
+            break;
+        case KXLD_TARGET_SECTNUM:
+            dst->r_address = reloc->address;
+            dst->r_pcrel = reloc->pcrel;
+            dst->r_length = reloc->length;
+            dst->r_type = reloc->reloc_type;
+            dst->r_symbolnum = reloc->target + 1;
+            dst->r_extern = 0;
+            break;
+        case KXLD_TARGET_SYMBOLNUM:
+           /* Assume that everything will be slid together; otherwise,
+            * there is no sensible value for the section number.
+            */
+            dst->r_address = reloc->address;
+            dst->r_pcrel = reloc->pcrel;
+            dst->r_length = reloc->length;
+            dst->r_type = reloc->reloc_type;
+            dst->r_symbolnum = 1;
+            dst->r_extern = 0;
+            break;
+        default:
+            rval = KERN_FAILURE;
+            goto finish;
+        }
 
-    switch (type) {
-    case GENERIC_RELOC_VANILLA:
-        instr_data += target;
-        break;
-    case GENERIC_RELOC_SECTDIFF:
-    case GENERIC_RELOC_LOCAL_SECTDIFF:
-        instr_data = instr_data + target - pair_target;
-        break;
-    case GENERIC_RELOC_PB_LA_PTR:
-        rval = KERN_FAILURE;
-        goto finish;
-    case GENERIC_RELOC_PAIR:
-    default:
-        rval = KERN_FAILURE;
-        goto finish;
+        ++dst;
+
+        if(reloc->pair_target_type != KXLD_TARGET_NONE) {
+            ++i;
+            require_action(i < relocs->nitems, finish, rval=KERN_FAILURE);
+            scatdst = (struct scattered_relocation_info *) dst;
+            switch (reloc->pair_target_type) {
+            case KXLD_TARGET_LOOKUP:
+                scatdst->r_address = reloc->pair_address;
+                scatdst->r_pcrel = reloc->pcrel;
+                scatdst->r_length = reloc->length;
+                scatdst->r_type = relocator->reloc_get_pair_type(reloc->reloc_type);
+                scatdst->r_value = reloc->pair_target;
+                scatdst->r_scattered = 1;
+                break;
+            case KXLD_TARGET_SECTNUM:
+                dst->r_address = reloc->pair_address;
+                dst->r_pcrel = reloc->pcrel;
+                dst->r_length = reloc->length;
+                dst->r_type = relocator->reloc_get_pair_type(reloc->reloc_type);
+                dst->r_symbolnum = reloc->pair_target + 1;
+                dst->r_extern = 0;
+                break;
+            case KXLD_TARGET_SYMBOLNUM:
+                dst->r_address = reloc->pair_address;
+                dst->r_pcrel = reloc->pcrel;
+                dst->r_length = reloc->length;
+                dst->r_type = relocator->reloc_get_pair_type(reloc->reloc_type);
+                dst->r_symbolnum = 1;
+                dst->r_extern = 0;
+                break;
+            default:
+                rval = KERN_FAILURE;
+                goto finish;
+            }
+            ++dst;
+        }
     }
 
-#if !KERNEL
-    if (swap) instr_data = OSSwapInt32(instr_data);
-#endif
-
-    *instr_addr = instr_data;
-
     rval = KERN_SUCCESS;
-
 finish:
+    *dstp = dst;
     return rval;
 }
-#endif /* KXLD_USER_OR_I386 */
+#endif /* KXLD_PIC_KEXTS */
 
-#if KXLD_USER_OR_PPC
+#if KXLD_USER_OR_I386 
 /*******************************************************************************
 *******************************************************************************/
 static boolean_t
-ppc_reloc_has_pair(u_int _type)
+generic_reloc_has_pair(u_int _type)
 {
-    enum reloc_type_ppc type = _type;
+    enum reloc_type_generic type = _type;
 
-    switch(type) {
-    case PPC_RELOC_HI16:
-    case PPC_RELOC_LO16:
-    case PPC_RELOC_HA16:
-    case PPC_RELOC_LO14:
-    case PPC_RELOC_JBSR:
-    case PPC_RELOC_SECTDIFF:
-        return TRUE;
-    default:
-        return FALSE;
-    }
+    return (type == GENERIC_RELOC_SECTDIFF || 
+        type == GENERIC_RELOC_LOCAL_SECTDIFF);
 }
 
 /*******************************************************************************
 *******************************************************************************/
-static boolean_t
-ppc_reloc_is_pair(u_int _type, u_int _prev_type __unused)
+static u_int 
+generic_reloc_get_pair_type(u_int _prev_type __unused)
 {
-    enum reloc_type_ppc type = _type;
-
-    return (type == PPC_RELOC_PAIR);
+    return GENERIC_RELOC_PAIR;
 }
 
 /*******************************************************************************
 *******************************************************************************/
-static boolean_t ppc_reloc_has_got(u_int _type __unused)
+static boolean_t generic_reloc_has_got(u_int _type __unused)
 {
     return FALSE;
 }
 
 /*******************************************************************************
 *******************************************************************************/
-static kern_return_t
-ppc_process_reloc(u_char *instruction, u_int length, u_int pcrel,
-    kxld_addr_t _base_pc, kxld_addr_t _link_pc, kxld_addr_t _link_disp __unused,
-    u_int _type, kxld_addr_t _target, kxld_addr_t _pair_target __unused,
-    boolean_t swap __unused)
+static kern_return_t 
+generic_process_reloc(const KXLDRelocator *relocator, u_char *instruction, 
+    u_int length, u_int pcrel, kxld_addr_t _base_pc, kxld_addr_t _link_pc, 
+    kxld_addr_t _link_disp __unused, u_int _type, kxld_addr_t _target, 
+    kxld_addr_t _pair_target, boolean_t swap __unused)
 {
     kern_return_t rval = KERN_FAILURE;
-    uint32_t *instr_addr = NULL;
-    uint32_t instr_data = 0;
     uint32_t base_pc = (uint32_t) _base_pc;
     uint32_t link_pc = (uint32_t) _link_pc;
+    uint32_t *instr_addr = NULL;
+    uint32_t instr_data = 0;
     uint32_t target = (uint32_t) _target;
     uint32_t pair_target = (uint32_t) _pair_target;
-    int32_t addend = 0;
-    int32_t displacement = 0;
-    uint32_t difference = 0;
-    uint32_t br14_disp_sign = 0;
-    enum reloc_type_ppc type = _type;
+    enum reloc_type_generic type = _type;
 
     check(instruction);
-    require_action(length == 2 || length == 3, finish, 
-        rval=KERN_FAILURE);
+    require_action(length == 2, finish, rval=KERN_FAILURE);
 
-    if (pcrel) displacement = target + base_pc - link_pc;
+    if (pcrel) target = target + base_pc - link_pc;
 
-    instr_addr = (uint32_t *)instruction;
+    instr_addr = (uint32_t *) ((void *) instruction);
     instr_data = *instr_addr;
-    
+
 #if !KERNEL
     if (swap) instr_data = OSSwapInt32(instr_data);
 #endif
 
-    switch (type) {
-    case PPC_RELOC_VANILLA:
-        require_action(!pcrel, finish, rval=KERN_FAILURE);
+    rval = check_for_direct_pure_virtual_call(relocator, instr_data);
+    require_noerr(rval, finish);
 
+    switch (type) {
+    case GENERIC_RELOC_VANILLA:
         instr_data += target;
         break;
-    case PPC_RELOC_BR14:
-        require_action(pcrel, finish, rval=KERN_FAILURE);
-
-        addend = BR14D(instr_data);
-        displacement += SIGN_EXTEND(addend, BR14_NBITS_DISPLACEMENT);
-        difference = ABSOLUTE_VALUE(displacement);
-        require_action(difference < BR14_LIMIT, finish, 
-            rval=KERN_FAILURE;
-            kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogRelocationOverflow));
-
-
-        br14_disp_sign = BIT15(instr_data);
-        instr_data = BR14I(instr_data) | BR14D(displacement);
-        
-        /* If this is a predicted conditional branch (signified by an
-         * instruction length of 3) that is not branch-always, and the sign of
-         * the displacement is different after relocation, then flip the y-bit
-         * to preserve the branch prediction
-         */
-        if ((length == 3) && 
-            IS_COND_BR_INSTR(instr_data) &&
-            IS_NOT_ALWAYS_TAKEN(instr_data) && 
-            (BIT15(instr_data) != br14_disp_sign))
-        {     
-            FLIP_PREDICT_BIT(instr_data);
-        }
-        break;
-    case PPC_RELOC_BR24:
-        require_action(pcrel, finish, rval=KERN_FAILURE);
-
-        addend = BR24D(instr_data);
-        displacement += SIGN_EXTEND(addend, BR24_NBITS_DISPLACEMENT);
-        difference = ABSOLUTE_VALUE(displacement);
-        require_action(difference < BR24_LIMIT, finish, 
-            rval=KERN_FAILURE;
-            kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogRelocationOverflow));
-
-        instr_data = BR24I(instr_data) | BR24D(displacement);
-        break;
-    case PPC_RELOC_HI16:
-        require_action(!pcrel, finish, rval=KERN_FAILURE);
-
-        target += LO16S(instr_data) | LO16(pair_target);
-        instr_data = HI16(instr_data) | HI16S(target);
-        break;
-    case PPC_RELOC_LO16:
-        require_action(!pcrel, finish, rval=KERN_FAILURE);
-
-        target += LO16S(pair_target) | LO16(instr_data);
-        instr_data = HI16(instr_data) | LO16(target);
-        break;
-    case PPC_RELOC_HA16:
-        require_action(!pcrel, finish, rval=KERN_FAILURE);
-
-        instr_data -= BIT15(pair_target) ? 1 : 0;
-        target += LO16S(instr_data) | LO16(pair_target);
-        instr_data = HI16(instr_data) | HI16S(target);
-        instr_data += BIT15(target) ? 1 : 0;
-        break;
-    case PPC_RELOC_JBSR:
-        require_action(!pcrel, finish, rval=KERN_FAILURE);
-
-        /* The generated code as written branches to an island that loads the
-         * absolute address of the target.  If we can branch to the target 
-         * directly with less than 24 bits of displacement, we modify the branch
-         * instruction to do so which avoids the cost of the island.
-         */
-
-        displacement = target + pair_target - link_pc;
-        difference = ABSOLUTE_VALUE(displacement);
-        if (difference < BR24_LIMIT) {
-            instr_data = BR24I(instr_data) | BR24D(displacement);
-        }
-        break;
-    case PPC_RELOC_SECTDIFF:
-        require_action(!pcrel, finish, rval=KERN_FAILURE);
-        
+    case GENERIC_RELOC_SECTDIFF:
+    case GENERIC_RELOC_LOCAL_SECTDIFF:
         instr_data = instr_data + target - pair_target;
         break;
-    case PPC_RELOC_LO14:
-    case PPC_RELOC_PB_LA_PTR:
-    case PPC_RELOC_HI16_SECTDIFF:
-    case PPC_RELOC_LO16_SECTDIFF:
-    case PPC_RELOC_HA16_SECTDIFF:
-    case PPC_RELOC_LO14_SECTDIFF:
-    case PPC_RELOC_LOCAL_SECTDIFF:
+    case GENERIC_RELOC_PB_LA_PTR:
         rval = KERN_FAILURE;
         goto finish;
-    case PPC_RELOC_PAIR:
+    case GENERIC_RELOC_PAIR:
     default:
         rval = KERN_FAILURE;
         goto finish;
@@ -1082,11 +1213,11 @@ ppc_process_reloc(u_char *instruction, u_int length, u_int pcrel,
     *instr_addr = instr_data;
 
     rval = KERN_SUCCESS;
-finish:
 
+finish:
     return rval;
 }
-#endif /* KXLD_USER_OR_PPC */
+#endif /* KXLD_USER_OR_I386 */
 
 #if KXLD_USER_OR_X86_64
 /*******************************************************************************
@@ -1101,13 +1232,10 @@ x86_64_reloc_has_pair(u_int _type)
 
 /*******************************************************************************
 *******************************************************************************/
-static boolean_
-x86_64_reloc_is_pair(u_int _type, u_int _prev_type)
+static u_in
+x86_64_reloc_get_pair_type(u_int _prev_type __unused)
 {
-    enum reloc_type_x86_64 type = _type;
-    enum reloc_type_x86_64 prev_type = _prev_type;
-
-    return (x86_64_reloc_has_pair(prev_type) && type == X86_64_RELOC_UNSIGNED);
+    return X86_64_RELOC_UNSIGNED;
 }
 
 /*******************************************************************************
@@ -1123,10 +1251,10 @@ x86_64_reloc_has_got(u_int _type)
 /*******************************************************************************
 *******************************************************************************/
 static kern_return_t 
-x86_64_process_reloc(u_char *instruction, u_int length, u_int pcrel,
-    kxld_addr_t _base_pc __unused, kxld_addr_t _link_pc, kxld_addr_t _link_disp,
-    u_int _type, kxld_addr_t _target, kxld_addr_t _pair_target
-    boolean_t swap __unused)
+x86_64_process_reloc(const KXLDRelocator *relocator __unused, u_char *instruction, 
+    u_int length, u_int pcrel, kxld_addr_t _base_pc __unused, 
+    kxld_addr_t _link_pc, kxld_addr_t _link_disp, u_int _type
+    kxld_addr_t _target, kxld_addr_t _pair_target, boolean_t swap __unused)
 {
     kern_return_t rval = KERN_FAILURE;
     enum reloc_type_x86_64 type = _type;
@@ -1145,13 +1273,16 @@ x86_64_process_reloc(u_char *instruction, u_int length, u_int pcrel,
         finish, rval=KERN_FAILURE);
 
     if (length == 2) {
-        instr32p = (int32_t *) instruction;
+        instr32p = (int32_t *) ((void *) instruction);
         instr32 = *instr32p;
 
 #if !KERNEL
         if (swap) instr32 = OSSwapInt32(instr32);
 #endif
 
+        rval = check_for_direct_pure_virtual_call(relocator, instr32);
+        require_noerr(rval, finish);
+
         /* There are a number of different small adjustments for pc-relative
          * relocation entries.  The general case is to subtract the size of the
          * relocation (represented by the length parameter), and it applies to
@@ -1244,13 +1375,16 @@ x86_64_process_reloc(u_char *instruction, u_int length, u_int pcrel,
 
         *instr32p = instr32;
     } else {
-        instr64p = (uint64_t *) instruction;
+        instr64p = (uint64_t *) ((void *) instruction);
         instr64 = *instr64p;
 
 #if !KERNEL
         if (swap) instr64 = OSSwapInt64(instr64);
 #endif
 
+        rval = check_for_direct_pure_virtual_call(relocator, (u_long) instr64);
+        require_noerr(rval, finish);
+
         switch (type) {
         case X86_64_RELOC_UNSIGNED:
             require_action(!pcrel, finish, rval=KERN_FAILURE);
@@ -1330,12 +1464,10 @@ arm_reloc_has_pair(u_int _type)
 
 /*******************************************************************************
 *******************************************************************************/
-static boolean_
-arm_reloc_is_pair(u_int _type, u_int _prev_type __unused)
+static u_in
+arm_reloc_get_pair_type(u_int _prev_type __unused)
 {
-    enum reloc_type_arm type = _type;
-
-    return (type == ARM_RELOC_PAIR);
+    return ARM_RELOC_PAIR;
 }
 
 /*******************************************************************************
@@ -1349,10 +1481,11 @@ arm_reloc_has_got(u_int _type __unused)
 /*******************************************************************************
 *******************************************************************************/
 static kern_return_t 
-arm_process_reloc(u_char *instruction, u_int length, u_int pcrel,
-    kxld_addr_t _base_pc __unused, kxld_addr_t _link_pc __unused, kxld_addr_t _link_disp __unused,
-    u_int _type __unused, kxld_addr_t _target __unused, kxld_addr_t _pair_target __unused, 
-    boolean_t swap __unused)
+arm_process_reloc(const KXLDRelocator *relocator __unused, u_char *instruction, 
+    u_int length, u_int pcrel, kxld_addr_t _base_pc __unused, 
+    kxld_addr_t _link_pc __unused, kxld_addr_t _link_disp __unused,
+    u_int _type __unused, kxld_addr_t _target __unused, 
+    kxld_addr_t _pair_target __unused,  boolean_t swap __unused)
 {
     kern_return_t rval = KERN_FAILURE;
     uint32_t *instr_addr = NULL;
@@ -1368,16 +1501,18 @@ arm_process_reloc(u_char *instruction, u_int length, u_int pcrel,
 
     if (pcrel) displacement = target + base_pc - link_pc;
 
-    instr_addr = (uint32_t *)instruction;
+    instr_addr = (uint32_t *) ((void *) instruction);
     instr_data = *instr_addr;
     
 #if !KERNEL
     if (swap) instr_data = OSSwapInt32(instr_data);
 #endif
 
+    rval = check_for_direct_pure_virtual_call(relocator, instr_data);
+    require_noerr(rval, finish);
+
     switch (type) {
     case ARM_RELOC_VANILLA:
-        require_action(!pcrel, finish, rval=KERN_FAILURE);
         instr_data += target;
         break;
 
@@ -1426,3 +1561,115 @@ finish:
 
 #endif /* KXLD_USER_OR_ARM */
 
+#if KXLD_USER_OR_ARM64
+/*******************************************************************************
+*******************************************************************************/
+boolean_t
+arm64_reloc_has_pair(u_int _type)
+{
+    return (_type == ARM64_RELOC_SUBTRACTOR);
+}
+
+/*******************************************************************************
+*******************************************************************************/
+u_int
+arm64_reloc_get_pair_type(u_int _prev_type __unused)
+{
+    if (_prev_type == ARM64_RELOC_SUBTRACTOR) {
+        return ARM64_RELOC_UNSIGNED;
+    } else {
+        return -1u;
+    }
+}
+
+/*******************************************************************************
+*******************************************************************************/
+boolean_t
+arm64_reloc_has_got(u_int _type)
+{
+    return (_type == ARM64_RELOC_GOT_LOAD_PAGE21 ||
+            _type == ARM64_RELOC_GOT_LOAD_PAGEOFF12);
+}
+
+/*******************************************************************************
+*******************************************************************************/
+kern_return_t
+arm64_process_reloc(const KXLDRelocator *relocator __unused, u_char *instruction,
+    u_int length, u_int pcrel, kxld_addr_t _base_pc __unused, kxld_addr_t _link_pc,
+    kxld_addr_t _link_disp __unused, u_int _type, kxld_addr_t _target,
+    kxld_addr_t _pair_target __unused, boolean_t swap)
+{
+    kern_return_t rval = KERN_FAILURE;
+    enum reloc_type_arm64 type = _type;
+    uint64_t target = _target;
+    uint64_t link_pc = (uint64_t) _link_pc;
+    uint64_t difference = 0;
+    int64_t displacement = 0;
+    uint32_t addend = 0;
+
+    check(instruction);
+    require_action((length == 2 || length == 3), finish, rval=KERN_FAILURE);
+
+    if (length == 2) {
+        uint32_t *instr32p = (uint32_t *) (void *) instruction;
+        uint32_t instr32 = *instr32p;
+
+#if !KERNEL
+        if (swap) instr32 = OSSwapInt32(instr32);
+#endif
+
+        switch (type) {
+        case ARM64_RELOC_BRANCH26:
+            require_action(pcrel, finish, rval=KERN_FAILURE);
+            addend = (instr32 & 0x03FFFFFF) << 2;
+            addend = SIGN_EXTEND(addend, 27);
+            displacement = (target - link_pc + addend);
+            difference = ABSOLUTE_VALUE(displacement);
+            displacement = (displacement >> 2);
+            require_action(difference < (128 * 1024 * 1024), finish,
+                           rval = KERN_FAILURE;
+                           kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogRelocationOverflow));
+            instr32 = (instr32 & 0xFC000000) | (displacement & 0x03FFFFFF);
+            break;
+
+        default:
+            rval = KERN_FAILURE;
+            goto finish;
+        }
+
+#if !KERNEL
+        if (swap) instr32 = OSSwapInt32(instr32);
+#endif
+
+        *instr32p = instr32;
+    } else { /* length == 3 */
+        uint64_t *instr64p = (uint64_t *) (void *) instruction;
+        uint64_t instr64 = *instr64p;
+
+#if !KERNEL
+        if (swap) instr64 = OSSwapInt64(instr64);
+#endif
+
+        switch (type) {
+        case ARM64_RELOC_UNSIGNED:
+            require_action(!pcrel, finish, rval=KERN_FAILURE);
+            instr64 += target;
+            break;
+        default:
+            rval = KERN_FAILURE;
+            goto finish;
+        }
+
+#if !KERNEL
+        if (swap) instr64 = OSSwapInt64(instr64);
+#endif
+
+        *instr64p = instr64;
+    }
+
+    rval = KERN_SUCCESS;
+finish:
+    return rval;
+}
+
+#endif /* KXLD_USER_OR_ARM64 */