/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_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. 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_LICENSE_HEADER_END@ */ #ifndef RLD #include #include #include #include "stuff/ofile.h" #include "stuff/breakout.h" #include "stuff/allocate.h" #include "stuff/errors.h" #include "stuff/round.h" #include "stuff/crc32.h" static void breakout_internal( char *filename, struct arch **archs, uint32_t *narchs, enum bool calculate_input_prebind_cksum, struct ofile *ofile); static void breakout_loop_through_archive( char *filename, struct arch *arch, struct ofile *ofile); static void cksum_object( struct arch *arch, enum bool calculate_input_prebind_cksum); static struct arch *new_arch( struct arch **archs, uint32_t *narchs); static struct member *new_member( struct arch *arch); __private_extern__ struct ofile * breakout_mem( void *membuf, uint32_t length, char *filename, struct arch **archs, uint32_t *narchs, enum bool calculate_input_prebind_cksum) { struct ofile *ofile; uint32_t previous_errors; *archs = NULL; *narchs = 0; ofile = allocate(sizeof(struct ofile)); /* * If the file_name is NULL, we will use a dummy file name so * that error reporting, etc. works. */ if(filename == NULL) filename = "(broken out from memory)"; /* * Rely on the ofile_*() routines to do all the checking and only * return valid ofiles files broken out. */ if(ofile_map_from_memory((char *)membuf, length, filename, NULL, NULL, ofile, FALSE) == FALSE){ free(ofile); return(NULL); } previous_errors = errors; breakout_internal(filename, archs, narchs, calculate_input_prebind_cksum, ofile); errors += previous_errors; if(errors != 0){ free(ofile); return(NULL); } return(ofile); } __private_extern__ struct ofile * breakout( char *filename, struct arch **archs, uint32_t *narchs, enum bool calculate_input_prebind_cksum) { struct ofile *ofile; uint32_t previous_errors; *archs = NULL; *narchs = 0; ofile = allocate(sizeof(struct ofile)); /* * Rely on the ofile_*() routines to do all the checking and only * return valid ofiles files broken out. */ if(ofile_map(filename, NULL, NULL, ofile, FALSE) == FALSE){ free(ofile); return(NULL); } previous_errors = errors; breakout_internal(filename, archs, narchs, calculate_input_prebind_cksum, ofile); errors += previous_errors; if(errors != 0){ free(ofile); return(NULL); } return(ofile); } static void breakout_internal( char *filename, struct arch **archs, uint32_t *narchs, enum bool calculate_input_prebind_cksum, struct ofile *ofile) { struct arch *arch; errors = 0; if(ofile->file_type == OFILE_FAT && errors == 0){ /* loop through the fat architectures (can't have zero archs) */ (void)ofile_first_arch(ofile); do{ if(errors != 0) break; arch = new_arch(archs, narchs); arch->file_name = savestr(filename); arch->type = ofile->arch_type; arch->fat_arch = ofile->fat_archs + ofile->narch; arch->fat_arch_name = savestr(ofile->arch_flag.name); if(ofile->arch_type == OFILE_ARCHIVE){ breakout_loop_through_archive(filename, arch, ofile); } else if(ofile->arch_type == OFILE_Mach_O){ arch->object = allocate(sizeof(struct object)); memset(arch->object, '\0', sizeof(struct object)); arch->object->object_addr = ofile->object_addr; arch->object->object_size = ofile->object_size; arch->object->object_byte_sex = ofile->object_byte_sex; arch->object->mh64 = ofile->mh64; arch->object->mh = ofile->mh; arch->object->mh_filetype = ofile->mh_filetype; arch->object->mh_cputype = ofile->mh_cputype; arch->object->mh_cpusubtype = ofile->mh_cpusubtype; arch->object->load_commands = ofile->load_commands; cksum_object(arch, calculate_input_prebind_cksum); } else{ /* ofile->arch_type == OFILE_UNKNOWN */ arch->unknown_addr = ofile->file_addr + arch->fat_arch->offset; arch->unknown_size = arch->fat_arch->size; } }while(ofile_next_arch(ofile) == TRUE); } else if(ofile->file_type == OFILE_ARCHIVE && errors == 0){ arch = new_arch(archs, narchs); arch->file_name = savestr(filename); arch->type = ofile->file_type; breakout_loop_through_archive(filename, arch, ofile); } else if(ofile->file_type == OFILE_Mach_O && errors == 0){ arch = new_arch(archs, narchs); arch->file_name = savestr(filename); arch->type = ofile->file_type; arch->object = allocate(sizeof(struct object)); memset(arch->object, '\0', sizeof(struct object)); arch->object->object_addr = ofile->object_addr; arch->object->object_size = ofile->object_size; arch->object->object_byte_sex = ofile->object_byte_sex; arch->object->mh64 = ofile->mh64; arch->object->mh = ofile->mh; arch->object->mh_filetype = ofile->mh_filetype; arch->object->mh_cputype = ofile->mh_cputype; arch->object->mh_cpusubtype = ofile->mh_cpusubtype; arch->object->load_commands = ofile->load_commands; cksum_object(arch, calculate_input_prebind_cksum); } else if(errors == 0){ /* ofile->file_type == OFILE_UNKNOWN */ arch = new_arch(archs, narchs); arch->file_name = savestr(filename); arch->type = ofile->file_type; arch->unknown_addr = ofile->file_addr; arch->unknown_size = ofile->file_size; } if(errors != 0){ free_archs(*archs, *narchs); *archs = NULL; *narchs = 0; } } static void breakout_loop_through_archive( char *filename, struct arch *arch, struct ofile *ofile) { struct member *member; enum bool flag; struct ar_hdr *ar_hdr; uint32_t size, ar_name_size; char ar_name_buf[sizeof(ofile->member_ar_hdr->ar_name) + 1]; char ar_size_buf[sizeof(ofile->member_ar_hdr->ar_size) + 1]; /* loop through archive (can be empty) */ if((flag = ofile_first_member(ofile)) == TRUE && errors == 0){ /* * If the first member is a table of contents then skip * it as it is always rebuilt (so to get the time to * match the modtime so it won't appear out of date). * Also force it to be a long name so members can be 8 byte * aligned. */ if(ofile->member_ar_hdr != NULL && strncmp(ofile->member_name, SYMDEF, sizeof(SYMDEF) - 1) == 0){ arch->toc_long_name = TRUE; flag = ofile_next_member(ofile); } while(flag == TRUE && errors == 0){ member = new_member(arch); member->type = ofile->member_type; member->member_name = ofile->member_name; /* * Determine the size this member will have in the library which * includes the padding as a result of rounding the size of the * member. To get all members on an 8 byte boundary (so that * mapping in object files can be used directly) the size of the * member is CHANGED to reflect this padding. In the UNIX * definition of archives the size of the member is never * changed but the offset to the next member is defined to be * the offset of the previous member plus the size of the * previous member rounded to 2. So to get 8 byte boundaries * without breaking the UNIX definition of archives the size is * changed here. As with the UNIX ar(1) program the padded * bytes will be set to the character '\n'. */ if(ofile->mh != NULL || ofile->mh64 != NULL) size = round(ofile->object_size, 8); else size = round(ofile->member_size, 8); /* * We will force the use of long names so we can make sure the * size of the name and the size of struct ar_hdr are rounded to * 8 bytes. And that rounded size is what will be in the * ar_name with the AR_EFMT1 string. To avoid growing the size * of names first trim the name size before rounding up. */ member->member_long_name = TRUE; for(ar_name_size = ofile->member_name_size; ar_name_size > 1 ; ar_name_size--){ if(ofile->member_name[ar_name_size - 1] != '\0') break; } member->member_name_size = ar_name_size; ar_name_size = round(ar_name_size, 8) + (round(sizeof(struct ar_hdr), 8) - sizeof(struct ar_hdr)); size += ar_name_size; /* * Now with the output sizes of the long member name and rounded * size of the member the offset to this member can be set and * then left incremented for the next member's offset. */ member->offset = arch->library_size; arch->library_size += sizeof(struct ar_hdr) + size; /* * Since we are rounding the member size and forcing a the use * of a long name make a new ar_hdr with this information. * Note the code in writeout() will do the padding with '\n' * characters as needed. */ ar_hdr = allocate(sizeof(struct ar_hdr)); *ar_hdr = *(ofile->member_ar_hdr); sprintf(ar_name_buf, "%s%-*lu", AR_EFMT1, (int)(sizeof(ar_hdr->ar_name) - (sizeof(AR_EFMT1) - 1)), (long unsigned int)ar_name_size); memcpy(ar_hdr->ar_name, ar_name_buf, sizeof(ar_hdr->ar_name)); sprintf(ar_size_buf, "%-*ld", (int)sizeof(ar_hdr->ar_size), (long unsigned int)size); memcpy(ar_hdr->ar_size, ar_size_buf, sizeof(ar_hdr->ar_size)); member->ar_hdr = ar_hdr; member->input_ar_hdr = ofile->member_ar_hdr; member->input_file_name = filename; if(ofile->member_type == OFILE_Mach_O){ member->object = allocate(sizeof(struct object)); memset(member->object, '\0', sizeof(struct object)); member->object->object_addr = ofile->object_addr; member->object->object_size = ofile->object_size; member->object->object_byte_sex = ofile->object_byte_sex; member->object->mh64 = ofile->mh64; member->object->mh = ofile->mh; member->object->mh_filetype = ofile->mh_filetype; member->object->mh_cputype = ofile->mh_cputype; member->object->mh_cpusubtype = ofile->mh_cpusubtype; member->object->load_commands = ofile->load_commands; } else{ /* ofile->member_type == OFILE_UNKNOWN */ member->unknown_addr = ofile->member_addr; member->unknown_size = ofile->member_size; } flag = ofile_next_member(ofile); } } } /* * cksum_object() is called to set the pointer to the LC_PREBIND_CKSUM load * command in the object struct for the specified arch. If the parameter * calculate_input_prebind_cksum is TRUE then calculate the value * of the check sum for the input object if needed, set that into the * the calculated_input_prebind_cksum field of the object struct for the * specified arch. This is needed for prebound files where the original * checksum (or zero) is recorded in the LC_PREBIND_CKSUM load command. * Only redo_prebinding operations sets the value of the cksum field to * non-zero and only if previously zero. All other operations will set this * field to zero indicating a new original prebound file. */ static void cksum_object( struct arch *arch, enum bool calculate_input_prebind_cksum) { uint32_t i, buf_size, ncmds; struct load_command *lc; enum byte_sex host_byte_sex; char *buf; arch->object->cs = NULL; lc = arch->object->load_commands; if(arch->object->mh != NULL) ncmds = arch->object->mh->ncmds; else ncmds = arch->object->mh64->ncmds; for(i = 0; i < ncmds && arch->object->cs == NULL; i++){ if(lc->cmd == LC_PREBIND_CKSUM) arch->object->cs = (struct prebind_cksum_command *)lc; lc = (struct load_command *)((char *)lc + lc->cmdsize); } /* * If we don't want to calculate the input check sum, or there is no * LC_PREBIND_CKSUM load command or there is one and the check sum is * not zero then return. */ if(calculate_input_prebind_cksum == FALSE || arch->object->cs == NULL || arch->object->cs->cksum != 0) return; host_byte_sex = get_host_byte_sex(); buf_size = 0; buf = NULL; if(arch->object->object_byte_sex != host_byte_sex){ if(arch->object->mh != NULL){ buf_size = sizeof(struct mach_header) + arch->object->mh->sizeofcmds; buf = allocate(buf_size); memcpy(buf, arch->object->mh, buf_size); if(swap_object_headers(arch->object->mh, arch->object->load_commands) == FALSE) return; } else{ buf_size = sizeof(struct mach_header_64) + arch->object->mh64->sizeofcmds; buf = allocate(buf_size); memcpy(buf, arch->object->mh64, buf_size); if(swap_object_headers(arch->object->mh64, arch->object->load_commands) == FALSE) return; } } arch->object->calculated_input_prebind_cksum = crc32(arch->object->object_addr, arch->object->object_size); if(arch->object->object_byte_sex != host_byte_sex){ if(arch->object->mh != NULL) memcpy(arch->object->mh, buf, buf_size); else memcpy(arch->object->mh64, buf, buf_size); free(buf); } } __private_extern__ void free_archs( struct arch *archs, uint32_t narchs) { uint32_t i, j; for(i = 0; i < narchs; i++){ if(archs[i].type == OFILE_ARCHIVE){ for(j = 0; j < archs[i].nmembers; j++){ if(archs[i].members[j].type == OFILE_Mach_O){ if(archs[i].members[j].object->ld_r_ofile != NULL) ofile_unmap(archs[i].members[j].object->ld_r_ofile); free(archs[i].members[j].object); } } if(archs[i].nmembers > 0 && archs[i].members != NULL) free(archs[i].members); } else if(archs[i].type == OFILE_Mach_O){ if(archs[i].object->ld_r_ofile != NULL) ofile_unmap(archs[i].object->ld_r_ofile); free(archs[i].object); } } if(narchs > 0 && archs != NULL) free(archs); } static struct arch * new_arch( struct arch **archs, uint32_t *narchs) { struct arch *arch; *archs = reallocate(*archs, (*narchs + 1) * sizeof(struct arch)); arch = *archs + *narchs; *narchs = *narchs + 1; memset(arch, '\0', sizeof(struct arch)); return(arch); } static struct member * new_member( struct arch *arch) { struct member *member; arch->members = reallocate(arch->members, (arch->nmembers + 1) * sizeof(struct member)); member = arch->members + arch->nmembers; arch->nmembers++; memset(member, '\0', sizeof(struct member)); return(member); } #endif /* !defined(RLD) */