/* * 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@ */ #include #include #include #include #include #include #include #include #include #include "stuff/arch.h" #include "stuff/errors.h" #include "stuff/allocate.h" #include "libgcc.h" #include "mkshlib.h" extern char *mktemp(); /* * These are the names of the temporary files used for the branch table * assembly language source and resulting object. */ char *branch_source_filename; char *branch_object_filename; /* * This is the name of the temporary file for the library idenification * object file. */ char *lib_id_object_filename; /* * The names of the the assembly branch and trap instructions we are using for * the output file's architecture. */ static char *branch = NULL; static char *trap = NULL; static unsigned long branch_slot_size = 0; static void build_branch_object(void); static void build_lib_id_object(void); /* * Build the target shared library. */ void target(void) { int fd; unsigned long magic; struct mach_header mh; char text_addr_string[10], data_addr_string[10]; struct alias *ap; struct oddball *obp; unsigned long i; const struct arch_flag *family_arch_flag; /* * Determine the cputype and cupsubtype of the output file use the * specified -arch is one is specified. If not and if no object files * use the host architecture. If an -arch flag was not specified then * get the cputype and subtype from the first object file. If the * first object file is a fat file then -arch must be specified. */ if(arch_flag.name == NULL && nobject_list == 0) if(get_arch_from_host(&arch_flag, NULL) == 0) fatal("can't determine the host architecture (specify an " "-arch flag or fix get_arch_from_host() )"); if(arch_flag.name == NULL){ if((fd = open(object_list[0]->name, O_RDONLY, 0)) == -1) system_fatal("can't open object file: %s", object_list[0]->name); if(read(fd, &magic, sizeof(unsigned long)) != sizeof(unsigned long)) system_fatal("can't read magic number of object file: %s", object_list[0]->name); #ifdef __BIG_ENDIAN__ if(magic == FAT_MAGIC) #endif /* __BIG_ENDIAN__ */ #ifdef __LITTLE_ENDIAN__ if(magic == SWAP_LONG(FAT_MAGIC)) #endif /* __LITTLE_ENDIAN__ */ { fatal("-arch must be specified with use of fat files"); } else if(magic == MH_MAGIC || magic == SWAP_LONG(MH_MAGIC)){ lseek(fd, 0, L_SET); if(read(fd, &mh, sizeof(struct mach_header)) != sizeof(struct mach_header)) system_fatal("can't read mach_header of object file: " "%s", object_list[0]->name); if(magic == SWAP_LONG(MH_MAGIC)) swap_mach_header(&mh, host_byte_sex); arch_flag.cputype = mh.cputype; arch_flag.cpusubtype = mh.cpusubtype; } else fatal("first file listed in #object directive: %s is not " "an object file (bad magic number)", object_list[0]->name); close(fd); } if(arch_flag.cputype == CPU_TYPE_MC680x0){ branch = "jmp"; trap = "trapt.l\t#0xfeadface"; target_byte_sex = BIG_ENDIAN_BYTE_SEX; branch_slot_size = 6; } else if(arch_flag.cputype == CPU_TYPE_POWERPC){ branch = "b"; trap = ".long 0"; target_byte_sex = BIG_ENDIAN_BYTE_SEX; branch_slot_size = 4; } else if(arch_flag.cputype == CPU_TYPE_MC88000){ branch = "br"; trap = "illop1"; target_byte_sex = BIG_ENDIAN_BYTE_SEX; branch_slot_size = 4; } else if(arch_flag.cputype == CPU_TYPE_I386){ branch = "jmp"; trap = ".byte 0xf4,0xf4,0xf4,0xf4,0xf4"; target_byte_sex = LITTLE_ENDIAN_BYTE_SEX; branch_slot_size = 5; } else if(arch_flag.cputype == CPU_TYPE_HPPA){ trap = "break 0,0\nbreak 0,0"; target_byte_sex = BIG_ENDIAN_BYTE_SEX; branch_slot_size = 8; } else if(arch_flag.cputype == CPU_TYPE_SPARC){ branch = "ba,a"; trap = "ta 0"; target_byte_sex = BIG_ENDIAN_BYTE_SEX; branch_slot_size = 4; } else{ if(arch_flag.name == NULL) fatal("unknown or unsupported cputype (%d) in object file: " "%s", mh.cputype, object_list[0]->name); else fatal("unsupported architecture for -arch %s", arch_flag.name); } family_arch_flag = get_arch_family_from_cputype(arch_flag.cputype); if(family_arch_flag != NULL){ arch_flag.name = (char *)(family_arch_flag->name); arch_flag.cpusubtype = family_arch_flag->cpusubtype; } /* * Build the branch table object from the branch table data structrure * created from the #branch directive in the specification file. */ build_branch_object(); /* * Build the library identification object. */ build_lib_id_object(); /* * Check to see if all the objects listed exist and are readable */ for(i = 0; i < nobject_list; i++){ if(access(object_list[i]->name, R_OK) == -1) system_error("can't read object file: %s", object_list[i]->name); } if(errors) cleanup(0); /* * Link the branch table object will all the objects in the object data * structure created from the #objects directive in the specification * file, at the addresses specified in there creating the target shared * library. */ reset_runlist(); add_runlist(ldname); add_runlist("-arch"); add_runlist(arch_flag.name); add_runlist("-fvmlib"); for(i = 0; i < ODDBALL_HASH_SIZE; i++){ obp = oddball_hash[i]; while(obp != (struct oddball *)0){ if(obp->undefined){ add_runlist("-U"); add_runlist(obp->name); } obp = obp->next; } } ap = aliases; while(ap != (struct alias *)0){ add_runlist(makestr("-i", ap->alias_name, ":", ap->real_name, (char *)0) ); ap = ap->next; } add_runlist("-segaddr"); add_runlist(SEG_TEXT); sprintf(text_addr_string, "%x", (unsigned int)text_addr); add_runlist(text_addr_string); add_runlist("-segaddr"); add_runlist(SEG_DATA); sprintf(data_addr_string, "%x", (unsigned int)data_addr); add_runlist(data_addr_string); for(i = 0; i < nldflags; i++) add_runlist(ldflags[i]); add_runlist("-o"); add_runlist(target_filename); add_runlist(branch_object_filename); add_runlist(lib_id_object_filename); for(i = 0; i < nobject_list; i++) add_runlist(object_list[i]->name); if(run()) fatal("internal link edit using: %s failed", ldname); if(!dflag) unlink(lib_id_object_filename); } /* * Build the branch table object by writing an assembly source file and * assembling it. The assembly source has the form: * .text * .globl * : jmp * Where is some name (out of the name space for high level * languages) unique to that slot number and is the name of * symbol in that branch table slot. */ static void build_branch_object(void) { long i, text_size, pad; FILE *stream; char *p; struct shlib_info shlib; branch_source_filename = "branch.s"; branch_object_filename = "branch.o"; (void)unlink(branch_source_filename); if((stream = fopen(branch_source_filename, "w")) == NULL) system_fatal("can't create temporary file: %s for branch table " "assembly source", branch_source_filename); if(fprintf(stream, "\t.text\n") == EOF) system_fatal("can't write to temporary file: %s (branch table " "assembly source)", branch_source_filename); if(fprintf(stream, "\t.globl %s\n%s:\n", shlib_symbol_name, shlib_symbol_name) == EOF) system_fatal("can't write to temporary file: %s (branch table " "assembly source)", branch_source_filename); if(fprintf(stream, SHLIB_STRUCT, text_addr, 0, data_addr, 0, minor_version, target_name, sizeof(shlib.name) - (strlen(target_name) + 1)) == EOF) system_fatal("can't write to temporary file: %s (branch table " "assembly source)", branch_source_filename); for(i = 0; i < nbranch_list; i++){ if(branch_list[i]->empty_slot){ if(fprintf(stream, "\t%s\n", trap) == EOF) system_fatal("can't write to temporary file: %s (branch " "table assembly source)", branch_source_filename); } else{ p = branch_slot_symbol(i); if(fprintf(stream, "\t.globl %s\n%s:",p,p) == EOF) system_fatal("can't write to temporary file: %s (branch " "table assembly source)", branch_source_filename); /* special case for hppa, since this machine needs to use * multiple instructions to reach the branch target. */ if(arch_flag.cputype == CPU_TYPE_HPPA) { if (fprintf(stream,"\tldil L`%s,%%r1\n\tbe,n R`%s(%%sr4,%%r1)\n", branch_list[i]->name, branch_list[i]->name) == EOF) system_fatal("can't write to temporary file: %s (branch " "table assembly source)", branch_source_filename); } else { if (fprintf(stream,"\t%s\t%s\n", branch,branch_list[i]->name) == EOF) system_fatal("can't write to temporary file: %s (branch " "table assembly source)", branch_source_filename); } } } /* * The old assembler use to pad all sections to 4 bytes. Since the new * assemble does not and we need this for compatiblity the pad is added * here. */ text_size = sizeof(struct shlib_info) + nbranch_list * branch_slot_size; pad = round(text_size, 4) - text_size; for(i = 0; i < pad; i++){ if(fprintf(stream, "\t.byte 0\n") == EOF) system_fatal("can't write to temporary file: %s (branch table " "assembly source)", branch_source_filename); } if(fclose(stream) == EOF) system_fatal("can't close temporary file: %s for branch table " "assembly source", branch_source_filename); /* * Assemble the source for the branch table and create the object file. */ reset_runlist(); add_runlist(asname); add_runlist("-arch"); add_runlist(arch_flag.name); add_runlist("-o"); add_runlist(branch_object_filename); add_runlist(branch_source_filename); if(run()) fatal("internal assembly using: %s failed", asname); } /* * Build the library identification object. This is done by directly writing * the object file. This object file just contains the library identification * load command. */ static void build_lib_id_object(void) { int fd; long fvmlib_name_size, object_size, offset; char *buffer; struct mach_header *mh; struct fvmlib_command *fvmlib; if(dflag){ lib_id_object_filename = "lib_id.o"; } else{ lib_id_object_filename = mktemp(savestr("mkshlib_lid_XXXXXX")); } (void)unlink(lib_id_object_filename); if((fd = open(lib_id_object_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) system_fatal("can't create file: %s", lib_id_object_filename); /* * This file is made up of the following: * mach header * an LC_IDFVMLIB command for the library */ fvmlib_name_size = round(strlen(target_name) + 1, sizeof(long)); object_size = sizeof(struct mach_header) + sizeof(struct fvmlib_command) + fvmlib_name_size; buffer = allocate(object_size); offset = 0; mh = (struct mach_header *)(buffer + offset); mh->magic = MH_MAGIC; mh->cputype = arch_flag.cputype; mh->cpusubtype = arch_flag.cpusubtype; mh->filetype = MH_OBJECT; mh->ncmds = 1; mh->sizeofcmds = sizeof(struct fvmlib_command) + fvmlib_name_size; mh->flags = MH_NOUNDEFS; offset += sizeof(struct mach_header); /* * The LC_IDFVMLIB command for the library. This appearing in an * object is what causes the target library to be mapped in when it * is executed. The link editor collects these into the output file * it builds from the input files. Since every object in the host * library refers to the library definition symbol defined in here * this object is always linked with anything that uses this library. */ fvmlib = (struct fvmlib_command *)(buffer + offset); fvmlib->cmd = LC_IDFVMLIB; fvmlib->cmdsize = sizeof(struct fvmlib_command) + fvmlib_name_size; fvmlib->fvmlib.name.offset = sizeof(struct fvmlib_command); if(image_version != 0) fvmlib->fvmlib.minor_version = image_version; else fvmlib->fvmlib.minor_version = minor_version; fvmlib->fvmlib.header_addr = text_addr; offset += sizeof(struct fvmlib_command); strcpy(buffer + offset, target_name); offset += fvmlib_name_size; if(host_byte_sex != target_byte_sex){ swap_mach_header(mh, target_byte_sex); swap_fvmlib_command(fvmlib, target_byte_sex); } if(write(fd, buffer, object_size) != object_size) system_fatal("can't write file: %s", lib_id_object_filename); if(close(fd) == -1) system_fatal("can't close file: %s", lib_id_object_filename); free(buffer); }