diff --git a/Patches/Mtoc/libstuff-1009.2.xcconfig b/Patches/Mtoc/libstuff-1009.2.xcconfig new file mode 100644 index 000000000..93f3073fb --- /dev/null +++ b/Patches/Mtoc/libstuff-1009.2.xcconfig @@ -0,0 +1,31 @@ +// +// libstuff.xcconfig +// stuff +// +// Created by Michael Trent on 12/20/18. +// + +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 + +// HEADER_SEARCH_PATHS includes the local toolchain headers in order to find +// "llvm-c/lto.h" +HEADER_SEARCH_PATHS = ${inherited} ${DT_TOOLCHAIN_DIR}/usr/local/include ${TOOLCHAIN_DIR}/usr/local/include + +// libstuff currently requires the LTO_SUPPORT macro to be defined in order to +// compile support for LTO. This is built non-conditionally, and can probably +// be factored out of the source code at some future point. +// LIBSTUFF_C_FLAGS_LTO = -DLTO_SUPPORT + +// libstuff builds an "apple_version.c" file that stamps the current project +// version into the archive. Various cctools commands will print this version +// upon request. To support this, libstuff passes the value of the +// CURRENT_PROJECT_VERSION setting into apple_version.c as a #define +LIBSTUFF_C_FLAGS_VERS = -DCURRENT_PROJECT_VERSION=\"${CURRENT_PROJECT_VERSION}\" + +// MacOS and Xcode default toolchain binaries are built with support for +// re-signing binaries via libcodedirectory. +// CCTOOLS_C_FLAGS_CODEDIR_YES = -DCODEDIRECTORY_SUPPORT +// CCTOOLS_C_FLAGS_CODEDIR = ${CCTOOLS_C_FLAGS_CODEDIR_${CCTB_MACOS}${CCTB_XCODE}} + +OTHER_CFLAGS = ${inherited} ${LIBSTUFF_C_FLAGS_LTO} ${LIBSTUFF_C_FLAGS_VERS} ${CCTOOLS_C_FLAGS_CODEDIR} diff --git a/Patches/Mtoc/mtoc-1009.2_jief.c b/Patches/Mtoc/mtoc-1009.2_jief.c new file mode 100644 index 000000000..f281d00ff --- /dev/null +++ b/Patches/Mtoc/mtoc-1009.2_jief.c @@ -0,0 +1,2650 @@ +/* + * Copyright (c) 2007 Apple 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@ + * + * Version modified accordingly to jief patches to keep the __mod_init_func, to allow global C++ variables + */ + +#define __eip eip +#define __rip rip + +#include +#include +#include +#include +#include +#include +#include +#include "stuff/breakout.h" +#include "stuff/errors.h" +#include "stuff/allocate.h" +#include "stuff/reloc.h" +#include "stuff/rnd.h" +#include "stuff/write64.h" + +#include "coff/ms_dos_stub.h" +#include "coff/filehdr.h" +#include "coff/aouthdr.h" +#include "coff/scnhdr.h" +#include "coff/syment.h" +#include "coff/bytesex.h" + +#include "coff/base_relocs.h" +#include "mach-o/x86_64/reloc.h" +#include "mach-o/arm64/reloc.h" + +/* used by error routines as the name of this program */ +char *progname = NULL; + +/* the bytesex of our target object file and of this host machine */ +static enum byte_sex target_byte_sex; +static enum byte_sex host_byte_sex; +static enum bool swapped; + +/* the size of the pecoff output file */ +static uint32_t output_size = 0; + +static uint32_t majorVersion = 0; +static uint32_t minorVersion = 0; + +/* + * The headers, and elements of them in the pecoff output file. + */ +static struct ms_dos_stub ms_dos_stub; +static char signature[4]; +static struct filehdr filehdr; +static struct aouthdr aouthdr; +static struct aouthdr_64 aouthdr64; +uint32_t entry = 0; /* the entry point */ +uint32_t nscns = 0; /* the number of section headers and contents pointers */ +static struct scnhdr *scnhdrs = NULL; /* the section headers */ +static char **scn_contents = NULL; /* pointers to the section contents */ + +/* + * The value of the -subsystem argument to then set in the PECOFF aouthdr. + */ +static uint16_t Subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION; + +struct subsystem_argument { + char *name; + uint16_t value; +}; + +struct subsystem_argument subsystem_arguments[] = { + { "application", IMAGE_SUBSYSTEM_EFI_APPLICATION }, + { "app", IMAGE_SUBSYSTEM_EFI_APPLICATION }, + { "UEFI_APPLICATION", IMAGE_SUBSYSTEM_EFI_APPLICATION }, + { "APPLICATION", IMAGE_SUBSYSTEM_EFI_APPLICATION }, + + { "boot", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "bsdrv", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "DXE_DRIVER", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "SEC", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "peim", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "BASE", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "PEI_CORE", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "PEIM", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "DXE_SMM_DRIVER", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "TOOL", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "USER_DEFINED", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "UEFI_DRIVER", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "DXE_CORE", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "SECURITY_CORE", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "COMBINED_PEIM_DRIVER", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "PIC_PEIM", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "RELOCATABLE_PEIM", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "BS_DRIVER", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "SMM_CORE", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + + { "runtime", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER }, + { "rtdrv", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER }, + { "DXE_RUNTIME_DRIVER", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER }, + + { NULL, 0 } +}; + +/* + * The value of the -section_alignment argument (or the -align argument) to + * layout the added PECOFF sections and set into the PECOFF aouthdr. + */ +static uint32_t section_alignment = SECTIONALIGNMENT; + +/* + * The value of the -align argument to layout the PECOFF file. + */ +static uint32_t file_alignment = FILEALIGNMENT; + +/* The maximum alignment allowed to be specified, in hex */ +#define MAXALIGN 0x8000 + +/* Static routine to help parse arguments */ +static enum bool ispoweroftwo(uint32_t x); + +/* + * The string for the -d argument. + */ +char *debug_filename = NULL; + +/* + * The string for the -u argument. + */ +char *debug_uuid = NULL; + +/* + * Format specifier for scanf() to convert UUID to individual bytes + */ +#define UUID_FORMAT_STRING "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" + +/* + * The string for the entry point symbol name. + */ +char *entry_point = NULL; + +#ifdef HACK_TO_MATCH_TEST_CASE +/* + * These are are used for the HACK to get the symbol table for 32-bit files to + * match the one produced by objcopy. They are the pecoff section numbers of + * the .common and .bss sections. + */ +static uint32_t common_scnum = 0; +static uint32_t bss_scnum = 0; +#endif + +/* + * These are for the .reloc section that contains the base relocations. + */ +static struct scnhdr *reloc_scnhdr = NULL; +static uint32_t reloc_size = 0; +static char *reloc_contents = NULL; + +/* + * These are for the pecoff symbol table and string table. + */ +static uint32_t nsyments = 0; /* number of symbols */ +static struct syment *syments = NULL; /* pointer to symbol table elements */ +static uint32_t syment_offset = 0; /* file offset of the symbol table */ +static uint32_t strsize = 0; /* size of the string table */ +static char *strings = NULL; /* pointer to the string table */ +static uint32_t section_names_size = 0; /* size of the section names */ +static char *section_names = NULL; /* pointer to section names */ +static uint32_t string_offset = 0; /* file offset of the string table */ + +/* + * These are for the .debug section that contains the -d filename information. + */ +static struct scnhdr *debug_scnhdr = NULL; +static uint32_t debug_size = 0; +static char *debug_contents = NULL; +static struct debug_directory_entry *dde = NULL; +static struct mtoc_debug_info *mdi = NULL; + +static void process_arch( + struct arch *archs, + uint32_t narchs); +static void process_32bit_arch( + struct arch *arch); +static void process_64bit_arch( + struct arch *arch); +static void layout_output( + struct ofile *ofile); +static void create_output( + struct ofile *ofile, + char *out); +static void create_ms_dos_stub( + struct ms_dos_stub *p); +static void usage( + void); + +static void create_32bit_symbol_table( + struct arch *arch); +static void create_64bit_symbol_table( + struct arch *arch); + +/* + * This is the internal structure that we gather the base relocation in from + * the Mach-O relocation entries. + */ +struct base_reloc { + uint64_t addr; + uint32_t type; +}; +struct base_reloc *base_relocs = NULL; +uint32_t nbase_reloc = 0; + +static void create_base_reloc( + struct arch *arch); +static void gather_base_reloc_info( + uint32_t addr, + struct relocation_info *relocs, + uint32_t nreloc, + cpu_type_t cpu_type, + uint32_t length, + int macho_reloc_type, + int base_reloc_type); +static void add_base_reloc( + uint64_t addr, + uint32_t type); +static void make_base_relocs( + void); +static int cmp_base_relocs( + struct base_reloc *x1, + struct base_reloc *x2); +static uint32_t checksum( + unsigned char *buf); +static void string_to_uuid( + char *string, + uint8_t *uuid); + +static void create_debug( + struct arch *arch); +static void set_debug_addrs_and_offsets( + void); + +/* apple_version is created by the libstuff/Makefile */ +extern char apple_version[]; +char *version = apple_version; + +/* + * The mtoc(1) tool makes a PECOFF file from a fully linked Mach-O file + * compiled with dynamic code gen and relocation entries saved (linked with -r). + * + * mtoc [-subsystem type] [-section_alignment hexvalue] [-align hexvalue] + * [-d filename] input_Mach-O output_pecoff + */ +int +main( +int argc, +char **argv, +char **envp) +{ + int i, j; + char *input, *output; + struct ofile *ofile; + struct arch *archs; + uint32_t narchs; + char *endp; + enum bool section_alignment_specified, align_specified; + + progname = argv[0]; + host_byte_sex = get_host_byte_sex(); + + input = NULL; + output = NULL; + + section_alignment_specified = FALSE; + align_specified = FALSE; + + for(i = 1; i < argc; i++){ + if(strcmp(argv[i], "-subsystem") == 0){ + if(i + 1 >= argc){ + warning("no argument specified for -subsystem option"); + usage(); + } + for(j = 0; subsystem_arguments[j].name != NULL; j++){ + if(strcmp(argv[i+1], subsystem_arguments[j].name) == 0){ + Subsystem = subsystem_arguments[j].value; + break; + } + } + if(subsystem_arguments[j].name == NULL){ + warning("unknown argument: %s specified for -subsystem " + "argument can be:", argv[i+1]); + for(j = 0; subsystem_arguments[j].name != NULL; j++) + fprintf(stderr, "%s\n", subsystem_arguments[j].name); + usage(); + } + i++; + } + else if(strcmp(argv[i], "-d") == 0){ + if(i + 1 >= argc){ + warning("no argument specified for -d option"); + usage(); + } + debug_filename = argv[i+1]; + i++; + } + else if(strcmp(argv[i], "-e") == 0){ + if(i + 1 >= argc){ + warning("no argument specified for -e option"); + usage(); + } + entry_point = argv[i+1]; + i++; + } + else if(strcmp(argv[i], "-u") == 0){ + if(i + 1 >= argc){ + warning("no argument specified for -u option"); + usage(); + } + if(debug_filename == NULL) { + fatal("-u option requires -d option"); + } + debug_uuid = argv[i+1]; + i++; + } + else if(strcmp(argv[i], "-section_alignment") == 0){ + if(i + 1 >= argc){ + warning("no argument specified for -section_alignment " + "option"); + usage(); + } + section_alignment = (uint32_t)strtoul(argv[i+1], &endp, 16); + if(*endp != '\0') + fatal("argument for -section_alignment %s not a proper " + "hexadecimal number", argv[i+1]); + if(!ispoweroftwo(section_alignment) || section_alignment == 0) + fatal("argument to -section_alignment: %x (hex) must be a " + "non-zero power of two", section_alignment); + if(section_alignment > MAXALIGN) + fatal("argument to -section_alignment: %x (hex) must " + "equal to or less than %x (hex)", section_alignment, + (unsigned int)MAXALIGN); + section_alignment_specified = TRUE; + if(align_specified == TRUE && + section_alignment != file_alignment) + fatal("can't specifiy a -section_alignment value %x (hex) " + "different from the -align value %x (hex)", + section_alignment, file_alignment); + i++; + } + else if(strcmp(argv[i], "-align") == 0){ + if(i + 1 >= argc){ + warning("no argument specified for -align option"); + usage(); + } + file_alignment = (uint32_t)strtoul(argv[i+1], &endp, 16); + if(*endp != '\0') + fatal("argument for -align %s not a proper hexadecimal " + "number", argv[i+1]); + if(!ispoweroftwo(file_alignment) || file_alignment == 0) + fatal("argument to -align: %x (hex) must be a non-zero " + "power of two", file_alignment); + if(file_alignment > MAXALIGN) + fatal("argument to -file_alignment: %x (hex) must " + "equal to or less than %x (hex)", file_alignment, + (unsigned int)MAXALIGN); + align_specified = TRUE; + if(section_alignment_specified == TRUE && + section_alignment != file_alignment) + fatal("can't specifiy a -section_alignment value %x (hex) " + "different from the -align value %x (hex)", + section_alignment, file_alignment); + section_alignment = file_alignment; + i++; + } + else if(strcmp(argv[i], "-version") == 0){ + if(i + 1 >= argc){ + warning("no argument specified for -version option"); + usage(); + } + if (sscanf(argv[i+1], "%u.%u", &majorVersion, + &minorVersion) != 2){ + warning("invalid argument specified for -version option"); + usage(); + } + i++; + } + else if(input == NULL) + input = argv[i]; + else if(output == NULL) + output = argv[i]; + else + usage(); + } + if(input == NULL){ + warning("no input file specified"); + usage(); + } + if(output == NULL){ + warning("no output file specified"); + usage(); + } + + /* breakout the file for processing */ + ofile = breakout(input, &archs, &narchs, FALSE); + if(errors) + return(EXIT_FAILURE); + + /* checkout the file for symbol table replacement processing */ + checkout(archs, narchs); + + /* process the input file */ + process_arch(archs, narchs); + if(errors){ + free_archs(archs, narchs); + ofile_unmap(ofile); + return(EXIT_FAILURE); + } + + /* + * Layout the pecoff output file from the information gathered from + * the input file creating the needed headers, relocs, etc. + */ + layout_output(ofile); + + create_output(ofile, output); + + if(errors == 0) + return(EXIT_SUCCESS); + else + return(EXIT_FAILURE); +} + +/* + * usage() prints the current usage message and exits indicating failure. + */ +static +void +usage( +void) +{ + fprintf(stderr, "Usage: %s [-subsystem type] " + "[-section_alignment hexvalue] [-align hexvalue] " + "[-version major.minor] [-ddebug_filename] " + "[-u debug_guid] input_Mach-O output_pecoff\n", progname); + exit(EXIT_FAILURE); +} + +/* + * ispoweroftwo() returns TRUE or FALSE depending if x is a power of two. + */ +static +enum +bool +ispoweroftwo( +uint32_t x) +{ + if(x == 0) + return(TRUE); + while((x & 0x1) != 0x1){ + x >>= 1; + } + if((x & ~0x1) != 0) + return(FALSE); + else + return(TRUE); +} + +/* + * process_arch() is the routine that process the broken out ofile to gather + * the info to create the pecoff file. This routine basically counts and adds + * up the sizes of the elements that will be in the pecoff output file. + */ +static +void +process_arch( +struct arch *archs, +uint32_t narchs) +{ + /* + * Check to see the input file is something this program can convert to + * a pecoff file. + */ + if(narchs != 1) + fatal("input file: %s must only have one architecture", + archs->file_name); + if(archs->type != OFILE_Mach_O) + fatal("input file: %s must be a Mach-O file", archs->file_name); + if(archs->object->mh_cputype != CPU_TYPE_I386 && + archs->object->mh_cputype != CPU_TYPE_ARM && + archs->object->mh_cputype != CPU_TYPE_ARM64 && + archs->object->mh_cputype != CPU_TYPE_X86_64) + fatal("input file: %s must be an i386 or ARM architecture", + archs->file_name); + if(archs->object->mh != NULL){ + if(archs->object->mh->filetype == MH_PRELOAD || + (archs->object->mh->filetype == MH_EXECUTE && + (archs->object->mh->flags & MH_PIE) == MH_PIE)){ + if(entry_point != NULL) + fatal("entry point option, -e %s, not allowed with " + "MH_PRELOAD or MH_EXECUTE file types", entry_point); + } + else{ + fatal("input file: %s must be an MH_PRELOAD file type or " + "MH_EXECUTE file type with MH_PIE flag", + archs->file_name); + } + } + else{ + if(archs->object->mh64->filetype == MH_DYLIB || + (archs->object->mh64->filetype == MH_EXECUTE && + (archs->object->mh64->flags & MH_PIE) == MH_PIE)){ + if(entry_point == NULL && + archs->object->mh64->filetype == MH_DYLIB) + fatal("input file: %s is a MH_DYLIB file type, so entry " + "point option, -e name, must be specified", + archs->file_name); + } + else if(archs->object->mh64->filetype == MH_PRELOAD || + (archs->object->mh64->filetype == MH_EXECUTE && + (archs->object->mh64->flags & MH_PIE) == MH_PIE)){ + if(entry_point != NULL) + fatal("entry point option, -e %s, not allowed with " + "MH_PRELOAD or MH_EXECUTE file types", + archs->file_name); + } + else + fatal("input file: %s must be an MH_PRELOAD or MH_DYLIB file " + "type or MH_EXECUTE file type with MH_PIE flag", + archs->file_name); + } + + target_byte_sex = archs->object->object_byte_sex; + swapped = host_byte_sex != target_byte_sex; + + /* + * Create base relocation entries for this Mach-O file. This is done + * before the sections are created as this produces the contents for + * the .reloc section and determines it size. + */ + create_base_reloc(archs); + + /* + * If there is a -d flag create the information that will be in .debug + * section for it. + */ + if(debug_filename != NULL) + create_debug(archs); + + if(archs->object->mh != NULL) + process_32bit_arch(archs); + else + process_64bit_arch(archs); +} + +/* + * process_32bit_arch() is the routine that processes a 32-bit broken out ofile + * to gather the info to create the pecoff file. This routine basically counts + * and adds up the sizes of the elements that will be in the pecoff output file. + */ +static +void +process_32bit_arch( +struct arch *arch) +{ + uint32_t i, j, reloc_addr, debug_addr; + struct load_command *lc; + struct segment_command *sg; + struct thread_command *ut; + char *p, *state; + uint32_t flavor, count; + char *object_addr, *section_name; +#ifdef HACK_TO_MATCH_TEST_CASE + uint32_t len; + struct section *s; +#endif + + /* + * Determine the number of sections in the pecoff output file. + * +#ifdef HACK_TO_MATCH_TEST_CASE + * + * The hack implementation of this routine is done to match the + * current ld_efi(1) script that uses objcopy(1) to make the pecoff + * file. So for 32-bit file the contents of the Mach-O file gets + * placed into pecoff sections as follows: + * + * the entire __TEXT segment becomes the .text section + * the entire __DATA segment becomes the .data section + * the zero fill section (__DATA,__common) becomes .common + * the zero fill section (__DATA,__bss) becomes .bss + * the (__IMPORT,__pointers) section becomes .pointers + * the base relocation entries go into the .reloc section + * +#else + * + * The whole Mach-O segments __TEXT, __DATA and __IMPORT are placed in + * the pecoff file from the Mach-O file. And then the .reloc section + * added for the base relocations. + * +#endif + */ + nscns = 0; + reloc_addr = 0; + lc = arch->object->load_commands; + for(i = 0; i < arch->object->mh->ncmds; i++){ + if(lc->cmd == LC_SEGMENT){ + sg = (struct segment_command *)lc; + if(strcmp(sg->segname, SEG_LINKEDIT) != 0 && + sg->vmaddr + sg->vmsize > reloc_addr) + reloc_addr = sg->vmaddr + sg->vmsize; + if(strcmp(sg->segname, SEG_TEXT) == 0) + nscns++; + else if(strcmp(sg->segname, SEG_DATA) == 0){ + nscns++; +#ifdef HACK_TO_MATCH_TEST_CASE + s = (struct section *) + ((char *)sg + sizeof(struct segment_command)); + for(j = 0; j < sg->nsects; j++, s++){ + if(strcmp(s->sectname, SECT_COMMON) == 0 || + strcmp(s->sectname, SECT_BSS) == 0){ + nscns++; + } + else if(s->size != 0 && + strcmp(s->sectname, SECT_DATA) != 0) + fatal("input file: %s contains Mach-O section " + "(%.16s,%.16s) unsupported for conversion " + "to a pecoff file", arch->file_name, + s->segname, s->sectname); + } +#endif /* HACK_TO_MATCH_TEST_CASE */ + } + else if(strcmp(sg->segname, SEG_IMPORT) == 0){ +#ifndef HACK_TO_MATCH_TEST_CASE + nscns++; +#else + s = (struct section *) + ((char *)sg + sizeof(struct segment_command)); + for(j = 0; j < sg->nsects; j++, s++){ + if(strcmp(s->sectname, "__pointers") == 0){ + section_names_size += strlen(".pointers") + 1; + nscns++; + } + else if(s->size != 0) + fatal("input file: %s contains Mach-O section " + "(%.16s,%.16s) unsupported for conversion " + "to a pecoff file", arch->file_name, + s->segname, s->sectname); + } + +#endif /* HACK_TO_MATCH_TEST_CASE */ + } + else if((arch->object->mh->flags & MH_PIE) != MH_PIE || + strcmp(sg->segname, SEG_LINKEDIT) != 0){ + fatal("input file: %s contains Mach-O segment %.16s " + "unsupported for conversion to a pecoff file", + arch->file_name, sg->segname); + } + } + /* + * Also while processing the Mach-O file pick up the entry point. + */ + else if(lc->cmd == LC_UNIXTHREAD){ + ut = (struct thread_command *)lc; + state = (char *)ut + sizeof(struct thread_command); + p = (char *)ut + ut->cmdsize; + while(state < p){ + flavor = *((uint32_t *)state); + state += sizeof(uint32_t); + count = *((uint32_t *)state); + state += sizeof(uint32_t); + switch(arch->object->mh_cputype){ + case CPU_TYPE_I386: + switch((int)flavor){ + case i386_THREAD_STATE: +#if i386_THREAD_STATE == 1 + case -1: +#endif /* i386_THREAD_STATE == 1 */ +/* i386 thread states on older releases */ +#if i386_THREAD_STATE == -1 + case 1: +#endif /* i386_THREAD_STATE == -1 */ + { + i386_thread_state_t *cpu = + (i386_thread_state_t *)state; + entry = cpu->eip; + state += sizeof(i386_thread_state_t); + } + break; + default: + state += count * sizeof(uint32_t); + break; + } + break; + case CPU_TYPE_ARM: + switch(flavor){ + case ARM_THREAD_STATE: + { + arm_thread_state_t *cpu = + (arm_thread_state_t *)state; + entry = cpu->__pc; + state += sizeof(arm_thread_state_t); + } + break; + default: + state += count * sizeof(uint32_t); + break; + } + break; + default: + break; + } + } + } + lc = (struct load_command *)((char *)lc + lc->cmdsize); + } + if(reloc_size != 0){ + /* add one for the .reloc section to contain the base relocations */ + nscns++; + } + + /* + * If there is a -d flag add one for the .debug section to contain + * the information. + */ + if(debug_filename != NULL) + nscns++; + + /* + * At the beginning of the COFF string table are 4 bytes that contain + * the total size (in bytes) of the rest of the string table. This size + * includes the size field itself, so that the value in this location + * would be 4 if no strings were present. + */ + strsize = sizeof(uint32_t); + + /* + * Section names longer than 8 bytes are placed in the string table. + * So here we allocate memory to put them into, which later will be + * copied to the start of the string table. + */ + section_names = allocate(section_names_size); + section_name = section_names; + if(section_names_size != 0) + *section_name = '\0'; + + /* + * Allocate space for the section headers and fill in everything but + * their file offsets. + * +#ifndef HACK_TO_MATCH_TEST_CASE + * + * We use the SizeOfRawData field (s_size) as the unrounded value of + * the size of the initialized section contents coming from the + * segment's filesize. The VirtualSize field s_vsize may be bigger + * with the remaining space zero filled coming from the segment's + * vmsize. +#else + * + * Note to match what objcopy(1) does the s_vsize is an unrounded value + * of the size (more like the actual size) and the s_size is a value + * rounded to the file_alignment. So the s_vsize can be smaller than + * the s_size, as in the case of pecoff sections created from Mach-O + * sections (and not segments). This seems to volate the spec where + * s_vsize can be bigger than s_size with the remaining space zero + * filled but does NOT allow the s_vsize to be smaller than the s_size. +#endif + */ + scnhdrs = allocate(nscns * sizeof(struct scnhdr)); + memset(scnhdrs, '\0', nscns * sizeof(struct scnhdr)); + scn_contents = allocate(nscns * sizeof(char *)); + object_addr = arch->object->object_addr; + j = 0; + lc = arch->object->load_commands; + for(i = 0; i < arch->object->mh->ncmds; i++){ + if(lc->cmd == LC_SEGMENT){ + sg = (struct segment_command *)lc; + if(strcmp(sg->segname, SEG_TEXT) == 0){ + strcpy(scnhdrs[j].s_name, ".text"); +#ifdef HACK_TO_MATCH_TEST_CASE + scnhdrs[j].s_vsize = sg->filesize; +#else + scnhdrs[j].s_vsize = sg->vmsize; +#endif + scnhdrs[j].s_vaddr = sg->vmaddr; + scnhdrs[j].s_size = rnd32(sg->filesize, file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_EXECUTE | + IMAGE_SCN_MEM_READ | + IMAGE_SCN_CNT_CODE; + scn_contents[j] = object_addr + sg->fileoff; + j++; + } + else if(strcmp(sg->segname, SEG_DATA) == 0){ + strcpy(scnhdrs[j].s_name, ".data"); +#ifdef HACK_TO_MATCH_TEST_CASE + scnhdrs[j].s_vsize = sg->filesize; +#else + scnhdrs[j].s_vsize = sg->vmsize; +#endif + scnhdrs[j].s_vaddr = sg->vmaddr; + scnhdrs[j].s_size = rnd32(sg->filesize, file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE | + IMAGE_SCN_CNT_CODE | + IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_EXECUTE; + scn_contents[j] = object_addr + sg->fileoff; + j++; +#ifdef HACK_TO_MATCH_TEST_CASE + s = (struct section *) + ((char *)sg + sizeof(struct segment_command)); + for(i = 0; i < sg->nsects; i++, s++){ + if(s->size == 0) + continue; + scnhdrs[j].s_vsize = s->size; + scnhdrs[j].s_vaddr = s->addr; + scnhdrs[j].s_size = 0; + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE | + IMAGE_SCN_CNT_UNINITIALIZED_DATA; + if(strcmp(s->sectname, SECT_DATA) == 0){ + continue; + } + else if(strcmp(s->sectname, SECT_COMMON) == 0){ + strcpy(scnhdrs[j].s_name, ".common"); + common_scnum = j + 1; + } + else if(strcmp(s->sectname, SECT_BSS) == 0){ + strcpy(scnhdrs[j].s_name, ".bss"); + bss_scnum = j + 1; + } + scn_contents[j] = NULL; + j++; + } +#endif /* HACK_TO_MATCH_TEST_CASE */ + } + else if(strcmp(sg->segname, SEG_IMPORT) == 0){ +#ifndef HACK_TO_MATCH_TEST_CASE + strcpy(scnhdrs[j].s_name, ".import"); + scnhdrs[j].s_vsize = sg->vmsize; + scnhdrs[j].s_vaddr = sg->vmaddr; + scnhdrs[j].s_size = rnd32(sg->filesize, file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE | + IMAGE_SCN_CNT_INITIALIZED_DATA; + scn_contents[j] = object_addr + sg->fileoff; + j++; +#else /* defined(HACK_TO_MATCH_TEST_CASE) */ + s = (struct section *) + ((char *)sg + sizeof(struct segment_command)); + for(i = 0; i < sg->nsects; i++, s++){ + if(s->size == 0) + continue; + scnhdrs[j].s_vsize = s->size; + scnhdrs[j].s_vaddr = s->addr; + scnhdrs[j].s_size = rnd(s->size, file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE | + IMAGE_SCN_CNT_INITIALIZED_DATA; + if(strcmp(s->sectname, "__pointers") == 0){ + sprintf(scnhdrs[j].s_name, "/%d", strsize); + strcat(section_name, ".pointers"); + len = strlen(section_name) + 1; + strsize += len; + } + scn_contents[j] = object_addr + s->offset; + j++; + } +#endif /* HACK_TO_MATCH_TEST_CASE */ + } + } + lc = (struct load_command *)((char *)lc + lc->cmdsize); + } + if(reloc_size != 0){ + strcpy(scnhdrs[j].s_name, ".reloc"); + scnhdrs[j].s_vsize = reloc_size; + reloc_addr = rnd32(reloc_addr, section_alignment); + scnhdrs[j].s_vaddr = reloc_addr; + scnhdrs[j].s_size = rnd32(reloc_size, file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_READ | + IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_DISCARDABLE; + reloc_scnhdr = scnhdrs + j; + scn_contents[j] = reloc_contents; + j++; + debug_addr = reloc_addr + reloc_scnhdr->s_size; + } + else{ + debug_addr = rnd32(reloc_addr, section_alignment); + } + + if(debug_filename != NULL){ + strcpy(scnhdrs[j].s_name, ".debug"); + scnhdrs[j].s_vsize = debug_size; + scnhdrs[j].s_vaddr = debug_addr; + scnhdrs[j].s_size = rnd32(debug_size, file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_READ | + IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_DISCARDABLE; + debug_scnhdr = scnhdrs + j; + scn_contents[j] = debug_contents; + j++; + } + + /* + * Create the pecoff symbol and string table from this Mach-O file. + */ + create_32bit_symbol_table(arch); +} + +/* + * process_64bit_arch() is the routine that processes a 64-bit broken out ofile + * to gather the info to create the pecoff file. This routine basically counts + * and adds up the sizes of the elements that will be in the pecoff output file. + */ +static +void +process_64bit_arch( +struct arch *arch) +{ + uint32_t i, j; + uint64_t reloc_addr, debug_addr; + struct load_command *lc; + struct segment_command_64 *sg64; + struct thread_command *ut; + char *p, *state; + uint32_t flavor, count; + char *object_addr, *section_name; +#ifdef HACK_TO_MATCH_TEST_CASE + struct section_64 *s64; + uint32_t len; +#endif + + /* + * Determine the number of sections in the pecoff output file. + * +#ifdef HACK_TO_MATCH_TEST_CASE + * + * The hack implementation of this routine is done to match the + * current ld_efi(1) script that uses objcopy(1) to make the pecoff + * file. So for 64-bit files the contents of the Mach-O sections get + * placed into pecoff sections with a section name made up of the + * strings "LC_SEGMENT" the segment and section names separated with + * a dot, '.', character. So the Mach-O (__TEXT,__text) section becomes + * a pecoff section with the name "LC_SEGMENT.__TEXT.__text". The base + * relocation entries go into a ".reloc" section. + * +#else + * + * The whole Mach-O __TEXT and __DATA segments are placed in the + * pecoff file from the Mach-O file. And then the .reloc section added + * for the base relocations. + * +#endif + */ + nscns = 0; + reloc_addr = 0; + lc = arch->object->load_commands; + for(i = 0; i < arch->object->mh64->ncmds; i++){ + if(lc->cmd == LC_SEGMENT_64){ + sg64 = (struct segment_command_64 *)lc; +#ifndef HACK_TO_MATCH_TEST_CASE + if(strcmp(sg64->segname, SEG_LINKEDIT) != 0 && + sg64->vmaddr + sg64->vmsize > reloc_addr) + reloc_addr = sg64->vmaddr + sg64->vmsize; + if(strcmp(sg64->segname, SEG_TEXT) == 0) + nscns++; + else if(strcmp(sg64->segname, SEG_DATA) == 0) + nscns++; + else if(strcmp(sg64->segname, SEG_LINKEDIT) != 0){ + fatal("input file: %s contains Mach-O segment %.16s " + "unsupported for conversion to a pecoff file", + arch->file_name, sg64->segname); + } +#else /* defined(HACK_TO_MATCH_TEST_CASE) */ + s64 = (struct section_64 *) + ((char *)sg64 + sizeof(struct segment_command_64)); + for(i = 0; i < sg64->nsects; i++, s64++){ + if(s64->addr + s64->size > reloc_addr) + reloc_addr = s64->addr + s64->size; + section_names_size += strlen("LC_SEGMENT.") + + strlen(s64->segname) + 1 + + strlen(s64->sectname) + 1; + nscns++; + } +#endif /* HACK_TO_MATCH_TEST_CASE */ + } + /* + * Also while process the Mach-O file pick up the entry point. + */ + else if(lc->cmd == LC_UNIXTHREAD){ + ut = (struct thread_command *)lc; + state = (char *)ut + sizeof(struct thread_command); + p = (char *)ut + ut->cmdsize; + while(state < p){ + flavor = *((uint32_t *)state); + state += sizeof(uint32_t); + count = *((uint32_t *)state); + state += sizeof(uint32_t); + switch(arch->object->mh_cputype){ +#ifdef x86_THREAD_STATE64 + case CPU_TYPE_X86_64: + switch(flavor){ + case x86_THREAD_STATE64: + { + x86_thread_state64_t *cpu64 = + (x86_thread_state64_t *)state; + /* + * The aouthdr_64 struct only allows for a + * 32-bit entry point. + */ + entry = (uint32_t)cpu64->rip; + state += sizeof(x86_thread_state64_t); + } + break; + default: + state += count * sizeof(uint32_t); + break; + } + break; +#endif /* x86_THREAD_STATE64 */ +#ifdef ARM_THREAD_STATE64 + case CPU_TYPE_ARM64: + switch(flavor){ + case ARM_THREAD_STATE64: + { + arm_thread_state64_t *cpu64 = + (arm_thread_state64_t *)state; + /* + * The aouthdr_64 struct only allows for a + * 32-bit entry point. + */ + entry = (uint32_t)cpu64->__pc; + state += sizeof(arm_thread_state64_t); + } + break; + default: + state += count * sizeof(uint32_t); + break; + } + break; +#endif /* ARM_THREAD_STATE64 */ + } + } + } + lc = (struct load_command *)((char *)lc + lc->cmdsize); + } + if(reloc_size != 0){ + /* add one for the .reloc section to contain the base relocations */ + nscns++; + } + + /* + * If there is a -d flag add one for the .debug section to contain + * the information. + */ + if(debug_filename != NULL) + nscns++; + + /* + * At the beginning of the COFF string table are 4 bytes that contain + * the total size (in bytes) of the rest of the string table. This size + * includes the size field itself, so that the value in this location + * would be 4 if no strings were present. + */ + strsize = sizeof(uint32_t); + + /* + * Section names longer than 8 bytes are placed in the string table. + * So here we allocate memory to put them into, which later will be + * copied to the start of the string table. + */ + section_names = allocate(section_names_size) + 1; + section_name = section_names; + if(section_names_size != 0) + *section_name = '\0'; + + /* + * Allocate space for the section headers and fill in everything but + * their file offsets. + * +#ifndef HACK_TO_MATCH_TEST_CASE + * + * We use the SizeOfRawData field (s_size) as the unrounded value of + * the size of the initialized section contents coming from the + * segment's filesize. The VirtualSize field s_vsize may be bigger + * with the remaining space zero filled coming from the segment's + * vmsize. +#else + * + * Note to match what objcopy(1) does the s_vsize is an unrounded value + * of the size (more like the actual size) and the s_size is a value + * rounded to the file_alignment. So the s_vsize can be smaller than + * the s_size, as in the case of pecoff sections created from Mach-O + * sections (and not segments). This seems to volate the spec where + * s_vsize can be bigger than s_size with the remaining space zero + * filled but does NOT allow the s_vsize to be smaller than the s_size. +#endif + */ + scnhdrs = allocate(nscns * sizeof(struct scnhdr)); + memset(scnhdrs, '\0', nscns * sizeof(struct scnhdr)); + scn_contents = allocate(nscns * sizeof(char *)); + object_addr = arch->object->object_addr; + j = 0; + lc = arch->object->load_commands; + for(i = 0; i < arch->object->mh64->ncmds; i++){ + if(lc->cmd == LC_SEGMENT_64){ + sg64 = (struct segment_command_64 *)lc; +#ifndef HACK_TO_MATCH_TEST_CASE + if(strcmp(sg64->segname, SEG_TEXT) == 0){ + strcpy(scnhdrs[j].s_name, ".text"); + scnhdrs[j].s_vsize = (uint32_t)sg64->vmsize; + scnhdrs[j].s_vaddr = (uint32_t)sg64->vmaddr; + scnhdrs[j].s_size = (uint32_t)rnd64(sg64->filesize, + file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_EXECUTE | + IMAGE_SCN_MEM_READ | + IMAGE_SCN_CNT_CODE; + scn_contents[j] = object_addr + sg64->fileoff; + j++; + } + else if(strcmp(sg64->segname, SEG_DATA) == 0){ + strcpy(scnhdrs[j].s_name, ".data"); + scnhdrs[j].s_vsize = (uint32_t)sg64->vmsize; + scnhdrs[j].s_vaddr = (uint32_t)sg64->vmaddr; + scnhdrs[j].s_size = (uint32_t)rnd64(sg64->filesize, + file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE | + IMAGE_SCN_CNT_CODE | + IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_EXECUTE; + scn_contents[j] = object_addr + sg64->fileoff; + j++; + } +#else /* defined(HACK_TO_MATCH_TEST_CASE) */ + s64 = (struct section_64 *) + ((char *)sg64 + sizeof(struct segment_command_64)); + for(i = 0; i < sg64->nsects; i++, s64++){ + sprintf(scnhdrs[j].s_name, "/%d", strsize); + strcat(section_name, "LC_SEGMENT."); + strcat(section_name, s64->segname); + strcat(section_name, "."); + strcat(section_name, s64->sectname); + len = strlen(section_name); + strsize += len + 1; + section_name += len + 1; + *section_name = '\0'; /* start of next section name */ + + /* NOTE zerofill sections are not handled */ + scnhdrs[j].s_vsize = s64->size; + scnhdrs[j].s_vaddr = s64->addr; + scnhdrs[j].s_size = rnd(s64->size, file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_EXECUTE | + IMAGE_SCN_CNT_CODE | + IMAGE_SCN_MEM_WRITE; + if(sg64->initprot & VM_PROT_READ) + scnhdrs[j].s_flags |= IMAGE_SCN_MEM_READ; + scn_contents[j] = object_addr + s64->offset; + j++; + } +#endif /* HACK_TO_MATCH_TEST_CASE */ + } + lc = (struct load_command *)((char *)lc + lc->cmdsize); + } + if(reloc_size != 0){ + strcpy(scnhdrs[j].s_name, ".reloc"); + scnhdrs[j].s_vsize = reloc_size; + reloc_addr = rnd(reloc_addr, section_alignment); + scnhdrs[j].s_vaddr = (uint32_t)reloc_addr; + scnhdrs[j].s_size = rnd32(reloc_size, file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_READ | + IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_DISCARDABLE | + IMAGE_SCN_CNT_CODE | + IMAGE_SCN_MEM_EXECUTE; + reloc_scnhdr = scnhdrs + j; + scn_contents[j] = reloc_contents; + j++; + debug_addr = reloc_addr + reloc_scnhdr->s_size; + } + else{ + debug_addr = rnd(reloc_addr, section_alignment); + } + + if(debug_filename != NULL){ + strcpy(scnhdrs[j].s_name, ".debug"); + scnhdrs[j].s_vsize = debug_size; + scnhdrs[j].s_vaddr = (uint32_t)debug_addr; + scnhdrs[j].s_size = rnd32(debug_size, file_alignment); + scnhdrs[j].s_relptr = 0; + scnhdrs[j].s_lnnoptr = 0; + scnhdrs[j].s_nlnno = 0; + scnhdrs[j].s_flags = IMAGE_SCN_MEM_READ | + IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_DISCARDABLE | + IMAGE_SCN_CNT_CODE | + IMAGE_SCN_MEM_EXECUTE; + debug_scnhdr = scnhdrs + j; + scn_contents[j] = debug_contents; + j++; + } + + /* + * Create the pecoff symbol and string table from this Mach-O file. + */ + create_64bit_symbol_table(arch); +} + +/* + * layout_output() takes the info gathered from the input Mach-O file and + * layouts the pecoff output file and creates and fills in the elements of + * the coff file. This routine basically sets of the offsets of the elements + * of the output file from the previously determined sizes. + */ +static +void +layout_output( +struct ofile *ofile) +{ + uint32_t i, header_size, offset, least_vaddr; + + /* + * Determine the size of the output file and where each element will be + * in the output file. + */ + header_size = sizeof(struct ms_dos_stub) + + sizeof(signature) + + sizeof(struct filehdr) + + nscns * sizeof(struct scnhdr); + if(ofile->mh != NULL) + header_size += sizeof(struct aouthdr); + else + header_size += sizeof(struct aouthdr_64); + header_size = rnd32(header_size, file_alignment); +#ifdef HACK_TO_MATCH_TEST_CASE + /* for some unknown reason the header size is 0x488 not 0x400 */ + if(ofile->mh64 != NULL) + header_size += 0x88; +#endif + /* + * If the lowest section virtual address is greater than the header + * size, pad the header up to the virtual address. This modification + * will make the file offset and virtual address equal, and fixes + * problems with XIP rebasing in the EFI tools. + */ + least_vaddr = 0xffffffff; + for(i = 0; i < nscns; i++){ + if(scnhdrs[i].s_vaddr < least_vaddr) + least_vaddr = scnhdrs[i].s_vaddr; + } + if(least_vaddr > header_size) + header_size = least_vaddr; + + offset = header_size; + for(i = 0; i < nscns; i++){ + if((scnhdrs[i].s_flags & IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0){ + /* + * We need to check that the headers can be mapped starting at + * the ImageBase, fixed at zero in this program, and fit before + * the Virtual Address of the first section (really any section) + * and if it doesn't then we need the Mach-O file relinked. + */ + if(scnhdrs[i].s_vaddr < header_size) + fatal("input file: %s must be relinked so PECOFF headers " + "can be mapped before its sections (use a -seg1addr " + "0x%x or greater)", ofile->file_name, header_size); + /* + * The s_scnptr is set to the offset and then the offset is + * incremented by the SizeOfRawData field (s_vsize). + */ + scnhdrs[i].s_scnptr = offset; +#ifndef HACK_TO_MATCH_TEST_CASE + offset += scnhdrs[i].s_vsize; +#else + /* for some unknown reason the offset after the __dyld section + is changed from 0x10 bytes to 0x20 bytes */ + if(ofile->mh64 != NULL && scnhdrs[i].s_vsize < 0x20) + offset += 0x20; + /* for some unknown reason the offset after the __data section + is changed from 0x380 bytes to 0x3e0 bytes */ + else if(ofile->mh64 != NULL && scnhdrs[i].s_vsize == 0x380) + offset += scnhdrs[i].s_vsize + 0x60; + else + /* + * Note to match what objcopy(1) does the offset is + * incremented by the VirtualSize field (s_vsize) not the + * SizeOfRawData field (s_size) field as that is what was + * previously set up. + */ + offset += scnhdrs[i].s_vsize; +#endif +#ifdef HACK_TO_MATCH_TEST_CASE + if(ofile->mh != NULL) +#endif + offset = rnd32(offset, file_alignment); +#ifdef HACK_TO_MATCH_TEST_CASE + else{ + /* for some unknown reason the next offset is moved up + 0x200 then rounded to 8 bytes */ + offset += 0x200; + offset = rnd(offset, 8); + } +#endif + } + } +#ifdef HACK_TO_MATCH_TEST_CASE + /* for some unknown reason the offset of the symbol is moved back 0x58 + bytes */ + if(ofile->mh64 != NULL) + offset -= 0x58; +#endif + syment_offset = offset; + offset += nsyments * sizeof(struct syment); + string_offset = offset; + offset += strsize; + + output_size = offset; + + /* + * Now with all the sizes and placement of things know fill in headers + * of the pecoff file for this Mach-O file. + */ + + /* first in the pecoff file is the MS-DOS stub */ + create_ms_dos_stub(&ms_dos_stub); + + /* + * Second in the pecoff file is the PE format image file signature. + * This signature is PE\0\0 (the letters P and E followed by two null + * bytes). + */ + signature[0] = 'P'; + signature[1] = 'E'; + signature[2] = '\0'; + signature[3] = '\0'; + + /* next is the filehdr */ + if(ofile->mh != NULL){ + if(ofile->mh->cputype == CPU_TYPE_I386) + filehdr.f_magic = IMAGE_FILE_MACHINE_I386; + else + filehdr.f_magic = IMAGE_FILE_MACHINE_ARM; + } + else{ + if(ofile->mh64->cputype == CPU_TYPE_X86_64) + filehdr.f_magic = IMAGE_FILE_MACHINE_AMD64; + else + filehdr.f_magic = IMAGE_FILE_MACHINE_ARM64; + } + filehdr.f_nscns = nscns; +#ifdef HACK_TO_MATCH_TEST_CASE + if(ofile->mh != NULL){ + filehdr.f_timdat = 0x46cb5980; + } + else + filehdr.f_timdat = 0x47671e62; +#else + filehdr.f_timdat = (uint32_t)time(NULL); +#endif + filehdr.f_symptr = syment_offset; + filehdr.f_nsyms = nsyments; + if(ofile->mh != NULL) + filehdr.f_opthdr = sizeof(struct aouthdr); + else + filehdr.f_opthdr = sizeof(struct aouthdr_64); + filehdr.f_flags = IMAGE_FILE_EXECUTABLE_IMAGE | + IMAGE_FILE_LINE_NUMS_STRIPPED | + IMAGE_FILE_32BIT_MACHINE | + IMAGE_FILE_DEBUG_STRIPPED; + if(ofile->mh64 != NULL) + filehdr.f_flags |= IMAGE_FILE_LOCAL_SYMS_STRIPPED; + + /* next is the aouthdr */ + if(ofile->mh != NULL){ + aouthdr.magic = PE32MAGIC; + aouthdr.vstamp = VSTAMP; + + /* + * EFI does not use t, d, or b size. + * EFI uses SizeOfImage to errorcheck vaddrs in the image + */ + aouthdr.tsize = 0; + aouthdr.dsize = 0; + aouthdr.bsize = 0; + aouthdr.SizeOfImage = rnd32(header_size, section_alignment); + for(i = 0; i < nscns; i++){ + aouthdr.SizeOfImage += rnd32(scnhdrs[i].s_vsize, section_alignment); + } + + aouthdr.entry = entry; + + aouthdr.text_start = 0; + aouthdr.data_start = 0; + for(i = 0; i < nscns; i++){ + if((scnhdrs[i].s_flags & IMAGE_SCN_CNT_UNINITIALIZED_DATA) ==0){ + if((scnhdrs[i].s_flags & IMAGE_SCN_MEM_WRITE) == 0){ + if(aouthdr.text_start == 0) + aouthdr.text_start = scnhdrs[i].s_vaddr; + } + else{ + if(aouthdr.data_start == 0) + aouthdr.data_start = scnhdrs[i].s_vaddr; + } + } + } + + aouthdr.ImageBase = 0; + aouthdr.SectionAlignment = section_alignment; + aouthdr.FileAlignment = file_alignment; + aouthdr.MajorOperatingSystemVersion = 0; + aouthdr.MinorOperatingSystemVersion = 0; + aouthdr.MajorImageVersion = majorVersion; + aouthdr.MinorImageVersion = minorVersion; + aouthdr.MajorSubsystemVersion = 0; + aouthdr.MinorSubsystemVersion = 0; + aouthdr.Win32VersionValue = 0; + + + aouthdr.SizeOfHeaders = header_size; + aouthdr.CheckSum = 0; + aouthdr.Subsystem = Subsystem; + aouthdr.DllCharacteristics = 0; + aouthdr.SizeOfStackReserve = 0; + aouthdr.SizeOfStackCommit = 0; + aouthdr.SizeOfHeapReserve = 0; + aouthdr.SizeOfHeapCommit = 0; + aouthdr.LoaderFlags = 0; + aouthdr.NumberOfRvaAndSizes = 16; + /* Entry 5, Base Relocation Directory [.reloc] address & size */ + if(reloc_size != 0){ + aouthdr.DataDirectory[5][0] = reloc_scnhdr->s_vaddr; + aouthdr.DataDirectory[5][1] = reloc_scnhdr->s_vsize; + } + /* Entry 6, Debug Directory [.debug] address & size */ + if(debug_filename != NULL){ + aouthdr.DataDirectory[6][0] = debug_scnhdr->s_vaddr; + aouthdr.DataDirectory[6][1] = debug_scnhdr->s_vsize; + } + } + else{ + aouthdr64.magic = PE32PMAGIC; + aouthdr64.vstamp = VSTAMP; + + /* + * EFI does not use t, d, or b size. + * EFI uses SizeOfImage to errorcheck vaddrs in the image + */ + aouthdr64.tsize = 0; + aouthdr64.dsize = 0; + aouthdr64.bsize = 0; + + aouthdr64.SizeOfImage = rnd32(header_size, section_alignment); + for(i = 0; i < nscns; i++){ + aouthdr64.SizeOfImage += rnd(scnhdrs[i].s_vsize, section_alignment); + } +#ifdef HACK_TO_MATCH_TEST_CASE + /* with the IMAGE_SCN_CNT_CODE flag set on all sections this is + just a quick hack to match the PECOFF file */ + aouthdr64.dsize = 0x200; +#endif + /* + * The aouthdr_64 struct only allows for a + * 32-bit entry point. + */ + aouthdr64.entry = entry; +#ifdef HACK_TO_MATCH_TEST_CASE + aouthdr64.entry = 0x4a2; +#endif + aouthdr64.text_start = 0; + for(i = 0; i < nscns; i++){ + if((scnhdrs[i].s_flags & IMAGE_SCN_CNT_UNINITIALIZED_DATA) ==0){ + if((scnhdrs[i].s_flags & IMAGE_SCN_MEM_WRITE) == 0){ + if(aouthdr64.text_start == 0) + aouthdr64.text_start = scnhdrs[i].s_vaddr; + } + } + } +#ifdef HACK_TO_MATCH_TEST_CASE + /* this is a hack as the start of the text for 64-bit Mach-O files + built with -dylib does not have the text section starting at 0 */ + aouthdr64.text_start = 0; +#endif + + aouthdr64.ImageBase = 0; + aouthdr64.SectionAlignment = section_alignment; + aouthdr64.FileAlignment = file_alignment; + aouthdr64.MajorOperatingSystemVersion = 0; + aouthdr64.MinorOperatingSystemVersion = 0; + aouthdr64.MajorImageVersion = majorVersion; + aouthdr64.MinorImageVersion = minorVersion; + aouthdr64.MajorSubsystemVersion = 0; + aouthdr64.MinorSubsystemVersion = 0; + aouthdr64.Win32VersionValue = 0; + + +#ifdef HACK_TO_MATCH_TEST_CASE + /* this is a hack as it seams that the minimum size is 0x10000 */ + if(aouthdr64.SizeOfImage < 0x10000) + aouthdr64.SizeOfImage = 0x10000; +#endif + aouthdr64.SizeOfHeaders = header_size; + aouthdr64.CheckSum = 0; + aouthdr64.Subsystem = Subsystem; +#ifdef HACK_TO_MATCH_TEST_CASE + aouthdr64.Subsystem = IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER; +#endif + aouthdr64.DllCharacteristics = 0; + aouthdr64.SizeOfStackReserve = 0; + aouthdr64.SizeOfStackCommit = 0; + aouthdr64.SizeOfHeapReserve = 0; + aouthdr64.SizeOfHeapCommit = 0; + aouthdr64.LoaderFlags = 0; + aouthdr64.NumberOfRvaAndSizes = 16; + /* Entry 5, Base Relocation Directory [.reloc] address & size */ + if(reloc_size != 0){ + aouthdr64.DataDirectory[5][0] = reloc_scnhdr->s_vaddr; + aouthdr64.DataDirectory[5][1] = reloc_scnhdr->s_vsize; + } + /* Entry 6, Debug Directory [.debug] address & size */ + if(debug_filename != NULL){ + aouthdr64.DataDirectory[6][0] = debug_scnhdr->s_vaddr; + aouthdr64.DataDirectory[6][1] = debug_scnhdr->s_vsize; + } + } + + /* + * If there is a debug directory entry set the address and offsets in + * it now that the values are known. + */ + if(debug_filename != NULL) + set_debug_addrs_and_offsets(); +} + +/* + * create_output() takes the info gathered from the input Mach-O file and + * creates the pecoff output file. + */ +static +void +create_output( +struct ofile *ofile, +char *out) +{ + int i, f; + unsigned char *buf, *p, *p_aouthdr; + + /* + * Allocate the buffer to place the pecoff file in. + */ + buf = calloc(1, output_size); + if(buf == NULL) + fatal("Can't allocate buffer for output file (size = %u)", + output_size); + + /* + * Copy the parts of the pecoff file into the buffer. + */ + p = buf; + + memcpy(p, &ms_dos_stub, sizeof(struct ms_dos_stub)); + if(swapped) + swap_ms_dos_stub((struct ms_dos_stub *)p, target_byte_sex); + p += sizeof(struct ms_dos_stub); + + memcpy(p, signature, sizeof(signature)); + p += sizeof(signature); + + memcpy(p, &filehdr, sizeof(struct filehdr)); + if(swapped) + swap_filehdr((struct filehdr *)p, target_byte_sex); + p += sizeof(struct filehdr); + + p_aouthdr = p; + if(ofile->mh != NULL){ + memcpy(p, &aouthdr, sizeof(struct aouthdr)); + if(swapped) + swap_aouthdr((struct aouthdr *)p, target_byte_sex); + p += sizeof(struct aouthdr); + } + else{ + memcpy(p, &aouthdr64, sizeof(struct aouthdr_64)); + if(swapped) + swap_aouthdr_64((struct aouthdr_64 *)p, target_byte_sex); + p += sizeof(struct aouthdr_64); + } + + /* + * Now copy in the section contents. Note the base relocations + * (the contents of the .reloc section) has already been swapped if + * that was needed. + */ + for(i = 0; i < nscns; i++){ + if((scnhdrs[i].s_flags & IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0){ + memcpy(buf + scnhdrs[i].s_scnptr, + scn_contents[i], +#ifndef HACK_TO_MATCH_TEST_CASE + scnhdrs[i].s_size); +#else + scnhdrs[i].s_vsize); +#endif + +#ifdef HACK_TO_MATCH_TEST_CASE + /* this is a hack as this is zero in 64-bit file */ + if(ofile->mh64 != NULL) + scnhdrs[i].s_vsize = 0; +#endif + } + } + + memcpy(p, scnhdrs, nscns * sizeof(struct scnhdr)); + if(swapped) + swap_scnhdr((struct scnhdr *)p, nscns, target_byte_sex); + p += nscns * sizeof(struct scnhdr); + + /* + * Note the base relocations (the contents of the reloc section), + * the symbol table and string table all have already been swapped if + * that was needed. + */ + memcpy(buf + syment_offset, syments, nsyments * sizeof(struct syment)); + memcpy(buf + string_offset, strings, strsize); + + /* + * Now with the file contents complete compute the CheckSum in the + * optional header and update that in the output buffer. + */ + if(ofile->mh != NULL){ + aouthdr.CheckSum = checksum(buf) + output_size; + memcpy(p_aouthdr, &aouthdr, sizeof(struct aouthdr)); + if(swapped) + swap_aouthdr((struct aouthdr *)p_aouthdr, target_byte_sex); + } + else{ + aouthdr64.CheckSum = checksum(buf) + output_size; + memcpy(p_aouthdr, &aouthdr64, sizeof(struct aouthdr_64)); + if(swapped) + swap_aouthdr_64((struct aouthdr_64 *)p_aouthdr,target_byte_sex); + } + + /* + * Create the pecoff file and write the buffer to the file. + */ + f = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if(f == -1) + system_fatal("Can't create output file: %s", out); + + if(write64(f, buf, output_size) != (ssize_t)output_size) + system_fatal("Can't write output file: %s", out); + + if(close(f) == -1) + system_fatal("Can't close output file: %s", out); +} + +/* + * create_ms_dos_stub() is pass a pointer to the buffer where to fill in the + * MS-DOS stub. + */ +static +void +create_ms_dos_stub( +struct ms_dos_stub *p) +{ + int i; + + p->e_magic = DOSMAGIC; + p->e_cblp = 0x90; + p->e_cp = 0x3; + p->e_crlc = 0x0; + p->e_cparhdr = 0x4; + p->e_minalloc = 0x0; + p->e_maxalloc = 0xffff; + p->e_ss = 0x0; + p->e_sp = 0xb8; + p->e_csum = 0x0; + p->e_ip = 0x0; + p->e_cs = 0x0; + p->e_lfarlc = 0x40; + p->e_ovno = 0x0; + + for(i = 0; i < 4; i++) + p->e_res[i] = 0x0; + + p->e_oemid = 0x0; + p->e_oeminfo = 0x0; + + for(i = 0; i < 10; i++) + p->e_res2[i] = 0x0; + + p->e_lfanew = 0x80; + + /* + * The sub dos program that prints "This program cannot be run in DOS + * mode". + */ + p->dos_program[0] = 0x0e; + p->dos_program[1] = 0x1f; + p->dos_program[2] = 0xba; + p->dos_program[3] = 0x0e; + p->dos_program[4] = 0x00; + p->dos_program[5] = 0xb4; + p->dos_program[6] = 0x09; + p->dos_program[7] = 0xcd; + p->dos_program[8] = 0x21; + p->dos_program[9] = 0xb8; + p->dos_program[10] = 0x01; + p->dos_program[11] = 0x4c; + p->dos_program[12] = 0xcd; + p->dos_program[13] = 0x21; + p->dos_program[14] = 0x54; + p->dos_program[15] = 0x68; + p->dos_program[16] = 0x69; + p->dos_program[17] = 0x73; + p->dos_program[18] = 0x20; + p->dos_program[19] = 0x70; + p->dos_program[20] = 0x72; + p->dos_program[21] = 0x6f; + p->dos_program[22] = 0x67; + p->dos_program[23] = 0x72; + p->dos_program[24] = 0x61; + p->dos_program[25] = 0x6d; + p->dos_program[26] = 0x20; + p->dos_program[27] = 0x63; + p->dos_program[28] = 0x61; + p->dos_program[29] = 0x6e; + p->dos_program[30] = 0x6e; + p->dos_program[31] = 0x6f; + p->dos_program[32] = 0x74; + p->dos_program[33] = 0x20; + p->dos_program[34] = 0x62; + p->dos_program[35] = 0x65; + p->dos_program[36] = 0x20; + p->dos_program[37] = 0x72; + p->dos_program[38] = 0x75; + p->dos_program[39] = 0x6e; + p->dos_program[40] = 0x20; + p->dos_program[41] = 0x69; + p->dos_program[42] = 0x6e; + p->dos_program[43] = 0x20; + p->dos_program[44] = 0x44; + p->dos_program[45] = 0x4f; + p->dos_program[46] = 0x53; + p->dos_program[47] = 0x20; + p->dos_program[48] = 0x6d; + p->dos_program[49] = 0x6f; + p->dos_program[50] = 0x64; + p->dos_program[51] = 0x65; + p->dos_program[52] = 0x2e; + p->dos_program[53] = 0x0d; + p->dos_program[54] = 0x0d; + p->dos_program[55] = 0x0a; + p->dos_program[56] = 0x24; + p->dos_program[57] = 0x0; + p->dos_program[58] = 0x0; + p->dos_program[59] = 0x0; + p->dos_program[60] = 0x0; + p->dos_program[61] = 0x0; + p->dos_program[62] = 0x0; + p->dos_program[63] = 0x0; +} + + +/* + * create_32bit_symbol_table() is called to process the input Mach-O file and + * create the pecoff symbol and string table. + */ +static +void +create_32bit_symbol_table( +struct arch *arch) +{ + char *object_addr; + struct symtab_command *st; + struct nlist *syms; + char *strs; + enum bool found_undef; +#ifdef HACK_TO_MATCH_TEST_CASE + uint32_t j, n_sect, bss_n_sect, common_n_sect, + bss_addr, common_addr, size; + struct load_command *lc; + struct segment_command *sg; + struct section *s; +#endif /* HACK_TO_MATCH_TEST_CASE */ + uint32_t i; + char *p; + + /* + * No symbols are actually needed in the pecoff file from the Mach-O + * file so create an empty symbol table. + */ + nsyments = 0; + + /* + * Make sure the Mach-O file does not have any undefined symbols. + */ + st = arch->object->st; + object_addr = arch->object->object_addr; + syms = (struct nlist *)(object_addr + st->symoff); + strs = object_addr + st->stroff; + if(swapped) + swap_nlist(syms, st->nsyms, host_byte_sex); + found_undef = FALSE; + for(i = 0; i < st->nsyms; i++){ + if((syms[i].n_type & N_STAB) != 0) + continue; + if((syms[i].n_type & N_TYPE) == N_UNDF){ + if(found_undef == FALSE){ + error("input file: %s contains undefined symbols:", + arch->file_name); + } + found_undef = TRUE; + if(syms[i].n_un.n_strx != 0) + printf("%s\n", strs + syms[i].n_un.n_strx); + else + printf("symbol at index %u is undefined but has NULL " + "name (like a malformed Mach-O file)\n", i); + } + } + if(found_undef == TRUE) + fatal("undefined symbols are unsupported for conversion to a " + "pecoff file"); + +#ifdef HACK_TO_MATCH_TEST_CASE + /* + * The hack implementation of this routine exist only in order to + * match the current ld_efi(1) script that uses objcopy(1) to make the + * pecoff file. So for that only the common symbols and bss symbols + * make it into the output pecoff file. + * + */ + + /* + * First figure out the section number of the common and bss sections + * and address of those sections. + */ + n_sect = 1; + bss_n_sect = 0; + bss_addr = 0; + common_n_sect = 0; + common_addr = 0; + lc = arch->object->load_commands; + for(i = 0; i < arch->object->mh->ncmds; i++){ + if(lc->cmd == LC_SEGMENT){ + sg = (struct segment_command *)lc; + if(strcmp(sg->segname, SEG_DATA) == 0){ + s = (struct section *) + ((char *)sg + sizeof(struct segment_command)); + for(j = 0; j < sg->nsects; j++){ + if(strcmp(s->sectname, SECT_BSS) == 0){ + bss_n_sect = n_sect; + bss_addr = s->addr; + } + else if(strcmp(s->sectname, SECT_COMMON) == 0){ + common_n_sect = n_sect; + common_addr = s->addr; + } + s++; + n_sect++; + } + } + else{ + n_sect += sg->nsects; + } + } + lc = (struct load_command *)((char *)lc + lc->cmdsize); + } + + /* + * Count the number of the common and bss sections symbols and add up + * the size of their strings. Note the size of long section names is + * already accounted for in strsize by the code in process_32bit_arch(). + */ + for(i = 0; i < st->nsyms; i++){ + if((syms[i].n_type & N_STAB) == 0 && + (syms[i].n_type & N_TYPE) == N_SECT && + (syms[i].n_sect == bss_n_sect || + syms[i].n_sect == common_n_sect)){ + nsyments++; + if(syms[i].n_un.n_strx != 0){ + size = strlen(strs + syms[i].n_un.n_strx); + if(size > E_SYMNMLEN) + strsize += strlen(strs + syms[i].n_un.n_strx) + 1; + } + } + } +#endif /* HACK_TO_MATCH_TEST_CASE */ + + /* + * Allocate space for the pecoff symbol table and string table. + */ + syments = allocate(nsyments * sizeof(struct syment)); + memset(syments, '\0', nsyments * sizeof(struct syment)); + strings = allocate(strsize); + + /* + * Put the size of the string table in the string table first. Then + * the strings for the long section names right after the size. + */ + p = strings; + + i = strsize; + if(swapped) + i = SWAP_INT(i); + memcpy(p, &i, sizeof(uint32_t)); + p += sizeof(uint32_t); + + memcpy(p, section_names, section_names_size); + p += section_names_size; + +#ifdef HACK_TO_MATCH_TEST_CASE + /* + * First put in the bss symbols, again to match what is done by + * objcopy. + */ + j = 0; + for(i = 0; i < st->nsyms; i++){ + if((syms[i].n_type & N_STAB) == 0 && + (syms[i].n_type & N_TYPE) == N_SECT && + syms[i].n_sect == bss_n_sect){ + if(syms[i].n_un.n_strx != 0){ + size = strlen(strs + syms[i].n_un.n_strx); + if(size > E_SYMNMLEN){ + syments[j].e.e.e_zeroes = 0; + syments[j].e.e.e_offset = p - strings; + strcpy(p, strs + syms[i].n_un.n_strx); + p += strlen(strs + syms[i].n_un.n_strx) + 1; + } + else{ + strncpy(syments[j].e.e_name, + strs + syms[i].n_un.n_strx, E_SYMNMLEN); + } + } + syments[j].e_value = syms[i].n_value - bss_addr; + syments[j].e_scnum = bss_scnum; + syments[j].e_type = 0; + syments[j].e_sclass = IMAGE_SYM_CLASS_EXTERNAL; + syments[j].e_numaux = 0; + j++; + } + } + /* + * Next put in the common symbols, again to match what is done by + * objcopy. + */ + for(i = 0; i < st->nsyms; i++){ + if((syms[i].n_type & N_STAB) == 0 && + (syms[i].n_type & N_TYPE) == N_SECT && + syms[i].n_sect == common_n_sect){ + if(syms[i].n_un.n_strx != 0){ + size = strlen(strs + syms[i].n_un.n_strx); + if(size > E_SYMNMLEN){ + syments[j].e.e.e_zeroes = 0; + syments[j].e.e.e_offset = p - strings; + strcpy(p, strs + syms[i].n_un.n_strx); + p += strlen(strs + syms[i].n_un.n_strx) + 1; + } + else{ + strncpy(syments[j].e.e_name, + strs + syms[i].n_un.n_strx, E_SYMNMLEN); + } + } + syments[j].e_value = syms[i].n_value - common_addr; + syments[j].e_scnum = common_scnum; + syments[j].e_type = 0; + syments[j].e_sclass = IMAGE_SYM_CLASS_EXTERNAL; + syments[j].e_numaux = 0; + j++; + } + } + + if(swapped) + swap_syment(syments, nsyments, target_byte_sex); + +#endif /* HACK_TO_MATCH_TEST_CASE */ +} + +/* + * create_64bit_symbol_table() is called to process the input Mach-O file and + * create the pecoff symbol and string table. + */ +static +void +create_64bit_symbol_table( +struct arch *arch) +{ + char *p; + uint32_t i; + char *object_addr; + struct symtab_command *st; + struct nlist_64 *syms64; + char *strs; + enum bool found_undef; + + st = arch->object->st; + object_addr = arch->object->object_addr; + syms64 = (struct nlist_64 *)(object_addr + st->symoff); + strs = object_addr + st->stroff; + if(swapped) + swap_nlist_64(syms64, st->nsyms, host_byte_sex); + /* + * If the entry point option was specified then look for that symbol + * and set the entry point value. + */ + if(entry_point != NULL){ + for(i = 0; i < st->nsyms; i++){ + if((syms64[i].n_type & N_STAB) == 0 && + syms64[i].n_un.n_strx != 0 && + strcmp(strs + syms64[i].n_un.n_strx, entry_point) == 0){ + entry = (uint32_t)syms64[i].n_value; + break; + } + } + if(i == st->nsyms) + fatal("can't find symbol for -e %s in input file: %s", + entry_point, arch->file_name); + } + + /* + * Make sure the Mach-O file does not have any undefined symbols. + */ + found_undef = FALSE; + for(i = 0; i < st->nsyms; i++){ + if((syms64[i].n_type & N_STAB) != 0) + continue; + if((syms64[i].n_type & N_TYPE) == N_UNDF){ + if(found_undef == FALSE){ + error("input file: %s contains undefined symbols:", + arch->file_name); + } + found_undef = TRUE; + if(syms64[i].n_un.n_strx != 0) + printf("%s\n", strs + syms64[i].n_un.n_strx); + else + printf("symbol at index %u is undefined but has NULL " + "name (like a malformed Mach-O file)\n", i); + } + } + if(found_undef == TRUE) + fatal("undefined symbols are unsupported for conversion to a " + "pecoff file"); + + /* + * No symbols are actually needed in the pecoff file from the Mach-O + * file so create an empty symbol table. + * + * Set the number of symbols to zero and allocate the string table. + * Note the size of long section names is already accounted for in + * strsize by the code in process_64bit_arch(). + */ + nsyments = 0; + strings = allocate(strsize); + + /* + * Put the size of the string table in the string table first. Then + * the strings for the long section names right after the size. + */ + p = strings; + + i = strsize; + if(swapped) + i = SWAP_INT(i); + memcpy(p, &i, sizeof(uint32_t)); + p += sizeof(uint32_t); + + memcpy(p, section_names, section_names_size); +} + +/* + * create_base_reloc() is called to process the input Mach-O file and gather + * the info needed and then to create the base relocation entries. + */ +static +void +create_base_reloc( +struct arch *arch) +{ + uint32_t ncmds, i, j; + uint64_t addr, first_addr; + struct load_command *lc; + struct segment_command *sg; + struct segment_command_64 *sg64; + struct section *s; + struct section_64 *s64; + struct relocation_info *relocs; + + char *object_addr; + struct dysymtab_command *dyst; + + if(arch->object->mh != NULL) + ncmds = arch->object->mh->ncmds; + else + ncmds = arch->object->mh64->ncmds; + dyst = arch->object->dyst; + object_addr = arch->object->object_addr; + + first_addr = 0; + lc = arch->object->load_commands; + for(i = 0; i < ncmds; i++){ + if(lc->cmd == LC_SEGMENT){ + sg = (struct segment_command *)lc; + if(first_addr == 0) + first_addr = sg->vmaddr; + s = (struct section *) + ((char *)sg + sizeof(struct segment_command)); + for(j = 0; j < sg->nsects; j++){ + relocs = (struct relocation_info *)(object_addr + + s[j].reloff); + if(swapped) + swap_relocation_info(relocs, s[j].nreloc, + host_byte_sex); + if(arch->object->mh_cputype == CPU_TYPE_I386) + gather_base_reloc_info(s[j].addr, relocs, s[j].nreloc, + CPU_TYPE_I386, 2, GENERIC_RELOC_VANILLA, + IMAGE_REL_BASED_HIGHLOW); + else if(arch->object->mh_cputype == CPU_TYPE_ARM) + gather_base_reloc_info(s[j].addr, relocs, s[j].nreloc, + CPU_TYPE_ARM, 2, GENERIC_RELOC_VANILLA, + IMAGE_REL_BASED_HIGHLOW); + if((s[j].flags & SECTION_TYPE) == + S_NON_LAZY_SYMBOL_POINTERS){ + for(addr = s[j].addr; + addr < s[j].addr + s[j].size; + addr += 4) { + add_base_reloc(addr, IMAGE_REL_BASED_HIGHLOW); + } + } + } + } + else if(lc->cmd == LC_SEGMENT_64){ + sg64 = (struct segment_command_64 *)lc; + if(arch->object->mh_cputype == CPU_TYPE_X86_64) { + /* + * X86_64 relocations are relative to the first writable + * segment. + */ + /* + * But arm64 relocations are NOT relative to the first + * writable segment but just the first segment. + */ + if((first_addr == 0) && + ((sg64->initprot & VM_PROT_WRITE) != 0)) { + first_addr = sg64->vmaddr; + } + } else { + if(first_addr == 0) + first_addr = sg64->vmaddr; + } + s64 = (struct section_64 *) + ((char *)sg64 + sizeof(struct segment_command_64)); + for(j = 0; j < sg64->nsects; j++){ + relocs = (struct relocation_info *)(object_addr + + s64[j].reloff); + if(swapped) + swap_relocation_info(relocs, s64[j].nreloc, + host_byte_sex); + if(arch->object->mh_cputype == CPU_TYPE_X86_64) + gather_base_reloc_info((uint32_t)s64[j].addr, relocs, + s64[j].nreloc, CPU_TYPE_X86_64, 3, + X86_64_RELOC_UNSIGNED, IMAGE_REL_BASED_DIR64); + else if(arch->object->mh_cputype == CPU_TYPE_ARM64) + gather_base_reloc_info((uint32_t)s64[j].addr, relocs, + s64[j].nreloc, CPU_TYPE_ARM64, 3, + ARM64_RELOC_UNSIGNED, IMAGE_REL_BASED_DIR64); + if((s64[j].flags & SECTION_TYPE) == + S_NON_LAZY_SYMBOL_POINTERS){ + for(addr = s64[j].addr; + addr < s64[j].addr + s64[j].size; + addr += 8) { + add_base_reloc(addr, IMAGE_REL_BASED_DIR64); + } + } + } + } + lc = (struct load_command *)((char *)lc + lc->cmdsize); + } + if(dyst != NULL && dyst->nlocrel != 0){ + relocs = (struct relocation_info *)(object_addr + + dyst->locreloff); + if(swapped) + swap_relocation_info(relocs, dyst->nlocrel, host_byte_sex); + if(arch->object->mh_cputype == CPU_TYPE_I386) + gather_base_reloc_info((uint32_t)first_addr, relocs, + dyst->nlocrel, CPU_TYPE_I386, 2, + GENERIC_RELOC_VANILLA, + IMAGE_REL_BASED_HIGHLOW); + else if(arch->object->mh_cputype == CPU_TYPE_ARM) + gather_base_reloc_info((uint32_t)first_addr, relocs, + dyst->nlocrel, CPU_TYPE_ARM, 2, + GENERIC_RELOC_VANILLA, + IMAGE_REL_BASED_HIGHLOW); + else if(arch->object->mh_cputype == CPU_TYPE_X86_64) + gather_base_reloc_info((uint32_t)first_addr, relocs, + dyst->nlocrel, CPU_TYPE_X86_64, 3, + X86_64_RELOC_UNSIGNED, + IMAGE_REL_BASED_DIR64); + else if(arch->object->mh_cputype == CPU_TYPE_ARM64) + gather_base_reloc_info((uint32_t)first_addr, relocs, + dyst->nlocrel, CPU_TYPE_ARM64, 3, + ARM64_RELOC_UNSIGNED, + IMAGE_REL_BASED_DIR64); + } + /* + if(dyst != NULL && dyst->nextrel != 0) + ; TODO error if there are external relocation entries */ + + /* + * Now with all the info gathered make the base relocation entries. + */ + make_base_relocs(); +} + +/* + * gather_base_reloc_info() is passed the base address for the set of Mach-O + * relocation entries. And is passed the cpu_type, length and macho_reloc_type + * to look for and the base_reloc_type to create if found. + */ +static +void +gather_base_reloc_info( +uint32_t addr, +struct relocation_info *relocs, +uint32_t nreloc, +cpu_type_t cpu_type, +uint32_t length, +int macho_reloc_type, +int base_reloc_type) +{ + uint32_t i, r_address, r_pcrel, r_length, r_extern, r_type; + struct scattered_relocation_info *sreloc; + + for(i = 0; i < nreloc; i++){ + if((relocs[i].r_address & R_SCATTERED) != 0){ + sreloc = (struct scattered_relocation_info *)(relocs + i); + r_address = sreloc->r_address; + r_pcrel = sreloc->r_pcrel; + r_length = sreloc->r_length; + r_type = (enum reloc_type_generic)sreloc->r_type; + r_extern = 0; + } + else{ + r_address = relocs[i].r_address; + r_pcrel = relocs[i].r_pcrel; + r_length = relocs[i].r_length; + r_extern = relocs[i].r_extern; + r_type = (enum reloc_type_generic)relocs[i].r_type; + } + + if(r_extern == 0 && r_pcrel == 0 && + r_length == length && r_type == macho_reloc_type) + add_base_reloc(addr + r_address, base_reloc_type); + else + ; /* TODO add checking and error messages here */ + + if((relocs[i].r_address & R_SCATTERED) == 0){ + if(reloc_has_pair(cpu_type, relocs[i].r_type)) + i++; + } + else{ + sreloc = (struct scattered_relocation_info *)relocs + i; + if(reloc_has_pair(cpu_type, sreloc->r_type)) + i++; + } + } +} + +/* + * add_base_reloc() is passed a addr and a type for a base relocation entry to + * add to the list. + */ +static +void +add_base_reloc( +uint64_t addr, +uint32_t type) +{ + static int max = 0; + struct base_reloc *new_base_relocs; + + if(!max){ + max = 128; + base_relocs = (struct base_reloc *) + malloc(max * sizeof(struct base_reloc)); + } + if(nbase_reloc >= max){ + new_base_relocs = malloc(2 * max * sizeof(struct base_reloc)); + memcpy(new_base_relocs, base_relocs, + max * sizeof(struct base_reloc)); + max *= 2; + free(base_relocs); + base_relocs = new_base_relocs; + } + base_relocs[nbase_reloc].addr = addr; + base_relocs[nbase_reloc].type = type; + nbase_reloc++; +} + +/* + * The base relocation table in a PECOFF file is divided into blocks. Each + * block represents the base relocations for a 4K page. Each block must start + * on a 32-bit boundary. Which is why one "nop" base relocation entry may be + * be added as padding in a block. + */ +#define MAX_BLOCK_OFFSET 0x1000 +#define BLOCK_MASK (MAX_BLOCK_OFFSET-1) + +/* + * make_base_relocs() takes the info for the base relocation entries gathered + * and creates the fixup blocks as they would be in a PECOFF file and sets the + * static variables reloc_contents and reloc_size to the pointer to contents + * and the size of that contents. + */ +static +void +make_base_relocs( +void) +{ + int blockcnt; + int i, entries; + uint64_t base; + int size, s_size, pad; + char *fb; + struct base_relocation_block_header *h; + struct base_relocation_entry *b; + uint32_t offset; + + blockcnt = 0; + + /* + * After we create each base relocation block we will allocate space + * for it in the .reloc section contents buffer and copy it into the + * buffer. + */ + reloc_size = 0; + reloc_contents = NULL; + + /* + * If there are no base relocation entries return so we don't create a + * base relocation block with 0 entries. + */ + if(nbase_reloc == 0) + return; + + qsort(base_relocs, nbase_reloc, sizeof(struct base_reloc), + (int (*)(const void *, const void *))cmp_base_relocs); + + /* + * The size of the base relocation tables must be a multiple of 4 bytes. + * so we may need to add one relocation entry as padding. We make this + * fixup block large enought to hold all the base relocation entries. + * But it will be broken up for the base relocation entries for each + * each group that refers to the same 4K page. + */ + size = sizeof(struct base_relocation_block_header) + + (nbase_reloc + 1) * sizeof(struct base_relocation_entry); + fb = malloc(size); + + + entries = 0; + base = base_relocs[0].addr & ~BLOCK_MASK; + h = (struct base_relocation_block_header *)fb; + b = (struct base_relocation_entry *) + (fb + sizeof(struct base_relocation_block_header)); + for(i = 0; i < nbase_reloc; i++){ + offset = (uint32_t)(base_relocs[i].addr - base); + if(offset >= MAX_BLOCK_OFFSET) { + /* add padding if needed */ + if((entries % 2) != 0){ + b[entries].type = IMAGE_REL_BASED_ABSOLUTE; + b[entries].offset = 0; + entries++; + } + h->page_rva = (uint32_t)base; + size = sizeof(struct base_relocation_block_header) + + entries * sizeof(struct base_relocation_entry); + h->block_size = size; + if(swapped){ + swap_base_relocation_block_header(h, + target_byte_sex); + swap_base_relocation_entry(b, entries, + target_byte_sex); + } + /* copy this finished block into the .reloc contents buffer */ + reloc_contents = reallocate(reloc_contents, reloc_size + size); + memcpy(reloc_contents + reloc_size, fb, size); + reloc_size += size; + + entries = 0; + blockcnt++; + base = base_relocs[i].addr & ~BLOCK_MASK; + offset = (uint32_t)(base_relocs[i].addr - base); + } + b[entries].type = base_relocs[i].type; + b[entries].offset = offset; + entries++; + } + + /* add padding if needed */ + if((entries % 2) != 0){ + b[entries].type = IMAGE_REL_BASED_ABSOLUTE; + b[entries].offset = 0; + entries++; + } + h->page_rva = (uint32_t)base; + size = sizeof(struct base_relocation_block_header) + + entries * sizeof(struct base_relocation_entry); + h->block_size = size; + if(swapped){ + swap_base_relocation_block_header(h, target_byte_sex); + swap_base_relocation_entry(b, entries, target_byte_sex); + } + + /* copy this last block into the .reloc contents buffer */ + reloc_contents = reallocate(reloc_contents, reloc_size + size); + memcpy(reloc_contents + reloc_size, fb, size); + reloc_size += size; + + /* + * The make the relocs buffer the s_size rounded to file_alignment and + * zero out the padding + */ + s_size = rnd32(reloc_size, file_alignment); + pad = s_size - reloc_size; + reloc_contents = reallocate(reloc_contents, s_size); + memset(reloc_contents + reloc_size, '\0', pad); + + blockcnt++; + free(fb); +} + +static +int +cmp_base_relocs( +struct base_reloc *x1, +struct base_reloc *x2) +{ + if(x1->addr < x2->addr) + return(-1); + if(x1->addr == x2->addr) + return(0); + /* x1->addr > x2->addr */ + return(1); +} + +/* + * create_debug() is called to create the .debug section contents from + * the -d filename argument. + */ +static +void +create_debug( +struct arch *arch) +{ + char *p; + uint32_t i, ncmds, s_size; + struct load_command *lc; + struct uuid_command *uuid; + + /* + * Allocate space for everything that will be in the .debug section: + * the debug_directory_entry struct + * the mtoc_debug_info struct + * the name of the -d filename argument null terminated. + */ + debug_size = sizeof(struct debug_directory_entry) + + sizeof(struct mtoc_debug_info) + + (uint32_t)strlen(debug_filename) + 1; + /* + * The make the debug buffer the s_size rounded to the file_alignment + * and also zero out the padding + */ + s_size = rnd32(debug_size, file_alignment); + debug_contents = allocate(s_size); + memset(debug_contents, '\0', s_size); + /* + * Set up pointers to all the parts to be filled in. + */ + p = debug_contents; + dde = (struct debug_directory_entry *)p; + p += sizeof(struct debug_directory_entry); + mdi = (struct mtoc_debug_info *)p; + p += sizeof(struct mtoc_debug_info); + + dde->Characteristics = 0; + dde->TimeDateStamp = (uint32_t)time(NULL); + dde->MajorVersion = 0; + dde->MinorVersion = 0; + dde->Type = IMAGE_DEBUG_TYPE_CODEVIEW; + dde->SizeOfData = sizeof(struct mtoc_debug_info) + + (uint32_t)strlen(debug_filename) + 1; + /* + * These two will be filled in later when address and offsets + * are known. + */ + dde->AddressOfRawData = 0; + dde->PointerToRawData = 0; + + mdi->Signature = MTOC_SIGNATURE; + if(arch->object->mh != NULL) + ncmds = arch->object->mh->ncmds; + else + ncmds = arch->object->mh64->ncmds; + lc = arch->object->load_commands; + for(i = 0; i < ncmds; i++){ + if(lc->cmd == LC_UUID){ + uuid = (struct uuid_command *)lc; + if (debug_uuid != NULL) { + string_to_uuid (debug_uuid, uuid->uuid); + } + /* Swizzle UUID to match EFI GUID definition */ + mdi->uuid[0] = uuid->uuid[3]; + mdi->uuid[1] = uuid->uuid[2]; + mdi->uuid[2] = uuid->uuid[1]; + mdi->uuid[3] = uuid->uuid[0]; + mdi->uuid[4] = uuid->uuid[5]; + mdi->uuid[5] = uuid->uuid[4]; + mdi->uuid[6] = uuid->uuid[7]; + mdi->uuid[7] = uuid->uuid[6]; + mdi->uuid[8] = uuid->uuid[8]; + mdi->uuid[9] = uuid->uuid[9]; + mdi->uuid[10] = uuid->uuid[10]; + mdi->uuid[11] = uuid->uuid[11]; + mdi->uuid[12] = uuid->uuid[12]; + mdi->uuid[13] = uuid->uuid[13]; + mdi->uuid[14] = uuid->uuid[14]; + mdi->uuid[15] = uuid->uuid[15]; + break; + } + lc = (struct load_command *)((char *)lc + lc->cmdsize); + } + + strcpy(p, debug_filename); +} + +/* + * set_debug_addrs_and_offsets() is called after the .debug section's address + * and offset has been set and this routine sets the other needed addresses + * and offsets in the section contents. And swaps the section contents if + * needed for output. + */ +static +void +set_debug_addrs_and_offsets( +void) +{ + dde->AddressOfRawData = debug_scnhdr->s_vaddr + + sizeof(struct debug_directory_entry); + dde->PointerToRawData = debug_scnhdr->s_scnptr + + sizeof(struct debug_directory_entry); + if(swapped){ + swap_debug_directory_entry(dde, target_byte_sex); + swap_mtoc_debug_info(mdi, target_byte_sex); + } +} + +/* + * checksum() calculates the value for the CheckSum field in the optional + * header from the bytes in the output buffer passed to it which has the + * size output_size. + */ +static +uint32_t +checksum( +unsigned char *buf) +{ + uint32_t i, v, t; + + t = 0; + for(i = 0; i < output_size; i += 2){ + if(output_size - i == 1) + v = buf[i]; + else + v = buf[i] + (buf[i+1] << 8); + t += v; + t = 0xffff & (t + (t >> 0x10)); + } + return(0xffff & (t + (t >> 0x10))); +} + +/* + * string_to_uuid() creates a 128-bit uuid from a well-formatted UUID string + * (i.e. aabbccdd-eeff-gghh-iijj-kkllmmnnoopp) + */ +static +void +string_to_uuid( +char *string, +uint8_t *uuid) +{ + uint8_t count; + + count = sscanf (string, UUID_FORMAT_STRING, + &uuid[0], &uuid[1], &uuid[2], &uuid[3], + &uuid[4], &uuid[5], &uuid[6], &uuid[7], + &uuid[8], &uuid[9], &uuid[10], &uuid[11], + &uuid[12], &uuid[13], &uuid[14], &uuid[15]); + + if (count != 16) { + fatal ("invalid UUID specified for -u option"); + } +} diff --git a/buildmtoc_1009.2.sh b/buildmtoc_1009.2.sh new file mode 100755 index 000000000..a692eccf4 --- /dev/null +++ b/buildmtoc_1009.2.sh @@ -0,0 +1,247 @@ +#!/bin/bash + +# Script for building MTOC +# + +if [ -z "$WORKSPACE" ] +then + echo WORKSPACE must be defined to Clover root path + export WORKSPACE=`pwd` +fi + +# Ctools source version +# here we can change source versions of tools +# +OSXVER="`/usr/bin/sw_vers -productVersion | cut -d '.' -f1,2`" +if [[ ${OSXVER} < 10.15 ]]; then + export CCTOOLS_VERSION=${CCTOOLS_VERSION:-cctools-949.0.1} +else + # export CCTOOLS_VERSION=${CCTOOLS_VERSION:-cctools-986} + export CCTOOLS_VERSION=${CCTOOLS_VERSION:-cctools-1009.2} +fi +# Change PREFIX if you want mtoc installed on different place +# +TOOLCHAIN_DIR=${TOOLCHAIN_DIR:-~/src/opt/local} +export PREFIX=${PREFIX:-$TOOLCHAIN_DIR} + +# You can change DIR_MAIN if u want cctools source downloaded +# in different folder. +# +export RAMDISK_MNT_PT=/tmp/buildmtoc-ramdisk +export DIR_MAIN=${DIR_MAIN:-~/src} +export DIR_TOOLS=${DIR_TOOLS:-$DIR_MAIN/tools} +export DIR_BUILD=${DIR_BUILD:-$RAMDISK_MNT_PT} +export DIR_DOWNLOADS=${DIR_DOWNLOADS:-$DIR_TOOLS/download} +export DIR_LOGS=${DIR_LOGS:-$DIR_TOOLS/logs} + +# Here we set MAKEFLAGS for GMAKE so it knows how many cores can use +# faster compile! +# +export MAKEFLAGS="-j `sysctl -n hw.ncpu`" + +set -u + +### Check Functions ### + +# Function: checking installation of Xcode Tools +fnCheckXcode () { + [ ! -f /usr/bin/xcodebuild ] && \ + echo "ERROR: Install Xcode Tools from Apple before using this script." && \ + exit +} + + +### Main Function START ### + +# Function: Creating directory structure for EDK + +[ ! -d ${DIR_MAIN} ] && mkdir ${DIR_MAIN} +[ ! -d ${DIR_TOOLS} ] && mkdir ${DIR_TOOLS} +[ ! -d ${DIR_DOWNLOADS} ] && mkdir ${DIR_DOWNLOADS} +[ ! -d ${DIR_LOGS} ] && mkdir ${DIR_LOGS} +[ ! -d ${PREFIX}/bin ] && mkdir -p ${PREFIX}/bin +echo + +# Function: to manage PATH +pathmunge () { + if [[ ! $PATH =~ (^|:)$1(:|$) ]]; then + if [[ "${2:-}" = "after" ]]; then + export PATH=$PATH:$1 + else + export PATH=$1:$PATH + fi + fi +} + +# RAMdisk +function mountRamDisk() { + dev_ramdisk=$(mount | grep "$RAMDISK_MNT_PT" | awk '{print $1}') + if [ -z "$dev_ramdisk" ];then + echo "- Creating new RAM disk" + dev_ramdisk=`hdiutil attach -nomount ram://614400 | awk '{print $1}'` + echo + [ -n "$dev_ramdisk" ] && newfs_hfs -v "Build Mtoc RamDisk" "$dev_ramdisk" + [ ! -d "$RAMDISK_MNT_PT" ] && mkdir "$RAMDISK_MNT_PT" + mount -t hfs "$dev_ramdisk" "$RAMDISK_MNT_PT" + touch "$RAMDISK_MNT_PT/.metadata_never_index" + fi + # Automatically remove RAMDISK on exit + trap 'echo; echo "- Ejecting RAM disk"; cd "$HOME"; umount "$RAMDISK_MNT_PT" && hdiutil detach "$dev_ramdisk"' EXIT +} + + +### Download ### + +fnDownloadCctools () +# Function: Download cctools source +{ + cd "$DIR_DOWNLOADS" + local tarball="${CCTOOLS_VERSION}.tar.gz" + if [[ ! -f ${DIR_DOWNLOADS}/${CCTOOLS_VERSION}.tar.gz ]]; then + echo "Status: ${CCTOOLS_VERSION} not found." + curl -k -f -o download.tmp --remote-name https://codeload.github.com/apple-oss-distributions/cctools/tar.gz/refs/tags/${CCTOOLS_VERSION} || exit 1 + mv download.tmp $tarball + fi +} +#https://github.com/apple-oss-distributions/cctools/archive/refs/tags/cctools-973.0.1.tar.gz + +### Extract ### + +# Function to extract source tarballs +fnExtract () +{ + exec 3>&1 1>&2 # Save stdout and redirect stdout to stderr + + # OS version check + local OSXVER="`/usr/bin/sw_vers -productVersion | cut -d '.' -f1,2`" + + local tarball="$1" + local package=${tarball%%.tar*} + tarball="${DIR_DOWNLOADS}/$tarball" + local flagfile="${DIR_BUILD}/$package.extracted" + + local filetype=$(file -L --brief "$tarball" | tr '[A-Z]' '[a-z]') + local tar_filter_option="" + + case ${filetype} in # convert to lowercase + gzip\ *) tar_filter_option='--gzip' ;; + bzip2\ *) tar_filter_option='--bzip2';; + lzip\ *) tar_filter_option='--lzip' ;; + lzop\ *) tar_filter_option='--lzop' ;; + lzma\ *) tar_filter_option='--lzma' ;; + xz\ *) tar_filter_option='--xz' ;; + *tar\ archive*) tar_filter_option='';; + *) echo "Unrecognized file format of '$tarball'" + exit 1 + ;; + esac + + # Get the root directory from the tarball + local first_line=$(dd if="$tarball" bs=1024 count=256 2>/dev/null | \ + tar -t $tar_filter_option -f - 2>/dev/null | head -1) + local top_level_dir=${first_line#./} # remove leading ./ + top_level_dir=${top_level_dir%%/*} # keep only the top level directory + + [ -z "$top_level_dir" ] && echo "Error can't extract top level dir from $tarball" && exit 1 + + if [[ ! -d "${DIR_BUILD}/$top_level_dir" || ! -f "$flagfile" ]]; then + echo "- ${package} extract..." + rm -rf "${DIR_BUILD}/$top_level_dir" # Remove old directory if exists + tar -C "$DIR_BUILD" -x "$tar_filter_option" -f "${tarball}" && touch "${DIR_BUILD}/$package.extracted" + + #jief copy the modified version that keeps __mod_init_func section + #cp "$WORKSPACE"/BaseTools/Source/C/mtoc/mtoc-v921_jief.c "$DIR_BUILD/$top_level_dir"/efitools/mtoc.c + if [[ ${OSXVER} < 10.15 ]]; then + cp "$WORKSPACE"/Patches/Mtoc/mtoc-v973_jief.c "$DIR_BUILD/$top_level_dir"/efitools/mtoc.c + else + cp "$WORKSPACE"/Patches/Mtoc/mtoc-1009.2_jief.c "$DIR_BUILD/$top_level_dir"/efitools/mtoc.c + cp "$WORKSPACE"/Patches/Mtoc/libstuff-1009.2.xcconfig "$DIR_BUILD/$top_level_dir"/xcode/libstuff.xcconfig + fi + fi + + # Restore stdout for the result and close file desciptor 3 + exec 1>&3- + echo "${DIR_BUILD}/$top_level_dir" # Return the full path where the tarball has been extracted +} + +### Compile ### + +fnCompileMtoc () +# Function: Compiling Mtoc in PREFIX location +{ + # Mount RamDisk + mountRamDisk + + # OS version check + local OSXVER="`/usr/bin/sw_vers -productVersion | cut -d '.' -f1,2`" + + # Extract the tarball + local CCTOOLS_DIR=$(fnExtract "${CCTOOLS_VERSION}.tar.gz") + + # Set SDKROOT for Makefiles + export SDKROOT="$(xcrun -n --show-sdk-path)" + + # Mtoc build + local cmd logfile + cd "$CCTOOLS_DIR" + # Replace DT_TOOLCHAIN_DIR needed in cctools as a workaround for building in Xcode 15 + sed -i '' 's/DT_TOOLCHAIN_DIR/TOOLCHAIN_DIR/g' "$CCTOOLS_DIR"/xcode/cctools.xcconfig + sed -i '' 's/DT_TOOLCHAIN_DIR/TOOLCHAIN_DIR/g' "$CCTOOLS_DIR"/xcode/strip.xcconfig + sed -i '' 's/enum :/enum/g' "$CCTOOLS_DIR"/include/stuff/depinfo.h + # Removal of _structs.h needed as a workaround for a bug in cctools-900 or above + /bin/rm -f ./include/mach/i386/_structs.h + echo "- cctools-${CCTOOLS_VERSION} make mtoc..." + if [[ ${OSXVER} < 10.15 ]]; then + cmd="(make LTO= EFITOOLS=efitools -C libstuff) && (make -C efitools)" + else + cmd="(xcodebuild -scheme mtoc -configuration Release ONLY_ACTIVE_ARCH=YES CONFIGURATION_BUILD_DIR=$PREFIX/bin)" + fi + logfile="$DIR_LOGS/mtoc.make.log.txt" + echo "$cmd" > "$logfile" + eval "$cmd" >> "$logfile" 2>&1 + if [[ $? -ne 0 ]]; then + echo "Error compiling cctools-${CCTOOLS_VERSION} mtoc ! Check the log $logfile" + exit 1 + fi + echo "- cctools-${CCTOOLS_VERSION} installing mtoc..." + if [[ ${OSXVER} < 10.15 ]]; then + cmd="install -c -s -m 555 efitools/mtoc.NEW ${PREFIX}/bin/mtoc.NEW_jief" + else + cmd="mv ${PREFIX}/bin/mtoc ${PREFIX}/bin/mtoc.NEW_jief" + fi + logfile="$DIR_LOGS/mtoc.install.log.txt" + echo "$cmd" > "$logfile" + eval "$cmd" >> "$logfile" 2>&1 + if [[ $? -ne 0 ]]; then + echo "Error installing cctools-${CCTOOLS_VERSION} mtoc ! Check the log $logfile" + exit 1 + fi + rm -rf "$CCTOOLS_DIR" + echo "- cctools-${CCTOOLS_VERSION} mtoc installed in $PREFIX" +} + +# remove unused additional files +fnCleanUp() +{ + if [[ -f $PREFIX/bin/*.dSYM ]]; then + rm -r $PREFIX/bin/*.dSYM $PREFIX/bin/*.a + fi +} + +### fnFunctions ### + +fnMtoc () +# Function: Mtoc main script +{ + fnDownloadCctools + fnCompileMtoc + fnCleanUp +} + + +### Main ### + +# Add XCode bin directory for the command line tools to the PATH +#pathmunge "$(xcode-select --print-path)"/usr/bin + +fnMtoc