]> git.saurik.com Git - apple/xnu.git/blobdiff - libkern/kxld/kxld.c
xnu-4903.221.2.tar.gz
[apple/xnu.git] / libkern / kxld / kxld.c
index 728774c371805130b73ca73fd3d06215b0e663b0..a98a897f23d5841468d5dd7f044c2dcbad86d774 100644 (file)
@@ -41,7 +41,7 @@
 #if !KERNEL
     #include "kxld.h"
     #include "kxld_types.h"
-#else 
+#else
     #include <libkern/kxld.h>
     #include <libkern/kxld_types.h>
 #endif /* KERNEL */
@@ -72,6 +72,15 @@ struct kxld_context {
     cpu_subtype_t cpusubtype;
 };
 
+// set to TRUE if the kext has a vmaddr_TEXT_EXEC != 0
+boolean_t   isSplitKext         = FALSE;
+
+// set to TRUE is we come in via kxld_link_file
+boolean_t   isOldInterface      = FALSE;
+uint32_t    kaslr_offsets_count = 0;
+uint32_t   *kaslr_offsets = NULL;
+uint32_t    kaslr_offsets_index = 0;
+
 /*******************************************************************************
 * Globals
 *******************************************************************************/
@@ -96,13 +105,14 @@ static KXLDDict *s_order_dict;
 *******************************************************************************/
 
 static kern_return_t init_context(KXLDContext *context, u_int ndependencies);
-static kern_return_t init_kext_objects(KXLDContext *context, u_char *file, 
-    u_long size, const char *name, KXLDDependency *dependencies, 
-    u_int ndependencies);
-static KXLDObject * get_object_for_file(KXLDContext *context, 
+static KXLDObject * get_object_for_file(KXLDContext *context,
     u_char *file, u_long size, const char *name);
+static kern_return_t allocate_split_kext(KXLDContext *context, splitKextLinkInfo * link_info);
 static u_char * allocate_kext(KXLDContext *context, void *callback_data,
-    kxld_addr_t *vmaddr, u_long *vmsize, u_char **linked_object_alloc_out);
+                              kxld_addr_t *vmaddr, u_long *vmsize, u_char **linked_object_alloc_out);
+static kern_return_t init_kext_objects(KXLDContext *context, u_char *file,
+                                       u_long size, const char *name, KXLDDependency *dependencies,
+                                       u_int ndependencies);
 static void clear_context(KXLDContext *context);
 
 /*******************************************************************************
@@ -121,7 +131,9 @@ kxld_create_context(KXLDContext **_context,
 #endif
 
     check(_context);
-    check(allocate_callback);
+    if (isOldInterface) {
+        check(allocate_callback);
+    }
     check(logging_callback);
     *_context = NULL;
 
@@ -245,74 +257,185 @@ kxld_destroy_context(KXLDContext *context)
 }
 
 /*******************************************************************************
-*******************************************************************************/
+ *******************************************************************************/
 kern_return_t
-kxld_link_file(
+kxld_link_split_file(
     KXLDContext       * context,
-    u_char            * file,
-    u_long              size,
+    splitKextLinkInfo *link_info,
     const char        * name,
     void              * callback_data,
     KXLDDependency    * dependencies,
     u_int               ndependencies,
-    u_char           ** linked_object_out,
     kxld_addr_t       * kmod_info_kern)
+{
+    kern_return_t       rval                    = KERN_FAILURE;
+    KXLDObject *        kext_object             = NULL;
+    splitKextLinkInfo * my_link_info            = NULL;
+
+    isSplitKext = (link_info->vmaddr_TEXT_EXEC != 0);
+    isOldInterface = FALSE;
+
+    kxld_set_logging_callback_data(name, callback_data);
+
+    kxld_log(kKxldLogLinking, kKxldLogBasic, "Linking kext %s", name);
+
+    kaslr_offsets_count = 0;
+    kaslr_offsets_index = 0;
+    kaslr_offsets = NULL;
+
+    require_action(context, finish, rval=KERN_INVALID_ARGUMENT);
+    require_action(link_info, finish, rval=KERN_INVALID_ARGUMENT);
+    require_action(dependencies, finish, rval=KERN_INVALID_ARGUMENT);
+    require_action(ndependencies, finish, rval=KERN_INVALID_ARGUMENT);
+    require_action(kmod_info_kern, finish, rval=KERN_INVALID_ARGUMENT);
+    
+    rval = init_context(context, ndependencies);
+    require_noerr(rval, finish);
+    
+    rval = init_kext_objects(context,
+                             link_info->kextExecutable,
+                             link_info->kextSize,
+                             name,
+                             dependencies, ndependencies);
+    require_noerr(rval, finish);
+    
+    kext_object = get_object_for_file(context,
+                                      link_info->kextExecutable,
+                                      link_info->kextSize,
+                                      name);
+    require_action(kext_object, finish, rval=KERN_FAILURE);
+    
+    // copy vmaddrs and fileoffsets for split segments into kext_object
+    kxld_object_set_link_info(kext_object, link_info);
+
+    my_link_info = kxld_object_get_link_info(kext_object);
+    
+    rval = allocate_split_kext(context, my_link_info);
+    require_noerr(rval, finish);
+    
+#if SPLIT_KEXTS_DEBUG
+    kxld_log(kKxldLogLinking, kKxldLogErr, "Linking kext %s", name);
+    kxld_show_split_info(link_info);
+#endif // SPLIT_KEXTS_DEBUG
+    
+    rval = kxld_kext_relocate(context->kext,
+                              (kxld_addr_t)my_link_info,
+                              &context->vtables_by_name,
+                              &context->defined_symbols_by_name,
+                              &context->obsolete_symbols_by_name,
+                              &context->defined_cxx_symbols_by_value);
+    require_noerr(rval, finish);
+    
+    rval = kxld_kext_export_linked_object(context->kext,
+                                          (void *) my_link_info,
+                                          kmod_info_kern);
+    require_noerr(rval, finish);
+    
+    // pass back info about linked kext
+    link_info->kaslr_offsets_count = kaslr_offsets_count;
+    link_info->kaslr_offsets = kaslr_offsets;
+    link_info->linkedKext = my_link_info->linkedKext;
+    link_info->linkedKextSize = my_link_info->linkedKextSize;
+
+    if (kaslr_offsets_count != kaslr_offsets_index) {
+        kxld_log(kKxldLogLinking, kKxldLogErr, "[ERROR] %s: KASLR pointers: count=%d, but only populated %d!", name, kaslr_offsets_count, kaslr_offsets_index);
+        rval = KERN_FAILURE;
+        goto finish;
+    }
+
+    // the values are now the responsibility of the caller
+    kaslr_offsets_count = 0;
+    kaslr_offsets_index = 0;
+    kaslr_offsets = NULL;
+
+    rval = KERN_SUCCESS;
+finish:
+    clear_context(context);
+    kxld_set_logging_callback_data(NULL, NULL);
+    
+    return rval;
+}
+
+/*******************************************************************************
+ *******************************************************************************/
+kern_return_t
+kxld_link_file(
+               KXLDContext       * context,
+               u_char            * file,
+               u_long              size,
+               const char        * name,
+               void              * callback_data,
+               KXLDDependency    * dependencies,
+               u_int               ndependencies,
+               u_char           ** linked_object_out,
+               kxld_addr_t       * kmod_info_kern)
 {
     kern_return_t       rval                    = KERN_FAILURE;
     kxld_addr_t         vmaddr                  = 0;
     u_long              vmsize                  = 0;
     u_char            * linked_object           = NULL;
     u_char            * linked_object_alloc     = NULL;
+    
+    kaslr_offsets_count = 0;
+    kaslr_offsets_index = 0;
+    kaslr_offsets = NULL;
 
     kxld_set_logging_callback_data(name, callback_data);
-
+    
     kxld_log(kKxldLogLinking, kKxldLogBasic, "Linking kext %s", name);
-
+    
     require_action(context, finish, rval=KERN_INVALID_ARGUMENT);
-    require_action(file, finish, rval=KERN_INVALID_ARGUMENT);
-    require_action(size, finish, rval=KERN_INVALID_ARGUMENT);
     require_action(dependencies, finish, rval=KERN_INVALID_ARGUMENT);
     require_action(ndependencies, finish, rval=KERN_INVALID_ARGUMENT);
+    require_action(file, finish, rval=KERN_INVALID_ARGUMENT);
+    require_action(size, finish, rval=KERN_INVALID_ARGUMENT);
     require_action(linked_object_out, finish, rval=KERN_INVALID_ARGUMENT);
     require_action(kmod_info_kern, finish, rval=KERN_INVALID_ARGUMENT);
+    
+    isSplitKext = FALSE;
+    isOldInterface = TRUE;
 
     rval = init_context(context, ndependencies);
     require_noerr(rval, finish);
-
-    rval = init_kext_objects(context, file, size, name, 
-        dependencies, ndependencies);
+    
+    rval = init_kext_objects(context, file, size, name,
+                             dependencies, ndependencies);
     require_noerr(rval, finish);
-
-    linked_object = allocate_kext(context, callback_data, 
-        &vmaddr, &vmsize, &linked_object_alloc);
+    
+    linked_object = allocate_kext(context, callback_data,
+                                  &vmaddr, &vmsize, &linked_object_alloc);
     require_action(linked_object, finish, rval=KERN_RESOURCE_SHORTAGE);
-
-    rval = kxld_kext_relocate(context->kext, vmaddr, 
-        &context->vtables_by_name, 
-        &context->defined_symbols_by_name, 
-        &context->obsolete_symbols_by_name,
-        &context->defined_cxx_symbols_by_value);
+    
+    
+    rval = kxld_kext_relocate(context->kext,
+                              vmaddr,
+                              &context->vtables_by_name,
+                              &context->defined_symbols_by_name,
+                              &context->obsolete_symbols_by_name,
+                              &context->defined_cxx_symbols_by_value);
     require_noerr(rval, finish);
-
-    rval = kxld_kext_export_linked_object(context->kext, 
-        linked_object, kmod_info_kern);
+    
+    rval = kxld_kext_export_linked_object(context->kext,
+                                          (void *) linked_object,
+                                          kmod_info_kern);
     require_noerr(rval, finish);
-
     *linked_object_out = linked_object;
+    
     linked_object_alloc = NULL;
-
+    
     rval = KERN_SUCCESS;
 finish:
     if (linked_object_alloc) {
         kxld_page_free_untracked(linked_object_alloc, vmsize);
     }
-
+    
     clear_context(context);
     kxld_set_logging_callback_data(NULL, NULL);
-
+    
     return rval;
 }
 
+
 /*******************************************************************************
 *******************************************************************************/
 static kern_return_t
@@ -352,17 +475,21 @@ finish:
 }
 
 /*******************************************************************************
-*******************************************************************************/
-static kern_return_t 
-init_kext_objects(KXLDContext *context, u_char *file, u_long size, 
-    const char *name, KXLDDependency *dependencies, u_int ndependencies)
+ *******************************************************************************/
+static kern_return_t
+init_kext_objects(KXLDContext *context,
+                  u_char *file,
+                  u_long size,
+                  const char *name,
+                  KXLDDependency *dependencies,
+                  u_int ndependencies)
 {
     kern_return_t rval = KERN_FAILURE;
     KXLDKext *kext = NULL;
     KXLDObject *kext_object = NULL;
     KXLDObject *interface_object = NULL;
     u_int i = 0;
-
+    
     /* Create a kext object for each dependency.  If it's a direct dependency,
      * export its symbols by name by value.  If it's indirect, just export the
      * C++ symbols by value.
@@ -371,60 +498,60 @@ init_kext_objects(KXLDContext *context, u_char *file, u_long size,
         kext = kxld_array_get_item(&context->dependencies, i);
         kext_object = NULL;
         interface_object = NULL;
-
+        
         kext_object = get_object_for_file(context, dependencies[i].kext,
-            dependencies[i].kext_size, dependencies[i].kext_name);
+                                          dependencies[i].kext_size, dependencies[i].kext_name);
         require_action(kext_object, finish, rval=KERN_FAILURE);
-
+        
         if (dependencies[i].interface) {
-            interface_object = get_object_for_file(context, 
-                dependencies[i].interface, dependencies[i].interface_size,
-                dependencies[i].interface_name);
+            interface_object = get_object_for_file(context,
+                                                   dependencies[i].interface, dependencies[i].interface_size,
+                                                   dependencies[i].interface_name);
             require_action(interface_object, finish, rval=KERN_FAILURE);
         }
-
+        
         rval = kxld_kext_init(kext, kext_object, interface_object);
         require_noerr(rval, finish);
-
+        
         if (dependencies[i].is_direct_dependency) {
             rval = kxld_kext_export_symbols(kext,
-                &context->defined_symbols_by_name, 
-                &context->obsolete_symbols_by_name,
-                &context->defined_cxx_symbols_by_value);
+                                            &context->defined_symbols_by_name,
+                                            &context->obsolete_symbols_by_name,
+                                            &context->defined_cxx_symbols_by_value);
             require_noerr(rval, finish);
         } else {
-            rval = kxld_kext_export_symbols(kext, 
-                /* defined_symbols */ NULL, /* obsolete_symbols */ NULL, 
-                &context->defined_cxx_symbols_by_value);
+            rval = kxld_kext_export_symbols(kext,
+                                            /* defined_symbols */ NULL, /* obsolete_symbols */ NULL,
+                                            &context->defined_cxx_symbols_by_value);
             require_noerr(rval, finish);
         }
     }
-
+    
     /* Export the vtables for all of the dependencies. */
     for (i = 0; i < context->dependencies.nitems; ++i) {
         kext = kxld_array_get_item(&context->dependencies, i);
-
+        
         rval = kxld_kext_export_vtables(kext,
-            &context->defined_cxx_symbols_by_value,
-            &context->defined_symbols_by_name,
-            &context->vtables_by_name);
+                                        &context->defined_cxx_symbols_by_value,
+                                        &context->defined_symbols_by_name,
+                                        &context->vtables_by_name);
         require_noerr(rval, finish);
     }
-
+    
     /* Create a kext object for the kext we're linking and export its locally
-     * defined C++ symbols. 
+     * defined C++ symbols.
      */
     kext_object = get_object_for_file(context, file, size, name);
     require_action(kext_object, finish, rval=KERN_FAILURE);
-
+    
     rval = kxld_kext_init(context->kext, kext_object, /* interface */ NULL);
     require_noerr(rval, finish);
-
+    
     rval = kxld_kext_export_symbols(context->kext,
-        /* defined_symbols */ NULL, /* obsolete_symbols */ NULL, 
-        &context->defined_cxx_symbols_by_value);
+                                    /* defined_symbols */ NULL, /* obsolete_symbols */ NULL, 
+                                    &context->defined_cxx_symbols_by_value);
     require_noerr(rval, finish);
-
+    
     rval = KERN_SUCCESS;
 finish:
     return rval;
@@ -462,38 +589,74 @@ get_object_for_file(KXLDContext *context, u_char *file, u_long size,
 finish:
     return rval;
 }
+
+#include <mach-o/loader.h>
+
 /*******************************************************************************
-*******************************************************************************/
+ *******************************************************************************/
+static kern_return_t
+allocate_split_kext(KXLDContext *context, splitKextLinkInfo * link_info)
+{
+    kern_return_t       rval                    = KERN_FAILURE;
+    u_long              vmsize                  = 0;
+    u_long              header_size             = 0;
+    u_char            * linked_object           = NULL;
+    
+    kxld_kext_get_vmsize(context->kext, &header_size, &vmsize);
+    
+    if (isSplitKext) {
+        /* get __LINKEDIT vmsize */
+        kxld_kext_get_vmsize_for_seg_by_name(context->kext, SEG_LINKEDIT, &vmsize);
+        // add in the gaps
+        vmsize += (link_info->vmaddr_LINKEDIT - link_info->vmaddr_TEXT);
+    }
+    link_info->linkedKextSize = vmsize;
+    
+    linked_object = kxld_page_alloc_untracked(link_info->linkedKextSize);
+    require(linked_object, finish);
+    link_info->linkedKext = linked_object;
+
+    bzero(linked_object, vmsize);
+    rval = KERN_SUCCESS;
+
+finish:
+    return rval;
+}
+
+/*******************************************************************************
+ *******************************************************************************/
 static u_char *
-allocate_kext(KXLDContext *context, void *callback_data,
-    kxld_addr_t *vmaddr_out, u_long *vmsize_out, 
-    u_char **linked_object_alloc_out)
+allocate_kext(KXLDContext *context,
+              void *callback_data,
+              kxld_addr_t *vmaddr_out,
+              u_long *vmsize_out,
+              u_char **linked_object_alloc_out)
 {
     KXLDAllocateFlags   flags                   = 0;
     kxld_addr_t         vmaddr                  = 0;
     u_long              vmsize                  = 0;
     u_long              header_size             = 0;
     u_char            * linked_object           = NULL;
-
+    
     *linked_object_alloc_out = NULL;
-
+    
     kxld_kext_get_vmsize(context->kext, &header_size, &vmsize);
+    
     vmaddr = context->allocate_callback(vmsize, &flags, callback_data);
     require_action(!(vmaddr & (kxld_get_effective_page_size()-1)), finish,
-        kxld_log(kKxldLogLinking, kKxldLogErr,
-            "Load address %p is not page-aligned.",
-            (void *) (uintptr_t) vmaddr));
-
+                   kxld_log(kKxldLogLinking, kKxldLogErr,
+                            "Load address %p is not page-aligned.",
+                            (void *) (uintptr_t) vmaddr));
+    
     if (flags & kKxldAllocateWritable) {
         linked_object = (u_char *) (u_long) vmaddr;
     } else {
         linked_object = kxld_page_alloc_untracked(vmsize);
         require(linked_object, finish);
-
+        
         *linked_object_alloc_out = linked_object;
     }
-
+    
     kxld_kext_set_linked_object_size(context->kext, vmsize);
     
     /* Zero out the memory before we fill it.  We fill this buffer in a
@@ -504,7 +667,7 @@ allocate_kext(KXLDContext *context, void *callback_data,
     bzero(linked_object, vmsize);
     *vmaddr_out = vmaddr;
     *vmsize_out = vmsize;
-
+    
 finish:
     return linked_object;
 }
@@ -539,4 +702,3 @@ clear_context(KXLDContext *context)
     kxld_dict_clear(&context->obsolete_symbols_by_name);
     kxld_dict_clear(&context->vtables_by_name);
 }
-