]> git.saurik.com Git - apple/xnu.git/blobdiff - libkern/kxld/kxld_sect.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / libkern / kxld / kxld_sect.c
diff --git a/libkern/kxld/kxld_sect.c b/libkern/kxld/kxld_sect.c
new file mode 100644 (file)
index 0000000..0c286b5
--- /dev/null
@@ -0,0 +1,629 @@
+/*
+ * 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
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * 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,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * 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>
+#include <mach-o/loader.h>
+#include <mach-o/reloc.h>
+#include <sys/types.h>
+
+#define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld"
+#include <AssertMacros.h>
+
+#include "kxld_reloc.h"
+#include "kxld_sect.h"
+#include "kxld_seg.h"
+#include "kxld_symtab.h"
+#include "kxld_util.h"
+
+static kern_return_t export_macho(const KXLDSect *sect, u_char *buf, u_long offset, 
+    u_long bufsize, boolean_t is_32_bit);
+#if KXLD_USER_OR_ILP32
+static kern_return_t sect_export_macho_header_32(const KXLDSect *sect, u_char *buf, 
+    u_long *header_offset, u_long header_size, u_long data_offset);
+#endif
+#if KXLD_USER_OR_LP64
+static kern_return_t sect_export_macho_header_64(const KXLDSect *sect, u_char *buf, 
+    u_long *header_offset, u_long header_size, u_long data_offset);
+#endif
+
+#if KXLD_USER_OR_ILP32
+/*******************************************************************************
+*******************************************************************************/
+kern_return_t
+kxld_sect_init_from_macho_32(KXLDSect *sect, u_char *macho, u_long *sect_offset,
+    u_int sectnum, const KXLDRelocator *relocator)
+{
+    kern_return_t rval = KERN_FAILURE;
+    struct section *src = (struct section *) (macho + *sect_offset);
+    struct relocation_info *relocs = NULL;
+
+    check(sect);
+    check(macho);
+    check(src);
+
+    strlcpy(sect->segname, src->segname, sizeof(sect->segname));
+    strlcpy(sect->sectname, src->sectname, sizeof(sect->sectname));
+    sect->base_addr = src->addr;
+    sect->link_addr = src->addr;
+    sect->size = src->size;
+    sect->sectnum = sectnum;
+    sect->flags = src->flags;
+    sect->align = src->align;
+    sect->reserved1 = src->reserved1;
+    sect->reserved2 = src->reserved2;
+
+    if (src->offset) {
+        sect->data = macho + src->offset;
+    } else {
+        sect->data = NULL;
+    }
+
+    relocs = (struct relocation_info *) (macho + src->reloff);
+
+    rval = kxld_reloc_create_macho(&sect->relocs, relocator, 
+        relocs, src->nreloc);
+    require_noerr(rval, finish);
+
+    *sect_offset += sizeof(*src);
+    rval = KERN_SUCCESS;
+
+finish:
+    if (rval) kxld_sect_deinit(sect);
+
+    return rval;
+}
+#endif /* KXLD_USER_OR_ILP32 */
+
+#if KXLD_USER_OR_LP64
+/*******************************************************************************
+*******************************************************************************/
+kern_return_t
+kxld_sect_init_from_macho_64(KXLDSect *sect, u_char *macho, u_long *sect_offset,
+    u_int sectnum, const KXLDRelocator *relocator)
+{
+    kern_return_t rval = KERN_FAILURE;
+    struct section_64 *src = (struct section_64 *) (macho + *sect_offset);
+    struct relocation_info *relocs = NULL;
+
+    check(sect);
+    check(macho);
+    check(src);
+
+    strlcpy(sect->segname, src->segname, sizeof(sect->segname));
+    strlcpy(sect->sectname, src->sectname, sizeof(sect->sectname));
+    sect->base_addr = src->addr;
+    sect->link_addr = src->addr;
+    sect->size = src->size;
+    sect->sectnum = sectnum;
+    sect->flags = src->flags;
+    sect->align = src->align;
+    sect->reserved1 = src->reserved1;
+    sect->reserved2 = src->reserved2;
+
+    if (src->offset) {
+        sect->data = macho + src->offset;
+    } else {
+        sect->data = NULL;
+    }
+
+    relocs = (struct relocation_info *) (macho + src->reloff);
+
+    rval = kxld_reloc_create_macho(&sect->relocs, relocator, 
+        relocs, src->nreloc);
+    require_noerr(rval, finish);
+
+    *sect_offset += sizeof(*src);
+    rval = KERN_SUCCESS;
+
+finish:
+    if (rval) kxld_sect_deinit(sect);
+
+    return rval;
+}
+#endif /* KXLD_USER_OR_LP64 */
+
+#if KXLD_USER_OR_GOT
+/*******************************************************************************
+* Assumes GOT is comprised of kxld_addr_t entries
+*******************************************************************************/
+kern_return_t
+kxld_sect_init_got(KXLDSect *sect, u_int ngots)
+{
+    kern_return_t rval = KERN_FAILURE;
+
+    check(sect);
+
+    strlcpy(sect->segname, KXLD_SEG_GOT, sizeof(sect->segname));
+    strlcpy(sect->sectname, KXLD_SECT_GOT, sizeof(sect->sectname));
+    sect->base_addr = 0;
+    sect->link_addr = 0;
+    sect->flags = 0;
+    sect->align = 4;
+    sect->reserved1 = 0;
+    sect->reserved2 = 0;
+
+    sect->size = ngots * sizeof(kxld_addr_t);
+    sect->data = kxld_alloc((u_long) sect->size);
+    require_action(sect->data, finish, rval=KERN_RESOURCE_SHORTAGE);
+
+    sect->allocated = TRUE;
+
+    rval = KERN_SUCCESS;
+
+finish:
+    return rval;
+}
+#endif /* KXLD_USER_OR_GOT */
+
+#if KXLD_USER_OR_COMMON
+/*******************************************************************************
+*******************************************************************************/
+void
+kxld_sect_init_zerofill(KXLDSect *sect, const char *segname, 
+    const char *sectname, kxld_size_t size, u_int align)
+{
+    check(sect);
+    check(segname);
+    check(sectname);
+
+    strlcpy(sect->segname, segname, sizeof(sect->segname));
+    strlcpy(sect->sectname, sectname, sizeof(sect->sectname));
+    sect->size = size;
+    sect->align = align;
+    sect->base_addr = 0;
+    sect->link_addr = 0;
+    sect->flags = S_ZEROFILL;
+}
+#endif /* KXLD_USER_OR_COMMON */
+
+/*******************************************************************************
+*******************************************************************************/
+void
+kxld_sect_clear(KXLDSect *sect)
+{
+    check(sect);
+
+    if (sect->allocated) {
+        kxld_free(sect->data, (u_long) sect->size);
+        sect->allocated = FALSE;
+    }
+
+    bzero(sect->sectname, sizeof(sect->sectname));
+    bzero(sect->segname, sizeof(sect->segname));
+    sect->data = NULL;
+    sect->base_addr = 0;
+    sect->link_addr = 0;
+    sect->size = 0;
+    sect->flags = 0;
+    sect->align = 0;
+    sect->reserved1 = 0;
+    sect->reserved2 = 0;
+    kxld_array_clear(&sect->relocs);
+}
+
+/*******************************************************************************
+*******************************************************************************/
+void
+kxld_sect_deinit(KXLDSect *sect)
+{
+    check(sect);
+
+    if (streq_safe(sect->sectname, KXLD_SECT_GOT, sizeof(KXLD_SECT_GOT))) {
+        kxld_free(sect->data, (u_long) sect->size);
+    }
+
+    kxld_array_deinit(&sect->relocs);
+    bzero(sect, sizeof(*sect));
+}
+
+/*******************************************************************************
+*******************************************************************************/
+u_int 
+kxld_sect_get_num_relocs(const KXLDSect *sect)
+{
+    check(sect);
+
+    return sect->relocs.nitems;
+}
+
+/*******************************************************************************
+*******************************************************************************/
+u_long
+kxld_sect_get_macho_header_size(boolean_t is_32_bit)
+{
+    if (is_32_bit) {
+        return sizeof(struct section);
+    } else {
+        return sizeof(struct section_64);
+    }
+}
+
+/*******************************************************************************
+*******************************************************************************/
+u_long
+kxld_sect_get_macho_data_size(const KXLDSect *sect)
+{
+    u_long size = 0;
+
+    check(sect);
+
+    if (sect->data) {
+        size = (u_long) sect->size;
+    }
+
+    return size;
+}
+
+#if KXLD_USER_OR_GOT
+/*******************************************************************************
+*******************************************************************************/
+u_int
+kxld_sect_get_ngots(const KXLDSect *sect, const KXLDRelocator *relocator,
+    const KXLDSymtab *symtab)
+{
+    const KXLDReloc *reloc = NULL;
+    KXLDSym *sym = NULL;
+    u_int ngots = 0;
+    u_int i = 0;
+    
+    for (i = 0; i < sect->relocs.nitems; ++i) {
+        reloc = kxld_array_get_item(&sect->relocs, i);
+
+        if (relocator->reloc_has_got(reloc->reloc_type)) {
+            /* @TODO This assumes 64-bit symbols (which is valid at the
+             * moment since only x86_64 has a GOT)
+             */
+            sym = kxld_reloc_get_symbol(relocator, reloc, sect->data, symtab);
+            if (!kxld_sym_is_got(sym)) {
+                kxld_sym_set_got(sym);
+                ++ngots;
+            }
+        }
+    }
+
+    return ngots;
+}
+#endif /* KXLD_USER_OR_GOT */
+
+/*******************************************************************************
+* Each section must be aligned at a certain power of two.  To figure out that 
+* alignment, we mask for the low bits that may need to be adjusted.  If they are 
+* non zero, we then subtract them from the target alignment to find the offset, 
+* and then add that offset to the link address.
+*******************************************************************************/
+kxld_addr_t
+kxld_sect_align_address(const KXLDSect *sect, kxld_addr_t address)
+{
+    return kxld_align_address(address, sect->align);
+}
+
+/*******************************************************************************
+*******************************************************************************/
+kern_return_t
+kxld_sect_export_macho_to_file_buffer(const KXLDSect *sect, u_char *buf,
+    u_long *header_offset, u_long header_size, u_long *data_offset, 
+    u_long data_size, boolean_t is_32_bit)
+{
+    kern_return_t rval = KERN_FAILURE;
+
+    check(sect);
+    check(buf);
+    check(header_offset);
+    check(data_offset);
+
+    /* If there is no data to export, we only need to write the header.  We
+     * make it a separate call so that we don't modify data_offset.
+     */
+    if (!sect->data) {
+        KXLD_3264_FUNC(is_32_bit, rval,
+            sect_export_macho_header_32, sect_export_macho_header_64,
+            sect, buf, header_offset, header_size, /* data_offset */ 0);
+        require_noerr(rval, finish);
+    } else {
+        *data_offset = (u_long) kxld_sect_align_address(sect, *data_offset);
+
+        KXLD_3264_FUNC(is_32_bit, rval,
+            sect_export_macho_header_32, sect_export_macho_header_64,
+            sect, buf, header_offset, header_size, *data_offset);
+        require_noerr(rval, finish);
+
+        rval = export_macho(sect, buf, *data_offset, data_size, is_32_bit);
+        require_noerr(rval, finish);
+
+        *data_offset += (u_long) sect->size;
+    }
+        
+    rval = KERN_SUCCESS;
+
+finish:
+    return rval;
+}
+
+/*******************************************************************************
+*******************************************************************************/
+kern_return_t
+kxld_sect_export_macho_to_vm(const KXLDSect *sect, u_char *buf, 
+    u_long *header_offset, u_long header_size, 
+    kxld_addr_t link_addr, u_long data_size, 
+    boolean_t is_32_bit)
+{
+    kern_return_t rval = KERN_FAILURE;
+    u_long data_offset = (u_long) (sect->link_addr - link_addr);
+
+    check(sect);
+    check(buf);
+    check(header_offset);
+
+    KXLD_3264_FUNC(is_32_bit, rval,
+        sect_export_macho_header_32, sect_export_macho_header_64,
+        sect, buf, header_offset, header_size, data_offset);
+    require_noerr(rval, finish);
+
+    rval = export_macho(sect, buf, data_offset, data_size, is_32_bit);
+    require_noerr(rval, finish);
+
+    rval = KERN_SUCCESS;
+
+finish:
+    return rval;
+}
+
+/*******************************************************************************
+*******************************************************************************/
+static kern_return_t
+export_macho(const KXLDSect *sect, u_char *buf, u_long offset, u_long bufsize,
+    boolean_t is_32_bit)
+{
+    kern_return_t rval = KERN_FAILURE;
+
+    check(sect);
+    check(buf);
+
+    if (!sect->data) {
+        rval = KERN_SUCCESS;
+        goto finish;
+    }
+
+    /* Verify that the section is properly aligned */
+
+    require_action(kxld_sect_align_address(sect, offset) == offset, finish,
+        rval = KERN_FAILURE);
+
+    /* Verify that we have enough space to copy */
+
+    require_action(sect->size <= bufsize - offset, finish,
+        rval=KERN_FAILURE);
+
+    /* Copy section data */
+
+    switch (sect->flags & SECTION_TYPE) {
+    case S_NON_LAZY_SYMBOL_POINTERS:
+    case S_MOD_INIT_FUNC_POINTERS:
+    case S_MOD_TERM_FUNC_POINTERS:
+        require_action(!is_32_bit, finish, rval=KERN_FAILURE;
+            kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogMalformedMachO
+                "Invalid section type in 32-bit kext: %u.", 
+                sect->flags & SECTION_TYPE));
+        /* Fall through */
+    case S_REGULAR:
+    case S_CSTRING_LITERALS:
+    case S_4BYTE_LITERALS:
+    case S_8BYTE_LITERALS:
+    case S_LITERAL_POINTERS:
+    case S_COALESCED:
+    case S_16BYTE_LITERALS:
+        memcpy(buf + offset, sect->data, (size_t)sect->size);
+        break;
+    case S_ZEROFILL: /* sect->data should be NULL, so we'll never get here */
+    case S_LAZY_SYMBOL_POINTERS:
+    case S_SYMBOL_STUBS:
+    case S_GB_ZEROFILL:
+    case S_INTERPOSING:
+    case S_DTRACE_DOF:
+    default:
+        rval = KERN_FAILURE;
+        kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogMalformedMachO
+            "Invalid section type: %u.", sect->flags & SECTION_TYPE);
+        goto finish;
+    }
+
+    rval = KERN_SUCCESS;
+
+finish:
+    return rval;
+}
+
+#if KXLD_USER_OR_ILP32
+/*******************************************************************************
+*******************************************************************************/
+static kern_return_t
+sect_export_macho_header_32(const KXLDSect *sect, u_char *buf, 
+    u_long *header_offset, u_long header_size, u_long data_offset)
+{
+    kern_return_t rval = KERN_FAILURE;
+    struct section *secthdr = NULL;
+
+    check(sect);
+    check(buf);
+    check(header_offset);
+    
+    require_action(sizeof(*secthdr) <= header_size - *header_offset, finish,
+        rval=KERN_FAILURE);
+    secthdr = (struct section *) (buf + *header_offset);
+    *header_offset += sizeof(*secthdr);
+
+    /* Initalize header */
+
+    strlcpy(secthdr->sectname, sect->sectname, sizeof(secthdr->sectname));
+    strlcpy(secthdr->segname, sect->segname, sizeof(secthdr->segname));
+    secthdr->addr = (uint32_t) sect->link_addr;
+    secthdr->size = (uint32_t) sect->size;
+    secthdr->offset = (uint32_t) ((sect->data) ? data_offset : 0);
+    secthdr->align = sect->align;
+    secthdr->reloff = 0;
+    secthdr->nreloc = 0;
+    secthdr->flags = sect->flags;
+    secthdr->reserved1 = sect->reserved1;
+    secthdr->reserved2 = sect->reserved2;
+
+    rval = KERN_SUCCESS;
+
+finish:
+    return rval;
+}
+#endif /* KXLD_USER_OR_ILP32 */
+
+#if KXLD_USER_OR_LP64
+/*******************************************************************************
+*******************************************************************************/
+static kern_return_t
+sect_export_macho_header_64(const KXLDSect *sect, u_char *buf, 
+    u_long *header_offset, u_long header_size, u_long data_offset)
+{
+    kern_return_t rval = KERN_FAILURE;
+    struct section_64 *secthdr = NULL;
+
+    check(sect);
+    check(buf);
+    check(header_offset);
+    
+    require_action(sizeof(*secthdr) <= header_size - *header_offset, finish,
+        rval=KERN_FAILURE);
+    secthdr = (struct section_64 *) (buf + *header_offset);
+    *header_offset += sizeof(*secthdr);
+
+    /* Initalize header */
+
+    strlcpy(secthdr->sectname, sect->sectname, sizeof(secthdr->sectname));
+    strlcpy(secthdr->segname, sect->segname, sizeof(secthdr->segname));
+    secthdr->addr = (uint64_t) sect->link_addr;
+    secthdr->size = (uint64_t) sect->size;
+    secthdr->offset = (uint32_t) ((sect->data) ? data_offset : 0);
+    secthdr->align = sect->align;
+    secthdr->reloff = 0;
+    secthdr->nreloc = 0;
+    secthdr->flags = sect->flags;
+    secthdr->reserved1 = sect->reserved1;
+    secthdr->reserved2 = sect->reserved2;
+
+    rval = KERN_SUCCESS;
+
+finish:
+    return rval;
+}
+#endif /* KXLD_USER_OR_LP64 */
+
+#if KXLD_USER_OR_COMMON
+/*******************************************************************************
+*******************************************************************************/
+kxld_size_t
+kxld_sect_grow(KXLDSect *sect, kxld_size_t nbytes, u_int align)
+{
+    kxld_size_t size = kxld_align_address(sect->size, align);
+
+    if (align > sect->align) sect->align = align;
+    sect->size = size + nbytes;
+
+    return size;
+}
+#endif /* KXLD_USER_OR_COMMON */
+
+/*******************************************************************************
+*******************************************************************************/
+void
+kxld_sect_relocate(KXLDSect *sect, kxld_addr_t link_addr)
+{
+    sect->link_addr = kxld_sect_align_address(sect, 
+        sect->link_addr + link_addr);
+}
+
+#if KXLD_USER_OR_GOT
+/*******************************************************************************
+*******************************************************************************/
+kern_return_t
+kxld_sect_populate_got(KXLDSect *sect, KXLDSymtab *symtab, 
+    boolean_t swap __unused)
+{
+    kern_return_t rval = KERN_FAILURE;
+    KXLDSymtabIterator iter;
+    KXLDSym *sym = NULL;
+    kxld_addr_t *entry = NULL;
+    kxld_addr_t entry_addr = 0;
+
+    check(sect);
+    check(symtab);
+    require(streq_safe(sect->segname, KXLD_SEG_GOT, sizeof(KXLD_SEG_GOT)), 
+        finish);
+    require(streq_safe(sect->sectname, KXLD_SECT_GOT, sizeof(KXLD_SECT_GOT)), 
+        finish);
+
+    kxld_symtab_iterator_init(&iter, symtab, kxld_sym_is_got, FALSE);
+    
+    entry = (kxld_addr_t *) sect->data;
+    entry_addr = sect->link_addr;
+    while ((sym = kxld_symtab_iterator_get_next(&iter))) {
+        *entry = sym->link_addr;
+        sym->got_addr = entry_addr;
+
+#if !KERNEL
+        if (swap) *entry = OSSwapInt64(*entry);
+#endif /* !KERNEL */
+
+        ++entry;
+        entry_addr += sizeof(*entry);
+    }
+
+    rval = KERN_SUCCESS;
+
+finish:
+    return rval;
+}
+#endif /* KXLD_USER_OR_GOT */
+
+/*******************************************************************************
+*******************************************************************************/
+kern_return_t
+kxld_sect_process_relocs(KXLDSect *sect, const KXLDRelocator *relocator,
+    const KXLDArray *sectarray, const KXLDSymtab *symtab)
+{
+    kern_return_t rval = KERN_FAILURE;
+    KXLDReloc *reloc = NULL;
+    u_int i = 0;
+
+    for (i = 0; i < sect->relocs.nitems; ++i) {
+        reloc = kxld_array_get_item(&sect->relocs, i);
+        rval = kxld_relocator_process_sect_reloc(relocator, reloc, sect, 
+            sectarray, symtab);
+        require_noerr(rval, finish);
+    }
+
+    rval = KERN_SUCCESS;
+
+finish:
+    return rval;
+}
+