.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
.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.
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
isa = PBXAggregateTarget;
buildConfigurationList = F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */;
buildPhases = (
- F9871A3413340B4600DB3F24 /* Platform install */,
);
dependencies = (
F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */,
F9C12EEA0ED65765005BC69D /* PBXTargetDependency */,
F9B8135D0EC2620E00F94C13 /* PBXTargetDependency */,
F9A3DE160ED76D9A00C590B9 /* PBXTargetDependency */,
+ F9FF3BDD1C586D7C0015D843 /* PBXTargetDependency */,
);
name = all;
productName = all;
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";
}
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) {
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;
}
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 ) {
OSAtomicIncrement32(&_totalObjectLoaded);
return objResult;
}
-
+
// see if it is a dynamic library (or text-based dynamic library)
ld::dylib::File* dylibResult;
bool dylibsNotAllowed = false;
}
}
-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;
}
// 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);
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();
warning("Auto-Linking supplied '%s', %s", info.path, msg);
}
}
+ state.linkerOptionLibrariesProcessed.insert(libName);
}
}
//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
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);
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;
};
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;
{
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";
}
}
-template <typename A>
-bool IndirectSymbolTableAtom<A>::kextBundlesDontHaveIndirectSymbolTable()
-{
- return true;
-}
-
template <typename A>
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
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),
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();
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();
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;
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 )
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) ) {
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
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 {
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;
}
}
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;
}
return versionString.str();
}
+// Convert X.Y[.Z] to 32-bit value xxxxyyzz
+bool Options::parsePackedVersion32(const std::string& versionStr, uint32_t &result)
+{
+ result = 0;
+
+ if ( versionStr.empty() )
+ return false;
+
+ size_t pos = versionStr.find('.');
+ if ( pos == std::string::npos )
+ return false;
+
+ std::string majorStr = versionStr.substr(0, pos);
+ std::string rest = versionStr.substr(pos+1);
+
+ try {
+ size_t majorEnd;
+ int majorValue = std::stoi(majorStr, &majorEnd);
+ if ( majorEnd != majorStr.size() )
+ return false;
+ if ( majorValue < 0 )
+ return false;
+ if ( majorValue > 65535 )
+ return false;
+
+ std::string minorStr;
+ std::string microStr;
+ pos = rest.find('.');
+ if ( pos == std::string::npos ) {
+ minorStr = rest;
+ }
+ else {
+ minorStr = rest.substr(0, pos);
+ microStr = rest.substr(pos+1);
+ }
+
+ size_t minorEnd;
+ int minorValue = std::stoi(minorStr, &minorEnd);
+ if ( minorEnd != minorStr.size() )
+ return false;
+ if ( minorValue < 0 )
+ return false;
+ if ( minorValue > 255 )
+ return false;
+
+ int microValue = 0;
+ if ( !microStr.empty() ) {
+ size_t microEnd;
+ microValue = std::stoi(microStr, µEnd);
+ if ( microEnd != microStr.size() )
+ return false;
+ if ( microValue < 0 )
+ return false;
+ if ( microValue > 255 )
+ return false;
+ }
+
+ result = (majorValue << 16) | (minorValue << 8) | microValue;
+
+ return true;
+ }
+ catch (...) {
+ // std::stoi() throws exception on malformed input
+ return false;
+ }
+}
+
std::string Options::getSDKVersionStr() const
{
return getVersionString32(fSDKVersion);
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 ) {
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;
// 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) ) {
}
}
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;
}
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, ':');
}
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];
}
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;
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;
+ }
}
}
}
// 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;
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;
+ }
}
}
}
// 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);
+ }
}
}
}
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");
if (pipeFdString != NULL) {
fPipelineFifo = pipeFdString;
}
+
+ // Workaround for rdar://problem/24301175
+ if ((getenv("RC_XBS") != NULL) && !(getenv("RC_BUILDIT") != NULL))
+ fForceTextBasedStub = true;
}
// 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;
}
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 ) {
}
}
+ // <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()
// 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
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);
}
// 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 ) {
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:
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 )
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");
// 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) {};
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);
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; }
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; }
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;
enum ExportMode { kExportDefault, kExportSome, kDontExportSome };
enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives };
enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome };
+ enum FilePreference { kModTime, kTextBasedStub, kMachO };
class SetWithWildcards {
public:
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);
bool fMakeCompressedDyldInfoForceOff;
bool fNoEHLabels;
bool fAllowCpuSubtypeMismatches;
+ bool fEnforceDylibSubtypesMatch;
bool fUseSimplifiedDylibReExports;
bool fObjCABIVersion2Override;
bool fObjCABIVersion1Override;
bool fHideSymbols;
bool fVerifyBitcode;
bool fReverseMapUUIDRename;
+ bool fDeDupe;
+ bool fVerboseDeDupe;
const char* fReverseMapPath;
std::string fReverseMapTempPath;
bool fLTOCodegenOnly;
const char* fPipelineFifo;
const char* fDependencyInfoPath;
mutable int fDependencyFileDescriptor;
+ uint8_t fMaxDefaultCommonAlign;
+ FilePreference fFilePreference;
+ bool fForceTextBasedStub;
};
#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; \
}
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 ) {
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));
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);
}
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 {
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 ) {
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));
// 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);
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:
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:
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:
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);
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;
uint8_t targetSectionIndex;
uint8_t referenceKind;
};
+ static void dumpAtomsBySection(ld::Internal& state, bool);
private:
void writeAtoms(ld::Internal& state, uint8_t* wholeBuffer);
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);
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() ) {
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
}
}
+ // 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);
+
}
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();
}
+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);
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; }
#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"
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;
// 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) )
// update running totals
if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace )
address += sect->size;
+ prevSect = sect;
}
if ( overlappingFixedSection != NULL ) {
fprintf(stderr, "Section layout:\n");
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
};
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; }
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;
// data-in-code markers
kindDataInCodeStartData, kindDataInCodeStartJT8, kindDataInCodeStartJT16,
kindDataInCodeStartJT32, kindDataInCodeStartJTA32, kindDataInCodeEnd,
- // linker optimzation hints
+ // linker optimization hints
kindLinkerOptimizationHint,
// pointer store combinations
kindStoreTargetAddressLittleEndian32, // kindSetTargetAddress + kindStoreLittleEndian32
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;
--- /dev/null
+/* -*- 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__
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;
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,
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;
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);
bool simulator;
bool ignoreMismatchPlatform;
bool bitcodeBundle;
+ uint8_t maxDefaultCommonAlignment;
cpu_type_t arch;
const char* mcpu;
Options::Platform platform;
#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,
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"; }
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();
// 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) ) {
// 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;
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:
#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;
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);
}
}
}
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
// 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:
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:
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
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
// 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:
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;
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;
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);
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];
}
// 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);
}
}
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>
// 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 )
}
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 )
}
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:
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 )
{
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;
}
}
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;
}
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;
}
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
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
-
-
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
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);
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; }
bool _ignoreMismatchPlatform;
bool _treateBitcodeAsData;
bool _usingBitcode;
+ uint8_t _maxDefaultCommonAlignment;
unsigned int _stubsSectionNum;
const macho_section<P>* _stubsMachOSection;
std::vector<const char*> _dtraceProviderInfo;
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);
}
}
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) )
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());
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:
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;
}
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
++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,
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;
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;
}
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());
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() ) {
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());
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);
}
// 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() ) {
}
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);
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) {
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);
ld::relocatable::File::SourceKind srcKind;
bool treateBitcodeAsData;
bool usingBitcode;
+ uint8_t maxDefaultCommonAlignment;
};
extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength,
#include "bitcode.hpp"
#include "MachOFileAbstraction.hpp"
#include "MachOTrie.hpp"
+#include "generic_dylib_file.hpp"
#include "textstub_dylib_file.hpp"
namespace {
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; }
_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.
///
}
}
- 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) {
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;
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;
return;
while ( true ) {
- if ( !parseArchFlowSequence(archName) ) {
+ if ( !parseArchFlowSequence(selectedArchName) ) {
Token token;
while ( true ) {
token = peek();
}
}
- 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);
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() {
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,
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,
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);
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 {
}
}
- _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
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(),
};
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;
} // namespace dylib
} // namespace textstub
-
-
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");
_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);
#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
// 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();
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
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);
}
// 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 § : _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());
}
}
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
//
-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;
(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);
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);
}
}
//
-// 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)
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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__
// 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;
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
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)
}
}
}
- if ( canDo )
- encodingIndexes.push_back(encodingIndex);
// compute function offset
uint32_t funcOffsetWithInPage = lastEntryAddress - info.funcTentAddress;
if ( funcOffsetWithInPage > 0x00FFFF00 ) {
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
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 {
-
-
-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;
}
}
}
}
+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
// 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 ) {
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;
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());
#include <sys/mman.h>
#include <stdarg.h>
#include <stdio.h>
+#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
{
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() {}
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);
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);
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;
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;
};
-
-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)
{
return false;
}
+#if SUPPORT_ARCH_arm_any
template <>
bool MachOChecker<arm>::validFile(const uint8_t* fileContent)
{
}
return false;
}
+#endif
#if SUPPORT_ARCH_arm64
template <>
}
#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)
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)
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
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
checkSymbolTable();
checkInitTerms();
+
+ if ( verifierDstRoot != NULL )
+ verify();
}
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) )
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 )
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:
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;
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();
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";
}
}
+ // 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;
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();
}
+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()
}
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 );
+ }
}
}
-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()
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)
}
-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)
{
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";
#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)
{
}
#endif
-
template <typename A>
void MachOChecker<A>::checkRelocations()
{
}
-static void check(const char* path)
+static void check(const char* path, const char* verifierDstRoot)
{
struct stat stat_buf;
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);
}
}
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 {
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];
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);
}
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);
return result;
}
-
-
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;
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/'`
--- /dev/null
+
+#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
+
+
+
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
${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
# 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}
# 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}
${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
--- /dev/null
+##
+# 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
+
--- /dev/null
+void bar() { }
--- /dev/null
+
+extern void bar();
+extern void subbar();
+
+void foo() {
+ bar();
+ subbar();
+}
--- /dev/null
+
+extern void foo();
+
+int main()
+{
+ foo();
+ return 0;
+}
--- /dev/null
+void subbar() { }
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
${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}