* Copyright (c) 2008 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- *
+ *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
- *
+ *
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
- *
+ *
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
+ *
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#include <string.h>
#if KERNEL
#ifdef MACH_ASSERT
- #undef MACH_ASSERT
+ #undef MACH_ASSERT
#endif
#define MACH_ASSERT 1
#include <kern/assert.h>
static kern_return_t init_by_relocs(KXLDVTable *vtable, const KXLDSym *vtable_sym,
const KXLDSect *sect, const KXLDRelocator *relocator);
-static kern_return_t init_by_entries_and_relocs(KXLDVTable *vtable,
- const KXLDSym *vtable_sym, const KXLDRelocator *relocator,
+static kern_return_t init_by_entries_and_relocs(KXLDVTable *vtable,
+ const KXLDSym *vtable_sym, const KXLDRelocator *relocator,
const KXLDArray *relocs, const KXLDDict *defined_cxx_symbols);
static kern_return_t init_by_entries(KXLDVTable *vtable,
/*******************************************************************************
*******************************************************************************/
-kern_return_t
-kxld_vtable_init(KXLDVTable *vtable, const KXLDSym *vtable_sym,
+kern_return_t
+kxld_vtable_init(KXLDVTable *vtable, const KXLDSym *vtable_sym,
const KXLDObject *object, const KXLDDict *defined_cxx_symbols)
{
- kern_return_t rval = KERN_FAILURE;
- const KXLDArray *extrelocs = NULL;
- const KXLDRelocator *relocator = NULL;
- const KXLDSect *vtable_sect = NULL;
- char *demangled_name = NULL;
- size_t demangled_length = 0;
-
- check(vtable);
- check(vtable_sym);
- check(object);
-
- relocator = kxld_object_get_relocator(object);
-
- vtable_sect = kxld_object_get_section_by_index(object,
- vtable_sym->sectnum);
- require_action(vtable_sect, finish, rval=KERN_FAILURE);
-
- vtable->name = vtable_sym->name;
- vtable->vtable = vtable_sect->data +
- kxld_sym_get_section_offset(vtable_sym, vtable_sect);
-
- if (kxld_object_is_linked(object)) {
- rval = init_by_entries(vtable, relocator, defined_cxx_symbols);
- require_noerr(rval, finish);
-
- vtable->is_patched = TRUE;
- } else {
- if (kxld_object_is_final_image(object)) {
- extrelocs = kxld_object_get_extrelocs(object);
- require_action(extrelocs, finish,
- rval=KERN_FAILURE;
- kxld_log(kKxldLogPatching, kKxldLogErr,
- kKxldLogMalformedVTable,
- kxld_demangle(vtable->name,
- &demangled_name, &demangled_length)));
-
- rval = init_by_entries_and_relocs(vtable, vtable_sym,
- relocator, extrelocs, defined_cxx_symbols);
- require_noerr(rval, finish);
- } else {
- require_action(kxld_sect_get_num_relocs(vtable_sect) > 0, finish,
- rval=KERN_FAILURE;
- kxld_log(kKxldLogPatching, kKxldLogErr,
- kKxldLogMalformedVTable,
- kxld_demangle(vtable->name,
- &demangled_name, &demangled_length)));
-
- rval = init_by_relocs(vtable, vtable_sym, vtable_sect, relocator);
- require_noerr(rval, finish);
- }
-
- vtable->is_patched = FALSE;
- }
-
- rval = KERN_SUCCESS;
+ kern_return_t rval = KERN_FAILURE;
+ const KXLDArray *extrelocs = NULL;
+ const KXLDRelocator *relocator = NULL;
+ const KXLDSect *vtable_sect = NULL;
+ char *demangled_name = NULL;
+ size_t demangled_length = 0;
+
+ check(vtable);
+ check(vtable_sym);
+ check(object);
+
+ relocator = kxld_object_get_relocator(object);
+
+ vtable_sect = kxld_object_get_section_by_index(object,
+ vtable_sym->sectnum);
+ require_action(vtable_sect, finish, rval = KERN_FAILURE);
+
+ vtable->name = vtable_sym->name;
+ vtable->vtable = vtable_sect->data +
+ kxld_sym_get_section_offset(vtable_sym, vtable_sect);
+
+ if (kxld_object_is_linked(object)) {
+ rval = init_by_entries(vtable, relocator, defined_cxx_symbols);
+ require_noerr(rval, finish);
+
+ vtable->is_patched = TRUE;
+ } else {
+ if (kxld_object_is_final_image(object)) {
+ extrelocs = kxld_object_get_extrelocs(object);
+
+ require_action(extrelocs, finish,
+ rval = KERN_FAILURE;
+ kxld_log(kKxldLogPatching, kKxldLogErr,
+ kKxldLogMalformedVTable,
+ kxld_demangle(vtable->name,
+ &demangled_name, &demangled_length)));
+
+ rval = init_by_entries_and_relocs(vtable, vtable_sym,
+ relocator, extrelocs, defined_cxx_symbols);
+ require_noerr(rval, finish);
+ } else {
+ require_action(kxld_sect_get_num_relocs(vtable_sect) > 0, finish,
+ rval = KERN_FAILURE;
+ kxld_log(kKxldLogPatching, kKxldLogErr,
+ kKxldLogMalformedVTable,
+ kxld_demangle(vtable->name,
+ &demangled_name, &demangled_length)));
+
+ rval = init_by_relocs(vtable, vtable_sym, vtable_sect, relocator);
+ require_noerr(rval, finish);
+ }
+
+ vtable->is_patched = FALSE;
+ }
+
+ rval = KERN_SUCCESS;
finish:
- if (demangled_name) kxld_free(demangled_name, demangled_length);
- return rval;
+ if (demangled_name) {
+ kxld_free(demangled_name, demangled_length);
+ }
+
+ return rval;
}
/*******************************************************************************
*******************************************************************************/
-static void
+static void
get_vtable_base_sizes(boolean_t is_32_bit, u_int *vtable_entry_size,
u_int *vtable_header_size)
{
- check(vtable_entry_size);
- check(vtable_header_size);
-
- if (is_32_bit) {
- *vtable_entry_size = VTABLE_ENTRY_SIZE_32;
- *vtable_header_size = VTABLE_HEADER_SIZE_32;
- } else {
- *vtable_entry_size = VTABLE_ENTRY_SIZE_64;
- *vtable_header_size = VTABLE_HEADER_SIZE_64;
- }
+ check(vtable_entry_size);
+ check(vtable_header_size);
+
+ if (is_32_bit) {
+ *vtable_entry_size = VTABLE_ENTRY_SIZE_32;
+ *vtable_header_size = VTABLE_HEADER_SIZE_32;
+ } else {
+ *vtable_entry_size = VTABLE_ENTRY_SIZE_64;
+ *vtable_header_size = VTABLE_HEADER_SIZE_64;
+ }
}
/*******************************************************************************
* entries and finding the corresponding symbols.
*******************************************************************************/
static kern_return_t
-init_by_relocs(KXLDVTable *vtable, const KXLDSym *vtable_sym,
+init_by_relocs(KXLDVTable *vtable, const KXLDSym *vtable_sym,
const KXLDSect *sect, const KXLDRelocator *relocator)
{
- kern_return_t rval = KERN_FAILURE;
- KXLDReloc *reloc = NULL;
- KXLDVTableEntry *entry = NULL;
- KXLDSym *sym = NULL;
- kxld_addr_t vtable_base_offset = 0;
- kxld_addr_t entry_offset = 0;
- u_int i = 0;
- u_int nentries = 0;
- u_int vtable_entry_size = 0;
- u_int vtable_header_size = 0;
- u_int base_reloc_index = 0;
- u_int reloc_index = 0;
-
- check(vtable);
- check(vtable_sym);
- check(sect);
- check(relocator);
-
- /* Find the first entry past the vtable padding */
-
- (void) get_vtable_base_sizes(relocator->is_32_bit,
- &vtable_entry_size, &vtable_header_size);
-
- vtable_base_offset = kxld_sym_get_section_offset(vtable_sym, sect) +
- vtable_header_size;
-
- /* Find the relocation entry at the start of the vtable */
-
- rval = kxld_reloc_get_reloc_index_by_offset(§->relocs,
- vtable_base_offset, &base_reloc_index);
- require_noerr(rval, finish);
-
- /* Count the number of consecutive relocation entries to find the number of
- * vtable entries. For some reason, the __TEXT,__const relocations are
- * sorted in descending order, so we have to walk backwards. Also, make
- * sure we don't run off the end of the section's relocs.
- */
-
- reloc_index = base_reloc_index;
- entry_offset = vtable_base_offset;
- reloc = kxld_array_get_item(§->relocs, reloc_index);
- while (reloc->address == entry_offset) {
- ++nentries;
- if (!reloc_index) break;
-
- --reloc_index;
-
- reloc = kxld_array_get_item(§->relocs, reloc_index);
- entry_offset += vtable_entry_size;
- }
-
- /* Allocate the symbol index */
-
- rval = kxld_array_init(&vtable->entries, sizeof(KXLDVTableEntry), nentries);
- require_noerr(rval, finish);
-
- /* Find the symbols for each vtable entry */
-
- for (i = 0; i < vtable->entries.nitems; ++i) {
- reloc = kxld_array_get_item(§->relocs, base_reloc_index - i);
- entry = kxld_array_get_item(&vtable->entries, i);
-
- /* If we can't find a symbol, it means it is a locally-defined,
- * non-external symbol that has been stripped. We don't patch over
- * locally-defined symbols, so we leave the symbol as NULL and just
- * skip it. We won't be able to patch subclasses with this symbol,
- * but there isn't much we can do about that.
- */
- sym = kxld_reloc_get_symbol(relocator, reloc, sect->data);
-
- entry->unpatched.sym = sym;
- entry->unpatched.reloc = reloc;
- }
-
- rval = KERN_SUCCESS;
+ kern_return_t rval = KERN_FAILURE;
+ KXLDReloc *reloc = NULL;
+ KXLDVTableEntry *entry = NULL;
+ KXLDSym *sym = NULL;
+ kxld_addr_t vtable_base_offset = 0;
+ kxld_addr_t entry_offset = 0;
+ u_int i = 0;
+ u_int nentries = 0;
+ u_int vtable_entry_size = 0;
+ u_int vtable_header_size = 0;
+ u_int base_reloc_index = 0;
+ u_int reloc_index = 0;
+
+ check(vtable);
+ check(vtable_sym);
+ check(sect);
+ check(relocator);
+
+ /* Find the first entry past the vtable padding */
+
+ (void) get_vtable_base_sizes(relocator->is_32_bit,
+ &vtable_entry_size, &vtable_header_size);
+
+ vtable_base_offset = kxld_sym_get_section_offset(vtable_sym, sect) +
+ vtable_header_size;
+
+ /* Find the relocation entry at the start of the vtable */
+
+ rval = kxld_reloc_get_reloc_index_by_offset(§->relocs,
+ vtable_base_offset, &base_reloc_index);
+ require_noerr(rval, finish);
+
+ /* Count the number of consecutive relocation entries to find the number of
+ * vtable entries. For some reason, the __TEXT,__const relocations are
+ * sorted in descending order, so we have to walk backwards. Also, make
+ * sure we don't run off the end of the section's relocs.
+ */
+
+ reloc_index = base_reloc_index;
+ entry_offset = vtable_base_offset;
+ reloc = kxld_array_get_item(§->relocs, reloc_index);
+ while (reloc->address == entry_offset) {
+ ++nentries;
+ if (!reloc_index) {
+ break;
+ }
+
+ --reloc_index;
+
+ reloc = kxld_array_get_item(§->relocs, reloc_index);
+ entry_offset += vtable_entry_size;
+ }
+
+ /* Allocate the symbol index */
+
+ rval = kxld_array_init(&vtable->entries, sizeof(KXLDVTableEntry), nentries);
+ require_noerr(rval, finish);
+
+ /* Find the symbols for each vtable entry */
+
+ for (i = 0; i < vtable->entries.nitems; ++i) {
+ reloc = kxld_array_get_item(§->relocs, base_reloc_index - i);
+ entry = kxld_array_get_item(&vtable->entries, i);
+
+ /* If we can't find a symbol, it means it is a locally-defined,
+ * non-external symbol that has been stripped. We don't patch over
+ * locally-defined symbols, so we leave the symbol as NULL and just
+ * skip it. We won't be able to patch subclasses with this symbol,
+ * but there isn't much we can do about that.
+ */
+ sym = kxld_reloc_get_symbol(relocator, reloc, sect->data);
+
+ entry->unpatched.sym = sym;
+ entry->unpatched.reloc = reloc;
+ }
+
+ rval = KERN_SUCCESS;
finish:
- return rval;
+ return rval;
}
/*******************************************************************************
init_by_entries(KXLDVTable *vtable, const KXLDRelocator *relocator,
const KXLDDict *defined_cxx_symbols)
{
- kern_return_t rval = KERN_FAILURE;
- KXLDVTableEntry *tmpentry = NULL;
- KXLDSym *sym = NULL;
- kxld_addr_t entry_value = 0;
- u_long entry_offset;
- u_int vtable_entry_size = 0;
- u_int vtable_header_size = 0;
- u_int nentries = 0;
- u_int i = 0;
-
- check(vtable);
- check(relocator);
-
- (void) get_vtable_base_sizes(relocator->is_32_bit,
- &vtable_entry_size, &vtable_header_size);
-
- /* Count the number of entries (the vtable is null-terminated) */
-
- entry_offset = vtable_header_size;
- while (1) {
- entry_value = kxld_relocator_get_pointer_at_addr(relocator,
- vtable->vtable, entry_offset);
- if (!entry_value) break;
-
- entry_offset += vtable_entry_size;
- ++nentries;
- }
-
- /* Allocate the symbol index */
-
- rval = kxld_array_init(&vtable->entries, sizeof(KXLDVTableEntry), nentries);
- require_noerr(rval, finish);
-
- /* Look up the symbols for each entry */
-
- for (i = 0, entry_offset = vtable_header_size;
- i < vtable->entries.nitems;
- ++i, entry_offset += vtable_entry_size)
- {
- entry_value = kxld_relocator_get_pointer_at_addr(relocator,
- vtable->vtable, entry_offset);
-
- /* If we can't find the symbol, it means that the virtual function was
- * defined inline. There's not much I can do about this; it just means
- * I can't patch this function.
- */
- tmpentry = kxld_array_get_item(&vtable->entries, i);
- sym = kxld_dict_find(defined_cxx_symbols, &entry_value);
-
- if (sym) {
- tmpentry->patched.name = sym->name;
- tmpentry->patched.addr = sym->link_addr;
- } else {
- tmpentry->patched.name = NULL;
- tmpentry->patched.addr = 0;
- }
- }
-
- rval = KERN_SUCCESS;
+ kern_return_t rval = KERN_FAILURE;
+ KXLDVTableEntry *tmpentry = NULL;
+ KXLDSym *sym = NULL;
+ kxld_addr_t entry_value = 0;
+ u_long entry_offset;
+ u_int vtable_entry_size = 0;
+ u_int vtable_header_size = 0;
+ u_int nentries = 0;
+ u_int i = 0;
+
+ check(vtable);
+ check(relocator);
+
+ (void) get_vtable_base_sizes(relocator->is_32_bit,
+ &vtable_entry_size, &vtable_header_size);
+
+ /* Count the number of entries (the vtable is null-terminated) */
+
+ entry_offset = vtable_header_size;
+ while (1) {
+ entry_value = kxld_relocator_get_pointer_at_addr(relocator,
+ vtable->vtable, entry_offset);
+ if (!entry_value) {
+ break;
+ }
+
+ entry_offset += vtable_entry_size;
+ ++nentries;
+ }
+
+ /* Allocate the symbol index */
+
+ rval = kxld_array_init(&vtable->entries, sizeof(KXLDVTableEntry), nentries);
+ require_noerr(rval, finish);
+
+ /* Look up the symbols for each entry */
+
+ for (i = 0, entry_offset = vtable_header_size;
+ i < vtable->entries.nitems;
+ ++i, entry_offset += vtable_entry_size) {
+ entry_value = kxld_relocator_get_pointer_at_addr(relocator,
+ vtable->vtable, entry_offset);
+
+ /* If we can't find the symbol, it means that the virtual function was
+ * defined inline. There's not much I can do about this; it just means
+ * I can't patch this function.
+ */
+ tmpentry = kxld_array_get_item(&vtable->entries, i);
+ sym = kxld_dict_find(defined_cxx_symbols, &entry_value);
+
+ if (sym) {
+ tmpentry->patched.name = sym->name;
+ tmpentry->patched.addr = sym->link_addr;
+ } else {
+ tmpentry->patched.name = NULL;
+ tmpentry->patched.addr = 0;
+ }
+ }
+
+ rval = KERN_SUCCESS;
finish:
- return rval;
+ return rval;
}
/*******************************************************************************
* external symbols.
*******************************************************************************/
static kern_return_t
-init_by_entries_and_relocs(KXLDVTable *vtable, const KXLDSym *vtable_sym,
+init_by_entries_and_relocs(KXLDVTable *vtable, const KXLDSym *vtable_sym,
const KXLDRelocator *relocator, const KXLDArray *relocs,
const KXLDDict *defined_cxx_symbols)
{
- kern_return_t rval = KERN_FAILURE;
- KXLDReloc *reloc = NULL;
- KXLDVTableEntry *tmpentry = NULL;
- KXLDSym *sym = NULL;
- u_int vtable_entry_size = 0;
- u_int vtable_header_size = 0;
- kxld_addr_t entry_value = 0;
- u_long entry_offset = 0;
- u_int nentries = 0;
- u_int i = 0;
- char *demangled_name1 = NULL;
- size_t demangled_length1 = 0;
-
- check(vtable);
- check(vtable_sym);
- check(relocator);
- check(relocs);
-
- /* Find the first entry and its offset past the vtable padding */
-
- (void) get_vtable_base_sizes(relocator->is_32_bit,
- &vtable_entry_size, &vtable_header_size);
-
- /* In a final linked image, a vtable slot is valid if it is nonzero
- * (meaning the userspace linker has already resolved it) or if it has
- * a relocation entry. We'll know the end of the vtable when we find a
- * slot that meets neither of these conditions.
- */
- entry_offset = vtable_header_size;
- while (1) {
- entry_value = kxld_relocator_get_pointer_at_addr(relocator,
- vtable->vtable, entry_offset);
- if (!entry_value) {
- reloc = kxld_reloc_get_reloc_by_offset(relocs,
- vtable_sym->base_addr + entry_offset);
- if (!reloc) break;
- }
-
- ++nentries;
- entry_offset += vtable_entry_size;
- }
-
- /* Allocate the symbol index */
-
- rval = kxld_array_init(&vtable->entries, sizeof(KXLDVTableEntry), nentries);
- require_noerr(rval, finish);
-
- /* Find the symbols for each vtable entry */
-
- for (i = 0, entry_offset = vtable_header_size;
- i < vtable->entries.nitems;
- ++i, entry_offset += vtable_entry_size)
- {
- entry_value = kxld_relocator_get_pointer_at_addr(relocator,
- vtable->vtable, entry_offset);
-
- /* If we can't find a symbol, it means it is a locally-defined,
- * non-external symbol that has been stripped. We don't patch over
- * locally-defined symbols, so we leave the symbol as NULL and just
- * skip it. We won't be able to patch subclasses with this symbol,
- * but there isn't much we can do about that.
- */
- if (entry_value) {
- reloc = NULL;
- sym = kxld_dict_find(defined_cxx_symbols, &entry_value);
- } else {
- reloc = kxld_reloc_get_reloc_by_offset(relocs,
- vtable_sym->base_addr + entry_offset);
- require_action(reloc, finish,
- rval=KERN_FAILURE;
- kxld_log(kKxldLogPatching, kKxldLogErr,
- kKxldLogMalformedVTable,
- kxld_demangle(vtable->name, &demangled_name1,
- &demangled_length1)));
-
- sym = kxld_reloc_get_symbol(relocator, reloc, /* data */ NULL);
- }
-
- tmpentry = kxld_array_get_item(&vtable->entries, i);
- tmpentry->unpatched.reloc = reloc;
- tmpentry->unpatched.sym = sym;
- }
-
- rval = KERN_SUCCESS;
+ kern_return_t rval = KERN_FAILURE;
+ KXLDReloc *reloc = NULL;
+ KXLDVTableEntry *tmpentry = NULL;
+ KXLDSym *sym = NULL;
+ u_int vtable_entry_size = 0;
+ u_int vtable_header_size = 0;
+ kxld_addr_t entry_value = 0;
+ u_long entry_offset = 0;
+ u_int nentries = 0;
+ u_int i = 0;
+ char *demangled_name1 = NULL;
+ size_t demangled_length1 = 0;
+
+ check(vtable);
+ check(vtable_sym);
+ check(relocator);
+ check(relocs);
+
+ /* Find the first entry and its offset past the vtable padding */
+
+ (void) get_vtable_base_sizes(relocator->is_32_bit,
+ &vtable_entry_size, &vtable_header_size);
+
+ /* In a final linked image, a vtable slot is valid if it is nonzero
+ * (meaning the userspace linker has already resolved it) or if it has
+ * a relocation entry. We'll know the end of the vtable when we find a
+ * slot that meets neither of these conditions.
+ */
+ entry_offset = vtable_header_size;
+ while (1) {
+ entry_value = kxld_relocator_get_pointer_at_addr(relocator,
+ vtable->vtable, entry_offset);
+ if (!entry_value) {
+ reloc = kxld_reloc_get_reloc_by_offset(relocs,
+ vtable_sym->base_addr + entry_offset);
+ if (!reloc) {
+ break;
+ }
+ }
+
+ ++nentries;
+ entry_offset += vtable_entry_size;
+ }
+
+ /* Allocate the symbol index */
+
+ rval = kxld_array_init(&vtable->entries, sizeof(KXLDVTableEntry), nentries);
+ require_noerr(rval, finish);
+
+ /* Find the symbols for each vtable entry */
+
+ for (i = 0, entry_offset = vtable_header_size;
+ i < vtable->entries.nitems;
+ ++i, entry_offset += vtable_entry_size) {
+ entry_value = kxld_relocator_get_pointer_at_addr(relocator,
+ vtable->vtable, entry_offset);
+
+ /* If we can't find a symbol, it means it is a locally-defined,
+ * non-external symbol that has been stripped. We don't patch over
+ * locally-defined symbols, so we leave the symbol as NULL and just
+ * skip it. We won't be able to patch subclasses with this symbol,
+ * but there isn't much we can do about that.
+ */
+ if (entry_value) {
+ reloc = NULL;
+ sym = kxld_dict_find(defined_cxx_symbols, &entry_value);
+ } else {
+ reloc = kxld_reloc_get_reloc_by_offset(relocs,
+ vtable_sym->base_addr + entry_offset);
+
+ require_action(reloc, finish,
+ rval = KERN_FAILURE;
+ kxld_log(kKxldLogPatching, kKxldLogErr,
+ kKxldLogMalformedVTable,
+ kxld_demangle(vtable->name, &demangled_name1,
+ &demangled_length1)));
+
+ sym = kxld_reloc_get_symbol(relocator, reloc, /* data */ NULL);
+ }
+
+ tmpentry = kxld_array_get_item(&vtable->entries, i);
+ tmpentry->unpatched.reloc = reloc;
+ tmpentry->unpatched.sym = sym;
+ }
+
+ rval = KERN_SUCCESS;
finish:
- return rval;
+ return rval;
}
/*******************************************************************************
void
kxld_vtable_clear(KXLDVTable *vtable)
{
- check(vtable);
+ check(vtable);
- vtable->vtable = NULL;
- vtable->name = NULL;
- vtable->is_patched = FALSE;
- kxld_array_clear(&vtable->entries);
+ vtable->vtable = NULL;
+ vtable->name = NULL;
+ vtable->is_patched = FALSE;
+ kxld_array_clear(&vtable->entries);
}
/*******************************************************************************
void
kxld_vtable_deinit(KXLDVTable *vtable)
{
- check(vtable);
+ check(vtable);
- kxld_array_deinit(&vtable->entries);
- bzero(vtable, sizeof(*vtable));
+ kxld_array_deinit(&vtable->entries);
+ bzero(vtable, sizeof(*vtable));
}
/*******************************************************************************
*******************************************************************************/
-KXLDVTableEntry *
-kxld_vtable_get_entry_for_offset(const KXLDVTable *vtable, u_long offset,
+KXLDVTableEntry *
+kxld_vtable_get_entry_for_offset(const KXLDVTable *vtable, u_long offset,
boolean_t is_32_bit)
{
- KXLDVTableEntry *rval = NULL;
- u_int vtable_entry_size = 0;
- u_int vtable_header_size = 0;
- u_int vtable_entry_idx = 0;
+ KXLDVTableEntry *rval = NULL;
+ u_int vtable_entry_size = 0;
+ u_int vtable_header_size = 0;
+ u_int vtable_entry_idx = 0;
- (void) get_vtable_base_sizes(is_32_bit,
- &vtable_entry_size, &vtable_header_size);
+ (void) get_vtable_base_sizes(is_32_bit,
+ &vtable_entry_size, &vtable_header_size);
- if (offset % vtable_entry_size) {
- goto finish;
- }
+ if (offset % vtable_entry_size) {
+ goto finish;
+ }
- vtable_entry_idx = (u_int) ((offset - vtable_header_size) / vtable_entry_size);
- rval = kxld_array_get_item(&vtable->entries, vtable_entry_idx);
+ vtable_entry_idx = (u_int) ((offset - vtable_header_size) / vtable_entry_size);
+ rval = kxld_array_get_item(&vtable->entries, vtable_entry_idx);
finish:
- return rval;
+ return rval;
}
/*******************************************************************************
kxld_vtable_patch(KXLDVTable *vtable, const KXLDVTable *super_vtable,
KXLDObject *object)
{
- kern_return_t rval = KERN_FAILURE;
- const KXLDSymtab *symtab = NULL;
- const KXLDSym *sym = NULL;
- KXLDVTableEntry *child_entry = NULL;
- KXLDVTableEntry *parent_entry = NULL;
- u_int symindex = 0;
- u_int i = 0;
- char *demangled_name1 = NULL;
- char *demangled_name2 = NULL;
- char *demangled_name3 = NULL;
- size_t demangled_length1 = 0;
- size_t demangled_length2 = 0;
- size_t demangled_length3 = 0;
- boolean_t failure = FALSE;
-
- check(vtable);
- check(super_vtable);
-
- symtab = kxld_object_get_symtab(object);
-
- require_action(!vtable->is_patched, finish, rval=KERN_SUCCESS);
- require_action(super_vtable->is_patched, finish, rval=KERN_FAILURE);
- require_action(vtable->entries.nitems >= super_vtable->entries.nitems, finish,
- rval=KERN_FAILURE;
- kxld_log(kKxldLogPatching, kKxldLogErr, kKxldLogMalformedVTable,
- kxld_demangle(vtable->name, &demangled_name1, &demangled_length1)));
-
- for (i = 0; i < super_vtable->entries.nitems; ++i) {
- child_entry = kxld_array_get_item(&vtable->entries, i);
- parent_entry = kxld_array_get_item(&super_vtable->entries, i);
-
- /* The child entry can be NULL when a locally-defined, non-external
- * symbol is stripped. We wouldn't patch this entry anyway, so we
- * just skip it.
- */
-
- if (!child_entry->unpatched.sym) continue;
-
- /* It's possible for the patched parent entry not to have a symbol
- * (e.g. when the definition is inlined). We can't patch this entry no
- * matter what, so we'll just skip it and die later if it's a problem
- * (which is not likely).
- */
-
- if (!parent_entry->patched.name) continue;
-
- /* 1) If the symbol is defined locally, do not patch */
-
- if (kxld_sym_is_defined_locally(child_entry->unpatched.sym)) continue;
-
- /* 2) If the child is a pure virtual function, do not patch.
- * In general, we want to proceed with patching when the symbol is
- * externally defined because pad slots fall into this category.
- * The pure virtual function symbol is special case, as the pure
- * virtual property itself overrides the parent's implementation.
- */
-
- if (kxld_sym_is_pure_virtual(child_entry->unpatched.sym)) continue;
-
- /* 3) If the symbols are the same, do not patch */
-
- if (streq(child_entry->unpatched.sym->name,
- parent_entry->patched.name))
- {
- continue;
- }
-
- /* 4) If the parent vtable entry is a pad slot, and the child does not
- * match it, then the child was built against a newer version of the
- * libraries, so it is binary-incompatible.
- */
-
- require_action(!kxld_sym_name_is_padslot(parent_entry->patched.name),
- finish, rval=KERN_FAILURE;
- kxld_log(kKxldLogPatching, kKxldLogErr,
- kKxldLogParentOutOfDate,
- kxld_demangle(super_vtable->name, &demangled_name1,
- &demangled_length1),
- kxld_demangle(vtable->name, &demangled_name2,
- &demangled_length2)));
+ kern_return_t rval = KERN_FAILURE;
+ const KXLDSymtab *symtab = NULL;
+ const KXLDSym *sym = NULL;
+ KXLDVTableEntry *child_entry = NULL;
+ KXLDVTableEntry *parent_entry = NULL;
+ u_int symindex = 0;
+ u_int i = 0;
+ char *demangled_name1 = NULL;
+ char *demangled_name2 = NULL;
+ char *demangled_name3 = NULL;
+ size_t demangled_length1 = 0;
+ size_t demangled_length2 = 0;
+ size_t demangled_length3 = 0;
+ boolean_t failure = FALSE;
+
+ check(vtable);
+ check(super_vtable);
+
+ symtab = kxld_object_get_symtab(object);
+
+ require_action(!vtable->is_patched, finish, rval = KERN_SUCCESS);
+ require_action(super_vtable->is_patched, finish, rval = KERN_FAILURE);
+ require_action(vtable->entries.nitems >= super_vtable->entries.nitems, finish,
+ rval = KERN_FAILURE;
+ kxld_log(kKxldLogPatching, kKxldLogErr, kKxldLogMalformedVTable,
+ kxld_demangle(vtable->name, &demangled_name1, &demangled_length1)));
+
+ for (i = 0; i < super_vtable->entries.nitems; ++i) {
+ child_entry = kxld_array_get_item(&vtable->entries, i);
+ parent_entry = kxld_array_get_item(&super_vtable->entries, i);
+
+ /* The child entry can be NULL when a locally-defined, non-external
+ * symbol is stripped. We wouldn't patch this entry anyway, so we
+ * just skip it.
+ */
+
+ if (!child_entry->unpatched.sym) {
+ continue;
+ }
+
+ /* It's possible for the patched parent entry not to have a symbol
+ * (e.g. when the definition is inlined). We can't patch this entry no
+ * matter what, so we'll just skip it and die later if it's a problem
+ * (which is not likely).
+ */
+
+ if (!parent_entry->patched.name) {
+ continue;
+ }
+
+ /* 1) If the symbol is defined locally, do not patch */
+
+ if (kxld_sym_is_defined_locally(child_entry->unpatched.sym)) {
+ continue;
+ }
+
+ /* 2) If the child is a pure virtual function, do not patch.
+ * In general, we want to proceed with patching when the symbol is
+ * externally defined because pad slots fall into this category.
+ * The pure virtual function symbol is special case, as the pure
+ * virtual property itself overrides the parent's implementation.
+ */
+
+ if (kxld_sym_is_pure_virtual(child_entry->unpatched.sym)) {
+ continue;
+ }
+
+ /* 3) If the symbols are the same, do not patch */
+
+ if (streq(child_entry->unpatched.sym->name,
+ parent_entry->patched.name)) {
+ continue;
+ }
+
+ /* 4) If the parent vtable entry is a pad slot, and the child does not
+ * match it, then the child was built against a newer version of the
+ * libraries, so it is binary-incompatible.
+ */
+
+ require_action(!kxld_sym_name_is_padslot(parent_entry->patched.name),
+ finish, rval = KERN_FAILURE;
+ kxld_log(kKxldLogPatching, kKxldLogErr,
+ kKxldLogParentOutOfDate,
+ kxld_demangle(super_vtable->name, &demangled_name1,
+ &demangled_length1),
+ kxld_demangle(vtable->name, &demangled_name2,
+ &demangled_length2)));
#if KXLD_USER_OR_STRICT_PATCHING
- /* 5) If we are doing strict patching, we prevent kexts from declaring
- * virtual functions and not implementing them. We can tell if a
- * virtual function is declared but not implemented because we resolve
- * symbols before patching; an unimplemented function will still be
- * undefined at this point. We then look at whether the symbol has
- * the same class prefix as the vtable. If it does, the symbol was
- * declared as part of the class and not inherited, which means we
- * should not patch it.
- */
-
- if (kxld_object_target_supports_strict_patching(object) &&
- !kxld_sym_is_defined(child_entry->unpatched.sym))
- {
- char class_name[KXLD_MAX_NAME_LEN];
- char function_prefix[KXLD_MAX_NAME_LEN];
- u_long function_prefix_len = 0;
-
- rval = kxld_sym_get_class_name_from_vtable_name(vtable->name,
- class_name, sizeof(class_name));
- require_noerr(rval, finish);
-
- function_prefix_len =
- kxld_sym_get_function_prefix_from_class_name(class_name,
- function_prefix, sizeof(function_prefix));
- require(function_prefix_len, finish);
-
- if (!strncmp(child_entry->unpatched.sym->name,
- function_prefix, function_prefix_len))
- {
- failure = TRUE;
- kxld_log(kKxldLogPatching, kKxldLogErr,
- "The %s is unpatchable because its class declares the "
- "method '%s' without providing an implementation.",
- kxld_demangle(vtable->name,
- &demangled_name1, &demangled_length1),
- kxld_demangle(child_entry->unpatched.sym->name,
- &demangled_name2, &demangled_length2));
- continue;
- }
- }
+ /* 5) If we are doing strict patching, we prevent kexts from declaring
+ * virtual functions and not implementing them. We can tell if a
+ * virtual function is declared but not implemented because we resolve
+ * symbols before patching; an unimplemented function will still be
+ * undefined at this point. We then look at whether the symbol has
+ * the same class prefix as the vtable. If it does, the symbol was
+ * declared as part of the class and not inherited, which means we
+ * should not patch it.
+ */
+
+ if (kxld_object_target_supports_strict_patching(object) &&
+ !kxld_sym_is_defined(child_entry->unpatched.sym)) {
+ char class_name[KXLD_MAX_NAME_LEN];
+ char function_prefix[KXLD_MAX_NAME_LEN];
+ u_long function_prefix_len = 0;
+
+ rval = kxld_sym_get_class_name_from_vtable_name(vtable->name,
+ class_name, sizeof(class_name));
+ require_noerr(rval, finish);
+
+ function_prefix_len =
+ kxld_sym_get_function_prefix_from_class_name(class_name,
+ function_prefix, sizeof(function_prefix));
+ require(function_prefix_len, finish);
+
+ if (!strncmp(child_entry->unpatched.sym->name,
+ function_prefix, function_prefix_len)) {
+ failure = TRUE;
+ kxld_log(kKxldLogPatching, kKxldLogErr,
+ "The %s is unpatchable because its class declares the "
+ "method '%s' without providing an implementation.",
+ kxld_demangle(vtable->name,
+ &demangled_name1, &demangled_length1),
+ kxld_demangle(child_entry->unpatched.sym->name,
+ &demangled_name2, &demangled_length2));
+ continue;
+ }
+ }
#endif /* KXLD_USER_OR_STRICT_PATCHING */
-
- /* 6) The child symbol is unresolved and different from its parent, so
- * we need to patch it up. We do this by modifying the relocation
- * entry of the vtable entry to point to the symbol of the parent
- * vtable entry. If that symbol does not exist (i.e. we got the data
- * from a link state object's vtable representation), then we create a
- * new symbol in the symbol table and point the relocation entry to
- * that.
- */
-
- sym = kxld_symtab_get_locally_defined_symbol_by_name(symtab,
- parent_entry->patched.name);
- if (!sym) {
- rval = kxld_object_add_symbol(object, parent_entry->patched.name,
- parent_entry->patched.addr, &sym);
- require_noerr(rval, finish);
- }
- require_action(sym, finish, rval=KERN_FAILURE);
-
- rval = kxld_symtab_get_sym_index(symtab, sym, &symindex);
- require_noerr(rval, finish);
-
- rval = kxld_reloc_update_symindex(child_entry->unpatched.reloc, symindex);
- require_noerr(rval, finish);
- kxld_log(kKxldLogPatching, kKxldLogDetail,
- "In vtable '%s', patching '%s' with '%s'.",
- kxld_demangle(vtable->name, &demangled_name1, &demangled_length1),
- kxld_demangle(child_entry->unpatched.sym->name,
- &demangled_name2, &demangled_length2),
- kxld_demangle(sym->name, &demangled_name3, &demangled_length3));
-
- rval = kxld_object_patch_symbol(object, child_entry->unpatched.sym);
- require_noerr(rval, finish);
-
- child_entry->unpatched.sym = sym;
-
- /*
- * The C++ ABI requires that functions be aligned on a 2-byte boundary:
- * http://www.codesourcery.com/public/cxx-abi/abi.html#member-pointers
- * If the LSB of any virtual function's link address is 1, then the
- * compiler has violated that part of the ABI, and we're going to panic
- * in _ptmf2ptf() (in OSMetaClass.h). Better to panic here with some
- * context.
- */
- assert(kxld_sym_is_pure_virtual(sym) || !(sym->link_addr & 1));
- }
-
- require_action(!failure, finish, rval=KERN_FAILURE);
-
- /* Change the vtable representation from the unpatched layout to the
- * patched layout.
- */
-
- for (i = 0; i < vtable->entries.nitems; ++i) {
- char *name;
- kxld_addr_t addr;
-
- child_entry = kxld_array_get_item(&vtable->entries, i);
- if (child_entry->unpatched.sym) {
- name = child_entry->unpatched.sym->name;
- addr = child_entry->unpatched.sym->link_addr;
- } else {
- name = NULL;
- addr = 0;
- }
-
- child_entry->patched.name = name;
- child_entry->patched.addr = addr;
- }
-
- vtable->is_patched = TRUE;
- rval = KERN_SUCCESS;
+
+ /* 6) The child symbol is unresolved and different from its parent, so
+ * we need to patch it up. We do this by modifying the relocation
+ * entry of the vtable entry to point to the symbol of the parent
+ * vtable entry. If that symbol does not exist (i.e. we got the data
+ * from a link state object's vtable representation), then we create a
+ * new symbol in the symbol table and point the relocation entry to
+ * that.
+ */
+
+ sym = kxld_symtab_get_locally_defined_symbol_by_name(symtab,
+ parent_entry->patched.name);
+ if (!sym) {
+ rval = kxld_object_add_symbol(object, parent_entry->patched.name,
+ parent_entry->patched.addr, &sym);
+ require_noerr(rval, finish);
+ }
+ require_action(sym, finish, rval = KERN_FAILURE);
+
+ rval = kxld_symtab_get_sym_index(symtab, sym, &symindex);
+ require_noerr(rval, finish);
+
+ rval = kxld_reloc_update_symindex(child_entry->unpatched.reloc, symindex);
+ require_noerr(rval, finish);
+
+ kxld_log(kKxldLogPatching, kKxldLogDetail,
+ "In vtable '%s', patching '%s' with '%s'.",
+ kxld_demangle(vtable->name, &demangled_name1, &demangled_length1),
+ kxld_demangle(child_entry->unpatched.sym->name,
+ &demangled_name2, &demangled_length2),
+ kxld_demangle(sym->name, &demangled_name3, &demangled_length3));
+
+ rval = kxld_object_patch_symbol(object, child_entry->unpatched.sym);
+ require_noerr(rval, finish);
+
+ child_entry->unpatched.sym = sym;
+
+ /*
+ * The C++ ABI requires that functions be aligned on a 2-byte boundary:
+ * http://www.codesourcery.com/public/cxx-abi/abi.html#member-pointers
+ * If the LSB of any virtual function's link address is 1, then the
+ * compiler has violated that part of the ABI, and we're going to panic
+ * in _ptmf2ptf() (in OSMetaClass.h). Better to panic here with some
+ * context.
+ */
+ assert(kxld_sym_is_pure_virtual(sym) || !(sym->link_addr & 1));
+ }
+
+ require_action(!failure, finish, rval = KERN_FAILURE);
+
+ /* Change the vtable representation from the unpatched layout to the
+ * patched layout.
+ */
+
+ for (i = 0; i < vtable->entries.nitems; ++i) {
+ char *name;
+ kxld_addr_t addr;
+
+ child_entry = kxld_array_get_item(&vtable->entries, i);
+ if (child_entry->unpatched.sym) {
+ name = child_entry->unpatched.sym->name;
+ addr = child_entry->unpatched.sym->link_addr;
+ } else {
+ name = NULL;
+ addr = 0;
+ }
+
+ child_entry->patched.name = name;
+ child_entry->patched.addr = addr;
+ }
+
+ vtable->is_patched = TRUE;
+ rval = KERN_SUCCESS;
finish:
- if (demangled_name1) kxld_free(demangled_name1, demangled_length1);
- if (demangled_name2) kxld_free(demangled_name2, demangled_length2);
- if (demangled_name3) kxld_free(demangled_name3, demangled_length3);
-
- return rval;
+ if (demangled_name1) {
+ kxld_free(demangled_name1, demangled_length1);
+ }
+ if (demangled_name2) {
+ kxld_free(demangled_name2, demangled_length2);
+ }
+ if (demangled_name3) {
+ kxld_free(demangled_name3, demangled_length3);
+ }
+
+ return rval;
}
-