build
DerivedData
+ld64.xcodeproj/project.xcworkspace
+ld64.xcodeproj/xcuserdata
Error if the output contains any static initializers
.It Fl no_warn_inits
Do not warn if the output contains any static initializers
+.It Fl debug_variant
+Do not warn about issues that are only problems for binaries shipping to customers.
.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
.It Fl no_function_starts
By default the linker creates a compress table of function start addresses in the LINKEDIT of
final linked image. This option disables that behavior.
-.It Fl no_version_load_command
-By default the linker creates a load command in final linked images that contains the -macosx_version_min.
-This option disables that behavior.
.It Fl no_objc_category_merging
By default when producing final linked image, the linker will optimize Objective-C classes by merging
any categories on a class into the class. Both the class and its categories must be defined in the image
.It Fl cache_path_lto Ar path
When performing Incremental Link Time Optimization (LTO), use this directory as a cache for incremental rebuild.
.It Fl prune_interval_lto Ar seconds
-When performing Incremental Link Time Optimization (LTO), the cache will pruned after the specified interval.
+When performing Incremental Link Time Optimization (LTO), the cache will pruned after the specified interval. A value 0
+will force pruning to occur and a value of -1 will disable pruning.
.It Fl prune_after_lto Ar seconds
When pruning the cache for Incremental Link Time Optimization (LTO), the cache entries are removed after the
specificied interval.
83046A851C8FF2F700024A7E /* objcimageinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83046A841C8FF2D000024A7E /* objcimageinfo.cpp */; };
B028FCF21A9E7C3F00E3584B /* bitcode_bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */; };
B3B672421406D42800A376BB /* Snapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3B672411406D42800A376BB /* Snapshot.cpp */; };
+ C1E27B581F6B1B68003B8FA6 /* thread_starts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1E27B571F6B1B67003B8FA6 /* thread_starts.cpp */; };
F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; };
F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; };
F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F93CB246116E69EB003233B8 /* tlvp.cpp */; };
F97F5025070D0B6300B9FCD7 /* copy man page */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
- dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1";
+ dstPath = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/share/man/man1";
dstSubfolderSpec = 0;
files = (
F97F5029070D0BB200B9FCD7 /* ld.1 in copy man page */,
F9A3DE140ED76D7700C590B9 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
- dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/include/mach-o";
+ dstPath = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/local/include/mach-o";
dstSubfolderSpec = 0;
files = (
F9A3DE1E0ED7738300C590B9 /* prune_trie.h in CopyFiles */,
F9B1A25E0A3A44CB00DA8FAB /* install man page */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
- dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1";
+ dstPath = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/share/man/man1";
dstSubfolderSpec = 0;
files = (
F9B1A2640A3A563E00DA8FAB /* rebase.1 in install man page */,
F9B813870EC2659600F94C13 /* install man page */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
- dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1";
+ dstPath = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/share/man/man1";
dstSubfolderSpec = 0;
files = (
F9B813850EC2657800F94C13 /* unwinddump.1 in install man page */,
F9C12EA50ED63E05005BC69D /* install man page */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
- dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1";
+ dstPath = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/share/man/man1";
dstSubfolderSpec = 0;
files = (
F9C12EA30ED63DE7005BC69D /* dyldinfo.1 in install man page */,
B3B672411406D42800A376BB /* Snapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Snapshot.cpp; path = src/ld/Snapshot.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
+ C1E27B571F6B1B67003B8FA6 /* thread_starts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_starts.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
+ C1E27B591F6B1B70003B8FA6 /* thread_starts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = thread_starts.h; sourceTree = "<group>"; };
F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; };
F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
F91B7B0318987D5F0099486F /* AddressSpace.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AddressSpace.hpp; sourceTree = "<group>"; };
F9CC24181461FB4300A92174 /* superblob.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = superblob.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
F9CCF761144CE1AD007CB524 /* create_configure */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = text.script.sh; name = create_configure; path = src/create_configure; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
F9EA72CB097454A6008B4F1D /* machocheck */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = machocheck; sourceTree = BUILT_PRODUCTS_DIR; };
- F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/other/machochecker.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
+ F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/other/machochecker.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/ld/debugline.c; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
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; };
F9AE20FE1107D1440007ED5D /* dylibs.h */,
F9A4DB8F10F816FF00BD8423 /* objc.cpp */,
F9A4DB9010F816FF00BD8423 /* objc.h */,
+ C1E27B571F6B1B67003B8FA6 /* thread_starts.cpp */,
+ C1E27B591F6B1B70003B8FA6 /* thread_starts.h */,
F9AA650C1051BD2B003E3539 /* stubs */,
);
name = passes;
buildActionMask = 2147483647;
files = (
F9FC510A1BC893C400FEC3F8 /* code_dedup.cpp in Sources */,
+ C1E27B581F6B1B68003B8FA6 /* thread_starts.cpp in Sources */,
FA95D6141AB25CF400395811 /* textstub_dylib_file.cpp in Sources */,
F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */,
F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */,
"$(DEVELOPER_DIR)/usr/local/include",
"$(DT_TOOLCHAIN_DIR)/usr/local/include",
);
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/local/bin";
ONLY_ACTIVE_ARCH = NO;
PRODUCT_NAME = objcimageinfo;
SDKROOT = macosx.internal;
"$(DEVELOPER_DIR)/usr/local/include",
"$(DT_TOOLCHAIN_DIR)/usr/local/include",
);
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/local/bin";
PRODUCT_NAME = objcimageinfo;
SDKROOT = macosx.internal;
WARNING_CFLAGS = (
"$(DEVELOPER_DIR)/usr/local/include",
"$(DT_TOOLCHAIN_DIR)/usr/local/include",
);
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/local/bin";
PRODUCT_NAME = objcimageinfo;
SDKROOT = macosx.internal;
WARNING_CFLAGS = (
"$(DT_TOOLCHAIN_DIR)/usr/local/include",
"$(TOOLCHAIN_DIR)/usr/local/include",
);
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
LINKER_DISPLAYS_MANGLED_NAMES = NO;
MACOSX_DEPLOYMENT_TARGET = "";
"-L$(DT_TOOLCHAIN_DIR)/usr/lib",
"-ltapi",
"@$(DERIVED_FILE_DIR)/linkExtras",
+ "-Wl,-stack_size,0x10000000",
);
PREBINDING = NO;
PRODUCT_NAME = ld;
"$(DT_TOOLCHAIN_DIR)/usr/local/include",
"$(TOOLCHAIN_DIR)/usr/local/include",
);
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"-L$(DT_TOOLCHAIN_DIR)/usr/lib",
"-ltapi",
"@$(DERIVED_FILE_DIR)/linkExtras",
+ "-Wl,-stack_size,0x01000000",
);
PREBINDING = NO;
PRODUCT_NAME = ld;
"$(TOOLCHAIN_DIR)/usr/local/include",
"$(SRCROOT)/src/ld",
);
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/local/bin";
ONLY_ACTIVE_ARCH = NO;
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(TOOLCHAIN_DIR)/usr/local/include",
"$(SRCROOT)/src/ld",
);
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/local/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
"$(DT_TOOLCHAIN_DIR)/usr/local/include",
"$(TOOLCHAIN_DIR)/usr/local/include",
);
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
GCC_MODEL_TUNING = G5;
GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))";
HEADER_SEARCH_PATHS = "";
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header";
PREBINDING = NO;
PRODUCT_NAME = rebase;
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
HEADER_SEARCH_PATHS = "";
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
"$(TOOLCHAIN_DIR)/usr/local/include",
"$(SRCROOT)/src/ld",
);
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/local/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
HEADER_SEARCH_PATHS = "";
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/local/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/lib";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_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 = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/lib";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_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 = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/lib";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_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 = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
HEADER_SEARCH_PATHS = "";
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_WARN_UNUSED_LABEL = NO;
GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/local/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
HEADER_SEARCH_PATHS = "";
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/local/bin";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
PREBINDING = NO;
PRODUCT_NAME = rebase;
SDKROOT = macosx.internal;
GCC_MODEL_TUNING = G5;
GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))";
HEADER_SEARCH_PATHS = "";
- INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin";
+ INSTALL_PATH = "$(DT_VARIANT)/$(TOOLCHAIN_INSTALL_DIR)/usr/bin";
OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header";
PREBINDING = NO;
PRODUCT_NAME = rebase;
#ifndef MH_KEXT_BUNDLE
#define MH_KEXT_BUNDLE 11
#endif
+#ifndef MH_SIM_SUPPORT
+ #define MH_SIM_SUPPORT 0x08000000
+#endif
#ifndef LC_DYLD_INFO
#define LC_DYLD_INFO 0x22 /* compressed dyld information */
#define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) /* compressed dyld information only */
#define EXPORT_SYMBOL_FLAGS_INDIRECT_DEFINITION 0x08
#define EXPORT_SYMBOL_FLAGS_HAS_SPECIALIZATIONS 0x10
-#endif
+#endif
+
+#ifndef BIND_SPECIAL_DYLIB_WEAK_LOOKUP
+#define BIND_SPECIAL_DYLIB_WEAK_LOOKUP -3
+#endif
+
+#ifndef BIND_OPCODE_THREADED
+#define BIND_OPCODE_THREADED 0xD0
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
+#define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB 0x00
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_APPLY
+#define BIND_SUBOPCODE_THREADED_APPLY 0x01
+#endif
+
+#if SUPPORT_ARCH_arm64e
+
+// clang encodes the combination of the key bits as these values.
+typedef enum {
+ ptrauth_key_asia = 0,
+ ptrauth_key_asib = 1,
+ ptrauth_key_asda = 2,
+ ptrauth_key_asdb = 3,
+} ptrauth_key;
+
+#endif
#ifndef S_THREAD_LOCAL_REGULAR
#define S_THREAD_LOCAL_REGULAR 0x11
#define ARM64_RELOC_TLVP_LOAD_PAGEOFF12 9 // offset within page of TLVP slot, scaled by r_length
#define ARM64_RELOC_ADDEND 10 // r_symbolnum is addend for next reloc
+#if SUPPORT_ARCH_arm64e
+ #define ARM64_RELOC_AUTHENTICATED_POINTER 11 // An authenticated pointer.
+#endif
#define UNW_ARM64_X0 0
#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A
#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B
#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C
+#define DYLD_CACHE_ADJ_V2_THREADED_POINTER_64 0x0D
#ifndef LC_BUILD_VERSION
};
#endif
+#ifndef PLATFORM_IOSMAC
+ #define PLATFORM_IOSMAC 6
+#endif
// kind target-address fixup-addr [adj]
fi
done
-if [ -n "${RC_HIDE_TIDE}" ]; then
- echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h
- echo "#define SUPPORT_APPLE_TV 0" >> ${DERIVED_FILE_DIR}/configure.h
-else
- if [ -n "${DT_VARIANT}" -a "${DT_VARIANT}" != "PONDEROSA" ]; then
- echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h
- echo "#define SUPPORT_APPLE_TV 0" >> ${DERIVED_FILE_DIR}/configure.h
- else
- echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS} (tvOS)\"" >> ${DERIVED_FILE_DIR}/configure.h
- echo "#define SUPPORT_APPLE_TV 1" >> ${DERIVED_FILE_DIR}/configure.h
- fi
-fi
+echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h
if [ -f "${DT_TOOLCHAIN_DIR}/usr/lib/libswiftDemangle.dylib" ]; then
echo "${DT_TOOLCHAIN_DIR}/usr/lib/libswiftDemangle.dylib" > ${DERIVED_FILE_DIR}/linkExtras
};
+
#endif // __ARCHITECTURES__
virtual const char* name() const { return "mach-o header and load commands"; }
virtual uint64_t size() const;
virtual uint64_t objectAddress() const { return _address; }
+ virtual bool lcBuildVersionDisabled() const;
+
virtual void copyRawContent(uint8_t buffer[]) const;
// overrides of HeaderAndLoadCommandsAbtract
uint8_t* copyDylibIDLoadCommand(uint8_t* p) const;
uint8_t* copyRoutinesLoadCommand(uint8_t* p) const;
uint8_t* copyUUIDLoadCommand(uint8_t* p) const;
- uint8_t* copyVersionLoadCommand(uint8_t* p) const;
- uint8_t* copyBuildVersionLoadCommand(uint8_t* p) const;
+ uint8_t* copyVersionLoadCommand(uint8_t* p, const ld::Platform& platform, uint32_t minVersion, uint32_t sdkVersion) const;
+ uint8_t* copyBuildVersionLoadCommand(uint8_t* p, const ld::Platform& platform, uint32_t minVersion, uint32_t sdkVersion) const;
uint8_t* copySourceVersionLoadCommand(uint8_t* p) const;
uint8_t* copyThreadsLoadCommand(uint8_t* p) const;
uint8_t* copyEntryPointLoadCommand(uint8_t* p) const;
bool _hasRPathLoadCommands;
bool _hasSubFrameworkLoadCommand;
bool _hasVersionLoadCommand;
- bool _hasBuildVersionLoadCommand;
bool _hasFunctionStartsLoadCommand;
bool _hasDataInCodeLoadCommand;
bool _hasSourceVersionLoadCommand;
bool _hasOptimizationHints;
- Options::Platform _platform;
+ bool _simulatorSupportDylib;
+ ld::VersionSet _platforms;
uint32_t _dylibLoadCommmandsCount;
uint32_t _allowableClientLoadCommmandsCount;
uint32_t _dyldEnvironExrasCount;
}
_hasRPathLoadCommands = (_options.rpaths().size() != 0);
_hasSubFrameworkLoadCommand = (_options.umbrellaName() != NULL);
- _platform = _options.platform();
- if ( (_platform == Options::kPlatformUnknown) && (_state.derivedPlatform != 0) )
- _platform = (Options::Platform)_state.derivedPlatform;
- if ( _options.addVersionLoadCommand() ) {
- _hasBuildVersionLoadCommand = _options.addBuildVersionLoadCommand();
- _hasVersionLoadCommand = !_hasBuildVersionLoadCommand;
- }
- else if (((_options.outputKind() == Options::kObjectFile) && (_platform != Options::kPlatformUnknown) && !state.objectFileFoundWithNoVersion) ) {
- _hasBuildVersionLoadCommand = (_platform == Options::kPlatform_bridgeOS);
- _hasVersionLoadCommand = !_hasBuildVersionLoadCommand;
- }
- else {
- _hasVersionLoadCommand = false;
- _hasBuildVersionLoadCommand = false;
+ _platforms = _options.platforms();
+ if ( _platforms.empty() && (!_state.derivedPlatforms.empty()) )
+ _platforms = _state.derivedPlatforms;
+ if (_platforms.contains(ld::kPlatform_macOS) &&
+ ((strcmp(_options.installPath(), "/usr/lib/system/libsystem_kernel.dylib") == 0)
+ || (strcmp(_options.installPath(), "/usr/lib/system/libsystem_platform.dylib") == 0)
+ || (strcmp(_options.installPath(), "/usr/lib/system/libsystem_pthread.dylib") == 0)
+ || (strcmp(_options.installPath(), "/usr/lib/system/libsystem_platform_debug.dylib") == 0)
+ || (strcmp(_options.installPath(), "/usr/lib/system/libsystem_pthread_debug.dylib") == 0)
+ || (strcmp(_options.installPath(), "/System/Library/PrivateFrameworks/SiriUI.framework/Versions/A/SiriUI") == 0))) {
+ _simulatorSupportDylib = true;
+ } else {
+ _simulatorSupportDylib = false;
}
+ _hasVersionLoadCommand = _options.addVersionLoadCommand();
+ // in ld -r mode, only if all input .o files have load command, then add one to output
+ if ( !_hasVersionLoadCommand && (_options.outputKind() == Options::kObjectFile) && !state.objectFileFoundWithNoVersion )
+ _hasVersionLoadCommand = true;
_hasFunctionStartsLoadCommand = _options.addFunctionStarts();
_hasDataInCodeLoadCommand = _options.addDataInCodeInfo();
_hasSourceVersionLoadCommand = _options.needsSourceVersionLoadCommand();
if ( _hasUUIDLoadCommand )
sz += sizeof(macho_uuid_command<P>);
- if ( _hasVersionLoadCommand )
- sz += sizeof(macho_version_min_command<P>);
-
- if ( _hasBuildVersionLoadCommand )
- sz += alignedSize(sizeof(macho_build_version_command<P>) + sizeof(macho_build_tool_version<P>)*_toolsVersions.size());
-
+ if ( _hasVersionLoadCommand ) {
+ if (_simulatorSupportDylib) {
+#if 0
+//FIXME hack to workaround simulator issues without cctools changes
+ sz += sizeof(macho_version_min_command<P>);
+ sz += (_options.platforms().count() * alignedSize(sizeof(macho_build_version_command<P>) + sizeof(macho_build_tool_version<P>)*_toolsVersions.size()));
+#else
+ sz += sizeof(macho_version_min_command<P>);
+#endif
+ } else if (_options.platforms().minOS(ld::supportsLCBuildVersion) && !lcBuildVersionDisabled()) {
+ sz += (_options.platforms().count() * alignedSize(sizeof(macho_build_version_command<P>) + sizeof(macho_build_tool_version<P>)*_toolsVersions.size()));
+ } else {
+ sz += sizeof(macho_version_min_command<P>);
+ }
+ }
+
if ( _hasSourceVersionLoadCommand )
sz += sizeof(macho_source_version_command<P>);
if ( _hasUUIDLoadCommand )
++count;
-
- if ( _hasVersionLoadCommand )
- ++count;
- if ( _hasBuildVersionLoadCommand )
- ++count;
-
+ if ( _hasVersionLoadCommand ) {
+ if (_simulatorSupportDylib) {
+#if 0
+ //FIXME hack to workaround simulator issues without cctools changes
+ ++count;
+ count += _options.platforms().count();
+#else
+ ++count;
+#endif
+ } else if (_options.platforms().minOS(ld::supportsLCBuildVersion) && !lcBuildVersionDisabled()) {
+ count += _options.platforms().count();
+ } else {
+ ++count;
+ }
+ }
+
if ( _hasSourceVersionLoadCommand )
++count;
bits |= MH_WEAK_DEFINES;
if ( _writer.usesWeakExternalSymbols || _state.hasWeakExternalSymbols )
bits |= MH_BINDS_TO_WEAK;
- if ( _options.prebind() )
- bits |= MH_PREBOUND;
- if ( _options.splitSeg() )
- bits |= MH_SPLIT_SEGS;
if ( (_options.outputKind() == Options::kDynamicLibrary)
&& _writer._noReExportedDylibs
&& _options.useSimplifiedDylibReExports() ) {
bits |= MH_NO_HEAP_EXECUTION;
if ( _options.markAppExtensionSafe() && (_options.outputKind() == Options::kDynamicLibrary) )
bits |= MH_APP_EXTENSION_SAFE;
+#if 0
+ //FIXME hack to workaround simulator issues without cctools changes
+ if (_simulatorSupportDylib)
+ bits |= MH_SIM_SUPPORT;
+#endif
}
if ( _options.hasExecutableStack() )
bits |= MH_ALLOW_STACK_EXECUTION;
template <>
uint32_t HeaderAndLoadCommandsAtom<x86_64>::cpuSubType() const
{
- if ( (_options.outputKind() == Options::kDynamicExecutable) && (_state.cpuSubType == CPU_SUBTYPE_X86_64_ALL) && (_options.macosxVersionMin() >= ld::mac10_5) )
+ if ( (_options.outputKind() == Options::kDynamicExecutable) && (_state.cpuSubType == CPU_SUBTYPE_X86_64_ALL) && _options.platforms().minOS(ld::mac10_5) )
return (_state.cpuSubType | 0x80000000);
else
return _state.cpuSubType;
return S_DTRACE_DOF;
case ld::Section::typeUnwindInfo:
return S_REGULAR;
+ case ld::Section::typeThreadStarts:
+ return S_REGULAR;
case ld::Section::typeObjCClassRefs:
case ld::Section::typeObjC2CategoryList:
return S_REGULAR | S_ATTR_NO_DEAD_STRIP;
template <typename A>
-uint8_t* HeaderAndLoadCommandsAtom<A>::copyVersionLoadCommand(uint8_t* p) const
+uint8_t* HeaderAndLoadCommandsAtom<A>::copyVersionLoadCommand(uint8_t* p, const ld::Platform& platform, uint32_t minVersion, uint32_t sdkVersion) const
{
macho_version_min_command<P>* cmd = (macho_version_min_command<P>*)p;
- switch (_platform) {
- case Options::kPlatformOSX:
+ switch (platform) {
+ case ld::kPlatform_macOS:
cmd->set_cmd(LC_VERSION_MIN_MACOSX);
cmd->set_cmdsize(sizeof(macho_version_min_command<P>));
- cmd->set_version(_state.minOSVersion);
- cmd->set_sdk(_options.sdkVersion());
+ cmd->set_version(minVersion);
+ cmd->set_sdk(sdkVersion);
break;
- case Options::kPlatformiOS:
+ case ld::kPlatform_iOS:
+ case ld::kPlatform_iOSSimulator:
cmd->set_cmd(LC_VERSION_MIN_IPHONEOS);
cmd->set_cmdsize(sizeof(macho_version_min_command<P>));
- cmd->set_version(_state.minOSVersion);
- cmd->set_sdk(_options.sdkVersion());
+ cmd->set_version(minVersion);
+ cmd->set_sdk(sdkVersion);
break;
- case Options::kPlatformWatchOS:
+ case ld::kPlatform_watchOS:
+ case ld::kPlatform_watchOSSimulator:
cmd->set_cmd(LC_VERSION_MIN_WATCHOS);
cmd->set_cmdsize(sizeof(macho_version_min_command<P>));
- cmd->set_version(_state.minOSVersion);
- cmd->set_sdk(_options.sdkVersion());
+ cmd->set_version(minVersion);
+ cmd->set_sdk(sdkVersion);
break;
-#if SUPPORT_APPLE_TV
- case Options::kPlatform_tvOS:
+ case ld::kPlatform_tvOS:
+ case ld::kPlatform_tvOSSimulator:
cmd->set_cmd(LC_VERSION_MIN_TVOS);
cmd->set_cmdsize(sizeof(macho_version_min_command<P>));
- cmd->set_version(_state.minOSVersion);
- cmd->set_sdk(_options.sdkVersion());
+ cmd->set_version(minVersion);
+ cmd->set_sdk(sdkVersion);
break;
-#endif
- case Options::kPlatformUnknown:
+ case ld::kPlatform_unknown:
assert(0 && "unknown platform");
break;
- case Options::kPlatform_bridgeOS:
+ case ld::kPlatform_iOSMac:
+ assert(0 && "iOSMac uses LC_BUILD_VERSION");
+ break;
+ case ld::kPlatform_bridgeOS:
assert(0 && "bridgeOS uses LC_BUILD_VERSION");
break;
}
template <typename A>
-uint8_t* HeaderAndLoadCommandsAtom<A>::copyBuildVersionLoadCommand(uint8_t* p) const
+ uint8_t* HeaderAndLoadCommandsAtom<A>::copyBuildVersionLoadCommand(uint8_t* p, const ld::Platform& platform, uint32_t minVersion, uint32_t sdkVersion) const
{
macho_build_version_command<P>* cmd = (macho_build_version_command<P>*)p;
cmd->set_cmd(LC_BUILD_VERSION);
cmd->set_cmdsize(alignedSize(sizeof(macho_build_version_command<P>) + sizeof(macho_build_tool_version<P>)*_toolsVersions.size()));
- cmd->set_platform(_options.platform());
- cmd->set_minos(_state.minOSVersion);
- cmd->set_sdk(_options.sdkVersion());
+ cmd->set_platform(platform);
+ cmd->set_minos(minVersion);
+ cmd->set_sdk(sdkVersion);
cmd->set_ntools(_toolsVersions.size());
macho_build_tool_version<P>* tools = (macho_build_tool_version<P>*)(p + sizeof(macho_build_version_command<P>));
for (uint64_t tool : _toolsVersions) {
return p + sizeof(macho_linkedit_data_command<P>);
}
+template <typename A>
+bool HeaderAndLoadCommandsAtom<A>::lcBuildVersionDisabled() const {
+ static std::once_flag envChecked;
+ static bool disabled = false;
+ std::call_once(envChecked, [&](){
+ if (getenv("LD_FORCE_LEGACY_VERSION_LOAD_CMDS") != nullptr ) {
+ disabled = true;
+ }
+ });
+ return disabled;
+}
template <typename A>
void HeaderAndLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const
mh->set_flags(this->flags());
// copy load commands
- uint8_t* p = &buffer[sizeof(macho_header<P>)];
+ __block uint8_t* p = &buffer[sizeof(macho_header<P>)];
if ( _options.outputKind() == Options::kObjectFile )
p = this->copySingleSegmentLoadCommand(p);
if ( _hasUUIDLoadCommand )
p = this->copyUUIDLoadCommand(p);
-
- if ( _hasVersionLoadCommand )
- p = this->copyVersionLoadCommand(p);
- if ( _hasBuildVersionLoadCommand )
- p = this->copyBuildVersionLoadCommand(p);
+ if ( _hasVersionLoadCommand ) {
+ if (_simulatorSupportDylib) {
+#if 0
+ //FIXME hack to workaround simulator issues without cctools changes
+ p = this->copyVersionLoadCommand(p, ld::kPlatform_macOS, 0, _options.sdkVersion());
+ _options.platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ p = this->copyBuildVersionLoadCommand(p, platform, version, _options.sdkVersion());
+ });
+#else
+ _options.platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ if (platform == ld::kPlatform_macOS) {
+ p = this->copyVersionLoadCommand(p, platform, version, _options.sdkVersion());
+ }
+ });
+#endif
+ } else if (_options.platforms().minOS(ld::supportsLCBuildVersion) && !lcBuildVersionDisabled()) {
+ _options.platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ p = this->copyBuildVersionLoadCommand(p, platform, version, _options.sdkVersion());
+ });
+ } else {
+ assert(_platforms.count() == 1 && "LC_BUILD_VERSION required when there are multiple platforms");
+ _options.platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ p = this->copyVersionLoadCommand(p, platform, version, _options.sdkVersion());
+ });
+ }
+ }
if ( _hasSourceVersionLoadCommand )
p = this->copySourceVersionLoadCommand(p);
// handle inlined framework first.
if (info.isInlined) {
auto interface = _options.findTAPIFile(info.path);
- auto file = textstub::dylib::parse(info.path, std::move(interface), info.modTime, info.ordinal, _options, indirectDylib);
- assert(file && "could not locate the inlined file");
+ if (!interface)
+ throwf("could not find inlined dylib file: %s", info.path);
+ auto file = textstub::dylib::parse(info.path, interface, info.modTime, info.ordinal, _options, indirectDylib);
if (!file)
- throwf("could not parse inline dylib file: %s(%s)", interface->getInstallName().c_str(), info.path);
+ throwf("could not parse inlined dylib file: %s(%s)", interface->getInstallName().c_str(), info.path);
return file;
}
// map in whole file
const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
bool sliceFound = false;
sliceCount = OSSwapBigToHostInt32(fh->nfat_arch);
- if ( _options.preferSubArchitecture() ) {
- // first try to find a slice that match cpu-type and cpu-sub-type
+ // first try to find a slice that match cpu-type and cpu-sub-type
+ for (uint32_t i=0; i < sliceCount; ++i) {
+ if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture())
+ && ((OSSwapBigToHostInt32(archs[i].cpusubtype) & ~CPU_SUBTYPE_MASK) == (uint32_t)_options.subArchitecture()) ) {
+ sliceToUse = i;
+ sliceFound = true;
+ break;
+ }
+ }
+ if ( !sliceFound && _options.allowSubArchitectureMismatches() ) {
+ // look for any slice that matches just cpu-type
for (uint32_t i=0; i < sliceCount; ++i) {
- if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture())
- && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)_options.subArchitecture()) ) {
+ if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture() ) {
sliceToUse = i;
sliceFound = true;
break;
}
}
if ( !sliceFound ) {
- // look for any slice that matches just cpu-type
- for (uint32_t i=0; i < sliceCount; ++i) {
- if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture() ) {
+ // Look for a fallback slice.
+ for (uint32_t i = 0; i < sliceCount; ++i) {
+ if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.fallbackArchitecture() &&
+ OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)_options.fallbackSubArchitecture() ) {
sliceToUse = i;
sliceFound = true;
break;
objOpts.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions();
objOpts.simulator = _options.targetIOSSimulator();
objOpts.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable));
+#if SUPPORT_ARCH_arm64e
+ objOpts.supportsAuthenticatedPointers = _options.supportsAuthenticatedPointers();
+#endif
objOpts.subType = _options.subArchitecture();
- objOpts.platform = _options.platform();
- objOpts.minOSVersion = _options.minOSversion();
+ objOpts.platforms = _options.platforms();
objOpts.srcKind = ld::relocatable::File::kSourceObj;
objOpts.treateBitcodeAsData = _options.bitcodeKind() == Options::kBitcodeAsData;
objOpts.usingBitcode = _options.bundleBitcode();
}
else {
if ( isFatFile )
- throwf("file is universal (%u slices) but does not contain a(n) %s slice: %s", sliceCount, _options.architectureName(), info.path);
+ throwf("file is universal (%u slices) but does not contain the %s architecture: %s", sliceCount, _options.architectureName(), info.path);
else
throwf("file was built for %s which is not the architecture being linked (%s): %s", fileArch(p, len), _options.architectureName(), info.path);
}
}
}
catch (const char* msg) {
- warning("Auto-Linking %s", msg);
+ // <rdar://problem/40829444> only warn about missing auto-linked framework if some missing symbol error happens later
+ state.missingLinkerOptionFrameworks.insert(frameworkName);
}
state.linkerOptionFrameworks.insert(frameworkName);
}
}
}
catch (const char* msg) {
- warning("Auto-Linking %s", msg);
+ // <rdar://problem/40829444> only warn about missing auto-linked library if some missing symbol error happens later
+ state.missingLinkerOptionLibraries.insert(libName);
}
state.linkerOptionLibraries.insert(libName);
}
if ( amount >= readAmount ) {
cpu_type_t type;
cpu_subtype_t subtype;
- Options::Platform platform;
- if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype, &platform) ) {
- opts.setArchitecture(type, subtype, platform);
+ ld::Platform platform;
+ uint32_t minOsVersion;
+ if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype, &platform, &minOsVersion) ) {
+ opts.setArchitecture(type, subtype, platform, minOsVersion);
*archName = opts.architectureName();
return;
}
// no thin .o files found, so default to same architecture this tool was built as
warning("-arch not specified");
#if __i386__
- opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL, Options::kPlatformOSX);
+ opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL, ld::kPlatform_macOS, 0);
#elif __x86_64__
- opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, Options::kPlatformOSX);
+ opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, ld::kPlatform_macOS, 0);
#elif __arm__
- opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6, Options::kPlatformOSX);
+ opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6, ld::kPlatform_macOS, 0);
#else
#error unknown default architecture
#endif
state.bundleLoader = _bundleLoader;
// <rdar://problem/10807040> give an error when -nostdlib is used and libSystem is missing
- if ( (state.dylibs.size() == 0) && _options.needsEntryPointLoadCommand() )
- throw "dynamic main executables must link with libSystem.dylib";
+ if ( (state.dylibs.size() == 0) && _options.needsEntryPointLoadCommand() ) {
+ // HACK until 39514191 is fixed
+ bool grandfather = false;
+ for (const File* inFile : _inputFiles) {
+ if ( strstr(inFile->path(), "exit-asm.o") != NULL )
+ grandfather = true;
+ }
+ if ( !grandfather )
+ throw "dynamic main executables must link with libSystem.dylib";
+ }
}
void InputFiles::archives(ld::Internal& state)
virtual void encode() const;
private:
+ void encodeV1() const;
+
struct rebase_tmp
{
rebase_tmp(uint8_t op, uint64_t p1, uint64_t p2=0) : opcode(op), operand1(p1), operand2(p2) {}
// sort rebase info by type, then address
std::vector<OutputFile::RebaseInfo>& info = this->_writer._rebaseInfo;
+ if (info.empty())
+ return;
+
std::sort(info.begin(), info.end());
+ // use encoding based on target minOS
+ if ( _options.useLinkedListBinding() && !this->_writer._hasUnalignedFixup ) {
+ if ( info.back()._type != REBASE_TYPE_POINTER )
+ throw "unsupported rebase type with linked list opcodes";
+ // As the binding and rebasing are both linked lists, just use the binds
+ // to do everything.
+ } else {
+ encodeV1();
+ }
+}
+
+
+template <typename A>
+void RebaseInfoAtom<A>::encodeV1() const
+{
+ std::vector<OutputFile::RebaseInfo>& info = this->_writer._rebaseInfo;
+
// convert to temp encoding that can be more easily optimized
std::vector<rebase_tmp> mid;
uint64_t curSegStart = 0;
private:
+ void encodeV1() const;
+ void encodeV2() const;
+
typedef typename A::P P;
typedef typename A::P::E E;
typedef typename A::P::uint_t pint_t;
template <typename A>
void BindingInfoAtom<A>::encode() const
+{
+ // use encoding based on target minOS
+ if ( _options.useLinkedListBinding() && !this->_writer._hasUnalignedFixup ) {
+ encodeV2();
+ } else {
+ encodeV1();
+ }
+}
+
+
+template <typename A>
+void BindingInfoAtom<A>::encodeV1() const
{
// sort by library, symbol, type, then address
std::vector<OutputFile::BindingInfo>& info = this->_writer._bindingInfo;
std::sort(info.begin(), info.end());
-
+
// convert to temp encoding that can be more easily optimized
std::vector<binding_tmp> mid;
uint64_t curSegStart = 0;
if (log) fprintf(stderr, "total binding info size = %ld\n", this->_encodedData.size());
}
+template <typename A>
+void BindingInfoAtom<A>::encodeV2() const
+{
+ std::vector<OutputFile::BindingInfo>& bindInfo = this->_writer._bindingInfo;
+ std::vector<OutputFile::RebaseInfo>& rebaseInfo = this->_writer._rebaseInfo;
+ const static bool log = false;
+
+ std::sort(bindInfo.begin(), bindInfo.end());
+
+ // convert to temp encoding that can be more easily optimized
+ std::vector<binding_tmp> mid;
+ uint64_t curSegStart = 0;
+ uint64_t curSegEnd = 0;
+ uint32_t curSegIndex = 0;
+ int ordinal = 0x80000000;
+ const char* symbolName = NULL;
+ uint8_t type = 0;
+ uint64_t address = (uint64_t)(-1);
+ int64_t addend = 0;
+ uint64_t numBinds = (uint64_t)(-1);
+ for (std::vector<OutputFile::BindingInfo>::iterator it = bindInfo.begin(); it != bindInfo.end(); ++it) {
+ bool madeChange = false;
+ if ( ordinal != it->_libraryOrdinal ) {
+ if ( it->_libraryOrdinal <= 0 ) {
+ // special lookups are encoded as negative numbers in BindingInfo
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, it->_libraryOrdinal));
+ }
+ else if ( it->_libraryOrdinal <= 15 ) {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM, it->_libraryOrdinal));
+ }
+ else {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB, it->_libraryOrdinal));
+ }
+ ordinal = it->_libraryOrdinal;
+ madeChange = true;
+ }
+ if ( symbolName != it->_symbolName ) {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->_flags, 0, it->_symbolName));
+ symbolName = it->_symbolName;
+ madeChange = true;
+ }
+ if ( type != it->_type ) {
+ if ( it->_type != BIND_TYPE_POINTER )
+ throw "unsupported bind type with linked list opcodes";
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->_type));
+ type = it->_type;
+ madeChange = true;
+ }
+ if ( address != it->_address ) {
+ // Note, we don't push the addresses here. That is all done later with the threaded chains
+ if ( (it->_address < curSegStart) || ( it->_address >= curSegEnd) ) {
+ if ( ! this->_writer.findSegment(this->_state, it->_address, &curSegStart, &curSegEnd, &curSegIndex) )
+ throw "binding address outside range of any segment";
+ }
+ address = it->_address;
+ }
+ if ( addend != it->_addend ) {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->_addend));
+ addend = it->_addend;
+ madeChange = true;
+ }
+
+ if (madeChange) {
+ ++numBinds;
+ mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0));
+ }
+ it->_threadedBindOrdinal = numBinds;
+ }
+
+ // We can only support 2^16 bind ordinals.
+ if ( (numBinds > 0x10000) && (numBinds != (uint64_t)(-1)) )
+ throwf("too many binds (%llu). The limit is 65536", numBinds);
+
+ // Now that we have the bind ordinal table populate, set the page starts.
+
+ std::vector<int64_t>& threadedRebaseBindIndices = this->_writer._threadedRebaseBindIndices;
+ threadedRebaseBindIndices.reserve(bindInfo.size() + rebaseInfo.size());
+
+ for (int64_t i = 0, e = rebaseInfo.size(); i != e; ++i)
+ threadedRebaseBindIndices.push_back(-i);
+
+ for (int64_t i = 0, e = bindInfo.size(); i != e; ++i)
+ threadedRebaseBindIndices.push_back(i + 1);
+
+ // Now sort the entries by address.
+ std::sort(threadedRebaseBindIndices.begin(), threadedRebaseBindIndices.end(),
+ [&rebaseInfo, &bindInfo](int64_t indexA, int64_t indexB) {
+ if (indexA == indexB)
+ return false;
+ uint64_t addressA = indexA <= 0 ? rebaseInfo[-indexA]._address : bindInfo[indexA - 1]._address;
+ uint64_t addressB = indexB <= 0 ? rebaseInfo[-indexB]._address : bindInfo[indexB - 1]._address;
+ assert(addressA != addressB);
+ return addressA < addressB;
+ });
+
+ curSegStart = 0;
+ curSegEnd = 0;
+ curSegIndex = 0;
+ uint64_t prevPageIndex = 0;
+ for (int64_t entryIndex : threadedRebaseBindIndices) {
+ OutputFile::RebaseInfo* rebase = nullptr;
+ OutputFile::BindingInfo* bind = nullptr;
+ uint64_t address = 0;
+ if (entryIndex <= 0) {
+ rebase = &rebaseInfo[-entryIndex];
+ address = rebase->_address;
+ } else {
+ bind = &bindInfo[entryIndex - 1];
+ address = bind->_address;
+ }
+ assert((address & 7) == 0);
+
+ bool newSegment = false;
+ if ( (address < curSegStart) || ( address >= curSegEnd) ) {
+ // Start of a new segment.
+ if ( ! this->_writer.findSegment(this->_state, address, &curSegStart, &curSegEnd, &curSegIndex) )
+ throw "binding address outside range of any segment";
+ newSegment = true;
+ }
+
+ // At this point we know we have the page starts array space reserved
+ // so set the page start for this entry if we haven't got one already.
+ uint64_t pageIndex = ( address - curSegStart ) / 4096;
+ if ( newSegment || (pageIndex != prevPageIndex) ) {
+ mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, curSegIndex, address - curSegStart));
+ mid.push_back(binding_tmp(BIND_OPCODE_THREADED | BIND_SUBOPCODE_THREADED_APPLY, 0));
+ }
+ prevPageIndex = pageIndex;
+ }
+ mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0));
+
+ // convert to compressed encoding
+ this->_encodedData.reserve(bindInfo.size()*2);
+
+ // First push the total number of binds so that we can allocate space for this in dyld.
+ if ( log ) fprintf(stderr, "BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB(%lld)\n", numBinds + 1);
+ this->_encodedData.append_byte(BIND_OPCODE_THREADED | BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB);
+ this->_encodedData.append_uleb128(numBinds + 1);
+
+ bool done = false;
+ for (typename std::vector<binding_tmp>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) {
+ switch ( it->opcode ) {
+ case BIND_OPCODE_DONE:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n");
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1);
+ this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1);
+ this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
+ this->_encodedData.append_uleb128(it->operand1);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1);
+ this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK));
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name);
+ this->_encodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1);
+ this->_encodedData.append_string(it->name);
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1);
+ this->_encodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1);
+ this->_encodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB);
+ this->_encodedData.append_sleb128(it->operand1);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2);
+ this->_encodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1);
+ this->_encodedData.append_uleb128(it->operand2);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1);
+ this->_encodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB);
+ this->_encodedData.append_uleb128(it->operand1);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n");
+ this->_encodedData.append_byte(BIND_OPCODE_DO_BIND);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1);
+ this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB);
+ this->_encodedData.append_uleb128(it->operand1);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t));
+ this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 );
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2);
+ this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB);
+ this->_encodedData.append_uleb128(it->operand1);
+ this->_encodedData.append_uleb128(it->operand2);
+ break;
+ case BIND_OPCODE_THREADED | BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ if ( log ) fprintf(stderr, "BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB(%lld)\n", it->operand1);
+ this->_encodedData.append_byte(BIND_OPCODE_THREADED | BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB);
+ this->_encodedData.append_uleb128(it->operand1);
+ break;
+ case BIND_OPCODE_THREADED | BIND_SUBOPCODE_THREADED_APPLY:
+ this->_encodedData.append_byte(BIND_OPCODE_THREADED | BIND_SUBOPCODE_THREADED_APPLY);
+ if ( log ) fprintf(stderr, "BIND_SUBOPCODE_THREADED_APPLY()\n");
+ break;
+ }
+ }
+
+ // align to pointer size
+ this->_encodedData.append_byte(BIND_OPCODE_DONE);
+ this->_encodedData.pad_to_size(sizeof(pint_t));
+
+ this->_encoded = true;
+
+ if (log) fprintf(stderr, "total binding info size = %ld\n", this->_encodedData.size());
+}
+
template <typename A>
case ld::Fixup::kindStoreTargetAddressLittleEndian64:
_64bitPointerLocations.push_back(address);
break;
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreLittleEndianAuth64:
+ case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64:
+ assert(false);
+ break;
+#endif
default:
warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address);
break;
case ld::Fixup::kindStoreTargetAddressLittleEndian64:
_64bitPointerLocations.push_back(address);
break;
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreLittleEndianAuth64:
+ case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64:
+ warning("authenticated pointer at address 0x%08llX prevents image from working in dyld shared cache", address);
+ break;
+#endif
default:
warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address);
break;
entry.set_n_type(N_UNDF | N_EXT);
}
else {
- if ( this->_options.prebind() )
- entry.set_n_type(N_PBUD | N_EXT);
- else
- entry.set_n_type(N_UNDF | N_EXT);
+ entry.set_n_type(N_UNDF | N_EXT);
}
// set n_sect
virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind,
const ld::Atom* inAtom, uint32_t offsetInAtom,
bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc,
+#if SUPPORT_ARCH_arm64e
+ ld::Fixup* fixupWithAuthData,
+#endif
const ld::Atom* toTarget, uint64_t toAddend,
const ld::Atom* fromTarget, uint64_t fromAddend) = 0;
protected:
virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind,
const ld::Atom* inAtom, uint32_t offsetInAtom,
bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc,
+#if SUPPORT_ARCH_arm64e
+ ld::Fixup* fixupWithAuthData,
+#endif
const ld::Atom* toTarget, uint64_t toAddend,
const ld::Atom* fromTarget, uint64_t fromAddend) { }
virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind,
const ld::Atom* inAtom, uint32_t offsetInAtom,
bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc,
+#if SUPPORT_ARCH_arm64e
+ ld::Fixup* fixupWithAuthData,
+#endif
const ld::Atom* toTarget, uint64_t toAddend,
const ld::Atom* fromTarget, uint64_t fromAddend) { }
virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind,
const ld::Atom* inAtom, uint32_t offsetInAtom,
bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc,
+#if SUPPORT_ARCH_arm64e
+ ld::Fixup* fixupWithAuthData,
+#endif
const ld::Atom* toTarget, uint64_t toAddend,
const ld::Atom* fromTarget, uint64_t fromAddend);
uint64_t toAddend;
const ld::Atom* fromTarget;
uint64_t fromAddend;
+#if SUPPORT_ARCH_arm64e
+ bool hasAuthData;
+ ld::Fixup::AuthData authData;
+#endif
};
uint32_t sectSymNum(bool external, const ld::Atom* target);
void encodeSectionReloc(ld::Internal::FinalSection* sect,
relocs.push_back(reloc1);
break;
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreLittleEndianAuth64:
+ case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64: {
+ assert(entry.fromTarget == NULL);
+ assert(entry.hasAuthData);
+
+ // An authenticated pointer is:
+ // {
+ // int32_t addend;
+ // uint16_t diversityData;
+ // uint16_t hasAddressDiversity : 1;
+ // uint16_t key : 2;
+ // uint16_t zeroes : 11;
+ // uint16_t zero : 1;
+ // uint16_t authenticated : 1;
+ // }
+ reloc1.set_r_address(address);
+ reloc1.set_r_symbolnum(symbolNum);
+ reloc1.set_r_pcrel(false);
+ reloc1.set_r_length(3);
+ reloc1.set_r_extern(external);
+ reloc1.set_r_type(ARM64_RELOC_AUTHENTICATED_POINTER);
+ relocs.push_back(reloc1);
+ }
+ break;
+#endif
+
default:
assert(0 && "need to handle arm64 -r reloc");
void SectionRelocationsAtom<A>::addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind kind,
const ld::Atom* inAtom, uint32_t offsetInAtom,
bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc,
+#if SUPPORT_ARCH_arm64e
+ ld::Fixup* fixupWithAuthData,
+#endif
const ld::Atom* toTarget, uint64_t toAddend,
const ld::Atom* fromTarget, uint64_t fromAddend)
{
entry.toAddend = toAddend;
entry.fromTarget = fromTarget;
entry.fromAddend = fromAddend;
+#if SUPPORT_ARCH_arm64e
+ if (fixupWithAuthData) {
+ entry.hasAuthData = true;
+ entry.authData = fixupWithAuthData->u.authData;
+ } else {
+ entry.hasAuthData = false;
+ }
+#endif
static ld::Internal::FinalSection* lastSection = NULL;
static SectionAndEntries* lastSectionAndEntries = NULL;
template <typename A>
uint32_t IndirectSymbolTableAtom<A>::symIndexOfLazyPointerAtom(const ld::Atom* lpAtom)
{
- for (ld::Fixup::iterator fit = lpAtom->fixupsBegin(); fit != lpAtom->fixupsEnd(); ++fit) {
- if ( fit->kind == ld::Fixup::kindLazyTarget ) {
- assert(fit->binding == ld::Fixup::bindingDirectlyBound);
- return symbolIndex(fit->u.target);
+ if ( lpAtom->contentType() == ld::Atom::typeLazyPointer || lpAtom->contentType() == ld::Atom::typeLazyDylibPointer ) {
+ for (ld::Fixup::iterator fit = lpAtom->fixupsBegin(); fit != lpAtom->fixupsEnd(); ++fit) {
+ if ( fit->kind == ld::Fixup::kindLazyTarget ) {
+ assert(fit->binding == ld::Fixup::bindingDirectlyBound);
+ return symbolIndex(fit->u.target);
+ }
+ }
+ }
+ else if ( lpAtom->contentType() == ld::Atom::typeNonLazyPointer ) {
+ for (ld::Fixup::iterator fit = lpAtom->fixupsBegin(); fit != lpAtom->fixupsEnd(); ++fit) {
+ if ( (fit->kind == ld::Fixup::kindStoreTargetAddressLittleEndian32) || (fit->kind == ld::Fixup::kindStoreTargetAddressLittleEndian64) ) {
+ assert(fit->binding == ld::Fixup::bindingDirectlyBound);
+ return symbolIndex(fit->u.target);
+ }
}
}
throw "internal error: lazy pointer missing fixupLazyTarget fixup";
{
//fprintf(stderr, "symIndexOfNonLazyPointerAtom(%p) %s\n", nlpAtom, nlpAtom->name());
for (ld::Fixup::iterator fit = nlpAtom->fixupsBegin(); fit != nlpAtom->fixupsEnd(); ++fit) {
- // non-lazy-pointer to a stripped symbol => no symbol index
- if ( fit->clusterSize != ld::Fixup::k1of1 )
- return INDIRECT_SYMBOL_LOCAL;
+#if SUPPORT_ARCH_arm64e
+ // Skip authentication fixups
+ if ( fit->clusterSize == ld::Fixup::k1of2 ) {
+ if ( fit->kind != ld::Fixup::kindSetAuthData )
+ break;
+ ++fit;
+ } else
+#endif
+ {
+ if ( fit->clusterSize != ld::Fixup::k1of1 )
+ return INDIRECT_SYMBOL_LOCAL;
+ }
const ld::Atom* target;
switch ( fit->binding ) {
case ld::Fixup::bindingDirectlyBound:
#include <map>
#include <sstream>
+#include "ld.hpp"
#include "Options.h"
#include "Architectures.hpp"
#include "MachOFileAbstraction.hpp"
Options::Options(int argc, const char* argv[])
- : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable),
- fHasPreferredSubType(false), fArchSupportsThumb2(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false),
- fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false),
+ : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0),
+ fFallbackArchitecture(0), fFallbackSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable),
+ fHasPreferredSubType(false), fArchSupportsThumb2(false), fBindAtLoad(false), fKeepPrivateExterns(false),
+ fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false),
fInterposeMode(kInterposeNone), fDeadStrip(false), fNameSpace(kTwoLevelNameSpace),
- fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName(NULL),
- fBaseAddress(0), fMaxAddress(0x7FFFFFFFFFFFFFFFLL),
- fBaseWritableAddress(0), fSplitSegs(false),
+ fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName(NULL),
+ fBaseAddress(0), fMaxAddress(0xFFFFFFFFFFFFFFFFULL),
+ fBaseWritableAddress(0),
fExportMode(kExportDefault), fLibrarySearchMode(kSearchDylibAndArchiveInEachDir),
fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(true),
fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak),
fClientName(NULL),
fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL),
- fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL),
+ fBundleLoader(NULL), fDtraceScriptName(NULL), fMapPath(NULL),
fDyldInstallPath("/usr/lib/dyld"), fLtoCachePath(NULL), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL),
+ fKextObjectsEnable(-1),fKextObjectsDirPath(NULL),fToolchainPath(NULL),
fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false),
fNonExecutableHeap(false), fDisableNonExecutableHeap(false),
fMinimumHeaderPad(32), fSegmentAlignment(4096),
fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fKextsUseStubs(false),
fUsingLazyDylibLinking(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false),
fOrderData(true), fMarkDeadStrippableDylib(false),
- fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false),
+ fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false),
+ fMakeThreadedStartsSection(false), fNoEHLabels(false),
fAllowCpuSubtypeMismatches(false), fEnforceDylibSubtypesMatch(false),
fWarnOnSwiftABIVersionMismatches(false), fUseSimplifiedDylibReExports(false),
fObjCABIVersion2Override(false), fObjCABIVersion1Override(false), fCanUseUpwardDylib(false),
fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false),
fDemangle(false), fTLVSupport(false),
fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false),
- fVersionLoadCommandForcedOff(false), fBuildVersionLoadCommand(false), fFunctionStartsLoadCommand(false),
+ fVersionLoadCommandForcedOff(false), fFunctionStartsLoadCommand(false),
fFunctionStartsForcedOn(false), fFunctionStartsForcedOff(false),
fDataInCodeInfoLoadCommand(false), fDataInCodeInfoLoadCommandForcedOn(false), fDataInCodeInfoLoadCommandForcedOff(false),
fCanReExportSymbols(false), fObjcCategoryMerging(true), fPageAlignDataAtoms(false),
fNeedsThreadLoadCommand(false), fEntryPointLoadCommand(false),
- fEntryPointLoadCommandForceOn(false), fEntryPointLoadCommandForceOff(false),
- fSourceVersionLoadCommand(false),
+ fSourceVersionLoadCommand(false),
fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false),
- fTargetIOSSimulator(false), fExportDynamic(false), fAbsoluteSymbols(false),
+ fExportDynamic(false), fAbsoluteSymbols(false),
fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true),
fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false),
fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false),
fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), fUseTextExecSegment(false),
fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false),
fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false),
+ fUseLinkedListBinding(false), fNoLazyBinding(false), fDebugVariant(false),
fReverseMapPath(NULL), fLTOCodegenOnly(false),
fIgnoreAutoLink(false), fAllowDeadDups(false), fAllowWeakImports(true), fInitializersTreatment(Options::kInvalid),
fZeroModTimeInDebugMap(false), fBitcodeKind(kBitcodeProcess),
- fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL),
- fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fWatchOSVersionMin(ld::wOSVersionUnset),
- fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL),
- fDependencyInfoPath(NULL), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0),
+ fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL),
+ fSaveTempFiles(false), fLinkSnapshot(this), fSnapshotRequested(false), fPipelineFifo(NULL),
+ fDependencyInfoPath(NULL), fBuildContextName(NULL), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0),
fUnalignedPointerTreatment(kUnalignedPointerIgnore)
{
this->checkForClassic(argc, argv);
{
// iPhoneOS always uses same protection for max and initial
// <rdar://problem/11663436> simulator apps need to use MacOSX max-prot
- if ( (fPlatform != kPlatformOSX) && !fTargetIOSSimulator )
+ if ( (platforms().contains(ld::kPlatform_macOS) == 0) && !targetIOSSimulator() )
return initialSegProtection(segName);
for(std::vector<Options::SegmentProtect>::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) {
return NULL;
}
-uint32_t Options::minOSversion() const
-{
- switch (fPlatform) {
- case kPlatformiOS:
- return iOSVersionMin();
- case kPlatformOSX:
- return macosxVersionMin();
- case kPlatformWatchOS:
- return watchOSVersionMin();
-#if SUPPORT_APPLE_TV
- case Options::kPlatform_tvOS:
- return iOSVersionMin();
-#endif
- case kPlatform_bridgeOS:
- return iOSVersionMin();
- case kPlatformUnknown:
- return 0;
- }
- return 0;
-}
-
-void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::Platform platform)
+void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, ld::Platform platform, uint32_t minOsVers)
{
for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
if ( (type == t->cpuType) && (subtype == t->cpuSubType) ) {
fArchitectureName = t->archName;
fHasPreferredSubType = t->isSubType;
fArchSupportsThumb2 = t->supportsThumb2;
- if ( fPlatform == kPlatformUnknown)
- fPlatform = platform;
+ if ( platforms().empty() && (platform != ld::kPlatform_unknown) )
+ fPlatforms.add({platform, minOsVers});
switch ( type ) {
case CPU_TYPE_I386:
case CPU_TYPE_X86_64:
- if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) && (fMacVersionMin == ld::macVersionUnset) ) {
+ if ( platforms().contains(ld::kPlatform_macOS) && (fOutputKind != Options::kObjectFile) && (platforms().minOS(ld::kPlatform_macOS) == 0) ) {
#ifdef DEFAULT_MACOSX_MIN_VERSION
warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION);
- setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
+ setVersionMin(ld::kPlatform_macOS, DEFAULT_MACOSX_MIN_VERSION);
#else
warning("-macosx_version_min not specified, assuming 10.6");
- fMacVersionMin = ld::mac10_6;
- #endif
+ setVersionMin(ld::kPlatform_macOS, "10.6");
+ #endif
}
break;
case CPU_TYPE_ARM:
case CPU_TYPE_ARM64:
- if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) {
+ if ( platforms().contains(ld::kPlatform_iOS) && (platforms().minOS(ld::kPlatform_iOS) == 0) && (fOutputKind != Options::kObjectFile) ) {
#if defined(DEFAULT_IPHONEOS_MIN_VERSION)
warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION);
- setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION);
+ setVersionMin(ld::kPlatformiOS, DEFAULT_IPHONEOS_MIN_VERSION);
#else
warning("-ios_version_min not specified, assuming 6.0");
- setIOSVersionMin("6.0");
+ setVersionMin(ld::kPlatform_iOS, "6.0");
#endif
}
+#if SUPPORT_ARCH_arm64e
+ if ( (fArchitecture == CPU_TYPE_ARM64) && (fSubArchitecture == CPU_SUBTYPE_ARM64_E) ) {
+ fSupportsAuthenticatedPointers = true;
+ }
+#endif
break;
}
fLinkSnapshot.recordArch(fArchitectureName);
// only use compressed LINKEDIT for:
// Mac OS X 10.6 or later
// iOS 3.1 or later
- if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff )
+ if ( !fMakeCompressedDyldInfo && platforms().minOS(ld::version2009) && !fMakeCompressedDyldInfoForceOff )
fMakeCompressedDyldInfo = true;
// Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB
- if ( minOS(ld::mac10_5, ld::iOS_2_0) )
+ if ( platforms().minOS(ld::version2008) )
fUseSimplifiedDylibReExports = true;
return;
}
return ( (fArchitecture == CPU_TYPE_ARM) && (fSubArchitecture == CPU_SUBTYPE_ARM_V7K) );
}
+void Options::selectFallbackArch(const char *arch)
+{
+ // Should have the format "desired_arch:fallback_arch", for example "arm64_32:armv7k" to allow an armv7k
+ // slice to substitute for arm64_32 if the latter isn't present.
+ if (const char* fallbackEnv = getenv("LD_DYLIB_ARCH_FALLBACK") ) {
+ std::string fallback(fallbackEnv);
+ auto delimPos = fallback.find(':');
+
+ // Check we've got a potentially valid fallback string and that it's this architecture we're falling back from.
+ if ( delimPos == std::string::npos || fallback.substr(0, delimPos) != arch )
+ return;
+
+ std::string fallbackTo = fallback.substr(delimPos + 1);
+ for (const ArchInfo *t = archInfoArray; t->archName != nullptr; ++t) {
+ if ( fallbackTo == t->archName ) {
+ fFallbackArchitecture = t->cpuType;
+ fFallbackSubArchitecture = t->cpuSubType;
+ }
+ }
+ }
+ else {
+ // <rdar://problem/39797337> let x86_64h fallback and use x86_64 slice
+ if ( (fArchitecture == CPU_TYPE_X86_64) && (fSubArchitecture == CPU_SUBTYPE_X86_64_H) ) {
+ fFallbackArchitecture = CPU_TYPE_X86_64;
+ fFallbackSubArchitecture = CPU_SUBTYPE_X86_ALL;
+ }
+ }
+}
+
void Options::parseArch(const char* arch)
{
if ( arch == NULL )
fSubArchitecture = t->cpuSubType;
fHasPreferredSubType = t->isSubType;
fArchSupportsThumb2 = t->supportsThumb2;
+ selectFallbackArch(arch);
return;
}
}
return result;
}
-void Options::addTAPIInterface(tapi::LinkerInterfaceFile *interface, const char *path) const {
+void Options::addTAPIInterface(tapi::LinkerInterfaceFile* interface, const char *path) const {
#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1))
if (tapi::APIVersion::isAtLeast(1, 3)) {
for (auto &name : interface->inlinedFrameworkNames()) {
return false;
}
-std::unique_ptr<tapi::LinkerInterfaceFile> Options::findTAPIFile(const std::string &path) const
+tapi::LinkerInterfaceFile* Options::findTAPIFile(const std::string &path) const
{
- std::unique_ptr<tapi::LinkerInterfaceFile> interface;
+#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1))
+ tapi::LinkerInterfaceFile* interface = nullptr;
std::string TBDPath;
// create parsing options.
if (!allowWeakImports())
flags |= tapi::ParsingFlags::DisallowWeakImports;
-
+
+ __block uint32_t linkMinOSVersion = 0;
+ //FIXME handle this correctly once we have multi-platform TAPI.
+ platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ if (linkMinOSVersion == 0)
+ linkMinOSVersion = version;
+ if (platform == ld::kPlatform_macOS)
+ linkMinOSVersion = version;
+ });
+
// Search through all the inlined framework.
for (const auto &dylib : fTAPIFiles) {
if (dylib.getInstallName() == path) {
// If the install name matches, parse the framework.
std::string errorMessage;
auto file = dylib.getInterfaceFile()->getInlinedFramework(path.c_str(), architecture(), subArchitecture(),
- flags, tapi::PackedVersion32(minOSversion()), errorMessage);
+ flags, tapi::PackedVersion32(linkMinOSVersion), errorMessage);
if (!file)
throw strdup(errorMessage.c_str());
if (!interface) {
// If this is the first inlined framework found, record the information.
- interface.reset(file);
+ interface = file;
TBDPath = dylib.getTAPIFilePath();
} else {
// If we found other inlined framework already, check to see if their versions are the same.
warning("Inlined framework/dylib mismatch: %s (%s and %s)", path.c_str(),
TBDPath.c_str(), dylib.getTAPIFilePath().c_str());
if (interface->getCurrentVersion() < file->getCurrentVersion()) {
- interface.reset(file);
+ interface = file;
TBDPath = dylib.getTAPIFilePath();
}
}
}
}
return interface;
+#else
+ return nullptr;
+#endif
}
// search for indirect dylib first using -F and -L paths first
return findFile(installName, fromDylib);
}
-
-
-void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth)
-{
- FILE* file = fopen(segAddrPath, "r");
- if ( file == NULL ) {
- warning("-seg_addr_table file cannot be read: %s", segAddrPath);
- return;
- }
-
- char path[PATH_MAX];
- uint64_t firstColumAddress = 0;
- uint64_t secondColumAddress = 0;
- bool hasSecondColumn = false;
- while ( fgets(path, PATH_MAX, file) != NULL ) {
- path[PATH_MAX-1] = '\0';
- char* eol = strchr(path, '\n');
- if ( eol != NULL )
- *eol = '\0';
- // ignore lines not starting with 0x number
- if ( (path[0] == '0') && (path[1] == 'x') ) {
- char* p;
- firstColumAddress = strtoull(path, &p, 16);
- while ( isspace(*p) )
- ++p;
- // see if second column is a number
- if ( (p[0] == '0') && (p[1] == 'x') ) {
- secondColumAddress = strtoull(p, &p, 16);
- hasSecondColumn = true;
- while ( isspace(*p) )
- ++p;
- }
- while ( isspace(*p) )
- ++p;
- if ( p[0] == '/' ) {
- // remove any trailing whitespace
- for(char* end = eol-1; (end > p) && isspace(*end); --end)
- *end = '\0';
- // see if this line is for the dylib being linked
- if ( strcmp(p, installPth) == 0 ) {
- fBaseAddress = firstColumAddress;
- if ( hasSecondColumn ) {
- fBaseWritableAddress = secondColumAddress;
- fSplitSegs = true;
- }
- break; // out of while loop
- }
- }
- }
- }
-
- fclose(file);
-}
-
void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdinal)
{
FILE* file;
return kInvalid;
}
-void Options::setMacOSXVersionMin(const char* version)
-{
- uint32_t value;
- if ( !parsePackedVersion32(version, value) ) {
- throwf("-macosx_version_min value malformed: '%s'", version);
- }
- fMacVersionMin = (ld::MacVersionMin)value;
- fPlatform = kPlatformOSX;
-}
-
-void Options::setIOSVersionMin(const char* version)
-{
- uint32_t value;
- if ( !parsePackedVersion32(version, value) ) {
- throwf("-ios_version_min value malformed: '%s'", version);
- }
- fIOSVersionMin = (ld::IOSVersionMin)value;
- fPlatform = kPlatformiOS;
-}
-
-
-void Options::setWatchOSVersionMin(const char* version)
-{
- uint32_t value;
- if ( !parsePackedVersion32(version, value) ) {
- throwf("-watchos_version_min value malformed: '%s'", version);
- }
- fWatchOSVersionMin = (ld::WatchOSVersionMin)value;
- fPlatform = kPlatformWatchOS;
-}
-
-
-bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IOSVersionMin requirediPhoneOSMin)
-{
- if ( fMacVersionMin != ld::macVersionUnset ) {
- return ( fMacVersionMin >= requiredMacMin );
- }
- else {
- return min_iOS(requirediPhoneOSMin);
- }
-}
-
-bool Options::min_iOS(ld::IOSVersionMin requirediOSMin)
-{
- if ( fWatchOSVersionMin != ld::wOSVersionUnset ) {
- // Hack until we fully track watch and ios versions seperately
- return ( (fWatchOSVersionMin + 0x00070000) >= requirediOSMin);
- }
- else if ( fPlatform == Options::kPlatform_bridgeOS ) {
- // Hack until we fully track bridge and ios versions seperately
- return ( (fIOSVersionMin + 0x00090000) >= requirediOSMin);
- }
- else {
- return ( fIOSVersionMin >= requirediOSMin );
- }
+void Options::setVersionMin(const ld::Platform& platform, const char *version) {
+ auto checkAndParse = [&](const char *errorFlag) {
+ if ( version == NULL )
+ throwf("%s missing version argument", errorFlag);
+ uint32_t value;
+ if ( !parsePackedVersion32(version, value) ) {
+ throwf("%s value malformed: '%s'", errorFlag, version);
+ }
+ fPlatforms.add({platform, value});
+ };
+
+ switch(platform) {
+ case ld::kPlatform_macOS: checkAndParse("-macosx_version_min"); break;
+ case ld::kPlatform_iOS: checkAndParse("-ios_version_min"); break;
+ case ld::kPlatform_tvOS: checkAndParse("-tvos_version_min"); break;
+ case ld::kPlatform_watchOS: checkAndParse("-watchos_version_min"); break;
+ case ld::kPlatform_bridgeOS: checkAndParse("-bridgeos_version_min"); break;
+ case ld::kPlatform_iOSSimulator: checkAndParse("-ios_simulator_version_min"); break;
+ case ld::kPlatform_tvOSSimulator: checkAndParse("-tvos_simulator_version_min"); break;
+ case ld::kPlatform_watchOSSimulator: checkAndParse("-watchos_simulator_version_min"); break;
+ case ld::kPlatform_unknown: throw "kPlatformUnknown is an invalid argument to setVersionMin()";
+ case ld::kPlatform_iOSMac: checkAndParse("-iosmac_version_min"); break;
+ };
}
void Options::setWeakReferenceMismatchTreatment(const char* treatment)
return false;
}
+bool Options::moveAXMethodList(const char* className) const
+{
+ for (const SymbolsMove& sm : fSymbolsMovesAXMethodLists) {
+ bool wildcard;
+ if ( sm.symbols.containsWithPrefix(className, NULL, wildcard) )
+ return true;
+ }
+ return false;
+}
+
bool Options::moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const
{
for (std::vector<SymbolsMove>::const_iterator it=fSymbolsMovesCode.begin(); it != fSymbolsMovesCode.end(); ++it) {
return false;
}
- result = (majorValue << 16) | (minorValue << 8) | microValue;
+ result = (uint32_t)((majorValue << 16) | (minorValue << 8) | microValue);
return true;
}
return getVersionString32(fSDKVersion);
}
-std::string Options::getPlatformStr() const
-{
- switch (fPlatform) {
- case Options::kPlatformOSX:
- return "MacOSX";
- case Options::kPlatformiOS:
- if (targetIOSSimulator())
- return "iPhoneSimulator";
- else
- return "iPhoneOS";
- case Options::kPlatformWatchOS:
- if (targetIOSSimulator())
- return "watchOS Simulator";
- else
- return "watchOS";
-#if SUPPORT_APPLE_TV
- case Options::kPlatform_tvOS:
- if (targetIOSSimulator())
- return "AppleTVSimulator";
- else
- return "AppleTVOS";
- break;
-#endif
- case Options::kPlatform_bridgeOS:
- return "bridgeOS";
- case Options::kPlatformUnknown:
- return "Unknown";
- }
-}
-
std::vector<std::string> Options::writeBitcodeLinkOptions() const
{
- std::vector<std::string> linkCommand;
+ __block std::vector<std::string> linkCommand;
switch ( fOutputKind ) {
case Options::kDynamicLibrary:
linkCommand.push_back("-dylib");
// Add deployment target.
// Platform is allowed to be unknown for "ld -r".
- switch (fPlatform) {
- case Options::kPlatformOSX:
- linkCommand.push_back("-macosx_version_min");
- linkCommand.push_back(getVersionString32((unsigned)fMacVersionMin));
- break;
- case Options::kPlatformiOS:
- if (targetIOSSimulator())
- linkCommand.push_back("-ios_simulator_version_min");
- else
+
+ platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ switch (platform) {
+ case ld::kPlatform_macOS:
+ linkCommand.push_back("-macosx_version_min");
+ linkCommand.push_back(getVersionString32(version));
+ break;
+ case ld::kPlatform_iOS:
linkCommand.push_back("-ios_version_min");
- linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin));
- break;
- case Options::kPlatformWatchOS:
- if (targetIOSSimulator())
- linkCommand.push_back("-watchos_simulator_version_min");
- else
+ linkCommand.push_back(getVersionString32(version));
+ break;
+ case ld::kPlatform_iOSSimulator:
+ linkCommand.push_back("-ios_simulator_version_min");
+ linkCommand.push_back(getVersionString32(version));
+ break;
+ case ld::kPlatform_watchOS:
linkCommand.push_back("-watchos_version_min");
- linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin));
- break;
-#if SUPPORT_APPLE_TV
- case Options::kPlatform_tvOS:
- if (targetIOSSimulator())
- linkCommand.push_back("-tvos_simulator_version_min");
- else
+ linkCommand.push_back(getVersionString32(version));
+ break;
+ case ld::kPlatform_watchOSSimulator:
+ linkCommand.push_back("-watchos_simulator_version_min");
+ linkCommand.push_back(getVersionString32(version));
+ break;
+ case ld::kPlatform_tvOS:
linkCommand.push_back("-tvos_version_min");
- linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin));
- break;
-#endif
- case Options::kPlatform_bridgeOS:
- linkCommand.push_back("-bridgeos_version_min");
- linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin));
- break;
- case Options::kPlatformUnknown:
- if ( fOutputKind != Options::kObjectFile ) {
- throwf("platform is unknown for final bitcode bundle,"
- "deployment target and min version is required for -bitcode_bundle");
- }
- break;
- }
+ linkCommand.push_back(getVersionString32(version));
+ break;
+ case ld::kPlatform_tvOSSimulator:
+ linkCommand.push_back("-tvos_simulator_version_min");
+ linkCommand.push_back(getVersionString32(version));
+ break;
+ case ld::kPlatform_bridgeOS:
+ linkCommand.push_back("-bridgeos_version_min");
+ linkCommand.push_back(getVersionString32(version));
+ break;
+ case ld::kPlatform_iOSMac:
+ linkCommand.push_back("-iosmac_version_min");
+ linkCommand.push_back(getVersionString32(version));
+ break;
+ case ld::kPlatform_unknown:
+ if ( fOutputKind != Options::kObjectFile ) {
+ throwf("platform is unknown for final bitcode bundle,"
+ "deployment target and min version is required for -bitcode_bundle");
+ }
+ break;
+ }
+ });
// entry name
return linkCommand;
}
+const char* Options::checkForNullArgument(const char* argument_name, const char* arg) const
+{
+ if ( arg == NULL )
+ throwf("missing argument for %s", argument_name);
+ return arg;
+}
+
+const char* Options::checkForNullVersionArgument(const char* argument_name, const char* arg) const
+{
+ if ( arg == NULL )
+ throwf("%s missing version argument", argument_name);
+ return arg;
+}
+
//
// Process all command line arguments.
//
exit (0);
}
else if ( strcmp(arg, "-arch") == 0 ) {
- parseArch(argv[++i]);
+ const char* arch = argv[++i];
+ parseArch(arch);
+ fLinkSnapshot.recordArch(arch);
}
else if ( strcmp(arg, "-dynamic") == 0 ) {
// default
fOutputKind = kKextBundle;
cannotBeUsedWithBitcode(arg);
}
+ else if ( strcmp(arg, "-kext_objects_dir") == 0 ) {
+ fKextObjectsDirPath = argv[++i];
+ if ( fKextObjectsDirPath == NULL )
+ throw "missing argument to -kext_objects_dir";
+ fKextObjectsEnable = 1;
+ }
+ else if ( strcmp(arg, "-no_kext_objects") == 0 ) {
+ fKextObjectsEnable = 0;
+ }
else if ( strcmp(arg, "-o") == 0 ) {
- snapshotArgCount = 0;
- fOutputFile = argv[++i];
- fLinkSnapshot.setSnapshotName(fOutputFile);
+ snapshotArgCount = 0;
+ fOutputFile = checkForNullArgument(arg, argv[++i]);
+ fLinkSnapshot.setOutputPath(fOutputFile);
}
else if ( strncmp(arg, "-lazy-l", 7) == 0 ) {
snapshotArgCount = 0;
if ( value == NULL )
throw "missing argument to -prune_interval_lto";
char* endptr;
+ fLtoPruneIntervalOverwrite = true;
fLtoPruneInterval = strtoul(value, &endptr, 10);
if ( *endptr != '\0')
throw "invalid argument for -prune_interval_lto";
}
// Similar to -all_load, but for the following archive only.
else if ( strcmp(arg, "-force_load") == 0 ) {
- FileInfo info = findFile(argv[++i]);
+ const char* path = checkForNullArgument(arg, argv[++i]);
+ FileInfo info = findFile(path);
info.options.fForceLoad = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
if ( fExportMode == kDontExportSome )
throw "can't use -exported_symbol and -unexported_symbols";
fExportMode = kExportSome;
- fExportSymbols.insert(argv[++i]);
+ const char* symbol = checkForNullArgument(arg, argv[++i]);
+ fExportSymbols.insert(symbol);
}
else if ( strcmp(arg, "-unexported_symbol") == 0 ) {
if ( fExportMode == kExportSome )
throw "can't use -unexported_symbol and -exported_symbol";
fExportMode = kDontExportSome;
- fDontExportSymbols.insert(argv[++i]);
+ const char* symbol = checkForNullArgument(arg, argv[++i]);
+ fDontExportSymbols.insert(symbol);
}
else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) {
snapshotFileArgIndex = 1;
else if ( strcmp(arg, "-weak_library") == 0 ) {
// SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
snapshotArgCount = 0;
- FileInfo info = findFile(argv[++i]);
+ const char* path = checkForNullArgument(arg, argv[++i]);
+ FileInfo info = findFile(path);
info.options.fWeakImport = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
else if ( strcmp(arg, "-lazy_library") == 0 ) {
// SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
snapshotArgCount = 0;
- FileInfo info = findFile(argv[++i]);
+ const char* path = checkForNullArgument(arg, argv[++i]);
+ FileInfo info = findFile(path);
info.options.fLazyLoad = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
// are prebound. This can then be fixed up by update_prebinding
// later. Prebinding is less useful on 10.4 and greater.
else if ( strcmp(arg, "-prebind") == 0 ) {
- fPrebind = true;
+ warnObsolete(arg);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-noprebind") == 0 ) {
warnObsolete(arg);
- fPrebind = false;
}
else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) {
warnObsolete(arg);
}
// ??? Deprecate when we deprecate split-seg.
else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) {
- fBaseAddress = parseAddress(argv[++i]);
+ warnObsolete(arg);
+ ++i;
cannotBeUsedWithBitcode(arg);
}
// ??? Deprecate when we deprecate split-seg.
else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) {
- fBaseWritableAddress = parseAddress(argv[++i]);
- fSplitSegs = true;
+ warnObsolete(arg);
+ ++i;
cannotBeUsedWithBitcode(arg);
}
// ??? Deprecate when we get rid of basing at build time.
else if ( strcmp(arg, "-seg_addr_table") == 0 ) {
+ warnObsolete(arg);
snapshotFileArgIndex = 1;
const char* name = argv[++i];
if ( name == NULL )
throw "-seg_addr_table missing argument";
- fSegAddrTablePath = name;
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) {
}
// 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* macVers = checkForNullVersionArgument(arg, argv[++i]);
const char* envMacVers = getenv("MACOSX_DEPLOYMENT_TARGET");
const char* enviPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET");
if ( (envMacVers != NULL) && (enviPhoneVers != NULL) ) {
const char* sysrootPath = fSDKPaths.back();
const char* lastSlash = strrchr(sysrootPath, '/');
if ( strstr(lastSlash, "Simulator") != NULL )
- setIOSVersionMin(enviPhoneVers);
+ setVersionMin(ld::kPlatform_iOS, enviPhoneVers);
else
- setMacOSXVersionMin(macVers);
+ setVersionMin(ld::kPlatform_macOS, macVers);
}
else {
- setMacOSXVersionMin(macVers);
+ setVersionMin(ld::kPlatform_macOS, macVers);
}
}
else {
- setMacOSXVersionMin(macVers);
+ setVersionMin(ld::kPlatform_macOS, macVers);
}
}
else if ( (strcmp(arg, "-ios_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) {
- const char* vers = argv[++i];
- if ( vers == NULL )
- throw "-ios_version_min missing version argument";
- setIOSVersionMin(vers);
+ const char* vers = checkForNullVersionArgument(arg, argv[++i]);
+ setVersionMin(ld::kPlatform_iOS, vers);
}
else if ( strcmp(arg, "-ios_simulator_version_min") == 0 ) {
- const char* vers = argv[++i];
- if ( vers == NULL )
- throw "-ios_simulator_version_min missing version argument";
- setIOSVersionMin(vers);
- fTargetIOSSimulator = true;
+ const char* vers = checkForNullVersionArgument(arg, argv[++i]);
+ setVersionMin(ld::kPlatform_iOSSimulator, vers);
}
else if ( strcmp(arg, "-watchos_version_min") == 0 ) {
- const char* vers = argv[++i];
- if ( vers == NULL )
- throw "-watchos_version_min missing version argument";
- setWatchOSVersionMin(vers);
+ const char* vers = checkForNullVersionArgument(arg, argv[++i]);
+ setVersionMin(ld::kPlatform_watchOS, vers);
}
else if ( strcmp(arg, "-watchos_simulator_version_min") == 0 ) {
- const char* vers = argv[++i];
- if ( vers == NULL )
- throw "-watchos_simulator_version_min missing version argument";
- setWatchOSVersionMin(vers);
- fTargetIOSSimulator = true;
+ const char* vers = checkForNullVersionArgument(arg, argv[++i]);
+ setVersionMin(ld::kPlatform_watchOSSimulator, vers);
}
- #if SUPPORT_APPLE_TV
else if ( strcmp(arg, "-tvos_version_min") == 0 ) {
- const char* vers = argv[++i];
- if ( vers == NULL )
- throw "-tvos_version_min missing version argument";
- setIOSVersionMin(vers);
- fPlatform = kPlatform_tvOS;
+ const char* vers = checkForNullVersionArgument(arg, argv[++i]);
+ setVersionMin(ld::kPlatform_tvOS, vers);
}
else if ( strcmp(arg, "-tvos_simulator_version_min") == 0 ) {
- const char* vers = argv[++i];
- if ( vers == NULL )
- throw "-tvos_simulator_version_min missing version argument";
- setIOSVersionMin(vers);
- fPlatform = kPlatform_tvOS;
- fTargetIOSSimulator = true;
- }
- #endif
+ const char* vers = checkForNullVersionArgument(arg, argv[++i]);
+ setVersionMin(ld::kPlatform_tvOSSimulator, vers);
+ }
else if ( strcmp(arg, "-bridgeos_version_min") == 0 ) {
- const char* vers = argv[++i];
- if ( vers == NULL )
- throw "-bridgeos_version_min missing version argument";
- setIOSVersionMin(vers);
- fPlatform = kPlatform_bridgeOS;
+ const char* vers = checkForNullVersionArgument(arg, argv[++i]);
+ setVersionMin(ld::kPlatform_bridgeOS, vers);
+ }
+ else if ( strcmp(arg, "-iosmac_version_min") == 0 ) {
+ const char* vers = checkForNullVersionArgument(arg, argv[++i]);
+ setVersionMin(ld::kPlatform_iOSMac, vers);
}
else if ( strcmp(arg, "-multiply_defined") == 0 ) {
//warnObsolete(arg);
}
else if ( strcmp(arg, "-alias") == 0 ) {
Options::AliasPair pair;
- pair.realName = argv[++i];
- if ( pair.realName == NULL )
- throw "missing argument to -alias";
- pair.alias = argv[++i];
- if ( pair.alias == NULL )
- throw "missing argument to -alias";
+ pair.realName = checkForNullArgument(arg, argv[++i]);
+ pair.alias = checkForNullArgument(arg, argv[++i]);
fAliases.push_back(pair);
cannotBeUsedWithBitcode(arg);
}
fVerifyBitcode = true;
}
else if ( strcmp(arg, "-bitcode_symbol_map") == 0) {
- fReverseMapPath = argv[++i];
- if ( fReverseMapPath == NULL )
- throw "missing argument to -bitcode_symbol_map";
+ fReverseMapPath = checkForNullArgument(arg, argv[++i]);
struct stat statbuf;
int ret = ::stat(fReverseMapPath, &statbuf);
if ( ret == 0 && S_ISDIR(statbuf.st_mode)) {
} else
fReverseMapTempPath = std::string(fReverseMapPath);
}
- else if ( strcmp(argv[i], "-flto-codegen-only") == 0) {
+ else if ( strcmp(arg, "-flto-codegen-only") == 0) {
fLTOCodegenOnly = true;
}
- else if ( strcmp(argv[i], "-ignore_auto_link") == 0) {
+ else if ( strcmp(arg, "-ignore_auto_link") == 0) {
fIgnoreAutoLink = true;
}
- else if ( strcmp(argv[i], "-allow_dead_duplicates") == 0) {
+ else if ( strcmp(arg, "-allow_dead_duplicates") == 0) {
fAllowDeadDups = true;
}
- else if ( strcmp(argv[i], "-bitcode_process_mode") == 0 ) {
- const char* bitcode_type = argv[++i];
- if ( bitcode_type == NULL )
- throw "missing argument to -bitcode_process_mode";
- else if ( strcmp(bitcode_type, "strip") == 0 )
+ else if ( strcmp(arg, "-bitcode_process_mode") == 0 ) {
+ const char* bitcode_type = checkForNullArgument(arg, argv[++i]);
+ if ( strcmp(bitcode_type, "strip") == 0 )
fBitcodeKind = kBitcodeStrip;
else if ( strcmp(bitcode_type, "marker") == 0 )
fBitcodeKind = kBitcodeMarker;
throw "unknown argument to -bitcode_process_mode {strip,marker,data,bitcode}";
}
else if ( strcmp(arg, "-rpath") == 0 ) {
- const char* path = argv[++i];
- if ( path == NULL )
- throw "missing argument to -rpath";
+ const char* path = checkForNullArgument(arg, argv[++i]);
fRPaths.push_back(path);
}
else if ( strcmp(arg, "-read_only_stubs") == 0 ) {
warnObsolete(arg);
}
else if ( strcmp(arg, "-map") == 0 ) {
- fMapPath = argv[++i];
- if ( fMapPath == NULL )
- throw "missing argument to -map";
+ fMapPath = checkForNullArgument(arg, argv[++i]);
}
else if ( strcmp(arg, "-pie") == 0 ) {
fPositionIndependentExecutable = true;
else if ( strcmp(arg, "-reexport_library") == 0 ) {
// SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
snapshotArgCount = 0;
- FileInfo info = findFile(argv[++i]);
+ const char* path = checkForNullArgument(arg, argv[++i]);
+ FileInfo info = findFile(path);
info.options.fReExport = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
else if ( strcmp(arg, "-upward_library") == 0 ) {
// SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now)
snapshotArgCount = 0;
- FileInfo info = findFile(argv[++i]);
+ const char* path = checkForNullArgument(arg, argv[++i]);
+ FileInfo info = findFile(path);
info.options.fUpward = true;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-mllvm") == 0 ) {
- const char* opts = argv[++i];
- if ( opts == NULL )
- throw "missing argument to -mllvm";
+ const char* opts = checkForNullArgument(arg, argv[++i]);
fLLVMOptions.push_back(opts);
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-mcpu") == 0 ) {
- const char* cpu = argv[++i];
- if ( cpu == NULL )
- throw "missing argument to -mcpu";
+ const char* cpu = checkForNullArgument(arg, argv[++i]);
fLtoCpu = cpu;
cannotBeUsedWithBitcode(arg);
}
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-objc_abi_version") == 0 ) {
- const char* version = argv[++i];
- if ( version == NULL )
- throw "-objc_abi_version missing version number";
+ const char* version = checkForNullVersionArgument(arg, argv[++i]);
if ( strcmp(version, "2") == 0 ) {
fObjCABIVersion1Override = false;
fObjCABIVersion2Override = true;
}
else if ( strcmp(arg, "-objc_gc") == 0 ) {
fObjCGc = true;
- if ( fObjCGcOnly ) {
+ if ( fObjCGcOnly ) {
warning("-objc_gc overriding -objc_gc_only");
- fObjCGcOnly = false;
+ fObjCGcOnly = false;
}
cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-objc_gc_only") == 0 ) {
fObjCGcOnly = true;
- if ( fObjCGc ) {
+ if ( fObjCGc ) {
warning("-objc_gc_only overriding -objc_gc");
- fObjCGc = false;
+ fObjCGc = false;
}
cannotBeUsedWithBitcode(arg);
}
fVersionLoadCommandForcedOn = true;
fVersionLoadCommandForcedOff = false;
}
- else if ( strcmp(arg, "-no_version_load_command") == 0 ) {
- fVersionLoadCommandForcedOff = true;
- fVersionLoadCommandForcedOn = false;
- cannotBeUsedWithBitcode(arg);
- }
else if ( strcmp(arg, "-function_starts") == 0 ) {
fFunctionStartsForcedOn = true;
fFunctionStartsForcedOff = false;
fDataInCodeInfoLoadCommandForcedOff = false;
}
else if ( strcmp(arg, "-object_path_lto") == 0 ) {
- fTempLtoObjectPath = argv[++i];
- if ( fTempLtoObjectPath == NULL )
- throw "missing argument to -object_path_lto";
+ fTempLtoObjectPath = checkForNullArgument(arg, argv[++i]);
}
else if ( strcmp(arg, "-no_objc_category_merging") == 0 ) {
fObjcCategoryMerging = false;
fSnapshotRequested = true;
cannotBeUsedWithBitcode(arg);
}
- else if ( strcmp(arg, "-new_main") == 0 ) {
- fEntryPointLoadCommandForceOn = true;
- cannotBeUsedWithBitcode(arg);
- }
- else if ( strcmp(arg, "-no_new_main") == 0 ) {
- fEntryPointLoadCommandForceOff = true;
- cannotBeUsedWithBitcode(arg);
- }
else if ( strcmp(arg, "-source_version") == 0 ) {
const char* vers = argv[++i];
if ( vers == NULL )
fKextsUseStubs = true;
cannotBeUsedWithBitcode(arg);
}
- else if ( strcmp(argv[i], "-dependency_info") == 0 ) {
+ else if ( strcmp(arg, "-dependency_info") == 0 ) {
snapshotArgCount = 0;
++i;
// previously handled by buildSearchPaths()
}
fMaxDefaultCommonAlign = alignment;
}
- else if ( strcmp(argv[i], "-no_weak_imports") == 0 ) {
+ else if ( strcmp(arg, "-no_weak_imports") == 0 ) {
fAllowWeakImports = false;
}
- else if ( strcmp(argv[i], "-no_inits") == 0 ) {
+ else if ( strcmp(arg, "-no_inits") == 0 ) {
fInitializersTreatment = Options::kError;
}
- else if ( strcmp(argv[i], "-no_warn_inits") == 0 ) {
+ else if ( strcmp(arg, "-no_warn_inits") == 0 ) {
fInitializersTreatment = Options::kSuppress;
}
+ else if ( strcmp(arg, "-threaded_starts_section") == 0 ) {
+ fMakeThreadedStartsSection = true;
+ }
+ else if (strcmp(arg, "-debug_variant") == 0) {
+ fDebugVariant = true;
+ }
+ else if (strcmp(arg, "-no_new_main") == 0) {
+ // HACK until 39514191 is fixed
+ }
// 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 (fSnapshotRequested)
fLinkSnapshot.createSnapshot();
-}
-
+ if ( kKextBundle == fOutputKind ) {
+ if ( fKextObjectsEnable < 0 )
+ fKextObjectsEnable = ((fArchitecture == CPU_TYPE_ARM64) || (fArchitecture == CPU_TYPE_ARM));
+
+ if (fKextObjectsEnable > 0) {
+ if ( !fKextObjectsDirPath ) {
+ const char* dstroot;
+ const char* objdir = getenv("LD_KEXT_OBJECTS_DIR");
+ if ( objdir )
+ fKextObjectsDirPath = strdup(objdir);
+ else if ( (dstroot = getenv("DSTROOT")) )
+ asprintf((char **)&fKextObjectsDirPath, "%s/AppleInternal/KextObjects", dstroot);
+ }
+ fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_KEXT);
+ fLinkSnapshot.createSnapshot();
+ }
+ }
+}
//
// -syslibroot <path> is used for SDK support.
fVerbose = true;
extern const char ldVersionString[];
fprintf(stderr, "%s", ldVersionString);
+ fprintf(stderr, "BUILD " __TIME__ " " __DATE__"\n");
fprintf(stderr, "configured to support archs: %s\n", ALL_SUPPORTED_ARCHS);
// if only -v specified, exit cleanly
if ( argc == 2 ) {
if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL)
fPrintOrderFileStatistics = true;
- if (getenv("LD_SPLITSEGS_NEW_LIBRARIES") != NULL)
- fSplitSegs = true;
-
if (getenv("LD_NO_ENCRYPT") != NULL) {
fEncryptable = false;
fMarkAppExtensionSafe = true; // temporary
// <rdar://problem/30746905> [Reproducible Builds] If env ZERO_AR_DATE is set, zero out timestamp in N_OSO stab
if ( getenv("ZERO_AR_DATE") != NULL )
fZeroModTimeInDebugMap = true;
+
+ char rawPath[PATH_MAX];
+ char path[PATH_MAX];
+ char *base;
+ uint32_t bufSize = PATH_MAX;
+ if ( _NSGetExecutablePath(rawPath, &bufSize) != -1 ) {
+ if ( realpath(rawPath, path) != NULL ) {
+#define TOOLCHAINBASEPATH "/Developer/Toolchains/"
+ if ( (base = strstr(path, TOOLCHAINBASEPATH)) )
+ fToolchainPath = strndup(path, base - path + strlen(TOOLCHAINBASEPATH));
+ }
+ }
+
+ // <rdar://problem/38679559> ld64 should consider RC_RELEASE when calculating a binary's UUID
+ fBuildContextName = getenv("RC_RELEASE");
}
fExecutablePath = fOutputFile;
}
- // allow build system to set default seg_addr_table
- if ( fSegAddrTablePath == NULL )
- fSegAddrTablePath = getenv("LD_SEG_ADDR_TABLE");
-
- // allow build system to turn on prebinding
- if ( !fPrebind ) {
- fPrebind = ( getenv("LD_PREBIND") != NULL );
- }
-
// allow build system to force on dead-code-stripping
if ( !fDeadStrip ) {
if ( getenv("LD_DEAD_STRIP") != NULL ) {
}
// set default min OS version
- if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fWatchOSVersionMin == ld::wOSVersionUnset) ) {
+ if ( platforms().empty() ) {
// if neither -macosx_version_min nor -iphoneos_version_min used, try environment variables
+ if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) )
+ warning("No version-min specified on command line");
const char* macVers = getenv("MACOSX_DEPLOYMENT_TARGET");
const char* iPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET");
const char* iOSVers = getenv("IOS_DEPLOYMENT_TARGET");
const char* wOSVers = getenv("WATCHOS_DEPLOYMENT_TARGET");
if ( macVers != NULL )
- setMacOSXVersionMin(macVers);
+ setVersionMin(ld::kPlatform_macOS, macVers);
else if ( iPhoneVers != NULL )
- setIOSVersionMin(iPhoneVers);
+ setVersionMin(ld::kPlatform_iOS, iPhoneVers);
else if ( iOSVers != NULL )
- setIOSVersionMin(iOSVers);
+ setVersionMin(ld::kPlatform_iOS, iOSVers);
else if ( wOSVers != NULL )
- setWatchOSVersionMin(wOSVers);
+ setVersionMin(ld::kPlatform_watchOS, wOSVers);
else {
// if still nothing, set default based on architecture
switch ( fArchitecture ) {
if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) {
#ifdef DEFAULT_MACOSX_MIN_VERSION
warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION);
- setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
+ setVersionMin(ld::kPlatform_macOS, DEFAULT_MACOSX_MIN_VERSION);
#else
warning("-macosx_version_min not specified, assuming 10.6");
- setMacOSXVersionMin("10.6");
- #endif
+ setVersionMin(ld::kPlatform_macOS, "10.6");
+ #endif
}
break;
case CPU_TYPE_ARM:
if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) {
#if defined(DEFAULT_IPHONEOS_MIN_VERSION)
warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION);
- setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION);
+ setVersionMin(ld::kPlatformiOS, DEFAULT_IPHONEOS_MIN_VERSION);
#else
if ( fSubArchitecture == CPU_SUBTYPE_ARM_V7K ) {
warning("-watchos_version_min not specified, assuming 2.0");
- setWatchOSVersionMin("2.0");
+ setVersionMin(ld::kPlatform_watchOS, "2.0");
}
else {
warning("-ios_version_min not specified, assuming 6.0");
- setIOSVersionMin("6.0");
+ setVersionMin(ld::kPlatform_iOS, "6.0");
}
#endif
}
}
}
-
+ __block ld::VersionSet platformOverrides;
// adjust min based on architecture
- switch ( fArchitecture ) {
- case CPU_TYPE_I386:
- if ( (fPlatform == kPlatformOSX) && (fMacVersionMin < ld::mac10_4) ) {
- //warning("-macosx_version_min should be 10.4 or later for i386");
- fMacVersionMin = ld::mac10_4;
- }
- break;
- case CPU_TYPE_X86_64:
- if ( (fPlatform == kPlatformOSX) && (fMacVersionMin < ld::mac10_4) ) {
- //warning("-macosx_version_min should be 10.4 or later for x86_64");
- fMacVersionMin = ld::mac10_4;
- }
- break;
- case CPU_TYPE_ARM64:
- if ( (fPlatform == kPlatformiOS) && (fIOSVersionMin < ld::iOS_7_0) ) {
- //warning("-mios_version_min should be 7.0 or later for arm64");
- fIOSVersionMin = ld::iOS_7_0;
- }
- break;
- }
-
+ platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ if ( (fArchitecture == CPU_TYPE_I386 || fArchitecture == CPU_TYPE_X86_64)
+ && platform == ld::kPlatform_macOS && !platforms().minOS(ld::mac10_4) ) {
+ platformOverrides.add(ld::mac10_4);
+ } else if (fArchitecture == CPU_TYPE_ARM64 && platform == ld::kPlatform_iOS
+ && !platforms().minOS(ld::iOS_7_0)) {
+ platformOverrides.add(ld::iOS_7_0);
+ }
+ });
+
+ // Insert the overrides into fPlatfroms
+ platformOverrides.forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ fPlatforms.add({platform, version});
+ });
+
// default to adding functions start for dynamic code, static code must opt-in
switch ( fOutputKind ) {
case Options::kPreload:
fUndefinedTreatment = kUndefinedDynamicLookup;
break;
case CPU_TYPE_ARM:
- if ( min_iOS(ld::iOS_5_0) ) {
- // iOS 5.0 and later use new MH_KEXT_BUNDLE type
- fMakeCompressedDyldInfo = false;
- fMakeCompressedDyldInfoForceOff = true;
- // kexts are PIC in iOS 6.0 and later
- fAllowTextRelocs = !min_iOS(ld::iOS_6_0);
- fKextsUseStubs = !fAllowTextRelocs;
- fUndefinedTreatment = kUndefinedDynamicLookup;
- break;
- }
- // else use object file
+ fMakeCompressedDyldInfo = false;
+ fMakeCompressedDyldInfoForceOff = true;
+ fAllowTextRelocs = false;
+ fKextsUseStubs = !fAllowTextRelocs;
+ fUndefinedTreatment = kUndefinedDynamicLookup;
+ break;
case CPU_TYPE_I386:
// use .o files
fOutputKind = kObjectFile;
// disable implicit dylibs when targeting 10.3
// <rdar://problem/5451987> add option to disable implicit load commands for indirectly used public dylibs
- if ( !minOS(ld::mac10_4, ld::iOS_2_0) )
+ if ( !platforms().minOS(ld::version2007) )
fImplicitlyLinkPublicDylibs = false;
-
- // allow build system to force linker to ignore -prebind
- if ( getenv("LD_FORCE_NO_PREBIND") != NULL )
- fPrebind = false;
-
- // allow build system to force linker to ignore -seg_addr_table
- if ( getenv("LD_FORCE_NO_SEG_ADDR_TABLE") != NULL )
- fSegAddrTablePath = NULL;
-
- // check for base address specified externally
- if ( (fSegAddrTablePath != NULL) && (fOutputKind == Options::kDynamicLibrary) ) {
- parseSegAddrTable(fSegAddrTablePath, this->installPath());
- // HACK to support seg_addr_table entries that are physical paths instead of install paths
- if ( fBaseAddress == 0 ) {
- if ( strcmp(this->installPath(), "/usr/lib/libstdc++.6.dylib") == 0 ) {
- parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.4.dylib");
- if ( fBaseAddress == 0 )
- parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.9.dylib");
- }
-
- else if ( strcmp(this->installPath(), "/usr/lib/libz.1.dylib") == 0 )
- parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libz.1.2.3.dylib");
-
- else if ( strcmp(this->installPath(), "/usr/lib/libutil.dylib") == 0 )
- parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libutil1.0.dylib");
- }
- }
-
- // split segs only allowed for dylibs
- if ( fSplitSegs ) {
- // split seg only supported for i386, and arm.
- switch ( fArchitecture ) {
- case CPU_TYPE_I386:
- if ( fOutputKind != Options::kDynamicLibrary )
- fSplitSegs = false;
- // make sure read and write segments are proper distance apart
- if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x10000000) )
- fBaseWritableAddress = fBaseAddress + 0x10000000;
- break;
- case CPU_TYPE_ARM:
- if ( fOutputKind != Options::kDynamicLibrary ) {
- fSplitSegs = false;
- }
- else {
- // make sure read and write segments are proper distance apart
- if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x08000000) )
- fBaseWritableAddress = fBaseAddress + 0x08000000;
- }
- break;
- default:
- fSplitSegs = false;
- fBaseAddress = 0;
- fBaseWritableAddress = 0;
- }
- }
-
// set too-large size
switch ( fArchitecture ) {
case CPU_TYPE_I386:
break;
}
- // <rdar://problem/6138961> -r implies no prebinding for all architectures
- if ( fOutputKind == Options::kObjectFile )
- fPrebind = false;
-
- // disable prebinding depending on arch and min OS version
- if ( fPrebind ) {
- switch ( fArchitecture ) {
- case CPU_TYPE_I386:
- if ( fMacVersionMin == ld::mac10_4 ) {
- // in 10.4 only split seg dylibs are prebound
- if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs )
- fPrebind = false;
- }
- else if ( fMacVersionMin >= ld::mac10_5 ) {
- // in 10.5 nothing is prebound
- fPrebind = false;
- }
- else if ( fIOSVersionMin != ld::iOSVersionUnset ) {
- // nothing in simulator is prebound
- fPrebind = false;
- }
- else {
- // in 10.3 and earlier only dylibs and main executables could be prebound
- switch ( fOutputKind ) {
- case Options::kDynamicExecutable:
- case Options::kDynamicLibrary:
- // only main executables and dylibs can be prebound
- break;
- case Options::kStaticExecutable:
- case Options::kDynamicBundle:
- case Options::kObjectFile:
- case Options::kDyld:
- case Options::kPreload:
- case Options::kKextBundle:
- // disable prebinding for everything else
- fPrebind = false;
- break;
- }
- }
- break;
- case CPU_TYPE_X86_64:
- fPrebind = false;
- break;
- case CPU_TYPE_ARM:
- switch ( fOutputKind ) {
- case Options::kDynamicExecutable:
- case Options::kDynamicLibrary:
- // only main executables and dylibs can be prebound
- break;
- case Options::kStaticExecutable:
- case Options::kDynamicBundle:
- case Options::kObjectFile:
- case Options::kDyld:
- case Options::kPreload:
- case Options::kKextBundle:
- // disable prebinding for everything else
- fPrebind = false;
- break;
- }
- break;
- }
- }
-
- // only prebound images can be split-seg
- if ( fSplitSegs && !fPrebind )
- fSplitSegs = false;
-
// determine if info for shared region should be added
if ( fOutputKind == Options::kDynamicLibrary ) {
- if ( minOS(ld::mac10_5, ld::iOS_3_1) )
- if ( !fPrebind && !fSharedRegionEligibleForceOff )
+ if ( platforms().minOS(ld::version2008Fall) )
+ if ( !fSharedRegionEligibleForceOff )
if ( sharedCacheEligiblePath(this->installPath()) )
fSharedRegionEligible = true;
}
fSharedRegionEligible = true;
}
- // automatically use __DATA_CONST in iOS dylibs
- if ( fSharedRegionEligible && minOS(ld::mac10_Future, ld::iOS_9_0) && !fUseDataConstSegmentForceOff && !fTargetIOSSimulator) {
+ // automatically use __DATA_CONST in dylibs on all platforms except macOS
+ if ( fSharedRegionEligible && !fUseDataConstSegmentForceOff
+ && !platforms().contains(ld::kPlatform_macOS) && !targetIOSSimulator()) {
fUseDataConstSegment = true;
}
if ( fUseDataConstSegmentForceOn ) {
fUseDataConstSegment = true;
}
// A -kext for iOS 10 ==> -data_const, -text_exec, -add_split_seg_info
- if ( (fOutputKind == Options::kKextBundle) && minOS(ld::mac10_Future, ld::iOS_10_0) && (fArchitecture == CPU_TYPE_ARM64) ) {
+ if ( (fOutputKind == Options::kKextBundle) && (fArchitecture == CPU_TYPE_ARM64) ) {
fUseDataConstSegment = true;
fUseTextExecSegment = true;
fSharedRegionEligible = true;
}
if ( fUseDataConstSegment ) {
addSectionRename("__DATA", "__got", "__DATA_CONST", "__got");
+
+#if SUPPORT_ARCH_arm64e
+ addSectionRename("__DATA", "__auth_got", "__DATA_CONST", "__auth_got");
+#endif
addSectionRename("__DATA", "__la_symbol_ptr", "__DATA_CONST", "__la_symbol_ptr");
addSectionRename("__DATA", "__nl_symbol_ptr", "__DATA_CONST", "__nl_symbol_ptr");
addSectionRename("__DATA", "__const", "__DATA_CONST", "__const");
}
// Use V2 shared cache info when targetting newer OSs
- if ( fSharedRegionEligible && minOS(ld::mac10_12, ld::iOS_9_0)) {
+ if ( fSharedRegionEligible && platforms().minOS(ld::supportsSplitSegV2)) {
fSharedRegionEncodingV2 = true;
- if ( fPlatform == kPlatformOSX ) {
+ if ( platforms().contains(ld::kPlatform_macOS) ) {
fSharedRegionEncodingV2 = false;
// <rdar://problem/24772435> only use v2 for Swift dylibs on Mac OS X
if ( strncmp(this->installPath(), "/System/Library/PrivateFrameworks/Swift/", 40) == 0 )
fIgnoreOptimizationHints = true;
}
- // figure out if module table is needed for compatibility with old ld/dyld
- if ( fOutputKind == Options::kDynamicLibrary ) {
- switch ( fArchitecture ) {
- case CPU_TYPE_I386:
- if ( fIOSVersionMin != ld::iOSVersionUnset ) // simulator never needs modules
- break;
- case CPU_TYPE_ARM:
- if ( fPrebind )
- fNeedsModuleTable = true; // redo_prebinding requires a module table
- break;
- }
- }
-
// <rdar://problem/5366363> -r -x implies -S
if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) )
fDebugInfoStripping = Options::kDebugInfoNone;
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
// <rdar://problem/16293398> Add LC_ENCRYPTION_INFO load command to bundled frameworks
- if ( !min_iOS(ld::iOS_7_0) )
+ if ( !platforms().minOS(ld::version2013) )
fEncryptable = false;
break;
}
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
break;
+ case Options::kDyld:
+ // arm64e has support for compressed LINKEDIT.
+ if ( (fArchitecture == CPU_TYPE_ARM64) && (fSubArchitecture == CPU_SUBTYPE_ARM64_E) )
+ break;
case Options::kPreload:
case Options::kStaticExecutable:
case Options::kObjectFile:
- case Options::kDyld:
case Options::kKextBundle:
fMakeCompressedDyldInfoForceOff = true;
break;
}
- if ( fMakeCompressedDyldInfoForceOff )
- fMakeCompressedDyldInfo = false;
-
- // only use compressed LINKEDIT for:
- // Mac OS X 10.6 or later
- // iOS 3.1 or later
- if ( fMakeCompressedDyldInfo ) {
- if ( !minOS(ld::mac10_6, ld::iOS_3_1) )
- fMakeCompressedDyldInfo = false;
- }
+ // only use legacy LINKEDIT if compressed LINKEDIT is forced off of:
+ // macOS before 10.6
+ // iOS before 3.1
+ if ( fMakeCompressedDyldInfoForceOff || !platforms().minOS(ld::version2009) )
+ fMakeCompressedDyldInfo = false;
// only ARM and x86_64 enforces that cpu-sub-types must match
switch ( fArchitecture ) {
case CPU_TYPE_ARM:
+ case CPU_TYPE_ARM64:
break;
case CPU_TYPE_X86_64:
fEnforceDylibSubtypesMatch = false;
break;
case CPU_TYPE_I386:
- case CPU_TYPE_ARM64:
fEnforceDylibSubtypesMatch = false;
break;
}
}
// Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB
- if ( minOS(ld::mac10_5, ld::iOS_2_0) )
+ if ( platforms().minOS(ld::version2008) )
fUseSimplifiedDylibReExports = true;
// Mac OS X 10.7 and iOS 4.2 support LC_LOAD_UPWARD_DYLIB
- if ( minOS(ld::mac10_7, ld::iOS_4_2) && (fOutputKind == kDynamicLibrary) )
+ if ( platforms().minOS(ld::version2010) && (fOutputKind == kDynamicLibrary) )
fCanUseUpwardDylib = true;
-
+
+ if (fArchitecture == CPU_TYPE_ARM64) {
+#if SUPPORT_ARCH_arm64e
+ if (fSubArchitecture == CPU_SUBTYPE_ARM64_E)
+ {
+ // FIXME: Move some of these to arm64
+ fNoLazyBinding = true;
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kDyld:
+ fUseLinkedListBinding = true;
+ fUseAuthenticatedStubs = true;
+ break;
+ case Options::kPreload:
+ case Options::kStaticExecutable:
+ case Options::kObjectFile:
+ case Options::kKextBundle:
+ break;
+ }
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kDyld:
+ case Options::kDynamicLibrary:
+ case Options::kObjectFile:
+ case Options::kDynamicBundle:
+ case Options::kKextBundle:
+ fSupportsAuthenticatedPointers = true;
+ break;
+ case Options::kStaticExecutable:
+ case Options::kPreload:
+ fSupportsAuthenticatedPointers = false;
+ break;
+ }
+ }
+#endif
+ }
+
+ if ( fMakeThreadedStartsSection && (fArchitecture != CPU_TYPE_ARM64) ) {
+ // Threaded starts isn't valid here so ignore it.
+ warning("-threaded_starts_section ignored ignored for non-arm64");
+ fMakeThreadedStartsSection = false;
+ }
+
+ if (fMakeThreadedStartsSection) {
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kDyld:
+ case Options::kDynamicLibrary:
+ case Options::kObjectFile:
+ case Options::kDynamicBundle:
+ case Options::kKextBundle:
+ // Threaded starts isn't valid here so ignore it.
+ warning("-threaded_starts_section ignored for binaries other than -static or -preload");
+ fMakeThreadedStartsSection = false;
+ break;
+ case Options::kStaticExecutable:
+ case Options::kPreload:
+ fUseLinkedListBinding = true;
+ fNoLazyBinding = true;
+#if SUPPORT_ARCH_arm64e
+ if ( (fArchitecture == CPU_TYPE_ARM64) && (fSubArchitecture == CPU_SUBTYPE_ARM64_E) )
+ fSupportsAuthenticatedPointers = true;
+#endif
+ break;
+ }
+ }
+
+ // Weak binding requires that if we want to use linked list binding, we must
+ // also be using no lazy binding.
+ if ( fUseLinkedListBinding )
+ assert(fNoLazyBinding);
+
// MacOSX 10.7 defaults to PIE
if ( (fArchitecture == CPU_TYPE_I386)
&& (fOutputKind == kDynamicExecutable)
- && (fMacVersionMin >= ld::mac10_7) ) {
+ && platforms().minOS(ld::mac10_7) ) {
fPositionIndependentExecutable = true;
}
// armv7 for iOS4.3 defaults to PIE
if ( (fArchitecture == CPU_TYPE_ARM)
&& fArchSupportsThumb2
- && (fOutputKind == kDynamicExecutable)
- && min_iOS(ld::iOS_4_3) ) {
+ && (fOutputKind == kDynamicExecutable)
+ && (platforms().contains(ld::kPlatform_watchOS) || platforms().minOS(ld::iOS_4_3)) ) {
fPositionIndependentExecutable = true;
}
- // <rdar://problem/24535196> x86_64 defaults PIE (regardless of minOS version)
- if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_6) )
+ // <rdar://problem/24535196> x86_64 defaults PIE (for 10.6 and later)
+ if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) && (platforms().minOS(ld::mac10_6) || platforms().contains(ld::kPlatform_iOSMac)) )
fPositionIndependentExecutable = true;
// Simulator defaults to PIE
- if ( fTargetIOSSimulator && (fOutputKind == kDynamicExecutable) )
+ if ( targetIOSSimulator() && (fOutputKind == kDynamicExecutable) )
fPositionIndependentExecutable = true;
// -no_pie anywhere on command line disable PIE
break;
}
- // let linker know if thread local variables are supported
- if ( fMacVersionMin >= ld::mac10_7 ) {
- fTLVSupport = true;
- }
- else if ( ((fArchitecture == CPU_TYPE_ARM64)
- )
- && min_iOS(ld::iOS_8_0) ) {
- fTLVSupport = true;
- }
- 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) ) {
+ // Let linker know if thread local variables are supported
+ // This is more complex than normal version checks since
+ // runtime support varies by architecture
+ if (platforms().minOS(ld::supportsTLV)
+ || ((fArchitecture == CPU_TYPE_ARM64) && platforms().minOS(ld::iOS_8_0))
+ || ((fArchitecture == CPU_TYPE_X86_64) && platforms().minOS(ld::iOS_9_0))) {
fTLVSupport = true;
}
}
// support re-export of individual symbols in MacOSX 10.7 and iOS 4.2
- if ( (fOutputKind == kDynamicLibrary) && minOS(ld::mac10_7, ld::iOS_4_2) )
+ if ( (fOutputKind == kDynamicLibrary) && platforms().minOS(ld::version2010) )
fCanReExportSymbols = true;
// ObjC optimization is only in dynamic final linked images
// Use LC_MAIN instead of LC_UNIXTHREAD for newer OSs
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
- if ( fEntryPointLoadCommandForceOn ) {
+ // <rdar://problem/16310363> Linker should look for "_main" not "start" when building for sim regardless of min OS
+ if ( platforms().minOS(ld::version2012) ) {
fEntryPointLoadCommand = true;
- if ( fEntryName == NULL )
+ if ( fEntryName == NULL )
fEntryName = "_main";
+ if ( strcmp(fEntryName, "start") == 0 ) {
+ warning("Ignoring '-e start' because entry point 'start' is not used for the targeted OS version");
+ fEntryName = "_main";
+ }
}
- else if ( fEntryPointLoadCommandForceOff ) {
+ else {
fNeedsThreadLoadCommand = true;
- if ( fEntryName == NULL )
+ if ( fEntryName == NULL )
fEntryName = "start";
}
- else {
- // <rdar://problem/16310363> Linker should look for "_main" not "start" when building for sim regardless of min OS
- if ( minOS(ld::mac10_8, ld::iOS_6_0) || fTargetIOSSimulator ) {
- fEntryPointLoadCommand = true;
- if ( fEntryName == NULL )
- fEntryName = "_main";
- if ( strcmp(fEntryName, "start") == 0 ) {
- warning("Ignoring '-e start' because entry point 'start' is not used for the targeted OS version");
- fEntryName = "_main";
- }
- }
- else {
- fNeedsThreadLoadCommand = true;
- if ( fEntryName == NULL )
- fEntryName = "start";
- }
- }
break;
case Options::kObjectFile:
case Options::kKextBundle:
fSourceVersionLoadCommand = false;
}
else {
- if ( minOS(ld::mac10_8, ld::iOS_6_0) ) {
+ if ( platforms().minOS(ld::version2012) ) {
fSourceVersionLoadCommand = true;
}
else
fSDKVersion = parseVersionNumber32(sdkVersionStr);
}
}
-
+
// if -sdk_version and -syslibroot not used, but targeting MacOSX, use current OS version
- if ( (fSDKVersion == 0) && (fMacVersionMin != ld::macVersionUnset) ) {
+ if ( (fSDKVersion == 0) && platforms().contains(ld::kPlatform_macOS) ) {
// special case if RC_ProjectName and MACOSX_DEPLOYMENT_TARGET are both set that sdkversion=minos
if ( getenv("RC_ProjectName") && getenv("MACOSX_DEPLOYMENT_TARGET") ) {
- fSDKVersion = fMacVersionMin;
+ fSDKVersion = platforms().minOS(ld::kPlatform_macOS);
}
else {
int mib[2] = { CTL_KERN, KERN_OSRELEASE };
// allow trie based absolute symbols if targeting new enough OS
if ( fMakeCompressedDyldInfo ) {
- if ( minOS(ld::mac10_9, ld::iOS_7_0) ) {
+ if ( platforms().minOS(ld::version2013) ) {
fAbsoluteSymbols = true;
}
}
-
- // <rdar://problem/12959510> iOS main executables now default to 16KB page size
- if ( (fIOSVersionMin != ld::iOSVersionUnset) && (fOutputKind == Options::kDynamicExecutable) ) {
- // <rdar://problem/13070042> Only third party apps should have 16KB page segments by default
- if ( fEncryptable ) {
- if ( fSegmentAlignment == 4096 )
- fSegmentAlignment = 4096*4;
- }
+
+ // <rdar://problem/13070042> Only third party apps should have 16KB page segments by default
+ if ( fEncryptable ) {
+ if ( fSegmentAlignment == 4096 )
+ fSegmentAlignment = 4096*4;
}
// <rdar://problem/12258065> ARM64 needs 16KB page size for user land code
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
case Options::kDyld:
- if ( ((fArchitecture == CPU_TYPE_ARM64)
- )
- || ((fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_7_0)) ) {
+ // <rdar://problem/14676611> 16KB segments for arm64 kexts
+ if ( (fArchitecture == CPU_TYPE_ARM64)
+ || (fArchitecture == CPU_TYPE_ARM) ) {
fSegmentAlignment = 4096*4;
}
break;
case Options::kStaticExecutable:
case Options::kKextBundle:
// <rdar://problem/14676611> 16KB segments for arm64 kexts
- if ( ((fArchitecture == CPU_TYPE_ARM64)
- )
- && min_iOS(ld::iOS_9_0) ) {
+ if ( (fArchitecture == CPU_TYPE_ARM64)
+ ) {
fSegmentAlignment = 4096*4;
}
break;
fKeepDwarfUnwind = false;
}
else {
- if ( minOS(ld::mac10_9, ld::iOS_7_0) )
+ if ( platforms().minOS(ld::version2013) )
fKeepDwarfUnwind = false;
else
fKeepDwarfUnwind = true;
}
+ // Look in $SDKROOT/AppleInternal/AccessibilityLinkerSymbols/<dylib>.axsymbols for objc-class names
+ if ( fUseDataConstSegment && (fDylibInstallName != NULL) && !fSDKPaths.empty() ) {
+ const char* dylibLeaf = strrchr(fDylibInstallName, '/');
+ if ( dylibLeaf ) {
+ char path[PATH_MAX];
+ strlcpy(path , fSDKPaths.front(), sizeof(path));
+ strlcat(path , "/AppleInternal/AccessibilityLinkerSymbols", sizeof(path));
+ strlcat(path , dylibLeaf, sizeof(path));
+ strlcat(path , ".axsymbols", sizeof(path));
+ FileInfo info;
+ if ( info.checkFileExists(*this, path) ) {
+ SymbolsMove tmp;
+ fSymbolsMovesAXMethodLists.push_back(tmp);
+ loadExportFile(path, ".axsymbols", fSymbolsMovesAXMethodLists.back().symbols);
+ }
+ }
+ }
+
// <rdar://problem/32138080> Automatically use OrderFiles found in the AppleInternal SDK
if ( (fFinalName != NULL) && fOrderedSymbols.empty() && !fSDKPaths.empty() ) {
char path[PATH_MAX];
}
// Add warnings for issues likely to cause OS verification issues
- if ( fSharedRegionEligible && !fRPaths.empty() ) {
+ if ( fSharedRegionEligible && !fRPaths.empty() && !fDebugVariant ) {
// <rdar://problem/18719327> warn if -rpath is used with OS dylibs
warning("OS dylibs should not add rpaths (linker option: -rpath) (Xcode build setting: LD_RUNPATH_SEARCH_PATHS)");
}
- if ( (fOutputKind == Options::kDynamicLibrary) && (fDylibInstallName != NULL) && (fFinalName != NULL) && sharedCacheEligiblePath(fFinalName) ) {
+ if ( (fOutputKind == Options::kDynamicLibrary) && (fDylibInstallName != NULL) && (fFinalName != NULL) && sharedCacheEligiblePath(fFinalName) && !fDebugVariant ) {
if ( strncmp(fDylibInstallName, "@rpath", 6) == 0 )
warning("OS dylibs should not use @rpath for -install_name. Use absolute path instead");
if ( strcmp(fDylibInstallName, fFinalName) != 0 ) {
}
// set if unaligned pointers are warnings or errors
- if ( fMacVersionMin >= ld::mac10_12 ) {
+ if ( platforms().minOS(ld::mac10_12) ) {
// ignore unaligned pointers when targeting older macOS versions
if ( fSharedRegionEligible )
fUnalignedPointerTreatment = Options::kUnalignedPointerWarning;
else
fUnalignedPointerTreatment = Options::kUnalignedPointerIgnore;
}
- else if ( min_iOS(ld::iOS_10_0) ) {
- fUnalignedPointerTreatment = Options::kUnalignedPointerWarning;
+ else if ( platforms().minOS(ld::iOS_10_0) ) {
+#if SUPPORT_ARCH_arm64e
+ if ( (fArchitecture == CPU_TYPE_ARM64) && (fSubArchitecture == CPU_SUBTYPE_ARM64_E) ) {
+ fUnalignedPointerTreatment = Options::kUnalignedPointerError;
+ } else
+#endif
+ fUnalignedPointerTreatment = Options::kUnalignedPointerWarning;
}
else {
fUnalignedPointerTreatment = Options::kUnalignedPointerIgnore;
// warn by default for OS dylibs
if ( fInitializersTreatment == Options::kInvalid ) {
- if ( fSharedRegionEligible && (fOutputKind == Options::kDynamicLibrary) ) {
+ if ( fSharedRegionEligible && (fOutputKind == Options::kDynamicLibrary) && !fDebugVariant ) {
fInitializersTreatment = Options::kWarning;
- // TEMP HACK
- if ( (fOutputKind == Options::kDynamicLibrary) && (fDylibInstallName != NULL) && (strstr(fDylibInstallName, "EmbeddedAcousticRecognition.framework") != NULL) && !fNoWeakExports )
- fInitializersTreatment = Options::kSuppress;
}
else
fInitializersTreatment = Options::kSuppress;
case kUndefinedError:
// always legal
break;
- case kUndefinedDynamicLookup:
- switch (fPlatform) {
- case kPlatformOSX:
- break;
- case kPlatformiOS:
- case kPlatformWatchOS:
- case kPlatform_bridgeOS:
- #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 kUndefinedDynamicLookup: {
+ platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ switch (platform) {
+ case ld::kPlatform_macOS:
+ case ld::kPlatform_iOSMac:
+ case ld::kPlatform_unknown:
+ break;
+ case ld::kPlatform_iOS:
+ case ld::kPlatform_iOSSimulator:
+ case ld::kPlatform_watchOS:
+ case ld::kPlatform_watchOSSimulator:
+ case ld::kPlatform_bridgeOS:
+ case ld::kPlatform_tvOS:
+ case ld::kPlatform_tvOSSimulator:
+ if ( fOutputKind != kKextBundle )
+ warning("-undefined dynamic_lookup is deprecated on %s", platformName(platform));
+ break;
+ }
+ });
+ } break;
case kUndefinedWarning:
case kUndefinedSuppress:
// requires flat namespace
// sync reader options
if ( fNameSpace != kTwoLevelNameSpace ) {
fFlatNamespace = true;
- switch (fPlatform) {
- case kPlatformOSX:
- break;
- case kPlatformiOS:
- case kPlatformWatchOS:
- case kPlatform_bridgeOS:
- #if SUPPORT_APPLE_TV
- case Options::kPlatform_tvOS:
- #endif
- warning("-flat_namespace is deprecated on %s", platformName(fPlatform));
- break;
- default:
- break;
- }
+ platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ switch (platform) {
+ case ld::kPlatform_unknown:
+ case ld::kPlatform_macOS:
+ case ld::kPlatform_iOSMac:
+ break;
+ case ld::kPlatform_iOS:
+ case ld::kPlatform_iOSSimulator:
+ case ld::kPlatform_watchOS:
+ case ld::kPlatform_watchOSSimulator:
+ case ld::kPlatform_bridgeOS:
+ case ld::kPlatform_tvOS:
+ case ld::kPlatform_tvOSSimulator:
+ warning("-flat_namespace is deprecated on %s", platformName(platform));
+ break;
+ }
+ });
+
}
if ( fStackSize != 0 ) {
switch (fArchitecture) {
case CPU_TYPE_I386:
- if ( fPlatform == kPlatformOSX ) {
+ if ( platforms().contains(ld::kPlatform_macOS) ) {
if ( fStackSize > 0xFFFFFFFF )
throw "-stack_size must be < 4GB for 32-bit processes";
if ( fStackAddr == 0 )
throw "-stack_addr must be < 0x20000000 for arm";
break;
case CPU_TYPE_X86_64:
- if ( fPlatform == kPlatformOSX ) {
+ if ( platforms().contains(ld::kPlatform_macOS) ) {
if ( fStackSize > 0x10000000000 )
throw "-stack_size must be <= 1TB";
if ( fStackAddr == 0 ) {
throw "-setuid_safe cannot be used with -r";
// <rdar://problem/12781832> compiler driver no longer uses -objc_abi_version, it uses -ios_simulator_version_min instead
- if ( !fObjCABIVersion1Override && !fObjCABIVersion2Override && fTargetIOSSimulator )
+ if ( !fObjCABIVersion1Override && !fObjCABIVersion2Override && targetIOSSimulator() )
fObjCABIVersion2Override = true;
// rdar://problem/4718189 map ObjC class names to new runtime names
// can't use -rpath unless targeting 10.5 or later
if ( fRPaths.size() > 0 ) {
- if ( !minOS(ld::mac10_5, ld::iOS_2_0) )
+ if ( !platforms().minOS(ld::version2008) )
throw "-rpath can only be used when targeting Mac OS X 10.5 or later";
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
// check -pie is only used when building a dynamic main executable for 10.5
- if ( !minOS(ld::mac10_5, ld::iOS_4_2) ) {
- if ( fIOSVersionMin == ld::iOSVersionUnset )
- throw "-pie can only be used when targeting Mac OS X 10.5 or later";
- else
- throw "-pie can only be used when targeting iOS 4.2 or later";
+ if ( !platforms().minOS(ld::supportsPIE) ) {
+ throw "-pie requires targetting a newer minimum version";
}
break;
case Options::kStaticExecutable:
}
}
- // check -read_only_relocs is not used with x86_64
+ // check -read_only_relocs is not used with x86_64 or arm64
if ( fAllowTextRelocs ) {
if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind != kKextBundle) ) {
warning("-read_only_relocs cannot be used with x86_64");
fAllowTextRelocs = false;
}
+ else if ( fArchitecture == CPU_TYPE_ARM64 ) {
+ warning("-read_only_relocs cannot be used with arm64");
+ }
}
// check -mark_auto_dead_strip is only used with dylibs
if ( !fReExportSymbols.empty() ) {
if ( fOutputKind != Options::kDynamicLibrary )
throw "-reexported_symbols_list can only used used when created dynamic libraries";
- if ( !minOS(ld::mac10_7, ld::iOS_4_2) )
+ if ( !platforms().minOS(ld::version2010) )
throw "targeted OS version does not support -reexported_symbols_list";
}
// <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?
- if ( (fOutputKind == Options::kDynamicLibrary) && (fIOSVersionMin != ld::iOSVersionUnset) && (fDylibInstallName != NULL) ) {
- if ( !min_iOS(ld::iOS_8_0) && (fDylibInstallName[0] == '@') && !fEncryptableForceOff )
+ if ( (fOutputKind == Options::kDynamicLibrary) && (fDylibInstallName != NULL) ) {
+ if ( platforms().contains(ld::kPlatform_iOS) && !platforms().minOS(ld::iOS_8_0) && (fDylibInstallName[0] == '@') && !fEncryptableForceOff )
warning("embedded dylibs/frameworks only run on iOS 8 or later");
}
- // bridgeOS always generates new load command
- if ( fVersionLoadCommand && (fPlatform == kPlatform_bridgeOS) ) {
- fBuildVersionLoadCommand = true;
- }
-
// produce nicer error when no input
if ( fInputFiles.empty() ) {
throw "no object files specified";
}
-}
+
+ // Check zippered combinations.
+ if (platforms().count() > 2) {
+ throw "Illegal platform count. Only 2 platforms at a maximum can be specified";
+ }
+
+ // Convert from -ios_version_min to -ios_simulator_version_min for now until clang has been updated
+ if (architecture() == CPU_TYPE_X86_64 || architecture() == CPU_TYPE_X86) {
+ if (platforms().contains(ld::kPlatform_iOS)) {
+ uint32_t version = platforms().minOS(ld::kPlatform_iOS);
+ fPlatforms.erase(ld::kPlatform_iOS);
+ // HACK infer the build environment from the SDK path
+ bool macOSSDK = false;
+ for (const auto& sdkPath : fSDKPaths) {
+ if (strstr(sdkPath, "MacOSX10") != 0) {
+ macOSSDK = true;
+ break;
+ }
+ }
+ if (macOSSDK) {
+ fPlatforms.add({ ld::kPlatform_iOSMac, version });
+ warning("URGENT: -ios_version_min is invalid for architecture x86/x86_64, inferring -iosmac_version_min. "
+ "This will be an error in the future.");
+ } else {
+ fPlatforms.add({ ld::kPlatform_iOSSimulator, version });
+ warning("URGENT: -ios_version_min is invalid for architecture x86/x86_64, inferring -ios_simulator_version_min. "
+ "This will be an error in the future.");
+ }
+ }
+ }
+
+ if (platforms().count() == 2) {
+ if (!platforms().minOS(ld::mac10_14))
+ throw "Zippered macOS version platform must be at least 10.14";
+ if (!platforms().minOS(ld::iOS_12_0))
+ throw "Zippered iosmac version platform must be at least 12.0";
+ }
+
+ if (platforms().contains(ld::kPlatform_iOSMac) && !platforms().minOS(ld::iOS_12_0)) {
+ throw "iosmac platform version must be at least 12.0";
+ }
+
+ // <rdar://problem/38155581> ld64 shouldn't allow final executables to have more than one version load command
+ if ( platforms().contains(ld::kPlatform_iOSMac) && platforms().contains(ld::kPlatform_macOS) ) {
+ if ( (fOutputKind != Options::kDynamicLibrary) && (fOutputKind != Options::kDynamicBundle) ) {
+ warning("Only dylibs and bundles can be zippered, changing output to be macOS only.");
+ fPlatforms.erase(ld::kPlatform_iOSMac);
+ }
+ }
+
+ // <rdar://problem/39095109> Linker warning for i386 macOS binaries
+ if ( (architecture() == CPU_TYPE_I386) && platforms().contains(ld::kPlatform_macOS) ) {
+ bool internalSDK = false;
+ for (const char* sdkPath : fSDKPaths) {
+ std::string possiblePath = std::string(sdkPath) + "/AppleInternal/";
+ struct stat statBuffer;
+ if ( stat(possiblePath.c_str(), &statBuffer) == 0 ) {
+ internalSDK = true;
+ break;
+ }
+ }
+ if ( !internalSDK )
+ warning("The i386 architecture is deprecated for macOS (remove from the Xcode build setting: ARCHS)");
+ }
+}
void Options::checkForClassic(int argc, const char* argv[])
#if DEMANGLE_SWIFT
// only try to demangle symbols that look like Swift symbols
- if ( strncmp(sym, "__T", 3) == 0 ) {
+ if ( strncmp(sym, "_$", 2) == 0 ) {
size_t demangledSize = fnd_get_demangled_name(&sym[1], buff, size);
if ( demangledSize > size ) {
size = demangledSize+2;
enum BitcodeMode { kBitcodeProcess, kBitcodeAsData, kBitcodeMarker, kBitcodeStrip };
enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull };
enum UnalignedPointerTreatment { kUnalignedPointerError, kUnalignedPointerWarning, kUnalignedPointerIgnore };
-#if SUPPORT_APPLE_TV
- enum Platform { kPlatformUnknown, kPlatformOSX=1, kPlatformiOS=2, kPlatformWatchOS=3, kPlatform_tvOS=4, kPlatform_bridgeOS=5 };
-#else
- enum Platform { kPlatformUnknown, kPlatformOSX, kPlatformiOS, kPlatformWatchOS };
-#endif
- static Platform platformForLoadCommand(uint32_t lc) {
+ static ld::Platform platformForLoadCommand(uint32_t lc, bool useSimulatorVariant) {
switch (lc) {
case LC_VERSION_MIN_MACOSX:
- return kPlatformOSX;
+ return ld::kPlatform_macOS;
case LC_VERSION_MIN_IPHONEOS:
- return kPlatformiOS;
+ return useSimulatorVariant ? ld::kPlatform_iOSSimulator : ld::kPlatform_iOS;
case LC_VERSION_MIN_WATCHOS:
- return kPlatformWatchOS;
- #if SUPPORT_APPLE_TV
+ return useSimulatorVariant ? ld::kPlatform_watchOSSimulator : ld::kPlatform_watchOS;
case LC_VERSION_MIN_TVOS:
- return kPlatform_tvOS;
- #endif
+ return useSimulatorVariant ? ld::kPlatform_tvOSSimulator : ld::kPlatform_tvOS;
}
assert(!lc && "unknown LC_VERSION_MIN load command");
- return kPlatformUnknown;
+ return ld::kPlatform_unknown;
}
- static const char* platformName(Platform platform) {
+ static const char* platformName(ld::Platform platform) {
switch (platform) {
- case kPlatformOSX:
- return "OSX";
- case kPlatformiOS:
- return "iOS";
- case kPlatformWatchOS:
- return "watchOS";
- #if SUPPORT_APPLE_TV
- case kPlatform_tvOS:
- return "tvOS";
- #endif
- case kPlatform_bridgeOS:
- return "bridgeOS";
- case kPlatformUnknown:
- default:
- return "(unknown)";
+ case ld::kPlatform_macOS: return "OSX";
+ case ld::kPlatform_iOS: return "iOS";
+ case ld::kPlatform_iOSSimulator: return "iOS Simulator";
+ case ld::kPlatform_iOSMac: return "iOSMac";
+ case ld::kPlatform_watchOS: return "watchOS";
+ case ld::kPlatform_watchOSSimulator: return "watchOS Simulator";
+ case ld::kPlatform_tvOS: return "tvOS";
+ case ld::kPlatform_tvOSSimulator: return "tvOS Simulator";
+ case ld::kPlatform_bridgeOS: return "bridgeOS";
+ case ld::kPlatform_unknown: return "(unknown)";
}
}
class TAPIInterface {
public:
- TAPIInterface(tapi::LinkerInterfaceFile *file, const char* path, const char *installName) :
+ TAPIInterface(tapi::LinkerInterfaceFile* file, const char* path, const char *installName) :
_file(file), _tbdPath(path), _installName(installName) {}
- tapi::LinkerInterfaceFile *getInterfaceFile() const { return _file; }
+ tapi::LinkerInterfaceFile* getInterfaceFile() const { return _file; }
std::string getTAPIFilePath() const { return _tbdPath; }
const std::string &getInstallName() const { return _installName; }
private:
- tapi::LinkerInterfaceFile *_file;
+ tapi::LinkerInterfaceFile* _file;
std::string _tbdPath;
std::string _installName;
};
cpu_type_t architecture() const { return fArchitecture; }
bool preferSubArchitecture() const { return fHasPreferredSubType; }
cpu_subtype_t subArchitecture() const { return fSubArchitecture; }
+ cpu_type_t fallbackArchitecture() const { return fFallbackArchitecture; }
+ cpu_subtype_t fallbackSubArchitecture() const { return fFallbackSubArchitecture; }
bool allowSubArchitectureMismatches() const { return fAllowCpuSubtypeMismatches; }
bool enforceDylibSubtypesMatch() const { return fEnforceDylibSubtypesMatch; }
bool warnOnSwiftABIVersionMismatches() const { return fWarnOnSwiftABIVersionMismatches; }
bool forceCpuSubtypeAll() const { return fForceSubtypeAll; }
const char* architectureName() const { return fArchitectureName; }
- void setArchitecture(cpu_type_t, cpu_subtype_t subtype, Options::Platform platform);
+ void setArchitecture(cpu_type_t, cpu_subtype_t subtype, ld::Platform platform, uint32_t minOsVers);
bool archSupportsThumb2() const { return fArchSupportsThumb2; }
OutputKind outputKind() const { return fOutputKind; }
- bool prebind() const { return fPrebind; }
bool bindAtLoad() const { return fBindAtLoad; }
NameSpace nameSpace() const { return fNameSpace; }
const char* installPath() const; // only for kDynamicLibrary
uint64_t baseAddress() const { return fBaseAddress; }
uint64_t maxAddress() const { return fMaxAddress; }
bool keepPrivateExterns() const { return fKeepPrivateExterns; } // only for kObjectFile
- bool needsModuleTable() const { return fNeedsModuleTable; } // only for kDynamicLibrary
bool interposable(const char* name) const;
bool hasExportRestrictList() const { return (fExportMode != kExportDefault); } // -exported_symbol or -unexported_symbol
bool hasExportMaskList() const { return (fExportMode == kExportSome); } // just -exported_symbol
bool traceEmitJSON() const { return fTraceEmitJSON; }
bool deadCodeStrip() const { return fDeadStrip; }
UndefinedTreatment undefinedTreatment() const { return fUndefinedTreatment; }
- ld::MacVersionMin macosxVersionMin() const { return fMacVersionMin; }
- ld::IOSVersionMin iOSVersionMin() const { return fIOSVersionMin; }
- ld::WatchOSVersionMin watchOSVersionMin() const { return fWatchOSVersionMin; }
- uint32_t minOSversion() const;
- bool minOS(ld::MacVersionMin mac, ld::IOSVersionMin iPhoneOS);
- bool min_iOS(ld::IOSVersionMin requirediOSMin);
+ uint32_t minOSversion(const ld::Platform& platform) const;
bool messagesPrefixedWithArchitecture();
Treatment picTreatment();
WeakReferenceMismatchTreatment weakReferenceMismatchTreatment() const { return fWeakReferenceMismatchTreatment; }
FileInfo findFile(const std::string &path, const ld::dylib::File* fromDylib=nullptr) const;
bool findFile(const std::string &path, const std::vector<std::string> &tbdExtensions, FileInfo& result) const;
bool hasInlinedTAPIFile(const std::string &path) const;
- std::unique_ptr<tapi::LinkerInterfaceFile> findTAPIFile(const std::string &path) const;
+ tapi::LinkerInterfaceFile* findTAPIFile(const std::string &path) const;
UUIDMode UUIDMode() const { return fUUIDMode; }
bool warnStabs();
bool pauseAtEnd() { return fPause; }
unsigned long orderedSymbolsCount() const { return fOrderedSymbols.size(); }
OrderedSymbolsIterator orderedSymbolsBegin() const { return &fOrderedSymbols[0]; }
OrderedSymbolsIterator orderedSymbolsEnd() const { return &fOrderedSymbols[fOrderedSymbols.size()]; }
- bool splitSeg() const { return fSplitSegs; }
uint64_t baseWritableAddress() { return fBaseWritableAddress; }
uint64_t segmentAlignment() const { return fSegmentAlignment; }
uint64_t segPageSize(const char* segName) const;
const std::vector<const char*>& dyldEnvironExtras() const{ return fDyldEnvironExtras; }
const std::vector<const char*>& astFilePaths() const{ return fASTFilePaths; }
bool makeCompressedDyldInfo() const { return fMakeCompressedDyldInfo; }
+ bool makeThreadedStartsSection() const { return fMakeThreadedStartsSection; }
bool hasExportedSymbolOrder();
bool exportedSymbolOrder(const char* sym, unsigned int* order) const;
bool orderData() { return fOrderData; }
bool objcGc() const { return fObjCGc; }
bool objcGcOnly() const { return fObjCGcOnly; }
bool canUseThreadLocalVariables() const { return fTLVSupport; }
- bool addVersionLoadCommand() const { return fVersionLoadCommand && (fPlatform != kPlatformUnknown); }
- bool addBuildVersionLoadCommand() const { return fBuildVersionLoadCommand; }
+ bool addVersionLoadCommand() const { return fVersionLoadCommand && (platforms().count() != 0); }
bool addFunctionStarts() const { return fFunctionStartsLoadCommand; }
bool addDataInCodeInfo() const { return fDataInCodeInfoLoadCommand; }
bool canReExportSymbols() const { return fCanReExportSymbols; }
const char* ltoCachePath() const { return fLtoCachePath; }
+ bool ltoPruneIntervalOverwrite() const { return fLtoPruneIntervalOverwrite; }
int ltoPruneInterval() const { return fLtoPruneInterval; }
int ltoPruneAfter() const { return fLtoPruneAfter; }
unsigned ltoMaxCacheSize() const { return fLtoMaxCacheSize; }
const char* tempLtoObjectPath() const { return fTempLtoObjectPath; }
const char* overridePathlibLTO() const { return fOverridePathlibLTO; }
const char* mcpuLTO() const { return fLtoCpu; }
+ const char* kextObjectsPath() const { return fKextObjectsDirPath; }
+ int kextObjectsEnable() const { return fKextObjectsEnable; }
+ const char* toolchainPath() const { return fToolchainPath; }
bool objcCategoryMerging() const { return fObjcCategoryMerging; }
bool pageAlignDataAtoms() const { return fPageAlignDataAtoms; }
bool keepDwarfUnwind() const { return fKeepDwarfUnwind; }
bool renameReverseSymbolMap() const { return fReverseMapUUIDRename; }
bool deduplicateFunctions() const { return fDeDupe; }
bool verboseDeduplicate() const { return fVerboseDeDupe; }
+ bool useLinkedListBinding() const { return fUseLinkedListBinding; }
+#if SUPPORT_ARCH_arm64e
+ bool useAuthenticatedStubs() const { return fUseAuthenticatedStubs; }
+ bool supportsAuthenticatedPointers() const { return fSupportsAuthenticatedPointers; }
+#endif
+ bool noLazyBinding() const { return fNoLazyBinding; }
+ bool debugVariant() const { return fDebugVariant; }
const char* reverseSymbolMapPath() const { return fReverseMapPath; }
std::string reverseMapTempPath() const { return fReverseMapTempPath; }
bool ltoCodegenOnly() const { return fLTOCodegenOnly; }
const char* pipelineFifo() const { return fPipelineFifo; }
bool dumpDependencyInfo() const { return (fDependencyInfoPath != NULL); }
const char* dependencyInfoPath() const { return fDependencyInfoPath; }
- bool targetIOSSimulator() const { return fTargetIOSSimulator; }
- ld::relocatable::File::LinkerOptionsList&
+ bool targetIOSSimulator() const { return platforms().contains(ld::simulatorPlatforms); }
+ ld::relocatable::File::LinkerOptionsList&
linkerOptions() const { return fLinkerOptions; }
FileInfo findFramework(const char* frameworkName) const;
FileInfo findLibrary(const char* rootName, bool dylibsOnly=false) const;
const std::vector<SegmentRename>& segmentRenames() const { return fSegmentRenames; }
bool moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const;
bool moveRwSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const;
- Platform platform() const { return fPlatform; }
+ bool moveAXMethodList(const char* className) const;
+ const ld::VersionSet& platforms() const { return fPlatforms; }
const std::vector<const char*>& sdkPaths() const { return fSDKPaths; }
std::vector<std::string> writeBitcodeLinkOptions() const;
std::string getSDKVersionStr() const;
- std::string getPlatformStr() const;
uint8_t maxDefaultCommonAlign() const { return fMaxDefaultCommonAlign; }
bool hasDataSymbolMoves() const { return !fSymbolsMovesData.empty(); }
bool hasCodeSymbolMoves() const { return !fSymbolsMovesCode.empty(); }
bool zeroModTimeInDebugMap() const { return fZeroModTimeInDebugMap; }
void writeDependencyInfo() const;
std::vector<TAPIInterface> &TAPIFiles() { return fTAPIFiles; }
- void addTAPIInterface(tapi::LinkerInterfaceFile *interface, const char *path) const;
+ void addTAPIInterface(tapi::LinkerInterfaceFile* interface, const char *path) const;
+ const char* buildContextName() const { return fBuildContextName; }
static uint32_t parseVersionNumber32(const char*);
std::string path;
};
+ const char* checkForNullArgument(const char* argument_name, const char* arg) const;
+ const char* checkForNullVersionArgument(const char* argument_name, const char* arg) const;
void parse(int argc, const char* argv[]);
void checkIllegalOptionCombinations();
void buildSearchPaths(int argc, const char* argv[]);
void parseArch(const char* architecture);
+ void selectFallbackArch(const char *architecture);
FileInfo findFramework(const char* rootName, const char* suffix) const;
bool checkForFile(const char* format, const char* dir, const char* rootName,
FileInfo& result) const;
void parsePreCommandLineEnvironmentSettings();
void parsePostCommandLineEnvironmentSettings();
void setUndefinedTreatment(const char* treatment);
- void setMacOSXVersionMin(const char* version);
- void setIOSVersionMin(const char* version);
- void setWatchOSVersionMin(const char* version);
+ void setVersionMin(const ld::Platform& platform, const char *version);
void setWeakReferenceMismatchTreatment(const char* treatment);
void addDylibOverride(const char* paths);
void addSectionAlignment(const char* segment, const char* section, const char* alignment);
std::vector<Options::FileInfo> fInputFiles;
cpu_type_t fArchitecture;
cpu_subtype_t fSubArchitecture;
+ cpu_type_t fFallbackArchitecture;
+ cpu_subtype_t fFallbackSubArchitecture;
const char* fArchitectureName;
OutputKind fOutputKind;
bool fHasPreferredSubType;
bool fArchSupportsThumb2;
- bool fPrebind;
bool fBindAtLoad;
bool fKeepPrivateExterns;
- bool fNeedsModuleTable;
bool fIgnoreOtherArchFiles;
bool fErrorOnOtherArchFiles;
bool fForceSubtypeAll;
uint64_t fBaseAddress;
uint64_t fMaxAddress;
uint64_t fBaseWritableAddress;
- bool fSplitSegs;
SetWithWildcards fExportSymbols;
SetWithWildcards fDontExportSymbols;
SetWithWildcards fInterposeList;
const char* fExecutablePath;
const char* fBundleLoader;
const char* fDtraceScriptName;
- const char* fSegAddrTablePath;
const char* fMapPath;
const char* fDyldInstallPath;
const char* fLtoCachePath;
+ bool fLtoPruneIntervalOverwrite;
int fLtoPruneInterval;
int fLtoPruneAfter;
unsigned fLtoMaxCacheSize;
const char* fTempLtoObjectPath;
const char* fOverridePathlibLTO;
const char* fLtoCpu;
+ int fKextObjectsEnable;
+ const char* fKextObjectsDirPath;
+ const char* fToolchainPath;
uint64_t fZeroPageSize;
uint64_t fStackSize;
uint64_t fStackAddr;
bool fMarkDeadStrippableDylib;
bool fMakeCompressedDyldInfo;
bool fMakeCompressedDyldInfoForceOff;
+ bool fMakeThreadedStartsSection;
bool fNoEHLabels;
bool fAllowCpuSubtypeMismatches;
bool fEnforceDylibSubtypesMatch;
bool fVersionLoadCommand;
bool fVersionLoadCommandForcedOn;
bool fVersionLoadCommandForcedOff;
- bool fBuildVersionLoadCommand;
bool fFunctionStartsLoadCommand;
bool fFunctionStartsForcedOn;
bool fFunctionStartsForcedOff;
bool fPageAlignDataAtoms;
bool fNeedsThreadLoadCommand;
bool fEntryPointLoadCommand;
- bool fEntryPointLoadCommandForceOn;
- bool fEntryPointLoadCommandForceOff;
bool fSourceVersionLoadCommand;
bool fSourceVersionLoadCommandForceOn;
bool fSourceVersionLoadCommandForceOff;
- bool fTargetIOSSimulator;
bool fExportDynamic;
bool fAbsoluteSymbols;
bool fAllowSimulatorToLinkWithMacOSX;
bool fReverseMapUUIDRename;
bool fDeDupe;
bool fVerboseDeDupe;
+ bool fUseLinkedListBinding;
+#if SUPPORT_ARCH_arm64e
+ bool fUseAuthenticatedStubs = false;
+ bool fSupportsAuthenticatedPointers = false;
+#endif
+ bool fNoLazyBinding;
+ bool fDebugVariant;
const char* fReverseMapPath;
std::string fReverseMapTempPath;
bool fLTOCodegenOnly;
Treatment fInitializersTreatment;
bool fZeroModTimeInDebugMap;
BitcodeMode fBitcodeKind;
- Platform fPlatform;
DebugInfoStripping fDebugInfoStripping;
const char* fTraceOutputFile;
- ld::MacVersionMin fMacVersionMin;
- ld::IOSVersionMin fIOSVersionMin;
- ld::WatchOSVersionMin fWatchOSVersionMin;
+ ld::VersionSet fPlatforms;
std::vector<AliasPair> fAliases;
std::vector<const char*> fInitialUndefines;
NameSet fAllowedUndefined;
std::vector<SegmentRename> fSegmentRenames;
std::vector<SymbolsMove> fSymbolsMovesData;
std::vector<SymbolsMove> fSymbolsMovesCode;
+ std::vector<SymbolsMove> fSymbolsMovesAXMethodLists;
bool fSaveTempFiles;
mutable Snapshot fLinkSnapshot;
bool fSnapshotRequested;
const char* fPipelineFifo;
const char* fDependencyInfoPath;
+ const char* fBuildContextName;
mutable int fTraceFileDescriptor;
uint8_t fMaxDefaultCommonAlign;
UnalignedPointerTreatment fUnalignedPointerTreatment;
symbolTableSection(NULL), stringPoolSection(NULL),
localRelocationsSection(NULL), externalRelocationsSection(NULL),
sectionRelocationsSection(NULL),
- indirectSymbolTableSection(NULL),
+ indirectSymbolTableSection(NULL),
+ threadedPageStartsSection(NULL),
_options(opts),
_hasDyldInfo(opts.makeCompressedDyldInfo()),
+ _hasThreadedPageStarts(opts.makeThreadedStartsSection()),
_hasSymbolTable(true),
_hasSectionRelocations(opts.outputKind() == Options::kObjectFile),
_hasSplitSegInfo(opts.sharedRegionEligible()),
_hasFunctionStartsInfo(opts.addFunctionStarts()),
_hasDataInCodeInfo(opts.addDataInCodeInfo()),
_hasDynamicSymbolTable(true),
- _hasLocalRelocations(!opts.makeCompressedDyldInfo()),
- _hasExternalRelocations(!opts.makeCompressedDyldInfo()),
+ _hasLocalRelocations(!opts.makeCompressedDyldInfo() && !opts.makeThreadedStartsSection()),
+ _hasExternalRelocations(!opts.makeCompressedDyldInfo() && !opts.makeThreadedStartsSection()),
_hasOptimizationHints(opts.outputKind() == Options::kObjectFile),
_encryptedTEXTstartOffset(0),
_encryptedTEXTendOffset(0),
_sectionsRelocationsAtom->encode();
}
- if ( ! _options.makeCompressedDyldInfo() ) {
+ if ( !_options.makeCompressedDyldInfo() && !_options.makeThreadedStartsSection() ) {
// build external relocations
assert(_externalRelocsAtom != NULL);
_externalRelocsAtom->encode();
// work backwards from end of segment and lay out sections so that extra room goes to padding atom
uint64_t addr = 0;
uint64_t textSegPageSize = _options.segPageSize("__TEXT");
- if ( _options.sharedRegionEligible() && (_options.iOSVersionMin() >= ld::iOS_8_0) && (textSegPageSize == 0x4000) )
+ if ( _options.sharedRegionEligible() && _options.platforms().minOS(ld::iOS_8_0) && (textSegPageSize == 0x4000) )
textSegPageSize = 0x1000;
for (std::vector<ld::Internal::FinalSection*>::reverse_iterator it = state.sections.rbegin(); it != state.sections.rend(); ++it) {
ld::Internal::FinalSection* sect = *it;
uint64_t OutputFile::addressOf(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target)
{
- if ( !_options.makeCompressedDyldInfo() ) {
+ // FIXME: Is this right for makeThreadedStartsSection?
+ if ( !_options.makeCompressedDyldInfo() && !_options.makeThreadedStartsSection() ) {
// For external relocations the classic mach-o format
// has addend only stored in the content. That means
// that the address of the target is not used.
bool is_b;
bool thumbTarget = false;
std::map<uint32_t, const Fixup*> usedByHints;
+#if SUPPORT_ARCH_arm64e
+ Fixup::AuthData authData;
+#endif
for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
uint8_t* fixUpLocation = &buffer[fit->offsetInAtom];
ld::Fixup::LOH_arm64 lohExtra;
accumulator = 0;
set64LE(fixUpLocation, accumulator);
break;
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64: {
+ accumulator = addressOf(state, fit, &toTarget);
+ if ( fit->contentAddendOnly ) {
+ // ld -r mode. We want to write out the original relocation again
+
+ // FIXME: Should we zero out the accumulator here as done in kindStoreTargetAddressLittleEndian64?
+ // Make sure the high bits aren't set. The low-32-bits should be the addend.
+ assert((accumulator & 0xFFFFFFFF00000000ULL) == 0);
+ accumulator |= ((uint64_t)authData.discriminator) << 32;
+ accumulator |= ((uint64_t)authData.hasAddressDiversity) << 48;
+ accumulator |= ((uint64_t)authData.key) << 49;
+ // Set the high bit as we are authenticated
+ accumulator |= 1ULL << 63;
+ set64LE(fixUpLocation, accumulator);
+ }
+ else if (_options.outputKind() == Options::kKextBundle ) {
+ // kexts dont' handle auth pointers, write unauth pointer
+ set64LE(fixUpLocation, accumulator);
+ }
+ else {
+ auto fixupOffset = (uintptr_t)(fixUpLocation - mhAddress);
+ assert(_authenticatedFixupData.find(fixupOffset) == _authenticatedFixupData.end());
+ auto authneticatedData = std::make_pair(authData, accumulator);
+ _authenticatedFixupData[fixupOffset] = authneticatedData;
+ // Zero out this entry which we will expect later.
+ set64LE(fixUpLocation, 0);
+ }
+ break;
+ }
+ case ld::Fixup::kindStoreLittleEndianAuth64: {
+ if ( fit->contentAddendOnly ) {
+ // ld -r mode. We want to write out the original relocation again
+
+ // FIXME: Should we zero out the accumulator here as done in kindStoreTargetAddressLittleEndian64?
+ // Make sure the high bits aren't set. The low-32-bits should be the addend.
+ assert((accumulator & 0xFFFFFFFF00000000ULL) == 0);
+ accumulator |= ((uint64_t)authData.discriminator) << 32;
+ accumulator |= ((uint64_t)authData.hasAddressDiversity) << 48;
+ accumulator |= ((uint64_t)authData.key) << 49;
+ // Set the high bit as we are authenticated
+ accumulator |= 1ULL << 63;
+ set64LE(fixUpLocation, accumulator);
+ }
+ else if (_options.outputKind() == Options::kKextBundle ) {
+ // kexts dont' handle auth pointers, write unauth pointer
+ set64LE(fixUpLocation, accumulator);
+ }
+ else {
+ auto fixupOffset = (uintptr_t)(fixUpLocation - mhAddress);
+ assert(_authenticatedFixupData.find(fixupOffset) == _authenticatedFixupData.end());
+ auto authneticatedData = std::make_pair(authData, accumulator);
+ _authenticatedFixupData[fixupOffset] = authneticatedData;
+ // Zero out this entry which we will expect later.
+ set64LE(fixUpLocation, 0);
+ }
+ break;
+ }
+ case ld::Fixup::kindSetAuthData:
+ authData = fit->u.authData;
+ break;
+#endif
case ld::Fixup::kindStoreTargetAddressBigEndian32:
accumulator = addressOf(state, fit, &toTarget);
if ( fit->contentAddendOnly )
}
break;
case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12:
+ // In -r mode, the GOT doesn't exist but the relocations track it
+ // so the address doesn't need to be aligned.
+ if ( _options.outputKind() == Options::kObjectFile )
+ break;
case ld::Fixup::kindStoreTargetAddressARM64PageOff12:
case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12:
accumulator = addressOf(state, fit, &toTarget);
//fprintf(stderr, "ADRPs changed to NOPs: %d\n", sAdrpNoped);
//fprintf(stderr, "ADRPs unchanged: %d\n", sAdrpNotNoped);
}
+
+ if ( _options.makeThreadedStartsSection() ) {
+ assert(_threadedRebaseBindIndices.empty());
+
+ std::vector<OutputFile::BindingInfo>& bindInfo = _bindingInfo;
+ std::vector<OutputFile::RebaseInfo>& rebaseInfo = _rebaseInfo;
+
+ std::vector<int64_t>& threadedRebaseBindIndices = _threadedRebaseBindIndices;
+ threadedRebaseBindIndices.reserve(bindInfo.size() + rebaseInfo.size());
+
+ for (int64_t i = 0, e = rebaseInfo.size(); i != e; ++i)
+ threadedRebaseBindIndices.push_back(-i);
+
+ for (int64_t i = 0, e = bindInfo.size(); i != e; ++i)
+ threadedRebaseBindIndices.push_back(i + 1);
+
+ // Now sort the entries by address.
+ std::sort(threadedRebaseBindIndices.begin(), threadedRebaseBindIndices.end(),
+ [&rebaseInfo, &bindInfo](int64_t indexA, int64_t indexB) {
+ if (indexA == indexB)
+ return false;
+ uint64_t addressA = indexA <= 0 ? rebaseInfo[-indexA]._address : bindInfo[indexA - 1]._address;
+ uint64_t addressB = indexB <= 0 ? rebaseInfo[-indexB]._address : bindInfo[indexB - 1]._address;
+ assert(addressA != addressB);
+ return addressA < addressB;
+ });
+ }
+
+ // new rebasing/binding scheme requires making another pass at DATA
+ // segment and building linked list of rebase locations
+ if ( _options.useLinkedListBinding() && !_threadedRebaseBindIndices.empty() ) {
+ uint64_t curSegStart = 0;
+ uint64_t curSegEnd = 0;
+ uint32_t curSegIndex = 0;
+ ld::Internal::FinalSection* curSection = NULL;
+
+ const uint64_t deltaBits = 11;
+ const uint32_t fixupAlignment = _options.makeThreadedStartsSection() ? 4 : 8;
+ const bool allowThreadsToCrossPages = _options.makeThreadedStartsSection();
+ std::vector<uint64_t> threadStarts;
+
+ // Find the thread starts section
+ ld::Internal::FinalSection* threadStartsSection = nullptr;
+ uint64_t threadStartsReservedSpace = 0;
+ if ( _options.makeThreadedStartsSection() ) {
+ for (ld::Internal::FinalSection* sect : state.sections) {
+ if ( sect->type() == ld::Section::typeThreadStarts ) {
+ threadStartsSection = sect;
+ break;
+ }
+ }
+ assert(threadStartsSection);
+ threadStartsReservedSpace = (threadStartsSection->size - 4) / 4;
+ threadStarts.reserve(threadStartsReservedSpace);
+ }
+
+ auto getAddress = [this](int64_t index) {
+ if (index <= 0)
+ return _rebaseInfo[-index]._address;
+ else
+ return _bindingInfo[index - 1]._address;
+ };
+
+ if ( (_bindingInfo.size() > 1)
+ && ! findSegment(state, getAddress(_threadedRebaseBindIndices.front()),
+ &curSegStart, &curSegEnd, &curSegIndex) )
+ throw "binding address outside range of any segment";
+
+ auto applyBind = [&](int64_t currentIndex, int64_t nextIndex) {
+ uint64_t currentAddress = getAddress(currentIndex);
+ uint64_t nextAddress = getAddress(nextIndex);
+
+ // The very first pointer we see must be a new chain
+ if ( _options.makeThreadedStartsSection() && curSection == NULL )
+ threadStarts.push_back(currentAddress);
+
+ if ( (curSection == NULL)
+ || (currentAddress < curSection->address)
+ || (currentAddress >= curSection->address+curSection->size) ) {
+ for (ld::Internal::FinalSection* sect : state.sections) {
+ if ( (sect->address <= currentAddress)
+ && (currentAddress < sect->address+sect->size) ) {
+ curSection = sect;
+ break;
+ }
+ }
+ }
+
+ bool makeChainToNextAddress = true;
+ if ( allowThreadsToCrossPages ) {
+ // Even if we allow threads to cross pages, we still need to have the same section.
+ if ( (nextAddress < curSection->address) || (nextAddress >= curSection->address+curSection->size) )
+ makeChainToNextAddress = false;
+ } else {
+ // If threads can't cross pages then make sure they are on the same page.
+ uint64_t currentPageIndex = ( currentAddress - curSegStart) / 4096;
+ uint64_t nextPageIndex = ( nextAddress - curSegStart) / 4096;
+ if ( currentPageIndex != nextPageIndex )
+ makeChainToNextAddress = false;
+ }
+
+ uint64_t delta = 0;
+ if (makeChainToNextAddress) {
+ delta = nextAddress - currentAddress;
+
+ // The value should already be aligned to 4 or 8, so make sure the low bits are zeroes
+ assert( (delta & (fixupAlignment - 1)) == 0 );
+ delta /= fixupAlignment;
+ if ( delta >= (1 << deltaBits) ) {
+ // Current and next are both in the same segment, so see if they are
+ // on the same page. If so, patch current to point to next.
+ makeChainToNextAddress = false;
+ }
+ }
+
+ if (!makeChainToNextAddress) {
+ delta = 0;
+ if (_options.makeThreadedStartsSection())
+ threadStarts.push_back(nextAddress);
+ }
+
+ uint8_t* lastBindLocation = wholeBuffer + curSection->fileOffset + currentAddress - curSection->address;
+ switch ( _options.architecture() ) {
+ case CPU_TYPE_X86_64:
+ case CPU_TYPE_ARM64:
+ uint64_t value = 0;
+ if (currentIndex <= 0) {
+ // For rebases, bits [0..50] is the mh offset which is already set
+ // Bit 62 is a 0 to say this is a rebase
+ value = get64LE(lastBindLocation);
+#if SUPPORT_ARCH_arm64e
+ auto fixupOffset = (uintptr_t)(lastBindLocation - mhAddress);
+ auto it = _authenticatedFixupData.find(fixupOffset);
+ if (it != _authenticatedFixupData.end()) {
+ // For authenticated data, we zeroed out the location
+ assert(value == 0);
+ const auto &authData = it->second.first;
+ uint64_t accumulator = it->second.second;
+ assert(accumulator >= mhAddress);
+ accumulator -= mhAddress;
+
+ // Make sure the high bits aren't set. The low 32-bits may
+ // be the target value.
+ assert((accumulator & 0xFFFFFFFF00000000ULL) == 0);
+ accumulator |= ((uint64_t)authData.discriminator) << 32;
+ accumulator |= ((uint64_t)authData.hasAddressDiversity) << 48;
+ accumulator |= ((uint64_t)authData.key) << 49;
+ // Set the high bit as we are authenticated
+ accumulator |= 1ULL << 63;
+
+ value = accumulator;
+ } else
+#endif
+ {
+ // Regular pointer which needs to fit in 51-bits of value.
+ // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+ // and the bottom 43-bits with sign-extension to be fit in to 51-bits.
+ uint64_t top8Bits = value & 0xFF00000000000000ULL;
+ uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+ // Ensure that the sign-extended bottom 43-bits is equivalent in sign to the gap bits
+ assert( ((value & ~0xFF0003FFFFFFFFFF) == 0) || ((value & ~0xFF0003FFFFFFFFFF) == ~0xFF0003FFFFFFFFFF) );
+ value = ( top8Bits >> 13 ) | bottom43Bits;
+ }
+ } else {
+ // The ordinal in [0..15]
+ // Bit 62 is a 1 to say this is a bind
+ value = get64LE(lastBindLocation);
+#if SUPPORT_ARCH_arm64e
+ auto fixupOffset = (uintptr_t)(lastBindLocation - mhAddress);
+ auto it = _authenticatedFixupData.find(fixupOffset);
+ if (it != _authenticatedFixupData.end()) {
+ // For authenticated data, we zeroed out the location
+ assert(value == 0);
+ const auto &authData = it->second.first;
+ uint64_t accumulator = it->second.second;
+
+ // Make sure the high bits aren't set. The low 32-bits may
+ // be the target value.
+ // Note, this doesn't work for binds to a weak def as we actually
+ // manage to resolve their address to an address in this binary so
+ // its not 0.
+ if (_bindingInfo[currentIndex - 1]._libraryOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP)
+ accumulator = 0;
+ assert((accumulator & 0xFFFFFFFF00000000ULL) == 0);
+ accumulator |= ((uint64_t)authData.discriminator) << 32;
+ accumulator |= ((uint64_t)authData.hasAddressDiversity) << 48;
+ accumulator |= ((uint64_t)authData.key) << 49;
+ // Set the high bit as we are authenticated
+ accumulator |= 1ULL << 63;
+
+ value = accumulator;
+ } else
+#endif
+ {
+ // Regular pointer
+ // The current data is unused as we get a new address from the bind table.
+ // So zero it out to avoid the bits interfering with the authentication bits.
+ value = 0;
+ }
+ value &= 0xFFFFFFFFFFFF0000;
+ value |= _bindingInfo[currentIndex - 1]._threadedBindOrdinal;
+ value |= 1ULL << 62;
+ }
+
+ // The delta is bits [51..61]
+ value |= ( delta << 51 );
+ set64LE(lastBindLocation, value);
+ break;
+ }
+ };
+
+ // Loop over every value and see if it needs to point to its successor.
+ // Note that on every iteration, info[i] is already known to be in the current
+ // segment.
+ for (int64_t i = 0, e = _threadedRebaseBindIndices.size() - 1; i != e; ++i) {
+ int64_t currentIndex = _threadedRebaseBindIndices[i];
+ int64_t nextIndex = _threadedRebaseBindIndices[i + 1];
+ uint64_t nextAddress = getAddress(nextIndex);
+ if ( (nextAddress < curSegStart) || ( nextAddress >= curSegEnd) ) {
+ // The next pointer is in a new segment.
+ // This means current is the end of a chain, and we need to move
+ // the segment addresses on to be the next ones.
+ if ( ! findSegment(state, nextAddress, &curSegStart, &curSegEnd, &curSegIndex) )
+ throw "binding address outside range of any segment";
+ }
+
+ applyBind(currentIndex, nextIndex);
+ }
+
+ applyBind(_threadedRebaseBindIndices.back(), _threadedRebaseBindIndices.back());
+
+ if ( _options.makeThreadedStartsSection() ) {
+ if ( threadStarts.size() > threadStartsReservedSpace )
+ throw "overflow in thread starts section";
+
+ // Now write over this section content with the new array.
+ const ld::Atom *threadStartsAtom = nullptr;
+ for (const ld::Atom *atom : threadStartsSection->atoms) {
+ if ( (atom->contentType() == ld::Atom::typeSectionStart) || (atom->contentType() == ld::Atom::typeSectionEnd) ) {
+ assert(atom->size() == 0);
+ continue;
+ }
+ assert(threadStartsAtom == nullptr);
+ threadStartsAtom = atom;
+ }
+ uint64_t threadStartsFileOffset = threadStartsAtom->finalAddress() - threadStartsSection->address + threadStartsSection->fileOffset;
+ // Skip the header
+ threadStartsFileOffset += sizeof(uint32_t);
+ for (uint64_t threadStart : threadStarts) {
+ uint64_t offset = threadStart - mhAddress;
+ assert(offset < 0x100000000);
+ set32LE(&wholeBuffer[threadStartsFileOffset], offset);
+ threadStartsFileOffset += sizeof(uint32_t);
+ }
+ }
+ }
}
void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer)
if ( lastSlash != NULL ) {
CC_MD5_Update(&md5state, lastSlash, strlen(lastSlash));
}
+ // <rdar://problem/38679559> use train name when calculating a binary's UUID
+ const char* buildName = _options.buildContextName();
+ if ( buildName != NULL ) {
+ CC_MD5_Update(&md5state, buildName, strlen(buildName));
+ }
std::sort(excludeRegions.begin(), excludeRegions.end());
uint64_t checksumStart = 0;
for ( auto& region : excludeRegions ) {
case ld::Atom::scopeLinkageUnit:
if ( _options.outputKind() == Options::kObjectFile ) {
if ( _options.keepPrivateExterns() ) {
- _exportedAtoms.push_back(atom);
+ if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel ) {
+ // <rdar://problem/42150005> ld -r should not promote static 'l' labels to hidden
+ (const_cast<ld::Atom*>(atom))->setScope(ld::Atom::scopeTranslationUnit);
+ _localAtoms.push_back(atom);
+ }
+ else {
+ _exportedAtoms.push_back(atom);
+ }
}
else if ( _options.keepLocalSymbol(atom->name()) ) {
_localAtoms.push_back(atom);
_localAtoms.push_back(atom);
// <rdar://problem/5804214> ld should never have a symbol in the non-lazy indirect symbol table with index 0
// this works by making __mh_execute_header be a local symbol which takes symbol index 0
- else if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip) && !_options.makeCompressedDyldInfo() )
+ else if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip) && !_options.makeCompressedDyldInfo() && !_options.makeThreadedStartsSection() )
_localAtoms.push_back(atom);
else
(const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn);
_externalRelocsAtom = new ExternalRelocationsAtom<x86>(_options, state, *this);
externalRelocationsSection = state.addAtom(*_externalRelocsAtom);
}
+ if ( _hasDataInCodeInfo ) {
+ _dataInCodeAtom = new DataInCodeAtom<x86_64>(_options, state, *this);
+ dataInCodeSection = state.addAtom(*_dataInCodeAtom);
+ }
if ( _hasSymbolTable ) {
_indirectSymbolTableAtom = new IndirectSymbolTableAtom<x86>(_options, state, *this);
indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom);
_externalRelocsAtom = new ExternalRelocationsAtom<x86_64>(_options, state, *this);
externalRelocationsSection = state.addAtom(*_externalRelocsAtom);
}
+ if ( _hasDataInCodeInfo ) {
+ _dataInCodeAtom = new DataInCodeAtom<x86_64>(_options, state, *this);
+ dataInCodeSection = state.addAtom(*_dataInCodeAtom);
+ }
if ( _hasSymbolTable ) {
_indirectSymbolTableAtom = new IndirectSymbolTableAtom<x86_64>(_options, state, *this);
indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom);
_externalRelocsAtom = new ExternalRelocationsAtom<arm>(_options, state, *this);
externalRelocationsSection = state.addAtom(*_externalRelocsAtom);
}
+ if ( _hasDataInCodeInfo ) {
+ _dataInCodeAtom = new DataInCodeAtom<x86_64>(_options, state, *this);
+ dataInCodeSection = state.addAtom(*_dataInCodeAtom);
+ }
if ( _hasSymbolTable ) {
_indirectSymbolTableAtom = new IndirectSymbolTableAtom<arm>(_options, state, *this);
indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom);
_externalRelocsAtom = new ExternalRelocationsAtom<arm64>(_options, state, *this);
externalRelocationsSection = state.addAtom(*_externalRelocsAtom);
}
+ if ( _hasDataInCodeInfo ) {
+ _dataInCodeAtom = new DataInCodeAtom<x86_64>(_options, state, *this);
+ dataInCodeSection = state.addAtom(*_dataInCodeAtom);
+ }
if ( _hasSymbolTable ) {
_indirectSymbolTableAtom = new IndirectSymbolTableAtom<arm64>(_options, state, *this);
indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom);
}
-bool OutputFile::isPcRelStore(ld::Fixup::Kind kind)
+bool OutputFile::isPcRelStore(const ld::Fixup* fixup)
{
- switch ( kind ) {
- case ld::Fixup::kindStoreX86BranchPCRel8:
- case ld::Fixup::kindStoreX86BranchPCRel32:
- case ld::Fixup::kindStoreX86PCRel8:
- case ld::Fixup::kindStoreX86PCRel16:
- case ld::Fixup::kindStoreX86PCRel32:
- case ld::Fixup::kindStoreX86PCRel32_1:
- case ld::Fixup::kindStoreX86PCRel32_2:
- case ld::Fixup::kindStoreX86PCRel32_4:
- case ld::Fixup::kindStoreX86PCRel32GOTLoad:
- case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA:
- case ld::Fixup::kindStoreX86PCRel32GOT:
- case ld::Fixup::kindStoreX86PCRel32TLVLoad:
- case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA:
- case ld::Fixup::kindStoreARMBranch24:
- case ld::Fixup::kindStoreThumbBranch22:
- case ld::Fixup::kindStoreARMLoad12:
- case ld::Fixup::kindStoreTargetAddressX86PCRel32:
- case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
- case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA:
- case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad:
- case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA:
- case ld::Fixup::kindStoreTargetAddressARMBranch24:
- case ld::Fixup::kindStoreTargetAddressThumbBranch22:
- case ld::Fixup::kindStoreTargetAddressARMLoad12:
-#if SUPPORT_ARCH_arm64
- case ld::Fixup::kindStoreARM64Page21:
- case ld::Fixup::kindStoreARM64PageOff12:
- case ld::Fixup::kindStoreARM64GOTLoadPage21:
- 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::kindStoreTargetAddressARM64GOTLoadPage21:
- 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:
-#if SUPPORT_ARCH_arm64
- case ld::Fixup::kindStoreTargetAddressARM64Branch26:
-#endif
- return (_options.outputKind() != Options::kKextBundle);
- default:
- break;
- }
- return false;
+ return fixup->isPcRelStore(_options.outputKind() == Options::kKextBundle);
}
bool OutputFile::isStore(ld::Fixup::Kind kind)
case ld::Fixup::kindSubtractAddend:
case ld::Fixup::kindSetTargetImageOffset:
case ld::Fixup::kindSetTargetSectionOffset:
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindSetAuthData:
+#endif
return false;
default:
break;
}
-bool OutputFile::setsTarget(ld::Fixup::Kind kind)
+bool OutputFile::setsTarget(const ld::Fixup &fixup)
{
- switch ( kind ) {
- case ld::Fixup::kindSetTargetAddress:
- case ld::Fixup::kindLazyTarget:
- case ld::Fixup::kindStoreTargetAddressLittleEndian32:
- case ld::Fixup::kindStoreTargetAddressLittleEndian64:
- case ld::Fixup::kindStoreTargetAddressBigEndian32:
- case ld::Fixup::kindStoreTargetAddressBigEndian64:
- case ld::Fixup::kindStoreTargetAddressX86PCRel32:
- case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32:
- case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
- case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA:
- case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad:
- case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA:
- case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad:
- case ld::Fixup::kindStoreTargetAddressARMBranch24:
- case ld::Fixup::kindStoreTargetAddressThumbBranch22:
- case ld::Fixup::kindStoreTargetAddressARMLoad12:
-#if SUPPORT_ARCH_arm64
- case ld::Fixup::kindStoreTargetAddressARM64Branch26:
- case ld::Fixup::kindStoreTargetAddressARM64Page21:
- case ld::Fixup::kindStoreTargetAddressARM64PageOff12:
- case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
- 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::kindStoreX86DtraceIsEnableSiteClear:
- case ld::Fixup::kindStoreARMDtraceCallSiteNop:
- case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear:
- case ld::Fixup::kindStoreARM64DtraceCallSiteNop:
- case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear:
- case ld::Fixup::kindStoreThumbDtraceCallSiteNop:
- case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear:
- return (_options.outputKind() == Options::kObjectFile);
- default:
- break;
- }
- return false;
+ return fixup.setsTarget(_options.outputKind() == Options::kObjectFile);
}
bool OutputFile::isPointerToTarget(ld::Fixup::Kind kind)
case ld::Fixup::kindSetTargetAddress:
case ld::Fixup::kindStoreTargetAddressLittleEndian32:
case ld::Fixup::kindStoreTargetAddressLittleEndian64:
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64:
+#endif
case ld::Fixup::kindStoreTargetAddressBigEndian32:
case ld::Fixup::kindStoreTargetAddressBigEndian64:
case ld::Fixup::kindLazyTarget:
if ( _options.makeCompressedDyldInfo() ) {
uint8_t wtype = BIND_TYPE_OVERRIDE_OF_WEAKDEF_IN_DYLIB;
bool nonWeakDef = (atom->combine() == ld::Atom::combineNever);
- _weakBindingInfo.push_back(BindingInfo(wtype, atom->name(), nonWeakDef, atom->finalAddress(), 0));
+ // Don't push weak binding info for threaded bind.
+ // Instead we use a special ordinal in the regular bind info
+ if ( !_options.useLinkedListBinding() )
+ _weakBindingInfo.push_back(BindingInfo(wtype, atom->name(), nonWeakDef, atom->finalAddress(), 0));
}
this->overridesWeakExternalSymbols = true;
if ( _options.warnWeakExports() )
const ld::Atom* minusTarget = NULL;
uint64_t targetAddend = 0;
uint64_t minusTargetAddend = 0;
+#if SUPPORT_ARCH_arm64e
+ ld::Fixup* fixupWithAuthData = NULL;
+#endif
for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
if ( fit->firstInCluster() ) {
fixupWithTarget = NULL;
targetAddend = 0;
minusTargetAddend = 0;
}
- if ( this->setsTarget(fit->kind) ) {
+ if ( this->setsTarget(*fit) ) {
switch ( fit->binding ) {
case ld::Fixup::bindingNone:
case ld::Fixup::bindingByNameUnbound:
case ld::Fixup::kindDataInCodeEnd:
hasDataInCode = true;
break;
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindSetAuthData:
+ fixupWithAuthData = fit;
+ break;
+#endif
default:
break;
}
- if ( this->isStore(fit->kind) ) {
+ if ( fit->isStore() ) {
fixupWithStore = fit;
}
if ( fit->lastInCluster() ) {
if ( (fixupWithStore != NULL) && (target != NULL) ) {
if ( _options.outputKind() == Options::kObjectFile ) {
this->addSectionRelocs(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithAddend, fixupWithStore,
+#if SUPPORT_ARCH_arm64e
+ fixupWithAuthData,
+#endif
target, minusTarget, targetAddend, minusTargetAddend);
}
else {
if ( _options.makeCompressedDyldInfo() ) {
this->addDyldInfo(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore,
- target, minusTarget, targetAddend, minusTargetAddend);
+ target, minusTarget, targetAddend, minusTargetAddend);
+ } else if ( _options.makeThreadedStartsSection() ) {
+ this->addThreadedRebaseInfo(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore,
+ target, minusTarget, targetAddend, minusTargetAddend);
}
else {
this->addClassicRelocs(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore,
warning("text reloc in %s to %s", atom->name(), target->name());
}
else if ( _options.positionIndependentExecutable() && (_options.outputKind() == Options::kDynamicExecutable)
- && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) {
+ && _options.platforms().minOS(ld::version2010Fall)) {
if ( ! this->pieDisabled ) {
switch ( _options.architecture()) {
#if SUPPORT_ARCH_arm64
return;
// no need to rebase or bind PCRel stores
- if ( this->isPcRelStore(fixupWithStore->kind) ) {
+ if ( this->isPcRelStore(fixupWithStore) ) {
// as long as target is in same linkage unit
if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) ) {
// make sure target is not global and weak
}
}
}
- }
+ }
+
+ // Find the ordinal for the bind target
+ int compressedOrdinal = 0;
+ if ( needsBinding || needsLazyBinding || needsWeakBinding ) {
+ compressedOrdinal = this->compressedOrdinalForAtom(target);
+ }
+ // Linked list binding puts the weak binds in to the regular binds but with a special ordinal.
+ if ( needsWeakBinding && _options.useLinkedListBinding() ) {
+ assert(!needsLazyBinding);
+ needsWeakBinding = false;
+ needsBinding = true;
+ needsRebase = false;
+ compressedOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
+ }
// record dyld info for this cluster
if ( needsRebase ) {
// do nothing
break;
}
+ _hasUnalignedFixup = true;
}
_rebaseInfo.push_back(RebaseInfo(rebaseType, address));
}
+
if ( needsBinding ) {
if ( inReadOnlySeg ) {
noteTextReloc(atom, target);
// do nothing
break;
}
+ _hasUnalignedFixup = true;
}
- _bindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend));
+ _bindingInfo.push_back(BindingInfo(type, compressedOrdinal, target->name(), weak_import, address, addend));
}
if ( needsLazyBinding ) {
if ( _options.bindAtLoad() )
- _bindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend));
+ _bindingInfo.push_back(BindingInfo(type, compressedOrdinal, target->name(), weak_import, address, addend));
else
- _lazyBindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend));
+ _lazyBindingInfo.push_back(BindingInfo(type, compressedOrdinal, target->name(), weak_import, address, addend));
}
if ( needsWeakBinding )
_weakBindingInfo.push_back(BindingInfo(type, 0, target->name(), false, address, addend));
}
+void OutputFile::addThreadedRebaseInfo(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom,
+ ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore,
+ const ld::Atom* target, const ld::Atom* minusTarget,
+ uint64_t targetAddend, uint64_t minusTargetAddend)
+{
+ if ( sect->isSectionHidden() )
+ return;
+
+ // no need to rebase or bind PCRel stores
+ if ( this->isPcRelStore(fixupWithStore) ) {
+ // as long as target is in same linkage unit
+ if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) ) {
+ // make sure target is not global and weak
+ if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular)) {
+ if ( (atom->section().type() == ld::Section::typeCFI)
+ || (atom->section().type() == ld::Section::typeDtraceDOF)
+ || (atom->section().type() == ld::Section::typeUnwindInfo) ) {
+ // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols
+ return;
+ }
+ // <rdar://problem/13700961> spurious warning when weak function has reference to itself
+ if ( fixupWithTarget->binding == ld::Fixup::bindingDirectlyBound ) {
+ // ok to ignore pc-rel references within a weak function to itself
+ return;
+ }
+ // Have direct reference to weak-global. This should be an indrect reference
+ const char* demangledName = strdup(_options.demangleSymbol(atom->name()));
+ warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. "
+ "This was likely caused by different translation units being compiled with different visibility settings.",
+ demangledName, atom->safeFilePath(), _options.demangleSymbol(target->name()), target->safeFilePath());
+ }
+ return;
+ }
+ }
+
+ // no need to rebase or bind PIC internal pointer diff
+ if ( minusTarget != NULL ) {
+ // with pointer diffs, both need to be in same linkage unit
+ assert(minusTarget->definition() != ld::Atom::definitionProxy);
+ assert(target != NULL);
+ assert(target->definition() != ld::Atom::definitionProxy);
+ if ( target == minusTarget ) {
+ // This is a compile time constant and could have been optimized away by compiler
+ return;
+ }
+
+ // check if target of pointer-diff is global and weak
+ if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) ) {
+ if ( (atom->section().type() == ld::Section::typeCFI)
+ || (atom->section().type() == ld::Section::typeDtraceDOF)
+ || (atom->section().type() == ld::Section::typeUnwindInfo) ) {
+ // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols
+ return;
+ }
+ // Have direct reference to weak-global. This should be an indrect reference
+ const char* demangledName = strdup(_options.demangleSymbol(atom->name()));
+ warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. "
+ "This was likely caused by different translation units being compiled with different visibility settings.",
+ demangledName, atom->safeFilePath(), _options.demangleSymbol(target->name()), target->safeFilePath());
+ }
+ return;
+ }
+
+ // no need to rebase or bind an atom's references to itself if the output is not slidable
+ if ( (atom == target) && !_options.outputSlidable() )
+ return;
+
+ // cluster has no target, so needs no rebasing or binding
+ if ( target == NULL )
+ return;
+
+ const uint64_t minAlignment = 4;
+ bool inReadOnlySeg = ( strcmp(sect->segmentName(), "__TEXT") == 0 );
+ bool needsRebase = false;
+
+ uint8_t rebaseType = REBASE_TYPE_POINTER;
+ uint64_t address = atom->finalAddress() + fixupWithTarget->offsetInAtom;
+
+ // special case lazy pointers
+ switch ( target->definition() ) {
+ case ld::Atom::definitionProxy:
+ break;
+ case ld::Atom::definitionRegular:
+ case ld::Atom::definitionTentative:
+ needsRebase = true;
+ break;
+ case ld::Atom::definitionAbsolute:
+ break;
+ }
+
+ // record dyld info for this cluster
+ if ( needsRebase ) {
+ if ( inReadOnlySeg ) {
+ noteTextReloc(atom, target);
+ sect->hasLocalRelocs = true; // so dyld knows to change permissions on __TEXT segment
+ }
+ if ( ((address & (minAlignment-1)) != 0) ) {
+ throwf("pointer not aligned to at least 4-bytes at address 0x%llX (%s + %lld from %s)",
+ address, atom->name(), (address - atom->finalAddress()), atom->safeFilePath());
+ }
+ _rebaseInfo.push_back(RebaseInfo(rebaseType, address));
+ }
+}
+
void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom,
ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore,
}
// no need to rebase or bind PCRel stores
- if ( this->isPcRelStore(fixupWithStore->kind) ) {
+ if ( this->isPcRelStore(fixupWithStore) ) {
// as long as target is in same linkage unit
if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) )
return;
case ld::Fixup::kindStoreBigEndian64:
case ld::Fixup::kindStoreTargetAddressLittleEndian32:
case ld::Fixup::kindStoreTargetAddressLittleEndian64:
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64:
+#endif
case ld::Fixup::kindStoreTargetAddressBigEndian32:
case ld::Fixup::kindStoreTargetAddressBigEndian64:
// is pointer
throwf("no supported runtime hi16 relocation in %s from %s to %s", atom->name(), atom->safeFilePath(), target->name());
break;
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreLittleEndianAuth64:
+ if ( _options.outputKind() == Options::kKextBundle ) {
+ if ( target->definition() == ld::Atom::definitionProxy ) {
+ _externalRelocsAtom->addExternalPointerReloc(relocAddress, target);
+ sect->hasExternalRelocs = true;
+ fixupWithTarget->contentAddendOnly = true;
+ }
+ else {
+ _localRelocsAtom->addPointerReloc(relocAddress, target->machoSection());
+ sect->hasLocalRelocs = true;
+ }
+ }
+ else {
+ throwf("authenticated pointer in atom %s from %s to %s is not supported", atom->name(), atom->safeFilePath(), target->name());
+ }
+ break;
+#endif
default:
break;
}
void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom,
ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget,
- ld::Fixup* fixupWithAddend, ld::Fixup* fixupWithStore,
+ ld::Fixup* fixupWithAddend, ld::Fixup* fixupWithStore,
+#if SUPPORT_ARCH_arm64e
+ ld::Fixup* fixupWithAuthData,
+#endif
const ld::Atom* target, const ld::Atom* minusTarget,
uint64_t targetAddend, uint64_t minusTargetAddend)
{
fixupWithTarget->contentAddendOnly = true;
fixupWithStore->contentAddendOnly = true;
}
- else if ( isPcRelStore(fixupWithStore->kind) ) {
+ else if ( isPcRelStore(fixupWithStore) ) {
fixupWithTarget->contentDetlaToAddendOnly = true;
fixupWithStore->contentDetlaToAddendOnly = true;
}
if ( fixupWithStore != NULL ) {
_sectionsRelocationsAtom->addSectionReloc(sect, fixupWithStore->kind, atom, fixupWithStore->offsetInAtom,
targetUsesExternalReloc, minusTargetUsesExternalReloc,
+#if SUPPORT_ARCH_arm64e
+ fixupWithAuthData,
+#endif
target, targetAddend, minusTarget, minusTargetAddend);
}
for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
if ( fit->firstInCluster() )
target = NULL;
- if ( this->setsTarget(fit->kind) ) {
+ if ( this->setsTarget(*fit) ) {
accumulator = addressOf(state, fit, &target);
thumbTarget = targetIsThumb(state, fit);
if ( thumbTarget )
assert(target != NULL);
hadSubtract = true;
break;
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreLittleEndianAuth64:
+ case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64:
+ case ld::Fixup::kindSetAuthData:
+ throw "authenticated pointers are not supported in split seg v1";
+ break;
+#endif
default:
break;
}
toSectionIndex = 255;
fromOffset = atom->finalAddress() + fit->offsetInAtom - sect->address;
}
- if ( this->setsTarget(fit->kind) ) {
+ if ( this->setsTarget(*fit) ) {
accumulator = addressAndTarget(state, fit, &target);
thumbTarget = targetIsThumb(state, fit);
if ( thumbTarget )
case ld::Fixup::kindStoreTargetAddressLittleEndian64:
if ( hadSubtract )
kind = DYLD_CACHE_ADJ_V2_DELTA_64;
+ else if ( _options.useLinkedListBinding() && !this->_hasUnalignedFixup )
+ kind = DYLD_CACHE_ADJ_V2_THREADED_POINTER_64;
else
kind = DYLD_CACHE_ADJ_V2_POINTER_64;
break;
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreLittleEndianAuth64:
+ case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64:
+ // FIXME: Do we need to handle subtracts on authenticated pointers?
+ assert(!hadSubtract);
+ kind = DYLD_CACHE_ADJ_V2_THREADED_POINTER_64;
+ break;
+#endif
case ld::Fixup::kindStoreX86PCRel32:
case ld::Fixup::kindStoreX86PCRel32_1:
case ld::Fixup::kindStoreX86PCRel32_2:
ld::Internal::FinalSection* externalRelocationsSection;
ld::Internal::FinalSection* sectionRelocationsSection;
ld::Internal::FinalSection* indirectSymbolTableSection;
+ ld::Internal::FinalSection* threadedPageStartsSection;
struct RebaseInfo {
RebaseInfo(uint8_t t, uint64_t addr) : _type(t), _address(addr) {}
struct BindingInfo {
BindingInfo(uint8_t t, int ord, const char* sym, bool weak_import, uint64_t addr, int64_t add)
- : _type(t), _flags(weak_import ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0 ), _libraryOrdinal(ord),
+ : _type(t), _flags(weak_import ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0 ),
+ _threadedBindOrdinal(0), _libraryOrdinal(ord),
_symbolName(sym), _address(addr), _addend(add) {}
BindingInfo(uint8_t t, const char* sym, bool non_weak_definition, uint64_t addr, int64_t add)
: _type(t), _flags(non_weak_definition ? BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION : 0 ),
- _libraryOrdinal(0), _symbolName(sym), _address(addr), _addend(add) {}
+ _threadedBindOrdinal(0), _libraryOrdinal(0), _symbolName(sym), _address(addr), _addend(add) {}
uint8_t _type;
uint8_t _flags;
+ uint16_t _threadedBindOrdinal;
int _libraryOrdinal;
const char* _symbolName;
uint64_t _address;
const ld::Atom* atom, ld::Fixup* fixupWithTarget,
ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithAddend,
ld::Fixup* fixupWithStore,
+#if SUPPORT_ARCH_arm64e
+ ld::Fixup* fixupWithAuthData,
+#endif
const ld::Atom* target, const ld::Atom* minusTarget,
uint64_t targetAddend, uint64_t minusTargetAddend);
void addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* sect,
ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore,
const ld::Atom* target, const ld::Atom* minusTarget,
uint64_t targetAddend, uint64_t minusTargetAddend);
+ void addThreadedRebaseInfo(ld::Internal& state, ld::Internal::FinalSection* sect,
+ const ld::Atom* atom, ld::Fixup* fixupWithTarget,
+ ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore,
+ const ld::Atom* target, const ld::Atom* minusTarget,
+ uint64_t targetAddend, uint64_t minusTargetAddend);
void addClassicRelocs(ld::Internal& state, ld::Internal::FinalSection* sect,
const ld::Atom* atom, ld::Fixup* fixupWithTarget,
ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore,
void copyNoOps(uint8_t* from, uint8_t* to, bool thumb);
bool isPointerToTarget(ld::Fixup::Kind kind);
bool isPointerFromTarget(ld::Fixup::Kind kind);
- bool isPcRelStore(ld::Fixup::Kind kind);
+ bool isPcRelStore(const ld::Fixup* fixup);
bool isStore(ld::Fixup::Kind kind);
bool storeAddendOnly(const ld::Atom* inAtom, const ld::Atom* target, bool pcRel=false);
- bool setsTarget(ld::Fixup::Kind kind);
+ bool setsTarget(const ld::Fixup &fixup);
void addFixupOutInfo(ld::Internal& state);
void makeRelocations(ld::Internal& state);
void makeSectionRelocations(ld::Internal& state);
std::vector<const ld::dylib::File*> _dylibsToLoad;
std::vector<const char*> _dylibOrdinalPaths;
const bool _hasDyldInfo;
+ const bool _hasThreadedPageStarts;
const bool _hasSymbolTable;
const bool _hasSectionRelocations;
const bool _hasSplitSegInfo;
std::vector<BindingInfo> _bindingInfo;
std::vector<BindingInfo> _lazyBindingInfo;
std::vector<BindingInfo> _weakBindingInfo;
+ bool _hasUnalignedFixup = false;
+ // Note, <= 0 values are indices in to rebases, > 0 are binds.
+ std::vector<int64_t> _threadedRebaseBindIndices;
+#if SUPPORT_ARCH_arm64e
+ std::map<uintptr_t, std::pair<Fixup::AuthData, uint64_t>> _authenticatedFixupData;
+#endif
std::vector<SplitSegInfoEntry> _splitSegInfos;
std::vector<SplitSegInfoV2Entry> _splitSegV2Infos;
class HeaderAndLoadCommandsAbtract* _headersAndLoadCommandAtom;
SectionBoundaryAtom* SectionBoundaryAtom::makeSectionBoundaryAtom(const char* name, bool start, const char* segSectName)
{
-
+
const char* segSectDividor = strrchr(segSectName, '$');
if ( segSectDividor == NULL )
throwf("malformed section$ symbol name: %s", name);
throwf("malformed section$ symbol name: %s", name);
char segName[18];
strlcpy(segName, segSectName, segNameLen+1);
-
- const ld::Section* section = new ld::Section(strdup(segName), sectionName, ld::Section::typeUnclassified);
+
+ ld::Section::Type sectType = ld::Section::typeUnclassified;
+ if (!strcmp(segName, "__TEXT") && !strcmp(sectionName, "__thread_starts"))
+ sectType = ld::Section::typeThreadStarts;
+
+ const ld::Section* section = new ld::Section(strdup(segName), sectionName, sectType);
return new SectionBoundaryAtom(name, *section, (start ? ld::Atom::typeSectionStart : typeSectionEnd));
}
void Resolver::initializeState()
{
- // set initial objc constraint based on command line options
- if ( _options.objcGc() )
- _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseOrGC;
- else if ( _options.objcGcOnly() )
- _internal.objcObjectConstraint = ld::File::objcConstraintGC;
-
_internal.cpuSubType = _options.subArchitecture();
- _internal.minOSVersion = _options.minOSversion();
- _internal.derivedPlatform = 0;
// In -r mode, look for -linker_option additions
if ( _options.outputKind() == Options::kObjectFile ) {
_inputFiles.createIndirectDylibs();
}
}
+ // update which form of ObjC is being used
+ if ( objFile->hasObjC() )
+ _internal.hasObjC = true;
+
// Resolve bitcode section in the object file
if ( _options.bundleBitcode() ) {
if ( objFile->getBitcode() == NULL ) {
_internal.filesFromCompilerRT.push_back(objFile);
} else if (objFile->sourceKind() != ld::relocatable::File::kSourceLTO ) {
// No bitcode section, figure out if the object file comes from LTO/compiler static library
- switch ( _options.platform() ) {
- case Options::kPlatformOSX:
- case Options::kPlatform_bridgeOS:
- case Options::kPlatformUnknown:
- warning("all bitcode will be dropped because '%s' was built without bitcode. "
- "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. ", file.path());
- _internal.filesWithBitcode.clear();
- _internal.dropAllBitcode = true;
- break;
- case Options::kPlatformiOS:
- throwf("'%s' does not contain bitcode. "
- "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path());
- break;
- case Options::kPlatformWatchOS:
-#if SUPPORT_APPLE_TV
- case Options::kPlatform_tvOS:
-#endif
- throwf("'%s' does not contain bitcode. "
- "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path());
- break;
- }
+ _options.platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ switch ( platform ) {
+ case ld::kPlatform_macOS:
+ case ld::kPlatform_bridgeOS:
+ case ld::kPlatform_iOSMac:
+ case ld::kPlatform_unknown:
+ warning("all bitcode will be dropped because '%s' was built without bitcode. "
+ "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. ", file.path());
+ _internal.filesWithBitcode.clear();
+ _internal.dropAllBitcode = true;
+ break;
+ case ld::kPlatform_iOS:
+ case ld::kPlatform_iOSSimulator:
+ throwf("'%s' does not contain bitcode. "
+ "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path());
+ break;
+ case ld::kPlatform_watchOS:
+ case ld::kPlatform_watchOSSimulator:
+ case ld::kPlatform_tvOS:
+ case ld::kPlatform_tvOSSimulator:
+ throwf("'%s' does not contain bitcode. "
+ "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path());
+ break;
+ }
+ });
}
} else {
// contains bitcode, check if it is just a marker
}
}
- // update which form of ObjC is being used
- switch ( file.objCConstraint() ) {
- case ld::File::objcConstraintNone:
- break;
- case ld::File::objcConstraintRetainRelease:
- if ( _internal.objcObjectConstraint == ld::File::objcConstraintGC )
- throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", file.path());
- if ( _options.objcGcOnly() )
- throwf("command line specified -objc_gc_only, but file is retain/release based: %s", file.path());
- if ( _options.objcGc() )
- throwf("command line specified -objc_gc, but file is retain/release based: %s", file.path());
- if ( !_options.targetIOSSimulator() && (_internal.objcObjectConstraint != ld::File::objcConstraintRetainReleaseForSimulator) )
- _internal.objcObjectConstraint = ld::File::objcConstraintRetainRelease;
- break;
- case ld::File::objcConstraintRetainReleaseOrGC:
- if ( _internal.objcObjectConstraint == ld::File::objcConstraintNone )
- _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseOrGC;
- if ( _options.targetIOSSimulator() )
- warning("linking ObjC for iOS Simulator, but object file (%s) was compiled for MacOSX", file.path());
- break;
- case ld::File::objcConstraintGC:
- if ( _internal.objcObjectConstraint == ld::File::objcConstraintRetainRelease )
- throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", file.path());
- _internal.objcObjectConstraint = ld::File::objcConstraintGC;
- if ( _options.targetIOSSimulator() )
- warning("linking ObjC for iOS Simulator, but object file (%s) was compiled for MacOSX", file.path());
- break;
- case ld::File::objcConstraintRetainReleaseForSimulator:
- if ( _internal.objcObjectConstraint == ld::File::objcConstraintNone ) {
- if ( !_options.targetIOSSimulator() && (_options.outputKind() != Options::kObjectFile) )
- warning("ObjC object file (%s) was compiled for iOS Simulator, but linking for MacOSX", file.path());
- _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
- }
- else if ( _internal.objcObjectConstraint != ld::File::objcConstraintRetainReleaseForSimulator ) {
- _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
- }
- break;
- }
-
// verify all files use same version of Swift language
if ( file.swiftVersion() != 0 ) {
+ _internal.someObjectFileHasSwift = true;
if ( _internal.swiftVersion == 0 ) {
_internal.swiftVersion = file.swiftVersion();
}
if ( objFile->hasllvmProfiling() )
_havellvmProfiling = true;
+#if MULTI
// update minOSVersion off all .o files
uint32_t objMinOS = objFile->minOSVersion();
if ( !objMinOS )
_internal.objectFileFoundWithNoVersion = true;
if ( (_options.outputKind() == Options::kObjectFile) && (objMinOS > _internal.minOSVersion) )
_internal.minOSVersion = objMinOS;
+#endif
- uint32_t objPlatform = objFile->platform();
- if ( (objPlatform != 0) && (_options.outputKind() == Options::kObjectFile) && (_internal.derivedPlatform == 0) )
- _internal.derivedPlatform = objPlatform;
-
+ auto objPlatforms = objFile->platforms();
+ if ( (!objPlatforms.empty()) && (_options.outputKind() == Options::kObjectFile) && (_internal.derivedPlatforms.empty()) )
+ _internal.derivedPlatforms = objPlatforms;
// update set of known tools used
for (const std::pair<uint32_t,uint32_t>& entry : objFile->toolVersions()) {
uint64_t combined = (uint64_t)entry.first << 32 | entry.second;
if ( realpath(tempPath, tcLibPath) == NULL ||
realpath(dylibFile->path(), tempPath) == NULL ||
strncmp(tcLibPath, tempPath, strlen(tcLibPath)) != 0 ) {
- switch ( _options.platform() ) {
- case Options::kPlatformOSX:
- case Options::kPlatform_bridgeOS:
- case Options::kPlatformUnknown:
- warning("all bitcode will be dropped because '%s' was built without bitcode. "
- "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path());
- _internal.filesWithBitcode.clear();
- _internal.dropAllBitcode = true;
- break;
- case Options::kPlatformiOS:
- throwf("'%s' does not contain bitcode. "
- "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path());
- break;
- case Options::kPlatformWatchOS:
-#if SUPPORT_APPLE_TV
- case Options::kPlatform_tvOS:
-#endif
- throwf("'%s' does not contain bitcode. "
- "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path());
- break;
- }
+ _options.platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ switch ( platform ) {
+ case ld::kPlatform_macOS:
+ case ld::kPlatform_bridgeOS:
+ case ld::kPlatform_iOSMac:
+ case ld::kPlatform_unknown:
+ warning("all bitcode will be dropped because '%s' was built without bitcode. "
+ "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path());
+ _internal.filesWithBitcode.clear();
+ _internal.dropAllBitcode = true;
+ break;
+ case ld::kPlatform_iOS:
+ case ld::kPlatform_iOSSimulator:
+ throwf("'%s' does not contain bitcode. "
+ "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path());
+ break;
+ case ld::kPlatform_watchOS:
+ case ld::kPlatform_watchOSSimulator:
+ case ld::kPlatform_tvOS:
+ case ld::kPlatform_tvOSSimulator:
+ throwf("'%s' does not contain bitcode. "
+ "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path());
+ break;
+ }
+ });
}
}
// Error on bitcode marker in non-system frameworks if -bitcode_verify is used
dylibFile->path());
}
- // update which form of ObjC dylibs are being linked
- switch ( dylibFile->objCConstraint() ) {
- case ld::File::objcConstraintNone:
- break;
- case ld::File::objcConstraintRetainRelease:
- if ( _internal.objcDylibConstraint == ld::File::objcConstraintGC )
- throwf("%s built with incompatible Garbage Collection settings to link with previous dylibs", file.path());
- if ( _options.objcGcOnly() )
- throwf("command line specified -objc_gc_only, but dylib is retain/release based: %s", file.path());
- if ( _options.objcGc() )
- throwf("command line specified -objc_gc, but dylib is retain/release based: %s", file.path());
- if ( _options.targetIOSSimulator() )
- warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path());
- _internal.objcDylibConstraint = ld::File::objcConstraintRetainRelease;
- break;
- case ld::File::objcConstraintRetainReleaseOrGC:
- if ( _internal.objcDylibConstraint == ld::File::objcConstraintNone )
- _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseOrGC;
- if ( _options.targetIOSSimulator() )
- warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path());
- break;
- case ld::File::objcConstraintGC:
- if ( _internal.objcDylibConstraint == ld::File::objcConstraintRetainRelease )
- throwf("%s built with incompatible Garbage Collection settings to link with previous dylibs", file.path());
- if ( _options.targetIOSSimulator() )
- warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path());
- _internal.objcDylibConstraint = ld::File::objcConstraintGC;
- break;
- case ld::File::objcConstraintRetainReleaseForSimulator:
- if ( _internal.objcDylibConstraint == ld::File::objcConstraintNone )
- _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
- else if ( _internal.objcDylibConstraint != ld::File::objcConstraintRetainReleaseForSimulator ) {
- warning("ObjC dylib (%s) was compiled for iOS Simulator, but dylibs others were compiled for MacOSX", file.path());
- _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
+ // Don't allow swift frameworks to link other swift frameworks.
+ if ( !_internal.firstSwiftDylibFile && _options.outputKind() == Options::kDynamicLibrary
+ && file.swiftVersion() != 0 && getenv("LD_DISALLOW_SWIFT_LINKING_SWIFT")) {
+ // Check that we aren't a whitelisted path.
+ bool inWhiteList = false;
+ const char *whitelistedPaths[] = { "/System/Library/PrivateFrameworks/Swift" };
+ for (auto whitelistedPath : whitelistedPaths) {
+ if (!strncmp(whitelistedPath, dylibFile->installPath(), strlen(whitelistedPath))) {
+ inWhiteList = true;
+ break;
}
- break;
+ }
+ if (!inWhiteList) {
+ _internal.firstSwiftDylibFile = dylibFile;
+ }
}
// <rdar://problem/25680358> verify dylibs use same version of Swift language
const char* depInstallName = dylibFile->installPath();
// <rdar://problem/17229513> embedded frameworks are only supported on iOS 8 and later
if ( (depInstallName != NULL) && (depInstallName[0] != '/') ) {
- if ( (_options.iOSVersionMin() != iOSVersionUnset) && (_options.iOSVersionMin() < iOS_8_0) ) {
+ if ( _options.platforms().contains(ld::kPlatform_iOS) && !_options.platforms().minOS(iOS_8_0) ) {
// <rdar://problem/17598404> only warn about linking against embedded dylib if it is built for iOS 8 or later
- if ( dylibFile->minOSVersion() >= iOS_8_0 )
+ if ( dylibFile->platforms().minOS(ld::iOS_8_0) )
throwf("embedded dylibs/frameworks are only supported on iOS 8.0 and later (%s)", depInstallName);
}
}
}
}
}
-
}
void Resolver::doAtom(const ld::Atom& atom)
case ld::Fixup::kindSubtractTargetAddress:
case ld::Fixup::kindStoreTargetAddressLittleEndian32:
case ld::Fixup::kindStoreTargetAddressLittleEndian64:
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64:
+#endif
case ld::Fixup::kindStoreTargetAddressBigEndian32:
case ld::Fixup::kindStoreTargetAddressBigEndian64:
case ld::Fixup::kindStoreTargetAddressX86PCRel32:
int unresolvableExportsCount = 0;
if ( unresolvableCount != 0 ) {
if ( doPrint ) {
+ for (const auto& lib : _internal.missingLinkerOptionLibraries)
+ warning("Could not find auto-linked library '%s'", lib);
+ for (const auto& frm : _internal.missingLinkerOptionFrameworks)
+ warning("Could not find auto-linked framework '%s'", frm);
if ( _options.printArchPrefix() )
fprintf(stderr, "Undefined symbols for architecture %s:\n", _options.architectureName());
else
}
_internal.classicBindingHelper = NULL;
+ // FIXME: What about fMakeThreadedStartsSection?
if ( needsStubHelper && !_options.makeCompressedDyldInfo() ) {
// "dyld_stub_binding_helper" comes from .o file, so should already exist in symbol table
if ( _symbolTable.hasName("dyld_stub_binding_helper") ) {
}
_internal.compressedFastBinderProxy = NULL;
+ // FIXME: What about fMakeThreadedStartsSection?
if ( needsStubHelper && _options.makeCompressedDyldInfo() ) {
// "dyld_stub_binder" comes from libSystem.dylib so will need to manually resolve
if ( !_symbolTable.hasName("dyld_stub_binder") ) {
// <rdar://problem/7783918> make sure there is a __text section so that codesigning works
if ( (_options.outputKind() == Options::kDynamicLibrary) || (_options.outputKind() == Options::kDynamicBundle) )
_internal.getFinalSection(*new ld::Section("__TEXT", "__text", ld::Section::typeCode));
+
+ // Don't allow swift frameworks to link other swift frameworks.
+ if ( _internal.someObjectFileHasSwift && _internal.firstSwiftDylibFile != nullptr )
+ throwf("linking swift frameworks against other swift frameworks (%s) is not permitted",
+ _internal.firstSwiftDylibFile->path());
}
void Resolver::fillInEntryPoint()
optOpt.outputFilePath = _options.outputFilePath();
optOpt.tmpObjectFilePath = _options.tempLtoObjectPath();
optOpt.ltoCachePath = _options.ltoCachePath();
+ optOpt.ltoPruneIntervalOverwrite = _options.ltoPruneIntervalOverwrite();
optOpt.ltoPruneInterval = _options.ltoPruneInterval();
optOpt.ltoPruneAfter = _options.ltoPruneAfter();
optOpt.ltoMaxCacheSize = _options.ltoMaxCacheSize();
optOpt.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions();
optOpt.simulator = _options.targetIOSSimulator();
optOpt.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable));
+#if SUPPORT_ARCH_arm64e
+ optOpt.supportsAuthenticatedPointers = _options.supportsAuthenticatedPointers();
+#endif
optOpt.bitcodeBundle = (_options.bundleBitcode() && (_options.bitcodeKind() != Options::kBitcodeMarker));
optOpt.maxDefaultCommonAlignment = _options.maxDefaultCommonAlign();
optOpt.arch = _options.architecture();
optOpt.mcpu = _options.mcpuLTO();
- optOpt.platform = _options.platform();
- optOpt.minOSVersion = _options.minOSversion();
+ optOpt.platforms = _options.platforms();
optOpt.llvmOptions = &_options.llvmOptions();
optOpt.initialUndefines = &_options.initialUndefines();
#include <string.h>
#include <unistd.h>
+#include <errno.h>
#include <stdio.h>
#include <limits.h>
#include <fcntl.h>
static const char *frameworksString = "frameworks"; // directory containing framework stubs (mach-o files)
static const char *dylibsString = "dylibs"; // directory containing dylib stubs (mach-o files)
static const char *archiveFilesString = "archive_files"; // directory containing .a files
-static const char *origCommandLineString = "orig_command_line"; // text file containing the original command line
-static const char *linkCommandString = "link_command"; // text file containing the snapshot equivalent command line
-static const char *dataFilesString = "data_files"; // arbitrary data files referenced on the command line
static const char *objectsString = "objects"; // directory containing object files
static const char *frameworkStubsString = "framework_stubs"; // directory containing framework stub info (text files)
+static const char *dataFilesString = "data_files"; // arbitrary data files referenced on the command line
static const char *dylibStubsString = "dylib_stubs"; // directory containing dylib stub info (text files)
+static const char *filesString = "files"; // directory containing files
+static const char *origCommandLineString = "orig_command_line"; // text file containing the original command line
+static const char *linkCommandString = "link_command"; // text file containing the snapshot equivalent command line
static const char *assertFileString = "assert_info"; // text file containing assertion failure logs
static const char *compileFileString = "compile_stubs"; // text file containing compile_stubs script
Snapshot *Snapshot::globalSnapshot = NULL;
-Snapshot::Snapshot() : fRecordArgs(false), fRecordObjects(false), fRecordDylibSymbols(false), fRecordArchiveFiles(false), fRecordUmbrellaFiles(false), fRecordDataFiles(false), fFrameworkArgAdded(false), fSnapshotLocation(NULL), fSnapshotName(NULL), fRootDir(NULL), fFilelistFile(-1), fCopiedArchives(NULL)
+Snapshot::Snapshot(const Options * opts) : fOptions(opts), fRecordArgs(false), fRecordObjects(false), fRecordDylibSymbols(false), fRecordArchiveFiles(false), fRecordUmbrellaFiles(false), fRecordDataFiles(false), fFrameworkArgAdded(false), fRecordKext(false), fSnapshotLocation(NULL), fSnapshotName(NULL), fRootDir(NULL), fFilelistFile(-1), fCopiedArchives(NULL)
{
if (globalSnapshot != NULL)
throw "only one snapshot supported";
void Snapshot::setSnapshotMode(SnapshotMode mode)
{
+ if (fRootDir == NULL) {
+ // free stuff
+ fRootDir = NULL;
+ }
+
if (fRootDir == NULL) {
fRecordArgs = false;
fRecordObjects = false;
fRecordArchiveFiles = false;
fRecordUmbrellaFiles = false;
fRecordDataFiles = false;
-
+ fRecordKext = false;
+
switch (mode) {
case SNAPSHOT_DISABLED:
break;
case SNAPSHOT_DEBUG:
fRecordArgs = fRecordObjects = fRecordDylibSymbols = fRecordArchiveFiles = fRecordUmbrellaFiles = fRecordDataFiles = true;
break;
+ case SNAPSHOT_KEXT:
+ fRecordKext = fRecordArgs = fRecordObjects = fRecordDylibSymbols = fRecordArchiveFiles = fRecordUmbrellaFiles = fRecordDataFiles = true;
+ break;
default:
break;
}
}
}
-void Snapshot::setSnapshotName(const char *path)
+void Snapshot::setOutputPath(const char *path)
+{
+ fOutputPath = strdup(path);
+}
+
+void Snapshot::setSnapshotName()
{
if (fRootDir == NULL) {
- const char *base = basename((char *)path);
- time_t now = time(NULL);
- struct tm t;
- localtime_r(&now, &t);
- char buf[PATH_MAX];
- snprintf(buf, sizeof(buf)-1, "%s-%4.4d-%2.2d-%2.2d-%2.2d%2.2d%2.2d.ld-snapshot", base, t.tm_year+1900, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
- fSnapshotName = strdup(buf);
+ if (fOutputPath == NULL) {
+ fSnapshotName = strdup("ld_snapshot");
+ } else {
+ const char *base = basename((char *)fOutputPath);
+ if (fRecordKext) {
+ const char *kextobjects;
+ if ((kextobjects = fOptions->kextObjectsPath())) {
+ fSnapshotLocation = strdup(kextobjects);
+ } else {
+ fSnapshotLocation = strdup(dirname((char *)fOutputPath));
+ }
+ asprintf((char **)&fSnapshotName, "%s.%s.ld", base, fArchString);
+ } else {
+ time_t now = time(NULL);
+ struct tm t;
+ localtime_r(&now, &t);
+ char buf[PATH_MAX];
+ snprintf(buf, sizeof(buf)-1, "%s-%4.4d-%2.2d-%2.2d-%2.2d%2.2d%2.2d.ld-snapshot", base, t.tm_year+1900, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
+ fSnapshotName = strdup(buf);
+ }
+ }
}
}
if (subdir) {
strcat(buf, subdir);
// implicitly create the subdirectory
- mkdir(buf, S_IRUSR|S_IWUSR|S_IXUSR);
+ mode_t mode = fRecordKext ? (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) : (S_IRUSR|S_IWUSR|S_IXUSR);
+ mkdir(buf, mode);
strcat(buf, "/");
}
if (file != NULL)
{
buildPath(buf, subdir, file);
struct stat st;
- if (stat(buf, &st)==0) {
+ if (!fRecordKext && (stat(buf, &st)==0)) {
// make it unique
int counter=1;
char *number = strrchr(buf, 0);
}
}
+const char * Snapshot::subdir(const char *subdir)
+{
+ if (fRecordKext) {
+ return filesString;
+ }
+ return subdir;
+}
// Copy a file to the snapshot.
// sourcePath is the original file
{
const int copyBufSize=(1<<14); // 16kb buffer
static void *copyBuf = NULL;
+ bool inSdk;
+
+ if (fRecordKext) {
+ for (const char* sdkPath : fOptions->sdkPaths()) {
+ const char *toolchainPath;
+ inSdk = (!strncmp(sdkPath, sourcePath, strlen(sdkPath)));
+ if (!inSdk && (toolchainPath = fOptions->toolchainPath()))
+ inSdk = (!strncmp(toolchainPath, sourcePath, strlen(toolchainPath)));
+ if (inSdk) {
+ if (path) {
+ strcpy(path, sourcePath);
+ }
+ return;
+ }
+ }
+ }
+
if (copyBuf == NULL)
copyBuf = malloc(copyBufSize);
char buf[PATH_MAX];
if (path == NULL) path = buf;
buildUniquePath(path, subdir, file);
- int out_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+ mode_t mode = fRecordKext ? (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR);
+ int out_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
int in_fd = open(sourcePath, O_RDONLY);
int len;
if (out_fd != -1 && in_fd != -1) {
}
close(in_fd);
close(out_fd);
-}
+ const char * relPath = snapshotRelativePath(path);
+ memmove(path, relPath, 1+strlen(relPath));
+}
// Create the snapshot root directory.
void Snapshot::createSnapshot()
{
if (fRootDir == NULL) {
+
+ mode_t mask = umask(0);
+
// provide default name and location
+ setSnapshotName();
if (fSnapshotLocation == NULL)
fSnapshotLocation = "/tmp";
- if (fSnapshotName == NULL) {
- setSnapshotName("ld_snapshot");
- }
-
+
char buf[PATH_MAX];
fRootDir = (char *)fSnapshotLocation;
buildUniquePath(buf, NULL, fSnapshotName);
fRootDir = strdup(buf);
- if (mkdir(fRootDir, S_IRUSR|S_IWUSR|S_IXUSR)!=0) {
+
+ int mkpatherr = mkpath_np(fRootDir, (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH|S_IXUSR|S_IXGRP|S_IXOTH));
+ if ((mkpatherr!=0) && !(fRecordKext && (mkpatherr==EEXIST))) {
warning("unable to create link snapshot directory: %s", fRootDir);
fRootDir = NULL;
setSnapshotMode(SNAPSHOT_DISABLED); // don't try to write anything if we can't create snapshot dir
}
-
- buildPath(buf, NULL, compileFileString);
- int compileScript = open(buf, O_WRONLY|O_CREAT|O_TRUNC, S_IXUSR|S_IRUSR|S_IWUSR);
- write(compileScript, compile_stubs, strlen(compile_stubs));
- close(compileScript);
+
+ if (!fRecordKext) {
+ buildPath(buf, NULL, compileFileString);
+ mode_t mode = fRecordKext ? (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IXUSR|S_IRUSR|S_IWUSR);
+ int compileScript = open(buf, O_WRONLY|O_CREAT|O_TRUNC, mode);
+ write(compileScript, compile_stubs, strlen(compile_stubs));
+ close(compileScript);
+ }
SnapshotLog::iterator it;
for (it = fLog.begin(); it != fLog.end(); it++) {
fLog.erase(fLog.begin(), fLog.end());
if (fRecordArgs) {
- writeCommandLine(fRawArgs, origCommandLineString, true);
- writeCommandLine(fArgs);
+ writeCommandLine(true);
+ writeCommandLine();
}
#if STORE_PID_IN_SNAPSHOT
char path[PATH_MAX];
buildUniquePath(path, NULL, pidString);
- int pidfile = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+ mode = fRecordKext ? (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR);
+ int pidfile = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
char pid_buf[32];
sprintf(pid_buf, "%lu\n", (long unsigned)getpid());
write(pidfile, pid_buf, strlen(pid_buf));
write(pidfile, "\n", 1);
close(pidfile);
#endif
-
+ umask(mask);
}
}
// Write the current command line vector to filename.
-void Snapshot::writeCommandLine(StringVector &args, const char *filename, bool includeCWD)
+void Snapshot::writeCommandLine(bool rawArgs)
{
+ StringVector &args = rawArgs ? fRawArgs : fArgs;
+ const char *filename;
+
+ if (rawArgs) {
+ args = fRawArgs;
+ filename = origCommandLineString;
+ } else {
+ args = fArgs;
+ filename = linkCommandString;
+ }
+
if (!isLazy() && fRecordArgs) {
- // Figure out the file name and open it.
- if (filename == NULL)
- filename = linkCommandString;
+ // Open the file
char path[PATH_MAX];
buildPath(path, NULL, filename);
- int argsFile = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IXUSR|S_IRUSR|S_IWUSR);
+
+ mode_t mode = fRecordKext ? (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IXUSR|S_IRUSR|S_IWUSR);
+ int argsFile = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
FILE *argsStream = fdopen(argsFile, "w");
- if (includeCWD)
+ if (rawArgs)
fprintf(argsStream, "cd %s\n", getcwd(path, sizeof(path)));
// iterate to write args, quoting as needed
- StringVector::iterator it;
- for (it = args.begin(); it != args.end(); it++) {
- const char *arg = *it;
+ unsigned idx;
+ unsigned idxidx;
+ bool inner = false;
+
+ for (idx = idxidx = 0; idx < args.size(); idx++) {
+ const char *arg = args[idx];
bool needQuotes = false;
+
+ if (fRecordKext && !rawArgs) {
+ if (idx == fArgIndicies[idxidx]) {
+ idxidx++;
+ if (idx > 0) {
+ fprintf(argsStream, "\n");
+ inner = false;
+ }
+ }
+ }
for (const char *c = arg; *c != 0 && !needQuotes; c++) {
if (isspace(*c))
needQuotes = true;
}
- if (it != args.begin()) fprintf(argsStream, " ");
+ if (inner) fprintf(argsStream, " ");
+ inner = true;
if (needQuotes) fprintf(argsStream, "\"");
fprintf(argsStream, "%s", arg);
if (needQuotes) fprintf(argsStream, "\"");
for (int i=0; i<argc; i++) {
fRawArgs.push_back(argv[i]);
}
+ fArgIndicies.push_back(fArgs.size());
fArgs.insert(fArgs.begin(), argv[0]);
+ fArgIndicies.push_back(fArgs.size());
fArgs.insert(fArgs.begin()+1, "-Z"); // don't search standard paths when running in the snapshot
}
fLog.push_back(Block_copy(^{ this->addSnapshotLinkArg(argIndex, argCount, fileArg); }));
} else {
char buf[PATH_MAX];
- const char *subdir = dataFilesString;
+ fArgIndicies.push_back(fArgs.size());
for (int i=0, arg=argIndex; i<argCount && argIndex+1<(int)fRawArgs.size(); i++, arg++) {
if (i != fileArg) {
fArgs.push_back(fRawArgs[arg]);
} else {
if (fRecordDataFiles) {
- copyFileToSnapshot(fRawArgs[arg], subdir, buf);
- fArgs.push_back(strdup(snapshotRelativePath(buf)));
+ copyFileToSnapshot(fRawArgs[arg], subdir(dataFilesString), buf);
+ fArgs.push_back(strdup(buf));
} else {
// if we don't copy the file then just record the original path
fArgs.push_back(strdup(fRawArgs[arg]));
if (fRawArgs.size() == 0)
throw "raw args not set";
- // only need to store the arch explicitly if it is not mentioned on the command line
+ fArchString = strdup(arch);
+
+ // only need to add the arch argument explicitly if it is not mentioned on the command line
bool archInArgs = false;
StringVector::iterator it;
for (it = fRawArgs.begin(); it != fRawArgs.end() && !archInArgs; it++) {
} else {
char path_buf[PATH_MAX];
buildUniquePath(path_buf, NULL, "arch");
- int fd=open(path_buf, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+ mode_t mode = fRecordKext ? (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR);
+ int fd=open(path_buf, O_WRONLY|O_CREAT|O_TRUNC, mode);
write(fd, arch, strlen(arch));
close(fd);
}
} else {
if (fRecordObjects) {
char path_buf[PATH_MAX];
- copyFileToSnapshot(path, objectsString, path_buf);
+ copyFileToSnapshot(path, subdir(objectsString), path_buf);
// lazily open the filelist file
if (fFilelistFile == -1) {
char filelist_path[PATH_MAX];
- buildUniquePath(filelist_path, objectsString, "filelist");
- fFilelistFile = open(filelist_path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
- fArgs.push_back("-filelist");
- fArgs.push_back(strdup(snapshotRelativePath(filelist_path)));
- writeCommandLine(fArgs);
+ const char * dir;
+ dir = (fRecordKext ? NULL : subdir(objectsString));
+ buildUniquePath(filelist_path, dir, "filelist");
+ mode_t mode = fRecordKext ? (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR);
+ fFilelistFile = open(filelist_path, O_WRONLY|O_CREAT|O_TRUNC, mode);
+
+ if (!fRecordKext) {
+ fArgIndicies.push_back(fArgs.size());
+ fArgs.push_back("-filelist");
+ fArgs.push_back(strdup(snapshotRelativePath(filelist_path)));
+ writeCommandLine();
+ }
}
// record the snapshot path in the filelist
- const char *relative_path = snapshotRelativePath(path_buf);
- write(fFilelistFile, relative_path, strlen(relative_path));
+ write(fFilelistFile, path_buf, strlen(path_buf));
write(fFilelistFile, "\n", 1);
}
}
if (!found) {
if (!fFrameworkArgAdded) {
fFrameworkArgAdded = true;
+ fArgIndicies.push_back(fArgs.size());
fArgs.push_back("-Fframeworks");
}
+ fArgIndicies.push_back(fArgs.size());
fArgs.push_back("-framework");
fArgs.push_back(strdup(framework));
- writeCommandLine(fArgs);
+ writeCommandLine();
}
}
}
if (!found) {
char buf[ARG_MAX];
- sprintf(buf, "%s/%s", dylibsString, dylib);
+ sprintf(buf, "%s/%s", subdir(dylibsString), dylib);
+ fArgIndicies.push_back(fArgs.size());
fArgs.push_back(strdup(buf));
- writeCommandLine(fArgs);
+ writeCommandLine();
}
}
// find the dylib in the table
DylibMap::iterator it;
const char *dylibPath = dylibFile->path();
+
it = fDylibSymbols.find(dylibPath);
bool isFramework = (strstr(dylibPath, "framework") != NULL);
int dylibFd;
if (it == fDylibSymbols.end()) {
// Didn't find a file descriptor for this dylib. Create one and add it to the dylib map.
char path_buf[PATH_MAX];
- buildUniquePath(path_buf, isFramework ? frameworkStubsString : dylibStubsString, dylibPath);
- dylibFd = open(path_buf, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
+ buildUniquePath(path_buf, subdir(isFramework ? frameworkStubsString : dylibStubsString), dylibPath);
+
+ mode_t mode = fRecordKext ? (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR);
+ dylibFd = open(path_buf, O_WRONLY|O_APPEND|O_CREAT, mode);
fDylibSymbols.insert(std::pair<const char *, int>(dylibPath, dylibFd));
char *base_name = strdup(basename(path_buf));
if (isFramework) {
} else {
addDylibArg(base_name);
}
- writeCommandLine(fArgs);
+ writeCommandLine();
} else {
dylibFd = it->second;
}
fLog.push_back(Block_copy(^{ this->recordArchive(archiveFile); ::free((void *)copy); }));
} else {
if (fRecordArchiveFiles) {
+
// lazily create a vector of .a files that have been added
if (fCopiedArchives == NULL) {
fCopiedArchives = new StringVector;
if (!found) {
char path[PATH_MAX];
fCopiedArchives->push_back(archiveFile);
- copyFileToSnapshot(archiveFile, archiveFilesString, path);
- fArgs.push_back(strdup(snapshotRelativePath(path)));
- writeCommandLine(fArgs);
+
+ if (fRecordKext) {
+ recordObjectFile(archiveFile);
+ } else {
+ copyFileToSnapshot(archiveFile, subdir(archiveFilesString), path);
+ fArgIndicies.push_back(fArgs.size());
+ fArgs.push_back(strdup(path));
+ writeCommandLine();
+ }
}
}
}
if (fRecordUmbrellaFiles) {
const char *framework = basename((char *)frameworkPath);
char buf[PATH_MAX], wrapper[PATH_MAX];
- strcpy(wrapper, frameworksString);
+ strcpy(wrapper, subdir(frameworksString));
buildPath(buf, wrapper, NULL); // ensure the frameworks directory exists
strcat(wrapper, "/");
strcat(wrapper, framework);
fLog.push_back(Block_copy(^{ this->recordSubLibrary(copy); ::free((void *)copy); }));
} else {
if (fRecordUmbrellaFiles) {
- copyFileToSnapshot(dylibPath, dylibsString);
+ copyFileToSnapshot(dylibPath, subdir(dylibsString));
addDylibArg(basename((char *)dylibPath));
}
}
} else {
char path[PATH_MAX];
buildPath(path, NULL, assertFileString);
- int log = open(path, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
+ mode_t mode = fRecordKext ? (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR);
+ int log = open(path, O_WRONLY|O_APPEND|O_CREAT, mode);
write(log, msg, strlen(msg));
close(log);
free(msg);
typedef enum {
SNAPSHOT_DISABLED, // nothing is recorded
SNAPSHOT_DEBUG, // records: .o, .dylib, .framework, .a, and other data files
+ SNAPSHOT_KEXT, // records: .o, .a, and other data files
} SnapshotMode;
- Snapshot();
+ Snapshot(const Options * opts);
~Snapshot();
// Control the data captured in the snapshot
// Use the basename of path to construct the snapshot name.
// Must be called prior to createSnapshot().
- void setSnapshotName(const char *path);
+ void setOutputPath(const char *path);
// Set the directory in which the snapshot will be created.
// Must be called prior to createSnapshot().
typedef std::vector<const char *> StringVector;
typedef std::map<const char *, int, strcompclass > DylibMap;
typedef std::map<const char *, const char *, strcompclass> PathMap;
-
+ typedef std::vector<unsigned> IntVector;
// Write the current contents of the args vector to a file in the snapshot.
// If filename is NULL then "link_command" is used.
// This is used to write both the original and the "cooked" versions of the link command
- void writeCommandLine(StringVector &args, const char *filename=NULL, bool includeCWD=false);
+ void writeCommandLine(bool rawArgs=false);
+
+ //
+ void setSnapshotName();
+
+ //
+ const char * subdir(const char *subdir);
// Construct a path in the snapshot.
// buf is a sring buffer in which the path is constructed
void addFrameworkArg(const char *framework);
void addDylibArg(const char *dylib);
+ const Options * fOptions;
SnapshotLog fLog; // log of events that recorded data in a snapshot prior to createSnapshot()
bool fRecordArgs; // record command line
bool fRecordObjects; // record .o files
bool fRecordUmbrellaFiles; // record re-exported sub frameworks/dylibs
bool fRecordDataFiles; // record other data files
bool fFrameworkArgAdded;
+ bool fRecordKext;
const char *fSnapshotLocation; // parent directory of frootDir
const char *fSnapshotName; // a string to use in constructing the snapshot name
+ const char *fOutputPath; // -o path
char *fRootDir; // root directory of the snapshot
+ const char *fArchString;
int fFilelistFile; // file descriptor to the open text file used for the -filelist
StringVector fRawArgs; // stores the raw command line args
StringVector fArgs; // stores the "cooked" command line args
+ IntVector fArgIndicies; // where args start in fArgs
PathMap fPathMap; // mapping of original paths->snapshot paths for copied files
DylibMap fDylibSymbols; // map of dylib names to string vector containing referenced symbol names
#include "passes/compact_unwind.h"
#include "passes/order.h"
#include "passes/branch_island.h"
+#include "passes/thread_starts.h"
#include "passes/branch_shim.h"
#include "passes/objc.h"
#include "passes/dylibs.h"
#include "parsers/lto_file.h"
#include "parsers/opaque_section_file.h"
+const ld::VersionSet ld::File::_platforms;
struct PerformanceStatistics {
uint64_t startTool;
{
public:
FinalSection(const ld::Section& sect, uint32_t sectionsSeen, const Options&);
- static int sectionComparer(const void* l, const void* r);
static const ld::Section& outputSection(const ld::Section& sect, bool mergeZeroFill);
static const ld::Section& objectOutputSection(const ld::Section& sect, const Options&);
private:
return 12;
case ld::Section::typeStubHelper:
return 13;
+ case ld::Section::typeThreadStarts:
+ return INT_MAX-5;
case ld::Section::typeLSDA:
return INT_MAX-4;
case ld::Section::typeUnwindInfo:
return 32;
else if ( strcmp(sect.sectionName(), "__objc_data") == 0 )
return 33;
+ else if ( strcmp(sect.sectionName(), "__objc_const_ax") == 0 )
+ return 34;
else
return sectionsSeen+40;
}
if ( (sectType != ld::Section::typeZeroFill)
&& (sectType != ld::Section::typeUnclassified)
&& (sectType != ld::Section::typeTentativeDefs)
+ && (sectType != ld::Section::typeTLVDefs)
&& (sectType != ld::Section::typeDyldInfo) ) {
if ( !wildCardMatch )
warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not data (is %d)", atom.name(), path, dstSeg, sectType);
}
}
}
+ else if ( atom.symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages ) {
+ const char* symName = atom.name();
+ if ( strncmp(symName, "l_OBJC_$_INSTANCE_METHODS_", 26) == 0 ) {
+ if ( _options.moveAXMethodList(&symName[26]) ) {
+ curSectName = "__objc_const_ax";
+ fs = this->getFinalSection(curSegName, curSectName, sectType);
+ if ( _options.traceSymbolLayout() )
+ printf("symbol '%s', .axsymbol mapped it to %s/%s\n", atom.name(), curSegName, curSectName);
+ }
+ }
+ else if ( strncmp(symName, "l_OBJC_$_CLASS_METHODS_", 23) == 0 ) {
+ if ( _options.moveAXMethodList(&symName[23]) ) {
+ curSectName = "__objc_const_ax";
+ fs = this->getFinalSection(curSegName, curSectName, sectType);
+ if ( _options.traceSymbolLayout() )
+ printf("symbol '%s', .axsymbol mapped it to %s/%s\n", atom.name(), curSegName, curSectName);
+ }
+ }
+ }
+
// support for -rename_section and -rename_segment
for (const Options::SectionRename& rename : _options.sectionRenames()) {
if ( (strcmp(curSectName, rename.fromSection) == 0) && (strcmp(curSegName, rename.fromSegment) == 0) ) {
}
-int InternalState::FinalSection::sectionComparer(const void* l, const void* r)
-{
- const FinalSection* left = *(FinalSection**)l;
- const FinalSection* right = *(FinalSection**)r;
- if ( left->_segmentOrder != right->_segmentOrder )
- return (left->_segmentOrder - right->_segmentOrder);
- return (left->_sectionOrder - right->_sectionOrder);
-}
-
void InternalState::sortSections()
{
//fprintf(stderr, "UNSORTED final sections:\n");
//for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
// fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName());
//}
- qsort(§ions[0], sections.size(), sizeof(FinalSection*), &InternalState::FinalSection::sectionComparer);
+ std::stable_sort(sections.begin(), sections.end(), [](const ld::Internal::FinalSection* l, const ld::Internal::FinalSection* r) {
+ const FinalSection* left = (FinalSection*)l;
+ const FinalSection* right = (FinalSection*)r;
+ if ( left->_segmentOrder != right->_segmentOrder )
+ return (left->_segmentOrder < right->_segmentOrder);
+ return (left->_sectionOrder < right->_sectionOrder);
+ });
//fprintf(stderr, "SORTED final sections:\n");
//for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) {
// fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName());
ld::passes::dtrace::doPass(options, state);
ld::passes::compact_unwind::doPass(options, state); // must be after order pass
ld::passes::bitcode_bundle::doPass(options, state); // must be after dylib
+
+ // Sort again so that we get the segments in order.
+ state.sortSections();
+ ld::passes::thread_starts::doPass(options, state); // must be after dylib
// sort final sections
state.sortSections();
namespace ld {
+//
+// platform
+//
+
+enum Platform {
+ kPlatform_unknown=0,
+ kPlatform_macOS=1,
+ kPlatform_iOS=2,
+ kPlatform_tvOS=3,
+ kPlatform_watchOS=4,
+ kPlatform_bridgeOS=5,
+ kPlatform_iOSMac=6,
+ kPlatform_iOSSimulator=7,
+ kPlatform_tvOSSimulator=8,
+ kPlatform_watchOSSimulator=9
+};
+
+typedef std::set<Platform> PlatformSet;
+
+//
+// minumum OS versions
+//
+
+typedef std::pair<Platform, uint32_t> Version;
+
+struct VersionSet {
+private:
+ std::map<Platform, uint32_t> _versions;
+public:
+ VersionSet() {}
+ VersionSet(const std::map<Platform, uint32_t>& P) : _versions(P) {}
+ void add(ld::Version platformVersion) {
+ _versions.insert(platformVersion);
+ }
+ void erase(const Platform& platform) {
+ _versions.erase(platform);
+ }
+ size_t count() const { return _versions.size(); }
+ size_t empty() const { return _versions.empty(); }
+
+ void forEach(void (^callback)(ld::Platform platform, uint32_t version, bool &stop)) const {
+ bool stop = false;
+ for (const auto& version : _versions) {
+ callback(version.first, version.second, stop);
+ if (stop)
+ return;
+ }
+ }
+
+ bool contains(ld::Platform platform) const { return _versions.count(platform) != 0; }
+ bool contains(ld::PlatformSet platforms) const {
+ __block bool retval = true;
+ forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ if (platforms.find(platform) == platforms.end()) {
+ stop = true;
+ retval = false;
+ }
+ });
+ return retval;
+ }
+
+ uint32_t minOS(const ld::Platform& platform) const {
+ for (const auto& version : _versions) {
+ if (basePlatform(version.first) == platform) {
+ return version.second;
+ }
+ }
+ return 0;
+ }
+
+ bool minOS(const Version& version) const {
+ return minOS(version.first) >= version.second;
+ }
+
+ const ld::Platform basePlatform(const ld::Platform& platform) const {
+ switch(platform) {
+ case kPlatform_iOSMac:
+ case kPlatform_iOSSimulator:
+ return kPlatform_iOS;
+ case kPlatform_watchOSSimulator:
+ return kPlatform_watchOS;
+ case kPlatform_tvOSSimulator:
+ return kPlatform_tvOS;
+ default:
+ return platform;
+ }
+ }
+
+ bool minOS(const ld::VersionSet& requiredMinVersions) const {
+ __block bool retval = true;
+ forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ if (!requiredMinVersions.contains(basePlatform(platform)))
+ return;
+ if (version < requiredMinVersions.minOS(basePlatform(platform))) {
+ stop = true;
+ retval = false;
+ }
+ });
+ return retval;
+ }
+
+ std::string to_str() const {
+ std::string retval;
+ auto appendPlatform = [&](const std::string& platform) {
+ if (retval.empty()) {
+ retval = platform;
+ } else {
+ retval += "/";
+ retval += platform;
+ }
+ };
+
+ forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ switch (platform) {
+ case ld::kPlatform_macOS: appendPlatform("macOS"); break;
+ case ld::kPlatform_iOSMac: appendPlatform("iOSMac"); break;
+ case ld::kPlatform_iOS: appendPlatform("iOS"); break;
+ case ld::kPlatform_iOSSimulator: appendPlatform("iOS Simulator"); break;
+ case ld::kPlatform_watchOS: appendPlatform("watchOS"); break;
+ case ld::kPlatform_watchOSSimulator: appendPlatform("watchOS Simulator"); break;
+ case ld::kPlatform_tvOS: appendPlatform("tvOS"); break;
+ case ld::kPlatform_tvOSSimulator: appendPlatform("tvOS Simulator"); break;
+ case ld::kPlatform_bridgeOS: appendPlatform("bridgeOS"); break;
+ case ld::kPlatform_unknown: appendPlatform("Unkown"); break;
+ }
+ });
+
+ return retval;
+ }
+};
+
+static const Version mac10_4 ({kPlatform_macOS, 0x000A0400});
+static const Version mac10_5 ({kPlatform_macOS, 0x000A0500});
+static const Version mac10_6 ({kPlatform_macOS, 0x000A0600});
+static const Version mac10_7 ({kPlatform_macOS, 0x000A0700});
+static const Version mac10_8 ({kPlatform_macOS, 0x000A0800});
+static const Version mac10_9 ({kPlatform_macOS, 0x000A0900});
+static const Version mac10_12 ({kPlatform_macOS, 0x000A0C00});
+static const Version mac10_14 ({kPlatform_macOS, 0x000A0E00});
+static const Version mac10_Future ({kPlatform_macOS, 0x10000000});
+
+static const Version iOS_2_0 ({kPlatform_iOS, 0x00020000});
+static const Version iOS_3_1 ({kPlatform_iOS, 0x00030100});
+static const Version iOS_4_2 ({kPlatform_iOS, 0x00040200});
+static const Version iOS_4_3 ({kPlatform_iOS, 0x00040300});
+static const Version iOS_5_0 ({kPlatform_iOS, 0x00050000});
+static const Version iOS_6_0 ({kPlatform_iOS, 0x00060000});
+static const Version iOS_7_0 ({kPlatform_iOS, 0x00070000});
+static const Version iOS_8_0 ({kPlatform_iOS, 0x00080000});
+static const Version iOS_9_0 ({kPlatform_iOS, 0x00090000});
+static const Version iOS_10_0 ({kPlatform_iOS, 0x000A0000});
+static const Version iOS_11_0 ({kPlatform_iOS, 0x000B0000});
+static const Version iOS_12_0 ({kPlatform_iOS, 0x000C0000});
+static const Version iOS_Future ({kPlatform_iOS, 0x10000000});
+
+static const Version watchOS_1_0 ({kPlatform_watchOS, 0x00010000});
+static const Version watchOS_2_0 ({kPlatform_watchOS, 0x00020000});
+static const Version watchOS_5_0 ({kPlatform_watchOS, 0x00050000});
+static const Version watchOS_Future ({kPlatform_watchOS, 0x10000000});
+
+static const Version tvOS_9_0 ({kPlatform_tvOS, 0x00090000});
+static const Version tvOS_12_0 ({kPlatform_tvOS, 0x000C0000});
+static const Version tvOS_Future ({kPlatform_tvOS, 0x10000000});
+
+static const Version bridgeOS_1_0 ({kPlatform_bridgeOS, 0x00010000});
+static const Version bridgeOS_Future ({kPlatform_bridgeOS, 0x10000000});
+
+// Platform Sets
+
+static const PlatformSet simulatorPlatforms ( {kPlatform_iOSSimulator, kPlatform_tvOSSimulator, kPlatform_watchOSSimulator} );
+
+//FIXME do we need to add simulatots to these?
+//FIXME Are the dates correct?
+static const VersionSet version2007 ({mac10_4, iOS_2_0});
+static const VersionSet version2008 ({mac10_5, iOS_2_0});
+static const VersionSet version2008Fall ({mac10_5, iOS_3_1});
+static const VersionSet version2009 ({mac10_6, iOS_3_1});
+static const VersionSet version2010 ({mac10_7, iOS_4_2});
+static const VersionSet version2010Fall ({mac10_7, iOS_4_3});
+
+static const VersionSet version2012 ({mac10_8, iOS_6_0});
+static const VersionSet version2013 ({mac10_9, iOS_7_0});
+
+static const VersionSet supportsSplitSegV2 ({mac10_12, iOS_9_0, watchOS_2_0, tvOS_9_0});
+// FIXME: Use the comment out line instead.
+static const VersionSet supportsLCBuildVersion ({mac10_14, iOS_12_0, watchOS_5_0, tvOS_12_0, bridgeOS_1_0});
+static const VersionSet supportsPIE ({mac10_5, iOS_4_2});
+static const VersionSet supportsTLV ({mac10_7, iOS_9_0});
+
// Forward declaration for bitcode support
class Bitcode;
class File
{
public:
- enum ObjcConstraint { objcConstraintNone, objcConstraintRetainRelease,
- objcConstraintRetainReleaseOrGC, objcConstraintGC,
- objcConstraintRetainReleaseForSimulator };
-
+
class AtomHandler {
public:
virtual ~AtomHandler() {}
Ordinal ordinal() const { return _ordinal; }
virtual bool forEachAtom(AtomHandler&) const = 0;
virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const = 0;
- virtual ObjcConstraint objCConstraint() const { return objcConstraintNone; }
- virtual bool objcHasCategoryClassPropertiesField() const { return false; }
- virtual uint8_t swiftVersion() const { return 0; }
+ virtual uint8_t swiftVersion() const { return 0; }
virtual uint32_t cpuSubType() const { return 0; }
virtual uint32_t subFileCount() const { return 1; }
- virtual uint32_t minOSVersion() const { return 0; }
- virtual uint32_t platform() const { return 0; }
+ virtual const VersionSet& platforms() const { return _platforms; }
bool fileExists() const { return _modTime != 0; }
Type type() const { return _type; }
virtual Bitcode* getBitcode() const { return NULL; }
time_t _modTime;
const Ordinal _ordinal;
const Type _type;
+ // Note this is just a placeholder as platforms() needs something to return
+ static const VersionSet _platforms;
};
-
-//
-// minumum OS versions
-//
-enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500,
- mac10_6=0x000A0600, mac10_7=0x000A0700, mac10_8=0x000A0800,
- mac10_9=0x000A0900, mac10_12=0x000A0C00, mac10_Future=0x10000000 };
-enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100,
- iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000,
- iOS_6_0=0x00060000, iOS_7_0=0x00070000, iOS_8_0=0x00080000,
- iOS_9_0=0x00090000, iOS_10_0=0x000A0000, iOS_Future=0x10000000};
-enum WatchOSVersionMin { wOSVersionUnset=0, wOS_1_0=0x00010000, wOS_2_0=0x00020000 };
-
-
namespace relocatable {
//
// ld::relocatable::File
virtual bool canScatterAtoms() const = 0;
virtual bool hasLongBranchStubs() { return false; }
virtual bool hasllvmProfiling() const { return false; }
+ virtual bool hasObjC() const { return false; }
+ virtual bool objcHasCategoryClassPropertiesField() const { return false; }
virtual LinkerOptionsList* linkerOptions() const = 0;
virtual const ToolVersionList& toolVersions() const = 0;
virtual SourceKind sourceKind() const { return kSourceUnknown; }
typeCFI, typeLSDA, typeDtraceDOF, typeUnwindInfo, typeObjCClassRefs, typeObjC2CategoryList,
typeZeroFill, typeTentativeDefs, typeLazyPointer, typeStub, typeNonLazyPointer, typeDyldInfo,
typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers,
- typeStubClose, typeLazyPointerClose, typeAbsoluteSymbols,
+ typeStubClose, typeLazyPointerClose, typeAbsoluteSymbols, typeThreadStarts,
typeTLVDefs, typeTLVZeroFill, typeTLVInitialValues, typeTLVInitializerPointers, typeTLVPointers,
typeFirstSection, typeLastSection, typeDebug, typeSectCreate };
kindStoreTargetAddressARM64TLVPLoadPageOff12,// kindSetTargetAddress + kindStoreARM64TLVPLoadPageOff12
kindStoreTargetAddressARM64TLVPLoadNowLeaPage21, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPage21
kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPageOff12
+#endif
+#if SUPPORT_ARCH_arm64e
+ kindStoreLittleEndianAuth64,
+ kindStoreTargetAddressLittleEndianAuth64, // kindSetTargetAddress + kindStoreLittleEndianAuth64
+ kindSetAuthData,
#endif
};
+#if SUPPORT_ARCH_arm64e
+ struct AuthData {
+ // clang encodes the combination of the key bits as these values.
+ typedef enum {
+ ptrauth_key_asia = 0,
+ ptrauth_key_asib = 1,
+ ptrauth_key_asda = 2,
+ ptrauth_key_asdb = 3,
+ } ptrauth_key;
+
+ uint16_t discriminator;
+ bool hasAddressDiversity;
+ ptrauth_key key;
+ };
+#endif
+
union {
const Atom* target;
const char* name;
uint64_t addend;
uint32_t bindingIndex;
+#if SUPPORT_ARCH_arm64e
+ AuthData authData;
+#endif
} u;
uint32_t offsetInAtom;
Kind kind : 8;
binding(Fixup::bindingNone),
contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false)
{ u.addend = addend; }
+
+#if SUPPORT_ARCH_arm64e
+ Fixup(uint32_t off, Cluster c, Kind k, AuthData authData) :
+ offsetInAtom(off), kind(k), clusterSize(c), weakImport(false),
+ binding(Fixup::bindingNone),
+ contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false)
+ { u.authData = authData; }
+#endif
Fixup(Kind k, uint32_t lohKind, uint32_t off1, uint32_t off2) :
offsetInAtom(off1), kind(k), clusterSize(k1of1),
}
return false;
}
+
+ bool isStore() const {
+ switch ( kind ) {
+ case ld::Fixup::kindNone:
+ case ld::Fixup::kindNoneFollowOn:
+ case ld::Fixup::kindNoneGroupSubordinate:
+ case ld::Fixup::kindNoneGroupSubordinateFDE:
+ case ld::Fixup::kindNoneGroupSubordinateLSDA:
+ case ld::Fixup::kindNoneGroupSubordinatePersonality:
+ case ld::Fixup::kindSetTargetAddress:
+ case ld::Fixup::kindSubtractTargetAddress:
+ case ld::Fixup::kindAddAddend:
+ case ld::Fixup::kindSubtractAddend:
+ case ld::Fixup::kindSetTargetImageOffset:
+ case ld::Fixup::kindSetTargetSectionOffset:
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindSetAuthData:
+#endif
+ return false;
+ default:
+ break;
+ }
+ return true;
+ }
+
+
+ bool setsTarget(bool isObjectFile) const {
+ switch ( kind ) {
+ case ld::Fixup::kindSetTargetAddress:
+ case ld::Fixup::kindLazyTarget:
+ case ld::Fixup::kindStoreTargetAddressLittleEndian32:
+ case ld::Fixup::kindStoreTargetAddressLittleEndian64:
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64:
+#endif
+ case ld::Fixup::kindStoreTargetAddressBigEndian32:
+ case ld::Fixup::kindStoreTargetAddressBigEndian64:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32:
+ case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA:
+ case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad:
+ case ld::Fixup::kindStoreTargetAddressARMBranch24:
+ case ld::Fixup::kindStoreTargetAddressThumbBranch22:
+ case ld::Fixup::kindStoreTargetAddressARMLoad12:
+#if SUPPORT_ARCH_arm64
+ case ld::Fixup::kindStoreTargetAddressARM64Branch26:
+ case ld::Fixup::kindStoreTargetAddressARM64Page21:
+ case ld::Fixup::kindStoreTargetAddressARM64PageOff12:
+ case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21:
+ 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::kindStoreX86DtraceIsEnableSiteClear:
+ case ld::Fixup::kindStoreARMDtraceCallSiteNop:
+ case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear:
+ case ld::Fixup::kindStoreARM64DtraceCallSiteNop:
+ case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear:
+ case ld::Fixup::kindStoreThumbDtraceCallSiteNop:
+ case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear:
+ return isObjectFile;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ bool isPcRelStore(bool isKextBundle) const {
+ switch ( kind ) {
+ case ld::Fixup::kindStoreX86BranchPCRel8:
+ case ld::Fixup::kindStoreX86BranchPCRel32:
+ case ld::Fixup::kindStoreX86PCRel8:
+ case ld::Fixup::kindStoreX86PCRel16:
+ case ld::Fixup::kindStoreX86PCRel32:
+ case ld::Fixup::kindStoreX86PCRel32_1:
+ case ld::Fixup::kindStoreX86PCRel32_2:
+ case ld::Fixup::kindStoreX86PCRel32_4:
+ case ld::Fixup::kindStoreX86PCRel32GOTLoad:
+ case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA:
+ case ld::Fixup::kindStoreX86PCRel32GOT:
+ case ld::Fixup::kindStoreX86PCRel32TLVLoad:
+ case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA:
+ case ld::Fixup::kindStoreARMBranch24:
+ case ld::Fixup::kindStoreThumbBranch22:
+ case ld::Fixup::kindStoreARMLoad12:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA:
+ case ld::Fixup::kindStoreTargetAddressARMBranch24:
+ case ld::Fixup::kindStoreTargetAddressThumbBranch22:
+ case ld::Fixup::kindStoreTargetAddressARMLoad12:
+#if SUPPORT_ARCH_arm64
+ case ld::Fixup::kindStoreARM64Page21:
+ case ld::Fixup::kindStoreARM64PageOff12:
+ case ld::Fixup::kindStoreARM64GOTLoadPage21:
+ 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::kindStoreTargetAddressARM64GOTLoadPage21:
+ 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:
+#if SUPPORT_ARCH_arm64
+ case ld::Fixup::kindStoreTargetAddressARM64Branch26:
+#endif
+ return !isKextBundle;
+#if SUPPORT_ARCH_arm64
+ case ld::Fixup::kindStoreARM64Branch26:
+#endif
+ return !isKextBundle;
+ default:
+ break;
+ }
+ return false;
+ }
union LOH_arm64 {
uint64_t addend;
Internal() : bundleLoader(NULL),
entryPoint(NULL), classicBindingHelper(NULL),
lazyBindingHelper(NULL), compressedFastBinderProxy(NULL),
- objcObjectConstraint(ld::File::objcConstraintNone),
- objcDylibConstraint(ld::File::objcConstraintNone),
+ hasObjC(false),
swiftVersion(0), cpuSubType(0), minOSVersion(0),
objectFileFoundWithNoVersion(false),
allObjectFilesScatterable(true),
someObjectFileHasDwarf(false), usingHugeSections(false),
+ someObjectFileHasSwift(false), firstSwiftDylibFile(nullptr),
hasThreadLocalVariableDefinitions(false),
hasWeakExternalSymbols(false),
someObjectHasOptimizationHints(false),
CStringSet unprocessedLinkerOptionFrameworks;
CStringSet linkerOptionLibraries;
CStringSet linkerOptionFrameworks;
+ CStringSet missingLinkerOptionLibraries;
+ CStringSet missingLinkerOptionFrameworks;
std::vector<const ld::Atom*> indirectBindingTable;
std::vector<const ld::relocatable::File*> filesWithBitcode;
std::vector<const ld::relocatable::File*> filesFromCompilerRT;
const Atom* classicBindingHelper;
const Atom* lazyBindingHelper;
const Atom* compressedFastBinderProxy;
- ld::File::ObjcConstraint objcObjectConstraint;
- ld::File::ObjcConstraint objcDylibConstraint;
+ bool hasObjC;
uint8_t swiftVersion;
uint32_t cpuSubType;
uint32_t minOSVersion;
- uint32_t derivedPlatform;
+ VersionSet derivedPlatforms;
bool objectFileFoundWithNoVersion;
bool allObjectFilesScatterable;
bool someObjectFileHasDwarf;
bool usingHugeSections;
+ bool someObjectFileHasSwift;
+ const ld::dylib::File* firstSwiftDylibFile;
bool hasThreadLocalVariableDefinitions;
bool hasWeakExternalSymbols;
bool someObjectHasOptimizationHints;
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 allowWeakImports, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs,
+ File(const char* path, time_t mTime, ld::File::Ordinal ordinal, const ld::VersionSet& platforms,
+ bool allowWeakImports, 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 platform() const override final { return _platformInDylib; }
virtual ld::Bitcode* getBitcode() const override final { return _bitcode.get(); }
std::vector<const char*> _rpaths;
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;
+ ld::VersionSet _platforms;
uint8_t _swiftVersion;
bool _wrongOS;
bool _linkingFlat;
bool _deadStrippable;
bool _hasPublicInstallName;
bool _appExtensionSafe;
- const bool _allowWeakImports;
+ const bool _allowWeakImports;
const bool _allowSimToMacOSXLinking;
const bool _addVersionLoadCommand;
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 allowWeakImports, bool linkingFlatNamespace,
+File<A>::File(const char* path, time_t mTime, ld::File::Ordinal ord, const ld::VersionSet& platforms,
+ bool allowWeakImports, bool linkingFlatNamespace,
bool hoistImplicitPublicDylibs,
bool allowSimToMacOSX, bool addVers)
: ld::dylib::File(path, mTime, ord),
_indirectDylibsProcessed(false),
_importAtom(nullptr),
_parentUmbrella(nullptr),
- _platform(platform),
- _objcConstraint(ld::File::objcConstraintNone),
- _linkMinOSVersion(linkMinOSVersion),
- _minVersionInDylib(0),
- _platformInDylib(Options::kPlatformUnknown),
+ _platforms(platforms),
_swiftVersion(0),
_wrongOS(false),
_linkingFlat(linkingFlatNamespace),
_deadStrippable(false),
_hasPublicInstallName(false),
_appExtensionSafe(false),
- _allowWeakImports(allowWeakImports),
+ _allowWeakImports(allowWeakImports),
_allowSimToMacOSXLinking(allowSimToMacOSX),
_addVersionLoadCommand(addVers)
{
objOpts.armUsesZeroCostExceptions = options.armUsesZeroCostExceptions;
objOpts.simulator = options.simulator;
objOpts.ignoreMismatchPlatform = options.ignoreMismatchPlatform;
- objOpts.platform = options.platform;
- objOpts.minOSVersion = options.minOSVersion;
+#if SUPPORT_ARCH_arm64e
+ objOpts.supportsAuthenticatedPointers = options.supportsAuthenticatedPointers;
+#endif
+ objOpts.platforms = options.platforms;
objOpts.subType = 0;
objOpts.srcKind = ld::relocatable::File::kSourceLTO;
objOpts.treateBitcodeAsData = false;
}
}
thinlto_codegen_set_cache_dir(thingenerator, options.ltoCachePath);
- thinlto_codegen_set_cache_pruning_interval(thingenerator, options.ltoPruneInterval);
+ if (options.ltoPruneIntervalOverwrite)
+ thinlto_codegen_set_cache_pruning_interval(thingenerator, options.ltoPruneInterval);
thinlto_codegen_set_cache_entry_expiration(thingenerator, options.ltoPruneAfter);
thinlto_codegen_set_final_cache_size_relative_to_available_space(thingenerator, options.ltoMaxCacheSize);
}
}
}
if (atom->contentType() == ld::Atom::typeLTOtemporary &&
- ((lto::File *)atom->file())->isThinLTO()) {
+ ((lto::File *)atom->file())->isThinLTO() &&
+ atom != &((lto::File *)atom->file())->internalAtom()) {
+ assert(atom->scope() != ld::Atom::scopeTranslationUnit && "LTO should not expose static atoms");
+ assert(llvmAtoms.find(atom->name()) == llvmAtoms.end() && "Unexpected llvmAtom with duplicate name");
llvmAtoms[atom->name()] = (Atom*)atom;
}
}
} else {
if ( logMustPreserve ) fprintf(stderr, "NOT preserving(%s)\n", name);
}
+
+ // <rdar://problem/16165191> tell code generator to preserve initial undefines
+ for (const char* undefName : *options.initialUndefines) {
+ if ( logMustPreserve ) fprintf(stderr, "thinlto_codegen_add_cross_referenced_symbol(%s) because it is an initial undefine\n", undefName);
+ ::thinlto_codegen_add_cross_referenced_symbol(thingenerator, undefName, strlen(undefName));
+ }
+
// FIXME: to be implemented
// else if ( options.relocatable && hasNonllvmAtoms ) {
// // <rdar://problem/14334895> ld -r mode but merging in some mach-o files, so need to keep libLTO from optimizing away anything
// Save the codegenerator
thinlto_code_gen_t bitcode_generator = thingenerator;
// Create a new codegen generator for the codegen part.
+ // Clear out the stored atoms so we can recompute them.
+ deadllvmAtoms.clear();
+ llvmAtoms.clear();
thingenerator = init_thinlto_codegen(files, allAtoms, state, options, deadllvmAtoms, llvmAtoms);
// Disable the optimizer
thinlto_codegen_set_codegen_only(thingenerator, true);
if (log) fprintf(stderr, "demote %s to hidden after LTO\n", name);
(const_cast<ld::Atom*>(&machoAtom))->setScope(ld::Atom::scopeLinkageUnit);
}
+ // If both llvmAtom and machoAtom has the same scope and combine, but machoAtom loses auto hide, add it back.
+ // rdar://problem/38646854
+ if (pos->second->scope() == machoAtom.scope() &&
+ pos->second->combine() == machoAtom.combine() &&
+ pos->second->autoHide() && !machoAtom.autoHide()) {
+ if (log) fprintf(stderr, "set %s to auto hide after LTO\n", name);
+ (const_cast<ld::Atom*>(&machoAtom))->setAutoHide();
+ }
pos->second->setCompiledAtom(machoAtom);
_lastProxiedAtom = &machoAtom;
_lastProxiedFile = pos->second->file();
if ( (_lastProxiedAtom != NULL) && (_lastProxiedAtom->section() == machoAtom.section()) ) {
ld::Atom* ma = const_cast<ld::Atom*>(&machoAtom);
ma->setFile(_lastProxiedFile);
- if (log) fprintf(stderr, "AtomSyncer, mach-o atom %s is proxied to %s (path=%s)\n", machoAtom.name(), _lastProxiedAtom->name(), _lastProxiedFile->path());
+ if (log) fprintf(stderr, "AtomSyncer, mach-o atom %s is static and being assigned to %s\n", machoAtom.name(), _lastProxiedFile->path());
}
if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p is totally new (name=%s)\n", &machoAtom, machoAtom.name());
}
int ltoPruneInterval;
int ltoPruneAfter;
unsigned ltoMaxCacheSize;
+ bool ltoPruneIntervalOverwrite;
bool preserveAllGlobals;
bool verbose;
bool saveTemps;
bool armUsesZeroCostExceptions;
bool simulator;
bool ignoreMismatchPlatform;
+#if SUPPORT_ARCH_arm64e
+ bool supportsAuthenticatedPointers;
+#endif
bool bitcodeBundle;
uint8_t maxDefaultCommonAlignment;
cpu_type_t arch;
const char* mcpu;
- Options::Platform platform;
- uint32_t minOSVersion;
+ ld::VersionSet platforms;
const std::vector<const char*>* llvmOptions;
const std::vector<const char*>* initialUndefines;
};
File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace,
bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
- Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports,
+ const ld::VersionSet& platforms, bool allowWeakImports,
bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
bool logAllFiles, const char* installPath,
bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode);
virtual ~File() noexcept {}
+ virtual const ld::VersionSet& platforms() const { return this->_platforms; }
private:
using P = typename A::P;
void addSymbol(const char* name, bool weakDef = false, bool tlv = false, pint_t address = 0);
static const char* objCInfoSegmentName();
static const char* objCInfoSectionName();
+ static bool useSimulatorVariant();
uint64_t _fileLength;
template <> const char* File<arm>::objCInfoSectionName() { return "__objc_imageinfo"; }
template <typename A> const char* File<A>::objCInfoSectionName() { return "__image_info"; }
+template <> bool File<x86>::useSimulatorVariant() { return true; }
+template <> bool File<x86_64>::useSimulatorVariant() { return true; }
+template <typename A> bool File<A>::useSimulatorVariant() { return false; }
+
template <typename A>
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 allowWeakImports,
+ bool hoistImplicitPublicDylibs, const ld::VersionSet& platforms, bool allowWeakImports,
bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles,
const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode)
- : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace,
+ : Base(strdup(path), mTime, ord, platforms, allowWeakImports, linkingFlatNamespace,
hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), _fileLength(fileLength), _linkeditStartOffset(0)
{
const macho_header<P>* header = (const macho_header<P>*)fileContent;
const char* strings = nullptr;
bool compressedLinkEdit = false;
uint32_t dependentLibCount = 0;
- Options::Platform lcPlatform = Options::kPlatformUnknown;
+ ld::VersionSet lcPlatforms;
const macho_load_command<P>* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
macho_dylib_command<P>* dylibID;
case LC_VERSION_MIN_MACOSX:
case LC_VERSION_MIN_IPHONEOS:
case LC_VERSION_MIN_WATCHOS:
- #if SUPPORT_APPLE_TV
case LC_VERSION_MIN_TVOS:
- #endif
- this->_minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command<P>*)cmd)->version();
- this->_platformInDylib = cmd->cmd();
- lcPlatform = Options::platformForLoadCommand(this->_platformInDylib);
+ lcPlatforms.add({Options::platformForLoadCommand(cmd->cmd(), useSimulatorVariant()), ((macho_version_min_command<P>*)cmd)->version()});
break;
case LC_BUILD_VERSION:
{
const macho_build_version_command<P>* buildVersCmd = (macho_build_version_command<P>*)cmd;
- this->_platformInDylib = buildVersCmd->platform();
- this->_minVersionInDylib = buildVersCmd->minos();
- lcPlatform = (Options::Platform)this->_platformInDylib;
+ lcPlatforms.add({(ld::Platform)buildVersCmd->platform(), buildVersCmd->minos()});
}
break;
case LC_CODE_SIGNATURE:
const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]);
if ( (sect->size() >= 8) && (contents[0] == 0) ) {
uint32_t flags = E::get32(contents[1]);
- if ( (flags & 4) == 4 )
- this->_objcConstraint = ld::File::objcConstraintGC;
- else if ( (flags & 2) == 2 )
- this->_objcConstraint = ld::File::objcConstraintRetainReleaseOrGC;
- else if ( (flags & 32) == 32 )
- this->_objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
- else
- this->_objcConstraint = ld::File::objcConstraintRetainRelease;
this->_swiftVersion = ((flags >> 8) & 0xFF);
}
else if ( sect->size() > 0 ) {
}
// arm/arm64 objects are default to ios platform if not set.
// rdar://problem/21746314
- if (lcPlatform == Options::kPlatformUnknown &&
+ if (lcPlatforms.empty() &&
(std::is_same<A, arm>::value || std::is_same<A, arm64>::value))
- lcPlatform = Options::kPlatformiOS;
+ lcPlatforms.add({ld::kPlatform_iOS, 0});
// check cross-linking
- if ( lcPlatform != platform ) {
- this->_wrongOS = true;
- if ( this->_addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) {
- if ( buildingForSimulator ) {
- if ( !this->_allowSimToMacOSXLinking ) {
- switch (platform) {
- case Options::kPlatformOSX:
- case Options::kPlatform_bridgeOS:
- case Options::kPlatformiOS:
- if ( lcPlatform == Options::kPlatformUnknown )
- break;
- // fall through if the Platform is not Unknown
- case Options::kPlatformWatchOS:
- // 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:
- // tvOS is a warning temporarily. rdar://problem/21746965
- 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;
- #endif
- case Options::kPlatformUnknown:
- // skip if the target platform is unknown
- break;
- }
- }
- }
- else {
- switch (platform) {
- case Options::kPlatformOSX:
- case Options::kPlatform_bridgeOS:
- case Options::kPlatformiOS:
- if ( lcPlatform == Options::kPlatformUnknown )
- break;
- // fall through if the Platform is not Unknown
- case Options::kPlatformWatchOS:
- // 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 )
- 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;
- #endif
- case Options::kPlatformUnknown:
- // skip if the target platform is unknown
- break;
+ platforms.forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ if (!lcPlatforms.contains(platform) ) {
+ this->_wrongOS = true;
+ if ( this->_addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) {
+ if (buildingForSimulator && !this->_allowSimToMacOSXLinking) {
+ if ( usingBitcode )
+ throwf("building for %s simulator, but linking against dylib built for %s,",
+ platforms.to_str().c_str(), lcPlatforms.to_str().c_str());
+ else
+ warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. "
+ "Note: This will be an error in the future.",
+ platforms.to_str().c_str(), path, lcPlatforms.to_str().c_str());
}
+ } else {
+ if ( usingBitcode )
+ throwf("building for %s, but linking against dylib built for %s,",
+ platforms.to_str().c_str(), lcPlatforms.to_str().c_str());
+ else if ( (getenv("RC_XBS") != NULL) && (getenv("RC_BUILDIT") == NULL) ) // FIXME: remove after platform bringup
+ warning("URGENT: building for %s, but linking against dylib (%s) built for %s. "
+ "Note: This will be an error in the future.",
+ platforms.to_str().c_str(), path, lcPlatforms.to_str().c_str());
}
}
- }
+ });
// figure out if we need to examine dependent dylibs
// with compressed LINKEDIT format, MH_NO_REEXPORTED_DYLIBS can be trusted
template <typename A>
void File<A>::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address)
{
+ __block uint32_t linkMinOSVersion = 0;
+
+ this->platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ //FIXME hack to handle symbol versioning in a zippered world.
+ //This will need to be rethought
+ if (linkMinOSVersion == 0)
+ linkMinOSVersion = version;
+ if (platform == ld::kPlatform_macOS)
+ linkMinOSVersion = version;
+ });
+
// 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 ) {
const char* symCond = strchr(symAction, '$');
if ( symCond != nullptr ) {
char curOSVers[16];
- sprintf(curOSVers, "$os%d.%d$", (this->_linkMinOSVersion >> 16), ((this->_linkMinOSVersion >> 8) & 0xFF));
+ 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 ) {
{
return new File<A>(fileContent, fileLength, path, mTime, ordinal, opts.flatNamespace(),
opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(),
- opts.platform(), opts.minOSversion(), opts.allowWeakImports(),
+ opts.platforms(), opts.allowWeakImports(),
opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(),
opts.targetIOSSimulator(), opts.logAllFiles(), opts.installPath(),
indirectDylib, opts.outputKind() == Options::kPreload, opts.bundleBitcode());
return Parser<arm>::fileKind(fileContent);
}
#if SUPPORT_ARCH_arm64
- if ( Parser<arm64>::validFile(fileContent, false) ) {
+ if ( Parser<arm64>::validFile(fileContent, true) ) {
return Parser<arm64>::fileKind(fileContent);
}
#endif
}
-//
-// 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)
+static ld::dylib::File* parseAsArchitecture(const uint8_t* fileContent, uint64_t fileLength, const char* path,
+ time_t modTime, const Options& opts, ld::File::Ordinal ordinal,
+ bool bundleLoader, bool indirectDylib,
+ cpu_type_t architecture, cpu_subtype_t subArchitecture)
{
bool subTypeMustMatch = opts.enforceDylibSubtypesMatch();
- switch ( opts.architecture() ) {
+ switch ( architecture) {
#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
- if ( Parser<x86_64>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
+ if ( Parser<x86_64>::validFile(fileContent, bundleLoader, subTypeMustMatch, subArchitecture) )
return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
- break;
+ break;
#endif
#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
- if ( Parser<x86>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
+ if ( Parser<x86>::validFile(fileContent, bundleLoader, subTypeMustMatch, subArchitecture) )
return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
- break;
+ break;
#endif
#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
- if ( Parser<arm>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
+ if ( Parser<arm>::validFile(fileContent, bundleLoader, subTypeMustMatch, subArchitecture) )
return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
- break;
+ break;
#endif
#if SUPPORT_ARCH_arm64
case CPU_TYPE_ARM64:
- if ( Parser<arm64>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
+ if ( Parser<arm64>::validFile(fileContent, bundleLoader, subTypeMustMatch, subArchitecture) )
return Parser<arm64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
- break;
+ break;
#endif
}
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)
+{
+ // First make sure we are even a dylib with a known arch. If we aren't then there's no point in continuing.
+ if (!archName(fileContent))
+ return nullptr;
+
+ auto file = parseAsArchitecture(fileContent, fileLength, path, modtime, opts, ordinal, bundleLoader, indirectDylib, opts.architecture(), opts.subArchitecture());
+
+ // If we've been provided with an architecture we can fall back to, try to parse the dylib as that instead.
+ if (!file && opts.fallbackArchitecture()) {
+ warning("architecture %s not present in dylib file %s, attempting fallback", opts.architectureName(), path);
+ file = parseAsArchitecture(fileContent, fileLength, path, modtime, opts, ordinal, bundleLoader, indirectDylib, opts.fallbackArchitecture(), opts.fallbackSubArchitecture());
+ }
+
+ return file;
+}
+
+
}; // namespace dylib
}; // namespace mach_o
_dwarfTranslationUnitPath(NULL),
_dwarfDebugInfoSect(NULL), _dwarfDebugAbbrevSect(NULL),
_dwarfDebugLineSect(NULL), _dwarfDebugStringSect(NULL),
- _objConstraint(ld::File::objcConstraintNone),
+ _hasObjC(false),
_swiftVersion(0),
_cpuSubType(0),
_minOSVersion(0),
- _platform(Options::kPlatformUnknown),
_canScatterAtoms(false),
_hasllvmProfiling(false),
_objcHasCategoryClassPropertiesField(false),
virtual bool forEachAtom(ld::File::AtomHandler&) const;
virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const
{ return false; }
- virtual uint32_t minOSVersion() const { return _minOSVersion; }
- virtual uint32_t platform() const { return _platform; }
+ virtual const ld::VersionSet& platforms() const { return _platforms; }
// overrides of ld::relocatable::File
- virtual ObjcConstraint objCConstraint() const { return _objConstraint; }
+ virtual bool hasObjC() const { return _hasObjC; }
virtual bool objcHasCategoryClassPropertiesField() const
{ return _objcHasCategoryClassPropertiesField; }
virtual uint32_t cpuSubType() const { return _cpuSubType; }
const macho_section<P>* _dwarfDebugAbbrevSect;
const macho_section<P>* _dwarfDebugLineSect;
const macho_section<P>* _dwarfDebugStringSect;
- ld::File::ObjcConstraint _objConstraint;
+ bool _hasObjC;
uint8_t _swiftVersion;
uint32_t _cpuSubType;
uint32_t _minOSVersion;
- Options::Platform _platform;
+ ld::VersionSet _platforms;
bool _canScatterAtoms;
bool _hasllvmProfiling;
bool _objcHasCategoryClassPropertiesField;
static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false,
cpu_subtype_t subtype=0);
static const char* fileKind(const uint8_t* fileContent);
- static Options::Platform findPlatform(const macho_header<typename A::P>* header);
+ static ld::Platform findPlatform(const macho_header<typename A::P>* header, uint32_t* minOsVers);
static bool hasObjC2Categories(const uint8_t* fileContent);
static bool hasObjC1Categories(const uint8_t* fileContent);
static bool getNonLocalSymbols(const uint8_t* fileContnet, std::vector<const char*> &syms);
const char* name; // only used if targetAtom is NULL
int64_t addend;
bool weakImport; // only used if targetAtom is NULL
+#if SUPPORT_ARCH_arm64e
+ ld::Fixup::AuthData authData; // only used for authenticated pointers
+#endif
};
struct FixupInAtom {
FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, uint64_t addend) :
fixup(src.offsetInAtom, c, k, addend), atom(src.atom) { src.atom->incrementFixupCount(); }
+#if SUPPORT_ARCH_arm64e
+ FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, ld::Fixup::AuthData authData) :
+ fixup(src.offsetInAtom, c, k, authData), atom(src.atom) { src.atom->incrementFixupCount(); }
+#endif
+
FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k) :
fixup(src.offsetInAtom, c, k, (uint64_t)0), atom(src.atom) { src.atom->incrementFixupCount(); }
_allFixups.push_back(FixupInAtom(src, c, k, addend));
}
+#if SUPPORT_ARCH_arm64e
+ void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, ld::Fixup::AuthData authData) {
+ _allFixups.push_back(FixupInAtom(src, c, k, authData));
+ }
+#endif
+
void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k) {
_allFixups.push_back(FixupInAtom(src, c, k));
}
bool verboseOptimizationHints, bool ignoreMismatchPlatform);
ld::relocatable::File* parse(const ParserOptions& opts);
static uint8_t loadCommandSizeMask();
- bool parseLoadCommands(Options::Platform platform, uint32_t minOSVersion, bool simulator, bool ignoreMismatchPlatform);
+ static bool useSimulatorVariant();
+ bool parseLoadCommands(ld::VersionSet platforms, bool simulator, bool ignoreMismatchPlatform);
void makeSections();
void prescanSymbolTable();
void makeSortedSymbolsArray(uint32_t symArray[], const uint32_t sectionArray[]);
const macho_section<P>* _stubsMachOSection;
std::vector<const char*> _dtraceProviderInfo;
std::vector<FixupInAtom> _allFixups;
+#if SUPPORT_ARCH_arm64e
+ bool _supportsAuthenticatedPointers;
+#endif
};
return false;
if ( header->filetype() != MH_OBJECT )
return false;
+ if ( subtypeMustMatch && (header->cpusubtype() != (uint32_t)subtype) )
+ return false;
return true;
}
bool Parser<A>::LabelAndCFIBreakIterator::next(Parser<A>& parser, const Section<A>& sect, uint32_t sectNum, pint_t startAddr, pint_t endAddr,
pint_t* addr, pint_t* size, const macho_nlist<P>** symbol)
{
+ bool cfiApplicable = (sect.machoSection()->flags() & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS));
// may not be a label on start of section, but need atom demarcation there
if ( newSection ) {
newSection = false;
// advance symIndex until we get to the first label at or past the start of this section
while ( symIndex < sortedSymbolCount ) {
- const macho_nlist<P>& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]);
- if ( ! sect.ignoreLabel(parser.nameFromSymbol(sym)) ) {
- pint_t nextSymbolAddr = sym.n_value();
+ const macho_nlist<P>* sym = &parser.symbolFromIndex(sortedSymbolIndexes[symIndex]);
+ // if compile threw in "ltmp*" symbol at start of section and there is another real label at same location, ignore ltmp one
+ if ( symIndex+1 < sortedSymbolCount ) {
+ const macho_nlist<P>* sym2 = &parser.symbolFromIndex(sortedSymbolIndexes[symIndex+1]);
+ if ( (sym->n_sect() == sym2->n_sect()) && (sym->n_value() == sym2->n_value()) ) {
+ if ( strncmp(parser.nameFromSymbol(*sym), "ltmp", 4) == 0 ) {
+ ++symIndex;
+ sym = sym2;
+ }
+ }
+ }
+ if ( ! sect.ignoreLabel(parser.nameFromSymbol(*sym)) ) {
+ pint_t nextSymbolAddr = sym->n_value();
//fprintf(stderr, "sectNum=%d, nextSymbolAddr=0x%08llX, name=%s\n", sectNum, (uint64_t)nextSymbolAddr, parser.nameFromSymbol(sym));
- if ( (nextSymbolAddr > startAddr) || ((nextSymbolAddr == startAddr) && (sym.n_sect() == sectNum)) )
+ if ( (nextSymbolAddr > startAddr) || ((nextSymbolAddr == startAddr) && (sym->n_sect() == sectNum)) )
break;
}
++symIndex;
return true;
}
// no symbols in section, check CFI
- if ( cfiIndex < cfiStartsCount ) {
+ if ( cfiApplicable && (cfiIndex < cfiStartsCount) ) {
pint_t nextCfiAddr = cfiStartsArray[cfiIndex];
if ( nextCfiAddr < endAddr ) {
// use cfi
_treateBitcodeAsData = opts.treateBitcodeAsData;
_usingBitcode = opts.usingBitcode;
+#if SUPPORT_ARCH_arm64e
+ _supportsAuthenticatedPointers = opts.supportsAuthenticatedPointers;
+#endif
+
// respond to -t option
if ( opts.logAllFiles )
printf("%s\n", _path);
_maxDefaultCommonAlignment = opts.maxDefaultCommonAlignment;
// parse start of mach-o file
- if ( ! parseLoadCommands(opts.platform, opts.minOSVersion, opts.simulator, opts.ignoreMismatchPlatform) )
+ if ( ! parseLoadCommands(opts.platforms, opts.simulator, opts.ignoreMismatchPlatform) )
return _file;
// make array of
template <> uint8_t Parser<x86_64>::loadCommandSizeMask() { return 0x07; }
template <> uint8_t Parser<arm>::loadCommandSizeMask() { return 0x03; }
template <> uint8_t Parser<arm64>::loadCommandSizeMask() { return 0x07; }
+template <> bool Parser<x86>::useSimulatorVariant() { return true; }
+template <> bool Parser<x86_64>::useSimulatorVariant() { return true; }
+template <typename A> bool Parser<A>::useSimulatorVariant() { return false; }
+
template <typename A>
-bool Parser<A>::parseLoadCommands(Options::Platform platform, uint32_t linkMinOSVersion, bool simulator, bool ignoreMismatchPlatform)
+bool Parser<A>::parseLoadCommands(ld::VersionSet platforms, bool simulator, bool ignoreMismatchPlatform)
{
const macho_header<P>* header = (const macho_header<P>*)_fileContent;
// <rdar://problem/5394172> an empty .o file with zero load commands will crash linker
if ( cmd_count == 0 )
return false;
- Options::Platform lcPlatform = Options::kPlatformUnknown;
+ ld::VersionSet lcPlatforms;
const macho_load_command<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>));
const macho_load_command<P>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds());
const macho_load_command<P>* cmd = cmds;
case LC_VERSION_MIN_MACOSX:
case LC_VERSION_MIN_IPHONEOS:
case LC_VERSION_MIN_WATCHOS:
- #if SUPPORT_APPLE_TV
case LC_VERSION_MIN_TVOS:
- #endif
if ( ignoreMismatchPlatform )
break;
- lcPlatform = Options::platformForLoadCommand(cmd->cmd());
- _file->_platform = lcPlatform;
- _file->_minOSVersion = ((macho_version_min_command<P>*)cmd)->version();
+ lcPlatforms.add({Options::platformForLoadCommand(cmd->cmd(), useSimulatorVariant()), ((macho_version_min_command<P>*)cmd)->version()});
+ _file->_platforms.add({Options::platformForLoadCommand(cmd->cmd(), useSimulatorVariant()), ((macho_version_min_command<P>*)cmd)->version()});
break;
case LC_BUILD_VERSION:
{
const macho_build_version_command<P>* buildVersCmd = (macho_build_version_command<P>*)cmd;
if ( ignoreMismatchPlatform )
break;
- lcPlatform = (Options::Platform)buildVersCmd->platform();
- _file->_platform = lcPlatform;
- _file->_minOSVersion = buildVersCmd->minos();
+ lcPlatforms.add({(ld::Platform)buildVersCmd->platform(), buildVersCmd->minos()});
+ _file->_platforms.add({(ld::Platform)buildVersCmd->platform(), buildVersCmd->minos()});
const macho_build_tool_version<P>* entry = (macho_build_tool_version<P>*)((uint8_t*)cmd + sizeof(macho_build_version_command<P>));
for (uint32_t t=0; t < buildVersCmd->ntools(); ++t) {
_file->_toolVersions.push_back(std::make_pair(entry->tool(), entry->version()));
if ( cmd > cmdsEnd )
throwf("malformed mach-o file, load command #%d is outside size of load commands", i);
}
+
// arm/arm64 objects are default to ios platform if not set.
// rdar://problem/21746314
- if (lcPlatform == Options::kPlatformUnknown &&
+ if (lcPlatforms.empty() &&
(std::is_same<A, arm>::value || std::is_same<A, arm64>::value))
- lcPlatform = Options::kPlatformiOS;
+ lcPlatforms.add({ld::kPlatform_iOS,0});
// Check platform cross-linking.
if ( !ignoreMismatchPlatform ) {
- if ( lcPlatform != platform ) {
- switch (platform) {
- case Options::kPlatformOSX:
- case Options::kPlatformiOS:
- if ( lcPlatform == Options::kPlatformUnknown )
- break;
- // fall through if the Platform is not Unknown
- case Options::kPlatform_bridgeOS:
- case Options::kPlatformWatchOS:
- // 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:
- // 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;
- #endif
- case Options::kPlatformUnknown:
- // skip if the target platform is unknown
- break;
+ __block bool warned = false;
+ platforms.forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ if ( !warned && !lcPlatforms.contains(platform) ) {
+ if (_usingBitcode)
+ throwf("building for %s, but linking in object file built for %s,",
+ platforms.to_str().c_str(), lcPlatforms.to_str().c_str());
+#if 0
+// FIXME: Re-enable once clang supports zippering
+// <rdar://problem/36749415> Turn off "urgent:" linker warning about iOSMac / macOS mismatch
+ else
+ warning("URGENT: building for %s, but linking in object file (%s) built for %s. "
+ "Note: This will be an error in the future.",
+ platforms.to_str().c_str(), path(), lcPlatforms.to_str().c_str());
+#endif
+ warned = true;
}
- }
- if ( linkMinOSVersion && (_file->_minOSVersion > linkMinOSVersion) ) {
- char t1[32];
- char t2[32];
- versionToString(_file->_minOSVersion, t1);
- versionToString(linkMinOSVersion, t2);
- warning("object file (%s) was built for newer %s version (%s) than being linked (%s)",
- _path, Options::platformName(lcPlatform), t1, t2);
- }
+ if ( version && (lcPlatforms.minOS(platform) > version) ) {
+ char t1[32];
+ char t2[32];
+ versionToString(lcPlatforms.minOS(platform), t1);
+ versionToString(version, t2);
+ warning("object file (%s) was built for newer %s version (%s) than being linked (%s)",
+ _path, Options::platformName(platform), t1, t2);
+ }
+ });
}
-
- // record range of sections
+ // validate just one segment
if ( segment == NULL )
throw "missing LC_SEGMENT";
+ if ( segment->filesize() > _fileLength )
+ throw "LC_SEGMENT filesize too large";
+
+ // record and validate sections
_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;
}
template <typename A>
-Options::Platform Parser<A>::findPlatform(const macho_header<P>* header)
+ld::Platform Parser<A>::findPlatform(const macho_header<P>* header, uint32_t* minOsVers)
{
const uint32_t cmd_count = header->ncmds();
if ( cmd_count == 0 )
- return Options::kPlatformUnknown;
+ return ld::kPlatform_unknown;
const macho_load_command<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>));
const macho_load_command<P>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds());
const macho_load_command<P>* cmd = cmds;
const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
if ( endOfCmd > (uint8_t*)cmdsEnd )
throwf("load command #%d extends beyond the end of the load commands", i);
+ const macho_version_min_command<P>* versCmd = (macho_version_min_command<P>*)cmd;
+ const macho_build_version_command<P>* buildVersCmd = (macho_build_version_command<P>*)cmd;
+ *minOsVers = versCmd->version();
switch (cmd->cmd()) {
case LC_VERSION_MIN_MACOSX:
- return Options::kPlatformOSX;
+ return ld::kPlatform_macOS;
case LC_VERSION_MIN_IPHONEOS:
- return Options::kPlatformiOS;
+ if (useSimulatorVariant())
+ return ld::kPlatform_iOSSimulator;
+ else
+ return ld::kPlatform_iOS;
case LC_VERSION_MIN_WATCHOS:
- return Options::kPlatformWatchOS;
- #if SUPPORT_APPLE_TV
+ if (useSimulatorVariant())
+ return ld::kPlatform_watchOSSimulator;
+ else
+ return ld::kPlatform_watchOS;
case LC_VERSION_MIN_TVOS:
- return Options::kPlatform_tvOS;
- #endif
+ if (useSimulatorVariant())
+ return ld::kPlatform_tvOSSimulator;
+ else
+ return ld::kPlatform_tvOS;
case LC_BUILD_VERSION:
- return (Options::Platform)((macho_build_version_command<P>*)cmd)->platform();
+ *minOsVers = buildVersCmd->minos();
+ return (ld::Platform)buildVersCmd->platform();
}
cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
if ( cmd > cmdsEnd )
throwf("malformed mach-o file, load command #%d is outside size of load commands", i);
}
- return Options::kPlatformUnknown;
+ *minOsVers = 0;
+ return ld::kPlatform_unknown;
}
const uint32_t* contents = (uint32_t*)(_file->fileContent()+sect->offset());
if ( (sect->size() >= 8) && (contents[0] == 0) ) {
uint32_t flags = E::get32(contents[1]);
- if ( (flags & 4) == 4 )
- _file->_objConstraint = ld::File::objcConstraintGC;
- else if ( (flags & 2) == 2 )
- _file->_objConstraint = ld::File::objcConstraintRetainReleaseOrGC;
- else if ( (flags & 32) == 32 )
- _file->_objConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
- else
- _file->_objConstraint = ld::File::objcConstraintRetainRelease;
+ _file->_hasObjC = true;
_file->_swiftVersion = ((flags >> 8) & 0xFF);
_file->_objcHasCategoryClassPropertiesField = (flags & 64);
if ( sect->size() > 8 ) {
ld::Fixup::Cluster cl = ld::Fixup::k1of3;
ld::Fixup::Kind firstKind = ld::Fixup::kindSetTargetAddress;
bool combined = false;
+
+#if SUPPORT_ARCH_arm64e
+ bool isAuthenticated = setKind == ld::Fixup::kindStoreLittleEndianAuth64;
+ // Authenticated pointers need an extra fixup for the auth data.
+ if (isAuthenticated)
+ cl = ld::Fixup::k2of4;
+#endif
if ( target.addend == 0 ) {
cl = ld::Fixup::k1of1;
combined = true;
case ld::Fixup::kindStoreARM64TLVPLoadPageOff12:
firstKind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12;
break;
+#endif
+#if SUPPORT_ARCH_arm64e
+ case ld::Fixup::kindStoreLittleEndianAuth64:
+ firstKind = ld::Fixup::kindStoreTargetAddressLittleEndianAuth64;
+ cl = ld::Fixup::k2of2;
+ break;
#endif
default:
combined = false;
}
}
+#if SUPPORT_ARCH_arm64e
+ // As the auth data is independent of the addend and target, we can just always
+ // put it first.
+ if (isAuthenticated) {
+ if (cl == ld::Fixup::k2of2) {
+ addFixup(src, ld::Fixup::k1of2, ld::Fixup::kindSetAuthData, target.authData);
+ } else {
+ assert(cl == ld::Fixup::k2of4);
+ addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetAuthData, target.authData);
+ }
+ }
+#endif
+
if ( target.atom != NULL ) {
if ( target.atom->scope() == ld::Atom::scopeTranslationUnit ) {
addFixup(src, cl, firstKind, target.atom);
addFixup(src, cl, firstKind, target.weakImport, target.name);
}
if ( target.addend == 0 ) {
+#if SUPPORT_ARCH_arm64e
+ if (isAuthenticated)
+ assert(combined);
+#endif
if ( ! combined )
addFixup(src, ld::Fixup::k2of2, setKind);
}
else {
- addFixup(src, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, target.addend);
- addFixup(src, ld::Fixup::k3of3, setKind);
+#if SUPPORT_ARCH_arm64e
+ if (isAuthenticated) {
+ addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindAddAddend, target.addend);
+ addFixup(src, ld::Fixup::k4of4, setKind);
+ } else
+#endif
+ {
+ addFixup(src, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, target.addend);
+ addFixup(src, ld::Fixup::k3of3, setKind);
+ }
}
}
}
else {
// make named atom for label
- //fprintf(stderr, " 0x%08llX make labeled\n", (uint64_t)foundAddr);
+ //fprintf(stderr, " 0x%08llX make labeled: %s\n", (uint64_t)foundAddr, parser.nameFromSymbol(*foundLabel));
new (allocatedSpace) Atom<A>(*this, parser, *foundLabel, labeledAtomSize);
}
if ( !skip ) {
Parser<x86_64>::TargetDesc target;
Parser<x86_64>::TargetDesc toTarget;
src.atom = this->findAtomByAddress(srcAddr);
+ if ( src.atom == NULL )
+ throwf("malformed mach-o, reloc addr 0x%llX not in any atom", srcAddr);
src.offsetInAtom = srcAddr - src.atom->_objAddress;
const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address();
uint64_t contentValue = 0;
parser.addFixups(src, ld::Fixup::kindStoreARM64PointerToGOT, target);
}
break;
+#if SUPPORT_ARCH_arm64e
+ case ARM64_RELOC_AUTHENTICATED_POINTER: {
+ if ( reloc->r_pcrel() )
+ throw "pcrel and ARM64_RELOC_AUTHENTICATED_POINTER not supported";
+ if ( ! reloc->r_extern() )
+ throw "r_extern == 0 and ARM64_RELOC_AUTHENTICATED_POINTER not supported";
+ // An authenticated pointer is:
+ // {
+ // int32_t addend;
+ // uint16_t diversityData;
+ // uint16_t hasAddressDiversity : 1;
+ // uint16_t key : 2;
+ // uint16_t zeroes : 11;
+ // uint16_t zero : 1;
+ // uint16_t authenticated : 1;
+ // }
+ target.addend = (int32_t)(contentValue & 0xFFFFFFFF);
+ if (parser._supportsAuthenticatedPointers) {
+ target.authData.discriminator = (uint16_t)(contentValue >> 32);
+ target.authData.hasAddressDiversity = (contentValue & (1ULL << 48)) != 0;
+ target.authData.key = (ld::Fixup::AuthData::ptrauth_key)((contentValue >> 49) & 0x3);
+ } else {
+ static bool emittedWarning = false;
+ if (!emittedWarning) {
+ emittedWarning = true;
+ warning("stripping authenticated relocation as image uses -preload or -static");
+ }
+ }
+ bool isAuthenticated = (contentValue & (1ULL << 63)) != 0;
+ if (!isAuthenticated)
+ throw "ARM64_RELOC_AUTHENTICATED_POINTER value must have authenticated bit set";
+ switch ( reloc->r_length() ) {
+ case 0:
+ case 1:
+ case 2:
+ throw "length < 3 and ARM64_RELOC_AUTHENTICATED_POINTER not supported";
+ case 3:
+ if (parser._supportsAuthenticatedPointers)
+ parser.addFixups(src, ld::Fixup::kindStoreLittleEndianAuth64, target);
+ else
+ parser.addFixups(src, ld::Fixup::kindStoreLittleEndian64, target);
+ break;
+ }
+ break;
+ }
+#endif
default:
throwf("unknown relocation type %d", reloc->r_type());
}
//
// used by linker to infer architecture when no -arch is on command line
//
-bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult, Options::Platform* platform)
+bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult, ld::Platform* platform, uint32_t* minOsVers)
{
if ( mach_o::relocatable::Parser<x86_64>::validFile(fileContent) ) {
*result = CPU_TYPE_X86_64;
const macho_header<Pointer64<LittleEndian> >* header = (const macho_header<Pointer64<LittleEndian> >*)fileContent;
*subResult = header->cpusubtype();
- *platform = Parser<x86_64>::findPlatform(header);
+ *platform = Parser<x86_64>::findPlatform(header, minOsVers);
return true;
}
if ( mach_o::relocatable::Parser<x86>::validFile(fileContent) ) {
const macho_header<Pointer32<LittleEndian> >* header = (const macho_header<Pointer32<LittleEndian> >*)fileContent;
*result = CPU_TYPE_I386;
*subResult = CPU_SUBTYPE_X86_ALL;
- *platform = Parser<x86>::findPlatform(header);
+ *platform = Parser<x86>::findPlatform(header, minOsVers);
return true;
}
if ( mach_o::relocatable::Parser<arm>::validFile(fileContent, false, 0) ) {
const macho_header<Pointer32<LittleEndian> >* header = (const macho_header<Pointer32<LittleEndian> >*)fileContent;
*result = CPU_TYPE_ARM;
*subResult = header->cpusubtype();
- *platform = Parser<arm>::findPlatform(header);
+ *platform = Parser<arm>::findPlatform(header, minOsVers);
return true;
}
if ( mach_o::relocatable::Parser<arm64>::validFile(fileContent, false, 0) ) {
const macho_header<Pointer64<LittleEndian> >* header = (const macho_header<Pointer64<LittleEndian> >*)fileContent;
*result = CPU_TYPE_ARM64;
*subResult = header->cpusubtype();
- *platform = Parser<arm64>::findPlatform(header);
+ *platform = Parser<arm64>::findPlatform(header, minOsVers);
return true;
}
return false;
else if ( mach_o::relocatable::Parser<x86>::validFile(fileContent, false, 0) ) {
return mach_o::relocatable::Parser<x86>::getNonLocalSymbols(fileContent, syms);
}
+#if SUPPORT_ARCH_arm64
else if ( mach_o::relocatable::Parser<arm64>::validFile(fileContent, false, 0) ) {
return mach_o::relocatable::Parser<arm64>::getNonLocalSymbols(fileContent, syms);
}
+#endif
return false;
}
bool armUsesZeroCostExceptions;
bool simulator;
bool ignoreMismatchPlatform;
+#if SUPPORT_ARCH_arm64e
+ bool supportsAuthenticatedPointers;
+#endif
uint32_t subType;
- Options::Platform platform;
- uint32_t minOSVersion;
+ ld::VersionSet platforms;
ld::relocatable::File::SourceKind srcKind;
bool treateBitcodeAsData;
bool usingBitcode;
extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserOptions& opts);
-extern bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult, Options::Platform* platform);
+extern bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult, ld::Platform* platform, uint32_t* minOsVers);
extern bool hasObjC2Categories(const uint8_t* fileContent);
File(const char* path, const uint8_t* fileContent, uint64_t fileLength, const Options *opts,
time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace,
bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
- Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports,
+ const ld::VersionSet& platforms, bool allowWeakImports,
cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool enforceDylibSubtypesMatch,
bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
- bool logAllFiles, const char* installPath, bool indirectDylib);
- File(std::unique_ptr<tapi::LinkerInterfaceFile> &&file, const char *path, const Options *opts,
+ bool logAllFiles, const char* installPath, bool indirectDylib,
+ bool ignoreMismatchPlatform, bool usingBitcode);
+ File(tapi::LinkerInterfaceFile* file, const char *path, const Options *opts,
time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace,
bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
- Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports,
+ const ld::VersionSet& platforms, bool allowWeakImports,
cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool enforceDylibSubtypesMatch,
bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
- bool logAllFiles, const char* installPath, bool indirectDylib);
+ bool logAllFiles, const char* installPath, bool indirectDylib,
+ bool ignoreMismatchPlatform, bool usingBitcode);
virtual ~File() noexcept {}
// overrides of generic::dylib::File
virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool addImplicitDylibs) override final;
private:
- void init(tapi::LinkerInterfaceFile *file, const Options *opts, bool buildingForSimulator,
- bool indirectDylib, bool linkingFlatNamespace, bool linkingMainExecutable,
- const char *path, Options::Platform platform, const char *targetInstallPath);
- void buildExportHashTable(const tapi::LinkerInterfaceFile* file);
+ void init(tapi::LinkerInterfaceFile* file, const Options *opts, bool buildingForSimulator,
+ bool indirectDylib, bool linkingFlatNamespace, bool linkingMainExecutable,
+ const char *path, const ld::VersionSet& platforms, const char *targetInstallPath,
+ bool ignoreMismatchPlatform, bool usingBitcode);
+ void buildExportHashTable(const tapi::LinkerInterfaceFile* file);
+ static bool useSimulatorVariant();
- const Options *_opts;
- std::unique_ptr<tapi::LinkerInterfaceFile> _interface;
+ const Options* _opts;
+ tapi::LinkerInterfaceFile* _interface;
};
-static ld::File::ObjcConstraint mapObjCConstraint(tapi::ObjCConstraint constraint) {
- switch (constraint) {
- case tapi::ObjCConstraint::None:
- return ld::File::objcConstraintNone;
- case tapi::ObjCConstraint::Retain_Release:
- return ld::File::objcConstraintRetainRelease;
- case tapi::ObjCConstraint::Retain_Release_For_Simulator:
- return ld::File::objcConstraintRetainReleaseForSimulator;
- case tapi::ObjCConstraint::Retain_Release_Or_GC:
- return ld::File::objcConstraintRetainReleaseOrGC;
- case tapi::ObjCConstraint::GC:
- return ld::File::objcConstraintGC;
- }
+template <> bool File<x86>::useSimulatorVariant() { return true; }
+template <> bool File<x86_64>::useSimulatorVariant() { return true; }
+template <typename A> bool File<A>::useSimulatorVariant() { return false; }
- return ld::File::objcConstraintNone;
-}
-static Options::Platform mapPlatform(tapi::Platform platform) {
+static ld::VersionSet mapPlatform(tapi::Platform platform, bool useSimulatorVariant) {
+ ld::VersionSet platforms;
switch (platform) {
case tapi::Platform::Unknown:
- return Options::kPlatformUnknown;
+ break;
case tapi::Platform::OSX:
- return Options::kPlatformOSX;
+ platforms.add({ld::kPlatform_macOS, 0});
+ break;
case tapi::Platform::iOS:
- return Options::kPlatformiOS;
+ if (useSimulatorVariant)
+ platforms.add({ld::kPlatform_iOSSimulator, 0});
+ else
+ platforms.add({ld::kPlatform_iOS, 0});
+ break;
case tapi::Platform::watchOS:
- return Options::kPlatformWatchOS;
+ if (useSimulatorVariant)
+ platforms.add({ld::kPlatform_watchOSSimulator, 0});
+ else
+ platforms.add({ld::kPlatform_watchOS, 0});
+ break;
case tapi::Platform::tvOS:
- return Options::kPlatform_tvOS;
+ if (useSimulatorVariant)
+ platforms.add({ld::kPlatform_tvOSSimulator, 0});
+ else
+ platforms.add({ld::kPlatform_tvOS, 0});
+ break;
#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 2) || (TAPI_API_VERSION_MAJOR > 1))
case tapi::Platform::bridgeOS:
- return Options::kPlatform_bridgeOS;
+ platforms.add({ld::kPlatform_bridgeOS, 0});
+ break;
+ #endif
+ #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 4) || (TAPI_API_VERSION_MAJOR > 1))
+ case tapi::Platform::iOSMac:
+ platforms.add({ld::kPlatform_iOSMac, 0});
+ break;
+ case tapi::Platform::zippered:
+ platforms.add({ld::kPlatform_macOS, 0});
+ platforms.add({ld::kPlatform_iOSMac, 0});
+ break;
#endif
}
- return Options::kPlatformUnknown;
+ return platforms;
}
- template <typename A>
- File<A>::File(const char* path, const uint8_t* fileContent, uint64_t fileLength, const Options *opts,
- time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace,
- bool linkingMainExecutable, bool hoistImplicitPublicDylibs, Options::Platform platform,
- uint32_t linkMinOSVersion, bool allowWeakImports, cpu_type_t cpuType, cpu_subtype_t cpuSubType,
- bool enforceDylibSubtypesMatch, bool allowSimToMacOSX, bool addVers,
- bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath,
- bool indirectDylib)
- : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace,
- hoistImplicitPublicDylibs, allowSimToMacOSX, addVers)
+template <typename A>
+File<A>::File(const char* path, const uint8_t* fileContent, uint64_t fileLength, const Options *opts,
+ time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace,
+ bool linkingMainExecutable, bool hoistImplicitPublicDylibs, const ld::VersionSet& platforms,
+ bool allowWeakImports, cpu_type_t cpuType, cpu_subtype_t cpuSubType,
+ bool enforceDylibSubtypesMatch, bool allowSimToMacOSX, bool addVers,
+ bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath,
+ bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode)
+: Base(strdup(path), mTime, ord, platforms, allowWeakImports, linkingFlatNamespace,
+ hoistImplicitPublicDylibs, allowSimToMacOSX, addVers)
{
+ std::unique_ptr<tapi::LinkerInterfaceFile> file;
std::string errorMessage;
+ __block uint32_t linkMinOSVersion = 0;
+ //FIXME handle this correctly once we have multi-platfrom TAPI
+ platforms.forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ if (linkMinOSVersion == 0)
+ linkMinOSVersion = version;
+ if (platform == ld::kPlatform_macOS)
+ linkMinOSVersion = version;
+ });
// <rdar://problem/29038544> Support $ld$weak symbols in .tbd files
#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1))
if (!allowWeakImports)
flags |= tapi::ParsingFlags::DisallowWeakImports;
- _interface.reset(tapi::LinkerInterfaceFile::create(
+ _interface = tapi::LinkerInterfaceFile::create(
path, cpuType, cpuSubType, flags,
- tapi::PackedVersion32(linkMinOSVersion), errorMessage));
+ tapi::PackedVersion32(linkMinOSVersion), errorMessage);
} else
#endif
#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 1) || (TAPI_API_VERSION_MAJOR > 1))
if (!allowWeakImports)
flags |= tapi::ParsingFlags::DisallowWeakImports;
- _interface.reset(tapi::LinkerInterfaceFile::create(
+ _interface = tapi::LinkerInterfaceFile::create(
path, fileContent, fileLength, cpuType, cpuSubType, flags,
- tapi::PackedVersion32(linkMinOSVersion), errorMessage));
+ tapi::PackedVersion32(linkMinOSVersion), errorMessage);
} else
#endif
#if (TAPI_API_VERSION_MAJOR >= 1)
auto matchingType = enforceDylibSubtypesMatch ?
tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
- _interface.reset(tapi::LinkerInterfaceFile::create(
+ _interface = tapi::LinkerInterfaceFile::create(
path, fileContent, fileLength, cpuType, cpuSubType, matchingType,
- tapi::PackedVersion32(linkMinOSVersion), errorMessage));
+ tapi::PackedVersion32(linkMinOSVersion), errorMessage);
}
#endif
if ( logAllFiles )
printf("%s\n", path);
- init(_interface.get(), opts, buildingForSimulator, indirectDylib, linkingFlatNamespace,
- linkingMainExecutable, path, platform, targetInstallPath);
+ init(_interface, opts, buildingForSimulator, indirectDylib, linkingFlatNamespace,
+ linkingMainExecutable, path, platforms, targetInstallPath, ignoreMismatchPlatform, usingBitcode);
}
template<typename A>
- File<A>::File(std::unique_ptr<tapi::LinkerInterfaceFile> &&file, const char* path, const Options *opts,
+ File<A>::File(tapi::LinkerInterfaceFile* file, const char* path, const Options *opts,
time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace,
bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
- Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports,
+ const ld::VersionSet& platforms, bool allowWeakImports,
cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool enforceDylibSubtypesMatch,
bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
- bool logAllFiles, const char* installPath, bool indirectDylib)
- : Base(strdup(path), mTime, ordinal, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace,
- hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), _interface(std::move(file))
+ bool logAllFiles, const char* installPath, bool indirectDylib,
+ bool ignoreMismatchPlatform, bool usingBitcode)
+ : Base(strdup(path), mTime, ordinal, platforms, allowWeakImports, linkingFlatNamespace,
+ hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), _interface(file)
{
- init(_interface.get(), opts, buildingForSimulator, indirectDylib, linkingFlatNamespace,
- linkingMainExecutable, path, platform, installPath);
+ init(_interface, opts, buildingForSimulator, indirectDylib, linkingFlatNamespace,
+ linkingMainExecutable, path, platforms, installPath, ignoreMismatchPlatform, usingBitcode);
}
template<typename A>
-void File<A>::init(tapi::LinkerInterfaceFile *file, const Options *opts, bool buildingForSimulator,
+void File<A>::init(tapi::LinkerInterfaceFile* file, const Options *opts, bool buildingForSimulator,
bool indirectDylib, bool linkingFlatNamespace, bool linkingMainExecutable,
- const char *path, Options::Platform platform, const char *targetInstallPath) {
+ const char *path, const ld::VersionSet& platforms, const char *targetInstallPath,
+ bool ignoreMismatchPlatform, bool usingBitcode) {
_opts = opts;
this->_bitcode = std::unique_ptr<ld::Bitcode>(new ld::Bitcode(nullptr, 0));
this->_noRexports = !file->hasReexportedLibraries();
this->_dylibCurrentVersion = file->getCurrentVersion();
this->_dylibCompatibilityVersion = file->getCompatibilityVersion();
this->_swiftVersion = file->getSwiftVersion();
- this->_objcConstraint = mapObjCConstraint(file->getObjCConstraint());
this->_parentUmbrella = file->getParentFrameworkName().empty() ? nullptr : strdup(file->getParentFrameworkName().c_str());
this->_appExtensionSafe = file->isApplicationExtensionSafe();
for (const auto &client : file->allowableClients())
this->_allowableClients.emplace_back(strdup(client.c_str()));
-
- auto dylibPlatform = mapPlatform(file->getPlatform());
- if ( (dylibPlatform != platform) && (platform != Options::kPlatformUnknown) ) {
- this->_wrongOS = true;
- if ( this->_addVersionLoadCommand && !indirectDylib ) {
- if ( buildingForSimulator ) {
- if ( !this->_allowSimToMacOSXLinking )
- throwf("building for %s simulator, but linking against dylib built for %s (%s).",
- Options::platformName(platform), Options::platformName(dylibPlatform), path);
+
+ ld::VersionSet lcPlatforms = mapPlatform(file->getPlatform(), useSimulatorVariant());
+ this->_platforms = lcPlatforms;
+
+ // check cross-linking
+ platforms.forEach(^(ld::Platform platform, uint32_t version, bool &stop) {
+ if (!lcPlatforms.contains(platform) ) {
+ this->_wrongOS = true;
+ if ( this->_addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) {
+ if (buildingForSimulator && !this->_allowSimToMacOSXLinking) {
+ if ( usingBitcode )
+ throwf("building for %s simulator, but linking against dylib built for %s,",
+ platforms.to_str().c_str(), lcPlatforms.to_str().c_str());
+ else
+ warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. "
+ "Note: This will be an error in the future.",
+ platforms.to_str().c_str(), path, lcPlatforms.to_str().c_str());
+ }
} else {
- throwf("building for %s, but linking against dylib built for %s (%s).",
- Options::platformName(platform), Options::platformName(dylibPlatform), path);
+ if ( usingBitcode )
+ throwf("building for %s, but linking against dylib built for %s,",
+ platforms.to_str().c_str(), lcPlatforms.to_str().c_str());
+ else if ( (getenv("RC_XBS") != NULL) && (getenv("RC_BUILDIT") == NULL) ) // FIXME: remove after platform bringup
+ warning("URGENT: building for %s, but linking against dylib (%s) built for %s. "
+ "Note: This will be an error in the future.",
+ platforms.to_str().c_str(), path, lcPlatforms.to_str().c_str());
}
}
- }
+ });
for (const auto& reexport : file->reexportedLibraries()) {
const char *path = strdup(reexport.c_str());
template <typename A>
void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) {
if (_interface)
- _opts->addTAPIInterface(_interface.get(), this->path());
+ _opts->addTAPIInterface(_interface, this->path());
Base::processIndirectLibraries(handler, addImplicitDylibs);
}
static ld::dylib::File* parse(const char* path, const uint8_t* fileContent,
uint64_t fileLength, time_t mTime,
ld::File::Ordinal ordinal, const Options& opts,
- bool indirectDylib)
+ bool indirectDylib, cpu_type_t architecture, cpu_subtype_t subArchitecture)
{
return new File<A>(path, fileContent, fileLength, &opts, mTime, ordinal,
opts.flatNamespace(),
opts.linkingMainExecutable(),
opts.implicitlyLinkIndirectPublicDylibs(),
- opts.platform(),
- opts.minOSversion(),
+ opts.platforms(),
opts.allowWeakImports(),
- opts.architecture(),
- opts.subArchitecture(),
+ architecture,
+ subArchitecture,
opts.enforceDylibSubtypesMatch(),
opts.allowSimulatorToLinkWithMacOSX(),
opts.addVersionLoadCommand(),
opts.targetIOSSimulator(),
opts.logAllFiles(),
opts.installPath(),
- indirectDylib);
+ indirectDylib,
+ opts.outputKind() == Options::kPreload,
+ opts.bundleBitcode());
}
- static ld::dylib::File* parse(const char* path, std::unique_ptr<tapi::LinkerInterfaceFile> &&file, time_t mTime,
+ static ld::dylib::File* parse(const char* path, tapi::LinkerInterfaceFile* file, time_t mTime,
ld::File::Ordinal ordinal, const Options& opts,
- bool indirectDylib)
+ bool indirectDylib, cpu_type_t architecture, cpu_subtype_t subArchitecture)
{
- return new File<A>(std::move(file), path, &opts, mTime, ordinal,
+ return new File<A>(file, path, &opts, mTime, ordinal,
opts.flatNamespace(),
opts.linkingMainExecutable(),
opts.implicitlyLinkIndirectPublicDylibs(),
- opts.platform(),
- opts.minOSversion(),
+ opts.platforms(),
opts.allowWeakImports(),
- opts.architecture(),
- opts.subArchitecture(),
+ architecture,
+ subArchitecture,
opts.enforceDylibSubtypesMatch(),
opts.allowSimulatorToLinkWithMacOSX(),
opts.addVersionLoadCommand(),
opts.targetIOSSimulator(),
opts.logAllFiles(),
opts.installPath(),
- indirectDylib);
+ indirectDylib,
+ opts.outputKind() == Options::kPreload,
+ opts.bundleBitcode());
}
};
-//
-// 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)
-{
- switch ( opts.architecture() ) {
+static ld::dylib::File* parseAsArchitecture(const uint8_t* fileContent, uint64_t fileLength, const char* path,
+ time_t modTime, ld::File::Ordinal ordinal, const Options& opts,
+ bool bundleLoader, bool indirectDylib,
+ cpu_type_t architecture, cpu_subtype_t subArchitecture)
+{
+ switch ( architecture ) {
#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
- if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
- return Parser<x86_64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
+ return Parser<x86_64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
#endif
#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
- if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
- return Parser<x86>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
+ return Parser<x86>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
#endif
#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
- if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
- return Parser<arm>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
+ return Parser<arm>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
#endif
#if SUPPORT_ARCH_arm64
case CPU_TYPE_ARM64:
- if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
- return Parser<arm64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
+ return Parser<arm64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
#endif
+ default:
+ throwf("unsupported architecture for tbd file");
}
- return nullptr;
+ assert(0 && "function should return valid pointer or throw");
}
-ld::dylib::File *parse(const char *path, std::unique_ptr<tapi::LinkerInterfaceFile> &&file, time_t modTime,
- ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) {
- switch ( opts.architecture() ) {
+
+static ld::dylib::File *parseAsArchitecture(const char *path, tapi::LinkerInterfaceFile* file, time_t modTime,
+ ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib,
+ cpu_type_t architecture, cpu_subtype_t subArchitecture)
+{
+ switch ( architecture ) {
#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
- return Parser<x86_64>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
+ return Parser<x86_64>::parse(path, file, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
#endif
#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
- return Parser<x86>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
+ return Parser<x86>::parse(path, file, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
#endif
#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
- return Parser<arm>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
+ return Parser<arm>::parse(path, file, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
#endif
#if SUPPORT_ARCH_arm64
case CPU_TYPE_ARM64:
- return Parser<arm64>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
+ return Parser<arm64>::parse(path, file, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
#endif
+ default:
+ throwf("unsupported architecture for tbd file");
}
- return nullptr;
+ assert(0 && "function should return valid pointer or throw");
+}
+
+//
+// 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)
+{
+ if (!tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
+ return nullptr;
+
+ try {
+ return parseAsArchitecture(fileContent, fileLength, path, modtime, ordinal, opts, bundleLoader, indirectDylib, opts.architecture(), opts.subArchitecture());
+ } catch (...) {
+ if (!opts.fallbackArchitecture())
+ throw;
+ }
+
+ warning("architecture %s not present in TBD %s, attempting fallback", opts.architectureName(), path);
+ return parseAsArchitecture(fileContent, fileLength, path, modtime, ordinal, opts, bundleLoader, indirectDylib, opts.fallbackArchitecture(), opts.fallbackSubArchitecture());
}
+ld::dylib::File *parse(const char *path, tapi::LinkerInterfaceFile* file, time_t modTime,
+ ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) {
+ try {
+ return parseAsArchitecture(path, file, modTime, ordinal, opts, indirectDylib, opts.architecture(), opts.subArchitecture());
+ } catch (...) {
+ if (!opts.fallbackArchitecture())
+ throw;
+ }
+
+ warning("architecture %s not present in TBD %s, attempting fallback", opts.architectureName(), path);
+ return parseAsArchitecture(path, file, modTime, ordinal, opts, indirectDylib, opts.fallbackArchitecture(), opts.fallbackSubArchitecture());
+}
} // namespace dylib
} // namespace textstub
time_t modTime, const Options& opts, ld::File::Ordinal ordinal,
bool bundleLoader, bool indirectDylib);
-extern ld::dylib::File *parse(const char *path, std::unique_ptr<tapi::LinkerInterfaceFile> &&file, time_t modTime,
+extern ld::dylib::File *parse(const char *path, tapi::LinkerInterfaceFile* file, time_t modTime,
ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib);
} // namespace dylib
// Write SDK version
if ( _options.sdkPaths().size() > 1 )
throwf("only one -syslibroot is accepted for bitcode bundle");
- if ( xar_prop_create((xar_file_t)linkXML, "platform", _options.getPlatformStr().c_str()) != 0 )
+ if ( xar_prop_create((xar_file_t)linkXML, "platform", _options.platforms().to_str().c_str()) != 0 )
throwf("could not add platform name to bitcode bundle");
if ( xar_prop_create((xar_file_t)linkXML, "sdkversion", _options.getSDKVersionStr().c_str()) != 0 )
throwf("could not add SDK version to bitcode bundle");
ld::Section GOTEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
ld::Section GOTEntryAtom::_s_sectionWeak("__DATA", "__got_weak", ld::Section::typeNonLazyPointer);
+#if SUPPORT_ARCH_arm64e
-static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Fixup* fixup, bool* optimizable, bool* targetIsExternalWeakDef)
+class GOTAuthEntryAtom : public ld::Atom {
+public:
+ GOTAuthEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool weakDef)
+ : ld::Atom(weakDef ? _s_sectionWeak : _s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)),
+ _fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetAuthData, (ld::Fixup::AuthData){ 0, true, ld::Fixup::AuthData::ptrauth_key_asia }),
+ _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStoreTargetAddressLittleEndianAuth64, target),
+ _target(target)
+ { _fixup2.weakImport = weakImport; internal.addAtom(*this); }
+
+ virtual const ld::File* file() const { return NULL; }
+ virtual const char* name() const { return _target->name(); }
+ virtual uint64_t size() const { return 8; }
+ 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 (ld::Fixup*)&_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; }
+
+private:
+ mutable ld::Fixup _fixup1;
+ mutable ld::Fixup _fixup2;
+ const ld::Atom* _target;
+
+ static ld::Section _s_section;
+ static ld::Section _s_sectionWeak;
+};
+
+ld::Section GOTAuthEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
+ld::Section GOTAuthEntryAtom::_s_sectionWeak("__DATA", "__got_weak", ld::Section::typeNonLazyPointer);
+
+#endif
+
+
+static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Atom* fixupAtom,
+ const ld::Fixup* fixup, bool* optimizable, bool* targetIsExternalWeakDef, bool* targetIsPersonalityFn)
{
*targetIsExternalWeakDef = false;
+ *targetIsPersonalityFn = false;
switch (fixup->kind) {
case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
#if SUPPORT_ARCH_arm64
case ld::Fixup::kindStoreX86PCRel32GOT:
#if SUPPORT_ARCH_arm64
case ld::Fixup::kindStoreARM64PCRelToGOT:
+#endif
+#if SUPPORT_ARCH_arm64e
+ // Note, this handles identifying DWARF unwind info personality functions
+ if (opts.supportsAuthenticatedPointers()) {
+ if (fixupAtom->section().type() == ld::Section::typeCFI)
+ *targetIsPersonalityFn = true;
+ }
#endif
*optimizable = false;
return true;
case ld::Fixup::kindNoneGroupSubordinatePersonality:
*optimizable = false;
+#if SUPPORT_ARCH_arm64e
+ // Note, this is a compact unwind info personality function
+ if (opts.supportsAuthenticatedPointers())
+ *targetIsPersonalityFn = true;
+#endif
return true;
default:
break;
}
};
+struct GotMapEntry {
+ const ld::Atom* atom;
+ bool isPersonalityFn;
+
+ bool operator<(const GotMapEntry& other) const {
+ if (atom != other.atom)
+ return atom < other.atom;
+ return (int)isPersonalityFn < (int)other.isPersonalityFn;
+ }
+};
+
void doPass(const Options& opts, ld::Internal& internal)
{
const bool log = false;
return;
// pre-fill gotMap with existing non-lazy pointers
- std::map<const ld::Atom*, const ld::Atom*> gotMap;
+ std::map<GotMapEntry, const ld::Atom*> gotMap;
for (ld::Internal::FinalSection* sect : internal.sections) {
if ( sect->type() != ld::Section::typeNonLazyPointer )
continue;
}
if ( target != NULL ) {
if (log) fprintf(stderr, "found existing got entry to %s\n", target->name());
- gotMap[target] = atom;
+ gotMap[{ target, false }] = atom;
}
}
}
}
bool optimizable;
bool targetIsExternalWeakDef;
- if ( !gotFixup(opts, internal, targetOfGOT, fit, &optimizable, &targetIsExternalWeakDef) )
+ bool targetIsPersonalityFn;
+ if ( !gotFixup(opts, internal, targetOfGOT, atom, fit, &optimizable, &targetIsExternalWeakDef, &targetIsPersonalityFn) )
continue;
if ( optimizable ) {
// change from load of GOT entry to lea of target
atomsReferencingGOT.push_back(atom);
atomUsesGOT = true;
}
- if ( gotMap.count(targetOfGOT) == 0 )
- gotMap[targetOfGOT] = NULL;
+ if ( gotMap.count({ targetOfGOT, targetIsPersonalityFn }) == 0 )
+ gotMap[{ targetOfGOT, targetIsPersonalityFn }] = NULL;
// record if target is weak def
weakDefMap[targetOfGOT] = targetIsExternalWeakDef;
// record weak_import attribute
// make GOT entries
for (auto& entry : gotMap) {
if ( entry.second == NULL ) {
- entry.second = new GOTEntryAtom(internal, entry.first, weakImportMap[entry.first], opts.useDataConstSegment() && weakDefMap[entry.first], is64);
- if (log) fprintf(stderr, "making new GOT slot for %s, gotMap[%p] = %p\n", entry.first->name(), entry.first, entry.second);
+#if SUPPORT_ARCH_arm64e
+ if ( entry.first.isPersonalityFn && (opts.supportsAuthenticatedPointers()) ) {
+ entry.second = new GOTAuthEntryAtom(internal, entry.first.atom, weakImportMap[entry.first.atom], opts.useDataConstSegment() && weakDefMap[entry.first.atom]);
+ if (log) fprintf(stderr, "making new GOT slot for %s, gotMap[%p] = %p\n", entry.first.atom->name(), entry.first.atom, entry.second);
+ continue;
+ }
+#endif
+ entry.second = new GOTEntryAtom(internal, entry.first.atom, weakImportMap[entry.first.atom], opts.useDataConstSegment() && weakDefMap[entry.first.atom], is64);
+ if (log) fprintf(stderr, "making new GOT slot for %s, gotMap[%p] = %p\n", entry.first.atom->name(), entry.first.atom, entry.second);
}
}
}
bool optimizable;
bool targetIsExternalWeakDef;
- if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, fit, &optimizable, &targetIsExternalWeakDef) )
+ bool targetIsPersonalityFn;
+ if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, atom, fit,
+ &optimizable, &targetIsExternalWeakDef, &targetIsPersonalityFn) )
continue;
if ( !optimizable ) {
// GOT use not optimized away, update to bind to GOT entry
case ld::Fixup::bindingDirectlyBound:
if ( log ) fprintf(stderr, "updating GOT use in %s to %s\n", atom->name(), targetOfGOT->name());
fitThatSetTarget->binding = ld::Fixup::bindingDirectlyBound;
- fitThatSetTarget->u.target = gotMap[targetOfGOT];
+ fitThatSetTarget->u.target = gotMap[{ targetOfGOT, targetIsPersonalityFn }];
break;
default:
assert(0 && "unsupported GOT reference");
template <typename A>
class ObjCImageInfoAtom : public ld::Atom {
public:
- ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint,
- bool compaction, bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion);
+ ObjCImageInfoAtom(bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion);
virtual const ld::File* file() const { return NULL; }
virtual const char* name() const { return "objc image info"; }
template <typename A>
-ObjCImageInfoAtom<A>::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction,
- bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion)
+ObjCImageInfoAtom<A>::ObjCImageInfoAtom(bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion)
: ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
symbolTableNotIn, false, false, false, ld::Atom::Alignment(2))
{
uint32_t value = 0;
- switch ( objcConstraint ) {
- case ld::File::objcConstraintNone:
- case ld::File::objcConstraintRetainRelease:
- if ( compaction )
- warning("ignoring -objc_gc_compaction because code not compiled for ObjC garbage collection");
- break;
- case ld::File::objcConstraintRetainReleaseOrGC:
- value |= OBJC_IMAGE_SUPPORTS_GC;
- if ( compaction )
- value |= OBJC_IMAGE_SUPPORTS_COMPACTION;
- break;
- case ld::File::objcConstraintGC:
- value |= OBJC_IMAGE_SUPPORTS_GC | OBJC_IMAGE_REQUIRES_GC;
- if ( compaction )
- value |= OBJC_IMAGE_SUPPORTS_COMPACTION;
- break;
- case ld::File::objcConstraintRetainReleaseForSimulator:
- value |= OBJC_IMAGE_IS_SIMULATED;
- break;
- }
-
if ( hasCategoryClassProperties ) {
value |= OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES;
}
const ld::Atom* Category<A>::getClassProperties(ld::Internal& state, const ld::Atom* contentAtom)
{
// Only specially-marked files have this field.
- if (!contentAtom->file()->objcHasCategoryClassPropertiesField()) return NULL;
-
- return ObjCData<A>::getPointerInContent(state, contentAtom, 6*sizeof(pint_t)); // category_t.classProperties
+ if ( const ld::relocatable::File* objFile = dynamic_cast<const ld::relocatable::File*>(contentAtom->file()) ) {
+ if ( objFile->objcHasCategoryClassPropertiesField() ) {
+ return ObjCData<A>::getPointerInContent(state, contentAtom, 6*sizeof(pint_t)); // category_t.classProperties
+ }
+ }
+ return NULL;
}
haveCategoriesWithNonNullClassProperties = true;
// fprintf(stderr, "category in file %s has non-null class properties\n", categoryAtom->safeFilePath());
}
-
- if (!categoryAtom->file()->objcHasCategoryClassPropertiesField()) {
- haveCategoriesWithoutClassPropertyStorage = true;
- // fprintf(stderr, "category in file %s has no storage for class properties\n", categoryAtom->safeFilePath());
+
+ if ( const ld::relocatable::File* objFile = dynamic_cast<const ld::relocatable::File*>(categoryAtom->file()) ) {
+ if ( !objFile->objcHasCategoryClassPropertiesField() ) {
+ haveCategoriesWithoutClassPropertyStorage = true;
+ // fprintf(stderr, "category in file %s has no storage for class properties\n", categoryAtom->safeFilePath());
+ }
}
}
}
void doPass(const Options& opts, ld::Internal& state)
{
// Do nothing if the output has no ObjC content.
- if ( state.objcObjectConstraint == ld::File::objcConstraintNone ) {
+ if ( !state.hasObjC ) {
return;
}
-
- // verify dylibs are GC compatible with object files
- if ( state.objcObjectConstraint != state.objcDylibConstraint ) {
- if ( (state.objcDylibConstraint == ld::File::objcConstraintRetainRelease)
- && (state.objcObjectConstraint == ld::File::objcConstraintGC) ) {
- throw "Linked dylibs built for retain/release but object files built for GC-only";
- }
- else if ( (state.objcDylibConstraint == ld::File::objcConstraintGC)
- && (state.objcObjectConstraint == ld::File::objcConstraintRetainRelease) ) {
- throw "Linked dylibs built for GC-only but object files built for retain/release";
- }
- }
-
+
if ( opts.objcCategoryMerging() ) {
// optimize classes defined in this linkage unit by merging in categories also in this linkage unit
OptimizeCategories<A>::doit(opts, state);
// add image info atom
// The HasCategoryClassProperties bit is set as often as possible.
- state.addAtom(*new ObjCImageInfoAtom<A>(state.objcObjectConstraint, opts.objcGcCompaction(), isObjC2,
- !haveCategoriesWithoutClassPropertyStorage, state.swiftVersion));
+ state.addAtom(*new ObjCImageInfoAtom<A>(isObjC2, !haveCategoriesWithoutClassPropertyStorage, state.swiftVersion));
}
#endif
#if SUPPORT_ARCH_arm64
case CPU_TYPE_ARM64:
+#if SUPPORT_ARCH_arm64e
+ if (opts.subArchitecture() == CPU_SUBTYPE_ARM64_E) {
+ doPass<arm64e, true>(opts, state);
+ break;
+ }
+#endif
doPass<arm64, true>(opts, state);
break;
#endif
class NonLazyPointerAtom : public ld::Atom {
public:
- NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo)
+ NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo,
+ bool weakImport)
: ld::Atom(_s_section, ld::Atom::definitionRegular,
ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeLazyPointer,
symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
_stubTo(stubTo),
_fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &stubTo) {
+ _fixup1.weakImport = weakImport;
pass.addAtom(*this);
}
class StubPICKextAtom : public ld::Atom {
public:
- StubPICKextAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo)
+ StubPICKextAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo,
+ bool weakImport)
: ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeStub,
- symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
+ symbolTableNotIn, false, true, false, ld::Atom::Alignment(2)),
_stubTo(stubTo),
- _nonLazyPointer(pass, stubTo),
+ _nonLazyPointer(pass, stubTo, weakImport),
_fixup1(0, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_nonLazyPointer),
_fixup2(0, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
_fixup3(0, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12),
_fixup6(4, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
_fixup7(4, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12),
_fixup8(4, ld::Fixup::k4of4, ld::Fixup::kindStoreThumbHigh16) {
- pass.addAtom(*this);
asprintf((char**)&_name, "%s.stub", _stubTo.name());
+ pass.addAtom(*this);
}
virtual const ld::File* file() const { return _stubTo.file(); }
const ld::Atom& stubTo)
: ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper,
- symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)),
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
_stubTo(stubTo),
_fixup1(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, helperHelper(pass)),
_fixup2(8, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer),
const ld::Atom& stubTo)
: ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper,
- symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)),
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
_stubTo(stubTo),
_fixup1(24, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, &stubTo),
_fixup2(28, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, lazyPointer),
bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport, bool dataConstUsed)
: ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeStub,
- symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)),
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
_stubTo(stubTo),
_lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, dataConstUsed),
_fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_lazyPointer),
class NonLazyPointerAtom : public ld::Atom {
public:
- NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo)
+ NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo,
+ bool weakImport)
: ld::Atom(_s_section, ld::Atom::definitionRegular,
ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)),
_stubTo(stubTo),
_fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, &stubTo) {
+ _fixup1.weakImport = weakImport;
pass.addAtom(*this);
}
ld::Section NonLazyPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
-class KextStubAtom : public ld::Atom {
+class NonLazyStubAtom : public ld::Atom {
public:
- KextStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo)
+ NonLazyStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo,
+ bool weakImport)
: ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeStub,
- symbolTableIn, false, false, false, ld::Atom::Alignment(1)),
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
_stubTo(stubTo),
- _nonLazyPointer(pass, stubTo),
+ _nonLazyPointer(pass, stubTo, weakImport),
_fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_nonLazyPointer),
- _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_nonLazyPointer) {
- pass.addAtom(*this);
+ _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_nonLazyPointer) {
asprintf((char**)&_name, "%s.stub", _stubTo.name());
+ pass.addAtom(*this);
}
virtual const ld::File* file() const { return _stubTo.file(); }
static ld::Section _s_section;
};
-ld::Section KextStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeCode);
+ld::Section NonLazyStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub);
} // namespace arm64
+#if SUPPORT_ARCH_arm64e
+
+// already in ld::passes::stubs namespace
+namespace arm64e {
+
+
+
+class FastBindingPointerAtom : public ld::Atom {
+public:
+ FastBindingPointerAtom(ld::passes::stubs::Pass& pass)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)),
+ _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64,
+ pass.internal()->compressedFastBinderProxy)
+ { pass.addAtom(*this); }
+
+ virtual const ld::File* file() const { return NULL; }
+ virtual const char* name() const { return "fast binder pointer"; }
+ virtual uint64_t size() const { return 8; }
+ 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 &_fixup; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; }
+
+private:
+ mutable ld::Fixup _fixup;
+
+ static ld::Section _s_section;
+};
+
+ld::Section FastBindingPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
+
+
+class ImageCachePointerAtom : public ld::Atom {
+public:
+ ImageCachePointerAtom(ld::passes::stubs::Pass& pass)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)) { pass.addAtom(*this); }
+
+ virtual const ld::File* file() const { return NULL; }
+ virtual const char* name() const { return "image cache pointer"; }
+ virtual uint64_t size() const { return 8; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const { }
+ virtual void setScope(Scope) { }
+
+private:
+
+ static ld::Section _s_section;
+};
+
+ld::Section ImageCachePointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
+
+
+
+
+
+//
+// The stub-helper-helper is the common code factored out of each helper function.
+// It is in the same section as the stub-helpers.
+// Similar to the PLT0 entry in ELF.
+//
+class StubHelperHelperAtom : public ld::Atom {
+public:
+ StubHelperHelperAtom(ld::passes::stubs::Pass& pass)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
+ _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, compressedImageCache(pass)),
+ _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedImageCache(pass)),
+ _fixup3(12, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, compressedFastBinder(pass)),
+ _fixup4(16, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedFastBinder(pass)),
+ _fixup5(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_ADD, 0, 4),
+ _fixup6(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_LDR, 12, 16)
+ { pass.addAtom(*this); }
+
+ virtual ld::File* file() const { return NULL; }
+ virtual const char* name() const { return "helper helper"; }
+ virtual uint64_t size() const { return 24; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ OSWriteLittleInt32(&buffer[ 0], 0, 0x90000011); // ADRP X17, dyld_mageLoaderCache@page
+ OSWriteLittleInt32(&buffer[ 4], 0, 0x91000231); // ADD X17, X17, dyld_mageLoaderCache@pageoff
+ OSWriteLittleInt32(&buffer[ 8], 0, 0xA9BF47F0); // STP X16/X17, [SP, #-16]!
+ OSWriteLittleInt32(&buffer[12], 0, 0x90000010); // ADRP X16, _fast_lazy_bind@page
+ OSWriteLittleInt32(&buffer[16], 0, 0xF9400210); // LDR X16, [X16,_fast_lazy_bind@pageoff]
+ OSWriteLittleInt32(&buffer[20], 0, 0xD61F0200); // BR X16
+ }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup6)[1]; }
+
+private:
+ static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) {
+ if ( pass.compressedImageCache == NULL )
+ pass.compressedImageCache = new ImageCachePointerAtom(pass);
+ return pass.compressedImageCache;
+ }
+ static ld::Atom* compressedFastBinder(ld::passes::stubs::Pass& pass) {
+ if ( pass.compressedFastBinderPointer == NULL )
+ pass.compressedFastBinderPointer = new FastBindingPointerAtom(pass);
+ return pass.compressedFastBinderPointer;
+ }
+
+ mutable ld::Fixup _fixup1;
+ ld::Fixup _fixup2;
+ ld::Fixup _fixup3;
+ ld::Fixup _fixup4;
+ ld::Fixup _fixup5;
+ ld::Fixup _fixup6;
+
+ static ld::Section _s_section;
+};
+
+ld::Section StubHelperHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper);
+
+
+
+class StubHelperAtom : public ld::Atom {
+public:
+ StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer,
+ const ld::Atom& stubTo)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
+ _stubTo(stubTo),
+ _fixup1(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, helperHelper(pass)),
+ _fixup2(8, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer),
+ _fixup3(8, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32) { }
+
+ virtual const ld::File* file() const { return _stubTo.file(); }
+ virtual const char* name() const { return _stubTo.name(); }
+ virtual uint64_t size() const { return 12; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ OSWriteLittleInt32(&buffer[0], 0, 0x18000050); // LDR W16, L0
+ OSWriteLittleInt32(&buffer[4], 0, 0x14000000); // B helperhelper
+ OSWriteLittleInt32(&buffer[8], 0, 0x00000000); // L0: .long 0
+ }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; }
+
+private:
+ static ld::Atom* helperHelper(ld::passes::stubs::Pass& pass) {
+ if ( pass.compressedHelperHelper == NULL )
+ pass.compressedHelperHelper = new StubHelperHelperAtom(pass);
+ return pass.compressedHelperHelper;
+ }
+
+ const ld::Atom& _stubTo;
+ mutable ld::Fixup _fixup1;
+ ld::Fixup _fixup2;
+ ld::Fixup _fixup3;
+
+ static ld::Section _s_section;
+};
+
+ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper);
+
+
+class ResolverHelperAtom : public ld::Atom {
+public:
+ ResolverHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer,
+ const ld::Atom& stubTo)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
+ _stubTo(stubTo),
+ _fixup1(24, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, &stubTo),
+ _fixup2(28, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, lazyPointer),
+ _fixup3(32, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, lazyPointer) { }
+
+ virtual const ld::File* file() const { return _stubTo.file(); }
+ virtual const char* name() const { return _stubTo.name(); }
+ virtual uint64_t size() const { return 68; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ OSWriteLittleInt32(&buffer[ 0], 0, 0xa9bf7bfd); // stp fp, lr, [sp, #-16]!
+ OSWriteLittleInt32(&buffer[ 4], 0, 0x910003fd); // mov fp, sp
+ OSWriteLittleInt32(&buffer[ 8], 0, 0xa9bf03e1); // stp x1, x0, [sp, #-16]!
+ OSWriteLittleInt32(&buffer[12], 0, 0xa9bf0be3); // stp x3, x2, [sp, #-16]!
+ OSWriteLittleInt32(&buffer[16], 0, 0xa9bf13e5); // stp x5, x4, [sp, #-16]!
+ OSWriteLittleInt32(&buffer[20], 0, 0xa9bf1be7); // stp x7, x6, [sp, #-16]!
+ OSWriteLittleInt32(&buffer[24], 0, 0x94000000); // bl _foo
+ OSWriteLittleInt32(&buffer[28], 0, 0x90000010); // adrp x16, lazy_pointer@PAGE
+ OSWriteLittleInt32(&buffer[32], 0, 0x91000210); // add x16, x16, lazy_pointer@PAGEOFF
+ OSWriteLittleInt32(&buffer[36], 0, 0xf9000200); // str x0, [x16]
+ OSWriteLittleInt32(&buffer[40], 0, 0xaa0003f0); // mov x16, x0
+ OSWriteLittleInt32(&buffer[44], 0, 0xa8c11be7); // ldp x7, x6, [sp], #16
+ OSWriteLittleInt32(&buffer[48], 0, 0xa8c113e5); // ldp x5, x4, [sp], #16
+ OSWriteLittleInt32(&buffer[52], 0, 0xa8c10be3); // ldp x3, x2, [sp], #16
+ OSWriteLittleInt32(&buffer[56], 0, 0xa8c103e1); // ldp x1, x0, [sp], #16
+ OSWriteLittleInt32(&buffer[60], 0, 0xa8c17bfd); // ldp fp, lr, [sp], #16
+ OSWriteLittleInt32(&buffer[64], 0, 0xD61F0A1F); // braaz x16
+ }
+
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; }
+
+private:
+
+ const ld::Atom& _stubTo;
+ mutable ld::Fixup _fixup1;
+ ld::Fixup _fixup2;
+ ld::Fixup _fixup3;
+
+ static ld::Section _s_section;
+};
+
+ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper);
+
+
+
+class LazyPointerAtom : public ld::Atom {
+public:
+ LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo,
+ bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport, bool dataConstUsed)
+ : ld::Atom(selectSection(stubToGlobalWeakDef, stubToResolver, dataConstUsed),
+ ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeTranslationUnit, ld::Atom::typeLazyPointer,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)),
+ _stubTo(stubTo),
+ _helper(pass, this, stubTo),
+ _resolverHelper(pass, this, stubTo),
+ _fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetAuthData, (ld::Fixup::AuthData){ 0, false, ld::Fixup::AuthData::ptrauth_key_asia }),
+ _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStoreTargetAddressLittleEndianAuth64,
+ stubToResolver ? &_resolverHelper : (stubToGlobalWeakDef ? &stubTo : &_helper)),
+ _fixup3(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) {
+ _fixup2.weakImport = weakImport; pass.addAtom(*this);
+ if ( stubToResolver )
+ pass.addAtom(_resolverHelper);
+ else if ( !stubToGlobalWeakDef )
+ pass.addAtom(_helper);
+ }
+
+ virtual const ld::File* file() const { return _stubTo.file(); }
+ virtual const char* name() const { return _stubTo.name(); }
+ virtual uint64_t size() const { return 8; }
+ 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 &_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; }
+
+private:
+ static ld::Section& selectSection(bool stubToGlobalWeakDef, bool stubToResolver, bool dataConstUsed) {
+ if ( stubToGlobalWeakDef && dataConstUsed )
+ return _s_sectionWeak;
+ else if ( stubToResolver && dataConstUsed )
+ return _s_sectionResolver;
+ else
+ return _s_section;
+ }
+
+ const ld::Atom& _stubTo;
+ StubHelperAtom _helper;
+ ResolverHelperAtom _resolverHelper;
+ mutable ld::Fixup _fixup1;
+ ld::Fixup _fixup2;
+ ld::Fixup _fixup3;
+
+ static ld::Section _s_section;
+ static ld::Section _s_sectionResolver;
+ static ld::Section _s_sectionWeak;
+};
+
+ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer);
+ld::Section LazyPointerAtom::_s_sectionResolver("__DATA_DIRTY", "__la_resolver", ld::Section::typeLazyPointer);
+ld::Section LazyPointerAtom::_s_sectionWeak("__DATA", "__la_weak_ptr", ld::Section::typeLazyPointer);
+
+
+class StubAtom : public ld::Atom {
+public:
+ StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo,
+ bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport, bool dataConstUsed)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeStub,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
+ _stubTo(stubTo),
+ _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, dataConstUsed),
+ _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_lazyPointer),
+ _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_lazyPointer),
+ _fixup3(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_LDR, 0, 4)
+ { pass.addAtom(*this); }
+
+ virtual const ld::File* file() const { return _stubTo.file(); }
+ virtual const char* name() const { return _stubTo.name(); }
+ virtual uint64_t size() const { return 12; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ OSWriteLittleInt32(&buffer[0], 0, 0x90000010); // ADRP X16, lazy_pointer@page
+ OSWriteLittleInt32(&buffer[4], 0, 0xF9400210); // LDR X16, [X16, lazy_pointer@pageoff]
+ OSWriteLittleInt32(&buffer[8], 0, 0xD61F0A1F); // BRAAZ X16
+ }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; }
+
+private:
+ const ld::Atom& _stubTo;
+ LazyPointerAtom _lazyPointer;
+ mutable ld::Fixup _fixup1;
+ mutable ld::Fixup _fixup2;
+ mutable ld::Fixup _fixup3;
+
+ static ld::Section _s_section;
+};
+
+ld::Section StubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub);
+
+
+
+class NonLazyPointerAtom : public ld::Atom {
+public:
+ NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo,
+ bool weakImport)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular,
+ ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)),
+ _stubTo(stubTo),
+ _fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetAuthData, (ld::Fixup::AuthData){ 0, true, ld::Fixup::AuthData::ptrauth_key_asia }),
+ _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStoreTargetAddressLittleEndianAuth64, &stubTo) {
+ _fixup2.weakImport = weakImport;
+ pass.addAtom(*this);
+ }
+
+ virtual const ld::File* file() const { return _stubTo.file(); }
+ virtual const char* name() const { return _stubTo.name(); }
+ virtual uint64_t size() const { return 8; }
+ 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 (ld::Fixup*)&_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; }
+
+private:
+ const ld::Atom& _stubTo;
+ ld::Fixup _fixup1;
+ ld::Fixup _fixup2;
+
+ static ld::Section _s_section;
+};
+
+ld::Section NonLazyPointerAtom::_s_section("__DATA", "__auth_got", ld::Section::typeNonLazyPointer);
+
+
+class NonLazyStubAtom : public ld::Atom {
+public:
+ NonLazyStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo,
+ bool weakImport)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeStub,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
+ _stubTo(stubTo),
+ _nonLazyPointer(pass, stubTo, weakImport),
+ _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_nonLazyPointer),
+ _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_nonLazyPointer) {
+ asprintf((char**)&_name, "%s.stub", _stubTo.name());
+ pass.addAtom(*this);
+ }
+
+ virtual const ld::File* file() const { return _stubTo.file(); }
+ virtual const char* name() const { return _name; }
+ virtual uint64_t size() const { return 16; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ OSWriteLittleInt32(&buffer[ 0], 0, 0x90000011); // ADRP X17, dyld_mageLoaderCache@page
+ OSWriteLittleInt32(&buffer[ 4], 0, 0x91000231); // ADD X17, X17, dyld_mageLoaderCache@pageoff
+ OSWriteLittleInt32(&buffer[ 8], 0, 0xF9400230); // LDR X16, [X17]
+ OSWriteLittleInt32(&buffer[12], 0, 0xD71F0A11); // BRAA X16, X17
+ }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; }
+
+private:
+ const ld::Atom& _stubTo;
+ const char* _name;
+ NonLazyPointerAtom _nonLazyPointer;
+ mutable ld::Fixup _fixup1;
+ mutable ld::Fixup _fixup2;
+
+ static ld::Section _s_section;
+};
+
+ld::Section NonLazyStubAtom::_s_section("__TEXT", "__auth_stubs", ld::Section::typeStub);
+
+
+} // namespace arm64e
+#endif
+
ld::Section StubAtom::_s_section("__TEXT", "__symbol_stub", ld::Section::typeStub);
+class NonLazyPointerAtom : public ld::Atom {
+public:
+ NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& target,
+ bool weakImport)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular,
+ ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
+ _target(target),
+ _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &target) {
+ _fixup1.weakImport = weakImport;
+ pass.addAtom(*this);
+ }
+
+ virtual const ld::File* file() const { return _target.file(); }
+ virtual const char* name() const { return _target.name(); }
+ virtual uint64_t size() const { return 4; }
+ 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 (ld::Fixup*)&_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; }
+
+private:
+ const ld::Atom& _target;
+ ld::Fixup _fixup1;
+
+ static ld::Section _s_section;
+};
+
+ld::Section NonLazyPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer);
+
+
+class NonLazyStubAtom : public ld::Atom {
+public:
+ NonLazyStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& target,
+ bool weakImport)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeStub,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)),
+ _target(target),
+ _nonlazyPointer(pass, target, weakImport),
+ _fixup1(8, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_nonlazyPointer),
+ _fixup2(8, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this),
+ _fixup3(8, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 5),
+ _fixup4(8, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) { pass.addAtom(*this); }
+
+ virtual const ld::File* file() const { return _target.file(); }
+ virtual const char* name() const { return _target.name(); }
+ virtual uint64_t size() const { return 14; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ buffer[0] = 0xE8; // call next instruction (picbase)
+ buffer[1] = 0x00;
+ buffer[2] = 0x00;
+ buffer[3] = 0x00;
+ buffer[4] = 0x00;
+ buffer[5] = 0x58; // pop eax
+ buffer[6] = 0x8D; // lea foo$nonlazy_pointer-picbase(eax),eax
+ buffer[7] = 0x80;
+ buffer[8] = 0x00;
+ buffer[9] = 0x00;
+ buffer[10] = 0x00;
+ buffer[11] = 0x00;
+ buffer[12] = 0xFF; // jmp *(eax)
+ buffer[13] = 0x20;
+ }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; }
+
+private:
+ const ld::Atom& _target;
+ NonLazyPointerAtom _nonlazyPointer;
+ mutable ld::Fixup _fixup1;
+ ld::Fixup _fixup2;
+ ld::Fixup _fixup3;
+ ld::Fixup _fixup4;
+
+ static ld::Section _s_section;
+};
+
+ld::Section NonLazyStubAtom::_s_section("__TEXT", "__symbol_stub", ld::Section::typeStub);
+
} // namespace x86
class NonLazyPointerAtom : public ld::Atom {
public:
- NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo)
+ NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo,
+ bool weakImport)
: ld::Atom(_s_section, ld::Atom::definitionRegular,
ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer,
symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)),
_stubTo(stubTo),
_fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, &stubTo) {
+ _fixup1.weakImport = weakImport;
pass.addAtom(*this);
}
-class KextStubAtom : public ld::Atom {
+class NonLazyStubAtom : public ld::Atom {
public:
- KextStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo)
+ NonLazyStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo,
+ bool weakImport)
: ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeStub,
symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)),
_stubTo(stubTo),
- _nonLazyPointer(pass, stubTo),
+ _nonLazyPointer(pass, stubTo, weakImport),
_fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &_nonLazyPointer) { pass.addAtom(*this); }
virtual const ld::File* file() const { return _stubTo.file(); }
static ld::Section _s_section;
};
-ld::Section KextStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub);
+ld::Section NonLazyStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub);
} // namespace x86_64
const cpu_type_t _architecture;
const bool _lazyDylibsInUuse;
const bool _compressedLINKEDIT;
- const bool _prebind;
const bool _mightBeInSharedRegion;
const bool _pic;
const bool _flatNamespace;
_architecture(opts.architecture()),
_lazyDylibsInUuse(opts.usingLazyDylibLinking()),
_compressedLINKEDIT(opts.makeCompressedDyldInfo()),
- _prebind(opts.prebind()),
_mightBeInSharedRegion(opts.sharedRegionEligible()),
_pic(opts.outputSlidable()),
_flatNamespace(opts.nameSpace() != Options::kTwoLevelNameSpace),
switch ( _architecture ) {
#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
- if ( usingCompressedLINKEDIT() && !forLazyDylib )
+ if ( usingCompressedLINKEDIT() && !forLazyDylib && _options.noLazyBinding() && !stubToResolver)
+ return new ld::passes::stubs::x86::NonLazyStubAtom(*this, target, weakImport);
+ else if ( usingCompressedLINKEDIT() && !forLazyDylib )
return new ld::passes::stubs::x86::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport);
else
return new ld::passes::stubs::x86::classic::StubAtom(*this, target, forLazyDylib, weakImport);
#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() )
- return new ld::passes::stubs::x86_64::KextStubAtom(*this, target);
+ return new ld::passes::stubs::x86_64::NonLazyStubAtom(*this, target, weakImport);
+ else if ( usingCompressedLINKEDIT() && !forLazyDylib && _options.noLazyBinding() && !stubToResolver )
+ return new ld::passes::stubs::x86_64::NonLazyStubAtom(*this, target, weakImport);
else if ( usingCompressedLINKEDIT() && !forLazyDylib )
return new ld::passes::stubs::x86_64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport);
else
case CPU_TYPE_ARM:
if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() ) {
// if text relocs are not allows in kext bundles, then linker must create a stub
- return new ld::passes::stubs::arm::StubPICKextAtom(*this, target);
+ return new ld::passes::stubs::arm::StubPICKextAtom(*this, target, weakImport);
}
else if ( usingCompressedLINKEDIT() && !forLazyDylib ) {
if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText && !_options.makeEncryptable() )
return new ld::passes::stubs::arm::StubCloseAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport);
+ else if ( usingCompressedLINKEDIT() && !forLazyDylib && _options.noLazyBinding() && !stubToResolver)
+ return new ld::passes::stubs::arm::StubPICKextAtom(*this, target, weakImport);
else if ( _pic )
return new ld::passes::stubs::arm::StubPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport, usingDataConst);
else
#endif
#if SUPPORT_ARCH_arm64
case CPU_TYPE_ARM64:
+#if SUPPORT_ARCH_arm64e
+ if ( (_options.subArchitecture() == CPU_SUBTYPE_ARM64_E) && _options.useAuthenticatedStubs() ) {
+ if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() )
+ return new ld::passes::stubs::arm64e::NonLazyStubAtom(*this, target, weakImport);
+ else if ( usingCompressedLINKEDIT() && !forLazyDylib && _options.noLazyBinding() && !stubToResolver )
+ return new ld::passes::stubs::arm64e::NonLazyStubAtom(*this, target, weakImport);
+ else
+ return new ld::passes::stubs::arm64e::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport, usingDataConst);
+ break;
+ }
+#endif
if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() )
- return new ld::passes::stubs::arm64::KextStubAtom(*this, target);
+ return new ld::passes::stubs::arm64::NonLazyStubAtom(*this, target, weakImport);
+ else if ( usingCompressedLINKEDIT() && !forLazyDylib && _options.noLazyBinding() && !stubToResolver )
+ return new ld::passes::stubs::arm64::NonLazyStubAtom(*this, target, weakImport);
else
return new ld::passes::stubs::arm64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport, usingDataConst);
break;
return;
// <rdar://problem/8553283> lazily check for helper
- if ( !_options.makeCompressedDyldInfo() && (state.classicBindingHelper == NULL) && (_options.outputKind() != Options::kKextBundle) )
+ if ( !_options.makeCompressedDyldInfo() && !_options.makeThreadedStartsSection() && (state.classicBindingHelper == NULL) && (_options.outputKind() != Options::kKextBundle) )
throw "symbol dyld_stub_binding_helper not found, normally in crt1.o/dylib1.o/bundle1.o";
// disable arm close stubs in some cases
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 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 <libkern/OSByteOrder.h>
+
+#include <vector>
+#include <map>
+
+#include "MachOFileAbstraction.hpp"
+#include "Architectures.hpp"
+#include "ld.hpp"
+#include "thread_starts.h"
+
+namespace ld {
+namespace passes {
+namespace thread_starts {
+
+
+static std::map<const Atom*, uint64_t> sAtomToAddress;
+
+
+
+
+template <typename A>
+class ThreadStartsAtom : public ld::Atom {
+public:
+ ThreadStartsAtom(uint32_t fixupAlignment, uint32_t numThreadStarts);
+ ~ThreadStartsAtom();
+
+ virtual const ld::File* file() const { return NULL; }
+ virtual const char* name() const { return "thread starts"; }
+ virtual uint64_t size() const { return 4 + (_numThreadStarts * 4); }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ uint32_t header = 0;
+ if (_fixupAlignment == 8)
+ header |= 1;
+ bzero(buffer, size());
+ A::P::E::set32(*((uint32_t*)(&buffer[0])), header); // header
+ // Fill in offsets with 0xFFFFFFFF's for now as that wouldn't be a valid offset
+ memset(&buffer[4], 0xFFFFFFFF, _numThreadStarts * sizeof(uint32_t));
+ }
+ virtual void setScope(Scope) { }
+ virtual Fixup::iterator fixupsBegin() const { return NULL; }
+ virtual Fixup::iterator fixupsEnd() const { return NULL; }
+
+private:
+
+ uint32_t _fixupAlignment;
+ uint32_t _numThreadStarts;
+
+ static bool _s_log;
+ static ld::Section _s_section;
+};
+
+template <typename A>
+bool ThreadStartsAtom<A>::_s_log = false;
+
+template <typename A>
+ld::Section ThreadStartsAtom<A>::_s_section("__TEXT", "__thread_starts", ld::Section::typeThreadStarts);
+
+
+template <typename A>
+ThreadStartsAtom<A>::ThreadStartsAtom(uint32_t fixupAlignment, uint32_t numThreadStarts)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)),
+ _fixupAlignment(fixupAlignment), _numThreadStarts(numThreadStarts)
+{
+ assert(_fixupAlignment == 4 || _fixupAlignment == 8);
+}
+
+template <typename A>
+ThreadStartsAtom<A>::~ThreadStartsAtom()
+{
+}
+
+
+static void buildAddressMap(const Options& opts, ld::Internal& state) {
+ // Assign addresses to sections
+ state.setSectionSizesAndAlignments();
+ state.assignFileOffsets();
+
+ // Assign addresses to atoms in a side table
+ const bool log = false;
+ if ( log ) fprintf(stderr, "buildAddressMap()\n");
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ uint16_t maxAlignment = 0;
+ uint64_t offset = 0;
+ if ( log ) fprintf(stderr, " section=%s/%s, address=0x%08llX\n", sect->segmentName(), sect->sectionName(), sect->address);
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2;
+ uint32_t atomModulus = atom->alignment().modulus;
+ if ( atomAlignmentPowerOf2 > maxAlignment )
+ maxAlignment = atomAlignmentPowerOf2;
+ // calculate section offset for this atom
+ uint64_t alignment = 1 << atomAlignmentPowerOf2;
+ uint64_t currentModulus = (offset % alignment);
+ uint64_t requiredModulus = atomModulus;
+ if ( currentModulus != requiredModulus ) {
+ if ( requiredModulus > currentModulus )
+ offset += requiredModulus-currentModulus;
+ else
+ offset += requiredModulus+alignment-currentModulus;
+ }
+
+ if ( log ) fprintf(stderr, " 0x%08llX atom=%p, name=%s\n", sect->address+offset, atom, atom->name());
+ sAtomToAddress[atom] = sect->address + offset;
+
+ offset += atom->size();
+ }
+ }
+}
+
+static uint32_t threadStartsCountInSection(std::vector<uint64_t>& fixupAddressesInSection) {
+ if (fixupAddressesInSection.empty())
+ return 0;
+
+ std::sort(fixupAddressesInSection.begin(), fixupAddressesInSection.end());
+
+ uint32_t numThreadStarts = 0;
+
+ // Walk all the fixups, and compute the number of rebase chains we need, assuming
+ // 11-bits of delta, with 4-byte alignment for each entry.
+ const uint64_t deltaBits = 11;
+ const uint64_t minAlignment = 4;
+
+ uint64_t prevAddress = 0;
+ for (uint64_t address : fixupAddressesInSection) {
+ uint64_t delta = address - prevAddress;
+ assert( (delta & (minAlignment - 1)) == 0 );
+ delta /= minAlignment;
+ if (delta >= (1 << deltaBits)) {
+ ++numThreadStarts;
+ }
+ prevAddress = address;
+ }
+ fixupAddressesInSection.clear();
+
+ return numThreadStarts;
+}
+
+static uint32_t processSections(ld::Internal& state, uint64_t minAlignment) {
+ uint32_t numThreadStarts = 0;
+
+ std::vector<uint64_t> fixupAddressesInSection;
+ for (ld::Internal::FinalSection* sect : state.sections) {
+ if ( sect->isSectionHidden() )
+ continue;
+ for (const ld::Atom* atom : sect->atoms) {
+ bool seenTarget = false;
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ if ( fit->firstInCluster() ) {
+ seenTarget = false;
+ }
+ if ( fit->setsTarget(false) ) {
+ seenTarget = true;
+ }
+ if ( !fit->lastInCluster() )
+ continue;
+ if ( !fit->isStore() )
+ continue;
+ if ( fit->isPcRelStore(false) )
+ continue;
+ if ( !seenTarget )
+ continue;
+ uint64_t address = sAtomToAddress[atom] + fit->offsetInAtom;
+ fixupAddressesInSection.push_back(address);
+
+ if ( (address & (minAlignment-1)) != 0 ) {
+ throwf("pointer not aligned at address 0x%llX (%s + %d from %s)",
+ address, atom->name(), fit->offsetInAtom, atom->safeFilePath());
+ }
+ }
+ }
+ numThreadStarts += threadStartsCountInSection(fixupAddressesInSection);
+ }
+
+ return numThreadStarts;
+}
+
+void doPass(const Options& opts, ld::Internal& state)
+{
+ if ( !opts.makeThreadedStartsSection() )
+ return;
+
+ buildAddressMap(opts, state);
+
+ const uint32_t fixupAlignment = 4;
+ uint32_t numThreadStarts = processSections(state, fixupAlignment);
+
+ // create atom that contains the whole compact unwind table
+ switch ( opts.architecture() ) {
+#if SUPPORT_ARCH_x86_64
+ case CPU_TYPE_X86_64:
+ state.addAtom(*new ThreadStartsAtom<x86_64>(fixupAlignment, numThreadStarts));
+ break;
+#endif
+#if SUPPORT_ARCH_i386
+ case CPU_TYPE_I386:
+ state.addAtom(*new ThreadStartsAtom<x86>(fixupAlignment, numThreadStarts));
+ break;
+#endif
+#if SUPPORT_ARCH_arm64
+ case CPU_TYPE_ARM64:
+ state.addAtom(*new ThreadStartsAtom<arm64>(fixupAlignment, numThreadStarts));
+ break;
+#endif
+#if SUPPORT_ARCH_arm_any
+ case CPU_TYPE_ARM:
+ state.addAtom(*new ThreadStartsAtom<arm>(fixupAlignment, numThreadStarts));
+ break;
+#endif
+ default:
+ assert(0 && "no threaded starts for arch");
+ }
+}
+
+
+} // namespace thread_starts
+} // namespace passes
+} // namespace ld
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 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 __THREAD_STARTS_H__
+#define __THREAD_STARTS_H__
+
+#include "Options.h"
+#include "ld.hpp"
+
+
+namespace ld {
+namespace passes {
+namespace thread_starts {
+
+// called by linker to generate a thread starts section
+extern void doPass(const Options& opts, ld::Internal& internal);
+
+
+} // namespace thread_starts
+} // namespace passes
+} // namespace ld
+
+#endif // __THREAD_STARTS_H__
#include "parsers/macho_relocatable_file.h"
#include "parsers/lto_file.h"
+const ld::VersionSet ld::File::_platforms;
+
static bool sDumpContent= true;
static bool sDumpStabs = false;
static bool sSort = true;
if ( ! foundFatSlice ) {
cpu_type_t archOfObj;
cpu_subtype_t subArchOfObj;
- Options::Platform platform;
- if ( mach_o::relocatable::isObjectFile(p, &archOfObj, &subArchOfObj, &platform) ) {
+ ld::Platform platform;
+ uint32_t minOS;
+ if ( mach_o::relocatable::isObjectFile(p, &archOfObj, &subArchOfObj, &platform, &minOS) ) {
objOpts.architecture = archOfObj;
objOpts.subType = subArchOfObj;
}
#include <errno.h>
#include <vector>
+#include <tuple>
#include <set>
#include <unordered_set>
return "main-executable";
case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
return "flat-namespace";
+ case BIND_SPECIAL_DYLIB_WEAK_LOOKUP:
+ return "weak";
}
if ( libraryOrdinal < BIND_SPECIAL_DYLIB_FLAT_LOOKUP )
throw "unknown special ordinal";
template <typename A>
void DyldInfoPrinter<A>::printRebaseInfo()
{
+ bool seenThreadedRebase = false;
if ( (fInfo == NULL) || (fInfo->rebase_off() == 0) ) {
- printf("no compressed rebase info\n");
+ // If we have no rebase opcodes, then we may be using the threaded rebase/bind combined
+ // format and need to parse the bind opcodes instead.
+ if ( (fInfo->rebase_size() == 0) && (fInfo->bind_size() != 0) ) {
+ const uint8_t* p = (uint8_t*)fHeader + fInfo->bind_off();
+ const uint8_t* end = &p[fInfo->bind_size()];
+
+ uint8_t type = 0;
+ uint8_t flags = 0;
+ uint8_t segIndex = 0;
+ uint64_t segOffset = 0;
+ const char* symbolName = NULL;
+ const char* fromDylib = "??";
+ int libraryOrdinal = 0;
+ int64_t addend = 0;
+ uint32_t count;
+ uint32_t skip;
+ pint_t segStartAddr = 0;
+ const char* segName = "??";
+ const char* typeName = "??";
+ const char* weak_import = "";
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = read_uleb128(p, end);
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ flags = immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT;
+ if ( flags != 0 )
+ weak_import = " (weak import)";
+ else
+ weak_import = "";
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ typeName = bindTypeName(type);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segStartAddr = segStartAddress(segIndex);
+ segName = segmentName(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ segOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ segOffset += skip + sizeof(pint_t);
+ }
+ break;
+ case BIND_OPCODE_THREADED:
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ printf("rebase information (from compressed dyld info):\n");
+ printf("segment section address type value\n");
+ count = read_uleb128(p, end);
+ seenThreadedRebase = true;
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY: {
+ uint64_t delta = 0;
+ do {
+ const uint8_t* pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ uint64_t value = P::getP(*(uint64_t*)pointerLocation);
+#if SUPPORT_ARCH_arm64e
+ uint16_t diversity = (uint16_t)(value >> 32);
+ bool hasAddressDiversity = (value & (1ULL << 48)) != 0;
+ ptrauth_key key = (ptrauth_key)((value >> 49) & 0x3);
+ bool isAuthenticated = (value & (1ULL << 63)) != 0;
+#endif
+ bool isRebase = (value & (1ULL << 62)) == 0;
+ if (isRebase) {
+
+#if SUPPORT_ARCH_arm64e
+ static const char* keyNames[] = {
+ "IA", "IB", "DA", "DB"
+ };
+ if (isAuthenticated) {
+ uint64_t targetValue = value & 0xFFFFFFFFULL;
+ targetValue += fBaseAddress;
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX (JOP: diversity %d, address %s, %s)\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, targetValue, diversity, hasAddressDiversity ? "true" : "false", keyNames[key]);
+ } else
+#endif
+ {
+ // Regular pointer which needs to fit in 51-bits of value.
+ // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+ // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+ uint64_t top8Bits = value & 0x0007F80000000000ULL;
+ uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+ uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, targetValue);
+ }
+ }
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ segOffset += delta * sizeof(pint_t);
+ } while ( delta != 0);
+ break;
+ }
+ default:
+ throwf("unknown threaded bind subopcode %d", immediate);
+ }
+ break;
+ default:
+ throwf("bad bind opcode %d", *p);
+ }
+ }
+ }
+
+ if (!seenThreadedRebase)
+ printf("no compressed rebase info\n");
}
else {
printf("rebase information (from compressed dyld info):\n");
- printf("segment section address type\n");
+ printf("segment section address type value\n");
const uint8_t* p = (uint8_t*)fHeader + fInfo->rebase_off();
const uint8_t* end = &p[fInfo->rebase_size()];
pint_t segStartAddr = 0;
const char* segName = "??";
const char* typeName = "??";
+ const uint8_t* pointerLocation = nullptr;
+ uint64_t value = 0;
bool done = false;
while ( !done && (p < end) ) {
uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
break;
case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
for (int i=0; i < immediate; ++i) {
- printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ value = P::getP(*(uint64_t*)pointerLocation);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, value);
segOffset += sizeof(pint_t);
}
break;
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
count = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ value = P::getP(*(uint64_t*)pointerLocation);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, value);
segOffset += sizeof(pint_t);
}
break;
case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
- printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ value = P::getP(*(uint64_t*)pointerLocation);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, value);
segOffset += read_uleb128(p, end) + sizeof(pint_t);
break;
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ value = P::getP(*(uint64_t*)pointerLocation);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, value);
segOffset += skip + sizeof(pint_t);
}
break;
default:
throwf("bad rebase opcode %d", *p);
}
- }
+ }
}
-
}
}
-
-
-
-
-
template <typename A>
void DyldInfoPrinter<A>::printBindingInfoOpcodes(bool weakbinding)
{
skip = read_uleb128(p, end);
printf("0x%04X BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%d, 0x%08X)\n", opcodeOffset, count, skip);
break;
+ case BIND_OPCODE_THREADED:
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ count = read_uleb128(p, end);
+ printf("0x%04X BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB(%d)\n", opcodeOffset, count);
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY:
+ printf("0x%04X BIND_SUBOPCODE_THREADED_APPLY\n", opcodeOffset);
+ break;
+ default:
+ throwf("unknown threaded bind subopcode %d", immediate);
+ }
+ break;
default:
throwf("unknown bind opcode %d", *p);
}
}
+struct ThreadedBindData {
+ ThreadedBindData(const char* symbolName, int64_t addend, int libraryOrdinal, uint8_t flags, uint8_t type)
+ : symbolName(symbolName), addend(addend), libraryOrdinal(libraryOrdinal), flags(flags), type(type) { }
+ std::tuple<const char*, int64_t, int, uint8_t, uint8_t> pack() const {
+ return std::make_tuple(symbolName, addend, libraryOrdinal, flags, type);
+ }
+
+ const char* symbolName = nullptr;
+ int64_t addend = 0;
+ int libraryOrdinal = 0;
+ uint8_t flags = 0;
+ uint8_t type = 0;
+};
template <typename A>
void DyldInfoPrinter<A>::printBindingInfo()
printf("segment section address type addend dylib symbol\n");
const uint8_t* p = (uint8_t*)fHeader + fInfo->bind_off();
const uint8_t* end = &p[fInfo->bind_size()];
-
+
uint8_t type = 0;
+ uint8_t flags = 0;
uint8_t segIndex = 0;
uint64_t segOffset = 0;
const char* symbolName = NULL;
const char* segName = "??";
const char* typeName = "??";
const char* weak_import = "";
+ std::vector<ThreadedBindData> ordinalTable;
+ bool useThreadedRebaseBind = false;
bool done = false;
while ( !done && (p < end) ) {
uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
while (*p != '\0')
++p;
++p;
- if ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 )
+ flags = immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT;
+ if ( flags != 0 )
weak_import = " (weak import)";
else
weak_import = "";
segOffset += read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import );
- segOffset += sizeof(pint_t);
+ if (!useThreadedRebaseBind) {
+ printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import );
+ segOffset += sizeof(pint_t);
+ } else {
+ ordinalTable.push_back(ThreadedBindData(symbolName, addend, libraryOrdinal, flags, type));
+ }
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import );
segOffset += skip + sizeof(pint_t);
}
break;
+ case BIND_OPCODE_THREADED:
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ count = read_uleb128(p, end);
+ ordinalTable.clear();
+ ordinalTable.reserve(count);
+ useThreadedRebaseBind = true;
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY: {
+ uint64_t delta = 0;
+ do {
+ const uint8_t* pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ uint64_t value = P::getP(*(uint64_t*)pointerLocation);
+#if SUPPORT_ARCH_arm64e
+ uint16_t diversity = (uint16_t)(value >> 32);
+ bool hasAddressDiversity = (value & (1ULL << 48)) != 0;
+ ptrauth_key key = (ptrauth_key)((value >> 49) & 0x3);
+ bool isAuthenticated = (value & (1ULL << 63)) != 0;
+#endif
+ bool isRebase = (value & (1ULL << 62)) == 0;
+
+ if (isRebase) {
+ //printf("(rebase): %-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ } else {
+ // the ordinal is bits [0..15]
+ uint16_t ordinal = value & 0xFFFF;
+ if (ordinal >= ordinalTable.size()) {
+ fprintf(stderr, "bind ordinal is out of range\n");
+ break;
+ }
+ std::tie(symbolName, addend, libraryOrdinal, flags, type) = ordinalTable[ordinal].pack();
+ if ( (flags & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 )
+ weak_import = " (weak import)";
+ else
+ weak_import = "";
+ fromDylib = ordinalName(libraryOrdinal);
+#if SUPPORT_ARCH_arm64e
+ if (isAuthenticated) {
+ static const char* keyNames[] = {
+ "IA", "IB", "DA", "DB"
+ };
+ printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s with value 0x%016llX (JOP: diversity %d, address %s, %s)\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import, value, diversity, hasAddressDiversity ? "true" : "false", keyNames[key]);
+ } else
+#endif
+ {
+ printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s with value 0x%016llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import, value );
+ }
+ }
+
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ segOffset += delta * sizeof(pint_t);
+ } while ( delta != 0);
+ break;
+ }
+ default:
+ throwf("unknown threaded bind subopcode %d", immediate);
+ }
+ break;
default:
throwf("bad bind opcode %d", *p);
}
return "br22";
case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32:
return "off32";
+ case DYLD_CACHE_ADJ_V2_THREADED_POINTER_64:
+ return "theaded-pointer64";
}
}
template <typename A>
void DyldInfoPrinter<A>::printRelocRebaseInfo()
{
+ // First check if we can find a magic section for threaded rebase
+ {
+ auto rebaseChain = [this](uintptr_t chainStartMappedAddress, uintptr_t chainStartVMAddress, uint64_t stepMultiplier, uintptr_t baseAddress) {
+ uint64_t delta = 0;
+ uintptr_t mappedAddress = chainStartMappedAddress;
+ uintptr_t vmAddress = chainStartVMAddress;
+ do {
+ uint64_t value = *(uint64_t*)mappedAddress;
+
+ uint8_t segIndex = segmentIndexForAddress(vmAddress);
+ const char* segName = segmentName(segIndex);
+ const char* sectName = sectionName(segIndex, vmAddress);
+
+#if SUPPORT_ARCH_arm64e
+ uint16_t diversity = (uint16_t)(value >> 32);
+ bool hasAddressDiversity = (value & (1ULL << 48)) != 0;
+ ptrauth_key key = (ptrauth_key)((value >> 49) & 0x3);
+ bool isAuthenticated = (value & (1ULL << 63)) != 0;
+#endif
+ bool isRebase = (value & (1ULL << 62)) == 0;
+ if (isRebase) {
+
+#if SUPPORT_ARCH_arm64e
+ if (isAuthenticated) {
+ uint64_t slide = 0;
+ static const char* keyNames[] = {
+ "IA", "IB", "DA", "DB"
+ };
+ // The new value for a rebase is the low 32-bits of the threaded value plus the slide.
+ uint64_t newValue = (value & 0xFFFFFFFF) + slide;
+ // Add in the offset from the mach_header
+ newValue += baseAddress;
+ // We have bits to merge in to the discriminator
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX (JOP: diversity %d, address %s, %s)\n",
+ segName, sectName, (uint64_t)vmAddress, "pointer", newValue,
+ diversity, hasAddressDiversity ? "true" : "false", keyNames[key]);
+ } else
+#endif
+ {
+ // Regular pointer which needs to fit in 51-bits of value.
+ // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+ // and the bottom 43-bits to be fit in to 51-bits.
+ uint64_t top8Bits = value & 0x0007F80000000000ULL;
+ uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+ uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectName, (uint64_t)vmAddress, "pointer", targetValue);
+ }
+ }
+
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ mappedAddress += delta * stepMultiplier;
+ vmAddress += delta * stepMultiplier;
+ } while ( delta != 0 );
+ };
+ for(const macho_segment_command<P>* segCmd : fSegments) {
+ if (strcmp(segCmd->segname(), "__TEXT") != 0)
+ continue;
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if (strcmp(sect->sectname(), "__thread_starts") != 0)
+ continue;
+ printf("rebase information (from __TEXT,__thread_starts):\n");
+ printf("segment section address type\n");
+ const uint8_t* sectionContent = (uint8_t*)fHeader + segCmd->fileoff() + sect->offset();
+ uint32_t *threadStarts = (uint32_t*)sectionContent;
+ uint32_t *threadEnds = (uint32_t*)(sectionContent + sect->size());
+ uint32_t threadStartsHeader = threadStarts[0];
+ uint64_t stepMultiplier = (threadStartsHeader & 1) == 1 ? 8 : 4;
+ for (uint32_t* threadOffset = threadStarts + 1; threadOffset != threadEnds; ++threadOffset) {
+ //printf("Thread start offset: 0x%016X\n", *threadOffset);
+ // If we get a 0xFFFFFFFF offset then ld64 overestimated the size required. So skip the remainder
+ // of the entries.
+ if (*threadOffset == 0xFFFFFFFF)
+ break;
+ uint64_t chainStartVMAddr = fBaseAddress + *threadOffset;
+ uintptr_t chainStartMappedAddress = (uintptr_t)mappedAddressForVMAddress(chainStartVMAddr);
+ rebaseChain(chainStartMappedAddress, chainStartVMAddr, stepMultiplier, fBaseAddress);
+ }
+ return;
+ }
+ }
+ }
+
if ( fDynamicSymbolTable == NULL ) {
printf("no classic dynamic symbol table");
}
sPreferredArch = CPU_TYPE_POWERPC64;
else if ( strcmp(arch, "ppc") == 0 )
sPreferredArch = CPU_TYPE_POWERPC;
- else if ( strcmp(arch, "i386") == 0 )
- sPreferredArch = CPU_TYPE_I386;
- else if ( strcmp(arch, "x86_64") == 0 )
- sPreferredArch = CPU_TYPE_X86_64;
-#if SUPPORT_ARCH_arm64
- else if ( strcmp(arch, "arm64") == 0 )
- sPreferredArch = CPU_TYPE_ARM64;
-#endif
else {
if ( arch == NULL )
throw "-arch missing architecture name";
uint8_t loadCommandSizeMask();
void checkSymbolTable();
void checkInitTerms();
+ void checkThreadedRebaseBind();
void checkIndirectSymbolTable();
void checkRelocations();
void checkExternalReloation(const macho_relocation_info<P>* reloc);
void verifyInstallName();
void verifyNoRpaths();
void verifyNoFlatLookups();
+ void verifyiOSMac();
pint_t relocBase();
bool addressInWritableSegment(pint_t address);
bool hasTextRelocInRange(pint_t start, pint_t end);
pint_t segStartAddress(uint8_t segIndex);
- bool addressIsRebaseSite(pint_t addr);
+ bool addressIsRebaseSite(pint_t addr, pint_t& pointeeAddr);
bool addressIsBindingSite(pint_t addr);
pint_t getInitialStackPointer(const macho_thread_command<P>*);
pint_t getEntryPoint(const macho_thread_command<P>*);
bool fWriteableSegmentWithAddrOver4G;
bool fSlidableImage;
bool fHasLC_RPATH;
+ bool fIsDebugVariant;
+ uint64_t fBaseAddress = 0;
const macho_segment_command<P>* fFirstSegment;
const macho_segment_command<P>* fFirstWritableSegment;
const macho_segment_command<P>* fTEXTSegment;
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), fHasLC_RPATH(false), fFirstSegment(NULL), fFirstWritableSegment(NULL),
+ fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fHasLC_RPATH(false), fIsDebugVariant(false), fFirstSegment(NULL), fFirstWritableSegment(NULL),
fTEXTSegment(NULL), fDyldInfo(NULL), fSectionCount(0)
{
// sanity check
checkInitTerms();
+ checkThreadedRebaseBind();
+
if ( verifierDstRoot != NULL )
verify();
}
if ( segCmd->vmaddr() > 0x100000000ULL )
fWriteableSegmentWithAddrOver4G = true;
}
+ if ( (segCmd->fileoff() == 0) && (segCmd->filesize() != 0) )
+ fBaseAddress = segCmd->vmaddr();
// check section ranges
const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
// more section tests here
}
+static bool endsWith(const char* str, const char* suffix)
+{
+ size_t suffixLen = strlen(suffix);
+ size_t strLen = strlen(str);
+ if ( strLen > suffixLen ) {
+ return (strcmp(&str[strLen-suffixLen], suffix) == 0);
+ }
+ return false;
+}
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();
+ if ( endsWith(fPath, "_asan.dylib") || endsWith(fPath, "_asan") || endsWith(fPath, "_debug.dylib") || endsWith(fPath, "_debug") )
+ fIsDebugVariant = true;
+
+ if ( (fDstRoot != NULL) && (strlen(fDstRoot) < strlen(fPath)) ) {
+ const char* installLocationInDstRoot = &fPath[strlen(fDstRoot)];
+ if ( installLocationInDstRoot[0] != '/' )
+ --installLocationInDstRoot;
+ if ( (strncmp(installLocationInDstRoot, "/usr/lib/", 9) == 0) || (strncmp(installLocationInDstRoot, "/System/Library/", 16) == 0) ) {
+ if ( !fIsDebugVariant && (strstr(fPath, ".app/") == NULL) ) {
+ verifyInstallName();
+ verifyNoRpaths();
+ }
}
}
verifyNoFlatLookups();
+ verifyiOSMac();
}
}
}
+template <typename A>
+void MachOChecker<A>::verifyiOSMac()
+{
+ const char* fileLocationWithinDstRoot = &fPath[strlen(fDstRoot)];
+ if ( strncmp(fileLocationWithinDstRoot, "/System/iOSSupport/", 19) == 0 ) {
+ // everything in /System/iOSSupport/ should be iOSMac only
+ bool bad = false;
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ const macho_build_version_command<P>* buildVersCmd;
+ switch ( cmd->cmd() ) {
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ case LC_VERSION_MIN_TVOS:
+ case LC_VERSION_MIN_WATCHOS:
+ bad = true;
+ break;
+ case LC_BUILD_VERSION:
+ buildVersCmd = (macho_build_version_command<P>*)cmd;
+ if ( buildVersCmd->platform() != PLATFORM_IOSMAC )
+ bad = true;
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ if ( bad )
+ printf("macos_in_ios_support\twarn\tnon-iOSMac in /System/iOSSupport/ in arch %s\n", archName());
+ }
+ else {
+ // maybe someday warn about iOSMac only stuff not in /System/iOSSupport/
+ }
+}
+
+
template <typename A>
void MachOChecker<A>::checkIndirectSymbolTable()
{
throwf("%s section size is not an even multiple of element size", sect->sectname());
if ( (sect->addr() % sizeof(pint_t)) != 0 )
throwf("%s section size is not pointer size aligned", sect->sectname());
- // check each pointer in array points within TEXT
arrayStart = (pint_t*)((char*)fHeader + sect->offset());
arrayEnd = (pint_t*)((char*)fHeader + sect->offset() + sect->size());
- for (pint_t* p=arrayStart; p < arrayEnd; ++p) {
- pint_t pointer = P::getP(*p);
- if ( (pointer < fTEXTSegment->vmaddr()) || (pointer >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) )
- throwf("%s 0x%08llX points outside __TEXT segment", kind, (long long)pointer);
- }
// check each pointer in array will be rebased and not bound
if ( fSlidableImage ) {
pint_t sectionBeginAddr = sect->addr();
pint_t sectionEndddr = sect->addr() + sect->size();
- for(pint_t addr = sectionBeginAddr; addr < sectionEndddr; addr += sizeof(pint_t)) {
+ for(pint_t addr = sectionBeginAddr, *p = arrayStart; addr < sectionEndddr; addr += sizeof(pint_t), ++p) {
if ( addressIsBindingSite(addr) )
throwf("%s at 0x%0llX has binding to external symbol", kind, (long long)addr);
- if ( ! addressIsRebaseSite(addr) )
+ pint_t pointer = P::getP(*p);
+ if ( ! addressIsRebaseSite(addr, pointer) )
throwf("%s at 0x%0llX is not rebased", kind, (long long)addr);
+ // check each pointer in array points within TEXT
+ if ( (pointer < fTEXTSegment->vmaddr()) || (pointer >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) )
+ throwf("%s 0x%08llX points outside __TEXT segment", kind, (long long)pointer);
+ }
+ } else {
+ // check each pointer in array points within TEXT
+ for (pint_t* p=arrayStart; p < arrayEnd; ++p) {
+ pint_t pointer = P::getP(*p);
+ if ( (pointer < fTEXTSegment->vmaddr()) || (pointer >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) )
+ throwf("%s 0x%08llX 3 points outside __TEXT segment", kind, (long long)pointer);
}
}
break;
}
+template <typename A>
+void MachOChecker<A>::checkThreadedRebaseBind()
+{
+ // look bind info
+ if ( fDyldInfo != NULL ) {
+ const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->bind_off();
+ const uint8_t* end = &p[fDyldInfo->bind_size()];
+
+ uint8_t type = 0;
+ uint64_t segOffset = 0;
+ uint32_t count;
+ uint32_t skip;
+ const char* symbolName = NULL;
+ int libraryOrdinal = 0;
+ int segIndex;
+ int64_t addend = 0;
+ pint_t segStartAddr = 0;
+ uint64_t ordinalTableSize = 0;
+ bool useThreadedRebaseBind = false;
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segStartAddr = segStartAddress(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ segOffset += sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ segOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ segOffset += skip + sizeof(pint_t);
+ }
+ break;
+ case BIND_OPCODE_THREADED:
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ ordinalTableSize = read_uleb128(p, end);
+ useThreadedRebaseBind = true;
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY: {
+ if ( !useThreadedRebaseBind ) {
+ throwf("BIND_SUBOPCODE_THREADED_APPLY without ordinal table");
+ }
+ uint64_t delta = 0;
+ do {
+ const uint8_t* pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ uint64_t value = P::getP(*(uint64_t*)pointerLocation);
+ bool isRebase = (value & (1ULL << 62)) == 0;
+
+ if (isRebase) {
+ //printf("(rebase): %-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ } else {
+ // the ordinal is bits [0..15]
+ uint16_t ordinal = value & 0xFFFF;
+ if (ordinal >= ordinalTableSize) {
+ throwf("bind ordinal is out of range");
+ }
+ }
+
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ segOffset += delta * sizeof(pint_t);
+ } while ( delta != 0);
+ break;
+ }
+ default:
+ throwf("unknown threaded bind subopcode %d", immediate);
+ }
+ break;
+ default:
+ throwf("bad bind opcode %d", *p);
+ }
+ }
+ }
+}
+
template <>
}
template <typename A>
-bool MachOChecker<A>::addressIsRebaseSite(pint_t targetAddr)
+bool MachOChecker<A>::addressIsRebaseSite(pint_t targetAddr, pint_t& pointeeAddr)
{
// look at local relocs
const macho_relocation_info<P>* const localRelocsEnd = &fLocalRelocations[fLocalRelocationsCount];
default:
throwf("bad rebase opcode %d", *p);
}
- }
+ }
+
+ // If we have no rebase opcodes, then we may be using the threaded rebase/bind combined
+ // format and need to parse the bind opcodes instead.
+ if ( (fDyldInfo->rebase_size() == 0) && (fDyldInfo->bind_size() != 0) ) {
+ const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->bind_off();
+ const uint8_t* end = &p[fDyldInfo->bind_size()];
+
+ uint8_t segIndex = 0;
+ uint64_t segOffset = 0;
+ uint32_t count;
+ uint32_t skip;
+ pint_t segStartAddr = 0;
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ while (*p != '\0')
+ ++p;
+ ++p;
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segStartAddr = segStartAddress(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ segOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ segOffset += skip + sizeof(pint_t);
+ }
+ break;
+ case BIND_OPCODE_THREADED:
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ count = read_uleb128(p, end);
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY: {
+ uint64_t delta = 0;
+ do {
+ uint8_t* pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ uint64_t value = P::getP(*(uint64_t*)pointerLocation);
+#if SUPPORT_ARCH_arm64e
+ bool isAuthenticated = (value & (1ULL << 63)) != 0;
+#endif
+ bool isRebase = (value & (1ULL << 62)) == 0;
+ if ( isRebase && ( (segStartAddr+segOffset) == targetAddr ) ) {
+
+#if SUPPORT_ARCH_arm64e
+ if (isAuthenticated) {
+ uint64_t targetValue = value & 0xFFFFFFFFULL;
+ targetValue += fBaseAddress;
+ pointeeAddr = (pint_t)targetValue;
+ } else
+#endif
+ {
+ // Regular pointer which needs to fit in 51-bits of value.
+ // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+ // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+ uint64_t top8Bits = value & 0x0007F80000000000ULL;
+ uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+ uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+ pointeeAddr = (pint_t)targetValue;
+ }
+ return true;
+ }
+
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ segOffset += delta * sizeof(pint_t);
+ } while ( delta != 0 );
+ break;
+ }
+ default:
+ throwf("unknown threaded bind subopcode %d", immediate);
+ }
+ break;
+ default:
+ throwf("bad bind opcode %d", *p);
+ }
+ }
+ }
}
return false;
}
-
template <typename A>
bool MachOChecker<A>::addressIsBindingSite(pint_t targetAddr)
{
segOffset += skip + sizeof(pint_t);
}
break;
+ case BIND_OPCODE_THREADED:
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ count = read_uleb128(p, end);
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY: {
+ uint64_t delta = 0;
+ do {
+ uint8_t* pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ uint64_t value = P::getP(*(uint64_t*)pointerLocation);
+ bool isRebase = (value & (1ULL << 62)) == 0;
+ if (!isRebase) {
+ if ( (segStartAddr+segOffset) == targetAddr )
+ return true;
+ }
+
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ segOffset += delta * sizeof(pint_t);
+ } while ( delta != 0 );
+ break;
+ }
+ default:
+ throwf("unknown threaded bind subopcode %d", immediate);
+ }
+ break;
default:
throwf("bad bind opcode %d", *p);
}
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_rpath\tOS dylibs should not contain LC_RPATH load commands (from -rpath linker option)(remove LD_RUNPATH_SEARCH_PATHS Xcode build setting)\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");
+ printf("os_dylib_malformed\tthe mach-o file is malformed\n");
+ printf("macos_in_ios_support\t/System/iOSSupport/ should only contain mach-o files that support iosmac\n");
return 0;
}
else {
MYDIR=$(shell cd ../../bin;pwd)
-LD = ld
+LD = ld
OBJECTDUMP = ObjectDump
-OBJCIMAGEINFO = objcimageinfo
+OBJCIMAGEINFO = objcimageinfo
MACHOCHECK = machocheck
OTOOL = xcrun otool
+MTOC = xcrun mtoc
REBASE = rebase
DYLDINFO = dyldinfo
OSX_SDK = /Developer/SDKs/MacOSX10.6.sdk
endif
-CC = $(shell xcrun -find clang) -arch ${ARCH} -mmacosx-version-min=10.8 -isysroot $(OSX_SDK)
+CC = $(shell xcrun -find clang) -arch ${ARCH} -mmacosx-version-min=10.8 -isysroot $(OSX_SDK)
+AS = $(shell xcrun -f as) -arch ${ARCH} -mmacosx-version-min=10.8
CCFLAGS = -Wall
LDFLAGS = -syslibroot $(OSX_SDK)
ASMFLAGS =
VERSION_OLD_LINKEDIT = -mmacosx-version-min=10.4
LD_NEW_LINKEDIT = -macosx_version_min 10.6
-CXX = $(shell xcrun -find clang++) -arch ${ARCH} -isysroot $(OSX_SDK)
+CXX = $(shell xcrun -find clang++) -arch ${ARCH} -mmacosx-version-min=10.9 -isysroot $(OSX_SDK)
CXXFLAGS = -Wall -stdlib=libc++
ifeq ($(ARCH),armv6)
LDFLAGS := -syslibroot $(IOS_SDK)
override FILEARCH = arm
+ AS = $(shell xcrun -f as) -arch ${ARCH} -miphoneos-version-min=5.0
CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
ifeq ($(ARCH),armv7)
LDFLAGS := -syslibroot $(IOS_SDK)
override FILEARCH = arm
+ AS = $(shell xcrun -f as) -arch ${ARCH} -miphoneos-version-min=5.0
CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
CXXFLAGS += -mthumb
override ARCH = armv6
override FILEARCH = arm
+ AS = $(shell xcrun -f as) -arch ${ARCH} -miphoneos-version-min=5.0
CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
CXXFLAGS += -mthumb
override ARCH = armv7
override FILEARCH = arm
+ AS = $(shell xcrun -f as) -arch ${ARCH} -miphoneos-version-min=5.0
CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
ifeq ($(ARCH),arm64)
LDFLAGS := -syslibroot $(IOS_SDK)
+ AS = $(shell xcrun -f as) -arch ${ARCH} -miphoneos-version-min=5.0
CC = $(shell xcrun --sdk iphoneos.internal -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=9.0 -isysroot $(IOS_SDK)
CXX = $(shell xcrun --sdk iphoneos.internal -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=9.0 -isysroot $(IOS_SDK)
VERSION_NEW_LINKEDIT = -miphoneos-version-min=7.0
all_archs="x86_64 i386"
valid_archs="x86_64 i386"
+ios_sdk_directory="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs"
# only test arm code if iOS platform tools installed
-if [ -d /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs ]
+min_available_ios_sdk_version()
+{
+ ls "${ios_sdk_directory}" |
+ grep --invert-match --ignore-case "internal" | # exclude internal SDKs
+ sort --version-sort | head -n 1 | # take SDK with min version
+ sed "s/^iPhoneOS\(.*\)\.sdk$/\1/" # return only the version string
+}
+version_ge()
+{
+ test "$(printf '%s\n' "$@" | sort --version-sort | head -n 1)" == "$2";
+}
+if [ -d $ios_sdk_directory ]
then
- all_archs="${all_archs} armv7"
- valid_archs="${valid_archs} armv7"
+ arm_arch="armv7"
+ # Newer SDKs don't support older architectures.
+ if version_ge "$(min_available_ios_sdk_version)" "11.0"
+ then
+ arm_arch="arm64"
+ fi
+ all_archs="${all_archs} ${arm_arch}"
+ valid_archs="${valid_archs} ${arm_arch}"
fi
ifeq ($(FILEARCH),arm)
LD_VERS = -ios_version_min 4.0 -syslibroot $(IOS_SDK)
+ CC_VERS = -miphoneos-version-min=4.0
+else
+ifeq ($(FILEARCH),arm64)
+ LD_VERS = -ios_version_min 4.0 -syslibroot $(IOS_SDK)
+ CC_VERS = -miphoneos-version-min=4.0
else
LD_VERS = -macosx_version_min 10.6
+ CC_VERS = -mmacosx-version-min=10.6
+endif
endif
run: all
all:
- ${CC} ${CCFLAGS} hello.c -c -o hello.o
+ ${CC} ${CC_VERS} ${CCFLAGS} hello.c -c -o hello.o
${FAIL_IF_BAD_OBJ} hello.o
${LD} ${LDFLAGS} hello.o -dylib -o hello.dylib -lSystem $(LD_VERS)
file hello.dylib | grep ${FILEARCH} | ${PASS_IFF_STDIN}
TESTROOT = ../..
include ${TESTROOT}/include/common.makefile
-ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(subst ppc,,$(VALID_ARCHS)) )
+#ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(subst ppc,,$(VALID_ARCHS)) )
+
+# build the list of all valid archs for this platform...
+ifeq ($(ARCH),i386)
+ ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(filter-out arm%,$(VALID_ARCHS)) )
+ SYSROOT = -isysroot $(OSX_SDK)
+endif
+ifeq ($(ARCH),x86_64)
+ ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(filter-out arm%,$(VALID_ARCHS)) )
+ SYSROOT = -isysroot $(OSX_SDK)
+endif
+ifeq ($(ARCH),armv7)
+ ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(filter arm%,$(VALID_ARCHS)) )
+ SYSROOT = -isysroot $(IOS_SDK)
+endif
+ifeq ($(ARCH),arm64)
+ ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(filter arm%,$(VALID_ARCHS)) )
+ SYSROOT = -isysroot $(IOS_SDK)
+endif
#
# Test that blank stubs are handled properly
all:
# build example fully fat dylib
-
- gcc `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib
+ gcc $(SYSROOT) ${CCFLAGS} `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib
${FAIL_IF_BAD_MACHO} libfoo.dylib
- # handle the case of a native ppc compile--this sets the subtype, which must be passed to lipo
+# handle the case of a native ppc compile--this sets the subtype, which must be passed to lipo
if [ x${ARCH} != xppc ]; \
then \
SUB_ARCH=${ARCH}; \
${CC} ${CCFLAGS} main.c libfoo.dylib -o main
${OTOOL} -L main | grep libfoo | ${FAIL_IF_STDIN}
${PASS_IFF_GOOD_MACHO} main
-
clean:
rm -rf libfoo.dylib main
STRING_LABEL_COUNT = 0
ifeq (${ARCH},x86_64)
- STRING_LABEL_COUNT = 3
+ STRING_LABEL_COUNT = 2
endif
-ifeq (${ARCH},i386)
- STRING_LABEL_COUNT = 1
+ifeq (${ARCH},arm64)
+ STRING_LABEL_COUNT = 2
endif
-
run: all
all:
${PASS_IFF} /usr/bin/true
clean:
- rm foo.o bar.o foobar.o libfoobar.dylib
+ rm -f foo.o bar.o foobar.o libfoobar.dylib
#define DTRACE_LAB(p, n) \
"__dtrace_probe$" DTRACE_TOSTRING(%=__LINE__) DTRACE_STRINGIFY(_##p##___##n)
+#if (defined __x86_64__ || defined __arm64__)
+#define DTRACE_LABEL(p, n) \
+ ".section __DATA, __data\n\t" \
+ ".globl " DTRACE_LAB(p, n) "\n\t" \
+ DTRACE_LAB(p, n) ":" ".quad 1f""\n\t" \
+ ".text" "\n\t" \
+ "1:"
+#else
#define DTRACE_LABEL(p, n) \
".section __DATA, __data\n\t" \
".globl " DTRACE_LAB(p, n) "\n\t" \
DTRACE_LAB(p, n) ":\n\t" ".long 1f""\n\t" \
".text" "\n\t" \
"1:"
+#endif
#define DTRACE_CALL(p,n) \
DTRACE_LABEL(p,n) \
libtool -static bar.o baz.o foo.o -o liball.a
${CC} ${CCFLAGS} liball.a -all_load -dynamiclib -o liball.dylib -nodefaultlibs -lSystem
${FAIL_IF_BAD_MACHO} liball.dylib
- nm -fap liball.dylib | ./stabs-filter.pl > liball.dylib.stabs
+ nm -ap liball.dylib | ./stabs-filter.pl > liball.dylib.stabs
${PASS_IFF} diff liball.dylib.stabs expected-stabs
clean:
${CXX} ${CCXXFLAGS} -flto=thin -gdwarf-2 hello.o other.o -o hello.thin -Wl,-object_path_lto,thinlto.o
${FAIL_IF_BAD_MACHO} hello.thin
# Check that we have a non zero timestamp in the debug note
- nm -ap hello.thin | grep '^[0-9a-z]*[1-9a-z][0-9a-z]* .*OSO.*thinlto.o/0.o$$' | ${FAIL_IF_EMPTY}
- nm -ap hello.thin | grep '^[0-9a-z]*[1-9a-z][0-9a-z]* .*OSO.*thinlto.o/1.o$$' | ${FAIL_IF_EMPTY}
+ nm -ap hello.thin | grep '^[0-9a-z]*[1-9a-z][0-9a-z]* .*OSO.*thinlto.o/0.thinlto.o$$' | ${FAIL_IF_EMPTY}
+ nm -ap hello.thin | grep '^[0-9a-z]*[1-9a-z][0-9a-z]* .*OSO.*thinlto.o/1.thinlto.o$$' | ${FAIL_IF_EMPTY}
+ ${PASS_IFF} true
clean:
rm -rf hello hello.thin hello.o other.o lto.o thinlto.o
0000 BNSYM
0000 FUN __Z3foov
0000 FUN
-0000 ENSYM
+0000 ENSYM
0000 SO
0000 SO CWD/
0000 SO bar.cxx
0000 BNSYM
0000 FUN __Z3barv
0000 FUN
-0000 ENSYM
+0000 ENSYM
0000 SO
${CC} -arch $(ARCH) -o MtocTest1.obj $(LOCAL_CC_FLAGS) -c MtocTest.c
libtool -static -o MtocTest1.lib MtocTest1.obj
${LD} -arch $(ARCH) -o MtocTest1.macho -u __ModuleEntryPoint -e __ModuleEntryPoint $(LD_ARG) -preload -segalign 0x20 -pie -seg1addr 0x240 -map MtocTest1.map LibTest1.lib MtocTest1.lib
- /usr/local/efi/bin/mtoc -subsystem application -align 0x20 -d MtocTest1.macho MtocTest1.macho MtocTest1.pecoff
- otool -rR MtocTest1.macho > otool-reloc.log
- otool -l MtocTest1.macho > otool-load.log
- /usr/local/efi/bin/objdump -b $(OBJ_DUMP_ARCH) -x MtocTest1.pecoff > objdump-raw.log
+ ${MTOC} -subsystem application -align 0x20 -d MtocTest1.macho MtocTest1.macho MtocTest1.pecoff
+ ${OTOOL} -r MtocTest1.macho > otool-reloc.log
+ ${OTOOL} -l MtocTest1.macho > otool-load.log
+# The following will fail as the binutils objdump is no longer available.
+# /usr/local/efi/bin/objdump -b $(OBJ_DUMP_ARCH) -x MtocTest1.pecoff > objdump-raw.log
+ ./efi-pecoff-util MtocTest1.pecoff > efi-pecoff-util-raw.log
${PASS_IFF_SUCCESS} ./mtoctest.py --arch $(ARCH)
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright (c) 2013 - 2016 Apple Inc. All rights reserved
+#
+# Dump out a PE/COFF, PE/COFF+, or TE file using the EfiPeCoff class
+#
+# Read from memory in lldb
+# T=pecoff.EfiPeCoff(lldb.target, 0x86c7d000)
+#
+# Read from a Python file object
+# T=pecoff.EfiPeCoff(file)
+#
+# Read from a Python string
+# T=pecoff.EfiPeCoff(file.read())
+#
+
+import sys
+import struct
+import collections
+import optparse
+import commands
+import os
+import platform
+
+#----------------------------------------------------------------------
+# Code that auto imports LLDB
+#----------------------------------------------------------------------
+try:
+ # Just try for LLDB in case PYTHONPATH is already correctly setup
+ import lldb
+except ImportError:
+ lldb_python_dirs = list()
+ # lldb is not in the PYTHONPATH, try some defaults for the current platform
+ platform_system = platform.system()
+ if platform_system == 'Darwin':
+ # On Darwin, try the currently selected Xcode directory
+ xcode_dir = commands.getoutput("xcode-select --print-path")
+ if xcode_dir:
+ lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python'))
+ lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
+ lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
+ success = False
+ for lldb_python_dir in lldb_python_dirs:
+ if os.path.exists(lldb_python_dir):
+ if not (sys.path.__contains__(lldb_python_dir)):
+ sys.path.append(lldb_python_dir)
+ try:
+ import lldb
+ except ImportError:
+ pass
+ else:
+ success = True
+ break
+ if not success:
+ print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
+ sys.exit(1)
+
+class ReadOnlyFile:
+ '''Abstract reading data from an object:
+ Duck type an lldb.SBTarget, string (output of file.read()), or Python File object.
+ '''
+ def __init__(self, readAbstraction, address = 0):
+ # Python file object
+ self.file = None
+ # offset for FAT binaries.
+ self.offset = None
+
+ # lldb SBTarget
+ self.address = None
+ self.startingAddress = None
+ self.SBTarget = None
+ self.SBError = None
+
+ # Python string (file.read())
+ self.data = None
+ self.dataIndex = 0
+
+ if isinstance(readAbstraction, lldb.SBTarget):
+ # duck type lldb memory reads
+ self.address = address
+ self.startingAddress = address
+ self.SBTarget = readAbstraction
+ self.SBError = lldb.SBError()
+ elif isinstance(readAbstraction, file):
+ # duck type to a Python file
+ self.file = readAbstraction
+ self.offset = address
+ elif isinstance(readAbstraction, str):
+ # string, like the result of reading the file in via Python
+ self.data = readAbstraction
+ self.dataIndex = 0
+ else:
+ raise SyntaxError('Unsupported type for readAbstraction')
+
+ def Read (self, size, offset=None):
+ if offset is not None:
+ self.Seek (offset)
+
+ if self.file:
+ return self.file.read(size)
+
+ if self.address:
+ data = self.SBTarget.process.ReadMemory (self.address, size, self.SBError)
+ self.address += size
+ return bytearray(data)
+
+ if self.data:
+ data = self.data[self.dataIndex:self.dataIndex+size]
+ self.dataIndex += size
+ return data
+
+ def ReadCString (self, offset=None, maxSize=512):
+ if offset:
+ self.Seek (offset)
+
+ if self.file:
+ data = self.file.read(maxSize)
+ str = data.split('\x00')[0]
+ # seek to end of string
+ self.file.seek (-(maxSize - len(str)), os.SEEK_CUR)
+ return data
+
+ if self.address:
+ data = self.SBTarget.process.ReadCStringFromMemory (self.address, maxSize, self.SBError)
+ self.address += len(data)
+ return data
+
+ if self.data:
+ data = self.data[self.dataIndex:self.dataIndex+maxSize]
+ str = data.split('\x00')[0]
+ self.dataIndex += len(str)
+ return str
+
+
+ def Seek (self, offset, whence = os.SEEK_SET):
+ if self.file:
+ return self.file.seek(offset, whence)
+
+ if self.address:
+ if whence == os.SEEK_SET:
+ self.address = self.startingAddress + offset
+ elif whence == os.SEEK_CUR:
+ self.address = self.address + offset
+ elif whence == os.SEEK_END:
+ raise SyntaxError('whence does not support SEEK_END due to memory not having an end')
+ else:
+ raise SyntaxError('illegal whence value')
+
+ if self.data:
+ if whence == os.SEEK_SET:
+ self.dataIndex = offset
+ elif whence == os.SEEK_CUR:
+ self.dataIndex = self.dataIndex + offset
+ elif whence == os.SEEK_END:
+ raise SyntaxError('whence does not support SEEK_END due to memory not having an end')
+ else:
+ raise SyntaxError('illegal whence value')
+
+ def Tell (self):
+ if self.file:
+ return self.file.tell()
+
+ if self.address:
+ return self.address
+
+ if self.data:
+ return self.dataIndex
+
+
+ def __del__(self):
+ if self.file:
+ self.file.close()
+
+class EfiPeCoff:
+ ''' class to abstract PE/COFF walking'''
+
+ # PE/COFF class definitions
+
+ #
+ # typedef struct {
+ # UINT32 VirtualAddress;
+ # UINT32 Size;
+ # } EFI_IMAGE_DATA_DIRECTORY;
+ #
+ # typedef struct {
+ # UINT16 Signature; ///< The signature for TE format = "VZ".
+ # UINT16 Machine; ///< From the original file header.
+ # UINT8 NumberOfSections; ///< From the original file header.
+ # UINT8 Subsystem; ///< From original optional header.
+ # UINT16 StrippedSize; ///< Number of bytes we removed from the header.
+ # UINT32 AddressOfEntryPoint; ///< Offset to entry point -- from original optional header.
+ # UINT32 BaseOfCode; ///< From original image -- required for ITP debug.
+ # UINT64 ImageBase; ///< From original file header.
+ # EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]; ///< Only base relocation and debug directory.
+ # } EFI_TE_IMAGE_HEADER;
+ #
+
+ EFI_TE_IMAGE_HEADER_fmt = '<HHBBHLLQLLLL'
+ TeHdrLength = struct.calcsize(EFI_TE_IMAGE_HEADER_fmt)
+ EFI_TE_IMAGE_HEADER_tuple = 'Signature Machine NumberOfSections Subsystem StrippedSize AddressOfEntryPoint BaseOfCode ImageBase DataDirVirt_Reloc DataDirSize_Reloc DataDirVirt_Debug DataDirSize_Debug'
+ EFI_TE_IMAGE_HEADER = collections.namedtuple ('EFI_TE_IMAGE_HEADER', EFI_TE_IMAGE_HEADER_tuple)
+
+ EFI_IMAGE_NT_SIGNATURE = 0x00004550
+ #
+ # ///
+ # /// PE images can start with an optional DOS header, so if an image is run
+ # /// under DOS it can print an error message.
+ # ///
+ # typedef struct {
+ # UINT16 e_magic; ///< Magic number.
+ # UINT16 e_cblp; ///< Bytes on last page of file.
+ # UINT16 e_cp; ///< Pages in file.
+ # UINT16 e_crlc; ///< Relocations.
+ # UINT16 e_cparhdr; ///< Size of header in paragraphs.
+ # UINT16 e_minalloc; ///< Minimum extra paragraphs needed.
+ # UINT16 e_maxalloc; ///< Maximum extra paragraphs needed.
+ # UINT16 e_ss; ///< Initial (relative) SS value.
+ # UINT16 e_sp; ///< Initial SP value.
+ # UINT16 e_csum; ///< Checksum.
+ # UINT16 e_ip; ///< Initial IP value.
+ # UINT16 e_cs; ///< Initial (relative) CS value.
+ # UINT16 e_lfarlc; ///< File address of relocation table.
+ # UINT16 e_ovno; ///< Overlay number.
+ # UINT16 e_res[4]; ///< Reserved words.
+ # UINT16 e_oemid; ///< OEM identifier (for e_oeminfo).
+ # UINT16 e_oeminfo; ///< OEM information; e_oemid specific.
+ # UINT16 e_res2[10]; ///< Reserved words.
+ # UINT32 e_lfanew; ///< File address of new exe header.
+ # } EFI_IMAGE_DOS_HEADER;
+ #
+
+ # cheat as 58s is really e_cblp -> e_res2[10]
+ EFI_IMAGE_DOS_HEADER_fmt = '<H58sI'
+ DosHdrLength = struct.calcsize(EFI_IMAGE_DOS_HEADER_fmt)
+ EFI_IMAGE_DOS_HEADER_tuple = 'e_magic e_ignore e_lfanew'
+ EFI_IMAGE_DOS_HEADER = collections.namedtuple ('EFI_IMAGE_DOS_HEADER', EFI_IMAGE_DOS_HEADER_tuple)
+
+ #define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16
+ #
+ # ///
+ # /// @attention
+ # /// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and
+ # /// EFI_IMAGE_OPTIONAL_HEADER32 must be used. The data structures only vary
+ # /// after NT additional fields.
+ # ///
+ EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
+ #
+ # UINT32 Signature;
+ # typedef struct {
+ # UINT16 Machine;
+ # UINT16 NumberOfSections;
+ # UINT32 TimeDateStamp;
+ # UINT32 PointerToSymbolTable;
+ # UINT32 NumberOfSymbols;
+ # UINT16 SizeOfOptionalHeader;
+ # UINT16 Characteristics;
+ # } EFI_IMAGE_FILE_HEADER;
+ # ///
+ # /// Optional Header Standard Fields for PE32.
+ # ///
+ # typedef struct {
+ # ///
+ # /// Standard fields.
+ # ///
+ # UINT16 Magic;
+ # UINT8 MajorLinkerVersion;
+ # UINT8 MinorLinkerVersion;
+ # UINT32 SizeOfCode;
+ # UINT32 SizeOfInitializedData;
+ # UINT32 SizeOfUninitializedData;
+ # UINT32 AddressOfEntryPoint;
+ # UINT32 BaseOfCode;
+ # UINT32 BaseOfData; ///< PE32 contains this additional field, which is absent in PE32+.
+ # ///
+ # /// Optional Header Windows-Specific Fields.
+ # ///
+ # UINT32 ImageBase;
+ # UINT32 SectionAlignment;
+ # UINT32 FileAlignment;
+ # UINT16 MajorOperatingSystemVersion;
+ # UINT16 MinorOperatingSystemVersion;
+ # UINT16 MajorImageVersion;
+ # UINT16 MinorImageVersion;
+ # UINT16 MajorSubsystemVersion;
+ # UINT16 MinorSubsystemVersion;
+ # UINT32 Win32VersionValue;
+ # UINT32 SizeOfImage;
+ # UINT32 SizeOfHeaders;
+ # UINT32 Checksum;
+ # UINT16 Subsystem;
+ # UINT16 DllCharacteristics;
+ # UINT32 SizeOfStackReserve;
+ # UINT32 SizeOfStackCommit;
+ # UINT32 SizeOfHeapReserve;
+ # UINT32 SizeOfHeapCommit;
+ # UINT32 LoaderFlags;
+ # UINT32 NumberOfRvaAndSizes;
+ # EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES];
+ # } EFI_IMAGE_OPTIONAL_HEADER32;
+ #
+
+ EFI_IMAGE_DATA_DIRECTORY_tuple = '''
+ DataDirVirt_Export DataDirSize_Export
+ DataDirVirt_Import DataDirSize_Import
+ DataDirVirt_Resource DataDirSize_Resource
+ DataDirVirt_Exception DataDirSize_Exception
+ DataDirVirt_Security DataDirSize_Security
+ DataDirVirt_Reloc DataDirSize_Reloc
+ DataDirVirt_Debug DataDirSize_Debug
+ DataDir7Virt DataDir7Size
+ DataDir8Virt DataDir8Size
+ DataDir9Virt DataDir9Size
+ DataDir10Virt DataDir10Size
+ DataDir11Virt DataDir11Size
+ DataDir12Virt DataDir12Size
+ DataDir13Virt DataDir13Size
+ DataDir14Virt DataDir14Size
+ DataDir15Virt DataDir15Size
+ '''
+
+
+ EFI_IMAGE_OPTIONAL_HEADER32_fmt = '<IHHIIIHHHBBIIIIIIIIIHHHHHHIIIIHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'
+ OptionalHeader32Length = struct.calcsize(EFI_IMAGE_OPTIONAL_HEADER32_fmt)
+ EFI_IMAGE_OPTIONAL_HEADER32_tuple = '''
+ Signature
+ Machine
+ NumberOfSections
+ TimeDateStamp
+ PointerToSymbolTable
+ NumberOfSymbols
+ SizeOfOptionalHeader
+ Characteristics
+ Magic
+ MajorLinkerVersion
+ MinorLinkerVersion
+ SizeOfCode
+ SizeOfInitializedData
+ SizeOfUninitializedData
+ AddressOfEntryPoint
+ BaseOfCode
+ BaseOfData
+ ImageBase
+ SectionAlignment
+ FileAlignment
+ MajorOperatingSystemVersion
+ MinorOperatingSystemVersion
+ MajorImageVersion
+ MinorImageVersion
+ MajorSubsystemVersion
+ MinorSubsystemVersion
+ Win32VersionValue
+ SizeOfImage
+ SizeOfHeaders
+ Checksum
+ Subsystem
+ DllCharacteristics
+ SizeOfStackReserve
+ SizeOfStackCommit
+ SizeOfHeapReserve
+ SizeOfHeapCommit
+ LoaderFlags
+ NumberOfRvaAndSizes
+ ''' + EFI_IMAGE_DATA_DIRECTORY_tuple
+ EFI_IMAGE_OPTIONAL_HEADER32 = collections.namedtuple ('EFI_IMAGE_OPTIONAL_HEADER32', EFI_IMAGE_OPTIONAL_HEADER32_tuple)
+
+
+ #
+ # ///
+ # /// @attention
+ # /// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and
+ # /// EFI_IMAGE_OPTIONAL_HEADER64 must be used. The data structures only vary
+ # /// after NT additional fields.
+ # ///
+ EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
+ #
+ # ///
+ # /// Optional Header Standard Fields for PE32+.
+ # ///
+
+
+ # UINT32 Signature;
+ # typedef struct {
+ # UINT16 Machine;
+ # UINT16 NumberOfSections;
+ # UINT32 TimeDateStamp;
+ # UINT32 PointerToSymbolTable;
+ # UINT32 NumberOfSymbols;
+ # UINT16 SizeOfOptionalHeader;
+ # UINT16 Characteristics;
+ # } EFI_IMAGE_FILE_HEADER;
+ # ///
+ # /// COFF File Header (Object and Image).
+ # ///
+ # typedef struct {
+ # ///
+ # /// Standard fields.
+ # ///
+ # UINT16 Magic;
+ # UINT8 MajorLinkerVersion;
+ # UINT8 MinorLinkerVersion;
+ # UINT32 SizeOfCode;
+ # UINT32 SizeOfInitializedData;
+ # UINT32 SizeOfUninitializedData;
+ # UINT32 AddressOfEntryPoint;
+ # UINT32 BaseOfCode;
+ # ///
+ # /// Optional Header Windows-Specific Fields.
+ # ///
+ # UINT64 ImageBase;
+ # UINT32 SectionAlignment;
+ # UINT32 FileAlignment;
+ # UINT16 MajorOperatingSystemVersion;
+ # UINT16 MinorOperatingSystemVersion;
+ # UINT16 MajorImageVersion;
+ # UINT16 MinorImageVersion;
+ # UINT16 MajorSubsystemVersion;
+ # UINT16 MinorSubsystemVersion;
+ # UINT32 Win32VersionValue;
+ # UINT32 SizeOfImage;
+ # UINT32 SizeOfHeaders;
+ # UINT32 Checksum;
+ # UINT16 Subsystem;
+ # UINT16 DllCharacteristics;
+ # UINT64 SizeOfStackReserve;
+ # UINT64 SizeOfStackCommit;
+ # UINT64 SizeOfHeapReserve;
+ # UINT64 SizeOfHeapCommit;
+ # UINT32 LoaderFlags;
+ # UINT32 NumberOfRvaAndSizes;
+ # EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES];
+ # } EFI_IMAGE_OPTIONAL_HEADER64;
+ #
+
+ EFI_IMAGE_OPTIONAL_HEADER64_fmt = '<IHHIIIHHHBBIIIIIQIIHHHHHHIIIIHHQQQQIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'
+ OptionalHeader64Length = struct.calcsize(EFI_IMAGE_OPTIONAL_HEADER64_fmt)
+ EFI_IMAGE_OPTIONAL_HEADER64_tuple = '''
+ Signature
+ Machine
+ NumberOfSections
+ TimeDateStamp
+ PointerToSymbolTable
+ NumberOfSymbols
+ SizeOfOptionalHeader
+ Characteristics
+ Magic
+ MajorLinkerVersion
+ MinorLinkerVersion
+ SizeOfCode
+ SizeOfInitializedData
+ SizeOfUninitializedData
+ AddressOfEntryPoint
+ BaseOfCode
+ ImageBase
+ SectionAlignment
+ FileAlignment
+ MajorOperatingSystemVersion
+ MinorOperatingSystemVersion
+ MajorImageVersion
+ MinorImageVersion
+ MajorSubsystemVersion
+ MinorSubsystemVersion
+ Win32VersionValue
+ SizeOfImage
+ SizeOfHeaders
+ Checksum
+ Subsystem
+ DllCharacteristics
+ SizeOfStackReserve
+ SizeOfStackCommit
+ SizeOfHeapReserve
+ SizeOfHeapCommit
+ LoaderFlags
+ NumberOfRvaAndSizes
+ ''' + EFI_IMAGE_DATA_DIRECTORY_tuple
+ EFI_IMAGE_OPTIONAL_HEADER64 = collections.namedtuple ('EFI_IMAGE_OPTIONAL_HEADER64', EFI_IMAGE_OPTIONAL_HEADER64_tuple)
+
+ #
+ # #define EFI_IMAGE_SIZEOF_SHORT_NAME 8
+ #
+ # ///
+ # /// Section Table. This table immediately follows the optional header.
+ # ///
+ # typedef struct {
+ # UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME];
+ # union {
+ # UINT32 PhysicalAddress;
+ # UINT32 VirtualSize;
+ # } Misc;
+ # UINT32 VirtualAddress;
+ # UINT32 SizeOfRawData;
+ # UINT32 PointerToRawData;
+ # UINT32 PointerToRelocations;
+ # UINT32 PointerToLinenumbers;
+ # UINT16 NumberOfRelocations;
+ # UINT16 NumberOfLinenumbers;
+ # UINT32 Characteristics;
+ # } EFI_IMAGE_SECTION_HEADER;
+ #
+
+ EFI_IMAGE_SECTION_HEADER_fmt = '<QIIIIIIHHI'
+ PeCoffSectionLength = struct.calcsize(EFI_IMAGE_SECTION_HEADER_fmt)
+ EFI_IMAGE_SECTION_HEADER_tuple = 'Name VirtualSize VirtualAddress SizeOfRawData PointerToRawData PointerToRelocations PointerToLinenumbers NumberOfRelocations NumberOfLinenumbers Characteristics'
+ EFI_IMAGE_SECTION_HEADER = collections.namedtuple ('EFI_IMAGE_SECTION_HEADER', EFI_IMAGE_SECTION_HEADER_tuple)
+
+ #
+ # ///
+ # /// Debug Directory Format.
+ # ///
+ # typedef struct {
+ # UINT32 Characteristics;
+ # UINT32 TimeDateStamp;
+ # UINT16 MajorVersion;
+ # UINT16 MinorVersion;
+ # UINT32 Type;
+ # UINT32 SizeOfData;
+ # UINT32 RVA; ///< The address of the debug data when loaded, relative to the image base.
+ # UINT32 FileOffset; ///< The file pointer to the debug data.
+ # } EFI_IMAGE_DEBUG_DIRECTORY_ENTRY;
+ #
+
+ EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_fmt = '<IIHHIIII'
+ EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_tuple = 'Characteristics TimeDateStamp MajorVersion MinorVersion Type SizeOfData RVA FileOffset'
+ EFI_IMAGE_DEBUG_DIRECTORY_ENTRY = collections.namedtuple ('EFI_IMAGE_DEBUG_DIRECTORY_ENTRY', EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_tuple)
+
+ ##define CODEVIEW_SIGNATURE_MTOC SIGNATURE_32('M', 'T', 'O', 'C')
+ #typedef struct {
+ # UINT32 Signature; ///< "MTOC".
+ # GUID MachOUuid;
+ # //
+ # // Filename of .DLL (Mach-O with debug info) goes here
+ # //
+ #} EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY;
+
+ #typedef struct {
+ # UINT32 Data1;
+ # UINT16 Data2;
+ # UINT16 Data3;
+ # UINT8 Data4[8];
+ #} GUID;
+
+ EFI_GUID_fmt = '<IHHBBBBBBBB'
+
+ # typedef struct {
+ # UINT32 VirtualAddress;
+ # UINT32 SizeOfBlock;
+ # } EFI_IMAGE_BASE_RELOCATION;
+
+ EFI_IMAGE_BASE_RELOCATION_fmt = '<II'
+ BaseRelocationLength = struct.calcsize(EFI_IMAGE_BASE_RELOCATION_fmt)
+
+ # ///
+ # /// The WIN_CERTIFICATE structure is part of the PE/COFF specification.
+ # ///
+ # typedef struct {
+ # ///
+ # /// The length of the entire certificate,
+ # /// including the length of the header, in bytes.
+ # ///
+ # UINT32 dwLength;
+ # ///
+ # /// The revision level of the WIN_CERTIFICATE
+ # /// structure. The current revision level is 0x0200.
+ # ///
+ # UINT16 wRevision;
+ # ///
+ # /// The certificate type. See WIN_CERT_TYPE_xxx for the UEFI
+ # /// certificate types. The UEFI specification reserves the range of
+ # /// certificate type values from 0x0EF0 to 0x0EFF.
+ # ///
+ # UINT16 wCertificateType;
+ # ///
+ # /// The following is the actual certificate. The format of
+ # /// the certificate depends on wCertificateType.
+ # ///
+ # /// UINT8 bCertificate[ANYSIZE_ARRAY];
+ # ///
+ # } WIN_CERTIFICATE;
+
+ WIN_CERTIFICATE_fmt = "<IHH"
+ WinCertLength = struct.calcsize(WIN_CERTIFICATE_fmt)
+
+ def __init__(self, readAbstraction, address = 0):
+ self.f = ReadOnlyFile(readAbstraction, address)
+ # ( ImageType: 'TE'/'PE32'/'PE32+'
+ # OptionalHeaderCollection: Optional Header and Data Directory
+ # HeaderFormat: in struct.Struct() form
+ # TeOffset
+ # HeaderSize)
+ self.PeHdr = None
+ self.TeHdr = None
+ self.TeAdjust = 0
+
+ self.PeCoffType = ''
+ self.MachineType = ''
+ self.PeHdrFmt = None
+ self.PeSections = 0
+ self.FvSection = False
+ self.ZeroList = []
+ self.PeCoffHdrRead ()
+
+ def TeHdrTuple (self, offset=0):
+ data = self.f.Read (EfiPeCoff.TeHdrLength,offset)
+ TeHdr = EfiPeCoff.EFI_TE_IMAGE_HEADER._make (struct.Struct(EfiPeCoff.EFI_TE_IMAGE_HEADER_fmt).unpack_from (data))
+ return (TeHdr, EfiPeCoff.TeHdrLength - TeHdr.StrippedSize)
+
+ def DosHdrTuple (self, offset=0):
+ data = self.f.Read(EfiPeCoff.DosHdrLength,offset)
+ return EfiPeCoff.EFI_IMAGE_DOS_HEADER._make (struct.unpack_from(EfiPeCoff.EFI_IMAGE_DOS_HEADER_fmt, data))
+
+ ImageFileMachine = {
+ 0x014c : "IA32",
+ 0x0200 : "IPF",
+ 0x0EBC : "EBC",
+ 0x8664 : "X64",
+ 0x01c2 : "ARM",
+ 0xAA64 : "AArch64",
+ }
+
+ def PeCoffHdrRead (self):
+ # Test for FV Section (*.te build output)
+ image = self.f.Read(4, 0)
+ if image[0:2] == 'MZ' or image[0:2] == 'VZ' or image[0:4] == 'PE\0\0':
+ offset = 0
+ else:
+ offset = 4
+ self.FvSection = True
+
+ image = self.f.Read(2, offset)
+ if image[0:2] == 'MZ':
+ # PE/COFF starts with DOS Header
+ DosHdr = self.DosHdrTuple (offset)
+ offset += DosHdr.e_lfanew
+ elif image[0:2] == 'VZ':
+ # PE/COFF starts with TE Header
+ self.TeHdr, self.TeAdjust = self.TeHdrTuple (offset)
+ self.PeCoffType = "TE"
+ self.MachineType = self.ImageFileMachine.get (self.TeHdr.Machine, 'Unknown')
+ self.PeSections = EfiPeCoff.TeHdrLength
+
+ data = self.f.Read(EfiPeCoff.TeHdrLength, offset)
+ self.PeHdrFmt = EfiPeCoff.EFI_TE_IMAGE_HEADER_fmt
+ return
+ else:
+ # PE/COFF starts with PE/COFF header
+ offset = 0
+
+ image = self.f.Read(4, offset)
+ if image[0:4] != 'PE\0\0':
+ print "Bad PE/COFF Signature = %4s" % image
+ return ("Unknown", None, "", 0, 0)
+
+ # Check the magic to figure out if 32 or 64 bit PE/COFF
+ (Magic,) = struct.unpack_from ('<H', self.f.Read(2, offset+24))
+ if Magic == EfiPeCoff.EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
+ data = self.f.Read(EfiPeCoff.OptionalHeader32Length, offset)
+ self.PeHdr = EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER32._make (struct.Struct(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER32_fmt).unpack_from (data))
+ self.PeCoffType = "PE32"
+ self.PeHdrFmt = EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER32_fmt
+
+ # TimeDateStamp: Offset, size
+ self.ZeroList.append([offset + struct.calcsize(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER32_fmt[0:4]), 4])
+ # Checksum: Offset, size
+ self.ZeroList.append([offset + struct.calcsize(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER32_fmt[0:29]), 4])
+ elif Magic == EfiPeCoff.EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC:
+ data = self.f.Read(EfiPeCoff.OptionalHeader64Length, offset)
+ self.PeHdr = EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER64._make (struct.Struct(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER64_fmt).unpack_from (data))
+ self.PeCoffType = "PE32+"
+ self.PeHdrFmt = EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER64_fmt
+
+ # TimeDateStamp: Offset, size
+ self.ZeroList.append([offset + struct.calcsize(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER64_fmt[0:4]), 4])
+ # Checksum: Offset, size
+ self.ZeroList.append([offset + struct.calcsize(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER64_fmt[0:29]), 4])
+ else:
+ print "Unknown Magic 0x%02x" % Magic
+ return ("Unknown", None, "", 0, 0)
+
+ self.MachineType = self.ImageFileMachine.get (self.PeHdr.Machine, 'Unknown')
+
+ # ImageContext->PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader;
+ self.PeSections = offset + 4 + 20 + self.PeHdr.SizeOfOptionalHeader
+
+ def PeCoffDumpHdr (self):
+ PeHdr = self.PeHdr if self.TeHdr is None else self.TeHdr
+ Width = max (len (s) for s in PeHdr._fields)
+ return "\n".join('{0} = {1:#0{2}x}'.format(s.ljust(Width), getattr(PeHdr, s), FmtStrToWidth(self.PeHdrFmt[i+1])+2) for i, s in enumerate (PeHdr._fields))
+
+ def PeCoffZeroInfo (self):
+ # Return the files offsets and number of bytes that need to get zero'ed
+ return self.ZeroList
+
+ def NumberOfSections (self):
+ if self.PeHdr is not None:
+ return self.PeHdr.NumberOfSections
+ elif self.TeHdr is not None:
+ return self.TeHdr.NumberOfSections
+ else:
+ return 0
+
+ def PeCoffGetSection (self, index):
+ offset = self.PeSections + (index * EfiPeCoff.PeCoffSectionLength)
+ data = self.f.Read(EfiPeCoff.PeCoffSectionLength, offset)
+ return (data[0:8].split('\x00')[0], EfiPeCoff.EFI_IMAGE_SECTION_HEADER._make (struct.Struct(EfiPeCoff.EFI_IMAGE_SECTION_HEADER_fmt).unpack_from (data)))
+
+ def PeCoffDumpSectionHdr (self, Name, Section):
+ Width = max (len (s) for s in Section._fields)
+ result = ''
+ for i, s in enumerate (Section._fields):
+ result += '{0} = '.format(s.ljust(Width))
+ if i == 0 and Name != '':
+ # print name as a string, not a hex value
+ result += Name + '\n'
+ else:
+ result += '{0:#0{1}x}\n'.format(getattr(Section, s), FmtStrToWidth(EfiPeCoff.EFI_IMAGE_SECTION_HEADER_fmt[i+1])+2)
+
+ return result
+
+ def PeCoffDumpSection (self, Name, Section):
+ data = self.f.Read (Section.SizeOfRawData, Section.VirtualAddress)
+ result = []
+ Address = Section.VirtualAddress
+ for i in xrange (0, Section.SizeOfRawData, 16):
+ HexStr = ' '.join(["%02X"%ord(x) for x in data[i:i+16]])
+ TextStr = ''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in data[i:i+16]])
+ result.append("%08X %-*s |%s|\n" % (Address + i, 16*3, HexStr, TextStr))
+
+ return ''.join(result)
+
+
+ def PeCoffGetPdePointer (self, DebugEntry = 0, DebugEntrySize = 0, adjust = 0):
+ #
+ if DebugEntrySize == 0:
+ if self.PeHdr is not None:
+ DebugEntry = self.PeHdr.DataDirVirt_Debug
+ DebugEntrySize = self.PeHdr.DataDirSize_Debug
+ elif self.TeHdr is not None:
+ DebugEntry = self.TeHdr.DataDirVirt_Debug
+ DebugEntrySize = self.TeHdr.DataDirSize_Debug
+ adjust = self.TeAdjust
+ else:
+ return ('','')
+
+ offset = DebugEntry + adjust
+ data = self.f.Read(DebugEntrySize, offset)
+ DirectoryEntry = EfiPeCoff.EFI_IMAGE_DEBUG_DIRECTORY_ENTRY._make (struct.Struct(EfiPeCoff.EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_fmt).unpack_from (data))
+ offset = DirectoryEntry.FileOffset + adjust
+
+ data = self.f.Read(4, offset)
+ guid = ''
+ if data == 'MTOC':
+ data = self.f.Read(16)
+ tup = struct.unpack (EfiPeCoff.EFI_GUID_fmt, data)
+ guid = '{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}'.format(*tup)
+ Str = self.f.ReadCString ()
+ elif data == 'NB10':
+ Str = self.f.ReadCString (offset + 16)
+ elif data == 'RSDS':
+ Str = self.f.ReadCString (offset + 24)
+ else:
+ Str = "\x00"
+
+
+ # Python is more that happy to print out a NULL
+ return (Str.split('\x00')[0], guid)
+
+ def PeCoffDumpRelocations (self, offset, size):
+ data = self.f.Read(size, offset)
+ base = 0
+ baseEnd = size - EfiPeCoff.BaseRelocationLength
+ value = ''
+ while base < baseEnd:
+ (VirtualAddress, SizeOfBlock) = struct.unpack_from (EfiPeCoff.EFI_IMAGE_BASE_RELOCATION_fmt, data[base:base + EfiPeCoff.BaseRelocationLength])
+ if SizeOfBlock == 0 or SizeOfBlock > size:
+ break
+ reloc = base + EfiPeCoff.BaseRelocationLength
+ relocEnd = base + SizeOfBlock
+ value += '0x%08x SizeOfBlock 0x%x\n' % (VirtualAddress, SizeOfBlock)
+ while reloc < relocEnd:
+ rel, = struct.unpack_from ('<H', data[reloc:reloc+2])
+ value += ' 0x%04x 0x%x\n' % ((rel & 0xFFF), rel >> 12, )
+ reloc += 2
+
+ base = relocEnd
+ return value
+
+ def PeCoffDumpCert (self, offset, size):
+ data = self.f.Read(size, offset)
+ value = '\n'
+ if size > 8:
+ (dwLength, wRevision, wCertificateType) = struct.unpack_from (EfiPeCoff.WIN_CERTIFICATE_fmt, data)
+ value += "dwLength = 0x%04x wRevision = 0x%02x wCertificateType = 0x%02x\n" % (dwLength, wRevision, wCertificateType)
+ # UEFI Scheme
+ for i in range(struct.calcsize(EfiPeCoff.WIN_CERTIFICATE_fmt), size, 0x10):
+ value += "0x{:04x}:".format(i),
+ value += " ".join("{:02x}".format(ord(c)) for c in data[i:i+0x10])
+ value += '\n'
+ else:
+ # Loki Scheme
+ start = 0
+ while start < size:
+ (VirtualAddress, SizeOfBlock) = struct.unpack_from (EfiPeCoff.EFI_IMAGE_BASE_RELOCATION_fmt, data[start: start + struct.calcsize(EfiPeCoff.EFI_IMAGE_BASE_RELOCATION_fmt)])
+ start += struct.calcsize(EfiPeCoff.EFI_IMAGE_BASE_RELOCATION_fmt)
+ value += "CERT: 0x%X size 0x%x\n" % (VirtualAddress, SizeOfBlock)
+ cert = self.f.Read(SizeOfBlock, VirtualAddress)
+ for i in range(0, SizeOfBlock, 0x10):
+ value += "0x{:04x}:".format(i)
+ value += " ".join("{:02x}".format(ord(c)) for c in cert[i:i+0x10])
+ value += '\n'
+ return value
+
+ def __str__(self):
+ return self.PeCoffDumpHdr()
+
+def FmtStrToWidth (c):
+ c = c.upper()
+ if c== 'B':
+ return 1*2
+ if c== 'H':
+ return 2*2
+ if c== 'I' or c=='L':
+ return 4*2
+ if c== 'Q':
+ return 8*2
+ return 0
+
+#define EFI_FAT_BINARY_MAGIC 0x0ef1fab9
+
+# typedef struct _EFI_FAT_BINARY_HEADER {
+# UINT32 magic; /* FAT_MAGIC */
+# UINT32 nfat_arch; /* number of structs that follow */
+# } EFI_FAT_BINARY_HEADER;
+
+# typedef struct _EFI_FAT_BINARY_ARCH {
+# UINT32 cputype; /* cpu specifier (int) */
+# UINT32 cpusubtype; /* machine specifier (int) */
+# UINT32 offset; /* file offset to this object file */
+# UINT32 size; /* size of this object file */
+# UINT32 align; /* alignment as a power of 2 */
+# } EFI_FAT_BINARY_ARCH;
+
+EFI_FAT_BINARY_ARCH_fmt = '<IIIII'
+
+fatCpuType = {
+ 0x01000007: 'x86_64 (X64)',
+ 0x00000007: 'i386 (Ia32)',
+ 0x0000000C: 'ARM (Arm)',
+ 0x0100000C: 'ARM64 (AArch64)',
+}
+
+def CheckForFatBinary (f):
+ '''Return a list of PE/COFF binary objects, from a file object.
+ '''
+ fatEntry = 4 + 4
+ data = f.read(fatEntry)
+ (magic, nfat_arch) = struct.unpack_from ('<II', data)
+ if magic == 0x0ef1fab9:
+ res = []
+ for i in range (nfat_arch):
+ f.seek(fatEntry)
+ fatEntry += struct.calcsize(EFI_FAT_BINARY_ARCH_fmt)
+ data = f.read(struct.calcsize(EFI_FAT_BINARY_ARCH_fmt))
+ (cputype, cpusubtype, offset, size, align) = struct.unpack_from (EFI_FAT_BINARY_ARCH_fmt, data)
+ f.seek(offset)
+ res.append((EfiPeCoff(f.read (size)), "FAT Binary of type %s: offset 0x%x size 0x%x alignment 0x%x" % (fatCpuType.get(cputype, 'Unknown'), offset, size, align)))
+ else:
+ # entire file is a PE/COFF image
+ f.seek(0)
+ res = [(EfiPeCoff(f.read()), "")]
+ return res
+
+if __name__ == "__main__":
+ usage = "usage: %prog [options] PECOFF_FILE"
+ parser = optparse.OptionParser(usage=usage)
+ parser.add_option('-r', '--relocations', action='store_true', dest='relocation', help='display relocation info', default=False)
+ parser.add_option('-c', '--cert', action='store_true', dest='cert', help='display security cert info', default=False)
+ parser.add_option('-s', '--section', type=str, dest='section', help='dump info on a section', default='')
+ parser.add_option('-z', '--zero', action='store_true', dest='zero', help='Zero out fields to enable build reproducibility', default=False)
+ (options, args) = parser.parse_args(sys.argv)
+
+ if len(args) <= 1:
+ parser.print_help()
+ sys.exit(-1)
+
+ with open(args[1], "rb" if not options.zero else "r+b") as f:
+ for (pecoff, description) in CheckForFatBinary (f):
+ if not options.zero:
+ if description != '':
+ print description
+
+ print "%s is a %s:%s image%s" % (args[1], pecoff.PeCoffType, pecoff.MachineType, ' wrapped in FV Section:' if pecoff.FvSection else ':')
+ if pecoff.PeCoffType == '':
+ sys.exit(-1)
+
+ # print header
+ print pecoff.PeCoffDumpHdr()
+ print
+ print "\nSections:"
+ for i in range (0, pecoff.NumberOfSections()):
+ (Name, Section) = pecoff.PeCoffGetSection (i)
+ print pecoff.PeCoffDumpSectionHdr (Name, Section)
+ print
+
+ if '.reloc' in Name and options.relocation:
+ print pecoff.PeCoffDumpRelocations (Section.PointerToRawData, Section.VirtualSize)
+
+ elif '.debug' in Name:
+ (PdbPointer, Guid) = pecoff.PeCoffGetPdePointer ()
+ if Guid == '':
+ print "PdbPointer:{0}\n".format(PdbPointer)
+ else:
+ print "PdbPointer (Mach-O symbol file):{0}\n (Mach-O UUID): {1}".format(PdbPointer, Guid)
+
+ if options.section in Name and options.section != '':
+ print pecoff.PeCoffDumpSection(Name, Section)
+
+ if options.cert:
+ print pecoff.PeCoffDumpCert (pecoff.PeHdr.DataDirVirt_Security, pecoff.PeHdr.DataDirSize_Security)
+
+ else:
+ # this is necessary to find the 'zero list' entries for the debug section
+ for i in range (0, pecoff.NumberOfSections()):
+ (Name, Section) = pecoff.PeCoffGetSection (i)
+
+ if Section.SizeOfRawData > Section.VirtualSize:
+ sizeDiff = Section.SizeOfRawData - Section.VirtualSize
+ offset = pecoff.TeAdjust + Section.PointerToRawData + Section.SizeOfRawData - sizeDiff
+ pecoff.ZeroList.append( [offset, sizeDiff] )
+
+ # The .debug section also contains a timestamp
+ if '.debug' in Name:
+ pecoff.ZeroList.append([pecoff.TeAdjust + Section.PointerToRawData + struct.calcsize(EfiPeCoff.EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_fmt[0:2]), 4])
+
+ for patch, size in pecoff.PeCoffZeroInfo():
+ #print 'patching 0x%x for 0x%x bytes' % (patch, size)
+ if patch != 0:
+ if size == -1:
+ # -1 means to the end of the file
+ f.seek(0,2)
+ size = f.tell() - patch
+ f.seek(patch)
+ f.write(bytearray(size))
+
def main():
try:
- opts, args = getopt.getopt(sys.argv[1:],"hav:",["help","arch=","verbose"])
+ opts, args = getopt.getopt(sys.argv[1:],"ha:v",["help","arch=","verbose"])
except getopt.GetoptError:
print "Unkown command line args"
sys.exit(2)
Verbose = 0
+ EntryPoint = 0
for o,a in opts:
if o in ("-h", "--help"):
Usage ()
sys.exit ()
elif o in ("-a", "--arch"):
Arch = a
+ elif o in ("-v", "--verbose"):
+ Verbose = 1
else:
Usage ()
sys.exit ()
-
if Verbose:
- print "\nmach-o load commands:"
+ print "\nmach-o load commands:"
otoolload = open("otool-load.log", "r")
data = otoolload.read()
otoolload.close()
-
-
+
# extract extry point from ' ss 0x00000000 eflags 0x00000000 eip 0x00000259 cs 0x00000000'
if Arch == "i386":
eip = data.find("eip")
if r15 != -1:
EntryPoint = int (data[r15 + 4:r15 + 4 + 10], 16)
- # extract entry point from ' r15 0x0000000000000000 rip 0x0000000000000253'
if Arch == "x86_64":
rip = data.find("rip")
if rip != -1:
EntryPoint = int (data[rip + 4:rip + 4 + 18], 16)
-
+
+ if Arch == "arm64":
+ pc = data.find("pc")
+ if pc != -1:
+ EntryPoint = int (data[pc + 3:pc + 3 + 18], 16)
+
if EntryPoint == 0:
print "FAIL - no entry point for PE/COFF image"
sys.exit(-1)
else:
- if Verbose:
- print "Entry Point = 0x%08x" % EntryPoint
+ if Verbose:
+ print "Entry Point = 0x%08x" % EntryPoint
if Verbose:
- print "\nPE/COFF dump:"
- objdump = open("objdump-raw.log", "r")
+ print "\nPE/COFF dump:"
+ objdump = open("efi-pecoff-util-raw.log", "r")
data = objdump.read()
objdump.close()
# Extract 'SizeOfImage 00000360'
Index = data.find("SizeOfImage")
+ Index += data[Index:].find("=")
End = data[Index:].find("\n")
- SizeOfImage = int (data[Index+11:Index + End], 16);
+ SizeOfImage = int (data[Index+1:Index + End], 16)
if Verbose:
- print "SizeOfImage = 0x%08x" % SizeOfImage
-
+ print "SizeOfImage = 0x%08x" % SizeOfImage
+
+ # We used to parse output from objdump...
#Parse ' 0 .text 00000080 00000240 00000240 00000240 2**2'
# ' CONTENTS, ALLOC, LOAD, READONLY, CODE '
- EndOfTable = data.find("SYMBOL TABLE:")
- Index = data.find("Idx Name")
- End = data[Index:].find("\n")
- Index = Index + End + 1
-
+ #
+ # But now we parse efi-pecoff-util
+ #Parse 'Sections:
+ # 'Name = .text
+ # 'VirtualSize = 0x00000100
+ # 'VirtualAddress = 0x00000240
+ # 'SizeOfRawData = 0x00000100
+ # 'PointerToRawData = 0x00000240
+ # 'PointerToRelocations = 0x00000000
+ # 'PointerToLinenumbers = 0x00000000
+ # 'NumberOfRelocations = 0x0000
+ # 'NumberOfLinenumbers = 0x0000
+ # 'Characteristics = 0x60000020
+ EndOfTable = data.find("PdbPointer")
+ Index = data.find("Sections:");
+ Index += data[Index:].find("\n");
+
+ Name = ""
+ Size = -1
+ VMA = -1
+ LMA = -1
+ FileOff = -1
PeCoffEnd = 0
while Index < EndOfTable:
End = data[Index:].find("\n")
Split = data[Index:Index+End].split()
- # Split[0] Indx
- # Split[1] Name i.e. .text
- # Split[2] Size
- # Split[3] VMA
- # Split[4] LMA
- # Split[5] File Off
- # Split[6] Align
- if int(Split[3],16) != int(Split[5],16):
- print "FAIL - %s VMA %08x not equal File off %08x XIP will not work" % (Split[1], int(Split[3],16), int(Split[5],16))
- sys.exit(-1)
-
- if int(Split[3],16) + int(Split[2],16) > PeCoffEnd:
- PeCoffEnd = int(Split[3],16) + int(Split[2],16)
-
- if Split[1] == ".text":
- SecStart = int(Split[3],16)
- SecEnd = int(Split[3],16) + int(Split[2],16)
- if (EntryPoint < SecStart) or (EntryPoint > SecEnd):
- print "FAIL - Entry point (0x%x) not in .text section (0x%x - 0x%x)" % (EntryPoint, SecStart, SecEnd)
- sys.exit(-1)
+ # Split[0] Key
+ # Split[1] =
+ # Split[2] Value
+ if len(Split) == 0:
+ # blank line, we've finished reading a section. process results
+ if Name != "":
+ # make sure we've found everything
+ if Size == -1:
+ print "FAIL - %s Size missing" % Name
+ sys.exit(-1)
+ if VMA == -1:
+ print "FAIL - %s VMA missing" % Name
+ sys.exit(-1)
+ if LMA == -1:
+ print "FAIL - %s LMA missing" % Name
+ sys.exit(-1)
+ if FileOff == -1:
+ print "FAIL - %s FileOff missing" % Name
+ sys.exit(-1)
+
+ if VMA != FileOff:
+ print "FAIL - %s VMA %08x not equal File off %08x XIP will not work" % (Name, VMA, FileOff)
+ sys.exit(-1)
+
+ SecStart = VMA
+ SecEnd = VMA + Size
+ if SecEnd > PeCoffEnd:
+ PeCoffEnd = SecEnd
+
+ if Name == ".text":
+ if (EntryPoint < SecStart) or (EntryPoint > SecEnd):
+ print "FAIL - Entry point (0x%x) not in .text section (0x%x - 0x%x)" % (EntryPoint, SecStart, SecEnd)
+ sys.exit(-1)
+
+ if Verbose:
+ print "%10s %08x %016x %016x %08x" % (Name, Size, VMA, LMA, FileOff) + " End = %x" % PeCoffEnd
+
+ # clear values for next time
+ Name = ""
+ Size = -1
+ VMA = -1
+ LMA = -1
+ FileOff = -1
+ elif len(Split) == 3:
+ Key = Split[0]
+ Value = Split[2]
- if Verbose:
- print "%10s" % Split[1] + ' ' + Split[2] + ' ' + Split[3] + ' ' + Split[4] + ' ' + Split[5] + " End = %x" % PeCoffEnd
- Index += data[Index:].find("\n") + 1
+ if Key == "Name":
+ Name = Value
+ if Key == "VirtualSize":
+ Size = int(Value,16)
+ if Key == "VirtualAddress":
+ VMA = int(Value,16)
+ LMA = VMA # BUG: on our platform the virtual memory address and the load memory address are the same?
+ if Key == "PointerToRawData":
+ FileOff = int(Value,16)
+ else:
+ print "FAIL - Line is not (key = value): '%s'" % data[Index:Index+End]
+ sys.exit(-1)
+
Index += data[Index:].find("\n") + 1
if SizeOfImage < PeCoffEnd:
sys.exit(-1)
if Verbose:
- print "\nmach-o relocations:"
+ print "\nmach-o relocations:"
otoolreloc = open("otool-reloc.log", "r")
lines = otoolreloc.readlines()
otoolreloc.close()
found = True
if Verbose:
- print
+ print
if __name__ == "__main__":
all:
${CC} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_AB*' -dead_strip
- nm -j -f libfoo.dylib | grep _good | ${FAIL_IF_EMPTY}
- nm -j -f libfoo.dylib | grep _bad | ${FAIL_IF_STDIN}
+ nm -j libfoo.dylib | grep _good | ${FAIL_IF_EMPTY}
+ nm -j libfoo.dylib | grep _bad | ${FAIL_IF_STDIN}
${PASS_IFF_GOOD_MACHO} libfoo.dylib
clean:
all:
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*bar'
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect1 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect1 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f?o'
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect2 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect2 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*'
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect3 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect3 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f*o*'
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect4 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect4 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,_foo -Wl,-exported_symbol -Wl,'_*bar'
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect5 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -exported_symbols_list list5
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect5 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-unexported_symbol -Wl,'_*2*'
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect6 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect6 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[abcdef]o'
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect7 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-f]o'
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect7 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-z]o'
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect8 | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-fnop]o'
- nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN}
+ nm -j -g libfoo.dylib -s __TEXT __text | diff - expect8 | ${FAIL_IF_STDIN}
${PASS_IFF_GOOD_MACHO} libfoo.dylib
clean:
all:
${CXX} ${CXXFLAGS} -flto foo.cxx -c -o foo.o
${CXX} ${CXXFLAGS} -flto bar.cxx -c -o bar.o
- ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libfoo.dylib -exported_symbols_list foo.exp
+ ${CXX} ${CXXFLAGS} foo.o bar.o -dynamiclib -o libfoo.dylib -exported_symbols_list foo.exp
nm -m libfoo.dylib | grep "weak external" | ${FAIL_IF_STDIN}
${PASS_IFF_GOOD_MACHO} libfoo.dylib
run: all
all:
- ${CC} ${CCFLAGS} -flto -o foo foo.c -Wl,-dead_strip,-exported_symbol,_my_global -v
+ ${CC} ${CCFLAGS} -flto -o foo foo.c -Wl,-dead_strip,-exported_symbol,_my_global
# Check symbols that are expected to be preserved
nm -m foo | grep _global_metadata | ${FAIL_IF_EMPTY}
nm -m foo | grep _liveness_binder | ${FAIL_IF_EMPTY}
# Check symbols that are expected to be stripped
nm -m foo | grep unused | ${FAIL_IF_STDIN}
-
+
+ ${PASS_IFF} true
clean:
rm foo
# Check that unexported symbols are hidden
nm a.out | grep "t _bar" | ${FAIL_IF_EMPTY}
nm a.out | grep "t _foo" | ${FAIL_IF_EMPTY}
-
+ ${PASS_IFF} true
+
clean:
rm a.out b.o a.o
run: all
all:
- ${CC} -arch ${ARCH} -c -Wa,-n empty.s -o empty.o
+ ${CC} -arch ${ARCH} -c empty.s -o empty.o
${LD} -r empty.o -x -o empty2.o
- otool -lv empty2.o | egrep 'vmsize 0x0[0]+$$' | ${FAIL_IF_EMPTY}
- otool -lv empty2.o | grep 'filesize 0' | ${FAIL_IF_EMPTY}
- otool -lv empty2.o | grep 'nsyms 0' | ${FAIL_IF_EMPTY}
- otool -lv empty2.o | grep 'symoff 0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -lv empty2.o | egrep 'vmsize 0x0[0]+$$' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -lv empty2.o | grep 'filesize 0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -lv empty2.o | grep 'nsyms 0' | ${FAIL_IF_EMPTY}
+ ${OTOOL} -lv empty2.o | grep 'symoff 0' | ${FAIL_IF_EMPTY}
${PASS_IFF} true
clean:
+++ /dev/null
-##
-# Copyright (c) 2007-2008 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
-
-SHELL = bash # use bash shell so we can redirect just stderr
-
-#
-# Validate that the linker catches illegal combinations of .o files
-# compiled with different GC settings.
-#
-
-IMAGE_INFO = "__objc_imageinfo"
-ifeq ($(ARCH),i386)
- IMAGE_INFO = "__image_info"
-endif
-
-ifeq ($(ARCH),i386)
- ALL = all-gc
-else
-ifeq ($(ARCH),x86_64)
- ALL = all-gc
-else
- ALL = all-nogc
-endif
-endif
-
-all: ${ALL}
-
-# For platforms that do not support GC.
-all-nogc:
- ${PASS_IFF} true
-
-# For platforms that support GC.
-all-gc:
- ${CC} ${CCFLAGS} foo.m -c -o foo.o
- ${FAIL_IF_BAD_OBJ} foo.o
-
- ${CC} ${CCFLAGS} bar.m -c -o bar.o
- ${FAIL_IF_BAD_OBJ} bar.o
-
- # clang no longer builds GC so we create fake GC object files instead.
-
- cp -f foo.o foo-gc.o
- ${OBJCIMAGEINFO} +supports-gc foo-gc.o
-
- cp -f foo.o foo-gc-only.o
- ${OBJCIMAGEINFO} +supports-gc +requires-gc foo-gc-only.o
-
- cp -f bar.o bar-gc.o
- ${OBJCIMAGEINFO} +supports-gc bar-gc.o
-
- cp -f bar.o bar-gc-only.o
- ${OBJCIMAGEINFO} +supports-gc +requires-gc bar-gc-only.o
-
- # check RR + RR -> RR
- ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
- ${FAIL_IF_BAD_MACHO} libfoobar.dylib
-
- # check GC/RR + GC/RR -> GC/RR
- ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
- ${FAIL_IF_BAD_MACHO} libfoobar.dylib
- ${OBJCIMAGEINFO} libfoobar.dylib | grep supports-gc | ${FAIL_IF_EMPTY}
-
- # check GC + GC -> GC
- ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
- ${FAIL_IF_BAD_MACHO} libfoobar.dylib
- ${OBJCIMAGEINFO} libfoobar.dylib | grep 'supports-gc requires-gc' | ${FAIL_IF_EMPTY}
-
- # check RR + GC/RR -> RR
- ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
- ${FAIL_IF_BAD_MACHO} libfoobar.dylib
- ${OBJCIMAGEINFO} libfoobar.dylib | grep gc | ${FAIL_IF_STDIN}
-
- # check GC/RR + RR -> RR
- ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
- ${FAIL_IF_BAD_MACHO} libfoobar.dylib
- ${OBJCIMAGEINFO} libfoobar.dylib | grep gc | ${FAIL_IF_STDIN}
-
- # check GC + GC/RR -> GC
- ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation
- ${FAIL_IF_BAD_MACHO} libfoobar.dylib
- ${OBJCIMAGEINFO} libfoobar.dylib | grep 'supports-gc requires-gc' | ${FAIL_IF_EMPTY}
-
- # check RR + GC -> error
- ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation 2> fail.log
-
- # check cmd line GC/RR, GC/RR + RR -> error
- ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc -framework Foundation 2> fail.log
-
- # check GC/RR + compaction
- ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib -framework Foundation
- ${OBJCIMAGEINFO} libfoobar.dylib | grep 0x12 | ${FAIL_IF_EMPTY}
-
- # check GC + compaction
- ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib -framework Foundation
- ${OBJCIMAGEINFO} libfoobar.dylib | grep 0x16 | ${FAIL_IF_EMPTY}
-
- # none + GC/RR-dylib -> none
- ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation
- ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation
- size -m -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
-
- # none + GC-dylib -> none
- ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation
- ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation
- size -m -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
-
- # none + RR-dylib -> none
- ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation
- ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation
- size -m -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN}
-
- # check RR + GC-dylib -> error
- ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation
- ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib -framework Foundation 2> fail.log
-
- # check GC + RR-dylib -> error
- ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation
- ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar-gc-only.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib -framework Foundation 2> fail.log
-
- ${PASS_IFF} true
-
-clean:
- rm -rf foo*.o bar*.o libfoobar.dylib fail.log libfoo.dylib libnone.dylib
+++ /dev/null
-#include <Foundation/Foundation.h>
-
-@interface Bar : NSObject {
- int f;
-}
-- (void) doit;
-@end
-
-@implementation Bar
-- (void) doit { }
-@end
-
+++ /dev/null
-Validate that the linker catches illegal combintations of .o files compiled with different GC settings
+++ /dev/null
-#include <Foundation/Foundation.h>
-
-@interface Foo : NSObject {
- int f;
-}
-- (void) doit;
-@end
-
-
-@implementation Foo
-- (void) doit { }
-@end
-
+++ /dev/null
-void none() {}
+++ /dev/null
-void _objc_empty_cache() {}
-void _objc_empty_vtable() {}
# -sub_library for 10.4
${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4
${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.4
+ ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libmiddle.dylib -mmacosx-version-min=10.4
${FAIL_IF_BAD_MACHO} libmiddle.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.4
${FAIL_IF_BAD_MACHO} libfoo.dylib
# -sub_library for 10.5
${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib
${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib
+ ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libmiddle.dylib
${FAIL_IF_BAD_MACHO} libmiddle.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib
${FAIL_IF_BAD_MACHO} libfoo.dylib
run: all
all:
- as -arch ${ARCH} -L extra.s -o extra.o
+ ${AS} -Wa,-L extra.s -o extra.o
${CC} ${CCFLAGS} main.c extra.o -o main
nm main | grep "lother" | ${FAIL_IF_STDIN}
nm main | grep "L123" | ${FAIL_IF_STDIN}
+++ /dev/null
-##
-# Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
-#
-# @APPLE_LICENSE_HEADER_START@
-#
-# This file contains Original Code and/or Modifications of Original Code
-# as defined in and that are subject to the Apple Public Source License
-# Version 2.0 (the 'License'). You may not use this file except in
-# compliance with the License. Please obtain a copy of the License at
-# http://www.opensource.apple.com/apsl/ and read it before using this
-# file.
-#
-# The Original Code and all software distributed under the License are
-# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
-# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
-# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
-# Please see the License for the specific language governing rights and
-# limitations under the License.
-#
-# @APPLE_LICENSE_HEADER_END@
-##
-TESTROOT = ../..
-include ${TESTROOT}/include/common.makefile
-
-#
-# The point of this test is a sanity check that ld
-# removes the stabs associated with a copy of a coalesced
-# function that was removed.
-# Running nm through stabs-filter.pl produces connonical stabs
-# that can be diffed against a checked in know good set of stabs
-#
-
-run: all
-
-all: hello.o other.o
- g++-4.2 ${CCXXFLAGS} -gstabs+ -gused hello.o other.o -o stabs-hello-${ARCH}
- ${FAIL_IF_BAD_MACHO} stabs-hello-${ARCH}
- nm -ap stabs-hello-${ARCH} | grep FUN | grep _Z3fooi | wc -l > stabs-hello-foo-count
- echo " 1" > one
- ${PASS_IFF} diff stabs-hello-foo-count one
-
-hello.o : hello.cxx
- g++-4.2 ${CCXXFLAGS} -gstabs+ -gused hello.cxx -c -o $@
- ${FAIL_IF_BAD_OBJ} $@
-
-other.o : other.cxx
- g++-4.2 ${CCXXFLAGS} -gstabs+ -gused other.cxx -c -o $@
- ${FAIL_IF_BAD_OBJ} $@
-
-clean:
- rm -rf stabs-hello-* *.o *.stabs stabs-hello-foo-count one
+++ /dev/null
-The point of this test is a sanity check that ld removes the stabs associated with a copy of a coalesced
-function that was removed. Running nm through stabs-filter.pl produces connonical stabs
-that can be diffed against a checked in know good set of stabs
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-inline int foo(int x)
-{
- return x + 10;
-}
-
-extern int bar(int x);
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-#include <stdio.h>
-
-#include "header.h"
-
-
-int main()
-{
- foo(bar(3));
- fprintf(stdout, "hello\n");
-}
\ No newline at end of file
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include "header.h"
-
-int uninit;
-int init = 1;
-static int suninit;
-static int sinit=0;
-
-int bar(int x)
-{
- static int bar_uninit;
- static int bar_init=3;
- bar_uninit = x;
- return 20 + suninit + sinit +
- bar_init + bar_uninit + foo(x);
-}
-
-
+++ /dev/null
-##
-# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
-#
-# @APPLE_LICENSE_HEADER_START@
-#
-# This file contains Original Code and/or Modifications of Original Code
-# as defined in and that are subject to the Apple Public Source License
-# Version 2.0 (the 'License'). You may not use this file except in
-# compliance with the License. Please obtain a copy of the License at
-# http://www.opensource.apple.com/apsl/ and read it before using this
-# file.
-#
-# The Original Code and all software distributed under the License are
-# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
-# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
-# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
-# Please see the License for the specific language governing rights and
-# limitations under the License.
-#
-# @APPLE_LICENSE_HEADER_END@
-##
-TESTROOT = ../..
-include ${TESTROOT}/include/common.makefile
-
-# Test that file paths in a stab reference ends with a /
-# if there is no terminating /, gdb does not recognize this as a file path
-# The provided files coalesced1a.o coalesced1b.o are ppc64 linked
-# rdar://problem/4565088
-
-run: all
-
-all:
- $(CC) -gstabs+ main.c -o outfile
- ${FAIL_IF_BAD_MACHO} outfile
- nm -ap outfile | grep '.*\<SO\>.*test-cases.*/$$' | ${PASS_IFF_STDIN}
-
-clean:
- rm -rf outfile*
+++ /dev/null
-int main()
-{
- return 0;
-}
+++ /dev/null
-##
-# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
-#
-# @APPLE_LICENSE_HEADER_START@
-#
-# This file contains Original Code and/or Modifications of Original Code
-# as defined in and that are subject to the Apple Public Source License
-# Version 2.0 (the 'License'). You may not use this file except in
-# compliance with the License. Please obtain a copy of the License at
-# http://www.opensource.apple.com/apsl/ and read it before using this
-# file.
-#
-# The Original Code and all software distributed under the License are
-# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
-# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
-# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
-# Please see the License for the specific language governing rights and
-# limitations under the License.
-#
-# @APPLE_LICENSE_HEADER_END@
-##
-TESTROOT = ../..
-include ${TESTROOT}/include/common.makefile
-
-# Test the ld option -stack_addr and -stack_size used together
-
-ifeq ($(FILEARCH),arm)
- STACK_ADDR = 0x2C000000
- STACK_SIZE = 0x05000000
- STACK_TOP = 0x27000000
-else
-ifeq (,${findstring 64,$(ARCH)})
- STACK_ADDR = 0xCC000000
- STACK_SIZE = 0x05000000
- STACK_TOP = 0xc7000000
-else
- STACK_ADDR = 0x110000000
- STACK_TOP = 0x000000010b000000
- STACK_SIZE = 0x0000000005000000
-endif
-endif
-
-run: all
-
-
-
-all:
- ${CC} ${CCFLAGS} main.c -o main -Wl,-stack_size,${STACK_SIZE} -Wl,-stack_addr,${STACK_ADDR}
- # Check for __UNIXSTACK section in object, check that it has the correct value
- otool -l main | grep -A6 __UNIXSTACK > main.otool
- grep " vmsize[ ]*${STACK_SIZE}" main.otool | ${FAIL_IF_EMPTY}
- grep " vmaddr[ ]*${STACK_TOP}" main.otool | ${FAIL_IF_EMPTY}
- ${FAIL_IF_BAD_MACHO} main
- ${CC} ${CCFLAGS} -static main.c -o main2 -e _main -nostdlib -Wl,-new_linker -Wl,-stack_size,${STACK_SIZE} -Wl,-stack_addr,${STACK_ADDR}
- otool -l main2 | grep -A6 __UNIXSTACK > main2.otool
- grep " vmsize[ ]*${STACK_SIZE}" main2.otool | ${FAIL_IF_EMPTY}
- grep " vmaddr[ ]*${STACK_TOP}" main2.otool | ${FAIL_IF_EMPTY}
- ${PASS_IFF_GOOD_MACHO} main2
-
-clean:
- rm -rf main main.otool main2.otool main2
+++ /dev/null
-Test the ld commands -stack_addr, -stack_size (3939852 and 4729162)
-
-
--stack_addr value
-Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_size is specified and -stack_addr is not, a default stack address specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK. Note that the initial stack address will be either at the high address of the segment or the low address of the segment depending on which direction the stack grows for the architecture being linked.
-
-
--stack_size value
-Specifies the size of the stack segment value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_addr is specified and -stack_size is not, a default stack size specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK .
-
-
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-#include <stdio.h>
-
-int main()
-{
- return 0;
-}
endif
endif
+STACK_SIZE_DEC = $(shell printf "%d" $(STACK_SIZE))
run: all
all:
${CC} ${CCFLAGS} main.c -o main -Wl,-stack_size,${STACK_SIZE}
# Check for __UNIXSTACK section in object, check that it has the correct value
- otool -l main | grep -A6 __UNIXSTACK > main.otool
- grep " vmsize[ ]*${STACK_SIZE}" main.otool | ${FAIL_IF_EMPTY}
- grep " vmaddr[ ]*${STACK_TOP}" main.otool | ${FAIL_IF_EMPTY}
+ ${OTOOL} -l main | grep -A3 LC_MAIN > main.otool
+ grep " stacksize ${STACK_SIZE_DEC}" main.otool | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
clean:
all:
${CC} ${CCFLAGS} test.c -c -o test.o
${CC} ${CCFLAGS} test.o -static -Wl,-pie -o test -e _entry -nostdlib -Wl,-new_linker
- otool -rv test | grep __DATA | wc -l | grep ${RELOC_COUNT} | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -rebase test | grep __DATA | wc -l | grep ${RELOC_COUNT} | ${FAIL_IF_EMPTY}
# verify trying to use absolute addressing fails
${CC} ${CCFLAGS} -static bad.c -c -o bad.o
${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} test.o bad.o -static -Wl,-pie -o test.bad -e _entry -nostdlib -Wl,-new_linker 2>/dev/null
int main()
{
- malloc(1);
- malloc(2);
- malloc(3);
- malloc(4);
+ (void)malloc(1);
+ (void)malloc(2);
+ (void)malloc(3);
+ (void)malloc(4);
return 0;
}
all:
# build bootstrap dylib of with all code
- ${CC} ${CCFLAGS} -dynamiclib a.c b.c c.c -o libBig.stub -install_name /usr/lib/libBig.dylib
+ ${CC} ${CCFLAGS} -dynamiclib a.c b.c c.c -o libBig.stub -install_name /usr/local/lib/libBig.dylib
# link each sub library against bootstrap to intra-Big symbols
- ${CC} ${CCFLAGS} -dynamiclib a.c -o liba.dylib libBig.stub -install_name /usr/local/lib/big/liba.dylib -umbrella Big
- ${CC} ${CCFLAGS} -dynamiclib b.c -o libb.dylib libBig.stub -install_name /usr/local/lib/big/libb.dylib -umbrella Big
- ${CC} ${CCFLAGS} -dynamiclib c.c -o libc.dylib libBig.stub -install_name /usr/local/lib/big/libc.dylib -umbrella Big
- ${CC} ${CCFLAGS} -dynamiclib -Wl,-reexport_library,liba.dylib -Wl,-reexport_library,libb.dylib -Wl,-reexport_library,libc.dylib -o libBig.dylib -install_name /usr/lib/libBig.dylib
+ ${CC} ${CCFLAGS} -dynamiclib a.c -o liba.dylib libBig.stub -install_name /usr/local/lib/liba.dylib -umbrella Big
+ ${CC} ${CCFLAGS} -dynamiclib b.c -o libb.dylib libBig.stub -install_name /usr/local/lib/libb.dylib -umbrella Big
+ ${CC} ${CCFLAGS} -dynamiclib c.c -o libc.dylib libBig.stub -install_name /usr/local/lib/libc.dylib -umbrella Big
+ ${CC} ${CCFLAGS} -dynamiclib -Wl,-reexport_library,liba.dylib -Wl,-reexport_library,libb.dylib -Wl,-reexport_library,libc.dylib -o libBig.dylib -install_name /usr/local/lib/libBig.dylib
# re-link against correct dylibs now that everything is built
- ${CC} ${CCFLAGS} -dynamiclib a.c -o liba.dylib libc.dylib -install_name /usr/local/lib/big/liba.dylib -umbrella Big
+ ${CC} ${CCFLAGS} -dynamiclib a.c -o liba.dylib libc.dylib -install_name /usr/local/lib/liba.dylib -umbrella Big
${DYLDINFO} -lazy_bind liba.dylib | grep c2 | grep libc | ${FAIL_IF_EMPTY}
- ${CC} ${CCFLAGS} -dynamiclib b.c -o libb.dylib libc.dylib -install_name /usr/local/lib/big/libb.dylib -umbrella Big
- ${CC} ${CCFLAGS} -dynamiclib c.c -o libc.dylib liba.dylib -install_name /usr/local/lib/big/libc.dylib -umbrella Big
+ ${CC} ${CCFLAGS} -dynamiclib b.c -o libb.dylib libc.dylib -install_name /usr/local/lib/libb.dylib -umbrella Big
+ ${CC} ${CCFLAGS} -dynamiclib c.c -o libc.dylib liba.dylib -install_name /usr/local/lib/libc.dylib -umbrella Big
${DYLDINFO} -lazy_bind libc.dylib | grep a2 | grep liba | ${FAIL_IF_EMPTY}
- ${CC} ${CCFLAGS} -dynamiclib -Wl,-reexport_library,liba.dylib -Wl,-reexport_library,libb.dylib -Wl,-reexport_library,libc.dylib -o libBig.dylib -install_name /usr/lib/libBig.dylib
+ ${CC} ${CCFLAGS} -dynamiclib -Wl,-reexport_library,liba.dylib -Wl,-reexport_library,libb.dylib -Wl,-reexport_library,libc.dylib -o libBig.dylib -install_name /usr/local/lib/libBig.dylib
${CC} ${CCFLAGS} main.c -o main libBig.dylib -L.
${PASS_IFF_GOOD_MACHO} main
# Check that -U and -undefined dynamic_lookup work
#
+ALL = all-mac
+
+# -undefined dynamic_lookup is deprecated on iOS
+ifeq ($(ARCH),arm64)
+ ALL = all-ios
+else
+ifeq ($(ARCH),armv7)
+ ALL = all-ios
+endif
+endif
+
+
run: all
-all:
+all: ${ALL}
+
+all-mac:
${CC} ${CCFLAGS} main.c -o main -undefined dynamic_lookup
nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY}
${DYLDINFO} -lazy_bind -bind main | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY}
- ${DYLDINFO} -lazy_bind -bind main | grep _exit | grep "flat-namespace" | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} main
${CC} ${CCFLAGS} main.c -o main -Wl,-U,_foo
nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY}
${DYLDINFO} -lazy_bind -bind main | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY}
- ${DYLDINFO} -lazy_bind -bind main | grep _exit | grep "flat-namespace" | ${FAIL_IF_STDIN}
${FAIL_IF_BAD_MACHO} main
${CC} ${CCFLAGS} main.c -o main -flat_namespace -Wl,-U,_foo
nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_STDIN}
${DYLDINFO} -lazy_bind -bind main | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY}
- ${DYLDINFO} -lazy_bind -bind main | grep _exit | grep "flat-namespace" | ${FAIL_IF_EMPTY}
${FAIL_IF_BAD_MACHO} main
${CC} ${CCFLAGS} main.c -bundle -o main.bundle -nodefaultlibs -undefined dynamic_lookup
nm -m main.bundle | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY}
${DYLDINFO} -lazy_bind -bind main.bundle | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY}
- ${DYLDINFO} -lazy_bind -bind main.bundle | grep _exit | grep "flat-namespace" | ${FAIL_IF_STDIN}
${PASS_IFF_GOOD_MACHO} main.bundle
-
+all-ios:
+ ${PASS_IFF} true
clean:
rm -f main main.bundle