#define TEXT_SEG_PROT (VM_PROT_READ | VM_PROT_EXECUTE)
#define DATA_SEG_PROT (VM_PROT_READ | VM_PROT_WRITE)
+extern boolean_t isSplitKext;
+extern boolean_t isOldInterface;
+
#if KXLD_USER_OR_OBJECT
static kern_return_t reorder_sections(KXLDSeg *seg, KXLDArray *section_order);
static void reorder_section(KXLDArray *sects, u_int *sect_reorder_index,
seg->base_addr = src->vmaddr;
seg->link_addr = src->vmaddr;
seg->vmsize = src->vmsize;
+
seg->fileoff = src->fileoff;
seg->maxprot = src->maxprot;
seg->initprot = src->initprot;
seg->flags = src->flags;
-
+
rval = kxld_array_init(&seg->sects, sizeof(KXLDSect *), src->nsects);
require_noerr(rval, finish);
/* Set the initial link address at the end of the header pages */
- seg->link_addr = round_page(hdrsize);
+ seg->link_addr = kxld_round_page_cross_safe(hdrsize);
/* Fix up all of the section addresses */
/* Finish initializing the segment */
- seg->vmsize = round_page(sect_offset) - seg->link_addr;
+ seg->vmsize = kxld_round_page_cross_safe(sect_offset) - seg->link_addr;
rval = KERN_SUCCESS;
finish:
le = kxld_array_get_item(segs, 1);
strlcpy(le->segname, SEG_LINKEDIT, sizeof(le->segname));
- le->link_addr = round_page(seg->link_addr + seg->vmsize);
+ le->link_addr = kxld_round_page_cross_safe(seg->link_addr + seg->vmsize);
le->maxprot = VM_PROT_ALL;
le->initprot = VM_PROT_DEFAULT;
kxld_seg_get_vmsize(const KXLDSeg *seg)
{
check(seg);
-
+
return seg->vmsize;
}
size += kxld_sect_get_macho_data_size(sect);
}
- return round_page(size);
+ return kxld_round_page_cross_safe(size);
}
#endif
hdr64->filesize = (uint64_t) (*data_offset - base_data_offset);
}
- *data_offset = round_page(*data_offset);
+ *data_offset = (u_long)kxld_round_page_cross_safe(*data_offset);
rval = KERN_SUCCESS;
}
+
/*******************************************************************************
*******************************************************************************/
kern_return_t
-kxld_seg_export_macho_to_vm(const KXLDSeg *seg, u_char *buf,
- u_long *header_offset, u_long header_size,
- u_long data_size, kxld_addr_t file_link_addr,
- boolean_t is_32_bit)
+kxld_seg_export_macho_to_vm(const KXLDSeg *seg,
+ u_char *buf,
+ u_long *header_offset,
+ u_long header_size,
+ u_long data_size,
+ kxld_addr_t file_link_addr,
+ boolean_t is_32_bit)
{
- kern_return_t rval = KERN_FAILURE;
- KXLDSect *sect = NULL;
- u_long data_offset = (u_long) (seg->link_addr - file_link_addr);
- u_int i = 0;
+ kern_return_t rval = KERN_FAILURE;
+ KXLDSect * sect = NULL;
+
+ // data_offset is used to set fileoff field in segment header
+ u_long data_offset;
+ u_int i = 0;
check(seg);
check(buf);
check(header_offset);
+
+ data_offset = (u_long) (seg->link_addr - file_link_addr);
/* Write out the header */
- KXLD_3264_FUNC(is_32_bit, rval,
- seg_export_macho_header_32, seg_export_macho_header_64,
- seg, buf, header_offset, header_size, data_offset);
+ KXLD_3264_FUNC(is_32_bit, rval,
+ seg_export_macho_header_32, seg_export_macho_header_64,
+ seg,
+ buf,
+ header_offset, header_size, data_offset);
require_noerr(rval, finish);
/* Write out each section */
for (i = 0; i < seg->sects.nitems; ++i) {
sect = get_sect_by_index(seg, i);
- rval = kxld_sect_export_macho_to_vm(sect, buf, header_offset,
- header_size, file_link_addr, data_size, is_32_bit);
- require_noerr(rval, finish);
+ rval = kxld_sect_export_macho_to_vm(sect, buf, header_offset,
+ header_size, file_link_addr, data_size, is_32_bit);
+ require_noerr(rval, finish);
}
rval = KERN_SUCCESS;
seghdr->nsects = seg->sects.nitems;
seghdr->flags = 0;
+#if SPLIT_KEXTS_DEBUG
+ {
+ kxld_log(kKxldLogLinking, kKxldLogErr,
+ "segname %s seghdr %p vmaddr %p vmsize 0x%02X %u fileoff 0x%02X %u <%s>",
+ seg->segname[0] ? seg->segname : "none",
+ (void *) seghdr,
+ (void *) ((uint64_t)seghdr->vmaddr),
+ seghdr->vmsize,
+ seghdr->vmsize,
+ seghdr->fileoff,
+ seghdr->fileoff,
+ __func__);
+ }
+#endif
+
rval = KERN_SUCCESS;
finish:
require_action(sizeof(*seghdr) <= header_size - *header_offset, finish,
rval=KERN_FAILURE);
+
+#if SPLIT_KEXTS_DEBUG
+ {
+ struct mach_header_64 *mach;
+
+ mach = (struct mach_header_64 *) ((void *) buf);
+
+ if (mach->magic != MH_MAGIC_64) {
+ kxld_log(kKxldLogLinking, kKxldLogErr,
+ "bad macho header at %p <%s>",
+ (void *) mach, __func__);
+ goto finish;
+ }
+ }
+#endif
+
seghdr = (struct segment_command_64 *) ((void *) (buf + *header_offset));
*header_offset += sizeof(*seghdr);
seghdr->nsects = seg->sects.nitems;
seghdr->flags = 0;
+#if SPLIT_KEXTS_DEBUG
+ {
+ kxld_log(kKxldLogLinking, kKxldLogErr,
+ "%p >>> Start of %s seghdr (size %lu) <%s>",
+ (void *) seghdr,
+ seg->segname[0] ? seg->segname : "none",
+ sizeof(*seghdr),
+ __func__);
+ kxld_log(kKxldLogLinking, kKxldLogErr,
+ "%p <<< End of %s seghdr <%s>",
+ (void *) ((u_char *)seghdr + sizeof(*seghdr)),
+ seg->segname[0] ? seg->segname : "none",
+ __func__);
+
+ kxld_log(kKxldLogLinking, kKxldLogErr,
+ "%s seghdr, cmdsize %d vmaddr %p vmsize %p %llu fileoff %p %llu <%s>",
+ seg->segname[0] ? seg->segname : "none",
+ seghdr->cmdsize,
+ (void *) seghdr->vmaddr,
+ (void *) seghdr->vmsize,
+ seghdr->vmsize,
+ (void *) seghdr->fileoff,
+ seghdr->fileoff,
+ __func__);
+ }
+#endif
+
rval = KERN_SUCCESS;
finish:
KXLDSect *sect = NULL;
kxld_addr_t maxaddr = 0;
kxld_size_t maxsize = 0;
-
- if (seg->sects.nitems) {
+
+ /* If we already have a size for this segment (e.g. from the mach-o load
+ * command) then don't recalculate the segment size. This is safer since
+ * when we recalculate we are making assumptions about page alignment and
+ * padding that the kext mach-o file was built with. Better to trust the
+ * macho-o info, if we have it. If we don't (i.e. vmsize == 0) then add up
+ * the section sizes and take a best guess at page padding.
+ */
+ if ((seg->vmsize == 0) && (seg->sects.nitems)) {
for (i = 0; i < seg->sects.nitems; ++i) {
sect = get_sect_by_index(seg, i);
require_action(sect, finish, rval=KERN_FAILURE);
maxsize = sect->size;
}
}
+ seg->vmsize = kxld_round_page_cross_safe(maxaddr +
+ maxsize - seg->base_addr);
- seg->vmsize = round_page(maxaddr + maxsize - seg->base_addr);
}
rval = KERN_SUCCESS;
void
kxld_seg_set_vm_protections(KXLDSeg *seg, boolean_t strict_protections)
{
- /* This is unnecessary except to make the clang analyzer happy. When
- * the analyzer no longer ignores nonnull attributes for if statements,
- * we can remove this line.
- */
- if (!seg) return;
-
if (strict_protections) {
- if (streq_safe(seg->segname, SEG_TEXT, const_strlen(SEG_TEXT))) {
+ if (!strncmp(seg->segname, SEG_TEXT, sizeof(seg->segname))) {
seg->initprot = TEXT_SEG_PROT;
seg->maxprot = TEXT_SEG_PROT;
} else {
{
KXLDSect *sect = NULL;
u_int i = 0;
-
- seg->link_addr += link_addr;
+ splitKextLinkInfo * link_info = (splitKextLinkInfo *) link_addr;
+ kxld_addr_t my_link_addr;
+
+ if (isOldInterface) {
+ seg->link_addr += link_addr;
+ }
+ else {
+ if (isSplitKext) {
+ // we have a split kext
+ if (kxld_seg_is_text_seg(seg)) {
+ // assumes this is the beginning of the kext
+ my_link_addr = link_info->vmaddr_TEXT;
+ seg->link_addr = my_link_addr;
+ }
+ else if (kxld_seg_is_text_exec_seg(seg)) {
+ my_link_addr = link_info->vmaddr_TEXT_EXEC;
+ seg->link_addr = my_link_addr;
+ // vmaddr_TEXT_EXEC is the actual vmaddr for this segment so we need
+ // to adjust for kxld_sect_relocate assuming the link addr is
+ // the address of the kext (macho header in __TEXT)
+ my_link_addr -= seg->base_addr;
+ }
+ else if (kxld_seg_is_data_seg(seg)) {
+ my_link_addr = link_info->vmaddr_DATA;
+ seg->link_addr = my_link_addr;
+ // vmaddr_DATA is the actual vmaddr for this segment so we need
+ // to adjust for kxld_sect_relocate assuming the link addr is
+ // the address of the kext (macho header in __TEXT)
+ my_link_addr -= seg->base_addr;
+ }
+ else if (kxld_seg_is_data_const_seg(seg)) {
+ my_link_addr = link_info->vmaddr_DATA_CONST;
+ seg->link_addr = my_link_addr;
+ // vmaddr_DATA_CONST is the actual vmaddr for this segment so we need
+ // to adjust for kxld_sect_relocate assuming the link addr is
+ // the address of the kext (macho header in __TEXT)
+ my_link_addr -= seg->base_addr;
+ }
+ else if (kxld_seg_is_linkedit_seg(seg)) {
+ my_link_addr = link_info->vmaddr_LINKEDIT;
+ seg->link_addr = my_link_addr;
+ // vmaddr_DATA is the actual vmaddr for this segment so we need
+ // to adjust for kxld_sect_relocate assuming the link addr is
+ // the address of the kext (macho header in __TEXT)
+ my_link_addr -= seg->base_addr;
+ }
+ else {
+ kxld_log(kKxldLogLinking, kKxldLogErr,
+ " not expecting this segment %s!!! <%s>",
+ seg->segname[0] ? seg->segname : "none",
+ __func__);
+ my_link_addr = link_info->vmaddr_TEXT;
+ seg->link_addr += my_link_addr;
+ }
+ }
+ else {
+ my_link_addr = link_info->vmaddr_TEXT;
+ seg->link_addr += my_link_addr;
+ }
+ }
+
+#if SPLIT_KEXTS_DEBUG
+ {
+ kxld_log(kKxldLogLinking, kKxldLogErr,
+ "%p >>> Start of %s segment (vmsize %llu) <%s>)",
+ (void *) seg->link_addr,
+ seg->segname[0] ? seg->segname : "none",
+ seg->vmsize,
+ __func__);
+ kxld_log(kKxldLogLinking, kKxldLogErr,
+ "%p <<< End of %s segment <%s>",
+ (void *) (seg->link_addr + seg->vmsize),
+ seg->segname[0] ? seg->segname : "none",
+ __func__);
+ }
+#endif
+
for (i = 0; i < seg->sects.nitems; ++i) {
sect = get_sect_by_index(seg, i);
- kxld_sect_relocate(sect, link_addr);
+ if (isOldInterface) {
+ kxld_sect_relocate(sect, link_addr);
+ }
+ else {
+ kxld_sect_relocate(sect, my_link_addr);
+ }
}
}
, const KXLDArray *extrelocs
, boolean_t target_supports_slideable_kexts
#endif /* KXLD_PIC_KEXTS */
- )
+ , uint32_t splitinfolc_size
+ )
{
u_long size = 0;
}
#endif /* KXLD_PIC_KEXTS */
- seg->vmsize = round_page(size);
+ // 0 unless this is a split kext
+ size += splitinfolc_size;
+
+ seg->vmsize = kxld_round_page_cross_safe(size);
+}
+
+/*******************************************************************************
+ *******************************************************************************/
+boolean_t
+kxld_seg_is_split_seg(const KXLDSeg *seg)
+{
+ boolean_t result = FALSE;
+
+ check(seg);
+ if (isSplitKext) {
+ if (kxld_seg_is_data_seg(seg) || kxld_seg_is_linkedit_seg(seg) ||
+ kxld_seg_is_text_exec_seg(seg) || kxld_seg_is_data_const_seg(seg)) {
+ result = TRUE;
+ }
+ }
+
+ return result;
+}
+
+boolean_t
+kxld_seg_is_text_seg(const KXLDSeg *seg)
+{
+ boolean_t result = FALSE;
+
+ check(seg);
+ result = !strncmp(seg->segname, SEG_TEXT, sizeof(seg->segname));
+
+ return result;
+}
+
+boolean_t
+kxld_seg_is_text_exec_seg(const KXLDSeg *seg)
+{
+ boolean_t result = FALSE;
+
+ check(seg);
+ result = !strncmp(seg->segname, "__TEXT_EXEC", sizeof(seg->segname));
+
+ return result;
+}
+
+boolean_t
+kxld_seg_is_data_seg(const KXLDSeg *seg)
+{
+ boolean_t result = FALSE;
+
+ check(seg);
+ result = !strncmp(seg->segname, SEG_DATA, sizeof(seg->segname));
+
+ return result;
+}
+
+boolean_t
+kxld_seg_is_data_const_seg(const KXLDSeg *seg)
+{
+ boolean_t result = FALSE;
+
+ check(seg);
+ result = !strncmp(seg->segname, "__DATA_CONST", sizeof(seg->segname));
+
+ return result;
+}
+
+boolean_t
+kxld_seg_is_linkedit_seg(const KXLDSeg *seg)
+{
+ boolean_t result = FALSE;
+
+ check(seg);
+ result = !strncmp(seg->segname, SEG_LINKEDIT, sizeof(seg->segname));
+
+ return result;
}