From: Apple Date: Tue, 18 Sep 2018 22:03:37 +0000 (+0000) Subject: ld64-409.12.tar.gz X-Git-Tag: developer-tools-100^0 X-Git-Url: https://git.saurik.com/apple/ld64.git/commitdiff_plain/e456bf1059cf7e6b8b71545d1b2f2092b55a9684?hp=f410558f5d60087e4c310119a1751b437121c3b9 ld64-409.12.tar.gz --- diff --git a/.gitignore b/.gitignore index 9190838..a994d26 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ build DerivedData +ld64.xcodeproj/project.xcworkspace +ld64.xcodeproj/xcuserdata diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 index 64641a6..d448b5c 100644 --- a/doc/man/man1/ld.1 +++ b/doc/man/man1/ld.1 @@ -552,6 +552,8 @@ Prints names of functions that are eliminated by deduplication and total code sa 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 @@ -767,9 +769,6 @@ interposable then if _malloc was interposed at runtime, calls to malloc from wit .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 @@ -787,7 +786,8 @@ to load instead. .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. diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index 3297210..f6c54d0 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -47,6 +47,7 @@ 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 */; }; @@ -206,7 +207,7 @@ 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 */, @@ -217,7 +218,7 @@ 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 */, @@ -227,7 +228,7 @@ 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 */, @@ -238,7 +239,7 @@ 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 */, @@ -249,7 +250,7 @@ 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 */, @@ -268,6 +269,8 @@ B3B672411406D42800A376BB /* Snapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Snapshot.cpp; path = src/ld/Snapshot.cpp; sourceTree = ""; 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 = ""; tabWidth = 4; usesTabs = 1; }; B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + C1E27B571F6B1B67003B8FA6 /* thread_starts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_starts.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + C1E27B591F6B1B70003B8FA6 /* thread_starts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = thread_starts.h; sourceTree = ""; }; 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 = ""; tabWidth = 4; usesTabs = 1; }; F91B7B0318987D5F0099486F /* AddressSpace.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AddressSpace.hpp; sourceTree = ""; }; @@ -353,7 +356,7 @@ F9CC24181461FB4300A92174 /* superblob.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = superblob.h; sourceTree = ""; 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 = ""; 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 = ""; 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 = ""; 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 = ""; tabWidth = 8; usesTabs = 1; }; F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/ld/debugline.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -483,6 +486,8 @@ F9AE20FE1107D1440007ED5D /* dylibs.h */, F9A4DB8F10F816FF00BD8423 /* objc.cpp */, F9A4DB9010F816FF00BD8423 /* objc.h */, + C1E27B571F6B1B67003B8FA6 /* thread_starts.cpp */, + C1E27B591F6B1B70003B8FA6 /* thread_starts.h */, F9AA650C1051BD2B003E3539 /* stubs */, ); name = passes; @@ -986,6 +991,7 @@ 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 */, @@ -1159,7 +1165,7 @@ "$(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; @@ -1186,7 +1192,7 @@ "$(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 = ( @@ -1212,7 +1218,7 @@ "$(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 = ( @@ -1267,7 +1273,7 @@ "$(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 = ""; @@ -1284,6 +1290,7 @@ "-L$(DT_TOOLCHAIN_DIR)/usr/lib", "-ltapi", "@$(DERIVED_FILE_DIR)/linkExtras", + "-Wl,-stack_size,0x10000000", ); PREBINDING = NO; PRODUCT_NAME = ld; @@ -1343,7 +1350,7 @@ "$(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++", @@ -1357,6 +1364,7 @@ "-L$(DT_TOOLCHAIN_DIR)/usr/lib", "-ltapi", "@$(DERIVED_FILE_DIR)/linkExtras", + "-Wl,-stack_size,0x01000000", ); PREBINDING = NO; PRODUCT_NAME = ld; @@ -1395,7 +1403,7 @@ "$(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++", @@ -1434,7 +1442,7 @@ "$(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)", @@ -1459,6 +1467,7 @@ 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; @@ -1488,6 +1497,7 @@ 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; @@ -1534,6 +1544,7 @@ 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; @@ -1616,7 +1627,7 @@ "$(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++", @@ -1654,7 +1665,7 @@ 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; @@ -1676,7 +1687,7 @@ 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)", @@ -1710,7 +1721,7 @@ "$(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)", @@ -1740,7 +1751,7 @@ 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)", @@ -1756,12 +1767,13 @@ 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)", @@ -1786,7 +1798,7 @@ 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)", @@ -1816,7 +1828,7 @@ 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)", @@ -1836,7 +1848,7 @@ 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)", @@ -1879,7 +1891,7 @@ 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)", @@ -1902,7 +1914,7 @@ 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)", @@ -1920,6 +1932,7 @@ 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; @@ -1936,7 +1949,7 @@ 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)", @@ -1953,12 +1966,13 @@ 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)", @@ -1982,7 +1996,7 @@ 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)", @@ -2003,7 +2017,7 @@ 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)", @@ -2025,7 +2039,7 @@ 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; @@ -2042,7 +2056,7 @@ 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; diff --git a/src/abstraction/MachOFileAbstraction.hpp b/src/abstraction/MachOFileAbstraction.hpp index 252e413..a7198df 100644 --- a/src/abstraction/MachOFileAbstraction.hpp +++ b/src/abstraction/MachOFileAbstraction.hpp @@ -64,6 +64,9 @@ #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 */ @@ -133,7 +136,35 @@ #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 @@ -276,6 +307,9 @@ #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 @@ -505,6 +539,7 @@ #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 @@ -554,6 +589,9 @@ }; #endif +#ifndef PLATFORM_IOSMAC + #define PLATFORM_IOSMAC 6 +#endif // kind target-address fixup-addr [adj] diff --git a/src/create_configure b/src/create_configure index 7ba470b..4ab3998 100755 --- a/src/create_configure +++ b/src/create_configure @@ -25,18 +25,7 @@ do 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 diff --git a/src/ld/Architectures.hpp b/src/ld/Architectures.hpp index eca8c56..3bfd95b 100644 --- a/src/ld/Architectures.hpp +++ b/src/ld/Architectures.hpp @@ -62,6 +62,7 @@ struct arm64 }; + #endif // __ARCHITECTURES__ diff --git a/src/ld/HeaderAndLoadCommands.hpp b/src/ld/HeaderAndLoadCommands.hpp index fbcc13f..e03c59c 100644 --- a/src/ld/HeaderAndLoadCommands.hpp +++ b/src/ld/HeaderAndLoadCommands.hpp @@ -69,6 +69,8 @@ public: 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 @@ -105,8 +107,8 @@ private: 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; @@ -146,12 +148,12 @@ private: 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; @@ -238,21 +240,24 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: } _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(); @@ -424,12 +429,22 @@ uint64_t HeaderAndLoadCommandsAtom::size() const if ( _hasUUIDLoadCommand ) sz += sizeof(macho_uuid_command

); - if ( _hasVersionLoadCommand ) - sz += sizeof(macho_version_min_command

); - - if ( _hasBuildVersionLoadCommand ) - sz += alignedSize(sizeof(macho_build_version_command

) + sizeof(macho_build_tool_version

)*_toolsVersions.size()); - + if ( _hasVersionLoadCommand ) { + if (_simulatorSupportDylib) { +#if 0 +//FIXME hack to workaround simulator issues without cctools changes + sz += sizeof(macho_version_min_command

); + sz += (_options.platforms().count() * alignedSize(sizeof(macho_build_version_command

) + sizeof(macho_build_tool_version

)*_toolsVersions.size())); +#else + sz += sizeof(macho_version_min_command

); +#endif + } else if (_options.platforms().minOS(ld::supportsLCBuildVersion) && !lcBuildVersionDisabled()) { + sz += (_options.platforms().count() * alignedSize(sizeof(macho_build_version_command

) + sizeof(macho_build_tool_version

)*_toolsVersions.size())); + } else { + sz += sizeof(macho_version_min_command

); + } + } + if ( _hasSourceVersionLoadCommand ) sz += sizeof(macho_source_version_command

); @@ -529,13 +544,23 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const 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; @@ -642,10 +667,6 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const 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() ) { @@ -661,6 +682,11 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const 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; @@ -688,7 +714,7 @@ uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const template <> uint32_t HeaderAndLoadCommandsAtom::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; @@ -847,6 +873,8 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* 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; @@ -1179,40 +1207,44 @@ uint8_t* HeaderAndLoadCommandsAtom::copyUUIDLoadCommand(uint8_t* p) const template -uint8_t* HeaderAndLoadCommandsAtom::copyVersionLoadCommand(uint8_t* p) const +uint8_t* HeaderAndLoadCommandsAtom::copyVersionLoadCommand(uint8_t* p, const ld::Platform& platform, uint32_t minVersion, uint32_t sdkVersion) const { macho_version_min_command

* cmd = (macho_version_min_command

*)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

)); - 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

)); - 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

)); - 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

)); - 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; } @@ -1222,15 +1254,15 @@ uint8_t* HeaderAndLoadCommandsAtom::copyVersionLoadCommand(uint8_t* p) const template -uint8_t* HeaderAndLoadCommandsAtom::copyBuildVersionLoadCommand(uint8_t* p) const + uint8_t* HeaderAndLoadCommandsAtom::copyBuildVersionLoadCommand(uint8_t* p, const ld::Platform& platform, uint32_t minVersion, uint32_t sdkVersion) const { macho_build_version_command

* cmd = (macho_build_version_command

*)p; cmd->set_cmd(LC_BUILD_VERSION); cmd->set_cmdsize(alignedSize(sizeof(macho_build_version_command

) + sizeof(macho_build_tool_version

)*_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

* tools = (macho_build_tool_version

*)(p + sizeof(macho_build_version_command

)); for (uint64_t tool : _toolsVersions) { @@ -1551,6 +1583,17 @@ uint8_t* HeaderAndLoadCommandsAtom::copyOptimizationHintsLoadCommand(uint8_t* return p + sizeof(macho_linkedit_data_command

); } +template +bool HeaderAndLoadCommandsAtom::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 void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const @@ -1568,7 +1611,7 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const mh->set_flags(this->flags()); // copy load commands - uint8_t* p = &buffer[sizeof(macho_header

)]; + __block uint8_t* p = &buffer[sizeof(macho_header

)]; if ( _options.outputKind() == Options::kObjectFile ) p = this->copySingleSegmentLoadCommand(p); @@ -1595,12 +1638,33 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const 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); diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp index 2a79ae2..af66ec7 100644 --- a/src/ld/InputFiles.cpp +++ b/src/ld/InputFiles.cpp @@ -217,10 +217,11 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib // 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 @@ -247,11 +248,19 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib 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; @@ -259,9 +268,10 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } } 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; @@ -313,9 +323,11 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib 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(); @@ -427,7 +439,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } 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); } @@ -659,7 +671,8 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan } } catch (const char* msg) { - warning("Auto-Linking %s", msg); + // only warn about missing auto-linked framework if some missing symbol error happens later + state.missingLinkerOptionFrameworks.insert(frameworkName); } state.linkerOptionFrameworks.insert(frameworkName); } @@ -701,7 +714,8 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan } } catch (const char* msg) { - warning("Auto-Linking %s", msg); + // only warn about missing auto-linked library if some missing symbol error happens later + state.missingLinkerOptionLibraries.insert(libName); } state.linkerOptionLibraries.insert(libName); } @@ -861,9 +875,10 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) 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; } @@ -875,11 +890,11 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) // 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 @@ -1485,8 +1500,16 @@ void InputFiles::dylibs(ld::Internal& state) state.bundleLoader = _bundleLoader; // 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) diff --git a/src/ld/LinkEdit.hpp b/src/ld/LinkEdit.hpp index bde0473..4387ffd 100644 --- a/src/ld/LinkEdit.hpp +++ b/src/ld/LinkEdit.hpp @@ -176,6 +176,8 @@ public: 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) {} @@ -204,8 +206,28 @@ void RebaseInfoAtom::encode() const // sort rebase info by type, then address std::vector& 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 +void RebaseInfoAtom::encodeV1() const +{ + std::vector& info = this->_writer._rebaseInfo; + // convert to temp encoding that can be more easily optimized std::vector mid; uint64_t curSegStart = 0; @@ -388,6 +410,9 @@ public: 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; @@ -411,11 +436,23 @@ ld::Section BindingInfoAtom::_s_section("__LINKEDIT", "__binding", ld::Sectio template void BindingInfoAtom::encode() const +{ + // use encoding based on target minOS + if ( _options.useLinkedListBinding() && !this->_writer._hasUnalignedFixup ) { + encodeV2(); + } else { + encodeV1(); + } +} + + +template +void BindingInfoAtom::encodeV1() const { // sort by library, symbol, type, then address std::vector& info = this->_writer._bindingInfo; std::sort(info.begin(), info.end()); - + // convert to temp encoding that can be more easily optimized std::vector mid; uint64_t curSegStart = 0; @@ -600,6 +637,229 @@ void BindingInfoAtom::encode() const if (log) fprintf(stderr, "total binding info size = %ld\n", this->_encodedData.size()); } +template +void BindingInfoAtom::encodeV2() const +{ + std::vector& bindInfo = this->_writer._bindingInfo; + std::vector& 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 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::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& 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::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 @@ -1135,6 +1395,12 @@ void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Ki 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; @@ -1206,6 +1472,12 @@ void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kin 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; diff --git a/src/ld/LinkEditClassic.hpp b/src/ld/LinkEditClassic.hpp index fd9098f..be25666 100644 --- a/src/ld/LinkEditClassic.hpp +++ b/src/ld/LinkEditClassic.hpp @@ -491,10 +491,7 @@ void SymbolTableAtom::addImport(const ld::Atom* atom, StringPoolAtom* pool) 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 @@ -760,6 +757,9 @@ public: 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: @@ -804,6 +804,9 @@ public: 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) { } @@ -901,6 +904,9 @@ public: 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) { } @@ -1060,6 +1066,9 @@ public: 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); @@ -1079,6 +1088,10 @@ private: 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, @@ -1928,6 +1941,33 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSectio 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"); @@ -1941,6 +1981,9 @@ template void SectionRelocationsAtom::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) { @@ -1954,6 +1997,14 @@ void SectionRelocationsAtom::addSectionReloc(ld::Internal::FinalSection* sect 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; @@ -2072,10 +2123,20 @@ uint32_t IndirectSymbolTableAtom::symIndexOfStubAtom(const ld::Atom* stubAtom template uint32_t IndirectSymbolTableAtom::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"; @@ -2086,9 +2147,18 @@ uint32_t IndirectSymbolTableAtom::symIndexOfNonLazyPointerAtom(const ld::Atom { //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: diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index 6f115d8..09290a9 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -40,6 +40,7 @@ #include #include +#include "ld.hpp" #include "Options.h" #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" @@ -135,20 +136,22 @@ bool Options::FileInfo::checkFileExists(const Options& options, const char *p) 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), @@ -161,7 +164,8 @@ Options::Options(int argc, const char* argv[]) 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), @@ -176,15 +180,14 @@ Options::Options(int argc, const char* argv[]) 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), @@ -194,13 +197,13 @@ Options::Options(int argc, const char* argv[]) 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); @@ -346,7 +349,7 @@ uint32_t Options::maxSegProtection(const char* segName) const { // iPhoneOS always uses same protection for max and initial // simulator apps need to use MacOSX max-prot - if ( (fPlatform != kPlatformOSX) && !fTargetIOSSimulator ) + if ( (platforms().contains(ld::kPlatform_macOS) == 0) && !targetIOSSimulator() ) return initialSegProtection(segName); for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { @@ -587,28 +590,7 @@ const std::vector* Options::sectionOrder(const char* segName) const 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) ) { @@ -617,42 +599,47 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::P 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; } @@ -665,6 +652,35 @@ bool Options::armUsesZeroCostExceptions() const 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 { + // 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 ) @@ -676,6 +692,7 @@ void Options::parseArch(const char* arch) fSubArchitecture = t->cpuSubType; fHasPreferredSubType = t->isSubType; fArchSupportsThumb2 = t->supportsThumb2; + selectFallbackArch(arch); return; } } @@ -826,7 +843,7 @@ static std::string replace_extension(const std::string &path, const std::string 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()) { @@ -968,9 +985,10 @@ bool Options::hasInlinedTAPIFile(const std::string &path) const { return false; } -std::unique_ptr Options::findTAPIFile(const std::string &path) const +tapi::LinkerInterfaceFile* Options::findTAPIFile(const std::string &path) const { - std::unique_ptr 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. @@ -980,20 +998,29 @@ std::unique_ptr Options::findTAPIFile(const std::stri 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. @@ -1003,13 +1030,16 @@ std::unique_ptr Options::findTAPIFile(const std::stri 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 @@ -1065,60 +1095,6 @@ Options::FileInfo Options::findIndirectDylib(const std::string& installName, con 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; @@ -1535,61 +1511,29 @@ Options::Treatment Options::parseTreatment(const char* treatment) 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) @@ -2061,6 +2005,16 @@ bool Options::moveRwSymbol(const char* symName, const char* filePath, const char 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::const_iterator it=fSymbolsMovesCode.begin(); it != fSymbolsMovesCode.end(); ++it) { @@ -2212,7 +2166,7 @@ bool Options::parsePackedVersion32(const std::string& versionStr, uint32_t &resu return false; } - result = (majorValue << 16) | (minorValue << 8) | microValue; + result = (uint32_t)((majorValue << 16) | (minorValue << 8) | microValue); return true; } @@ -2227,39 +2181,9 @@ std::string Options::getSDKVersionStr() const return getVersionString32(fSDKVersion); } -std::string Options::getPlatformStr() const -{ - switch (fPlatform) { - case Options::kPlatformOSX: - return "MacOSX"; - case Options::kPlatformiOS: - if (targetIOSSimulator()) - return "iPhoneSimulator"; - else - return "iPhoneOS"; - case Options::kPlatformWatchOS: - if (targetIOSSimulator()) - return "watchOS Simulator"; - else - return "watchOS"; -#if SUPPORT_APPLE_TV - case Options::kPlatform_tvOS: - if (targetIOSSimulator()) - return "AppleTVSimulator"; - else - return "AppleTVOS"; - break; -#endif - case Options::kPlatform_bridgeOS: - return "bridgeOS"; - case Options::kPlatformUnknown: - return "Unknown"; - } -} - std::vector Options::writeBitcodeLinkOptions() const { - std::vector linkCommand; + __block std::vector linkCommand; switch ( fOutputKind ) { case Options::kDynamicLibrary: linkCommand.push_back("-dylib"); @@ -2291,45 +2215,53 @@ std::vector Options::writeBitcodeLinkOptions() const // 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 @@ -2380,6 +2312,20 @@ std::vector Options::writeBitcodeLinkOptions() const 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. // @@ -2430,7 +2376,9 @@ void Options::parse(int argc, const char* argv[]) 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 @@ -2468,10 +2416,19 @@ void Options::parse(int argc, const char* argv[]) 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; @@ -2498,6 +2455,7 @@ void Options::parse(int argc, const char* argv[]) 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"; @@ -2570,7 +2528,8 @@ void Options::parse(int argc, const char* argv[]) } // 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); @@ -2697,13 +2656,15 @@ void Options::parse(int argc, const char* argv[]) 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; @@ -2734,7 +2695,8 @@ void Options::parse(int argc, const char* argv[]) 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); @@ -2743,7 +2705,8 @@ void Options::parse(int argc, const char* argv[]) 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); @@ -2827,12 +2790,11 @@ void Options::parse(int argc, const char* argv[]) // 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); @@ -2901,22 +2863,23 @@ void Options::parse(int argc, const char* argv[]) } // ??? 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 ) { @@ -3000,9 +2963,7 @@ void Options::parse(int argc, const char* argv[]) } // Use this flag to set default behavior for deployement targets. else if ( strcmp(arg, "-macosx_version_min") == 0 ) { - const char* macVers = argv[++i]; - if ( macVers == NULL ) - throw "-macosx_version_min missing version argument"; + const char* macVers = checkForNullVersionArgument(arg, argv[++i]); const char* envMacVers = getenv("MACOSX_DEPLOYMENT_TARGET"); const char* enviPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); if ( (envMacVers != NULL) && (enviPhoneVers != NULL) ) { @@ -3012,67 +2973,49 @@ void Options::parse(int argc, const char* argv[]) 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); @@ -3307,12 +3250,8 @@ void Options::parse(int argc, const char* argv[]) } 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); } @@ -3331,9 +3270,7 @@ void Options::parse(int argc, const char* argv[]) 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)) { @@ -3348,20 +3285,18 @@ void Options::parse(int argc, const char* argv[]) } 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; @@ -3373,9 +3308,7 @@ void Options::parse(int argc, const char* argv[]) 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 ) { @@ -3385,9 +3318,7 @@ void Options::parse(int argc, const char* argv[]) 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; @@ -3409,7 +3340,8 @@ void Options::parse(int argc, const char* argv[]) 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); @@ -3436,7 +3368,8 @@ void Options::parse(int argc, const char* argv[]) 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); @@ -3474,16 +3407,12 @@ void Options::parse(int argc, const char* argv[]) 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); } @@ -3539,9 +3468,7 @@ void Options::parse(int argc, const char* argv[]) 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; @@ -3565,17 +3492,17 @@ void Options::parse(int argc, const char* argv[]) } 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); } @@ -3586,11 +3513,6 @@ void Options::parse(int argc, const char* argv[]) 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; @@ -3610,9 +3532,7 @@ void Options::parse(int argc, const char* argv[]) 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; @@ -3674,14 +3594,6 @@ void Options::parse(int argc, const char* argv[]) 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 ) @@ -3711,7 +3623,7 @@ void Options::parse(int argc, const char* argv[]) 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() @@ -3928,15 +3840,24 @@ void Options::parse(int argc, const char* argv[]) } 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, ':'); @@ -3977,9 +3898,25 @@ void Options::parse(int argc, const char* argv[]) 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 is used for SDK support. @@ -4033,6 +3970,7 @@ void Options::buildSearchPaths(int argc, const char* argv[]) 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 ) { @@ -4243,9 +4181,6 @@ void Options::parsePreCommandLineEnvironmentSettings() 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 @@ -4288,6 +4223,21 @@ void Options::parsePreCommandLineEnvironmentSettings() // [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)); + } + } + + // ld64 should consider RC_RELEASE when calculating a binary's UUID + fBuildContextName = getenv("RC_RELEASE"); } @@ -4299,15 +4249,6 @@ void Options::parsePostCommandLineEnvironmentSettings() 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 ) { @@ -4374,20 +4315,22 @@ void Options::reconfigureDefaults() } // 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 ) { @@ -4396,26 +4339,26 @@ void Options::reconfigureDefaults() 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 } @@ -4427,29 +4370,23 @@ void Options::reconfigureDefaults() } } - + __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: @@ -4496,17 +4433,12 @@ void Options::reconfigureDefaults() 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; @@ -4516,65 +4448,9 @@ void Options::reconfigureDefaults() // disable implicit dylibs when targeting 10.3 // 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: @@ -4606,77 +4482,10 @@ void Options::reconfigureDefaults() break; } - // -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; } @@ -4685,21 +4494,26 @@ void Options::reconfigureDefaults() 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"); @@ -4720,9 +4534,9 @@ void Options::reconfigureDefaults() } // 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; // only use v2 for Swift dylibs on Mac OS X if ( strncmp(this->installPath(), "/System/Library/PrivateFrameworks/Swift/", 40) == 0 ) @@ -4741,19 +4555,6 @@ void Options::reconfigureDefaults() 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; - } - } - // -r -x implies -S if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) ) fDebugInfoStripping = Options::kDebugInfoNone; @@ -4825,7 +4626,7 @@ void Options::reconfigureDefaults() case Options::kDynamicLibrary: case Options::kDynamicBundle: // Add LC_ENCRYPTION_INFO load command to bundled frameworks - if ( !min_iOS(ld::iOS_7_0) ) + if ( !platforms().minOS(ld::version2013) ) fEncryptable = false; break; } @@ -4864,35 +4665,33 @@ void Options::reconfigureDefaults() 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; } @@ -4924,34 +4723,107 @@ void Options::reconfigureDefaults() } // 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; } - // x86_64 defaults PIE (regardless of minOS version) - if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_6) ) + // 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 @@ -4987,22 +4859,12 @@ void Options::reconfigureDefaults() 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; } @@ -5027,7 +4889,7 @@ void Options::reconfigureDefaults() } // 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 @@ -5054,33 +4916,21 @@ void Options::reconfigureDefaults() // Use LC_MAIN instead of LC_UNIXTHREAD for newer OSs switch ( fOutputKind ) { case Options::kDynamicExecutable: - if ( fEntryPointLoadCommandForceOn ) { + // 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 { - // 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: @@ -5112,7 +4962,7 @@ void Options::reconfigureDefaults() fSourceVersionLoadCommand = false; } else { - if ( minOS(ld::mac10_8, ld::iOS_6_0) ) { + if ( platforms().minOS(ld::version2012) ) { fSourceVersionLoadCommand = true; } else @@ -5141,12 +4991,12 @@ void Options::reconfigureDefaults() 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 }; @@ -5162,18 +5012,15 @@ void Options::reconfigureDefaults() // 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; } } - - // iOS main executables now default to 16KB page size - if ( (fIOSVersionMin != ld::iOSVersionUnset) && (fOutputKind == Options::kDynamicExecutable) ) { - // Only third party apps should have 16KB page segments by default - if ( fEncryptable ) { - if ( fSegmentAlignment == 4096 ) - fSegmentAlignment = 4096*4; - } + + // Only third party apps should have 16KB page segments by default + if ( fEncryptable ) { + if ( fSegmentAlignment == 4096 ) + fSegmentAlignment = 4096*4; } // ARM64 needs 16KB page size for user land code @@ -5184,18 +5031,17 @@ void Options::reconfigureDefaults() case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDyld: - if ( ((fArchitecture == CPU_TYPE_ARM64) - ) - || ((fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_7_0)) ) { + // 16KB segments for arm64 kexts + if ( (fArchitecture == CPU_TYPE_ARM64) + || (fArchitecture == CPU_TYPE_ARM) ) { fSegmentAlignment = 4096*4; } break; case Options::kStaticExecutable: case Options::kKextBundle: // 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; @@ -5220,7 +5066,7 @@ void Options::reconfigureDefaults() fKeepDwarfUnwind = false; } else { - if ( minOS(ld::mac10_9, ld::iOS_7_0) ) + if ( platforms().minOS(ld::version2013) ) fKeepDwarfUnwind = false; else fKeepDwarfUnwind = true; @@ -5257,6 +5103,24 @@ void Options::reconfigureDefaults() } + // Look in $SDKROOT/AppleInternal/AccessibilityLinkerSymbols/.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); + } + } + } + // Automatically use OrderFiles found in the AppleInternal SDK if ( (fFinalName != NULL) && fOrderedSymbols.empty() && !fSDKPaths.empty() ) { char path[PATH_MAX]; @@ -5278,11 +5142,11 @@ void Options::reconfigureDefaults() } // Add warnings for issues likely to cause OS verification issues - if ( fSharedRegionEligible && !fRPaths.empty() ) { + if ( fSharedRegionEligible && !fRPaths.empty() && !fDebugVariant ) { // 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 ) { @@ -5312,15 +5176,20 @@ void Options::reconfigureDefaults() } // 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; @@ -5328,11 +5197,8 @@ void Options::reconfigureDefaults() // 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; @@ -5347,23 +5213,26 @@ void Options::checkIllegalOptionCombinations() 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 @@ -5422,20 +5291,24 @@ void Options::checkIllegalOptionCombinations() // 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; + } + }); + } @@ -5461,7 +5334,7 @@ void Options::checkIllegalOptionCombinations() 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 ) @@ -5485,7 +5358,7 @@ void Options::checkIllegalOptionCombinations() 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 ) { @@ -5603,7 +5476,7 @@ void Options::checkIllegalOptionCombinations() throw "-setuid_safe cannot be used with -r"; // 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 @@ -5753,7 +5626,7 @@ void Options::checkIllegalOptionCombinations() // 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: @@ -5773,11 +5646,8 @@ void Options::checkIllegalOptionCombinations() 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: @@ -5796,12 +5666,15 @@ void Options::checkIllegalOptionCombinations() } } - // 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 @@ -5823,7 +5696,7 @@ void Options::checkIllegalOptionCombinations() 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"; } @@ -5852,21 +5725,80 @@ void Options::checkIllegalOptionCombinations() // warn if building an embedded iOS dylib for pre-iOS 8 // 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"; + } + + // 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); + } + } + + // 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[]) @@ -6006,7 +5938,7 @@ const char* Options::demangleSymbol(const char* sym) const #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; diff --git a/src/ld/Options.h b/src/ld/Options.h index 3b0a37c..62bcddf 100644 --- a/src/ld/Options.h +++ b/src/ld/Options.h @@ -91,46 +91,34 @@ public: 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)"; } } @@ -209,13 +197,13 @@ public: 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; }; @@ -301,15 +289,16 @@ public: 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 @@ -321,7 +310,6 @@ public: 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 @@ -338,12 +326,7 @@ public: 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; } @@ -372,7 +355,7 @@ public: FileInfo findFile(const std::string &path, const ld::dylib::File* fromDylib=nullptr) const; bool findFile(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const; bool hasInlinedTAPIFile(const std::string &path) const; - std::unique_ptr 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; } @@ -386,7 +369,6 @@ public: 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; @@ -422,6 +404,7 @@ public: const std::vector& dyldEnvironExtras() const{ return fDyldEnvironExtras; } const std::vector& 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; } @@ -454,18 +437,21 @@ public: 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; } @@ -483,6 +469,13 @@ public: 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; } @@ -514,8 +507,8 @@ public: 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; @@ -524,11 +517,11 @@ public: const std::vector& 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& sdkPaths() const { return fSDKPaths; } std::vector 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(); } @@ -537,7 +530,8 @@ public: bool zeroModTimeInDebugMap() const { return fZeroModTimeInDebugMap; } void writeDependencyInfo() const; std::vector &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*); @@ -579,10 +573,13 @@ private: 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; @@ -601,9 +598,7 @@ private: 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); @@ -627,14 +622,14 @@ private: std::vector 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; @@ -649,7 +644,6 @@ private: uint64_t fBaseAddress; uint64_t fMaxAddress; uint64_t fBaseWritableAddress; - bool fSplitSegs; SetWithWildcards fExportSymbols; SetWithWildcards fDontExportSymbols; SetWithWildcards fInterposeList; @@ -675,16 +669,19 @@ private: 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; @@ -728,6 +725,7 @@ private: bool fMarkDeadStrippableDylib; bool fMakeCompressedDyldInfo; bool fMakeCompressedDyldInfoForceOff; + bool fMakeThreadedStartsSection; bool fNoEHLabels; bool fAllowCpuSubtypeMismatches; bool fEnforceDylibSubtypesMatch; @@ -771,7 +769,6 @@ private: bool fVersionLoadCommand; bool fVersionLoadCommandForcedOn; bool fVersionLoadCommandForcedOff; - bool fBuildVersionLoadCommand; bool fFunctionStartsLoadCommand; bool fFunctionStartsForcedOn; bool fFunctionStartsForcedOff; @@ -783,12 +780,9 @@ private: bool fPageAlignDataAtoms; bool fNeedsThreadLoadCommand; bool fEntryPointLoadCommand; - bool fEntryPointLoadCommandForceOn; - bool fEntryPointLoadCommandForceOff; bool fSourceVersionLoadCommand; bool fSourceVersionLoadCommandForceOn; bool fSourceVersionLoadCommandForceOff; - bool fTargetIOSSimulator; bool fExportDynamic; bool fAbsoluteSymbols; bool fAllowSimulatorToLinkWithMacOSX; @@ -814,6 +808,13 @@ private: 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; @@ -823,12 +824,9 @@ private: 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 fAliases; std::vector fInitialUndefines; NameSet fAllowedUndefined; @@ -853,11 +851,13 @@ private: std::vector fSegmentRenames; std::vector fSymbolsMovesData; std::vector fSymbolsMovesCode; + std::vector 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; diff --git a/src/ld/OutputFile.cpp b/src/ld/OutputFile.cpp index e5059d5..4daa510 100644 --- a/src/ld/OutputFile.cpp +++ b/src/ld/OutputFile.cpp @@ -88,17 +88,19 @@ OutputFile::OutputFile(const Options& opts) 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), @@ -283,7 +285,7 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _sectionsRelocationsAtom->encode(); } - if ( ! _options.makeCompressedDyldInfo() ) { + if ( !_options.makeCompressedDyldInfo() && !_options.makeThreadedStartsSection() ) { // build external relocations assert(_externalRelocsAtom != NULL); _externalRelocsAtom->encode(); @@ -367,7 +369,7 @@ void OutputFile::setLoadCommandsPadding(ld::Internal& state) // 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::reverse_iterator it = state.sections.rbegin(); it != state.sections.rend(); ++it) { ld::Internal::FinalSection* sect = *it; @@ -476,7 +478,8 @@ bool OutputFile::targetIsThumb(ld::Internal& state, const ld::Fixup* fixup) 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. @@ -1345,6 +1348,9 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: bool is_b; bool thumbTarget = false; std::map 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; @@ -1634,6 +1640,68 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: 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 ) @@ -1981,6 +2049,10 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: } 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); @@ -2640,6 +2712,262 @@ void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer) //fprintf(stderr, "ADRPs changed to NOPs: %d\n", sAdrpNoped); //fprintf(stderr, "ADRPs unchanged: %d\n", sAdrpNotNoped); } + + if ( _options.makeThreadedStartsSection() ) { + assert(_threadedRebaseBindIndices.empty()); + + std::vector& bindInfo = _bindingInfo; + std::vector& rebaseInfo = _rebaseInfo; + + std::vector& 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 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) @@ -2714,6 +3042,11 @@ void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) if ( lastSlash != NULL ) { CC_MD5_Update(&md5state, lastSlash, strlen(lastSlash)); } + // 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 ) { @@ -3058,7 +3391,14 @@ void OutputFile::buildSymbolTable(ld::Internal& state) case ld::Atom::scopeLinkageUnit: if ( _options.outputKind() == Options::kObjectFile ) { if ( _options.keepPrivateExterns() ) { - _exportedAtoms.push_back(atom); + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel ) { + // ld -r should not promote static 'l' labels to hidden + (const_cast(atom))->setScope(ld::Atom::scopeTranslationUnit); + _localAtoms.push_back(atom); + } + else { + _exportedAtoms.push_back(atom); + } } else if ( _options.keepLocalSymbol(atom->name()) ) { _localAtoms.push_back(atom); @@ -3073,7 +3413,7 @@ void OutputFile::buildSymbolTable(ld::Internal& state) _localAtoms.push_back(atom); // 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(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn); @@ -3180,6 +3520,10 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); externalRelocationsSection = state.addAtom(*_externalRelocsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } if ( _hasSymbolTable ) { _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); @@ -3200,6 +3544,10 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); externalRelocationsSection = state.addAtom(*_externalRelocsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } if ( _hasSymbolTable ) { _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); @@ -3220,6 +3568,10 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); externalRelocationsSection = state.addAtom(*_externalRelocsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } if ( _hasSymbolTable ) { _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); @@ -3240,6 +3592,10 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); externalRelocationsSection = state.addAtom(*_externalRelocsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } if ( _hasSymbolTable ) { _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); @@ -3680,66 +4036,9 @@ int OutputFile::compressedOrdinalForAtom(const ld::Atom* target) } -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) @@ -3757,6 +4056,9 @@ 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; @@ -3765,52 +4067,9 @@ bool OutputFile::isStore(ld::Fixup::Kind kind) } -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) @@ -3819,6 +4078,9 @@ 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: @@ -3900,7 +4162,10 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) 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() ) @@ -3915,6 +4180,9 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) 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; @@ -3925,7 +4193,7 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) targetAddend = 0; minusTargetAddend = 0; } - if ( this->setsTarget(fit->kind) ) { + if ( this->setsTarget(*fit) ) { switch ( fit->binding ) { case ld::Fixup::bindingNone: case ld::Fixup::bindingByNameUnbound: @@ -3976,22 +4244,33 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) 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, @@ -4022,7 +4301,7 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) 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 @@ -4063,7 +4342,7 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s 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 @@ -4242,7 +4521,21 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s } } } - } + } + + // 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 ) { @@ -4288,9 +4581,11 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s // do nothing break; } + _hasUnalignedFixup = true; } _rebaseInfo.push_back(RebaseInfo(rebaseType, address)); } + if ( needsBinding ) { if ( inReadOnlySeg ) { noteTextReloc(atom, target); @@ -4310,19 +4605,124 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s // 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; + } + // 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, @@ -4350,7 +4750,7 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio } // 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; @@ -4400,6 +4800,9 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio 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 @@ -4490,6 +4893,24 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio 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; } @@ -4561,7 +4982,10 @@ bool OutputFile::useSectionRelocAddend(ld::Fixup* fixupWithTarget) 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) { @@ -4611,7 +5035,7 @@ void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSectio fixupWithTarget->contentAddendOnly = true; fixupWithStore->contentAddendOnly = true; } - else if ( isPcRelStore(fixupWithStore->kind) ) { + else if ( isPcRelStore(fixupWithStore) ) { fixupWithTarget->contentDetlaToAddendOnly = true; fixupWithStore->contentDetlaToAddendOnly = true; } @@ -4625,6 +5049,9 @@ void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSectio if ( fixupWithStore != NULL ) { _sectionsRelocationsAtom->addSectionReloc(sect, fixupWithStore->kind, atom, fixupWithStore->offsetInAtom, targetUsesExternalReloc, minusTargetUsesExternalReloc, +#if SUPPORT_ARCH_arm64e + fixupWithAuthData, +#endif target, targetAddend, minusTarget, minusTargetAddend); } @@ -4651,7 +5078,7 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) 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 ) @@ -4726,6 +5153,13 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) 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; } @@ -4770,7 +5204,7 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state) 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 ) @@ -4812,9 +5246,19 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state) 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: diff --git a/src/ld/OutputFile.h b/src/ld/OutputFile.h index 81f2cfb..d4024eb 100644 --- a/src/ld/OutputFile.h +++ b/src/ld/OutputFile.h @@ -89,6 +89,7 @@ public: 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) {} @@ -105,13 +106,15 @@ public: 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; @@ -164,6 +167,9 @@ private: 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, @@ -171,6 +177,11 @@ private: 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, @@ -195,10 +206,10 @@ private: 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); @@ -279,6 +290,7 @@ private: std::vector _dylibsToLoad; std::vector _dylibOrdinalPaths; const bool _hasDyldInfo; + const bool _hasThreadedPageStarts; const bool _hasSymbolTable; const bool _hasSectionRelocations; const bool _hasSplitSegInfo; @@ -307,6 +319,12 @@ public: std::vector _bindingInfo; std::vector _lazyBindingInfo; std::vector _weakBindingInfo; + bool _hasUnalignedFixup = false; + // Note, <= 0 values are indices in to rebases, > 0 are binds. + std::vector _threadedRebaseBindIndices; +#if SUPPORT_ARCH_arm64e + std::map> _authenticatedFixupData; +#endif std::vector _splitSegInfos; std::vector _splitSegV2Infos; class HeaderAndLoadCommandsAbtract* _headersAndLoadCommandAtom; diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp index 51afe8d..17e00cb 100644 --- a/src/ld/Resolver.cpp +++ b/src/ld/Resolver.cpp @@ -179,7 +179,7 @@ private: 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); @@ -189,8 +189,12 @@ SectionBoundaryAtom* SectionBoundaryAtom::makeSectionBoundaryAtom(const char* na 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)); } @@ -279,15 +283,7 @@ SegmentBoundaryAtom* SegmentBoundaryAtom::makeOldSegmentBoundaryAtom(const char* 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 ) { @@ -364,6 +360,10 @@ void Resolver::doFile(const ld::File& file) _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 ) { @@ -372,27 +372,31 @@ void Resolver::doFile(const ld::File& file) _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 @@ -412,47 +416,9 @@ void Resolver::doFile(const ld::File& file) } } - // 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(); } @@ -494,17 +460,18 @@ void Resolver::doFile(const ld::File& file) 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& entry : objFile->toolVersions()) { uint64_t combined = (uint64_t)entry.first << 32 | entry.second; @@ -586,27 +553,31 @@ void Resolver::doFile(const ld::File& file) 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 @@ -617,42 +588,21 @@ void Resolver::doFile(const ld::File& file) 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; + } } // verify dylibs use same version of Swift language @@ -692,9 +642,9 @@ void Resolver::doFile(const ld::File& file) const char* depInstallName = dylibFile->installPath(); // 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) ) { // 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); } } @@ -709,7 +659,6 @@ void Resolver::doFile(const ld::File& file) } } } - } void Resolver::doAtom(const ld::Atom& atom) @@ -1076,6 +1025,9 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) 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: @@ -1507,6 +1459,10 @@ void Resolver::checkUndefines(bool force) 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 @@ -1663,6 +1619,7 @@ void Resolver::fillInHelpersInInternalState() } _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") ) { @@ -1683,6 +1640,7 @@ void Resolver::fillInHelpersInInternalState() } _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") ) { @@ -1713,6 +1671,11 @@ void Resolver::fillInInternalState() // 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() @@ -1782,6 +1745,7 @@ void Resolver::linkTimeOptimize() 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(); @@ -1802,12 +1766,14 @@ void Resolver::linkTimeOptimize() 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(); diff --git a/src/ld/Snapshot.cpp b/src/ld/Snapshot.cpp index 27ce370..a8060f5 100644 --- a/src/ld/Snapshot.cpp +++ b/src/ld/Snapshot.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -29,18 +30,19 @@ 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"; @@ -64,6 +66,11 @@ void Snapshot::setSnapshotPath(const char *path) void Snapshot::setSnapshotMode(SnapshotMode mode) { + if (fRootDir == NULL) { + // free stuff + fRootDir = NULL; + } + if (fRootDir == NULL) { fRecordArgs = false; fRecordObjects = false; @@ -71,29 +78,52 @@ void Snapshot::setSnapshotMode(SnapshotMode mode) 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); + } + } } } @@ -111,7 +141,8 @@ void Snapshot::buildPath(char *buf, const char *subdir, const char *file) 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) @@ -127,7 +158,7 @@ void Snapshot::buildUniquePath(char *buf, const char *subdir, const char *file) { 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); @@ -139,6 +170,13 @@ void Snapshot::buildUniquePath(char *buf, const char *subdir, const char *file) } } +const char * Snapshot::subdir(const char *subdir) +{ + if (fRecordKext) { + return filesString; + } + return subdir; +} // Copy a file to the snapshot. // sourcePath is the original file @@ -149,6 +187,23 @@ void Snapshot::copyFileToSnapshot(const char *sourcePath, const char *subdir, ch { 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); @@ -156,7 +211,8 @@ void Snapshot::copyFileToSnapshot(const char *sourcePath, const char *subdir, ch 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) { @@ -167,34 +223,42 @@ void Snapshot::copyFileToSnapshot(const char *sourcePath, const char *subdir, ch } 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++) { @@ -205,50 +269,76 @@ void Snapshot::createSnapshot() 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, "\""); @@ -266,7 +356,9 @@ void Snapshot::recordRawArgs(int argc, const char *argv[]) for (int i=0; iaddSnapshotLinkArg(argIndex, argCount, fileArg); })); } else { char buf[PATH_MAX]; - const char *subdir = dataFilesString; + fArgIndicies.push_back(fArgs.size()); for (int i=0, arg=argIndex; ipath(); + 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(dylibPath, dylibFd)); char *base_name = strdup(basename(path_buf)); if (isFramework) { @@ -418,7 +525,7 @@ void Snapshot::recordDylibSymbol(ld::dylib::File* dylibFile, const char *name) } else { addDylibArg(base_name); } - writeCommandLine(fArgs); + writeCommandLine(); } else { dylibFd = it->second; } @@ -458,6 +565,7 @@ void Snapshot::recordArchive(const char *archiveFile) 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; @@ -475,9 +583,15 @@ void Snapshot::recordArchive(const char *archiveFile) 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(); + } } } } @@ -492,7 +606,7 @@ void Snapshot::recordSubUmbrella(const char *frameworkPath) 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); @@ -510,7 +624,7 @@ void Snapshot::recordSubLibrary(const char *dylibPath) 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)); } } @@ -529,7 +643,8 @@ void Snapshot::recordAssertionMessage(const char *fmt, ...) } 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); diff --git a/src/ld/Snapshot.h b/src/ld/Snapshot.h index 8cfd17e..800852f 100644 --- a/src/ld/Snapshot.h +++ b/src/ld/Snapshot.h @@ -26,9 +26,10 @@ public: 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 @@ -36,7 +37,7 @@ public: // 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(). @@ -96,12 +97,18 @@ private: typedef std::vector StringVector; typedef std::map DylibMap; typedef std::map PathMap; - + typedef std::vector 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 @@ -128,6 +135,7 @@ private: 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 @@ -136,14 +144,18 @@ private: 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 diff --git a/src/ld/ld.cpp b/src/ld/ld.cpp index e0830ab..87c58a5 100644 --- a/src/ld/ld.cpp +++ b/src/ld/ld.cpp @@ -76,6 +76,7 @@ extern "C" double log2 ( double ); #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" @@ -88,6 +89,7 @@ extern "C" double log2 ( double ); #include "parsers/lto_file.h" #include "parsers/opaque_section_file.h" +const ld::VersionSet ld::File::_platforms; struct PerformanceStatistics { uint64_t startTool; @@ -125,7 +127,6 @@ private: { 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: @@ -363,6 +364,8 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint 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: @@ -435,6 +438,8 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint 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; } @@ -663,6 +668,7 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) 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); @@ -688,6 +694,26 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) } } } + 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) ) { @@ -820,22 +846,19 @@ ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& in } -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::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::iterator it = sections.begin(); it != sections.end(); ++it) { // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName()); @@ -1349,6 +1372,10 @@ int main(int argc, const char* argv[]) 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(); diff --git a/src/ld/ld.hpp b/src/ld/ld.hpp index 71e5a21..1794141 100644 --- a/src/ld/ld.hpp +++ b/src/ld/ld.hpp @@ -41,6 +41,195 @@ 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 PlatformSet; + +// +// minumum OS versions +// + +typedef std::pair Version; + +struct VersionSet { +private: + std::map _versions; +public: + VersionSet() {} + VersionSet(const std::map& 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; @@ -58,10 +247,7 @@ class Bitcode; class File { public: - enum ObjcConstraint { objcConstraintNone, objcConstraintRetainRelease, - objcConstraintRetainReleaseOrGC, objcConstraintGC, - objcConstraintRetainReleaseForSimulator }; - + class AtomHandler { public: virtual ~AtomHandler() {} @@ -151,13 +337,10 @@ public: 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; } @@ -166,22 +349,10 @@ private: 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 @@ -226,6 +397,8 @@ namespace relocatable { 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; } @@ -343,7 +516,7 @@ public: 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 }; @@ -489,14 +662,38 @@ struct Fixup 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; @@ -553,6 +750,14 @@ struct Fixup 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), @@ -596,6 +801,146 @@ struct Fixup } 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; @@ -898,12 +1243,12 @@ public: 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), @@ -919,6 +1264,8 @@ public: CStringSet unprocessedLinkerOptionFrameworks; CStringSet linkerOptionLibraries; CStringSet linkerOptionFrameworks; + CStringSet missingLinkerOptionLibraries; + CStringSet missingLinkerOptionFrameworks; std::vector indirectBindingTable; std::vector filesWithBitcode; std::vector filesFromCompilerRT; @@ -930,16 +1277,17 @@ public: 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; diff --git a/src/ld/parsers/generic_dylib_file.hpp b/src/ld/parsers/generic_dylib_file.hpp index 0b781be..baf09f9 100644 --- a/src/ld/parsers/generic_dylib_file.hpp +++ b/src/ld/parsers/generic_dylib_file.hpp @@ -123,17 +123,14 @@ template 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(); } @@ -207,11 +204,7 @@ protected: std::vector _rpaths; const char* _parentUmbrella; std::unique_ptr _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; @@ -223,7 +216,7 @@ protected: bool _deadStrippable; bool _hasPublicInstallName; bool _appExtensionSafe; - const bool _allowWeakImports; + const bool _allowWeakImports; const bool _allowSimToMacOSXLinking; const bool _addVersionLoadCommand; @@ -234,8 +227,8 @@ template bool File::_s_logHashtable = false; template -File::File(const char* path, time_t mTime, ld::File::Ordinal ord, Options::Platform platform, - uint32_t linkMinOSVersion, bool allowWeakImports, bool linkingFlatNamespace, +File::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), @@ -245,11 +238,7 @@ File::File(const char* path, time_t mTime, ld::File::Ordinal ord, Options::P _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), @@ -261,7 +250,7 @@ File::File(const char* path, time_t mTime, ld::File::Ordinal ord, Options::P _deadStrippable(false), _hasPublicInstallName(false), _appExtensionSafe(false), - _allowWeakImports(allowWeakImports), + _allowWeakImports(allowWeakImports), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers) { diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp index 811b6b3..6282e6b 100644 --- a/src/ld/parsers/lto_file.cpp +++ b/src/ld/parsers/lto_file.cpp @@ -371,8 +371,10 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons 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; @@ -1059,7 +1061,8 @@ thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector& } } 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); } @@ -1129,7 +1132,10 @@ thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector& } } 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; } } @@ -1179,6 +1185,13 @@ thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector& } else { if ( logMustPreserve ) fprintf(stderr, "NOT preserving(%s)\n", name); } + + // 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 ) { // // ld -r mode but merging in some mach-o files, so need to keep libLTO from optimizing away anything @@ -1242,6 +1255,9 @@ bool Parser::optimizeThinLTO(const std::vector& files, // 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); @@ -1465,6 +1481,14 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) if (log) fprintf(stderr, "demote %s to hidden after LTO\n", name); (const_cast(&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(&machoAtom))->setAutoHide(); + } pos->second->setCompiledAtom(machoAtom); _lastProxiedAtom = &machoAtom; _lastProxiedFile = pos->second->file(); @@ -1513,7 +1537,7 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) if ( (_lastProxiedAtom != NULL) && (_lastProxiedAtom->section() == machoAtom.section()) ) { ld::Atom* ma = const_cast(&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()); } diff --git a/src/ld/parsers/lto_file.h b/src/ld/parsers/lto_file.h index a086548..ad22492 100644 --- a/src/ld/parsers/lto_file.h +++ b/src/ld/parsers/lto_file.h @@ -55,6 +55,7 @@ struct OptimizeOptions { int ltoPruneInterval; int ltoPruneAfter; unsigned ltoMaxCacheSize; + bool ltoPruneIntervalOverwrite; bool preserveAllGlobals; bool verbose; bool saveTemps; @@ -72,12 +73,14 @@ struct OptimizeOptions { 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* llvmOptions; const std::vector* initialUndefines; }; diff --git a/src/ld/parsers/macho_dylib_file.cpp b/src/ld/parsers/macho_dylib_file.cpp index b740c55..e4f7b95 100644 --- a/src/ld/parsers/macho_dylib_file.cpp +++ b/src/ld/parsers/macho_dylib_file.cpp @@ -61,11 +61,12 @@ public: 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; @@ -81,6 +82,7 @@ private: 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; @@ -96,13 +98,17 @@ template <> const char* File::objCInfoSectionName() { return "__objc_ima template <> const char* File::objCInfoSectionName() { return "__objc_imageinfo"; } template const char* File::objCInfoSectionName() { return "__image_info"; } +template <> bool File::useSimulatorVariant() { return true; } +template <> bool File::useSimulatorVariant() { return true; } +template bool File::useSimulatorVariant() { return false; } + template File::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

* header = (const macho_header

*)fileContent; @@ -138,7 +144,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, const char* strings = nullptr; bool compressedLinkEdit = false; uint32_t dependentLibCount = 0; - Options::Platform lcPlatform = Options::kPlatformUnknown; + ld::VersionSet lcPlatforms; const macho_load_command

* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { macho_dylib_command

* dylibID; @@ -193,19 +199,13 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, 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

*)cmd)->version(); - this->_platformInDylib = cmd->cmd(); - lcPlatform = Options::platformForLoadCommand(this->_platformInDylib); + lcPlatforms.add({Options::platformForLoadCommand(cmd->cmd(), useSimulatorVariant()), ((macho_version_min_command

*)cmd)->version()}); break; case LC_BUILD_VERSION: { const macho_build_version_command

* buildVersCmd = (macho_build_version_command

*)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: @@ -229,14 +229,6 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, 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 ) { @@ -262,96 +254,35 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, } // arm/arm64 objects are default to ios platform if not set. // rdar://problem/21746314 - if (lcPlatform == Options::kPlatformUnknown && + if (lcPlatforms.empty() && (std::is_same::value || std::is_same::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 @@ -539,6 +470,17 @@ void File::buildExportHashTableFromExportInfo(const macho_dyld_info_command

void File::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 // need way for ld and dyld to see different exported symbols in a dylib if ( strncmp(name, "$ld$", 4) == 0 ) { @@ -547,7 +489,7 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address 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 ) { @@ -630,7 +572,7 @@ public: { return new File(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()); @@ -848,7 +790,7 @@ const char* archName(const uint8_t* fileContent) return Parser::fileKind(fileContent); } #if SUPPORT_ARCH_arm64 - if ( Parser::validFile(fileContent, false) ) { + if ( Parser::validFile(fileContent, true) ) { return Parser::fileKind(fileContent); } #endif @@ -856,43 +798,64 @@ const char* archName(const uint8_t* fileContent) } -// -// 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::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, subArchitecture) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); - break; + break; #endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: - if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, subArchitecture) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); - break; + break; #endif #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: - if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, subArchitecture) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); - break; + break; #endif #if SUPPORT_ARCH_arm64 case CPU_TYPE_ARM64: - if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, subArchitecture) ) return Parser::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 diff --git a/src/ld/parsers/macho_relocatable_file.cpp b/src/ld/parsers/macho_relocatable_file.cpp index ae3be5f..00d5feb 100644 --- a/src/ld/parsers/macho_relocatable_file.cpp +++ b/src/ld/parsers/macho_relocatable_file.cpp @@ -80,11 +80,10 @@ public: _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), @@ -95,11 +94,10 @@ public: 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; } @@ -144,11 +142,11 @@ private: const macho_section

* _dwarfDebugAbbrevSect; const macho_section

* _dwarfDebugLineSect; const macho_section

* _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; @@ -995,7 +993,7 @@ public: 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* header); + static ld::Platform findPlatform(const macho_header* 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 &syms); @@ -1026,6 +1024,9 @@ public: 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 { @@ -1044,6 +1045,11 @@ public: 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(); } @@ -1071,6 +1077,12 @@ public: _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)); } @@ -1210,7 +1222,8 @@ private: 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[]); @@ -1284,6 +1297,9 @@ private: const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; std::vector _allFixups; +#if SUPPORT_ARCH_arm64e + bool _supportsAuthenticatedPointers; +#endif }; @@ -1371,6 +1387,8 @@ bool Parser::validFile(const uint8_t* fileContent, bool subtypeMustMatch, return false; if ( header->filetype() != MH_OBJECT ) return false; + if ( subtypeMustMatch && (header->cpusubtype() != (uint32_t)subtype) ) + return false; return true; } @@ -1562,16 +1580,27 @@ template bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, const Section& sect, uint32_t sectNum, pint_t startAddr, pint_t endAddr, pint_t* addr, pint_t* size, const macho_nlist

** 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

& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); - if ( ! sect.ignoreLabel(parser.nameFromSymbol(sym)) ) { - pint_t nextSymbolAddr = sym.n_value(); + const macho_nlist

* 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

* 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; @@ -1621,7 +1650,7 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, const Section< 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 @@ -1749,6 +1778,10 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) _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); @@ -1757,7 +1790,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) _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 @@ -1999,9 +2032,13 @@ template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } +template <> bool Parser::useSimulatorVariant() { return true; } +template <> bool Parser::useSimulatorVariant() { return true; } +template bool Parser::useSimulatorVariant() { return false; } + template -bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOSVersion, bool simulator, bool ignoreMismatchPlatform) +bool Parser::parseLoadCommands(ld::VersionSet platforms, bool simulator, bool ignoreMismatchPlatform) { const macho_header

* header = (const macho_header

*)_fileContent; @@ -2015,7 +2052,7 @@ bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS // 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

* const cmds = (macho_load_command

*)((char*)header + sizeof(macho_header

)); const macho_load_command

* const cmdsEnd = (macho_load_command

*)((char*)header + sizeof(macho_header

) + header->sizeofcmds()); const macho_load_command

* cmd = cmds; @@ -2097,23 +2134,19 @@ bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS 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

*)cmd)->version(); + lcPlatforms.add({Options::platformForLoadCommand(cmd->cmd(), useSimulatorVariant()), ((macho_version_min_command

*)cmd)->version()}); + _file->_platforms.add({Options::platformForLoadCommand(cmd->cmd(), useSimulatorVariant()), ((macho_version_min_command

*)cmd)->version()}); break; case LC_BUILD_VERSION: { const macho_build_version_command

* buildVersCmd = (macho_build_version_command

*)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

* entry = (macho_build_tool_version

*)((uint8_t*)cmd + sizeof(macho_build_version_command

)); for (uint32_t t=0; t < buildVersCmd->ntools(); ++t) { _file->_toolVersions.push_back(std::make_pair(entry->tool(), entry->version())); @@ -2134,80 +2167,63 @@ bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS 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::value || std::is_same::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 +// 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

*)((char*)segment + sizeof(macho_segment_command

)); _machOSectionsCount = segment->nsects(); if ( (sizeof(macho_segment_command

) + _machOSectionsCount * sizeof(macho_section

)) > segment->cmdsize() ) throw "too many sections for size of LC_SEGMENT command"; + return true; } template -Options::Platform Parser::findPlatform(const macho_header

* header) +ld::Platform Parser::findPlatform(const macho_header

* 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

* const cmds = (macho_load_command

*)((char*)header + sizeof(macho_header

)); const macho_load_command

* const cmdsEnd = (macho_load_command

*)((char*)header + sizeof(macho_header

) + header->sizeofcmds()); const macho_load_command

* cmd = cmds; @@ -2218,25 +2234,37 @@ Options::Platform Parser::findPlatform(const macho_header

* header) 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

* versCmd = (macho_version_min_command

*)cmd; + const macho_build_version_command

* buildVersCmd = (macho_build_version_command

*)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

*)cmd)->platform(); + *minOsVers = buildVersCmd->minos(); + return (ld::Platform)buildVersCmd->platform(); } cmd = (const macho_load_command

*)(((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; } @@ -2578,14 +2606,7 @@ void Parser::makeSections() 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 ) { @@ -3055,6 +3076,13 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co 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; @@ -3114,6 +3142,12 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co 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; @@ -3122,6 +3156,19 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co } } +#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); @@ -3146,12 +3193,24 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co 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); + } } } @@ -5470,7 +5529,7 @@ uint32_t ImplicitSizeSection::appendAtoms(class Parser& parser, uint8_t* p } 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(*this, parser, *foundLabel, labeledAtomSize); } if ( !skip ) { @@ -6308,6 +6367,8 @@ bool Section::addRelocFixup(class Parser& parser, const macho_re Parser::TargetDesc target; Parser::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; @@ -7524,6 +7585,52 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relo 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()); } @@ -7930,34 +8037,34 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserO // // 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::validFile(fileContent) ) { *result = CPU_TYPE_X86_64; const macho_header >* header = (const macho_header >*)fileContent; *subResult = header->cpusubtype(); - *platform = Parser::findPlatform(header); + *platform = Parser::findPlatform(header, minOsVers); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent) ) { const macho_header >* header = (const macho_header >*)fileContent; *result = CPU_TYPE_I386; *subResult = CPU_SUBTYPE_X86_ALL; - *platform = Parser::findPlatform(header); + *platform = Parser::findPlatform(header, minOsVers); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { const macho_header >* header = (const macho_header >*)fileContent; *result = CPU_TYPE_ARM; *subResult = header->cpusubtype(); - *platform = Parser::findPlatform(header); + *platform = Parser::findPlatform(header, minOsVers); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { const macho_header >* header = (const macho_header >*)fileContent; *result = CPU_TYPE_ARM64; *subResult = header->cpusubtype(); - *platform = Parser::findPlatform(header); + *platform = Parser::findPlatform(header, minOsVers); return true; } return false; @@ -8027,9 +8134,11 @@ bool getNonLocalSymbols(const uint8_t* fileContent, std::vector &sy else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { return mach_o::relocatable::Parser::getNonLocalSymbols(fileContent, syms); } +#if SUPPORT_ARCH_arm64 else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { return mach_o::relocatable::Parser::getNonLocalSymbols(fileContent, syms); } +#endif return false; } diff --git a/src/ld/parsers/macho_relocatable_file.h b/src/ld/parsers/macho_relocatable_file.h index e31f7f9..c8e98cf 100644 --- a/src/ld/parsers/macho_relocatable_file.h +++ b/src/ld/parsers/macho_relocatable_file.h @@ -43,9 +43,11 @@ struct ParserOptions { 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; @@ -58,7 +60,7 @@ extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLen 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); diff --git a/src/ld/parsers/textstub_dylib_file.cpp b/src/ld/parsers/textstub_dylib_file.cpp index 27c4560..e15e2e9 100644 --- a/src/ld/parsers/textstub_dylib_file.cpp +++ b/src/ld/parsers/textstub_dylib_file.cpp @@ -53,82 +53,107 @@ public: 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 &&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 _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::useSimulatorVariant() { return true; } +template <> bool File::useSimulatorVariant() { return true; } +template bool File::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 - File::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 +File::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 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; + }); // Support $ld$weak symbols in .tbd files #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1)) @@ -141,9 +166,9 @@ static Options::Platform mapPlatform(tapi::Platform platform) { 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)) @@ -155,9 +180,9 @@ static Options::Platform mapPlatform(tapi::Platform platform) { 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) @@ -165,9 +190,9 @@ static Options::Platform mapPlatform(tapi::Platform platform) { 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 @@ -181,29 +206,31 @@ static Options::Platform mapPlatform(tapi::Platform platform) { 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 - File::File(std::unique_ptr &&file, const char* path, const Options *opts, + File::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 -void File::init(tapi::LinkerInterfaceFile *file, const Options *opts, bool buildingForSimulator, +void File::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(new ld::Bitcode(nullptr, 0)); this->_noRexports = !file->hasReexportedLibraries(); @@ -213,7 +240,6 @@ void File::init(tapi::LinkerInterfaceFile *file, const Options *opts, bool bu 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(); @@ -237,21 +263,35 @@ void File::init(tapi::LinkerInterfaceFile *file, const Options *opts, bool bu 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()); @@ -297,7 +337,7 @@ void File::buildExportHashTable(const tapi::LinkerInterfaceFile* file) { template void File::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); } @@ -310,107 +350,143 @@ public: 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(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 &&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(std::move(file), path, &opts, mTime, ordinal, + return new File(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::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); + return Parser::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::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); + return Parser::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::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); + return Parser::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::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); + return Parser::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 &&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::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib); + return Parser::parse(path, file, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture); #endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: - return Parser::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib); + return Parser::parse(path, file, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture); #endif #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: - return Parser::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib); + return Parser::parse(path, file, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture); #endif #if SUPPORT_ARCH_arm64 case CPU_TYPE_ARM64: - return Parser::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib); + return Parser::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 diff --git a/src/ld/parsers/textstub_dylib_file.hpp b/src/ld/parsers/textstub_dylib_file.hpp index 9931908..609c08e 100644 --- a/src/ld/parsers/textstub_dylib_file.hpp +++ b/src/ld/parsers/textstub_dylib_file.hpp @@ -36,7 +36,7 @@ extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, c 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 &&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 diff --git a/src/ld/passes/bitcode_bundle.cpp b/src/ld/passes/bitcode_bundle.cpp index 7165051..19e8b41 100644 --- a/src/ld/passes/bitcode_bundle.cpp +++ b/src/ld/passes/bitcode_bundle.cpp @@ -986,7 +986,7 @@ void BitcodeBundle::doPass() // 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"); diff --git a/src/ld/passes/got.cpp b/src/ld/passes/got.cpp index 01c2e30..51b0347 100644 --- a/src/ld/passes/got.cpp +++ b/src/ld/passes/got.cpp @@ -74,10 +74,48 @@ private: 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 @@ -136,11 +174,23 @@ static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom 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; @@ -157,6 +207,17 @@ struct AtomByNameSorter } }; +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; @@ -166,7 +227,7 @@ void doPass(const Options& opts, ld::Internal& internal) return; // pre-fill gotMap with existing non-lazy pointers - std::map gotMap; + std::map gotMap; for (ld::Internal::FinalSection* sect : internal.sections) { if ( sect->type() != ld::Section::typeNonLazyPointer ) continue; @@ -194,7 +255,7 @@ void doPass(const Options& opts, ld::Internal& internal) } if ( target != NULL ) { if (log) fprintf(stderr, "found existing got entry to %s\n", target->name()); - gotMap[target] = atom; + gotMap[{ target, false }] = atom; } } } @@ -229,7 +290,8 @@ void doPass(const Options& opts, ld::Internal& internal) } 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 @@ -268,8 +330,8 @@ void doPass(const Options& opts, ld::Internal& internal) 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 @@ -327,8 +389,15 @@ void doPass(const Options& opts, ld::Internal& internal) // 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); } } @@ -357,7 +426,9 @@ void doPass(const Options& opts, ld::Internal& internal) } 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 @@ -367,7 +438,7 @@ void doPass(const Options& opts, ld::Internal& internal) 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"); diff --git a/src/ld/passes/objc.cpp b/src/ld/passes/objc.cpp index a9a8c6a..293646b 100644 --- a/src/ld/passes/objc.cpp +++ b/src/ld/passes/objc.cpp @@ -65,8 +65,7 @@ struct objc_image_info { template 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"; } @@ -89,35 +88,13 @@ template ld::Section ObjCImageInfoAtom::_s_sectionABI2("__DATA", template -ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction, - bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion) +ObjCImageInfoAtom::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; } @@ -421,9 +398,12 @@ template const ld::Atom* Category::getClassProperties(ld::Internal& state, const ld::Atom* contentAtom) { // Only specially-marked files have this field. - if (!contentAtom->file()->objcHasCategoryClassPropertiesField()) return NULL; - - return ObjCData::getPointerInContent(state, contentAtom, 6*sizeof(pint_t)); // category_t.classProperties + if ( const ld::relocatable::File* objFile = dynamic_cast(contentAtom->file()) ) { + if ( objFile->objcHasCategoryClassPropertiesField() ) { + return ObjCData::getPointerInContent(state, contentAtom, 6*sizeof(pint_t)); // category_t.classProperties + } + } + return NULL; } @@ -1272,10 +1252,12 @@ void scanCategories(ld::Internal& state, 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(categoryAtom->file()) ) { + if ( !objFile->objcHasCategoryClassPropertiesField() ) { + haveCategoriesWithoutClassPropertyStorage = true; + // fprintf(stderr, "category in file %s has no storage for class properties\n", categoryAtom->safeFilePath()); + } } } } @@ -1287,22 +1269,10 @@ template 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::doit(opts, state); @@ -1324,8 +1294,7 @@ void doPass(const Options& opts, ld::Internal& state) // add image info atom // The HasCategoryClassProperties bit is set as often as possible. - state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, opts.objcGcCompaction(), isObjC2, - !haveCategoriesWithoutClassPropertyStorage, state.swiftVersion)); + state.addAtom(*new ObjCImageInfoAtom(isObjC2, !haveCategoriesWithoutClassPropertyStorage, state.swiftVersion)); } @@ -1353,6 +1322,12 @@ void doPass(const Options& opts, ld::Internal& state) #endif #if SUPPORT_ARCH_arm64 case CPU_TYPE_ARM64: +#if SUPPORT_ARCH_arm64e + if (opts.subArchitecture() == CPU_SUBTYPE_ARM64_E) { + doPass(opts, state); + break; + } +#endif doPass(opts, state); break; #endif diff --git a/src/ld/passes/stubs/stub_arm.hpp b/src/ld/passes/stubs/stub_arm.hpp index 02ae454..c97fab9 100644 --- a/src/ld/passes/stubs/stub_arm.hpp +++ b/src/ld/passes/stubs/stub_arm.hpp @@ -293,12 +293,14 @@ ld::Section LazyPointerAtom::_s_sectionWeak("__DATA", "__la_weak_ptr", ld::Secti 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); } @@ -324,12 +326,13 @@ ld::Section NonLazyPointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Sect 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), @@ -338,8 +341,8 @@ public: _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(); } diff --git a/src/ld/passes/stubs/stub_arm64.hpp b/src/ld/passes/stubs/stub_arm64.hpp index 815e28a..e473f06 100644 --- a/src/ld/passes/stubs/stub_arm64.hpp +++ b/src/ld/passes/stubs/stub_arm64.hpp @@ -148,7 +148,7 @@ public: 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), @@ -191,7 +191,7 @@ public: 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), @@ -301,7 +301,7 @@ public: 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), @@ -338,12 +338,14 @@ 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) + 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); } @@ -366,18 +368,19 @@ private: 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(); } @@ -403,8 +406,404 @@ private: 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 + diff --git a/src/ld/passes/stubs/stub_x86.hpp b/src/ld/passes/stubs/stub_x86.hpp index 08879c1..8e79b39 100644 --- a/src/ld/passes/stubs/stub_x86.hpp +++ b/src/ld/passes/stubs/stub_x86.hpp @@ -328,6 +328,89 @@ private: 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 diff --git a/src/ld/passes/stubs/stub_x86_64.hpp b/src/ld/passes/stubs/stub_x86_64.hpp index 5031dbc..5c7de10 100644 --- a/src/ld/passes/stubs/stub_x86_64.hpp +++ b/src/ld/passes/stubs/stub_x86_64.hpp @@ -351,12 +351,14 @@ 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) + 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); } @@ -380,14 +382,15 @@ ld::Section NonLazyPointerAtom::_s_section("__DATA", "__got", ld::Section::typeN -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(); } @@ -414,7 +417,7 @@ private: 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 diff --git a/src/ld/passes/stubs/stubs.cpp b/src/ld/passes/stubs/stubs.cpp index fb5b9f3..219c65e 100644 --- a/src/ld/passes/stubs/stubs.cpp +++ b/src/ld/passes/stubs/stubs.cpp @@ -75,7 +75,6 @@ private: const cpu_type_t _architecture; const bool _lazyDylibsInUuse; const bool _compressedLINKEDIT; - const bool _prebind; const bool _mightBeInSharedRegion; const bool _pic; const bool _flatNamespace; @@ -102,7 +101,6 @@ Pass::Pass(const Options& opts) _architecture(opts.architecture()), _lazyDylibsInUuse(opts.usingLazyDylibLinking()), _compressedLINKEDIT(opts.makeCompressedDyldInfo()), - _prebind(opts.prebind()), _mightBeInSharedRegion(opts.sharedRegionEligible()), _pic(opts.outputSlidable()), _flatNamespace(opts.nameSpace() != Options::kTwoLevelNameSpace), @@ -190,7 +188,9 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) 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); @@ -199,7 +199,9 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool 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 @@ -210,11 +212,13 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) 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 @@ -230,8 +234,21 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) #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; @@ -357,7 +374,7 @@ void Pass::process(ld::Internal& state) return; // 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 diff --git a/src/ld/passes/thread_starts.cpp b/src/ld/passes/thread_starts.cpp new file mode 100644 index 0000000..493b4d0 --- /dev/null +++ b/src/ld/passes/thread_starts.cpp @@ -0,0 +1,250 @@ +/* -*- 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 +#include +#include +#include +#include + +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ld.hpp" +#include "thread_starts.h" + +namespace ld { +namespace passes { +namespace thread_starts { + + +static std::map sAtomToAddress; + + + + +template +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 +bool ThreadStartsAtom::_s_log = false; + +template +ld::Section ThreadStartsAtom::_s_section("__TEXT", "__thread_starts", ld::Section::typeThreadStarts); + + +template +ThreadStartsAtom::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 +ThreadStartsAtom::~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::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::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& 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 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(fixupAlignment, numThreadStarts)); + break; +#endif +#if SUPPORT_ARCH_i386 + case CPU_TYPE_I386: + state.addAtom(*new ThreadStartsAtom(fixupAlignment, numThreadStarts)); + break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new ThreadStartsAtom(fixupAlignment, numThreadStarts)); + break; +#endif +#if SUPPORT_ARCH_arm_any + case CPU_TYPE_ARM: + state.addAtom(*new ThreadStartsAtom(fixupAlignment, numThreadStarts)); + break; +#endif + default: + assert(0 && "no threaded starts for arch"); + } +} + + +} // namespace thread_starts +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/thread_starts.h b/src/ld/passes/thread_starts.h new file mode 100644 index 0000000..a1213c4 --- /dev/null +++ b/src/ld/passes/thread_starts.h @@ -0,0 +1,45 @@ +/* -*- 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__ diff --git a/src/other/ObjectDump.cpp b/src/other/ObjectDump.cpp index 6763e1a..5a355dc 100644 --- a/src/other/ObjectDump.cpp +++ b/src/other/ObjectDump.cpp @@ -35,6 +35,8 @@ #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; @@ -1258,8 +1260,9 @@ static ld::relocatable::File* createReader(const char* path) 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; } diff --git a/src/other/dyldinfo.cpp b/src/other/dyldinfo.cpp index e271978..45fcb0f 100644 --- a/src/other/dyldinfo.cpp +++ b/src/other/dyldinfo.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -615,6 +616,8 @@ const char* DyldInfoPrinter::ordinalName(int libraryOrdinal) 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"; @@ -644,12 +647,164 @@ const char* DyldInfoPrinter::classicOrdinalName(int libraryOrdinal) template void DyldInfoPrinter::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()]; @@ -662,6 +817,8 @@ void DyldInfoPrinter::printRebaseInfo() 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; @@ -689,35 +846,42 @@ void DyldInfoPrinter::printRebaseInfo() 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); } - } + } } - } @@ -791,11 +955,6 @@ void DyldInfoPrinter::printRebaseInfoOpcodes() } - - - - - template void DyldInfoPrinter::printBindingInfoOpcodes(bool weakbinding) { @@ -901,6 +1060,20 @@ void DyldInfoPrinter::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); } @@ -909,7 +1082,20 @@ void DyldInfoPrinter::printBindingInfoOpcodes(bool weakbinding) } +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 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 void DyldInfoPrinter::printBindingInfo() @@ -922,8 +1108,9 @@ void DyldInfoPrinter::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; @@ -936,6 +1123,8 @@ void DyldInfoPrinter::printBindingInfo() const char* segName = "??"; const char* typeName = "??"; const char* weak_import = ""; + std::vector ordinalTable; + bool useThreadedRebaseBind = false; bool done = false; while ( !done && (p < end) ) { uint8_t immediate = *p & BIND_IMMEDIATE_MASK; @@ -968,7 +1157,8 @@ void DyldInfoPrinter::printBindingInfo() 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 = ""; @@ -990,8 +1180,12 @@ void DyldInfoPrinter::printBindingInfo() 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 ); @@ -1009,6 +1203,68 @@ void DyldInfoPrinter::printBindingInfo() 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); } @@ -1734,6 +1990,8 @@ const char* DyldInfoPrinter::sharedRegionKindName(uint8_t kind) return "br22"; case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32: return "off32"; + case DYLD_CACHE_ADJ_V2_THREADED_POINTER_64: + return "theaded-pointer64"; } } @@ -2009,6 +2267,93 @@ const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) template void DyldInfoPrinter::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

* segCmd : fSegments) { + if (strcmp(segCmd->segname(), "__TEXT") != 0) + continue; + macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for(macho_section

* 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"); } @@ -2412,14 +2757,6 @@ int main(int argc, const char* argv[]) 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"; diff --git a/src/other/machochecker.cpp b/src/other/machochecker.cpp index ef6ba48..8e95458 100644 --- a/src/other/machochecker.cpp +++ b/src/other/machochecker.cpp @@ -134,6 +134,7 @@ private: uint8_t loadCommandSizeMask(); void checkSymbolTable(); void checkInitTerms(); + void checkThreadedRebaseBind(); void checkIndirectSymbolTable(); void checkRelocations(); void checkExternalReloation(const macho_relocation_info

* reloc); @@ -142,12 +143,13 @@ private: 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

*); pint_t getEntryPoint(const macho_thread_command

*); @@ -173,6 +175,8 @@ private: bool fWriteableSegmentWithAddrOver4G; bool fSlidableImage; bool fHasLC_RPATH; + bool fIsDebugVariant; + uint64_t fBaseAddress = 0; const macho_segment_command

* fFirstSegment; const macho_segment_command

* fFirstWritableSegment; const macho_segment_command

* fTEXTSegment; @@ -356,7 +360,7 @@ template MachOChecker::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 @@ -381,6 +385,8 @@ MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, c checkInitTerms(); + checkThreadedRebaseBind(); + if ( verifierDstRoot != NULL ) verify(); } @@ -578,6 +584,8 @@ void MachOChecker::checkLoadCommands() if ( segCmd->vmaddr() > 0x100000000ULL ) fWriteableSegmentWithAddrOver4G = true; } + if ( (segCmd->fileoff() == 0) && (segCmd->filesize() != 0) ) + fBaseAddress = segCmd->vmaddr(); // check section ranges const macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); @@ -848,19 +856,35 @@ void MachOChecker::checkSection(const macho_segment_command

* segCmd, const // 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 void MachOChecker::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(); } @@ -929,6 +953,41 @@ void MachOChecker::verifyNoFlatLookups() } } +template +void MachOChecker::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

* cmd = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); + for (uint32_t i = 0; i < cmd_count; ++i) { + const macho_build_version_command

* 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

*)cmd; + if ( buildVersCmd->platform() != PLATFORM_IOSMAC ) + bad = true; + break; + } + cmd = (const macho_load_command

*)(((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 void MachOChecker::checkIndirectSymbolTable() { @@ -1084,23 +1143,28 @@ void MachOChecker::checkInitTerms() 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; @@ -1112,6 +1176,131 @@ void MachOChecker::checkInitTerms() } +template +void MachOChecker::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 <> @@ -1437,7 +1626,7 @@ bool MachOChecker::hasTextRelocInRange(pint_t rangeStart, pint_t rangeEnd) } template -bool MachOChecker::addressIsRebaseSite(pint_t targetAddr) +bool MachOChecker::addressIsRebaseSite(pint_t targetAddr, pint_t& pointeeAddr) { // look at local relocs const macho_relocation_info

* const localRelocsEnd = &fLocalRelocations[fLocalRelocationsCount]; @@ -1521,12 +1710,125 @@ bool MachOChecker::addressIsRebaseSite(pint_t targetAddr) 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 bool MachOChecker::addressIsBindingSite(pint_t targetAddr) { @@ -1619,6 +1921,35 @@ bool MachOChecker::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); } @@ -1729,10 +2060,11 @@ int main(int argc, const char* argv[]) 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 { diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index c3a7864..617677b 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -10,11 +10,12 @@ VALID_ARCHS ?= "i386 x86_64 armv6" 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 @@ -60,7 +61,8 @@ ifeq ($(ARCH),ppc) 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 = @@ -68,12 +70,13 @@ VERSION_NEW_LINKEDIT = -mmacosx-version-min=10.6 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 @@ -87,6 +90,7 @@ endif 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 @@ -103,6 +107,7 @@ ifeq ($(ARCH),thumb) 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 @@ -119,6 +124,7 @@ ifeq ($(ARCH),thumb2) 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 @@ -131,6 +137,7 @@ endif 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 diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index cdab927..058878e 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -12,11 +12,29 @@ cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` 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 diff --git a/unit-tests/test-cases/auto-arch/Makefile b/unit-tests/test-cases/auto-arch/Makefile index 06ca6ed..4382cc1 100644 --- a/unit-tests/test-cases/auto-arch/Makefile +++ b/unit-tests/test-cases/auto-arch/Makefile @@ -25,8 +25,15 @@ include ${TESTROOT}/include/common.makefile 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 @@ -37,7 +44,7 @@ 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} diff --git a/unit-tests/test-cases/blank-stubs/Makefile b/unit-tests/test-cases/blank-stubs/Makefile index 19f0af1..9db9b97 100644 --- a/unit-tests/test-cases/blank-stubs/Makefile +++ b/unit-tests/test-cases/blank-stubs/Makefile @@ -23,7 +23,25 @@ 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 @@ -33,11 +51,10 @@ run: all 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}; \ @@ -55,7 +72,6 @@ all: ${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 diff --git a/unit-tests/test-cases/cstring-labels/Makefile b/unit-tests/test-cases/cstring-labels/Makefile index 76db33a..fb55ccf 100644 --- a/unit-tests/test-cases/cstring-labels/Makefile +++ b/unit-tests/test-cases/cstring-labels/Makefile @@ -33,14 +33,13 @@ include ${TESTROOT}/include/common.makefile 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: @@ -56,4 +55,4 @@ 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 diff --git a/unit-tests/test-cases/dtrace-old-probes/main.c b/unit-tests/test-cases/dtrace-old-probes/main.c index 811449a..bd02587 100644 --- a/unit-tests/test-cases/dtrace-old-probes/main.c +++ b/unit-tests/test-cases/dtrace-old-probes/main.c @@ -13,12 +13,21 @@ #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) \ diff --git a/unit-tests/test-cases/dwarf-archive-all_load/Makefile b/unit-tests/test-cases/dwarf-archive-all_load/Makefile index aa02b11..b284b43 100644 --- a/unit-tests/test-cases/dwarf-archive-all_load/Makefile +++ b/unit-tests/test-cases/dwarf-archive-all_load/Makefile @@ -38,7 +38,7 @@ run: 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: diff --git a/unit-tests/test-cases/dwarf-debug-notes-lto/Makefile b/unit-tests/test-cases/dwarf-debug-notes-lto/Makefile index b498430..c0c27c2 100644 --- a/unit-tests/test-cases/dwarf-debug-notes-lto/Makefile +++ b/unit-tests/test-cases/dwarf-debug-notes-lto/Makefile @@ -46,9 +46,10 @@ all: ${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 diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs b/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs index f8abc12..8adb891 100644 --- a/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs +++ b/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs @@ -12,7 +12,7 @@ 0000 BNSYM 0000 FUN __Z3foov 0000 FUN -0000 ENSYM +0000 ENSYM 0000 SO 0000 SO CWD/ 0000 SO bar.cxx @@ -20,5 +20,5 @@ 0000 BNSYM 0000 FUN __Z3barv 0000 FUN -0000 ENSYM +0000 ENSYM 0000 SO diff --git a/unit-tests/test-cases/efi-basic/Makefile b/unit-tests/test-cases/efi-basic/Makefile index 99f2fa6..7fceb5f 100644 --- a/unit-tests/test-cases/efi-basic/Makefile +++ b/unit-tests/test-cases/efi-basic/Makefile @@ -60,10 +60,12 @@ all: ${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) diff --git a/unit-tests/test-cases/efi-basic/efi-pecoff-util b/unit-tests/test-cases/efi-basic/efi-pecoff-util new file mode 100755 index 0000000..8bd8ad9 --- /dev/null +++ b/unit-tests/test-cases/efi-basic/efi-pecoff-util @@ -0,0 +1,942 @@ +#!/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 = ' e_res2[10] + EFI_IMAGE_DOS_HEADER_fmt = '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 ('> 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 = ' 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)) + diff --git a/unit-tests/test-cases/efi-basic/mtoctest.py b/unit-tests/test-cases/efi-basic/mtoctest.py index 460c8f5..6e3e1b2 100755 --- a/unit-tests/test-cases/efi-basic/mtoctest.py +++ b/unit-tests/test-cases/efi-basic/mtoctest.py @@ -9,30 +9,31 @@ def Usage(): 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") @@ -44,68 +45,127 @@ def main(): 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: @@ -113,7 +173,7 @@ def main(): sys.exit(-1) if Verbose: - print "\nmach-o relocations:" + print "\nmach-o relocations:" otoolreloc = open("otool-reloc.log", "r") lines = otoolreloc.readlines() otoolreloc.close() @@ -128,7 +188,7 @@ def main(): found = True if Verbose: - print + print if __name__ == "__main__": diff --git a/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile b/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile index cf300a3..d3b0f86 100644 --- a/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile +++ b/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile @@ -31,8 +31,8 @@ run: all 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: diff --git a/unit-tests/test-cases/exported-symbols-wildcards/Makefile b/unit-tests/test-cases/exported-symbols-wildcards/Makefile index 7b000e4..902f6b3 100644 --- a/unit-tests/test-cases/exported-symbols-wildcards/Makefile +++ b/unit-tests/test-cases/exported-symbols-wildcards/Makefile @@ -31,47 +31,47 @@ run: all 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: diff --git a/unit-tests/test-cases/lto-dylib-export_list/Makefile b/unit-tests/test-cases/lto-dylib-export_list/Makefile index 0fa97ce..c9b62e0 100644 --- a/unit-tests/test-cases/lto-dylib-export_list/Makefile +++ b/unit-tests/test-cases/lto-dylib-export_list/Makefile @@ -35,7 +35,7 @@ run: all 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 diff --git a/unit-tests/test-cases/lto-live_support_section/Makefile b/unit-tests/test-cases/lto-live_support_section/Makefile index 01b417f..df62d3f 100644 --- a/unit-tests/test-cases/lto-live_support_section/Makefile +++ b/unit-tests/test-cases/lto-live_support_section/Makefile @@ -31,7 +31,7 @@ include ${TESTROOT}/include/common.makefile 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} @@ -40,6 +40,7 @@ all: # Check symbols that are expected to be stripped nm -m foo | grep unused | ${FAIL_IF_STDIN} - + + ${PASS_IFF} true clean: rm foo diff --git a/unit-tests/test-cases/lto-unexport-sym/Makefile b/unit-tests/test-cases/lto-unexport-sym/Makefile index de0808a..ba63f26 100644 --- a/unit-tests/test-cases/lto-unexport-sym/Makefile +++ b/unit-tests/test-cases/lto-unexport-sym/Makefile @@ -37,6 +37,7 @@ all: # 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 diff --git a/unit-tests/test-cases/no-object-symbols/Makefile b/unit-tests/test-cases/no-object-symbols/Makefile index 933533f..8b49b1b 100644 --- a/unit-tests/test-cases/no-object-symbols/Makefile +++ b/unit-tests/test-cases/no-object-symbols/Makefile @@ -34,12 +34,12 @@ include ${TESTROOT}/include/common.makefile 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: diff --git a/unit-tests/test-cases/objc-gc-checks/Makefile b/unit-tests/test-cases/objc-gc-checks/Makefile deleted file mode 100644 index 426d9b2..0000000 --- a/unit-tests/test-cases/objc-gc-checks/Makefile +++ /dev/null @@ -1,145 +0,0 @@ -## -# 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 diff --git a/unit-tests/test-cases/objc-gc-checks/bar.m b/unit-tests/test-cases/objc-gc-checks/bar.m deleted file mode 100644 index f66df50..0000000 --- a/unit-tests/test-cases/objc-gc-checks/bar.m +++ /dev/null @@ -1,12 +0,0 @@ -#include - -@interface Bar : NSObject { - int f; -} -- (void) doit; -@end - -@implementation Bar -- (void) doit { } -@end - diff --git a/unit-tests/test-cases/objc-gc-checks/comment.txt b/unit-tests/test-cases/objc-gc-checks/comment.txt deleted file mode 100644 index 953da58..0000000 --- a/unit-tests/test-cases/objc-gc-checks/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Validate that the linker catches illegal combintations of .o files compiled with different GC settings diff --git a/unit-tests/test-cases/objc-gc-checks/foo.m b/unit-tests/test-cases/objc-gc-checks/foo.m deleted file mode 100644 index 819049d..0000000 --- a/unit-tests/test-cases/objc-gc-checks/foo.m +++ /dev/null @@ -1,13 +0,0 @@ -#include - -@interface Foo : NSObject { - int f; -} -- (void) doit; -@end - - -@implementation Foo -- (void) doit { } -@end - diff --git a/unit-tests/test-cases/objc-gc-checks/none.c b/unit-tests/test-cases/objc-gc-checks/none.c deleted file mode 100644 index 44506fa..0000000 --- a/unit-tests/test-cases/objc-gc-checks/none.c +++ /dev/null @@ -1 +0,0 @@ -void none() {} diff --git a/unit-tests/test-cases/objc-gc-checks/runtime.c b/unit-tests/test-cases/objc-gc-checks/runtime.c deleted file mode 100644 index df4aeef..0000000 --- a/unit-tests/test-cases/objc-gc-checks/runtime.c +++ /dev/null @@ -1,2 +0,0 @@ -void _objc_empty_cache() {} -void _objc_empty_vtable() {} diff --git a/unit-tests/test-cases/re-export-optimizations-indirect/Makefile b/unit-tests/test-cases/re-export-optimizations-indirect/Makefile index 4eb7b43..dddf231 100644 --- a/unit-tests/test-cases/re-export-optimizations-indirect/Makefile +++ b/unit-tests/test-cases/re-export-optimizations-indirect/Makefile @@ -40,7 +40,7 @@ all-old: # -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 @@ -54,7 +54,7 @@ all-new: # -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 diff --git a/unit-tests/test-cases/special-labels/Makefile b/unit-tests/test-cases/special-labels/Makefile index 060f12e..e4d81ac 100644 --- a/unit-tests/test-cases/special-labels/Makefile +++ b/unit-tests/test-cases/special-labels/Makefile @@ -31,7 +31,7 @@ include ${TESTROOT}/include/common.makefile 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} diff --git a/unit-tests/test-cases/stabs-coalesce/Makefile b/unit-tests/test-cases/stabs-coalesce/Makefile deleted file mode 100644 index 806f406..0000000 --- a/unit-tests/test-cases/stabs-coalesce/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -## -# 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 diff --git a/unit-tests/test-cases/stabs-coalesce/comment.txt b/unit-tests/test-cases/stabs-coalesce/comment.txt deleted file mode 100644 index f22b9a1..0000000 --- a/unit-tests/test-cases/stabs-coalesce/comment.txt +++ /dev/null @@ -1,3 +0,0 @@ -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 diff --git a/unit-tests/test-cases/stabs-coalesce/header.h b/unit-tests/test-cases/stabs-coalesce/header.h deleted file mode 100644 index 378308f..0000000 --- a/unit-tests/test-cases/stabs-coalesce/header.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- 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); diff --git a/unit-tests/test-cases/stabs-coalesce/hello.cxx b/unit-tests/test-cases/stabs-coalesce/hello.cxx deleted file mode 100644 index 33bf273..0000000 --- a/unit-tests/test-cases/stabs-coalesce/hello.cxx +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- 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 - -#include "header.h" - - -int main() -{ - foo(bar(3)); - fprintf(stdout, "hello\n"); -} \ No newline at end of file diff --git a/unit-tests/test-cases/stabs-coalesce/other.cxx b/unit-tests/test-cases/stabs-coalesce/other.cxx deleted file mode 100644 index ee97d7d..0000000 --- a/unit-tests/test-cases/stabs-coalesce/other.cxx +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- 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); -} - - diff --git a/unit-tests/test-cases/stabs-directory-slash/Makefile b/unit-tests/test-cases/stabs-directory-slash/Makefile deleted file mode 100644 index cd857b9..0000000 --- a/unit-tests/test-cases/stabs-directory-slash/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -## -# 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 '.*\.*test-cases.*/$$' | ${PASS_IFF_STDIN} - -clean: - rm -rf outfile* diff --git a/unit-tests/test-cases/stabs-directory-slash/main.c b/unit-tests/test-cases/stabs-directory-slash/main.c deleted file mode 100644 index a46866d..0000000 --- a/unit-tests/test-cases/stabs-directory-slash/main.c +++ /dev/null @@ -1,4 +0,0 @@ -int main() -{ - return 0; -} diff --git a/unit-tests/test-cases/stack_addr_size/Makefile b/unit-tests/test-cases/stack_addr_size/Makefile deleted file mode 100644 index c88f66b..0000000 --- a/unit-tests/test-cases/stack_addr_size/Makefile +++ /dev/null @@ -1,62 +0,0 @@ -## -# 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 diff --git a/unit-tests/test-cases/stack_addr_size/comment.txt b/unit-tests/test-cases/stack_addr_size/comment.txt deleted file mode 100644 index da74f89..0000000 --- a/unit-tests/test-cases/stack_addr_size/comment.txt +++ /dev/null @@ -1,11 +0,0 @@ -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 . - - diff --git a/unit-tests/test-cases/stack_addr_size/main.c b/unit-tests/test-cases/stack_addr_size/main.c deleted file mode 100644 index 5c73586..0000000 --- a/unit-tests/test-cases/stack_addr_size/main.c +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- 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 - -int main() -{ - return 0; -} diff --git a/unit-tests/test-cases/stack_size_no_addr/Makefile b/unit-tests/test-cases/stack_size_no_addr/Makefile index cbb6d49..082e208 100644 --- a/unit-tests/test-cases/stack_size_no_addr/Makefile +++ b/unit-tests/test-cases/stack_size_no_addr/Makefile @@ -41,15 +41,15 @@ else 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: diff --git a/unit-tests/test-cases/static-executable-pie/Makefile b/unit-tests/test-cases/static-executable-pie/Makefile index 5854b0f..cc02303 100644 --- a/unit-tests/test-cases/static-executable-pie/Makefile +++ b/unit-tests/test-cases/static-executable-pie/Makefile @@ -38,7 +38,7 @@ endif 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 diff --git a/unit-tests/test-cases/thumb-blx/test.c b/unit-tests/test-cases/thumb-blx/test.c index ce0359f..9755e7d 100644 --- a/unit-tests/test-cases/thumb-blx/test.c +++ b/unit-tests/test-cases/thumb-blx/test.c @@ -26,10 +26,10 @@ int main() { - malloc(1); - malloc(2); - malloc(3); - malloc(4); + (void)malloc(1); + (void)malloc(2); + (void)malloc(3); + (void)malloc(4); return 0; } diff --git a/unit-tests/test-cases/umbrella-dylib/Makefile b/unit-tests/test-cases/umbrella-dylib/Makefile index 0835fdb..89c6b3e 100644 --- a/unit-tests/test-cases/umbrella-dylib/Makefile +++ b/unit-tests/test-cases/umbrella-dylib/Makefile @@ -32,19 +32,19 @@ run: all 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 diff --git a/unit-tests/test-cases/undefined-dynamic-lookup/Makefile b/unit-tests/test-cases/undefined-dynamic-lookup/Makefile index 69b40c7..1f82773 100644 --- a/unit-tests/test-cases/undefined-dynamic-lookup/Makefile +++ b/unit-tests/test-cases/undefined-dynamic-lookup/Makefile @@ -27,34 +27,45 @@ include ${TESTROOT}/include/common.makefile # 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