*/
#include <string.h>
#include <mach/boolean.h>
-#include <mach/machine.h>
#include <sys/types.h>
#if KERNEL
#include <libkern/libkern.h>
+ #include <mach/machine.h>
#else
- #include <libkern/OSByteOrder.h>
#include <stdlib.h>
+ #include <libkern/OSByteOrder.h>
+
+ /* Get machine.h from the kernel source so we can support all platforms
+ * that the kernel supports. Otherwise we're at the mercy of the host.
+ */
+ #include "../../osfmk/mach/machine.h"
#endif
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld"
#include <AssertMacros.h>
#include "kxld_array.h"
+#include "kxld_demangle.h"
+#include "kxld_dict.h"
#include "kxld_reloc.h"
#include "kxld_sect.h"
#include "kxld_seg.h"
#include "kxld_sym.h"
#include "kxld_symtab.h"
#include "kxld_util.h"
+#include "kxld_vtable.h"
+
+#if KXLD_PIC_KEXTS
+/* This will try to pull in mach/machine.h, so it has to come after the
+ * explicit include above.
+ */
+#include <mach-o/loader.h>
+#endif
/* include target-specific relocation prototypes */
#include <mach-o/reloc.h>
-#if KXLD_USER_OR_PPC
-#include <mach-o/ppc/reloc.h>
-#endif
#if KXLD_USER_OR_X86_64
#include <mach-o/x86_64/reloc.h>
#endif
#if KXLD_USER_OR_ARM
#include <mach-o/arm/reloc.h>
#endif
+#if KXLD_USER_OR_ARM64
+#include <mach-o/arm64/reloc.h>
+#endif
#define KXLD_TARGET_NONE (u_int) 0x0
#define KXLD_TARGET_VALUE (u_int) 0x1
#if KXLD_USER_OR_I386
static boolean_t generic_reloc_has_pair(u_int _type)
__attribute__((const));
-static boolean_t generic_reloc_is_pair(u_int _type, u_int _prev_type)
+static u_int generic_reloc_get_pair_type(u_int _prev_type)
__attribute__((const));
static boolean_t generic_reloc_has_got(u_int _type)
__attribute__((const));
-static kern_return_t generic_process_reloc(u_char *instruction, u_int length,
- u_int pcrel, kxld_addr_t base_pc, kxld_addr_t link_pc, kxld_addr_t link_disp,
- u_int type, kxld_addr_t target, kxld_addr_t pair_target, boolean_t swap);
+static kern_return_t generic_process_reloc(const KXLDRelocator *relocator,
+ u_char *instruction, u_int length, u_int pcrel, kxld_addr_t base_pc,
+ kxld_addr_t link_pc, kxld_addr_t link_disp, u_int type, kxld_addr_t target,
+ kxld_addr_t pair_target, boolean_t swap);
#endif /* KXLD_USER_OR_I386 */
-#if KXLD_USER_OR_PPC
-static boolean_t ppc_reloc_has_pair(u_int _type)
- __attribute__((const));
-static boolean_t ppc_reloc_is_pair(u_int _type, u_int _prev_type)
- __attribute__((const));
-static boolean_t ppc_reloc_has_got(u_int _type)
- __attribute__((const));
-static kern_return_t ppc_process_reloc(u_char *instruction, u_int length,
- u_int pcrel, kxld_addr_t base_pc, kxld_addr_t link_pc, kxld_addr_t link_disp,
- u_int type, kxld_addr_t target, kxld_addr_t pair_target, boolean_t swap);
-#endif /* KXLD_USER_OR_PPC */
-
#if KXLD_USER_OR_X86_64
static boolean_t x86_64_reloc_has_pair(u_int _type)
__attribute__((const));
-static boolean_t x86_64_reloc_is_pair(u_int _type, u_int _prev_type)
+static u_int x86_64_reloc_get_pair_type(u_int _prev_type)
__attribute__((const));
static boolean_t x86_64_reloc_has_got(u_int _type)
__attribute__((const));
-static kern_return_t x86_64_process_reloc(u_char *instruction, u_int length,
- u_int pcrel, kxld_addr_t base_pc, kxld_addr_t link_pc, kxld_addr_t link_disp,
- u_int type, kxld_addr_t target, kxld_addr_t pair_target, boolean_t swap);
+static kern_return_t x86_64_process_reloc(const KXLDRelocator *relocator,
+ u_char *instruction, u_int length, u_int pcrel, kxld_addr_t base_pc,
+ kxld_addr_t link_pc, kxld_addr_t link_disp, u_int type, kxld_addr_t target,
+ kxld_addr_t pair_target, boolean_t swap);
static kern_return_t calculate_displacement_x86_64(uint64_t target,
uint64_t adjustment, int32_t *instr32);
#endif /* KXLD_USER_OR_X86_64 */
#if KXLD_USER_OR_ARM
static boolean_t arm_reloc_has_pair(u_int _type)
__attribute__((const));
-static boolean_t arm_reloc_is_pair(u_int _type, u_int _prev_type)
+static u_int arm_reloc_get_pair_type(u_int _prev_type)
__attribute__((const));
static boolean_t arm_reloc_has_got(u_int _type)
__attribute__((const));
-static kern_return_t arm_process_reloc(u_char *instruction, u_int length,
- u_int pcrel, kxld_addr_t base_pc, kxld_addr_t link_pc, kxld_addr_t link_disp,
- u_int type, kxld_addr_t target, kxld_addr_t pair_target, boolean_t swap);
+static kern_return_t arm_process_reloc(const KXLDRelocator *relocator,
+ u_char *instruction, u_int length, u_int pcrel, kxld_addr_t base_pc,
+ kxld_addr_t link_pc, kxld_addr_t link_disp, u_int type, kxld_addr_t target,
+ kxld_addr_t pair_target, boolean_t swap);
#endif /* KXLD_USER_OR_ARM */
+#if KXLD_USER_OR_ARM64
+static boolean_t arm64_reloc_has_pair(u_int _type)
+ __attribute__((const));
+static u_int arm64_reloc_get_pair_type(u_int _prev_type)
+ __attribute__((const));
+static boolean_t arm64_reloc_has_got(u_int _type)
+ __attribute__((const));
+static kern_return_t arm64_process_reloc(const KXLDRelocator *relocator,
+ u_char *instruction, u_int length, u_int pcrel, kxld_addr_t base_pc,
+ kxld_addr_t link_pc, kxld_addr_t link_disp, u_int type, kxld_addr_t target,
+ kxld_addr_t pair_target, boolean_t swap);
+#endif /* KXLD_USER_OR_ARM64 */
+
#if KXLD_USER_OR_ILP32
-static kxld_addr_t get_pointer_at_addr_32(u_char *data, u_long offset,
- const KXLDRelocator *relocator __unused)
+static kxld_addr_t get_pointer_at_addr_32(const KXLDRelocator *relocator,
+ const u_char *data, u_long offset)
__attribute__((pure, nonnull));
#endif /* KXLD_USER_OR_ILP32 */
#if KXLD_USER_OR_LP64
-static kxld_addr_t get_pointer_at_addr_64(u_char *data, u_long offset,
- const KXLDRelocator *relocator __unused)
+static kxld_addr_t get_pointer_at_addr_64(const KXLDRelocator *relocator,
+ const u_char *data, u_long offset)
__attribute__((pure, nonnull));
#endif /* KXLD_USER_OR_LP64 */
const struct relocation_info *relocs, u_int nrelocs)
__attribute__((pure));
-static kern_return_t calculate_targets(kxld_addr_t *_target,
- kxld_addr_t *_pair_target, const KXLDReloc *reloc,
- const KXLDArray *sectarray, const KXLDSymtab *symtab);
+static kern_return_t calculate_targets(KXLDRelocator *relocator,
+ kxld_addr_t *_target, kxld_addr_t *_pair_target, const KXLDReloc *reloc);
+
+static kxld_addr_t align_raw_function_address(const KXLDRelocator *relocator,
+ kxld_addr_t value);
+
static kern_return_t get_target_by_address_lookup(kxld_addr_t *target,
kxld_addr_t addr, const KXLDArray *sectarray);
+static kern_return_t check_for_direct_pure_virtual_call(
+ const KXLDRelocator *relocator, u_long offset);
+
+#if KXLD_PIC_KEXTS
+static u_long get_macho_data_size_for_array(const KXLDArray *relocs);
+
+static kern_return_t export_macho_for_array(const KXLDRelocator *relocator,
+ const KXLDArray *relocs, struct relocation_info **dstp);
+#endif /* KXLD_PIC_KEXTS */
+
/*******************************************************************************
*******************************************************************************/
kern_return_t
-kxld_relocator_init(KXLDRelocator *relocator, cpu_type_t cputype,
+kxld_relocator_init(KXLDRelocator *relocator, u_char *file,
+ const KXLDSymtab *symtab, const KXLDArray *sectarray, cpu_type_t cputype,
cpu_subtype_t cpusubtype __unused, boolean_t swap)
{
kern_return_t rval = KERN_FAILURE;
check(relocator);
-
+
switch(cputype) {
#if KXLD_USER_OR_I386
case CPU_TYPE_I386:
relocator->reloc_has_pair = generic_reloc_has_pair;
- relocator->reloc_is_pair = generic_reloc_is_pair;
+ relocator->reloc_get_pair_type = generic_reloc_get_pair_type;
relocator->reloc_has_got = generic_reloc_has_got;
relocator->process_reloc = generic_process_reloc;
+ relocator->function_align = 0;
relocator->is_32_bit = TRUE;
+ relocator->may_scatter = TRUE;
break;
#endif /* KXLD_USER_OR_I386 */
-#if KXLD_USER_OR_PPC
- case CPU_TYPE_POWERPC:
- relocator->reloc_has_pair = ppc_reloc_has_pair;
- relocator->reloc_is_pair = ppc_reloc_is_pair;
- relocator->reloc_has_got = ppc_reloc_has_got;
- relocator->process_reloc = ppc_process_reloc;
- relocator->is_32_bit = TRUE;
- break;
-#endif /* KXLD_USER_OR_PPC */
#if KXLD_USER_OR_X86_64
case CPU_TYPE_X86_64:
relocator->reloc_has_pair = x86_64_reloc_has_pair;
- relocator->reloc_is_pair = x86_64_reloc_is_pair;
+ relocator->reloc_get_pair_type = x86_64_reloc_get_pair_type;
relocator->reloc_has_got = x86_64_reloc_has_got;
relocator->process_reloc = x86_64_process_reloc;
+ relocator->function_align = 0;
relocator->is_32_bit = FALSE;
+ relocator->may_scatter = FALSE;
break;
#endif /* KXLD_USER_OR_X86_64 */
#if KXLD_USER_OR_ARM
case CPU_TYPE_ARM:
relocator->reloc_has_pair = arm_reloc_has_pair;
- relocator->reloc_is_pair = arm_reloc_is_pair;
+ relocator->reloc_get_pair_type = arm_reloc_get_pair_type;
relocator->reloc_has_got = arm_reloc_has_got;
relocator->process_reloc = arm_process_reloc;
+ relocator->function_align = 1;
relocator->is_32_bit = TRUE;
+ relocator->may_scatter = FALSE;
break;
#endif /* KXLD_USER_OR_ARM */
+#if KXLD_USER_OR_ARM64
+ case CPU_TYPE_ARM64:
+ relocator->reloc_has_pair = arm64_reloc_has_pair;
+ relocator->reloc_get_pair_type = arm64_reloc_get_pair_type;
+ relocator->reloc_has_got = arm64_reloc_has_got;
+ relocator->process_reloc = arm64_process_reloc;
+ relocator->function_align = 0;
+ relocator->is_32_bit = FALSE;
+ relocator->may_scatter = FALSE;
+ break;
+#endif /* KXLD_USER_OR_ARM64 */
+
default:
rval = KERN_FAILURE;
kxld_log(kKxldLogLinking, kKxldLogErr,
goto finish;
}
+ relocator->file = file;
+ relocator->symtab = symtab;
+ relocator->sectarray = sectarray;
relocator->is_32_bit = kxld_is_32_bit(cputype);
relocator->swap = swap;
kern_return_t rval = KERN_FAILURE;
KXLDReloc *reloc = NULL;
u_int nrelocs = 0;
- const struct relocation_info *src = NULL, *prev_src = NULL;
- const struct scattered_relocation_info *scatsrc = NULL, *prev_scatsrc = NULL;
+ const struct relocation_info *src = NULL;
+ const struct scattered_relocation_info *scatsrc = NULL;
u_int i = 0;
u_int reloc_index = 0;
* 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;
}
* 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;
++i;
require_action(i < nsrcs, finish, rval=KERN_FAILURE);
- prev_src = src;
src = srcs + i;
- prev_scatsrc = (const struct scattered_relocation_info *) prev_src;
scatsrc = (const struct scattered_relocation_info *) src;
- if (src->r_address & R_SCATTERED) {
- require_action(relocator->reloc_is_pair(
- scatsrc->r_type, reloc->reloc_type),
+ if (relocator->may_scatter && (src->r_address & R_SCATTERED)) {
+ require_action(relocator->reloc_get_pair_type(
+ reloc->reloc_type) == scatsrc->r_type,
finish, rval=KERN_FAILURE);
+ reloc->pair_address= scatsrc->r_address;
reloc->pair_target = scatsrc->r_value;
reloc->pair_target_type = KXLD_TARGET_LOOKUP;
} else {
- require_action(relocator->reloc_is_pair(src->r_type,
- reloc->reloc_type), finish, rval=KERN_FAILURE);
-
+ require_action(relocator->reloc_get_pair_type(
+ reloc->reloc_type) == scatsrc->r_type,
+ finish, rval=KERN_FAILURE);
+ reloc->pair_address = scatsrc->r_address;
if (src->r_extern) {
reloc->pair_target = src->r_symbolnum;
reloc->pair_target_type = KXLD_TARGET_SYMBOLNUM;
{
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;
/* 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;
sreloc = (const struct scattered_relocation_info *) reloc;
num_nonpair_relocs +=
- (!relocator->reloc_is_pair(sreloc->r_type, prev_type));
-
- prev_type = sreloc->r_type;
+ !relocator->reloc_has_pair(sreloc->r_type);
} else {
/* A normal relocation entry is relocatable if it is not a pair and
* if it is not a section-based relocation for an absolute symbol.
*/
num_nonpair_relocs +=
- !(relocator->reloc_is_pair(reloc->r_type, prev_type)
+ !(relocator->reloc_has_pair(reloc->r_type)
|| (0 == reloc->r_extern && R_ABS == reloc->r_symbolnum));
-
- prev_type = reloc->r_type;
}
}
/*******************************************************************************
*******************************************************************************/
-boolean_t
-kxld_relocator_is_pair(const KXLDRelocator *relocator, u_int r_type,
+u_int
+kxld_relocator_get_pair_type(const KXLDRelocator *relocator,
u_int prev_r_type)
{
check(relocator);
- return relocator->reloc_is_pair(r_type, prev_r_type);
+ return relocator->reloc_get_pair_type(prev_r_type);
}
/*******************************************************************************
/*******************************************************************************
*******************************************************************************/
KXLDSym *
-kxld_reloc_get_symbol(const KXLDRelocator *relocator, const KXLDReloc *reloc,
- u_char *data, const KXLDSymtab *symtab)
+kxld_reloc_get_symbol(const KXLDRelocator *relocator, const KXLDReloc *reloc,
+ const u_char *data)
{
KXLDSym *sym = NULL;
kxld_addr_t value = 0;
check(reloc);
- check(symtab);
switch (reloc->target_type) {
case KXLD_TARGET_SYMBOLNUM:
- sym = kxld_symtab_get_symbol_by_index(symtab, reloc->target);
+ sym = kxld_symtab_get_symbol_by_index(relocator->symtab, reloc->target);
break;
case KXLD_TARGET_SECTNUM:
- if (data) {
- KXLD_3264_FUNC(relocator->is_32_bit, value,
- get_pointer_at_addr_32, get_pointer_at_addr_64,
- data, reloc->address, relocator);
- sym = kxld_symtab_get_cxx_symbol_by_value(symtab, value);
+ if (data) {
+ value = kxld_relocator_get_pointer_at_addr(relocator, data,
+ reloc->address);
+ sym = kxld_symtab_get_cxx_symbol_by_value(relocator->symtab, value);
}
break;
default:
return reloc;
}
+#if KXLD_PIC_KEXTS
+/*******************************************************************************
+*******************************************************************************/
+u_long
+kxld_reloc_get_macho_header_size()
+{
+ return sizeof(struct dysymtab_command);
+}
+
+/*******************************************************************************
+*******************************************************************************/
+u_long
+kxld_reloc_get_macho_data_size(const KXLDArray *locrelocs,
+ const KXLDArray *extrelocs)
+{
+ u_long rval = 0;
+
+ rval += get_macho_data_size_for_array(locrelocs);
+ rval += get_macho_data_size_for_array(extrelocs);
+
+ return (rval);
+}
+
+/*******************************************************************************
+*******************************************************************************/
+kern_return_t
+kxld_reloc_export_macho(const KXLDRelocator *relocator,
+ const KXLDArray *locrelocs, const KXLDArray *extrelocs,
+ u_char *buf, u_long *header_offset, u_long header_size,
+ u_long *data_offset, u_long size)
+{
+ kern_return_t rval = KERN_FAILURE;
+ struct dysymtab_command *dysymtabhdr = NULL;
+ struct relocation_info *start = NULL;
+ struct relocation_info *dst = NULL;
+ u_long count = 0;
+ u_long data_size = 0;
+
+ check(locrelocs);
+ check(extrelocs);
+ check(buf);
+ check(header_offset);
+ check(data_offset);
+
+ require_action(sizeof(*dysymtabhdr) <= header_size - *header_offset, finish, rval=KERN_FAILURE);
+ dysymtabhdr = (struct dysymtab_command *) ((void *) (buf + *header_offset));
+ *header_offset += sizeof(*dysymtabhdr);
+
+ data_size = kxld_reloc_get_macho_data_size(locrelocs, extrelocs);
+ require_action((*data_offset + data_size) <= size, finish, rval=KERN_FAILURE);
+
+ start = dst = (struct relocation_info *) ((void *) (buf + *data_offset));
+
+ rval = export_macho_for_array(relocator, locrelocs, &dst);
+ require_noerr(rval, finish);
+
+ rval = export_macho_for_array(relocator, extrelocs, &dst);
+ require_noerr(rval, finish);
+
+ count = dst - start;
+
+ memset(dysymtabhdr, 0, sizeof(*dysymtabhdr));
+ dysymtabhdr->cmd = LC_DYSYMTAB;
+ dysymtabhdr->cmdsize = (uint32_t) sizeof(*dysymtabhdr);
+ dysymtabhdr->locreloff = (uint32_t) *data_offset;
+ dysymtabhdr->nlocrel = (uint32_t) count;
+
+ *data_offset += count * sizeof(struct relocation_info);
+
+ rval = KERN_SUCCESS;
+finish:
+ return rval;
+}
+#endif /* KXLD_PIC_KEXTS */
+
+/*******************************************************************************
+*******************************************************************************/
+kxld_addr_t
+kxld_relocator_get_pointer_at_addr(const KXLDRelocator *relocator,
+ const u_char *data, u_long offset)
+{
+ kxld_addr_t value;
+
+ KXLD_3264_FUNC(relocator->is_32_bit, value,
+ get_pointer_at_addr_32, get_pointer_at_addr_64,
+ relocator, data, offset);
+
+ return value;
+}
+
#if KXLD_USER_OR_ILP32
/*******************************************************************************
*******************************************************************************/
static kxld_addr_t
-get_pointer_at_addr_32(u_char *data, u_long offset,
- const KXLDRelocator *relocator __unused)
+get_pointer_at_addr_32(const KXLDRelocator *relocator,
+ const u_char *data, u_long offset)
{
uint32_t addr = 0;
check(relocator);
- check(data);
- addr = *(uint32_t *) (data + offset);
+ addr = *(const uint32_t *) ((void *) (data + offset));
#if !KERNEL
if (relocator->swap) {
addr = OSSwapInt32(addr);
}
#endif
- return (kxld_addr_t) addr;
+ return align_raw_function_address(relocator, addr);
}
#endif /* KXLD_USER_OR_ILP32 */
/*******************************************************************************
*******************************************************************************/
static kxld_addr_t
-get_pointer_at_addr_64(u_char *data, u_long offset,
- const KXLDRelocator *relocator __unused)
+get_pointer_at_addr_64(const KXLDRelocator *relocator,
+ const u_char *data, u_long offset)
{
uint64_t addr = 0;
check(relocator);
- check(data);
- addr = *(uint64_t *) (data + offset);
+ addr = *(const uint64_t *) ((void *) (data + offset));
#if !KERNEL
if (relocator->swap) {
addr = OSSwapInt64(addr);
}
#endif
- return (kxld_addr_t) addr;
+ return align_raw_function_address(relocator, addr);
}
#endif /* KXLD_USER_OR_LP64 */
+/*******************************************************************************
+*******************************************************************************/
+void
+kxld_relocator_set_vtables(KXLDRelocator *relocator, const KXLDDict *vtables)
+{
+ relocator->vtables = vtables;
+}
+
+/*******************************************************************************
+* When we're inspecting the raw binary and not the symbol table, value may
+* hold a THUMB address (with bit 0 set to 1) but the index will have the real
+* address (bit 0 set to 0). So if bit 0 is set here, we clear it. This only
+* impacts ARM for now, but it's implemented as a generic function alignment
+* mask.
+*******************************************************************************/
+static kxld_addr_t
+align_raw_function_address(const KXLDRelocator *relocator, kxld_addr_t value)
+{
+ if (relocator->function_align) {
+ value &= ~((1ULL << relocator->function_align) - 1);
+ }
+
+ return value;
+}
+
/*******************************************************************************
*******************************************************************************/
kern_return_t
-kxld_relocator_process_sect_reloc(const KXLDRelocator *relocator,
- const KXLDReloc *reloc, const struct kxld_sect *sect,
- const KXLDArray *sectarray, const struct kxld_symtab *symtab)
+kxld_relocator_process_sect_reloc(KXLDRelocator *relocator,
+ const KXLDReloc *reloc, const KXLDSect *sect)
{
kern_return_t rval = KERN_FAILURE;
u_char *instruction = NULL;
check(relocator);
check(reloc);
check(sect);
- check(sectarray);
- check(symtab);
/* Find the instruction */
/* Calculate the target */
- rval = calculate_targets(&target, &pair_target, reloc, sectarray, symtab);
+ rval = calculate_targets(relocator, &target, &pair_target, reloc);
require_noerr(rval, finish);
base_pc = reloc->address;
/* Relocate */
- rval = relocator->process_reloc(instruction, reloc->length, reloc->pcrel,
- base_pc, link_pc, link_disp, reloc->reloc_type, target, pair_target,
- relocator->swap);
+ rval = relocator->process_reloc(relocator, instruction, reloc->length,
+ reloc->pcrel, base_pc, link_pc, link_disp, reloc->reloc_type, target,
+ pair_target, relocator->swap);
require_noerr(rval, finish);
/* Return */
+ relocator->current_vtable = NULL;
rval = KERN_SUCCESS;
finish:
/*******************************************************************************
*******************************************************************************/
kern_return_t
-kxld_relocator_process_table_reloc(const KXLDRelocator *relocator,
- const KXLDReloc *reloc, const KXLDSeg *seg, u_char *file,
- const struct kxld_array *sectarray, const struct kxld_symtab *symtab)
+kxld_relocator_process_table_reloc(KXLDRelocator *relocator,
+ const KXLDReloc *reloc, const KXLDSeg *seg, kxld_addr_t link_addr)
{
kern_return_t rval = KERN_FAILURE;
u_char *instruction = NULL;
kxld_addr_t pair_target = 0;
kxld_addr_t base_pc = 0;
kxld_addr_t link_pc = 0;
- kxld_addr_t link_disp = 0;
+ u_long offset = 0;
check(relocator);
check(reloc);
- check(file);
- check(sectarray);
- check(symtab);
/* Find the instruction */
- instruction = file + seg->fileoff + reloc->address;
+ offset = (u_long)(seg->fileoff + (reloc->address - seg->base_addr));
+ instruction = relocator->file + offset;
/* Calculate the target */
- rval = calculate_targets(&target, &pair_target, reloc, sectarray, symtab);
+ rval = calculate_targets(relocator, &target, &pair_target, reloc);
require_noerr(rval, finish);
base_pc = reloc->address;
- link_pc = base_pc + seg->link_addr;
- link_disp = seg->link_addr - seg->base_addr;
+ link_pc = base_pc + link_addr;
/* Relocate */
- rval = relocator->process_reloc(instruction, reloc->length, reloc->pcrel,
- base_pc, link_pc, link_disp, reloc->reloc_type, target, pair_target,
- relocator->swap);
+ rval = relocator->process_reloc(relocator, instruction, reloc->length,
+ reloc->pcrel, base_pc, link_pc, link_addr, reloc->reloc_type, target,
+ pair_target, relocator->swap);
require_noerr(rval, finish);
/* Return */
+ relocator->current_vtable = NULL;
rval = KERN_SUCCESS;
finish:
/*******************************************************************************
*******************************************************************************/
static kern_return_t
-calculate_targets(kxld_addr_t *_target, kxld_addr_t *_pair_target,
- const KXLDReloc *reloc, const KXLDArray *sectarray, const KXLDSymtab *symtab)
+calculate_targets(KXLDRelocator *relocator, kxld_addr_t *_target,
+ kxld_addr_t *_pair_target, const KXLDReloc *reloc)
{
kern_return_t rval = KERN_FAILURE;
const KXLDSect *sect = NULL;
const KXLDSym *sym = NULL;
kxld_addr_t target = 0;
kxld_addr_t pair_target = 0;
+ char *demangled_name = NULL;
+ size_t demangled_length = 0;
check(_target);
check(_pair_target);
- check(sectarray);
- check(symtab);
*_target = 0;
*_pair_target = 0;
reloc->pair_target_type == KXLD_TARGET_VALUE,
finish, rval=KERN_FAILURE);
- rval = get_target_by_address_lookup(&target, reloc->target, sectarray);
+ rval = get_target_by_address_lookup(&target, reloc->target,
+ relocator->sectarray);
require_noerr(rval, finish);
if (reloc->pair_target_type == KXLD_TARGET_LOOKUP) {
rval = get_target_by_address_lookup(&pair_target,
- reloc->pair_target, sectarray);
+ reloc->pair_target, relocator->sectarray);
require_noerr(rval, finish);
} else if (reloc->pair_target_type == KXLD_TARGET_VALUE) {
pair_target = reloc->pair_target;
finish, rval=KERN_FAILURE);
/* Get the target's section by section number */
- sect = kxld_array_get_item(sectarray, reloc->target);
+ sect = kxld_array_get_item(relocator->sectarray, reloc->target);
require_action(sect, finish, rval=KERN_FAILURE);
/* target is the change in the section's address */
rval=KERN_FAILURE);
/* Get the target's symbol by symbol number */
- sym = kxld_symtab_get_symbol_by_index(symtab, reloc->target);
+ sym = kxld_symtab_get_symbol_by_index(relocator->symtab, reloc->target);
require_action(sym, finish, rval=KERN_FAILURE);
+
+ /* If this symbol is a padslot that has already been replaced, then the
+ * only way a relocation entry can still reference it is if there is a
+ * vtable that has not been patched. The vtable patcher uses the
+ * MetaClass structure to find classes for patching, so an unpatched
+ * vtable means that there is an OSObject-dervied class that is missing
+ * its OSDeclare/OSDefine macros.
+ */
+ require_action(!kxld_sym_is_padslot(sym) || !kxld_sym_is_replaced(sym),
+ finish, rval=KERN_FAILURE;
+ kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogRelocatingPatchedSym,
+ kxld_demangle(sym->name, &demangled_name, &demangled_length)));
+
target = sym->link_addr;
+ if (kxld_sym_is_vtable(sym)) {
+ relocator->current_vtable = kxld_dict_find(relocator->vtables, sym->name);
+ }
+
/* Some relocation types need the GOT entry address instead of the
* symbol's actual address. These types don't have pair relocation
* entries, so we store the GOT entry address as the pair target.
if (reloc->pair_target_type == KXLD_TARGET_VALUE) {
pair_target = reloc->pair_target;
} else if (reloc->pair_target_type == KXLD_TARGET_SYMBOLNUM ) {
- sym = kxld_symtab_get_symbol_by_index(symtab, reloc->pair_target);
+ sym = kxld_symtab_get_symbol_by_index(relocator->symtab,
+ reloc->pair_target);
require_action(sym, finish, rval=KERN_FAILURE);
pair_target = sym->link_addr;
} else if (reloc->pair_target_type == KXLD_TARGET_GOT) {
rval = KERN_SUCCESS;
finish:
+ if (demangled_name) kxld_free(demangled_name, demangled_length);
return rval;
}
end = start + sect->size;
if (start <= addr && addr < end) break;
+
+ sect = NULL;
}
- require_action(i < sectarray->nitems, finish,
- rval=KERN_FAILURE);
+ require_action(sect, finish, rval=KERN_FAILURE);
*target = sect->link_addr - sect->base_addr;
rval = KERN_SUCCESS;
return rval;
}
-#if KXLD_USER_OR_I386
/*******************************************************************************
*******************************************************************************/
-static boolean_t
-generic_reloc_has_pair(u_int _type)
+static kern_return_t
+check_for_direct_pure_virtual_call(const KXLDRelocator *relocator, u_long offset)
{
- enum reloc_type_generic type = _type;
+ kern_return_t rval = KERN_FAILURE;
+ const KXLDVTableEntry *entry = NULL;
+
+ if (relocator->current_vtable) {
+ entry = kxld_vtable_get_entry_for_offset(relocator->current_vtable,
+ offset, relocator->is_32_bit);
+ require_action(!entry || !entry->patched.name ||
+ !kxld_sym_name_is_pure_virtual(entry->patched.name),
+ finish, rval=KERN_FAILURE;
+ kxld_log(kKxldLogLinking, kKxldLogErr,
+ kKxldLogDirectPureVirtualCall));
+ }
- return (type == GENERIC_RELOC_SECTDIFF ||
- type == GENERIC_RELOC_LOCAL_SECTDIFF);
+ rval = KERN_SUCCESS;
+finish:
+ return rval;
}
+#if KXLD_PIC_KEXTS
/*******************************************************************************
*******************************************************************************/
-static boolean_t
-generic_reloc_is_pair(u_int _type, u_int _prev_type __unused)
+static u_long
+get_macho_data_size_for_array(const KXLDArray *relocs)
{
- enum reloc_type_generic type = _type;
+ const KXLDReloc *reloc = NULL;
+ u_int i = 0;
+ u_long size = 0;
- return (type == GENERIC_RELOC_PAIR);
-}
+ check(relocs);
-/*******************************************************************************
-*******************************************************************************/
-static boolean_t generic_reloc_has_got(u_int _type __unused)
-{
- return FALSE;
+ for (i = 0; i < relocs->nitems; ++i) {
+ reloc = kxld_array_get_item(relocs, i);
+ if (!reloc->pcrel) {
+ size += sizeof(struct relocation_info);
+ if(reloc->pair_target_type != KXLD_TARGET_NONE) {
+ size += sizeof(struct relocation_info);
+ }
+ }
+ }
+
+ return size;
}
/*******************************************************************************
*******************************************************************************/
-static kern_return_t
-generic_process_reloc(u_char *instruction, u_int length, u_int pcrel,
- kxld_addr_t _base_pc, kxld_addr_t _link_pc, kxld_addr_t _link_disp __unused,
- u_int _type, kxld_addr_t _target, kxld_addr_t _pair_target,
- boolean_t swap __unused)
+static kern_return_t
+export_macho_for_array(const KXLDRelocator *relocator,
+ const KXLDArray *relocs, struct relocation_info **dstp)
{
kern_return_t rval = KERN_FAILURE;
- uint32_t base_pc = (uint32_t) _base_pc;
- uint32_t link_pc = (uint32_t) _link_pc;
- uint32_t *instr_addr = NULL;
- uint32_t instr_data = 0;
- uint32_t target = (uint32_t) _target;
- uint32_t pair_target = (uint32_t) _pair_target;
- enum reloc_type_generic type = _type;
+ const KXLDReloc *reloc = NULL;
+ struct relocation_info *dst = NULL;
+ struct scattered_relocation_info *scatdst = NULL;
+ u_int i = 0;
- check(instruction);
- require_action(length == 2, finish, rval=KERN_FAILURE);
+ dst = *dstp;
- if (pcrel) target = target + base_pc - link_pc;
+ for (i = 0; i < relocs->nitems; ++i) {
+ reloc = kxld_array_get_item(relocs, i);
+ scatdst = (struct scattered_relocation_info *) dst;
- instr_addr = (uint32_t *)instruction;
- instr_data = *instr_addr;
+ if (reloc->pcrel) {
+ continue;
+ }
-#if !KERNEL
- if (swap) instr_data = OSSwapInt32(instr_data);
-#endif
+ switch (reloc->target_type) {
+ case KXLD_TARGET_LOOKUP:
+ scatdst->r_address = reloc->address;
+ scatdst->r_pcrel = reloc->pcrel;
+ scatdst->r_length = reloc->length;
+ scatdst->r_type = reloc->reloc_type;
+ scatdst->r_value = reloc->target;
+ scatdst->r_scattered = 1;
+ break;
+ case KXLD_TARGET_SECTNUM:
+ dst->r_address = reloc->address;
+ dst->r_pcrel = reloc->pcrel;
+ dst->r_length = reloc->length;
+ dst->r_type = reloc->reloc_type;
+ dst->r_symbolnum = reloc->target + 1;
+ dst->r_extern = 0;
+ break;
+ case KXLD_TARGET_SYMBOLNUM:
+ /* Assume that everything will be slid together; otherwise,
+ * there is no sensible value for the section number.
+ */
+ dst->r_address = reloc->address;
+ dst->r_pcrel = reloc->pcrel;
+ dst->r_length = reloc->length;
+ dst->r_type = reloc->reloc_type;
+ dst->r_symbolnum = 1;
+ dst->r_extern = 0;
+ break;
+ default:
+ rval = KERN_FAILURE;
+ goto finish;
+ }
- switch (type) {
- case GENERIC_RELOC_VANILLA:
- instr_data += target;
- break;
- case GENERIC_RELOC_SECTDIFF:
- case GENERIC_RELOC_LOCAL_SECTDIFF:
- instr_data = instr_data + target - pair_target;
- break;
- case GENERIC_RELOC_PB_LA_PTR:
- rval = KERN_FAILURE;
- goto finish;
- case GENERIC_RELOC_PAIR:
- default:
- rval = KERN_FAILURE;
- goto finish;
+ ++dst;
+
+ if(reloc->pair_target_type != KXLD_TARGET_NONE) {
+ ++i;
+ require_action(i < relocs->nitems, finish, rval=KERN_FAILURE);
+ scatdst = (struct scattered_relocation_info *) dst;
+ switch (reloc->pair_target_type) {
+ case KXLD_TARGET_LOOKUP:
+ scatdst->r_address = reloc->pair_address;
+ scatdst->r_pcrel = reloc->pcrel;
+ scatdst->r_length = reloc->length;
+ scatdst->r_type = relocator->reloc_get_pair_type(reloc->reloc_type);
+ scatdst->r_value = reloc->pair_target;
+ scatdst->r_scattered = 1;
+ break;
+ case KXLD_TARGET_SECTNUM:
+ dst->r_address = reloc->pair_address;
+ dst->r_pcrel = reloc->pcrel;
+ dst->r_length = reloc->length;
+ dst->r_type = relocator->reloc_get_pair_type(reloc->reloc_type);
+ dst->r_symbolnum = reloc->pair_target + 1;
+ dst->r_extern = 0;
+ break;
+ case KXLD_TARGET_SYMBOLNUM:
+ dst->r_address = reloc->pair_address;
+ dst->r_pcrel = reloc->pcrel;
+ dst->r_length = reloc->length;
+ dst->r_type = relocator->reloc_get_pair_type(reloc->reloc_type);
+ dst->r_symbolnum = 1;
+ dst->r_extern = 0;
+ break;
+ default:
+ rval = KERN_FAILURE;
+ goto finish;
+ }
+ ++dst;
+ }
}
-#if !KERNEL
- if (swap) instr_data = OSSwapInt32(instr_data);
-#endif
-
- *instr_addr = instr_data;
-
rval = KERN_SUCCESS;
-
finish:
+ *dstp = dst;
return rval;
}
-#endif /* KXLD_USER_OR_I386 */
+#endif /* KXLD_PIC_KEXTS */
-#if KXLD_USER_OR_PPC
+#if KXLD_USER_OR_I386
/*******************************************************************************
*******************************************************************************/
static boolean_t
-ppc_reloc_has_pair(u_int _type)
+generic_reloc_has_pair(u_int _type)
{
- enum reloc_type_ppc type = _type;
+ enum reloc_type_generic type = _type;
- switch(type) {
- case PPC_RELOC_HI16:
- case PPC_RELOC_LO16:
- case PPC_RELOC_HA16:
- case PPC_RELOC_LO14:
- case PPC_RELOC_JBSR:
- case PPC_RELOC_SECTDIFF:
- return TRUE;
- default:
- return FALSE;
- }
+ return (type == GENERIC_RELOC_SECTDIFF ||
+ type == GENERIC_RELOC_LOCAL_SECTDIFF);
}
/*******************************************************************************
*******************************************************************************/
-static boolean_t
-ppc_reloc_is_pair(u_int _type, u_int _prev_type __unused)
+static u_int
+generic_reloc_get_pair_type(u_int _prev_type __unused)
{
- enum reloc_type_ppc type = _type;
-
- return (type == PPC_RELOC_PAIR);
+ return GENERIC_RELOC_PAIR;
}
/*******************************************************************************
*******************************************************************************/
-static boolean_t ppc_reloc_has_got(u_int _type __unused)
+static boolean_t generic_reloc_has_got(u_int _type __unused)
{
return FALSE;
}
/*******************************************************************************
*******************************************************************************/
-static kern_return_t
-ppc_process_reloc(u_char *instruction, u_int length, u_int pcrel,
- kxld_addr_t _base_pc, kxld_addr_t _link_pc, kxld_addr_t _link_disp __unused,
- u_int _type, kxld_addr_t _target, kxld_addr_t _pair_target __unused,
- boolean_t swap __unused)
+static kern_return_t
+generic_process_reloc(const KXLDRelocator *relocator, u_char *instruction,
+ u_int length, u_int pcrel, kxld_addr_t _base_pc, kxld_addr_t _link_pc,
+ kxld_addr_t _link_disp __unused, u_int _type, kxld_addr_t _target,
+ kxld_addr_t _pair_target, boolean_t swap __unused)
{
kern_return_t rval = KERN_FAILURE;
- uint32_t *instr_addr = NULL;
- uint32_t instr_data = 0;
uint32_t base_pc = (uint32_t) _base_pc;
uint32_t link_pc = (uint32_t) _link_pc;
+ uint32_t *instr_addr = NULL;
+ uint32_t instr_data = 0;
uint32_t target = (uint32_t) _target;
uint32_t pair_target = (uint32_t) _pair_target;
- int32_t addend = 0;
- int32_t displacement = 0;
- uint32_t difference = 0;
- uint32_t br14_disp_sign = 0;
- enum reloc_type_ppc type = _type;
+ enum reloc_type_generic type = _type;
check(instruction);
- require_action(length == 2 || length == 3, finish,
- rval=KERN_FAILURE);
+ require_action(length == 2, finish, rval=KERN_FAILURE);
- if (pcrel) displacement = target + base_pc - link_pc;
+ if (pcrel) target = target + base_pc - link_pc;
- instr_addr = (uint32_t *)instruction;
+ instr_addr = (uint32_t *) ((void *) instruction);
instr_data = *instr_addr;
-
+
#if !KERNEL
if (swap) instr_data = OSSwapInt32(instr_data);
#endif
- switch (type) {
- case PPC_RELOC_VANILLA:
- require_action(!pcrel, finish, rval=KERN_FAILURE);
+ rval = check_for_direct_pure_virtual_call(relocator, instr_data);
+ require_noerr(rval, finish);
+ switch (type) {
+ case GENERIC_RELOC_VANILLA:
instr_data += target;
break;
- case PPC_RELOC_BR14:
- require_action(pcrel, finish, rval=KERN_FAILURE);
-
- addend = BR14D(instr_data);
- displacement += SIGN_EXTEND(addend, BR14_NBITS_DISPLACEMENT);
- difference = ABSOLUTE_VALUE(displacement);
- require_action(difference < BR14_LIMIT, finish,
- rval=KERN_FAILURE;
- kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogRelocationOverflow));
-
-
- br14_disp_sign = BIT15(instr_data);
- instr_data = BR14I(instr_data) | BR14D(displacement);
-
- /* If this is a predicted conditional branch (signified by an
- * instruction length of 3) that is not branch-always, and the sign of
- * the displacement is different after relocation, then flip the y-bit
- * to preserve the branch prediction
- */
- if ((length == 3) &&
- IS_COND_BR_INSTR(instr_data) &&
- IS_NOT_ALWAYS_TAKEN(instr_data) &&
- (BIT15(instr_data) != br14_disp_sign))
- {
- FLIP_PREDICT_BIT(instr_data);
- }
- break;
- case PPC_RELOC_BR24:
- require_action(pcrel, finish, rval=KERN_FAILURE);
-
- addend = BR24D(instr_data);
- displacement += SIGN_EXTEND(addend, BR24_NBITS_DISPLACEMENT);
- difference = ABSOLUTE_VALUE(displacement);
- require_action(difference < BR24_LIMIT, finish,
- rval=KERN_FAILURE;
- kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogRelocationOverflow));
-
- instr_data = BR24I(instr_data) | BR24D(displacement);
- break;
- case PPC_RELOC_HI16:
- require_action(!pcrel, finish, rval=KERN_FAILURE);
-
- target += LO16S(instr_data) | LO16(pair_target);
- instr_data = HI16(instr_data) | HI16S(target);
- break;
- case PPC_RELOC_LO16:
- require_action(!pcrel, finish, rval=KERN_FAILURE);
-
- target += LO16S(pair_target) | LO16(instr_data);
- instr_data = HI16(instr_data) | LO16(target);
- break;
- case PPC_RELOC_HA16:
- require_action(!pcrel, finish, rval=KERN_FAILURE);
-
- instr_data -= BIT15(pair_target) ? 1 : 0;
- target += LO16S(instr_data) | LO16(pair_target);
- instr_data = HI16(instr_data) | HI16S(target);
- instr_data += BIT15(target) ? 1 : 0;
- break;
- case PPC_RELOC_JBSR:
- require_action(!pcrel, finish, rval=KERN_FAILURE);
-
- /* The generated code as written branches to an island that loads the
- * absolute address of the target. If we can branch to the target
- * directly with less than 24 bits of displacement, we modify the branch
- * instruction to do so which avoids the cost of the island.
- */
-
- displacement = target + pair_target - link_pc;
- difference = ABSOLUTE_VALUE(displacement);
- if (difference < BR24_LIMIT) {
- instr_data = BR24I(instr_data) | BR24D(displacement);
- }
- break;
- case PPC_RELOC_SECTDIFF:
- require_action(!pcrel, finish, rval=KERN_FAILURE);
-
+ case GENERIC_RELOC_SECTDIFF:
+ case GENERIC_RELOC_LOCAL_SECTDIFF:
instr_data = instr_data + target - pair_target;
break;
- case PPC_RELOC_LO14:
- case PPC_RELOC_PB_LA_PTR:
- case PPC_RELOC_HI16_SECTDIFF:
- case PPC_RELOC_LO16_SECTDIFF:
- case PPC_RELOC_HA16_SECTDIFF:
- case PPC_RELOC_LO14_SECTDIFF:
- case PPC_RELOC_LOCAL_SECTDIFF:
+ case GENERIC_RELOC_PB_LA_PTR:
rval = KERN_FAILURE;
goto finish;
- case PPC_RELOC_PAIR:
+ case GENERIC_RELOC_PAIR:
default:
rval = KERN_FAILURE;
goto finish;
*instr_addr = instr_data;
rval = KERN_SUCCESS;
-finish:
+finish:
return rval;
}
-#endif /* KXLD_USER_OR_PPC */
+#endif /* KXLD_USER_OR_I386 */
#if KXLD_USER_OR_X86_64
/*******************************************************************************
/*******************************************************************************
*******************************************************************************/
-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 __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;
}
/*******************************************************************************
/*******************************************************************************
*******************************************************************************/
static kern_return_t
-x86_64_process_reloc(u_char *instruction, u_int length, u_int pcrel,
- kxld_addr_t _base_pc __unused, kxld_addr_t _link_pc, kxld_addr_t _link_disp,
- u_int _type, kxld_addr_t _target, kxld_addr_t _pair_target,
- boolean_t swap __unused)
+x86_64_process_reloc(const KXLDRelocator *relocator __unused, u_char *instruction,
+ u_int length, u_int pcrel, kxld_addr_t _base_pc __unused,
+ kxld_addr_t _link_pc, kxld_addr_t _link_disp, u_int _type,
+ kxld_addr_t _target, kxld_addr_t _pair_target, boolean_t swap __unused)
{
kern_return_t rval = KERN_FAILURE;
enum reloc_type_x86_64 type = _type;
finish, rval=KERN_FAILURE);
if (length == 2) {
- instr32p = (int32_t *) instruction;
+ instr32p = (int32_t *) ((void *) instruction);
instr32 = *instr32p;
#if !KERNEL
if (swap) instr32 = OSSwapInt32(instr32);
#endif
+ rval = check_for_direct_pure_virtual_call(relocator, instr32);
+ require_noerr(rval, finish);
+
/* There are a number of different small adjustments for pc-relative
* relocation entries. The general case is to subtract the size of the
* relocation (represented by the length parameter), and it applies to
*instr32p = instr32;
} else {
- instr64p = (uint64_t *) instruction;
+ instr64p = (uint64_t *) ((void *) instruction);
instr64 = *instr64p;
#if !KERNEL
if (swap) instr64 = OSSwapInt64(instr64);
#endif
+ rval = check_for_direct_pure_virtual_call(relocator, (u_long) instr64);
+ require_noerr(rval, finish);
+
switch (type) {
case X86_64_RELOC_UNSIGNED:
require_action(!pcrel, finish, rval=KERN_FAILURE);
/*******************************************************************************
*******************************************************************************/
-static boolean_t
-arm_reloc_is_pair(u_int _type, u_int _prev_type __unused)
+static u_int
+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;
}
/*******************************************************************************
/*******************************************************************************
*******************************************************************************/
static kern_return_t
-arm_process_reloc(u_char *instruction, u_int length, u_int pcrel,
- kxld_addr_t _base_pc __unused, kxld_addr_t _link_pc __unused, kxld_addr_t _link_disp __unused,
- u_int _type __unused, kxld_addr_t _target __unused, kxld_addr_t _pair_target __unused,
- boolean_t swap __unused)
+arm_process_reloc(const KXLDRelocator *relocator __unused, u_char *instruction,
+ u_int length, u_int pcrel, kxld_addr_t _base_pc __unused,
+ kxld_addr_t _link_pc __unused, kxld_addr_t _link_disp __unused,
+ u_int _type __unused, kxld_addr_t _target __unused,
+ kxld_addr_t _pair_target __unused, boolean_t swap __unused)
{
kern_return_t rval = KERN_FAILURE;
uint32_t *instr_addr = NULL;
if (pcrel) displacement = target + base_pc - link_pc;
- instr_addr = (uint32_t *)instruction;
+ instr_addr = (uint32_t *) ((void *) instruction);
instr_data = *instr_addr;
#if !KERNEL
if (swap) instr_data = OSSwapInt32(instr_data);
#endif
+ rval = check_for_direct_pure_virtual_call(relocator, instr_data);
+ require_noerr(rval, finish);
+
switch (type) {
case ARM_RELOC_VANILLA:
- require_action(!pcrel, finish, rval=KERN_FAILURE);
instr_data += target;
break;
#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 */