]> git.saurik.com Git - apple/objc4.git/commitdiff
objc4-787.1.tar.gz macos-10154 macos-10155 macos-10156 v787.1
authorApple <opensource@apple.com>
Fri, 1 May 2020 18:26:59 +0000 (18:26 +0000)
committerApple <opensource@apple.com>
Fri, 1 May 2020 18:26:59 +0000 (18:26 +0000)
36 files changed:
libobjc.order
objc.xcodeproj/project.pbxproj
objcdt/json.h [new file with mode: 0644]
objcdt/json.mm [new file with mode: 0644]
objcdt/objcdt-entitlements.plist [new file with mode: 0644]
objcdt/objcdt.1 [new file with mode: 0644]
objcdt/objcdt.mm [new file with mode: 0644]
runtime/Messengers.subproj/objc-msg-arm.s
runtime/Messengers.subproj/objc-msg-arm64.s
runtime/Messengers.subproj/objc-msg-simulator-i386.s
runtime/Messengers.subproj/objc-msg-simulator-x86_64.s
runtime/Messengers.subproj/objc-msg-x86_64.s
runtime/PointerUnion.h
runtime/objc-cache.mm
runtime/objc-file.h
runtime/objc-internal.h
runtime/objc-opt.mm
runtime/objc-os.h
runtime/objc-os.mm
runtime/objc-private.h
runtime/objc-ptrauth.h
runtime/objc-runtime-new.h
runtime/objc-runtime-new.mm
runtime/objc-sel-old.mm
runtime/objc-sel.mm
runtime/objc.h
test/gdb.m
test/libraryPath.c [new file with mode: 0644]
test/load-noobjc.m
test/methodListSmall.h [new file with mode: 0644]
test/methodListSmall.mm [new file with mode: 0644]
test/methodListSmallMutableMemory.mm [new file with mode: 0644]
test/nonpointerisa.m
test/supported-inline-refcnt.m [new file with mode: 0644]
test/test.h
test/test.pl

index c7415fc1171e97e2f5bd3e137941421945fd7d10..1d56c5929bca72d7cc052dda18dfb50bec066afe 100644 (file)
@@ -19,8 +19,6 @@ __objc_update_stubs_in_mach_header
 _sel_init
 ___sel_registerName
 __objc_search_builtins
-__ZNK8objc_opt13objc_selopt_t3getEPKc
-__ZNK8objc_opt13objc_selopt_t4hashEPKc
 _sel_registerName
 _arr_init
 __ZN4objc8DenseMapIP11objc_objectmLb1ENS_12DenseMapInfoIS2_EENS3_ImEEE4initEj
index 80c47fb6bf2721fa378151abccf3bbc33f317dea..5587470daa0abbf253d97d8cbab48f5db53fd943 100644 (file)
@@ -7,6 +7,17 @@
        objects = {
 
 /* Begin PBXAggregateTarget section */
+               6EF877EF23263D7000963DBB /* objc_executables */ = {
+                       isa = PBXAggregateTarget;
+                       buildConfigurationList = 6EF877F223263D7000963DBB /* Build configuration list for PBXAggregateTarget "objc_executables" */;
+                       buildPhases = (
+                       );
+                       dependencies = (
+                               6EF877F423263D8000963DBB /* PBXTargetDependency */,
+                       );
+                       name = objc_executables;
+                       productName = "objc-executables";
+               };
                834F9B01212E560100F95A54 /* objc4_tests */ = {
                        isa = PBXAggregateTarget;
                        buildConfigurationList = 834F9B04212E560200F95A54 /* Build configuration list for PBXAggregateTarget "objc4_tests" */;
                6EACB842232C97A400CE9176 /* objc-zalloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EACB841232C97A400CE9176 /* objc-zalloc.h */; };
                6EACB844232C97B900CE9176 /* objc-zalloc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EACB843232C97B900CE9176 /* objc-zalloc.mm */; };
                6ECD0B1F2244999E00910D88 /* llvm-DenseSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 6ECD0B1E2244999E00910D88 /* llvm-DenseSet.h */; };
+               6EF877DA2325D62600963DBB /* objcdt.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EF877D92325D62600963DBB /* objcdt.mm */; };
+               6EF877DE2325D79000963DBB /* objc-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = 87BB4E900EC39633005D08E1 /* objc-probes.d */; };
+               6EF877E02325D92E00963DBB /* CoreSymbolication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EF877DF2325D92E00963DBB /* CoreSymbolication.framework */; };
+               6EF877E22325D93200963DBB /* Symbolication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EF877E12325D93200963DBB /* Symbolication.framework */; };
+               6EF877E52325FAC400963DBB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EF877E42325FAC400963DBB /* Foundation.framework */; };
+               6EF877E82326184000963DBB /* json.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EF877E72326184000963DBB /* json.mm */; };
+               6EF877E923261D3E00963DBB /* objc-cache.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485CB0D6D68A200CEA253 /* objc-cache.mm */; };
+               6EF877EC232635A700963DBB /* objcdt.1 in Install Manpages */ = {isa = PBXBuildFile; fileRef = 6EF877EA232633CC00963DBB /* objcdt.1 */; };
                7213C36321FA7C730090A271 /* NSObject-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7213C36221FA7C730090A271 /* NSObject-internal.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7593EC58202248E50046AB96 /* objc-object.h in Headers */ = {isa = PBXBuildFile; fileRef = 7593EC57202248DF0046AB96 /* objc-object.h */; };
                75A9504F202BAA0600D7D56F /* objc-locks-new.h in Headers */ = {isa = PBXBuildFile; fileRef = 75A9504E202BAA0300D7D56F /* objc-locks-new.h */; };
                83F550E0155E030800E95D3B /* objc-cache-old.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83F550DF155E030800E95D3B /* objc-cache-old.mm */; };
                87BB4EA70EC39854005D08E1 /* objc-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = 87BB4E900EC39633005D08E1 /* objc-probes.d */; };
                9672F7EE14D5F488007CEC96 /* NSObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9672F7ED14D5F488007CEC96 /* NSObject.mm */; };
+               C22F5208230EF38B001BFE14 /* objc-ptrauth.h in Headers */ = {isa = PBXBuildFile; fileRef = C22F5207230EF38B001BFE14 /* objc-ptrauth.h */; };
                C2E6D3FC2225DCF00059DFAA /* DenseMapExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = C2E6D3FB2225DCF00059DFAA /* DenseMapExtras.h */; };
                E8923DA5116AB2820071B552 /* objc-block-trampolines.mm in Sources */ = {isa = PBXBuildFile; fileRef = E8923DA0116AB2820071B552 /* objc-block-trampolines.mm */; };
                F9BCC71B205C68E800DD9AFC /* objc-blocktramps-arm64.s in Sources */ = {isa = PBXBuildFile; fileRef = 8379996D13CBAF6F007C2B5F /* objc-blocktramps-arm64.s */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
+               6EF877F323263D8000963DBB /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 6EF877D62325D62600963DBB;
+                       remoteInfo = objcdt;
+               };
                837F67AC1A771F6E004D34FA /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
                };
 /* End PBXContainerItemProxy section */
 
+/* Begin PBXCopyFilesBuildPhase section */
+               6EF877D52325D62600963DBB /* Install Manpages */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = /usr/local/share/man/man1/;
+                       dstSubfolderSpec = 0;
+                       files = (
+                               6EF877EC232635A700963DBB /* objcdt.1 in Install Manpages */,
+                       );
+                       name = "Install Manpages";
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
+/* End PBXCopyFilesBuildPhase section */
+
 /* Begin PBXFileReference section */
                393CEABF0DC69E3E000B69DE /* objc-references.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-references.mm"; path = "runtime/objc-references.mm"; sourceTree = "<group>"; };
                393CEAC50DC69E67000B69DE /* objc-references.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-references.h"; path = "runtime/objc-references.h"; sourceTree = "<group>"; };
                6EACB841232C97A400CE9176 /* objc-zalloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-zalloc.h"; path = "runtime/objc-zalloc.h"; sourceTree = "<group>"; };
                6EACB843232C97B900CE9176 /* objc-zalloc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-zalloc.mm"; path = "runtime/objc-zalloc.mm"; sourceTree = "<group>"; };
                6ECD0B1E2244999E00910D88 /* llvm-DenseSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "llvm-DenseSet.h"; path = "runtime/llvm-DenseSet.h"; sourceTree = "<group>"; };
+               6EF877D72325D62600963DBB /* objcdt */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = objcdt; sourceTree = BUILT_PRODUCTS_DIR; };
+               6EF877D92325D62600963DBB /* objcdt.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = objcdt.mm; sourceTree = "<group>"; usesTabs = 0; };
+               6EF877DF2325D92E00963DBB /* CoreSymbolication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreSymbolication.framework; path = System/Library/PrivateFrameworks/CoreSymbolication.framework; sourceTree = SDKROOT; };
+               6EF877E12325D93200963DBB /* Symbolication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Symbolication.framework; path = System/Library/PrivateFrameworks/Symbolication.framework; sourceTree = SDKROOT; };
+               6EF877E32325D95300963DBB /* objcdt-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "objcdt-entitlements.plist"; sourceTree = "<group>"; };
+               6EF877E42325FAC400963DBB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+               6EF877E62326184000963DBB /* json.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json.h; sourceTree = "<group>"; usesTabs = 1; };
+               6EF877E72326184000963DBB /* json.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = json.mm; sourceTree = "<group>"; usesTabs = 1; };
+               6EF877EA232633CC00963DBB /* objcdt.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = objcdt.1; sourceTree = "<group>"; };
                7213C36221FA7C730090A271 /* NSObject-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject-internal.h"; path = "runtime/NSObject-internal.h"; sourceTree = "<group>"; };
                7593EC57202248DF0046AB96 /* objc-object.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "objc-object.h"; path = "runtime/objc-object.h"; sourceTree = "<group>"; };
                75A9504E202BAA0300D7D56F /* objc-locks-new.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "objc-locks-new.h"; path = "runtime/objc-locks-new.h"; sourceTree = "<group>"; };
                87BB4E900EC39633005D08E1 /* objc-probes.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; name = "objc-probes.d"; path = "runtime/objc-probes.d"; sourceTree = "<group>"; };
                9672F7ED14D5F488007CEC96 /* NSObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = NSObject.mm; path = runtime/NSObject.mm; sourceTree = "<group>"; };
                BC8B5D1212D3D48100C78A5B /* libauto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libauto.dylib; path = /usr/lib/libauto.dylib; sourceTree = "<absolute>"; };
+               C217B55222DE556D004369BA /* objc-env.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "objc-env.h"; path = "runtime/objc-env.h"; sourceTree = "<group>"; };
+               C22F5207230EF38B001BFE14 /* objc-ptrauth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-ptrauth.h"; path = "runtime/objc-ptrauth.h"; sourceTree = "<group>"; };
                C2E6D3FB2225DCF00059DFAA /* DenseMapExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DenseMapExtras.h; path = runtime/DenseMapExtras.h; sourceTree = "<group>"; };
                D2AAC0630554660B00DB518D /* libobjc.A.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libobjc.A.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
                E8923D9C116AB2820071B552 /* objc-blocktramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-blocktramps-i386.s"; path = "runtime/objc-blocktramps-i386.s"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+               6EF877D42325D62600963DBB /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               6EF877E22325D93200963DBB /* Symbolication.framework in Frameworks */,
+                               6EF877E52325FAC400963DBB /* Foundation.framework in Frameworks */,
+                               6EF877E02325D92E00963DBB /* CoreSymbolication.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                D289988505E68E00004EDB86 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                                838486270D6D690F00CEA253 /* Obsolete Source */,
                                08FB7795FE84155DC02AAC07 /* Source */,
                                838485B20D6D67F900CEA253 /* Other */,
+                               6EF877D82325D62600963DBB /* objcdt */,
                                1AB674ADFE9D54B511CA2CBB /* Products */,
                                F9BCC72A205C6A1600DD9AFC /* Frameworks */,
                        );
                        children = (
                                D2AAC0630554660B00DB518D /* libobjc.A.dylib */,
                                F9BCC727205C68E800DD9AFC /* libobjc-trampolines.dylib */,
+                               6EF877D72325D62600963DBB /* objcdt */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                };
+               6EF877D82325D62600963DBB /* objcdt */ = {
+                       isa = PBXGroup;
+                       children = (
+                               6EF877EA232633CC00963DBB /* objcdt.1 */,
+                               6EF877E62326184000963DBB /* json.h */,
+                               6EF877E72326184000963DBB /* json.mm */,
+                               6EF877D92325D62600963DBB /* objcdt.mm */,
+                               6EF877E32325D95300963DBB /* objcdt-entitlements.plist */,
+                       );
+                       path = objcdt;
+                       sourceTree = "<group>";
+               };
                838485B20D6D67F900CEA253 /* Other */ = {
                        isa = PBXGroup;
                        children = (
                838485C70D6D688200CEA253 /* Private Headers */ = {
                        isa = PBXGroup;
                        children = (
+                               838485BB0D6D687300CEA253 /* maptable.h */,
                                7213C36221FA7C730090A271 /* NSObject-internal.h */,
-                               83112ED30F00599600A5FBAF /* objc-internal.h */,
                                834EC0A311614167009B2563 /* objc-abi.h */,
-                               838485BB0D6D687300CEA253 /* maptable.h */,
-                               834266D70E665A8B002E4DA2 /* objc-gdb.h */,
                                8306440620D24A3E00E356D2 /* objc-block-trampolines.h */,
+                               834266D70E665A8B002E4DA2 /* objc-gdb.h */,
+                               83112ED30F00599600A5FBAF /* objc-internal.h */,
+                               C22F5207230EF38B001BFE14 /* objc-ptrauth.h */,
                        );
                        name = "Private Headers";
                        sourceTree = "<group>";
                                83D9269721225A7400299F69 /* arm64-asm.h */,
                                83D92695212254CF00299F69 /* isa.h */,
                                838485CF0D6D68A200CEA253 /* objc-config.h */,
+                               C217B55222DE556D004369BA /* objc-env.h */,
                                83BE02E50FCCB24D00661494 /* objc-file-old.h */,
                                83BE02E60FCCB24D00661494 /* objc-file.h */,
                                838485D40D6D68A200CEA253 /* objc-initialize.h */,
                F9BCC72A205C6A1600DD9AFC /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
+                               6EF877E42325FAC400963DBB /* Foundation.framework */,
+                               6EF877E12325D93200963DBB /* Symbolication.framework */,
+                               6EF877DF2325D92E00963DBB /* CoreSymbolication.framework */,
                        );
                        name = Frameworks;
                        sourceTree = "<group>";
                                838486200D6D68A800CEA253 /* runtime.h in Headers */,
                                39ABD72312F0B61800D1054C /* objc-weak.h in Headers */,
                                83F4B52815E843B100E0926F /* NSObjCRuntime.h in Headers */,
+                               C22F5208230EF38B001BFE14 /* objc-ptrauth.h in Headers */,
                                6ECD0B1F2244999E00910D88 /* llvm-DenseSet.h in Headers */,
                                83F4B52915E843B100E0926F /* NSObject.h in Headers */,
                        );
 /* End PBXHeadersBuildPhase section */
 
 /* Begin PBXNativeTarget section */
+               6EF877D62325D62600963DBB /* objcdt */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 6EF877DD2325D62600963DBB /* Build configuration list for PBXNativeTarget "objcdt" */;
+                       buildPhases = (
+                               6EF877D32325D62600963DBB /* Sources */,
+                               6EF877D42325D62600963DBB /* Frameworks */,
+                               6EF877D52325D62600963DBB /* Install Manpages */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = objcdt;
+                       productName = objcdt;
+                       productReference = 6EF877D72325D62600963DBB /* objcdt */;
+                       productType = "com.apple.product-type.tool";
+               };
                D2AAC0620554660B00DB518D /* objc */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = 1DEB914A08733D8E0010E9CD /* Build configuration list for PBXNativeTarget "objc" */;
                                BuildIndependentTargetsInParallel = NO;
                                LastUpgradeCheck = 0440;
                                TargetAttributes = {
+                                       6EF877D62325D62600963DBB = {
+                                               CreatedOnToolsVersion = 11.0;
+                                       };
+                                       6EF877EF23263D7000963DBB = {
+                                               CreatedOnToolsVersion = 11.0;
+                                               ProvisioningStyle = Automatic;
+                                       };
                                        834F9B01212E560100F95A54 = {
                                                CreatedOnToolsVersion = 10.0;
                                                DevelopmentTeam = 59GAB85EFG;
                                837F67A81A771F63004D34FA /* objc-simulator */,
                                F9BCC6CA205C68E800DD9AFC /* objc-trampolines */,
                                834F9B01212E560100F95A54 /* objc4_tests */,
+                               6EF877EF23263D7000963DBB /* objc_executables */,
+                               6EF877D62325D62600963DBB /* objcdt */,
                        );
                };
 /* End PBXProject section */
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
+               6EF877D32325D62600963DBB /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               6EF877E923261D3E00963DBB /* objc-cache.mm in Sources */,
+                               6EF877E82326184000963DBB /* json.mm in Sources */,
+                               6EF877DA2325D62600963DBB /* objcdt.mm in Sources */,
+                               6EF877DE2325D79000963DBB /* objc-probes.d in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                D2AAC0610554660B00DB518D /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
+               6EF877F423263D8000963DBB /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 6EF877D62325D62600963DBB /* objcdt */;
+                       targetProxy = 6EF877F323263D8000963DBB /* PBXContainerItemProxy */;
+               };
                837F67AD1A771F6E004D34FA /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = D2AAC0620554660B00DB518D /* objc */;
                        };
                        name = Release;
                };
+               6EF877DB2325D62600963DBB /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CODE_SIGN_ENTITLEMENTS = "objcdt/objcdt-entitlements.plist";
+                               CODE_SIGN_IDENTITY = "-";
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "__BUILDING_OBJCDT__=1",
+                                       "$(inherited)",
+                               );
+                               HEADER_SEARCH_PATHS = (
+                                       "$(SRCROOT)/runtime",
+                                       /System/Library/Frameworks/System.framework/PrivateHeaders,
+                               );
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SYSTEM_FRAMEWORK_SEARCH_PATHS = "$(inherited) $(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks";
+                       };
+                       name = Debug;
+               };
+               6EF877DC2325D62600963DBB /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CODE_SIGN_ENTITLEMENTS = "objcdt/objcdt-entitlements.plist";
+                               CODE_SIGN_IDENTITY = "-";
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "__BUILDING_OBJCDT__=1",
+                                       "$(inherited)",
+                               );
+                               HEADER_SEARCH_PATHS = (
+                                       "$(SRCROOT)/runtime",
+                                       /System/Library/Frameworks/System.framework/PrivateHeaders,
+                               );
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SYSTEM_FRAMEWORK_SEARCH_PATHS = "$(inherited) $(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks";
+                       };
+                       name = Release;
+               };
+               6EF877F023263D7000963DBB /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CODE_SIGN_STYLE = Automatic;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Debug;
+               };
+               6EF877F123263D7000963DBB /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CODE_SIGN_STYLE = Automatic;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Release;
+               };
                834F9B02212E560200F95A54 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                COPY_HEADERS_UNIFDEF_FLAGS = "-UBUILD_FOR_OSX";
                                "COPY_HEADERS_UNIFDEF_FLAGS[sdk=macosx*]" = "-DBUILD_FOR_OSX";
                                COPY_PHASE_STRIP = NO;
+                               DEPLOYMENT_LOCATION = YES;
                                DYLIB_CURRENT_VERSION = 228;
                                EXECUTABLE_PREFIX = lib;
                                GCC_CW_ASM_SYNTAX = NO;
                                COPY_HEADERS_RUN_UNIFDEF = YES;
                                COPY_HEADERS_UNIFDEF_FLAGS = "-UBUILD_FOR_OSX";
                                "COPY_HEADERS_UNIFDEF_FLAGS[sdk=macosx*]" = "-DBUILD_FOR_OSX";
+                               DEPLOYMENT_LOCATION = YES;
                                DYLIB_CURRENT_VERSION = 228;
                                EXECUTABLE_PREFIX = lib;
                                GCC_CW_ASM_SYNTAX = NO;
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               6EF877DD2325D62600963DBB /* Build configuration list for PBXNativeTarget "objcdt" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               6EF877DB2325D62600963DBB /* Debug */,
+                               6EF877DC2325D62600963DBB /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               6EF877F223263D7000963DBB /* Build configuration list for PBXAggregateTarget "objc_executables" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               6EF877F023263D7000963DBB /* Debug */,
+                               6EF877F123263D7000963DBB /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
                834F9B04212E560200F95A54 /* Build configuration list for PBXAggregateTarget "objc4_tests" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
diff --git a/objcdt/json.h b/objcdt/json.h
new file mode 100644 (file)
index 0000000..95dbea2
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+* Copyright (c) 2019 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 _OBJC_OBJCDT_JSON_H_
+#define _OBJC_OBJCDT_JSON_H_
+
+#include <cstdint>
+#include <cstdbool>
+#include <stdio.h>
+#include <functional>
+
+namespace json {
+
+enum context: uint8_t {
+    root,
+    array_value,
+    object_value,
+    object_key,
+    done,
+};
+
+class writer {
+private:
+    FILE *_file;
+    context _context;
+    int _depth;
+    bool _needs_comma;
+
+    void begin_value(int sep = '\0');
+    void advance(context old);
+    void key(const char *key);
+
+public:
+
+    writer(FILE *f);
+    ~writer();
+
+    void object(std::function<void()>);
+    void object(const char *key, std::function<void()>);
+
+    void array(std::function<void()>);
+    void array(const char *key, std::function<void()>);
+
+    void boolean(bool value);
+    void boolean(const char *key, bool value);
+
+    void number(uint64_t value);
+    void number(const char *key, uint64_t value);
+
+    void string(const char *s);
+    void string(const char *key, const char *s);
+
+    __printflike(2, 3)
+    void stringf(const char *fmt, ...);
+
+    __printflike(3, 4)
+    void stringf(const char *key, const char *fmt, ...);
+};
+
+}
+
+#endif /* _OBJC_OBJCDT_JSON_H_ */
diff --git a/objcdt/json.mm b/objcdt/json.mm
new file mode 100644 (file)
index 0000000..7eb7488
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+* Copyright (c) 2019 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 <assert.h>
+#include "json.h"
+
+namespace json {
+
+static bool
+context_is_value(context c)
+{
+    return c == root || c == array_value || c == object_value;
+}
+
+writer::writer(FILE *f)
+: _file(f)
+, _context(root)
+, _depth(0)
+, _needs_comma(false)
+{
+}
+
+writer::~writer()
+{
+    fputc('\n', _file);
+    fflush(_file);
+}
+
+void
+writer::begin_value(int sep)
+{
+    if (_needs_comma) {
+        _needs_comma = false;
+        if (sep) {
+            fprintf(_file, ", %c\n", sep);
+            return;
+        }
+        fputs(",\n", _file);
+    }
+    if (_context == array_value || _context == object_key) {
+        fprintf(_file, "%*s", _depth * 2, "");
+    }
+    if (sep) {
+        fprintf(_file, "%c\n", sep);
+    }
+}
+
+void
+writer::advance(context c)
+{
+    switch (c) {
+    case root:
+        _context = done;
+        _needs_comma = false;
+        break;
+    case array_value:
+        _context = array_value;
+        _needs_comma = true;
+        break;
+    case object_value:
+        _context = object_key;
+        _needs_comma = true;
+        break;
+    case object_key:
+        _context = object_value;
+        _needs_comma = false;
+        break;
+    case done:
+        assert(false);
+        break;
+    }
+}
+
+void
+writer::key(const char *key)
+{
+    assert(_context == object_key);
+
+    begin_value();
+    fprintf(_file, "\"%s\": ", key);
+    advance(_context);
+}
+
+void
+writer::object(std::function<void()> f)
+{
+    context old = _context;
+    assert(context_is_value(old));
+
+    begin_value('{');
+
+    _depth++;
+    _context = object_key;
+    _needs_comma = false;
+    f();
+
+    _depth--;
+    fprintf(_file, "\n%*s}", _depth * 2, "");
+    advance(old);
+}
+
+void
+writer::object(const char *k, std::function<void()> f)
+{
+    key(k);
+    object(f);
+}
+
+void
+writer::array(std::function<void()> f)
+{
+    context old = _context;
+    assert(context_is_value(old));
+
+    begin_value('[');
+
+    _depth++;
+    _context = array_value;
+    _needs_comma = false;
+    f();
+
+    _depth--;
+    fprintf(_file, "\n%*s]", _depth * 2, "");
+    advance(old);
+}
+
+void
+writer::array(const char *k, std::function<void()> f)
+{
+    key(k);
+    array(f);
+}
+
+void
+writer::boolean(bool value)
+{
+    assert(context_is_value(_context));
+    begin_value();
+    fputs(value ? "true" : "false", _file);
+    advance(_context);
+}
+
+void
+writer::boolean(const char *k, bool value)
+{
+    key(k);
+    boolean(value);
+}
+
+void
+writer::number(uint64_t value)
+{
+    assert(context_is_value(_context));
+    begin_value();
+    fprintf(_file, "%lld", value);
+    advance(_context);
+}
+
+void
+writer::number(const char *k, uint64_t value)
+{
+    key(k);
+    number(value);
+}
+
+void
+writer::string(const char *s)
+{
+    assert(context_is_value(_context));
+    begin_value();
+    fprintf(_file, "\"%s\"", s);
+    advance(_context);
+}
+
+void
+writer::string(const char *k, const char *s)
+{
+    key(k);
+    string(s);
+}
+
+void
+writer::stringf(const char *fmt, ...)
+{
+    va_list ap;
+
+    assert(context_is_value(_context));
+    begin_value();
+    fputc('"', _file);
+    va_start(ap, fmt);
+    vfprintf(_file, fmt, ap);
+    va_end(ap);
+    fputc('"', _file);
+    advance(_context);
+}
+
+void
+writer::stringf(const char *k, const char *fmt, ...)
+{
+    va_list ap;
+
+    key(k);
+
+    assert(context_is_value(_context));
+    begin_value();
+    fputc('"', _file);
+    va_start(ap, fmt);
+    vfprintf(_file, fmt, ap);
+    va_end(ap);
+    fputc('"', _file);
+    advance(_context);
+}
+
+} // json
diff --git a/objcdt/objcdt-entitlements.plist b/objcdt/objcdt-entitlements.plist
new file mode 100644 (file)
index 0000000..b7b4e6c
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>task_for_pid-allow</key>
+       <true/>
+       <key>com.apple.system-task-ports</key>
+       <true/>
+</dict>
+</plist>
diff --git a/objcdt/objcdt.1 b/objcdt/objcdt.1
new file mode 100644 (file)
index 0000000..5522491
--- /dev/null
@@ -0,0 +1,20 @@
+.\" Copyright (c) 2019, Apple Computer, Inc.  All rights reserved.
+.\"
+.Dd September 9, 2019   \" DATE
+.Dt objcdt 1           \" Program name and manual section number
+.Os "OS X"
+.Sh NAME
+.Nm objcdt
+.Nd Tool to debug objective-C usage in live processes
+.Sh SYNOPSIS
+.Nm objcdt
+.Sh DESCRIPTION
+The
+.Nm
+utility is a small CLI with embedded help that can dump some information about
+the Objective-C runtime state in live processes.
+.Pp
+Help can be obtained using
+.Nm
+.Ar help
+.Ed
diff --git a/objcdt/objcdt.mm b/objcdt/objcdt.mm
new file mode 100644 (file)
index 0000000..81a5a49
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+* Copyright (c) 2019 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 "objc-private.h"
+#include "objc-ptrauth.h"
+#include <stdio.h>
+#include <sysexits.h>
+#include <getopt.h>
+#include <pthread.h>
+
+int main(int argc, const char *argv[])
+{
+    return EX_UNAVAILABLE;
+}
index b1a9aeca3e0874c5ce0f2ca46eb7db7d9e11d0b5..67317c7ee5d0cb1609ac5d8d98f8a6d24e28c903 100644 (file)
@@ -222,6 +222,40 @@ LExit$0:
 .endmacro
 
 
+//////////////////////////////////////////////////////////////////////
+//
+// SAVE_REGS
+//
+// Create a stack frame and save all argument registers in preparation
+// for a function call.
+//////////////////////////////////////////////////////////////////////
+
+.macro SAVE_REGS
+
+       stmfd   sp!, {r0-r3,r7,lr}
+       add     r7, sp, #16
+       sub     sp, #8                  // align stack
+       FP_SAVE
+
+.endmacro
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// RESTORE_REGS
+//
+// Restore all argument registers and pop the stack frame created by
+// SAVE_REGS.
+//////////////////////////////////////////////////////////////////////
+
+.macro RESTORE_REGS
+
+       FP_RESTORE
+       add     sp, #8                  // align stack
+       ldmfd   sp!, {r0-r3,r7,lr}
+
+.endmacro
+
 /////////////////////////////////////////////////////////////////////
 //
 // CacheLookup NORMAL|STRET <function>
@@ -666,10 +700,7 @@ LNilReceiver:
        
 .macro MethodTableLookup
        
-       stmfd   sp!, {r0-r3,r7,lr}
-       add     r7, sp, #16
-       sub     sp, #8                  // align stack
-       FP_SAVE
+       SAVE_REGS
 
        // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
 .if $0 == NORMAL
@@ -690,9 +721,7 @@ LNilReceiver:
        tst     r12, r12                // set ne for stret forwarding
 .endif
 
-       FP_RESTORE
-       add     sp, #8                  // align stack
-       ldmfd   sp!, {r0-r3,r7,lr}
+       RESTORE_REGS
 
 .endmacro
 
@@ -819,18 +848,55 @@ LNilReceiver:
 
 
        ENTRY _method_invoke
+
+       // See if this is a small method.
+       lsls    r12, r1, #31
+       bne.w   L_method_invoke_small
+
+       // We can directly load the IMP from big methods.
        // r1 is method triplet instead of SEL
        ldr     r12, [r1, #METHOD_IMP]
        ldr     r1, [r1, #METHOD_NAME]
        bx      r12
+
+L_method_invoke_small:
+       // Small methods require a call to handle swizzling.
+       SAVE_REGS
+       mov     r0, r1
+       bl      __method_getImplementationAndName
+       mov     r12, r0
+       mov     r9, r1
+       RESTORE_REGS
+       mov     r1, r9
+       bx      r12
+
+
        END_ENTRY _method_invoke
 
 
        ENTRY _method_invoke_stret
+
+       // See if this is a small method.
+       lsls    r12, r2, #31
+       bne.w   L_method_invoke_stret_small
+
+       // We can directly load the IMP from big methods.
        // r2 is method triplet instead of SEL
        ldr     r12, [r2, #METHOD_IMP]
        ldr     r2, [r2, #METHOD_NAME]
        bx      r12
+
+L_method_invoke_stret_small:
+       // Small methods require a call to handle swizzling.
+       SAVE_REGS
+       mov     r0, r2
+       bl      __method_getImplementationAndName
+       mov     r12, r0
+       mov     r9, r1
+       RESTORE_REGS
+       mov     r2, r9
+       bx      r12
+
        END_ENTRY _method_invoke_stret
        
 
index 6bf3f2948c834e7272fdf0cf827b0ef5ed6dd5b0..595b03ee309b55b2a0883617e57cdc3c794b8203 100755 (executable)
@@ -169,6 +169,63 @@ LExit$0:
 #define FrameWithNoSaves 0x04000000  // frame, no non-volatile saves
 
 
+//////////////////////////////////////////////////////////////////////
+//
+// SAVE_REGS
+//
+// Create a stack frame and save all argument registers in preparation
+// for a function call.
+//////////////////////////////////////////////////////////////////////
+
+.macro SAVE_REGS
+
+    // push frame
+    SignLR
+    stp    fp, lr, [sp, #-16]!
+    mov    fp, sp
+
+    // save parameter registers: x0..x8, q0..q7
+    sub    sp, sp, #(10*8 + 8*16)
+    stp    q0, q1, [sp, #(0*16)]
+    stp    q2, q3, [sp, #(2*16)]
+    stp    q4, q5, [sp, #(4*16)]
+    stp    q6, q7, [sp, #(6*16)]
+    stp    x0, x1, [sp, #(8*16+0*8)]
+    stp    x2, x3, [sp, #(8*16+2*8)]
+    stp    x4, x5, [sp, #(8*16+4*8)]
+    stp    x6, x7, [sp, #(8*16+6*8)]
+    str    x8,     [sp, #(8*16+8*8)]
+
+.endmacro
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// RESTORE_REGS
+//
+// Restore all argument registers and pop the stack frame created by
+// SAVE_REGS.
+//////////////////////////////////////////////////////////////////////
+
+.macro RESTORE_REGS
+
+    ldp    q0, q1, [sp, #(0*16)]
+    ldp    q2, q3, [sp, #(2*16)]
+    ldp    q4, q5, [sp, #(4*16)]
+    ldp    q6, q7, [sp, #(6*16)]
+    ldp    x0, x1, [sp, #(8*16+0*8)]
+    ldp    x2, x3, [sp, #(8*16+2*8)]
+    ldp    x4, x5, [sp, #(8*16+4*8)]
+    ldp    x6, x7, [sp, #(8*16+6*8)]
+    ldr    x8,     [sp, #(8*16+8*8)]
+
+    mov    sp, fp
+    ldp    fp, lr, [sp], #16
+    AuthenticateLR
+
+.endmacro
+
+
 /********************************************************************
  *
  * CacheLookup NORMAL|GETIMP|LOOKUP <function>
@@ -494,22 +551,7 @@ LLookup_Nil:
 
 .macro MethodTableLookup
        
-       // push frame
-       SignLR
-       stp     fp, lr, [sp, #-16]!
-       mov     fp, sp
-
-       // save parameter registers: x0..x8, q0..q7
-       sub     sp, sp, #(10*8 + 8*16)
-       stp     q0, q1, [sp, #(0*16)]
-       stp     q2, q3, [sp, #(2*16)]
-       stp     q4, q5, [sp, #(4*16)]
-       stp     q6, q7, [sp, #(6*16)]
-       stp     x0, x1, [sp, #(8*16+0*8)]
-       stp     x2, x3, [sp, #(8*16+2*8)]
-       stp     x4, x5, [sp, #(8*16+4*8)]
-       stp     x6, x7, [sp, #(8*16+6*8)]
-       str     x8,     [sp, #(8*16+8*8)]
+       SAVE_REGS
 
        // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
        // receiver and selector already in x0 and x1
@@ -519,21 +561,8 @@ LLookup_Nil:
 
        // IMP in x0
        mov     x17, x0
-       
-       // restore registers and return
-       ldp     q0, q1, [sp, #(0*16)]
-       ldp     q2, q3, [sp, #(2*16)]
-       ldp     q4, q5, [sp, #(4*16)]
-       ldp     q6, q7, [sp, #(6*16)]
-       ldp     x0, x1, [sp, #(8*16+0*8)]
-       ldp     x2, x3, [sp, #(8*16+2*8)]
-       ldp     x4, x5, [sp, #(8*16+4*8)]
-       ldp     x6, x7, [sp, #(8*16+6*8)]
-       ldr     x8,     [sp, #(8*16+8*8)]
-
-       mov     sp, fp
-       ldp     fp, lr, [sp], #16
-       AuthenticateLR
+
+       RESTORE_REGS
 
 .endmacro
 
@@ -615,11 +644,37 @@ LGetImpMiss:
 
        
        ENTRY _method_invoke
+
+       // See if this is a small method.
+       tbnz    p1, #0, L_method_invoke_small
+
+       // We can directly load the IMP from big methods.
        // x1 is method triplet instead of SEL
        add     p16, p1, #METHOD_IMP
        ldr     p17, [x16]
        ldr     p1, [x1, #METHOD_NAME]
        TailCallMethodListImp x17, x16
+
+L_method_invoke_small:
+       // Small methods require a call to handle swizzling.
+       SAVE_REGS
+       mov     p0, p1
+       bl      __method_getImplementationAndName
+       // ARM64_32 packs both return values into x0, with SEL in the high bits and IMP in the low.
+       // ARM64 just returns them in x0 and x1.
+       mov     x17, x0
+#if __LP64__
+       mov     x16, x1
+#endif
+       RESTORE_REGS
+#if __LP64__
+       mov     x1, x16
+#else
+       lsr     x1, x17, #32
+       mov     w17, w17
+#endif
+       TailCallFunctionPointer x17
+
        END_ENTRY _method_invoke
 
 #endif
index 727b9831ce642a5439097c24ba03ba8b56aeb44b..914a9ac950ccef679110044293605b4a8a2689c5 100644 (file)
@@ -192,6 +192,47 @@ LExit$0:
 #define FrameWithNoSaves 0x01000000  // frame, no non-volatile saves
 
 
+//////////////////////////////////////////////////////////////////////
+//
+// SAVE_REGS
+//
+// Create a stack frame and save all argument registers in preparation
+// for a function call.
+//////////////////////////////////////////////////////////////////////
+
+.macro SAVE_REGS
+
+       pushl   %ebp
+       movl    %esp, %ebp
+
+       subl    $$(8+5*16), %esp
+
+       movdqa  %xmm3, 4*16(%esp)
+       movdqa  %xmm2, 3*16(%esp)
+       movdqa  %xmm1, 2*16(%esp)
+       movdqa  %xmm0, 1*16(%esp)
+
+.endmacro
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// RESTORE_REGS
+//
+// Restore all argument registers and pop the stack frame created by
+// SAVE_REGS.
+//////////////////////////////////////////////////////////////////////
+
+.macro RESTORE_REGS
+
+       movdqa  4*16(%esp), %xmm3
+       movdqa  3*16(%esp), %xmm2
+       movdqa  2*16(%esp), %xmm1
+       movdqa  1*16(%esp), %xmm0
+
+       leave
+
+.endmacro
 /////////////////////////////////////////////////////////////////////
 //
 // CacheLookup return-type, caller
@@ -314,10 +355,7 @@ LExit$0:
 /////////////////////////////////////////////////////////////////////
 
 .macro MethodTableLookup
-       pushl   %ebp
-       movl    %esp, %ebp
-
-       subl    $$(8+5*16), %esp
+       SAVE_REGS
 
 .if $0 == NORMAL
        movl    self+4(%ebp), %eax
@@ -327,11 +365,6 @@ LExit$0:
        movl    selector_stret+4(%ebp), %ecx
 .endif
        
-       movdqa  %xmm3, 4*16(%esp)
-       movdqa  %xmm2, 3*16(%esp)
-       movdqa  %xmm1, 2*16(%esp)
-       movdqa  %xmm0, 1*16(%esp)
-       
        // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
        movl    $$3,  12(%esp)          // LOOKUP_INITIALIZE | LOOKUP_RESOLVER
        movl    %edx, 8(%esp)           // class
@@ -341,18 +374,13 @@ LExit$0:
 
        // imp in eax
 
-       movdqa  4*16(%esp), %xmm3
-       movdqa  3*16(%esp), %xmm2
-       movdqa  2*16(%esp), %xmm1
-       movdqa  1*16(%esp), %xmm0
-
 .if $0 == NORMAL
        test    %eax, %eax      // set ne for stret forwarding
 .else
        cmp     %eax, %eax      // set eq for nonstret forwarding
 .endif
 
-       leave
+       RESTORE_REGS
 
 .endmacro
 
@@ -906,23 +934,55 @@ L_forward_stret_handler:
 
        ENTRY _method_invoke
 
+       // See if this is a small method.
+       testb   $1, selector(%esp)
+       jnz     L_method_invoke_small
+
+       // We can directly load the IMP from big methods.
        movl    selector(%esp), %ecx
        movl    method_name(%ecx), %edx
        movl    method_imp(%ecx), %eax
        movl    %edx, selector(%esp)
        jmp     *%eax
-       
+
+L_method_invoke_small:
+       // Small methods require a call to handle swizzling.
+       SAVE_REGS
+
+       movl    selector+4(%ebp), %eax
+       movl    %eax, 0(%esp)
+       call    __method_getImplementationAndName
+       RESTORE_REGS
+       movl    %edx, selector(%esp)
+       jmp     *%eax
+
        END_ENTRY _method_invoke
 
 
        ENTRY _method_invoke_stret
 
+       // See if this is a small method.
+       testb   $1, selector_stret(%esp)
+       jnz     L_method_invoke_stret_small
+
+       // We can directly load the IMP from big methods.
        movl    selector_stret(%esp), %ecx
        movl    method_name(%ecx), %edx
        movl    method_imp(%ecx), %eax
        movl    %edx, selector_stret(%esp)
        jmp     *%eax
        
+L_method_invoke_stret_small:
+       // Small methods require a call to handle swizzling.
+       SAVE_REGS
+
+       movl    selector_stret+4(%ebp), %eax
+       movl    %eax, 0(%esp)
+       call    __method_getImplementationAndName
+       RESTORE_REGS
+       movl    %edx, selector_stret(%esp)
+       jmp     *%eax
+
        END_ENTRY _method_invoke_stret
        
 
index a5410c46d315506f887efe0af96cb3bafc311ad3..9186278405322f23ca54e1811de7842dd1742a70 100644 (file)
@@ -93,6 +93,7 @@ _objc_restartableRanges:
 #define a2b sil
 #define a3  rdx
 #define a3d edx
+#define a3b dl
 #define a4  rcx
 #define a4d ecx
 #define a5  r8
@@ -212,6 +213,70 @@ LExit$0:
 #define FrameWithNoSaves 0x01000000  // frame, no non-volatile saves
 
 
+//////////////////////////////////////////////////////////////////////
+//
+// SAVE_REGS
+//
+// Create a stack frame and save all argument registers in preparation
+// for a function call.
+//////////////////////////////////////////////////////////////////////
+
+.macro SAVE_REGS
+
+       push    %rbp
+       mov     %rsp, %rbp
+
+       sub     $$0x80+8, %rsp          // +8 for alignment
+
+       movdqa  %xmm0, -0x80(%rbp)
+       push    %rax                    // might be xmm parameter count
+       movdqa  %xmm1, -0x70(%rbp)
+       push    %a1
+       movdqa  %xmm2, -0x60(%rbp)
+       push    %a2
+       movdqa  %xmm3, -0x50(%rbp)
+       push    %a3
+       movdqa  %xmm4, -0x40(%rbp)
+       push    %a4
+       movdqa  %xmm5, -0x30(%rbp)
+       push    %a5
+       movdqa  %xmm6, -0x20(%rbp)
+       push    %a6
+       movdqa  %xmm7, -0x10(%rbp)
+
+.endmacro
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// RESTORE_REGS
+//
+// Restore all argument registers and pop the stack frame created by
+// SAVE_REGS.
+//////////////////////////////////////////////////////////////////////
+
+.macro RESTORE_REGS
+
+       movdqa  -0x80(%rbp), %xmm0
+       pop     %a6
+       movdqa  -0x70(%rbp), %xmm1
+       pop     %a5
+       movdqa  -0x60(%rbp), %xmm2
+       pop     %a4
+       movdqa  -0x50(%rbp), %xmm3
+       pop     %a3
+       movdqa  -0x40(%rbp), %xmm4
+       pop     %a2
+       movdqa  -0x30(%rbp), %xmm5
+       pop     %a1
+       movdqa  -0x20(%rbp), %xmm6
+       pop     %rax
+       movdqa  -0x10(%rbp), %xmm7
+       leave
+
+.endmacro
+
+
 /////////////////////////////////////////////////////////////////////
 //
 // CacheLookup return-type, caller
@@ -347,26 +412,7 @@ LExit$0:
 
 .macro MethodTableLookup
 
-       push    %rbp
-       mov     %rsp, %rbp
-       
-       sub     $$0x80+8, %rsp          // +8 for alignment
-
-       movdqa  %xmm0, -0x80(%rbp)
-       push    %rax                    // might be xmm parameter count
-       movdqa  %xmm1, -0x70(%rbp)
-       push    %a1
-       movdqa  %xmm2, -0x60(%rbp)
-       push    %a2
-       movdqa  %xmm3, -0x50(%rbp)
-       push    %a3
-       movdqa  %xmm4, -0x40(%rbp)
-       push    %a4
-       movdqa  %xmm5, -0x30(%rbp)
-       push    %a5
-       movdqa  %xmm6, -0x20(%rbp)
-       push    %a6
-       movdqa  %xmm7, -0x10(%rbp)
+       SAVE_REGS
 
        // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
 .if $0 == NORMAL
@@ -383,29 +429,13 @@ LExit$0:
        // IMP is now in %rax
        movq    %rax, %r11
 
-       movdqa  -0x80(%rbp), %xmm0
-       pop     %a6
-       movdqa  -0x70(%rbp), %xmm1
-       pop     %a5
-       movdqa  -0x60(%rbp), %xmm2
-       pop     %a4
-       movdqa  -0x50(%rbp), %xmm3
-       pop     %a3
-       movdqa  -0x40(%rbp), %xmm4
-       pop     %a2
-       movdqa  -0x30(%rbp), %xmm5
-       pop     %a1
-       movdqa  -0x20(%rbp), %xmm6
-       pop     %rax
-       movdqa  -0x10(%rbp), %xmm7
+       RESTORE_REGS
 
 .if $0 == NORMAL
        test    %r11, %r11              // set ne for stret forwarding
 .else
        cmp     %r11, %r11              // set eq for nonstret forwarding
 .endif
-       
-       leave
 
 .endmacro
 
@@ -1104,19 +1134,51 @@ LCacheMiss:
 
        ENTRY _method_invoke
 
+       // See if this is a small method.
+       testb   $1, %a2b
+       jnz     L_method_invoke_small
+
+       // We can directly load the IMP from big methods.
        movq    method_imp(%a2), %r11
        movq    method_name(%a2), %a2
        jmp     *%r11
-       
+
+L_method_invoke_small:
+       // Small methods require a call to handle swizzling.
+       SAVE_REGS
+       movq    %a2, %a1
+       call    __method_getImplementationAndName
+       movq    %rdx, %r10
+       movq    %rax, %r11
+       RESTORE_REGS
+       movq    %r10, %a2
+       jmp     *%r11
+
        END_ENTRY _method_invoke
 
 
        ENTRY _method_invoke_stret
 
+       // See if this is a small method.
+       testb   $1, %a3b
+       jnz     L_method_invoke_stret_small
+
+       // We can directly load the IMP from big methods.
        movq    method_imp(%a3), %r11
        movq    method_name(%a3), %a3
        jmp     *%r11
-       
+
+L_method_invoke_stret_small:
+       // Small methods require a call to handle swizzling.
+       SAVE_REGS
+       movq    %a3, %a1
+       call    __method_getImplementationAndName
+       movq    %rdx, %r10
+       movq    %rax, %r11
+       RESTORE_REGS
+       movq    %r10, %a3
+       jmp     *%r11
+
        END_ENTRY _method_invoke_stret
 
 
index 8fc6d48273f1a3f1369cf2857d9fd714825d4336..d0909958ac6830467927954eacf117f2d0fe3228 100644 (file)
@@ -102,6 +102,7 @@ _objc_restartableRanges:
 #define a2b sil
 #define a3  rdx
 #define a3d edx
+#define a3b dl
 #define a4  rcx
 #define a4d ecx
 #define a5  r8
@@ -218,6 +219,70 @@ LExit$0:
 #define FrameWithNoSaves 0x01000000  // frame, no non-volatile saves
 
 
+//////////////////////////////////////////////////////////////////////
+//
+// SAVE_REGS
+//
+// Create a stack frame and save all argument registers in preparation
+// for a function call.
+//////////////////////////////////////////////////////////////////////
+
+.macro SAVE_REGS
+
+       push    %rbp
+       mov     %rsp, %rbp
+
+       sub     $$0x80+8, %rsp          // +8 for alignment
+
+       movdqa  %xmm0, -0x80(%rbp)
+       push    %rax                    // might be xmm parameter count
+       movdqa  %xmm1, -0x70(%rbp)
+       push    %a1
+       movdqa  %xmm2, -0x60(%rbp)
+       push    %a2
+       movdqa  %xmm3, -0x50(%rbp)
+       push    %a3
+       movdqa  %xmm4, -0x40(%rbp)
+       push    %a4
+       movdqa  %xmm5, -0x30(%rbp)
+       push    %a5
+       movdqa  %xmm6, -0x20(%rbp)
+       push    %a6
+       movdqa  %xmm7, -0x10(%rbp)
+
+.endmacro
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// RESTORE_REGS
+//
+// Restore all argument registers and pop the stack frame created by
+// SAVE_REGS.
+//////////////////////////////////////////////////////////////////////
+
+.macro RESTORE_REGS
+
+       movdqa  -0x80(%rbp), %xmm0
+       pop     %a6
+       movdqa  -0x70(%rbp), %xmm1
+       pop     %a5
+       movdqa  -0x60(%rbp), %xmm2
+       pop     %a4
+       movdqa  -0x50(%rbp), %xmm3
+       pop     %a3
+       movdqa  -0x40(%rbp), %xmm4
+       pop     %a2
+       movdqa  -0x30(%rbp), %xmm5
+       pop     %a1
+       movdqa  -0x20(%rbp), %xmm6
+       pop     %rax
+       movdqa  -0x10(%rbp), %xmm7
+       leave
+
+.endmacro
+
+
 /////////////////////////////////////////////////////////////////////
 //
 // CacheLookup return-type, caller, function
@@ -382,26 +447,7 @@ LLookupEnd$2:
 
 .macro MethodTableLookup
 
-       push    %rbp
-       mov     %rsp, %rbp
-       
-       sub     $$0x80+8, %rsp          // +8 for alignment
-
-       movdqa  %xmm0, -0x80(%rbp)
-       push    %rax                    // might be xmm parameter count
-       movdqa  %xmm1, -0x70(%rbp)
-       push    %a1
-       movdqa  %xmm2, -0x60(%rbp)
-       push    %a2
-       movdqa  %xmm3, -0x50(%rbp)
-       push    %a3
-       movdqa  %xmm4, -0x40(%rbp)
-       push    %a4
-       movdqa  %xmm5, -0x30(%rbp)
-       push    %a5
-       movdqa  %xmm6, -0x20(%rbp)
-       push    %a6
-       movdqa  %xmm7, -0x10(%rbp)
+       SAVE_REGS
 
        // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
 .if $0 == NORMAL
@@ -418,29 +464,13 @@ LLookupEnd$2:
        // IMP is now in %rax
        movq    %rax, %r11
 
-       movdqa  -0x80(%rbp), %xmm0
-       pop     %a6
-       movdqa  -0x70(%rbp), %xmm1
-       pop     %a5
-       movdqa  -0x60(%rbp), %xmm2
-       pop     %a4
-       movdqa  -0x50(%rbp), %xmm3
-       pop     %a3
-       movdqa  -0x40(%rbp), %xmm4
-       pop     %a2
-       movdqa  -0x30(%rbp), %xmm5
-       pop     %a1
-       movdqa  -0x20(%rbp), %xmm6
-       pop     %rax
-       movdqa  -0x10(%rbp), %xmm7
+       RESTORE_REGS
 
 .if $0 == NORMAL
        test    %r11, %r11              // set ne for nonstret forwarding
 .else
        cmp     %r11, %r11              // set eq for stret forwarding
 .endif
-       
-       leave
 
 .endmacro
 
@@ -1216,19 +1246,51 @@ LCacheMiss_objc_msgLookupSuper2_stret:
 
        ENTRY _method_invoke
 
+       // See if this is a small method.
+       testb   $1, %a2b
+       jnz     L_method_invoke_small
+
+       // We can directly load the IMP from big methods.
        movq    method_imp(%a2), %r11
        movq    method_name(%a2), %a2
        jmp     *%r11
-       
+
+L_method_invoke_small:
+       // Small methods require a call to handle swizzling.
+       SAVE_REGS
+       movq    %a2, %a1
+       call    __method_getImplementationAndName
+       movq    %rdx, %r10
+       movq    %rax, %r11
+       RESTORE_REGS
+       movq    %r10, %a2
+       jmp     *%r11
+
        END_ENTRY _method_invoke
 
 
        ENTRY _method_invoke_stret
 
+       // See if this is a small method.
+       testb   $1, %a3b
+       jnz     L_method_invoke_stret_small
+
+       // We can directly load the IMP from big methods.
        movq    method_imp(%a3), %r11
        movq    method_name(%a3), %a3
        jmp     *%r11
-       
+
+L_method_invoke_stret_small:
+       // Small methods require a call to handle swizzling.
+       SAVE_REGS
+       movq    %a3, %a1
+       call    __method_getImplementationAndName
+       movq    %rdx, %r10
+       movq    %rax, %r11
+       RESTORE_REGS
+       movq    %r10, %a3
+       jmp     *%r11
+
        END_ENTRY _method_invoke_stret
 
 
index 85b7846932cb80df03c13930e58c815ad5a698d5..1108673b2cb6e3224d004350d4f84bf18844f02f 100644 (file)
@@ -59,12 +59,12 @@ struct PointerUnionTypeSelectorReturn<
       typename PointerUnionTypeSelector<T1, T2, RET_EQ, RET_NE>::Return;
 };
 
-template <class PT1, class PT2>
+template <class T1, class T2, typename Auth1, typename Auth2>
 class PointerUnion {
     uintptr_t _value;
 
-    static_assert(alignof(PT1) >= 2, "alignment requirement");
-    static_assert(alignof(PT2) >= 2, "alignment requirement");
+    static_assert(alignof(T1) >= 2, "alignment requirement");
+    static_assert(alignof(T2) >= 2, "alignment requirement");
 
     struct IsPT1 {
       static const uintptr_t Num = 0;
@@ -85,8 +85,12 @@ public:
     explicit PointerUnion(const std::atomic<uintptr_t> &raw)
     : _value(raw.load(std::memory_order_relaxed))
     { }
-    PointerUnion(PT1 t) : _value((uintptr_t)t) { }
-    PointerUnion(PT2 t) : _value((uintptr_t)t | 1) { }
+    PointerUnion(T1 *t, const void *address) {
+        _value = (uintptr_t)Auth1::sign(t, address);
+    }
+    PointerUnion(T2 *t, const void *address) {
+        _value = (uintptr_t)Auth2::sign(t, address) | 1;
+    }
 
     void storeAt(std::atomic<uintptr_t> &raw, std::memory_order order) const {
         raw.store(_value, order);
@@ -94,20 +98,24 @@ public:
 
     template <typename T>
     bool is() const {
-        using Ty = typename PointerUnionTypeSelector<PT1, T, IsPT1,
-            PointerUnionTypeSelector<PT2, T, IsPT2,
+        using Ty = typename PointerUnionTypeSelector<T1 *, T, IsPT1,
+            PointerUnionTypeSelector<T2 *, T, IsPT2,
             UNION_DOESNT_CONTAIN_TYPE<T>>>::Return;
         return getTag() == Ty::Num;
     }
 
-    template <typename T> T get() const {
-      ASSERT(is<T>() && "Invalid accessor called");
-      return reinterpret_cast<T>(getPointer());
+    template <typename T> T get(const void *address) const {
+        ASSERT(is<T>() && "Invalid accessor called");
+        using AuthT = typename PointerUnionTypeSelector<T1 *, T, Auth1,
+            PointerUnionTypeSelector<T2 *, T, Auth2,
+            UNION_DOESNT_CONTAIN_TYPE<T>>>::Return;
+
+        return AuthT::auth((T)getPointer(), address);
     }
 
-    template <typename T> T dyn_cast() const {
+    template <typename T> T dyn_cast(const void *address) const {
       if (is<T>())
-        return get<T>();
+        return get<T>(address);
       return T();
     }
 };
index 4e6ca11309fdb2e82657977e04980519f6d57db0..7656391f386e070ecb5e30cb101ea6d5e4a64659 100644 (file)
@@ -99,6 +99,18 @@ static void cache_collect_free(struct bucket_t *data, mask_t capacity);
 static int _collecting_in_critical(void);
 static void _garbage_make_room(void);
 
+#if DEBUG_TASK_THREADS
+static kern_return_t objc_task_threads
+(
+       task_t target_task,
+       thread_act_array_t *act_list,
+       mach_msg_type_number_t *act_listCnt
+);
+#endif
+
+#if DEBUG_TASK_THREADS
+#undef HAVE_TASK_RESTARTABLE_RANGES
+#endif
 
 /***********************************************************************
 * Cache statistics for OBJC_PRINT_CACHE_SETUP
index 23c0da1542783442445b6c9b4818f9a9e8db2286..3dc54c788060938706d4fc548262618e9dcf8e5a 100644 (file)
@@ -38,10 +38,14 @@ extern message_ref_t *_getObjc2MessageRefs(const header_info *hi, size_t *count)
 extern Class*_getObjc2ClassRefs(const header_info *hi, size_t *count);
 extern Class*_getObjc2SuperRefs(const header_info *hi, size_t *count);
 extern classref_t const *_getObjc2ClassList(const header_info *hi, size_t *count);
-extern classref_t const *_getObjc2NonlazyClassList(const header_info *hi, size_t *count);
-extern category_t * const *_getObjc2CategoryList(const header_info *hi, size_t *count);
-extern category_t * const *_getObjc2CategoryList2(const header_info *hi, size_t *count);
-extern category_t * const *_getObjc2NonlazyCategoryList(const header_info *hi, size_t *count);
+// Use hi->nlclslist() instead
+// extern classref_t const *_getObjc2NonlazyClassList(const header_info *hi, size_t *count);
+// Use hi->catlist() instead
+// extern category_t * const *_getObjc2CategoryList(const header_info *hi, size_t *count);
+// Use hi->catlist2() instead
+// extern category_t * const *_getObjc2CategoryList2(const header_info *hi, size_t *count);
+// Use hi->nlcatlist() instead
+// extern category_t * const *_getObjc2NonlazyCategoryList(const header_info *hi, size_t *count);
 extern protocol_t * const *_getObjc2ProtocolList(const header_info *hi, size_t *count);
 extern protocol_t **_getObjc2ProtocolRefs(const header_info *hi, size_t *count);
 
@@ -62,6 +66,8 @@ public:
 extern UnsignedInitializer *getLibobjcInitializers(const header_info *hi, size_t *count);
 
 extern classref_t const *_getObjc2NonlazyClassList(const headerType *mhdr, size_t *count);
+extern category_t * const *_getObjc2CategoryList(const headerType *mhdr, size_t *count);
+extern category_t * const *_getObjc2CategoryList2(const headerType *mhdr, size_t *count);
 extern category_t * const *_getObjc2NonlazyCategoryList(const headerType *mhdr, size_t *count);
 extern UnsignedInitializer *getLibobjcInitializers(const headerType *mhdr, size_t *count);
 
index afc82f51e982d4bccd47eb98a01dfe09950917b2..112804d029160040a5df7325f44cd3d539496331 100644 (file)
@@ -287,6 +287,8 @@ enum
     OBJC_TAG_UIColor           = 17,
     OBJC_TAG_CGColor           = 18,
     OBJC_TAG_NSIndexSet        = 19,
+    OBJC_TAG_NSMethodSignature = 20,
+    OBJC_TAG_UTTypeRecord      = 21,
 
     OBJC_TAG_First60BitPayload = 0, 
     OBJC_TAG_Last60BitPayload  = 6, 
@@ -944,7 +946,7 @@ typedef enum {
         }                                                                       \
     }                                                                           \
     -(NSUInteger)retainCount {                                                  \
-        return (_rc_ivar + 2) >> 1;                                             \
+        return (NSUInteger)(_rc_ivar + 2) >> 1;                                 \
     }                                                                           \
     -(BOOL)_tryRetain {                                                         \
         __typeof__(_rc_ivar) _prev;                                             \
@@ -966,12 +968,12 @@ typedef enum {
         } else if (_rc_ivar < -2) {                                             \
             __builtin_trap(); /* BUG: over-release elsewhere */                 \
         }                                                                       \
-        return _rc_ivar & 1;                                                    \
+        return (_rc_ivar & 1) != 0;                                             \
     }
 
 #define _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC(_rc_ivar, _dealloc2main)            \
     _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC_BLOCK(_rc_ivar, (^(id _self_ __attribute__((unused))) { \
-        if (_dealloc2main && !pthread_main_np()) {                              \
+        if ((_dealloc2main) && !pthread_main_np()) {                            \
             return _OBJC_DEALLOC_OBJECT_LATER;                                  \
         } else {                                                                \
             return _OBJC_DEALLOC_OBJECT_NOW;                                    \
index f4afb20dd17ccd09bbf576b6d4e7d692a5f7767b..b21869b44cd9bd5ed29caa95064c586b9067ae29 100644 (file)
 */
 
 #include "objc-private.h"
+#include "objc-os.h"
+#include "objc-file.h"
 
 
 #if !SUPPORT_PREOPT
 // Preoptimization not supported on this platform.
 
-struct objc_selopt_t;
-
 bool isPreoptimized(void) 
 {
     return false;
@@ -64,16 +64,6 @@ bool header_info::hasPreoptimizedProtocols() const
     return false;
 }
 
-objc_selopt_t *preoptimizedSelectors(void) 
-{
-    return nil;
-}
-
-bool sharedCacheSupportsProtocolRoots(void)
-{
-    return false;
-}
-
 Protocol *getPreoptimizedProtocol(const char *name)
 {
     return nil;
@@ -123,7 +113,6 @@ void preopt_init(void)
 #include <objc-shared-cache.h>
 
 using objc_opt::objc_stringhash_offset_t;
-using objc_opt::objc_protocolopt_t;
 using objc_opt::objc_protocolopt2_t;
 using objc_opt::objc_clsopt_t;
 using objc_opt::objc_headeropt_ro_t;
@@ -141,6 +130,62 @@ static bool preoptimized;
 
 extern const objc_opt_t _objc_opt_data;  // in __TEXT, __objc_opt_ro
 
+namespace objc_opt {
+struct objc_headeropt_ro_t {
+    uint32_t count;
+    uint32_t entsize;
+    header_info headers[0];  // sorted by mhdr address
+
+    header_info& getOrEnd(uint32_t i) const {
+        ASSERT(i <= count);
+        return *(header_info *)((uint8_t *)&headers + (i * entsize));
+    }
+
+    header_info& get(uint32_t i) const {
+        ASSERT(i < count);
+        return *(header_info *)((uint8_t *)&headers + (i * entsize));
+    }
+
+    uint32_t index(const header_info* hi) const {
+        const header_info* begin = &get(0);
+        const header_info* end = &getOrEnd(count);
+        ASSERT(hi >= begin && hi < end);
+        return (uint32_t)(((uintptr_t)hi - (uintptr_t)begin) / entsize);
+    }
+
+    header_info *get(const headerType *mhdr)
+    {
+        int32_t start = 0;
+        int32_t end = count;
+        while (start <= end) {
+            int32_t i = (start+end)/2;
+            header_info &hi = get(i);
+            if (mhdr == hi.mhdr()) return &hi;
+            else if (mhdr < hi.mhdr()) end = i-1;
+            else start = i+1;
+        }
+
+#if DEBUG
+        for (uint32_t i = 0; i < count; i++) {
+            header_info &hi = get(i);
+            if (mhdr == hi.mhdr()) {
+                _objc_fatal("failed to find header %p (%d/%d)",
+                            mhdr, i, count);
+            }
+        }
+#endif
+
+        return nil;
+    }
+};
+
+struct objc_headeropt_rw_t {
+    uint32_t count;
+    uint32_t entsize;
+    header_info_rw headers[0];  // sorted by mhdr address
+};
+};
+
 /***********************************************************************
 * Return YES if we have a valid optimized shared cache.
 **********************************************************************/
@@ -199,38 +244,114 @@ bool header_info::hasPreoptimizedProtocols() const
     return info()->optimizedByDyld() || info()->optimizedByDyldClosure();
 }
 
+bool header_info::hasPreoptimizedSectionLookups() const
+{
+    objc_opt::objc_headeropt_ro_t *hinfoRO = opt->headeropt_ro();
+    if (hinfoRO->entsize == (2 * sizeof(intptr_t)))
+        return NO;
 
-objc_selopt_t *preoptimizedSelectors(void) 
+    return YES;
+}
+
+const classref_t *header_info::nlclslist(size_t *outCount) const
 {
-    return opt ? opt->selopt() : nil;
+#if __OBJC2__
+    // This field is new, so temporarily be resilient to the shared cache
+    // not generating it
+    if (isPreoptimized() && hasPreoptimizedSectionLookups()) {
+          *outCount = nlclslist_count;
+          const classref_t *list = (const classref_t *)(((intptr_t)&nlclslist_offset) + nlclslist_offset);
+      #if DEBUG
+          size_t debugCount;
+          assert((list == _getObjc2NonlazyClassList(mhdr(), &debugCount)) && (*outCount == debugCount));
+      #endif
+          return list;
+    }
+    return _getObjc2NonlazyClassList(mhdr(), outCount);
+#else
+    return NULL;
+#endif
 }
 
-bool sharedCacheSupportsProtocolRoots(void)
+category_t * const *header_info::nlcatlist(size_t *outCount) const
 {
-    return (opt != nil) && (opt->protocolopt2() != nil);
+#if __OBJC2__
+    // This field is new, so temporarily be resilient to the shared cache
+    // not generating it
+    if (isPreoptimized() && hasPreoptimizedSectionLookups()) {
+        *outCount = nlcatlist_count;
+        category_t * const *list = (category_t * const *)(((intptr_t)&nlcatlist_offset) + nlcatlist_offset);
+        #if DEBUG
+        size_t debugCount;
+        assert((list == _getObjc2NonlazyCategoryList(mhdr(), &debugCount)) && (*outCount == debugCount));
+        #endif
+        return list;
+    }
+    return _getObjc2NonlazyCategoryList(mhdr(), outCount);
+#else
+    return NULL;
+#endif
 }
 
+category_t * const *header_info::catlist(size_t *outCount) const
+{
+#if __OBJC2__
+    // This field is new, so temporarily be resilient to the shared cache
+    // not generating it
+    if (isPreoptimized() && hasPreoptimizedSectionLookups()) {
+      *outCount = catlist_count;
+      category_t * const *list = (category_t * const *)(((intptr_t)&catlist_offset) + catlist_offset);
+      #if DEBUG
+      size_t debugCount;
+      assert((list == _getObjc2CategoryList(mhdr(), &debugCount)) && (*outCount == debugCount));
+      #endif
+      return list;
+    }
+    return _getObjc2CategoryList(mhdr(), outCount);
+#else
+    return NULL;
+#endif
+}
 
-Protocol *getSharedCachePreoptimizedProtocol(const char *name)
+category_t * const *header_info::catlist2(size_t *outCount) const
 {
-    // Look in the new table if we have it
-    if (objc_protocolopt2_t *protocols2 = opt ? opt->protocolopt2() : nil) {
-        // Note, we have to pass the lambda directly here as otherwise we would try
-        // message copy and autorelease.
-        return (Protocol *)protocols2->getProtocol(name, [](const void* hi) -> bool {
-            return ((header_info *)hi)->isLoaded();
-        });
+#if __OBJC2__
+    // This field is new, so temporarily be resilient to the shared cache
+    // not generating it
+    if (isPreoptimized() && hasPreoptimizedSectionLookups()) {
+      *outCount = catlist2_count;
+      category_t * const *list = (category_t * const *)(((intptr_t)&catlist2_offset) + catlist2_offset);
+      #if DEBUG
+      size_t debugCount;
+      assert((list == _getObjc2CategoryList2(mhdr(), &debugCount)) && (*outCount == debugCount));
+      #endif
+      return list;
     }
+    return _getObjc2CategoryList2(mhdr(), outCount);
+#else
+    return NULL;
+#endif
+}
+
 
-    objc_protocolopt_t *protocols = opt ? opt->protocolopt() : nil;
+Protocol *getSharedCachePreoptimizedProtocol(const char *name)
+{
+    objc_protocolopt2_t *protocols = opt ? opt->protocolopt2() : nil;
     if (!protocols) return nil;
 
-    return (Protocol *)protocols->getProtocol(name);
+    // Note, we have to pass the lambda directly here as otherwise we would try
+    // message copy and autorelease.
+    return (Protocol *)protocols->getProtocol(name, [](const void* hi) -> bool {
+      return ((header_info *)hi)->isLoaded();
+    });
 }
 
 
 Protocol *getPreoptimizedProtocol(const char *name)
 {
+    objc_protocolopt2_t *protocols = opt ? opt->protocolopt2() : nil;
+    if (!protocols) return nil;
+
     // Try table from dyld closure first.  It was built to ignore the dupes it
     // knows will come from the cache, so anything left in here was there when
     // we launched
@@ -354,47 +475,6 @@ Class* copyPreoptimizedClasses(const char *name, int *outCount)
     return nil;
 }
 
-namespace objc_opt {
-struct objc_headeropt_ro_t {
-    uint32_t count;
-    uint32_t entsize;
-    header_info headers[0];  // sorted by mhdr address
-
-    header_info *get(const headerType *mhdr) 
-    {
-        ASSERT(entsize == sizeof(header_info));
-
-        int32_t start = 0;
-        int32_t end = count;
-        while (start <= end) {
-            int32_t i = (start+end)/2;
-            header_info *hi = headers+i;
-            if (mhdr == hi->mhdr()) return hi;
-            else if (mhdr < hi->mhdr()) end = i-1;
-            else start = i+1;
-        }
-
-#if DEBUG
-        for (uint32_t i = 0; i < count; i++) {
-            header_info *hi = headers+i;
-            if (mhdr == hi->mhdr()) {
-                _objc_fatal("failed to find header %p (%d/%d)", 
-                            mhdr, i, count);
-            }
-        }
-#endif
-
-        return nil;
-    }
-};
-
-struct objc_headeropt_rw_t {
-    uint32_t count;
-    uint32_t entsize;
-    header_info_rw headers[0];  // sorted by mhdr address
-};
-};
-
 
 header_info *preoptimizedHinfoForHeader(const headerType *mhdr)
 {
@@ -422,7 +502,7 @@ header_info_rw *getPreoptimizedHeaderRW(const struct header_info *const hdr)
         _objc_fatal("preoptimized header_info missing for %s (%p %p %p)",
                     hdr->fname(), hdr, hinfoRO, hinfoRW);
     }
-    int32_t index = (int32_t)(hdr - hinfoRO->headers);
+    int32_t index = hinfoRO->index(hdr);
     ASSERT(hinfoRW->entsize == sizeof(header_info_rw));
     return &hinfoRW->headers[index];
 }
index c28ba05ce9900e6463633e536130c94b7f90224b..5a0625245774d9018768bca487a0d2bfdbbbec5d 100644 (file)
@@ -1076,6 +1076,7 @@ ustrdupMaybeNil(const uint8_t *str)
     (unsigned  char)(((uint32_t)(v))>>8),   \
     (unsigned  char)(((uint32_t)(v))>>0)
 
+#ifndef __BUILDING_OBJCDT__
 // fork() safety requires careful tracking of all locks.
 // Our custom lock types check this in debug builds.
 // Disallow direct use of all other lock types.
@@ -1083,6 +1084,6 @@ typedef __darwin_pthread_mutex_t pthread_mutex_t UNAVAILABLE_ATTRIBUTE;
 typedef __darwin_pthread_rwlock_t pthread_rwlock_t UNAVAILABLE_ATTRIBUTE;
 typedef int32_t OSSpinLock UNAVAILABLE_ATTRIBUTE;
 typedef struct os_unfair_lock_s os_unfair_lock UNAVAILABLE_ATTRIBUTE;
-
+#endif
 
 #endif
index 7d600ef4a0ca962be94d42c167029b41574c96e7..db021d01bfa1be03ba22db3d7c58556c1c90268b 100644 (file)
@@ -492,11 +492,16 @@ map_images_nolock(unsigned mhCount, const char * const mhPaths[],
             if (mhdr->filetype == MH_EXECUTE) {
                 // Size some data structures based on main executable's size
 #if __OBJC2__
-                size_t count;
-                _getObjc2SelectorRefs(hi, &count);
-                selrefCount += count;
-                _getObjc2MessageRefs(hi, &count);
-                selrefCount += count;
+                // If dyld3 optimized the main executable, then there shouldn't
+                // be any selrefs needed in the dynamic map so we can just init
+                // to a 0 sized map
+                if ( !hi->hasPreoptimizedSelectors() ) {
+                  size_t count;
+                  _getObjc2SelectorRefs(hi, &count);
+                  selrefCount += count;
+                  _getObjc2MessageRefs(hi, &count);
+                  selrefCount += count;
+                }
 #else
                 _getObjcSelectorRefs(hi, &selrefCount);
 #endif
index 4d7aab23f5bc5efef2fd866d8f523d7d88ea5d07..bf2a8de88a486ba14800ea9377dfa49299531686 100644 (file)
 
 struct objc_class;
 struct objc_object;
+struct category_t;
 
 typedef struct objc_class *Class;
 typedef struct objc_object *id;
+typedef struct classref *classref_t;
 
 namespace {
     struct SideTable;
@@ -241,14 +243,6 @@ typedef struct old_property *objc_property_t;
 #include "objc-loadmethod.h"
 
 
-#if SUPPORT_PREOPT  &&  __cplusplus
-#include <objc-shared-cache.h>
-using objc_selopt_t = const objc_opt::objc_selopt_t;
-#else
-struct objc_selopt_t;
-#endif
-
-
 #define STRINGIFY(x) #x
 #define STRINGIFY2(x) STRINGIFY(x)
 
@@ -358,6 +352,22 @@ private:
     // from this location.
     intptr_t info_offset;
 
+    // Offset from this location to the non-lazy class list
+    intptr_t nlclslist_offset;
+    uintptr_t nlclslist_count;
+
+    // Offset from this location to the non-lazy category list
+    intptr_t nlcatlist_offset;
+    uintptr_t nlcatlist_count;
+
+    // Offset from this location to the category list
+    intptr_t catlist_offset;
+    uintptr_t catlist_count;
+
+    // Offset from this location to the category list 2
+    intptr_t catlist2_offset;
+    uintptr_t catlist2_count;
+
     // Do not add fields without editing ObjCModernAbstraction.hpp
 public:
 
@@ -384,6 +394,30 @@ public:
         info_offset = (intptr_t)info - (intptr_t)&info_offset;
     }
 
+    const classref_t *nlclslist(size_t *outCount) const;
+
+    void set_nlclslist(const void *list) {
+        nlclslist_offset = (intptr_t)list - (intptr_t)&nlclslist_offset;
+    }
+
+    category_t * const *nlcatlist(size_t *outCount) const;
+
+    void set_nlcatlist(const void *list) {
+        nlcatlist_offset = (intptr_t)list - (intptr_t)&nlcatlist_offset;
+    }
+
+    category_t * const *catlist(size_t *outCount) const;
+
+    void set_catlist(const void *list) {
+        catlist_offset = (intptr_t)list - (intptr_t)&catlist_offset;
+    }
+
+    category_t * const *catlist2(size_t *outCount) const;
+
+    void set_catlist2(const void *list) {
+        catlist2_offset = (intptr_t)list - (intptr_t)&catlist2_offset;
+    }
+
     bool isLoaded() {
         return getHeaderInfoRW()->getLoaded();
     }
@@ -424,6 +458,8 @@ public:
 
     bool hasPreoptimizedProtocols() const;
 
+    bool hasPreoptimizedSectionLookups() const;
+
 #if !__OBJC2__
     struct old_protocol **proto_refs;
     struct objc_module *mod_ptr;
@@ -497,9 +533,6 @@ extern bool isPreoptimized(void);
 extern bool noMissingWeakSuperclasses(void);
 extern header_info *preoptimizedHinfoForHeader(const headerType *mhdr);
 
-extern objc_selopt_t *preoptimizedSelectors(void);
-
-extern bool sharedCacheSupportsProtocolRoots(void);
 extern Protocol *getPreoptimizedProtocol(const char *name);
 extern Protocol *getSharedCachePreoptimizedProtocol(const char *name);
 
@@ -525,6 +558,14 @@ lookUpImpOrNil(id obj, SEL sel, Class cls, int behavior = 0)
 }
 
 extern IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel);
+
+struct IMPAndSEL {
+    IMP imp;
+    SEL sel;
+};
+
+extern IMPAndSEL _method_getImplementationAndName(Method m);
+
 extern BOOL class_respondsToSelector_inst(id inst, SEL sel, Class cls);
 extern Class class_initialize(Class cls, id inst);
 
@@ -971,7 +1012,7 @@ class ChainedHookFunction {
     std::atomic<Fn> hook{nil};
 
 public:
-    ChainedHookFunction(Fn f) : hook{f} { };
+    constexpr ChainedHookFunction(Fn f) : hook{f} { };
 
     Fn get() {
         return hook.load(std::memory_order_acquire);
@@ -990,10 +1031,10 @@ public:
 
 
 // A small vector for use as a global variable. Only supports appending and
-// iteration. Stores a single element inline, and multiple elements in a heap
+// iteration. Stores up to N elements inline, and multiple elements in a heap
 // allocation. There is no attempt to amortize reallocation cost; this is
-// intended to be used in situation where zero or one element is common, two
-// might happen, and three or more is very rare.
+// intended to be used in situation where a small number of elements is
+// common, more might happen, and significantly more is very rare.
 //
 // This does not clean up its allocation, and thus cannot be used as a local
 // variable or member of something with limited lifetime.
@@ -1006,7 +1047,7 @@ protected:
     unsigned count{0};
     union {
         T inlineElements[InlineCount];
-        T *elements;
+        T *elements{nullptr};
     };
     
 public:
index e275dcadd8c91af65aed0119ed4349a3c65e858d..388abe6258b72dff9ae96c6e3a13e30200367403 100644 (file)
@@ -76,5 +76,119 @@ using MethodListIMP = IMP;
 
 #endif
 
+// A struct that wraps a pointer using the provided template.
+// The provided Auth parameter is used to sign and authenticate
+// the pointer as it is read and written.
+template<typename T, typename Auth>
+struct WrappedPtr {
+private:
+    T *ptr;
+
+public:
+    WrappedPtr(T *p) {
+        *this = p;
+    }
+
+    WrappedPtr(const WrappedPtr<T, Auth> &p) {
+        *this = p;
+    }
+
+    WrappedPtr<T, Auth> &operator =(T *p) {
+        ptr = Auth::sign(p, &ptr);
+        return *this;
+    }
+
+    WrappedPtr<T, Auth> &operator =(const WrappedPtr<T, Auth> &p) {
+        *this = (T *)p;
+        return *this;
+    }
+
+    operator T*() const { return get(); }
+    T *operator->() const { return get(); }
+
+    T *get() const { return Auth::auth(ptr, &ptr); }
+
+    // When asserts are enabled, ensure that we can read a byte from
+    // the underlying pointer. This can be used to catch ptrauth
+    // errors early for easier debugging.
+    void validate() const {
+#if !NDEBUG
+        char *p = (char *)get();
+        char dummy;
+        memset_s(&dummy, 1, *p, 1);
+        ASSERT(dummy == *p);
+#endif
+    }
+};
+
+// A "ptrauth" struct that just passes pointers through unchanged.
+struct PtrauthRaw {
+    template <typename T>
+    static T *sign(T *ptr, const void *address) {
+        return ptr;
+    }
+
+    template <typename T>
+    static T *auth(T *ptr, const void *address) {
+        return ptr;
+    }
+};
+
+// A ptrauth struct that stores pointers raw, and strips ptrauth
+// when reading.
+struct PtrauthStrip {
+    template <typename T>
+    static T *sign(T *ptr, const void *address) {
+        return ptr;
+    }
+
+    template <typename T>
+    static T *auth(T *ptr, const void *address) {
+        return ptrauth_strip(ptr, ptrauth_key_process_dependent_data);
+    }
+};
+
+// A ptrauth struct that signs and authenticates pointers using the
+// DB key with the given discriminator and address diversification.
+template <unsigned discriminator>
+struct Ptrauth {
+    template <typename T>
+    static T *sign(T *ptr, const void *address) {
+        if (!ptr)
+            return nullptr;
+        return ptrauth_sign_unauthenticated(ptr, ptrauth_key_process_dependent_data, ptrauth_blend_discriminator(address, discriminator));
+    }
+
+    template <typename T>
+    static T *auth(T *ptr, const void *address) {
+        if (!ptr)
+            return nullptr;
+        return ptrauth_auth_data(ptr, ptrauth_key_process_dependent_data, ptrauth_blend_discriminator(address, discriminator));
+    }
+};
+
+// A template that produces a WrappedPtr to the given type using a
+// plain unauthenticated pointer.
+template <typename T> using RawPtr = WrappedPtr<T, PtrauthRaw>;
+
+#if __has_feature(ptrauth_calls)
+// Get a ptrauth type that uses a string discriminator.
+#define PTRAUTH_STR(name) Ptrauth<ptrauth_string_discriminator(#name)>
+
+// When ptrauth is available, declare a template that wraps a type
+// in a WrappedPtr that uses an authenticated pointer using the
+// process-dependent data key, address diversification, and a
+// discriminator based on the name passed in.
+//
+// When ptrauth is not available, equivalent to RawPtr.
+#define DECLARE_AUTHED_PTR_TEMPLATE(name)                      \
+    template <typename T> using name ## _authed_ptr            \
+        = WrappedPtr<T, PTRAUTH_STR(name)>;
+#else
+#define PTRAUTH_STR(name) PtrauthRaw
+#define DECLARE_AUTHED_PTR_TEMPLATE(name)                      \
+    template <typename T> using name ## _authed_ptr = RawPtr<T>;
+#endif
+
 // _OBJC_PTRAUTH_H_
 #endif
index d3541cf61e01f2242a543e1b220bae6c18126e93..d6ce37c4618aff63459eee6323e238306d7e6b42 100644 (file)
@@ -240,6 +240,19 @@ private:
 public:
     inline SEL sel() const { return _sel.load(memory_order::memory_order_relaxed); }
 
+    inline IMP rawImp(objc_class *cls) const {
+        uintptr_t imp = _imp.load(memory_order::memory_order_relaxed);
+        if (!imp) return nil;
+#if CACHE_IMP_ENCODING == CACHE_IMP_ENCODING_PTRAUTH
+#elif CACHE_IMP_ENCODING == CACHE_IMP_ENCODING_ISA_XOR
+        imp ^= (uintptr_t)cls;
+#elif CACHE_IMP_ENCODING == CACHE_IMP_ENCODING_NONE
+#else
+#error Unknown method cache IMP encoding.
+#endif
+        return (IMP)imp;
+    }
+
     inline IMP imp(Class cls) const {
         uintptr_t imp = _imp.load(memory_order::memory_order_relaxed);
         if (!imp) return nil;
@@ -398,6 +411,27 @@ public:
 typedef struct classref * classref_t;
 
 
+/***********************************************************************
+* RelativePointer<T>
+* A pointer stored as an offset from the address of that offset.
+*
+* The target address is computed by taking the address of this struct
+* and adding the offset stored within it. This is a 32-bit signed
+* offset giving Â±2GB of range.
+**********************************************************************/
+template <typename T>
+struct RelativePointer: nocopy_t {
+    int32_t offset;
+
+    T get() const {
+        uintptr_t base = (uintptr_t)&offset;
+        uintptr_t signExtendedOffset = (uintptr_t)(intptr_t)offset;
+        uintptr_t pointer = base + signExtendedOffset;
+        return (T)pointer;
+    }
+};
+
+
 #ifdef __PTRAUTH_INTRINSICS__
 #   define StubClassInitializerPtrauth __ptrauth(ptrauth_key_function_pointer, 1, 0xc671)
 #else
@@ -408,20 +442,27 @@ struct stub_class_t {
     _objc_swiftMetadataInitializer StubClassInitializerPtrauth initializer;
 };
 
+// A pointer modifier that does nothing to the pointer.
+struct PointerModifierNop {
+    template <typename ListType, typename T>
+    static T *modify(const ListType &list, T *ptr) { return ptr; }
+};
+
 /***********************************************************************
-* entsize_list_tt<Element, List, FlagMask>
+* entsize_list_tt<Element, List, FlagMask, PointerModifier>
 * Generic implementation of an array of non-fragile structs.
 *
 * Element is the struct type (e.g. method_t)
 * List is the specialization of entsize_list_tt (e.g. method_list_t)
 * FlagMask is used to stash extra bits in the entsize field
 *   (e.g. method list fixup markers)
+* PointerModifier is applied to the element pointers retrieved from
+* the array.
 **********************************************************************/
-template <typename Element, typename List, uint32_t FlagMask>
+template <typename Element, typename List, uint32_t FlagMask, typename PointerModifier = PointerModifierNop>
 struct entsize_list_tt {
     uint32_t entsizeAndFlags;
     uint32_t count;
-    Element first;
 
     uint32_t entsize() const {
         return entsizeAndFlags & ~FlagMask;
@@ -432,7 +473,7 @@ struct entsize_list_tt {
 
     Element& getOrEnd(uint32_t i) const { 
         ASSERT(i <= count);
-        return *(Element *)((uint8_t *)&first + i*entsize()); 
+        return *PointerModifier::modify(*this, (Element *)((uint8_t *)this + sizeof(*this) + i*entsize()));
     }
     Element& get(uint32_t i) const { 
         ASSERT(i < count);
@@ -444,15 +485,7 @@ struct entsize_list_tt {
     }
     
     static size_t byteSize(uint32_t entsize, uint32_t count) {
-        return sizeof(entsize_list_tt) + (count-1)*entsize;
-    }
-
-    List *duplicate() const {
-        auto *dup = (List *)calloc(this->byteSize(), 1);
-        dup->entsizeAndFlags = this->entsizeAndFlags;
-        dup->count = this->count;
-        std::copy(begin(), end(), dup->begin());
-        return dup;
+        return sizeof(entsize_list_tt) + count*entsize;
     }
 
     struct iterator;
@@ -541,18 +574,109 @@ struct entsize_list_tt {
 
 
 struct method_t {
-    SEL name;
-    const char *types;
-    MethodListIMP imp;
+    static const uint32_t smallMethodListFlag = 0x80000000;
+
+    method_t(const method_t &other) = delete;
+
+    // The representation of a "big" method. This is the traditional
+    // representation of three pointers storing the selector, types
+    // and implementation.
+    struct big {
+        SEL name;
+        const char *types;
+        MethodListIMP imp;
+    };
+
+private:
+    bool isSmall() const {
+        return ((uintptr_t)this & 1) == 1;
+    }
+
+    // The representation of a "small" method. This stores three
+    // relative offsets to the name, types, and implementation.
+    struct small {
+        RelativePointer<SEL *> name;
+        RelativePointer<const char *> types;
+        RelativePointer<IMP> imp;
+    };
+
+    small &small() const {
+        ASSERT(isSmall());
+        return *(struct small *)((uintptr_t)this & ~(uintptr_t)1);
+    }
+
+    IMP remappedImp(bool needsLock) const;
+    void remapImp(IMP imp);
+    objc_method_description *getSmallDescription() const;
+
+public:
+    static const auto bigSize = sizeof(struct big);
+    static const auto smallSize = sizeof(struct small);
+
+    // The pointer modifier used with method lists. When the method
+    // list contains small methods, set the bottom bit of the pointer.
+    // We use that bottom bit elsewhere to distinguish between big
+    // and small methods.
+    struct pointer_modifier {
+        template <typename ListType>
+        static method_t *modify(const ListType &list, method_t *ptr) {
+            if (list.flags() & smallMethodListFlag)
+                return (method_t *)((uintptr_t)ptr | 1);
+            return ptr;
+        }
+    };
+
+    big &big() const {
+        ASSERT(!isSmall());
+        return *(struct big *)this;
+    }
+
+    SEL &name() const {
+        return isSmall() ? *small().name.get() : big().name;
+    }
+    const char *types() const {
+        return isSmall() ? small().types.get() : big().types;
+    }
+    IMP imp(bool needsLock) const {
+        if (isSmall()) {
+            IMP imp = remappedImp(needsLock);
+            if (!imp)
+                imp = ptrauth_sign_unauthenticated(small().imp.get(),
+                                                   ptrauth_key_function_pointer, 0);
+            return imp;
+        }
+        return big().imp;
+    }
+
+    void setImp(IMP imp) {
+        if (isSmall()) {
+            remapImp(imp);
+        } else {
+            big().imp = imp;
+        }
+        
+    }
+
+    objc_method_description *getDescription() const {
+        return isSmall() ? getSmallDescription() : (struct objc_method_description *)this;
+    }
 
     struct SortBySELAddress :
-        public std::binary_function<const method_t&,
-                                    const method_t&, bool>
+    public std::binary_function<const struct method_t::big&,
+                                const struct method_t::big&, bool>
     {
-        bool operator() (const method_t& lhs,
-                         const method_t& rhs)
+        bool operator() (const struct method_t::big& lhs,
+                         const struct method_t::big& rhs)
         { return lhs.name < rhs.name; }
     };
+
+    method_t &operator=(const method_t &other) {
+        ASSERT(!isSmall());
+        big().name = other.name();
+        big().types = other.types();
+        big().imp = other.imp(false);
+        return *this;
+    }
 };
 
 struct ivar_t {
@@ -583,7 +707,15 @@ struct property_t {
 };
 
 // Two bits of entsize are used for fixup markers.
-struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> {
+// Reserve the top half of entsize for more flags. We never
+// need entry sizes anywhere close to 64kB.
+//
+// Currently there is one flag defined: the small method list flag,
+// method_t::smallMethodListFlag. Other flags are currently ignored.
+// (NOTE: these bits are only ignored on runtimes that support small
+// method lists. Older runtimes will treat them as part of the entry
+// size!)
+struct method_list_t : entsize_list_tt<method_t, method_list_t, 0xffff0003, method_t::pointer_modifier> {
     bool isUniqued() const;
     bool isFixedUp() const;
     void setFixedUp();
@@ -594,6 +726,31 @@ struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> {
         ASSERT(i < count);
         return i;
     }
+
+    bool isSmallList() const {
+        return flags() & method_t::smallMethodListFlag;
+    }
+
+    bool isExpectedSize() const {
+        if (isSmallList())
+            return entsize() == method_t::smallSize;
+        else
+            return entsize() == method_t::bigSize;
+    }
+
+    method_list_t *duplicate() const {
+        method_list_t *dup;
+        if (isSmallList()) {
+            dup = (method_list_t *)calloc(byteSize(method_t::bigSize, count), 1);
+            dup->entsizeAndFlags = method_t::bigSize;
+        } else {
+            dup = (method_list_t *)calloc(this->byteSize(), 1);
+            dup->entsizeAndFlags = this->entsizeAndFlags;
+        }
+        dup->count = this->count;
+        std::copy(begin(), end(), dup->begin());
+        return dup;
+    }
 };
 
 struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {
@@ -707,7 +864,7 @@ struct class_ro_t {
     const uint8_t * ivarLayout;
     
     const char * name;
-    method_list_t * baseMethodList;
+    WrappedPtr<method_list_t, PtrauthStrip> baseMethodList;
     protocol_list_t * baseProtocols;
     const ivar_list_t * ivars;
 
@@ -745,11 +902,13 @@ struct class_ro_t {
 
 
 /***********************************************************************
-* list_array_tt<Element, List>
+* list_array_tt<Element, List, Ptr>
 * Generic implementation for metadata that can be augmented by categories.
 *
 * Element is the underlying metadata type (e.g. method_t)
 * List is the metadata's list type (e.g. method_list_t)
+* List is a template applied to Element to make Element*. Useful for
+* applying qualifiers to the pointer type.
 *
 * A list_array_tt has one of three values:
 * - empty
@@ -759,11 +918,11 @@ struct class_ro_t {
 * countLists/beginLists/endLists iterate the metadata lists
 * count/begin/end iterate the underlying metadata elements
 **********************************************************************/
-template <typename Element, typename List>
+template <typename Element, typename List, template<typename> class Ptr>
 class list_array_tt {
     struct array_t {
         uint32_t count;
-        List* lists[0];
+        Ptr<List> lists[0];
 
         static size_t byteSize(uint32_t count) {
             return sizeof(array_t) + count*sizeof(lists[0]);
@@ -775,12 +934,12 @@ class list_array_tt {
 
  protected:
     class iterator {
-        List * const *lists;
-        List * const *listsEnd;
+        const Ptr<List> *lists;
+        const Ptr<List> *listsEnd;
         typename List::iterator m, mEnd;
 
      public:
-        iterator(List *const *begin, List *const *end)
+        iterator(const Ptr<List> *begin, const Ptr<List> *end)
             : lists(begin), listsEnd(end)
         {
             if (begin != end) {
@@ -820,7 +979,7 @@ class list_array_tt {
 
  private:
     union {
-        List* list;
+        Ptr<List> list;
         uintptr_t arrayAndFlag;
     };
 
@@ -836,9 +995,26 @@ class list_array_tt {
         arrayAndFlag = (uintptr_t)array | 1;
     }
 
+    void validate() {
+        for (auto cursor = beginLists(), end = endLists(); cursor != end; cursor++)
+            cursor->validate();
+    }
+
  public:
     list_array_tt() : list(nullptr) { }
     list_array_tt(List *l) : list(l) { }
+    list_array_tt(const list_array_tt &other) {
+        *this = other;
+    }
+
+    list_array_tt &operator =(const list_array_tt &other) {
+        if (other.hasArray()) {
+            arrayAndFlag = other.arrayAndFlag;
+        } else {
+            list = other.list;
+        }
+        return *this;
+    }
 
     uint32_t count() const {
         uint32_t result = 0;
@@ -856,7 +1032,7 @@ class list_array_tt {
     }
 
     iterator end() const {
-        List * const *e = endLists();
+        auto e = endLists();
         return iterator(e, e);
     }
 
@@ -871,7 +1047,7 @@ class list_array_tt {
         }
     }
 
-    List* const * beginLists() const {
+    const Ptr<List>* beginLists() const {
         if (hasArray()) {
             return array()->lists;
         } else {
@@ -879,7 +1055,7 @@ class list_array_tt {
         }
     }
 
-    List* const * endLists() const {
+    const Ptr<List>* endLists() const {
         if (hasArray()) {
             return array()->lists + array()->count;
         } else if (list) {
@@ -896,27 +1072,34 @@ class list_array_tt {
             // many lists -> many lists
             uint32_t oldCount = array()->count;
             uint32_t newCount = oldCount + addedCount;
-            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
+            array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
+            newArray->count = newCount;
             array()->count = newCount;
-            memmove(array()->lists + addedCount, array()->lists, 
-                    oldCount * sizeof(array()->lists[0]));
-            memcpy(array()->lists, addedLists, 
-                   addedCount * sizeof(array()->lists[0]));
+
+            for (int i = oldCount - 1; i >= 0; i--)
+                newArray->lists[i + addedCount] = array()->lists[i];
+            for (unsigned i = 0; i < addedCount; i++)
+                newArray->lists[i] = addedLists[i];
+            free(array());
+            setArray(newArray);
+            validate();
         }
         else if (!list  &&  addedCount == 1) {
             // 0 lists -> 1 list
             list = addedLists[0];
+            validate();
         } 
         else {
             // 1 list -> many lists
-            List* oldList = list;
+            Ptr<List> oldList = list;
             uint32_t oldCount = oldList ? 1 : 0;
             uint32_t newCount = oldCount + addedCount;
             setArray((array_t *)malloc(array_t::byteSize(newCount)));
             array()->count = newCount;
             if (oldList) array()->lists[addedCount] = oldList;
-            memcpy(array()->lists, addedLists, 
-                   addedCount * sizeof(array()->lists[0]));
+            for (unsigned i = 0; i < addedCount; i++)
+                array()->lists[i] = addedLists[i];
+            validate();
         }
     }
 
@@ -932,79 +1115,66 @@ class list_array_tt {
         }
     }
 
-    template<typename Result>
-    Result duplicate() {
-        Result result;
-
+    template<typename Other>
+    void duplicateInto(Other &other) {
         if (hasArray()) {
             array_t *a = array();
-            result.setArray((array_t *)memdup(a, a->byteSize()));
+            other.setArray((array_t *)memdup(a, a->byteSize()));
             for (uint32_t i = 0; i < a->count; i++) {
-                result.array()->lists[i] = a->lists[i]->duplicate();
+                other.array()->lists[i] = a->lists[i]->duplicate();
             }
         } else if (list) {
-            result.list = list->duplicate();
+            other.list = list->duplicate();
         } else {
-            result.list = nil;
+            other.list = nil;
         }
-
-        return result;
     }
 };
 
 
+DECLARE_AUTHED_PTR_TEMPLATE(method_list_t)
+
 class method_array_t : 
-    public list_array_tt<method_t, method_list_t
+    public list_array_tt<method_t, method_list_t, method_list_t_authed_ptr>
 {
-    typedef list_array_tt<method_t, method_list_t> Super;
+    typedef list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> Super;
 
  public:
     method_array_t() : Super() { }
     method_array_t(method_list_t *l) : Super(l) { }
 
-    method_list_t * const *beginCategoryMethodLists() const {
+    const method_list_t_authed_ptr<method_list_t> *beginCategoryMethodLists() const {
         return beginLists();
     }
     
-    method_list_t * const *endCategoryMethodLists(Class cls) const;
-
-    method_array_t duplicate() {
-        return Super::duplicate<method_array_t>();
-    }
+    const method_list_t_authed_ptr<method_list_t> *endCategoryMethodLists(Class cls) const;
 };
 
 
 class property_array_t : 
-    public list_array_tt<property_t, property_list_t
+    public list_array_tt<property_t, property_list_t, RawPtr>
 {
-    typedef list_array_tt<property_t, property_list_t> Super;
+    typedef list_array_tt<property_t, property_list_t, RawPtr> Super;
 
  public:
     property_array_t() : Super() { }
     property_array_t(property_list_t *l) : Super(l) { }
-
-    property_array_t duplicate() {
-        return Super::duplicate<property_array_t>();
-    }
 };
 
 
 class protocol_array_t : 
-    public list_array_tt<protocol_ref_t, protocol_list_t
+    public list_array_tt<protocol_ref_t, protocol_list_t, RawPtr>
 {
-    typedef list_array_tt<protocol_ref_t, protocol_list_t> Super;
+    typedef list_array_tt<protocol_ref_t, protocol_list_t, RawPtr> Super;
 
  public:
     protocol_array_t() : Super() { }
     protocol_array_t(protocol_list_t *l) : Super(l) { }
-
-    protocol_array_t duplicate() {
-        return Super::duplicate<protocol_array_t>();
-    }
 };
 
 struct class_rw_ext_t {
-    const class_ro_t *ro;
+    DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
+    class_ro_t_authed_ptr<const class_ro_t> ro;
     method_array_t methods;
     property_array_t properties;
     protocol_array_t protocols;
@@ -1026,21 +1196,21 @@ struct class_rw_t {
     Class nextSiblingClass;
 
 private:
-    using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t *, class_rw_ext_t *>;
+    using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;
 
     const ro_or_rw_ext_t get_ro_or_rwe() const {
         return ro_or_rw_ext_t{ro_or_rw_ext};
     }
 
     void set_ro_or_rwe(const class_ro_t *ro) {
-        ro_or_rw_ext_t{ro}.storeAt(ro_or_rw_ext, memory_order_relaxed);
+        ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
     }
 
     void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
         // the release barrier is so that the class_rw_ext_t::ro initialization
         // is visible to lockless readers
         rwe->ro = ro;
-        ro_or_rw_ext_t{rwe}.storeAt(ro_or_rw_ext, memory_order_release);
+        ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
     }
 
     class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);
@@ -1069,15 +1239,15 @@ public:
     }
 
     class_rw_ext_t *ext() const {
-        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>();
+        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
     }
 
     class_rw_ext_t *extAllocIfNeeded() {
         auto v = get_ro_or_rwe();
         if (fastpath(v.is<class_rw_ext_t *>())) {
-            return v.get<class_rw_ext_t *>();
+            return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
         } else {
-            return extAlloc(v.get<const class_ro_t *>());
+            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
         }
     }
 
@@ -1088,15 +1258,15 @@ public:
     const class_ro_t *ro() const {
         auto v = get_ro_or_rwe();
         if (slowpath(v.is<class_rw_ext_t *>())) {
-            return v.get<class_rw_ext_t *>()->ro;
+            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
         }
-        return v.get<const class_ro_t *>();
+        return v.get<const class_ro_t *>(&ro_or_rw_ext);
     }
 
     void set_ro(const class_ro_t *ro) {
         auto v = get_ro_or_rwe();
         if (v.is<class_rw_ext_t *>()) {
-            v.get<class_rw_ext_t *>()->ro = ro;
+            v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
         } else {
             set_ro_or_rwe(ro);
         }
@@ -1105,27 +1275,27 @@ public:
     const method_array_t methods() const {
         auto v = get_ro_or_rwe();
         if (v.is<class_rw_ext_t *>()) {
-            return v.get<class_rw_ext_t *>()->methods;
+            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
         } else {
-            return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
+            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
         }
     }
 
     const property_array_t properties() const {
         auto v = get_ro_or_rwe();
         if (v.is<class_rw_ext_t *>()) {
-            return v.get<class_rw_ext_t *>()->properties;
+            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
         } else {
-            return property_array_t{v.get<const class_ro_t *>()->baseProperties};
+            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
         }
     }
 
     const protocol_array_t protocols() const {
         auto v = get_ro_or_rwe();
         if (v.is<class_rw_ext_t *>()) {
-            return v.get<class_rw_ext_t *>()->protocols;
+            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
         } else {
-            return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
+            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
         }
     }
 };
@@ -1650,8 +1820,8 @@ struct swift_class_t : objc_class {
 struct category_t {
     const char *name;
     classref_t cls;
-    struct method_list_t *instanceMethods;
-    struct method_list_t *classMethods;
+    WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
+    WrappedPtr<method_list_t, PtrauthStrip> classMethods;
     struct protocol_list_t *protocols;
     struct property_list_t *instanceProperties;
     // Fields below this point are not always present on disk.
index e1583755a6f1c46ad36cec4d93989cd13f2aee83..1eabd5cf46aeffc5303371d91fb33fcd5a95baa9 100644 (file)
@@ -46,7 +46,7 @@ static void free_class(Class cls);
 static IMP addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace);
 static void adjustCustomFlagsForMethodChange(Class cls, method_t *meth);
 static method_t *search_method_list(const method_list_t *mlist, SEL sel);
-static bool method_lists_contains_any(method_list_t * const *mlists, method_list_t * const *end,
+template<typename T> static bool method_lists_contains_any(T *mlists, T *end,
         SEL sels[], size_t selcount);
 static void flushCaches(Class cls);
 static void initializeTaggedPointerObfuscator(void);
@@ -212,6 +212,59 @@ static bool didInitialAttachCategories = false;
 **********************************************************************/
 bool didCallDyldNotifyRegister = false;
 
+
+/***********************************************************************
+* smallMethodIMPMap
+* The map from small method pointers to replacement IMPs.
+*
+* Locking: runtimeLock must be held when accessing this map.
+**********************************************************************/
+namespace objc {
+    static objc::LazyInitDenseMap<const method_t *, IMP> smallMethodIMPMap;
+}
+
+static IMP method_t_remappedImp_nolock(const method_t *m) {
+    runtimeLock.assertLocked();
+    auto *map = objc::smallMethodIMPMap.get(false);
+    if (!map)
+        return nullptr;
+    auto iter = map->find(m);
+    if (iter == map->end())
+        return nullptr;
+    return iter->second;
+}
+
+IMP method_t::remappedImp(bool needsLock) const {
+    ASSERT(isSmall());
+    if (needsLock) {
+        mutex_locker_t guard(runtimeLock);
+        return method_t_remappedImp_nolock(this);
+    } else {
+        return method_t_remappedImp_nolock(this);
+    }
+}
+
+void method_t::remapImp(IMP imp) {
+    ASSERT(isSmall());
+    runtimeLock.assertLocked();
+    auto *map = objc::smallMethodIMPMap.get(true);
+    (*map)[this] = imp;
+}
+
+objc_method_description *method_t::getSmallDescription() const {
+    static objc::LazyInitDenseMap<const method_t *, objc_method_description *> map;
+
+    mutex_locker_t guard(runtimeLock);
+
+    auto &ptr = (*map.get(true))[this];
+    if (!ptr) {
+        ptr = (objc_method_description *)malloc(sizeof *ptr);
+        ptr->name = name();
+        ptr->types = (char *)types();
+    }
+    return ptr;
+}
+
 /*
   Low two bits of mlist->entsize is used as the fixed-up marker.
   PREOPTIMIZED VERSION:
@@ -258,7 +311,8 @@ bool method_list_t::isUniqued() const {
 }
 
 bool method_list_t::isFixedUp() const {
-    return flags() == fixed_up_method_list;
+    // Ignore any flags in the top bits, just look at the bottom two.
+    return (flags() & 0x3) == fixed_up_method_list;
 }
 
 void method_list_t::setFixedUp() {
@@ -288,11 +342,11 @@ void protocol_t::clearIsCanonical() {
 }
 
 
-method_list_t * const *method_array_t::endCategoryMethodLists(Class cls) const
+const method_list_t_authed_ptr<method_list_t> *method_array_t::endCategoryMethodLists(Class cls) const
 {
     auto mlists = beginLists();
     auto mlistsEnd = endLists();
-    
+
     if (mlists == mlistsEnd  ||  !cls->data()->ro()->baseMethods())
     {
         // No methods, or no base methods. 
@@ -536,7 +590,7 @@ printReplacements(Class cls, const locstamped_category_t *cats_list, uint32_t ca
         if (!mlist) continue;
 
         for (const auto& meth : *mlist) {
-            SEL s = sel_registerName(sel_cname(meth.name));
+            SEL s = sel_registerName(sel_cname(meth.name()));
 
             // Search for replaced methods in method lookup order.
             // Complain about the first duplicate only.
@@ -549,11 +603,11 @@ printReplacements(Class cls, const locstamped_category_t *cats_list, uint32_t ca
                 if (!mlist2) continue;
 
                 for (const auto& meth2 : *mlist2) {
-                    SEL s2 = sel_registerName(sel_cname(meth2.name));
+                    SEL s2 = sel_registerName(sel_cname(meth2.name()));
                     if (s == s2) {
                         logReplacedMethod(cls->nameForLogging(), s, 
                                           cls->isMetaClass(), cat->name, 
-                                          meth2.imp, meth.imp);
+                                          meth2.imp(false), meth.imp(false));
                         goto complained;
                     }
                 }
@@ -561,11 +615,11 @@ printReplacements(Class cls, const locstamped_category_t *cats_list, uint32_t ca
 
             // Look for method in cls
             for (const auto& meth2 : cls->data()->methods()) {
-                SEL s2 = sel_registerName(sel_cname(meth2.name));
+                SEL s2 = sel_registerName(sel_cname(meth2.name()));
                 if (s == s2) {
                     logReplacedMethod(cls->nameForLogging(), s, 
                                       cls->isMetaClass(), cat->name, 
-                                      meth2.imp, meth.imp);
+                                      meth2.imp(false), meth.imp(false));
                     goto complained;
                 }
             }
@@ -892,7 +946,7 @@ public:
     static void
     scanChangedMethod(Class cls, const method_t *meth)
     {
-        if (fastpath(!Traits::isInterestingSelector(meth->name))) {
+        if (fastpath(!Traits::isInterestingSelector(meth->name()))) {
             return;
         }
 
@@ -938,7 +992,8 @@ struct AWZScanner : scanner::Mixin<AWZScanner, AWZ, PrintCustomAWZ, scanner::Sco
     static bool isInterestingSelector(SEL sel) {
         return sel == @selector(alloc) || sel == @selector(allocWithZone:);
     }
-    static bool scanMethodLists(method_list_t * const *mlists, method_list_t * const *end) {
+    template<typename T>
+    static bool scanMethodLists(T *mlists, T *end) {
         SEL sels[2] = { @selector(alloc), @selector(allocWithZone:), };
         return method_lists_contains_any(mlists, end, sels, 2);
     }
@@ -972,7 +1027,8 @@ struct RRScanner : scanner::Mixin<RRScanner, RR, PrintCustomRR
                sel == @selector(allowsWeakReference) ||
                sel == @selector(retainWeakReference);
     }
-    static bool scanMethodLists(method_list_t * const *mlists, method_list_t * const *end) {
+    template <typename T>
+    static bool scanMethodLists(T *mlists, T *end) {
         SEL sels[8] = {
             @selector(retain),
             @selector(release),
@@ -1007,7 +1063,8 @@ struct CoreScanner : scanner::Mixin<CoreScanner, Core, PrintCustomCore> {
                sel == @selector(isKindOfClass:) ||
                sel == @selector(respondsToSelector:);
     }
-    static bool scanMethodLists(method_list_t * const *mlists, method_list_t * const *end) {
+    template <typename T>
+    static bool scanMethodLists(T *mlists, T *end) {
         SEL sels[5] = {
             @selector(new),
             @selector(self),
@@ -1193,19 +1250,25 @@ fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
     
         // Unique selectors in list.
         for (auto& meth : *mlist) {
-            const char *name = sel_cname(meth.name);
-            meth.name = sel_registerNameNoLock(name, bundleCopy);
+            const char *name = sel_cname(meth.name());
+            meth.name() = sel_registerNameNoLock(name, bundleCopy);
         }
     }
 
     // Sort by selector address.
-    if (sort) {
+    // Don't try to sort small lists, as they're immutable.
+    // Don't try to sort big lists of nonstandard size, as stable_sort
+    // won't copy the entries properly.
+    if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
         method_t::SortBySELAddress sorter;
-        std::stable_sort(mlist->begin(), mlist->end(), sorter);
+        std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
     }
     
-    // Mark method list as uniqued and sorted
-    mlist->setFixedUp();
+    // Mark method list as uniqued and sorted.
+    // Can't mark small lists, since they're immutable.
+    if (!mlist->isSmallList()) {
+        mlist->setFixedUp();
+    }
 }
 
 
@@ -1391,6 +1454,9 @@ static void methodizeClass(Class cls, Class previously)
     // Install methods and properties that the class implements itself.
     method_list_t *list = ro->baseMethods();
     if (list) {
+        if (list->isSmallList() && !_dyld_is_memory_immutable(list, list->byteSize()))
+            _objc_fatal("CLASS: class '%s' %p small method list %p is not in immutable memory",
+                        cls->nameForLogging(), cls, list);
         prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
         if (rwe) rwe->methods.attachLists(&list, 1);
     }
@@ -1433,9 +1499,9 @@ static void methodizeClass(Class cls, Class previously)
     for (const auto& meth : rw->methods()) {
         if (PrintConnecting) {
             _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', 
-                         cls->nameForLogging(), sel_getName(meth.name));
+                         cls->nameForLogging(), sel_getName(meth.name()));
         }
-        ASSERT(sel_registerName(sel_getName(meth.name)) == meth.name); 
+        ASSERT(sel_registerName(sel_getName(meth.name())) == meth.name());
     }
 #endif
 }
@@ -2270,16 +2336,8 @@ static NEVER_INLINE Protocol *getProtocol(const char *name)
         if (result) return result;
     }
 
-    // Try table from dyld shared cache
-    // Temporarily check that we are using the new table.  Eventually this check
-    // will always be true.
-    // FIXME: Remove this check when we can
-    if (sharedCacheSupportsProtocolRoots()) {
-        result = getPreoptimizedProtocol(name);
-        if (result) return result;
-    }
-
-    return nil;
+    // Try table from dyld3 closure and dyld shared cache
+    return getPreoptimizedProtocol(name);
 }
 
 
@@ -3053,8 +3111,8 @@ static void load_categories_nolock(header_info *hi) {
         }
     };
 
-    processCatlist(_getObjc2CategoryList(hi, &count));
-    processCatlist(_getObjc2CategoryList2(hi, &count));
+    processCatlist(hi->catlist(&count));
+    processCatlist(hi->catlist2(&count));
 }
 
 static void loadAllCategories() {
@@ -3554,7 +3612,6 @@ void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int un
     ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
 #endif
 
-    bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
 
     // Discover protocols. Fix up protocol refs.
     for (EACH_HEADER) {
@@ -3570,7 +3627,7 @@ void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int un
         // in the shared cache is marked with isCanonical() and that may not
         // be true if some non-shared cache binary was chosen as the canonical
         // definition
-        if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
+        if (launchTime && isPreoptimized) {
             if (PrintProtocols) {
                 _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
                              hi->fname());
@@ -3597,7 +3654,7 @@ void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int un
         // shared cache definition of a protocol.  We can skip the check on
         // launch, but have to visit @protocol refs for shared cache images
         // loaded later.
-        if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
+        if (launchTime && hi->isPreoptimized())
             continue;
         protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
         for (i = 0; i < count; i++) {
@@ -3627,8 +3684,7 @@ void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int un
 
     // Realize non-lazy classes (for +load methods and static instances)
     for (EACH_HEADER) {
-        classref_t const *classlist = 
-            _getObjc2NonlazyClassList(hi, &count);
+        classref_t const *classlist = hi->nlclslist(&count);
         for (i = 0; i < count; i++) {
             Class cls = remapClass(classlist[i]);
             if (!cls) continue;
@@ -3808,7 +3864,7 @@ void _unload_image(header_info *hi)
 
     // Ignore __objc_catlist2. We don't support unloading Swift
     // and we never will.
-    category_t * const *catlist = _getObjc2CategoryList(hi, &count);
+    category_t * const *catlist = hi->catlist(&count);
     for (i = 0; i < count; i++) {
         category_t *cat = catlist[i];
         Class cls = remapClass(cat->cls);
@@ -3838,7 +3894,7 @@ void _unload_image(header_info *hi)
         if (cls) classes.insert(cls);
     }
 
-    classlist = _getObjc2NonlazyClassList(hi, &count);
+    classlist = hi->nlclslist(&count);
     for (i = 0; i < count; i++) {
         Class cls = remapClass(classlist[i]);
         if (cls) classes.insert(cls);
@@ -3873,14 +3929,19 @@ struct objc_method_description *
 method_getDescription(Method m)
 {
     if (!m) return nil;
-    return (struct objc_method_description *)m;
+    return m->getDescription();
 }
 
 
 IMP 
 method_getImplementation(Method m)
 {
-    return m ? m->imp : nil;
+    return m ? m->imp(true) : nil;
+}
+
+IMPAndSEL _method_getImplementationAndName(Method m)
+{
+    return { m->imp(true), m->name() };
 }
 
 
@@ -3896,8 +3957,8 @@ method_getName(Method m)
 {
     if (!m) return nil;
 
-    ASSERT(m->name == sel_registerName(sel_getName(m->name)));
-    return m->name;
+    ASSERT(m->name() == sel_registerName(sel_getName(m->name())));
+    return m->name();
 }
 
 
@@ -3911,7 +3972,7 @@ const char *
 method_getTypeEncoding(Method m)
 {
     if (!m) return nil;
-    return m->types;
+    return m->types();
 }
 
 
@@ -3928,8 +3989,8 @@ _method_setImplementation(Class cls, method_t *m, IMP imp)
     if (!m) return nil;
     if (!imp) return nil;
 
-    IMP old = m->imp;
-    m->imp = imp;
+    IMP old = m->imp(false);
+    m->setImp(imp);
 
     // Cache updates are slow if cls is nil (i.e. unknown)
     // RR/AWZ updates are slow if cls is nil (i.e. unknown)
@@ -3958,9 +4019,9 @@ void method_exchangeImplementations(Method m1, Method m2)
 
     mutex_locker_t lock(runtimeLock);
 
-    IMP m1_imp = m1->imp;
-    m1->imp = m2->imp;
-    m2->imp = m1_imp;
+    IMP m1_imp = m1->imp(false);
+    m1->setImp(m2->imp(false));
+    m2->setImp(m1_imp);
 
 
     // RR/AWZ updates are slow because class is unknown
@@ -4121,7 +4182,7 @@ fixupProtocolMethodList(protocol_t *proto, method_list_t *mlist,
     fixupMethodList(mlist, true/*always copy for simplicity*/,
                     !extTypes/*sort if no extended method types*/);
     
-    if (extTypes) {
+    if (extTypes && !mlist->isSmallList()) {
         // Sort method list and extended method types together.
         // fixupMethodList() can't do this.
         // fixme COW stomp
@@ -4132,8 +4193,8 @@ fixupProtocolMethodList(protocol_t *proto, method_list_t *mlist,
                                          required, instance, prefix, junk);
         for (uint32_t i = 0; i < count; i++) {
             for (uint32_t j = i+1; j < count; j++) {
-                method_t& mi = mlist->get(i);
-                method_t& mj = mlist->get(j);
+                auto& mi = mlist->get(i).big();
+                auto& mj = mlist->get(j).big();
                 if (mi.name > mj.name) {
                     std::swap(mi, mj);
                     std::swap(extTypes[prefix+i], extTypes[prefix+j]);
@@ -4372,7 +4433,9 @@ protocol_getMethodDescription(Protocol *p, SEL aSel,
     Method m = 
         protocol_getMethod(newprotocol(p), aSel, 
                            isRequiredMethod, isInstanceMethod, true);
-    if (m) return *method_getDescription(m);
+    // method_getDescription is inefficient for small methods. Don't bother
+    // trying to use it, just make our own.
+    if (m) return (struct objc_method_description){m->name(), (char *)m->types()};
     else return (struct objc_method_description){nil, nil};
 }
 
@@ -4477,8 +4540,8 @@ protocol_copyMethodDescriptionList(Protocol *p,
         result = (struct objc_method_description *)
             calloc(mlist->count + 1, sizeof(struct objc_method_description));
         for (const auto& meth : *mlist) {
-            result[count].name = meth.name;
-            result[count].types = (char *)meth.types;
+            result[count].name = meth.name();
+            result[count].types = (char *)meth.types();
             count++;
         }
     }
@@ -4763,15 +4826,15 @@ static void
 protocol_addMethod_nolock(method_list_t*& list, SEL name, const char *types)
 {
     if (!list) {
-        list = (method_list_t *)calloc(sizeof(method_list_t), 1);
-        list->entsizeAndFlags = sizeof(list->first);
+        list = (method_list_t *)calloc(method_list_t::byteSize(sizeof(struct method_t::big), 1), 1);
+        list->entsizeAndFlags = sizeof(struct method_t::big);
         list->setFixedUp();
     } else {
         size_t size = list->byteSize() + list->entsize();
         list = (method_list_t *)realloc(list, size);
     }
 
-    method_t& meth = list->get(list->count++);
+    auto &meth = list->get(list->count++).big();
     meth.name = name;
     meth.types = types ? strdupIfMutable(types) : "";
     meth.imp = nil;
@@ -4819,15 +4882,15 @@ protocol_addProperty_nolock(property_list_t *&plist, const char *name,
                             unsigned int count)
 {
     if (!plist) {
-        plist = (property_list_t *)calloc(sizeof(property_list_t), 1);
+        plist = (property_list_t *)calloc(property_list_t::byteSize(sizeof(property_t), 1), 1);
         plist->entsizeAndFlags = sizeof(property_t);
+        plist->count = 1;
     } else {
-        plist = (property_list_t *)
-            realloc(plist, sizeof(property_list_t) 
-                    + plist->count * plist->entsize());
+        plist->count++;
+        plist = (property_list_t *)realloc(plist, plist->byteSize());
     }
 
-    property_t& prop = plist->get(plist->count++);
+    property_t& prop = plist->get(plist->count - 1);
     prop.name = strdupIfMutable(name);
     prop.attributes = copyPropertyAttributeString(attrs, count);
 }
@@ -5038,7 +5101,7 @@ objc_copyProtocolList(unsigned int *outCount)
     // Find all the protocols from the pre-optimized images.  These protocols
     // won't be in the protocol map.
     objc::DenseMap<const char*, Protocol*> preoptimizedProtocols;
-    if (sharedCacheSupportsProtocolRoots()) {
+    {
         header_info *hi;
         for (hi = FirstHeader; hi; hi = hi->getNext()) {
             if (!hi->hasPreoptimizedProtocols())
@@ -5242,9 +5305,9 @@ objc_class::getLoadMethod()
     mlist = ISA()->data()->ro()->baseMethods();
     if (mlist) {
         for (const auto& meth : *mlist) {
-            const char *name = sel_cname(meth.name);
+            const char *name = sel_cname(meth.name());
             if (0 == strcmp(name, "load")) {
-                return meth.imp;
+                return meth.imp(false);
             }
         }
     }
@@ -5312,9 +5375,9 @@ _category_getLoadMethod(Category cat)
     mlist = cat->classMethods;
     if (mlist) {
         for (const auto& meth : *mlist) {
-            const char *name = sel_cname(meth.name);
+            const char *name = sel_cname(meth.name());
             if (0 == strcmp(name, "load")) {
-                return meth.imp;
+                return meth.imp(false);
             }
         }
     }
@@ -5740,25 +5803,26 @@ findMethodInSortedMethodList(SEL key, const method_list_t *list)
 {
     ASSERT(list);
 
-    const method_t * const first = &list->first;
-    const method_t *base = first;
-    const method_t *probe;
+    auto first = list->begin();
+    auto base = first;
+    decltype(first) probe;
+
     uintptr_t keyValue = (uintptr_t)key;
     uint32_t count;
     
     for (count = list->count; count != 0; count >>= 1) {
         probe = base + (count >> 1);
         
-        uintptr_t probeValue = (uintptr_t)probe->name;
+        uintptr_t probeValue = (uintptr_t)probe->name();
         
         if (keyValue == probeValue) {
             // `probe` is a match.
             // Rewind looking for the *first* occurrence of this value.
             // This is required for correct category overrides.
-            while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
+            while (probe > first && keyValue == (uintptr_t)(probe - 1)->name()) {
                 probe--;
             }
-            return (method_t *)probe;
+            return &*probe;
         }
         
         if (keyValue > probeValue) {
@@ -5774,14 +5838,14 @@ ALWAYS_INLINE static method_t *
 search_method_list_inline(const method_list_t *mlist, SEL sel)
 {
     int methodListIsFixedUp = mlist->isFixedUp();
-    int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
+    int methodListHasExpectedSize = mlist->isExpectedSize();
     
     if (fastpath(methodListIsFixedUp && methodListHasExpectedSize)) {
         return findMethodInSortedMethodList(sel, mlist);
     } else {
         // Linear search of unsorted method list
         for (auto& meth : *mlist) {
-            if (meth.name == sel) return &meth;
+            if (meth.name() == sel) return &meth;
         }
     }
 
@@ -5789,7 +5853,7 @@ search_method_list_inline(const method_list_t *mlist, SEL sel)
     // sanity-check negative results
     if (mlist->isFixedUp()) {
         for (auto& meth : *mlist) {
-            if (meth.name == sel) {
+            if (meth.name() == sel) {
                 _objc_fatal("linear search worked when binary search did not");
             }
         }
@@ -5808,14 +5872,15 @@ search_method_list(const method_list_t *mlist, SEL sel)
 /***********************************************************************
  * method_lists_contains_any
  **********************************************************************/
+template<typename T>
 static NEVER_INLINE bool
-method_lists_contains_any(method_list_t * const *mlists, method_list_t * const *end,
+method_lists_contains_any(T *mlists, T *end,
                           SEL sels[], size_t selcount)
 {
     while (mlists < end) {
         const method_list_t *mlist = *mlists++;
         int methodListIsFixedUp = mlist->isFixedUp();
-        int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
+        int methodListHasExpectedSize = mlist->entsize() == sizeof(struct method_t::big);
 
         if (fastpath(methodListIsFixedUp && methodListHasExpectedSize)) {
             for (size_t i = 0; i < selcount; i++) {
@@ -5826,7 +5891,7 @@ method_lists_contains_any(method_list_t * const *mlists, method_list_t * const *
         } else {
             for (auto& meth : *mlist) {
                 for (size_t i = 0; i < selcount; i++) {
-                    if (meth.name == sels[i]) {
+                    if (meth.name() == sels[i]) {
                         return true;
                     }
                 }
@@ -6124,8 +6189,6 @@ IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
     // To make these harder we want to make sure this is a class that was
     // either built into the binary or legitimately registered through
     // objc_duplicateClass, objc_initializeClassPair or objc_allocateClassPair.
-    //
-    // TODO: this check is quite costly during process startup.
     checkIsKnownClass(cls);
 
     if (slowpath(!cls->isRealized())) {
@@ -6157,7 +6220,7 @@ IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
         // curClass method list.
         Method meth = getMethodNoSuper_nolock(curClass, sel);
         if (meth) {
-            imp = meth->imp;
+            imp = meth->imp(false);
             goto done;
         }
 
@@ -6230,8 +6293,8 @@ IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel)
 
     if (meth) {
         // Hit in method list. Cache it.
-        cache_fill(cls, sel, meth->imp, nil);
-        return meth->imp;
+        cache_fill(cls, sel, meth->imp(false), nil);
+        return meth->imp(false);
     } else {
         // Miss in method list. Cache objc_msgForward.
         cache_fill(cls, sel, _objc_msgForward_impcache, nil);
@@ -6603,7 +6666,7 @@ addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
     if ((m = getMethodNoSuper_nolock(cls, name))) {
         // already exists
         if (!replace) {
-            result = m->imp;
+            result = m->imp(false);
         } else {
             result = _method_setImplementation(cls, m, imp);
         }
@@ -6612,13 +6675,14 @@ addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
 
         // fixme optimize
         method_list_t *newlist;
-        newlist = (method_list_t *)calloc(sizeof(*newlist), 1);
+        newlist = (method_list_t *)calloc(method_list_t::byteSize(method_t::bigSize, 1), 1);
         newlist->entsizeAndFlags = 
-            (uint32_t)sizeof(method_t) | fixed_up_method_list;
+            (uint32_t)sizeof(struct method_t::big) | fixed_up_method_list;
         newlist->count = 1;
-        newlist->first.name = name;
-        newlist->first.types = strdupIfMutable(types);
-        newlist->first.imp = imp;
+        auto &first = newlist->begin()->big();
+        first.name = name;
+        first.types = strdupIfMutable(types);
+        first.imp = imp;
 
         prepareMethodLists(cls, &newlist, 1, NO, NO);
         rwe->methods.attachLists(&newlist, 1);
@@ -6650,14 +6714,12 @@ addMethods(Class cls, const SEL *names, const IMP *imps, const char **types,
     ASSERT(cls->isRealized());
     
     method_list_t *newlist;
-    size_t newlistSize = method_list_t::byteSize(sizeof(method_t), count);
+    size_t newlistSize = method_list_t::byteSize(sizeof(struct method_t::big), count);
     newlist = (method_list_t *)calloc(newlistSize, 1);
     newlist->entsizeAndFlags =
-        (uint32_t)sizeof(method_t) | fixed_up_method_list;
+        (uint32_t)sizeof(struct method_t::big) | fixed_up_method_list;
     newlist->count = 0;
     
-    method_t *newlistMethods = &newlist->first;
-    
     SEL *failedNames = nil;
     uint32_t failedCount = 0;
     
@@ -6673,16 +6735,16 @@ addMethods(Class cls, const SEL *names, const IMP *imps, const char **types,
                     failedNames = (SEL *)calloc(sizeof(*failedNames),
                                                 count + 1);
                 }
-                failedNames[failedCount] = m->name;
+                failedNames[failedCount] = m->name();
                 failedCount++;
             } else {
                 _method_setImplementation(cls, m, imps[i]);
             }
         } else {
-            method_t *newmethod = &newlistMethods[newlist->count];
-            newmethod->name = names[i];
-            newmethod->types = strdupIfMutable(types[i]);
-            newmethod->imp = imps[i];
+            auto &newmethod = newlist->end()->big();
+            newmethod.name = names[i];
+            newmethod.types = strdupIfMutable(types[i]);
+            newmethod.imp = imps[i];
             newlist->count++;
         }
     }
@@ -6694,7 +6756,7 @@ addMethods(Class cls, const SEL *names, const IMP *imps, const char **types,
         // Note that realloc() alone doesn't work due to ptrauth.
         
         method_t::SortBySELAddress sorter;
-        std::stable_sort(newlist->begin(), newlist->end(), sorter);
+        std::stable_sort(&newlist->begin()->big(), &newlist->end()->big(), sorter);
         
         prepareMethodLists(cls, &newlist, 1, NO, NO);
         rwe->methods.attachLists(&newlist, 1);
@@ -6803,7 +6865,7 @@ class_addIvar(Class cls, const char *name, size_t size,
         memcpy(newlist, oldlist, oldsize);
         free(oldlist);
     } else {
-        newlist = (ivar_list_t *)calloc(sizeof(ivar_list_t), 1);
+        newlist = (ivar_list_t *)calloc(ivar_list_t::byteSize(sizeof(ivar_t), 1), 1);
         newlist->entsizeAndFlags = (uint32_t)sizeof(ivar_t);
     }
 
@@ -6897,11 +6959,11 @@ _class_addProperty(Class cls, const char *name,
         ASSERT(cls->isRealized());
         
         property_list_t *proplist = (property_list_t *)
-            malloc(sizeof(*proplist));
+            malloc(property_list_t::byteSize(sizeof(property_t), 1));
         proplist->count = 1;
-        proplist->entsizeAndFlags = sizeof(proplist->first);
-        proplist->first.name = strdupIfMutable(name);
-        proplist->first.attributes = copyPropertyAttributeString(attrs, count);
+        proplist->entsizeAndFlags = sizeof(property_t);
+        proplist->begin()->name = strdupIfMutable(name);
+        proplist->begin()->attributes = copyPropertyAttributeString(attrs, count);
         
         rwe->properties.attachLists(&proplist, 1);
         
@@ -7053,7 +7115,7 @@ objc_duplicateClass(Class original, const char *name,
     if (orig_rwe) {
         auto rwe = rw->extAllocIfNeeded();
         rwe->version = orig_rwe->version;
-        rwe->methods = orig_rwe->methods.duplicate();
+        orig_rwe->methods.duplicateInto(rwe->methods);
 
         // fixme dies when categories are added to the base
         rwe->properties = orig_rwe->properties;
@@ -7390,7 +7452,7 @@ static void free_class(Class cls)
 
     if (rwe) {
         for (auto& meth : rwe->methods) {
-            try_free(meth.types);
+            try_free(meth.types());
         }
         rwe->methods.tryFree();
     }
index 2a3a242aad6e004197447bd0c7e231503e041fa8..02fc2b5ffbc22b40299a5e75fcf09c12b233d398 100644 (file)
 #include "objc-private.h"
 #include "objc-sel-set.h"
 
-#if SUPPORT_PREOPT
-#include <objc-shared-cache.h>
-static const objc_selopt_t *builtins = NULL;
-#endif
-
 __BEGIN_DECLS
 
 static size_t SelrefCount = 0;
@@ -55,10 +50,6 @@ static SEL _objc_search_builtins(const char *key)
     if (!key) return (SEL)0;
     if ('\0' == *key) return (SEL)_objc_empty_selector;
 
-#if SUPPORT_PREOPT
-    if (builtins) return (SEL)builtins->get(key);
-#endif
-
     return (SEL)0;
 }
 
@@ -151,10 +142,6 @@ void sel_init(size_t selrefCount)
     // save this value for later
     SelrefCount = selrefCount;
 
-#if SUPPORT_PREOPT
-    builtins = preoptimizedSelectors();
-#endif
-
     // Register selectors used by libobjc
 
     mutex_locker_t lock(selLock);
index 27ee356f0b61e0870915e81bd7d59ff4efb44a42..da4c2286e30588a24945e67a03e3366f96bd6459 100644 (file)
 #include "objc-cache.h"
 #include "DenseMapExtras.h"
 
-#if SUPPORT_PREOPT
-static const objc_selopt_t *builtins = NULL;
-static bool useDyldSelectorLookup = false;
-#endif
-
 
 static objc::ExplicitInitDenseSet<const char *> namedSelectors;
 static SEL search_builtins(const char *key);
@@ -44,32 +39,13 @@ static SEL search_builtins(const char *key);
 void sel_init(size_t selrefCount)
 {
 #if SUPPORT_PREOPT
-    // If dyld finds a known shared cache selector, then it must be also looking
-    // in the shared cache table.
-    if (_dyld_get_objc_selector("retain") != nil)
-        useDyldSelectorLookup = true;
-    else
-        builtins = preoptimizedSelectors();
-
-    if (PrintPreopt && useDyldSelectorLookup) {
+    if (PrintPreopt) {
         _objc_inform("PREOPTIMIZATION: using dyld selector opt");
     }
-
-    if (PrintPreopt  &&  builtins) {
-        uint32_t occupied = builtins->occupied;
-        uint32_t capacity = builtins->capacity;
-        
-        _objc_inform("PREOPTIMIZATION: using selopt at %p", builtins);
-        _objc_inform("PREOPTIMIZATION: %u selectors", occupied);
-        _objc_inform("PREOPTIMIZATION: %u/%u (%u%%) hash table occupancy",
-                     occupied, capacity,
-                     (unsigned)(occupied/(double)capacity*100));
-    }
-       namedSelectors.init(useDyldSelectorLookup ? 0 : (unsigned)selrefCount);
-#else
-       namedSelectors.init((unsigned)selrefCount);
 #endif
 
+  namedSelectors.init((unsigned)selrefCount);
+
     // Register selectors used by libobjc
 
     mutex_locker_t lock(selLock);
@@ -110,17 +86,8 @@ BOOL sel_isMapped(SEL sel)
 static SEL search_builtins(const char *name) 
 {
 #if SUPPORT_PREOPT
-  if (builtins) {
-      SEL result = 0;
-      if ((result = (SEL)builtins->get(name)))
-          return result;
-
-      if ((result = (SEL)_dyld_get_objc_selector(name)))
-          return result;
-  } else if (useDyldSelectorLookup) {
-      if (SEL result = (SEL)_dyld_get_objc_selector(name))
-          return result;
-  }
+  if (SEL result = (SEL)_dyld_get_objc_selector(name))
+    return result;
 #endif
     return nil;
 }
index 6a73568a556d9a89c45770968d413a1e013a2d6a..6b974a398e52ead921fa230b994abfd1c8271836 100644 (file)
@@ -180,8 +180,7 @@ OBJC_EXPORT const char * _Nonnull object_getClassName(id _Nullable obj)
  * @note In a garbage-collected environment, the memory is scanned conservatively.
  */
 OBJC_EXPORT void * _Nullable object_getIndexedIvars(id _Nullable obj)
-    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
-    OBJC_ARC_UNAVAILABLE;
+    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
 
 /** 
  * Identifies a selector as being valid or invalid.
index e2c8b7d4e97c277f0e74a39c07255553cd93f37a..22e033443a94e8d73ba23fb8a490278986eff625 100644 (file)
@@ -23,9 +23,31 @@ int main()
     [TestRoot class];
     // Now class should be realized
 
-    result = (__bridge Class)(NXMapGet(gdb_objc_realized_classes, "TestRoot"));
+    if (!testdyld3()) {
+        // In dyld3 mode, the class will be in the launch closure and not in our table.
+        result = (__bridge Class)(NXMapGet(gdb_objc_realized_classes, "TestRoot"));
+        testassert(result);
+        testassert(result == [TestRoot class]);
+    }
+
+    Class dynamic = objc_allocateClassPair([TestRoot class], "Dynamic", 0);
+    objc_registerClassPair(dynamic);
+    result = (__bridge Class)(NXMapGet(gdb_objc_realized_classes, "Dynamic"));
     testassert(result);
-    testassert(result == [TestRoot class]);
+    testassert(result == dynamic);
+
+    Class *realizedClasses = objc_copyRealizedClassList(NULL);
+    bool foundTestRoot = false;
+    bool foundDynamic = false;
+    for (Class *cursor = realizedClasses; *cursor; cursor++) {
+        if (*cursor == [TestRoot class])
+            foundTestRoot = true;
+        if (*cursor == dynamic)
+            foundDynamic = true;
+    }
+    free(realizedClasses);
+    testassert(foundTestRoot);
+    testassert(foundDynamic);
 
     result = (__bridge Class)(NXMapGet(gdb_objc_realized_classes, "DoesNotExist"));
     testassert(!result);
diff --git a/test/libraryPath.c b/test/libraryPath.c
new file mode 100644 (file)
index 0000000..40cf7bd
--- /dev/null
@@ -0,0 +1,44 @@
+// TEST_CFLAGS -lobjc
+
+#include "test.h"
+#include <dlfcn.h>
+
+// We use DYLD_LIBRARY_PATH to run the tests against a particular copy of
+// libobjc. If this fails somehow (path is wrong, codesigning prevents loading,
+// etc.) then the typical result is a silent failure and we end up testing
+// /usr/lib/libobjc.A.dylib instead. This test detects when DYLD_LIBRARY_PATH is
+// set but libobjc isn't loaded from it.
+int main() {
+    char *dyldLibraryPath = getenv("DYLD_LIBRARY_PATH");
+    testprintf("DYLD_LIBRARY_PATH is %s\n", dyldLibraryPath);
+    if (dyldLibraryPath != NULL && strlen(dyldLibraryPath) > 0) {
+        int foundMatch = 0;
+        
+        dyldLibraryPath = strdup(dyldLibraryPath);
+        
+        Dl_info info;
+        int success = dladdr((void *)objc_msgSend, &info);
+        testassert(success);
+
+        testprintf("libobjc is located at %s\n", info.dli_fname);
+        
+        char *cursor = dyldLibraryPath;
+        char *path;
+        while ((path = strsep(&cursor, ":"))) {
+            char *resolved = realpath(path, NULL);
+            testprintf("Resolved %s to %s\n", path, resolved);
+            testprintf("Comparing %s and %s\n", resolved, info.dli_fname);
+            int comparison = strncmp(resolved, info.dli_fname, strlen(resolved));
+            free(resolved);
+            if (comparison == 0) {
+                testprintf("Found a match!\n");
+                foundMatch = 1;
+                break;
+            }
+        }
+
+        testprintf("Finished searching, foundMatch=%d\n", foundMatch);
+        testassert(foundMatch);
+    }
+    succeed(__FILE__);
+}
index 4dd9f86b6cded8174c61f16ac1a5dc5c1bc648f1..c2be1a017d32c9df2371bddc23680f22669dcbac 100644 (file)
@@ -1,4 +1,8 @@
 /*
+dyld3 calls the load callback with its own internal lock held, which causes
+this test to deadlock. Disable the test in dyld3 mode. If
+rdar://problem/53769512 is fixed then remove this.
+TEST_CONFIG DYLD=2
 TEST_BUILD
     $C{COMPILE} $DIR/load-noobjc.m -o load-noobjc.exe
     $C{COMPILE} $DIR/load-noobjc2.m -o libload-noobjc2.dylib -bundle -bundle_loader load-noobjc.exe
diff --git a/test/methodListSmall.h b/test/methodListSmall.h
new file mode 100644 (file)
index 0000000..c6f32e2
--- /dev/null
@@ -0,0 +1,226 @@
+#include "test.h"
+
+struct ObjCClass {
+    struct ObjCClass *isa;
+    struct ObjCClass *superclass;
+    void *cachePtr;
+    uintptr_t zero;
+    struct ObjCClass_ro *data;
+};
+
+struct ObjCClass_ro {
+    uint32_t flags;
+    uint32_t instanceStart;
+    uint32_t instanceSize;
+#ifdef __LP64__
+    uint32_t reserved;
+#endif
+
+    const uint8_t * ivarLayout;
+    
+    const char * name;
+    struct ObjCMethodList * baseMethodList;
+    struct protocol_list_t * baseProtocols;
+    const struct ivar_list_t * ivars;
+
+    const uint8_t * weakIvarLayout;
+    struct property_list_t *baseProperties;
+};
+
+struct ObjCMethod {
+    char *name;
+    char *type;
+    IMP imp;
+};
+
+struct ObjCMethodList {
+    uint32_t sizeAndFlags;
+    uint32_t count;
+    struct ObjCMethod methods[];
+};
+
+struct ObjCMethodSmall {
+    int32_t nameOffset;
+    int32_t typeOffset;
+    int32_t impOffset;
+};
+
+struct ObjCMethodListSmall {
+    uint32_t sizeAndFlags;
+    uint32_t count;
+    struct ObjCMethodSmall methods[];
+};
+
+
+extern struct ObjCClass OBJC_METACLASS_$_NSObject;
+extern struct ObjCClass OBJC_CLASS_$_NSObject;
+
+
+struct ObjCClass_ro FooMetaclass_ro = {
+    .flags = 1,
+    .instanceStart = 40,
+    .instanceSize = 40,
+    .name = "Foo",
+};
+
+struct ObjCClass FooMetaclass = {
+    .isa = &OBJC_METACLASS_$_NSObject,
+    .superclass = &OBJC_METACLASS_$_NSObject,
+    .cachePtr = &_objc_empty_cache,
+    .data = &FooMetaclass_ro,
+};
+
+
+int ranMyMethod1;
+extern "C" void myMethod1(id self __unused, SEL _cmd) {
+    testprintf("myMethod1\n");
+    testassert(_cmd == @selector(myMethod1));
+    ranMyMethod1 = 1;
+}
+
+int ranMyMethod2;
+extern "C" void myMethod2(id self __unused, SEL _cmd) {
+    testprintf("myMethod2\n");
+    testassert(_cmd == @selector(myMethod2));
+    ranMyMethod2 = 1;
+}
+
+int ranMyMethod3;
+extern "C" void myMethod3(id self __unused, SEL _cmd) {
+    testprintf("myMethod3\n");
+    testassert(_cmd == @selector(myMethod3));
+    ranMyMethod3 = 1;
+}
+
+int ranMyReplacedMethod1;
+extern "C" void myReplacedMethod1(id self __unused, SEL _cmd) {
+    testprintf("myReplacedMethod1\n");
+    testassert(_cmd == @selector(myMethod1));
+    ranMyReplacedMethod1 = 1;
+}
+
+int ranMyReplacedMethod2;
+extern "C" void myReplacedMethod2(id self __unused, SEL _cmd) {
+    testprintf("myReplacedMethod2\n");
+    testassert(_cmd == @selector(myMethod2));
+    ranMyReplacedMethod2 = 1;
+}
+
+struct BigStruct {
+  uintptr_t a, b, c, d, e, f, g;
+};
+
+int ranMyMethodStret;
+extern "C" BigStruct myMethodStret(id self __unused, SEL _cmd) {
+    testprintf("myMethodStret\n");
+    testassert(_cmd == @selector(myMethodStret));
+    ranMyMethodStret = 1;
+    BigStruct ret = {};
+    return ret;
+}
+
+int ranMyReplacedMethodStret;
+extern "C" BigStruct myReplacedMethodStret(id self __unused, SEL _cmd) {
+    testprintf("myReplacedMethodStret\n");
+    testassert(_cmd == @selector(myMethodStret));
+    ranMyReplacedMethodStret = 1;
+    BigStruct ret = {};
+    return ret;
+}
+
+extern struct ObjCMethodList Foo_methodlistSmall;
+
+asm(R"ASM(
+.section __TEXT,__cstring
+_MyMethod1Name:
+    .asciz "myMethod1"
+_MyMethod2Name:
+    .asciz "myMethod2"
+_MyMethod3Name:
+    .asciz "myMethod3"
+_BoringMethodType:
+    .asciz "v16@0:8"
+_MyMethodStretName:
+    .asciz "myMethodStret"
+_StretType:
+    .asciz "{BigStruct=QQQQQQQ}16@0:8"
+)ASM");
+
+#if __LP64__
+asm(R"ASM(
+.section __DATA,__objc_selrefs,literal_pointers,no_dead_strip
+_MyMethod1NameRef:
+    .quad _MyMethod1Name
+_MyMethod2NameRef:
+    .quad _MyMethod2Name
+_MyMethod3NameRef:
+    .quad _MyMethod3Name
+_MyMethodStretNameRef:
+    .quad _MyMethodStretName
+)ASM");
+#else
+asm(R"ASM(
+.section __DATA,__objc_selrefs,literal_pointers,no_dead_strip
+_MyMethod1NameRef:
+    .long _MyMethod1Name
+_MyMethod2NameRef:
+    .long _MyMethod2Name
+_MyMethod3NameRef:
+    .long _MyMethod3Name
+_MyMethodStretNameRef:
+    .long _MyMethodStretName
+)ASM");
+#endif
+
+#if MUTABLE_METHOD_LIST
+asm(".section __DATA,__objc_methlist\n");
+#else
+asm(".section __TEXT,__objc_methlist\n");
+#endif
+
+asm(R"ASM(
+    .p2align 2
+_Foo_methodlistSmall:
+    .long 12 | 0x80000000
+    .long 4
+    
+    .long _MyMethod1NameRef - .
+    .long _BoringMethodType - .
+    .long _myMethod1 - .
+    
+    .long _MyMethod2NameRef - .
+    .long _BoringMethodType - .
+    .long _myMethod2 - .
+    
+    .long _MyMethod3NameRef - .
+    .long _BoringMethodType - .
+    .long _myMethod3 - .
+    
+    .long _MyMethodStretNameRef - .
+    .long _StretType - .
+    .long _myMethodStret - .
+)ASM");
+
+struct ObjCClass_ro Foo_ro = {
+    .instanceStart = 8,
+    .instanceSize = 8,
+    .name = "Foo",
+    .baseMethodList = &Foo_methodlistSmall,
+};
+
+struct ObjCClass FooClass = {
+    .isa = &FooMetaclass,
+    .superclass = &OBJC_CLASS_$_NSObject,
+    .cachePtr = &_objc_empty_cache,
+    .data = &Foo_ro,
+};
+
+
+@interface Foo: NSObject
+
+- (void)myMethod1;
+- (void)myMethod2;
+- (void)myMethod3;
+- (BigStruct)myMethodStret;
+
+@end
diff --git a/test/methodListSmall.mm b/test/methodListSmall.mm
new file mode 100644 (file)
index 0000000..c10f29d
--- /dev/null
@@ -0,0 +1,89 @@
+// TEST_CFLAGS -std=c++11
+
+#include "methodListSmall.h"
+
+void testClass(Class c) {    
+    id foo = [c new];
+    [foo myMethod1];
+    testassert(ranMyMethod1);
+    [foo myMethod2];
+    testassert(ranMyMethod2);
+    [foo myMethod3];
+    testassert(ranMyMethod3);
+    
+    Method m1 = class_getInstanceMethod(c, @selector(myMethod1));
+    testassert(m1);
+    testassert(method_getName(m1) == @selector(myMethod1));
+    testassert(strcmp(method_getTypeEncoding(m1), "v16@0:8") == 0);
+    testassert(method_getImplementation(m1) == (IMP)myMethod1);
+    
+    method_setImplementation(m1, (IMP)myReplacedMethod1);
+    testassert(method_getImplementation(m1) == (IMP)myReplacedMethod1);
+    [foo myMethod1];
+    testassert(ranMyReplacedMethod1);
+    
+    Method m2 = class_getInstanceMethod(c, @selector(myMethod2));
+    auto method_invoke_cast = (void (*)(id, Method))method_invoke;
+    
+    ranMyMethod2 = 0;
+    method_invoke_cast(foo, m2);
+    testassert(ranMyMethod2);
+    
+    method_setImplementation(m2, (IMP)myReplacedMethod2);
+    method_invoke_cast(foo, m2);
+    testassert(ranMyReplacedMethod2);
+    
+    Method mstret = class_getInstanceMethod(c, @selector(myMethodStret));
+#if __arm64__
+    // No _stret variant on ARM64. We'll test struct return through
+    // method_invoke anyway just to be thorough.
+    auto method_invoke_stret_cast = (BigStruct (*)(id, Method))method_invoke;
+#else
+    auto method_invoke_stret_cast = (BigStruct (*)(id, Method))method_invoke_stret;
+#endif
+    
+    [foo myMethodStret];
+    testassert(ranMyMethodStret);
+    
+    ranMyMethodStret = 0;
+    method_invoke_stret_cast(foo, mstret);
+    testassert(ranMyMethodStret);
+    
+    method_setImplementation(mstret, (IMP)myReplacedMethodStret);
+    [foo myMethodStret];
+    testassert(ranMyReplacedMethodStret);
+    
+    ranMyReplacedMethodStret = 0;
+    method_invoke_stret_cast(foo, mstret);
+    testassert(ranMyReplacedMethodStret);
+    
+    auto *desc1 = method_getDescription(m1);
+    testassert(desc1->name == @selector(myMethod1));
+    testassert(desc1->types == method_getTypeEncoding(m1));
+    
+    auto *desc2 = method_getDescription(m2);
+    testassert(desc2->name == @selector(myMethod2));
+    testassert(desc2->types == method_getTypeEncoding(m2));
+    
+    auto *descstret = method_getDescription(mstret);
+    testassert(descstret->name == @selector(myMethodStret));
+    testassert(descstret->types == method_getTypeEncoding(mstret));
+}
+
+int main() {
+    Class fooClass = (__bridge Class)&FooClass;
+
+    // Make sure this class can be duplicated and works as expected.
+    // Duplicate it before testClass mucks around with the methods.
+    // Need to realize fooClass before duplicating it, hence the
+    // class message.
+    Class dupedClass = objc_duplicateClass([fooClass class], "FooDup", 0);
+
+    testprintf("Testing class.\n");
+    testClass(fooClass);
+
+    testprintf("Testing duplicate class.\n");
+    testClass(dupedClass);
+
+    succeed(__FILE__);
+}
diff --git a/test/methodListSmallMutableMemory.mm b/test/methodListSmallMutableMemory.mm
new file mode 100644 (file)
index 0000000..9250fea
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+TEST_CFLAGS -std=c++11
+TEST_CRASHES
+TEST_RUN_OUTPUT
+objc\[\d+\]: CLASS: class 'Foo' 0x[0-9a-fA-F]+ small method list 0x[0-9a-fA-F]+ is not in immutable memory
+objc\[\d+\]: HALTED
+END
+*/
+
+#define MUTABLE_METHOD_LIST 1
+
+#include "methodListSmall.h"
+
+int main() {
+    Class fooClass = (__bridge Class)&FooClass;
+    [fooClass new];
+    fail("Should have crashed");
+}
index dbe222cfb4169cbd9137526d8e64b3f21be04ce6..d7b007f4a2466751635c8a6fc8163e6ea3228ff2 100644 (file)
@@ -114,10 +114,6 @@ void check_nonpointer(id obj, Class cls)
 }
 
 
-@interface OS_object <NSObject>
-+(id)alloc;
-@end
-
 @interface Fake_OS_object : NSObject {
     int refcnt;
     int xref_cnt;
@@ -138,7 +134,7 @@ void check_nonpointer(id obj, Class cls)
 }
 @end
 
-@interface Sub_OS_object : OS_object @end
+@interface Sub_OS_object : NSObject @end
 
 @implementation Sub_OS_object
 @end
@@ -147,6 +143,9 @@ void check_nonpointer(id obj, Class cls)
 
 int main()
 {
+    Class OS_object = objc_getClass("OS_object");
+    class_setSuperclass([Sub_OS_object class], OS_object);
+
     uintptr_t isa;
 
 #if SUPPORT_PACKED_ISA
@@ -193,7 +192,6 @@ int main()
     objc_setAssociatedObject(index_o, assoc, assoc, OBJC_ASSOCIATION_ASSIGN);
     testassert(__builtin_popcountl(isa ^ ISA(index_o)) == 1);
 
-
     testprintf("Isa without index\n");
     id raw_o = [OS_object alloc];
     check_raw_pointer(raw_o, [OS_object class]);
diff --git a/test/supported-inline-refcnt.m b/test/supported-inline-refcnt.m
new file mode 100644 (file)
index 0000000..bd32ba9
--- /dev/null
@@ -0,0 +1,85 @@
+// TEST_CONFIG MEM=mrc
+// TEST_CFLAGS -framework CoreFoundation -Weverything
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+#include "test.h"
+#pragma clang diagnostic pop
+#include <objc/NSObject.h>
+#include <objc/objc-internal.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+
+// Some warnings just aren't feasible to work around. We'll disable them instead.
+#pragma clang diagnostic ignored "-Watomic-implicit-seq-cst"
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+#pragma clang diagnostic ignored "-Wold-style-cast"
+
+static int deallocCount;
+@interface Refcnt: NSObject @end
+@implementation Refcnt {
+    int _rc;
+}
+
+_OBJC_SUPPORTED_INLINE_REFCNT(_rc)
+
+- (void)dealloc {
+    deallocCount++;
+    [super dealloc];
+}
+
+@end
+
+@interface MainRefcnt: NSObject @end
+@implementation MainRefcnt {
+    int _rc;
+}
+
+_OBJC_SUPPORTED_INLINE_REFCNT_WITH_DEALLOC2MAIN(_rc)
+
+- (void)dealloc {
+    testassert(pthread_main_np());
+    deallocCount++;
+    [super dealloc];
+}
+
+@end
+
+int main()
+{
+    Refcnt *obj = [Refcnt new];
+    [obj retain];
+    [obj retain];
+    [obj retain];
+    [obj release];
+    [obj release];
+    [obj release];
+    [obj release];
+    testassert(deallocCount == 1);
+
+    MainRefcnt *obj2 = [MainRefcnt new];
+    [obj2 retain];
+    [obj2 retain];
+    [obj2 retain];
+    
+    dispatch_group_t group = dispatch_group_create();
+    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
+        [obj2 release];
+    });
+    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
+        [obj2 release];
+    });
+    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
+        [obj2 release];
+    });
+    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
+        [obj2 release];
+    });
+    
+    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
+        testassert(deallocCount == 2);
+        succeed(__FILE__);
+    });
+    
+    CFRunLoopRun();
+}
index f4332a1c909aec1e1c96e8733e82772a43edd357..4ae8137326109257041e066fe34cecc0cf9008c1 100644 (file)
@@ -143,6 +143,19 @@ static inline void testwarn(const char *msg, ...)
 
 static inline void testnoop() { }
 
+// Are we running in dyld3 mode?
+// Note: checks by looking for the DYLD_USE_CLOSURES environment variable.
+// This is is always set by our test script, but this won't give the right
+// answer when being run manually unless that variable is set.
+static inline bool testdyld3(void) {
+    static int dyld = 0;
+    if (dyld == 0) {
+        const char *useClosures = getenv("DYLD_USE_CLOSURES");
+        dyld = useClosures && useClosures[0] == '1' ? 3 : 2;
+    }
+    return dyld == 3;
+}
+
 // Prevent deprecation warnings from some runtime functions.
 
 static inline void test_objc_flush_caches(Class cls)
index 0d0886bb4a11efaa836b3982cb39765255fba696..46c3d0350b295885b7da344fdc6b7844e2361b67 100755 (executable)
@@ -42,6 +42,8 @@ options:
     RUN=0|1        (run the tests?)
     VERBOSE=0|1|2  (0=quieter  1=print commands executed  2=full test output)
     BATS=0|1       (build for and/or run in BATS?)
+    BUILD_SHARED_CACHE=0|1  (build a dyld shared cache with the root and test against that)
+    DYLD=2|3       (test in dyld 2 or dyld 3 mode)
 
 examples:
 
@@ -230,7 +232,22 @@ sub make {
         next if $cmd =~ /^\s*$/;
         $cmd .= " 2>&1";
         print "$cmd\n" if $VERBOSE;
-        $output .= `$cmd`;
+        eval {
+            local $SIG{ALRM} = sub { die "alarm\n" };
+            # Timeout after 600 seconds so a deadlocked test doesn't wedge the
+            # entire test suite. Increase to an hour for B&I builds.
+            if (exists $ENV{"RC_XBS"}) {
+                alarm 3600;
+            } else {
+                alarm 600;
+            }
+            $output .= `$cmd`;
+            alarm 0;
+        };
+        if ($@) {
+            die unless $@ eq "alarm\n";
+            $output .= "\nTIMED OUT";
+        }
         last if $?;
     }
     print "$output\n" if $VERBOSE;
@@ -966,6 +983,16 @@ sub run_simple {
         $env .= " OBJC_DEBUG_DONT_CRASH=YES";
     }
 
+    if ($C{DYLD} eq "2") {
+        $env .= " DYLD_USE_CLOSURES=0";
+    }
+    elsif ($C{DYLD} eq "3") {
+        $env .= " DYLD_USE_CLOSURES=1";
+    }
+    else {
+        die "unknown DYLD setting $C{DYLD}";
+    }
+
     my $output;
 
     if ($C{ARCH} =~ /^arm/ && `uname -p` !~ /^arm/) {
@@ -1060,6 +1087,26 @@ sub dirContainsAllTestLibs {
     return 1;
 }
 
+sub findIncludeDir {
+    my ($root, $includePath) = @_;
+
+    foreach my $candidate ("$root/../SDKContentRoot/$includePath", "$root/$includePath") {
+        my $found = -e $candidate;
+        my $foundstr = ($found ? "found" : "didn't find");
+        print "note:   $foundstr $includePath at $candidate\n" if $VERBOSE;
+        return $candidate if $found;
+    }
+
+    die "Unable to find $includePath in $root.\n";
+}
+
+sub buildSharedCache {
+    my $Cref = shift;
+    my %C = %$Cref;
+    
+    make("update_dyld_shared_cache -verbose -cache_dir $BUILDDIR -overlay $C{TESTLIBDIR}/../..");
+}
+
 sub make_one_config {
     my $configref = shift;
     my $root = shift;
@@ -1315,9 +1362,8 @@ sub make_one_config {
 
         my $library_path = $C{TESTLIBDIR};
         $cflags .= " -L$library_path";
-        # fixme Root vs SDKContentRoot
-        $C{TESTINCLUDEDIR} = "$root/../SDKContentRoot/usr/include";
-        $C{TESTLOCALINCLUDEDIR} = "$root/../SDKContentRoot/usr/local/include";
+        $C{TESTINCLUDEDIR} = findIncludeDir($root, "usr/include");
+        $C{TESTLOCALINCLUDEDIR} = findIncludeDir($root, "usr/local/include");
         $cflags .= " -isystem '$C{TESTINCLUDEDIR}'";
         $cflags .= " -isystem '$C{TESTLOCALINCLUDEDIR}'";
     }
@@ -1634,6 +1680,10 @@ $args{OSVERSION} = getargs("OS", "macosx-default-default");
 $args{MEM} = getargs("MEM", "mrc,arc");
 $args{LANGUAGE} = [ map { lc($_) } @{getargs("LANGUAGE", "c,objective-c,c++,objective-c++")} ];
 
+$args{BUILD_SHARED_CACHE} = getargs("BUILD_SHARED_CACHE", 0);
+
+$args{DYLD} = getargs("DYLD", "2,3");
+
 $args{CC} = getargs("CC", "clang");
 
 $HOST = getarg("HOST", "iphone");