X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/39236c6e673c41db228275375ab7fdb0f837b292..3903760236c30e3b5ace7a4eefac3a269d68957c:/libkern/kxld/kxld_seg.c diff --git a/libkern/kxld/kxld_seg.c b/libkern/kxld/kxld_seg.c index 7160f7e7d..f512c2b90 100644 --- a/libkern/kxld/kxld_seg.c +++ b/libkern/kxld/kxld_seg.c @@ -50,6 +50,9 @@ #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, @@ -114,11 +117,12 @@ kxld_seg_init_from_macho_64(KXLDSeg *seg, struct segment_command_64 *src) 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); @@ -195,7 +199,7 @@ kxld_seg_finalize_object_segment(KXLDArray *segarray, KXLDArray *section_order, /* 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 */ @@ -209,7 +213,7 @@ kxld_seg_finalize_object_segment(KXLDArray *segarray, KXLDArray *section_order, /* 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: @@ -420,7 +424,7 @@ kxld_seg_init_linkedit(KXLDArray *segs) 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; @@ -469,7 +473,7 @@ kxld_size_t kxld_seg_get_vmsize(const KXLDSeg *seg) { check(seg); - + return seg->vmsize; } @@ -511,7 +515,7 @@ kxld_seg_get_macho_data_size(const KXLDSeg *seg) size += kxld_sect_get_macho_data_size(sect); } - return round_page(size); + return kxld_round_page_cross_safe(size); } #endif @@ -572,7 +576,7 @@ kxld_seg_export_macho_to_file_buffer(const KXLDSeg *seg, u_char *buf, 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; @@ -581,28 +585,38 @@ finish: } + /******************************************************************************* *******************************************************************************/ 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 */ @@ -610,9 +624,9 @@ kxld_seg_export_macho_to_vm(const KXLDSeg *seg, u_char *buf, 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; @@ -654,6 +668,21 @@ seg_export_macho_header_32(const KXLDSeg *seg, u_char *buf, 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: @@ -677,6 +706,22 @@ seg_export_macho_header_64(const KXLDSeg *seg, u_char *buf, 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); @@ -694,6 +739,33 @@ seg_export_macho_header_64(const KXLDSeg *seg, u_char *buf, 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: @@ -743,8 +815,15 @@ kxld_seg_finish_init(KXLDSeg *seg) 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); @@ -753,8 +832,9 @@ kxld_seg_finish_init(KXLDSeg *seg) 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; @@ -768,14 +848,8 @@ finish: 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 { @@ -795,11 +869,91 @@ kxld_seg_relocate(KXLDSeg *seg, kxld_addr_t link_addr) { 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); + } } } @@ -812,7 +966,8 @@ kxld_seg_populate_linkedit(KXLDSeg *seg, const KXLDSymtab *symtab, boolean_t is_ , const KXLDArray *extrelocs , boolean_t target_supports_slideable_kexts #endif /* KXLD_PIC_KEXTS */ - ) + , uint32_t splitinfolc_size + ) { u_long size = 0; @@ -824,6 +979,82 @@ kxld_seg_populate_linkedit(KXLDSeg *seg, const KXLDSymtab *symtab, boolean_t is_ } #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; }