--- /dev/null
+Language: Cpp
+BasedOnStyle: WebKit
+
+AlignConsecutiveDeclarations: true
+AlignOperands: false
+AlignTrailingComments: true
+IndentWidth: 4
+
+Standard: Cpp11
+
+UseTab: Never
+
+SortIncludes: false
my $in = <IN>;
close IN or die $!;
- if($in =~ s{/usr/lib/dyld}{/usr/lib/dyle})
+ if($in =~ s{/usr/lib/dyld}{/usr/local/dy})
{
open OUT, ">$arg" or die $!;
print OUT $in;
ALIGNMENT[arch=armv7s] = -Wl,-segalign,0x4000
-BASE_ADDRESS[arch=armv7*] = 0x1fe00000
-BASE_ADDRESS[arch=arm64] = 0x120000000
-BASE_ADDRESS[arch=i386] = 0x8fe00000
-BASE_ADDRESS[arch=x86_64] = 0x7fff5fc00000
-
ENTRY[sdk=*simulator*] = -Wl,-e,_start_sim
ENTRY[sdk=iphoneos*] = -Wl,-e,__dyld_start
ENTRY[sdk=macosx*] = -Wl,-e,__dyld_start
-LIBSYSTEM_LIBS[sdk=*simulator*] = -Wl,-upward-lsystem_sim_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_sim_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_sim_kernel
-LIBSYSTEM_LIBS[sdk=iphoneos*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel
-LIBSYSTEM_LIBS[sdk=macosx*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel
+LIBSYSTEM_LIBS[sdk=*simulator*] = -Wl,-upward-lsystem_sim_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_sim_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_sim_kernel -Wl,-upward-lsystem_sandbox -Wl,-upward-ldispatch
+LIBSYSTEM_LIBS[sdk=iphoneos*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel -Wl,-upward-lsystem_sandbox -Wl,-upward-ldispatch
+LIBSYSTEM_LIBS[sdk=macosx*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel -Wl,-upward-lsystem_sandbox -Wl,-upward-ldispatch
INSTALL_PATH = /usr/lib/system
Right before the process's main() is called, dyld prints out information about how
dyld spent its time. Useful for analyzing launch performance.
.TP
-.B DYLD_DISABLE_DOFS
+.B DYLD_PRINT_STATISTICS_DETAILS
+Right before the process's main() is called, dyld prints out detailed information about how
+dyld spent its time. Useful for analyzing launch performance.
+.TP
+.B DYLD_DISABLE_DOFS
Causes dyld not register dtrace static probes with the kernel.
.TP
.B DYLD_PRINT_INITIALIZERS
-.Dd Oct 10, 2008
+.Dd June 23, 2016
.Dt update_dyld_shared_cache 1
.Os Darwin
.Sh NAME
.Op Fl arch Ar arch
.Op Fl force
.Op Fl debug
-.Op Fl sort_by_name
-.Op Fl universal_boot
+.Op Fl universal_boot
.Op Fl verify
-.Op Fl dylib_list Ar file
-.Op Fl iPhone
-.Op Fl cache_dir Ar dir
.Sh DESCRIPTION
.Nm update_dyld_shared_cache
ensures that dyld's shared cache is up-to-date. This tool is normally
to regenerated the shared cache files even if they appear to be already up-to-date.
.It Fl debug
This option prints out additional information about the work being done.
-.It Fl sort_by_name
-By default
-.Nm update_dyld_shared_cache
-assigns a random start address to each mach-o image in the cache.
-This option causes the start addresses to be chosen in path order, thus subsequent runs will
-produce the same address layout which can help reproduce some bugs.
.It Fl universal_boot
-This option can only be used running on an machine with an Intel processor. It builds caches
-that can be used when booting on both 32-bit and 64-bit machines.
-.It Fl dylib_list Ar file
-Instead of scanning /var/db/dyld/shared_region_roots/, this option provides a file that contains
-a list of the dylibs to use when building the shared cache file.
+This option builds caches for all machines.
.It Fl verify
Will regenerate a shared cache in-memory that matches the randomization of the existing shared
cache file. Then instead of writing the cache file, it compares the in-memory cache file to
the on disk version and reports any differences.
-.It Fl iPhone
-indicates that cache is not for the current Mac OS X, but for rather for an iPhone
-.It Fl cache_dir Ar directory
-This option specifies the directory in which to create the cache file(s). If not specified,
-the cache file(s) are created in the standard location (e.g. var/db/dyld/) of the root partition.
.El
.Sh FILES
.Tp
objects = {
/* Begin PBXAggregateTarget section */
+ 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 37A0AD0B1C15FFF500731E50 /* Build configuration list for PBXAggregateTarget "update_dyld_shared_cache" */;
+ buildPhases = (
+ );
+ dependencies = (
+ 37A0AD1B1C16004600731E50 /* PBXTargetDependency */,
+ 37A0AD131C16003600731E50 /* PBXTargetDependency */,
+ 37A0AD151C16003600731E50 /* PBXTargetDependency */,
+ 37A0AD171C16003600731E50 /* PBXTargetDependency */,
+ 37A0AD191C16003600731E50 /* PBXTargetDependency */,
+ 37A0AD111C16003600731E50 /* PBXTargetDependency */,
+ );
+ name = update_dyld_shared_cache;
+ productName = update_dyld_shared_cache;
+ };
F908134211D3ED0B00626CC1 /* libdyld */ = {
isa = PBXAggregateTarget;
buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */;
dependencies = (
F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */,
F9ED4CA90630A78A00DF4E74 /* PBXTargetDependency */,
- F93937380A94FB6A00070A07 /* PBXTargetDependency */,
+ 37A0AD0F1C16000F00731E50 /* PBXTargetDependency */,
);
name = all;
productName = all;
};
+ F9F6F4271C1FB0A700BD8FED /* dyld_tests */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = F9F6F42A1C1FB0A700BD8FED /* Build configuration list for PBXAggregateTarget "dyld_tests" */;
+ buildPhases = (
+ F9F6F42B1C1FB0AE00BD8FED /* build */,
+ );
+ dependencies = (
+ F97FF3661C237F97000ACDD2 /* PBXTargetDependency */,
+ );
+ name = dyld_tests;
+ productName = dyld_tests;
+ };
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
+ 3703A1141B38C1B300ADBA7F /* AdjustForNewSegmentLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */; };
+ 3703A1161B38C1B300ADBA7F /* BindAllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6E1A367093001E839B /* BindAllImages.cpp */; };
+ 3703A1171B38C1B300ADBA7F /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */; };
+ 3703A1181B38C1B300ADBA7F /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */; };
+ 3703A1191B38C1B300ADBA7F /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */; };
+ 3703A11A1B38C1B300ADBA7F /* MultiCacheBuilder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */; };
+ 3703A11B1B38C1B300ADBA7F /* FileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6F1A367093001E839B /* FileCache.cpp */; };
+ 3703A11C1B38C1B300ADBA7F /* SharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE701A367093001E839B /* SharedCache.cpp */; };
+ 3703A1261B38C22900ADBA7F /* dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3703A1251B38C22900ADBA7F /* dyld_shared_cache_builder.mm */; };
+ 370C6E541BDEF08000387223 /* libspindump.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 370C6E531BDEF08000387223 /* libspindump.dylib */; settings = {ATTRIBUTES = (Required, ); }; };
+ 370E5F421CC06CF8000158F2 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 370E5F401CC06CF8000158F2 /* Logging.cpp */; };
+ 370E5F431CC06CF8000158F2 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 370E5F401CC06CF8000158F2 /* Logging.cpp */; };
+ 370E5F441CC06CF8000158F2 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 370E5F401CC06CF8000158F2 /* Logging.cpp */; };
+ 371D29821B2F53C8000BBE48 /* MultiCacheBuilder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */; };
+ 3733C9071BD98F6800420392 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
+ 3733C9081BD98F6900420392 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
+ 3733C9091BD98F6A00420392 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
+ 375E6F441C59DEFF001BB760 /* MachOProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */; };
+ 376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; };
+ 376ABDBA1C5930E7009F0011 /* MachOProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */; };
+ 376ABDBB1C5930E7009F0011 /* MachOProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */; };
+ 376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; };
+ 377685F51AC4B27D00026E6C /* AdjustForNewSegmentLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */; };
+ 377685F61AC4B27D00026E6C /* BindAllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6E1A367093001E839B /* BindAllImages.cpp */; };
+ 377685F71AC4B27D00026E6C /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */; };
+ 377685F81AC4B27D00026E6C /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */; };
+ 377685F91AC4B27D00026E6C /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */; };
+ 377685FA1AC4B27D00026E6C /* FileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6F1A367093001E839B /* FileCache.cpp */; };
+ 377685FB1AC4B27D00026E6C /* SharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE701A367093001E839B /* SharedCache.cpp */; };
+ 377686041AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 377686031AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm */; };
+ 378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
+ 378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
+ 37BF1D761B6168150048BC27 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37BF1D731B6168150048BC27 /* Manifest.mm */; };
+ 37BF1D771B6168150048BC27 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37BF1D731B6168150048BC27 /* Manifest.mm */; };
+ 37F7A5951BB362CA0039043A /* update_dyld_shared_cache.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E93E291AAFB0B10080E640 /* update_dyld_shared_cache.mm */; };
+ 37F7A5981BB364130039043A /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37BF1D731B6168150048BC27 /* Manifest.mm */; };
+ 37F7A5991BB364130039043A /* MultiCacheBuilder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */; };
+ 37F7A59A1BB3642F0039043A /* FileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6F1A367093001E839B /* FileCache.cpp */; };
+ 37F7A59B1BB3642F0039043A /* SharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE701A367093001E839B /* SharedCache.cpp */; };
+ 37F7A59C1BB364530039043A /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */; };
+ 37F7A59D1BB364530039043A /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */; };
+ 37F7A59E1BB364530039043A /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */; };
+ 37F7A59F1BB364530039043A /* AdjustForNewSegmentLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */; };
+ 37F7A5A01BB364530039043A /* BindAllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6E1A367093001E839B /* BindAllImages.cpp */; };
F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; };
F908134D11D3ED6200626CC1 /* dyld_images.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; };
F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */ = {isa = PBXBuildFile; fileRef = F99EE6AE06B48D4200BF1992 /* dlfcn.h */; };
F908136D11D3FB3A00626CC1 /* dyld.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FF0070D27BB00F78484 /* dyld.3 */; };
F908136E11D3FB3A00626CC1 /* dlopen_preflight.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */; };
F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; };
- F93666E0163B4C42002ECADA /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F93666DF163B4C42002ECADA /* CoreFoundation.framework */; };
- F93666E2163B4C58002ECADA /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F93666E1163B4C58002ECADA /* Security.framework */; };
- F93937470A94FC4700070A07 /* update_dyld_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */; };
+ F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */; };
F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */; };
F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */; settings = {COMPILER_FLAGS = "-O3"; }; };
+ F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */; };
+ F958D4771C7FCE6700A0B199 /* dyld_process_info_notify.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */; };
+ F97FF3601C236408000ACDD2 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35E1C236402000ACDD2 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; };
+ F97FF3611C23640C000ACDD2 /* nocr.c in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.c */; };
+ F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */ = {isa = PBXBuildFile; fileRef = F97FF3631C237F5C000ACDD2 /* nocr.1 */; };
F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */; };
F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */; };
F99B8EA30FEC1C4200701838 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */; };
F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */; };
F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
+ F9FF8C161C69B080009F8A53 /* dyld_process_info.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F95090D01C5AB89A0031F81D /* dyld_process_info.h */; };
/* End PBXBuildFile section */
/* Begin PBXBuildRule section */
isEditable = 1;
outputFiles = (
);
+ script = "";
};
F921D317070376A6000D1056 /* PBXBuildRule */ = {
isa = PBXBuildRule;
isEditable = 1;
outputFiles = (
);
+ script = "";
};
F921D318070376B0000D1056 /* PBXBuildRule */ = {
isa = PBXBuildRule;
isEditable = 1;
outputFiles = (
);
+ script = "";
};
F921D31E070376F1000D1056 /* PBXBuildRule */ = {
isa = PBXBuildRule;
/* End PBXBuildRule section */
/* Begin PBXContainerItemProxy section */
- F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = {
+ 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
- remoteInfo = libdyld.dylib;
+ remoteGlobalIDString = 37A0AD0A1C15FFF500731E50;
+ remoteInfo = update_dyld_shared_cache;
};
- F93937370A94FB6A00070A07 /* PBXContainerItemProxy */ = {
+ 37A0AD101C16003600731E50 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
remoteGlobalIDString = F93937310A94FAF700070A07;
- remoteInfo = update_dyld_shared_cache;
+ remoteInfo = update_dyld_shared_cache_tool;
};
- F99B8E9F0FEC195800701838 /* PBXContainerItemProxy */ = {
+ 37A0AD121C16003600731E50 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = F99B8E550FEC10F600701838;
- remoteInfo = dyld_shared_cache_util;
+ remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
+ remoteInfo = libdsc;
};
- F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */ = {
+ 37A0AD141C16003600731E50 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F9D1001114D8D0BA00099D91;
+ remoteInfo = dsc_extractor;
+ };
+ 37A0AD161C16003600731E50 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 377685F21AC4B27D00026E6C;
+ remoteInfo = multi_dyld_shared_cache_builder;
+ };
+ 37A0AD181C16003600731E50 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 3703A1111B38C1B300ADBA7F;
+ remoteInfo = dyld_shared_cache_builder;
+ };
+ 37A0AD1A1C16004600731E50 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
remoteGlobalIDString = F99B8E550FEC10F600701838;
remoteInfo = dyld_shared_cache_util;
};
- F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */ = {
+ F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
- remoteInfo = libdsc;
+ remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
+ remoteInfo = libdyld.dylib;
};
- F9CE330A120F40EA0098B590 /* PBXContainerItemProxy */ = {
+ F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
- remoteInfo = libdsc;
+ remoteGlobalIDString = F97FF3551C23638F000ACDD2;
+ remoteInfo = nocr;
};
- F9D1004614D8D91100099D91 /* PBXContainerItemProxy */ = {
+ F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = F9D1001114D8D0BA00099D91;
- remoteInfo = dsc_extractor;
+ remoteGlobalIDString = F99B8E550FEC10F600701838;
+ remoteInfo = dyld_shared_cache_util;
+ };
+ F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
+ remoteInfo = libdsc;
};
F9ED4CA60630A78A00DF4E74 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
+ 3703A1201B38C1B300ADBA7F /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ 377685FE1AC4B27D00026E6C /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
F908135111D3ED9000626CC1 /* usr|include|mach-o */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
files = (
F908135D11D3FACD00626CC1 /* dyld-interposing.h in usr|local|include|mach-o */,
F908135E11D3FACD00626CC1 /* dyld_cache_format.h in usr|local|include|mach-o */,
+ F9FF8C161C69B080009F8A53 /* dyld_process_info.h in usr|local|include|mach-o */,
F908135F11D3FACD00626CC1 /* dyld_gdb.h in usr|local|include|mach-o */,
F908136011D3FACD00626CC1 /* dyld_priv.h in usr|local|include|mach-o */,
);
name = "usr|share|man|man3";
runOnlyForDeploymentPostprocessing = 1;
};
+ F97FF3541C23638F000ACDD2 /* install man page */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man1";
+ dstSubfolderSpec = 0;
+ files = (
+ F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */,
+ );
+ name = "install man page";
+ runOnlyForDeploymentPostprocessing = 1;
+ };
F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 834A90AB0E1D85D600555761 /* ObjCLegacyAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjCLegacyAbstraction.hpp; sourceTree = "<group>"; };
- 834A90AC0E1D85D600555761 /* ObjCModernAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjCModernAbstraction.hpp; sourceTree = "<group>"; };
+ 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3703A1251B38C22900ADBA7F /* dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dyld_shared_cache_builder.mm; sourceTree = "<group>"; usesTabs = 0; };
+ 370C6E531BDEF08000387223 /* libspindump.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libspindump.dylib; path = usr/lib/libspindump.dylib; sourceTree = SDKROOT; };
+ 370E5F401CC06CF8000158F2 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Logging.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ 370E5F411CC06CF8000158F2 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; usesTabs = 0; };
+ 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MultiCacheBuilder.mm; sourceTree = "<group>"; usesTabs = 0; };
+ 371D29831B30E587000BBE48 /* MultiCacheBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiCacheBuilder.h; sourceTree = "<group>"; usesTabs = 0; };
+ 374DDAE11AC0A0F70097CFF0 /* Trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Trie.hpp; sourceTree = "<group>"; };
+ 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachOProxy.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ 376ABDB81C5930E7009F0011 /* MachOProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachOProxy.h; sourceTree = "<group>"; usesTabs = 0; };
+ 376ED1D71C46F2710051DD54 /* Metabom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metabom.framework; path = AppleInternal/Library/Frameworks/Metabom.framework; sourceTree = SDKROOT; };
+ 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = multi_dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; };
+ 377686031AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = multi_dyld_shared_cache_builder.mm; sourceTree = "<group>"; usesTabs = 0; };
+ 37BF1D731B6168150048BC27 /* Manifest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Manifest.mm; sourceTree = "<group>"; usesTabs = 0; };
+ 37BF1D741B6168150048BC27 /* Manifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Manifest.h; sourceTree = "<group>"; usesTabs = 0; };
+ 37E93E291AAFB0B10080E640 /* update_dyld_shared_cache.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = update_dyld_shared_cache.mm; sourceTree = "<group>"; usesTabs = 0; };
+ 37F7A5961BB363820039043A /* Bom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bom.framework; path = System/Library/PrivateFrameworks/Bom.framework; sourceTree = SDKROOT; };
EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; };
EF799FEB070D27BB00F78484 /* dladdr.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dladdr.3; path = doc/man/man3/dladdr.3; sourceTree = SOURCE_ROOT; };
EF799FEC070D27BB00F78484 /* dlclose.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlclose.3; path = doc/man/man3/dlclose.3; sourceTree = SOURCE_ROOT; };
F90C540A1A82D78000558E8C /* update_dyld_shared_cache_entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = update_dyld_shared_cache_entitlements.plist; sourceTree = "<group>"; };
F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = "<group>"; };
F918691408B16D2500E0F9DB /* dyld-interposing.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-interposing.h"; path = "include/mach-o/dyld-interposing.h"; sourceTree = "<group>"; };
- F93666DF163B4C42002ECADA /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
- F93666E1163B4C58002ECADA /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
+ F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMegaDylib.cpp; path = src/ImageLoaderMegaDylib.cpp; sourceTree = "<group>"; };
+ F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMegaDylib.h; path = src/ImageLoaderMegaDylib.h; sourceTree = "<group>"; };
F93937320A94FAF700070A07 /* update_dyld_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; };
F939373E0A94FC4700070A07 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = Architectures.hpp; sourceTree = "<group>"; };
F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = CacheFileAbstraction.hpp; sourceTree = "<group>"; };
F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = "<group>"; };
F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = "<group>"; };
F93937440A94FC4700070A07 /* MachOLayout.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOLayout.hpp; sourceTree = "<group>"; };
- F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = update_dyld_shared_cache.cpp; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOClassic.cpp; path = src/ImageLoaderMachOClassic.cpp; sourceTree = "<group>"; };
F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOClassic.h; path = src/ImageLoaderMachOClassic.h; sourceTree = "<group>"; };
F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = "<group>"; };
F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = "<group>"; };
+ F95090D01C5AB89A0031F81D /* dyld_process_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyld_process_info.h; path = "include/mach-o/dyld_process_info.h"; sourceTree = "<group>"; usesTabs = 0; };
+ F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_process_info.cpp; path = src/dyld_process_info.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F958D4751C7FCD4A00A0B199 /* dyld_process_info_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyld_process_info_internal.h; path = src/dyld_process_info_internal.h; sourceTree = "<group>"; };
+ F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_process_info_notify.cpp; path = src/dyld_process_info_notify.cpp; sourceTree = "<group>"; };
F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = "<group>"; };
F971DD131A4A0E0700BBDD52 /* base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = "<group>"; };
F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = "<group>"; };
F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = "<group>"; };
F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_shared_cache.xcconfig; sourceTree = "<group>"; };
F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = "<group>"; };
+ F97FF3561C23638F000ACDD2 /* nocr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nocr; sourceTree = BUILT_PRODUCTS_DIR; };
+ F97FF3581C23638F000ACDD2 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
+ F97FF35E1C236402000ACDD2 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; sourceTree = "<group>"; };
+ F97FF35F1C236402000ACDD2 /* nocr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nocr.c; path = testing/nocr/nocr.c; sourceTree = "<group>"; };
+ F97FF3631C237F5C000ACDD2 /* nocr.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = nocr.1; path = ../../../testing/nocr/nocr.1; sourceTree = "<group>"; };
F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.h; sourceTree = "<group>"; };
F98935B90A9A412B00FB6228 /* MachOBinder.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOBinder.hpp; sourceTree = "<group>"; };
F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachORebaser.hpp; sourceTree = "<group>"; };
F98D274C0AA79D7400416316 /* dyld_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_images.h; path = "include/mach-o/dyld_images.h"; sourceTree = "<group>"; };
+ F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptimizerBranches.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F98E19001A3FC1A9008DB73F /* OptimizerBranches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OptimizerBranches.h; sourceTree = "<group>"; usesTabs = 0; };
+ F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptimizerLinkedit.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptimizerObjC.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F98E190E1A40D167008DB73F /* ObjC1Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjC1Abstraction.hpp; sourceTree = "<group>"; usesTabs = 0; };
+ F98E190F1A40D167008DB73F /* ObjC2Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjC2Abstraction.hpp; sourceTree = "<group>"; usesTabs = 0; };
F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dyld_shared_cache_util.cpp; sourceTree = "<group>"; };
F99B8E670FEC121100701838 /* dyld_shared_cache_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_util; sourceTree = BUILT_PRODUCTS_DIR; };
F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = libdyld_data_symbols.dirty; path = src/libdyld_data_symbols.dirty; sourceTree = "<group>"; };
F9AC7E930B7BB67700FEB38B /* version.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = BUILT_PRODUCTS_DIR; };
F9AFEA3216F15CE300CB5161 /* start_glue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = start_glue.h; path = src/start_glue.h; sourceTree = "<group>"; };
F9B01E3D0739ABDE00CF981B /* dyld.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld.exp; path = src/dyld.exp; sourceTree = SOURCE_ROOT; };
- F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-shared-cache.h"; path = "include/objc-shared-cache.h"; sourceTree = "<group>"; };
+ F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-shared-cache.h"; path = "include/objc-shared-cache.h"; sourceTree = "<group>"; usesTabs = 0; };
F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_extractor.cpp; sourceTree = "<group>"; };
F9CE30791208F1B50098B590 /* dsc_extractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_extractor.h; sourceTree = "<group>"; };
+ F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AdjustForNewSegmentLocation.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F9D0FE6E1A367093001E839B /* BindAllImages.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BindAllImages.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F9D0FE6F1A367093001E839B /* FileCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FileCache.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F9D0FE701A367093001E839B /* SharedCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SharedCache.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F9D0FE711A367093001E839B /* mega-dylib-utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "mega-dylib-utils.h"; sourceTree = "<group>"; usesTabs = 0; };
F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = dsc_extractor.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; path = update_dyld_shared_cache.1; sourceTree = "<group>"; };
F9D49CCB1458B95200F86ADD /* start_glue.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = start_glue.s; path = src/start_glue.s; sourceTree = "<group>"; };
F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = dlopen_preflight.3; sourceTree = "<group>"; };
+ F9E7AD981BE47CE30025D311 /* CodeSigningTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeSigningTypes.h; sourceTree = "<group>"; };
F9ED4C980630A76000DF4E74 /* dyld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld; sourceTree = BUILT_PRODUCTS_DIR; };
F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdyld.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_gdb.cpp; path = src/dyld_gdb.cpp; sourceTree = SOURCE_ROOT; };
F9F2A5590F7AEE9800B7C9EB /* libdsc.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdsc.a; sourceTree = BUILT_PRODUCTS_DIR; };
F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_iterator.cpp; sourceTree = "<group>"; };
F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_iterator.h; sourceTree = "<group>"; };
+ F9F6F4261C1FAF8000BD8FED /* testing */ = {isa = PBXFileReference; lastKnownFileType = folder; path = testing; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 3703A11D1B38C1B300ADBA7F /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */,
+ 378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 377685FD1AC4B27D00026E6C /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */,
+ 378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F93937300A94FAF700070A07 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- F93666E2163B4C58002ECADA /* Security.framework in Frameworks */,
- F93666E0163B4C42002ECADA /* CoreFoundation.framework in Frameworks */,
+ 370C6E541BDEF08000387223 /* libspindump.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F97FF3531C23638F000ACDD2 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXGroup;
children = (
EF799FE9070D27BB00F78484 /* dyld.1 */,
+ F97FF3631C237F5C000ACDD2 /* nocr.1 */,
F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */,
);
name = man1;
isa = PBXGroup;
children = (
F90C540A1A82D78000558E8C /* update_dyld_shared_cache_entitlements.plist */,
- F93666E1163B4C58002ECADA /* Security.framework */,
- F93666DF163B4C42002ECADA /* CoreFoundation.framework */,
F939373E0A94FC4700070A07 /* Architectures.hpp */,
F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */,
F93937400A94FC4700070A07 /* dyld_cache_format.h */,
F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */,
F98935B90A9A412B00FB6228 /* MachOBinder.hpp */,
F95C95160E994796007B7CB8 /* MachOTrie.hpp */,
- 834A90AB0E1D85D600555761 /* ObjCLegacyAbstraction.hpp */,
- 834A90AC0E1D85D600555761 /* ObjCModernAbstraction.hpp */,
- F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */,
F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */,
F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */,
F9CE30781208F1B50098B590 /* dsc_extractor.cpp */,
F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */,
);
path = configs;
+ sourceTree = SOURCE_ROOT;
+ };
+ F97FF3571C23638F000ACDD2 /* nocr */ = {
+ isa = PBXGroup;
+ children = (
+ F97FF3581C23638F000ACDD2 /* main.c */,
+ );
+ path = nocr;
sourceTree = "<group>";
};
+ F9D0FE6C1A367093001E839B /* interlinked-dylibs */ = {
+ isa = PBXGroup;
+ children = (
+ 370C6E531BDEF08000387223 /* libspindump.dylib */,
+ 376ED1D71C46F2710051DD54 /* Metabom.framework */,
+ 37F7A5961BB363820039043A /* Bom.framework */,
+ 374DDAE11AC0A0F70097CFF0 /* Trie.hpp */,
+ F9E7AD981BE47CE30025D311 /* CodeSigningTypes.h */,
+ F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */,
+ F98E19001A3FC1A9008DB73F /* OptimizerBranches.h */,
+ F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */,
+ F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */,
+ F98E190F1A40D167008DB73F /* ObjC2Abstraction.hpp */,
+ F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */,
+ F98E190E1A40D167008DB73F /* ObjC1Abstraction.hpp */,
+ F9D0FE6E1A367093001E839B /* BindAllImages.cpp */,
+ F9D0FE6F1A367093001E839B /* FileCache.cpp */,
+ F9D0FE701A367093001E839B /* SharedCache.cpp */,
+ 376ABDB81C5930E7009F0011 /* MachOProxy.h */,
+ 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */,
+ 37BF1D741B6168150048BC27 /* Manifest.h */,
+ 37BF1D731B6168150048BC27 /* Manifest.mm */,
+ 371D29831B30E587000BBE48 /* MultiCacheBuilder.h */,
+ 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */,
+ 37E93E291AAFB0B10080E640 /* update_dyld_shared_cache.mm */,
+ 377686031AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm */,
+ 3703A1251B38C22900ADBA7F /* dyld_shared_cache_builder.mm */,
+ F9D0FE711A367093001E839B /* mega-dylib-utils.h */,
+ 370E5F411CC06CF8000158F2 /* Logging.h */,
+ 370E5F401CC06CF8000158F2 /* Logging.cpp */,
+ );
+ path = "interlinked-dylibs";
+ sourceTree = SOURCE_ROOT;
+ };
F9ED4C870630A72200DF4E74 = {
isa = PBXGroup;
children = (
+ F9F6F4261C1FAF8000BD8FED /* testing */,
F971DD121A4A0E0700BBDD52 /* configs */,
F9ED4CBB0630A7AA00DF4E74 /* src */,
F9ED4CC30630A7BE00DF4E74 /* doc */,
F9ED4CBE0630A7B100DF4E74 /* include */,
+ F97FF3571C23638F000ACDD2 /* nocr */,
F9ED4C990630A76000DF4E74 /* Products */,
+ F9D0FE6C1A367093001E839B /* interlinked-dylibs */,
F939373D0A94FC4700070A07 /* launch-cache */,
);
indentWidth = 4;
F9F2A5590F7AEE9800B7C9EB /* libdsc.a */,
F99B8E670FEC121100701838 /* dyld_shared_cache_util */,
F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */,
+ 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
+ 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */,
+ F97FF3561C23638F000ACDD2 /* nocr */,
);
name = Products;
sourceTree = "<group>";
F9ED4CBB0630A7AA00DF4E74 /* src */ = {
isa = PBXGroup;
children = (
+ F97FF35E1C236402000ACDD2 /* execserver.defs */,
+ F97FF35F1C236402000ACDD2 /* nocr.c */,
F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */,
F9ED4CC70630A7F100DF4E74 /* dyld.cpp */,
F9ED4CC80630A7F100DF4E74 /* dyld.h */,
F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */,
F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */,
F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */,
+ F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */,
+ F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */,
F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */,
F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */,
F9B01E3D0739ABDE00CF981B /* dyld.exp */,
F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */,
F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */,
F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */,
+ F958D4751C7FCD4A00A0B199 /* dyld_process_info_internal.h */,
+ F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */,
+ F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */,
);
name = src;
sourceTree = "<group>";
F9ED4CBE0630A7B100DF4E74 /* include */ = {
isa = PBXGroup;
children = (
+ F95090D01C5AB89A0031F81D /* dyld_process_info.h */,
F918691408B16D2500E0F9DB /* dyld-interposing.h */,
F98D274C0AA79D7400416316 /* dyld_images.h */,
F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */,
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
- F93937310A94FAF700070A07 /* update_dyld_shared_cache */ = {
+ 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */ = {
isa = PBXNativeTarget;
- buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache" */;
+ buildConfigurationList = 3703A1211B38C1B300ADBA7F /* Build configuration list for PBXNativeTarget "dyld_shared_cache_builder" */;
+ buildPhases = (
+ 3703A1121B38C1B300ADBA7F /* make dyld_cache_config.h */,
+ 3703A1131B38C1B300ADBA7F /* Sources */,
+ 3703A11D1B38C1B300ADBA7F /* Frameworks */,
+ 3703A1201B38C1B300ADBA7F /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = dyld_shared_cache_builder;
+ productName = update_os_interlinked_dylib;
+ productReference = 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */;
+ productType = "com.apple.product-type.tool";
+ };
+ 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */;
+ buildPhases = (
+ 377685F31AC4B27D00026E6C /* make dyld_cache_config.h */,
+ 377685F41AC4B27D00026E6C /* Sources */,
+ 377685FD1AC4B27D00026E6C /* Frameworks */,
+ 377685FE1AC4B27D00026E6C /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = multi_dyld_shared_cache_builder;
+ productName = update_os_interlinked_dylib;
+ productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
+ productType = "com.apple.product-type.tool";
+ };
+ F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */;
buildPhases = (
F91083C91702592700831889 /* create dyld_cache_config.h */,
F939372F0A94FAF700070A07 /* Sources */,
buildRules = (
);
dependencies = (
- F99B8EA00FEC195800701838 /* PBXTargetDependency */,
- F9CE330B120F40EA0098B590 /* PBXTargetDependency */,
- F9D1004714D8D91100099D91 /* PBXTargetDependency */,
);
- name = update_dyld_shared_cache;
+ name = update_dyld_shared_cache_tool;
productName = update_dyld_shared_cache;
productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */;
productType = "com.apple.product-type.tool";
};
+ F97FF3551C23638F000ACDD2 /* nocr */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */;
+ buildPhases = (
+ F97FF3521C23638F000ACDD2 /* Sources */,
+ F97FF3531C23638F000ACDD2 /* Frameworks */,
+ F97FF3541C23638F000ACDD2 /* install man page */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = nocr;
+ productName = nocr;
+ productReference = F97FF3561C23638F000ACDD2 /* nocr */;
+ productType = "com.apple.product-type.tool";
+ };
F99B8E550FEC10F600701838 /* dyld_shared_cache_util */ = {
isa = PBXNativeTarget;
buildConfigurationList = F99B8E5D0FEC10F800701838 /* Build configuration list for PBXNativeTarget "dyld_shared_cache_util" */;
F9ED4C8B0630A72300DF4E74 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0440;
+ LastUpgradeCheck = 0630;
+ TargetAttributes = {
+ 37A0AD0A1C15FFF500731E50 = {
+ CreatedOnToolsVersion = 7.0;
+ };
+ F97FF3551C23638F000ACDD2 = {
+ CreatedOnToolsVersion = 7.1;
+ };
+ F9F6F4271C1FB0A700BD8FED = {
+ CreatedOnToolsVersion = 7.1;
+ };
+ };
};
buildConfigurationList = F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */;
compatibilityVersion = "Xcode 3.2";
projectRoot = "";
targets = (
F9ED4C920630A73900DF4E74 /* all */,
+ 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */,
F9ED4C970630A76000DF4E74 /* dyld */,
F908134211D3ED0B00626CC1 /* libdyld */,
F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */,
- F93937310A94FAF700070A07 /* update_dyld_shared_cache */,
+ F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */,
F9F2A5580F7AEE9800B7C9EB /* libdsc */,
F99B8E550FEC10F600701838 /* dyld_shared_cache_util */,
F9D1001114D8D0BA00099D91 /* dsc_extractor */,
+ 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
+ 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */,
+ F9F6F4271C1FB0A700BD8FED /* dyld_tests */,
+ F97FF3551C23638F000ACDD2 /* nocr */,
);
};
/* End PBXProject section */
/* Begin PBXShellScriptBuildPhase section */
+ 3703A1121B38C1B300ADBA7F /* make dyld_cache_config.h */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "make dyld_cache_config.h";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/dyld_cache_config.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/bash;
+ shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
+ showEnvVarsInLog = 0;
+ };
+ 377685F31AC4B27D00026E6C /* make dyld_cache_config.h */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "make dyld_cache_config.h";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/dyld_cache_config.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/bash;
+ shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
+ showEnvVarsInLog = 0;
+ };
F907E2490FA6469000BFEDBD /* install iPhone file */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/bash;
- shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -n \"${ARM_SDK}\" ]; then\n if [ -r \"${ARM_SDK}/usr/include/mach/shared_region.h\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n else\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${ARM_SDK}/usr/include/mach/shared_region.h'\"\n exit 1\n fi\nelse\n\tif [ -z ${RC_PURPLE} ]; then \n\t\techo \"#define ARM_SHARED_REGION_START 0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM_SHARED_REGION_SIZE 0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM64_SHARED_REGION_START 0x180000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM64_SHARED_REGION_SIZE 0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n else\n echo \"ERROR: env var ARM_SDK not defined\"\n exit 1\n fi\nfi\n\n";
+ shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\n";
showEnvVarsInLog = 0;
};
F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */ = {
shellScript = "# link with all .a files in /usr/local/lib/dyld\nls -1 ${SDKROOT}/usr/local/lib/dyld/*.a > ${DERIVED_SOURCES_DIR}/archives.txt \n\n# link with crash report archive if it exists\nif [ -f ${SDKROOT}/usr/local/lib/libCrashReporterClient.a ]\nthen\n echo \\\"${SDKROOT}/usr/local/lib/libCrashReporterClient.a\\\" >> ${DERIVED_SOURCES_DIR}/archives.txt \nfi\n\n";
showEnvVarsInLog = 0;
};
+ F9F6F42B1C1FB0AE00BD8FED /* build */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 12;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = build;
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "${SRCROOT}/testing/build_tests.py && cp ${SRCROOT}/testing/run_all_dyld_tests.py ${DSTROOT}/AppleInternal/CoreOS/tests/dyld/\n";
+ showEnvVarsInLog = 0;
+ };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 3703A1131B38C1B300ADBA7F /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3703A1141B38C1B300ADBA7F /* AdjustForNewSegmentLocation.cpp in Sources */,
+ 370E5F441CC06CF8000158F2 /* Logging.cpp in Sources */,
+ 376ABDBB1C5930E7009F0011 /* MachOProxy.cpp in Sources */,
+ 3703A1161B38C1B300ADBA7F /* BindAllImages.cpp in Sources */,
+ 3703A1171B38C1B300ADBA7F /* OptimizerObjC.cpp in Sources */,
+ 3703A1181B38C1B300ADBA7F /* OptimizerBranches.cpp in Sources */,
+ 3703A1191B38C1B300ADBA7F /* OptimizerLinkedit.cpp in Sources */,
+ 3733C9081BD98F6900420392 /* dsc_iterator.cpp in Sources */,
+ 3703A11A1B38C1B300ADBA7F /* MultiCacheBuilder.mm in Sources */,
+ 3703A1261B38C22900ADBA7F /* dyld_shared_cache_builder.mm in Sources */,
+ 3703A11B1B38C1B300ADBA7F /* FileCache.cpp in Sources */,
+ 3703A11C1B38C1B300ADBA7F /* SharedCache.cpp in Sources */,
+ 37BF1D771B6168150048BC27 /* Manifest.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 377685F41AC4B27D00026E6C /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 377685F51AC4B27D00026E6C /* AdjustForNewSegmentLocation.cpp in Sources */,
+ 370E5F431CC06CF8000158F2 /* Logging.cpp in Sources */,
+ 376ABDBA1C5930E7009F0011 /* MachOProxy.cpp in Sources */,
+ 377686041AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm in Sources */,
+ 377685F61AC4B27D00026E6C /* BindAllImages.cpp in Sources */,
+ 377685F71AC4B27D00026E6C /* OptimizerObjC.cpp in Sources */,
+ 377685F81AC4B27D00026E6C /* OptimizerBranches.cpp in Sources */,
+ 3733C9071BD98F6800420392 /* dsc_iterator.cpp in Sources */,
+ 377685F91AC4B27D00026E6C /* OptimizerLinkedit.cpp in Sources */,
+ 371D29821B2F53C8000BBE48 /* MultiCacheBuilder.mm in Sources */,
+ 377685FA1AC4B27D00026E6C /* FileCache.cpp in Sources */,
+ 377685FB1AC4B27D00026E6C /* SharedCache.cpp in Sources */,
+ 37BF1D761B6168150048BC27 /* Manifest.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F939372F0A94FAF700070A07 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- F93937470A94FC4700070A07 /* update_dyld_shared_cache.cpp in Sources */,
+ 37F7A59C1BB364530039043A /* OptimizerBranches.cpp in Sources */,
+ 370E5F421CC06CF8000158F2 /* Logging.cpp in Sources */,
+ 375E6F441C59DEFF001BB760 /* MachOProxy.cpp in Sources */,
+ 37F7A59D1BB364530039043A /* OptimizerLinkedit.cpp in Sources */,
+ 37F7A59E1BB364530039043A /* OptimizerObjC.cpp in Sources */,
+ 37F7A59F1BB364530039043A /* AdjustForNewSegmentLocation.cpp in Sources */,
+ 37F7A5A01BB364530039043A /* BindAllImages.cpp in Sources */,
+ 3733C9091BD98F6A00420392 /* dsc_iterator.cpp in Sources */,
+ 37F7A59A1BB3642F0039043A /* FileCache.cpp in Sources */,
+ 37F7A59B1BB3642F0039043A /* SharedCache.cpp in Sources */,
+ 37F7A5981BB364130039043A /* Manifest.mm in Sources */,
+ 37F7A5991BB364130039043A /* MultiCacheBuilder.mm in Sources */,
+ 37F7A5951BB362CA0039043A /* update_dyld_shared_cache.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F97FF3521C23638F000ACDD2 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F97FF3601C236408000ACDD2 /* execserver.defs in Sources */,
+ F97FF3611C23640C000ACDD2 /* nocr.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F9ED4CDA0630A7F100DF4E74 /* dyldExceptions.c in Sources */,
F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */,
F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */,
+ F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */,
F9ED4CE10630A7F100DF4E74 /* ImageLoader.cpp in Sources */,
F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */,
F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */,
F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */,
F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */,
F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */,
+ F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */,
+ F958D4771C7FCE6700A0B199 /* dyld_process_info_notify.cpp in Sources */,
F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
- F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = {
+ 37A0AD0F1C16000F00731E50 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
- targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
+ target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */;
+ targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */;
+ };
+ 37A0AD111C16003600731E50 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */;
+ targetProxy = 37A0AD101C16003600731E50 /* PBXContainerItemProxy */;
+ };
+ 37A0AD131C16003600731E50 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
+ targetProxy = 37A0AD121C16003600731E50 /* PBXContainerItemProxy */;
};
- F93937380A94FB6A00070A07 /* PBXTargetDependency */ = {
+ 37A0AD151C16003600731E50 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = F93937310A94FAF700070A07 /* update_dyld_shared_cache */;
- targetProxy = F93937370A94FB6A00070A07 /* PBXContainerItemProxy */;
+ target = F9D1001114D8D0BA00099D91 /* dsc_extractor */;
+ targetProxy = 37A0AD141C16003600731E50 /* PBXContainerItemProxy */;
};
- F99B8EA00FEC195800701838 /* PBXTargetDependency */ = {
+ 37A0AD171C16003600731E50 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
+ targetProxy = 37A0AD161C16003600731E50 /* PBXContainerItemProxy */;
+ };
+ 37A0AD191C16003600731E50 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */;
+ targetProxy = 37A0AD181C16003600731E50 /* PBXContainerItemProxy */;
+ };
+ 37A0AD1B1C16004600731E50 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
- targetProxy = F99B8E9F0FEC195800701838 /* PBXContainerItemProxy */;
+ targetProxy = 37A0AD1A1C16004600731E50 /* PBXContainerItemProxy */;
+ };
+ F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
+ targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
+ };
+ F97FF3661C237F97000ACDD2 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F97FF3551C23638F000ACDD2 /* nocr */;
+ targetProxy = F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */;
};
F99B8EB20FEC220C00701838 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
targetProxy = F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */;
};
- F9CE330B120F40EA0098B590 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
- targetProxy = F9CE330A120F40EA0098B590 /* PBXContainerItemProxy */;
- };
- F9D1004714D8D91100099D91 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = F9D1001114D8D0BA00099D91 /* dsc_extractor */;
- targetProxy = F9D1004614D8D91100099D91 /* PBXContainerItemProxy */;
- };
F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F9ED4C970630A76000DF4E74 /* dyld */;
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
+ 3703A1221B38C1B300ADBA7F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks",
+ );
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ USER_HEADER_SEARCH_PATHS = "../launch-cache/";
+ VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+ };
+ name = Debug;
+ };
+ 3703A1231B38C1B300ADBA7F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks",
+ );
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ USER_HEADER_SEARCH_PATHS = "../launch-cache/";
+ VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+ 377686001AC4B27D00026E6C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)/AppleInternal/Library/Frameworks",
+ );
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+ PRODUCT_NAME = multi_dyld_shared_cache_builder;
+ SDKROOT = macosx.internal;
+ STRIP_INSTALLED_PRODUCT = NO;
+ USER_HEADER_SEARCH_PATHS = "../launch-cache/";
+ VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+ };
+ name = Debug;
+ };
+ 377686011AC4B27D00026E6C /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)/AppleInternal/Library/Frameworks",
+ );
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+ PRODUCT_NAME = multi_dyld_shared_cache_builder;
+ SDKROOT = macosx.internal;
+ STRIP_INSTALLED_PRODUCT = NO;
+ USER_HEADER_SEARCH_PATHS = "../launch-cache/";
+ VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+ 37A0AD0C1C15FFF500731E50 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 37A0AD0D1C15FFF500731E50 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
F908134311D3ED0C00626CC1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
F93937350A94FB2900070A07 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ );
GCC_DYNAMIC_NO_PIC = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
- OTHER_CPLUSPLUSFLAGS = (
- "-std=c++11",
- "-stdlib=libc++",
- "$(OTHER_CFLAGS)",
- );
+ OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "-stdlib=libc++";
PRODUCT_NAME = update_dyld_shared_cache;
SDKROOT = macosx.internal;
- VALID_ARCHS = "x86_64 i386";
+ VALID_ARCHS = x86_64;
};
name = Debug;
};
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */;
buildSettings = {
- ARCHS = x86_64;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ );
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = s;
GCC_THREADSAFE_STATICS = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
- OTHER_CPLUSPLUSFLAGS = (
- "-std=c++11",
- "-stdlib=libc++",
- "$(OTHER_CFLAGS)",
- );
+ OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "-stdlib=libc++";
PRODUCT_NAME = update_dyld_shared_cache;
SDKROOT = macosx.internal;
STRIP_INSTALLED_PRODUCT = YES;
STRIP_STYLE = debugging;
- VALID_ARCHS = "x86_64 i386";
+ VALID_ARCHS = x86_64;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
+ F97FF35A1C23638F000ACDD2 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(DERIVED_SOURCES_DIR)/**",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ };
+ name = Debug;
+ };
+ F97FF35B1C23638F000ACDD2 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(DERIVED_SOURCES_DIR)/**",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ };
+ name = Release;
+ };
F99B8E580FEC10F600701838 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.10.xctoolchain/usr/include,
+ "$(SRCROOT)/interlinked-dylibs/",
+ );
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
PRODUCT_NAME = dyld_shared_cache_util;
};
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_DYNAMIC_NO_PIC = NO;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.10.xctoolchain/usr/include,
+ "$(SRCROOT)/interlinked-dylibs/",
+ );
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
PRODUCT_NAME = dyld_shared_cache_util;
SKIP_INSTALL = NO;
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DYLIB_COMPATIBILITY_VERSION = "";
DYLIB_CURRENT_VERSION = "";
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DYLIB_COMPATIBILITY_VERSION = "";
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
CLANG_CXX_LIBRARY = "libc++";
+ CLANG_WARN_EMPTY_BODY = YES;
+ CODE_SIGN_IDENTITY = "-";
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO;
GCC_WARN_MISSING_PARENTHESES = YES;
+ GCC_WARN_SHADOW = YES;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
GCC_WARN_UNINITIALIZED_AUTOS = NO;
HEADER_SEARCH_PATHS = (
"$(OTHER_CFLAGS)",
);
OTHER_LDFLAGS = (
- "-Wl,-seg1addr,$(BASE_ADDRESS)",
"@$(DERIVED_SOURCES_DIR)/archives.txt",
"-nostdlib",
"-Wl,-dylinker",
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
CLANG_CXX_LIBRARY = "libc++";
+ CLANG_WARN_EMPTY_BODY = YES;
+ CODE_SIGN_IDENTITY = "-";
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_PREPROCESSOR_DEFINITIONS = "DYLD_VERSION=$(RC_ProjectSourceVersion)";
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
+ GCC_WARN_SHADOW = YES;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
HEADER_SEARCH_PATHS = (
./include,
);
LD_GENERATE_MAP_FILE = YES;
ORDER_FILE = "$(SRCROOT)/src/dyld.order";
+ OTHER_CFLAGS = "";
"OTHER_CFLAGS[arch=armv6]" = "-mthumb";
OTHER_CPLUSPLUSFLAGS = (
"-stdlib=libc++",
"$(OTHER_CFLAGS)",
);
OTHER_LDFLAGS = (
- "-Wl,-seg1addr,$(BASE_ADDRESS)",
"@$(DERIVED_SOURCES_DIR)/archives.txt",
"-nostdlib",
"-Wl,-dylinker",
"-stdlib=libc++",
"$(ALIGNMENT)",
"$(ENTRY)",
+ "-Wl,-no_data_const",
+ "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__const:__crash_info:__data:__common:__bss",
);
SDKROOT = macosx.internal;
STRIPFLAGS = "-S";
baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_WARN_EMPTY_BODY = YES;
+ COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES;
EXECUTABLE_PREFIX = lib;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
INSTALLHDRS_COPY_PHASE = YES;
- OTHER_CFLAGS = "-mno-global-merge";
+ OTHER_CFLAGS = "";
OTHER_LDFLAGS = (
"-nostdlib",
"$(LIBSYSTEM_LIBS)",
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_WARN_EMPTY_BODY = YES;
+ COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEAD_CODE_STRIPPING = YES;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
INSTALLHDRS_COPY_PHASE = YES;
- OTHER_CFLAGS = "-mno-global-merge";
+ OTHER_CFLAGS = "";
OTHER_CPLUSPLUSFLAGS = (
"-fno-exceptions",
"$(OTHER_CFLAGS)",
baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
buildSettings = {
CLANG_CXX_LIBRARY = "compiler-default";
+ ONLY_ACTIVE_ARCH = NO;
+ SDKROOT = macosx.internal;
+ SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx watchos appletvos";
};
name = Debug;
};
baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
buildSettings = {
CLANG_CXX_LIBRARY = "compiler-default";
+ SDKROOT = macosx.internal;
+ SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx watchos appletvos";
};
name = Release;
};
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
F9F2A55B0F7AEE9900B7C9EB /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
};
name = Release;
};
+ F9F6F4281C1FB0A700BD8FED /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ F9F6F4291C1FB0A700BD8FED /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 3703A1211B38C1B300ADBA7F /* Build configuration list for PBXNativeTarget "dyld_shared_cache_builder" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3703A1221B38C1B300ADBA7F /* Debug */,
+ 3703A1231B38C1B300ADBA7F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 377686001AC4B27D00026E6C /* Debug */,
+ 377686011AC4B27D00026E6C /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 37A0AD0B1C15FFF500731E50 /* Build configuration list for PBXAggregateTarget "update_dyld_shared_cache" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 37A0AD0C1C15FFF500731E50 /* Debug */,
+ 37A0AD0D1C15FFF500731E50 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache" */ = {
+ F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F93937350A94FB2900070A07 /* Debug */,
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F97FF35A1C23638F000ACDD2 /* Debug */,
+ F97FF35B1C23638F000ACDD2 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F99B8E5D0FEC10F800701838 /* Build configuration list for PBXNativeTarget "dyld_shared_cache_util" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ F9F6F42A1C1FB0A700BD8FED /* Build configuration list for PBXAggregateTarget "dyld_tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9F6F4281C1FB0A700BD8FED /* Debug */,
+ F9F6F4291C1FB0A700BD8FED /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
};
rootObject = F9ED4C8B0630A72300DF4E74 /* Project object */;
#endif
+
/*
* Beginning in Mac OS X 10.4, this is how gdb discovers which mach-o images are loaded in a process.
*
dyld_error_kind_symbol_missing=4
};
+/* internal limit */
+#define DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT 8
struct dyld_all_image_infos {
uint32_t version; /* 1 in Mac OS X 10.4 and 10.5 */
uintptr_t sharedCacheSlide;
/* the following field is only in version 13 (Mac OS X 10.9, iOS 7.0) and later */
uint8_t sharedCacheUUID[16];
- /* the following field is only in version 14 (Mac OS X 10.9, iOS 7.0) and later */
- uintptr_t reserved[16];
+ /* the following field is only in version 15 (macOS 10.12, iOS 10.0) and later */
+ uintptr_t sharedCacheBaseAddress;
+ uint64_t infoArrayChangeTimestamp;
+ const char* dyldPath;
+ mach_port_t notifyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+#if __LP64__
+ uintptr_t reserved[13-(DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT/2)];
+#else
+ uintptr_t reserved[12-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+#endif
};
extern void _dyld_fork_child();
-//
-// Possible state changes for which you can register to be notified
-//
+// DEPRECATED
enum dyld_image_states
{
dyld_image_state_mapped = 10, // No batch notification for this
dyld_image_state_terminated = 60 // Only single notification for this
};
-//
-// Callback that provides a bottom-up array of images
-// For dyld_image_state_[dependents_]mapped state only, returning non-NULL will cause dyld to abort loading all those images
-// and append the returned string to its load failure error message. dyld does not free the string, so
-// it should be a literal string or a static buffer
-//
+// DEPRECATED
typedef const char* (*dyld_image_state_change_handler)(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]);
+
+
+typedef void (*_dyld_objc_notify_mapped)(unsigned count, const char* const paths[], const struct mach_header* const mh[]);
+typedef void (*_dyld_objc_notify_init)(const char* path, const struct mach_header* mh);
+typedef void (*_dyld_objc_notify_unmapped)(const char* path, const struct mach_header* mh);
+
+
//
-// Register a handler to be called when any image changes to the requested state.
-// If 'batch' is true, the callback is called with an array of all images that are in the requested state sorted by dependency.
-// If 'batch' is false, the callback is called with one image at a time as each image transitions to the the requested state.
-// During the call to this function, the handler may be called back with existing images and the handler should
-// not return a string, since there is no load to abort. In batch mode, existing images at or past the request
-// state supplied in the callback. In non-batch mode, the callback is called for each image exactly in the
-// requested state.
+// Note: only for use by objc runtime
+// Register handlers to be called when objc images are mapped, unmapped, and initialized.
+// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
+// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
+// call dlopen() on them to keep them from being unloaded. During the call to _dyld_objc_notify_register(),
+// dyld will call the "mapped" function with already loaded objc images. During any later dlopen() call,
+// dyld will also call the "mapped" function. Dyld will call the "init" function when dyld would be called
+// initializers in that image. This is when objc calls any +load methods in that image.
//
-extern void
-dyld_register_image_state_change_handler(enum dyld_image_states state, bool batch, dyld_image_state_change_handler handler);
+void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
+ _dyld_objc_notify_init init,
+ _dyld_objc_notify_unmapped unmapped);
+
//
// Exists in Mac OS X 10.4 and later through _dyld_func_lookup()
// Exists in Mac OS X 10.6 and later through libSystem.dylib
//
-const struct dyld_all_image_infos* _dyld_get_all_image_infos();
+const struct dyld_all_image_infos* _dyld_get_all_image_infos() __attribute__((deprecated));
#define DYLD_MACOSX_VERSION_10_9 0x000A0900
#define DYLD_MACOSX_VERSION_10_10 0x000A0A00
#define DYLD_MACOSX_VERSION_10_11 0x000A0B00
+#define DYLD_MACOSX_VERSION_10_12 0x000A0C00
#define DYLD_IOS_VERSION_2_0 0x00020000
#define DYLD_IOS_VERSION_2_1 0x00020100
#define DYLD_IOS_VERSION_8_0 0x00080000
#define DYLD_IOS_VERSION_8_1 0x00080100
#define DYLD_IOS_VERSION_8_2 0x00080200
+#define DYLD_IOS_VERSION_8_3 0x00080300
+#define DYLD_IOS_VERSION_8_4 0x00080400
#define DYLD_IOS_VERSION_9_0 0x00090000
+#define DYLD_IOS_VERSION_9_1 0x00090100
+#define DYLD_IOS_VERSION_9_2 0x00090200
+#define DYLD_IOS_VERSION_9_3 0x00090300
+#define DYLD_IOS_VERSION_10_0 0x000A0000
+
+
+#define DYLD_WATCHOS_VERSION_1_0 0x00010000
+#define DYLD_WATCHOS_VERSION_2_0 0x00020000
+#define DYLD_WATCHOS_VERSION_2_1 0x00020100
+#define DYLD_WATCHOS_VERSION_2_2 0x00020200
+#define DYLD_WATCHOS_VERSION_3_0 0x00030000
+
//
extern uint32_t dyld_get_program_sdk_watch_os_version(); // __WATCHOS_AVAILABLE(2.0);
+// Watch OS only.
+// This finds the Watch min OS version that the main executable was built to run on.
+// Note: dyld_get_program_min_os_version() returns the iOS equivalent (e.g. 9.0)
+// whereas this returns the raw watchOS version (e.g. 2.0).
+// Exists in Watch OS 3.0 and later
+extern uint32_t dyld_get_program_min_watch_os_version(); // __WATCHOS_AVAILABLE(3.0);
+
+
//
// This finds the min OS version a binary was built to run on.
// Returns zero on error, or if no min OS recorded in binary.
extern void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count);
+struct dyld_shared_cache_dylib_text_info {
+ uint64_t version; // current version 1
+ // following fields all exist in version 1
+ uint64_t loadAddressUnslid;
+ uint64_t textSegmentSize;
+ uuid_t dylibUuid;
+ const char* path; // pointer invalid at end of iterations
+};
+typedef struct dyld_shared_cache_dylib_text_info dyld_shared_cache_dylib_text_info;
+
+//
+// Given the UUID of a dyld shared cache file, this function will attempt to locate the cache
+// file and if found iterate all images, returning info about each one. Returns 0 on success.
+//
+// Exists in Mac OS X 10.11 and later
+// iOS 9.0 and later
+extern int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info));
+
+
+//
+// Given the UUID of a dyld shared cache file, and a NULL terminated array of extra directory paths to search,
+// this function will scan the standard and extra directories looking for a cache file that matches the UUID
+// and if found iterate all images, returning info about each one. Returns 0 on success.
+//
+// Exists in Mac OS X 10.12 and later
+// iOS 10.0 and later
+extern int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info));
+
+
+//
+// Returns if the specified address range is in a dyld owned memory
+// that is mapped read-only and will never be unloaded.
+//
+// Exists in Mac OS X 10.12 and later
+// iOS 10.0 and later
+extern bool _dyld_is_memory_immutable(const void* addr, size_t length);
+
+
+//
+// Finds the UUID (from LC_UUID load command) of given image.
+// Returns false if LC_UUID is missing or mach_header is malformed.
+//
+// Exists in Mac OS X 10.12 and later
+// Exists in iOS 10.0 and later
+extern bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid);
+
+
+//
+// Gets the UUID of the dyld shared cache in the current process.
+// Returns false if there is no dyld shared cache in use by the processes.
+//
+// Exists in Mac OS X 10.12 and later
+// Exists in iOS 10.0 and later
+extern bool _dyld_get_shared_cache_uuid(uuid_t uuid);
+
+
+
+//
+// When dyld must terminate a process because of a required dependent dylib
+// could not be loaded or a symbol is missing, dyld calls abort_with_reason()
+// using one of the following error codes.
+//
+#define DYLD_EXIT_REASON_DYLIB_MISSING 1
+#define DYLD_EXIT_REASON_DYLIB_WRONG_ARCH 2
+#define DYLD_EXIT_REASON_DYLIB_WRONG_VERSION 3
+#define DYLD_EXIT_REASON_SYMBOL_MISSING 4
+#define DYLD_EXIT_REASON_CODE_SIGNATURE 5
+#define DYLD_EXIT_REASON_FILE_SYSTEM_SANDBOX 6
+#define DYLD_EXIT_REASON_MALFORMED_MACHO 7
+#define DYLD_EXIT_REASON_OTHER 9
+
+//
+// When it has more information about the termination, dyld will use abort_with_payload().
+// The payload is a dyld_abort_payload structure. The fixed fields are offsets into the
+// payload for the corresponding string. If the offset is zero, that string is not available.
+//
+struct dyld_abort_payload {
+ uint32_t version; // first version is 1
+ uint32_t flags; // 0x00000001 means dyld terminated at launch, backtrace not useful
+ uint32_t targetDylibPathOffset; // offset in payload of path string to dylib that could not be loaded
+ uint32_t clientPathOffset; // offset in payload of path string to image requesting dylib
+ uint32_t symbolOffset; // offset in payload of symbol string that could not be found
+ // string data
+};
+typedef struct dyld_abort_payload dyld_abort_payload;
#if __cplusplus
}
--- /dev/null
+/*
+ * Copyright (c) 2016 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 _DYLD_PROCESS_INFO_
+#define _DYLD_PROCESS_INFO_
+
+#include <stdbool.h>
+#include <unistd.h>
+#include <mach/mach.h>
+#include <dispatch/dispatch.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Beginning in iOS 10.0 and Mac OS X 10.12, this is how lldb figures out mach-o binaries are in a process:
+//
+// When attaching to an existing process, lldb uses _dyld_process_info_create() to get the current list of images
+// in a process, then falls into the start up case.
+//
+// When starting a process, lldb starts the process suspended, finds the "_dyld_debugger_notification" symbol in
+// dyld, sets a break point on it, then resumes the process. Dyld will call _dyld_debugger_notification() with
+// a list of images that were just added or removed from the process. Dyld calls this function before running
+// any initializers in the image, so the debugger will have a chance to set break points in the image.
+//
+
+enum dyld_notify_mode { dyld_notify_adding=0, dyld_notify_removing=1, dyld_notify_remove_all=2 };
+
+void _dyld_debugger_notification(enum dyld_notify_mode, unsigned long count, uint64_t machHeaders[]);
+
+
+struct dyld_process_cache_info {
+ uuid_t cacheUUID; // UUID of cache used by process
+ uint64_t cacheBaseAddress; // load address of dyld shared cache
+ bool noCache; // process is running without a dyld cache
+ bool privateCache; // process is using a private copy of its dyld cache
+};
+typedef struct dyld_process_cache_info dyld_process_cache_info;
+
+enum {
+ dyld_process_state_not_started = 0x00, // process is suspended, dyld has not started running yet
+ dyld_process_state_dyld_initialized = 0x10, // dyld has initialzed itself
+ dyld_process_state_terminated_before_inits = 0x20, // process was terminated due missing library or symbol before it got to main()
+ dyld_process_state_libSystem_initialized = 0x30, // dyld has run libSystem's initializer
+ dyld_process_state_running_initializers = 0x40, // dyld is running other initializers
+ dyld_process_state_program_running = 0x50, // dyld has finished and jumped into main()
+ dyld_process_state_dyld_terminated = 0x60 // process was terminated by dyld post-main (e.g. bad lazying binding info)
+};
+
+struct dyld_process_state_info {
+ uint64_t timestamp; // mach_absolute_time of last time dyld change to image list
+ uint32_t imageCount; // number of images currently loaded into process
+ uint32_t initialImageCount; // number of images statically loaded into process (before any dlopen() calls)
+ uint8_t dyldState; // one of dyld_process_state_* values
+};
+typedef struct dyld_process_state_info dyld_process_state_info;
+
+
+typedef const struct dyld_process_info_base* dyld_process_info;
+
+//
+// Generate a dyld_process_info object for specified task.
+//
+// The timestamp parameter is an optimization to not spend the time to gather all the image information
+// if the process image list has not changed since the last call. If timestamp is zero, this function
+// always gathers the full process info. If timestamp is non-zero, this function will check if the target
+// task's image list has changed since that time. If is has not changed, the function returns NULL and
+// kern_return_t is KERN_SUCCESS. If it has changed, the function gathers the full image info.
+// The kernelError parameter can be NULL for clients that don't care why it failed.
+//
+extern dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kernelError);
+
+// retain/release dyld_process_info for specified task
+extern void _dyld_process_info_release(dyld_process_info info);
+extern void _dyld_process_info_retain(dyld_process_info info);
+
+// fill in struct with basic info about dyld in the process
+extern void _dyld_process_info_get_state(dyld_process_info info, dyld_process_state_info* stateInfo);
+
+// fill in struct with info about dyld cache in use by process
+extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo);
+
+// iterate all images in process
+extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path));
+
+// iterate all segments in an image
+extern void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName));
+
+
+
+
+typedef const struct dyld_process_info_notify_base* dyld_process_info_notify;
+
+//
+// Request notifications if image list changes in target process. Each time a load or unload happens in the target taks,
+// the notify block will be called in this process. If the process exits, the notifyExit block will be called.
+// If the notifications cannot be set up, this function will return NULL, and the reason in the kernError parameter.
+// The kernelError parameter can be NULL for clients that don't care why it failed.
+// If you want to stop receiving notifications, call _dyld_process_info_notify_release().
+//
+extern dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue,
+ void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path),
+ void (^notifyExit)(),
+ kern_return_t* kernelError);
+
+// stop notifications and invalid dyld_process_info_notify object
+extern void _dyld_process_info_notify_release(dyld_process_info_notify object);
+extern void _dyld_process_info_notify_retain(dyld_process_info_notify object);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DYLD_PROCESS_INFO_ */
// Precomputed perfect hash table of strings.
// Base class for precomputed selector table and class table.
-// Edit objc-sel-table.s and OPT_INITIALIZER if you change this structure.
+// Edit objc-sel-table.s if you change this structure.
struct objc_stringhash_t {
uint32_t capacity;
uint32_t occupied;
// Precomputed selector table.
-// Edit objc-sel-table.s and OPT_INITIALIZER if you change this structure.
+// Edit objc-sel-table.s if you change this structure.
struct objc_selopt_t : objc_stringhash_t {
const char *get(const char *key) const
{
};
// Precomputed class list.
-// Edit objc-sel-table.s and OPT_INITIALIZER if you change these structures.
+// Edit objc-sel-table.s if you change these structures.
struct objc_classheader_t {
objc_stringhash_offset_t clsOffset;
continue;
}
- uint32_t count = classes.count(c->first);
+ uint32_t count = (uint32_t)classes.count(c->first);
if (count == 1) {
// only one class with this name
// Precomputed image list.
-struct objc_headeropt_t;
+struct objc_headeropt_ro_t;
+
+// Precomputed image list.
+struct objc_headeropt_rw_t;
// Precomputed class list.
struct objc_clsopt_t;
// Edit objc-sel-table.s if you change this value.
-enum { VERSION = 13 };
+// lldb and Symbolication read these structures. Inform them of any changes.
+enum { VERSION = 15 };
+
+// Values for objc_opt_t::flags
+enum : uint32_t {
+ IsProduction = (1 << 0), // never set in development cache
+ NoMissingWeakSuperclasses = (1 << 1), // never set in development cache
+};
// Top-level optimization structure.
-// Edit objc-sel-table.s and OPT_INITIALIZER if you change this structure.
+// Edit objc-sel-table.s if you change this structure.
struct alignas(alignof(void*)) objc_opt_t {
uint32_t version;
+ uint32_t flags;
int32_t selopt_offset;
- int32_t headeropt_offset;
+ int32_t headeropt_ro_offset;
int32_t clsopt_offset;
int32_t protocolopt_offset;
+ int32_t headeropt_rw_offset;
- const objc_selopt_t* selopt() const {
+ const objc_selopt_t* selopt() const {
if (selopt_offset == 0) return NULL;
return (objc_selopt_t *)((uint8_t *)this + selopt_offset);
}
return (objc_selopt_t *)((uint8_t *)this + selopt_offset);
}
- struct objc_headeropt_t* headeropt() const {
- if (headeropt_offset == 0) return NULL;
- return (struct objc_headeropt_t *)((uint8_t *)this + headeropt_offset);
+ struct objc_headeropt_ro_t* headeropt_ro() const {
+ if (headeropt_ro_offset == 0) return NULL;
+ return (struct objc_headeropt_ro_t *)((uint8_t *)this + headeropt_ro_offset);
}
struct objc_clsopt_t* clsopt() const {
if (protocolopt_offset == 0) return NULL;
return (objc_protocolopt_t *)((uint8_t *)this + protocolopt_offset);
}
+
+ struct objc_headeropt_rw_t* headeropt_rw() const {
+ if (headeropt_rw_offset == 0) return NULL;
+ return (struct objc_headeropt_rw_t *)((uint8_t *)this + headeropt_rw_offset);
+ }
};
// sizeof(objc_opt_t) must be pointer-aligned
STATIC_ASSERT(sizeof(objc_opt_t) % sizeof(void*) == 0);
-// Initializer for empty opt of type uint32_t[].
-#define X8(x) x, x, x, x, x, x, x, x
-#define X64(x) X8(x), X8(x), X8(x), X8(x), X8(x), X8(x), X8(x), X8(x)
-#define X256(x) X64(x), X64(x), X64(x), X64(x)
-#define OPT_INITIALIZER { \
- /* objc_opt_t */ \
- objc_opt::VERSION, 16, 0, 0, \
- /* objc_selopt_t */ \
- 4, 4, 63, 3, 0, 0, 0,0, X256(0), 0, 0, 16, 16, 16, 16 \
- /* no objc_headeropt_t */ \
- /* no objc_clsopt_t */ \
- /* no objc_protocolopt_t */ \
-}
-
// List of offsets in libobjc that the shared cache optimization needs to use.
template <typename T>
for (i = 0; i < nkeys; i++) {
key *mykey = keys+i;
ub8 hash = lookup8(mykey->name_k, mykey->len_k, salt);
- mykey->a_k = (loga > 0) ? hash>>(UB8BITS-loga) : 0;
- mykey->b_k = (blen > 1) ? hash&(blen-1) : 0;
+ mykey->a_k = (loga > 0) ? (ub4)(hash >> (UB8BITS-loga)) : 0;
+ mykey->b_k = (blen > 1) ? (hash & (blen-1)) : 0;
}
}
mykey->len_k = (ub4)strlen(s->first);
}
*keys = buf;
- *nkeys = strings.size();
+ *nkeys = (ub4)strings.size();
}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 "mega-dylib-utils.h"
+#include "Logging.h"
+#include "MachOFileAbstraction.hpp"
+
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <assert.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "Trie.hpp"
+#include "dyld_cache_config.h"
+
+#if !NEW_CACHE_FILE_FORMAT
+ #include "CacheFileAbstraction.hpp"
+#endif
+
+#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
+ #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
+#endif
+
+
+namespace {
+
+template <typename P>
+class Adjustor {
+public:
+ Adjustor(void* cacheBuffer, macho_header<P>* mh, const std::vector<uint64_t>& segNewStartAddresses,
+ const std::vector<uint64_t>& segCacheFileOffset, const std::vector<uint64_t>& segCacheFileSizes);
+ void adjustImageForNewSegmentLocations(std::vector<void*>& pointersForASLR);
+
+private:
+ void adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASLR);
+ void adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide,
+ uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector<void*>& pointersForASLR, uint32_t*& lastMappedAddr32,
+ uint32_t& lastKind, uint64_t& lastToNewAddress);
+ void adjustDataPointers(std::vector<void*>& pointersForASLR);
+ void slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersForASLR);
+ void adjustSymbolTable();
+ void adjustExportsTrie(std::vector<uint8_t>& newTrieBytes);
+ void rebuildLinkEdit();
+ void adjustCode();
+ void adjustInstruction(uint8_t kind, uint64_t cacheOffset, uint64_t codeToDataDelta);
+ void rebuildLinkEditAndLoadCommands();
+ uint64_t slideForOrigAddress(uint64_t addr);
+
+ typedef typename P::uint_t pint_t;
+ typedef typename P::E E;
+
+ void* _cacheBuffer;
+ macho_header<P>* _mh;
+ const uint8_t* _linkeditBias = nullptr;
+ int64_t _linkeditAdjust = 0;
+ unsigned _linkeditSegIndex = 0;
+ bool _maskPointers = false;
+ bool _splitSegInfoV2 = false;
+ const char* _installName = nullptr;
+ macho_symtab_command<P>* _symTabCmd = nullptr;
+ macho_dysymtab_command<P>* _dynSymTabCmd = nullptr;
+ macho_dyld_info_command<P>* _dyldInfo = nullptr;
+ macho_linkedit_data_command<P>* _splitSegInfoCmd = nullptr;
+ macho_linkedit_data_command<P>* _functionStartsCmd = nullptr;
+ macho_linkedit_data_command<P>* _dataInCodeCmd = nullptr;
+ std::vector<uint64_t> _segOrigStartAddresses;
+ std::vector<uint64_t> _segNewStartAddresses;
+ std::vector<uint64_t> _segCacheOffsets;
+ std::vector<uint64_t> _segCacheSizes;
+ std::vector<uint64_t> _segSlides;
+ std::vector<macho_segment_command<P>*> _segCmds;
+};
+
+template <typename P>
+Adjustor<P>::Adjustor(void* cacheBuffer, macho_header<P>* mh, const std::vector<uint64_t>& segNewStartAddresses,
+ const std::vector<uint64_t>& segCacheFileOffsets, const std::vector<uint64_t>& segCacheFileSizes)
+ : _mh(mh), _cacheBuffer(cacheBuffer), _segNewStartAddresses(segNewStartAddresses),
+ _segCacheOffsets(segCacheFileOffsets), _segCacheSizes(segCacheFileSizes)
+{
+ macho_segment_command<P>* segCmd;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ const uint32_t cmd_count = mh->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ unsigned segIndex = 0;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd()) {
+ case LC_ID_DYLIB:
+ _installName = ((macho_dylib_command<P>*)cmd)->name();
+ break;
+ case LC_SYMTAB:
+ _symTabCmd = (macho_symtab_command<P>*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ _dyldInfo = (macho_dyld_info_command<P>*)cmd;
+ break;
+ case LC_SEGMENT_SPLIT_INFO:
+ _splitSegInfoCmd = (macho_linkedit_data_command<P>*)cmd;
+ break;
+ case LC_FUNCTION_STARTS:
+ _functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
+ break;
+ case LC_DATA_IN_CODE:
+ _dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
+ break;
+ case macho_segment_command<P>::CMD:
+ segCmd = (macho_segment_command<P>*)cmd;
+ _segCmds.push_back(segCmd);
+ _segOrigStartAddresses.push_back(segCmd->vmaddr());
+ _segSlides.push_back(_segNewStartAddresses[segIndex] - segCmd->vmaddr());
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+ _linkeditAdjust = segCacheFileOffsets[segIndex] - segCmd->fileoff();
+ _linkeditBias = (uint8_t*)cacheBuffer + _linkeditAdjust;
+ _linkeditSegIndex = segIndex;
+ }
+ ++segIndex;
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ _maskPointers = (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64);
+ if ( _splitSegInfoCmd != NULL ) {
+ const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
+ _splitSegInfoV2 = (*infoStart == DYLD_CACHE_ADJ_V2_FORMAT);
+ }
+}
+
+template <typename P>
+void Adjustor<P>::adjustImageForNewSegmentLocations(std::vector<void*>& pointersForASLR)
+{
+ if ( _splitSegInfoV2 ) {
+ adjustReferencesUsingInfoV2(pointersForASLR);
+ }
+ else {
+ adjustDataPointers(pointersForASLR);
+ adjustCode();
+ }
+ adjustSymbolTable();
+ rebuildLinkEditAndLoadCommands();
+}
+
+template <typename P>
+uint64_t Adjustor<P>::slideForOrigAddress(uint64_t addr)
+{
+ for (unsigned i=0; i < _segOrigStartAddresses.size(); ++i) {
+ if ( (_segOrigStartAddresses[i] <= addr) && (addr < (_segOrigStartAddresses[i]+_segCmds[i]->vmsize())) )
+ return _segSlides[i];
+ }
+ // On arm64, high nibble of pointers can have extra bits
+ if ( _maskPointers && (addr & 0xF000000000000000) ) {
+ return slideForOrigAddress(addr & 0x0FFFFFFFFFFFFFFF);
+ }
+ terminate("slide not known for dylib address 0x%llX in %s", addr, _installName);
+}
+
+template <typename P>
+void Adjustor<P>::rebuildLinkEditAndLoadCommands()
+{
+ // Exports trie is only data structure in LINKEDIT that might grow
+ std::vector<uint8_t> newTrieBytes;
+ adjustExportsTrie(newTrieBytes);
+
+ // Remove: code signature, rebase info, code-sign-dirs, split seg info
+ uint32_t bindOffset = 0;
+ uint32_t bindSize = _dyldInfo->bind_size();
+ uint32_t lazyBindOffset = bindOffset + bindSize;
+ uint32_t lazyBindSize = _dyldInfo->lazy_bind_size();
+ uint32_t weakBindOffset = lazyBindOffset + lazyBindSize;
+ uint32_t weakBindSize = _dyldInfo->weak_bind_size();
+ uint32_t exportOffset = weakBindOffset + weakBindSize;
+ uint32_t exportSize = (uint32_t)newTrieBytes.size();
+ uint32_t splitSegInfoOffset = exportOffset + exportSize;
+ uint32_t splitSegInfosSize = (_splitSegInfoCmd ? _splitSegInfoCmd->datasize() : 0);
+ uint32_t funcStartsOffset = splitSegInfoOffset + splitSegInfosSize;
+ uint32_t funcStartsSize = (_functionStartsCmd ? _functionStartsCmd->datasize() : 0);
+ uint32_t dataInCodeOffset = funcStartsOffset + funcStartsSize;
+ uint32_t dataInCodeSize = (_dataInCodeCmd ? _dataInCodeCmd->datasize() : 0);
+ uint32_t symbolTableOffset = dataInCodeOffset + dataInCodeSize;
+ uint32_t symbolTableSize = _symTabCmd->nsyms() * sizeof(macho_nlist<P>);
+ uint32_t indirectTableOffset = symbolTableOffset + symbolTableSize;
+ uint32_t indirectTableSize = _dynSymTabCmd->nindirectsyms() * sizeof(uint32_t);
+ uint32_t symbolStringsOffset = indirectTableOffset + indirectTableSize;
+ uint32_t symbolStringsSize = _symTabCmd->strsize();
+ uint32_t newLinkEditSize = symbolStringsOffset + symbolStringsSize;
+
+ size_t linkeditBufferSize = align(_segCmds[_linkeditSegIndex]->vmsize(), 12);
+ if ( linkeditBufferSize < newLinkEditSize ) {
+ terminate("LINKEDIT overflow in %s", _installName);
+ }
+
+ uint32_t linkeditStartOffset = (uint32_t)_segCacheOffsets[_linkeditSegIndex];
+ uint8_t* newLinkeditBufer = (uint8_t*)::calloc(linkeditBufferSize, 1);
+ if ( bindSize )
+ memcpy(&newLinkeditBufer[bindOffset], &_linkeditBias[_dyldInfo->bind_off()], bindSize);
+ if ( lazyBindSize )
+ memcpy(&newLinkeditBufer[lazyBindOffset], &_linkeditBias[_dyldInfo->lazy_bind_off()], lazyBindSize);
+ if ( weakBindSize )
+ memcpy(&newLinkeditBufer[weakBindOffset], &_linkeditBias[_dyldInfo->weak_bind_off()], weakBindSize);
+ if ( exportSize )
+ memcpy(&newLinkeditBufer[exportOffset], &newTrieBytes[0], exportSize);
+ if ( splitSegInfosSize )
+ memcpy(&newLinkeditBufer[splitSegInfoOffset], &_linkeditBias[_splitSegInfoCmd->dataoff()], splitSegInfosSize);
+ if ( funcStartsSize )
+ memcpy(&newLinkeditBufer[funcStartsOffset], &_linkeditBias[_functionStartsCmd->dataoff()], funcStartsSize);
+ if ( dataInCodeSize )
+ memcpy(&newLinkeditBufer[dataInCodeOffset], &_linkeditBias[_dataInCodeCmd->dataoff()], dataInCodeSize);
+ if ( symbolTableSize )
+ memcpy(&newLinkeditBufer[symbolTableOffset], &_linkeditBias[_symTabCmd->symoff()], symbolTableSize);
+ if ( indirectTableSize )
+ memcpy(&newLinkeditBufer[indirectTableOffset], &_linkeditBias[_dynSymTabCmd->indirectsymoff()], indirectTableSize);
+ if ( symbolStringsSize )
+ memcpy(&newLinkeditBufer[symbolStringsOffset], &_linkeditBias[_symTabCmd->stroff()], symbolStringsSize);
+
+ memcpy((uint8_t*)_cacheBuffer+linkeditStartOffset, newLinkeditBufer, newLinkEditSize);
+ ::bzero((uint8_t*)_cacheBuffer+linkeditStartOffset+newLinkEditSize, linkeditBufferSize-newLinkEditSize);
+ ::free(newLinkeditBufer);
+
+ // updates load commands and removed ones no longer needed
+ macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)_mh + sizeof(macho_header<P>));
+ uint32_t cmd_count = _mh->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ const unsigned origLoadCommandsSize = _mh->sizeofcmds();
+ unsigned bytesRemaining = origLoadCommandsSize;
+ unsigned removedCount = 0;
+ unsigned segIndex = 0;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ macho_symtab_command<P>* symTabCmd;
+ macho_dysymtab_command<P>* dynSymTabCmd;
+ macho_dyld_info_command<P>* dyldInfo;
+ macho_linkedit_data_command<P>* functionStartsCmd;
+ macho_linkedit_data_command<P>* dataInCodeCmd;
+ macho_linkedit_data_command<P>* splitSegInfoCmd;
+ macho_segment_command<P>* segCmd;
+ macho_routines_command<P>* routinesCmd;
+ macho_dylib_command<P>* dylibIDCmd;
+ uint32_t cmdSize = cmd->cmdsize();
+ int32_t segFileOffsetDelta;
+ bool remove = false;
+ switch ( cmd->cmd() ) {
+ case LC_ID_DYLIB:
+ dylibIDCmd = (macho_dylib_command<P>*)cmd;
+ dylibIDCmd->set_timestamp(2); // match what static linker sets in LC_LOAD_DYLIB
+ break;
+ case LC_SYMTAB:
+ symTabCmd = (macho_symtab_command<P>*)cmd;
+ symTabCmd->set_symoff(linkeditStartOffset+symbolTableOffset);
+ symTabCmd->set_stroff(linkeditStartOffset+symbolStringsOffset);
+ break;
+ case LC_DYSYMTAB:
+ dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
+ dynSymTabCmd->set_indirectsymoff(linkeditStartOffset+indirectTableOffset);
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ dyldInfo = (macho_dyld_info_command<P>*)cmd;
+ dyldInfo->set_rebase_off(0);
+ dyldInfo->set_rebase_size(0);
+ dyldInfo->set_bind_off(bindSize ? linkeditStartOffset+bindOffset : 0);
+ dyldInfo->set_bind_size(bindSize);
+ dyldInfo->set_weak_bind_off(weakBindSize ? linkeditStartOffset+weakBindOffset : 0);
+ dyldInfo->set_weak_bind_size(weakBindSize);
+ dyldInfo->set_lazy_bind_off(lazyBindSize ? linkeditStartOffset+lazyBindOffset : 0);
+ dyldInfo->set_lazy_bind_size(lazyBindSize);
+ dyldInfo->set_export_off(exportSize ? linkeditStartOffset+exportOffset : 0);
+ dyldInfo->set_export_size(exportSize);
+ break;
+ case LC_FUNCTION_STARTS:
+ functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
+ functionStartsCmd->set_dataoff(linkeditStartOffset+funcStartsOffset);
+ break;
+ case LC_DATA_IN_CODE:
+ dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
+ dataInCodeCmd->set_dataoff(linkeditStartOffset+dataInCodeOffset);
+ break;
+ case macho_routines_command<P>::CMD:
+ routinesCmd = (macho_routines_command<P>*)cmd;
+ routinesCmd->set_init_address(routinesCmd->init_address()+slideForOrigAddress(routinesCmd->init_address()));
+ break;
+ case macho_segment_command<P>::CMD:
+ segCmd = (macho_segment_command<P>*)cmd;
+ segFileOffsetDelta = (int32_t)(_segCacheOffsets[segIndex] - segCmd->fileoff());
+ segCmd->set_vmaddr(_segNewStartAddresses[segIndex]);
+ segCmd->set_vmsize(_segCacheSizes[segIndex]);
+ segCmd->set_fileoff(_segCacheOffsets[segIndex]);
+ segCmd->set_filesize(_segCacheSizes[segIndex]);
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
+ segCmd->set_vmsize(linkeditBufferSize);
+ if ( segCmd->nsects() > 0 ) {
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for (macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ sect->set_addr(sect->addr() + _segSlides[segIndex]);
+ if ( sect->offset() != 0 )
+ sect->set_offset(sect->offset() + segFileOffsetDelta);
+ }
+ }
+ ++segIndex;
+ break;
+ case LC_RPATH:
+ warning("dyld shared cache does not support LC_RPATH found in %s", _installName);
+ remove = true;
+ break;
+ case LC_SEGMENT_SPLIT_INFO:
+ splitSegInfoCmd = (macho_linkedit_data_command<P>*)cmd;
+ splitSegInfoCmd->set_dataoff(linkeditStartOffset+splitSegInfoOffset);
+ break;
+ case LC_CODE_SIGNATURE:
+ case LC_DYLIB_CODE_SIGN_DRS:
+ remove = true;
+ break;
+ default:
+ break;
+ }
+ macho_load_command<P>* nextCmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmdSize);
+ if ( remove ) {
+ ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
+ ++removedCount;
+ }
+ else {
+ bytesRemaining -= cmdSize;
+ cmd = nextCmd;
+ }
+ }
+ // zero out stuff removed
+ ::bzero((void*)cmd, bytesRemaining);
+ // update header
+ _mh->set_ncmds(cmd_count-removedCount);
+ _mh->set_sizeofcmds(origLoadCommandsSize-bytesRemaining);
+ _mh->set_flags(_mh->flags() | 0x80000000);
+}
+
+
+template <typename P>
+void Adjustor<P>::adjustSymbolTable()
+{
+ macho_nlist<P>* symbolTable = (macho_nlist<P>*)&_linkeditBias[_symTabCmd->symoff()];
+
+ // adjust global symbol table entries
+ macho_nlist<P>* lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()];
+ for (macho_nlist<P>* entry = &symbolTable[_dynSymTabCmd->iextdefsym()]; entry < lastExport; ++entry) {
+ if ( (entry->n_type() & N_TYPE) == N_SECT )
+ entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value()));
+ }
+
+ // adjust local symbol table entries
+ macho_nlist<P>* lastLocal = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()];
+ for (macho_nlist<P>* entry = &symbolTable[_dynSymTabCmd->ilocalsym()]; entry < lastLocal; ++entry) {
+ if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) )
+ entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value()));
+ }
+}
+
+template <typename P>
+void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersForASLR)
+{
+ pint_t* mappedAddrP = (pint_t*)((uint8_t*)_cacheBuffer + _segCacheOffsets[segIndex] + segOffset);
+ uint32_t* mappedAddr32 = (uint32_t*)mappedAddrP;
+ pint_t valueP;
+ uint32_t value32;
+ switch ( type ) {
+ case REBASE_TYPE_POINTER:
+ valueP = (pint_t)P::getP(*mappedAddrP);
+ P::setP(*mappedAddrP, valueP + slideForOrigAddress(valueP));
+ pointersForASLR.push_back(mappedAddrP);
+ break;
+
+ case REBASE_TYPE_TEXT_ABSOLUTE32:
+ value32 = P::E::get32(*mappedAddr32);
+ P::E::set32(*mappedAddr32, value32 + (uint32_t)slideForOrigAddress(value32));
+ break;
+
+ case REBASE_TYPE_TEXT_PCREL32:
+ // general text relocs not support
+ default:
+ terminate("unknown rebase type 0x%02X in %s", type, _installName);
+ }
+}
+
+
+static bool isThumbMovw(uint32_t instruction)
+{
+ return ( (instruction & 0x8000FBF0) == 0x0000F240 );
+}
+
+static bool isThumbMovt(uint32_t instruction)
+{
+ return ( (instruction & 0x8000FBF0) == 0x0000F2C0 );
+}
+
+static uint16_t getThumbWord(uint32_t instruction)
+{
+ uint32_t i = ((instruction & 0x00000400) >> 10);
+ uint32_t imm4 = (instruction & 0x0000000F);
+ uint32_t imm3 = ((instruction & 0x70000000) >> 28);
+ uint32_t imm8 = ((instruction & 0x00FF0000) >> 16);
+ return ((imm4 << 12) | (i << 11) | (imm3 << 8) | imm8);
+}
+
+static uint32_t setThumbWord(uint32_t instruction, uint16_t word) {
+ uint32_t imm4 = (word & 0xF000) >> 12;
+ uint32_t i = (word & 0x0800) >> 11;
+ uint32_t imm3 = (word & 0x0700) >> 8;
+ uint32_t imm8 = word & 0x00FF;
+ return (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16);
+}
+
+static bool isArmMovw(uint32_t instruction)
+{
+ return (instruction & 0x0FF00000) == 0x03000000;
+}
+
+static bool isArmMovt(uint32_t instruction)
+{
+ return (instruction & 0x0FF00000) == 0x03400000;
+}
+
+static uint16_t getArmWord(uint32_t instruction)
+{
+ uint32_t imm4 = ((instruction & 0x000F0000) >> 16);
+ uint32_t imm12 = (instruction & 0x00000FFF);
+ return (imm4 << 12) | imm12;
+}
+
+static uint32_t setArmWord(uint32_t instruction, uint16_t word) {
+ uint32_t imm4 = (word & 0xF000) >> 12;
+ uint32_t imm12 = word & 0x0FFF;
+ return (instruction & 0xFFF0F000) | (imm4 << 16) | imm12;
+}
+
+template <typename P>
+void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress,
+ int64_t adjust, int64_t targetSlide, uint64_t imageStartAddress, uint64_t imageEndAddress,
+ std::vector<void*>& pointersForASLR, uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress)
+{
+ uint64_t value64;
+ uint64_t* mappedAddr64;
+ uint32_t value32;
+ uint32_t* mappedAddr32;
+ uint32_t instruction;
+ int64_t offsetAdjust;
+ int64_t delta;
+ switch ( kind ) {
+ case DYLD_CACHE_ADJ_V2_DELTA_32:
+ mappedAddr32 = (uint32_t*)mappedAddr;
+ value32 = P::E::get32(*mappedAddr32);
+ delta = (int32_t)value32;
+ delta += adjust;
+ if ( (delta > 0x80000000) || (-delta > 0x80000000) )
+ terminate("DYLD_CACHE_ADJ_V2_DELTA_32 can't be adjust by 0x%016llX in %s", adjust, _installName);
+ P::E::set32(*mappedAddr32, (int32_t)delta);
+ break;
+ case DYLD_CACHE_ADJ_V2_POINTER_32:
+ mappedAddr32 = (uint32_t*)mappedAddr;
+ if ( toNewAddress != (E::get32(*mappedAddr32) + targetSlide) )
+ terminate("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX in %s", fromNewAddress, _installName);
+ E::set32(*mappedAddr32, (uint32_t)toNewAddress);
+ pointersForASLR.push_back(mappedAddr);
+ break;
+ case DYLD_CACHE_ADJ_V2_POINTER_64:
+ mappedAddr64 = (uint64_t*)mappedAddr;
+ if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) )
+ terminate("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _installName);
+ E::set64(*mappedAddr64, toNewAddress);
+ pointersForASLR.push_back(mappedAddr);
+ break;
+ case DYLD_CACHE_ADJ_V2_DELTA_64:
+ mappedAddr64 = (uint64_t*)mappedAddr;
+ value64 = P::E::get64(*mappedAddr64);
+ E::set64(*mappedAddr64, value64 + adjust);
+ break;
+ case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32:
+ if ( adjust == 0 )
+ break;
+ mappedAddr32 = (uint32_t*)mappedAddr;
+ value32 = P::E::get32(*mappedAddr32);
+ value64 = toNewAddress - imageStartAddress;
+ if ( value64 > imageEndAddress )
+ terminate("DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 can't be adjust to 0x%016llX in %s", toNewAddress, _installName);
+ P::E::set32(*mappedAddr32, (uint32_t)value64);
+ break;
+ break;
+ case DYLD_CACHE_ADJ_V2_ARM64_ADRP:
+ mappedAddr32 = (uint32_t*)mappedAddr;
+ instruction = P::E::get32(*mappedAddr32);
+ if ( (instruction & 0x9F000000) == 0x90000000 ) {
+ int64_t pageDistance = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF));
+ int64_t newPage21 = pageDistance >> 12;
+ if ( (newPage21 > 2097151) || (newPage21 < -2097151) )
+ terminate("DYLD_CACHE_ADJ_V2_ARM64_ADRP can't be adjusted that far in %s", _installName);
+ instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0);
+ P::E::set32(*mappedAddr32, instruction);
+ }
+ else {
+ // ADRP instructions are sometimes optimized to other instructions (e.g. ADR) after the split-seg-info is generated
+ }
+ break;
+ case DYLD_CACHE_ADJ_V2_ARM64_OFF12:
+ mappedAddr32 = (uint32_t*)mappedAddr;
+ instruction = P::E::get32(*mappedAddr32);
+ offsetAdjust = (adjust & 0xFFF);
+ if ( offsetAdjust == 0 )
+ break;
+ if ( (instruction & 0x3B000000) == 0x39000000 ) {
+ // LDR/STR imm12
+ if ( offsetAdjust != 0 ) {
+ uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10);
+ uint32_t newAddend = 0;
+ switch ( instruction & 0xC0000000 ) {
+ case 0x00000000:
+ if ( (instruction & 0x04800000) == 0x04800000 ) {
+ if ( offsetAdjust & 0xF )
+ terminate("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+ if ( encodedAddend*16 >= 4096 )
+ terminate("off12 scale=16 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
+ newAddend = (encodedAddend + offsetAdjust/16) % 256;
+ }
+ else {
+ // scale=1
+ newAddend = (encodedAddend + (int32_t)offsetAdjust) % 4096;
+ }
+ break;
+ case 0x40000000:
+ if ( offsetAdjust & 1 )
+ terminate("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+ if ( encodedAddend*2 >= 4096 )
+ terminate("off12 scale=2 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
+ newAddend = (encodedAddend + offsetAdjust/2) % 2048;
+ break;
+ case 0x80000000:
+ if ( offsetAdjust & 3 )
+ terminate("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+ if ( encodedAddend*4 >= 4096 )
+ terminate("off12 scale=4 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
+ newAddend = (encodedAddend + offsetAdjust/4) % 1024;
+ break;
+ case 0xC0000000:
+ if ( offsetAdjust & 7 )
+ terminate("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+ if ( encodedAddend*8 >= 4096 )
+ terminate("off12 scale=8 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
+ newAddend = (encodedAddend + offsetAdjust/8) % 512;
+ break;
+ }
+ uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
+ P::E::set32(*mappedAddr32, newInstruction);
+ }
+ }
+ else if ( (instruction & 0xFFC00000) == 0x91000000 ) {
+ // ADD imm12
+ if ( instruction & 0x00C00000 )
+ terminate("ADD off12 uses shift at mapped address=%p in %s", mappedAddr, _installName);
+ uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10);
+ uint32_t newAddend = (encodedAddend + offsetAdjust) & 0xFFF;
+ uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
+ P::E::set32(*mappedAddr32, newInstruction);
+ }
+ else if ( instruction != 0xD503201F ) {
+ // ignore imm12 instructions optimized into a NOP, but warn about others
+ terminate("unknown off12 instruction 0x%08X at 0x%0llX in %s", instruction, fromNewAddress, _installName);
+ }
+ break;
+ case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT:
+ mappedAddr32 = (uint32_t*)mappedAddr;
+ // to update a movw/movt pair we need to extract the 32-bit they will make,
+ // add the adjust and write back the new movw/movt pair.
+ if ( lastKind == kind ) {
+ if ( lastToNewAddress == toNewAddress ) {
+ uint32_t instruction1 = P::E::get32(*lastMappedAddr32);
+ uint32_t instruction2 = P::E::get32(*mappedAddr32);
+ if ( isThumbMovw(instruction1) && isThumbMovt(instruction2) ) {
+ uint16_t high = getThumbWord(instruction2);
+ uint16_t low = getThumbWord(instruction1);
+ uint32_t full = high << 16 | low;
+ full += adjust;
+ instruction1 = setThumbWord(instruction1, full & 0xFFFF);
+ instruction2 = setThumbWord(instruction2, full >> 16);
+ }
+ else if ( isThumbMovt(instruction1) && isThumbMovw(instruction2) ) {
+ uint16_t high = getThumbWord(instruction1);
+ uint16_t low = getThumbWord(instruction2);
+ uint32_t full = high << 16 | low;
+ full += adjust;
+ instruction2 = setThumbWord(instruction2, full & 0xFFFF);
+ instruction1 = setThumbWord(instruction1, full >> 16);
+ }
+ else {
+ terminate("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not paried in %s", _installName);
+ }
+ P::E::set32(*lastMappedAddr32, instruction1);
+ P::E::set32(*mappedAddr32, instruction2);
+ kind = 0;
+ }
+ else {
+ terminate("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses in %s", _installName);
+ }
+ }
+ break;
+ case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT:
+ mappedAddr32 = (uint32_t*)mappedAddr;
+ // to update a movw/movt pair we need to extract the 32-bit they will make,
+ // add the adjust and write back the new movw/movt pair.
+ if ( lastKind == kind ) {
+ if ( lastToNewAddress == toNewAddress ) {
+ uint32_t instruction1 = P::E::get32(*lastMappedAddr32);
+ uint32_t instruction2 = P::E::get32(*mappedAddr32);
+ if ( isArmMovw(instruction1) && isArmMovt(instruction2) ) {
+ uint16_t high = getArmWord(instruction2);
+ uint16_t low = getArmWord(instruction1);
+ uint32_t full = high << 16 | low;
+ full += adjust;
+ instruction1 = setArmWord(instruction1, full & 0xFFFF);
+ instruction2 = setArmWord(instruction2, full >> 16);
+ }
+ else if ( isArmMovt(instruction1) && isArmMovw(instruction2) ) {
+ uint16_t high = getArmWord(instruction1);
+ uint16_t low = getArmWord(instruction2);
+ uint32_t full = high << 16 | low;
+ full += adjust;
+ instruction2 = setArmWord(instruction2, full & 0xFFFF);
+ instruction1 = setArmWord(instruction1, full >> 16);
+ }
+ else {
+ terminate("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not paired in %s", _installName);
+ }
+ P::E::set32(*lastMappedAddr32, instruction1);
+ P::E::set32(*mappedAddr32, instruction2);
+ kind = 0;
+ }
+ else {
+ terminate("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses in %s", _installName);
+ }
+ }
+ break;
+ case DYLD_CACHE_ADJ_V2_ARM64_BR26:
+ case DYLD_CACHE_ADJ_V2_THUMB_BR22:
+ case DYLD_CACHE_ADJ_V2_ARM_BR24:
+ // nothing to do with calls to stubs
+ break;
+ default:
+ terminate("unknown split seg kind=%d in %s", kind, _installName);
+ }
+ lastKind = kind;
+ lastToNewAddress = toNewAddress;
+ lastMappedAddr32 = mappedAddr32;
+}
+
+template <typename P>
+void Adjustor<P>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASLR)
+{
+ static const bool log = false;
+
+ const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
+ const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];
+ if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT )
+ terminate("malformed split seg info in %s", _installName);
+
+ // build section arrays of slide and mapped address for each section
+ std::vector<uint64_t> sectionSlides;
+ std::vector<uint64_t> sectionNewAddress;
+ std::vector<uint8_t*> sectionMappedAddress;
+ sectionSlides.reserve(16);
+ sectionNewAddress.reserve(16);
+ sectionMappedAddress.reserve(16);
+ // section index 0 refers to mach_header
+ sectionMappedAddress.push_back((uint8_t*)_cacheBuffer + _segCacheOffsets[0]);
+ sectionSlides.push_back(_segSlides[0]);
+ sectionNewAddress.push_back(_segNewStartAddresses[0]);
+ // section 1 and later refer to real sections
+ unsigned sectionIndex = 0;
+ for (unsigned segmentIndex=0; segmentIndex < _segCmds.size(); ++segmentIndex) {
+ macho_segment_command<P>* segCmd = _segCmds[segmentIndex];
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ sectionMappedAddress.push_back((uint8_t*)_cacheBuffer + _segCacheOffsets[segmentIndex] + sect->addr() - segCmd->vmaddr());
+ sectionSlides.push_back(_segSlides[segmentIndex]);
+ sectionNewAddress.push_back(_segNewStartAddresses[segmentIndex] + sect->addr() - segCmd->vmaddr());
+ if (log) {
+ fprintf(stderr, " %s/%s, sectIndex=%d, mapped at=%p\n",
+ sect->segname(), sect->sectname(), sectionIndex, sectionMappedAddress.back());
+ }
+ ++sectionIndex;
+ }
+ }
+
+ // Whole :== <count> FromToSection+
+ // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
+ // ToOffset :== <to-sect-offset-delta> <count> FromOffset+
+ // FromOffset :== <kind> <count> <from-sect-offset-delta>
+ const uint8_t* p = infoStart;
+ uint64_t sectionCount = read_uleb128(p, infoEnd);
+ for (uint64_t i=0; i < sectionCount; ++i) {
+ uint32_t* lastMappedAddr32 = NULL;
+ uint32_t lastKind = 0;
+ uint64_t lastToNewAddress = 0;
+ uint64_t fromSectionIndex = read_uleb128(p, infoEnd);
+ uint64_t toSectionIndex = read_uleb128(p, infoEnd);
+ uint64_t toOffsetCount = read_uleb128(p, infoEnd);
+ uint64_t fromSectionSlide = sectionSlides[fromSectionIndex];
+ uint64_t fromSectionNewAddress = sectionNewAddress[fromSectionIndex];
+ uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex];
+ uint64_t toSectionSlide = sectionSlides[toSectionIndex];
+ uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex];
+ if (log) printf(" from sect=%lld (mapped=%p), to sect=%lld (new addr=0x%llX):\n", fromSectionIndex, fromSectionMappedAddress, toSectionIndex, toSectionNewAddress);
+ uint64_t toSectionOffset = 0;
+ for (uint64_t j=0; j < toOffsetCount; ++j) {
+ uint64_t toSectionDelta = read_uleb128(p, infoEnd);
+ uint64_t fromOffsetCount = read_uleb128(p, infoEnd);
+ toSectionOffset += toSectionDelta;
+ for (uint64_t k=0; k < fromOffsetCount; ++k) {
+ uint64_t kind = read_uleb128(p, infoEnd);
+ if ( kind > 12 )
+ terminate("bad kind value (%llu) in %s", kind, _installName);
+ uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
+ uint64_t fromSectionOffset = 0;
+ for (uint64_t l=0; l < fromSectDeltaCount; ++l) {
+ uint64_t delta = read_uleb128(p, infoEnd);
+ fromSectionOffset += delta;
+ int64_t deltaAdjust = toSectionSlide - fromSectionSlide;
+ //if (log) printf(" kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, deltaAdjust, toSectionSlide);
+ uint8_t* fromMappedAddr = fromSectionMappedAddress + fromSectionOffset;
+ uint64_t toNewAddress = toSectionNewAddress + toSectionOffset;
+ uint64_t fromNewAddress = fromSectionNewAddress + fromSectionOffset;
+ uint64_t imageStartAddress = sectionNewAddress.front();
+ uint64_t imageEndAddress = sectionNewAddress.back();
+ if ( toSectionIndex != 255 )
+ adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide, imageStartAddress, imageEndAddress, pointersForASLR, lastMappedAddr32, lastKind, lastToNewAddress);
+ }
+ }
+ }
+ }
+
+}
+
+template <typename P>
+void Adjustor<P>::adjustDataPointers(std::vector<void*>& pointersForASLR)
+{
+ const uint8_t* p = &_linkeditBias[_dyldInfo->rebase_off()];
+ const uint8_t* end = &p[_dyldInfo->rebase_size()];
+
+ uint8_t type = 0;
+ int segIndex = 0;
+ uint64_t segOffset = 0;
+ uint64_t count;
+ uint64_t skip;
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
+ uint8_t opcode = *p & REBASE_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case REBASE_OPCODE_DONE:
+ done = true;
+ break;
+ case REBASE_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segOffset = read_uleb128(p, end);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
+ segOffset += immediate*sizeof(pint_t);
+ break;
+ case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
+ for (int i=0; i < immediate; ++i) {
+ slidePointer(segIndex, segOffset, type, pointersForASLR);
+ segOffset += sizeof(pint_t);
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+ count = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ slidePointer(segIndex, segOffset, type, pointersForASLR);
+ segOffset += sizeof(pint_t);
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+ slidePointer(segIndex, segOffset, type, pointersForASLR);
+ segOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ slidePointer(segIndex, segOffset, type, pointersForASLR);
+ segOffset += skip + sizeof(pint_t);
+ }
+ break;
+ default:
+ terminate("unknown rebase opcode 0x%02X in %s", opcode, _installName);
+ }
+ }
+}
+
+
+template <typename P>
+void Adjustor<P>::adjustInstruction(uint8_t kind, uint64_t cacheOffset, uint64_t codeToDataDelta)
+{
+ uint8_t* fixupLoc = (uint8_t*)_cacheBuffer + cacheOffset;
+ uint32_t* fixupLoc32 = (uint32_t*)fixupLoc;
+ uint64_t* fixupLoc64 = (uint64_t*)fixupLoc;
+ uint32_t instruction;
+ uint32_t value32;
+ uint64_t value64;
+
+ switch (kind) {
+ case 1: // 32-bit pointer (including x86_64 RIP-rel)
+ value32 = P::E::get32(*fixupLoc32);
+ value32 += codeToDataDelta;
+ P::E::set32(*fixupLoc32, value32);
+ break;
+ case 2: // 64-bit pointer
+ value64 = P::E::get64(*fixupLoc64);
+ value64 += codeToDataDelta;
+ P::E::set64(*fixupLoc64, value64);
+ break;
+ case 4: // only used for i386, a reference to something in the IMPORT segment
+ break;
+ case 5: // used by thumb2 movw
+ instruction = P::E::get32(*fixupLoc32);
+ // slide is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting
+ value32 = (instruction & 0x0000000F) + ((uint32_t)codeToDataDelta >> 12);
+ instruction = (instruction & 0xFFFFFFF0) | (value32 & 0x0000000F);
+ P::E::set32(*fixupLoc32, instruction);
+ break;
+ case 6: // used by ARM movw
+ instruction = P::E::get32(*fixupLoc32);
+ // slide is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting
+ value32 = ((instruction & 0x000F0000) >> 16) + ((uint32_t)codeToDataDelta >> 12);
+ instruction = (instruction & 0xFFF0FFFF) | ((value32 <<16) & 0x000F0000);
+ P::E::set32(*fixupLoc32, instruction);
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ // used by thumb2 movt (low nibble of kind is high 4-bits of paired movw)
+ {
+ instruction = P::E::get32(*fixupLoc32);
+ assert((instruction & 0x8000FBF0) == 0x0000F2C0);
+ // extract 16-bit value from instruction
+ uint32_t i = ((instruction & 0x00000400) >> 10);
+ uint32_t imm4 = (instruction & 0x0000000F);
+ uint32_t imm3 = ((instruction & 0x70000000) >> 28);
+ uint32_t imm8 = ((instruction & 0x00FF0000) >> 16);
+ uint32_t imm16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
+ // combine with codeToDataDelta and kind nibble
+ uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12);
+ uint32_t newTargetValue = targetValue + (uint32_t)codeToDataDelta;
+ // construct new bits slices
+ uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28;
+ uint32_t i_ = (newTargetValue & 0x08000000) >> 27;
+ uint32_t imm3_ = (newTargetValue & 0x07000000) >> 24;
+ uint32_t imm8_ = (newTargetValue & 0x00FF0000) >> 16;
+ // update instruction to match codeToDataDelta
+ uint32_t newInstruction = (instruction & 0x8F00FBF0) | imm4_ | (i_ << 10) | (imm3_ << 28) | (imm8_ << 16);
+ P::E::set32(*fixupLoc32, newInstruction);
+ }
+ break;
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ // used by arm movt (low nibble of kind is high 4-bits of paired movw)
+ {
+ instruction = P::E::get32(*fixupLoc32);
+ // extract 16-bit value from instruction
+ uint32_t imm4 = ((instruction & 0x000F0000) >> 16);
+ uint32_t imm12 = (instruction & 0x00000FFF);
+ uint32_t imm16 = (imm4 << 12) | imm12;
+ // combine with codeToDataDelta and kind nibble
+ uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12);
+ uint32_t newTargetValue = targetValue + (uint32_t)codeToDataDelta;
+ // construct new bits slices
+ uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28;
+ uint32_t imm12_ = (newTargetValue & 0x0FFF0000) >> 16;
+ // update instruction to match codeToDataDelta
+ uint32_t newInstruction = (instruction & 0xFFF0F000) | (imm4_ << 16) | imm12_;
+ P::E::set32(*fixupLoc32, newInstruction);
+ }
+ break;
+ case 3: // used for arm64 ADRP
+ instruction = P::E::get32(*fixupLoc32);
+ if ( (instruction & 0x9F000000) == 0x90000000 ) {
+ // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting
+ value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9);
+ value64 += codeToDataDelta;
+ instruction = (instruction & 0x9F00001F) | ((value64 << 17) & 0x60000000) | ((value64 >> 9) & 0x00FFFFE0);
+ P::E::set32(*fixupLoc32, instruction);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+template <typename P>
+void Adjustor<P>::adjustCode()
+{
+ // find compressed info on how code needs to be updated
+ const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
+ const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];;
+
+ // This encoding only works if all data segments slide by the same amount
+ uint64_t codeToDataDelta = _segSlides[1] - _segSlides[0];
+
+ // compressed data is: [ <kind> [uleb128-delta]+ <0> ] + <0>
+ for (const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) {
+ uint8_t kind = *p++;
+ uint64_t cacheOffset = _segCacheOffsets[0];
+ while (uint64_t delta = read_uleb128(p, infoEnd)) {
+ cacheOffset += delta;
+ adjustInstruction(kind, cacheOffset, codeToDataDelta);
+ }
+ }
+}
+
+
+template <typename P>
+void Adjustor<P>::adjustExportsTrie(std::vector<uint8_t>& newTrieBytes)
+{
+ // if no export info, nothing to adjust
+ if ( _dyldInfo->export_size() == 0 )
+ return;
+
+ // since export info addresses are offsets from mach_header, everything in __TEXT is fine
+ // only __DATA addresses need to be updated
+ const uint8_t* start = &_linkeditBias[_dyldInfo->export_off()];
+ const uint8_t* end = &start[_dyldInfo->export_size()];
+ std::vector<ExportInfoTrie::Entry> originalExports;
+ if ( !ExportInfoTrie::parseTrie(start, end, originalExports) ) {
+ terminate("malformed exports trie in %s", _installName);
+ }
+
+ std::vector<ExportInfoTrie::Entry> newExports;
+ newExports.reserve(originalExports.size());
+ uint64_t baseAddress = _segOrigStartAddresses[0];
+ uint64_t baseAddressSlide = slideForOrigAddress(baseAddress);
+ for (auto& entry: originalExports) {
+ // remove symbols used by the static linker only
+ if ( (strncmp(entry.name.c_str(), "$ld$", 4) == 0)
+ || (strncmp(entry.name.c_str(), ".objc_class_name",16) == 0)
+ || (strncmp(entry.name.c_str(), ".objc_category_name",19) == 0) ) {
+ continue;
+ }
+ // adjust symbols in slid segments
+ if ( (entry.info.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE )
+ entry.info.address += (slideForOrigAddress(entry.info.address + baseAddress) - baseAddressSlide);
+ newExports.push_back(entry);
+ }
+
+ // rebuild export trie
+ newTrieBytes.reserve(_dyldInfo->export_size());
+
+ ExportInfoTrie(newExports).emit(newTrieBytes);
+ // align
+ while ( (newTrieBytes.size() % sizeof(pint_t)) != 0 )
+ newTrieBytes.push_back(0);
+}
+
+
+} // anonymous namespace
+
+
+void SharedCache::adjustImageForNewSegmentLocations(const std::vector<uint64_t>& segNewStartAddresses,
+ const std::vector<uint64_t>& segCacheFileOffsets,
+ const std::vector<uint64_t>& segCacheFileSizes,
+ std::vector<void*>& pointersForASLR)
+{
+ void* mh = (uint8_t*)_buffer.get() + segCacheFileOffsets[0];
+ switch ( _arch.arch ) {
+ case CPU_TYPE_ARM:
+ case CPU_TYPE_I386:
+ {
+ if ( LittleEndian::get32(*(uint32_t*)mh) != MH_MAGIC )
+ return;
+ Adjustor<Pointer32<LittleEndian>> adjustor32(_buffer.get(), (macho_header<Pointer32<LittleEndian>>*)mh, segNewStartAddresses, segCacheFileOffsets, segCacheFileSizes);
+ adjustor32.adjustImageForNewSegmentLocations(pointersForASLR);
+ }
+ break;
+ case CPU_TYPE_X86_64:
+ case CPU_TYPE_ARM64:
+ {
+ if ( LittleEndian::get32(*(uint32_t*)mh) != MH_MAGIC_64 )
+ return;
+ Adjustor<Pointer64<LittleEndian>> adjustor64(_buffer.get(), (macho_header<Pointer64<LittleEndian>>*)mh, segNewStartAddresses, segCacheFileOffsets, segCacheFileSizes);
+ adjustor64.adjustImageForNewSegmentLocations(pointersForASLR);
+ }
+ break;
+ default:
+ terminate("unsupported arch 0x%08X", _arch.arch);
+ }
+}
+
+
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 "mega-dylib-utils.h"
+#include "MachOFileAbstraction.hpp"
+#include "Trie.hpp"
+#include "Logging.h"
+
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <assert.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "dyld_cache_config.h"
+
+#if !NEW_CACHE_FILE_FORMAT
+ #include "CacheFileAbstraction.hpp"
+#endif
+
+#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
+ #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
+#endif
+
+
+namespace {
+
+template <typename P>
+class BindInfo {
+public:
+ BindInfo(void* cacheBuffer, macho_header<P>* mh);
+
+ void setReExports(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo);
+ void setDependentDylibs(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo);
+ void bind(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
+
+ static void bindAllImagesInCache(void* cacheBuffer, const std::unordered_map<std::string, void*>& dylibPathToMachHeader, std::vector<void*>& pointersForASLR);
+
+ void addExportsToGlobalMap(std::unordered_map<std::string, BindInfo<P>*>& reverseMap);
+
+private:
+ typedef typename P::uint_t pint_t;
+ typedef typename P::E E;
+
+ struct SymbolInfo {
+ SymbolInfo() { }
+ pint_t address = 0;
+ bool isResolver = false;
+ bool isAbsolute = false;
+ bool isSymbolReExport = false;
+ bool isThreadLocal = false;
+ int reExportDylibIndex = 0;
+ std::string reExportName;
+ };
+
+ void bindImmediates(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
+ void bindLazyPointers(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
+
+ void bindLocation(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal,
+ int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport,
+ const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
+
+ bool findExportedSymbolAddress(const char* symbolName, const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo,
+ pint_t* address, BindInfo<P>** foundIn, bool* isResolverSymbol, bool* isAbsolute);
+ pint_t findBlessedLazyPointerFor(const std::string& resolverSymbolName);
+ void switchStubToUseSharedLazyPointer(const std::string& resolverSymbolName, pint_t lpVMAddr);
+ void switchArmStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
+ void switchArm64StubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
+
+ typedef std::unordered_map<std::string, std::unordered_set<BindInfo<P>*>> ResolverClientsMap;
+ typedef std::unordered_map<std::string, pint_t> ResolverToBlessedLazyPointerMap;
+
+ void* _cacheBuffer;
+ macho_header<P>* _mh;
+ const uint8_t* _linkeditBias;
+ const char* _installName;
+ const macho_symtab_command<P>* _symTabCmd;
+ const macho_dysymtab_command<P>* _dynSymTabCmd;
+ const macho_dyld_info_command<P>* _dyldInfo;
+ std::vector<std::string> _dependentPaths;
+ std::vector<uint64_t> _segSizes;
+ std::vector<uint64_t> _segCacheOffsets;
+ std::vector<const macho_segment_command<P>*>_segCmds;
+ std::unordered_map<std::string, SymbolInfo> _exports;
+ std::vector<std::string> _reExportedDylibNames;
+ std::vector<BindInfo<P>*> _reExportedDylibs;
+ std::vector<BindInfo<P>*> _dependentDylibs;
+ pint_t _baseAddress;
+ ResolverClientsMap _resolverClients;
+ ResolverToBlessedLazyPointerMap _resolverBlessedMap;
+};
+
+
+template <typename P>
+BindInfo<P>::BindInfo(void* cacheBuffer, macho_header<P>* mh)
+ : _cacheBuffer(cacheBuffer), _mh(mh), _linkeditBias((uint8_t*)cacheBuffer), _symTabCmd(nullptr), _dynSymTabCmd(nullptr), _dyldInfo(nullptr), _baseAddress(0)
+{
+ macho_segment_command<P>* segCmd;
+ macho_dylib_command<P>* dylibCmd;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ const uint32_t cmd_count = mh->ncmds();
+ unsigned segIndex = 0;
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd()) {
+ case LC_ID_DYLIB:
+ dylibCmd = (macho_dylib_command<P>*)cmd;
+ _installName = dylibCmd->name();
+ break;
+ case LC_SYMTAB:
+ _symTabCmd = (macho_symtab_command<P>*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ _dyldInfo = (macho_dyld_info_command<P>*)cmd;
+ break;
+ case LC_REEXPORT_DYLIB:
+ dylibCmd = (macho_dylib_command<P>*)cmd;
+ _dependentPaths.push_back(dylibCmd->name());
+ _reExportedDylibNames.push_back(dylibCmd->name());
+ break;
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
+ dylibCmd = (macho_dylib_command<P>*)cmd;
+ _dependentPaths.push_back(dylibCmd->name());
+ break;
+ case macho_segment_command<P>::CMD:
+ segCmd = (macho_segment_command<P>*)cmd;
+ _segCmds.push_back(segCmd);
+ _segSizes.push_back(segCmd->vmsize());
+ _segCacheOffsets.push_back(segCmd->fileoff());
+ if ( segIndex == 0 )
+ _baseAddress = (pint_t)segCmd->vmaddr();
+ ++segIndex;
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+
+ // if no export info, no _exports map to build
+ if ( _dyldInfo->export_size() == 0 )
+ return;
+
+ std::vector<ExportInfoTrie::Entry> exports;
+ const uint8_t* exportsStart = &_linkeditBias[_dyldInfo->export_off()];
+ const uint8_t* exportsEnd = &exportsStart[_dyldInfo->export_size()];
+ if ( !ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports) ) {
+ terminate("malformed exports trie in %s", _installName);
+ }
+
+ for(const ExportInfoTrie::Entry& entry : exports) {
+ _exports[entry.name].address = (pint_t)entry.info.address + _baseAddress;
+ switch ( entry.info.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
+ case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
+ if ( (entry.info.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
+ _exports[entry.name].isResolver = true;
+ }
+ if ( entry.info.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ SymbolInfo& info = _exports[entry.name];
+ info.isSymbolReExport = true;
+ info.reExportDylibIndex = (int)entry.info.other;
+ if ( !entry.info.importName.empty())
+ info.reExportName = entry.info.importName;
+ }
+ break;
+ case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
+ _exports[entry.name].isThreadLocal = true;
+ break;
+ case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
+ _exports[entry.name].isAbsolute = true;
+ _exports[entry.name].address = (pint_t)entry.info.address;
+ break;
+ default:
+ terminate("non-regular symbol binding not supported for %s in %s", entry.name.c_str(), _installName);
+ break;
+ }
+ }
+
+}
+
+template <typename P>
+void BindInfo<P>::bind(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
+{
+ bindImmediates(dylibPathToBindInfo, pointersForASLR);
+ bindLazyPointers(dylibPathToBindInfo, pointersForASLR);
+ // weak bind info is processed at launch time
+}
+
+
+template <typename P>
+void BindInfo<P>::setReExports(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo)
+{
+ for (const std::string& depName : _reExportedDylibNames) {
+ auto pos = dylibPathToBindInfo.find(depName);
+ if ( pos == dylibPathToBindInfo.end() ) {
+ terminate("can't find re-exported dylib '%s' needed by '%s'", depName.c_str(), _installName);
+ }
+ _reExportedDylibs.push_back(pos->second);
+ }
+}
+
+template <typename P>
+void BindInfo<P>::setDependentDylibs(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo)
+{
+ for (const std::string& depName : _dependentPaths) {
+ auto pos = dylibPathToBindInfo.find(depName);
+ if ( pos == dylibPathToBindInfo.end() ) {
+ terminate("can't find dependent dylib '%s' needed by '%s'", depName.c_str(), _installName);
+ }
+ _dependentDylibs.push_back(pos->second);
+ }
+}
+
+
+template <typename P>
+void BindInfo<P>::bindImmediates(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
+{
+ const uint8_t* p = &_linkeditBias[_dyldInfo->bind_off()];
+ const uint8_t* end = &p[_dyldInfo->bind_size()];
+
+ uint8_t type = 0;
+ uint64_t segmentOffset = 0;
+ uint8_t segmentIndex = 0;
+ const char* symbolName = NULL;
+ int libraryOrdinal = 0;
+ int64_t addend = 0;
+ uint64_t count;
+ uint64_t skip;
+ bool weakImport = false;
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = (int)read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segmentIndex = immediate;
+ segmentOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segmentOffset += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
+ segmentOffset += sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
+ segmentOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
+ segmentOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
+ segmentOffset += skip + sizeof(pint_t);
+ }
+ break;
+ default:
+ terminate("bad bind opcode 0x%02X in %s", *p, _installName);
+ }
+ }
+
+}
+
+template <typename P>
+void BindInfo<P>::bindLazyPointers(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
+{
+ const uint8_t* p = &_linkeditBias[_dyldInfo->lazy_bind_off()];
+ const uint8_t* end = &p[_dyldInfo->lazy_bind_size()];
+
+ uint8_t type = BIND_TYPE_POINTER;
+ uint64_t segmentOffset = 0;
+ uint8_t segmentIndex = 0;
+ const char* symbolName = NULL;
+ int libraryOrdinal = 0;
+ int64_t addend = 0;
+ bool weakImport = false;
+ while ( p < end ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ // this opcode marks the end of each lazy pointer binding
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = (int)read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segmentIndex = immediate;
+ segmentOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, true, weakImport, dylibPathToBindInfo, pointersForASLR);
+ segmentOffset += sizeof(pint_t);
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ default:
+ terminate("bad lazy bind opcode 0x%02X in %s", opcode, _installName);
+ }
+ }
+
+}
+
+
+template <typename P>
+void BindInfo<P>::bindLocation(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal,
+ int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport,
+ const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
+{
+ //printf("bindLocation: seg=%d, segOffset=0x%08llX, type=%d, lib=%d, addend=%lld, symbol=%s\n", segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName);
+ if ( segmentIndex > _segSizes.size() )
+ terminate("bad segment index in bind info in %s", _installName);
+
+ if ( segmentOffset > _segSizes[segmentIndex] )
+ terminate("bad segment offset in bind info in %s", _installName);
+
+ BindInfo<P>* targetBinder = nullptr;
+ std::string depName;
+ switch ( libraryOrdinal ) {
+ case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
+ terminate("dynamic lookup linkage not allowed in dyld shared cache in %s", _installName);
+ break;
+
+ case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
+ terminate("linkage to main executable not allowed in dyld shared cache in %s", _installName);
+ break;
+
+ case BIND_SPECIAL_DYLIB_SELF:
+ targetBinder = this;
+ break;
+
+ default:
+ if ( libraryOrdinal < 0 )
+ terminate("bad mach-o binary, special library ordinal not allowd in dyld shared cache in %s", _installName);
+ if ( (unsigned)libraryOrdinal > _dependentPaths.size() )
+ terminate("bad mach-o binary, library ordinal too big in %s", _installName);
+ depName = _dependentPaths[libraryOrdinal-1];
+ auto pos = dylibPathToBindInfo.find(depName);
+ if ( pos != dylibPathToBindInfo.end() )
+ targetBinder = pos->second;
+ break;
+ }
+
+ pint_t targetSymbolAddress;
+ bool isResolverSymbol = false;
+ bool isAbsolute = false;
+ BindInfo<P>* foundIn;
+ if ( weakImport && (targetBinder == nullptr) ) {
+ targetSymbolAddress = 0;
+ foundIn = nullptr;
+ }
+ else {
+ if (targetBinder == nullptr)
+ terminate("could not bind symbol '%s' used in '%s' because installname '%s' not found", symbolName, _installName, depName.c_str());
+ if ( ! targetBinder->findExportedSymbolAddress(symbolName, dylibPathToBindInfo, &targetSymbolAddress, &foundIn, &isResolverSymbol, &isAbsolute) )
+ terminate("could not bind symbol '%s' used in: %s expected in: %s", symbolName, _installName, targetBinder->_installName);
+ }
+
+ //if ( isResolverSymbol )
+ // fprintf(stderr, "found resolver based symbol '%s' in %s\n", symbolName, targetBinder->_installName);
+ // don't bind lazy pointers to resolvers in shared cache
+ if ( lazyPointer && isResolverSymbol ) {
+ // instead find common lazy pointer that can be re-used by all clients
+ pint_t lpVMAddr = targetBinder->findBlessedLazyPointerFor(symbolName);
+ // switch stub to use shared lazy pointer to reduce dirty pages
+ this->switchStubToUseSharedLazyPointer(symbolName, lpVMAddr);
+ return;
+ }
+
+
+ // do actual update
+ uint8_t* mappedAddr = (uint8_t*)_cacheBuffer + _segCacheOffsets[segmentIndex] + segmentOffset;
+ pint_t* mappedAddrP = (pint_t*)mappedAddr;
+ pint_t newValue = (pint_t)(targetSymbolAddress + addend);
+ switch ( type ) {
+ case BIND_TYPE_POINTER:
+ // only write new value if it will change it
+ // this reduces pages dirtied
+ if ( P::getP(*mappedAddrP) != newValue )
+ P::setP(*mappedAddrP, newValue);
+ break;
+
+ case BIND_TYPE_TEXT_ABSOLUTE32:
+ case BIND_TYPE_TEXT_PCREL32:
+ terminate("text relocs not supported for shared cache binding in %s", _installName);
+ break;
+
+ default:
+ terminate("bad bind type (%d) in %s", type, _installName);
+ }
+ if ( !isAbsolute )
+ pointersForASLR.push_back(mappedAddr);
+}
+
+
+template <typename P>
+bool BindInfo<P>::findExportedSymbolAddress(const char* symbolName, const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo,
+ pint_t* address, BindInfo<P>** foundIn, bool* isResolverSymbol, bool* isAbsolute)
+{
+ auto pos = _exports.find(symbolName);
+ if ( pos != _exports.end() ) {
+ if ( pos->second.isSymbolReExport ) {
+ const char* importName = symbolName;
+ if ( !pos->second.reExportName.empty() )
+ importName = pos->second.reExportName.c_str();
+ std::string& depPath = _dependentPaths[pos->second.reExportDylibIndex-1];
+ auto pos2 = dylibPathToBindInfo.find(depPath);
+ if ( pos2 != dylibPathToBindInfo.end() ) {
+ BindInfo<P>* reExportFrom = pos2->second;
+ return reExportFrom->findExportedSymbolAddress(importName, dylibPathToBindInfo, address, foundIn, isResolverSymbol, isAbsolute);
+ }
+ else {
+ verboseLog("findExportedSymbolAddress(%s) => ???\n", symbolName);
+ }
+ }
+ *address = pos->second.address;
+ *foundIn = this;
+ *isResolverSymbol = pos->second.isResolver;
+ *isAbsolute = pos->second.isAbsolute;
+ //verboseLog("findExportedSymbolAddress(%s) => 0x0%llX\n", symbolName, (uint64_t)*address);
+ return true;
+ }
+
+ for (BindInfo<P>* dep : _reExportedDylibs) {
+ if ( dep->findExportedSymbolAddress(symbolName, dylibPathToBindInfo, address, foundIn, isResolverSymbol, isAbsolute) )
+ return true;
+ }
+ return false;
+}
+
+template <typename P>
+void BindInfo<P>::addExportsToGlobalMap(std::unordered_map<std::string, BindInfo<P>*>& reverseMap)
+{
+ for (const auto& expEntry : _exports) {
+ const std::string& symName = expEntry.first;
+ auto pos = reverseMap.find(symName);
+ if ( pos == reverseMap.end() ) {
+ reverseMap[symName] = this;
+ }
+ else {
+ BindInfo<P>* other = pos->second;
+ if ( expEntry.second.isSymbolReExport )
+ continue;
+ if ( other->_exports[symName].isSymbolReExport )
+ continue;
+ //warning("symbol '%s' exported from %s and %s\n", symName.c_str(), this->_installName, other->_installName);
+ }
+ }
+}
+
+template <typename P>
+typename P::uint_t BindInfo<P>::findBlessedLazyPointerFor(const std::string& resolverSymbolName)
+{
+ static const bool log = false;
+
+ // check if this has already been looked up
+ auto pos1 = _resolverBlessedMap.find(resolverSymbolName);
+ if ( pos1 != _resolverBlessedMap.end() ) {
+ return pos1->second;
+ }
+
+ // if this symbol is re-exported from another dylib, look there
+ bool thisDylibImplementsResolver = false;
+ auto pos = _exports.find(resolverSymbolName);
+ if ( pos != _exports.end() ) {
+ const SymbolInfo& info = pos->second;
+ if ( info.isSymbolReExport ) {
+ std::string reImportName = resolverSymbolName;
+ if ( !info.reExportName.empty() )
+ reImportName = info.reExportName;
+ if ( info.reExportDylibIndex > _dependentDylibs.size() ) {
+ warning("dylib index for re-exported symbol %s too large (%d) in %s", resolverSymbolName.c_str(), info.reExportDylibIndex, _installName);
+ }
+ else {
+ BindInfo<P>* reExportedFrom = _dependentDylibs[info.reExportDylibIndex-1];
+ if ( log ) verboseLog( "following re-export of %s in %s, to %s in %s", resolverSymbolName.c_str(), _installName, reImportName.c_str(), reExportedFrom->_installName);
+ pint_t lp = reExportedFrom->findBlessedLazyPointerFor(reImportName);
+ if ( lp != 0 ) {
+ _resolverBlessedMap[resolverSymbolName] = lp;
+ return lp;
+ }
+ }
+ }
+ if ( info.isResolver )
+ thisDylibImplementsResolver = true;
+ }
+
+ // lookup in lazy pointer section
+ if ( thisDylibImplementsResolver ) {
+ const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
+ const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
+ const char* symbolStringPool = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
+
+ for (const macho_segment_command<P>* seg : _segCmds) {
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)seg + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
+ for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ uint8_t sectionType = sect->flags() & SECTION_TYPE;
+ if ( sectionType == S_LAZY_SYMBOL_POINTERS) {
+ uint32_t elementCount = (uint32_t)(sect->size() / sizeof(pint_t));
+ const uint32_t indirectTableOffset = sect->reserved1();
+ pint_t vmlocation = (pint_t)sect->addr();
+ for (uint32_t j=0; j < elementCount; ++j, vmlocation += sizeof(pint_t)) {
+ uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]);
+ switch ( symbolIndex ) {
+ case INDIRECT_SYMBOL_ABS:
+ case INDIRECT_SYMBOL_LOCAL:
+ break;
+ default:
+ const macho_nlist<P>* aSymbol = &symbolTable[symbolIndex];
+ const char* aName = &symbolStringPool[aSymbol->n_strx()];
+ if ( resolverSymbolName == aName) {
+ if ( log ) verboseLog("found shared lazy pointer at 0x%llX for %s in %s in %s", (uint64_t)vmlocation, aName, sect->sectname(), _installName);
+ _resolverBlessedMap[resolverSymbolName] = vmlocation;
+ return vmlocation;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( log ) verboseLog( "not found shared lazy pointer for %s in %s, checking re-export dylibs", resolverSymbolName.c_str(), _installName);
+ for (BindInfo<P>* reExportedDylib : _reExportedDylibs ) {
+ pint_t result = reExportedDylib->findBlessedLazyPointerFor(resolverSymbolName);
+ if ( result != 0 ) {
+ _resolverBlessedMap[resolverSymbolName] = result;
+ return result;
+ }
+ }
+
+ if ( log ) verboseLog( "NOT found shared lazy pointer for %s in %s", resolverSymbolName.c_str(), _installName);
+ return 0;
+}
+
+template <typename P>
+void BindInfo<P>::switchStubToUseSharedLazyPointer(const std::string& resolverSymbolName, pint_t lpVMAddr)
+{
+ // find named stub
+ const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
+ const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
+ const char* symbolStringPool = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
+ for (const macho_segment_command<P>* seg : _segCmds) {
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) {
+ pint_t stubsVMStart = (pint_t)sect->addr();
+ uint8_t* stubsMappingStart = ((uint8_t*)_cacheBuffer) + sect->offset();
+ const uint32_t indirectTableOffset = sect->reserved1();
+ const uint32_t stubSize = sect->reserved2();
+ uint32_t elementCount = (uint32_t)(sect->size() / stubSize);
+ pint_t stubVMAddr = stubsVMStart;
+ uint8_t* stubMappedAddr = stubsMappingStart;
+ for (uint32_t j=0; j < elementCount; ++j, stubMappedAddr += stubSize, stubVMAddr += stubSize) {
+ uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]);
+ switch ( symbolIndex ) {
+ case INDIRECT_SYMBOL_ABS:
+ case INDIRECT_SYMBOL_LOCAL:
+ break;
+ default:
+ {
+ const macho_nlist<P>* aSymbol = &symbolTable[symbolIndex];
+ const char* stubName = &symbolStringPool[aSymbol->n_strx()];
+ if ( resolverSymbolName == stubName ) {
+ switch (_mh->cputype()) {
+ case CPU_TYPE_ARM:
+ switchArmStubsLazyPointer(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr);
+ break;
+ default:
+ //warning("shared resolver lazy pointer to %s not implemented for this arch", resolverSymbolName.c_str());
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+template <typename P>
+void BindInfo<P>::switchArmStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
+{
+ if ( stubSize != 16 ) {
+ warning("could not optimize ARM stub to resolver function in %s because it is wrong size\n", _installName);
+ return;
+ }
+ uint32_t* instructions = (uint32_t*)stubMappedAddress;
+ if ( (E::get32(instructions[0]) != 0xe59fc004)
+ || (E::get32(instructions[1]) != 0xe08fc00c)
+ || (E::get32(instructions[2]) != 0xe59cf000)
+ ) {
+ warning("could not optimize ARM stub to resolver function in %s because instructions are not as expected", _installName);
+ return;
+ }
+ // last .long in stub is: lazyPtr - (stub+8)
+ // alter to point to more optimal lazy pointer
+ uint32_t betterOffset = (uint32_t)(lpVMAddr - (stubVMAddress + 12));
+ E::set32(instructions[3], betterOffset);
+}
+
+
+template <typename P>
+void BindInfo<P>::bindAllImagesInCache(void* cacheBuffer, const std::unordered_map<std::string, void*>& dylibPathToMachHeader, std::vector<void*>& pointersForASLR)
+{
+ // build BindInfo object for each dylib
+ std::unordered_map<macho_header<P>*, BindInfo<P>*> headersToBindInfo;
+ std::unordered_map<std::string, BindInfo<P>*> dylibPathToBindInfo;
+ for (const auto& entry: dylibPathToMachHeader) {
+ macho_header<P>* mh = (macho_header<P>*)entry.second;
+ if ( headersToBindInfo.count(mh) == 0 )
+ headersToBindInfo[mh] = new BindInfo<P>(cacheBuffer, mh);
+ dylibPathToBindInfo[entry.first] = headersToBindInfo[mh];
+ }
+
+ // chain re-exported dylibs
+ for (const auto& entry: headersToBindInfo) {
+ entry.second->setDependentDylibs(dylibPathToBindInfo);
+ entry.second->setReExports(dylibPathToBindInfo);
+ }
+
+ // bind each dylib
+ for (const auto& entry: headersToBindInfo) {
+ entry.second->bind(dylibPathToBindInfo, pointersForASLR);
+ }
+
+ // look for exported symbol collisions
+ std::unordered_map<std::string, BindInfo<P>*> reverseMap;
+ for (const auto& entry: headersToBindInfo) {
+ entry.second->addExportsToGlobalMap(reverseMap);
+ }
+
+ // clean up
+ for (const auto& entry: headersToBindInfo) {
+ delete entry.second;
+ }
+}
+
+
+} // anonymous namespace
+
+
+void SharedCache::bindAllImagesInCache(const std::unordered_map<std::string, void*>& dylibPathToMachHeader, std::vector<void*>& pointersForASLR)
+{
+ switch ( _arch.arch ) {
+ case CPU_TYPE_ARM:
+ case CPU_TYPE_I386:
+ BindInfo<Pointer32<LittleEndian>>::bindAllImagesInCache(_buffer.get(), dylibPathToMachHeader, pointersForASLR);
+ break;
+ case CPU_TYPE_X86_64:
+ case CPU_TYPE_ARM64:
+ BindInfo<Pointer64<LittleEndian>>::bindAllImagesInCache(_buffer.get(), dylibPathToMachHeader, pointersForASLR);
+ break;
+ default:
+ terminate("unsupported arch 0x%08X", _arch.arch);
+ }
+}
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef _CODE_SIGNING_TYPES_
+#define _CODE_SIGNING_TYPES_
+
+#include <stdint.h>
+#include <stddef.h>
+
+
+//
+// Magic numbers used by Code Signing
+//
+enum {
+ CSMAGIC_REQUIREMENT = 0xfade0c00, // single Requirement blob
+ CSMAGIC_REQUIREMENTS = 0xfade0c01, // Requirements vector (internal requirements)
+ CSMAGIC_CODEDIRECTORY = 0xfade0c02, // CodeDirectory blob
+ CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, // embedded form of signature data
+ CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, // multi-arch collection of embedded signatures
+ CSMAGIC_BLOBWRAPPER = 0xfade0b01, // used for the cms blob
+};
+
+enum {
+ CS_PAGE_SIZE = 4096,
+
+ CS_HASHTYPE_SHA1 = 1,
+ CS_HASHTYPE_SHA256 = 2,
+ CS_HASHTYPE_SHA256_TRUNCATED = 3,
+
+ CS_HASH_SIZE_SHA1 = 20,
+ CS_HASH_SIZE_SHA256 = 32,
+ CS_HASH_SIZE_SHA256_TRUNCATED = 20,
+
+ CSSLOT_CODEDIRECTORY = 0,
+ CSSLOT_INFOSLOT = 1,
+ CSSLOT_REQUIREMENTS = 2,
+ CSSLOT_RESOURCEDIR = 3,
+ CSSLOT_APPLICATION = 4,
+ CSSLOT_ENTITLEMENTS = 5,
+ CSSLOT_CMS_SIGNATURE = 0x10000,
+
+ kSecCodeSignatureAdhoc = 2
+};
+
+
+
+//
+// Structure of a SuperBlob
+//
+struct CS_BlobIndex {
+ uint32_t type; // type of entry
+ uint32_t offset; // offset of entry
+};
+
+struct CS_SuperBlob {
+ uint32_t magic; // magic number
+ uint32_t length; // total length of SuperBlob
+ uint32_t count; // number of index entries following
+ CS_BlobIndex index[]; // (count) entries
+ // followed by Blobs in no particular order as indicated by offsets in index
+};
+
+//
+// C form of a CodeDirectory.
+//
+struct CS_CodeDirectory {
+ uint32_t magic; // magic number (CSMAGIC_CODEDIRECTORY) */
+ uint32_t length; // total length of CodeDirectory blob
+ uint32_t version; // compatibility version
+ uint32_t flags; // setup and mode flags
+ uint32_t hashOffset; // offset of hash slot element at index zero
+ uint32_t identOffset; // offset of identifier string
+ uint32_t nSpecialSlots; // number of special hash slots
+ uint32_t nCodeSlots; // number of ordinary (code) hash slots
+ uint32_t codeLimit; // limit to main image signature range
+ uint8_t hashSize; // size of each hash in bytes
+ uint8_t hashType; // type of hash (cdHashType* constants)
+ uint8_t platform; // platform identifier; zero if not platform binary
+ uint8_t pageSize; // log2(page size in bytes); 0 => infinite
+ uint32_t spare2; // unused (must be zero)
+ // Version 0x20100 or later
+ uint32_t scatterOffset; // offset of optional scatter vector
+ // followed by dynamic content as located by offset fields above
+};
+
+struct CS_Blob {
+ uint32_t magic; // magic number
+ uint32_t length; // total length of blob
+};
+
+struct CS_RequirementsBlob {
+ uint32_t magic; // magic number
+ uint32_t length; // total length of blob
+ uint32_t data; // zero for dyld shared cache
+};
+
+
+struct CS_Scatter {
+ uint32_t count; // number of pages; zero for sentinel (only)
+ uint32_t base; // first page number
+ uint64_t targetOffset; // byte offset in target
+ uint64_t spare; // reserved (must be zero)
+};
+
+
+#endif // _CODE_SIGNING_TYPES_
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 "mega-dylib-utils.h"
+#include "MachOFileAbstraction.hpp"
+#include "Trie.hpp"
+
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <mach-o/dyld.h>
+#include <assert.h>
+#include <Availability.h>
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <System/sys/csr.h>
+
+#include "dyld_cache_config.h"
+
+#include "OptimizerBranches.h"
+
+#include "CacheFileAbstraction.hpp"
+
+#include "mega-dylib-utils.h"
+#include "Logging.h"
+
+
+//#include <rootless.h>
+extern "C" int rootless_check_trusted(const char *path);
+extern "C" int rootless_check_trusted_fd(int fd) __attribute__((weak_import));
+
+static bool rootlessEnabled;
+static dispatch_once_t onceToken;
+
+bool isProtectedBySIP(const std::string& path, int fd)
+{
+ bool isProtected = false;
+ // Check to make sure file system protections are on at all
+ dispatch_once(&onceToken, ^{
+ rootlessEnabled = csr_check(CSR_ALLOW_UNRESTRICTED_FS);
+ });
+ if (!rootlessEnabled)
+ return false;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+ if ( (fd != -1) && (&rootless_check_trusted_fd != NULL) )
+ isProtected = (rootless_check_trusted_fd(fd) == 0);
+ else
+#endif
+ if ( &rootless_check_trusted != NULL )
+ isProtected = (rootless_check_trusted(path.c_str()) == 0);
+ return isProtected;
+}
+
+
+std::string toolDir()
+{
+ char buffer[PATH_MAX];
+ uint32_t bufsize = PATH_MAX;
+ int result = _NSGetExecutablePath(buffer, &bufsize);
+ if ( result == 0 ) {
+ std::string path = buffer;
+ size_t pos = path.rfind('/');
+ if ( pos != std::string::npos )
+ return path.substr(0,pos+1);
+ }
+ warning("tool directory not found");
+ return "/tmp/";
+}
+
+std::string baspath(const std::string& path)
+{
+ std::string::size_type slash_pos = path.rfind("/");
+ if (slash_pos != std::string::npos) {
+ slash_pos++;
+ return path.substr(slash_pos);
+ } else {
+ return path;
+ }
+}
+
+std::string dirpath(const std::string& path)
+{
+ std::string::size_type slash_pos = path.rfind("/");
+ if (slash_pos != std::string::npos) {
+ slash_pos++;
+ return path.substr(0, slash_pos);
+ } else {
+ char cwd[MAXPATHLEN];
+ (void)getcwd(cwd, MAXPATHLEN);
+ return cwd;
+ }
+}
+
+std::string normalize_absolute_file_path(const std::string &path) {
+ std::vector<std::string> components;
+ std::vector<std::string> processed_components;
+ std::stringstream ss(path);
+ std::string retval;
+ std::string item;
+
+ while (std::getline(ss, item, '/')) {
+ components.push_back(item);
+ }
+
+ if (components[0] == ".") {
+ retval = ".";
+ }
+
+ for (auto& component : components) {
+ if (component.empty() || component == ".")
+ continue;
+ else if (component == ".." && processed_components.size())
+ processed_components.pop_back();
+ else
+ processed_components.push_back(component);
+ }
+
+ for (auto & component : processed_components) {
+ retval = retval + "/" + component;
+ }
+
+ return retval;
+}
+
+FileCache fileCache;
+
+FileCache::FileCache(void) {
+ cache_queue = dispatch_queue_create("com.apple.dyld.cache.cache", DISPATCH_QUEUE_SERIAL);
+}
+
+
+void FileCache::preflightCache(const std::unordered_set<std::string>& paths) {
+ for (auto &path : paths) {
+ preflightCache(path);
+ }
+}
+
+void FileCache::preflightCache(const std::string& path)
+{
+ cacheBuilderDispatchAsync(cache_queue, [=] {
+ std::string normalizedPath = normalize_absolute_file_path(path);
+ if (entries.count(normalizedPath) == 0) {
+ fill(normalizedPath);
+ }
+ });
+}
+
+std::tuple<uint8_t *, struct stat, bool> FileCache::cacheLoad(const std::string path) {
+ std::string normalizedPath = normalize_absolute_file_path(path);
+ cacheBuilderDispatchSync(cache_queue, [=] {
+ if ( entries.count(normalizedPath) == 0 )
+ fill(normalizedPath);
+ });
+
+ return entries[normalizedPath];
+}
+
+
+//FIXME error handling
+void FileCache::fill(const std::string& path) {
+ struct stat stat_buf;
+
+ int fd = ::open(path.c_str(), O_RDONLY, 0);
+ if ( fd == -1 ) {
+ verboseLog("can't open file '%s', errno=%d", path.c_str(), errno);
+ entries[path] = std::make_tuple((uint8_t*)(-1), stat_buf, false);
+ return;
+ }
+
+ if ( fstat(fd, &stat_buf) == -1) {
+ verboseLog("can't stat open file '%s', errno=%d", path.c_str(), errno);
+ entries[path] = std::make_tuple((uint8_t*)(-1), stat_buf, false);
+ ::close(fd);
+ return;
+ }
+
+ if ( stat_buf.st_size < 4096 ) {
+ verboseLog("file too small '%s'", path.c_str());
+ entries[path] = std::make_tuple((uint8_t*)(-1), stat_buf, false);
+ ::close(fd);
+ return;
+ }
+
+ bool rootlessProtected = isProtectedBySIP(path, fd);
+
+ void* buffer_ptr = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (buffer_ptr == MAP_FAILED) {
+ verboseLog("mmap() for shared cache at %s failed, errno=%d", path.c_str(), errno);
+ entries[path] = std::make_tuple((uint8_t*)(-1), stat_buf, false);
+ ::close(fd);
+ return;
+ }
+
+ entries[path] = std::make_tuple((uint8_t*)buffer_ptr, stat_buf, rootlessProtected);
+ ::close(fd);
+}
+
+
+
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2016 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 <dispatch/dispatch.h>
+#include <string.h>
+#include <stdlib.h>
+#include <set>
+
+#include <assert.h>
+#include "mega-dylib-utils.h"
+
+#include "Logging.h"
+
+#define MAX_LOG_STR_LEN (512)
+//const char* kDispatchQueueLogNameKey = "kDispatchQueueLogNameKey";
+const char* kDispatchWarningArrayKey = "kDispatchWarningArrayKey";
+
+static dispatch_queue_t log_queue;
+static dispatch_once_t logQueueInit = 0;
+static dispatch_queue_t unique_queue;
+static dispatch_once_t uniqueQueueInit = 0;
+
+static uint32_t verbose = 0;
+static bool returnNonZeroIfTerminateCalled = false;
+static bool terminateCalled = false;
+
+static const char* warningPrefix = "WARNING: ";
+static const char* errorPrefix = "ERROR: ";
+
+LoggingContext::LoggingContext(const std::string& N)
+ : _name(N)
+ , _tainted(false)
+{
+}
+
+LoggingContext::LoggingContext(const std::string& N, WarningTargets T)
+ : _name(N)
+ , _warnings(T)
+ , _tainted(false)
+{
+}
+
+void LoggingContext::taint()
+{
+ _tainted = true;
+}
+
+bool LoggingContext::isTainted()
+{
+ return _tainted;
+}
+
+const std::string& LoggingContext::name()
+{
+ return _name;
+}
+
+const WarningTargets& LoggingContext::targets()
+{
+ return _warnings;
+}
+
+
+
+pthread_key_t getLoggingContextKey(void) {
+ static pthread_key_t logContextKey;
+ static dispatch_once_t logContextToken;
+ dispatch_once(&logContextToken, ^{
+ pthread_key_create(&logContextKey, nullptr);
+ });
+ return logContextKey;
+}
+
+
+void setLoggingContext(std::shared_ptr<LoggingContext>& context)
+{
+ pthread_setspecific(getLoggingContextKey(), (void*)&context);
+
+ if (context && !context->name().empty()) {
+ pthread_setname_np(context->name().substr(0, MAXTHREADNAMESIZE-1).c_str());
+ }
+}
+
+std::shared_ptr<LoggingContext> getLoggingContext()
+{
+ if (void* val = pthread_getspecific(getLoggingContextKey()))
+ return *((std::shared_ptr<LoggingContext>*)val);
+ return nullptr;
+}
+
+void runBody(void* Ctx)
+{
+ std::unique_ptr<std::function<void(void)>>
+ Body(reinterpret_cast<std::function<void(void)>*>(Ctx));
+ (*Body)();
+}
+
+static dispatch_queue_t getLogQueue()
+{
+ dispatch_once(&logQueueInit, ^{
+ log_queue = dispatch_queue_create("com.apple.dyld.cache.logging", DISPATCH_QUEUE_SERIAL);
+ });
+ return log_queue;
+}
+
+void setVerbose(bool level)
+{
+ verbose = level;
+}
+
+void setWarnAnErrorPrefixes(const char* warn, const char* err)
+{
+ warningPrefix = warn;
+ errorPrefix = err;
+}
+
+void setReturnNonZeroOnTerminate()
+{
+ returnNonZeroIfTerminateCalled = true;
+}
+
+void queued_print(FILE* __restrict fd, const char* str)
+{
+ const char* qstr = strdup(str);
+
+ dispatch_async(getLogQueue(), ^{
+ (void)fprintf(fd, "%s", qstr);
+ free((void*)qstr);
+ });
+}
+
+#define VLOG(header) \
+ va_list list; \
+ va_start(list, format); \
+ char temp[MAX_LOG_STR_LEN]; \
+ vsprintf(temp, format, list); \
+ auto ctx = getLoggingContext(); \
+ char temp2[MAX_LOG_STR_LEN]; \
+ if (ctx && !ctx->name().empty()) { \
+ snprintf(temp2, MAX_LOG_STR_LEN, "[%s] %s%s\n", ctx->name().c_str(), header, \
+ temp); \
+ } else { \
+ snprintf(temp2, MAX_LOG_STR_LEN, "%s%s\n", header, temp); \
+ } \
+ queued_print(stderr, temp2); \
+ va_end(list);
+
+void log(const char* __restrict format, ...)
+{
+ VLOG("");
+}
+
+void verboseLog(const char* format, ...)
+{
+ if (verbose) {
+ VLOG("");
+ }
+}
+
+static std::set<std::string> warnings;
+
+void warning(const char* format, ...)
+{
+ dispatch_once(&uniqueQueueInit, ^{
+ unique_queue = dispatch_queue_create("com.apple.dyld.cache.logging", DISPATCH_QUEUE_SERIAL);
+ });
+
+ va_list list;
+ va_start(list, format);
+ char temp[MAX_LOG_STR_LEN];
+ vsprintf(temp, format, list);
+ char* blockTemp = strdup(temp);
+
+ auto ctx = getLoggingContext();
+ if (ctx) {
+ for (auto& target : ctx->targets().second) {
+ ctx->targets().first->configurations[target.first].architectures[target.second].results.warnings.push_back(blockTemp);
+ }
+ }
+
+ dispatch_sync(unique_queue, ^{
+ if (warnings.count(blockTemp) == 0) {
+ warnings.insert(blockTemp);
+ }
+
+ free(blockTemp);
+ });
+
+ va_end(list);
+}
+
+void terminate(const char* format, ...)
+{
+ VLOG(errorPrefix);
+
+ terminateCalled = true;
+
+ if (ctx) {
+ // We are a work in a logging context, throw
+ throw std::string(temp);
+ } else {
+ // We are in general handing, let the loggging queue darain and exit
+ dispatch_sync(getLogQueue(), ^{
+ for (auto& warning : warnings) {
+ (void)fprintf(stderr, "%s%s\n", warningPrefix, warning.c_str());
+ }
+ if ( returnNonZeroIfTerminateCalled ) {
+ exit(1);
+ }
+ else {
+ time_t endtime = time(0);
+ (void)fprintf(stderr, "Finished: %s\n", asctime(localtime(&endtime)));
+ (void)fprintf(stderr, "Exiting\n");
+ exit(0);
+ }
+ });
+ }
+
+ // clang can't reason out that we won't hit this due to the dispatch_sync in the exit path
+ __builtin_unreachable();
+}
+
+void dumpLogAndExit(bool logFinishTime)
+{
+ dispatch_async(getLogQueue(), ^{
+ for (auto& warning : warnings) {
+ (void)fprintf(stderr, "%s%s\n", warningPrefix, warning.c_str());
+ }
+ if ( logFinishTime ) {
+ time_t endtime = time(0);
+ (void)fprintf(stderr, "Finished: %s\n", asctime(localtime(&endtime)));
+ (void)fprintf(stderr, "Exiting\n");
+ }
+ exit(returnNonZeroIfTerminateCalled && terminateCalled ? 1 : 0);
+ });
+}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2016 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 LOGGING_H
+#define LOGGING_H
+
+#include <stdio.h>
+#include <pthread.h>
+#include "mega-dylib-utils.h"
+
+void verboseLog(const char* format, ...) __printflike(1, 2);
+void log(const char* __restrict format, ...) __printflike(1, 2);
+void alwaysLog(const char* __restrict format, ...) __printflike(1, 2);
+
+void warning(const char* format, ...) __printflike(1, 2);
+void terminate(const char* format, ...) __printflike(1, 2) __attribute__((noreturn));
+void dumpLogAndExit(bool logFinishTime=true);
+
+void setVerbose(bool level);
+void setReturnNonZeroOnTerminate();
+void setWarnAnErrorPrefixes(const char* warn, const char* err);
+
+struct LoggingContext {
+ LoggingContext(const std::string& N);
+ LoggingContext(const std::string& N, WarningTargets T);
+ void taint();
+ bool isTainted();
+ const std::string& name();
+ const WarningTargets& targets();
+
+private:
+ const std::string _name;
+ const WarningTargets _warnings;
+ bool _tainted;
+};
+
+void setLoggingContext(std::shared_ptr<LoggingContext>& context);
+std::shared_ptr<LoggingContext> getLoggingContext();
+
+/* Okay, so this gets tricky
+ * Naively, what we are doing is stashing some information in pthread specific
+ * variables so that the logging system can pick it up and tag messages with it,
+ * but without us having to track it all through the entire cache builder code base.
+ * Additionally, we use the presence of that information to determine if we are in
+ * the root logging context (where terminate() calls are fatal), or a thread context
+ * (where they should just throw so the thread fails, logs an error, and cleans up its
+ * state.
+ *
+ * The problem is that we need that context to follow our blocks when we switch threads.
+ * We achieve that by wrapping dispatch_(a)sync with our own calls that setup try{} blocks
+ * around the executing lambda, that way it is always safe to throw in a named context
+ * name. We also use those wrappers to copy the context between the threads using
+ * the closures as glue. Finally, we set a taint variable that can back propgate to stop
+ * the execution of any furthur blocks related to a context that has thrown.
+ *
+ * This is exposed in the header because we need it for the templates to work, but aside from
+ * cacheBuilderDispatchAsync() and friends nothing here should be used directly.
+ */
+
+void runBody(void* Ctx);
+
+pthread_key_t getLoggingContextKey(void);
+
+template <typename BodyFtor>
+std::function<void(void)>* heapSafe(BodyFtor&& Body, std::shared_ptr<LoggingContext> context)
+{
+ auto retval = new std::function<void(void)>([ B = std::move(Body), context ]() mutable {
+ if (!context || !context->isTainted()) {
+
+ void* oldCtx = pthread_getspecific(getLoggingContextKey());
+ setLoggingContext(context);
+ try {
+ B();
+ } catch (std::string exception) {
+ WarningTargets warningTargets = context->targets();
+ for (auto& target : warningTargets.second) {
+ warningTargets.first->configurations[target.first].architectures[target.second].results.failure = exception;
+ }
+ if (context) {
+ context->taint();
+ }
+ } catch (...) {
+ if (context) {
+ context->taint();
+ }
+ }
+ pthread_setspecific(getLoggingContextKey(), oldCtx);
+ }
+ });
+ return retval;
+}
+
+template <typename BodyFtor>
+void cacheBuilderDispatchAsync(dispatch_queue_t queue, BodyFtor&& Body)
+{
+ dispatch_async_f(queue, heapSafe(Body, getLoggingContext()), runBody);
+}
+
+template <typename BodyFtor>
+void cacheBuilderDispatchGroupAsync(dispatch_group_t group, dispatch_queue_t queue, BodyFtor&& Body)
+{
+ dispatch_group_async_f(group, queue, heapSafe(Body, getLoggingContext()), runBody);
+}
+
+template <typename BodyFtor>
+void cacheBuilderDispatchSync(dispatch_queue_t queue, BodyFtor&& Body)
+{
+ dispatch_sync_f(queue, heapSafe(Body, getLoggingContext()), runBody);
+}
+
+#endif /* LOGGING_H */
--- /dev/null
+//
+// DylibProxy.cpp
+// dyld
+//
+// Created by Louis Gerbarg on 1/27/16.
+//
+//
+
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+
+#include "mega-dylib-utils.h"
+#include "Logging.h"
+
+#include "MachOProxy.h"
+
+namespace {
+std::map<std::string, MachOProxy*> mapMachOFile( const std::string& path ) {
+ std::map<std::string, MachOProxy*> retval;
+ const uint8_t* p = (uint8_t*)( -1 );
+ struct stat stat_buf;
+ bool rootless;
+
+ std::tie( p, stat_buf, rootless ) = fileCache.cacheLoad( path );
+
+ if ( p == (uint8_t*)( -1 ) ) {
+ return retval;
+ }
+
+ // if fat file, process each architecture
+ const fat_header* fh = (fat_header*)p;
+ const mach_header* mh = (mach_header*)p;
+ if ( OSSwapBigToHostInt32( fh->magic ) == FAT_MAGIC ) {
+ // Fat header is always big-endian
+ const fat_arch* slices = (const fat_arch*)( (char*)fh + sizeof( fat_header ) );
+ const uint32_t sliceCount = OSSwapBigToHostInt32( fh->nfat_arch );
+ for ( uint32_t i = 0; i < sliceCount; ++i ) {
+ // FIXME Should we validate the fat header matches the slices?
+ ArchPair arch( OSSwapBigToHostInt32( slices[i].cputype ), OSSwapBigToHostInt32( slices[i].cpusubtype ) );
+ uint32_t fileOffset = OSSwapBigToHostInt32( slices[i].offset );
+ const mach_header* th = (mach_header*)(p+fileOffset);
+ if ( ( OSSwapLittleToHostInt32( th->magic ) == MH_MAGIC ) || ( OSSwapLittleToHostInt32( th->magic ) == MH_MAGIC_64 ) ) {
+ uint32_t fileSize = static_cast<uint32_t>( stat_buf.st_size );
+ retval[stringForArch( arch )] = new MachOProxy( path, stat_buf.st_ino, stat_buf.st_mtime, fileOffset, fileSize, rootless );
+ }
+ }
+ } else if ( ( OSSwapLittleToHostInt32( mh->magic ) == MH_MAGIC ) || ( OSSwapLittleToHostInt32( mh->magic ) == MH_MAGIC_64 ) ) {
+ ArchPair arch( OSSwapLittleToHostInt32( mh->cputype ), OSSwapLittleToHostInt32( mh->cpusubtype ) );
+ uint32_t fileOffset = OSSwapBigToHostInt32( 0 );
+ uint32_t fileSize = static_cast<uint32_t>( stat_buf.st_size );
+ retval[stringForArch( arch )] = new MachOProxy( path, stat_buf.st_ino, stat_buf.st_mtime, fileOffset, fileSize, rootless );
+ } else {
+ // warning( "file '%s' is not contain requested a MachO", path.c_str() );
+ }
+ return retval;
+}
+}
+
+template <typename P>
+std::string MachOProxy::machoParser(bool ignoreUncacheableDylibsInExecutables)
+{
+ const uint8_t* buffer = getBuffer();
+ bool hasSplitSegInfo = false;
+ bool hasDylidInfo = false;
+ const macho_header<P>* mh = (const macho_header<P>*)buffer;
+ const macho_symtab_command<P>* symTab = nullptr;
+ const macho_dysymtab_command<P>* dynSymTab = nullptr;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ const uint32_t cmd_count = mh->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ _dylib = (mh->filetype() == MH_DYLIB);
+ _executable = (mh->filetype() == MH_EXECUTE);
+ if (mh->filetype() == MH_DYLIB_STUB) {
+ return "stub dylib";
+ }
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd()) {
+ case LC_ID_DYLIB: {
+ macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
+ if (dylib->name()[0] != '/') {
+ if (strncmp(dylib->name(), "@rpath", 6) == 0)
+ return "@rpath cannot be used in -install_name for OS dylibs";
+ else
+ return "-install_name is not an absolute path";
+ }
+ installName = dylib->name();
+ installNameOffsetInTEXT = (uint32_t)((uint8_t*)cmd - buffer) + dylib->name_offset();
+ } break;
+ case LC_UUID: {
+ const macho_uuid_command<P>* uuidCmd = (macho_uuid_command<P>*)cmd;
+ ::memcpy(uuid, uuidCmd->uuid(), sizeof(uuid_t));
+ } break;
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB: {
+ macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
+ std::string depName = dylib->name();
+ if ( _executable && ignoreUncacheableDylibsInExecutables && !has_prefix(depName, "/usr/lib/") && !has_prefix(depName, "/System/Library/") ) {
+ // <rdar://problem/25918268> in update_dyld_shared_cache don't warn if root executable links with something not eligible for shared cache
+ break;
+ }
+ else if ( depName[0] != '/' ) {
+ return "linked against a dylib whose -install_name was non-absolute (e.g. @rpath)";
+ }
+ dependencies.insert(depName);
+ } break;
+ case macho_segment_command<P>::CMD: {
+ const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
+ MachOProxy::Segment seg;
+ seg.name = segCmd->segname();
+ seg.size = align(segCmd->vmsize(), 12);
+ seg.diskSize = (uint32_t)segCmd->filesize();
+ seg.fileOffset = (uint32_t)segCmd->fileoff();
+ seg.protection = segCmd->initprot();
+ if (segCmd->nsects() > 0) {
+ seg.p2align = 0;
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsLast = §ionsStart[segCmd->nsects() - 1];
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for (const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if (sect->align() > seg.p2align)
+ seg.p2align = sect->align();
+ }
+ seg.sizeOfSections = sectionsLast->addr() + sectionsLast->size() - segCmd->vmaddr();
+ } else {
+ seg.p2align = 12;
+ }
+ segments.push_back(seg);
+ } break;
+ case LC_SEGMENT_SPLIT_INFO:
+ hasSplitSegInfo = true;
+ break;
+ case LC_SYMTAB:
+ symTab = (macho_symtab_command<P>*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ dynSymTab = (macho_dysymtab_command<P>*)cmd;
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ hasDylidInfo = true;
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd) + cmd->cmdsize());
+ }
+
+ if (!_dylib) {
+ return "";
+ }
+
+ if (!hasDylidInfo) {
+ return "built for old OS";
+ }
+
+ if ((mh->flags() & MH_TWOLEVEL) == 0) {
+ return "built with -flat_namespace";
+ }
+
+ if (!hasSplitSegInfo) {
+ bool inUsrLib = (installName.size() > 9) && (installName.substr(0, 9) == "/usr/lib/");
+ bool inSystemLibrary = (installName.size() > 16) && (installName.substr(0, 16) == "/System/Library/");
+ if (!inUsrLib && !inSystemLibrary) {
+ return "-install_name not /usr/lib/* or /System/Library/*";
+ }
+ return "no shared region info";
+ }
+
+ if ((symTab == nullptr) && (dynSymTab == nullptr)) {
+ return "no symbol table";
+ }
+
+ if (installName.empty()) {
+ return "dylib missing install name";
+ }
+
+ // scan undefines looking for invalid ordinals
+ const macho_nlist<P>* symbolTable = (macho_nlist<P>*)((uint8_t*)mh + symTab->symoff());
+ const uint32_t startUndefs = dynSymTab->iundefsym();
+ const uint32_t endUndefs = startUndefs + dynSymTab->nundefsym();
+ for (uint32_t i = startUndefs; i < endUndefs; ++i) {
+ uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[i].n_desc());
+ if (ordinal == DYNAMIC_LOOKUP_ORDINAL) {
+ return "built with '-undefined dynamic_lookup'";
+ } else if (ordinal == EXECUTABLE_ORDINAL) {
+ return "built with -bundle_loader";
+ }
+ }
+
+ return "";
+}
+
+const bool MachOProxy::isDylib()
+{
+ return _dylib;
+}
+
+const bool MachOProxy::isExecutable()
+{
+ return _executable;
+}
+
+std::map<std::string, MachOProxy*> MachOProxy::findDylibInfo(const std::string& path, bool warnOnProblems, bool ignoreUncacheableDylibsInExecutables) {
+ std::map<std::string, MachOProxy*> slices = mapMachOFile( path );
+ std::vector<std::string> errorSlices;
+
+ for ( auto& slice : slices ) {
+ std::string errorMessage;
+ verboseLog( "analyzing file '%s'", path.c_str() );
+ switch ( archForString( slice.first ).arch ) {
+ case CPU_TYPE_ARM:
+ case CPU_TYPE_I386:
+ errorMessage = slice.second->machoParser<Pointer32<LittleEndian>>(ignoreUncacheableDylibsInExecutables);
+ break;
+ case CPU_TYPE_X86_64:
+ case CPU_TYPE_ARM64:
+ errorMessage = slice.second->machoParser<Pointer64<LittleEndian>>(ignoreUncacheableDylibsInExecutables);
+ break;
+ default:
+ errorMessage = "unsupported arch '" + slice.first + "'";
+ break;
+ }
+
+ if ( !errorMessage.empty() ) {
+ if ( warnOnProblems )
+ warning( "%s (%s)", errorMessage.c_str(), path.c_str() );
+ errorSlices.push_back( slice.first );
+ }
+ }
+
+ for ( const auto& slice : errorSlices ) {
+ slices.erase( slice );
+ }
+
+ return slices;
+}
+
+const uint8_t* MachOProxy::getBuffer() {
+ const uint8_t* p = (uint8_t*)( -1 );
+ struct stat stat_buf;
+ bool rootless;
+ std::tie(p, stat_buf,rootless) = fileCache.cacheLoad(path);
+ return p + fatFileOffset;
+}
+
+bool MachOProxy::addAlias( const std::string& alias ) {
+ if (!has_prefix(alias, "/usr/lib/") && !has_prefix(alias, "/System/Library/"))
+ return false;
+ if ( alias != installName && installNameAliases.count( alias ) == 0 ) {
+ installNameAliases.insert( alias );
+ return true;
+ }
+ return false;
+}
--- /dev/null
+//
+// DylibProxy.h
+// dyld
+//
+// Created by Louis Gerbarg on 1/27/16.
+//
+//
+
+#ifndef MachOProxy_h
+#define MachOProxy_h
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <cstdint>
+
+#include <sys/stat.h>
+
+struct MachOProxy {
+ MachOProxy(const std::string& p, ino_t i, time_t t, uint32_t o, uint32_t s, bool r) :
+ path(p), fatFileOffset(o), fileSize(s), lastModTime(t), inode(i), installNameOffsetInTEXT(0), rootlessProtected(r) {
+ bzero( &uuid, sizeof( uuid_t ) );
+ }
+
+ struct Segment {
+ std::string name;
+ uint64_t size;
+ uint64_t sizeOfSections;
+ uint32_t diskSize;
+ uint32_t fileOffset;
+ uint8_t p2align;
+ uint8_t protection;
+ };
+
+ const std::string path;
+ const uint32_t fatFileOffset;
+ const uint32_t fileSize;
+ const time_t lastModTime;
+ const ino_t inode;
+ const bool rootlessProtected;
+ std::string installName;
+ std::set<std::string> installNameAliases;
+ uint32_t installNameOffsetInTEXT;
+ std::set<std::string> dependencies;
+ std::set<std::string> dependents;
+ uuid_t uuid;
+ std::vector<Segment> segments;
+
+ const uint8_t* getBuffer();
+ const bool isDylib();
+ const bool isExecutable();
+ bool addAlias(const std::string& alias);
+
+ static std::map<std::string, MachOProxy*> findDylibInfo(const std::string& path, bool warnOnProblems=false, bool ignoreUncacheableDylibsInExecutables=false);
+
+private:
+ bool _dylib;
+ bool _executable;
+
+ template <typename P>
+ std::string machoParser(bool ignoreUncacheableDylibsInExecutables);
+};
+
+
+#endif /* MachOProxy_h */
--- /dev/null
+//
+// Manifest.h
+// dyld
+//
+// Created by Louis Gerbarg on 7/23/15.
+//
+//
+
+#ifndef Manifest_h
+#define Manifest_h
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <unordered_map>
+#include <unordered_set>
+
+
+struct SharedCache;
+struct MachOProxy;
+struct Manifest;
+
+struct Manifest {
+ struct Project {
+ std::vector<std::string> sources;
+ };
+
+ struct File {
+ MachOProxy* proxy;
+
+ File( MachOProxy* P ) : proxy( P ) {}
+ };
+
+ struct FileSet {
+ std::map<std::string, File> dylibs;
+ std::map<std::string, File> executables;
+ };
+
+ struct Anchor {
+ std::string installname;
+ bool required;
+
+ Anchor( const std::string& IN ) : installname( IN ) {}
+ };
+
+ struct SegmentInfo {
+ std::string name;
+ uint64_t startAddr;
+ uint64_t endAddr;
+ };
+
+ struct SegmentInfoHasher {
+ std::size_t operator()(const SegmentInfo &x) const {
+ return std::hash<std::string>()(x.name) ^ std::hash<uint64_t>()(x.startAddr) ^ std::hash<uint64_t>()(x.endAddr);
+ }
+ };
+
+ struct CacheInfo {
+ std::vector<SegmentInfo> regions;
+ std::string cdHash;
+ };
+
+ struct DylibInfo {
+ bool included;
+ std::string exclusionInfo;
+ uuid_t uuid;
+ std::vector<SegmentInfo> segments;
+ DylibInfo(void) : included(true) {}
+ void exclude(const std::string& reason) {
+ included = false;
+ exclusionInfo = reason;
+ }
+ };
+
+ struct Results {
+ std::string failure;
+ std::map<std::string, DylibInfo> dylibs;
+ std::vector<std::string> warnings;
+ CacheInfo developmentCache;
+ CacheInfo productionCache;
+ };
+
+ struct Architecture {
+ std::vector<Anchor> anchors;
+ mutable Results results;
+ //FIXME: Gross
+ std::unordered_map<std::string, std::unordered_set<std::string>> dependents;
+
+ uint64_t hash(void) const {
+ if (!_hash) {
+ for (auto& dylib : results.dylibs) {
+ if (dylib.second.included) {
+ _hash ^= std::hash<std::string>()(dylib.first);
+ //HACK to get some of the UUID into the hash
+ _hash ^= std::hash<uint64_t>()(*(uint64_t *)(&dylib.second.uuid[0]));
+ }
+ };
+ }
+
+ return _hash;
+ }
+
+ bool equivalent(const Architecture& O) const {
+ if (hash() != O.hash()) {
+ return false;
+ }
+ for (auto& dylib : results.dylibs) {
+ if (dylib.second.included) {
+ auto Odylib = O.results.dylibs.find(dylib.first);
+ if (Odylib == O.results.dylibs.end()
+ || Odylib->second.included == false
+ || memcmp(&Odylib->second.uuid[0], &dylib.second.uuid[0], sizeof(uuid_t)) != 0)
+ return false;
+ }
+ }
+ //Iterate over O.results to make sure we included all the same things
+ for (auto Odylib : O.results.dylibs) {
+ if (Odylib.second.included) {
+ auto dylib = results.dylibs.find(Odylib.first);
+ if (dylib == results.dylibs.end()
+ || dylib->second.included == false)
+ return false;
+ }
+ }
+ return true;
+ }
+ private:
+ mutable uint64_t _hash = 0;
+ };
+
+ struct Configuration {
+ std::string platformName;
+ std::string metabomTag;
+ std::set<std::string> metabomExcludeTags;
+ std::set<std::string> metabomRestrictTags;
+ std::set<std::string> restrictedInstallnames;
+ std::map<std::string, Architecture> architectures;
+
+ uint64_t hash( void ) const {
+ if (!_hash) {
+ _hash ^= std::hash<size_t>()(architectures.size());
+ // We want the preliminary info here to make dedupe decisions
+ for (auto& arch : architectures) {
+ _hash ^= arch.second.hash();
+ };
+ }
+ return _hash;
+ }
+
+ //Used for dedupe
+ bool equivalent(const Configuration& O) const {
+ if (hash() != O.hash())
+ return false;
+ for (const auto& arch : architectures) {
+ if (O.architectures.count(arch.first) == 0)
+ return false;
+ if (!arch.second.equivalent(O.architectures.find(arch.first)->second))
+ return false;
+ }
+
+ return true;
+ }
+ private:
+ mutable uint64_t _hash = 0;
+ };
+
+ std::map<std::string, FileSet> architectureFiles;
+ std::map<std::string, Project> projects;
+ std::map<std::string, Configuration> configurations;
+ std::string dylibOrderFile;
+ std::string dirtyDataOrderFile;
+ std::string metabomFile;
+ std::string build;
+ // FIXME every needs to adopt platform string for v5
+ std::string platform;
+ uint32_t manifest_version;
+ bool normalized;
+
+ Manifest( void ) {}
+#if BOM_SUPPORT
+ Manifest( const std::string& path );
+ Manifest( const std::string& path, const std::set<std::string>& overlays );
+#endif
+ void write( const std::string& path );
+ void canonicalize( void );
+ void calculateClosure( bool enforeceRootless );
+ void pruneClosure();
+ bool sameContentsAsCacheAtPath( const std::string& configuration, const std::string& architecture,
+ const std::string& path ) const;
+ MachOProxy* dylibProxy( const std::string& installname, const std::string& arch );
+ MachOProxy* removeLargestLeafDylib( const std::string& configuration, const std::string& architecture );
+
+private:
+ void removeDylib( MachOProxy* proxy, const std::string& reason, const std::string& configuration, const std::string& architecture,
+ std::unordered_set<std::string>& processedInstallnames );
+ File* dylibForInstallName( const std::string& installname, const std::string& arch );
+ void calculateClosure( const std::string& configuration, const std::string& architecture);
+ void pruneClosure(const std::string& configuration, const std::string& architecture);
+ void canonicalizeDylib( const std::string& installname );
+ template <typename P>
+ void canonicalizeDylib( const std::string& installname, const uint8_t* p );
+ void addImplicitAliases( void );
+};
+
+
+#endif /* Manifest_h */
--- /dev/null
+//
+// Manifest.mm
+// dyld
+//
+// Created by Louis Gerbarg on 7/23/15.
+//
+//
+
+#if BOM_SUPPORT
+extern "C" {
+#include <Bom/Bom.h>
+#include <Metabom/MBTypes.h>
+#include <Metabom/MBEntry.h>
+#include <Metabom/MBMetabom.h>
+#include <Metabom/MBIterator.h>
+};
+#endif /* BOM_SUPPORT */
+
+#include <algorithm>
+
+#include <Foundation/Foundation.h>
+#include <rootless.h>
+
+#include "MachOFileAbstraction.hpp"
+#include "FileAbstraction.hpp"
+#include "Trie.hpp"
+#include "Logging.h"
+
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+
+#include <array>
+#include <vector>
+
+#include "Manifest.h"
+#include "dsc_iterator.h"
+
+#include "mega-dylib-utils.h"
+
+namespace {
+ //FIXME this should be in a class
+ static bool rootless = true;
+
+ static inline NSString* cppToObjStr(const std::string& str) { return [NSString stringWithUTF8String:str.c_str()]; }
+
+#if BOM_SUPPORT
+ std::string effectivePath(const std::string source, const std::string target)
+ {
+ if (target[0] == '/')
+ return normalize_absolute_file_path(target);
+
+ std::size_t found = source.find_last_of('/');
+ return normalize_absolute_file_path(source.substr(0, found) + "/" + target);
+ }
+
+ std::string checkSymlink(const std::string path, const std::pair<std::string, std::string>& symlink, const std::set<std::string>& directories)
+ {
+ if (directories.count(symlink.second) == 0) {
+ if (path == symlink.second)
+ return symlink.first;
+ } else {
+ auto res = std::mismatch(symlink.second.begin(), symlink.second.end(), path.begin());
+ if (res.first == symlink.second.end()) {
+ std::string alias = normalize_absolute_file_path(symlink.first + std::string(res.second, path.end()));
+ return alias;
+ }
+ }
+ return "";
+ }
+#endif
+ }
+
+#if BOM_SUPPORT
+
+ Manifest::Manifest(const std::string& path)
+ : Manifest(path, std::set<std::string>())
+ {
+ }
+
+ Manifest::Manifest(const std::string& path, const std::set<std::string>& overlays)
+ {
+ NSMutableDictionary* manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
+ std::map<std::string, std::string> metabomTagMap;
+ std::map<std::string, std::set<std::string>> metabomExcludeTagMap;
+ std::map<std::string, std::set<std::string>> metabomRestrictedTagMap;
+ metabomFile = [manifestDict[@"metabomFile"] UTF8String];
+
+ for (NSString* project in manifestDict[@"projects"]) {
+ for (NSString* source in manifestDict[@"projects"][project]) {
+ projects[[project UTF8String]].sources.push_back([source UTF8String]);
+ }
+ }
+
+ for (NSString* configuration in manifestDict[@"configurations"]) {
+ std::string configStr = [configuration UTF8String];
+ std::string configTag = [manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
+ metabomTagMap[configTag] = configStr;
+
+ if (manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
+ for (NSString* excludeTag in manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
+ metabomExcludeTagMap[configStr].insert([excludeTag UTF8String]);
+ configurations[configStr].metabomExcludeTags.insert([excludeTag UTF8String]);
+ }
+ }
+
+ if (manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
+ for (NSString* restrictTag in manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
+ metabomRestrictedTagMap[configStr].insert([restrictTag UTF8String]);
+ configurations[configStr].metabomRestrictTags.insert([restrictTag UTF8String]);
+ }
+ }
+
+ configurations[configStr].metabomTag = configTag;
+ configurations[configStr].platformName =
+ [manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
+ }
+
+ manifest_version = [manifestDict[@"manifest-version"] unsignedIntValue];
+ build = [manifestDict[@"build"] UTF8String];
+ if (manifestDict[@"dylibOrderFile"]) {
+ dylibOrderFile = [manifestDict[@"dylibOrderFile"] UTF8String];
+ }
+ if (manifestDict[@"dirtyDataOrderFile"]) {
+ dirtyDataOrderFile = [manifestDict[@"dirtyDataOrderFile"] UTF8String];
+ }
+
+ auto metabom = MBMetabomOpen(metabomFile.c_str(), false);
+ auto metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
+ MBEntry entry;
+
+ std::map<std::string, std::string> symlinks;
+ std::set<std::string> directories;
+
+ // FIXME error handling (NULL metabom)
+
+ while ((entry = MBIteratorNext(metabomEnumerator))) {
+ auto fsObject = MBEntryGetFSObject(entry);
+ std::string entryPath = BOMFSObjectPathName(fsObject);
+ if (entryPath[0] == '.') {
+ entryPath.erase(0, 1);
+ }
+ auto entryType = BOMFSObjectType(fsObject);
+
+ switch (entryType) {
+ case BOMFileType: {
+ MBTag tag;
+ auto tagCount = MBEntryGetNumberOfProjectTags(entry);
+
+ if (!BOMFSObjectIsBinaryObject(fsObject))
+ break;
+
+ if (tagCount == 0) {
+ break;
+ } else if (tagCount == 1) {
+ MBEntryGetProjectTags(entry, &tag);
+ } else {
+ MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
+ MBEntryGetProjectTags(entry, tags);
+
+ //Sigh, we can have duplicate entries for the same tag, so build a set to work with
+ std::set<std::string> tagStrs;
+ std::map<std::string, MBTag> tagStrMap;
+ for (auto i = 0; i < tagCount; ++i) {
+ tagStrs.insert(MBMetabomGetProjectForTag(metabom, tags[i]));
+ tagStrMap.insert(std::make_pair(MBMetabomGetProjectForTag(metabom, tags[i]), tags[i]));
+ }
+
+ if (tagStrs.size() > 1) {
+ std::string projects;
+ for (const auto& tagStr : tagStrs) {
+ if (!projects.empty())
+ projects += ", ";
+
+ projects += "'" + tagStr + "'";
+ }
+ warning("Bom entry '%s' is claimed by multiple projects: %s, taking first entry", entryPath.c_str(), projects.c_str());
+ }
+ tag = tagStrMap[*tagStrs.begin()];
+ free(tags);
+ }
+
+ std::string projectName = MBMetabomGetProjectForTag(metabom, tag);
+
+ // FIXME we need to actually walk down the searchpaths
+ auto project = projects.find(projectName);
+ if (project == projects.end())
+ break;
+ if (project->second.sources.size() == 0)
+ break;
+ std::string projectPath = project->second.sources[0];
+ std::map<std::string, MachOProxy*> proxies;
+
+ for (const auto& overlay : overlays) {
+ proxies = MachOProxy::findDylibInfo(overlay + "/" + entryPath);
+ if (proxies.size() > 0)
+ break;
+ }
+
+ if (proxies.size() == 0) {
+ proxies = MachOProxy::findDylibInfo(projectPath + "/" + entryPath);
+ }
+
+ for (auto& proxy : proxies) {
+ assert(proxy.second != nullptr);
+ if (proxy.second->isExecutable()) {
+ architectureFiles[proxy.first].executables.insert(std::make_pair(entryPath, File(proxy.second)));
+ continue;
+ }
+ if (!proxy.second->isDylib())
+ continue;
+ assert(proxy.second->installName != "");
+ proxy.second->addAlias(entryPath);
+ architectureFiles[proxy.first].dylibs.insert(
+ std::make_pair(proxy.second->installName, File(proxy.second)));
+ auto tagCount = MBEntryGetNumberOfPackageTags(entry);
+ MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
+ MBEntryGetPackageTags(entry, tags);
+ std::set<std::string> tagStrs;
+
+ for (auto i = 0; i < tagCount; ++i) {
+ tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i]));
+ }
+
+ for (const auto& tagStr : tagStrs) {
+ // Does the configuration exist
+ auto configuration = metabomTagMap.find(tagStr);
+ if (configuration == metabomTagMap.end())
+ continue;
+
+ auto restrictions = metabomRestrictedTagMap.find(configuration->second);
+ if (restrictions != metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, tagStrs)) {
+ configurations[configuration->second].restrictedInstallnames.insert(proxy.second->installName);
+ }
+ // Is the configuration excluded
+ auto exclusions = metabomExcludeTagMap.find(configuration->second);
+ if (exclusions != metabomExcludeTagMap.end() && !is_disjoint(exclusions->second, tagStrs))
+ continue;
+
+ if ([manifestDict[@"configurations"][cppToObjStr(configuration->second)][@"architectures"]
+ containsObject:cppToObjStr(proxy.first)]) {
+ configurations[configuration->second.c_str()].architectures[proxy.first].anchors.push_back(
+ proxy.second->installName);
+ }
+ }
+
+ free(tags);
+ }
+ } break;
+ case BOMSymlinkType: {
+ if (!has_prefix(entryPath, "/usr/lib/") && !has_prefix(entryPath, "/System/Library/"))
+ break;
+ const char* target = BOMFSObjectSymlinkTarget(fsObject);
+ if (target) {
+ symlinks[entryPath] = effectivePath(entryPath, target);
+ }
+ } break;
+ case BOMDirectoryType: {
+ if (!has_prefix(entryPath, "/usr/lib/") && !has_prefix(entryPath, "/System/Library/"))
+ break;
+ directories.insert(entryPath);
+ } break;
+ default:
+ break;
+ }
+ }
+
+ MBIteratorFree(metabomEnumerator);
+ MBMetabomFree(metabom);
+
+ dispatch_queue_t symlinkQueue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, NULL);
+ dispatch_group_t symlinkGroup = dispatch_group_create();
+
+ for (auto& fileSet : architectureFiles) {
+ cacheBuilderDispatchGroupAsync(symlinkGroup, symlinkQueue, [&] {
+ for (auto& file : fileSet.second.dylibs) {
+ bool aliasAdded = true;
+ auto proxy = file.second.proxy;
+
+ while (aliasAdded) {
+ aliasAdded = false;
+
+ for (auto& symlink : symlinks) {
+ std::set<std::string> newAliases;
+ auto alias = checkSymlink(proxy->installName, symlink, directories);
+ if (alias != "") {
+ newAliases.insert(alias);
+ }
+
+ for (auto& existingAlias : proxy->installNameAliases) {
+ alias = checkSymlink(existingAlias, symlink, directories);
+ if (alias != "") {
+ newAliases.insert(alias);
+ }
+ }
+
+ for (auto& alias : newAliases) {
+ if (proxy->addAlias(alias)) {
+ aliasAdded = true;
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+ dispatch_group_wait(symlinkGroup, DISPATCH_TIME_FOREVER);
+
+ for (auto& fileSet : architectureFiles) {
+ for (auto& file : fileSet.second.dylibs) {
+ auto proxy = file.second.proxy;
+
+ for (const auto& dependency : proxy->dependencies) {
+ auto dependencyProxy = dylibProxy(dependency, fileSet.first);
+ if (dependencyProxy == nullptr)
+ break;
+
+ dependencyProxy->dependents.insert(proxy->installName);
+ }
+ }
+ }
+}
+#endif
+
+void Manifest::calculateClosure( bool enforceRootless ) {
+ rootless = enforceRootless;
+
+ for ( auto& config : configurations ) {
+ for ( auto& arch : config.second.architectures ) {
+ calculateClosure( config.first, arch.first );
+ }
+ }
+}
+
+Manifest::File* Manifest::dylibForInstallName( const std::string& installname, const std::string& arch ) {
+ auto archIter = architectureFiles.find( arch );
+ if ( archIter == architectureFiles.end() ) return nullptr;
+
+ auto& files = archIter->second.dylibs;
+ auto dylibIterator = files.find( installname );
+
+ if ( dylibIterator != files.end() ) return &dylibIterator->second;
+
+ for ( auto& candidate : files ) {
+ if ( candidate.second.proxy->installNameAliases.count( installname ) > 0 ) {
+ dylibIterator = files.find( candidate.first );
+ return &dylibIterator->second;
+ }
+ }
+ // Check if we can fallback to an interworkable architecture
+ std::string fallbackArchStr = fallbackArchStringForArchString( arch );
+ if ( !fallbackArchStr.empty() ) {
+ return dylibForInstallName( installname, fallbackArchStr );
+ }
+
+ return nullptr;
+}
+
+
+MachOProxy* Manifest::dylibProxy( const std::string& installname, const std::string& arch ) {
+ auto dylib = dylibForInstallName( installname, arch );
+
+ if ( dylib != nullptr ) {
+ assert( dylib->proxy != nullptr );
+ return dylib->proxy;
+ }
+
+ return nullptr;
+}
+
+bool
+Manifest::sameContentsAsCacheAtPath(const std::string& configuration, const std::string& architecture, const std::string& path) const {
+ __block std::set<std::pair<std::string, std::array<char, 16>>> cacheDylibs;
+ std::set<std::pair<std::string, std::array<char, 16>>> manifestDylibs;
+ struct stat statbuf;
+ if ( ::stat(path.c_str(), &statbuf) == -1 ) {
+ // <rdar://problem/25912438> don't warn if there is no existing cache file
+ if ( errno != ENOENT )
+ warning("stat() failed for dyld shared cache at %s, errno=%d", path.c_str(), errno);
+ return false;
+ }
+
+ int cache_fd = ::open(path.c_str(), O_RDONLY);
+ if ( cache_fd < 0 ) {
+ warning("open() failed for shared cache file at %s, errno=%d", path.c_str(), errno);
+ return false;
+ }
+
+ const void *mappedCache = ::mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
+ if (mappedCache == MAP_FAILED) {
+ ::close(cache_fd);
+ warning("mmap() for shared cache at %s failed, errno=%d", path.c_str(), errno);
+ return false;
+ }
+ ::close(cache_fd);
+
+ if (configurations.count(configuration) == 0
+ || configurations.find(configuration)->second.architectures.count(architecture) == 0)
+ return false;
+
+ for (auto& dylib : configurations.find(configuration)->second.architectures.find(architecture)->second.results.dylibs) {
+ if ( dylib.second.included == true) {
+ std::pair<std::string, std::array<char, 16>> dylibPair;
+ dylibPair.first = dylib.first;
+ bcopy((const void *)&dylib.second.uuid[0], &dylibPair.second[0], sizeof(uuid_t));
+ manifestDylibs.insert(dylibPair);
+ auto file = architectureFiles.find(architecture)->second.dylibs.find(dylib.first);
+ if (file != architectureFiles.find(architecture)->second.dylibs.end()) {
+ for ( auto& alias : file->second.proxy->installNameAliases ) {
+ std::pair<std::string, std::array<char, 16>> aliasPair;
+ aliasPair.first = alias;
+ bcopy((const void *)&dylib.second.uuid[0], &aliasPair.second[0], sizeof(uuid_t));
+ manifestDylibs.insert(aliasPair);
+ }
+ }
+ }
+ }
+
+ (void)dyld_shared_cache_iterate(mappedCache, (uint32_t)statbuf.st_size,
+ ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo){
+ std::pair<std::string, std::array<char, 16>> dylibPair;
+ dylibPair.first = dylibInfo->path;
+ bcopy((const void *)&dylibInfo->uuid[0], &dylibPair.second[0], sizeof(uuid_t));
+ cacheDylibs.insert(dylibPair);
+ });
+
+ return (manifestDylibs == cacheDylibs);
+}
+
+void Manifest::removeDylib( MachOProxy* proxy, const std::string& reason, const std::string& configuration,
+ const std::string& architecture, std::unordered_set<std::string>& processedInstallnames ) {
+ auto configIter = configurations.find( configuration );
+ if ( configIter == configurations.end() ) return;
+ auto archIter = configIter->second.architectures.find( architecture );
+ if ( archIter == configIter->second.architectures.end() ) return;
+ auto& archManifest = archIter->second;
+
+ if ( archManifest.results.dylibs.count( proxy->installName ) == 0 ) {
+ bcopy( &proxy->uuid[0], &archManifest.results.dylibs[proxy->installName].uuid[0], sizeof( uuid_t ) );
+ processedInstallnames.insert( proxy->installName );
+ }
+ archManifest.results.dylibs[proxy->installName].exclude( reason );
+
+ processedInstallnames.insert( proxy->installName );
+ for ( auto& alias : proxy->installNameAliases ) {
+ processedInstallnames.insert( alias );
+ }
+
+ for ( const auto& dependent : proxy->dependents ) {
+ auto dependentProxy = dylibProxy( dependent, architecture );
+ auto dependentResultIter = archManifest.results.dylibs.find( dependentProxy->installName );
+ if ( dependentProxy &&
+ ( dependentResultIter == archManifest.results.dylibs.end() || dependentResultIter->second.included == true ) ) {
+ removeDylib( dependentProxy, "Missing dependency: " + proxy->installName, configuration, architecture,
+ processedInstallnames );
+ }
+ }
+}
+
+MachOProxy* Manifest::removeLargestLeafDylib( const std::string& configuration, const std::string& architecture ) {
+ std::set<std::string> activeInstallnames;
+ std::set<MachOProxy*> leafDylibs;
+
+ auto configIter = configurations.find( configuration );
+ if ( configIter == configurations.end() ) terminate( "Internal error" );
+ ;
+ auto archIter = configIter->second.architectures.find( architecture );
+ if ( archIter == configIter->second.architectures.end() ) terminate( "Internal error" );
+ ;
+ for ( const auto& dylibInfo : archIter->second.results.dylibs ) {
+ if ( dylibInfo.second.included ) {
+ activeInstallnames.insert( dylibInfo.first );
+ }
+ }
+ for ( const auto& installname : activeInstallnames ) {
+ auto dylib = dylibProxy( installname, architecture );
+ bool dependents = false;
+ for ( const auto& depedent : dylib->dependents ) {
+ if ( depedent != dylib->installName && activeInstallnames.count( depedent ) ) {
+ dependents = true;
+ break;
+ }
+ }
+ if ( !dependents ) {
+ leafDylibs.insert( dylib );
+ }
+ }
+ if ( leafDylibs.empty() ) {
+ terminate( "No leaf dylibs to evict" );
+ }
+ MachOProxy* largestLeafDylib = nullptr;
+ for ( const auto& dylib : leafDylibs ) {
+ if ( largestLeafDylib == nullptr || dylib->fileSize > largestLeafDylib->fileSize ) {
+ largestLeafDylib = dylib;
+ }
+ }
+ std::unordered_set<std::string> empty;
+ removeDylib( largestLeafDylib, "VM space overflow", configuration, architecture, empty );
+ return largestLeafDylib;
+}
+
+static void recursiveInvalidate(const std::string& invalidName, std::unordered_map<std::string, std::unordered_set<std::string>>& usesOf, std::unordered_set<std::string>& unusableInstallNames)
+{
+ if ( unusableInstallNames.count(invalidName) )
+ return;
+ unusableInstallNames.insert(invalidName);
+ for (const std::string& name : usesOf[invalidName] ) {
+ recursiveInvalidate(name, usesOf, unusableInstallNames);
+ }
+}
+
+void Manifest::pruneClosure()
+{
+ for (auto& config : configurations) {
+ for (auto& arch : config.second.architectures) {
+ pruneClosure(config.first, arch.first);
+ }
+ }
+}
+
+void Manifest::pruneClosure(const std::string& configuration, const std::string& architecture)
+{
+ auto configIter = configurations.find(configuration);
+ if ( configIter == configurations.end() )
+ return;
+ auto archIter = configIter->second.architectures.find(architecture);
+ if ( archIter == configIter->second.architectures.end() )
+ return;
+ auto& archManifest = archIter->second;
+
+ // build reverse dependency map and list of excluded dylibs
+ std::unordered_map<std::string, std::unordered_set<std::string>> reverseDep;
+ std::unordered_set<std::string> unusableStart;
+ for (const auto& dylib : archManifest.results.dylibs) {
+ const std::string dylibInstallName = dylib.first;
+ if ( dylib.second.included ) {
+ if ( MachOProxy* proxy = dylibProxy(dylibInstallName, architecture) ) {
+ for (const std::string& dependentPath : proxy->dependencies) {
+ reverseDep[dependentPath].insert(dylibInstallName);
+ }
+ }
+ }
+ else {
+ unusableStart.insert(dylibInstallName);
+ }
+ }
+
+ // mark unusable, all dylibs depending on the initially unusable dylibs
+ std::unordered_set<std::string> newUnusable;
+ for (const std::string& unusable : unusableStart) {
+ recursiveInvalidate(unusable, reverseDep, newUnusable);
+ }
+
+ // remove unusable dylibs from manifest
+ std::unordered_set<std::string> dummy;
+ for (const std::string& unusable : newUnusable) {
+ if ( MachOProxy* proxy = dylibProxy(unusable, architecture) )
+ removeDylib(proxy, "Missing dependency: " + unusable, configuration, architecture, dummy);
+ warning("can't use: %s because dependent dylib cannot be used", unusable.c_str());
+ }
+}
+
+void Manifest::calculateClosure( const std::string& configuration, const std::string& architecture ) {
+ auto& archManifest = configurations[configuration].architectures[architecture];
+ auto archFileIter = architectureFiles.find( architecture );
+ assert( archFileIter != architectureFiles.end() );
+ auto files = archFileIter->second.dylibs;
+
+ std::unordered_set<std::string> newInstallnames;
+
+ for ( auto& anchor : archManifest.anchors ) {
+ newInstallnames.insert(anchor.installname);
+ }
+
+ std::unordered_set<std::string> processedInstallnames;
+
+ while (!newInstallnames.empty()) {
+ std::unordered_set<std::string> installnamesToProcess = newInstallnames;
+ newInstallnames.clear();
+
+ for (const std::string& installname : installnamesToProcess) {
+ if (processedInstallnames.count(installname) > 0) {
+ continue;
+ }
+
+ auto proxy = dylibProxy( installname, architecture );
+
+ if ( proxy == nullptr ) {
+ // No path
+ archManifest.results.dylibs[installname].exclude( "Could not find file for install name" );
+ warning("Could not find file for install name (%s)", installname.c_str());
+ continue;
+ }
+
+ if (configurations[configuration].restrictedInstallnames.count(installname) != 0) {
+ removeDylib(proxy, "Dylib '" + installname + "' removed due to explict restriction", configuration, architecture,
+ processedInstallnames);
+ continue;
+ }
+ // Validate we have all are depedencies
+ for ( const auto& dependency : proxy->dependencies ) {
+ if ( !dylibProxy( dependency, architecture ) ) {
+ removeDylib( proxy, "Missing dependency: " + dependency, configuration, architecture,
+ processedInstallnames );
+ break;
+ }
+ }
+
+ // assert(info->installName == installname);
+ if ( archManifest.results.dylibs.count( proxy->installName ) == 0 ) {
+ bcopy( &proxy->uuid[0], &archManifest.results.dylibs[proxy->installName].uuid[0], sizeof( uuid_t ) );
+ processedInstallnames.insert( proxy->installName );
+
+ auto fileIter = files.find( proxy->installName );
+ if ( fileIter != files.end() ) {
+ for ( auto& aliasName : fileIter->second.proxy->installNameAliases ) {
+ processedInstallnames.insert( aliasName );
+ }
+ }
+ }
+
+ for ( const auto& dependency : proxy->dependencies ) {
+ if ( processedInstallnames.count( dependency ) == 0 ) {
+ newInstallnames.insert( dependency );
+ }
+ }
+ }
+ }
+}
+
+void Manifest::write( const std::string& path ) {
+ if ( path.empty() ) return;
+
+ NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
+ NSMutableDictionary* projectDict = [[NSMutableDictionary alloc] init];
+ NSMutableDictionary* configurationsDict = [[NSMutableDictionary alloc] init];
+ NSMutableDictionary* resultsDict = [[NSMutableDictionary alloc] init];
+
+ cacheDict[@"manifest-version"] = @( manifest_version );
+ cacheDict[@"build"] = cppToObjStr( build );
+ cacheDict[@"dylibOrderFile"] = cppToObjStr( dylibOrderFile );
+ cacheDict[@"dirtyDataOrderFile"] = cppToObjStr( dirtyDataOrderFile );
+ cacheDict[@"metabomFile"] = cppToObjStr( metabomFile );
+
+ cacheDict[@"projects"] = projectDict;
+ cacheDict[@"results"] = resultsDict;
+ cacheDict[@"configurations"] = configurationsDict;
+
+ for ( const auto& project : projects ) {
+ NSMutableArray* sources = [[NSMutableArray alloc] init];
+
+ for ( const auto& source : project.second.sources ) {
+ [sources addObject:cppToObjStr( source )];
+ }
+
+ projectDict[cppToObjStr( project.first )] = sources;
+ }
+
+ for ( auto& configuration : configurations ) {
+ NSMutableArray* archArray = [[NSMutableArray alloc] init];
+ for ( auto& arch : configuration.second.architectures ) {
+ [archArray addObject:cppToObjStr( arch.first )];
+ }
+
+ NSMutableArray* excludeTags = [[NSMutableArray alloc] init];
+ for ( const auto& excludeTag : configuration.second.metabomExcludeTags ) {
+ [excludeTags addObject:cppToObjStr( excludeTag )];
+ }
+
+ configurationsDict[cppToObjStr( configuration.first )] = @{
+ @"platformName" : cppToObjStr( configuration.second.platformName ),
+ @"metabomTag" : cppToObjStr( configuration.second.metabomTag ),
+ @"metabomExcludeTags" : excludeTags,
+ @"architectures" : archArray
+ };
+ }
+
+ for ( auto& configuration : configurations ) {
+ NSMutableDictionary* archResultsDict = [[NSMutableDictionary alloc] init];
+ for ( auto& arch : configuration.second.architectures ) {
+ NSMutableDictionary* dylibsDict = [[NSMutableDictionary alloc] init];
+ NSMutableArray* warningsArray = [[NSMutableArray alloc] init];
+ NSMutableDictionary* devRegionsDict = [[NSMutableDictionary alloc] init];
+ NSMutableDictionary* prodRegionsDict = [[NSMutableDictionary alloc] init];
+ NSString *prodCDHash = cppToObjStr(arch.second.results.productionCache.cdHash);
+ NSString *devCDHash = cppToObjStr(arch.second.results.developmentCache.cdHash);
+
+ for ( auto& dylib : arch.second.results.dylibs ) {
+ NSMutableDictionary* dylibDict = [[NSMutableDictionary alloc] init];
+ if ( dylib.second.included ) {
+ NSMutableDictionary* segments = [[NSMutableDictionary alloc] init];
+ dylibDict[@"included"] = @YES;
+ for ( auto& segment : dylib.second.segments ) {
+ segments[cppToObjStr( segment.name )] =
+ @{ @"startAddr" : @( segment.startAddr ),
+ @"endAddr" : @( segment.endAddr ) };
+ }
+ dylibDict[@"segments"] = segments;
+ } else {
+ dylibDict[@"included"] = @NO;
+ dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo);
+ }
+ dylibsDict[cppToObjStr( dylib.first )] = dylibDict;
+ }
+
+ for ( auto& region : arch.second.results.developmentCache.regions ) {
+ devRegionsDict[cppToObjStr( region.name )] =
+ @{ @"startAddr" : @( region.startAddr ),
+ @"endAddr" : @( region.endAddr ) };
+ }
+
+ for ( auto& region : arch.second.results.productionCache.regions ) {
+ prodRegionsDict[cppToObjStr( region.name )] =
+ @{ @"startAddr" : @( region.startAddr ),
+ @"endAddr" : @( region.endAddr ) };
+ }
+
+ for ( auto& warning : arch.second.results.warnings ) {
+ [warningsArray addObject:cppToObjStr( warning )];
+ }
+
+ BOOL built = arch.second.results.failure.empty();
+ archResultsDict[cppToObjStr( arch.first )] = @{
+ @"dylibs" : dylibsDict,
+ @"built" : @( built ),
+ @"failure" : cppToObjStr( arch.second.results.failure ),
+ @"productionCache" : @{@"cdhash" : prodCDHash, @"regions" : prodRegionsDict},
+ @"developmentCache" : @{@"cdhash" : devCDHash, @"regions" : devRegionsDict},
+ @"warnings" : warningsArray
+ };
+ }
+ resultsDict[cppToObjStr( configuration.first )] = archResultsDict;
+ }
+
+ NSError* error = nil;
+ NSData *outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
+ format:NSPropertyListBinaryFormat_v1_0
+ options:0
+ error:&error];
+ (void)[outData writeToFile:cppToObjStr(path) atomically:YES];
+}
--- /dev/null
+//
+// MultiCacheBuilder.h
+// dyld
+//
+// Created by Louis Gerbarg on 6/16/15.
+//
+//
+
+#ifndef MultiCacheBuilder_h
+#define MultiCacheBuilder_h
+
+#include <dispatch/dispatch.h>
+
+#include "Manifest.h"
+
+#include "mega-dylib-utils.h"
+#include <stdlib.h>
+
+typedef struct _BOMBom* BOMBom;
+
+struct MultiCacheBuilder {
+ dispatch_semaphore_t _concurrencyLimitingSemaphore;
+ dispatch_semaphore_t _writeLimitingSemaphore;
+ dispatch_queue_t _writeQueue;
+ dispatch_group_t _writeGroup;
+ dispatch_queue_t _buildQueue;
+ Manifest& _manifest;
+
+ uint64_t _filesWritten = 0;
+ uint64_t _bytesWritten = 0;
+ const bool _bniMode;
+ const bool _skipWrites;
+ const bool _skipBuilds;
+ const bool _buildRoot;
+ const bool _enforceRootless;
+
+ MultiCacheBuilder(Manifest& manifest, bool BNI = false, bool SW = false, bool buildRoot = false, bool skipBuilds = false, bool enforceRootless = false);
+
+ void buildCaches(std::string masterDstRoot);
+
+ void logStats();
+private:
+ void runOnManifestConcurrently(std::function<void(const std::string configuration, const std::string architecture)> lambda);
+ void buildCache(const std::string cachePath, const std::set<std::string> configurations, const std::string architecture, bool development);
+ void write_cache(std::string cachePath, const std::set<std::string>& configurations, const std::string& architecture, std::shared_ptr<SharedCache> cache, bool developmentCache);
+};
+
+#endif /* MultiCacheBuilder_h */
--- /dev/null
+//
+// SharedCacheBuilder.m
+// dyld
+//
+// Created by Louis Gerbarg on 6/15/15.
+//
+//
+
+#include <CommonCrypto/CommonCrypto.h>
+#include <Bom/Bom.h>
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <pthread.h>
+#include <mach/mach.h>
+#include <unistd.h>
+
+#include <cstring>
+
+#include <array>
+#include <sstream>
+#include <iomanip> // std::setfill, std::setw
+#include "mega-dylib-utils.h"
+#include "Logging.h"
+
+#include "MultiCacheBuilder.h"
+
+
+namespace {
+#if BOM_SUPPORT
+void insertDirInBom( const std::string& path, const std::string& name, BOMBom bom ) {
+ std::string fullPath = path + "/" + name;
+ BOMFSObject fso = BOMFSObjectNew( BOMDirectoryType );
+ BOMFSObjectSetFlags( fso, B_PATHONLY );
+ BOMFSObjectSetPathName( fso, fullPath.c_str(), true );
+ BOMFSObjectSetShortName( fso, name.c_str(), true );
+ (void)BOMBomInsertFSObject( bom, fso, false );
+ BOMFSObjectFree( fso );
+}
+
+void insertFileInBom( const std::string& path, const std::string& name, BOMBom bom ) {
+ std::string fullPath = path + "/" + name;
+ BOMFSObject fso = BOMFSObjectNew( BOMFileType );
+ BOMFSObjectSetFlags( fso, B_PATHONLY );
+ BOMFSObjectSetPathName( fso, fullPath.c_str(), true );
+ BOMFSObjectSetShortName( fso, name.c_str(), true );
+ (void)BOMBomInsertFSObject( bom, fso, false );
+ BOMFSObjectFree( fso );
+}
+
+void insertCacheDirInBom( BOMBom bom ) {
+ BOMFSObject fso = BOMFSObjectNew( BOMDirectoryType );
+ BOMFSObjectSetFlags( fso, B_PATHONLY );
+ BOMFSObjectSetPathName( fso, ".", true );
+ BOMFSObjectSetShortName( fso, ".", true );
+ (void)BOMBomInsertFSObject( bom, fso, false );
+ BOMFSObjectFree( fso );
+ insertDirInBom( ".", "System", bom );
+ insertDirInBom( "./System", "Library", bom );
+ insertDirInBom( "./System/Library", "Caches", bom );
+ insertDirInBom( "./System/Library/Caches", "com.apple.dyld", bom );
+}
+#endif /* BOM_SUPPORT */
+}
+
+MultiCacheBuilder::MultiCacheBuilder(Manifest& manifest, bool BNI, bool SW, bool buildRoot, bool skipBuilds, bool enforceRootles)
+ : _manifest(manifest), _bniMode(BNI), _skipWrites(SW), _buildRoot(buildRoot), _skipBuilds(skipBuilds), _enforceRootless(enforceRootles),
+ _writeQueue(dispatch_queue_create("com.apple.dyld.cache.writeout",
+ dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
+ QOS_CLASS_USER_INITIATED, 0))),
+ _writeGroup(dispatch_group_create()),
+ _buildQueue(dispatch_queue_create("com.apple.dyld.cache.multi-build", DISPATCH_QUEUE_CONCURRENT)) {
+ uint64_t thread_count;
+ uint64_t ram_size;
+
+ size_t len = sizeof(thread_count);
+ sysctlbyname ("hw.logicalcpu",&thread_count,&len,NULL,0);
+ len = sizeof(ram_size);
+ sysctlbyname ("hw.memsize",&ram_size,&len,NULL,0);
+
+ uint64_t buildCount = MIN((ram_size/(1024*1024*1024)/2), thread_count);
+ uint64_t writerCount = MAX((ram_size/((uint64_t)2*1024*1024*1024)) - buildCount, 1);
+
+ _buildQueue = dispatch_queue_create("com.apple.dyld.cache.build", DISPATCH_QUEUE_CONCURRENT);
+ _concurrencyLimitingSemaphore = dispatch_semaphore_create(buildCount);
+ _writeLimitingSemaphore = dispatch_semaphore_create(writerCount);
+
+ if ( _bniMode ) {
+ log("Running: %llu threads", buildCount);
+ log("Queuing: %llu writers", writerCount);
+ }
+}
+
+void MultiCacheBuilder::write_cache(std::string cachePath, const std::set<std::string>& configurations, const std::string& architecture, std::shared_ptr<SharedCache> cache, bool developmentCache)
+{
+ //FIXME
+ dispatch_semaphore_wait(_writeLimitingSemaphore, DISPATCH_TIME_FOREVER);
+ dispatch_group_enter(_writeGroup);
+ cacheBuilderDispatchAsync(_writeQueue, [=] {
+ if (!_skipWrites) {
+ verboseLog("Queuing write out: %s", cachePath.c_str());
+
+ //Turn off file caching since we won't read it back
+ //(void)fcntl(fd, F_NOCACHE, 1);
+ // We should do this after the cache write, but that would involve copying the path string
+ std::string tempPath = cachePath;
+ cache->writeCacheMapFile(cachePath + ".map");
+ char tempEXT[] = ".XXXXXX";
+ mktemp(tempEXT);
+ tempPath += tempEXT;
+
+ int fd = ::open(tempPath.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644);
+ if (fd == -1) {
+ dispatch_group_leave(_writeGroup);
+ dispatch_semaphore_signal(_writeLimitingSemaphore);
+ terminate("can't create temp file for %s, errnor=%d (%s)", cachePath.c_str(), errno, strerror(errno));
+ }
+
+ if (isProtectedBySIP(tempPath, fd) != _enforceRootless) {
+ ::close(fd);
+ ::unlink(tempPath.c_str());
+ dispatch_group_leave(_writeGroup);
+ dispatch_semaphore_signal(_writeLimitingSemaphore);
+ terminate("SIP protection of output cache file changed (%s)", cachePath.c_str());
+ }
+
+ ssize_t writtenSize = pwrite(fd, cache->buffer().get(), cache->fileSize(), 0);
+ if (writtenSize != cache->fileSize()) {
+ ::close(fd);
+ ::unlink(tempPath.c_str());
+ dispatch_group_leave(_writeGroup);
+ dispatch_semaphore_signal(_writeLimitingSemaphore);
+ terminate("write() failure creating cache file, requested %lld, wrote %ld, errno=%d (%s)", cache->fileSize(), writtenSize, errno, strerror(errno));
+ }
+
+ ::close(fd);
+
+ if (rename(tempPath.c_str(), cachePath.c_str()) != 0) {
+ dispatch_group_leave(_writeGroup);
+ dispatch_semaphore_signal(_writeLimitingSemaphore);
+ terminate("move() failure creating cache file, errno=%d (%s)", errno, strerror(errno));
+ }
+ if (_bniMode)
+ log("Wrote out: %s", cachePath.c_str());
+ } else {
+ log("Skipped: %s", cachePath.c_str());
+ }
+ _filesWritten++;
+ _bytesWritten += cache->fileSize();
+ dispatch_group_leave(_writeGroup);
+ dispatch_semaphore_signal(_writeLimitingSemaphore);
+ });
+}
+
+//FIXME (make development a type)
+void MultiCacheBuilder::buildCache(const std::string cachePath, const std::set<std::string> configurations, const std::string architecture, bool development)
+{
+ auto& configResults = _manifest.configurations[*configurations.begin()].architectures[architecture].results.dylibs;
+
+ if ( _skipBuilds ) {
+ log( "Build Skipped" );
+
+ for ( auto& config : configurations ) {
+ for ( auto& dylib : configResults ) {
+ _manifest.configurations[config].architectures[architecture].results.dylibs[dylib.first].exclude(
+ "All dylibs excluded" );
+ }
+ }
+ return;
+ }
+
+ Manifest::Architecture arch;
+ std::vector<std::unique_ptr<MachOProxy>> dylibs;
+ std::vector<std::string> emptyList;
+ std::shared_ptr<SharedCache> cache = std::make_shared<SharedCache>(_manifest, *configurations.begin(), architecture);
+
+ for (auto& config : configurations) {
+ auto& results = _manifest.configurations[config].architectures[architecture].results.dylibs;
+
+ for (auto& dylib : configResults) {
+ if (dylib.second.included == false
+ && results.count(dylib.first)
+ && results[dylib.first].included == true) {
+ results[dylib.first].exclude(dylib.second.exclusionInfo);
+ }
+ }
+ }
+
+ if (development) {
+ cache->buildForDevelopment(cachePath);
+ } else {
+ cache->buildForProduction(cachePath);
+ }
+
+ std::vector<uint64_t> regionStartAddresses;
+ std::vector<uint64_t> regionSizes;
+ std::vector<uint64_t> regionFileOffsets;
+
+ cache->forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ regionStartAddresses.push_back(vmAddr);
+ regionSizes.push_back(size);
+ regionFileOffsets.push_back((uint8_t*)content - (uint8_t*)cache->buffer().get());
+ const char* prot = "RW";
+ if ( permissions == (VM_PROT_EXECUTE|VM_PROT_READ) )
+ prot = "EX";
+ else if ( permissions == VM_PROT_READ )
+ prot = "RO";
+ for (auto& config : configurations) {
+ if (development) {
+ _manifest.configurations[config].architectures[architecture].results.developmentCache.regions.push_back({prot, vmAddr,vmAddr+size });
+ } else {
+ _manifest.configurations[config].architectures[architecture].results.productionCache.regions.push_back({prot, vmAddr,vmAddr+size });
+ }
+ }
+ });
+
+ cache->forEachImage([&](const void* machHeader, const char* installName, time_t mtime,
+ ino_t inode, const std::vector<MachOProxy::Segment>& segments) {
+ for (auto& seg : segments) {
+ uint64_t vmAddr = 0;
+ for (int i=0; i < regionSizes.size(); ++i) {
+ if ( (seg.fileOffset >= regionFileOffsets[i]) && (seg.fileOffset < (regionFileOffsets[i]+regionSizes[i])) ) {
+ vmAddr = regionStartAddresses[i] + seg.fileOffset - regionFileOffsets[i];
+ }
+ }
+ for (auto& config : configurations) {
+ _manifest.configurations[config].architectures[architecture].results.dylibs[installName].segments.push_back({seg.name, vmAddr, vmAddr+seg.size});
+ if (_manifest.configurations[config].architectures[architecture].results.dylibs[installName].segments.size() == 0) {
+ warning("Attempting to write info for excluded dylib");
+ _manifest.configurations[config].architectures[architecture].results.dylibs[installName].exclude("Internal Error");
+ }
+ }
+ }
+ });
+ if (development) {
+ verboseLog("developement cache size = %llu", cache->fileSize());
+ } else {
+ verboseLog("production cache size = %llu", cache->fileSize());
+ }
+ if ( cache->vmSize()+align(cache->vmSize()/200, sharedRegionRegionAlignment(archForString(architecture))) > sharedRegionRegionSize(archForString(architecture))) {
+ warning("shared cache will not fit in shared regions address space. Overflow amount: %llu",
+ cache->vmSize() + align(cache->vmSize() / 200, sharedRegionRegionAlignment(archForString(architecture))) - sharedRegionRegionSize(archForString(architecture)));
+ return;
+ }
+ write_cache(cachePath, configurations, architecture, cache, development);
+ for (auto& config : configurations) {
+ if (development) {
+ _manifest.configurations[config].architectures[architecture].results.developmentCache.cdHash = cache->cdHashString();
+ }
+ else {
+ _manifest.configurations[config].architectures[architecture].results.productionCache.cdHash = cache->cdHashString();
+ }
+ }
+}
+
+void MultiCacheBuilder::runOnManifestConcurrently(std::function<void(const std::string configuration, const std::string architecture)> lambda)
+{
+ dispatch_group_t runGroup = dispatch_group_create();
+ for (auto& config : _manifest.configurations) {
+ for (auto& architecture : config.second.architectures) {
+ dispatch_semaphore_wait(_concurrencyLimitingSemaphore, DISPATCH_TIME_FOREVER);
+ cacheBuilderDispatchGroupAsync(runGroup, _buildQueue, [&] {
+ WarningTargets targets;
+ targets.first = &_manifest;
+ targets.second.insert(std::make_pair(config.first, architecture.first));
+ auto ctx = std::make_shared<LoggingContext>(config.first + "/" + architecture.first, targets);
+ setLoggingContext(ctx);
+ lambda(config.first, architecture.first);
+ dispatch_semaphore_signal(_concurrencyLimitingSemaphore);
+ });
+ }
+ }
+
+ dispatch_group_wait(runGroup, DISPATCH_TIME_FOREVER);
+}
+
+void MultiCacheBuilder::buildCaches(std::string masterDstRoot) {
+ if (_bniMode) {
+ std::vector<std::set<std::string>> dedupedCacheSets;
+ for (auto& config : _manifest.configurations) {
+ bool dupeFound = false;
+
+ for (auto& cacheSet : dedupedCacheSets) {
+ if (config.second.equivalent(_manifest.configurations[*cacheSet.begin()])) {
+ cacheSet.insert(config.first);
+ dupeFound = true;
+ break;
+ }
+ }
+
+ if (!dupeFound) {
+ std::set<std::string> temp;
+ temp.insert(config.first);
+ dedupedCacheSets.push_back(temp);
+ }
+ }
+
+ for (auto& cacheSet : dedupedCacheSets) {
+ //FIXME we may want to consider moving to hashes of UUID sets
+ std::string setName;
+
+ for (auto &archName : cacheSet) {
+ if (!setName.empty()) {
+ setName += "|";
+ }
+ setName += archName;
+ }
+
+ std::stringstream fileNameStream;
+ std::array<uint8_t, CC_SHA1_DIGEST_LENGTH> digest = {0};
+ CC_SHA1(setName.c_str(), (unsigned int)setName.length(), &digest[0]);
+
+ fileNameStream << std::hex << std::uppercase << std::setfill( '0' );
+ for( int c : digest ) {
+ fileNameStream << std::setw( 2 ) << c;
+ }
+
+ std::string fileName(fileNameStream.str());
+
+ for (auto& config : cacheSet) {
+ if (!_skipWrites) {
+ int err = symlink(("DedupedConfigs/" + fileName).c_str(), (masterDstRoot + "/" + config).c_str());
+ if (err) {
+ warning("Could not create symlink '%s' -> 'DedupedConfigs/%s' (%d)", config.c_str(), fileName.c_str(), err);
+ }
+ }
+ }
+
+ for (auto& arch : _manifest.configurations[*cacheSet.begin()].architectures) {
+ dispatch_semaphore_wait(_concurrencyLimitingSemaphore, DISPATCH_TIME_FOREVER);
+ cacheBuilderDispatchGroupAsync(_writeGroup, _buildQueue, [=] {
+ WarningTargets targets;
+ targets.first = &_manifest;
+ for (auto& config : cacheSet) {
+ targets.second.insert(std::make_pair(config, arch.first));
+ }
+ auto ctx = std::make_shared<LoggingContext>(setName + "/" + arch.first, targets);
+ setLoggingContext(ctx);
+
+ std::string configPath = masterDstRoot + "/DedupedConfigs/" + fileName + "/System/Library/Caches/com.apple.dyld/";
+
+ if (!_skipWrites) {
+ int err = mkpath_np(configPath.c_str(), 0755);
+
+ if (err != 0 && err != EEXIST) {
+ dispatch_semaphore_signal(_concurrencyLimitingSemaphore);
+ terminate("mkpath_np fail: %d", err);
+ }
+ }
+
+ buildCache(configPath + "dyld_shared_cache_" + arch.first + ".development", cacheSet, arch.first, true);
+ buildCache(configPath + "dyld_shared_cache_" + arch.first, cacheSet, arch.first, false);
+ dispatch_semaphore_signal(_concurrencyLimitingSemaphore);
+ });
+ }
+ }
+
+ dispatch_group_wait(_writeGroup, DISPATCH_TIME_FOREVER);
+
+#if BOM_SUPPORT
+ if ( !_skipWrites ) {
+ for ( auto& configuration : _manifest.configurations ) {
+ std::vector<std::string> prodBomPaths;
+ std::vector<std::string> devBomPaths;
+
+ for (auto& arch : configuration.second.architectures) {
+ std::string cachePath = "dyld_shared_cache_" + arch.first;
+ prodBomPaths.push_back(cachePath);
+ cachePath += ".development";
+ devBomPaths.push_back(cachePath);
+ dispatch_group_enter(_writeGroup);
+ cacheBuilderDispatchAsync(_writeQueue, [=] {
+ char buffer[MAXPATHLEN];
+ sprintf(buffer, "%s/Boms/%s.prod.bom", masterDstRoot.c_str(), configuration.first.c_str());
+ BOMBom bom = BOMBomNew(buffer);
+ insertCacheDirInBom(bom);
+ for (auto& path : prodBomPaths) {
+ insertFileInBom("./System/Library/Caches/com.apple.dyld", path, bom);
+ }
+ BOMBomFree(bom);
+
+ sprintf(buffer, "%s/Boms/%s.dev.bom", masterDstRoot.c_str(), configuration.first.c_str());
+ bom = BOMBomNew(buffer);
+ insertCacheDirInBom(bom);
+ for (auto& path : devBomPaths) {
+ insertFileInBom("./System/Library/Caches/com.apple.dyld", path, bom);
+ }
+ BOMBomFree(bom);
+
+ sprintf(buffer, "%s/Boms/%s.full.bom", masterDstRoot.c_str(), configuration.first.c_str());
+ bom = BOMBomNew(buffer);
+ insertCacheDirInBom(bom);
+ for (auto& path : prodBomPaths) {
+ insertFileInBom("./System/Library/Caches/com.apple.dyld", path, bom);
+ }
+ for (auto& path : devBomPaths) {
+ insertFileInBom("./System/Library/Caches/com.apple.dyld", path, bom);
+ }
+ BOMBomFree(bom);
+ dispatch_group_leave(_writeGroup);
+ });
+ }
+ }
+ }
+#endif /* BOM_SUPPORT */
+ } else {
+ runOnManifestConcurrently(
+ [&](const std::string configuration, const std::string architecture) {
+ cacheBuilderDispatchGroupAsync(_writeGroup, _buildQueue, [=] {
+ std::set<std::string> configurations;
+ configurations.insert( configuration );
+ // FIXME hacky, we make implicit assumptions about dev vs non-dev and layout depending on the flags
+ if ( _buildRoot ) {
+ int err = mkpath_np( ( masterDstRoot + "/System/Library/Caches/com.apple.dyld/" ).c_str(), 0755 );
+
+ if ( err != 0 && err != EEXIST ) {
+ terminate( "mkpath_np fail: %d", err );
+ }
+ buildCache(masterDstRoot + "/System/Library/Caches/com.apple.dyld/dyld_shared_cache_" + architecture,
+ configurations, architecture, false);
+ buildCache(masterDstRoot + "/System/Library/Caches/com.apple.dyld/dyld_shared_cache_" + architecture + ".development",
+ configurations, architecture, true);
+ } else {
+ buildCache(masterDstRoot + "/dyld_shared_cache_" + architecture, configurations, architecture, true);
+ }
+ });
+ });
+ dispatch_group_wait(_writeGroup, DISPATCH_TIME_FOREVER);
+ }
+
+ int err = sync_volume_np(masterDstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT);
+ if (err) {
+ warning("Volume sync failed errnor=%d (%s)", err, strerror(err));
+ }
+}
+
+void MultiCacheBuilder::logStats(void) {
+ if ( _bniMode )
+ log("Processed %llu caches (%.2fGB)", _filesWritten, ((float)_bytesWritten)/(1024*1024*1024));
+}
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008-2010 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@
+ */
+
+#define OBJC_IMAGE_SUPPORTS_GC (1<<1)
+#define OBJC_IMAGE_REQUIRES_GC (1<<2)
+
+template <typename P>
+struct objc_image_info {
+ uint32_t version;
+ uint32_t flags;
+
+ uint32_t getFlags() INLINE { return P::E::get32(flags); }
+
+ bool supportsGCFlagSet() INLINE { return getFlags() & OBJC_IMAGE_SUPPORTS_GC; }
+ bool requiresGCFlagSet() INLINE { return getFlags() & OBJC_IMAGE_REQUIRES_GC; }
+
+ void setFlag(uint32_t bits) INLINE { uint32_t old = P::E::get32(flags); P::E::set32(flags, old | bits); }
+ void setOptimizedByDyld() INLINE { setFlag(1<<3); }
+};
+
+template <typename P>
+struct objc_method {
+ uint32_t method_name; // SEL
+ uint32_t method_types; // char *
+ uint32_t method_imp; // IMP
+
+ uint32_t getName() const INLINE { return P::E::get32(method_name); }
+ void setName(uint32_t newName) INLINE { P::E::set32(method_name, newName); }
+};
+
+template <typename P>
+struct objc_method_list {
+ enum { OBJC_FIXED_UP = 1771 };
+ uint32_t obsolete; // struct objc_method_list *
+ uint32_t method_count; // int
+ struct objc_method<P> method_list[0];
+
+ uint32_t getCount() const INLINE { return P::E::get32(method_count); }
+ void setFixedUp(bool fixed) INLINE { P::E::set32(obsolete, fixed ? OBJC_FIXED_UP : 0); }
+};
+
+template <typename P>
+struct objc_class {
+ uint32_t isa; // struct objc_class *
+ uint32_t super_class; // struct objc_class *
+ uint32_t name; // const char *
+ uint32_t version; // long
+ uint32_t info; // long
+ uint32_t instance_size; // long
+ uint32_t ivars; // struct objc_ivar_list *
+ uint32_t methodList; // struct objc_method_list *
+ uint32_t method_cache; // struct objc_cache *
+ uint32_t protocols; // objc_protocol_list *
+ uint32_t ivar_layout; // const char *
+ uint32_t ext; // struct objc_class_ext *
+
+ struct objc_class<P> *getIsa(ContentAccessor* cache) const INLINE { return (struct objc_class<P> *)cache->contentForVMAddr(P::E::get32(isa)); }
+ struct objc_method_list<P> *getMethodList(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(methodList)); }
+};
+
+template <typename P>
+struct objc_category {
+ uint32_t category_name; // char *
+ uint32_t class_name; // char *
+ uint32_t instance_methods; // struct objc_method_list *
+ uint32_t class_methods; // struct objc_method_list *
+ uint32_t protocols; // objc_protocol_list *
+ uint32_t size; // uint32_t
+ uint32_t instance_properties; // struct objc_property_list *
+
+ struct objc_method_list<P> *getInstanceMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(instance_methods)); }
+ struct objc_method_list<P> *getClassMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(class_methods)); }
+};
+
+template <typename P>
+struct objc_symtab {
+ uint32_t sel_ref_cnt; // unsigned long
+ uint32_t refs; // SEL *
+ uint16_t cls_def_cnt; // unsigned short
+ uint16_t cat_def_cnt; // unsigned short
+ uint32_t defs[0]; // void *
+
+ uint16_t getClassCount(void) const INLINE { return P::E::get16(cls_def_cnt); }
+ uint16_t getCategoryCount(void) const INLINE { return P::E::get16(cat_def_cnt); }
+ struct objc_class<P> *getClass(ContentAccessor* cache, int index) const INLINE { return (struct objc_class<P> *)cache->contentForVMAddr(P::E::get32(defs[index])); }
+ struct objc_category<P> *getCategory(ContentAccessor* cache, int index) const INLINE { return (struct objc_category<P> *)cache->contentForVMAddr(P::E::get32(defs[getClassCount() + index])); }
+};
+
+template <typename P>
+struct objc_module {
+ uint32_t version; // unsigned long
+ uint32_t size; // unsigned long
+ uint32_t name; // char*
+ uint32_t symtab; // Symtab
+
+ struct objc_symtab<P> *getSymtab(ContentAccessor* cache) const INLINE { return (struct objc_symtab<P> *)cache->contentForVMAddr(P::E::get32(symtab)); }
+};
+
+template <typename P>
+struct objc_method_description {
+ uint32_t name; // SEL
+ uint32_t types; // char *
+
+ uint32_t getName() const INLINE { return P::E::get32(name); }
+ void setName(uint32_t newName) INLINE { P::E::set32(name, newName); }
+};
+
+template <typename P>
+struct objc_method_description_list {
+ uint32_t count; // int
+ struct objc_method_description<P> list[0];
+
+ uint32_t getCount() const INLINE { return P::E::get32(count); }
+};
+
+template <typename P>
+struct objc_protocol {
+ uint32_t isa; // danger! contains strange values!
+ uint32_t protocol_name; // const char *
+ uint32_t protocol_list; // struct objc_protocol_list
+ uint32_t instance_methods; // struct objc_method_description_list *
+ uint32_t class_methods; // struct objc_method_description_list *
+
+ struct objc_method_description_list<P> *getInstanceMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list<P> *)cache->contentForVMAddr(P::E::get32(instance_methods)); }
+ struct objc_method_description_list<P> *getClassMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list<P> *)cache->contentForVMAddr(P::E::get32(class_methods)); }
+};
+
+
+template <typename P, typename V>
+class LegacySelectorUpdater {
+ typedef typename P::uint_t pint_t;
+
+ static void visitMethodList(objc_method_list<P> *mlist, V& visitor)
+ {
+ for (uint32_t m = 0; m < mlist->getCount(); m++) {
+ pint_t oldValue = mlist->method_list[m].getName();
+ pint_t newValue = visitor.visit(oldValue);
+ mlist->method_list[m].setName((uint32_t)newValue);
+ }
+ mlist->setFixedUp(true);
+ }
+
+ static void visitMethodDescriptionList(objc_method_description_list<P> *mlist, V& visitor)
+ {
+ for (pint_t m = 0; m < mlist->getCount(); m++) {
+ pint_t oldValue = mlist->list[m].getName();
+ pint_t newValue = visitor.visit(oldValue);
+ mlist->list[m].setName((uint32_t)newValue);
+ }
+ }
+
+public:
+
+ static void update(ContentAccessor* cache, const macho_header<P>* header, V& visitor)
+ {
+ ArraySection<P, objc_module<P> >
+ modules(cache, header, "__OBJC", "__module_info");
+ for (uint64_t m = 0; m < modules.count(); m++) {
+ objc_symtab<P> *symtab = modules.get(m).getSymtab(cache);
+ if (!symtab) continue;
+
+ // Method lists in classes
+ for (int c = 0; c < symtab->getClassCount(); c++) {
+ objc_class<P> *cls = symtab->getClass(cache, c);
+ objc_class<P> *isa = cls->getIsa(cache);
+ objc_method_list<P> *mlist;
+ if ((mlist = cls->getMethodList(cache))) {
+ visitMethodList(mlist, visitor);
+ }
+ if ((mlist = isa->getMethodList(cache))) {
+ visitMethodList(mlist, visitor);
+ }
+ }
+
+ // Method lists from categories
+ for (int c = 0; c < symtab->getCategoryCount(); c++) {
+ objc_category<P> *cat = symtab->getCategory(cache, c);
+ objc_method_list<P> *mlist;
+ if ((mlist = cat->getInstanceMethods(cache))) {
+ visitMethodList(mlist, visitor);
+ }
+ if ((mlist = cat->getClassMethods(cache))) {
+ visitMethodList(mlist, visitor);
+ }
+ }
+ }
+
+ // Method description lists from protocols
+ ArraySection<P, objc_protocol<P>>
+ protocols(cache, header, "__OBJC", "__protocol");
+ for (uint64_t p = 0; p < protocols.count(); p++) {
+ objc_protocol<P>& protocol = protocols.get(p);
+ objc_method_description_list<P> *mlist;
+ if ((mlist = protocol.getInstanceMethodDescriptions(cache))) {
+ visitMethodDescriptionList(mlist, visitor);
+ }
+ if ((mlist = protocol.getClassMethodDescriptions(cache))) {
+ visitMethodDescriptionList(mlist, visitor);
+ }
+ }
+
+ // Message refs
+ PointerSection<P, const char *> selrefs(cache, header, "__OBJC", "__message_refs");
+ for (pint_t s = 0; s < selrefs.count(); s++) {
+ pint_t oldValue = selrefs.getVMAddress(s);
+ pint_t newValue = visitor.visit(oldValue);
+ selrefs.setVMAddress(s, newValue);
+ }
+ }
+};
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008-2010 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 <iterator>
+#include <deque>
+#include <set>
+
+// iterate an entsize-based list
+// typedef entsize_iterator<P, type_t<P>, type_list_t<P> > type_iterator;
+template <typename P, typename T, typename Tlist>
+struct entsize_iterator {
+ uint32_t entsize;
+ uint32_t index; // keeping track of this saves a divide in operator-
+ T* current;
+
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef T& reference;
+
+ entsize_iterator() { }
+
+ entsize_iterator(const Tlist& list, uint32_t start = 0)
+ : entsize(list.getEntsize()), index(start), current(&list.get(start))
+ { }
+
+ const entsize_iterator<P,T,Tlist>& operator += (ptrdiff_t count) {
+ current = (T*)((uint8_t *)current + count*entsize);
+ index += count;
+ return *this;
+ }
+ const entsize_iterator<P,T,Tlist>& operator -= (ptrdiff_t count) {
+ current = (T*)((uint8_t *)current - count*entsize);
+ index -= count;
+ return *this;
+ }
+ const entsize_iterator<P,T,Tlist> operator + (ptrdiff_t count) const {
+ return entsize_iterator(*this) += count;
+ }
+ const entsize_iterator<P,T,Tlist> operator - (ptrdiff_t count) const {
+ return entsize_iterator(*this) -= count;
+ }
+
+ entsize_iterator<P,T,Tlist>& operator ++ () { *this += 1; return *this; }
+ entsize_iterator<P,T,Tlist>& operator -- () { *this -= 1; return *this; }
+ entsize_iterator<P,T,Tlist> operator ++ (int) {
+ entsize_iterator<P,T,Tlist> result(*this); *this += 1; return result;
+ }
+ entsize_iterator<P,T,Tlist> operator -- (int) {
+ entsize_iterator<P,T,Tlist> result(*this); *this -= 1; return result;
+ }
+
+ ptrdiff_t operator - (const entsize_iterator<P,T,Tlist>& rhs) const {
+ return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index;
+ }
+
+ T& operator * () { return *current; }
+ T& operator * () const { return *current; }
+ T& operator -> () { return *current; }
+ const T& operator -> () const { return *current; }
+
+ operator T& () const { return *current; }
+
+ bool operator == (const entsize_iterator<P,T,Tlist>& rhs) {
+ return this->current == rhs.current;
+ }
+ bool operator != (const entsize_iterator<P,T,Tlist>& rhs) {
+ return this->current != rhs.current;
+ }
+
+ bool operator < (const entsize_iterator<P,T,Tlist>& rhs) {
+ return this->current < rhs.current;
+ }
+ bool operator > (const entsize_iterator<P,T,Tlist>& rhs) {
+ return this->current > rhs.current;
+ }
+
+
+ static void overwrite(entsize_iterator<P,T,Tlist>& dst, const Tlist* srcList)
+ {
+ entsize_iterator<P,T,Tlist> src;
+ uint32_t ee = srcList->getEntsize();
+ for (src = srcList->begin(); src != srcList->end(); ++src) {
+ memcpy(&*dst, &*src, ee);
+ ++dst;
+ }
+ }
+};
+
+template <typename P>
+class objc_header_info_rw_t {
+
+ typedef typename P::uint_t pint_t;
+
+ pint_t data; // loaded:1, allRealised:1, objc_header_info *:ptr
+
+public:
+ objc_header_info_rw_t(ContentAccessor* cache, const macho_header<P>* mh)
+ : data(0) {
+ }
+};
+
+template <typename P>
+class objc_header_info_ro_t {
+
+ typedef typename P::uint_t pint_t;
+
+ pint_t mhdr_offset; // offset to mach_header or mach_header_64
+ pint_t info_offset; // offset to objc_image_info *
+
+public:
+ objc_header_info_ro_t(ContentAccessor* cache, const macho_header<P>* mh)
+ : mhdr_offset(0), info_offset(0) {
+ P::setP(mhdr_offset, (uint64_t)cache->vmAddrForContent((void*)mh) - (uint64_t)cache->vmAddrForContent(&mhdr_offset));
+ assert(header_vmaddr(cache) == (uint64_t)cache->vmAddrForContent((void*)mh));
+ const macho_section<P>* sect = mh->getSection("__DATA", "__objc_imageinfo");
+ if (sect) {
+ P::setP(info_offset, (uint64_t)sect->addr() - (uint64_t)cache->vmAddrForContent(&info_offset));
+ // set bit in mach_header.flags to tell dyld that this image has objc content
+ macho_header<P>* rwmh = const_cast<macho_header<P>*>(mh);
+ rwmh->set_flags(mh->flags() | MH_HAS_OBJC);
+ }
+ else
+ P::setP(info_offset, - (uint64_t)cache->vmAddrForContent(&info_offset));
+ }
+
+ pint_t header_vmaddr(ContentAccessor* cache) const {
+ return (pint_t)(((uint64_t)cache->vmAddrForContent(&mhdr_offset)) + mhdr_offset);
+ }
+};
+
+
+template <typename P>
+class objc_method_list_t; // forward reference
+
+
+template <typename P>
+class objc_method_t {
+ typedef typename P::uint_t pint_t;
+ pint_t name; // SEL
+ pint_t types; // const char *
+ pint_t imp; // IMP
+ friend class objc_method_list_t<P>;
+public:
+ pint_t getName() const { return (pint_t)P::getP(name); }
+ void setName(pint_t newName) { P::setP(name, newName); }
+
+ struct SortBySELAddress :
+ public std::binary_function<const objc_method_t<P>&,
+ const objc_method_t<P>&, bool>
+ {
+ bool operator() (const objc_method_t<P>& lhs,
+ const objc_method_t<P>& rhs)
+ {
+ return lhs.getName() < rhs.getName();
+ }
+ };
+};
+
+template <typename P>
+class objc_method_list_t {
+ uint32_t entsize;
+ uint32_t count;
+ objc_method_t<P> first;
+
+ void* operator new (size_t, void* buf) { return buf; }
+
+public:
+
+ typedef entsize_iterator<P, objc_method_t<P>, objc_method_list_t<P> > method_iterator;
+
+ uint32_t getCount() const { return P::E::get32(count); }
+
+ uint32_t getEntsize() const {return P::E::get32(entsize)&~(uint32_t)3;}
+
+ objc_method_t<P>& get(uint32_t i) const { return *(objc_method_t<P> *)((uint8_t *)&first + i * getEntsize()); }
+
+ uint32_t byteSize() const {
+ return byteSizeForCount(getCount(), getEntsize());
+ }
+
+ static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_method_t<P>)) {
+ return sizeof(objc_method_list_t<P>) - sizeof(objc_method_t<P>) + c*e;
+ }
+
+ method_iterator begin() { return method_iterator(*this, 0); }
+ method_iterator end() { return method_iterator(*this, getCount()); }
+ const method_iterator begin() const { return method_iterator(*this, 0); }
+ const method_iterator end() const { return method_iterator(*this, getCount()); }
+
+ void setFixedUp() { P::E::set32(entsize, getEntsize() | 3); }
+
+ void getPointers(std::set<void*>& pointersToRemove) {
+ for(method_iterator it = begin(); it != end(); ++it) {
+ objc_method_t<P>& entry = *it;
+ pointersToRemove.insert(&(entry.name));
+ pointersToRemove.insert(&(entry.types));
+ pointersToRemove.insert(&(entry.imp));
+ }
+ }
+
+ static void addPointers(uint8_t* methodList, std::vector<void*>& pointersToAdd) {
+ objc_method_list_t<P>* mlist = (objc_method_list_t<P>*)methodList;
+ for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) {
+ objc_method_t<P>& entry = *it;
+ pointersToAdd.push_back(&(entry.name));
+ pointersToAdd.push_back(&(entry.types));
+ pointersToAdd.push_back(&(entry.imp));
+ }
+ }
+
+ static objc_method_list_t<P>* newMethodList(size_t newCount, uint32_t newEntsize) {
+ void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
+ return new (buf) objc_method_list_t<P>(newCount, newEntsize);
+ }
+
+ void operator delete(void * p) {
+ ::free(p);
+ }
+
+ objc_method_list_t(uint32_t newCount,
+ uint32_t newEntsize = sizeof(objc_method_t<P>))
+ : entsize(newEntsize), count(newCount)
+ { }
+
+private:
+ // use newMethodList instead
+ void* operator new (size_t);
+};
+
+
+template <typename P>
+class objc_ivar_t {
+ typedef typename P::uint_t pint_t;
+
+ pint_t offset; // uint32_t* (uint64_t* on x86_64)
+ pint_t name; // const char*
+ pint_t type; // const char*
+ uint32_t alignment;
+ uint32_t size;
+
+public:
+ const char* getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); }
+
+ bool hasOffset() const { return offset != 0; }
+ uint32_t getOffset(ContentAccessor* cache) const { return P::E::get32(*(uint32_t*)(cache->contentForVMAddr(P::getP(offset)))); }
+ void setOffset(ContentAccessor* cache, uint32_t newOffset) { P::E::set32(*(uint32_t*)(cache->contentForVMAddr(P::getP(offset))), newOffset); }
+
+
+ uint32_t getAlignment() {
+ uint32_t a = P::E::get32(alignment);
+ return (a == (uint32_t)-1) ? sizeof(pint_t) : 1<<a;
+ }
+};
+
+template <typename P>
+class objc_ivar_list_t {
+ typedef typename P::uint_t pint_t;
+ uint32_t entsize;
+ uint32_t count;
+ objc_ivar_t<P> first;
+
+ void* operator new (size_t, void* buf) { return buf; }
+
+public:
+
+ typedef entsize_iterator<P, objc_ivar_t<P>, objc_ivar_list_t<P> > ivar_iterator;
+
+ uint32_t getCount() const { return P::E::get32(count); }
+
+ uint32_t getEntsize() const { return P::E::get32(entsize); }
+
+ objc_ivar_t<P>& get(pint_t i) const { return *(objc_ivar_t<P> *)((uint8_t *)&first + i * P::E::get32(entsize)); }
+
+ uint32_t byteSize() const {
+ return byteSizeForCount(getCount(), getEntsize());
+ }
+
+ static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_ivar_t<P>)) {
+ return sizeof(objc_ivar_list_t<P>) - sizeof(objc_ivar_t<P>) + c*e;
+ }
+
+ ivar_iterator begin() { return ivar_iterator(*this, 0); }
+ ivar_iterator end() { return ivar_iterator(*this, getCount()); }
+ const ivar_iterator begin() const { return ivar_iterator(*this, 0); }
+ const ivar_iterator end() const { return ivar_iterator(*this, getCount()); }
+
+ static objc_ivar_list_t<P>* newIvarList(size_t newCount, uint32_t newEntsize) {
+ void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
+ return new (buf) objc_ivar_list_t<P>(newCount, newEntsize);
+ }
+
+ void operator delete(void * p) {
+ ::free(p);
+ }
+
+ objc_ivar_list_t(uint32_t newCount,
+ uint32_t newEntsize = sizeof(objc_ivar_t<P>))
+ : entsize(newEntsize), count(newCount)
+ { }
+private:
+ // use newIvarList instead
+ void* operator new (size_t);
+};
+
+
+template <typename P> class objc_property_list_t; // forward
+
+template <typename P>
+class objc_property_t {
+ typedef typename P::uint_t pint_t;
+ pint_t name;
+ pint_t attributes;
+ friend class objc_property_list_t<P>;
+public:
+
+ const char * getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); }
+
+ const char * getAttributes(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(attributes)); }
+};
+
+template <typename P>
+class objc_property_list_t {
+ uint32_t entsize;
+ uint32_t count;
+ objc_property_t<P> first;
+
+ void* operator new (size_t, void* buf) { return buf; }
+
+public:
+
+ typedef entsize_iterator<P, objc_property_t<P>, objc_property_list_t<P> > property_iterator;
+
+ uint32_t getCount() const { return P::E::get32(count); }
+
+ uint32_t getEntsize() const { return P::E::get32(entsize); }
+
+ objc_property_t<P>& get(uint32_t i) const { return *(objc_property_t<P> *)((uint8_t *)&first + i * getEntsize()); }
+
+ uint32_t byteSize() const {
+ return byteSizeForCount(getCount(), getEntsize());
+ }
+
+ static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_property_t<P>)) {
+ return sizeof(objc_property_list_t<P>) - sizeof(objc_property_t<P>) + c*e;
+ }
+
+ property_iterator begin() { return property_iterator(*this, 0); }
+ property_iterator end() { return property_iterator(*this, getCount()); }
+ const property_iterator begin() const { return property_iterator(*this, 0); }
+ const property_iterator end() const { return property_iterator(*this, getCount()); }
+
+ void getPointers(std::set<void*>& pointersToRemove) {
+ for(property_iterator it = begin(); it != end(); ++it) {
+ objc_property_t<P>& entry = *it;
+ pointersToRemove.insert(&(entry.name));
+ pointersToRemove.insert(&(entry.attributes));
+ }
+ }
+
+ static void addPointers(uint8_t* propertyList, std::vector<void*>& pointersToAdd) {
+ objc_property_list_t<P>* plist = (objc_property_list_t<P>*)propertyList;
+ for(property_iterator it = plist->begin(); it != plist->end(); ++it) {
+ objc_property_t<P>& entry = *it;
+ pointersToAdd.push_back(&(entry.name));
+ pointersToAdd.push_back(&(entry.attributes));
+ }
+ }
+
+ static objc_property_list_t<P>* newPropertyList(size_t newCount, uint32_t newEntsize) {
+ void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
+ return new (buf) objc_property_list_t<P>(newCount, newEntsize);
+ }
+
+ void operator delete(void * p) {
+ ::free(p);
+ }
+
+ objc_property_list_t(uint32_t newCount,
+ uint32_t newEntsize = sizeof(objc_property_t<P>))
+ : entsize(newEntsize), count(newCount)
+ { }
+private:
+ // use newPropertyList instead
+ void* operator new (size_t);
+};
+
+
+template <typename A> class objc_protocol_list_t; // forward reference
+
+template <typename P>
+class objc_protocol_t {
+ typedef typename P::uint_t pint_t;
+
+ pint_t isa;
+ pint_t name;
+ pint_t protocols;
+ pint_t instanceMethods;
+ pint_t classMethods;
+ pint_t optionalInstanceMethods;
+ pint_t optionalClassMethods;
+ pint_t instanceProperties;
+ uint32_t size;
+ uint32_t flags;
+ pint_t extendedMethodTypes;
+ pint_t demangledName;
+ pint_t classProperties;
+
+public:
+ pint_t getIsaVMAddr() const { return (pint_t)P::getP(isa); }
+ void setIsaVMAddr(pint_t newIsa) { P::setP(isa, newIsa); }
+
+ const char *getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); }
+
+ uint32_t getSize() const { return P::E::get32(size); }
+ void setSize(uint32_t newSize) { P::E::set32(size, newSize); }
+
+ uint32_t getFlags() const { return P::E::get32(flags); }
+
+ void setFixedUp() { P::E::set32(flags, getFlags() | (1<<30)); }
+
+ objc_protocol_list_t<P> *getProtocols(ContentAccessor* cache) const { return (objc_protocol_list_t<P> *)cache->contentForVMAddr(P::getP(protocols)); }
+
+ objc_method_list_t<P> *getInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(instanceMethods)); }
+
+ objc_method_list_t<P> *getClassMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(classMethods)); }
+
+ objc_method_list_t<P> *getOptionalInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(optionalInstanceMethods)); }
+
+ objc_method_list_t<P> *getOptionalClassMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(optionalClassMethods)); }
+
+ objc_property_list_t<P> *getInstanceProperties(ContentAccessor* cache) const { return (objc_property_list_t<P> *)cache->contentForVMAddr(P::getP(instanceProperties)); }
+
+ pint_t *getExtendedMethodTypes(ContentAccessor* cache) const {
+ if (getSize() < offsetof(objc_protocol_t<P>, extendedMethodTypes) + sizeof(extendedMethodTypes)) {
+ return NULL;
+ }
+ return (pint_t *)cache->contentForVMAddr(P::getP(extendedMethodTypes));
+ }
+
+ const char *getDemangledName(ContentAccessor* cache) const {
+ if (sizeof(*this) < offsetof(objc_protocol_t<P>, demangledName) + sizeof(demangledName)) {
+ return NULL;
+ }
+ return (const char *)cache->contentForVMAddr(P::getP(demangledName));
+ }
+
+ void setDemangledName(ContentAccessor* cache, const char *newName) {
+ if (sizeof(*this) < offsetof(objc_protocol_t<P>, demangledName) + sizeof(demangledName)) {
+ terminate("objc protocol has the wrong size");
+ }
+ P::setP(demangledName, cache->vmAddrForContent((void*)newName));
+ }
+
+ void addPointers(std::vector<void*>& pointersToAdd)
+ {
+ pointersToAdd.push_back(&isa);
+ pointersToAdd.push_back(&name);
+ if (protocols) pointersToAdd.push_back(&protocols);
+ if (instanceMethods) pointersToAdd.push_back(&instanceMethods);
+ if (classMethods) pointersToAdd.push_back(&classMethods);
+ if (optionalInstanceMethods) pointersToAdd.push_back(&optionalInstanceMethods);
+ if (optionalClassMethods) pointersToAdd.push_back(&optionalClassMethods);
+ if (instanceProperties) pointersToAdd.push_back(&instanceProperties);
+ if (extendedMethodTypes) pointersToAdd.push_back(&extendedMethodTypes);
+ if (demangledName) pointersToAdd.push_back(&demangledName);
+ }
+};
+
+
+template <typename P>
+class objc_protocol_list_t {
+ typedef typename P::uint_t pint_t;
+ pint_t count;
+ pint_t list[0];
+
+ void* operator new (size_t, void* buf) { return buf; }
+
+public:
+
+ pint_t getCount() const { return (pint_t)P::getP(count); }
+
+ pint_t getVMAddress(pint_t i) {
+ return (pint_t)P::getP(list[i]);
+ }
+
+ objc_protocol_t<P>* get(ContentAccessor* cache, pint_t i) {
+ return (objc_protocol_t<P>*)cache->contentForVMAddr(getVMAddress(i));
+ }
+
+ void setVMAddress(pint_t i, pint_t protoVMAddr) {
+ P::setP(list[i], protoVMAddr);
+ }
+
+ void set(ContentAccessor* cache, pint_t i, objc_protocol_t<P>* proto) {
+ setVMAddress(i, cache->vmAddrForContent(proto));
+ }
+
+ uint32_t byteSize() const {
+ return byteSizeForCount(getCount());
+ }
+ static uint32_t byteSizeForCount(pint_t c) {
+ return sizeof(objc_protocol_list_t<P>) + c*sizeof(pint_t);
+ }
+
+ void getPointers(std::set<void*>& pointersToRemove) {
+ for(int i=0 ; i < count; ++i) {
+ pointersToRemove.insert(&list[i]);
+ }
+ }
+
+ static void addPointers(uint8_t* protocolList, std::vector<void*>& pointersToAdd) {
+ objc_protocol_list_t<P>* plist = (objc_protocol_list_t<P>*)protocolList;
+ for(int i=0 ; i < plist->count; ++i) {
+ pointersToAdd.push_back(&plist->list[i]);
+ }
+ }
+
+ static objc_protocol_list_t<P>* newProtocolList(pint_t newCount) {
+ void *buf = ::calloc(byteSizeForCount(newCount), 1);
+ return new (buf) objc_protocol_list_t<P>(newCount);
+ }
+
+ void operator delete(void * p) {
+ ::free(p);
+ }
+
+ objc_protocol_list_t(uint32_t newCount) : count(newCount) { }
+private:
+ // use newProtocolList instead
+ void* operator new (size_t);
+};
+
+
+template <typename P>
+class objc_class_data_t {
+ typedef typename P::uint_t pint_t;
+ uint32_t flags;
+ uint32_t instanceStart;
+ // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout
+ // on 64-bit archs, but no padding on 32-bit archs.
+ // This union is a way to model that.
+ union {
+ uint32_t instanceSize;
+ pint_t pad;
+ } instanceSize;
+ pint_t ivarLayout;
+ pint_t name;
+ pint_t baseMethods;
+ pint_t baseProtocols;
+ pint_t ivars;
+ pint_t weakIvarLayout;
+ pint_t baseProperties;
+
+public:
+ bool isMetaClass() { return P::E::get32(flags) & (1 << 0); }
+ bool isRootClass() { return P::E::get32(flags) & (1 << 1); }
+
+ uint32_t getInstanceStart() { return P::E::get32(instanceStart); }
+ void setInstanceStart(uint32_t newStart) { P::E::set32(instanceStart, newStart); }
+
+ uint32_t getInstanceSize() { return P::E::get32(instanceSize.instanceSize); }
+ void setInstanceSize(uint32_t newSiz) { P::E::set32(instanceSize.instanceSize, newSiz); }
+
+ objc_method_list_t<P> *getMethodList(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(baseMethods)); }
+
+ objc_protocol_list_t<P> *getProtocolList(ContentAccessor* cache) const { return (objc_protocol_list_t<P> *)cache->contentForVMAddr(P::getP(baseProtocols)); }
+
+ objc_ivar_list_t<P> *getIvarList(ContentAccessor* cache) const { return (objc_ivar_list_t<P> *)cache->contentForVMAddr(P::getP(ivars)); }
+
+ objc_property_list_t<P> *getPropertyList(ContentAccessor* cache) const { return (objc_property_list_t<P> *)cache->contentForVMAddr(P::getP(baseProperties)); }
+
+ const char * getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); }
+
+ void setMethodList(ContentAccessor* cache, objc_method_list_t<P>* mlist) {
+ P::setP(baseMethods, cache->vmAddrForContent(mlist));
+ }
+
+ void setProtocolList(ContentAccessor* cache, objc_protocol_list_t<P>* protolist) {
+ P::setP(baseProtocols, cache->vmAddrForContent(protolist));
+ }
+
+ void setPropertyList(ContentAccessor* cache, objc_property_list_t<P>* proplist) {
+ P::setP(baseProperties, cache->vmAddrForContent(proplist));
+ }
+
+ void addMethodListPointer(std::vector<void*>& pointersToAdd) {
+ pointersToAdd.push_back(&this->baseMethods);
+ }
+
+ void addPropertyListPointer(std::vector<void*>& pointersToAdd) {
+ pointersToAdd.push_back(&this->baseProperties);
+ }
+
+ void addProtocolListPointer(std::vector<void*>& pointersToAdd) {
+ pointersToAdd.push_back(&this->baseProtocols);
+ }
+};
+
+template <typename P>
+class objc_class_t {
+ typedef typename P::uint_t pint_t;
+
+ pint_t isa;
+ pint_t superclass;
+ pint_t method_cache;
+ pint_t vtable;
+ pint_t data;
+
+public:
+ bool isMetaClass(ContentAccessor* cache) const { return getData(cache)->isMetaClass(); }
+ bool isRootClass(ContentAccessor* cache) const { return getData(cache)->isRootClass(); }
+
+ objc_class_t<P> *getIsa(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(isa)); }
+
+ objc_class_t<P> *getSuperclass(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(superclass)); }
+
+ // Low bit marks Swift classes.
+ objc_class_data_t<P> *getData(ContentAccessor* cache) const { return (objc_class_data_t<P> *)cache->contentForVMAddr(P::getP(data & ~0x1LL)); }
+
+ objc_method_list_t<P> *getMethodList(ContentAccessor* cache) const {
+ objc_class_data_t<P>* d = getData(cache);
+ return d->getMethodList(cache);
+ }
+
+ objc_protocol_list_t<P> *getProtocolList(ContentAccessor* cache) const { return getData(cache)->getProtocolList(cache); }
+
+ objc_property_list_t<P> *getPropertyList(ContentAccessor* cache) const { return getData(cache)->getPropertyList(cache); }
+
+ const char* getName(ContentAccessor* cache) const {
+ return getData(cache)->getName(cache);
+ }
+
+ void setMethodList(ContentAccessor* cache, objc_method_list_t<P>* mlist) {
+ getData(cache)->setMethodList(cache, mlist);
+ }
+
+ void setProtocolList(ContentAccessor* cache, objc_protocol_list_t<P>* protolist) {
+ getData(cache)->setProtocolList(cache, protolist);
+ }
+
+ void setPropertyList(ContentAccessor* cache, objc_property_list_t<P>* proplist) {
+ getData(cache)->setPropertyList(cache, proplist);
+ }
+
+ void addMethodListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
+ getData(cache)->addMethodListPointer(pointersToAdd);
+ }
+
+ void addPropertyListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
+ getData(cache)->addPropertyListPointer(pointersToAdd);
+ }
+
+ void addProtocolListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
+ getData(cache)->addProtocolListPointer(pointersToAdd);
+ }
+
+};
+
+
+
+template <typename P>
+class objc_category_t {
+ typedef typename P::uint_t pint_t;
+
+ pint_t name;
+ pint_t cls;
+ pint_t instanceMethods;
+ pint_t classMethods;
+ pint_t protocols;
+ pint_t instanceProperties;
+
+public:
+
+ const char * getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); }
+
+ objc_class_t<P> *getClass(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(cls)); }
+
+ objc_method_list_t<P> *getInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(instanceMethods)); }
+
+ objc_method_list_t<P> *getClassMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(classMethods)); }
+
+ objc_protocol_list_t<P> *getProtocols(ContentAccessor* cache) const { return (objc_protocol_list_t<P> *)cache->contentForVMAddr(P::getP(protocols)); }
+
+ objc_property_list_t<P> *getInstanceProperties(ContentAccessor* cache) const { return (objc_property_list_t<P> *)cache->contentForVMAddr(P::getP(instanceProperties)); }
+
+ void getPointers(std::set<void*>& pointersToRemove) {
+ pointersToRemove.insert(&name);
+ pointersToRemove.insert(&cls);
+ pointersToRemove.insert(&instanceMethods);
+ pointersToRemove.insert(&classMethods);
+ pointersToRemove.insert(&protocols);
+ pointersToRemove.insert(&instanceProperties);
+ }
+
+
+};
+
+template <typename P>
+class objc_message_ref_t {
+ typedef typename P::uint_t pint_t;
+
+ pint_t imp;
+ pint_t sel;
+
+public:
+ pint_t getName() const { return (pint_t)P::getP(sel); }
+
+ void setName(pint_t newName) { P::setP(sel, newName); }
+};
+
+// Call visitor.visitIvar() on every ivar in a given class.
+template <typename P, typename V>
+class IvarWalker {
+ typedef typename P::uint_t pint_t;
+ V& ivarVisitor;
+public:
+
+ IvarWalker(V& visitor) : ivarVisitor(visitor) { }
+
+ void walk(ContentAccessor* cache, const macho_header<P>* header, objc_class_t<P> *cls)
+ {
+ objc_class_data_t<P> *data = cls->getData(cache);
+ objc_ivar_list_t<P> *ivars = data->getIvarList(cache);
+ if (ivars) {
+ for (pint_t i = 0; i < ivars->getCount(); i++) {
+ objc_ivar_t<P>& ivar = ivars->get(i);
+ //fprintf(stderr, "visiting ivar: %s\n", ivar.getName(cache));
+ ivarVisitor.visitIvar(cache, header, cls, &ivar);
+ }
+ } else {
+ //fprintf(stderr, "no ivars\n");
+ }
+ }
+
+ void visitClass(ContentAccessor* cache, const macho_header<P>* header, objc_class_t<P> *cls)
+ {
+ walk(cache, header, cls);
+ }
+};
+
+// Call visitor.visitClass() on every class.
+template <typename P, typename V>
+class ClassWalker {
+ typedef typename P::uint_t pint_t;
+ V& _visitor;
+public:
+
+ ClassWalker(V& visitor) : _visitor(visitor) { }
+
+ void walk(ContentAccessor* cache, const macho_header<P>* header)
+ {
+ PointerSection<P, objc_class_t<P>*> classList(cache, header, "__DATA", "__objc_classlist");
+
+ for (pint_t i = 0; i < classList.count(); i++) {
+ objc_class_t<P>* cls = classList.get(i);
+ //fprintf(stderr, "visiting class: %s\n", cls->getName(cache));
+ if (cls) _visitor.visitClass(cache, header, cls);
+ }
+ }
+};
+
+// Call visitor.visitProtocol() on every protocol.
+template <typename P, typename V>
+class ProtocolWalker {
+ typedef typename P::uint_t pint_t;
+ V& _protocolVisitor;
+public:
+
+ ProtocolWalker(V& visitor) : _protocolVisitor(visitor) { }
+
+ void walk(ContentAccessor* cache, const macho_header<P>* header)
+ {
+ PointerSection<P, objc_protocol_t<P> *>
+ protocols(cache, header, "__DATA", "__objc_protolist");
+
+ for (pint_t i = 0; i < protocols.count(); i++) {
+ objc_protocol_t<P> *proto = protocols.get(i);
+ _protocolVisitor.visitProtocol(cache, header, proto);
+ }
+ }
+};
+
+// Call visitor.visitProtocolReference() on every protocol.
+template <typename P, typename V>
+class ProtocolReferenceWalker {
+ typedef typename P::uint_t pint_t;
+ V& _visitor;
+
+ void visitProtocolList(ContentAccessor* cache,
+ objc_protocol_list_t<P>* protolist)
+ {
+ if (!protolist) return;
+ for (pint_t i = 0; i < protolist->getCount(); i++) {
+ pint_t oldValue = protolist->getVMAddress(i);
+ pint_t newValue = _visitor.visitProtocolReference(cache, oldValue);
+ protolist->setVMAddress(i, newValue);
+ }
+ }
+
+ friend class ClassWalker<P, ProtocolReferenceWalker<P, V>>;
+
+ void visitClass(ContentAccessor* cache, const macho_header<P>*,
+ objc_class_t<P>* cls)
+ {
+ visitProtocolList(cache, cls->getProtocolList(cache));
+ visitProtocolList(cache, cls->getIsa(cache)->getProtocolList(cache));
+ }
+
+public:
+
+ ProtocolReferenceWalker(V& visitor) : _visitor(visitor) { }
+ void walk(ContentAccessor* cache, const macho_header<P>* header)
+ {
+ // @protocol expressions
+ PointerSection<P, objc_protocol_t<P> *>
+ protorefs(cache, header, "__DATA", "__objc_protorefs");
+ for (pint_t i = 0; i < protorefs.count(); i++) {
+ pint_t oldValue = protorefs.getVMAddress(i);
+ pint_t newValue = _visitor.visitProtocolReference(cache, oldValue);
+ protorefs.setVMAddress(i, newValue);
+ }
+
+ // protocol lists in classes
+ ClassWalker<P, ProtocolReferenceWalker<P, V>> classes(*this);
+ classes.walk(cache, header);
+
+ // protocol lists in protocols
+ // __objc_protolists itself is NOT updated
+ PointerSection<P, objc_protocol_t<P> *>
+ protocols(cache, header, "__DATA", "__objc_protolist");
+ for (pint_t i = 0; i < protocols.count(); i++) {
+ objc_protocol_t<P>* proto = protocols.get(i);
+ visitProtocolList(cache, proto->getProtocols(cache));
+ // not recursive: every old protocol object
+ // must be in some protolist section somewhere
+ }
+ }
+};
+
+// Call visitor.visitMethodList(mlist) on every
+// class and category method list in a header.
+// Call visitor.visitProtocolMethodList(mlist, typelist) on every
+// protocol method list in a header.
+template <typename P, typename V>
+class MethodListWalker {
+
+ typedef typename P::uint_t pint_t;
+
+ V& mVisitor;
+
+public:
+
+ MethodListWalker(V& visitor) : mVisitor(visitor) { }
+
+ void walk(ContentAccessor* cache, const macho_header<P>* header)
+ {
+ // Method lists in classes
+ PointerSection<P, objc_class_t<P> *>
+ classes(cache, header, "__DATA", "__objc_classlist");
+
+ for (pint_t i = 0; i < classes.count(); i++) {
+ objc_class_t<P> *cls = classes.get(i);
+ objc_method_list_t<P> *mlist;
+ if ((mlist = cls->getMethodList(cache))) {
+ mVisitor.visitMethodList(mlist);
+ }
+ if ((mlist = cls->getIsa(cache)->getMethodList(cache))) {
+ mVisitor.visitMethodList(mlist);
+ }
+ }
+
+ // Method lists from categories
+ PointerSection<P, objc_category_t<P> *>
+ cats(cache, header, "__DATA", "__objc_catlist");
+ for (pint_t i = 0; i < cats.count(); i++) {
+ objc_category_t<P> *cat = cats.get(i);
+ objc_method_list_t<P> *mlist;
+ if ((mlist = cat->getInstanceMethods(cache))) {
+ mVisitor.visitMethodList(mlist);
+ }
+ if ((mlist = cat->getClassMethods(cache))) {
+ mVisitor.visitMethodList(mlist);
+ }
+ }
+
+ // Method description lists from protocols
+ PointerSection<P, objc_protocol_t<P> *>
+ protocols(cache, header, "__DATA", "__objc_protolist");
+ for (pint_t i = 0; i < protocols.count(); i++) {
+ objc_protocol_t<P> *proto = protocols.get(i);
+ objc_method_list_t<P> *mlist;
+ pint_t *typelist = proto->getExtendedMethodTypes(cache);
+
+ if ((mlist = proto->getInstanceMethods(cache))) {
+ mVisitor.visitProtocolMethodList(mlist, typelist);
+ if (typelist) typelist += mlist->getCount();
+ }
+ if ((mlist = proto->getClassMethods(cache))) {
+ mVisitor.visitProtocolMethodList(mlist, typelist);
+ if (typelist) typelist += mlist->getCount();
+ }
+ if ((mlist = proto->getOptionalInstanceMethods(cache))) {
+ mVisitor.visitProtocolMethodList(mlist, typelist);
+ if (typelist) typelist += mlist->getCount();
+ }
+ if ((mlist = proto->getOptionalClassMethods(cache))) {
+ mVisitor.visitProtocolMethodList(mlist, typelist);
+ if (typelist) typelist += mlist->getCount();
+ }
+ }
+ }
+};
+
+
+// Update selector references. The visitor performs recording and uniquing.
+template <typename P, typename V>
+class SelectorOptimizer {
+
+ typedef typename P::uint_t pint_t;
+
+ V& mVisitor;
+
+ friend class MethodListWalker<P, SelectorOptimizer<P,V> >;
+ void visitMethodList(objc_method_list_t<P> *mlist)
+ {
+ // Gather selectors. Update method names.
+ for (uint32_t m = 0; m < mlist->getCount(); m++) {
+ pint_t oldValue = mlist->get(m).getName();
+ pint_t newValue = mVisitor.visit(oldValue);
+ mlist->get(m).setName(newValue);
+ }
+ // Do not setFixedUp: the methods are not yet sorted.
+ }
+
+ void visitProtocolMethodList(objc_method_list_t<P> *mlist, pint_t *types)
+ {
+ visitMethodList(mlist);
+ }
+
+public:
+
+ SelectorOptimizer(V& visitor) : mVisitor(visitor) { }
+
+ void optimize(ContentAccessor* cache, const macho_header<P>* header)
+ {
+ // method lists in classes, categories, and protocols
+ MethodListWalker<P, SelectorOptimizer<P,V> > mw(*this);
+ mw.walk(cache, header);
+
+ // @selector references
+ PointerSection<P, const char *>
+ selrefs(cache, header, "__DATA", "__objc_selrefs");
+ for (pint_t i = 0; i < selrefs.count(); i++) {
+ pint_t oldValue = selrefs.getVMAddress(i);
+ pint_t newValue = mVisitor.visit(oldValue);
+ selrefs.setVMAddress(i, newValue);
+ }
+
+ // message references
+ ArraySection<P, objc_message_ref_t<P> >
+ msgrefs(cache, header, "__DATA", "__objc_msgrefs");
+ for (pint_t i = 0; i < msgrefs.count(); i++) {
+ objc_message_ref_t<P>& msg = msgrefs.get(i);
+ pint_t oldValue = msg.getName();
+ pint_t newValue = mVisitor.visit(oldValue);
+ msg.setName(newValue);
+ }
+ }
+};
+
+
+// Update selector references. The visitor performs recording and uniquing.
+template <typename P>
+class IvarOffsetOptimizer {
+ uint32_t _slide;
+ uint32_t _maxAlignment;
+ uint32_t _optimized;
+
+public:
+
+ IvarOffsetOptimizer() : _optimized(0) { }
+
+ size_t optimized() const { return _optimized; }
+
+ // dual purpose ivar visitor function
+ // if slide!=0 then slides the ivar by that amount, otherwise computes _maxAlignment
+ void visitIvar(ContentAccessor* cache, const macho_header<P>* /*unused, may be NULL*/, objc_class_t<P> *cls, objc_ivar_t<P> *ivar)
+ {
+ if (_slide == 0) {
+ uint32_t alignment = ivar->getAlignment();
+ if (alignment > _maxAlignment) _maxAlignment = alignment;
+ } else {
+ // skip anonymous bitfields
+ if (ivar->hasOffset()) {
+ uint32_t oldOffset = (uint32_t)ivar->getOffset(cache);
+ ivar->setOffset(cache, oldOffset + _slide);
+ _optimized++;
+ //fprintf(stderr, "%d -> %d for %s.%s\n", oldOffset, oldOffset + _slide, cls->getName(cache), ivar->getName(cache));
+ } else {
+ //fprintf(stderr, "NULL offset\n");
+ }
+ }
+ }
+
+ // Class visitor function. Evaluates whether to slide ivars and performs slide if needed.
+ // The slide algorithm is also implemented in objc. Any changes here should be reflected there also.
+ void visitClass(ContentAccessor* cache, const macho_header<P>* /*unused, may be NULL*/, objc_class_t<P> *cls)
+ {
+ objc_class_t<P> *super = cls->getSuperclass(cache);
+ if (super) {
+ // Recursively visit superclasses to ensure we have the correct superclass start
+ // Note that we don't need the macho_header, so just pass NULL.
+ visitClass(cache, nullptr, super);
+
+ objc_class_data_t<P> *data = cls->getData(cache);
+ objc_class_data_t<P> *super_data = super->getData(cache);
+ int32_t diff = super_data->getInstanceSize() - data->getInstanceStart();
+ if (diff > 0) {
+ IvarWalker<P, IvarOffsetOptimizer<P> > ivarVisitor(*this);
+ _maxAlignment = 1;
+ _slide = 0;
+
+ // This walk computes _maxAlignment
+ ivarVisitor.walk(cache, nullptr, cls);
+
+ // Compute a slide value that preserves that alignment
+ uint32_t alignMask = _maxAlignment - 1;
+ if (diff & alignMask) diff = (diff + alignMask) & ~alignMask;
+
+ // Slide all of this class's ivars en masse
+ _slide = diff;
+ if (_slide != 0) {
+ //fprintf(stderr, "Sliding ivars in %s by %u (superclass was %d, now %d)\n", cls->getName(cache), _slide, data->getInstanceStart(), super_data->getInstanceSize());
+ ivarVisitor.walk(cache, nullptr, cls);
+ data->setInstanceStart(data->getInstanceStart() + _slide);
+ data->setInstanceSize(data->getInstanceSize() + _slide);
+ }
+ }
+ }
+ }
+
+ // Enumerates objc classes in the module and performs any ivar slides
+ void optimize(ContentAccessor* cache, const macho_header<P>* header)
+ {
+ // The slide code cannot fix up GC layout strings so skip modules that support or require GC
+ const macho_section<P> *imageInfoSection = header->getSection("__DATA", "__objc_imageinfo");
+ if (imageInfoSection) {
+ objc_image_info<P> *info = (objc_image_info<P> *)cache->contentForVMAddr(imageInfoSection->addr());
+ if (!info->supportsGCFlagSet() && !info->requiresGCFlagSet()) {
+ ClassWalker<P, IvarOffsetOptimizer<P> > classVisitor(*this);
+ classVisitor.walk(cache, header);
+ } else {
+ //fprintf(stderr, "GC support present - skipped module\n");
+ }
+ }
+ }
+};
+
+
+// Detect classes that have missing weak-import superclasses.
+template <typename P>
+class WeakClassDetector {
+ bool noMissing;
+
+ friend class ClassWalker<P, WeakClassDetector<P>>;
+ void visitClass(ContentAccessor* cache, const macho_header<P>*,
+ objc_class_t<P>* cls)
+ {
+ auto supercls = cls->getSuperclass(cache);
+ if (supercls) {
+ // okay: class with superclass
+ // Note that the superclass itself might have a missing superclass.
+ // That is fine for mere detection because we will visit the
+ // superclass separately.
+ } else if (cls->isRootClass(cache)) {
+ // okay: root class is expected to have no superclass
+ } else {
+ // bad: cls's superclass is missing.
+ warning("Superclass of class '%s' is weak-import and missing.",
+ cls->getName(cache));
+ noMissing = false;
+ }
+ }
+
+public:
+ bool noMissingWeakSuperclasses(ContentAccessor* cache,
+ std::vector<const macho_header<P>*> dylibs)
+ {
+ noMissing = true;
+ ClassWalker<P, WeakClassDetector<P>> classes(*this);
+ for (auto mh : dylibs) {
+ classes.walk(cache, mh);
+ }
+ return noMissing;
+ }
+};
+
+
+// Sort methods in place by selector.
+template <typename P>
+class MethodListSorter {
+
+ typedef typename P::uint_t pint_t;
+
+ uint32_t _optimized;
+
+ friend class MethodListWalker<P, MethodListSorter<P> >;
+ void visitMethodList(objc_method_list_t<P> *mlist)
+ {
+ typename objc_method_t<P>::SortBySELAddress sorter;
+ std::stable_sort(mlist->begin(), mlist->end(), sorter);
+ mlist->setFixedUp();
+ _optimized++;
+ }
+
+ void visitProtocolMethodList(objc_method_list_t<P> *mlist, pint_t *typelist)
+ {
+ typename objc_method_t<P>::SortBySELAddress sorter;
+ // can't easily use std::stable_sort here
+ for (uint32_t i = 0; i < mlist->getCount(); i++) {
+ for (uint32_t j = i+1; j < mlist->getCount(); j++) {
+ objc_method_t<P>& mi = mlist->get(i);
+ objc_method_t<P>& mj = mlist->get(j);
+ if (! sorter(mi, mj)) {
+ std::swap(mi, mj);
+ if (typelist) std::swap(typelist[i], typelist[j]);
+ }
+ }
+ }
+
+ mlist->setFixedUp();
+ _optimized++;
+ }
+
+public:
+ MethodListSorter() : _optimized(0) { }
+
+ size_t optimized() const { return _optimized; }
+
+ void optimize(ContentAccessor* cache, const macho_header<P>* header)
+ {
+ MethodListWalker<P, MethodListSorter<P> > mw(*this);
+ mw.walk(cache, header);
+ }
+};
+
+
+template <typename P, typename InfoT>
+class HeaderInfoOptimizer {
+public:
+
+ typedef typename P::uint_t pint_t;
+
+ HeaderInfoOptimizer() : _hInfos(0), _count(0) { }
+
+ const char* init(uint32_t count, uint8_t*& buf, size_t& bufSize) {
+ if (count == 0)
+ return nullptr;
+
+ size_t requiredSize =
+ 2*sizeof(uint32_t) + count*sizeof(InfoT);
+ if (bufSize < requiredSize) {
+ return "libobjc's read/write section is too small (metadata not optimized)";
+ }
+
+ uint32_t *buf32 = (uint32_t *)buf;
+ P::E::set32(buf32[0], count);
+ P::E::set32(buf32[1], sizeof(InfoT));
+ _hInfos = (InfoT*)(buf32+2);
+
+ buf += requiredSize;
+ bufSize -= requiredSize;
+
+ return nullptr;
+ }
+
+ void update(ContentAccessor* cache, const macho_header<P>* mh, std::vector<void*>& pointersInData) {
+ InfoT* hi = new(&_hInfos[_count++]) InfoT(cache, mh);
+ (void)hi;
+ }
+
+ InfoT* hinfoForHeader(ContentAccessor* cache, const macho_header<P>* mh) {
+ // FIXME: could be binary search
+ uint64_t mh_vmaddr = cache->vmAddrForContent((void*)mh);
+ for (size_t i = 0; i < _count; i++) {
+ InfoT* hi = &_hInfos[i];
+ if (hi->header_vmaddr(cache) == mh_vmaddr) return hi;
+ }
+ return nullptr;
+ }
+private:
+ InfoT* _hInfos;
+ size_t _count;
+};
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "OptimizerBranches.h"
+#include "Trie.hpp"
+#include "MachOFileAbstraction.hpp"
+#include "Logging.h"
+
+#include "dyld_cache_config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <CommonCrypto/CommonDigest.h>
+
+
+static const bool verbose = false;
+
+// These are functions that are interposed by Instruments.app or ASan
+static const char* sNeverStubEliminateSymbols[] = {
+ "___bzero",
+ "___cxa_atexit",
+ "___cxa_throw",
+ "__longjmp",
+ "__objc_autoreleasePoolPop",
+ "_accept",
+ "_access",
+ "_asctime",
+ "_asctime_r",
+ "_asprintf",
+ "_atoi",
+ "_atol",
+ "_atoll",
+ "_calloc",
+ "_chmod",
+ "_chown",
+ "_close",
+ "_confstr",
+ "_ctime",
+ "_ctime_r",
+ "_dispatch_after",
+ "_dispatch_after_f",
+ "_dispatch_async",
+ "_dispatch_async_f",
+ "_dispatch_barrier_async_f",
+ "_dispatch_group_async",
+ "_dispatch_group_async_f",
+ "_dispatch_source_set_cancel_handler",
+ "_dispatch_source_set_event_handler",
+ "_dispatch_sync_f",
+ "_dlclose",
+ "_dlopen",
+ "_dup",
+ "_dup2",
+ "_endgrent",
+ "_endpwent",
+ "_ether_aton",
+ "_ether_hostton",
+ "_ether_line",
+ "_ether_ntoa",
+ "_ether_ntohost",
+ "_fchmod",
+ "_fchown",
+ "_fclose",
+ "_fdopen",
+ "_fflush",
+ "_fopen",
+ "_fork",
+ "_fprintf",
+ "_free",
+ "_freopen",
+ "_frexp",
+ "_frexpf",
+ "_frexpl",
+ "_fscanf",
+ "_fstat",
+ "_fstatfs",
+ "_fstatfs64",
+ "_fsync",
+ "_ftime",
+ "_getaddrinfo",
+ "_getattrlist",
+ "_getcwd",
+ "_getgrent",
+ "_getgrgid",
+ "_getgrgid_r",
+ "_getgrnam",
+ "_getgrnam_r",
+ "_getgroups",
+ "_gethostbyaddr",
+ "_gethostbyname",
+ "_gethostbyname2",
+ "_gethostent",
+ "_getifaddrs",
+ "_getitimer",
+ "_getnameinfo",
+ "_getpass",
+ "_getpeername",
+ "_getpwent",
+ "_getpwnam",
+ "_getpwnam_r",
+ "_getpwuid",
+ "_getpwuid_r",
+ "_getsockname",
+ "_getsockopt",
+ "_gmtime",
+ "_gmtime_r",
+ "_if_indextoname",
+ "_if_nametoindex",
+ "_index",
+ "_inet_aton",
+ "_inet_ntop",
+ "_inet_pton",
+ "_initgroups",
+ "_ioctl",
+ "_lchown",
+ "_lgamma",
+ "_lgammaf",
+ "_lgammal",
+ "_link",
+ "_listxattr",
+ "_localtime",
+ "_localtime_r",
+ "_longjmp",
+ "_lseek",
+ "_lstat",
+ "_malloc",
+ "_malloc_create_zone",
+ "_malloc_default_purgeable_zone",
+ "_malloc_default_zone",
+ "_malloc_good_size",
+ "_malloc_make_nonpurgeable",
+ "_malloc_make_purgeable",
+ "_malloc_set_zone_name",
+ "_mbsnrtowcs",
+ "_mbsrtowcs",
+ "_mbstowcs",
+ "_memchr",
+ "_memcmp",
+ "_memcpy",
+ "_memmove",
+ "_memset",
+ "_mktime",
+ "_mlock",
+ "_mlockall",
+ "_modf",
+ "_modff",
+ "_modfl",
+ "_munlock",
+ "_munlockall",
+ "_objc_autoreleasePoolPop",
+ "_objc_setProperty",
+ "_objc_setProperty_atomic",
+ "_objc_setProperty_atomic_copy",
+ "_objc_setProperty_nonatomic",
+ "_objc_setProperty_nonatomic_copy",
+ "_objc_storeStrong",
+ "_open",
+ "_opendir",
+ "_poll",
+ "_posix_memalign",
+ "_pread",
+ "_printf",
+ "_pthread_attr_getdetachstate",
+ "_pthread_attr_getguardsize",
+ "_pthread_attr_getinheritsched",
+ "_pthread_attr_getschedparam",
+ "_pthread_attr_getschedpolicy",
+ "_pthread_attr_getscope",
+ "_pthread_attr_getstack",
+ "_pthread_attr_getstacksize",
+ "_pthread_condattr_getpshared",
+ "_pthread_create",
+ "_pthread_getschedparam",
+ "_pthread_join",
+ "_pthread_mutex_lock",
+ "_pthread_mutex_unlock",
+ "_pthread_mutexattr_getprioceiling",
+ "_pthread_mutexattr_getprotocol",
+ "_pthread_mutexattr_getpshared",
+ "_pthread_mutexattr_gettype",
+ "_pthread_rwlockattr_getpshared",
+ "_pwrite",
+ "_rand_r",
+ "_read",
+ "_readdir",
+ "_readdir_r",
+ "_readv",
+ "_readv$UNIX2003",
+ "_realloc",
+ "_realpath",
+ "_recv",
+ "_recvfrom",
+ "_recvmsg",
+ "_remquo",
+ "_remquof",
+ "_remquol",
+ "_scanf",
+ "_send",
+ "_sendmsg",
+ "_sendto",
+ "_setattrlist",
+ "_setgrent",
+ "_setitimer",
+ "_setlocale",
+ "_setpwent",
+ "_shm_open",
+ "_shm_unlink",
+ "_sigaction",
+ "_sigemptyset",
+ "_sigfillset",
+ "_siglongjmp",
+ "_signal",
+ "_sigpending",
+ "_sigprocmask",
+ "_sigwait",
+ "_snprintf",
+ "_sprintf",
+ "_sscanf",
+ "_stat",
+ "_statfs",
+ "_statfs64",
+ "_strcasecmp",
+ "_strcat",
+ "_strchr",
+ "_strcmp",
+ "_strcpy",
+ "_strdup",
+ "_strerror",
+ "_strerror_r",
+ "_strlen",
+ "_strncasecmp",
+ "_strncat",
+ "_strncmp",
+ "_strncpy",
+ "_strptime",
+ "_strtoimax",
+ "_strtol",
+ "_strtoll",
+ "_strtoumax",
+ "_tempnam",
+ "_time",
+ "_times",
+ "_tmpnam",
+ "_tsearch",
+ "_unlink",
+ "_valloc",
+ "_vasprintf",
+ "_vfprintf",
+ "_vfscanf",
+ "_vprintf",
+ "_vscanf",
+ "_vsnprintf",
+ "_vsprintf",
+ "_vsscanf",
+ "_wait",
+ "_wait$UNIX2003",
+ "_wait3",
+ "_wait4",
+ "_waitid",
+ "_waitid$UNIX2003",
+ "_waitpid",
+ "_waitpid$UNIX2003",
+ "_wcslen",
+ "_wcsnrtombs",
+ "_wcsrtombs",
+ "_wcstombs",
+ "_wordexp",
+ "_write",
+ "_writev",
+ "_writev$UNIX2003",
+ // <rdar://problem/22050956> always use stubs for C++ symbols that can be overridden
+ "__ZdaPv",
+ "__ZdlPv",
+ "__Znam",
+ "__Znwm",
+
+ nullptr
+};
+
+
+// These are dylibs that are interposed and stubs calling into them should never be bypassed
+static const char* sNeverStubEliminateDylibs[] = {
+ "/usr/lib/system/libdispatch.dylib",
+ nullptr
+};
+
+
+
+uint64_t branchPoolTextSize(ArchPair arch)
+{
+ if ( arch.arch == CPU_TYPE_ARM64 )
+ return 0x0000C000; // 48KB
+ else
+ return 0;
+}
+
+uint64_t branchPoolLinkEditSize(ArchPair arch)
+{
+ if ( arch.arch == CPU_TYPE_ARM64 )
+ return 0x00100000; // 1MB
+ else
+ return 0;
+}
+
+
+uint64_t branchReach(ArchPair arch)
+{
+ if ( arch.arch == CPU_TYPE_ARM64 )
+ return 0x08000000 - (2*branchPoolTextSize(arch)); // 128MB
+ else
+ return 0;
+}
+
+template <typename P>
+class BranchPoolDylib {
+public:
+ BranchPoolDylib(ArchPair arch, void* cacheBuffer, uint64_t cacheSize, uint64_t startAddr,
+ uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset);
+
+ uint64_t addr() { return _startAddr; }
+ uint64_t getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& branchIslandPools);
+ uint64_t getBackBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& branchIslandPools);
+ void finalizeLoadCommands();
+ void printStats();
+
+private:
+ uint64_t indexToAddr(uint32_t index) { return _startAddr + _firstStubOffset + sizeof(uint32_t)*index; }
+
+ static const int64_t b128MegLimit = 0x07FFFFFF;
+
+ typedef typename P::uint_t pint_t;
+ typedef typename P::E E;
+
+ ArchPair _arch;
+ void* _cacheBuffer;
+ uint64_t _startAddr;
+ std::unordered_map<uint64_t, uint32_t> _targetToIslandIndex;
+ std::unordered_map<uint32_t, const char*> _islandIndexToName;
+ macho_symtab_command<P>* _symbolTableCmd;
+ macho_dysymtab_command<P>* _dynamicSymbolTableCmd;
+ macho_uuid_command<P>* _uuidCmd;
+ uint32_t _maxStubs;
+ uint32_t _nextIndex;
+ uint32_t _firstStubOffset;
+ uint32_t* _stubInstructions;
+ macho_nlist<P>* _symbolTable;
+ char* _nextString;
+ char* _stringPoolStart;
+ char* _stringPoolEnd;
+};
+
+
+template <typename P>
+BranchPoolDylib<P>::BranchPoolDylib(ArchPair arch, void* cacheBuffer, uint64_t cacheSize, uint64_t poolStartAddr,
+ uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset)
+ : _arch(arch), _cacheBuffer(cacheBuffer),_startAddr(poolStartAddr), _nextIndex(0), _firstStubOffset(0x280)
+{
+ bool is64 = (sizeof(typename P::uint_t) == 8);
+
+ const uint64_t textSegSize = branchPoolTextSize(arch);
+ const uint64_t linkEditSegSize = branchPoolLinkEditSize(arch);
+ const unsigned stubCount = (unsigned)((textSegSize - _firstStubOffset)/4);
+ const uint32_t linkeditOffsetSymbolTable = 0;
+ const uint32_t linkeditOffsetIndirectSymbolTable = stubCount*sizeof(macho_nlist<P>);
+ const uint32_t linkeditOffsetSymbolPoolOffset = linkeditOffsetIndirectSymbolTable + stubCount*sizeof(uint32_t);
+ _maxStubs = stubCount;
+
+ // write mach_header and load commands for pseudo dylib
+ macho_header<P>* mh = (macho_header<P>*)((uint8_t*)cacheBuffer + poolStartAddr - textRegionStartAddr);
+ mh->set_magic(is64 ? MH_MAGIC_64 : MH_MAGIC);
+ mh->set_cputype(arch.arch);
+ mh->set_cpusubtype(arch.subtype);
+ mh->set_filetype(MH_DYLIB);
+ mh->set_ncmds(6);
+ mh->set_sizeofcmds(is64 ? 0x210 : 100); // FIXME: 32-bit size
+ mh->set_flags(0x80000000);
+ // LC_SEGMENT
+ macho_load_command<P>* cmd = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ macho_segment_command<P>* textSegCmd = (macho_segment_command<P>*)cmd;
+ textSegCmd->set_cmd(is64 ? LC_SEGMENT_64 : LC_SEGMENT);
+ textSegCmd->set_cmdsize(sizeof(macho_segment_command<P>)*2+sizeof(macho_section<P>));
+ textSegCmd->set_segname("__TEXT");
+ textSegCmd->set_vmaddr(poolStartAddr);
+ textSegCmd->set_vmsize(textSegSize);
+ textSegCmd->set_fileoff(poolStartAddr - textRegionStartAddr);
+ textSegCmd->set_filesize(branchPoolTextSize(arch));
+ textSegCmd->set_maxprot(PROT_READ|PROT_EXEC);
+ textSegCmd->set_initprot(PROT_READ|PROT_EXEC);
+ textSegCmd->set_nsects(1);
+ textSegCmd->set_flags(0);
+ macho_section<P>* stubSection = (macho_section<P>*)((uint8_t*)textSegCmd + sizeof(macho_segment_command<P>));
+ stubSection->set_sectname("__stubs");
+ stubSection->set_segname("__TEXT");
+ stubSection->set_addr(poolStartAddr + _firstStubOffset);
+ stubSection->set_size(textSegSize - _firstStubOffset);
+ stubSection->set_offset((uint32_t)(poolStartAddr + _firstStubOffset - textRegionStartAddr));
+ stubSection->set_align(2);
+ stubSection->set_reloff(0);
+ stubSection->set_nreloc(0);
+ stubSection->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS);
+ stubSection->set_reserved1(0); // start index in indirect table
+ stubSection->set_reserved2(4); // size of stubs
+ // LC_SEGMENT
+ cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ macho_segment_command<P>* linkEditSegCmd = (macho_segment_command<P>*)cmd;
+ linkEditSegCmd->set_cmd(is64 ? LC_SEGMENT_64 : LC_SEGMENT);
+ linkEditSegCmd->set_cmdsize(sizeof(macho_segment_command<P>));
+ linkEditSegCmd->set_segname("__LINKEDIT");
+ linkEditSegCmd->set_vmaddr(poolLinkEditStartAddr);
+ linkEditSegCmd->set_vmsize(linkEditSegSize);
+ linkEditSegCmd->set_fileoff(poolLinkEditStartOffset);
+ linkEditSegCmd->set_filesize(linkEditSegSize);
+ linkEditSegCmd->set_maxprot(PROT_READ);
+ linkEditSegCmd->set_initprot(PROT_READ);
+ linkEditSegCmd->set_nsects(0);
+ linkEditSegCmd->set_flags(0);
+ // LC_ID_DYLIB
+ cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ macho_dylib_command<P>* installNameCmd = (macho_dylib_command<P>*)cmd;
+ installNameCmd->set_cmd(LC_ID_DYLIB);
+ installNameCmd->set_cmdsize(sizeof(macho_dylib_command<P>) + 48);
+ installNameCmd->set_timestamp(2);
+ installNameCmd->set_current_version(0x10000);
+ installNameCmd->set_compatibility_version(0x10000);
+ installNameCmd->set_name_offset();
+ strcpy((char*)cmd + sizeof(macho_dylib_command<P>), "dyld_shared_cache_branch_islands");
+ // LC_SYMTAB
+ cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ _symbolTableCmd = (macho_symtab_command<P>*)cmd;
+ _symbolTableCmd->set_cmd(LC_SYMTAB);
+ _symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command<P>));
+ _symbolTableCmd->set_nsyms(stubCount);
+ _symbolTableCmd->set_symoff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetSymbolTable));
+ _symbolTableCmd->set_stroff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetSymbolPoolOffset));
+ _symbolTableCmd->set_strsize((uint32_t)(linkEditSegSize - linkeditOffsetSymbolPoolOffset));
+ // LC_DYSYMTAB
+ cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ _dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)cmd;
+ _dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB);
+ _dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command<P>));
+ _dynamicSymbolTableCmd->set_ilocalsym(0);
+ _dynamicSymbolTableCmd->set_nlocalsym(0);
+ _dynamicSymbolTableCmd->set_iextdefsym(0);
+ _dynamicSymbolTableCmd->set_nextdefsym(0);
+ _dynamicSymbolTableCmd->set_iundefsym(0);
+ _dynamicSymbolTableCmd->set_nundefsym(stubCount);
+ _dynamicSymbolTableCmd->set_tocoff(0);
+ _dynamicSymbolTableCmd->set_ntoc(0);
+ _dynamicSymbolTableCmd->set_modtaboff(0);
+ _dynamicSymbolTableCmd->set_nmodtab(0);
+ _dynamicSymbolTableCmd->set_extrefsymoff(0);
+ _dynamicSymbolTableCmd->set_nextrefsyms(0);
+ _dynamicSymbolTableCmd->set_indirectsymoff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetIndirectSymbolTable));
+ _dynamicSymbolTableCmd->set_nindirectsyms(stubCount);
+ _dynamicSymbolTableCmd->set_extreloff(0);
+ _dynamicSymbolTableCmd->set_nextrel(0);
+ _dynamicSymbolTableCmd->set_locreloff(0);
+ _dynamicSymbolTableCmd->set_nlocrel(0);
+ cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ // LC_UUID
+ _uuidCmd = (macho_uuid_command<P>*)cmd;
+ _uuidCmd->set_cmd(LC_UUID);
+ _uuidCmd->set_cmdsize(sizeof(macho_uuid_command<P>));
+ cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+
+ // write stubs section content
+ _stubInstructions = (uint32_t*)((uint8_t*)mh + _firstStubOffset);
+ for (int i=0; i < stubCount; ++i) {
+ E::set32(_stubInstructions[i], 0xD4200000);
+ }
+
+ // write linkedit content
+ uint8_t* linkeditBufferStart = (uint8_t*)cacheBuffer + poolLinkEditStartOffset;
+ // write symbol table
+ _symbolTable = (macho_nlist<P>*)(linkeditBufferStart);
+ for (int i=0; i < stubCount; ++i) {
+ _symbolTable[i].set_n_strx(1);
+ _symbolTable[i].set_n_type(N_UNDF | N_EXT);
+ _symbolTable[i].set_n_sect(0);
+ _symbolTable[i].set_n_desc(0);
+ _symbolTable[i].set_n_value(0);
+ }
+ // write indirect symbol table
+ uint32_t* indirectSymboTable = (uint32_t*)(linkeditBufferStart + linkeditOffsetIndirectSymbolTable);
+ for (int i=0; i < stubCount; ++i) {
+ P::E::set32(indirectSymboTable[i], i);
+ }
+ // write string pool
+ _stringPoolStart = (char*)(linkeditBufferStart + linkeditOffsetSymbolPoolOffset);
+ _stringPoolEnd = _stringPoolStart + linkEditSegSize - linkeditOffsetSymbolPoolOffset;
+ _stringPoolStart[0] = '\0';
+ strcpy(&_stringPoolStart[1], "<unused>");
+ _nextString = &_stringPoolStart[10];
+}
+
+
+template <typename P>
+void BranchPoolDylib<P>::finalizeLoadCommands()
+{
+ _symbolTableCmd->set_nsyms(_nextIndex);
+ _symbolTableCmd->set_strsize((uint32_t)(_nextString - _stringPoolStart));
+ _dynamicSymbolTableCmd->set_nundefsym(_nextIndex);
+
+ uint8_t digest[CC_MD5_DIGEST_LENGTH];
+ CC_MD5(_stubInstructions, _maxStubs*sizeof(uint64_t), digest);
+ _uuidCmd->set_uuid(digest);
+
+ if ( verbose ) {
+ verboseLog("branch islands in image at 0x%0llX:", _startAddr);
+ for (uint32_t i=0; i < _nextIndex; ++i) {
+ verboseLog(" 0x%llX %s", indexToAddr(i), _islandIndexToName[i]);
+ }
+ }
+}
+
+template <typename P>
+uint64_t BranchPoolDylib<P>::getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& branchIslandPools)
+{
+ // check if we can re-used existing branch island
+ const auto& pos = _targetToIslandIndex.find(finalTargetAddr);
+ if ( pos != _targetToIslandIndex.end() )
+ return indexToAddr(pos->second);
+
+ // skip if instruction pool is full
+ if ( _nextIndex >= _maxStubs )
+ return 0;
+
+ // skip if string pool is full
+ if ( (_nextString + strlen(name)+1) >= _stringPoolEnd )
+ return 0;
+
+ uint64_t branchIslandTargetAddr = finalTargetAddr;
+ // if final target is too far, we need to use branch island in next pool
+ if ( (finalTargetAddr - _startAddr) > b128MegLimit ) {
+ BranchPoolDylib<P>* nextPool = nullptr;
+ for (size_t i=0; i < branchIslandPools.size()-1; ++i) {
+ if ( branchIslandPools[i] == this ) {
+ nextPool = branchIslandPools[i+1];
+ break;
+ }
+ }
+
+ if (nextPool == nullptr) {
+ warning("BranchPoolDylib<P>::getForwardBranch: nextPool unreachable");
+ return 0;
+ }
+
+ branchIslandTargetAddr = nextPool->getForwardBranch(finalTargetAddr, name, branchIslandPools);
+ if ( branchIslandTargetAddr == 0 )
+ return 0; // next pool is full
+ }
+
+ // write branch instruction in stubs section
+ uint32_t index = _nextIndex++;
+ int64_t branchDelta = branchIslandTargetAddr - indexToAddr(index);
+ uint32_t branchInstr = 0x14000000 + ((branchDelta/4) & 0x03FFFFFF);
+ E::set32(_stubInstructions[index], branchInstr);
+
+ // update symbol table
+ _symbolTable[index].set_n_strx((uint32_t)(_nextString - _stringPoolStart));
+ strcpy(_nextString, name);
+ _nextString += (strlen(name) +1);
+
+ // record island
+ _targetToIslandIndex[finalTargetAddr] = index;
+ _islandIndexToName[index] = name;
+ return indexToAddr(index);
+}
+
+template <typename P>
+uint64_t BranchPoolDylib<P>::getBackBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& branchIslandPools)
+{
+ // check if we can re-used existing branch island
+ const auto& pos = _targetToIslandIndex.find(finalTargetAddr);
+ if ( pos != _targetToIslandIndex.end() )
+ return indexToAddr(pos->second);
+
+ // skip if instruction pool is full
+ if ( _nextIndex >= _maxStubs )
+ return 0;
+
+ // skip if string pool is full
+ if ( (_nextString + strlen(name)+1) >= _stringPoolEnd )
+ return 0;
+
+ uint64_t branchIslandTargetAddr = finalTargetAddr;
+ // if final target is too far, we need to use branch island in next pool
+ if ( (indexToAddr(_nextIndex) - finalTargetAddr) > b128MegLimit ) {
+ BranchPoolDylib<P>* nextPool = nullptr;
+ for (long i=branchIslandPools.size()-1; i > 0; --i) {
+ if ( branchIslandPools[i] == this ) {
+ nextPool = branchIslandPools[i-1];
+ break;
+ }
+ }
+
+ if (nextPool == nullptr) {
+ warning("BranchPoolDylib<P>::getBackBranch: nextPool unreachable");
+ return 0;
+ }
+
+ branchIslandTargetAddr = nextPool->getBackBranch(finalTargetAddr, name, branchIslandPools);
+ if ( branchIslandTargetAddr == 0 )
+ return 0; // next pool is full
+ }
+
+ // write branch instruction in stubs section
+ uint32_t index = _nextIndex++;
+ int64_t branchDelta = branchIslandTargetAddr - indexToAddr(index);
+ uint32_t branchInstr = 0x14000000 + ((branchDelta/4) & 0x03FFFFFF);
+ E::set32(_stubInstructions[index], branchInstr);
+
+ // update symbol table
+ _symbolTable[index].set_n_strx((uint32_t)(_nextString - _stringPoolStart));
+ strcpy(_nextString, name);
+ _nextString += (strlen(name) +1);
+
+ // record island
+ _targetToIslandIndex[finalTargetAddr] = index;
+ _islandIndexToName[index] = name;
+ return indexToAddr(index);
+}
+
+template <typename P>
+void BranchPoolDylib<P>::printStats()
+{
+ verboseLog(" island pool at 0x%0llX has %u stubs and stringPool size=%lu", _startAddr, _nextIndex, _nextString-_stringPoolStart);
+}
+
+
+
+template <typename P>
+class StubOptimizer {
+public:
+ StubOptimizer(void* cacheBuffer, macho_header<P>* mh);
+ void buildStubMap(const std::unordered_set<std::string>& neverStubEliminate);
+ void optimizeStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& targetToBranchIslands);
+ void bypassStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& targetToBranchIslands);
+ void optimizeCallSites(std::vector<BranchPoolDylib<P>*>& branchIslandPools);
+ const char* installName() { return _installName; }
+ const uint8_t* exportsTrie() { return (uint8_t*)_cacheBuffer + _dyldInfo->export_off(); }
+ uint32_t exportsTrieSize() { return _dyldInfo->export_size(); }
+
+ uint32_t _stubCount = 0;
+ uint32_t _stubOptimizedCount = 0;
+ uint32_t _branchesCount = 0;
+ uint32_t _branchesModifiedCount = 0;
+ uint32_t _branchesDirectCount = 0;
+ uint32_t _branchesIslandCount = 0;
+
+private:
+
+ typedef std::function<bool(uint8_t callSiteKind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction)> CallSiteHandler;
+ typedef typename P::uint_t pint_t;
+ typedef typename P::E E;
+
+ void forEachCallSiteToAStub(CallSiteHandler);
+ void optimizeArm64CallSites(std::vector<BranchPoolDylib<P>*>& branchIslandPools);
+ void optimizeArmCallSites();
+ void optimizeArmStubs();
+ uint64_t lazyPointerAddrFromArm64Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr);
+ uint32_t lazyPointerAddrFromArmStub(const uint8_t* stubInstructions, uint32_t stubVMAddr);
+ int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t instrAddr);
+ uint32_t setDisplacementInThumbBranch(uint32_t instruction, uint32_t instrAddr,
+ int32_t displacement, bool targetIsThumb);
+
+
+ struct AddressAndName { pint_t targetVMAddr; const char* targetName; };
+ typedef std::unordered_map<pint_t, AddressAndName> StubVMAddrToTarget;
+
+ static const int64_t b128MegLimit = 0x07FFFFFF;
+ static const int64_t b16MegLimit = 0x00FFFFFF;
+
+
+ macho_header<P>* _mh;
+ void* _cacheBuffer;
+ uint32_t _linkeditSize = 0;
+ uint32_t _linkeditCacheOffset = 0;
+ uint64_t _linkeditAddr = 0;
+ const uint8_t* _linkeditBias = nullptr;
+ const char* _installName = nullptr;
+ const macho_symtab_command<P>* _symTabCmd = nullptr;
+ const macho_dysymtab_command<P>* _dynSymTabCmd = nullptr;
+ const macho_dyld_info_command<P>* _dyldInfo = nullptr;
+ macho_linkedit_data_command<P>* _splitSegInfoCmd = nullptr;
+ const macho_section<P>* _textSection = nullptr;
+ const macho_section<P>* _stubSection = nullptr;
+ uint32_t _textSectionIndex = 0;
+ uint32_t _stubSectionIndex = 0;
+ pint_t _textSegStartAddr = 0;
+ uint32_t _textSegCacheOffset = 0;
+ std::vector<macho_segment_command<P>*> _segCmds;
+ std::unordered_map<pint_t, pint_t> _stubAddrToLPAddr;
+ std::unordered_map<pint_t, pint_t> _lpAddrToTargetAddr;
+ std::unordered_map<pint_t, const char*> _targetAddrToName;
+};
+
+
+
+template <typename P>
+StubOptimizer<P>::StubOptimizer(void* cacheBuffer, macho_header<P>* mh)
+: _mh(mh), _cacheBuffer(cacheBuffer)
+{
+ _linkeditBias = (uint8_t*)cacheBuffer;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ const uint32_t cmd_count = mh->ncmds();
+ macho_segment_command<P>* segCmd;
+ uint32_t sectionIndex = 0;
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd()) {
+ case LC_ID_DYLIB:
+ _installName = ((macho_dylib_command<P>*)cmd)->name();
+ break;
+ case LC_SYMTAB:
+ _symTabCmd = (macho_symtab_command<P>*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
+ break;
+ case LC_SEGMENT_SPLIT_INFO:
+ _splitSegInfoCmd = (macho_linkedit_data_command<P>*)cmd;
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ _dyldInfo = (macho_dyld_info_command<P>*)cmd;
+ break;
+ case macho_segment_command<P>::CMD:
+ segCmd =( macho_segment_command<P>*)cmd;
+ _segCmds.push_back(segCmd);
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+ _linkeditSize = (uint32_t)segCmd->vmsize();
+ _linkeditCacheOffset = (uint32_t)segCmd->fileoff();
+ _linkeditAddr = segCmd->vmaddr();
+ }
+ else if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) {
+ _textSegStartAddr = (pint_t)segCmd->vmaddr();
+ _textSegCacheOffset = (uint32_t)((uint8_t*)mh - (uint8_t*)cacheBuffer);
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for (const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ ++sectionIndex;
+ if ( strcmp(sect->sectname(), "__text") == 0 ) {
+ _textSection = sect;
+ _textSectionIndex = sectionIndex;
+ }
+ else if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) {
+ _stubSection = sect;
+ _stubSectionIndex = sectionIndex;
+ }
+ }
+ }
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+}
+
+
+
+template <typename P>
+uint32_t StubOptimizer<P>::lazyPointerAddrFromArmStub(const uint8_t* stubInstructions, uint32_t stubVMAddr)
+{
+ uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions);
+ uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions+4));
+ uint32_t stubInstr3 = E::get32(*(uint32_t*)(stubInstructions+8));
+ int32_t stubData = E::get32(*(uint32_t*)(stubInstructions+12));
+ if ( stubInstr1 != 0xe59fc004 ) {
+ warning("first instruction of stub (0x%08X) is not 'ldr ip, pc + 12' for stub at addr 0x%0llX in %s",
+ stubInstr1, (uint64_t)stubVMAddr, _installName);
+ return 0;
+ }
+ if ( stubInstr2 != 0xe08fc00c ) {
+ warning("second instruction of stub (0x%08X) is not 'add ip, pc, ip' for stub at addr 0x%0llX in %s",
+ stubInstr1, (uint64_t)stubVMAddr, _installName);
+ return 0;
+ }
+ if ( stubInstr3 != 0xe59cf000 ) {
+ warning("third instruction of stub (0x%08X) is not 'ldr pc, [ip]' for stub at addr 0x%0llX in %s",
+ stubInstr1, (uint64_t)stubVMAddr, _installName);
+ return 0;
+ }
+ return stubVMAddr + 12 + stubData;
+}
+
+
+template <typename P>
+uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr)
+{
+ uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions);
+ if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) {
+ warning("first instruction of stub (0x%08X) is not ADRP for stub at addr 0x%0llX in %s",
+ stubInstr1, (uint64_t)stubVMAddr, _installName);
+ return 0;
+ }
+ int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29);
+ if ( stubInstr1 & 0x00800000 )
+ adrpValue |= 0xFFF00000;
+ uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions + 4));
+ if ( (stubInstr2 & 0xFFC003FF) != 0xF9400210 ) {
+ warning("second instruction of stub (0x%08X) is not LDR for stub at addr 0x%0llX in %s",
+ stubInstr2, (uint64_t)stubVMAddr, _installName);
+ return 0;
+ }
+ uint32_t ldrValue = ((stubInstr2 >> 10) & 0x00000FFF);
+ return (stubVMAddr & (-4096)) + adrpValue*4096 + ldrValue*8;
+}
+
+
+
+template <typename P>
+void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& neverStubEliminate)
+{
+ // find all stubs and lazy pointers
+ const macho_nlist<P>* symbolTable = (const macho_nlist<P>*)(((uint8_t*)_cacheBuffer) + _symTabCmd->symoff());
+ const char* symbolStrings = (char*)_cacheBuffer + _symTabCmd->stroff();
+ const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)_cacheBuffer) + _dynSymTabCmd->indirectsymoff());
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)_mh + sizeof(macho_header<P>));
+ const uint32_t cmd_count = _mh->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( sect->size() == 0 )
+ continue;
+ unsigned sectionType = (sect->flags() & SECTION_TYPE);
+ const uint32_t indirectTableOffset = sect->reserved1();
+ if ( sectionType == S_SYMBOL_STUBS ) {
+ const uint32_t stubSize = sect->reserved2();
+ _stubCount = (uint32_t)(sect->size() / stubSize);
+ pint_t stubVMAddr = (pint_t)sect->addr();
+ for (uint32_t j=0; j < _stubCount; ++j, stubVMAddr += stubSize) {
+ uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]);
+ switch ( symbolIndex ) {
+ case INDIRECT_SYMBOL_ABS:
+ case INDIRECT_SYMBOL_LOCAL:
+ break;
+ default:
+ if ( symbolIndex >= _symTabCmd->nsyms() ) {
+ warning("symbol index out of range (%d of %d) for stub at addr 0x%0llX in %s",
+ symbolIndex, _symTabCmd->nsyms(), (uint64_t)stubVMAddr, _installName);
+ continue;
+ }
+ const macho_nlist<P>* sym = &symbolTable[symbolIndex];
+ uint32_t stringOffset = sym->n_strx();
+ if ( stringOffset > _symTabCmd->strsize() ) {
+ warning("symbol string offset out of range (%u of %u) for stub at addr 0x%0llX in %s",
+ stringOffset, sym->n_strx(), (uint64_t)stubVMAddr, _installName);
+ continue;
+ }
+ const char* symName = &symbolStrings[stringOffset];
+ if ( neverStubEliminate.count(symName) ) {
+ //verboseLog("not bypassing stub to %s in %s because target is interposable\n", symName, _installName);
+ continue;
+ }
+ const uint8_t* stubInstrs = (uint8_t*)_cacheBuffer + sect->offset() + stubVMAddr - sect->addr();
+ pint_t targetLPAddr = 0;
+ switch ( _mh->cputype() ) {
+ case CPU_TYPE_ARM64:
+ targetLPAddr = (pint_t)lazyPointerAddrFromArm64Stub(stubInstrs, stubVMAddr);
+ break;
+ case CPU_TYPE_ARM:
+ targetLPAddr = (pint_t)lazyPointerAddrFromArmStub(stubInstrs, (uint32_t)stubVMAddr);
+ break;
+ }
+ if ( targetLPAddr != 0 )
+ _stubAddrToLPAddr[stubVMAddr] = targetLPAddr;
+ break;
+ }
+ }
+ }
+ else if ( sectionType == S_LAZY_SYMBOL_POINTERS ) {
+ pint_t lpVMAddr = (pint_t)sect->addr();
+ pint_t* lpContent = (pint_t*)(((uint8_t*)_cacheBuffer) + sect->offset());
+ uint32_t elementCount = (uint32_t)(sect->size() / sizeof(pint_t));
+ uint64_t textSegStartAddr = _segCmds[0]->vmaddr();
+ uint64_t textSegEndAddr = _segCmds[0]->vmaddr() + _segCmds[0]->vmsize();
+ pint_t lpValue;
+ for (uint32_t j=0; j < elementCount; ++j) {
+ uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]);
+ switch ( symbolIndex ) {
+ case INDIRECT_SYMBOL_ABS:
+ case INDIRECT_SYMBOL_LOCAL:
+ break;
+ default:
+ lpValue = (pint_t)P::getP(lpContent[j]);
+ if ( symbolIndex >= _symTabCmd->nsyms() ) {
+ warning("symbol index out of range (%d of %d) for lazy pointer at addr 0x%0llX in %s",
+ symbolIndex, _symTabCmd->nsyms(), (uint64_t)lpVMAddr, _installName);
+ continue;
+ }
+ const macho_nlist<P>* sym = &symbolTable[symbolIndex];
+ uint32_t stringOffset = sym->n_strx();
+ if ( stringOffset > _symTabCmd->strsize() ) {
+ warning("symbol string offset out of range (%u of %u) for lazy pointer at addr 0x%0llX in %s",
+ stringOffset, sym->n_strx(), (uint64_t)lpVMAddr, _installName);
+ continue;
+ }
+ const char* symName = &symbolStrings[stringOffset];
+ if ( (lpValue > textSegStartAddr) && (lpValue< textSegEndAddr) ) {
+ //verboseLog("skipping lazy pointer at 0x%0lX to %s in %s because target is within dylib\n", lpVMAddr, symName, _installName);
+ }
+ else if ( (sizeof(pint_t) == 8) && ((lpValue % 4) != 0) ) {
+ warning("lazy pointer at 0x%0llX does not point to 4-byte aligned address(0x%0llX) in %s",
+ (uint64_t)lpVMAddr, (uint64_t)lpValue, _installName);
+ }
+ else {
+ _lpAddrToTargetAddr[lpVMAddr] = lpValue;
+ _targetAddrToName[lpValue] = symName;
+ }
+ break;
+ }
+ lpVMAddr += sizeof(pint_t);
+ }
+ }
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+}
+
+
+template <typename P>
+void StubOptimizer<P>::forEachCallSiteToAStub(CallSiteHandler handler)
+{
+ const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
+ const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];
+ if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT )
+ terminate("malformed split seg info in %s", _installName);
+
+ uint8_t* textSectionContent = (uint8_t*)_cacheBuffer + _textSegCacheOffset + _textSection->addr() -_textSegStartAddr;
+
+ // Whole :== <count> FromToSection+
+ // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
+ // ToOffset :== <to-sect-offset-delta> <count> FromOffset+
+ // FromOffset :== <kind> <count> <from-sect-offset-delta>
+ const uint8_t* p = infoStart;
+ uint64_t sectionCount = read_uleb128(p, infoEnd);
+ for (uint64_t i=0; i < sectionCount; ++i) {
+ uint64_t fromSectionIndex = read_uleb128(p, infoEnd);
+ uint64_t toSectionIndex = read_uleb128(p, infoEnd);
+ uint64_t toOffsetCount = read_uleb128(p, infoEnd);
+ uint64_t toSectionOffset = 0;
+ for (uint64_t j=0; j < toOffsetCount; ++j) {
+ uint64_t toSectionDelta = read_uleb128(p, infoEnd);
+ uint64_t fromOffsetCount = read_uleb128(p, infoEnd);
+ toSectionOffset += toSectionDelta;
+ for (uint64_t k=0; k < fromOffsetCount; ++k) {
+ uint64_t kind = read_uleb128(p, infoEnd);
+ if ( kind > 12 )
+ terminate("bad kind (%llu) value in %s", kind, _installName);
+ uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
+ uint64_t fromSectionOffset = 0;
+ for (uint64_t l=0; l < fromSectDeltaCount; ++l) {
+ uint64_t delta = read_uleb128(p, infoEnd);
+ fromSectionOffset += delta;
+ if ( (fromSectionIndex == _textSectionIndex) && (toSectionIndex == _stubSectionIndex) ) {
+ uint32_t* instrPtr = (uint32_t*)(textSectionContent + fromSectionOffset);
+ uint64_t instrAddr = _textSection->addr() + fromSectionOffset;
+ uint64_t stubAddr = _stubSection->addr() + toSectionOffset;
+ uint32_t instruction = E::get32(*instrPtr);
+ _branchesCount++;
+ if ( handler(kind, instrAddr, stubAddr, instruction) ) {
+ _branchesModifiedCount++;
+ E::set32(*instrPtr, instruction);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/// Extract displacement from a thumb b/bl/blx instruction.
+template <typename P>
+int32_t StubOptimizer<P>::getDisplacementFromThumbBranch(uint32_t instruction, uint32_t instrAddr)
+{
+ bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
+ uint32_t s = (instruction >> 10) & 0x1;
+ uint32_t j1 = (instruction >> 29) & 0x1;
+ uint32_t j2 = (instruction >> 27) & 0x1;
+ uint32_t imm10 = instruction & 0x3FF;
+ uint32_t imm11 = (instruction >> 16) & 0x7FF;
+ uint32_t i1 = (j1 == s);
+ uint32_t i2 = (j2 == s);
+ uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
+ int32_t sdis = dis;
+ int32_t result = s ? (sdis | 0xFE000000) : sdis;
+ if ( is_blx && (instrAddr & 0x2) ) {
+ // The thumb blx instruction always has low bit of imm11 as zero. The way
+ // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that
+ // the blx instruction always 4-byte aligns the pc before adding the
+ // displacement from the blx. We must emulate that when decoding this.
+ result -= 2;
+ }
+ return result;
+}
+
+/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed.
+template <typename P>
+uint32_t StubOptimizer<P>::setDisplacementInThumbBranch(uint32_t instruction, uint32_t instrAddr,
+ int32_t displacement, bool targetIsThumb) {
+ if ( (displacement > 16777214) || (displacement < (-16777216)) )
+ terminate("thumb branch out of range at 0x%0X in %s", instrAddr, _installName);
+ bool is_bl = ((instruction & 0xD000F800) == 0xD000F000);
+ bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
+ bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
+ uint32_t newInstruction = (instruction & 0xD000F800);
+ if (is_bl || is_blx) {
+ if (targetIsThumb) {
+ newInstruction = 0xD000F000; // Use bl
+ }
+ else {
+ newInstruction = 0xC000F000; // Use blx
+ // See note in getDisplacementFromThumbBranch() about blx.
+ if (instrAddr & 0x2)
+ displacement += 2;
+ }
+ }
+ else if (is_b) {
+ if ( !targetIsThumb )
+ terminate("no pc-rel thumb branch instruction that switches to arm mode at 0x%0X in %s", instrAddr, _installName);
+ }
+ else {
+ terminate("not b/bl/blx at 0x%0X in %s", instrAddr, _installName);
+ }
+ uint32_t s = (uint32_t)(displacement >> 24) & 0x1;
+ uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1;
+ uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1;
+ uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF;
+ uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF;
+ uint32_t j1 = (i1 == s);
+ uint32_t j2 = (i2 == s);
+ uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11;
+ uint32_t firstDisp = (s << 10) | imm10;
+ newInstruction |= (nextDisp << 16) | firstDisp;
+ return newInstruction;
+}
+
+
+template <typename P>
+void StubOptimizer<P>::optimizeArmCallSites()
+{
+ forEachCallSiteToAStub([&](uint8_t kind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction) -> bool {
+ if ( kind == DYLD_CACHE_ADJ_V2_THUMB_BR22 ) {
+ bool is_bl = ((instruction & 0xD000F800) == 0xD000F000);
+ bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
+ bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
+ if ( !is_bl && !is_blx && !is_b ){
+ warning("non-branch instruction at 0x%0llX in %s", callSiteAddr, _installName);
+ return false;
+ }
+ int32_t brDelta = getDisplacementFromThumbBranch(instruction, (uint32_t)callSiteAddr);
+ pint_t targetAddr = (pint_t)callSiteAddr + 4 + brDelta;
+ if ( targetAddr != stubAddr ) {
+ warning("stub target mismatch at callsite 0x%0llX in %s", callSiteAddr, _installName);
+ return false;
+ }
+ // ignore branch if not to a known stub
+ const auto& pos = _stubAddrToLPAddr.find(targetAddr);
+ if ( pos == _stubAddrToLPAddr.end() )
+ return false;
+ // ignore branch if lazy pointer is not known (could be resolver based)
+ pint_t lpAddr = pos->second;
+ const auto& pos2 = _lpAddrToTargetAddr.find(lpAddr);
+ if ( pos2 == _lpAddrToTargetAddr.end() )
+ return false;
+ uint64_t finalTargetAddr = pos2->second;
+ int64_t deltaToFinalTarget = finalTargetAddr - (callSiteAddr + 4);
+ // if final target within range, change to branch there directly
+ if ( (deltaToFinalTarget > -b16MegLimit) && (deltaToFinalTarget < b16MegLimit) ) {
+ bool targetIsThumb = finalTargetAddr & 1;
+ instruction = setDisplacementInThumbBranch(instruction, (uint32_t)callSiteAddr, (int32_t)deltaToFinalTarget, targetIsThumb);
+ _branchesDirectCount++;
+ return true;
+ }
+ }
+ else if ( kind == DYLD_CACHE_ADJ_V2_ARM_BR24 ) {
+ // too few of these to be worth trying to optimize
+ }
+
+ return false;
+ });
+}
+
+
+template <typename P>
+void StubOptimizer<P>::optimizeArmStubs()
+{
+ for (const auto& stubEntry : _stubAddrToLPAddr) {
+ pint_t stubVMAddr = stubEntry.first;
+ pint_t lpVMAddr = stubEntry.second;
+ const auto& pos = _lpAddrToTargetAddr.find(lpVMAddr);
+ if ( pos == _lpAddrToTargetAddr.end() )
+ return;
+ pint_t targetVMAddr = pos->second;
+
+ int32_t delta = (int32_t)(targetVMAddr - (stubVMAddr + 12));
+ const uint32_t* stubInstructions = (uint32_t*)((uint8_t*)_cacheBuffer + _stubSection->offset() + stubVMAddr - _stubSection->addr());
+ E::set32(*(uint32_t*)&stubInstructions[0], 0xe59fc000); // ldr ip, L0
+ E::set32(*(uint32_t*)&stubInstructions[1], 0xe08ff00c); // add pc, pc, ip
+ E::set32(*(uint32_t*)&stubInstructions[2], delta); // L0: .long xxxx
+ E::set32(*(uint32_t*)&stubInstructions[3], 0xe7ffdefe); // trap
+ _stubOptimizedCount++;
+ }
+}
+
+
+
+
+template <typename P>
+void StubOptimizer<P>::optimizeArm64CallSites(std::vector<BranchPoolDylib<P>*>& branchIslandPools)
+{
+ forEachCallSiteToAStub([&](uint8_t kind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction) -> bool {
+ if ( kind != DYLD_CACHE_ADJ_V2_ARM64_BR26 )
+ return false;
+ // skip all but BL or B
+ if ( (instruction & 0x7C000000) != 0x14000000 )
+ return false;
+ // compute target of branch instruction
+ int32_t brDelta = (instruction & 0x03FFFFFF) << 2;
+ if ( brDelta & 0x08000000 )
+ brDelta |= 0xF0000000;
+ uint64_t targetAddr = callSiteAddr + (int64_t)brDelta;
+ if ( targetAddr != stubAddr ) {
+ warning("stub target mismatch");
+ return false;
+ }
+ // ignore branch if not to a known stub
+ const auto& pos = _stubAddrToLPAddr.find((pint_t)targetAddr);
+ if ( pos == _stubAddrToLPAddr.end() )
+ return false;
+ // ignore branch if lazy pointer is not known (could be resolver based)
+ uint64_t lpAddr = pos->second;
+ const auto& pos2 = _lpAddrToTargetAddr.find((pint_t)lpAddr);
+ if ( pos2 == _lpAddrToTargetAddr.end() )
+ return false;
+ uint64_t finalTargetAddr = pos2->second;
+ int64_t deltaToFinalTarget = finalTargetAddr - callSiteAddr;
+ // if final target within range, change to branch there directly
+ if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) {
+ instruction= (instruction & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF);
+ _branchesDirectCount++;
+ return true;
+ }
+ // find closest branch island pool between instruction and target and get island
+ const auto& pos3 = _targetAddrToName.find((pint_t)finalTargetAddr);
+ if ( pos3 == _targetAddrToName.end() )
+ return false;
+ const char* targetName = pos3->second;
+ if ( finalTargetAddr > callSiteAddr ) {
+ // target is after branch so find first pool after branch
+ for ( BranchPoolDylib<P>* pool : branchIslandPools ) {
+ if ( (pool->addr() > callSiteAddr) && (pool->addr() < finalTargetAddr) ) {
+ uint64_t brIslandAddr = pool->getForwardBranch(finalTargetAddr, targetName, branchIslandPools);
+ if ( brIslandAddr == 0 ) {
+ // branch island pool full
+ warning("pool full. Can't optimizer branch to %s from 0x%llX in %s\n", targetName, callSiteAddr, _installName);
+ break;
+ }
+ int64_t deltaToTarget = brIslandAddr - callSiteAddr;
+ instruction = (instruction & 0xFC000000) | ((deltaToTarget >> 2) & 0x03FFFFFF);
+ _branchesIslandCount++;
+ return true;
+ }
+ }
+ }
+ else {
+ // target is before branch so find closest pool before branch
+ for (size_t j = branchIslandPools.size(); j > 0; --j) {
+ BranchPoolDylib<P>* pool = branchIslandPools[j-1];
+ if ( (pool->addr() < callSiteAddr) && (pool->addr() > finalTargetAddr) ) {
+ uint64_t brIslandAddr = pool->getBackBranch(finalTargetAddr, targetName, branchIslandPools);
+ if ( brIslandAddr == 0 ) {
+ // branch island pool full
+ warning("pool full. Can't optimizer branch to %s from 0x%llX in %s\n", targetName, callSiteAddr, _installName);
+ break;
+ }
+ int64_t deltaToTarget = brIslandAddr - callSiteAddr;
+ instruction = (instruction & 0xFC000000) | ((deltaToTarget >> 2) & 0x03FFFFFF);
+ _branchesIslandCount++;
+ return true;
+ }
+ }
+ }
+ return false;
+ });
+}
+
+
+template <typename P>
+void StubOptimizer<P>::optimizeCallSites(std::vector<BranchPoolDylib<P>*>& branchIslandPools)
+{
+ if ( _textSection == NULL )
+ return;
+ if ( _stubSection == NULL )
+ return;
+
+
+ switch ( _mh->cputype() ) {
+ case CPU_TYPE_ARM64:
+ optimizeArm64CallSites(branchIslandPools);
+ if ( verbose ) {
+ verboseLog("%5u branches in __text, %5u changed to direct branches, %5u changed to use islands for %s",
+ _branchesCount, _branchesDirectCount, _branchesIslandCount, _installName);
+ }
+ break;
+ case CPU_TYPE_ARM:
+ optimizeArmCallSites();
+ optimizeArmStubs();
+ if ( verbose ) {
+ verboseLog("%3u of %3u stubs optimized. %5u branches in __text, %5u changed to direct branches for %s",
+ _stubOptimizedCount, _stubCount, _branchesCount, _branchesDirectCount, _installName);
+ }
+ break;
+ }
+}
+
+
+template <typename P>
+void SharedCache::bypassStubs(const std::vector<uint64_t>& branchPoolStartAddrs)
+{
+ verboseLog("Stub elimination optimization:");
+
+ // construct a StubOptimizer for each image
+ std::vector<StubOptimizer<P>*> optimizers;
+ forEachImage([&](const void* mh, const char*, time_t, ino_t, const std::vector<MachOProxy::Segment>&) {
+ optimizers.push_back(new StubOptimizer<P>(_buffer.get(), (macho_header<P>*)mh));
+ });
+
+ // construct a BranchPoolDylib for each pool
+ std::vector<BranchPoolDylib<P>*> pools;
+
+ if ( _arch.arch == CPU_TYPE_ARM64 ) {
+ // Find hole at end of linkedit region for branch pool linkedits
+ uint64_t textRegionStartAddr = 0;
+ uint64_t linkEditRegionStartAddr = 0;
+ uint64_t linkEditRegionEndAddr = 0;
+ uint64_t linkEditRegionStartCacheOffset = 0;
+ forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ if ( permissions == (PROT_READ|PROT_EXEC) ) {
+ textRegionStartAddr = vmAddr;
+ }
+ else if ( permissions == PROT_READ ) {
+ linkEditRegionStartAddr = vmAddr;
+ linkEditRegionEndAddr = vmAddr + size;
+ linkEditRegionStartCacheOffset = (char*)content - (char*)_buffer.get();
+ }
+ });
+ uint64_t lastLinkEditRegionUsedOffset = 0;
+ forEachImage([&](const void* mh, const char*, time_t, ino_t, const std::vector<MachOProxy::Segment>& segs) {
+ for (MachOProxy::Segment seg : segs) {
+ if ( seg.name != "__LINKEDIT" )
+ continue;
+ if ( seg.fileOffset >= lastLinkEditRegionUsedOffset )
+ lastLinkEditRegionUsedOffset = seg.fileOffset + seg.size;
+ }
+ });
+ uint64_t allPoolsLinkEditStartOffset = lastLinkEditRegionUsedOffset;
+ uint64_t allPoolsLinkEditStartAddr = linkEditRegionStartAddr + allPoolsLinkEditStartOffset - linkEditRegionStartCacheOffset;
+ uint64_t allPoolsLinkEditSize = linkEditRegionEndAddr - allPoolsLinkEditStartAddr;
+ if ( !branchPoolStartAddrs.empty() ) {
+ uint64_t poolLinkEditStartAddr = allPoolsLinkEditStartAddr;
+ uint64_t poolLinkEditStartOffset = allPoolsLinkEditStartOffset;
+ const uint64_t poolSize = (allPoolsLinkEditSize/branchPoolStartAddrs.size()) & (-4096);
+ for (uint64_t poolAddr : branchPoolStartAddrs) {
+ pools.push_back(new BranchPoolDylib<P>(_arch, _buffer.get(), _fileSize, poolAddr,
+ textRegionStartAddr, poolLinkEditStartAddr, poolLinkEditStartOffset));
+ poolLinkEditStartAddr += poolSize;
+ poolLinkEditStartOffset += poolSize;
+ }
+ }
+ }
+
+ // build set of functions to never stub-eliminate because tools may need to override them
+ std::unordered_set<std::string> neverStubEliminate;
+ for (const char** p=sNeverStubEliminateSymbols; *p != nullptr; ++p) {
+ neverStubEliminate.insert(*p);
+ }
+ for (const char** d=sNeverStubEliminateDylibs; *d != nullptr; ++d) {
+ for (StubOptimizer<P>* op : optimizers) {
+ if ( strcmp(op->installName(), *d) == 0 ) {
+ // add all exports
+ const uint8_t* exportsStart = op->exportsTrie();
+ const uint8_t* exportsEnd = exportsStart + op->exportsTrieSize();
+ std::vector<ExportInfoTrie::Entry> exports;
+ if ( !ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports) ) {
+ terminate("malformed exports trie in %s", *d);
+ }
+ for(const ExportInfoTrie::Entry& entry : exports) {
+ neverStubEliminate.insert(entry.name);
+ }
+ }
+ }
+ }
+
+ // build maps of stubs-to-lp and lp-to-target
+ for (StubOptimizer<P>* op : optimizers)
+ op->buildStubMap(neverStubEliminate);
+
+ // optimize call sites to by-pass stubs or jump through island
+ for (StubOptimizer<P>* op : optimizers)
+ op->optimizeCallSites(pools);
+
+ // final fix ups in branch pools
+ for (BranchPoolDylib<P>* pool : pools) {
+ pool->finalizeLoadCommands();
+ pool->printStats();
+ }
+
+ // write total optimization info
+ uint32_t callSiteCount = 0;
+ uint32_t callSiteDirectOptCount = 0;
+ uint32_t callSiteOneHopOptCount = 0;
+ for (StubOptimizer<P>* op : optimizers) {
+ callSiteCount += op->_branchesCount;
+ callSiteDirectOptCount += op->_branchesDirectCount;
+ callSiteOneHopOptCount += op->_branchesIslandCount;
+ }
+ verboseLog(" cache contains %u call sites of which %u were direct bound and %u were bound through islands", callSiteCount, callSiteDirectOptCount, callSiteOneHopOptCount);
+
+ // clean up
+ for (StubOptimizer<P>* op : optimizers)
+ delete op;
+ for (BranchPoolDylib<P>* p : pools)
+ delete p;
+
+}
+
+
+void SharedCache::bypassStubs(const std::vector<uint64_t>& branchPoolStartAddrs) {
+ switch( _arch.arch ) {
+ case CPU_TYPE_ARM:
+ bypassStubs<Pointer32<LittleEndian>>(branchPoolStartAddrs);
+ break;
+ case CPU_TYPE_ARM64:
+ bypassStubs<Pointer64<LittleEndian>>(branchPoolStartAddrs);
+ break;
+ default:
+ // no stub optimization done for other arches
+ break;
+ }
+}
+
+
+/*
+template <typename P>
+void StubOptimizer<P>::optimizeStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& targetToBranchIslands)
+{
+ for (const auto& stubEntry : _stubAddrToLPAddr) {
+ pint_t stubVMAddr = stubEntry.first;
+ pint_t lpVMAddr = stubEntry.second;
+ const auto& pos = _lpAddrToTargetAddr.find(lpVMAddr);
+ if ( pos == _lpAddrToTargetAddr.end() )
+ continue;
+ pint_t targetVMAddr = pos->second;
+ int64_t delta = targetVMAddr - stubVMAddr;
+ if ( (delta > -b128MegLimit) && (delta < b128MegLimit) ) {
+ // target within reach, change stub to direct branch
+ uint32_t* stubInstructions = (uint32_t*)((uint8_t*)_cacheBuffer + _textSegCacheOffset + stubVMAddr -_textSegStartAddr);
+ uint32_t stubInstr1 = E::get32(stubInstructions[0]);
+ if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) {
+ warning("first instruction of stub (0x%08X) is no longer ADRP for stub at addr 0x%0X in %s\n",
+ stubInstr1, stubVMAddr, _installName);
+ continue;
+ }
+ uint32_t directBranchInstr = 0x14000000 + ((delta/4) & 0x03FFFFFF);
+ E::set32(stubInstructions[0], directBranchInstr);
+ uint32_t brkInstr = 0xD4200000;
+ E::set32(stubInstructions[1], brkInstr);
+ E::set32(stubInstructions[2], brkInstr);
+ _stubOptimizedCount++;
+ targetToBranchIslands[targetVMAddr].push_back(stubVMAddr);
+ }
+ }
+ verboseLog("%3u of %3u stubs optimized for %s\n", _stubOptimizedCount, _stubCount, _installName);
+}
+
+
+template <typename P>
+void StubOptimizer<P>::bypassStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& targetToBranchIslands)
+{
+ if ( _textSection == NULL )
+ return;
+
+ // scan __text section looking for B(L) instructions that branch to a stub
+ unsigned instructionCount = (unsigned)(_textSection->size() / 4);
+ uint32_t* instructions = (uint32_t*)((uint8_t*)_cacheBuffer + _textSegCacheOffset + _textSection->addr() -_textSegStartAddr);
+ for (unsigned i=0; i < instructionCount; ++i) {
+ uint32_t instr = E::get32(instructions[i]);
+ // skip all but BL or B
+ if ( (instr & 0x7C000000) != 0x14000000 )
+ continue;
+ // compute target of branch instruction
+ int32_t brDelta = (instr & 0x03FFFFFF) << 2;
+ if ( brDelta & 0x08000000 )
+ brDelta |= 0xF0000000;
+ uint64_t branchAddr = _textSection->addr() + i*4;
+ uint64_t targetAddr = branchAddr + (int64_t)brDelta;
+ // ignore branch if not to a known stub
+ const auto& pos = _stubAddrToLPAddr.find(targetAddr);
+ if ( pos == _stubAddrToLPAddr.end() )
+ continue;
+ _branchesCount++;
+ // ignore branch if lazy pointer is not known (could be resolver based)
+ const auto& pos2 = _lpAddrToTargetAddr.find(pos->second);
+ if ( pos2 == _lpAddrToTargetAddr.end() )
+ continue;
+ uint64_t finalTargetAddr = pos2->second;
+ int64_t deltaToFinalTarget = finalTargetAddr - branchAddr;
+ // if final target within range, change to branch there directly
+ if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) {
+ uint32_t newInstr = (instr & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF);
+ E::set32(instructions[i], newInstr);
+ _branchesDirectCount++;
+ continue;
+ }
+ // see if there is an existing branch island in range that can be used
+ std::vector<uint64_t>& existingBranchIslands = targetToBranchIslands[finalTargetAddr];
+ for (uint64_t branchIslandAddr : existingBranchIslands) {
+ int64_t deltaToBranchIsland = branchIslandAddr - branchAddr;
+ // if final target within range, change to branch deltaToBranchIsland directly
+ if ( (deltaToBranchIsland > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) {
+ uint32_t newInstr = (instr & 0xFC000000) | ((deltaToBranchIsland >> 2) & 0x03FFFFFF);
+ E::set32(instructions[i], newInstr);
+ _branchesIslandCount++;
+ break;
+ }
+ }
+ }
+ if ( verbose ) {
+ verboseLog("%5u branches in __text, %5u changed to direct branches, %5u changed to indirect for %s\n",
+ _branchesCount, _branchesDirectCount, _branchesIslandCount, _installName);
+ }
+}
+*/
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 __OPTIMIZER_BRANCHES_H__
+#define __OPTIMIZER_BRANCHES_H__
+
+#include "mega-dylib-utils.h"
+
+//FIXME: merge these into the SharedCache object and delete this header
+uint64_t branchPoolTextSize(ArchPair arch);
+uint64_t branchPoolLinkEditSize(ArchPair arch);
+uint64_t branchReach(ArchPair arch);
+
+#endif // __OPTIMIZER_BRANCHES_H__
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 "mega-dylib-utils.h"
+#include "MachOFileAbstraction.hpp"
+#include "Logging.h"
+
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <assert.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "dyld_cache_config.h"
+#include "Trie.hpp"
+
+#if !NEW_CACHE_FILE_FORMAT
+ #include "CacheFileAbstraction.hpp"
+#endif
+
+#define ALIGN_AS_TYPE(value, type) \
+ ((value + alignof(type) - 1) & (-alignof(type)))
+
+namespace {
+
+template <typename P>
+class SortedStringPool
+{
+public:
+ // add a string and symbol table entry index to be updated later
+ void add(uint32_t symbolIndex, const char* symbolName) {
+ _map[symbolName].push_back(symbolIndex);
+ }
+
+ // copy sorted strings to buffer and update all symbol's string offsets
+ uint32_t copyPoolAndUpdateOffsets(char* dstStringPool, macho_nlist<P>* symbolTable) {
+ // make sorted list of strings
+ std::vector<std::string> allStrings;
+ allStrings.reserve(_map.size());
+ for (auto& entry : _map) {
+ allStrings.push_back(entry.first);
+ }
+ std::sort(allStrings.begin(), allStrings.end());
+ // walk sorted list of strings
+ dstStringPool[0] = '\0'; // tradition for start of pool to be empty string
+ uint32_t poolOffset = 1;
+ for (const std::string& symName : allStrings) {
+ // append string to pool
+ strcpy(&dstStringPool[poolOffset], symName.c_str());
+ // set each string offset of each symbol using it
+ for (uint32_t symbolIndex : _map[symName]) {
+ symbolTable[symbolIndex].set_n_strx(poolOffset);
+ }
+ poolOffset += symName.size() + 1;
+ }
+ // return size of pool
+ return poolOffset;
+ }
+
+private:
+ std::unordered_map<std::string, std::vector<uint32_t>> _map;
+};
+
+
+
+
+struct LocalSymbolInfo
+{
+ uint32_t dylibOffset;
+ uint32_t nlistStartIndex;
+ uint32_t nlistCount;
+};
+
+
+template <typename P>
+class LinkeditOptimizer {
+public:
+ LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh);
+
+ uint32_t linkeditSize() { return _linkeditSize; }
+ uint32_t linkeditOffset() { return _linkeditCacheOffset; }
+ uint64_t linkeditAddr() { return _linkeditAddr; }
+ const char* installName() { return _installName; }
+ void copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
+ void copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
+ void copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
+ void copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset);
+ void copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex);
+ void copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex);
+ void copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex,
+ bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
+ std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& localSymbolsStringPool);
+ void copyFunctionStarts(uint8_t* newLinkEditContent, uint32_t& offset);
+ void copyDataInCode(uint8_t* newLinkEditContent, uint32_t& offset);
+ void copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset);
+ void updateLoadCommands(uint32_t linkeditStartOffset, uint64_t mergedLinkeditAddr, uint64_t newLinkeditSize,
+ uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount,
+ uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize);
+
+ macho_header<P>* machHeader() { return _mh; }
+ const std::vector<const char*> getDownwardDependents() { return _downDependentPaths; }
+ const std::vector<const char*> getAllDependents() { return _allDependentPaths; }
+ const std::vector<const char*> getReExportPaths() { return _reExportPaths; }
+ const std::vector<uint64_t> initializerAddresses() { return _initializerAddresses; }
+ const std::vector<macho_section<P>*> dofSections() { return _dofSections; }
+ uint32_t exportsTrieLinkEditOffset() { return _newExportInfoOffset; }
+ uint32_t exportsTrieLinkEditSize() { return _exportInfoSize; }
+ uint32_t weakBindingLinkEditOffset() { return _newWeakBindingInfoOffset; }
+ uint32_t weakBindingLinkEditSize() { return _newWeakBindingSize; }
+ uint64_t dyldSectionAddress() { return _dyldSectionAddr; }
+ const std::vector<macho_segment_command<P>*>& segCmds() { return _segCmds; }
+
+
+private:
+
+ typedef typename P::uint_t pint_t;
+ typedef typename P::E E;
+
+ macho_header<P>* _mh;
+ void* _cacheBuffer;
+ uint32_t _linkeditSize = 0;
+ uint32_t _linkeditCacheOffset = 0;
+ uint64_t _linkeditAddr = 0;
+ const uint8_t* _linkeditBias = nullptr;
+ const char* _installName = nullptr;
+ macho_symtab_command<P>* _symTabCmd = nullptr;
+ macho_dysymtab_command<P>* _dynSymTabCmd = nullptr;
+ macho_dyld_info_command<P>* _dyldInfo = nullptr;
+ macho_linkedit_data_command<P>* _functionStartsCmd = nullptr;
+ macho_linkedit_data_command<P>* _dataInCodeCmd = nullptr;
+ std::vector<macho_segment_command<P>*> _segCmds;
+ std::unordered_map<uint32_t,uint32_t> _oldToNewSymbolIndexes;
+ std::vector<const char*> _reExportPaths;
+ std::vector<const char*> _downDependentPaths;
+ std::vector<const char*> _allDependentPaths;
+ std::vector<uint64_t> _initializerAddresses;
+ std::vector<macho_section<P>*> _dofSections;
+ uint32_t _newWeakBindingInfoOffset = 0;
+ uint32_t _newLazyBindingInfoOffset = 0;
+ uint32_t _newBindingInfoOffset = 0;
+ uint32_t _newExportInfoOffset = 0;
+ uint32_t _exportInfoSize = 0;
+ uint32_t _newWeakBindingSize = 0;
+ uint32_t _newExportedSymbolsStartIndex = 0;
+ uint32_t _newExportedSymbolCount = 0;
+ uint32_t _newImportedSymbolsStartIndex = 0;
+ uint32_t _newImportedSymbolCount = 0;
+ uint32_t _newLocalSymbolsStartIndex = 0;
+ uint32_t _newLocalSymbolCount = 0;
+ uint32_t _newFunctionStartsOffset = 0;
+ uint32_t _newDataInCodeOffset = 0;
+ uint32_t _newIndirectSymbolTableOffset = 0;
+ uint64_t _dyldSectionAddr = 0;
+};
+
+
+
+template <typename P>
+class AcceleratorTables {
+public:
+ AcceleratorTables(void* cacheBuffer, uint64_t linkeditStartAddr, const std::vector<LinkeditOptimizer<P>*>& optimizers);
+
+ uint32_t totalSize() const;
+ void copyTo(uint8_t* buffer);
+
+private:
+ typedef typename P::E E;
+
+ struct NodeChain;
+
+ struct DepNode {
+ std::vector<DepNode*> _dependents;
+ unsigned _depth;
+ const char* _installName;
+
+ DepNode() : _depth(0), _installName(nullptr) { }
+ void computeDepth();
+ static void verifyUnreachable(DepNode* target, NodeChain& chain, std::unordered_set<DepNode*>& visitedNodes, const std::vector<DepNode*>& from);
+ };
+
+ struct NodeChain {
+ NodeChain* prev;
+ DepNode* node;
+ };
+
+ std::unordered_map<macho_header<P>*, DepNode> _depDAG;
+ std::vector<dyldCacheImageInfoExtra<E>> _extraInfo;
+ std::vector<uint8_t> _trieBytes;
+ std::vector<uint16_t> _reExportArray;
+ std::vector<uint16_t> _dependencyArray;
+ std::vector<uint16_t> _bottomUpArray;
+ std::vector<dyldCacheAcceleratorInitializer<E>> _initializers;
+ std::vector<dyldCacheAcceleratorDOFEntry<E>> _dofSections;
+ std::vector<dyldCacheAcceleratorRangeEntry<E>> _rangeTable;
+ std::unordered_map<macho_header<P>*, uint32_t> _machHeaderToImageIndex;
+ std::unordered_map<std::string, macho_header<P>*> _dylibPathToMachHeader;
+ std::unordered_map<macho_header<P>*, LinkeditOptimizer<P>*> _machHeaderToOptimizer;
+ dyldCacheAcceleratorInfo<E> _acceleratorInfoHeader;
+};
+
+
+template <typename P>
+void AcceleratorTables<P>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables<P>::DepNode* target, struct AcceleratorTables<P>::NodeChain& chain,
+ std::unordered_set<DepNode*>& visitedNodes, const std::vector<AcceleratorTables<P>::DepNode*>& from) {
+ for (DepNode* node : from) {
+ bool foundCycle = (node == target);
+ for (NodeChain* c = &chain; c->prev != nullptr; c = c->prev) {
+ if ( c->node == node )
+ foundCycle = true;
+ }
+ if ( foundCycle ) {
+ NodeChain* chp = &chain;
+ std::string msg = std::string("found cycle for ") + target->_installName;
+ while (chp != nullptr) {
+ msg = msg + "\n " + chp->node->_installName;
+ chp = chp->prev;
+ }
+ warning("%s", msg.c_str());
+ return;
+ }
+
+ if ( visitedNodes.count(node) )
+ continue;
+ NodeChain nextChain;
+ nextChain.prev = &chain;
+ nextChain.node = node;
+ verifyUnreachable(target, nextChain, visitedNodes, node->_dependents);
+ visitedNodes.insert(node);
+ }
+}
+
+const uint16_t kBranchIslandDylibIndex = 0x7FFF;
+
+template <typename P>
+AcceleratorTables<P>::AcceleratorTables(void* cacheBuffer, uint64_t linkeditStartAddr, const std::vector<LinkeditOptimizer<P>*>& optimizers)
+{
+ // build table mapping tables to map between mach_header, index, and optimizer
+ for ( LinkeditOptimizer<P>* op : optimizers ) {
+ _machHeaderToOptimizer[op->machHeader()] = op;
+ }
+ uint64_t cacheStartAddress;
+#if NEW_CACHE_FILE_FORMAT
+ #error new format support not implemented
+#else
+ typedef typename P::E E;
+ const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cacheBuffer;
+ const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)((uint8_t*)cacheBuffer + header->mappingOffset());
+ cacheStartAddress = mappings[0].address();
+ const dyldCacheImageInfo<E>* images = (dyldCacheImageInfo<E>*)((uint8_t*)cacheBuffer + header->imagesOffset());
+ const unsigned imageCount = header->imagesCount();
+ for (unsigned i=0; i < imageCount; ++i) {
+ uint64_t segCacheFileOffset = images[i].address() - cacheStartAddress;
+ macho_header<P>* mhMapped = (macho_header<P>*)((uint8_t*)cacheBuffer+segCacheFileOffset);
+ const char* path = (char*)cacheBuffer + images[i].pathFileOffset();
+ _dylibPathToMachHeader[path] = mhMapped;
+ // don't add alias entries (path offset in pool near start of cache) to header->index map
+ if ( images[i].pathFileOffset() > segCacheFileOffset )
+ _machHeaderToImageIndex[mhMapped] = i;
+ }
+#endif
+
+ // build DAG of image dependencies
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ _depDAG[op->machHeader()]._installName = op->installName();
+ }
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ DepNode& node = _depDAG[op->machHeader()];
+ for (const char* depPath : op->getDownwardDependents()) {
+ macho_header<P>* depMH = _dylibPathToMachHeader[depPath];
+ assert(depMH != NULL);
+ DepNode* depNode = &_depDAG[depMH];
+ node._dependents.push_back(depNode);
+ }
+ }
+
+ // check for cycles in DAG
+ for (auto& entry : _depDAG) {
+ DepNode* node = &entry.second;
+ NodeChain chain;
+ chain.prev = nullptr;
+ chain.node = node;
+ std::unordered_set<DepNode*> visitedNodes;
+ DepNode::verifyUnreachable(node, chain, visitedNodes, node->_dependents);
+ }
+
+ // compute depth for each DAG node
+ for (auto& entry : _depDAG) {
+ entry.second.computeDepth();
+ }
+
+ // build sorted (bottom up) list of images
+ std::vector<macho_header<P>*> sortedMachHeaders;
+ sortedMachHeaders.reserve(optimizers.size());
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ if ( strcmp(op->installName(), "dyld_shared_cache_branch_islands") != 0 )
+ sortedMachHeaders.push_back(op->machHeader());
+ else
+ _machHeaderToImageIndex[op->machHeader()] = kBranchIslandDylibIndex;
+ }
+ std::sort(sortedMachHeaders.begin(), sortedMachHeaders.end(),
+ [&](macho_header<P>* lmh, macho_header<P>* rmh) -> bool {
+ if ( _depDAG[lmh]._depth != _depDAG[rmh]._depth )
+ return (_depDAG[lmh]._depth < _depDAG[rmh]._depth);
+ else
+ return (lmh < rmh);
+ });
+
+ // build zeroed array of extra infos
+ dyldCacheImageInfoExtra<E> emptyExtra;
+ emptyExtra.set_exportsTrieAddr(0);
+ emptyExtra.set_weakBindingsAddr(0);
+ emptyExtra.set_exportsTrieSize(0);
+ emptyExtra.set_weakBindingsSize(0);
+ emptyExtra.set_dependentsStartArrayIndex(0);
+ emptyExtra.set_reExportsStartArrayIndex(0);
+ _extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra);
+
+ //for ( macho_header<P>* mh : sortedMachHeaders ) {
+ // fprintf(stderr, "depth: %3d mh: %p path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName());
+ //}
+
+ // build dependency table
+ _dependencyArray.push_back(0xFFFF); // reserve 0 slot to be "no-dependencies"
+ for (macho_header<P>* mh : sortedMachHeaders) {
+ LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+ unsigned index = _machHeaderToImageIndex[mh];
+ auto depPaths = op->getAllDependents();
+ if ( depPaths.empty() ) {
+ _extraInfo[index].set_dependentsStartArrayIndex(0);
+ }
+ else {
+ _extraInfo[index].set_dependentsStartArrayIndex((uint32_t)_dependencyArray.size());
+ auto downPaths = op->getDownwardDependents();
+ for (const char* depPath : depPaths) {
+ macho_header<P>* depMH = _dylibPathToMachHeader[depPath];
+ uint16_t depIndex = _machHeaderToImageIndex[depMH];
+ if ( std::find(downPaths.begin(), downPaths.end(), depPath) == downPaths.end())
+ depIndex |= 0x8000;
+ _dependencyArray.push_back(depIndex);
+ }
+ _dependencyArray.push_back(0xFFFF); // mark end of list
+ }
+ }
+
+ // build re-exports table
+ _reExportArray.push_back(0xFFFF); // reserve 0 slot to be "no-re-exports"
+ for (macho_header<P>* mh : sortedMachHeaders) {
+ LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+ unsigned index = _machHeaderToImageIndex[mh];
+ auto reExPaths = op->getReExportPaths();
+ if ( reExPaths.empty() ) {
+ _extraInfo[index].set_reExportsStartArrayIndex(0);
+ }
+ else {
+ _extraInfo[index].set_reExportsStartArrayIndex((uint32_t)_reExportArray.size());
+ for (const char* reExPath : reExPaths) {
+ macho_header<P>* reExMH = _dylibPathToMachHeader[reExPath];
+ uint32_t reExIndex = _machHeaderToImageIndex[reExMH];
+ _reExportArray.push_back(reExIndex);
+ }
+ _reExportArray.push_back(0xFFFF); // mark end of list
+ }
+ }
+
+ // build ordered list of initializers
+ for (macho_header<P>* mh : sortedMachHeaders) {
+ LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+ unsigned index = _machHeaderToImageIndex[mh];
+ _bottomUpArray.push_back(index);
+ for (uint64_t initializer : op->initializerAddresses()) {
+ //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
+ dyldCacheAcceleratorInitializer<E> entry;
+ entry.set_functionOffset((uint32_t)(initializer-cacheStartAddress));
+ entry.set_imageIndex(_machHeaderToImageIndex[mh]);
+ _initializers.push_back(entry);
+ }
+ }
+
+ // build ordered list of DOF sections
+ for (macho_header<P>* mh : sortedMachHeaders) {
+ LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+ assert(op != NULL);
+ unsigned imageIndex = _machHeaderToImageIndex[mh];
+ for (auto& sect : op->dofSections()) {
+ //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
+ dyldCacheAcceleratorDOFEntry<E> entry;
+ entry.set_sectionAddress(sect->addr());
+ entry.set_sectionSize((uint32_t)sect->size());
+ entry.set_imageIndex(imageIndex);
+ _dofSections.push_back(entry);
+ }
+ }
+
+ // register exports trie and weak binding info in each dylib with image extra info
+ for (macho_header<P>* mh : sortedMachHeaders) {
+ LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+ unsigned index = _machHeaderToImageIndex[mh];
+ _extraInfo[index].set_exportsTrieAddr(op->exportsTrieLinkEditOffset() + linkeditStartAddr);
+ _extraInfo[index].set_exportsTrieSize(op->exportsTrieLinkEditSize());
+ _extraInfo[index].set_weakBindingsAddr(op->weakBindingLinkEditOffset() + linkeditStartAddr);
+ _extraInfo[index].set_weakBindingsSize(op->weakBindingLinkEditSize());
+ }
+
+ // record location of __DATA/__dyld section in libdyld.dylib
+ macho_header<P>* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"];
+ LinkeditOptimizer<P>* libdyldOp = _machHeaderToOptimizer[libdyldMH];
+ uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress();
+
+ // build range table for fast address->image lookups
+ for (macho_header<P>* mh : sortedMachHeaders) {
+ LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+ unsigned imageIndex = _machHeaderToImageIndex[mh];
+ for ( const macho_segment_command<P>* segCmd : op->segCmds() ) {
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
+ continue;
+ dyldCacheAcceleratorRangeEntry<E> entry;
+ entry.set_startAddress(segCmd->vmaddr());
+ entry.set_size((uint32_t)segCmd->vmsize());
+ entry.set_imageIndex(imageIndex);
+ _rangeTable.push_back(entry);
+ }
+ }
+ std::sort(_rangeTable.begin(), _rangeTable.end(),
+ [&](const dyldCacheAcceleratorRangeEntry<E>& lRange, const dyldCacheAcceleratorRangeEntry<E>& rRange) -> bool {
+ return (lRange.startAddress() < rRange.startAddress());
+ });
+
+ // build trie that maps install names to image index
+ std::vector<DylibIndexTrie::Entry> dylibEntrys;
+ for (auto &x : _dylibPathToMachHeader) {
+ const std::string& path = x.first;
+ unsigned index = _machHeaderToImageIndex[x.second];
+ dylibEntrys.push_back(DylibIndexTrie::Entry(path, DylibIndex(index)));
+ }
+ DylibIndexTrie dylibsTrie(dylibEntrys);
+ dylibsTrie.emit(_trieBytes);
+ while ( (_trieBytes.size() % 4) != 0 )
+ _trieBytes.push_back(0);
+
+ // fill out header
+ dyldCacheAcceleratorInfo<E>& h = _acceleratorInfoHeader;
+ h.set_version(1);
+ h.set_imageExtrasCount((uint32_t)_extraInfo.size());
+ h.set_imagesExtrasOffset(ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra));
+ h.set_bottomUpListOffset(h.imagesExtrasOffset() + h.imageExtrasCount()*sizeof(dyld_cache_image_info_extra));
+ h.set_dylibTrieOffset(h.bottomUpListOffset() + h.imageExtrasCount()*sizeof(uint16_t));
+ h.set_dylibTrieSize((uint32_t)_trieBytes.size());
+ h.set_initializersOffset(ALIGN_AS_TYPE(h.dylibTrieOffset() + h.dylibTrieSize(), dyld_cache_accelerator_initializer));
+ h.set_initializersCount((uint32_t)_initializers.size());
+ h.set_dofSectionsOffset(ALIGN_AS_TYPE(h.initializersOffset() + h.initializersCount()*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer));
+ h.set_dofSectionsCount((uint32_t)_dofSections.size());
+ h.set_reExportListOffset(ALIGN_AS_TYPE(h.dofSectionsOffset() + h.dofSectionsCount()*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof));
+ h.set_reExportCount((uint32_t)_reExportArray.size());
+ h.set_depListOffset(ALIGN_AS_TYPE(h.reExportListOffset() + h.reExportCount()*sizeof(uint16_t), uint16_t));
+ h.set_depListCount((uint32_t)_dependencyArray.size());
+ h.set_rangeTableOffset(ALIGN_AS_TYPE(h.depListOffset() + h.depListCount()*sizeof(uint16_t), dyld_cache_range_entry));
+ h.set_rangeTableCount((uint32_t)_rangeTable.size());
+ h.set_dyldSectionAddr(dyldSectionAddr);
+}
+
+
+template <typename P>
+void AcceleratorTables<P>::DepNode::computeDepth()
+{
+ if ( _depth != 0 )
+ return;
+ _depth = 1;
+ for (DepNode* node : _dependents) {
+ node->computeDepth();
+ if ( node->_depth >= _depth )
+ _depth = node->_depth + 1;
+ }
+}
+
+template <typename P>
+uint32_t AcceleratorTables<P>::totalSize() const
+{
+ return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset() + _acceleratorInfoHeader.rangeTableCount()*sizeof(dyld_cache_range_entry), 14);
+}
+
+template <typename P>
+void AcceleratorTables<P>::copyTo(uint8_t* buffer)
+{
+ memcpy(buffer, &_acceleratorInfoHeader, sizeof(dyld_cache_accelerator_info));
+ memcpy(&buffer[_acceleratorInfoHeader.imagesExtrasOffset()], &_extraInfo[0], _extraInfo.size()*sizeof(dyld_cache_image_info_extra));
+ memcpy(&buffer[_acceleratorInfoHeader.bottomUpListOffset()], &_bottomUpArray[0], _bottomUpArray.size()*sizeof(uint16_t));
+ memcpy(&buffer[_acceleratorInfoHeader.initializersOffset()], &_initializers[0], _initializers.size()*sizeof(dyld_cache_accelerator_initializer));
+ memcpy(&buffer[_acceleratorInfoHeader.reExportListOffset()], &_reExportArray[0], _reExportArray.size()*sizeof(uint16_t));
+ memcpy(&buffer[_acceleratorInfoHeader.dofSectionsOffset()], &_dofSections[0], _dofSections.size()*sizeof(dyld_cache_accelerator_dof));
+ memcpy(&buffer[_acceleratorInfoHeader.depListOffset()], &_dependencyArray[0], _dependencyArray.size()*sizeof(uint16_t));
+ memcpy(&buffer[_acceleratorInfoHeader.rangeTableOffset()], &_rangeTable[0], _rangeTable.size()*sizeof(dyld_cache_range_entry));
+ memcpy(&buffer[_acceleratorInfoHeader.dylibTrieOffset()], &_trieBytes[0], _trieBytes.size());
+}
+
+
+
+template <typename P>
+LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh)
+: _mh(mh), _cacheBuffer(cacheBuffer)
+{
+ _linkeditBias = (uint8_t*)cacheBuffer;
+ const unsigned origLoadCommandsSize = mh->sizeofcmds();
+ unsigned bytesRemaining = origLoadCommandsSize;
+ unsigned removedCount = 0;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ const uint32_t cmdCount = mh->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ const macho_dylib_command<P>* dylibCmd;
+ const macho_routines_command<P>* routinesCmd;
+ macho_segment_command<P>* segCmd;
+ for (uint32_t i = 0; i < cmdCount; ++i) {
+ bool remove = false;
+ switch (cmd->cmd()) {
+ case LC_ID_DYLIB:
+ _installName = ((macho_dylib_command<P>*)cmd)->name();
+ break;
+ case LC_SYMTAB:
+ _symTabCmd = (macho_symtab_command<P>*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ _dyldInfo = (macho_dyld_info_command<P>*)cmd;
+ _exportInfoSize = _dyldInfo->export_size();
+ break;
+ case LC_FUNCTION_STARTS:
+ _functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
+ break;
+ case LC_DATA_IN_CODE:
+ _dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
+ break;
+ case LC_ROUTINES:
+ case LC_ROUTINES_64:
+ routinesCmd = (macho_routines_command<P>*)cmd;
+ _initializerAddresses.push_back(routinesCmd->init_address());
+ break;
+ case LC_REEXPORT_DYLIB:
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
+ dylibCmd = (macho_dylib_command<P>*)cmd;
+ _allDependentPaths.push_back(dylibCmd->name());
+ if ( cmd->cmd() != LC_LOAD_UPWARD_DYLIB )
+ _downDependentPaths.push_back(dylibCmd->name());
+ if ( cmd->cmd() == LC_REEXPORT_DYLIB )
+ _reExportPaths.push_back(dylibCmd->name());
+ break;
+ case macho_segment_command<P>::CMD:
+ segCmd = (macho_segment_command<P>*)cmd;
+ _segCmds.push_back(segCmd);
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+ _linkeditSize = (uint32_t)segCmd->vmsize();
+ _linkeditCacheOffset = (uint32_t)segCmd->fileoff();
+ _linkeditAddr = segCmd->vmaddr();
+ }
+ else if ( segCmd->nsects() > 0 ) {
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for (macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ const uint8_t type = sect->flags() & SECTION_TYPE;
+ if ( type == S_MOD_INIT_FUNC_POINTERS ) {
+ const pint_t* inits = (pint_t*)((char*)cacheBuffer + sect->offset());
+ const size_t count = sect->size() / sizeof(pint_t);
+ for (size_t j=0; j < count; ++j) {
+ uint64_t func = P::getP(inits[j]);
+ _initializerAddresses.push_back(func);
+ }
+ }
+ else if ( type == S_DTRACE_DOF ) {
+ _dofSections.push_back(sect);
+ }
+ else if ( (strcmp(sect->sectname(), "__dyld") == 0) && (strncmp(sect->segname(), "__DATA", 6) == 0) ) {
+ _dyldSectionAddr = sect->addr();
+ }
+ }
+ }
+ break;
+ case LC_SEGMENT_SPLIT_INFO:
+ remove = true;
+ break;
+ }
+ uint32_t cmdSize = cmd->cmdsize();
+ macho_load_command<P>* nextCmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmdSize);
+ if ( remove ) {
+ ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
+ ++removedCount;
+ }
+ else {
+ bytesRemaining -= cmdSize;
+ cmd = nextCmd;
+ }
+ }
+ // zero out stuff removed
+ ::bzero((void*)cmd, bytesRemaining);
+ // update header
+ mh->set_ncmds(cmdCount - removedCount);
+ mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining);
+}
+
+/*
+static void dumpLoadCommands(const uint8_t* mheader)
+{
+ const mach_header* const mh = (mach_header*)mheader;
+ const uint32_t cmd_count = mh->ncmds;
+ bool is64 = (mh->magic == MH_MAGIC_64);
+ const load_command* cmds = (load_command*)(mheader + (is64 ? sizeof(mach_header_64) : sizeof(mach_header)));
+ const load_command* cmd = cmds;
+ const segment_command* segCmd;
+ const segment_command_64* seg64Cmd;
+ const symtab_command* symTab;
+ const linkedit_data_command* leData;
+ const uint8_t* linkEditBias = NULL;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT:
+ segCmd = (const segment_command*)cmd;
+ printf("LC_SEGMENT\n");
+ printf(" segname = %s\n", segCmd->segname);
+ printf(" vmaddr = 0x%08X\n", segCmd->vmaddr);
+ printf(" vmsize = 0x%08X\n", segCmd->vmsize);
+ printf(" fileoff = 0x%08X\n", segCmd->fileoff);
+ printf(" filesize = 0x%08X\n", segCmd->filesize);
+ if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
+ linkEditBias = mheader - segCmd->fileoff;
+ }
+ break;
+ case LC_SEGMENT_64:
+ seg64Cmd = (const segment_command_64*)cmd;
+ printf("LC_SEGMENT_64\n");
+ printf(" segname = %s\n", seg64Cmd->segname);
+ printf(" vmaddr = 0x%09llX\n", seg64Cmd->vmaddr);
+ printf(" vmsize = 0x%09llX\n", seg64Cmd->vmsize);
+ printf(" fileoff = 0x%09llX\n", seg64Cmd->fileoff);
+ printf(" filesize = 0x%09llX\n", seg64Cmd->filesize);
+ if ( strcmp(seg64Cmd->segname, "__TEXT") == 0 ) {
+ linkEditBias = mheader - seg64Cmd->fileoff;
+ }
+ break;
+ case LC_SYMTAB:
+ symTab = (const symtab_command*)cmd;
+ printf("LC_SYMTAB\n");
+ printf(" symoff = 0x%08X\n", symTab->symoff);
+ printf(" nsyms = 0x%08X\n", symTab->nsyms);
+ printf(" stroff = 0x%08X\n", symTab->stroff);
+ printf(" strsize = 0x%08X\n", symTab->strsize);
+ {
+ const char* strPool = (char*)&linkEditBias[symTab->stroff];
+ const nlist_64* sym0 = (nlist_64*)(&linkEditBias[symTab->symoff]);
+ printf(" sym[0].n_strx = 0x%08X (%s)\n", sym0->n_un.n_strx, &strPool[sym0->n_un.n_strx]);
+ printf(" sym[0].n_type = 0x%02X\n", sym0->n_type);
+ printf(" sym[0].n_sect = 0x%02X\n", sym0->n_sect);
+ printf(" sym[0].n_desc = 0x%04X\n", sym0->n_desc);
+ printf(" sym[0].n_value = 0x%llX\n", sym0->n_value);
+ const nlist_64* sym1 = (nlist_64*)(&linkEditBias[symTab->symoff+16]);
+ printf(" sym[1].n_strx = 0x%08X (%s)\n", sym1->n_un.n_strx, &strPool[sym1->n_un.n_strx]);
+ printf(" sym[1].n_type = 0x%02X\n", sym1->n_type);
+ printf(" sym[1].n_sect = 0x%02X\n", sym1->n_sect);
+ printf(" sym[1].n_desc = 0x%04X\n", sym1->n_desc);
+ printf(" sym[1].n_value = 0x%llX\n", sym1->n_value);
+ }
+ break;
+ case LC_FUNCTION_STARTS:
+ leData = (const linkedit_data_command*)cmd;
+ printf("LC_FUNCTION_STARTS\n");
+ printf(" dataoff = 0x%08X\n", leData->dataoff);
+ printf(" datasize = 0x%08X\n", leData->datasize);
+ default:
+ //printf("0x%08X\n", cmd->cmd);
+ break;
+ }
+ cmd = (const load_command*)(((uint8_t*)cmd)+cmd->cmdsize);
+ }
+}
+*/
+
+template <typename P>
+void LinkeditOptimizer<P>::updateLoadCommands(uint32_t mergedLinkeditStartOffset, uint64_t mergedLinkeditAddr, uint64_t newLinkeditSize,
+ uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount,
+ uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize)
+{
+ // update __LINKEDIT segment in all dylibs to overlap the same shared region
+ for (macho_segment_command<P>* segCmd : _segCmds) {
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+ segCmd->set_vmaddr(mergedLinkeditAddr);
+ segCmd->set_vmsize(newLinkeditSize);
+ segCmd->set_fileoff(mergedLinkeditStartOffset);
+ segCmd->set_filesize(newLinkeditSize);
+ }
+ else if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) {
+ // HACK until lldb fixed in: <rdar://problem/20357466> DynamicLoaderMacOSXDYLD fixes for Monarch dyld shared cache
+ segCmd->set_fileoff(0);
+
+ }
+ }
+
+ // update symbol table to point to shared symbol table
+ _symTabCmd->set_symoff(mergedLinkeditStartOffset + sharedSymbolTableStartOffset + _newLocalSymbolsStartIndex*sizeof(macho_nlist<P>));
+ _symTabCmd->set_nsyms(_newLocalSymbolCount+_newExportedSymbolCount+_newImportedSymbolCount);
+ _symTabCmd->set_stroff(mergedLinkeditStartOffset + sharedSymbolStringsOffset);
+ _symTabCmd->set_strsize(sharedSymbolStringsSize);
+
+ // update dynamic symbol table to have proper offsets into shared symbol table
+ _dynSymTabCmd->set_ilocalsym(0);
+ _dynSymTabCmd->set_nlocalsym(_newLocalSymbolCount);
+ _dynSymTabCmd->set_iextdefsym(_newExportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
+ _dynSymTabCmd->set_nextdefsym(_newExportedSymbolCount);
+ _dynSymTabCmd->set_iundefsym(_newImportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
+ _dynSymTabCmd->set_nundefsym(_newImportedSymbolCount);
+ _dynSymTabCmd->set_tocoff(0);
+ _dynSymTabCmd->set_ntoc(0);
+ _dynSymTabCmd->set_modtaboff(0);
+ _dynSymTabCmd->set_nmodtab(0);
+ _dynSymTabCmd->set_indirectsymoff(mergedLinkeditStartOffset + _newIndirectSymbolTableOffset);
+ _dynSymTabCmd->set_extreloff(0);
+ _dynSymTabCmd->set_locreloff(0);
+ _dynSymTabCmd->set_nlocrel(0);
+
+ // update dyld info
+ if ( _dyldInfo != nullptr ) {
+ _dyldInfo->set_rebase_off(0);
+ _dyldInfo->set_rebase_size(0);
+ _dyldInfo->set_bind_off(_dyldInfo->bind_size() ? mergedLinkeditStartOffset + _newBindingInfoOffset : 0);
+ _dyldInfo->set_weak_bind_off(_dyldInfo->weak_bind_size() ? mergedLinkeditStartOffset + _newWeakBindingInfoOffset : 0 );
+ _dyldInfo->set_lazy_bind_off(_dyldInfo->lazy_bind_size() ? mergedLinkeditStartOffset + _newLazyBindingInfoOffset : 0 );
+ _dyldInfo->set_export_off(mergedLinkeditStartOffset + _newExportInfoOffset);
+ }
+
+ // update function-starts
+ if ( _functionStartsCmd != nullptr )
+ _functionStartsCmd->set_dataoff(mergedLinkeditStartOffset+_newFunctionStartsOffset);
+
+ // update data-in-code
+ if ( _dataInCodeCmd != nullptr )
+ _dataInCodeCmd->set_dataoff(mergedLinkeditStartOffset+_newDataInCodeOffset);
+}
+
+template <typename P>
+void LinkeditOptimizer<P>::copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset)
+{
+ if ( _dyldInfo == nullptr )
+ return;
+ unsigned size = _dyldInfo->weak_bind_size();
+ if ( size != 0 ) {
+ ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->weak_bind_off()], size);
+ _newWeakBindingInfoOffset = offset;
+ _newWeakBindingSize = size;
+ offset += size;
+ }
+}
+
+
+template <typename P>
+void LinkeditOptimizer<P>::copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset)
+{
+ if ( _dyldInfo == nullptr )
+ return;
+ unsigned size = _dyldInfo->lazy_bind_size();
+ if ( size != 0 ) {
+ ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->lazy_bind_off()], size);
+ _newLazyBindingInfoOffset = offset;
+ offset += size;
+ }
+}
+
+template <typename P>
+void LinkeditOptimizer<P>::copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset)
+{
+ if ( _dyldInfo == nullptr )
+ return;
+ unsigned size = _dyldInfo->bind_size();
+ if ( size != 0 ) {
+ ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->bind_off()], size);
+ _newBindingInfoOffset = offset;
+ offset += size;
+ }
+}
+
+template <typename P>
+void LinkeditOptimizer<P>::copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset)
+{
+ if ( _dyldInfo == nullptr )
+ return;
+ unsigned size = _dyldInfo->export_size();
+ if ( size != 0 ) {
+ ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->export_off()], size);
+ _newExportInfoOffset = offset;
+ offset += size;
+ }
+}
+
+
+template <typename P>
+void LinkeditOptimizer<P>::copyFunctionStarts(uint8_t* newLinkEditContent, uint32_t& offset)
+{
+ if ( _functionStartsCmd == nullptr )
+ return;
+ unsigned size = _functionStartsCmd->datasize();
+ ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_functionStartsCmd->dataoff()], size);
+ _newFunctionStartsOffset = offset;
+ offset += size;
+}
+
+template <typename P>
+void LinkeditOptimizer<P>::copyDataInCode(uint8_t* newLinkEditContent, uint32_t& offset)
+{
+ if ( _dataInCodeCmd == nullptr )
+ return;
+ unsigned size = _dataInCodeCmd->datasize();
+ ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dataInCodeCmd->dataoff()], size);
+ _newDataInCodeOffset = offset;
+ offset += size;
+}
+
+
+template <typename P>
+void LinkeditOptimizer<P>::copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex,
+ bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
+ std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& localSymbolsStringPool)
+{
+ LocalSymbolInfo localInfo;
+ localInfo.dylibOffset = (uint32_t)(((uint8_t*)_mh) - (uint8_t*)_cacheBuffer);
+ localInfo.nlistStartIndex = (uint32_t)unmappedLocalSymbols.size();
+ localInfo.nlistCount = 0;
+ _newLocalSymbolsStartIndex = symbolIndex;
+ const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
+ const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
+ const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->ilocalsym()];
+ const macho_nlist<P>* const lastExport = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()];
+ for (const macho_nlist<P>* entry = firstExport; entry < lastExport; ++entry) {
+ if ( (entry->n_type() & N_TYPE) != N_SECT)
+ continue;
+ if ( (entry->n_type() & N_STAB) != 0)
+ continue;
+ const char* name = &strings[entry->n_strx()];
+ macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
+ *newSymbolEntry = *entry;
+ if ( redact ) {
+ // if removing local symbols, change __text symbols to "<redacted>" so backtraces don't have bogus names
+ if ( entry->n_sect() == 1 ) {
+ stringPool.add(symbolIndex, "<redacted>");
+ ++symbolIndex;
+ offset += sizeof(macho_nlist<P>);
+ }
+ // copy local symbol to unmmapped locals area
+ localSymbolsStringPool.add((uint32_t)unmappedLocalSymbols.size(), name);
+ unmappedLocalSymbols.push_back(*entry);
+ unmappedLocalSymbols.back().set_n_strx(0);
+ }
+ else {
+ stringPool.add(symbolIndex, name);
+ ++symbolIndex;
+ offset += sizeof(macho_nlist<P>);
+ }
+ }
+ _newLocalSymbolCount = symbolIndex - _newLocalSymbolsStartIndex;
+ localInfo.nlistCount = (uint32_t)unmappedLocalSymbols.size() - localInfo.nlistStartIndex;
+ localSymbolInfos.push_back(localInfo);
+}
+
+
+template <typename P>
+void LinkeditOptimizer<P>::copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
+{
+ _newExportedSymbolsStartIndex = symbolIndex;
+ const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
+ const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
+ const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->iextdefsym()];
+ const macho_nlist<P>* const lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()];
+ uint32_t oldSymbolIndex = _dynSymTabCmd->iextdefsym();
+ for (const macho_nlist<P>* entry = firstExport; entry < lastExport; ++entry, ++oldSymbolIndex) {
+ if ( (entry->n_type() & N_TYPE) != N_SECT)
+ continue;
+ const char* name = &strings[entry->n_strx()];
+ if ( strncmp(name, ".objc_", 6) == 0 )
+ continue;
+ if ( strncmp(name, "$ld$", 4) == 0 )
+ continue;
+ macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
+ *newSymbolEntry = *entry;
+ newSymbolEntry->set_n_strx(0);
+ stringPool.add(symbolIndex, name);
+ _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex;
+ ++symbolIndex;
+ offset += sizeof(macho_nlist<P>);
+ }
+ _newExportedSymbolCount = symbolIndex - _newExportedSymbolsStartIndex;
+}
+
+template <typename P>
+void LinkeditOptimizer<P>::copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
+{
+ _newImportedSymbolsStartIndex = symbolIndex;
+ const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
+ const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
+ const macho_nlist<P>* const firstImport = &symbolTable[_dynSymTabCmd->iundefsym()];
+ const macho_nlist<P>* const lastImport = &symbolTable[_dynSymTabCmd->iundefsym()+_dynSymTabCmd->nundefsym()];
+ uint32_t oldSymbolIndex = _dynSymTabCmd->iundefsym();
+ for (const macho_nlist<P>* entry = firstImport; entry < lastImport; ++entry, ++oldSymbolIndex) {
+ if ( (entry->n_type() & N_TYPE) != N_UNDF)
+ continue;
+ const char* name = &strings[entry->n_strx()];
+ macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
+ *newSymbolEntry = *entry;
+ newSymbolEntry->set_n_strx(0);
+ stringPool.add(symbolIndex, name);
+ _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex;
+ ++symbolIndex;
+ offset += sizeof(macho_nlist<P>);
+ }
+ _newImportedSymbolCount = symbolIndex - _newImportedSymbolsStartIndex;
+}
+
+template <typename P>
+void LinkeditOptimizer<P>::copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset)
+{
+ _newIndirectSymbolTableOffset = offset;
+ const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
+ uint32_t* newIndirectTable = (uint32_t*)&newLinkEditContent[offset];
+ for (int i=0; i < _dynSymTabCmd->nindirectsyms(); ++i) {
+ uint32_t symbolIndex = E::get32(indirectTable[i]);
+ if ( (symbolIndex == INDIRECT_SYMBOL_ABS) || (symbolIndex == INDIRECT_SYMBOL_LOCAL) )
+ E::set32(newIndirectTable[i], symbolIndex);
+ else
+ E::set32(newIndirectTable[i], _oldToNewSymbolIndexes[symbolIndex]);
+ offset += sizeof(uint32_t);
+ }
+}
+
+template <typename P>
+uint64_t mergeLinkedits(SharedCache& cache, bool dontMapLocalSymbols, bool addAcceleratorTables, std::vector<LinkeditOptimizer<P>*>& optimizers)
+{
+ // allocate space for new linkedit data
+ uint32_t linkeditStartOffset = 0xFFFFFFFF;
+ uint32_t linkeditEndOffset = 0;
+ uint64_t linkeditStartAddr = 0;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ uint32_t leOffset = op->linkeditOffset();
+ if ( leOffset < linkeditStartOffset ) {
+ linkeditStartOffset = leOffset;
+ linkeditStartAddr = op->linkeditAddr();
+ }
+ uint32_t leEndOffset = op->linkeditOffset() + op->linkeditSize();
+ if ( leEndOffset > linkeditEndOffset )
+ linkeditEndOffset = leEndOffset;
+ }
+ uint64_t totalUnoptLinkeditsSize = linkeditEndOffset - linkeditStartOffset;
+ uint8_t* newLinkEdit = (uint8_t*)calloc(totalUnoptLinkeditsSize, 1);
+ SortedStringPool<P> stringPool;
+ uint32_t offset = 0;
+
+ verboseLog("Merged LINKEDIT:");
+
+ // copy weak binding info
+ uint32_t startWeakBindInfosOffset = offset;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyWeakBindingInfo(newLinkEdit, offset);
+ }
+ verboseLog(" weak bindings size: %5uKB", (offset-startWeakBindInfosOffset)/1024);
+
+ // copy export info
+ uint32_t startExportInfosOffset = offset;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyExportInfo(newLinkEdit, offset);
+ }
+ verboseLog(" exports info size: %5uKB", (offset-startExportInfosOffset)/1024);
+
+ // in theory, an optimized cache can drop the binding info
+ if ( true ) {
+ // copy binding info
+ uint32_t startBindingsInfosOffset = offset;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyBindingInfo(newLinkEdit, offset);
+ }
+ verboseLog(" bindings size: %5uKB", (offset-startBindingsInfosOffset)/1024);
+
+ // copy lazy binding info
+ uint32_t startLazyBindingsInfosOffset = offset;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyLazyBindingInfo(newLinkEdit, offset);
+ }
+ verboseLog(" lazy bindings size: %5uKB", (offset-startLazyBindingsInfosOffset)/1024);
+ }
+
+ // copy symbol table entries
+ std::vector<macho_nlist<P>> unmappedLocalSymbols;
+ if ( dontMapLocalSymbols )
+ unmappedLocalSymbols.reserve(0x01000000);
+ std::vector<LocalSymbolInfo> localSymbolInfos;
+ localSymbolInfos.reserve(optimizers.size());
+ SortedStringPool<P> localSymbolsStringPool;
+ uint32_t symbolIndex = 0;
+ const uint32_t sharedSymbolTableStartOffset = offset;
+ uint32_t sharedSymbolTableExportsCount = 0;
+ uint32_t sharedSymbolTableImportsCount = 0;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, dontMapLocalSymbols,
+ localSymbolInfos, unmappedLocalSymbols, localSymbolsStringPool);
+ uint32_t x = symbolIndex;
+ op->copyExportedSymbols(newLinkEdit, stringPool, offset, symbolIndex);
+ sharedSymbolTableExportsCount += (symbolIndex-x);
+ uint32_t y = symbolIndex;
+ op->copyImportedSymbols(newLinkEdit, stringPool, offset, symbolIndex);
+ sharedSymbolTableImportsCount += (symbolIndex-y);
+ }
+ uint32_t sharedSymbolTableCount = symbolIndex;
+ const uint32_t sharedSymbolTableEndOffset = offset;
+
+ // copy function starts
+ uint32_t startFunctionStartsOffset = offset;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyFunctionStarts(newLinkEdit, offset);
+ }
+ verboseLog(" function starts size: %5uKB", (offset-startFunctionStartsOffset)/1024);
+
+ // copy data-in-code info
+ uint32_t startDataInCodeOffset = offset;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyDataInCode(newLinkEdit, offset);
+ }
+ verboseLog(" data in code size: %5uB", offset-startDataInCodeOffset);
+
+ // copy indirect symbol tables
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyIndirectSymbolTable(newLinkEdit, offset);
+ }
+ // if indirect table has odd number of entries, end will not be 8-byte aligned
+ if ( (offset % sizeof(typename P::uint_t)) != 0 )
+ offset += 4;
+
+ // copy string pool
+ uint32_t sharedSymbolStringsOffset = offset;
+ uint32_t sharedSymbolStringsSize = stringPool.copyPoolAndUpdateOffsets((char*)&newLinkEdit[sharedSymbolStringsOffset], (macho_nlist<P>*)&newLinkEdit[sharedSymbolTableStartOffset]);
+ offset += sharedSymbolStringsSize;
+ uint32_t newLinkeditUnalignedSize = offset;
+ uint64_t newLinkeditEnd = align(linkeditStartOffset+newLinkeditUnalignedSize, 14);
+ verboseLog(" symbol table size: %5uKB (%d exports, %d imports)", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount);
+ verboseLog(" symbol string pool size: %5uKB", sharedSymbolStringsSize/1024);
+
+ // overwrite mapped LINKEDIT area in cache with new merged LINKEDIT content
+ verboseLog("LINKEDITS optimized from %uMB to %uMB", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024));
+ ::memcpy((char*)cache.buffer().get()+linkeditStartOffset, newLinkEdit, newLinkeditUnalignedSize);
+ ::bzero((char*)cache.buffer().get()+linkeditStartOffset+newLinkeditUnalignedSize, totalUnoptLinkeditsSize-newLinkeditUnalignedSize);
+ ::free(newLinkEdit);
+
+ // If making cache for customers, add extra accelerator tables for dyld
+ if ( addAcceleratorTables ) {
+ AcceleratorTables<P> tables(cache.buffer().get(), linkeditStartAddr, optimizers);
+ uint32_t tablesSize = tables.totalSize();
+ if ( tablesSize < (totalUnoptLinkeditsSize-newLinkeditUnalignedSize) ) {
+ tables.copyTo((uint8_t*)cache.buffer().get()+newLinkeditEnd);
+ newLinkeditEnd += tablesSize;
+ uint64_t accelInfoAddr = align(linkeditStartAddr + newLinkeditUnalignedSize, 14);
+ cache.setAcceleratorInfoRange(accelInfoAddr, tablesSize);
+ }
+ else {
+ warning("not enough room to add dyld accelerator tables");
+ }
+ }
+
+ // update mapping to reduce linkedit size
+ cache.setLinkeditsMappingEndFileOffset(newLinkeditEnd);
+
+ // overwrite end of un-opt linkedits to create a new unmapped region for local symbols
+ uint64_t newFileSize = newLinkeditEnd;
+ if ( dontMapLocalSymbols ) {
+ typedef typename P::E E;
+ uint32_t localSymbolsOffset = (uint32_t)align(newFileSize, 14);
+ uint32_t spaceAtEnd = linkeditEndOffset - (uint32_t)newLinkeditEnd;
+ dyldCacheLocalSymbolsInfo<E>* infoHeader = (dyldCacheLocalSymbolsInfo<E>*)((uint8_t*)cache.buffer().get()+localSymbolsOffset);
+ const uint32_t entriesOffset = sizeof(dyldCacheLocalSymbolsInfo<E>);
+ const uint32_t entriesCount = (uint32_t)localSymbolInfos.size();
+ const uint32_t nlistOffset = (uint32_t)align(entriesOffset + entriesCount * sizeof(dyldCacheLocalSymbolEntry<E>), 4); // 16-byte align start
+ const uint32_t nlistCount = (uint32_t)unmappedLocalSymbols.size();
+ const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist<P>);
+ // copy info for each dylib
+ dyldCacheLocalSymbolEntry<E>* entries = (dyldCacheLocalSymbolEntry<E>*)(((uint8_t*)infoHeader)+entriesOffset);
+ for (int i=0; i < entriesCount; ++i) {
+ entries[i].set_dylibOffset(localSymbolInfos[i].dylibOffset);
+ entries[i].set_nlistStartIndex(localSymbolInfos[i].nlistStartIndex);
+ entries[i].set_nlistCount(localSymbolInfos[i].nlistCount);
+ }
+ // copy nlists
+ macho_nlist<P>* newLocalsSymbolTable = (macho_nlist<P>*)(((uint8_t*)infoHeader)+nlistOffset);
+ ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
+ // copy string pool
+ const uint32_t stringsSize = localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
+ const uint32_t localsRegionSize = (uint32_t)align(stringsOffset+stringsSize, 14);
+ if ( localsRegionSize > spaceAtEnd )
+ terminate("not enough room to store local symbols");
+ // fill in local symbols info
+ infoHeader->set_nlistOffset(nlistOffset);
+ infoHeader->set_nlistCount(nlistCount);
+ infoHeader->set_stringsOffset(stringsOffset);
+ infoHeader->set_stringsSize(stringsSize);
+ infoHeader->set_entriesOffset(entriesOffset);
+ infoHeader->set_entriesCount(entriesCount);
+ // update cache size
+ newFileSize += localsRegionSize;
+ verboseLog("Unmapped local symbol info: %uMB", localsRegionSize/(1024*1024));
+ cache.setUnmappedLocalsRange(localSymbolsOffset, localsRegionSize);
+ }
+
+ // update all load commands to new merged layout
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->updateLoadCommands(linkeditStartOffset, linkeditStartAddr, newLinkeditEnd-linkeditStartOffset,
+ sharedSymbolTableStartOffset, sharedSymbolTableCount,
+ sharedSymbolStringsOffset, sharedSymbolStringsSize);
+ }
+
+ return newFileSize;
+}
+
+} // anonymous namespace
+
+template <typename P>
+void SharedCache::optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets)
+{
+ // construct a LinkeditOptimizer for each image
+ std::vector<LinkeditOptimizer<P>*> optimizers;
+ forEachImage([&](const void* mh, const char*, time_t, ino_t, const std::vector<MachOProxy::Segment>&) {
+ optimizers.push_back(new LinkeditOptimizer<P>(_buffer.get(), (macho_header<P>*)mh));
+ });
+ // add optimizer for each branch pool
+ for (uint64_t poolOffset : branchPoolOffsets) {
+ macho_header<P>* mh = (macho_header<P>*)((char*)_buffer.get() + poolOffset);
+ optimizers.push_back(new LinkeditOptimizer<P>(_buffer.get(), mh));
+ }
+
+ // merge linkedit info
+ _fileSize = mergeLinkedits(*this, dontMapLocalSymbols, addAcceleratorTables, optimizers);
+
+ // delete optimizers
+ for (LinkeditOptimizer<P>* op : optimizers)
+ delete op;
+}
+
+void SharedCache::optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets)
+{
+ switch ( _arch.arch ) {
+ case CPU_TYPE_ARM:
+ case CPU_TYPE_I386:
+ optimizeLinkedit<Pointer32<LittleEndian>>(dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets);
+ break;
+ case CPU_TYPE_X86_64:
+ case CPU_TYPE_ARM64:
+ optimizeLinkedit<Pointer64<LittleEndian>>(dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets);
+ break;
+ default:
+ terminate("unsupported arch 0x%08X", _arch.arch);
+ }
+}
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 "mega-dylib-utils.h"
+#include "Logging.h"
+#include "MachOFileAbstraction.hpp"
+
+
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <assert.h>
+
+
+// Scan a C++ or Swift length-mangled field.
+static bool scanMangledField(const char *&string, const char *end,
+ const char *&field, int& length)
+{
+ // Leading zero not allowed.
+ if (*string == '0') return false;
+
+ length = 0;
+ field = string;
+ while (field < end) {
+ char c = *field;
+ if (!isdigit(c)) break;
+ field++;
+ if (__builtin_smul_overflow(length, 10, &length)) return false;
+ if (__builtin_sadd_overflow(length, c - '0', &length)) return false;
+ }
+
+ string = field + length;
+ return length > 0 && string <= end;
+}
+
+
+// copySwiftDemangledName
+// Returns the pretty form of the given Swift-mangled class or protocol name.
+// Returns nullptr if the string doesn't look like a mangled Swift name.
+// The result must be freed with free().
+static char *copySwiftDemangledName(const char *string, bool isProtocol = false)
+{
+ if (!string) return nullptr;
+
+ // Swift mangling prefix.
+ if (strncmp(string, isProtocol ? "_TtP" : "_TtC", 4) != 0) return nullptr;
+ string += 4;
+
+ const char *end = string + strlen(string);
+
+ // Module name.
+ const char *prefix;
+ int prefixLength;
+ if (string[0] == 's') {
+ // "s" is the Swift module.
+ prefix = "Swift";
+ prefixLength = 5;
+ string += 1;
+ } else {
+ if (! scanMangledField(string, end, prefix, prefixLength)) return nullptr;
+ }
+
+ // Class or protocol name.
+ const char *suffix;
+ int suffixLength;
+ if (! scanMangledField(string, end, suffix, suffixLength)) return nullptr;
+
+ if (isProtocol) {
+ // Remainder must be "_".
+ if (strcmp(string, "_") != 0) return nullptr;
+ } else {
+ // Remainder must be empty.
+ if (string != end) return nullptr;
+ }
+
+ char *result;
+ asprintf(&result, "%.*s.%.*s", prefixLength,prefix, suffixLength,suffix);
+ return result;
+}
+
+
+class ContentAccessor {
+public:
+ ContentAccessor(SharedCache& cache) {
+ cache.forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ Info info = { (uint8_t*)content, (uint8_t*)content+size, vmAddr, vmAddr+size };
+ _regions.push_back(info);
+ });
+ }
+
+ void* contentForVMAddr(uint64_t vmaddr) {
+ for (Info& info : _regions) {
+ if ( (info.startAddr <= vmaddr) && (vmaddr < info.endAddr) )
+ return (void*)(info.contentStart + vmaddr - info.startAddr);
+ }
+ if ( vmaddr == 0 )
+ return nullptr;
+ terminate("contentForVMAddr(0x%0llX) invalid vmaddr in ObjC data", vmaddr);
+ }
+
+ uint64_t vmAddrForContent(const void* content) {
+ for (Info& info : _regions) {
+ if ( (info.contentStart <= content) && (content < info.contentEnd) )
+ return info.startAddr + ((uint8_t*)content - (uint8_t*)info.contentStart);
+ }
+ terminate("vmAddrForContent(%p) invalid content pointer in ObjC data", content);
+ }
+
+private:
+ struct Info { uint8_t* contentStart; uint8_t* contentEnd; uint64_t startAddr; uint64_t endAddr; };
+ std::vector<Info> _regions;
+};
+
+
+// Access a section containing a list of pointers
+template <typename P, typename T>
+class PointerSection
+{
+ typedef typename P::uint_t pint_t;
+public:
+ PointerSection(ContentAccessor* cache, const macho_header<P>* mh,
+ const char* segname, const char* sectname)
+ : _cache(cache),
+ _section(mh->getSection(segname, sectname)),
+ _base(_section ? (pint_t*)cache->contentForVMAddr(_section->addr()) : 0),
+ _count(_section ? (pint_t)(_section->size() / sizeof(pint_t)) : 0) {
+ }
+
+ pint_t count() const { return _count; }
+
+ pint_t getVMAddress(pint_t index) const {
+ if ( index >= _count )
+ terminate("index out of range in section %s", _section->sectname());
+ return (pint_t)P::getP(_base[index]);
+ }
+
+ T get(pint_t index) const {
+ return (T)_cache->contentForVMAddr(getVMAddress(index));
+ }
+
+ void setVMAddress(pint_t index, pint_t value) {
+ if (index >= _count)
+ terminate("index out of range in section %s", _section->sectname());
+ P::setP(_base[index], value);
+ }
+
+ void removeNulls() {
+ pint_t shift = 0;
+ for (pint_t i = 0; i < _count; i++) {
+ pint_t value = _base[i];
+ if (value) {
+ _base[i-shift] = value;
+ } else {
+ shift++;
+ }
+ }
+ _count -= shift;
+ const_cast<macho_section<P>*>(_section)->set_size(_count * sizeof(pint_t));
+ }
+
+private:
+ ContentAccessor* const _cache;
+ const macho_section<P>* const _section;
+ pint_t* const _base;
+ pint_t const _count;
+};
+
+
+// Access a section containing an array of structures
+template <typename P, typename T>
+class ArraySection
+{
+public:
+ ArraySection(ContentAccessor* cache, const macho_header<P>* mh,
+ const char *segname, const char *sectname)
+ : _cache(cache),
+ _section(mh->getSection(segname, sectname)),
+ _base(_section ? (T *)cache->contentForVMAddr(_section->addr()) : 0),
+ _count(_section ? _section->size() / sizeof(T) : 0) {
+ }
+
+ uint64_t count() const { return _count; }
+
+ T& get(uint64_t index) const {
+ if (index >= _count)
+ terminate("index out of range in section %s", _section->sectname());
+ return _base[index];
+ }
+
+private:
+ ContentAccessor* const _cache;
+ const macho_section<P>* const _section;
+ T * const _base;
+ uint64_t const _count;
+};
+
+
+#define SELOPT_WRITE
+#include "objc-shared-cache.h"
+#include "ObjC1Abstraction.hpp"
+#include "ObjC2Abstraction.hpp"
+
+
+namespace {
+
+
+
+template <typename P>
+class ObjCSelectorUniquer
+{
+public:
+ typedef typename P::uint_t pint_t;
+
+ ObjCSelectorUniquer(ContentAccessor* cache) : _cache(cache) { }
+
+ pint_t visit(pint_t oldValue)
+ {
+ _count++;
+ const char *s = (const char *)_cache->contentForVMAddr(oldValue);
+ objc_opt::string_map::iterator element =
+ _selectorStrings.insert(objc_opt::string_map::value_type(s, oldValue)).first;
+ return (pint_t)element->second;
+ }
+
+ objc_opt::string_map& strings() {
+ return _selectorStrings;
+ }
+
+ size_t count() const { return _count; }
+
+private:
+ objc_opt::string_map _selectorStrings;
+ ContentAccessor* _cache;
+ size_t _count = 0;
+};
+
+
+template <typename P>
+class ClassListBuilder
+{
+private:
+ objc_opt::string_map _classNames;
+ objc_opt::class_map _classes;
+ size_t _count = 0;
+ HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& _hInfos;
+
+public:
+
+ ClassListBuilder(HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& hinfos) : _hInfos(hinfos) { }
+
+ void visitClass(ContentAccessor* cache,
+ const macho_header<P>* header,
+ objc_class_t<P>* cls)
+ {
+ if (cls->isMetaClass(cache)) return;
+
+ const char *name = cls->getName(cache);
+ uint64_t name_vmaddr = cache->vmAddrForContent((void*)name);
+ uint64_t cls_vmaddr = cache->vmAddrForContent(cls);
+ uint64_t hinfo_vmaddr = cache->vmAddrForContent(_hInfos.hinfoForHeader(cache, header));
+ _classNames.insert(objc_opt::string_map::value_type(name, name_vmaddr));
+ _classes.insert(objc_opt::class_map::value_type(name, std::pair<uint64_t, uint64_t>(cls_vmaddr, hinfo_vmaddr)));
+ _count++;
+ }
+
+ objc_opt::string_map& classNames() {
+ return _classNames;
+ }
+
+ objc_opt::class_map& classes() {
+ return _classes;
+ }
+
+ size_t count() const { return _count; }
+};
+
+template <typename P>
+class ProtocolOptimizer
+{
+private:
+ typedef typename P::uint_t pint_t;
+
+ objc_opt::string_map _protocolNames;
+ objc_opt::protocol_map _protocols;
+ size_t _protocolCount;
+ size_t _protocolReferenceCount;
+
+ friend class ProtocolReferenceWalker<P, ProtocolOptimizer<P>>;
+
+ pint_t visitProtocolReference(ContentAccessor* cache, pint_t oldValue)
+ {
+ objc_protocol_t<P>* proto = (objc_protocol_t<P>*)
+ cache->contentForVMAddr(oldValue);
+ pint_t newValue = (pint_t)_protocols[proto->getName(cache)];
+ if (oldValue != newValue) _protocolReferenceCount++;
+ return newValue;
+ }
+
+public:
+
+ ProtocolOptimizer()
+ : _protocolNames()
+ , _protocols()
+ , _protocolCount(0)
+ , _protocolReferenceCount(0)
+ { }
+
+ void addProtocols(ContentAccessor* cache,
+ const macho_header<P>* header)
+ {
+ PointerSection<P, objc_protocol_t<P> *>
+ protocols(cache, header, "__DATA", "__objc_protolist");
+
+ for (pint_t i = 0; i < protocols.count(); i++) {
+ objc_protocol_t<P> *proto = protocols.get(i);
+
+ const char *name = proto->getName(cache);
+ if (_protocolNames.count(name) == 0) {
+ if (proto->getSize() > sizeof(objc_protocol_t<P>)) {
+ terminate("objc protocol is too big");
+ }
+
+ uint64_t name_vmaddr = cache->vmAddrForContent((void*)name);
+ uint64_t proto_vmaddr = cache->vmAddrForContent(proto);
+ _protocolNames.insert(objc_opt::string_map::value_type(name, name_vmaddr));
+ _protocols.insert(objc_opt::protocol_map::value_type(name, proto_vmaddr));
+ _protocolCount++;
+ }
+ }
+ }
+
+ const char *writeProtocols(ContentAccessor* cache,
+ uint8_t *& rwdest, size_t& rwremaining,
+ uint8_t *& rodest, size_t& roremaining,
+ std::vector<void*>& pointersInData,
+ pint_t protocolClassVMAddr)
+ {
+ if (_protocolCount == 0) return NULL;
+
+ if (protocolClassVMAddr == 0) {
+ return "libobjc's Protocol class symbol not found (metadata not optimized)";
+ }
+
+ size_t rwrequired = _protocolCount * sizeof(objc_protocol_t<P>);
+ if (rwremaining < rwrequired) {
+ return "libobjc's read-write section is too small (metadata not optimized)";
+ }
+
+ for (objc_opt::protocol_map::iterator iter = _protocols.begin();
+ iter != _protocols.end();
+ ++iter)
+ {
+ objc_protocol_t<P>* oldProto = (objc_protocol_t<P>*)
+ cache->contentForVMAddr(iter->second);
+
+ // Create a new protocol object.
+ objc_protocol_t<P>* proto = (objc_protocol_t<P>*)rwdest;
+ rwdest += sizeof(*proto);
+ rwremaining -= sizeof(*proto);
+
+ // Initialize it.
+ uint32_t oldSize = oldProto->getSize();
+ memcpy(proto, oldProto, oldSize);
+ if (!proto->getIsaVMAddr()) {
+ proto->setIsaVMAddr(protocolClassVMAddr);
+ }
+ if (oldSize < sizeof(*proto)) {
+ // Protocol object is old. Populate new fields.
+ proto->setSize(sizeof(objc_protocol_t<P>));
+ // missing extendedMethodTypes is already nil
+ }
+ // Some protocol objects are big enough to have the
+ // demangledName field but don't initialize it.
+ // Initialize it here if it is not already set.
+ if (!proto->getDemangledName(cache)) {
+ const char *roName = proto->getName(cache);
+ char *demangledName = copySwiftDemangledName(roName, true);
+ if (demangledName) {
+ size_t length = 1 + strlen(demangledName);
+ if (roremaining < length) {
+ return "libobjc's read-only section is too small (metadata not optimized)";
+ }
+
+ memmove(rodest, demangledName, length);
+ roName = (const char *)rodest;
+ rodest += length;
+ roremaining -= length;
+
+ free(demangledName);
+ }
+ proto->setDemangledName(cache, roName);
+ }
+ proto->setFixedUp();
+
+ // Redirect the protocol table at our new object.
+ iter->second = cache->vmAddrForContent(proto);
+
+ // Add new rebase entries.
+ proto->addPointers(pointersInData);
+ }
+
+ return NULL;
+ }
+
+ void updateReferences(ContentAccessor* cache, const macho_header<P>* header)
+ {
+ ProtocolReferenceWalker<P, ProtocolOptimizer<P>> refs(*this);
+ refs.walk(cache, header);
+ }
+
+ objc_opt::string_map& protocolNames() {
+ return _protocolNames;
+ }
+
+ objc_opt::protocol_map& protocols() {
+ return _protocols;
+ }
+
+ size_t protocolCount() const { return _protocolCount; }
+ size_t protocolReferenceCount() const { return _protocolReferenceCount; }
+};
+
+
+static int percent(size_t num, size_t denom) {
+ if (denom)
+ return (int)(num / (double)denom * 100);
+ else
+ return 100;
+}
+
+
+template <typename P>
+void optimizeObjC(SharedCache& cache, std::vector<void*>& pointersForASLR, bool forProduction)
+{
+ typedef typename P::E E;
+ typedef typename P::uint_t pint_t;
+
+ verboseLog("Optimizing objc metadata:");
+ verboseLog(" cache type is %s",
+ forProduction ? "production" : "development");
+
+ ContentAccessor cacheAccessor(cache);
+
+ size_t headerSize = P::round_up(sizeof(objc_opt::objc_opt_t));
+ if (headerSize != sizeof(objc_opt::objc_opt_t)) {
+ warning("libobjc's optimization structure size is wrong (metadata not optimized)");
+ }
+
+ //
+ // Find libobjc's empty sections and build list of images with objc metadata
+ //
+ const macho_section<P> *optROSection = nullptr;
+ const macho_section<P> *optRWSection = nullptr;
+ const macho_section<P> *optPointerListSection = nullptr;
+ std::vector<const macho_header<P>*> objcDylibs;
+ cache.forEachImage([&](const void* machHeader, const char* installName,
+ time_t, ino_t, const std::vector<MachOProxy::Segment>& segments) {
+ const macho_header<P>* mh = (const macho_header<P>*)machHeader;
+ if ( strstr(installName, "/libobjc.") != nullptr ) {
+ optROSection = mh->getSection("__TEXT", "__objc_opt_ro");
+ optRWSection = mh->getSection("__DATA", "__objc_opt_rw");
+ optPointerListSection = mh->getSection("__DATA", "__objc_opt_ptrs");
+ }
+ if ( mh->getSection("__DATA", "__objc_imageinfo") || mh->getSection("__OBJC", "__image_info") ) {
+ objcDylibs.push_back(mh);
+ }
+ // log("installName %s at mhdr 0x%016lx", installName, (uintptr_t)cacheAccessor.vmAddrForContent((void*)mh));
+ });
+ if ( optROSection == nullptr ) {
+ warning("libobjc's read-only section missing (metadata not optimized)");
+ return;
+ }
+ if ( optRWSection == nullptr ) {
+ warning("libobjc's read/write section missing (metadata not optimized)");
+ return;
+ }
+ if ( optPointerListSection == nullptr ) {
+ warning("libobjc's pointer list section missing (metadata not optimized)");
+ return;
+ }
+
+ uint8_t* optROData = (uint8_t*)cacheAccessor.contentForVMAddr(optROSection->addr());
+ size_t optRORemaining = optROSection->size();
+ uint8_t* optRWData = (uint8_t*)cacheAccessor.contentForVMAddr(optRWSection->addr());
+ size_t optRWRemaining = optRWSection->size();
+ if (optRORemaining < headerSize) {
+ warning("libobjc's read-only section is too small (metadata not optimized)");
+ return;
+ }
+ objc_opt::objc_opt_t* optROHeader = (objc_opt::objc_opt_t *)optROData;
+ optROData += headerSize;
+ optRORemaining -= headerSize;
+ if (E::get32(optROHeader->version) != objc_opt::VERSION) {
+ warning("libobjc's read-only section version is unrecognized (metadata not optimized)");
+ return;
+ }
+
+ if (optPointerListSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt<pint_t>)) {
+ warning("libobjc's pointer list section is too small (metadata not optimized)");
+ return;
+ }
+ const objc_opt::objc_opt_pointerlist_tt<pint_t> *optPointerList = (const objc_opt::objc_opt_pointerlist_tt<pint_t> *)cacheAccessor.contentForVMAddr(optPointerListSection->addr());
+
+ // Write nothing to optROHeader until everything else is written.
+ // If something fails below, libobjc will not use the section.
+
+
+ //
+ // Make copy of objcList and sort that list.
+ //
+ std::vector<const macho_header<P>*> addressSortedDylibs = objcDylibs;
+ std::sort(addressSortedDylibs.begin(), addressSortedDylibs.end(), [](const macho_header<P>* lmh, const macho_header<P>* rmh) -> bool {
+ return lmh < rmh;
+ });
+
+ //
+ // Build HeaderInfo list in cache
+ //
+ // First the RO header info
+ // log("writing out %d RO dylibs at offset %d", (uint32_t)objcDylibs.size(), (uint32_t)(optROSection->size() - optRORemaining));
+ uint64_t hinfoROVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
+ HeaderInfoOptimizer<P, objc_header_info_ro_t<P>> hinfoROOptimizer;
+ const char* err = hinfoROOptimizer.init((uint32_t)objcDylibs.size(), optROData, optRORemaining);
+ if (err) {
+ warning("%s", err);
+ return;
+ }
+ else {
+ for (const macho_header<P>* mh : addressSortedDylibs) {
+ hinfoROOptimizer.update(&cacheAccessor, mh, pointersForASLR);
+ }
+ }
+
+ // Then the RW header info
+ // log("writing out %d RW dylibs at offset %d", (uint32_t)objcDylibs.size(), (uint32_t)(optRWSection->size() - optRWRemaining));
+ uint64_t hinfoRWVMAddr = (uint64_t)optRWSection->addr() + (uint64_t)optRWSection->size() - optRWRemaining;
+ HeaderInfoOptimizer<P, objc_header_info_rw_t<P>> hinfoRWOptimizer;
+ err = hinfoRWOptimizer.init((uint32_t)objcDylibs.size(), optRWData, optRWRemaining);
+ if (err) {
+ warning("%s", err);
+ return;
+ }
+ else {
+ for (const macho_header<P>* mh : addressSortedDylibs) {
+ hinfoRWOptimizer.update(&cacheAccessor, mh, pointersForASLR);
+ }
+ }
+
+ //
+ // Update selector references and build selector list
+ //
+ // This is SAFE: if we run out of room for the selector table,
+ // the modified binaries are still usable.
+ //
+ // Heuristic: choose selectors from libraries with more selector cstring data first.
+ // This tries to localize selector cstring memory.
+ //
+ ObjCSelectorUniquer<P> uniq(&cacheAccessor);
+ std::vector<const macho_header<P>*> sizeSortedDylibs = objcDylibs;
+ std::sort(sizeSortedDylibs.begin(), sizeSortedDylibs.end(), [](const macho_header<P>* lmh, const macho_header<P>* rmh) -> bool {
+ const macho_section<P>* lSection = lmh->getSection("__TEXT", "__objc_methname");
+ const macho_section<P>* rSection = rmh->getSection("__TEXT", "__objc_methname");
+ uint64_t lSelectorSize = (lSection ? lSection->size() : 0);
+ uint64_t rSelectorSize = (rSection ? rSection->size() : 0);
+ return lSelectorSize > rSelectorSize;
+ });
+
+ SelectorOptimizer<P, ObjCSelectorUniquer<P> > selOptimizer(uniq);
+ for (const macho_header<P>* mh : sizeSortedDylibs) {
+ LegacySelectorUpdater<P, ObjCSelectorUniquer<P>>::update(&cacheAccessor, mh, uniq);
+ selOptimizer.optimize(&cacheAccessor, mh);
+ }
+
+ verboseLog(" uniqued % 6ld selectors",
+ uniq.strings().size());
+ verboseLog(" updated % 6ld selector references",
+ uniq.count());
+
+ uint64_t seloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
+ objc_opt::objc_selopt_t *selopt = new(optROData) objc_opt::objc_selopt_t;
+ err = selopt->write(seloptVMAddr, optRORemaining, uniq.strings());
+ if (err) {
+ warning("%s", err);
+ return;
+ }
+ optROData += selopt->size();
+ optRORemaining -= selopt->size();
+ uint32_t seloptCapacity = selopt->capacity;
+ uint32_t seloptOccupied = selopt->occupied;
+ selopt->byteswap(E::little_endian), selopt = nullptr;
+
+ verboseLog(" selector table occupancy %u/%u (%u%%)",
+ seloptOccupied, seloptCapacity,
+ (unsigned)(seloptOccupied/(double)seloptCapacity*100));
+
+
+ //
+ // Detect classes that have missing weak-import superclasses.
+ //
+ // Production only. Development cache does not do this: a replacement
+ // library could omit a class at runtime that was present during
+ // cache construction.
+ //
+ // This is SAFE: the binaries themselves are unmodified.
+ bool noMissingWeakSuperclasses = false; // dev cache can't promise otherwise
+ if (forProduction) {
+ WeakClassDetector<P> weakopt;
+ noMissingWeakSuperclasses =
+ weakopt.noMissingWeakSuperclasses(&cacheAccessor, sizeSortedDylibs);
+
+ // Shared cache does not currently support unbound weak references.
+ // Here we assert that there are none. If support is added later then
+ // this assertion needs to be removed and this path needs to be tested.
+ if (!noMissingWeakSuperclasses) {
+ terminate("Some Objective-C class has a superclass that is "
+ "weak-import and missing from the cache.");
+ }
+ }
+
+
+ //
+ // Build class table.
+ //
+ // This is SAFE: the binaries themselves are unmodified.
+ ClassListBuilder<P> classes(hinfoROOptimizer);
+ ClassWalker<P, ClassListBuilder<P>> classWalker(classes);
+ for (const macho_header<P>* mh : sizeSortedDylibs) {
+ classWalker.walk(&cacheAccessor, mh);
+ }
+
+ verboseLog(" recorded % 6ld classes",
+ classes.classNames().size());
+
+ uint64_t clsoptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
+ objc_opt::objc_clsopt_t *clsopt = new(optROData) objc_opt::objc_clsopt_t;
+ err = clsopt->write(clsoptVMAddr, optRORemaining,
+ classes.classNames(), classes.classes(), false);
+ if (err) {
+ warning("%s", err);
+ return;
+ }
+ optROData += clsopt->size();
+ optRORemaining -= clsopt->size();
+ size_t duplicateCount = clsopt->duplicateCount();
+ uint32_t clsoptCapacity = clsopt->capacity;
+ uint32_t clsoptOccupied = clsopt->occupied;
+ clsopt->byteswap(E::little_endian);
+ clsopt = nullptr;
+
+ verboseLog(" found % 6ld duplicate classes",
+ duplicateCount);
+ verboseLog(" class table occupancy %u/%u (%u%%)",
+ clsoptOccupied, clsoptCapacity,
+ (unsigned)(clsoptOccupied/(double)clsoptCapacity*100));
+
+
+ //
+ // Sort method lists.
+ //
+ // This is SAFE: modified binaries are still usable as unsorted lists.
+ // This must be done AFTER uniquing selectors.
+ MethodListSorter<P> methodSorter;
+ for (const macho_header<P>* mh : sizeSortedDylibs) {
+ methodSorter.optimize(&cacheAccessor, mh);
+ }
+
+ verboseLog(" sorted % 6ld method lists",
+ methodSorter.optimized());
+
+
+ // Unique protocols and build protocol table.
+
+ // This is SAFE: no protocol references are updated yet
+ // This must be done AFTER updating method lists.
+
+ ProtocolOptimizer<P> protocolOptimizer;
+ for (const macho_header<P>* mh : sizeSortedDylibs) {
+ protocolOptimizer.addProtocols(&cacheAccessor, mh);
+ }
+
+ verboseLog(" uniqued % 6ld protocols",
+ protocolOptimizer.protocolCount());
+
+ pint_t protocolClassVMAddr = (pint_t)P::getP(optPointerList->protocolClass);
+ err = protocolOptimizer.writeProtocols(&cacheAccessor,
+ optRWData, optRWRemaining,
+ optROData, optRORemaining,
+ pointersForASLR, protocolClassVMAddr);
+ if (err) {
+ warning("%s", err);
+ return;
+ }
+
+ uint64_t protocoloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
+ objc_opt::objc_protocolopt_t *protocolopt = new (optROData) objc_opt::objc_protocolopt_t;
+ err = protocolopt->write(protocoloptVMAddr, optRORemaining,
+ protocolOptimizer.protocolNames(),
+ protocolOptimizer.protocols(), true);
+ if (err) {
+ warning("%s", err);
+ return;
+ }
+ optROData += protocolopt->size();
+ optRORemaining -= protocolopt->size();
+ uint32_t protocoloptCapacity = protocolopt->capacity;
+ uint32_t protocoloptOccupied = protocolopt->occupied;
+ protocolopt->byteswap(E::little_endian), protocolopt = NULL;
+
+ verboseLog(" protocol table occupancy %u/%u (%u%%)",
+ protocoloptOccupied, protocoloptCapacity,
+ (unsigned)(protocoloptOccupied/(double)protocoloptCapacity*100));
+
+
+ // Redirect protocol references to the uniqued protocols.
+
+ // This is SAFE: the new protocol objects are still usable as-is.
+ for (const macho_header<P>* mh : sizeSortedDylibs) {
+ protocolOptimizer.updateReferences(&cacheAccessor, mh);
+ }
+
+ verboseLog(" updated % 6ld protocol references",
+ protocolOptimizer.protocolReferenceCount());
+
+
+ //
+ // Repair ivar offsets.
+ //
+ // This is SAFE: the runtime always validates ivar offsets at runtime.
+ IvarOffsetOptimizer<P> ivarOffsetOptimizer;
+ for (const macho_header<P>* mh : sizeSortedDylibs) {
+ ivarOffsetOptimizer.optimize(&cacheAccessor, mh);
+ }
+
+ verboseLog(" updated % 6ld ivar offsets",
+ ivarOffsetOptimizer.optimized());
+
+
+ // Collect flags.
+ uint32_t headerFlags = 0;
+ if (forProduction) {
+ headerFlags |= objc_opt::IsProduction;
+ }
+ if (noMissingWeakSuperclasses) {
+ headerFlags |= objc_opt::NoMissingWeakSuperclasses;
+ }
+
+
+ // Success. Mark dylibs as optimized.
+ for (const macho_header<P>* mh : sizeSortedDylibs) {
+ const macho_section<P>* imageInfoSection = mh->getSection("__DATA", "__objc_imageinfo");
+ if (!imageInfoSection) {
+ imageInfoSection = mh->getSection("__OBJC", "__image_info");
+ }
+ if (imageInfoSection) {
+ objc_image_info<P>* info = (objc_image_info<P>*)cacheAccessor.contentForVMAddr(imageInfoSection->addr());
+ info->setOptimizedByDyld();
+ }
+ }
+
+
+ // Success. Update RO header last.
+ E::set32(optROHeader->flags, headerFlags);
+ E::set32(optROHeader->selopt_offset, (uint32_t)(seloptVMAddr - optROSection->addr()));
+ E::set32(optROHeader->clsopt_offset, (uint32_t)(clsoptVMAddr - optROSection->addr()));
+ E::set32(optROHeader->protocolopt_offset, (uint32_t)(protocoloptVMAddr - optROSection->addr()));
+ E::set32(optROHeader->headeropt_ro_offset, (uint32_t)(hinfoROVMAddr - optROSection->addr()));
+ E::set32(optROHeader->headeropt_rw_offset, (uint32_t)(hinfoRWVMAddr - optROSection->addr()));
+
+ // Log statistics.
+ size_t roSize = optROSection->size() - optRORemaining;
+ size_t rwSize = optRWSection->size() - optRWRemaining;
+ verboseLog(" %zu/%llu bytes "
+ "(%d%%) used in libobjc read-only optimization section",
+ roSize, optROSection->size(),
+ percent(roSize, optROSection->size()));
+ verboseLog(" %zu/%llu bytes "
+ "(%d%%) used in libobjc read/write optimization section",
+ rwSize, optRWSection->size(),
+ percent(rwSize, optRWSection->size()));
+ verboseLog(" wrote objc metadata optimization version %d",
+ objc_opt::VERSION);
+}
+
+
+} // anon namespace
+
+
+void SharedCache::optimizeObjC(bool forProduction)
+{
+ switch ( _arch.arch ) {
+ case CPU_TYPE_ARM:
+ case CPU_TYPE_I386:
+ ::optimizeObjC<Pointer32<LittleEndian>>(*this, _pointersForASLR, forProduction);
+ break;
+ case CPU_TYPE_X86_64:
+ case CPU_TYPE_ARM64:
+ ::optimizeObjC<Pointer64<LittleEndian>>(*this, _pointersForASLR, forProduction);
+ break;
+ default:
+ terminate("unsupported arch 0x%08X", _arch.arch);
+ }
+}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 "mega-dylib-utils.h"
+#include "MachOFileAbstraction.hpp"
+#include "FileAbstraction.hpp"
+#include "Logging.h"
+
+#include "dyld_cache_config.h"
+
+#import "Trie.hpp"
+
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <assert.h>
+#include <unistd.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <map>
+#include <set>
+#include <array>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "OptimizerBranches.h"
+
+#include "CacheFileAbstraction.hpp"
+#include "CodeSigningTypes.h"
+
+namespace {
+
+uint64_t sharedRegionStartExecutableAddress(ArchPair arch)
+{
+ switch (arch.arch) {
+ case CPU_TYPE_ARM:
+ return ARM_SHARED_REGION_START;
+ case CPU_TYPE_I386:
+ return SHARED_REGION_BASE_I386;
+ case CPU_TYPE_X86_64:
+ return SHARED_REGION_BASE_X86_64;
+ case CPU_TYPE_ARM64:
+ return ARM64_SHARED_REGION_START;
+ default:
+ terminate("unsupported arch 0x%08X", arch.arch);
+ }
+}
+
+uint64_t sharedRegionStartWriteableAddress(ArchPair arch, uint64_t textEndAddress)
+{
+ switch (arch.arch) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ // more efficient if code and data never in same 2MB chunk
+ return textEndAddress + 0x04000000;
+ case CPU_TYPE_ARM:
+ return textEndAddress;
+ case CPU_TYPE_ARM64:
+ return textEndAddress + 32*1024*1024; // <rdar://problem/18564532> Add 32MB padding before arm64 dyld shared cache R/W region
+ default:
+ terminate("unsupported arch 0x%08X", arch.arch);
+ }
+}
+
+uint64_t sharedRegionStartReadOnlyAddress(ArchPair arch, uint64_t dataEndAddress, uint64_t textEndAddress)
+{
+ switch (arch.arch) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ // more efficient if code and data never in same 2MB chunk
+ return dataEndAddress + 0x04000000;
+ case CPU_TYPE_ARM:
+ return dataEndAddress;
+ case CPU_TYPE_ARM64:
+ return dataEndAddress + 32*1024*1024; // <rdar://problem/18564532> Add 32MB padding before arm64 dyld shared cache R/W region
+ default:
+ terminate("unsupported arch 0x%08X", arch.arch);
+ }
+}
+
+} // anon namespace
+
+uint8_t sharedRegionRegionAlignment(ArchPair arch) {
+ switch (arch.arch) {
+ return ARM_SHARED_REGION_SIZE;
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ return 12; // 4KB
+ case CPU_TYPE_ARM:
+ case CPU_TYPE_ARM64:
+ return 14; // 16KB
+ default:
+ terminate("unsupported arch 0x%08X", arch.arch);
+ }
+}
+
+uint64_t sharedRegionRegionSize(ArchPair arch) {
+ switch ( arch.arch ) {
+ case CPU_TYPE_I386:
+ return SHARED_REGION_SIZE_I386;
+ case CPU_TYPE_X86_64:
+ return SHARED_REGION_SIZE_X86_64;
+ case CPU_TYPE_ARM:
+ return ARM_SHARED_REGION_SIZE;
+ case CPU_TYPE_ARM64:
+ return ARM64_SHARED_REGION_SIZE;
+ default:
+ terminate("unsupported arch 0x%08X", arch.arch);
+ }
+}
+
+static const std::tuple<const char* const, const char* const, const ArchPair> gArchitectures[] = {
+ {"i386", nullptr, ArchPair( CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL )},
+ {"x86_64", nullptr, ArchPair( CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL )},
+ {"x86_64h", "x86_64", ArchPair( CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H )},
+ {"armv4t", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T )},
+ {"armv5", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ )},
+ {"armv6", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 )},
+ {"armv7", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 )},
+ {"armv7f", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7F )},
+ {"armv7k", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K )},
+ {"armv7s", "armv7", ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S )},
+ {"arm64", nullptr, ArchPair( CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL )},
+};
+
+ArchPair archForString(const std::string& archStr) {
+ for (auto& a : gArchitectures) {
+ if ( std::get<0>( a ) == archStr ) return std::get<2>( a );
+ }
+ terminate("unknown architecture %s", archStr.c_str());
+}
+
+std::string stringForArch(ArchPair arch, bool allowUnknown) {
+ for (auto& a : gArchitectures) {
+ // FIXME LIB64 is set on some binaries and not other
+ if ( std::get<2>( a ).arch == arch.arch && std::get<2>( a ).subtype == ( arch.subtype & ~CPU_SUBTYPE_MASK ) )
+ return std::get<0>( a );
+ }
+
+ auto unknownString =
+ "unrecognized cpu type " + std::to_string(arch.arch) +
+ " subtype " + std::to_string(arch.subtype);
+ if (allowUnknown) return unknownString;
+ else terminate("%s", unknownString.c_str());
+}
+
+std::string fallbackArchStringForArchString( const std::string& archStr ) {
+ for ( auto& a : gArchitectures ) {
+ if ( std::get<0>( a ) == archStr && std::get<1>( a ) != nullptr ) {
+ return std::get<1>( a );
+ }
+ }
+
+ return "";
+}
+
+SharedCache::SharedCache(Manifest& manifest,
+ const std::string& configuration, const std::string& architecture) :
+ _manifest(manifest), _arch(archForString(architecture)),
+ _archManifest(manifest.configurations.find(configuration)->second.architectures.find(architecture)->second), _buffer(nullptr),
+ _fileSize(0), _vmSize(0), _aliasCount(0), _slideInfoFileOffset(0), _slideInfoBufferSize(0) {
+ auto maxCacheVMSize = sharedRegionRegionSize(_arch);
+
+ for ( auto& includedDylib : _archManifest.results.dylibs ) {
+ if (includedDylib.second.included) {
+ //assert(manifest.dylibs.count(includedDylib.first) > 0);
+ //assert(manifest.dylibs.find(includedDylib.first)->second.proxies.count(architecture) > 0);
+ MachOProxy* proxy = _manifest.dylibProxy( includedDylib.first, architecture );
+ assert(proxy != nullptr);
+ _dylibs.push_back(proxy);
+ }
+ }
+
+ // <rdar://problem/21317611> error out instead of crash if cache has no dylibs
+ if ( _dylibs.size() < 30 ) // FIXME: plist should specify required vs optional dylibs
+ terminate("missing required minimum set of dylibs");
+
+ for (auto &dylib : _dylibs) {
+ _segmentMap[dylib].reserve(dylib->segments.size());
+ for (const auto& seg : dylib->segments)
+ _segmentMap[dylib].push_back(&seg);
+ _aliasCount += dylib->installNameAliases.size();
+ }
+
+ sortDylibs(_manifest.dylibOrderFile);
+ if ( !_manifest.dirtyDataOrderFile.empty() )
+ loadDirtyDataOrderFile(_manifest.dirtyDataOrderFile);
+
+ assignSegmentAddresses();
+ if ( _vmSize > maxCacheVMSize )
+ verboseLog("%s cache overflow. %lluMB (max %lluMB)", archName().c_str(), _vmSize/1024/1024, maxCacheVMSize/1024/1024);
+ while (_vmSize > maxCacheVMSize) {
+ auto evictedDylib = manifest.removeLargestLeafDylib( configuration, architecture );
+ _dylibs.erase( std::remove( _dylibs.begin(), _dylibs.end(), evictedDylib ), _dylibs.end() );
+ _aliasCount -= evictedDylib->installNameAliases.size();
+ assignSegmentAddresses();
+ }
+}
+
+// There is an order file specifying the order in which dylibs are laid out in
+// general, as well as an order file specifying the order in which __DATA_DIRTY
+// segments are laid out in particular.
+//
+// The syntax is one dylib (install name) per line. Blank lines are ignored.
+// Comments start with the # character.
+
+static std::unordered_map<std::string, uint32_t> loadOrderFile(const std::string& orderFile) {
+ std::unordered_map<std::string, uint32_t> order;
+
+ std::ifstream myfile(orderFile);
+ if ( myfile.is_open() ) {
+ uint32_t count = 0;
+ std::string line;
+ while ( std::getline(myfile, line) ) {
+ size_t pos = line.find('#');
+ if ( pos != std::string::npos )
+ line.resize(pos);
+ while ( !line.empty() && isspace(line.back()) ) {
+ line.pop_back();
+ }
+ if ( !line.empty() )
+ order[line] = count++;
+ }
+ myfile.close();
+ } else {
+ warning("could not load orderfile '%s'", orderFile.c_str());
+ }
+
+ return order;
+}
+
+void SharedCache::loadDirtyDataOrderFile(const std::string& dirtyDataOrderFile) {
+ _dataDirtySegsOrder = loadOrderFile(dirtyDataOrderFile);
+}
+
+void SharedCache::sortDylibs(const std::string& dylibOrderFile) {
+ std::unordered_map<std::string, uint32_t> dylibOrder;
+ if ( !dylibOrderFile.empty() )
+ dylibOrder = loadOrderFile(dylibOrderFile);
+
+ std::sort(_dylibs.begin(), _dylibs.end(), [&](const MachOProxy* a,
+ const MachOProxy* b) {
+ const std::string& pathA = a->installName;
+ const std::string& pathB = b->installName;
+
+ const auto& orderA = dylibOrder.find(pathA);
+ const auto& orderB = dylibOrder.find(pathB);
+ bool foundA = (orderA != dylibOrder.end());
+ bool foundB = (orderB != dylibOrder.end());
+
+ // Order all dylibs specified in the order file first, in the order specified in
+ // the file, followed by any other dylibs in lexicographic order.
+ if ( foundA && foundB )
+ return orderA->second < orderB->second;
+ else if ( foundA )
+ return true;
+ else if ( foundB )
+ return false;
+ else
+ return pathA < pathB;
+ });
+}
+
+void SharedCache::buildUnoptimizedCache(void) {
+ _buffer = std::shared_ptr<void>(calloc(_fileSize, 1), free);
+ writeCacheHeader();
+ writeCacheSegments();
+ rebaseAll();
+ bindAll();
+}
+
+template <typename P>
+void SharedCache::buildForDevelopment(const std::string& cachePath) {
+ typedef typename P::E E;
+ std::vector<uint64_t> emptyBranchPoolOffsets;
+ buildUnoptimizedCache();
+ optimizeObjC(false/*not production*/);
+ if (_manifest.platform == "osx") {
+ optimizeLinkedit(false, false, emptyBranchPoolOffsets);
+ } else {
+ optimizeLinkedit(true, false, emptyBranchPoolOffsets);
+ }
+ writeSlideInfoV2();
+
+ dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
+ header->set_cacheType(kDyldSharedCacheTypeDevelopment);
+ recomputeCacheUUID();
+
+ // Calculate the VMSize of the resulting cache
+ uint64_t endAddr = 0;
+
+ forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ if (vmAddr+size > endAddr)
+ endAddr = vmAddr+size;
+ });
+ _vmSize = endAddr - sharedRegionStartExecutableAddress(_arch);
+
+ if (_manifest.platform == "osx") {
+ appendCodeSignature("release");
+ } else {
+ appendCodeSignature("development");
+ }
+}
+
+template <typename P>
+void SharedCache::buildForProduction(const std::string& cachePath) {
+ typedef typename P::E E;
+ buildUnoptimizedCache();
+ optimizeObjC(true/*production*/);
+ uint64_t cacheStartAddress = sharedRegionStartExecutableAddress(_arch);
+
+ dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
+ header->set_cacheType(kDyldSharedCacheTypeProduction);
+
+ // build vector of branch pool addresss
+ std::vector<uint64_t> branchPoolStartAddrs;
+ std::vector<uint64_t> branchPoolOffsets;
+ const uint64_t* p = (uint64_t*)((uint8_t*)_buffer.get() + header->branchPoolsOffset());
+ for (int i=0; i < header->branchPoolsCount(); ++i) {
+ uint64_t poolAddr = LittleEndian::get64(p[i]);
+ branchPoolStartAddrs.push_back(poolAddr);
+ branchPoolOffsets.push_back(poolAddr - cacheStartAddress);
+ }
+
+ bypassStubs(branchPoolStartAddrs);
+ optimizeLinkedit(true, true, branchPoolOffsets);
+ writeSlideInfoV2();
+
+ recomputeCacheUUID();
+
+ // Calculate the VMSize of the resulting cache
+ uint64_t endAddr = 0;
+
+ forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ if (vmAddr+size > endAddr)
+ endAddr = vmAddr+size;
+ });
+ _vmSize = endAddr - cacheStartAddress;
+
+ appendCodeSignature("release");
+}
+
+bool SharedCache::writeCacheMapFile(const std::string& mapPath) {
+ FILE* fmap = ::fopen(mapPath.c_str(), "w");
+ if ( fmap == NULL )
+ return false;
+
+ std::vector<uint64_t> regionStartAddresses;
+ std::vector<uint64_t> regionSizes;
+ std::vector<uint64_t> regionFileOffsets;
+
+ forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ regionStartAddresses.push_back(vmAddr);
+ regionSizes.push_back(size);
+ regionFileOffsets.push_back((uint8_t*)content - (uint8_t*)_buffer.get());
+ const char* prot = "RW";
+ if ( permissions == (VM_PROT_EXECUTE|VM_PROT_READ) )
+ prot = "EX";
+ else if ( permissions == VM_PROT_READ )
+ prot = "RO";
+ if ( size > 1024*1024 )
+ fprintf(fmap, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, size/(1024*1024), vmAddr, vmAddr+size);
+ else
+ fprintf(fmap, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, size/1024, vmAddr, vmAddr+size);
+ });
+
+ // TODO: add linkedit breakdown
+ fprintf(fmap, "\n\n");
+
+ std::unordered_set<const void*> seenHeaders;
+ forEachImage([&](const void* machHeader, const char* installName, time_t mtime,
+ ino_t inode, const std::vector<MachOProxy::Segment>& segments) {
+ if ( !seenHeaders.count(machHeader) ) {
+ seenHeaders.insert(machHeader);
+
+ fprintf(fmap, "%s\n", installName);
+ for (const MachOProxy::Segment& seg : segments) {
+ uint64_t vmAddr = 0;
+ for (int i=0; i < regionSizes.size(); ++i) {
+ if ( (seg.fileOffset >= regionFileOffsets[i]) && (seg.fileOffset < (regionFileOffsets[i]+regionSizes[i])) ) {
+ vmAddr = regionStartAddresses[i] + seg.fileOffset - regionFileOffsets[i];
+ }
+ }
+ fprintf(fmap, "\t%16s 0x%08llX -> 0x%08llX\n", seg.name.c_str(), vmAddr, vmAddr+seg.size);
+ }
+ }
+ });
+
+
+ ::fclose(fmap);
+ return true;
+}
+
+template <typename P>
+std::vector<MachOProxy::Segment> getSegments(const void* cacheBuffer, const void* machHeader)
+{
+ std::vector<MachOProxy::Segment> result;
+ macho_header<P>* mh = (macho_header<P>*)machHeader;
+ const uint32_t cmd_count = mh->ncmds();
+ const macho_load_command<P>* cmd = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() != macho_segment_command<P>::CMD )
+ continue;
+ macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
+ MachOProxy::Segment seg;
+ seg.name = segCmd->segname();
+ seg.size = segCmd->vmsize();
+ seg.diskSize = (uint32_t)segCmd->filesize();
+ seg.fileOffset = (uint32_t)segCmd->fileoff();
+ seg.protection = segCmd->initprot();
+ // HACK until lldb fixed in <rdar://problem/20357466>
+ if ( (seg.fileOffset == 0) && (strcmp(segCmd->segname(), "__TEXT") == 0) )
+ seg.fileOffset = (uint32_t)((char*)machHeader - (char*)cacheBuffer);
+ if ( segCmd->nsects() > 0 ) {
+ seg.p2align = 0;
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( sect->align() > seg.p2align )
+ seg.p2align = sect->align();
+ }
+ }
+ else {
+ seg.p2align = 12;
+ }
+ result.push_back(seg);
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ return result;
+}
+
+template <typename P>
+void SharedCache::forEachImage(DylibHandler handler)
+{
+#if NEW_CACHE_FILE_FORMAT
+ terminate("forEachImage() not implemented");
+#else
+ typedef typename P::E E;
+ const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
+ const dyldCacheImageInfo<E>* dylibs = (dyldCacheImageInfo<E>*)((char*)_buffer.get() + header->imagesOffset());
+ const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)((char*)_buffer.get() + header->mappingOffset());
+ if ( mappings[0].file_offset() != 0 )
+ terminate("malformed cache file");
+ uint64_t firstImageOffset = 0;
+ uint64_t firstRegionAddress = mappings[0].address();
+ const void* cacheEnd = (char*)_buffer.get() + _fileSize;
+ if ( (const void*)&dylibs[header->imagesCount()] > cacheEnd )
+ return;
+ for (uint32_t i=0; i < header->imagesCount(); ++i) {
+ const char* dylibPath = (char*)_buffer.get() + dylibs[i].pathFileOffset();
+ if ( dylibPath > cacheEnd )
+ return;
+ uint64_t offset = dylibs[i].address() - firstRegionAddress;
+ if ( firstImageOffset == 0 )
+ firstImageOffset = offset;
+ // skip over aliases
+ if ( dylibs[i].pathFileOffset() < firstImageOffset)
+ continue;
+ const void* mh = (char*)_buffer.get() + offset;
+ time_t inode = dylibs[i].inode();
+ ino_t modTime = dylibs[i].modTime();
+ handler(mh, dylibPath, modTime, inode, getSegments<P>(_buffer.get(), mh));
+ }
+#endif
+}
+
+
+template <typename P>
+void SharedCache::recomputeCacheUUID(void)
+{
+ uint8_t* uuidLoc = nullptr;
+#if NEW_CACHE_FILE_FORMAT
+ const macho_header<P>* mh = (macho_header<P>*)cacheBuffer;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ const uint32_t cmd_count = mh->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == LC_UUID ) {
+ const macho_uuid_command<P>* uuidCmd = (macho_uuid_command<P>*)cmd;
+ uuidLoc = const_cast<uint8_t*>(uuidCmd->uuid());
+ break;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+#else
+ dyldCacheHeader<P>* header = (dyldCacheHeader<P>*)_buffer.get();
+ uuidLoc = const_cast<uint8_t*>(header->uuid());
+#endif
+
+ // Clear existing UUID, then MD5 whole cache buffer.
+ bzero(uuidLoc, 16);
+ CC_MD5(_buffer.get(), (unsigned)_fileSize, uuidLoc);
+ // <rdar://problem/6723729> uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
+ uuidLoc[6] = ( uuidLoc[6] & 0x0F ) | ( 3 << 4 );
+ uuidLoc[8] = ( uuidLoc[8] & 0x3F ) | 0x80;
+}
+
+template <typename P>
+void SharedCache::setLinkeditsMappingEndFileOffset(uint64_t newFileSize)
+{
+#if NEW_CACHE_FILE_FORMAT
+ terminate("setLinkeditsMappingEndFileOffset() not implemented");
+#else
+ typedef typename P::E E;
+ dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
+ dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)((char*)_buffer.get() + header->mappingOffset());
+ uint64_t newReadOnlySize = newFileSize - mappings[2].file_offset();
+ mappings[2].set_size(newReadOnlySize);
+ header->set_codeSignatureOffset(newFileSize);
+ _readOnlyRegion.size = (newReadOnlySize);
+#endif
+}
+
+template <typename P>
+void SharedCache::setUnmappedLocalsRange(uint64_t localSymbolsOffset, uint32_t unmappedSize)
+{
+#if NEW_CACHE_FILE_FORMAT
+ terminate("setUnmappedLocalsRange() not implemented");
+#else
+ typedef typename P::E E;
+ dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
+ header->set_localSymbolsOffset(localSymbolsOffset);
+ header->set_localSymbolsSize(unmappedSize);
+ // move start of code signature to new end of file
+ header->set_codeSignatureOffset(localSymbolsOffset+unmappedSize);
+#endif
+}
+
+template <typename P>
+void SharedCache::setAcceleratorInfoRange(uint64_t accelInfoAddr, uint32_t accelInfoSize)
+{
+#if NEW_CACHE_FILE_FORMAT
+ terminate("setUnmappedLocalsRange() not implemented");
+#else
+ typedef typename P::E E;
+ dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
+ header->set_accelerateInfoAddr(accelInfoAddr);
+ header->set_accelerateInfoSize(accelInfoSize);
+#endif
+}
+
+template <typename P>
+void SharedCache::forEachRegion(RegionHandler handler)
+{
+#if NEW_CACHE_FILE_FORMAT
+ const macho_header<P>* mh = (macho_header<P>*)cacheBuffer;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ const uint32_t cmd_count = mh->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
+ handler((char*)cacheBuffer + segCmd->fileoff(), segCmd->vmaddr(), segCmd->vmsize(), segCmd->initprot());
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+#else
+ typedef typename P::E E;
+ const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
+ const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)((char*)_buffer.get() + header->mappingOffset());
+ const dyldCacheFileMapping<E>* mappingsEnd = &mappings[header->mappingCount()];
+ for (const dyldCacheFileMapping<E>* m=mappings; m < mappingsEnd; ++m) {
+ handler((char*)_buffer.get() + m->file_offset(), m->address(), m->size(), m->init_prot());
+ }
+#endif
+}
+
+std::shared_ptr<void> SharedCache::buffer(void) const {
+ return _buffer;
+}
+
+std::string SharedCache::archName() {
+ return stringForArch(_arch);
+}
+
+void SharedCache::assignSegmentAddresses()
+{
+ _branchPoolStarts.clear();
+ uint64_t addr = sharedRegionStartExecutableAddress(_arch);
+
+ // assign TEXT segment addresses
+ _textRegion.address = addr;
+ _textRegion.fileOffset = 0;
+ _textRegion.prot = VM_PROT_READ | VM_PROT_EXECUTE;
+#if NEW_CACHE_FILE_FORMAT
+ addr += 0x4000; // header
+#else
+ addr += 0x28000; // header
+#endif
+ uint64_t brPoolTextSize = branchPoolTextSize(_arch);
+ uint64_t brPoolLinkEditSize = branchPoolLinkEditSize(_arch);
+ uint64_t brRearch = branchReach(_arch);
+ uint64_t lastPoolAddress = addr;
+ for (auto& dylib : _dylibs) {
+ for (auto& seg : _segmentMap[dylib]) {
+ if ( seg.base->protection != (VM_PROT_READ | VM_PROT_EXECUTE) )
+ continue;
+ // Insert branch island pools every 128MB for arm64
+ if ( (brPoolTextSize != 0) && ((addr + seg.base->size - lastPoolAddress) > brRearch) ) {
+ _branchPoolStarts.push_back(addr);
+ //verboseLog("adding branch pool at 0x%lX\n", addr);
+ lastPoolAddress = addr;
+ addr += brPoolTextSize;
+ }
+ // Keep __TEXT segments 4K or more aligned
+ uint64_t startAlignPad = align(addr, std::max(seg.base->p2align, (uint8_t)12)) - addr;
+ addr += startAlignPad;
+ seg.address = addr;
+ seg.cacheFileOffset = addr - _textRegion.address + _textRegion.fileOffset;
+ seg.cacheSegSize = align(seg.base->sizeOfSections, 12);
+ addr += align(seg.base->sizeOfSections, 12);
+ }
+ }
+ // align TEXT region end
+ uint64_t endTextAddress = align(addr, sharedRegionRegionAlignment(_arch));
+ _textRegion.size = endTextAddress - _textRegion.address;
+
+ std::unordered_map<const SegmentInfo*, std::string> dataDirtySegPaths;
+
+ // co-locate similar __DATA* segments
+ std::vector<SegmentInfo*> dataSegs;
+ std::vector<SegmentInfo*> dataConstSegs;
+ std::vector<SegmentInfo*> dataDirtySegs;
+ for (auto& dylib : _dylibs) {
+ for (auto& seg : _segmentMap[dylib]) {
+ if ( seg.base->protection == (VM_PROT_READ | VM_PROT_WRITE) ) {
+ if ( seg.base->name == "__DATA_CONST" ) {
+ dataConstSegs.push_back(&seg);
+ }
+ else if ( seg.base->name == "__DATA_DIRTY" ) {
+ dataDirtySegs.push_back(&seg);
+ dataDirtySegPaths[&seg] = dylib->installName;
+ }
+ else {
+ dataSegs.push_back(&seg);
+ }
+ }
+ }
+ }
+
+ // assign __DATA* addresses
+ addr = sharedRegionStartWriteableAddress(_arch, endTextAddress);
+ _dataRegion.address = addr;
+ _dataRegion.fileOffset = _textRegion.fileOffset + _textRegion.size;
+ _dataRegion.prot = VM_PROT_READ | VM_PROT_WRITE;
+
+ // layout all __DATA_CONST segments
+ for (SegmentInfo* seg : dataConstSegs) {
+ // Keep __DATA_CONST segments 4K or more aligned
+ uint64_t startAlignPad = align(addr, std::max(seg->base->p2align, (uint8_t)12)) - addr;
+ addr += startAlignPad;
+ seg->address = addr;
+ seg->cacheFileOffset = addr - _dataRegion.address + _dataRegion.fileOffset;
+ seg->cacheSegSize = seg->base->sizeOfSections;
+ addr += seg->base->sizeOfSections;
+ }
+
+ // layout all __DATA segments
+ for (SegmentInfo* seg : dataSegs) {
+ // Keep __DATA segments 4K or more aligned
+ uint64_t startAlignPad = align(addr, std::max(seg->base->p2align, (uint8_t)12)) - addr;
+ addr += startAlignPad;
+ seg->address = addr;
+ seg->cacheFileOffset = addr - _dataRegion.address + _dataRegion.fileOffset;
+ seg->cacheSegSize = seg->base->sizeOfSections;
+ addr += seg->base->sizeOfSections;
+ }
+
+ // layout all __DATA_DIRTY segments
+ addr = align(addr, 12);
+ std::sort(dataDirtySegs.begin(), dataDirtySegs.end(), [&](const SegmentInfo *a, const SegmentInfo *b) {
+ const std::string& pathA = dataDirtySegPaths[a];
+ const std::string& pathB = dataDirtySegPaths[b];
+
+ const auto& orderA = _dataDirtySegsOrder.find(pathA);
+ const auto& orderB = _dataDirtySegsOrder.find(pathB);
+ bool foundA = (orderA != _dataDirtySegsOrder.end());
+ bool foundB = (orderB != _dataDirtySegsOrder.end());
+
+ // Order all __DATA_DIRTY segments specified in the order file first, in
+ // the order specified in the file, followed by any other __DATA_DIRTY
+ // segments in lexicographic order.
+ if ( foundA && foundB )
+ return orderA->second < orderB->second;
+ else if ( foundA )
+ return true;
+ else if ( foundB )
+ return false;
+ else
+ return pathA < pathB;
+ });
+ for (SegmentInfo* seg : dataDirtySegs) {
+ // Pack __DATA_DIRTY segments
+ uint64_t startAlignPad = align(addr, seg->base->p2align) - addr;
+ addr += startAlignPad;
+ seg->address = addr;
+ seg->cacheFileOffset = addr - _dataRegion.address + _dataRegion.fileOffset;
+ seg->cacheSegSize = seg->base->sizeOfSections;
+ addr += seg->base->sizeOfSections;
+ }
+
+ // align DATA region end
+ uint64_t endDataAddress = align(addr, sharedRegionRegionAlignment(_arch));
+ _dataRegion.size = endDataAddress - _dataRegion.address;
+
+ // start read-only region
+ addr = sharedRegionStartReadOnlyAddress(_arch, endDataAddress, endTextAddress);
+ _readOnlyRegion.address = addr;
+ _readOnlyRegion.fileOffset = _dataRegion.fileOffset + _dataRegion.size;
+ _readOnlyRegion.prot = VM_PROT_READ;
+
+ // reserve space for kernel ASLR slide info at start of r/o region
+ _slideInfoBufferSize = align((_dataRegion.size/4096) * 130, 12); // bitmap entry + toc entry
+ _slideInfoFileOffset = _readOnlyRegion.fileOffset;
+ addr += _slideInfoBufferSize;
+
+ // layout all read-only (but not LINKEDIT) segments
+ for (auto& dylib : _dylibs) {
+ for (auto& seg : _segmentMap[dylib]) {
+ if ( seg.base->protection != VM_PROT_READ )
+ continue;
+ if ( seg.base->name == "__LINKEDIT" )
+ continue;
+ // Keep segments 4K or more aligned
+ addr = align(addr, std::min(seg.base->p2align, (uint8_t)12));
+ seg.address = addr;
+ seg.cacheFileOffset = addr - _readOnlyRegion.address + _readOnlyRegion.fileOffset;;
+ seg.cacheSegSize = seg.base->size;
+ addr += seg.base->size;
+ //verboseLog("read-only offset=0x%08X, for path=%s\n", seg.cacheFileOffset, ex->proxy->installName.c_str());
+ }
+ }
+ // layout all LINKEDIT segments (after other read-only segments)
+ for (auto& dylib : _dylibs) {
+ for (auto& seg : _segmentMap[dylib]) {
+ if ( seg.base->protection != VM_PROT_READ )
+ continue;
+ if ( seg.base->name != "__LINKEDIT" )
+ continue;
+ // Keep LINKEDIT segments 4K aligned
+ addr = align(addr, 12);
+ seg.address = addr;
+ seg.cacheFileOffset = addr - _readOnlyRegion.address + _readOnlyRegion.fileOffset;;
+ seg.cacheSegSize = seg.base->diskSize;
+ addr += seg.base->size;
+ //verboseLog("linkedit offset=0x%08X, for path=%s\n", seg.cacheFileOffset, ex->proxy->installName.c_str());
+ }
+ }
+ // add room for branch pool linkedits
+ _branchPoolsLinkEditStartAddr = addr;
+ addr += (_branchPoolStarts.size() * brPoolLinkEditSize);
+
+ // align r/o region end
+ uint64_t endReadOnlyAddress = align(addr, sharedRegionRegionAlignment(_arch));
+ _readOnlyRegion.size = endReadOnlyAddress - _readOnlyRegion.address;
+ _fileSize = _readOnlyRegion.fileOffset + _readOnlyRegion.size;
+ // assume LINKEDIT optimzation reduces LINKEDITs to %40 of original size
+ _vmSize = _readOnlyRegion.address+(_readOnlyRegion.size * 2/5) - _textRegion.address;
+}
+
+uint64_t SharedCache::pathHash(const char* path)
+{
+ uint64_t sum = 0;
+ for (const char* s=path; *s != '\0'; ++s)
+ sum += sum*4 + *s;
+ return sum;
+}
+
+
+
+void SharedCache::findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName)
+{
+ uint64_t fileOffset = (uint8_t*)contentPtr - (uint8_t*)_buffer.get();
+ for (const auto& entry : _segmentMap ) {
+ const MachOProxy* dylib = entry.first;
+ for (const SegmentInfo& segInfo : entry.second) {
+ //fprintf(stderr, " cacheFileOffset=0x%08llX, end=0x%08llX\n", segInfo.cacheFileOffset, segInfo.cacheFileOffset+segInfo.base->size);
+ if ( (segInfo.cacheFileOffset <= fileOffset) && (fileOffset < segInfo.cacheFileOffset+segInfo.base->size) ) {
+ dylibName = dylib->installName;
+ segName = segInfo.base->name;
+ return;
+ }
+ }
+ }
+ dylibName = "???";
+ segName = "???";
+}
+
+
+template <typename P>
+bool SharedCache::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyldCacheSlideInfo2<typename P::E>* info)
+{
+ typedef typename P::uint_t pint_t;
+
+ const pint_t deltaMask = (pint_t)(info->delta_mask());
+ const pint_t valueMask = ~deltaMask;
+ const pint_t valueAdd = (pint_t)(info->value_add());
+ const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
+ const uint32_t maxDelta = (uint32_t)(deltaMask >> deltaShift);
+
+ pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0];
+ pint_t lastValue = (pint_t)P::getP(*lastLoc);
+ if ( (lastValue - valueAdd) & deltaMask ) {
+ std::string dylibName;
+ std::string segName;
+ findDylibAndSegment((void*)pageContent, dylibName, segName);
+ terminate("rebase pointer does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
+ lastLocationOffset, segName.c_str(), dylibName.c_str());
+ }
+ if ( offset <= (lastLocationOffset+maxDelta) ) {
+ // previous location in range, make link from it
+ // encode this location into last value
+ pint_t delta = offset - lastLocationOffset;
+ pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift);
+ //warning(" add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
+ // offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
+ P::setP(*lastLoc, newLastValue);
+ return true;
+ }
+ //warning(" too big delta = %d, lastOffset=0x%03X, offset=0x%03X", offset - lastLocationOffset, lastLocationOffset, offset);
+
+ // distance between rebase locations is too far
+ // see if we can make a chain from non-rebase locations
+ uint16_t nonRebaseLocationOffsets[1024];
+ unsigned nrIndex = 0;
+ for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) {
+ nonRebaseLocationOffsets[nrIndex] = 0;
+ for (int j=maxDelta; j > 0; j -= 4) {
+ pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]);
+ if ( value == 0 ) {
+ // Steal values of 0 to be used in the rebase chain
+ nonRebaseLocationOffsets[nrIndex] = i+j;
+ break;
+ }
+ }
+ if ( nonRebaseLocationOffsets[nrIndex] == 0 ) {
+ lastValue = (pint_t)P::getP(*lastLoc);
+ pint_t newValue = ((lastValue - valueAdd) & valueMask);
+ //warning(" no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX", lastLocationOffset, (long)value, (long)newValue);
+ P::setP(*lastLoc, newValue);
+ return false;
+ }
+ i = nonRebaseLocationOffsets[nrIndex];
+ ++nrIndex;
+ }
+
+ // we can make chain. go back and add each non-rebase location to chain
+ uint16_t prevOffset = lastLocationOffset;
+ pint_t* prevLoc = (pint_t*)&pageContent[prevOffset];
+ for (int n=0; n < nrIndex; ++n) {
+ uint16_t nOffset = nonRebaseLocationOffsets[n];
+ assert(nOffset != 0);
+ pint_t* nLoc = (pint_t*)&pageContent[nOffset];
+ uint32_t delta2 = nOffset - prevOffset;
+ pint_t value = (pint_t)P::getP(*prevLoc);
+ pint_t newValue;
+ if ( value == 0 )
+ newValue = (delta2 << deltaShift);
+ else
+ newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift);
+ //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
+ P::setP(*prevLoc, newValue);
+ prevOffset = nOffset;
+ prevLoc = nLoc;
+ }
+ uint32_t delta3 = offset - prevOffset;
+ pint_t value = (pint_t)P::getP(*prevLoc);
+ pint_t newValue;
+ if ( value == 0 )
+ newValue = (delta3 << deltaShift);
+ else
+ newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift);
+ //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
+ P::setP(*prevLoc, newValue);
+
+ return true;
+}
+
+
+template <typename P>
+void SharedCache::addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyldCacheSlideInfo2<typename P::E>* info,
+ std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
+{
+ typedef typename P::uint_t pint_t;
+
+ const pint_t deltaMask = (pint_t)(info->delta_mask());
+ const pint_t valueMask = ~deltaMask;
+ const uint32_t pageSize = info->page_size();
+ const pint_t valueAdd = (pint_t)(info->value_add());
+
+ uint16_t startValue = DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE;
+ uint16_t lastLocationOffset = 0xFFFF;
+ for(int i=0; i < pageSize/4; ++i) {
+ unsigned offset = i*4;
+ if ( bitmap[i] ) {
+ if ( startValue == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
+ // found first rebase location in page
+ startValue = i;
+ }
+ else if ( !makeRebaseChain<P>(pageContent, lastLocationOffset, offset, info) ) {
+ // can't record all rebasings in one chain
+ if ( (startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 ) {
+ // switch page_start to "extras" which is a list of chain starts
+ unsigned indexInExtras = (unsigned)pageExtras.size();
+ if ( indexInExtras > 0x3FFF )
+ terminate("rebase overflow in page extras");
+ pageExtras.push_back(startValue);
+ startValue = indexInExtras | DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA;
+ }
+ pageExtras.push_back(i);
+ }
+ lastLocationOffset = offset;
+ }
+ }
+ if ( lastLocationOffset != 0xFFFF ) {
+ // mark end of chain
+ pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset];
+ pint_t lastValue = (pint_t)P::getP(*lastLoc);
+ pint_t newValue = ((lastValue - valueAdd) & valueMask);
+ P::setP(*lastLoc, newValue);
+ }
+ if ( startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
+ // add end bit to extras
+ pageExtras.back() |= DYLD_CACHE_SLIDE_PAGE_ATTR_END;
+ }
+ pageStarts.push_back(startValue);
+}
+
+template <typename P>
+void SharedCache::writeSlideInfoV2(uint64_t deltaMask, uint64_t valueAdd)
+{
+ // i386 cache does not support sliding because stubs use absolute addressing (text relocs)
+ if (_arch.arch == CPU_TYPE_I386 ) {
+ dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)_buffer.get();
+ header->set_slideInfoSize(0);
+ return;
+ }
+
+ typedef typename P::E E;
+ const uint32_t pageSize = 4096;
+
+ // build one 1024/4096 bool bitmap per page (4KB/16KB) of DATA
+ uint8_t* const dataStart = (uint8_t*)_buffer.get() + _dataRegion.fileOffset;
+ uint8_t* const dataEnd = dataStart + _dataRegion.size;
+ unsigned pageCount = (unsigned)(_dataRegion.size+pageSize-1)/pageSize;
+ const long bitmapSize = pageCount*(pageSize/4)*sizeof(bool);
+ bool* bitmap = (bool*)calloc(bitmapSize, 1);
+ for (void* p : _pointersForASLR) {
+ if ( (p < dataStart) || ( p > dataEnd) )
+ terminate("DATA pointer for sliding, out of range\n");
+ long byteOffset = (long)((uint8_t*)p - dataStart);
+ if ( (byteOffset % 4) != 0 )
+ terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", byteOffset);
+ long boolIndex = byteOffset / 4;
+ // work around <rdar://24941083> by ignoring pointers to be slid that are NULL on disk
+ if ( *(typename P::uint_t*)p == 0 ) {
+ std::string dylibName;
+ std::string segName;
+ findDylibAndSegment(p, dylibName, segName);
+ warning("NULL pointer asked to be slid in %s of %s", segName.c_str(), dylibName.c_str());
+ continue;
+ }
+ bitmap[boolIndex] = true;
+ }
+
+ // fill in fixed info
+ dyldCacheSlideInfo2<E>* info = (dyldCacheSlideInfo2<E>*)((uint8_t*)_buffer.get() + _slideInfoFileOffset);
+ info->set_version(2);
+ info->set_page_size(pageSize);
+ info->set_delta_mask(deltaMask);
+ info->set_value_add(valueAdd);
+
+ // set page starts and extras for each page
+ std::vector<uint16_t> pageStarts;
+ std::vector<uint16_t> pageExtras;
+ pageStarts.reserve(pageCount);
+ uint8_t* pageContent = dataStart;;
+ const bool* bitmapForPage = bitmap;
+ for (unsigned i=0; i < pageCount; ++i) {
+ //warning("page[%d]", i);
+ addPageStarts<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
+ pageContent += pageSize;
+ bitmapForPage += (sizeof(bool)*(pageSize/4));
+ }
+ free((void*)bitmap);
+
+ // fill in computed info
+ info->set_page_starts_offset(sizeof(dyldCacheSlideInfo2<E>));
+ info->set_page_starts_count((unsigned)pageStarts.size());
+ info->set_page_extras_offset((unsigned)(sizeof(dyldCacheSlideInfo2<E>)+pageStarts.size()*sizeof(uint16_t)));
+ info->set_page_extras_count((unsigned)pageExtras.size());
+ for (unsigned i=0; i < pageStarts.size(); ++i)
+ info->set_page_starts(i, pageStarts[i]);
+ for (unsigned i=0; i < pageExtras.size(); ++i)
+ info->set_page_extras(i, pageExtras[i]);
+ //warning("pageCount=%u, page_starts_count=%lu, page_extras_count=%lu", pageCount, pageStarts.size(), pageExtras.size());
+ _slideInfoBufferSize = align(info->page_extras_offset() + pageExtras.size()*sizeof(uint16_t), 12);
+
+#if NEW_CACHE_FILE_FORMAT
+
+#else
+ unsigned long slideInfoPageSize = align(_slideInfoBufferSize, sharedRegionRegionAlignment(_arch));
+ dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
+ header->set_slideInfoSize(slideInfoPageSize);
+#endif
+}
+
+void SharedCache::writeSlideInfoV2(void)
+{
+ switch (_arch.arch) {
+ case CPU_TYPE_ARM:
+ // linked list based slide info needs high 3 bits of pointer, won't work with > 512MB of pointable content
+ if ( (_textRegion.size + _dataRegion.size) > 512*1024*1024 ) {
+ warning("cache TEXT+DATA > 512MB, using larger slide info format");
+ writeSlideInfo<LittleEndian>();
+ }
+ else {
+ writeSlideInfoV2<Pointer32<LittleEndian>>(0xE0000000, ARM_SHARED_REGION_START);
+ }
+ break;
+ case CPU_TYPE_I386:
+ writeSlideInfoV2<Pointer32<LittleEndian>>(0xE0000000, 0x90000000);
+ break;
+ case CPU_TYPE_X86_64:
+ writeSlideInfoV2<Pointer64<LittleEndian>>(0xFFFF000000000000, 0);
+ break;
+ case CPU_TYPE_ARM64:
+ writeSlideInfoV2<Pointer64<LittleEndian>>(0x00FFFF0000000000, 0);
+ break;
+ default:
+ warning("unsupported arch 0x%08X", _arch.arch);
+ return;
+ }
+}
+
+
+
+template <typename E>
+void SharedCache::writeSlideInfo(void)
+{
+ // i386 cache does not support sliding because stubs use absolute addressing (text relocs)
+ if (_arch.arch == CPU_TYPE_I386 ) {
+ dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
+ header->set_slideInfoSize(0);
+ return;
+ }
+
+ // build one 128-byte bitmap per page (4096) of DATA
+ uint8_t* const dataStart = (uint8_t*)_buffer.get() + _dataRegion.fileOffset;
+ uint8_t* const dataEnd = dataStart + _dataRegion.size;
+ const long bitmapSize = (dataEnd - dataStart)/(4*8);
+ uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1);
+ for (void* p : _pointersForASLR) {
+ if ( (p < dataStart) || ( p > dataEnd) )
+ terminate("DATA pointer for sliding, out of range\n");
+ long offset = (long)((uint8_t*)p - dataStart);
+ if ( (offset % 4) != 0 )
+ terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset);
+ long byteIndex = offset / (4*8);
+ long bitInByte = (offset % 32) >> 2;
+ bitmap[byteIndex] |= (1 << bitInByte);
+ }
+
+ // allocate worst case size block of all slide info
+ const unsigned entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes.
+ const unsigned toc_count = (unsigned)bitmapSize/entry_size;
+ dyldCacheSlideInfo<E>* slideInfo = (dyldCacheSlideInfo<E>*)((uint8_t*)_buffer.get() + _slideInfoFileOffset);
+ slideInfo->set_version(1);
+ slideInfo->set_toc_offset(sizeof(dyldCacheSlideInfo<E>));
+ slideInfo->set_toc_count(toc_count);
+ slideInfo->set_entries_offset((slideInfo->toc_offset()+2*toc_count+127)&(-128));
+ slideInfo->set_entries_count(0);
+ slideInfo->set_entries_size(entry_size);
+ // append each unique entry
+ const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap;
+ dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset());
+ int entry_count = 0;
+ for (int i=0; i < toc_count; ++i) {
+ const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i];
+ // see if it is same as one already added
+ bool found = false;
+ for (int j=0; j < entry_count; ++j) {
+ if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) {
+ slideInfo->set_toc(i, j);
+ found = true;
+ break;
+ }
+ }
+ if ( !found ) {
+ // append to end
+ memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size);
+ slideInfo->set_toc(i, entry_count++);
+ }
+ }
+ slideInfo->set_entries_count(entry_count);
+ ::free((void*)bitmap);
+
+#if NEW_CACHE_FILE_FORMAT
+
+#else
+ unsigned long slideInfoPageSize = align(slideInfo->entries_offset() + entry_count*entry_size, sharedRegionRegionAlignment(_arch));
+ dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
+ header->set_slideInfoSize(slideInfoPageSize);
+#endif
+}
+
+template <typename P>
+void SharedCache::writeCacheHeader(void)
+{
+#if NEW_CACHE_FILE_FORMAT
+ macho_header<P>* mh = (macho_header<P>*)cacheBuffer;
+ mh->set_magic((sizeof(typename P::uint_t) == 8) ? MH_MAGIC_64 : MH_MAGIC);
+ mh->set_cputype(arch.arch);
+ mh->set_cpusubtype(arch.subtype);
+ mh->set_filetype(MH_DYLIB);
+ mh->set_ncmds(0);
+ mh->set_sizeofcmds(0);
+ mh->set_flags(0);
+
+ uint8_t* cmd = (uint8_t*)cacheBuffer + sizeof(macho_header<P>);
+
+ // write LC_SEGMENT for each region
+ macho_segment_command<P>* rxSegCmd = (macho_segment_command<P>*)cmd;
+ rxSegCmd->set_cmd(macho_segment_command<P>::CMD);
+ rxSegCmd->set_cmdsize(sizeof(macho_segment_command<P>));
+ rxSegCmd->set_segname("R.X");
+ rxSegCmd->set_vmaddr(_textRegion.address);
+ rxSegCmd->set_vmsize(_textRegion.size);
+ rxSegCmd->set_fileoff(_textRegion.fileOffset);
+ rxSegCmd->set_filesize(_textRegion.size);
+ rxSegCmd->set_maxprot(VM_PROT_READ | VM_PROT_EXECUTE);
+ rxSegCmd->set_initprot(VM_PROT_READ | VM_PROT_EXECUTE);
+ rxSegCmd->set_nsects(0);
+ rxSegCmd->set_flags(0);
+ mh->set_ncmds(mh->ncmds()+1);
+ mh->set_sizeofcmds(mh->sizeofcmds()+rxSegCmd->cmdsize());
+ cmd += rxSegCmd->cmdsize();
+
+ macho_segment_command<P>* rwSegCmd = (macho_segment_command<P>*)cmd;
+ rwSegCmd->set_cmd(macho_segment_command<P>::CMD);
+ rwSegCmd->set_cmdsize(sizeof(macho_segment_command<P>));
+ rwSegCmd->set_segname("RW.");
+ rwSegCmd->set_vmaddr(_dataRegion.address);
+ rwSegCmd->set_vmsize(_dataRegion.size);
+ rwSegCmd->set_fileoff(_dataRegion.fileOffset);
+ rwSegCmd->set_filesize(_dataRegion.size);
+ rwSegCmd->set_maxprot(VM_PROT_READ | VM_PROT_WRITE);
+ rwSegCmd->set_initprot(VM_PROT_READ | VM_PROT_WRITE);
+ rwSegCmd->set_nsects(0);
+ rwSegCmd->set_flags(0);
+ mh->set_ncmds(mh->ncmds()+1);
+ mh->set_sizeofcmds(mh->sizeofcmds()+rwSegCmd->cmdsize());
+ cmd += rwSegCmd->cmdsize();
+
+ macho_segment_command<P>* roSegCmd = (macho_segment_command<P>*)cmd;
+ roSegCmd->set_cmd(macho_segment_command<P>::CMD);
+ roSegCmd->set_cmdsize(sizeof(macho_segment_command<P>));
+ roSegCmd->set_segname("R..");
+ roSegCmd->set_vmaddr(_readOnlyRegion.address);
+ roSegCmd->set_vmsize(_readOnlyRegion.size);
+ roSegCmd->set_fileoff(_readOnlyRegion.fileOffset);
+ roSegCmd->set_filesize(_readOnlyRegion.size);
+ roSegCmd->set_maxprot(VM_PROT_READ);
+ roSegCmd->set_initprot(VM_PROT_READ);
+ roSegCmd->set_nsects(0);
+ roSegCmd->set_flags(0);
+ mh->set_ncmds(mh->ncmds()+1);
+ mh->set_sizeofcmds(mh->sizeofcmds()+roSegCmd->cmdsize());
+ cmd += roSegCmd->cmdsize();
+
+ // Add LC_ID_DYLIB
+ macho_dylib_command<P>* dylibIdCmd = (macho_dylib_command<P>*)cmd;
+ const char* installName = "/System/Library/Frameworks/OS.framework/OS"; // FIXME
+ uint32_t sz = (uint32_t)align(sizeof(macho_dylib_command<P>) + strlen(installName) + 1, 3);
+ dylibIdCmd->set_cmd(LC_ID_DYLIB);
+ dylibIdCmd->set_cmdsize(sz);
+ dylibIdCmd->set_name_offset();
+ dylibIdCmd->set_timestamp(1);
+ dylibIdCmd->set_current_version(0x10000);
+ dylibIdCmd->set_compatibility_version(0x10000);
+ strcpy((char*)&cmd[sizeof(macho_dylib_command<P>)], installName);
+ mh->set_ncmds(mh->ncmds()+1);
+ mh->set_sizeofcmds(mh->sizeofcmds()+sz);
+ cmd += dylibIdCmd->cmdsize();
+
+ // Add LC_UUID
+ macho_uuid_command<P>* uuidCmd = (macho_uuid_command<P>*)cmd;
+ uint8_t zeros[16];
+ bzero(zeros, 16);
+ uuidCmd->set_cmd(LC_UUID);
+ uuidCmd->set_cmdsize(sizeof(macho_uuid_command<P>));
+ uuidCmd->set_uuid(zeros);
+ cmd += uuidCmd->cmdsize();
+
+ // Build dylib trie
+ std::vector<mach_o::trie::Entry> dylibTrieEntires;
+ int pathLengths = 0;
+ for (Extra* ex : _sortedDylibs) {
+ mach_o::trie::Entry entry;
+ entry.name = ex->proxy->installName.c_str();
+ entry.address = ex->segments[0].address;
+ entry.flags = 0;
+ entry.other = 0;
+ entry.importName = NULL;
+ dylibTrieEntires.push_back(entry);
+ pathLengths += (strlen(entry.name) + 1);
+ for (const std::string& alias : ex->proxy->installNameAliases) {
+ mach_o::trie::Entry aliasEntry;
+ aliasEntry.name = alias.c_str();
+ aliasEntry.address = ex->segments[0].address;
+ aliasEntry.flags = 0;
+ aliasEntry.other = 0;
+ aliasEntry.importName = NULL;
+ dylibTrieEntires.push_back(aliasEntry);
+ pathLengths += (strlen(aliasEntry.name) + 1);
+ }
+ }
+ std::vector<uint8_t> dylibTrieBytes;
+ dylibTrieBytes.reserve(4096);
+ mach_o::trie::makeTrie(dylibTrieEntires, dylibTrieBytes);
+ fprintf(stderr, "dylib trie size = %lu bytes, for %lu entries, pathLength=%d\n", dylibTrieBytes.size(), dylibTrieEntires.size(), pathLengths);
+
+
+
+ // Build SPI trie (optimized cache only)
+
+
+
+ // add LC_CODE_SIGNATURE
+ macho_linkedit_data_command<P>* codeSigCmd = (macho_linkedit_data_command<P>*)cmd;
+ codeSigCmd->set_cmd(LC_CODE_SIGNATURE);
+ codeSigCmd->set_cmdsize(sizeof(macho_linkedit_data_command<P>));
+ codeSigCmd->set_dataoff((uint32_t)(_readOnlyRegion.fileOffset + _readOnlyRegion.size));
+ codeSigCmd->set_datasize(0); // FIXME
+ mh->set_ncmds(mh->ncmds()+1);
+ mh->set_sizeofcmds(mh->sizeofcmds()+codeSigCmd->cmdsize());
+ cmd += codeSigCmd->cmdsize();
+
+#else
+ typedef typename P::E E;
+ // fill in header
+ uint8_t* buffer = (uint8_t*)_buffer.get();
+ dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();;
+ // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
+ std::string magic = "dyld_v1";
+ magic.append(15 - magic.length() - archName().length(), ' ');
+ magic.append(archName());
+ assert(magic.length() == 15);
+ header->set_magic(magic.c_str());
+ header->set_mappingOffset(sizeof(dyldCacheHeader<E>));
+ header->set_mappingCount(3);
+ header->set_imagesOffset((uint32_t)(header->mappingOffset() + 3*sizeof(dyldCacheFileMapping<E>) + sizeof(uint64_t)*_branchPoolStarts.size()));
+ header->set_imagesCount((uint32_t)_dylibs.size() + _aliasCount);
+ header->set_dyldBaseAddress(0);
+ header->set_codeSignatureOffset(_fileSize);
+ header->set_codeSignatureSize(0);
+ header->set_slideInfoOffset(_slideInfoFileOffset);
+ header->set_slideInfoSize(_slideInfoBufferSize);
+ header->set_localSymbolsOffset(0);
+ header->set_localSymbolsSize(0);
+ header->set_cacheType(kDyldSharedCacheTypeDevelopment);
+ header->set_accelerateInfoAddr(0);
+ header->set_accelerateInfoSize(0);
+ static const uint8_t zero_uuid[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
+ header->set_uuid(zero_uuid); // overwritten later by recomputeCacheUUID()
+ header->set_branchPoolsOffset(header->mappingOffset() + 3*sizeof(dyldCacheFileMapping<E>));
+ header->set_branchPoolsCount((uint32_t)_branchPoolStarts.size());
+ header->set_imagesTextOffset(0);
+ header->set_imagesTextCount(_dylibs.size());
+
+ // fill in mappings
+ dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&buffer[header->mappingOffset()];
+ mappings[0].set_address(_textRegion.address);
+ mappings[0].set_size(_textRegion.size);
+ mappings[0].set_file_offset(_textRegion.fileOffset);
+ mappings[0].set_max_prot(_textRegion.prot);
+ mappings[0].set_init_prot(_textRegion.prot);
+
+ mappings[1].set_address(_dataRegion.address);
+ mappings[1].set_size(_dataRegion.size);
+ mappings[1].set_file_offset(_dataRegion.fileOffset);
+ mappings[1].set_max_prot(_dataRegion.prot);
+ mappings[1].set_init_prot(_dataRegion.prot);
+
+ mappings[2].set_address(_readOnlyRegion.address);
+ mappings[2].set_size(_readOnlyRegion.size);
+ mappings[2].set_file_offset(_readOnlyRegion.fileOffset);
+ mappings[2].set_max_prot(_readOnlyRegion.prot);
+ mappings[2].set_init_prot(_readOnlyRegion.prot);
+
+ // fill in branch pool addresses
+ uint64_t* p = (uint64_t*)&buffer[header->branchPoolsOffset()];
+ for (uint64_t pool : _branchPoolStarts) {
+ E::set64(*p, pool);
+ ++p;
+ }
+
+ // fill in image table
+ dyldCacheImageInfo<E>* images = (dyldCacheImageInfo<E>*)&buffer[header->imagesOffset()];
+ for (auto& dylib : _dylibs) {
+ auto textSeg = _segmentMap[dylib][0];
+ images->set_address(textSeg.address);
+ if (_manifest.platform == "osx") {
+ images->set_modTime(dylib->lastModTime);
+ images->set_inode(dylib->inode);
+ } else {
+ images->set_modTime(0);
+ images->set_inode(pathHash(dylib->installName.c_str()));
+ }
+ images->set_pathFileOffset((uint32_t)textSeg.cacheFileOffset + dylib->installNameOffsetInTEXT);
+ ++images;
+ }
+ // append aliases image records and strings
+ uint32_t offset = header->imagesOffset() + header->imagesCount()*sizeof(dyld_cache_image_info);
+ for (auto &dylib : _dylibs) {
+ if (!dylib->installNameAliases.empty()) {
+ for (const std::string& alias : dylib->installNameAliases) {
+ images->set_address(_segmentMap[dylib][0].address);
+ if (_manifest.platform == "osx") {
+ images->set_modTime(dylib->lastModTime);
+ images->set_inode(dylib->inode);
+ } else {
+ images->set_modTime(0);
+ images->set_inode(pathHash(alias.c_str()));
+ }
+ images->set_pathFileOffset(offset);
+ ::strcpy((char*)&buffer[offset], alias.c_str());
+ offset += alias.size() + 1;
+ ++images;
+ }
+ }
+ }
+
+ // calculate start of text image array and trailing string pool
+ offset = (offset + 15) & (-16);
+ header->set_imagesTextOffset(offset);
+ dyldCacheImageTextInfo<E>* textImages = (dyldCacheImageTextInfo<E>*)&buffer[header->imagesTextOffset()];
+ uint32_t stringOffset = offset + (uint32_t)(sizeof(dyldCacheImageTextInfo<E>) * _dylibs.size());
+
+ // write text image array and image names pool at same time
+ for (auto& dylib : _dylibs) {
+ textImages->set_uuid(dylib->uuid);
+ textImages->set_loadAddress(_segmentMap[dylib][0].address);
+ textImages->set_textSegmentSize((uint32_t)dylib->segments[0].size);
+ textImages->set_pathOffset(stringOffset);
+ ::strcpy((char*)&buffer[stringOffset], dylib->installName.c_str());
+ stringOffset += dylib->installName.size()+1;
+ ++textImages;
+ }
+
+ assert(stringOffset < 0x28000);
+#endif
+}
+
+void SharedCache::rebase(MachOProxy* dylib)
+{
+ std::vector<uint64_t> segNewStartAddresses;
+ std::vector<uint64_t> segCacheFileOffsets;
+ std::vector<uint64_t> segCacheFileSizes;
+ for (auto& seg : _segmentMap[dylib]) {
+ segNewStartAddresses.push_back(seg.address);
+ segCacheFileOffsets.push_back(seg.cacheFileOffset);
+ segCacheFileSizes.push_back(seg.cacheSegSize);
+ }
+ adjustImageForNewSegmentLocations(segNewStartAddresses, segCacheFileOffsets, segCacheFileSizes, _pointersForASLR);
+}
+
+
+void SharedCache::rebaseAll(void)
+{
+ for (auto& dylib : _dylibs)
+ rebase(dylib);
+}
+
+void SharedCache::bindAll(void)
+{
+ std::unordered_map<std::string, void*> dylibPathToMachHeader;
+ for (auto& dylib : _dylibs) {
+ void* mh = (uint8_t*)_buffer.get() + _segmentMap[dylib][0].cacheFileOffset;
+ dylibPathToMachHeader[dylib->installName] = mh;
+ for (const std::string& path : dylib->installNameAliases) {
+ if (path != dylib->installName) {
+ dylibPathToMachHeader[path] = mh;
+ }
+ }
+ }
+
+ bindAllImagesInCache(dylibPathToMachHeader, _pointersForASLR);
+}
+
+void SharedCache::writeCacheSegments(void)
+{
+ uint8_t* cacheBytes = (uint8_t*)_buffer.get();
+ for (auto& dylib : _dylibs) {
+ struct stat stat_buf;
+ const uint8_t* srcDylib;
+ bool rootless;
+
+ std::tie(srcDylib, stat_buf, rootless) = fileCache.cacheLoad(dylib->path);
+ for (auto& seg : _segmentMap[dylib]) {
+ uint32_t segFileOffset = dylib->fatFileOffset + seg.base->fileOffset;
+ uint64_t copySize = std::min(seg.cacheSegSize, (uint64_t)seg.base->diskSize);
+ verboseLog("copy segment %12s (0x%08llX bytes) to %p (logical addr 0x%llX) for %s", seg.base->name.c_str(), copySize, &cacheBytes[seg.cacheFileOffset], seg.address, dylib->installName.c_str());
+ ::memcpy(&cacheBytes[seg.cacheFileOffset], &srcDylib[segFileOffset], copySize);
+ }
+ }
+}
+
+
+
+void SharedCache::appendCodeSignature(const std::string& suffix)
+{
+ // select which codesigning hash
+ uint8_t dscHashType = CS_HASHTYPE_SHA1;
+ uint8_t dscHashSize = CS_HASH_SIZE_SHA1;
+ uint32_t dscDigestFormat = kCCDigestSHA1;
+ if ( _manifest.platform == "osx" ) {
+ dscHashType = CS_HASHTYPE_SHA256;
+ dscHashSize = CS_HASH_SIZE_SHA256;
+ dscDigestFormat = kCCDigestSHA256;
+ }
+
+ std::string cacheIdentifier = "com.apple.dyld.cache." + archName() + "." + suffix;
+ // get pointers into shared cache buffer
+ size_t inBbufferSize = _fileSize;
+ const uint8_t* inBuffer = (uint8_t*)_buffer.get();
+ uint8_t* csBuffer = (uint8_t*)_buffer.get()+inBbufferSize;
+
+ // layout code signature contents
+ size_t idSize = cacheIdentifier.size()+1; // +1 for terminating 0
+ uint32_t slotCount = (uint32_t)((inBbufferSize + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE);
+ uint32_t xSlotCount = CSSLOT_REQUIREMENTS;
+ size_t scatOffset = sizeof(CS_CodeDirectory);
+ size_t scatSize = 4*sizeof(CS_Scatter); // only 3 used??
+ size_t idOffset = scatOffset+scatSize;
+ size_t hashOffset = idOffset+idSize + dscHashSize*xSlotCount;
+ size_t cdSize = hashOffset + (slotCount * dscHashSize);
+ size_t reqsSize = 12;
+ size_t cmsSize = sizeof(CS_Blob);
+ size_t cdOffset = sizeof(CS_SuperBlob) + 3*sizeof(CS_BlobIndex);
+ size_t reqsOffset = cdOffset + cdSize;
+ size_t cmsOffset = reqsOffset + reqsSize;
+ size_t sbSize = cmsOffset + cmsSize;
+ size_t sigSize = align(sbSize, 14); // keep whole cache 16KB aligned
+
+ // create overall code signature which is a superblob
+ CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(csBuffer);
+ sb->magic = htonl(CSMAGIC_EMBEDDED_SIGNATURE);
+ sb->length = htonl(sbSize);
+ sb->count = htonl(3);
+ sb->index[0].type = htonl(CSSLOT_CODEDIRECTORY);
+ sb->index[0].offset = htonl(cdOffset);
+ sb->index[1].type = htonl(CSSLOT_REQUIREMENTS);
+ sb->index[1].offset = htonl(reqsOffset);
+ sb->index[2].type = htonl(CSSLOT_CMS_SIGNATURE);
+ sb->index[2].offset = htonl(cmsOffset);
+
+ // initialize fixed fields of Code Directory
+ CS_CodeDirectory* cd = (CS_CodeDirectory*)(((char*)sb)+cdOffset);
+ cd->magic = htonl(CSMAGIC_CODEDIRECTORY);
+ cd->length = htonl(cdSize);
+ cd->version = htonl(0x20100);
+ cd->flags = htonl(kSecCodeSignatureAdhoc);
+ cd->hashOffset = htonl(hashOffset);
+ cd->identOffset = htonl(idOffset);
+ cd->nSpecialSlots = htonl(xSlotCount);
+ cd->nCodeSlots = htonl(slotCount);
+ cd->codeLimit = htonl(inBbufferSize);
+ cd->hashSize = dscHashSize;
+ cd->hashType = dscHashType;
+ cd->platform = 0; // not platform binary
+ cd->pageSize = __builtin_ctz(CS_PAGE_SIZE); // log2(CS_PAGE_SIZE);
+ cd->spare2 = 0; // unused (must be zero)
+ cd->scatterOffset = htonl(scatOffset);
+
+ // initialize dynamic fields of Code Directory
+ strcpy((char*)cd + idOffset, cacheIdentifier.c_str());
+
+ // add scatter info
+ CS_Scatter* scatter = reinterpret_cast<CS_Scatter*>((char*)cd+scatOffset);
+ scatter[0].count = htonl(_textRegion.size/CS_PAGE_SIZE);
+ scatter[0].base = htonl(_textRegion.fileOffset/CS_PAGE_SIZE);
+ scatter[0].targetOffset = htonll(_textRegion.address);
+ scatter[0].spare = 0;
+ scatter[1].count = htonl(_dataRegion.size/CS_PAGE_SIZE);
+ scatter[1].base = htonl(_dataRegion.fileOffset/CS_PAGE_SIZE);
+ scatter[1].targetOffset = htonll(_dataRegion.address);
+ scatter[1].spare = 0;
+ scatter[2].count = htonl(_readOnlyRegion.size/CS_PAGE_SIZE);
+ scatter[2].base = htonl(_readOnlyRegion.fileOffset/CS_PAGE_SIZE);
+ scatter[2].targetOffset = htonll(_readOnlyRegion.address);
+ scatter[2].spare = 0;
+
+ // fill in empty requirements
+ CS_RequirementsBlob* reqs = (CS_RequirementsBlob*)(((char*)sb)+reqsOffset);
+ reqs->magic = htonl(CSMAGIC_REQUIREMENTS);
+ reqs->length = htonl(sizeof(CS_RequirementsBlob));
+ reqs->data = 0;
+
+ // fill in empty CMS blob for ad-hoc signing
+ CS_Blob* cms = (CS_Blob*)(((char*)sb)+cmsOffset);
+ cms->magic = htonl(CSMAGIC_BLOBWRAPPER);
+ cms->length = htonl(sizeof(CS_Blob));
+
+ // add special slot hashes
+ uint8_t* hashSlot = (uint8_t*)cd + hashOffset;
+ uint8_t* reqsHashSlot = &hashSlot[-CSSLOT_REQUIREMENTS*dscHashSize];
+ CCDigest(dscDigestFormat, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHashSlot);
+
+ // alter header of cache to record size and location of code signature
+ // do this *before* hashing each page
+ dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)inBuffer;
+ header->set_codeSignatureOffset(inBbufferSize);
+ header->set_codeSignatureSize(sigSize);
+
+ // compute hashes
+ const uint8_t* code = inBuffer;
+ for (uint32_t i=0; i < slotCount; ++i) {
+ CCDigest(dscDigestFormat, code, CS_PAGE_SIZE, hashSlot);
+ hashSlot += dscHashSize;
+ code += CS_PAGE_SIZE;
+ }
+
+ // hash of entire code directory (cdHash) uses same has hash as each page
+ uint8_t fullCdHash[dscHashSize];
+ CCDigest(dscDigestFormat, (const uint8_t*)cd, cdSize, fullCdHash);
+ // Note: cdHash is defined as first 20 bytes of hash
+ memcpy(_cdHash, fullCdHash, 20);
+
+ // increase file size to include newly append code signature
+ _fileSize += sigSize;
+}
+
+std::string SharedCache::cdHashString()
+{
+ char buff[48];
+ for (int i = 0; i < sizeof(_cdHash); ++i)
+ sprintf(&buff[2*i], "%2.2x", _cdHash[i]);
+ return buff;
+}
+
+
+#pragma mark -
+#pragma mark Template dispatchers
+
+#define TEMPLATE_DISPATCHER_BODY(method,...) \
+ switch( _arch.arch ) { \
+ case CPU_TYPE_ARM: \
+ case CPU_TYPE_I386: \
+ method<Pointer32<LittleEndian>>(__VA_ARGS__); \
+ break; \
+ case CPU_TYPE_X86_64: \
+ case CPU_TYPE_ARM64: \
+ method<Pointer64<LittleEndian>>(__VA_ARGS__); \
+ break; \
+ default: \
+ terminate("unsupported arch 0x%08X", _arch.arch); \
+ }
+
+void SharedCache::writeCacheHeader() {
+ TEMPLATE_DISPATCHER_BODY(writeCacheHeader)
+};
+
+void SharedCache::buildForDevelopment(const std::string& cachePath) {
+ TEMPLATE_DISPATCHER_BODY(buildForDevelopment, cachePath)
+};
+
+void SharedCache::buildForProduction(const std::string& cachePath) {
+ TEMPLATE_DISPATCHER_BODY(buildForProduction, cachePath)
+};
+
+void SharedCache::setLinkeditsMappingEndFileOffset(uint64_t newFileSize) {
+ TEMPLATE_DISPATCHER_BODY(setLinkeditsMappingEndFileOffset, newFileSize)
+}
+
+void SharedCache::setUnmappedLocalsRange(uint64_t localSymbolsOffset, uint32_t unmappedSize) {
+ TEMPLATE_DISPATCHER_BODY(setUnmappedLocalsRange, localSymbolsOffset, unmappedSize)
+}
+
+void SharedCache::setAcceleratorInfoRange(uint64_t accelInfoAddr, uint32_t accelInfoSize) {
+ TEMPLATE_DISPATCHER_BODY(setAcceleratorInfoRange, accelInfoAddr, accelInfoSize)
+}
+
+void SharedCache::recomputeCacheUUID(void) {
+ TEMPLATE_DISPATCHER_BODY(recomputeCacheUUID)
+}
+
+void SharedCache::forEachImage(DylibHandler handler) {
+ TEMPLATE_DISPATCHER_BODY(forEachImage, handler)
+}
+
+void SharedCache::forEachRegion(RegionHandler handler) {
+ TEMPLATE_DISPATCHER_BODY(forEachRegion, handler)
+}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008-2010 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@
+ */
+
+/*
+ //This is the exposed iterface for the Trie template
+ //TODO: add erase methods
+ //TODO: re-enable iterators
+
+ template <typename V>
+ struct Trie {
+ struct Entry
+ {
+ std::string name;
+ V info;
+
+ Entry(void) {}
+ Entry(const std::string& N, V I) : name(N), info(I) {}
+ };
+
+ struct const_iterator : std::iterator<std::bidirectional_iterator_tag, const Entry>;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ Trie(void);
+ Trie(const uint8_t* start, const uint8_t* end);
+ Trie(const std::vector<Entry>& entries);
+
+ void emit(std::vector<uint8_t>& output);
+ */
+
+
+#ifndef __TRIE__
+#define __TRIE__
+#define TRIE_DEBUG (0)
+
+#include <algorithm>
+#include <vector>
+#include <memory>
+#include <string>
+#include <map>
+#include <iterator>
+
+#include "MachOFileAbstraction.hpp"
+
+#if __cplusplus <= 201103L
+namespace std {
+ template<typename T, typename... Args>
+ std::unique_ptr<T> make_unique(Args&&... args)
+ {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
+}
+#endif
+
+namespace TrieUtils {
+
+static void append_uleb128(uint64_t value, std::vector<uint8_t>& out) {
+ uint8_t byte;
+ do {
+ byte = value & 0x7F;
+ value &= ~0x7F;
+ if ( value != 0 )
+ byte |= 0x80;
+ out.push_back(byte);
+ value = value >> 7;
+ } while( byte >= 0x80 );
+}
+
+static void append_string(std::string str, std::vector<uint8_t>& out) {
+ for(char& c : str)
+ out.push_back(c);
+ out.push_back('\0');
+}
+
+static unsigned int uleb128_size(uint64_t value) {
+ uint32_t result = 0;
+ do {
+ value = value >> 7;
+ ++result;
+ } while ( value != 0 );
+ return result;
+}
+
+static
+inline bool parse_uleb128(const uint8_t*& p, const uint8_t* end, uint64_t& result) {
+ result = 0;
+ int bit = 0;
+ do {
+ if (p == end)
+ return false; // malformed uleb128 extends beyond input
+ uint64_t slice = *p & 0x7f;
+
+ if (bit >= 64 || slice << bit >> bit != slice)
+ return false; // malformed
+ else {
+ result |= (slice << bit);
+ bit += 7;
+ }
+ }
+ while (*p++ & 0x80);
+ return true;
+}
+};
+
+template <typename V>
+struct Trie {
+ uint32_t count;
+ uint32_t nodeCount;
+
+ struct Entry
+ {
+ std::string name;
+ V info;
+
+ Entry(void) {}
+ Entry(const std::string& N, V I) : name(N), info(I) {}
+ };
+
+ Trie(const std::vector<Entry>& entries) : count(0), nodeCount(1) {
+ // make nodes for all exported symbols
+ for (auto& entry : entries) {
+ addEntry(entry);
+ }
+ }
+
+ void emit(std::vector<uint8_t>& output) {
+ // create vector of nodes
+ std::vector<Node*> orderedNodes;
+ orderedNodes.reserve(nodeCount);
+ orderTrie(&root, orderedNodes);
+
+ // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized
+ bool more;
+ do {
+ uint32_t offset = 0;
+ more = false;
+ for (auto& node : orderedNodes) {
+ if (node->updateOffset(offset)) {
+ more = true;
+ }
+ }
+ } while ( more );
+
+ // create trie stream
+ for (auto& node : orderedNodes) {
+ node->appendToStream(output);
+ }
+ }
+
+ static
+ inline bool parseTrie(const uint8_t* start, const uint8_t* end, std::vector<Entry>& output)
+ {
+ // empty trie has no entries
+ if ( start == end )
+ return false;
+ char cummulativeString[32768];
+ std::vector<EntryWithOffset> entries;
+ if ( !processExportNode(start, start, end, cummulativeString, 0, entries) )
+ return false;
+ // to preserve tie layout order, sort by node offset
+ std::sort(entries.begin(), entries.end());
+ // copy to output
+ output.reserve(entries.size());
+ for (auto& entryWithOffset : entries) {
+ output.push_back(entryWithOffset.entry);
+ }
+ return true;
+ }
+
+private:
+ struct Node
+ {
+ //This needs to be a map to unsure deterministic ordering of tries.
+ std::map<std::string,std::unique_ptr<Node> > fChildren;
+ bool fIsTerminal;
+ uint32_t fTrieOffset;
+ V fInfo;
+
+ Node(void) : fIsTerminal(false), fTrieOffset(0) {}
+ Node(V v) :fInfo(v), fIsTerminal(true), fTrieOffset(0) {}
+ Node(Node&&) = default;
+
+ // byte for terminal node size in bytes, or 0x00 if not terminal node
+ // teminal node (uleb128 flags, uleb128 addr [uleb128 other])
+ // byte for child node count
+ // each child: zero terminated substring, uleb128 node offset
+ bool updateOffset(uint32_t& offset) {
+ uint32_t nodeSize = 1; // length of export info when no export info
+ if ( fIsTerminal ) {
+ nodeSize = fInfo.encodedSize();
+ // do have export info, overall node size so far is uleb128 of export info + export info
+ nodeSize += TrieUtils::uleb128_size(nodeSize);
+ }
+ // add children
+ ++nodeSize; // byte for count of chidren
+
+ for (auto &edge : fChildren) {
+ nodeSize += edge.first.length() + 1 + TrieUtils::uleb128_size(edge.second->fTrieOffset);
+ }
+
+ bool result = (fTrieOffset != offset);
+ fTrieOffset = offset;
+ //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString);
+ offset += nodeSize;
+ // return true if fTrieOffset was changed
+ return result;
+ }
+
+ void appendToStream(std::vector<uint8_t>& out) {
+ if ( fIsTerminal ) {
+ fInfo.appendToStream(out);
+ }
+ else {
+ // no export info uleb128 of zero is one byte of zero
+ out.push_back(0);
+ }
+ // write number of children
+ out.push_back(fChildren.size());
+ // write each child
+ for (auto &edge : fChildren) {
+ TrieUtils::append_string(edge.first, out);
+ TrieUtils::append_uleb128(edge.second->fTrieOffset, out);
+ }
+ }
+ };
+
+ Node root;
+
+ struct EntryWithOffset
+ {
+ uintptr_t nodeOffset;
+ Entry entry;
+
+ bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); }
+ };
+
+ void addEntry(const std::string& fullStr, std::string::const_iterator start, V v) {
+ Node *currentNode = &root;
+ bool done = false;
+
+ while (!done && !currentNode->fChildren.empty() ) {
+ done = true;
+
+ for (auto &entry : currentNode->fChildren) {
+ auto res = std::mismatch(entry.first.begin(), entry.first.end(), start);
+
+ if (res.first == entry.first.end()) {
+ //Matched a full edge, go down it
+ done = false;
+ currentNode = entry.second.get();
+ start = res.second;
+ break;
+ } else if (res.first != entry.first.begin()) {
+ // found a common substring, splice in new node
+ // was A -> C, now A -> B -> C
+
+ //Build the new strings
+ std::string abEdgeStr(entry.first.begin(), res.first);
+ std::string bcEdgeStr(res.first, entry.first.end());
+
+ //Copy out the exist node and delete it from the currentNode
+ std::unique_ptr<Node> nodeC;
+ std::swap(nodeC, entry.second);
+ currentNode->fChildren.erase(entry.first);
+
+ //Build the new node and insert it
+ std::unique_ptr<Node> nodeB = std::make_unique<Node>();
+ Node *newNode = nodeB.get();
+
+ nodeB->fChildren.insert(std::make_pair(bcEdgeStr, std::move(nodeC)));
+ currentNode->fChildren.insert(std::make_pair(abEdgeStr, std::move(nodeB)));
+
+ currentNode = newNode;
+ start = res.second;
+ ++nodeCount;
+ break;
+ }
+ }
+ }
+
+ // no commonality with any existing child, make a new edge that is this whole string
+ std::string edgeStr(start, fullStr.end());
+ v.willInsertAs(fullStr);
+
+ if (edgeStr.empty()) {
+ currentNode->fIsTerminal = true;
+ currentNode->fInfo = v;
+ } else {
+ currentNode->fChildren.emplace(edgeStr, std::make_unique<Node>(v));
+ ++nodeCount;
+ }
+ ++count;
+ }
+
+ void addEntry(Entry entry) {
+ addEntry(entry.name, entry.name.begin(), entry.info);
+ }
+
+#if TRIE_DEBUG
+ void printTrie(Node& node, std::string cummulativeString) {
+ if (node.fTerminal) {
+ printf("%s: \n", cummulativeString.c_str());
+ }
+ for (auto &edge : node.fChildren) {
+ printTrie(*edge.second, cummulativeString+edge.first);
+ }
+ }
+
+public:
+ void printTrie(void) {
+ printTrie(root, "");
+ }
+private:
+#endif
+
+ static inline bool processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
+ char* cummulativeString, int curStrOffset,
+ std::vector<EntryWithOffset>& output)
+ {
+ if ( p >= end )
+ return false;
+ uint64_t terminalSize;
+ if ( !TrieUtils::parse_uleb128(p, end, terminalSize) )
+ return false;
+ const uint8_t* children = p + terminalSize;
+ if ( children >= end )
+ return false;
+ if ( terminalSize != 0 ) {
+ EntryWithOffset e;
+ e.nodeOffset = p-start;
+ e.entry.name = cummulativeString;
+ e.entry.info.loadData(p,end);
+ output.push_back(e);
+ }
+ const uint8_t childrenCount = *children++;
+ const uint8_t* s = children;
+ for (uint8_t i=0; i < childrenCount; ++i) {
+ int edgeStrLen = 0;
+ while (*s != '\0') {
+ cummulativeString[curStrOffset+edgeStrLen] = *s++;
+ ++edgeStrLen;
+ }
+ cummulativeString[curStrOffset+edgeStrLen] = *s++;
+ uint64_t childNodeOffet;
+ if ( !TrieUtils::parse_uleb128(s, end, childNodeOffet) )
+ return false;
+ if ( !processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output) )
+ return false;
+ }
+ return true;
+ }
+
+ void orderTrie(Node* node, std::vector<Node*>& orderedNodes) {
+ orderedNodes.push_back(node);
+ for (auto &edge : node->fChildren) {
+ orderTrie(edge.second.get(), orderedNodes);
+ }
+ }
+}; // struct Trie
+
+struct ExportInfo {
+ uint64_t address;
+ uint64_t flags;
+ uint64_t other;
+ std::string importName;
+
+ ExportInfo() : other(0), address(0) {}
+
+ uint32_t encodedSize(void) {
+ uint32_t size = 0;
+ if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ size = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(other); // ordinal
+ if ( !importName.empty() )
+ size += importName.length();
+ ++size; // trailing zero in imported name
+ }
+ else {
+ size = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(address);
+ if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
+ size += TrieUtils::uleb128_size(other);
+ }
+ return size;
+ }
+
+ void appendToStream(std::vector<uint8_t>& out) {
+ if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ if ( !importName.empty() ) {
+ // nodes with re-export info: size, flags, ordinal, string
+ uint32_t nodeSize = (uint32_t)(TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(other) + importName.length() + 1);
+ out.push_back(nodeSize);
+ TrieUtils::append_uleb128(flags, out);
+ TrieUtils::append_uleb128(other, out);
+ TrieUtils::append_string(importName, out);
+ }
+ else {
+ // nodes with re-export info: size, flags, ordinal, empty-string
+ uint32_t nodeSize = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(other) + 1;
+ out.push_back(nodeSize);
+ TrieUtils::append_uleb128(flags, out);
+ TrieUtils::append_uleb128(other, out);
+ out.push_back(0);
+ }
+ }
+ else if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
+ // nodes with export info: size, flags, address, other
+ uint32_t nodeSize = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(address) + TrieUtils::uleb128_size(other);
+ out.push_back(nodeSize);
+ TrieUtils::append_uleb128(flags, out);
+ TrieUtils::append_uleb128(address, out);
+ TrieUtils::append_uleb128(other, out);
+ }
+ else {
+ // nodes with export info: size, flags, address
+ uint32_t nodeSize = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(address);
+ out.push_back(nodeSize);
+ TrieUtils::append_uleb128(flags, out);
+ TrieUtils::append_uleb128(address, out);
+ }
+ }
+
+ void loadData(const uint8_t* p, const uint8_t* const end) {
+ TrieUtils::parse_uleb128(p, end, flags);
+ if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ TrieUtils::parse_uleb128(p, end, other); // dylib ordinal
+ importName = (char*)p;
+ }
+ else {
+ TrieUtils::parse_uleb128(p, end, address);
+ if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
+ TrieUtils::parse_uleb128(p, end, other);
+ }
+ }
+
+ void willInsertAs(const std::string& name) {
+ // Symbols re-exported under the same name do not need an explict import name, delete it to save space
+ if ((name == importName) ) {
+ importName = "";
+ }
+ }
+};
+
+typedef Trie<ExportInfo> ExportInfoTrie;
+
+
+// Used by accelerator tables in dyld shared cache
+struct DylibIndex {
+ uint32_t index;
+
+ DylibIndex() : index(0) {}
+ DylibIndex(uint32_t i) : index(i) {}
+
+ uint32_t encodedSize(void) {
+ return TrieUtils::uleb128_size(index);
+ }
+
+ void appendToStream(std::vector<uint8_t>& out) {
+ uint32_t nodeSize = TrieUtils::uleb128_size(index);
+ out.push_back(nodeSize);
+ TrieUtils::append_uleb128(index, out);
+ }
+
+ void loadData(const uint8_t* p, const uint8_t* const end) {
+ uint64_t temp;
+ TrieUtils::parse_uleb128(p, end, temp);
+ index = (uint32_t)temp;
+ }
+
+ void willInsertAs(const std::string& name) {
+ }
+};
+typedef Trie<DylibIndex> DylibIndexTrie;
+
+
+#endif // __TRIE__
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2016 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 <dispatch/dispatch.h>
+#include <Security/Security.h>
+#include <Security/SecCodeSigner.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <fts.h>
+
+#include <vector>
+#include <array>
+#include <set>
+#include <map>
+#include <algorithm>
+
+#include <spawn.h>
+
+#include <Bom/Bom.h>
+
+#include "Manifest.h"
+#include "MultiCacheBuilder.h"
+
+#include "mega-dylib-utils.h"
+#include "Logging.h"
+
+#if !__has_feature(objc_arc)
+#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks
+#endif
+
+extern char** environ;
+
+static dispatch_queue_t build_queue;
+
+inline bool has_suffix(std::string const & value, std::string const & ending)
+{
+ if (ending.size() > value.size()) return false;
+ return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
+}
+
+static const char *tempRootDirTemplate = "/tmp/dyld_shared_cache_builder.XXXXXX";
+static char *tempRootDir = nullptr;
+
+void processRoot( const std::string &root, std::set<std::string> &roots ) {
+ struct stat sb;
+ int res = 0;
+ pid_t pid;
+ int status;
+ const char* args[8];
+
+ res = stat(root.c_str(), &sb);
+
+ if (res == 0 && S_ISDIR(sb.st_mode)) {
+ roots.insert( root );
+ return;
+ } else if ( has_suffix( root, ".cpio" ) || has_suffix( root, ".cpio.gz" ) || has_suffix( root, ".cpgz" ) ||
+ has_suffix( root, ".cpio.bz2" ) || has_suffix( root, ".cpbz2" ) || has_suffix( root, ".pax" ) ||
+ has_suffix( root, ".pax.gz" ) || has_suffix( root, ".pgz" ) || has_suffix( root, ".pax.bz2" ) ||
+ has_suffix( root, ".pbz2" ) ) {
+ args[0] = (char *)"/usr/bin/ditto";
+ args[1] = (char *)"-x";
+ args[2] = (char *)root.c_str();
+ args[3] = tempRootDir;
+ args[4] = nullptr;
+ } else if (has_suffix(root, ".tar")) {
+ args[0] = (char *)"/usr/bin/tar";
+ args[1] = (char *)"xf";
+ args[2] = (char *)root.c_str();
+ args[3] = (char *)"-C";
+ args[4] = tempRootDir;
+ args[5] = nullptr;
+ } else if (has_suffix(root, ".tar.gz") || has_suffix(root, ".tgz")) {
+ args[0] = (char *)"/usr/bin/tar";
+ args[1] = (char *)"xzf";
+ args[2] = (char *)root.c_str();
+ args[3] = (char *)"-C";
+ args[4] = tempRootDir;
+ args[5] = nullptr;
+ } else if (has_suffix(root, ".tar.bz2")
+ || has_suffix(root, ".tbz2")
+ || has_suffix(root, ".tbz")) {
+ args[0] = (char *)"/usr/bin/tar";
+ args[1] = (char *)"xjf";
+ args[2] = (char *)root.c_str();
+ args[3] = (char *)"-C";
+ args[4] = tempRootDir;
+ args[5] = nullptr;
+ } else if (has_suffix(root, ".xar")) {
+ args[0] = (char *)"/usr/bin/xar";
+ args[1] = (char *)"-xf";
+ args[2] = (char *)root.c_str();
+ args[3] = (char *)"-C";
+ args[4] = tempRootDir;
+ args[5] = nullptr;
+ } else if (has_suffix(root, ".zip")) {
+ args[0] = (char *)"/usr/bin/ditto";
+ args[1] = (char *)"-xk";
+ args[2] = (char *)root.c_str();
+ args[3] = tempRootDir;
+ args[4] = nullptr;
+ } else {
+ terminate("unknown archive type: %s", root.c_str());
+ }
+
+ res = posix_spawn(&pid, args[0], nullptr, nullptr, (char**)args, environ);
+ if (res != 0) terminate("Failed to spawn %s: %s (%d)", args[0], strerror(res), res);
+
+ do {
+ res = waitpid(pid, &status, 0);
+ } while (res == -1 && errno == EINTR);
+ if (res != -1) {
+ if (WIFEXITED(status)) {
+ res = WEXITSTATUS(status);
+ } else {
+ res = -1;
+ }
+ }
+
+ if (res != 0) terminate("Could not expand archive %s: %s (%d)", root.c_str(), strerror(res), res);
+
+ for (auto &existingRoot : roots) {
+ if (existingRoot == tempRootDir)
+ return;
+ }
+
+ roots.insert( tempRootDir );
+}
+
+bool writeRootList( const std::string &dstRoot, const std::set<std::string> &roots ) {
+ if ( roots.size() == 0 ) return false;
+
+ std::string rootFile = dstRoot + "/roots.txt";
+ FILE* froots = ::fopen(rootFile.c_str(), "w");
+ if ( froots == NULL )
+ return false;
+
+ for (auto &root : roots) {
+ fprintf(froots, "%s\n", root.c_str());
+ }
+
+ ::fclose(froots);
+ return true;
+}
+
+std::set<std::string> cachePaths;
+
+BOMCopierCopyOperation filteredCopy(BOMCopier copier, const char *path, BOMFSObjType type, off_t size) {
+ std::string absolutePath = &path[1];
+ if (cachePaths.count(absolutePath)) {
+ return BOMCopierSkipFile;
+ }
+ return BOMCopierContinue;
+}
+
+int main (int argc, const char * argv[]) {
+ @autoreleasepool {
+ std::set<std::string> roots;
+ std::vector<std::string> inputRoots;
+ std::string dylibCacheDir;
+ std::string release;
+ bool emitDevCaches = true;
+ bool emitElidedDylibs = true;
+ bool listConfigs = false;
+ bool copyRoots = false;
+ std::string dstRoot;
+ std::string configuration;
+ std::string resultPath;
+
+ time_t mytime = time(0);
+ log("Started: %s", asctime(localtime(&mytime)));
+
+ tempRootDir = strdup(tempRootDirTemplate);
+ mkdtemp(tempRootDir);
+
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (arg[0] == '-') {
+ if (strcmp(arg, "-debug") == 0) {
+ setVerbose(true);
+ } else if (strcmp(arg, "-list_configs") == 0) {
+ listConfigs = true;
+ } else if (strcmp(arg, "-root") == 0) {
+ std::string root = argv[++i];
+ inputRoots.push_back(root);
+ processRoot(root, roots);
+ } else if (strcmp(arg, "-copy_roots") == 0) {
+ copyRoots = true;
+ } else if (strcmp(arg, "-dylib_cache") == 0) {
+ dylibCacheDir = argv[++i];
+ } else if (strcmp(arg, "-no_development_cache") == 0) {
+ emitDevCaches = false;
+ } else if (strcmp(arg, "-no_overflow_dylibs") == 0) {
+ emitElidedDylibs = false;
+ } else if (strcmp(arg, "-development_cache") == 0) {
+ emitDevCaches = true;
+ } else if (strcmp(arg, "-overflow_dylibs") == 0) {
+ emitElidedDylibs = true;
+ } else if (strcmp(arg, "-dst_root") == 0) {
+ dstRoot = argv[++i];
+ } else if (strcmp(arg, "-release") == 0) {
+ release = argv[++i];
+ } else if (strcmp(arg, "-results") == 0) {
+ resultPath = argv[++i];
+ } else {
+ //usage();
+ terminate("unknown option: %s\n", arg);
+ }
+ } else {
+ if (!configuration.empty()) {
+ terminate("You may only specify one configruation");
+ }
+ configuration = argv[i];
+ }
+ }
+
+ struct rlimit rl = {OPEN_MAX, OPEN_MAX};
+ (void)setrlimit(RLIMIT_NOFILE, &rl);
+
+ if (dylibCacheDir.empty() && release.empty()) {
+ terminate("you must specify either -dylib_cache or -release");
+ } else if (!dylibCacheDir.empty() && !release.empty()) {
+ terminate("you may not use -dylib_cache and -release at the same time");
+ }
+
+ if ((configuration.empty() || dstRoot.empty()) && !listConfigs) {
+ terminate("Must specify a configuration and a -dst_root OR -list_configs\n");
+ }
+
+
+ if (dylibCacheDir.empty()) {
+ dylibCacheDir = std::string("/AppleInternal/Developer/DylibCaches/") + release + ".dlc";
+ }
+
+ //Move into the dir so we can use relative path manifests
+ chdir(dylibCacheDir.c_str());
+
+ auto manifest = Manifest(dylibCacheDir + "/Manifest.plist", roots);
+
+ if (manifest.build.empty()) {
+ terminate("No manifest found at '%s/Manifest.plist'\n", dylibCacheDir.c_str());
+ }
+ log("Building Caches for %s", manifest.build.c_str());
+
+ if (listConfigs) {
+ for (auto& config : manifest.configurations) {
+ printf("%s\n", config.first.c_str());
+ }
+ exit(0);
+ }
+
+ std::map<std::string, Manifest::Configuration> filteredConfigs;
+
+ for (auto& config : manifest.configurations) {
+ if (config.first == configuration) {
+ filteredConfigs[config.first] = config.second;
+
+ for (auto& arch : filteredConfigs[config.first].architectures) {
+ arch.second.results = Manifest::Results();
+ }
+ }
+ }
+
+ if ( filteredConfigs.empty() ) {
+ terminate( "No config %s. Please run with -list_configs to see configurations available for this %s.\n",
+ configuration.c_str(), manifest.build.c_str() );
+ }
+
+ manifest.configurations = filteredConfigs;
+ manifest.calculateClosure(false);
+
+ // FIXME: Plumb through no_development
+
+ std::shared_ptr<MultiCacheBuilder> builder = std::make_shared<MultiCacheBuilder>( manifest, false, false, true );
+ builder->buildCaches(dstRoot);
+ writeRootList(dstRoot, roots);
+
+ if (copyRoots) {
+ for (auto& config : manifest.configurations) {
+ for (auto& arch : config.second.architectures) {
+ for (auto& dylib : arch.second.results.dylibs) {
+ if (dylib.second.included) {
+ MachOProxy *proxy = manifest.dylibProxy( dylib.first, arch.first );
+ cachePaths.insert( proxy->installName );
+ for ( auto &alias : proxy->installNameAliases ) {
+ cachePaths.insert(alias);
+ }
+ }
+ }
+ }
+ }
+
+ BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
+ BOMCopierSetCopyFileStartedHandler(copier, filteredCopy);
+ for (auto& root : roots) {
+ BOMCopierCopy(copier, root.c_str(), dstRoot.c_str());
+ }
+ BOMCopierFree(copier);
+ }
+
+ // Create an empty FIPS data in the root
+ (void)mkpath_np((dstRoot + "/private/var/db/FIPS/").c_str(), 0755);
+ int fd = open((dstRoot + "/private/var/db/FIPS/fips_data").c_str(), O_CREAT | O_TRUNC, 0644);
+ close(fd);
+
+ // Now that all the build commands have been issued lets put a barrier in after then which can tear down the app after
+ // everything is written.
+
+ builder->logStats();
+ if (!resultPath.empty()) {
+ manifest.write(resultPath);
+ }
+
+ pid_t pid;
+ int res = 0;
+ int status;
+ const char* args[8];
+
+ args[0] = (char*)"/bin/rm";
+ args[1] = (char*)"-rf";
+ args[2] = (char*)tempRootDir;
+ args[3] = nullptr;
+
+ res = posix_spawn(&pid, args[0], nullptr, nullptr, (char**)args, environ);
+ if (res != 0)
+ terminate("Failed to spawn %s: %s (%d)", args[0], strerror(res), res);
+
+ do {
+ res = waitpid(pid, &status, 0);
+ } while (res == -1 && errno == EINTR);
+ if (res != -1) {
+ if (WIFEXITED(status)) {
+ res = WEXITSTATUS(status);
+ } else {
+ res = -1;
+ }
+ }
+
+ dumpLogAndExit();
+ }
+
+ dispatch_main();
+
+ return 0;
+}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 __MEGA_DYLIB_UTILS_H__
+#define __MEGA_DYLIB_UTILS_H__
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <dispatch/dispatch.h>
+
+#include <memory>
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <unordered_set>
+#include <unordered_map>
+
+#include "CacheFileAbstraction.hpp"
+
+#include "MachOFileAbstraction.hpp"
+
+#include "Manifest.h"
+#include "MachOProxy.h"
+
+struct SharedCache;
+
+struct FileCache {
+ FileCache(void);
+ std::tuple<uint8_t *, struct stat, bool> cacheLoad(const std::string path);
+ void preflightCache(const std::string& path);
+ void preflightCache(const std::unordered_set<std::string> &paths);
+private:
+ void fill(const std::string& path);
+
+ std::unordered_map<std::string, std::tuple<uint8_t *, struct stat, bool>> entries;
+ dispatch_queue_t cache_queue;
+};
+
+extern FileCache fileCache;
+
+typedef std::pair<Manifest*,std::set<std::pair<std::string, std::string>>> WarningTargets;
+
+inline uint64_t align(uint64_t addr, uint8_t p2)
+{
+ uint64_t mask = (1 << p2);
+ return (addr + mask - 1) & (-mask);
+}
+
+
+uint64_t sharedRegionRegionSize(ArchPair arch);
+uint8_t sharedRegionRegionAlignment(ArchPair arch);
+ArchPair archForString(const std::string& archStr);
+std::string stringForArch(ArchPair arch, bool allowUnknown = false);
+std::string fallbackArchStringForArchString( const std::string& archStr );
+
+struct SharedCache {
+ struct SegmentInfo {
+ SegmentInfo(const MachOProxy::Segment* seg)
+ : base(seg), address(0), cacheFileOffset(0), cacheSegSize(0) { }
+
+ const MachOProxy::Segment* base;
+ uint64_t address;
+ uint64_t cacheFileOffset;
+ uint64_t cacheSegSize;
+ };
+
+ struct Region {
+ uint64_t address;
+ uint64_t size;
+ uint64_t fileOffset;
+ uint32_t prot;
+ };
+
+ SharedCache(Manifest& manifest,
+ const std::string& configuration, const std::string& architecture);
+
+ // We do not need an explicit destructor despite our move/copy constructor because the resource the are dealing with is a
+ // std::unique<void> which has a registered destructor
+
+ //FIXME reminants of merging the two classes, unifyu these
+ uint64_t vmSize() const { return _vmSize; }
+ uint64_t fileSize() const { return _fileSize; }
+ const uint8_t* cdHash() { return _cdHash; }
+ std::string cdHashString();
+
+ //FIXME: This should be private, but we can't do that until we move write out into the class after the next refactor
+ std::shared_ptr<void> buffer() const;
+
+ void buildForDevelopment(const std::string& cachePath);
+ void buildForProduction(const std::string& cachePath);
+ bool writeCacheMapFile(const std::string& mapPath);
+
+ typedef std::function<void(const void* machHeader, const char* installName, time_t lastModTime, ino_t inode,
+ const std::vector<MachOProxy::Segment>& segments)> DylibHandler;
+ // Calls lambda once per image in the cache
+ void forEachImage(DylibHandler handler);
+
+ typedef std::function<void(void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)> RegionHandler;
+ // Calls lambda once per region in the cache
+ void forEachRegion(RegionHandler handler);
+
+ void setLinkeditsMappingEndFileOffset(uint64_t newFileSize);
+ void setAcceleratorInfoRange(uint64_t accelInfoAddr, uint32_t accelInfoSize);
+ void setUnmappedLocalsRange(uint64_t localSymbolsOffset, uint32_t unmappedSize);
+ void recomputeCacheUUID(void);
+
+private:
+ void loadDirtyDataOrderFile(const std::string& dirtyDataOrderFile);
+ void sortDylibs(const std::string& dylibOrderFile);
+ void assignSegmentAddresses();
+ void bypassStubs(const std::vector<uint64_t>& branchPoolStartAddrs);
+ void optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets);
+
+ // Once all a dylib's segments are copied into a cache, this function will adjust the contents of
+ // the TEXT, DATA, and LINKEDIT segments in the cache to be correct for their new addresses.
+ void bindAllImagesInCache(const std::unordered_map<std::string, void*>& dylibPathToMachHeader, std::vector<void*>& pointersForASLR);
+
+ // After adjustImageForNewSegmentLocations() is called to rebase all segments, this function can be called to
+ // bind all symbols to their new addresses
+ void adjustImageForNewSegmentLocations(const std::vector<uint64_t>& segNewStartAddresses,
+ const std::vector<uint64_t>& segCacheFileOffsets,
+ const std::vector<uint64_t>& segCacheSizes, std::vector<void*>& pointersForASLR);
+
+ uint64_t pathHash(const char* path);
+ void writeCacheHeader(void);
+ void writeCacheSegments(void);
+ void rebaseAll(void);
+ void rebase(MachOProxy* dylib);
+ void bindAll(void);
+ void optimizeObjC(bool forProduction);
+ void writeSlideInfoV2(void);
+
+ void buildUnoptimizedCache();
+ void appendCodeSignature(const std::string& suffix);
+ template <typename P> void buildForDevelopment(const std::string& cachePath);
+ template <typename P> void buildForProduction(const std::string& cachePath);
+ template <typename P> void forEachImage(DylibHandler handler);
+ template <typename P> void forEachRegion(RegionHandler handler);
+ template <typename P> void setLinkeditsMappingEndFileOffset(uint64_t newFileSize);
+ template <typename P> void setAcceleratorInfoRange(uint64_t accelInfoAddr, uint32_t accelInfoSize);
+ template <typename P> void setUnmappedLocalsRange(uint64_t localSymbolsOffset, uint32_t unmappedSize);
+ template <typename P> void recomputeCacheUUID(void);
+ template <typename P> void bypassStubs(const std::vector<uint64_t>& branchPoolStartAddrs);
+ template <typename P> void optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets);
+ template <typename P> void writeCacheHeader(void);
+ template <typename E> void writeSlideInfo(void);
+ template <typename P> void writeSlideInfoV2(uint64_t deltaMask, uint64_t valueAdd);
+
+ template <typename P> void findImplicitAliases(std::shared_ptr<MachOProxy> dylib);
+ template <typename P> void addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyldCacheSlideInfo2<typename P::E>* info,
+ std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
+ template <typename P> bool makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const dyldCacheSlideInfo2<typename P::E>* info);
+ void findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
+
+ ArchPair _arch;
+ std::vector<MachOProxy *> _dylibs;
+ std::shared_ptr<void> _buffer;
+ std::unordered_map<const MachOProxy *, std::vector<SegmentInfo>> _segmentMap;
+
+ std::string archName();
+
+ Manifest& _manifest;
+ const Manifest::Architecture& _archManifest;
+ uint32_t _aliasCount;
+ Region _textRegion;
+ Region _dataRegion;
+ Region _readOnlyRegion;
+ uint64_t _slideInfoFileOffset;
+ uint64_t _slideInfoBufferSize;
+ uint64_t _fileSize;
+ uint64_t _vmSize;
+ std::unordered_map<std::string, uint32_t> _dataDirtySegsOrder;
+ std::vector<void*> _pointersForASLR;
+ std::vector<uint64_t> _branchPoolStarts;
+ uint64_t _branchPoolsLinkEditStartAddr;
+ uint8_t _cdHash[20];
+};
+
+std::string normalize_absolute_file_path(const std::string &path);
+std::string baspath(const std::string& path);
+std::string dirpath(const std::string& path);
+
+std::string toolDir();
+bool isProtectedBySIP(const std::string& path, int fd=-1);
+
+
+template <class Set1, class Set2>
+inline bool is_disjoint(const Set1& set1, const Set2& set2)
+{
+ if (set1.empty() || set2.empty())
+ return true;
+
+ typename Set1::const_iterator it1 = set1.begin(), it1End = set1.end();
+ typename Set2::const_iterator it2 = set2.begin(), it2End = set2.end();
+
+ if (*it1 > *set2.rbegin() || *it2 > *set1.rbegin())
+ return true;
+
+ while (it1 != it1End && it2 != it2End) {
+ if (*it1 == *it2)
+ return false;
+ if (*it1 < *it2) {
+ it1++;
+ } else {
+ it2++;
+ }
+ }
+
+ return true;
+}
+
+inline bool has_prefix(const std::string& str, const std::string& prefix)
+{
+ return std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
+}
+
+
+#define NEW_CACHE_FILE_FORMAT 0
+
+#endif // __MEGA_DYLIB_UTILS_H__
+
+
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2016 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 <dispatch/dispatch.h>
+#include <Security/Security.h>
+#include <Security/SecCodeSigner.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <pthread.h>
+
+#include <vector>
+#include <array>
+#include <set>
+#include <map>
+#include <algorithm>
+
+#include "MultiCacheBuilder.h"
+#include "Manifest.h"
+
+#include "mega-dylib-utils.h"
+#include "Logging.h"
+
+#if !__has_feature(objc_arc)
+#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks
+#endif
+
+static dispatch_queue_t build_queue = dispatch_queue_create("com.apple.dyld.build", DISPATCH_QUEUE_CONCURRENT);
+static dispatch_group_t build_group = dispatch_group_create();
+static dispatch_semaphore_t writeLimitingSemaphore = dispatch_semaphore_create(1);
+
+bool copyFile(const std::string& from, const std::string& to)
+{
+ const uint8_t* p = (uint8_t*)(-1);
+ struct stat stat_buf;
+ bool rp;
+
+ std::tie(p, stat_buf, rp) = fileCache.cacheLoad(from);
+ if (p == (uint8_t*)(-1))
+ return false;
+
+ dispatch_group_enter(build_group);
+ mkpath_np(dirpath(to).c_str(), 0755);
+
+ dispatch_semaphore_wait(writeLimitingSemaphore, DISPATCH_TIME_FOREVER);
+ int fd = open(to.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd > 0) {
+ ssize_t writtenSize = pwrite(fd, p, stat_buf.st_size, 0);
+ if (writtenSize != stat_buf.st_size)
+ terminate("write() failure creating cache file, errno=%d (%s)", errno, strerror(errno));
+
+ ::close(fd);
+ verboseLog("Wrote out: %s", to.c_str());
+ } else {
+ terminate("can't create file '%s', errnor=%d (%s)", to.c_str(), errno, strerror(errno));
+ }
+ dispatch_semaphore_signal(writeLimitingSemaphore);
+ dispatch_group_leave(build_group);
+
+ return true;
+}
+
+#define kDylibCachePrefix "/AppleInternal/Developer/DylibCaches/"
+
+void createArtifact(Manifest& manifest, const std::string& dylibCachePath, bool includeExecutables)
+{
+ mkpath_np(dylibCachePath.c_str(), 0755);
+ (void)copyFile(manifest.dylibOrderFile, dylibCachePath + "Metadata/dylibOrderFile.txt");
+ (void)copyFile(manifest.dirtyDataOrderFile, dylibCachePath + "Metadata/dirtyDataOrderFile.txt");
+ (void)copyFile(manifest.metabomFile, dylibCachePath + "Metadata/metabom.bom");
+
+ std::set<std::string> copied;
+
+ for (auto archFiles : manifest.architectureFiles) {
+ for (auto& file : archFiles.second.dylibs) {
+ std::string installname = file.first;
+ if (copied.count(installname) > 0) {
+ continue;
+ }
+ (void)copyFile(file.second.proxy->path, normalize_absolute_file_path(dylibCachePath + "/Root/" + file.first));
+ copied.insert(installname);
+ }
+ if (includeExecutables) {
+ for (auto& file : archFiles.second.executables) {
+ std::string installname = file.first;
+ if (copied.count(installname) > 0) {
+ continue;
+ }
+ (void)copyFile(file.second.proxy->path, normalize_absolute_file_path(dylibCachePath + "/Root/" + file.first));
+ copied.insert(installname);
+ }
+ }
+ }
+
+ log("Artifact dylibs copied");
+}
+
+void addArtifactPaths(Manifest &manifest) {
+ manifest.dylibOrderFile = "./Metadata/dylibOrderFile.txt";
+ manifest.dirtyDataOrderFile = "./Metadata/dirtyDataOrderFile.txt";
+ manifest.metabomFile = "./Metadata/metabom.bom";
+
+ for ( auto& projects : manifest.projects ) {
+ if ( projects.second.sources[0] != "./Root/" ) {
+ projects.second.sources.insert( projects.second.sources.begin(), "./Root/" );
+ }
+ }
+}
+
+int main (int argc, const char * argv[]) {
+ @autoreleasepool {
+ auto defaultCtx = std::make_shared<LoggingContext>("");
+ setLoggingContext(defaultCtx);
+
+ Manifest manifest;
+ std::string masterDstRoot;
+ std::string dylibCacheDir;
+ std::string resultPath;
+ bool skipWrites = false;
+ bool skipBuilds = false;
+ bool preflight = false;
+ std::string manifestPath;
+
+ time_t mytime = time(0);
+ log("Started: %s", asctime(localtime(&mytime)));
+
+ // parse command line options
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (arg[0] == '-') {
+ if (strcmp(arg, "-debug") == 0) {
+ setVerbose(true);
+ } else if (strcmp(arg, "-skip_writes") == 0) {
+ skipWrites = true;
+ } else if (strcmp(arg, "-skip_builds") == 0) {
+ skipBuilds = true;
+ } else if (strcmp(arg, "-delete_writes") == 0) {
+ skipWrites = true;
+ } else if (strcmp(arg, "-dylib_cache") == 0) {
+ dylibCacheDir = argv[++i];
+ } else if (strcmp(arg, "-preflight") == 0) {
+ preflight = true;
+ skipWrites = true;
+ } else if (strcmp(arg, "-master_dst_root") == 0) {
+ masterDstRoot = argv[++i];
+ if (masterDstRoot.empty()) {
+ terminate("-master_dst_root missing path argument");
+ }
+ } else if (strcmp(arg, "-results") == 0) {
+ resultPath = argv[++i];
+ } else if (strcmp(arg, "-plist") == 0) {
+ manifestPath = argv[++i];
+ (void)chdir(dirname(strdup(manifestPath.c_str())));
+ manifest = Manifest(manifestPath);
+ } else {
+ // usage();
+ terminate("unknown option: %s", arg);
+ }
+ } else {
+ manifestPath = argv[i];
+
+ (void)chdir(dirname(strdup(argv[i])));
+ }
+ }
+
+ if ( getenv( "LGG_SKIP_CACHE_FUN" ) != nullptr ) {
+ skipBuilds = true;
+ }
+
+ if (manifest.build.empty()) {
+ terminate("No version found in manifest");
+ }
+ log("Building Caches for %s", manifest.build.c_str());
+
+ if ( masterDstRoot.empty() ) {
+ terminate("-master_dst_root required path argument");
+ }
+
+ if (manifest.manifest_version < 4) {
+ terminate("must specify valid manifest file");
+ }
+
+ struct rlimit rl = {OPEN_MAX, OPEN_MAX};
+ (void)setrlimit(RLIMIT_NOFILE, &rl);
+
+ if (!skipWrites) {
+ (void)mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755);
+ }
+
+ if (manifest.dylibOrderFile.empty()) {
+ manifest.dylibOrderFile = toolDir() + "/dylib-order.txt";
+ }
+
+ if (manifest.dirtyDataOrderFile.empty()) {
+ manifest.dirtyDataOrderFile = toolDir() + "/dirty-data-segments-order.txt";
+ }
+
+ auto dylibCacheCtx = std::make_shared<LoggingContext>("DylibCache");
+ setLoggingContext(dylibCacheCtx);
+
+ if (!skipWrites) {
+ cacheBuilderDispatchGroupAsync(build_group, build_queue, [&] {
+ createArtifact(manifest, masterDstRoot + "/Artifact.dlc/", true);
+ });
+
+ if (!dylibCacheDir.empty()) {
+ cacheBuilderDispatchGroupAsync(build_group, build_queue, [&] {
+ createArtifact(manifest, dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build + ".dlc/", false);
+ });
+ }
+ }
+ setLoggingContext(defaultCtx);
+
+ manifest.calculateClosure(false);
+ std::shared_ptr<MultiCacheBuilder> builder = std::make_shared<MultiCacheBuilder>(manifest, true, skipWrites, false, skipBuilds);
+ dispatch_group_async(build_group, build_queue, [&] { builder->buildCaches(masterDstRoot); });
+ dispatch_group_wait(build_group, DISPATCH_TIME_FOREVER);
+
+ if (!preflight) {
+ setLoggingContext(dylibCacheCtx);
+ addArtifactPaths(manifest);
+ setLoggingContext(defaultCtx);
+
+ if (!resultPath.empty()) {
+ manifest.write(resultPath);
+ }
+
+ setLoggingContext(dylibCacheCtx);
+ copyFile(manifestPath, masterDstRoot + "/Artifact.dlc/BNIManifest.plist");
+ manifest.write(masterDstRoot + "/Artifact.dlc/Manifest.plist");
+
+ if (!dylibCacheDir.empty()) {
+ manifest.write(dylibCacheDir + kDylibCachePrefix + manifest.build + ".dlc/Manifest.plist");
+ }
+ setLoggingContext(defaultCtx);
+ }
+
+ builder->logStats();
+
+ dumpLogAndExit();
+ }
+
+ dispatch_main();
+
+ return 0;
+}
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <rootless.h>
+
+extern "C" {
+#include <dscsym.h>
+}
+
+#include <vector>
+#include <set>
+#include <map>
+#include <iostream>
+#include <fstream>
+
+#include "MachOProxy.h"
+#include "manifest.h"
+#include "mega-dylib-utils.h"
+#include "Logging.h"
+
+#import "MultiCacheBuilder.h"
+
+#if !__has_feature(objc_arc)
+#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks
+#endif
+
+const std::string anchorsDirPath = "/private/var/db/dyld/shared_region_roots";
+
+bool parsePathsFile( const std::string& filePath, std::set<std::string>& paths ) {
+ verboseLog( "parsing .paths file '%s'", filePath.c_str() );
+ std::ifstream myfile( filePath );
+ if ( myfile.is_open() ) {
+ std::string line;
+ while ( std::getline(myfile, line) ) {
+ size_t pos = line.find('#');
+ if ( pos != std::string::npos )
+ line.resize(pos);
+ while ( line.size() != 0 && isspace(line.back()) ) {
+ line.pop_back();
+ }
+ if ( !line.empty() ) paths.insert( line );
+ }
+ myfile.close();
+
+ return true;
+ }
+ return false;
+}
+
+static bool parseDirectoryOfPathsFiles(const std::string& dirPath, std::set<std::string>& paths)
+{
+ DIR* dir = ::opendir(dirPath.c_str());
+ if ( dir == NULL )
+ return false;
+
+ for (dirent* entry = ::readdir(dir); entry != NULL; entry = ::readdir(dir)) {
+ if ( entry->d_type == DT_REG || entry->d_type == DT_UNKNOWN ) {
+ // only look at regular files ending in .paths
+ if ( strcmp(&entry->d_name[entry->d_namlen-6], ".paths") == 0 ) {
+ struct stat statBuf;
+ std::string filePathStr = dirPath + "/" + entry->d_name;
+ const char* filePath = filePathStr.c_str();
+ if ( lstat(filePath, &statBuf) == -1 ) {
+ warning("can't access file '%s'", filePath);
+ }
+ else if ( S_ISREG(statBuf.st_mode) ) {
+ parsePathsFile(filePath, paths);
+ }
+ else {
+ warning("not a regular file '%s'", filePath);
+ }
+ }
+ else {
+ warning("ignoring file with wrong extension '%s'", entry->d_name);
+ }
+ }
+ }
+ ::closedir(dir);
+ return true;
+}
+
+static bool buildInitialPaths(const std::string& volumeRootPath, const std::string& overlayPath, std::set<std::string>& paths)
+{
+ // in -root mode, look for roots in /rootpath/private/var/db/dyld/shared_region_roots
+ if ( volumeRootPath != "/" ) {
+ if ( parseDirectoryOfPathsFiles(volumeRootPath + "/" + anchorsDirPath, paths) )
+ return true;
+ // fallback to .paths files on boot volume
+ return parseDirectoryOfPathsFiles(anchorsDirPath, paths);
+ }
+
+ // in -overlay mode, look for .paths first in each /overlay/private/var/db/dyld/shared_region_roots
+ if ( !overlayPath.empty() ) {
+ parseDirectoryOfPathsFiles(overlayPath + "/" + anchorsDirPath, paths);
+ }
+
+ // look for .paths files in /private/var/db/dyld/shared_region_roots
+ return parseDirectoryOfPathsFiles(anchorsDirPath, paths);
+}
+
+bool fileExists(const std::string& path, bool& isSymLink)
+{
+ struct stat statBuf;
+ if ( lstat(path.c_str(), &statBuf) == -1 )
+ return false;
+ isSymLink = S_ISLNK(statBuf.st_mode);
+ return S_ISREG(statBuf.st_mode) || isSymLink;
+}
+
+bool tryPath(const std::string& prefix, const std::string& path, std::string& foundPath, std::vector<std::string>& aliases)
+{
+ foundPath = prefix + path;
+ bool isSymLink;
+ if ( !fileExists(foundPath, isSymLink) )
+ return false;
+ if ( isSymLink ) {
+ // handle case where install name is a symlink to real file (e.g. libstdc++.6.dylib -> libstdc++.6.0.9.dylib)
+ char pathInSymLink[MAXPATHLEN];
+ long len = ::readlink(foundPath.c_str(), pathInSymLink, sizeof(pathInSymLink));
+ if ( len != -1 ) {
+ pathInSymLink[len] = '\0';
+ if ( pathInSymLink[0] != '/' ) {
+ std::string aliasPath = path;
+ size_t pos = aliasPath.rfind('/');
+ if ( pos != std::string::npos ) {
+ std::string newPath = aliasPath.substr(0,pos+1) + pathInSymLink;
+ aliases.push_back(newPath);
+ }
+ }
+ }
+ }
+ char realPath[MAXPATHLEN];
+ if ( ::realpath(foundPath.c_str(), realPath) ) {
+ if ( foundPath != realPath ) {
+ std::string altPath = realPath;
+ if ( !prefix.empty() ) {
+ if ( altPath.substr(0, prefix.size()) == prefix ) {
+ altPath = altPath.substr(prefix.size());
+ }
+ }
+ if ( altPath != path )
+ aliases.push_back(path);
+ else
+ aliases.push_back(altPath);
+ }
+ }
+ return true;
+}
+
+bool improvePath(const char* volumeRootPath, const std::vector<const char*>& overlayPaths,
+ const std::string& path, std::string& foundPath, std::vector<std::string>& aliases)
+{
+ for (const char* overlay : overlayPaths) {
+ if ( tryPath(overlay, path, foundPath, aliases) )
+ return true;
+ }
+ if ( volumeRootPath[0] != '\0' ) {
+ if ( tryPath(volumeRootPath, path, foundPath, aliases) )
+ return true;
+ }
+ return tryPath("", path, foundPath, aliases);
+}
+
+std::string fileExists( const std::string& path ) {
+ const uint8_t* p = (uint8_t*)( -1 );
+ struct stat stat_buf;
+ bool rootless;
+
+ std::tie( p, stat_buf, rootless ) = fileCache.cacheLoad( path );
+ if ( p != (uint8_t*)( -1 ) ) {
+ return normalize_absolute_file_path( path );
+ }
+
+ return "";
+}
+
+void populateManifest(Manifest& manifest, std::set<std::string> archs, const std::string& overlayPath,
+ const std::string& rootPath, const std::set<std::string>& paths) {
+ for ( const auto& arch : archs ) {
+ auto fallback = fallbackArchStringForArchString(arch);
+ std::set<std::string> allArchs = archs;
+ std::set<std::string> processedPaths;
+ std::set<std::string> unprocessedPaths = paths;
+ std::set<std::string> pathsToProcess;
+ std::set_difference( unprocessedPaths.begin(), unprocessedPaths.end(), processedPaths.begin(), processedPaths.end(),
+ std::inserter( pathsToProcess, pathsToProcess.begin() ) );
+ while ( !pathsToProcess.empty() ) {
+ for (const std::string path : pathsToProcess) {
+ processedPaths.insert(path);
+ std::string fullPath;
+ if ( rootPath != "/" ) {
+ // with -root, only look in the root path volume
+ fullPath = fileExists(rootPath + path);
+ }
+ else {
+ // with -overlay, look first in overlay dir
+ if ( !overlayPath.empty() )
+ fullPath = fileExists(overlayPath + path);
+ // if not in overlay, look in boot volume
+ if ( fullPath.empty() )
+ fullPath = fileExists(path);
+ }
+ if ( fullPath.empty() )
+ continue;
+ auto proxies = MachOProxy::findDylibInfo(fullPath, true, true);
+ auto proxy = proxies.find(arch);
+ if (proxy == proxies.end())
+ proxy = proxies.find(fallback);
+ if (proxy == proxies.end())
+ continue;
+
+ for ( const auto& dependency : proxy->second->dependencies ) {
+ unprocessedPaths.insert( dependency );
+ }
+
+ if ( proxy->second->installName.empty() ) {
+ continue;
+ }
+
+ proxy->second->addAlias( path );
+ manifest.architectureFiles[arch].dylibs.insert(std::make_pair(proxy->second->installName,
+ Manifest::File(proxy->second)));
+ manifest.configurations["localhost"].architectures[arch].anchors.push_back( proxy->second->installName );
+ }
+
+ pathsToProcess.clear();
+ std::set_difference( unprocessedPaths.begin(), unprocessedPaths.end(), processedPaths.begin(), processedPaths.end(),
+ std::inserter( pathsToProcess, pathsToProcess.begin() ) );
+ }
+ }
+}
+
+static bool runningOnHaswell()
+{
+ // check system is capable of running x86_64h code
+ struct host_basic_info info;
+ mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+ mach_port_t hostPort = mach_host_self();
+ kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
+ mach_port_deallocate(mach_task_self(), hostPort);
+
+ return ( (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H) );
+}
+
+
+#define TERMINATE_IF_LAST_ARG( s ) \
+ do { \
+ if ( i == argc - 1 ) terminate( s ); \
+ } while ( 0 )
+
+int main(int argc, const char* argv[])
+{
+ std::string rootPath;
+ std::string overlayPath;
+ std::string platform = "osx";
+ std::string dylibListFile;
+ bool universal = false;
+ bool force = false;
+ std::string cacheDir;
+ std::set<std::string> archStrs;
+ std::vector<Manifest::Anchor> anchors;
+
+ // parse command line options
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (arg[0] == '-') {
+ if (strcmp(arg, "-debug") == 0) {
+ setVerbose(true);
+ } else if (strcmp(arg, "-verbose") == 0) {
+ setVerbose(true);
+ } else if (strcmp(arg, "-dont_map_local_symbols") == 0) {
+ //We are going to ignore this
+ } else if (strcmp(arg, "-iPhone") == 0) {
+ platform = "iphoneos";
+ } else if (strcmp(arg, "-dylib_list") == 0) {
+ TERMINATE_IF_LAST_ARG("-dylib_list missing argument");
+ dylibListFile = argv[++i];
+ } else if ((strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0)) {
+ TERMINATE_IF_LAST_ARG("-root missing path argument\n");
+ rootPath = argv[++i];
+ } else if (strcmp(arg, "-overlay") == 0) {
+ TERMINATE_IF_LAST_ARG("-overlay missing path argument\n");
+ overlayPath = argv[++i];
+ } else if (strcmp(arg, "-cache_dir") == 0) {
+ TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n");
+ cacheDir = argv[++i];
+ } else if (strcmp(arg, "-arch") == 0) {
+ TERMINATE_IF_LAST_ARG("-arch missing argument\n");
+ archStrs.insert(argv[++i]);
+ } else if (strcmp(arg, "-force") == 0) {
+ force = true;
+ } else if (strcmp(arg, "-sort_by_name") == 0) {
+ //No-op, we always do this now
+ } else if (strcmp(arg, "-universal_boot") == 0) {
+ universal = true;
+ } else {
+ //usage();
+ terminate("unknown option: %s\n", arg);
+ }
+ } else {
+ //usage();
+ terminate("unknown option: %s\n", arg);
+ }
+ }
+
+ setReturnNonZeroOnTerminate();
+ setWarnAnErrorPrefixes("update_dyld_shared_cache: warning: ", "update_dyld_shared_cache failed: ");
+
+ if ( !rootPath.empty() & !overlayPath.empty() )
+ terminate("-root and -overlay cannot be used together\n");
+
+ //FIXME realpath on root and overlays
+
+ if (rootPath.empty()) {
+ rootPath = "/";
+ }
+
+ if ( cacheDir.empty() ) {
+ // write cache file into -root or -overlay directory, if used
+ if ( rootPath != "/" )
+ cacheDir = rootPath + MACOSX_DYLD_SHARED_CACHE_DIR;
+ else if ( !overlayPath.empty() )
+ cacheDir = overlayPath + MACOSX_DYLD_SHARED_CACHE_DIR;
+ else
+ cacheDir = MACOSX_DYLD_SHARED_CACHE_DIR;
+ }
+
+ if (universal) {
+ if ( platform == "iphoneos" ) {
+ terminate("-iPhoneOS and -universal are incompatible\n");
+ }
+ archStrs.clear();
+ }
+
+ if (archStrs.size() == 0) {
+ if ( platform == "iphoneos" ) {
+ terminate("Must specify -arch(s) when using -iPhone\n");
+ }
+ else {
+ if ( universal ) {
+ // <rdar://problem/26182089> -universal_boot should make all possible dyld caches
+ archStrs.insert("i386");
+ archStrs.insert("x86_64");
+ archStrs.insert("x86_64h");
+ }
+ else {
+ // just make caches for this machine
+ archStrs.insert("i386");
+ archStrs.insert(runningOnHaswell() ? "x86_64h" : "x86_64");
+ }
+ }
+ }
+
+ int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+ if (err != 0 && err != EEXIST) {
+ terminate("mkpath_np fail: %d", err);
+ }
+
+ Manifest manifest;
+
+ std::set<std::string> paths;
+
+ if ( !dylibListFile.empty() ) {
+ if ( !parsePathsFile( dylibListFile, paths ) ) {
+ terminate( "could not build intiial paths\n" );
+ }
+ } else if ( !buildInitialPaths( rootPath, overlayPath, paths ) ) {
+ terminate( "could not build intiial paths\n" );
+ }
+
+ manifest.platform = platform;
+ populateManifest( manifest, archStrs, overlayPath, rootPath, paths );
+
+ // If the path we are writing to is trusted then our sources need to be trusted
+ // <rdar://problem/21166835> Can't update the update_dyld_shared_cache on a non-boot volume
+ bool requireDylibsBeRootlessProtected = isProtectedBySIP(cacheDir);
+ manifest.calculateClosure( requireDylibsBeRootlessProtected );
+ manifest.pruneClosure();
+
+ for (const std::string& archStr : archStrs) {
+ std::string cachePath = cacheDir + "/dyld_shared_cache_" + archStr;
+ if ( manifest.sameContentsAsCacheAtPath("localhost", archStr, cachePath) && !force ) {
+ manifest.configurations["localhost"].architectures.erase(archStr);
+ verboseLog("%s is already up to date", cachePath.c_str());
+ }
+ }
+
+ // If caches already up to date, do nothing
+ if ( manifest.configurations["localhost"].architectures.empty() )
+ dumpLogAndExit(false);
+
+ // build caches
+ std::shared_ptr<MultiCacheBuilder> builder = std::make_shared<MultiCacheBuilder>(manifest, false, false, false, false, requireDylibsBeRootlessProtected);
+ builder->buildCaches(cacheDir);
+
+ // Save off spintrace data
+ std::string nuggetRoot = (overlayPath.empty() ? rootPath : overlayPath);
+ (void)dscsym_save_nuggets_for_current_caches(nuggetRoot.c_str());
+
+ // Now that all the build commands have been issued lets put a barrier in after then which can tear down the app after
+ // everything is written.
+ builder->logStats();
+ dumpLogAndExit(false);
+
+ dispatch_main();
+}
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <dirent.h>
+
+#include <vector>
+#include <set>
+#include <map>
+
+#include "mega-dylib-utils.h"
+
+/*
+ This is a compatibility driver to allow us to support migrating to the new cache codebase and format without forcing B&I to alter gencaches.
+ This tool only supports flags used by B&I
+ This intention is for this tool to be removed in the near future and replaced with a tool that is not commandline compatible,
+ but allows shared caches to be built directly out of BuildRecords.
+ */
+
+/*
+ Example commandline:
+ [60042] INFO - Executing Command: /SWE/Teams/DT-SWB/iOS/Binaries/Binaries5/update_dyld_shared_cache/update_dyld_shared_cache-357.0.91~2/Root/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/local/bin/update_dyld_shared_cache
+ -debug
+ -root /BuildRoot//Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.Internal.sdk
+ -dylib_list /tmp/dylibs.K93aInternalOS.60042
+ -cache_dir /tmp/K93aInternalOS.60042
+ -arch armv7
+ -iPhone
+ -dont_map_local_symbols
+ -overlay /SWE/Teams/DT-SWB/iOS/Binaries/Binaries5/XPCService_caches/XPCService_caches-119~94/Root/K93aInternalOS/
+ -overlay /SWE/Teams/DT-SWB/iOS/Binaries/Binaries5/libxpc_caches/libxpc_caches-649~18/Root/K93aInternalOS/
+ */
+
+// record warnings and add to .map file
+static std::vector<std::unique_ptr<const char>> sWarnings;
+
+
+static void write_cache(const char* cachePath, SharedCache& cache) {
+ // create temp file for cache
+ int fd = ::open(cachePath, O_CREAT | O_RDWR | O_TRUNC, 0644);
+ if ( fd == -1 )
+ terminate("can't create temp file %s, errnor=%d", cachePath, errno);
+
+ // try to allocate whole cache file contiguously
+ fstore_t fcntlSpec = { F_ALLOCATECONTIG|F_ALLOCATEALL, F_PEOFPOSMODE, 0, static_cast<off_t>(cache.fileSize()), 0 };
+ ::fcntl(fd, F_PREALLOCATE, &fcntlSpec);
+
+ // write out cache file
+ if ( ::pwrite(fd, cache.buffer().get(), cache.fileSize(), 0) != cache.fileSize() )
+ terminate("write() failure creating cache file, errno=%d", errno);
+
+ // flush to disk and close
+ int result = ::fcntl(fd, F_FULLFSYNC, NULL);
+ if ( result == -1 )
+ warning("fcntl(F_FULLFSYNC) failed with errno=%d for %s\n", errno, cachePath);
+ ::close(fd);
+
+ // write .map file
+ char mapPath[MAXPATHLEN];
+ strlcpy(mapPath, cachePath, MAXPATHLEN);
+ strlcat(mapPath, ".map", MAXPATHLEN);
+ cache.writeCacheMapFile(mapPath);
+
+ // append any warnings encountered
+ if ( !sWarnings.empty() ) {
+ FILE* fmap = ::fopen(mapPath, "a");
+ if ( fmap != NULL ) {
+ fprintf(fmap, "\n\n");
+ for ( std::unique_ptr<const char>& msg : sWarnings ) {
+ fprintf(fmap, "# %s", &*msg);
+ }
+ ::fclose(fmap);
+ }
+ }
+}
+
+int main(int argc, const char* argv[])
+{
+ std::set<ArchPair> onlyArchs;
+ const char *rootPath = NULL;
+ std::vector<const char*> searchPaths;
+ std::vector<std::unique_ptr<DylibProxy>> dylibs;
+ const char* dylibListFile = NULL;
+// bool force = false;
+// bool keepSignatures = false;
+ bool explicitCacheDir = false;
+ bool dontMapLocalSymbols = false;
+ bool verbose = false;
+ bool iPhoneOS = false;
+ const char* cacheDir = NULL;
+ const char* archStr = NULL;
+ ArchPair archPair(CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL);
+
+ // parse command line options
+ for(int i=1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if ( arg[0] == '-' ) {
+ if ( strcmp(arg, "-debug") == 0 ) {
+ verbose = true;
+ }
+ else if ( strcmp(arg, "-dont_map_local_symbols") == 0 ) {
+ dontMapLocalSymbols = true;
+ }
+ else if ( strcmp(arg, "-iPhone") == 0 ) {
+ iPhoneOS = true;
+ }
+ else if ( strcmp(arg, "-dylib_list") == 0 ) {
+ dylibListFile = argv[++i];
+ if ( dylibListFile == NULL )
+ terminate("-dylib_list missing path argument\n");
+ }
+ else if ( (strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0) ) {
+ rootPath = argv[++i];
+ }
+ else if ( strcmp(arg, "-overlay") == 0 ) {
+ const char* path = argv[++i];
+ if ( path == NULL )
+ terminate("-overlay missing path argument\n");
+ searchPaths.push_back(path);
+ }
+ else if ( strcmp(arg, "-cache_dir") == 0 ) {
+ cacheDir = argv[++i];
+ if ( cacheDir == NULL )
+ terminate("-cache_dir missing path argument\n");
+ explicitCacheDir = true;
+ }
+ else if ( strcmp(arg, "-arch") == 0 ) {
+ archStr = argv[++i];
+ archPair = archForString(archStr); // terminates if unknown
+ }
+ else if ( strcmp(arg, "-force") == 0 ) {
+ // ignore. always forced for iOS
+ }
+ else {
+ //usage();
+ terminate("unknown option: %s\n", arg);
+ }
+ }
+ else {
+ //usage();
+ terminate("unknown option: %s\n", arg);
+ }
+ }
+
+ if (!rootPath) {
+ terminate("-root is a required option\n");
+ }
+ if (!iPhoneOS) {
+ terminate("-iPhone is a required option\n");
+ }
+ if (!cacheDir) {
+ terminate("-cache_dir is a required option\n");
+ }
+ if (!dylibListFile) {
+ terminate("-dylib_list is a required option\n");
+ }
+ if (!archStr) {
+ terminate("-arch is a required option\n");
+ }
+
+ char prodCachePath[MAXPATHLEN];
+ char devCachePath[MAXPATHLEN];
+ strcpy(prodCachePath, cacheDir);
+ if ( prodCachePath[strlen(prodCachePath)-1] != '/' )
+ strcat(prodCachePath, "/");
+ strcat(prodCachePath, "dyld_shared_cache_");
+ strcat(prodCachePath, archStr);
+ strcpy(devCachePath, prodCachePath);
+ strcat(devCachePath, ".development");
+
+ verboseLog("developement cache path = %s\n", devCachePath);
+ verboseLog("cache path = %s\n", prodCachePath);
+
+ // create cache dirs if needed
+ char neededDirs[1024];
+ strcpy(neededDirs, prodCachePath);
+ char* lastSlash = strrchr(neededDirs, '/');
+ if ( lastSlash != NULL )
+ lastSlash[1] = '\0';
+ struct stat stat_buf;
+ if ( stat(neededDirs, &stat_buf) != 0 ) {
+ const char* afterSlash = &neededDirs[1];
+ char* slash;
+ while ( (slash = strchr(afterSlash, '/')) != NULL ) {
+ *slash = '\0';
+ ::mkdir(neededDirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+ *slash = '/';
+ afterSlash = slash+1;
+ }
+ }
+
+ std::string dylibOrderFile = toolDir() + "/dylib-order.txt";
+ std::string dirtyDataOrderFile = toolDir() + "/dirty-data-segments-order.txt";
+
+ std::unordered_map<std::string, std::unordered_set<std::string>> dependents;
+ SharedCache devCache(rootPath, searchPaths, dylibListFile, archPair, dylibOrderFile, dirtyDataOrderFile);
+
+ if ( devCache.fileSize() == 0 )
+ terminate("Could not find all necessary dylibs\n");
+
+ devCache.buildUnoptimizedCache();
+
+ SharedCache prodCache = devCache;
+
+ prodCache.optimizeForProduction();
+ devCache.optimizeForDevelopment();
+
+ verboseLog("developement cache size = %llu", devCache.fileSize());
+ verboseLog("developement cache vm size = %llu", devCache.vmSize());
+
+ // optimize cache
+ write_cache(devCachePath, devCache);
+ if ( devCache.vmSize()+align(devCache.vmSize()/200, sharedRegionRegionAlignment(archPair)) > sharedRegionRegionSize(archPair)) {
+ terminate("update_dyld_shared_cache[%u] for arch=%s, shared cache will not fit in shared regions address space. Overflow amount: %llu\n",
+ getpid(), archStr, devCache.vmSize()+align(devCache.vmSize()/200, sharedRegionRegionAlignment(archPair)) - sharedRegionRegionSize(archPair));
+ }
+
+ write_cache(prodCachePath, prodCache);
+
+ return 0;
+}
+
+
+void uniqueWarning(const char* format, ...)
+{
+ va_list list;
+ va_start(list, format);
+ vfprintf(stderr, format, list);
+ va_end(list);
+ fprintf(stderr, "\n");
+}
+
+void log(const char * __restrict format, ...)
+{
+ va_list list;
+ va_start(list, format);
+ vfprintf(stderr, format, list);
+ va_end(list);
+ fprintf(stderr, "\n");
+}
+void verboseLog(const char* format, ...)
+{
+ va_list list;
+ va_start(list, format);
+ vfprintf(stderr, format, list);
+ va_end(list);
+ fprintf(stderr, "\n");
+}
+
+void warning(const char* format, ...)
+{
+ fprintf(stderr, "update_dyld_shared_cache warning: ");
+ va_list list;
+ va_start(list, format);
+ char* msg;
+ vasprintf(&msg, format, list);
+ va_end(list);
+ fprintf(stderr, "%s\n", msg);
+ sWarnings.push_back(std::unique_ptr<const char>(msg));
+}
+
+void terminate(const char* format, ...)
+{
+ fprintf(stderr, "update_dyld_shared_cache error: ");
+ va_list list;
+ va_start(list, format);
+ vfprintf(stderr, format, list);
+ va_end(list);
+ exit(1);
+}
const uint8_t* uuid() const INLINE { return fields.uuid; }
void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); }
- uint64_t cacheType() const INLINE { return E::get64(fields.cacheType); }
- void set_cacheType(uint64_t value) INLINE { E::set64(fields.cacheType, value); }
+ uint64_t cacheType() const INLINE { return E::get64(fields.cacheType); }
+ void set_cacheType(uint64_t value) INLINE { E::set64(fields.cacheType, value); }
+
+ uint32_t branchPoolsOffset() const INLINE { return E::get32(fields.branchPoolsOffset); }
+ void set_branchPoolsOffset(uint32_t value) INLINE { E::set32(fields.branchPoolsOffset, value); }
+
+ uint32_t branchPoolsCount() const INLINE { return E::get32(fields.branchPoolsCount); }
+ void set_branchPoolsCount(uint32_t value) INLINE { E::set32(fields.branchPoolsCount, value); }
+
+ uint64_t accelerateInfoAddr() const INLINE { return E::get64(fields.accelerateInfoAddr); }
+ void set_accelerateInfoAddr(uint64_t value) INLINE { E::set64(fields.accelerateInfoAddr, value); }
+
+ uint64_t accelerateInfoSize() const INLINE { return E::get64(fields.accelerateInfoSize); }
+ void set_accelerateInfoSize(uint64_t value) INLINE { E::set64(fields.accelerateInfoSize, value); }
+
+ uint64_t imagesTextOffset() const INLINE { return E::get64(fields.imagesTextOffset); }
+ void set_imagesTextOffset(uint64_t value) INLINE { E::set64(fields.imagesTextOffset, value); }
+
+ uint64_t imagesTextCount() const INLINE { return E::get64(fields.imagesTextCount); }
+ void set_imagesTextCount(uint64_t value) INLINE { E::set64(fields.imagesTextCount, value); }
private:
dyld_cache_header fields;
dyld_cache_image_info fields;
};
+template <typename E>
+class dyldCacheImageTextInfo {
+public:
+ const uint8_t* uuid() const INLINE { return fields.uuid; }
+ void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); }
+
+ uint64_t loadAddress() const INLINE { return E::get64(fields.loadAddress); }
+ void set_loadAddress(uint64_t value) INLINE { E::set64(fields.loadAddress, value); }
+
+ uint32_t textSegmentSize() const INLINE { return E::get32(fields.textSegmentSize); }
+ void set_textSegmentSize(uint32_t value) INLINE { E::set32(fields.textSegmentSize, value); }
+
+ uint32_t pathOffset() const INLINE { return E::get32(fields.pathOffset); }
+ void set_pathOffset(uint32_t value) INLINE { E::set32(fields.pathOffset, value); }
+
+private:
+ dyld_cache_image_text_info fields;
+};
+
+
+
+template <typename E>
+class dyldCacheImageInfoExtra {
+public:
+ uint64_t exportsTrieAddr() const INLINE { return E::get64(fields.exportsTrieAddr); }
+ void set_exportsTrieAddr(uint64_t value) INLINE { E::set64(fields.exportsTrieAddr, value); }
+
+ uint64_t weakBindingsAddr() const INLINE { return E::get64(fields.weakBindingsAddr); }
+ void set_weakBindingsAddr(uint64_t value) INLINE { E::set64(fields.weakBindingsAddr, value); }
+
+ uint32_t exportsTrieSize() const INLINE { return E::get32(fields.exportsTrieSize); }
+ void set_exportsTrieSize(uint32_t value) INLINE { E::set32(fields.exportsTrieSize, value); }
+
+ uint32_t weakBindingsSize() const INLINE { return E::get32(fields.weakBindingsSize); }
+ void set_weakBindingsSize(uint32_t value) INLINE { E::set32(fields.weakBindingsSize, value); }
+
+ uint32_t dependentsStartArrayIndex() const INLINE { return E::get32(fields.dependentsStartArrayIndex); }
+ void set_dependentsStartArrayIndex(uint32_t value) INLINE { E::set32(fields.dependentsStartArrayIndex, value); }
+
+ uint32_t reExportsStartArrayIndex() const INLINE { return E::get32(fields.reExportsStartArrayIndex); }
+ void set_reExportsStartArrayIndex(uint32_t value) INLINE { E::set32(fields.reExportsStartArrayIndex, value); }
+
+private:
+ dyld_cache_image_info_extra fields;
+};
+
+
+template <typename E>
+class dyldCacheAcceleratorInfo {
+public:
+ uint32_t version() const INLINE { return E::get32(fields.version); }
+ void set_version(uint32_t value) INLINE { E::set32(fields.version, value); }
+
+ uint32_t imageExtrasCount() const INLINE { return E::get32(fields.imageExtrasCount); }
+ void set_imageExtrasCount(uint32_t value) INLINE { E::set32(fields.imageExtrasCount, value); }
+
+ uint32_t imagesExtrasOffset() const INLINE { return E::get32(fields.imagesExtrasOffset); }
+ void set_imagesExtrasOffset(uint32_t value) INLINE { E::set32(fields.imagesExtrasOffset, value); }
+
+ uint32_t bottomUpListOffset() const INLINE { return E::get32(fields.bottomUpListOffset); }
+ void set_bottomUpListOffset(uint32_t value) INLINE { E::set32(fields.bottomUpListOffset, value); }
+
+ uint32_t dylibTrieOffset() const INLINE { return E::get32(fields.dylibTrieOffset); }
+ void set_dylibTrieOffset(uint32_t value) INLINE { E::set32(fields.dylibTrieOffset, value); }
+
+ uint32_t dylibTrieSize() const INLINE { return E::get32(fields.dylibTrieSize); }
+ void set_dylibTrieSize(uint32_t value) INLINE { E::set32(fields.dylibTrieSize, value); }
+
+ uint32_t initializersOffset() const INLINE { return E::get32(fields.initializersOffset); }
+ void set_initializersOffset(uint32_t value) INLINE { E::set32(fields.initializersOffset, value); }
+
+ uint32_t initializersCount() const INLINE { return E::get32(fields.initializersCount); }
+ void set_initializersCount(uint32_t value) INLINE { E::set32(fields.initializersCount, value); }
+
+ uint32_t dofSectionsOffset() const INLINE { return E::get32(fields.dofSectionsOffset); }
+ void set_dofSectionsOffset(uint32_t value) INLINE { E::set32(fields.dofSectionsOffset, value); }
+
+ uint32_t dofSectionsCount() const INLINE { return E::get32(fields.dofSectionsCount); }
+ void set_dofSectionsCount(uint32_t value) INLINE { E::set32(fields.dofSectionsCount, value); }
+
+ uint32_t reExportListOffset() const INLINE { return E::get32(fields.reExportListOffset); }
+ void set_reExportListOffset(uint32_t value) INLINE { E::set32(fields.reExportListOffset, value); }
+
+ uint32_t reExportCount() const INLINE { return E::get32(fields.reExportCount); }
+ void set_reExportCount(uint32_t value) INLINE { E::set32(fields.reExportCount, value); }
+
+ uint32_t depListOffset() const INLINE { return E::get32(fields.depListOffset); }
+ void set_depListOffset(uint32_t value) INLINE { E::set32(fields.depListOffset, value); }
+
+ uint32_t depListCount() const INLINE { return E::get32(fields.depListCount); }
+ void set_depListCount(uint32_t value) INLINE { E::set32(fields.depListCount, value); }
+
+ uint32_t rangeTableOffset() const INLINE { return E::get32(fields.rangeTableOffset); }
+ void set_rangeTableOffset(uint32_t value) INLINE { E::set32(fields.rangeTableOffset, value); }
+
+ uint32_t rangeTableCount() const INLINE { return E::get32(fields.rangeTableCount); }
+ void set_rangeTableCount(uint32_t value) INLINE { E::set32(fields.rangeTableCount, value); }
+
+ uint64_t dyldSectionAddr() const INLINE { return E::get64(fields.dyldSectionAddr); }
+ void set_dyldSectionAddr(uint64_t value) INLINE { E::set64(fields.dyldSectionAddr, value); }
+
+
+private:
+ dyld_cache_accelerator_info fields;
+};
+
+
+template <typename E>
+class dyldCacheAcceleratorInitializer {
+public:
+ uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); }
+ void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); }
+
+ uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); }
+ void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); }
+
+private:
+ dyld_cache_accelerator_initializer fields;
+};
+
+
+template <typename E>
+class dyldCacheAcceleratorRangeEntry {
+public:
+ uint64_t startAddress() const INLINE { return E::get64(fields.startAddress); }
+ void set_startAddress(uint64_t value) INLINE { E::set64(fields.startAddress, value); }
+
+ uint32_t size() const INLINE { return E::get32(fields.size); }
+ void set_size(uint32_t value) INLINE { E::set32(fields.size, value); }
+
+ uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); }
+ void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); }
+
+private:
+ dyld_cache_range_entry fields;
+};
+
+template <typename E>
+class dyldCacheAcceleratorDOFEntry {
+public:
+ uint64_t sectionAddress() const INLINE { return E::get64(fields.sectionAddress); }
+ void set_sectionAddress(uint64_t value) INLINE { E::set64(fields.sectionAddress, value); }
+
+ uint32_t sectionSize() const INLINE { return E::get32(fields.sectionSize); }
+ void set_sectionSize(uint32_t value) INLINE { E::set32(fields.sectionSize, value); }
+
+ uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); }
+ void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); }
+
+private:
+ dyld_cache_accelerator_dof fields;
+};
+
template <typename E>
class dyldCacheSlideInfo {
public:
};
+template <typename E>
+class dyldCacheSlideInfo2 {
+public:
+ uint32_t version() const INLINE { return E::get32(fields.version); }
+ void set_version(uint32_t value) INLINE { E::set32(fields.version, value); }
+
+ uint32_t page_starts_offset() const INLINE { return E::get32(fields.page_starts_offset); }
+ void set_page_starts_offset(uint32_t value) INLINE { E::set32(fields.page_starts_offset, value); }
+
+ uint32_t page_starts_count() const INLINE { return E::get32(fields.page_starts_count); }
+ void set_page_starts_count(uint32_t value) INLINE { E::set32(fields.page_starts_count, value); }
+
+ uint32_t page_extras_offset() const INLINE { return E::get32(fields.page_extras_offset); }
+ void set_page_extras_offset(uint32_t value) INLINE { E::set32(fields.page_extras_offset, value); }
+
+ uint32_t page_extras_count() const INLINE { return E::get32(fields.page_extras_count); }
+ void set_page_extras_count(uint32_t value) INLINE { E::set32(fields.page_extras_count, value); }
+
+ uint32_t page_size() const INLINE { return E::get32(fields.page_size); }
+ void set_page_size(uint32_t value) INLINE { E::set32(fields.page_size, value); }
+
+ uint64_t delta_mask() const INLINE { return E::get64(fields.delta_mask); }
+ void set_delta_mask(uint64_t value) INLINE { E::set64(fields.delta_mask, value); }
+
+ uint64_t value_add() const INLINE { return E::get64(fields.value_add); }
+ void set_value_add(uint64_t value) INLINE { E::set64(fields.value_add, value); }
+
+ uint16_t page_starts(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index]); }
+ void set_page_starts(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index], value); }
+
+ uint16_t page_extras(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index]); }
+ void set_page_extras(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index], value); }
+
+
+private:
+ dyld_cache_slide_info2 fields;
+};
+
+
template <typename E>
class dyldCacheLocalSymbolsInfo {
#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B
#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C
+#define MH_HAS_OBJC 0x40000000
#include "FileAbstraction.hpp"
#include "Architectures.hpp"
return (this->arch < other.arch);
return (this->subtype < other.subtype);
}
+
+ bool operator==(const ArchPair& other) const {
+ return this->arch == other.arch && this->subtype == other.subtype;
+ }
};
}
-static int64_t read_sleb128(const uint8_t*& p, const uint8_t* end)
+inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end)
{
int64_t result = 0;
int bit = 0;
#include <unistd.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
-#include <rootless.h>
#include <vector>
#include <set>
virtual bool isSplitSeg() const = 0;
virtual bool hasSplitSegInfo() const = 0;
virtual bool hasSplitSegInfoV2() const = 0;
- virtual int notTrusted() const = 0;
virtual bool inSharableLocation() const = 0;
virtual bool hasDynamicLookupLinkage() const = 0;
virtual bool hasMainExecutableLookupLinkage() const = 0;
virtual bool isSplitSeg() const;
virtual bool hasSplitSegInfo() const { return fSplitSegInfo != NULL; }
virtual bool hasSplitSegInfoV2() const{ return fHasSplitSegInfoV2; }
- virtual int notTrusted() const { return fRootlessErrno; }
virtual bool inSharableLocation() const { return fShareableLocation; }
virtual bool hasDynamicLookupLinkage() const { return fDynamicLookupLinkage; }
virtual bool hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; }
uint64_t fVMReadOnlySize;
const macho_linkedit_data_command<P>* fSplitSegInfo;
bool fHasSplitSegInfoV2;
- int fRootlessErrno;
bool fShareableLocation;
bool fDynamicLookupLinkage;
bool fMainExecutableLookupLinkage;
template <typename A>
MachOLayout<A>::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid)
- : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fSplitSegInfo(NULL), fHasSplitSegInfoV2(false), fRootlessErrno(0),
+ : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fSplitSegInfo(NULL), fHasSplitSegInfoV2(false),
fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false),
fHasDyldInfo(false), fHasTooManyWritableSegments(false), fDyldInfoExports(NULL)
{
fFileType = mh->filetype();
fArchPair.arch = mh->cputype();
fArchPair.subtype = mh->cpusubtype();
- if ( rootless_check_trusted(path) != 0 && rootless_protected_volume(path) == 1)
- fRootlessErrno = errno;
const macho_dyld_info_command<P>* dyldInfo = NULL;
const macho_symtab_command<P>* symbolTableCmd = NULL;
pointersInData.push_back( mappedAddr);
break;
case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32:
+ if ( adjust == 0 )
+ break;
mappedAddr32 = (uint32_t*)mappedAddr;
value32 = (uint32_t)(toNewAddress - imageStartAddress);
E::set32(*mappedAddr32, value32);
const char* partialStr = &fullStr[strlen(fCummulativeString)];
for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
Edge& e = *it;
- int subStringLen = strlen(e.fSubString);
+ long subStringLen = strlen(e.fSubString);
if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) {
// already have matching edge, go down that path
e.fChild->addSymbol(fullStr, address, flags, other, importName);
return;
}
else {
- for (int i=subStringLen-1; i > 0; --i) {
+ for (long i=subStringLen-1; i > 0; --i) {
if ( strncmp(e.fSubString, partialStr, i) == 0 ) {
// found a common substring, splice in new node
// was A -> C, now A -> B -> C
const char* partialStr = &name[strlen(fCummulativeString)];
for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
Edge& e = *it;
- int subStringLen = strlen(e.fSubString);
+ long subStringLen = strlen(e.fSubString);
if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) {
// already have matching edge, go down that path
e.fChild->addOrderedNodes(name, orderedNodes);
if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
if ( fImportedName != NULL ) {
// nodes with re-export info: size, flags, ordinal, string
- uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + strlen(fImportedName) + 1;
+ uint32_t nodeSize = (uint32_t)(uleb128_size(fFlags) + uleb128_size(fOther) + strlen(fImportedName) + 1);
out.push_back(nodeSize);
append_uleb128(fFlags, out);
append_uleb128(fOther, out);
++edgeStrLen;
}
cummulativeString[curStrOffset+edgeStrLen] = *s++;
- uint32_t childNodeOffet = read_uleb128(s, end);
+ uint32_t childNodeOffet = (uint32_t)read_uleb128(s, end);
processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output);
}
}
// empty trie has no entries
if ( start == end )
return;
- char cummulativeString[4000];
+ char cummulativeString[32000];
std::vector<EntryWithOffset> entries;
processExportNode(start, start, end, cummulativeString, 0, entries);
// to preserve tie layout order, sort by node offset
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2008-2010 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@
- */
-
-#define OBJC_IMAGE_SUPPORTS_GC (1<<1)
-#define OBJC_IMAGE_REQUIRES_GC (1<<2)
-
-template <typename A>
-struct objc_image_info {
- uint32_t version;
- uint32_t flags;
-
- uint32_t getFlags() INLINE { return A::P::E::get32(flags); }
-
- bool supportsGCFlagSet() INLINE { return getFlags() & OBJC_IMAGE_SUPPORTS_GC; }
- bool requiresGCFlagSet() INLINE { return getFlags() & OBJC_IMAGE_REQUIRES_GC; }
-
- void setFlag(uint32_t bits) INLINE { uint32_t old = A::P::E::get32(flags); A::P::E::set32(flags, old | bits); }
- void setOptimizedByDyld() INLINE { setFlag(1<<3); }
-};
-
-template <typename A>
-struct objc_method {
- uint32_t method_name; // SEL
- uint32_t method_types; // char *
- uint32_t method_imp; // IMP
-
- uint32_t getName() const INLINE { return A::P::E::get32(method_name); }
- void setName(uint32_t newName) INLINE { A::P::E::set32(method_name, newName); }
-};
-
-template <typename A>
-struct objc_method_list {
- enum { OBJC_FIXED_UP = 1771 };
- uint32_t obsolete; // struct objc_method_list *
- uint32_t method_count; // int
- struct objc_method<A> method_list[0];
-
- uint32_t getCount() const INLINE { return A::P::E::get32(method_count); }
- void setFixedUp(bool fixed) INLINE { A::P::E::set32(obsolete, fixed ? OBJC_FIXED_UP : 0); }
-};
-
-template <typename A>
-struct objc_class {
- uint32_t isa; // struct objc_class *
- uint32_t super_class; // struct objc_class *
- uint32_t name; // const char *
- uint32_t version; // long
- uint32_t info; // long
- uint32_t instance_size; // long
- uint32_t ivars; // struct objc_ivar_list *
- uint32_t methodList; // struct objc_method_list *
- uint32_t method_cache; // struct objc_cache *
- uint32_t protocols; // objc_protocol_list *
- uint32_t ivar_layout; // const char *
- uint32_t ext; // struct objc_class_ext *
-
- struct objc_class<A> *getIsa(SharedCache<A> *cache) const INLINE { return (struct objc_class<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(isa)); }
- struct objc_method_list<A> *getMethodList(SharedCache<A> *cache) const INLINE { return (struct objc_method_list<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(methodList)); }
-};
-
-template <typename A>
-struct objc_category {
- uint32_t category_name; // char *
- uint32_t class_name; // char *
- uint32_t instance_methods; // struct objc_method_list *
- uint32_t class_methods; // struct objc_method_list *
- uint32_t protocols; // objc_protocol_list *
- uint32_t size; // uint32_t
- uint32_t instance_properties; // struct objc_property_list *
-
- struct objc_method_list<A> *getInstanceMethods(SharedCache<A> *cache) const INLINE { return (struct objc_method_list<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(instance_methods)); }
- struct objc_method_list<A> *getClassMethods(SharedCache<A> *cache) const INLINE { return (struct objc_method_list<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(class_methods)); }
-};
-
-template <typename A>
-struct objc_symtab {
- uint32_t sel_ref_cnt; // unsigned long
- uint32_t refs; // SEL *
- uint16_t cls_def_cnt; // unsigned short
- uint16_t cat_def_cnt; // unsigned short
- uint32_t defs[0]; // void *
-
- uint16_t getClassCount(void) const INLINE { return A::P::E::get16(cls_def_cnt); }
- uint16_t getCategoryCount(void) const INLINE { return A::P::E::get16(cat_def_cnt); }
- struct objc_class<A> *getClass(SharedCache<A> *cache, int index) const INLINE { return (struct objc_class<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(defs[index])); }
- struct objc_category<A> *getCategory(SharedCache<A> *cache, int index) const INLINE { return (struct objc_category<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(defs[getClassCount() + index])); }
-};
-
-template <typename A>
-struct objc_module {
- uint32_t version; // unsigned long
- uint32_t size; // unsigned long
- uint32_t name; // char*
- uint32_t symtab; // Symtab
-
- struct objc_symtab<A> *getSymtab(SharedCache<A> *cache) const INLINE { return (struct objc_symtab<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(symtab)); }
-};
-
-template <typename A>
-struct objc_method_description {
- uint32_t name; // SEL
- uint32_t types; // char *
-
- uint32_t getName() const INLINE { return A::P::E::get32(name); }
- void setName(uint32_t newName) INLINE { A::P::E::set32(name, newName); }
-};
-
-template <typename A>
-struct objc_method_description_list {
- uint32_t count; // int
- struct objc_method_description<A> list[0];
-
- uint32_t getCount() const INLINE { return A::P::E::get32(count); }
-};
-
-template <typename A>
-struct objc_protocol {
- uint32_t isa; // danger! contains strange values!
- uint32_t protocol_name; // const char *
- uint32_t protocol_list; // struct objc_protocol_list
- uint32_t instance_methods; // struct objc_method_description_list *
- uint32_t class_methods; // struct objc_method_description_list *
-
- struct objc_method_description_list<A> *getInstanceMethodDescriptions(SharedCache<A> *cache) const INLINE { return (struct objc_method_description_list<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(instance_methods)); }
- struct objc_method_description_list<A> *getClassMethodDescriptions(SharedCache<A> *cache) const INLINE { return (struct objc_method_description_list<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(class_methods)); }
-};
-
-
-template <typename A, typename V>
-class LegacySelectorUpdater {
-
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
-
- static void visitMethodList(objc_method_list<A> *mlist, V& visitor)
- {
- for (uint32_t m = 0; m < mlist->getCount(); m++) {
- uint64_t oldValue = mlist->method_list[m].getName();
- uint64_t newValue = visitor.visit(oldValue);
- mlist->method_list[m].setName(newValue);
- }
- mlist->setFixedUp(true);
- }
-
- static void visitMethodDescriptionList(objc_method_description_list<A> *mlist, V& visitor)
- {
- for (uint32_t m = 0; m < mlist->getCount(); m++) {
- uint64_t oldValue = mlist->list[m].getName();
- uint64_t newValue = visitor.visit(oldValue);
- mlist->list[m].setName(newValue);
- }
- }
-
-public:
-
- static void update(SharedCache<A>* cache, const macho_header<P>* header,
- V& visitor)
- {
- ArraySection<A, objc_module<A> >
- modules(cache, header, "__OBJC", "__module_info");
- for (uint64_t m = 0; m < modules.count(); m++) {
- objc_symtab<A> *symtab = modules.get(m).getSymtab(cache);
- if (!symtab) continue;
-
- // Method lists in classes
- for (uint64_t c = 0; c < symtab->getClassCount(); c++) {
- objc_class<A> *cls = symtab->getClass(cache, c);
- objc_class<A> *isa = cls->getIsa(cache);
- objc_method_list<A> *mlist;
- if ((mlist = cls->getMethodList(cache))) {
- visitMethodList(mlist, visitor);
- }
- if ((mlist = isa->getMethodList(cache))) {
- visitMethodList(mlist, visitor);
- }
- }
-
- // Method lists from categories
- for (uint64_t c = 0; c < symtab->getCategoryCount(); c++) {
- objc_category<A> *cat = symtab->getCategory(cache, c);
- objc_method_list<A> *mlist;
- if ((mlist = cat->getInstanceMethods(cache))) {
- visitMethodList(mlist, visitor);
- }
- if ((mlist = cat->getClassMethods(cache))) {
- visitMethodList(mlist, visitor);
- }
- }
- }
-
- // Method description lists from protocols
- ArraySection<A, objc_protocol<A> >
- protocols(cache, header, "__OBJC", "__protocol");
- for (uint64_t p = 0; p < protocols.count(); p++) {
- objc_protocol<A>& protocol = protocols.get(p);
- objc_method_description_list<A> *mlist;
- if ((mlist = protocol.getInstanceMethodDescriptions(cache))) {
- visitMethodDescriptionList(mlist, visitor);
- }
- if ((mlist = protocol.getClassMethodDescriptions(cache))) {
- visitMethodDescriptionList(mlist, visitor);
- }
- }
-
- // Message refs
- PointerSection<A, const char *> selrefs(cache, header, "__OBJC", "__message_refs");
- for (pint_t s = 0; s < selrefs.count(); s++) {
- pint_t oldValue = selrefs.getVMAddress(s);
- pint_t newValue = visitor.visit(oldValue);
- selrefs.setVMAddress(s, newValue);
- }
- }
-};
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2008-2010 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 "MachOLayout.hpp"
-#include <iterator>
-#include <deque>
-
-// iterate an entsize-based list
-// typedef entsize_iterator< A, type_t<A>, type_list_t<A> > type_iterator;
-template <typename A, typename T, typename Tlist>
-struct entsize_iterator {
- uint32_t entsize;
- uint32_t index; // keeping track of this saves a divide in operator-
- T* current;
-
- typedef std::random_access_iterator_tag iterator_category;
- typedef T value_type;
- typedef ptrdiff_t difference_type;
- typedef T* pointer;
- typedef T& reference;
-
- entsize_iterator() { }
-
- entsize_iterator(const Tlist& list, uint32_t start = 0)
- : entsize(list.getEntsize()), index(start), current(&list.get(start))
- { }
-
- const entsize_iterator<A,T,Tlist>& operator += (ptrdiff_t count) {
- current = (T*)((uint8_t *)current + count*entsize);
- index += count;
- return *this;
- }
- const entsize_iterator<A,T,Tlist>& operator -= (ptrdiff_t count) {
- current = (T*)((uint8_t *)current - count*entsize);
- index -= count;
- return *this;
- }
- const entsize_iterator<A,T,Tlist> operator + (ptrdiff_t count) const {
- return entsize_iterator(*this) += count;
- }
- const entsize_iterator<A,T,Tlist> operator - (ptrdiff_t count) const {
- return entsize_iterator(*this) -= count;
- }
-
- entsize_iterator<A,T,Tlist>& operator ++ () { *this += 1; return *this; }
- entsize_iterator<A,T,Tlist>& operator -- () { *this -= 1; return *this; }
- entsize_iterator<A,T,Tlist> operator ++ (int) {
- entsize_iterator<A,T,Tlist> result(*this); *this += 1; return result;
- }
- entsize_iterator<A,T,Tlist> operator -- (int) {
- entsize_iterator<A,T,Tlist> result(*this); *this -= 1; return result;
- }
-
- ptrdiff_t operator - (const entsize_iterator<A,T,Tlist>& rhs) const {
- return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index;
- }
-
- T& operator * () { return *current; }
- T& operator * () const { return *current; }
- T& operator -> () { return *current; }
- const T& operator -> () const { return *current; }
-
- operator T& () const { return *current; }
-
- bool operator == (const entsize_iterator<A,T,Tlist>& rhs) {
- return this->current == rhs.current;
- }
- bool operator != (const entsize_iterator<A,T,Tlist>& rhs) {
- return this->current != rhs.current;
- }
-
- bool operator < (const entsize_iterator<A,T,Tlist>& rhs) {
- return this->current < rhs.current;
- }
- bool operator > (const entsize_iterator<A,T,Tlist>& rhs) {
- return this->current > rhs.current;
- }
-};
-
-template <typename A>
-class objc_header_info_t {
-
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
-
- pint_t next; // objc_header_info *
- pint_t mhdr; // mach_header or mach_header_64
- pint_t info; // objc_image_info *
- pint_t fname; // const char *
- bool loaded;
- bool inSharedCache;
- bool allClassesRealized;
-
-public:
- objc_header_info_t(SharedCache<A>* cache, const macho_header<P>* mh)
- : next(0),
- mhdr(0),
- info(0),
- fname(0),
- loaded(0),
- allClassesRealized(0)
- {
- A::P::setP(mhdr, cache->VMAddressForMappedAddress(mh));
- const macho_section<P>* sect = mh->getSection("__DATA", "__objc_imageinfo");
- if (sect) A::P::setP(info, sect->addr());
-
- // can't set fname because dyld sometimes edits it
- }
-
- void addPointers(std::vector<void*>& pointersToAdd) {
- pointersToAdd.push_back(&mhdr);
- if (info) pointersToAdd.push_back(&info);
- }
-
- uint64_t header_vmaddr() const { return mhdr; }
-};
-
-template <typename A> class objc_method_list_t; // forward reference
-
-template <typename A>
-class objc_method_t {
- typename A::P::uint_t name; // SEL
- typename A::P::uint_t types; // const char *
- typename A::P::uint_t imp; // IMP
- friend class objc_method_list_t<A>;
-public:
- typename A::P::uint_t getName() const { return A::P::getP(name); }
- void setName(typename A::P::uint_t newName) { A::P::setP(name, newName); }
-
- struct SortBySELAddress :
- public std::binary_function<const objc_method_t<A>&,
- const objc_method_t<A>&, bool>
- {
- bool operator() (const objc_method_t<A>& lhs,
- const objc_method_t<A>& rhs)
- {
- return lhs.getName() < rhs.getName();
- }
- };
-};
-
-template <typename A>
-class objc_method_list_t {
- uint32_t entsize;
- uint32_t count;
- objc_method_t<A> first;
-
- void* operator new (size_t, void* buf) { return buf; }
-
-public:
-
- typedef entsize_iterator< A, objc_method_t<A>, objc_method_list_t<A> > method_iterator;
-
- uint32_t getCount() const { return A::P::E::get32(count); }
-
- uint32_t getEntsize() const {return A::P::E::get32(entsize)&~(uint32_t)3;}
-
- objc_method_t<A>& get(uint32_t i) const { return *(objc_method_t<A> *)((uint8_t *)&first + i * getEntsize()); }
-
- uint32_t byteSize() const {
- return byteSizeForCount(getCount(), getEntsize());
- }
-
- static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_method_t<A>)) {
- return sizeof(objc_method_list_t<A>) - sizeof(objc_method_t<A>) + c*e;
- }
-
- method_iterator begin() { return method_iterator(*this, 0); }
- method_iterator end() { return method_iterator(*this, getCount()); }
- const method_iterator begin() const { return method_iterator(*this, 0); }
- const method_iterator end() const { return method_iterator(*this, getCount()); }
-
- void setFixedUp() { A::P::E::set32(entsize, getEntsize() | 3); }
-
- void getPointers(std::set<void*>& pointersToRemove) {
- for(method_iterator it = begin(); it != end(); ++it) {
- objc_method_t<A>& entry = *it;
- pointersToRemove.insert(&(entry.name));
- pointersToRemove.insert(&(entry.types));
- pointersToRemove.insert(&(entry.imp));
- }
- }
-
- static void addPointers(uint8_t* methodList, std::vector<void*>& pointersToAdd) {
- objc_method_list_t<A>* mlist = (objc_method_list_t<A>*)methodList;
- for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) {
- objc_method_t<A>& entry = *it;
- pointersToAdd.push_back(&(entry.name));
- pointersToAdd.push_back(&(entry.types));
- pointersToAdd.push_back(&(entry.imp));
- }
- }
-
- static objc_method_list_t<A>* newMethodList(size_t newCount, uint32_t newEntsize) {
- void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
- return new (buf) objc_method_list_t<A>(newCount, newEntsize);
- }
-
- void operator delete(void * p) {
- ::free(p);
- }
-
- objc_method_list_t(uint32_t newCount,
- uint32_t newEntsize = sizeof(objc_method_t<A>))
- : entsize(newEntsize), count(newCount)
- { }
-
-private:
- // use newMethodList instead
- void* operator new (size_t);
-};
-
-
-// Ivar offset variables are 64-bit on x86_64 and 32-bit everywhere else.
-
-template <typename A>
-class objc_ivar_offset_t {
- typedef typename A::P::uint_t pint_t;
- typename A::P::uint_t ptr; // uint32_t *
-
- uint32_t& offset(SharedCache<A> *cache) const { return *(uint32_t *)cache->mappedAddressForVMAddress(A::P::getP(ptr)); }
-
-public:
- bool hasOffset() const { return A::P::getP(ptr) != 0; }
- pint_t getOffset(SharedCache<A> *cache) const { return A::P::E::get32(offset(cache)); }
- void setOffset(SharedCache<A> *cache, pint_t newOffset) { A::P::E::set32(offset(cache), newOffset); }
-};
-
-template <>
-class objc_ivar_offset_t<x86_64> {
- typedef x86_64 A;
- typedef typename A::P::uint_t pint_t;
- typename A::P::uint_t ptr; // uint64_t *
-
- uint64_t& offset(SharedCache<A> *cache) const { return *(uint64_t *)cache->mappedAddressForVMAddress(A::P::getP(ptr)); }
-
-public:
- bool hasOffset() const { return A::P::getP(ptr) != 0; }
- pint_t getOffset(SharedCache<A> *cache) const { return A::P::E::get64(offset(cache)); }
- void setOffset(SharedCache<A> *cache, pint_t newOffset) { A::P::E::set64(offset(cache), newOffset); }
-};
-
-template <typename A>
-class objc_ivar_t {
- typedef typename A::P::uint_t pint_t;
- objc_ivar_offset_t<A> offset; // uint32_t * (uint64_t * on x86_64)
- typename A::P::uint_t name; // const char *
- typename A::P::uint_t type; // const char *
- uint32_t alignment;
- uint32_t size;
-
-public:
- const char * getName(SharedCache<A> *cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
-
- bool hasOffset() const { return offset.hasOffset(); }
- pint_t getOffset(SharedCache<A> *cache) const { return offset.getOffset(cache); }
- void setOffset(SharedCache<A> *cache, pint_t newOffset) { offset.setOffset(cache, newOffset); }
-
- uint32_t getAlignment()
- {
- uint32_t a = A::P::E::get32(alignment);
- return a == (uint32_t)-1 ? sizeof(typename A::P::uint_t) : 1<<a;
- }
-};
-
-template <typename A>
-class objc_ivar_list_t {
- typedef typename A::P::uint_t pint_t;
- uint32_t entsize;
- uint32_t count;
- objc_ivar_t<A> first;
-
- void* operator new (size_t, void* buf) { return buf; }
-
-public:
-
- typedef entsize_iterator< A, objc_ivar_t<A>, objc_ivar_list_t<A> > ivar_iterator;
-
- uint32_t getCount() const { return A::P::E::get32(count); }
-
- uint32_t getEntsize() const { return A::P::E::get32(entsize); }
-
- objc_ivar_t<A>& get(pint_t i) const { return *(objc_ivar_t<A> *)((uint8_t *)&first + i * A::P::E::get32(entsize)); }
-
- uint32_t byteSize() const {
- return byteSizeForCount(getCount(), getEntsize());
- }
-
- static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_ivar_t<A>)) {
- return sizeof(objc_ivar_list_t<A>) - sizeof(objc_ivar_t<A>) + c*e;
- }
-
- ivar_iterator begin() { return ivar_iterator(*this, 0); }
- ivar_iterator end() { return ivar_iterator(*this, getCount()); }
- const ivar_iterator begin() const { return ivar_iterator(*this, 0); }
- const ivar_iterator end() const { return ivar_iterator(*this, getCount()); }
-
- static objc_ivar_list_t<A>* newIvarList(size_t newCount, uint32_t newEntsize) {
- void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
- return new (buf) objc_ivar_list_t<A>(newCount, newEntsize);
- }
-
- void operator delete(void * p) {
- ::free(p);
- }
-
- objc_ivar_list_t(uint32_t newCount,
- uint32_t newEntsize = sizeof(objc_ivar_t<A>))
- : entsize(newEntsize), count(newCount)
- { }
-private:
- // use newIvarList instead
- void* operator new (size_t);
-};
-
-
-template <typename A> class objc_property_list_t; // forward
-
-template <typename A>
-class objc_property_t {
- typename A::P::uint_t name;
- typename A::P::uint_t attributes;
- friend class objc_property_list_t<A>;
-public:
-
- const char * getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
-
- const char * getAttributes(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(attributes)); }
-};
-
-template <typename A>
-class objc_property_list_t {
- uint32_t entsize;
- uint32_t count;
- objc_property_t<A> first;
-
- void* operator new (size_t, void* buf) { return buf; }
-
-public:
-
- typedef entsize_iterator< A, objc_property_t<A>, objc_property_list_t<A> > property_iterator;
-
- uint32_t getCount() const { return A::P::E::get32(count); }
-
- uint32_t getEntsize() const { return A::P::E::get32(entsize); }
-
- objc_property_t<A>& get(uint32_t i) const { return *(objc_property_t<A> *)((uint8_t *)&first + i * getEntsize()); }
-
- uint32_t byteSize() const {
- return byteSizeForCount(getCount(), getEntsize());
- }
-
- static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_property_t<A>)) {
- return sizeof(objc_property_list_t<A>) - sizeof(objc_property_t<A>) + c*e;
- }
-
- property_iterator begin() { return property_iterator(*this, 0); }
- property_iterator end() { return property_iterator(*this, getCount()); }
- const property_iterator begin() const { return property_iterator(*this, 0); }
- const property_iterator end() const { return property_iterator(*this, getCount()); }
-
- void getPointers(std::set<void*>& pointersToRemove) {
- for(property_iterator it = begin(); it != end(); ++it) {
- objc_property_t<A>& entry = *it;
- pointersToRemove.insert(&(entry.name));
- pointersToRemove.insert(&(entry.attributes));
- }
- }
-
- static void addPointers(uint8_t* propertyList, std::vector<void*>& pointersToAdd) {
- objc_property_list_t<A>* plist = (objc_property_list_t<A>*)propertyList;
- for(property_iterator it = plist->begin(); it != plist->end(); ++it) {
- objc_property_t<A>& entry = *it;
- pointersToAdd.push_back(&(entry.name));
- pointersToAdd.push_back(&(entry.attributes));
- }
- }
-
- static objc_property_list_t<A>* newPropertyList(size_t newCount, uint32_t newEntsize) {
- void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
- return new (buf) objc_property_list_t<A>(newCount, newEntsize);
- }
-
- void operator delete(void * p) {
- ::free(p);
- }
-
- objc_property_list_t(uint32_t newCount,
- uint32_t newEntsize = sizeof(objc_property_t<A>))
- : entsize(newEntsize), count(newCount)
- { }
-private:
- // use newPropertyList instead
- void* operator new (size_t);
-};
-
-
-template <typename A> class objc_protocol_list_t; // forward reference
-
-template <typename A>
-class objc_protocol_t {
- typedef typename A::P::uint_t pint_t;
-
- pint_t isa;
- pint_t name;
- pint_t protocols;
- pint_t instanceMethods;
- pint_t classMethods;
- pint_t optionalInstanceMethods;
- pint_t optionalClassMethods;
- pint_t instanceProperties;
- uint32_t size;
- uint32_t flags;
- pint_t extendedMethodTypes;
- pint_t demangledName;
-
-public:
- pint_t getIsaVMAddr() const { return A::P::getP(isa); }
- pint_t setIsaVMAddr(pint_t newIsa) { A::P::setP(isa, newIsa); }
-
- const char *getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
-
- uint32_t getSize() const { return A::P::E::get32(size); }
- void setSize(uint32_t newSize) { A::P::E::set32(size, newSize); }
-
- uint32_t getFlags() const { return A::P::E::get32(flags); }
-
- void setFixedUp() { A::P::E::set32(flags, getFlags() | (1<<30)); }
-
- objc_protocol_list_t<A> *getProtocols(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(protocols)); }
-
- objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
-
- objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
-
- objc_method_list_t<A> *getOptionalInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalInstanceMethods)); }
-
- objc_method_list_t<A> *getOptionalClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalClassMethods)); }
-
- objc_property_list_t<A> *getInstanceProperties(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceProperties)); }
-
- pint_t *getExtendedMethodTypes(SharedCache<A>* cache) const {
- if (getSize() < offsetof(objc_protocol_t<A>, extendedMethodTypes) + sizeof(extendedMethodTypes)) {
- return NULL;
- }
- return (pint_t *)cache->mappedAddressForVMAddress(A::P::getP(extendedMethodTypes));
- }
-
- const char *getDemangledName(SharedCache<A>* cache) const {
- if (sizeof(*this) < offsetof(objc_protocol_t<A>, demangledName) + sizeof(demangledName)) {
- return NULL;
- }
- return (const char *)cache->mappedAddressForVMAddress(A::P::getP(demangledName));
- }
-
- void setDemangledName(SharedCache<A>* cache, const char *newName) {
- if (sizeof(*this) < offsetof(objc_protocol_t<A>, demangledName) + sizeof(demangledName)) {
- throw "objc protocol has the wrong size";
- }
- A::P::setP(demangledName, cache->VMAddressForMappedAddress(newName));
- }
-
- void addPointers(std::vector<void*>& pointersToAdd)
- {
- pointersToAdd.push_back(&isa);
- pointersToAdd.push_back(&name);
- if (protocols) pointersToAdd.push_back(&protocols);
- if (instanceMethods) pointersToAdd.push_back(&instanceMethods);
- if (classMethods) pointersToAdd.push_back(&classMethods);
- if (optionalInstanceMethods) pointersToAdd.push_back(&optionalInstanceMethods);
- if (optionalClassMethods) pointersToAdd.push_back(&optionalClassMethods);
- if (instanceProperties) pointersToAdd.push_back(&instanceProperties);
- if (extendedMethodTypes) pointersToAdd.push_back(&extendedMethodTypes);
- if (demangledName) pointersToAdd.push_back(&demangledName);
- }
-};
-
-template <typename A>
-class objc_protocol_list_t {
- typedef typename A::P::uint_t pint_t;
- pint_t count;
- pint_t list[0];
-
- void* operator new (size_t, void* buf) { return buf; }
-
-public:
-
- pint_t getCount() const { return A::P::getP(count); }
-
- pint_t getVMAddress(pint_t i) {
- return A::P::getP(list[i]);
- }
-
- objc_protocol_t<A>* get(SharedCache<A>* cache, pint_t i) {
- return (objc_protocol_t<A>*)cache->mappedAddressForVMAddress(getVMAddress(i));
- }
-
- void setVMAddress(pint_t i, pint_t protoVMAddr) {
- A::P::setP(list[i], protoVMAddr);
- }
-
- void set(SharedCache<A>* cache, pint_t i, objc_protocol_t<A>* proto) {
- setVMAddress(i, cache->VMAddressForMappedAddress(proto));
- }
-
- uint32_t byteSize() const {
- return byteSizeForCount(getCount());
- }
- static uint32_t byteSizeForCount(pint_t c) {
- return sizeof(objc_protocol_list_t<A>) + c*sizeof(pint_t);
- }
-
- void getPointers(std::set<void*>& pointersToRemove) {
- for(int i=0 ; i < count; ++i) {
- pointersToRemove.insert(&list[i]);
- }
- }
-
- static void addPointers(uint8_t* protocolList, std::vector<void*>& pointersToAdd) {
- objc_protocol_list_t<A>* plist = (objc_protocol_list_t<A>*)protocolList;
- for(int i=0 ; i < plist->count; ++i) {
- pointersToAdd.push_back(&plist->list[i]);
- }
- }
-
- static objc_protocol_list_t<A>* newProtocolList(pint_t newCount) {
- void *buf = ::calloc(byteSizeForCount(newCount), 1);
- return new (buf) objc_protocol_list_t<A>(newCount);
- }
-
- void operator delete(void * p) {
- ::free(p);
- }
-
- objc_protocol_list_t(uint32_t newCount) : count(newCount) { }
-private:
- // use newProtocolList instead
- void* operator new (size_t);
-};
-
-
-template <typename A>
-class objc_class_data_t {
- uint32_t flags;
- uint32_t instanceStart;
- // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout
- // on 64-bit archs, but no padding on 32-bit archs.
- // This union is a way to model that.
- union {
- uint32_t instanceSize;
- typename A::P::uint_t pad;
- } instanceSize;
- typename A::P::uint_t ivarLayout;
- typename A::P::uint_t name;
- typename A::P::uint_t baseMethods;
- typename A::P::uint_t baseProtocols;
- typename A::P::uint_t ivars;
- typename A::P::uint_t weakIvarLayout;
- typename A::P::uint_t baseProperties;
-
-public:
- bool isMetaClass() { return A::P::E::get32(flags) & 1; }
-
- uint32_t getInstanceStart() { return A::P::E::get32(instanceStart); }
- void setInstanceStart(uint32_t newStart) { A::P::E::set32(instanceStart, newStart); }
-
- uint32_t getInstanceSize() { return A::P::E::get32(instanceSize.instanceSize); }
- void setInstanceSize(uint32_t newSiz) { A::P::E::set32(instanceSize.instanceSize, newSiz); }
-
- objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseMethods)); }
-
- objc_protocol_list_t<A> *getProtocolList(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseProtocols)); }
-
- objc_ivar_list_t<A> *getIvarList(SharedCache<A>* cache) const { return (objc_ivar_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(ivars)); }
-
- objc_property_list_t<A> *getPropertyList(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseProperties)); }
-
- const char * getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
-
- void setMethodList(SharedCache<A>* cache, objc_method_list_t<A>* mlist) {
- A::P::setP(baseMethods, cache->VMAddressForMappedAddress(mlist));
- }
-
- void setProtocolList(SharedCache<A>* cache, objc_protocol_list_t<A>* protolist) {
- A::P::setP(baseProtocols, cache->VMAddressForMappedAddress(protolist));
- }
-
- void setPropertyList(SharedCache<A>* cache, objc_property_list_t<A>* proplist) {
- A::P::setP(baseProperties, cache->VMAddressForMappedAddress(proplist));
- }
-
- void addMethodListPointer(std::vector<void*>& pointersToAdd) {
- pointersToAdd.push_back(&this->baseMethods);
- }
-
- void addPropertyListPointer(std::vector<void*>& pointersToAdd) {
- pointersToAdd.push_back(&this->baseProperties);
- }
-
- void addProtocolListPointer(std::vector<void*>& pointersToAdd) {
- pointersToAdd.push_back(&this->baseProtocols);
- }
-};
-
-template <typename A>
-class objc_class_t {
- typename A::P::uint_t isa;
- typename A::P::uint_t superclass;
- typename A::P::uint_t method_cache;
- typename A::P::uint_t vtable;
- typename A::P::uint_t data;
-
-public:
- bool isMetaClass(SharedCache<A>* cache) const { return getData(cache)->isMetaClass(); }
-
- objc_class_t<A> *getIsa(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(isa)); }
-
- objc_class_t<A> *getSuperclass(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(superclass)); }
-
- objc_class_data_t<A> *getData(SharedCache<A>* cache) const { return (objc_class_data_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(data)); }
-
- objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return getData(cache)->getMethodList(cache); }
-
- objc_protocol_list_t<A> *getProtocolList(SharedCache<A>* cache) const { return getData(cache)->getProtocolList(cache); }
-
- objc_property_list_t<A> *getPropertyList(SharedCache<A>* cache) const { return getData(cache)->getPropertyList(cache); }
-
- const char * getName(SharedCache<A>* cache) const {
- return getData(cache)->getName(cache);
- }
-
- void setMethodList(SharedCache<A>* cache, objc_method_list_t<A>* mlist) {
- getData(cache)->setMethodList(cache, mlist);
- }
-
- void setProtocolList(SharedCache<A>* cache, objc_protocol_list_t<A>* protolist) {
- getData(cache)->setProtocolList(cache, protolist);
- }
-
- void setPropertyList(SharedCache<A>* cache, objc_property_list_t<A>* proplist) {
- getData(cache)->setPropertyList(cache, proplist);
- }
-
- void addMethodListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
- getData(cache)->addMethodListPointer(pointersToAdd);
- }
-
- void addPropertyListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
- getData(cache)->addPropertyListPointer(pointersToAdd);
- }
-
- void addProtocolListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
- getData(cache)->addProtocolListPointer(pointersToAdd);
- }
-
-};
-
-
-
-template <typename A>
-class objc_category_t {
- typename A::P::uint_t name;
- typename A::P::uint_t cls;
- typename A::P::uint_t instanceMethods;
- typename A::P::uint_t classMethods;
- typename A::P::uint_t protocols;
- typename A::P::uint_t instanceProperties;
-
-public:
-
- const char * getName(SharedCache<A> *cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
-
- objc_class_t<A> *getClass(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(cls)); }
-
- objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
-
- objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
-
- objc_protocol_list_t<A> *getProtocols(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(protocols)); }
-
- objc_property_list_t<A> *getInstanceProperties(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceProperties)); }
-
- void getPointers(std::set<void*>& pointersToRemove) {
- pointersToRemove.insert(&name);
- pointersToRemove.insert(&cls);
- pointersToRemove.insert(&instanceMethods);
- pointersToRemove.insert(&classMethods);
- pointersToRemove.insert(&protocols);
- pointersToRemove.insert(&instanceProperties);
- }
-
-
-};
-
-template <typename A>
-class objc_message_ref_t {
- typename A::P::uint_t imp;
- typename A::P::uint_t sel;
-
-public:
- typename A::P::uint_t getName() const { return A::P::getP(sel); }
-
- void setName(typename A::P::uint_t newName) { A::P::setP(sel, newName); }
-};
-
-// Call visitor.visitIvar() on every ivar in a given class.
-template <typename A, typename V>
-class IvarWalker {
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
- V& ivarVisitor;
-public:
-
- IvarWalker(V& visitor) : ivarVisitor(visitor) { }
-
- void walk(SharedCache<A>* cache, const macho_header<P>* header, objc_class_t<A> *cls)
- {
- objc_class_data_t<A> *data = cls->getData(cache);
- objc_ivar_list_t<A> *ivars = data->getIvarList(cache);
- if (ivars) {
- for (pint_t i = 0; i < ivars->getCount(); i++) {
- objc_ivar_t<A>& ivar = ivars->get(i);
- //fprintf(stderr, "visiting ivar: %s\n", ivar.getName(cache));
- ivarVisitor.visitIvar(cache, header, cls, &ivar);
- }
- } else {
- //fprintf(stderr, "no ivars\n");
- }
- }
-
- void visitClass(SharedCache<A>* cache, const macho_header<P>* header, objc_class_t<A> *cls)
- {
- walk(cache, header, cls);
- }
-};
-
-// Call visitor.visitClass() on every class.
-template <typename A, typename V>
-class ClassWalker {
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
- V& classVisitor;
-public:
-
- ClassWalker(V& visitor) : classVisitor(visitor) { }
-
- void walk(SharedCache<A>* cache, const macho_header<P>* header)
- {
- PointerSection<A, objc_class_t<A> *>
- classes(cache, header, "__DATA", "__objc_classlist");
-
- for (pint_t i = 0; i < classes.count(); i++) {
- objc_class_t<A> *cls = classes.get(i);
- //fprintf(stderr, "visiting class: %s\n", cls->getName(cache));
- if (cls) classVisitor.visitClass(cache, header, cls);
- }
- }
-};
-
-
-// Call visitor.visitProtocol() on every protocol.
-template <typename A, typename V>
-class ProtocolWalker {
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
- V& protocolVisitor;
-public:
-
- ProtocolWalker(V& visitor) : protocolVisitor(visitor) { }
-
- void walk(SharedCache<A>* cache, const macho_header<P>* header)
- {
- PointerSection<A, objc_protocol_t<A> *>
- protocols(cache, header, "__DATA", "__objc_protolist");
-
- for (pint_t i = 0; i < protocols.count(); i++) {
- objc_protocol_t<A> *proto = protocols.get(i);
- protocolVisitor.visitProtocol(cache, header, proto);
- }
- }
-};
-
-
-// Call visitor.visitProtocolReference() on every protocol.
-template <typename A, typename V>
-class ProtocolReferenceWalker {
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
- V& mVisitor;
-
- void visitProtocolList(SharedCache<A>* cache,
- objc_protocol_list_t<A>* protolist)
- {
- if (!protolist) return;
- for (pint_t i = 0; i < protolist->getCount(); i++) {
- pint_t oldValue = protolist->getVMAddress(i);
- pint_t newValue = mVisitor.visitProtocolReference(cache, oldValue);
- protolist->setVMAddress(i, newValue);
- }
- }
-
- friend class ClassWalker<A, ProtocolReferenceWalker<A, V>>;
- void visitClass(SharedCache<A>* cache, const macho_header<P>*,
- objc_class_t<A>* cls)
- {
- visitProtocolList(cache, cls->getProtocolList(cache));
- visitProtocolList(cache, cls->getIsa(cache)->getProtocolList(cache));
- }
-
-public:
-
- ProtocolReferenceWalker(V& visitor) : mVisitor(visitor) { }
- void walk(SharedCache<A>* cache, const macho_header<P>* header)
- {
- // @protocol expressions
- PointerSection<A, objc_protocol_t<A> *>
- protorefs(cache, header, "__DATA", "__objc_protorefs");
- for (pint_t i = 0; i < protorefs.count(); i++) {
- pint_t oldValue = protorefs.getVMAddress(i);
- pint_t newValue = mVisitor.visitProtocolReference(cache, oldValue);
- protorefs.setVMAddress(i, newValue);
- }
-
- // protocol lists in classes
- ClassWalker<A, ProtocolReferenceWalker<A, V>> classes(*this);
- classes.walk(cache, header);
-
- // protocol lists in protocols
- // __objc_protolists itself is NOT updated
- PointerSection<A, objc_protocol_t<A> *>
- protocols(cache, header, "__DATA", "__objc_protolist");
- for (pint_t i = 0; i < protocols.count(); i++) {
- objc_protocol_t<A>* proto = protocols.get(i);
- visitProtocolList(cache, proto->getProtocols(cache));
- // not recursive: every old protocol object
- // must be in some protolist section somewhere
- }
- }
-};
-
-
-// Call visitor.visitMethodList(mlist) on every
-// class and category method list in a header.
-// Call visitor.visitProtocolMethodList(mlist, typelist) on every
-// protocol method list in a header.
-template <typename A, typename V>
-class MethodListWalker {
-
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
-
- V& mVisitor;
-
-public:
-
- MethodListWalker(V& visitor) : mVisitor(visitor) { }
-
- void walk(SharedCache<A>* cache, const macho_header<P>* header)
- {
- // Method lists in classes
- PointerSection<A, objc_class_t<A> *>
- classes(cache, header, "__DATA", "__objc_classlist");
-
- for (pint_t i = 0; i < classes.count(); i++) {
- objc_class_t<A> *cls = classes.get(i);
- objc_method_list_t<A> *mlist;
- if ((mlist = cls->getMethodList(cache))) {
- mVisitor.visitMethodList(mlist);
- }
- if ((mlist = cls->getIsa(cache)->getMethodList(cache))) {
- mVisitor.visitMethodList(mlist);
- }
- }
-
- // Method lists from categories
- PointerSection<A, objc_category_t<A> *>
- cats(cache, header, "__DATA", "__objc_catlist");
- for (pint_t i = 0; i < cats.count(); i++) {
- objc_category_t<A> *cat = cats.get(i);
- objc_method_list_t<A> *mlist;
- if ((mlist = cat->getInstanceMethods(cache))) {
- mVisitor.visitMethodList(mlist);
- }
- if ((mlist = cat->getClassMethods(cache))) {
- mVisitor.visitMethodList(mlist);
- }
- }
-
- // Method description lists from protocols
- PointerSection<A, objc_protocol_t<A> *>
- protocols(cache, header, "__DATA", "__objc_protolist");
- for (pint_t i = 0; i < protocols.count(); i++) {
- objc_protocol_t<A> *proto = protocols.get(i);
- objc_method_list_t<A> *mlist;
- pint_t *typelist = proto->getExtendedMethodTypes(cache);
-
- if ((mlist = proto->getInstanceMethods(cache))) {
- mVisitor.visitProtocolMethodList(mlist, typelist);
- if (typelist) typelist += mlist->getCount();
- }
- if ((mlist = proto->getClassMethods(cache))) {
- mVisitor.visitProtocolMethodList(mlist, typelist);
- if (typelist) typelist += mlist->getCount();
- }
- if ((mlist = proto->getOptionalInstanceMethods(cache))) {
- mVisitor.visitProtocolMethodList(mlist, typelist);
- if (typelist) typelist += mlist->getCount();
- }
- if ((mlist = proto->getOptionalClassMethods(cache))) {
- mVisitor.visitProtocolMethodList(mlist, typelist);
- if (typelist) typelist += mlist->getCount();
- }
- }
- }
-};
-
-
-// Update selector references. The visitor performs recording and uniquing.
-template <typename A, typename V>
-class SelectorOptimizer {
-
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
-
- V& mVisitor;
-
- friend class MethodListWalker< A, SelectorOptimizer<A,V> >;
- void visitMethodList(objc_method_list_t<A> *mlist)
- {
- // Gather selectors. Update method names.
- for (pint_t m = 0; m < mlist->getCount(); m++) {
- pint_t oldValue = mlist->get(m).getName();
- pint_t newValue = mVisitor.visit(oldValue);
- mlist->get(m).setName(newValue);
- }
- // Do not setFixedUp: the methods are not yet sorted.
- }
-
- void visitProtocolMethodList(objc_method_list_t<A> *mlist, pint_t *types)
- {
- visitMethodList(mlist);
- }
-
-public:
-
- SelectorOptimizer(V& visitor) : mVisitor(visitor) { }
-
- void optimize(SharedCache<A>* cache, const macho_header<P>* header)
- {
- // method lists in classes, categories, and protocols
- MethodListWalker< A, SelectorOptimizer<A,V> > mw(*this);
- mw.walk(cache, header);
-
- // @selector references
- PointerSection<A, const char *>
- selrefs(cache, header, "__DATA", "__objc_selrefs");
- for (pint_t i = 0; i < selrefs.count(); i++) {
- pint_t oldValue = selrefs.getVMAddress(i);
- pint_t newValue = mVisitor.visit(oldValue);
- selrefs.setVMAddress(i, newValue);
- }
-
- // message references
- ArraySection<A, objc_message_ref_t<A> >
- msgrefs(cache, header, "__DATA", "__objc_msgrefs");
- for (pint_t i = 0; i < msgrefs.count(); i++) {
- objc_message_ref_t<A>& msg = msgrefs.get(i);
- pint_t oldValue = msg.getName();
- pint_t newValue = mVisitor.visit(oldValue);
- msg.setName(newValue);
- }
- }
-};
-
-
-template <typename A>
-static bool headerSupportsGC(SharedCache<A>* cache,
- const macho_header<typename A::P>* header)
-{
- const macho_section<typename A::P> *imageInfoSection =
- header->getSection("__DATA", "__objc_imageinfo");
- if (imageInfoSection) {
- objc_image_info<A> *info = (objc_image_info<A> *)
- cache->mappedAddressForVMAddress(imageInfoSection->addr());
- return (info->supportsGCFlagSet() || info->requiresGCFlagSet());
- }
-
- return false;
-}
-
-
-// Gather the set of GC-supporting classes
-template <typename A>
-class GCClassSet {
- typedef typename A::P P;
-
- std::set<objc_class_t<A>*> fGCClasses;
-
-public:
- bool contains(objc_class_t<A>* cls) const {
- return fGCClasses.count(cls) != 0;
- }
-
- void visitClass(SharedCache<A>* cache, const macho_header<P>* header, objc_class_t<A> *cls)
- {
- fGCClasses.insert(cls);
- }
-};
-
-
-// Update selector references. The visitor performs recording and uniquing.
-template <typename A>
-class IvarOffsetOptimizer {
- typedef typename A::P P;
-
- uint32_t slide;
- uint32_t maxAlignment;
-
- uint32_t fOptimized;
-
- GCClassSet<A> fGCClasses;
-
-public:
-
- IvarOffsetOptimizer() : fOptimized(0) { }
-
- size_t optimized() const { return fOptimized; }
-
- // dual purpose ivar visitor function
- // if slide!=0 then slides the ivar by that amount, otherwise computes maxAlignment
- void visitIvar(SharedCache<A>* cache, const macho_header<P>* /*unused, may be NULL*/, objc_class_t<A> *cls, objc_ivar_t<A> *ivar)
- {
- if (slide == 0) {
- uint32_t alignment = ivar->getAlignment();
- if (alignment > maxAlignment) maxAlignment = alignment;
- } else {
- // skip anonymous bitfields
- if (ivar->hasOffset()) {
- uint32_t oldOffset = (uint32_t)ivar->getOffset(cache);
- ivar->setOffset(cache, oldOffset + slide);
- fOptimized++;
- //fprintf(stderr, "%d -> %d for %s.%s\n", oldOffset, oldOffset + slide, cls->getName(cache), ivar->getName(cache));
- } else {
- //fprintf(stderr, "NULL offset\n");
- }
- }
- }
-
- // Class visitor function. Evaluates whether to slide ivars and performs slide if needed.
- // The slide algorithm is also implemented in objc. Any changes here should be reflected there also.
- void visitClass(SharedCache<A>* cache, const macho_header<P>* /*unused, may be NULL*/, objc_class_t<A> *cls)
- {
- if (fGCClasses.contains(cls)) {
- // This class supports GC. We don't know how to update
- // GC ivar layout bitmaps, so don't touch anything.
- return;
- }
-
- objc_class_t<A> *super = cls->getSuperclass(cache);
- if (super) {
- // Recursively visit superclasses to ensure we have the correct superclass start
- // Note that we don't need the macho_header, so just pass NULL.
- visitClass(cache, NULL, super);
-
- objc_class_data_t<A> *data = cls->getData(cache);
- objc_class_data_t<A> *super_data = super->getData(cache);
- int32_t diff = super_data->getInstanceSize() - data->getInstanceStart();
- if (diff > 0) {
- IvarWalker<A, IvarOffsetOptimizer<A> > ivarVisitor(*this);
- maxAlignment = 0;
- slide = 0;
-
- // This walk computes maxAlignment
- ivarVisitor.walk(cache, NULL, cls);
-
- // Compute a slide value that preserves that alignment
- uint32_t alignMask = maxAlignment - 1;
- if (diff & alignMask) diff = (diff + alignMask) & ~alignMask;
-
- // Slide all of this class's ivars en masse
- slide = diff;
- if (slide != 0) {
- //fprintf(stderr, "Sliding ivars in %s by %u (superclass was %d, now %d)\n", cls->getName(cache), slide, data->getInstanceStart(), super_data->getInstanceSize());
- ivarVisitor.walk(cache, NULL, cls);
- data->setInstanceStart(data->getInstanceStart() + slide);
- data->setInstanceSize(data->getInstanceSize() + slide);
- }
- }
- }
- }
-
- // Gather the list of GC-supporting classes.
- // Ivars in these classes cannot be updated because
- // we don't know how to update ivar layout bitmaps.
- void findGCClasses(SharedCache<A>* cache, const macho_header<P>* header)
- {
- if (headerSupportsGC(cache, header)) {
- ClassWalker<A, GCClassSet<A> > classVisitor(fGCClasses);
- classVisitor.walk(cache, header);
- }
- }
-
- // Enumerates objc classes in the module and performs any ivar slides
- void optimize(SharedCache<A>* cache, const macho_header<P>* header)
- {
- if (! headerSupportsGC(cache, header)) {
- ClassWalker<A, IvarOffsetOptimizer<A> > classVisitor(*this);
- classVisitor.walk(cache, header);
- }
- }
-};
-
-
-// Sort methods in place by selector.
-template <typename A>
-class MethodListSorter {
-
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
-
- uint32_t fOptimized;
-
- friend class MethodListWalker<A, MethodListSorter<A> >;
- void visitMethodList(objc_method_list_t<A> *mlist)
- {
- typename objc_method_t<A>::SortBySELAddress sorter;
- std::stable_sort(mlist->begin(), mlist->end(), sorter);
- mlist->setFixedUp();
- fOptimized++;
- }
-
- void visitProtocolMethodList(objc_method_list_t<A> *mlist, pint_t *typelist)
- {
- typename objc_method_t<A>::SortBySELAddress sorter;
- // can't easily use std::stable_sort here
- for (uint32_t i = 0; i < mlist->getCount(); i++) {
- for (uint32_t j = i+1; j < mlist->getCount(); j++) {
- objc_method_t<A>& mi = mlist->get(i);
- objc_method_t<A>& mj = mlist->get(j);
- if (! sorter(mi, mj)) {
- std::swap(mi, mj);
- if (typelist) std::swap(typelist[i], typelist[j]);
- }
- }
- }
-
- mlist->setFixedUp();
- fOptimized++;
- }
-
-public:
- MethodListSorter() : fOptimized(0) { }
-
- size_t optimized() const { return fOptimized; }
-
- void optimize(SharedCache<A>* cache, macho_header<P>* header)
- {
- MethodListWalker<A, MethodListSorter<A> > mw(*this);
- mw.walk(cache, header);
- }
-};
-
-
-template <typename A>
-class HeaderInfoOptimizer {
-
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
-
- objc_header_info_t<A>* fHinfos;
- size_t fCount;
-
-public:
- HeaderInfoOptimizer() : fHinfos(0), fCount(0) { }
-
- const char *init(size_t count, uint8_t*& buf, size_t& bufSize)
- {
- if (count == 0) return NULL;
-
- size_t requiredSize =
- 2*sizeof(uint32_t) + count*sizeof(objc_header_info_t<A>);
- if (bufSize < requiredSize) {
- return "libobjc's read/write section is too small (metadata not optimized)";
- }
-
- uint32_t *buf32 = (uint32_t *)buf;
- A::P::E::set32(buf32[0], count);
- A::P::E::set32(buf32[1], sizeof(objc_header_info_t<A>));
- fHinfos = (objc_header_info_t<A>*)(buf32+2);
-
- buf += requiredSize;
- bufSize -= requiredSize;
-
- return NULL;
- }
-
- void update(SharedCache<A>* cache, const macho_header<P>* mh, std::vector<void*>& pointersInData)
- {
- objc_header_info_t<A>* hi = new(&fHinfos[fCount++]) objc_header_info_t<A>(cache, mh);
- hi->addPointers(pointersInData);
- }
-
- objc_header_info_t<A>* hinfoForHeader(SharedCache<A>* cache, const macho_header<P>* mh)
- {
- // fixme could be binary search
- pint_t mh_vmaddr = cache->VMAddressForMappedAddress(mh);
- for (size_t i = 0; i < fCount; i++) {
- objc_header_info_t<A>* hi = &fHinfos[i];
- if (hi->header_vmaddr() == mh_vmaddr) return hi;
- }
- return NULL;
- }
-};
if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 )
return true;
// If the symbol comes from a dylib that is re-exported, this is not an individual symbol re-export
- if ( _reexportDeps.count(entry.other) != 0 )
+ if ( _reexportDeps.count((int)entry.other) != 0 )
return true;
return false;
}
// update load commands
uint64_t cumulativeFileSize = 0;
+ const unsigned origLoadCommandsSize = mh->sizeofcmds();
+ unsigned bytesRemaining = origLoadCommandsSize;
+ unsigned removedCount = 0;
const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
- const uint32_t cmd_count = mh->ncmds();
+ const uint32_t cmdCount = mh->ncmds();
const macho_load_command<P>* cmd = cmds;
macho_segment_command<P>* linkEditSegCmd = NULL;
macho_symtab_command<P>* symtab = NULL;
uint32_t exportsTrieSize = 0;
std::set<int> reexportDeps;
int depIndex = 0;
- for (uint32_t i = 0; i < cmd_count; ++i) {
+ for (uint32_t i = 0; i < cmdCount; ++i) {
+ bool remove = false;
switch ( cmd->cmd() ) {
case macho_segment_command<P>::CMD:
{
reexportDeps.insert(depIndex);
}
break;
+ case LC_SEGMENT_SPLIT_INFO:
+ // <rdar://problem/23212513> dylibs iOS 9 dyld caches have bogus LC_SEGMENT_SPLIT_INFO
+ remove = true;
+ break;
+ }
+ uint32_t cmdSize = cmd->cmdsize();
+ macho_load_command<P>* nextCmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmdSize);
+ if ( remove ) {
+ ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
+ ++removedCount;
+ }
+ else {
+ bytesRemaining -= cmdSize;
+ cmd = nextCmd;
}
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
-
+ // zero out stuff removed
+ ::bzero((void*)cmd, bytesRemaining);
+ // update header
+ mh->set_ncmds(cmdCount - removedCount);
+ mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining);
+
// rebuild symbol table
if ( linkEditSegCmd == NULL ) {
fprintf(stderr, "__LINKEDIT not found\n");
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2015 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <sys/types.h>
#include <stdint.h>
#include <mach/shared_region.h>
+#include <uuid/uuid.h>
struct dyld_cache_header
uint64_t localSymbolsOffset; // file offset of where local symbols are stored
uint64_t localSymbolsSize; // size of local symbols information
uint8_t uuid[16]; // unique value for each shared cache file
- uint64_t cacheType; // 1 for development, 0 for optimized
+ uint64_t cacheType; // 0 for development, 1 for production
+ uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses
+ uint32_t branchPoolsCount; // number of uint64_t entries
+ uint64_t accelerateInfoAddr; // (unslid) address of optimization info
+ uint64_t accelerateInfoSize; // size of optimization info
+ uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info
+ uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries
};
struct dyld_cache_mapping_info {
uint32_t pad;
};
+struct dyld_cache_image_info_extra
+{
+ uint64_t exportsTrieAddr; // address of trie in unslid cache
+ uint64_t weakBindingsAddr;
+ uint32_t exportsTrieSize;
+ uint32_t weakBindingsSize;
+ uint32_t dependentsStartArrayIndex;
+ uint32_t reExportsStartArrayIndex;
+};
+
+
+struct dyld_cache_accelerator_info
+{
+ uint32_t version; // currently 1
+ uint32_t imageExtrasCount; // does not include aliases
+ uint32_t imagesExtrasOffset; // offset into this chunk of first dyld_cache_image_info_extra
+ uint32_t bottomUpListOffset; // offset into this chunk to start of 16-bit array of sorted image indexes
+ uint32_t dylibTrieOffset; // offset into this chunk to start of trie containing all dylib paths
+ uint32_t dylibTrieSize; // size of trie containing all dylib paths
+ uint32_t initializersOffset; // offset into this chunk to start of initializers list
+ uint32_t initializersCount; // size of initializers list
+ uint32_t dofSectionsOffset; // offset into this chunk to start of DOF sections list
+ uint32_t dofSectionsCount; // size of initializers list
+ uint32_t reExportListOffset; // offset into this chunk to start of 16-bit array of re-exports
+ uint32_t reExportCount; // size of re-exports
+ uint32_t depListOffset; // offset into this chunk to start of 16-bit array of dependencies (0x8000 bit set if upward)
+ uint32_t depListCount; // size of dependencies
+ uint32_t rangeTableOffset; // offset into this chunk to start of ss
+ uint32_t rangeTableCount; // size of dependencies
+ uint64_t dyldSectionAddr; // address of libdyld's __dyld section in unslid cache
+};
+
+struct dyld_cache_accelerator_initializer
+{
+ uint32_t functionOffset; // address offset from start of cache mapping
+ uint32_t imageIndex;
+};
+
+struct dyld_cache_range_entry
+{
+ uint64_t startAddress; // unslid address of start of region
+ uint32_t size;
+ uint32_t imageIndex;
+};
+
+struct dyld_cache_accelerator_dof
+{
+ uint64_t sectionAddress; // unslid address of start of region
+ uint32_t sectionSize;
+ uint32_t imageIndex;
+};
+
+struct dyld_cache_image_text_info
+{
+ uuid_t uuid;
+ uint64_t loadAddress; // unslid address of start of __TEXT
+ uint32_t textSegmentSize;
+ uint32_t pathOffset; // offset from start of cache file
+};
+
+
+// The rebasing info is to allow the kernel to lazily rebase DATA pages of the
+// dyld shared cache. Rebasing is adding the slide to interior pointers.
struct dyld_cache_slide_info
{
uint32_t version; // currently 1
};
+// The version 2 of the slide info uses a different compression scheme. Since
+// only interior pointers (pointers that point within the cache) are rebased
+// (slid), we know the possible range of the pointers and thus know there are
+// unused bits in each pointer. We use those bits to form a linked list of
+// locations needing rebasing in each page.
+//
+// Definitions:
+//
+// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size
+// pageStarts[] = info + info->page_starts_offset
+// pageExtras[] = info + info->page_extras_offset
+// valueMask = ~(info->delta_mask)
+// deltaShift = __builtin_ctzll(info->delta_mask) - 2
+//
+// There are three cases:
+//
+// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE
+// The page contains no values that need rebasing.
+//
+// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0
+// All rebase locations are in one linked list. The offset of the first
+// rebase location in the page is pageStarts[pageIndex] * 4.
+//
+// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA
+// Multiple linked lists are needed for all rebase locations in a page.
+// The pagesExtras array contains 2 or more entries each of which is the
+// start of a new linked list in the page. The first is at:
+// extrasStartIndex = (pageStarts[pageIndex] & 0x3FFF)
+// The next is at extrasStartIndex+1. The last is denoted by
+// having the high bit (DYLD_CACHE_SLIDE_PAGE_ATTR_END) of the pageExtras[]
+// set.
+//
+// For 64-bit architectures, there is always enough free bits to encode all
+// possible deltas. The info->delta_mask field shows where the delta is located
+// in the pointer. That value must be masked off (valueMask) before the slide
+// is added to the pointer.
+//
+// For 32-bit architectures, there are only three bits free (the three most
+// significant bits). To extract the delta, you must first subtract value_add
+// from the pointer value, then AND with delta_mask, then shift by deltaShift.
+// That still leaves a maximum delta to the next rebase location of 28 bytes.
+// To reduce the number or chains needed, an optimization was added. Turns
+// out zero is common in the DATA region. A zero can be turned into a
+// non-rebasing entry in the linked list. The can be done because nothing
+// in the shared cache should point out of its dylib to the start of the shared
+// cache.
+//
+// The code for processing a linked list (chain) is:
+//
+// uint32_t delta = 1;
+// while ( delta != 0 ) {
+// uint8_t* loc = pageStart + pageOffset;
+// uintptr_t rawValue = *((uintptr_t*)loc);
+// delta = ((rawValue & deltaMask) >> deltaShift);
+// uintptr_t newValue = (rawValue & valueMask);
+// if ( newValue != 0 ) {
+// newValue += valueAdd;
+// newValue += slideAmount;
+// }
+// *((uintptr_t*)loc) = newValue;
+// pageOffset += delta;
+// }
+//
+//
+struct dyld_cache_slide_info2
+{
+ uint32_t version; // currently 2
+ uint32_t page_size; // currently 4096 (may also be 16384)
+ uint32_t page_starts_offset;
+ uint32_t page_starts_count;
+ uint32_t page_extras_offset;
+ uint32_t page_extras_count;
+ uint64_t delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location
+ uint64_t value_add;
+ //uint16_t page_starts[page_starts_count];
+ //uint16_t page_extras[page_extras_count];
+};
+#define DYLD_CACHE_SLIDE_PAGE_ATTRS 0xC000 // high bits of uint16_t are flags
+#define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA 0x8000 // index is into extras array (not starts array)
+#define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 0x4000 // page has no rebasing
+#define DYLD_CACHE_SLIDE_PAGE_ATTR_END 0x8000 // last chain entry for page
+
+
struct dyld_cache_local_symbols_info
{
uint32_t nlistOffset; // offset into this chunk of nlist entries
-#define MACOSX_DYLD_SHARED_CACHE_DIR "/var/db/dyld/"
+#define MACOSX_DYLD_SHARED_CACHE_DIR "/private/var/db/dyld/"
#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/"
#define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_"
+#define DYLD_SHARED_CACHE_DEVELOPMENT_EXT ".development"
+
+static const uint64_t kDyldSharedCacheTypeDevelopment = 0;
+static const uint64_t kDyldSharedCacheTypeProduction = 1;
+
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
+#include <dlfcn.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/syslimits.h>
#include <mach-o/arch.h>
#include <mach-o/loader.h>
+#include <mach-o/dyld.h>
#include <mach/mach.h>
#include <map>
#include <vector>
#include "dsc_iterator.h"
+#include "dsc_extractor.h"
#include "dyld_cache_format.h"
#include "Architectures.hpp"
#include "MachOFileAbstraction.hpp"
#include "CacheFileAbstraction.hpp"
+#include "Trie.hpp"
enum Mode {
modeNone,
modeMap,
modeDependencies,
modeSlideInfo,
+ modeAcceleratorInfo,
+ modeTextInfo,
modeLinkEdit,
+ modeLocalSymbols,
modeInfo,
- modeSize
+ modeSize,
+ modeExtract
};
struct Options {
Mode mode;
const char* dependentsOfPath;
const void* mappedCache;
+ const char* extractionDir;
bool printUUIDs;
bool printVMAddrs;
bool printDylibVersions;
void usage() {
- fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map [ shared-cache-file ] | -slide_info | -info\n");
+ fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map | -slide_info | -info | -extract <dylib-dir> [ shared-cache-file ] \n");
}
#if __x86_64__
static void checkMode(Mode mode) {
if ( mode != modeNone ) {
- fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, or -size\n");
+ fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, -extract, or -size\n");
usage();
exit(1);
}
options.printDylibVersions = false;
options.printInodes = false;
options.dependentsOfPath = NULL;
-
+ options.extractionDir = NULL;
+
for (uint32_t i = 1; i < argc; i++) {
const char* opt = argv[i];
if (opt[0] == '-') {
else if (strcmp(opt, "-slide_info") == 0) {
checkMode(options.mode);
options.mode = modeSlideInfo;
- }
+ }
+ else if (strcmp(opt, "-accelerator_info") == 0) {
+ checkMode(options.mode);
+ options.mode = modeAcceleratorInfo;
+ }
+ else if (strcmp(opt, "-text_info") == 0) {
+ checkMode(options.mode);
+ options.mode = modeTextInfo;
+ }
+ else if (strcmp(opt, "-local_symbols") == 0) {
+ checkMode(options.mode);
+ options.mode = modeLocalSymbols;
+ }
else if (strcmp(opt, "-map") == 0) {
checkMode(options.mode);
options.mode = modeMap;
else if (strcmp(opt, "-size") == 0) {
checkMode(options.mode);
options.mode = modeSize;
- }
+ }
+ else if (strcmp(opt, "-extract") == 0) {
+ checkMode(options.mode);
+ options.mode = modeExtract;
+ options.extractionDir = argv[++i];
+ if ( i >= argc ) {
+ fprintf(stderr, "Error: option -extract requires a directory argument\n");
+ usage();
+ exit(1);
+ }
+ }
else if (strcmp(opt, "-uuid") == 0) {
options.printUUIDs = true;
}
uint64_t dataSize = dataMapping->size();
const dyldCacheSlideInfo<LittleEndian>* slideInfoHeader = (dyldCacheSlideInfo<LittleEndian>*)((char*)options.mappedCache+header->slideInfoOffset());
printf("slide info version=%d\n", slideInfoHeader->version());
- printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096);
- const dyldCacheSlideInfoEntry* entries = (dyldCacheSlideInfoEntry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset());
- for(int i=0; i < slideInfoHeader->toc_count(); ++i) {
- printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, slideInfoHeader->toc(i));
- const dyldCacheSlideInfoEntry* entry = &entries[slideInfoHeader->toc(i)];
- for(int j=0; j < slideInfoHeader->entries_size(); ++j)
- printf("%02X", entry->bits[j]);
- printf("\n");
+ if ( slideInfoHeader->version() == 1 ) {
+ printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096);
+ const dyldCacheSlideInfoEntry* entries = (dyldCacheSlideInfoEntry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset());
+ for(int i=0; i < slideInfoHeader->toc_count(); ++i) {
+ printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, slideInfoHeader->toc(i));
+ const dyldCacheSlideInfoEntry* entry = &entries[slideInfoHeader->toc(i)];
+ for(int j=0; j < slideInfoHeader->entries_size(); ++j)
+ printf("%02X", entry->bits[j]);
+ printf("\n");
+ }
+ }
+ else if ( slideInfoHeader->version() == 2 ) {
+ const dyldCacheSlideInfo2<LittleEndian>* slideInfo = (dyldCacheSlideInfo2<LittleEndian>*)(slideInfoHeader);
+ printf("page_size=%d\n", slideInfo->page_size());
+ printf("delta_mask=0x%016llX\n", slideInfo->delta_mask());
+ printf("value_add=0x%016llX\n", slideInfo->value_add());
+ printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count(), slideInfo->page_extras_count());
+ const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset());
+ const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset());
+ for (int i=0; i < slideInfo->page_starts_count(); ++i) {
+ const uint16_t start = starts[i];
+ if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
+ printf("page[% 5d]: no rebasing\n", i);
+ }
+ else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
+ printf("page[% 5d]: ", i);
+ int j=(start & 0x3FFF);
+ bool done = false;
+ do {
+ uint16_t aStart = extras[j];
+ printf("start=0x%04X ", aStart & 0x3FFF);
+ done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
+ ++j;
+ } while ( !done );
+ printf("\n");
+ }
+ else {
+ printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
+ }
+ }
}
}
else if ( options.mode == modeInfo ) {
printf("n/a\n");
}
printf("image count: %u\n", header->imagesCount());
+ if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) {
+ printf("branch pool count: %u\n", header->branchPoolsCount());
+ }
printf("mappings:\n");
const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
for (uint32_t i=0; i < header->mappingCount(); ++i) {
if ( mappings[i].init_prot() & VM_PROT_EXECUTE )
- printf(" __TEXT %3lluMB, 0x%08llX -> 0x%08llX\n", mappings[i].size()/(1024*1024), mappings[i].address(), mappings[i].address() + mappings[i].size());
+ printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
+ mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(),
+ mappings[i].address(), mappings[i].address() + mappings[i].size());
else if ( mappings[i]. init_prot() & VM_PROT_WRITE )
- printf(" __DATA %3lluMB, 0x%08llX -> 0x%08llX\n", mappings[i].size()/(1024*1024), mappings[i].address(), mappings[i].address() + mappings[i].size());
+ printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
+ mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(),
+ mappings[i].address(), mappings[i].address() + mappings[i].size());
else if ( mappings[i].init_prot() & VM_PROT_READ )
- printf(" __LINKEDIT %3lluMB, 0x%08llX -> 0x%08llX\n", mappings[i].size()/(1024*1024), mappings[i].address(), mappings[i].address() + mappings[i].size());
+ printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
+ mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(),
+ mappings[i].address(), mappings[i].address() + mappings[i].size());
}
if ( header->codeSignatureOffset() != 0 ) {
- uint64_t size = statbuf.st_size - header->codeSignatureOffset();
+ uint64_t size = statbuf.st_size - header->codeSignatureOffset();
uint64_t csAddr = mappings[header->mappingCount()-1].address() + mappings[header->mappingCount()-1].size();
- printf(" code sign %3lluMB, 0x%08llX -> 0x%08llX\n", size/(1024*1024), csAddr, csAddr + size);
+ if ( size != 0 )
+ printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
+ size/(1024*1024), header->codeSignatureOffset(), header->codeSignatureOffset() + size, csAddr, csAddr + size);
+ }
+ printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n",
+ header->slideInfoSize()/1024, header->slideInfoOffset(), header->slideInfoOffset() + header->slideInfoSize());
+ if ( header->localSymbolsOffset() != 0 )
+ printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n",
+ header->localSymbolsSize()/(1024*1024), header->localSymbolsOffset(), header->localSymbolsOffset() + header->localSymbolsSize());
+ if ( (header->mappingOffset() >= 0x78) && (header->accelerateInfoSize() != 0) )
+ printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n",
+ header->accelerateInfoSize()/1024, header->accelerateInfoAddr(), header->accelerateInfoAddr() + header->accelerateInfoSize());
+ }
+ else if ( options.mode == modeAcceleratorInfo ) {
+ const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
+ if ( (header->mappingOffset() < sizeof(dyldCacheHeader<LittleEndian>)) || (header->accelerateInfoSize() == 0) ) {
+ printf("no accelerator info\n");
+ }
+ else {
+ const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
+ uint64_t aiAddr = header->accelerateInfoAddr();
+ dyldCacheAcceleratorInfo<LittleEndian>* accelInfo = NULL;
+ for (uint32_t i=0; i < header->mappingCount(); ++i) {
+ if ( (mappings[i].address() <= aiAddr) && (aiAddr < mappings[i].address()+mappings[i].size()) ) {
+ uint64_t offset = aiAddr - mappings[i].address() + mappings[i].file_offset();
+ accelInfo = (dyldCacheAcceleratorInfo<LittleEndian>*)((uint8_t*)options.mappedCache + offset);
+ }
+ }
+ if ( accelInfo == NULL ) {
+ printf("accelerator info not in any mapped range\n");
+ }
+ else {
+ const dyldCacheImageInfo<LittleEndian>* images = (dyldCacheImageInfo<LittleEndian>*)((char*)options.mappedCache + header->imagesOffset());
+ const dyldCacheImageInfoExtra<LittleEndian>* imagesExtra = (dyldCacheImageInfoExtra<LittleEndian>*)((char*)accelInfo + accelInfo->imagesExtrasOffset());
+ const uint16_t* dependencyArray = (uint16_t*)((char*)accelInfo + accelInfo->depListOffset());
+ const uint16_t* reExportArray = (uint16_t*)((char*)accelInfo + accelInfo->reExportListOffset());
+ printf("extra image info (count=%u):\n", accelInfo->imageExtrasCount());
+ for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) {
+ printf(" image[%3u] %s:\n", i, (char*)options.mappedCache +images[i].pathFileOffset());
+ printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra[i].exportsTrieAddr(), imagesExtra[i].exportsTrieSize());
+ if ( imagesExtra[i].weakBindingsSize() )
+ printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra[i].weakBindingsAddr(), imagesExtra[i].weakBindingsSize());
+ printf(" dependents: ");
+ for (uint32_t d=imagesExtra[i].dependentsStartArrayIndex(); dependencyArray[d] != 0xFFFF; ++d) {
+ uint16_t depIndex = dependencyArray[d];
+ if ( depIndex & 0x8000 )
+ printf(" up(%d) ", depIndex & 0x7FFF);
+ else
+ printf(" %d ", depIndex);
+ }
+ printf("\n");
+ printf(" re-exports: ");
+ for (uint32_t r=imagesExtra[i].reExportsStartArrayIndex(); reExportArray[r] != 0xFFFF; ++r)
+ printf(" %d ", reExportArray[r]);
+ printf("\n");
+ }
+ printf("libdyld.dylib:\n");
+ printf(" __dyld section address: 0x%llX\n", accelInfo->dyldSectionAddr());
+ printf("initializers (count=%u):\n", accelInfo->initializersCount());
+ const dyldCacheAcceleratorInitializer<LittleEndian>* initializers = (dyldCacheAcceleratorInitializer<LittleEndian>*)((char*)accelInfo + accelInfo->initializersOffset());
+ for (uint32_t i=0; i < accelInfo->initializersCount(); ++i) {
+ printf(" image[%3u] 0x%llX\n", initializers[i].imageIndex(), mappings[0].address() + initializers[i].functionOffset());
+ }
+ printf("DOF sections (count=%u):\n", accelInfo->dofSectionsCount());
+ const dyldCacheAcceleratorDOFEntry<LittleEndian>* dofs = (dyldCacheAcceleratorDOFEntry<LittleEndian>*)((char*)accelInfo + accelInfo->dofSectionsOffset());
+ for (uint32_t i=0; i < accelInfo->dofSectionsCount(); ++i) {
+ printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs[i].imageIndex(), dofs[i].sectionAddress(), dofs[i].sectionAddress()+dofs[i].sectionSize());
+ }
+ printf("bottom up order (count=%u):\n", accelInfo->imageExtrasCount());
+ const uint16_t* bottomUpArray = (uint16_t*)((char*)accelInfo + accelInfo->bottomUpListOffset());
+ for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) {
+ unsigned imageIndex = bottomUpArray[i];
+ if ( imageIndex < accelInfo->imageExtrasCount() )
+ printf(" image[%3u] %s\n", imageIndex, (char*)options.mappedCache + images[imageIndex].pathFileOffset());
+ else
+ printf(" image[%3u] BAD INDEX\n", imageIndex);
+ }
+ printf("range table (count=%u):\n", accelInfo->rangeTableCount());
+ const dyldCacheAcceleratorRangeEntry<LittleEndian>* rangeTable = (dyldCacheAcceleratorRangeEntry<LittleEndian>*)((char*)accelInfo + accelInfo->rangeTableOffset());
+ for (uint32_t i=0; i < accelInfo->rangeTableCount(); ++i) {
+ const dyldCacheAcceleratorRangeEntry<LittleEndian>& entry = rangeTable[i];
+ printf(" 0x%llX -> 0x%llX %s\n", entry.startAddress(), entry.startAddress() + entry.size(), (char*)options.mappedCache + images[entry.imageIndex()].pathFileOffset());
+ }
+ printf("dylib trie (size=%u):\n", accelInfo->dylibTrieSize());
+ const uint8_t* dylibTrieStart = (uint8_t*)accelInfo + accelInfo->dylibTrieOffset();
+ const uint8_t* dylibTrieEnd = dylibTrieStart + accelInfo->dylibTrieSize();
+ std::vector<DylibIndexTrie::Entry> dylibEntries;
+ if ( !Trie<DylibIndex>::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) )
+ printf(" malformed dylibs trie\n");
+ for (const DylibIndexTrie::Entry& x : dylibEntries) {
+ printf(" image[%3u] %s\n", x.info.index, x.name.c_str());
+ }
+ }
+ }
+ }
+ else if ( options.mode == modeTextInfo ) {
+ const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
+ if ( (header->mappingOffset() < sizeof(dyldCacheHeader<LittleEndian>)) || (header->imagesTextCount() == 0) ) {
+ printf("no text info\n");
+ }
+ else {
+ const dyldCacheImageTextInfo<LittleEndian>* imagesText = (dyldCacheImageTextInfo<LittleEndian>*)((char*)options.mappedCache + header->imagesTextOffset());
+ const dyldCacheImageTextInfo<LittleEndian>* imagesTextEnd = &imagesText[header->imagesTextCount()];
+ printf("dylib text infos (count=%llu):\n", header->imagesTextCount());
+ for (const dyldCacheImageTextInfo<LittleEndian>* p=imagesText; p < imagesTextEnd; ++p) {
+ printf(" 0x%09llX -> 0x%09llX <", p->loadAddress(), p->loadAddress() + p->textSegmentSize());
+ for (int i=0; i<16; ++i) {
+ switch (i) {
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ printf("-");
+ break;
+ }
+ printf("%02X", p->uuid()[i]);
+ }
+ printf("> %s\n", (char*)options.mappedCache + p->pathOffset());
+ }
}
}
+ else if ( options.mode == modeLocalSymbols ) {
+ const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
+ if ( header->localSymbolsOffset() == 0 ) {
+ fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n");
+ exit(1);
+ }
+ const bool is64 = (strstr((char*)options.mappedCache, "64") != NULL);
+ const dyldCacheImageInfo<LittleEndian>* imageInfos = (dyldCacheImageInfo<LittleEndian>*)((char*)options.mappedCache + header->imagesOffset());
+ const dyldCacheLocalSymbolsInfo<LittleEndian>* localsInfo = (dyldCacheLocalSymbolsInfo<LittleEndian>*)((char*)options.mappedCache + header->localSymbolsOffset());
+ const uint32_t nlistFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->nlistOffset());
+ const uint32_t nlistCount = localsInfo->nlistCount();
+ const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12;
+ const uint32_t stringsFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->stringsOffset());
+ const uint32_t stringsSize = localsInfo->stringsSize();
+ const uint32_t entriesCount = localsInfo->entriesCount();
+ const dyldCacheLocalSymbolEntry<LittleEndian>* entries = (dyldCacheLocalSymbolEntry<LittleEndian>*)((char*)localsInfo + localsInfo->entriesOffset());
+ printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize);
+ printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize);
+ printf("local symbols by dylib (count=%d):\n", entriesCount);
+ const char* stringPool = (char*)options.mappedCache + stringsFileOffset;
+ for (int i=0; i < entriesCount; ++i) {
+ const char* imageName = (char*)options.mappedCache + imageInfos[i].pathFileOffset();
+ printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries[i].nlistStartIndex(), entries[i].nlistCount(), imageName);
+ #if 0
+ if ( is64 ) {
+ const nlist_64* symTab = (nlist_64*)((char*)options.mappedCache + nlistFileOffset);
+ for (int e=0; e < entries[i].nlistCount(); ++e) {
+ const nlist_64* entry = &symTab[entries[i].nlistStartIndex()+e];
+ printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &stringPool[entry->n_un.n_strx]);
+ printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value);
+ }
+ }
+ #endif
+ }
+ }
+ else if ( options.mode == modeExtract ) {
+ char pathBuffer[PATH_MAX];
+ uint32_t bufferSize = PATH_MAX;
+ if ( _NSGetExecutablePath(pathBuffer, &bufferSize) != 0 ) {
+ fprintf(stderr, "Error: could not get path of program\n");
+ return 1;
+ }
+ char* last = strrchr(pathBuffer, '/');
+ strcpy(last+1, "../../lib/dsc_extractor.bundle");
+ void* handle = dlopen(pathBuffer, RTLD_LAZY);
+ if ( handle == NULL ) {
+ fprintf(stderr, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer);
+ return 1;
+ }
+
+ typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
+ void (^progress)(unsigned current, unsigned total));
+
+ extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
+ if ( proc == NULL ) {
+ fprintf(stderr, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
+ return 1;
+ }
+
+ int result = (*proc)(sharedCachePath, options.extractionDir, ^(unsigned c, unsigned total) { } );
+ return result;
+ }
else {
segment_callback_t callback;
if ( strcmp((char*)options.mappedCache, "dyld_v1 i386") == 0 ) {
case modeNone:
case modeInfo:
case modeSlideInfo:
+ case modeAcceleratorInfo:
+ case modeTextInfo:
+ case modeLocalSymbols:
+ case modeExtract:
break;
}
}
case modeNone:
case modeInfo:
case modeSlideInfo:
+ case modeAcceleratorInfo:
+ case modeTextInfo:
+ case modeLocalSymbols:
+ case modeExtract:
break;
}
}
case modeNone:
case modeInfo:
case modeSlideInfo:
+ case modeAcceleratorInfo:
+ case modeTextInfo:
+ case modeLocalSymbols:
+ case modeExtract:
break;
}
}
case modeNone:
case modeInfo:
case modeSlideInfo:
+ case modeAcceleratorInfo:
+ case modeTextInfo:
+ case modeLocalSymbols:
+ case modeExtract:
break;
}
}
printf(" 0x%08llX %s\n", it->textSize, it->path);
}
}
-
+
if ( (options.mode == modeDependencies) && options.dependentsOfPath && !results.dependentTargetFound) {
fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath);
exit(1);
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2006-2011 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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <fcntl.h>
-#include <dlfcn.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/resource.h>
-#include <dirent.h>
-#include <servers/bootstrap.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/Security.h>
-#include <Security/SecCodeSigner.h>
-#include <CommonCrypto/CommonDigest.h>
-
-#include "dyld_cache_format.h"
-
-#include <vector>
-#include <set>
-#include <map>
-#include <unordered_map>
-
-#include "Architectures.hpp"
-#include "MachOLayout.hpp"
-#include "MachORebaser.hpp"
-#include "MachOBinder.hpp"
-#include "CacheFileAbstraction.hpp"
-#include "dyld_cache_config.h"
-
-#define SELOPT_WRITE
-#include "objc-shared-cache.h"
-
-#define FIRST_DYLIB_TEXT_OFFSET 0x10000
-
-#ifndef LC_FUNCTION_STARTS
- #define LC_FUNCTION_STARTS 0x26
-#endif
-
-static bool verbose = false;
-static bool progress = false;
-static bool iPhoneOS = false;
-static bool rootless = true;
-static std::vector<const char*> warnings;
-
-
-static void warn(const char *arch, const char *format, ...)
-{
- char *msg;
-
- va_list args;
- va_start(args, format);
- ::vasprintf(&msg, format, args);
- va_end(args);
-
- warnings.push_back(msg);
-
- if ( verbose ) {
- ::fprintf(::stderr, "update_dyld_shared_cache: warning: %s%s%s%s\n",
- arch ? "for arch " : "",
- arch ? arch : "",
- arch ? ", " : "",
- msg);
- }
-}
-
-
-class CStringHash {
-public:
- size_t operator()(const char* __s) const {
- size_t __h = 0;
- for ( ; *__s; ++__s)
- __h = 5 * __h + *__s;
- return __h;
- };
-};
-class CStringEquals
-{
-public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
-};
-
-
-
-class ArchGraph
-{
-public:
- typedef std::unordered_map<const char*, const char*, CStringHash, CStringEquals> StringToString;
-
- static void addArchPair(ArchPair ap);
- static void addRoot(const char* vpath, const std::set<ArchPair>& archs);
- static uint64_t maxCacheSizeForArchPair(ArchPair ap);
- static void findSharedDylibs(ArchPair ap);
- static ArchGraph* graphForArchPair(ArchPair ap) { return fgPerArchGraph[ap]; }
- static void setFileSystemRoot(const char* root) { fgFileSystemRoot = root; }
- static void setFileSystemOverlay(const std::vector<const char*>& overlays);
- static const char* archName(ArchPair ap);
-
- ArchPair getArchPair() { return fArchPair; }
- std::set<const class MachOLayoutAbstraction*>& getSharedDylibs() { return fSharedDylibs; }
- StringToString& getDylibAliases() { return fAliasesMap; }
- const char* archName() { return archName(fArchPair); }
-
-private:
-
- class DependencyNode
- {
- public:
- DependencyNode(ArchGraph*, const char* path, const MachOLayoutAbstraction* layout);
- void loadDependencies(const MachOLayoutAbstraction*);
- void markNeededByRoot(DependencyNode*);
- const char* getPath() const { return fPath; }
- const MachOLayoutAbstraction* getLayout() const { return fLayout; }
- size_t useCount() const { return fRootsDependentOnThis.size(); }
- bool allDependentsFound() const { return !fDependentMissing; }
- bool dependsOnDylibList() const { return fRootsDependentOnThis.count(const_cast<DependencyNode*>(this)); }
-
- private:
- ArchGraph* fGraph;
- const char* fPath;
- const MachOLayoutAbstraction* fLayout;
- bool fDependenciesLoaded;
- bool fDependentMissing;
- std::set<DependencyNode*> fDependsOn;
- std::set<DependencyNode*> fRootsDependentOnThis;
- };
-
- typedef std::unordered_map<const char*, class DependencyNode*, CStringHash, CStringEquals> PathToNode;
-
-
- ArchGraph(ArchPair ap) : fArchPair(ap) {}
- void addRoot(const char* path, const MachOLayoutAbstraction*);
- DependencyNode* getNode(const char* path);
- DependencyNode* getNodeForVirtualPath(const char* vpath);
- static bool canBeShared(const MachOLayoutAbstraction* layout, ArchPair ap, const std::set<const MachOLayoutAbstraction*>& possibleLibs, std::map<const MachOLayoutAbstraction*, bool>& shareableMap);
- static bool sharable(const MachOLayoutAbstraction* layout, ArchPair ap, char** msg);
-
- static std::map<ArchPair, ArchGraph*> fgPerArchGraph;
- static const char* fgFileSystemRoot;
- static std::vector<const char*> fgFileSystemOverlays;
-
- ArchPair fArchPair;
- std::set<DependencyNode*> fRoots;
- PathToNode fNodes;
- std::set<const MachOLayoutAbstraction*> fSharedDylibs; // use set to avoid duplicates when installname!=realpath
- StringToString fAliasesMap;
-};
-std::map<ArchPair, ArchGraph*> ArchGraph::fgPerArchGraph;
-const char* ArchGraph::fgFileSystemRoot = "";
-std::vector<const char*> ArchGraph::fgFileSystemOverlays;
-
-void ArchGraph::addArchPair(ArchPair ap)
-{
- //fprintf(stderr, "adding ArchPair 0x%08X,0x%08X\n", ap.arch, ap.subtype);
- fgPerArchGraph[ap] = new ArchGraph(ap);
-}
-
-void ArchGraph::setFileSystemOverlay(const std::vector<const char*>& overlays)
-{
- for (std::vector<const char*>::const_iterator it=overlays.begin(); it != overlays.end(); ++it)
- fgFileSystemOverlays.push_back(*it);
-}
-
-void ArchGraph::addRoot(const char* vpath, const std::set<ArchPair>& onlyArchs)
-{
- //fprintf(stderr, "addRoot(%s)\n", vpath);
- char completePath[MAXPATHLEN];
- const char* path = NULL;
- // check -overlay path first
- for (std::vector<const char*>::const_iterator it=fgFileSystemOverlays.begin(); it != fgFileSystemOverlays.end(); ++it) {
- strcpy(completePath, *it);
- strcat(completePath, vpath); // assumes vpath starts with '/'
- struct stat stat_buf;
- if ( stat(completePath, &stat_buf) == 0 ) {
- path = completePath;
- break;
- }
- }
- // if not found in overlay, check for -root
- if ( (path == NULL) && (fgFileSystemRoot[0] != '\0') ) {
- strcpy(completePath, fgFileSystemRoot);
- strcat(completePath, vpath); // assumes vpath starts with '/'
- struct stat stat_buf;
- if ( stat(completePath, &stat_buf) == 0 )
- path = completePath;
- }
- if ( path == NULL )
- path = vpath;
-
- try {
- //fprintf(stderr, " UniversalMachOLayout::find(%s)\n", path);
- const UniversalMachOLayout& uni = UniversalMachOLayout::find(path, &onlyArchs);
- for(std::set<ArchPair>::iterator ait = onlyArchs.begin(); ait != onlyArchs.end(); ++ait) {
- try {
- const MachOLayoutAbstraction* layout = uni.getSlice(*ait);
- if ( layout != NULL )
- fgPerArchGraph[*ait]->addRoot(path, layout);
- }
- catch (const char* msg) {
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: warning for %s can't use root '%s': %s\n", fgPerArchGraph[*ait]->archName(), path, msg);
- }
-
- }
- }
- catch (const char* msg) {
- fprintf(stderr, "update_dyld_shared_cache: warning can't use root '%s': %s\n", path, msg);
- }
-}
-
-
-
-void ArchGraph::addRoot(const char* path, const MachOLayoutAbstraction* layout)
-{
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: adding root: %s\n", path);
- DependencyNode* node = this->getNode(path);
- fRoots.insert(node);
- const MachOLayoutAbstraction* mainExecutableLayout = NULL;
- if ( layout->getFileType() == MH_EXECUTE )
- mainExecutableLayout = layout;
- node->loadDependencies(mainExecutableLayout);
- node->markNeededByRoot(node);
- if ( layout->getFileType() == MH_DYLIB )
- node->markNeededByRoot(NULL);
-}
-
-// a virtual path does not have the fgFileSystemRoot prefix
-ArchGraph::DependencyNode* ArchGraph::getNodeForVirtualPath(const char* vpath)
-{
- //fprintf(stderr, "getNodeForVirtualPath(%s)\n", vpath);
- char completePath[MAXPATHLEN];
- for (std::vector<const char*>::const_iterator it=fgFileSystemOverlays.begin(); it != fgFileSystemOverlays.end(); ++it) {
- const char* overlayPath = *it;
- // using -overlay means if /overlay/path/dylib exists use it, otherwise use /path/dylib
- strcpy(completePath, overlayPath);
- strcat(completePath, vpath); // assumes vpath starts with '/'
- struct stat stat_buf;
- if ( stat(completePath, &stat_buf) == 0 ) {
- return this->getNode(completePath);
- }
- // <rdar://problem/9279770> support when install name is a symlink
- const char* pathToSymlink = vpath;
- if ( fgFileSystemRoot[0] != '\0' ) {
- strcpy(completePath, fgFileSystemRoot);
- strcat(completePath, vpath);
- pathToSymlink = completePath;
- }
- if ( (lstat(pathToSymlink, &stat_buf) == 0) && S_ISLNK(stat_buf.st_mode) ) {
- // requested path did not exist in /overlay, but leaf of path is a symlink in /
- char pathInSymLink[MAXPATHLEN];
- size_t res = readlink(pathToSymlink, pathInSymLink, sizeof(pathInSymLink));
- if ( res != -1 ) {
- pathInSymLink[res] = '\0';
- if ( pathInSymLink[0] != '/' ) {
- char symFullPath[MAXPATHLEN];
- strcpy(symFullPath, vpath);
- char* lastSlash = strrchr(symFullPath, '/');
- if ( lastSlash != NULL ) {
- strcpy(lastSlash+1, pathInSymLink);
- // (re)try looking for what symlink points to, but in /overlay
- return this->getNodeForVirtualPath(symFullPath);
- }
- }
- }
- }
- }
-
- if ( fgFileSystemRoot[0] != '\0' ) {
- // using -root means always use /rootpath/usr/lib
- strcpy(completePath, fgFileSystemRoot);
- strcat(completePath, vpath); // assumes vpath starts with '/'
- return this->getNode(completePath);
- }
- // not found in -overlay or -root not used
- return this->getNode(vpath);
-}
-
-ArchGraph::DependencyNode* ArchGraph::getNode(const char* path)
-{
- //fprintf(stderr, "getNode(%s)\n", path);
- // look up supplied path to see if node already exists
- PathToNode::iterator pos = fNodes.find(path);
- if ( pos != fNodes.end() )
- return pos->second;
-
- // get real path
- char realPath[MAXPATHLEN];
- if ( realpath(path, realPath) == NULL )
- throwf("realpath() failed on %s\n", path);
-
- // look up real path to see if node already exists
- pos = fNodes.find(realPath);
- if ( pos != fNodes.end() ) {
- // update fAliasesMap with symlinks found
- const char* aliasPath = path;
- if ( (fgFileSystemRoot != NULL) && (strncmp(path, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) {
- aliasPath = &path[strlen(fgFileSystemRoot)];
- }
- if ( fAliasesMap.find(aliasPath) == fAliasesMap.end() ) {
- if ( strcmp(aliasPath, pos->second->getLayout()->getID().name) != 0 ) {
- fAliasesMap[strdup(aliasPath)] = pos->second->getLayout()->getID().name;
- //fprintf(stderr, "getNode() %s: added alias %s -> %s\n", archName(fArchPair), aliasPath, fAliasesMap[aliasPath]);
- }
- }
- return pos->second;
- }
-
- // still does not exist, so create a new node
- const UniversalMachOLayout& uni = UniversalMachOLayout::find(realPath);
- DependencyNode* node = new DependencyNode(this, realPath, uni.getSlice(fArchPair));
- if ( node->getLayout() == NULL ) {
- throwf("%s is missing arch %s", realPath, archName(fArchPair));
- }
- // add realpath to node map
- fNodes[node->getPath()] = node;
- // if install name is not real path, add install name to node map
- if ( (node->getLayout()->getFileType() == MH_DYLIB) && (strcmp(realPath, node->getLayout()->getID().name) != 0) ) {
- //fprintf(stderr, "adding %s node alias %s for %s\n", archName(fArchPair), node->getLayout()->getID().name, realPath);
- pos = fNodes.find(node->getLayout()->getID().name);
- if ( pos != fNodes.end() ) {
- // get uuids of two dylibs to see if this is accidental copy of a dylib or two differnent dylibs with same -install_name
- uuid_t uuid1;
- uuid_t uuid2;
- node->getLayout()->uuid(uuid1);
- pos->second->getLayout()->uuid(uuid2);
- if ( memcmp(&uuid1, &uuid2, 16) == 0 ) {
- // <rdar://problem/8305479> warn if two dylib in cache have same install_name
- char* msg;
- asprintf(&msg, "update_dyld_shared_cache: warning, found two copies of the same dylib with same install path: %s\n\t%s\n\t%s\n",
- node->getLayout()->getID().name, pos->second->getPath(), node->getPath());
- fprintf(stderr, "%s", msg);
- warnings.push_back(msg);
- }
- else {
- // <rdar://problem/12763450> update_dyld_shared_cache should fail if two images have same install name
- fprintf(stderr, "update_dyld_shared_cache: found two different dylibs with same install path: %s\n\t%s\n\t%s\n",
- node->getLayout()->getID().name, pos->second->getPath(), node->getPath());
- exit(1);
- }
- }
- else
- fNodes[node->getLayout()->getID().name] = node;
- // update fAliasesMap with symlinks found
- const char* aliasPath = realPath;
- if ( (fgFileSystemRoot != NULL) && (fgFileSystemRoot[0] != '\0') && (strncmp(realPath, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) {
- aliasPath = &realPath[strlen(fgFileSystemRoot)];
- }
- // <rdar://problem/11192810> Too many aliases in -overlay mode
- for (std::vector<const char*>::const_iterator it=fgFileSystemOverlays.begin(); it != fgFileSystemOverlays.end(); ++it) {
- const char* overlayPath = *it;
- if ( strncmp(realPath, overlayPath, strlen(overlayPath)) == 0 ) {
- aliasPath = &realPath[strlen(overlayPath)];
- break;
- }
- }
- if ( fAliasesMap.find(aliasPath) == fAliasesMap.end() ) {
- if ( strcmp(aliasPath, node->getLayout()->getID().name) != 0 ) {
- fAliasesMap[strdup(aliasPath)] = node->getLayout()->getID().name;
- //fprintf(stderr, "getNode() %s: added alias %s -> %s\n", archName(fArchPair), aliasPath, fAliasesMap[aliasPath]);
- }
- }
- }
- return node;
-}
-
-
-void ArchGraph::DependencyNode::loadDependencies(const MachOLayoutAbstraction* mainExecutableLayout)
-{
- if ( !fDependenciesLoaded ) {
- fDependenciesLoaded = true;
- // add dependencies
- const std::vector<MachOLayoutAbstraction::Library>& dependsOn = fLayout->getLibraries();
- for(std::vector<MachOLayoutAbstraction::Library>::const_iterator it = dependsOn.begin(); it != dependsOn.end(); ++it) {
- try {
- const char* dependentPath = it->name;
- if ( strncmp(dependentPath, "@executable_path/", 17) == 0 ) {
- if ( mainExecutableLayout == NULL )
- throw "@executable_path without main executable";
- // expand @executable_path path prefix
- const char* executablePath = mainExecutableLayout->getFilePath();
- char newPath[strlen(executablePath) + strlen(dependentPath)+2];
- if ( (fgFileSystemRoot != NULL) && (strncmp(executablePath, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) {
- // executablePath already has rootPath prefix, need to remove that to get to base virtual path
- strcpy(newPath, &executablePath[strlen(fgFileSystemRoot)]);
- }
- else {
- strcpy(newPath, executablePath);
- }
- char* addPoint = strrchr(newPath,'/');
- if ( addPoint != NULL )
- strcpy(&addPoint[1], &dependentPath[17]);
- else
- strcpy(newPath, &dependentPath[17]);
- dependentPath = strdup(newPath);
- }
- else if ( strncmp(dependentPath, "@loader_path/", 13) == 0 ) {
- // expand @loader_path path prefix
- char newPath[strlen(fPath) + strlen(dependentPath)+2];
- if ( (fgFileSystemRoot != NULL) && (strncmp(fPath, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) {
- // fPath already has rootPath prefix, need to remove that to get to base virtual path
- strcpy(newPath, &fPath[strlen(fgFileSystemRoot)]);
- }
- else {
- strcpy(newPath, fPath);
- }
- char* addPoint = strrchr(newPath,'/');
- if ( addPoint != NULL )
- strcpy(&addPoint[1], &dependentPath[13]);
- else
- strcpy(newPath, &dependentPath[13]);
- dependentPath = strdup(newPath);
- }
- else if ( strncmp(dependentPath, "@rpath/", 7) == 0 ) {
- throw "@rpath not supported in dyld shared cache";
- }
- // <rdar://problem/9161945> silently ignore dependents from main executables that can't be in shared cache
- bool addDependent = true;
- if ( fLayout->getFileType() == MH_EXECUTE ) {
- if ( (strncmp(dependentPath, "/usr/lib/", 9) != 0) && (strncmp(dependentPath, "/System/Library/", 16) != 0) ) {
- addDependent = false;
- }
- }
- if ( addDependent )
- fDependsOn.insert(fGraph->getNodeForVirtualPath(dependentPath));
- }
- catch (const char* msg) {
- if ( it->weakImport || ! fLayout->hasSplitSegInfo() ) {
- // ok to ignore missing weak imported dylibs from things that are
- // not going to be in the dyld shared cache
- }
- else {
- fprintf(stderr, "warning, could not bind %s because %s\n", fPath, msg);
- fDependentMissing = true;
- }
- }
- }
- // recurse
- for(std::set<DependencyNode*>::iterator it = fDependsOn.begin(); it != fDependsOn.end(); ++it) {
- (*it)->loadDependencies(mainExecutableLayout);
- }
- }
-}
-
-void ArchGraph::DependencyNode::markNeededByRoot(ArchGraph::DependencyNode* rootNode)
-{
- if ( fRootsDependentOnThis.count(rootNode) == 0 ) {
- fRootsDependentOnThis.insert(rootNode);
- for(std::set<DependencyNode*>::iterator it = fDependsOn.begin(); it != fDependsOn.end(); ++it) {
- (*it)->markNeededByRoot(rootNode);
- }
- }
-}
-
-
-
-ArchGraph::DependencyNode::DependencyNode(ArchGraph* graph, const char* path, const MachOLayoutAbstraction* layout)
- : fGraph(graph), fPath(strdup(path)), fLayout(layout), fDependenciesLoaded(false), fDependentMissing(false)
-{
- //fprintf(stderr, "new DependencyNode(0x%08X, %s)\n", graph->fArch, path);
-}
-
-uint64_t ArchGraph::maxCacheSizeForArchPair(ArchPair ap) {
- switch ( ap.arch ) {
- case CPU_TYPE_I386:
- return 0x20000000;
- case CPU_TYPE_X86_64:
- return 0x40000000;
- case CPU_TYPE_ARM:
- return ARM_SHARED_REGION_SIZE;
- case CPU_TYPE_ARM64:
- return ARM64_SHARED_REGION_SIZE;
- default: return UINT64_MAX;
- }
-}
-
-void ArchGraph::findSharedDylibs(ArchPair ap)
-{
- const PathToNode& nodes = fgPerArchGraph[ap]->fNodes;
- std::set<const MachOLayoutAbstraction*> possibleLibs;
- std::map<const MachOLayoutAbstraction*, const DependencyNode *> layoutToNode;
- //fprintf(stderr, "shared for arch %s\n", archName(ap));
- for(PathToNode::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
- DependencyNode* node = it->second;
- // <rdar://problem/6127437> put all dylibs in shared cache - not just ones used by more than one app
- if ( node->allDependentsFound() /*&& (node->useCount() > 1)*/ ) {
- const MachOLayoutAbstraction* layout = node->getLayout();
- if ( layout->isDylib() ) {
- char* msg;
- if ( sharable(layout, ap, &msg) ) {
- possibleLibs.insert(layout);
- layoutToNode[layout] = node;
- }
- else {
- if ( !iPhoneOS && (layout->getID().name[0] == '@') ) {
- // <rdar://problem/7770139> update_dyld_shared_cache should suppress warnings for embedded frameworks
- }
- else {
- warnings.push_back(msg);
- fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg);
- }
- }
- }
- }
- }
-
- // prune so that all shareable libs depend only on other shareable libs
- std::set<const MachOLayoutAbstraction*>& sharedLibs = fgPerArchGraph[ap]->fSharedDylibs;
- std::map<const MachOLayoutAbstraction*,bool> shareableMap;
- uint64_t totalLibSize = 0;
- for (std::set<const MachOLayoutAbstraction*>::iterator lit = possibleLibs.begin(); lit != possibleLibs.end(); ++lit) {
- if ( canBeShared(*lit, ap, possibleLibs, shareableMap) ) {
- totalLibSize += (*lit)->getVMSize();
- sharedLibs.insert(*lit);
- }
- }
-
-#if 0 // disable auto-eviction because it happens before linkedit optimization which means it is overly conservative.
-
- // Check to see if the unoptimized cache size is too large, if so trim out some libraries
- uint64_t maxCacheSize = maxCacheSizeForArchPair(ap);
- if (totalLibSize > maxCacheSize) {
- fprintf(stderr, "update_dyld_shared_cache: unoptimized %s shared cache overflow, total VM space: %lldMB (max=%lldMB)\n", archName(ap), totalLibSize/(1024*1024), maxCacheSize/(1024*1024));
- std::vector<const MachOLayoutAbstraction*> removableLibs;
-
- for (const MachOLayoutAbstraction* layout : sharedLibs) {
- // Every library uses itself, and every MH_DYLIB has an extra useCount, so we know useCount of 2 implies nothing else in the shared cache uses it
- if (layoutToNode[layout]->useCount() == 2) {
- if ( layoutToNode[layout]->dependsOnDylibList() ) {
- removableLibs.push_back(layout);
- //fprintf(stderr, " possible to evict: %s\n", layout->getID().name);
- }
- }
- }
-
- std::sort(removableLibs.begin(), removableLibs.end(), [](const MachOLayoutAbstraction* a, const MachOLayoutAbstraction* b){
- return a->getVMSize() < b->getVMSize();
- });
-
- while ( (totalLibSize > maxCacheSize) && !removableLibs.empty() ) {
- const MachOLayoutAbstraction* largestRemovableLib = removableLibs.back();
- removableLibs.pop_back();
- if ( largestRemovableLib->getVMSize() > 1024*1024 )
- fprintf(stderr, "update_dyld_shared_cache: evicting % 3lldMB leaf dylib %s\n", largestRemovableLib->getVMSize()/(1024*1024), largestRemovableLib->getID().name);
- else
- fprintf(stderr, "update_dyld_shared_cache: evicting % 3lldKB leaf dylib %s\n", largestRemovableLib->getVMSize()/1024, largestRemovableLib->getID().name);
- sharedLibs.erase(largestRemovableLib);
- totalLibSize -= largestRemovableLib->getVMSize();
- }
- fprintf(stderr, "update_dyld_shared_cache: unoptimized %s shared cache reduced to total VM space: %lldMB\n", archName(ap), totalLibSize/1024/1024);
- }
-#endif
-}
-
-const char* ArchGraph::archName(ArchPair ap)
-{
- switch ( ap.arch ) {
- case CPU_TYPE_I386:
- return "i386";
- case CPU_TYPE_X86_64:
- switch ( ap.subtype ) {
- case CPU_SUBTYPE_X86_64_H:
- return "x86_64h";
- default:
- return "x86_64";
- }
- case CPU_TYPE_ARM:
- switch ( ap.subtype ) {
- case CPU_SUBTYPE_ARM_V4T:
- return "armv4t";
- case CPU_SUBTYPE_ARM_V6:
- return "armv6";
- case CPU_SUBTYPE_ARM_V5TEJ:
- return "armv5";
- case CPU_SUBTYPE_ARM_XSCALE:
- return "arm-xscale";
- case CPU_SUBTYPE_ARM_V7:
- return "armv7";
- case CPU_SUBTYPE_ARM_V7F:
- return "armv7f";
- case CPU_SUBTYPE_ARM_V7K:
- return "armv7k";
- case CPU_SUBTYPE_ARM_V7S:
- return "armv7s";
- default:
- return "arm";
- }
- case CPU_TYPE_ARM64:
- return "arm64";
- default:
- return "unknown";
- }
-}
-
-bool ArchGraph::sharable(const MachOLayoutAbstraction* layout, ArchPair ap, char** msg)
-{
- int trustErr = layout->notTrusted();
- if ( ! layout->isTwoLevelNamespace() )
- asprintf(msg, "can't put %s in shared cache because it was built -flat_namespace", layout->getID().name);
- else if ( ! layout->inSharableLocation() )
- asprintf(msg, "can't put %s in shared cache because its -install_name is not in /usr/lib or /System/Library", layout->getID().name);
- else if ( ! layout->hasSplitSegInfo() )
- asprintf(msg, "can't put %s in shared cache because it was not built for %s or later", layout->getID().name, (iPhoneOS ? "iPhoneOS 3.1" : "MacOSX 10.5"));
- else if ( rootless == true && trustErr != 0 )
- asprintf(msg, "can't put %s in shared cache because it is not trusted: %s", layout->getFilePath(), strerror(trustErr));
- else if ( layout->hasDynamicLookupLinkage() )
- asprintf(msg, "can't put %s in shared cache because it was built with '-undefined dynamic_lookup'", layout->getID().name);
- else if ( layout->hasMainExecutableLookupLinkage() )
- asprintf(msg, "can't put %s in shared cache because it was built with '-bundle_loader'", layout->getID().name);
- else
- return true;
- return false;
-}
-
-bool ArchGraph::canBeShared(const MachOLayoutAbstraction* layout, ArchPair ap, const std::set<const MachOLayoutAbstraction*>& possibleLibs, std::map<const MachOLayoutAbstraction*, bool>& shareableMap)
-{
- // check map which is a cache of results
- std::map<const MachOLayoutAbstraction*, bool>::iterator mapPos = shareableMap.find(layout);
- if ( mapPos != shareableMap.end() ) {
- return mapPos->second;
- }
- // see if possible
- if ( possibleLibs.count(layout) == 0 ) {
- shareableMap[layout] = false;
- char* msg;
- if ( sharable(layout, ap, &msg) )
- asprintf(&msg, "can't put %s in shared cache, unknown reason", layout->getID().name);
- warnings.push_back(msg);
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg);
- return false;
- }
- // look recursively
- shareableMap[layout] = true; // mark this shareable early in case of circular references
- const PathToNode& nodes = fgPerArchGraph[ap]->fNodes;
- const std::vector<MachOLayoutAbstraction::Library>& dependents = layout->getLibraries();
- for (std::vector<MachOLayoutAbstraction::Library>::const_iterator dit = dependents.begin(); dit != dependents.end(); ++dit) {
- PathToNode::const_iterator pos = nodes.find(dit->name);
- if ( pos == nodes.end() ) {
- // path from load command does not match any loaded dylibs, maybe there is a temp symlink
- char realPath[MAXPATHLEN];
- if ( realpath(dit->name, realPath) != NULL ) {
- if ( nodes.find(realPath) != nodes.end() )
- continue;
- }
- // handle weak imported dylibs not found
- if ( dit->weakImport )
- continue;
- shareableMap[layout] = false;
- char* msg;
- asprintf(&msg, "can't put %s in shared cache because it depends on %s which can't be found", layout->getID().name, dit->name);
- warnings.push_back(msg);
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg);
- return false;
- }
- else {
- if ( ! canBeShared(pos->second->getLayout(), ap, possibleLibs, shareableMap) ) {
- shareableMap[layout] = false;
- char* msg;
- asprintf(&msg, "can't put %s in shared cache because it depends on %s which can't be in shared cache", layout->getID().name, dit->name);
- warnings.push_back(msg);
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg);
- return false;
- }
- }
- }
- return true;
-}
-
-
-
-class StringPool
-{
-public:
- StringPool();
- const char* getBuffer();
- uint32_t size();
- uint32_t add(const char* str);
- uint32_t addUnique(const char* str);
- const char* stringAtIndex(uint32_t) const;
-
-private:
- typedef std::unordered_map<const char*, uint32_t, CStringHash, CStringEquals> StringToOffset;
-
- char* fBuffer;
- uint32_t fBufferAllocated;
- uint32_t fBufferUsed;
- StringToOffset fUniqueStrings;
-};
-
-
-StringPool::StringPool()
- : fBufferUsed(0), fBufferAllocated(128*1024*1024)
-{
- fBuffer = (char*)malloc(fBufferAllocated);
-}
-
-uint32_t StringPool::add(const char* str)
-{
- uint32_t len = strlen(str);
- if ( (fBufferUsed + len + 1) > fBufferAllocated ) {
- // grow buffer
- throw "string buffer exhausted";
- }
- strcpy(&fBuffer[fBufferUsed], str);
- uint32_t result = fBufferUsed;
- fUniqueStrings[&fBuffer[fBufferUsed]] = result;
- fBufferUsed += len+1;
- return result;
-}
-
-uint32_t StringPool::addUnique(const char* str)
-{
- StringToOffset::iterator pos = fUniqueStrings.find(str);
- if ( pos != fUniqueStrings.end() )
- return pos->second;
- else {
- //fprintf(stderr, "StringPool::addUnique() new string: %s\n", str);
- return this->add(str);
- }
-}
-
-uint32_t StringPool::size()
-{
- return fBufferUsed;
-}
-
-const char* StringPool::getBuffer()
-{
- return fBuffer;
-}
-
-const char* StringPool::stringAtIndex(uint32_t index) const
-{
- return &fBuffer[index];
-}
-
-
-
-struct LocalSymbolInfo
-{
- uint32_t dylibOffset;
- uint32_t nlistStartIndex;
- uint32_t nlistCount;
-};
-
-
-template <typename A>
-class SharedCache
-{
-public:
- SharedCache(ArchGraph* graph, const char* rootPath, const std::vector<const char*>& overlayPaths, const char* cacheDir, bool explicitCacheDir,
- bool alphaSort, bool verify, bool optimize, uint64_t dyldBaseAddress);
- bool update(bool force, bool optimize, bool deleteExistingFirst, int archIndex,
- int archCount, bool keepSignatures, bool dontMapLocalSymbols);
- void writeCacheFile(const char *cacheFilePath, uint8_t *cacheFileBuffer, uint32_t cacheFileSize, bool deleteOldCache);
- static const char* cacheFileSuffix(bool optimized, const char* archName);
-
- // vm address = address AS WRITTEN into the cache
- // mapped address = address AS MAPPED into the update process only
- // file offset = offset relative to start of cache file
- void * mappedAddressForVMAddress(uint64_t vmaddr);
- uint64_t VMAddressForMappedAddress(const void *mapaddr);
- uint64_t cacheFileOffsetForVMAddress(uint64_t addr) const;
- uint64_t VMAddressForCacheFileOffset(uint64_t addr) const;
-
- static const char* archName();
-
-private:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
-
- bool notUpToDate(const char* path, unsigned int aliasCount);
- bool notUpToDate(const void* cache, unsigned int aliasCount);
- uint8_t* optimizeLINKEDIT(bool keepSignatures, bool dontMapLocalSymbols);
- void optimizeObjC(std::vector<void*>& pointersInData);
-
- static void getSharedCacheBasAddresses(cpu_type_t arch, uint64_t* baseReadOnly, uint64_t* baseWritable);
- static cpu_type_t arch();
- static uint64_t sharedRegionStartAddress();
- static uint64_t sharedRegionSize();
- static uint64_t sharedRegionStartWritableAddress(uint64_t);
- static uint64_t sharedRegionStartReadOnlyAddress(uint64_t, uint64_t);
- static uint64_t getWritableSegmentNewAddress(uint64_t proposedNewAddress, uint64_t originalAddress, uint64_t executableSlide);
- static bool addCacheSlideInfo();
- static uint64_t pathHash(const char*);
-
- static uint64_t pageAlign(uint64_t addr);
- static uint64_t regionAlign(uint64_t addr);
- static uint64_t pageAlign4KB(uint64_t addr);
- void assignNewBaseAddresses(bool verify);
-
- struct LayoutInfo {
- const MachOLayoutAbstraction* layout;
- std::vector<const char*> aliases;
- dyld_cache_image_info info;
- };
-
- struct ByNameSorter {
- bool operator()(const LayoutInfo& left, const LayoutInfo& right)
- { return (strcmp(left.layout->getID().name, right.layout->getID().name) < 0); }
- };
-
- struct ByAddressSorter {
- bool operator()(const LayoutInfo& left, const LayoutInfo& right) {
- return (left.layout->getSegments()[0].newAddress() < right.layout->getSegments()[0].newAddress());
- }
- };
-
- struct ByCStringSectionSizeSorter {
- bool operator()(const LayoutInfo& left, const LayoutInfo& right) {
- const std::vector<MachOLayoutAbstraction::Segment>& segs_l =
- left.layout->getSegments();
- const std::vector<MachOLayoutAbstraction::Segment>& segs_r =
- right.layout->getSegments();
- if (segs_l.size() == 0 || segs_r.size() == 0) {
- // one image has no segments
- return segs_l.size() > segs_r.size();
- }
- const macho_header<P> *mh_l = (macho_header<P>*)segs_l[0].mappedAddress();
- const macho_header<P> *mh_r = (macho_header<P>*)segs_r[0].mappedAddress();
- const macho_section<P> *cstring_l = mh_l->getSection("__TEXT", "__cstring");
- const macho_section<P> *cstring_r = mh_r->getSection("__TEXT", "__cstring");
- if (!cstring_l || !cstring_r) {
- // one image has no cstrings
- return cstring_l && !cstring_r;
- }
-
- return cstring_l->size() > cstring_r->size();
- }
- };
-
- struct Sorter {
- Sorter(std::map<const MachOLayoutAbstraction*, uint32_t>& map): fMap(map) {}
- bool operator()(const LayoutInfo& left, const LayoutInfo& right) {
- return (fMap[left.layout] < fMap[right.layout]);
- }
- private:
- std::map<const MachOLayoutAbstraction*, uint32_t>& fMap;
- };
-
-
- ArchGraph* fArchGraph;
- const bool fVerify;
- bool fExistingIsNotUpToDate;
- bool fCacheFileInFinalLocation;
- const char* fCacheFilePath;
- uint8_t* fExistingCacheForVerification;
- std::vector<LayoutInfo> fDylibs;
- std::vector<LayoutInfo> fDylibAliases;
- std::vector<shared_file_mapping_np> fMappings;
- std::vector<macho_nlist<P> > fUnmappedLocalSymbols;
- StringPool fUnmappedLocalsStringPool;
- std::vector<LocalSymbolInfo> fLocalSymbolInfos;
- uint32_t fHeaderSize;
- uint8_t* fInMemoryCache;
- uint64_t fDyldBaseAddress;
- uint64_t fLinkEditsTotalUnoptimizedSize;
- uint64_t fLinkEditsStartAddress;
- MachOLayoutAbstraction::Segment* fFirstLinkEditSegment;
- uint32_t fOffsetOfBindInfoInCombinedLinkedit;
- uint32_t fOffsetOfWeakBindInfoInCombinedLinkedit;
- uint32_t fOffsetOfLazyBindInfoInCombinedLinkedit;
- uint32_t fOffsetOfExportInfoInCombinedLinkedit;
- uint32_t fOffsetOfOldSymbolTableInfoInCombinedLinkedit;
- uint32_t fSizeOfOldSymbolTableInfoInCombinedLinkedit;
- uint32_t fOffsetOfOldExternalRelocationsInCombinedLinkedit;
- uint32_t fSizeOfOldExternalRelocationsInCombinedLinkedit;
- uint32_t fOffsetOfOldIndirectSymbolsInCombinedLinkedit;
- uint32_t fSizeOfOldIndirectSymbolsInCombinedLinkedit;
- uint32_t fOffsetOfOldStringPoolInCombinedLinkedit;
- uint32_t fSizeOfOldStringPoolInCombinedLinkedit;
- uint32_t fOffsetOfFunctionStartsInCombinedLinkedit;
- uint32_t fSizeOfFunctionStartsInCombinedLinkedit;
- uint32_t fOffsetOfDataInCodeInCombinedLinkedit;
- uint32_t fSizeOfDataInCodeInCombinedLinkedit;
- uint32_t fLinkEditsTotalOptimizedSize;
- uint32_t fUnmappedLocalSymbolsSize;
-};
-
-
-// Access a section containing a list of pointers
-template <typename A, typename T>
-class PointerSection
-{
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
-
- SharedCache<A>* const fCache;
- const macho_section<P>* const fSection;
- pint_t * const fBase;
- pint_t fCount;
-
-public:
- PointerSection(SharedCache<A>* cache, const macho_header<P>* header,
- const char *segname, const char *sectname)
- : fCache(cache)
- , fSection(header->getSection(segname, sectname))
- , fBase(fSection ? (pint_t *)cache->mappedAddressForVMAddress(fSection->addr()) : 0)
- , fCount(fSection ? fSection->size() / sizeof(pint_t) : 0)
- {
- }
-
- pint_t count() const { return fCount; }
-
- pint_t getVMAddress(pint_t index) const {
- if (index >= fCount) throwf("index out of range");
- return P::getP(fBase[index]);
- }
-
- T get(pint_t index) const {
- return (T)fCache->mappedAddressForVMAddress(getVMAddress(index));
- }
-
- void setVMAddress(pint_t index, pint_t value) {
- if (index >= fCount) throwf("index out of range");
- P::setP(fBase[index], value);
- }
-
- void removeNulls() {
- pint_t shift = 0;
- for (pint_t i = 0; i < fCount; i++) {
- pint_t value = fBase[i];
- if (value) {
- fBase[i-shift] = value;
- } else {
- shift++;
- }
- }
- fCount -= shift;
- const_cast<macho_section<P>*>(fSection)->set_size(fCount * sizeof(pint_t));
- }
-};
-
-// Access a section containing an array of structures
-template <typename A, typename T>
-class ArraySection
-{
- typedef typename A::P P;
-
- SharedCache<A>* const fCache;
- const macho_section<P>* const fSection;
- T * const fBase;
- uint64_t const fCount;
-
-public:
- ArraySection(SharedCache<A>* cache, const macho_header<P>* header,
- const char *segname, const char *sectname)
- : fCache(cache)
- , fSection(header->getSection(segname, sectname))
- , fBase(fSection ? (T *)cache->mappedAddressForVMAddress(fSection->addr()) : 0)
- , fCount(fSection ? fSection->size() / sizeof(T) : 0)
- {
- }
-
- uint64_t count() const { return fCount; }
-
- T& get(uint64_t index) const {
- if (index >= fCount) throwf("index out of range");
- return fBase[index];
- }
-};
-
-
-// GrP fixme
-#include "ObjCLegacyAbstraction.hpp"
-#include "ObjCModernAbstraction.hpp"
-
-
-
-template <> cpu_type_t SharedCache<x86>::arch() { return CPU_TYPE_I386; }
-template <> cpu_type_t SharedCache<x86_64>::arch() { return CPU_TYPE_X86_64; }
-template <> cpu_type_t SharedCache<arm>::arch() { return CPU_TYPE_ARM; }
-template <> cpu_type_t SharedCache<arm64>::arch() { return CPU_TYPE_ARM64; }
-
-template <> uint64_t SharedCache<x86>::sharedRegionStartAddress() { return 0x90000000; }
-template <> uint64_t SharedCache<x86_64>::sharedRegionStartAddress() { return 0x7FFF80000000LL; }
-template <> uint64_t SharedCache<arm>::sharedRegionStartAddress() { return ARM_SHARED_REGION_START; }
-template <> uint64_t SharedCache<arm64>::sharedRegionStartAddress() { return ARM64_SHARED_REGION_START; }
-
-template <> uint64_t SharedCache<x86>::sharedRegionSize() { return 0x20000000; }
-template <> uint64_t SharedCache<x86_64>::sharedRegionSize() { return 0x40000000; }
-template <> uint64_t SharedCache<arm>::sharedRegionSize() { return ARM_SHARED_REGION_SIZE; }
-template <> uint64_t SharedCache<arm64>::sharedRegionSize() { return ARM64_SHARED_REGION_SIZE; }
-
-template <> uint64_t SharedCache<x86>::sharedRegionStartWritableAddress(uint64_t exEnd) { return exEnd + 0x04000000; }
-template <> uint64_t SharedCache<x86_64>::sharedRegionStartWritableAddress(uint64_t exEnd) { return 0x7FFF70000000LL; }
-template <> uint64_t SharedCache<arm>::sharedRegionStartWritableAddress(uint64_t exEnd) { return (exEnd + 16383) & (-16384); }
-template <> uint64_t SharedCache<arm64>::sharedRegionStartWritableAddress(uint64_t exEnd) { return exEnd; }
-
-template <> uint64_t SharedCache<x86>::sharedRegionStartReadOnlyAddress(uint64_t wrEnd, uint64_t exEnd) { return wrEnd + 0x04000000; }
-template <> uint64_t SharedCache<x86_64>::sharedRegionStartReadOnlyAddress(uint64_t wrEnd, uint64_t exEnd){ return exEnd; }
-template <> uint64_t SharedCache<arm>::sharedRegionStartReadOnlyAddress(uint64_t wrEnd, uint64_t exEnd) { return (wrEnd + 16383) & (-16384); }
-template <> uint64_t SharedCache<arm64>::sharedRegionStartReadOnlyAddress(uint64_t wrEnd, uint64_t exEnd) { return (wrEnd + 16383) & (-16384); }
-
-template <> const char* SharedCache<x86>::archName() { return "i386"; }
-template <> const char* SharedCache<x86_64>::archName() { return "x86_64"; }
-template <> const char* SharedCache<arm>::archName() { return "arm"; }
-template <> const char* SharedCache<arm64>::archName() { return "arm64"; }
-
-template <> const char* SharedCache<x86>::cacheFileSuffix(bool, const char* archName) { return archName; }
-template <> const char* SharedCache<x86_64>::cacheFileSuffix(bool, const char* archName){ return archName; }
-template <> const char* SharedCache<arm>::cacheFileSuffix(bool, const char* archName) { return archName; }
-template <> const char* SharedCache<arm64>::cacheFileSuffix(bool, const char* archName) { return archName; }
-
-template <> uint64_t SharedCache<x86>::pageAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); }
-template <> uint64_t SharedCache<x86_64>::pageAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); }
-template <> uint64_t SharedCache<arm>::pageAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); }
-template <> uint64_t SharedCache<arm64>::pageAlign(uint64_t addr) { return ( (addr + 16383) & (-16384) ); }
-
-template <> uint64_t SharedCache<x86>::regionAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); }
-template <> uint64_t SharedCache<x86_64>::regionAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); }
-template <> uint64_t SharedCache<arm>::regionAlign(uint64_t addr) { return ( (addr + 16383) & (-16384) ); }
-template <> uint64_t SharedCache<arm64>::regionAlign(uint64_t addr) { return ( (addr + 16383) & (-16384) ); }
-
-
-template <typename A>
-uint64_t SharedCache<A>::pageAlign4KB(uint64_t addr) { return ( (addr + 4095) & (-4096) ); }
-
-template <typename A>
-SharedCache<A>::SharedCache(ArchGraph* graph, const char* rootPath, const std::vector<const char*>& overlayPaths, const char* cacheDir, bool explicitCacheDir, bool alphaSort, bool verify, bool optimize, uint64_t dyldBaseAddress)
- : fArchGraph(graph), fVerify(verify), fExistingIsNotUpToDate(true),
- fCacheFileInFinalLocation(rootPath[0] == '\0'), fCacheFilePath(NULL),
- fExistingCacheForVerification(NULL), fDyldBaseAddress(dyldBaseAddress),
- fOffsetOfBindInfoInCombinedLinkedit(0), fOffsetOfWeakBindInfoInCombinedLinkedit(0),
- fOffsetOfLazyBindInfoInCombinedLinkedit(0), fOffsetOfExportInfoInCombinedLinkedit(0),
- fOffsetOfOldSymbolTableInfoInCombinedLinkedit(0), fSizeOfOldSymbolTableInfoInCombinedLinkedit(0),
- fOffsetOfOldExternalRelocationsInCombinedLinkedit(0), fSizeOfOldExternalRelocationsInCombinedLinkedit(0),
- fOffsetOfOldIndirectSymbolsInCombinedLinkedit(0), fSizeOfOldIndirectSymbolsInCombinedLinkedit(0),
- fOffsetOfOldStringPoolInCombinedLinkedit(0), fSizeOfOldStringPoolInCombinedLinkedit(0),
- fOffsetOfFunctionStartsInCombinedLinkedit(0), fSizeOfFunctionStartsInCombinedLinkedit(0),
- fOffsetOfDataInCodeInCombinedLinkedit(0), fSizeOfDataInCodeInCombinedLinkedit(0),
- fUnmappedLocalSymbolsSize(0)
-{
- if ( fArchGraph->getArchPair().arch != arch() )
- throwf("SharedCache object is wrong architecture: 0x%08X vs 0x%08X", fArchGraph->getArchPair().arch, arch());
-
- // build vector of all shared dylibs
- unsigned int aliasCount = 0;
- std::set<const MachOLayoutAbstraction*>& dylibs = fArchGraph->getSharedDylibs();
- ArchGraph::StringToString& aliases = fArchGraph->getDylibAliases();
- for(std::set<const MachOLayoutAbstraction*>::iterator it = dylibs.begin(); it != dylibs.end(); ++it) {
- const MachOLayoutAbstraction* lib = *it;
- LayoutInfo temp;
- temp.layout = lib;
- temp.info.address = 0;
- temp.info.inode = lib->getInode();
- temp.info.modTime = lib->getLastModTime();
- if ( iPhoneOS ) {
- temp.info.inode = pathHash(lib->getID().name);
- temp.info.modTime = 0;
- }
- temp.info.pathFileOffset = lib->getNameFileOffset(); // for now this is the offset within the dylib
- for(ArchGraph::StringToString::iterator ait = aliases.begin(); ait != aliases.end(); ++ait) {
- if ( strcmp(ait->second, lib->getID().name) == 0 ) {
- temp.aliases.push_back(ait->first);
- ++aliasCount;
- }
- }
- fDylibs.push_back(temp);
- }
-
- // create path to cache file
- char cachePathCanonical[MAXPATHLEN];
- strcpy(cachePathCanonical, cacheDir);
- if ( cachePathCanonical[strlen(cachePathCanonical)-1] != '/' )
- strcat(cachePathCanonical, "/");
- strcat(cachePathCanonical, DYLD_SHARED_CACHE_BASE_NAME);
- strcat(cachePathCanonical, cacheFileSuffix(optimize, fArchGraph->archName()));
- char cachePath[MAXPATHLEN];
- if ( explicitCacheDir ) {
- fCacheFilePath = strdup(cachePathCanonical);
- }
- else if ( overlayPaths.size() == 1 ) {
- // if no -cache_dir and exactly on -overlay, write cache file into that overlay dir
- strcpy(cachePath, overlayPaths[0]);
- strcat(cachePath, "/");
- strcat(cachePath, cachePathCanonical);
- fCacheFilePath = strdup(cachePath);
- }
- else if ( rootPath[0] != '\0' ) {
- strcpy(cachePath, rootPath);
- strcat(cachePath, "/");
- strcat(cachePath, cachePathCanonical);
- fCacheFilePath = strdup(cachePath);
- }
- else {
- fCacheFilePath = strdup(cachePathCanonical);
- }
-
- // If the path we are writing to is trusted then our sources need to be trusted
- // <rdar://problem/21166835> Can't update the update_dyld_shared_cache on a non-boot volume
- rootless = rootless_check_trusted(fCacheFilePath);
-
- if ( overlayPaths.size() == 1 ) {
- // in overlay mode if there already is a cache file in the overlay,
- // check if it is up to date.
- struct stat stat_buf;
- if ( stat(fCacheFilePath, &stat_buf) == 0 ) {
- fExistingIsNotUpToDate = this->notUpToDate(fCacheFilePath, aliasCount);
- }
- else if ( rootPath[0] != '\0' ) {
- // using -root and -overlay, but no cache file in overlay, check one in -root
- char cachePathRoot[MAXPATHLEN];
- strcpy(cachePathRoot, rootPath);
- strcat(cachePathRoot, "/");
- strcat(cachePathRoot, cachePathCanonical);
- fExistingIsNotUpToDate = this->notUpToDate(cachePathRoot, aliasCount);
- }
- else {
- // uisng -overlay, but no cache file in overlay, check one in boot volume
- fExistingIsNotUpToDate = this->notUpToDate(cachePathCanonical, aliasCount);
- }
- }
- else {
- fExistingIsNotUpToDate = this->notUpToDate(fCacheFilePath, aliasCount);
- }
-
- // sort shared dylibs
- if ( verify ) {
- // already sorted by notUpToDate()
- }
- else if ( alphaSort ) {
- std::sort(fDylibs.begin(), fDylibs.end(), ByNameSorter());
- }
- else {
- // random sort for Address Space Randomization
- std::map<const MachOLayoutAbstraction*, uint32_t> map;
- for(typename std::vector<struct LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it)
- map[it->layout] = arc4random();
- std::sort(fDylibs.begin(), fDylibs.end(), Sorter(map));
- }
-
- // assign segments in each dylib a new address
- this->assignNewBaseAddresses(verify);
-
- // calculate where string pool offset will start
- // calculate cache file header size
- fHeaderSize = sizeof(dyld_cache_header)
- + fMappings.size()*sizeof(shared_file_mapping_np)
- + (fDylibs.size()+aliasCount)*sizeof(dyld_cache_image_info);
- const uint64_t baseHeaderSize = fHeaderSize;
- //fprintf(stderr, "aliasCount=%d, fHeaderSize=0x%08X\n", aliasCount, fHeaderSize);
- // build list of aliases and compute where each ones path string will go
- for(typename std::vector<struct LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- for(std::vector<const char*>::const_iterator ait = it->aliases.begin(); ait != it->aliases.end(); ++ait) {
- LayoutInfo temp = *it;
- // alias looks just like real dylib, but has a different name string
- const char* aliasPath = *ait;
- temp.aliases.clear();
- temp.aliases.push_back(aliasPath);
- temp.info.pathFileOffset = fHeaderSize;
- if ( iPhoneOS ) {
- temp.info.inode = pathHash(aliasPath);
- temp.info.modTime = 0;
- }
- fDylibAliases.push_back(temp);
- fHeaderSize += strlen(aliasPath)+1;
- }
- }
- std::sort(fDylibAliases.begin(), fDylibAliases.end(), ByNameSorter());
- //fprintf(stderr, "fHeaderSize=0x%08X, fDylibAliases.size()=%lu\n", fHeaderSize, fDylibAliases.size());
- fHeaderSize = pageAlign(fHeaderSize);
-
- // check that cache we are about to create for verification purposes has same layout as existing cache
- if ( verify ) {
- // if no existing cache, say so
- if ( fExistingCacheForVerification == NULL ) {
- throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify because cache file does not exist in /var/db/dyld/\n",
- getpid(), fArchGraph->archName());
- }
- const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)fExistingCacheForVerification;
- const dyldCacheImageInfo<E>* cacheEntry = (dyldCacheImageInfo<E>*)(fExistingCacheForVerification + header->imagesOffset());
- for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it, ++cacheEntry) {
- if ( cacheEntry->address() != it->layout->getSegments()[0].newAddress() ) {
- throwf("update_dyld_shared_cache[%u] warning: for arch=%s, could not verify cache because start address of %s is 0x%llX in cache, but should be 0x%llX\n",
- getpid(), fArchGraph->archName(), it->layout->getID().name, cacheEntry->address(), it->layout->getSegments()[0].newAddress());
- }
- }
- }
-
-
- if ( fHeaderSize > FIRST_DYLIB_TEXT_OFFSET )
- throwf("header size overflow: allowed=0x%08X, base=0x%08llX, aliases=0x%08llX", FIRST_DYLIB_TEXT_OFFSET, baseHeaderSize, fHeaderSize-baseHeaderSize);
-}
-
-
-template <typename A>
-uint64_t SharedCache<A>::getWritableSegmentNewAddress(uint64_t proposedNewAddress, uint64_t originalAddress, uint64_t executableSlide)
-{
- return proposedNewAddress;
-}
-
-template <typename A>
-uint64_t SharedCache<A>::pathHash(const char* path)
-{
- uint64_t sum = 0;
- for (const char* s=path; *s != '\0'; ++s)
- sum += sum*4 + *s;
- return sum;
-}
-
-
-template <typename A>
-void SharedCache<A>::assignNewBaseAddresses(bool verify)
-{
- // first layout TEXT for dylibs
- const uint64_t startExecuteAddress = sharedRegionStartAddress();
- uint64_t currentExecuteAddress = startExecuteAddress + FIRST_DYLIB_TEXT_OFFSET;
- for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- std::vector<MachOLayoutAbstraction::Segment>& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments();
- for (int i=0; i < segs.size(); ++i) {
- MachOLayoutAbstraction::Segment& seg = segs[i];
- seg.reset();
- if ( seg.executable() && !seg.writable() ) {
- // <rdar://problem/15947734> Some dylib require extra alignment
- currentExecuteAddress = (currentExecuteAddress + seg.alignment() - 1) & (-seg.alignment());
- // __TEXT segment
- if ( it->info.address == 0 )
- it->info.address = currentExecuteAddress;
- seg.setNewAddress(currentExecuteAddress);
- currentExecuteAddress += pageAlign(seg.size());
- }
- }
- }
- // align __TEXT region
- currentExecuteAddress = regionAlign(currentExecuteAddress);
-
-#define DENSE_PACK 0
- // layout __DATA* segments
- std::vector<MachOLayoutAbstraction::Segment*> dataSegs;
- std::vector<MachOLayoutAbstraction::Segment*> dataConstSegs;
- std::vector<MachOLayoutAbstraction::Segment*> dataDirtySegs;
- const uint64_t startWritableAddress = sharedRegionStartWritableAddress(currentExecuteAddress);
- uint64_t currentWritableAddress = startWritableAddress;
- for (const LayoutInfo& info : fDylibs ) {
- for (MachOLayoutAbstraction::Segment& seg : ((MachOLayoutAbstraction*)(info.layout))->getSegments()) {
- if ( seg.writable() ) {
- if ( seg.executable() )
- throw "found writable and executable segment";
- seg.reset();
- if ( strcmp(seg.name(), "__DATA_CONST") == 0 )
- dataConstSegs.push_back(&seg);
- else if ( strcmp(seg.name(), "__DATA_DIRTY") == 0 )
- dataDirtySegs.push_back(&seg);
- else
- dataSegs.push_back(&seg);
- }
- }
- }
- // coalesce all __DATA_CONST segments
- for (MachOLayoutAbstraction::Segment* seg : dataConstSegs) {
- #if DENSE_PACK
- // start segment at needed alignment
- currentWritableAddress = (currentWritableAddress + seg->sectionsAlignment() - 1) & (-seg->sectionsAlignment());
- seg->setNewAddress(currentWritableAddress);
- // pack together
- uint64_t justSectionsSize = seg->sectionsSize();
- currentWritableAddress = seg->newAddress() + justSectionsSize;
- seg->setSize(justSectionsSize);
- if ( seg->fileSize() > justSectionsSize )
- seg->setFileSize(justSectionsSize);
- #else
- seg->setNewAddress(currentWritableAddress);
- // pack to 4KB pages
- currentWritableAddress = pageAlign4KB(seg->newAddress() + seg->size());
- #endif
- }
- #if DENSE_PACK
- currentWritableAddress = pageAlign4KB(currentWritableAddress);
- #endif
- // coalesce all __DATA segments
- for (MachOLayoutAbstraction::Segment* seg : dataSegs) {
- #if DENSE_PACK
- // start segment at needed alignment
- currentWritableAddress = (currentWritableAddress + seg->sectionsAlignment() - 1) & (-seg->sectionsAlignment());
- seg->setNewAddress(currentWritableAddress);
- // pack together
- uint64_t justSectionsSize = seg->sectionsSize();
- currentWritableAddress = seg->newAddress() + justSectionsSize;
- seg->setSize(justSectionsSize);
- if ( seg->fileSize() > justSectionsSize )
- seg->setFileSize(justSectionsSize);
- #else
- seg->setNewAddress(currentWritableAddress);
- // pack to 4KB pages
- currentWritableAddress = pageAlign4KB(seg->newAddress() + seg->size());
- #endif
- }
- #if DENSE_PACK
- currentWritableAddress = pageAlign4KB(currentWritableAddress);
- #endif
- // coalesce all __DATA_DIRTY segments
- for (MachOLayoutAbstraction::Segment* seg : dataDirtySegs) {
- // start segment at needed alignment
- currentWritableAddress = (currentWritableAddress + seg->sectionsAlignment() - 1) & (-seg->sectionsAlignment());
- seg->setNewAddress(currentWritableAddress);
- // pack together
- uint64_t justSectionsSize = seg->sectionsSize();
- currentWritableAddress = seg->newAddress() + justSectionsSize;
- seg->setSize(justSectionsSize);
- if ( seg->fileSize() > justSectionsSize )
- seg->setFileSize(justSectionsSize);
- }
- // align __DATA region
- currentWritableAddress = regionAlign(currentWritableAddress);
-
- // layout all read-only (but not LINKEDIT) segments
- const uint64_t startReadOnlyAddress = sharedRegionStartReadOnlyAddress(currentWritableAddress, currentExecuteAddress);
- uint64_t currentReadOnlyAddress = startReadOnlyAddress;
- for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- std::vector<MachOLayoutAbstraction::Segment>& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments();
- for(int i=0; i < segs.size(); ++i) {
- MachOLayoutAbstraction::Segment& seg = segs[i];
- if ( seg.readable() && !seg.writable() && !seg.executable() && (strcmp(seg.name(), "__LINKEDIT") != 0) ) {
- // __UNICODE segment
- seg.setNewAddress(currentReadOnlyAddress);
- currentReadOnlyAddress += pageAlign(seg.size());
- }
- }
- }
-
- // layout all LINKEDIT segments at end of all read-only segments
- currentReadOnlyAddress = regionAlign(currentReadOnlyAddress); // <rdar://problem/16491435>
- fLinkEditsStartAddress = currentReadOnlyAddress;
- fFirstLinkEditSegment = NULL;
- for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- std::vector<MachOLayoutAbstraction::Segment>& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments();
- for(int i=0; i < segs.size(); ++i) {
- MachOLayoutAbstraction::Segment& seg = segs[i];
- if ( seg.readable() && !seg.writable() && !seg.executable() && (strcmp(seg.name(), "__LINKEDIT") == 0) ) {
- if ( fFirstLinkEditSegment == NULL )
- fFirstLinkEditSegment = &seg;
- seg.setNewAddress(currentReadOnlyAddress);
- currentReadOnlyAddress += pageAlign(seg.size());
- }
- }
- }
- fLinkEditsTotalUnoptimizedSize = pageAlign(currentReadOnlyAddress - fLinkEditsStartAddress);
-
- // populate large mappings
- uint64_t cacheFileOffset = 0;
- if ( currentExecuteAddress > startExecuteAddress ) {
- shared_file_mapping_np executeMapping;
- executeMapping.sfm_address = startExecuteAddress;
- executeMapping.sfm_size = currentExecuteAddress - startExecuteAddress;
- executeMapping.sfm_file_offset = cacheFileOffset;
- executeMapping.sfm_max_prot = VM_PROT_READ | VM_PROT_EXECUTE;
- executeMapping.sfm_init_prot = VM_PROT_READ | VM_PROT_EXECUTE;
- fMappings.push_back(executeMapping);
- cacheFileOffset += executeMapping.sfm_size;
-
- shared_file_mapping_np writableMapping;
- writableMapping.sfm_address = startWritableAddress;
- writableMapping.sfm_size = currentWritableAddress - startWritableAddress;
- writableMapping.sfm_file_offset = cacheFileOffset;
- writableMapping.sfm_max_prot = VM_PROT_READ | VM_PROT_WRITE;
- writableMapping.sfm_init_prot = VM_PROT_READ | VM_PROT_WRITE;
- fMappings.push_back(writableMapping);
- cacheFileOffset += writableMapping.sfm_size;
-
- // make read-only (contains LINKEDIT segments) last, so it can be cut back when optimized
- shared_file_mapping_np readOnlyMapping;
- readOnlyMapping.sfm_address = startReadOnlyAddress;
- readOnlyMapping.sfm_size = currentReadOnlyAddress - startReadOnlyAddress;
- readOnlyMapping.sfm_file_offset = cacheFileOffset;
- readOnlyMapping.sfm_max_prot = VM_PROT_READ;
- readOnlyMapping.sfm_init_prot = VM_PROT_READ;
- fMappings.push_back(readOnlyMapping);
- cacheFileOffset += readOnlyMapping.sfm_size;
- }
- else {
- // empty cache
- shared_file_mapping_np cacheHeaderMapping;
- cacheHeaderMapping.sfm_address = startExecuteAddress;
- cacheHeaderMapping.sfm_size = FIRST_DYLIB_TEXT_OFFSET;
- cacheHeaderMapping.sfm_file_offset = cacheFileOffset;
- cacheHeaderMapping.sfm_max_prot = VM_PROT_READ;
- cacheHeaderMapping.sfm_init_prot = VM_PROT_READ;
- fMappings.push_back(cacheHeaderMapping);
- cacheFileOffset += cacheHeaderMapping.sfm_size;
- }
-}
-
-
-template <typename A>
-uint64_t SharedCache<A>::cacheFileOffsetForVMAddress(uint64_t vmaddr) const
-{
- for(std::vector<shared_file_mapping_np>::const_iterator it = fMappings.begin(); it != fMappings.end(); ++it) {
- if ( (it->sfm_address <= vmaddr) && (vmaddr < it->sfm_address+it->sfm_size) )
- return it->sfm_file_offset + vmaddr - it->sfm_address;
- }
- throwf("address 0x%0llX is not in cache", vmaddr);
-}
-
-template <typename A>
-uint64_t SharedCache<A>::VMAddressForCacheFileOffset(uint64_t offset) const
-{
- for(std::vector<shared_file_mapping_np>::const_iterator it = fMappings.begin(); it != fMappings.end(); ++it) {
- if ( (it->sfm_file_offset <= offset) && (offset < it->sfm_file_offset+it->sfm_size) )
- return it->sfm_address + offset - it->sfm_file_offset;
- }
- throwf("offset 0x%0llX is not in cache", offset);
-}
-
-template <typename A>
-void *SharedCache<A>::mappedAddressForVMAddress(uint64_t vmaddr)
-{
- if (!vmaddr) return NULL;
- else return fInMemoryCache + cacheFileOffsetForVMAddress(vmaddr);
-}
-
-template <typename A>
-uint64_t SharedCache<A>::VMAddressForMappedAddress(const void *mapaddr)
-{
- if (!mapaddr) return 0;
- uint64_t offset = (uint8_t *)mapaddr - (uint8_t *)fInMemoryCache;
- return VMAddressForCacheFileOffset(offset);
-}
-
-
-template <typename A>
-bool SharedCache<A>::notUpToDate(const void* cache, unsigned int aliasCount)
-{
- dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cache;
- // not valid if header signature is wrong
- const char* archPairName = fArchGraph->archName();
- char temp[16];
- strcpy(temp, "dyld_v1 ");
- strcpy(&temp[15-strlen(archPairName)], archPairName);
- if ( strcmp(header->magic(), temp) != 0 ) {
- if ( fVerify ) {
- fprintf(stderr, "update_dyld_shared_cache[%u] cannot verify %s because current cache file has invalid header\n", getpid(), archPairName);
- return false;
- }
- else {
- fprintf(stderr, "update_dyld_shared_cache[%u] updating cache because current cache file has invalid header\n", getpid());
- return true;
- }
- }
- // not valid if count of images does not match current images needed
- if ( header->imagesCount() != (fDylibs.size()+aliasCount) ) {
- if ( fVerify ) {
- fprintf(stderr, "update_dyld_shared_cache[%u] cannot verify %s because current cache file contains a different set of dylibs\n", getpid(), archPairName);
- return false;
- }
- else {
- fprintf(stderr, "update_dyld_shared_cache[%u] updating %s cache because current cache file contains a different set of dylibs\n", getpid(), archPairName);
- return true;
- }
- }
- // get end of TEXT region
- const dyldCacheFileMapping<E>* textMapping = (dyldCacheFileMapping<E>*)((uint8_t*)cache+sizeof(dyldCacheHeader<E>));
- const uint32_t textSize = textMapping->size();
-
- // verify every dylib in constructed graph is in existing cache with same inode and modTime
- std::map<const MachOLayoutAbstraction*, uint32_t> sortingMap;
- const dyldCacheImageInfo<E>* imagesStart = (dyldCacheImageInfo<E>*)((uint8_t*)cache + header->imagesOffset());
- const dyldCacheImageInfo<E>* imagesEnd = &imagesStart[header->imagesCount()];
- for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- bool found = false;
- //fprintf(stderr, "inode=0x%llX, mTime=0x%llX, path=%s\n", it->info.inode, it->info.modTime, it->layout->getID().name);
- for(const dyldCacheImageInfo<E>* cacheEntry = imagesStart; cacheEntry < imagesEnd; ++cacheEntry) {
- if ( fVerify ) {
- if ( cacheEntry->pathFileOffset() > textSize ) {
- throwf("update_dyld_shared_cache[%u]: for arch=%s, image entries corrupt, bad path offset in %s\n",
- getpid(), archPairName, it->layout->getID().name);
- }
- // in -verify mode, just match by path and warn if file looks different
- if ( strcmp((char*)cache+cacheEntry->pathFileOffset(), it->layout->getID().name) == 0 ) {
- found = true;
- sortingMap[it->layout] = cacheEntry-imagesStart;
- if ( (cacheEntry->inode() != it->info.inode) || (cacheEntry->modTime() != it->info.modTime) ) {
- fprintf(stderr, "update_dyld_shared_cache[%u] warning: for arch=%s, %s has changed since cache was built\n",
- getpid(), archPairName, it->layout->getID().name);
- }
- break;
- }
- }
- else {
- if ( cacheEntry->pathFileOffset() > textSize ) {
- // cache corrupt, needs to be regenerated
- return true;
- }
- // in normal update mode, everything has to match for cache to be up-to-date
- if ( (cacheEntry->inode() == it->info.inode)
- && (cacheEntry->modTime() == it->info.modTime)
- && (strcmp((char*)cache+cacheEntry->pathFileOffset(), it->layout->getID().name) == 0) ) {
- found = true;
- break;
- }
- }
- }
- if ( !found ) {
- if ( fVerify ) {
- throwf("update_dyld_shared_cache[%u] can't verify %s cache because %s is not in existing cache\n", getpid(), archPairName, it->layout->getID().name);
- }
- else {
- fprintf(stderr, "update_dyld_shared_cache[%u] updating %s cache because dylib at %s has changed\n", getpid(), archPairName, it->layout->getID().name);
- return true;
- }
- }
- }
- // all dylibs in existing cache file match those determined need to be in shared cache
- if ( fVerify ) {
- // sort fDylibs to match existing cache file so we can compare content
- std::sort(fDylibs.begin(), fDylibs.end(), Sorter(sortingMap));
- //fprintf(stderr, "dylibs sorted like existing cache:\n");
- //for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- // fprintf(stderr," %s\n", it->layout->getID().name);
- //}
- // do regenerate a new cache so we can compare content with existing
- return true;
- }
- else {
- // existing cache file is up-to-date, don't need to regenerate
- return false;
- }
-}
-
-
-template <typename A>
-bool SharedCache<A>::notUpToDate(const char* path, unsigned int aliasCount)
-{
- // mmap existing cache file
- int fd = ::open(path, O_RDONLY);
- if ( fd == -1 )
- return true;
- struct stat stat_buf;
- ::fstat(fd, &stat_buf);
- uint32_t cacheFileSize = stat_buf.st_size;
- uint32_t cacheAllocatedSize = pageAlign(cacheFileSize);
- uint8_t* mappingAddr = NULL;
- if ( vm_allocate(mach_task_self(), (vm_address_t*)(&mappingAddr), cacheAllocatedSize, VM_FLAGS_ANYWHERE) != KERN_SUCCESS )
- throwf("can't vm_allocate cache of size %u", cacheFileSize);
- // <rdar://problem/8960832> update_dyld_shared_cache -verify finds differences
- (void)fcntl(fd, F_NOCACHE, 1);
- ssize_t readResult = pread(fd, mappingAddr, cacheFileSize, 0);
- if ( readResult != cacheFileSize )
- throwf("can't read all of existing cache file (%lu of %u): %s", readResult, cacheFileSize, path);
- ::close(fd);
-
- // validate it
- bool result = this->notUpToDate(mappingAddr, aliasCount);
- if ( fVerify ) {
- // don't unmap yet, leave so it can be verified later
- fExistingCacheForVerification = mappingAddr;
- }
- else {
- // unmap
- vm_deallocate(mach_task_self(), (vm_address_t)mappingAddr, cacheAllocatedSize);
- if ( verbose && !result )
- fprintf(stderr, "update_dyld_shared_cache: %s is up-to-date\n", path);
- }
- return result;
-}
-
-
-
-template <typename A>
-class LinkEditOptimizer
-{
-public:
- LinkEditOptimizer(const MachOLayoutAbstraction&, const SharedCache<A>&, uint8_t*, StringPool&);
- virtual ~LinkEditOptimizer() {}
-
- void copyBindInfo(uint32_t&);
- void copyWeakBindInfo(uint32_t&);
- void copyLazyBindInfo(uint32_t&);
- void copyExportInfo(uint32_t&);
- void copyLocalSymbols(uint32_t symbolTableOffset, uint32_t&, bool dontMapLocalSymbols,
- uint8_t* cacheStart, StringPool& unmappedLocalsStringPool,
- std::vector<macho_nlist<typename A::P> >& unmappedSymbols,
- std::vector<LocalSymbolInfo>& info);
- void copyExportedSymbols(uint32_t symbolTableOffset, uint32_t&);
- void copyImportedSymbols(uint32_t symbolTableOffset, uint32_t&);
- void copyExternalRelocations(uint32_t& offset);
- void copyIndirectSymbolTable(uint32_t& offset);
- void copyFunctionStarts(uint32_t& offset);
- void copyDataInCode(uint32_t& offset);
- void updateLoadCommands(uint64_t newVMAddress, uint64_t size, uint32_t stringPoolOffset,
- uint32_t linkEditsFileOffset, bool keepSignatures);
-
-
-protected:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
-
-private:
-
- const SharedCache<A>& fSharedCache;
- const macho_header<P>* fHeader;
- uint8_t* fNewLinkEditStart;
- uint8_t* fLinkEditBase;
- const MachOLayoutAbstraction& fLayout;
- macho_dyld_info_command<P>* fDyldInfo;
- macho_dysymtab_command<P>* fDynamicSymbolTable;
- macho_linkedit_data_command<P>* fFunctionStarts;
- macho_linkedit_data_command<P>* fDataInCode;
- macho_symtab_command<P>* fSymbolTableLoadCommand;
- const macho_nlist<P>* fSymbolTable;
- const char* fStrings;
- StringPool& fNewStringPool;
- std::map<uint32_t,uint32_t> fOldToNewSymbolIndexes;
- uint32_t fBindInfoOffsetIntoNewLinkEdit;
- uint32_t fBindInfoSizeInNewLinkEdit;
- uint32_t fWeakBindInfoOffsetIntoNewLinkEdit;
- uint32_t fWeakBindInfoSizeInNewLinkEdit;
- uint32_t fLazyBindInfoOffsetIntoNewLinkEdit;
- uint32_t fLazyBindInfoSizeInNewLinkEdit;
- uint32_t fExportInfoOffsetIntoNewLinkEdit;
- uint32_t fExportInfoSizeInNewLinkEdit;
- uint32_t fSymbolTableStartOffsetInNewLinkEdit;
- uint32_t fLocalSymbolsStartIndexInNewLinkEdit;
- uint32_t fLocalSymbolsCountInNewLinkEdit;
- uint32_t fExportedSymbolsStartIndexInNewLinkEdit;
- uint32_t fExportedSymbolsCountInNewLinkEdit;
- uint32_t fImportSymbolsStartIndexInNewLinkEdit;
- uint32_t fImportedSymbolsCountInNewLinkEdit;
- uint32_t fExternalRelocationsOffsetIntoNewLinkEdit;
- uint32_t fIndirectSymbolTableOffsetInfoNewLinkEdit;
- uint32_t fFunctionStartsOffsetInNewLinkEdit;
- uint32_t fDataInCodeOffsetInNewLinkEdit;
- uint32_t fUnmappedLocalSymbolsStartIndexInNewLinkEdit;
- uint32_t fUnmappedLocalSymbolsCountInNewLinkEdit;
-};
-
-
-
-template <typename A>
-LinkEditOptimizer<A>::LinkEditOptimizer(const MachOLayoutAbstraction& layout, const SharedCache<A>& sharedCache, uint8_t* newLinkEdit, StringPool& stringPool)
- : fSharedCache(sharedCache), fLayout(layout), fLinkEditBase(NULL), fNewLinkEditStart(newLinkEdit), fDyldInfo(NULL),
- fDynamicSymbolTable(NULL), fFunctionStarts(NULL), fDataInCode(NULL),
- fSymbolTableLoadCommand(NULL), fSymbolTable(NULL), fStrings(NULL), fNewStringPool(stringPool),
- fBindInfoOffsetIntoNewLinkEdit(0), fBindInfoSizeInNewLinkEdit(0),
- fWeakBindInfoOffsetIntoNewLinkEdit(0), fWeakBindInfoSizeInNewLinkEdit(0),
- fLazyBindInfoOffsetIntoNewLinkEdit(0), fLazyBindInfoSizeInNewLinkEdit(0),
- fExportInfoOffsetIntoNewLinkEdit(0), fExportInfoSizeInNewLinkEdit(0),
- fSymbolTableStartOffsetInNewLinkEdit(0),
- fLocalSymbolsStartIndexInNewLinkEdit(0), fLocalSymbolsCountInNewLinkEdit(0),
- fExportedSymbolsStartIndexInNewLinkEdit(0), fExportedSymbolsCountInNewLinkEdit(0),
- fImportSymbolsStartIndexInNewLinkEdit(0), fImportedSymbolsCountInNewLinkEdit(0),
- fExternalRelocationsOffsetIntoNewLinkEdit(0), fIndirectSymbolTableOffsetInfoNewLinkEdit(0),
- fFunctionStartsOffsetInNewLinkEdit(0), fDataInCodeOffsetInNewLinkEdit(0),
- fUnmappedLocalSymbolsStartIndexInNewLinkEdit(0), fUnmappedLocalSymbolsCountInNewLinkEdit(0)
-
-{
- fHeader = (const macho_header<P>*)fLayout.getSegments()[0].mappedAddress();
-
- const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
- for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
- const MachOLayoutAbstraction::Segment& seg = *it;
- if ( strcmp(seg.name(), "__LINKEDIT") == 0 )
- fLinkEditBase = (uint8_t*)seg.mappedAddress() - seg.fileOffset();
- }
- if ( fLinkEditBase == NULL )
- throw "no __LINKEDIT segment";
-
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd()) {
- case LC_SYMTAB:
- {
- fSymbolTableLoadCommand = (macho_symtab_command<P>*)cmd;
- fSymbolTable = (macho_nlist<P>*)(&fLinkEditBase[fSymbolTableLoadCommand->symoff()]);
- fStrings = (char*)&fLinkEditBase[fSymbolTableLoadCommand->stroff()];
- }
- break;
- case LC_DYSYMTAB:
- fDynamicSymbolTable = (macho_dysymtab_command<P>*)cmd;
- break;
- case LC_DYLD_INFO:
- case LC_DYLD_INFO_ONLY:
- fDyldInfo = (macho_dyld_info_command<P>*)cmd;
- break;
- case LC_FUNCTION_STARTS:
- fFunctionStarts = (macho_linkedit_data_command<P>*)cmd;
- case LC_DATA_IN_CODE:
- fDataInCode = (macho_linkedit_data_command<P>*)cmd;
- break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
- if ( fSymbolTable == NULL )
- throw "no LC_SYMTAB";
- if ( fDynamicSymbolTable == NULL )
- throw "no LC_DYSYMTAB";
-
-}
-
-
-template <typename A>
-class SymbolSorter
-{
-public:
- typedef typename A::P P;
- SymbolSorter(const StringPool& pool) : fStringPool(pool) {}
- bool operator()(const macho_nlist<P>& left, const macho_nlist<P>& right) {
- return (strcmp(fStringPool.stringAtIndex(left.n_strx()) , fStringPool.stringAtIndex(right.n_strx())) < 0);
- }
-
-private:
- const StringPool& fStringPool;
-};
-
-
-template <typename A>
-void LinkEditOptimizer<A>::copyBindInfo(uint32_t& offset)
-{
- if ( (fDyldInfo != NULL) && (fDyldInfo->bind_off() != 0) ) {
- fBindInfoOffsetIntoNewLinkEdit = offset;
- fBindInfoSizeInNewLinkEdit = fDyldInfo->bind_size();
- memcpy(fNewLinkEditStart+offset, &fLinkEditBase[fDyldInfo->bind_off()], fDyldInfo->bind_size());
- offset += fDyldInfo->bind_size();
- }
-}
-
-template <typename A>
-void LinkEditOptimizer<A>::copyWeakBindInfo(uint32_t& offset)
-{
- if ( (fDyldInfo != NULL) && (fDyldInfo->weak_bind_off() != 0) ) {
- fWeakBindInfoOffsetIntoNewLinkEdit = offset;
- fWeakBindInfoSizeInNewLinkEdit = fDyldInfo->weak_bind_size();
- memcpy(fNewLinkEditStart+offset, &fLinkEditBase[fDyldInfo->weak_bind_off()], fDyldInfo->weak_bind_size());
- offset += fDyldInfo->weak_bind_size();
- }
-}
-
-template <typename A>
-void LinkEditOptimizer<A>::copyLazyBindInfo(uint32_t& offset)
-{
- if ( (fDyldInfo != NULL) && (fDyldInfo->lazy_bind_off() != 0) ) {
- fLazyBindInfoOffsetIntoNewLinkEdit = offset;
- fLazyBindInfoSizeInNewLinkEdit = fDyldInfo->lazy_bind_size();
- memcpy(fNewLinkEditStart+offset, &fLinkEditBase[fDyldInfo->lazy_bind_off()], fDyldInfo->lazy_bind_size());
- offset += fDyldInfo->lazy_bind_size();
- }
-}
-
-template <typename A>
-void LinkEditOptimizer<A>::copyExportInfo(uint32_t& offset)
-{
- if ( (fDyldInfo != NULL) && (fLayout.getDyldInfoExports() != NULL) ) {
- fExportInfoOffsetIntoNewLinkEdit = offset;
- fExportInfoSizeInNewLinkEdit = fDyldInfo->export_size();
- memcpy(fNewLinkEditStart+offset, fLayout.getDyldInfoExports(), fDyldInfo->export_size());
- offset += fDyldInfo->export_size();
- }
-}
-
-
-template <typename A>
-void LinkEditOptimizer<A>::copyLocalSymbols(uint32_t symbolTableOffset, uint32_t& symbolIndex, bool dontMapLocalSymbols, uint8_t* cacheStart,
- StringPool& unmappedLocalsStringPool, std::vector<macho_nlist<P> >& unmappedSymbols,
- std::vector<LocalSymbolInfo>& dylibInfos)
-{
- fLocalSymbolsStartIndexInNewLinkEdit = symbolIndex;
- LocalSymbolInfo localInfo;
- localInfo.dylibOffset = ((uint8_t*)fHeader) - cacheStart;
- localInfo.nlistStartIndex = unmappedSymbols.size();
- localInfo.nlistCount = 0;
- fSymbolTableStartOffsetInNewLinkEdit = symbolTableOffset + symbolIndex*sizeof(macho_nlist<P>);
- macho_nlist<P>* const newSymbolTableStart = (macho_nlist<P>*)(fNewLinkEditStart+symbolTableOffset);
- const macho_nlist<P>* const firstLocal = &fSymbolTable[fDynamicSymbolTable->ilocalsym()];
- const macho_nlist<P>* const lastLocal = &fSymbolTable[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()];
- uint32_t oldIndex = fDynamicSymbolTable->ilocalsym();
- for (const macho_nlist<P>* entry = firstLocal; entry < lastLocal; ++entry, ++oldIndex) {
- // <rdar://problem/12237639> don't copy stab symbols
- if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) ) {
- const char* name = &fStrings[entry->n_strx()];
- macho_nlist<P>* newSymbolEntry = &newSymbolTableStart[symbolIndex];
- *newSymbolEntry = *entry;
- if ( dontMapLocalSymbols ) {
- // if local in __text, add <redacted> symbol name to shared cache so backtraces don't have bogus names
- if ( entry->n_sect() == 1 ) {
- newSymbolEntry->set_n_strx(fNewStringPool.addUnique("<redacted>"));
- ++symbolIndex;
- }
- // copy local symbol to unmmapped locals area
- unmappedSymbols.push_back(*entry);
- unmappedSymbols.back().set_n_strx(unmappedLocalsStringPool.addUnique(name));
- }
- else {
- newSymbolEntry->set_n_strx(fNewStringPool.addUnique(name));
- ++symbolIndex;
- }
- }
- }
- fLocalSymbolsCountInNewLinkEdit = symbolIndex - fLocalSymbolsStartIndexInNewLinkEdit;
- localInfo.nlistCount = unmappedSymbols.size() - localInfo.nlistStartIndex;
- dylibInfos.push_back(localInfo);
- //fprintf(stderr, "%u locals starting at %u for %s\n", fLocalSymbolsCountInNewLinkEdit, fLocalSymbolsStartIndexInNewLinkEdit, fLayout.getFilePath());
-}
-
-
-template <typename A>
-void LinkEditOptimizer<A>::copyExportedSymbols(uint32_t symbolTableOffset, uint32_t& symbolIndex)
-{
- fExportedSymbolsStartIndexInNewLinkEdit = symbolIndex;
- macho_nlist<P>* const newSymbolTableStart = (macho_nlist<P>*)(fNewLinkEditStart+symbolTableOffset);
- const macho_nlist<P>* const firstExport = &fSymbolTable[fDynamicSymbolTable->iextdefsym()];
- const macho_nlist<P>* const lastExport = &fSymbolTable[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()];
- uint32_t oldIndex = fDynamicSymbolTable->iextdefsym();
- for (const macho_nlist<P>* entry = firstExport; entry < lastExport; ++entry, ++oldIndex) {
- if ( ((entry->n_type() & N_TYPE) == N_SECT) && (strncmp(&fStrings[entry->n_strx()], ".objc_", 6) != 0)
- && (strncmp(&fStrings[entry->n_strx()], "$ld$", 4) != 0) ) {
- macho_nlist<P>* newSymbolEntry = &newSymbolTableStart[symbolIndex];
- *newSymbolEntry = *entry;
- newSymbolEntry->set_n_strx(fNewStringPool.addUnique(&fStrings[entry->n_strx()]));
- fOldToNewSymbolIndexes[oldIndex] = symbolIndex-fLocalSymbolsStartIndexInNewLinkEdit;
- ++symbolIndex;
- }
- }
- fExportedSymbolsCountInNewLinkEdit = symbolIndex - fExportedSymbolsStartIndexInNewLinkEdit;
- //fprintf(stderr, "%u exports starting at %u for %s\n", fExportedSymbolsCountInNewLinkEdit, fExportedSymbolsStartIndexInNewLinkEdit, fLayout.getFilePath());
- // sort by name, so that dyld does not need a toc
- macho_nlist<P>* newSymbolsStart = &newSymbolTableStart[fExportedSymbolsStartIndexInNewLinkEdit];
- macho_nlist<P>* newSymbolsEnd = &newSymbolTableStart[fExportedSymbolsStartIndexInNewLinkEdit+fExportedSymbolsCountInNewLinkEdit];
- std::sort(newSymbolsStart, newSymbolsEnd, SymbolSorter<A>(fNewStringPool));
- //for (macho_nlist<P>* entry = newSymbolsStart; entry < newSymbolsEnd; ++entry)
- // fprintf(stderr, "\t%u\t %s\n", (entry-newSymbolsStart)+fExportedSymbolsStartIndexInNewLinkEdit, fNewStringPool.stringAtIndex(entry->n_strx()));
-}
-
-
-template <typename A>
-void LinkEditOptimizer<A>::copyImportedSymbols(uint32_t symbolTableOffset, uint32_t& symbolIndex)
-{
- fImportSymbolsStartIndexInNewLinkEdit = symbolIndex;
- macho_nlist<P>* const newSymbolTableStart = (macho_nlist<P>*)(fNewLinkEditStart+symbolTableOffset);
- const macho_nlist<P>* const firstImport = &fSymbolTable[fDynamicSymbolTable->iundefsym()];
- const macho_nlist<P>* const lastImport = &fSymbolTable[fDynamicSymbolTable->iundefsym()+fDynamicSymbolTable->nundefsym()];
- uint32_t oldIndex = fDynamicSymbolTable->iundefsym();
- for (const macho_nlist<P>* entry = firstImport; entry < lastImport; ++entry, ++oldIndex) {
- if ( ((entry->n_type() & N_TYPE) == N_UNDF) && (strncmp(&fStrings[entry->n_strx()], ".objc_", 6) != 0) ) {
- macho_nlist<P>* newSymbolEntry = &newSymbolTableStart[symbolIndex];
- *newSymbolEntry = *entry;
- newSymbolEntry->set_n_strx(fNewStringPool.addUnique(&fStrings[entry->n_strx()]));
- fOldToNewSymbolIndexes[oldIndex] = symbolIndex-fLocalSymbolsStartIndexInNewLinkEdit;
- ++symbolIndex;
- }
- }
- fImportedSymbolsCountInNewLinkEdit = symbolIndex - fImportSymbolsStartIndexInNewLinkEdit;
- //fprintf(stderr, "%u imports starting at %u for %s\n", fImportedSymbolsCountInNewLinkEdit, fImportSymbolsStartIndexInNewLinkEdit, fLayout.getFilePath());
- //macho_nlist<P>* newSymbolsStart = &((macho_nlist<P>*)fNewLinkEditStart)[fImportSymbolsStartIndexInNewLinkEdit];
- //macho_nlist<P>* newSymbolsEnd = &((macho_nlist<P>*)fNewLinkEditStart)[fImportSymbolsStartIndexInNewLinkEdit+fImportedSymbolsCountInNewLinkEdit];
- //for (macho_nlist<P>* entry = newSymbolsStart; entry < newSymbolsEnd; ++entry)
- // fprintf(stderr, "\t%u\t%s\n", (entry-newSymbolsStart)+fImportSymbolsStartIndexInNewLinkEdit, fNewStringPool.stringAtIndex(entry->n_strx()));
-}
-
-
-template <typename A>
-void LinkEditOptimizer<A>::copyExternalRelocations(uint32_t& offset)
-{
- fExternalRelocationsOffsetIntoNewLinkEdit = offset;
- const macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(&fLinkEditBase[fDynamicSymbolTable->extreloff()]);
- const macho_relocation_info<P>* const relocsEnd = &relocsStart[fDynamicSymbolTable->nextrel()];
- for (const macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
- macho_relocation_info<P>* newReloc = (macho_relocation_info<P>*)(&fNewLinkEditStart[offset]);
- *newReloc = *reloc;
- uint32_t newSymbolIndex = fOldToNewSymbolIndexes[reloc->r_symbolnum()];
- //fprintf(stderr, "copyExternalRelocations() old=%d, new=%u name=%s in %s\n", reloc->r_symbolnum(), newSymbolIndex,
- // &fStrings[fSymbolTable[reloc->r_symbolnum()].n_strx()], fLayout.getFilePath());
- newReloc->set_r_symbolnum(newSymbolIndex);
- offset += sizeof(macho_relocation_info<P>);
- }
-}
-
-template <typename A>
-void LinkEditOptimizer<A>::copyFunctionStarts(uint32_t& offset)
-{
- if ( fFunctionStarts != NULL ) {
- fFunctionStartsOffsetInNewLinkEdit = offset;
- memcpy(&fNewLinkEditStart[offset], &fLinkEditBase[fFunctionStarts->dataoff()], fFunctionStarts->datasize());
- offset += fFunctionStarts->datasize();
- }
-}
-
-template <typename A>
-void LinkEditOptimizer<A>::copyDataInCode(uint32_t& offset)
-{
- if ( fDataInCode != NULL ) {
- fDataInCodeOffsetInNewLinkEdit = offset;
- memcpy(&fNewLinkEditStart[offset], &fLinkEditBase[fDataInCode->dataoff()], fDataInCode->datasize());
- offset += fDataInCode->datasize();
- }
-}
-
-
-template <typename A>
-void LinkEditOptimizer<A>::copyIndirectSymbolTable(uint32_t& offset)
-{
- fIndirectSymbolTableOffsetInfoNewLinkEdit = offset;
- const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicSymbolTable->indirectsymoff()];
- uint32_t* newIndirectTable = (uint32_t*)&fNewLinkEditStart[offset];
- for (int i=0; i < fDynamicSymbolTable->nindirectsyms(); ++i) {
- uint32_t oldSymbolIndex = E::get32(indirectTable[i]);
- uint32_t newSymbolIndex = oldSymbolIndex;
- if ( (oldSymbolIndex != INDIRECT_SYMBOL_ABS) && (oldSymbolIndex != INDIRECT_SYMBOL_LOCAL) ) {
- newSymbolIndex = fOldToNewSymbolIndexes[oldSymbolIndex];
- //fprintf(stderr, "copyIndirectSymbolTable() old=%d, new=%u name=%s in %s\n", oldSymbolIndex, newSymbolIndex,
- // &fStrings[fSymbolTable[oldSymbolIndex].n_strx()], fLayout.getFilePath());
- }
- E::set32(newIndirectTable[i], newSymbolIndex);
- }
- offset += (fDynamicSymbolTable->nindirectsyms() * 4);
-}
-
-template <typename A>
-void LinkEditOptimizer<A>::updateLoadCommands(uint64_t newVMAddress, uint64_t leSize, uint32_t stringPoolOffset,
- uint32_t linkEditsFileOffset, bool keepSignatures)
-{
- // set LINKEDIT segment commmand to new merged LINKEDIT
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
- macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
- if ( strcmp(seg->segname(), "__LINKEDIT") == 0 ) {
- seg->set_vmaddr(newVMAddress);
- seg->set_vmsize(leSize);
- seg->set_filesize(leSize);
- seg->set_fileoff(linkEditsFileOffset);
- }
- else {
- pint_t oldFileOff = seg->fileoff();
- // don't alter __TEXT until <rdar://problem/7022345> is fixed
- if ( strcmp(seg->segname(), "__TEXT") != 0 ) {
- // update all other segments fileoff to be offset from start of cache file
- seg->set_fileoff(fSharedCache.cacheFileOffsetForVMAddress(seg->vmaddr()));
- }
- pint_t fileOffsetDelta = seg->fileoff() - oldFileOff;
- const MachOLayoutAbstraction::Segment* layoutSeg = fLayout.getSegment(seg->segname());
- if ( layoutSeg != NULL ) {
- //if ( seg->filesize() != layoutSeg->fileSize() ) {
- // fprintf(stderr, "LC filesize=0x%08llX, trimmed seg file size=0x%08llX, seg=%s, path=%s\n",
- // seg->filesize(), layoutSeg->fileSize(), seg->segname(), fLayout.getFilePath());
- //}
- //if ( seg->vmsize() != layoutSeg->size() ) {
- // fprintf(stderr, "LC vmsize=0x%08llX, trimmed seg size=0x%08llX, seg=%s, path=%s\n",
- // seg->vmsize(), layoutSeg->size(), seg->segname(), fLayout.getFilePath());
- //}
- seg->set_vmsize(layoutSeg->size());
- seg->set_filesize(layoutSeg->fileSize());
- }
- // update all sections in this segment
- macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
- macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
- for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
- if ( sect->offset() != 0 )
- sect->set_offset(sect->offset()+fileOffsetDelta);
- //if ( (sect->flags() & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS )
- // fprintf(stderr, "found initializer(s) in %s\n", fLayout.getFilePath());
- }
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-
- // update dyld_info with new offsets
- if ( fDyldInfo != NULL ) {
- fDyldInfo->set_rebase_off(0);
- fDyldInfo->set_rebase_size(0);
- fDyldInfo->set_bind_off(linkEditsFileOffset+fBindInfoOffsetIntoNewLinkEdit);
- fDyldInfo->set_bind_size(fBindInfoSizeInNewLinkEdit);
- fDyldInfo->set_weak_bind_off(linkEditsFileOffset+fWeakBindInfoOffsetIntoNewLinkEdit);
- fDyldInfo->set_weak_bind_size(fWeakBindInfoSizeInNewLinkEdit);
- fDyldInfo->set_lazy_bind_off(linkEditsFileOffset+fLazyBindInfoOffsetIntoNewLinkEdit);
- fDyldInfo->set_lazy_bind_size(fLazyBindInfoSizeInNewLinkEdit);
- fDyldInfo->set_export_off(linkEditsFileOffset+fExportInfoOffsetIntoNewLinkEdit);
- fDyldInfo->set_export_size(fExportInfoSizeInNewLinkEdit);
-
-// fprintf(stderr, "dylib %s\n", fLayout.getFilePath());
-// fprintf(stderr, " bind_off=0x%08X\n", fDyldInfo->bind_off());
-// fprintf(stderr, " export_off=0x%08X\n", fDyldInfo->export_off());
-// fprintf(stderr, " export_size=%d\n", fDyldInfo->export_size());
-
- }
-
- // update symbol table and dynamic symbol table with new offsets
- fSymbolTableLoadCommand->set_symoff(linkEditsFileOffset+fSymbolTableStartOffsetInNewLinkEdit);
- fSymbolTableLoadCommand->set_nsyms(fLocalSymbolsCountInNewLinkEdit+fExportedSymbolsCountInNewLinkEdit+fImportedSymbolsCountInNewLinkEdit);
- fSymbolTableLoadCommand->set_stroff(linkEditsFileOffset+stringPoolOffset);
- fSymbolTableLoadCommand->set_strsize(fNewStringPool.size());
- fDynamicSymbolTable->set_ilocalsym(0);
- fDynamicSymbolTable->set_nlocalsym(fLocalSymbolsCountInNewLinkEdit);
- fDynamicSymbolTable->set_iextdefsym(fExportedSymbolsStartIndexInNewLinkEdit-fLocalSymbolsStartIndexInNewLinkEdit);
- fDynamicSymbolTable->set_nextdefsym(fExportedSymbolsCountInNewLinkEdit);
- fDynamicSymbolTable->set_iundefsym(fImportSymbolsStartIndexInNewLinkEdit-fLocalSymbolsStartIndexInNewLinkEdit);
- fDynamicSymbolTable->set_nundefsym(fImportedSymbolsCountInNewLinkEdit);
- fDynamicSymbolTable->set_tocoff(0);
- fDynamicSymbolTable->set_ntoc(0);
- fDynamicSymbolTable->set_modtaboff(0);
- fDynamicSymbolTable->set_nmodtab(0);
- fDynamicSymbolTable->set_indirectsymoff(linkEditsFileOffset+fIndirectSymbolTableOffsetInfoNewLinkEdit);
- fDynamicSymbolTable->set_extreloff(linkEditsFileOffset+fExternalRelocationsOffsetIntoNewLinkEdit);
- fDynamicSymbolTable->set_locreloff(0);
- fDynamicSymbolTable->set_nlocrel(0);
-
- // update function starts
- if ( fFunctionStarts != NULL ) {
- fFunctionStarts->set_dataoff(linkEditsFileOffset+fFunctionStartsOffsetInNewLinkEdit);
- }
- // update data-in-code info
- if ( fDataInCode != NULL ) {
- fDataInCode->set_dataoff(linkEditsFileOffset+fDataInCodeOffsetInNewLinkEdit);
- }
-
- // now remove load commands no longer needed
- const macho_load_command<P>* srcCmd = cmds;
- macho_load_command<P>* dstCmd = (macho_load_command<P>*)cmds;
- int32_t newCount = 0;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- uint32_t cmdSize = srcCmd->cmdsize();
- switch ( srcCmd->cmd() ) {
- case LC_SEGMENT_SPLIT_INFO:
- case LC_DYLIB_CODE_SIGN_DRS:
- case LC_RPATH:
- // don't copy
- break;
- case LC_CODE_SIGNATURE:
- if ( !keepSignatures )
- break;
- // otherwise fall into copy case
- default:
- memmove(dstCmd, srcCmd, cmdSize);
- dstCmd = (macho_load_command<P>*)(((uint8_t*)dstCmd)+cmdSize);
- ++newCount;
- break;
- }
- srcCmd = (const macho_load_command<P>*)(((uint8_t*)srcCmd)+cmdSize);
- }
- // zero out stuff removed
- bzero(dstCmd, (uint8_t*)srcCmd - (uint8_t*)dstCmd);
-
- // update mach_header
- macho_header<P>* writableHeader = (macho_header<P>*)fHeader;
- writableHeader->set_ncmds(newCount);
- writableHeader->set_sizeofcmds((uint8_t*)dstCmd - ((uint8_t*)fHeader + sizeof(macho_header<P>)));
-
- // this invalidates some ivars
- fDynamicSymbolTable = NULL;
- fSymbolTableLoadCommand = NULL;
- fDyldInfo = NULL;
- fSymbolTable = NULL;
- fStrings = NULL;
-}
-
-
-
-template <typename A>
-uint8_t* SharedCache<A>::optimizeLINKEDIT(bool keepSignatures, bool dontMapLocalSymbols)
-{
- // allocate space for optimized LINKEDIT area
- uint8_t* newLinkEdit = new uint8_t[fLinkEditsTotalUnoptimizedSize];
- bzero(newLinkEdit, fLinkEditsTotalUnoptimizedSize);
-
- // make a string pool
- StringPool stringPool;
-
- // create optimizer object for each LINKEDIT segment
- std::vector<LinkEditOptimizer<A>*> optimizers;
- for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- optimizers.push_back(new LinkEditOptimizer<A>(*it->layout, *this, newLinkEdit, stringPool));
- }
-
- // rebase info is not copied because images in shared cache are never rebased
-
- // copy weak bind info
- uint32_t offset = 0;
- fOffsetOfWeakBindInfoInCombinedLinkedit = offset;
- for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyWeakBindInfo(offset);
- }
-
- // copy export info
- fOffsetOfExportInfoInCombinedLinkedit = offset;
- for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyExportInfo(offset);
- }
-
- // copy bind info
- fOffsetOfBindInfoInCombinedLinkedit = offset;
- for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyBindInfo(offset);
- }
-
- // copy lazy bind info
- fOffsetOfLazyBindInfoInCombinedLinkedit = offset;
- for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyLazyBindInfo(offset);
- }
-
- // copy symbol table entries
- fOffsetOfOldSymbolTableInfoInCombinedLinkedit = offset;
- uint32_t symbolTableOffset = offset;
- uint32_t symbolTableIndex = 0;
- if ( dontMapLocalSymbols )
- fUnmappedLocalSymbols.reserve(16384);
- for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyLocalSymbols(symbolTableOffset, symbolTableIndex, dontMapLocalSymbols, fInMemoryCache,
- fUnmappedLocalsStringPool, fUnmappedLocalSymbols, fLocalSymbolInfos);
- (*it)->copyExportedSymbols(symbolTableOffset, symbolTableIndex);
- (*it)->copyImportedSymbols(symbolTableOffset, symbolTableIndex);
- }
- fSizeOfOldSymbolTableInfoInCombinedLinkedit = symbolTableIndex * sizeof(macho_nlist<typename A::P>);
- offset = symbolTableOffset + fSizeOfOldSymbolTableInfoInCombinedLinkedit & (-8);
-
- // copy external relocations, 8-byte aligned after end of symbol table
- fOffsetOfOldExternalRelocationsInCombinedLinkedit = offset;
- for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyExternalRelocations(offset);
- }
- fSizeOfOldExternalRelocationsInCombinedLinkedit = offset - fOffsetOfOldExternalRelocationsInCombinedLinkedit;
-
- // copy function starts
- fOffsetOfFunctionStartsInCombinedLinkedit = offset;
- for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyFunctionStarts(offset);
- }
- fSizeOfFunctionStartsInCombinedLinkedit = offset - fOffsetOfFunctionStartsInCombinedLinkedit;
-
- // copy data-in-code info
- fOffsetOfDataInCodeInCombinedLinkedit = offset;
- for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyDataInCode(offset);
- }
- fSizeOfDataInCodeInCombinedLinkedit = offset - fOffsetOfDataInCodeInCombinedLinkedit;
-
- // copy indirect symbol tables
- fOffsetOfOldIndirectSymbolsInCombinedLinkedit = offset;
- for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyIndirectSymbolTable(offset);
- }
- fSizeOfOldIndirectSymbolsInCombinedLinkedit = offset - fOffsetOfOldIndirectSymbolsInCombinedLinkedit;
-
- // copy string pool
- fOffsetOfOldStringPoolInCombinedLinkedit = offset;
- memcpy(&newLinkEdit[offset], stringPool.getBuffer(), stringPool.size());
- fSizeOfOldStringPoolInCombinedLinkedit = stringPool.size();
-
- // total new size round up to page size
- fLinkEditsTotalOptimizedSize = pageAlign(fOffsetOfOldStringPoolInCombinedLinkedit + fSizeOfOldStringPoolInCombinedLinkedit);
-
- // choose new linkedit file offset
- uint32_t linkEditsFileOffset = cacheFileOffsetForVMAddress(fLinkEditsStartAddress);
-// uint32_t linkEditsFileOffset = fLinkEditsStartAddress - sharedRegionStartAddress();
-
- // update load commands so that all dylibs shared different areas of the same LINKEDIT segment
- for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->updateLoadCommands(fLinkEditsStartAddress, fLinkEditsTotalOptimizedSize, fOffsetOfOldStringPoolInCombinedLinkedit, linkEditsFileOffset, keepSignatures);
- }
-
- //fprintf(stderr, "fLinkEditsTotalUnoptimizedSize=%llu, fLinkEditsTotalOptimizedSize=%u\n", fLinkEditsTotalUnoptimizedSize, fLinkEditsTotalOptimizedSize);
- //printf(stderr, "mega link edit mapped starting at: %p\n", fFirstLinkEditSegment->mappedAddress());
-
- // overwrite mapped LINKEDIT area with new optimized LINKEDIT segment
- memcpy(fFirstLinkEditSegment->mappedAddress(), newLinkEdit, fLinkEditsTotalUnoptimizedSize);
-
- // update all LINKEDIT Segment objects to point to same merged LINKEDIT area
- for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- std::vector<MachOLayoutAbstraction::Segment>& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments();
- for(int i=0; i < segs.size(); ++i) {
- MachOLayoutAbstraction::Segment& seg = segs[i];
- if ( !seg.writable() && !seg.executable() && (strcmp(seg.name(), "__LINKEDIT") == 0) ) {
- seg.setNewAddress(fLinkEditsStartAddress);
- seg.setMappedAddress(fFirstLinkEditSegment->mappedAddress());
- seg.setSize(fLinkEditsTotalOptimizedSize);
- seg.setFileSize(fLinkEditsTotalOptimizedSize);
- seg.setFileOffset(linkEditsFileOffset);
- }
- }
- }
-
- // return new end of cache
- return (uint8_t*)fFirstLinkEditSegment->mappedAddress() + regionAlign(fLinkEditsTotalOptimizedSize);
-}
-
-
-template <typename A>
-class ObjCSelectorUniquer
-{
-private:
- objc_opt::string_map fSelectorStrings;
- SharedCache<A> *fCache;
- size_t fCount;
-
-public:
-
- ObjCSelectorUniquer(SharedCache<A> *newCache)
- : fSelectorStrings()
- , fCache(newCache)
- , fCount(0)
- { }
-
- typename A::P::uint_t visit(typename A::P::uint_t oldValue)
- {
- fCount++;
- const char *s = (const char *)
- fCache->mappedAddressForVMAddress(oldValue);
- objc_opt::string_map::iterator element =
- fSelectorStrings.insert(objc_opt::string_map::value_type(s, oldValue)).first;
- return (typename A::P::uint_t)element->second;
- }
-
- objc_opt::string_map& strings() {
- return fSelectorStrings;
- }
-
- size_t count() const { return fCount; }
-};
-
-
-template <typename A>
-class ClassListBuilder
-{
-private:
- typedef typename A::P P;
-
- objc_opt::string_map fClassNames;
- objc_opt::class_map fClasses;
- size_t fCount;
- HeaderInfoOptimizer<A>& fHinfos;
-
-public:
-
- ClassListBuilder(HeaderInfoOptimizer<A>& hinfos)
- : fClassNames()
- , fClasses()
- , fCount(0)
- , fHinfos(hinfos)
- { }
-
- void visitClass(SharedCache<A>* cache,
- const macho_header<P>* header,
- objc_class_t<A>* cls)
- {
- if (cls->isMetaClass(cache)) return;
-
- const char *name = cls->getName(cache);
- uint64_t name_vmaddr = cache->VMAddressForMappedAddress(name);
- uint64_t cls_vmaddr = cache->VMAddressForMappedAddress(cls);
- uint64_t hinfo_vmaddr = cache->VMAddressForMappedAddress(fHinfos.hinfoForHeader(cache, header));
- fClassNames.insert(objc_opt::string_map::value_type(name, name_vmaddr));
- fClasses.insert(objc_opt::class_map::value_type(name, std::pair<uint64_t, uint64_t>(cls_vmaddr, hinfo_vmaddr)));
- fCount++;
- }
-
- objc_opt::string_map& classNames() {
- return fClassNames;
- }
-
- objc_opt::class_map& classes() {
- return fClasses;
- }
-
- size_t count() const { return fCount; }
-};
-
-
-template <typename A>
-class ProtocolOptimizer
-{
-private:
- typedef typename A::P P;
- typedef typename A::P::uint_t pint_t;
-
- objc_opt::string_map fProtocolNames;
- objc_opt::protocol_map fProtocols;
- size_t fProtocolCount;
- size_t fProtocolReferenceCount;
-
- friend class ProtocolReferenceWalker<A, ProtocolOptimizer<A>>;
- pint_t visitProtocolReference(SharedCache<A>* cache, pint_t oldValue)
- {
- objc_protocol_t<A>* proto = (objc_protocol_t<A>*)
- cache->mappedAddressForVMAddress(oldValue);
- pint_t newValue = fProtocols[proto->getName(cache)];
- if (oldValue != newValue) fProtocolReferenceCount++;
- return newValue;
- }
-
-public:
-
- ProtocolOptimizer()
- : fProtocolNames()
- , fProtocols()
- , fProtocolCount(0)
- , fProtocolReferenceCount(0)
- { }
-
- void addProtocols(SharedCache<A>* cache,
- const macho_header<P>* header)
- {
- PointerSection<A, objc_protocol_t<A> *>
- protocols(cache, header, "__DATA", "__objc_protolist");
-
- for (pint_t i = 0; i < protocols.count(); i++) {
- objc_protocol_t<A> *proto = protocols.get(i);
-
- const char *name = proto->getName(cache);
- if (fProtocolNames.count(name) == 0) {
- // Need a Swift demangler API in OS before we can handle this
- if (0 == strncmp(name, "_TtP", 4)) {
- throw "objc protocol has Swift name";
- }
- if (proto->getSize() > sizeof(objc_protocol_t<A>)) {
- throw "objc protocol is too big";
- }
-
- uint64_t name_vmaddr = cache->VMAddressForMappedAddress(name);
- uint64_t proto_vmaddr = cache->VMAddressForMappedAddress(proto);
- fProtocolNames.insert(objc_opt::string_map::value_type(name, name_vmaddr));
- fProtocols.insert(objc_opt::protocol_map::value_type(name, proto_vmaddr));
- fProtocolCount++;
- }
- }
- }
-
- const char *writeProtocols(SharedCache<A>* cache,
- uint8_t *& dest, size_t& remaining,
- std::vector<void*>& pointersInData,
- pint_t protocolClassVMAddr)
- {
- if (fProtocolCount == 0) return NULL;
-
- if (protocolClassVMAddr == 0) {
- return "libobjc's Protocol class symbol not found (metadata not optimized)";
- }
-
- size_t required = fProtocolCount * sizeof(objc_protocol_t<A>);
- if (remaining < required) {
- return "libobjc's read-write section is too small (metadata not optimized)";
- }
-
- for (objc_opt::protocol_map::iterator iter = fProtocols.begin();
- iter != fProtocols.end();
- ++iter)
- {
- objc_protocol_t<A>* oldProto = (objc_protocol_t<A>*)
- cache->mappedAddressForVMAddress(iter->second);
-
- // Create a new protocol object.
- objc_protocol_t<A>* proto = (objc_protocol_t<A>*)dest;
- dest += sizeof(*proto);
- remaining -= sizeof(*proto);
-
- // Initialize it.
- uint32_t oldSize = oldProto->getSize();
- memcpy(proto, oldProto, oldSize);
- if (!proto->getIsaVMAddr()) {
- proto->setIsaVMAddr(protocolClassVMAddr);
- }
- if (oldSize < sizeof(*proto)) {
- // Protocol object is old. Populate new fields.
- proto->setSize(sizeof(objc_protocol_t<A>));
- // missing extendedMethodTypes is already nil
- }
- // Some protocol objects are big enough to have the
- // demangledName field but don't initialize it.
- if (! proto->getDemangledName(cache)) {
- proto->setDemangledName(cache, proto->getName(cache));
- }
- proto->setFixedUp();
-
- // Redirect the protocol table at our new object.
- iter->second = cache->VMAddressForMappedAddress(proto);
-
- // Add new rebase entries.
- proto->addPointers(pointersInData);
- }
-
- return NULL;
- }
-
- void updateReferences(SharedCache<A>* cache, const macho_header<P>* header)
- {
- ProtocolReferenceWalker<A, ProtocolOptimizer<A>> refs(*this);
- refs.walk(cache, header);
- }
-
- objc_opt::string_map& protocolNames() {
- return fProtocolNames;
- }
-
- objc_opt::protocol_map& protocols() {
- return fProtocols;
- }
-
- size_t protocolCount() const { return fProtocolCount; }
- size_t protocolReferenceCount() const { return fProtocolReferenceCount; }
-};
-
-
-static int percent(size_t num, size_t denom) {
- if (denom) return (int)(num / (double)denom * 100);
- else return 100;
-}
-
-template <typename A>
-void SharedCache<A>::optimizeObjC(std::vector<void*>& pointersInData)
-{
- const char *err;
-
- if ( verbose ) {
- fprintf(stderr, "update_dyld_shared_cache: for %s, optimizing objc metadata\n", archName());
- }
-
- size_t headerSize = P::round_up(sizeof(objc_opt::objc_opt_t));
- if (headerSize != sizeof(objc_opt::objc_opt_t)) {
- warn(archName(), "libobjc's optimization structure size is wrong (metadata not optimized)");
- }
-
- // Find libobjc's empty sections to fill in.
- // Find libobjc's list of pointers for us to use.
- const macho_section<P> *optROSection = NULL;
- const macho_section<P> *optRWSection = NULL;
- const macho_section<P> *optPointerListSection = NULL;
- for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- if ( strstr(it->layout->getFilePath(), "/libobjc.") != NULL ) {
- const macho_header<P>* mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- optROSection = mh->getSection("__TEXT", "__objc_opt_ro");
- optRWSection = mh->getSection("__DATA", "__objc_opt_rw");
- optPointerListSection = mh->getSection("__DATA", "__objc_opt_ptrs");
- break;
- }
- }
-
- if ( optROSection == NULL ) {
- warn(archName(), "libobjc's read-only section missing (metadata not optimized)");
- return;
- }
-
- if ( optRWSection == NULL ) {
- warn(archName(), "libobjc's read/write section missing (metadata not optimized)");
- return;
- }
-
- if ( optPointerListSection == NULL ) {
- warn(archName(), "libobjc's pointer list section missing (metadata not optimized)");
- return;
- }
-
- uint8_t* optROData = (uint8_t*)mappedAddressForVMAddress(optROSection->addr());
- size_t optRORemaining = optROSection->size();
-
- uint8_t* optRWData = (uint8_t*)mappedAddressForVMAddress(optRWSection->addr());
- size_t optRWRemaining = optRWSection->size();
-
- if (optRORemaining < headerSize) {
- warn(archName(), "libobjc's read-only section is too small (metadata not optimized)");
- return;
- }
- objc_opt::objc_opt_t* optROHeader = (objc_opt::objc_opt_t *)optROData;
- optROData += headerSize;
- optRORemaining -= headerSize;
-
- if (E::get32(optROHeader->version) != objc_opt::VERSION) {
- warn(archName(), "libobjc's read-only section version is unrecognized (metadata not optimized)");
- return;
- }
-
- if (optPointerListSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt<pint_t>)) {
- warn(archName(), "libobjc's pointer list section is too small (metadata not optimized)");
- return;
- }
- const objc_opt::objc_opt_pointerlist_tt<pint_t> *optPointerList = (const objc_opt::objc_opt_pointerlist_tt<pint_t> *)mappedAddressForVMAddress(optPointerListSection->addr());
-
- // Write nothing to optROHeader until everything else is written.
- // If something fails below, libobjc will not use the section.
-
- // Find objc-containing dylibs
- std::vector<LayoutInfo> objcDylibs;
- for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- macho_header<P> *mh = (macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- if (mh->getSection("__DATA", "__objc_imageinfo") || mh->getSegment("__OBJC")) {
- objcDylibs.push_back(*it);
- }
- }
-
- // Build image list
-
- // This is SAFE: the binaries themselves are unmodified.
-
- std::vector<LayoutInfo> addressSortedDylibs = objcDylibs;
- std::sort(addressSortedDylibs.begin(), addressSortedDylibs.end(), ByAddressSorter());
-
- uint64_t hinfoVMAddr = optRWSection->addr() + optRWSection->size() - optRWRemaining;
- HeaderInfoOptimizer<A> hinfoOptimizer;
- err = hinfoOptimizer.init(objcDylibs.size(), optRWData, optRWRemaining);
- if (err) {
- warn(archName(), err);
- return;
- }
- for(typename std::vector<LayoutInfo>::const_iterator it = addressSortedDylibs.begin(); it != addressSortedDylibs.end(); ++it) {
- const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- hinfoOptimizer.update(this, mh, pointersInData);
- }
-
-
- // Update selector references and build selector list
-
- // This is SAFE: if we run out of room for the selector table,
- // the modified binaries are still usable.
-
- // Heuristic: choose selectors from libraries with more cstring data first.
- // This tries to localize selector cstring memory.
- ObjCSelectorUniquer<A> uniq(this);
- std::vector<LayoutInfo> sizeSortedDylibs = objcDylibs;
- std::sort(sizeSortedDylibs.begin(), sizeSortedDylibs.end(), ByCStringSectionSizeSorter());
-
- SelectorOptimizer<A, ObjCSelectorUniquer<A> > selOptimizer(uniq);
- for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
- const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- LegacySelectorUpdater<A, ObjCSelectorUniquer<A> >::update(this, mh, uniq);
- selOptimizer.optimize(this, mh);
- }
-
- uint64_t seloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
- objc_opt::objc_selopt_t *selopt = new(optROData) objc_opt::objc_selopt_t;
- err = selopt->write(seloptVMAddr, optRORemaining, uniq.strings());
- if (err) {
- warn(archName(), err);
- return;
- }
- optROData += selopt->size();
- optRORemaining -= selopt->size();
- selopt->byteswap(E::little_endian), selopt = NULL;
-
-
- // Build class table.
-
- // This is SAFE: the binaries themselves are unmodified.
-
- ClassListBuilder<A> classes(hinfoOptimizer);
- ClassWalker< A, ClassListBuilder<A> > classWalker(classes);
- for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
- const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- classWalker.walk(this, mh);
- }
-
- uint64_t clsoptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
- objc_opt::objc_clsopt_t *clsopt = new(optROData) objc_opt::objc_clsopt_t;
- err = clsopt->write(clsoptVMAddr, optRORemaining,
- classes.classNames(), classes.classes(), verbose);
- if (err) {
- warn(archName(), err);
- return;
- }
- optROData += clsopt->size();
- optRORemaining -= clsopt->size();
- size_t duplicateCount = clsopt->duplicateCount();
- clsopt->byteswap(E::little_endian), clsopt = NULL;
-
-
- // Sort method lists.
-
- // This is SAFE: modified binaries are still usable as unsorted lists.
- // This must be done AFTER uniquing selectors.
-
- MethodListSorter<A> methodSorter;
- for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
- macho_header<P> *mh = (macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- methodSorter.optimize(this, mh);
- }
-
-
- // Unique protocols and build protocol table.
-
- // This is SAFE: no protocol references are updated yet
- // This must be done AFTER updating method lists.
-
- ProtocolOptimizer<A> protocolOptimizer;
- for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
- const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- protocolOptimizer.addProtocols(this, mh);
- }
-
- pint_t protocolClassVMAddr = P::getP(optPointerList->protocolClass);
- err = protocolOptimizer.writeProtocols(this, optRWData, optRWRemaining,
- pointersInData, protocolClassVMAddr);
- if (err) {
- warn(archName(), err);
- return;
- }
-
- uint64_t protocoloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
- objc_opt::objc_protocolopt_t *protocolopt = new(optROData) objc_opt::objc_protocolopt_t;
- err = protocolopt->write(protocoloptVMAddr, optRORemaining,
- protocolOptimizer.protocolNames(),
- protocolOptimizer.protocols(), verbose);
- if (err) {
- warn(archName(), err);
- return;
- }
- optROData += protocolopt->size();
- optRORemaining -= protocolopt->size();
- protocolopt->byteswap(E::little_endian), protocolopt = NULL;
-
-
- // Redirect protocol references to the uniqued protocols.
-
- // This is SAFE: the new protocol objects are still usable as-is.
- for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
- const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- protocolOptimizer.updateReferences(this, mh);
- }
-
-
- // Repair ivar offsets.
-
- // This is SAFE: the runtime always validates ivar offsets at runtime.
-
- IvarOffsetOptimizer<A> ivarOffsetOptimizer;
- for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
- const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- ivarOffsetOptimizer.findGCClasses(this, mh);
- }
- for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
- const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- ivarOffsetOptimizer.optimize(this, mh);
- }
-
-
- // Success. Mark dylibs as optimized.
- for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
- const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- const macho_section<P> *imageInfoSection;
- imageInfoSection = mh->getSection("__DATA", "__objc_imageinfo");
- if (!imageInfoSection) {
- imageInfoSection = mh->getSection("__OBJC", "__image_info");
- }
- if (imageInfoSection) {
- objc_image_info<A> *info = (objc_image_info<A> *)
- mappedAddressForVMAddress(imageInfoSection->addr());
- info->setOptimizedByDyld();
- }
- }
-
-
- // Success. Update RO header last.
- E::set32(optROHeader->selopt_offset, seloptVMAddr - optROSection->addr());
- E::set32(optROHeader->clsopt_offset, clsoptVMAddr - optROSection->addr());
- E::set32(optROHeader->protocolopt_offset, protocoloptVMAddr - optROSection->addr());
- E::set32(optROHeader->headeropt_offset, hinfoVMAddr - optROSection->addr());
-
- if ( verbose ) {
- size_t roSize = optROSection->size() - optRORemaining;
- size_t rwSize = optRWSection->size() - optRWRemaining;
- fprintf(stderr, "update_dyld_shared_cache: for %s, %zu/%llu bytes "
- "(%d%%) used in libobjc read-only optimization section\n",
- archName(), roSize, optROSection->size(),
- percent(roSize, optROSection->size()));
- fprintf(stderr, "update_dyld_shared_cache: for %s, %zu/%llu bytes "
- "(%d%%) used in libobjc read/write optimization section\n",
- archName(), rwSize, optRWSection->size(),
- percent(rwSize, optRWSection->size()));
- fprintf(stderr, "update_dyld_shared_cache: for %s, "
- "uniqued %zu selectors\n",
- archName(), uniq.strings().size());
- fprintf(stderr, "update_dyld_shared_cache: for %s, "
- "updated %zu selector references\n",
- archName(), uniq.count());
- fprintf(stderr, "update_dyld_shared_cache: for %s, "
- "uniqued %zu protocols\n",
- archName(), protocolOptimizer.protocolCount());
- fprintf(stderr, "update_dyld_shared_cache: for %s, "
- "updated %zu protocol references\n",
- archName(), protocolOptimizer.protocolReferenceCount());
- fprintf(stderr, "update_dyld_shared_cache: for %s, "
- "updated %zu ivar offsets\n",
- archName(), ivarOffsetOptimizer.optimized());
- fprintf(stderr, "update_dyld_shared_cache: for %s, "
- "sorted %zu method lists\n",
- archName(), methodSorter.optimized());
- fprintf(stderr, "update_dyld_shared_cache: for %s, "
- "recorded %zu classes (%zu duplicates)\n",
- archName(), classes.classNames().size(), duplicateCount);
- fprintf(stderr, "update_dyld_shared_cache: for %s, "
- "wrote objc metadata optimization version %d\n",
- archName(), objc_opt::VERSION);
- }
-
- return;
-}
-
-
-static const char* sCleanupFile = NULL;
-static void cleanup(int sig)
-{
- ::signal(sig, SIG_DFL);
- if ( sCleanupFile != NULL )
- ::unlink(sCleanupFile);
- //if ( verbose )
- // fprintf(stderr, "update_dyld_shared_cache: deleting temp file in response to a signal\n");
- if ( sig == SIGINT )
- ::exit(1);
-}
-
-
-// <rdar://problem/10730767> update_dyld_shared_cache should use sync_volume_np() instead of sync()
-static void sync_volume(const char* volumePath)
-{
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
- int error = sync_volume_np(volumePath, SYNC_VOLUME_FULLSYNC|SYNC_VOLUME_FULLSYNC);
-#else
- int full_sync = 3; // SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_FULLSYNC
- int error = 0;
- if ( fsctl(volumePath, 0x80004101 /*FSCTL_SYNC_VOLUME*/, &full_sync, 0) == -1)
- error = errno;
-#endif
- if ( error )
- ::sync();
-}
-
-
-// <rdar://problem/12552226> update shared cache should sign the shared cache
-static bool adhoc_codesign_share_cache(const char* path)
-{
- CFURLRef target = ::CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)path, strlen(path), FALSE);
- if ( target == NULL )
- return false;
-
- SecStaticCodeRef code;
- OSStatus status = ::SecStaticCodeCreateWithPath(target, kSecCSDefaultFlags, &code);
- CFRelease(target);
- if ( status ) {
- ::fprintf(stderr, "codesign: failed to create url to signed object\n");
- return false;
- }
-
- const void * keys[1] = { (void *)kSecCodeSignerIdentity } ;
- const void * values[1] = { (void *)kCFNull };
- CFDictionaryRef params = ::CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- if ( params == NULL ) {
- CFRelease(code);
- return false;
- }
-
- SecCodeSignerRef signer;
- status = ::SecCodeSignerCreate(params, kSecCSDefaultFlags, &signer);
- CFRelease(params);
- if ( status ) {
- CFRelease(code);
- ::fprintf(stderr, "codesign: failed to create signer object\n");
- return false;
- }
-
- status = ::SecCodeSignerAddSignatureWithErrors(signer, code, kSecCSDefaultFlags, NULL);
- CFRelease(code);
- CFRelease(signer);
- if ( status ) {
- ::fprintf(stderr, "codesign: failed to sign object: %s\n", path);
- return false;
- }
-
- if ( verbose )
- ::fprintf(stderr, "codesigning complete of %s\n", path);
-
- return true;
-}
-
-template <typename A>
-void SharedCache<A>::writeCacheFile(const char *cacheFilePath, uint8_t *cacheFileBuffer, uint32_t cacheFileSize, bool deleteOldCache) {
- char tempCachePath[strlen(cacheFilePath)+16];
- sprintf(tempCachePath, "%s.tmp%u", cacheFilePath, getpid());
-
- try {
- // install signal handlers to delete temp file if program is killed
- sCleanupFile = tempCachePath;
- ::signal(SIGINT, cleanup);
- ::signal(SIGBUS, cleanup);
- ::signal(SIGSEGV, cleanup);
-
- // compute UUID of whole cache
- uint8_t digest[16];
- CC_MD5(cacheFileBuffer, cacheFileSize, digest);
- // <rdar://problem/6723729> uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
- digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 );
- digest[8] = ( digest[8] & 0x3F ) | 0x80;
- ((dyldCacheHeader<E>*)cacheFileBuffer)->set_uuid(digest);
-
- // create var/db/dyld dirs if needed
- char dyldDirs[1024];
- strcpy(dyldDirs, cacheFilePath);
- char* lastSlash = strrchr(dyldDirs, '/');
- if ( lastSlash != NULL )
- lastSlash[1] = '\0';
- struct stat stat_buf;
- if ( stat(dyldDirs, &stat_buf) != 0 ) {
- const char* afterSlash = &dyldDirs[1];
- char* slash;
- while ( (slash = strchr(afterSlash, '/')) != NULL ) {
- *slash = '\0';
- ::mkdir(dyldDirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
- *slash = '/';
- afterSlash = slash+1;
- }
- }
-
- // create temp file for cache
- int fd = ::open(tempCachePath, O_CREAT | O_RDWR | O_TRUNC, 0644);
- if ( fd == -1 )
- throwf("can't create temp file %s, errno=%d", tempCachePath, errno);
-
- // try to allocate whole cache file contiguously
- fstore_t fcntlSpec = { F_ALLOCATECONTIG|F_ALLOCATEALL, F_PEOFPOSMODE, 0, cacheFileSize, 0 };
- ::fcntl(fd, F_PREALLOCATE, &fcntlSpec);
-
- // write out cache file
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: writing cache to disk: %s\n", tempCachePath);
- if ( ::pwrite(fd, cacheFileBuffer, cacheFileSize, 0) != cacheFileSize )
- throwf("write() failure creating cache file, errno=%d", errno);
-
- // flush to disk and close
- int result = ::fcntl(fd, F_FULLFSYNC, NULL);
- if ( result == -1 )
- fprintf(stderr, "update_dyld_shared_cache: warning, fcntl(F_FULLFSYNC) failed with errno=%d for %s\n", errno, tempCachePath);
- result = ::close(fd);
- if ( result != 0 )
- fprintf(stderr, "update_dyld_shared_cache: warning, close() failed with errno=%d for %s\n", errno, tempCachePath);
-
- if ( !iPhoneOS )
- adhoc_codesign_share_cache(tempCachePath);
-
- if ( deleteOldCache ) {
- const char* pathLastSlash = strrchr(cacheFilePath, '/');
- if ( pathLastSlash != NULL ) {
- result = ::unlink(cacheFilePath);
- if ( result != 0 ) {
- if ( errno != ENOENT )
- fprintf(stderr, "update_dyld_shared_cache: warning, unable to remove existing cache %s because errno=%d\n", cacheFilePath, errno);
- }
- }
- }
-
- // move new cache file to correct location for use after reboot
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: atomically moving cache file into place: %s\n", cacheFilePath);
- result = ::rename(tempCachePath, cacheFilePath);
- if ( result != 0 )
- throwf("can't swap newly create dyld shared cache file: rename(%s,%s) returned errno=%d", tempCachePath, cacheFilePath, errno);
-
- // flush everything to disk to assure rename() gets recorded
- sync_volume(cacheFilePath);
-
- // restore default signal handlers
- ::signal(SIGINT, SIG_DFL);
- ::signal(SIGBUS, SIG_DFL);
- ::signal(SIGSEGV, SIG_DFL);
- }
- catch (...){
- // remove temp cache file
- ::unlink(tempCachePath);
- throw;
- }
-}
-
-
-template <> bool SharedCache<x86_64>::addCacheSlideInfo(){ return true; }
-template <> bool SharedCache<arm>::addCacheSlideInfo() { return true; }
-template <> bool SharedCache<x86>::addCacheSlideInfo() { return false; }
-template <> bool SharedCache<arm64>::addCacheSlideInfo() { return true; }
-
-
-template <typename A>
-bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst, int archIndex,
- int archCount, bool keepSignatures, bool dontMapLocalSymbols)
-{
- bool didUpdate = false;
- bool canEmitDevelopmentCache = true;
- char devCacheFilePath[strlen(fCacheFilePath)+strlen(".development")];
- char fileListFilePath[strlen(fCacheFilePath)+strlen(".list")];
- sprintf(devCacheFilePath, "%s.development", fCacheFilePath);
- sprintf(fileListFilePath, "%s.list", fCacheFilePath);
- std::vector<const char *> paths;
-
- // already up to date?
- if ( force || fExistingIsNotUpToDate ) {
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: regenerating %s\n", fCacheFilePath);
- if ( fDylibs.size() == 0 ) {
- fprintf(stderr, "update_dyld_shared_cache: warning, empty cache not generated for arch %s\n", archName());
- return false;
- }
- // delete existing cache while building the new one
- // this is a flag to dyld to stop pinging update_dyld_shared_cache
- if ( deleteExistingFirst )
- ::unlink(fCacheFilePath);
- uint8_t* inMemoryCache = NULL;
- uint32_t allocatedCacheSize = 0;
- try {
- // allocate a memory block to hold cache
- uint32_t cacheFileSize = 0;
- for(std::vector<shared_file_mapping_np>::iterator it = fMappings.begin(); it != fMappings.end(); ++it) {
- uint32_t end = it->sfm_file_offset + it->sfm_size;
- if ( end > cacheFileSize )
- cacheFileSize = end;
- }
- if ( vm_allocate(mach_task_self(), (vm_address_t*)(&inMemoryCache), cacheFileSize, VM_FLAGS_ANYWHERE) != KERN_SUCCESS )
- throwf("can't vm_allocate cache of size %u", cacheFileSize);
- allocatedCacheSize = cacheFileSize;
- fInMemoryCache = inMemoryCache;
-
- // fill in header
- dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)inMemoryCache;
- const char* archPairName = fArchGraph->archName();
- char temp[16];
- strcpy(temp, "dyld_v1 ");
- strcpy(&temp[15-strlen(archPairName)], archPairName);
- header->set_magic(temp);
- //header->set_architecture(arch());
- header->set_mappingOffset(sizeof(dyldCacheHeader<E>));
- header->set_mappingCount(fMappings.size());
- header->set_imagesOffset(header->mappingOffset() + fMappings.size()*sizeof(dyldCacheFileMapping<E>));
- header->set_imagesCount(fDylibs.size()+fDylibAliases.size());
- header->set_dyldBaseAddress(fDyldBaseAddress);
- header->set_codeSignatureOffset(cacheFileSize);
- header->set_codeSignatureSize(0);
- header->set_slideInfoOffset(0);
- header->set_slideInfoSize(0);
- header->set_localSymbolsOffset(0);
- header->set_localSymbolsSize(0);
-
- // fill in mappings
- dyldCacheFileMapping<E>* mapping = (dyldCacheFileMapping<E>*)&inMemoryCache[sizeof(dyldCacheHeader<E>)];
- for(std::vector<shared_file_mapping_np>::iterator it = fMappings.begin(); it != fMappings.end(); ++it) {
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: cache mappings: address=0x%0llX, size=0x%0llX, fileOffset=0x%0llX, prot=0x%X\n",
- it->sfm_address, it->sfm_size, it->sfm_file_offset, it->sfm_init_prot);
- mapping->set_address(it->sfm_address);
- mapping->set_size(it->sfm_size);
- mapping->set_file_offset(it->sfm_file_offset);
- mapping->set_max_prot(it->sfm_max_prot);
- mapping->set_init_prot(it->sfm_init_prot);
- ++mapping;
- }
-
- // fill in image table
- dyldCacheImageInfo<E>* image = (dyldCacheImageInfo<E>*)mapping;
- for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- image->set_address(it->info.address);
- image->set_modTime(it->info.modTime);
- image->set_inode(it->info.inode);
- image->set_pathFileOffset(cacheFileOffsetForVMAddress(it->info.address+it->info.pathFileOffset));
- ++image;
- }
-
- // add aliases to end of image table
- for(typename std::vector<LayoutInfo>::iterator it = fDylibAliases.begin(); it != fDylibAliases.end(); ++it) {
- image->set_address(it->info.address);
- image->set_modTime(it->info.modTime);
- image->set_inode(it->info.inode);
- image->set_pathFileOffset(it->info.pathFileOffset);
- strcpy((char*)inMemoryCache+it->info.pathFileOffset, it->aliases[0]);
- //fprintf(stderr, "adding alias to offset 0x%08X %s\n", it->info.pathFileOffset, it->aliases[0]);
- ++image;
- }
-
- // copy each segment to cache buffer
- const int dylibCount = fDylibs.size();
- int dylibIndex = 0;
- int progressIndex = 0;
- bool foundLibSystem = false;
- for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it, ++dylibIndex) {
- const char* path = it->layout->getFilePath();
- int src = ::open(path, O_RDONLY, 0);
- if ( src == -1 )
- throwf("can't open file %s, errno=%d", it->layout->getID().name, errno);
- // mark source as "don't cache"
- (void)fcntl(src, F_NOCACHE, 1);
- // verify file has not changed since dependency analysis
- struct stat stat_buf;
- if ( fstat(src, &stat_buf) == -1)
- throwf("can't stat open file %s, errno=%d", path, errno);
- if ( (it->layout->getInode() != stat_buf.st_ino) )
- throwf("file inode changed from %llu to %llu during cache creation: %s", it->layout->getInode(), stat_buf.st_ino, path);
- else if ( it->layout->getLastModTime() != stat_buf.st_mtime )
- throwf("file mtime changed from 0x%lX to 0x%lX during cache creation: %s", it->layout->getLastModTime(), stat_buf.st_mtime, path);
- if ( strcmp(it->layout->getID().name, "/usr/lib/libSystem.B.dylib") == 0 )
- foundLibSystem = true;
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: copying %s to cache\n", it->layout->getFilePath());
- try {
- const std::vector<MachOLayoutAbstraction::Segment>& segs = it->layout->getSegments();
- for (int i=0; i < segs.size(); ++i) {
- const MachOLayoutAbstraction::Segment& seg = segs[i];
- if ( verbose ) {
- fprintf(stderr, "\t\tsegment %s, size=0x%0llX, cache address=0x%0llX, buffer address=%p\n",
- seg.name(), seg.size(), seg.newAddress(), &inMemoryCache[cacheFileOffsetForVMAddress(seg.newAddress())]);
- }
- if ( seg.size() > 0 ) {
- const uint64_t segmentSrcStartOffset = it->layout->getOffsetInUniversalFile()+seg.fileOffset();
- const uint64_t segmentSize = seg.fileSize();
- const uint64_t segmentDstStartOffset = cacheFileOffsetForVMAddress(seg.newAddress());
- ssize_t readResult = ::pread(src, &inMemoryCache[segmentDstStartOffset], segmentSize, segmentSrcStartOffset);
- if ( readResult != segmentSize ) {
- if ( readResult == -1 )
- throwf("read failure copying dylib errno=%d for %s", errno, it->layout->getID().name);
- else
- throwf("read failure copying dylib. Read of %lld bytes at file offset %lld returned %ld for %s",
- segmentSize, segmentSrcStartOffset, readResult, it->layout->getID().name);
- }
- }
- }
- }
- catch (const char* msg) {
- throwf("%s while copying %s to shared cache", msg, it->layout->getID().name);
- }
- ::close(src);
- paths.push_back(it->layout->getID().name);
- if ( progress ) {
- // assuming read takes 40% of time
- int nextProgressIndex = archIndex*100+(40*dylibIndex)/dylibCount;
- if ( nextProgressIndex != progressIndex )
- fprintf(stdout, "%3u/%u\n", nextProgressIndex, archCount*100);
- progressIndex = nextProgressIndex;
- }
- }
- if ( !foundLibSystem )
- throw "cache would be missing required dylib /usr/lib/libSystem.B.dylib";
-
- // set mapped address for each segment
- for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- std::vector<MachOLayoutAbstraction::Segment>& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments();
- for (int i=0; i < segs.size(); ++i) {
- MachOLayoutAbstraction::Segment& seg = segs[i];
- if ( seg.size() > 0 )
- seg.setMappedAddress(inMemoryCache + cacheFileOffsetForVMAddress(seg.newAddress()));
- //fprintf(stderr, "%s at %p to %p for %s\n", seg.name(), seg.mappedAddress(), (char*)seg.mappedAddress()+ seg.size(), it->layout->getID().name);
- }
- }
-
- // also construct list of all pointers in cache to other things in cache
- std::vector<void*> pointersInData;
- pointersInData.reserve(1024);
-
- // rebase each dylib in shared cache
- for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- try {
- Rebaser<A> r(*it->layout);
- if (!r.rebase(pointersInData)) {
- canEmitDevelopmentCache = false;
- fprintf(stderr, "update_dyld_shared_cache: Omitting development cache for %s, cannot rebase dylib into place for %s\n", archName(), it->layout->getID().name);
- }
- //if ( verbose )
- // fprintf(stderr, "update_dyld_shared_cache: for %s, rebasing dylib into cache for %s\n", archName(), it->layout->getID().name);
- }
- catch (const char* msg) {
- throwf("%s in %s", msg, it->layout->getID().name);
- }
- }
-
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: for %s, updating binding information for %lu files:\n", archName(), fDylibs.size());
- // instantiate a Binder for each image and add to map
- typename Binder<A>::Map map;
- std::vector<Binder<A>*> binders;
- for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- //fprintf(stderr, "binding %s\n", it->layout->getID().name);
- Binder<A>* binder = new Binder<A>(*it->layout);
- binders.push_back(binder);
- // only add dylibs to map
- if ( it->layout->getID().name != NULL )
- map[it->layout->getID().name] = binder;
- }
- // tell each Binder about the others
- for(typename std::vector<Binder<A>*>::iterator it = binders.begin(); it != binders.end(); ++it) {
- (*it)->setDependentBinders(map);
- }
- // perform binding
- for(typename std::vector<Binder<A>*>::iterator it = binders.begin(); it != binders.end(); ++it) {
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: for %s, updating binding information in cache for %s\n", archName(), (*it)->getDylibID());
- try {
- (*it)->bind(pointersInData);
- }
- catch (const char* msg) {
- throwf("%s in %s", msg, (*it)->getDylibID());
- }
- }
-
- for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- const macho_header<P>* fHeader = (const macho_header<P>*)it->layout->getSegments()[0].mappedAddress();
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- macho_dyld_info_command<P>* fDyldInfo;
- uint64_t originalLinkEditVMAddr = 0;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
- macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
- if ( strcmp(seg->segname(), "__LINKEDIT") != 0 ) {
- pint_t oldFileOff = seg->fileoff();
- originalLinkEditVMAddr += seg->vmsize();
- // don't alter __TEXT until <rdar://problem/7022345> is fixed
- if ( strcmp(seg->segname(), "__TEXT") != 0 ) {
- // update all other segments fileoff to be offset from start of cache file
- seg->set_fileoff(cacheFileOffsetForVMAddress(seg->vmaddr()));
- }
- pint_t fileOffsetDelta = seg->fileoff() - oldFileOff;
- const MachOLayoutAbstraction::Segment* layoutSeg = it->layout->getSegment(seg->segname());
- if ( layoutSeg != NULL ) {
- seg->set_vmsize(layoutSeg->size());
- seg->set_filesize(layoutSeg->fileSize());
- }
- // update all sections in this segment
- macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
- macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
- for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
- if ( sect->offset() != 0 )
- sect->set_offset(sect->offset()+fileOffsetDelta);
- }
- }
- } else if (cmd->cmd() == LC_DYLD_INFO || cmd->cmd() == LC_DYLD_INFO_ONLY) {
- fDyldInfo = (macho_dyld_info_command<P>*)cmd;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
- }
-
- // optimize binding
- for(typename std::vector<Binder<A>*>::iterator it = binders.begin(); it != binders.end(); ++it) {
- try {
- (*it)->optimize();
- }
- catch (const char* msg) {
- throwf("%s in %s", msg, (*it)->getDylibID());
- }
- }
-
- // delete binders
- for(typename std::vector<Binder<A>*>::iterator it = binders.begin(); it != binders.end(); ++it) {
- delete *it;
- }
-
- // merge/optimize all LINKEDIT segments
- if ( optimize ) {
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024));
- cacheFileSize = (this->optimizeLINKEDIT(keepSignatures, dontMapLocalSymbols) - inMemoryCache);
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: optimized cache file size %uMB\n", cacheFileSize/(1024*1024));
- // update header to reduce mapping size
- dyldCacheHeader<E>* cacheHeader = (dyldCacheHeader<E>*)inMemoryCache;
- dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&inMemoryCache[sizeof(dyldCacheHeader<E>)];
- dyldCacheFileMapping<E>* lastMapping = &mappings[cacheHeader->mappingCount()-1];
- lastMapping->set_size(cacheFileSize-lastMapping->file_offset());
- // update fMappings so .map file will print correctly
- fMappings.back().sfm_size = cacheFileSize-fMappings.back().sfm_file_offset;
- // update header
- //fprintf(stderr, "update_dyld_shared_cache: changing end of cache address from 0x%08llX to 0x%08llX\n",
- // header->codeSignatureOffset(), fMappings.back().sfm_address + fMappings.back().sfm_size);
- header->set_codeSignatureOffset(fMappings.back().sfm_file_offset + fMappings.back().sfm_size);
- }
-
- // dump dev cache with optimized linkedit, but not ObjC optimizations
- if (iPhoneOS && canEmitDevelopmentCache) {
- int fileListFD = ::open(fileListFilePath, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- if ( fileListFD != -1 ) {
- for (const char* path : paths) {
- write(fileListFD, path, strlen(path)+1);
- write(fileListFD, "\n", 1);
- }
- close(fileListFD);
- }
-
- ((dyldCacheHeader<E>*)inMemoryCache)->set_cacheType(1);
- writeCacheFile(devCacheFilePath, inMemoryCache, cacheFileSize, fCacheFileInFinalLocation);
- }
-
- // unique objc selectors and update other objc metadata
- if ( optimize ) {
- optimizeObjC(pointersInData);
- if ( progress ) {
- // assuming objc optimizations takes 15% of time
- fprintf(stdout, "%3u/%u\n", (archIndex+1)*55, archCount*100);
- }
- }
-
- if ( addCacheSlideInfo() ) {
- // build bitmap of which pointers need sliding
- uint8_t* const dataStart = &inMemoryCache[fMappings[1].sfm_file_offset]; // R/W mapping is always second
- uint8_t* const dataEnd = &inMemoryCache[fMappings[1].sfm_file_offset+fMappings[1].sfm_size];
- const int bitmapSize = (dataEnd - dataStart)/(4*8);
- uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1);
- void* lastPointer = inMemoryCache;
- for(std::vector<void*>::iterator pit=pointersInData.begin(); pit != pointersInData.end(); ++pit) {
- if ( *pit != lastPointer ) {
- void* p = *pit;
- if ( (p < dataStart) || ( p > dataEnd) )
- throwf("DATA pointer for sliding, out of range 0x%08lX\n", (long)((uint8_t*)p-inMemoryCache));
- long offset = (long)((uint8_t*)p - dataStart);
- if ( (offset % 4) != 0 )
- throwf("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset);
- long byteIndex = offset / (4*8);
- long bitInByte = (offset % 32) >> 2;
- bitmap[byteIndex] |= (1 << bitInByte);
- lastPointer = p;
- }
- }
-
- // allocate worst case size block of all slide info
- const int entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes.
- const int toc_count = bitmapSize/entry_size;
- int slideInfoSize = sizeof(dyldCacheSlideInfo<E>) + 2*toc_count + entry_size*(toc_count+1);
- dyldCacheSlideInfo<E>* slideInfo = (dyldCacheSlideInfo<E>*)calloc(slideInfoSize, 1);
- slideInfo->set_version(1);
- slideInfo->set_toc_offset(sizeof(dyldCacheSlideInfo<E>));
- slideInfo->set_toc_count(toc_count);
- slideInfo->set_entries_offset((slideInfo->toc_offset()+2*toc_count+127)&(-128));
- slideInfo->set_entries_count(0);
- slideInfo->set_entries_size(entry_size);
- // append each unique entry
- const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap;
- dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset());
- int entry_count = 0;
- for (int i=0; i < toc_count; ++i) {
- const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i];
- // see if it is same as one already added
- bool found = false;
- for (int j=0; j < entry_count; ++j) {
- if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) {
- //fprintf(stderr, "toc[%d] optimized to %d\n", i, j);
- slideInfo->set_toc(i, j);
- found = true;
- break;
- }
- }
- if ( ! found ) {
- // append to end
- memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size);
- slideInfo->set_toc(i, entry_count++);
- }
- }
- slideInfo->set_entries_count(entry_count);
-
- int slideInfoPageSize = regionAlign(slideInfo->entries_offset() + entry_count*entry_size);
- cacheFileSize += slideInfoPageSize;
-
- // update mappings to increase RO size
- dyldCacheHeader<E>* cacheHeader = (dyldCacheHeader<E>*)inMemoryCache;
- dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&inMemoryCache[sizeof(dyldCacheHeader<E>)];
- dyldCacheFileMapping<E>* lastMapping = &mappings[cacheHeader->mappingCount()-1];
- lastMapping->set_size(lastMapping->size()+slideInfoPageSize);
-
- // update header to show location of slidePointers
- cacheHeader->set_slideInfoOffset(cacheHeader->codeSignatureOffset());
- cacheHeader->set_slideInfoSize(slideInfoPageSize);
- cacheHeader->set_codeSignatureOffset(cacheHeader->codeSignatureOffset()+slideInfoPageSize);
-
- // update fMappings so .map file will print correctly
- fMappings.back().sfm_size = cacheFileSize-fMappings.back().sfm_file_offset;
-
- // copy compressed into into buffer
- memcpy(&inMemoryCache[cacheHeader->slideInfoOffset()], slideInfo, slideInfoPageSize);
- }
-
- // append local symbol info in an unmapped region
- if ( dontMapLocalSymbols ) {
- uint32_t spaceAtEnd = allocatedCacheSize - cacheFileSize;
- uint32_t localSymbolsOffset = pageAlign(cacheFileSize);
- dyldCacheLocalSymbolsInfo<E>* infoHeader = (dyldCacheLocalSymbolsInfo<E>*)(&inMemoryCache[localSymbolsOffset]);
- const uint32_t entriesOffset = sizeof(dyldCacheLocalSymbolsInfo<E>);
- const uint32_t entriesCount = fLocalSymbolInfos.size();
- const uint32_t nlistOffset = entriesOffset + entriesCount * sizeof(dyldCacheLocalSymbolEntry<E>);
- const uint32_t nlistCount = fUnmappedLocalSymbols.size();
- const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist<P>);
- const uint32_t stringsSize = fUnmappedLocalsStringPool.size();
- if ( stringsOffset+stringsSize > spaceAtEnd )
- throwf("update_dyld_shared_cache[%u] for arch=%s, out of space for local symbols. Have 0x%X, Need 0x%X\n",
- getpid(), fArchGraph->archName(), spaceAtEnd, stringsOffset+stringsSize);
- // fill in local symbols info
- infoHeader->set_nlistOffset(nlistOffset);
- infoHeader->set_nlistCount(nlistCount);
- infoHeader->set_stringsOffset(stringsOffset);
- infoHeader->set_stringsSize(stringsSize);
- infoHeader->set_entriesOffset(entriesOffset);
- infoHeader->set_entriesCount(entriesCount);
- // copy info for each dylib
- dyldCacheLocalSymbolEntry<E>* entries = (dyldCacheLocalSymbolEntry<E>*)(&inMemoryCache[localSymbolsOffset+entriesOffset]);
- for (int i=0; i < entriesCount; ++i) {
- entries[i].set_dylibOffset(fLocalSymbolInfos[i].dylibOffset);
- entries[i].set_nlistStartIndex(fLocalSymbolInfos[i].nlistStartIndex);
- entries[i].set_nlistCount(fLocalSymbolInfos[i].nlistCount);
- }
- // copy nlists
- memcpy(&inMemoryCache[localSymbolsOffset+nlistOffset], &fUnmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
- // copy string pool
- memcpy(&inMemoryCache[localSymbolsOffset+stringsOffset], fUnmappedLocalsStringPool.getBuffer(), stringsSize);
-
- // update state
- fUnmappedLocalSymbolsSize = pageAlign(stringsOffset + stringsSize);
- cacheFileSize = regionAlign(localSymbolsOffset + fUnmappedLocalSymbolsSize);
-
- // update header to show location of slidePointers
- dyldCacheHeader<E>* cacheHeader = (dyldCacheHeader<E>*)inMemoryCache;
- cacheHeader->set_localSymbolsOffset(localSymbolsOffset);
- cacheHeader->set_localSymbolsSize(stringsOffset+stringsSize);
- cacheHeader->set_codeSignatureOffset(cacheFileSize);
- }
-
- // make sure after all optimizations, that whole cache file fits into shared region address range
- {
- dyldCacheHeader<E>* cacheHeader = (dyldCacheHeader<E>*)inMemoryCache;
- dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&inMemoryCache[cacheHeader->mappingOffset()];
- // <rdar://problem/16128830> incorporate code signature size into overflow check
- uint32_t estCodeSigSize = regionAlign(cacheFileSize/200); // guess 0.5% for code signature
- for (int i=0; i < cacheHeader->mappingCount(); ++i) {
- uint64_t endAddr = mappings[i].address() + mappings[i].size() + estCodeSigSize;
- if ( endAddr > (sharedRegionStartAddress() + sharedRegionSize()) ) {
- throwf("update_dyld_shared_cache[%u] for arch=%s, shared cache will not fit in shared regions address space. Overflow amount: %lluKB\n",
- getpid(), fArchGraph->archName(), (endAddr-(sharedRegionStartAddress() + sharedRegionSize()))/1024);
- }
- }
- }
-
- if ( fVerify ) {
- // if no existing cache, say so
- if ( fExistingCacheForVerification == NULL ) {
- throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify because cache file does not exist in /var/db/dyld/\n",
- getpid(), archName());
- }
- // new cache is built, compare header entries
- const dyldCacheHeader<E>* newHeader = (dyldCacheHeader<E>*)inMemoryCache;
- const dyldCacheHeader<E>* oldHeader = (dyldCacheHeader<E>*)fExistingCacheForVerification;
- if ( newHeader->mappingCount() != oldHeader->mappingCount() ) {
- throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify cache because caches have a different number of mappings\n",
- getpid(), archName());
- }
- const dyldCacheFileMapping<E>* newMappings = (dyldCacheFileMapping<E>*)&inMemoryCache[newHeader->mappingOffset()];
- const dyldCacheFileMapping<E>* oldMappings = (dyldCacheFileMapping<E>*)&fExistingCacheForVerification[oldHeader->mappingOffset()];
- for (int i=0; i < newHeader->mappingCount(); ++i) {
- if ( newMappings[i].address() != oldMappings[i].address() ) {
- throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify cache because mapping %d starts at a different address 0x%0llX vs 0x%0llX\n",
- getpid(), archName(), i, newMappings[i].address(), oldMappings[i].address() );
- }
- if ( newMappings[i].size() != oldMappings[i].size() ) {
- throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify cache because mapping %d has a different size\n",
- getpid(), archName(), i);
- }
- }
-
- //fprintf(stderr, "%s existing cache = %p\n", archName(), fExistingCacheForVerification);
- //fprintf(stderr, "%s new cache = %p\n", archName(), inMemoryCache);
- // compare content to existing cache page by page
- for (int offset=0; offset < cacheFileSize; offset += 4096) {
- if ( memcmp(&inMemoryCache[offset], &fExistingCacheForVerification[offset], 4096) != 0 ) {
- fprintf(stderr, "verifier found differences on page offset 0x%08X for %s:\n", offset, archName());
- for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it, ++dylibIndex) {
- const std::vector<MachOLayoutAbstraction::Segment>& segs = it->layout->getSegments();
- for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator sit = segs.begin(); sit != segs.end(); ++sit) {
- const MachOLayoutAbstraction::Segment& seg = *sit;
- if ( (seg.mappedAddress() <= &inMemoryCache[offset]) && (&inMemoryCache[offset] < ((uint8_t*)seg.mappedAddress() + seg.fileSize())) ) {
- // all LINKEDITs point to the same region, so just print one
- if ( strcmp(seg.name(), "__LINKEDIT") == 0 )
- fprintf(stderr, " in merged LINKEDIT segment\n");
- else
- fprintf(stderr, " in segment %s of dylib %s\n", seg.name(), it->layout->getID().name);
- break;
- }
- }
- }
- for (int po=0; po < 4096; po += 16) {
- if ( memcmp(&inMemoryCache[offset+po], &fExistingCacheForVerification[offset+po], 16) != 0 ) {
- fprintf(stderr, " existing: 0x%08X: ", offset+po);
- for ( int j=0; j < 16; ++j)
- fprintf(stderr, " 0x%02X", fExistingCacheForVerification[offset+po+j]);
- fprintf(stderr, "\n");
- fprintf(stderr, " should be: 0x%08X: ", offset+po);
- for ( int j=0; j < 16; ++j)
- fprintf(stderr, " 0x%02X", inMemoryCache[offset+po+j]);
- fprintf(stderr, "\n");
- }
- }
- }
- }
- }
- else {
- ((dyldCacheHeader<E>*)inMemoryCache)->set_cacheType(0);
- writeCacheFile(fCacheFilePath, inMemoryCache, cacheFileSize, fCacheFileInFinalLocation);
- didUpdate = true;
- // generate human readable "map" file that shows the layout of the cache file
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: writing .map file to disk\n");
- char mapFilePath[strlen(fCacheFilePath)+16];
- sprintf(mapFilePath, "%s.map", fCacheFilePath);
- char tempMapFilePath[strlen(fCacheFilePath)+32];
- sprintf(tempMapFilePath, "%s.map%u", fCacheFilePath, getpid());
- FILE* fmap = ::fopen(tempMapFilePath, "w");
- if ( fmap == NULL ) {
- fprintf(stderr, "can't create map file %s, errno=%d", tempMapFilePath, errno);
- }
- else {
- for(std::vector<shared_file_mapping_np>::iterator it = fMappings.begin(); it != fMappings.end(); ++it) {
- const char* prot = "RW";
- if ( it->sfm_init_prot == (VM_PROT_EXECUTE|VM_PROT_READ) )
- prot = "EX";
- else if ( it->sfm_init_prot == VM_PROT_READ )
- prot = "RO";
- else if ( it->sfm_init_prot == (VM_PROT_EXECUTE|VM_PROT_WRITE|VM_PROT_READ) )
- prot = "WX";
- if ( it->sfm_size > 1024*1024 )
- fprintf(fmap, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, it->sfm_size/(1024*1024),
- it->sfm_address, it->sfm_address+it->sfm_size);
- else
- fprintf(fmap, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, it->sfm_size/1024,
- it->sfm_address, it->sfm_address+it->sfm_size);
- }
-
- fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX weak binding info\n",
- (fOffsetOfExportInfoInCombinedLinkedit-fOffsetOfWeakBindInfoInCombinedLinkedit)/1024,
- fLinkEditsStartAddress+fOffsetOfWeakBindInfoInCombinedLinkedit,
- fLinkEditsStartAddress+fOffsetOfExportInfoInCombinedLinkedit);
- fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX export info\n",
- (fOffsetOfBindInfoInCombinedLinkedit-fOffsetOfExportInfoInCombinedLinkedit)/1024,
- fLinkEditsStartAddress+fOffsetOfExportInfoInCombinedLinkedit,
- fLinkEditsStartAddress+fOffsetOfBindInfoInCombinedLinkedit);
- fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX binding info\n",
- (fOffsetOfLazyBindInfoInCombinedLinkedit-fOffsetOfBindInfoInCombinedLinkedit)/1024,
- fLinkEditsStartAddress+fOffsetOfBindInfoInCombinedLinkedit,
- fLinkEditsStartAddress+fOffsetOfLazyBindInfoInCombinedLinkedit);
- fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX lazy binding info\n",
- (fOffsetOfOldSymbolTableInfoInCombinedLinkedit-fOffsetOfLazyBindInfoInCombinedLinkedit)/1024,
- fLinkEditsStartAddress+fOffsetOfLazyBindInfoInCombinedLinkedit,
- fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit);
- fprintf(fmap, " linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld symbol table size\n",
- (fSizeOfOldSymbolTableInfoInCombinedLinkedit)/(1024*1024),
- fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit,
- fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit+fSizeOfOldSymbolTableInfoInCombinedLinkedit);
- if ( fSizeOfFunctionStartsInCombinedLinkedit != 0 )
- fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld functions starts size\n",
- fSizeOfFunctionStartsInCombinedLinkedit/1024,
- fLinkEditsStartAddress+fOffsetOfFunctionStartsInCombinedLinkedit,
- fLinkEditsStartAddress+fOffsetOfFunctionStartsInCombinedLinkedit+fSizeOfFunctionStartsInCombinedLinkedit);
- if ( fSizeOfDataInCodeInCombinedLinkedit != 0 )
- fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld data-in-code info size\n",
- fSizeOfDataInCodeInCombinedLinkedit/1024,
- fLinkEditsStartAddress+fOffsetOfDataInCodeInCombinedLinkedit,
- fLinkEditsStartAddress+fOffsetOfDataInCodeInCombinedLinkedit+fSizeOfDataInCodeInCombinedLinkedit);
- if ( fSizeOfOldExternalRelocationsInCombinedLinkedit != 0 )
- fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld external relocs size\n",
- fSizeOfOldExternalRelocationsInCombinedLinkedit/1024,
- fLinkEditsStartAddress+fOffsetOfOldExternalRelocationsInCombinedLinkedit,
- fLinkEditsStartAddress+fOffsetOfOldExternalRelocationsInCombinedLinkedit+fSizeOfOldExternalRelocationsInCombinedLinkedit);
- fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld indirect symbol table size\n",
- fSizeOfOldIndirectSymbolsInCombinedLinkedit/1024,
- fLinkEditsStartAddress+fOffsetOfOldIndirectSymbolsInCombinedLinkedit,
- fLinkEditsStartAddress+fOffsetOfOldIndirectSymbolsInCombinedLinkedit+fSizeOfOldIndirectSymbolsInCombinedLinkedit);
- fprintf(fmap, " linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld string pool\n",
- (fSizeOfOldStringPoolInCombinedLinkedit)/(1024*1024),
- fLinkEditsStartAddress+fOffsetOfOldStringPoolInCombinedLinkedit,
- fLinkEditsStartAddress+fOffsetOfOldStringPoolInCombinedLinkedit+fSizeOfOldStringPoolInCombinedLinkedit);
-
- dyldCacheHeader<E>* cacheHeader = (dyldCacheHeader<E>*)inMemoryCache;
- if ( cacheHeader->slideInfoSize() != 0 ) {
- fprintf(fmap, " linkedit %4lluKB kernel slide info\n", (cacheHeader->slideInfoSize())/1024);
- }
-
- fprintf(fmap, "unmapped -- %4uMB local symbol info\n", fUnmappedLocalSymbolsSize/(1024*1024));
-
- uint64_t endMappingAddr = fMappings[2].sfm_address + fMappings[2].sfm_size;
- fprintf(fmap, "total map %4lluMB\n", (endMappingAddr - sharedRegionStartAddress())/(1024*1024));
- if ( sharedRegionStartWritableAddress(0) == 0x7FFF70000000LL ) {
- // x86_64 has different slide constraints
- uint64_t freeSpace = 256*1024*1024 - fMappings[1].sfm_size;
- fprintf(fmap, "r/w space %4lluMB -> %d bits of entropy for ASLR\n\n", freeSpace/(1024*1024), (int)log2(freeSpace/4096));
- }
- else {
- uint64_t freeSpace = sharedRegionStartAddress() + sharedRegionSize() - endMappingAddr;
- fprintf(fmap, "free space %4lluMB -> %d bits of entropy for ASLR\n\n", freeSpace/(1024*1024), (int)log2(freeSpace/4096));
- }
-
- for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- fprintf(fmap, "%s\n", it->layout->getID().name);
- for (std::vector<const char*>::const_iterator ait = it->aliases.begin(); ait != it->aliases.end(); ++ait)
- fprintf(fmap, "%s\n", *ait);
- const std::vector<MachOLayoutAbstraction::Segment>& segs = it->layout->getSegments();
- for (int i=0; i < segs.size(); ++i) {
- const MachOLayoutAbstraction::Segment& seg = segs[i];
- fprintf(fmap, "\t%16s 0x%0llX -> 0x%0llX\n", seg.name(), seg.newAddress(), seg.newAddress()+seg.size());
- }
- }
- if ( warnings.size() > 0 ) {
- fprintf(fmap, "# Warnings:\n");
- for (std::vector<const char*>::iterator it=warnings.begin(); it != warnings.end(); ++it) {
- fprintf(fmap, "# %s\n", *it);
- }
- }
- fclose(fmap);
- ::rename(tempMapFilePath, mapFilePath);
- }
- }
-
- // free in memory cache
- vm_deallocate(mach_task_self(), (vm_address_t)inMemoryCache, allocatedCacheSize);
- inMemoryCache = NULL;
- if ( progress ) {
- // finished
- fprintf(stdout, "%3u/%u\n", (archIndex+1)*100, archCount*100);
- }
- }
- catch (...){
- // remove in memory cache
- if ( inMemoryCache != NULL )
- vm_deallocate(mach_task_self(), (vm_address_t)inMemoryCache, allocatedCacheSize);
- throw;
- }
- }
- return didUpdate;
-}
-
-
-
-//
-// The shared cache is driven by /var/db/dyld/shared_region_roots which contains
-// the paths used to search for dylibs that should go in the shared cache
-//
-// Leading and trailing white space is ignored
-// Blank lines are ignored
-// Lines starting with # are ignored
-//
-static void parsePathsFile(const char* filePath, std::vector<const char*>& paths)
-{
- // read in whole file
- int fd = open(filePath, O_RDONLY, 0);
- if ( fd == -1 ) {
- fprintf(stderr, "update_dyld_shared_cache: can't open file: %s\n", filePath);
- exit(1);
- }
- struct stat stat_buf;
- fstat(fd, &stat_buf);
- char* p = (char*)malloc(stat_buf.st_size);
- if ( p == NULL ) {
- fprintf(stderr, "update_dyld_shared_cache: malloc failure\n");
- exit(1);
- }
- if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) {
- fprintf(stderr, "update_dyld_shared_cache: can't read file: %s\n", filePath);
- exit(1);
- }
- ::close(fd);
-
- // parse into paths and add to vector
- char * const end = &p[stat_buf.st_size];
- enum { lineStart, inSymbol, inComment } state = lineStart;
- char* symbolStart = NULL;
- for (char* s = p; s < end; ++s ) {
- switch ( state ) {
- case lineStart:
- if ( *s =='#' ) {
- state = inComment;
- }
- else if ( !isspace(*s) ) {
- state = inSymbol;
- symbolStart = s;
- }
- break;
- case inSymbol:
- if ( *s == '\n' ) {
- *s = '\0';
- // removing any trailing spaces
- char* last = s-1;
- while ( isspace(*last) ) {
- *last = '\0';
- --last;
- }
- paths.push_back(symbolStart);
- symbolStart = NULL;
- state = lineStart;
- }
- break;
- case inComment:
- if ( *s == '\n' )
- state = lineStart;
- break;
- }
- }
- // Note: we do not free() the malloc buffer, because the strings in it are used by exec()
-}
-
-
-
-static void setSharedDylibs(const char* rootPath, const std::vector<const char*>& overlayPaths, const std::set<ArchPair>& onlyArchs, std::vector<const char*> rootsPaths)
-{
- // set file system root
- ArchGraph::setFileSystemRoot(rootPath);
- ArchGraph::setFileSystemOverlay(overlayPaths);
-
- // initialize all architectures requested
- for(std::set<ArchPair>::iterator a = onlyArchs.begin(); a != onlyArchs.end(); ++a)
- ArchGraph::addArchPair(*a);
-
- // add roots to graph
- for(std::vector<const char*>::const_iterator it = rootsPaths.begin(); it != rootsPaths.end(); ++it)
- ArchGraph::addRoot(*it, onlyArchs);
-
- // determine shared dylibs
- for(std::set<ArchPair>::iterator a = onlyArchs.begin(); a != onlyArchs.end(); ++a)
- ArchGraph::findSharedDylibs(*a);
-}
-
-
-static void scanForSharedDylibs(const char* rootPath, const std::vector<const char*>& overlayPaths, const char* dirOfPathFiles, const std::set<ArchPair>& onlyArchs)
-{
- char rootDirOfPathFiles[strlen(rootPath)+strlen(dirOfPathFiles)+2];
- // in -root mode, look for roots in /rootpath/var/db/dyld
- if ( rootPath[0] != '\0' ) {
- strcpy(rootDirOfPathFiles, rootPath);
- strcat(rootDirOfPathFiles, dirOfPathFiles);
- dirOfPathFiles = rootDirOfPathFiles;
- }
-
- // extract all root paths from files in "/var/db/dyld/shared_region_roots/"
- if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: finding roots in: %s\n", dirOfPathFiles);
- std::vector<const char*> rootsPaths;
- DIR* dir = ::opendir(dirOfPathFiles);
- if ( dir == NULL )
- throwf("%s does not exist, errno=%d\n", dirOfPathFiles, errno);
- for (dirent* entry = ::readdir(dir); entry != NULL; entry = ::readdir(dir)) {
- if ( entry->d_type == DT_REG || entry->d_type == DT_UNKNOWN ) {
- // only look at regular files ending in .paths
- if ( strcmp(&entry->d_name[entry->d_namlen-6], ".paths") == 0 ) {
- struct stat tmpStatPathsFile;
- char fullPath[strlen(dirOfPathFiles)+entry->d_namlen+2];
- strcpy(fullPath, dirOfPathFiles);
- strcat(fullPath, "/");
- strcat(fullPath, entry->d_name);
- if ( lstat(fullPath, &tmpStatPathsFile) == -1 ) {
- fprintf(stderr, "update_dyld_shared_cache: can't access %s\n", fullPath);
- }
- else if ( S_ISREG(tmpStatPathsFile.st_mode) ) {
- parsePathsFile(fullPath, rootsPaths);
- }
- else {
- fprintf(stderr, "update_dyld_shared_cache: wrong file type for %s\n", fullPath);
- }
- }
- else {
- fprintf(stderr, "update_dyld_shared_cache: warning, ignore file with wrong extension: %s\n", entry->d_name);
- }
- }
- }
- ::closedir(dir);
-
- if ( rootsPaths.size() == 0 )
- fprintf(stderr, "update_dyld_shared_cache: warning, no entries found in shared_region_roots\n");
- setSharedDylibs(rootPath, overlayPaths, onlyArchs, rootsPaths);
-}
-
-static void setSharedDylibs(const char* rootPath, const std::vector<const char*>& overlayPaths, const char* pathsFile, const std::set<ArchPair>& onlyArchs)
-{
- std::vector<const char*> rootsPaths;
- parsePathsFile(pathsFile, rootsPaths);
- setSharedDylibs(rootPath, overlayPaths, onlyArchs, rootsPaths);
-}
-
-
-// If the 10.5.0 version of update_dyld_shared_cache was killed or crashed, it
-// could leave large half written cache files laying around. The function deletes
-// those files. To prevent the deletion of tmp files being created by another
-// copy of update_dyld_shared_cache, it only deletes the temp cache file if its
-// creation time was before the last restart of this machine.
-static void deleteOrphanTempCacheFiles()
-{
- DIR* dir = ::opendir(MACOSX_DYLD_SHARED_CACHE_DIR);
- if ( dir != NULL ) {
- std::vector<const char*> filesToDelete;
- for (dirent* entry = ::readdir(dir); entry != NULL; entry = ::readdir(dir)) {
- if ( entry->d_type == DT_REG ) {
- // only look at files with .tmp in name
- if ( strstr(entry->d_name, ".tmp") != NULL ) {
- char fullPath[strlen(MACOSX_DYLD_SHARED_CACHE_DIR)+entry->d_namlen+2];
- strcpy(fullPath, MACOSX_DYLD_SHARED_CACHE_DIR);
- strcat(fullPath, "/");
- strcat(fullPath, entry->d_name);
- struct stat tmpFileStatInfo;
- if ( stat(fullPath, &tmpFileStatInfo) != -1 ) {
- int mib[2] = {CTL_KERN, KERN_BOOTTIME};
- struct timeval boottime;
- size_t size = sizeof(boottime);
- if ( (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1) && (boottime.tv_sec != 0) ) {
- // make sure this file is older than the boot time of this machine
- if ( tmpFileStatInfo.st_mtime < boottime.tv_sec ) {
- filesToDelete.push_back(strdup(fullPath));
- }
- }
- }
- }
- }
- }
- ::closedir(dir);
- for(std::vector<const char*>::iterator it = filesToDelete.begin(); it != filesToDelete.end(); ++it) {
- fprintf(stderr, "update_dyld_shared_cache: deleting old temp cache file: %s\n", *it);
- ::unlink(*it);
- }
- }
-}
-
-
-
-static bool updateSharedeCacheFile(const char* rootPath, const std::vector<const char*>& overlayPaths, const char* cacheDir, bool explicitCacheDir, const std::set<ArchPair>& onlyArchs,
- bool force, bool alphaSort, bool optimize, bool deleteExistingFirst, bool verify, bool keepSignatures, bool dontMapLocalSymbols)
-{
- bool didUpdate = false;
- // get dyld load address info
- UniversalMachOLayout* dyldLayout = NULL;
- char dyldPath[1024];
- strlcpy(dyldPath, rootPath, 1024);
- strlcat(dyldPath, "/usr/lib/dyld", 1024);
- struct stat stat_buf;
- if ( stat(dyldPath, &stat_buf) == 0 ) {
- dyldLayout = new UniversalMachOLayout(dyldPath, &onlyArchs);
- }
- else {
- dyldLayout = new UniversalMachOLayout("/usr/lib/dyld", &onlyArchs);
- }
- const int archCount = onlyArchs.size();
- int index = 0;
- for(std::set<ArchPair>::iterator a = onlyArchs.begin(); a != onlyArchs.end(); ++a, ++index) {
- const MachOLayoutAbstraction* dyldLayoutForArch = dyldLayout->getSlice(*a);
- uint64_t dyldBaseAddress = 0;
- if ( dyldLayoutForArch != NULL )
- dyldBaseAddress = dyldLayoutForArch->getBaseAddress();
- else
- fprintf(stderr, "update_dyld_shared_cache: warning, dyld not available for specified architectures\n");
- switch ( a->arch ) {
- case CPU_TYPE_I386:
- {
- SharedCache<x86> cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPaths, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress);
- didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures, dontMapLocalSymbols);
- }
- break;
- case CPU_TYPE_X86_64:
- {
- SharedCache<x86_64> cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPaths, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress);
- didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures, dontMapLocalSymbols);
- }
- break;
- case CPU_TYPE_ARM:
- {
- SharedCache<arm> cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPaths, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress);
- didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures, dontMapLocalSymbols);
- }
- break;
- case CPU_TYPE_ARM64:
- {
- SharedCache<arm64> cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPaths, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress);
- didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures, dontMapLocalSymbols);
- }
- break;
- }
- }
-
- if ( !iPhoneOS )
- deleteOrphanTempCacheFiles();
-
- return didUpdate;
-}
-
-
-static void usage()
-{
- fprintf(stderr, "update_dyld_shared_cache [-force] [-root dir] [-overlay dir] [-arch arch] [-debug]\n");
-}
-
-
-int main(int argc, const char* argv[])
-{
- std::set<ArchPair> onlyArchs;
- const char* rootPath = "";
- std::vector<const char*> overlayPaths;
- const char* dylibListFile = NULL;
- bool force = false;
- bool alphaSort = false;
- bool optimize = true;
- bool verify = false;
- bool keepSignatures = false;
- bool explicitCacheDir = false;
- bool dontMapLocalSymbols = false;
- bool relaunchForHaswell = false;
- const char* cacheDir = NULL;
-
- try {
- // parse command line options
- for(int i=1; i < argc; ++i) {
- const char* arg = argv[i];
- if ( arg[0] == '-' ) {
- if ( strcmp(arg, "-debug") == 0 ) {
- verbose = true;
- }
- else if ( strcmp(arg, "-force") == 0 ) {
- force = true;
- }
- else if ( strcmp(arg, "-verify") == 0 ) {
- verify = true;
- }
- else if ( strcmp(arg, "-sort_by_name") == 0 ) {
- alphaSort = true;
- }
- else if ( strcmp(arg, "-progress") == 0 ) {
- progress = true;
- }
- else if ( strcmp(arg, "-opt") == 0 ) {
- optimize = true;
- }
- else if ( strcmp(arg, "-no_opt") == 0 ) {
- optimize = false;
- }
- else if ( strcmp(arg, "-dont_map_local_symbols") == 0 ) {
- dontMapLocalSymbols = true;
- }
- else if ( strcmp(arg, "-iPhone") == 0 ) {
- iPhoneOS = true;
- alphaSort = true;
- }
- else if ( strcmp(arg, "-dylib_list") == 0 ) {
- dylibListFile = argv[++i];
- if ( dylibListFile == NULL )
- throw "-dylib_list missing path argument";
- }
- else if ( (strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0) ) {
- rootPath = argv[++i];
- if ( rootPath == NULL )
- throw "-root missing path argument";
- }
- else if ( strcmp(arg, "-overlay") == 0 ) {
- const char* path = argv[++i];
- if ( path == NULL )
- throw "-overlay missing path argument";
- overlayPaths.push_back(path);
- }
- else if ( strcmp(arg, "-cache_dir") == 0 ) {
- cacheDir = argv[++i];
- if ( cacheDir == NULL )
- throw "-cache_dir missing path argument";
- explicitCacheDir = true;
- }
- else if ( strcmp(arg, "-arch") == 0 ) {
- const char* arch = argv[++i];
- if ( strcmp(arch, "i386") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL));
- else if ( strcmp(arch, "x86_64") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL));
- else if ( strcmp(arch, "x86_64h") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H));
- else if ( strcmp(arch, "armv4t") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T));
- else if ( strcmp(arch, "armv5") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ));
- else if ( strcmp(arch, "armv6") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6));
- else if ( strcmp(arch, "armv7") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7));
- else if ( strcmp(arch, "armv7f") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7F));
- else if ( strcmp(arch, "armv7k") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K));
- else if ( strcmp(arch, "armv7s") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S));
- else if ( strcmp(arch, "arm64") == 0 )
- onlyArchs.insert(ArchPair(CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL));
- else
- throwf("unknown architecture %s", arch);
- }
- else if ( strcmp(arg, "-universal_boot") == 0 ) {
- onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL));
- onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL));
- relaunchForHaswell = true;
- }
- else {
- usage();
- throwf("unknown option: %s\n", arg);
- }
- }
- else {
- usage();
- throwf("unknown option: %s\n", arg);
- }
- }
-
- // strip tailing slashes on -root
- // make it a real path so as to not make all dylibs look like symlink aliases
- if ( rootPath[0] != '\0' ) {
- char realRootPath[MAXPATHLEN];
- if ( realpath(rootPath, realRootPath) == NULL )
- throwf("realpath() failed on %s\n", rootPath);
- rootPath = strdup(realRootPath);
- }
-
- // strip tailing slashes on -overlay
- for (std::vector<const char*>::iterator it=overlayPaths.begin(); it != overlayPaths.end(); ++it) {
- char realOverlayPath[MAXPATHLEN];
- if ( realpath(*it, realOverlayPath) == NULL )
- throwf("realpath() failed on %s\n", *it);
- *it = strdup(realOverlayPath);
- }
-
- // set default location to write cache dir
- if ( cacheDir == NULL )
- cacheDir = (iPhoneOS ? IPHONE_DYLD_SHARED_CACHE_DIR : MACOSX_DYLD_SHARED_CACHE_DIR);
-
- // if no restrictions specified, use architectures that work on this machine
- if ( onlyArchs.size() == 0 ) {
- if ( iPhoneOS ) {
- onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6));
- onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7));
- }
- else {
- int available;
- size_t len = sizeof(int);
- #if __i386__ || __x86_64__
- onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL));
- // check system is capable of running 64-bit programs
- if ( (sysctlbyname("hw.optional.x86_64", &available, &len, NULL, 0) == 0) && available ) {
- // check system is capable of running x86_64h code
- struct host_basic_info info;
- mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
- mach_port_t hostPort = mach_host_self();
- kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
- mach_port_deallocate(mach_task_self(), hostPort);
- if ( result != KERN_SUCCESS )
- throw "host_info() failed";
- if ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H )
- onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H));
- else
- onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_ALL));
- }
- #else
- #error unsupported architecture
- #endif
- }
- }
-
- if ( !verify && (geteuid() != 0) )
- throw "you must be root to run this tool";
-
- // build list of shared dylibs
- if ( dylibListFile != NULL )
- setSharedDylibs(rootPath, overlayPaths, dylibListFile, onlyArchs);
- else
- scanForSharedDylibs(rootPath, overlayPaths, "/var/db/dyld/shared_region_roots/", onlyArchs);
- bool didUpdate = updateSharedeCacheFile(rootPath, overlayPaths, cacheDir, explicitCacheDir, onlyArchs, force, alphaSort, optimize,
- false, verify, keepSignatures, dontMapLocalSymbols);
-
- if ( didUpdate && !iPhoneOS ) {
- void* handle = dlopen("/usr/lib/libspindump.dylib", RTLD_LAZY);
- if ( handle != NULL ) {
- typedef bool (*dscsym_proc_t)(const char *root);
- dscsym_proc_t proc = (dscsym_proc_t)dlsym(handle, "dscsym_save_nuggets_for_current_caches");
- const char* nuggetRootPath = "/";
- if ( !overlayPaths.empty() )
- nuggetRootPath = overlayPaths[0];
- else if ( rootPath[0] != '\0' )
- nuggetRootPath = rootPath;
- (*proc)(nuggetRootPath);
- }
- dlclose(handle);
- }
-
- if ( relaunchForHaswell ) {
- char cmd[2048];
- strlcpy(cmd, argv[0], 2048);
- strlcat(cmd, " -arch x86_64h", 2048);
- if ( force )
- strlcat(cmd, " -force", 2048);
- if ( verify )
- strlcat(cmd, " -verify", 2048);
- if ( alphaSort )
- strlcat(cmd, " -sort_by_name", 2048);
- if ( (rootPath != NULL) && (rootPath[0] != '\0') ) {
- strlcat(cmd, " -root ", 2048);
- strlcat(cmd, rootPath, 2048);
- }
- return system(cmd);
- }
-
- }
- catch (const char* msg) {
- fprintf(stderr, "update_dyld_shared_cache failed: %s\n", msg);
- return 1;
- }
-
- return 0;
-}
-
-
-
uint64_t ImageLoader::fgTotalBytesMapped = 0;
uint64_t ImageLoader::fgTotalBytesPreFetched = 0;
uint64_t ImageLoader::fgTotalLoadLibrariesTime;
+uint64_t ImageLoader::fgTotalObjCSetupTime = 0;
+uint64_t ImageLoader::fgTotalDebuggerPausedTime = 0;
+uint64_t ImageLoader::fgTotalRebindCacheTime = 0;
uint64_t ImageLoader::fgTotalRebaseTime;
uint64_t ImageLoader::fgTotalBindTime;
uint64_t ImageLoader::fgTotalWeakBindTime;
uint64_t ImageLoader::fgTotalDOF;
uint64_t ImageLoader::fgTotalInitTime;
uint16_t ImageLoader::fgLoadOrdinal = 0;
+uint32_t ImageLoader::fgSymbolTrieSearchs = 0;
std::vector<ImageLoader::InterposeTuple>ImageLoader::fgInterposingTuples;
uintptr_t ImageLoader::fgNextPIEDylibAddress = 0;
void ImageLoader::setMapped(const LinkContext& context)
{
fState = dyld_image_state_mapped;
- context.notifySingle(dyld_image_state_mapped, this); // note: can throw exception
+ context.notifySingle(dyld_image_state_mapped, this, NULL); // note: can throw exception
}
int ImageLoader::compare(const ImageLoader* right) const
return ( (this->fDevice == stat_buf.st_dev) && (this->fInode == stat_buf.st_ino) );
}
-const char* ImageLoader::getShortName() const
+const char* ImageLoader::shortName(const char* fullName)
{
// try to return leaf name
- if ( fPath != NULL ) {
- const char* s = strrchr(fPath, '/');
+ if ( fullName != NULL ) {
+ const char* s = strrchr(fullName, '/');
if ( s != NULL )
return &s[1];
}
- return fPath;
+ return fullName;
+}
+
+const char* ImageLoader::getShortName() const
+{
+ return shortName(fPath);
}
void ImageLoader::setLeaveMapped()
return true;
}
+bool ImageLoader::findExportedSymbolAddress(const LinkContext& context, const char* symbolName,
+ const ImageLoader* requestorImage, int requestorOrdinalOfDef,
+ bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const
+{
+ const Symbol* sym = this->findExportedSymbol(symbolName, true, foundIn);
+ if ( sym != NULL ) {
+ *address = (*foundIn)->getExportedSymbolAddress(sym, context, requestorImage, runResolver);
+ return true;
+ }
+ return false;
+}
+
// private method that handles circular dependencies by only search any image once
const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImagesExcept(const char* name,
const ImageLoader::Symbol* sym;
// search self
if ( notInImgageList(this, dsiStart, dsiCur) ) {
- sym = this->findExportedSymbol(name, false, foundIn);
+ sym = this->findExportedSymbol(name, false, this->getPath(), foundIn);
if ( sym != NULL )
return sym;
*dsiCur++ = this;
for(unsigned int i=0; i < libraryCount(); ++i) {
ImageLoader* dependentImage = libImage(i);
if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) {
- const ImageLoader::Symbol* sym = dependentImage->findExportedSymbol(name, false, foundIn);
+ sym = dependentImage->findExportedSymbol(name, false, libPath(i), foundIn);
if ( sym != NULL )
return sym;
}
ImageLoader* dependentImage = libImage(i);
if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) {
*dsiCur++ = dependentImage;
- const ImageLoader::Symbol* sym = dependentImage->findExportedSymbolInDependentImagesExcept(name, dsiStart, dsiCur, dsiEnd, foundIn);
+ sym = dependentImage->findExportedSymbolInDependentImagesExcept(name, dsiStart, dsiCur, dsiEnd, foundIn);
if ( sym != NULL )
return sym;
}
const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const
{
- unsigned int imageCount = context.imageCount();
+ unsigned int imageCount = context.imageCount()+2;
const ImageLoader* dontSearchImages[imageCount];
dontSearchImages[0] = this; // don't search this image
const ImageLoader** cur = &dontSearchImages[1];
const ImageLoader::Symbol* ImageLoader::findExportedSymbolInImageOrDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const
{
- unsigned int imageCount = context.imageCount();
+ unsigned int imageCount = context.imageCount()+2;
const ImageLoader* dontSearchImages[imageCount];
const ImageLoader** cur = &dontSearchImages[0];
return this->findExportedSymbolInDependentImagesExcept(name, &dontSearchImages[0], cur, &dontSearchImages[imageCount], foundIn);
}
-void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths)
+void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath)
{
- //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", this->getPath(), fDlopenReferenceCount, fNeverUnload);
+ //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", imagePath, fDlopenReferenceCount, fNeverUnload);
// clear error strings
- (*context.setErrorStrings)(dyld_error_kind_none, NULL, NULL, NULL);
+ (*context.setErrorStrings)(0, NULL, NULL, NULL);
uint64_t t0 = mach_absolute_time();
- this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths);
- context.notifyBatch(dyld_image_state_dependents_mapped);
-
+ this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath);
+ context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly);
+
// we only do the loading step for preflights
if ( preflightOnly )
return;
uint64_t t2 = mach_absolute_time();
this->recursiveRebase(context);
- context.notifyBatch(dyld_image_state_rebased);
+ context.notifyBatch(dyld_image_state_rebased, false);
uint64_t t3 = mach_absolute_time();
this->recursiveBind(context, forceLazysBound, neverUnload);
this->weakBind(context);
uint64_t t5 = mach_absolute_time();
- context.notifyBatch(dyld_image_state_bound);
+ context.notifyBatch(dyld_image_state_bound, false);
uint64_t t6 = mach_absolute_time();
std::vector<DOFInfo> dofs;
}
// clear error strings
- (*context.setErrorStrings)(dyld_error_kind_none, NULL, NULL, NULL);
+ (*context.setErrorStrings)(0, NULL, NULL, NULL);
fgTotalLoadLibrariesTime += t1 - t0;
fgTotalRebaseTime += t3 - t2;
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread,
InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
{
- uint32_t maxImageCount = context.imageCount();
+ uint32_t maxImageCount = context.imageCount()+2;
ImageLoader::UninitedUpwards upsBuffer[maxImageCount];
ImageLoader::UninitedUpwards& ups = upsBuffer[0];
ups.count = 0;
// Calling recursive init on all images in images list, building a new list of
// uninitialized upward dependencies.
for (uintptr_t i=0; i < images.count; ++i) {
- images.images[i]->recursiveInitialization(context, thisThread, timingInfo, ups);
+ images.images[i]->recursiveInitialization(context, thisThread, images.images[i]->getPath(), timingInfo, ups);
}
// If any upward dependencies remain, init them.
if ( ups.count > 0 )
up.count = 1;
up.images[0] = this;
processInitializers(context, thisThread, timingInfo, up);
- context.notifyBatch(dyld_image_state_initialized);
+ context.notifyBatch(dyld_image_state_initialized, false);
mach_port_deallocate(mach_task_self(), thisThread);
uint64_t t2 = mach_absolute_time();
fgTotalInitTime += (t2 - t1);
}
-void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths)
+void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath)
{
if ( fState < dyld_image_state_dependents_mapped ) {
// break cycles
for(unsigned int i=0; i < fLibraryCount; ++i){
ImageLoader* dependentLib;
bool depLibReExported = false;
- bool depLibReRequired = false;
+ bool depLibRequired = false;
bool depLibCheckSumsMatch = false;
DependentLibraryInfo& requiredLibInfo = libraryInfos[i];
#if DYLD_SHARED_CACHE_SUPPORT
}
#endif
try {
- dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths);
+ unsigned cacheIndex;
+ dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths, cacheIndex);
if ( dependentLib == this ) {
// found circular reference, perhaps DYLD_LIBARY_PATH is causing this rdar://problem/3684168
- dependentLib = context.loadLibrary(requiredLibInfo.name, false, NULL, NULL);
+ dependentLib = context.loadLibrary(requiredLibInfo.name, false, NULL, NULL, cacheIndex);
if ( dependentLib != this )
dyld::warn("DYLD_ setting caused circular dependency in %s\n", this->getPath());
}
else {
dependentLib->fIsReferencedDownward = true;
}
- LibraryInfo actualInfo = dependentLib->doGetLibraryInfo();
- depLibReRequired = requiredLibInfo.required;
+ LibraryInfo actualInfo = dependentLib->doGetLibraryInfo(requiredLibInfo.info);
+ depLibRequired = requiredLibInfo.required;
depLibCheckSumsMatch = ( actualInfo.checksum == requiredLibInfo.info.checksum );
depLibReExported = requiredLibInfo.reExported;
if ( ! depLibReExported ) {
dependentLib->getShortName(), actualInfo.minVersion >> 16, (actualInfo.minVersion >> 8) & 0xff, actualInfo.minVersion & 0xff);
}
// prebinding for this image disabled if any dependent library changed
- if ( !depLibCheckSumsMatch )
- canUsePrelinkingInfo = false;
+ //if ( !depLibCheckSumsMatch )
+ // canUsePrelinkingInfo = false;
// prebinding for this image disabled unless both this and dependent are in the shared cache
if ( !dependentLib->inSharedCache() || !this->inSharedCache() )
canUsePrelinkingInfo = false;
if ( requiredLibInfo.required ) {
fState = dyld_image_state_mapped;
// record values for possible use by CrashReporter or Finder
- if ( strstr(msg, "Incompatible") != NULL )
- (*context.setErrorStrings)(dyld_error_kind_dylib_version, this->getPath(), requiredLibInfo.name, NULL);
+ if ( strstr(msg, "Incompatible library version") != NULL )
+ (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_WRONG_VERSION, this->getPath(), requiredLibInfo.name, NULL);
else if ( strstr(msg, "architecture") != NULL )
- (*context.setErrorStrings)(dyld_error_kind_dylib_wrong_arch, this->getPath(), requiredLibInfo.name, NULL);
+ (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_WRONG_ARCH, this->getPath(), requiredLibInfo.name, NULL);
+ else if ( strstr(msg, "file system sandbox") != NULL )
+ (*context.setErrorStrings)(DYLD_EXIT_REASON_FILE_SYSTEM_SANDBOX, this->getPath(), requiredLibInfo.name, NULL);
+ else if ( strstr(msg, "code signature") != NULL )
+ (*context.setErrorStrings)(DYLD_EXIT_REASON_CODE_SIGNATURE, this->getPath(), requiredLibInfo.name, NULL);
+ else if ( strstr(msg, "malformed") != NULL )
+ (*context.setErrorStrings)(DYLD_EXIT_REASON_MALFORMED_MACHO, this->getPath(), requiredLibInfo.name, NULL);
else
- (*context.setErrorStrings)(dyld_error_kind_dylib_missing, this->getPath(), requiredLibInfo.name, NULL);
+ (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_MISSING, this->getPath(), requiredLibInfo.name, NULL);
const char* newMsg = dyld::mkstringf("Library not loaded: %s\n Referenced from: %s\n Reason: %s", requiredLibInfo.name, this->getRealPath(), msg);
free((void*)msg); // our free() will do nothing if msg is a string literal
throw newMsg;
for(unsigned int i=0; i < libraryCount(); ++i) {
ImageLoader* dependentImage = libImage(i);
if ( dependentImage != NULL ) {
- dependentImage->recursiveLoadLibraries(context, preflightOnly, thisRPaths);
+ dependentImage->recursiveLoadLibraries(context, preflightOnly, thisRPaths, libraryInfos[i].name);
}
}
doRebase(context);
// notify
- context.notifySingle(dyld_image_state_rebased, this);
+ context.notifySingle(dyld_image_state_rebased, this, NULL);
}
catch (const char* msg) {
// this image is not rebased
if ( neverUnload )
this->setNeverUnload();
- context.notifySingle(dyld_image_state_bound, this);
+ context.notifySingle(dyld_image_state_bound, this, NULL);
}
catch (const char* msg) {
// restore state
uint64_t t1 = mach_absolute_time();
// get set of ImageLoaders that participate in coalecsing
ImageLoader* imagesNeedingCoalescing[fgImagesRequiringCoalescing];
- int count = context.getCoalescedImages(imagesNeedingCoalescing);
+ unsigned imageIndexes[fgImagesRequiringCoalescing];
+ int count = context.getCoalescedImages(imagesNeedingCoalescing, imageIndexes);
// count how many have not already had weakbinding done
int countNotYetWeakBound = 0;
- int countOfImagesWithWeakDefinitions = 0;
int countOfImagesWithWeakDefinitionsNotInSharedCache = 0;
for(int i=0; i < count; ++i) {
- if ( ! imagesNeedingCoalescing[i]->fWeakSymbolsBound )
+ if ( ! imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) )
++countNotYetWeakBound;
- if ( imagesNeedingCoalescing[i]->hasCoalescedExports() ) {
- ++countOfImagesWithWeakDefinitions;
- if ( ! imagesNeedingCoalescing[i]->inSharedCache() )
- ++countOfImagesWithWeakDefinitionsNotInSharedCache;
- }
+ if ( ! imagesNeedingCoalescing[i]->inSharedCache() )
+ ++countOfImagesWithWeakDefinitionsNotInSharedCache;
}
// don't need to do any coalescing if only one image has overrides, or all have already been done
ImageLoader::CoalIterator iterators[count];
ImageLoader::CoalIterator* sortedIts[count];
for(int i=0; i < count; ++i) {
- imagesNeedingCoalescing[i]->initializeCoalIterator(iterators[i], i);
+ imagesNeedingCoalescing[i]->initializeCoalIterator(iterators[i], i, imageIndexes[i]);
sortedIts[i] = &iterators[i];
if ( context.verboseWeakBind )
- dyld::log("dyld: weak bind load order %d/%d for %s\n", i, count, imagesNeedingCoalescing[i]->getPath());
+ dyld::log("dyld: weak bind load order %d/%d for %s\n", i, count, imagesNeedingCoalescing[i]->getIndexedPath(imageIndexes[i]));
}
// walk all symbols keeping iterators in sync by
// pick first symbol in load order (and non-weak overrides weak)
uintptr_t targetAddr = 0;
ImageLoader* targetImage = NULL;
+ unsigned targetImageIndex = 0;
for(int i=0; i < count; ++i) {
if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) {
if ( context.verboseWeakBind )
- dyld::log("dyld: weak bind, found %s weak=%d in %s \n", nameToCoalesce, iterators[i].weakSymbol, iterators[i].image->getPath());
+ dyld::log("dyld: weak bind, found %s weak=%d in %s \n", nameToCoalesce, iterators[i].weakSymbol, iterators[i].image->getIndexedPath((unsigned)iterators[i].imageIndex));
if ( iterators[i].weakSymbol ) {
if ( targetAddr == 0 ) {
targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context);
- if ( targetAddr != 0 )
+ if ( targetAddr != 0 ) {
targetImage = iterators[i].image;
+ targetImageIndex = (unsigned)iterators[i].imageIndex;
+ }
}
}
else {
targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context);
if ( targetAddr != 0 ) {
targetImage = iterators[i].image;
+ targetImageIndex = (unsigned)iterators[i].imageIndex;
// strong implementation found, stop searching
break;
}
}
// tell each to bind to this symbol (unless already bound)
if ( targetAddr != 0 ) {
- if ( context.verboseWeakBind )
- dyld::log("dyld: weak binding all uses of %s to copy from %s\n", nameToCoalesce, targetImage->getShortName());
+ if ( context.verboseWeakBind ) {
+ dyld::log("dyld: weak binding all uses of %s to copy from %s\n",
+ nameToCoalesce, targetImage->getIndexedShortName(targetImageIndex));
+ }
for(int i=0; i < count; ++i) {
if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) {
- if ( context.verboseWeakBind )
- dyld::log("dyld: weak bind, setting all uses of %s in %s to 0x%lX from %s\n", nameToCoalesce, iterators[i].image->getShortName(), targetAddr, targetImage->getShortName());
- if ( ! iterators[i].image->fWeakSymbolsBound )
- iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, context);
+ if ( context.verboseWeakBind ) {
+ dyld::log("dyld: weak bind, setting all uses of %s in %s to 0x%lX from %s\n",
+ nameToCoalesce, iterators[i].image->getIndexedShortName((unsigned)iterators[i].imageIndex),
+ targetAddr, targetImage->getIndexedShortName(targetImageIndex));
+ }
+ if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) )
+ iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, targetImageIndex, context);
iterators[i].symbolMatches = false;
}
}
// mark all as having all weak symbols bound
for(int i=0; i < count; ++i) {
- imagesNeedingCoalescing[i]->fWeakSymbolsBound = true;
+ imagesNeedingCoalescing[i]->setWeakSymbolsBound(imageIndexes[i]);
}
}
uint64_t t2 = mach_absolute_time();
fInitializerRecursiveLock = NULL;
}
+void ImageLoader::InitializerTimingList::addTime(const char* name, uint64_t time)
+{
+ for (int i=0; i < count; ++i) {
+ if ( strcmp(images[i].shortName, name) == 0 ) {
+ images[i].initTime += time;
+ return;
+ }
+ }
+ images[count].initTime = time;
+ images[count].shortName = name;
+ ++count;
+}
-void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread,
+void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
recursive_lock lock_info(this_thread);
uninitUps.count++;
}
else if ( dependentImage->fDepth >= fDepth ) {
- dependentImage->recursiveInitialization(context, this_thread, timingInfo, uninitUps);
+ dependentImage->recursiveInitialization(context, this_thread, libPath(i), timingInfo, uninitUps);
}
}
}
uint64_t t1 = mach_absolute_time();
fState = dyld_image_state_dependents_initialized;
oldState = fState;
- context.notifySingle(dyld_image_state_dependents_initialized, this);
+ context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
// initialize this image
bool hasInitializers = this->doInitialization(context);
// let anyone know we finished initializing this image
fState = dyld_image_state_initialized;
oldState = fState;
- context.notifySingle(dyld_image_state_initialized, this);
+ context.notifySingle(dyld_image_state_initialized, this, NULL);
if ( hasInitializers ) {
uint64_t t2 = mach_absolute_time();
- timingInfo.images[timingInfo.count].image = this;
- timingInfo.images[timingInfo.count].initTime = (t2-t1);
- timingInfo.count++;
+ timingInfo.addTime(this->getShortName(), t2-t1);
}
}
catch (const char* msg) {
uint32_t milliSeconds = (uint32_t)(milliSecondsTimesHundred/100);
uint32_t percentTimesTen = (uint32_t)((partTime*1000)/totalTime);
uint32_t percent = percentTimesTen/10;
- dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10);
+ if ( milliSeconds >= 100 )
+ dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10);
+ else if ( milliSeconds >= 10 )
+ dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10);
+ else
+ dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10);
}
else {
uint32_t secondsTimeTen = (uint32_t)((partTime*10)/sUnitsPerSecond);
void ImageLoader::printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo)
{
- uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime;
+ uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime;
+
+ uint64_t totalDyldTime = totalTime - fgTotalDebuggerPausedTime - fgTotalRebindCacheTime;
+ printTime("Total pre-main time", totalDyldTime, totalDyldTime);
+ printTime(" dylib loading time", fgTotalLoadLibrariesTime-fgTotalDebuggerPausedTime, totalDyldTime);
+ printTime(" rebase/binding time", fgTotalRebaseTime+fgTotalBindTime+fgTotalWeakBindTime-fgTotalRebindCacheTime, totalDyldTime);
+ printTime(" ObjC setup time", fgTotalObjCSetupTime, totalDyldTime);
+ printTime(" initializer time", fgTotalInitTime-fgTotalObjCSetupTime, totalDyldTime);
+ dyld::log(" slowest intializers :\n");
+ for (uintptr_t i=0; i < timingInfo.count; ++i) {
+ uint64_t t = timingInfo.images[i].initTime;
+ if ( t*50 < totalDyldTime )
+ continue;
+ dyld::log("%30s ", timingInfo.images[i].shortName);
+ if ( strncmp(timingInfo.images[i].shortName, "libSystem.", 10) == 0 )
+ t -= fgTotalObjCSetupTime;
+ printTime("", t, totalDyldTime);
+ }
+ dyld::log("\n");
+}
+
+void ImageLoader::printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo)
+{
+ uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime;
char commaNum1[40];
char commaNum2[40];
- printTime("total time", totalTime, totalTime);
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
- if ( fgImagesUsedFromSharedCache != 0 )
- dyld::log("total images loaded: %d (%u from dyld shared cache)\n", imageCount, fgImagesUsedFromSharedCache);
- else
- dyld::log("total images loaded: %d\n", imageCount);
-#else
- dyld::log("total images loaded: %d (%u from dyld shared cache)\n", imageCount, fgImagesUsedFromSharedCache);
-#endif
- dyld::log("total segments mapped: %u, into %llu pages with %llu pages pre-fetched\n", fgTotalSegmentsMapped, fgTotalBytesMapped/4096, fgTotalBytesPreFetched/4096);
- printTime("total images loading time", fgTotalLoadLibrariesTime, totalTime);
- printTime("total dtrace DOF registration time", fgTotalDOF, totalTime);
- dyld::log("total rebase fixups: %s\n", commatize(fgTotalRebaseFixups, commaNum1));
- printTime("total rebase fixups time", fgTotalRebaseTime, totalTime);
- dyld::log("total binding fixups: %s\n", commatize(fgTotalBindFixups, commaNum1));
+ printTime(" total time", totalTime, totalTime);
+ dyld::log(" total images loaded: %d (%u from dyld shared cache)\n", imageCount, fgImagesUsedFromSharedCache);
+ dyld::log(" total segments mapped: %u, into %llu pages with %llu pages pre-fetched\n", fgTotalSegmentsMapped, fgTotalBytesMapped/4096, fgTotalBytesPreFetched/4096);
+ printTime(" total images loading time", fgTotalLoadLibrariesTime, totalTime);
+ printTime(" total load time in ObjC", fgTotalObjCSetupTime, totalTime);
+ printTime(" total debugger pause time", fgTotalDebuggerPausedTime, totalTime);
+ printTime(" total dtrace DOF registration time", fgTotalDOF, totalTime);
+ dyld::log(" total rebase fixups: %s\n", commatize(fgTotalRebaseFixups, commaNum1));
+ printTime(" total rebase fixups time", fgTotalRebaseTime, totalTime);
+ dyld::log(" total binding fixups: %s\n", commatize(fgTotalBindFixups, commaNum1));
if ( fgTotalBindSymbolsResolved != 0 ) {
uint32_t avgTimesTen = (fgTotalBindImageSearches * 10) / fgTotalBindSymbolsResolved;
uint32_t avgInt = fgTotalBindImageSearches / fgTotalBindSymbolsResolved;
dyld::log("total binding symbol lookups: %s, average images searched per symbol: %u.%u\n",
commatize(fgTotalBindSymbolsResolved, commaNum1), avgInt, avgTenths);
}
- printTime("total binding fixups time", fgTotalBindTime, totalTime);
- printTime("total weak binding fixups time", fgTotalWeakBindTime, totalTime);
- dyld::log("total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups, commaNum1), commatize(fgTotalPossibleLazyBindFixups, commaNum2));
- printTime("total initializer time", fgTotalInitTime, totalTime);
+ printTime(" total binding fixups time", fgTotalBindTime, totalTime);
+ printTime(" total weak binding fixups time", fgTotalWeakBindTime, totalTime);
+ printTime(" total redo shared cached bindings time", fgTotalRebindCacheTime, totalTime);
+ dyld::log(" total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups, commaNum1), commatize(fgTotalPossibleLazyBindFixups, commaNum2));
+ printTime(" total time in initializers and ObjC +load", fgTotalInitTime-fgTotalObjCSetupTime, totalTime);
for (uintptr_t i=0; i < timingInfo.count; ++i) {
- dyld::log("%21s ", timingInfo.images[i].image->getShortName());
- printTime("", timingInfo.images[i].initTime, totalTime);
+ uint64_t t = timingInfo.images[i].initTime;
+ if ( t*1000 < totalTime )
+ continue;
+ dyld::log("%42s ", timingInfo.images[i].shortName);
+ if ( strncmp(timingInfo.images[i].shortName, "libSystem.", 10) == 0 )
+ t -= fgTotalObjCSetupTime;
+ printTime("", t, totalTime);
}
}
}
+//
+// This function is the hotspot of symbol lookup. It was pulled out of findExportedSymbol()
+// to enable it to be re-written in assembler if needed.
+//
+const uint8_t* ImageLoader::trieWalk(const uint8_t* start, const uint8_t* end, const char* s)
+{
+ //dyld::log("trieWalk(%p, %p, %s)\n", start, end, s);
+ ++fgSymbolTrieSearchs;
+ const uint8_t* p = start;
+ while ( p != NULL ) {
+ uintptr_t terminalSize = *p++;
+ if ( terminalSize > 127 ) {
+ // except for re-export-with-rename, all terminal sizes fit in one byte
+ --p;
+ terminalSize = read_uleb128(p, end);
+ }
+ if ( (*s == '\0') && (terminalSize != 0) ) {
+ //dyld::log("trieWalk(%p) returning %p\n", start, p);
+ return p;
+ }
+ const uint8_t* children = p + terminalSize;
+ if ( children > end ) {
+ dyld::log("trieWalk() malformed trie node, terminalSize=0x%lx extends past end of trie\n", terminalSize);
+ return NULL;
+ }
+ //dyld::log("trieWalk(%p) sym=%s, terminalSize=%lu, children=%p\n", start, s, terminalSize, children);
+ uint8_t childrenRemaining = *children++;
+ p = children;
+ uintptr_t nodeOffset = 0;
+ for (; childrenRemaining > 0; --childrenRemaining) {
+ const char* ss = s;
+ //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p);
+ bool wrongEdge = false;
+ // scan whole edge to get to next edge
+ // if edge is longer than target symbol name, don't read past end of symbol name
+ char c = *p;
+ while ( c != '\0' ) {
+ if ( !wrongEdge ) {
+ if ( c != *ss )
+ wrongEdge = true;
+ ++ss;
+ }
+ ++p;
+ c = *p;
+ }
+ if ( wrongEdge ) {
+ // advance to next child
+ ++p; // skip over zero terminator
+ // skip over uleb128 until last byte is found
+ while ( (*p & 0x80) != 0 )
+ ++p;
+ ++p; // skip over last byte of uleb128
+ if ( p > end ) {
+ dyld::log("trieWalk() malformed trie node, child node extends past end of trie\n");
+ return NULL;
+ }
+ }
+ else {
+ // the symbol so far matches this edge (child)
+ // so advance to the child's node
+ ++p;
+ nodeOffset = read_uleb128(p, end);
+ if ( (nodeOffset == 0) || ( &start[nodeOffset] > end) ) {
+ dyld::log("trieWalk() malformed trie child, nodeOffset=0x%lx out of range\n", nodeOffset);
+ return NULL;
+ }
+ s = ss;
+ //dyld::log("trieWalk() found matching edge advancing to node 0x%lx\n", nodeOffset);
+ break;
+ }
+ }
+ if ( nodeOffset != 0 )
+ p = &start[nodeOffset];
+ else
+ p = NULL;
+ }
+ //dyld::log("trieWalk(%p) return NULL\n", start);
+ return NULL;
+}
+
+
+
+uintptr_t ImageLoader::read_uleb128(const uint8_t*& p, const uint8_t* end)
+{
+ uint64_t result = 0;
+ int bit = 0;
+ do {
+ if (p == end)
+ dyld::throwf("malformed uleb128");
+
+ uint64_t slice = *p & 0x7f;
+
+ if (bit > 63)
+ dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result);
+ else {
+ result |= (slice << bit);
+ bit += 7;
+ }
+ } while (*p++ & 0x80);
+ return result;
+}
+
+
+intptr_t ImageLoader::read_sleb128(const uint8_t*& p, const uint8_t* end)
+{
+ int64_t result = 0;
+ int bit = 0;
+ uint8_t byte;
+ do {
+ if (p == end)
+ throw "malformed sleb128";
+ byte = *p++;
+ result |= (((int64_t)(byte & 0x7f)) << bit);
+ bit += 7;
+ } while (byte & 0x80);
+ // sign extend negative numbers
+ if ( (byte & 0x40) != 0 )
+ result |= (-1LL) << bit;
+ return result;
+}
+
+
VECTOR_NEVER_DESTRUCTED_IMPL(ImageLoader::InterposeTuple);
VECTOR_NEVER_DESTRUCTED_IMPL(ImagePair);
#define SUPPORT_VERSIONED_PATHS 1
#define SUPPORT_CLASSIC_MACHO __arm__
#define SUPPORT_ZERO_COST_EXCEPTIONS (!__USING_SJLJ_EXCEPTIONS__)
- #define INITIAL_IMAGE_COUNT 256
+ #define INITIAL_IMAGE_COUNT 150
+ #define SUPPORT_ACCELERATE_TABLES (__arm__ || __arm64__)
+ #define SUPPORT_ROOT_PATH TARGET_IPHONE_SIMULATOR
#else
#define SPLIT_SEG_SHARED_REGION_SUPPORT 0
#define SPLIT_SEG_DYLIB_SUPPORT __i386__
#define SUPPORT_CLASSIC_MACHO 1
#define SUPPORT_ZERO_COST_EXCEPTIONS 1
#define INITIAL_IMAGE_COUNT 200
+ #define SUPPORT_ACCELERATE_TABLES 0
+ #define SUPPORT_ROOT_PATH 1
#endif
+#define MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE (32*1024)
+#define MH_HAS_OBJC 0x40000000
// <rdar://problem/13590567> optimize away dyld's initializers
#define VECTOR_NEVER_DESTRUCTED(type) \
ImageLoader* to;
};
+ struct InitializerTimingList
+ {
+ uintptr_t count;
+ struct {
+ const char* shortName;
+ uint64_t initTime;
+ } images[1];
+
+ void addTime(const char* name, uint64_t time);
+ };
+
struct LinkContext {
- ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths);
+ ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths, unsigned& cacheIndex);
void (*terminationRecorder)(ImageLoader* image);
bool (*flatExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image);
bool (*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image);
- unsigned int (*getCoalescedImages)(ImageLoader* images[]);
+ unsigned int (*getCoalescedImages)(ImageLoader* images[], unsigned imageIndex[]);
void (*undefinedHandler)(const char* name);
MappedRegion* (*getAllMappedRegions)(MappedRegion*);
void * (*bindingHandler)(const char *, const char *, void *);
- void (*notifySingle)(dyld_image_states, const ImageLoader* image);
- void (*notifyBatch)(dyld_image_states state);
+ void (*notifySingle)(dyld_image_states, const ImageLoader* image, InitializerTimingList*);
+ void (*notifyBatch)(dyld_image_states state, bool preflightOnly);
void (*removeImage)(ImageLoader* image);
void (*registerDOFs)(const std::vector<DOFInfo>& dofs);
void (*clearAllDepths)();
const char* errorTargetDylibPath, const char* errorSymbol);
ImageLoader* (*findImageContainingAddress)(const void* addr);
void (*addDynamicReference)(ImageLoader* from, ImageLoader* to);
+#if SUPPORT_ACCELERATE_TABLES
+ void (*notifySingleFromCache)(dyld_image_states, const mach_header* mh, const char* path);
+ dyld_image_state_change_handler (*getPreInitNotifyHandler)(unsigned index);
+ dyld_image_state_change_handler (*getBoundBatchHandler)(unsigned index);
+#endif
#if SUPPORT_OLD_CRT_INITIALIZATION
void (*setRunInitialzersOldWay)();
ProgramVars programVars;
ImageLoader* mainExecutable;
const char* imageSuffix;
+#if SUPPORT_ROOT_PATH
const char** rootPaths;
+#endif
const dyld_interpose_tuple* dynamicInterposeArray;
size_t dynamicInterposeCount;
PrebindMode prebindUsage;
SharedRegionMode sharedRegionMode;
bool dyldLoadedAtSameAddressNeededBySharedCache;
- bool codeSigningEnforced;
+ bool strictMachORequired;
+ bool requireCodeSignature;
bool mainExecutableCodeSigned;
bool preFetchDisabled;
bool prebinding;
bool bindFlat;
bool linkingMainExecutable;
bool startedInitializingMainExecutable;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
bool processIsRestricted;
- bool processRequiresLibraryValidation;
+ bool processUsingLibraryValidation;
+#endif
bool verboseOpts;
bool verboseEnv;
+ bool verboseLoading;
bool verboseMapping;
bool verboseRebase;
bool verboseBind;
uintptr_t address;
uintptr_t type;
uintptr_t addend;
+ uintptr_t imageIndex;
};
- virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder) = 0;
+ virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned imageIndex) = 0;
virtual bool incrementCoalIterator(CoalIterator&) = 0;
virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& context) = 0;
- virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context) = 0;
-
- struct InitializerTimingList
- {
- uintptr_t count;
- struct {
- ImageLoader* image;
- uint64_t initTime;
- } images[1];
- };
+ virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context) = 0;
struct UninitedUpwards
{
// link() takes a newly instantiated ImageLoader and does all
// fixups needed to make it usable by the process
- void link(const LinkContext& context, bool forceLazysBound, bool preflight, bool neverUnload, const RPathChain& loaderRPaths);
+ void link(const LinkContext& context, bool forceLazysBound, bool preflight, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath);
// runInitializers() is normally called in link() but the main executable must
// run crt code before initializers
// get short name of this image
const char* getShortName() const;
+ // returns leaf name
+ static const char* shortName(const char* fullName);
+
// get path used to load this image, not necessarily the "real" path
const char* getPath() const { return fPath; }
// get path this image is intended to be placed on disk or NULL if no preferred install location
virtual const char* getInstallPath() const = 0;
- // image was loaded with NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME and all clients are looking for install path
+ // image was loaded with NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME and all clients are looking for install path
bool matchInstallPath() const;
void setMatchInstallPath(bool);
// image has exports that participate in runtime coalescing
virtual bool hasCoalescedExports() const = 0;
+
+ // search symbol table of definitions in this image for requested name
+ virtual bool findExportedSymbolAddress(const LinkContext& context, const char* symbolName,
+ const ImageLoader* requestorImage, int requestorOrdinalOfDef,
+ bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const;
+
+ // search symbol table of definitions in this image for requested name
+ virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const = 0;
// search symbol table of definitions in this image for requested name
- virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const = 0;
+ virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const {
+ return findExportedSymbol(name, searchReExports, this->getPath(), foundIn);
+ }
// gets address of implementation (code) of the specified exported symbol
virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context,
- const ImageLoader* requestor=NULL, bool runResolver=false) const = 0;
+ const ImageLoader* requestor=NULL, bool runResolver=false, const char* symbolName=NULL) const = 0;
// gets attributes of the specified exported symbol
virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const = 0;
// find the closest symbol before addr
virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const = 0;
+ // for use with accelerator tables
+ virtual const char* getIndexedPath(unsigned) const { return getPath(); }
+ virtual const char* getIndexedShortName(unsigned) const { return getShortName(); }
+
// checks if this image is a bundle and can be loaded but not linked
virtual bool isBundle() const = 0;
// record interposing for any late binding
void addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count);
+ virtual const char* libPath(unsigned int) const = 0;
+
+ // Image has objc sections, so information objc about when it comes and goes
+ virtual bool notifyObjC() const { return false; }
+
//
// A segment is a chunk of an executable file that is mapped into memory.
//
void applyInterposing(const LinkContext& context);
dyld_image_states getState() { return (dyld_image_states)fState; }
-
+
+ ino_t getInode() const { return fInode; }
+
// used to sort images bottom-up
int compare(const ImageLoader* right) const;
// triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast
static void printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo);
-
+ static void printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo);
+
// used with DYLD_IMAGE_SUFFIX
static void addSuffix(const char* path, const char* suffix, char* result);
static uint32_t hash(const char*);
+ static const uint8_t* trieWalk(const uint8_t* start, const uint8_t* end, const char* stringToFind);
+
// used instead of directly deleting image
static void deleteImage(ImageLoader*);
-
+
+ static bool haveInterposingTuples() { return !fgInterposingTuples.empty(); }
+ static void clearInterposingTuples() { fgInterposingTuples.clear(); }
+
bool dependsOn(ImageLoader* image);
void setPath(const char* path);
uintptr_t replacee;
};
+ static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end);
+ static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end);
+
protected:
// abstract base class so all constructors protected
ImageLoader(const char* path, unsigned int libCount);
// To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized.
// These methods do the above, exactly once, and it the right order
- void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths);
- void recursiveUnLoadMappedLibraries(const LinkContext& context);
- unsigned int recursiveUpdateDepth(unsigned int maxDepth);
- void recursiveValidate(const LinkContext& context);
- void recursiveRebase(const LinkContext& context);
- void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload);
- void recursiveApplyInterposing(const LinkContext& context);
- void recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs);
- void recursiveInitialization(const LinkContext& context, mach_port_t this_thread,
+ virtual void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath);
+ virtual unsigned recursiveUpdateDepth(unsigned int maxDepth);
+ virtual void recursiveRebase(const LinkContext& context);
+ virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload);
+ virtual void recursiveApplyInterposing(const LinkContext& context);
+ virtual void recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs);
+ virtual void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&);
// fill in information about dependent libraries (array length is fLibraryCount)
virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) = 0;
// called on images that are libraries, returns info about itself
- virtual LibraryInfo doGetLibraryInfo() = 0;
+ virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo) = 0;
// do any fix ups in this image that depend only on the load address of the image
virtual void doRebase(const LinkContext& context) = 0;
// built with PIC code and can load at any address
virtual bool segmentsCanSlide() const = 0;
-
+
// set how much all segments slide
virtual void setSlide(intptr_t slide) = 0;
-
+
// returns if all dependent libraries checksum's were as expected and none slide
bool allDependentLibrariesAsWhenPreBound() const;
// in mach-o a parent library knows name of sub libraries it re-exports..
virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0;
-
+
+ virtual bool weakSymbolsBound(unsigned index) { return fWeakSymbolsBound; }
+ virtual void setWeakSymbolsBound(unsigned index) { fWeakSymbolsBound = true; }
+
// set fState to dyld_image_state_memory_mapped
void setMapped(const LinkContext& context);
void setFileInfo(dev_t device, ino_t inode, time_t modDate);
-
+
+ void setDepth(uint16_t depth) { fDepth = depth; }
+
static uintptr_t interposedAddress(const LinkContext& context, uintptr_t address, const ImageLoader* notInImage, const ImageLoader* onlyInImage=NULL);
static uintptr_t fgNextPIEDylibAddress;
static uint32_t fgTotalLazyBindFixups;
static uint32_t fgTotalPossibleLazyBindFixups;
static uint32_t fgTotalSegmentsMapped;
+ static uint32_t fgSymbolTrieSearchs;
static uint64_t fgTotalBytesMapped;
static uint64_t fgTotalBytesPreFetched;
static uint64_t fgTotalLoadLibrariesTime;
+public:
+ static uint64_t fgTotalObjCSetupTime;
+ static uint64_t fgTotalDebuggerPausedTime;
+ static uint64_t fgTotalRebindCacheTime;
+protected:
static uint64_t fgTotalRebaseTime;
static uint64_t fgTotalBindTime;
static uint64_t fgTotalWeakBindTime;
uint32_t fPathHash;
uint32_t fDlopenReferenceCount; // count of how many dlopens have been done on this image
-private:
struct recursive_lock {
recursive_lock(mach_port_t t) : thread(t), count(0) {}
mach_port_t thread;
void recursiveSpinLock(recursive_lock&);
void recursiveSpinUnLock();
+private:
const ImageLoader::Symbol* findExportedSymbolInDependentImagesExcept(const char* name, const ImageLoader** dsiStart,
const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const;
#include "ImageLoaderMachOClassic.h"
#endif
#include "mach-o/dyld_images.h"
+#include "dyld.h"
// <rdar://problem/8718137> use stack guard random value to add padding between dylibs
extern "C" long __stack_chk_guard;
#endif
+#if TARGET_IPHONE_SIMULATOR
+ #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.dylib"
+#else
+ #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.B.dylib"
+#endif
+
// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
#if __LP64__
#define LC_SEGMENT_COMMAND LC_SEGMENT_64
#endif
uint32_t ImageLoaderMachO::fgSymbolTableBinarySearchs = 0;
-uint32_t ImageLoaderMachO::fgSymbolTrieSearchs = 0;
ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount,
fReadOnlyImportSegment(false),
#endif
fHasSubLibraries(false), fHasSubUmbrella(false), fInUmbrella(false), fHasDOFSections(false), fHasDashInit(false),
- fHasInitializers(false), fHasTerminators(false), fRegisteredAsRequiresCoalescing(false)
+ fHasInitializers(false), fHasTerminators(false), fNotifyObjC(false), fRetainForObjC(false), fRegisteredAsRequiresCoalescing(false)
{
fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0);
*encryptCmd = NULL;
const uint32_t cmd_count = mh->ncmds;
- const struct load_command* const startCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header));
- const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + mh->sizeofcmds);
+ const uint32_t sizeofcmds = mh->sizeofcmds;
+ if ( sizeofcmds > (MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE-sizeof(macho_header)) )
+ dyld::throwf("malformed mach-o: load commands size (%u) > %u", sizeofcmds, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE);
+ if ( cmd_count > (sizeofcmds/sizeof(load_command)) )
+ dyld::throwf("malformed mach-o: ncmds (%u) too large to fit in sizeofcmds (%u)", cmd_count, sizeofcmds);
+ const struct load_command* const startCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header));
+ const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + sizeofcmds);
const struct load_command* cmd = startCmds;
bool foundLoadCommandSegment = false;
+ const macho_segment_command* linkeditSegCmd = NULL;
+ const macho_segment_command* startOfFileSegCmd = NULL;
+ const dyld_info_command* dyldInfoCmd = NULL;
+ const symtab_command* symTabCmd = NULL;
+ const dysymtab_command* dynSymbTabCmd = NULL;
for (uint32_t i = 0; i < cmd_count; ++i) {
uint32_t cmdLength = cmd->cmdsize;
- struct macho_segment_command* segCmd;
+ const macho_segment_command* segCmd;
+ const dylib_command* dylibCmd;
if ( cmdLength < 8 ) {
dyld::throwf("malformed mach-o image: load command #%d length (%u) too small in %s",
i, cmdLength, path);
switch (cmd->cmd) {
case LC_DYLD_INFO:
case LC_DYLD_INFO_ONLY:
+ if ( cmd->cmdsize != sizeof(dyld_info_command) )
+ throw "malformed mach-o image: LC_DYLD_INFO size wrong";
+ dyldInfoCmd = (struct dyld_info_command*)cmd;
*compressed = true;
break;
case LC_SEGMENT_COMMAND:
// rdar://problem/19617624 allow unmapped segments on OSX (but not iOS)
if ( (segCmd->filesize > segCmd->vmsize) && (segCmd->vmsize != 0) )
#else
- if ( segCmd->filesize > segCmd->vmsize )
+ // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
+ if ( (segCmd->filesize > segCmd->vmsize) && ((segCmd->vmsize != 0) || ((segCmd->flags & SG_NORELOC) == 0)) )
#endif
dyld::throwf("malformed mach-o image: segment load command %s filesize is larger than vmsize", segCmd->segname);
+ if ( cmd->cmdsize < sizeof(macho_segment_command) )
+ throw "malformed mach-o image: LC_SEGMENT size too small";
+ if ( cmd->cmdsize != (sizeof(macho_segment_command) + segCmd->nsects * sizeof(macho_section)) )
+ throw "malformed mach-o image: LC_SEGMENT size wrong for number of sections";
// ignore zero-sized segments
if ( segCmd->vmsize != 0 )
*segCount += 1;
- if ( context.codeSigningEnforced ) {
+ if ( strcmp(segCmd->segname, "__LINKEDIT") == 0 ) {
+ #if TARGET_IPHONE_SIMULATOR
+ // Note: should check on all platforms that __LINKEDIT is read-only, but <rdar://problem/22637626&22525618>
+ if ( segCmd->initprot != VM_PROT_READ )
+ throw "malformed mach-o image: __LINKEDIT segment does not have read-only permissions";
+ #endif
+ if ( segCmd->fileoff == 0 )
+ throw "malformed mach-o image: __LINKEDIT has fileoff==0 which overlaps mach_header";
+ if ( linkeditSegCmd != NULL )
+ throw "malformed mach-o image: multiple __LINKEDIT segments";
+ linkeditSegCmd = segCmd;
+ }
+ else {
+ if ( segCmd->initprot & 0xFFFFFFF8 )
+ dyld::throwf("malformed mach-o image: %s segment has invalid permission bits (0x%X) in initprot", segCmd->segname, segCmd->initprot);
+ if ( segCmd->maxprot & 0xFFFFFFF8 )
+ dyld::throwf("malformed mach-o image: %s segment has invalid permission bits (0x%X) in maxprot", segCmd->segname, segCmd->maxprot);
+ if ( (segCmd->initprot != 0) && ((segCmd->initprot & VM_PROT_READ) == 0) )
+ dyld::throwf("malformed mach-o image: %s segment is not mapped readable", segCmd->segname);
+ }
+ if ( (segCmd->fileoff == 0) && (segCmd->filesize != 0) ) {
+ if ( (segCmd->initprot & VM_PROT_READ) == 0 )
+ dyld::throwf("malformed mach-o image: %s segment maps start of file but is not readable", segCmd->segname);
+ if ( (segCmd->initprot & VM_PROT_WRITE) == VM_PROT_WRITE ) {
+ if ( context.strictMachORequired )
+ dyld::throwf("malformed mach-o image: %s segment maps start of file but is writable", segCmd->segname);
+ }
+ if ( segCmd->filesize < (sizeof(macho_header) + mh->sizeofcmds) )
+ dyld::throwf("malformed mach-o image: %s segment does not map all of load commands", segCmd->segname);
+ if ( startOfFileSegCmd != NULL )
+ dyld::throwf("malformed mach-o image: multiple segments map start of file: %s %s", startOfFileSegCmd->segname, segCmd->segname);
+ startOfFileSegCmd = segCmd;
+ }
+ if ( context.strictMachORequired ) {
uintptr_t vmStart = segCmd->vmaddr;
uintptr_t vmSize = segCmd->vmsize;
uintptr_t vmEnd = vmStart + vmSize;
uintptr_t fileStart = segCmd->fileoff;
uintptr_t fileSize = segCmd->filesize;
- if ( (intptr_t)(vmEnd) < 0)
- dyld::throwf("malformed mach-o image: segment load command %s vmsize too large", segCmd->segname);
+ if ( (intptr_t)(vmSize) < 0 )
+ dyld::throwf("malformed mach-o image: segment load command %s vmsize too large in %s", segCmd->segname, path);
if ( vmStart > vmEnd )
dyld::throwf("malformed mach-o image: segment load command %s wraps around address space", segCmd->segname);
if ( vmSize != fileSize ) {
- if ( (segCmd->initprot == 0) && (fileSize != 0) )
- dyld::throwf("malformed mach-o image: unaccessable segment %s has filesize != 0", segCmd->segname);
- else if ( vmSize < fileSize )
- dyld::throwf("malformed mach-o image: segment %s has vmsize < filesize", segCmd->segname);
+ if ( segCmd->initprot == 0 ) {
+ // allow: fileSize == 0 && initprot == 0 e.g. __PAGEZERO
+ // allow: vmSize == 0 && initprot == 0 e.g. __LLVM
+ if ( (fileSize != 0) && (vmSize != 0) )
+ dyld::throwf("malformed mach-o image: unaccessable segment %s has non-zero filesize and vmsize", segCmd->segname);
+ }
+ else {
+ // allow: vmSize > fileSize && initprot != X e.g. __DATA
+ if ( vmSize < fileSize )
+ dyld::throwf("malformed mach-o image: segment %s has vmsize < filesize", segCmd->segname);
+ if ( segCmd->initprot & VM_PROT_EXECUTE )
+ dyld::throwf("malformed mach-o image: segment %s has vmsize != filesize and is executable", segCmd->segname);
+ }
}
if ( inCache ) {
if ( (fileSize != 0) && (segCmd->initprot == (VM_PROT_READ | VM_PROT_EXECUTE)) ) {
case LC_REEXPORT_DYLIB:
case LC_LOAD_UPWARD_DYLIB:
*libCount += 1;
+ // fall thru
+ case LC_ID_DYLIB:
+ dylibCmd = (dylib_command*)cmd;
+ if ( dylibCmd->dylib.name.offset > cmdLength )
+ dyld::throwf("malformed mach-o image: dylib load command #%d has offset (%u) outside its size (%u)", i, dylibCmd->dylib.name.offset, cmdLength);
+ if ( (dylibCmd->dylib.name.offset + strlen((char*)dylibCmd + dylibCmd->dylib.name.offset) + 1) > cmdLength )
+ dyld::throwf("malformed mach-o image: dylib load command #%d string extends beyond end of load command", i);
break;
case LC_CODE_SIGNATURE:
- *codeSigCmd = (struct linkedit_data_command*)cmd; // only support one LC_CODE_SIGNATURE per image
+ if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+ throw "malformed mach-o image: LC_CODE_SIGNATURE size wrong";
+ // <rdar://problem/22799652> only support one LC_CODE_SIGNATURE per image
+ if ( *codeSigCmd != NULL )
+ throw "malformed mach-o image: multiple LC_CODE_SIGNATURE load commands";
+ *codeSigCmd = (struct linkedit_data_command*)cmd;
break;
case LC_ENCRYPTION_INFO:
+ if ( cmd->cmdsize != sizeof(encryption_info_command) )
+ throw "malformed mach-o image: LC_ENCRYPTION_INFO size wrong";
+ // <rdar://problem/22799652> only support one LC_ENCRYPTION_INFO per image
+ if ( *encryptCmd != NULL )
+ throw "malformed mach-o image: multiple LC_ENCRYPTION_INFO load commands";
+ *encryptCmd = (encryption_info_command*)cmd;
+ break;
case LC_ENCRYPTION_INFO_64:
- *encryptCmd = (struct encryption_info_command*)cmd; // only support one LC_ENCRYPTION_INFO[_64] per image
+ if ( cmd->cmdsize != sizeof(encryption_info_command_64) )
+ throw "malformed mach-o image: LC_ENCRYPTION_INFO_64 size wrong";
+ // <rdar://problem/22799652> only support one LC_ENCRYPTION_INFO_64 per image
+ if ( *encryptCmd != NULL )
+ throw "malformed mach-o image: multiple LC_ENCRYPTION_INFO_64 load commands";
+ *encryptCmd = (encryption_info_command*)cmd;
break;
+ case LC_SYMTAB:
+ if ( cmd->cmdsize != sizeof(symtab_command) )
+ throw "malformed mach-o image: LC_SYMTAB size wrong";
+ symTabCmd = (symtab_command*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ if ( cmd->cmdsize != sizeof(dysymtab_command) )
+ throw "malformed mach-o image: LC_DYSYMTAB size wrong";
+ dynSymbTabCmd = (dysymtab_command*)cmd;
+ break;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // <rdar://problem/26797345> error when loading iOS Simulator mach-o binary into macOS process
+ case LC_VERSION_MIN_WATCHOS:
+ case LC_VERSION_MIN_TVOS:
+ case LC_VERSION_MIN_IPHONEOS:
+ throw "mach-o, but built for simulator (not macOS)";
+ break;
+#endif
}
cmd = nextCmd;
}
- if ( context.codeSigningEnforced && !foundLoadCommandSegment )
+ if ( context.strictMachORequired && !foundLoadCommandSegment )
throw "load commands not in a segment";
-
+ if ( linkeditSegCmd == NULL )
+ throw "malformed mach-o image: missing __LINKEDIT segment";
+ if ( startOfFileSegCmd == NULL )
+ throw "malformed mach-o image: missing __TEXT segment that maps start of file";
// <rdar://problem/13145644> verify every segment does not overlap another segment
- if ( context.codeSigningEnforced ) {
+ if ( context.strictMachORequired ) {
uintptr_t lastFileStart = 0;
uintptr_t linkeditFileStart = 0;
const struct load_command* cmd1 = startCmds;
dyld::throwf("malformed mach-o image: __LINKEDIT must be last segment");
}
+ // validate linkedit content
+ if ( (dyldInfoCmd == NULL) && (symTabCmd == NULL) )
+ throw "malformed mach-o image: missing LC_SYMTAB or LC_DYLD_INFO";
+ if ( dynSymbTabCmd == NULL )
+ throw "malformed mach-o image: missing LC_DYSYMTAB";
+
+ uint32_t linkeditFileOffsetStart = (uint32_t)linkeditSegCmd->fileoff;
+ uint32_t linkeditFileOffsetEnd = (uint32_t)linkeditSegCmd->fileoff + (uint32_t)linkeditSegCmd->filesize;
+
+ if ( !inCache && (dyldInfoCmd != NULL) && context.strictMachORequired ) {
+ // validate all LC_DYLD_INFO chunks fit in LINKEDIT and don't overlap
+ uint32_t offset = linkeditFileOffsetStart;
+ if ( dyldInfoCmd->rebase_size != 0 ) {
+ if ( dyldInfoCmd->rebase_size & 0x80000000 )
+ throw "malformed mach-o image: dyld rebase info size overflow";
+ if ( dyldInfoCmd->rebase_off < offset )
+ throw "malformed mach-o image: dyld rebase info underruns __LINKEDIT";
+ offset = dyldInfoCmd->rebase_off + dyldInfoCmd->rebase_size;
+ if ( offset > linkeditFileOffsetEnd )
+ throw "malformed mach-o image: dyld rebase info overruns __LINKEDIT";
+ }
+ if ( dyldInfoCmd->bind_size != 0 ) {
+ if ( dyldInfoCmd->bind_size & 0x80000000 )
+ throw "malformed mach-o image: dyld bind info size overflow";
+ if ( dyldInfoCmd->bind_off < offset )
+ throw "malformed mach-o image: dyld bind info overlaps rebase info";
+ offset = dyldInfoCmd->bind_off + dyldInfoCmd->bind_size;
+ if ( offset > linkeditFileOffsetEnd )
+ throw "malformed mach-o image: dyld bind info overruns __LINKEDIT";
+ }
+ if ( dyldInfoCmd->weak_bind_size != 0 ) {
+ if ( dyldInfoCmd->weak_bind_size & 0x80000000 )
+ throw "malformed mach-o image: dyld weak bind info size overflow";
+ if ( dyldInfoCmd->weak_bind_off < offset )
+ throw "malformed mach-o image: dyld weak bind info overlaps bind info";
+ offset = dyldInfoCmd->weak_bind_off + dyldInfoCmd->weak_bind_size;
+ if ( offset > linkeditFileOffsetEnd )
+ throw "malformed mach-o image: dyld weak bind info overruns __LINKEDIT";
+ }
+ if ( dyldInfoCmd->lazy_bind_size != 0 ) {
+ if ( dyldInfoCmd->lazy_bind_size & 0x80000000 )
+ throw "malformed mach-o image: dyld lazy bind info size overflow";
+ if ( dyldInfoCmd->lazy_bind_off < offset )
+ throw "malformed mach-o image: dyld lazy bind info overlaps weak bind info";
+ offset = dyldInfoCmd->lazy_bind_off + dyldInfoCmd->lazy_bind_size;
+ if ( offset > linkeditFileOffsetEnd )
+ throw "malformed mach-o image: dyld lazy bind info overruns __LINKEDIT";
+ }
+ if ( dyldInfoCmd->export_size != 0 ) {
+ if ( dyldInfoCmd->export_size & 0x80000000 )
+ throw "malformed mach-o image: dyld export info size overflow";
+ if ( dyldInfoCmd->export_off < offset )
+ throw "malformed mach-o image: dyld export info overlaps lazy bind info";
+ offset = dyldInfoCmd->export_off + dyldInfoCmd->export_size;
+ if ( offset > linkeditFileOffsetEnd )
+ throw "malformed mach-o image: dyld export info overruns __LINKEDIT";
+ }
+ }
+
+ if ( symTabCmd != NULL ) {
+ // validate symbol table fits in LINKEDIT
+ if ( symTabCmd->symoff < linkeditFileOffsetStart )
+ throw "malformed mach-o image: symbol table underruns __LINKEDIT";
+ if ( symTabCmd->nsyms > 0x10000000 )
+ throw "malformed mach-o image: symbol table too large";
+ uint32_t symbolsSize = symTabCmd->nsyms * sizeof(macho_nlist);
+ if ( symbolsSize > linkeditSegCmd->filesize )
+ throw "malformed mach-o image: symbol table overruns __LINKEDIT";
+ if ( symTabCmd->symoff + symbolsSize < symTabCmd->symoff )
+ throw "malformed mach-o image: symbol table size wraps";
+ if ( symTabCmd->symoff + symbolsSize > symTabCmd->stroff )
+ throw "malformed mach-o image: symbol table overlaps symbol strings";
+ if ( symTabCmd->stroff + symTabCmd->strsize < symTabCmd->stroff )
+ throw "malformed mach-o image: symbol string size wraps";
+ if ( symTabCmd->stroff + symTabCmd->strsize > linkeditFileOffsetEnd ) {
+ // <rdar://problem/24220313> let old apps overflow as long as it stays within mapped page
+ if ( context.strictMachORequired || (symTabCmd->stroff + symTabCmd->strsize > ((linkeditFileOffsetEnd + 4095) & (-4096))) )
+ throw "malformed mach-o image: symbol strings overrun __LINKEDIT";
+ }
+ // validate indirect symbol table
+ if ( dynSymbTabCmd->nindirectsyms != 0 ) {
+ if ( dynSymbTabCmd->indirectsymoff < linkeditFileOffsetStart )
+ throw "malformed mach-o image: indirect symbol table underruns __LINKEDIT";
+ if ( dynSymbTabCmd->nindirectsyms > 0x10000000 )
+ throw "malformed mach-o image: indirect symbol table too large";
+ uint32_t indirectTableSize = dynSymbTabCmd->nindirectsyms * sizeof(uint32_t);
+ if ( indirectTableSize > linkeditSegCmd->filesize )
+ throw "malformed mach-o image: indirect symbol table overruns __LINKEDIT";
+ if ( dynSymbTabCmd->indirectsymoff + indirectTableSize < dynSymbTabCmd->indirectsymoff )
+ throw "malformed mach-o image: indirect symbol table size wraps";
+ if ( context.strictMachORequired && (dynSymbTabCmd->indirectsymoff + indirectTableSize > symTabCmd->stroff) )
+ throw "malformed mach-o image: indirect symbol table overruns string pool";
+ }
+ if ( (dynSymbTabCmd->nlocalsym > symTabCmd->nsyms) || (dynSymbTabCmd->ilocalsym > symTabCmd->nsyms) )
+ throw "malformed mach-o image: indirect symbol table local symbol count exceeds total symbols";
+ if ( dynSymbTabCmd->ilocalsym + dynSymbTabCmd->nlocalsym < dynSymbTabCmd->ilocalsym )
+ throw "malformed mach-o image: indirect symbol table local symbol count wraps";
+ if ( (dynSymbTabCmd->nextdefsym > symTabCmd->nsyms) || (dynSymbTabCmd->iextdefsym > symTabCmd->nsyms) )
+ throw "malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols";
+ if ( dynSymbTabCmd->iextdefsym + dynSymbTabCmd->nextdefsym < dynSymbTabCmd->iextdefsym )
+ throw "malformed mach-o image: indirect symbol table extern symbol count wraps";
+ if ( (dynSymbTabCmd->nundefsym > symTabCmd->nsyms) || (dynSymbTabCmd->iundefsym > symTabCmd->nsyms) )
+ throw "malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols";
+ if ( dynSymbTabCmd->iundefsym + dynSymbTabCmd->nundefsym < dynSymbTabCmd->iundefsym )
+ throw "malformed mach-o image: indirect symbol table undefined symbol count wraps";
+ }
+
+
// fSegmentsArrayCount is only 8-bits
if ( *segCount > 255 )
dyld::throwf("malformed mach-o image: more than 255 segments in %s", path);
// create image by mapping in a mach-o file
-ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, const uint8_t firstPage[4096], uint64_t offsetInFat,
+ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, const uint8_t firstPages[], size_t firstPagesSize, uint64_t offsetInFat,
uint64_t lenInFat, const struct stat& info, const LinkContext& context)
{
- // get load commands
- const unsigned int dataSize = sizeof(macho_header) + ((macho_header*)firstPage)->sizeofcmds;
- uint8_t buffer[dataSize];
- const uint8_t* fileData = firstPage;
- if ( dataSize > 4096 ) {
- // only read more if cmds take up more space than first page
- fileData = buffer;
- memcpy(buffer, firstPage, 4096);
- pread(fd, &buffer[4096], dataSize-4096, offsetInFat+4096);
- }
-
bool compressed;
unsigned int segCount;
unsigned int libCount;
const linkedit_data_command* codeSigCmd;
const encryption_info_command* encryptCmd;
- sniffLoadCommands((const macho_header*)fileData, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
+ sniffLoadCommands((const macho_header*)firstPages, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
// instantiate concrete class based on content of load commands
if ( compressed )
- return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, dataSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, encryptCmd, context);
+ return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, firstPages, firstPagesSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, encryptCmd, context);
else
#if SUPPORT_CLASSIC_MACHO
- return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, dataSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context);
+ return ImageLoaderMachOClassic::instantiateFromFile(path, fd, firstPages, firstPagesSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context);
#else
throw "missing LC_DYLD_INFO load command";
#endif
for(unsigned int i=0; i < fSegmentsCount; ++i) {
// set up pointer to __LINKEDIT segment
if ( strcmp(segName(i),"__LINKEDIT") == 0 ) {
- if ( context.codeSigningEnforced && (segFileOffset(i) > fCoveredCodeLength))
+ if ( context.requireCodeSignature && (segFileOffset(i) > fCoveredCodeLength))
dyld::throwf("cannot load '%s' (segment outside of code signature)", this->getShortName());
fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i));
}
{
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
const bool isTextSeg = (strcmp(seg->segname, "__TEXT") == 0);
+ #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED
+ const bool isObjCSeg = (strcmp(seg->segname, "__OBJC") == 0);
+ if ( isObjCSeg )
+ fNotifyObjC = true;
+ #else
+ const bool isDataSeg = (strncmp(seg->segname, "__DATA", 6) == 0);
+ #endif
const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
fEHFrameSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData);
else if ( isTextSeg && (strcmp(sect->sectname, "__unwind_info") == 0) )
fUnwindInfoSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData);
+
+ #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED
+ else if ( isObjCSeg ) {
+ if ( strcmp(sect->sectname, "__image_info") == 0 ) {
+ const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide);
+ uint32_t flags = imageInfo[1];
+ if ( (flags & 4) && (((macho_header*)fMachOData)->filetype != MH_EXECUTE) )
+ dyld::throwf("cannot load '%s' because Objective-C garbage collection is not supported", getPath());
+ }
+ else if ( ((macho_header*)fMachOData)->filetype == MH_DYLIB ) {
+ fRetainForObjC = true;
+ }
+ }
+ #else
+ else if ( isDataSeg && (strncmp(sect->sectname, "__objc_imageinfo", 16) == 0) ) {
+ #if __MAC_OS_X_VERSION_MIN_REQUIRED
+ const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide);
+ uint32_t flags = imageInfo[1];
+ if ( (flags & 4) && (((macho_header*)fMachOData)->filetype != MH_EXECUTE) )
+ dyld::throwf("cannot load '%s' because Objective-C garbage collection is not supported", getPath());
+ #endif
+ fNotifyObjC = true;
+ }
+ else if ( isDataSeg && (strncmp(sect->sectname, "__objc_", 7) == 0) && (((macho_header*)fMachOData)->filetype == MH_DYLIB) )
+ fRetainForObjC = true;
+ #endif
}
}
break;
unsigned int textSegmentIndex = 0;
for(unsigned int i=0; i < fSegmentsCount; ++i) {
//dyld::log("unmap %s at 0x%08lX\n", seg->getName(), seg->getActualLoadAddress(this));
- if ( strcmp(segName(i), "__TEXT") == 0 ) {
+ if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) {
textSegmentIndex = i;
}
else {
void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context)
{
// if dylib being loaded has no code signature load command
- if ( codeSigCmd == NULL ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- bool codeSigningEnforced = context.codeSigningEnforced;
- if ( context.mainExecutableCodeSigned && !codeSigningEnforced ) {
- static bool codeSignEnforcementDynamicallyEnabled = false;
- if ( !codeSignEnforcementDynamicallyEnabled ) {
- uint32_t flags;
- if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
- if ( flags & CS_ENFORCEMENT ) {
- codeSignEnforcementDynamicallyEnabled = true;
- }
- }
- }
- codeSigningEnforced = codeSignEnforcementDynamicallyEnabled;
- }
- // if we require dylibs to be code signed
- if ( codeSigningEnforced ) {
- // if there is a non-load command based code signature, use it
- off_t offset = (off_t)offsetInFatFile;
- if ( fcntl(fd, F_FINDSIGS, &offset, sizeof(offset)) != -1 )
- return;
- // otherwise gracefully return from dlopen()
+ if ( codeSigCmd == NULL) {
+ if (context.requireCodeSignature ) {
+ // if we require dylibs to be codesigned there needs to be a signature.
dyld::throwf("required code signature missing for '%s'\n", this->getPath());
+ } else {
+ disableCoverageCheck();
}
-#endif
- //Since we don't have a range for the signature we have to assume full coverage
- fCoveredCodeLength = UINT64_MAX;
}
else {
#if __MAC_OS_X_VERSION_MIN_REQUIRED
dyld::log("dyld: Registered code signature for %s\n", this->getPath());
}
fCoveredCodeLength = siginfo.fs_file_start;
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( context.processUsingLibraryValidation ) {
+ fchecklv checkInfo;
+ char messageBuffer[512];
+ messageBuffer[0] = '\0';
+ checkInfo.lv_file_start = offsetInFatFile;
+ checkInfo.lv_error_message_size = sizeof(messageBuffer);
+ checkInfo.lv_error_message = messageBuffer;
+ int res = fcntl(fd, F_CHECK_LV, &checkInfo);
+ if ( res == -1 ) {
+ dyld::throwf("code signature in (%s) not valid for use in process using Library Validation: %s", this->getPath(), messageBuffer);
+ }
+ }
+#endif
}
}
}
#endif
if (codeSigCmd != NULL) {
- if ( context.verboseMapping )
- dyld::log("dyld: validate pages: %llu\n", (unsigned long long)offsetInFat);
-
void *fdata = xmmap(NULL, lenFileData, PROT_READ | PROT_EXEC, MAP_SHARED, fd, offsetInFat);
if ( fdata == MAP_FAILED ) {
- if ( context.processRequiresLibraryValidation )
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( context.processUsingLibraryValidation ) {
dyld::throwf("cannot load image with wrong team ID in process using Library Validation");
+ }
else
- dyld::throwf("mmap() errno=%d validating first page of '%s'", errno, getInstallPath());
+#endif
+ {
+ int errnoCopy = errno;
+ if ( errnoCopy == EPERM ) {
+ if ( dyld::sandboxBlockedMmap(getPath()) )
+ dyld::throwf("file system sandbox blocked mmap() of '%s'", getPath());
+ else
+ dyld::throwf("code signing blocked mmap() of '%s'", getPath());
+ }
+ else
+ dyld::throwf("mmap() errno=%d validating first page of '%s'", errnoCopy, getPath());
+ }
}
if ( memcmp(fdata, fileData, lenFileData) != 0 )
- dyld::throwf("mmap() page compare failed for '%s'", getInstallPath());
+ dyld::throwf("mmap() page compare failed for '%s'", getPath());
munmap(fdata, lenFileData);
}
}
const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
if ( ((sect->flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sect->sectname, "__interpose") == 0) && (strcmp(seg->segname, "__DATA") == 0)) ) {
+ // <rdar://problem/23929217> Ensure section is within segment
+ if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
+ dyld::throwf("interpose section has malformed address range for %s\n", this->getPath());
const InterposeData* interposeArray = (InterposeData*)(sect->addr + fSlide);
const size_t count = sect->size / sizeof(InterposeData);
- for (size_t i=0; i < count; ++i) {
+ for (size_t j=0; j < count; ++j) {
ImageLoader::InterposeTuple tuple;
- tuple.replacement = interposeArray[i].replacement;
+ tuple.replacement = interposeArray[j].replacement;
tuple.neverImage = this;
tuple.onlyImage = NULL;
- tuple.replacee = interposeArray[i].replacee;
+ tuple.replacee = interposeArray[j].replacee;
+ // <rdar://problem/25686570> ignore interposing on a weak function that does not exist
+ if ( tuple.replacee == 0 )
+ continue;
// <rdar://problem/7937695> verify that replacement is in this image
if ( this->containsAddress((void*)tuple.replacement) ) {
// chain to any existing interpositions
}
}
-uint32_t ImageLoaderMachO::sdkVersion() const
+uint32_t ImageLoaderMachO::sdkVersion(const mach_header* mh)
{
- const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
- const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
+ const uint32_t cmd_count = mh->ncmds;
+ const struct load_command* const cmds = (struct load_command*)(((char*)mh) + sizeof(macho_header));
const struct load_command* cmd = cmds;
const struct version_min_command* versCmd;
for (uint32_t i = 0; i < cmd_count; ++i) {
return 0;
}
+uint32_t ImageLoaderMachO::sdkVersion() const
+{
+ return ImageLoaderMachO::sdkVersion(machHeader());
+}
+
uint32_t ImageLoaderMachO::minOSVersion(const mach_header* mh)
{
const uint32_t cmd_count = mh->ncmds;
{
if ( needsAddedLibSystemDepency(libraryCount(), (macho_header*)fMachOData) ) {
DependentLibraryInfo* lib = &libs[0];
- lib->name = "/usr/lib/libSystem.B.dylib";
+ lib->name = LIBSYSTEM_DYLIB_PATH;
lib->info.checksum = 0;
lib->info.minVersion = 0;
lib->info.maxVersion = 0;
}
}
-ImageLoader::LibraryInfo ImageLoaderMachO::doGetLibraryInfo()
+ImageLoader::LibraryInfo ImageLoaderMachO::doGetLibraryInfo(const LibraryInfo&)
{
LibraryInfo info;
if ( fDylibIDOffset != 0 ) {
const char* pathToAdd = NULL;
const char* path = (char*)cmd + ((struct rpath_command*)cmd)->path.offset;
if ( (strncmp(path, "@loader_path", 12) == 0) && ((path[12] == '/') || (path[12] == '\0')) ) {
- if ( context.processIsRestricted && !context.processRequiresLibraryValidation && (context.mainExecutable == this) ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( context.processIsRestricted && (context.mainExecutable == this) ) {
dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @loader_path\n", path, this->getPath());
break;
}
+#endif
char resolvedPath[PATH_MAX];
if ( realpath(this->getPath(), resolvedPath) != NULL ) {
char newRealPath[strlen(resolvedPath) + strlen(path)];
}
}
else if ( (strncmp(path, "@executable_path", 16) == 0) && ((path[16] == '/') || (path[16] == '\0')) ) {
- if ( context.processIsRestricted && !context.processRequiresLibraryValidation ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( context.processIsRestricted ) {
dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @executable_path\n", path, this->getPath());
break;
}
+#endif
char resolvedPath[PATH_MAX];
if ( realpath(context.mainExecutable->getPath(), resolvedPath) != NULL ) {
char newRealPath[strlen(resolvedPath) + strlen(path)];
}
}
}
- else if ( (path[0] != '/') && context.processIsRestricted && !context.processRequiresLibraryValidation ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ else if ( (path[0] != '/') && context.processIsRestricted ) {
dyld::warn("LC_RPATH %s in %s being ignored in restricted program because it is a relative path\n", path, this->getPath());
break;
}
+#endif
+#if SUPPORT_ROOT_PATH
else if ( (path[0] == '/') && (context.rootPaths != NULL) ) {
// <rdar://problem/5869973> DYLD_ROOT_PATH should apply to LC_RPATH rpaths
// DYLD_ROOT_PATH can be a list of paths, but at this point we can only support one, so use first combination that exists
pathToAdd = strdup(path);
}
}
+#endif
else {
// make copy so that all elements of 'paths' can be freed
pathToAdd = strdup(path);
void ImageLoaderMachO::doRebase(const LinkContext& context)
{
+ // <rdar://problem/25329861> Delay calling setNeverUnload() until we know this is not for dlopen_preflight()
+ if ( fRetainForObjC )
+ this->setNeverUnload();
+
// if prebound and loaded at prebound address, then no need to rebase
if ( this->usablePrebinding(context) ) {
// skip rebasing because prebinding is valid
#endif
// do actual rebasing
- this->rebase(context);
+ this->rebase(context, fSlide);
#if TEXT_RELOC_SUPPORT
// if there were __TEXT fixups, restore write protection
}
#endif
-const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const
+const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const
{
// look in this image first
- const ImageLoader::Symbol* result = this->findExportedSymbol(name, foundIn);
+ const ImageLoader::Symbol* result = this->findShallowExportedSymbol(name, foundIn);
if ( result != NULL )
return result;
if ( libReExported(i) ) {
ImageLoader* image = libImage(i);
if ( image != NULL ) {
- const Symbol* result = image->findExportedSymbol(name, searchReExports, foundIn);
+ const char* reExPath = libPath(i);
+ result = image->findExportedSymbol(name, searchReExports, reExPath, foundIn);
if ( result != NULL )
return result;
}
uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context,
- const ImageLoader* requestor, bool runResolver) const
+ const ImageLoader* requestor, bool runResolver, const char* symbolName) const
{
return this->getSymbolAddress(sym, requestor, context, runResolver);
}
}
}
+intptr_t ImageLoaderMachO::computeSlide(const mach_header* mh)
+{
+ const uint32_t cmd_count = mh->ncmds;
+ const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header));
+ const load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
+ const macho_segment_command* seg = (macho_segment_command*)cmd;
+ if ( (seg->fileoff == 0) && (seg->filesize != 0) )
+ return (char*)mh - (char*)(seg->vmaddr);
+ }
+ cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ return 0;
+}
+
+bool ImageLoaderMachO::findSection(const mach_header* mh, const char* segmentName, const char* sectionName, void** sectAddress, size_t* sectSize)
+{
+ const uint32_t cmd_count = mh->ncmds;
+ const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header));
+ const load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND:
+ {
+ const macho_segment_command* seg = (macho_segment_command*)cmd;
+ const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command));
+ const macho_section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( (strcmp(sect->segname, segmentName) == 0) && (strcmp(sect->sectname, sectionName) == 0) ) {
+ *sectAddress = (void*)(sect->addr + computeSlide(mh));
+ *sectSize = sect->size;
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ return false;
+}
+
bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset)
{
return false;
}
+const char* ImageLoaderMachO::libPath(unsigned int index) const
+{
+ const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
+ const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
+ const struct load_command* cmd = cmds;
+ unsigned count = 0;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch ( cmd->cmd ) {
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
+ if ( index == count ) {
+ const struct dylib_command* dylibCmd = (struct dylib_command*)cmd;
+ return (char*)cmd + dylibCmd->dylib.name.offset;
+ }
+ ++count;
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+
+ // <rdar://problem/24256354> if image linked with nothing and we implicitly added libSystem.dylib, return that
+ if ( needsAddedLibSystemDepency(libraryCount(), (macho_header*)fMachOData) ) {
+ return LIBSYSTEM_DYLIB_PATH;
+ }
+
+ return NULL;
+}
+
void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const LinkContext& context, const char* symbol,
const char* referencedFrom, const char* fromVersMismatch,
const char* expectedIn)
{
// record values for possible use by CrashReporter or Finder
- (*context.setErrorStrings)(dyld_error_kind_symbol_missing, referencedFrom, expectedIn, symbol);
+ (*context.setErrorStrings)(DYLD_EXIT_REASON_SYMBOL_MISSING, referencedFrom, expectedIn, symbol);
dyld::throwf("Symbol not found: %s\n Referenced from: %s%s\n Expected in: %s\n",
symbol, referencedFrom, fromVersMismatch, expectedIn);
}
uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value,
- const ImageLoader* targetImage, uint8_t type, const char* symbolName,
- intptr_t addend, const char* msg)
+ uint8_t type, const char* symbolName,
+ intptr_t addend, const char* inPath, const char* toPath, const char* msg)
{
// log
if ( context.verboseBind ) {
if ( addend != 0 )
dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX + %ld\n",
- msg, this->getShortName(), (uintptr_t)location,
- ((targetImage != NULL) ? targetImage->getShortName() : "<weak_import-missing>"),
+ msg, shortName(inPath), (uintptr_t)location,
+ ((toPath != NULL) ? shortName(toPath) : "<missing weak_import>"),
symbolName, (uintptr_t)location, value, addend);
else
dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX\n",
- msg, this->getShortName(), (uintptr_t)location,
- ((targetImage != NULL) ? targetImage->getShortName() : "<weak>import-missing>"),
+ msg, shortName(inPath), (uintptr_t)location,
+ ((toPath != NULL) ? shortName(toPath) : "<missing weak_import>"),
symbolName, (uintptr_t)location, value);
}
#if LOG_BINDINGS
vars.mh = (macho_header*)fMachOData;
// lookup _NXArgc
- sym = this->findExportedSymbol("_NXArgc", false, NULL);
+ sym = this->findShallowExportedSymbol("_NXArgc", NULL);
if ( sym != NULL )
- vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this, false);
+ vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this, false, NULL);
// lookup _NXArgv
- sym = this->findExportedSymbol("_NXArgv", false, NULL);
+ sym = this->findShallowExportedSymbol("_NXArgv", NULL);
if ( sym != NULL )
- vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false);
+ vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false, NULL);
// lookup _environ
- sym = this->findExportedSymbol("_environ", false, NULL);
+ sym = this->findShallowExportedSymbol("_environ", NULL);
if ( sym != NULL )
- vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false);
+ vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false, NULL);
// lookup __progname
- sym = this->findExportedSymbol("___progname", false, NULL);
+ sym = this->findShallowExportedSymbol("___progname", NULL);
if ( sym != NULL )
- vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this, false);
+ vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this, false, NULL);
context.setNewProgramVars(vars);
}
if ( ! this->containsAddress((void*)func) ) {
dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
}
+ if ( ! dyld::gProcessInfo->libSystemInitialized ) {
+ // <rdar://problem/17973316> libSystem initializer must run first
+ dyld::throwf("-init function in image (%s) that does not link with libSystem.dylib\n", this->getPath());
+ }
if ( context.verboseInit )
dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath());
func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
if ( type == S_MOD_INIT_FUNC_POINTERS ) {
Initializer* inits = (Initializer*)(sect->addr + fSlide);
const size_t count = sect->size / sizeof(uintptr_t);
- for (size_t i=0; i < count; ++i) {
- Initializer func = inits[i];
+ // <rdar://problem/23929217> Ensure __mod_init_func section is within segment
+ if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
+ dyld::throwf("__mod_init_funcs section has malformed address range for %s\n", this->getPath());
+ for (size_t j=0; j < count; ++j) {
+ Initializer func = inits[j];
// <rdar://problem/8543820&9228031> verify initializers are in image
if ( ! this->containsAddress((void*)func) ) {
dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
}
+ if ( ! dyld::gProcessInfo->libSystemInitialized ) {
+ // <rdar://problem/17973316> libSystem initializer must run first
+ const char* installPath = getInstallPath();
+ if ( (installPath == NULL) || (strcmp(installPath, LIBSYSTEM_DYLIB_PATH) != 0) )
+ dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath());
+ }
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
+ bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
+ bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
+ if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
+ // now safe to use malloc() and other calls in libSystem.dylib
+ dyld::gProcessInfo->libSystemInitialized = true;
+ }
}
}
}
-
-
-
void ImageLoaderMachO::doGetDOFSections(const LinkContext& context, std::vector<ImageLoader::DOFInfo>& dofs)
{
if ( fHasDOFSections ) {
const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
if ( (sect->flags & SECTION_TYPE) == S_DTRACE_DOF ) {
+ // <rdar://problem/23929217> Ensure section is within segment
+ if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
+ dyld::throwf("DOF section has malformed address range for %s\n", this->getPath());
ImageLoader::DOFInfo info;
info.dof = (void*)(sect->addr + fSlide);
info.imageHeader = this->machHeader();
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
const uint8_t type = sect->flags & SECTION_TYPE;
if ( type == S_MOD_TERM_FUNC_POINTERS ) {
+ // <rdar://problem/23929217> Ensure section is within segment
+ if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
+ dyld::throwf("DOF section has malformed address range for %s\n", this->getPath());
Terminator* terms = (Terminator*)(sect->addr + fSlide);
const size_t count = sect->size / sizeof(uintptr_t);
- for (size_t i=count; i > 0; --i) {
- Terminator func = terms[i-1];
+ for (size_t j=count; j > 0; --j) {
+ Terminator func = terms[j-1];
// <rdar://problem/8543820&9228031> verify terminators are in image
if ( ! this->containsAddress((void*)func) ) {
dyld::throwf("termination function %p not in mapped image for %s\n", func, this->getPath());
}
-void ImageLoaderMachO::printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo)
+void ImageLoaderMachO::printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo)
{
- ImageLoader::printStatistics(imageCount, timingInfo);
+ ImageLoader::printStatisticsDetails(imageCount, timingInfo);
dyld::log("total symbol trie searches: %d\n", fgSymbolTrieSearchs);
dyld::log("total symbol table binary searches: %d\n", fgSymbolTableBinarySearchs);
dyld::log("total images defining weak symbols: %u\n", fgImagesHasWeakDefinitions);
}
else if ( ! this->segmentsCanSlide() ) {
for(unsigned int i=0, e=segmentCount(); i < e; ++i) {
- if ( strcmp(segName(i), "__PAGEZERO") == 0 )
+ if ( (strcmp(segName(i), "__PAGEZERO") == 0) && (segFileSize(i) == 0) && (segPreferredLoadAddress(i) == 0) )
continue;
if ( !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) )
dyld::throwf("can't map unslidable segment %s to 0x%lX with size 0x%lX", segName(i), segPreferredLoadAddress(i), segSize(i));
protection |= PROT_EXEC;
if ( segReadable(i) )
protection |= PROT_READ;
- if ( segWriteable(i) )
+ if ( segWriteable(i) ) {
protection |= PROT_WRITE;
+ // rdar://problem/22525618 force __LINKEDIT to always be mapped read-only
+ if ( strcmp(segName(i), "__LINKEDIT") == 0 )
+ protection = PROT_READ;
+ }
}
#if __i386__
// initially map __IMPORT segments R/W so dyld can update them
}
void* loadAddress = xmmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset);
if ( loadAddress == ((void*)(-1)) ) {
- dyld::throwf("mmap() error %d at address=0x%08lX, size=0x%08lX segment=%s in Segment::map() mapping %s",
- errno, requestedLoadAddress, (uintptr_t)size, segName(i), getPath());
+ int mmapErr = errno;
+ if ( mmapErr == EPERM ) {
+ if ( dyld::sandboxBlockedMmap(getPath()) )
+ dyld::throwf("file system sandbox blocked mmap() of '%s'", this->getPath());
+ else
+ dyld::throwf("code signing blocked mmap() of '%s'", this->getPath());
+ }
+ else
+ dyld::throwf("mmap() errno=%d at address=0x%08lX, size=0x%08lX segment=%s in Segment::map() mapping %s",
+ mmapErr, requestedLoadAddress, (uintptr_t)size, segName(i), getPath());
}
}
// update stats
}
+const char* ImageLoaderMachO::findClosestSymbol(const mach_header* mh, const void* addr, const void** closestAddr)
+{
+ // called by dladdr()
+ // only works with compressed LINKEDIT if classic symbol table is also present
+ const dysymtab_command* dynSymbolTable = NULL;
+ const symtab_command* symtab = NULL;
+ const macho_segment_command* seg;
+ const uint8_t* unslidLinkEditBase = NULL;
+ bool linkEditBaseFound = false;
+ intptr_t slide = 0;
+ const uint32_t cmd_count = mh->ncmds;
+ const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header));
+ const load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND:
+ seg = (macho_segment_command*)cmd;
+ if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) {
+ unslidLinkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff);
+ linkEditBaseFound = true;
+ }
+ else if ( (seg->fileoff == 0) && (seg->filesize != 0) )
+ slide = (uintptr_t)mh - seg->vmaddr;
+ break;
+ case LC_SYMTAB:
+ symtab = (symtab_command*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ dynSymbolTable = (dysymtab_command*)cmd;
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ // no symbol table => no lookup by address
+ if ( (symtab == NULL) || (dynSymbolTable == NULL) || !linkEditBaseFound )
+ return NULL;
+
+ const uint8_t* linkEditBase = unslidLinkEditBase + slide;
+ const char* symbolTableStrings = (const char*)&linkEditBase[symtab->stroff];
+ const macho_nlist* symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]);
+
+ uintptr_t targetAddress = (uintptr_t)addr - slide;
+ const struct macho_nlist* bestSymbol = NULL;
+ // first walk all global symbols
+ const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym];
+ const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym];
+ for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) {
+ if ( (s->n_type & N_TYPE) == N_SECT ) {
+ if ( bestSymbol == NULL ) {
+ if ( s->n_value <= targetAddress )
+ bestSymbol = s;
+ }
+ else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) {
+ bestSymbol = s;
+ }
+ }
+ }
+ // next walk all local symbols
+ const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym];
+ const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym];
+ for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) {
+ if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
+ if ( bestSymbol == NULL ) {
+ if ( s->n_value <= targetAddress )
+ bestSymbol = s;
+ }
+ else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) {
+ bestSymbol = s;
+ }
+ }
+ }
+ if ( bestSymbol != NULL ) {
+#if __arm__
+ if (bestSymbol->n_desc & N_ARM_THUMB_DEF)
+ *closestAddr = (void*)((bestSymbol->n_value | 1) + slide);
+ else
+ *closestAddr = (void*)(bestSymbol->n_value + slide);
+#else
+ *closestAddr = (void*)(bestSymbol->n_value + slide);
+#endif
+ return &symbolTableStrings[bestSymbol->n_un.n_strx];
+ }
+ return NULL;
+}
+
+bool ImageLoaderMachO::getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd,
+ uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind)
+{
+ if ( lazyBindingInfoOffset > (lazyInfoEnd-lazyInfoStart) )
+ return false;
+ uint8_t type = BIND_TYPE_POINTER;
+ uint8_t symboFlags = 0;
+ bool done = false;
+ const uint8_t* p = &lazyInfoStart[lazyBindingInfoOffset];
+ while ( !done && (p < lazyInfoEnd) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ *doneAfterBind = false;
+ return true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ *ordinal = immediate;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ *ordinal = (int)read_uleb128(p, lazyInfoEnd);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ *ordinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ *ordinal = signExtended;
+ }
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ *symbolName = (char*)p;
+ symboFlags = immediate;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ *segIndex = immediate;
+ *segOffset = read_uleb128(p, lazyInfoEnd);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ *doneAfterBind = ((*p & BIND_OPCODE_MASK) == BIND_OPCODE_DONE);
+ lazyBindingInfoOffset += p - &lazyInfoStart[lazyBindingInfoOffset];
+ return true;
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+const dyld_info_command* ImageLoaderMachO::findDyldInfoLoadCommand(const mach_header* mh)
+{
+ const uint32_t cmd_count = mh->ncmds;
+ const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header));
+ const load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ return (dyld_info_command*)cmd;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ return NULL;
+}
+
+
+uintptr_t ImageLoaderMachO::segPreferredAddress(const mach_header* mh, unsigned segIndex)
+{
+ const uint32_t cmd_count = mh->ncmds;
+ const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header));
+ const load_command* cmd = cmds;
+ unsigned curSegIndex = 0;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
+ if ( segIndex == curSegIndex ) {
+ const macho_segment_command* segCmd = (macho_segment_command*)cmd;
+ return segCmd->vmaddr;
+ }
+ ++curSegIndex;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ return 0;
+}
+
+
+
class ImageLoaderMachO : public ImageLoader {
public:
static ImageLoader* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context);
- static ImageLoader* instantiateFromFile(const char* path, int fd, const uint8_t firstPages[4096], uint64_t offsetInFat,
+ static ImageLoader* instantiateFromFile(const char* path, int fd, const uint8_t firstPages[], size_t firstPagesSize, uint64_t offsetInFat,
uint64_t lenInFat, const struct stat& info, const LinkContext& context);
static ImageLoader* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, const LinkContext& context);
static ImageLoader* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context);
virtual uintptr_t getSlide() const;
virtual const void* getEnd() const;
virtual bool hasCoalescedExports() const;
- virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const;
+ virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const;
virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context,
- const ImageLoader* requestor, bool runResolver) const;
+ const ImageLoader* requestor, bool runResolver, const char*) const;
virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const;
virtual const char* getExportedSymbolName(const Symbol* sym) const;
virtual uint32_t getExportedSymbolCount() const;
virtual bool forceFlat() const;
virtual bool participatesInCoalescing() const;
virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const = 0;
- virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder) = 0;
+ virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned) = 0;
virtual bool incrementCoalIterator(CoalIterator&) = 0;
virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex) = 0;
- virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context) = 0;
+ virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context) = 0;
virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0;
virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()) = 0;
virtual void doTermination(const LinkContext& context);
virtual void registerInterposing();
virtual uint32_t sdkVersion() const;
virtual uint32_t minOSVersion() const;
-
-
- static void printStatistics(unsigned int imageCount, const InitializerTimingList&);
- static uint32_t minOSVersion(const mach_header*);
-
+ virtual const char* libPath(unsigned int) const;
+ virtual bool notifyObjC() const { return fNotifyObjC; }
+
+ static void printStatisticsDetails(unsigned int imageCount, const InitializerTimingList&);
+ static uint32_t minOSVersion(const mach_header*);
+ static uint32_t sdkVersion(const mach_header* mh);
+ static intptr_t computeSlide(const mach_header* mh);
+ static bool findSection(const mach_header* mh, const char* segmentName, const char* sectionName, void** sectAddress, uintptr_t* sectSize);
+ static const dyld_info_command* findDyldInfoLoadCommand(const mach_header* mh);
+ static const char* findClosestSymbol(const mach_header* mh, const void* addr, const void** closestAddr);
+ static bool getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd,
+ uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind);
+ static uintptr_t segPreferredAddress(const mach_header* mh, unsigned segIndex);
+ static uintptr_t bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value,
+ uint8_t type, const char* symbolName,
+ intptr_t addend, const char* inPath, const char* toPath, const char* msg);
+ virtual void rebase(const LinkContext& context, uintptr_t slide) = 0;
+
+
+
protected:
ImageLoaderMachO(const ImageLoaderMachO&);
ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount,
virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const = 0;
virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0;
virtual uint32_t* segmentCommandOffsets() const = 0;
- virtual void rebase(const LinkContext& context) = 0;
- virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const = 0;
+ virtual const ImageLoader::Symbol* findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const = 0;
virtual bool containsSymbol(const void* addr) const = 0;
virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const = 0;
virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const = 0;
#if PREBOUND_IMAGE_SUPPORT
virtual void resetPreboundLazyPointers(const LinkContext& context) = 0;
#endif
-
+
virtual void doGetDependentLibraries(DependentLibraryInfo libs[]);
- virtual LibraryInfo doGetLibraryInfo();
+ virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo);
virtual void getRPaths(const LinkContext& context, std::vector<const char*>&) const;
virtual bool getUUID(uuid_t) const;
virtual void doRebase(const LinkContext& context);
virtual bool usesTwoLevelNameSpace() const;
virtual bool isPrebindable() const;
-
protected:
void destroy();
void doModInitFunctions(const LinkContext& context);
void setupLazyPointerHandler(const LinkContext& context);
void lookupProgramVars(const LinkContext& context) const;
- uintptr_t bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value,
- const ImageLoader* targetImage, uint8_t type, const char* symbolName,
- intptr_t addend, const char* msg);
-
+
void makeTextSegmentWritable(const LinkContext& context, bool writeable);
void preFetchDATA(int fd, uint64_t offsetInFat, const LinkContext& context);
fHasDashInit : 1,
fHasInitializers : 1,
fHasTerminators : 1,
+ fNotifyObjC : 1,
+ fRetainForObjC : 1,
fRegisteredAsRequiresCoalescing : 1; // <rdar://problem/7886402> Loading MH_DYLIB_STUB causing coalescable miscount
-
+
static uint32_t fgSymbolTableBinarySearchs;
- static uint32_t fgSymbolTrieSearchs;
};
if ( 0 != r ) {
// no room here, deallocate what has succeeded so far
for(unsigned int j=0; j < i; ++j) {
- vm_address_t addr = nextAltLoadAddress + regions[j].sfm_address - regions[0].sfm_address;
- vm_size_t size = regions[j].sfm_size ;
+ addr = nextAltLoadAddress + regions[j].sfm_address - regions[0].sfm_address;
+ size = regions[j].sfm_size ;
(void)vm_deallocate(mach_task_self(), addr, size);
}
nextAltLoadAddress += 0x00100000; // skip ahead 1MB and try again
-void ImageLoaderMachOClassic::rebase(const LinkContext& context)
+void ImageLoaderMachOClassic::rebase(const LinkContext& context, uintptr_t slide)
{
CRSetCrashLogMessage2(this->getPath());
- register const uintptr_t slide = this->fSlide;
const uintptr_t relocBase = this->getRelocBase();
// prefetch any LINKEDIT pages needed
}
-const ImageLoader::Symbol* ImageLoaderMachOClassic::findExportedSymbol(const char* name, const ImageLoader** foundIn) const
+const ImageLoader::Symbol* ImageLoaderMachOClassic::findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const
{
const struct macho_nlist* sym = NULL;
if ( fDynamicInfo->tocoff == 0 )
}
uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, const struct macho_nlist* undefinedSymbol,
- bool twoLevel, bool dontCoalesce, const ImageLoader** foundIn)
+ bool twoLevel, bool dontCoalesce, bool runResolver, const ImageLoader** foundIn)
{
++fgTotalBindSymbolsResolved;
const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx];
// if a bundle is loaded privately the above will not find its exports
if ( this->isBundle() && this->hasHiddenExports() ) {
// look in self for needed symbol
- sym = this->findExportedSymbol(symbolName, foundIn);
+ sym = this->findShallowExportedSymbol(symbolName, foundIn);
if ( sym != NULL )
return (*foundIn)->getExportedSymbolAddress(sym, context, this);
}
//dyld::log("resolveUndefined(%s) in %s\n", symbolName, this->getPath());
throw "symbol not found";
}
-
- const Symbol* sym = target->findExportedSymbol(symbolName, true, foundIn);
- if ( sym!= NULL ) {
- return (*foundIn)->getExportedSymbolAddress(sym, context, this);
- }
- else if ( (undefinedSymbol->n_type & N_PEXT) != 0 ) {
+
+ uintptr_t address;
+ if ( target->findExportedSymbolAddress(context, symbolName, this, ord, runResolver, foundIn, &address) )
+ return address;
+
+ if ( (undefinedSymbol->n_type & N_PEXT) != 0 ) {
// don't know why the static linker did not eliminate the internal reference to a private extern definition
*foundIn = this;
return this->getSymbolAddress(undefinedSymbol, context, false);
// range of global symbols. To handle that case we do the coalesing now.
dontCoalesce = false;
}
- symbolAddr = this->resolveUndefined(context, undefinedSymbol, twoLevel, dontCoalesce, &image);
+ symbolAddr = this->resolveUndefined(context, undefinedSymbol, twoLevel, dontCoalesce, false, &image);
lastUndefinedSymbol = undefinedSymbol;
symbolAddrCached = false;
}
if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) {
const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx];
const ImageLoader* image = NULL;
- uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], twoLevel, false, &image);
+ uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], twoLevel, false, true, &image);
symbolAddr = this->bindIndirectSymbol(lazyPointer, sect, symbolName, symbolAddr, image, context);
++fgTotalLazyBindFixups;
return symbolAddr;
-void ImageLoaderMachOClassic::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder)
+void ImageLoaderMachOClassic::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder, unsigned)
{
it.image = this;
it.symbolName = " ";
}
-void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context)
+void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, unsigned targetIndex, const LinkContext& context)
{
// flat_namespace images with classic LINKEDIT do not need late coalescing.
// They still need to be iterated becuase they may implement
if ( reloc->r_pcrel )
type = BIND_TYPE_TEXT_PCREL32;
#endif
- this->bindLocation(context, (uintptr_t)location, value, targetImage, type, symbolName, addend, "weak ");
+ this->bindLocation(context, (uintptr_t)location, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
boundSomething = true;
}
}
// range of global symbols. To handle that case we do the coalesing now.
dontCoalesce = false;
}
- uintptr_t symbolAddr = resolveUndefined(context, sym, twoLevel, dontCoalesce, &image);
+ uintptr_t symbolAddr = resolveUndefined(context, sym, twoLevel, dontCoalesce, false, &image);
// update pointer
symbolAddr = this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, &fStrings[sym->n_un.n_strx], symbolAddr, image, context);
// update stats
const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx];
const ImageLoader* image = NULL;
try {
- uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], this->usesTwoLevelNameSpace(), false, &image);
+ uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], this->usesTwoLevelNameSpace(), false, false, &image);
symbolAddr = this->bindIndirectSymbol((uintptr_t*)entry, sect, symbolName, symbolAddr, image, context);
++fgTotalBindFixups;
uint32_t rel32 = symbolAddr - (((uint32_t)entry)+5);
const size_t pointerCount = sect->size / sizeof(uintptr_t);
uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide);
for (size_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) {
- for(size_t i=0; i < context.dynamicInterposeCount; ++i) {
+ for(size_t j=0; j < context.dynamicInterposeCount; ++j) {
// replace all references to 'replacee' with 'replacement'
- if ( symbolPointers[pointerIndex] == (uintptr_t)context.dynamicInterposeArray[i].replacee ) {
+ if ( symbolPointers[pointerIndex] == (uintptr_t)context.dynamicInterposeArray[j].replacee ) {
if ( context.verboseInterposing ) {
dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n",
- &symbolPointers[pointerIndex], context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath());
+ &symbolPointers[pointerIndex], context.dynamicInterposeArray[j].replacee, context.dynamicInterposeArray[j].replacement, this->getPath());
}
- symbolPointers[pointerIndex] = (uintptr_t)context.dynamicInterposeArray[i].replacement;
+ symbolPointers[pointerIndex] = (uintptr_t)context.dynamicInterposeArray[j].replacement;
}
}
}
virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context);
virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)());
virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const;
- virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder);
+ virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned);
virtual bool incrementCoalIterator(CoalIterator&);
virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex);
- virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context);
+ virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context);
protected:
virtual void doInterpose(const LinkContext& context);
virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const;
virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const;
virtual uint32_t* segmentCommandOffsets() const;
- virtual void rebase(const LinkContext& context);
- virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const;
+ virtual void rebase(const LinkContext& context, uintptr_t slide);
+ virtual const ImageLoader::Symbol* findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const;
virtual bool containsSymbol(const void* addr) const;
virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const;
virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const;
static bool symbolIsWeakReference(const struct macho_nlist* symbol);
static bool symbolIsWeakDefinition(const struct macho_nlist* symbol);
uintptr_t resolveUndefined(const LinkContext& context, const struct macho_nlist* symbol, bool twoLevel,
- bool dontCoalesce, const ImageLoader **foundIn);
+ bool dontCoalesce, bool runResolver, const ImageLoader **foundIn);
uintptr_t getSymbolAddress(const macho_nlist*, const LinkContext& context, bool runResolver) const;
bool isAddrInSection(uintptr_t addr, uint8_t sectionIndex);
void doBindExternalRelocations(const LinkContext& context);
struct macho_routines_command : public routines_command {};
#endif
-
-static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end)
-{
- uint64_t result = 0;
- int bit = 0;
- do {
- if (p == end)
- dyld::throwf("malformed uleb128");
-
- uint64_t slice = *p & 0x7f;
-
- if (bit > 63)
- dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result);
- else {
- result |= (slice << bit);
- bit += 7;
- }
- } while (*p++ & 0x80);
- return result;
-}
-
-
-static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end)
-{
- int64_t result = 0;
- int bit = 0;
- uint8_t byte;
- do {
- if (p == end)
- throw "malformed sleb128";
- byte = *p++;
- result |= (((int64_t)(byte & 0x7f)) << bit);
- bit += 7;
- } while (byte & 0x80);
- // sign extend negative numbers
- if ( (byte & 0x40) != 0 )
- result |= (-1LL) << bit;
- return result;
-}
-
// create image for main executable
ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path,
void ImageLoaderMachOCompressed::throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex,
const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos)
{
- dyld::throwf("malformed rebase opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)",
+ dyld::throwf("malformed rebase opcodes (%ld/%ld): address 0x%08lX is outside of segment %s (0x%08lX -> 0x%08lX)",
(intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex),
segActualLoadAddress(segmentIndex), segmentEndAddress);
}
-void ImageLoaderMachOCompressed::rebase(const LinkContext& context)
+void ImageLoaderMachOCompressed::rebase(const LinkContext& context, uintptr_t slide)
{
CRSetCrashLogMessage2(this->getPath());
- const uintptr_t slide = this->fSlide;
const uint8_t* const start = fLinkEditBase + fDyldInfo->rebase_off;
const uint8_t* const end = &start[fDyldInfo->rebase_size];
const uint8_t* p = start;
uint8_t type = 0;
int segmentIndex = 0;
uintptr_t address = segActualLoadAddress(0);
+ uintptr_t segmentStartAddress = segActualLoadAddress(0);
uintptr_t segmentEndAddress = segActualEndAddress(0);
uintptr_t count;
uintptr_t skip;
if ( segmentIndex >= fSegmentsCount )
dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)",
segmentIndex, fSegmentsCount-1);
- address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end);
+ #if TEXT_RELOC_SUPPORT
+ if ( !segWriteable(segmentIndex) && !segHasRebaseFixUps(segmentIndex) && !segHasBindFixUps(segmentIndex) )
+ #else
+ if ( !segWriteable(segmentIndex) )
+ #endif
+ dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not a writable segment (%s)",
+ segmentIndex, segName(segmentIndex));
+ segmentStartAddress = segActualLoadAddress(segmentIndex);
segmentEndAddress = segActualEndAddress(segmentIndex);
+ address = segmentStartAddress + read_uleb128(p, end);
break;
case REBASE_OPCODE_ADD_ADDR_ULEB:
address += read_uleb128(p, end);
break;
case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
for (int i=0; i < immediate; ++i) {
- if ( address >= segmentEndAddress )
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p);
rebaseAt(context, address, slide, type);
address += sizeof(uintptr_t);
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
count = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- if ( address >= segmentEndAddress )
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p);
rebaseAt(context, address, slide, type);
address += sizeof(uintptr_t);
fgTotalRebaseFixups += count;
break;
case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
- if ( address >= segmentEndAddress )
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p);
rebaseAt(context, address, slide, type);
address += read_uleb128(p, end) + sizeof(uintptr_t);
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- if ( address >= segmentEndAddress )
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p);
rebaseAt(context, address, slide, type);
address += skip + sizeof(uintptr_t);
CRSetCrashLogMessage2(NULL);
}
-//
-// This function is the hotspot of symbol lookup. It was pulled out of findExportedSymbol()
-// to enable it to be re-written in assembler if needed.
-//
-const uint8_t* ImageLoaderMachOCompressed::trieWalk(const uint8_t* start, const uint8_t* end, const char* s)
-{
- const uint8_t* p = start;
- while ( p != NULL ) {
- uintptr_t terminalSize = *p++;
- if ( terminalSize > 127 ) {
- // except for re-export-with-rename, all terminal sizes fit in one byte
- --p;
- terminalSize = read_uleb128(p, end);
- }
- if ( (*s == '\0') && (terminalSize != 0) ) {
- //dyld::log("trieWalk(%p) returning %p\n", start, p);
- return p;
- }
- const uint8_t* children = p + terminalSize;
- //dyld::log("trieWalk(%p) sym=%s, terminalSize=%d, children=%p\n", start, s, terminalSize, children);
- uint8_t childrenRemaining = *children++;
- p = children;
- uintptr_t nodeOffset = 0;
- for (; childrenRemaining > 0; --childrenRemaining) {
- const char* ss = s;
- //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p);
- bool wrongEdge = false;
- // scan whole edge to get to next edge
- // if edge is longer than target symbol name, don't read past end of symbol name
- char c = *p;
- while ( c != '\0' ) {
- if ( !wrongEdge ) {
- if ( c != *ss )
- wrongEdge = true;
- ++ss;
- }
- ++p;
- c = *p;
- }
- if ( wrongEdge ) {
- // advance to next child
- ++p; // skip over zero terminator
- // skip over uleb128 until last byte is found
- while ( (*p & 0x80) != 0 )
- ++p;
- ++p; // skil over last byte of uleb128
- }
- else {
- // the symbol so far matches this edge (child)
- // so advance to the child's node
- ++p;
- nodeOffset = read_uleb128(p, end);
- s = ss;
- //dyld::log("trieWalk() found matching edge advancing to node 0x%x\n", nodeOffset);
- break;
- }
- }
- if ( nodeOffset != 0 )
- p = &start[nodeOffset];
- else
- p = NULL;
- }
- //dyld::log("trieWalk(%p) return NULL\n", start);
- return NULL;
-}
-
-
-const ImageLoader::Symbol* ImageLoaderMachOCompressed::findExportedSymbol(const char* symbol, const ImageLoader** foundIn) const
+const ImageLoader::Symbol* ImageLoaderMachOCompressed::findShallowExportedSymbol(const char* symbol, const ImageLoader** foundIn) const
{
//dyld::log("Compressed::findExportedSymbol(%s) in %s\n", symbol, this->getShortName());
if ( fDyldInfo->export_size == 0 )
if ( (ordinal > 0) && (ordinal <= libraryCount()) ) {
const ImageLoader* reexportedFrom = libImage((unsigned int)ordinal-1);
//dyld::log("Compressed::findExportedSymbol(), %s -> %s/%s\n", symbol, reexportedFrom->getShortName(), importedName);
- return reexportedFrom->findExportedSymbol(importedName, true, foundIn);
+ const char* reExportLibPath = libPath((unsigned int)ordinal-1);
+ return reexportedFrom->findExportedSymbol(importedName, true, reExportLibPath, foundIn);
}
else {
//dyld::throwf("bad mach-o binary, library ordinal (%u) invalid (max %u) for re-exported symbol %s in %s",
// if a bundle is loaded privately the above will not find its exports
if ( this->isBundle() && this->hasHiddenExports() ) {
// look in self for needed symbol
- sym = this->ImageLoaderMachO::findExportedSymbol(symbolName, false, foundIn);
+ sym = this->findShallowExportedSymbol(symbolName, foundIn);
if ( sym != NULL )
return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
}
}
-uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import,
- const char* symbolName, bool runResolver, const ImageLoader** foundIn)
+uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage,
+ const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver,
+ const ImageLoader** foundIn)
{
// two level lookup
- const Symbol* sym = targetImage->findExportedSymbol(symbolName, true, foundIn);
- if ( sym != NULL ) {
- return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
- }
-
+ uintptr_t address;
+ if ( definedInImage->findExportedSymbolAddress(context, symbolName, requestorImage, requestorOrdinalOfDef, runResolver, foundIn, &address) )
+ return address;
+
if ( weak_import ) {
// definition can't be found anywhere, ok because it is weak, just return 0
return 0;
strcpy(versMismatch, msg);
::free((void*)msg);
}
- throwSymbolNotFound(context, symbolName, this->getPath(), versMismatch, targetImage->getPath());
+ throwSymbolNotFound(context, symbolName, this->getPath(), versMismatch, definedInImage->getPath());
}
}
}
else {
- symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, runResolver, targetImage);
+ symbolAddress = resolveTwolevel(context, symbolName, *targetImage, this, (unsigned)libraryOrdinal, weak_import, runResolver, targetImage);
}
}
-
+
// save off lookup results if client wants
if ( last != NULL ) {
last->ordinal = libraryOrdinal;
}
uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName,
- uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg,
+ uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, const char* msg,
LastLookup* last, bool runResolver)
{
const ImageLoader* targetImage;
uintptr_t symbolAddress;
// resolve symbol
- symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last, runResolver);
+ symbolAddress = this->resolve(context, symbolName, symbolFlags, libraryOrdinal, &targetImage, last, runResolver);
// do actual update
- return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg);
+ return this->bindLocation(context, addr, symbolAddress, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, msg);
}
+
void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex,
const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos)
{
- dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)",
+ dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is outside segment %s (0x%08lX -> 0x%08lX)",
(intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex),
segActualLoadAddress(segmentIndex), segmentEndAddress);
}
// don't need to bind
}
else {
-
+ uint64_t t0 = mach_absolute_time();
+
#if TEXT_RELOC_SUPPORT
// if there are __TEXT fixups, temporarily make __TEXT writable
if ( fTextSegmentBinds )
// tell kernel we are done with chunks of LINKEDIT
if ( !context.preFetchDisabled )
this->markFreeLINKEDIT(context);
+
+ uint64_t t1 = mach_absolute_time();
+ ImageLoader::fgTotalRebindCacheTime += (t1-t0);
}
// set up dyld entry points in image
{
try {
uint8_t type = 0;
- int segmentIndex = 0;
+ int segmentIndex = -1;
uintptr_t address = segActualLoadAddress(0);
+ uintptr_t segmentStartAddress = segActualLoadAddress(0);
uintptr_t segmentEndAddress = segActualEndAddress(0);
const char* symbolName = NULL;
uint8_t symboFlags = 0;
+ bool libraryOrdinalSet = false;
long libraryOrdinal = 0;
intptr_t addend = 0;
uintptr_t count;
uintptr_t skip;
+ uintptr_t segOffset;
LastLookup last = { 0, 0, NULL, 0, NULL };
const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off;
const uint8_t* const end = &start[fDyldInfo->bind_size];
break;
case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
libraryOrdinal = immediate;
+ libraryOrdinalSet = true;
break;
case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
libraryOrdinal = read_uleb128(p, end);
+ libraryOrdinalSet = true;
break;
case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
// the special ordinals are negative numbers
int8_t signExtended = BIND_OPCODE_MASK | immediate;
libraryOrdinal = signExtended;
}
+ libraryOrdinalSet = true;
break;
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
symbolName = (char*)p;
break;
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
segmentIndex = immediate;
- if ( segmentIndex >= fSegmentsCount )
- dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)",
+ if ( (segmentIndex >= fSegmentsCount) || (segmentIndex < 0) )
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is out of range (0..%d)",
segmentIndex, fSegmentsCount-1);
- address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end);
+ #if TEXT_RELOC_SUPPORT
+ if ( !segWriteable(segmentIndex) && !segHasRebaseFixUps(segmentIndex) && !segHasBindFixUps(segmentIndex) )
+ #else
+ if ( !segWriteable(segmentIndex) )
+ #endif
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not writable", segmentIndex);
+ segOffset = read_uleb128(p, end);
+ if ( segOffset > segSize(segmentIndex) )
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segmentIndex));
+ segmentStartAddress = segActualLoadAddress(segmentIndex);
+ address = segmentStartAddress + segOffset;
segmentEndAddress = segActualEndAddress(segmentIndex);
break;
case BIND_OPCODE_ADD_ADDR_ULEB:
address += read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- if ( address >= segmentEndAddress )
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
+ if ( symbolName == NULL )
+ dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
+ if ( segmentIndex == -1 )
+ dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
+ if ( !libraryOrdinalSet )
+ dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
(this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
address += sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- if ( address >= segmentEndAddress )
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
+ if ( symbolName == NULL )
+ dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
+ if ( segmentIndex == -1 )
+ dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
+ if ( !libraryOrdinalSet )
+ dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
(this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
address += read_uleb128(p, end) + sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- if ( address >= segmentEndAddress )
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
+ if ( symbolName == NULL )
+ dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
+ if ( segmentIndex == -1 )
+ dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
+ if ( !libraryOrdinalSet )
+ dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
(this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ if ( symbolName == NULL )
+ dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
+ if ( segmentIndex == -1 )
+ dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
count = read_uleb128(p, end);
+ if ( !libraryOrdinalSet )
+ dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- if ( address >= segmentEndAddress )
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
(this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
address += skip + sizeof(intptr_t);
{
try {
uint8_t type = BIND_TYPE_POINTER;
- int segmentIndex = 0;
+ int segmentIndex = -1;
uintptr_t address = segActualLoadAddress(0);
+ uintptr_t segmentStartAddress = segActualLoadAddress(0);
uintptr_t segmentEndAddress = segActualEndAddress(0);
+ uintptr_t segOffset;
const char* symbolName = NULL;
uint8_t symboFlags = 0;
long libraryOrdinal = 0;
break;
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
segmentIndex = immediate;
- if ( segmentIndex >= fSegmentsCount )
- dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)",
+ if ( (segmentIndex >= fSegmentsCount) || (segmentIndex < 0) )
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is out of range (0..%d)",
segmentIndex, fSegmentsCount-1);
- address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end);
+ if ( !segWriteable(segmentIndex) )
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not writable", segmentIndex);
+ segOffset = read_uleb128(p, end);
+ if ( segOffset > segSize(segmentIndex) )
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segmentIndex));
+ segmentStartAddress = segActualLoadAddress(segmentIndex);
segmentEndAddress = segActualEndAddress(segmentIndex);
+ address = segmentStartAddress + segOffset;
break;
case BIND_OPCODE_ADD_ADDR_ULEB:
address += read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- if ( address >= segmentEndAddress )
+ if ( segmentIndex == -1 )
+ dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
+ if ( symbolName == NULL )
+ dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
(this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false);
address += sizeof(intptr_t);
break;
}
+
uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context,
void (*lock)(), void (*unlock)())
{
const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off;
const uint8_t* const end = &start[fDyldInfo->lazy_bind_size];
- if ( lazyBindingInfoOffset > fDyldInfo->lazy_bind_size ) {
- dyld::throwf("fast lazy bind offset out of range (%u, max=%u) in image %s",
- lazyBindingInfoOffset, fDyldInfo->lazy_bind_size, this->getPath());
- }
+ uint8_t segIndex;
+ uintptr_t segOffset;
+ int libraryOrdinal;
+ const char* symbolName;
+ bool doneAfterBind;
+ uintptr_t result;
+ do {
+ if ( ! getLazyBindingInfo(lazyBindingInfoOffset, start, end, &segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind) )
+ dyld::throwf("bad lazy bind info");
+
+ if ( segIndex >= fSegmentsCount )
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)",
+ segIndex, fSegmentsCount-1);
+ if ( segOffset > segSize(segIndex) )
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segIndex));
+ uintptr_t address = segActualLoadAddress(segIndex) + segOffset;
+ result = this->bindAt(context, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true);
+ // <rdar://problem/24140465> Some old apps had multiple lazy symbols bound at once
+ } while (!doneAfterBind && !context.strictMachORequired);
- uint8_t type = BIND_TYPE_POINTER;
- uintptr_t address = 0;
- const char* symbolName = NULL;
- uint8_t symboFlags = 0;
- long libraryOrdinal = 0;
- bool done = false;
- uintptr_t result = 0;
- const uint8_t* p = &start[lazyBindingInfoOffset];
- while ( !done && (p < end) ) {
- uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
- uint8_t opcode = *p & BIND_OPCODE_MASK;
- ++p;
- switch (opcode) {
- case BIND_OPCODE_DONE:
- done = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
- libraryOrdinal = immediate;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
- libraryOrdinal = read_uleb128(p, end);
- break;
- case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
- // the special ordinals are negative numbers
- if ( immediate == 0 )
- libraryOrdinal = 0;
- else {
- int8_t signExtended = BIND_OPCODE_MASK | immediate;
- libraryOrdinal = signExtended;
- }
- break;
- case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
- symbolName = (char*)p;
- symboFlags = immediate;
- while (*p != '\0')
- ++p;
- ++p;
- break;
- case BIND_OPCODE_SET_TYPE_IMM:
- type = immediate;
- break;
- case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- if ( immediate >= fSegmentsCount )
- dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)",
- immediate, fSegmentsCount-1);
- address = segActualLoadAddress(immediate) + read_uleb128(p, end);
- break;
- case BIND_OPCODE_DO_BIND:
-
-
- result = this->bindAt(context, address, type, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true);
- break;
- case BIND_OPCODE_SET_ADDEND_SLEB:
- case BIND_OPCODE_ADD_ADDR_ULEB:
- case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
- default:
- dyld::throwf("bad lazy bind opcode %d", *p);
- }
- }
-
if ( !this->usesTwoLevelNameSpace() ) {
// release dyld global lock
if ( unlock != NULL )
return result;
}
-void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder)
+void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder, unsigned)
{
it.image = this;
it.symbolName = " ";
const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size;
uintptr_t count;
uintptr_t skip;
+ uintptr_t segOffset;
while ( p < end ) {
uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
uint8_t opcode = *p & BIND_OPCODE_MASK;
if ( immediate >= fSegmentsCount )
dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)",
immediate, fSegmentsCount-1);
- it.address = segActualLoadAddress(immediate) + read_uleb128(p, end);
+ #if __arm__
+ // <rdar://problem/23138428> iOS app compatibility
+ if ( !segWriteable(immediate) && it.image->isPositionIndependentExecutable() )
+ #elif TEXT_RELOC_SUPPORT
+ // <rdar://problem/23479396&23590867> i386 OS X app compatibility
+ if ( !segWriteable(immediate) && !segHasRebaseFixUps(immediate) && !segHasBindFixUps(immediate)
+ && (!it.image->isExecutable() || it.image->isPositionIndependentExecutable()) )
+ #else
+ if ( !segWriteable(immediate) )
+ #endif
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB targets segment %s which is not writable", segName(immediate));
+ segOffset = read_uleb128(p, end);
+ if ( segOffset > segSize(immediate) )
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(immediate));
+ it.address = segActualLoadAddress(immediate) + segOffset;
break;
case BIND_OPCODE_ADD_ADDR_ULEB:
it.address += read_uleb128(p, end);
{
//dyld::log("looking for %s in %s\n", it.symbolName, this->getPath());
const ImageLoader* foundIn = NULL;
- const ImageLoader::Symbol* sym = this->findExportedSymbol(it.symbolName, &foundIn);
+ const ImageLoader::Symbol* sym = this->findShallowExportedSymbol(it.symbolName, &foundIn);
if ( sym != NULL ) {
//dyld::log("sym=%p, foundIn=%p\n", sym, foundIn);
return foundIn->getExportedSymbolAddress(sym, context, this);
}
-void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context)
+void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, unsigned targetIndex, const LinkContext& context)
{
// <rdar://problem/6570879> weak binding done too early with inserted libraries
if ( this->getState() < dyld_image_state_bound )
intptr_t addend = it.addend;
uintptr_t count;
uintptr_t skip;
+ uintptr_t segOffset;
bool done = false;
bool boundSomething = false;
while ( !done && (p < end) ) {
if ( immediate >= fSegmentsCount )
dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)",
immediate, fSegmentsCount-1);
- address = segActualLoadAddress(immediate) + read_uleb128(p, end);
+ #if __arm__
+ // <rdar://problem/23138428> iOS app compatibility
+ if ( !segWriteable(immediate) && it.image->isPositionIndependentExecutable() )
+ #elif TEXT_RELOC_SUPPORT
+ // <rdar://problem/23479396&23590867> i386 OS X app compatibility
+ if ( !segWriteable(immediate) && !segHasRebaseFixUps(immediate) && !segHasBindFixUps(immediate)
+ && (!it.image->isExecutable() || it.image->isPositionIndependentExecutable()) )
+ #else
+ if ( !segWriteable(immediate) )
+ #endif
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB targets segment %s which is not writable", segName(immediate));
+ segOffset = read_uleb128(p, end);
+ if ( segOffset > segSize(immediate) )
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(immediate));
+ address = segActualLoadAddress(immediate) + segOffset;
break;
case BIND_OPCODE_ADD_ADDR_ULEB:
address += read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak ");
+ bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
boundSomething = true;
address += sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak ");
+ bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
boundSomething = true;
address += read_uleb128(p, end) + sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak ");
+ bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
boundSomething = true;
address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
break;
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak ");
+ bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
boundSomething = true;
address += skip + sizeof(intptr_t);
}
uintptr_t* fixupLocation = (uintptr_t*)addr;
uintptr_t curValue = *fixupLocation;
uintptr_t newValue = interposedAddress(context, curValue, this);
- if ( newValue != curValue)
+ if ( newValue != curValue) {
*fixupLocation = newValue;
+ }
}
return 0;
}
eachLazyBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt);
}
-
const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const
{
- // called by dladdr()
- // only works with compressed LINKEDIT if classic symbol table is also present
- const macho_nlist* symbolTable = NULL;
- const char* symbolTableStrings = NULL;
- const dysymtab_command* dynSymbolTable = NULL;
- const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
- const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
- const struct load_command* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd) {
- case LC_SYMTAB:
- {
- const struct symtab_command* symtab = (struct symtab_command*)cmd;
- symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff];
- symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]);
- }
- break;
- case LC_DYSYMTAB:
- dynSymbolTable = (struct dysymtab_command*)cmd;
- break;
- }
- cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
- }
- // no symbol table => no lookup by address
- if ( (symbolTable == NULL) || (dynSymbolTable == NULL) )
- return NULL;
-
- uintptr_t targetAddress = (uintptr_t)addr - fSlide;
- const struct macho_nlist* bestSymbol = NULL;
- // first walk all global symbols
- const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym];
- const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym];
- for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) {
- if ( (s->n_type & N_TYPE) == N_SECT ) {
- if ( bestSymbol == NULL ) {
- if ( s->n_value <= targetAddress )
- bestSymbol = s;
- }
- else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) {
- bestSymbol = s;
- }
- }
- }
- // next walk all local symbols
- const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym];
- const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym];
- for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) {
- if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
- if ( bestSymbol == NULL ) {
- if ( s->n_value <= targetAddress )
- bestSymbol = s;
- }
- else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) {
- bestSymbol = s;
- }
- }
- }
- if ( bestSymbol != NULL ) {
-#if __arm__
- if (bestSymbol->n_desc & N_ARM_THUMB_DEF)
- *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide);
- else
- *closestAddr = (void*)(bestSymbol->n_value + fSlide);
-#else
- *closestAddr = (void*)(bestSymbol->n_value + fSlide);
-#endif
- return &symbolTableStrings[bestSymbol->n_un.n_strx];
- }
- return NULL;
+ return ImageLoaderMachO::findClosestSymbol((mach_header*)fMachOData, addr, closestAddr);
}
void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context)
{
#if __arm__ || __x86_64__
- // find stubs and lazy pointer sections
+ // find stubs and indirect symbol table
const struct macho_section* stubsSection = NULL;
- const struct macho_section* lazyPointerSection = NULL;
const dysymtab_command* dynSymbolTable = NULL;
const macho_header* mh = (macho_header*)fMachOData;
const uint32_t cmd_count = mh->ncmds;
const uint8_t type = sect->flags & SECTION_TYPE;
if ( type == S_SYMBOL_STUBS )
stubsSection = sect;
- else if ( type == S_LAZY_SYMBOL_POINTERS )
- lazyPointerSection = sect;
}
}
else if ( cmd->cmd == LC_DYSYMTAB ) {
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
-
- // sanity check
if ( dynSymbolTable == NULL )
return;
- if ( (stubsSection == NULL) || (lazyPointerSection == NULL) )
- return;
- const uint32_t stubsCount = stubsSection->size / stubsSection->reserved2;
- const uint32_t lazyPointersCount = lazyPointerSection->size / sizeof(void*);
- if ( stubsCount != lazyPointersCount )
+ const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff];
+ if ( stubsSection == NULL )
return;
+ const uint32_t stubsSize = stubsSection->reserved2;
+ const uint32_t stubsCount = (uint32_t)(stubsSection->size / stubsSize);
const uint32_t stubsIndirectTableOffset = stubsSection->reserved1;
- const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1;
if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms )
return;
- if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms )
- return;
-
- // walk stubs and lazy pointers
- const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff];
- void** const lazyPointersStartAddr = (void**)(lazyPointerSection->addr + this->fSlide);
- uint8_t* const stubsStartAddr = (uint8_t*)(stubsSection->addr + this->fSlide);
- uint8_t* stub = stubsStartAddr;
- void** lpa = lazyPointersStartAddr;
- for(uint32_t i=0; i < stubsCount; ++i, stub += stubsSection->reserved2, ++lpa) {
- // sanity check symbol index of stub and lazy pointer match
- if ( indirectTable[stubsIndirectTableOffset+i] != indirectTable[lazyPointersIndirectTableOffset+i] )
- continue;
- this->updateAlternateLazyPointer(stub, lpa, context);
+ uint8_t* const stubsAddr = (uint8_t*)(stubsSection->addr + this->fSlide);
+
+ // for each lazy pointer section
+ cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if (cmd->cmd == LC_SEGMENT_COMMAND) {
+ const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+ const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
+ const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const struct macho_section* lazyPointerSection=sectionsStart; lazyPointerSection < sectionsEnd; ++lazyPointerSection) {
+ const uint8_t type = lazyPointerSection->flags & SECTION_TYPE;
+ if ( type != S_LAZY_SYMBOL_POINTERS )
+ continue;
+ const uint32_t lazyPointersCount = (uint32_t)(lazyPointerSection->size / sizeof(void*));
+ const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1;
+ if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms )
+ continue;
+ void** const lazyPointersAddr = (void**)(lazyPointerSection->addr + this->fSlide);
+ // for each lazy pointer
+ for(uint32_t lpIndex=0; lpIndex < lazyPointersCount; ++lpIndex) {
+ const uint32_t lpSymbolIndex = indirectTable[lazyPointersIndirectTableOffset+lpIndex];
+ // find matching stub and validate it uses this lazy pointer
+ for(uint32_t stubIndex=0; stubIndex < stubsCount; ++stubIndex) {
+ if ( indirectTable[stubsIndirectTableOffset+stubIndex] == lpSymbolIndex ) {
+ this->updateAlternateLazyPointer(stubsAddr+stubIndex*stubsSize, &lazyPointersAddr[lpIndex], context);
+ break;
+ }
+ }
+ }
+
+ }
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
-
+
#endif
}
virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context);
virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)());
virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const;
- virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder);
+ virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned);
virtual bool incrementCoalIterator(CoalIterator&);
virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex);
- virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context);
+ virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context);
-
protected:
virtual void doInterpose(const LinkContext& context);
virtual void dynamicInterpose(const LinkContext& context);
virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const { return false; }
virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const { return false; }
virtual uint32_t* segmentCommandOffsets() const;
- virtual void rebase(const LinkContext& context);
- virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const;
+ virtual void rebase(const LinkContext& context, uintptr_t slide);
+ virtual const ImageLoader::Symbol* findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const;
virtual bool containsSymbol(const void* addr) const;
virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const;
virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const;
uintptr_t resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver,
const ImageLoader** foundIn);
uintptr_t resolveCoalesced(const LinkContext& context, const char* symbolName, const ImageLoader** foundIn);
- uintptr_t resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import,
- const char* symbolName, bool runResolver, const ImageLoader** foundIn);
+ uintptr_t resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage,
+ const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver,
+ const ImageLoader** foundInn);
uintptr_t interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*,
uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver);
uintptr_t dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*,
uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver);
- static const uint8_t* trieWalk(const uint8_t* start, const uint8_t* end, const char* s);
void updateOptimizedLazyPointers(const LinkContext& context);
void updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context);
void registerEncryption(const struct encryption_info_command* encryptCmd, const LinkContext& context);
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#if __arm__ || __arm64__
+ #include <System/sys/mman.h>
+#else
+ #include <sys/mman.h>
+#endif
+#include <string.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <mach/mach.h>
+#include <mach/thread_status.h>
+#include <mach-o/loader.h>
+#include <libkern/OSAtomic.h>
+
+#include "ImageLoaderMegaDylib.h"
+#include "ImageLoaderMachO.h"
+#include "mach-o/dyld_images.h"
+#include "dyldLibSystemInterface.h"
+#include "dyld.h"
+
+// from dyld_gdb.cpp
+extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]);
+
+
+#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
+ #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
+#endif
+
+// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
+#if __LP64__
+ #define RELOC_SIZE 3
+ #define LC_SEGMENT_COMMAND LC_SEGMENT_64
+ #define LC_ROUTINES_COMMAND LC_ROUTINES_64
+ struct macho_segment_command : public segment_command_64 {};
+ struct macho_section : public section_64 {};
+ struct macho_routines_command : public routines_command_64 {};
+#else
+ #define RELOC_SIZE 2
+ #define LC_SEGMENT_COMMAND LC_SEGMENT
+ #define LC_ROUTINES_COMMAND LC_ROUTINES
+ struct macho_segment_command : public segment_command {};
+ struct macho_section : public section {};
+ struct macho_routines_command : public routines_command {};
+#endif
+
+
+
+#if SUPPORT_ACCELERATE_TABLES
+
+
+ImageLoaderMegaDylib* ImageLoaderMegaDylib::makeImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const LinkContext& context)
+{
+ return new ImageLoaderMegaDylib(header, slide, context);
+}
+
+struct DATAdyld {
+ void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper
+ void* dyldFuncLookup; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup
+ ProgramVars vars;
+};
+
+
+
+
+ImageLoaderMegaDylib::ImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const LinkContext& context)
+ : ImageLoader("dyld shared cache", 0), _header(header), _linkEditBias(NULL), _slide(slide),
+ _lockArray(NULL), _lockArrayInUseCount(0)
+{
+ pthread_mutex_init(&_lockArrayGuard, NULL);
+ const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((uint8_t*)_header + _header->mappingOffset);
+ const dyld_cache_mapping_info* lastMapping = &mappings[_header->mappingCount-1];
+ const dyld_cache_accelerator_info* accHeader = (dyld_cache_accelerator_info*)(_header->accelerateInfoAddr + slide);
+ for (const dyld_cache_mapping_info* m=mappings; m <= lastMapping; ++m) {
+ if ( m->initProt == VM_PROT_READ ) {
+ _linkEditBias = (uint8_t*)(m->address - m->fileOffset) + _slide;
+ }
+ }
+
+ _endOfCacheInMemory = (void*)(lastMapping->address + lastMapping->size + slide);
+ _images = (const dyld_cache_image_info*)((uint8_t*)_header + _header->imagesOffset);
+ _imageExtras = (dyld_cache_image_info_extra*)((char*)accHeader + accHeader->imagesExtrasOffset);
+ _initializers = (dyld_cache_accelerator_initializer*)((char*)accHeader + accHeader->initializersOffset);
+ _reExportsArray = (uint16_t*)((char*)accHeader + accHeader->reExportListOffset);
+ _dependenciesArray = (uint16_t*)((char*)accHeader + accHeader->depListOffset);
+ _bottomUpArray = (uint16_t*)((char*)accHeader + accHeader->bottomUpListOffset);
+ _rangeTable = (dyld_cache_range_entry*)((char*)accHeader + accHeader->rangeTableOffset);
+ _rangeTableCount = accHeader->rangeTableCount;
+ _imageCount = accHeader->imageExtrasCount;
+ _stateFlags = (uint8_t*)calloc(_imageCount, 1);
+ _initializerCount = accHeader->initializersCount;
+ _dylibsTrieStart = (uint8_t*)accHeader + accHeader->dylibTrieOffset;
+ _dylibsTrieEnd = _dylibsTrieStart + accHeader->dylibTrieSize;
+ _imageTextInfo = (dyld_cache_image_text_info*)((uint8_t*)_header + _header->imagesTextOffset);
+ DATAdyld* dyldSection = (DATAdyld*)(accHeader->dyldSectionAddr + slide);
+ dyldSection->dyldLazyBinder = NULL; // not used by libdyld.dylib
+ dyldSection->dyldFuncLookup = (void*)&_dyld_func_lookup;
+ dyldSection->vars.mh = context.mainExecutable->machHeader();
+ context.setNewProgramVars(dyldSection->vars);
+}
+
+
+void ImageLoaderMegaDylib::unreachable() const
+{
+ abort();
+}
+
+ImageLoaderMegaDylib::~ImageLoaderMegaDylib()
+{
+}
+
+const char* ImageLoaderMegaDylib::getInstallPath() const {
+ unreachable();
+}
+
+const macho_header* ImageLoaderMegaDylib::getIndexedMachHeader(unsigned index) const
+{
+ if ( index > _header->imagesCount )
+ dyld::throwf("cache image index out of range (%u), max=%u", index, _header->imagesCount - 1);
+ return (const macho_header*)(_images[index].address + _slide);
+}
+
+const char* ImageLoaderMegaDylib::getIndexedPath(unsigned index) const
+{
+ if ( index > _header->imagesCount )
+ dyld::throwf("cache image index out of range (%u), max=%u", index, _header->imagesCount - 1);
+ return (char*)_header + _images[index].pathFileOffset;
+}
+
+const char* ImageLoaderMegaDylib::getIndexedShortName(unsigned index) const
+{
+ const char* path = getIndexedPath(index);
+ const char* lastSlash = strrchr(path, '/');
+ if ( lastSlash == NULL )
+ return path;
+ else
+ return lastSlash+1;
+}
+
+void ImageLoaderMegaDylib::getDylibUUID(unsigned int index, uuid_t uuid) const
+{
+ if ( index > _header->imagesCount )
+ dyld::throwf("cache image index out of range (%u), max=%u", index, _header->imagesCount - 1);
+ memcpy(uuid, _imageTextInfo[index].uuid, 16);
+}
+
+void ImageLoaderMegaDylib::printSegments(const macho_header* mh) const
+{
+ const uint32_t cmd_count = mh->ncmds;
+ const struct load_command* const cmds = (struct load_command*)((uint8_t*)mh + sizeof(macho_header));
+ const struct load_command* cmd = cmds;
+ const macho_segment_command* seg;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND:
+ seg = (macho_segment_command*)cmd;
+ dyld::log("%18s at 0x%08lX->0x%08lX\n", seg->segname, (long)(seg->vmaddr + _slide), (long)(seg->vmaddr + seg->vmsize + _slide));
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+}
+
+bool ImageLoaderMegaDylib::hasDylib(const char* path, unsigned* index) const
+{
+ const uint8_t* imageNode = ImageLoader::trieWalk(_dylibsTrieStart, _dylibsTrieEnd, path);
+ if ( imageNode == NULL ) {
+ #if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // not all symlinks are recorded as aliases in accelerator tables
+ if ( (strncmp(path, "/usr/lib/", 9) == 0) || (strncmp(path, "/System/Library/", 16) == 0) ) {
+ char resolvedPath[PATH_MAX];
+ if ( realpath(path, resolvedPath) != NULL ) {
+ imageNode = ImageLoader::trieWalk(_dylibsTrieStart, _dylibsTrieEnd, resolvedPath);
+ }
+ }
+ if ( imageNode == NULL )
+ return false;
+ #else
+ return false;
+ #endif
+ }
+ *index = (unsigned)read_uleb128(imageNode, _dylibsTrieEnd);
+ return true;
+}
+
+bool ImageLoaderMegaDylib::addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index)
+{
+ // quick out of bounds check
+#if __x86_64__
+ if ( (uintptr_t)address < 0x7FFF70000000LL )
+ return false;
+#else
+ if ( address < (void*)_header )
+ return false;
+#endif
+ if ( address > _endOfCacheInMemory )
+ return false;
+
+ uint64_t unslidAddress = (uint64_t)address - _slide;
+ // linear search for now
+ const dyld_cache_range_entry* rangeTableEnd = &_rangeTable[_rangeTableCount];
+ for (const dyld_cache_range_entry* r = _rangeTable; r < rangeTableEnd; ++r) {
+ if ( (r->startAddress <= unslidAddress) && (unslidAddress < r->startAddress+r->size) ) {
+ *index = r->imageIndex;
+ *mh = (mach_header*)getIndexedMachHeader(r->imageIndex);
+ *path = getIndexedPath(r->imageIndex);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool ImageLoaderMegaDylib::findUnwindSections(const void* address, dyld_unwind_sections* info)
+{
+ const char* path;
+ unsigned index;
+ if ( addressInCache(address, &info->mh, &path, &index) ) {
+ info->dwarf_section = NULL;
+ info->dwarf_section_length = 0;
+ ImageLoaderMachO::findSection(info->mh, "__TEXT", "__eh_frame", (void**)&info->dwarf_section, &info->dwarf_section_length);
+
+ info->compact_unwind_section = NULL;
+ info->compact_unwind_section_length = 0;
+ ImageLoaderMachO::findSection(info->mh, "__TEXT", "__unwind_info", (void**)&info->compact_unwind_section, &info->compact_unwind_section_length);
+
+ return true;
+ }
+ return false;
+}
+
+
+unsigned ImageLoaderMegaDylib::findImageIndex(const LinkContext& context, const char* path) const
+{
+ unsigned index;
+ if ( hasDylib(path, &index) )
+ return index;
+
+ // <rdar://problem/26934069> Somehow we found the dylib in the cache, but it is not this literal string, try simple expansions of @rpath
+ if ( strncmp(path, "@rpath/", 7) == 0 ) {
+ std::vector<const char*> rpathsFromMainExecutable;
+ context.mainExecutable->getRPaths(context, rpathsFromMainExecutable);
+ const char* trailingPath = &path[7];
+ for (const char* anRPath : rpathsFromMainExecutable) {
+ if ( anRPath[0] != '/' )
+ continue;
+ char possiblePath[strlen(anRPath) + strlen(trailingPath)+2];
+ strcpy(possiblePath, anRPath);
+ if ( possiblePath[strlen(possiblePath)-1] != '/' )
+ strcat(possiblePath, "/");
+ strcat(possiblePath, trailingPath);
+ if ( hasDylib(possiblePath, &index) ) {
+ return index;
+ }
+ }
+ }
+ dyld::throwf("no cache image with name (%s)", path);
+}
+
+void ImageLoaderMegaDylib::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder, unsigned imageIndex)
+{
+ it.image = this;
+ it.symbolName = " ";
+ it.loadOrder = loadOrder;
+ it.weakSymbol = false;
+ it.symbolMatches = false;
+ it.done = false;
+ it.curIndex = 0;
+ it.endIndex = _imageExtras[imageIndex].weakBindingsSize;
+ it.address = 0;
+ it.type = 0;
+ it.addend = 0;
+ it.imageIndex = imageIndex;
+}
+
+bool ImageLoaderMegaDylib::incrementCoalIterator(CoalIterator& it)
+{
+ if ( it.done )
+ return false;
+
+ if ( _imageExtras[it.imageIndex].weakBindingsSize == 0 ) {
+ /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info
+ it.done = true;
+ it.symbolName = "~~~";
+ return true;
+ }
+ const uint8_t* start = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide);
+ const uint8_t* end = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide + _imageExtras[it.imageIndex].weakBindingsSize);
+ const uint8_t* p = start + it.curIndex;
+ uintptr_t count;
+ uintptr_t skip;
+ uint64_t segOffset;
+ unsigned segIndex;
+ const mach_header* mh;
+ while ( p < end ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ it.done = true;
+ it.curIndex = p - start;
+ it.symbolName = "~~~"; // sorts to end
+ return true;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ it.symbolName = (char*)p;
+ it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0);
+ it.symbolMatches = false;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ it.curIndex = p - start;
+ return false;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ it.type = immediate;
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ it.addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segOffset = read_uleb128(p, end);
+ mh = (mach_header*)getIndexedMachHeader((unsigned)it.imageIndex);
+ if ( uintptr_t segPrefAddress = ImageLoaderMachO::segPreferredAddress(mh, segIndex) )
+ it.address = segPrefAddress + segOffset + _slide;
+ else
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large", segIndex);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ it.address += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ it.address += sizeof(intptr_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ it.address += read_uleb128(p, end) + sizeof(intptr_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ it.address += skip + sizeof(intptr_t);
+ }
+ break;
+ default:
+ dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath());
+ }
+ }
+ /// hmmm, BIND_OPCODE_DONE is missing...
+ it.done = true;
+ it.symbolName = "~~~";
+ //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath());
+ return true;
+}
+
+uintptr_t ImageLoaderMegaDylib::getAddressCoalIterator(CoalIterator& it, const LinkContext& context)
+{
+ //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath());
+ uintptr_t address;
+ if ( findInChainedTries(context, it.symbolName, (unsigned)it.imageIndex, NULL, false, &address) ) {
+ return address;
+ }
+ return 0;
+}
+
+void ImageLoaderMegaDylib::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, unsigned targetIndex, const LinkContext& context)
+{
+
+ const uint8_t* start = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide);
+ const uint8_t* end = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide + _imageExtras[it.imageIndex].weakBindingsSize);
+ const uint8_t* p = start + it.curIndex;
+
+ uint8_t type = it.type;
+ uintptr_t address = it.address;
+ const char* symbolName = it.symbolName;
+ intptr_t addend = it.addend;
+ uint64_t segOffset;
+ unsigned segIndex;
+ const mach_header* mh;
+ uintptr_t count;
+ uintptr_t skip;
+ bool done = false;
+ bool boundSomething = false;
+ const char* targetImagePath = targetImage ? targetImage->getIndexedPath(targetIndex) : NULL;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segOffset = read_uleb128(p, end);
+ mh = (mach_header*)getIndexedMachHeader((unsigned)it.imageIndex);
+ if ( uintptr_t segPrefAddress = ImageLoaderMachO::segPreferredAddress(mh, segIndex) )
+ address = segPrefAddress + segOffset + _slide;
+ else
+ dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large", segIndex);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ address += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+ boundSomething = true;
+ address += sizeof(intptr_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+ boundSomething = true;
+ address += read_uleb128(p, end) + sizeof(intptr_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+ boundSomething = true;
+ address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+ boundSomething = true;
+ address += skip + sizeof(intptr_t);
+ }
+ break;
+ default:
+ dyld::throwf("bad bind opcode %d in weak binding info", *p);
+ }
+ }
+ // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading.
+ if ( boundSomething && (targetImage != this) )
+ context.addDynamicReference(this, targetImage);
+}
+
+
+void ImageLoaderMegaDylib::appendImagesNeedingCoalescing(ImageLoader* images[], unsigned imageIndexes[], unsigned& count)
+{
+ for (unsigned i=0; i < _imageCount; ++i) {
+ uint16_t index = _bottomUpArray[i];
+ if ( _stateFlags[index] == kStateUnused )
+ continue;
+ if ( _imageExtras[index].weakBindingsSize == 0 )
+ continue;
+ images[count] = this;
+ imageIndexes[count] = index;
+ ++count;
+ }
+}
+
+
+bool ImageLoaderMegaDylib::weakSymbolsBound(unsigned index)
+{
+ return ( _stateFlags[index] >= kStateFlagWeakBound );
+}
+
+void ImageLoaderMegaDylib::setWeakSymbolsBound(unsigned index)
+{
+ if ( _stateFlags[index] == kStateFlagBound )
+ _stateFlags[index] = kStateFlagWeakBound;
+}
+
+
+void ImageLoaderMegaDylib::recursiveMarkLoaded(const LinkContext& context, unsigned imageIndex)
+{
+ if ( _stateFlags[imageIndex] != kStateUnused )
+ return;
+
+ const macho_header* mh = getIndexedMachHeader(imageIndex);
+ const char* path = getIndexedPath(imageIndex);
+
+ if ( context.verboseLoading )
+ dyld::log("dyld: loaded: %s\n", path);
+ if ( context.verboseMapping ) {
+ dyld::log("dyld: Using shared cached for %s\n", path);
+ printSegments(mh);
+ }
+
+ // change state to "loaded" before recursing to break cycles
+ _stateFlags[imageIndex] = kStateLoaded;
+ ++fgImagesUsedFromSharedCache;
+
+ dyld_image_info debuggerInfo;
+ debuggerInfo.imageLoadAddress = (mach_header*)mh;
+ debuggerInfo.imageFilePath = path;
+ debuggerInfo.imageFileModDate = 0;
+ addImagesToAllImages(1, &debuggerInfo);
+
+ if ( _imageExtras[imageIndex].weakBindingsSize != 0 ) {
+ ++fgImagesRequiringCoalescing;
+ ++fgImagesHasWeakDefinitions;
+ }
+
+ unsigned startArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex;
+ for (int i=startArrayIndex; _dependenciesArray[i] != 0xFFFF; ++i) {
+ unsigned subDep = (_dependenciesArray[i] & 0x7FFF); // mask off upward bit
+ recursiveMarkLoaded(context, subDep);
+ }
+}
+
+void ImageLoaderMegaDylib::recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath)
+{
+ unsigned index = findImageIndex(context, loadPath);
+ recursiveMarkLoaded(context, index);
+}
+
+unsigned int ImageLoaderMegaDylib::recursiveUpdateDepth(unsigned int maxDepth)
+{
+ setDepth(maxDepth);
+ return maxDepth;
+}
+
+
+const ImageLoader::Symbol* ImageLoaderMegaDylib::findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const
+{
+ unsigned index;
+ if ( !hasDylib(thisPath, &index) )
+ return NULL;
+ const uint8_t* exportNode;
+ const uint8_t* exportTrieEnd;
+ unsigned foundinIndex;
+ // <rdar://problem/22068598> always search re-exports
+ // the point of searchReExports was to break cycles in dylibs, we don't have cycles in cache, so ok to search deep
+ searchReExports = true;
+ if ( searchReExports ) {
+ if ( !exportTrieHasNodeRecursive(name, index, &exportNode, &exportTrieEnd, &foundinIndex) )
+ return NULL;
+ }
+ else {
+ if ( !exportTrieHasNode(name, index, &exportNode, &exportTrieEnd) )
+ return NULL;
+ }
+ *foundIn = this;
+ return (ImageLoader::Symbol*)exportNode;
+}
+
+bool ImageLoaderMegaDylib::exportTrieHasNode(const char* symbolName, unsigned index,
+ const uint8_t** exportNode, const uint8_t** exportTrieEnd) const
+{
+ const uint8_t* start = (uint8_t*)(_imageExtras[index].exportsTrieAddr + _slide);
+ const uint32_t size = _imageExtras[index].exportsTrieSize;
+ if ( size == 0 )
+ return false;
+ const uint8_t* end = start + size;
+ const uint8_t* node = ImageLoader::trieWalk(start, end, symbolName);
+ if ( node == NULL )
+ return false;
+ *exportNode = node;
+ *exportTrieEnd = end;
+ return true;
+}
+
+bool ImageLoaderMegaDylib::exportTrieHasNodeRecursive(const char* symbolName, unsigned index,
+ const uint8_t** exportNode, const uint8_t** exportTrieEnd,
+ unsigned* foundinIndex) const
+{
+ // look in trie for image index
+ if ( exportTrieHasNode(symbolName, index, exportNode, exportTrieEnd) ) {
+ *foundinIndex = index;
+ return true;
+ }
+ // recursively look in all re-exported tries
+ unsigned startArrayIndex = _imageExtras[index].reExportsStartArrayIndex;
+ for (int i=startArrayIndex; _reExportsArray[i] != 0xFFFF; ++i) {
+ unsigned reExIndex = _reExportsArray[i];
+ if ( exportTrieHasNodeRecursive(symbolName, reExIndex, exportNode, exportTrieEnd, foundinIndex) )
+ return true;
+ }
+ return false;
+}
+
+bool ImageLoaderMegaDylib::findExportedSymbolAddress(const LinkContext& context, const char* symbolName,
+ const ImageLoader* requestorImage, int requestorOrdinalOfDef,
+ bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const
+{
+ const char* definedImagePath = requestorImage->libPath(requestorOrdinalOfDef-1);
+ unsigned index = findImageIndex(context, definedImagePath);
+ *foundIn = this;
+ return findInChainedTries(context, symbolName, index, requestorImage, runResolver, address);
+}
+
+uintptr_t ImageLoaderMegaDylib::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context,
+ const ImageLoader* requestor, bool runResolver, const char* symbolName) const
+{
+ // scan for with trie contains this node
+ const uint8_t* exportTrieEnd = NULL;
+ unsigned imageIndex = 0xFFFF;
+ const macho_header* mh = NULL;
+ uint64_t unslidTrieNode = ((uintptr_t)sym) - _slide;
+ for (unsigned i=0; i < _imageCount; ++i) {
+ uint64_t start = _imageExtras[i].exportsTrieAddr;
+ uint64_t end = _imageExtras[i].exportsTrieAddr + _imageExtras[i].exportsTrieSize;
+ if ( (start < unslidTrieNode) && (unslidTrieNode < end) ) {
+ exportTrieEnd = (uint8_t*)(end + _slide);
+ imageIndex = i;
+ mh = (macho_header*)(_images[imageIndex].address + _slide);
+ break;
+ }
+ }
+
+ if ( mh == NULL )
+ dyld::throwf("getExportedSymbolAddress(Symbol=%p) not in a cache trie", sym);
+
+ const uint8_t* exportNode = (const uint8_t*)sym;
+ uintptr_t address;
+ processExportNode(context, symbolName ? symbolName : "unknown", imageIndex, exportNode, exportTrieEnd, requestor, runResolver, &address);
+ return address;
+}
+
+void ImageLoaderMegaDylib::processExportNode(const LinkContext& context, const char* symbolName, unsigned definedImageIndex,
+ const uint8_t* exportNode, const uint8_t* exportTrieEnd,
+ const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const
+{
+ const macho_header* mh = getIndexedMachHeader(definedImageIndex);
+ uintptr_t flags = read_uleb128(exportNode, exportTrieEnd);
+ uintptr_t rawAddress;
+ switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
+ case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
+ if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
+ // this node has a stub and resolver, run the resolver to get target address
+ uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh; // skip over stub
+ // <rdar://problem/10657737> interposing dylibs have the stub address as their replacee
+ uintptr_t interposedStub = interposedAddress(context, stub, requestorImage);
+ if ( interposedStub != stub ) {
+ *address = interposedStub;
+ return;
+ }
+ // stub was not interposed, so run resolver
+ typedef uintptr_t (*ResolverProc)(void);
+ ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh);
+ *address = (*resolver)();
+ if ( context.verboseBind )
+ dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, *address);
+ return;
+ }
+ if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ // re-export from another dylib, lookup there
+ const uintptr_t ordinal = read_uleb128(exportNode, exportTrieEnd);
+ const char* importedName = (char*)exportNode;
+ if ( importedName[0] == '\0' )
+ importedName = symbolName;
+ unsigned startArrayIndex = _imageExtras[definedImageIndex].dependentsStartArrayIndex;
+ unsigned reExImageIndex = _dependenciesArray[startArrayIndex + ordinal-1] & 0x7FFF;
+ if ( findInChainedTries(context, importedName, reExImageIndex, requestorImage, runResolver, address) )
+ return;
+ dyld::throwf("re-exported symbol '%s' not found for image %s expected re-exported in %s, node=%p",
+ symbolName, getIndexedShortName(definedImageIndex), getIndexedShortName(reExImageIndex), exportNode);
+ }
+ rawAddress = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh;
+ *address = interposedAddress(context, rawAddress, requestorImage);
+ return;
+ case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
+ if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
+ dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, exportNode);
+ *address = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh;
+ return;
+ case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
+ if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
+ dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, exportNode);
+ *address = read_uleb128(exportNode, exportTrieEnd);
+ return;
+ default:
+ dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, exportNode);
+ }
+ dyld::throwf("unsupported exported symbol node=%p", exportNode);
+}
+
+bool ImageLoaderMegaDylib::findInChainedTries(const LinkContext& context, const char* symbolName, unsigned definedImageIndex,
+ const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const
+{
+ //dyld::log("findInChainedTries(sym=%s, index=%u, path=%s)\n", symbolName, definedImageIndex, getIndexedPath(definedImageIndex));
+ const uint8_t* exportNode;
+ const uint8_t* exportTrieEnd;
+ unsigned foundinIndex;
+ if ( !exportTrieHasNodeRecursive(symbolName, definedImageIndex, &exportNode, &exportTrieEnd, &foundinIndex) )
+ return false;
+
+ processExportNode(context, symbolName, foundinIndex, exportNode, exportTrieEnd, requestorImage, runResolver, address);
+ return true;
+}
+
+
+bool ImageLoaderMegaDylib::findInChainedTriesAndDependentsExcept(const LinkContext& context, const char* symbolName, unsigned imageIndex,
+ const ImageLoader* requestorImage, bool runResolver, bool alreadyVisited[], uintptr_t* address) const
+{
+ //dyld::log("findInChainedTriesAndDependentsExcept(sym=%s, index=%u, path=%s)\n", symbolName, imageIndex, getIndexedPath(imageIndex));
+ if ( alreadyVisited[imageIndex] )
+ return false;
+ alreadyVisited[imageIndex] = true;
+
+ if ( findInChainedTries(context, symbolName, imageIndex, requestorImage, runResolver, address) )
+ return true;
+
+ unsigned startArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex;
+ for (int i=startArrayIndex; _dependenciesArray[i] != 0xFFFF; ++i) {
+ // ignore upward links
+ if ( (_dependenciesArray[i] & 0x8000) == 0 ) {
+ unsigned depIndex = _dependenciesArray[i] & 0x7FFF;
+ if ( _stateFlags[depIndex] != kStateFlagInitialized )
+ continue;
+ if ( findInChainedTriesAndDependentsExcept(context, symbolName, depIndex, requestorImage, runResolver, alreadyVisited, address) )
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ImageLoaderMegaDylib::findInChainedTriesAndDependents(const LinkContext& context, const char* symbolName, unsigned definedImageIndex,
+ const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const
+{
+ //dyld::log("findInChainedTriesAndDependents(sym=%s, index=%u, path=%s)\n", symbolName, definedImageIndex, getIndexedPath(definedImageIndex));
+ if ( findInChainedTries(context, symbolName, definedImageIndex, requestorImage, runResolver, address) )
+ return true;
+
+ bool alreadyVisited[_header->imagesCount];
+ bzero(alreadyVisited, sizeof(alreadyVisited));
+ return findInChainedTriesAndDependentsExcept(context, symbolName, definedImageIndex, requestorImage, runResolver, alreadyVisited, address);
+}
+
+
+bool ImageLoaderMegaDylib::flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image)
+{
+ // check export trie of all in-use images
+ for (unsigned i=0; i < _imageCount ; ++i) {
+ uint16_t imageIndex = _bottomUpArray[i];
+ if ( _stateFlags[imageIndex] == kStateUnused )
+ continue;
+ if ( onlyInCoalesced && (_imageExtras[imageIndex].weakBindingsSize == 0) )
+ continue;
+ const uint8_t* exportNode;
+ const uint8_t* exportTrieEnd;
+ if ( exportTrieHasNode(name, imageIndex, &exportNode, &exportTrieEnd) ) {
+ *sym = (Symbol*)exportNode;
+ *image = this;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void ImageLoaderMegaDylib::markAllbound(const LinkContext& context)
+{
+ for (unsigned i=0; i < _imageCount; ++i) {
+ uint16_t imageIndex = _bottomUpArray[i];
+ if ( _stateFlags[imageIndex] == kStateLoaded ) {
+ _stateFlags[imageIndex] = kStateFlagBound;
+ context.notifySingleFromCache(dyld_image_state_bound, (mach_header*)getIndexedMachHeader(imageIndex), getIndexedPath(imageIndex));
+ }
+ }
+}
+
+
+void ImageLoaderMegaDylib::recursiveSpinLockAcquire(unsigned int imageIndex, mach_port_t thisThread)
+{
+ pthread_mutex_lock(&_lockArrayGuard);
+ if ( _lockArray == NULL )
+ _lockArray = (recursive_lock*)calloc(_imageCount, sizeof(recursive_lock));
+ _lockArrayInUseCount++;
+ pthread_mutex_unlock(&_lockArrayGuard);
+
+ recursive_lock* imagesLock = &_lockArray[imageIndex];
+ while ( !OSAtomicCompareAndSwap32Barrier(0, thisThread, (int*)&imagesLock->thread) ) {
+ if ( imagesLock->thread == thisThread )
+ break;
+ }
+ imagesLock->count++;
+}
+
+void ImageLoaderMegaDylib::recursiveSpinLockRelease(unsigned int imageIndex, mach_port_t thisThread)
+{
+ recursive_lock* imagesLock = &_lockArray[imageIndex];
+ if ( --imagesLock->count == 0 )
+ imagesLock->thread = 0;
+
+ pthread_mutex_lock(&_lockArrayGuard);
+ _lockArrayInUseCount--;
+ if ( _lockArrayInUseCount == 0 ) {
+ free((void*)_lockArray);
+ _lockArray = NULL;
+ }
+ pthread_mutex_unlock(&_lockArrayGuard);
+}
+
+
+void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, unsigned int imageIndex,
+ InitializerTimingList& timingInfo)
+{
+ // Don't do any locking until libSystem.dylib is initialized, so we can malloc() the lock array
+ bool useLock = dyld::gProcessInfo->libSystemInitialized;
+ if ( useLock )
+ recursiveSpinLockAcquire(imageIndex, thisThread);
+
+ // only run initializers if currently in bound state
+ if ( (_stateFlags[imageIndex] == kStateFlagBound) || (_stateFlags[imageIndex] == kStateFlagWeakBound) ) {
+
+ // Each image in cache has its own lock. We only set the state to Initialized if we hold the lock for the image.
+ _stateFlags[imageIndex] = kStateFlagInitialized;
+
+ // first recursively init all dependents
+ unsigned startArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex;
+ for (int i=startArrayIndex; _dependenciesArray[i] != 0xFFFF; ++i) {
+ unsigned subDepIndex = _dependenciesArray[i];
+ // ignore upward links
+ if ( (subDepIndex & 0x8000) == 0 )
+ recursiveInitialization(context, thisThread, subDepIndex, timingInfo);
+ }
+
+ // notify objc about this image
+ context.notifySingleFromCache(dyld_image_state_dependents_initialized, (mach_header*)getIndexedMachHeader(imageIndex), getIndexedPath(imageIndex));
+
+ // run all initializers for imageIndex
+ const dyld_cache_accelerator_initializer* pInitStart = _initializers;
+ const dyld_cache_accelerator_initializer* pInitEnd = &pInitStart[_initializerCount];
+ bool ranSomeInitializers = false;
+ uint64_t t1 = mach_absolute_time();
+ for (const dyld_cache_accelerator_initializer* p=pInitStart; p < pInitEnd; ++p) {
+ if ( p->imageIndex == imageIndex ) {
+ Initializer func = (Initializer)(p->functionOffset + (uintptr_t)_header);
+ if ( context.verboseInit )
+ dyld::log("dyld: calling initializer function %p in %s\n", func, getIndexedPath(imageIndex));
+ bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
+ func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
+ bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
+ ranSomeInitializers = true;
+ if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
+ // now safe to use malloc() and other calls in libSystem.dylib
+ dyld::gProcessInfo->libSystemInitialized = true;
+ }
+ }
+ }
+ if ( ranSomeInitializers ) {
+ uint64_t t2 = mach_absolute_time();
+ const char* shortName = strrchr(getIndexedPath(imageIndex), '/');
+ if ( shortName == NULL )
+ shortName = getIndexedPath(imageIndex);
+ else
+ ++shortName;
+ timingInfo.images[timingInfo.count].shortName = shortName;
+ timingInfo.images[timingInfo.count].initTime = (t2-t1);
+ timingInfo.count++;
+ }
+ }
+
+ // only unlock if this frame locked (note: libSystemInitialized changes after libSystem's initializer is run)
+ if ( useLock )
+ recursiveSpinLockRelease(imageIndex, thisThread);
+}
+
+
+void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, const char* pathToInitialize,
+ InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
+{
+ unsigned imageIndex;
+ if ( hasDylib(pathToInitialize, &imageIndex) ) {
+ this->recursiveInitialization(context, thisThread, imageIndex, timingInfo);
+ }
+}
+
+void ImageLoaderMegaDylib::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload)
+{
+ markAllbound(context);
+}
+
+uint8_t ImageLoaderMegaDylib::dyldStateToCacheState(dyld_image_states state) {
+ switch (state) {
+ case dyld_image_state_mapped:
+ case dyld_image_state_dependents_mapped:
+ return kStateLoaded;
+ case dyld_image_state_bound:
+ return kStateFlagBound;
+ case dyld_image_state_initialized:
+ return kStateFlagInitialized;
+ case dyld_image_state_rebased:
+ case dyld_image_state_dependents_initialized:
+ case dyld_image_state_terminated:
+ return kStateUnused;
+ }
+ return kStateUnused;
+}
+
+void ImageLoaderMegaDylib::recursiveApplyInterposing(const LinkContext& context)
+{
+ if ( context.verboseInterposing )
+ dyld::log("dyld: interposing %lu tuples onto shared cache\n", fgInterposingTuples.size());
+
+
+}
+
+unsigned ImageLoaderMegaDylib::appendImagesToNotify(dyld_image_states state, bool orLater, dyld_image_info* infos)
+{
+ uint8_t targetCacheState = dyldStateToCacheState(state);
+ if ( targetCacheState == kStateUnused )
+ return 0;
+
+ unsigned usedCount = 0;
+ for (int i=_imageCount-1; i > 0; --i) {
+ uint16_t index = _bottomUpArray[i];
+ uint8_t imageState = _stateFlags[index];
+ if ( imageState == kStateFlagWeakBound )
+ imageState = kStateFlagBound;
+ if ( (imageState == targetCacheState) || (orLater && (imageState > targetCacheState)) ) {
+ infos[usedCount].imageLoadAddress = (mach_header*)getIndexedMachHeader(index);
+ infos[usedCount].imageFilePath = getIndexedPath(index);
+ infos[usedCount].imageFileModDate = 0;
+ ++usedCount;
+ }
+ }
+ return usedCount;
+}
+
+
+bool ImageLoaderMegaDylib::dlopenFromCache(const LinkContext& context, const char* path, int mode, void** handle)
+{
+ unsigned imageIndex;
+ if ( !hasDylib(path, &imageIndex) ) {
+ return false;
+ }
+
+ // RTLD_NOLOAD means return handle if already loaded, but don't now load it
+ if ( mode & RTLD_NOLOAD ) {
+ dyld::gLibSystemHelpers->releaseGlobalDyldLock();
+ if ( _stateFlags[imageIndex] == kStateUnused ) {
+ *handle = NULL;
+ return true;
+ }
+ }
+ else {
+ this->recursiveMarkLoaded(context, imageIndex);
+ context.notifyBatch(dyld_image_state_dependents_mapped, false);
+ this->markAllbound(context);
+ context.notifyBatch(dyld_image_state_bound, false);
+
+ this->weakBind(context);
+
+ // <rdar://problem/25069046> Release dyld global lock before running initializers in dlopen() with customer cache
+ dyld::gLibSystemHelpers->releaseGlobalDyldLock();
+
+ InitializerTimingList timingInfo[_initializerCount];
+ timingInfo[0].count = 0;
+ mach_port_t thisThread = mach_thread_self();
+ this->recursiveInitialization(context, thisThread, imageIndex, timingInfo[0]);
+ mach_port_deallocate(mach_task_self(), thisThread);
+ context.notifyBatch(dyld_image_state_initialized, false);
+ }
+
+ *handle = makeCacheHandle(imageIndex, mode);
+ return true;
+}
+
+bool ImageLoaderMegaDylib::makeCacheHandle(const LinkContext& context, unsigned cacheIndex, int mode, void** result)
+{
+ if ( cacheIndex >= _imageCount )
+ return false;
+
+ *result = makeCacheHandle(cacheIndex, mode);
+ return true;
+}
+
+void* ImageLoaderMegaDylib::makeCacheHandle(unsigned index, int mode)
+{
+ uint8_t flags = ((mode & RTLD_FIRST) ? 1 : 0);
+
+#if __LP64__
+ return (void*)(uintptr_t)( 0xFFEEDDCC00000000LL | (index << 8) | flags);
+#else
+ return (void*)(uintptr_t)( 0xF8000000 | (index << 8) | flags);
+#endif
+}
+
+bool ImageLoaderMegaDylib::isCacheHandle(void* handle, unsigned* index, uint8_t* flags)
+{
+#if __LP64__
+ if ( (((uintptr_t)handle) >> 32) == 0xFFEEDDCC ) {
+ if ( index )
+ *index = (((uintptr_t)handle) >> 8) & 0xFFFF;
+ if ( flags )
+ *flags = ((uintptr_t)handle) & 0xFF;
+ return true;
+ }
+#else
+ if ( (((uintptr_t)handle) >> 24) == 0xF8 ) {
+ if ( index )
+ *index = (((uintptr_t)handle) >> 8) & 0xFFFF;
+ if ( flags )
+ *flags = ((uintptr_t)handle) & 0xFF;
+ return true;
+ }
+#endif
+ return false;
+}
+
+
+void* ImageLoaderMegaDylib::dlsymFromCache(const LinkContext& context, void* handle, const char* symbolName, unsigned imageIndex)
+{
+ unsigned indexInHandle;
+ uint8_t flags;
+ uintptr_t symAddress;
+ if ( handle == RTLD_SELF ) {
+ if ( findInChainedTriesAndDependents(context, symbolName, imageIndex, NULL, true, &symAddress) )
+ return (void*)symAddress;
+ }
+ else if ( handle == RTLD_NEXT ) {
+ // FIXME: really need to not look in imageIndex, but look in others.
+ if ( findInChainedTriesAndDependents(context, symbolName, imageIndex, NULL, true, &symAddress) )
+ return (void*)symAddress;
+ }
+ else if ( isCacheHandle(handle, &indexInHandle, &flags) ) {
+ bool searchOnlyFirst = (flags & 1); // RTLD_FIRST
+ // normal dlsym(handle,) semantics is that the handle is just the first place to search. RTLD_FIRST disables that
+ if ( searchOnlyFirst ) {
+ if ( findInChainedTries(context, symbolName, indexInHandle, NULL, true, &symAddress) )
+ return (void*)symAddress;
+ }
+ else {
+ if ( findInChainedTriesAndDependents(context, symbolName, indexInHandle, NULL, true, &symAddress) )
+ return (void*)symAddress;
+ }
+ }
+
+ return NULL;
+}
+
+bool ImageLoaderMegaDylib::dladdrFromCache(const void* address, Dl_info* info)
+{
+ const mach_header* mh;
+ unsigned index;
+ if ( !addressInCache(address, &mh, &info->dli_fname, &index) )
+ return false;
+
+ info->dli_fbase = (void*)mh;
+ if ( address == mh ) {
+ // special case lookup of header
+ info->dli_sname = "__dso_handle";
+ info->dli_saddr = info->dli_fbase;
+ return true;
+ }
+
+ // find closest symbol in the image
+ info->dli_sname = ImageLoaderMachO::findClosestSymbol(mh, address, (const void**)&info->dli_saddr);
+
+ // never return the mach_header symbol
+ if ( info->dli_saddr == info->dli_fbase ) {
+ info->dli_sname = NULL;
+ info->dli_saddr = NULL;
+ return true;
+ }
+
+ // strip off leading underscore
+ if ( info->dli_sname != NULL ) {
+ if ( info->dli_sname[0] == '_' )
+ info->dli_sname = info->dli_sname +1;
+ }
+ return true;
+}
+
+
+uintptr_t ImageLoaderMegaDylib::bindLazy(uintptr_t lazyBindingInfoOffset, const LinkContext& context, const mach_header* mh, unsigned imageIndex)
+{
+ const dyld_info_command* dyldInfoCmd = ImageLoaderMachO::findDyldInfoLoadCommand(mh);
+ if ( dyldInfoCmd == NULL )
+ return 0;
+
+ const uint8_t* const lazyInfoStart = &_linkEditBias[dyldInfoCmd->lazy_bind_off];
+ const uint8_t* const lazyInfoEnd = &lazyInfoStart[dyldInfoCmd->lazy_bind_size];
+ uint32_t lbOffset = (uint32_t)lazyBindingInfoOffset;
+ uint8_t segIndex;
+ uintptr_t segOffset;
+ int libraryOrdinal;
+ const char* symbolName;
+ bool doneAfterBind;
+ if ( ImageLoaderMachO::getLazyBindingInfo(lbOffset, lazyInfoStart, lazyInfoEnd, &segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind) ) {
+ //const char* thisPath = getIndexedPath(imageIndex);
+ //dyld::log("%s needs symbol '%s' from ordinal=%d\n", thisPath, symbolName, libraryOrdinal);
+ unsigned startDepArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex;
+ unsigned targetIndex;
+ if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF )
+ targetIndex = imageIndex;
+ else
+ targetIndex = _dependenciesArray[startDepArrayIndex+libraryOrdinal-1] & 0x7FFF;
+ //const char* targetPath = getIndexedPath(targetIndex);
+ //dyld::log("%s needs symbol '%s' from %s\n", thisPath, symbolName, targetPath);
+ uintptr_t targetAddress;
+ if ( findInChainedTries(context, symbolName, targetIndex, this, true, &targetAddress) ) {
+ if ( uintptr_t segPrefAddress = ImageLoaderMachO::segPreferredAddress(mh, segIndex) ) {
+ uintptr_t* lp = (uintptr_t*)(segPrefAddress + segOffset + _slide);
+ //dyld::log(" storing 0x%0lX to lp %p\n", targetAddress, lp);
+ *lp = targetAddress;
+ return targetAddress;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+#endif // SUPPORT_ACCELERATE_TABLES
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#ifndef __IMAGELOADER_MEGADYLIB__
+#define __IMAGELOADER_MEGADYLIB__
+
+#include <stdint.h>
+#include <pthread.h>
+
+#include "ImageLoaderMachO.h"
+#include "dyld_cache_format.h"
+
+
+//
+// ImageLoaderMegaDylib is the concrete subclass of ImageLoader which represents
+// all dylibs in the shared cache.
+//
+class ImageLoaderMegaDylib : public ImageLoader {
+public:
+ static ImageLoaderMegaDylib* makeImageLoaderMegaDylib(const dyld_cache_header*, long slide, const LinkContext&);
+
+
+ virtual ~ImageLoaderMegaDylib();
+
+ void appendImagesNeedingCoalescing(ImageLoader* images[], unsigned imageIndex[], unsigned& count);
+ virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned imageIndex);
+ virtual bool incrementCoalIterator(CoalIterator&);
+ virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex);
+ virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned imageIndex, const LinkContext& context);
+
+ virtual const char* getIndexedPath(unsigned index) const;
+ virtual const char* getIndexedShortName(unsigned) const;
+ virtual const char* getInstallPath() const;
+ virtual bool inSharedCache() const { return true; }
+ virtual bool containsSymbol(const void* addr) const { unreachable(); }
+ virtual void* getThreadPC() const { unreachable(); }
+ virtual void* getMain() const { unreachable(); }
+ virtual const struct mach_header* machHeader() const { unreachable(); }
+ virtual uintptr_t getSlide() const { return _slide; }
+ virtual const void* getEnd() const { unreachable(); }
+ virtual bool hasCoalescedExports() const { unreachable(); }
+ virtual bool findExportedSymbolAddress(const LinkContext& context, const char* symbolName,
+ const ImageLoader* requestorImage, int requestorOrdinalOfDef,
+ bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const;
+ virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const;
+ virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context,
+ const ImageLoader* requestor, bool runResolver, const char* symbolName) const;
+ virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const { unreachable(); }
+ virtual const char* getExportedSymbolName(const Symbol* sym) const { unreachable(); }
+ virtual uint32_t getExportedSymbolCount() const { unreachable(); }
+ virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const { unreachable(); }
+
+ virtual uint32_t getImportedSymbolCount() const { unreachable(); }
+ virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const { unreachable(); }
+ virtual ReferenceFlags getImportedSymbolInfo(const Symbol* sym) const { unreachable(); }
+ virtual const char* getImportedSymbolName(const Symbol* sym) const { unreachable(); }
+ virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const { unreachable(); }
+ virtual bool isBundle() const { return false; }
+ virtual bool isDylib() const { return true; }
+ virtual bool isExecutable() const { unreachable(); }
+ virtual bool isPositionIndependentExecutable() const { unreachable(); }
+ virtual bool forceFlat() const { unreachable(); }
+ virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) { unreachable(); }
+ virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context,
+ void (*lock)(), void (*unlock)()) { unreachable(); }
+ virtual void doTermination(const LinkContext& context) { unreachable(); }
+ virtual bool needsInitialization() { unreachable(); }
+ virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) { unreachable(); }
+ virtual void getUnwindInfo(dyld_unwind_sections* info) { unreachable(); }
+ virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) { unreachable(); }
+ virtual bool isPrebindable() const { unreachable(); }
+ virtual bool usablePrebinding(const LinkContext& context) const { unreachable(); }
+ virtual void getRPaths(const LinkContext& context, std::vector<const char*>&) const { }
+ virtual bool participatesInCoalescing() const { unreachable(); }
+ virtual bool getUUID(uuid_t) const { unreachable(); }
+ virtual void dynamicInterpose(const LinkContext& context) { unreachable(); }
+ void addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count) { unreachable(); }
+ virtual unsigned int segmentCount() const { unreachable(); }
+ virtual const char* segName(unsigned int) const { unreachable(); }
+ virtual uintptr_t segSize(unsigned int) const { unreachable(); }
+ virtual uintptr_t segFileSize(unsigned int) const { unreachable(); }
+ virtual bool segHasTrailingZeroFill(unsigned int) { unreachable(); }
+ virtual uintptr_t segFileOffset(unsigned int) const { unreachable(); }
+ virtual bool segReadable(unsigned int) const { unreachable(); }
+ virtual bool segWriteable(unsigned int) const { unreachable(); }
+ virtual bool segExecutable(unsigned int) const { unreachable(); }
+ virtual bool segUnaccessible(unsigned int) const { unreachable(); }
+ virtual bool segHasPreferredLoadAddress(unsigned int) const { unreachable(); }
+ virtual uintptr_t segPreferredLoadAddress(unsigned int) const { unreachable(); }
+ virtual uintptr_t segActualLoadAddress(unsigned int) const { unreachable(); }
+ virtual uintptr_t segActualEndAddress(unsigned int) const { unreachable(); }
+
+
+ // info from LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS
+ virtual uint32_t sdkVersion() const { unreachable(); }
+ virtual uint32_t minOSVersion() const { unreachable(); }
+
+ // if the image contains interposing functions, register them
+ virtual void registerInterposing() { unreachable(); }
+
+ virtual ImageLoader* libImage(unsigned int) const { unreachable(); }
+ virtual bool libReExported(unsigned int) const { unreachable(); }
+ virtual bool libIsUpward(unsigned int) const { unreachable(); }
+ virtual void setLibImage(unsigned int, ImageLoader*, bool, bool) { unreachable(); }
+ virtual const char* libPath(unsigned int) const { unreachable(); }
+
+ unsigned appendImagesToNotify(dyld_image_states state, bool orLater, dyld_image_info* infos);
+ const char* notify(dyld_image_states state, bool orLater, dyld_image_state_change_handler);
+ bool dlopenFromCache(const LinkContext& context, const char* path, int mode, void** handle);
+ bool makeCacheHandle(const LinkContext& context, unsigned cacheIndex, int mode, void** result);
+ void* dlsymFromCache(const LinkContext& context, void* handle, const char* symName, unsigned index);
+ bool isCacheHandle(void* handle, unsigned* index, uint8_t* flags);
+ bool hasDylib(const char* path, unsigned* index) const;
+ bool addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index);
+ bool findUnwindSections(const void* addr, dyld_unwind_sections* info);
+ bool dladdrFromCache(const void* address, Dl_info* info);
+ uintptr_t bindLazy(uintptr_t lazyBindingInfoOffset, const LinkContext& context, const mach_header* mh, unsigned index);
+ bool flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image);
+ void getDylibUUID(unsigned int index, uuid_t) const;
+
+protected:
+ virtual void setDyldInfo(const dyld_info_command* dyldInfo) { unreachable(); }
+ virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) { unreachable(); }
+ virtual uint32_t* segmentCommandOffsets() const { unreachable(); }
+ virtual void rebase(const LinkContext& context, uintptr_t slide) { unreachable(); }
+ virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const { unreachable(); }
+ virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const { unreachable(); }
+ virtual const char* exportedSymbolName(const Symbol* symbol) const { unreachable(); }
+ virtual unsigned int exportedSymbolCount() const { unreachable(); }
+ virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const { unreachable(); }
+ virtual unsigned int importedSymbolCount() const { unreachable(); }
+ virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const { unreachable(); }
+ virtual const char* importedSymbolName(const Symbol* symbol) const { unreachable(); }
+#if PREBOUND_IMAGE_SUPPORT
+ virtual void resetPreboundLazyPointers(const LinkContext& context) { unreachable(); }
+#endif
+
+ virtual void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath);
+ virtual unsigned recursiveUpdateDepth(unsigned int maxDepth);
+ virtual void recursiveRebase(const LinkContext& context) { }
+ virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload);
+ virtual void recursiveApplyInterposing(const LinkContext& context);
+ virtual void recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs) { }
+ virtual void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
+ ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&);
+
+ virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) { unreachable(); }
+ virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo) { return requestorInfo; }
+ virtual void doRebase(const LinkContext& context) { unreachable(); }
+ virtual void doBind(const LinkContext& context, bool forceLazysBound) { unreachable(); }
+ virtual void doBindJustLazies(const LinkContext& context) { unreachable(); }
+ virtual void doGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs) { unreachable(); }
+ virtual void doInterpose(const LinkContext& context) { unreachable(); }
+ virtual bool doInitialization(const LinkContext& context) { unreachable(); }
+ virtual bool needsTermination() { unreachable(); }
+ virtual bool segmentsMustSlideTogether() const { unreachable(); }
+ virtual bool segmentsCanSlide() const { unreachable(); }
+ virtual void setSlide(intptr_t slide) { unreachable(); }
+ bool allDependentLibrariesAsWhenPreBound() const { unreachable(); }
+ virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const { return false; }
+ virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const { return false; }
+ virtual bool weakSymbolsBound(unsigned index);
+ virtual void setWeakSymbolsBound(unsigned index);
+
+private:
+ ImageLoaderMegaDylib(const dyld_cache_header*, long slide, const LinkContext&);
+
+ const macho_header* getIndexedMachHeader(unsigned index) const;
+ const uint8_t* getIndexedTrie(unsigned index, uint32_t& trieSize) const;
+ unsigned findImageIndex(const LinkContext& context, const char* path) const;
+ void recursiveMarkLoaded(const LinkContext& context, unsigned imageIndex);
+ void markAllbound(const LinkContext& context);
+ bool findInChainedTries(const LinkContext& context, const char* symbolName, unsigned definedImageIndex,
+ const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const;
+ bool findInChainedTriesAndDependents(const LinkContext& context, const char* symbolName, unsigned definedImageIndex,
+ const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const;
+ bool findInChainedTriesAndDependentsExcept(const LinkContext& context, const char* symbolName, unsigned imageIndex,
+ const ImageLoader* requestorImage, bool runResolver, bool alreadyVisited[], uintptr_t* address) const;
+ bool exportTrieHasNodeRecursive(const char* symbolName, unsigned index,
+ const uint8_t** exportNode, const uint8_t** exportTrieEnd,
+ unsigned* foundinIndex) const;
+ bool exportTrieHasNode(const char* symbolName, unsigned index,
+ const uint8_t** exportNode, const uint8_t** exportTrieEnd) const;
+
+ void initAllLoaded(const LinkContext& context, InitializerTimingList& timingInfo);
+ void printSegments(const macho_header* mh) const;
+ void* makeCacheHandle(unsigned index, int mode);
+ uint8_t flagsFromCacheHandle(void* handle);
+ void processExportNode(const LinkContext& context, const char* symbolName, unsigned definedImageIndex,
+ const uint8_t* exportNode, const uint8_t* exportTrieEnd,
+ const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const;
+ static uint8_t dyldStateToCacheState(dyld_image_states state);
+ void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, unsigned int imageIndex,
+ InitializerTimingList& timingInfo);
+ void recursiveSpinLockAcquire(unsigned int imageIndex, mach_port_t thisThread);
+ void recursiveSpinLockRelease(unsigned int imageIndex, mach_port_t thisThread);
+
+ __attribute__((noreturn))
+ void unreachable() const;
+
+ enum { kStateUnused=0, kStateLoaded=1, kStateFlagBound=2, kStateFlagWeakBound=3, kStateFlagInitialized=4 };
+
+
+ const dyld_cache_header* _header;
+ const void* _endOfCacheInMemory;
+ const uint8_t* _linkEditBias;
+ const dyld_cache_image_info* _images;
+ const dyld_cache_image_info_extra* _imageExtras;
+ long _slide;
+ const dyld_cache_accelerator_initializer* _initializers;
+ const dyld_cache_range_entry* _rangeTable;
+ const uint16_t* _reExportsArray;
+ const uint16_t* _dependenciesArray;
+ const uint16_t* _bottomUpArray;
+ const uint8_t* _dylibsTrieStart;
+ const uint8_t* _dylibsTrieEnd;
+ const dyld_cache_image_text_info* _imageTextInfo;
+ uint8_t* _stateFlags;
+ uint32_t _imageCount;
+ uint32_t _initializerCount;
+ uint32_t _rangeTableCount;
+ pthread_mutex_t _lockArrayGuard;
+ ImageLoader::recursive_lock* _lockArray;
+ unsigned int _lockArrayInUseCount;
+};
+
+
+
+#endif // __IMAGELOADER_MEGADYLIB__
+
+
+
+
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
+#include <pthread.h>
+#include <libproc.h>
#include <sys/param.h>
#include <mach/mach_time.h> // mach_absolute_time()
#include <mach/mach_init.h>
#include <System/sys/csr.h>
#include <_simple.h>
#include <os/lock_private.h>
+#include <System/machine/cpu_capabilities.h>
+#include <System/sys/reason.h>
+#include <kern/kcdata.h>
+#include <sandbox.h>
+#include <sandbox/private.h>
+#include <array>
#ifndef CPU_SUBTYPE_ARM_V5TEJ
#define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7)
#include "ImageLoader.h"
#include "ImageLoaderMachO.h"
#include "dyldLibSystemInterface.h"
-#include "dyldSyscallInterface.h"
#if DYLD_SHARED_CACHE_SUPPORT
#include "dyld_cache_format.h"
#endif
+#include "dyld_process_info_internal.h"
#include <coreSymbolicationDyldSupport.h>
#if TARGET_IPHONE_SIMULATOR
extern "C" void xcoresymbolication_load_notifier(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header);
#define coresymbolication_unload_notifier(c, t, p, h) xcoresymbolication_unload_notifier(c, t, p, h)
#endif
+#if SUPPORT_ACCELERATE_TABLES
+ #include "ImageLoaderMegaDylib.h"
+#endif
+
+#if TARGET_IPHONE_SIMULATOR
+ extern "C" void* gSyscallHelpers;
+#else
+ #include "dyldSyscallInterface.h"
+#endif
+
+
// not libc header for send() syscall interface
extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t);
#if __LP64__
#define LC_SEGMENT_COMMAND LC_SEGMENT_64
#define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT
+ #define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO
#define macho_segment_command segment_command_64
#define macho_section section_64
#else
#define LC_SEGMENT_COMMAND LC_SEGMENT
#define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64
+ #define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO_64
#define macho_segment_command segment_command
#define macho_section section
#endif
/* implemented in dyld_gdb.cpp */
+extern void resetAllImages();
extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]);
extern void removeImageFromAllImages(const mach_header* mh);
-extern void setAlImageInfosHalt(const char* message, uintptr_t flags);
extern void addNonSharedCacheImageUUID(const dyld_uuid_info& info);
extern const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]);
+extern size_t allImagesCount();
// magic so CrashReporter logs message
extern "C" {
char error_string[1024];
}
-// implemented in dyldStartup.s for CrashReporter
-extern "C" void dyld_fatal_error(const char* errString) __attribute__((noreturn));
// magic linker symbol for start of dyld binary
extern "C" const macho_header __dso_handle;
const char* const * LD_LIBRARY_PATH; // for unix conformance
const char* const * DYLD_VERSIONED_LIBRARY_PATH;
const char* const * DYLD_VERSIONED_FRAMEWORK_PATH;
- bool DYLD_PRINT_LIBRARIES;
bool DYLD_PRINT_LIBRARIES_POST_LAUNCH;
bool DYLD_BIND_AT_LAUNCH;
bool DYLD_PRINT_STATISTICS;
+ bool DYLD_PRINT_STATISTICS_DETAILS;
bool DYLD_PRINT_OPTS;
bool DYLD_PRINT_ENV;
bool DYLD_DISABLE_DOFS;
// DYLD_PRINT_WARNINGS ==> gLinkContext.verboseWarnings
// DYLD_PRINT_RPATHS ==> gLinkContext.verboseRPaths
// DYLD_PRINT_INTERPOSING ==> gLinkContext.verboseInterposing
+ // DYLD_PRINT_LIBRARIES ==> gLinkContext.verboseLoading
};
typedef std::vector<dyld_image_state_change_handler> StateHandlers;
-enum RestrictedReason { restrictedNot, restrictedBySetGUid, restrictedBySegment, restrictedByEntitlements };
+enum EnvVarMode { envNone, envPrintOnly, envAll };
// all global state
static const char* sExecPath = NULL;
static cpu_type_t sHostCPU;
static cpu_subtype_t sHostCPUsubtype;
#endif
-static ImageLoader* sMainExecutable = NULL;
-static bool sProcessIsRestricted = false;
-static bool sProcessRequiresLibraryValidation = false;
-static RestrictedReason sRestrictedReason = restrictedNot;
+static ImageLoaderMachO* sMainExecutable = NULL;
+static EnvVarMode sEnvMode = envNone;
static size_t sInsertedDylibCount = 0;
static std::vector<ImageLoader*> sAllImages;
static std::vector<ImageLoader*> sImageRoots;
static const char* sFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL };
static const char* sLibraryFallbackPaths[] = { "/usr/local/lib", "/usr/lib", NULL };
#endif
+static const char* sRestrictedFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL };
+static const char* sRestrictedLibraryFallbackPaths[] = { "/usr/lib", NULL };
static UndefinedHandler sUndefinedHandler = NULL;
static ImageLoader* sBundleBeingLoaded = NULL; // hack until OFI is reworked
#if DYLD_SHARED_CACHE_SUPPORT
bool gSharedCacheOverridden = false;
#if __IPHONE_OS_VERSION_MIN_REQUIRED
static const char* sSharedCacheDir = IPHONE_DYLD_SHARED_CACHE_DIR;
- static bool sDylibsOverrideCache = false;
#define ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE 1024
#else
static const char* sSharedCacheDir = MACOSX_DYLD_SHARED_CACHE_DIR;
#endif
ImageLoader::LinkContext gLinkContext;
bool gLogAPIs = false;
+#if SUPPORT_ACCELERATE_TABLES
+bool gLogAppAPIs = false;
+#endif
const struct LibSystemHelpers* gLibSystemHelpers = NULL;
#if SUPPORT_OLD_CRT_INITIALIZATION
bool gRunInitializersOldWay = false;
static int sLogSocket = -1;
#endif
static bool sFrameworksFoundAsDylibs = false;
-#if __x86_64__
+#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT
static bool sHaswell = false;
#endif
static std::vector<ImageLoader::DynamicReference> sDynamicReferences;
static OSSpinLock sDynamicReferencesLock = 0;
+#if !TARGET_IPHONE_SIMULATOR
static bool sLogToFile = false;
+#endif
static char sLoadingCrashMessage[1024] = "dyld: launch, loading dependent libraries";
+static _dyld_objc_notify_mapped sNotifyObjCMapped;
+static _dyld_objc_notify_init sNotifyObjCInit;
+static _dyld_objc_notify_unmapped sNotifyObjCUnmapped;
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+static bool sForceStderr = false;
+#endif
+
+
+
+#if SUPPORT_ACCELERATE_TABLES
+static ImageLoaderMegaDylib* sAllCacheImagesProxy = NULL;
+static bool sDisableAcceleratorTables = false;
+#endif
+
//
// The MappedRanges structure is used for fast address->image lookups.
// The table is only updated when the dyld lock is held, so we don't
//
struct MappedRanges
{
- enum { count=400 };
+ MappedRanges* next;
+ unsigned long count;
struct {
ImageLoader* image;
uintptr_t start;
uintptr_t end;
- } array[count];
- MappedRanges* next;
+ } array[1];
};
-static MappedRanges sMappedRangesStart;
+static MappedRanges* sMappedRangesStart;
void addMappedRange(ImageLoader* image, uintptr_t start, uintptr_t end)
{
//dyld::log("addMappedRange(0x%lX->0x%lX) for %s\n", start, end, image->getShortName());
- for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) {
- for (int i=0; i < MappedRanges::count; ++i) {
+ for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) {
+ for (unsigned long i=0; i < p->count; ++i) {
if ( p->array[i].image == NULL ) {
p->array[i].start = start;
p->array[i].end = end;
}
}
// table must be full, chain another
- MappedRanges* newRanges = (MappedRanges*)malloc(sizeof(MappedRanges));
- bzero(newRanges, sizeof(MappedRanges));
+#if SUPPORT_ACCELERATE_TABLES
+ unsigned count = (sAllCacheImagesProxy != NULL) ? 16 : 400;
+#else
+ unsigned count = 400;
+#endif
+ size_t allocationSize = sizeof(MappedRanges) + (count-1)*3*sizeof(void*);
+ MappedRanges* newRanges = (MappedRanges*)malloc(allocationSize);
+ bzero(newRanges, allocationSize);
+ newRanges->count = count;
newRanges->array[0].start = start;
newRanges->array[0].end = end;
newRanges->array[0].image = image;
- for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) {
- if ( p->next == NULL ) {
- OSMemoryBarrier();
- p->next = newRanges;
- break;
+ OSMemoryBarrier();
+ if ( sMappedRangesStart == NULL ) {
+ sMappedRangesStart = newRanges;
+ }
+ else {
+ for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) {
+ if ( p->next == NULL ) {
+ OSMemoryBarrier();
+ p->next = newRanges;
+ break;
+ }
}
}
}
void removedMappedRanges(ImageLoader* image)
{
- for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) {
- for (int i=0; i < MappedRanges::count; ++i) {
+ for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) {
+ for (unsigned long i=0; i < p->count; ++i) {
if ( p->array[i].image == image ) {
// clear with a barrier so that any reader will see consistent records
OSMemoryBarrier();
ImageLoader* findMappedRange(uintptr_t target)
{
- for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) {
- for (int i=0; i < MappedRanges::count; ++i) {
+ for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) {
+ for (unsigned long i=0; i < p->count; ++i) {
if ( p->array[i].image != NULL ) {
if ( (p->array[i].start <= target) && (target < p->array[i].end) )
return p->array[i].image;
static int sLogfile = STDERR_FILENO;
#endif
-#if LOG_BINDINGS
-static int sBindingsLogfile = -1;
-static void mysprintf(char* dst, const char* format, ...)
-{
- _SIMPLE_STRING buf = _simple_salloc();
- if ( buf != NULL ) {
- va_list list;
- va_start(list, format);
- _simple_vsprintf(buf, format, list);
- va_end(list);
- strcpy(dst, _simple_string(buf));
- _simple_sfree(buf);
- }
- else {
- strcpy(dst, "out of memory");
- }
-}
-void logBindings(const char* format, ...)
-{
- if ( sBindingsLogfile != -1 ) {
- va_list list;
- va_start(list, format);
- _simple_vdprintf(sBindingsLogfile, format, list);
- va_end(list);
- }
-}
-#endif
-
#if !TARGET_IPHONE_SIMULATOR
// based on CFUtilities.c: also_do_stderr()
static bool useSyslog()
{
// Use syslog() for processes managed by launchd
- if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 11) ) {
- if ( (*gLibSystemHelpers->isLaunchdOwned)() ) {
- return true;
+ static bool launchdChecked = false;
+ static bool launchdOwned = false;
+ if ( !launchdChecked && gProcessInfo->libSystemInitialized ) {
+ if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 11) ) {
+ // <rdar://problem/23520449> only call isLaunchdOwned() after libSystem is initialized
+ launchdOwned = (*gLibSystemHelpers->isLaunchdOwned)();
+ launchdChecked = true;
}
}
+ if ( launchdChecked && launchdOwned )
+ return true;
// If stderr is not available, use syslog()
struct stat sb;
_simple_sfree(buf);
}
+
+
void vlog(const char* format, va_list list)
{
- if ( !sLogToFile && useSyslog() )
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+ // <rdar://problem/25965832> log to console when running iOS app from Xcode
+ if ( !sLogToFile && !sForceStderr && useSyslog() )
+#else
+ if ( !sLogToFile && useSyslog() )
+#endif
socket_syslogv(LOG_ERR, format, list);
else {
_simple_vdprintf(sLogfile, format, list);
return NULL;
}
-static void notifySingle(dyld_image_states state, const ImageLoader* image)
+#if SUPPORT_ACCELERATE_TABLES
+static dyld_image_state_change_handler getPreInitNotifyHandler(unsigned index)
+{
+ std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(dyld_image_state_dependents_initialized, sSingleHandlers);
+ if ( index >= handlers->size() )
+ return NULL;
+ return (*handlers)[index];
+}
+
+static dyld_image_state_change_handler getBoundBatchHandler(unsigned index)
+{
+ std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(dyld_image_state_bound, sBatchHandlers);
+ if ( index >= handlers->size() )
+ return NULL;
+ return (*handlers)[index];
+}
+
+static void notifySingleFromCache(dyld_image_states state, const mach_header* mh, const char* path)
+{
+ //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath());
+ std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sSingleHandlers);
+ if ( handlers != NULL ) {
+ dyld_image_info info;
+ info.imageLoadAddress = mh;
+ info.imageFilePath = path;
+ info.imageFileModDate = 0;
+ for (dyld_image_state_change_handler handler : *handlers) {
+ const char* result = (*handler)(state, 1, &info);
+ if ( (result != NULL) && (state == dyld_image_state_mapped) ) {
+ //fprintf(stderr, " image rejected by handler=%p\n", *it);
+ // make copy of thrown string so that later catch clauses can free it
+ const char* str = strdup(result);
+ throw str;
+ }
+ }
+ }
+ if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && (mh->flags & MH_HAS_OBJC) ) {
+ (*sNotifyObjCInit)(path, mh);
+ }
+}
+#endif
+
+static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+
+
+static void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned imageCount, const dyld_image_info infos[])
+{
+ unsigned entriesSize = imageCount*sizeof(dyld_process_info_image_entry);
+ unsigned pathsSize = 0;
+ for (unsigned j=0; j < imageCount; ++j) {
+ pathsSize += (strlen(infos[j].imageFilePath) + 1);
+ }
+ unsigned totalSize = (sizeof(dyld_process_info_notify_header) + entriesSize + pathsSize + 127) & -128; // align
+ if ( totalSize > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE ) {
+ // Putting all image paths into one message would make buffer too big.
+ // Instead split into two messages. Recurse as needed until paths fit in buffer.
+ unsigned imageHalfCount = imageCount/2;
+ notifyMonitoringDyld(unloading, portSlot, imageHalfCount, infos);
+ notifyMonitoringDyld(unloading, portSlot, imageCount - imageHalfCount, &infos[imageHalfCount]);
+ return;
+ }
+ uint8_t buffer[totalSize];
+ dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer;
+ header->version = 1;
+ header->imageCount = imageCount;
+ header->imagesOffset = sizeof(dyld_process_info_notify_header);
+ header->stringsOffset = sizeof(dyld_process_info_notify_header) + entriesSize;
+ header->timestamp = mach_absolute_time();
+ dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&buffer[header->imagesOffset];
+ char* const pathPoolStart = (char*)&buffer[header->stringsOffset];
+ char* pathPool = pathPoolStart;
+ for (unsigned j=0; j < imageCount; ++j) {
+ strcpy(pathPool, infos[j].imageFilePath);
+ uint32_t len = (uint32_t)strlen(pathPool);
+ bzero(entries->uuid, 16);
+ const ImageLoader* image = findImageByMachHeader(infos[j].imageLoadAddress);
+ if ( image != NULL ) {
+ image->getUUID(entries->uuid);
+ }
+#if SUPPORT_ACCELERATE_TABLES
+ else if ( sAllCacheImagesProxy != NULL ) {
+ const mach_header* mh;
+ const char* path;
+ unsigned index;
+ if ( sAllCacheImagesProxy->addressInCache(infos[j].imageLoadAddress, &mh, &path, &index) ) {
+ sAllCacheImagesProxy->getDylibUUID(index, entries->uuid);
+ }
+ }
+#endif
+ entries->loadAddress = (uint64_t)infos[j].imageLoadAddress;
+ entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart);
+ entries->pathLength = len;
+ pathPool += (len +1);
+ ++entries;
+ }
+
+ if ( sNotifyReplyPorts[portSlot] == 0 ) {
+ if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[portSlot]) )
+ mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[portSlot], sNotifyReplyPorts[portSlot], MACH_MSG_TYPE_MAKE_SEND);
+ //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]);
+ }
+ //dyld::log("found port to send to\n");
+ mach_msg_header_t* h = (mach_msg_header_t*)buffer;
+ h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE
+ h->msgh_id = unloading ? DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID : DYLD_PROCESS_INFO_NOTIFY_LOAD_ID;
+ h->msgh_local_port = sNotifyReplyPorts[portSlot];
+ h->msgh_remote_port = dyld::gProcessInfo->notifyPorts[portSlot];
+ h->msgh_reserved = 0;
+ h->msgh_size = (mach_msg_size_t)sizeof(buffer);
+ //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, dyld::gProcessInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id);
+ kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_SEND_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 100, MACH_PORT_NULL);
+ //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size);
+ if ( sendResult == MACH_SEND_INVALID_DEST ) {
+ // sender is not responding, detatch
+ //dyld::log("process requesting notification gone. deallocation send port %d and receive port %d\n", dyld::gProcessInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]);
+ mach_port_deallocate(mach_task_self(), dyld::gProcessInfo->notifyPorts[portSlot]);
+ mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
+ dyld::gProcessInfo->notifyPorts[portSlot] = 0;
+ sNotifyReplyPorts[portSlot] = 0;
+ }
+}
+
+#define MAX_KERNEL_IMAGES_PER_CALL (100)
+
+static void flushKernelNotifications(bool loading, bool force, std::array<dyld_kernel_image_info_t,MAX_KERNEL_IMAGES_PER_CALL>& kernelInfos, uint32_t &kernelInfoCount) {
+ if ((force && kernelInfoCount != 0) || kernelInfoCount == MAX_KERNEL_IMAGES_PER_CALL) {
+ if (loading) {
+ task_register_dyld_image_infos(mach_task_self(), kernelInfos.data(), kernelInfoCount);
+ } else {
+ task_unregister_dyld_image_infos(mach_task_self(), kernelInfos.data(), kernelInfoCount);
+ }
+ kernelInfoCount = 0;
+ }
+}
+
+static
+void queueKernelNotification(const ImageLoader& image, bool loading, std::array<dyld_kernel_image_info_t,MAX_KERNEL_IMAGES_PER_CALL>& kernelInfos, uint32_t &kernelInfoCount) {
+ if ( !image.inSharedCache() ) {
+ ino_t inode = image.getInode();
+ image.getUUID(kernelInfos[kernelInfoCount].uuid);
+ memcpy(&kernelInfos[kernelInfoCount].fsobjid, &inode, 8);
+ kernelInfos[kernelInfoCount].load_addr = (uint64_t)image.machHeader();
+ // FIXME we should also be grabbing the device ID, but that is not necessary yet,
+ // and requires threading it through the ImageLoader
+ kernelInfos[kernelInfoCount].fsid.val[0] = 0;
+ kernelInfos[kernelInfoCount].fsid.val[1] = 0;
+ kernelInfoCount++;
+ }
+ flushKernelNotifications(loading, false, kernelInfos, kernelInfoCount);
+}
+
+void notifyKernel(const ImageLoader& image, bool loading) {
+ std::array<dyld_kernel_image_info_t,MAX_KERNEL_IMAGES_PER_CALL> kernelInfos;
+ uint32_t kernelInfoCount = 0;
+ queueKernelNotification(image, loading, kernelInfos, kernelInfoCount);
+ flushKernelNotifications(loading, true, kernelInfos, kernelInfoCount);
+}
+
+static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
//dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath());
std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sSingleHandlers);
}
}
}
+ if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
+ uint64_t t0 = mach_absolute_time();
+ (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
+ uint64_t t1 = mach_absolute_time();
+ uint64_t t2 = mach_absolute_time();
+ uint64_t timeInObjC = t1-t0;
+ uint64_t emptyTime = (t2-t1)*100;
+ if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) {
+ timingInfo->addTime(image->getShortName(), timeInObjC);
+ }
+ }
// mach message csdlc about dynamically unloaded images
if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) {
+ notifyKernel(*image, false);
+
uint64_t loadTimestamp = mach_absolute_time();
if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
dyld::log("dyld: coresymbolication_unload_notifier(%p, 0x%016llX, %p, %s)\n",
if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) {
coresymbolication_unload_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->getPath(), image->machHeader());
}
+ for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+ if ( dyld::gProcessInfo->notifyPorts[slot] != 0 ) {
+ dyld_image_info info;
+ info.imageLoadAddress = image->machHeader();
+ info.imageFilePath = image->getPath();
+ info.imageFileModDate = 0;
+ notifyMonitoringDyld(true, slot, 1, &info);
+ }
+ else if ( sNotifyReplyPorts[slot] != 0 ) {
+ // monitoring process detached from this process, so release reply port
+ //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]);
+ mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
+ sNotifyReplyPorts[slot] = 0;
+ }
+ }
}
-}
-
+}
//
return left->compare(right);
}
-static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler)
+static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification)
{
std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sBatchHandlers);
- if ( handlers != NULL ) {
+ std::array<dyld_kernel_image_info_t,MAX_KERNEL_IMAGES_PER_CALL> kernelInfos;
+ uint32_t kernelInfoCount = 0;
+
+ if ( (handlers != NULL) || ((state == dyld_image_state_bound) && (sNotifyObjCMapped != NULL)) ) {
// don't use a vector because it will use malloc/free and we want notifcation to be low cost
allImagesLock();
- ImageLoader* images[sAllImages.size()+1];
+ dyld_image_info infos[allImagesCount()+1];
+ ImageLoader* images[allImagesCount()+1];
ImageLoader** end = images;
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
dyld_image_states imageState = (*it)->getState();
*end++ = sBundleBeingLoaded;
}
const char* dontLoadReason = NULL;
- uint32_t count = (uint32_t)(end-images);
- if ( end != images ) {
+ uint32_t imageCount = (uint32_t)(end-images);
+ if ( imageCount != 0 ) {
// sort bottom up
- qsort(images, count, sizeof(ImageLoader*), &imageSorter);
+ qsort(images, imageCount, sizeof(ImageLoader*), &imageSorter);
// build info array
- dyld_image_info infos[count];
- for (unsigned int i=0; i < count; ++i) {
+ for (unsigned int i=0; i < imageCount; ++i) {
dyld_image_info* p = &infos[i];
ImageLoader* image = images[i];
//dyld::log(" state=%d, name=%s\n", state, image->getPath());
p->imageLoadAddress = image->machHeader();
- p->imageFilePath = image->getRealPath();
+ p->imageFilePath = image->getRealPath();
p->imageFileModDate = image->lastModified();
+ // get these registered with the kernel as early as possible
+ if ( state == dyld_image_state_dependents_mapped)
+ queueKernelNotification(*image, true, kernelInfos, kernelInfoCount);
// special case for add_image hook
if ( state == dyld_image_state_bound )
notifyAddImageCallbacks(image);
}
-
- if ( onlyHandler != NULL ) {
- const char* result = (*onlyHandler)(state, count, infos);
- if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) {
- //fprintf(stderr, " images rejected by handler=%p\n", onlyHandler);
- // make copy of thrown string so that later catch clauses can free it
- dontLoadReason = strdup(result);
+ flushKernelNotifications(true, true, kernelInfos, kernelInfoCount);
+ }
+#if SUPPORT_ACCELERATE_TABLES
+ if ( sAllCacheImagesProxy != NULL ) {
+ unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(state, orLater, &infos[imageCount]);
+ // support _dyld_register_func_for_add_image()
+ if ( state == dyld_image_state_bound ) {
+ for (ImageCallback callback : sAddImageCallbacks) {
+ for (unsigned i=0; i < cacheCount; ++i)
+ (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheSlide);
}
}
- else {
- // call each handler with whole array
- for (std::vector<dyld_image_state_change_handler>::iterator it = handlers->begin(); it != handlers->end(); ++it) {
- const char* result = (*it)(state, count, infos);
+ imageCount += cacheCount;
+ }
+#endif
+ if ( imageCount != 0 ) {
+ if ( !onlyObjCMappedNotification ) {
+ if ( onlyHandler != NULL ) {
+ const char* result = NULL;
+ if ( result == NULL ) {
+ result = (*onlyHandler)(state, imageCount, infos);
+ }
if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) {
- //fprintf(stderr, " images rejected by handler=%p\n", *it);
+ //fprintf(stderr, " images rejected by handler=%p\n", onlyHandler);
// make copy of thrown string so that later catch clauses can free it
dontLoadReason = strdup(result);
- break;
}
}
+ else {
+ // call each handler with whole array
+ if ( handlers != NULL ) {
+ for (std::vector<dyld_image_state_change_handler>::iterator it = handlers->begin(); it != handlers->end(); ++it) {
+ const char* result = (*it)(state, imageCount, infos);
+ if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) {
+ //fprintf(stderr, " images rejected by handler=%p\n", *it);
+ // make copy of thrown string so that later catch clauses can free it
+ dontLoadReason = strdup(result);
+ break;
+ }
+ }
+ }
+ }
+ }
+ // tell objc about new images
+ if ( (onlyHandler == NULL) && ((state == dyld_image_state_bound) || (orLater && (dyld_image_state_bound > state))) && (sNotifyObjCMapped != NULL) ) {
+ const char* paths[imageCount];
+ const mach_header* mhs[imageCount];
+ unsigned objcImageCount = 0;
+ for (int i=0; i < imageCount; ++i) {
+ const ImageLoader* image = findImageByMachHeader(infos[i].imageLoadAddress);
+ bool hasObjC = false;
+ if ( image != NULL ) {
+ hasObjC = image->notifyObjC();
+ }
+#if SUPPORT_ACCELERATE_TABLES
+ else if ( sAllCacheImagesProxy != NULL ) {
+ const mach_header* mh;
+ const char* path;
+ unsigned index;
+ if ( sAllCacheImagesProxy->addressInCache(infos[i].imageLoadAddress, &mh, &path, &index) ) {
+ hasObjC = (mh->flags & MH_HAS_OBJC);
+ }
+ }
+#endif
+ if ( hasObjC ) {
+ paths[objcImageCount] = infos[i].imageFilePath;
+ mhs[objcImageCount] = infos[i].imageLoadAddress;
+ ++objcImageCount;
+ }
+ }
+ if ( objcImageCount != 0 ) {
+ uint64_t t0 = mach_absolute_time();
+ (*sNotifyObjCMapped)(objcImageCount, paths, mhs);
+ uint64_t t1 = mach_absolute_time();
+ ImageLoader::fgTotalObjCSetupTime += (t1-t0);
+ }
}
- if ( (state == dyld_image_state_dependents_mapped) && ((dyld::gProcessInfo->coreSymbolicationShmPage != NULL) || sEnv.DYLD_PRINT_CS_NOTIFICATIONS) ) {
+ }
+ allImagesUnlock();
+ if ( dontLoadReason != NULL )
+ throw dontLoadReason;
+ if ( !preflightOnly && (state == dyld_image_state_dependents_mapped) ) {
+ if ( (dyld::gProcessInfo->coreSymbolicationShmPage != NULL) || sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
// mach message csdlc about loaded images
uint64_t loadTimestamp = mach_absolute_time();
- for (unsigned j=0; j < count; ++j) {
+ for (unsigned j=0; j < imageCount; ++j) {
if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
dyld::log("dyld: coresymbolication_load_notifier(%p, 0x%016llX, %p, %s)\n",
dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageLoadAddress, infos[j].imageFilePath);
coresymbolication_load_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageFilePath, infos[j].imageLoadAddress);
}
}
+ for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+ if ( dyld::gProcessInfo->notifyPorts[slot] )
+ notifyMonitoringDyld(false, slot, imageCount, infos);
+ }
}
- allImagesUnlock();
- if ( dontLoadReason != NULL )
- throw dontLoadReason;
}
}
-static void notifyBatch(dyld_image_states state)
+static void notifyBatch(dyld_image_states state, bool preflightOnly)
{
- notifyBatchPartial(state, false, NULL);
+ notifyBatchPartial(state, false, NULL, preflightOnly, false);
}
// In order for register_func_for_add_image() callbacks to to be called bottom up,
static unsigned int imageCount()
{
- return (unsigned int)sAllImages.size();
+ allImagesLock();
+ unsigned int result = (unsigned int)sAllImages.size();
+ allImagesUnlock();
+ return (result);
}
}
#endif
+static bool sandboxBlocked(const char* path, const char* kind)
+{
+#if TARGET_IPHONE_SIMULATOR
+ // sandbox calls not yet supported in simulator runtime
+ return false;
+#else
+ sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT);
+ return ( sandbox_check(getpid(), kind, filter, path) > 0 );
+#endif
+}
+
+bool sandboxBlockedMmap(const char* path)
+{
+ return sandboxBlocked(path, "file-map-executable");
+}
+
+bool sandboxBlockedOpen(const char* path)
+{
+ return sandboxBlocked(path, "file-read-data");
+}
+
+bool sandboxBlockedStat(const char* path)
+{
+ return sandboxBlocked(path, "file-read-metadata");
+}
+
+
static void addDynamicReference(ImageLoader* from, ImageLoader* to) {
- // don't add dynamic reference if either are in the shared cache
- if( from->inSharedCache() )
- return;
- if( to->inSharedCache() )
+ // don't add dynamic reference if target is in the shared cache (since it can't be unloaded)
+ if ( to->inSharedCache() )
return;
// don't add dynamic reference if there already is a static one
addMappedRange(image, lastSegStart, lastSegEnd);
- if ( sEnv.DYLD_PRINT_LIBRARIES || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) {
+ if ( gLinkContext.verboseLoading || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) {
dyld::log("dyld: loaded: %s\n", image->getPath());
}
(*it)(image->machHeader(), image->getSlide());
}
sRemoveImageCallbacksInUse = false;
+
+ if ( sNotifyObjCUnmapped != NULL && image->notifyObjC() )
+ (*sNotifyObjCUnmapped)(image->getRealPath(), image->machHeader());
}
// notify
- notifySingle(dyld_image_state_terminated, image);
+ notifySingle(dyld_image_state_terminated, image, NULL);
// remove from mapped images table
removedMappedRanges(image);
}
// log if requested
- if ( sEnv.DYLD_PRINT_LIBRARIES || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) {
+ if ( gLinkContext.verboseLoading || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) {
dyld::log("dyld: unloaded: %s\n", image->getPath());
}
image->doTermination(gLinkContext);
}
sImageFilesNeedingTermination.clear();
- notifyBatch(dyld_image_state_terminated);
+ notifyBatch(dyld_image_state_terminated, false);
}
catch (const char* msg) {
halt(msg);
gLinkContext.startedInitializingMainExecutable = true;
// run initialzers for any inserted dylibs
- ImageLoader::InitializerTimingList initializerTimes[sAllImages.size()];
+ ImageLoader::InitializerTimingList initializerTimes[allImagesCount()];
initializerTimes[0].count = 0;
const size_t rootCount = sImageRoots.size();
if ( rootCount > 1 ) {
// dump info if requested
if ( sEnv.DYLD_PRINT_STATISTICS )
- ImageLoaderMachO::printStatistics((unsigned int)sAllImages.size(), initializerTimes[0]);
+ ImageLoader::printStatistics((unsigned int)allImagesCount(), initializerTimes[0]);
+ if ( sEnv.DYLD_PRINT_STATISTICS_DETAILS )
+ ImageLoaderMachO::printStatisticsDetails((unsigned int)allImagesCount(), initializerTimes[0]);
}
bool mainExecutablePrebound()
{
//dyld::log("checkDylibOverridesInDir('%s')\n", dirPath);
char dylibPath[PATH_MAX];
- int dirPathLen = strlcpy(dylibPath, dirPath, PATH_MAX-1);
+ long dirPathLen = strlcpy(dylibPath, dirPath, PATH_MAX-1);
if ( dirPathLen >= PATH_MAX )
return;
DIR* dirp = opendir(dirPath);
{
//dyld::log("checkFrameworkOverridesInDir('%s')\n", dirPath);
char frameworkPath[PATH_MAX];
- int dirPathLen = strlcpy(frameworkPath, dirPath, PATH_MAX-1);
+ long dirPathLen = strlcpy(frameworkPath, dirPath, PATH_MAX-1);
if ( dirPathLen >= PATH_MAX )
return;
DIR* dirp = opendir(dirPath);
if (*s == ':') {
size_t len = s-start;
if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( gLinkContext.processIsRestricted ) {
+ dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n");
+ continue;
+ }
+#endif
size_t mainExecDirLen = strlen(mainExecutableDir);
char* str = new char[mainExecDirLen+len+1];
strcpy(str, mainExecutableDir);
result[index++] = str;
}
else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( gLinkContext.processIsRestricted ) {
+ dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n");
+ continue;
+ }
+#endif
size_t mainExecDirLen = strlen(mainExecutableDir);
char* str = new char[mainExecDirLen+len+1];
strcpy(str, mainExecutableDir);
}
size_t len = strlen(start);
if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) {
- size_t mainExecDirLen = strlen(mainExecutableDir);
- char* str = new char[mainExecDirLen+len+1];
- strcpy(str, mainExecutableDir);
- strlcat(str, &start[13], mainExecDirLen+len+1);
- str[mainExecDirLen+len-13] = '\0';
- result[index++] = str;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( gLinkContext.processIsRestricted ) {
+ dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n");
+ }
+ else
+#endif
+ {
+ size_t mainExecDirLen = strlen(mainExecutableDir);
+ char* str = new char[mainExecDirLen+len+1];
+ strcpy(str, mainExecutableDir);
+ strlcat(str, &start[13], mainExecDirLen+len+1);
+ str[mainExecDirLen+len-13] = '\0';
+ result[index++] = str;
+ }
}
else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) {
- size_t mainExecDirLen = strlen(mainExecutableDir);
- char* str = new char[mainExecDirLen+len+1];
- strcpy(str, mainExecutableDir);
- strlcat(str, &start[17], mainExecDirLen+len+1);
- str[mainExecDirLen+len-17] = '\0';
- result[index++] = str;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( gLinkContext.processIsRestricted ) {
+ dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n");
+ }
+ else
+#endif
+ {
+ size_t mainExecDirLen = strlen(mainExecutableDir);
+ char* str = new char[mainExecDirLen+len+1];
+ strcpy(str, mainExecutableDir);
+ strlcat(str, &start[17], mainExecDirLen+len+1);
+ str[mainExecDirLen+len-17] = '\0';
+ result[index++] = str;
+ }
}
else {
char* str = new char[len+1];
}
}
-
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
static void paths_expand_roots(const char **paths, const char *key, const char *val)
{
// assert(val != NULL);
}
paths[i-skip] = NULL;
}
+#endif
#if 0
else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) {
appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_LIBRARY_PATH);
}
+#if SUPPORT_ROOT_PATH
else if ( (strcmp(key, "DYLD_ROOT_PATH") == 0) || (strcmp(key, "DYLD_PATHS_ROOT") == 0) ) {
if ( strcmp(value, "/") != 0 ) {
gLinkContext.rootPaths = parseColonList(value, mainExecutableDir);
}
}
}
- }
+ }
+#endif
else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) {
gLinkContext.imageSuffix = value;
}
else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value, NULL);
+#if SUPPORT_ACCELERATE_TABLES
+ sDisableAcceleratorTables = true;
+#endif
}
else if ( strcmp(key, "DYLD_PRINT_OPTS") == 0 ) {
sEnv.DYLD_PRINT_OPTS = true;
gLinkContext.preFetchDisabled = true;
}
else if ( strcmp(key, "DYLD_PRINT_LIBRARIES") == 0 ) {
- sEnv.DYLD_PRINT_LIBRARIES = true;
+ gLinkContext.verboseLoading = true;
}
else if ( strcmp(key, "DYLD_PRINT_LIBRARIES_POST_LAUNCH") == 0 ) {
sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH = true;
}
else if ( strcmp(key, "DYLD_PRINT_STATISTICS") == 0 ) {
sEnv.DYLD_PRINT_STATISTICS = true;
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+ // <rdar://problem/26614838> DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps
+ sForceStderr = true;
+#endif
+ }
+ else if ( strcmp(key, "DYLD_PRINT_TO_STDERR") == 0 ) {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+ // <rdar://problem/26633440> DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps
+ sForceStderr = true;
+#endif
+ }
+ else if ( strcmp(key, "DYLD_PRINT_STATISTICS_DETAILS") == 0 ) {
+ sEnv.DYLD_PRINT_STATISTICS_DETAILS = true;
}
else if ( strcmp(key, "DYLD_PRINT_SEGMENTS") == 0 ) {
gLinkContext.verboseMapping = true;
else if ( strcmp(key, "DYLD_PRINT_APIS") == 0 ) {
gLogAPIs = true;
}
+#if SUPPORT_ACCELERATE_TABLES
+ else if ( strcmp(key, "DYLD_PRINT_APIS_APP") == 0 ) {
+ gLogAppAPIs = true;
+ }
+#endif
else if ( strcmp(key, "DYLD_PRINT_WARNINGS") == 0 ) {
gLinkContext.verboseWarnings = true;
}
#if SUPPORT_VERSIONED_PATHS
else if ( strcmp(key, "DYLD_VERSIONED_LIBRARY_PATH") == 0 ) {
appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_LIBRARY_PATH);
+ #if SUPPORT_ACCELERATE_TABLES
+ sDisableAcceleratorTables = true;
+ #endif
}
else if ( strcmp(key, "DYLD_VERSIONED_FRAMEWORK_PATH") == 0 ) {
appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_FRAMEWORK_PATH);
+ #if SUPPORT_ACCELERATE_TABLES
+ sDisableAcceleratorTables = true;
+ #endif
}
#endif
#if !TARGET_IPHONE_SIMULATOR
#endif
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
//
// For security, setuid programs ignore DYLD_* environment variables.
// Additionally, the DYLD_* enviroment variables are removed
//
static void pruneEnvironmentVariables(const char* envp[], const char*** applep)
{
+#if SUPPORT_LC_DYLD_ENVIRONMENT
+ checkLoadCommandEnvironmentVariables();
+#endif
+
// delete all DYLD_* and LD_LIBRARY_PATH environment variables
int removedCount = 0;
const char** d = envp;
}
}
*d++ = NULL;
-// <rdar://11894054> Disable warnings about DYLD_ env vars being ignored. The warnings are causing too much confusion.
-#if 0
- if ( removedCount != 0 ) {
- dyld::log("dyld: DYLD_ environment variables being ignored because ");
- switch (sRestrictedReason) {
- case restrictedNot:
- break;
- case restrictedBySetGUid:
- dyld::log("main executable (%s) is setuid or setgid\n", sExecPath);
- break;
- case restrictedBySegment:
- dyld::log("main executable (%s) has __RESTRICT/__restrict section\n", sExecPath);
- break;
- case restrictedByEntitlements:
- dyld::log("main executable (%s) is code signed with entitlements\n", sExecPath);
- break;
- }
- }
-#endif
// slide apple parameters
if ( removedCount > 0 ) {
*applep = d;
if ( removedCount > 0 )
strlcat(sLoadingCrashMessage, ", ignoring DYLD_* env vars", sizeof(sLoadingCrashMessage));
}
+#endif
static void defaultUninitializedFallbackPaths(const char* envp[])
{
#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( gLinkContext.processIsRestricted ) {
+ sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths;
+ sEnv.DYLD_FALLBACK_LIBRARY_PATH = sRestrictedLibraryFallbackPaths;
+ return;
+ }
+
// default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment
const char* home = _simple_getenv(envp, "HOME");;
if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) {
static void checkEnvironmentVariables(const char* envp[])
{
+ if ( sEnvMode == envNone )
+ return;
const char** p;
for(p = envp; *p != NULL; p++) {
const char* keyEqualsValue = *p;
char key[keyLen+1];
strncpy(key, keyEqualsValue, keyLen);
key[keyLen] = '\0';
+ if ( (sEnvMode == envPrintOnly) && (strncmp(key, "DYLD_PRINT_", 11) != 0) )
+ continue;
processDyldEnvironmentVariable(key, value, NULL);
}
}
checkLoadCommandEnvironmentVariables();
#endif // SUPPORT_LC_DYLD_ENVIRONMENT
+#if SUPPORT_ROOT_PATH
// <rdar://problem/11281064> DYLD_IMAGE_SUFFIX and DYLD_ROOT_PATH cannot be used together
if ( (gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) {
dyld::warn("Ignoring DYLD_IMAGE_SUFFIX because DYLD_ROOT_PATH is used.\n");
gLinkContext.imageSuffix = NULL;
}
+#endif
}
-#if __x86_64__
+#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT
static bool isGCProgram(const macho_header* mh, uintptr_t slide)
{
const uint32_t cmd_count = mh->ncmds;
return false;
}
#endif
+
static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide)
{
#if CPU_SUBTYPES_SUPPORTED
sHostCPUsubtype = info.cpu_subtype;
mach_port_deallocate(mach_task_self(), hostPort);
#if __x86_64__
- #if TARGET_IPHONE_SIMULATOR
- sHaswell = false;
- #else
+ #if DYLD_SHARED_CACHE_SUPPORT
sHaswell = (sHostCPUsubtype == CPU_SUBTYPE_X86_64_H);
// <rdar://problem/18528074> x86_64h: Fall back to the x86_64 slice if an app requires GC.
if ( sHaswell ) {
dyld::warn("disabling shared region because main executable overlaps\n");
}
#if __i386__
- if ( sProcessIsRestricted ) {
+ if ( gLinkContext.processIsRestricted ) {
// <rdar://problem/15280847> use private or no shared region for suid processes
gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
}
ImageLoader* findImageContainingAddress(const void* addr)
{
+ #if SUPPORT_ACCELERATE_TABLES
+ if ( sAllCacheImagesProxy != NULL ) {
+ const mach_header* mh;
+ const char* path;
+ unsigned index;
+ if ( sAllCacheImagesProxy->addressInCache(addr, &mh, &path, &index) )
+ return sAllCacheImagesProxy;
+ }
+ #endif
return findMappedRange((uintptr_t)addr);
}
#endif // CPU_SUBTYPES_SUPPORTED
+
+//
+// Validate the fat_header and fat_arch array:
+//
+// 1) arch count would not cause array to extend past 4096 byte read buffer
+// 2) no slice overlaps the fat_header and arch array
+// 3) arch list does not contain duplicate cputype/cpusubtype tuples
+// 4) arch list does not have two overlapping slices.
+//
+static bool fatValidate(const fat_header* fh)
+{
+ if ( fh->magic != OSSwapBigToHostInt32(FAT_MAGIC) )
+ return false;
+
+ // since only first 4096 bytes of file read, we can only handle up to 204 slices.
+ const uint32_t sliceCount = OSSwapBigToHostInt32(fh->nfat_arch);
+ if ( sliceCount > 204 )
+ return false;
+
+ // compare all slices looking for conflicts
+ const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header));
+ for (uint32_t i=0; i < sliceCount; ++i) {
+ uint32_t i_offset = OSSwapBigToHostInt32(archs[i].offset);
+ uint32_t i_size = OSSwapBigToHostInt32(archs[i].size);
+ uint32_t i_cputype = OSSwapBigToHostInt32(archs[i].cputype);
+ uint32_t i_cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype);
+ uint32_t i_end = i_offset + i_size;
+ // slice cannot overlap with header
+ if ( i_offset < 4096 )
+ return false;
+ // slice size cannot overflow
+ if ( i_end < i_offset )
+ return false;
+ for (uint32_t j=i+1; j < sliceCount; ++j) {
+ uint32_t j_offset = OSSwapBigToHostInt32(archs[j].offset);
+ uint32_t j_size = OSSwapBigToHostInt32(archs[j].size);
+ uint32_t j_cputype = OSSwapBigToHostInt32(archs[j].cputype);
+ uint32_t j_cpusubtype = OSSwapBigToHostInt32(archs[j].cpusubtype);
+ uint32_t j_end = j_offset + j_size;
+ // duplicate slices types not allowed
+ if ( (i_cputype == j_cputype) && (i_cpusubtype == j_cpusubtype) )
+ return false;
+ // slice size cannot overflow
+ if ( j_end < j_offset )
+ return false;
+ // check for overlap of slices
+ if ( i_offset <= j_offset ) {
+ if ( j_offset < i_end )
+ return false; // j overlaps end of i
+ }
+ else {
+ // j overlaps end of i
+ if ( i_offset < j_end )
+ return false; // i overlaps end of j
+ }
+ }
+ }
+ return true;
+}
+
//
// A fat file may contain multiple sub-images for the same cpu-type,
// each optimized for a different cpu-sub-type (e.g G3 or G5).
//
static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len)
{
+ if ( !fatValidate(fh) )
+ return false;
+
#if CPU_SUBTYPES_SUPPORTED
// assume all dylibs loaded must have same cpu type as main executable
const cpu_type_t cpu = sMainExecutableMachHeader->cputype;
const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(cpu, sHostCPUsubtype);
// use ordered list to find best sub-image in fat file
- if ( subTypePreferenceList != NULL )
- return fatFindBestFromOrderedList(cpu, subTypePreferenceList, fh, offset, len);
-
+ if ( subTypePreferenceList != NULL ) {
+ if ( fatFindBestFromOrderedList(cpu, subTypePreferenceList, fh, offset, len) )
+ return true;
+ }
+
// if running cpu is not in list, try for an exact match
if ( fatFindExactMatch(cpu, sHostCPUsubtype, fh, offset, len) )
return true;
// The kernel maps in main executable before dyld gets control. We need to
// make an ImageLoader* for the already mapped in main executable.
-static ImageLoader* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path)
+static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path)
{
// try mach-o loader
if ( isCompatibleMachO((const uint8_t*)mh, path) ) {
ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
addImage(image);
- return image;
+ return (ImageLoaderMachO*)image;
}
throw "main executable not a known format";
}
-
#if DYLD_SHARED_CACHE_SUPPORT
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+static bool dylibsCanOverrideCache()
+{
+ uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM);
+ if ( (devFlags & 1) == 0 )
+ return false;
+ return ( (sSharedCache != NULL) && (sSharedCache->cacheType == kDyldSharedCacheTypeDevelopment) );
+}
+#endif
+
static bool findInSharedCacheImage(const char* path, bool searchByPath, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide)
{
if ( sSharedCache != NULL ) {
*mh = (macho_header*)(p->address+sSharedCacheSlide);
*pathInCache = aPath;
*slide = sSharedCacheSlide;
+ if ( aPath < (char*)(*mh) ) {
+ // <rdar://problem/22056997> found alias, rescan list to get canonical name
+ for (const dyld_cache_image_info* p2 = start; p2 != end; ++p2) {
+ if ( p2->address == p->address ) {
+ *pathInCache = (char*)sSharedCache + p2->pathFileOffset;
+ break;
+ }
+ }
+ }
return true;
}
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
if ( cacheHit ) {
// found image in cache, return info
*mh = (macho_header*)(p->address+sSharedCacheSlide);
- //dyld::log("findInSharedCacheImage(), mh=%p, p->address=0x%0llX, slid=0x%0lX, path=%p\n",
+ //dyld::log("findInSharedCacheImage(), mh=%p, p->address=0x%0llX, slid=0x%0lX, path=%s\n",
// *mh, p->address, sSharedCacheSlide, aPath);
*pathInCache = aPath;
*slide = sSharedCacheSlide;
}
#if TARGET_IPHONE_SIMULATOR
-static bool isSimulatorBinary(const uint8_t* firstPage, const char* path)
+static bool isSimulatorBinary(const uint8_t* firstPages, const char* path)
{
- const macho_header* mh = (macho_header*)firstPage;
+ const macho_header* mh = (macho_header*)firstPages;
const uint32_t cmd_count = mh->ncmds;
- const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
- const struct load_command* const cmdsReadEnd = (struct load_command*)(((char*)mh)+4096);
+ const load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
+ const load_command* const cmdsEnd = (load_command*)((char*)cmds + mh->sizeofcmds);
const struct load_command* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
switch (cmd->cmd) {
- case LC_VERSION_MIN_IPHONEOS:
- case LC_VERSION_MIN_TVOS:
+ #if TARGET_OS_WATCH
case LC_VERSION_MIN_WATCHOS:
return true;
+ #elif TARGET_OS_TV
+ case LC_VERSION_MIN_TVOS:
+ return true;
+ #elif TARGET_OS_IOS
+ case LC_VERSION_MIN_IPHONEOS:
+ return true;
+ #endif
case LC_VERSION_MIN_MACOSX:
// grandfather in a few libSystem dylibs
- if (strstr(path, "/usr/lib/system") || strstr(path, "/usr/lib/libSystem"))
- return true;
+ if ((strcmp(path, "/usr/lib/system/libsystem_kernel.dylib") == 0) ||
+ (strcmp(path, "/usr/lib/system/libsystem_platform.dylib") == 0) ||
+ (strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0))
+ return true;
return false;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
- if ( cmd > cmdsReadEnd )
- return true;
+ if ( cmd > cmdsEnd )
+ return false;
}
return false;
}
if ( (stat_buf.st_mode & S_IFMT) != S_IFREG )
throw "not a file";
- uint8_t firstPage[4096];
+ uint8_t firstPages[MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE];
bool shortPage = false;
// min mach-o file is 4K
if ( fileLength < 4096 ) {
- if ( pread(fd, firstPage, fileLength, 0) != (ssize_t)fileLength )
+ if ( pread(fd, firstPages, fileLength, 0) != (ssize_t)fileLength )
throwf("pread of short file failed: %d", errno);
shortPage = true;
}
else {
- if ( pread(fd, firstPage, 4096,0) != 4096 )
+ // optimistically read only first 4KB
+ if ( pread(fd, firstPages, 4096, 0) != 4096 )
throwf("pread of first 4K failed: %d", errno);
}
// if fat wrapper, find usable sub-file
- const fat_header* fileStartAsFat = (fat_header*)firstPage;
+ const fat_header* fileStartAsFat = (fat_header*)firstPages;
if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
+ if ( OSSwapBigToHostInt32(fileStartAsFat->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) )
+ throwf("fat header too large: %u entries", OSSwapBigToHostInt32(fileStartAsFat->nfat_arch));
if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) {
if ( (fileOffset+fileLength) > (uint64_t)(stat_buf.st_size) )
throwf("truncated fat file. file length=%llu, but needed slice goes to %llu", stat_buf.st_size, fileOffset+fileLength);
- if (pread(fd, firstPage, 4096, fileOffset) != 4096)
+ if (pread(fd, firstPages, 4096, fileOffset) != 4096)
throwf("pread of fat file failed: %d", errno);
}
else {
// try mach-o loader
if ( shortPage )
throw "file too short";
- if ( isCompatibleMachO(firstPage, path) ) {
+ if ( isCompatibleMachO(firstPages, path) ) {
// only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded
- switch ( ((mach_header*)firstPage)->filetype ) {
+ const mach_header* mh = (mach_header*)firstPages;
+ switch ( mh->filetype ) {
case MH_EXECUTE:
case MH_DYLIB:
case MH_BUNDLE:
throw "mach-o, but wrong filetype";
}
+ uint32_t headerAndLoadCommandsSize = sizeof(macho_header) + mh->sizeofcmds;
+ if ( headerAndLoadCommandsSize > MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE )
+ throwf("malformed mach-o: load commands size (%u) > %u", headerAndLoadCommandsSize, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE);
+
+ if ( headerAndLoadCommandsSize > fileLength )
+ dyld::throwf("malformed mach-o: load commands size (%u) > mach-o file size (%llu)", headerAndLoadCommandsSize, fileLength);
+
+ if ( headerAndLoadCommandsSize > 4096 ) {
+ // read more pages
+ unsigned readAmount = headerAndLoadCommandsSize - 4096;
+ if ( pread(fd, &firstPages[4096], readAmount, fileOffset+4096) != readAmount )
+ throwf("pread of extra load commands past 4KB failed: %d", errno);
+ }
+
#if TARGET_IPHONE_SIMULATOR
- #if TARGET_OS_WATCH || TARGET_OS_TV
- // disable error during bring up of these simulators
- #else
// <rdar://problem/14168872> dyld_sim should restrict loading osx binaries
- if ( !isSimulatorBinary(firstPage, path) ) {
+ if ( !isSimulatorBinary(firstPages, path) ) {
+ #if TARGET_OS_WATCH
+ throw "mach-o, but not built for watchOS simulator";
+ #elif TARGET_OS_TV
+ throw "mach-o, but not built for tvOS simulator";
+ #else
throw "mach-o, but not built for iOS simulator";
- }
#endif
+ }
#endif
// instantiate an image
- ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPage, fileOffset, fileLength, stat_buf, gLinkContext);
+ ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPages, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext);
// validate
return checkandAddImage(image, context);
// throw error about what was found
- switch (*(uint32_t*)firstPage) {
+ switch (*(uint32_t*)firstPages) {
case MH_MAGIC:
case MH_CIGAM:
case MH_MAGIC_64:
throw "mach-o, but wrong architecture";
default:
throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
- firstPage[0], firstPage[1], firstPage[2], firstPage[3], firstPage[4], firstPage[5], firstPage[6],firstPage[7]);
+ firstPages[0], firstPages[1], firstPages[2], firstPages[3], firstPages[4], firstPages[5], firstPages[6],firstPages[7]);
}
}
if ( file.getFileDescriptor() == -1 ) {
int err = errno;
if ( err != ENOENT ) {
- const char* newMsg = dyld::mkstringf("%s: open() failed with errno=%d", path, err);
+ const char* newMsg;
+ if ( (err == EPERM) && sandboxBlockedOpen(path) )
+ newMsg = dyld::mkstringf("file system sandbox blocked open() of '%s'", path);
+ else
+ newMsg = dyld::mkstringf("%s: open() failed with errno=%d", path, err);
exceptions->push_back(newMsg);
}
return NULL;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
-static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
+ #if SUPPORT_ACCELERATE_TABLES
+ if ( sAllCacheImagesProxy != NULL ) {
+ unsigned index;
+ if ( sAllCacheImagesProxy->hasDylib(path, &index) )
+ return sAllCacheImagesProxy;
+ }
+ #endif
+
// just return NULL if file not found, but record any other errors
struct stat stat_buf;
if ( my_stat(path, &stat_buf) == -1 ) {
int err = errno;
if ( err != ENOENT ) {
- exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, err));
+ if ( (err == EPERM) && sandboxBlockedStat(path) )
+ exceptions->push_back(dyld::mkstringf("%s: file system sandbox blocked stat()", path));
+ else
+ exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, err));
}
return NULL;
}
-
+
// in case image was renamed or found via symlinks, check for inode match
image = findLoadedImage(stat_buf);
if ( image != NULL )
}
// try to open file
-static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
struct stat stat_buf;
int statErrNo;
ImageLoader* image;
#if DYLD_SHARED_CACHE_SUPPORT
- if ( sDylibsOverrideCache ) {
+ #if SUPPORT_ACCELERATE_TABLES
+ if ( sAllCacheImagesProxy != NULL ) {
+ if ( sAllCacheImagesProxy->hasDylib(path, &cacheIndex) )
+ return sAllCacheImagesProxy;
+ }
+ #endif
+ if ( dylibsCanOverrideCache() ) {
// flag is set that allows installed framework roots to override dyld shared cache
image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions);
if ( imageFound )
return checkandAddImage(image, context);
}
- if ( !sDylibsOverrideCache ) {
+ if ( !dylibsCanOverrideCache() ) {
// flag is not set, and not in cache to try opening it
image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions);
if ( imageFound )
#endif
// just return NULL if file not found, but record any other errors
if ( (statErrNo != ENOENT) && (statErrNo != 0) ) {
- exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, statErrNo));
+ if ( (statErrNo == EPERM) && sandboxBlockedStat(path) )
+ exceptions->push_back(dyld::mkstringf("%s: file system sandbox blocked stat()", path));
+ else
+ exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, statErrNo));
}
return NULL;
}
// open or check existing
-static ImageLoader* loadPhase5(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase5(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
}
if ( exceptions != NULL )
- return loadPhase5load(path, orgPath, context, exceptions);
+ return loadPhase5load(path, orgPath, context, cacheIndex, exceptions);
else
return loadPhase5check(path, orgPath, context);
}
// try with and without image suffix
-static ImageLoader* loadPhase4(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase4(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
if ( gLinkContext.imageSuffix != NULL ) {
char pathWithSuffix[strlen(path)+strlen( gLinkContext.imageSuffix)+2];
ImageLoader::addSuffix(path, gLinkContext.imageSuffix, pathWithSuffix);
- image = loadPhase5(pathWithSuffix, orgPath, context, exceptions);
+ image = loadPhase5(pathWithSuffix, orgPath, context, cacheIndex, exceptions);
}
if ( image == NULL )
- image = loadPhase5(path, orgPath, context, exceptions);
+ image = loadPhase5(path, orgPath, context, cacheIndex, exceptions);
return image;
}
-static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context,
- const char* const frameworkPaths[], const char* const libraryPaths[],
- std::vector<const char*>* exceptions); // forward reference
+static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context,
+ const char* const frameworkPaths[], const char* const libraryPaths[],
+ unsigned& cacheIndex, std::vector<const char*>* exceptions); // forward reference
// expand @ variables
-static ImageLoader* loadPhase3(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase3(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
if ( strncmp(path, "@executable_path/", 17) == 0 ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// executable_path cannot be in used in any binary in a setuid process rdar://problem/4589305
- if ( sProcessIsRestricted && !sProcessRequiresLibraryValidation )
+ if ( gLinkContext.processIsRestricted )
throwf("unsafe use of @executable_path in %s with restricted binary", context.origin);
+#endif
// handle @executable_path path prefix
const char* executablePath = sExecPath;
char newPath[strlen(executablePath) + strlen(path)];
strcpy(&addPoint[1], &path[17]);
else
strcpy(newPath, &path[17]);
- image = loadPhase4(newPath, orgPath, context, exceptions);
+ image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions);
if ( image != NULL )
return image;
if ( realpath(sExecPath, resolvedPath) != NULL ) {
char newRealPath[strlen(resolvedPath) + strlen(path)];
strcpy(newRealPath, resolvedPath);
- char* addPoint = strrchr(newRealPath,'/');
+ addPoint = strrchr(newRealPath,'/');
if ( addPoint != NULL )
strcpy(&addPoint[1], &path[17]);
else
strcpy(newRealPath, &path[17]);
- image = loadPhase4(newRealPath, orgPath, context, exceptions);
+ image = loadPhase4(newRealPath, orgPath, context, cacheIndex, exceptions);
if ( image != NULL )
return image;
}
}
else if ( (strncmp(path, "@loader_path/", 13) == 0) && (context.origin != NULL) ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// @loader_path cannot be used from the main executable of a setuid process rdar://problem/4589305
- if ( sProcessIsRestricted && (strcmp(context.origin, sExecPath) == 0) && !sProcessRequiresLibraryValidation )
+ if ( gLinkContext.processIsRestricted && (strcmp(context.origin, sExecPath) == 0) )
throwf("unsafe use of @loader_path in %s with restricted binary", context.origin);
+#endif
// handle @loader_path path prefix
char newPath[strlen(context.origin) + strlen(path)];
strcpy(newPath, context.origin);
strcpy(&addPoint[1], &path[13]);
else
strcpy(newPath, &path[13]);
- image = loadPhase4(newPath, orgPath, context, exceptions);
+ image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions);
if ( image != NULL )
return image;
if ( realpath(context.origin, resolvedPath) != NULL ) {
char newRealPath[strlen(resolvedPath) + strlen(path)];
strcpy(newRealPath, resolvedPath);
- char* addPoint = strrchr(newRealPath,'/');
+ addPoint = strrchr(newRealPath,'/');
if ( addPoint != NULL )
strcpy(&addPoint[1], &path[13]);
else
strcpy(newRealPath, &path[13]);
- image = loadPhase4(newRealPath, orgPath, context, exceptions);
+ image = loadPhase4(newRealPath, orgPath, context, cacheIndex, exceptions);
if ( image != NULL )
return image;
}
const char* anRPath = *it;
char newPath[strlen(anRPath) + strlen(trailingPath)+2];
strcpy(newPath, anRPath);
- strcat(newPath, "/");
+ if ( newPath[strlen(newPath)-1] != '/' )
+ strcat(newPath, "/");
strcat(newPath, trailingPath);
- image = loadPhase4(newPath, orgPath, context, exceptions);
+ image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions);
if ( gLinkContext.verboseRPaths && (exceptions != NULL) ) {
if ( image != NULL )
dyld::log("RPATH successful expansion of %s to: %s\n", orgPath, newPath);
// substitute @rpath with LD_LIBRARY_PATH
if ( sEnv.LD_LIBRARY_PATH != NULL ) {
- image = loadPhase2(trailingPath, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions);
+ image = loadPhase2(trailingPath, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, cacheIndex, exceptions);
if ( image != NULL )
return image;
}
if ( (exceptions != NULL) && (trailingPath != path) )
return NULL;
}
- else if (sProcessIsRestricted && (path[0] != '/' ) && !sProcessRequiresLibraryValidation) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ else if ( gLinkContext.processIsRestricted && (path[0] != '/' ) ) {
throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin);
}
+#endif
- return loadPhase4(path, orgPath, context, exceptions);
+ return loadPhase4(path, orgPath, context, cacheIndex, exceptions);
}
// try search paths
-static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context,
+static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context,
const char* const frameworkPaths[], const char* const libraryPaths[],
- std::vector<const char*>* exceptions)
+ unsigned& cacheIndex, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
strcat(npath, "/");
strcat(npath, frameworkPartialPath);
//dyld::log("dyld: fallback framework path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, npath);
- image = loadPhase4(npath, orgPath, context, exceptions);
+ image = loadPhase4(npath, orgPath, context, cacheIndex, exceptions);
if ( image != NULL )
return image;
}
strcat(libpath, "/");
strcat(libpath, libraryLeafName);
//dyld::log("dyld: fallback library path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, libpath);
- image = loadPhase4(libpath, orgPath, context, exceptions);
+ image = loadPhase4(libpath, orgPath, context, cacheIndex, exceptions);
if ( image != NULL )
return image;
}
}
// try search overrides and fallbacks
-static ImageLoader* loadPhase1(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase1(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
// handle LD_LIBRARY_PATH environment variables that force searching
if ( context.useLdLibraryPath && (sEnv.LD_LIBRARY_PATH != NULL) ) {
- image = loadPhase2(path, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions);
+ image = loadPhase2(path, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, cacheIndex,exceptions);
if ( image != NULL )
return image;
}
// handle DYLD_ environment variables that force searching
if ( context.useSearchPaths && ((sEnv.DYLD_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_LIBRARY_PATH != NULL)) ) {
- image = loadPhase2(path, orgPath, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, exceptions);
+ image = loadPhase2(path, orgPath, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, cacheIndex, exceptions);
if ( image != NULL )
return image;
}
// try raw path
- image = loadPhase3(path, orgPath, context, exceptions);
+ image = loadPhase3(path, orgPath, context, cacheIndex, exceptions);
if ( image != NULL )
return image;
if ( (fallbackLibraryPaths != NULL) && !context.useFallbackPaths )
fallbackLibraryPaths = NULL;
if ( !context.dontLoad && (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (fallbackLibraryPaths != NULL)) ) {
- image = loadPhase2(path, orgPath, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, fallbackLibraryPaths, exceptions);
+ image = loadPhase2(path, orgPath, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, fallbackLibraryPaths, cacheIndex, exceptions);
if ( image != NULL )
return image;
}
}
// try root substitutions
-static ImageLoader* loadPhase0(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase0(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
+#if SUPPORT_ROOT_PATH
// handle DYLD_ROOT_PATH which forces absolute paths to use a new root
if ( (gLinkContext.rootPaths != NULL) && (path[0] == '/') ) {
for(const char* const* rootPath = gLinkContext.rootPaths ; *rootPath != NULL; ++rootPath) {
char newPath[strlen(*rootPath) + strlen(path)+2];
strcpy(newPath, *rootPath);
strcat(newPath, path);
- ImageLoader* image = loadPhase1(newPath, orgPath, context, exceptions);
+ ImageLoader* image = loadPhase1(newPath, orgPath, context, cacheIndex, exceptions);
if ( image != NULL )
return image;
}
}
+#endif
// try raw path
- return loadPhase1(path, orgPath, context, exceptions);
+ return loadPhase1(path, orgPath, context, cacheIndex, exceptions);
}
#if DYLD_SHARED_CACHE_SUPPORT
// the path. Either time, if an image is found, the phases all unwind without checking
// for other paths.
//
-ImageLoader* load(const char* path, const LoadContext& context)
+ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheIndex)
{
CRSetCrashLogMessage2(path);
const char* orgPath = path;
+ cacheIndex = UINT32_MAX;
//dyld::log("%s(%s)\n", __func__ , path);
char realPath[PATH_MAX];
}
// try all path permutations and check against existing loaded images
- ImageLoader* image = loadPhase0(path, orgPath, context, NULL);
+
+ ImageLoader* image = loadPhase0(path, orgPath, context, cacheIndex, NULL);
if ( image != NULL ) {
CRSetCrashLogMessage2(NULL);
return image;
// try all path permutations and try open() until first success
std::vector<const char*> exceptions;
- image = loadPhase0(path, orgPath, context, &exceptions);
+ image = loadPhase0(path, orgPath, context, cacheIndex, &exceptions);
#if __IPHONE_OS_VERSION_MIN_REQUIRED && DYLD_SHARED_CACHE_SUPPORT && !TARGET_IPHONE_SIMULATOR
// <rdar://problem/16704628> support symlinks on disk to a path in dyld shared cache
if ( (image == NULL) && cacheablePath(path) && !context.dontLoad ) {
}
-static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[],
- int codeSignatureMappingIndex, long slide, void* slideInfo, unsigned long slideInfoSize)
+static void rebaseChain(uint8_t* pageContent, uint16_t startOffset, uintptr_t slideAmount, const dyld_cache_slide_info2* slideInfo)
+{
+ const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask);
+ const uintptr_t valueMask = ~deltaMask;
+ const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add);
+ const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
+
+ uint32_t pageOffset = startOffset;
+ uint32_t delta = 1;
+ while ( delta != 0 ) {
+ uint8_t* loc = pageContent + pageOffset;
+ uintptr_t rawValue = *((uintptr_t*)loc);
+ delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
+ uintptr_t value = (rawValue & valueMask);
+ if ( value != 0 ) {
+ value += valueAdd;
+ value += slideAmount;
+ }
+ *((uintptr_t*)loc) = value;
+ //dyld::log(" pageOffset=0x%03X, loc=%p, org value=0x%08llX, new value=0x%08llX, delta=0x%X\n", pageOffset, loc, (uint64_t)rawValue, (uint64_t)value, delta);
+ pageOffset += delta;
+ }
+}
+
+
+static void loadAndCheckCodeSignature(int fd, uint32_t count, const shared_file_mapping_np mappings[],
+ off_t codeSignatureOffset, size_t codeSignatureSize,
+ const void *firstPages, size_t firstPagesSize)
{
// register code signature blob for whole dyld cache
- if ( codeSignatureMappingIndex != -1 ) {
- fsignatures_t siginfo;
- siginfo.fs_file_start = 0; // cache always starts at beginning of file
- siginfo.fs_blob_start = (void*)mappings[codeSignatureMappingIndex].sfm_file_offset;
- siginfo.fs_blob_size = mappings[codeSignatureMappingIndex].sfm_size;
- int result = fcntl(fd, F_ADDFILESIGS, &siginfo);
+ fsignatures_t siginfo;
+ siginfo.fs_file_start = 0; // cache always starts at beginning of file
+ siginfo.fs_blob_start = (void*)codeSignatureOffset;
+ siginfo.fs_blob_size = codeSignatureSize;
+
+ int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo);
// <rdar://problem/12891874> don't warn in chrooted case because mapping syscall is about to fail too
- if ( (result == -1) && gLinkContext.verboseMapping )
+ if ( result == -1 ) {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ throwf("code signature registration for shared cache failed with errno=%d\n", errno);
+#else
+ if ( gLinkContext.verboseMapping )
dyld::log("dyld: code signature registration for shared cache failed with errno=%d\n", errno);
+#endif
}
+ uint64_t codeSignedLength = siginfo.fs_file_start;
+ for (uint32_t i = 0; i < count; ++i) {
+ if ( (mappings[i].sfm_size > codeSignedLength) || (mappings[i].sfm_file_offset > (codeSignedLength - mappings[i].sfm_size)) )
+ throw "dyld shared cache mapping not covered by code signature";
+ }
+
+ void *fdata = xmmap(NULL, firstPagesSize, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0);
+ if ( fdata == MAP_FAILED )
+ throwf("mmap() errno=%d validating first page of shared cache", errno);
+ if ( memcmp(fdata, firstPages, firstPagesSize) != 0 )
+ throwf("mmap() page compare failed for shared cache");
+ munmap(fdata, firstPagesSize);
+}
+static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[],
+ long slide, void* slideInfo, unsigned long slideInfoSize)
+{
if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) {
return syscall(438, fd, count, mappings, slide, slideInfo, slideInfoSize);
}
}
// update all __DATA pages with slide info
- if ( slide != 0 ) {
+ const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo;
+ if ( slideInfoHeader->version == 2 ) {
+ const dyld_cache_slide_info2* slideHeader = (dyld_cache_slide_info2*)slideInfo;
+ const uint32_t page_size = slideHeader->page_size;
+ const uint16_t* page_starts = (uint16_t*)((long)(slideInfo) + slideHeader->page_starts_offset);
+ const uint16_t* page_extras = (uint16_t*)((long)(slideInfo) + slideHeader->page_extras_offset);
+ const uintptr_t dataPagesStart = mappings[1].sfm_address;
+ for (int i=0; i < slideHeader->page_starts_count; ++i) {
+ uint8_t* page = (uint8_t*)(long)(dataPagesStart + (page_size*i));
+ uint16_t pageEntry = page_starts[i];
+ //dyld::log("page[%d]: page_starts[i]=0x%04X\n", i, pageEntry);
+ if ( pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE )
+ continue;
+ if ( pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
+ uint16_t chainIndex = (pageEntry & 0x3FFF);
+ bool done = false;
+ while ( !done ) {
+ uint16_t info = page_extras[chainIndex];
+ uint16_t pageStartOffset = (info & 0x3FFF)*4;
+ //dyld::log(" chain[%d] pageOffset=0x%03X\n", chainIndex, pageStartOffset);
+ rebaseChain(page, pageStartOffset, slide, slideHeader);
+ done = (info & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
+ ++chainIndex;
+ }
+ }
+ else {
+ uint32_t pageOffset = pageEntry * 4;
+ //dyld::log(" start pageOffset=0x%03X\n", pageOffset);
+ rebaseChain(page, pageOffset, slide, slideHeader);
+ }
+ }
+ }
+ else if ( slide != 0 ) {
const uintptr_t dataPagesStart = mappings[1].sfm_address;
- const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo;
const uint16_t* toc = (uint16_t*)((long)(slideInfoHeader) + slideInfoHeader->toc_offset);
const uint8_t* entries = (uint8_t*)((long)(slideInfoHeader) + slideInfoHeader->entries_offset);
for(uint32_t i=0; i < slideInfoHeader->toc_count; ++i) {
}
#endif
strlcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, MAXPATHLEN);
- if ( gLinkContext.verboseMapping )
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ struct stat enableStatBuf;
+ struct stat devCacheStatBuf;
+ struct stat prodCacheStatBuf;
+ if ( ((my_stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0)
+ && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE)
+ && (my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME DYLD_SHARED_CACHE_DEVELOPMENT_EXT, &devCacheStatBuf) == 0))
+ || (my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &prodCacheStatBuf) != 0))
+ strlcat(path, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, MAXPATHLEN);
+#endif
+ if ( gLinkContext.verboseMapping )
dyld::log("dyld: Mapping%s shared cache from %s\n", (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion) ? " private": "", path);
return my_open(path, O_RDONLY, 0);
}
const uint64_t space = (SHARED_REGION_BASE + SHARED_REGION_SIZE) - highAddress;
// choose new random slide
+#if __arm__
+ // <rdar://problem/20848977> change shared cache slide for 32-bit arm to always be 16k aligned
+ long slide = ((arc4random() % space) & (-16384));
+#else
long slide = dyld_page_trunc(arc4random() % space);
+#endif
//dyld::log("slideSpace=0x%0llX\n", space);
//dyld::log("slide=0x%0lX\n", slide);
-
+
// update mappings
for(uint32_t i=0; i < mappingsCount; ++i) {
mappings[i].sfm_address += slide;
const uint8_t* preferedLoadAddress = (uint8_t*)(long)(mappings[0].address);
sSharedCacheSlide = loadedAddress - preferedLoadAddress;
dyld::gProcessInfo->sharedCacheSlide = sSharedCacheSlide;
+ dyld::gProcessInfo->sharedCacheBaseAddress = cacheBaseAddress;
//dyld::log("sSharedCacheSlide=0x%08lX, loadedAddress=%p, preferedLoadAddress=%p\n", sSharedCacheSlide, loadedAddress, preferedLoadAddress);
}
// if cache has a uuid, copy it
}
// verbose logging
if ( gLinkContext.verboseMapping ) {
- dyld::log("dyld: re-using existing shared cache mapping\n");
+ dyld::log("dyld: re-using existing %s shared cache mapping\n", (header->cacheType == kDyldSharedCacheTypeDevelopment ? "development" : "production"));
+ }
+ if (header->mappingOffset >= 0x68) {
+ dyld_kernel_image_info_t kernelCacheInfo;
+ memcpy(&kernelCacheInfo.uuid[0], &sSharedCache->uuid[0], sizeof(uuid_t));
+ kernelCacheInfo.load_addr = (uint64_t)sSharedCache;
+ kernelCacheInfo.fsobjid.fid_objno = 0;
+ kernelCacheInfo.fsobjid.fid_generation = 0;
+ kernelCacheInfo.fsid.val[0] = 0;
+ kernelCacheInfo.fsid.val[0] = 0;
+ task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, false, false);
}
}
else {
if ( strcmp(header->magic, magic) == 0 ) {
const dyld_cache_mapping_info* const fileMappingsStart = (dyld_cache_mapping_info*)&firstPages[header->mappingOffset];
const dyld_cache_mapping_info* const fileMappingsEnd = &fileMappingsStart[header->mappingCount];
- shared_file_mapping_np mappings[header->mappingCount+1]; // add room for code-sig
+ #if __IPHONE_OS_VERSION_MIN_REQUIRED
+ if ( (header->mappingCount != 3)
+ || (header->mappingOffset > 256)
+ || (fileMappingsStart[0].fileOffset != 0)
+ || (fileMappingsStart[0].address != SHARED_REGION_BASE)
+ || ((fileMappingsStart[0].address + fileMappingsStart[0].size) > fileMappingsStart[1].address)
+ || ((fileMappingsStart[1].address + fileMappingsStart[1].size) > fileMappingsStart[2].address)
+ || ((fileMappingsStart[0].fileOffset + fileMappingsStart[0].size) != fileMappingsStart[1].fileOffset)
+ || ((fileMappingsStart[1].fileOffset + fileMappingsStart[1].size) != fileMappingsStart[2].fileOffset) )
+ throw "dyld shared cache file is invalid";
+ #endif
+ shared_file_mapping_np mappings[header->mappingCount];
unsigned int mappingCount = header->mappingCount;
- int codeSignatureMappingIndex = -1;
int readWriteMappingIndex = -1;
int readOnlyMappingIndex = -1;
// validate that the cache file has not been truncated
if ( signatureSize == 0 )
signatureSize = stat_buf.st_size - header->codeSignatureOffset;
if ( signatureSize != 0 ) {
- int linkeditMapping = mappingCount-1;
- codeSignatureMappingIndex = mappingCount++;
- mappings[codeSignatureMappingIndex].sfm_address = mappings[linkeditMapping].sfm_address + mappings[linkeditMapping].sfm_size;
#if __arm__ || __arm64__
- mappings[codeSignatureMappingIndex].sfm_size = (signatureSize+16383) & (-16384);
+ size_t alignedSignatureSize = (signatureSize+16383) & (-16384);
#else
- mappings[codeSignatureMappingIndex].sfm_size = (signatureSize+4095) & (-4096);
+ size_t alignedSignatureSize = (signatureSize+4095) & (-4096);
#endif
- mappings[codeSignatureMappingIndex].sfm_file_offset = header->codeSignatureOffset;
- mappings[codeSignatureMappingIndex].sfm_max_prot = VM_PROT_READ;
- mappings[codeSignatureMappingIndex].sfm_init_prot = VM_PROT_READ;
+ // <rdar://problem/23188073> validate code signature covers entire shared cache
+ loadAndCheckCodeSignature(fd, mappingCount, mappings, header->codeSignatureOffset, alignedSignatureSize, firstPages, sizeof(firstPages));
+ }
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ else {
+ throw "dyld shared cache file not code signed";
}
+#endif
}
#if __MAC_OS_X_VERSION_MIN_REQUIRED
// sanity check that /usr/lib/libSystem.B.dylib stat() info matches cache
}
if ( goodCache ) {
long cacheSlide = 0;
- void* slideInfo = NULL;
- uint64_t slideInfoSize = 0;
+ void* slideInfo = (void*)(long)(mappings[readOnlyMappingIndex].sfm_address + (header->slideInfoOffset - mappings[readOnlyMappingIndex].sfm_file_offset));;
+ uint64_t slideInfoSize = header->slideInfoSize;
// check if shared cache contains slid info
- if ( header->slideInfoSize != 0 ) {
+ if ( slideInfoSize != 0 ) {
// <rdar://problem/8611968> don't slide shared cache if ASLR disabled (main executable didn't slide)
- if ( sMainExecutable->isPositionIndependentExecutable() && (sMainExecutable->getSlide() == 0) )
+ if ( sMainExecutable->isPositionIndependentExecutable() && (sMainExecutable->getSlide() == 0) ) {
cacheSlide = 0;
+ }
else {
// generate random slide amount
cacheSlide = pickCacheSlide(mappingCount, mappings);
- slideInfo = (void*)(long)(mappings[readOnlyMappingIndex].sfm_address + (header->slideInfoOffset - mappings[readOnlyMappingIndex].sfm_file_offset));
- slideInfoSize = header->slideInfoSize;
- // add VM_PROT_SLIDE bit to __DATA area of cache
- mappings[readWriteMappingIndex].sfm_max_prot |= VM_PROT_SLIDE;
- mappings[readWriteMappingIndex].sfm_init_prot |= VM_PROT_SLIDE;
}
+
+ slideInfo = (void*)((uint8_t*)slideInfo + cacheSlide);
+ // add VM_PROT_SLIDE bit to __DATA area of cache
+ mappings[readWriteMappingIndex].sfm_max_prot |= VM_PROT_SLIDE;
+ mappings[readWriteMappingIndex].sfm_init_prot |= VM_PROT_SLIDE;
}
if ( gLinkContext.verboseMapping ) {
dyld::log("dyld: calling _shared_region_map_and_slide_np() with regions:\n");
dyld::log(" address=0x%08llX, size=0x%08llX, fileOffset=0x%08llX\n", mappings[i].sfm_address, mappings[i].sfm_size, mappings[i].sfm_file_offset);
}
}
- if (_shared_region_map_and_slide_np(fd, mappingCount, mappings, codeSignatureMappingIndex, cacheSlide, slideInfo, slideInfoSize) == 0) {
+
+ if (_shared_region_map_and_slide_np(fd, mappingCount, mappings, cacheSlide, slideInfo, slideInfoSize) == 0) {
// successfully mapped cache into shared region
sSharedCache = (dyld_cache_header*)mappings[0].sfm_address;
sSharedCacheSlide = cacheSlide;
dyld::gProcessInfo->sharedCacheSlide = cacheSlide;
+ dyld::gProcessInfo->sharedCacheBaseAddress = mappings[0].sfm_address;
//dyld::log("sSharedCache=%p sSharedCacheSlide=0x%08lX\n", sSharedCache, sSharedCacheSlide);
// if cache has a uuid, copy it
if ( header->mappingOffset >= 0x68 ) {
+ const bool privateSharedCache = gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion;
memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16);
+ dyld_kernel_image_info_t kernelCacheInfo;
+ memcpy(&kernelCacheInfo.uuid[0], &sSharedCache->uuid[0], sizeof(uuid_t));
+ kernelCacheInfo.load_addr = (uint64_t)sSharedCache;
+ kernelCacheInfo.fsobjid.fid_objno = 0;
+ kernelCacheInfo.fsobjid.fid_generation = 0;
+ kernelCacheInfo.fsid.val[0] = 0;
+ kernelCacheInfo.fsid.val[0] = 0;
+ if (privateSharedCache) {
+ kernelCacheInfo.fsobjid = *(fsobj_id_t*)(&stat_buf.st_ino);
+ struct statfs statfs_buf;
+ if ( fstatfs(fd, &statfs_buf) == 0 ) {
+ kernelCacheInfo.fsid = statfs_buf.f_fsid;
+ }
+ }
+ task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, false, privateSharedCache);
}
}
else {
#if __IPHONE_OS_VERSION_MIN_REQUIRED
- throw "dyld shared cache could not be mapped";
+ throwf("dyld shared cache could not be mapped. errno=%d, slide=0x%08lX, slideInfo=%p, slideInfoSize=0x%08llX, mappingCount=%u, "
+ "address/size/off/init/max [0]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X, [1]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X, [2]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X",
+ errno, cacheSlide, slideInfo, slideInfoSize, mappingCount,
+ mappings[0].sfm_address, mappings[0].sfm_size, mappings[0].sfm_file_offset, mappings[0].sfm_init_prot, mappings[0].sfm_max_prot,
+ mappings[1].sfm_address, mappings[1].sfm_size, mappings[1].sfm_file_offset, mappings[1].sfm_init_prot, mappings[1].sfm_max_prot,
+ mappings[2].sfm_address, mappings[2].sfm_size, mappings[2].sfm_file_offset, mappings[2].sfm_init_prot, mappings[2].sfm_max_prot);
#endif
if ( gLinkContext.verboseMapping )
dyld::log("dyld: shared cached file could not be mapped\n");
dyld::log(" 0x%08llX->0x%08llX (code signature)\n", codeBlobStart, codeBlobStart+signatureSize);
}
}
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
- // check for file that enables dyld shared cache dylibs to be overridden
- struct stat enableStatBuf;
- // check file size to determine if correct file is in place.
- // See <rdar://problem/13591370> Need a way to disable roots without removing /S/L/C/com.apple.dyld/enable...
- sDylibsOverrideCache = ( (my_stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0)
- && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) );
-#endif
+ #if SUPPORT_ACCELERATE_TABLES
+ if ( !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCache->mappingOffset > 0x80) && (sSharedCache->accelerateInfoAddr != 0) ) {
+ sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(sSharedCache, sSharedCacheSlide, gLinkContext);
+ }
+ #endif
}
}
#endif // #if DYLD_SHARED_CACHE_SUPPORT
if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated )
(*func)(image->machHeader(), image->getSlide());
}
+#if SUPPORT_ACCELERATE_TABLES
+ if ( sAllCacheImagesProxy != NULL ) {
+ dyld_image_info infos[allImagesCount()+1];
+ unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(dyld_image_state_bound, true, infos);
+ for (unsigned i=0; i < cacheCount; ++i) {
+ (*func)(infos[i].imageLoadAddress, sSharedCacheSlide);
+ }
+ }
+#endif
}
void registerRemoveCallback(ImageCallback func)
return error_string;
}
-
void halt(const char* message)
{
dyld::log("dyld: %s\n", message);
setErrorMessage(message);
- uintptr_t terminationFlags = 0;
- if ( !gLinkContext.startedInitializingMainExecutable )
- terminationFlags = 1;
- setAlImageInfosHalt(error_string, terminationFlags);
- dyld_fatal_error(error_string);
+ dyld::gProcessInfo->errorMessage = error_string;
+ if ( !gLinkContext.startedInitializingMainExecutable )
+ dyld::gProcessInfo->terminationFlags = 1;
+ else
+ dyld::gProcessInfo->terminationFlags = 0;
+
+ char payloadBuffer[EXIT_REASON_PAYLOAD_MAX_LEN];
+ dyld_abort_payload* payload = (dyld_abort_payload*)payloadBuffer;
+ payload->version = 1;
+ payload->flags = gLinkContext.startedInitializingMainExecutable ? 0 : 1;
+ payload->targetDylibPathOffset = 0;
+ payload->clientPathOffset = 0;
+ payload->symbolOffset = 0;
+ int payloadSize = sizeof(dyld_abort_payload);
+
+ if ( dyld::gProcessInfo->errorTargetDylibPath != NULL ) {
+ payload->targetDylibPathOffset = payloadSize;
+ payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorTargetDylibPath, sizeof(payloadBuffer)-payloadSize) + 1;
+ }
+ if ( dyld::gProcessInfo->errorClientOfDylibPath != NULL ) {
+ payload->clientPathOffset = payloadSize;
+ payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorClientOfDylibPath, sizeof(payloadBuffer)-payloadSize) + 1;
+ }
+ if ( dyld::gProcessInfo->errorSymbol != NULL ) {
+ payload->symbolOffset = payloadSize;
+ payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorSymbol, sizeof(payloadBuffer)-payloadSize) + 1;
+ }
+ char truncMessage[EXIT_REASON_USER_DESC_MAX_LEN];
+ strlcpy(truncMessage, message, EXIT_REASON_USER_DESC_MAX_LEN);
+ abort_with_payload(OS_REASON_DYLD, dyld::gProcessInfo->errorKind ? dyld::gProcessInfo->errorKind : DYLD_EXIT_REASON_OTHER, payloadBuffer, payloadSize, truncMessage, 0);
}
static void setErrorStrings(unsigned errorCode, const char* errorClientOfDylibPath,
// save in cache
*imageLoaderCache = dyld::findMappedRange((uintptr_t)imageLoaderCache);
if ( *imageLoaderCache == NULL ) {
+#if SUPPORT_ACCELERATE_TABLES
+ if ( sAllCacheImagesProxy != NULL ) {
+ const mach_header* mh;
+ const char* path;
+ unsigned index;
+ if ( sAllCacheImagesProxy->addressInCache(imageLoaderCache, &mh, &path, &index) ) {
+ result = sAllCacheImagesProxy->bindLazy(lazyBindingInfoOffset, gLinkContext, mh, index);
+ if ( result == 0 ) {
+ halt("dyld: lazy symbol binding failed for image in dyld shared\n");
+ }
+ return result;
+ }
+ }
+#endif
const char* message = "fast lazy binding from unknown image";
dyld::log("dyld: %s\n", message);
halt(message);
*image = firstWeakImage;
return true;
}
-
+#if SUPPORT_ACCELERATE_TABLES
+ if ( sAllCacheImagesProxy != NULL ) {
+ if ( sAllCacheImagesProxy->flatFindSymbol(name, onlyInCoalesced, sym, image) )
+ return true;
+ }
+#endif
+
return false;
}
return false;
}
-unsigned int getCoalescedImages(ImageLoader* images[])
+
+unsigned int getCoalescedImages(ImageLoader* images[], unsigned imageIndex[])
{
unsigned int count = 0;
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( image->participatesInCoalescing() ) {
- *images++ = *it;
+ images[count] = *it;
+ imageIndex[count] = 0;
++count;
}
}
+#if SUPPORT_ACCELERATE_TABLES
+ if ( sAllCacheImagesProxy != NULL ) {
+ sAllCacheImagesProxy->appendImagesNeedingCoalescing(images, imageIndex, count);
+ }
+#endif
return count;
}
// call callback with all existing images
try {
- notifyBatchPartial(state, true, handler);
+ notifyBatchPartial(state, true, handler, false, false);
}
catch (const char* msg) {
// ignore request to abort during registration
}
}
-static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const ImageLoader::RPathChain* rpaths)
+
+void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
+{
+ // record functions to call
+ sNotifyObjCMapped = mapped;
+ sNotifyObjCInit = init;
+ sNotifyObjCUnmapped = unmapped;
+
+ // call 'mapped' function with all images mapped so far
+ try {
+ notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
+ }
+ catch (const char* msg) {
+ // ignore request to abort during registration
+ }
+}
+
+bool sharedCacheUUID(uuid_t uuid)
+{
+#if DYLD_SHARED_CACHE_SUPPORT
+ if ( sSharedCache == NULL )
+ return false;
+
+ memcpy(uuid, sSharedCache->uuid, 16);
+ return true;
+#else
+ return false;
+#endif
+}
+
+#if SUPPORT_ACCELERATE_TABLES
+
+bool dlopenFromCache(const char* path, int mode, void** handle)
+{
+ if ( sAllCacheImagesProxy == NULL )
+ return false;
+ bool result = sAllCacheImagesProxy->dlopenFromCache(gLinkContext, path, mode, handle);
+ if ( !result && (strchr(path, '/') == NULL) ) {
+ // POSIX says you can call dlopen() with a leaf name (e.g. dlopen("libz.dylb"))
+ char fallbackPath[PATH_MAX];
+ strcpy(fallbackPath, "/usr/lib/");
+ strlcat(fallbackPath, path, PATH_MAX);
+ result = sAllCacheImagesProxy->dlopenFromCache(gLinkContext, fallbackPath, mode, handle);
+ }
+ return result;
+}
+
+bool makeCacheHandle(ImageLoader* image, unsigned cacheIndex, int mode, void** result)
+{
+ if ( sAllCacheImagesProxy == NULL )
+ return false;
+ return sAllCacheImagesProxy->makeCacheHandle(gLinkContext, cacheIndex, mode, result);
+}
+
+bool isCacheHandle(void* handle)
+{
+ if ( sAllCacheImagesProxy == NULL )
+ return false;
+ return sAllCacheImagesProxy->isCacheHandle(handle, NULL, NULL);
+}
+
+bool isPathInCache(const char* path)
+{
+ if ( sAllCacheImagesProxy == NULL )
+ return false;
+ unsigned index;
+ return sAllCacheImagesProxy->hasDylib(path, &index);
+}
+
+const char* getPathFromIndex(unsigned cacheIndex)
+{
+ if ( sAllCacheImagesProxy == NULL )
+ return NULL;
+ return sAllCacheImagesProxy->getIndexedPath(cacheIndex);
+}
+
+void* dlsymFromCache(void* handle, const char* symName, unsigned index)
+{
+ if ( sAllCacheImagesProxy == NULL )
+ return NULL;
+ return sAllCacheImagesProxy->dlsymFromCache(gLinkContext, handle, symName, index);
+}
+
+bool addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index)
+{
+ if ( sAllCacheImagesProxy == NULL )
+ return false;
+ unsigned ignore;
+ return sAllCacheImagesProxy->addressInCache(address, mh, path, index ? index : &ignore);
+}
+
+bool findUnwindSections(const void* addr, dyld_unwind_sections* info)
+{
+ if ( sAllCacheImagesProxy == NULL )
+ return false;
+ return sAllCacheImagesProxy->findUnwindSections(addr, info);
+}
+
+bool dladdrFromCache(const void* address, Dl_info* info)
+{
+ if ( sAllCacheImagesProxy == NULL )
+ return false;
+ return sAllCacheImagesProxy->dladdrFromCache(address, info);
+}
+#endif
+
+static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const ImageLoader::RPathChain* rpaths, unsigned& cacheIndex)
{
dyld::LoadContext context;
context.useSearchPaths = search;
context.canBePIE = false;
context.origin = origin;
context.rpath = rpaths;
- return load(libraryName, context);
+ return load(libraryName, context, cacheIndex);
}
static const char* basename(const char* path)
#endif
gLinkContext.findImageContainingAddress = &findImageContainingAddress;
gLinkContext.addDynamicReference = &addDynamicReference;
+#if SUPPORT_ACCELERATE_TABLES
+ gLinkContext.notifySingleFromCache = ¬ifySingleFromCache;
+ gLinkContext.getPreInitNotifyHandler= &getPreInitNotifyHandler;
+ gLinkContext.getBoundBatchHandler = &getBoundBatchHandler;
+#endif
gLinkContext.bindingOptions = ImageLoader::kBindingNone;
gLinkContext.argc = argc;
gLinkContext.argv = argv;
return false;
}
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+static bool isFairPlayEncrypted(const macho_header* mh)
+{
+ const uint32_t cmd_count = mh->ncmds;
+ const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd == LC_ENCRYPT_COMMAND ) {
+ const encryption_info_command* enc = (encryption_info_command*)cmd;
+ return (enc->cryptid != 0);
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+
+ return false;
+}
+#endif
#if SUPPORT_VERSIONED_PATHS
}
#endif
-void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths)
+void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex)
{
// add to list of known images. This did not happen at creation time for bundles
if ( image->isBundle() && !image->isLinked() )
// process images
try {
- image->link(gLinkContext, forceLazysBound, false, neverUnload, loaderRPaths);
+ const char* path = image->getPath();
+#if SUPPORT_ACCELERATE_TABLES
+ if ( image == sAllCacheImagesProxy )
+ path = sAllCacheImagesProxy->getIndexedPath(cacheIndex);
+#endif
+ image->link(gLinkContext, forceLazysBound, false, neverUnload, loaderRPaths, path);
}
catch (const char* msg) {
garbageCollectImages();
void runInitializers(ImageLoader* image)
{
// do bottom up initialization
- ImageLoader::InitializerTimingList initializerTimes[sAllImages.size()];
+ ImageLoader::InitializerTimingList initializerTimes[allImagesCount()];
initializerTimes[0].count = 0;
image->runInitializers(gLinkContext, initializerTimes[0]);
}
// sweep phase: mark as in-use, images reachable from never-unload or in-use image
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
- if ( (image->dlopenCount() != 0) || image->neverUnload() ) {
+ if ( (image->dlopenCount() != 0) || image->neverUnload() || (image == sMainExecutable) ) {
OSSpinLockLock(&sDynamicReferencesLock);
image->markedUsedRecursive(sDynamicReferences);
OSSpinLockUnlock(&sDynamicReferencesLock);
ImageLoader* deadImages[sAllImages.size()];
unsigned deadCount = 0;
int maxRangeCount = 0;
- unsigned i = 0;
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( ! image->isMarkedInUse() ) {
- deadImages[i++] = image;
+ deadImages[deadCount++] = image;
if (gLogAPIs) dyld::log("dlclose(), found unused image %p %s\n", image, image->getShortName());
- ++deadCount;
maxRangeCount += image->segmentCount();
}
}
}
-void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths)
+void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex)
{
try {
if ( image->isBundle() )
sBundleBeingLoaded = image; // hack
- image->link(gLinkContext, false, true, false, loaderRPaths);
+ const char* path = image->getPath();
+#if SUPPORT_ACCELERATE_TABLES
+ if ( image == sAllCacheImagesProxy )
+ path = sAllCacheImagesProxy->getIndexedPath(cacheIndex);
+#endif
+ image->link(gLinkContext, false, true, false, loaderRPaths, path);
}
catch (const char* msg) {
preflight_finally(image);
static void loadInsertedDylib(const char* path)
{
ImageLoader* image = NULL;
+ unsigned cacheIndex;
try {
LoadContext context;
context.useSearchPaths = false;
context.canBePIE = false;
context.origin = NULL; // can't use @loader_path with DYLD_INSERT_LIBRARIES
context.rpath = NULL;
- image = load(path, context);
+ image = load(path, context, cacheIndex);
}
catch (const char* msg) {
#if TARGET_IPHONE_SIMULATOR
dyld::log("dyld: warning: could not load inserted library '%s' because %s\n", path, msg);
#else
- if ( sProcessRequiresLibraryValidation )
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( gLinkContext.processUsingLibraryValidation )
dyld::log("dyld: warning: could not load inserted library '%s' into library validated process because %s\n", path, msg);
else
+#endif
halt(dyld::mkstringf("could not load inserted library '%s' because %s\n", path, msg));
#endif
}
}
}
-static bool processRestricted(const macho_header* mainExecutableMH, bool* ignoreEnvVars, bool* processRequiresLibraryValidation)
-{
-#if TARGET_IPHONE_SIMULATOR
- gLinkContext.codeSigningEnforced = true;
-#else
- // ask kernel if code signature of program makes it restricted
+
+//
+// Sets:
+// sEnvMode
+// gLinkContext.requireCodeSignature
+// gLinkContext.processIsRestricted // Mac OS X only
+// gLinkContext.processUsingLibraryValidation // Mac OS X only
+//
+static void configureProcessRestrictions(const macho_header* mainExecutableMH)
+{
uint32_t flags;
+#if TARGET_IPHONE_SIMULATOR
+ sEnvMode = envAll;
+ gLinkContext.requireCodeSignature = true;
+#elif __IPHONE_OS_VERSION_MIN_REQUIRED
+ sEnvMode = envNone;
+ gLinkContext.requireCodeSignature = true;
if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
- if (flags & CS_REQUIRE_LV)
- *processRequiresLibraryValidation = true;
-
- #if __MAC_OS_X_VERSION_MIN_REQUIRED
if ( flags & CS_ENFORCEMENT ) {
- gLinkContext.codeSigningEnforced = true;
+ if ( flags & CS_GET_TASK_ALLOW ) {
+ // Xcode built app for Debug allowed to use DYLD_* variables
+ sEnvMode = envAll;
+ }
+ else {
+ // Development kernel can use DYLD_PRINT_* variables on any FairPlay encrypted app
+ uint32_t secureValue = 0;
+ size_t secureValueSize = sizeof(secureValue);
+ if ( (sysctlbyname("kern.secure_kernel", &secureValue, &secureValueSize, NULL, 0) == 0) && (secureValue == 0) && isFairPlayEncrypted(mainExecutableMH) ) {
+ sEnvMode = envPrintOnly;
+ }
+ }
}
+ else {
+ // Development kernel can run unsigned code
+ sEnvMode = envAll;
+ gLinkContext.requireCodeSignature = false;
+ }
+ }
+ if ( issetugid() ) {
+ sEnvMode = envNone;
+ }
+#elif __MAC_OS_X_VERSION_MIN_REQUIRED
+ sEnvMode = envAll;
+ gLinkContext.requireCodeSignature = false;
+ gLinkContext.processIsRestricted = false;
+ gLinkContext.processUsingLibraryValidation = false;
+ // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
+ if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
+ gLinkContext.processIsRestricted = true;
+ }
+ if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
+ // On OS X CS_RESTRICT means the program was signed with entitlements
if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0) ) {
- sRestrictedReason = restrictedByEntitlements;
- return true;
+ gLinkContext.processIsRestricted = true;
}
- #else
- if ((flags & CS_ENFORCEMENT) && !(flags & CS_GET_TASK_ALLOW)) {
- *ignoreEnvVars = true;
+ // Library Validation loosens searching but requires everything to be code signed
+ if ( flags & CS_REQUIRE_LV ) {
+ gLinkContext.processIsRestricted = false;
+ //gLinkContext.requireCodeSignature = true;
+ gLinkContext.processUsingLibraryValidation = true;
}
- gLinkContext.codeSigningEnforced = true;
- #endif
}
#endif
-
- // all processes with setuid or setgid bit set are restricted
- if ( issetugid() ) {
- sRestrictedReason = restrictedBySetGUid;
- return true;
- }
-
- // <rdar://problem/13158444&13245742> Respect __RESTRICT,__restrict section for root processes
- if ( hasRestrictedSegment(mainExecutableMH) ) {
- // existence of __RESTRICT/__restrict section make process restricted
- sRestrictedReason = restrictedBySegment;
- return true;
- }
- return false;
}
bool processIsRestricted()
{
- return sProcessIsRestricted;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ return gLinkContext.processIsRestricted;
+#else
+ return false;
+#endif
}
}
}
+void notifyKernelAboutDyld()
+{
+ const struct macho_header* mh = (macho_header*)&__dso_handle;
+ const uint32_t cmd_count = mh->ncmds;
+ const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header));
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_UUID: {
+ // Add dyld to the kernel image info
+ uuid_command* uc = (uuid_command*)cmd;
+ dyld_kernel_image_info_t kernelInfo;
+ memcpy(kernelInfo.uuid, uc->uuid, 16);
+ kernelInfo.load_addr = (uint64_t)mh;
+ kernelInfo.fsobjid.fid_objno = 0;
+ kernelInfo.fsobjid.fid_generation = 0;
+ kernelInfo.fsid.val[0] = 0;
+ kernelInfo.fsid.val[1] = 0;
+ task_register_dyld_image_infos(mach_task_self(), &kernelInfo, 1);
+ return;
+ }
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+}
+
#if __MAC_OS_X_VERSION_MIN_REQUIRED
typedef int (*open_proc_t)(const char*, int, int);
typedef int (*fcntl_proc_t)(int, int, void*);
typedef int (*ioctl_proc_t)(int, unsigned long, void*);
static void* getProcessInfo() { return dyld::gProcessInfo; }
static SyscallHelpers sSysCalls = {
- 4,
+ 7,
// added in version 1
(open_proc_t)&open,
&close,
&closedir,
// added in version 4
&coresymbolication_load_notifier,
- &coresymbolication_unload_notifier
+ &coresymbolication_unload_notifier,
+ // Added in version 5
+ &proc_regionfilename,
+ &getpid,
+ &mach_port_insert_right,
+ &mach_port_allocate,
+ &mach_msg,
+ // Added in version 6
+ &abort_with_payload,
+ // Added in version 7
+ &task_register_dyld_image_infos,
+ &task_unregister_dyld_image_infos,
+ &task_get_dyld_image_infos,
+ &task_register_dyld_shared_cache_image_info,
+ &task_register_dyld_set_dyld_state,
+ &task_register_dyld_get_process_state
};
__attribute__((noinline))
-static uintptr_t useSimulatorDyld(int fd, const macho_header* mainExecutableMH, const char* dyldPath,
- int argc, const char* argv[], const char* envp[], const char* apple[], uintptr_t* startGlue)
+static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH, const char* dyldPath,
+ int argc, const char* argv[], const char* envp[], const char* apple[],
+ uintptr_t* startGlue, uintptr_t* mainAddr)
{
*startGlue = 0;
+ *mainAddr = 0;
// <rdar://problem/25311921> simulator does not support restricted processes
uint32_t flags;
if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) == -1 )
- return 0;
+ return "csops() failed";
if ( (flags & CS_RESTRICT) == CS_RESTRICT )
- return 0;
+ return "dyld_sim cannot be loaded in a restricted process";
if ( issetugid() )
- return 0;
+ return "dyld_sim cannot be loaded in a setuid process";
if ( hasRestrictedSegment(mainExecutableMH) )
- return 0;
+ return "dyld_sim cannot be loaded in a restricted process";
- // verify simulator dyld file is owned by root
+ // get file size of dyld_sim
struct stat sb;
if ( fstat(fd, &sb) == -1 )
- return 0;
+ return "stat(dyld_sim) failed";
- // read first page of dyld file
+ // read first page of dyld_sim file
uint8_t firstPage[4096];
if ( pread(fd, firstPage, 4096, 0) != 4096 )
- return 0;
-
+ return "pread(dyld_sim) failed";
+
// if fat file, pick matching slice
uint64_t fileOffset = 0;
uint64_t fileLength = sb.st_size;
const fat_header* fileStartAsFat = (fat_header*)firstPage;
if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
if ( !fatFindBest(fileStartAsFat, &fileOffset, &fileLength) )
- return 0;
+ return "no matching arch in dyld_sim";
// re-read buffer from start of mach-o slice in fat file
if ( pread(fd, firstPage, 4096, fileOffset) != 4096 )
- return 0;
+ return "pread(dyld_sim) failed";
}
else if ( !isCompatibleMachO(firstPage, dyldPath) ) {
- return 0;
+ return "dyld_sim not compatible mach-o";
}
// calculate total size of dyld segments
uintptr_t mappingSize = 0;
uintptr_t preferredLoadAddress = 0;
const uint32_t cmd_count = mh->ncmds;
+ if ( mh->sizeofcmds > 4096 )
+ return "dyld_sim load commands to large";
if ( (sizeof(macho_header) + mh->sizeofcmds) > 4096 )
- return 0;
+ return "dyld_sim load commands to large";
const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
const struct load_command* const endCmds = (struct load_command*)(((char*)mh) + sizeof(macho_header) + mh->sizeofcmds);
const struct load_command* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
uint32_t cmdLength = cmd->cmdsize;
if ( cmdLength < 8 )
- return 0;
+ return "dyld_sim load command too small";
const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength);
if ( (nextCmd > endCmds) || (nextCmd < cmd) )
- return 0;
+ return "dyld_sim load command too large";
switch (cmd->cmd) {
case LC_SEGMENT_COMMAND:
{
struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
if ( seg->vmaddr + seg->vmsize < seg->vmaddr )
- return 0;
+ return "dyld_sim seg wraps address space";
if ( seg->vmsize < seg->filesize )
- return 0;
+ return "dyld_sim seg vmsize too small";
+ if ( (seg->fileoff + seg->filesize) < seg->fileoff )
+ return "dyld_sim seg size wraps address space";
if ( lastSeg == NULL ) {
// first segment must be __TEXT and start at beginning of file/slice
firstSeg = seg;
if ( strcmp(seg->segname, "__TEXT") != 0 )
- return 0;
+ return "dyld_sim first segment not __TEXT";
if ( seg->fileoff != 0 )
- return 0;
+ return "dyld_sim first segment not at file offset zero";
if ( seg->filesize < (sizeof(macho_header) + mh->sizeofcmds) )
- return 0;
+ return "dyld_sim first segment smaller than load commands";
preferredLoadAddress = seg->vmaddr;
}
else {
// other sements must be continguous with previous segment and not executable
if ( lastSeg->fileoff + lastSeg->filesize != seg->fileoff )
- return 0;
+ return "dyld_sim segments not contiguous";
if ( lastSeg->vmaddr + lastSeg->vmsize != seg->vmaddr )
- return 0;
+ return "dyld_sim segments not address contiguous";
if ( (seg->initprot & VM_PROT_EXECUTE) != 0 )
- return 0;
+ return "dyld_sim non-first segment is executable";
}
mappingSize += seg->vmsize;
lastSeg = seg;
}
break;
case LC_SEGMENT_COMMAND_WRONG:
- return 0;
+ return "dyld_sim wrong load segment load command";
}
cmd = nextCmd;
}
// last segment must be named __LINKEDIT and not writable
if ( strcmp(lastSeg->segname, "__LINKEDIT") != 0 )
- return 0;
+ return "dyld_sim last segment not __LINKEDIT";
if ( lastSeg->initprot & VM_PROT_WRITE )
- return 0;
+ return "dyld_sim __LINKEDIT segment writable";
// reserve space, then mmap each segment
vm_address_t loadAddress = 0;
if ( ::vm_allocate(mach_task_self(), &loadAddress, mappingSize, VM_FLAGS_ANYWHERE) != 0 )
- return 0;
+ return "dyld_sim cannot allocate space";
cmd = cmds;
struct linkedit_data_command* codeSigCmd = NULL;
struct source_version_command* dyldVersionCmd = NULL;
void* segAddress = ::mmap((void*)requestedLoadAddress, seg->filesize, seg->initprot, MAP_FIXED | MAP_PRIVATE, fd, fileOffset + seg->fileoff);
//dyld::log("dyld_sim %s mapped at %p\n", seg->segname, segAddress);
if ( segAddress == (void*)(-1) )
- return 0;
+ return "dyld_sim mmap() of segment failed";
if ( ((uintptr_t)segAddress < loadAddress) || ((uintptr_t)segAddress+seg->filesize > loadAddress+mappingSize) )
- return 0;
+ return "dyld_sim mmap() to wrong location";
}
break;
case LC_CODE_SIGNATURE:
// must have code signature which is contained within LINKEDIT segment
if ( codeSigCmd == NULL )
- return 0;
+ return "dyld_sim not code signed";
if ( codeSigCmd->dataoff < lastSeg->fileoff )
- return 0;
+ return "dyld_sim code signature not in __LINKEDIT";
+ if ( (codeSigCmd->dataoff + codeSigCmd->datasize) < codeSigCmd->dataoff )
+ return "dyld_sim code signature size wraps";
if ( (codeSigCmd->dataoff + codeSigCmd->datasize) > (lastSeg->fileoff + lastSeg->filesize) )
- return 0;
+ return "dyld_sim code signature extends beyond __LINKEDIT";
fsignatures_t siginfo;
siginfo.fs_file_start=fileOffset; // start of mach-o slice in fat file
siginfo.fs_blob_size=codeSigCmd->datasize; // size of code-signature
int result = fcntl(fd, F_ADDFILESIGS_FOR_DYLD_SIM, &siginfo);
if ( result == -1 ) {
- dyld::log("fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d\n", errno);
- return 0;
+ return mkstringf("dyld_sim fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d", errno);
}
close(fd);
// file range covered by code signature must extend up to code signature itself
if ( siginfo.fs_file_start < codeSigCmd->dataoff )
- return 0;
+ return mkstringf("dyld_sim code signature does not cover all of dyld_sim. Signature covers up to 0x%08lX. Signature starts at 0x%08X", (unsigned long)siginfo.fs_file_start, codeSigCmd->dataoff);
+
// walk newly mapped dyld_sim __TEXT load commands to find entry point
uintptr_t entry = 0;
const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16);
// entry point must be in first segment
if ( registers->__eip < firstSeg->vmaddr )
- return 0;
+ return "dyld_sim entry point not in __TEXT segment";
if ( registers->__eip > (firstSeg->vmaddr + firstSeg->vmsize) )
- return 0;
+ return "dyld_sim entry point not in __TEXT segment";
entry = (registers->__eip + loadAddress - preferredLoadAddress);
#elif __x86_64__
const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16);
// entry point must be in first segment
if ( registers->__rip < firstSeg->vmaddr )
- return 0;
+ return "dyld_sim entry point not in __TEXT segment";
if ( registers->__rip > (firstSeg->vmaddr + firstSeg->vmsize) )
- return 0;
+ return "dyld_sim entry point not in __TEXT segment";
entry = (registers->__rip + loadAddress - preferredLoadAddress);
#endif
}
const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide,
const dyld::SyscallHelpers* vtable, uintptr_t* startGlue);
sim_entry_proc_t newDyld = (sim_entry_proc_t)entry;
- return (*newDyld)(argc, argv, envp, appleParams, mainExecutableMH, (macho_header*)loadAddress,
+ *mainAddr = (*newDyld)(argc, argv, envp, appleParams, mainExecutableMH, (macho_header*)loadAddress,
loadAddress - preferredLoadAddress,
&sSysCalls, startGlue);
+ return NULL;
}
#endif
// if this is host dyld, check to see if iOS simulator is being run
const char* rootPath = _simple_getenv(envp, "DYLD_ROOT_PATH");
if ( rootPath != NULL ) {
+ // Add dyld to the kernel image info before we jump to the sim
+ notifyKernelAboutDyld();
+
// look to see if simulator has its own dyld
char simDyldPath[PATH_MAX];
strlcpy(simDyldPath, rootPath, PATH_MAX);
strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX);
int fd = my_open(simDyldPath, O_RDONLY, 0);
if ( fd != -1 ) {
- result = useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue);
- if ( !result && (*startGlue == 0) )
- halt("problem loading iOS simulator dyld");
+ const char* errMessage = useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue, &result);
+ if ( errMessage != NULL )
+ halt(errMessage);
return result;
}
}
CRSetCrashLogMessage("dyld: launch started");
-#if LOG_BINDINGS
- char bindingsLogPath[256];
-
- const char* shortProgName = "unknown";
- if ( argc > 0 ) {
- shortProgName = strrchr(argv[0], '/');
- if ( shortProgName == NULL )
- shortProgName = argv[0];
- else
- ++shortProgName;
- }
- mysprintf(bindingsLogPath, "/tmp/bindings/%d-%s", getpid(), shortProgName);
- sBindingsLogfile = open(bindingsLogPath, O_WRONLY | O_CREAT, 0666);
- if ( sBindingsLogfile == -1 ) {
- ::mkdir("/tmp/bindings", 0777);
- sBindingsLogfile = open(bindingsLogPath, O_WRONLY | O_CREAT, 0666);
- }
- //dyld::log("open(%s) => %d, errno = %d\n", bindingsLogPath, sBindingsLogfile, errno);
-#endif
setContext(mainExecutableMH, argc, argv, envp, apple);
// Pickup the pointer to the exec path.
// <rdar://problem/13868260> Remove interim apple[0] transition code from dyld
if (!sExecPath) sExecPath = apple[0];
- bool ignoreEnvironmentVariables = false;
if ( sExecPath[0] != '/' ) {
// have relative path, use cwd to make absolute
char cwdbuff[MAXPATHLEN];
++sExecShortName;
else
sExecShortName = sExecPath;
- sProcessIsRestricted = processRestricted(mainExecutableMH, &ignoreEnvironmentVariables, &sProcessRequiresLibraryValidation);
- if ( sProcessIsRestricted ) {
-#if SUPPORT_LC_DYLD_ENVIRONMENT
- checkLoadCommandEnvironmentVariables();
-#endif
+
+ configureProcessRestrictions(mainExecutableMH);
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( gLinkContext.processIsRestricted ) {
pruneEnvironmentVariables(envp, &apple);
// set again because envp and apple may have changed or moved
setContext(mainExecutableMH, argc, argv, envp, apple);
}
- else {
- if ( !ignoreEnvironmentVariables )
- checkEnvironmentVariables(envp);
+ else
+#endif
+ {
+ checkEnvironmentVariables(envp);
defaultUninitializedFallbackPaths(envp);
}
if ( sEnv.DYLD_PRINT_OPTS )
stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB);
stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
// make initial allocations large enough that it is unlikely to need to be re-alloced
- sAllImages.reserve(INITIAL_IMAGE_COUNT);
sImageRoots.reserve(16);
sAddImageCallbacks.reserve(4);
sRemoveImageCallbacks.reserve(4);
sImageFilesNeedingTermination.reserve(16);
sImageFilesNeedingDOFUnregistration.reserve(8);
-
+
+#if !TARGET_IPHONE_SIMULATOR
#ifdef WAIT_FOR_SYSTEM_ORDER_HANDSHAKE
// <rdar://problem/6849505> Add gating mechanism to dyld support system order file generation process
WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld::gProcessInfo->systemOrderFlag);
#endif
-
+#endif
+
try {
// add dyld itself to UUID list
addDyldImageToUUIDList();
+ notifyKernelAboutDyld();
+
+#if SUPPORT_ACCELERATE_TABLES
+ bool mainExcutableAlreadyRebased = false;
+
+reloadAllImages:
+#endif
+
CRSetCrashLogMessage(sLoadingCrashMessage);
// instantiate ImageLoader for main executable
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
gLinkContext.mainExecutable = sMainExecutable;
- gLinkContext.processIsRestricted = sProcessIsRestricted;
- gLinkContext.processRequiresLibraryValidation = sProcessRequiresLibraryValidation;
gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
#if TARGET_IPHONE_SIMULATOR
- #if TARGET_OS_WATCH || TARGET_OS_TV
- // disable error during bring up of these simulators
- #else
// check main executable is not too new for this OS
{
if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH, sExecPath) ) {
- throwf("program was built for Mac OS X and cannot be run in simulator");
+ throwf("program was built for a platform that is not supported by this runtime");
}
uint32_t mainMinOS = sMainExecutable->minOSVersion();
+
// dyld is always built for the current OS, so we can get the current OS version
// from the load command in dyld itself.
uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion((const mach_header*)&__dso_handle);
if ( mainMinOS > dyldMinOS ) {
- throwf("app was built for iOS %d.%d which is newer than this simulator %d.%d",
+ #if TARGET_OS_WATCH
+ throwf("app was built for watchOS %d.%d which is newer than this simulator %d.%d",
+ mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF),
+ dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF));
+ #elif TARGET_OS_TV
+ throwf("app was built for tvOS %d.%d which is newer than this simulator %d.%d",
+ mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF),
+ dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF));
+ #else
+ throwf("app was built for iOS %d.%d which is newer than this simulator %d.%d",
mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF),
dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF));
+ #endif
}
}
- #endif
#endif
+
+ #if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // <rdar://problem/22805519> be less strict about old mach-o binaries
+ uint32_t mainSDK = sMainExecutable->sdkVersion();
+ gLinkContext.strictMachORequired = (mainSDK >= DYLD_MACOSX_VERSION_10_12) || gLinkContext.processUsingLibraryValidation;
+ #else
+ // simulators, iOS, tvOS, and watchOS are always strict
+ gLinkContext.strictMachORequired = true;
+ #endif
+
// load shared cache
checkSharedRegionDisable();
#if DYLD_SHARED_CACHE_SUPPORT
- if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion )
+ if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
mapSharedCache();
+ } else {
+ dyld_kernel_image_info_t kernelCacheInfo;
+ bzero(&kernelCacheInfo.uuid[0], sizeof(uuid_t));
+ kernelCacheInfo.load_addr = 0;
+ kernelCacheInfo.fsobjid.fid_objno = 0;
+ kernelCacheInfo.fsobjid.fid_generation = 0;
+ kernelCacheInfo.fsid.val[0] = 0;
+ kernelCacheInfo.fsid.val[0] = 0;
+ task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, true, false);
+ }
+ #endif
+
+ #if SUPPORT_ACCELERATE_TABLES
+ sAllImages.reserve((sAllCacheImagesProxy != NULL) ? 16 : INITIAL_IMAGE_COUNT);
+ #else
+ sAllImages.reserve(INITIAL_IMAGE_COUNT);
#endif
// Now that shared cache is loaded, setup an versioned dylib overrides
checkVersionedPaths();
#endif
+
+ // dyld_all_image_infos image list does not contain dyld
+ // add it as dyldPath field in dyld_all_image_infos
+ // for simulator, dyld_sim is in image list, need host dyld added
+#if TARGET_IPHONE_SIMULATOR
+ // get path of host dyld from table of syscall vectors in host dyld
+ void* addressInDyld = gSyscallHelpers;
+#else
+ // get path of dyld itself
+ void* addressInDyld = (void*)&__dso_handle;
+#endif
+ char dyldPathBuffer[MAXPATHLEN+1];
+ int len = proc_regionfilename(getpid(), (uint64_t)(long)addressInDyld, dyldPathBuffer, MAXPATHLEN);
+ if ( (len != 0) && (strcmp(dyldPathBuffer, gProcessInfo->dyldPath) != 0) ) {
+ gProcessInfo->dyldPath = strdup(dyldPathBuffer);
+ }
+
// load any inserted libraries
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
// link main executable
gLinkContext.linkingMainExecutable = true;
- link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL));
+#if SUPPORT_ACCELERATE_TABLES
+ if ( mainExcutableAlreadyRebased ) {
+ // previous link() on main executable has already adjusted its internal pointers for ASLR
+ // work around that by rebasing by inverse amount
+ sMainExecutable->rebase(gLinkContext, -mainExecutableSlide);
+ }
+#endif
+ link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
sMainExecutable->setNeverUnloadRecursive();
if ( sMainExecutable->forceFlat() ) {
gLinkContext.bindFlat = true;
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
- link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL));
+ link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
image->setNeverUnloadRecursive();
}
// only INSERTED libraries can interpose
}
// <rdar://problem/19315404> dyld should support interposition even without DYLD_INSERT_LIBRARIES
- for (int i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) {
+ for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) {
ImageLoader* image = sAllImages[i];
if ( image->inSharedCache() )
continue;
image->registerInterposing();
}
+ #if SUPPORT_ACCELERATE_TABLES
+ if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) {
+ // Accelerator tables cannot be used with implicit interposing, so relaunch with accelerator tables disabled
+ ImageLoader::clearInterposingTuples();
+ // unmap all loaded dylibs (but not main executable)
+ for (long i=1; i < sAllImages.size(); ++i) {
+ ImageLoader* image = sAllImages[i];
+ if ( image == sMainExecutable )
+ continue;
+ if ( image == sAllCacheImagesProxy )
+ continue;
+ image->setCanUnload();
+ ImageLoader::deleteImage(image);
+ }
+ // note: we don't need to worry about inserted images because if DYLD_INSERT_LIBRARIES was set we would not be using the accelerator table
+ sAllImages.clear();
+ sImageRoots.clear();
+ sImageFilesNeedingTermination.clear();
+ sImageFilesNeedingDOFUnregistration.clear();
+ sAddImageCallbacks.clear();
+ sRemoveImageCallbacks.clear();
+ sDisableAcceleratorTables = true;
+ sAllCacheImagesProxy = NULL;
+ sMappedRangesStart = NULL;
+ mainExcutableAlreadyRebased = true;
+ gLinkContext.linkingMainExecutable = false;
+ resetAllImages();
+ goto reloadAllImages;
+ }
+ #endif
// apply interposing to initial set of images
for(int i=0; i < sImageRoots.size(); ++i) {
// <rdar://problem/12186933> do weak binding only after all inserted images linked
sMainExecutable->weakBind(gLinkContext);
-
+
+ #if DYLD_SHARED_CACHE_SUPPORT
+ // If cache has branch island dylibs, tell debugger about them
+ if ( (sSharedCache != NULL) && (sSharedCache->mappingOffset >= 0x78) && (sSharedCache->branchPoolsOffset != 0) ) {
+ uint32_t count = sSharedCache->branchPoolsCount;
+ dyld_image_info info[count];
+ const uint64_t* poolAddress = (uint64_t*)((char*)sSharedCache + sSharedCache->branchPoolsOffset);
+ // <rdar://problem/20799203> empty branch pools can be in development cache
+ if ( ((mach_header*)poolAddress)->magic == sMainExecutableMachHeader->magic ) {
+ for (int poolIndex=0; poolIndex < count; ++poolIndex) {
+ uint64_t poolAddr = poolAddress[poolIndex] + sSharedCacheSlide;
+ info[poolIndex].imageLoadAddress = (mach_header*)(long)poolAddr;
+ info[poolIndex].imageFilePath = "dyld_shared_cache_branch_islands";
+ info[poolIndex].imageFileModDate = 0;
+ }
+ // add to all_images list
+ addImagesToAllImages(count, info);
+ // tell gdb about new branch island images
+ gProcessInfo->notification(dyld_image_adding, count, info);
+ }
+ }
+ #endif
+
CRSetCrashLogMessage("dyld: launch, running initializers");
#if SUPPORT_OLD_CRT_INITIALIZATION
// Old way is to run initializers via a callback from crt1.o
# gdb and Symbolication look at these to discover a process's loaded images
_dyld_all_image_infos
_dyld_shared_cache_ranges
+__dyld_debugger_notification
# CrashReporter uses this to get message as to why dyld terminated the process
_error_string
-_dyld_fatal_error
# Used by various tools to see build number of dyld
_dyldVersionString
#include <stdint.h>
#include <sys/stat.h>
+#include <dlfcn.h>
#include "ImageLoader.h"
#include "mach-o/dyld_priv.h"
extern ImageLoader::LinkContext gLinkContext;
extern struct dyld_all_image_infos* gProcessInfo;
extern bool gLogAPIs;
+#if SUPPORT_ACCELERATE_TABLES
+ extern bool gLogAppAPIs;
+#endif
#if DYLD_SHARED_CACHE_SUPPORT
extern bool gSharedCacheOverridden;
#endif
extern void registerRemoveCallback(ImageCallback func);
extern void registerUndefinedHandler(UndefinedHandler);
extern void initializeMainExecutable();
- extern void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths);
- extern void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths);
+ extern void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex);
+ extern void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex);
extern void runInitializers(ImageLoader* image);
extern void runImageStaticTerminators(ImageLoader* image);
extern const char* getExecutablePath();
extern ImageLoader* findLoadedImageByInstallPath(const char* path);
extern bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image);
extern bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, const ImageLoader** image);
- extern ImageLoader* load(const char* path, const LoadContext& context);
+ extern ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheIndex);
extern ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName);
extern void removeImage(ImageLoader* image);
extern ImageLoader* cloneImage(ImageLoader* image);
extern void processDyldEnvironmentVariable(const char* key, const char* value, const char* mainDir);
extern void registerImageStateSingleChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler);
extern void registerImageStateBatchChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler);
+ extern void registerObjCNotifiers(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped);
+ extern bool sharedCacheUUID(uuid_t uuid);
extern void garbageCollectImages();
extern int openSharedCacheFile();
extern const void* imMemorySharedCacheHeader();
extern const char* getStandardSharedCacheFilePath();
extern int my_stat(const char* path, struct stat* buf);
extern int my_open(const char* path, int flag, int other);
+ bool sandboxBlockedOpen(const char* path);
+ bool sandboxBlockedMmap(const char* path);
+ bool sandboxBlockedStat(const char* path);
+
+#if SUPPORT_ACCELERATE_TABLES
+ bool dlopenFromCache(const char* path, int mode, void** handle);
+ bool makeCacheHandle(ImageLoader* image, unsigned cacheIndex, int mode, void** result);
+ void* dlsymFromCache(void* handle, const char* symName, unsigned index);
+ bool isCacheHandle(void* handle);
+ bool addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index=NULL);
+ bool findUnwindSections(const void* addr, dyld_unwind_sections* info);
+ bool dladdrFromCache(const void* address, Dl_info* info);
+ bool isPathInCache(const char* path);
+ const char* getPathFromIndex(unsigned cacheIndex);
+#endif
}
#
# Cluster these __DATA symbols in dyld to reduce pages dirtied
#
-_sDyldInfo.0
-_sDyldInfo.1
-_sDyldInfo.2
-_sDyldInfo.3
-_sDyldInfo.4
-_sDyldTextEnd
-_mach_init_inited.7154.b
+_mach_init.mach_init_inited
__task_reply_port
_mach_task_self_
_mach_host_self_
_vm_page_size
_vm_page_mask
_vm_page_shift
+_vm_kernel_page_size
+_vm_kernel_page_mask
+_vm_kernel_page_shift
+_exitf
+___pthread_head
___stack_chk_guard
__ZN4dyldL23sFrameworkFallbackPathsE
__ZN4dyldL21sLibraryFallbackPathsE
__ZL11initialPool
__ZN4dyld12gLinkContextE
__ZN4dyld17gLibSystemHelpersE
+_sThreadChainKey
+_sStaticThreadChain
__ZN4dyldL17sSharedCacheSlideE
_dyld_shared_cache_ranges
+__ZN4dyldL20sAllCacheImagesProxyE
__ZN11ImageLoader27fgImagesUsedFromSharedCacheE
__ZN11ImageLoader19fgInterposingTuplesE
__ZN11ImageLoader24fgTotalLoadLibrariesTimeE
__ZN11ImageLoader26fgImagesWithUsedPrebindingE
__ZN11ImageLoader26fgImagesHasWeakDefinitionsE
__ZN16ImageLoaderMachO26fgSymbolTableBinarySearchsE
-__ZN16ImageLoaderMachO19fgSymbolTrieSearchsE
+__ZN11ImageLoader19fgSymbolTrieSearchsE
__ZN11ImageLoader13fgLoadOrdinalE
__ZN4dyldL9sExecPathE
__ZN4dyldL25sMainExecutableMachHeaderE
__ZN4dyldL19sInsertedDylibCountE
__ZN4dyldL20sProcessIsRestrictedE
__ZN4dyldL18sMappedRangesStartE
+__ZN4dyldL14sExecShortNameE
+__ZN4dyld12gProcessInfoE
+___cxa_terminate_handler
+___cxa_unexpected_handler
+__ZL11currentPool
+__ZN4dyldL8sHostCPUE
+__ZN4dyldL15sHostCPUsubtypeE
+
__thread
#include "mach-o/dyld_priv.h"
#include "ImageLoader.h"
+#include "ImageLoaderMachO.h"
#include "dyld.h"
#include "dyldLibSystemInterface.h"
// from dyld_gdb.cpp
extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]);
+extern uint32_t allImagesCount();
+extern const mach_header* allImagesIndexedMachHeader(uint32_t index);
+extern const char* allImagesIndexedPath(uint32_t index);
+
// deprecated APIs are still availble on Mac OS X, but not on iPhone OS
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#if SUPPORT_ZERO_COST_EXCEPTIONS
static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info);
#endif
+#if DEPRECATED_APIS_SUPPORTED
+#endif
static void unimplemented()
{
{"__dyld__NSGetExecutablePath", (void*)_NSGetExecutablePath },
// SPIs
- {"__dyld_dyld_register_image_state_change_handler", (void*)dyld_register_image_state_change_handler },
{"__dyld_register_thread_helpers", (void*)registerThreadHelpers },
{"__dyld_fork_child", (void*)_dyld_fork_child },
{"__dyld_make_delayed_module_initializer_calls", (void*)_dyld_make_delayed_module_initializer_calls },
{"__dyld_shared_cache_file_path", (void*)dyld::getStandardSharedCacheFilePath },
#endif
{"__dyld_get_image_header_containing_address", (void*)dyld_image_header_containing_address },
+ {"__dyld_is_memory_immutable", (void*)_dyld_is_memory_immutable },
+ {"__dyld_objc_notify_register", (void*)_dyld_objc_notify_register },
+ {"__dyld_get_shared_cache_uuid", (void*)_dyld_get_shared_cache_uuid },
+
// deprecated
#if DEPRECATED_APIS_SUPPORTED
{
if ( dyld::gLogAPIs )
dyld::log("%s()\n", __func__);
- return dyld::getImageCount();
+ return allImagesCount();
}
const struct mach_header* _dyld_get_image_header(uint32_t image_index)
{
if ( dyld::gLogAPIs )
dyld::log("%s(%u)\n", __func__, image_index);
- ImageLoader* image = dyld::getIndexedImage(image_index);
- if ( image != NULL )
- return (struct mach_header*)image->machHeader();
- else
- return NULL;
+ return allImagesIndexedMachHeader(image_index);
}
intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index)
{
if ( dyld::gLogAPIs )
dyld::log("%s(%u)\n", __func__, image_index);
- ImageLoader* image = dyld::getIndexedImage(image_index);
- if ( image != NULL )
- return image->getSlide();
+ const struct mach_header* mh = allImagesIndexedMachHeader(image_index);
+ if ( mh != NULL )
+ return ImageLoaderMachO::computeSlide(mh);
else
return 0;
}
{
if ( dyld::gLogAPIs )
dyld::log("%s(%p)\n", __func__, mh);
- ImageLoader* image = dyld::findImageByMachHeader(mh);
- if ( image != NULL )
- return image->getSlide();
- else
- return 0;
+ return ImageLoaderMachO::computeSlide(mh);
}
{
if ( dyld::gLogAPIs )
dyld::log("%s(%u)\n", __func__, image_index);
- ImageLoader* image = dyld::getIndexedImage(image_index);
- if ( image != NULL )
- return image->getRealPath();
- else
- return NULL;
+ return allImagesIndexedPath(image_index);
}
const struct mach_header * dyld_image_header_containing_address(const void* address)
{
if ( dyld::gLogAPIs )
dyld::log("%s(%p)\n", __func__, address);
+#if SUPPORT_ACCELERATE_TABLES
+ const mach_header* mh;
+ const char* path;
+ if ( dyld::addressInCache(address, &mh, &path) )
+ return mh;
+#endif
ImageLoader* image = dyld::findImageContainingAddress(address);
if ( image != NULL )
return image->machHeader();
context.canBePIE = false;
context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
context.rpath = &callersRPaths; // rpaths from caller and main executable
-
- image = load(path, context);
+
+ unsigned cacheIndex;
+ image = load(path, context, cacheIndex);
if ( image != NULL ) {
if ( context.matchByInstallName )
image->setMatchInstallPath(true);
- dyld::link(image, false, false, callersRPaths);
+ dyld::link(image, false, false, callersRPaths, cacheIndex);
dyld::runInitializers(image);
// images added with NSAddImage() can never be unloaded
image->setNeverUnload();
context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
context.rpath = NULL; // support not yet implemented
- ImageLoader* image = dyld::load(pathName, context);
+ unsigned cacheIndex;
+ ImageLoader* image = dyld::load(pathName, context, cacheIndex);
// Note: We DO NOT link the image! NSLinkModule will do that
if ( image != NULL ) {
if ( !image->isBundle() ) {
bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 );
// load libraries, rebase, bind, to make this image usable
- dyld::link(objectFileImage->image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL));
+ dyld::link(objectFileImage->image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL), UINT32_MAX);
// bump reference count to keep this bundle from being garbage collected
objectFileImage->image->incrementDlopenReferenceCount();
bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 );
// load libraries, rebase, bind, to make this image usable
- dyld::link(image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL));
+ dyld::link(image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL), UINT32_MAX);
// run initializers unless magic flag says not to
if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) == 0 )
{
dyld::gLibSystemHelpers = helpers;
- // let gdb know it is safe to run code in inferior that might call malloc()
- dyld::gProcessInfo->libSystemInitialized = true;
-
#if !SUPPORT_ZERO_COST_EXCEPTIONS
if ( helpers->version >= 5 ) {
// create key use by dyld exception handling
dlerrorClear();
+ CRSetCrashLogMessage("dyld: in dlopen_preflight()");
+
+ const bool leafName = (strchr(path, '/') == NULL);
+ const bool absolutePath = (path[0] == '/');
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ char canonicalPath[PATH_MAX];
+ // <rdar://problem/7017050> dlopen() not opening frameworks from shared cache with // or ./ in path
+ if ( !leafName ) {
+ // make path canonical if it contains a // or ./
+ if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) {
+ const char* lastSlash = strrchr(path, '/');
+ char dirPath[PATH_MAX];
+ if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) {
+ dirPath[lastSlash-path] = '\0';
+ if ( realpath(dirPath, canonicalPath) ) {
+ strlcat(canonicalPath, "/", sizeof(canonicalPath));
+ if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) {
+ // if all fit in buffer, use new canonical path
+ path = canonicalPath;
+ }
+ }
+ }
+ }
+ }
+#endif
+#if SUPPORT_ACCELERATE_TABLES
+ if ( dyld::isPathInCache(path) )
+ return true;
+#endif
+
#if DYLD_SHARED_CACHE_SUPPORT
// <rdar://problem/5910137> dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized
// if requested path is to something in the dyld shared cache, always succeed
return true;
#endif
- CRSetCrashLogMessage("dyld: in dlopen_preflight()");
-
bool result = false;
std::vector<const char*> rpathsFromCallerImage;
try {
}
ImageLoader* image = NULL;
- const bool leafName = (strchr(path, '/') == NULL);
- const bool absolutePath = (path[0] == '/');
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
- char canonicalPath[PATH_MAX];
- // <rdar://problem/7017050> dlopen() not opening frameworks from shared cache with // or ./ in path
- if ( !leafName ) {
- // make path canonical if it contains a // or ./
- if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) {
- const char* lastSlash = strrchr(path, '/');
- char dirPath[PATH_MAX];
- if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) {
- dirPath[lastSlash-path] = '\0';
- if ( realpath(dirPath, canonicalPath) ) {
- strlcat(canonicalPath, "/", sizeof(canonicalPath));
- if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) {
- // if all fit in buffer, use new canonical path
- path = canonicalPath;
- }
- }
- }
- }
- }
-#endif
dyld::LoadContext context;
context.useSearchPaths = true;
context.useFallbackPaths= leafName; // a partial path implies don't use fallback paths
context.canBePIE = true;
context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
context.rpath = &callersRPaths; // rpaths from caller and main executable
-
- image = load(path, context);
+
+ unsigned cacheIndex;
+ image = load(path, context, cacheIndex);
if ( image != NULL ) {
- dyld::preflight(image, callersRPaths); // image object deleted by dyld::preflight()
+ dyld::preflight(image, callersRPaths, cacheIndex); // image object deleted by dyld::preflight()
result = true;
}
}
return result;
}
+#if SUPPORT_ACCELERATE_TABLES
+bool static callerIsNonOSApp(void* callerAddress, const char** shortName)
+{
+ *shortName = NULL;
+ const mach_header* unusedMh;
+ const char* unusedPath;
+ unsigned unusedIndex;
+ // any address in shared cache is not from app
+ if ( dyld::addressInCache(callerAddress, &unusedMh, &unusedPath, &unusedIndex) )
+ return false;
+
+ ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
+ if ( callerImage == NULL )
+ return false;
+
+ *shortName = callerImage->getShortName();
+ return ( strncmp(callerImage->getPath(), "/var/containers/", 16) == 0 );
+}
+#endif
void* dlopen(const char* path, int mode)
{
if ( dyld::gLogAPIs )
dyld::log("%s(%s, 0x%08X)\n", __func__, ((path==NULL) ? "NULL" : path), mode);
+#if SUPPORT_ACCELERATE_TABLES
+ if ( dyld::gLogAppAPIs ) {
+ void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+ const char* shortName;
+ if ( callerIsNonOSApp(callerAddress, &shortName) ) {
+ dyld::log("%s: %s(%s, 0x%08X)\n", shortName, __func__, ((path==NULL) ? "NULL" : path), mode);
+ }
+ }
+#endif
+
dlerrorClear();
-
+
// passing NULL for path means return magic object
if ( path == NULL ) {
// RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images
}
void* result = NULL;
+ const bool leafName = (strchr(path, '/') == NULL);
+ const bool absolutePath = (path[0] == '/');
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ char canonicalPath[PATH_MAX];
+ // <rdar://problem/7017050> dlopen() not opening frameworks from shared cache with // or ./ in path
+ if ( !leafName ) {
+ // make path canonical if it contains a // or ./
+ if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) {
+ const char* lastSlash = strrchr(path, '/');
+ char dirPath[PATH_MAX];
+ if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) {
+ dirPath[lastSlash-path] = '\0';
+ if ( realpath(dirPath, canonicalPath) ) {
+ strlcat(canonicalPath, "/", sizeof(canonicalPath));
+ if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) {
+ // if all fit in buffer, use new canonical path
+ path = canonicalPath;
+ }
+ }
+ }
+ }
+ }
+#endif
+#if SUPPORT_ACCELERATE_TABLES
+ if ( dyld::dlopenFromCache(path, mode, &result) ) {
+ // Note: dlopenFromCache() releases the lock
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(%s) ==> %p\n", __func__, path, result);
+ return result;
+ }
+#endif
+
ImageLoader* image = NULL;
std::vector<const char*> rpathsFromCallerImage;
ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage);
dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage);
}
- const bool leafName = (strchr(path, '/') == NULL);
- const bool absolutePath = (path[0] == '/');
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
- char canonicalPath[PATH_MAX];
- // <rdar://problem/7017050> dlopen() not opening frameworks from shared cache with // or ./ in path
- if ( !leafName ) {
- // make path canonical if it contains a // or ./
- if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) {
- const char* lastSlash = strrchr(path, '/');
- char dirPath[PATH_MAX];
- if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) {
- dirPath[lastSlash-path] = '\0';
- if ( realpath(dirPath, canonicalPath) ) {
- strlcat(canonicalPath, "/", sizeof(canonicalPath));
- if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) {
- // if all fit in buffer, use new canonical path
- path = canonicalPath;
- }
- }
- }
- }
- }
-#endif
dyld::LoadContext context;
context.useSearchPaths = true;
context.useFallbackPaths= leafName; // a partial path means no fallback paths
context.canBePIE = true;
context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
context.rpath = &callersRPaths; // rpaths from caller and main executable
-
- image = load(path, context);
+
+ unsigned cacheIndex;
+ image = load(path, context, cacheIndex);
+#if SUPPORT_ACCELERATE_TABLES
+ if ( (image != NULL) && (cacheIndex != UINT32_MAX) ) {
+ if ( dyld::makeCacheHandle(image, cacheIndex, mode, &result) ) {
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(%s) ==> %p\n", __func__, path, result);
+ if ( lockHeld )
+ dyld::gLibSystemHelpers->releaseGlobalDyldLock();
+ return result;
+ }
+ }
+#endif
if ( image != NULL ) {
// bump reference count. Do this before link() so that if an initializer calls dlopen and fails
// this image is not garbage collected
if ( (mode & RTLD_NOLOAD) == 0 ) {
bool alreadyLinked = image->isLinked();
bool forceLazysBound = ( (mode & RTLD_NOW) != 0 );
- dyld::link(image, forceLazysBound, false, callersRPaths);
+ dyld::link(image, forceLazysBound, false, callersRPaths, cacheIndex);
if ( ! alreadyLinked ) {
// only hide exports if image is not already in use
if ( (mode & RTLD_LOCAL) != 0 )
dyld::log("%s(%p, %p)\n", __func__, address, info);
CRSetCrashLogMessage("dyld: in dladdr()");
+#if SUPPORT_ACCELERATE_TABLES
+ if ( dyld::dladdrFromCache(address, info) ) {
+ CRSetCrashLogMessage(NULL);
+ return 1; // success
+ }
+#endif
+
ImageLoader* image = dyld::findImageContainingAddress(address);
if ( image != NULL ) {
info->dli_fname = image->getRealPath();
if ( dyld::gLogAPIs )
dyld::log("%s(%p, %s)\n", __func__, handle, symbolName);
+#if SUPPORT_ACCELERATE_TABLES
+ if ( dyld::gLogAppAPIs ) {
+ void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+ const char* shortName;
+ if ( callerIsNonOSApp(callerAddress, &shortName) ) {
+ dyld::log("%s: %s(%p, %s)\n", shortName, __func__, handle, symbolName);
+ }
+ }
+#endif
+
CRSetCrashLogMessage("dyld: in dlsym()");
dlerrorClear();
const ImageLoader* image;
const ImageLoader::Symbol* sym;
+ void* result;
// dlsym() assumes symbolName passed in is same as in C source code
// dyld assumes all symbol names have an underscore prefix
if ( handle == RTLD_DEFAULT ) {
if ( dyld::flatFindExportedSymbol(underscoredName, &sym, &image) ) {
CRSetCrashLogMessage(NULL);
- return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext);
+ result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(RTLD_DEFAULT, %s) ==> %p\n", __func__, symbolName, result);
+ return result;
}
const char* str = dyld::mkstringf("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName);
dlerrorSet(str);
free((void*)str);
CRSetCrashLogMessage(NULL);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(RTLD_DEFAULT, %s) ==> NULL\n", __func__, symbolName);
return NULL;
}
// magic "search only main executable" handle
- if ( handle == RTLD_MAIN_ONLY ) {
+ else if ( handle == RTLD_MAIN_ONLY ) {
image = dyld::mainExecutable();
sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way
if ( sym != NULL ) {
CRSetCrashLogMessage(NULL);
- return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext);
+ result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(RTLD_MAIN_ONLY, %s) ==> %p\n", __func__, symbolName, result);
+ return result;
}
const char* str = dyld::mkstringf("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName);
dlerrorSet(str);
free((void*)str);
CRSetCrashLogMessage(NULL);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(RTLD_MAIN_ONLY, %s) ==> NULL\n", __func__, symbolName);
return NULL;
}
// magic "search what I would see" handle
- if ( handle == RTLD_NEXT ) {
+ else if ( handle == RTLD_NEXT ) {
void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+#if SUPPORT_ACCELERATE_TABLES
+ const mach_header* mh;
+ const char* path;
+ unsigned index;
+ if ( dyld::addressInCache(callerAddress, &mh, &path, &index) ) {
+ // if dylib in cache is calling dlsym(RTLD_NEXT,xxx) handle search differently
+ result = dyld::dlsymFromCache(RTLD_NEXT, underscoredName, index);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(RTLD_NEXT, %s) ==> %p\n", __func__, symbolName, result);
+ return result;
+ }
+#endif
ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
sym = callerImage->findExportedSymbolInDependentImages(underscoredName, dyld::gLinkContext, &image); // don't search image, but do search what it links against
if ( sym != NULL ) {
CRSetCrashLogMessage(NULL);
- return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext);
+ result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext , callerImage, false, underscoredName);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(RTLD_NEXT, %s) ==> %p\n", __func__, symbolName, result);
+ return result;
}
const char* str = dyld::mkstringf("dlsym(RTLD_NEXT, %s): symbol not found", symbolName);
dlerrorSet(str);
free((void*)str);
CRSetCrashLogMessage(NULL);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(RTLD_NEXT, %s) ==> NULL\n", __func__, symbolName);
return NULL;
}
// magic "search me, then what I would see" handle
- if ( handle == RTLD_SELF ) {
+ else if ( handle == RTLD_SELF ) {
void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+#if SUPPORT_ACCELERATE_TABLES
+ const mach_header* mh;
+ const char* path;
+ unsigned index;
+ if ( dyld::addressInCache(callerAddress, &mh, &path, &index) ) {
+ // if dylib in cache is calling dlsym(RTLD_SELF,xxx) handle search differently
+ result = dyld::dlsymFromCache(RTLD_SELF, underscoredName, index);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(RTLD_SELF, %s) ==> %p\n", __func__, symbolName, result);
+ return result;
+ }
+#endif
ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
sym = callerImage->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against
if ( sym != NULL ) {
CRSetCrashLogMessage(NULL);
- return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext);
+ result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(RTLD_SELF, %s) ==> %p\n", __func__, symbolName, result);
+ return result;
}
const char* str = dyld::mkstringf("dlsym(RTLD_SELF, %s): symbol not found", symbolName);
dlerrorSet(str);
free((void*)str);
CRSetCrashLogMessage(NULL);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(RTLD_SELF, %s) ==> NULL\n", __func__, symbolName);
return NULL;
}
+#if SUPPORT_ACCELERATE_TABLES
+ // check for mega dylib handle
+ else if ( dyld::isCacheHandle(handle) ) {
+ result = dyld::dlsymFromCache(handle, underscoredName, 0);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(%p, %s) ==> %p\n", __func__, handle, symbolName, result);
+ return result;
+ }
+#endif
// real handle
image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits
if ( dyld::validImage(image) ) {
void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
callerImage = dyld::findImageContainingAddress(callerAddress);
}
- return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage);
+ result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(%p, %s) ==> %p\n", __func__, handle, symbolName, result);
+ return result;
}
const char* str = dyld::mkstringf("dlsym(%p, %s): symbol not found", handle, symbolName);
dlerrorSet(str);
dlerrorSet("invalid handle passed to dlsym()");
}
CRSetCrashLogMessage(NULL);
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(%p, %s) ==> NULL\n", __func__, handle, symbolName);
return NULL;
}
return dyld::gProcessInfo;
}
+
#if SUPPORT_ZERO_COST_EXCEPTIONS
static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
{
//if ( dyld::gLogAPIs )
// dyld::log("%s(%p, %p)\n", __func__, addr, info);
+#if SUPPORT_ACCELERATE_TABLES
+ if ( dyld::findUnwindSections(addr, info) )
+ return true;
+#endif
ImageLoader* image = dyld::findImageContainingAddress(addr);
if ( image != NULL ) {
image->getUnwindInfo(info);
#endif
-void dyld_register_image_state_change_handler(dyld_image_states state, bool batch,
- dyld_image_state_change_handler handler)
-{
- if ( dyld::gLogAPIs )
- dyld::log("%s(%d, %d, %p)\n", __func__, state, batch, handler);
- if ( batch )
- dyld::registerImageStateBatchChangeHandler(state, handler);
- else
- dyld::registerImageStateSingleChangeHandler(state, handler);
-}
-
const char* dyld_image_path_containing_address(const void* address)
{
if ( dyld::gLogAPIs )
dyld::log("%s(%p)\n", __func__, address);
+#if SUPPORT_ACCELERATE_TABLES
+ const mach_header* mh;
+ const char* path;
+ if ( dyld::addressInCache(address, &mh, &path) )
+ return path;
+#endif
+
ImageLoader* image = dyld::findImageContainingAddress(address);
if ( image != NULL )
return image->getRealPath();
}
+bool _dyld_is_memory_immutable(const void* addr, size_t length)
+{
+ if ( dyld::gLogAPIs )
+ dyld::log("%s(%p, %ld)\n", __func__, addr, length);
+
+ uintptr_t checkStart = (uintptr_t)addr;
+ uintptr_t checkEnd = checkStart + length;
+
+#if DYLD_SHARED_CACHE_SUPPORT
+ // quick check to see if in r/o region of shared cache. If so return true.
+ if ( dyld_shared_cache_ranges.sharedRegionsCount > 2 ) {
+ uintptr_t roStart = dyld_shared_cache_ranges.ranges[0].start;
+ uintptr_t roEnd = roStart + dyld_shared_cache_ranges.ranges[0].length;
+ if ( (roStart < checkStart) && (checkEnd < roEnd) )
+ return true;
+ }
+#endif
+ // Otherwise find if addr is in a dyld loaded image
+ ImageLoader* image = dyld::findImageContainingAddress(addr);
+ if ( image != NULL ) {
+ // <rdar://problem/24091154> already checked for r/o portion of cache
+ if ( image->inSharedCache() )
+ return false;
+ if ( !image->neverUnload() )
+ return false;
+ for (unsigned i=0, e=image->segmentCount(); i < e; ++i) {
+ if ( (image->segActualLoadAddress(i) < checkStart) && (checkEnd < image->segActualEndAddress(i)) ) {
+ return !image->segWriteable(i);
+ }
+ }
+ }
+ return false;
+}
+
+
+
+void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
+ _dyld_objc_notify_init init,
+ _dyld_objc_notify_unmapped unmapped)
+{
+ dyld::registerObjCNotifiers(mapped, init, unmapped);
+}
+
+
+bool _dyld_get_shared_cache_uuid(uuid_t uuid)
+{
+ return dyld::sharedCacheUUID(uuid);
+}
#include <Availability.h>
#include <vproc_priv.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include "mach-o/dyld_images.h"
#include "mach-o/dyld.h"
#include "mach-o/dyld_priv.h"
+#include "dyld_cache_format.h"
#include "ImageLoader.h"
#include "dyldLock.h"
}
return 0;
}
+
+uint32_t dyld_get_program_min_watch_os_version()
+{
+ const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
+ uint32_t loadCommand;
+ uint32_t minOS;
+ uint32_t sdk;
+
+ if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) {
+ if ( loadCommand == LC_VERSION_MIN_WATCHOS )
+ return minOS; // return raw minOS (not mapped to iOS version)
+ }
+ return 0;
+}
+
#endif
/*
}
+bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid)
+{
+ const load_command* startCmds = NULL;
+ if ( mh->magic == MH_MAGIC_64 )
+ startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
+ else if ( mh->magic == MH_MAGIC )
+ startCmds = (load_command*)((char *)mh + sizeof(mach_header));
+ else
+ return false; // not a mach-o file, or wrong endianness
+
+ const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds);
+ const load_command* cmd = startCmds;
+ for(uint32_t i = 0; i < mh->ncmds; ++i) {
+ const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
+ if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) {
+ return false;
+ }
+ if ( cmd->cmd == LC_UUID ) {
+ const uuid_command* uuidCmd = (uuid_command*)cmd;
+ memcpy(uuid, uuidCmd->uuid, 16);
+ return true;
+ }
+ cmd = nextCmd;
+ }
+ bzero(uuid, 16);
+ return false;
+}
+
+
+
#if DEPRECATED_APIS_SUPPORTED
/*
* NSCreateObjectFileImageFromFile() creates an NSObjectFileImage for the
NSObjectFileImage objectFileImage)
{
DYLD_LOCK_THIS_BLOCK;
- static unsigned long (*p)(NSObjectFileImage) = NULL;
+ static uint32_t (*p)(NSObjectFileImage) = NULL;
if(p == NULL)
_dyld_func_lookup("__dyld_NSSymbolDefinitionCountInObjectFileImage", (void**)&p);
NSObjectFileImage objectFileImage)
{
DYLD_LOCK_THIS_BLOCK;
- static unsigned long (*p)(NSObjectFileImage) = NULL;
+ static uint32_t (*p)(NSObjectFileImage) = NULL;
if(p == NULL)
_dyld_func_lookup("__dyld_NSSymbolReferenceCountInObjectFileImage", (void**)&p);
char *buf,
uint32_t *bufsize)
{
- DYLD_LOCK_THIS_BLOCK;
+ DYLD_NO_LOCK_THIS_BLOCK;
static int (*p)(char *buf, uint32_t *bufsize) = NULL;
if(p == NULL)
typedef vproc_err_t (*vswapproc)(vproc_t vp, vproc_gsk_t key,int64_t *inval, int64_t *outval);
static vswapproc swapProc = &vproc_swap_integer;
-static bool isLaunchdOwned() {
- static bool first = true;
- static bool result;
- if ( first ) {
- int64_t val = 0;
- (*swapProc)(NULL, VPROC_GSK_IS_MANAGED, NULL, &val);
- result = ( val != 0 );
- first = false;
- }
- return result;
+static bool isLaunchdOwned()
+{
+ int64_t val = 0;
+ (*swapProc)(NULL, VPROC_GSK_IS_MANAGED, NULL, &val);
+ return ( val != 0 );
}
-
#if DYLD_SHARED_CACHE_SUPPORT
static void shared_cache_missing()
{
&isLaunchdOwned,
&vm_allocate,
&mmap,
- &__cxa_finalize_ranges};
+ &__cxa_finalize_ranges
+ };
//
return(p(handle, symbol));
}
-void dyld_register_image_state_change_handler(dyld_image_states state,
- bool batch, dyld_image_state_change_handler handler)
-{
- DYLD_LOCK_THIS_BLOCK;
- static void* (*p)(dyld_image_states, bool, dyld_image_state_change_handler) = NULL;
-
- if(p == NULL)
- _dyld_func_lookup("__dyld_dyld_register_image_state_change_handler", (void**)&p);
- p(state, batch, handler);
-}
-
const struct dyld_all_image_infos* _dyld_get_all_image_infos()
{
return p();
}
+bool _dyld_get_shared_cache_uuid(uuid_t uuid)
+{
+ DYLD_NO_LOCK_THIS_BLOCK;
+ static bool (*p)(uuid_t) = NULL;
+
+ if(p == NULL)
+ _dyld_func_lookup("__dyld_get_shared_cache_uuid", (void**)&p);
+ return p(uuid);
+}
+
bool dyld_process_is_restricted()
{
+static void* mapStartOfCache(const char* path, size_t length)
+{
+ struct stat statbuf;
+ if ( ::stat(path, &statbuf) == -1 )
+ return NULL;
+
+ if ( statbuf.st_size < length )
+ return NULL;
+
+ int cache_fd = ::open(path, O_RDONLY);
+ if ( cache_fd < 0 )
+ return NULL;
+
+ void* result = ::mmap(NULL, length, PROT_READ, MAP_PRIVATE, cache_fd, 0);
+ close(cache_fd);
+
+ if ( result == MAP_FAILED )
+ return NULL;
+
+ return result;
+}
+
+
+static const dyld_cache_header* findCacheInDirAndMap(const uuid_t cacheUuid, const char* dirPath)
+{
+ DIR* dirp = ::opendir(dirPath);
+ if ( dirp != NULL) {
+ dirent entry;
+ dirent* entp = NULL;
+ char cachePath[PATH_MAX];
+ while ( ::readdir_r(dirp, &entry, &entp) == 0 ) {
+ if ( entp == NULL )
+ break;
+ if ( entp->d_type != DT_REG )
+ continue;
+ if ( strlcpy(cachePath, dirPath, PATH_MAX) >= PATH_MAX )
+ continue;
+ if ( strlcat(cachePath, "/", PATH_MAX) >= PATH_MAX )
+ continue;
+ if ( strlcat(cachePath, entp->d_name, PATH_MAX) >= PATH_MAX )
+ continue;
+ if ( const dyld_cache_header* cacheHeader = (dyld_cache_header*)mapStartOfCache(cachePath, 0x00100000) ) {
+ if ( ::memcmp(cacheHeader->uuid, cacheUuid, 16) != 0 ) {
+ // wrong uuid, unmap and keep looking
+ ::munmap((void*)cacheHeader, 0x00100000);
+ }
+ else {
+ // found cache
+ closedir(dirp);
+ return cacheHeader;
+ }
+ }
+ }
+ closedir(dirp);
+ }
+ return NULL;
+}
+
+int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info))
+{
+ const dyld_cache_header* cacheHeader = NULL;
+ bool needToUnmap = true;
+
+ // get info from dyld about this process, to see if requested cache is already mapped into this process
+ const dyld_all_image_infos* allInfo = _dyld_get_all_image_infos();
+ if ( (allInfo != NULL) && (memcmp(allInfo->sharedCacheUUID, cacheUuid, 16) == 0) ) {
+ // requested cache is already mapped, just re-use it
+ cacheHeader = (dyld_cache_header*)(SHARED_REGION_BASE + allInfo->sharedCacheSlide);
+ needToUnmap = false;
+ }
+ else {
+ // look first is default location for cache files
+ #if __IPHONE_OS_VERSION_MIN_REQUIRED
+ const char* defaultSearchDir = IPHONE_DYLD_SHARED_CACHE_DIR;
+ #else
+ const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR;
+ #endif
+ cacheHeader = findCacheInDirAndMap(cacheUuid, defaultSearchDir);
+ // if not there, look in extra search locations
+ if ( cacheHeader == NULL ) {
+ for (const char** p = extraSearchDirs; *p != NULL; ++p) {
+ cacheHeader = findCacheInDirAndMap(cacheUuid, *p);
+ if ( cacheHeader != NULL )
+ break;
+ }
+ }
+ }
+
+ if ( cacheHeader == NULL )
+ return -1;
+
+ if ( cacheHeader->mappingOffset < sizeof(dyld_cache_header) ) {
+ // old cache without imagesText array
+ if ( needToUnmap )
+ ::munmap((void*)cacheHeader, 0x00100000);
+ return -1;
+ }
+
+ // walk imageText table and call callback for each entry
+ const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)cacheHeader + cacheHeader->imagesTextOffset);
+ const dyld_cache_image_text_info* imagesTextEnd = &imagesText[cacheHeader->imagesTextCount];
+ for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd; ++p) {
+ dyld_shared_cache_dylib_text_info dylibTextInfo;
+ dylibTextInfo.version = 1;
+ dylibTextInfo.loadAddressUnslid = p->loadAddress;
+ dylibTextInfo.textSegmentSize = p->textSegmentSize;
+ dylibTextInfo.path = (char*)cacheHeader + p->pathOffset;
+ ::memcpy(dylibTextInfo.dylibUuid, p->uuid, 16);
+ callback(&dylibTextInfo);
+ }
+
+ if ( needToUnmap )
+ ::munmap((void*)cacheHeader, 0x00100000);
+
+ return 0;
+}
+
+int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info))
+{
+ const char* extraSearchDirs[] = { NULL };
+ return dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
+}
+
+
+bool _dyld_is_memory_immutable(const void* addr, size_t length)
+{
+ DYLD_NO_LOCK_THIS_BLOCK;
+ static bool (*p)(const void*, size_t) = NULL;
+
+ if(p == NULL)
+ _dyld_func_lookup("__dyld_is_memory_immutable", (void**)&p);
+ return p(addr, length);
+}
+
+
+void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
+ _dyld_objc_notify_init init,
+ _dyld_objc_notify_unmapped unmapped)
+{
+ DYLD_LOCK_THIS_BLOCK;
+ static bool (*p)(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped) = NULL;
+
+ if(p == NULL)
+ _dyld_func_lookup("__dyld_objc_notify_register", (void**)&p);
+ p(mapped, init, unmapped);
+}
+
+
+
+
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd;
- if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
+ if ( (segCmd->fileoff == 0) && (segCmd->filesize != 0)) {
return (uintptr_t)mh - segCmd->vmaddr;
}
}
void* malloc(size_t size)
{
- if ( dyld::gLibSystemHelpers != NULL) {
+ if ( (dyld::gLibSystemHelpers != NULL) && dyld::gProcessInfo->libSystemInitialized ) {
void* p = dyld::gLibSystemHelpers->malloc(size);
//dyld::log("malloc(%lu) => %p from libSystem\n", size, p);
return p;
#endif // __arm64__
-/*
- * dyld calls this function to terminate a process.
- * It has a label so that CrashReporter can distinguish this
- * termination from a random crash. rdar://problem/4764143
- */
+// When iOS 10.0 simulator runs on 10.11, abort_with_payload() does not exist,
+// so it falls back and uses dyld_fatal_error().
+#if TARGET_IPHONE_SIMULATOR
.text
.align 2
.globl _dyld_fatal_error
_dyld_fatal_error:
-#if __arm__
- trap
- nop
-#elif __x86_64__ || __i386__
int3
nop
-#elif __arm64__
- brk #3
-#else
- #error unknown architecture
#endif
-#if __arm__
- // work around for: <rdar://problem/6530727> gdb-1109: notifier in dyld does not work if it is in thumb
- .text
- .align 2
- .globl _gdb_image_notifier
- .private_extern _gdb_image_notifier
-_gdb_image_notifier:
- bx lr
-#endif
// Added in version 4
void (*coresymbolication_load_notifier)(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header);
void (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header);
+ // Added in version 5
+ int (*proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t buffersize);
+ int (*getpid)();
+ kern_return_t (*mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly);
+ kern_return_t (*mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*);
+ kern_return_t (*mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t);
+ // Added in version 6
+ void (*abort_with_payload)(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags);
+ // Add in version 7
+ kern_return_t (*task_register_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt);
+ kern_return_t (*task_unregister_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt);
+ kern_return_t (*task_get_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t *dyld_images, mach_msg_type_number_t *dyld_imagesCnt);
+ kern_return_t (*task_register_dyld_shared_cache_image_info)(task_t task, dyld_kernel_image_info_t dyld_cache_image, boolean_t no_cache, boolean_t private_cache);
+ kern_return_t (*task_register_dyld_set_dyld_state)(task_t task, uint8_t dyld_state);
+ kern_return_t (*task_register_dyld_get_process_state)(task_t task, dyld_kernel_process_info_t *dyld_process_state);
};
extern const struct SyscallHelpers* gSyscallHelpers;
}
#endif
-#endif
\ No newline at end of file
+#endif
#include "mach-o/dyld_gdb.h"
#include "mach-o/dyld_images.h"
+#include "mach-o/dyld_process_info.h"
#include "ImageLoader.h"
#include "dyld.h"
static std::vector<dyld_image_info> sImageInfos;
static std::vector<dyld_uuid_info> sImageUUIDs;
+size_t allImagesCount()
+{
+ return sImageInfos.size();
+}
+
+const mach_header* allImagesIndexedMachHeader(uint32_t index)
+{
+ if ( index < sImageInfos.size() )
+ return sImageInfos[index].imageLoadAddress;
+ else
+ return NULL;
+}
+
+const char* allImagesIndexedPath(uint32_t index)
+{
+ if ( index < sImageInfos.size() )
+ return sImageInfos[index].imageFilePath;
+ else
+ return NULL;
+}
+
void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[])
{
for (uint32_t i=0; i < infoCount; ++i)
sImageInfos.push_back(info[i]);
dyld::gProcessInfo->infoArrayCount = (uint32_t)sImageInfos.size();
-
+ dyld::gProcessInfo->infoArrayChangeTimestamp = mach_absolute_time();
+
// set infoArray back to base address of vector (other process can now read)
dyld::gProcessInfo->infoArray = &sImageInfos[0];
}
const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[])
{
// tell gdb that about the new images
+ uint64_t t0 = mach_absolute_time();
dyld::gProcessInfo->notification(dyld_image_adding, infoCount, info);
+ uint64_t t1 = mach_absolute_time();
+ ImageLoader::fgTotalDebuggerPausedTime += (t1-t0);
+
// <rdar://problem/7739489> record initial count of images
// so CrashReporter can note which images were dynamically loaded
if ( dyld::gProcessInfo->initialImageCount == 0 )
- dyld::gProcessInfo->initialImageCount = infoCount;
+ dyld::gProcessInfo->initialImageCount = dyld::gProcessInfo->infoArrayCount;
return NULL;
}
}
}
dyld::gProcessInfo->uuidArrayCount = sImageUUIDs.size();
-
+ dyld::gProcessInfo->infoArrayChangeTimestamp = mach_absolute_time();
+
// set infoArray back to base address of vector
dyld::gProcessInfo->uuidArray = &sImageUUIDs[0];
dyld::gProcessInfo->notification(dyld_image_removing, 1, &goingAway);
}
-void setAlImageInfosHalt(const char* message, uintptr_t flags)
-{
- dyld::gProcessInfo->errorMessage = message;
- dyld::gProcessInfo->terminationFlags = flags;
-}
-
#if TARGET_IPHONE_SIMULATOR
namespace dyld {
}
#else
- #if __arm__
- // work around for: <rdar://problem/6530727> gdb-1109: notifier in dyld does not work if it is in thumb
- extern "C" void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]);
- #else
- static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[])
- {
- // do nothing
- // gdb sets a break point here to catch notifications
- //dyld::log("dyld: gdb_image_notifier(%s, %d, ...)\n", mode ? "dyld_image_removing" : "dyld_image_adding", infoCount);
- //for (uint32_t i=0; i < infoCount; ++i)
- // dyld::log("dyld: %d loading at %p %s\n", i, info[i].imageLoadAddress, info[i].imageFilePath);
- //for (uint32_t i=0; i < dyld::gProcessInfo->infoArrayCount; ++i)
- // dyld::log("dyld: %d loading at %p %s\n", i, dyld::gProcessInfo->infoArray[i].imageLoadAddress, dyld::gProcessInfo->infoArray[i].imageFilePath);
+ static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[])
+ {
+ uint64_t machHeaders[infoCount];
+ for (uint32_t i=0; i < infoCount; ++i) {
+ machHeaders[i] = (uintptr_t)(info[i].imageLoadAddress);
+ }
+ switch ( mode ) {
+ case dyld_image_adding:
+ _dyld_debugger_notification(dyld_notify_adding, infoCount, machHeaders);
+ break;
+ case dyld_image_removing:
+ _dyld_debugger_notification(dyld_notify_removing, infoCount, machHeaders);
+ break;
+ default:
+ break;
}
- #endif
+ // do nothing
+ // gdb sets a break point here to catch notifications
+ //dyld::log("dyld: gdb_image_notifier(%s, %d, ...)\n", mode ? "dyld_image_removing" : "dyld_image_adding", infoCount);
+ //for (uint32_t i=0; i < infoCount; ++i)
+ // dyld::log("dyld: %d loading at %p %s\n", i, info[i].imageLoadAddress, info[i].imageFilePath);
+ //for (uint32_t i=0; i < dyld::gProcessInfo->infoArrayCount; ++i)
+ // dyld::log("dyld: %d loading at %p %s\n", i, dyld::gProcessInfo->infoArray[i].imageLoadAddress, dyld::gProcessInfo->infoArray[i].imageFilePath);
+ }
+
+ // only used with accelerator tables and ASan which images need to be re-loaded
+ void resetAllImages()
+ {
+ sImageInfos.clear();
+ sImageUUIDs.clear();
+ _dyld_debugger_notification(dyld_notify_remove_all, 0, NULL);
+ }
extern void* __dso_handle;
#define STR(s) # s
struct dyld_all_image_infos dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info")))
= {
- 14, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL,
+ 15, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL,
XSTR(DYLD_VERSION), NULL, 0, NULL, 0, 0, NULL, &dyld_all_image_infos,
- 0, dyld_error_kind_none, NULL, NULL, NULL, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},
- {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}
+ 0, 0, NULL, NULL, NULL, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},
+ 0, 0, "/usr/lib/dyld", {0}, {0}
};
struct dyld_shared_cache_ranges dyld_shared_cache_ranges;
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2016 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 <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include <libproc.h>
+#include <sys/param.h>
+#include <mach/shared_region.h>
+#include <mach/mach_vm.h>
+#include <mach/vm_region.h>
+#include <libkern/OSAtomic.h>
+
+#include "dyld_process_info.h"
+#include "dyld_process_info_internal.h"
+#include "dyld_images.h"
+#include "dyld_priv.h"
+
+
+//
+// Opaque object returned by _dyld_process_info_create()
+//
+
+struct __attribute__((visibility("hidden"))) dyld_process_info_base {
+ static dyld_process_info_base* make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr);
+ static dyld_process_info_base* makeSuspended(task_t task, kern_return_t* kr);
+
+ uint32_t& retainCount() const { return _retainCount; }
+ dyld_process_cache_info* cacheInfo() const { return (dyld_process_cache_info*)(((char*)this) + _cacheInfoOffset); }
+ dyld_process_state_info* stateInfo() const { return (dyld_process_state_info*)(((char*)this) + _stateInfoOffset); }
+ void forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const;
+ void forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const;
+
+private:
+ struct ImageInfo {
+ uuid_t uuid;
+ uint64_t loadAddress;
+ const char* path;
+ uint32_t segmentStartIndex;
+ uint32_t segmentsCount;
+ };
+
+ struct SegmentInfo {
+ const char* name;
+ uint64_t addr;
+ uint64_t size;
+ };
+
+ dyld_process_info_base(unsigned imageCount, size_t totalSize);
+ void* operator new (size_t, void* buf) { return buf; }
+
+ static bool inCache(uint64_t addr) { return (addr > SHARED_REGION_BASE) && (addr < SHARED_REGION_BASE+SHARED_REGION_SIZE); }
+ kern_return_t addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal);
+ kern_return_t addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath);
+
+ bool invalid() { return ((char*)_stringRevBumpPtr < (char*)_curSegment); }
+ const char* copyPath(task_t task, uint64_t pathAddr, kern_return_t* kr);
+ const char* addString(const char*);
+ const char* copySegmentName(const char*);
+
+ void addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size);
+
+ void inspectLocalImageLoadCommands(uint64_t imageAddress, void* func);
+ kern_return_t inspectRemoteImageLoadCommands(task_t task, uint64_t imageAddress, void* func);
+
+ mutable uint32_t _retainCount;
+ const uint32_t _cacheInfoOffset;
+ const uint32_t _stateInfoOffset;
+ const uint32_t _imageInfosOffset;
+ const uint32_t _segmentInfosOffset;
+ ImageInfo* const _firstImage;
+ ImageInfo* _curImage;
+ SegmentInfo* const _firstSegment;
+ SegmentInfo* _curSegment;
+ uint32_t _curSegmentIndex;
+ char* _stringRevBumpPtr;
+
+ // dyld_process_cache_info cacheInfo;
+ // dyld_process_state_info stateInfo;
+ // ImageInfo images[];
+ // SegmentInfo segments[];
+ // char stringPool[]
+};
+
+dyld_process_info_base::dyld_process_info_base(unsigned imageCount, size_t totalSize)
+ : _retainCount(1), _cacheInfoOffset(sizeof(dyld_process_info_base)),
+ _stateInfoOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info)),
+ _imageInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info)),
+ _segmentInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info) + imageCount*sizeof(ImageInfo)),
+ _firstImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)),
+ _curImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)),
+ _firstSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)),
+ _curSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)),
+ _curSegmentIndex(0),
+ _stringRevBumpPtr((char*)(this)+totalSize)
+{
+}
+
+
+dyld_process_info_base* dyld_process_info_base::make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr)
+{
+ // figure out how many path strings will need to be copied and their size
+ const dyld_all_image_infos* myInfo = _dyld_get_all_image_infos();
+ bool sameCacheAsThisProcess = ((memcmp(myInfo->sharedCacheUUID, allImageInfo.sharedCacheUUID, 16) == 0) && (myInfo->sharedCacheSlide == allImageInfo.sharedCacheSlide));
+ unsigned countOfPathsNeedingCopying = 0;
+ if ( sameCacheAsThisProcess ) {
+ for (int i=0; i < allImageInfo.infoArrayCount; ++i) {
+ if ( !inCache(imageArray[i].imageFilePath) )
+ ++countOfPathsNeedingCopying;
+ }
+ }
+ else {
+ countOfPathsNeedingCopying = allImageInfo.infoArrayCount+1;
+ }
+ unsigned imageCountWithDyld = allImageInfo.infoArrayCount+1;
+
+ // allocate result object
+ size_t allocationSize = sizeof(dyld_process_info_base)
+ + sizeof(dyld_process_cache_info)
+ + sizeof(dyld_process_state_info)
+ + sizeof(ImageInfo)*(imageCountWithDyld)
+ + sizeof(SegmentInfo)*imageCountWithDyld*5
+ + countOfPathsNeedingCopying*PATH_MAX;
+ void* storage = malloc(allocationSize);
+ dyld_process_info_base* obj = new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize); // placement new()
+
+ // fill in base info
+ dyld_process_cache_info* cacheInfo = obj->cacheInfo();
+ memcpy(cacheInfo->cacheUUID, allImageInfo.sharedCacheUUID, 16);
+ cacheInfo->cacheBaseAddress = allImageInfo.sharedCacheBaseAddress;
+ cacheInfo->privateCache = allImageInfo.processDetachedFromSharedRegion;
+ // if no cache is used, allImageInfo has all zeros for cache UUID
+ cacheInfo->noCache = true;
+ for (int i=0; i < 16; ++i) {
+ if ( cacheInfo->cacheUUID[i] != 0 ) {
+ cacheInfo->noCache = false;
+ }
+ }
+
+ dyld_process_state_info* stateInfo = obj->stateInfo();
+ stateInfo->timestamp = allImageInfo.infoArrayChangeTimestamp;
+ stateInfo->imageCount = imageCountWithDyld;
+ stateInfo->initialImageCount = (uint32_t)(allImageInfo.initialImageCount+1);
+ if ( allImageInfo.infoArray != 0 )
+ stateInfo->dyldState = dyld_process_state_dyld_initialized;
+ if ( allImageInfo.libSystemInitialized != 0 ) {
+ stateInfo->dyldState = dyld_process_state_libSystem_initialized;
+ if ( allImageInfo.initialImageCount != allImageInfo.infoArrayCount )
+ stateInfo->dyldState = dyld_process_state_program_running;
+ }
+ if ( allImageInfo.errorMessage != 0 )
+ stateInfo->dyldState = allImageInfo.terminationFlags ? dyld_process_state_terminated_before_inits : dyld_process_state_dyld_terminated;
+
+ // fill in info for dyld
+ if ( allImageInfo.dyldPath != 0 ) {
+ if ( kern_return_t r = obj->addDyldImage(task, allImageInfo.dyldImageLoadAddress, allImageInfo.dyldPath, NULL) ) {
+ if ( kr != NULL )
+ *kr = r;
+ goto fail;
+ }
+ }
+
+ // fill in info for each image
+ for (uint32_t i=0; i < allImageInfo.infoArrayCount; ++i) {
+ if ( kern_return_t r = obj->addImage(task, sameCacheAsThisProcess, imageArray[i].imageLoadAddress, imageArray[i].imageFilePath, NULL) ) {
+ if ( kr != NULL )
+ *kr = r;
+ goto fail;
+ }
+ }
+
+ // sanity check internal data did not overflow
+ if ( obj->invalid() )
+ goto fail;
+
+ return obj;
+
+fail:
+ free(obj);
+ return NULL;
+}
+
+dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_return_t* kr)
+{
+ pid_t pid;
+ kern_return_t result = pid_for_task(task, &pid);
+ if ( result != KERN_SUCCESS ) {
+ if ( kr != NULL )
+ *kr = result;
+ return NULL;
+ }
+
+ unsigned imageCount = 0; // main executable and dyld
+ uint64_t mainExecutableAddress = 0;
+ uint64_t dyldAddress = 0;
+ char dyldPathBuffer[PATH_MAX+1];
+ char mainExecutablePathBuffer[PATH_MAX+1];
+ mach_vm_size_t size;
+ for (mach_vm_address_t address = 0; ; address += size) {
+ vm_region_basic_info_data_64_t info;
+ mach_port_t objectName;
+ unsigned int infoCount = VM_REGION_BASIC_INFO_COUNT_64;
+ result = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO,
+ (vm_region_info_t)&info, &infoCount, &objectName);
+ if ( result != KERN_SUCCESS )
+ break;
+ if ( info.protection == (VM_PROT_READ|VM_PROT_EXECUTE) ) {
+ if ( mainExecutableAddress == 0 ) {
+ mainExecutableAddress = address;
+ int len = proc_regionfilename(pid, mainExecutableAddress, mainExecutablePathBuffer, PATH_MAX);
+ if ( len != 0 )
+ mainExecutablePathBuffer[len] = '\0';
+ ++imageCount;
+ }
+ else if ( dyldAddress == 0 ) {
+ dyldAddress = address;
+ int len = proc_regionfilename(pid, dyldAddress, dyldPathBuffer, PATH_MAX);
+ if ( len != 0 )
+ dyldPathBuffer[len] = '\0';
+ ++imageCount;
+ }
+ //fprintf(stderr, "vm region: addr=0x%llX, size=0x%llX, prot=0x%X\n", (uint64_t)address, (uint64_t)size, info.protection);
+ }
+ }
+ //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer);
+ //fprintf(stderr, "app: addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer);
+
+ // allocate result object
+ size_t allocationSize = sizeof(dyld_process_info_base)
+ + sizeof(dyld_process_cache_info)
+ + sizeof(dyld_process_state_info)
+ + sizeof(ImageInfo)*(imageCount)
+ + sizeof(SegmentInfo)*imageCount*5
+ + imageCount*PATH_MAX;
+ void* storage = malloc(allocationSize);
+ dyld_process_info_base* obj = new (storage) dyld_process_info_base(imageCount, allocationSize); // placement new()
+
+ // fill in base info
+ dyld_process_cache_info* cacheInfo = obj->cacheInfo();
+ bzero(cacheInfo->cacheUUID, 16);
+ cacheInfo->cacheBaseAddress = 0;
+ cacheInfo->noCache = true;
+ cacheInfo->privateCache = false;
+
+ dyld_process_state_info* stateInfo = obj->stateInfo();
+ stateInfo->timestamp = 0;
+ stateInfo->imageCount = imageCount;
+ stateInfo->initialImageCount = imageCount;
+ stateInfo->dyldState = dyld_process_state_not_started;
+
+ // fill in info for dyld
+ if ( dyldAddress != 0 ) {
+ if ( kern_return_t r = obj->addDyldImage(task, dyldAddress, 0, dyldPathBuffer) ) {
+ if ( kr != NULL )
+ *kr = r;
+ free(obj);
+ return NULL;
+ }
+ }
+
+ // fill in info for each image
+ if ( mainExecutableAddress != 0 ) {
+ if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePathBuffer) ) {
+ if ( kr != NULL )
+ *kr = r;
+ free(obj);
+ return NULL;
+ }
+ }
+
+ return obj;
+}
+
+
+
+const char* dyld_process_info_base::addString(const char* str)
+{
+ size_t len = strlen(str) + 1;
+ _stringRevBumpPtr -= len;
+ strcpy(_stringRevBumpPtr, str);
+ return _stringRevBumpPtr;
+}
+
+const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddressInTask, kern_return_t* kr)
+{
+ char temp[PATH_MAX+8]; // +8 is to allow '\0' at temp[PATH_MAX]
+ mach_vm_size_t readSize = PATH_MAX;
+ if ( ((stringAddressInTask & 0xFFF) + PATH_MAX) < 4096 ) {
+ // string fits within page, only one vm_read needed
+ if ( kern_return_t r = mach_vm_read_overwrite(task, stringAddressInTask, PATH_MAX, (vm_address_t)&temp, &readSize) ) {
+ if ( kr != NULL )
+ *kr = r;
+ return NULL;
+ }
+ }
+ else {
+ // string may cross page boundary, split into two reads
+ size_t firstLen = 4096 - (stringAddressInTask & 0xFFF);
+ readSize = firstLen;
+ if ( kern_return_t r = mach_vm_read_overwrite(task, stringAddressInTask, firstLen, (vm_address_t)&temp, &readSize) ) {
+ if ( kr != NULL )
+ *kr = r;
+ return NULL;
+ }
+ temp[firstLen] = '\0';
+ if ( strlen(temp) >= firstLen ) {
+ readSize = PATH_MAX-firstLen;
+ if ( kern_return_t r = mach_vm_read_overwrite(task, stringAddressInTask+firstLen, PATH_MAX-firstLen, (vm_address_t)&temp+firstLen, &readSize) ) {
+ if ( kr != NULL )
+ *kr = r;
+ return NULL;
+ temp[PATH_MAX] = '\0'; // truncate any string that is too long
+ }
+ }
+ }
+ if ( kr != NULL )
+ *kr = KERN_SUCCESS;
+ return addString(temp);
+}
+
+
+kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal)
+{
+ _curImage->loadAddress = imageAddress;
+ _curImage->segmentStartIndex = _curSegmentIndex;
+ if ( imagePathLocal != NULL ) {
+ _curImage->path = addString(imagePathLocal);
+ }
+ else if ( sameCacheAsThisProcess && inCache(imagePath) ) {
+ _curImage->path = (const char*)imagePath;
+ }
+ else {
+ kern_return_t kr;
+ _curImage->path = copyPath(task, imagePath, &kr);
+ if ( kr )
+ return kr;
+ }
+ if ( sameCacheAsThisProcess && inCache(imageAddress) ) {
+ addInfoFromLoadCommands((mach_header*)imageAddress, imageAddress, 32*1024);
+ }
+ else {
+ mach_vm_size_t readSize = sizeof(mach_header_64);
+ mach_header_64 mhBuffer;
+ if ( kern_return_t r = mach_vm_read_overwrite(task, imageAddress, sizeof(mach_header_64), (vm_address_t)&mhBuffer, &readSize) ) {
+ return r;
+ }
+ size_t headerPagesSize = (sizeof(mach_header_64) + mhBuffer.sizeofcmds + 4095) & (-4096);
+ vm_address_t localCopyBuffer;
+ unsigned int localCopyBufferSize;
+ if ( kern_return_t r = mach_vm_read(task, imageAddress, headerPagesSize, &localCopyBuffer, &localCopyBufferSize) ) {
+ return r;
+ }
+ addInfoFromLoadCommands((mach_header*)localCopyBuffer, imageAddress, localCopyBufferSize);
+ vm_deallocate(mach_task_self(), localCopyBuffer, localCopyBufferSize);
+ }
+ _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
+ _curImage++;
+ return KERN_SUCCESS;
+}
+
+
+kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath)
+{
+ kern_return_t kr;
+ _curImage->loadAddress = dyldAddress;
+ _curImage->segmentStartIndex = _curSegmentIndex;
+ if ( localPath != NULL ) {
+ _curImage->path = addString(localPath);
+ }
+ else {
+ _curImage->path = copyPath(task, dyldPathAddress, &kr);
+ if ( kr )
+ return kr;
+ }
+
+ mach_vm_size_t readSize = sizeof(mach_header_64);
+ mach_header_64 mhBuffer;
+ if ( kern_return_t r = mach_vm_read_overwrite(task, dyldAddress, sizeof(mach_header_64), (vm_address_t)&mhBuffer, &readSize) ) {
+ return r;
+ }
+ size_t headerPagesSize = (sizeof(mach_header_64) + mhBuffer.sizeofcmds + 4095) & (-4096);
+ vm_address_t localCopyBuffer;
+ unsigned int localCopyBufferSize;
+ if ( kern_return_t r = mach_vm_read(task, dyldAddress, headerPagesSize, &localCopyBuffer, &localCopyBufferSize) ) {
+ return r;
+ }
+ addInfoFromLoadCommands((mach_header*)localCopyBuffer, dyldAddress, localCopyBufferSize);
+ vm_deallocate(mach_task_self(), localCopyBuffer, localCopyBufferSize);
+ _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
+ _curImage++;
+ return KERN_SUCCESS;
+}
+
+
+void dyld_process_info_base::addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size)
+{
+ const load_command* startCmds = NULL;
+ if ( mh->magic == MH_MAGIC_64 )
+ startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
+ else if ( mh->magic == MH_MAGIC )
+ startCmds = (load_command*)((char *)mh + sizeof(mach_header));
+ else
+ return; // not a mach-o file, or wrong endianness
+
+ const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds);
+ const load_command* cmd = startCmds;
+ for(uint32_t i = 0; i < mh->ncmds; ++i) {
+ const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
+ if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+ return; // malformed load command
+ }
+ if ( cmd->cmd == LC_UUID ) {
+ const uuid_command* uuidCmd = (uuid_command*)cmd;
+ memcpy(_curImage->uuid, uuidCmd->uuid, 16);
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* segCmd = (segment_command*)cmd;
+ _curSegment->name = copySegmentName(segCmd->segname);
+ _curSegment->addr = segCmd->vmaddr;
+ _curSegment->size = segCmd->vmsize;
+ _curSegment++;
+ _curSegmentIndex++;
+ }
+ else if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* segCmd = (segment_command_64*)cmd;
+ _curSegment->name = copySegmentName(segCmd->segname);
+ _curSegment->addr = segCmd->vmaddr;
+ _curSegment->size = segCmd->vmsize;
+ _curSegment++;
+ _curSegmentIndex++;
+ }
+ cmd = nextCmd;
+ }
+}
+
+const char* dyld_process_info_base::copySegmentName(const char* name)
+{
+ // don't copy names of standard segments into string pool
+ static const char* stdSegNames[] = {"__TEXT", "__DATA", "__LINKEDIT", "__DATA_DIRTY", "__DATA_CONST", "__OBJC", NULL };
+ for (const char** s=stdSegNames; *s != NULL; ++s) {
+ if ( strcmp(name, *s) == 0 )
+ return *s;
+ }
+ // copy custom segment names into string pool
+ return addString(name);
+}
+
+void dyld_process_info_base::forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const
+{
+ for (const ImageInfo* p = _firstImage; p < _curImage; ++p) {
+ callback(p->loadAddress, p->uuid, p->path);
+ }
+}
+
+void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const
+{
+ for (const ImageInfo* p = _firstImage; p < _curImage; ++p) {
+ if ( p->loadAddress == machHeaderAddress ) {
+ uint64_t slide = 0;
+ for (int i=0; i < p->segmentsCount; ++i) {
+ const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i];
+ if ( strcmp(seg->name, "__TEXT") == 0 ) {
+ slide = machHeaderAddress - seg->addr;
+ break;
+ }
+ }
+ for (int i=0; i < p->segmentsCount; ++i) {
+ const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i];
+ callback(seg->addr + slide, seg->size, seg->name);
+ }
+ break;
+ }
+ }
+}
+
+
+
+
+
+// Implementation that works with existing dyld data structures
+dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kr)
+{
+ if ( kr != NULL )
+ *kr = KERN_SUCCESS;
+
+ task_dyld_info_data_t task_dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ if ( kern_return_t r = task_info(task, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) {
+ if ( kr != NULL )
+ *kr = r;
+ return NULL;
+ }
+
+ //The kernel will return MACH_VM_MIN_ADDRESS for an executable that has not had dyld loaded
+ if (task_dyld_info.all_image_info_addr == MACH_VM_MIN_ADDRESS)
+ return NULL;
+
+ if ( task_dyld_info.all_image_info_size > sizeof(dyld_all_image_infos_64) )
+ return NULL;
+
+ // read all_image_infos struct
+ dyld_all_image_infos_64 allImageInfo64;
+ mach_vm_size_t readSize = task_dyld_info.all_image_info_size;
+ if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) {
+ if ( kr != NULL )
+ *kr = r;
+ return NULL;
+ }
+ if ( allImageInfo64.infoArrayCount == 0 ) {
+ // could be task was launch suspended or still launching, wait a moment to see
+ usleep(1000 * 50); // 50ms
+ if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) {
+ if ( kr != NULL )
+ *kr = r;
+ return NULL;
+ }
+ // if infoArrayCount is still zero, then target was most likely launched suspended
+ if ( allImageInfo64.infoArrayCount == 0 )
+ return dyld_process_info_base::makeSuspended(task, kr);
+ }
+
+ // bail out of dyld is too old
+ if ( allImageInfo64.version < 15 ) {
+ if ( kr != NULL )
+ *kr = KERN_INVALID_HOST;
+ return NULL;
+ }
+
+ // normalize by expanding 32-bit all_image_infos into 64-bit one
+ uint32_t imageCount = allImageInfo64.infoArrayCount;
+ size_t imageArraySize = imageCount * sizeof(dyld_image_info_64);
+ if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
+ const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64;
+ dyld_all_image_infos_64 info64;
+ bzero(&info64, sizeof(info64));
+ info64.version = allImageInfo32->version;
+ info64.infoArrayCount = allImageInfo32->infoArrayCount;
+ info64.infoArray = allImageInfo32->infoArray;
+ info64.processDetachedFromSharedRegion = allImageInfo32->processDetachedFromSharedRegion;
+ info64.libSystemInitialized = allImageInfo32->libSystemInitialized;
+ info64.dyldImageLoadAddress = allImageInfo32->dyldImageLoadAddress;
+ info64.initialImageCount = allImageInfo32->initialImageCount;
+ info64.uuidArrayCount = allImageInfo32->uuidArrayCount;
+ info64.uuidArray = allImageInfo32->uuidArray;
+ info64.dyldAllImageInfosAddress = allImageInfo32->dyldAllImageInfosAddress;
+ info64.sharedCacheSlide = allImageInfo32->sharedCacheSlide;
+ info64.infoArrayChangeTimestamp = allImageInfo32->infoArrayChangeTimestamp;
+ info64.sharedCacheBaseAddress = allImageInfo32->sharedCacheBaseAddress;
+ info64.dyldPath = allImageInfo32->dyldPath;
+ memcpy((void*)(info64.sharedCacheUUID), (void*)(allImageInfo32->sharedCacheUUID), 16);
+ allImageInfo64 = info64;
+ imageCount = allImageInfo64.infoArrayCount;
+ imageArraySize = imageCount * sizeof(dyld_image_info_32);
+ }
+
+ // don't do any (more) work if target process's dyld timestamp has not changed since previous query
+ if ( (timestamp != 0) && (timestamp == allImageInfo64.infoArrayChangeTimestamp) ) {
+ if ( kr != NULL )
+ *kr = KERN_SUCCESS;
+ return NULL;
+ }
+
+ // For the moment we are going to truncate any image list longer than 8192 because some programs do
+ // terrible things that corrupt their own image lists and we need to stop clients from crashing
+ // reading them. We can try to do something more advanced in the future. rdar://27446361
+ imageCount = MIN(imageCount, 8192);
+
+ // read image array
+ dyld_image_info_64 imageArray64[imageCount];
+ if ( kern_return_t r = mach_vm_read_overwrite(task, allImageInfo64.infoArray, imageArraySize, (vm_address_t)&imageArray64, &readSize) ) {
+ if ( kr != NULL )
+ *kr = r;
+ return NULL;
+ }
+ // normalize by expanding 32-bit image_infos into 64-bit ones
+ if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
+ const dyld_image_info_32* imageArray32 = (dyld_image_info_32*)&imageArray64;
+ dyld_image_info_64 tempArray[imageCount];
+ for (uint32_t i=0; i < imageCount; ++i) {
+ tempArray[i].imageLoadAddress = imageArray32[i].imageLoadAddress;
+ tempArray[i].imageFilePath = imageArray32[i].imageFilePath;
+ tempArray[i].imageFileModDate = imageArray32[i].imageFileModDate;
+ }
+ memcpy(imageArray64, tempArray, sizeof(dyld_image_info_64)*imageCount);
+ }
+
+ // create object based on local copy of all image infos and image array
+ return dyld_process_info_base::make(task, allImageInfo64, imageArray64, kr);
+}
+
+
+void _dyld_process_info_get_state(dyld_process_info info, dyld_process_state_info* stateInfo)
+{
+ *stateInfo = *info->stateInfo();
+}
+
+void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo)
+{
+ *cacheInfo = *info->cacheInfo();
+}
+
+void _dyld_process_info_retain(dyld_process_info info)
+{
+ info->retainCount() += 1;
+}
+
+void _dyld_process_info_release(dyld_process_info info)
+{
+ info->retainCount() -= 1;
+ if ( info->retainCount() == 0 )
+ free((void*)info);
+}
+
+void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path))
+{
+ info->forEachImage(callback);
+}
+
+
+void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName))
+{
+ info->forEachSegment(machHeaderAddress, callback);
+}
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2016 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 _DYLD_PROCESS_INFO_INTERNAL_H_
+#define _DYLD_PROCESS_INFO_INTERNAL_H_
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+
+
+struct dyld_all_image_infos_32 {
+ uint32_t version;
+ uint32_t infoArrayCount;
+ uint32_t infoArray;
+ uint32_t notification;
+ bool processDetachedFromSharedRegion;
+ bool libSystemInitialized;
+ uint32_t dyldImageLoadAddress;
+ uint32_t jitInfo;
+ uint32_t dyldVersion;
+ uint32_t errorMessage;
+ uint32_t terminationFlags;
+ uint32_t coreSymbolicationShmPage;
+ uint32_t systemOrderFlag;
+ uint32_t uuidArrayCount;
+ uint32_t uuidArray;
+ uint32_t dyldAllImageInfosAddress;
+ uint32_t initialImageCount;
+ uint32_t errorKind;
+ uint32_t errorClientOfDylibPath;
+ uint32_t errorTargetDylibPath;
+ uint32_t errorSymbol;
+ uint32_t sharedCacheSlide;
+ uint8_t sharedCacheUUID[16];
+ uint32_t sharedCacheBaseAddress;
+ uint64_t infoArrayChangeTimestamp;
+ uint32_t dyldPath;
+ uint32_t notifyMachPorts[2];
+ uint32_t reserved[11];
+};
+
+struct dyld_all_image_infos_64 {
+ uint32_t version;
+ uint32_t infoArrayCount;
+ uint64_t infoArray;
+ uint64_t notification;
+ bool processDetachedFromSharedRegion;
+ bool libSystemInitialized;
+ uint32_t paddingToMakeTheSizeCorrectOn32bitAndDoesntAffect64b; // NOT PART OF DYLD_ALL_IMAGE_INFOS!
+ uint64_t dyldImageLoadAddress;
+ uint64_t jitInfo;
+ uint64_t dyldVersion;
+ uint64_t errorMessage;
+ uint64_t terminationFlags;
+ uint64_t coreSymbolicationShmPage;
+ uint64_t systemOrderFlag;
+ uint64_t uuidArrayCount;
+ uint64_t uuidArray;
+ uint64_t dyldAllImageInfosAddress;
+ uint64_t initialImageCount;
+ uint64_t errorKind;
+ uint64_t errorClientOfDylibPath;
+ uint64_t errorTargetDylibPath;
+ uint64_t errorSymbol;
+ uint64_t sharedCacheSlide;
+ uint8_t sharedCacheUUID[16];
+ uint64_t sharedCacheBaseAddress;
+ uint64_t infoArrayChangeTimestamp;
+ uint64_t dyldPath;
+ uint32_t notifyMachPorts[2];
+ uint64_t reserved[12];
+};
+
+struct dyld_image_info_32 {
+ uint32_t imageLoadAddress;
+ uint32_t imageFilePath;
+ uint32_t imageFileModDate;
+};
+struct dyld_image_info_64 {
+ uint64_t imageLoadAddress;
+ uint64_t imageFilePath;
+ uint64_t imageFileModDate;
+};
+
+#define DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE (32*1024)
+#define DYLD_PROCESS_INFO_NOTIFY_LOAD_ID 0x1000
+#define DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID 0x2000
+
+
+struct dyld_process_info_image_entry {
+ uuid_t uuid;
+ uint64_t loadAddress;
+ uint32_t pathStringOffset;
+ uint32_t pathLength;
+};
+
+struct dyld_process_info_notify_header {
+ mach_msg_header_t header;
+ uint32_t version;
+ uint32_t imageCount;
+ uint32_t imagesOffset;
+ uint32_t stringsOffset;
+ uint64_t timestamp;
+};
+
+
+
+
+#endif // _DYLD_PROCESS_INFO_INTERNAL_H_
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2016 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 <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include <mach/shared_region.h>
+#include <mach/mach_vm.h>
+#include <libkern/OSAtomic.h>
+
+#include "dyld_process_info.h"
+#include "dyld_process_info_internal.h"
+#include "dyld_images.h"
+#include "dyld_priv.h"
+
+typedef void (^Notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path);
+typedef void (^NotifyExit)();
+
+
+//
+// Object used for monitoring another processes dyld loads
+//
+struct __attribute__((visibility("hidden"))) dyld_process_info_notify_base
+{
+ static dyld_process_info_notify_base* make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr);
+ ~dyld_process_info_notify_base();
+
+ uint32_t& retainCount() const { return _retainCount; }
+
+private:
+ dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task);
+ kern_return_t makePorts();
+ kern_return_t pokeSendPortIntoTarget();
+ kern_return_t unpokeSendPortInTarget();
+ void setMachSourceOnQueue();
+ void* operator new (size_t, void* buf) { return buf; }
+
+ mutable uint32_t _retainCount;
+ dispatch_queue_t _queue;
+ Notify _notify;
+ NotifyExit _notifyExit;
+ task_t _targetTask;
+ dispatch_source_t _machSource;
+ uint64_t _portAddressInTarget;
+ mach_port_t _sendPortInTarget; // target is process being watched for image loading/unloading
+ mach_port_t _receivePortInMonitor; // monitor is process being notified of image loading/unloading
+};
+
+
+dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task)
+ : _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit), _targetTask(task), _machSource(NULL), _portAddressInTarget(0), _sendPortInTarget(0), _receivePortInMonitor(0)
+{
+ dispatch_retain(_queue);
+}
+
+dyld_process_info_notify_base::~dyld_process_info_notify_base()
+{
+ if ( _machSource ) {
+ dispatch_release(_machSource);
+ _machSource = NULL;
+ }
+ if ( _portAddressInTarget ) {
+ unpokeSendPortInTarget();
+ _portAddressInTarget = 0;
+ }
+ if ( _sendPortInTarget ) {
+ _sendPortInTarget = 0;
+ }
+ dispatch_release(_queue);
+ if ( _receivePortInMonitor != 0 ) {
+ mach_port_deallocate(mach_task_self(), _receivePortInMonitor);
+ _receivePortInMonitor = 0;
+ }
+}
+
+
+dyld_process_info_notify_base* dyld_process_info_notify_base::make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr)
+{
+ void* storage = malloc(sizeof(dyld_process_info_notify_base));
+ dyld_process_info_notify_base* obj = new (storage) dyld_process_info_notify_base(queue, notify, notifyExit, task);
+
+ if ( kern_return_t r = obj->makePorts() ) {
+ if ( kr != NULL )
+ *kr = r;
+ goto fail;
+ }
+
+ obj->setMachSourceOnQueue();
+
+ if ( kern_return_t r = obj->pokeSendPortIntoTarget() ) {
+ if ( kr != NULL )
+ *kr = r;
+ goto fail;
+ }
+
+ if ( kr != NULL )
+ *kr = KERN_SUCCESS;
+ return obj;
+
+fail:
+ free(obj);
+ return NULL;
+}
+
+
+kern_return_t dyld_process_info_notify_base::makePorts()
+{
+ // Allocate a port to listen on in this monitoring task
+ if ( kern_return_t r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_receivePortInMonitor) )
+ return r;
+
+ // Add send rights for replying
+ if ( kern_return_t r = mach_port_insert_right(mach_task_self(), _receivePortInMonitor, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) )
+ return r;
+
+ // Allocate a name in the target. We need a new name to add send rights to
+ if ( kern_return_t r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget) )
+ return r;
+
+ // Deallocate the dead name
+ if ( kern_return_t r = mach_port_mod_refs(_targetTask, _sendPortInTarget, MACH_PORT_RIGHT_DEAD_NAME, -1) )
+ return r;
+
+ // Make the dead name a send right to our listening port
+ if ( kern_return_t r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) )
+ return r;
+
+ // Notify us if the target dies
+ mach_port_t previous = MACH_PORT_NULL;
+ if ( kern_return_t r = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous))
+ return r;
+
+ //fprintf(stderr, "_sendPortInTarget=%d, _receivePortInMonitor=%d\n", _sendPortInTarget, _receivePortInMonitor);
+ return KERN_SUCCESS;
+}
+
+
+
+void dyld_process_info_notify_base::setMachSourceOnQueue()
+{
+ NotifyExit exitHandler = _notifyExit;
+ _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue);
+ dispatch_source_set_event_handler(_machSource, ^{
+ uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE];
+ mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
+
+ kern_return_t r = mach_msg(h, MACH_RCV_MSG, 0, sizeof(messageBuffer), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if ( r == KERN_SUCCESS ) {
+ //fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
+ if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_LOAD_ID || h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID ) {
+ // run notifier block for each [un]load image
+ const dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)messageBuffer;
+ const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&messageBuffer[header->imagesOffset];
+ const char* const stringPool = (char*)&messageBuffer[header->stringsOffset];
+ for (unsigned i=0; i < header->imageCount; ++i) {
+ bool isUnload = (h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID);
+ _notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset);
+ }
+ // reply to dyld, so it can continue
+ mach_msg_header_t replyHeader;
+ replyHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
+ replyHeader.msgh_id = 0;
+ replyHeader.msgh_local_port = MACH_PORT_NULL;
+ replyHeader.msgh_remote_port = h->msgh_remote_port;
+ replyHeader.msgh_reserved = 0;
+ replyHeader.msgh_size = sizeof(replyHeader);
+ mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL);
+ }
+ else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) {
+ mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port;
+ //fprintf(stderr, "received message id=MACH_NOTIFY_PORT_DELETED, size=%d, deadPort=%d\n", h->msgh_size, deadPort);
+ if ( deadPort == _sendPortInTarget ) {
+ // target process died. Clean up ports
+ _sendPortInTarget = 0;
+ mach_port_deallocate(mach_task_self(), _receivePortInMonitor);
+ _receivePortInMonitor = 0;
+ _portAddressInTarget = 0;
+ // notify that target is gone
+ exitHandler();
+ }
+ }
+ else {
+ fprintf(stderr, "received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
+ }
+ }
+ });
+ dispatch_resume(_machSource);
+}
+
+
+kern_return_t dyld_process_info_notify_base::pokeSendPortIntoTarget()
+{
+ // get location on all_image_infos in target task
+ task_dyld_info_data_t taskDyldInfo;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ kern_return_t r = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &count);
+ if ( r )
+ return r;
+
+ // remap the page containing all_image_infos into this process r/w
+ mach_vm_address_t mappedAddress = 0;
+ mach_vm_size_t mappedSize = taskDyldInfo.all_image_info_size;
+ vm_prot_t curProt = VM_PROT_NONE;
+ vm_prot_t maxProt = VM_PROT_NONE;
+ r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
+ _targetTask, taskDyldInfo.all_image_info_addr, false, &curProt, &maxProt, VM_INHERIT_NONE);
+ if ( r )
+ return r;
+ if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) )
+ return KERN_PROTECTION_FAILURE;
+
+ // atomically set port into all_image_info_struct
+ static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits");
+
+ mach_vm_address_t mappedAddressToPokePort = 0;
+ if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 )
+ mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_32,notifyMachPorts);
+ else
+ mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_64,notifyMachPorts);
+
+ // use first available slot
+ bool slotFound = false;
+ for (int slotIndex=0; slotIndex < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slotIndex) {
+ if ( OSAtomicCompareAndSwap32Barrier(0, _sendPortInTarget, (volatile int32_t*)mappedAddressToPokePort) ) {
+ slotFound = true;
+ break;
+ }
+ mappedAddressToPokePort += sizeof(uint32_t);
+ }
+ if ( !slotFound ) {
+ mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
+ return KERN_UREFS_OVERFLOW;
+ }
+ _portAddressInTarget = taskDyldInfo.all_image_info_addr + mappedAddressToPokePort - mappedAddress;
+ //fprintf(stderr, "poked port %d into target at address 0x%llX\n", _sendPortInTarget, _portAddressInTarget);
+ mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
+ return r;
+}
+
+
+
+kern_return_t dyld_process_info_notify_base::unpokeSendPortInTarget()
+{
+ // remap the page containing all_image_infos into this process r/w
+ mach_vm_address_t mappedAddress = 0;
+ mach_vm_size_t mappedSize = sizeof(mach_port_t);
+ vm_prot_t curProt = VM_PROT_NONE;
+ vm_prot_t maxProt = VM_PROT_NONE;
+ kern_return_t r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
+ _targetTask, _portAddressInTarget, false, &curProt, &maxProt, VM_INHERIT_NONE);
+ if ( r )
+ return r;
+ if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) )
+ return KERN_PROTECTION_FAILURE;
+
+ OSAtomicCompareAndSwap32Barrier(_sendPortInTarget, 0, (volatile int32_t*)mappedAddress);
+
+ //fprintf(stderr, "cleared port %d from target\n", _sendPortInTarget);
+ mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
+ return r;
+}
+
+
+
+dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue,
+ void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path),
+ void (^notifyExit)(),
+ kern_return_t* kr)
+{
+ return dyld_process_info_notify_base::make(task, queue, notify, notifyExit, kr);
+}
+
+void _dyld_process_info_notify_retain(dyld_process_info_notify object)
+{
+ object->retainCount() += 1;
+}
+
+void _dyld_process_info_notify_release(dyld_process_info_notify object)
+{
+ object->retainCount() -= 1;
+ if ( object->retainCount() == 0 ) {
+ object->~dyld_process_info_notify_base();
+ free((void*)object);
+ }
+}
+
+
+
// dyld::halt(const char* msg);
extern void _ZN4dyld4haltEPKc(const char* msg) __attribute__((noreturn));
+extern void dyld_fatal_error(const char* errString) __attribute__((noreturn));
+
// abort called by C++ unwinding code
void abort()
return gSyscallHelpers->closedir(dirp);
}
-#define SUPPORT_HOST_10_10 1
-
-#if SUPPORT_HOST_10_10
-typedef int (*FuncPtr_nanosleep)(const struct timespec*, struct timespec*);
-typedef kern_return_t (*FuncPtr_mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*);
-typedef mach_msg_return_t (*FuncPtr_mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t);
-typedef int (*FuncPtr_kill)(pid_t pid, int sig);
-typedef pid_t (*FuncPtr_getpid)();
-typedef bool (*FuncPtr_OSAtomicCompareAndSwap32)(int32_t, int32_t, volatile int32_t*);
-
-static FuncPtr_nanosleep proc_nanosleep = NULL;
-static FuncPtr_mach_port_allocate proc_mach_port_allocate = NULL;
-static FuncPtr_mach_msg proc_mach_msg = NULL;
-static FuncPtr_kill proc_kill = NULL;
-static FuncPtr_getpid proc_getpid = NULL;
-static FuncPtr_OSAtomicCompareAndSwap32 proc_OSAtomicCompareAndSwap32 = NULL;
-
-
-int nanosleep(const struct timespec* p1, struct timespec* p2)
+void xcoresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
{
- return (*proc_nanosleep)(p1, p2);
+ // if host dyld supports this notifier, call into host dyld
+ if ( gSyscallHelpers->version >= 4 )
+ return gSyscallHelpers->coresymbolication_load_notifier(connection, timestamp, path, mh);
}
-kern_return_t mach_port_allocate(ipc_space_t p1, mach_port_right_t p2, mach_port_name_t* p3)
+void xcoresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
{
- return (*proc_mach_port_allocate)(p1, p2, p3);
+ // if host dyld supports this notifier, call into host dyld
+ if ( gSyscallHelpers->version >= 4 )
+ return gSyscallHelpers->coresymbolication_unload_notifier(connection, timestamp, path, mh);
}
-mach_msg_return_t mach_msg(mach_msg_header_t* p1, mach_msg_option_t p2, mach_msg_size_t p3, mach_msg_size_t p4, mach_port_name_t p5, mach_msg_timeout_t p6, mach_port_name_t p7)
-{
- return (*proc_mach_msg)(p1, p2, p3, p4, p5, p6, p7);
-}
-int kill(pid_t p1, int p2)
-{
- return (*proc_kill)(p1, p2);
-}
-pid_t getpid()
-{
- return (*proc_getpid)();
-}
+#define SUPPORT_HOST_10_11 1
+
+#if SUPPORT_HOST_10_11
+typedef int (*FuncPtr_proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t bufferSize);
+typedef pid_t (*FuncPtr_getpid)();
+typedef bool (*FuncPtr_mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly);
+typedef kern_return_t (*FuncPtr_mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*);
+typedef mach_msg_return_t (*FuncPtr_mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t);
+
+static FuncPtr_proc_regionfilename proc_proc_regionfilename = NULL;
+static FuncPtr_getpid proc_getpid = NULL;
+static FuncPtr_mach_port_insert_right proc_mach_port_insert_right = NULL;
+static FuncPtr_mach_port_allocate proc_mach_port_allocate = NULL;
+static FuncPtr_mach_msg proc_mach_msg = NULL;
-bool OSAtomicCompareAndSwap32(int32_t p1, int32_t p2, volatile int32_t* p3)
-{
- return (*proc_OSAtomicCompareAndSwap32)(p1, p2, p3);
-}
// Look up sycalls in host dyld needed by coresymbolication_ routines in dyld_sim
static void findHostFunctions() {
// Only look up symbols once
- if ( proc_nanosleep != NULL )
+ if ( proc_mach_msg != NULL )
return;
struct dyld_all_image_infos* imageInfo = (struct dyld_all_image_infos*)(gSyscallHelpers->getProcessInfo());
for (const macho_nlist* s = localsStart; s < localsEnd; ++s) {
if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
const char* name = &symbolTableStrings[s->n_un.n_strx];
- if ( strcmp(name, "_nanosleep") == 0 )
- proc_nanosleep = (FuncPtr_nanosleep)(s->n_value + slide);
+ if ( strcmp(name, "_proc_regionfilename") == 0 )
+ proc_proc_regionfilename = (FuncPtr_proc_regionfilename)(s->n_value + slide);
+ else if ( strcmp(name, "_getpid") == 0 )
+ proc_getpid = (FuncPtr_getpid)(s->n_value + slide);
+ else if ( strcmp(name, "mach_port_insert_right") == 0 )
+ proc_mach_port_insert_right = (FuncPtr_mach_port_insert_right)(s->n_value + slide);
else if ( strcmp(name, "_mach_port_allocate") == 0 )
proc_mach_port_allocate = (FuncPtr_mach_port_allocate)(s->n_value + slide);
else if ( strcmp(name, "_mach_msg") == 0 )
proc_mach_msg = (FuncPtr_mach_msg)(s->n_value + slide);
- else if ( strcmp(name, "_kill") == 0 )
- proc_kill = (FuncPtr_kill)(s->n_value + slide);
- else if ( strcmp(name, "_getpid") == 0 )
- proc_getpid = (FuncPtr_getpid)(s->n_value + slide);
- else if ( strcmp(name, "_OSAtomicCompareAndSwap32") == 0 )
- proc_OSAtomicCompareAndSwap32 = (FuncPtr_OSAtomicCompareAndSwap32)(s->n_value + slide);
}
}
}
#endif
-void xcoresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+
+int proc_regionfilename(int pid, uint64_t address, void* buffer, uint32_t bufferSize)
{
- // if host dyld supports this notifier, call into host dyld
- if ( gSyscallHelpers->version >= 4 )
- return gSyscallHelpers->coresymbolication_load_notifier(connection, timestamp, path, mh);
-#if SUPPORT_HOST_10_10
- // otherwise use notifier code in dyld_sim
+ if ( gSyscallHelpers->version >= 5 )
+ return gSyscallHelpers->proc_regionfilename(pid, address, buffer, bufferSize);
+#if SUPPORT_HOST_10_11
+ findHostFunctions();
+ if ( proc_proc_regionfilename )
+ return (*proc_proc_regionfilename)(pid, address, buffer, bufferSize);
+ else
+ return 0;
+#else
+ return 0;
+#endif
+}
+
+pid_t getpid()
+{
+ if ( gSyscallHelpers->version >= 5 )
+ return gSyscallHelpers->getpid();
+#if SUPPORT_HOST_10_11
findHostFunctions();
- coresymbolication_load_notifier(connection, timestamp, path, mh);
+ return (*proc_getpid)();
+#else
+ return 0;
#endif
}
-void xcoresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+kern_return_t mach_port_insert_right(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly)
{
- // if host dyld supports this notifier, call into host dyld
- if ( gSyscallHelpers->version >= 4 )
- return gSyscallHelpers->coresymbolication_unload_notifier(connection, timestamp, path, mh);
-#if SUPPORT_HOST_10_10
- // otherwise use notifier code in dyld_sim
+ if ( gSyscallHelpers->version >= 5 )
+ return gSyscallHelpers->mach_port_insert_right(task, name, poly, polyPoly);
+#if SUPPORT_HOST_10_11
findHostFunctions();
- coresymbolication_unload_notifier(connection, timestamp, path, mh);
+ if ( proc_mach_port_insert_right )
+ return (*proc_mach_port_insert_right)(task, name, poly, polyPoly);
+ else
+ return KERN_NOT_SUPPORTED;
+#else
+ return KERN_NOT_SUPPORTED;
#endif
}
+kern_return_t mach_port_allocate(ipc_space_t task, mach_port_right_t right, mach_port_name_t* name)
+{
+ if ( gSyscallHelpers->version >= 5 )
+ return gSyscallHelpers->mach_port_allocate(task, right, name);
+#if SUPPORT_HOST_10_11
+ findHostFunctions();
+ return (*proc_mach_port_allocate)(task, right, name);
+#else
+ return KERN_NOT_SUPPORTED;
+#endif
+}
+
+kern_return_t mach_msg(mach_msg_header_t* msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t rcv_size, mach_port_name_t rcv_name, mach_msg_timeout_t timeout, mach_port_name_t notify)
+{
+ if ( gSyscallHelpers->version >= 5 )
+ return gSyscallHelpers->mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify);
+#if SUPPORT_HOST_10_11
+ findHostFunctions();
+ return (*proc_mach_msg)(msg, option, send_size, rcv_size, rcv_name, timeout, notify);
+#else
+ return KERN_NOT_SUPPORTED;
+#endif
+}
+
+
+void abort_with_payload(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags)
+{
+ if ( gSyscallHelpers->version >= 6 )
+ gSyscallHelpers->abort_with_payload(reason_namespace, reason_code, payload, payload_size, reason_string, reason_flags);
+ dyld_fatal_error(reason_string);
+}
+
+kern_return_t task_register_dyld_image_infos(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt) {
+ if ( gSyscallHelpers->version >= 7 )
+ return gSyscallHelpers->task_register_dyld_image_infos(task, dyld_images, dyld_imagesCnt);
+ return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t task_unregister_dyld_image_infos(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt) {
+ if ( gSyscallHelpers->version >= 7 )
+ return gSyscallHelpers->task_unregister_dyld_image_infos(task, dyld_images, dyld_imagesCnt);
+ return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t task_get_dyld_image_infos(task_t task, dyld_kernel_image_info_array_t *dyld_images, mach_msg_type_number_t *dyld_imagesCnt) {
+ if ( gSyscallHelpers->version >= 7 )
+ return gSyscallHelpers->task_get_dyld_image_infos(task, dyld_images, dyld_imagesCnt);
+ return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t task_register_dyld_shared_cache_image_info(task_t task, dyld_kernel_image_info_t dyld_cache_image, boolean_t no_cache, boolean_t private_cache) {
+ if ( gSyscallHelpers->version >= 7 )
+ return gSyscallHelpers->task_register_dyld_shared_cache_image_info(task, dyld_cache_image, no_cache, private_cache);
+ return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t task_register_dyld_set_dyld_state(task_t task, uint8_t dyld_state) {
+ if ( gSyscallHelpers->version >= 7 )
+ return gSyscallHelpers->task_register_dyld_set_dyld_state(task, dyld_state);
+ return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t task_register_dyld_get_process_state(task_t task, dyld_kernel_process_info_t *dyld_process_state) {
+ if ( gSyscallHelpers->version >= 7 )
+ return gSyscallHelpers->task_register_dyld_get_process_state(task, dyld_process_state);
+ return KERN_NOT_SUPPORTED;
+}
int* __error(void) {
return gSyscallHelpers->errnoAddress();
#endif // TARGET_IPHONE_SIMULATOR
+#if ! TARGET_IPHONE_SIMULATOR
+ #include "mach-o/dyld_process_info.h"
+
+ void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[])
+ {
+ // Do nothing. This exists for the debugger to set a break point on to see what images have been loaded or unloaded.
+ }
+#endif
+
+
+
+
#if __arm__
// returns address of TLV in r0, all other registers preserved
+ .align 2
.globl _tlv_get_addr
.private_extern _tlv_get_addr
_tlv_get_addr:
typedef void (*InitFunc)(void);
InitFunc* funcs = (InitFunc*)(sect->addr + slide);
const size_t count = sect->size / sizeof(uintptr_t);
- for (size_t i=count; i > 0; --i) {
- InitFunc func = funcs[i-1];
+ for (size_t j=count; j > 0; --j) {
+ InitFunc func = funcs[j-1];
func();
}
}
}
}
-// called by dyld when a image is loaded
-static const char* tlv_load_notification(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[])
+
+void tlv_load_notification(const struct mach_header* mh, intptr_t slide)
{
- // this is called on all images, even those without TLVs, so we want
- // this to be fast. The linker sets MH_HAS_TLV_DESCRIPTORS so we don't
- // have to search images just to find the don't have TLVs.
- for (uint32_t i=0; i < infoCount; ++i) {
- if ( info[i].imageLoadAddress->flags & MH_HAS_TLV_DESCRIPTORS )
- tlv_initialize_descriptors(info[i].imageLoadAddress);
- }
- return NULL;
+ // This is called on all images, even those without TLVs. So we want this to be fast.
+ // The linker sets MH_HAS_TLV_DESCRIPTORS so we don't have to search images just to find the don't have TLVs.
+ if ( mh->flags & MH_HAS_TLV_DESCRIPTORS )
+ tlv_initialize_descriptors(mh);
}
(void)pthread_key_create(&tlv_terminators_key, &tlv_finalize);
// register with dyld for notification when images are loaded
- dyld_register_image_state_change_handler(dyld_image_state_bound, true, tlv_load_notification);
+ _dyld_register_func_for_add_image(tlv_load_notification);
+
}
--- /dev/null
+
+When the dyld_tests target is built, all test cases are built into /AppleInternal/.
+A test case is a directory in $SRCROOT/testing/test-cases/ whose name ends in ".dtest".
+The build system scraps any .c or .cxx files in the .dtest directory looking for BUILD: or RUN: lines.
+The BUILD: lines are use to build the test case binaries.
+The RUN: lines are used to build the information needed for BATS to run the test cases.
+Example, main.c may contain:
+
+ // BUILD: $CC main.c -o $BUILD_DIR/example.exe
+ // RUN: ./example.exe
+ int main() { return 0; }
+
+When build lines are executed, the current directory is set to the test case's .dtest dir.
+Build lines may contain the follow variables:
+ $BUILD_DIR - expands to the directory in $DSTROOT where this test case binaries are installed
+ $RUN_DIR - expands to the directory in /AppleInternal/ where this test case binaries will be run
+ $TEMP_DIR - expands to a temporary directory that will be delete after this test case is built
+ $CC - expands to the C compiler command line for the current platform. It includes
+ the min-os-version option, then SDK option, and the architectures.
+ $CXX - expands to the C++ compiler plus standard options
+
+When run lines are executed, the current directory is set to what $RUN_DIR was during build.
+Run lines may contain the follow variables:
+ $RUN_DIR - expands to the directory in /AppleInternal/ where this test case binaries are installed
+ $REQUIRE_CRASH - expands to the 'nocr' tool which is used when a program is expected to "crash" in order to pass
+
+
+When a test program runs, it should initially print out the name of the test case. Ex:
+ [BEGIN] dlfoo-thread-safe
+Then it should print a pass or fail message with the same test name. Ex:
+ [PASS] dlfoo-thread-safe
+
+
+To support tests that dyld is supposed to terminate, use $REQUIRE_CRASH on the RUN: line
+along with setting env var NOCR_TEST_NAME to the name of the test case. When that env
+var is set, the nocr tool wil print out the [BEGIN], then [PASS] if the test crashes
+otherwise [FAIL]. Ex:
+ // RUN: NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe
+
+
+To support tests that are platform specific, add the BUILD_ONLY: line which specifies the platform.
+Valid platforms are: MacOSX, iOS, watchOS, and tvOS. When a specific platform is specified, a
+new min OS version can also be specified via the BUILD_MIN_OS option. For instance:
+ // BUILD_ONLY: MacOSX
+ // BUILD_MIN_OS: 10.5
+
+
--- /dev/null
+#!/usr/bin/python2.7
+
+import plistlib
+import string
+import argparse
+import sys
+import os
+import tempfile
+import shutil
+import subprocess
+
+
+#
+# Scan files in .dtest directory looking for BUILD: or RUN: directives.
+# Return a dictionary of all directives.
+#
+def parseDirectives(testCaseSourceDir):
+ onlyLines = []
+ buildLines = []
+ runLines = []
+ minOS = ""
+ timeout = ""
+ for file in os.listdir(testCaseSourceDir):
+ if file.endswith((".c", ".cpp", ".cxx")):
+ with open(testCaseSourceDir + "/" + file) as f:
+ for line in f.read().splitlines():
+ buildIndex = string.find(line, "BUILD:")
+ if buildIndex != -1:
+ buildLines.append(line[buildIndex + 6:].lstrip())
+ runIndex = string.find(line, "RUN:")
+ if runIndex != -1:
+ runLines.append(line[runIndex+4:].lstrip())
+ onlyIndex = string.find(line, "BUILD_ONLY:")
+ if onlyIndex != -1:
+ onlyLines.append(line[onlyIndex+11:].lstrip())
+ minOsIndex = string.find(line, "BUILD_MIN_OS:")
+ if minOsIndex != -1:
+ minOS = line[minOsIndex+13:].lstrip()
+ timeoutIndex = string.find(line, "RUN_TIMEOUT:")
+ if timeoutIndex != -1:
+ timeout = line[timeoutIndex+12:].lstrip()
+
+ return {
+ "BUILD": buildLines,
+ "BUILD_ONLY": onlyLines,
+ "BUILD_MIN_OS": minOS,
+ "RUN": runLines,
+ "RUN_TIMEOUT": timeout
+ }
+
+
+#
+# Look at directives dictionary to see if this test should be skipped for this platform
+#
+def useTestCase(testCaseDirectives, platformName):
+ onlyLines = testCaseDirectives["BUILD_ONLY"]
+ for only in onlyLines:
+ if only == "MacOSX" and platformName != "macosx":
+ return False
+ if only == "iOS" and platformName != "iphoneos":
+ return False
+ return True
+
+
+#
+# Use BUILD directives to construct the test case
+# Use RUN directives to generate a shell script to run test(s)
+#
+def buildTestCase(testCaseDirectives, testCaseSourceDir, toolsDir, sdkDir, minOsOptionsName, defaultMinOS, archOptions, testCaseDestDirBuild, testCaseDestDirRun):
+ scratchDir = tempfile.mkdtemp()
+ if testCaseDirectives["BUILD_MIN_OS"]:
+ minOS = testCaseDirectives["BUILD_MIN_OS"]
+ else:
+ minOS = defaultMinOS
+ compilerSearchOptions = " -isysroot " + sdkDir + " -I" + sdkDir + "/System/Library/Frameworks/System.framework/PrivateHeaders"
+ if minOsOptionsName == "mmacosx-version-min":
+ taskForPidCommand = "touch "
+ else:
+ taskForPidCommand = "codesign --force --sign - --entitlements " + testCaseSourceDir + "/../../task_for_pid_entitlement.plist "
+ buildSubs = {
+ "CC": toolsDir + "/usr/bin/clang " + archOptions + " -" + minOsOptionsName + "=" + str(minOS) + compilerSearchOptions,
+ "CXX": toolsDir + "/usr/bin/clang++ " + archOptions + " -" + minOsOptionsName + "=" + str(minOS) + compilerSearchOptions,
+ "BUILD_DIR": testCaseDestDirBuild,
+ "RUN_DIR": testCaseDestDirRun,
+ "TEMP_DIR": scratchDir,
+ "TASK_FOR_PID_ENABLE": taskForPidCommand
+ }
+ os.makedirs(testCaseDestDirBuild)
+ os.chdir(testCaseSourceDir)
+ print >> sys.stderr, "cd " + testCaseSourceDir
+ for line in testCaseDirectives["BUILD"]:
+ cmd = string.Template(line).safe_substitute(buildSubs)
+ print >> sys.stderr, cmd
+ cmdList = []
+ cmdList = string.split(cmd)
+ result = subprocess.call(cmdList)
+ if result:
+ return result
+ shutil.rmtree(scratchDir, ignore_errors=True)
+ sudoSub = ""
+ if minOsOptionsName == "mmacosx-version-min":
+ sudoSub = "sudo"
+ runSubs = {
+ "RUN_DIR": testCaseDestDirRun,
+ "REQUIRE_CRASH": "nocr -require_crash",
+ "SUDO": sudoSub,
+ }
+ runFilePath = testCaseDestDirBuild + "/run.sh"
+ with open(runFilePath, "a") as runFile:
+ runFile.write("#!/bin/sh\n")
+ runFile.write("cd " + testCaseDestDirRun + "\n")
+ os.chmod(runFilePath, 0755)
+ for runline in testCaseDirectives["RUN"]:
+ runFile.write(string.Template(runline).safe_substitute(runSubs) + "\n")
+ runFile.write("\n")
+ runFile.close()
+ return 0
+
+
+
+#
+# Use XCode build settings to build all unit tests for specified platform
+# Generate a .plist for BATS to use to run all tests
+#
+if __name__ == "__main__":
+ dstDir = os.getenv("DSTROOT", "/tmp/dyld_tests/")
+ testsRunDstTopDir = "/AppleInternal/CoreOS/tests/dyld/"
+ testsBuildDstTopDir = dstDir + testsRunDstTopDir
+ shutil.rmtree(testsBuildDstTopDir, ignore_errors=True)
+ testsSrcTopDir = os.getenv("SRCROOT", "./") + "/testing/test-cases/"
+ sdkDir = os.getenv("SDKROOT", "/")
+ toolsDir = os.getenv("TOOLCHAIN_DIR", "/")
+ defaultMinOS = ""
+ minOSOption = os.getenv("DEPLOYMENT_TARGET_CLANG_FLAG_NAME", "")
+ if minOSOption:
+ minOSVersName = os.getenv("DEPLOYMENT_TARGET_CLANG_ENV_NAME", "")
+ if minOSVersName:
+ minVersNum = os.getenv(minOSVersName, "")
+ platformName = os.getenv("PLATFORM_NAME", "osx")
+ archOptions = ""
+ archList = os.getenv("RC_ARCHS", "")
+ if archList:
+ for arch in string.split(archList, " "):
+ archOptions = archOptions + " -arch " + arch
+ else:
+ archList = os.getenv("ARCHS_STANDARD_32_64_BIT", "")
+ if platformName == "watchos":
+ archOptions = "-arch armv7k"
+ elif platformName == "appletvos":
+ archOptions = "-arch arm64"
+ else:
+ archOptions = ""
+ for arch in string.split(archList, " "):
+ archOptions = archOptions + " -arch " + arch
+ allTests = []
+ for f in os.listdir(testsSrcTopDir):
+ if f.endswith(".dtest"):
+ testName = f[0:-6]
+ outDirBuild = testsBuildDstTopDir + testName
+ outDirRun = testsRunDstTopDir + testName
+ testCaseDir = testsSrcTopDir + f
+ testCaseDirectives = parseDirectives(testCaseDir)
+ if useTestCase(testCaseDirectives, platformName):
+ result = buildTestCase(testCaseDirectives, testCaseDir, toolsDir, sdkDir, minOSOption, minVersNum, archOptions, outDirBuild, outDirRun)
+ if result:
+ sys.exit(result)
+ mytest = {}
+ mytest["TestName"] = testName
+ mytest["Arch"] = "platform-native"
+ mytest["WorkingDirectory"] = testsRunDstTopDir + testName
+ mytest["Command"] = []
+ mytest["Command"].append("./run.sh")
+ for runline in testCaseDirectives["RUN"]:
+ if "$SUDO" in runline:
+ mytest["AsRoot"] = 1
+ if testCaseDirectives["RUN_TIMEOUT"]:
+ mytest["Timeout"] = testCaseDirectives["RUN_TIMEOUT"]
+ allTests.append(mytest)
+ batsInfo = { "BATSConfigVersion": "0.1.0",
+ "Project": "dyld_tests",
+ "Tests": allTests }
+ batsFileDir = dstDir + "/AppleInternal/CoreOS/BATS/unit_tests/"
+ shutil.rmtree(batsFileDir, ignore_errors=True)
+ os.makedirs(batsFileDir)
+ batsFilePath = batsFileDir + "dyld.plist"
+ with open(batsFilePath, "w") as batsFile:
+ batsFile.write(plistlib.writePlistToString(batsInfo))
+ batsFile.close()
+ os.system('plutil -convert binary1 ' + batsFilePath) # convert the plist in place to binary
+ runHelper = dstDir + "/AppleInternal/CoreOS/tests/dyld/run_all_dyld_tests.sh"
+ print runHelper
+ with open(runHelper, "w") as shFile:
+ shFile.write("#!/bin/sh\n")
+ for test in allTests:
+ shFile.write(test["WorkingDirectory"] + "/run.sh\n")
+ shFile.close()
+ os.chmod(runHelper, 0755)
+
+
--- /dev/null
+#include <mach/mach_exc.defs>
--- /dev/null
+.Dd Dec 15, 2015
+.Dt nocr 1
+.Os Darwin
+.Sh NAME
+.Nm nocr
+.Nd "runs a command with crash reporter disabled"
+.Sh SYNOPSIS
+.HP 5n
+\fBnocr\fR
+\fIcommand\fR
+.br
+.Sh DESCRIPTION
+\fBnocr\fR
+executes
+\fIcommand\fR
+in a way that blocks crash reporter should the command process crash.
+This is useful when running test suites
+.Pp
+.Sh "EXIT VALUE"
+Upon successful execution of a program, the exit status from
+\fInocr\fR
+will simply be the exit status of the program that was executed.
+.Pp
+If the program
+\fIcommand\fR
+crashed, the exit value will be 1
--- /dev/null
+#include "execserverServer.h"
+
+#include <mach/mach.h>
+#include <mach/vm_map.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <dispatch/dispatch.h>
+#include <errno.h>
+#include <signal.h>
+#include <libproc.h>
+#include <System/sys/reason.h>
+#include <System/sys/proc_info.h>
+
+
+
+static pid_t sChildPid;
+static dispatch_semaphore_t sServerRunning;
+static bool sChildCrashed = false;
+static bool sChildTerminatedByDyld = false;
+
+/*
+ * setup exception handling port for EXC_CRASH and EXC_CORPSE_NOTIFY.
+ * runs mach_msg_server once for receiving exception messages from kernel.
+ */
+static void* serverCode(void* arg)
+{
+ mach_port_t exception_port;
+
+ kern_return_t kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port);
+ if (kret != KERN_SUCCESS)
+ errx(1, "mach_port_allocate: %s (%d)", mach_error_string(kret), kret);
+
+ kret = mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND);
+ if (kret != KERN_SUCCESS)
+ errx(1, "mach_port_insert_right: %s (%d)", mach_error_string(kret), kret);
+
+ kret = task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exception_port,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0);
+ if (kret != KERN_SUCCESS)
+ errx(1, "task_set_exception_ports: %s (%d)", mach_error_string(kret), kret);
+
+ dispatch_semaphore_signal(sServerRunning);
+
+ kret = mach_msg_server(mach_exc_server, MACH_MSG_SIZE_RELIABLE, exception_port, 0);
+ if (kret != KERN_SUCCESS)
+ errx(1, "mach_msg_server: %s (%d)", mach_error_string(kret), kret);
+
+ return NULL;
+}
+
+
+static void childDied(int sig)
+{
+ struct proc_exitreasoninfo info;
+ bzero(&info, sizeof(info));
+ uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE];
+ bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE);
+ info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
+ info.eri_kcd_buf = (user_addr_t)packReasonData;
+ //fprintf(stderr, "info=%p\n", &info);
+ if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) {
+ printf("bad return size from proc_pidinfo()\n");
+ return;
+ }
+ sChildTerminatedByDyld = (info.eri_namespace == OS_REASON_DYLD);
+ }
+
+
+int main(int argc, const char* argv[])
+{
+ if ( argc < 2 ) {
+ fprintf(stderr, "usage: nocr [-require_crash] prog args...\n");
+ return EXIT_FAILURE;
+ }
+ unsigned progArgIndex = 1;
+ bool requireCrash = false;
+ const char* testName = NULL;
+ if ( strcmp(argv[1], "-require_crash") == 0 ) {
+ progArgIndex = 2;
+ requireCrash = true;
+ testName = getenv("NOCR_TEST_NAME");
+ if ( testName )
+ printf("[BEGIN] %s\n", testName);
+ }
+
+ signal(SIGCHLD, childDied);
+
+ sServerRunning = dispatch_semaphore_create(0);
+
+ // start up thread for mach server which handles mach exception ports
+ pthread_t serverThread;
+ int result = pthread_create(&serverThread, NULL, serverCode, NULL);
+ if ( result )
+ err(EXIT_FAILURE, "pthread_create");
+
+ // wait until server is up before starting child
+ dispatch_semaphore_wait(sServerRunning, DISPATCH_TIME_FOREVER);
+
+ // fork and exec child
+ sChildPid = fork();
+ if ( sChildPid < 0 )
+ err(EXIT_FAILURE, "fork");
+ if ( sChildPid == 0 ) {
+ // child side
+ result = execvp(argv[progArgIndex], (char**)&argv[progArgIndex]);
+ err(EXIT_FAILURE, "exec(\"%s\",...)", argv[progArgIndex]);
+ }
+
+ // wait for child to finish (including crash)
+ int status;
+ int waitResult;
+ int childResult = EXIT_FAILURE;
+ do {
+ waitResult = waitpid(sChildPid, &status, 0);
+ } while ( (waitResult == -1) && (errno == EINTR) );
+ if ( waitResult != -1 ) {
+ if ( WIFEXITED(status) ) {
+ childResult = WEXITSTATUS(status);
+ }
+ }
+
+ if ( requireCrash ) {
+ if ( testName ) {
+ if ( sChildCrashed || sChildTerminatedByDyld )
+ printf("[PASS] %s\n", testName);
+ else
+ printf("[FAIL] %s\n", testName);
+ }
+ return sChildCrashed ? EXIT_SUCCESS : EXIT_FAILURE;
+ }
+ else
+ return childResult;
+}
+
+
+
+
+// Mach exception handler routines needed by execserverServer.c
+
+kern_return_t
+catch_mach_exception_raise(mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt)
+{
+ //fprintf(stderr, "child crashed\n");
+ sChildCrashed = true;
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+catch_mach_exception_raise_state(mach_port_t exception_port,
+ exception_type_t exception,
+ const mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt,
+ int * flavor,
+ const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t * new_stateCnt)
+{
+ errx(1, "Unsupported catch_mach_exception_raise_state");
+ return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t
+catch_mach_exception_raise_state_identity(mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt,
+ int * flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t * new_stateCnt)
+{
+ errx(1, "Unsupported catch_mach_exception_raise_state_identity");
+ return KERN_NOT_SUPPORTED;
+}
+
+
+
--- /dev/null
+#!/usr/bin/python2.7
+
+import plistlib
+import string
+import sys
+import os
+import shutil
+import subprocess
+
+
+
+
+#
+# Parse dyld's BATS input file and run each test
+#
+if __name__ == "__main__":
+ batsPlist = plistlib.readPlist("/AppleInternal/CoreOS/BATS/unit_tests/dyld.plist")
+ tests = batsPlist["Tests"]
+ passedCount = 0
+ failedCount = 0
+ for test in tests:
+ cwd = test["WorkingDirectory"]
+ if cwd:
+ os.chdir(cwd)
+ cmd = test["Command"]
+ testName = test["TestName"]
+ if cwd:
+ try:
+ runOutput = subprocess.check_output(cmd,stderr= subprocess.STDOUT)
+ lines = runOutput.splitlines()
+ foundBegin = False
+ passed = False
+ failed = False
+ currentTestName = ""
+ for line in lines:
+ line = line.lstrip().rstrip()
+ #print testName + ": " + line
+ beginIndex = string.find(line, "[BEGIN]")
+ if beginIndex != -1:
+ beginName = line[beginIndex + 7:].lstrip()
+ foundBegin = True
+ currentTestName = beginName
+ passIndex = string.find(line, "[PASS]")
+ if passIndex != -1:
+ passName = line[passIndex + 6:].lstrip()
+ passed = True
+ if passName != currentTestName:
+ print >> sys.stderr, "[PASS] name does not match [BEGIN] name for test " + testName
+ failIndex = string.find(line, "[FAIL]")
+ if failIndex != -1:
+ failName = line[failIndex + 6:].lstrip()
+ failed = True
+ if failName != currentTestName:
+ print >> sys.stderr, "[FAIL] name does not match [BEGIN] name for test " + testName
+ if foundBegin:
+ if not passed and not failed:
+ print >> sys.stderr, "[BEGIN] found [PASS] or [FAIL] for test " + testName
+ else:
+ print >> sys.stderr, "Missing [BEGIN] for test " + testName
+ if passed:
+ print "PASSED: " + testName
+ passedCount = passedCount + 1
+ elif failed:
+ print "FAILED: " + testName
+ failedCount = failedCount + 1
+ except subprocess.CalledProcessError as e:
+ print >> sys.stderr, "FAILED: " + testName + " (execution failure)"
+ print "Total PASS count: " + str(passedCount)
+ print "Total FAIL count: " + str(failedCount)
+
+
+def alt():
+ testsTopDir = "/AppleInternal/CoreOS/tests/dyld/"
+ for f in os.listdir(testsTopDir):
+ testRunner = testsTopDir + f + "/run.sh"
+ if os.path.isfile(testRunner):
+ try:
+ runOutput = subprocess.check_output([testRunner],stderr= subprocess.STDOUT)
+ lines = runOutput.splitlines()
+ foundBegin = False
+ passed = False
+ failed = False
+ currentTestName = ""
+ for line in lines:
+ line = line.lstrip().rstrip()
+ #print f + ": " + line
+ beginIndex = string.find(line, "[BEGIN]")
+ if beginIndex != -1:
+ beginName = line[beginIndex + 7:].lstrip()
+ foundBegin = True
+ currentTestName = beginName
+ passIndex = string.find(line, "[PASS]")
+ if passIndex != -1:
+ passName = line[passIndex + 6:].lstrip()
+ passed = True
+ if passName != currentTestName:
+ print >> sys.stderr, "[PASS] name does not match [BEGIN] name for test " + f
+ failIndex = string.find(line, "[FAIL]")
+ if failIndex != -1:
+ failName = line[failIndex + 6:].lstrip()
+ failed = True
+ if failName != currentTestName:
+ print >> sys.stderr, "[FAIL] name does not match [BEGIN] name for test " + f
+ if foundBegin:
+ if not passed and not failed:
+ print >> sys.stderr, "[BEGIN] found [PASS] or [FAIL] for test " + f
+ else:
+ print >> sys.stderr, "Missing [BEGIN] for test " + f
+ if passed:
+ print "PASSED: " + f
+ elif failed:
+ print "FAILED: " + f
+ except subprocess.CalledProcessError as e:
+ print >> sys.stderr, "FAILED: " + f + " (execution failure)"
--- /dev/null
+<?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>com.apple.system-task-ports</key>
+ <true/>
+ <key>task_for_pid-allow</key>
+ <true/>
+</dict>
+</plist>
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CC zzz.c -dynamiclib -o $BUILD_DIR/libzzz.dylib -install_name $RUN_DIR/libzzz.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-basic.exe -Wno-deprecated-declarations
+
+// RUN: ./NSAddImage-basic.exe $RUN_DIR/libzzz.dylib
+// RUN: ./NSAddImage-basic.exe libzzz.dylib
+
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+int main(int arg, const char* argv[])
+{
+ const char* path = argv[1];
+ printf("[BEGIN] NSAddImage-basic %s\n", path);
+
+ const struct mach_header* mh = NSAddImage(path, NSADDIMAGE_OPTION_WITH_SEARCHING);
+ if ( mh == NULL )
+ printf("[FAIL] NSAddImage-basic %s\n", path);
+ else
+ printf("[PASS] NSAddImage-basic %s\n", path);
+
+ return 0;
+}
+
--- /dev/null
+void zzz() {}
--- /dev/null
+int foo()
+{
+ return 10;
+}
+
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/test.dylib
+// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test.bundle
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-basic.exe
+
+// RUN: ./dlopen-basic.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+
+static void tryImage(const char* path)
+{
+ printf("[BEGIN] dlopen-basic %s\n", path);
+ void* handle = dlopen(path, RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-basic %s\n", path);
+ return;
+ }
+
+ void* sym = dlsym(handle, "foo");
+ if ( sym == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-basic %s\n", path);
+ return;
+ }
+
+ int result = dlclose(handle);
+ if ( result != 0 ) {
+ printf("dlclose() returned %c\n", result);
+ printf("[FAIL] dlopen-basic %s\n", path);
+ return;
+ }
+
+ printf("[PASS] dlopen-basic %s\n", path);
+}
+
+
+
+int main()
+{
+ tryImage("test.bundle");
+ tryImage("test.dylib");
+
+ return 0;
+}
+
--- /dev/null
+
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-framework-fallback.exe
+
+// RUN: ./dlopen-framework-fallback.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+
+
+int main()
+{
+ printf("[BEGIN] dlopen-framework-fallback\n");
+
+ // Verify dyld will fallback and look for framework in /System/Library/Frameworks/
+ void* handle = dlopen("/System/Library/BadPath/CoreFoundation.framework/CoreFoundation", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-framework-fallback\n");
+ return 0;
+ }
+
+ // validate handle works to find symbols
+ void* sym = dlsym(handle, "CFRetain");
+ if ( sym == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-framework-fallback\n");
+ return 0;
+ }
+
+ printf("[PASS] dlopen-framework-fallback\n");
+
+ return 0;
+}
+
--- /dev/null
+int foo() {
+ return 10;
+}
--- /dev/null
+// BUILD: $CC dylib.c -dynamiclib -o $BUILD_DIR/signed.dylib
+// BUILD: $CC dylib.c -dynamiclib -o $BUILD_DIR/unsigned.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-signed.exe
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-unsigned.exe
+
+// FIXME: add builds that sign the executable and the dylib in in various ways
+// At this time we don't have a way to do that, so this test must be run
+// manually.
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+int main() {
+ printf("[BEGIN] dlopen-signing\n");
+ void* handle = dlopen("signed.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-signing (signed loading signed)\n");
+ return 0;
+ } else {
+ int result = dlclose(handle);
+ if ( result != 0 ) {
+ printf("dlclose() returned %c\n", result);
+ printf("[FAIL] dlopen-signing (signed unloading signed)\n");
+ return 0;
+ }
+ }
+
+ handle = dlopen("unsigned.dylib", RTLD_LAZY);
+ if ( handle != NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-signing (signed loading unsigned)\n");
+ return 0;
+ } else {
+ int result = dlclose(handle);
+ if ( result != 0 ) {
+ printf("dlclose() returned %c\n", result);
+ printf("[FAIL] dlopen-signing (signed unloading signed)\n");
+ return 0;
+ }
+ }
+
+ printf("[PASS] dlopen-signing\n");
+
+ return 0;
+}
+
+
--- /dev/null
+
+
+#if HAS_SYMBOL
+int slipperySymbol = 5;
+#endif
--- /dev/null
+#include <stdlib.h>
+
+int main(int argc, const char* argv[])
+{
+ return 1;
+}
+
--- /dev/null
+
+void foo()
+{
+}
+
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib
+// BUILD: $CC emptyMain.c $BUILD_DIR/libmissing.dylib -o $BUILD_DIR/prog_missing_dylib.exe
+// BUILD: $CC defSymbol.c -dynamiclib -install_name libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib
+// BUILD: $CC defSymbol.c -dynamiclib -install_name libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL
+// BUILD: $CC useSymbol.c $BUILD_DIR/libHasSymbols.dylib -o $BUILD_DIR/prog_missing_symbol.exe
+// BUILD: $CC main.c -o $BUILD_DIR/dyld_abort_tests.exe
+
+// RUN: ./dyld_abort_tests.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <err.h>
+#include <System/sys/reason.h>
+#include <System/sys/proc_info.h>
+#include <System/kern/kern_cdata.h>
+#include <libproc.h>
+#include <mach-o/dyld_priv.h>
+
+
+static bool sSignalCaught = false;
+static bool sChildAbortInfoCorrect = false;
+static pid_t sChildPid = 0;
+static uint64_t sExpectedDyldReason = 0;
+static const char* sExpectedDylibPath = NULL;
+static const char* sExpectedSymbol = NULL;
+
+
+static void childDied(int sig)
+{
+ sSignalCaught = true;
+ //printf("sigchld for pid=%d\n", sChildPid);
+
+ struct proc_exitreasoninfo info;
+ bzero(&info, sizeof(info));
+ uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE];
+ bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE);
+ info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
+ info.eri_kcd_buf = (user_addr_t)packReasonData;
+ //fprintf(stderr, "info=%p\n", &info);
+ if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) {
+ printf("bad return size from proc_pidinfo()\n");
+ return;
+ }
+ if ( info.eri_namespace != OS_REASON_DYLD ) {
+ printf("eri_namespace != OS_REASON_DYLD\n");
+ return;
+ }
+ if ( info.eri_code != sExpectedDyldReason ) {
+ printf("eri_code != %lld\n", sExpectedDyldReason);
+ return;
+ }
+ kcdata_iter_t iter = kcdata_iter(packReasonData, info.eri_reason_buf_size);
+
+ if ( !kcdata_iter_valid(iter) ) {
+ printf("invalid kcdata iterator from payload data\n");
+ return;
+ }
+
+ if ( kcdata_iter_type(iter) != KCDATA_BUFFER_BEGIN_OS_REASON ){
+ printf("first kcdata from payload data is not KCDATA_BUFFER_BEGIN_OS_REASON\n");
+ return;
+ }
+
+ kcdata_iter_t payloadIter = kcdata_iter_find_type(iter, EXIT_REASON_USER_PAYLOAD);
+ if ( !kcdata_iter_valid(payloadIter) ) {
+ printf("invalid kcdata payload iterator from payload data\n");
+ return;
+ }
+ const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter);
+
+ if ( dyldInfo->version != 1 ) {
+ printf("dyld payload is not version 1\n");
+ return;
+ }
+
+ if ( (dyldInfo->flags & 1) == 0 ) {
+ printf("dyld flags should have low bit set to me process terminated at launch\n");
+ return;
+ }
+
+ if ( sExpectedDylibPath != NULL ) {
+ if ( dyldInfo->targetDylibPathOffset != 0 ) {
+ const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset;
+ if ( strcmp(sExpectedDylibPath, targetDylib) != 0 ) {
+ printf("dylib path (%s) not what expected (%s)\n", targetDylib, sExpectedDylibPath);
+ return;
+ }
+ }
+ else {
+ printf("dylib path (%s) not provided by dyld\n", sExpectedDylibPath);
+ return;
+ }
+ }
+
+ if ( sExpectedSymbol != NULL ) {
+ if ( dyldInfo->targetDylibPathOffset != 0 ) {
+ const char* missingSymbol = (char*)dyldInfo + dyldInfo->symbolOffset;
+ if ( strcmp(sExpectedSymbol, missingSymbol) != 0 ) {
+ printf("symbol (%s) not what expected (%s)\n", missingSymbol, sExpectedSymbol);
+ return;
+ }
+ }
+ else {
+ printf("symbol (%s) not provided by dyld\n", sExpectedSymbol);
+ return;
+ }
+ }
+
+ sChildAbortInfoCorrect = true;
+}
+
+
+bool runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* expectedSymbol)
+{
+ sSignalCaught = false;
+ sChildAbortInfoCorrect = false;
+ sExpectedDyldReason = dyldReason;
+ sExpectedDylibPath = expectedDylibPath;
+ sExpectedSymbol = expectedSymbol;
+
+ // fork and exec child
+ sChildPid = fork();
+ if ( sChildPid < 0 )
+ err(EXIT_FAILURE, "fork");
+ if ( sChildPid == 0 ) {
+ // child side
+ char* childArgv[] = { (char*)prog, NULL };
+ int result = execvp(prog, childArgv);
+ err(EXIT_FAILURE, "exec(\"%s\",...)", prog);
+ }
+ for(int i=0; i < 10; ++i) {
+ if ( sSignalCaught )
+ break;
+ sleep(1);
+ }
+
+ return sChildAbortInfoCorrect;
+}
+
+
+int main(int argc, const char* argv[])
+{
+ bool someTestFailed = false;
+ printf("[BEGIN] dyld_abort_payload\n");
+
+ // set up signal handler for catching child terminations
+ signal(SIGCHLD, childDied);
+
+ // test launch program with missing library
+ if ( !runTest("./prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL) ) {
+ printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_DYLIB_MISSING\n");
+ someTestFailed = true;
+ }
+
+ // test launch program with missing symbol
+ if ( !runTest("./prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol") ) {
+ printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_SYMBOL_MISSING\n");
+ someTestFailed = true;
+ }
+
+ if ( !someTestFailed )
+ printf("[PASS] dyld_abort_payload\n");
+
+ return 0;
+}
+
--- /dev/null
+
+
+extern int slipperySymbol;
+
+int main()
+{
+ return slipperySymbol;
+}
--- /dev/null
+#include <mach/mach.h>
+
+int main()
+{
+ task_suspend(mach_task_self());
+ return 0;
+}
+
--- /dev/null
+
+// BUILD: $CC linksWithCF.c -o $BUILD_DIR/linksWithCF.exe -framework CoreFoundation
+// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info.exe
+// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info.exe
+
+// RUN: $SUDO ./dyld_process_info.exe $RUN_DIR/linksWithCF.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/dyld_process_info.h>
+
+
+extern char** environ;
+
+#if __x86_64__
+ cpu_type_t otherArch[] = { CPU_TYPE_I386 };
+#elif __i386__
+ cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
+#elif __arm64__
+ cpu_type_t otherArch[] = { CPU_TYPE_ARM };
+#elif __arm__
+ cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
+#endif
+
+static task_t launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
+{
+ posix_spawnattr_t attr;
+ if ( posix_spawnattr_init(&attr) != 0 ) {
+ printf("[FAIL] dyld_process_info posix_spawnattr_init()\n");
+ exit(0);
+ }
+ if ( launchSuspended ) {
+ if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
+ printf("[FAIL] dyld_process_info POSIX_SPAWN_START_SUSPENDED\n");
+ exit(0);
+ }
+ }
+ if ( launchOtherArch ) {
+ size_t copied;
+ if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) {
+ printf("[FAIL] dyld_process_info posix_spawnattr_setbinpref_np()\n");
+ exit(0);
+ }
+ }
+
+ pid_t childPid;
+ const char* argv[] = { testProgPath, NULL };
+ int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ);
+ if ( psResult != 0 ) {
+ printf("[FAIL] dyld_process_info posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
+ exit(0);
+ }
+ //printf("child pid=%d\n", childPid);
+
+ task_t childTask = 0;
+ if ( task_for_pid(mach_task_self(), childPid, &childTask) != KERN_SUCCESS ) {
+ printf("[FAIL] dyld_process_info task_for_pid()\n");
+ kill(childPid, SIGKILL);
+ exit(0);
+ }
+
+ // wait until process is up and has suspended itself
+ struct task_basic_info info;
+ do {
+ unsigned count = TASK_BASIC_INFO_COUNT;
+ kern_return_t kr = task_info(childTask, TASK_BASIC_INFO, (task_info_t)&info, &count);
+ sleep(1);
+ } while ( info.suspend_count == 0 );
+
+ return childTask;
+}
+
+static bool hasCF(task_t task, bool launchedSuspended)
+{
+ kern_return_t result;
+ dyld_process_info info = _dyld_process_info_create(task, 0, &result);
+ if ( info == NULL ) {
+ printf("[FAIL] dyld_process_info _dyld_process_info_create(), kern_return_t=%d\n", result);
+ return false;
+ }
+
+ dyld_process_state_info stateInfo;
+ _dyld_process_info_get_state(info, &stateInfo);
+ bool valueSaysLaunchedSuspended = (stateInfo.dyldState == dyld_process_state_not_started);
+ if ( valueSaysLaunchedSuspended != launchedSuspended ) {
+ printf("[FAIL] dyld_process_info suspend state mismatch\n");
+ return false;
+ }
+
+ __block bool foundDyld = false;
+ _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
+ //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path);
+ if ( strstr(path, "/usr/lib/dyld") != NULL )
+ foundDyld = true;
+ });
+
+ if ( launchedSuspended ) {
+ // fprintf(stderr, "launched suspended image list:\n");
+ __block bool foundMain = false;
+ _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
+ //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path);
+ if ( strstr(path, "/linksWithCF.exe") != NULL )
+ foundMain = true;
+ });
+ return foundMain && foundDyld;
+ }
+
+ __block bool foundCF = false;
+ _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
+ //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path);
+ if ( strstr(path, "/CoreFoundation.framework/") != NULL )
+ foundCF = true;
+ });
+
+ _dyld_process_info_release(info);
+
+ return foundCF && foundDyld;
+}
+
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] dyld_process_info\n");
+
+ if ( argc < 2 ) {
+ printf("[FAIL] dyld_process_info missing argument\n");
+ exit(0);
+ }
+ const char* testProgPath = argv[1];
+ task_t childTask;
+
+ // launch test program same arch as this program
+ childTask = launchTest(testProgPath, false, false);
+ if ( ! hasCF(childTask, false) ) {
+ printf("[FAIL] dyld_process_info same arch does not link with CF and dyld\n");
+ task_terminate(childTask);
+ exit(0);
+ }
+ task_terminate(childTask);
+
+ // launch test program suspended
+ childTask = launchTest(testProgPath, false, true);
+ if ( ! hasCF(childTask, true) ) {
+ printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n");
+ task_terminate(childTask);
+ exit(0);
+ }
+ task_resume(childTask);
+ task_terminate(childTask);
+
+#if !TARGET_OS_WATCH && !TARGET_OS_TV && __LP64__
+ // on 64/32 devices, run test program as other arch too
+ childTask = launchTest(testProgPath, true, false);
+ if ( ! hasCF(childTask, false) ) {
+ printf("[FAIL] dyld_process_info other arch does not link with CF and dyld\n");
+ task_terminate(childTask);
+ exit(0);
+ }
+ task_terminate(childTask);
+#endif
+
+ // verify this program does not use CF
+ if ( hasCF(mach_task_self(), false) ) {
+ printf("[FAIL] dyld_process_info self links with CF and dyld\n");
+ exit(0);
+ }
+
+ printf("[PASS] dyld_process_info\n");
+ return 0;
+}
--- /dev/null
+
+void foo() {
+}
+
--- /dev/null
+
+// BUILD: $CC target.c -o $BUILD_DIR/target.exe
+// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib
+// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info_notify.exe
+// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_notify.exe
+
+// RUN_TIMEOUT: 2400
+// RUN: $SUDO ./dyld_process_info_notify.exe $RUN_DIR/target.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/dyld_process_info.h>
+#include <dispatch/dispatch.h>
+
+
+extern char** environ;
+
+#if __x86_64__
+ cpu_type_t otherArch[] = { CPU_TYPE_I386 };
+#elif __i386__
+ cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
+#elif __arm64__
+ cpu_type_t otherArch[] = { CPU_TYPE_ARM };
+#elif __arm__
+ cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
+#endif
+
+static task_t launchTest(const char* testProgPath, const char* arg1, bool launchOtherArch, bool launchSuspended)
+{
+ //fprintf(stderr, "launchTest() launchOtherArch=%d, launchSuspended=%d, arg=%s\n", launchOtherArch, launchSuspended, arg1);
+ posix_spawnattr_t attr;
+ if ( posix_spawnattr_init(&attr) != 0 ) {
+ printf("[FAIL] dyld_process_info_notify posix_spawnattr_init()\n");
+ exit(0);
+ }
+ if ( launchSuspended ) {
+ if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
+ printf("[FAIL] dyld_process_info_notify POSIX_SPAWN_START_SUSPENDED\n");
+ exit(0);
+ }
+ }
+ if ( launchOtherArch ) {
+ size_t copied;
+ if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) {
+ printf("[FAIL] dyld_process_info_notify posix_spawnattr_setbinpref_np()\n");
+ exit(0);
+ }
+ }
+
+ pid_t childPid;
+ const char* argv[] = { testProgPath, arg1, NULL };
+ int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ);
+ if ( psResult != 0 ) {
+ printf("[FAIL] dyld_process_info_notify posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
+ exit(0);
+ }
+ //printf("child pid=%d\n", childPid);
+
+ task_t childTask = 0;
+ if ( task_for_pid(mach_task_self(), childPid, &childTask) != KERN_SUCCESS ) {
+ printf("[FAIL] dyld_process_info_notify task_for_pid()\n");
+ kill(childPid, SIGKILL);
+ exit(0);
+ }
+
+ return childTask;
+}
+
+static void wait_util_task_suspended(task_t task)
+{
+ struct task_basic_info info;
+ do {
+ unsigned count = TASK_BASIC_INFO_COUNT;
+ kern_return_t kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count);
+ //fprintf(stderr, "task_info() => %d, suspendCount=%d\n", kr, info.suspend_count);
+ sleep(1);
+ } while ( info.suspend_count == 0 );
+}
+
+
+static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
+{
+ kern_return_t kr;
+ __block bool sawMainExecutable = false;
+ __block bool sawlibSystem = false;
+ __block bool gotTerminationNotice = false;
+ __block bool gotEarlyNotice = false;
+ __block int libFooLoadCount = 0;
+ __block int libFooUnloadCount = 0;
+ dispatch_semaphore_t taskDone = dispatch_semaphore_create(0);
+
+ dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
+
+ unsigned count = 0;
+ dyld_process_info_notify handle;
+ do {
+ handle = _dyld_process_info_notify(task, serviceQueue,
+ ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
+ if ( strstr(path, "/target.exe") != NULL )
+ sawMainExecutable = true;
+ if ( strstr(path, "/libSystem") != NULL )
+ sawlibSystem = true;
+ if ( strstr(path, "/libfoo.dylib") != NULL ) {
+ if ( unload )
+ ++libFooUnloadCount;
+ else
+ ++libFooLoadCount;
+ if ( disconnectEarly ) {
+ gotEarlyNotice = true;
+ dispatch_semaphore_signal(taskDone);
+ }
+ }
+ //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n",
+ // unload, machHeader, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
+ // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path);
+ },
+ ^{
+ //fprintf(stderr, "target exited\n");
+ gotTerminationNotice = true;
+ dispatch_semaphore_signal(taskDone);
+ },
+ &kr);
+ ++count;
+ if ( handle == NULL )
+ fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d, count=%d\n", kr, count);
+ } while ( (handle == NULL) && (count < 5) );
+
+ if ( handle == NULL ) {
+ return false;
+ }
+
+ // if process suspends itself, wait until it has done so
+ if ( attachLate )
+ wait_util_task_suspended(task);
+
+ // resume from initial suspend
+ task_resume(task);
+
+ // block waiting for notification that target has exited
+ bool gotSignal = (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0);
+ _dyld_process_info_notify_release(handle);
+
+
+ if ( !gotSignal ) {
+ fprintf(stderr, "did not get exit signal\n");
+ return false;
+ }
+
+ if ( !attachLate && !sawMainExecutable ) {
+ fprintf(stderr, "did not get load notification of main executable\n");
+ return false;
+ }
+
+ if ( !attachLate && !sawlibSystem ) {
+ fprintf(stderr, "did not get load notification of libSystem\n");
+ return false;
+ }
+
+ if ( disconnectEarly ) {
+ if ( libFooLoadCount != 1 ) {
+ fprintf(stderr, "got %d load notifications about libFoo instead of 1\n", libFooLoadCount);
+ return false;
+ }
+ if ( libFooUnloadCount != 0 ) {
+ fprintf(stderr, "got %d unload notifications about libFoo instead of 1\n", libFooUnloadCount);
+ return false;
+ }
+ }
+ else {
+ if ( libFooLoadCount != 3 ) {
+ fprintf(stderr, "got %d load notifications about libFoo instead of 3\n", libFooLoadCount);
+ return false;
+ }
+ if ( libFooUnloadCount != 3 ) {
+ fprintf(stderr, "got %d unload notifications about libFoo instead of 3\n", libFooUnloadCount);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void validateMaxNotifies(task_t task)
+{
+ dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
+ dyld_process_info_notify handles[10];
+ for (int i=0; i < 10; ++i) {
+ kern_return_t kr;
+ handles[i] = _dyld_process_info_notify(task, serviceQueue,
+ ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
+ //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n",
+ // unload, machHeader, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
+ // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path);
+ },
+ ^{
+ //fprintf(stderr, "target exited\n");
+ },
+ &kr);
+ if ( handles[i] == NULL ) {
+ if ( i == 8 ) {
+ // expected failure, because only 8 simultaneous connections allowed
+ // release one and try again
+ _dyld_process_info_notify_release(handles[4]);
+ handles[4] = NULL;
+ }
+ else {
+ fprintf(stderr, "_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d\n", kr, i);
+ task_terminate(task);
+ exit(0);
+ }
+ }
+ }
+ // release all
+ for (int i=0; i < 10; ++i) {
+ if ( handles[i] != NULL ) {
+ _dyld_process_info_notify_release(handles[i]);
+ }
+ }
+ dispatch_release(serviceQueue);
+}
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] dyld_process_info_notify\n");
+ if ( argc < 2 ) {
+ printf("[FAIL] dyld_process_info_notify missing argument\n");
+ exit(0);
+ }
+ const char* testProgPath = argv[1];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ task_t childTask;
+
+ // test 1) launch test program suspended in same arch as this program
+ childTask = launchTest(testProgPath, "", false, true);
+ if ( ! monitor(childTask, false, false) ) {
+ printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n");
+ task_terminate(childTask);
+ exit(0);
+ }
+ task_terminate(childTask);
+
+ // test 2) launch test program in same arch as this program where it sleeps itself
+ childTask = launchTest(testProgPath, "suspend-in-main", false, false);
+ validateMaxNotifies(childTask);
+ if ( ! monitor(childTask, false, true) ) {
+ printf("[FAIL] dyld_process_info_notify launch suspend-in-main missed some notifications\n");
+ task_terminate(childTask);
+ exit(0);
+ }
+ task_terminate(childTask);
+
+#if !TARGET_OS_WATCH && !TARGET_OS_TV && __LP64__
+ // test 3) launch test program suspended in opposite arch as this program
+ childTask = launchTest(testProgPath, "", true, true);
+ if ( ! monitor(childTask, false, false) ) {
+ printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n");
+ task_terminate(childTask);
+ exit(0);
+ }
+ task_terminate(childTask);
+
+ // test 4) launch test program in opposite arch as this program where it sleeps itself
+ childTask = launchTest(testProgPath, "suspend-in-main", true, false);
+ if ( ! monitor(childTask, false, true) ) {
+ printf("[FAIL] dyld_process_info_notify launch other arch suspend-in-main missed some notifications\n");
+ task_terminate(childTask);
+ exit(0);
+ }
+ task_terminate(childTask);
+#endif
+
+ // test 5) launch test program where we disconnect from it after first dlopen
+ childTask = launchTest(testProgPath, "", false, true);
+ if ( ! monitor(childTask, true, false) ) {
+ printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n");
+ task_terminate(childTask);
+ exit(0);
+ }
+ task_terminate(childTask);
+
+ printf("[PASS] dyld_process_info_notify\n");
+ exit(0);
+ });
+
+ dispatch_main();
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <mach/mach.h>
+
+
+
+int main(int argc, const char* argv[])
+{
+ if ( (argc > 1) && (strcmp(argv[1], "suspend-in-main") == 0) )
+ task_suspend(mach_task_self());
+
+ for (int i=0; i < 3; ++i) {
+ void* h = dlopen("./libfoo.dylib", 0);
+ dlclose(h);
+ }
+
+ return 0;
+}
+
--- /dev/null
+
+int bar() {
+ return 42;
+}
+
--- /dev/null
+
+int foo = 1;
+
--- /dev/null
+// BUILD_ONLY: MacOSX
+// BUILD_MIN_OS: 10.5
+// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR
+
+// RUN: ./dylib-re-export.exe
+
+
+#include <stdio.h>
+
+extern int bar();
+
+
+int main()
+{
+ printf("[BEGIN] dylib-re-export\n");
+ if ( bar() == 42 )
+ printf("[PASS] dylib-re-export\n");
+ else
+ printf("[FAIL] dylib-re-export, wrong value\n");
+
+ return 0;
+}
+
+
--- /dev/null
+
+int bar() {
+ return 42;
+}
+
--- /dev/null
+
+int foo = 1;
+
--- /dev/null
+
+
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib
+// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR
+
+// RUN: ./dylib-re-export.exe
+
+
+#include <stdio.h>
+
+extern int bar();
+
+
+int main()
+{
+ printf("[BEGIN] dylib-re-export\n");
+ if ( bar() == 42 )
+ printf("[PASS] dylib-re-export\n");
+ else
+ printf("[FAIL] dylib-re-export, wrong value\n");
+
+ return 0;
+}
+
+
--- /dev/null
+
+int foo = 42;
+
--- /dev/null
+#include <stdio.h>
+#include <signal.h>
+
+int main()
+{
+ printf("[BEGIN] dylib-static-link missing\n");
+ printf("[FAIL] dylib-static-link missing, program should not have launched\n");
+
+ return 0;
+}
+
+
--- /dev/null
+
+
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib
+// BUILD: $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-present.exe
+// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name libfoomissing.dylib
+// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-missing.exe
+
+// RUN: ./dylib-static-present.exe
+// RUN: NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe
+
+
+#include <stdio.h>
+
+extern int foo;
+
+
+int main()
+{
+ printf("[BEGIN] dylib-static-link present\n");
+ if ( foo == 42 )
+ printf("[PASS] dylib-static-link present\n");
+ else
+ printf("[FAIL] dylib-static-link present, wrong value\n");
+
+ return 0;
+}
+
+
--- /dev/null
+
+int foo = 42;
+
--- /dev/null
+#include <stddef.h>
+#include <stdio.h>
+
+extern int foo __attribute__((weak_import));
+
+
+int main()
+{
+ printf("[BEGIN] dylib-static-weak-link missing\n");
+ // dylib won't be found at runtime, so &foo should be NULL
+ if ( &foo == NULL )
+ printf("[PASS] dylib-static-weak-link missing\n");
+ else
+ printf("[FAIL] dylib-static-weak-link missing, &foo != NULL\n");
+
+ return 0;
+}
+
+
--- /dev/null
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib
+// BUILD: $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-weak-present.exe
+// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name libfoomissing.dylib
+// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe
+
+// RUN: ./dylib-static-weak-present.exe
+// RUN: ./dylib-static-weak-missing.exe
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+extern int foo __attribute__((weak_import));
+
+
+int main()
+{
+ printf("[BEGIN] dylib-static-weak-link present\n");
+ // dylib will be found at runtime, so &foo should never be NULL
+ if ( &foo != NULL ) {
+ if ( foo == 42 )
+ printf("[PASS] dylib-static-weak-link present\n");
+ else
+ printf("[FAIL] dylib-static-weak-link present, wrong value\n");
+ }
+ else {
+ printf("[FAIL] dylib-static-weak-link present, &foo == NULL\n");
+ }
+
+ return 0;
+}
+
+
--- /dev/null
+
+
+
+int foo1() { return 1; }
+
+int foo2() { return 2; }
+
+#ifndef NO_FOO34
+ int foo3() { return 3; }
+ int foo4() { return 4; }
+#endif
+
--- /dev/null
+
+#include <mach-o/dyld-interposing.h>
+
+extern int foo2();
+extern int foo4() __attribute__((weak_import));
+
+
+
+int myfoo2() { return 12; }
+int myfoo4() { return 14; }
+
+
+
+DYLD_INTERPOSE(myfoo2, foo2)
+DYLD_INTERPOSE(myfoo4, foo4)
--- /dev/null
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
+// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/interpose-weak-present.exe
+// BUILD: $CC interposer.c -dynamiclib $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/libinterposer.dylib -install_name libinterposer.dylib
+
+// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib
+// BUILD: $CC foo.c -DNO_FOO34 -dynamiclib -o $BUILD_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib
+// BUILD: $CC main.c -DNO_FOO34 $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/interpose-weak-missing.exe
+// BUILD: $CC interposer.c -dynamiclib $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/libinterposer2.dylib -install_name libinterposer.dylib
+
+
+// RUN: DYLD_INSERT_LIBRARIES=libinterposer.dylib ./interpose-weak-present.exe
+// RUN: DYLD_INSERT_LIBRARIES=libinterposer2.dylib ./interpose-weak-missing.exe
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+extern int foo1();
+extern int foo2();
+extern int foo3() __attribute__((weak_import));
+extern int foo4() __attribute__((weak_import));
+
+#ifndef NO_FOO34
+ #define MODE "present"
+#else
+ #define MODE "missing"
+#endif
+
+int main()
+{
+ printf("[BEGIN] interpose-weak-" MODE "\n");
+
+ if ( foo1() != 1 ) {
+ printf("[FAIL] interpose-weak-" MODE ", foo1() != 1\n");
+ return 0;
+ }
+
+ if ( foo2() != 12 ) {
+ printf("[FAIL] interpose-weak-" MODE ", foo2() != 12\n");
+ return 0;
+ }
+
+#ifndef NO_FOO34
+ if ( foo3() != 3 ) {
+ printf("[FAIL] interpose-weak-" MODE ", foo3() != 3\n");
+ return 0;
+ }
+
+ if ( foo4() != 14 ) {
+ printf("[FAIL] interpose-weak-" MODE ", foo4() != 14\n");
+ return 0;
+ }
+#else
+ if ( &foo3 != NULL ) {
+ printf("[FAIL] interpose-weak-" MODE ", &foo3 != NULL\n");
+ return 0;
+ }
+
+ if ( &foo4 != NULL ) {
+ printf("[FAIL] interpose-weak-" MODE ", &foo4 != NULL\n");
+ return 0;
+ }
+#endif
+
+ printf("[PASS] interpose-weak-" MODE "\n");
+ return 0;
+}
--- /dev/null
+int foo()
+{
+ return 10;
+}
+
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: mkdir $BUILD_DIR/lc
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/lc/libfoo.dylib -install_name /blah/libfoo.dylib
+// BUILD: $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-find -DSHOULD_BE_FOUND=1
+// BUILD: $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-no-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-no-find -sectcreate __RESTRICT __restrict /dev/null
+// BUILD: mkdir $BUILD_DIR/rpath
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/rpath/libfoo.dylib -install_name @rpath/libfoo.dylib
+// BUILD: $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-find -DSHOULD_BE_FOUND=1
+// BUILD: $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-no-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-no-find -sectcreate __RESTRICT __restrict /dev/null
+
+
+// RUN: ./restrict-search-lc-find.exe
+// RUN: ./restrict-search-lc-no-find.exe
+// RUN: ./restrict-search-rpath-find.exe
+// RUN: ./restrict-search-rpath-no-find.exe
+
+
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+// Two ways to find libfoo.dylb: @rpath or DYLD_LIBRARY_PATH (set via LC_DYLD_ENVIRONMENT)
+// These should work for non-restrictured programs.
+// These should fail for restricted programs.
+// By weak linking we can test if libfoo.dylib was found or not.
+
+
+extern int foo() __attribute__((weak_import));
+
+
+#define STRINGIFY2( x) #x
+#define STRINGIFY(x) STRINGIFY2(x)
+
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] restrict-search %s\n", STRINGIFY(MODE));
+#if SHOULD_BE_FOUND
+ if ( &foo == NULL )
+ printf("[FAIL] restrict-search %s\n", STRINGIFY(MODE));
+ else
+ printf("[PASS] restrict-search %s\n", STRINGIFY(MODE));
+#else
+ // dylib won't be found at runtime, so &foo should be NULL
+ if ( &foo == NULL )
+ printf("[PASS] restrict-search %s\n", STRINGIFY(MODE));
+ else
+ printf("[FAIL] restrict-search %s\n", STRINGIFY(MODE));
+#endif
+
+ return 0;
+}
+
--- /dev/null
+
+
+
+__thread int a;
+__thread int b = 5;
+
+
+
--- /dev/null
+
+
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
+// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/thread-local-variables.exe
+
+
+// RUN: ./thread-local-variables.exe
+
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <assert.h>
+#include <unistd.h>
+
+
+extern __thread int a;
+extern __thread int b;
+static __thread int c;
+static __thread int d = 5;
+
+
+static int* sAddr1[4];
+static int* sAddr2[4];
+static int* sAddr3[4];
+
+static pthread_t sWorker1;
+static pthread_t sWorker2;
+
+
+// verify all initial TLV values are correct
+static void checkValues()
+{
+ assert(a == 0);
+ assert(b == 5);
+ assert(c == 0);
+ assert(d == 5);
+}
+
+
+// return addresses of all TLVs
+static void getAddresses(int* array[])
+{
+ array[0] = &a;
+ array[1] = &b;
+ array[2] = &c;
+ array[3] = &d;
+}
+
+static void* work2(void* arg)
+{
+ checkValues();
+ getAddresses(sAddr2);
+
+ return NULL;
+}
+
+static void* work1(void* arg)
+{
+ checkValues();
+
+ if ( pthread_create(&sWorker2, NULL, work2, NULL) != 0 ) {
+ printf("[FAIL] thread-local-variables, pthread_create\n");
+ exit(0);
+ }
+ void* dummy;
+ pthread_join(sWorker2, &dummy);
+
+ getAddresses(sAddr1);
+
+ return NULL;
+}
+
+static bool someMatch(int* t1, int* t2, int* t3)
+{
+ if ( t1 == t2 )
+ return true;
+ if ( t1 == t3 )
+ return true;
+ if ( t2 == t3 )
+ return true;
+ return false;
+}
+
+int main()
+{
+ printf("[BEGIN] thread-local-variables\n");
+
+ checkValues();
+
+ if ( pthread_create(&sWorker1, NULL, work1, NULL) != 0 ) {
+ printf("[FAIL] thread-local-variables, pthread_create\n");
+ return 0;
+ }
+
+ getAddresses(sAddr3);
+
+ void* dummy;
+ pthread_join(sWorker1, &dummy);
+
+ // validate each thread had different addresses for all TLVs
+ if ( someMatch(sAddr1[0], sAddr2[0], sAddr3[0]) )
+ printf("[FAIL] thread-local-variables, &a is same across some threads\n");
+ else if ( someMatch(sAddr1[1], sAddr2[1], sAddr3[1]) )
+ printf("[FAIL] thread-local-variables, &b is same across some threads\n");
+ else if ( someMatch(sAddr1[2], sAddr2[2], sAddr3[2]) )
+ printf("[FAIL] thread-local-variables, &c is same across some threads\n");
+ else if ( someMatch(sAddr1[3], sAddr2[3], sAddr3[3]) )
+ printf("[FAIL] thread-local-variables, &d is same across some threads\n");
+ else
+ printf("[PASS] thread-local-variables\n");
+ return 0;
+}
+
check:
export DYLD_PRINT_CS_NOTIFICATIONS=1 && ./main 2> notifications.log
- grep " load" notifications.log | grep foo.bundle | wc -l | grep 1 >/dev/null
- grep " load" notifications.log | grep bar.dylib | wc -l | grep 1 >/dev/null
- grep " unload" notifications.log | grep foo.bundle | wc -l | grep 1 >/dev/null
- grep " unload" notifications.log | grep bar.dylib | wc -l | grep 1 >/dev/null
+ grep "_load" notifications.log | grep foo.bundle | wc -l | grep 1 >/dev/null
+ grep "_load" notifications.log | grep bar.dylib | wc -l | grep 1 >/dev/null
+ grep "_unload" notifications.log | grep foo.bundle | wc -l | grep 1 >/dev/null
+ grep "_unload" notifications.log | grep bar.dylib | wc -l | grep 1 >/dev/null
echo "PASS coreSymbolication-notify"
all: main foo.bundle
#include <string.h>
#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+#include <_simple.h>
///
/// verify parameters passed to initializer are same as passed to main()
exit(EXIT_SUCCESS);
}
- if ( strcmp(apple[0], argv[1]) == 0 )
- PASS("crt-apple %s", apple[0]);
+ const char* execPath = _simple_getenv(apple, "executable_path");
+ if ( execPath == NULL )
+ FAIL("crt-apple apple[] missing executable_path=");
+ else if ( strcmp(execPath, argv[1]) == 0 )
+ PASS("crt-apple %s", execPath);
else
- FAIL("crt-apple %s", apple[0]);
+ FAIL("crt-apple %s", execPath);
return EXIT_SUCCESS;
}
--- /dev/null
+##
+# Copyright (c) 2015 Apple Computer, Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all check
+
+check:
+ sandbox-exec -f sbs/deny-open.sb ./main "file system sandbox blocked open"
+ sandbox-exec -f sbs/deny-stat.sb ./main "file system sandbox blocked stat"
+ sandbox-exec -f sbs/deny-mmap.sb ./main "file system sandbox blocked mmap"
+
+all:
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
+ mkdir -p sbs
+ sed -e 's/FILTER/file-read-data/' -e "s|DIR|$$PWD|" < base.sb > sbs/deny-open.sb
+ sed -e 's/FILTER/file-read-metadata/' -e "s|DIR|$$PWD|" < base.sb > sbs/deny-stat.sb
+ sed -e 's/FILTER/file-map-executable/' -e "s|DIR|$$PWD|" < base.sb > sbs/deny-mmap.sb
+
+clean:
+ ${RM} -rf *~ main sbs libfoo.dylib
+
+
--- /dev/null
+
+(version 1)
+(allow default)
+(debug deny)
+
+(deny FILTER
+ (literal "DIR/libfoo.dylib"))
+
+
--- /dev/null
+
+
+int foo()
+{
+ return 42;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2015 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+#include <string.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+int main(int argc, const char* argv[])
+{
+ void* handle = dlopen("./libfoo.dylib", RTLD_LAZY);
+ if ( handle != NULL ) {
+ FAIL("dlopen-sandbox dylib should not have loaded");
+ return EXIT_SUCCESS;
+ }
+ const char* errorMsg = dlerror();
+ const char* shouldContain = argv[1];
+ if ( strstr(errorMsg, shouldContain) == NULL )
+ FAIL("dlopen-sandbox dylib correctly failed to loaded, but with wrong error message: %s", errorMsg);
+ else
+ PASS("dlopen-sandbox: %s", shouldContain);
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# verify addends work
+#
+
+all-check: all check
+
+check:
+ ./main
+
+all:
+ ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c
+
+
+clean:
+ ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib
+
--- /dev/null
+
+
+
+const char* bar()
+{
+ return "bar";
+}
--- /dev/null
+
+
+const char* foo()
+{
+ return "foo";
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+extern uint32_t _cpu_capabilities;
+
+extern const char* foo();
+
+typedef const char* (*BarProc)(void);
+
+const char* myStr = "myStr";
+
+int myInt;
+
+int main()
+{
+ if ( !_dyld_is_memory_immutable(myStr, 6) ) {
+ FAIL("_dyld_is_memory_immutable() returned false for string in main executable");
+ return EXIT_SUCCESS;
+ }
+
+ if ( _dyld_is_memory_immutable(strdup("hello"), 6) ) {
+ FAIL("_dyld_is_memory_immutable() returned true for result from strdup()");
+ return EXIT_SUCCESS;
+ }
+
+ if ( _dyld_is_memory_immutable(&myInt, 4) ) {
+ FAIL("_dyld_is_memory_immutable() returned true for global variabe in main executable");
+ return EXIT_SUCCESS;
+ }
+
+ if ( !_dyld_is_memory_immutable(foo(), 4) ) {
+ FAIL("_dyld_is_memory_immutable() returned false for string in statically linked dylib");
+ return EXIT_SUCCESS;
+ }
+
+ if ( !_dyld_is_memory_immutable(&strcpy, 4) ) {
+ FAIL("_dyld_is_memory_immutable() returned false for function in dyld shared cache");
+ return EXIT_SUCCESS;
+ }
+
+ if ( _dyld_is_memory_immutable(&_cpu_capabilities, 4) ) {
+ FAIL("_dyld_is_memory_immutable() returned true for global variabe in shared cache");
+ return EXIT_SUCCESS;
+ }
+
+ void* handle = dlopen("libbar.dylib", 0);
+ if ( handle == NULL ) {
+ FAIL("dlopen(libbar.dylib) failed");
+ return EXIT_SUCCESS;
+ }
+
+ BarProc proc = dlsym(handle, "bar");
+ if ( proc == NULL ) {
+ FAIL("dlsym(bar) failed");
+ return EXIT_SUCCESS;
+ }
+ const char* barStr = (*proc)();
+ if ( _dyld_is_memory_immutable(barStr, 4) ) {
+ FAIL("_dyld_is_memory_immutable() returned true for string in unloadable dylib");
+ return EXIT_SUCCESS;
+ }
+
+
+ PASS("_dyld_is_memory_immutable");
+ return 0;
+}
--- /dev/null
+##
+# Copyright (c) 2015 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+
+all-check: all check
+
+check:
+ ./main
+
+all:
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -I../../../launch-cache/ -o main main.c
+
+clean:
+ ${RM} ${RMFLAGS} main
--- /dev/null
+/*
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdlib.h> // EXIT_SUCCESS
+#include <stdio.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_images.h>
+#include <mach-o/dyld_priv.h>
+#include <mach/mach.h>
+#include <Availability.h>
+#include <mach/shared_region.h>
+
+#include "test.h"
+#include "dyld_cache_format.h"
+
+
+
+int main()
+{
+ const struct dyld_all_image_infos* allInfo = _dyld_get_all_image_infos();
+ if ( allInfo == NULL ) {
+ FAIL("dyld_shared_cache_iterate_text: _dyld_get_all_image_infos() failed");
+ exit(0);
+ }
+ uuid_t curUuid;
+ memcpy(curUuid, allInfo->sharedCacheUUID, 16);
+
+ int __block imageCount = 0;
+ int result = dyld_shared_cache_iterate_text(curUuid, ^(const dyld_shared_cache_dylib_text_info* info) {
+ ++imageCount;
+ //printf(" cur: 0x%09llX -> 0x%09llX %s\n", info->loadAddressUnslid, info->loadAddressUnslid + info->textSegmentSize, info->path);
+ });
+ if ( result != 0 ) {
+ FAIL("dyld_shared_cache_iterate_text: dyld_shared_cache_iterate_text() failed");
+ exit(0);
+ }
+
+ if ( imageCount < 500 ) {
+ FAIL("dyld_shared_cache_iterate_text: dyld_shared_cache_iterate_text() iterated less than 500 dylibs (%d)", imageCount);
+ exit(0);
+ }
+#if 0
+ //uuid_t fixedUUID = {0x3E, 0xDE, 0x37, 0x05, 0x81, 0x68, 0x33, 0xEF, 0xBF, 0x97, 0xC0, 0xF6, 0xD2, 0x4D, 0x93, 0xEC};
+ uuid_t fixedUUID = {0xD4, 0x3B, 0x31, 0x2B, 0xA5, 0xA7, 0x3C, 0x55, 0x90, 0xA0, 0x9A, 0x37, 0x60, 0x7D, 0x70, 0xAF};
+ result = dyld_shared_cache_iterate_text(fixedUUID, ^(const dyld_shared_cache_dylib_text_info* info) {
+ printf(" my: 0x%09llX -> 0x%09llX %s\n", info->loadAddressUnslid, info->loadAddressUnslid + info->textSegmentSize, info->path);
+ });
+#endif
+
+ PASS("dyld_shared_cache_iterate_text");
+ return EXIT_SUCCESS;
+}
+
+
static int singleMappedCount = 0;
static int batchMappedCount = 0;
static int singleUnMappedCount = 0;
-
+static int batchBoundCount = 0;
+static int singleDepsInitedCount = 0;
+static int singleBoundCount = 0;
static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[])
{
return NULL;
}
+static const char* batchBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[])
+{
+ for (uint32_t i=0; i < infoCount; ++i)
+ printf("batchBoundHandler(): %u/%u -> %s\n", i, infoCount, info[i].imageFilePath);
+ if ( state != dyld_image_state_bound ) {
+ FAIL("image-state-change: batchBoundHandler passed state %d", state);
+ exit(0);
+ }
+ batchBoundCount += infoCount;
+ return NULL;
+}
+
static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[])
{
printf("singleMappedHandler(%s)\n", info[0].imageFilePath);
return NULL;
}
+
+static const char* singleBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[])
+{
+ printf("singleBoundHandler(%s)\n", info[0].imageFilePath);
+ if ( state != dyld_image_state_bound ) {
+ FAIL("image-state-change: singleBoundHandler passed state %d", state);
+ exit(0);
+ }
+ if ( infoCount != 1 ) {
+ FAIL("image-state-change: singleBoundHandler given %d images", infoCount);
+ exit(0);
+ }
+ ++singleBoundCount;
+ return NULL;
+}
+
+
+
+static const char* singleDepsInitedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[])
+{
+ printf("singleDepsInitedHandler(%s)\n", info[0].imageFilePath);
+ if ( state != dyld_image_state_dependents_initialized ) {
+ FAIL("image-state-change: singleDepsInitedHandler passed state %d", state);
+ exit(0);
+ }
+ if ( infoCount != 1 ) {
+ FAIL("image-state-change: singleDepsInitedHandler given %d images", infoCount);
+ exit(0);
+ }
+ ++singleDepsInitedCount;
+ return NULL;
+}
+
+
+
static const char* singleUnmappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[])
{
printf("singleUnmappedHandler(%s)\n", info[0].imageFilePath);
// tell dyld we want to know when images are mapped
dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler);
dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler);
+ dyld_register_image_state_change_handler(dyld_image_state_bound, true, batchBoundHandler);
+ dyld_register_image_state_change_handler(dyld_image_state_bound, false, singleBoundHandler);
+ dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, false, singleDepsInitedHandler);
dyld_register_image_state_change_handler(dyld_image_state_terminated, false, singleUnmappedHandler);
+
// with batch mode we get notified of existing images, but not with single mode, so re-sync counts
batchMappedCount=0;