/* * 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 #include #include #include #define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld" #include #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); #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 *) ((void *) (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 *) ((void *) (macho + src->reloff)); rval = kxld_reloc_create_macho(§->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 *) ((void *) (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 *) ((void *) (macho + src->reloff)); rval = kxld_reloc_create_macho(§->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(§->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(§->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(§->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 __unused) { 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); 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 __unused) { 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); 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) { 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: 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: case S_SYMBOL_STUBS: 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_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 *) ((void *) (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 *) ((void *) (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, KXLDRelocator *relocator) { 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(§->relocs, i); rval = kxld_relocator_process_sect_reloc(relocator, reloc, sect); require_noerr(rval, finish); } rval = KERN_SUCCESS; finish: return rval; }