From 49782126c69171b86b4e46692e9e29913659c820 Mon Sep 17 00:00:00 2001 From: Eric Gallager Date: Thu, 26 Feb 2026 13:28:45 -0500 Subject: [PATCH 01/13] Silence CMake deprecation warning CMake Deprecation Warning at CMakeLists.txt:3 (cmake_minimum_required): Compatibility with CMake < 3.10 will be removed from a future version of CMake. Update the VERSION argument value. Or, use the ... syntax to tell CMake that the project requires at least but has been updated to work with policies introduced by or earlier. --- .gitignore | 1 + CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5a783b30..1d2c78cc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ ld64-* zPATCHES cctools-* libtapi* +build/* diff --git a/CMakeLists.txt b/CMakeLists.txt index f223eea0..41982c31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ # Top Level CMake file for XTOOLS. -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.10) if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "No build type selected, default to MinSizeRel") From 4c77f0f009c3bc9038dbdd1ce35c9be1825ce6c1 Mon Sep 17 00:00:00 2001 From: Eric Gallager Date: Thu, 26 Feb 2026 13:36:35 -0500 Subject: [PATCH 02/13] Apply patches from MacPorts see: https://github.com/macports/macports-ports/tree/master/devel/darwin-xtools/files --- CMakeLists.txt | 23 +- cctools/as/CMakeLists.txt | 12 +- cctools/cbtlibs/CMakeLists.txt | 1 + cctools/ld/pass1.c | 41 - cctools/misc/strings.c.rej | 13 + cctools/otool/arm_disasm.c | 2 +- cctools/otool/i386_disasm.c | 2 +- cctools/otool/print_objc.c | 8 + ld64/src/CMakeLists.txt | 2 +- ld64/src/ld/CMakeLists.txt | 21 +- ld64/src/ld/InputFiles.cpp | 2 + ld64/src/ld/Options.cpp | 2 +- ld64/src/ld/Options.cpp.orig | 6248 +++++++++++++++++++ ld64/src/ld/parsers/archive_file.cpp | 2 + ld64/src/ld/parsers/libunwind/Registers.hpp | 49 + ld64/src/other/unwinddump.cpp | 1 + tapilite/CMakeLists.txt | 39 + tapilite/LICENSE.TXT | 62 + tapilite/README | 6 + tapilite/include/tapi/Defines.h | 32 + tapilite/include/tapi/LinkerInterfaceFile.h | 496 ++ tapilite/include/tapi/PackedVersion32.h | 109 + tapilite/include/tapi/Symbol.h | 136 + tapilite/include/tapi/Version.h | 63 + tapilite/include/tapi/tapi.h | 34 + tapilite/src/CMakeLists.txt | 12 + tapilite/src/LinkerInterfaceFile.cpp | 1087 ++++ tapilite/src/Version.cpp | 48 + 28 files changed, 8493 insertions(+), 60 deletions(-) create mode 100644 cctools/misc/strings.c.rej create mode 100644 ld64/src/ld/Options.cpp.orig create mode 100644 tapilite/CMakeLists.txt create mode 100644 tapilite/LICENSE.TXT create mode 100644 tapilite/README create mode 100644 tapilite/include/tapi/Defines.h create mode 100644 tapilite/include/tapi/LinkerInterfaceFile.h create mode 100644 tapilite/include/tapi/PackedVersion32.h create mode 100644 tapilite/include/tapi/Symbol.h create mode 100644 tapilite/include/tapi/Version.h create mode 100644 tapilite/include/tapi/tapi.h create mode 100644 tapilite/src/CMakeLists.txt create mode 100644 tapilite/src/LinkerInterfaceFile.cpp create mode 100644 tapilite/src/Version.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 41982c31..d2a5bdf7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,10 @@ else() message(STATUS "*Top Level* NO LTO") endif() -if(XTOOLS_TAPI_PATH AND +if(XTOOLS_USE_TAPILITE) + option(XTOOLS_TAPI_SUPPORT "Support TAPI in ld64." ON) + message(STATUS "*Top Level* WITH TAPIlite") +elseif(XTOOLS_TAPI_PATH AND EXISTS ${XTOOLS_TAPI_PATH}/include/tapi AND EXISTS ${XTOOLS_TAPI_PATH}/lib/libtapi.dylib) configure_file( @@ -128,8 +131,13 @@ include_directories(BEFORE SYSTEM "${CMAKE_SOURCE_DIR}/macho-target-includes") include_directories(BEFORE SYSTEM "${CMAKE_BINARY_DIR}/include") include_directories(BEFORE SYSTEM "${CMAKE_BINARY_DIR}/host-includes") if(XTOOLS_TAPI_SUPPORT) - include_directories(BEFORE SYSTEM "${XTOOLS_TAPI_PATH}/include") - message(STATUS "*Top Level* including ${XTOOLS_TAPI_PATH}/include") + if(XTOOLS_USE_TAPILITE) + include_directories(BEFORE SYSTEM "${CMAKE_SOURCE_DIR}/tapilite/include") + message(STATUS "*Top Level* including ${CMAKE_SOURCE_DIR}/tapilite/include") + else() + include_directories(BEFORE SYSTEM "${XTOOLS_TAPI_PATH}/include") + message(STATUS "*Top Level* including ${XTOOLS_TAPI_PATH}/include") + endif() endif() if (EXISTS ${CMAKE_BINARY_DIR}/lib) @@ -160,6 +168,15 @@ if (NOT XTOOLS_HAS_MODERNXAR) COPYONLY) endif() +# build tapilite first, so ld64 can link against it +if(XTOOLS_USE_TAPILITE) + if(EXISTS ${XTOOLS_LIBYAML_PATH}/CMakeLists.txt) + add_subdirectory(${XTOOLS_LIBYAML_PATH}) + include_directories("${CMAKE_SOURCE_DIR}/libyaml/include") + endif () + add_subdirectory(tapilite) +endif() + # Evaluate first so that we find out about libprunetrie. if( EXISTS ${CMAKE_SOURCE_DIR}/ld64/CMakeLists.txt ) add_subdirectory(ld64) diff --git a/cctools/as/CMakeLists.txt b/cctools/as/CMakeLists.txt index 16d7b76b..02734ff6 100644 --- a/cctools/as/CMakeLists.txt +++ b/cctools/as/CMakeLists.txt @@ -42,13 +42,13 @@ if(XTOOLS_C_HAS_WNODEPRECATED_FLAG) endif() add_executable(as-driver driver.c) -set_target_properties(as-driver PROPERTIES COMPILE_FLAGS "-Di486 -Di586 -Di686") +set_target_properties(as-driver PROPERTIES COMPILE_FLAGS "-Di486 -Di586 -Di686 -UPPC") target_link_libraries(as-driver stuff) set_target_properties(as-driver PROPERTIES OUTPUT_NAME as) install(TARGETS as-driver DESTINATION bin ) add_executable(i386-as ${X86_SRCS}) -set_target_properties(i386-as PROPERTIES COMPILE_FLAGS "-DI386 -Di486 -Di586 -Di686") +set_target_properties(i386-as PROPERTIES COMPILE_FLAGS "-DI386 -Di486 -Di586 -Di686 -UPPC") set_target_properties(i386-as PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/i386) set_target_properties(i386-as PROPERTIES OUTPUT_NAME as) target_link_libraries(i386-as stuff) @@ -59,13 +59,13 @@ install(TARGETS i386-as DESTINATION bin/i386) if(BUILD_TESTING) add_executable(test-x86 i386-check.c) - set_target_properties(test-x86 PROPERTIES COMPILE_FLAGS "-DI386 -Di486 -Di586 -Di686") + set_target_properties(test-x86 PROPERTIES COMPILE_FLAGS "-DI386 -Di486 -Di586 -Di686 -UPPC") set_target_properties(test-x86 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests) add_test(NAME check-x86-as COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/check-as $ i386 $) endif() add_executable(x8664-as ${X86_SRCS}) -set_target_properties(x8664-as PROPERTIES COMPILE_FLAGS "-DI386 -Di486 -Di586 -Di686 -DARCH64") +set_target_properties(x8664-as PROPERTIES COMPILE_FLAGS "-DI386 -Di486 -Di586 -Di686 -DARCH64 -UPPC") set_target_properties(x8664-as PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/x86_64) set_target_properties(x8664-as PROPERTIES OUTPUT_NAME as) target_link_libraries(x8664-as stuff) @@ -76,7 +76,7 @@ install(TARGETS x8664-as DESTINATION bin/x86_64) if(BUILD_TESTING) add_executable(test-x86_64 i386-check.c) - set_target_properties(test-x86_64 PROPERTIES COMPILE_FLAGS "-DI386 -Di486 -Di586 -Di686 -DARCH64") + set_target_properties(test-x86_64 PROPERTIES COMPILE_FLAGS "-DI386 -Di486 -Di586 -Di686 -DARCH64 -UPPC") set_target_properties(test-x86_64 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests) add_test(NAME check-x86_86-as COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/check-as $ x86_64 $) endif() @@ -109,7 +109,7 @@ endif() install(TARGETS ppc64-as DESTINATION bin/ppc64) add_executable(arm-as ${ARM_SRCS}) -set_target_properties(arm-as PROPERTIES COMPILE_FLAGS "-DARM") +set_target_properties(arm-as PROPERTIES COMPILE_FLAGS "-DARM -UPPC") set_target_properties(arm-as PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/arm) set_target_properties(arm-as PROPERTIES OUTPUT_NAME as) target_link_libraries(arm-as stuff) diff --git a/cctools/cbtlibs/CMakeLists.txt b/cctools/cbtlibs/CMakeLists.txt index 77cc8f8c..f9eac32c 100644 --- a/cctools/cbtlibs/CMakeLists.txt +++ b/cctools/cbtlibs/CMakeLists.txt @@ -11,3 +11,4 @@ add_library(syminfo libsyminfo.c) if(XTOOLS_C_HAS_FNOCOMMON_FLAG) set_target_properties(syminfo PROPERTIES COMPILE_FLAGS "-fno-common") endif() +target_link_libraries(syminfo stuff) diff --git a/cctools/ld/pass1.c b/cctools/ld/pass1.c index 408f7666..1d58fcc8 100644 --- a/cctools/ld/pass1.c +++ b/cctools/ld/pass1.c @@ -1752,47 +1752,6 @@ enum bool force_weak) } } -/* - * get_toc_byte_sex() guesses the byte sex of the table of contents of the - * library mapped in at the address, addr, of size, size based on the first - * object file's bytesex. If it can't figure it out, because the library has - * no object file members or is malformed it will return UNKNOWN_BYTE_SEX. - */ -__private_extern__ -enum byte_sex -get_toc_byte_sex( -char *addr, -uint32_t size) -{ - uint32_t magic; - uint32_t ar_name_size; - struct ar_hdr *ar_hdr; - char *p; - - ar_hdr = (struct ar_hdr *)(addr + SARMAG); - - p = addr + SARMAG + sizeof(struct ar_hdr) + - rnd(strtoul(ar_hdr->ar_size, NULL, 10), sizeof(short)); - while(p + sizeof(struct ar_hdr) + sizeof(uint32_t) < addr + size){ - ar_hdr = (struct ar_hdr *)p; - if(strncmp(ar_hdr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0) - ar_name_size = strtoul(ar_hdr->ar_name + sizeof(AR_EFMT1) - 1, - NULL, 10); - else - ar_name_size = 0; - p += sizeof(struct ar_hdr); - memcpy(&magic, p + ar_name_size, sizeof(uint32_t)); - if(magic == MH_MAGIC || magic == MH_MAGIC_64) - return(get_host_byte_sex()); - else if(magic == SWAP_INT(MH_MAGIC) || - magic == SWAP_INT(MH_MAGIC_64)) - return(get_host_byte_sex() == BIG_ENDIAN_BYTE_SEX ? - LITTLE_ENDIAN_BYTE_SEX : BIG_ENDIAN_BYTE_SEX); - p += rnd(strtoul(ar_hdr->ar_size, NULL, 10), sizeof(short)); - } - return(UNKNOWN_BYTE_SEX); -} - /* * check_archive_arch() check the archive specified to see if it's architecture * does not match that of whats being loaded and if so returns FALSE. Else it diff --git a/cctools/misc/strings.c.rej b/cctools/misc/strings.c.rej new file mode 100644 index 00000000..94f0d76d --- /dev/null +++ b/cctools/misc/strings.c.rej @@ -0,0 +1,13 @@ +@@ -354,7 +354,11 @@ + * If the ofile is not an object file then process it without reguard + * to sections. + */ +- if(ofile->object_addr == NULL || ofile->member_type == OFILE_LLVM_BITCODE){ ++ if(ofile->object_addr == NULL ++#ifdef LTO_SUPPORT ++ || ofile->member_type == OFILE_LLVM_BITCODE ++#endif /* LTO_SUPPORT */ ++ ){ + if(ofile->file_type == OFILE_FAT && ofile->arch_flag.cputype != 0){ + if(ofile->fat_header->magic == FAT_MAGIC_64){ + addr = ofile->file_addr + diff --git a/cctools/otool/arm_disasm.c b/cctools/otool/arm_disasm.c index 8a2ea5d9..66f26ce8 100644 --- a/cctools/otool/arm_disasm.c +++ b/cctools/otool/arm_disasm.c @@ -64,7 +64,7 @@ typedef char bfd_byte; /* HACKS to avoid pulling in all of FSF binutils include/dis-asm.h */ typedef int (*fprintf_ftype) (void *, const char*, ...); -struct disassemble_info { /* HACK'ed up for just what we need here */ +static struct disassemble_info { /* HACK'ed up for just what we need here */ fprintf_ftype fprintf_func; void *stream; diff --git a/cctools/otool/i386_disasm.c b/cctools/otool/i386_disasm.c index 0b60eb81..4bafbb35 100644 --- a/cctools/otool/i386_disasm.c +++ b/cctools/otool/i386_disasm.c @@ -1617,7 +1617,7 @@ static unsigned int xmm_rm(int r_m, int rex) /* * This is passed to the llvm disassembler. */ -struct disassemble_info { +static struct disassemble_info { enum bool verbose; /* Relocation information. */ struct relocation_info *sorted_relocs; diff --git a/cctools/otool/print_objc.c b/cctools/otool/print_objc.c index e8253f40..3793c226 100644 --- a/cctools/otool/print_objc.c +++ b/cctools/otool/print_objc.c @@ -75,6 +75,14 @@ struct objc_class_t { uint32_t protocols; /* struct objc_protocol_list * (32-bit pointer) */ }; +#ifndef CLS_GETINFO +# define CLS_GETINFO(cls, infomask) ((cls)->info & (infomask)) +// class is not a metaclass +#define CLS_CLASS 0x1 +// class is a metaclass +#define CLS_META 0x2 +#endif + struct objc_category_t { uint32_t category_name; /* char * (32-bit pointer) */ uint32_t class_name; /* char * (32-bit pointer) */ diff --git a/ld64/src/CMakeLists.txt b/ld64/src/CMakeLists.txt index 1be1a5e7..0f6a0f46 100644 --- a/ld64/src/CMakeLists.txt +++ b/ld64/src/CMakeLists.txt @@ -76,4 +76,4 @@ include_directories("${CMAKE_CURRENT_SOURCE_DIR}/abstraction") add_subdirectory(ld) add_subdirectory(other) -add_library(prunetrie other/PruneTrie.cpp) +add_library(prunetrie STATIC other/PruneTrie.cpp) diff --git a/ld64/src/ld/CMakeLists.txt b/ld64/src/ld/CMakeLists.txt index c314ff45..7a3fdc59 100644 --- a/ld64/src/ld/CMakeLists.txt +++ b/ld64/src/ld/CMakeLists.txt @@ -51,8 +51,13 @@ configure_file ( ) if(XTOOLS_TAPI_SUPPORT) - include_directories("${XTOOLS_TAPI_PATH}/include") - message(STATUS "*LD64* including ${XTOOLS_TAPI_PATH}/include") + if(XTOOLS_USE_TAPILITE) + include_directories("${CMAKE_SOURCE_DIR}/tapilite/include") + message(STATUS "*LD64* including ${CMAKE_SOURCE_DIR}/tapilite/include") + else() + include_directories("${XTOOLS_TAPI_PATH}/include") + message(STATUS "*LD64* including ${XTOOLS_TAPI_PATH}/include") + endif() endif() include_directories("${CMAKE_CURRENT_SOURCE_DIR}") @@ -62,10 +67,6 @@ if(XTOOLS_LTO_SUPPORT) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lLTO") endif() -if(XTOOLS_TAPI_SUPPORT) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -ltapi") -endif() - add_definitions(-DLD_VERS="xtools-ld64-${LD64_VERSION_NUM}") add_executable(ld ${LD64_SOURCES}) @@ -73,4 +74,12 @@ if(NOT XTOOLS_HAS_MODERNXAR) target_link_libraries(ld xarextralib) endif() +if(XTOOLS_TAPI_SUPPORT) + if(XTOOLS_USE_TAPILITE) + target_link_libraries(ld tapilite yaml) + else() + target_link_libraries(ld tapi) + endif() +endif() + install(TARGETS ld DESTINATION bin) diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 617d9d49..957a676f 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -907,6 +907,8 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, Options::kPlatformOSX); #elif __arm__ opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6, Options::kPlatformOSX); +#elif __arm64__ + opts.setArchitecture(CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, Options::kPlatformOSX); #else #error unknown default architecture #endif diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index a1f330b8..da8b0802 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -1771,7 +1771,7 @@ uint32_t Options::parseVersionNumber32(const char* versionString) z = strtoul(&end[1], &end, 10); } } - if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) + if ( (x > 0xffff) || (y > 0xff) || (z > 0xff) ) throwf("malformed 32-bit x.y.z version number: %s", versionString); return (x << 16) | ( y << 8 ) | z; diff --git a/ld64/src/ld/Options.cpp.orig b/ld64/src/ld/Options.cpp.orig new file mode 100644 index 00000000..a1f330b8 --- /dev/null +++ b/ld64/src/ld/Options.cpp.orig @@ -0,0 +1,6248 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2011 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@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifndef PATH_MAX +#include +#endif + +#include // std::sort + +#include "Options.h" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "Snapshot.h" + +// from FunctionNameDemangle.h +extern "C" size_t fnd_get_demangled_name(const char *mangledName, char *outputBuffer, size_t length); + +#ifdef LTO_SUPPORT +// upward dependency on lto::version() +namespace lto { + extern const char* version(); + extern unsigned static_api_version(); + extern unsigned runtime_api_version(); +} +#endif + +// magic to place command line in crash reports +const int crashreporterBufferSize = 2000; +static char crashreporterBuffer[crashreporterBufferSize]; +#ifndef __has_include +# define __has_include(x) 0 +#endif +#if __has_include("CrashReporterClient.h") \ + && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + #define USE_CRASHREPORTER_CLIENT 1 + #include + // hack until ld does not need to build on 10.6 anymore + struct crashreporter_annotations_t gCRAnnotations + __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) + = { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0, 0, 0 }; +#else + extern "C" char* __crashreporter_info__; + __attribute__((used)) + char* __crashreporter_info__ = crashreporterBuffer; +#endif + + +static bool sEmitWarnings = true; +static bool sFatalWarnings = false; +static const char* sWarningsSideFilePath = NULL; +static FILE* sWarningsSideFile = NULL; +static int sWarningsCount = 0; + +void warning(const char* format, ...) +{ + ++sWarningsCount; + if ( sEmitWarnings ) { + va_list list; + if ( sWarningsSideFilePath != NULL ) { + if ( sWarningsSideFile == NULL ) + sWarningsSideFile = fopen(sWarningsSideFilePath, "a"); + } + va_start(list, format); + fprintf(stderr, "ld: warning: "); + vfprintf(stderr, format, list); + fprintf(stderr, "\n"); + if ( sWarningsSideFile != NULL ) { + fprintf(sWarningsSideFile, "ld: warning: "); + vfprintf(sWarningsSideFile, format, list); + fprintf(sWarningsSideFile, "\n"); + fflush(sWarningsSideFile); + } + va_end(list); + } +} + +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +bool Options::FileInfo::checkFileExists(const Options& options, const char *p) +{ + if (isInlined) { + modTime = 0; + return true; + } + struct stat statBuffer; + if (p == NULL) + p = path; + if ( stat(p, &statBuffer) == 0 ) { + if (p != path) path = strdup(p); + modTime = statBuffer.st_mtime; + return true; + } + options.addDependency(Options::depNotFound, p); +// fprintf(stderr, "not found: %s\n", p); + return false; +} + + +Options::Options(int argc, const char* argv[]) + : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable), + fHasPreferredSubType(false), fArchSupportsThumb2(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), + fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false), + fInterposeMode(kInterposeNone), fDeadStrip(false), fNameSpace(kTwoLevelNameSpace), + fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName(NULL), + fBaseAddress(0), fMaxAddress(0x7FFFFFFFFFFFFFFFLL), + fBaseWritableAddress(0), fSplitSegs(false), + fExportMode(kExportDefault), fLibrarySearchMode(kSearchDylibAndArchiveInEachDir), + fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(true), + fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak), + fClientName(NULL), + fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), + fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), + fDyldInstallPath("/usr/lib/dyld"), fLtoCachePath(NULL), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL), + fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false), + fNonExecutableHeap(false), fDisableNonExecutableHeap(false), + fMinimumHeaderPad(32), fSegmentAlignment(4096), + fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), + fVerbose(false), fKeepRelocations(false), fWarnStabs(false), + fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), + fSharedRegionEligible(false), fSharedRegionEligibleForceOff(false), fPrintOrderFileStatistics(false), + fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fPIEOnCommandLine(false), + fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), + fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fKextsUseStubs(false), + fUsingLazyDylibLinking(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false), + fOrderData(true), fMarkDeadStrippableDylib(false), + fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false), + fAllowCpuSubtypeMismatches(false), fEnforceDylibSubtypesMatch(false), + fWarnOnSwiftABIVersionMismatches(false), fUseSimplifiedDylibReExports(false), + fObjCABIVersion2Override(false), fObjCABIVersion1Override(false), fCanUseUpwardDylib(false), + fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), + fLinkingMainExecutable(false), fForFinalLinkedImage(false), fForStatic(false), + fForDyld(false), fMakeTentativeDefinitionsReal(false), fWhyLoad(false), fRootSafe(false), + fSetuidSafe(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true), + fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), + fAutoOrderInitializers(true), fOptimizeZeroFill(true), fMergeZeroFill(false), fLogObjectFiles(false), + fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceEmitJSON(false), + fOutputSlidable(false), fWarnWeakExports(false), fNoWeakExports(false), + fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false), + fDemangle(false), fTLVSupport(false), + fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false), + fVersionLoadCommandForcedOff(false), fBuildVersionLoadCommand(false), fFunctionStartsLoadCommand(false), + fFunctionStartsForcedOn(false), fFunctionStartsForcedOff(false), + fDataInCodeInfoLoadCommand(false), fDataInCodeInfoLoadCommandForcedOn(false), fDataInCodeInfoLoadCommandForcedOff(false), + fCanReExportSymbols(false), fObjcCategoryMerging(true), fPageAlignDataAtoms(false), + fNeedsThreadLoadCommand(false), fEntryPointLoadCommand(false), + fEntryPointLoadCommandForceOn(false), fEntryPointLoadCommandForceOff(false), + fSourceVersionLoadCommand(false), + fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false), + fTargetIOSSimulator(false), fExportDynamic(false), fAbsoluteSymbols(false), + fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true), + fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false), + fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false), + fGenerateDtraceDOF(true), fAllowBranchIslands(true), fTraceSymbolLayout(false), + fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), + fSharedRegionEncodingV2(false), fUseDataConstSegment(false), + fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), fUseTextExecSegment(false), + fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false), + fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false), + fReverseMapPath(NULL), fLTOCodegenOnly(false), + fIgnoreAutoLink(false), fAllowDeadDups(false), fAllowWeakImports(true), fInitializersTreatment(Options::kInvalid), + fZeroModTimeInDebugMap(false), fBitcodeKind(kBitcodeProcess), + fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), + fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fWatchOSVersionMin(ld::wOSVersionUnset), + fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), + fDependencyInfoPath(NULL), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0), + fUnalignedPointerTreatment(kUnalignedPointerIgnore) +{ + this->checkForClassic(argc, argv); + this->parsePreCommandLineEnvironmentSettings(); + this->parse(argc, argv); + this->parsePostCommandLineEnvironmentSettings(); + this->reconfigureDefaults(); + this->checkIllegalOptionCombinations(); + + this->addDependency(depOutputFile, fOutputFile); + if ( fMapPath != NULL ) + this->addDependency(depOutputFile, fMapPath); +} + +Options::~Options() +{ + if ( fTraceFileDescriptor != -1 ) + ::close(fTraceFileDescriptor); +} + +bool Options::errorBecauseOfWarnings() const +{ + return (sFatalWarnings && (sWarningsCount > 0)); +} + + +const char* Options::installPath() const +{ + if ( fDylibInstallName != NULL ) + return fDylibInstallName; + else if ( fFinalName != NULL ) + return fFinalName; + else + return fOutputFile; +} + + +bool Options::interposable(const char* name) const +{ + switch ( fInterposeMode ) { + case kInterposeNone: + return false; + case kInterposeAllExternal: + return true; + case kInterposeSome: + return fInterposeList.contains(name); + } + throw "internal error"; +} + + +bool Options::printWhyLive(const char* symbolName) const +{ + return fWhyLive.contains(symbolName); +} + + +const char* Options::dotOutputFile() +{ + return fDotOutputFile; +} + + +bool Options::hasWildCardExportRestrictList() const +{ + // has -exported_symbols_list which contains some wildcards + return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards()); +} + +bool Options::hasWeakBitTweaks() const +{ + // has -exported_symbols_list which contains some wildcards + return (!fForceWeakSymbols.empty() || !fForceNotWeakSymbols.empty()); +} + +bool Options::allGlobalsAreDeadStripRoots() const +{ + // -exported_symbols_list means globals are not exported by default + if ( fExportMode == kExportSome ) + return false; + // + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + // Add the -export_dynamic flag + return fExportDynamic; + case Options::kStaticExecutable: + // Support the -export_dynamic flag for xnu + return fExportDynamic; + case Options::kPreload: + // by default unused globals in a main executable are stripped + return false; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + case Options::kKextBundle: + return true; + } + return false; +} + + +bool Options::keepRelocations() +{ + return fKeepRelocations; +} + +bool Options::warnStabs() +{ + return fWarnStabs; +} + +const char* Options::executablePath() +{ + return fExecutablePath; +} + +uint32_t Options::initialSegProtection(const char* segName) const +{ + for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) { + return it->init; + } + } + if ( strcmp(segName, "__TEXT") == 0 ) { + return ( fUseTextExecSegment ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_EXECUTE) ); + } + else if ( strcmp(segName, "__TEXT_EXEC") == 0 ) { + return VM_PROT_READ | VM_PROT_EXECUTE; + } + else if ( strcmp(segName, "__PAGEZERO") == 0 ) { + return 0; + } + else if ( strcmp(segName, "__LINKEDIT") == 0 ) { + return VM_PROT_READ; + } + + // all others default to read-write + return VM_PROT_READ | VM_PROT_WRITE; +} + +uint32_t Options::maxSegProtection(const char* segName) const +{ + // iPhoneOS always uses same protection for max and initial + // simulator apps need to use MacOSX max-prot + if ( (fPlatform != kPlatformOSX) && !fTargetIOSSimulator ) + return initialSegProtection(segName); + + for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) { + return it->max; + } + } + if ( strcmp(segName, "__PAGEZERO") == 0 ) { + return 0; + } + // all others default to all + return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; +} + +uint64_t Options::segPageSize(const char* segName) const +{ + for(std::vector::const_iterator it=fCustomSegmentSizes.begin(); it != fCustomSegmentSizes.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) + return it->size; + } + return fSegmentAlignment; +} + +uint64_t Options::customSegmentAddress(const char* segName) const +{ + for(std::vector::const_iterator it=fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) + return it->address; + } + // if custom stack in use, model as segment with custom address + if ( (fStackSize != 0) && (strcmp("__UNIXSTACK", segName) == 0) ) + return fStackAddr - fStackSize; + return 0; +} + +bool Options::hasCustomSegmentAddress(const char* segName) const +{ + for(std::vector::const_iterator it=fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) + return true; + } + // if custom stack in use, model as segment with custom address + if ( (fStackSize != 0) && (strcmp("__UNIXSTACK", segName) == 0) ) + return true; + return false; +} + +bool Options::hasCustomSectionAlignment(const char* segName, const char* sectName) const +{ + for (std::vector::const_iterator it = fSectionAlignments.begin(); it != fSectionAlignments.end(); ++it) { + if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) + return true; + } + if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) ) + return true; + + return false; +} + +uint8_t Options::customSectionAlignment(const char* segName, const char* sectName) const +{ + for (std::vector::const_iterator it = fSectionAlignments.begin(); it != fSectionAlignments.end(); ++it) { + if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) + return it->alignment; + } + if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) ) + return __builtin_ctz(fSegmentAlignment); + + return 0; +} + +bool Options::segmentOrderAfterFixedAddressSegment(const char* segName) const +{ + bool nowPinned = false; + for (std::vector::const_iterator it=fSegmentOrder.begin(); it != fSegmentOrder.end(); ++it) { + if ( strcmp(*it, segName) == 0 ) + return nowPinned; + if ( hasCustomSegmentAddress(*it) ) + nowPinned = true; + } + return false; +} + +bool Options::hasExportedSymbolOrder() +{ + return (fExportSymbolsOrder.size() > 0); +} + +bool Options::exportedSymbolOrder(const char* sym, unsigned int* order) const +{ + NameToOrder::const_iterator pos = fExportSymbolsOrder.find(sym); + if ( pos != fExportSymbolsOrder.end() ) { + *order = pos->second; + return true; + } + else { + *order = 0xFFFFFFFF; + return false; + } +} + +void Options::loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping) +{ + // read in whole file + int fd = ::open(fileOfExports, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open -exported_symbols_order file: %s", fileOfExports); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) + throwf("can't process -exported_symbols_order file: %s", fileOfExports); + + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read -exported_symbols_order file: %s", fileOfExports); + + ::close(fd); + + // parse into symbols and add to unordered_set + unsigned int count = 0; + char * const end = &p[stat_buf.st_size]; + enum { lineStart, inSymbol, inComment } state = lineStart; + char* symbolStart = NULL; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( (*s == '\n') || (*s == '\r') ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + orderMapping[symbolStart] = ++count; + symbolStart = NULL; + state = lineStart; + } + break; + case inComment: + if ( (*s == '\n') || (*s == '\r') ) + state = lineStart; + break; + } + } + if ( state == inSymbol ) { + warning("missing line-end at end of file \"%s\"", fileOfExports); + int len = end-symbolStart+1; + char* temp = new char[len]; + strlcpy(temp, symbolStart, len); + + // remove any trailing spaces + char* last = &temp[len-2]; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + orderMapping[temp] = ++count; + } + + // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table +} + +bool Options::forceWeak(const char* symbolName) const +{ + return fForceWeakSymbols.contains(symbolName); +} + +bool Options::forceNotWeak(const char* symbolName) const +{ + return fForceNotWeakSymbols.contains(symbolName); +} + +bool Options::forceWeakNonWildCard(const char* symbolName) const +{ + return fForceWeakSymbols.containsNonWildcard(symbolName); +} + +bool Options::forceNotWeakNonWildcard(const char* symbolName) const +{ + return fForceNotWeakSymbols.containsNonWildcard(symbolName); +} + +bool Options::forceCoalesce(const char* symbolName) const +{ + return fForceCoalesceSymbols.contains(symbolName); +} + + +bool Options::shouldExport(const char* symbolName) const +{ + switch (fExportMode) { + case kExportSome: + return fExportSymbols.contains(symbolName); + case kDontExportSome: + return ! fDontExportSymbols.contains(symbolName); + case kExportDefault: + return true; + } + throw "internal error"; +} + +bool Options::shouldReExport(const char* symbolName) const +{ + return fReExportSymbols.contains(symbolName); +} + +bool Options::keepLocalSymbol(const char* symbolName) const +{ + switch (fLocalSymbolHandling) { + case kLocalSymbolsAll: + return true; + case kLocalSymbolsNone: + return false; + case kLocalSymbolsSelectiveInclude: + return fLocalSymbolsIncluded.contains(symbolName); + case kLocalSymbolsSelectiveExclude: + return ! fLocalSymbolsExcluded.contains(symbolName); + } + throw "internal error"; +} + +const std::vector* Options::sectionOrder(const char* segName) const +{ + for (std::vector::const_iterator it=fSectionOrder.begin(); it != fSectionOrder.end(); ++it) { + if ( strcmp(it->segmentName, segName) == 0 ) + return &it->sectionOrder; + } + return NULL; +} + +uint32_t Options::minOSversion() const +{ + switch (fPlatform) { + case kPlatformiOS: + return iOSVersionMin(); + case kPlatformOSX: + return macosxVersionMin(); + case kPlatformWatchOS: + return watchOSVersionMin(); +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + return iOSVersionMin(); +#endif + case kPlatform_bridgeOS: + return iOSVersionMin(); + case kPlatformUnknown: + return 0; + } + return 0; +} + +void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::Platform platform) +{ + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( (type == t->cpuType) && (subtype == t->cpuSubType) ) { + fArchitecture = type; + fSubArchitecture = subtype; + fArchitectureName = t->archName; + fHasPreferredSubType = t->isSubType; + fArchSupportsThumb2 = t->supportsThumb2; + if ( fPlatform == kPlatformUnknown) + fPlatform = platform; + switch ( type ) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) && (fMacVersionMin == ld::macVersionUnset) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specified, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + break; + case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: + if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + #if defined(DEFAULT_IPHONEOS_MIN_VERSION) + warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); + setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); + #else + warning("-ios_version_min not specified, assuming 6.0"); + setIOSVersionMin("6.0"); + #endif + } + break; + } + fLinkSnapshot.recordArch(fArchitectureName); + // only use compressed LINKEDIT for: + // Mac OS X 10.6 or later + // iOS 3.1 or later + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + // Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB + if ( minOS(ld::mac10_5, ld::iOS_2_0) ) + fUseSimplifiedDylibReExports = true; + return; + } + assert(fArchitectureName != NULL); + if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { +#if defined(DEFAULT_IPHONEOS_MIN_VERSION) + warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); + setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); +#elif defined(DEFAULT_MACOSX_MIN_VERSION) + warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); +#else + warning("-macosx_version_min not specified, assuming 10.5"); + fMacVersionMin = ld::mac10_5; +#endif + } + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + } + fArchitectureName = "unknown architecture"; +} + +bool Options::armUsesZeroCostExceptions() const +{ + return ( (fArchitecture == CPU_TYPE_ARM) && (fSubArchitecture == CPU_SUBTYPE_ARM_V7K) ); +} + +void Options::parseArch(const char* arch) +{ + if ( arch == NULL ) + throw "-arch must be followed by an architecture string"; + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( strcmp(t->archName,arch) == 0 ) { + fArchitectureName = arch; + fArchitecture = t->cpuType; + fSubArchitecture = t->cpuSubType; + fHasPreferredSubType = t->isSubType; + fArchSupportsThumb2 = t->supportsThumb2; + return; + } + } + throwf("unknown/unsupported architecture name for: -arch %s", arch); +} + +bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const +{ + char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; + sprintf(possiblePath, format, dir, rootName); + bool found = result.checkFileExists(*this, possiblePath); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); + return found; +} + + +Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) const +{ + FileInfo result; + const int rootNameLen = strlen(rootName); + // if rootName ends in .o there is no .a vs .dylib choice + if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/%s", dir, rootName, result) ) + return result; + } + } + else { + bool lookForDylibs = false; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: // + lookForDylibs = true; + break; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: + lookForDylibs = false; + break; + } + switch ( fLibrarySearchMode ) { + case kSearchAllDirsForDylibsThenAllDirsForArchives: + // first look in all directories for just for dylibs + if ( lookForDylibs ) { + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + auto path = std::string(dir) + "/lib" + rootName + ".dylib"; + if ( findFile(path, {".tbd"}, result) ) + return result; + } + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/lib%s.so", dir, rootName, result) ) + return result; + } + } + // next look in all directories for just for archives + if ( !dylibsOnly ) { + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/lib%s.a", dir, rootName, result) ) + return result; + } + } + break; + + case kSearchDylibAndArchiveInEachDir: + // look in each directory for just for a dylib then for an archive + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + auto path = std::string(dir) + "/lib" + rootName + ".dylib"; + if ( lookForDylibs && findFile(path, {".tbd"}, result) ) + return result; + if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) ) + return result; + if ( !dylibsOnly && checkForFile("%s/lib%s.a", dir, rootName, result) ) + return result; + } + break; + } + } + throwf("library not found for -l%s", rootName); +} + +Options::FileInfo Options::findFramework(const char* frameworkName) const +{ + if ( frameworkName == NULL ) + throw "-framework missing next argument"; + char temp[strlen(frameworkName)+1]; + strcpy(temp, frameworkName); + const char* name = temp; + const char* suffix = NULL; + char* comma = strchr(temp, ','); + if ( comma != NULL ) { + *comma = '\0'; + suffix = &comma[1]; + } + return findFramework(name, suffix); +} + +Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) const +{ + for (const auto* path : fFrameworkSearchPaths) { + auto possiblePath = std::string(path).append("/").append(rootName).append(".framework/").append(rootName); + if ( suffix != nullptr ) { + char realPath[PATH_MAX]; + // no symlink in framework to suffix variants, so follow main symlink + if ( realpath(possiblePath.c_str(), realPath) != nullptr ) + possiblePath = std::string(realPath).append(suffix); + } + FileInfo result; + if ( findFile(possiblePath, {".tbd"}, result) ) + return result; + } + // try without suffix + if ( suffix != NULL ) + return findFramework(rootName, NULL); + else + throwf("framework not found %s", rootName); +} + +static std::string replace_extension(const std::string &path, const std::string &ext) +{ + auto result = path; + auto lastSlashIdx = result.find_last_of('/'); + auto lastDotIdx = result.find_last_of('.'); + if (lastDotIdx != std::string::npos && lastDotIdx > lastSlashIdx) + result.erase(lastDotIdx, std::string::npos); + if ( ext.size() > 0 && ext[0] == '.' ) + result.append(ext); + else + result.append('.' + ext); + return result; +} + +void Options::addTAPIInterface(tapi::LinkerInterfaceFile *interface, const char *path) const { +#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1)) + if (tapi::APIVersion::isAtLeast(1, 3)) { + for (auto &name : interface->inlinedFrameworkNames()) { + fTAPIFiles.emplace_back(interface, path, name.c_str()); + } + } +#endif +} + +bool Options::findFile(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const +{ + FileInfo tbdInfo; + for ( const auto &ext : tbdExtensions ) { + auto newPath = replace_extension(path, ext); + bool found = tbdInfo.checkFileExists(*this, newPath.c_str()); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), newPath.c_str()); + if ( found ) + break; + } + + FileInfo dylibInfo; + { + bool found = dylibInfo.checkFileExists(*this, path.c_str()); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), path.c_str()); + } + + // There is only a text-based stub file or a dynamic library file. + if ( tbdInfo.missing() != dylibInfo.missing() ) { + result = tbdInfo.missing() ? dylibInfo : tbdInfo; + } + // There are both - a text-based stub file and a dynamic library file. + else if ( !tbdInfo.missing() && !dylibInfo.missing() ) { + // Check if we should prefer the text-based stub file (installapi). + if (tapi::LinkerInterfaceFile::shouldPreferTextBasedStubFile(tbdInfo.path)) { + result = tbdInfo; + } + // If the files are still in sync we can use and should use the text-based stub file. + else if (tapi::LinkerInterfaceFile::areEquivalent(tbdInfo.path, dylibInfo.path)) { + result = tbdInfo; + } + // Otherwise issue a warning and fall-back to the dynamic library file. + else { + warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path); + result = dylibInfo; + } + } else { + return false; + } + + return true; +} + +static bool startsWith(const std::string& str, const std::string& prefix) +{ + return (str.compare(0, prefix.length(), prefix) == 0); +} + +static std::string getDirPath(const std::string& path) +{ + std::string::size_type lastSlashPos = path.find_last_of('/'); + if ( lastSlashPos == std::string::npos ) + return "./"; + else + return path.substr(0, lastSlashPos+1); +} + +Options::FileInfo Options::findFile(const std::string &path, const ld::dylib::File* fromDylib) const +{ + FileInfo result; + + // if absolute path and not a .o file, then use SDK prefix + if ( (path[0] == '/') && (strcmp(&path[path.size()-2], ".o") != 0) ) { + for (const auto* sdkPathDir : fSDKPaths) { + auto possiblePath = std::string(sdkPathDir) + path; + if ( findFile(possiblePath, {".tbd"}, result) ) + return result; + } + } + + // expand @ variables + if ( path[0] == '@' ) { + if ( startsWith(path, "@executable_path/") && (fExecutablePath != nullptr) ) { + std::string exeBasedPath = getDirPath(fExecutablePath) + &path[17]; + if ( findFile(exeBasedPath, {".tbd"}, result) ) + return result; + } + else if ( startsWith(path, "@loader_path/") && (fromDylib != nullptr) ) { + char absPath[PATH_MAX]; + if ( realpath(fromDylib->path(), absPath) != NULL ) { + std::string loaderBasedPath = getDirPath(fromDylib->path()) + &path[13]; + if ( findFile(loaderBasedPath, {".tbd"}, result) ) + return result; + } + } + else if ( startsWith(path, "@rpath/") ) { + // first search any LC_RPATH supplied by dyld that re-exports dylib to be found + if ( fromDylib != nullptr ) { + for (const char* rp : fromDylib->rpaths() ) { + std::string rpath = rp; + // handle dylib that has LC_RPATH = @loader_path/blah + if ( startsWith(rpath, "@loader_path/") ) { + char absPath[PATH_MAX]; + if ( realpath(fromDylib->path(), absPath) != NULL ) + rpath = getDirPath(absPath) + &rpath[13]; + else + rpath = getDirPath(fromDylib->path()) + &rpath[13]; + } + std::string rpathBasedPath = rpath + "/" + &path[6]; + if ( findFile(rpathBasedPath, {".tbd"}, result) ) + return result; + } + } + } + } + + // find inlined TBD file before raw path. + // rdar://problem/35864452 + if (hasInlinedTAPIFile(path)) { + FileInfo inlinedFile(path.c_str()); + inlinedFile.isInlined = true; + return inlinedFile; + } + + // try raw path + if ( findFile(path, {".tbd"}, result) ) + return result; + + // not found + throwf("file not found: %s", path.c_str()); +} + +bool Options::hasInlinedTAPIFile(const std::string &path) const { + for (const auto &dylib : fTAPIFiles) { + if (dylib.getInstallName() == path) + return true; + } + return false; +} + +std::unique_ptr Options::findTAPIFile(const std::string &path) const +{ + std::unique_ptr interface; + std::string TBDPath; + + // create parsing options. + tapi::ParsingFlags flags = tapi::ParsingFlags::None; + if (enforceDylibSubtypesMatch()) + flags |= tapi::ParsingFlags::ExactCpuSubType; + + if (!allowWeakImports()) + flags |= tapi::ParsingFlags::DisallowWeakImports; + + // Search through all the inlined framework. + for (const auto &dylib : fTAPIFiles) { + if (dylib.getInstallName() == path) { + // If the install name matches, parse the framework. + std::string errorMessage; + auto file = dylib.getInterfaceFile()->getInlinedFramework(path.c_str(), architecture(), subArchitecture(), + flags, tapi::PackedVersion32(minOSversion()), errorMessage); + if (!file) + throw strdup(errorMessage.c_str()); + + if (!interface) { + // If this is the first inlined framework found, record the information. + interface.reset(file); + TBDPath = dylib.getTAPIFilePath(); + } else { + // If we found other inlined framework already, check to see if their versions are the same. + // If not the same, emit an warning and record the newer one. Otherwise, just use the current one. + if (interface->getCurrentVersion() == file->getCurrentVersion()) + continue; + warning("Inlined framework/dylib mismatch: %s (%s and %s)", path.c_str(), + TBDPath.c_str(), dylib.getTAPIFilePath().c_str()); + if (interface->getCurrentVersion() < file->getCurrentVersion()) { + interface.reset(file); + TBDPath = dylib.getTAPIFilePath(); + } + } + } + } + return interface; +} + +// search for indirect dylib first using -F and -L paths first +Options::FileInfo Options::findIndirectDylib(const std::string& installName, const ld::dylib::File* fromDylib) const +{ + FileInfo result; + + auto lastSlashPos = installName.find_last_of('/'); + auto pos = ( lastSlashPos != std::string::npos ) ? lastSlashPos + 1 : 0; + auto leafName = installName.substr(pos); + + // Is this in a framework? + // /path/Foo.framework/Foo ==> true (Foo) + // /path/Foo.framework/Frameworks/Bar.framework/Bar ==> true (Bar) + // /path/Foo.framework/Resources/Bar ==> false + bool isFramework = false; + if ( lastSlashPos != std::string::npos ) { + auto frameworkDir = std::string("/").append(leafName).append(".framework/"); + if ( installName.rfind(frameworkDir) != std::string::npos ) + isFramework = true; + } + + // These are abbreviated versions of the routines findFramework and findLibrary above + // because we already know the final name of the file that we're looking for and so + // don't need to try variations, just paths. We do need to add the additional bits + // onto the framework path though. + if ( isFramework ) { + auto endPos = installName.rfind(".framework"); + auto beginPos = installName.find_last_of('/', endPos); + auto leafPath = installName.substr(beginPos); + for (const auto* dir : fFrameworkSearchPaths) { + auto possiblePath = dir + leafPath; + if ( findFile(possiblePath, {".tbd"}, result) ) + return result; + } + } else { + // if this is a .dylib inside a framework, do not search -L paths + // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard + bool embeddedDylib = ( (leafName.size() > 6) + && (leafName.find(".dylib", leafName.size()-6) != std::string::npos) + && (installName.find(".framework/") != std::string::npos) ); + if ( !embeddedDylib ) { + for (const auto* dir : fLibrarySearchPaths) { + //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName); + std::string possiblePath = dir + std::string("/") + leafName; + if ( findFile(possiblePath, {".tbd"}, result) ) + return result; + } + } + } + + // If we didn't find it fall back to findFile. + return findFile(installName, fromDylib); +} + + + +void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth) +{ + FILE* file = fopen(segAddrPath, "r"); + if ( file == NULL ) { + warning("-seg_addr_table file cannot be read: %s", segAddrPath); + return; + } + + char path[PATH_MAX]; + uint64_t firstColumAddress = 0; + uint64_t secondColumAddress = 0; + bool hasSecondColumn = false; + while ( fgets(path, PATH_MAX, file) != NULL ) { + path[PATH_MAX-1] = '\0'; + char* eol = strchr(path, '\n'); + if ( eol != NULL ) + *eol = '\0'; + // ignore lines not starting with 0x number + if ( (path[0] == '0') && (path[1] == 'x') ) { + char* p; + firstColumAddress = strtoull(path, &p, 16); + while ( isspace(*p) ) + ++p; + // see if second column is a number + if ( (p[0] == '0') && (p[1] == 'x') ) { + secondColumAddress = strtoull(p, &p, 16); + hasSecondColumn = true; + while ( isspace(*p) ) + ++p; + } + while ( isspace(*p) ) + ++p; + if ( p[0] == '/' ) { + // remove any trailing whitespace + for(char* end = eol-1; (end > p) && isspace(*end); --end) + *end = '\0'; + // see if this line is for the dylib being linked + if ( strcmp(p, installPth) == 0 ) { + fBaseAddress = firstColumAddress; + if ( hasSecondColumn ) { + fBaseWritableAddress = secondColumAddress; + fSplitSegs = true; + } + break; // out of while loop + } + } + } + } + + fclose(file); +} + +void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdinal) +{ + FILE* file; + const char* comma = strrchr(fileOfPaths, ','); + const char* prefix = NULL; + if ( comma != NULL ) { + // -filelist fails with comma in path + file = fopen(fileOfPaths, "r"); + if ( file == NULL ) { + prefix = comma+1; + int realFileOfPathsLen = comma-fileOfPaths; + char realFileOfPaths[realFileOfPathsLen+1]; + strncpy(realFileOfPaths,fileOfPaths, realFileOfPathsLen); + realFileOfPaths[realFileOfPathsLen] = '\0'; + file = fopen(realFileOfPaths, "r"); + if ( file == NULL ) + throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", realFileOfPaths, errno, strerror(errno)); + this->addDependency(Options::depFileList, realFileOfPaths); + } + } + else { + file = fopen(fileOfPaths, "r"); + if ( file == NULL ) + throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", fileOfPaths, errno, strerror(errno)); + this->addDependency(Options::depFileList, fileOfPaths); + } + + char path[PATH_MAX]; + ld::File::Ordinal previousOrdinal = baseOrdinal; + while ( fgets(path, PATH_MAX, file) != NULL ) { + path[PATH_MAX-1] = '\0'; + char* eol = strchr(path, '\n'); + if ( eol != NULL ) + *eol = '\0'; + if ( prefix != NULL ) { + char builtPath[strlen(prefix)+strlen(path)+2]; + strcpy(builtPath, prefix); + strcat(builtPath, "/"); + strcat(builtPath, path); + if (fPipelineFifo != NULL) { + FileInfo info = FileInfo(builtPath); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } else { + FileInfo info = findFile(builtPath); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } + } + else { + if (fPipelineFifo != NULL) { + FileInfo info = FileInfo(path); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } else { + FileInfo info = findFile(path); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } + } + } + fclose(file); +} + + +void Options::SetWithWildcards::remove(const NameSet& toBeRemoved) +{ + for(NameSet::const_iterator it=toBeRemoved.begin(); it != toBeRemoved.end(); ++it) { + const char* symbolName = *it; + NameSet::iterator pos = fRegular.find(symbolName); + if ( pos != fRegular.end() ) + fRegular.erase(pos); + } +} + +bool Options::SetWithWildcards::hasWildCards(const char* symbol) +{ + // an exported symbol name containing *, ?, or [ requires wildcard matching + return ( strpbrk(symbol, "*?[") != NULL ); +} + +void Options::SetWithWildcards::insert(const char* symbol) +{ + if ( hasWildCards(symbol) ) + fWildCard.push_back(symbol); + else + fRegular.insert(symbol); +} + +bool Options::SetWithWildcards::contains(const char* symbol, bool* matchBecauseOfWildcard) const +{ + if ( matchBecauseOfWildcard != NULL ) + *matchBecauseOfWildcard = false; + // first look at hash table on non-wildcard symbols + if ( fRegular.find(symbol) != fRegular.end() ) + return true; + // next walk list of wild card symbols looking for a match + for(std::vector::const_iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { + if ( wildCardMatch(*it, symbol) ) { + if ( matchBecauseOfWildcard != NULL ) + *matchBecauseOfWildcard = true; + return true; + } + } + return false; +} + +// Support "foo.o:_bar" to mean symbol _bar in file foo.o +bool Options::SetWithWildcards::containsWithPrefix(const char* symbol, const char* file, bool& wildCardMatch) const +{ + wildCardMatch = false; + if ( contains(symbol, &wildCardMatch) ) + return true; + if ( file == NULL ) + return false; + const char* s = strrchr(file, '/'); + if ( s != NULL ) + file = s+1; + char buff[strlen(file)+strlen(symbol)+2]; + sprintf(buff, "%s:%s", file, symbol); + return contains(buff, &wildCardMatch); +} + +bool Options::SetWithWildcards::containsNonWildcard(const char* symbol) const +{ + // look at hash table on non-wildcard symbols + return ( fRegular.find(symbol) != fRegular.end() ); +} + + +std::vector Options::exportsData() const +{ + return fExportSymbols.data(); +} + + +std::vector Options::SetWithWildcards::data() const +{ + std::vector data; + for (NameSet::const_iterator it=regularBegin(); it != regularEnd(); ++it) { + data.push_back(*it); + } + for (std::vector::const_iterator it=fWildCard.begin(); it != fWildCard.end(); ++it) { + data.push_back(*it); + } + return data; +} + +bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) const +{ + ++p; // find end + const char* b = p; + while ( *p != '\0' ) { + if ( *p == ']') { + const char* e = p; + // found beginining [ and ending ] + unsigned char last = '\0'; + for ( const char* s = b; s < e; ++s ) { + if ( *s == '-' ) { + unsigned char next = *(++s); + if ( (last <= c) && (c <= next) ) + return true; + ++s; + } + else { + if ( *s == c ) + return true; + last = *s; + } + } + return false; + } + ++p; + } + return false; +} + +bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol) const +{ + const char* s = symbol; + for (const char* p = pattern; *p != '\0'; ++p) { + switch ( *p ) { + case '*': + if ( p[1] == '\0' ) + return true; + for (const char* t = s; *t != '\0'; ++t) { + if ( wildCardMatch(&p[1], t) ) + return true; + } + return false; + case '?': + if ( *s == '\0' ) + return false; + ++s; + break; + case '[': + if ( ! inCharRange(p, *s) ) + return false; + ++s; + break; + default: + if ( *s != *p ) + return false; + ++s; + } + } + return (*s == '\0'); +} + + +void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set) +{ + if ( fileOfExports == NULL ) + throwf("missing file after %s", option); + // read in whole file + int fd = ::open(fileOfExports, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open %s file: %s", option, fileOfExports); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) + throwf("can't process %s file: %s", option, fileOfExports); + + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read %s file: %s", option, fileOfExports); + + this->addDependency(Options::depMisc, fileOfExports); + + ::close(fd); + + // parse into symbols and add to unordered_set + char * const end = &p[stat_buf.st_size]; + enum { lineStart, inSymbol, inComment } state = lineStart; + char* symbolStart = NULL; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( (*s == '\n') || (*s == '\r') ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + set.insert(symbolStart); + symbolStart = NULL; + state = lineStart; + } + break; + case inComment: + if ( (*s == '\n') || (*s == '\r') ) + state = lineStart; + break; + } + } + if ( state == inSymbol ) { + warning("missing line-end at end of file \"%s\"", fileOfExports); + int len = end-symbolStart+1; + char* temp = new char[len]; + strlcpy(temp, symbolStart, len); + + // remove any trailing spaces + char* last = &temp[len-2]; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + set.insert(temp); + } + + // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table +} + +void Options::parseAliasFile(const char* fileOfAliases) +{ + // read in whole file + int fd = ::open(fileOfAliases, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open alias file: %s", fileOfAliases); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size+1); + if ( p == NULL ) + throwf("can't process alias file: %s", fileOfAliases); + + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read alias file: %s", fileOfAliases); + p[stat_buf.st_size] = '\n'; + ::close(fd); + this->addDependency(Options::depMisc, fileOfAliases); + + // parse into symbols and add to fAliases + AliasPair pair; + char * const end = &p[stat_buf.st_size+1]; + enum { lineStart, inRealName, inBetween, inAliasName, inComment } state = lineStart; + int lineNumber = 1; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inRealName; + pair.realName = s; + } + break; + case inRealName: + if ( *s == '\n' ) { + warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases); + ++lineNumber; + state = lineStart; + } + else if ( isspace(*s) ) { + *s = '\0'; + state = inBetween; + } + break; + case inBetween: + if ( *s == '\n' ) { + warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases); + ++lineNumber; + state = lineStart; + } + else if ( ! isspace(*s) ) { + state = inAliasName; + pair.alias = s; + } + break; + case inAliasName: + if ( *s =='#' ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + fAliases.push_back(pair); + state = inComment; + } + else if ( *s == '\n' ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + fAliases.push_back(pair); + state = lineStart; + } + break; + case inComment: + if ( *s == '\n' ) + state = lineStart; + break; + } + } + + // Note: we do not free() the malloc buffer, because the strings therein are used by fAliases +} + + + +void Options::setUndefinedTreatment(const char* treatment) +{ + if ( treatment == NULL ) + throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]"; + + if ( strcmp(treatment, "warning") == 0 ) + fUndefinedTreatment = kUndefinedWarning; + else if ( strcmp(treatment, "error") == 0 ) + fUndefinedTreatment = kUndefinedError; + else if ( strcmp(treatment, "suppress") == 0 ) + fUndefinedTreatment = kUndefinedSuppress; + else if ( strcmp(treatment, "dynamic_lookup") == 0 ) + fUndefinedTreatment = kUndefinedDynamicLookup; + else + throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]"; +} + +Options::Treatment Options::parseTreatment(const char* treatment) +{ + if ( treatment == NULL ) + return kNULL; + + if ( strcmp(treatment, "warning") == 0 ) + return kWarning; + else if ( strcmp(treatment, "error") == 0 ) + return kError; + else if ( strcmp(treatment, "suppress") == 0 ) + return kSuppress; + else + return kInvalid; +} + +void Options::setMacOSXVersionMin(const char* version) +{ + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-macosx_version_min value malformed: '%s'", version); + } + fMacVersionMin = (ld::MacVersionMin)value; + fPlatform = kPlatformOSX; +} + +void Options::setIOSVersionMin(const char* version) +{ + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-ios_version_min value malformed: '%s'", version); + } + fIOSVersionMin = (ld::IOSVersionMin)value; + fPlatform = kPlatformiOS; +} + + +void Options::setWatchOSVersionMin(const char* version) +{ + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-watchos_version_min value malformed: '%s'", version); + } + fWatchOSVersionMin = (ld::WatchOSVersionMin)value; + fPlatform = kPlatformWatchOS; +} + + +bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IOSVersionMin requirediPhoneOSMin) +{ + if ( fMacVersionMin != ld::macVersionUnset ) { + return ( fMacVersionMin >= requiredMacMin ); + } + else { + return min_iOS(requirediPhoneOSMin); + } +} + +bool Options::min_iOS(ld::IOSVersionMin requirediOSMin) +{ + if ( fWatchOSVersionMin != ld::wOSVersionUnset ) { + // Hack until we fully track watch and ios versions seperately + return ( (fWatchOSVersionMin + 0x00070000) >= requirediOSMin); + } + else if ( fPlatform == Options::kPlatform_bridgeOS ) { + // Hack until we fully track bridge and ios versions seperately + return ( (fIOSVersionMin + 0x00090000) >= requirediOSMin); + } + else { + return ( fIOSVersionMin >= requirediOSMin ); + } +} + +void Options::setWeakReferenceMismatchTreatment(const char* treatment) +{ + if ( treatment == NULL ) + throw "-weak_reference_mismatches missing [ error | weak | non-weak ]"; + + if ( strcmp(treatment, "error") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError; + else if ( strcmp(treatment, "weak") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak; + else if ( strcmp(treatment, "non-weak") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak; + else + throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]"; +} + +Options::CommonsMode Options::parseCommonsTreatment(const char* mode) +{ + if ( mode == NULL ) + throw "-commons missing [ ignore_dylibs | use_dylibs | error ]"; + + if ( strcmp(mode, "ignore_dylibs") == 0 ) + return kCommonsIgnoreDylibs; + else if ( strcmp(mode, "use_dylibs") == 0 ) + return kCommonsOverriddenByDylibs; + else if ( strcmp(mode, "error") == 0 ) + return kCommonsConflictsDylibsError; + else + throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]"; +} + +void Options::addDylibOverride(const char* paths) +{ + if ( paths == NULL ) + throw "-dylib_file must followed by two colon separated paths"; + const char* colon = strchr(paths, ':'); + if ( colon == NULL ) + throw "-dylib_file must followed by two colon separated paths"; + int len = colon-paths; + char* target = new char[len+2]; + strncpy(target, paths, len); + target[len] = '\0'; + DylibOverride entry; + entry.installName = target; + entry.useInstead = &colon[1]; + fDylibOverrides.push_back(entry); +} + +uint64_t Options::parseAddress(const char* addr) +{ + char* endptr; + uint64_t result = strtoull(addr, &endptr, 16); + return result; +} + +uint32_t Options::parseProtection(const char* prot) +{ + uint32_t result = 0; + for(const char* p = prot; *p != '\0'; ++p) { + switch(tolower(*p)) { + case 'r': + result |= VM_PROT_READ; + break; + case 'w': + result |= VM_PROT_WRITE; + break; + case 'x': + result |= VM_PROT_EXECUTE; + break; + case '-': + break; + default: + throwf("unknown -segprot lettter in %s", prot); + } + } + return result; +} + + +// +// Parses number of form A[.B[.B[.D[.E]]]] into a uint64_t where the bits are a24.b10.c10.d10.e10 +// +uint64_t Options::parseVersionNumber64(const char* versionString) +{ + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; + uint64_t e = 0; + char* end; + a = strtoul(versionString, &end, 10); + if ( *end == '.' ) { + b = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + c = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + d = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + e = strtoul(&end[1], &end, 10); + } + } + } + } + if ( (*end != '\0') || (a > 0xFFFFFF) || (b > 0x3FF) || (c > 0x3FF) || (d > 0x3FF) || (e > 0x3FF) ) + throwf("malformed 64-bit a.b.c.d.e version number: %s", versionString); + + return (a << 40) | ( b << 30 ) | ( c << 20 ) | ( d << 10 ) | e; +} + + +uint32_t Options::currentVersion32() const +{ + // warn if it does not fit into 32 bit vers number + uint32_t a = (fDylibCurrentVersion >> 40) & 0xFFFF; + uint32_t b = (fDylibCurrentVersion >> 30) & 0xFF; + uint32_t c = (fDylibCurrentVersion >> 20) & 0xFF; + uint64_t rep32 = ((uint64_t)a << 40) | ((uint64_t)b << 30) | ((uint64_t)c << 20); + if ( rep32 != fDylibCurrentVersion ) { + warning("truncating -current_version to fit in 32-bit space used by old mach-o format"); + a = (fDylibCurrentVersion >> 40) & 0xFFFFFF; + if ( a > 0xFFFF ) + a = 0xFFFF; + b = (fDylibCurrentVersion >> 30) & 0x3FF; + if ( b > 0xFF ) + b = 0xFF; + c = (fDylibCurrentVersion >> 20) & 0x3FF; + if ( c > 0xFF ) + c = 0xFF; + } + return (a << 16) | ( b << 8 ) | c; +} + +// +// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz +// +uint32_t Options::parseVersionNumber32(const char* versionString) +{ + uint32_t x = 0; + uint32_t y = 0; + uint32_t z = 0; + char* end; + x = strtoul(versionString, &end, 10); + if ( *end == '.' ) { + y = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + z = strtoul(&end[1], &end, 10); + } + } + if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) + throwf("malformed 32-bit x.y.z version number: %s", versionString); + + return (x << 16) | ( y << 8 ) | z; +} + +static const char* cstringSymbolName(const char* orderFileString) +{ + char* result; + asprintf(&result, "cstring=%s", orderFileString); + // convert escaped characters + char* d = result; + for(const char* s=result; *s != '\0'; ++s, ++d) { + if ( *s == '\\' ) { + ++s; + switch ( *s ) { + case 'n': + *d = '\n'; + break; + case 't': + *d = '\t'; + break; + case 'v': + *d = '\v'; + break; + case 'b': + *d = '\b'; + break; + case 'r': + *d = '\r'; + break; + case 'f': + *d = '\f'; + break; + case 'a': + *d = '\a'; + break; + case '\\': + *d = '\\'; + break; + case '?': + *d = '\?'; + break; + case '\'': + *d = '\r'; + break; + case '\"': + *d = '\"'; + break; + case 'x': + // hexadecimal value of char + { + ++s; + char value = 0; + while ( isxdigit(*s) ) { + value *= 16; + if ( isdigit(*s) ) + value += (*s-'0'); + else + value += ((toupper(*s)-'A') + 10); + ++s; + } + *d = value; + } + break; + default: + if ( isdigit(*s) ) { + // octal value of char + char value = 0; + while ( isdigit(*s) ) { + value = (value << 3) + (*s-'0'); + ++s; + } + *d = value; + } + } + } + else { + *d = *s; + } + } + *d = '\0'; + return result; +} + +void Options::parseOrderFile(const char* path, bool cstring) +{ + // order files override auto-ordering + fAutoOrderInitializers = false; + + // ld64 should prefer OrderFiles from the SDK over the ones in / + for (const char* sdkPath : fSDKPaths) { + char fullPath[PATH_MAX]; + strlcpy(fullPath, sdkPath, PATH_MAX); + strlcat(fullPath, "/", PATH_MAX); + strlcat(fullPath, path, PATH_MAX); + struct stat statBuffer; + if ( stat(fullPath, &statBuffer) == 0 ) { + path = strdup(fullPath); + break; + } + } + + // read in whole file + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open order file: %s", path); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size+1); + if ( p == NULL ) + throwf("can't process order file: %s", path); + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read order file: %s", path); + ::close(fd); + p[stat_buf.st_size] = '\n'; + this->addDependency(Options::depMisc, path); + + // parse into vector of pairs + char * const end = &p[stat_buf.st_size+1]; + enum { lineStart, inSymbol, inComment } state = lineStart; + char* symbolStart = NULL; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) || cstring ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( (*s == '\n') || (!cstring && (*s == '#')) ) { + bool wasComment = (*s == '#'); + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + if ( strncmp(symbolStart, "ppc:", 4) == 0 ) { + if ( fArchitecture == CPU_TYPE_POWERPC ) + symbolStart = &symbolStart[4]; + else + symbolStart = NULL; + } + // if there is an architecture prefix, only use this symbol it if matches current arch + else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) { + if ( fArchitecture == CPU_TYPE_POWERPC64 ) + symbolStart = &symbolStart[6]; + else + symbolStart = NULL; + } + else if ( strncmp(symbolStart, "i386:", 5) == 0 ) { + if ( fArchitecture == CPU_TYPE_I386 ) + symbolStart = &symbolStart[5]; + else + symbolStart = NULL; + } + else if ( strncmp(symbolStart, "x86_64:", 7) == 0 ) { + if ( fArchitecture == CPU_TYPE_X86_64 ) + symbolStart = &symbolStart[7]; + else + symbolStart = NULL; + } + else if ( strncmp(symbolStart, "arm:", 4) == 0 ) { + if ( fArchitecture == CPU_TYPE_ARM ) + symbolStart = &symbolStart[4]; + else + symbolStart = NULL; + } + else if ( strncmp(symbolStart, "arm64:", 6) == 0 ) { + if ( fArchitecture == CPU_TYPE_ARM64 ) + symbolStart = &symbolStart[6]; + else + symbolStart = NULL; + } + if ( symbolStart != NULL ) { + char* objFileName = NULL; + char* colon = strstr(symbolStart, ".o:"); + if ( colon != NULL ) { + colon[2] = '\0'; + objFileName = symbolStart; + symbolStart = &colon[3]; + } + else { + colon = strstr(symbolStart, ".o):"); + if ( colon != NULL ) { + colon[3] = '\0'; + objFileName = symbolStart; + symbolStart = &colon[4]; + } + } + // trim leading spaces + while ( isspace(*symbolStart) ) + ++symbolStart; + Options::OrderedSymbol pair; + if ( cstring ) + pair.symbolName = cstringSymbolName(symbolStart); + else + pair.symbolName = symbolStart; + pair.objectFileName = objFileName; + fOrderedSymbols.push_back(pair); + } + symbolStart = NULL; + if ( wasComment ) + state = inComment; + else + state = lineStart; + } + break; + case inComment: + if ( *s == '\n' ) + state = lineStart; + break; + } + } + // Note: we do not free() the malloc buffer, because the strings are used by the fOrderedSymbols +} + +void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path) +{ + if ( (strcmp(section, "__cstring") == 0) && (strcmp(segment, "__TEXT") == 0) ) { + parseOrderFile(path, true); + } + else if ( (strncmp(section, "__literal",9) == 0) && (strcmp(segment, "__TEXT") == 0) ) { + warning("sorting of __literal[4,8,16] sections not supported"); + } + else { + // ignore section information and append all symbol names to global order file + parseOrderFile(path, false); + } +} + +void Options::addSection(const char* segment, const char* section, const char* path) +{ + if ( strlen(segment) > 16 ) + throw "-seccreate segment name max 16 chars"; + if ( strlen(section) > 16 ) { + char* tmp = strdup(section); + tmp[16] = '\0'; + warning("-seccreate section name (%s) truncated to 16 chars (%s)\n", section, tmp); + section = tmp; + } + + // read in whole file + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open -sectcreate file: %s", path); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) + throwf("can't process -sectcreate file: %s", path); + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read -sectcreate file: %s", path); + ::close(fd); + + // record section to create + ExtraSection info = { segment, section, path, (uint8_t*)p, (uint64_t)stat_buf.st_size }; + fExtraSections.push_back(info); +} + +void Options::addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection) +{ + if ( strlen(srcSegment) > 16 ) + throw "-rename_section segment name max 16 chars"; + if ( strlen(srcSection) > 16 ) + throw "-rename_section section name max 16 chars"; + if ( strlen(dstSegment) > 16 ) + throw "-rename_section segment name max 16 chars"; + if ( strlen(dstSection) > 16 ) + throw "-rename_section section name max 16 chars"; + + SectionRename info; + info.fromSegment = srcSegment; + info.fromSection = srcSection; + info.toSegment = dstSegment; + info.toSection = dstSection; + + fSectionRenames.push_back(info); +} + + +void Options::addSegmentRename(const char* srcSegment, const char* dstSegment) +{ + if ( strlen(srcSegment) > 16 ) + throw "-rename_segment segment name max 16 chars"; + if ( strlen(dstSegment) > 16 ) + throw "-rename_segment segment name max 16 chars"; + + SegmentRename info; + info.fromSegment = srcSegment; + info.toSegment = dstSegment; + + fSegmentRenames.push_back(info); +} + + + +void Options::addSymbolMove(const char* dstSegment, const char* symbolList, + std::vector& list, const char* optionName) +{ + if ( strlen(dstSegment) > 16 ) + throwf("%s segment name max 16 chars", optionName); + + SymbolsMove tmp; + list.push_back(tmp); + SymbolsMove& info = list.back(); + info.toSegment = dstSegment; + loadExportFile(symbolList, optionName, info.symbols); +} + +bool Options::moveRwSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const +{ + for (std::vector::const_iterator it=fSymbolsMovesData.begin(); it != fSymbolsMovesData.end(); ++it) { + const SymbolsMove& info = *it; + if ( info.symbols.containsWithPrefix(symName, filePath, wildCardMatch)) { + seg = info.toSegment; + return true; + } + } + return false; +} + +bool Options::moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const +{ + for (std::vector::const_iterator it=fSymbolsMovesCode.begin(); it != fSymbolsMovesCode.end(); ++it) { + const SymbolsMove& info = *it; + if ( info.symbols.containsWithPrefix(symName, filePath, wildCardMatch)) { + seg = info.toSegment; + return true; + } + } + return false; +} + +void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) +{ + if ( strlen(segment) > 16 ) + throw "-sectalign segment name max 16 chars"; + if ( strlen(section) > 16 ) + throw "-sectalign section name max 16 chars"; + + // argument to -sectalign is a hexadecimal number + char* endptr; + unsigned long value = strtoul(alignmentStr, &endptr, 16); + if ( *endptr != '\0') + throw "argument for -sectalign is not a hexadecimal number"; + if ( value > 0x8000 ) + throw "argument for -sectalign must be less than or equal to 0x8000"; + if ( value == 0 ) { + warning("zero is not a valid -sectalign"); + value = 1; + } + + // alignment is power of 2 (e.g. page alignment = 12) + uint8_t alignment = (uint8_t)__builtin_ctz(value); + if ( (unsigned long)(1 << alignment) != value ) { + warning("alignment for -sectalign %s %s is not a power of two, using 0x%X", + segment, section, 1 << alignment); + } + + SectionAlignment info = { segment, section, alignment }; + fSectionAlignments.push_back(info); +} + +void Options::addLibrary(const FileInfo& info) +{ + // if this library has already been added, don't add again (archives are automatically repeatedly searched) + for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { + if ( strcmp(info.path, fit->path) == 0 ) { + // if dylib is specified again but weak, record that it should be weak + if ( info.options.fWeakImport ) + fit->options.fWeakImport = true; + return; + } + } + // add to list + fInputFiles.push_back(info); +} + +void Options::warnObsolete(const char* arg) +{ + warning("option %s is obsolete and being ignored", arg); +} + + +void Options::cannotBeUsedWithBitcode(const char* arg) +{ + if ( fBundleBitcode ) + throwf("%s and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together", arg); +} + +std::string Options::getVersionString32(uint32_t ver) const +{ + if (ver == 0 || ver >= 0x10000000) + return "0.0.0"; + + unsigned microVersion = ver & 0xFF; + unsigned minorVersion = (ver >> 8) & 0xFF; + unsigned majorVersion = (ver >> 16) & 0xFF; + std::stringstream versionString; + versionString << majorVersion << "." << minorVersion << "." << microVersion; + return versionString.str(); +} + +std::string Options::getVersionString64(uint64_t ver) const +{ + uint64_t a = (ver >> 40) & 0xFFFFFF; + uint64_t b = (ver >> 30) & 0x3FF; + uint64_t c = (ver >> 20) & 0x3FF; + uint64_t d = (ver >> 10) & 0x3FF; + uint64_t e = ver & 0x3FF; + std::stringstream versionString; + versionString << a << "." << b << "." << c << "." << d << "." << e; + return versionString.str(); +} + +// Convert X.Y[.Z] to 32-bit value xxxxyyzz +bool Options::parsePackedVersion32(const std::string& versionStr, uint32_t &result) +{ + result = 0; + + if ( versionStr.empty() ) + return false; + + size_t pos = versionStr.find('.'); + if ( pos == std::string::npos ) + return false; + + std::string majorStr = versionStr.substr(0, pos); + std::string rest = versionStr.substr(pos+1); + + try { + size_t majorEnd; + int majorValue = std::stoi(majorStr, &majorEnd); + if ( majorEnd != majorStr.size() ) + return false; + if ( majorValue < 0 ) + return false; + if ( majorValue > 65535 ) + return false; + + std::string minorStr; + std::string microStr; + pos = rest.find('.'); + if ( pos == std::string::npos ) { + minorStr = rest; + } + else { + minorStr = rest.substr(0, pos); + microStr = rest.substr(pos+1); + } + + size_t minorEnd; + int minorValue = std::stoi(minorStr, &minorEnd); + if ( minorEnd != minorStr.size() ) + return false; + if ( minorValue < 0 ) + return false; + if ( minorValue > 255 ) + return false; + + int microValue = 0; + if ( !microStr.empty() ) { + size_t microEnd; + microValue = std::stoi(microStr, µEnd); + if ( microEnd != microStr.size() ) + return false; + if ( microValue < 0 ) + return false; + if ( microValue > 255 ) + return false; + } + + result = (majorValue << 16) | (minorValue << 8) | microValue; + + return true; + } + catch (...) { + // std::stoi() throws exception on malformed input + return false; + } +} + +std::string Options::getSDKVersionStr() const +{ + return getVersionString32(fSDKVersion); +} + +std::string Options::getPlatformStr() const +{ + switch (fPlatform) { + case Options::kPlatformOSX: + return "MacOSX"; + case Options::kPlatformiOS: + if (targetIOSSimulator()) + return "iPhoneSimulator"; + else + return "iPhoneOS"; + case Options::kPlatformWatchOS: + if (targetIOSSimulator()) + return "watchOS Simulator"; + else + return "watchOS"; +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + if (targetIOSSimulator()) + return "AppleTVSimulator"; + else + return "AppleTVOS"; + break; +#endif + case Options::kPlatform_bridgeOS: + return "bridgeOS"; + case Options::kPlatformUnknown: + return "Unknown"; + } +} + +std::vector Options::writeBitcodeLinkOptions() const +{ + std::vector linkCommand; + switch ( fOutputKind ) { + case Options::kDynamicLibrary: + linkCommand.push_back("-dylib"); + linkCommand.push_back("-compatibility_version"); + if ( fDylibCompatVersion != 0 ) { + linkCommand.push_back(getVersionString32(fDylibCompatVersion)); + } else { + linkCommand.push_back(getVersionString32(currentVersion32())); + } + if ( fDylibCurrentVersion != 0 ) { + linkCommand.push_back("-current_version"); + linkCommand.push_back(getVersionString64(fDylibCurrentVersion)); + } + linkCommand.push_back("-install_name"); + linkCommand.push_back(installPath()); + break; + case Options::kDynamicExecutable: + linkCommand.push_back("-execute"); + break; + case Options::kObjectFile: + linkCommand.push_back("-r"); + break; + default: + throwf("could not write bitcode options file output kind\n"); + } + + if (!fImplicitlyLinkPublicDylibs) + linkCommand.push_back("-no_implicit_dylibs"); + + // Add deployment target. + // Platform is allowed to be unknown for "ld -r". + switch (fPlatform) { + case Options::kPlatformOSX: + linkCommand.push_back("-macosx_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fMacVersionMin)); + break; + case Options::kPlatformiOS: + if (targetIOSSimulator()) + linkCommand.push_back("-ios_simulator_version_min"); + else + linkCommand.push_back("-ios_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; + case Options::kPlatformWatchOS: + if (targetIOSSimulator()) + linkCommand.push_back("-watchos_simulator_version_min"); + else + linkCommand.push_back("-watchos_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + if (targetIOSSimulator()) + linkCommand.push_back("-tvos_simulator_version_min"); + else + linkCommand.push_back("-tvos_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; +#endif + case Options::kPlatform_bridgeOS: + linkCommand.push_back("-bridgeos_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; + case Options::kPlatformUnknown: + if ( fOutputKind != Options::kObjectFile ) { + throwf("platform is unknown for final bitcode bundle," + "deployment target and min version is required for -bitcode_bundle"); + } + break; + } + + + // entry name + if ( fEntryName ) { + linkCommand.push_back("-e"); + linkCommand.push_back(fEntryName); + } + + // Write rpaths + if (!fRPaths.empty()) { + for (std::vector::const_iterator it=fRPaths.begin(); it != fRPaths.end(); ++it) { + linkCommand.push_back("-rpath"); + linkCommand.push_back(*it); + } + } + + // Other bitcode compatiable options + if ( fObjCABIVersion1Override ) { + linkCommand.push_back("-objc_abi_version"); + linkCommand.push_back("1"); + } else if ( fObjCABIVersion2Override ) { + linkCommand.push_back("-objc_abi_version"); + linkCommand.push_back("2"); + } + if ( fExecutablePath ) { + linkCommand.push_back("-executable_path"); + linkCommand.push_back(fExecutablePath); + } + if ( fDeadStrip ) + linkCommand.push_back("-dead_strip"); + if ( fExportDynamic ) + linkCommand.push_back("-export_dynamic"); + if ( fMarkAppExtensionSafe && fCheckAppExtensionSafe ) + linkCommand.push_back("-application_extension"); + + if ( fSourceVersionLoadCommandForceOn ) + linkCommand.push_back("-add_source_version"); + if ( fSourceVersion != 0 ) { + linkCommand.push_back("-source_version"); + linkCommand.push_back(getVersionString64(fSourceVersion)); + } + + // linker flag added by swift driver + // rdar://problem/20108072 + if ( !fObjcCategoryMerging ) + linkCommand.push_back("-no_objc_category_merging"); + + return linkCommand; +} + +// +// Process all command line arguments. +// +// The only error checking done here is that each option is valid and if it has arguments +// that they too are valid. +// +// The general rule is "last option wins", i.e. if both -bundle and -dylib are specified, +// whichever was last on the command line is used. +// +// Error check for invalid combinations of options is done in checkIllegalOptionCombinations() +// +void Options::parse(int argc, const char* argv[]) +{ + // Store the original args in the link snapshot. + fLinkSnapshot.recordRawArgs(argc, argv); + + // pass one builds search list from -L and -F options + this->buildSearchPaths(argc, argv); + + // reduce re-allocations + fInputFiles.reserve(32); + + // pass two parse all other options + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + + if ( arg[0] == '-' ) { + // by default, copy one arg to the snapshot link command, and do no file copying + int snapshotArgIndex = i; + int snapshotArgCount = -1; // -1 means compute count based on change in index + int snapshotFileArgIndex = -1; // -1 means no data file parameter to arg + + // Since we don't care about the files passed, just the option names, we do this here. + if (fPrintOptions) + fprintf (stderr, "[Logging ld64 options]\t%s\n", arg); + + if ( (arg[1] == 'L') || (arg[1] == 'F') ) { + snapshotArgCount = 0; // stripped out of link snapshot + if (arg[2] == '\0') + ++i; + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-arch") == 0 ) { + parseArch(argv[++i]); + } + else if ( strcmp(arg, "-dynamic") == 0 ) { + // default + } + else if ( strcmp(arg, "-static") == 0 ) { + fForStatic = true; + if ( (fOutputKind != kObjectFile) && (fOutputKind != kKextBundle) ) { + fOutputKind = kStaticExecutable; + } + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-dylib") == 0 ) { + fOutputKind = kDynamicLibrary; + } + else if ( strcmp(arg, "-bundle") == 0 ) { + fOutputKind = kDynamicBundle; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-dylinker") == 0 ) { + fOutputKind = kDyld; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-execute") == 0 ) { + if ( fOutputKind != kStaticExecutable ) + fOutputKind = kDynamicExecutable; + } + else if ( strcmp(arg, "-preload") == 0 ) { + fOutputKind = kPreload; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-r") == 0 ) { + fOutputKind = kObjectFile; + } + else if ( strcmp(arg, "-kext") == 0 ) { + fOutputKind = kKextBundle; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-o") == 0 ) { + snapshotArgCount = 0; + fOutputFile = argv[++i]; + fLinkSnapshot.setSnapshotName(fOutputFile); + } + else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { + snapshotArgCount = 0; + FileInfo info = findLibrary(&arg[7], true); + info.options.fLazyLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-lto_library") == 0 ) { + snapshotFileArgIndex = 1; + fOverridePathlibLTO = argv[++i]; + if ( fOverridePathlibLTO == NULL ) + throw "missing argument to -lto_library"; + } + else if ( strcmp(arg, "-cache_path_lto") == 0 ) { + fLtoCachePath = argv[++i]; + if ( fLtoCachePath == NULL ) + throw "missing argument to -cache_path_lto"; + } + else if ( strcmp(arg, "-prune_interval_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -prune_interval_lto"; + char* endptr; + fLtoPruneInterval = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -prune_interval_lto"; + } + else if ( strcmp(arg, "-prune_after_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -prune_after_lto"; + char* endptr; + fLtoPruneAfter = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -prune_after_lto"; + } + else if ( strcmp(arg, "-max_relative_cache_size_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -max_relative_cache_size_lto"; + char* endptr; + fLtoMaxCacheSize = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -max_relative_cache_size_lto"; + if (fLtoMaxCacheSize > 100) + throw "Expect a value between 0 and 100 for -max_relative_cache_size_lto"; + } + else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) { + snapshotArgCount = 0; + FileInfo info = findLibrary(&arg[2]); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + } + // This causes a dylib to be weakly bound at + // link time. This corresponds to weak_import. + else if ( strncmp(arg, "-weak-l", 7) == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findLibrary(&arg[7]); + info.options.fWeakImport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + } + // Avoid lazy binding. + else if ( strcmp(arg, "-bind_at_load") == 0 ) { + fBindAtLoad = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-twolevel_namespace") == 0 ) { + fNameSpace = kTwoLevelNameSpace; + } + else if ( strcmp(arg, "-flat_namespace") == 0 ) { + fNameSpace = kFlatNameSpace; + cannotBeUsedWithBitcode(arg); + } + // Also sets a bit to ensure dyld causes everything + // in the namespace to be flat. + // ??? Deprecate + else if ( strcmp(arg, "-force_flat_namespace") == 0 ) { + fNameSpace = kForceFlatNameSpace; + cannotBeUsedWithBitcode(arg); + } + // Similar to --whole-archive. + else if ( strcmp(arg, "-all_load") == 0 ) { + fFullyLoadArchives = true; + } + else if ( strcmp(arg, "-noall_load") == 0) { + warnObsolete(arg); + } + // Similar to -all_load + else if ( strcmp(arg, "-ObjC") == 0 ) { + fLoadAllObjcObjectsFromArchives = true; + } + // Similar to -all_load, but for the following archive only. + else if ( strcmp(arg, "-force_load") == 0 ) { + FileInfo info = findFile(argv[++i]); + info.options.fForceLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + } + // Library versioning. + else if ( (strcmp(arg, "-dylib_compatibility_version") == 0) + || (strcmp(arg, "-compatibility_version") == 0)) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-dylib_compatibility_version missing "; + fDylibCompatVersion = parseVersionNumber32(vers); + } + else if ( (strcmp(arg, "-dylib_current_version") == 0) + || (strcmp(arg, "-current_version") == 0)) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-dylib_current_version missing "; + fDylibCurrentVersion = parseVersionNumber64(vers); + } + else if ( strcmp(arg, "-sectorder") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) + throw "-sectorder missing
"; + snapshotFileArgIndex = 3; + parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-order_file") == 0 ) { + snapshotFileArgIndex = 1; + parseOrderFile(argv[++i], false); + } + else if ( strcmp(arg, "-order_file_statistics") == 0 ) { + fPrintOrderFileStatistics = true; + cannotBeUsedWithBitcode(arg); + } + // ??? Deprecate segcreate. + // -sectcreate puts whole files into a section in the output. + else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) + throw "-sectcreate missing
"; + snapshotFileArgIndex = 3; + addSection(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + } + // Since we have a full path in binary/library names we need to be able to override it. + else if ( (strcmp(arg, "-dylib_install_name") == 0) + || (strcmp(arg, "-dylinker_install_name") == 0) + || (strcmp(arg, "-install_name") == 0)) { + fDylibInstallName = argv[++i]; + if ( fDylibInstallName == NULL ) + throw "-install_name missing "; + } + // Sets the base address of the output. + else if ( (strcmp(arg, "-seg1addr") == 0) || (strcmp(arg, "-image_base") == 0) ) { + const char* address = argv[++i]; + if ( address == NULL ) + throwf("%s missing
", arg); + fBaseAddress = parseAddress(address); + uint64_t temp = ((fBaseAddress+fSegmentAlignment-1) & (-fSegmentAlignment)); + if ( fBaseAddress != temp ) { + warning("-seg1addr not %lld byte aligned, rounding up", fSegmentAlignment); + fBaseAddress = temp; + } + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-e") == 0 ) { + fEntryName = argv[++i]; + } + // Same as -@ from the FSF linker. + else if ( strcmp(arg, "-filelist") == 0 ) { + snapshotArgCount = 0; + const char* path = argv[++i]; + if ( (path == NULL) || (path[0] == '-') ) + throw "-filelist missing "; + ld::File::Ordinal baseOrdinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + loadFileList(path, baseOrdinal); + } + else if ( strcmp(arg, "-keep_private_externs") == 0 ) { + cannotBeUsedWithBitcode(arg); + fKeepPrivateExterns = true; + } + else if ( strcmp(arg, "-final_output") == 0 ) { + fFinalName = argv[++i]; + } + // Ensure that all calls to exported symbols go through lazy pointers. Multi-module + // just ensures that this happens for cross object file boundaries. + else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) { + switch ( fInterposeMode ) { + case kInterposeNone: + case kInterposeAllExternal: + fInterposeMode = kInterposeAllExternal; + break; + case kInterposeSome: + // do nothing, -interposable_list overrides -interposable" + break; + } + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-interposable_list") == 0 ) { + snapshotFileArgIndex = 1; + fInterposeMode = kInterposeSome; + loadExportFile(argv[++i], "-interposable_list", fInterposeList); + cannotBeUsedWithBitcode(arg); + } + // Default for -interposable/-multi_module/-single_module. + else if ( strcmp(arg, "-single_module") == 0 ) { + fInterposeMode = kInterposeNone; + } + else if ( strcmp(arg, "-exported_symbols_list") == 0 ) { + snapshotFileArgIndex = 1; + if ( fExportMode == kDontExportSome ) + throw "can't use -exported_symbols_list and -unexported_symbols_list"; + fExportMode = kExportSome; + loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols); + } + else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) { + snapshotFileArgIndex = 1; + if ( fExportMode == kExportSome ) + throw "can't use -unexported_symbols_list and -exported_symbols_list"; + fExportMode = kDontExportSome; + loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); + } + else if ( strcmp(arg, "-exported_symbol") == 0 ) { + if ( fExportMode == kDontExportSome ) + throw "can't use -exported_symbol and -unexported_symbols"; + fExportMode = kExportSome; + fExportSymbols.insert(argv[++i]); + } + else if ( strcmp(arg, "-unexported_symbol") == 0 ) { + if ( fExportMode == kExportSome ) + throw "can't use -unexported_symbol and -exported_symbol"; + fExportMode = kDontExportSome; + fDontExportSymbols.insert(argv[++i]); + } + else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) { + snapshotFileArgIndex = 1; + if ( fLocalSymbolHandling == kLocalSymbolsSelectiveExclude ) + throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; + fLocalSymbolHandling = kLocalSymbolsSelectiveInclude; + loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) { + snapshotFileArgIndex = 1; + if ( fLocalSymbolHandling == kLocalSymbolsSelectiveInclude ) + throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; + fLocalSymbolHandling = kLocalSymbolsSelectiveExclude; + loadExportFile(argv[++i], "-non_global_symbols_strip_list", fLocalSymbolsExcluded); + cannotBeUsedWithBitcode(arg); + } + // ??? Deprecate + else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { + fIgnoreOtherArchFiles = true; + } + else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { + fForceSubtypeAll = true; + fAllowCpuSubtypeMismatches = true; + cannotBeUsedWithBitcode(arg); + } + // Similar to -weak-l but uses the absolute path name to the library. + else if ( strcmp(arg, "-weak_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findFile(argv[++i]); + info.options.fWeakImport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-lazy_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findFile(argv[++i]); + info.options.fLazyLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-framework") == 0 ) { + snapshotArgCount = 0; + FileInfo info = findFramework(argv[++i]); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + } + else if ( strcmp(arg, "-weak_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findFramework(argv[++i]); + info.options.fWeakImport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + } + else if ( strcmp(arg, "-lazy_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findFramework(argv[++i]); + info.options.fLazyLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-search_paths_first") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-search_dylibs_first") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-undefined") == 0 ) { + setUndefinedTreatment(argv[++i]); + cannotBeUsedWithBitcode(arg); + } + // Debugging output flag. + else if ( strcmp(arg, "-arch_multiple") == 0 ) { + fMessagesPrefixedWithArchitecture = true; + } + // Specify what to do with relocations in read only + // sections like .text. Could be errors, warnings, + // or suppressed. Currently we do nothing with the + // flag. + else if ( strcmp(arg, "-read_only_relocs") == 0 ) { + switch ( parseTreatment(argv[++i]) ) { + case kNULL: + case kInvalid: + throw "-read_only_relocs missing [ warning | error | suppress ]"; + case kWarning: + fWarnTextRelocs = true; + fAllowTextRelocs = true; + cannotBeUsedWithBitcode(arg); + break; + case kSuppress: + fWarnTextRelocs = false; + fAllowTextRelocs = true; + cannotBeUsedWithBitcode(arg); + break; + case kError: + fWarnTextRelocs = false; + fAllowTextRelocs = false; + break; + } + } + else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) { + warnObsolete(arg); + ++i; + } + // Warn, error or make strong a mismatch between weak + // and non-weak references. + else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) { + setWeakReferenceMismatchTreatment(argv[++i]); + } + // For a deployment target of 10.3 and earlier ld64 will + // prebind an executable with 0s in all addresses that + // are prebound. This can then be fixed up by update_prebinding + // later. Prebinding is less useful on 10.4 and greater. + else if ( strcmp(arg, "-prebind") == 0 ) { + fPrebind = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-noprebind") == 0 ) { + warnObsolete(arg); + fPrebind = false; + } + else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-nofixprebinding") == 0 ) { + warnObsolete(arg); + } + // This should probably be deprecated when we respect -L and -F + // when searching for libraries. + else if ( strcmp(arg, "-dylib_file") == 0 ) { + // ignore for snapshot because a stub dylib will be created in the snapshot + snapshotArgCount = 0; + addDylibOverride(argv[++i]); + cannotBeUsedWithBitcode(arg); + } + // What to expand @executable_path to if found in dependent dylibs + else if ( strcmp(arg, "-executable_path") == 0 ) { + fExecutablePath = argv[++i]; + if ( (fExecutablePath == NULL) || (fExecutablePath[0] == '-') ) + throw "-executable_path missing "; + // if a directory was passed, add / to end + // ld64 can't find @executable _path relative dylibs from our umbrella frameworks + struct stat statBuffer; + if ( stat(fExecutablePath, &statBuffer) == 0 ) { + if ( (statBuffer.st_mode & S_IFMT) == S_IFDIR ) { + char* pathWithSlash = new char[strlen(fExecutablePath)+2]; + strcpy(pathWithSlash, fExecutablePath); + strcat(pathWithSlash, "/"); + fExecutablePath = pathWithSlash; + } + } + } + // Aligns all segments to the power of 2 boundary specified. + else if ( strcmp(arg, "-segalign") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-segalign missing "; + fSegmentAlignment = parseAddress(size); + uint8_t alignment = (uint8_t)__builtin_ctz(fSegmentAlignment); + uint32_t p2aligned = (1 << alignment); + if ( p2aligned != fSegmentAlignment ) { + warning("alignment for -segalign %s is not a power of two, using 0x%X", size, p2aligned); + fSegmentAlignment = p2aligned; + } + cannotBeUsedWithBitcode(arg); + } + // Puts a specified segment at a particular address that must + // be a multiple of the segment alignment. + else if ( strcmp(arg, "-segaddr") == 0 ) { + SegmentStart seg; + seg.name = argv[++i]; + if ( (seg.name == NULL) || (argv[i+1] == NULL) ) + throw "-segaddr missing segName Adddress"; + seg.address = parseAddress(argv[++i]); + uint64_t temp = ((seg.address+fSegmentAlignment-1) & (-fSegmentAlignment)); + if ( seg.address != temp ) + warning("-segaddr %s not %lld byte aligned", seg.name, fSegmentAlignment); + fCustomSegmentAddresses.push_back(seg); + cannotBeUsedWithBitcode(arg); + } + // ??? Deprecate when we deprecate split-seg. + else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { + fBaseAddress = parseAddress(argv[++i]); + cannotBeUsedWithBitcode(arg); + } + // ??? Deprecate when we deprecate split-seg. + else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { + fBaseWritableAddress = parseAddress(argv[++i]); + fSplitSegs = true; + cannotBeUsedWithBitcode(arg); + } + // ??? Deprecate when we get rid of basing at build time. + else if ( strcmp(arg, "-seg_addr_table") == 0 ) { + snapshotFileArgIndex = 1; + const char* name = argv[++i]; + if ( name == NULL ) + throw "-seg_addr_table missing argument"; + fSegAddrTablePath = name; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { + warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-segprot") == 0 ) { + SegmentProtect seg; + seg.name = argv[++i]; + if ( (seg.name == NULL) || (argv[i+1] == NULL) || (argv[i+2] == NULL) ) + throw "-segprot missing segName max-prot init-prot"; + seg.max = parseProtection(argv[++i]); + seg.init = parseProtection(argv[++i]); + if ( strcmp(seg.name, "__LINKEDIT") == 0 ) + warning("-segprot cannot be used to modify __LINKEDIT protections"); + else + fCustomSegmentProtections.push_back(seg); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-pagezero_size") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-pagezero_size missing "; + fZeroPageSize = parseAddress(size); + uint64_t temp = fZeroPageSize & (-4096); // page align + if ( (fZeroPageSize != temp) ) + warning("-pagezero_size not page aligned, rounding down"); + fZeroPageSize = temp; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-stack_addr") == 0 ) { + const char* address = argv[++i]; + if ( address == NULL ) + throw "-stack_addr missing
"; + fStackAddr = parseAddress(address); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-stack_size") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-stack_size missing
"; + fStackSize = parseAddress(size); + } + else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { + fExecutableStack = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-allow_heap_execute") == 0 ) { + fDisableNonExecutableHeap = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-sectalign") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) + throw "-sectalign missing
"; + addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-sectorder_detail") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) { + warnObsolete(arg); + i += 2; + } + else if ( strcmp(arg, "-bundle_loader") == 0 ) { + snapshotFileArgIndex = 1; + fBundleLoader = argv[++i]; + if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') ) + throw "-bundle_loader missing "; + FileInfo info = findFile(fBundleLoader); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + info.options.fBundleLoader = true; + fInputFiles.push_back(info); + } + else if ( strcmp(arg, "-private_bundle") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) { + // FIX FIX + } + // Use this flag to set default behavior for deployement targets. + else if ( strcmp(arg, "-macosx_version_min") == 0 ) { + const char* macVers = argv[++i]; + if ( macVers == NULL ) + throw "-macosx_version_min missing version argument"; + const char* envMacVers = getenv("MACOSX_DEPLOYMENT_TARGET"); + const char* enviPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); + if ( (envMacVers != NULL) && (enviPhoneVers != NULL) ) { + // when conflicting deployments set, break tie by looking at syslibroot + warning("both MACOSX_DEPLOYMENT_TARGET and IPHONEOS_DEPLOYMENT_TARGET are set"); + if ( !fSDKPaths.empty() ) { + const char* sysrootPath = fSDKPaths.back(); + const char* lastSlash = strrchr(sysrootPath, '/'); + if ( strstr(lastSlash, "Simulator") != NULL ) + setIOSVersionMin(enviPhoneVers); + else + setMacOSXVersionMin(macVers); + } + else { + setMacOSXVersionMin(macVers); + } + } + else { + setMacOSXVersionMin(macVers); + } + } + else if ( (strcmp(arg, "-ios_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-ios_version_min missing version argument"; + setIOSVersionMin(vers); + } + else if ( strcmp(arg, "-ios_simulator_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-ios_simulator_version_min missing version argument"; + setIOSVersionMin(vers); + fTargetIOSSimulator = true; + } + else if ( strcmp(arg, "-watchos_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-watchos_version_min missing version argument"; + setWatchOSVersionMin(vers); + } + else if ( strcmp(arg, "-watchos_simulator_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-watchos_simulator_version_min missing version argument"; + setWatchOSVersionMin(vers); + fTargetIOSSimulator = true; + } + #if SUPPORT_APPLE_TV + else if ( strcmp(arg, "-tvos_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-tvos_version_min missing version argument"; + setIOSVersionMin(vers); + fPlatform = kPlatform_tvOS; + } + else if ( strcmp(arg, "-tvos_simulator_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-tvos_simulator_version_min missing version argument"; + setIOSVersionMin(vers); + fPlatform = kPlatform_tvOS; + fTargetIOSSimulator = true; + } + #endif + else if ( strcmp(arg, "-bridgeos_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-bridgeos_version_min missing version argument"; + setIOSVersionMin(vers); + fPlatform = kPlatform_bridgeOS; + } + else if ( strcmp(arg, "-multiply_defined") == 0 ) { + //warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) { + warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-nomultidefs") == 0 ) { + warnObsolete(arg); + } + // Display each file in which the argument symbol appears and whether + // the file defines or references it. This option takes an argument + // as -y note that there is no space. + else if ( strncmp(arg, "-y", 2) == 0 ) { + warnObsolete("-y"); + } + // Same output as -y, but output number of undefined symbols only. + else if ( strcmp(arg, "-Y") == 0 ) { + //warnObsolete(arg); + ++i; + } + // This option affects all objects linked into the final result. + else if ( strcmp(arg, "-m") == 0 ) { + warnObsolete(arg); + } + else if ( (strcmp(arg, "-why_load") == 0) || (strcmp(arg, "-whyload") == 0) ) { + fWhyLoad = true; + } + else if ( strcmp(arg, "-why_live") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-why_live missing symbol name argument"; + fWhyLive.insert(name); + } + else if ( strcmp(arg, "-u") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-u missing argument"; + fInitialUndefines.push_back(name); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-U") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-U missing argument"; + fAllowedUndefined.insert(name); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-s") == 0 ) { + warnObsolete(arg); + fLocalSymbolHandling = kLocalSymbolsNone; + fDebugInfoStripping = Options::kDebugInfoNone; + } + else if ( strcmp(arg, "-x") == 0 ) { + fLocalSymbolHandling = kLocalSymbolsNone; + } + else if ( strcmp(arg, "-S") == 0 ) { + fDebugInfoStripping = Options::kDebugInfoNone; + } + else if ( strcmp(arg, "-X") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-Si") == 0 ) { + warnObsolete(arg); + fDebugInfoStripping = Options::kDebugInfoFull; + } + else if ( strcmp(arg, "-b") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-Sn") == 0 ) { + warnObsolete(arg); + fDebugInfoStripping = Options::kDebugInfoFull; + } + else if ( strcmp(arg, "-Sp") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-dead_strip") == 0 ) { + fDeadStrip = true; + } + else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) { + fDeadStrip = true; + } + else if ( strcmp(arg, "-w") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-fatal_warnings") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) { + fErrorOnOtherArchFiles = true; + } + else if ( strcmp(arg, "-M") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-headerpad") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-headerpad missing argument"; + fMinimumHeaderPad = parseAddress(size); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { + // ignore -headerpad_max_install_names when compiling with bitcode + // rdar://problem/20748962 + if ( fBundleBitcode ) + warning("-headerpad_max_install_names is ignored when used with -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES)"); + else + fMaxMinimumHeaderPad = true; + } + else if ( strcmp(arg, "-t") == 0 ) { + fLogAllFiles = true; + } + else if ( strcmp(arg, "-whatsloaded") == 0 ) { + fLogObjectFiles = true; + } + else if ( strcmp(arg, "-A") == 0 ) { + warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-umbrella") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-umbrella missing argument"; + fUmbrellaName = name; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-allowable_client") == 0 ) { + const char* name = argv[++i]; + + if ( name == NULL ) + throw "-allowable_client missing argument"; + + fAllowableClients.push_back(name); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-client_name") == 0 ) { + const char* name = argv[++i]; + + if ( name == NULL ) + throw "-client_name missing argument"; + + fClientName = name; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-sub_umbrella") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-sub_umbrella missing argument"; + fSubUmbellas.push_back(name); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-sub_library") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-sub_library missing argument"; + fSubLibraries.push_back(name); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-init") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-init missing argument"; + fInitFunctionName = name; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-dot") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-dot missing argument"; + fDotOutputFile = name; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-warn_commons") == 0 ) { + fWarnCommons = true; + } + else if ( strcmp(arg, "-commons") == 0 ) { + fCommonsMode = parseCommonsTreatment(argv[++i]); + } + else if ( strcmp(arg, "-keep_relocs") == 0 ) { + fKeepRelocations = true; + } + else if ( strcmp(arg, "-warn_stabs") == 0 ) { + fWarnStabs = true; + } + else if ( strcmp(arg, "-pause") == 0 ) { + fPause = true; + } + else if ( strcmp(arg, "-print_statistics") == 0 ) { + fStatistics = true; + } + else if ( strcmp(arg, "-d") == 0 ) { + fMakeTentativeDefinitionsReal = true; + } + else if ( strcmp(arg, "-v") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-Z") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-syslibroot") == 0 ) { + snapshotArgCount = 0; + ++i; + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-bitcode_bundle") == 0 ) { + snapshotArgCount = 0; + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-no_uuid") == 0 ) { + fUUIDMode = kUUIDNone; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-random_uuid") == 0 ) { + fUUIDMode = kUUIDRandom; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-dtrace") == 0 ) { + snapshotFileArgIndex = 1; + const char* name = argv[++i]; + if ( name == NULL ) + throw "-dtrace missing argument"; + fDtraceScriptName = name; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-root_safe") == 0 ) { + fRootSafe = true; + } + else if ( strcmp(arg, "-setuid_safe") == 0 ) { + fSetuidSafe = true; + } + else if ( strcmp(arg, "-alias") == 0 ) { + Options::AliasPair pair; + pair.realName = argv[++i]; + if ( pair.realName == NULL ) + throw "missing argument to -alias"; + pair.alias = argv[++i]; + if ( pair.alias == NULL ) + throw "missing argument to -alias"; + fAliases.push_back(pair); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-alias_list") == 0 ) { + snapshotFileArgIndex = 1; + parseAliasFile(argv[++i]); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-save-temps") == 0 ) { + fSaveTempFiles = true; + } + else if ( strcmp(arg, "-bitcode_hide_symbols") == 0 ) { + fHideSymbols = true; + } + else if ( strcmp(arg, "-bitcode_verify") == 0 ) { + fVerifyBitcode = true; + } + else if ( strcmp(arg, "-bitcode_symbol_map") == 0) { + fReverseMapPath = argv[++i]; + if ( fReverseMapPath == NULL ) + throw "missing argument to -bitcode_symbol_map"; + struct stat statbuf; + int ret = ::stat(fReverseMapPath, &statbuf); + if ( ret == 0 && S_ISDIR(statbuf.st_mode)) { + char tempPath[PATH_MAX]; + sprintf(tempPath, "%s/XXXXXX", fReverseMapPath); + int tempFile = ::mkstemp(tempPath); + if (tempFile == -1) + throwf("could not write file to symbol map directory: %s", fReverseMapPath); + ::close(tempFile); + fReverseMapTempPath = std::string(tempPath); + fReverseMapUUIDRename = true; + } else + fReverseMapTempPath = std::string(fReverseMapPath); + } + else if ( strcmp(argv[i], "-flto-codegen-only") == 0) { + fLTOCodegenOnly = true; + } + else if ( strcmp(argv[i], "-ignore_auto_link") == 0) { + fIgnoreAutoLink = true; + } + else if ( strcmp(argv[i], "-allow_dead_duplicates") == 0) { + fAllowDeadDups = true; + } + else if ( strcmp(argv[i], "-bitcode_process_mode") == 0 ) { + const char* bitcode_type = argv[++i]; + if ( bitcode_type == NULL ) + throw "missing argument to -bitcode_process_mode"; + else if ( strcmp(bitcode_type, "strip") == 0 ) + fBitcodeKind = kBitcodeStrip; + else if ( strcmp(bitcode_type, "marker") == 0 ) + fBitcodeKind = kBitcodeMarker; + else if ( strcmp(bitcode_type, "data") == 0 ) + fBitcodeKind = kBitcodeAsData; + else if ( strcmp(bitcode_type, "bitcode") == 0 ) + fBitcodeKind = kBitcodeProcess; + else + throw "unknown argument to -bitcode_process_mode {strip,marker,data,bitcode}"; + } + else if ( strcmp(arg, "-rpath") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "missing argument to -rpath"; + fRPaths.push_back(path); + } + else if ( strcmp(arg, "-read_only_stubs") == 0 ) { + fReadOnlyx86Stubs = true; + } + else if ( strcmp(arg, "-slow_stubs") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-map") == 0 ) { + fMapPath = argv[++i]; + if ( fMapPath == NULL ) + throw "missing argument to -map"; + } + else if ( strcmp(arg, "-pie") == 0 ) { + fPositionIndependentExecutable = true; + fPIEOnCommandLine = true; + } + else if ( strcmp(arg, "-no_pie") == 0 ) { + fDisablePositionIndependentExecutable = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strncmp(arg, "-reexport-l", 11) == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findLibrary(&arg[11], true); + info.options.fReExport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-reexport_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findFile(argv[++i]); + info.options.fReExport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-reexport_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findFramework(argv[++i]); + info.options.fReExport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + cannotBeUsedWithBitcode(arg); + } + else if ( strncmp(arg, "-upward-l", 9) == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findLibrary(&arg[9], true); + info.options.fUpward = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-upward_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findFile(argv[++i]); + info.options.fUpward = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-upward_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; + FileInfo info = findFramework(argv[++i]); + info.options.fUpward = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) { + fDeadStripDylibs = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) { + fImplicitlyLinkPublicDylibs = false; + } + else if ( strcmp(arg, "-new_linker") == 0 ) { + // ignore + } + else if ( strcmp(arg, "-no_encryption") == 0 ) { + fEncryptableForceOff = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-encryptable") == 0 ) { + fEncryptableForceOn = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-compact_unwind") == 0 ) { + fAddCompactUnwindEncoding = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_compact_unwind") == 0 ) { + fAddCompactUnwindEncoding = false; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-mllvm") == 0 ) { + const char* opts = argv[++i]; + if ( opts == NULL ) + throw "missing argument to -mllvm"; + fLLVMOptions.push_back(opts); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-mcpu") == 0 ) { + const char* cpu = argv[++i]; + if ( cpu == NULL ) + throw "missing argument to -mcpu"; + fLtoCpu = cpu; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_order_inits") == 0 ) { + fAutoOrderInitializers = false; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_order_data") == 0 ) { + fOrderData = false; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-seg_page_size") == 0 ) { + SegmentSize seg; + seg.name = argv[++i]; + if ( (seg.name == NULL) || (argv[i+1] == NULL) ) + throw "-seg_page_size missing segName Adddress"; + seg.size = parseAddress(argv[++i]); + uint64_t temp = seg.size & (-4096); // page align + if ( (seg.size != temp) ) + warning("-seg_page_size %s not 4K aligned, rounding down", seg.name); + fCustomSegmentSizes.push_back(seg); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-mark_dead_strippable_dylib") == 0 ) { + fMarkDeadStrippableDylib = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-exported_symbols_order") == 0 ) { + snapshotFileArgIndex = 1; + loadSymbolOrderFile(argv[++i], fExportSymbolsOrder); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { + warnObsolete("-no_compact_linkedit"); + } + else if ( strcmp(arg, "-no_eh_labels") == 0 ) { + fNoEHLabels = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-warn_compact_unwind") == 0 ) { + fWarnCompactUnwind = true; + } + else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) { + fAllowCpuSubtypeMismatches = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) { + fOptimizeZeroFill = false; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-merge_zero_fill_sections") == 0 ) { + fMergeZeroFill = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-objc_abi_version") == 0 ) { + const char* version = argv[++i]; + if ( version == NULL ) + throw "-objc_abi_version missing version number"; + if ( strcmp(version, "2") == 0 ) { + fObjCABIVersion1Override = false; + fObjCABIVersion2Override = true; + } + else if ( strcmp(version, "1") == 0 ) { + fObjCABIVersion1Override = true; + fObjCABIVersion2Override = false; + } + else + warning("ignoring unrecognized argument (%s) to -objc_abi_version", version); + } + else if ( strcmp(arg, "-warn_weak_exports") == 0 ) { + fWarnWeakExports = true; + } + else if ( strcmp(arg, "-no_weak_exports") == 0 ) { + fNoWeakExports = true; + } + else if ( strcmp(arg, "-objc_gc_compaction") == 0 ) { + fObjcGcCompaction = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-objc_gc") == 0 ) { + fObjCGc = true; + if ( fObjCGcOnly ) { + warning("-objc_gc overriding -objc_gc_only"); + fObjCGcOnly = false; + } + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-objc_gc_only") == 0 ) { + fObjCGcOnly = true; + if ( fObjCGc ) { + warning("-objc_gc_only overriding -objc_gc"); + fObjCGc = false; + } + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-demangle") == 0 ) { + fDemangle = true; + } + else if ( strcmp(arg, "-version_load_command") == 0 ) { + fVersionLoadCommandForcedOn = true; + fVersionLoadCommandForcedOff = false; + } + else if ( strcmp(arg, "-no_version_load_command") == 0 ) { + fVersionLoadCommandForcedOff = true; + fVersionLoadCommandForcedOn = false; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-function_starts") == 0 ) { + if (fMacVersionMin >= ld::mac10_6) { + fFunctionStartsForcedOn = true; + fFunctionStartsForcedOff = false; + } else { + warning("-function_starts ignored for OS X < 10.6"); + } + } + else if ( strcmp(arg, "-no_function_starts") == 0 ) { + fFunctionStartsForcedOff = true; + fFunctionStartsForcedOn = false; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_data_in_code_info") == 0 ) { + fDataInCodeInfoLoadCommandForcedOff = true; + fDataInCodeInfoLoadCommandForcedOn = false; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-data_in_code_info") == 0 ) { + fDataInCodeInfoLoadCommandForcedOn = true; + fDataInCodeInfoLoadCommandForcedOff = false; + } +#ifdef LTO_SUPPORT + else if ( strcmp(arg, "-object_path_lto") == 0 ) { + fTempLtoObjectPath = argv[++i]; + if ( fTempLtoObjectPath == NULL ) + throw "missing argument to -object_path_lto"; + } +#endif + else if ( strcmp(arg, "-no_objc_category_merging") == 0 ) { + fObjcCategoryMerging = false; + } + else if ( strcmp(arg, "-force_symbols_weak_list") == 0 ) { + snapshotFileArgIndex = 1; + loadExportFile(argv[++i], "-force_symbols_weak_list", fForceWeakSymbols); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-force_symbols_not_weak_list") == 0 ) { + snapshotFileArgIndex = 1; + loadExportFile(argv[++i], "-force_symbols_not_weak_list", fForceNotWeakSymbols); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-force_symbol_weak") == 0 ) { + const char* symbol = argv[++i]; + if ( symbol == NULL ) + throw "-force_symbol_weak missing "; + fForceWeakSymbols.insert(symbol); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-force_symbol_not_weak") == 0 ) { + const char* symbol = argv[++i]; + if ( symbol == NULL ) + throw "-force_symbol_not_weak missing "; + fForceNotWeakSymbols.insert(symbol); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-reexported_symbols_list") == 0 ) { + snapshotFileArgIndex = 1; + if ( fExportMode == kExportSome ) + throw "can't use -exported_symbols_list and -reexported_symbols_list"; + loadExportFile(argv[++i], "-reexported_symbols_list", fReExportSymbols); + } + else if ( strcmp(arg, "-dyld_env") == 0 ) { + const char* envarg = argv[++i]; + if ( envarg == NULL ) + throw "-dyld_env missing ENV=VALUE"; + if ( strchr(envarg, '=') == NULL ) + throw "-dyld_env missing ENV=VALUE"; + fDyldEnvironExtras.push_back(envarg); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-page_align_data_atoms") == 0 ) { + fPageAlignDataAtoms = true; + cannotBeUsedWithBitcode(arg); + } + else if (strcmp(arg, "-debug_snapshot") == 0) { + fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + fSnapshotRequested = true; + cannotBeUsedWithBitcode(arg); + } + else if (strcmp(arg, "-snapshot_dir") == 0) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-snapshot_dir missing path"; + fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + fLinkSnapshot.setSnapshotPath(path); + fSnapshotRequested = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-new_main") == 0 ) { + fEntryPointLoadCommandForceOn = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_new_main") == 0 ) { + fEntryPointLoadCommandForceOff = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-source_version") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-source_version missing "; + fSourceVersion = parseVersionNumber64(vers); + } + else if ( strcmp(arg, "-add_source_version") == 0 ) { + fSourceVersionLoadCommandForceOn = true; + } + else if ( strcmp(arg, "-no_source_version") == 0 ) { + fSourceVersionLoadCommandForceOff = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-sdk_version") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-sdk_version missing "; + fSDKVersion = parseVersionNumber32(vers); + } + else if ( strcmp(arg, "-dependent_dr_info") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-no_dependent_dr_info") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-kexts_use_stubs") == 0 ) { + fKextsUseStubs = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(argv[i], "-dependency_info") == 0 ) { + snapshotArgCount = 0; + ++i; + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-export_dynamic") == 0 ) { + fExportDynamic = true; + } + else if ( strcmp(arg, "-force_symbols_coalesce_list") == 0 ) { + snapshotFileArgIndex = 1; + loadExportFile(argv[++i], "-force_symbols_coalesce_list", fForceCoalesceSymbols); + } + else if ( strcmp(arg, "-add_linker_option") == 0 ) { + // ex: -add_linker_option '-framework Foundation' + const char* optString = argv[++i]; + if ( optString == NULL ) + throw "-add_linker_option missing