]> 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 4867c8c78e41dc186299072a50ca67a16ef027ec..41e899eac27dad3d8667d730ad499bff90dc177d 100644 (file)
  */
 #include <string.h>
 #include <mach/boolean.h>
  */
 #include <string.h>
 #include <mach/boolean.h>
-#include <mach/machine.h>
 #include <sys/types.h>
 
 #if KERNEL
     #include <libkern/libkern.h>
 #include <sys/types.h>
 
 #if KERNEL
     #include <libkern/libkern.h>
+    #include <mach/machine.h>
 #else
 #else
-    #include <libkern/OSByteOrder.h>
     #include <stdlib.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"
 #endif
 
 #define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld"
 #include "kxld_util.h"
 #include "kxld_vtable.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>
 /* 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_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
 
 #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));
 #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));
     __attribute__((const));
 static boolean_t generic_reloc_has_got(u_int _type)
     __attribute__((const));
@@ -111,23 +123,10 @@ static kern_return_t generic_process_reloc(const KXLDRelocator *relocator,
     kxld_addr_t pair_target, boolean_t swap);
 #endif /* KXLD_USER_OR_I386 */
 
     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(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_PPC */
-
 #if KXLD_USER_OR_X86_64 
 static boolean_t x86_64_reloc_has_pair(u_int _type) 
     __attribute__((const));
 #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));
     __attribute__((const));
 static boolean_t x86_64_reloc_has_got(u_int _type)
     __attribute__((const));
@@ -142,7 +141,7 @@ 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));
 #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));
     __attribute__((const));
 static boolean_t arm_reloc_has_got(u_int _type)
     __attribute__((const));
@@ -152,6 +151,19 @@ static kern_return_t arm_process_reloc(const KXLDRelocator *relocator,
     kxld_addr_t pair_target, boolean_t swap);
 #endif /* KXLD_USER_OR_ARM */
 
     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(const KXLDRelocator *relocator, 
     const u_char *data, u_long offset)
 #if KXLD_USER_OR_ILP32
 static kxld_addr_t get_pointer_at_addr_32(const KXLDRelocator *relocator, 
     const u_char *data, u_long offset)
@@ -179,6 +191,13 @@ static kern_return_t get_target_by_address_lookup(kxld_addr_t *target,
 static kern_return_t check_for_direct_pure_virtual_call(
     const KXLDRelocator *relocator, u_long offset);
 
 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 
 /*******************************************************************************
 *******************************************************************************/
 kern_return_t 
@@ -189,48 +208,53 @@ kxld_relocator_init(KXLDRelocator *relocator, u_char *file,
     kern_return_t rval = KERN_FAILURE;
 
     check(relocator);
     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;
     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->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 */
         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->function_align = 0;
-        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;
 #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->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;
         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->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 */
         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,
     default:
         rval = KERN_FAILURE;
         kxld_log(kKxldLogLinking, kKxldLogErr,
@@ -293,8 +317,8 @@ kxld_reloc_create_macho(KXLDArray *relocarray, const KXLDRelocator *relocator,
              * symbols.
              */
 
              * 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;
             }
             {
                 continue;
             }
@@ -306,7 +330,7 @@ kxld_reloc_create_macho(KXLDArray *relocarray, const KXLDRelocator *relocator,
              *  Extern -> Symbolnum by Index
              */
             reloc = kxld_array_get_item(relocarray, reloc_index++);
              *  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;
                 reloc->address = scatsrc->r_address;
                 reloc->pcrel = scatsrc->r_pcrel;
                 reloc->length = scatsrc->r_length;
@@ -337,16 +361,18 @@ kxld_reloc_create_macho(KXLDArray *relocarray, const KXLDRelocator *relocator,
                 src = srcs + i;
                 scatsrc = (const struct scattered_relocation_info *) src;
                  
                 src = srcs + i;
                 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);
                         finish, rval=KERN_FAILURE);
+                    reloc->pair_address= scatsrc->r_address;
                     reloc->pair_target = scatsrc->r_value;
                     reloc->pair_target_type = KXLD_TARGET_LOOKUP;
                 } else {
                     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;
                     if (src->r_extern) {
                         reloc->pair_target = src->r_symbolnum;
                         reloc->pair_target_type = KXLD_TARGET_SYMBOLNUM;
@@ -384,7 +410,6 @@ count_relocatable_relocs(const KXLDRelocator *relocator,
 {
     u_int num_nonpair_relocs = 0;
     u_int i = 0;
 {
     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;
 
     const struct relocation_info *reloc = NULL;
     const struct scattered_relocation_info *sreloc = NULL;
 
@@ -394,7 +419,6 @@ count_relocatable_relocs(const KXLDRelocator *relocator,
     /* Loop over all of the relocation entries */
 
     num_nonpair_relocs = 1;
     /* 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;
 
     for (i = 1; i < nrelocs; ++i) {
         reloc = relocs + i;
 
@@ -405,18 +429,14 @@ count_relocatable_relocs(const KXLDRelocator *relocator,
             sreloc = (const struct scattered_relocation_info *) reloc;
 
             num_nonpair_relocs += 
             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 += 
         } 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));
                  || (0 == reloc->r_extern && R_ABS == reloc->r_symbolnum));
-
-            prev_type = reloc->r_type;
         }
 
     }
         }
 
     }
@@ -444,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);
 
     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);
 }
 
 /*******************************************************************************
 }
 
 /*******************************************************************************
@@ -538,6 +558,81 @@ finish:
     return reloc;
 }
 
     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_addr_t
@@ -564,7 +659,7 @@ get_pointer_at_addr_32(const KXLDRelocator *relocator,
     
     check(relocator);
 
     
     check(relocator);
 
-    addr = *(const uint32_t *) (data + offset);
+    addr = *(const uint32_t *) ((void *) (data + offset));
 #if !KERNEL
     if (relocator->swap) {
         addr = OSSwapInt32(addr);
 #if !KERNEL
     if (relocator->swap) {
         addr = OSSwapInt32(addr);
@@ -586,7 +681,7 @@ get_pointer_at_addr_64(const KXLDRelocator *relocator,
     
     check(relocator);
 
     
     check(relocator);
 
-    addr = *(const uint64_t *) (data + offset);
+    addr = *(const uint64_t *) ((void *) (data + offset));
 #if !KERNEL
     if (relocator->swap) {
         addr = OSSwapInt64(addr);
 #if !KERNEL
     if (relocator->swap) {
         addr = OSSwapInt64(addr);
@@ -600,8 +695,7 @@ get_pointer_at_addr_64(const KXLDRelocator *relocator,
 /*******************************************************************************
 *******************************************************************************/
 void 
 /*******************************************************************************
 *******************************************************************************/
 void 
-kxld_relocator_set_vtables(KXLDRelocator *relocator, 
-    const struct kxld_dict *vtables)
+kxld_relocator_set_vtables(KXLDRelocator *relocator, const KXLDDict *vtables)
 {
     relocator->vtables = vtables;
 }
 {
     relocator->vtables = vtables;
 }
@@ -627,7 +721,7 @@ align_raw_function_address(const KXLDRelocator *relocator, kxld_addr_t value)
 *******************************************************************************/
 kern_return_t 
 kxld_relocator_process_sect_reloc(KXLDRelocator *relocator,
 *******************************************************************************/
 kern_return_t 
 kxld_relocator_process_sect_reloc(KXLDRelocator *relocator,
-    const KXLDReloc *reloc, const struct kxld_sect *sect)
+    const KXLDReloc *reloc, const KXLDSect *sect)
 {
     kern_return_t rval = KERN_FAILURE;
     u_char *instruction = NULL;
 {
     kern_return_t rval = KERN_FAILURE;
     u_char *instruction = NULL;
@@ -910,165 +1004,184 @@ finish:
     return rval;
 }
 
     return rval;
 }
 
-#if KXLD_USER_OR_I386 
+#if KXLD_PIC_KEXTS
 /*******************************************************************************
 *******************************************************************************/
 /*******************************************************************************
 *******************************************************************************/
-static boolean_t
-generic_reloc_has_pair(u_int _type)
+static u_long
+get_macho_data_size_for_array(const KXLDArray *relocs)
 {
 {
-    enum reloc_type_generic type = _type;
-
-    return (type == GENERIC_RELOC_SECTDIFF || 
-        type == GENERIC_RELOC_LOCAL_SECTDIFF);
-}
+    const KXLDReloc *reloc = NULL;
+    u_int i = 0;
+    u_long size = 0;
 
 
-/*******************************************************************************
-*******************************************************************************/
-static boolean_t 
-generic_reloc_is_pair(u_int _type, u_int _prev_type __unused)
-{
-    enum reloc_type_generic type = _type;
+    check(relocs);
 
 
-    return (type == GENERIC_RELOC_PAIR);
-}
+    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);
+            }
+        }
+    }
 
 
-/*******************************************************************************
-*******************************************************************************/
-static boolean_t generic_reloc_has_got(u_int _type __unused)
-{
-    return FALSE;
+    return size;
 }
 
 /*******************************************************************************
 *******************************************************************************/
 }
 
 /*******************************************************************************
 *******************************************************************************/
-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)
+static kern_return_t
+export_macho_for_array(const KXLDRelocator *relocator,
+    const KXLDArray *relocs, struct relocation_info **dstp)
 {
     kern_return_t rval = KERN_FAILURE;
 {
     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;
-
-    check(instruction);
-    require_action(length == 2, finish, rval=KERN_FAILURE);
+    const KXLDReloc *reloc = NULL;
+    struct relocation_info *dst = NULL;
+    struct scattered_relocation_info *scatdst = NULL;
+    u_int i = 0;
 
 
-    if (pcrel) target = target + base_pc - link_pc;
+    dst = *dstp;
 
 
-    instr_addr = (uint32_t *)instruction;
-    instr_data = *instr_addr;
+    for (i = 0; i < relocs->nitems; ++i) {
+        reloc = kxld_array_get_item(relocs, i);
+        scatdst = (struct scattered_relocation_info *) dst;
 
 
-#if !KERNEL
-    if (swap) instr_data = OSSwapInt32(instr_data);
-#endif
+        if (reloc->pcrel) {
+            continue;
+        }
 
 
-    rval = check_for_direct_pure_virtual_call(relocator, instr_data);
-    require_noerr(rval, finish);
+        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;
     rval = KERN_SUCCESS;
-
 finish:
 finish:
+    *dstp = dst;
     return rval;
 }
     return rval;
 }
-#endif /* KXLD_USER_OR_I386 */
+#endif /* KXLD_PIC_KEXTS */
 
 
-#if KXLD_USER_OR_PPC
+#if KXLD_USER_OR_I386 
 /*******************************************************************************
 *******************************************************************************/
 static boolean_t
 /*******************************************************************************
 *******************************************************************************/
 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;
 }
 
 /*******************************************************************************
 *******************************************************************************/
 {
     return FALSE;
 }
 
 /*******************************************************************************
 *******************************************************************************/
-static kern_return_t
-ppc_process_reloc(const KXLDRelocator *relocator __unused, u_char *instruction, 
+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, 
     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)
+    kxld_addr_t _pair_target, boolean_t swap __unused)
 {
     kern_return_t rval = KERN_FAILURE;
 {
     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 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;
     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);
 
     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;
     instr_data = *instr_addr;
-    
+
 #if !KERNEL
     if (swap) instr_data = OSSwapInt32(instr_data);
 #endif
 #if !KERNEL
     if (swap) instr_data = OSSwapInt32(instr_data);
 #endif
@@ -1077,100 +1190,17 @@ ppc_process_reloc(const KXLDRelocator *relocator __unused, u_char *instruction,
     require_noerr(rval, finish);
 
     switch (type) {
     require_noerr(rval, finish);
 
     switch (type) {
-    case PPC_RELOC_VANILLA:
-        require_action(!pcrel, finish, rval=KERN_FAILURE);
-
+    case GENERIC_RELOC_VANILLA:
         instr_data += target;
         break;
         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;
         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;
         rval = KERN_FAILURE;
         goto finish;
-    case PPC_RELOC_PAIR:
+    case GENERIC_RELOC_PAIR:
     default:
         rval = KERN_FAILURE;
         goto finish;
     default:
         rval = KERN_FAILURE;
         goto finish;
@@ -1183,11 +1213,11 @@ ppc_process_reloc(const KXLDRelocator *relocator __unused, u_char *instruction,
     *instr_addr = instr_data;
 
     rval = KERN_SUCCESS;
     *instr_addr = instr_data;
 
     rval = KERN_SUCCESS;
-finish:
 
 
+finish:
     return rval;
 }
     return rval;
 }
-#endif /* KXLD_USER_OR_PPC */
+#endif /* KXLD_USER_OR_I386 */
 
 #if KXLD_USER_OR_X86_64
 /*******************************************************************************
 
 #if KXLD_USER_OR_X86_64
 /*******************************************************************************
@@ -1202,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;
 }
 
 /*******************************************************************************
 }
 
 /*******************************************************************************
@@ -1246,7 +1273,7 @@ x86_64_process_reloc(const KXLDRelocator *relocator __unused, u_char *instructio
         finish, rval=KERN_FAILURE);
 
     if (length == 2) {
         finish, rval=KERN_FAILURE);
 
     if (length == 2) {
-        instr32p = (int32_t *) instruction;
+        instr32p = (int32_t *) ((void *) instruction);
         instr32 = *instr32p;
 
 #if !KERNEL
         instr32 = *instr32p;
 
 #if !KERNEL
@@ -1348,7 +1375,7 @@ x86_64_process_reloc(const KXLDRelocator *relocator __unused, u_char *instructio
 
         *instr32p = instr32;
     } else {
 
         *instr32p = instr32;
     } else {
-        instr64p = (uint64_t *) instruction;
+        instr64p = (uint64_t *) ((void *) instruction);
         instr64 = *instr64p;
 
 #if !KERNEL
         instr64 = *instr64p;
 
 #if !KERNEL
@@ -1437,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;
 }
 
 /*******************************************************************************
 }
 
 /*******************************************************************************
@@ -1476,7 +1501,7 @@ arm_process_reloc(const KXLDRelocator *relocator __unused, u_char *instruction,
 
     if (pcrel) displacement = target + base_pc - link_pc;
 
 
     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
     instr_data = *instr_addr;
     
 #if !KERNEL
@@ -1536,3 +1561,115 @@ finish:
 
 #endif /* KXLD_USER_OR_ARM */
 
 
 #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 */