]> git.saurik.com Git - apple/ld64.git/commitdiff
ld64-264.3.101.tar.gz developer-tools-73 v264.3.101
authorApple <opensource@apple.com>
Fri, 4 Mar 2016 22:18:29 +0000 (22:18 +0000)
committerApple <opensource@apple.com>
Fri, 4 Mar 2016 22:18:29 +0000 (22:18 +0000)
43 files changed:
doc/man/man1/ld.1
ld64.xcodeproj/project.pbxproj
src/abstraction/MachOTrie.hpp
src/ld/InputFiles.cpp
src/ld/InputFiles.h
src/ld/LinkEditClassic.hpp
src/ld/Options.cpp
src/ld/Options.h
src/ld/OutputFile.cpp
src/ld/OutputFile.h
src/ld/Resolver.cpp
src/ld/SymbolTable.cpp
src/ld/SymbolTable.h
src/ld/ld.cpp
src/ld/ld.hpp
src/ld/parsers/generic_dylib_file.hpp [new file with mode: 0644]
src/ld/parsers/lto_file.cpp
src/ld/parsers/lto_file.h
src/ld/parsers/macho_dylib_file.cpp
src/ld/parsers/macho_dylib_file.h
src/ld/parsers/macho_relocatable_file.cpp
src/ld/parsers/macho_relocatable_file.h
src/ld/parsers/textstub_dylib_file.cpp
src/ld/passes/bitcode_bundle.cpp
src/ld/passes/branch_island.cpp
src/ld/passes/branch_shim.cpp
src/ld/passes/code_dedup.cpp [new file with mode: 0644]
src/ld/passes/code_dedup.h [new file with mode: 0644]
src/ld/passes/compact_unwind.cpp
src/ld/passes/objc.cpp
src/ld/passes/stubs/stubs.cpp
src/other/machochecker.cpp
src/other/unwinddump.cpp
unit-tests/run-all-unit-tests
unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdrField.s [new file with mode: 0644]
unit-tests/test-cases/linker-optimization-hints/Makefile
unit-tests/test-cases/linker_options-library-chain/Makefile [new file with mode: 0644]
unit-tests/test-cases/linker_options-library-chain/bar.c [new file with mode: 0644]
unit-tests/test-cases/linker_options-library-chain/foo.c [new file with mode: 0644]
unit-tests/test-cases/linker_options-library-chain/main.c [new file with mode: 0644]
unit-tests/test-cases/linker_options-library-chain/subbar.c [new file with mode: 0644]
unit-tests/test-cases/objc-category-optimize-load/Makefile
unit-tests/test-cases/objc-category-optimize/Makefile

index 1c118b8c8592736607a7df2eba37d1bde6843930..53fbaa5e777ac142545649319ed6d55149e7f811 100644 (file)
@@ -362,7 +362,7 @@ The argument
 .Ar size
 is a hexadecimal number with an optional leading 0x. The 
 .Ar size
-should be an even multiple of 4KB, that is the last three hexadecimal digits should be zero.
+should be a multiple of the architecture's page size (4KB or 16KB).
 .It Fl allow_stack_execute 
 Marks executable so that all stacks in the task will be given stack execution privilege. This includes pthread stacks.
 .It Fl export_dynamic
@@ -541,10 +541,21 @@ Otherwise, the reverse map will be written to a file at
 .Bl -tag
 .It Fl v
 Prints the version of the linker.
+.It Fl no_deduplicate
+Don't run deduplication pass in linker
+.It Fl verbose_deduplicate
+Prints names of functions that are eliminated by deduplication and total code savings size.
 .It Fl dirty_data_list Ar filename
 Specifies a file containing the names of data symbols likely to be dirtied.
 If the linker is creating a __DATA_DIRTY segment, those symbols will be moved
 to that segment.
+.It Fl max_default_common_align Ar value
+Any common symbols (aka tentative definitions, or uninitialized (zeroed) variables) that have no explicit alignment
+are normally aligned to their next power of two size (e.g. a 240 byte array is 256 aligned).
+This option lets you reduce the max alignment.  For instance, a value of 0x40 would reduce
+the alignment for a 240 byte array to 64 bytes (instead of 256). The value specified must be a hexadecimal power of two
+If -max_default_common_align is not used, the default alignment is already
+limited to 0x8 (2^3) bytes for -preload and 0x8000 (2^15) for all other output types.
 .It Fl move_to_rw_segment Ar segment_name Ar filename
 Moves data symbols to another segment.  The command line option specifies the
 target segment name and a path to a file containing a list of symbols to move.
@@ -760,6 +771,10 @@ option is used, the temporary file will be stored at the specified path and rema
 is complete.  Without the option, the linker picks a path and deletes the object file before the linker 
 tool completes, thus tools such as the debugger or dsymutil will not be able to access the DWARF debug
 info in the temporary object file.
+.It Fl lto_library Ar path
+When performing Link Time Optimization (LTO), the linker normally loads libLTO.dylib relative to the linker
+binary (../lib/libLTO.dylib). This option allows the user to specify the path to a specific libLTO.dylib
+to load instead.
 .It Fl page_align_data_atoms
 During development, this option can be used to space out all global variables so each is on a separate page. 
 This is useful when analyzing dirty and resident pages.  The information can then be used to create an 
index 1c5e6228ce5db63d2368623e657cc5f0685f9ba4..c47b806190262d8b4d9419c286ccbb7d613d4b0a 100644 (file)
@@ -28,7 +28,6 @@
                        isa = PBXAggregateTarget;
                        buildConfigurationList = F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */;
                        buildPhases = (
-                               F9871A3413340B4600DB3F24 /* Platform install */,
                        );
                        dependencies = (
                                F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */,
@@ -36,6 +35,7 @@
                                F9C12EEA0ED65765005BC69D /* PBXTargetDependency */,
                                F9B8135D0EC2620E00F94C13 /* PBXTargetDependency */,
                                F9A3DE160ED76D9A00C590B9 /* PBXTargetDependency */,
+                               F9FF3BDD1C586D7C0015D843 /* PBXTargetDependency */,
                        );
                        name = all;
                        productName = all;
@@ -83,6 +83,7 @@
                F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; };
                F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; };
                F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EC78050A2F8674002A3E39 /* rebase.cpp */; };
+               F9FC510A1BC893C400FEC3F8 /* code_dedup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9FC51081BC8915A00FEC3F8 /* code_dedup.cpp */; };
                FA95D6141AB25CF400395811 /* textstub_dylib_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */; };
 /* End PBXBuildFile section */
 
                        remoteGlobalIDString = F9BA51600ECE58BE00D1D62E;
                        remoteInfo = dyldinfo;
                };
+               F9FF3BDC1C586D7C0015D843 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = F9023C3006D5A227001BBF46 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = F9EA72CA097454A6008B4F1D;
+                       remoteInfo = machocheck;
+               };
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
                F97F5025070D0B6300B9FCD7 /* copy man page */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
-                       dstPath = /usr/share/man/man1;
+                       dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1";
                        dstSubfolderSpec = 0;
                        files = (
                                F97F5029070D0BB200B9FCD7 /* ld.1 in copy man page */,
                F9A3DE140ED76D7700C590B9 /* CopyFiles */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
-                       dstPath = "/usr/local/include/mach-o";
+                       dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/include/mach-o";
                        dstSubfolderSpec = 0;
                        files = (
                                F9A3DE1E0ED7738300C590B9 /* prune_trie.h in CopyFiles */,
                F9B1A25E0A3A44CB00DA8FAB /* install man page */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
-                       dstPath = /usr/share/man/man1;
+                       dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1";
                        dstSubfolderSpec = 0;
                        files = (
                                F9B1A2640A3A563E00DA8FAB /* rebase.1 in install man page */,
                F9B813870EC2659600F94C13 /* install man page */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
-                       dstPath = usr/share/man/man1;
+                       dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1";
                        dstSubfolderSpec = 0;
                        files = (
                                F9B813850EC2657800F94C13 /* unwinddump.1 in install man page */,
                F9C12EA50ED63E05005BC69D /* install man page */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
-                       dstPath = usr/share/man/man1;
+                       dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1";
                        dstSubfolderSpec = 0;
                        files = (
                                F9C12EA30ED63DE7005BC69D /* dyldinfo.1 in install man page */,
                F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/ld/debugline.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
                F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; };
                F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/other/rebase.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
+               F9FC51081BC8915A00FEC3F8 /* code_dedup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = code_dedup.cpp; sourceTree = "<group>"; };
+               F9FC51091BC8915A00FEC3F8 /* code_dedup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = code_dedup.h; sourceTree = "<group>"; };
+               FA4843BE1B7279ED001C8025 /* generic_dylib_file.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = generic_dylib_file.hpp; sourceTree = "<group>"; };
                FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = textstub_dylib_file.cpp; sourceTree = "<group>"; usesTabs = 1; };
                FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = textstub_dylib_file.hpp; sourceTree = "<group>"; };
 /* End PBXFileReference section */
                F9AA650B1051BD2B003E3539 /* passes */ = {
                        isa = PBXGroup;
                        children = (
+                               F9FC51081BC8915A00FEC3F8 /* code_dedup.cpp */,
+                               F9FC51091BC8915A00FEC3F8 /* code_dedup.h */,
                                B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */,
                                B028FCF01A9E7B4A00E3584B /* bitcode_bundle.h */,
                                F984A38010BB4B0D009E9878 /* branch_island.cpp */,
                F9AA65861051E750003E3539 /* parsers */ = {
                        isa = PBXGroup;
                        children = (
-                               FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */,
-                               FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */,
                                F91B7B0218987D5F0099486F /* libunwind */,
                                F9AA6784105700C2003E3539 /* opaque_section_file.cpp */,
                                F9AA6785105700C2003E3539 /* opaque_section_file.h */,
                                F9AA65D81051EC4A003E3539 /* archive_file.h */,
                                F9AA65D91051EC4A003E3539 /* lto_file.cpp */,
                                F9AA65DA1051EC4A003E3539 /* lto_file.h */,
+                               FA4843BE1B7279ED001C8025 /* generic_dylib_file.hpp */,
                                F9AA65DB1051EC4A003E3539 /* macho_dylib_file.cpp */,
                                F9AA65DC1051EC4A003E3539 /* macho_dylib_file.h */,
+                               FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */,
+                               FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */,
                                F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */,
                                F9AA65881051E750003E3539 /* macho_relocatable_file.h */,
                        );
                        shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# make linker relative libLTO.dylib\nmkdir -p ${BUILD_DIR}/lib\nln -sf /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib ${BUILD_DIR}/lib/libLTO.dylib\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0";
                        showEnvVarsInLog = 0;
                };
-               F9871A3413340B4600DB3F24 /* Platform install */ = {
-                       isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 8;
-                       files = (
-                       );
-                       inputPaths = (
-                       );
-                       name = "Platform install";
-                       outputPaths = (
-                       );
-                       runOnlyForDeploymentPostprocessing = 1;
-                       shellPath = /bin/sh;
-                       shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p  \"${DSTROOT}/${DT_VARIANT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_VARIANT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p  ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\telse\n\t\tmkdir -p  ${DSTROOT}/Developer/usr/bin\n\t\tcp ${DSTROOT}/usr/bin/ld  ${DSTROOT}/Developer/usr/bin\n\tfi\nfi\n\n";
-                       showEnvVarsInLog = 0;
-               };
                F9CCF765144CE244007CB524 /* make configure.h */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 2147483647;
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               F9FC510A1BC893C400FEC3F8 /* code_dedup.cpp in Sources */,
                                FA95D6141AB25CF400395811 /* textstub_dylib_file.cpp in Sources */,
                                F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */,
                                F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */,
                        target = F9BA51600ECE58BE00D1D62E /* dyldinfo */;
                        targetProxy = F9F9AD67116D58AF0028EFAB /* PBXContainerItemProxy */;
                };
+               F9FF3BDD1C586D7C0015D843 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = F9EA72CA097454A6008B4F1D /* machocheck */;
+                       targetProxy = F9FF3BDC1C586D7C0015D843 /* PBXContainerItemProxy */;
+               };
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                GCC_DYNAMIC_NO_PIC = YES;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
                                GCC_MODEL_TUNING = G5;
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
+                               GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_TREAT_WARNINGS_AS_ERRORS = YES;
                                GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
                                GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
                                        "$(DEVELOPER_DIR)/usr/local/include",
                                        "$(DEVELOPER_DIR)/usr/include",
                                );
-                               INSTALL_PATH = /usr/bin;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
                                LINKER_DISPLAYS_MANGLED_NAMES = NO;
                                MACOSX_DEPLOYMENT_TARGET = "";
                                );
                                PREBINDING = NO;
                                PRODUCT_NAME = ld;
+                               SDKROOT = macosx.internal;
                                SECTORDER_FLAGS = "";
                                VERSIONING_SYSTEM = "apple-generic";
                                WARNING_CFLAGS = "-Wall";
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                GCC_DYNAMIC_NO_PIC = YES;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
                                GCC_OPTIMIZATION_LEVEL = s;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1)",
                                        "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))",
                                );
                                GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1 = "LD_VERS='\"ld64-$(RC_ProjectSourceVersion)\"'";
+                               GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_TREAT_WARNINGS_AS_ERRORS = NO;
                                GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
                                GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
                                        "$(DEVELOPER_DIR)/usr/local/include",
                                        "$(DEVELOPER_DIR)/usr/include",
                                );
-                               INSTALL_PATH = /usr/bin;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                );
                                PREBINDING = NO;
                                PRODUCT_NAME = ld;
+                               SDKROOT = macosx.internal;
                                SECTORDER_FLAGS = "";
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                        "$(DEVELOPER_DIR)/usr/local/include",
                                        "$(DT_TOOLCHAIN_DIR)/usr/local/include",
                                );
-                               INSTALL_PATH = "$(HOME)/bin";
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
                                ONLY_ACTIVE_ARCH = NO;
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                OTHER_REZFLAGS = "";
                                PREBINDING = NO;
                                PRODUCT_NAME = ObjectDump;
+                               SDKROOT = macosx.internal;
                                SECTORDER_FLAGS = "";
                                WARNING_CFLAGS = (
                                        "-Wmost",
                                        "$(DEVELOPER_DIR)/usr/local/include",
                                        "$(DT_TOOLCHAIN_DIR)/usr/local/include",
                                );
-                               INSTALL_PATH = "$(HOME)/bin";
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                OTHER_REZFLAGS = "";
                                PREBINDING = NO;
                                PRODUCT_NAME = ObjectDump;
+                               SDKROOT = macosx.internal;
                                WARNING_CFLAGS = (
                                        "-Wmost",
                                        "-Wno-four-char-constants",
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                GCC_DYNAMIC_NO_PIC = YES;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
                                GCC_OPTIMIZATION_LEVEL = s;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1)",
                                        "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))",
                                );
                                GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1 = "LD_VERS='\"ld64-$(RC_ProjectSourceVersion)\"'";
+                               GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_TREAT_WARNINGS_AS_ERRORS = NO;
                                GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
                                GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
                                        "$(DEVELOPER_DIR)/usr/local/include",
                                        "$(DEVELOPER_DIR)/usr/include",
                                );
-                               INSTALL_PATH = /usr/bin;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                );
                                PREBINDING = NO;
                                PRODUCT_NAME = ld;
+                               SDKROOT = macosx.internal;
                                SECTORDER_FLAGS = "";
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                GCC_MODEL_TUNING = G5;
                                GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))";
                                HEADER_SEARCH_PATHS = "";
-                               INSTALL_PATH = /usr/bin;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header";
                                PREBINDING = NO;
                                PRODUCT_NAME = rebase;
+                               SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                VALID_ARCHS = "i386 ppc x86_64";
                                GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                HEADER_SEARCH_PATHS = "";
-                               INSTALL_PATH = /usr/bin;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                );
                                PREBINDING = NO;
                                PRODUCT_NAME = unwinddump;
+                               SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                        };
                                        "$(DEVELOPER_DIR)/usr/local/include",
                                        "$(DT_TOOLCHAIN_DIR)/usr/local/include",
                                );
-                               INSTALL_PATH = "$(HOME)/bin";
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                OTHER_REZFLAGS = "";
                                PREBINDING = NO;
                                PRODUCT_NAME = ObjectDump;
+                               SDKROOT = macosx.internal;
                                WARNING_CFLAGS = (
                                        "-Wmost",
                                        "-Wno-four-char-constants",
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_MODEL_TUNING = G5;
                                HEADER_SEARCH_PATHS = "";
-                               INSTALL_PATH = "$(HOME)/bin";
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PREBINDING = NO;
                                PRODUCT_NAME = machocheck;
+                               SDKROOT = macosx.internal;
                        };
                        name = "Release-assert";
                };
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_MODEL_TUNING = G5;
-                               INSTALL_PATH = /usr/bin;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PREBINDING = NO;
                                PRODUCT_NAME = dyldinfo;
+                               SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                ZERO_LINK = NO;
                                GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_MODEL_TUNING = G5;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
-                               INSTALL_PATH = /usr/local/lib;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/lib";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
-                               INSTALL_PATH = /usr/local/lib;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/lib";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_MODEL_TUNING = G5;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
-                               INSTALL_PATH = /usr/local/lib;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/lib";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
-                               INSTALL_PATH = "$(HOME)/bin";
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PREBINDING = NO;
                                PRODUCT_NAME = unwinddump;
+                               SDKROOT = macosx.internal;
                        };
                        name = Debug;
                };
                                GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                HEADER_SEARCH_PATHS = "";
-                               INSTALL_PATH = /usr/bin;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header";
                                PREBINDING = NO;
                                PRODUCT_NAME = unwinddump;
+                               SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                        };
                                GCC_WARN_UNUSED_LABEL = NO;
                                GCC_WARN_UNUSED_VALUE = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
-                               INSTALL_PATH = /usr/local/bin;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PREBINDING = NO;
                                PRODUCT_NAME = dyldinfo;
+                               SDKROOT = macosx.internal;
                                WARNING_CFLAGS = "-Wall";
                        };
                        name = Debug;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_MODEL_TUNING = G5;
-                               INSTALL_PATH = /usr/bin;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PREBINDING = NO;
                                PRODUCT_NAME = dyldinfo;
+                               SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                ZERO_LINK = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_MODEL_TUNING = G5;
                                GCC_OPTIMIZATION_LEVEL = 0;
-                               INSTALL_PATH = "$(HOME)/bin";
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PREBINDING = NO;
                                PRODUCT_NAME = machocheck;
+                               SDKROOT = macosx.internal;
                        };
                        name = Debug;
                };
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_MODEL_TUNING = G5;
                                HEADER_SEARCH_PATHS = "";
-                               INSTALL_PATH = "$(HOME)/bin";
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PREBINDING = NO;
                                PRODUCT_NAME = machocheck;
+                               SDKROOT = macosx.internal;
                        };
                        name = Release;
                };
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_MODEL_TUNING = G5;
                                GCC_OPTIMIZATION_LEVEL = 0;
-                               INSTALL_PATH = "$(HOME)/bin";
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                PREBINDING = NO;
                                PRODUCT_NAME = rebase;
+                               SDKROOT = macosx.internal;
                        };
                        name = Debug;
                };
                                GCC_MODEL_TUNING = G5;
                                GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))";
                                HEADER_SEARCH_PATHS = "";
-                               INSTALL_PATH = /usr/bin;
+                               INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
                                OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header";
                                PREBINDING = NO;
                                PRODUCT_NAME = rebase;
+                               SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                VALID_ARCHS = "i386 ppc x86_64";
index b7c55c1d3d71150a68f9216852743a1ca6433db2..575a6545a6ba20d80ca8fa9fc11266671680179a 100644 (file)
@@ -351,6 +351,8 @@ static inline void processExportNode(const uint8_t* const start, const uint8_t*
                }
                output.push_back(e);
        }
+       if ( children > end )
+               throw "malformed trie, terminalSize extends beyond trie data";
        const uint8_t childrenCount = *children++;
        const uint8_t* s = children;
        for (uint8_t i=0; i < childrenCount; ++i) {
@@ -383,7 +385,7 @@ inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector<Entr
        output.reserve(entries.size());
        for (std::vector<EntryWithOffset>::iterator it=entries.begin(); it != entries.end(); ++it)
                output.push_back(it->entry);
-       delete cummulativeString;
+       delete [] cummulativeString;
 }
 
 
index 839c5bc1f3e7bc90d80a92de028cd77a0fe141af..ab31d4acab07ffe72a8e69cfde6a37cbf064fffc 100644 (file)
@@ -303,6 +303,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib
        objOpts.srcKind                         = ld::relocatable::File::kSourceObj;
        objOpts.treateBitcodeAsData     = _options.bitcodeKind() == Options::kBitcodeAsData;
        objOpts.usingBitcode            = _options.bundleBitcode();
+       objOpts.maxDefaultCommonAlignment = _options.maxDefaultCommonAlign();
 
        ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts);
        if ( objResult != NULL ) {
@@ -318,7 +319,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib
                OSAtomicIncrement32(&_totalObjectLoaded);
                return objResult;
        }
-       
+
        // see if it is a dynamic library (or text-based dynamic library)
        ld::dylib::File* dylibResult;
        bool dylibsNotAllowed = false;
@@ -582,12 +583,37 @@ void InputFiles::markExplicitlyLinkedDylibs()
        }
 }
 
-bool InputFiles::libraryAlreadyLoaded(const char* path) 
+bool InputFiles::frameworkAlreadyLoaded(const char* path, const char* frameworkName)
+{
+       for (ld::File* file : _inputFiles) {
+               if ( strcmp(path, file->path()) == 0 )
+                       return true;
+       }
+       for (ld::dylib::File* dylibx : _allDylibs) {
+               const char* fname = dylibx->frameworkName();
+               if ( fname == NULL )
+                       continue;
+               if ( strcmp(frameworkName, fname) == 0 )
+                       return true;
+       }
+       return false;
+}
+
+bool InputFiles::libraryAlreadyLoaded(const char* path)
 {
-       for (std::vector<ld::File*>::const_iterator it = _inputFiles.begin(); it != _inputFiles.end(); ++it) {
-               if ( strcmp(path, (*it)->path()) == 0 )
+       for (ld::File* file : _inputFiles) {
+               if ( strcmp(path, file->path()) == 0 )
+                       return true;
+       }
+       for (ld::dylib::File* dylib : _allDylibs) {
+               if ( strcmp(path, dylib->path()) == 0 )
                        return true;
        }
+       for (const LibraryInfo& libInfo : _searchLibraries) {
+               if ( strcmp(path, libInfo.archive()->path()) == 0 )
+                       return true;
+       }
+
        return false;
 }
 
@@ -600,8 +626,10 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan
        // process frameworks specified in .o linker options
        for (CStringSet::const_iterator it = state.linkerOptionFrameworks.begin(); it != state.linkerOptionFrameworks.end(); ++it) {
                const char* frameworkName = *it;
+               if ( state.linkerOptionFrameworksProcessed.count(frameworkName) )
+                       continue;
                Options::FileInfo info = _options.findFramework(frameworkName);
-               if ( ! this->libraryAlreadyLoaded(info.path) ) {
+               if ( ! this->frameworkAlreadyLoaded(info.path, frameworkName) ) {
                        info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal();
                        try {
                                ld::File* reader = this->makeFile(info, true);
@@ -621,10 +649,13 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan
                                warning("Auto-Linking supplied '%s', %s", info.path, msg);
                        }
                }
+               state.linkerOptionFrameworksProcessed.insert(frameworkName);
        }
        // process libraries specified in .o linker options
        for (CStringSet::const_iterator it = state.linkerOptionLibraries.begin(); it != state.linkerOptionLibraries.end(); ++it) {
                const char* libName = *it;
+               if ( state.linkerOptionLibrariesProcessed.count(libName) )
+                       continue;
                Options::FileInfo info = _options.findLibrary(libName);
                if ( ! this->libraryAlreadyLoaded(info.path) ) {
                        info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal();
@@ -656,6 +687,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan
                                warning("Auto-Linking supplied '%s', %s", info.path, msg);
                        }
                }
+               state.linkerOptionLibrariesProcessed.insert(libName);
        }
 }
 
@@ -1407,7 +1439,7 @@ void InputFiles::dylibs(ld::Internal& state)
        //fprintf(stderr, "all dylibs:\n");
        //for(std::vector<ld::dylib::File*>::iterator it=state.dylibs.begin(); it != state.dylibs.end(); ++it) {
        //      const ld::dylib::File* dylib = *it;
-       //      fprintf(stderr, "    %p %s\n", dylib, dylib->path());
+       //      fprintf(stderr, "    %p impl=%d %s\n", dylib, dylib->implicitlyLinked(), dylib->path());
        //}
        
        // and -bundle_loader
index 608ce39f41f20349a0ec035a1a2eea7a26851219..e9927cd689346935098e5442ce0153add64bef21 100644 (file)
@@ -97,7 +97,8 @@ private:
        void                                            checkDylibClientRestrictions(ld::dylib::File*);
        void                                            createOpaqueFileSections();
        bool                                            libraryAlreadyLoaded(const char* path);
-       
+       bool                                            frameworkAlreadyLoaded(const char* path, const char* frameworkName);
+
        // for pipelined linking
     void                                               waitForInputFiles();
        static void                                     waitForInputFiles(InputFiles *inputFiles);
@@ -144,9 +145,9 @@ private:
         LibraryInfo(ld::dylib::File* dylib) : _lib(dylib), _isDylib(true) {};
         LibraryInfo(ld::archive::File* dylib) : _lib(dylib), _isDylib(false) {};
 
-        bool isDylib() { return _isDylib; }
-        ld::dylib::File *dylib() { return (ld::dylib::File*)_lib; }
-        ld::archive::File *archive() { return (ld::archive::File*)_lib; }
+        bool isDylib() const { return _isDylib; }
+        ld::dylib::File *dylib() const { return (ld::dylib::File*)_lib; }
+        ld::archive::File *archive() const { return (ld::archive::File*)_lib; }
     };
     std::vector<LibraryInfo>  _searchLibraries;
 };
index ee1c2078ef81e07d6c45680c9d73e1386667e3be..2eab13bfe85aa54fa8c1f3877c3aa12c0d3fbda5 100644 (file)
@@ -2002,7 +2002,6 @@ private:
        uint32_t                                                                        symIndexOfLazyPointerAtom(const ld::Atom*);
        uint32_t                                                                        symIndexOfNonLazyPointerAtom(const ld::Atom*);
        uint32_t                                                                        symbolIndex(const ld::Atom*);
-       bool                                                                            kextBundlesDontHaveIndirectSymbolTable();
 
 
        std::vector<uint32_t>                                           _entries;
@@ -2034,9 +2033,11 @@ uint32_t IndirectSymbolTableAtom<A>::symIndexOfStubAtom(const ld::Atom* stubAtom
 {
        for (ld::Fixup::iterator fit = stubAtom->fixupsBegin(); fit != stubAtom->fixupsEnd(); ++fit) {
                if ( fit->binding == ld::Fixup::bindingDirectlyBound ) {
-                       assert((fit->u.target->contentType() == ld::Atom::typeLazyPointer) 
-                                       || (fit->u.target->contentType() == ld::Atom::typeLazyDylibPointer));
-                       return symIndexOfLazyPointerAtom(fit->u.target);
+                       ld::Atom::ContentType type = fit->u.target->contentType();
+                       if (( type == ld::Atom::typeLazyPointer) || (type == ld::Atom::typeLazyDylibPointer) )
+                               return symIndexOfLazyPointerAtom(fit->u.target);
+                       if ( type == ld::Atom::typeNonLazyPointer )
+                               return symIndexOfNonLazyPointerAtom(fit->u.target);
                }
        }
        throw "internal error: stub missing fixup to lazy pointer";
@@ -2152,12 +2153,6 @@ void IndirectSymbolTableAtom<A>::encodeNonLazyPointerSection(ld::Internal::Final
        }
 }
 
-template <typename A>
-bool IndirectSymbolTableAtom<A>::kextBundlesDontHaveIndirectSymbolTable()
-{
-       return true;    
-}
-
 template <typename A>
 void IndirectSymbolTableAtom<A>::encode()
 {
@@ -2165,8 +2160,8 @@ void IndirectSymbolTableAtom<A>::encode()
        if ( (this->_options.outputKind() == Options::kStaticExecutable) && !_options.positionIndependentExecutable() ) 
                return;
 
-       // x86_64 kext bundles should not have an indirect symbol table
-       if ( (this->_options.outputKind() == Options::kKextBundle) && kextBundlesDontHaveIndirectSymbolTable() ) 
+       // x86_64 kext bundles should not have an indirect symbol table unless using stubs
+       if ( (this->_options.outputKind() == Options::kKextBundle) && !this->_options.kextsUseStubs() )
                return;
 
        // slidable static executables (-static -pie) should not have an indirect symbol table
index c915f5e13c22f5cd346e2dbf247a6a667707e6c1..3281f667646ccaeb4faa8521cfbb2d09161d9f16 100644 (file)
@@ -157,7 +157,7 @@ Options::Options(int argc, const char* argv[])
          fUsingLazyDylibLinking(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false),
          fOrderData(true), fMarkDeadStrippableDylib(false),
          fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false),
-         fAllowCpuSubtypeMismatches(false), fUseSimplifiedDylibReExports(false),
+         fAllowCpuSubtypeMismatches(false), fEnforceDylibSubtypesMatch(false), fUseSimplifiedDylibReExports(false),
          fObjCABIVersion2Override(false), fObjCABIVersion1Override(false), fCanUseUpwardDylib(false),
          fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false),
          fLinkingMainExecutable(false), fForFinalLinkedImage(false), fForStatic(false),
@@ -187,12 +187,14 @@ Options::Options(int argc, const char* argv[])
          fSharedRegionEncodingV2(false), fUseDataConstSegment(false),
          fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false),
          fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false),
-         fReverseMapUUIDRename(false), fReverseMapPath(NULL), fLTOCodegenOnly(false),
+         fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false),
+         fReverseMapPath(NULL), fLTOCodegenOnly(false),
          fIgnoreAutoLink(false), fAllowDeadDups(false), fBitcodeKind(kBitcodeProcess),
          fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL),
          fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset),
          fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL),
-         fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1)
+         fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fMaxDefaultCommonAlign(0), fFilePreference(kModTime),
+         fForceTextBasedStub(false)
 {
        this->checkForClassic(argc, argv);
        this->parsePreCommandLineEnvironmentSettings();
@@ -712,9 +714,8 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co
                                                 it != fLibrarySearchPaths.end();
                                                 it++) {
                                                const char* dir = *it;
-                                               if ( checkForFile("%s/lib%s.tbd", dir, rootName, result) )
-                                                       return result;
-                                               if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) )
+                                               auto path = std::string(dir) + "/lib" + rootName + ".dylib";
+                                               if ( findFile(path, {".tbd"}, result) )
                                                        return result;
                                        }
                                        for (std::vector<const char*>::const_iterator it = fLibrarySearchPaths.begin();
@@ -743,9 +744,8 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co
                                         it != fLibrarySearchPaths.end();
                                         it++) {
                                        const char* dir = *it;
-                                       if ( lookForDylibs && checkForFile("%s/lib%s.tbd", dir, rootName, result) )
-                                               return result;
-                                       if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) )
+                                       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;
@@ -785,15 +785,8 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi
                                possiblePath = std::string(realPath).append(suffix);
                }
         FileInfo result;
-               bool found = result.checkFileExists(*this, (possiblePath + ".tbd").c_str());
-               if ( !found )
-                       found = result.checkFileExists(*this, possiblePath.c_str());
-               if ( fTraceDylibSearching )
-                       printf("[Logging for XBS]%sfound framework: '%s'\n",
-                                  (found ? " " : " not "), possiblePath.c_str());
-               if ( found ) {
+               if ( findFile(possiblePath, {".tbd"}, result) )
                        return result;
-               }
        }
        // try without suffix
        if ( suffix != NULL )
@@ -802,41 +795,88 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi
                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;
+}
+
+bool Options::findFile(const std::string &path, const std::vector<std::string> &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 ) {
+                       if ( (fFilePreference == kTextBasedStub) || fForceTextBasedStub ) {
+                               result = tbdInfo;
+                               return true;
+                       } else {
+                               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());
+               if ( found &&  (fFilePreference == kMachO) ) {
+                       result = dylibInfo;
+                       return true;
+               }
+       }
+
+       if ( !dylibInfo.missing() && tbdInfo.missing() ) {
+               result = dylibInfo;
+               return true;
+       }
+       else if ( dylibInfo.missing() && !tbdInfo.missing() ) {
+               result = tbdInfo;
+               return true;
+       }
+       else if ( !dylibInfo.missing() && !tbdInfo.missing() ) {
+               if ( dylibInfo.modTime == tbdInfo.modTime ) {
+                       result = tbdInfo;
+                       return true;
+               }
+               else {
+                       // <rdar://problem/24459693> Disable false warning about out-of-sync .tbd files.
+                       //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;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
 Options::FileInfo Options::findFile(const std::string &path) 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) ) {
-               auto tbdFile = path;
-               auto lastSlashIdx = tbdFile.find_last_of('/');
-               auto lastDotIdx = tbdFile.find_last_of('.');
-               if (lastDotIdx != std::string::npos && lastDotIdx > lastSlashIdx)
-                       tbdFile.erase(lastDotIdx, std::string::npos);
-               tbdFile.append(".tbd");
-
                for (const auto* sdkPathDir : fSDKPaths) {
-                       auto possiblePath = std::string(sdkPathDir) + tbdFile;
-                       if ( result.checkFileExists(*this, possiblePath.c_str()) )
-                               return result;
-                       possiblePath = std::string(sdkPathDir) + path;
-                       if ( result.checkFileExists(*this, possiblePath.c_str()) )
+                       auto possiblePath = std::string(sdkPathDir) + path;
+                       if ( findFile(possiblePath, {".tbd"}, result) )
                                return result;
                }
        }
        // try raw path
-       {
-               std::string file = path;
-               auto lastDotIdx = file.find_last_of('.');
-               if (lastDotIdx != std::string::npos)
-                       file.erase(lastDotIdx, std::string::npos);
-               if ( result.checkFileExists(*this, file.append(".tbd").c_str()) )
-                       return result;
-       }
-       if ( result.checkFileExists(*this, path.c_str()) ) {
+       if ( findFile(path, {".tbd"}, result) )
                return result;
-       }
-
 
        // try @executable_path substitution
        if ( (path.find("@executable_path/") == 0) && (fExecutablePath != nullptr) ) {
@@ -848,16 +888,8 @@ Options::FileInfo Options::findFile(const std::string &path) const
                else
                        strcpy(newPath, &path[17]);
 
-               std::string file = newPath;
-               auto lastDotIdx = file.find_last_of('.');
-               if (lastDotIdx != std::string::npos)
-                       file.erase(lastDotIdx, std::string::npos);
-               if ( result.checkFileExists(*this, file.append(".tbd").c_str()) ) {
-                       return result;
-               }
-               if ( result.checkFileExists(*this, newPath) ) {
+               if ( findFile(newPath, {".tbd"}, result) )
                        return result;
-               }
        }
 
        // not found
@@ -893,9 +925,7 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const
                auto leafPath = path.substr(beginPos);
                for (const auto* dir : fFrameworkSearchPaths) {
                        auto possiblePath = dir + leafPath;
-                       if ( checkForFile("%s.%s", possiblePath.c_str(), "tbd", result) )
-                               return result;
-                       if ( checkForFile("%s", possiblePath.c_str(), "", result) )
+                       if ( findFile(possiblePath, {".tbd"}, result) )
                                return result;
                }
        } else {
@@ -907,9 +937,8 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const
                if ( !embeddedDylib ) {
                        for (const auto* dir : fLibrarySearchPaths) {
                                //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName);
-                               if ( checkForFile("%s/%s", dir, std::string(leafName).append(".tbd").c_str(), result) )
-                                       return result;
-                               if ( checkForFile("%s/%s", dir, leafName.c_str(), result) )
+                               auto possiblePath = dir + leafName;
+                               if ( findFile(possiblePath, {".tbd"}, result) )
                                        return result;
                        }
                }
@@ -1395,58 +1424,32 @@ Options::Treatment Options::parseTreatment(const char* treatment)
 
 void Options::setMacOSXVersionMin(const char* version)
 {
-       if ( version == NULL )
-               throw "-macosx_version_min argument missing";
-
-       if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) {
-               unsigned int minorVersion = 0;
-               for (int i=3; isdigit(version[i]); ++i) {
-                       minorVersion = minorVersion*10 + (version[i] - '0');
-               }
-               if ( minorVersion > 255 ) {
-                       warning("Mac OS X minor version > 255 in '%s'", version);
-                       minorVersion = 255;
-               }
-               fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8));
-               fPlatform = kPlatformOSX;
-       }
-       else {
-               warning("unknown option to -macosx_version_min, not 10.x");
+       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)
 {
-       if ( version == NULL )
-               throw "-ios_version_min argument missing";
-       if ( ! isdigit(version[0]) )
-               throw "-ios_version_min argument is not a number";
-       if ( version[1] != '.' )
-               throw "-ios_version_min argument is missing period as second character";
-       if ( ! isdigit(version[2]) )
-               throw "-ios_version_min argument is not a number";
-
-       unsigned int majorVersion = version[0] - '0';
-       unsigned int minorVersion = version[2] - '0';
-       fIOSVersionMin = (ld::IOSVersionMin)((majorVersion << 16) | (minorVersion << 8));
+       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)
 {
-       if ( version == NULL )
-               throw "-watchos_version_min argument missing";
-       if ( ! isdigit(version[0]) )
-               throw "-watchos_version_min argument is not a number";
-       if ( version[1] != '.' )
-               throw "-watchos_version_min argument is missing period as second character";
-       if ( ! isdigit(version[2]) )
-               throw "-watchos_version_min argument is not a number";
-
-       unsigned int majorVersion = version[0] - '0';
-       unsigned int minorVersion = version[2] - '0';
-       fWatchOSVersionMin = (ld::WatchOSVersionMin)((majorVersion << 16) | (minorVersion << 8));
+       uint32_t value;
+       if ( !parsePackedVersion32(version, value) ) {
+               throwf("-watchos_version_min value malformed: '%s'", version);
+       }
+       fWatchOSVersionMin = (ld::WatchOSVersionMin)value;
        fPlatform = kPlatformWatchOS;
 }
 
@@ -2027,6 +2030,73 @@ std::string Options::getVersionString64(uint64_t ver) const
        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, &microEnd);
+                       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);
@@ -2695,7 +2765,10 @@ void Options::parse(int argc, const char* argv[])
                                        throw "-segprot missing segName max-prot init-prot";
                                seg.max = parseProtection(argv[++i]);
                                seg.init = parseProtection(argv[++i]);
-                               fCustomSegmentProtections.push_back(seg);
+                               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 ) {
@@ -2721,9 +2794,6 @@ void Options::parse(int argc, const char* argv[])
                                 if ( size == NULL )
                                        throw "-stack_size missing <address>";
                                fStackSize = parseAddress(size);
-                               uint64_t temp = fStackSize & (-4096); // page align
-                               if ( (fStackSize != temp)  )
-                                       warning("-stack_size not page aligned, rounding down");
                        }
                        else if ( strcmp(arg, "-allow_stack_execute") == 0 ) {
                                fExecutableStack = true;
@@ -2766,6 +2836,8 @@ void Options::parse(int argc, const char* argv[])
                        // 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) ) {
@@ -2788,26 +2860,44 @@ void Options::parse(int argc, const char* argv[])
                                }
                        }
                        else if ( (strcmp(arg, "-ios_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) {
-                               setIOSVersionMin(argv[++i]); 
+                               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 ) {
-                               setIOSVersionMin(argv[++i]);
+                               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 ) {
-                               setWatchOSVersionMin(argv[++i]);
+                               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 ) {
-                               setWatchOSVersionMin(argv[++i]);
+                               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 ) {
-                               setIOSVersionMin(argv[++i]);
+                               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 ) {
-                               setIOSVersionMin(argv[++i]);
+                               const char* vers = argv[++i];
+                               if ( vers == NULL )
+                                       throw "-tvos_simulator_version_min missing version argument";
+                               setIOSVersionMin(vers);
                                fPlatform = kPlatform_tvOS;
                                fTargetIOSSimulator = true;
                        }
@@ -3627,6 +3717,43 @@ void Options::parse(int argc, const char* argv[])
                                fUseDataConstSegmentForceOff = true;
                                cannotBeUsedWithBitcode(arg);
                        }
+                       else if ( strcmp(arg, "-no_deduplicate") == 0 ) {
+                               fDeDupe = false;
+                       }
+                       else if ( strcmp(arg, "-verbose_deduplicate") == 0 ) {
+                               fVerboseDeDupe = true;
+                       }
+                       else if ( strcmp(arg, "-max_default_common_align") == 0 ) {
+                               const char* alignStr = argv[++i];
+                               if ( alignStr == NULL )
+                                       throw "-max_default_common_align missing <align-value>";
+                               // argument is a hexadecimal number
+                               char* endptr;
+                               unsigned long value = strtoul(alignStr, &endptr, 16);
+                               if ( *endptr != '\0')
+                                       throw "argument for -max_default_common_align is not a hexadecimal number";
+                               if ( value > 0x8000 )
+                                       throw "argument for -max_default_common_align must be less than or equal to 0x8000";
+                               if ( value == 0 ) {
+                                       warning("zero is not a valid -max_default_common_align");
+                                       value = 1;
+                               }
+                               // alignment is power of 2 
+                               uint8_t alignment = (uint8_t)__builtin_ctz(value);
+                               if ( (unsigned long)(1 << alignment) != value ) {
+                                       warning("alignment for -max_default_common_align is not a power of two, using 0x%X", 1 << alignment);
+                               }
+                               fMaxDefaultCommonAlign = alignment;
+                       }
+                       else if ( strcmp(arg, "-prefer-mod-time-check") == 0 ) {
+                               fFilePreference = kModTime;
+                       }
+                       else if ( strcmp(arg, "-prefer-text-based-stub-file") == 0 ) {
+                               fFilePreference = kTextBasedStub;
+                       }
+                       else if ( strcmp(arg, "-prefer-macho-file") == 0 ) {
+                               fFilePreference = kMachO;
+                       }
                        // put this last so that it does not interfer with other options starting with 'i'
                        else if ( strncmp(arg, "-i", 2) == 0 ) {
                                const char* colon = strchr(arg, ':');
@@ -3701,16 +3828,7 @@ void Options::buildSearchPaths(int argc, const char* argv[])
                        }
                        if ( libSearchDir[0] == '\0' ) 
                                throw "-L must be immediately followed by a directory path (no space)";
-                       struct stat statbuf;
-                       if ( stat(libSearchDir, &statbuf) == 0 ) {
-                               if ( statbuf.st_mode & S_IFDIR )
-                                       libraryPaths.push_back(libSearchDir);
-                               else
-                                       warning("path '%s' following -L not a directory", libSearchDir);
-                       }
-                       else {
-                               warning("directory not found for option '-L%s'", libSearchDir);
-                       }
+                       libraryPaths.push_back(libSearchDir);
                }
                else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) {
                        const char* frameworkSearchDir = &argv[i][2];
@@ -3724,16 +3842,7 @@ void Options::buildSearchPaths(int argc, const char* argv[])
                        }
                        if ( frameworkSearchDir[0] == '\0' ) 
                                throw "-F must be immediately followed by a directory path (no space)";
-                       struct stat statbuf;
-                       if ( stat(frameworkSearchDir, &statbuf) == 0 ) {
-                               if ( statbuf.st_mode & S_IFDIR )
-                                       frameworkPaths.push_back(frameworkSearchDir);
-                               else
-                                       warning("path '%s' following -F not a directory", frameworkSearchDir);
-                       }
-                       else {
-                               warning("directory not found for option '-F%s'", frameworkSearchDir);
-                       }
+                       frameworkPaths.push_back(frameworkSearchDir);
                }
                else if ( strcmp(argv[i], "-Z") == 0 )
                        addStandardLibraryDirectories = false;
@@ -3820,8 +3929,13 @@ void Options::buildSearchPaths(int argc, const char* argv[])
                                strcat(newPath, libDir);
                                struct stat statBuffer;
                                if ( stat(newPath, &statBuffer) == 0 ) {
-                                       fLibrarySearchPaths.push_back(strdup(newPath));
-                                       sdkOverride = true;
+                                       if ( (statBuffer.st_mode & S_IFDIR) == 0 ) {
+                                               warning("-syslibroot and -L combined path '%s' is not a directory", newPath);
+                                       }
+                                       else {
+                                               fLibrarySearchPaths.push_back(strdup(newPath));
+                                               sdkOverride = true;
+                                       }
                                }
                        }
                }
@@ -3831,11 +3945,21 @@ void Options::buildSearchPaths(int argc, const char* argv[])
                                // if one SDK is specified and a standard library path is not in the SDK, don't use it
                        }
                        else {
-                               fLibrarySearchPaths.push_back(libDir);
+                               struct stat statBuffer;
+                               if ( stat(libDir, &statBuffer) == 0 ) {
+                                       if ( (statBuffer.st_mode & S_IFDIR) == 0 )
+                                               warning("-L path '%s' is not a directory", libDir);
+                                       else
+                                               fLibrarySearchPaths.push_back(libDir);
+                               }
+                               else if ( !addStandardLibraryDirectories || (strcmp(libDir, "/usr/local/lib") != 0) ) {
+                                       warning("directory not found for option '-L%s'", libDir);
+                               }
                        }
                }
        }
 
+
        // now merge sdk and framework paths to make real search paths
        fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1));
        int frameIndex = 0;
@@ -3859,8 +3983,13 @@ void Options::buildSearchPaths(int argc, const char* argv[])
                                strcat(newPath, frameworkDir);
                                struct stat statBuffer;
                                if ( stat(newPath, &statBuffer) == 0 ) {
-                                       fFrameworkSearchPaths.push_back(strdup(newPath));
-                                       sdkOverride = true;
+                                       if ( (statBuffer.st_mode & S_IFDIR) == 0 ) {
+                                               warning("-syslibroot and -F combined path '%s' is not a directory", newPath);
+                                       }
+                                       else {
+                                               fFrameworkSearchPaths.push_back(strdup(newPath));
+                                               sdkOverride = true;
+                                       }
                                }
                        }
                }
@@ -3870,7 +3999,16 @@ void Options::buildSearchPaths(int argc, const char* argv[])
                                // if one SDK is specified and a standard library path is not in the SDK, don't use it
                        }
                        else {
-                               fFrameworkSearchPaths.push_back(frameworkDir);
+                               struct stat statBuffer;
+                               if ( stat(frameworkDir, &statBuffer) == 0 ) {
+                                       if ( (statBuffer.st_mode & S_IFDIR) == 0 )
+                                               warning("-F path '%s' is not a directory", frameworkDir);
+                                       else
+                                               fFrameworkSearchPaths.push_back(frameworkDir);
+                               }
+                               else if ( !addStandardLibraryDirectories || (strcmp(frameworkDir, "/Library/Frameworks/") != 0) ) {
+                                       warning("directory not found for option '-F%s'", frameworkDir);
+                               }
                        }
                }
        }
@@ -3932,6 +4070,9 @@ void Options::parsePreCommandLineEnvironmentSettings()
        if (getenv("LD_ALLOW_CPU_SUBTYPE_MISMATCHES") != NULL)
                fAllowCpuSubtypeMismatches = true;
        
+       if (getenv("LD_DYLIB_CPU_SUBTYPES_MUST_MATCH") != NULL)
+               fEnforceDylibSubtypesMatch = true;
+       
        sWarningsSideFilePath = getenv("LD_WARN_FILE");
        
        const char* customDyldPath = getenv("LD_DYLD_PATH");
@@ -3950,6 +4091,10 @@ void Options::parsePreCommandLineEnvironmentSettings()
     if (pipeFdString != NULL) {
                fPipelineFifo = pipeFdString;
     }
+
+       // Workaround for rdar://problem/24301175
+       if ((getenv("RC_XBS") != NULL) && !(getenv("RC_BUILDIT") != NULL))
+               fForceTextBasedStub = true;
 }
 
 
@@ -4521,10 +4666,13 @@ void Options::reconfigureDefaults()
        // only ARM and x86_64 enforces that cpu-sub-types must match
        switch ( fArchitecture ) {
                case CPU_TYPE_ARM:
+                       break;
                case CPU_TYPE_X86_64:
+                       fEnforceDylibSubtypesMatch = false;
                        break;
                case CPU_TYPE_I386:
                case CPU_TYPE_ARM64:
+                       fEnforceDylibSubtypesMatch = false;
                        fAllowCpuSubtypeMismatches = true;
                        break;
        }
@@ -4621,6 +4769,12 @@ void Options::reconfigureDefaults()
        else if ( (fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_9_0) ) {
                fTLVSupport = true;
        }
+       else if ( fTargetIOSSimulator && (fArchitecture == CPU_TYPE_X86_64) && min_iOS(ld::iOS_8_0) ) {
+               fTLVSupport = true;
+       }
+       else if ( fTargetIOSSimulator && (fArchitecture == CPU_TYPE_I386) && min_iOS(ld::iOS_9_0) ) {
+               fTLVSupport = true;
+       }
 
        // default to adding version load command for dynamic code, static code must opt-in
        switch ( fOutputKind ) {
@@ -4869,6 +5023,13 @@ void Options::reconfigureDefaults()
                }
        }
 
+       // <rdar://problem/20503811> Reduce the default alignment of structures/arrays to save memory in embedded systems
+       if ( fMaxDefaultCommonAlign == 0 ) {
+               if ( fOutputKind == Options::kPreload )
+                       fMaxDefaultCommonAlign = 8;
+               else
+                       fMaxDefaultCommonAlign = 15;
+       }
 }
 
 void Options::checkIllegalOptionCombinations()
@@ -4876,9 +5037,24 @@ void Options::checkIllegalOptionCombinations()
        // check -undefined setting
        switch ( fUndefinedTreatment ) {
                case kUndefinedError:
-               case kUndefinedDynamicLookup:
                        // always legal
                        break;
+               case kUndefinedDynamicLookup:
+                       switch (fPlatform) {
+                               case kPlatformOSX:
+                                       break;
+                               case kPlatformiOS:
+                               case kPlatformWatchOS:
+               #if SUPPORT_APPLE_TV
+                               case kPlatform_tvOS:
+               #endif
+                                       if ( fOutputKind != kKextBundle )
+                                               warning("-undefined dynamic_lookup is deprecated on %s", platformName(fPlatform));
+                                       break;
+                               default:
+                                       break;
+                       }
+                       break;
                case kUndefinedWarning:
                case kUndefinedSuppress:
                        // requires flat namespace
@@ -4896,7 +5072,11 @@ void Options::checkIllegalOptionCombinations()
                        const char* lastSlash = strrchr(info.path, '/');
                        if ( lastSlash == NULL )
                                lastSlash = info.path - 1;
-                       if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) {
+                       std::string path(&lastSlash[1]);
+                       auto idx = path.find(".tbd", path.size() - 4);
+                       if (idx != std::string::npos)
+                               path.erase(idx);
+                       if ( path == subUmbrella ) {
                                info.options.fReExport = true;
                                found = true;
                 fLinkSnapshot.recordSubUmbrella(info.path);
@@ -4931,8 +5111,23 @@ void Options::checkIllegalOptionCombinations()
        }
 
        // sync reader options
-       if ( fNameSpace != kTwoLevelNameSpace )
+       if ( fNameSpace != kTwoLevelNameSpace ) {
                fFlatNamespace = true;
+               switch (fPlatform) {
+                       case kPlatformOSX:
+                               break;
+                       case kPlatformiOS:
+                       case kPlatformWatchOS:
+       #if SUPPORT_APPLE_TV
+                       case Options::kPlatform_tvOS:
+       #endif
+                               warning("-flat_namespace is deprecated on %s", platformName(fPlatform));
+                               break;
+                       default:
+                               break;
+               }
+       }
+
 
        // check -stack_addr
        if ( fStackAddr != 0 ) {
@@ -4956,37 +5151,54 @@ void Options::checkIllegalOptionCombinations()
        if ( fStackSize != 0 ) {
                switch (fArchitecture) {
                        case CPU_TYPE_I386:
-                               if ( fStackSize > 0xFFFFFFFF )
-                                       throw "-stack_size must be < 4G for 32-bit processes";
-                               if ( fStackAddr == 0 ) {
-                                       fStackAddr = 0xC0000000;
+                               if ( fPlatform == kPlatformOSX ) {
+                                       if ( fStackSize > 0xFFFFFFFF )
+                                               throw "-stack_size must be < 4GB for 32-bit processes";
+                                       if ( fStackAddr == 0 )
+                                               fStackAddr = 0xC0000000;
+                                       if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000)  )
+                                               warning("custom stack placement overlaps and will disable shared region");
+                               }
+                               else {
+                                       if ( fStackSize > 0x1F000000 )
+                                               throw "-stack_size must be < 496MB";
+                                       if ( fStackAddr == 0 )
+                                               fStackAddr = 0xC0000000;
                                }
-                               if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000)  )
-                                       warning("custom stack placement overlaps and will disable shared region");
                                break;
             case CPU_TYPE_ARM:
-                               if ( fStackSize > 0x2F000000 )
-                                       throw "-stack_size must be < 752MB";
+                               if ( fStackSize > 0x1F000000 )
+                                       throw "-stack_size must be < 496MB";
                                if ( fStackAddr == 0 )
-                                       fStackAddr = 0x2F000000;
-                if ( fStackAddr > 0x30000000)
-                    throw "-stack_addr must be < 0x30000000 for arm";
+                                       fStackAddr = 0x1F000000;
+                if ( fStackAddr > 0x20000000)
+                    throw "-stack_addr must be < 0x20000000 for arm";
                                break;
                        case CPU_TYPE_X86_64:
-                               if ( fStackAddr == 0 ) {
-                                       fStackAddr = 0x00007FFF5C000000LL;
+                               if ( fPlatform == kPlatformOSX ) {
+                                       if ( fStackSize > 0x10000000000 )
+                                               throw "-stack_size must be <= 1TB";
+                                       if ( fStackAddr == 0 ) {
+                                               fStackAddr = 0x00007FFF5C000000LL;
+                                       }
+                               }
+                               else {
+                                       if ( fStackSize > 0x20000000 )
+                                               throw "-stack_size must be <= 512MB";
+                                       if ( fStackAddr == 0 ) {
+                                               fStackAddr = 0x120000000;
                                }
                                break;
                        case CPU_TYPE_ARM64:
                                if ( fStackSize > 0x20000000 )
-                                       throw "-stack_size must be < 512MB";
-                               if ( fStackAddr == 0 ) {
+                                       throw "-stack_size must be <= 512MB";
+                               if ( fStackAddr == 0 )
                                        fStackAddr = 0x120000000;
                                }
                                break;
                }
-               if ( (fStackSize & -4096) != fStackSize )
-                       throw "-stack_size must be multiples of 4K";
+               if ( (fStackSize & (-fSegmentAlignment)) != fStackSize )
+                       throwf("-stack_size must be multiple of segment alignment (%lldKB)", fSegmentAlignment/1024);
                switch ( fOutputKind ) {
                        case Options::kDynamicExecutable:
                        case Options::kStaticExecutable:
@@ -5313,15 +5525,6 @@ void Options::checkIllegalOptionCombinations()
        if ( !fSegmentOrder.empty() && (fOutputKind != Options::kPreload) )
                throw "-segment_order can only used used with -preload output";
 
-       if ( fBitcodeKind != kBitcodeProcess &&
-                fOutputKind != Options::kObjectFile ) {
-               throw "-bitcode_process_mode can only be used together with -r";
-       }
-       // auto fix up the process type for strip -S.
-       // when there is only one input and output type is object file, downgrade kBitcodeProcess to kBitcodeAsData.
-       if ( fOutputKind == Options::kObjectFile && fInputFiles.size() == 1 && fBitcodeKind == Options::kBitcodeProcess )
-               fBitcodeKind = Options::kBitcodeAsData;
-
        // warn about bitcode option combinations
        if ( !fBundleBitcode ) {
                if ( fVerifyBitcode )
@@ -5332,9 +5535,17 @@ void Options::checkIllegalOptionCombinations()
        if ( fReverseMapPath != NULL && !fHideSymbols ) {
                throw "-bitcode_symbol_map can only be used with -bitcode_hide_symbols";
        }
+       if ( fBitcodeKind != kBitcodeProcess &&
+                fOutputKind != Options::kObjectFile ) {
+               throw "-bitcode_process_mode can only be used together with -r";
+       }
+       // auto fix up the process type for strip -S.
+       // when there is only one input and output type is object file, downgrade kBitcodeProcess to kBitcodeAsData.
+       if ( fOutputKind == Options::kObjectFile && fInputFiles.size() == 1 && fBitcodeKind == Options::kBitcodeProcess )
+               fBitcodeKind = Options::kBitcodeAsData;
 
        // <rdar://problem/17598404> warn if building an embedded iOS dylib for pre-iOS 8
-       // <rdar://problem/18935714> How can we suppress "ld: warning: embedded dylibs/frameworks only run on iOS 8 or laterÓ when building XCTest?
+       // <rdar://problem/18935714> How can we suppress "ld: warning: embedded dylibs/frameworks only run on iOS 8 or later" when building XCTest?
        if ( (fOutputKind == Options::kDynamicLibrary) && (fIOSVersionMin != ld::iOSVersionUnset) && (fDylibInstallName != NULL) ) {
                if ( !min_iOS(ld::iOS_8_0) && (fDylibInstallName[0] == '@') && !fEncryptableForceOff )
                        warning("embedded dylibs/frameworks only run on iOS 8 or later");
index e7ffff23564e971e0d6be0366187bb94209058af..5155de4ad876f5d13b177331c0cb1509677d8938 100644 (file)
@@ -147,6 +147,18 @@ public:
         // the source, which dies with the stack frame.
         FileInfo(FileInfo const &other) : path(other.path), fileLen(other.fileLen), modTime(other.modTime), options(other.options), ordinal(other.ordinal), fromFileList(other.fromFileList), inputFileSlot(-1) { ((FileInfo&)other).path = NULL; };
 
+               FileInfo &operator=(FileInfo other) {
+                       std::swap(path, other.path);
+                       std::swap(fileLen, other.fileLen);
+                       std::swap(modTime, other.modTime);
+                       std::swap(options, other.options);
+                       std::swap(ordinal, other.ordinal);
+                       std::swap(fromFileList, other.fromFileList);
+                       std::swap(inputFileSlot, other.inputFileSlot);
+                       std::swap(readyToParse, other.readyToParse);
+                       return *this;
+               }
+
         // Create an empty FileInfo. The path can be set implicitly by checkFileExists().
         FileInfo() : path(NULL), fileLen(0), modTime(0), options(), fromFileList(false) {};
         
@@ -248,6 +260,7 @@ public:
        bool                                            preferSubArchitecture() const { return fHasPreferredSubType; }
        cpu_subtype_t                           subArchitecture() const { return fSubArchitecture; }
        bool                                            allowSubArchitectureMismatches() const { return fAllowCpuSubtypeMismatches; }
+       bool                                            enforceDylibSubtypesMatch() const { return fEnforceDylibSubtypesMatch; }
        bool                                            forceCpuSubtypeAll() const { return fForceSubtypeAll; }
        const char*                                     architectureName() const { return fArchitectureName; }
        void                                            setArchitecture(cpu_type_t, cpu_subtype_t subtype, Options::Platform platform);
@@ -313,6 +326,7 @@ public:
        bool                                            warnCommons() const { return fWarnCommons; }
        bool                                            keepRelocations();
        FileInfo                                        findFile(const std::string &path) const;
+       bool                                            findFile(const std::string &path, const std::vector<std::string> &tbdExtensions, FileInfo& result) const;
        UUIDMode                                        UUIDMode() const { return fUUIDMode; }
        bool                                            warnStabs();
        bool                                            pauseAtEnd() { return fPause; }
@@ -415,6 +429,8 @@ public:
        bool                                            hideSymbols() const { return fHideSymbols; }
        bool                                            verifyBitcode() const { return fVerifyBitcode; }
        bool                                            renameReverseSymbolMap() const { return fReverseMapUUIDRename; }
+       bool                                            deduplicateFunctions() const { return fDeDupe; }
+       bool                                            verboseDeduplicate() const { return fVerboseDeDupe; }
        const char*                                     reverseSymbolMapPath() const { return fReverseMapPath; }
        std::string                                     reverseMapTempPath() const { return fReverseMapTempPath; }
        bool                                            ltoCodegenOnly() const { return fLTOCodegenOnly; }
@@ -458,6 +474,9 @@ public:
        std::vector<std::string>        writeBitcodeLinkOptions() const;
        std::string                                     getSDKVersionStr() const;
        std::string                                     getPlatformStr() const;
+       uint8_t                                         maxDefaultCommonAlign() const { return fMaxDefaultCommonAlign; }
+
+       static uint32_t                         parseVersionNumber32(const char*);
 
 private:
        typedef std::unordered_map<const char*, unsigned int, ld::CStringHash, ld::CStringEquals> NameToOrder;
@@ -465,6 +484,7 @@ private:
        enum ExportMode { kExportDefault, kExportSome, kDontExportSome };
        enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives };
        enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome };
+       enum FilePreference { kModTime, kTextBasedStub, kMachO };
 
        class SetWithWildcards {
        public:
@@ -500,9 +520,9 @@ private:
        bool                                            checkForFile(const char* format, const char* dir, const char* rootName,
                                                                                         FileInfo& result) const;
        uint64_t                                        parseVersionNumber64(const char*);
-       uint32_t                                        parseVersionNumber32(const char*);
        std::string                                     getVersionString32(uint32_t ver) const;
        std::string                                     getVersionString64(uint64_t ver) const;
+       bool                                            parsePackedVersion32(const std::string& versionStr, uint32_t &result);
        void                                            parseSectionOrderFile(const char* segment, const char* section, const char* path);
        void                                            parseOrderFile(const char* path, bool cstring);
        void                                            addSection(const char* segment, const char* section, const char* path);
@@ -639,6 +659,7 @@ private:
        bool                                                            fMakeCompressedDyldInfoForceOff;
        bool                                                            fNoEHLabels;
        bool                                                            fAllowCpuSubtypeMismatches;
+       bool                                                            fEnforceDylibSubtypesMatch;
        bool                                                            fUseSimplifiedDylibReExports;
        bool                                                            fObjCABIVersion2Override;
        bool                                                            fObjCABIVersion1Override;
@@ -715,6 +736,8 @@ private:
        bool                                                            fHideSymbols;
        bool                                                            fVerifyBitcode;
        bool                                                            fReverseMapUUIDRename;
+       bool                                                            fDeDupe;
+       bool                                                            fVerboseDeDupe;
        const char*                                                     fReverseMapPath;
        std::string                                                     fReverseMapTempPath;
        bool                                                            fLTOCodegenOnly;
@@ -758,6 +781,9 @@ private:
     const char*                                                        fPipelineFifo;
        const char*                                                     fDependencyInfoPath;
        mutable int                                                     fDependencyFileDescriptor;
+       uint8_t                                                         fMaxDefaultCommonAlign;
+       FilePreference                                          fFilePreference;
+       bool                                                            fForceTextBasedStub;
 };
 
 
index dd0e5b241e8dca5f86c07e6744fc011c30b70e9b..1d708da079aea22a21a90a6e143887a366ec771d 100644 (file)
@@ -1294,7 +1294,7 @@ static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false)
 
 #define LOH_ASSERT(cond) \
        if ( !(cond) ) { \
-               warning("ignoring linker optimzation hint at %s+0x%X because " #cond, atom->name(), fit->offsetInAtom); \
+               warning("ignoring linker optimization hint at %s+0x%X because " #cond, atom->name(), fit->offsetInAtom); \
                break; \
        } 
 
@@ -2235,7 +2235,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::
                                        LOH_ASSERT(isADRP);
                                        isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC);
                                        LOH_ASSERT(isLDR);
-                                       LOH_ASSERT(ldrInfoC.offset == 0);
                                        isADD = parseADD(infoB.instruction, addInfoB);
                                        isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
                                        if ( isLDR ) {
@@ -2244,8 +2243,8 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::
                                                LOH_ASSERT(!ldrInfoB.isFloat);
                                                LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg);
                                                //fprintf(stderr, "infoA.target=%p, %s, infoA.targetAddress=0x%08llX\n", infoA.target, infoA.target->name(), infoA.targetAddress);
-                                               targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
-                                               if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+                                               targetFourByteAligned = ( ((infoA.targetAddress + ldrInfoC.offset) & 0x3) == 0 );
+                                               if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress + ldrInfoC.offset) ) {
                                                        // can do T5 transform
                                                        set32LE(infoA.instructionContent, makeNOP());
                                                        set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
@@ -2264,11 +2263,11 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::
                                                LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg);
                                                targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
                                                literalableSize  = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) );
-                                               if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress) ) {
+                                               if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress + ldrInfoC.offset) ) {
                                                        // can do T1 transform
                                                        set32LE(infoA.instructionContent, makeNOP());   
                                                        set32LE(infoB.instructionContent, makeNOP());   
-                                                       set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress, infoC.instructionAddress));
+                                                       set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress + ldrInfoC.offset, infoC.instructionAddress));
                                                        if ( _options.verboseOptimizationHints() ) 
                                                                fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress);
                                                }
@@ -2281,14 +2280,14 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::
                                                                fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoC.instructionAddress);
                                                        }
                                                }
-                                               else if ( (infoA.targetAddress % ldrInfoC.size) == 0 ) {
+                                               else if ( ((infoA.targetAddress % ldrInfoC.size) == 0) && ((addInfoB.addend + ldrInfoC.offset) < 4096) ) {
                                                        // can do T2 transform
                                                        set32LE(infoB.instructionContent, makeNOP());
                                                        ldrInfoC.baseReg = adrpInfoA.destReg;
-                                                       ldrInfoC.offset = addInfoB.addend;
+                                                       ldrInfoC.offset += addInfoB.addend;
                                                        set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
                                                        if ( _options.verboseOptimizationHints() ) {
-                                                               fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADRP/NOP/LDR\n", infoC.instructionAddress);
+                                                               fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T2 transformed to ADRP/NOP/LDR\n", infoC.instructionAddress);
                                                        }
                                                }
                                                else {
@@ -2354,7 +2353,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::
                                        LOH_ASSERT(isADRP);
                                        isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore);
                                        LOH_ASSERT(isSTR);
-                                       LOH_ASSERT(ldrInfoC.offset == 0);
                                        isADD = parseADD(infoB.instruction, addInfoB);
                                        isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB);
                                        if ( isLDR ) {
@@ -2362,8 +2360,8 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::
                                                LOH_ASSERT(ldrInfoB.size == 8);
                                                LOH_ASSERT(!ldrInfoB.isFloat);
                                                LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg);
-                                               targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 );
-                                               if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) {
+                                               targetFourByteAligned = ( ((infoA.targetAddress + ldrInfoC.offset) & 0x3) == 0 );
+                                               if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress + ldrInfoC.offset) ) {
                                                        // can do T5 transform
                                                        set32LE(infoA.instructionContent, makeNOP());
                                                        set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress));
@@ -2395,7 +2393,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::
                                                        // can do T2 transform
                                                        set32LE(infoB.instructionContent, makeNOP());
                                                        ldrInfoC.baseReg = adrpInfoA.destReg;
-                                                       ldrInfoC.offset = addInfoB.addend;
+                                                       ldrInfoC.offset += addInfoB.addend;
                                                        set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC));
                                                        if ( _options.verboseOptimizationHints() ) {
                                                                fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADRP/NOP/STR\n", infoC.instructionAddress);
@@ -3600,6 +3598,10 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind)
                case ld::Fixup::kindStoreARM64GOTLoadPageOff12:
                case ld::Fixup::kindStoreARM64GOTLeaPage21:
                case ld::Fixup::kindStoreARM64GOTLeaPageOff12:
+               case ld::Fixup::kindStoreARM64TLVPLoadPage21:
+               case ld::Fixup::kindStoreARM64TLVPLoadPageOff12:
+               case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21:
+               case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12:
                case ld::Fixup::kindStoreARM64PCRelToGOT:
                case ld::Fixup::kindStoreTargetAddressARM64Page21:
                case ld::Fixup::kindStoreTargetAddressARM64PageOff12:
@@ -3607,6 +3609,10 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind)
                case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
                case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21:
                case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12:
+               case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21:
+               case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12:
+               case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21:
+               case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12:
 #endif
                        return true;
                case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32:
@@ -3670,6 +3676,10 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind)
                case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
                case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21:
                case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12:
+               case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21:
+               case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12:
+               case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21:
+               case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12:
 #endif
                        return true;
                case ld::Fixup::kindStoreX86DtraceCallSiteNop:
@@ -4529,9 +4539,12 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state)
                                        case ld::Fixup::kindStoreARM64GOTLoadPage21:
                                        case ld::Fixup::kindStoreARM64GOTLeaPage21:
                                        case ld::Fixup::kindStoreARM64TLVPLoadPage21:
+                                       case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21:
                                        case ld::Fixup::kindStoreTargetAddressARM64Page21:
                                        case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
                                        case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21:
+                                       case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21:
+                                       case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21:
                                        case ld::Fixup::kindStoreARM64PCRelToGOT:
 #endif
                                                assert(target != NULL);
@@ -4666,9 +4679,12 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state)
                                        case ld::Fixup::kindStoreARM64GOTLoadPage21:
                                        case ld::Fixup::kindStoreARM64GOTLeaPage21:
                                        case ld::Fixup::kindStoreARM64TLVPLoadPage21:
+                                       case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21:
                                        case ld::Fixup::kindStoreTargetAddressARM64Page21:
                                        case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
                                        case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21:
+                                       case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21:
+                                       case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21:
                                                if ( fromSectionIndex != toSectionIndex )
                                                        kind = DYLD_CACHE_ADJ_V2_ARM64_ADRP;
                                                break;
index cbb1cfe621e457af397f2f9d52d66faaa4876a78..5fd27d80592c0d72253598a2df33826e34edbb0c 100644 (file)
@@ -147,6 +147,7 @@ public:
                uint8_t                 targetSectionIndex;
                uint8_t                 referenceKind;
        };
+       static void                                     dumpAtomsBySection(ld::Internal& state, bool);
 
 private:
        void                                            writeAtoms(ld::Internal& state, uint8_t* wholeBuffer);
@@ -237,7 +238,6 @@ private:
                                                                                                                                                                                        
        uint64_t                                        sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup);
        uint64_t                                        tlvTemplateOffsetOf(const ld::Internal& state, const ld::Fixup* fixup);
-       void                                            dumpAtomsBySection(ld::Internal& state, bool);
        void                                            synthesizeDebugNotes(ld::Internal& state);
        const char*                                     assureFullPath(const char* path);
        void                                            noteTextReloc(const ld::Atom* atom, const ld::Atom* target);
index 948906f8de6e0cdd7b2a0a5e1fb4786827cde9d0..ec04b46f889f49addbec98bf06a40835984c20e5 100644 (file)
@@ -362,6 +362,11 @@ void Resolver::doFile(const ld::File& file)
                        for (relocatable::File::LinkerOptionsList::const_iterator it=lo->begin(); it != lo->end(); ++it) {
                                this->doLinkerOption(*it, file.path());
                        }
+                       // <rdar://problem/23053404> process any additional linker-options introduced by this new archive member being loaded
+                       if ( _completedInitialObjectFiles ) {
+                               _inputFiles.addLinkerOptionLibraries(_internal, *this);
+                               _inputFiles.createIndirectDylibs();
+                       }
                }
                // Resolve bitcode section in the object file
                if ( _options.bundleBitcode() ) {
@@ -618,7 +623,7 @@ void Resolver::doFile(const ld::File& file)
                                break;
                }
                if ( _options.checkDylibsAreAppExtensionSafe() && !dylibFile->appExtensionSafe() ) {
-                       warning("linking against dylib not safe for use in application extensions: %s", file.path());
+                       warning("linking against a dylib which is not safe for use in application extensions: %s", file.path());
                }
                const char* depInstallName = dylibFile->installPath();
                // <rdar://problem/17229513> embedded frameworks are only supported on iOS 8 and later
@@ -944,6 +949,10 @@ void Resolver::resolveUndefines()
                }
        }
        
+       // After resolving all the undefs within the linkageUnit, record all the remaining undefs and all the proxies.
+       if (_options.bundleBitcode() && _options.hideSymbols())
+               _symbolTable.mustPreserveForBitcode(_internal.allUndefProxies);
+
 }
 
 
@@ -1705,6 +1714,7 @@ void Resolver::linkTimeOptimize()
        optOpt.simulator                                        = _options.targetIOSSimulator();
        optOpt.ignoreMismatchPlatform           = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable));
        optOpt.bitcodeBundle                            = _options.bundleBitcode();
+       optOpt.maxDefaultCommonAlignment        = _options.maxDefaultCommonAlign();
        optOpt.arch                                                     = _options.architecture();
        optOpt.mcpu                                                     = _options.mcpuLTO();
        optOpt.platform                                         = _options.platform();
index 60147796dddd4e93a2a6d6d91076537aacedc575..4be0c5e264a1b10903b7cc1ff09aeb896540422e 100644 (file)
@@ -566,6 +566,18 @@ void SymbolTable::tentativeDefs(std::vector<const char*>& tents)
 }
 
 
+void SymbolTable::mustPreserveForBitcode(std::unordered_set<const char*>& syms)
+{
+       // return all names in _byNameTable that have no associated atom
+       for (const auto &entry: _byNameTable) {
+               const char* name = entry.first;
+               const ld::Atom* atom = _indirectBindingTable[entry.second];
+               if ( (atom == NULL) || (atom->definition() == ld::Atom::definitionProxy) )
+                       syms.insert(name);
+       }
+}
+
+
 bool SymbolTable::hasName(const char* name)                    
 { 
        NameToSlot::iterator pos = _byNameTable.find(name);
index ce2f1dcac522d92baed7a5bbaeb6af7f09bbe143..2708f1fce6a75d73cfb45618ea9d78da04f888f8 100644 (file)
@@ -120,6 +120,7 @@ public:
        unsigned int            updateCount()                                           { return _indirectBindingTable.size(); }
        void                            undefines(std::vector<const char*>& undefines);
        void                            tentativeDefs(std::vector<const char*>& undefines);
+       void                            mustPreserveForBitcode(std::unordered_set<const char*>& syms);
        void                            removeDeadAtoms();
        bool                            hasName(const char* name);
        bool                            hasExternalTentativeDefinitions()       { return _hasExternalTentativeDefinitions; }
index 0b5bc4a867b4dc821e62f5a6110307415f054d43..a856598108348ed33ea12dd04a53cfa2693658bf 100644 (file)
@@ -80,6 +80,7 @@ extern "C" double log2 ( double );
 #include "passes/objc.h"
 #include "passes/dylibs.h"
 #include "passes/bitcode_bundle.h"
+#include "passes/code_dedup.h"
 
 #include "parsers/archive_file.h"
 #include "parsers/macho_relocatable_file.h"
@@ -956,6 +957,7 @@ uint64_t InternalState::assignFileOffsets()
        lastSegName = "";
        ld::Internal::FinalSection* overlappingFixedSection = NULL;
        ld::Internal::FinalSection* overlappingFlowSection = NULL;
+       ld::Internal::FinalSection* prevSect = NULL;
        if ( log ) fprintf(stderr, "Regular layout segments:\n");
        for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
                ld::Internal::FinalSection* sect = *it;
@@ -985,7 +987,16 @@ uint64_t InternalState::assignFileOffsets()
                // update section info
                sect->address = address;
                sect->alignmentPaddingBytes = (address - unalignedAddress);
-               
+
+               // <rdar://problem/21994854> if first section is more aligned than segment, move segment start up to match
+               if ( (prevSect != NULL) && (prevSect->type() == ld::Section::typeFirstSection) && (strcmp(prevSect->segmentName(), sect->segmentName()) == 0) ) {
+                       assert(prevSect->size == 0);
+                       if ( prevSect->address != sect->address ) {
+                               prevSect->alignmentPaddingBytes += (sect->address - prevSect->address);
+                               prevSect->address = sect->address;
+                       }
+               }
+
                // sanity check size
                if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) 
                                                                                                                          && (_options.outputKind() != Options::kStaticExecutable) )
@@ -1021,6 +1032,7 @@ uint64_t InternalState::assignFileOffsets()
                // update running totals
                if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace )
                        address += sect->size;
+               prevSect = sect;
        }
        if ( overlappingFixedSection != NULL ) {
                fprintf(stderr, "Section layout:\n");
@@ -1213,7 +1225,8 @@ int main(int argc, const char* argv[])
                ld::passes::dylibs::doPass(options, state);     // must be after stubs and GOT passes
                ld::passes::order::doPass(options, state);
                state.markAtomsOrdered();
-               ld::passes::branch_shim::doPass(options, state);        // must be after stubs 
+               ld::passes::dedup::doPass(options, state);
+               ld::passes::branch_shim::doPass(options, state);        // must be after stubs
                ld::passes::branch_island::doPass(options, state);      // must be after stubs and order pass
                ld::passes::dtrace::doPass(options, state);
                ld::passes::compact_unwind::doPass(options, state);  // must be after order pass
index e297036efe41243d1241d73b821df7b860f64e51..89c091b84c9449821e0f464d4ed0e10fb1280652 100644 (file)
@@ -246,12 +246,13 @@ namespace dylib {
                };
                        
                                                                                        File(const char* pth, time_t modTime, Ordinal ord)
-                                                                                               : ld::File(pth, modTime, ord, Dylib), _dylibInstallPath(NULL),
+                                                                                               : ld::File(pth, modTime, ord, Dylib), _dylibInstallPath(NULL), _frameworkName(NULL),
                                                                                                _dylibTimeStamp(0), _dylibCurrentVersion(0), _dylibCompatibilityVersion(0),
                                                                                                _explicitlyLinked(false), _implicitlyLinked(false),
                                                                                                _lazyLoadedDylib(false), _forcedWeakLinked(false), _reExported(false),
                                                                                                _upward(false), _dead(false) { }
                                const char*                                     installPath() const                     { return _dylibInstallPath; }
+                               const char*                                     frameworkName() const           { return _frameworkName; }
                                uint32_t                                        timestamp() const                       { return _dylibTimeStamp; }
                                uint32_t                                        currentVersion() const          { return _dylibCurrentVersion; }
                                uint32_t                                        compatibilityVersion() const{ return _dylibCompatibilityVersion; }
@@ -285,13 +286,9 @@ namespace dylib {
                virtual bool                                            installPathVersionSpecific() const { return false; }
                virtual bool                                            appExtensionSafe() const = 0;
 
-       protected:
-               struct ReExportChain { ReExportChain* prev; const File* file; };
-               virtual std::pair<bool, bool>           hasWeakDefinitionImpl(const char* name) const = 0;
-               virtual bool                                            containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const = 0;
-               virtual void                                            assertNoReExportCycles(ReExportChain*) const = 0;
-
+       public:
                const char*                                                     _dylibInstallPath;
+               const char*                                                     _frameworkName;
                uint32_t                                                        _dylibTimeStamp;
                uint32_t                                                        _dylibCurrentVersion;
                uint32_t                                                        _dylibCompatibilityVersion;
@@ -446,7 +443,7 @@ struct Fixup
                                        // data-in-code markers
                                        kindDataInCodeStartData, kindDataInCodeStartJT8, kindDataInCodeStartJT16, 
                                        kindDataInCodeStartJT32, kindDataInCodeStartJTA32, kindDataInCodeEnd,
-                                       // linker optimzation hints
+                                       // linker optimization hints
                                        kindLinkerOptimizationHint,
                                        // pointer store combinations
                                        kindStoreTargetAddressLittleEndian32,   // kindSetTargetAddress + kindStoreLittleEndian32
@@ -895,8 +892,11 @@ public:
        AtomToSection                                                           atomToSection;          
        CStringSet                                                                      linkerOptionLibraries;
        CStringSet                                                                      linkerOptionFrameworks;
+       CStringSet                                                                      linkerOptionLibrariesProcessed;
+       CStringSet                                                                      linkerOptionFrameworksProcessed;
        std::vector<const ld::Atom*>                            indirectBindingTable;
        std::vector<const ld::relocatable::File*>       filesWithBitcode;
+       std::unordered_set<const char*>                         allUndefProxies;
        const ld::dylib::File*                                          bundleLoader;
        const Atom*                                                                     entryPoint;
        const Atom*                                                                     classicBindingHelper;
diff --git a/src/ld/parsers/generic_dylib_file.hpp b/src/ld/parsers/generic_dylib_file.hpp
new file mode 100644 (file)
index 0000000..301074f
--- /dev/null
@@ -0,0 +1,565 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2015 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@
+ */
+
+#ifndef __GENERIC_DYLIB_FILE_H__
+#define __GENERIC_DYLIB_FILE_H__
+
+#include "ld.hpp"
+#include "Options.h"
+#include <unordered_map>
+#include <unordered_set>
+
+namespace generic {
+namespace dylib {
+
+// forward reference
+template <typename A> class File;
+
+//
+// An ExportAtom has no content.  It exists so that the linker can track which
+// imported symbols came from which dynamic libraries.
+//
+template <typename A>
+class ExportAtom final : public ld::Atom
+{
+public:
+       ExportAtom(const File<A>& f, const char* nm, bool weakDef, bool tlv,
+                          typename A::P::uint_t address)
+               : ld::Atom(f._importProxySection, ld::Atom::definitionProxy,
+                                  (weakDef ? ld::Atom::combineByName : ld::Atom::combineNever),
+                                  ld::Atom::scopeLinkageUnit,
+                                  (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified),
+                                  symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)),
+                 _file(f),
+                 _name(nm),
+                 _address(address)
+       {}
+
+       // overrides of ld::Atom
+       virtual const ld::File* file() const override final { return &_file; }
+       virtual const char*             name() const override final { return _name; }
+       virtual uint64_t                size() const override final { return 0; }
+       virtual uint64_t                objectAddress() const override final { return _address; }
+       virtual void                    copyRawContent(uint8_t buffer[]) const override final { }
+
+       virtual void                    setScope(Scope) { }
+
+private:
+       using pint_t = typename A::P::uint_t;
+
+       virtual                                 ~ExportAtom() {}
+
+       const File<A>&                  _file;
+       const char*                             _name;
+       pint_t                                  _address;
+};
+
+
+//
+// An ImportAtom has no content.  It exists so that when linking a main executable flat-namespace
+// the imports of all flat dylibs are checked
+//
+template <typename A>
+class ImportAtom final : public ld::Atom
+{
+public:
+       ImportAtom(File<A>& f, std::vector<const char*>& imports);
+
+       // overrides of ld::Atom
+       virtual ld::File*                               file() const override final { return &_file; }
+       virtual const char*                             name() const override final { return "import-atom"; }
+       virtual uint64_t                                size() const override final { return 0; }
+       virtual uint64_t                                objectAddress() const override final { return 0; }
+       virtual ld::Fixup::iterator             fixupsBegin() const     override final { return &_undefs[0]; }
+       virtual ld::Fixup::iterator             fixupsEnd()     const override final { return &_undefs[_undefs.size()]; }
+       virtual void                                    copyRawContent(uint8_t buffer[]) const override final { }
+
+       virtual void                                    setScope(Scope)         { }
+
+private:
+       virtual                                                 ~ImportAtom() {}
+
+       File<A>&                                                _file;
+       mutable std::vector<ld::Fixup>  _undefs;
+};
+
+template <typename A>
+ImportAtom<A>::ImportAtom(File<A>& f, std::vector<const char*>& imports)
+       : ld::Atom(f._flatDummySection, ld::Atom::definitionRegular, ld::Atom::combineNever,
+                          ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, symbolTableNotIn, false,
+                          false, false, ld::Atom::Alignment(0)),
+         _file(f)
+{
+       for(auto *name : imports)
+               _undefs.emplace_back(0, ld::Fixup::k1of1, ld::Fixup::kindNone, false, strdup(name));
+}
+
+//
+// A generic representation for the dynamic library files we support (Mach-O and text-based stubs).
+// Common state and functionality is consolidated in this class.
+//
+template <typename A>
+class File : public ld::dylib::File
+{
+public:
+       File(const char* path, time_t mTime, ld::File::Ordinal ordinal, Options::Platform platform,
+                uint32_t linkMinOSVersion, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs,
+                bool allowSimToMacOSX, bool addVers);
+
+       // overrides of ld::File
+       virtual bool                                                    forEachAtom(ld::File::AtomHandler&) const override final;
+       virtual bool                                                    justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const override final;
+       virtual ld::File::ObjcConstraint                objCConstraint() const override final { return _objcConstraint; }
+       virtual uint8_t                                                 swiftVersion() const override final { return _swiftVersion; }
+       virtual uint32_t                                                minOSVersion() const override final { return _minVersionInDylib; }
+       virtual uint32_t                                                platformLoadCommand() const     override final { return _platformInDylib; }
+       virtual ld::Bitcode*                                    getBitcode() const override final { return _bitcode.get(); }
+
+
+       // overrides of ld::dylib::File
+       virtual void                                                    processIndirectLibraries(ld::dylib::File::DylibHandler*, bool addImplicitDylibs) override final;
+       virtual bool                                                    providedExportAtom() const      override final { return _providedAtom; }
+       virtual const char*                                             parentUmbrella() const override final { return _parentUmbrella; }
+       virtual const std::vector<const char*>* allowableClients() const override final { return _allowableClients.empty() ? nullptr : &_allowableClients; }
+       virtual bool                                                    hasWeakExternals() const override final { return _hasWeakExports; }
+       virtual bool                                                    deadStrippable() const override final { return _deadStrippable; }
+       virtual bool                                                    hasWeakDefinition(const char* name) const override final;
+       virtual bool                                                    hasPublicInstallName() const override final { return _hasPublicInstallName; }
+       virtual bool                                                    allSymbolsAreWeakImported() const override final;
+       virtual bool                                                    installPathVersionSpecific() const override final { return _installPathOverride; }
+       virtual bool                                                    appExtensionSafe() const override final { return _appExtensionSafe; };
+
+
+       bool                                                                    wrongOS() const { return _wrongOS; }
+
+private:
+       using pint_t = typename A::P::uint_t;
+
+       friend class ExportAtom<A>;
+       friend class ImportAtom<A>;
+
+       struct CStringHash {
+               std::size_t operator()(const char* __s) const {
+                       unsigned long __h = 0;
+                       for ( ; *__s; ++__s)
+                               __h = 5 * __h + *__s;
+                       return size_t(__h);
+               };
+       };
+
+protected:
+       struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; pint_t address; };
+       struct Dependent {
+        const char* path;
+        File<A>* dylib;
+        bool reExport;
+
+        Dependent(const char* path, bool reExport)
+            : path(path), dylib(nullptr), reExport(reExport) {}
+    };
+       struct ReExportChain { ReExportChain* prev; const File* file; };
+
+private:
+       using NameToAtomMap = std::unordered_map<const char*, AtomAndWeak, ld::CStringHash, ld::CStringEquals>;
+       using NameSet = std::unordered_set<const char*, CStringHash, ld::CStringEquals>;
+
+       std::pair<bool, bool>           hasWeakDefinitionImpl(const char* name) const;
+       bool                                            containsOrReExports(const char* name, bool& weakDef, bool& tlv, pint_t& addr) const;
+       void                                            assertNoReExportCycles(ReExportChain*) const;
+
+protected:
+       bool                                            isPublicLocation(const char* path) const;
+       void                                            addSymbol(const char* name, bool weak = false, bool tlv = false, pint_t address = 0);
+
+private:
+       ld::Section                                                     _importProxySection;
+       ld::Section                                                     _flatDummySection;
+       mutable bool                                            _providedAtom;
+       bool                                                            _indirectDylibsProcessed;
+
+protected:
+       mutable NameToAtomMap                           _atoms;
+       NameSet                                                         _ignoreExports;
+       std::vector<Dependent>                          _dependentDylibs;
+       ImportAtom<A>*                                          _importAtom;
+       std::vector<const char*>                        _allowableClients;
+       const char*                                                     _parentUmbrella;
+       std::unique_ptr<ld::Bitcode>            _bitcode;
+       const Options::Platform                         _platform;
+       ld::File::ObjcConstraint                        _objcConstraint;
+       const uint32_t                                          _linkMinOSVersion;
+       uint32_t                                                        _minVersionInDylib;
+       uint32_t                                                        _platformInDylib;
+       uint8_t                                                         _swiftVersion;
+       bool                                                            _wrongOS;
+       bool                                                            _linkingFlat;
+       bool                                                            _noRexports;
+       bool                                                            _explictReExportFound;
+       bool                                                            _implicitlyLinkPublicDylibs;
+       bool                                                            _installPathOverride;
+       bool                                                            _hasWeakExports;
+       bool                                                            _deadStrippable;
+       bool                                                            _hasPublicInstallName;
+       bool                                                            _appExtensionSafe;
+
+       const bool                                                      _allowSimToMacOSXLinking;
+       const bool                                                      _addVersionLoadCommand;
+
+       static bool                                                     _s_logHashtable;
+};
+
+template <typename A>
+bool File<A>::_s_logHashtable = false;
+
+template <typename A>
+File<A>::File(const char* path, time_t mTime, ld::File::Ordinal ord,  Options::Platform platform,
+                         uint32_t linkMinOSVersion, bool linkingFlatNamespace,
+                         bool hoistImplicitPublicDylibs,
+                         bool allowSimToMacOSX, bool addVers)
+       : ld::dylib::File(path, mTime, ord),
+         _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true),
+         _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true),
+         _providedAtom(false),
+         _indirectDylibsProcessed(false),
+         _importAtom(nullptr),
+         _parentUmbrella(nullptr),
+         _platform(platform),
+         _objcConstraint(ld::File::objcConstraintNone),
+         _linkMinOSVersion(linkMinOSVersion),
+         _minVersionInDylib(0),
+         _platformInDylib(Options::kPlatformUnknown),
+         _swiftVersion(0),
+         _wrongOS(false),
+         _linkingFlat(linkingFlatNamespace),
+         _noRexports(false),
+         _explictReExportFound(false),
+         _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs),
+         _installPathOverride(false),
+         _hasWeakExports(false),
+         _deadStrippable(false),
+         _hasPublicInstallName(false),
+         _appExtensionSafe(false),
+         _allowSimToMacOSXLinking(allowSimToMacOSX),
+         _addVersionLoadCommand(addVers)
+{
+}
+
+template <typename A>
+std::pair<bool, bool> File<A>::hasWeakDefinitionImpl(const char* name) const
+{
+       const auto pos = _atoms.find(name);
+       if ( pos != this->_atoms.end() )
+               return std::make_pair(true, pos->second.weakDef);
+
+       // look in re-exported libraries.
+       for (const auto &dep : _dependentDylibs) {
+               if ( dep.reExport ) {
+                       auto ret = dep.dylib->hasWeakDefinitionImpl(name);
+                       if ( ret.first )
+                               return ret;
+               }
+       }
+       return std::make_pair(false, false);
+}
+
+template <typename A>
+bool File<A>::hasWeakDefinition(const char* name) const
+{
+       // If we are supposed to ignore this export, then pretend we don't have it.
+       if ( _ignoreExports.count(name) != 0 )
+               return false;
+
+       return hasWeakDefinitionImpl(name).second;
+}
+
+template <typename A>
+bool File<A>::containsOrReExports(const char* name, bool& weakDef, bool& tlv, pint_t& addr) const
+{
+       if ( _ignoreExports.count(name) != 0 )
+               return false;
+
+       // check myself
+       const auto pos = _atoms.find(name);
+       if ( pos != _atoms.end() ) {
+               weakDef = pos->second.weakDef;
+               tlv = pos->second.tlv;
+               addr = pos->second.address;
+               return true;
+       }
+
+       // check dylibs I re-export
+       for (const auto& dep : _dependentDylibs) {
+               if ( dep.reExport && !dep.dylib->implicitlyLinked() ) {
+                       if ( dep.dylib->containsOrReExports(name, weakDef, tlv, addr) )
+                               return true;
+               }
+       }
+       
+       return false;
+}
+
+template <typename A>
+bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
+{
+       handler.doFile(*this);
+
+       // if doing flatnamespace and need all this dylib's imports resolve
+       // add atom which references alls undefines in this dylib
+       if ( _importAtom != nullptr ) {
+               handler.doAtom(*_importAtom);
+               return true;
+       }
+       return false;
+}
+
+template <typename A>
+bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const
+{
+       // If we are supposed to ignore this export, then pretend we don't have it.
+       if ( _ignoreExports.count(name) != 0 )
+               return false;
+
+
+       AtomAndWeak bucket;
+       if ( containsOrReExports(name, bucket.weakDef, bucket.tlv, bucket.address) ) {
+               bucket.atom = new ExportAtom<A>(*this, name, bucket.weakDef, bucket.tlv, bucket.address);
+               _atoms[name] = bucket;
+               _providedAtom = true;
+               if ( _s_logHashtable )
+                       fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path());
+               // call handler with new export atom
+               handler.doAtom(*bucket.atom);
+               return true;
+       }
+
+       return false;
+}
+
+template <typename A>
+void File<A>::assertNoReExportCycles(ReExportChain* prev) const
+{
+       // recursively check my re-exported dylibs
+       ReExportChain chain = { prev, this };
+       for (const auto &dep : _dependentDylibs) {
+               if ( dep.reExport ) {
+                       auto* child = dep.dylib;
+                       // check child is not already in chain
+                       for (auto* p = prev; p != nullptr; p = p->prev) {
+                               if ( p->file == child ) {
+                                       throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path());
+                               }
+                       }
+                       if ( dep.dylib != nullptr )
+                               dep.dylib->assertNoReExportCycles(&chain);
+               }
+       }
+}
+
+template <typename A>
+void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs)
+{
+       // only do this once
+       if ( _indirectDylibsProcessed )
+               return;
+
+       const static bool log = false;
+       if ( log )
+               fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath());
+       if ( _linkingFlat ) {
+               for (auto &dep : _dependentDylibs)
+                       dep.dylib = (File<A>*)handler->findDylib(dep.path, this->path());
+       }
+       else if ( _noRexports ) {
+               // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
+       }
+       else {
+               // two-level, might have re-exports
+               for (auto &dep : this->_dependentDylibs) {
+                       if ( dep.reExport ) {
+                               if ( log )
+                                       fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), dep.path);
+                               // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child
+                               dep.dylib = (File<A>*)handler->findDylib(dep.path, this->path());
+                               if ( dep.dylib->hasPublicInstallName() && !dep.dylib->wrongOS() ) {
+                                       // promote this child to be automatically added as a direct dependent if this already is
+                                       if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(dep.path, dep.dylib->installPath()) == 0) ) {
+                                               if ( log )
+                                                       fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", dep.dylib->installPath());
+                                               dep.dylib->setImplicitlyLinked();
+                                       }
+                                       else if ( dep.dylib->explicitlyLinked() || dep.dylib->implicitlyLinked() ) {
+                                               if ( log )
+                                                       fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n");
+                                       }
+                                       else {
+                                               if ( log )
+                                                       fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), dep.path);
+                                       }
+                               }
+                               else {
+                                       // add all child's symbols to me
+                                       if ( log )
+                                               fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), dep.path);
+                               }
+                       }
+                       else if ( !_explictReExportFound ) {
+                               // see if child contains LC_SUB_FRAMEWORK with my name
+                               dep.dylib = (File<A>*)handler->findDylib(dep.path, this->path());
+                               const char* parentUmbrellaName = dep.dylib->parentUmbrella();
+                               if ( parentUmbrellaName != nullptr ) {
+                                       const char* parentName = this->path();
+                                       const char* lastSlash = strrchr(parentName, '/');
+                                       if ( (lastSlash != nullptr) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) {
+                                               // add all child's symbols to me
+                                               dep.reExport = true;
+                                               if ( log )
+                                                       fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->installPath(), dep.path);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       // check for re-export cycles
+       ReExportChain chain = { nullptr, this };
+       this->assertNoReExportCycles(&chain);
+       
+       _indirectDylibsProcessed = true;
+}
+
+template <typename A>
+bool File<A>::isPublicLocation(const char* path) const
+{
+       // -no_implicit_dylibs disables this optimization
+       if ( ! _implicitlyLinkPublicDylibs )
+               return false;
+
+       // /usr/lib is a public location
+       if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == nullptr) )
+               return true;
+
+       // /System/Library/Frameworks/ is a public location
+       if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) {
+               const char* frameworkDot = strchr(&path[27], '.');
+               // but only top level framework
+               // /System/Library/Frameworks/Foo.framework/Versions/A/Foo                 ==> true
+               // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib         ==> false
+               // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar   ==> false
+               // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false
+               if ( frameworkDot != nullptr ) {
+                       int frameworkNameLen = frameworkDot - &path[27];
+                       if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 )
+                               return true;
+               }
+       }
+       
+       return false;
+}
+
+template <typename A>
+void File<A>::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address)
+{
+       // symbols that start with $ld$ are meta-data to the static linker
+       // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
+       if ( strncmp(name, "$ld$", 4) == 0 ) {
+               //    $ld$ <action> $ <condition> $ <symbol-name>
+               const char* symAction = &name[4];
+               const char* symCond = strchr(symAction, '$');
+               if ( symCond != nullptr ) {
+                       char curOSVers[16];
+                       sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF));
+                       if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) {
+                               const char* symName = strchr(&symCond[1], '$');
+                               if ( symName != nullptr ) {
+                                       ++symName;
+                                       if ( strncmp(symAction, "hide$", 5) == 0 ) {
+                                               if ( _s_logHashtable )
+                                                       fprintf(stderr, "  adding %s to ignore set for %s\n", symName, this->path());
+                                               _ignoreExports.insert(strdup(symName));
+                                               return;
+                                       }
+                                       else if ( strncmp(symAction, "add$", 4) == 0 ) {
+                                               this->addSymbol(symName, weakDef);
+                                               return;
+                                       }
+                                       else if ( strncmp(symAction, "install_name$", 13) == 0 ) {
+                                               _dylibInstallPath = strdup(symName);
+                                               _installPathOverride = true;
+                                               // <rdar://problem/14448206> CoreGraphics redirects to ApplicationServices, but with wrong compat version
+                                               if ( strcmp(_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 )
+                                                       _dylibCompatibilityVersion = Options::parseVersionNumber32("1.0");
+                                               return;
+                                       }
+                                       else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) {
+                                               _dylibCompatibilityVersion = Options::parseVersionNumber32(symName);
+                                               return;
+                                       }
+                                       else {
+                                               warning("bad symbol action: %s in dylib %s", name, this->path());
+                                       }
+                               }
+                       }
+               }
+               else {
+                       warning("bad symbol condition: %s in dylib %s", name, this->path());
+               }
+       }
+
+       // add symbol as possible export if we are not supposed to ignore it
+       if ( _ignoreExports.count(name) == 0 ) {
+               AtomAndWeak bucket = { nullptr, weakDef, tlv, address };
+               if ( this->_s_logHashtable )
+                       fprintf(stderr, "  adding %s to hash table for %s\n", name, this->path());
+               _atoms[strdup(name)] = bucket;
+       }
+}
+
+// <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
+template <typename A>
+bool File<A>::allSymbolsAreWeakImported() const
+{
+       bool foundNonWeakImport = false;
+       bool foundWeakImport = false;
+       //fprintf(stderr, "%s:\n", this->path());
+       for (const auto &it : _atoms) {
+               auto* atom = it.second.atom;
+               if ( atom != nullptr ) {
+                       if ( atom->weakImported() )
+                               foundWeakImport = true;
+                       else
+                               foundNonWeakImport = true;
+                       //fprintf(stderr, "  weak_import=%d, name=%s\n", atom->weakImported(), it->first);
+               }
+       }
+
+       // don't automatically weak link dylib with no imports
+       // so at least one weak import symbol and no non-weak-imported symbols must be found
+       return foundWeakImport && !foundNonWeakImport;
+}
+
+
+} // end namespace dylib
+} // end namespace generic
+
+#endif // __GENERIC_DYLIB_FILE_H__
index d81e833994c1e4b82de353fcee02f99b8d2b25cd..f0f5f1047cc8a7eee907b37e0e5539a83481b2c5 100644 (file)
@@ -317,7 +317,8 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons
        objOpts.srcKind                         = ld::relocatable::File::kSourceLTO;
        objOpts.treateBitcodeAsData = false;
        objOpts.usingBitcode            = options.bitcodeBundle;
-       
+       objOpts.maxDefaultCommonAlignment = options.maxDefaultCommonAlignment;
+
        // mach-o parsing is done in-memory, but need path for debug notes
        const char* path = "/tmp/lto.o";
        time_t modTime = 0;
@@ -1022,7 +1023,6 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar
        return Parser::validFile(fileContent, fileLength, architecture, subarch);
 }
 
-
 static ld::relocatable::File *parseImpl(
           const uint8_t *fileContent, uint64_t fileLength, const char *path,
           time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture,
@@ -1043,6 +1043,12 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
                                                                cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles,
                                                                bool verboseOptimizationHints)
 {
+       // do light weight check before acquiring lock
+       if ( fileLength < 4 )
+               return NULL;
+       if ( (fileContent[0] != 0xDE) || (fileContent[1] != 0xC0) || (fileContent[2] != 0x17) || (fileContent[3] != 0x0B) )
+               return NULL;
+
        // Note: Once lto_module_create_in_local_context() and friends are thread safe
        // this lock can be removed.
        Mutex lock;
index 65682326fda8d772f0ffe85ea538fd7f577e75ad..38cfcd186f3d8cb442d2e0bc7a53648c9d7aaaea 100644 (file)
@@ -37,7 +37,7 @@ extern const char* archName(const uint8_t* fileContent, uint64_t fileLength);
 
 extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch);
 
-extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, 
+extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
                                                                        const char* path, time_t modTime, ld::File::Ordinal ordinal,
                                                                        cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles,
                                                                        bool verboseOptimizationHints);
@@ -62,6 +62,7 @@ struct OptimizeOptions {
        bool                                                            simulator;
        bool                                                            ignoreMismatchPlatform;
        bool                                                            bitcodeBundle;
+       uint8_t                                                         maxDefaultCommonAlignment;
        cpu_type_t                                                      arch;
        const char*                                                     mcpu;
        Options::Platform                                       platform;
index 573f06c211ffd1b9379e621c32051913244388ac..24d69c861e28d40004d171bea4fabd019306bf95 100644 (file)
 #include <set>
 #include <map>
 #include <algorithm>
-#include <unordered_map>
-#include <unordered_set>
 
 #include "Architectures.hpp"
 #include "Bitcode.hpp"
 #include "MachOFileAbstraction.hpp"
 #include "MachOTrie.hpp"
+#include "generic_dylib_file.hpp"
 #include "macho_dylib_file.h"
 #include "../code-sign-blobs/superblob.h"
 
 namespace mach_o {
 namespace dylib {
 
-
-// forward reference
-template <typename A> class File;
-
-
-//
-// An ExportAtom has no content.  It exists so that the linker can track which imported
-// symbols came from which dynamic libraries.
-//
-template <typename A>
-class ExportAtom : public ld::Atom
-{
-public:
-                                                                                       ExportAtom(const File<A>& f, const char* nm, bool weakDef, 
-                                                                                                               bool tlv, typename A::P::uint_t address)
-                                                                                               : ld::Atom(f._importProxySection, ld::Atom::definitionProxy, 
-                                                                                                       (weakDef? ld::Atom::combineByName : ld::Atom::combineNever),
-                                                                                                       ld::Atom::scopeLinkageUnit, 
-                                                                                                       (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified), 
-                                                                                                       symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), 
-                                                                                                       _file(f), _name(nm), _address(address) {}
-       // overrides of ld::Atom
-       virtual const ld::File*                                         file() const            { return &_file; }
-       virtual const char*                                                     name() const            { return _name; }
-       virtual uint64_t                                                        size() const            { return 0; }
-       virtual uint64_t                                                        objectAddress() const { return _address; }
-       virtual void                                                            copyRawContent(uint8_t buffer[]) const { }
-       virtual void                                                            setScope(Scope)         { }
-
-protected:
-       typedef typename A::P                                   P;
-       typedef typename A::P::uint_t                   pint_t;
-
-       virtual                                                                 ~ExportAtom() {}
-
-       const File<A>&                                                  _file;
-       const char*                                                             _name;
-       pint_t                                                                  _address;
-};
-
-
-
-//
-// An ImportAtom has no content.  It exists so that when linking a main executable flat-namespace
-// the imports of all flat dylibs are checked
-//
-template <typename A>
-class ImportAtom : public ld::Atom
-{
-public:
-                                                                                               ImportAtom(File<A>& f, std::vector<const char*>& imports);
-
-       // overrides of ld::Atom
-       virtual ld::File*                                                       file() const            { return &_file; }
-       virtual const char*                                                     name() const            { return "import-atom"; }
-       virtual uint64_t                                                        size() const            { return 0; }
-       virtual uint64_t                                                        objectAddress() const { return 0; }
-       virtual void                                                            copyRawContent(uint8_t buffer[]) const { }
-       virtual void                                                            setScope(Scope)         { }
-       virtual ld::Fixup::iterator                                     fixupsBegin() const     { return &_undefs[0]; }
-       virtual ld::Fixup::iterator                                     fixupsEnd()     const   { return &_undefs[_undefs.size()]; }
-
-protected:
-       typedef typename A::P                                   P;
-
-       virtual                                                                 ~ImportAtom() {}
-
-
-       File<A>&                                                                _file;
-       mutable std::vector<ld::Fixup>                  _undefs;
-};
-
-template <typename A>
-ImportAtom<A>::ImportAtom(File<A>& f, std::vector<const char*>& imports)
-: ld::Atom(f._flatDummySection, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, 
-       ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), _file(f) 
-{ 
-       for(std::vector<const char*>::iterator it=imports.begin(); it != imports.end(); ++it) {
-               _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNone, false, strdup(*it)));
-       }
-}
-
-
-
 //
 // The reader for a dylib extracts all exported symbols names from the memory-mapped
 // dylib, builds a hash table, then unmaps the file.  This is an important memory
 // savings for large dylibs.
 //
 template <typename A>
-class File : public ld::dylib::File
+class File final : public generic::dylib::File<A>
 {
+       using Base = generic::dylib::File<A>;
+
 public:
-       static bool                                                             validFile(const uint8_t* fileContent, bool executableOrDylib);
+       static bool                                                             validFile(const uint8_t* fileContent, bool executableOrDylib, bool subTypeMustMatch=false);
                                                                                        File(const uint8_t* fileContent, uint64_t fileLength, const char* path,   
                                                                                                        time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, 
                                                                                                        bool linkingMainExecutable, bool hoistImplicitPublicDylibs, 
@@ -148,106 +65,26 @@ public:
                                                                                                        bool addVers,  bool buildingForSimulator,
                                                                                                        bool logAllFiles, const char* installPath,
                                                                                                        bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode);
-       virtual                                                                 ~File() {}
-
-       // overrides of ld::File
-       virtual bool                                                    forEachAtom(ld::File::AtomHandler&) const;
-       virtual bool                                                    justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const;
-       virtual ld::File::ObjcConstraint                objCConstraint() const          { return _objcContraint; }
-       virtual uint8_t                                                 swiftVersion() const            { return _swiftVersion; }
-       virtual uint32_t                                                minOSVersion() const            { return _minVersionInDylib; }
-       virtual uint32_t                                                platformLoadCommand() const     { return _platformInDylib; }
-
-       // overrides of ld::dylib::File
-       virtual void                                                    processIndirectLibraries(ld::dylib::File::DylibHandler*, bool);
-       virtual bool                                                    providedExportAtom() const      { return _providedAtom; }
-       virtual const char*                                             parentUmbrella() const          { return _parentUmbrella; }
-       virtual const std::vector<const char*>* allowableClients() const        { return _allowableClients.size() != 0 ? &_allowableClients : NULL; }
-       virtual bool                                                    hasWeakExternals() const        { return _hasWeakExports; }
-       virtual bool                                                    deadStrippable() const          { return _deadStrippable; }
-       virtual bool                                                    hasPublicInstallName() const{ return _hasPublicInstallName; }
-       virtual bool                                                    hasWeakDefinition(const char* name) const;
-       virtual bool                                                    allSymbolsAreWeakImported() const;
-       virtual bool                                                    installPathVersionSpecific() const { return _installPathOverride; }
-       virtual bool                                                    appExtensionSafe() const        { return _appExtensionSafe; };
-       virtual ld::Bitcode*                                    getBitcode() const                      { return _bitcode.get(); }
-
-protected:
-       virtual void                                                    assertNoReExportCycles(ReExportChain*) const;
+       virtual                                                                 ~File() noexcept {}
 
 private:
-       typedef typename A::P                                   P;
-       typedef typename A::P::E                                E;
-       typedef typename A::P::uint_t                   pint_t;
-
-       friend class ExportAtom<A>;
-       friend class ImportAtom<A>;
-
-       struct CStringHash {
-               std::size_t operator()(const char* __s) const {
-                       unsigned long __h = 0;
-                       for ( ; *__s; ++__s)
-                               __h = 5 * __h + *__s;
-                       return size_t(__h);
-               };
-       };
-       struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; uint64_t address; };
-       typedef std::unordered_map<const char*, AtomAndWeak, ld::CStringHash, ld::CStringEquals> NameToAtomMap;
-       typedef std::unordered_set<const char*, CStringHash, ld::CStringEquals>  NameSet;
+       using P = typename A::P;
+       using E = typename A::P::E;
 
-       struct Dependent { const char* path; File<A>* dylib; bool reExport; };
-
-       virtual std::pair<bool, bool>                           hasWeakDefinitionImpl(const char* name) const;
-       virtual bool                                                            containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const;
-       bool                                                                            isPublicLocation(const char* pth);
-       bool                                                                            wrongOS() { return _wrongOS; }
-       void                                                                            addSymbol(const char* name, bool weak, bool tlv, pint_t address);
-       void                                                                            addDyldFastStub();
-       void                                                                            buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo,
+       void                            addDyldFastStub();
+       void                            buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo,
                                                                                                                                                                const uint8_t* fileContent);
-       void                                                                            buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo, 
+       void                            buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
                                                                                                                const macho_nlist<P>* symbolTable, const char* strings,
                                                                                                                const uint8_t* fileContent);
-       static uint32_t                                                         parseVersionNumber32(const char* versionString);
-       static const char*                                                      objCInfoSegmentName();
-       static const char*                                                      objCInfoSectionName();
+       static const char*      objCInfoSegmentName();
+       static const char*      objCInfoSectionName();
 
-       const Options::Platform                                         _platform;
-       const uint32_t                                                          _linkMinOSVersion;
-       const bool                                                                      _allowSimToMacOSXLinking;
-       const bool                                                                      _addVersionLoadCommand;
-       bool                                                                            _linkingFlat;
-       bool                                                                            _implicitlyLinkPublicDylibs;
-       ld::File::ObjcConstraint                                        _objcContraint;
-       uint8_t                                                                         _swiftVersion;
-       ld::Section                                                                     _importProxySection;
-       ld::Section                                                                     _flatDummySection;
-       std::vector<Dependent>                                          _dependentDylibs;
-       std::vector<const char*>                                        _allowableClients;
-       mutable NameToAtomMap                                           _atoms;
-       NameSet                                                                         _ignoreExports;
-       const char*                                                                     _parentUmbrella;
-       ImportAtom<A>*                                                          _importAtom;
-       bool                                                                            _noRexports;
-       bool                                                                            _hasWeakExports;
-       bool                                                                            _deadStrippable;
-       bool                                                                            _hasPublicInstallName;
-       mutable bool                                                            _providedAtom;
-       bool                                                                            _explictReExportFound;
-       bool                                                                            _wrongOS;
-       bool                                                                            _installPathOverride;
-       bool                                                                            _indirectDylibsProcessed;
-       bool                                                                            _appExtensionSafe;
-       bool                                                                            _usingBitcode;
-       uint32_t                                                                        _minVersionInDylib;
-       uint32_t                                                                        _platformInDylib;
-       std::unique_ptr<ld::Bitcode>                            _bitcode;
-       
-       static bool                                                                     _s_logHashtable;
-};
 
-template <typename A>
-bool File<A>::_s_logHashtable = false;
+       uint64_t  _fileLength;
+       uint32_t  _linkeditStartOffset;
+
+};
 
 template <> const char* File<x86_64>::objCInfoSegmentName() { return "__DATA"; }
 template <> const char* File<arm>::objCInfoSegmentName() { return "__DATA"; }
@@ -258,22 +95,13 @@ template <> const char* File<arm>::objCInfoSectionName() { return "__objc_imagei
 template <typename A> const char* File<A>::objCInfoSectionName() { return "__image_info"; }
 
 template <typename A>
-File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, ld::File::Ordinal ord,
-                               bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
-                               Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
-                               bool logAllFiles, const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode)
-       : ld::dylib::File(strdup(pth), mTime, ord), 
-       _platform(platform), _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), 
-       _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs),
-       _objcContraint(ld::File::objcConstraintNone), _swiftVersion(0),
-       _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true),
-       _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true),
-       _parentUmbrella(NULL), _importAtom(NULL),
-       _noRexports(false), _hasWeakExports(false), 
-       _deadStrippable(false), _hasPublicInstallName(false), 
-        _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), 
-       _indirectDylibsProcessed(false), _appExtensionSafe(false), _usingBitcode(usingBitcode),
-       _minVersionInDylib(0), _platformInDylib(Options::kPlatformUnknown)
+File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime,
+                         ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable,
+                         bool hoistImplicitPublicDylibs, Options::Platform platform, uint32_t linkMinOSVersion,
+                         bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles,
+                         const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode)
+       : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, linkingFlatNamespace,
+                  hoistImplicitPublicDylibs,   allowSimToMacOSX, addVers), _fileLength(fileLength), _linkeditStartOffset(0)
 {
        const macho_header<P>* header = (const macho_header<P>*)fileContent;
        const uint32_t cmd_count = header->ncmds();
@@ -282,7 +110,7 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
 
        // write out path for -t option
        if ( logAllFiles )
-               printf("%s\n", pth);
+               printf("%s\n", path);
 
        // a "blank" stub has zero load commands
        if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) {      
@@ -293,32 +121,33 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
 
 
        // optimize the case where we know there is no reason to look at indirect dylibs
-       _noRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS) 
-                                       || (header->filetype() == MH_BUNDLE) 
-                                       || (header->filetype() == MH_EXECUTE);  // bundles and exectuables can be used via -bundle_loader
-       _hasWeakExports = (header->flags() & MH_WEAK_DEFINES);
-       _deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB);
-       _appExtensionSafe = (header->flags() & MH_APP_EXTENSION_SAFE);
+       this->_noRexports =    (header->flags() & MH_NO_REEXPORTED_DYLIBS)
+                                               || (header->filetype() == MH_BUNDLE)
+                                           || (header->filetype() == MH_EXECUTE);  // bundles and exectuables can be used via -bundle_loader
+       this->_hasWeakExports = (header->flags() & MH_WEAK_DEFINES);
+       this->_deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB);
+       this->_appExtensionSafe = (header->flags() & MH_APP_EXTENSION_SAFE);
        
        // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format
-       const macho_dysymtab_command<P>* dynamicInfo = NULL;
-       const macho_dyld_info_command<P>* dyldInfo = NULL;
-       const macho_nlist<P>* symbolTable = NULL;
-       const char*     strings = NULL;
+       const macho_dysymtab_command<P>* dynamicInfo = nullptr;
+       const macho_dyld_info_command<P>* dyldInfo = nullptr;
+       const macho_nlist<P>* symbolTable = nullptr;
+       const macho_symtab_command<P>* symtab = nullptr;
+       const char*     strings = nullptr;
        bool compressedLinkEdit = false;
        uint32_t dependentLibCount = 0;
        Options::Platform lcPlatform = Options::kPlatformUnknown;
        const macho_load_command<P>* cmd = cmds;
        for (uint32_t i = 0; i < cmd_count; ++i) {
                macho_dylib_command<P>* dylibID;
-               const macho_symtab_command<P>* symtab;
+               uint32_t cmdLength = cmd->cmdsize();
                switch (cmd->cmd()) {
                        case LC_SYMTAB:
                                symtab = (macho_symtab_command<P>*)cmd;
                                symbolTable = (const macho_nlist<P>*)((char*)header + symtab->symoff());
                                strings = (char*)header + symtab->stroff();
                                if ( (symtab->stroff() + symtab->strsize()) > fileLength )
-                                       throwf("mach-o string pool extends beyond end of file in %s", pth);
+                                       throwf("mach-o string pool extends beyond end of file in %s", path);
                                break;
                        case LC_DYSYMTAB:
                                dynamicInfo = (macho_dysymtab_command<P>*)cmd;
@@ -330,27 +159,31 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
                                break;
                        case LC_ID_DYLIB:
                                dylibID = (macho_dylib_command<P>*)cmd;
-                               _dylibInstallPath                       = strdup(dylibID->name());
-                               _dylibTimeStamp                         = dylibID->timestamp();
-                               _dylibCurrentVersion            = dylibID->current_version();
-                               _dylibCompatibilityVersion      = dylibID->compatibility_version();
-                               _hasPublicInstallName           = isPublicLocation(_dylibInstallPath);
+                               if ( dylibID->name_offset() > cmdLength )
+                                       throwf("malformed mach-o: LC_ID_DYLIB load command has offset (%u) outside its size (%u)", dylibID->name_offset(), cmdLength);
+                               if ( (dylibID->name_offset() + strlen(dylibID->name()) + 1) > cmdLength )
+                                       throwf("malformed mach-o: LC_ID_DYLIB load command string extends beyond end of load command");
+                               this->_dylibInstallPath                         = strdup(dylibID->name());
+                               this->_dylibTimeStamp                           = dylibID->timestamp();
+                               this->_dylibCurrentVersion                      = dylibID->current_version();
+                               this->_dylibCompatibilityVersion        = dylibID->compatibility_version();
+                               this->_hasPublicInstallName                     = this->isPublicLocation(this->_dylibInstallPath);
                                break;
                        case LC_LOAD_DYLIB:
                        case LC_LOAD_WEAK_DYLIB:
                                ++dependentLibCount;
                                break;
                        case LC_REEXPORT_DYLIB:
-                               _explictReExportFound = true;
+                               this->_explictReExportFound = true;
                                ++dependentLibCount;
                                break;
                        case LC_SUB_FRAMEWORK:
-                               _parentUmbrella = strdup(((macho_sub_framework_command<P>*)cmd)->umbrella());
+                               this->_parentUmbrella = strdup(((macho_sub_framework_command<P>*)cmd)->umbrella());
                                break;
                        case LC_SUB_CLIENT:
-                               _allowableClients.push_back(strdup(((macho_sub_client_command<P>*)cmd)->client()));
+                               this->_allowableClients.push_back(strdup(((macho_sub_client_command<P>*)cmd)->client()));
                                // <rdar://problem/20627554> Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
-                               _hasPublicInstallName = false;
+                               this->_hasPublicInstallName = false;
                                break;
                        case LC_VERSION_MIN_MACOSX:
                        case LC_VERSION_MIN_IPHONEOS:
@@ -358,9 +191,9 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
        #if SUPPORT_APPLE_TV
                        case LC_VERSION_MIN_TVOS:
        #endif
-                               _minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command<P>*)cmd)->version();
-                               _platformInDylib = cmd->cmd();
-                               lcPlatform = Options::platformForLoadCommand(_platformInDylib);
+                               this->_minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command<P>*)cmd)->version();
+                               this->_platformInDylib = cmd->cmd();
+                               lcPlatform = Options::platformForLoadCommand(this->_platformInDylib);
                                break;
                        case LC_CODE_SIGNATURE:
                                break;
@@ -384,17 +217,17 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
                                                        if ( (sect->size() >= 8) && (contents[0] == 0) ) {
                                                                uint32_t flags = E::get32(contents[1]);
                                                                if ( (flags & 4) == 4 )
-                                                                       _objcContraint = ld::File::objcConstraintGC;
+                                                                       this->_objcConstraint = ld::File::objcConstraintGC;
                                                                else if ( (flags & 2) == 2 )
-                                                                       _objcContraint = ld::File::objcConstraintRetainReleaseOrGC;
+                                                                       this->_objcConstraint = ld::File::objcConstraintRetainReleaseOrGC;
                                                                else if ( (flags & 32) == 32 )
-                                                                       _objcContraint = ld::File::objcConstraintRetainReleaseForSimulator;
+                                                                       this->_objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
                                                                else
-                                                                       _objcContraint = ld::File::objcConstraintRetainRelease;
-                                                               _swiftVersion = ((flags >> 8) & 0xFF);
+                                                                       this->_objcConstraint = ld::File::objcConstraintRetainRelease;
+                                                               this->_swiftVersion = ((flags >> 8) & 0xFF);
                                                        }
                                                        else if ( sect->size() > 0 ) {
-                                                               warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), this->path());
+                                                               warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), path);
                                                        }
                                                }
                                        }
@@ -404,12 +237,15 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
                                else if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), "__LLVM") == 0 ) {
                                        const macho_section<P>* const sect = (macho_section<P>*)((char*)cmd + sizeof(macho_segment_command<P>));
                                        if ( strncmp(sect->sectname(), "__bundle", 8) == 0 )
-                                               _bitcode = std::unique_ptr<ld::Bitcode>(new ld::Bitcode(NULL, sect->size()));
+                                               this->_bitcode = std::unique_ptr<ld::Bitcode>(new ld::Bitcode(NULL, sect->size()));
+                               }
+                               else if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), "__LINKEDIT") == 0 ) {
+                                       _linkeditStartOffset = ((macho_segment_command<P>*)cmd)->fileoff();
                                }
                }
-               cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
+               cmd = (const macho_load_command<P>*)(((char*)cmd)+cmdLength);
                if ( cmd > cmdsEnd )
-                       throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, pth);
+                       throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path);
        }
        // arm/arm64 objects are default to ios platform if not set.
        // rdar://problem/21746314
@@ -419,10 +255,10 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
 
        // check cross-linking
        if ( lcPlatform != platform ) {
-               _wrongOS = true;
-               if ( _addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) {
+               this->_wrongOS = true;
+               if ( this->_addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) {
                        if ( buildingForSimulator ) {
-                               if ( !_allowSimToMacOSXLinking ) {
+                               if ( !this->_allowSimToMacOSXLinking ) {
                                        switch (platform) {
                                                case Options::kPlatformOSX:
                                                case Options::kPlatformiOS:
@@ -430,10 +266,16 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
                                                                break;
                                                        // fall through if the Platform is not Unknown
                                                case Options::kPlatformWatchOS:
-                                                       // WatchOS errors on cross-linking all the time.
-                                                       throwf("building for %s simulator, but linking against dylib built for %s,",
+                                                       // WatchOS errors on cross-linking when building for bitcode
+                                                       if ( usingBitcode )
+                                                               throwf("building for %s simulator, but linking against dylib built for %s,",
                                                                   Options::platformName(platform),
                                                                   Options::platformName(lcPlatform));
+                                                       else
+                                                               warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. "
+                                                                               "Note: This will be an error in the future.",
+                                                                               Options::platformName(platform), path,
+                                                                               Options::platformName(lcPlatform));
                                                        break;
        #if SUPPORT_APPLE_TV
                                                case Options::kPlatform_tvOS:
@@ -445,7 +287,7 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
                                                        else
                                                                warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. "
                                                                                "Note: This will be an error in the future.",
-                                                                               Options::platformName(platform), path(),
+                                                                               Options::platformName(platform), path,
                                                                                Options::platformName(lcPlatform));
                                                        break;
        #endif
@@ -463,22 +305,28 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
                                                        break;
                                                // fall through if the Platform is not Unknown
                                        case Options::kPlatformWatchOS:
-                                               // WatchOS errors on cross-linking all the time.
-                                               throwf("building for %s, but linking against dylib built for %s,",
+                                               // WatchOS errors on cross-linking when building for bitcode
+                                               if ( usingBitcode )
+                                                       throwf("building for %s, but linking against dylib built for %s,",
                                                           Options::platformName(platform),
                                                           Options::platformName(lcPlatform));
+                                               else
+                                                       warning("URGENT: building for %s, but linking against dylib (%s) built for %s. "
+                                                                       "Note: This will be an error in the future.",
+                                                                       Options::platformName(platform), path,
+                                                                       Options::platformName(lcPlatform));
                                                break;
        #if SUPPORT_APPLE_TV
                                        case Options::kPlatform_tvOS:
                                                // tvOS is a warning temporarily. rdar://problem/21746965
-                                               if ( _usingBitcode )
+                                               if ( usingBitcode )
                                                        throwf("building for %s, but linking against dylib built for %s,",
                                                                   Options::platformName(platform),
                                                                   Options::platformName(lcPlatform));
                                                else
                                                        warning("URGENT: building for %s, but linking against dylib (%s) built for %s. "
                                                                        "Note: This will be an error in the future.",
-                                                                       Options::platformName(platform), path(),
+                                                                       Options::platformName(platform), path,
                                                                        Options::platformName(lcPlatform));
                                                break;
        #endif
@@ -493,15 +341,17 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
        // figure out if we need to examine dependent dylibs
        // with compressed LINKEDIT format, MH_NO_REEXPORTED_DYLIBS can be trusted
        bool processDependentLibraries = true;
-       if  ( compressedLinkEdit && _noRexports && !linkingFlatNamespace) 
+       if  ( compressedLinkEdit && this->_noRexports && !linkingFlatNamespace)
                processDependentLibraries = false;
        
        if ( processDependentLibraries ) {
                // pass 2 builds list of all dependent libraries
-               _dependentDylibs.reserve(dependentLibCount);
+               this->_dependentDylibs.reserve(dependentLibCount);
                cmd = cmds;
                unsigned int reExportDylibCount = 0;  
                for (uint32_t i = 0; i < cmd_count; ++i) {
+                       uint32_t cmdLength = cmd->cmdsize();
+                       const macho_dylib_command<P>* dylibCmd = (macho_dylib_command<P>*)cmd;
                        switch (cmd->cmd()) {
                                case LC_LOAD_DYLIB:
                                case LC_LOAD_WEAK_DYLIB:
@@ -510,20 +360,22 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
                                                break;
                                case LC_REEXPORT_DYLIB:
                                        ++reExportDylibCount;
-                                       Dependent entry;
-                                       entry.path = strdup(((macho_dylib_command<P>*)cmd)->name());
-                                       entry.dylib = NULL;
-                                       entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB);
-                                       if ( (targetInstallPath == NULL) || (strcmp(targetInstallPath, entry.path) != 0) )
-                                               _dependentDylibs.push_back(entry);
+                                       if ( dylibCmd->name_offset() > cmdLength )
+                                               throwf("malformed mach-o: LC_*_DYLIB load command has offset (%u) outside its size (%u)", dylibCmd->name_offset(), cmdLength);
+                                       if ( (dylibCmd->name_offset() + strlen(dylibCmd->name()) + 1) > cmdLength )
+                                               throwf("malformed mach-o: LC_*_DYLIB load command string extends beyond end of load command");
+                                       const char *path = strdup(dylibCmd->name());
+                                       bool reExport = (cmd->cmd() == LC_REEXPORT_DYLIB);
+                                       if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, path) != 0) )
+                                               this->_dependentDylibs.emplace_back(path, reExport);
                                        break;
                        }
-                       cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
+                       cmd = (const macho_load_command<P>*)(((char*)cmd)+cmdLength);
                }
                // verify MH_NO_REEXPORTED_DYLIBS bit was correct
                if ( compressedLinkEdit && !linkingFlatNamespace ) {
                        if ( reExportDylibCount == 0 )
-                               throwf("malformed dylib has MH_NO_REEXPORTED_DYLIBS flag but no LC_REEXPORT_DYLIB load commands: %s", pth);
+                               throwf("malformed dylib has MH_NO_REEXPORTED_DYLIBS flag but no LC_REEXPORT_DYLIB load commands: %s", path);
                }
                // pass 3 add re-export info
                cmd = cmds;
@@ -533,44 +385,65 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
                        switch (cmd->cmd()) {
                                case LC_SUB_UMBRELLA:
                                        frameworkLeafName = ((macho_sub_umbrella_command<P>*)cmd)->sub_umbrella();
-                                       for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) {
-                                               const char* dylibName = it->path;
+                                       for (auto &dep : this->_dependentDylibs) {
+                                               const char* dylibName = dep.path;
                                                const char* lastSlash = strrchr(dylibName, '/');
-                                               if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) )
-                                                       it->reExport = true;
+                                               if ( (lastSlash != nullptr) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) )
+                                                       dep.reExport = true;
                                        }
                                        break;
                                case LC_SUB_LIBRARY:
                                        dylibBaseName = ((macho_sub_library_command<P>*)cmd)->sub_library();
-                                       for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) {
-                                               const char* dylibName = it->path;
+                                       for (auto &dep : this->_dependentDylibs) {
+                                               const char* dylibName = dep.path;
                                                const char* lastSlash = strrchr(dylibName, '/');
                                                const char* leafStart = &lastSlash[1];
-                                               if ( lastSlash == NULL )
+                                               if ( lastSlash == nullptr )
                                                        leafStart = dylibName;
                                                const char* firstDot = strchr(leafStart, '.');
                                                int len = strlen(leafStart);
-                                               if ( firstDot != NULL )
+                                               if ( firstDot != nullptr )
                                                        len = firstDot - leafStart;
                                                if ( strncmp(leafStart, dylibBaseName, len) == 0 )
-                                                       it->reExport = true;
+                                                       dep.reExport = true;
                                        }
                                        break;
                        }
                        cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
                }
        }
-       
+
+       // if framework, capture framework name
+       if ( this->_dylibInstallPath != NULL ) {
+               const char* lastSlash = strrchr(this->_dylibInstallPath, '/');
+               if ( lastSlash != NULL ) {
+                       const char* leafName = lastSlash+1;
+                       char frname[strlen(leafName)+32];
+                       strcpy(frname, leafName);
+                       strcat(frname, ".framework/");
+
+                       if ( strstr(this->_dylibInstallPath, frname) != NULL )
+                       this->_frameworkName = leafName;
+               }
+       }
+
        // validate minimal load commands
-       if ( (_dylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) 
-               throwf("dylib %s missing LC_ID_DYLIB load command", pth);
-       if ( dyldInfo == NULL ) {
-               if ( symbolTable == NULL )
+       if ( (this->_dylibInstallPath == nullptr) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) )
+               throwf("dylib %s missing LC_ID_DYLIB load command", path);
+       if ( dyldInfo == nullptr ) {
+               if ( symbolTable == nullptr )
                        throw "binary missing LC_SYMTAB load command";
-               if ( dynamicInfo == NULL )
+               if ( dynamicInfo == nullptr )
                        throw "binary missing LC_DYSYMTAB load command";
        }
-       
+
+       if ( symtab != nullptr ) {
+               if ( symtab->symoff() < _linkeditStartOffset )
+                       throwf("malformed mach-o, symbol table not in __LINKEDIT");
+               if ( symtab->stroff() < _linkeditStartOffset )
+                       throwf("malformed mach-o, symbol table strings not in __LINKEDIT");
+       }
+
        // if linking flat and this is a flat dylib, create one atom that references all imported symbols
        if ( linkingFlatNamespace && linkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) {
                std::vector<const char*> importNames;
@@ -580,11 +453,11 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
                for (const macho_nlist<P>* sym=start; sym < end; ++sym) {
                        importNames.push_back(&strings[sym->n_strx()]);
                }
-               _importAtom = new ImportAtom<A>(*this, importNames);
+               this->_importAtom = new generic::dylib::ImportAtom<A>(*this, importNames);
        }
 
        // build hash table
-       if ( dyldInfo != NULL ) 
+       if ( dyldInfo != nullptr )
                buildExportHashTableFromExportInfo(dyldInfo, fileContent);
        else
                buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent);
@@ -593,49 +466,27 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth,
        munmap((caddr_t)fileContent, fileLength);
 }
 
-
-//
-// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz
-//
-template <typename A>
-uint32_t File<A>::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;
-}
-
 template <typename A>
-void File<A>::buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo, 
-                                                                                               const macho_nlist<P>* symbolTable, const char* strings, 
-                                                                                               const uint8_t* fileContent)
+void File<A>::buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
+                                                                                                 const macho_nlist<P>* symbolTable,
+                                                                                                 const char* strings, const uint8_t* fileContent)
 {
        if ( dynamicInfo->tocoff() == 0 ) {
-               if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->path());
+               if ( this->_s_logHashtable )
+                       fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->path());
                const macho_nlist<P>* start = &symbolTable[dynamicInfo->iextdefsym()];
                const macho_nlist<P>* end = &start[dynamicInfo->nextdefsym()];
-               _atoms.reserve(dynamicInfo->nextdefsym()); // set initial bucket count
+               this->_atoms.reserve(dynamicInfo->nextdefsym()); // set initial bucket count
                for (const macho_nlist<P>* sym=start; sym < end; ++sym) {
                        this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, false, sym->n_value());
                }
        }
        else {
                int32_t count = dynamicInfo->ntoc();
-               _atoms.reserve(count); // set initial bucket count
-               if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->path());
-               const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff());
+               this->_atoms.reserve(count); // set initial bucket count
+               if ( this->_s_logHashtable )
+                       fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->path());
+               const auto* toc = reinterpret_cast<const dylib_table_of_contents*>(fileContent + dynamicInfo->tocoff());
                for (int32_t i = 0; i < count; ++i) {
                        const uint32_t index = E::get32(toc[i].symbol_index);
                        const macho_nlist<P>* sym = &symbolTable[index];
@@ -644,26 +495,29 @@ void File<A>::buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P
        }
        
        // special case old libSystem
-       if ( (_dylibInstallPath != NULL) && (strcmp(_dylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) )
+       if ( (this->_dylibInstallPath != nullptr) && (strcmp(this->_dylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) )
                addDyldFastStub();
 }
 
 
 template <typename A>
-void File<A>::buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo, 
-                                                                                                                               const uint8_t* fileContent)
+void File<A>::buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo,
+                                                                                                const uint8_t* fileContent)
 {
-       if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable from export info in %s\n", this->path());
+       if ( this->_s_logHashtable )
+               fprintf(stderr, "ld: building hashtable from export info in %s\n", this->path());
        if ( dyldInfo->export_size() > 0 ) {
                const uint8_t* start = fileContent + dyldInfo->export_off();
                const uint8_t* end = &start[dyldInfo->export_size()];
+               if ( (dyldInfo->export_off() + dyldInfo->export_size()) > _fileLength )
+                       throwf("malformed mach-o dylib, exports trie extends beyond end of file, ");
                std::vector<mach_o::trie::Entry> list;
                parseTrie(start, end, list);
-               for (std::vector<mach_o::trie::Entry>::iterator it=list.begin(); it != list.end(); ++it) 
-                       this->addSymbol(it->name, 
-                                                       it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION, 
-                                                       (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL,
-                                                        it->address);
+               for (const auto &entry : list)
+                       this->addSymbol(entry.name,
+                                                       entry.flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION,
+                                                       (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL,
+                                                       entry.address);
        }
 }
 
@@ -671,13 +525,13 @@ void File<A>::buildExportHashTableFromExportInfo(const macho_dyld_info_command<P
 template <>
 void File<x86_64>::addDyldFastStub()
 {
-       addSymbol("dyld_stub_binder", false, false, 0);
+       addSymbol("dyld_stub_binder");
 }
 
 template <>
 void File<x86>::addDyldFastStub()
 {
-       addSymbol("dyld_stub_binder", false, false, 0);
+       addSymbol("dyld_stub_binder");
 }
 
 template <typename A>
@@ -686,341 +540,34 @@ void File<A>::addDyldFastStub()
        // do nothing
 }
 
-template <typename A>
-void File<A>::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address)
-{
-       //fprintf(stderr, "addSymbol() %s\n", name);
-       // symbols that start with $ld$ are meta-data to the static linker
-       // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
-       if ( strncmp(name, "$ld$", 4) == 0 ) {  
-               //    $ld$ <action> $ <condition> $ <symbol-name>
-               const char* symAction = &name[4];
-               const char* symCond = strchr(symAction, '$');
-               if ( symCond != NULL ) {
-                       char curOSVers[16];
-                       sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF));
-                       if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) {
-                               const char* symName = strchr(&symCond[1], '$');
-                               if ( symName != NULL ) {
-                                       ++symName;
-                                       if ( strncmp(symAction, "hide$", 5) == 0 ) {
-                                               if ( _s_logHashtable ) fprintf(stderr, "  adding %s to ignore set for %s\n", symName, this->path());
-                                               _ignoreExports.insert(strdup(symName));
-                                               return;
-                                       }
-                                       else if ( strncmp(symAction, "add$", 4) == 0 ) {
-                                               this->addSymbol(symName, weakDef, false, 0);
-                                               return;
-                                       }
-                                       else if ( strncmp(symAction, "install_name$", 13) == 0 ) {
-                                               _dylibInstallPath = symName;
-                                               _installPathOverride = true;
-                                               // <rdar://problem/14448206> CoreGraphics redirects to ApplicationServices, but with wrong compat version
-                                               if ( strcmp(_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 )
-                                                       _dylibCompatibilityVersion = parseVersionNumber32("1.0");
-                                               return;
-                                       }
-                                       else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) {
-                                               _dylibCompatibilityVersion = parseVersionNumber32(symName);
-                                               return;
-                                       }
-                                       else {
-                                               warning("bad symbol action: %s in dylib %s", name, this->path());
-                                       }
-                               }
-                       }
-               }       
-               else {
-                       warning("bad symbol condition: %s in dylib %s", name, this->path());
-               }
-       }
-       
-       // add symbol as possible export if we are not supposed to ignore it
-       if ( _ignoreExports.count(name) == 0 ) {
-               AtomAndWeak bucket;
-               bucket.atom = NULL;
-               bucket.weakDef = weakDef;
-               bucket.tlv = tlv;
-               bucket.address = address;
-               if ( _s_logHashtable ) fprintf(stderr, "  adding %s to hash table for %s\n", name, this->path());
-               _atoms[strdup(name)] = bucket;
-       }
-}
-
-
-template <typename A>
-bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
-{
-       handler.doFile(*this);
-       // if doing flatnamespace and need all this dylib's imports resolve
-       // add atom which references alls undefines in this dylib
-       if ( _importAtom != NULL ) {
-               handler.doAtom(*_importAtom);
-               return true;
-       }
-       return false;
-}
-
-
-template <typename A>
-std::pair<bool, bool> File<A>::hasWeakDefinitionImpl(const char* name) const
-{
-       const auto pos = _atoms.find(name);
-       if ( pos != _atoms.end() )
-               return std::make_pair(true, pos->second.weakDef);
-
-       // look in children that I re-export
-       for (const auto &dep : _dependentDylibs) {
-               if ( dep.reExport ) {
-                       auto ret = dep.dylib->hasWeakDefinitionImpl(name);
-                       if ( ret.first )
-                               return ret;
-               }
-       }
-       return std::make_pair(false, false);
-}
-
-
-template <typename A>
-bool File<A>::hasWeakDefinition(const char* name) const
-{
-       // if supposed to ignore this export, then pretend I don't have it
-       if ( _ignoreExports.count(name) != 0 )
-               return false;
-
-       return hasWeakDefinitionImpl(name).second;
-}
-
-
-// <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
-template <typename A>
-bool File<A>::allSymbolsAreWeakImported() const
-{
-       bool foundNonWeakImport = false;
-       bool foundWeakImport = false;
-       //fprintf(stderr, "%s:\n", this->path());
-       for (typename NameToAtomMap::const_iterator it = _atoms.begin(); it != _atoms.end(); ++it) {
-               const ld::Atom* atom = it->second.atom;
-               if ( atom != NULL ) {
-                       if ( atom->weakImported() )
-                               foundWeakImport = true;
-                       else
-                               foundNonWeakImport = true;
-                       //fprintf(stderr, "  weak_import=%d, name=%s\n", atom->weakImported(), it->first);
-               }
-       }
-       
-       // don't automatically weak link dylib with no imports
-       // so at least one weak import symbol and no non-weak-imported symbols must be found
-       return foundWeakImport && !foundNonWeakImport;
-}
-
-
-template <typename A>
-bool File<A>::containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const
-{
-       if ( _ignoreExports.count(name) != 0 )
-               return false;
-
-       // check myself
-       const auto pos = _atoms.find(name);
-       if ( pos != _atoms.end() ) {
-               weakDef = pos->second.weakDef;
-               tlv = pos->second.tlv;
-               defAddress = pos->second.address;
-               return true;
-       }
-       
-       // check dylibs I re-export
-       for (const auto &dep : _dependentDylibs) {
-               if ( dep.reExport && !dep.dylib->implicitlyLinked() ) {
-                       if ( dep.dylib->containsOrReExports(name, weakDef, tlv, defAddress) )
-                               return true;
-               }
-       }
-               
-       return false;
-}
-
-
-template <typename A>
-bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const
-{
-       // if supposed to ignore this export, then pretend I don't have it
-       if ( _ignoreExports.count(name) != 0 )
-               return false;
-       
-       
-       AtomAndWeak bucket;
-       if ( this->containsOrReExports(name, bucket.weakDef, bucket.tlv, bucket.address) ) {
-               bucket.atom = new ExportAtom<A>(*this, name, bucket.weakDef, bucket.tlv, bucket.address);
-               _atoms[name] = bucket;
-               _providedAtom = true;
-               if ( _s_logHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path());
-               // call handler with new export atom
-               handler.doAtom(*bucket.atom);
-               return true;
-       }
-        
-       return false;
-}
-
-
-
-template <typename A>
-bool File<A>::isPublicLocation(const char* pth)
-{
-       // -no_implicit_dylibs disables this optimization
-       if ( ! _implicitlyLinkPublicDylibs )
-               return false;
-       
-       // /usr/lib is a public location
-       if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) )
-               return true;
-
-       // /System/Library/Frameworks/ is a public location
-       if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) {
-               const char* frameworkDot = strchr(&pth[27], '.');
-               // but only top level framework
-               // /System/Library/Frameworks/Foo.framework/Versions/A/Foo                 ==> true
-               // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib         ==> false
-               // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar   ==> false
-               // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false
-               if ( frameworkDot != NULL ) {
-                       int frameworkNameLen = frameworkDot - &pth[27];
-                       if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 )
-                               return true;
-               }
-       }
-               
-       return false;
-}
-
-template <typename A>
-void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs)
-{
-       // only do this once
-       if ( _indirectDylibsProcessed )
-               return;
-       const static bool log = false;
-       if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath());
-       if ( _linkingFlat ) {
-               for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) {
-                       it->dylib = (File<A>*)handler->findDylib(it->path, this->path());
-               }
-       }
-       else if ( _noRexports ) {
-               // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
-       }
-       else {
-               // two-level, might have re-exports
-               for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) {
-                       if ( it->reExport ) {
-                               if ( log ) fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), it->path);
-                               // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child
-                               it->dylib = (File<A>*)handler->findDylib(it->path, this->path());
-                               if ( it->dylib->hasPublicInstallName() && !it->dylib->wrongOS() ) {
-                                       // promote this child to be automatically added as a direct dependent if this already is
-                                       if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,it->dylib->installPath()) == 0) ) {
-                                               if ( log ) fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", it->dylib->installPath());
-                                               it->dylib->setImplicitlyLinked();
-                                       }
-                                       else if ( it->dylib->explicitlyLinked() || it->dylib->implicitlyLinked() ) {
-                                               if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n");
-                                       }
-                                       else {
-                                               if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), it->path);
-                                       }
-                               }
-                               else {
-                                       // add all child's symbols to me
-                                       if ( log ) fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), it->path);
-                               }
-                       }
-                       else if ( !_explictReExportFound ) {
-                               // see if child contains LC_SUB_FRAMEWORK with my name
-                               it->dylib = (File<A>*)handler->findDylib(it->path, this->path());
-                               const char* parentUmbrellaName = it->dylib->parentUmbrella();
-                               if ( parentUmbrellaName != NULL ) {
-                                       const char* parentName = this->path();
-                                       const char* lastSlash = strrchr(parentName, '/');
-                                       if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) {
-                                               // add all child's symbols to me
-                                               it->reExport = true;
-                                               if ( log ) fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->installPath(), it->path);
-                                       }
-                               }
-                       }
-               }
-       }
-       
-       // check for re-export cycles
-       ReExportChain chain;
-       chain.prev = NULL;
-       chain.file = this;
-       this->assertNoReExportCycles(&chain);
-       
-       _indirectDylibsProcessed = true;
-}
-
-template <typename A>
-void File<A>::assertNoReExportCycles(ReExportChain* prev) const
-{
-       // recursively check my re-exported dylibs
-       ReExportChain chain;
-       chain.prev = prev;
-       chain.file = this;
-       for (const auto &dep : _dependentDylibs) {
-               if ( dep.reExport ) {
-                       ld::File* child = dep.dylib;
-                       // check child is not already in chain 
-                       for (ReExportChain* p = prev; p != nullptr; p = p->prev) {
-                               if ( p->file == child ) {
-                                       throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path());
-                               }
-                       }
-                       if ( dep.dylib != nullptr )
-                               dep.dylib->assertNoReExportCycles(&chain);
-               }
-       }
-}
-
-
 template <typename A>
 class Parser 
 {
 public:
-       typedef typename A::P                                   P;
-
-       static bool                                                                             validFile(const uint8_t* fileContent, bool executableOrDyliborBundle);
-       static const char*                                                              fileKind(const uint8_t* fileContent);
-       static ld::dylib::File*                                                 parse(const uint8_t* fileContent, uint64_t fileLength, 
-                                                                                                                       const char* path, time_t mTime, 
-                                                                                                                       ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) {
-                                                                                                                       return new File<A>(fileContent, fileLength, path, mTime,
-                                                                                                                                                       ordinal, opts.flatNamespace(), 
-                                                                                                                                                       opts.linkingMainExecutable(),
-                                                                                                                                                       opts.implicitlyLinkIndirectPublicDylibs(), 
-                                                                                                                                                       opts.platform(), 
-                                                                                                                                                       opts.minOSversion(),
-                                                                                                                                                       opts.allowSimulatorToLinkWithMacOSX(),
-                                                                                                                                                       opts.addVersionLoadCommand(),
-                                                                                                                                                       opts.targetIOSSimulator(),
-                                                                                                                                                       opts.logAllFiles(),
-                                                                                                                                                       opts.installPath(),
-                                                                                                                                                       indirectDylib,
-                                                                                                                                                       opts.outputKind() == Options::kPreload,
-                                                                                                                                                       opts.bundleBitcode());
-                                                                                                               }
+       using P = typename A::P;
+
+       static bool                             validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch=false, uint32_t subType=0);
+       static const char*              fileKind(const uint8_t* fileContent);
+       static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
+                                                                 time_t mTime, ld::File::Ordinal ordinal, const Options& opts,
+                                                                 bool indirectDylib)
+       {
+               return new File<A>(fileContent, fileLength, path, mTime, ordinal, opts.flatNamespace(),
+                                                  opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(),
+                                                  opts.platform(), opts.minOSversion(),
+                                                  opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(),
+                                                  opts.targetIOSSimulator(), opts.logAllFiles(), opts.installPath(),
+                                                  indirectDylib, opts.outputKind() == Options::kPreload, opts.bundleBitcode());
+       }
 
 };
 
 
 
 template <>
-bool Parser<x86>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+bool Parser<x86>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType)
 {
-       const macho_header<P>* header = (const macho_header<P>*)fileContent;
+       const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
        if ( header->magic() != MH_MAGIC )
                return false;
        if ( header->cputype() != CPU_TYPE_I386 )
@@ -1045,9 +592,9 @@ bool Parser<x86>::validFile(const uint8_t* fileContent, bool executableOrDylibor
 }
 
 template <>
-bool Parser<x86_64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+bool Parser<x86_64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType)
 {
-       const macho_header<P>* header = (const macho_header<P>*)fileContent;
+       const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
        if ( header->magic() != MH_MAGIC_64 )
                return false;
        if ( header->cputype() != CPU_TYPE_X86_64 )
@@ -1072,13 +619,15 @@ bool Parser<x86_64>::validFile(const uint8_t* fileContent, bool executableOrDyli
 }
 
 template <>
-bool Parser<arm>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+bool Parser<arm>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType)
 {
-       const macho_header<P>* header = (const macho_header<P>*)fileContent;
+       const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
        if ( header->magic() != MH_MAGIC )
                return false;
        if ( header->cputype() != CPU_TYPE_ARM )
                return false;
+       if ( subTypeMustMatch && (header->cpusubtype() != subType) )
+               return false;
        switch ( header->filetype() ) {
                case MH_DYLIB:
                case MH_DYLIB_STUB:
@@ -1101,9 +650,9 @@ bool Parser<arm>::validFile(const uint8_t* fileContent, bool executableOrDylibor
 
 
 template <>
-bool Parser<arm64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+bool Parser<arm64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType)
 {
-       const macho_header<P>* header = (const macho_header<P>*)fileContent;
+       const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
        if ( header->magic() != MH_MAGIC_64 )
                return false;
        if ( header->cputype() != CPU_TYPE_ARM64 )
@@ -1132,7 +681,7 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t*
 {
        if ( Parser<x86_64>::validFile(fileContent, false) ) {
                *result = CPU_TYPE_X86_64;
-               const macho_header<Pointer64<LittleEndian> >* header = (const macho_header<Pointer64<LittleEndian> >*)fileContent;
+               const auto* header = reinterpret_cast<const macho_header<Pointer64<LittleEndian>>*>(fileContent);
                *subResult = header->cpusubtype();
                return true;
        }
@@ -1143,7 +692,7 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t*
        }
        if ( Parser<arm>::validFile(fileContent, false) ) {
                *result = CPU_TYPE_ARM;
-               const macho_header<Pointer32<LittleEndian> >* header = (const macho_header<Pointer32<LittleEndian> >*)fileContent;
+               const auto* header = reinterpret_cast<const macho_header<Pointer32<LittleEndian>>*>(fileContent);
                *subResult = header->cpusubtype();
                return true;
        }
@@ -1158,34 +707,34 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t*
 template <>
 const char* Parser<x86>::fileKind(const uint8_t* fileContent)
 {
-       const macho_header<P>* header = (const macho_header<P>*)fileContent;
+       const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
        if ( header->magic() != MH_MAGIC )
-               return NULL;
+               return nullptr;
        if ( header->cputype() != CPU_TYPE_I386 )
-               return NULL;
+               return nullptr;
        return "i386";
 }
 
 template <>
 const char* Parser<x86_64>::fileKind(const uint8_t* fileContent)
 {
-       const macho_header<P>* header = (const macho_header<P>*)fileContent;
+       const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
        if ( header->magic() != MH_MAGIC_64 )
-               return NULL;
+               return nullptr;
        if ( header->cputype() != CPU_TYPE_X86_64 )
-               return NULL;
+               return nullptr;
        return "x86_64";
 }
 
 template <>
 const char* Parser<arm>::fileKind(const uint8_t* fileContent)
 {
-       const macho_header<P>* header = (const macho_header<P>*)fileContent;
+       const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
        if ( header->magic() != MH_MAGIC )
-               return NULL;
+               return nullptr;
        if ( header->cputype() != CPU_TYPE_ARM )
-               return NULL;
-       for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+               return nullptr;
+       for (const auto* t = archInfoArray; t->archName != nullptr; ++t) {
                if ( (t->cpuType == CPU_TYPE_ARM) && ((cpu_subtype_t)header->cpusubtype() == t->cpuSubType) ) {
                        return t->archName;
                }
@@ -1197,11 +746,11 @@ const char* Parser<arm>::fileKind(const uint8_t* fileContent)
 template <>
 const char* Parser<arm64>::fileKind(const uint8_t* fileContent)
 {
-  const macho_header<P>* header = (const macho_header<P>*)fileContent;
+  const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
   if ( header->magic() != MH_MAGIC_64 )
-    return NULL;
+    return nullptr;
   if ( header->cputype() != CPU_TYPE_ARM64 )
-    return NULL;
+    return nullptr;
   return "arm64";
 }
 #endif
@@ -1225,48 +774,47 @@ const char* archName(const uint8_t* fileContent)
                return Parser<arm64>::fileKind(fileContent);
        }
 #endif
-       return NULL;
+       return nullptr;
 }
 
 
 //
 // main function used by linker to instantiate ld::Files
 //
-ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, 
-                                                       const char* path, time_t modTime, const Options& opts, ld::File::Ordinal ordinal, 
-                                                       bool bundleLoader, bool indirectDylib)
+ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
+                                          time_t modTime, const Options& opts, ld::File::Ordinal ordinal,
+                                          bool bundleLoader, bool indirectDylib)
 {
+       bool subTypeMustMatch = opts.enforceDylibSubtypesMatch();
        switch ( opts.architecture() ) {
 #if SUPPORT_ARCH_x86_64
                case CPU_TYPE_X86_64:
-                       if ( Parser<x86_64>::validFile(fileContent, bundleLoader) )
+                       if ( Parser<x86_64>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
                                return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
                        break;
 #endif
 #if SUPPORT_ARCH_i386
                case CPU_TYPE_I386:
-                       if ( Parser<x86>::validFile(fileContent, bundleLoader) )
+                       if ( Parser<x86>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
                                return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
                        break;
 #endif
 #if SUPPORT_ARCH_arm_any
                case CPU_TYPE_ARM:
-                       if ( Parser<arm>::validFile(fileContent, bundleLoader) )
+                       if ( Parser<arm>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
                                return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
                        break;
 #endif
 #if SUPPORT_ARCH_arm64
                case CPU_TYPE_ARM64:
-                       if ( Parser<arm64>::validFile(fileContent, bundleLoader) )
+                       if ( Parser<arm64>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
                                return Parser<arm64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
                        break;
 #endif
        }
-       return NULL;
+       return nullptr;
 }
 
 
 }; // namespace dylib
 }; // namespace mach_o
-
-
index 4d093f139d921e3528a32e3bcb42eb74d972c2fd..ba7b8ff44dffea5f219f4962239d16149103070d 100644 (file)
@@ -37,9 +37,9 @@ extern bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subt
 extern const char* archName(const uint8_t* fileContent);
 
 
-extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, 
-                                                               time_t modTime, const Options& opts, ld::File::Ordinal ordinal, 
-                                                               bool bundleLoader, bool indirectDylib);
+extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
+                                                         time_t modTime, const Options& opts, ld::File::Ordinal ordinal,
+                                                         bool bundleLoader, bool indirectDylib);
 
 } // namespace dylib
 } // namespace mach_o
index 2fa41b9f269b9a123b8ea6669d26381bbc4a3144..9689bf07890d5ecbd81eb4da8de73d97b298582d 100644 (file)
@@ -1089,7 +1089,8 @@ public:
                                                                                                                                                                                TargetDesc& target);
        uint32_t                                                                                tentativeDefinitionCount() { return _tentativeDefinitionCount; }
        uint32_t                                                                                absoluteSymbolCount() { return _absoluteSymbolCount; }
-       
+
+       uint32_t                                                                                fileLength() const { return _fileLength; }
        bool                                                                                    hasStubsSection() { return (_stubsSectionNum != 0); }
        unsigned int                                                                    stubsSectionNum() { return _stubsSectionNum; }
        void                                                                                    addDtraceExtraInfos(const SourceLocation& src, const char* provider);
@@ -1101,6 +1102,8 @@ public:
        bool                                                                                    verboseOptimizationHints() { return _verboseOptimizationHints; }
        bool                                                                                    neverConvertDwarf() { return _neverConvertDwarf; }
        bool                                                                                    armUsesZeroCostExceptions() { return _armUsesZeroCostExceptions; }
+       uint8_t                                                                                 maxDefaultCommonAlignment() { return _maxDefaultCommonAlignment; }
+
 
        macho_data_in_code_entry<P>*                                    dataInCodeStart() { return _dataInCodeStart; }
        macho_data_in_code_entry<P>*                                    dataInCodeEnd()   { return _dataInCodeEnd; }
@@ -1256,6 +1259,7 @@ private:
        bool                                                                            _ignoreMismatchPlatform;
        bool                                                                            _treateBitcodeAsData;
        bool                                                                            _usingBitcode;
+       uint8_t                                                                         _maxDefaultCommonAlignment;
        unsigned int                                                            _stubsSectionNum;
        const macho_section<P>*                                         _stubsMachOSection;
        std::vector<const char*>                                        _dtraceProviderInfo;
@@ -1474,11 +1478,11 @@ bool Parser<A>::getNonLocalSymbols(const uint8_t* fileContent, std::vector<const
                        uint32_t symbolCount = symtab->nsyms();
                        const macho_nlist<P>* symbols = (const macho_nlist<P>*)(fileContent + symtab->symoff());
                        const char* strings = (char*)fileContent + symtab->stroff();
-                       for (uint32_t i = 0; i < symbolCount; ++i) {
+                       for (uint32_t j = 0; j < symbolCount; ++j) {
                                // ignore stabs and count only ext symbols
-                               if ( (symbols[i].n_type() & N_STAB) == 0 &&
-                                        (symbols[i].n_type() & N_EXT) != 0 ) {
-                                       const char* symName = &strings[symbols[i].n_strx()];
+                               if ( (symbols[j].n_type() & N_STAB) == 0 &&
+                                        (symbols[j].n_type() & N_EXT) != 0 ) {
+                                       const char* symName = &strings[symbols[j].n_strx()];
                                        syms.push_back(symName);
                                }
                        }
@@ -1726,6 +1730,7 @@ ld::relocatable::File* Parser<A>::parse(const ParserOptions& opts)
                printf("%s\n", _path);
                
        _armUsesZeroCostExceptions = opts.armUsesZeroCostExceptions;
+       _maxDefaultCommonAlignment = opts.maxDefaultCommonAlignment;
 
        // parse start of mach-o file
        if ( ! parseLoadCommands(opts.platform, opts.minOSVersion, opts.simulator, opts.ignoreMismatchPlatform) )
@@ -2077,12 +2082,13 @@ bool Parser<A>::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS
                                lcPlatform = Options::platformForLoadCommand(cmd->cmd());
                                _file->_minOSVersion = ((macho_version_min_command<P>*)cmd)->version();
                                break;
+                       case macho_segment_command<P>::CMD:
+                               if ( segment != NULL )
+                                       throw "more than one LC_SEGMENT found in object file";
+                               segment = (macho_segment_command<P>*)cmd;
+                               break;
                        default:
-                               if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-                                       if ( segment != NULL )
-                                               throw "more than one LC_SEGMENT found in object file";
-                                       segment = (macho_segment_command<P>*)cmd;
-                               }
+                               // ignore unknown load commands
                                break;
                }
                cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
@@ -2105,10 +2111,16 @@ bool Parser<A>::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS
                                                break;
                                        // fall through if the Platform is not Unknown
                                case Options::kPlatformWatchOS:
-                                       // WatchOS errors on cross-linking all the time.
-                                       throwf("building for %s%s, but linking in object file built for %s,",
+                                       // Error when using bitcocde, warning otherwise.
+                                       if (_usingBitcode)
+                                               throwf("building for %s%s, but linking in object file built for %s,",
                                                   Options::platformName(platform), (simulator ? " simulator" : ""),
                                                   Options::platformName(lcPlatform));
+                                       else
+                                               warning("URGENT: building for %s%s, but linking in object file (%s) built for %s. "
+                                                               "Note: This will be an error in the future.",
+                                                               Options::platformName(platform), (simulator ? " simulator" : ""), path(),
+                                                               Options::platformName(lcPlatform));
                                        break;
        #if SUPPORT_APPLE_TV
                                case Options::kPlatform_tvOS:
@@ -2145,7 +2157,8 @@ bool Parser<A>::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS
                throw "missing LC_SEGMENT";
        _sectionsStart = (macho_section<P>*)((char*)segment + sizeof(macho_segment_command<P>));
        _machOSectionsCount = segment->nsects();
-       
+       if ( (sizeof(macho_segment_command<P>) + _machOSectionsCount * sizeof(macho_section<P>)) > segment->cmdsize() )
+               throw "too many sections for size of LC_SEGMENT command";
        return true;
 }
 
@@ -2453,6 +2466,9 @@ void Parser<A>::makeSections()
 
        for (uint32_t i=0; i < _machOSectionsCount; ++i) {
                const macho_section<P>* sect = &_sectionsStart[i];
+               if ( (sect->offset() + sect->size() > _fileLength) && ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) )
+                       throwf("section %s/%s extends beyond end of file,", sect->segname(), sect->sectname());
+
                if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) {
                        if ( strcmp(sect->segname(), "__DWARF") == 0 ) {
                                // note that .o file has dwarf
@@ -3152,8 +3168,8 @@ uint32_t TentativeDefinitionSection<A>::appendAtoms(class Parser<A>& parser, uin
                                        ++alignP2;
                        }
                        // limit alignment of extremely large commons to 2^15 bytes (8-page)
-                       if ( alignP2 > 15 )
-                               alignP2 = 15;
+                       if ( alignP2 > parser.maxDefaultCommonAlignment() )
+                               alignP2 = parser.maxDefaultCommonAlignment();
                        Atom<A>* allocatedSpace = (Atom<A>*)p;
                        new (allocatedSpace) Atom<A>(*this, parser.nameFromSymbol(sym), (pint_t)ULLONG_MAX, size,
                                                                                ld::Atom::definitionTentative,  ld::Atom::combineByName, 
@@ -3950,6 +3966,7 @@ bool Parser<A>::read_comp_unit(const char ** name, const char ** comp_dir,
        const uint8_t * debug_info;
        const uint8_t * debug_abbrev;
        const uint8_t * di;
+       const uint8_t * next_cu;
        const uint8_t * da;
        const uint8_t * end;
        const uint8_t * enda;
@@ -3967,108 +3984,122 @@ bool Parser<A>::read_comp_unit(const char ** name, const char ** comp_dir,
        if ( (_file->_dwarfDebugInfoSect == NULL) || (_file->_dwarfDebugAbbrevSect == NULL) )
                return false;
 
-       debug_info = (uint8_t*)_file->fileContent() + _file->_dwarfDebugInfoSect->offset();
-       debug_abbrev = (uint8_t*)_file->fileContent() + _file->_dwarfDebugAbbrevSect->offset();
-       di = debug_info;
-
        if (_file->_dwarfDebugInfoSect->size() < 12)
-               /* Too small to be a real debug_info section.  */
-               return false;
-       sz = A::P::E::get32(*(uint32_t*)di);
-       di += 4;
-       dwarf64 = sz == 0xffffffff;
-       if (dwarf64)
-               sz = A::P::E::get64(*(uint64_t*)di), di += 8;
-       else if (sz > 0xffffff00)
-               /* Unknown dwarf format.  */
-               return false;
-
-       /* Verify claimed size.  */
-       if (sz + (di - debug_info) > _file->_dwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11))
+       /* Too small to be a real debug_info section.  */
                return false;
 
-       vers = A::P::E::get16(*(uint16_t*)di);
-       if (vers < 2 || vers > 4)
-       /* DWARF version wrong for this code.
-          Chances are we could continue anyway, but we don't know for sure.  */
-               return false;
-       di += 2;
-
-       /* Find the debug_abbrev section.  */
-       abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di);
-       di += dwarf64 ? 8 : 4;
-
-       if (abbrev_base > _file->_dwarfDebugAbbrevSect->size())
-               return false;
-       da = debug_abbrev + abbrev_base;
-       enda = debug_abbrev + _file->_dwarfDebugAbbrevSect->size();
-
-       address_size = *di++;
-
-       /* Find the abbrev number we're looking for.  */
-       end = di + sz;
-       abbrev = read_uleb128 (&di, end);
-       if (abbrev == (uint64_t) -1)
-               return false;
-
-       /* Skip through the debug_abbrev section looking for that abbrev.  */
-       for (;;)
-       {
-               uint64_t this_abbrev = read_uleb128 (&da, enda);
-               uint64_t attr;
-
-               if (this_abbrev == abbrev)
-                       /* This is almost always taken.  */
-                       break;
-               skip_leb128 (&da, enda); /* Skip the tag.  */
-               if (da == enda)
-                       return false;
-               da++;  /* Skip the DW_CHILDREN_* value.  */
-
-               do {
-                       attr = read_uleb128 (&da, enda);
-                       skip_leb128 (&da, enda);
-               } while (attr != 0 && attr != (uint64_t) -1);
-               if (attr != 0)
-                       return false;
-       }
-
-       /* Check that the abbrev is one for a DW_TAG_compile_unit.  */
-       if (read_uleb128 (&da, enda) != DW_TAG_compile_unit)
-       return false;
-       if (da == enda)
-       return false;
-       da++;  /* Skip the DW_CHILDREN_* value.  */
-
-       /* Now, go through the DIE looking for DW_AT_name,
-        DW_AT_comp_dir, and DW_AT_stmt_list.  */
-       for (;;)
-       {
-               uint64_t attr = read_uleb128 (&da, enda);
-               uint64_t form = read_uleb128 (&da, enda);
-
-               if (attr == (uint64_t) -1)
-                       return false;
-               else if (attr == 0)
-                       return true;
-               if (form == DW_FORM_indirect)
-                       form = read_uleb128 (&di, end);
-
-               switch (attr) {
-                       case DW_AT_name:
-                               *name = getDwarfString(form, di);
-                               break;
-                       case DW_AT_comp_dir:
-                               *comp_dir = getDwarfString(form, di);
-                               break;
-                       case DW_AT_stmt_list:
-                               *stmt_list = getDwarfOffset(form, di, dwarf64);
-                               break;
-                       default:
-                               if (! skip_form (&di, end, form, address_size, dwarf64))
-                                       return false;
-               }
-       }
+    debug_info = (uint8_t*)_file->fileContent() + _file->_dwarfDebugInfoSect->offset();
+    debug_abbrev = (uint8_t*)_file->fileContent() + _file->_dwarfDebugAbbrevSect->offset();
+    next_cu = debug_info;
+
+    while ((uint64_t)(next_cu - debug_info) < _file->_dwarfDebugInfoSect->size()) {
+               di = next_cu;
+        sz = A::P::E::get32(*(uint32_t*)di);
+        di += 4;
+        dwarf64 = sz == 0xffffffff;
+        if (dwarf64)
+            sz = A::P::E::get64(*(uint64_t*)di), di += 8;
+        else if (sz > 0xffffff00)
+            /* Unknown dwarf format.  */
+            return false;
+
+        /* Verify claimed size.  */
+        if (sz + (di - debug_info) > _file->_dwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11))
+            return false;
+
+        next_cu = di + sz;
+
+        vers = A::P::E::get16(*(uint16_t*)di);
+        if (vers < 2 || vers > 4)
+        /* DWARF version wrong for this code.
+           Chances are we could continue anyway, but we don't know for sure.  */
+            return false;
+        di += 2;
+
+        /* Find the debug_abbrev section.  */
+        abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di);
+        di += dwarf64 ? 8 : 4;
+
+        if (abbrev_base > _file->_dwarfDebugAbbrevSect->size())
+            return false;
+        da = debug_abbrev + abbrev_base;
+        enda = debug_abbrev + _file->_dwarfDebugAbbrevSect->size();
+
+        address_size = *di++;
+
+        /* Find the abbrev number we're looking for.  */
+        end = di + sz;
+        abbrev = read_uleb128 (&di, end);
+        if (abbrev == (uint64_t) -1)
+            return false;
+
+        /* Skip through the debug_abbrev section looking for that abbrev.  */
+        for (;;)
+        {
+            uint64_t this_abbrev = read_uleb128 (&da, enda);
+            uint64_t attr;
+
+            if (this_abbrev == abbrev)
+                /* This is almost always taken.  */
+                break;
+            skip_leb128 (&da, enda); /* Skip the tag.  */
+            if (da == enda)
+                return false;
+            da++;  /* Skip the DW_CHILDREN_* value.  */
+
+            do {
+                attr = read_uleb128 (&da, enda);
+                skip_leb128 (&da, enda);
+            } while (attr != 0 && attr != (uint64_t) -1);
+            if (attr != 0)
+                return false;
+        }
+
+        /* Check that the abbrev is one for a DW_TAG_compile_unit.  */
+        if (read_uleb128 (&da, enda) != DW_TAG_compile_unit)
+        return false;
+        if (da == enda)
+        return false;
+        da++;  /* Skip the DW_CHILDREN_* value.  */
+
+        /* Now, go through the DIE looking for DW_AT_name,
+         DW_AT_comp_dir, and DW_AT_stmt_list.  */
+        bool skip_to_next_cu = false;
+        while (!skip_to_next_cu) {
+
+            uint64_t attr = read_uleb128 (&da, enda);
+            uint64_t form = read_uleb128 (&da, enda);
+
+            if (attr == (uint64_t) -1)
+                return false;
+            else if (attr == 0)
+                return true;
+            if (form == DW_FORM_indirect)
+                form = read_uleb128 (&di, end);
+
+            switch (attr) {
+                case DW_AT_name:
+                    *name = getDwarfString(form, di);
+                    /* Swift object files may contain two CUs: One
+                       describes the Swift code, one is created by the
+                       clang importer. Skip over the CU created by the
+                       clang importer as it may be empty. */
+                    if (std::string(*name) == "<swift-imported-modules>")
+                        skip_to_next_cu = true;
+                    break;
+                case DW_AT_comp_dir:
+                    *comp_dir = getDwarfString(form, di);
+                    break;
+                case DW_AT_stmt_list:
+                    *stmt_list = getDwarfOffset(form, di, dwarf64);
+                    break;
+                default:
+                    if (! skip_form (&di, end, form, address_size, dwarf64))
+                        return false;
+            }
+        }
+    }
+    return false;
 }
 
 
@@ -4366,8 +4397,9 @@ void CFISection<x86_64>::cfiParse(class Parser<x86_64>& parser, uint8_t* buffer,
                                                                        libunwind::CFI_Atom_Info<CFISection<x86_64>::OAS>::CFI_Atom_Info cfiArray[], 
                                                                        uint32_t& count, const pint_t cuStarts[], uint32_t cuCount)
 {
+       const uint32_t sectionSize = this->_machOSection->size();
        // copy __eh_frame data to buffer
-       memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size());
+       memcpy(buffer, file().fileContent() + this->_machOSection->offset(), sectionSize);
 
        // and apply relocations
        const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)(file().fileContent() + this->_machOSection->reloff());
@@ -4393,6 +4425,8 @@ void CFISection<x86_64>::cfiParse(class Parser<x86_64>& parser, uint8_t* buffer,
                                fprintf(stderr, "CFISection::cfiParse() unexpected relocation type at r_address=0x%08X\n", reloc->r_address());
                                break;
                }
+               if ( reloc->r_address() > sectionSize )
+                       throwf("malformed __eh_frame relocation, offset (0x%08X) is beyond end of section,", reloc->r_address());
                uint64_t*       p64;
                uint32_t*       p32;
                switch ( reloc->r_length() ) {
@@ -4476,7 +4510,8 @@ void CFISection<arm64>::cfiParse(class Parser<arm64>& parser, uint8_t* buffer,
                                                                        uint32_t& count, const pint_t cuStarts[], uint32_t cuCount)
 {
        // copy __eh_frame data to buffer
-       memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size());
+       const uint32_t sectionSize = this->_machOSection->size();
+       memcpy(buffer, file().fileContent() + this->_machOSection->offset(), sectionSize);
 
        // and apply relocations
        const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)(file().fileContent() + this->_machOSection->reloff());
@@ -4508,6 +4543,8 @@ void CFISection<arm64>::cfiParse(class Parser<arm64>& parser, uint8_t* buffer,
                                fprintf(stderr, "CFISection::cfiParse() unexpected relocation type at r_address=0x%08X\n", reloc->r_address());
                                break;
                }
+               if ( reloc->r_address() > sectionSize )
+                       throwf("malformed __eh_frame relocation, offset (0x%08X) is beyond end of section,", reloc->r_address());
                switch ( reloc->r_length() ) {
                        case 3:
                                E::set64(*p64, value + addend64);
@@ -5070,9 +5107,14 @@ void CUSection<A>::parse(class Parser<A>& parser, uint32_t cnt, Info array[])
        }
        
        // scan relocs, extern relocs are needed for personality references (possibly for function/lsda refs??)
+       const uint32_t sectionSize = this->_machOSection->size();
        const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)(this->file().fileContent() + this->_machOSection->reloff());
        const macho_relocation_info<P>* relocsEnd = &relocs[this->_machOSection->nreloc()];
        for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
+               if ( reloc->r_address() & R_SCATTERED )
+                       continue;
+               if ( reloc->r_address() > sectionSize )
+                       throwf("malformed __compact_unwind relocation, offset (0x%08X) is beyond end of section,", reloc->r_address());
                if ( reloc->r_extern() ) {
                        // only expect external relocs on some colummns
                        if ( (reloc->r_address() % sizeof(macho_compact_unwind_entry<P>)) == macho_compact_unwind_entry<P>::personalityFieldOffset() ) {
@@ -6309,10 +6351,14 @@ bool Section<x86_64>::addRelocFixup(class Parser<x86_64>& parser, const macho_re
                        }
                        else {
                                parser.findTargetFromAddressAndSectionNum(contentValue, nextReloc->r_symbolnum(), toTarget);
-                               useDirectBinding = (toTarget.atom->scope() == ld::Atom::scopeTranslationUnit);
+                               useDirectBinding = (toTarget.atom->scope() == ld::Atom::scopeTranslationUnit) || ((toTarget.atom->combine() == ld::Atom::combineByNameAndContent) || (toTarget.atom->combine() == ld::Atom::combineByNameAndReferences));
+                       }
+                       if ( useDirectBinding ) {
+                               if ( (toTarget.atom->combine() == ld::Atom::combineByNameAndContent) || (toTarget.atom->combine() == ld::Atom::combineByNameAndReferences) )
+                                       parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, toTarget.atom);
+                               else
+                                       parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.atom);
                        }
-                       if ( useDirectBinding )
-                               parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.atom);
                        else
                                parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.weakImport, toTarget.name);
                        parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, toTarget.addend);
@@ -7519,6 +7565,8 @@ template <typename A>
 void Section<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&)
 {
        const macho_section<P>* sect = this->machoSection();
+       if ( sect->reloff() + (sect->nreloc() * sizeof(macho_relocation_info<P>)) > parser.fileLength() )
+               throwf("relocations for section %s/%s extends beyond end of file,", sect->segname(), Section<A>::makeSectionName(sect) );
        const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)(file().fileContent() + sect->reloff());
        const uint32_t relocCount = sect->nreloc();
        for (uint32_t r = 0; r < relocCount; ++r) {
@@ -7558,7 +7606,8 @@ void Section<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI
                Atom<A>* end = &_endAtoms[-1];
                for(Atom<A>* p = _beginAtoms; p < end; ++p) {
                        Atom<A>* nextAtom = &p[1];
-                       if ( _altEntries.count(nextAtom) != 0 ) {
+                       // <rdar://problem/22960070> support alt_entry aliases (alias process already added followOn, don't repeat)
+                       if ( (_altEntries.count(nextAtom) != 0) && (p->_objAddress != nextAtom->_objAddress) ) {
                                typename Parser<A>::SourceLocation src(p, 0);
                                parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, nextAtom);
                                typename Parser<A>::SourceLocation src2(nextAtom, 0);
index ca1e5ccd9ef4251d6b4bd17262099d6f99119fe8..e31f7f902b9e37e331713ae486ad3956bdd8d033 100644 (file)
@@ -49,6 +49,7 @@ struct ParserOptions {
        ld::relocatable::File::SourceKind       srcKind;
        bool                    treateBitcodeAsData;
        bool                    usingBitcode;
+       uint8_t                 maxDefaultCommonAlignment;
 };
 
 extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, 
index 825cb0505cb1ffcc978c2006969e1d3deb312281..fb452d8b6fcb9590234b1fb2e2aebd1c216af69b 100644 (file)
@@ -32,6 +32,7 @@
 #include "bitcode.hpp"
 #include "MachOFileAbstraction.hpp"
 #include "MachOTrie.hpp"
+#include "generic_dylib_file.hpp"
 #include "textstub_dylib_file.hpp"
 
 namespace {
@@ -65,7 +66,7 @@ public:
 
        size_t size() const { return _size; }
 
-       std::string str() const { return std::move(std::string(_p, _size)); }
+       std::string str() const { return std::string(_p, _size); }
 
        bool empty() const { return _size == 0; }
 
@@ -195,33 +196,6 @@ struct DynamicLibrary {
                _objcConstraint(ld::File::objcConstraintNone)  {}
 };
 
-static uint32_t parseVersionNumber32(Token token) {
-       if ( token.size() >= 128 )
-               throwf("malformed version number");
-
-       char buffer[128];
-       uint32_t x = 0;
-       uint32_t y = 0;
-       uint32_t z = 0;
-       char* end;
-
-       // Make a null-terminated string.
-       ::memcpy(buffer, token.data(), token.size());
-       buffer[token.size()] = '\0';
-
-       x = strtoul(buffer, &end, 10);
-       if ( *end == '.' ) {
-               y = strtoul(&end[1], &end, 10);
-               if ( *end == '.' ) {
-                       z = strtoul(&end[1], &end, 10);
-               }
-       }
-       if ( (x > 0xffff) || (y > 0xff) || (z > 0xff) )
-               throwf("malformed 32-bit x.y.z version number: %s", buffer);
-
-       return (x << 16) | ( y << 8 ) | z;
-}
-
 ///
 /// A simple text-based dynamic library file parser.
 ///
@@ -310,20 +284,24 @@ class TBDFile {
                }
        }
 
-       bool parseArchFlowSequence(Token archName) {
+       std::vector<std::string> parseArchFlowSequence() {
+               std::vector<std::string> availabledArchitectures;
                expectToken("archs");
+               parseFlowSequence([&](Token name) {
+                       availabledArchitectures.emplace_back(name.str());
+               });
+               return availabledArchitectures;
+       }
 
-               // <rdar://problem/22268737> x86_64h fails to link against text based stubs
-               if ( archName == "x86_64h" )
-                       archName = "x86_64";
+       bool parseArchFlowSequence(std::string &selectedArchName) {
+               auto availabledArchitectures = parseArchFlowSequence();
 
-               bool foundArch = false;
-               parseFlowSequence([&](Token name) {
-                       if ( name == archName )
-                               foundArch = true;
-                       });
+               for (const auto &archName : availabledArchitectures) {
+                       if (archName == selectedArchName)
+                               return true;
+               }
 
-               return foundArch;
+               return false;
        }
 
        void parsePlatform(DynamicLibrary& lib) {
@@ -352,6 +330,18 @@ class TBDFile {
                        throwf("no install name specified");
        }
 
+       uint32_t parseVersionNumber32(Token token) {
+               if ( token.size() >= 128 )
+                       throwf("malformed version number");
+
+               // Make a null-terminated string.
+               char buffer[128];
+               ::memcpy(buffer, token.data(), token.size());
+               buffer[token.size()] = '\0';
+
+               return Options::parseVersionNumber32(buffer);
+       }
+
        void parseCurrentVersion(DynamicLibrary& lib) {
                if ( !hasOptionalToken("current-version") )
                        return;
@@ -395,7 +385,7 @@ class TBDFile {
                else
                        throwf("unexpected token: %s", token.str().c_str());
        }
-       void parseExportsBlock(DynamicLibrary& lib, Token archName) {
+       void parseExportsBlock(DynamicLibrary& lib, std::string &selectedArchName) {
                if ( !hasOptionalToken("exports") )
                        return;
 
@@ -403,7 +393,7 @@ class TBDFile {
                        return;
 
                while ( true ) {
-                       if ( !parseArchFlowSequence(archName) ) {
+                       if ( !parseArchFlowSequence(selectedArchName) ) {
                                Token token;
                                while ( true ) {
                                        token = peek();
@@ -425,8 +415,47 @@ class TBDFile {
                }
        }
 
-       void parseDocument(DynamicLibrary& lib, Token archName) {
-               if ( !parseArchFlowSequence(archName) )
+       std::vector<std::string> getCompatibleArchList(std::string &requestedArchName) {
+               if (requestedArchName == "i386")
+                       return {"i386"};
+               else if (requestedArchName == "x86_64" || requestedArchName == "x86_64h")
+                       return {"x86_64", "x86_64h"};
+               else if (requestedArchName == "armv7" || requestedArchName == "armv7s")
+                       return {"armv7", "armv7s"};
+               else if (requestedArchName == "armv7k")
+                       return {"armv7k"};
+               else if (requestedArchName == "arm64")
+                       return {"arm64"};
+               else
+                       return {};
+       }
+
+       std::string parseAndSelectArchitecture(std::string &requestedArchName) {
+               auto availabledArchitectures = parseArchFlowSequence();
+
+               // First try to find an exact match (cpu type and sub-cpu type).
+               if (std::find(availabledArchitectures.begin(), availabledArchitectures.end(), requestedArchName)
+                       != availabledArchitectures.end())
+                       return requestedArchName;
+
+               // If there is no exact match, then try to find an ABI compatible slice.
+               auto compatibleArchitectures = getCompatibleArchList(requestedArchName);
+               std::vector<std::string> result;
+               std::sort(availabledArchitectures.begin(), availabledArchitectures.end());
+               std::sort(compatibleArchitectures.begin(), compatibleArchitectures.end());
+               std::set_intersection(availabledArchitectures.begin(), availabledArchitectures.end(),
+                                                         compatibleArchitectures.begin(), compatibleArchitectures.end(),
+                                                         std::back_inserter(result));
+
+               if (result.empty())
+                       return std::string();
+               else
+                       return result.front();
+       }
+
+       void parseDocument(DynamicLibrary& lib, std::string &requestedArchName) {
+               auto selectedArchName = parseAndSelectArchitecture(requestedArchName);
+               if (selectedArchName.empty())
                        throwf("invalid arch");
 
                parsePlatform(lib);
@@ -435,27 +464,27 @@ class TBDFile {
                parseCompatibilityVersion(lib);
                parseSwiftVersion(lib);
                parseObjCConstraint(lib);
-               parseExportsBlock(lib, archName);
+               parseExportsBlock(lib, selectedArchName);
        }
 
 public:
        TBDFile(const char* data, uint64_t size) : _tokenizer(data, size) {}
 
-       DynamicLibrary parseFileForArch(Token archName) {
+       DynamicLibrary parseFileForArch(std::string requestedArchName) {
                _tokenizer.reset();
                DynamicLibrary lib;
                expectToken("---");
-               parseDocument(lib, archName);
+               parseDocument(lib, requestedArchName);
                expectToken("...");
-               return std::move(lib);
+               return lib;
        }
 
-       bool validForArch(Token archName) {
+       bool validForArch(std::string requestedArchName) {
                _tokenizer.reset();
                auto token = next();
                if ( token != "---" )
                        return false;
-               return parseArchFlowSequence(archName);
+               return !parseAndSelectArchitecture(requestedArchName).empty();
        }
 
        void dumpTokens() {
@@ -473,52 +502,16 @@ public:
 namespace textstub {
 namespace dylib {
 
-// forward reference
-template <typename A> class File;
-
-
-//
-// An ExportAtom has no content.  It exists so that the linker can track which imported
-// symbols came from which dynamic libraries.
-//
-template <typename A>
-class ExportAtom : public ld::Atom
-{
-public:
-       ExportAtom(const File<A>& f, const char* nm, bool weakDef, bool tlv)
-               : ld::Atom(f._importProxySection, ld::Atom::definitionProxy,
-                                  (weakDef? ld::Atom::combineByName : ld::Atom::combineNever),
-                                  ld::Atom::scopeLinkageUnit,
-                                  (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified),
-                                  symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)),
-                                  _file(f), _name(nm) {}
-       // overrides of ld::Atom
-       virtual const ld::File*                 file() const            { return &_file; }
-       virtual const char*                             name() const            { return _name; }
-       virtual uint64_t                                size() const            { return 0; }
-       virtual uint64_t                                objectAddress() const { return 0; }
-       virtual void                                    copyRawContent(uint8_t buffer[]) const { }
-       virtual void                                    setScope(Scope)         { }
-
-protected:
-       typedef typename A::P                   P;
-       typedef typename A::P::uint_t   pint_t;
-
-       virtual                                                 ~ExportAtom() {}
-
-       const File<A>&                                  _file;
-       const char*                                             _name;
-};
-
-
 //
 // The reader for a dylib extracts all exported symbols names from the memory-mapped
 // dylib, builds a hash table, then unmaps the file.  This is an important memory
 // savings for large dylibs.
 //
 template <typename A>
-class File : public ld::dylib::File
+class File final : public generic::dylib::File<A>
 {
+       using Base = generic::dylib::File<A>;
+
 public:
        static bool             validFile(const uint8_t* fileContent, bool executableOrDylib);
                                        File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
@@ -527,91 +520,13 @@ public:
                                                 cpu_type_t cpuType, const char* archName, uint32_t linkMinOSVersion,
                                                 bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
                                                 bool logAllFiles, const char* installPath, bool indirectDylib);
-       virtual                 ~File() {}
-
-       // overrides of ld::File
-       virtual bool                                                    forEachAtom(ld::File::AtomHandler&) const;
-       virtual bool                                                    justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const;
-       virtual ld::File::ObjcConstraint                objCConstraint() const          { return _objcConstraint; }
-       virtual uint8_t                                                 swiftVersion() const            { return _swiftVersion; }
-
-       // overrides of ld::dylib::File
-       virtual void                                                    processIndirectLibraries(ld::dylib::File::DylibHandler*, bool);
-       virtual bool                                                    providedExportAtom() const      { return _providedAtom; }
-       virtual const char*                                             parentUmbrella() const { return nullptr; }
-       virtual const std::vector<const char*>* allowableClients() const        { return _allowableClients.size() != 0 ? &_allowableClients : nullptr; }
-       virtual bool                                                    hasWeakExternals() const        { return _hasWeakExports; }
-       virtual bool                                                    deadStrippable() const          { return false; }
-       virtual bool                                                    hasPublicInstallName() const{ return _hasPublicInstallName; }
-       virtual bool                                                    hasWeakDefinition(const char* name) const;
-       virtual bool                                                    allSymbolsAreWeakImported() const;
-       virtual bool                                                    installPathVersionSpecific() const { return _installPathOverride; }
-       // All text-based stubs are per definition AppExtensionSafe.
-       virtual bool                                                    appExtensionSafe() const        { return true; };
-       virtual ld::Bitcode*                                    getBitcode() const                      { return _bitcode.get(); }
-
-
-protected:
-       virtual void                                                    assertNoReExportCycles(ReExportChain*) const;
+       virtual                 ~File() noexcept {}
 
 private:
-       typedef typename A::P                                   P;
-       typedef typename A::P::E                                E;
-       typedef typename A::P::uint_t                   pint_t;
-
-       friend class ExportAtom<A>;
-
-       struct CStringHash {
-               std::size_t operator()(const char* __s) const {
-                       unsigned long __h = 0;
-                       for ( ; *__s; ++__s)
-                               __h = 5 * __h + *__s;
-                       return size_t(__h);
-               };
-       };
-       struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; };
-       typedef std::unordered_map<const char*, AtomAndWeak, ld::CStringHash, ld::CStringEquals> NameToAtomMap;
-       typedef std::unordered_set<const char*, CStringHash, ld::CStringEquals>  NameSet;
-
-       struct Dependent { const char* path; File<A>* dylib; };
-
-       virtual std::pair<bool, bool>                   hasWeakDefinitionImpl(const char* name) const;
-       virtual bool                                                    containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& address) const;
-
-       void                                                                    buildExportHashTable(const DynamicLibrary &lib);
-       bool                                                                    isPublicLocation(const char* pth);
-       bool                                                                    wrongOS() { return _wrongOS; }
-       void                                                                    addSymbol(const char* name, bool weak, bool tlv);
-
-       const Options::Platform                                 _platform;
-       cpu_type_t                                                              _cpuType;
-       const uint32_t                                                  _linkMinOSVersion;
-       const bool                                                              _allowSimToMacOSXLinking;
-       const bool                                                              _addVersionLoadCommand;
-       bool                                                                    _linkingFlat;
-       bool                                                                    _implicitlyLinkPublicDylibs;
-       ld::File::ObjcConstraint                                _objcConstraint;
-       uint8_t                                                                 _swiftVersion;
-       ld::Section                                                             _importProxySection;
-       ld::Section                                                             _flatDummySection;
-       std::vector<Dependent>                                  _dependentDylibs;
-       std::vector<const char*>                                _allowableClients;
-       mutable NameToAtomMap                                   _atoms;
-       NameSet                                                                 _ignoreExports;
-       bool                                                                    _noRexports;
-       bool                                                                    _hasWeakExports;
-       bool                                                                    _hasPublicInstallName;
-       mutable bool                                                    _providedAtom;
-       bool                                                                    _wrongOS;
-       bool                                                                    _installPathOverride;
-       bool                                                                    _indirectDylibsProcessed;
-       std::unique_ptr<ld::Bitcode>                    _bitcode;
-       static bool                                                             _s_logHashtable;
-};
-
-template <typename A>
-bool File<A>::_s_logHashtable = false;
+       void                    buildExportHashTable(const DynamicLibrary &lib);
 
+       cpu_type_t              _cpuType;
+};
 
 template <typename A>
 File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime,
@@ -620,18 +535,14 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
                          uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers,
                          bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath,
                          bool indirectDylib)
-       : ld::dylib::File(strdup(path), mTime, ord), _platform(platform), _cpuType(cpuType),
-         _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX),
-         _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace),
-         _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs),
-         _objcConstraint(ld::File::objcConstraintNone), _swiftVersion(0),
-         _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true),
-         _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true),
-         _noRexports(false), _hasWeakExports(false),
-         _hasPublicInstallName(false), _providedAtom(false), _wrongOS(false),
-         _installPathOverride(false), _indirectDylibsProcessed(false),
-         _bitcode(new ld::Bitcode(nullptr, 0))
+       : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, linkingFlatNamespace,
+                  hoistImplicitPublicDylibs, allowSimToMacOSX, addVers),
+         _cpuType(cpuType)
 {
+       this->_bitcode = std::unique_ptr<ld::Bitcode>(new ld::Bitcode(nullptr, 0));
+       // Text stubs are implicit app extension safe.
+       this->_appExtensionSafe = true;
+
        // write out path for -t option
        if ( logAllFiles )
                printf("%s\n", path);
@@ -639,27 +550,66 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
        TBDFile stub((const char*)fileContent, fileLength);
        auto lib = stub.parseFileForArch(archName);
 
-       _noRexports = lib._reexportedLibraries.empty();
-       _hasWeakExports = !lib._weakDefSymbols.empty();
-       _dylibInstallPath = strdup(lib._installName.str().c_str());
-       _dylibCurrentVersion = lib._currentVersion;
-       _dylibCompatibilityVersion = lib._compatibilityVersion;
-       _swiftVersion = lib._swiftVersion;
-       _objcConstraint = lib._objcConstraint;
-       _hasPublicInstallName = isPublicLocation(_dylibInstallPath);
-
-       for (auto &client : lib._allowedClients)
-               _allowableClients.push_back(strdup(client.str().c_str()));
+       this->_noRexports = lib._reexportedLibraries.empty();
+       this->_hasWeakExports = !lib._weakDefSymbols.empty();
+       this->_dylibInstallPath = strdup(lib._installName.str().c_str());
+       this->_dylibCurrentVersion = lib._currentVersion;
+       this->_dylibCompatibilityVersion = lib._compatibilityVersion;
+       this->_swiftVersion = lib._swiftVersion;
+       this->_objcConstraint = lib._objcConstraint;
+       this->_hasPublicInstallName = this->isPublicLocation(this->_dylibInstallPath);
+
+       // if framework, capture framework name
+       const char* lastSlash = strrchr(this->_dylibInstallPath, '/');
+       if ( lastSlash != NULL ) {
+               const char* leafName = lastSlash+1;
+               char frname[strlen(leafName)+32];
+               strcpy(frname, leafName);
+               strcat(frname, ".framework/");
+
+               if ( strstr(this->_dylibInstallPath, frname) != NULL )
+                       this->_frameworkName = leafName;
+       }
+
+  // TEMPORARY HACK BEGIN: Support ancient re-export command LC_SUB_FRAMEWORK.
+       // <rdar://problem/23614899> [TAPI] Support LC_SUB_FRAMEWORK as re-export indicator.
+       auto installName = std::string(this->_dylibInstallPath);
+
+       // All sub-frameworks of ApplicationServices use LC_SUB_FRAMEWORK.
+       if (installName.find("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/") == 0 &&
+                       installName.find(".dylib") == std::string::npos) {
+               this->_parentUmbrella = "ApplicationServices";
+       } else if (installName.find("/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/") == 0) {
+               this->_parentUmbrella = "Carbon";
+       } else if (installName.find("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/") == 0 &&
+                                        installName.find(".dylib") == std::string::npos) {
+               this->_parentUmbrella = "CoreServices";
+       } else if (installName.find("/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib") == 0 ||
+                                        installName.find("/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib") == 0 ||
+                                        installName.find("System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib") == 0) {
+               this->_parentUmbrella = "vecLib";
+       } else if (installName.find("/System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/WebCore") == 0) {
+               this->_parentUmbrella = "WebKit";
+       } else if (installName.find("/usr/lib/system/") == 0 &&
+                          installName != "/usr/lib/system/libkxld.dylib") {
+               this->_parentUmbrella = "System";
+       }
+       // TEMPORARY HACK END
+
+       for (auto &client : lib._allowedClients) {
+               if ((this->_parentUmbrella != nullptr) && (client.str() != this->_parentUmbrella))
+                       this->_allowableClients.push_back(strdup(client.str().c_str()));
+       }
 
        // <rdar://problem/20659505> [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
-       if ( !_allowableClients.empty() )
-               _hasPublicInstallName = false;
+       if ( !this->_allowableClients.empty() )
+               this->_hasPublicInstallName = false;
 
        if ( (lib._platform != platform) && (platform != Options::kPlatformUnknown) ) {
-               _wrongOS = true;
-               if ( _addVersionLoadCommand && !indirectDylib ) {
+               this->_wrongOS = true;
+               if ( this->_addVersionLoadCommand && !indirectDylib ) {
                        if ( buildingForSimulator ) {
-                               if ( !_allowSimToMacOSXLinking )
+                               if ( !this->_allowSimToMacOSXLinking )
                                        throwf("building for %s simulator, but linking against dylib built for %s (%s).",
                                                        Options::platformName(platform), Options::platformName(lib._platform), path);
                        } else {
@@ -669,13 +619,11 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
                }
        }
 
-       _dependentDylibs.reserve(lib._reexportedLibraries.size());
-       for ( auto& reexport : lib._reexportedLibraries ) {
-               Dependent entry;
-               entry.path = strdup(reexport.str().c_str());
-               entry.dylib = nullptr;
-               if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, entry.path) != 0) )
-                       _dependentDylibs.push_back(entry);
+       this->_dependentDylibs.reserve(lib._reexportedLibraries.size());
+       for ( const auto& reexport : lib._reexportedLibraries ) {
+               const char *path = strdup(reexport.str().c_str());
+               if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, path) != 0) )
+                       this->_dependentDylibs.emplace_back(path, true);
        }
 
        // build hash table
@@ -686,324 +634,51 @@ File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
 
 template <typename A>
 void File<A>::buildExportHashTable(const DynamicLibrary& lib) {
-       if ( _s_logHashtable )
+       if (this->_s_logHashtable )
                fprintf(stderr, "ld: building hashtable from text-stub info in %s\n", this->path());
 
        for (auto &sym : lib._symbols)
-               addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/false);
+               this->addSymbol(sym.str().c_str());
 
 #if SUPPORT_ARCH_i386
-       if (_platform == Options::kPlatformOSX && _cpuType == CPU_TYPE_I386) {
+       if (this->_platform == Options::kPlatformOSX && _cpuType == CPU_TYPE_I386) {
                for (auto &sym : lib._classes)
-                       addSymbol((".objc_class_name" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
+                       this->addSymbol((".objc_class_name" + sym.str()).c_str());
        } else {
                for (auto &sym : lib._classes) {
-                       addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
-                       addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
+                       this->addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str());
+                       this->addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str());
                }
        }
 #else
        for (auto &sym : lib._classes) {
-               addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
-               addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
+               this->addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str());
+               this->addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str());
        }
 #endif
 
        for (auto &sym : lib._ivars)
-               addSymbol(("_OBJC_IVAR_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
+               this->addSymbol(("_OBJC_IVAR_$" + sym.str()).c_str());
 
        for (auto &sym : lib._weakDefSymbols)
-               addSymbol(sym.str().c_str(), /*weak=*/true, /*tlv=*/false);
+               this->addSymbol(sym.str().c_str(), /*weak=*/true);
 
        for (auto &sym : lib._tlvSymbols)
-               addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/true);
-}
-
-
-template <typename A>
-void File<A>::addSymbol(const char* name, bool weakDef, bool tlv)
-{
-       // symbols that start with $ld$ are meta-data to the static linker
-       // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
-       if ( strncmp(name, "$ld$", 4) == 0 ) {
-               //    $ld$ <action> $ <condition> $ <symbol-name>
-               const char* symAction = &name[4];
-               const char* symCond = strchr(symAction, '$');
-               if ( symCond != nullptr ) {
-                       char curOSVers[16];
-                       sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF));
-                       if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) {
-                               const char* symName = strchr(&symCond[1], '$');
-                               if ( symName != nullptr ) {
-                                       ++symName;
-                                       if ( strncmp(symAction, "hide$", 5) == 0 ) {
-                                               if ( _s_logHashtable )
-                                                       fprintf(stderr, "  adding %s to ignore set for %s\n", symName, this->path());
-                                               _ignoreExports.insert(strdup(symName));
-                                               return;
-                                       }
-                                       else if ( strncmp(symAction, "add$", 4) == 0 ) {
-                                               this->addSymbol(symName, weakDef, false);
-                                               return;
-                                       }
-                                       else if ( strncmp(symAction, "install_name$", 13) == 0 ) {
-                                               _dylibInstallPath = strdup(symName);
-                                               _installPathOverride = true;
-                                               return;
-                                       }
-                                       else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) {
-                                               _dylibCompatibilityVersion = parseVersionNumber32(symName);
-                                               return;
-                                       }
-                                       else {
-                                               warning("bad symbol action: %s in dylib %s", name, this->path());
-                                       }
-                               }
-                       }
-               }
-               else {
-                       warning("bad symbol condition: %s in dylib %s", name, this->path());
-               }
-       }
-
-       // add symbol as possible export if we are not supposed to ignore it
-       if ( _ignoreExports.count(name) == 0 ) {
-               AtomAndWeak bucket;
-               bucket.atom = nullptr;
-               bucket.weakDef = weakDef;
-               bucket.tlv = tlv;
-               if ( _s_logHashtable )
-                       fprintf(stderr, "  adding %s to hash table for %s\n", name, this->path());
-               _atoms[strdup(name)] = bucket;
-       }
-}
-
-
-template <typename A>
-bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
-{
-       handler.doFile(*this);
-       return false;
+               this->addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/true);
 }
 
-
-template <typename A>
-std::pair<bool, bool> File<A>::hasWeakDefinitionImpl(const char* name) const
-{
-       const auto pos = _atoms.find(name);
-       if ( pos != _atoms.end() )
-               return std::make_pair(true, pos->second.weakDef);
-
-       // look in children that I re-export
-       for (const auto &dep : _dependentDylibs) {
-               auto ret = dep.dylib->hasWeakDefinitionImpl(name);
-               if ( ret.first )
-                       return ret;
-       }
-       return std::make_pair(false, false);
-}
-
-
-template <typename A>
-bool File<A>::hasWeakDefinition(const char* name) const
-{
-       // if supposed to ignore this export, then pretend I don't have it
-       if ( _ignoreExports.count(name) != 0 )
-               return false;
-
-       return hasWeakDefinitionImpl(name).second;
-}
-
-
-// <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
-template <typename A>
-bool File<A>::allSymbolsAreWeakImported() const
-{
-       bool foundNonWeakImport = false;
-       bool foundWeakImport = false;
-       for (const auto &it : _atoms) {
-               const ld::Atom* atom = it.second.atom;
-               if ( atom != nullptr ) {
-                       if ( atom->weakImported() )
-                               foundWeakImport = true;
-                       else
-                               foundNonWeakImport = true;
-               }
-       }
-
-       // don't automatically weak link dylib with no imports
-       // so at least one weak import symbol and no non-weak-imported symbols must be found
-       return foundWeakImport && !foundNonWeakImport;
-}
-
-
-template <typename A>
-bool File<A>::containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& addr) const
-{
-       if ( _ignoreExports.count(name) != 0 )
-               return false;
-
-       // check myself
-       const auto pos = _atoms.find(name);
-       if ( pos != _atoms.end() ) {
-               weakDef = pos->second.weakDef;
-               tlv = pos->second.tlv;
-               addr = 0;
-               return true;
-       }
-
-       // check dylibs I re-export
-       for (const auto& lib : _dependentDylibs) {
-               if ( !lib.dylib->implicitlyLinked() ) {
-                       if ( lib.dylib->containsOrReExports(name, weakDef, tlv, addr) )
-                               return true;
-               }
-       }
-
-       return false;
-}
-
-
-template <typename A>
-bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const
-{
-       // if supposed to ignore this export, then pretend I don't have it
-       if ( _ignoreExports.count(name) != 0 )
-               return false;
-
-
-       AtomAndWeak bucket;
-       uint64_t addr;
-       if ( this->containsOrReExports(name, bucket.weakDef, bucket.tlv, addr) ) {
-               bucket.atom = new ExportAtom<A>(*this, name, bucket.weakDef, bucket.tlv);
-               _atoms[name] = bucket;
-               _providedAtom = true;
-               if ( _s_logHashtable )
-                       fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path());
-               // call handler with new export atom
-               handler.doAtom(*bucket.atom);
-               return true;
-       }
-
-       return false;
-}
-
-
-
-template <typename A>
-bool File<A>::isPublicLocation(const char* path)
-{
-       // -no_implicit_dylibs disables this optimization
-       if ( ! _implicitlyLinkPublicDylibs )
-               return false;
-
-       // /usr/lib is a public location
-       if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == nullptr) )
-               return true;
-
-       // /System/Library/Frameworks/ is a public location
-       if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) {
-               const char* frameworkDot = strchr(&path[27], '.');
-               // but only top level framework
-               // /System/Library/Frameworks/Foo.framework/Versions/A/Foo                 ==> true
-               // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib         ==> false
-               // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar   ==> false
-               // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false
-               if ( frameworkDot != nullptr ) {
-                       int frameworkNameLen = frameworkDot - &path[27];
-                       if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 )
-                               return true;
-               }
-       }
-
-       return false;
-}
-
-template <typename A>
-void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs)
-{
-       // only do this once
-       if ( _indirectDylibsProcessed )
-               return;
-
-       const static bool log = false;
-       if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath());
-       if ( _linkingFlat ) {
-               for (auto& lib : _dependentDylibs) {
-                       lib.dylib = (File<A>*)handler->findDylib(lib.path, this->path());
-               }
-       }
-       else if ( _noRexports ) {
-               // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
-       }
-       else {
-               // two-level, might have re-exports
-               for (auto& lib : _dependentDylibs) {
-                       if ( log )
-                               fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), lib.path);
-                       // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child
-                       lib.dylib = (File<A>*)handler->findDylib(lib.path, this->path());
-                       if ( lib.dylib->hasPublicInstallName() && !lib.dylib->wrongOS() ) {
-                               // promote this child to be automatically added as a direct dependent if this already is
-                               if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(lib.path, lib.dylib->installPath()) == 0) ) {
-                                       if ( log )
-                                               fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", lib.dylib->installPath());
-                                       lib.dylib->setImplicitlyLinked();
-                               }
-                               else if ( lib.dylib->explicitlyLinked() || lib.dylib->implicitlyLinked() ) {
-                                       if ( log )
-                                               fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n");
-                               } else {
-                                       if ( log )
-                                               fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), lib.path);
-                               }
-                       } else {
-                               // add all child's symbols to me
-                               if ( log )
-                                       fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), lib.path);
-                       }
-               }
-       }
-
-       // check for re-export cycles
-       ReExportChain chain;
-       chain.prev = nullptr;
-       chain.file = this;
-       this->assertNoReExportCycles(&chain);
-
-       _indirectDylibsProcessed = true;
-}
-
-template <typename A>
-void File<A>::assertNoReExportCycles(ReExportChain* prev) const
-{
-       // recursively check my re-exported dylibs
-       ReExportChain chain;
-       chain.prev = prev;
-       chain.file = this;
-       for (const auto& dep : _dependentDylibs) {
-               ld::File* child = dep.dylib;
-               // check child is not already in chain
-               for (ReExportChain* p = prev; p != nullptr; p = p->prev) {
-                       if ( p->file == child )
-                               throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path());
-               }
-               if ( dep.dylib != nullptr )
-                       dep.dylib->assertNoReExportCycles(&chain);
-       }
-}
-
-
 template <typename A>
 class Parser
 {
 public:
-       typedef typename A::P   P;
+       using P = typename A::P;
 
-       static bool                             validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName);
+       static bool                             validFile(const uint8_t* fileContent, uint64_t fileLength,
+                                                                         const std::string &path, const char* archName);
        static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
                                                                  time_t mTime, ld::File::Ordinal ordinal, const Options& opts,
-                                                                 bool indirectDylib) {
+                                                                 bool indirectDylib)
+       {
                return new File<A>(fileContent, fileLength, path, mTime, ordinal,
                                                   opts.flatNamespace(),
                                                   opts.implicitlyLinkIndirectPublicDylibs(),
@@ -1021,7 +696,8 @@ public:
 };
 
 template <typename A>
-bool Parser<A>::validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName)
+bool Parser<A>::validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path,
+                                                 const char* archName)
 {
        if ( path.find(".tbd", path.size()-4) == std::string::npos )
                return false;
@@ -1072,5 +748,3 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const ch
        
 } // namespace dylib
 } // namespace textstub
-
-
index 2ad5a3e9029e0479cb2424b9fe57e8a83346e98a..dce37836fb3187d496afe3dce909993d2ef731b5 100644 (file)
@@ -268,6 +268,9 @@ BitcodeTempFile::~BitcodeTempFile()
 
 BitcodeObfuscator::BitcodeObfuscator()
 {
+#if LTO_API_VERSION < 11
+    throwf("compile-time libLTO (%d) didn't support -bitcode_hide_symbols", LTO_API_VERSION);
+#else
     // check if apple internal libLTO is used
     if ( ::lto_get_version() == NULL )
         throwf("libLTO is not loaded");
@@ -282,11 +285,13 @@ BitcodeObfuscator::BitcodeObfuscator()
         _lto_get_asm_symbol_num == NULL || _lto_get_asm_symbol_name == NULL || ::lto_api_version() < 14 )
         throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version());
     _obfuscator = ::lto_codegen_create_in_local_context();
-#if LTO_API_VERSION >= 14
+  #if LTO_API_VERSION >= 14
     lto_codegen_set_should_internalize(_obfuscator, false);
+  #endif
 #endif
 }
 
+
 BitcodeObfuscator::~BitcodeObfuscator()
 {
     ::lto_codegen_dispose(_obfuscator);
@@ -302,7 +307,8 @@ void BitcodeObfuscator::bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath
 #if LTO_API_VERSION >= 13 && LTO_APPLE_INTERNAL
     lto_module_t module = ::lto_module_create_in_codegen_context(bc->getContent(), bc->getSize(), filePath, _obfuscator);
     if ( module == NULL )
-        throwf("object contains invalid bitcode: %s", filePath);
+        throwf("could not reparse object file %s in bitcode bundle: '%s', using libLTO version '%s'",
+               filePath, ::lto_get_error_message(), ::lto_get_version());
     ::lto_codegen_set_module(_obfuscator, module);
     (*_lto_hide_symbols)(_obfuscator);
 #if LTO_API_VERSION >= 15
@@ -397,6 +403,8 @@ void BundleHandler::init()
 
     // read the xar file
     _xar = xar_open(oldXARPath.c_str(), READ);
+    if ( _xar == NULL )
+        throwf("malformed bundle format");
 
     // Init the vector of handler
     xar_iter_t iter = xar_iter_new();
@@ -431,8 +439,10 @@ void BundleHandler::copyXARProp(xar_file_t src, xar_file_t dst)
         const char* key = xar_prop_first(src, p);
         for (int x = 0; x < i; x++)
             key = xar_prop_next(p);
-        if ( !key )
+        if ( !key ) {
+            xar_iter_free(p);
             break;
+        }
         const char* val = NULL;
         xar_prop_get(src, key, &val);
         if ( // Info from bitcode files
@@ -471,7 +481,14 @@ void BitcodeHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator)
     initFile();
 
     // init LTOModule and add asm labels
+#if LTO_API_VERSION < 11
     lto_module_t module = lto_module_create_from_memory(_file_buffer, _file_size);
+#else
+    lto_module_t module = lto_module_create_in_local_context(_file_buffer, _file_size, "bitcode bundle temp file");
+#endif
+    if ( module == NULL )
+        throwf("could not reparse object file in bitcode bundle: '%s', using libLTO version '%s'",
+               ::lto_get_error_message(), ::lto_get_version());
     obfuscator->addAsmSymbolsToMustPreserve(module);
     lto_module_dispose(module);
 }
@@ -617,14 +634,16 @@ void BitcodeBundle::doPass()
         // 3. symbols must not be stripped
         // 4. all the globals if the globals are dead_strip root (ex. dylibs)
         // 5. there is an exported symbol list suggests the symbol should be exported
-        // 6. the special symbols supplied by linker
+        // 6. weak external symbols (not auto-hide)
+        // 7. the special symbols supplied by linker
         for ( auto &sect : _state.sections ) {
             for ( auto &atom : sect->atoms ) {
                 if ( atom == _state.entryPoint ||
                      atom->definition() == ld::Atom::definitionProxy ||
                      atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip ||
                      ( _options.allGlobalsAreDeadStripRoots() && atom->scope() == ld::Atom::scopeGlobal ) ||
-                     ( _options.hasExportRestrictList() && _options.shouldExport(atom->name()) ) )
+                     ( _options.hasExportRestrictList() && _options.shouldExport(atom->name()) ) ||
+                     ( atom->combine() == ld::Atom::combineByName && atom->scope() == ld::Atom::scopeGlobal && !atom->autoHide() ) )
                     obfuscator->addMustPreserveSymbols(atom->name());
             }
         }
@@ -650,6 +669,11 @@ void BitcodeBundle::doPass()
         obfuscator->addMustPreserveSymbols("__mh_dylinker_header");
         obfuscator->addMustPreserveSymbols("__mh_object_header");
         obfuscator->addMustPreserveSymbols("__mh_preload_header");
+
+        // add all the Proxy Atom linker ever created and all the undefs that are possibily dead-stripped.
+        for (auto sym : _state.allUndefProxies)
+            obfuscator->addMustPreserveSymbols(sym);
+        _state.allUndefProxies.clear();
     }
 
     // Open XAR output
index f7465d0ac8970291d880235aa3e7f0e4f423fa4d..65e3cf20d0a60e84e2a33d8cc1c943fa9d7e2cde 100644 (file)
@@ -389,7 +389,7 @@ static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBra
 //
 
 
-static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld::Internal::FinalSection* textSection)
+static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld::Internal::FinalSection* textSection, unsigned stubCount)
 {
        // assign section offsets to each atom in __text section, watch for thumb branches, and find total size
        bool hasThumbBranches = false;
@@ -448,7 +448,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld::
                (const_cast<ld::Atom*>(atom))->setSectionOffset(offset);
                offset += atom->size();
        }
-       uint64_t totalTextSize = offset;
+       uint64_t totalTextSize = offset + stubCount*16;
        if ( (totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches)) && !haveCrossSectionBranches )
                return;
        if (_s_log) fprintf(stderr, "ld: section %s size=%llu, might need branch islands\n", textSection->sectionName(), totalTextSize);
@@ -723,11 +723,19 @@ void doPass(const Options& opts, ld::Internal& state)
                buildAddressMap(opts, state);
        }
        
+       // scan sections for number of stubs
+       unsigned stubCount = 0;
+       for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
+               ld::Internal::FinalSection* sect = *sit;
+               if ( sect->type() == ld::Section::typeStub )
+                       stubCount = sect->atoms.size();
+       }
+
        // scan sections and add island to each code section
        for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
                ld::Internal::FinalSection* sect = *sit;
                if ( sect->type() == ld::Section::typeCode ) 
-                       makeIslandsForSection(opts, state, sect);
+                       makeIslandsForSection(opts, state, sect, stubCount);
        }
 }
 
index efc010ca639880930026aae91872923cb7b64630..7be33d2ea5fa05d7a112555332227e1869636c9b 100644 (file)
@@ -266,7 +266,7 @@ static void extractTarget(ld::Fixup::iterator fixup, ld::Internal& state, const
 
 
 //
-// The tail-call optimzation may result in a function ending in a jump (b) 
+// The tail-call optimization may result in a function ending in a jump (b) 
 // to another functions.  At compile time the compiler does not know 
 // if the target of the jump will be in the same mode (arm vs thumb).
 // The arm/thumb instruction set has a way to change modes in a bl(x)
diff --git a/src/ld/passes/code_dedup.cpp b/src/ld/passes/code_dedup.cpp
new file mode 100644 (file)
index 0000000..e067acf
--- /dev/null
@@ -0,0 +1,375 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2015 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 <stdint.h>
+#include <math.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <mach/machine.h>
+
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <unordered_map>
+
+#include "ld.hpp"
+#include "code_dedup.h"
+
+namespace ld {
+namespace passes {
+namespace dedup {
+
+
+
+class DeDupAliasAtom : public ld::Atom
+{
+public:
+                                                                               DeDupAliasAtom(const ld::Atom* dupOf, const ld::Atom* replacement) :
+                                                                                       ld::Atom(dupOf->section(), ld::Atom::definitionRegular, ld::Atom::combineNever,
+                                                                                                       dupOf->scope(), dupOf->contentType(), ld::Atom::symbolTableIn,
+                                                                                                       false, false, true, 0),
+                                                                                       _dedupOf(dupOf),
+                                                                                       _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, ld::Fixup::bindingDirectlyBound, replacement) {
+                                                if ( dupOf->autoHide() )
+                                                    setAutoHide();
+                                            }
+
+       virtual const ld::File*                         file() const            { return _dedupOf->file(); }
+       virtual const char*                                     translationUnitSource() const
+                                                                                                                       { return NULL; }
+       virtual const char*                                     name() const            { return _dedupOf->name(); }
+       virtual uint64_t                                        size() const            { return 0; }
+       virtual uint64_t                                        objectAddress() const { return 0; }
+       virtual void                                            copyRawContent(uint8_t buffer[]) const { }
+       virtual ld::Fixup::iterator                     fixupsBegin() const     { return &((ld::Fixup*)&_fixup)[0]; }
+       virtual ld::Fixup::iterator                     fixupsEnd()     const   { return &((ld::Fixup*)&_fixup)[1]; }
+
+private:
+       const ld::Atom*                     _dedupOf;
+       ld::Fixup                                                       _fixup;
+};
+
+
+namespace {
+    typedef std::unordered_map<const ld::Atom*, unsigned long> CachedHashes;
+
+    ld::Internal*   sState = nullptr;
+    CachedHashes    sSavedHashes;
+    unsigned long   sHashCount = 0;
+    unsigned long   sFixupCompareCount = 0;
+};
+
+
+// A helper for std::unordered_map<> that hashes the instructions of a function
+struct atom_hashing {
+
+    static unsigned long hash(const ld::Atom* atom) {
+        auto pos = sSavedHashes.find(atom);
+        if ( pos != sSavedHashes.end() )
+            return pos->second;
+
+        const unsigned instructionBytes = atom->size();
+        const uint8_t* instructions = atom->rawContentPointer();
+        unsigned long hash = instructionBytes;
+        for (unsigned i=0; i < instructionBytes; ++i) {
+            hash = (hash * 33) + instructions[i];
+        }
+               for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+            const Atom* target = NULL;
+            switch ( fit->binding ) {
+                case ld::Fixup::bindingDirectlyBound:
+                    target = fit->u.target;
+                    break;
+                case ld::Fixup::bindingsIndirectlyBound:
+                    target = sState->indirectBindingTable[fit->u.bindingIndex];
+                    break;
+                default:
+                    break;
+            }
+            // don't include calls to auto-hide functions in hash because they might be de-dup'ed
+            switch ( fit->kind ) {
+#if SUPPORT_ARCH_arm64
+                case ld::Fixup::kindStoreTargetAddressARM64Branch26:
+#endif
+                case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32:
+                    if ( target && target->autoHide() )
+                        continue; // don't include
+                    break;
+                default:
+                    break;
+            }
+            if ( target != NULL ) {
+                const char* name = target->name();
+                if ( target->contentType() == ld::Atom::typeCString )
+                    name = (const char*)target->rawContentPointer();
+                for (const char* s = name; *s != '\0'; ++s)
+                    hash = (hash * 33) + *s;
+            }
+        }
+        ++sHashCount;
+        sSavedHashes[atom] = hash;
+        return hash;
+    }
+
+    unsigned long operator()(const ld::Atom* atom) const {
+        return hash(atom);
+    }
+};
+
+
+// A helper for std::unordered_map<> that compares functions
+struct atom_equal {
+
+    struct VisitedSet {
+        std::unordered_set<const ld::Atom*> atoms1;
+        std::unordered_set<const ld::Atom*> atoms2;
+    };
+
+    static bool sameFixups(const ld::Atom* atom1, const ld::Atom* atom2, VisitedSet& visited) {
+        ++sFixupCompareCount;
+        //fprintf(stderr, "sameFixups(%s,%s)\n", atom1->name(), atom2->name());
+        Fixup::iterator        f1   = atom1->fixupsBegin();
+        Fixup::iterator        end1 = atom1->fixupsEnd();
+        Fixup::iterator        f2   = atom2->fixupsBegin();
+        Fixup::iterator        end2 = atom2->fixupsEnd();
+        // two atoms must have same number of fixups
+        if ( (end1 - f1) != (end2 - f2) )
+            return false;
+        // if no fixups, fixups are equal
+        if ( f1 == end1 )
+            return true;
+        // all fixups must be the same
+        do {
+            if ( f1->offsetInAtom != f2->offsetInAtom )
+                return false;
+            if ( f1->kind != f2->kind )
+                return false;
+            if ( f1->clusterSize != f2->clusterSize )
+                return false;
+            if ( f1->binding != f2->binding )
+                return false;
+            const Atom* target1 = NULL;
+            const Atom* target2 = NULL;
+            switch ( f1->binding ) {
+                case ld::Fixup::bindingDirectlyBound:
+                    target1 = f1->u.target;
+                    target2 = f2->u.target;
+                    break;
+                case ld::Fixup::bindingsIndirectlyBound:
+                    target1 = sState->indirectBindingTable[f1->u.bindingIndex];
+                    target2 = sState->indirectBindingTable[f2->u.bindingIndex];
+                    break;
+                 case ld::Fixup::bindingNone:
+                    break;
+               default:
+                    return false;
+            }
+            if ( target1 != target2 ) {
+                // targets must match unless they are both calls to functions that will de-dup together
+    #if SUPPORT_ARCH_arm64
+                if ( (f1->kind != ld::Fixup::kindStoreTargetAddressARM64Branch26) && (f1->kind != ld::Fixup::kindStoreTargetAddressX86BranchPCRel32) )
+    #else
+                if ( f1->kind != ld::Fixup::kindStoreTargetAddressX86BranchPCRel32 )
+    #endif
+                    return false;
+                if ( target1->section().type() != target2->section().type() )
+                    return false;
+                if ( target1->section().type() != ld::Section::typeCode )
+                    return false;
+                // to support co-recursive functions, don't call equals() on targets we've already visited
+                if ( ((visited.atoms1.count(target1) == 0) || (visited.atoms2.count(target2) == 0)) && !equal(target1, target2, visited) )
+                    return false;
+              }
+
+            ++f1;
+            ++f2;
+        } while (f1 != end1);
+
+        return true;
+    }
+
+    static bool equal(const ld::Atom* atom1, const ld::Atom* atom2, VisitedSet& visited) {
+        if ( atom_hashing::hash(atom1) != atom_hashing::hash(atom2) )
+            return false;
+        visited.atoms1.insert(atom1);
+        visited.atoms2.insert(atom2);
+        return sameFixups(atom1, atom2, visited);
+    }
+
+    bool operator()(const ld::Atom* atom1, const ld::Atom* atom2) const {
+        VisitedSet visited;
+        return equal(atom1, atom2, visited);
+    }
+};
+
+
+
+void doPass(const Options& opts, ld::Internal& state)
+{
+       const bool log = false;
+       
+       // only de-duplicate in final linked images
+       if ( opts.outputKind() == Options::kObjectFile )
+               return;
+
+       // only de-duplicate for architectures that use relocations that don't store bits in instructions
+       if ( (opts.architecture() != CPU_TYPE_ARM64) && (opts.architecture() != CPU_TYPE_X86_64) )
+               return;
+
+    // support -no_deduplicate to suppress this pass
+    if ( ! opts.deduplicateFunctions() )
+        return;
+
+    const bool verbose = opts.verboseDeduplicate();
+
+    // find __text section
+    ld::Internal::FinalSection* textSection = NULL;
+    for (ld::Internal::FinalSection* sect : state.sections) {
+        if ( (sect->type() == ld::Section::typeCode) && (strcmp(sect->sectionName(), "__text") == 0) ) {
+            textSection = sect;
+            break;
+        }
+    }
+    if ( textSection == NULL )
+        return;
+
+    // build map of auto-hide functions and their duplicates
+    // the key for the map is always the first element in the value vector
+    // the key is always earlier in the atoms list then matching other atoms
+    sState = &state;
+    std::unordered_map<const ld::Atom*, std::vector<const ld::Atom*>, atom_hashing, atom_equal> map;
+    std::unordered_set<const ld::Atom*> masterAtoms;
+    for (const ld::Atom* atom : textSection->atoms) {
+        // ignore empty (alias) atoms
+        if ( atom->size() == 0 )
+            continue;
+        if ( atom->autoHide() )
+            map[atom].push_back(atom);
+       }
+
+    if ( log ) {
+        for (auto& entry : map) {
+            if ( entry.second.size() > 1 ) {
+                printf("Found following matching functions:\n");
+                for (const ld::Atom* atom : entry.second) {
+                    printf("  %p %s\n", atom, atom->name());
+                }
+            }
+        }
+        fprintf(stderr, "duplicate sets count:\n");
+        for (auto& entry : map)
+            fprintf(stderr, "  %p -> %lu\n", entry.first, entry.second.size());
+    }
+
+    // construct alias atoms to replace atoms found to be duplicates
+    unsigned atomsBeingComparedCount = 0;
+    uint64_t dedupSavings = 0;
+    std::vector<const ld::Atom*>& textAtoms = textSection->atoms;
+    std::unordered_map<const ld::Atom*, const ld::Atom*> replacementMap;
+    for (auto& entry : map) {
+        const ld::Atom* masterAtom = entry.first;
+        std::vector<const ld::Atom*>& dups = entry.second;
+        atomsBeingComparedCount += dups.size();
+        if ( dups.size() == 1 )
+            continue;
+        if ( verbose )  {
+            dedupSavings += ((dups.size() - 1) * masterAtom->size());
+            fprintf(stderr, "deduplicate the following %lu functions (%llu bytes apiece):\n", dups.size(), masterAtom->size());
+        }
+        for (const ld::Atom* dupAtom : dups) {
+            if ( verbose )
+                fprintf(stderr, "    %s\n", dupAtom->name());
+            if ( dupAtom == masterAtom )
+                continue;
+            const ld::Atom* aliasAtom = new DeDupAliasAtom(dupAtom, masterAtom);
+            auto pos = std::find(textAtoms.begin(), textAtoms.end(), masterAtom);
+            if ( pos != textAtoms.end() ) {
+                textAtoms.insert(pos, aliasAtom);
+                state.atomToSection[aliasAtom] = textSection;
+                replacementMap[dupAtom] = aliasAtom;
+            }
+        }
+    }
+    if ( verbose )  {
+        fprintf(stderr, "deduplication saved %llu bytes of __text\n", dedupSavings);
+    }
+
+    if ( log ) {
+        fprintf(stderr, "replacement map:\n");
+        for (auto& entry : replacementMap)
+            fprintf(stderr, "  %p -> %p\n", entry.first, entry.second);
+    }
+
+    // walk all atoms and replace references to dups with references to alias
+    for (ld::Internal::FinalSection* sect : state.sections) {
+        for (const ld::Atom* atom : sect->atoms) {
+                       for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+                std::unordered_map<const ld::Atom*, const ld::Atom*>::iterator pos;
+                switch ( fit->binding ) {
+                    case ld::Fixup::bindingsIndirectlyBound:
+                        pos = replacementMap.find(state.indirectBindingTable[fit->u.bindingIndex]);
+                        if ( pos != replacementMap.end() )
+                            state.indirectBindingTable[fit->u.bindingIndex] = pos->second;
+                        break;
+                    case ld::Fixup::bindingDirectlyBound:
+                        pos = replacementMap.find(fit->u.target);
+                        if ( pos != replacementMap.end() )
+                            fit->u.target = pos->second;
+                        break;
+                    default:
+                        break;
+                }
+                       }
+        }
+    }
+
+    if ( log ) {
+        fprintf(stderr, "atoms before pruning:\n");
+        for (const ld::Atom* atom : textSection->atoms)
+            fprintf(stderr, "  %p (size=%llu) %sp\n", atom, atom->size(), atom->name());
+    }
+    // remove replaced atoms from section
+       textSection->atoms.erase(std::remove_if(textSection->atoms.begin(), textSection->atoms.end(),
+                [&](const ld::Atom* atom) {
+                    return (replacementMap.count(atom) != 0);
+                }),
+                textSection->atoms.end());
+
+   for (auto& entry : replacementMap)
+        state.atomToSection.erase(entry.first);
+
+    if ( log ) {
+        fprintf(stderr, "atoms after pruning:\n");
+        for (const ld::Atom* atom : textSection->atoms)
+            fprintf(stderr, "  %p (size=%llu) %sp\n", atom, atom->size(), atom->name());
+    }
+
+   //fprintf(stderr, "hash-count=%lu, fixup-compares=%lu, atom-count=%u\n", sHashCount, sFixupCompareCount, atomsBeingComparedCount);
+}
+
+
+} // namespace dedup
+} // namespace passes 
+} // namespace ld 
diff --git a/src/ld/passes/code_dedup.h b/src/ld/passes/code_dedup.h
new file mode 100644 (file)
index 0000000..0b803ba
--- /dev/null
@@ -0,0 +1,45 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2015 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@
+ */
+
+
+#ifndef __DEDUPE_H__
+#define __DEDUPE_H__
+
+#include "Options.h"
+#include "ld.hpp"
+
+
+namespace ld {
+namespace passes {
+namespace dedup {
+
+// called by linker to merge duplicate functions
+extern void doPass(const Options& opts, ld::Internal& internal);
+
+
+} // namespace huge
+} // namespace passes 
+} // namespace ld 
+
+#endif // __DEDUPE_H__
index 1d257ee268407042ebc6f94258da80e8343c6b58..d2847bb98d16a84ab176e96110214f849fe1ae02 100644 (file)
@@ -630,11 +630,10 @@ unsigned int UnwindInfoAtom<A>::makeCompressedSecondLevelPage(const std::vector<
        // keep adding entries to page until:
        //  1) encoding table plus entry table plus header exceed page size
        //  2) the file offset delta from the first to last function > 24 bits
-       //  3) custom encoding index reachs 255
+       //  3) custom encoding index reaches 255
        //  4) run out of uniqueInfos to encode
        std::map<compact_unwind_encoding_t, unsigned int> pageSpecificEncodings;
        uint32_t space4 =  (pageSize - sizeof(unwind_info_compressed_second_level_page_header))/sizeof(uint32_t);
-       std::vector<uint8_t> encodingIndexes;
        int index = endIndex-1;
        int entryCount = 0;
        uint64_t lastEntryAddress = uniqueInfos[index].funcTentAddress;
@@ -646,6 +645,7 @@ unsigned int UnwindInfoAtom<A>::makeCompressedSecondLevelPage(const std::vector<
                std::map<compact_unwind_encoding_t, unsigned int>::const_iterator pos = commonEncodings.find(info.encoding);
                if ( pos != commonEncodings.end() ) {
                        encodingIndex = pos->second;
+                       if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): funcIndex=%d, re-use commonEncodings[%d]=0x%08X\n", index, encodingIndex, info.encoding);
                }
                else {
                        // no commmon entry, so add one on this page
@@ -657,12 +657,13 @@ unsigned int UnwindInfoAtom<A>::makeCompressedSecondLevelPage(const std::vector<
                        std::map<compact_unwind_encoding_t, unsigned int>::iterator ppos = pageSpecificEncodings.find(encoding);
                        if ( ppos != pageSpecificEncodings.end() ) {
                                encodingIndex = pos->second;
+                               if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): funcIndex=%d, re-use pageSpecificEncodings[%d]=0x%08X\n", index, encodingIndex, encoding);
                        }
                        else {
                                encodingIndex = commonEncodings.size() + pageSpecificEncodings.size();
                                if ( encodingIndex <= 255 ) {
                                        pageSpecificEncodings[encoding] = encodingIndex;
-                                       if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): pageSpecificEncodings[%d]=0x%08X\n", encodingIndex, encoding); 
+                                       if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): funcIndex=%d, pageSpecificEncodings[%d]=0x%08X\n", index, encodingIndex, encoding);
                                }
                                else {
                                        canDo = false; // case 3)
@@ -671,8 +672,6 @@ unsigned int UnwindInfoAtom<A>::makeCompressedSecondLevelPage(const std::vector<
                                }
                        }
                }
-               if ( canDo ) 
-                       encodingIndexes.push_back(encodingIndex);
                // compute function offset
                uint32_t funcOffsetWithInPage = lastEntryAddress - info.funcTentAddress;
                if ( funcOffsetWithInPage > 0x00FFFF00 ) {
@@ -680,16 +679,16 @@ unsigned int UnwindInfoAtom<A>::makeCompressedSecondLevelPage(const std::vector<
                        canDo = false; // case 2)
                        if (_s_log) fprintf(stderr, "can't use compressed page with %u entries because function offset too big\n", entryCount);
                }
-               else {
-                       ++entryCount;
-               }
                // check room for entry
-               if ( (pageSpecificEncodings.size()+entryCount) >= space4 ) {
+               if ( (pageSpecificEncodings.size()+entryCount) > space4 ) {
                        canDo = false; // case 1)
                        --entryCount;
                        if (_s_log) fprintf(stderr, "end of compressed page with %u entries because full\n", entryCount);
                }
                //if (_s_log) fprintf(stderr, "space4=%d, pageSpecificEncodings.size()=%ld, entryCount=%d\n", space4, pageSpecificEncodings.size(), entryCount);
+               if ( canDo ) {
+                       ++entryCount;
+               }
        }
        
        // check for cases where it would be better to use a regular (non-compressed) page
@@ -725,6 +724,7 @@ unsigned int UnwindInfoAtom<A>::makeCompressedSecondLevelPage(const std::vector<
                uint8_t encodingIndex;
                if ( encodingMeansUseDwarf(info.encoding) ) {
                        // dwarf entries are always in page specific encodings
+                       assert(pageSpecificEncodings.find(info.encoding+i) != pageSpecificEncodings.end());
                        encodingIndex = pageSpecificEncodings[info.encoding+i];
                }
                else {
@@ -760,16 +760,22 @@ unsigned int UnwindInfoAtom<A>::makeCompressedSecondLevelPage(const std::vector<
 
 
 
-
-
-static uint64_t calculateEHFrameSize(const ld::Internal& state)
+static uint64_t calculateEHFrameSize(ld::Internal& state)
 {
+       bool allCIEs = true;
        uint64_t size = 0;
-       for (std::vector<ld::Internal::FinalSection*>::const_iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
-               ld::Internal::FinalSection* sect = *sit;
+       for (ld::Internal::FinalSection* sect : state.sections) {
                if ( sect->type() == ld::Section::typeCFI ) {
-                       for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
-                               size += (*ait)->size();
+                       for (const ld::Atom* atom : sect->atoms) {
+                               size += atom->size();
+                               if ( strcmp(atom->name(), "CIE") != 0 )
+                                       allCIEs = false;
+                       }
+                       if ( allCIEs ) {
+                               // <rdar://problem/21427393> Linker generates eh_frame data even when there's only an unused CIEs in it
+                               sect->atoms.clear();
+                               state.sections.erase(std::remove(state.sections.begin(), state.sections.end(), sect), state.sections.end());
+                               return 0;
                        }
                }
        }
index ad4673cf564f94e764e7612c3adc5d67374cde13..c65ca2122282e5cc2abf7c5e82a99bd1a882ff0f 100644 (file)
@@ -755,6 +755,21 @@ bool OptimizeCategories<A>::hasProperties(ld::Internal& state, const std::vector
 }
 
 
+static const ld::Atom* fixClassAliases(const ld::Atom* classAtom)
+{
+       if ( (classAtom->size() != 0) || (classAtom->definition() == ld::Atom::definitionProxy) )
+               return classAtom;
+
+       for (ld::Fixup::iterator fit=classAtom->fixupsBegin(); fit != classAtom->fixupsEnd(); ++fit) {
+               if ( fit->kind == ld::Fixup::kindNoneFollowOn ) {
+                       assert(fit->offsetInAtom == 0);
+                       assert(fit->binding == ld::Fixup::bindingDirectlyBound);
+                       return fit->u.target;
+               }
+       }
+
+       return classAtom;
+}
 
 //
 // Helper for std::remove_if
@@ -841,7 +856,7 @@ void OptimizeCategories<A>::doit(const Options& opts, ld::Internal& state)
                                // ignore categories also in __objc_nlcatlist
                                if ( nlcatListAtoms.count(categoryAtom) != 0 )
                                        continue;
-                               const ld::Atom* categoryOnClassAtom = Category<A>::getClass(state, categoryAtom, hasAddend); 
+                               const ld::Atom* categoryOnClassAtom = fixClassAliases(Category<A>::getClass(state, categoryAtom, hasAddend));
                                assert(categoryOnClassAtom != NULL);
                                // only look at classes defined in this image
                                if ( categoryOnClassAtom->definition() != ld::Atom::definitionProxy ) {
index bee5f2f67c4f3f2432c4c4a8b8eb91a7525818e3..abed7e3b62f64bb49076115f5f4ff77df9becd27 100644 (file)
@@ -135,8 +135,12 @@ const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state)
                                        if ( (target->definition() == ld::Atom::definitionRegular) 
                                                && (target->combine() == ld::Atom::combineByName) 
                                                && ((target->symbolTableInclusion() == ld::Atom::symbolTableIn) 
-                                                || (target->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) )
+                                                || (target->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) {
+                                               // don't make stubs for auto-hide symbols
+                                               if ( target->autoHide() && (!_options.hasExportMaskList() || !_options.shouldExport(target->name())) )
+                                                       return NULL;
                                                return target;
+                                       }
                                        // create stub if target is interposable
                                        if ( _options.interposable(target->name()) ) 
                                                return target;
@@ -174,8 +178,10 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport)
        if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) 
                forLazyDylib = true;
        bool stubToResolver = (target.contentType() == ld::Atom::typeResolver);
+#if SUPPORT_ARCH_arm_any || SUPPORT_ARCH_arm64
        bool usingDataConst =  _options.useDataConstSegment();
-       
+#endif
+
        if ( usingCompressedLINKEDIT() && !forLazyDylib ) {
                if ( _internal->compressedFastBinderProxy == NULL )
                        throwf("symbol dyld_stub_binder not found (normally in libSystem.dylib).  Needed to perform lazy binding to function %s", target.name());
index aec6ebe1ff88a8336173033dad0e2e7ba91b507c..0465cd579c9f68a0718dc3ac37e8c0b6cf0d1f3d 100644 (file)
@@ -27,6 +27,7 @@
 #include <sys/mman.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
@@ -100,8 +101,8 @@ class MachOChecker
 {
 public:
        static bool                                                                     validFile(const uint8_t* fileContent);
-       static MachOChecker<A>*                                         make(const uint8_t* fileContent, uint32_t fileLength, const char* path
-                                                                                                               { return new MachOChecker<A>(fileContent, fileLength, path); }
+       static MachOChecker<A>*                                         make(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot)
+                                                                                                               { return new MachOChecker<A>(fileContent, fileLength, path, verifierDstRoot); }
        virtual                                                                         ~MachOChecker() {}
 
 
@@ -126,7 +127,7 @@ private:
 
        typedef std::unordered_set<const char*, CStringHash, CStringEquals>  StringSet;
 
-                                                                                               MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path);
+                                                                                               MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot);
        void                                                                            checkMachHeader();
        void                                                                            checkLoadCommands();
        void                                                                            checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect);
@@ -137,6 +138,11 @@ private:
        void                                                                            checkRelocations();
        void                                                                            checkExternalReloation(const macho_relocation_info<P>* reloc);
        void                                                                            checkLocalReloation(const macho_relocation_info<P>* reloc);
+       void                                                                            verify();
+       void                                                                            verifyInstallName();
+       void                                                                            verifyNoRpaths();
+       void                                                                            verifyNoFlatLookups();
+
        pint_t                                                                          relocBase();
        bool                                                                            addressInWritableSegment(pint_t address);
        bool                                                                            hasTextRelocInRange(pint_t start, pint_t end);
@@ -145,12 +151,14 @@ private:
        bool                                                                            addressIsBindingSite(pint_t addr);
        pint_t                                                                          getInitialStackPointer(const macho_thread_command<P>*);
        pint_t                                                                          getEntryPoint(const macho_thread_command<P>*);
-       
+       const char*                                                                     archName();
        
        
        const char*                                                                     fPath;
+       const char*                                                                     fDstRoot;
        const macho_header<P>*                                          fHeader;
        uint32_t                                                                        fLength;
+       const char*                                                                     fInstallName;
        const char*                                                                     fStrings;
        const char*                                                                     fStringsEnd;
        const macho_nlist<P>*                                           fSymbols;
@@ -164,6 +172,7 @@ private:
        uint32_t                                                                        fExternalRelocationsCount;
        bool                                                                            fWriteableSegmentWithAddrOver4G;
        bool                                                                            fSlidableImage;
+       bool                                                                            fHasLC_RPATH;
        const macho_segment_command<P>*                         fFirstSegment;
        const macho_segment_command<P>*                         fFirstWritableSegment;
        const macho_segment_command<P>*                         fTEXTSegment;
@@ -173,43 +182,6 @@ private:
 };
 
 
-
-template <>
-bool MachOChecker<ppc>::validFile(const uint8_t* fileContent)
-{      
-       const macho_header<P>* header = (const macho_header<P>*)fileContent;
-       if ( header->magic() != MH_MAGIC )
-               return false;
-       if ( header->cputype() != CPU_TYPE_POWERPC )
-               return false;
-       switch (header->filetype()) {
-               case MH_EXECUTE:
-               case MH_DYLIB:
-               case MH_BUNDLE:
-               case MH_DYLINKER:
-                       return true;
-       }
-       return false;
-}
-
-template <>
-bool MachOChecker<ppc64>::validFile(const uint8_t* fileContent)
-{      
-       const macho_header<P>* header = (const macho_header<P>*)fileContent;
-       if ( header->magic() != MH_MAGIC_64 )
-               return false;
-       if ( header->cputype() != CPU_TYPE_POWERPC64 )
-               return false;
-       switch (header->filetype()) {
-               case MH_EXECUTE:
-               case MH_DYLIB:
-               case MH_BUNDLE:
-               case MH_DYLINKER:
-                       return true;
-       }
-       return false;
-}
-
 template <>
 bool MachOChecker<x86>::validFile(const uint8_t* fileContent)
 {      
@@ -246,6 +218,7 @@ bool MachOChecker<x86_64>::validFile(const uint8_t* fileContent)
        return false;
 }
 
+#if SUPPORT_ARCH_arm_any
 template <>
 bool MachOChecker<arm>::validFile(const uint8_t* fileContent)
 {      
@@ -263,6 +236,7 @@ bool MachOChecker<arm>::validFile(const uint8_t* fileContent)
        }
        return false;
 }
+#endif
 
 #if SUPPORT_ARCH_arm64
 template <>
@@ -284,27 +258,12 @@ bool MachOChecker<arm64>::validFile(const uint8_t* fileContent)
 }
 #endif
 
-template <> uint8_t MachOChecker<ppc>::loadCommandSizeMask()   { return 0x03; }
-template <> uint8_t MachOChecker<ppc64>::loadCommandSizeMask() { return 0x07; }
 template <> uint8_t MachOChecker<x86>::loadCommandSizeMask()   { return 0x03; }
 template <> uint8_t MachOChecker<x86_64>::loadCommandSizeMask() { return 0x07; }
 template <> uint8_t MachOChecker<arm>::loadCommandSizeMask()   { return 0x03; }
-#if SUPPORT_ARCH_arm64
 template <> uint8_t MachOChecker<arm64>::loadCommandSizeMask() { return 0x07; }
-#endif
 
 
-template <>
-ppc::P::uint_t MachOChecker<ppc>::getInitialStackPointer(const macho_thread_command<ppc::P>* threadInfo)
-{
-       return threadInfo->thread_register(3);
-}
-
-template <>
-ppc64::P::uint_t MachOChecker<ppc64>::getInitialStackPointer(const macho_thread_command<ppc64::P>* threadInfo)
-{
-       return threadInfo->thread_register(3);
-}
 
 template <>
 x86::P::uint_t MachOChecker<x86>::getInitialStackPointer(const macho_thread_command<x86::P>* threadInfo)
@@ -324,25 +283,12 @@ arm::P::uint_t MachOChecker<arm>::getInitialStackPointer(const macho_thread_comm
        return threadInfo->thread_register(13);
 }
 
-#if SUPPORT_ARCH_arm64
 template <>
 arm64::P::uint_t MachOChecker<arm64>::getInitialStackPointer(const macho_thread_command<arm64::P>* threadInfo)
 {
        throw "LC_UNIXTHREAD not supported for arm64";
 }
-#endif
 
-template <>
-ppc::P::uint_t MachOChecker<ppc>::getEntryPoint(const macho_thread_command<ppc::P>* threadInfo)
-{
-       return threadInfo->thread_register(0);
-}
-
-template <>
-ppc64::P::uint_t MachOChecker<ppc64>::getEntryPoint(const macho_thread_command<ppc64::P>* threadInfo)
-{
-       return threadInfo->thread_register(0);
-}
 
 template <>
 x86::P::uint_t MachOChecker<x86>::getEntryPoint(const macho_thread_command<x86::P>* threadInfo)
@@ -362,19 +308,47 @@ arm::P::uint_t MachOChecker<arm>::getEntryPoint(const macho_thread_command<arm::
        return threadInfo->thread_register(15);
 }
 
-#if SUPPORT_ARCH_arm64
 template <>
 arm64::P::uint_t MachOChecker<arm64>::getEntryPoint(const macho_thread_command<arm64::P>* threadInfo)
 {
        throw "LC_UNIXTHREAD not supported for arm64";
 }
-#endif
+
+
+template <typename A>
+const char* MachOChecker<A>::archName()
+{
+       switch ( fHeader->cputype() ) {
+               case CPU_TYPE_I386:
+                       return "i386";
+               case CPU_TYPE_X86_64:
+                       if ( fHeader->cpusubtype() == CPU_SUBTYPE_X86_64_H )
+                               return "x86_64h";
+                       else
+                               return "x86_64";
+               case CPU_TYPE_ARM:
+                       switch ( fHeader->cpusubtype() ) {
+                               case CPU_SUBTYPE_ARM_V7:
+                                       return "armv7";
+                               case CPU_SUBTYPE_ARM_V7S:
+                                       return "armv7s";
+                               case CPU_SUBTYPE_ARM_V7K:
+                                       return "armv7k";
+                       }
+                       return "arm";
+               case CPU_TYPE_ARM64:
+                       return "arm64";
+       }
+       return "unknown";
+}
+
+
 
 template <typename A>
-MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path)
- : fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0),
+MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot)
+ : fHeader(NULL), fLength(fileLength), fInstallName(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0),
  fLocalRelocations(NULL),  fLocalRelocationsCount(0),  fExternalRelocations(NULL),  fExternalRelocationsCount(0),
- fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), 
+ fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fHasLC_RPATH(false), fFirstSegment(NULL), fFirstWritableSegment(NULL),
  fTEXTSegment(NULL), fDyldInfo(NULL), fSectionCount(0)
 {
        // sanity check
@@ -382,6 +356,7 @@ MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, c
                throw "not a mach-o file that can be checked";
 
        fPath = strdup(path);
+       fDstRoot = verifierDstRoot ? strdup(verifierDstRoot) : NULL;
        fHeader = (const macho_header<P>*)fileContent;
        
        // sanity check header
@@ -397,6 +372,9 @@ MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, c
        checkSymbolTable();
        
        checkInitTerms();
+
+       if ( verifierDstRoot != NULL )
+               verify();
 }
 
 
@@ -407,7 +385,7 @@ void MachOChecker<A>::checkMachHeader()
                throw "sizeofcmds in mach_header is larger than file";
        
        uint32_t flags = fHeader->flags();
-       const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFE000000;
+       const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFC000000;
        if ( flags & invalidBits )
                throw "invalid bits in mach_header flags";
        if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) ) 
@@ -438,6 +416,7 @@ void MachOChecker<A>::checkLoadCommands()
        const uint32_t cmd_count = fHeader->ncmds();
        const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
        const macho_load_command<P>* cmd = cmds;
+       const macho_dylib_command<P>* dylibID;
        for (uint32_t i = 0; i < cmd_count; ++i) {
                uint32_t size = cmd->cmdsize();
                if ( (size & this->loadCommandSizeMask()) != 0 )
@@ -452,9 +431,8 @@ void MachOChecker<A>::checkLoadCommands()
                        case LC_SYMTAB:
                        case LC_DYSYMTAB:
                        case LC_LOAD_DYLIB:
-                       case LC_ID_DYLIB:
-                       case LC_LOAD_DYLINKER:
                        case LC_ID_DYLINKER:
+                       case LC_LOAD_DYLINKER:
                        case macho_routines_command<P>::CMD:
                        case LC_SUB_FRAMEWORK:
                        case LC_SUB_CLIENT:
@@ -469,13 +447,23 @@ void MachOChecker<A>::checkLoadCommands()
                        case LC_LOAD_UPWARD_DYLIB:
                        case LC_VERSION_MIN_MACOSX:
                        case LC_VERSION_MIN_IPHONEOS:
-                       case LC_RPATH:
                        case LC_FUNCTION_STARTS:
                        case LC_DYLD_ENVIRONMENT:
                        case LC_DATA_IN_CODE:
                        case LC_DYLIB_CODE_SIGN_DRS:
                        case LC_SOURCE_VERSION:
                                break;
+                       case LC_RPATH:
+                               fHasLC_RPATH = true;
+                               break;
+                       case LC_ID_DYLIB:
+                               dylibID = (macho_dylib_command<P>*)cmd;
+                               if ( dylibID->name_offset() > size )
+                                       throwf("malformed mach-o: LC_ID_DYLIB load command has offset (%u) outside its size (%u)", dylibID->name_offset(), size);
+                               if ( (dylibID->name_offset() + strlen(dylibID->name()) + 1) > size )
+                                       throwf("malformed mach-o: LC_ID_DYLIB load command string extends beyond end of load command");
+                               fInstallName = dylibID->name();
+                               break;
                        case LC_DYLD_INFO:
                        case LC_DYLD_INFO_ONLY:
                                fDyldInfo = (macho_dyld_info_command<P>*)cmd;
@@ -533,7 +521,7 @@ void MachOChecker<A>::checkLoadCommands()
                                else {
                                        throw "overlapping segment vm addresses";
                                }
-                               segmentAddressRanges.push_back(std::make_pair<pint_t, pint_t>(startAddr, endAddr));
+                               segmentAddressRanges.push_back(std::make_pair(startAddr, endAddr));
                        }
                        // see if this overlaps another segment file offset range
                        uint64_t startOffset = segCmd->fileoff();
@@ -550,7 +538,7 @@ void MachOChecker<A>::checkLoadCommands()
                                else {
                                        throw "overlapping segment file data";
                                }
-                               segmentFileOffsetRanges.push_back(std::make_pair<pint_t, pint_t>(startOffset, endOffset));
+                               segmentFileOffsetRanges.push_back(std::make_pair(startOffset, endOffset));
                                // check is within file bounds
                                if ( (startOffset > fLength) || (endOffset > fLength) )
                                        throw "segment file data is past end of file";
@@ -690,6 +678,16 @@ void MachOChecker<A>::checkLoadCommands()
                }
        }
 
+       // verify dylib has LC_ID_DYLIB
+       if ( fHeader->filetype() == MH_DYLIB ) {
+               if ( fInstallName == NULL )
+                       throw "MH_DYLIB missing LC_ID_DYLIB";
+       }
+       else {
+               if ( fInstallName != NULL )
+                       throw "LC_ID_DYLIB found but file type is not MH_DYLIB";
+       }
+
        // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
        cmd = cmds;
        bool foundDynamicSymTab = false;
@@ -702,8 +700,8 @@ void MachOChecker<A>::checkLoadCommands()
                                        fSymbols = (const macho_nlist<P>*)((char*)fHeader + symtab->symoff());
                                        if ( symtab->symoff() < linkEditSegment->fileoff() )
                                                throw "symbol table not in __LINKEDIT";
-                                       if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist<P>*)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
-                                               throw "symbol table end not in __LINKEDIT";
+                                       if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist<P>*)) > symtab->stroff() )
+                                               throw "symbol table overlaps string pool";
                                        if ( (symtab->symoff() % sizeof(pint_t)) != 0 )
                                                throw "symbol table start not pointer aligned";
                                        fStrings = (char*)fHeader + symtab->stroff();
@@ -837,7 +835,85 @@ void MachOChecker<A>::checkSection(const macho_segment_command<P>* segCmd, const
 }
 
 
+template <typename A>
+void MachOChecker<A>::verify()
+{
+       bool sharedCacheCandidate = false;
+       if ( fInstallName != NULL ) {
+               if ( (strncmp(fInstallName, "/usr/lib/", 9) == 0) || (strncmp(fInstallName, "/System/Library/", 16) == 0) ) {
+                       sharedCacheCandidate = true;
+                       verifyInstallName();
+                       verifyNoRpaths();
+               }
+       }
+       verifyNoFlatLookups();
+}
+
+
+template <typename A>
+void MachOChecker<A>::verifyInstallName()
+{
+       // Don't allow @rpath to be used as -install_name for OS dylibs
+       if ( strncmp(fInstallName, "@rpath/", 7) == 0 ) {
+               printf("os_dylib_rpath_install_name\tfatal\t-install_name uses @rpath in arch %s\n", archName());
+       }
+       else {
+               // Verify -install_name match actual path of dylib
+               const char* installPathWithinDstRoot = &fPath[strlen(fDstRoot)];
+               if ( strcmp(installPathWithinDstRoot, fInstallName) != 0 ) {
+                       // see if install name is a symlink to actual file
+                       bool symlinkToDylib = false;
+                       char absDstPath[PATH_MAX];
+                       if ( realpath(fDstRoot, absDstPath) != NULL ) {
+                               char fullInstallNamePath[PATH_MAX];
+                               strlcpy(fullInstallNamePath, absDstPath, PATH_MAX);
+                               strlcat(fullInstallNamePath, fInstallName, PATH_MAX);
+                               char absInstallNamePath[PATH_MAX];
+                               if ( realpath(fullInstallNamePath, absInstallNamePath) != NULL ) {
+                                       char absFPath[PATH_MAX];
+                                       if ( realpath(fPath, absFPath) != NULL ) {
+                                               if ( strcmp(absInstallNamePath, absFPath) == 0 )
+                                                       symlinkToDylib = true;
+                                       }
+                               }
+                       }
+                       if ( !symlinkToDylib )
+                               printf("os_dylib_bad_install_name\twarn\t-install_name does not match install location in arch %s\n", archName());
+               }
+       }
 
+}
+
+template <typename A>
+void MachOChecker<A>::verifyNoRpaths()
+{
+       // Don't allow OS dylibs to add rpaths
+       if ( fHasLC_RPATH ) {
+               printf("os_dylib_rpath\twarn\tcontains LC_RPATH load command in arch %s\n", archName());
+       }
+}
+
+
+template <typename A>
+void MachOChecker<A>::verifyNoFlatLookups()
+{
+       if ( (fHeader->flags() & MH_TWOLEVEL) == 0 ) {
+               printf("os_dylib_flat_namespace\twarn\tbuilt with -flat_namespace in arch %s\n", archName());
+               return;
+       }
+
+       if ( fDynamicSymbolTable != NULL ) {
+               const macho_nlist<P>* const     undefinesStart = &fSymbols[fDynamicSymbolTable->iundefsym()];
+               const macho_nlist<P>* const undefinesEnd = &undefinesStart[fDynamicSymbolTable->nundefsym()];
+               for(const macho_nlist<P>* sym = undefinesStart; sym < undefinesEnd; ++sym) {
+                       //printf("0x%04X %s\n", sym->n_desc(), &fStrings[sym->n_strx()]);
+                       if ( GET_LIBRARY_ORDINAL(sym->n_desc()) == DYNAMIC_LOOKUP_ORDINAL ) {
+                               const char* symName = &fStrings[sym->n_strx()];
+                               printf("os_dylib_undefined_dynamic_lookup\twarn\tbuilt with -undefined dynamic_lookup for symbol %s in arch %s\n", symName, archName());
+                       }
+               }
+       }
+}
 
 template <typename A>
 void MachOChecker<A>::checkIndirectSymbolTable()
@@ -879,6 +955,44 @@ void MachOChecker<A>::checkIndirectSymbolTable()
                }
                cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
        }
+
+
+       if ( fDynamicSymbolTable->ilocalsym() != 0 )
+               throwf("start of local symbols (%d) not at start of symbol table", fDynamicSymbolTable->ilocalsym());
+
+       if ( fDynamicSymbolTable->ilocalsym() > fSymbolCount )
+               throwf("start of local symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable->ilocalsym(), fSymbolCount);
+       if ( fDynamicSymbolTable->ilocalsym() + fDynamicSymbolTable->nlocalsym() > fSymbolCount ) {
+               throwf("local symbols out of range (%d+%d > %d) in indirect symbol table",
+                       fDynamicSymbolTable->ilocalsym(), fDynamicSymbolTable->nlocalsym(), fSymbolCount);
+       }
+
+       if ( fDynamicSymbolTable->iextdefsym() > fSymbolCount )
+               throwf("start of extern symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable->iextdefsym(), fSymbolCount);
+       if ( fDynamicSymbolTable->iextdefsym() != fDynamicSymbolTable->ilocalsym() + fDynamicSymbolTable->nlocalsym() ) {
+               throwf("start of extern symbols (%d) not contiguous to local symbols (%d+%d) in indirect symbol table",
+                       fDynamicSymbolTable->iextdefsym(), fDynamicSymbolTable->ilocalsym(), fDynamicSymbolTable->nlocalsym() );
+       }
+       if ( fDynamicSymbolTable->iextdefsym() + fDynamicSymbolTable->nextdefsym() > fSymbolCount ) {
+               throwf("extern symbols out of range (%d+%d > %d) in indirect symbol table",
+                       fDynamicSymbolTable->iextdefsym(), fDynamicSymbolTable->nextdefsym(), fSymbolCount);
+       }
+
+       if ( fDynamicSymbolTable->iundefsym() > fSymbolCount )
+               throwf("start of undefined symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable->iundefsym(), fSymbolCount);
+       if ( fDynamicSymbolTable->iundefsym() != fDynamicSymbolTable->iextdefsym() + fDynamicSymbolTable->nextdefsym() ) {
+               throwf("start of undefined symbols (%d) not contiguous to extern symbols (%d+%d) in indirect symbol table",
+                       fDynamicSymbolTable->iundefsym(), fDynamicSymbolTable->iextdefsym(), fDynamicSymbolTable->nextdefsym());
+       }
+       if ( fDynamicSymbolTable->iundefsym() + fDynamicSymbolTable->nundefsym() > fSymbolCount ) {
+               throwf("undefined symbols out of range (%d+%d > %d) in indirect symbol table",
+                       fDynamicSymbolTable->iundefsym(), fDynamicSymbolTable->nundefsym(), fSymbolCount);
+       }
+
+       if ( fDynamicSymbolTable->iundefsym() + fDynamicSymbolTable->nundefsym() != fSymbolCount ) {
+               throwf("end undefined symbols (%d+%d) not at end of all symbols (%d) in indirect symbol table",
+                               fDynamicSymbolTable->iundefsym(), fDynamicSymbolTable->nundefsym(),  fSymbolCount );
+       }
 }
 
 
@@ -985,23 +1099,6 @@ void MachOChecker<A>::checkInitTerms()
 }
 
 
-template <>
-ppc::P::uint_t MachOChecker<ppc>::relocBase()
-{
-       if ( fHeader->flags() & MH_SPLIT_SEGS )
-               return fFirstWritableSegment->vmaddr();
-       else
-               return fFirstSegment->vmaddr();
-}
-
-template <>
-ppc64::P::uint_t MachOChecker<ppc64>::relocBase()
-{
-       if ( fWriteableSegmentWithAddrOver4G ) 
-               return fFirstWritableSegment->vmaddr();
-       else
-               return fFirstSegment->vmaddr();
-}
 
 template <>
 x86::P::uint_t MachOChecker<x86>::relocBase()
@@ -1028,15 +1125,11 @@ arm::P::uint_t MachOChecker<arm>::relocBase()
                return fFirstSegment->vmaddr();
 }
 
-#if SUPPORT_ARCH_arm64
 template <>
 arm64::P::uint_t MachOChecker<arm64>::relocBase()
 {
        return fFirstWritableSegment->vmaddr();
 }
-#endif
-
-
 
 template <typename A>
 bool MachOChecker<A>::addressInWritableSegment(pint_t address)
@@ -1068,37 +1161,6 @@ bool MachOChecker<A>::addressInWritableSegment(pint_t address)
 }
 
 
-template <>
-void MachOChecker<ppc>::checkExternalReloation(const macho_relocation_info<P>* reloc)
-{
-       if ( reloc->r_length() != 2 ) 
-               throw "bad external relocation length";
-       if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) 
-               throw "unknown external relocation type";
-       if ( reloc->r_pcrel() != 0 ) 
-               throw "bad external relocation pc_rel";
-       if ( reloc->r_extern() == 0 )
-               throw "local relocation found with external relocations";
-       if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
-               throw "external relocation address not in writable segment";
-       // FIX: check r_symbol
-}
-
-template <>
-void MachOChecker<ppc64>::checkExternalReloation(const macho_relocation_info<P>* reloc)
-{
-       if ( reloc->r_length() != 3 ) 
-               throw "bad external relocation length";
-       if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) 
-               throw "unknown external relocation type";
-       if ( reloc->r_pcrel() != 0 ) 
-               throw "bad external relocation pc_rel";
-       if ( reloc->r_extern() == 0 )
-               throw "local relocation found with external relocations";
-       if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
-               throw "external relocation address not in writable segment";
-       // FIX: check r_symbol
-}
 
 template <>
 void MachOChecker<x86>::checkExternalReloation(const macho_relocation_info<P>* reloc)
@@ -1139,7 +1201,7 @@ void MachOChecker<arm>::checkExternalReloation(const macho_relocation_info<P>* r
 {
        if ( reloc->r_length() != 2 ) 
                throw "bad external relocation length";
-       if ( reloc->r_type() != ARM_RELOC_VANILLA ) 
+       if ( reloc->r_type() != ARM_RELOC_VANILLA )
                throw "unknown external relocation type";
        if ( reloc->r_pcrel() != 0 ) 
                throw "bad external relocation pc_rel";
@@ -1160,38 +1222,6 @@ void MachOChecker<arm64>::checkExternalReloation(const macho_relocation_info<P>*
 #endif
 
 
-template <>
-void MachOChecker<ppc>::checkLocalReloation(const macho_relocation_info<P>* reloc)
-{
-       if ( reloc->r_address() & R_SCATTERED ) {
-               // scattered
-               const macho_scattered_relocation_info<P>* sreloc = (const macho_scattered_relocation_info<P>*)reloc;
-               // FIX
-       
-       }
-       else {
-               // FIX
-               if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
-                       throwf("local relocation address 0x%08X not in writable segment", reloc->r_address());
-       }
-}
-
-
-template <>
-void MachOChecker<ppc64>::checkLocalReloation(const macho_relocation_info<P>* reloc)
-{
-       if ( reloc->r_length() != 3 ) 
-               throw "bad local relocation length";
-       if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) 
-               throw "unknown local relocation type";
-       if ( reloc->r_pcrel() != 0 ) 
-               throw "bad local relocation pc_rel";
-       if ( reloc->r_extern() != 0 ) 
-               throw "external relocation found with local relocations";
-       if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
-               throw "local relocation address not in writable segment";
-}
-
 template <>
 void MachOChecker<x86>::checkLocalReloation(const macho_relocation_info<P>* reloc)
 {
@@ -1244,7 +1274,6 @@ void MachOChecker<arm64>::checkLocalReloation(const macho_relocation_info<P>* re
 }
 #endif
 
-
 template <typename A>
 void MachOChecker<A>::checkRelocations()
 {
@@ -1585,7 +1614,7 @@ bool MachOChecker<A>::addressIsBindingSite(pint_t targetAddr)
 }
 
 
-static void check(const char* path)
+static void check(const char* path, const char* verifierDstRoot)
 {
        struct stat stat_buf;
        
@@ -1610,37 +1639,33 @@ static void check(const char* path)
                                unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype);
 
                                switch(cputype) {
-                               case CPU_TYPE_POWERPC:
-                                       if ( MachOChecker<ppc>::validFile(p + offset) )
-                                               MachOChecker<ppc>::make(p + offset, size, path);
-                                       else
-                                               throw "in universal file, ppc slice does not contain ppc mach-o";
-                                       break;
                                case CPU_TYPE_I386:
                                        if ( MachOChecker<x86>::validFile(p + offset) )
-                                               MachOChecker<x86>::make(p + offset, size, path);
+                                               MachOChecker<x86>::make(p + offset, size, path, verifierDstRoot);
                                        else
                                                throw "in universal file, i386 slice does not contain i386 mach-o";
                                        break;
-                               case CPU_TYPE_POWERPC64:
-                                       if ( MachOChecker<ppc64>::validFile(p + offset) )
-                                               MachOChecker<ppc64>::make(p + offset, size, path);
-                                       else
-                                               throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
-                                       break;
                                case CPU_TYPE_X86_64:
                                        if ( MachOChecker<x86_64>::validFile(p + offset) )
-                                               MachOChecker<x86_64>::make(p + offset, size, path);
+                                               MachOChecker<x86_64>::make(p + offset, size, path, verifierDstRoot);
                                        else
                                                throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
                                        break;
 #if SUPPORT_ARCH_arm_any
                                case CPU_TYPE_ARM:
                                        if ( MachOChecker<arm>::validFile(p + offset) )
-                                               MachOChecker<arm>::make(p + offset, size, path);
+                                               MachOChecker<arm>::make(p + offset, size, path, verifierDstRoot);
                                        else
                                                throw "in universal file, arm slice does not contain arm mach-o";
                                        break;
+#endif
+#if SUPPORT_ARCH_arm64
+                               case CPU_TYPE_ARM64:
+                                       if ( MachOChecker<arm64>::validFile(p + offset) )
+                                               MachOChecker<arm64>::make(p + offset, size, path, verifierDstRoot);
+                                       else
+                                               throw "in universal file, arm64 slice does not contain arm mach-o";
+                                       break;
 #endif
                                default:
                                                throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
@@ -1648,25 +1673,19 @@ static void check(const char* path)
                        }
                }
                else if ( MachOChecker<x86>::validFile(p) ) {
-                       MachOChecker<x86>::make(p, length, path);
-               }
-               else if ( MachOChecker<ppc>::validFile(p) ) {
-                       MachOChecker<ppc>::make(p, length, path);
-               }
-               else if ( MachOChecker<ppc64>::validFile(p) ) {
-                       MachOChecker<ppc64>::make(p, length, path);
+                       MachOChecker<x86>::make(p, length, path, verifierDstRoot);
                }
                else if ( MachOChecker<x86_64>::validFile(p) ) {
-                       MachOChecker<x86_64>::make(p, length, path);
+                       MachOChecker<x86_64>::make(p, length, path, verifierDstRoot);
                }
 #if SUPPORT_ARCH_arm_any
                else if ( MachOChecker<arm>::validFile(p) ) {
-                       MachOChecker<arm>::make(p, length, path);
+                       MachOChecker<arm>::make(p, length, path, verifierDstRoot);
                }
 #endif
 #if SUPPORT_ARCH_arm64
                else if ( MachOChecker<arm64>::validFile(p) ) {
-                       MachOChecker<arm64>::make(p, length, path);
+                       MachOChecker<arm64>::make(p, length, path, verifierDstRoot);
                }
 #endif
                else {
@@ -1682,6 +1701,7 @@ static void check(const char* path)
 int main(int argc, const char* argv[])
 {
        bool progress = false;
+       const char* verifierDstRoot = NULL;
        int result = 0;
        for(int i=1; i < argc; ++i) {
                const char* arg = argv[i];
@@ -1689,6 +1709,18 @@ int main(int argc, const char* argv[])
                        if ( strcmp(arg, "-progress") == 0 ) {
                                progress = true;
                        }
+                       else if ( strcmp(arg, "-verifier_dstroot") == 0 ) {
+                               verifierDstRoot = argv[++i];
+                       }
+                       else if ( strcmp(arg, "-verifier_error_list") == 0 ) {
+                               printf("os_dylib_rpath_install_name\tOS dylibs (those in /usr/lib/ or /System/Library/) must be built with -install_name that is an absolute path - not an @rpath\n");
+                               printf("os_dylib_bad_install_name\tOS dylibs (those in /usr/lib/ or /System/Library/) must be built with -install_name matching their file system location\n");
+                               printf("os_dylib_rpath\tOS dylibs should not contain LC_RPATH load commands (from -rpath linker option)\n");
+                               printf("os_dylib_flat_namespace\tOS dylibs should not be built with -flat_namespace\n");
+                               printf("os_dylib_undefined_dynamic_lookup\tOS dylibs should not be built with -undefined dynamic_lookup\n");
+                               printf("os_dylib_malformed\the mach-o is malformed\n");
+                               return 0;
+                       }
                        else {
                                throwf("unknown option: %s\n", arg);
                        }
@@ -1696,12 +1728,17 @@ int main(int argc, const char* argv[])
                else {
                        bool success = true;
                        try {
-                               check(arg);
+                               check(arg, verifierDstRoot);
                        }
                        catch (const char* msg) {
-                               fprintf(stderr, "machocheck failed: %s %s\n", arg, msg);
-                               result = 1;
-                               success = false;
+                               if ( verifierDstRoot ) {
+                                       printf("os_dylib_malformed\twarn\t%s\n", msg);
+                               }
+                               else {
+                                       fprintf(stderr, "machocheck failed: %s\n", msg);
+                                       result = 1;
+                                       success = false;
+                               }
                        }
                        if ( success && progress )
                                printf("ok: %s\n", arg);
@@ -1711,5 +1748,3 @@ int main(int argc, const char* argv[])
        return result;
 }
 
-
-
index f91ece313bce5ec0040bb677ca9a9d9e22063504..bc925fbbdb6c0a0a16e7e477b1ebd2b0dc25f514 100644 (file)
@@ -240,7 +240,7 @@ const char* UnwindPrinter<A>::functionName(pint_t addr, uint32_t* offset)
        for (uint32_t i=0; i < fSymbolCount; ++i) {
                uint8_t type = fSymbols[i].n_type();
                if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) {
-                       uint32_t value = fSymbols[i].n_value();
+                       pint_t value = fSymbols[i].n_value();
                        if ( value == addr ) {
                                const char* r = &fStrings[fSymbols[i].n_strx()];
                                return r;
index 92851281512dcba73c398be50713cb6b274341e0..cdab9275ed9a43c6df18f39499b591c2458df413 100755 (executable)
@@ -5,8 +5,6 @@ unset RC_TRACE_ARCHIVES
 unset LD_TRACE_DYLIBS
 unset LD_TRACE_ARCHIVES
 
-export DYLD_FALLBACK_LIBRARY_PATH=${DYLD_FALLBACK_LIBRARY_PATH}:/Developer/usr/lib
-export MACOSX_DEPLOYMENT_TARGET=10.7
 # cd into test-cases directory
 cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'`
 
diff --git a/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdrField.s b/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdrField.s
new file mode 100644 (file)
index 0000000..477cdab
--- /dev/null
@@ -0,0 +1,53 @@
+
+#ifndef TARGET
+ #define TARGET _foo
+#endif
+
+      .text
+      .align 2
+_test:
+      nop
+L1:   adrp     x0, TARGET@GOTPAGE
+L2:   ldr   x1, [x0, #TARGET@GOTPAGEOFF]
+#if LOAD_GPR_8
+L3:   ldr   b2, [x1, #8]
+#elif LOAD_GPR_16
+L3:   ldr   h2, [x1, #8]
+#elif LOAD_GPR_32
+L3:   ldr   w2, [x1, #8]
+#elif LOAD_GPR_64
+L3:   ldr   x2, [x1, #8]
+#elif LOAD_FPR_32
+L3:   ldr   s2, [x1, #8]
+#elif LOAD_FPR_64
+L3:   ldr   d2, [x1, #8]
+#elif LOAD_VEC_128
+L3:   ldr   q2, [x1, #16]
+#endif
+      nop
+  
+    .loh AdrpLdrGotLdr L1, L2, L3
+    
+#if PADDING
+_pad:
+      .space 1100000
+#endif 
+
+#if FOO_AS_CONST
+    .const
+    .align 4
+#endif
+
+#if FOO_AS_DATA
+    .data
+_makePageOffsetNonZero: .long 0,0,0,0
+#endif
+
+#if MISALIGN_DATA
+_junk: .byte 0
+#endif
+
+_foo:     .long 0,0,0,0
+
+
+
index de5da0dbe97d656652abf6b76fb8d4edbe9f2a32..86622885a596da0ded55615a26933f7daf6f93fe 100644 (file)
@@ -34,7 +34,7 @@ all-x86_64: skip
 all-armv6: skip 
 all-armv7: skip
 
-all-arm64: AdrpAdd AdrpAddLdr AdrpLdr AdrpLdrGotLdr AdrpAddStr AdrpLdrGotStr AdrpLdrGot
+all-arm64: AdrpAdd AdrpAddLdr AdrpLdr AdrpLdrGotLdr AdrpLdrGotLdrField AdrpAddStr AdrpLdrGotStr AdrpLdrGot
 
 main.o:
        ${CC} ${CCFLAGS} main.s -c -o main.o
@@ -81,14 +81,14 @@ AdrpAddLdr-ldr: main.o
        ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 
        ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g8.o main.o -o AdrpAddLdr-ldr-g8.exe 
        ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'adr  x0' | ${FAIL_IF_EMPTY}
-       ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'ldr\s*b1, \[x0\]' | ${FAIL_IF_EMPTY}
        ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'add  x0' | ${FAIL_IF_STDIN}
        
        # test ADRP/ADD/LD -> ADR/LDR when target is in __TEXT for 16-bit load
        ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g16.o -DFOO_AS_CONST -DLOAD_GPR_16
        ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g16.o main.o -o AdrpAddLdr-ldr-g16.exe
        ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY}
-       ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'ldr\th1, \[x0\]' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'ldr\s*h1, \[x0\]' | ${FAIL_IF_EMPTY}
        ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN}
        
        # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit load
@@ -101,7 +101,7 @@ AdrpAddLdr-ldr: main.o
        # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit load
        ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g64.o -DFOO_AS_CONST -DLOAD_GPR_64
        ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g64.o main.o -o AdrpAddLdr-ldr-g64.exe
-       ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'ldr x1, _foo' | ${FAIL_IF_EMPTY}
+#      ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'ldr x1, _foo' | ${FAIL_IF_EMPTY}
        ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'adrp        x0' | ${FAIL_IF_STDIN}
        ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN}
 
@@ -228,51 +228,51 @@ AdrpAddLdr-seg:
        # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 8-bit load
        ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 
        ${CC} ${CCFLAGS} AdrpAddLdr-seg-g8.o -dynamiclib -o AdrpAddLdr-seg-g8.dylib -install_name /usr/lib/libjunk.dylib
-       ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'adrp       x0' | ${FAIL_IF_EMPTY}
-       ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'add        x0' | ${FAIL_IF_STDIN}
-       ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'ldr        b1, \[x0,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'ldr\s*b1, \[x0,' | ${FAIL_IF_EMPTY}
        
        # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 16-bit load
        ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 
        ${CC} ${CCFLAGS} AdrpAddLdr-seg-g16.o -dynamiclib -o AdrpAddLdr-seg-g16.dylib -install_name /usr/lib/libjunk.dylib
-       ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'adrp      x0' | ${FAIL_IF_EMPTY}
-       ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'add       x0' | ${FAIL_IF_STDIN}
-       ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'ldr       h1, \[x0,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'ldr\s*h1, \[x0,' | ${FAIL_IF_EMPTY}
        
        # test ADRP/ADD/LD -> ADRP/LD  when target is in movable segment for 32-bit load
        ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 
        ${CC} ${CCFLAGS} AdrpAddLdr-seg-g32.o -dynamiclib -o AdrpAddLdr-seg-g32.dylib -install_name /usr/lib/libjunk.dylib
-       ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'adrp      x0' | ${FAIL_IF_EMPTY}
-       ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'add       x0' | ${FAIL_IF_STDIN}
-       ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'ldr       w1, \[x0,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'ldr\s*w1, \[x0,' | ${FAIL_IF_EMPTY}
 
        # test ADRP/ADD/LD -> ADRP/LD  when target is in movable segment for 64-bit load
        ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 
        ${CC} ${CCFLAGS} AdrpAddLdr-seg-g64.o -dynamiclib -o AdrpAddLdr-seg-g64.dylib -install_name /usr/lib/libjunk.dylib
-       ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'adrp      x0' | ${FAIL_IF_EMPTY}
-       ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'add       x0' | ${FAIL_IF_STDIN}
-       ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'ldr       x1, \[x0,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'ldr\s*x1, \[x0,' | ${FAIL_IF_EMPTY}
 
        # test ADRP/ADD/LD -> ADRP/LD  when target is in movable segment for 32-bit fp load
        ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 
        ${CC} ${CCFLAGS} AdrpAddLdr-seg-f32.o -dynamiclib -o AdrpAddLdr-seg-f32.dylib -install_name /usr/lib/libjunk.dylib
-       ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'adrp      x0' | ${FAIL_IF_EMPTY}
-       ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'add       x0' | ${FAIL_IF_STDIN}
-       ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'ldr       s1, \[x0,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'ldr\s*s1, \[x0,' | ${FAIL_IF_EMPTY}
 
        # test ADRP/ADD/LD -> ADRP/LD  when target is in movable segment for 64-bit fp load
        ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 
        ${CC} ${CCFLAGS} AdrpAddLdr-seg-f64.o -dynamiclib -o AdrpAddLdr-seg-f64.dylib -install_name /usr/lib/libjunk.dylib
-       ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'adrp      x0' | ${FAIL_IF_EMPTY}
-       ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'add       x0' | ${FAIL_IF_STDIN}
-       ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'ldr       d1, \[x0,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'ldr\s*d1, \[x0,' | ${FAIL_IF_EMPTY}
        
        # test ADRP/ADD/LD -> ADRP/LD  when target is in movable segmentfor 128-bit vec load
        ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 
        ${CC} ${CCFLAGS} AdrpAddLdr-seg-v128.o -dynamiclib -o AdrpAddLdr-seg-v128.dylib -install_name /usr/lib/libjunk.dylib
-       ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'adrp     x0' | ${FAIL_IF_EMPTY}
-       ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'add      x0' | ${FAIL_IF_STDIN}
-       ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'ldr      q1, \[x0,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'ldr\s*q1, \[x0,' | ${FAIL_IF_EMPTY}
 
 
 
@@ -812,6 +812,322 @@ AdrpLdrGotLdr-farunaligned: main.o
        ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY}
 
 
+AdrpLdrGotLdrField: AdrpLdrGotLdrField-extern AdrpLdrGotLdrField-externfargot AdrpLdrGotLdrField-near AdrpLdrGotLdrField-far AdrpLdrGotLdrField-nearunaligned AdrpLdrGotLdrField-farunaligned
+       true
+
+AdrpLdrGotLdrField-extern: main.o
+       # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close  for 8-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-g8.o main.o -o AdrpLdrGotLdrField-extern-g8.exe
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g8.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g8.exe | grep 'ldr\s*b2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close  for 16-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-g16.o main.o -o AdrpLdrGotLdrField-extern-g16.exe
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g16.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g16.exe | grep 'ldr\s*h2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 32-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-g32.o main.o -o AdrpLdrGotLdrField-extern-g32.exe
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g32.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g32.exe | grep 'ldr\s*w2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 64-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-g64.o main.o -o AdrpLdrGotLdrField-extern-g64.exe
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g64.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-g64.exe | grep 'ldr\s*x2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 32-bit fp load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-f32.o main.o -o AdrpLdrGotLdrField-extern-f32.exe
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-f32.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-f32.exe | grep 'ldr\s*s2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 64-bit fp load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-f64.o main.o -o AdrpLdrGotLdrField-extern-f64.exe
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-f64.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-f64.exe | grep 'ldr\s*d2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 128-bit vec load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-v128.o main.o -o AdrpLdrGotLdrField-extern-v128.exe
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-v128.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-extern-v128.exe | grep 'ldr\s*q2, \[x1, #16\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotLdrField-externfargot: main.o
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 8-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-g8.o main.o -o AdrpLdrGotLdrField-externfargot-g8.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g8.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g8.exe | grep 'ldr\s*b2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-g16.o main.o -o AdrpLdrGotLdrField-externfargot-g16.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g16.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g16.exe | grep 'ldr\s*h2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot farfor 32-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-g32.o main.o -o AdrpLdrGotLdrField-externfargot-g32.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g32.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g32.exe | grep 'ldr\s*w2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-g64.o main.o -o AdrpLdrGotLdrField-externfargot-g64.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g64.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g64.exe | grep 'ldr\s*x2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-f32.o main.o -o AdrpLdrGotLdrField-externfargot-f32.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f32.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f32.exe | grep 'ldr\s*s2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-f64.o main.o -o AdrpLdrGotLdrField-externfargot-f64.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f64.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f64.exe | grep 'ldr\s*d2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-v128.o main.o -o AdrpLdrGotLdrField-externfargot-v128.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-v128.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-v128.exe | grep 'ldr\s*q2, \[x1, #16\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotLdrField-near: main.o 
+       # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-g8.o main.o -o AdrpLdrGotLdrField-near-g8.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g8.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g8.exe | grep 'ldr\s*b2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-g16.o main.o -o AdrpLdrGotLdrField-near-g16.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g16.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g16.exe | grep 'ldr\s*h2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-g32.o main.o -o AdrpLdrGotLdrField-near-g32.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g32.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g32.exe | grep 'ldr\s*w2, _foo' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g32.exe | grep 'ldr\s*w2,' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-g64.o main.o -o AdrpLdrGotLdrField-near-g64.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g64.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g64.exe | grep 'ldr\s*x2, _foo' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-g64.exe | grep 'ldr\s*x2,' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-f32.o main.o -o AdrpLdrGotLdrField-near-f32.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-f32.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-f32.exe | grep 'ldr\s*s2, _foo' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-f32.exe | grep 'ldr\s*s2,' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-f64.o main.o -o AdrpLdrGotLdrField-near-f64.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-f64.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-f64.exe | grep 'ldr\s*d2, _foo' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-f64.exe | grep 'ldr\s*d2,' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-v128.o main.o -o AdrpLdrGotLdrField-near-v128.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-v128.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-v128.exe | grep 'ldr\s*q2, _foo' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-near-v128.exe | grep 'ldr\s*q2,' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotLdrField-nearunaligned: main.o 
+       # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 8-bit unaligned load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DMISALIGN_DATA
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-g8.o main.o -o AdrpLdrGotLdrField-nearunaligned-g8.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g8.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g8.exe | grep 'ldr\s*b2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 16-bit unaligned load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DMISALIGN_DATA
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-g16.o main.o -o AdrpLdrGotLdrField-nearunaligned-g16.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g16.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g16.exe | grep 'ldr\s*h2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 32-bit unaligned load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DMISALIGN_DATA
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-g32.o main.o -o AdrpLdrGotLdrField-nearunaligned-g32.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g32.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g32.exe | grep 'ldr\s*w2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 64-bit unaligned load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DMISALIGN_DATA
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-g64.o main.o -o AdrpLdrGotLdrField-nearunaligned-g64.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g64.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g64.exe | grep 'ldr\s*x2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 32-bit FP unaligned load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DMISALIGN_DATA
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-f32.o main.o -o AdrpLdrGotLdrField-nearunaligned-f32.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f32.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f32.exe | grep 'ldr\s*s2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 64-bit FP unaligned load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DMISALIGN_DATA
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-f64.o main.o -o AdrpLdrGotLdrField-nearunaligned-f64.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f64.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f64.exe | grep 'ldr\s*d2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 12-bit vec unaligned load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DMISALIGN_DATA
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-v128.o main.o -o AdrpLdrGotLdrField-nearunaligned-v128.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-v128.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-v128.exe | grep 'ldr\s*q2, \[x1, #16\]' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotLdrField-far: main.o 
+       # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-g8.o main.o -o AdrpLdrGotLdrField-far-g8.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g8.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g8.exe | grep 'ldr\s*b2, \[x0,' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-g16.o main.o -o AdrpLdrGotLdrField-far-g16.exe  
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g16.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g16.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g16.exe | grep 'ldr\s*h2, \[x0,' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-g32.o main.o -o AdrpLdrGotLdrField-far-g32.exe  
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g32.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g32.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g32.exe | grep 'ldr\s*w2, \[x0,' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-g64.o main.o -o AdrpLdrGotLdrField-far-g64.exe  
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g64.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g64.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-g64.exe | grep 'ldr\s*x2, \[x0,' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-f32.o main.o -o AdrpLdrGotLdrField-far-f32.exe  
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-f32.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-f32.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-f32.exe | grep 'ldr\s*s2, \[x0,' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-f64.o main.o -o AdrpLdrGotLdrField-far-f64.exe  
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-f64.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-f64.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-f64.exe | grep 'ldr\s*d2, \[x0,' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 128-bit vec load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING 
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-v128.o main.o -o AdrpLdrGotLdrField-far-v128.exe  
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-v128.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-v128.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-far-v128.exe | grep 'ldr\s*q2, \[x0,' | ${FAIL_IF_EMPTY}
+
+
+AdrpLdrGotLdrField-farunaligned: main.o 
+       # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DMISALIGN_DATA -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-g8.o main.o -o AdrpLdrGotLdrField-farunaligned-g8.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g8.exe | grep 'ldr\s*b2, \[x0,' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DMISALIGN_DATA -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-g16.o main.o -o AdrpLdrGotLdrField-farunaligned-g16.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g16.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g16.exe | grep 'ldr\s*h2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DMISALIGN_DATA -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-g32.o main.o -o AdrpLdrGotLdrField-farunaligned-g32.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g32.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g32.exe | grep 'ldr\s*w2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DMISALIGN_DATA -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-g64.o main.o -o AdrpLdrGotLdrField-farunaligned-g64.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g64.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g64.exe | grep 'ldr\s*x2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DMISALIGN_DATA -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-f32.o main.o -o AdrpLdrGotLdrField-farunaligned-f32.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f32.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f32.exe | grep 'ldr\s*s2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+
+       # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DMISALIGN_DATA -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-f64.o main.o -o AdrpLdrGotLdrField-farunaligned-f64.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f64.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f64.exe | grep 'ldr\s*d2, \[x1, #8\]' | ${FAIL_IF_EMPTY}
+       
+       # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DMISALIGN_DATA -DPADDING
+       ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-v128.o main.o -o AdrpLdrGotLdrField-farunaligned-v128.exe 
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-v128.exe | grep 'add\t' | ${FAIL_IF_EMPTY}
+       ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-v128.exe | grep 'ldr\s*q2, \[x1, #16\]' | ${FAIL_IF_EMPTY}
+
+
+
+
 AdrpAddStr: AdrpAddStr-base AdrpAddStr-seg AdrpAddStr-align AdrpAddStr-addend AdrpAddStr-faraddend
        true
 
diff --git a/unit-tests/test-cases/linker_options-library-chain/Makefile b/unit-tests/test-cases/linker_options-library-chain/Makefile
new file mode 100644 (file)
index 0000000..240f553
--- /dev/null
@@ -0,0 +1,47 @@
+##
+# Copyright (c) 2013 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@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# main2.o has hint to link with libfoo
+# libfoo.a(libfoo2.o) has link to link with libbar.dylib
+#
+
+run: all
+
+all:
+       ${CC} ${CCFLAGS} subbar.c -dynamiclib -o libsubbar.dylib
+       ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -Wl,-reexport_library,libsubbar.dylib
+       ${CC} ${CCFLAGS} foo.c -c -o foo.o
+       ${LD} -r foo.o -add_linker_option -lbar -o foo2.o
+       libtool -static foo2.o -o libfoo.a
+       ${CC} ${CCFLAGS} main.c -c -o main.o
+       ${LD} -r main.o -add_linker_option -lfoo -o main2.o
+       ${CC} ${CCFLAGS} main2.o -o main -L.
+       ${DYLDINFO} -lazy_bind main | grep _bar | grep libbar | ${FAIL_IF_EMPTY}
+       ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+       rm -f libsubbar.dylib libbar.dylib foo.o foo2.o libfoo.a main.o main2.o main
+       
diff --git a/unit-tests/test-cases/linker_options-library-chain/bar.c b/unit-tests/test-cases/linker_options-library-chain/bar.c
new file mode 100644 (file)
index 0000000..981110f
--- /dev/null
@@ -0,0 +1 @@
+void bar() { }
diff --git a/unit-tests/test-cases/linker_options-library-chain/foo.c b/unit-tests/test-cases/linker_options-library-chain/foo.c
new file mode 100644 (file)
index 0000000..ac077b9
--- /dev/null
@@ -0,0 +1,8 @@
+
+extern void bar();
+extern void subbar();
+
+void foo() {
+    bar();
+    subbar();
+}
diff --git a/unit-tests/test-cases/linker_options-library-chain/main.c b/unit-tests/test-cases/linker_options-library-chain/main.c
new file mode 100644 (file)
index 0000000..4f56fe0
--- /dev/null
@@ -0,0 +1,8 @@
+
+extern void foo();
+
+int main()
+{
+       foo();
+       return 0;
+}
diff --git a/unit-tests/test-cases/linker_options-library-chain/subbar.c b/unit-tests/test-cases/linker_options-library-chain/subbar.c
new file mode 100644 (file)
index 0000000..3619a80
--- /dev/null
@@ -0,0 +1 @@
+void subbar() { }
index 7dcafac4b90fa208881767e642f0d2cbc06cb4eb..be39fa715b90df489d7af1b40dbaed54625887d3 100644 (file)
@@ -43,7 +43,7 @@ all-armv6: all-rest
 all-armv7: all-rest
 
 all-rest:
-       # check optimzation of category methods
+       # check optimization of category methods
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m  -framework Foundation -o libfoo.dylib
        size -l libfoo.dylib | grep "__objc_catlist:" | ${FAIL_IF_EMPTY}
        ${PASS_IFF_GOOD_MACHO} libfoo.dylib
index de4ea0cc1293e72d452ed7451193133367e835a3..37c69f137070c671dc7d6f3ecb85c1850c58d779 100644 (file)
@@ -51,19 +51,19 @@ all-rest:
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -Wl,-no_objc_category_merging -o libno.dylib
        size -l libno.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_STDIN}
        otool -ov libno.dylib | grep -A17 __objc_classlist | grep -A16 '_OBJC_CLASS_$$_Foo' | grep "count 1" | ${FAIL_IF_EMPTY}
-       # check optimzation of category methods
+       # check optimization of category methods
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -o libfoo.dylib
        size -l libfoo.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
        otool -ov libfoo.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 4" | ${FAIL_IF_EMPTY}
-       # check optimzation of protocol and category methods
+       # check optimization of protocol and category methods
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROTOCOLS -framework Foundation -o libfoo2.dylib
        size -l libfoo2.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
        otool -ov libfoo2.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY}
-       # check optimzation of properties and category methods
+       # check optimization of properties and category methods
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROPERTIES -framework Foundation -o libfoo3.dylib
        size -l libfoo3.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
        otool -ov libfoo3.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY}
-       # check optimzation of category methods and no base methods
+       # check optimization of category methods and no base methods
        ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DNO_BASE_METHODS -framework Foundation -o libfoo4.dylib
        size -l libfoo4.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY}
        otool -ov libfoo4.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 3" | ${FAIL_IF_EMPTY}