#if !KERNEL
#include "kxld.h"
#include "kxld_types.h"
-#else
+#else
#include <libkern/kxld.h>
#include <libkern/kxld_types.h>
#endif /* KERNEL */
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
*******************************************************************************/
*******************************************************************************/
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);
/*******************************************************************************
#endif
check(_context);
- check(allocate_callback);
+ if (isOldInterface) {
+ check(allocate_callback);
+ }
check(logging_callback);
*_context = NULL;
}
/*******************************************************************************
-*******************************************************************************/
+ *******************************************************************************/
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
}
/*******************************************************************************
-*******************************************************************************/
-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.
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;
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
bzero(linked_object, vmsize);
*vmaddr_out = vmaddr;
*vmsize_out = vmsize;
-
+
finish:
return linked_object;
}
kxld_dict_clear(&context->obsolete_symbols_by_name);
kxld_dict_clear(&context->vtables_by_name);
}
-